diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7151ff00c88e..9c0cd14f8b27 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,22 +9,25 @@ on:
- 'setup.py'
- 'requirements.txt'
- '*.iss'
+ - 'worlds/*/archipelago.json'
pull_request:
paths:
- '.github/workflows/build.yml'
- 'setup.py'
- 'requirements.txt'
- '*.iss'
+ - 'worlds/*/archipelago.json'
workflow_dispatch:
env:
ENEMIZER_VERSION: 7.1
# NOTE: since appimage/appimagetool and appimage/type2-runtime does not have tags anymore,
# we check the sha256 and require manual intervention if it was updated.
- APPIMAGETOOL_VERSION: continuous
- APPIMAGETOOL_X86_64_HASH: '29348a20b80827cd261c28e95172ff828b69d43d4e4e18e3fd069e2c8693c94e'
- APPIMAGE_RUNTIME_VERSION: continuous
- APPIMAGE_RUNTIME_X86_64_HASH: 'e70ffa9b69b211574d0917adc482dd66f25a0083427b5945783965d55b0b0a8b'
+ APPIMAGE_FORK: 'PopTracker'
+ APPIMAGETOOL_VERSION: 'r-2025-11-18'
+ APPIMAGETOOL_X86_64_HASH: '4577a452b30af2337123fbb383aea154b618e51ad5448c3b62085cbbbfbfd9a2'
+ APPIMAGE_RUNTIME_VERSION: 'r-2025-11-07'
+ APPIMAGE_RUNTIME_X86_64_HASH: '27ddd3f78e483fc5f7856e413d7c17092917f8c35bfe3318a0d378aa9435ad17'
permissions: # permissions required for attestation
id-token: 'write'
@@ -139,9 +142,9 @@ jobs:
- name: Install build-time dependencies
run: |
echo "PYTHON=python3.12" >> $GITHUB_ENV
- wget -nv https://github.com/AppImage/appimagetool/releases/download/$APPIMAGETOOL_VERSION/appimagetool-x86_64.AppImage
+ wget -nv https://github.com/$APPIMAGE_FORK/appimagetool/releases/download/$APPIMAGETOOL_VERSION/appimagetool-x86_64.AppImage
echo "$APPIMAGETOOL_X86_64_HASH appimagetool-x86_64.AppImage" | sha256sum -c
- wget -nv https://github.com/AppImage/type2-runtime/releases/download/$APPIMAGE_RUNTIME_VERSION/runtime-x86_64
+ wget -nv https://github.com/$APPIMAGE_FORK/type2-runtime/releases/download/$APPIMAGE_RUNTIME_VERSION/runtime-x86_64
echo "$APPIMAGE_RUNTIME_X86_64_HASH runtime-x86_64" | sha256sum -c
chmod a+rx appimagetool-x86_64.AppImage
./appimagetool-x86_64.AppImage --appimage-extract
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index cf9ce08faf38..0061dd15b000 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -11,7 +11,7 @@ on:
- "!.github/workflows/**"
- ".github/workflows/docker.yml"
branches:
- - "*"
+ - "main"
tags:
- "v?[0-9]+.[0-9]+.[0-9]*"
workflow_dispatch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 147f30942d99..7f81e5750746 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,10 +11,11 @@ env:
ENEMIZER_VERSION: 7.1
# NOTE: since appimage/appimagetool and appimage/type2-runtime does not have tags anymore,
# we check the sha256 and require manual intervention if it was updated.
- APPIMAGETOOL_VERSION: continuous
- APPIMAGETOOL_X86_64_HASH: '29348a20b80827cd261c28e95172ff828b69d43d4e4e18e3fd069e2c8693c94e'
- APPIMAGE_RUNTIME_VERSION: continuous
- APPIMAGE_RUNTIME_X86_64_HASH: 'e70ffa9b69b211574d0917adc482dd66f25a0083427b5945783965d55b0b0a8b'
+ APPIMAGE_FORK: 'PopTracker'
+ APPIMAGETOOL_VERSION: 'r-2025-11-18'
+ APPIMAGETOOL_X86_64_HASH: '4577a452b30af2337123fbb383aea154b618e51ad5448c3b62085cbbbfbfd9a2'
+ APPIMAGE_RUNTIME_VERSION: 'r-2025-11-07'
+ APPIMAGE_RUNTIME_X86_64_HASH: '27ddd3f78e483fc5f7856e413d7c17092917f8c35bfe3318a0d378aa9435ad17'
permissions: # permissions required for attestation
id-token: 'write'
@@ -127,9 +128,9 @@ jobs:
- name: Install build-time dependencies
run: |
echo "PYTHON=python3.12" >> $GITHUB_ENV
- wget -nv https://github.com/AppImage/appimagetool/releases/download/$APPIMAGETOOL_VERSION/appimagetool-x86_64.AppImage
+ wget -nv https://github.com/$APPIMAGE_FORK/appimagetool/releases/download/$APPIMAGETOOL_VERSION/appimagetool-x86_64.AppImage
echo "$APPIMAGETOOL_X86_64_HASH appimagetool-x86_64.AppImage" | sha256sum -c
- wget -nv https://github.com/AppImage/type2-runtime/releases/download/$APPIMAGE_RUNTIME_VERSION/runtime-x86_64
+ wget -nv https://github.com/$APPIMAGE_FORK/type2-runtime/releases/download/$APPIMAGE_RUNTIME_VERSION/runtime-x86_64
echo "$APPIMAGE_RUNTIME_X86_64_HASH runtime-x86_64" | sha256sum -c
chmod a+rx appimagetool-x86_64.AppImage
./appimagetool-x86_64.AppImage --appimage-extract
diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml
index 90a5d70b8e0c..b08b389005ec 100644
--- a/.github/workflows/unittests.yml
+++ b/.github/workflows/unittests.yml
@@ -59,7 +59,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install pytest pytest-subtests pytest-xdist
+ pip install -r ci-requirements.txt
python ModuleUpdate.py --yes --force --append "WebHostLib/requirements.txt"
python Launcher.py --update_settings # make sure host.yaml exists for tests
- name: Unittests
diff --git a/.gitignore b/.gitignore
index 3bb4e68c9924..3064aecef939 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@
*.gb
*.gbc
*.gba
+*.nds
*.wixobj
*.lck
*.db3
@@ -63,6 +64,7 @@ Output Logs/
/installdelete.iss
/data/user.kv
/datapackage
+/datapackage_export.json
/custom_worlds
# Byte-compiled / optimized / DLL files
diff --git a/.run/Build APWorld.run.xml b/.run/Build APWorld.run.xml
index db6a305e7bb3..fe41bfe48945 100644
--- a/.run/Build APWorld.run.xml
+++ b/.run/Build APWorld.run.xml
@@ -12,8 +12,8 @@
-
-
+
+
diff --git a/BaseClasses.py b/BaseClasses.py
index ee2f73ca5106..4d88fde4f3db 100644
--- a/BaseClasses.py
+++ b/BaseClasses.py
@@ -1721,9 +1721,10 @@ def create_playthrough(self, create_paths: bool = True) -> None:
logging.debug('The following items could not be reached: %s', ['%s (Player %d) at %s (Player %d)' % (
location.item.name, location.item.player, location.name, location.player) for location in
sphere_candidates])
- if any([multiworld.worlds[location.item.player].options.accessibility != 'minimal' for location in sphere_candidates]):
- raise RuntimeError(f'Not all progression items reachable ({sphere_candidates}). '
- f'Something went terribly wrong here.')
+ if not multiworld.has_beaten_game(state):
+ raise RuntimeError("During playthrough generation, the game was determined to be unbeatable. "
+ "Something went terribly wrong here. "
+ f"Unreachable progression items: {sphere_candidates}")
else:
self.unreachables = sphere_candidates
break
diff --git a/CommonClient.py b/CommonClient.py
old mode 100644
new mode 100755
index b1d9aeb15624..1111adb080cc
--- a/CommonClient.py
+++ b/CommonClient.py
@@ -323,7 +323,7 @@ def update_game(self, game: str, name_to_id_lookup_table: typing.Dict[str, int])
hint_cost: int | None
"""Current Hint Cost per Hint from the server"""
hint_points: int | None
- """Current avaliable Hint Points from the server"""
+ """Current available Hint Points from the server"""
player_names: dict[int, str]
"""Current lookup of slot number to player display name from server (includes aliases)"""
@@ -572,6 +572,10 @@ def is_uninteresting_item_send(self, print_json_packet: dict) -> bool:
return print_json_packet.get("type", "") == "ItemSend" \
and not self.slot_concerns_self(print_json_packet["receiving"]) \
and not self.slot_concerns_self(print_json_packet["item"].player)
+
+ def is_connection_change(self, print_json_packet: dict) -> bool:
+ """Helper function for filtering out connection changes."""
+ return print_json_packet.get("type", "") in ["Join","Part"]
def on_print(self, args: dict):
logger.info(args["text"])
@@ -856,9 +860,9 @@ async def server_loop(ctx: CommonContext, address: typing.Optional[str] = None)
server_url = urllib.parse.urlparse(address)
if server_url.username:
- ctx.username = server_url.username
+ ctx.username = urllib.parse.unquote(server_url.username)
if server_url.password:
- ctx.password = server_url.password
+ ctx.password = urllib.parse.unquote(server_url.password)
def reconnect_hint() -> str:
return ", type /connect to reconnect" if ctx.server_address else ""
diff --git a/Generate.py b/Generate.py
index 8e8132038509..5ad50df2b757 100644
--- a/Generate.py
+++ b/Generate.py
@@ -23,7 +23,7 @@
from Utils import parse_yamls, version_tuple, __version__, tuplize_version
-def mystery_argparse():
+def mystery_argparse(argv: list[str] | None = None):
from settings import get_settings
settings = get_settings()
defaults = settings.generator
@@ -57,7 +57,7 @@ def mystery_argparse():
parser.add_argument("--spoiler_only", action="store_true",
help="Skips generation assertion and multidata, outputting only a spoiler log. "
"Intended for debugging and testing purposes.")
- args = parser.parse_args()
+ args = parser.parse_args(argv)
if args.skip_output and args.spoiler_only:
parser.error("Cannot mix --skip_output and --spoiler_only")
@@ -119,9 +119,9 @@ def main(args=None) -> tuple[argparse.Namespace, int]:
else:
meta_weights = None
-
- player_id = 1
- player_files = {}
+ player_id: int = 1
+ player_files: dict[int, str] = {}
+ player_errors: list[str] = []
for file in os.scandir(args.player_files_path):
fname = file.name
if file.is_file() and not fname.startswith(".") and not fname.lower().endswith(".ini") and \
@@ -137,7 +137,11 @@ def main(args=None) -> tuple[argparse.Namespace, int]:
weights_cache[fname] = tuple(weights_for_file)
except Exception as e:
- raise ValueError(f"File {fname} is invalid. Please fix your yaml.") from e
+ logging.exception(f"Exception reading weights in file {fname}")
+ player_errors.append(
+ f"{len(player_errors) + 1}. "
+ f"File {fname} is invalid. Please fix your yaml.\n{Utils.get_all_causes(e)}"
+ )
# sort dict for consistent results across platforms:
weights_cache = {key: value for key, value in sorted(weights_cache.items(), key=lambda k: k[0].casefold())}
@@ -152,6 +156,10 @@ def main(args=None) -> tuple[argparse.Namespace, int]:
args.multi = max(player_id - 1, args.multi)
if args.multi == 0:
+ if player_errors:
+ errors = "\n\n".join(player_errors)
+ raise ValueError(f"Encountered {len(player_errors)} error(s) in player files. "
+ f"See logs for full tracebacks.\n\n{errors}")
raise ValueError(
"No individual player files found and number of players is 0. "
"Provide individual player files or specify the number of players via host.yaml or --multi."
@@ -161,6 +169,10 @@ def main(args=None) -> tuple[argparse.Namespace, int]:
f"{seed_name} Seed {seed} with plando: {args.plando}")
if not weights_cache:
+ if player_errors:
+ errors = "\n\n".join(player_errors)
+ raise ValueError(f"Encountered {len(player_errors)} error(s) in player files. "
+ f"See logs for full tracebacks.\n\n{errors}")
raise Exception(f"No weights found. "
f"Provide a general weights file ({args.weights_file_path}) or individual player files. "
f"A mix is also permitted.")
@@ -171,10 +183,6 @@ def main(args=None) -> tuple[argparse.Namespace, int]:
args.sprite_pool = dict.fromkeys(range(1, args.multi+1), None)
args.name = {}
- settings_cache: dict[str, tuple[argparse.Namespace, ...]] = \
- {fname: (tuple(roll_settings(yaml, args.plando) for yaml in yamls) if args.sameoptions else None)
- for fname, yamls in weights_cache.items()}
-
if meta_weights:
for category_name, category_dict in meta_weights.items():
for key in category_dict:
@@ -189,10 +197,32 @@ def main(args=None) -> tuple[argparse.Namespace, int]:
yaml[category][key] = option
elif category_name not in yaml:
logging.warning(f"Meta: Category {category_name} is not present in {path}.")
+ elif key == "triggers":
+ if "triggers" not in yaml[category_name]:
+ yaml[category_name][key] = []
+ for trigger in option:
+ yaml[category_name][key].append(trigger)
else:
yaml[category_name][key] = option
- player_path_cache = {}
+ settings_cache: dict[str, tuple[argparse.Namespace, ...]] = {fname: None for fname in weights_cache}
+ if args.sameoptions:
+ for fname, yamls in weights_cache.items():
+ try:
+ settings_cache[fname] = tuple(roll_settings(yaml, args.plando) for yaml in yamls)
+ except Exception as e:
+ logging.exception(f"Exception reading settings in file {fname}")
+ player_errors.append(
+ f"{len(player_errors) + 1}. "
+ f"File {fname} is invalid. Please fix your yaml.\n{Utils.get_all_causes(e)}"
+ )
+ # Exit early here to avoid throwing the same errors again later
+ if player_errors:
+ errors = "\n\n".join(player_errors)
+ raise ValueError(f"Encountered {len(player_errors)} error(s) in player files. "
+ f"See logs for full tracebacks.\n\n{errors}")
+
+ player_path_cache: dict[int, str] = {}
for player in range(1, args.multi + 1):
player_path_cache[player] = player_files.get(player, args.weights_file_path)
name_counter = Counter()
@@ -201,38 +231,62 @@ def main(args=None) -> tuple[argparse.Namespace, int]:
player = 1
while player <= args.multi:
path = player_path_cache[player]
- if path:
+ if not path:
+ player_errors.append(f'No weights specified for player {player}')
+ player += 1
+ continue
+
+ for doc_index, yaml in enumerate(weights_cache[path]):
+ name = yaml.get("name")
try:
- settings: tuple[argparse.Namespace, ...] = settings_cache[path] if settings_cache[path] else \
- tuple(roll_settings(yaml, args.plando) for yaml in weights_cache[path])
- for settingsObject in settings:
- for k, v in vars(settingsObject).items():
- if v is not None:
- try:
- getattr(args, k)[player] = v
- except AttributeError:
- setattr(args, k, {player: v})
- except Exception as e:
- raise Exception(f"Error setting {k} to {v} for player {player}") from e
-
- # name was not specified
- if player not in args.name:
- if path == args.weights_file_path:
- # weights file, so we need to make the name unique
- args.name[player] = f"Player{player}"
- else:
- # use the filename
- args.name[player] = os.path.splitext(os.path.split(path)[-1])[0]
- args.name[player] = handle_name(args.name[player], player, name_counter)
-
- player += 1
+ # Use the cached settings object if it exists, otherwise roll settings within the try-catch
+ # Invariant: settings_cache[path] and weights_cache[path] have the same length
+ settingsObject: argparse.Namespace = (
+ settings_cache[path][doc_index]
+ if settings_cache[path]
+ else roll_settings(yaml, args.plando)
+ )
+
+ for k, v in vars(settingsObject).items():
+ if v is not None:
+ try:
+ getattr(args, k)[player] = v
+ except AttributeError:
+ setattr(args, k, {player: v})
+ except Exception as e:
+ raise Exception(f"Error setting {k} to {v} for player {player}") from e
+
+ # name was not specified
+ if player not in args.name:
+ if path == args.weights_file_path:
+ # weights file, so we need to make the name unique
+ args.name[player] = f"Player{player}"
+ else:
+ # use the filename
+ args.name[player] = os.path.splitext(os.path.split(path)[-1])[0]
+ args.name[player] = handle_name(args.name[player], player, name_counter)
+
except Exception as e:
- raise ValueError(f"File {path} is invalid. Please fix your yaml.") from e
- else:
- raise RuntimeError(f'No weights specified for player {player}')
+ logging.exception(f"Exception reading settings in file {path} document #{doc_index + 1} "
+ f"(name: {args.name.get(player, name)})")
+ player_errors.append(
+ f"{len(player_errors) + 1}. "
+ f"File {path} document #{doc_index + 1} (name: {args.name.get(player, name)}) is invalid. "
+ f"Please fix your yaml.\n{Utils.get_all_causes(e)}")
+
+ # increment for each yaml document in the file
+ player += 1
if len(set(name.lower() for name in args.name.values())) != len(args.name):
- raise Exception(f"Names have to be unique. Names: {Counter(name.lower() for name in args.name.values())}")
+ player_errors.append(
+ f"{len(player_errors) + 1}. "
+ f"Names have to be unique. Names: {Counter(name.lower() for name in args.name.values())}"
+ )
+
+ if player_errors:
+ errors = "\n\n".join(player_errors)
+ raise ValueError(f"Encountered {len(player_errors)} error(s) in player files. "
+ f"See logs for full tracebacks.\n\n{errors}")
return args, seed
@@ -342,7 +396,9 @@ def update_weights(weights: dict, new_weights: dict, update_type: str, name: str
elif isinstance(new_value, list):
cleaned_value.extend(new_value)
elif isinstance(new_value, dict):
- cleaned_value = dict(Counter(cleaned_value) + Counter(new_value))
+ counter_value = Counter(cleaned_value)
+ counter_value.update(new_value)
+ cleaned_value = dict(counter_value)
else:
raise Exception(f"Cannot apply merge to non-dict, set, or list type {option_name},"
f" received {type(new_value).__name__}.")
@@ -356,13 +412,18 @@ def update_weights(weights: dict, new_weights: dict, update_type: str, name: str
for element in new_value:
cleaned_value.remove(element)
elif isinstance(new_value, dict):
- cleaned_value = dict(Counter(cleaned_value) - Counter(new_value))
+ counter_value = Counter(cleaned_value)
+ counter_value.subtract(new_value)
+ cleaned_value = dict(counter_value)
else:
raise Exception(f"Cannot apply remove to non-dict, set, or list type {option_name},"
f" received {type(new_value).__name__}.")
cleaned_weights[option_name] = cleaned_value
else:
- cleaned_weights[option_name] = new_weights[option]
+ # Options starting with + and - may modify values in-place, and new_weights may be shared by multiple slots
+ # using the same .yaml, so ensure that the new value is a copy.
+ cleaned_value = copy.deepcopy(new_weights[option])
+ cleaned_weights[option_name] = cleaned_value
new_options = set(cleaned_weights) - set(weights)
weights.update(cleaned_weights)
if new_options:
@@ -385,6 +446,8 @@ def roll_meta_option(option_key, game: str, category_dict: dict) -> Any:
if options[option_key].supports_weighting:
return get_choice(option_key, category_dict)
return category_dict[option_key]
+ if option_key == "triggers":
+ return category_dict[option_key]
raise Options.OptionError(f"Error generating meta option {option_key} for {game}.")
diff --git a/Launcher.py b/Launcher.py
index adc3cb96ef24..89421ff30508 100644
--- a/Launcher.py
+++ b/Launcher.py
@@ -75,12 +75,17 @@ def open_patch():
launch([*exe, file], component.cli)
-def generate_yamls():
+def generate_yamls(*args):
from Options import generate_yaml_templates
+ parser = argparse.ArgumentParser(description="Generate Template Options", usage="[-h] [--skip_open_folder]")
+ parser.add_argument("--skip_open_folder", action="store_true")
+ args = parser.parse_args(args)
+
target = Utils.user_path("Players", "Templates")
generate_yaml_templates(target, False)
- open_folder(target)
+ if not args.skip_open_folder:
+ open_folder(target)
def browse_files():
@@ -213,12 +218,17 @@ def launch(exe, in_terminal=False):
def create_shortcut(button: Any, component: Component) -> None:
from pyshortcuts import make_shortcut
- script = sys.argv[0]
- wkdir = Utils.local_path()
+ env = os.environ
+ if "APPIMAGE" in env:
+ script = env["ARGV0"]
+ wkdir = None # defaults to ~ on Linux
+ else:
+ script = sys.argv[0]
+ wkdir = Utils.local_path()
script = f"{script} \"{component.display_name}\""
make_shortcut(script, name=f"Archipelago {component.display_name}", icon=local_path("data", "icon.ico"),
- startmenu=False, terminal=False, working_dir=wkdir)
+ startmenu=False, terminal=False, working_dir=wkdir, noexe=Utils.is_frozen())
button.menu.dismiss()
diff --git a/Main.py b/Main.py
index 892baa8d4fa5..47a28813fce4 100644
--- a/Main.py
+++ b/Main.py
@@ -326,7 +326,7 @@ def precollect_hint(location: Location, auto_status: HintStatus):
if current_sphere:
spheres.append(dict(current_sphere))
- multidata: NetUtils.MultiData | bytes = {
+ multidata: NetUtils.MultiData = {
"slot_data": slot_data,
"slot_info": slot_info,
"connect_names": {name: (0, player) for player, name in multiworld.player_name.items()},
@@ -350,11 +350,11 @@ def precollect_hint(location: Location, auto_status: HintStatus):
for key in ("slot_data", "er_hint_data"):
multidata[key] = convert_to_base_types(multidata[key])
- multidata = zlib.compress(restricted_dumps(multidata), 9)
+ serialized_multidata = zlib.compress(restricted_dumps(multidata), 9)
with open(os.path.join(temp_dir, f'{outfilebase}.archipelago'), 'wb') as f:
f.write(bytes([3])) # version of format
- f.write(multidata)
+ f.write(serialized_multidata)
output_file_futures.append(pool.submit(write_multidata))
if not check_accessibility_task.result():
diff --git a/MultiServer.py b/MultiServer.py
index 23aab7b58b7d..d58b46639695 100644
--- a/MultiServer.py
+++ b/MultiServer.py
@@ -32,7 +32,7 @@
import colorama
import websockets
-from websockets.extensions.permessage_deflate import PerMessageDeflate
+from websockets.extensions.permessage_deflate import PerMessageDeflate, ServerPerMessageDeflateFactory
try:
# ponyorm is a requirement for webhost, not default server, so may not be importable
from pony.orm.dbapiprovider import OperationalError
@@ -50,6 +50,15 @@
min_client_version = Version(0, 5, 0)
colorama.just_fix_windows_console()
+no_version = Version(0, 0, 0)
+assert isinstance(no_version, tuple) # assert immutable
+
+server_per_message_deflate_factory = ServerPerMessageDeflateFactory(
+ server_max_window_bits=11,
+ client_max_window_bits=11,
+ compress_settings={"memLevel": 4},
+)
+
def remove_from_list(container, value):
try:
@@ -60,6 +69,12 @@ def remove_from_list(container, value):
def pop_from_container(container, value):
+ if isinstance(container, list) and isinstance(value, int) and len(container) <= value:
+ return container
+
+ if isinstance(container, dict) and value not in container:
+ return container
+
try:
container.pop(value)
except ValueError:
@@ -125,8 +140,31 @@ def get_saving_second(seed_name: str, interval: int = 60) -> int:
class Client(Endpoint):
- version = Version(0, 0, 0)
- tags: typing.List[str]
+ __slots__ = (
+ "__weakref__",
+ "version",
+ "auth",
+ "team",
+ "slot",
+ "send_index",
+ "tags",
+ "messageprocessor",
+ "ctx",
+ "remote_items",
+ "remote_start_inventory",
+ "no_items",
+ "no_locations",
+ "no_text",
+ )
+
+ version: Version
+ auth: bool
+ team: int | None
+ slot: int | None
+ send_index: int
+ tags: list[str]
+ messageprocessor: ClientMessageProcessor
+ ctx: weakref.ref[Context]
remote_items: bool
remote_start_inventory: bool
no_items: bool
@@ -135,6 +173,7 @@ class Client(Endpoint):
def __init__(self, socket: "ServerConnection", ctx: Context) -> None:
super().__init__(socket)
+ self.version = no_version
self.auth = False
self.team = None
self.slot = None
@@ -142,6 +181,11 @@ def __init__(self, socket: "ServerConnection", ctx: Context) -> None:
self.tags = []
self.messageprocessor = client_message_processor(ctx, self)
self.ctx = weakref.ref(ctx)
+ self.remote_items = False
+ self.remote_start_inventory = False
+ self.no_items = False
+ self.no_locations = False
+ self.no_text = False
@property
def items_handling(self):
@@ -179,6 +223,7 @@ class Context:
"release_mode": str,
"remaining_mode": str,
"collect_mode": str,
+ "countdown_mode": str,
"item_cheat": bool,
"compatibility": int}
# team -> slot id -> list of clients authenticated to slot.
@@ -208,8 +253,8 @@ class Context:
def __init__(self, host: str, port: int, server_password: str, password: str, location_check_points: int,
hint_cost: int, item_cheat: bool, release_mode: str = "disabled", collect_mode="disabled",
- remaining_mode: str = "disabled", auto_shutdown: typing.SupportsFloat = 0, compatibility: int = 2,
- log_network: bool = False, logger: logging.Logger = logging.getLogger()):
+ countdown_mode: str = "auto", remaining_mode: str = "disabled", auto_shutdown: typing.SupportsFloat = 0,
+ compatibility: int = 2, log_network: bool = False, logger: logging.Logger = logging.getLogger()):
self.logger = logger
super(Context, self).__init__()
self.slot_info = {}
@@ -242,6 +287,7 @@ def __init__(self, host: str, port: int, server_password: str, password: str, lo
self.release_mode: str = release_mode
self.remaining_mode: str = remaining_mode
self.collect_mode: str = collect_mode
+ self.countdown_mode: str = countdown_mode
self.item_cheat = item_cheat
self.exit_event = asyncio.Event()
self.client_activity_timers: typing.Dict[
@@ -453,7 +499,7 @@ def _load(self, decoded_obj: MultiData, game_data_packages: typing.Dict[str, typ
self.read_data["race_mode"] = lambda: decoded_obj.get("race_mode", 0)
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},"
+ raise RuntimeError(f"Supplied Multidata (.archipelago) requires a server of at least version {mdata_ver}, "
f"however this server is of version {version_tuple}")
self.generator_version = Version(*decoded_obj["version"])
clients_ver = decoded_obj["minimum_versions"].get("clients", {})
@@ -627,6 +673,7 @@ def get_save(self) -> dict:
"server_password": self.server_password, "password": self.password,
"release_mode": self.release_mode,
"remaining_mode": self.remaining_mode, "collect_mode": self.collect_mode,
+ "countdown_mode": self.countdown_mode,
"item_cheat": self.item_cheat, "compatibility": self.compatibility}
}
@@ -661,6 +708,7 @@ def set_save(self, savedata: dict):
self.release_mode = savedata["game_options"]["release_mode"]
self.remaining_mode = savedata["game_options"]["remaining_mode"]
self.collect_mode = savedata["game_options"]["collect_mode"]
+ self.countdown_mode = savedata["game_options"].get("countdown_mode", self.countdown_mode)
self.item_cheat = savedata["game_options"]["item_cheat"]
self.compatibility = savedata["game_options"]["compatibility"]
@@ -1158,16 +1206,17 @@ def collect_hints(ctx: Context, team: int, slot: int, item: typing.Union[int, st
found = location_id in ctx.location_checks[team, finding_player]
entrance = ctx.er_hint_data.get(finding_player, {}).get(location_id, "")
+ hint_status = status # Assign again because we're in a for loop
if found:
- status = HintStatus.HINT_FOUND
- elif status is None:
+ hint_status = HintStatus.HINT_FOUND
+ elif hint_status is None:
if item_flags & ItemClassification.trap:
- status = HintStatus.HINT_AVOID
+ hint_status = HintStatus.HINT_AVOID
else:
- status = HintStatus.HINT_PRIORITY
+ hint_status = HintStatus.HINT_PRIORITY
hints.append(
- Hint(receiving_player, finding_player, location_id, item_id, found, entrance, item_flags, status)
+ Hint(receiving_player, finding_player, location_id, item_id, found, entrance, item_flags, hint_status)
)
return hints
@@ -1492,6 +1541,23 @@ def _cmd_collect(self) -> bool:
" You can ask the server admin for a /collect")
return False
+ def _cmd_countdown(self, seconds: str = "10") -> bool:
+ """Start a countdown in seconds"""
+ if self.ctx.countdown_mode == "disabled" or \
+ self.ctx.countdown_mode == "auto" and len(self.ctx.player_names) >= 30:
+ self.output("Sorry, client countdowns have been disabled on this server. You can ask the server admin for a /countdown")
+ return False
+ try:
+ timer = int(seconds, 10)
+ except ValueError:
+ timer = 10
+ else:
+ if timer > 60 * 60:
+ raise ValueError(f"{timer} is invalid. Maximum is 1 hour.")
+
+ async_start(countdown(self.ctx, timer))
+ return True
+
def _cmd_remaining(self) -> bool:
"""List remaining items in your game, but not their location or recipient"""
if self.ctx.remaining_mode == "enabled":
@@ -2452,6 +2518,11 @@ def value_type(input_text: str):
elif value_type == str and option_name.endswith("password"):
def value_type(input_text: str):
return None if input_text.lower() in {"null", "none", '""', "''"} else input_text
+ elif option_name == "countdown_mode":
+ valid_values = {"enabled", "disabled", "auto"}
+ if option_value.lower() not in valid_values:
+ self.output(f"Unrecognized {option_name} value '{option_value}', known: {', '.join(valid_values)}")
+ return False
elif value_type == str and option_name.endswith("mode"):
valid_values = {"goal", "enabled", "disabled"}
valid_values.update(("auto", "auto_enabled") if option_name != "remaining_mode" else [])
@@ -2539,6 +2610,13 @@ def parse_args() -> argparse.Namespace:
goal: !collect can be used after goal completion
auto-enabled: !collect is available and automatically triggered on goal completion
''')
+ parser.add_argument('--countdown_mode', default=defaults["countdown_mode"], nargs='?',
+ choices=['enabled', 'disabled', "auto"], help='''\
+ Select !countdown Accessibility. (default: %(default)s)
+ enabled: !countdown is always available
+ disabled: !countdown is never available
+ auto: !countdown is available for rooms with less than 30 players
+ ''')
parser.add_argument('--remaining_mode', default=defaults["remaining_mode"], nargs='?',
choices=['enabled', 'disabled', "goal"], help='''\
Select !remaining Accessibility. (default: %(default)s)
@@ -2604,7 +2682,7 @@ async def main(args: argparse.Namespace):
ctx = Context(args.host, args.port, args.server_password, args.password, args.location_check_points,
args.hint_cost, not args.disable_item_cheat, args.release_mode, args.collect_mode,
- args.remaining_mode,
+ args.countdown_mode, args.remaining_mode,
args.auto_shutdown, args.compatibility, args.log_network)
data_filename = args.multidata
@@ -2639,7 +2717,13 @@ async def main(args: argparse.Namespace):
ssl_context = load_server_cert(args.cert, args.cert_key) if args.cert else None
- ctx.server = websockets.serve(functools.partial(server, ctx=ctx), host=ctx.host, port=ctx.port, ssl=ssl_context)
+ ctx.server = websockets.serve(
+ functools.partial(server, ctx=ctx),
+ host=ctx.host,
+ port=ctx.port,
+ ssl=ssl_context,
+ extensions=[server_per_message_deflate_factory],
+ )
ip = args.host if args.host else Utils.get_public_ipv4()
logging.info('Hosting game at %s:%d (%s)' % (ip, ctx.port,
'No password' if not ctx.password else 'Password: %s' % ctx.password))
diff --git a/NetUtils.py b/NetUtils.py
index 45279183f631..f61dbf9fcb0f 100644
--- a/NetUtils.py
+++ b/NetUtils.py
@@ -174,6 +174,8 @@ def _object_hook(o: typing.Any) -> typing.Any:
class Endpoint:
+ __slots__ = ("socket",)
+
socket: "ServerConnection"
def __init__(self, socket):
diff --git a/Options.py b/Options.py
index 826e45a52ae0..c37d0cee2810 100644
--- a/Options.py
+++ b/Options.py
@@ -688,6 +688,12 @@ class Range(NumericOption):
range_start = 0
range_end = 1
+ _RANDOM_OPTS = [
+ "random", "random-low", "random-middle", "random-high",
+ "random-range-low--", "random-range-middle--",
+ "random-range-high--", "random-range--",
+ ]
+
def __init__(self, value: int):
if value < self.range_start:
raise Exception(f"{value} is lower than minimum {self.range_start} for option {self.__class__.__name__}")
@@ -713,9 +719,26 @@ def from_text(cls, text: str) -> Range:
# these are the conditions where "true" and "false" make sense
if text == "true":
return cls.from_any(cls.default)
- else: # "false"
- return cls(0)
- return cls(int(text))
+ # "false"
+ return cls(0)
+
+ try:
+ num = int(text)
+ except ValueError:
+ # text is not a number
+ # Handle conditionally acceptable values here rather than in the f-string
+ default = ""
+ truefalse = ""
+ if hasattr(cls, "default"):
+ default = ", default"
+ if cls.range_start == 0 and cls.default != 0:
+ truefalse = ", \"true\", \"false\""
+ raise Exception(f"Invalid range value {text!r}. Acceptable values are: "
+ f"{default}, high, low{truefalse}, "
+ f"{', '.join(cls._RANDOM_OPTS)}.")
+
+ return cls(num)
+
@classmethod
def weighted_range(cls, text) -> Range:
@@ -731,9 +754,7 @@ def weighted_range(cls, text) -> Range:
return cls(random.randint(cls.range_start, cls.range_end))
else:
raise Exception(f"random text \"{text}\" did not resolve to a recognized pattern. "
- f"Acceptable values are: random, random-high, random-middle, random-low, "
- f"random-range-low--, random-range-middle--, "
- f"random-range-high--, or random-range--.")
+ f"Acceptable values are: {', '.join(cls._RANDOM_OPTS)}.")
@classmethod
def custom_range(cls, text) -> Range:
@@ -1018,6 +1039,8 @@ class PlandoTexts(Option[typing.List[PlandoText]], VerifyKeys):
supports_weighting = False
display_name = "Plando Texts"
+ visibility = Visibility.template | Visibility.complex_ui | Visibility.spoiler
+
def __init__(self, value: typing.Iterable[PlandoText]) -> None:
self.value = list(deepcopy(value))
super().__init__()
@@ -1144,6 +1167,8 @@ class PlandoConnections(Option[typing.List[PlandoConnection]], metaclass=Connect
entrances: typing.ClassVar[typing.AbstractSet[str]]
exits: typing.ClassVar[typing.AbstractSet[str]]
+ visibility = Visibility.template | Visibility.complex_ui | Visibility.spoiler
+
duplicate_exits: bool = False
"""Whether or not exits should be allowed to be duplicate."""
@@ -1435,6 +1460,7 @@ class DeathLink(Toggle):
class ItemLinks(OptionList):
"""Share part of your item pool with other players."""
display_name = "Item Links"
+ visibility = Visibility.template | Visibility.complex_ui | Visibility.spoiler
rich_text_doc = True
default = []
schema = Schema([
@@ -1474,8 +1500,10 @@ def verify(self, world: typing.Type[World], player_name: str, plando_options: "P
super(ItemLinks, self).verify(world, player_name, plando_options)
existing_links = set()
for link in self.value:
+ link["name"] = link["name"].strip()[:16].strip()
if link["name"] in existing_links:
- raise Exception(f"You cannot have more than one link named {link['name']}.")
+ raise Exception(f"Item link names are limited to their first 16 characters and must be unique. "
+ f"You have more than one link named '{link['name']}'.")
existing_links.add(link["name"])
pool = self.verify_items(link["item_pool"], link["name"], "item_pool", world)
@@ -1517,6 +1545,7 @@ class PlandoItems(Option[typing.List[PlandoItem]]):
default = ()
supports_weighting = False
display_name = "Plando Items"
+ visibility = Visibility.template | Visibility.spoiler
def __init__(self, value: typing.Iterable[PlandoItem]) -> None:
self.value = list(deepcopy(value))
@@ -1724,11 +1753,16 @@ def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], ge
def dictify_range(option: Range):
data = {option.default: 50}
- for sub_option in ["random", "random-low", "random-high"]:
+ for sub_option in ["random", "random-low", "random-high",
+ f"random-range-{option.range_start}-{option.range_end}"]:
if sub_option != option.default:
data[sub_option] = 0
-
- notes = {}
+ notes = {
+ "random-low": "random value weighted towards lower values",
+ "random-high": "random value weighted towards higher values",
+ f"random-range-{option.range_start}-{option.range_end}": f"random value between "
+ f"{option.range_start} and {option.range_end}"
+ }
for name, number in getattr(option, "special_range_names", {}).items():
notes[name] = f"equivalent to {number}"
if number in data:
diff --git a/OptionsCreator.py b/OptionsCreator.py
new file mode 100644
index 000000000000..05a0d58ccde0
--- /dev/null
+++ b/OptionsCreator.py
@@ -0,0 +1,674 @@
+if __name__ == "__main__":
+ import ModuleUpdate
+
+ ModuleUpdate.update()
+
+
+from kvui import (ThemedApp, ScrollBox, MainLayout, ContainerLayout, dp, Widget, MDBoxLayout, TooltipLabel, MDLabel,
+ ToggleButton, MarkupDropdown, ResizableTextField)
+from kivy.uix.behaviors.button import ButtonBehavior
+from kivymd.uix.behaviors import RotateBehavior
+from kivymd.uix.anchorlayout import MDAnchorLayout
+from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelContent, MDExpansionPanelHeader
+from kivymd.uix.list import MDListItem, MDListItemTrailingIcon, MDListItemSupportingText
+from kivymd.uix.slider import MDSlider
+from kivymd.uix.snackbar import MDSnackbar, MDSnackbarText
+from kivymd.uix.menu import MDDropdownMenu
+from kivymd.uix.button import MDButton, MDButtonText, MDIconButton
+from kivymd.uix.dialog import MDDialog
+from kivy.core.text.markup import MarkupLabel
+from kivy.utils import escape_markup
+from kivy.lang.builder import Builder
+from kivy.properties import ObjectProperty
+from textwrap import dedent
+from copy import deepcopy
+import Utils
+import typing
+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,
+ OptionCounter, Visibility)
+
+
+def validate_url(x):
+ try:
+ result = urlparse(x)
+ return all([result.scheme, result.netloc])
+ except AttributeError:
+ return False
+
+
+def filter_tooltip(tooltip):
+ if tooltip is None:
+ tooltip = "No tooltip available."
+ tooltip = dedent(tooltip).strip().replace("\n", " ").replace("&", "&") \
+ .replace("[", "&bl;").replace("]", "&br;")
+ tooltip = re.sub(r"\*\*(.+?)\*\*", r"[b]\g<1>[/b]", tooltip)
+ tooltip = re.sub(r"\*(.+?)\*", r"[i]\g<1>[/i]", tooltip)
+ return escape_markup(tooltip)
+
+
+def option_can_be_randomized(option: typing.Type[Option]):
+ # most options can be randomized, so we should just check for those that cannot
+ if not option.supports_weighting:
+ return False
+ elif issubclass(option, FreeText) and not issubclass(option, TextChoice):
+ return False
+ return True
+
+
+def check_random(value: typing.Any):
+ if not isinstance(value, str):
+ return value # cannot be random if evaluated
+ if value.startswith("random-"):
+ return "random"
+ return value
+
+
+class TrailingPressedIconButton(ButtonBehavior, RotateBehavior, MDListItemTrailingIcon):
+ pass
+
+
+class WorldButton(ToggleButton):
+ world_cls: typing.Type[World]
+
+
+class VisualRange(MDBoxLayout):
+ option: typing.Type[Range]
+ name: str
+ tag: MDLabel = ObjectProperty(None)
+ slider: MDSlider = ObjectProperty(None)
+
+ def __init__(self, *args, option: typing.Type[Range], name: str, **kwargs):
+ self.option = option
+ self.name = name
+ super().__init__(*args, **kwargs)
+
+ def update_points(*update_args):
+ pass
+
+ self.slider._update_points = update_points
+
+
+class VisualChoice(MDButton):
+ option: typing.Type[Choice]
+ name: str
+ text: MDButtonText = ObjectProperty(None)
+
+ def __init__(self, *args, option: typing.Type[Choice], name: str, **kwargs):
+ self.option = option
+ self.name = name
+ super().__init__(*args, **kwargs)
+
+
+class VisualNamedRange(MDBoxLayout):
+ option: typing.Type[NamedRange]
+ name: str
+ range: VisualRange = ObjectProperty(None)
+ choice: MDButton = ObjectProperty(None)
+
+ def __init__(self, *args, option: typing.Type[NamedRange], name: str, range_widget: VisualRange, **kwargs):
+ self.option = option
+ self.name = name
+ super().__init__(*args, **kwargs)
+ self.range = range_widget
+ self.add_widget(self.range)
+
+
+class VisualFreeText(ResizableTextField):
+ option: typing.Type[FreeText] | typing.Type[TextChoice]
+ name: str
+
+ def __init__(self, *args, option: typing.Type[FreeText] | typing.Type[TextChoice], name: str, **kwargs):
+ self.option = option
+ self.name = name
+ super().__init__(*args, **kwargs)
+
+
+class VisualTextChoice(MDBoxLayout):
+ option: typing.Type[TextChoice]
+ name: str
+ choice: VisualChoice = ObjectProperty(None)
+ text: VisualFreeText = ObjectProperty(None)
+
+ def __init__(self, *args, option: typing.Type[TextChoice], name: str, choice: VisualChoice,
+ text: VisualFreeText, **kwargs):
+ self.option = option
+ self.name = name
+ super(MDBoxLayout, self).__init__(*args, **kwargs)
+ self.choice = choice
+ self.text = text
+ self.add_widget(self.choice)
+ self.add_widget(self.text)
+
+
+class VisualToggle(MDBoxLayout):
+ button: MDIconButton = ObjectProperty(None)
+ option: typing.Type[Toggle]
+ name: str
+
+ def __init__(self, *args, option: typing.Type[Toggle], name: str, **kwargs):
+ self.option = option
+ self.name = name
+ super().__init__(*args, **kwargs)
+
+
+class CounterItemValue(ResizableTextField):
+ pat = re.compile('[^0-9]')
+
+ def insert_text(self, substring, from_undo=False):
+ return super().insert_text(re.sub(self.pat, "", substring), from_undo=from_undo)
+
+
+class VisualListSetCounter(MDDialog):
+ button: MDIconButton = ObjectProperty(None)
+ option: typing.Type[OptionSet] | typing.Type[OptionList] | typing.Type[OptionCounter]
+ scrollbox: ScrollBox = ObjectProperty(None)
+ add: MDIconButton = ObjectProperty(None)
+ save: MDButton = ObjectProperty(None)
+ input: ResizableTextField = ObjectProperty(None)
+ dropdown: MDDropdownMenu
+ valid_keys: typing.Iterable[str]
+
+ def __init__(self, *args, option: typing.Type[OptionSet] | typing.Type[OptionList],
+ name: str, valid_keys: typing.Iterable[str], **kwargs):
+ self.option = option
+ self.name = name
+ self.valid_keys = valid_keys
+ super().__init__(*args, **kwargs)
+ self.dropdown = MarkupDropdown(caller=self.input, border_margin=dp(2),
+ width=self.input.width, position="bottom")
+ self.input.bind(text=self.on_text)
+ self.input.bind(on_text_validate=self.validate_add)
+
+ def validate_add(self, instance):
+ if self.valid_keys:
+ if self.input.text not in self.valid_keys:
+ MDSnackbar(MDSnackbarText(text="Item must be a valid key for this option."), y=dp(24),
+ pos_hint={"center_x": 0.5}, size_hint_x=0.5).open()
+ return
+
+ if not issubclass(self.option, OptionList):
+ if any(self.input.text == child.text.text for child in self.scrollbox.layout.children):
+ MDSnackbar(MDSnackbarText(text="This value is already in the set."), y=dp(24),
+ pos_hint={"center_x": 0.5}, size_hint_x=0.5).open()
+ return
+
+ self.add_set_item(self.input.text)
+ self.input.set_text(self.input, "")
+
+ def remove_item(self, button: MDIconButton):
+ list_item = button.parent
+ self.scrollbox.layout.remove_widget(list_item)
+
+ def add_set_item(self, key: str, value: int | None = None):
+ text = MDListItemSupportingText(text=key, id="value")
+ if issubclass(self.option, OptionCounter):
+ value_txt = CounterItemValue(text=str(value) if value else "1")
+ item = MDListItem(text,
+ value_txt,
+ MDIconButton(icon="minus", on_release=self.remove_item), focus_behavior=False)
+ item.value = value_txt
+ else:
+ item = MDListItem(text, MDIconButton(icon="minus", on_release=self.remove_item), focus_behavior=False)
+ item.text = text
+ self.scrollbox.layout.add_widget(item)
+
+ def on_text(self, instance, value):
+ if not self.valid_keys:
+ return
+ if len(value) >= 3:
+ self.dropdown.items.clear()
+
+ def on_press(txt):
+ split_text = MarkupLabel(text=txt, markup=True).markup
+ self.input.set_text(self.input, "".join(text_frag for text_frag in split_text
+ if not text_frag.startswith("[")))
+ self.input.focus = True
+ self.dropdown.dismiss()
+
+ lowered = value.lower()
+ for item_name in self.valid_keys:
+ try:
+ index = item_name.lower().index(lowered)
+ except ValueError:
+ pass # substring not found
+ else:
+ text = escape_markup(item_name)
+ text = text[:index] + "[b]" + text[index:index + len(value)] + "[/b]" + text[index + len(value):]
+ self.dropdown.items.append({
+ "text": text,
+ "on_release": lambda txt=text: on_press(txt),
+ "markup": True
+ })
+ if not self.dropdown.parent:
+ self.dropdown.open()
+ else:
+ self.dropdown.dismiss()
+
+
+class OptionsCreator(ThemedApp):
+ base_title: str = "Archipelago Options Creator"
+ container: ContainerLayout
+ main_layout: MainLayout
+ scrollbox: ScrollBox
+ main_panel: MainLayout
+ player_options: MainLayout
+ option_layout: MainLayout
+ name_input: ResizableTextField
+ game_label: MDLabel
+ current_game: str
+ options: typing.Dict[str, typing.Any]
+
+ def __init__(self):
+ self.title = self.base_title + " " + Utils.__version__
+ self.icon = r"data/icon.png"
+ self.current_game = ""
+ self.options = {}
+ super().__init__()
+
+ def export_options(self, button: Widget):
+ if 0 < len(self.name_input.text) < 17 and self.current_game:
+ file_name = Utils.save_filename("Export Options File As...", [("YAML", ["*.yaml"])],
+ Utils.get_file_safe_name(f"{self.name_input.text}.yaml"))
+ options = {
+ "name": self.name_input.text,
+ "description": f"YAML generated by Archipelago {Utils.__version__}.",
+ "game": self.current_game,
+ self.current_game: {k: check_random(v) for k, v in self.options.items()}
+ }
+ try:
+ with open(file_name, 'w') as f:
+ f.write(Utils.dump(options, sort_keys=False))
+ f.close()
+ MDSnackbar(MDSnackbarText(text="File saved successfully."), y=dp(24), pos_hint={"center_x": 0.5},
+ size_hint_x=0.5).open()
+ except FileNotFoundError:
+ MDSnackbar(MDSnackbarText(text="Saving cancelled."), y=dp(24), pos_hint={"center_x": 0.5},
+ size_hint_x=0.5).open()
+ elif not self.name_input.text:
+ MDSnackbar(MDSnackbarText(text="Name must not be empty."), y=dp(24), pos_hint={"center_x": 0.5},
+ size_hint_x=0.5).open()
+ elif not self.current_game:
+ MDSnackbar(MDSnackbarText(text="You must select a game to play."), y=dp(24), pos_hint={"center_x": 0.5},
+ size_hint_x=0.5).open()
+ else:
+ MDSnackbar(MDSnackbarText(text="Name cannot be longer than 16 characters."), y=dp(24),
+ pos_hint={"center_x": 0.5}, size_hint_x=0.5).open()
+
+ def create_range(self, option: typing.Type[Range], name: str):
+ 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))
+ 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
+ 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):
+ button.text.text = text
+
+ 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))
+ set_button_text(range_box.choice, text)
+ self.options[name] = text.lower()
+ range_box.range.slider.dropdown.dismiss()
+
+ def open_dropdown(button):
+ # 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:
+ # value can get mismatched in this case
+ box.range.slider.value = min(max(option.special_range_names[option.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))
+ items = [
+ {
+ "text": choice.title(),
+ "on_release": lambda text=choice.title(): set_value(text, box)
+ }
+ for choice in option.special_range_names
+ ]
+ box.range.slider.dropdown = MDDropdownMenu(caller=box.choice, items=items)
+ box.choice.bind(on_release=open_dropdown)
+ self.options[name] = option.default
+ return box
+
+ def create_free_text(self, option: typing.Type[FreeText] | typing.Type[TextChoice], name: str):
+ text = VisualFreeText(option=option, name=name)
+
+ def set_value(instance):
+ self.options[name] = instance.text
+
+ text.bind(on_text_validate=set_value)
+ return text
+
+ def create_choice(self, option: typing.Type[Choice], name: str):
+ def set_button_text(button: VisualChoice, text: str):
+ button.text.text = text
+
+ def set_value(text, value):
+ set_button_text(main_button, text)
+ self.options[name] = value
+ dropdown.dismiss()
+
+ def open_dropdown(button):
+ # for some reason this fixes an issue causing some to not open
+ dropdown.open()
+
+ default_string = isinstance(option.default, str)
+ main_button = VisualChoice(option=option, name=name)
+ main_button.bind(on_release=open_dropdown)
+
+ items = [
+ {
+ "text": option.get_option_name(choice),
+ "on_release": lambda val=choice: set_value(option.get_option_name(val), option.name_lookup[val])
+ }
+ for choice in option.name_lookup
+ ]
+ dropdown = MDDropdownMenu(caller=main_button, items=items)
+ self.options[name] = option.name_lookup[option.default] if not default_string else option.default
+ return main_button
+
+ def create_text_choice(self, option: typing.Type[TextChoice], name: str):
+ def set_button_text(button: MDButton, text: str):
+ for child in button.children:
+ if isinstance(child, MDButtonText):
+ child.text = text
+
+ box = VisualTextChoice(option=option, name=name, choice=self.create_choice(option, name),
+ text=self.create_free_text(option, name))
+
+ def set_value(instance):
+ set_button_text(box.choice, "Custom")
+ self.options[name] = instance.text
+
+ box.text.bind(on_text_validate=set_value)
+ return box
+
+ def create_toggle(self, option: typing.Type[Toggle], name: str) -> Widget:
+ def set_value(instance: MDIconButton):
+ if instance.icon == "checkbox-outline":
+ instance.icon = "checkbox-blank-outline"
+ else:
+ instance.icon = "checkbox-outline"
+ self.options[name] = bool(not self.options[name])
+
+ self.options[name] = bool(option.default)
+ checkbox = VisualToggle(option=option, name=name)
+ checkbox.button.bind(on_release=set_value)
+
+ return checkbox
+
+ def create_popup(self, option: typing.Type[OptionList] | typing.Type[OptionSet] | typing.Type[OptionCounter],
+ name: str, world: typing.Type[World]):
+
+ valid_keys = sorted(option.valid_keys)
+ if option.verify_item_name:
+ valid_keys += list(world.item_name_to_id.keys())
+ if option.verify_location_name:
+ valid_keys += list(world.location_name_to_id.keys())
+
+ if not issubclass(option, OptionCounter):
+ def apply_changes(button):
+ self.options[name].clear()
+ for list_item in dialog.scrollbox.layout.children:
+ self.options[name].append(getattr(list_item.text, "text"))
+ dialog.dismiss()
+ else:
+ def apply_changes(button):
+ self.options[name].clear()
+ for list_item in dialog.scrollbox.layout.children:
+ self.options[name][getattr(list_item.text, "text")] = int(getattr(list_item.value, "text"))
+ dialog.dismiss()
+
+ dialog = VisualListSetCounter(option=option, name=name, valid_keys=valid_keys)
+ dialog.ids.container.spacing = dp(30)
+ dialog.scrollbox.layout.theme_bg_color = "Custom"
+ dialog.scrollbox.layout.md_bg_color = self.theme_cls.surfaceContainerLowColor
+ 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))
+ else:
+ for value in sorted(self.options[name]):
+ dialog.add_set_item(value)
+
+ dialog.save.bind(on_release=apply_changes)
+ dialog.open()
+
+ 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))
+ return main_button
+
+ def create_option(self, option: typing.Type[Option], name: str, world: typing.Type[World]) -> Widget:
+ option_base = MDBoxLayout(orientation="vertical", size_hint_y=None, padding=[0, 0, dp(5), dp(5)])
+
+ tooltip = filter_tooltip(option.__doc__)
+ option_label = TooltipLabel(text=f"[ref=0|{tooltip}]{getattr(option, 'display_name', name)}")
+ label_box = MDBoxLayout(orientation="horizontal")
+ label_anchor = MDAnchorLayout(anchor_x="right", anchor_y="center")
+ label_anchor.add_widget(option_label)
+ label_box.add_widget(label_anchor)
+
+ option_base.add_widget(label_box)
+ if issubclass(option, NamedRange):
+ option_base.add_widget(self.create_named_range(option, name))
+ elif issubclass(option, Range):
+ option_base.add_widget(self.create_range(option, name))
+ elif issubclass(option, Toggle):
+ option_base.add_widget(self.create_toggle(option, name))
+ elif issubclass(option, TextChoice):
+ option_base.add_widget(self.create_text_choice(option, name))
+ elif issubclass(option, Choice):
+ option_base.add_widget(self.create_choice(option, name))
+ elif issubclass(option, FreeText):
+ option_base.add_widget(self.create_free_text(option, name))
+ elif any(issubclass(option, cls) for cls in (OptionSet, OptionList, OptionCounter)):
+ option_base.add_widget(self.create_option_set_list_counter(option, name, world))
+ else:
+ option_base.add_widget(MDLabel(text="This option isn't supported by the option creator.\n"
+ "Please edit your yaml manually to set this option."))
+
+ if option_can_be_randomized(option):
+ def randomize_option(instance: Widget, value: str):
+ value = value == "down"
+ if value:
+ self.options[name] = "random-" + str(self.options[name])
+ else:
+ self.options[name] = self.options[name].replace("random-", "")
+ if self.options[name].isnumeric() or self.options[name] in ("True", "False"):
+ self.options[name] = eval(self.options[name])
+
+ base_object = instance.parent.parent
+ label_object = instance.parent
+ for child in base_object.children:
+ if child is not label_object:
+ child.disabled = value
+
+ default_random = option.default == "random"
+ random_toggle = ToggleButton(MDButtonText(text="Random?"), size_hint_x=None, width=dp(100),
+ state="down" if default_random else "normal")
+ random_toggle.bind(state=randomize_option)
+ label_box.add_widget(random_toggle)
+ if default_random:
+ randomize_option(random_toggle, "down")
+
+ return option_base
+
+ def create_options_panel(self, world_button: WorldButton):
+ self.option_layout.clear_widgets()
+ self.options.clear()
+ cls: typing.Type[World] = world_button.world_cls
+
+ self.current_game = cls.game
+ if not cls.web.options_page:
+ self.current_game = "None"
+ return
+ elif isinstance(cls.web.options_page, str):
+ self.current_game = "None"
+ if validate_url(cls.web.options_page):
+ webbrowser.open(cls.web.options_page)
+ MDSnackbar(MDSnackbarText(text="Launching in default browser..."), y=dp(24), pos_hint={"center_x": 0.5},
+ size_hint_x=0.5).open()
+ world_button.state = "normal"
+ else:
+ # attach onto archipelago.gg and see if we pass
+ new_url = "https://archipelago.gg/" + cls.web.options_page
+ if validate_url(new_url):
+ webbrowser.open(new_url)
+ MDSnackbar(MDSnackbarText(text="Launching in default browser..."), y=dp(24),
+ pos_hint={"center_x": 0.5},
+ size_hint_x=0.5).open()
+ else:
+ MDSnackbar(MDSnackbarText(text="Invalid options page, please report to world developer."), y=dp(24),
+ pos_hint={"center_x": 0.5},
+ size_hint_x=0.5).open()
+ world_button.state = "normal"
+ # else just fall through
+ else:
+ expansion_box = ScrollBox()
+ expansion_box.layout.orientation = "vertical"
+ expansion_box.layout.spacing = dp(3)
+ expansion_box.scroll_type = ["bars"]
+ expansion_box.do_scroll_x = False
+ group_names = ["Game Options", *(group.name for group in cls.web.option_groups)]
+ groups = {name: [] for name in group_names}
+ for name, option in cls.options_dataclass.type_hints.items():
+ group = next((group.name for group in cls.web.option_groups if option in group.options), "Game Options")
+ groups[group].append((name, option))
+
+ for group, options in groups.items():
+ options = [(name, option) for name, option in options
+ if name and option.visibility & Visibility.simple_ui]
+ if not options:
+ continue # Game Options can be empty if every other option is in another group
+ # Can also have an option group of options that should not render on simple ui
+ group_item = MDExpansionPanel(size_hint_y=None)
+ group_header = MDExpansionPanelHeader(MDListItem(MDListItemSupportingText(text=group),
+ TrailingPressedIconButton(icon="chevron-right",
+ on_release=lambda x,
+ item=group_item:
+ self.tap_expansion_chevron(
+ item, x)),
+ md_bg_color=self.theme_cls.surfaceContainerLowestColor,
+ theme_bg_color="Custom",
+ on_release=lambda x, item=group_item:
+ self.tap_expansion_chevron(item, x)))
+ group_content = MDExpansionPanelContent(orientation="vertical", theme_bg_color="Custom",
+ md_bg_color=self.theme_cls.surfaceContainerLowestColor,
+ padding=[dp(12), dp(100), dp(12), 0],
+ spacing=dp(3))
+ group_item.add_widget(group_header)
+ group_item.add_widget(group_content)
+ group_box = ScrollBox()
+ group_box.layout.orientation = "vertical"
+ group_box.layout.spacing = dp(3)
+ for name, option in options:
+ group_content.add_widget(self.create_option(option, name, cls))
+ expansion_box.layout.add_widget(group_item)
+ self.option_layout.add_widget(expansion_box)
+ self.game_label.text = f"Game: {self.current_game}"
+
+ @staticmethod
+ def tap_expansion_chevron(panel: MDExpansionPanel, chevron: TrailingPressedIconButton | MDListItem):
+ if isinstance(chevron, MDListItem):
+ chevron = next((child for child in chevron.ids.trailing_container.children
+ if isinstance(child, TrailingPressedIconButton)), None)
+ panel.open() if not panel.is_open else panel.close()
+ if chevron:
+ panel.set_chevron_down(
+ chevron
+ ) if not panel.is_open else panel.set_chevron_up(chevron)
+
+ def build(self):
+ self.set_colors()
+ self.options = {}
+ self.container = Builder.load_file(Utils.local_path("data/optionscreator.kv"))
+ self.root = self.container
+ self.main_layout = self.container.ids.main
+ self.scrollbox = self.container.ids.scrollbox
+
+ def world_button_action(world_btn: WorldButton):
+ if self.current_game != world_btn.world_cls.game:
+ old_button = next((button for button in self.scrollbox.layout.children
+ if button.world_cls.game == self.current_game), None)
+ if old_button:
+ old_button.state = "normal"
+ else:
+ world_btn.state = "down"
+ self.create_options_panel(world_btn)
+
+ for world, cls in sorted(AutoWorldRegister.world_types.items(), key=lambda x: x[0]):
+ if cls.hidden:
+ continue
+ world_text = MDButtonText(text=world, size_hint_y=None, width=dp(150),
+ pos_hint={"x": 0.03, "center_y": 0.5})
+ world_text.text_size = (world_text.width, None)
+ world_text.bind(width=lambda *x, text=world_text: text.setter('text_size')(text, (text.width, None)),
+ texture_size=lambda *x, text=world_text: text.setter("height")(text,
+ world_text.texture_size[1]))
+ world_button = WorldButton(world_text, size_hint_x=None, width=dp(150), theme_width="Custom",
+ radius=(dp(5), dp(5), dp(5), dp(5)))
+ world_button.bind(on_release=world_button_action)
+ world_button.world_cls = cls
+ self.scrollbox.layout.add_widget(world_button)
+ self.main_panel = self.container.ids.player_layout
+ self.player_options = self.container.ids.player_options
+ self.game_label = self.container.ids.game
+ self.name_input = self.container.ids.player_name
+ self.option_layout = self.container.ids.options
+
+ def set_height(instance, value):
+ instance.height = value[1]
+
+ self.game_label.bind(texture_size=set_height)
+
+ # Uncomment to re-enable the Kivy console/live editor
+ # Ctrl-E to enable it, make sure numlock/capslock is disabled
+ # from kivy.modules.console import create_console
+ # from kivy.core.window import Window
+ # create_console(Window, self.container)
+
+ return self.container
+
+
+def launch():
+ OptionsCreator().run()
+
+
+if __name__ == "__main__":
+ Utils.init_logging("OptionsCreator")
+ launch()
diff --git a/README.md b/README.md
index fa87190565dd..efa18bc1ef07 100644
--- a/README.md
+++ b/README.md
@@ -82,6 +82,9 @@ Currently, the following games are supported:
* Paint
* Celeste (Open World)
* Choo-Choo Charles
+* APQuest
+* Satisfactory
+* EarthBound
For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
diff --git a/SNIClient.py b/SNIClient.py
index d8bc05841f77..38fabcaab2e2 100644
--- a/SNIClient.py
+++ b/SNIClient.py
@@ -18,7 +18,7 @@
from CommonClient import CommonContext, server_loop, ClientCommandProcessor, gui_enabled, get_base_parser
import Utils
-from settings import Settings
+import settings
from Utils import async_start
from MultiServer import mark_raw
if typing.TYPE_CHECKING:
@@ -286,7 +286,7 @@ class SNESState(enum.IntEnum):
def launch_sni() -> None:
- sni_path = Settings.sni_options.sni_path
+ sni_path = settings.get_settings().sni_options.sni_path
if not os.path.isdir(sni_path):
sni_path = Utils.local_path(sni_path)
@@ -669,7 +669,7 @@ async def game_watcher(ctx: SNIContext) -> None:
async def run_game(romfile: str) -> None:
- auto_start = Settings.sni_options.snes_rom_start
+ auto_start = settings.get_settings().sni_options.snes_rom_start
if auto_start is True:
import webbrowser
webbrowser.open(romfile)
diff --git a/Utils.py b/Utils.py
index 02bc8e8f6fbd..2fe5d0f5629c 100644
--- a/Utils.py
+++ b/Utils.py
@@ -1,6 +1,7 @@
from __future__ import annotations
import asyncio
+import concurrent.futures
import json
import typing
import builtins
@@ -47,7 +48,7 @@ def as_simple_string(self) -> str:
return ".".join(str(item) for item in self)
-__version__ = "0.6.4"
+__version__ = "0.6.6"
version_tuple = tuplize_version(__version__)
is_linux = sys.platform.startswith("linux")
@@ -313,12 +314,8 @@ def get_public_ipv6() -> str:
return ip
-OptionsType = Settings # TODO: remove when removing get_options
-
-
def get_options() -> Settings:
- # TODO: switch to Utils.deprecate after 0.4.4
- warnings.warn("Utils.get_options() is deprecated. Use the settings API instead.", DeprecationWarning)
+ deprecate("Utils.get_options() is deprecated. Use the settings API instead.")
return get_settings()
@@ -477,7 +474,7 @@ def find_class(self, module: str, name: str) -> type:
mod = importlib.import_module(module)
obj = getattr(mod, name)
if issubclass(obj, (self.options_module.Option, self.options_module.PlandoConnection,
- self.options_module.PlandoText)):
+ self.options_module.PlandoItem, self.options_module.PlandoText)):
return obj
# Forbid everything else.
raise pickle.UnpicklingError(f"global '{module}.{name}' is forbidden")
@@ -754,6 +751,11 @@ def _mp_open_filename(res: "multiprocessing.Queue[typing.Optional[str]]", *args:
res.put(open_filename(*args))
+def _mp_save_filename(res: "multiprocessing.Queue[typing.Optional[str]]", *args: Any) -> None:
+ if is_kivy_running():
+ raise RuntimeError("kivy should not be running in multiprocess")
+ res.put(save_filename(*args))
+
def _run_for_stdout(*args: str):
env = os.environ
if "LD_LIBRARY_PATH" in env:
@@ -804,6 +806,51 @@ def open_filename(title: str, filetypes: typing.Iterable[typing.Tuple[str, typin
initialfile=suggest or None)
+def save_filename(title: str, filetypes: typing.Iterable[typing.Tuple[str, typing.Iterable[str]]], suggest: str = "") \
+ -> typing.Optional[str]:
+ logging.info(f"Opening file save dialog for {title}.")
+
+ def run(*args: str):
+ return subprocess.run(args, capture_output=True, text=True).stdout.split("\n", 1)[0] or None
+
+ if is_linux:
+ # prefer native dialog
+ from shutil import which
+ kdialog = which("kdialog")
+ if kdialog:
+ k_filters = '|'.join((f'{text} (*{" *".join(ext)})' for (text, ext) in filetypes))
+ return run(kdialog, f"--title={title}", "--getsavefilename", suggest or ".", k_filters)
+ zenity = which("zenity")
+ if zenity:
+ z_filters = (f'--file-filter={text} ({", ".join(ext)}) | *{" *".join(ext)}' for (text, ext) in filetypes)
+ selection = (f"--filename={suggest}",) if suggest else ()
+ return run(zenity, f"--title={title}", "--file-selection", "--save", *z_filters, *selection)
+
+ # fall back to tk
+ try:
+ import tkinter
+ import tkinter.filedialog
+ except Exception as e:
+ logging.error('Could not load tkinter, which is likely not installed. '
+ f'This attempt was made because save_filename was used for "{title}".')
+ raise e
+ else:
+ if is_macos and is_kivy_running():
+ # on macOS, mixing kivy and tk does not work, so spawn a new process
+ # FIXME: performance of this is pretty bad, and we should (also) look into alternatives
+ from multiprocessing import Process, Queue
+ res: "Queue[typing.Optional[str]]" = Queue()
+ Process(target=_mp_save_filename, args=(res, title, filetypes, suggest)).start()
+ return res.get()
+ try:
+ root = tkinter.Tk()
+ except tkinter.TclError:
+ return None # GUI not available. None is the same as a user clicking "cancel"
+ root.withdraw()
+ return tkinter.filedialog.asksaveasfilename(title=title, filetypes=((t[0], ' '.join(t[1])) for t in filetypes),
+ initialfile=suggest or None)
+
+
def _mp_open_directory(res: "multiprocessing.Queue[typing.Optional[str]]", *args: Any) -> None:
if is_kivy_running():
raise RuntimeError("kivy should not be running in multiprocess")
@@ -1138,3 +1185,72 @@ def is_iterable_except_str(obj: object) -> TypeGuard[typing.Iterable[typing.Any]
if isinstance(obj, str):
return False
return isinstance(obj, typing.Iterable)
+
+
+class DaemonThreadPoolExecutor(concurrent.futures.ThreadPoolExecutor):
+ """
+ ThreadPoolExecutor that uses daemonic threads that do not keep the program alive.
+ NOTE: use this with caution because killed threads will not properly clean up.
+ """
+
+ def _adjust_thread_count(self):
+ # see upstream ThreadPoolExecutor for details
+ import threading
+ import weakref
+ from concurrent.futures.thread import _worker
+
+ if self._idle_semaphore.acquire(timeout=0):
+ return
+
+ def weakref_cb(_, q=self._work_queue):
+ q.put(None)
+
+ num_threads = len(self._threads)
+ if num_threads < self._max_workers:
+ thread_name = f"{self._thread_name_prefix or self}_{num_threads}"
+ t = threading.Thread(
+ name=thread_name,
+ target=_worker,
+ args=(
+ weakref.ref(self, weakref_cb),
+ self._work_queue,
+ self._initializer,
+ self._initargs,
+ ),
+ daemon=True,
+ )
+ t.start()
+ self._threads.add(t)
+ # NOTE: don't add to _threads_queues so we don't block on shutdown
+
+
+def get_full_typename(t: type) -> str:
+ """Returns the full qualified name of a type, including its module (if not builtins)."""
+ module = t.__module__
+ if module and module != "builtins":
+ return f"{module}.{t.__qualname__}"
+ return t.__qualname__
+
+
+def get_all_causes(ex: Exception) -> str:
+ """Return a string describing the recursive causes of this exception.
+
+ :param ex: The exception to be described.
+ :return A multiline string starting with the initial exception on the first line and each resulting exception
+ on subsequent lines with progressive indentation.
+
+ For example:
+
+ ```
+ Exception: Invalid value 'bad'.
+ Which caused: Options.OptionError: Error generating option
+ Which caused: ValueError: File bad.yaml is invalid.
+ ```
+ """
+ cause = ex
+ causes = [f"{get_full_typename(type(ex))}: {ex}"]
+ while cause := cause.__cause__:
+ causes.append(f"{get_full_typename(type(cause))}: {cause}")
+ top = causes[-1]
+ others = "".join(f"\n{' ' * (i + 1)}Which caused: {c}" for i, c in enumerate(reversed(causes[:-1])))
+ return f"{top}{others}"
diff --git a/WebHostLib/__init__.py b/WebHostLib/__init__.py
index 74086cb8842b..f856eea4c538 100644
--- a/WebHostLib/__init__.py
+++ b/WebHostLib/__init__.py
@@ -1,6 +1,7 @@
import base64
import os
import socket
+import typing
import uuid
from flask import Flask
@@ -22,6 +23,17 @@
app.jinja_env.filters['all'] = all
app.jinja_env.filters['get_file_safe_name'] = get_file_safe_name
+# overwrites of flask default config
+app.config["DEBUG"] = False
+app.config["PORT"] = 80
+app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
+app.config["MAX_CONTENT_LENGTH"] = 64 * 1024 * 1024 # 64 megabyte limit
+# if you want to deploy, make sure you have a non-guessable secret key
+app.config["SECRET_KEY"] = bytes(socket.gethostname(), encoding="utf-8")
+app.config["SESSION_PERMANENT"] = True
+app.config["MAX_FORM_MEMORY_SIZE"] = 2 * 1024 * 1024 # 2 MB, needed for large option pages such as SC2
+
+# custom config
app.config["SELFHOST"] = True # application process is in charge of running the websites
app.config["GENERATORS"] = 8 # maximum concurrent world gens
app.config["HOSTERS"] = 8 # maximum concurrent room hosters
@@ -29,19 +41,12 @@
app.config["SELFLAUNCHCERT"] = None # can point to a SSL Certificate to encrypt Room websocket connections
app.config["SELFLAUNCHKEY"] = None # can point to a SSL Certificate Key to encrypt Room websocket connections
app.config["SELFGEN"] = True # application process is in charge of scheduling Generations.
-app.config["DEBUG"] = False
-app.config["PORT"] = 80
-app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
-app.config['MAX_CONTENT_LENGTH'] = 64 * 1024 * 1024 # 64 megabyte limit
-# if you want to deploy, make sure you have a non-guessable secret key
-app.config["SECRET_KEY"] = bytes(socket.gethostname(), encoding="utf-8")
# at what amount of worlds should scheduling be used, instead of rolling in the web-thread
app.config["JOB_THRESHOLD"] = 1
# after what time in seconds should generation be aborted, freeing the queue slot. Can be set to None to disable.
app.config["JOB_TIME"] = 600
# memory limit for generator processes in bytes
app.config["GENERATOR_MEMORY_LIMIT"] = 4294967296
-app.config['SESSION_PERMANENT'] = True
# waitress uses one thread for I/O, these are for processing of views that then get sent
# archipelago.gg uses gunicorn + nginx; ignoring this option
@@ -61,20 +66,21 @@
Compress(app)
-def to_python(value):
+def to_python(value: str) -> uuid.UUID:
return uuid.UUID(bytes=base64.urlsafe_b64decode(value + '=='))
-def to_url(value):
+def to_url(value: uuid.UUID) -> str:
return base64.urlsafe_b64encode(value.bytes).rstrip(b'=').decode('ascii')
class B64UUIDConverter(BaseConverter):
- def to_python(self, value):
+ def to_python(self, value: str) -> uuid.UUID:
return to_python(value)
- def to_url(self, value):
+ def to_url(self, value: typing.Any) -> str:
+ assert isinstance(value, uuid.UUID)
return to_url(value)
@@ -84,7 +90,7 @@ def to_url(self, value):
app.jinja_env.filters["title_sorted"] = title_sorted
-def register():
+def register() -> None:
"""Import submodules, triggering their registering on flask routing.
Note: initializes worlds subsystem."""
import importlib
diff --git a/WebHostLib/api/tracker.py b/WebHostLib/api/tracker.py
index 36692af42652..1e6e02bee778 100644
--- a/WebHostLib/api/tracker.py
+++ b/WebHostLib/api/tracker.py
@@ -58,6 +58,12 @@ class PlayerLocationsTotal(TypedDict):
total_locations: int
+class PlayerGame(TypedDict):
+ team: int
+ player: int
+ game: str
+
+
@api_endpoints.route("/tracker/")
@cache.memoize(timeout=60)
def tracker_data(tracker: UUID) -> dict[str, Any]:
@@ -80,7 +86,8 @@ def tracker_data(tracker: UUID) -> dict[str, Any]:
"""Slot aliases of all players."""
for team, players in all_players.items():
for player in players:
- player_aliases.append({"team": team, "player": player, "alias": tracker_data.get_player_alias(team, player)})
+ player_aliases.append(
+ {"team": team, "player": player, "alias": tracker_data.get_player_alias(team, player)})
player_items_received: list[PlayerItemsReceived] = []
"""Items received by each player."""
@@ -94,7 +101,8 @@ def tracker_data(tracker: UUID) -> dict[str, Any]:
for team, players in all_players.items():
for player in players:
player_checks_done.append(
- {"team": team, "player": player, "locations": sorted(tracker_data.get_player_checked_locations(team, player))})
+ {"team": team, "player": player,
+ "locations": sorted(tracker_data.get_player_checked_locations(team, player))})
total_checks_done: list[TeamTotalChecks] = [
{"team": team, "checks_done": checks_done}
@@ -144,7 +152,8 @@ def tracker_data(tracker: UUID) -> dict[str, Any]:
"""The current client status for each player."""
for team, players in all_players.items():
for player in players:
- player_status.append({"team": team, "player": player, "status": tracker_data.get_player_client_status(team, player)})
+ player_status.append(
+ {"team": team, "player": player, "status": tracker_data.get_player_client_status(team, player)})
return {
"aliases": player_aliases,
@@ -207,12 +216,20 @@ def static_tracker_data(tracker: UUID) -> dict[str, Any]:
player_locations_total.append(
{"team": team, "player": player, "total_locations": len(tracker_data.get_player_locations(player))})
+ player_game: list[PlayerGame] = []
+ """The played game per player slot."""
+ for team, players in all_players.items():
+ for player in players:
+ player_game.append({"team": team, "player": player, "game": tracker_data.get_player_game(player)})
+
return {
"groups": groups,
"datapackage": tracker_data._multidata["datapackage"],
"player_locations_total": player_locations_total,
+ "player_game": player_game,
}
+
# It should be exceedingly rare that slot data is needed, so it's separated out.
@api_endpoints.route("/slot_data_tracker/")
@cache.memoize(timeout=300)
diff --git a/WebHostLib/autolauncher.py b/WebHostLib/autolauncher.py
index 719963e37508..96ffbe9e9540 100644
--- a/WebHostLib/autolauncher.py
+++ b/WebHostLib/autolauncher.py
@@ -17,7 +17,7 @@
_stop_event = Event()
-def stop():
+def stop() -> None:
"""Stops previously launched threads"""
global _stop_event
stop_event = _stop_event
@@ -36,25 +36,39 @@ def handle_generation_failure(result: BaseException):
logging.exception(e)
-def _mp_gen_game(gen_options: dict, meta: dict[str, Any] | None = None, owner=None, sid=None) -> PrimaryKey | None:
+def _mp_gen_game(
+ gen_options: dict,
+ meta: dict[str, Any] | None = None,
+ owner=None,
+ sid=None,
+ timeout: int|None = None,
+) -> PrimaryKey | None:
from setproctitle import setproctitle
setproctitle(f"Generator ({sid})")
- res = gen_game(gen_options, meta=meta, owner=owner, sid=sid)
- setproctitle(f"Generator (idle)")
- return res
+ try:
+ return gen_game(gen_options, meta=meta, owner=owner, sid=sid, timeout=timeout)
+ finally:
+ setproctitle(f"Generator (idle)")
-def launch_generator(pool: multiprocessing.pool.Pool, generation: Generation):
+def launch_generator(pool: multiprocessing.pool.Pool, generation: Generation, timeout: int|None) -> None:
try:
meta = json.loads(generation.meta)
options = restricted_loads(generation.options)
logging.info(f"Generating {generation.id} for {len(options)} players")
- pool.apply_async(_mp_gen_game, (options,),
- {"meta": meta,
- "sid": generation.id,
- "owner": generation.owner},
- handle_generation_success, handle_generation_failure)
+ pool.apply_async(
+ _mp_gen_game,
+ (options,),
+ {
+ "meta": meta,
+ "sid": generation.id,
+ "owner": generation.owner,
+ "timeout": timeout,
+ },
+ handle_generation_success,
+ handle_generation_failure,
+ )
except Exception as e:
generation.state = STATE_ERROR
commit()
@@ -135,6 +149,7 @@ def keep_running():
with multiprocessing.Pool(config["GENERATORS"], initializer=init_generator,
initargs=(config,), maxtasksperchild=10) as generator_pool:
+ job_time = config["JOB_TIME"]
with db_session:
to_start = select(generation for generation in Generation if generation.state == STATE_STARTED)
@@ -145,7 +160,7 @@ def keep_running():
if sid:
generation.delete()
else:
- launch_generator(generator_pool, generation)
+ launch_generator(generator_pool, generation, timeout=job_time)
commit()
select(generation for generation in Generation if generation.state == STATE_ERROR).delete()
@@ -157,7 +172,7 @@ def keep_running():
generation for generation in Generation
if generation.state == STATE_QUEUED).for_update()
for generation in to_start:
- launch_generator(generator_pool, generation)
+ launch_generator(generator_pool, generation, timeout=job_time)
except AlreadyRunningException:
logging.info("Autogen reports as already running, not starting another.")
diff --git a/WebHostLib/customserver.py b/WebHostLib/customserver.py
index 45aca124f51b..14ae291982bb 100644
--- a/WebHostLib/customserver.py
+++ b/WebHostLib/customserver.py
@@ -19,7 +19,10 @@
import Utils
-from MultiServer import Context, server, auto_shutdown, ServerCommandProcessor, ClientMessageProcessor, load_server_cert
+from MultiServer import (
+ Context, server, auto_shutdown, ServerCommandProcessor, ClientMessageProcessor, load_server_cert,
+ server_per_message_deflate_factory,
+)
from Utils import restricted_loads, cache_argsless
from .locker import Locker
from .models import Command, GameDataPackage, Room, db
@@ -283,8 +286,12 @@ async def start_room(room_id):
assert ctx.server is None
try:
ctx.server = websockets.serve(
- functools.partial(server, ctx=ctx), ctx.host, ctx.port, ssl=get_ssl_context())
-
+ functools.partial(server, ctx=ctx),
+ ctx.host,
+ ctx.port,
+ ssl=get_ssl_context(),
+ extensions=[server_per_message_deflate_factory],
+ )
await ctx.server
except OSError: # likely port in use
ctx.server = websockets.serve(
diff --git a/WebHostLib/generate.py b/WebHostLib/generate.py
index a5147f66844d..f80663ff43f1 100644
--- a/WebHostLib/generate.py
+++ b/WebHostLib/generate.py
@@ -14,7 +14,7 @@
from BaseClasses import get_seed, seeddigits
from Generate import PlandoOptions, handle_name, mystery_argparse
from Main import main as ERmain
-from Utils import __version__, restricted_dumps
+from Utils import __version__, restricted_dumps, DaemonThreadPoolExecutor
from WebHostLib import app
from settings import ServerOptions, GeneratorOptions
from .check import get_yaml_data, roll_options
@@ -33,6 +33,7 @@ def get_meta(options_source: dict, race: bool = False) -> dict[str, list[str] |
"release_mode": str(options_source.get("release_mode", ServerOptions.release_mode)),
"remaining_mode": str(options_source.get("remaining_mode", ServerOptions.remaining_mode)),
"collect_mode": str(options_source.get("collect_mode", ServerOptions.collect_mode)),
+ "countdown_mode": str(options_source.get("countdown_mode", ServerOptions.countdown_mode)),
"item_cheat": bool(int(options_source.get("item_cheat", not ServerOptions.disable_item_cheat))),
"server_password": str(options_source.get("server_password", None)),
}
@@ -97,8 +98,6 @@ def start_generation(options: dict[str, dict | str], meta: dict[str, Any]):
from .autolauncher import handle_generation_failure
handle_generation_failure(e)
meta["error"] = format_exception(e)
- if e.__cause__:
- meta["source"] = format_exception(e.__cause__)
details = json.dumps(meta, indent=4).strip()
return render_template("seedError.html", seed_error=meta["error"], details=details)
@@ -108,20 +107,18 @@ def start_generation(options: dict[str, dict | str], meta: dict[str, Any]):
else:
try:
seed_id = gen_game({name: vars(options) for name, options in gen_options.items()},
- meta=meta, owner=session["_id"].int)
+ meta=meta, owner=session["_id"].int, timeout=app.config["JOB_TIME"])
except BaseException as e:
from .autolauncher import handle_generation_failure
handle_generation_failure(e)
meta["error"] = format_exception(e)
- if e.__cause__:
- meta["source"] = format_exception(e.__cause__)
details = json.dumps(meta, indent=4).strip()
return render_template("seedError.html", seed_error=meta["error"], details=details)
return redirect(url_for("view_seed", seed=seed_id))
-def gen_game(gen_options: dict, meta: dict[str, Any] | None = None, owner=None, sid=None):
+def gen_game(gen_options: dict, meta: dict[str, Any] | None = None, owner=None, sid=None, timeout: int|None = None):
if meta is None:
meta = {}
@@ -140,7 +137,7 @@ def task():
seedname = "W" + (f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits))
- args = mystery_argparse()
+ args = mystery_argparse([]) # Just to set up the Namespace with defaults
args.multi = playercount
args.seed = seed
args.name = {x: "" for x in range(1, playercount + 1)} # only so it can be overwritten in mystery
@@ -175,11 +172,12 @@ def task():
ERmain(args, seed, baked_server_options=meta["server_options"])
return upload_to_db(target.name, sid, owner, race)
- thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=1)
+
+ thread_pool = DaemonThreadPoolExecutor(max_workers=1)
thread = thread_pool.submit(task)
try:
- return thread.result(app.config["JOB_TIME"])
+ return thread.result(timeout)
except concurrent.futures.TimeoutError as e:
if sid:
with db_session:
@@ -190,10 +188,11 @@ def task():
meta["error"] = ("Allowed time for Generation exceeded, " +
"please consider generating locally instead. " +
format_exception(e))
- if e.__cause__:
- meta["source"] = format_exception(e.__cause__)
gen.meta = json.dumps(meta)
commit()
+ except (KeyboardInterrupt, SystemExit):
+ # don't update db, retry next time
+ raise
except BaseException as e:
if sid:
with db_session:
@@ -202,11 +201,14 @@ def task():
gen.state = STATE_ERROR
meta = json.loads(gen.meta)
meta["error"] = format_exception(e)
- if e.__cause__:
- meta["source"] = format_exception(e.__cause__)
gen.meta = json.dumps(meta)
commit()
raise
+ finally:
+ # free resources claimed by thread pool, if possible
+ # NOTE: Timeout depends on the process being killed at some point
+ # since we can't actually cancel a running gen at the moment.
+ thread_pool.shutdown(wait=False, cancel_futures=True)
@app.route('/wait/')
diff --git a/WebHostLib/markdown.py b/WebHostLib/markdown.py
new file mode 100644
index 000000000000..ff7a5fd7c5d5
--- /dev/null
+++ b/WebHostLib/markdown.py
@@ -0,0 +1,90 @@
+import re
+from collections import Counter
+
+import mistune
+from werkzeug.utils import secure_filename
+
+
+__all__ = [
+ "ImgUrlRewriteInlineParser",
+ 'render_markdown',
+]
+
+
+class ImgUrlRewriteInlineParser(mistune.InlineParser):
+ relative_url_base: str
+
+ def __init__(self, relative_url_base: str, hard_wrap: bool = False) -> None:
+ super().__init__(hard_wrap)
+ self.relative_url_base = relative_url_base
+
+ @staticmethod
+ def _find_game_name_by_folder_name(name: str) -> str | None:
+ from worlds.AutoWorld import AutoWorldRegister
+
+ for world_name, world_type in AutoWorldRegister.world_types.items():
+ if world_type.__module__ == f"worlds.{name}":
+ return world_name
+ return None
+
+ def parse_link(self, m: re.Match[str], state: mistune.InlineState) -> int | None:
+ res = super().parse_link(m, state)
+ if res is not None and state.tokens and state.tokens[-1]["type"] == "image":
+ image_token = state.tokens[-1]
+ url: str = image_token["attrs"]["url"]
+ if not url.startswith("/") and not "://" in url:
+ # replace relative URL to another world's doc folder with the webhost folder layout
+ if url.startswith("../../") and "/docs/" in self.relative_url_base:
+ parts = url.split("/", 4)
+ if parts[2] != ".." and parts[3] == "docs":
+ game_name = self._find_game_name_by_folder_name(parts[2])
+ if game_name is not None:
+ url = "/".join(parts[1:2] + [secure_filename(game_name)] + parts[4:])
+ # change relative URL to point to deployment folder
+ url = f"{self.relative_url_base}/{url}"
+ image_token['attrs']['url'] = url
+ return res
+
+
+def render_markdown(path: str, img_url_base: str | None = None) -> str:
+ markdown = mistune.create_markdown(
+ escape=False,
+ plugins=[
+ "strikethrough",
+ "footnotes",
+ "table",
+ "speedup",
+ ],
+ )
+
+ heading_id_count: Counter[str] = Counter()
+
+ def heading_id(text: str) -> str:
+ nonlocal heading_id_count
+
+ # there is no good way to do this without regex
+ s = re.sub(r"[^\w\- ]", "", text.lower()).replace(" ", "-").strip("-")
+ n = heading_id_count[s]
+ heading_id_count[s] += 1
+ if n > 0:
+ s += f"-{n}"
+ return s
+
+ def id_hook(_: mistune.Markdown, state: mistune.BlockState) -> None:
+ for tok in state.tokens:
+ if tok["type"] == "heading" and tok["attrs"]["level"] < 4:
+ text = tok["text"]
+ assert isinstance(text, str)
+ unique_id = heading_id(text)
+ tok["attrs"]["id"] = unique_id
+ tok["text"] = f"{text}" # make header link to itself
+
+ markdown.before_render_hooks.append(id_hook)
+ if img_url_base:
+ markdown.inline = ImgUrlRewriteInlineParser(img_url_base)
+
+ with open(path, encoding="utf-8-sig") as f:
+ document = f.read()
+ html = markdown(document)
+ assert isinstance(html, str), "Unexpected mistune renderer in render_markdown"
+ return html
diff --git a/WebHostLib/misc.py b/WebHostLib/misc.py
index b56b11dd6f47..82faaf2b164e 100644
--- a/WebHostLib/misc.py
+++ b/WebHostLib/misc.py
@@ -1,5 +1,7 @@
import datetime
import os
+import warnings
+from enum import StrEnum
from typing import Any, IO, Dict, Iterator, List, Tuple, Union
import jinja2.exceptions
@@ -9,14 +11,29 @@
from worlds.AutoWorld import AutoWorldRegister, World
from . import app, cache
+from .markdown import render_markdown
from .models import Seed, Room, Command, UUID, uuid4
from Utils import title_sorted
+class WebWorldTheme(StrEnum):
+ DIRT = "dirt"
+ GRASS = "grass"
+ GRASS_FLOWERS = "grassFlowers"
+ ICE = "ice"
+ JUNGLE = "jungle"
+ OCEAN = "ocean"
+ PARTY_TIME = "partyTime"
+ STONE = "stone"
def get_world_theme(game_name: str) -> str:
- if game_name in AutoWorldRegister.world_types:
- return AutoWorldRegister.world_types[game_name].web.theme
- return 'grass'
+ if game_name not in AutoWorldRegister.world_types:
+ return "grass"
+ chosen_theme = AutoWorldRegister.world_types[game_name].web.theme
+ available_themes = [theme.value for theme in WebWorldTheme]
+ if chosen_theme not in available_themes:
+ warnings.warn(f"Theme '{chosen_theme}' for {game_name} not valid, switching to default 'grass' theme.")
+ return "grass"
+ return chosen_theme
def get_visible_worlds() -> dict[str, type(World)]:
@@ -27,49 +44,6 @@ def get_visible_worlds() -> dict[str, type(World)]:
return worlds
-def render_markdown(path: str) -> str:
- import mistune
- from collections import Counter
-
- markdown = mistune.create_markdown(
- escape=False,
- plugins=[
- "strikethrough",
- "footnotes",
- "table",
- "speedup",
- ],
- )
-
- heading_id_count: Counter[str] = Counter()
-
- def heading_id(text: str) -> str:
- nonlocal heading_id_count
- import re # there is no good way to do this without regex
-
- s = re.sub(r"[^\w\- ]", "", text.lower()).replace(" ", "-").strip("-")
- n = heading_id_count[s]
- heading_id_count[s] += 1
- if n > 0:
- s += f"-{n}"
- return s
-
- def id_hook(_: mistune.Markdown, state: mistune.BlockState) -> None:
- for tok in state.tokens:
- if tok["type"] == "heading" and tok["attrs"]["level"] < 4:
- text = tok["text"]
- assert isinstance(text, str)
- unique_id = heading_id(text)
- tok["attrs"]["id"] = unique_id
- tok["text"] = f"{text}" # make header link to itself
-
- markdown.before_render_hooks.append(id_hook)
-
- with open(path, encoding="utf-8-sig") as f:
- document = f.read()
- return markdown(document)
-
-
@app.errorhandler(404)
@app.errorhandler(jinja2.exceptions.TemplateNotFound)
def page_not_found(err):
@@ -91,10 +65,9 @@ def game_info(game, lang):
theme = get_world_theme(game)
secure_game_name = secure_filename(game)
lang = secure_filename(lang)
- document = render_markdown(os.path.join(
- app.static_folder, "generated", "docs",
- secure_game_name, f"{lang}_{secure_game_name}.md"
- ))
+ file_dir = os.path.join(app.static_folder, "generated", "docs", secure_game_name)
+ file_dir_url = url_for("static", filename=f"generated/docs/{secure_game_name}")
+ document = render_markdown(os.path.join(file_dir, f"{lang}_{secure_game_name}.md"), file_dir_url)
return render_template(
"markdown_document.html",
title=f"{game} Guide",
@@ -119,10 +92,9 @@ def tutorial(game: str, file: str):
theme = get_world_theme(game)
secure_game_name = secure_filename(game)
file = secure_filename(file)
- document = render_markdown(os.path.join(
- app.static_folder, "generated", "docs",
- secure_game_name, file+".md"
- ))
+ file_dir = os.path.join(app.static_folder, "generated", "docs", secure_game_name)
+ file_dir_url = url_for("static", filename=f"generated/docs/{secure_game_name}")
+ document = render_markdown(os.path.join(file_dir, f"{file}.md"), file_dir_url)
return render_template(
"markdown_document.html",
title=f"{game} Guide",
diff --git a/WebHostLib/options.py b/WebHostLib/options.py
index 2df2e64aeb86..e3d2745855cd 100644
--- a/WebHostLib/options.py
+++ b/WebHostLib/options.py
@@ -13,6 +13,7 @@
from worlds.AutoWorld import AutoWorldRegister
from . import app, cache
from .generate import get_meta
+from .misc import get_world_theme
def create() -> None:
@@ -22,12 +23,6 @@ def create() -> None:
Options.generate_yaml_templates(yaml_folder)
-def get_world_theme(game_name: str) -> str:
- if game_name in AutoWorldRegister.world_types:
- return AutoWorldRegister.world_types[game_name].web.theme
- return 'grass'
-
-
def render_options_page(template: str, world_name: str, is_complex: bool = False) -> Union[Response, str]:
world = AutoWorldRegister.world_types[world_name]
if world.hidden or world.web.options_page is False:
@@ -76,7 +71,7 @@ def filter_rst_to_html(text: str) -> str:
lines = text.splitlines()
text = lines[0] + "\n" + dedent("\n".join(lines[1:]))
- return publish_parts(text, writer_name='html', settings=None, settings_overrides={
+ return publish_parts(text, writer='html', settings=None, settings_overrides={
'raw_enable': False,
'file_insertion_enabled': False,
'output_encoding': 'unicode'
diff --git a/WebHostLib/requirements.txt b/WebHostLib/requirements.txt
index f64ed085c982..c4267dc2846b 100644
--- a/WebHostLib/requirements.txt
+++ b/WebHostLib/requirements.txt
@@ -4,9 +4,10 @@ pony>=0.7.19; python_version <= '3.12'
pony @ git+https://github.com/black-sliver/pony@7feb1221953b7fa4a6735466bf21a8b4d35e33ba#0.7.19; python_version >= '3.13'
waitress>=3.0.2
Flask-Caching>=2.3.0
-Flask-Compress>=1.17
+Flask-Compress==1.18 # pkg_resources can't resolve the "backports.zstd" dependency of >1.18, breaking ModuleUpdate.py
Flask-Limiter>=3.12
bokeh>=3.6.3
markupsafe>=3.0.2
setproctitle>=1.3.5
mistune>=3.1.3
+docutils>=0.22.2
diff --git a/WebHostLib/static/assets/faq/en.md b/WebHostLib/static/assets/faq/en.md
index 588750065666..5b42cc6fc6c5 100644
--- a/WebHostLib/static/assets/faq/en.md
+++ b/WebHostLib/static/assets/faq/en.md
@@ -23,7 +23,7 @@ players to rely upon each other to complete their game.
While a multiworld game traditionally requires all players to be playing the same game, a multi-game multiworld allows
players to randomize any of the supported games, and send items between them. This allows players of different
games to interact with one another in a single multiplayer environment. Archipelago supports multi-game multiworlds.
-Here is a list of our [Supported Games](https://archipelago.gg/games).
+Here is a list of our [Supported Games](/games).
## Can I generate a single-player game with Archipelago?
@@ -33,7 +33,7 @@ play, open the Settings Page, pick your settings, and click Generate Game.
## How do I get started?
-We have a [Getting Started](https://archipelago.gg/tutorial/Archipelago/setup/en) guide that will help you get the
+We have a [Getting Started](/tutorial/Archipelago/setup/en) guide that will help you get the
software set up. You can use that guide to learn how to generate multiworlds. There are also basic instructions for
including multiple games, and hosting multiworlds on the website for ease and convenience.
@@ -57,7 +57,7 @@ their multiworld.
If a player must leave early, they can use Archipelago's release system. When a player releases their game, all items
in that game belonging to other players are sent out automatically. This allows other players to continue to play
-uninterrupted. Here is a list of all of our [Server Commands](https://archipelago.gg/tutorial/Archipelago/commands/en).
+uninterrupted. Here is a list of all of our [Server Commands](/tutorial/Archipelago/commands/en).
## What happens if an item is placed somewhere it is impossible to get?
diff --git a/WebHostLib/static/styles/sc2Tracker.css b/WebHostLib/static/styles/sc2Tracker.css
index 3048213e43cb..28a4e7bc8a22 100644
--- a/WebHostLib/static/styles/sc2Tracker.css
+++ b/WebHostLib/static/styles/sc2Tracker.css
@@ -241,12 +241,9 @@ input[type="checkbox"]{
}
/* Hidden items */
-.hidden-class:not(:has(img.acquired)){
+.hidden-class:not(:has(.f:not(.unacquired))), .hidden-item{
display: none;
}
-.hidden-item:not(.acquired){
- display:none;
-}
/* Keys */
#keys ol, #keys ul{
diff --git a/WebHostLib/templates/tracker__Starcraft2.html b/WebHostLib/templates/tracker__Starcraft2.html
index 932f21505d16..b7cc8a29c698 100644
--- a/WebHostLib/templates/tracker__Starcraft2.html
+++ b/WebHostLib/templates/tracker__Starcraft2.html
@@ -24,55 +24,55 @@
Filler Items
-
+
+{{minerals_count}}
-
+
+{{vespene_count}}
-
+
+{{supply_count}}
-
+
+{{max_supply_count}}
-
+
-{{reduced_supply_count}}
-
+
{{construction_speed_count}}
-
+
{{shield_regen_count}}
-
+
{{upgrade_speed_count}}
-
+
{{research_cost_count}}
@@ -116,133 +116,133 @@
Terran Items
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
+
+
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
@@ -250,155 +250,155 @@
Terran Items
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
-
+
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
+
+
+
@@ -406,156 +406,156 @@
Terran Items
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
+
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
+
@@ -563,74 +563,74 @@
Terran Items
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
+
+
-
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
+
+
-
+
-
-
-
+
+
+
-
-
-
+
+
+
@@ -638,94 +638,94 @@
Terran Items
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -734,96 +734,96 @@
Terran Items
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
@@ -871,133 +871,133 @@
Zerg Items
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
@@ -1005,47 +1005,47 @@
Zerg Items
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
+
+
+
@@ -1053,116 +1053,116 @@
Zerg Items
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
+
-
-
+
+
-
+
-
-
-
-
+
+
+
+
@@ -1170,75 +1170,75 @@
Zerg Items
-
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
+
+
+
+
@@ -1246,59 +1246,59 @@
Zerg Items
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
-
+
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
@@ -1306,9 +1306,9 @@
Zerg Items
-
-
-
+
+
+
@@ -1316,23 +1316,23 @@
Zerg Items
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
@@ -1341,64 +1341,64 @@
Zerg Items
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -1438,26 +1438,26 @@
Protoss Items
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1465,46 +1465,46 @@
Protoss Items
-
-
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
@@ -1512,38 +1512,38 @@
Protoss Items
-
-
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
+
+
+
@@ -1551,59 +1551,59 @@
Protoss Items
-
-
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
+
+
+
+
+
@@ -1611,9 +1611,9 @@
Protoss Items
-
-
-
+
+
+
@@ -1621,49 +1621,49 @@
Protoss Items
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
+
@@ -1671,10 +1671,10 @@
Protoss Items
-
-
-
-
+
+
+
+
@@ -1682,18 +1682,18 @@
Protoss Items
-
+
-
+
-
+
-
+
@@ -1701,96 +1701,96 @@
Protoss Items
-
-
-
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
-
+
+
+
+
@@ -1798,26 +1798,26 @@
Protoss Items
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1825,57 +1825,57 @@
Protoss Items
-
-
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
+
+
+
@@ -1883,49 +1883,49 @@
Protoss Items
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
@@ -1933,33 +1933,33 @@
Protoss Items
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1967,53 +1967,53 @@
Protoss Items
-
-
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
+
-
+
@@ -2021,16 +2021,16 @@
Protoss Items
-
-
-
+
+
+
-
-
-
+
+
+
@@ -2039,83 +2039,83 @@
Protoss Items
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
@@ -2129,44 +2129,44 @@
Nova Items
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
+
@@ -2178,41 +2178,41 @@
Kerrigan Items
-
+ {{kerrigan_level}}
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py
index ead679fd980b..d1471aa6580a 100644
--- a/WebHostLib/tracker.py
+++ b/WebHostLib/tracker.py
@@ -959,7 +959,7 @@ def render_Timespinner_tracker(tracker_data: TrackerData, team: int, player: int
timespinner_location_ids = {
"Present": list(range(1337000, 1337085)),
- "Past": list(range(1337086, 1337175)),
+ "Past": list(range(1337086, 1337157)) + list(range(1337159, 1337175)),
"Ancient Pyramid": [
1337236,
1337246, 1337247, 1337248, 1337249]
@@ -1228,7 +1228,7 @@ def render_ChecksFinder_tracker(tracker_data: TrackerData, team: int, player: in
def render_Starcraft2_tracker(tracker_data: TrackerData, team: int, player: int) -> str:
SC2WOL_ITEM_ID_OFFSET = 1000
SC2HOTS_ITEM_ID_OFFSET = 2000
- SC2LOTV_ITEM_ID_OFFSET = 2000
+ SC2LOTV_ITEM_ID_OFFSET = 3000
SC2_KEY_ITEM_ID_OFFSET = 4000
NCO_LOCATION_ID_LOW = 20004500
NCO_LOCATION_ID_HIGH = NCO_LOCATION_ID_LOW + 1000
diff --git a/Zelda1Client.py b/Zelda1Client.py
index 6dd7a361658c..b4292ecf7539 100644
--- a/Zelda1Client.py
+++ b/Zelda1Client.py
@@ -289,7 +289,7 @@ async def nes_sync_task(ctx: ZeldaContext):
if not ctx.auth:
ctx.auth = ''.join([chr(i) for i in data_decoded['playerName'] if i != 0])
if ctx.auth == '':
- logger.info("Invalid ROM detected. No player name built into the ROM. Please regenerate"
+ logger.info("Invalid ROM detected. No player name built into the ROM. Please regenerate "
"the ROM using the same link but adding your slot name")
if ctx.awaiting_rom:
await ctx.server_auth(False)
diff --git a/ci-requirements.txt b/ci-requirements.txt
new file mode 100644
index 000000000000..80a1c783660e
--- /dev/null
+++ b/ci-requirements.txt
@@ -0,0 +1,2 @@
+pytest>=9.0.1,<10 # this includes subtests support
+pytest-xdist>=3.8.0
diff --git a/data/client.kv b/data/client.kv
index 08f4c8d71831..cf8e88446d2c 100644
--- a/data/client.kv
+++ b/data/client.kv
@@ -224,6 +224,7 @@
height: self.content.texture_size[1] + 80
:
layout: layout
+ box_height: dp(100)
bar_width: "12dp"
scroll_wheel_distance: 40
do_scroll_x: False
@@ -234,4 +235,11 @@
orientation: "vertical"
spacing: 10
size_hint_y: None
- height: self.minimum_height
+ height: max(self.minimum_height, root.box_height)
+
+:
+ valign: "middle"
+ halign: "center"
+ text_size: self.width, None
+ height: self.texture_size[1]
+
diff --git a/data/optionscreator.kv b/data/optionscreator.kv
new file mode 100644
index 000000000000..4c0cc8178264
--- /dev/null
+++ b/data/optionscreator.kv
@@ -0,0 +1,174 @@
+:
+ id: this
+ spacing: 15
+ orientation: "horizontal"
+ slider: slider
+ tag: tag
+ MDLabel:
+ id: tag
+ text: str(this.option.default) if not isinstance(this.option.default, str) else str(this.option.range_start)
+ MDSlider:
+ id: slider
+ min: this.option.range_start
+ max: this.option.range_end
+ value: min(max(this.option.default, this.option.range_start), this.option.range_end) if not isinstance(this.option.default, str) else this.option.range_start
+ step: 1
+ step_point_size: 0
+ MDSliderHandle:
+
+ MDSliderValueLabel:
+
+:
+ id: this
+ text: text
+ MDButtonText:
+ id: text
+ text: this.option.get_option_name(this.option.default if not isinstance(this.option.default, str) else list(this.option.options.values())[0])
+ theme_text_color: "Primary"
+
+:
+ id: this
+ orientation: "horizontal"
+ spacing: "10dp"
+ padding: (0, 0, "10dp", 0)
+ choice: choice
+
+ MDButton:
+ id: choice
+ text: text
+ MDButtonText:
+ id: text
+ text: this.option.default.title() if this.option.default in this.option.special_range_names else "Custom"
+
+:
+ multiline: False
+ font_size: "15sp"
+ text: self.option.default if isinstance(self.option.default, str) else ""
+ theme_height: "Custom"
+ height: "30dp"
+
+
+:
+ id: this
+ orientation: "horizontal"
+ spacing: "5dp"
+ padding: (0, 0, "10dp", 0)
+
+:
+ id: this
+ button: button
+ MDIconButton:
+ id: button
+ icon: "checkbox-outline" if this.option.default else "checkbox-blank-outline"
+
+:
+ height: "20dp"
+
+:
+ height: "30dp"
+
+:
+ id: this
+ scrollbox: scrollbox
+ add: add
+ save: save
+ input: input
+ focus_behavior: False
+
+ MDDialogHeadlineText:
+ text: getattr(this.option, "display_name", this.name)
+
+ MDDialogSupportingText:
+ text: "Add or Remove Entries"
+
+ MDDialogContentContainer:
+ orientation: "vertical"
+ spacing: 10
+
+ MDBoxLayout:
+ orientation: "horizontal"
+ VisualListSetEntry:
+ id: input
+ height: "20dp"
+
+ MDIconButton:
+ id: add
+ icon: "plus"
+ theme_height: "Custom"
+ height: "20dp"
+ on_press: root.validate_add(input)
+
+ ScrollBox:
+ id: scrollbox
+ size_hint_y: None
+ adapt_minimum: False
+
+ MDButton:
+ id: save
+ MDButtonText:
+ text: "Save Changes"
+
+ContainerLayout:
+ md_bg_color: app.theme_cls.backgroundColor
+
+ MainLayout:
+ id: main
+ cols: 3
+ padding: 3, 5, 0, 3
+ spacing: "2dp"
+
+ ScrollBox:
+ id: scrollbox
+ size_hint_x: None
+ width: "150dp"
+
+ MDDivider:
+ orientation: "vertical"
+ width: "4dp"
+
+ MainLayout:
+ id: player_layout
+ rows: 2
+ spacing: "20dp"
+
+ MDBoxLayout:
+ id: player_options
+ orientation: "horizontal"
+ height: "75dp"
+ size_hint_y: None
+ padding: ["10dp", "30dp", "10dp", 0]
+ spacing: "10dp"
+
+ ResizableTextField:
+ id: player_name
+ multiline: False
+
+ MDTextFieldHintText:
+ text: "Player Name"
+
+ MDTextFieldMaxLengthText:
+ max_text_length: 16
+
+ MDBoxLayout:
+ orientation: "vertical"
+ spacing: "15dp"
+
+ MDLabel:
+ id: game
+ text: "Game: None"
+ pos_hint: {"center_x": 0.5, "center_y": 0.5}
+
+ MDButton:
+ pos_hint: {"center_x": 0.5, "center_y": 0.5}
+ on_press: app.export_options(self)
+ theme_width: "Custom"
+ size_hint_y: 1
+ size_hint_x: 1
+
+ MDButtonText:
+ pos_hint: {"center_x": 0.5, "center_y": 0.5}
+ text: "Export Options"
+
+ MainLayout:
+ cols: 1
+ id: options
diff --git a/deploy/example_config.yaml b/deploy/example_config.yaml
index d74f7f238fcf..f7b25d7b6abc 100644
--- a/deploy/example_config.yaml
+++ b/deploy/example_config.yaml
@@ -8,3 +8,7 @@ SELFLAUNCH: false
# Host Address. This is the address encoded into the patch that will be used for client auto-connect.
# Set as your local IP (192.168.x.x) to serve over LAN.
HOST_ADDRESS: localhost
+
+# Asset redistribution rights. If true, the host affirms they have been given explicit permission to redistribute
+# the proprietary assets in WebHostLib
+#ASSET_RIGHTS: false
diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS
index 7b8e48af142e..0e368386c54f 100644
--- a/docs/CODEOWNERS
+++ b/docs/CODEOWNERS
@@ -15,6 +15,10 @@
# A Link to the Past
/worlds/alttp/ @Berserker66
+# APQuest
+# NewSoupVi is acting maintainer, but world belongs to core with the exception of the music
+/worlds/apquest/ @NewSoupVi
+
# Sudoku (APSudoku)
/worlds/apsudoku/ @EmilyV99
@@ -66,6 +70,9 @@
# DOOM II
/worlds/doom_ii/ @Daivuk @KScl
+# EarthBound
+/worlds/earthbound/ @PinkSwitch
+
# Factorio
/worlds/factorio/ @Berserker66
@@ -172,8 +179,12 @@
# Sonic Adventure 2 Battle
/worlds/sa2b/ @PoryGone @RaspberrySpace
+# Satisfactory
+/worlds/satisfactory/ @Jarno458 @budak7273
+
# Starcraft 2
-/worlds/sc2/ @Ziktofel
+# Note: @Ziktofel acts as a mentor
+/worlds/sc2/ @MatthewMarinets @Snarkie @SirChuckOfTheChuckles
# Super Metroid
/worlds/sm/ @lordlou
diff --git a/docs/apworld specification.md b/docs/apworld specification.md
index 39282e157446..8494aecd5578 100644
--- a/docs/apworld specification.md
+++ b/docs/apworld specification.md
@@ -1,40 +1,83 @@
-# apworld Specification
+# APWorld Specification
Archipelago depends on worlds to provide game-specific details like items, locations and output generation.
-Those are located in the `worlds/` folder (source) or `/lib/worlds/` (when installed).
+These are called "APWorlds".
+They are located in the `worlds/` folder (source) or `/lib/worlds/` (when installed).
See [world api.md](world%20api.md) for details.
+APWorlds can either be a folder, or they can be packaged as an .apworld file.
-apworld provides a way to package and ship a world that is not part of the main distribution by placing a `*.apworld`
-file into the worlds folder.
+## .apworld File Format
-**Warning:** apworlds have to be all lower case, otherwise they raise a bogus Exception when trying to import in frozen python 3.10+!
+The `.apworld` file format provides a way to package and ship an APWorld that is not part of the main distribution
+by placing a `*.apworld` file into the worlds folder.
-
-## File Format
-
-apworld files are zip archives, all lower case, with the file ending `.apworld`.
+`.apworld` files are zip archives, all lower case, with the file ending `.apworld`.
The zip has to contain a folder with the same name as the zip, case-sensitive, that contains what would normally be in
the world's folder in `worlds/`. I.e. `worlds/ror2.apworld` containing `ror2/__init__.py`.
+**Warning:** `.apworld` files have to be all lower case,
+otherwise they raise a bogus Exception when trying to import in frozen python 3.10+!
## Metadata
-Metadata about the apworld is defined in an `archipelago.json` file inside the zip archive.
-The current format version has at minimum:
+Metadata about the APWorld is defined in an `archipelago.json` file.
+
+If the APWorld is a folder, the only required field is "game":
```json
{
- "version": 6,
- "compatible_version": 5,
- "game": "Game Name"
+ "game": "Game Name"
}
```
-with the following optional version fields using the format `"1.0.0"` to represent major.minor.build:
+There are also the following optional fields:
* `minimum_ap_version` and `maximum_ap_version` - which if present will each be compared against the current
- Archipelago version respectively to filter those files from being loaded
+ Archipelago version respectively to filter those files from being loaded.
* `world_version` - an arbitrary version for that world in order to only load the newest valid world.
- An apworld without a world_version is always treated as older than one with a version
+ An APWorld without a world_version is always treated as older than one with a version
+ (**Must** use exactly the format `"major.minor.build"`, e.g. `1.0.0`)
+* `authors` - a list of authors, to eventually be displayed in various user-facing places such as WebHost and
+ package managers. Should always be a list of strings.
+
+If the APWorld is packaged as an `.apworld` zip file, it also needs to have `version` and `compatible_version`,
+which refer to the version of the APContainer packaging scheme defined in [Files.py](../worlds/Files.py).
+These get automatically added to the `archipelago.json` of an .apworld if it is packaged using the
+["Build apworlds" launcher component](#build-apworlds-launcher-component),
+which is the correct way to package your `.apworld` as a world developer. Do not write these fields yourself.
+
+### "Build APWorlds" Launcher Component
+
+In the Archipelago Launcher, there is a "Build APWorlds" component that will package all world folders to `.apworld`,
+and add `archipelago.json` manifest files to them.
+These .apworld files will be output to `build/apworlds` (relative to the Archipelago root directory).
+The `archipelago.json` file in each .apworld will automatically include the appropriate
+`version` and `compatible_version`.
+
+If a world folder has an `archipelago.json` in its root, any fields it contains will be carried over.
+So, a world folder with an `archipelago.json` that looks like this:
+
+```json
+{
+ "game": "Game Name",
+ "minimum_ap_version": "0.6.4",
+ "world_version": "2.1.4",
+ "authors": ["NewSoupVi"]
+}
+```
+
+will be packaged into an `.apworld` with a manifest file inside of it that looks like this:
+
+```json
+{
+ "minimum_ap_version": "0.6.4",
+ "world_version": "2.1.4",
+ "authors": ["NewSoupVi"],
+ "version": 7,
+ "compatible_version": 7,
+ "game": "Game Name"
+}
+```
+This is the recommended workflow for packaging your world to an `.apworld`.
## Extra Data
@@ -43,7 +86,7 @@ The zip can contain arbitrary files in addition what was specified above.
## Caveats
-Imports from other files inside the apworld have to use relative imports. e.g. `from .options import MyGameOptions`
+Imports from other files inside the APWorld have to use relative imports. e.g. `from .options import MyGameOptions`
Imports from AP base have to use absolute imports, e.g. `from Options import Toggle` or
`from worlds.AutoWorld import World`
diff --git a/docs/contributing.md b/docs/contributing.md
index 06d83bebbc5a..689c7655593d 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -20,7 +20,7 @@ game contributions:
It is recommended that automated github actions are turned on in your fork to have github run unit tests after
pushing.
You can turn them on here:
- 
+ 
* **When reviewing PRs, please leave a message about what was done.**
We don't have full test coverage, so manual testing can help.
diff --git a/docs/network protocol.md b/docs/network protocol.md
index c3fc578a896c..0f6a246c1d7f 100644
--- a/docs/network protocol.md
+++ b/docs/network protocol.md
@@ -647,6 +647,16 @@ class Version(NamedTuple):
build: int
```
+If constructing version information as a dict for a custom client rather than as a NamedTuple built into the CommonClient, you must add the `class` key to allow Archipelago to compare version support.
+```
+"version": {
+ "class": "Version",
+ "build": X,
+ "major": Y,
+ "minor": Z
+ }
+```
+
### SlotType
An enum representing the nature of a slot.
diff --git a/docs/options api.md b/docs/options api.md
index 193d3953426a..7234d1cc4980 100644
--- a/docs/options api.md
+++ b/docs/options api.md
@@ -269,7 +269,8 @@ placed on them.
### PriorityLocations
Marks locations given here as `LocationProgressType.Priority` forcing progression items on them if any are available in
-the pool.
+the pool. Progression items without a deprioritized flag will be used first when filling priority_locations. Progression items with
+a deprioritized flag will be used next.
### ItemLinks
Allows users to share their item pool with other players. Currently item links are per game. A link of one game between
diff --git a/docs/webhost api.md b/docs/webhost api.md
index 048211348404..1229704b9c6a 100644
--- a/docs/webhost api.md
+++ b/docs/webhost api.md
@@ -385,6 +385,8 @@ Will provide a dict of static tracker data with the following keys:
- This hash can then be sent to the datapackage API to receive the appropriate datapackage as necessary
- The number of checks found vs. total checks available per player (`player_locations_total`)
- Same logic as the multitracker template: found = len(player_checks_done.locations) / total = player_locations_total.total_locations (all available checks).
+- The game each player is playing (`player_game`)
+ - Provided as a list of objects with `team`, `player`, and `game`.
Example:
```json
@@ -409,10 +411,10 @@ Example:
],
"datapackage": {
"Archipelago": {
- "checksum": "ac9141e9ad0318df2fa27da5f20c50a842afeecb",
+ "checksum": "ac9141e9ad0318df2fa27da5f20c50a842afeecb"
},
"The Messenger": {
- "checksum": "6991cbcda7316b65bcb072667f3ee4c4cae71c0b",
+ "checksum": "6991cbcda7316b65bcb072667f3ee4c4cae71c0b"
}
},
"player_locations_total": [
@@ -427,6 +429,18 @@ Example:
"total_locations": 20
}
],
+ "player_game": [
+ {
+ "team": 0,
+ "player": 1,
+ "game": "Archipelago"
+ },
+ {
+ "team": 0,
+ "player": 2,
+ "game": "The Messenger"
+ }
+ ]
}
```
diff --git a/docs/world api.md b/docs/world api.md
index db6da50cf41f..48e863fb2616 100644
--- a/docs/world api.md
+++ b/docs/world api.md
@@ -225,7 +225,10 @@ and has a classification. The name needs to be unique within each game and must
letter or symbol). The ID needs to be unique across all locations within the game.
Locations and items can share IDs, and locations can share IDs with other games' locations.
-World-specific IDs must be in the range 1 to 253-1; IDs ≤ 0 are global and reserved.
+World-specific IDs **must** be in the range 1 to 253-1 (the largest integer that is "[safe](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER#description)"
+to store in a 64-bit float, and thus all popular programming languages can handle). IDs ≤ 0 are global and reserved.
+It's **recommended** to keep your IDs in the range 1 to 231-1,
+so only 32-bit integers are needed to hold your IDs.
Classification is one of `LocationProgressType.DEFAULT`, `PRIORITY` or `EXCLUDED`.
The Fill algorithm will force progression items to be placed at priority locations, giving a higher chance of them being
diff --git a/entrance_rando.py b/entrance_rando.py
index 578059ae6f5b..a417767036ee 100644
--- a/entrance_rando.py
+++ b/entrance_rando.py
@@ -525,7 +525,7 @@ def find_pairing(dead_end: bool, require_new_exits: bool) -> bool:
running_time = time.perf_counter() - start_time
if running_time > 1.0:
- logging.info(f"Took {running_time:.4f} seconds during entrance randomization for player {world.player},"
+ logging.info(f"Took {running_time:.4f} seconds during entrance randomization for player {world.player}, "
f"named {world.multiworld.player_name[world.player]}")
return er_state
diff --git a/inno_setup.iss b/inno_setup.iss
index 8611c849fb4d..c396224c5631 100644
--- a/inno_setup.iss
+++ b/inno_setup.iss
@@ -180,8 +180,8 @@ Root: HKCR; Subkey: "{#MyAppName}mm2patch\shell\open\command"; ValueData: """{a
Root: HKCR; Subkey: ".apladx"; ValueData: "{#MyAppName}ladxpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}ladxpatch"; ValueData: "Archipelago Links Awakening DX Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
-Root: HKCR; Subkey: "{#MyAppName}ladxpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoLinksAwakeningClient.exe,0"; ValueType: string; ValueName: "";
-Root: HKCR; Subkey: "{#MyAppName}ladxpatch\shell\open\command"; ValueData: """{app}\ArchipelagoLinksAwakeningClient.exe"" ""%1"""; ValueType: string; ValueName: "";
+Root: HKCR; Subkey: "{#MyAppName}ladxpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoLauncher.exe,0"; ValueType: string; ValueName: "";
+Root: HKCR; Subkey: "{#MyAppName}ladxpatch\shell\open\command"; ValueData: """{app}\ArchipelagoLauncher.exe"" ""%1"""; ValueType: string; ValueName: "";
Root: HKCR; Subkey: ".aptloz"; ValueData: "{#MyAppName}tlozpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}tlozpatch"; ValueData: "Archipelago The Legend of Zelda Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
@@ -208,6 +208,11 @@ Root: HKCR; Subkey: "{#MyAppName}apcivvipatch"; ValueData: "
Root: HKCR; Subkey: "{#MyAppName}apcivvipatch\DefaultIcon"; ValueData: "{app}\ArchipelagoLauncher.exe,0"; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}apcivvipatch\shell\open\command"; ValueData: """{app}\ArchipelagoLauncher.exe"" ""%1"""; ValueType: string; ValueName: "";
+Root: HKCR; Subkey: ".apeb"; ValueData: "{#MyAppName}ebpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
+Root: HKCR; Subkey: "{#MyAppName}ebpatch"; ValueData: "Archipelago EarthBound Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
+Root: HKCR; Subkey: "{#MyAppName}ebpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: "";
+Root: HKCR; Subkey: "{#MyAppName}ebpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: "";
+
Root: HKCR; Subkey: ".archipelago"; ValueData: "{#MyAppName}multidata"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}multidata"; ValueData: "Archipelago Server Data"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}multidata\DefaultIcon"; ValueData: "{app}\ArchipelagoServer.exe,0"; ValueType: string; ValueName: "";
diff --git a/kvui.py b/kvui.py
index 86297c497940..b0091b5a200e 100644
--- a/kvui.py
+++ b/kvui.py
@@ -34,6 +34,28 @@
Config.set("input", "mouse", "mouse,disable_multitouch")
Config.set("kivy", "exit_on_escape", "0")
Config.set("graphics", "multisamples", "0") # multisamples crash old intel drivers
+
+# Workaround for Kivy issue #9226.
+# caused by kivy by default using probesysfs,
+# which assumes all multi touch deviecs are touch screens.
+# workaround provided by Snu of the kivy commmunity c:
+from kivy.utils import platform
+if platform == "linux":
+ options = Config.options("input")
+ for option in options:
+ if Config.get("input", option) == "probesysfs":
+ Config.remove_option("input", option)
+
+# Workaround for an issue where importing kivy.core.window before loading sounds
+# will hang the whole application on Linux once the first sound is loaded.
+# kivymd imports kivy.core.window, so we have to do this before the first kivymd import.
+# No longer necessary when we switch to kivy 3.0.0, which fixes this issue.
+from kivy.core.audio import SoundLoader
+for classobj in SoundLoader._classes:
+ # The least invasive way to force a SoundLoader class to load its audio engine seems to be calling
+ # .extensions(), which e.g. in audio_sdl2.pyx then calls a function called "mix_init()"
+ classobj.extensions()
+
from kivymd.uix.divider import MDDivider
from kivy.core.window import Window
from kivy.core.clipboard import Clipboard
@@ -116,7 +138,7 @@ def __init__(self, **kwargs):
val = kwargs.pop(kwarg, "None")
if val != "None":
image_args[kwarg.replace("image_", "")] = val
- super().__init__()
+ super().__init__(**kwargs)
self.image = ApAsyncImage(**image_args)
def set_center(button, center):
@@ -132,6 +154,7 @@ def add_widget(self, widget, index=0, canvas=None):
class ScrollBox(MDScrollView):
layout: MDBoxLayout = ObjectProperty(None)
+ box_height: int = NumericProperty(dp(100))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -142,6 +165,7 @@ class ToggleButton(MDButton, ToggleButtonBehavior):
def __init__(self, *args, **kwargs):
super(ToggleButton, self).__init__(*args, **kwargs)
self.bind(state=self._update_bg)
+ self._update_bg(self, self.state)
def _update_bg(self, _, state: str):
if self.disabled:
@@ -159,7 +183,7 @@ def _update_bg(self, _, state: str):
child.text_color = self.theme_cls.onPrimaryColor
child.icon_color = self.theme_cls.onPrimaryColor
else:
- self.md_bg_color = self.theme_cls.surfaceContainerLowestColor
+ self.md_bg_color = self.theme_cls.surfaceContainerLowColor
for child in self.children:
if child.theme_text_color == "Primary":
child.theme_text_color = "Custom"
@@ -173,7 +197,6 @@ def _update_bg(self, _, state: str):
class ResizableTextField(MDTextField):
"""
Resizable MDTextField that manually overrides the builtin sizing.
-
Note that in order to use this, the sizing must be specified from within a .kv rule.
"""
def __init__(self, *args, **kwargs):
@@ -237,7 +260,7 @@ def on_cursor_leave(self, *args):
class ToolTip(MDTooltipPlain):
- pass
+ markup = True
class ServerToolTip(ToolTip):
@@ -272,6 +295,8 @@ def create_tooltip(self, text, x, y):
def on_mouse_pos(self, window, pos):
if not self.get_root_window():
return # Abort if not displayed
+ if self.disabled:
+ return
super().on_mouse_pos(window, pos)
if self.refs and self.hovered:
@@ -1099,10 +1124,6 @@ def update_hints(self):
hints = self.ctx.stored_data.get(f"_read_hints_{self.ctx.team}_{self.ctx.slot}", [])
self.hint_log.refresh_hints(hints)
- # default F1 keybind, opens a settings menu, that seems to break the layout engine once closed
- def open_settings(self, *largs):
- pass
-
class LogtoUI(logging.Handler):
def __init__(self, on_log):
diff --git a/requirements.txt b/requirements.txt
index 951db610d311..928909b87b1b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,17 +1,17 @@
colorama>=0.4.6
websockets>=13.0.1,<14
-PyYAML>=6.0.2
-jellyfish>=1.1.3
+PyYAML>=6.0.3
+jellyfish>=1.2.1
jinja2>=3.1.6
-schema>=0.7.7
+schema>=0.7.8
kivy>=2.3.1
bsdiff4>=1.2.6
-platformdirs>=4.3.6
-certifi>=2025.4.26
-cython>=3.0.12
-cymem>=2.0.11
-orjson>=3.10.15
-typing_extensions>=4.12.2
-pyshortcuts>=1.9.1
+platformdirs>=4.5.0
+certifi>=2025.11.12
+cython>=3.2.1
+cymem>=2.0.13
+orjson>=3.11.4
+typing_extensions>=4.15.0
+pyshortcuts>=1.9.6
kivymd @ git+https://github.com/kivymd/KivyMD@5ff9d0d
kivymd>=2.0.1.dev0
diff --git a/ruff.toml b/ruff.toml
new file mode 100644
index 000000000000..ee2830cc1e2f
--- /dev/null
+++ b/ruff.toml
@@ -0,0 +1,16 @@
+line-length = 120
+indent-width = 4
+target-version = "py311"
+
+[lint]
+select = ["B", "C", "E", "F", "W", "I", "N", "Q", "UP", "RET", "RSE", "RUF", "ISC", "PLC", "PLE", "PLW", "T20", "PERF"]
+ignore = [
+ "B011", # In AP, the use of assert False is essential because we optimise out these statements for release builds.
+ "C901", # Author disagrees with limiting branch complexity
+ "N818", # Author agrees with this rule, but Core AP violates this and changing it would be a hassle.
+ "PLC0415", # In AP, we consider local imports totally fine & necessary
+ "PLC1802", # Author agrees with this rule, but it literally changes the functionality of the code, which is unsafe.
+ "PLC1901", # This is just not equivalent
+ "PLE1141", # Gives false positives when the dict keys are tuples, but does not mention this in the suggested fix.
+ "UP015", # Explicit is better than implicit, so we'd prefer to keep "r" in open() calls.
+]
diff --git a/settings.py b/settings.py
index 48bc57f04467..3b3d0deec73f 100644
--- a/settings.py
+++ b/settings.py
@@ -579,6 +579,17 @@ class RemainingMode(str):
"goal" -> Client can ask for remaining items after goal completion
"""
+ class CountdownMode(str):
+ """
+ Countdown modes
+ Determines whether or not a player can initiate a countdown with !countdown
+ Note that /countdown is always available to the host.
+
+ "enabled" -> Client can always initiate a countdown with !countdown.
+ "disabled" -> Client can never initiate a countdown with !countdown.
+ "auto" -> !countdown will be available for any room with less than 30 slots.
+ """
+
class AutoShutdown(int):
"""Automatically shut down the server after this many seconds without new location checks, 0 to keep running"""
@@ -613,6 +624,7 @@ class LogNetwork(IntEnum):
release_mode: ReleaseMode = ReleaseMode("auto")
collect_mode: CollectMode = CollectMode("auto")
remaining_mode: RemainingMode = RemainingMode("goal")
+ countdown_mode: CountdownMode = CountdownMode("auto")
auto_shutdown: AutoShutdown = AutoShutdown(0)
compatibility: Compatibility = Compatibility(2)
log_network: LogNetwork = LogNetwork(0)
diff --git a/setup.py b/setup.py
index f1d052b81570..949b1e3e303b 100644
--- a/setup.py
+++ b/setup.py
@@ -146,7 +146,16 @@ def download_SNI() -> None:
signtool: str | None = None
try:
- with urllib.request.urlopen('http://192.168.206.4:12345/connector/status') as response:
+ import socket
+
+ sign_host, sign_port = "192.168.206.4", 12345
+ # check if the sign_host is on a local network
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect((sign_host, sign_port))
+ if s.getsockname()[0].rsplit(".", 1)[0] != sign_host.rsplit(".", 1)[0]:
+ raise ConnectionError() # would go through default route
+ # configure signtool
+ with urllib.request.urlopen(f"http://{sign_host}:{sign_port}/connector/status") as response:
html = response.read()
if b"status=OK\n" in html:
signtool = (r'signtool sign /sha1 6df76fe776b82869a5693ddcb1b04589cffa6faf /fd sha256 /td sha256 '
@@ -381,14 +390,15 @@ def run(self) -> None:
file_name = os.path.split(os.path.dirname(worldtype.__file__))[1]
world_directory = self.libfolder / "worlds" / file_name
if os.path.isfile(world_directory / "archipelago.json"):
- manifest = json.load(open(world_directory / "archipelago.json"))
+ with open(os.path.join(world_directory, "archipelago.json"), mode="r", encoding="utf-8") as manifest_file:
+ manifest = json.load(manifest_file)
assert "game" in manifest, (
- f"World directory {world_directory} has an archipelago.json manifest file, but it"
+ f"World directory {world_directory} has an archipelago.json manifest file, but it "
"does not define a \"game\"."
)
assert manifest["game"] == worldtype.game, (
- f"World directory {world_directory} has an archipelago.json manifest file, but value of the"
+ f"World directory {world_directory} has an archipelago.json manifest file, but value of the "
f"\"game\" field ({manifest['game']} does not equal the World class's game ({worldtype.game})."
)
else:
diff --git a/test/benchmark/compression/__init__.py b/test/benchmark/compression/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/test/benchmark/compression/benchmark.py b/test/benchmark/compression/benchmark.py
new file mode 100644
index 000000000000..e620894fe1f2
--- /dev/null
+++ b/test/benchmark/compression/benchmark.py
@@ -0,0 +1,227 @@
+#!/usr/bin/env python
+
+# based on python-websockets compression benchmark (c) Aymeric Augustin and contributors
+# https://github.com/python-websockets/websockets/blob/main/experiments/compression/benchmark.py
+
+import collections
+import time
+import zlib
+from typing import Iterable
+
+
+REPEAT = 10
+
+WB, ML = 12, 5 # defaults used as a reference
+WBITS = range(9, 16)
+MEMLEVELS = range(1, 10)
+
+
+def benchmark(data: Iterable[bytes]) -> None:
+ size: dict[int, dict[int, float]] = collections.defaultdict(dict)
+ duration: dict[int, dict[int, float]] = collections.defaultdict(dict)
+
+ for wbits in WBITS:
+ for memLevel in MEMLEVELS:
+ encoder = zlib.compressobj(wbits=-wbits, memLevel=memLevel)
+ encoded = []
+
+ print(f"Compressing {REPEAT} times with {wbits=} and {memLevel=}")
+
+ t0 = time.perf_counter()
+
+ for _ in range(REPEAT):
+ for item in data:
+ # Taken from PerMessageDeflate.encode
+ item = encoder.compress(item) + encoder.flush(zlib.Z_SYNC_FLUSH)
+ if item.endswith(b"\x00\x00\xff\xff"):
+ item = item[:-4]
+ encoded.append(item)
+
+ t1 = time.perf_counter()
+
+ size[wbits][memLevel] = sum(len(item) for item in encoded) / REPEAT
+ duration[wbits][memLevel] = (t1 - t0) / REPEAT
+
+ raw_size = sum(len(item) for item in data)
+
+ print("=" * 79)
+ print("Compression ratio")
+ print("=" * 79)
+ print("\t".join(["wb \\ ml"] + [str(memLevel) for memLevel in MEMLEVELS]))
+ for wbits in WBITS:
+ print(
+ "\t".join(
+ [str(wbits)]
+ + [
+ f"{100 * (1 - size[wbits][memLevel] / raw_size):.1f}%"
+ for memLevel in MEMLEVELS
+ ]
+ )
+ )
+ print("=" * 79)
+ print()
+
+ print("=" * 79)
+ print("CPU time")
+ print("=" * 79)
+ print("\t".join(["wb \\ ml"] + [str(memLevel) for memLevel in MEMLEVELS]))
+ for wbits in WBITS:
+ print(
+ "\t".join(
+ [str(wbits)]
+ + [
+ f"{1000 * duration[wbits][memLevel]:.1f}ms"
+ for memLevel in MEMLEVELS
+ ]
+ )
+ )
+ print("=" * 79)
+ print()
+
+ print("=" * 79)
+ print(f"Size vs. {WB} \\ {ML}")
+ print("=" * 79)
+ print("\t".join(["wb \\ ml"] + [str(memLevel) for memLevel in MEMLEVELS]))
+ for wbits in WBITS:
+ print(
+ "\t".join(
+ [str(wbits)]
+ + [
+ f"{100 * (size[wbits][memLevel] / size[WB][ML] - 1):.1f}%"
+ for memLevel in MEMLEVELS
+ ]
+ )
+ )
+ print("=" * 79)
+ print()
+
+ print("=" * 79)
+ print(f"Time vs. {WB} \\ {ML}")
+ print("=" * 79)
+ print("\t".join(["wb \\ ml"] + [str(memLevel) for memLevel in MEMLEVELS]))
+ for wbits in WBITS:
+ print(
+ "\t".join(
+ [str(wbits)]
+ + [
+ f"{100 * (duration[wbits][memLevel] / duration[WB][ML] - 1):.1f}%"
+ for memLevel in MEMLEVELS
+ ]
+ )
+ )
+ print("=" * 79)
+ print()
+
+
+def generate_data_package_corpus() -> list[bytes]:
+ # compared to default 12, 5:
+ # 11, 4 saves 16K RAM, gives +4.6% size, -5.0% time .. +1.1% time
+ # 10, 4 saves 20K RAM, gives +10.2% size, -3.8% time .. +0.6% time
+ # 11, 3 saves 20K RAM, gives +6.5% size, +14.2% time
+ # 10, 3 saves 24K RAM, gives +12.8% size, +0.5% time .. +6.9% time
+ # NOTE: time delta is highly unstable; time is ~100ms
+ import warnings
+
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+
+ from NetUtils import encode
+ from worlds import network_data_package
+
+ return [encode(network_data_package).encode("utf-8")]
+
+
+def generate_solo_release_corpus() -> list[bytes]:
+ # compared to default 12, 5:
+ # 11, 4 saves 16K RAM, gives +0.9% size, +3.9% time
+ # 10, 4 saves 20K RAM, gives +1.4% size, +3.4% time
+ # 11, 3 saves 20K RAM, gives +1.8% size, +13.9% time
+ # 10, 3 saves 24K RAM, gives +2.1% size, +4.8% time
+ # NOTE: time delta is highly unstable; time is ~0.4ms
+
+ from random import Random
+ from MultiServer import json_format_send_event
+ from NetUtils import encode, NetworkItem
+
+ r = Random()
+ r.seed(0)
+ solo_release = []
+ solo_release_locations = [r.randint(1000, 1999) for _ in range(200)]
+ solo_release_items = sorted([r.randint(1000, 1999) for _ in range(200)]) # currently sorted by item
+ solo_player = 1
+ for location, item in zip(solo_release_locations, solo_release_items):
+ flags = r.choice((0, 0, 0, 0, 0, 0, 0, 1, 2, 3))
+ network_item = NetworkItem(item, location, solo_player, flags)
+ solo_release.append(json_format_send_event(network_item, solo_player))
+ solo_release.append({
+ "cmd": "ReceivedItems",
+ "index": 0,
+ "items": solo_release_items,
+ })
+ solo_release.append({
+ "cmd": "RoomUpdate",
+ "hint_points": 200,
+ "checked_locations": solo_release_locations,
+ })
+ return [encode(solo_release).encode("utf-8")]
+
+
+def generate_gameplay_corpus() -> list[bytes]:
+ # compared to default 12, 5:
+ # 11, 4 saves 16K RAM, gives +13.6% size, +4.1% time
+ # 10, 4 saves 20K RAM, gives +22.3% size, +2.2% time
+ # 10, 3 saves 24K RAM, gives +26.2% size, +1.6% time
+ # NOTE: time delta is highly unstable; time is 4ms
+
+ from copy import copy
+ from random import Random
+ from MultiServer import json_format_send_event
+ from NetUtils import encode, NetworkItem
+
+ r = Random()
+ r.seed(0)
+ gameplay = []
+ observer = 1
+ hint_points = 0
+ index = 0
+ players = list(range(1, 10))
+ player_locations = {player: [r.randint(1000, 1999) for _ in range(200)] for player in players}
+ player_items = {player: [r.randint(1000, 1999) for _ in range(200)] for player in players}
+ player_receiver = {player: [r.randint(1, len(players)) for _ in range(200)] for player in players}
+ for i in range(0, len(player_locations[1])):
+ player_sequence = copy(players)
+ r.shuffle(player_sequence)
+ for finder in player_sequence:
+ flags = r.choice((0, 0, 0, 0, 0, 0, 0, 1, 2, 3))
+ receiver = player_receiver[finder][i]
+ item = player_items[finder][i]
+ location = player_locations[finder][i]
+ network_item = NetworkItem(item, location, receiver, flags)
+ gameplay.append(json_format_send_event(network_item, observer))
+ if finder == observer:
+ hint_points += 1
+ gameplay.append({
+ "cmd": "RoomUpdate",
+ "hint_points": hint_points,
+ "checked_locations": [location],
+ })
+ if receiver == observer:
+ gameplay.append({
+ "cmd": "ReceivedItems",
+ "index": index,
+ "items": [item],
+ })
+ index += 1
+ return [encode(gameplay).encode("utf-8")]
+
+
+def main() -> None:
+ #corpus = generate_data_package_corpus()
+ #corpus = generate_solo_release_corpus()
+ #corpus = generate_gameplay_corpus()
+ corpus = generate_data_package_corpus() + generate_solo_release_corpus() + generate_gameplay_corpus()
+ benchmark(corpus)
+ print(f"raw size: {sum(len(data) for data in corpus)}")
+
+if __name__ == "__main__":
+ main()
diff --git a/test/benchmark/locations.py b/test/benchmark/locations.py
index 0e496cd3eefb..245dcb604e07 100644
--- a/test/benchmark/locations.py
+++ b/test/benchmark/locations.py
@@ -1,4 +1,12 @@
-def run_locations_benchmark():
+def run_locations_benchmark(freeze_gc: bool = True) -> None:
+ """
+ Run a benchmark of location access rule performance against an empty_state and an all_state.
+
+ :param freeze_gc: Whether to freeze gc before benchmarking and unfreeze gc afterward. Freezing gc moves all objects
+ tracked by the garbage collector to a permanent generation, ignoring them in all future collections. Freezing
+ greatly reduces the duration of running gc.collect() within benchmarks, which otherwise often takes much longer
+ than running all iterations for the location rule being benchmarked.
+ """
import argparse
import logging
import gc
@@ -34,6 +42,8 @@ def format_times_from_counter(counter: collections.Counter[str], top: int = 5) -
return "\n".join(f" {time:.4f} in {name}" for name, time in counter.most_common(top))
def location_test(self, test_location: Location, state: CollectionState, state_name: str) -> float:
+ if freeze_gc:
+ gc.freeze()
with TimeIt(f"{test_location.game} {self.rule_iterations} "
f"runs of {test_location}.access_rule({state_name})", logger) as t:
for _ in range(self.rule_iterations):
@@ -41,6 +51,8 @@ def location_test(self, test_location: Location, state: CollectionState, state_n
# if time is taken to disentangle complex ref chains,
# this time should be attributed to the rule.
gc.collect()
+ if freeze_gc:
+ gc.unfreeze()
return t.dif
def main(self):
@@ -64,9 +76,13 @@ def main(self):
gc.collect()
for step in self.gen_steps:
+ if freeze_gc:
+ gc.freeze()
with TimeIt(f"{game} step {step}", logger):
call_all(multiworld, step)
gc.collect()
+ if freeze_gc:
+ gc.unfreeze()
locations = sorted(multiworld.get_unfilled_locations())
if not locations:
diff --git a/test/general/test_options.py b/test/general/test_options.py
index d8ce7017f27b..cbd8d7b53335 100644
--- a/test/general/test_options.py
+++ b/test/general/test_options.py
@@ -1,7 +1,7 @@
import unittest
from BaseClasses import PlandoOptions
-from Options import ItemLinks, Choice
+from Options import Choice, ItemLinks, PlandoConnections, PlandoItems, PlandoTexts
from Utils import restricted_dumps
from worlds.AutoWorld import AutoWorldRegister
@@ -44,19 +44,19 @@ def test_item_links_name_groups(self):
}],
[{
"name": "ItemLinkGroup",
- "item_pool": ["Hammer", "Bow"],
+ "item_pool": ["Hammer", "Sword"],
"link_replacement": False,
"replacement_item": None,
}]
]
# we really need some sort of test world but generic doesn't have enough items for this
- world = AutoWorldRegister.world_types["A Link to the Past"]
+ world = AutoWorldRegister.world_types["APQuest"]
plando_options = PlandoOptions.from_option_string("bosses")
item_links = [ItemLinks.from_any(item_link_groups[0]), ItemLinks.from_any(item_link_groups[1])]
for link in item_links:
link.verify(world, "tester", plando_options)
self.assertIn("Hammer", link.value[0]["item_pool"])
- self.assertIn("Bow", link.value[0]["item_pool"])
+ self.assertIn("Sword", link.value[0]["item_pool"])
# TODO test that the group created using these options has the items
@@ -72,8 +72,8 @@ def test_item_links_resolve(self):
for link in item_links.values():
self.assertEqual(link.value[0], item_link_group[0])
- def test_pickle_dumps(self):
- """Test options can be pickled into database for WebHost generation"""
+ def test_pickle_dumps_default(self):
+ """Test that default option values can be pickled into database for WebHost generation"""
for gamename, world_type in AutoWorldRegister.world_types.items():
if not world_type.hidden:
for option_key, option in world_type.options_dataclass.type_hints.items():
@@ -81,3 +81,23 @@ def test_pickle_dumps(self):
restricted_dumps(option.from_any(option.default))
if issubclass(option, Choice) and option.default in option.name_lookup:
restricted_dumps(option.from_text(option.name_lookup[option.default]))
+
+ def test_pickle_dumps_plando(self):
+ """Test that plando options using containers of a custom type can be pickled"""
+ # The base PlandoConnections class can't be instantiated directly, create a subclass and then cast it
+ class TestPlandoConnections(PlandoConnections):
+ entrances = {"An Entrance"}
+ exits = {"An Exit"}
+ plando_connection_value = PlandoConnections(
+ TestPlandoConnections.from_any([{"entrance": "An Entrance", "exit": "An Exit"}])
+ )
+
+ plando_values = {
+ "PlandoConnections": plando_connection_value,
+ "PlandoItems": PlandoItems.from_any([{"item": "Something", "location": "Somewhere"}]),
+ "PlandoTexts": PlandoTexts.from_any([{"text": "Some text.", "at": "text_box"}]),
+ }
+
+ for option_key, value in plando_values.items():
+ with self.subTest(option=option_key):
+ restricted_dumps(value)
diff --git a/test/general/test_player_options.py b/test/general/test_player_options.py
index ea7f19e3d917..7dd612f4f1db 100644
--- a/test/general/test_player_options.py
+++ b/test/general/test_player_options.py
@@ -37,3 +37,23 @@ def test_update_weights(self):
self.assertEqual(new_weights["dict_2"]["option_g"], 50)
self.assertEqual(len(new_weights["set_1"]), 2)
self.assertIn("option_d", new_weights["set_1"])
+
+ def test_update_dict_supports_negatives_and_zeroes(self):
+ original_options = {
+ "dict_1": {"a": 1, "b": -1},
+ "dict_2": {"a": 1, "b": -1},
+ }
+ new_weights = Generate.update_weights(
+ original_options,
+ {
+ "+dict_1": {"a": -2, "b": 2},
+ "-dict_2": {"a": 1, "b": 2},
+ },
+ "Tested",
+ "",
+ )
+ self.assertEqual(new_weights["dict_1"]["a"], -1)
+ self.assertEqual(new_weights["dict_1"]["b"], 1)
+ self.assertEqual(new_weights["dict_2"]["a"], 0)
+ self.assertEqual(new_weights["dict_2"]["b"], -3)
+ self.assertIn("a", new_weights["dict_2"])
diff --git a/test/general/test_world_manifest.py b/test/general/test_world_manifest.py
new file mode 100644
index 000000000000..3aaa93b0ac19
--- /dev/null
+++ b/test/general/test_world_manifest.py
@@ -0,0 +1,102 @@
+"""Check world sources' manifest files"""
+
+import json
+import unittest
+from pathlib import Path
+from typing import Any, ClassVar
+
+import test
+from Utils import home_path, local_path
+from worlds.AutoWorld import AutoWorldRegister
+from ..param import classvar_matrix
+
+
+test_path = Path(test.__file__).parent
+worlds_paths = [
+ Path(local_path("worlds")),
+ Path(local_path("custom_worlds")),
+ Path(home_path("worlds")),
+ Path(home_path("custom_worlds")),
+]
+
+# Only check source folders for now. Zip validation should probably be in the loader and/or installer.
+source_world_names = [
+ k
+ for k, v in AutoWorldRegister.world_types.items()
+ if not v.zip_path and not Path(v.__file__).is_relative_to(test_path)
+]
+
+
+def get_source_world_manifest_path(game: str) -> Path | None:
+ """Get path of archipelago.json in the world's root folder from game name."""
+ # TODO: add a feature to AutoWorld that makes this less annoying
+ world_type = AutoWorldRegister.world_types[game]
+ world_type_path = Path(world_type.__file__)
+ for worlds_path in worlds_paths:
+ if world_type_path.is_relative_to(worlds_path):
+ world_root = worlds_path / world_type_path.relative_to(worlds_path).parents[0]
+ manifest_path = world_root / "archipelago.json"
+ return manifest_path if manifest_path.exists() else None
+ assert False, f"{world_type_path} not found in any worlds path"
+
+
+# TODO: remove the filter once manifests are mandatory.
+@classvar_matrix(game=filter(get_source_world_manifest_path, source_world_names))
+class TestWorldManifest(unittest.TestCase):
+ game: ClassVar[str]
+ manifest: ClassVar[dict[str, Any]]
+
+ @classmethod
+ def setUpClass(cls) -> None:
+ world_type = AutoWorldRegister.world_types[cls.game]
+ assert world_type.game == cls.game
+ manifest_path = get_source_world_manifest_path(cls.game)
+ assert manifest_path # make mypy happy
+ with manifest_path.open("r", encoding="utf-8") as f:
+ cls.manifest = json.load(f)
+
+ def test_game(self) -> None:
+ """Test that 'game' will be correctly defined when generating APWorld manifest from source."""
+ self.assertIn(
+ "game",
+ self.manifest,
+ f"archipelago.json manifest exists for {self.game} but does not contain 'game'",
+ )
+ self.assertEqual(
+ self.manifest["game"],
+ self.game,
+ f"archipelago.json manifest for {self.game} specifies wrong game '{self.manifest['game']}'",
+ )
+
+ def test_world_version(self) -> None:
+ """Test that world_version matches the requirements in apworld specification.md"""
+ if "world_version" in self.manifest:
+ world_version: str = self.manifest["world_version"]
+ self.assertIsInstance(
+ world_version,
+ str,
+ f"world_version in archipelago.json for '{self.game}' has to be string if provided.",
+ )
+ parts = world_version.split(".")
+ self.assertEqual(
+ len(parts),
+ 3,
+ f"world_version in archipelago.json for '{self.game}' has to be in the form of 'major.minor.build'.",
+ )
+ for part in parts:
+ self.assertTrue(
+ part.isdigit(),
+ f"world_version in archipelago.json for '{self.game}' may only contain numbers.",
+ )
+
+ def test_no_container_version(self) -> None:
+ self.assertNotIn(
+ "version",
+ self.manifest,
+ f"archipelago.json for '{self.game}' must not define 'version', see apworld specification.md.",
+ )
+ self.assertNotIn(
+ "compatible_version",
+ self.manifest,
+ f"archipelago.json for '{self.game}' must not define 'compatible_version', see apworld specification.md.",
+ )
diff --git a/test/hosting/__main__.py b/test/hosting/__main__.py
index e235d7bb7218..7ac2700eb8c6 100644
--- a/test/hosting/__main__.py
+++ b/test/hosting/__main__.py
@@ -3,6 +3,7 @@
# Run with `python test/hosting` instead,
import logging
import traceback
+from pathlib import Path
from tempfile import TemporaryDirectory
from time import sleep
from typing import Any
@@ -11,7 +12,7 @@
from test.hosting.generate import generate_local
from test.hosting.serve import ServeGame, LocalServeGame, WebHostServeGame
from test.hosting.webhost import (create_room, get_app, get_multidata_for_room, set_multidata_for_room, start_room,
- stop_autohost, upload_multidata)
+ stop_autogen, stop_autohost, upload_multidata, generate_remote)
from test.hosting.world import copy as copy_world, delete as delete_world
failure = False
@@ -56,35 +57,62 @@ def expect_equal(first: Any, second: Any, msg: str = "") -> None:
if __name__ == "__main__":
+ import sys
import warnings
+
warnings.simplefilter("ignore", ResourceWarning)
warnings.simplefilter("ignore", UserWarning)
+ warnings.simplefilter("ignore", DeprecationWarning)
spacer = '=' * 80
with TemporaryDirectory() as tempdir:
- multis = [["VVVVVV"], ["Temp World"], ["VVVVVV", "Temp World"]]
- p1_games = []
- data_paths = []
- rooms = []
-
- copy_world("VVVVVV", "Temp World")
+ empty_file = str(Path(tempdir) / "empty")
+ open(empty_file, "w").close()
+ sys.argv += ["--config_override", empty_file] # tests #5541
+ multis = [["APQuest"], ["Temp World"], ["APQuest", "Temp World"]]
+ p1_games: list[str] = []
+ data_paths: list[Path | None] = []
+ rooms: list[str] = []
+ multidata: Path | None
+
+ copy_world("APQuest", "Temp World")
try:
for n, games in enumerate(multis, 1):
- print(f"Generating [{n}] {', '.join(games)}")
+ print(f"Generating [{n}] {', '.join(games)} offline")
multidata = generate_local(games, tempdir)
print(f"Generated [{n}] {', '.join(games)} as {multidata}\n")
- p1_games.append(games[0])
data_paths.append(multidata)
+ p1_games.append(games[0])
finally:
delete_world("Temp World")
webapp = get_app(tempdir)
webhost_client = webapp.test_client()
+
for n, multidata in enumerate(data_paths, 1):
+ assert multidata
seed = upload_multidata(webhost_client, multidata)
+ print(f"Uploaded [{n}] {multidata} as {seed}\n")
room = create_room(webhost_client, seed)
- print(f"Uploaded [{n}] {multidata} as {room}\n")
+ print(f"Started [{n}] {seed} as {room}\n")
+ rooms.append(room)
+
+ # Generate 1 extra game on WebHost
+ from WebHostLib.autolauncher import autogen
+ for n, games in enumerate(multis[:1], len(multis) + 1):
+ multis.append(games)
+ try:
+ print(f"Generating [{n}] {', '.join(games)} online")
+ autogen(webapp.config)
+ sleep(5) # until we have lazy loading of worlds, wait here for the process to start up
+ seed = generate_remote(webhost_client, games)
+ print(f"Generated [{n}] {', '.join(games)} as {seed}\n")
+ finally:
+ stop_autogen()
+ data_paths.append(None) # WebHost-only
+ room = create_room(webhost_client, seed)
+ print(f"Started [{n}] {seed} as {room}\n")
rooms.append(room)
print("Starting autohost")
@@ -96,31 +124,10 @@ def expect_equal(first: Any, second: Any, msg: str = "") -> None:
for n, (multidata, room, game, multi_games) in enumerate(zip(data_paths, rooms, p1_games, multis), 1):
involved_games = {"Archipelago"} | set(multi_games)
for collected_items in range(3):
- print(f"\nTesting [{n}] {game} in {multidata} on MultiServer with {collected_items} items collected")
- with LocalServeGame(multidata) as host:
- with Client(host.address, game, "Player1") as client:
- local_data_packages = client.games_packages
- local_collected_items = len(client.checked_locations)
- if collected_items < 2: # Don't collect anything on the last iteration
- client.collect_any()
- # TODO: Ctrl+C test here as well
-
- for game_name in sorted(involved_games):
- expect_true(game_name in local_data_packages,
- f"{game_name} missing from MultiServer datap ackage")
- expect_true("item_name_groups" not in local_data_packages.get(game_name, {}),
- f"item_name_groups are not supposed to be in MultiServer data for {game_name}")
- expect_true("location_name_groups" not in local_data_packages.get(game_name, {}),
- f"location_name_groups are not supposed to be in MultiServer data for {game_name}")
- for game_name in local_data_packages:
- expect_true(game_name in involved_games,
- f"Received unexpected extra data package for {game_name} from MultiServer")
- assert_equal(local_collected_items, collected_items,
- "MultiServer did not load or save correctly")
-
print(f"\nTesting [{n}] {game} in {multidata} on customserver with {collected_items} items collected")
prev_host_adr: str
with WebHostServeGame(webhost_client, room) as host:
+ sleep(.1) # wait for the server to fully start before doing anything
prev_host_adr = host.address
with Client(host.address, game, "Player1") as client:
web_data_packages = client.games_packages
@@ -134,6 +141,7 @@ def expect_equal(first: Any, second: Any, msg: str = "") -> None:
autohost(webapp.config) # this will spin the room right up again
sleep(1) # make log less annoying
# if saving failed, the next iteration will fail below
+ sleep(2) # work around issue #5571
# verify server shut down
try:
@@ -156,6 +164,31 @@ def expect_equal(first: Any, second: Any, msg: str = "") -> None:
"customserver did not load or save correctly during/after "
+ ("Ctrl+C" if collected_items == 2 else "/exit"))
+ if not multidata:
+ continue # games rolled on WebHost can not be tested against MultiServer
+
+ print(f"\nTesting [{n}] {game} in {multidata} on MultiServer with {collected_items} items collected")
+ with LocalServeGame(multidata) as host:
+ with Client(host.address, game, "Player1") as client:
+ local_data_packages = client.games_packages
+ local_collected_items = len(client.checked_locations)
+ if collected_items < 2: # Don't collect anything on the last iteration
+ client.collect_any()
+ # TODO: Ctrl+C test here as well
+
+ for game_name in sorted(involved_games):
+ expect_true(game_name in local_data_packages,
+ f"{game_name} missing from MultiServer datapackage")
+ expect_true("item_name_groups" not in local_data_packages.get(game_name, {}),
+ f"item_name_groups are not supposed to be in MultiServer data for {game_name}")
+ expect_true("location_name_groups" not in local_data_packages.get(game_name, {}),
+ f"location_name_groups are not supposed to be in MultiServer data for {game_name}")
+ for game_name in local_data_packages:
+ expect_true(game_name in involved_games,
+ f"Received unexpected extra data package for {game_name} from MultiServer")
+ assert_equal(local_collected_items, collected_items,
+ "MultiServer did not load or save correctly")
+
# compare customserver to MultiServer
expect_equal(local_data_packages, web_data_packages,
"customserver datapackage differs from MultiServer")
@@ -176,10 +209,12 @@ def expect_equal(first: Any, second: Any, msg: str = "") -> None:
print(f"Restoring multidata for {room}")
set_multidata_for_room(webhost_client, room, old_data)
with WebHostServeGame(webhost_client, room) as host:
+ sleep(.1) # wait for the server to fully start before doing anything
with Client(host.address, game, "Player1") as client:
assert_equal(len(client.checked_locations), 2,
"Save was destroyed during exception in customserver")
print("Save file is not busted 🥳")
+ sleep(2) # work around issue #5571
finally:
print("Stopping autohost")
diff --git a/test/hosting/webhost.py b/test/hosting/webhost.py
index 8888c3fb87fc..a8e70a50c20c 100644
--- a/test/hosting/webhost.py
+++ b/test/hosting/webhost.py
@@ -1,6 +1,10 @@
+import io
+import json
import re
+import time
+import zipfile
from pathlib import Path
-from typing import TYPE_CHECKING, Optional, cast
+from typing import TYPE_CHECKING, Iterable, Optional, cast
from WebHostLib import to_python
@@ -10,6 +14,7 @@
__all__ = [
"get_app",
+ "generate_remote",
"upload_multidata",
"create_room",
"start_room",
@@ -17,6 +22,7 @@
"set_room_timeout",
"get_multidata_for_room",
"set_multidata_for_room",
+ "stop_autogen",
"stop_autohost",
]
@@ -33,10 +39,43 @@ def get_app(tempdir: str) -> "Flask":
"TESTING": True,
"HOST_ADDRESS": "localhost",
"HOSTERS": 1,
+ "GENERATORS": 1,
+ "JOB_THRESHOLD": 1,
})
return get_app()
+def generate_remote(app_client: "FlaskClient", games: Iterable[str]) -> str:
+ data = io.BytesIO()
+ with zipfile.ZipFile(data, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
+ for n, game in enumerate(games, 1):
+ name = f"{n}.yaml"
+ zip_file.writestr(name, json.dumps({
+ "name": f"Player{n}",
+ "game": game,
+ game: {},
+ "description": f"generate_remote slot {n} ('Player{n}'): {game}",
+ }))
+ data.seek(0)
+ response = app_client.post("/generate", content_type="multipart/form-data", data={
+ "file": (data, "yamls.zip"),
+ })
+ assert response.status_code < 400, f"Starting gen failed: status {response.status_code}"
+ assert "Location" in response.headers, f"Starting gen failed: no redirect"
+ location = response.headers["Location"]
+ assert isinstance(location, str)
+ assert location.startswith("/wait/"), f"Starting WebHost gen failed: unexpected redirect to {location}"
+ for attempt in range(10):
+ response = app_client.get(location)
+ if "Location" in response.headers:
+ location = response.headers["Location"]
+ assert isinstance(location, str)
+ assert location.startswith("/seed/"), f"Finishing WebHost gen failed: unexpected redirect to {location}"
+ return location[6:]
+ time.sleep(1)
+ raise TimeoutError("WebHost gen did not finish")
+
+
def upload_multidata(app_client: "FlaskClient", multidata: Path) -> str:
response = app_client.post("/uploads", data={
"file": multidata.open("rb"),
@@ -188,7 +227,7 @@ def set_multidata_for_room(webhost_client: "FlaskClient", room_id: str, data: by
room.seed.multidata = data
-def stop_autohost(graceful: bool = True) -> None:
+def _stop_webhost_mp(name_filter: str, graceful: bool = True) -> None:
import os
import signal
@@ -198,13 +237,30 @@ def stop_autohost(graceful: bool = True) -> None:
stop()
proc: multiprocessing.process.BaseProcess
- for proc in filter(lambda child: child.name.startswith("MultiHoster"), multiprocessing.active_children()):
+ for proc in filter(lambda child: child.name.startswith(name_filter), multiprocessing.active_children()):
+ # FIXME: graceful currently does not work on Windows because the signals are not properly emulated
+ # and ungraceful may not save the game
+ if proc.pid == os.getpid():
+ continue
if graceful and proc.pid:
os.kill(proc.pid, getattr(signal, "CTRL_C_EVENT", signal.SIGINT))
else:
proc.kill()
try:
- proc.join(30)
+ try:
+ proc.join(30)
+ except TimeoutError:
+ raise
+ except KeyboardInterrupt:
+ # on Windows, the MP exception may be forwarded to the host, so ignore once and retry
+ proc.join(30)
except TimeoutError:
proc.kill()
proc.join()
+
+def stop_autogen(graceful: bool = True) -> None:
+ # FIXME: this name filter is jank, but there seems to be no way to add a custom prefix for a Pool
+ _stop_webhost_mp("SpawnPoolWorker-", graceful)
+
+def stop_autohost(graceful: bool = True) -> None:
+ _stop_webhost_mp("MultiHoster", graceful)
diff --git a/test/hosting/world.py b/test/hosting/world.py
index cd53453c10c2..9a523f6845ae 100644
--- a/test/hosting/world.py
+++ b/test/hosting/world.py
@@ -11,7 +11,7 @@
def copy(src: str, dst: str) -> None:
from Utils import get_file_safe_name
- from worlds import AutoWorldRegister
+ from worlds.AutoWorld import AutoWorldRegister
assert dst not in _new_worlds, "World already created"
if '"' in dst or "\\" in dst: # easier to reject than to escape
@@ -20,7 +20,7 @@ def copy(src: str, dst: str) -> None:
src_cls = AutoWorldRegister.world_types[src]
src_folder = Path(src_cls.__file__).parent
worlds_folder = src_folder.parent
- if (not src_cls.__file__.endswith("__init__.py") or not src_folder.is_dir()
+ if (not src_cls.__file__.endswith(("__init__.py", "world.py")) or not src_folder.is_dir()
or not (worlds_folder / "generic").is_dir()):
raise ValueError(f"Unsupported layout for copy_world from {src}")
dst_folder = worlds_folder / dst_folder_name
@@ -28,11 +28,14 @@ def copy(src: str, dst: str) -> None:
raise ValueError(f"Destination {dst_folder} already exists")
shutil.copytree(src_folder, dst_folder)
_new_worlds[dst] = str(dst_folder)
- with open(dst_folder / "__init__.py", "r", encoding="utf-8-sig") as f:
- contents = f.read()
- contents = re.sub(r'game\s*(:\s*[a-zA-Z\[\]]+)?\s*=\s*[\'"]' + re.escape(src) + r'[\'"]', f'game = "{dst}"', contents)
- with open(dst_folder / "__init__.py", "w", encoding="utf-8") as f:
- f.write(contents)
+
+ for potential_world_class_file in ("__init__.py", "world.py"):
+ with open(dst_folder / potential_world_class_file, "r", encoding="utf-8-sig") as f:
+ contents = f.read()
+ r_src = re.escape(src)
+ contents = re.sub(r'game\s*(:\s*[a-zA-Z\[\]]+)?\s*=\s*[\'"]' + r_src + r'[\'"]', f'game = "{dst}"', contents)
+ with open(dst_folder / "__init__.py", "w", encoding="utf-8") as f:
+ f.write(contents)
def delete(name: str) -> None:
diff --git a/test/programs/data/one_player/test.yaml b/test/programs/data/one_player/test.yaml
index 47d79cf636c1..a93493702750 100644
--- a/test/programs/data/one_player/test.yaml
+++ b/test/programs/data/one_player/test.yaml
@@ -2,8 +2,8 @@ description: Almost blank test yaml
name: Player{NUMBER}
game:
- Timespinner: 1 # what else
+ APQuest: 1 # what else
requires:
version: 0.2.6
-Timespinner: {}
+APQuest: {}
diff --git a/test/utils/test_daemon_thread_pool.py b/test/utils/test_daemon_thread_pool.py
new file mode 100644
index 000000000000..b8702492311f
--- /dev/null
+++ b/test/utils/test_daemon_thread_pool.py
@@ -0,0 +1,14 @@
+import unittest
+
+from Utils import DaemonThreadPoolExecutor
+
+
+class DaemonThreadPoolExecutorTest(unittest.TestCase):
+ def test_is_daemon(self) -> None:
+ def run() -> None:
+ pass
+
+ with DaemonThreadPoolExecutor(1) as executor:
+ executor.submit(run)
+
+ self.assertTrue(next(iter(executor._threads)).daemon)
diff --git a/test/webhost/test_markdown.py b/test/webhost/test_markdown.py
new file mode 100644
index 000000000000..ae27a45e6349
--- /dev/null
+++ b/test/webhost/test_markdown.py
@@ -0,0 +1,78 @@
+import os
+import unittest
+from tempfile import NamedTemporaryFile
+
+from mistune import HTMLRenderer, Markdown
+
+from WebHostLib.markdown import ImgUrlRewriteInlineParser, render_markdown
+
+
+class ImgUrlRewriteTest(unittest.TestCase):
+ markdown: Markdown
+ base_url = "/static/generated/docs/some_game"
+
+ def setUp(self) -> None:
+ self.markdown = Markdown(
+ renderer=HTMLRenderer(escape=False),
+ inline=ImgUrlRewriteInlineParser(self.base_url),
+ )
+
+ def test_relative_img_rewrite(self) -> None:
+ html = self.markdown("")
+ self.assertIn(f'src="{self.base_url}/image.png"', html)
+
+ def test_absolute_img_no_rewrite(self) -> None:
+ html = self.markdown("")
+ self.assertIn(f'src="/image.png"', html)
+ self.assertNotIn(self.base_url, html)
+
+ def test_remote_img_no_rewrite(self) -> None:
+ html = self.markdown("")
+ self.assertIn(f'src="https://example.com/image.png"', html)
+ self.assertNotIn(self.base_url, html)
+
+ def test_relative_link_no_rewrite(self) -> None:
+ # The parser is only supposed to update images, not links.
+ html = self.markdown("[Link](image.png)")
+ self.assertIn(f'href="image.png"', html)
+ self.assertNotIn(self.base_url, html)
+
+ def test_absolute_link_no_rewrite(self) -> None:
+ html = self.markdown("[Link](/image.png)")
+ self.assertIn(f'href="/image.png"', html)
+ self.assertNotIn(self.base_url, html)
+
+ def test_auto_link_no_rewrite(self) -> None:
+ html = self.markdown("")
+ self.assertIn(f'href="https://example.com/image.png"', html)
+ self.assertNotIn(self.base_url, html)
+
+ def test_relative_img_to_other_game(self) -> None:
+ html = self.markdown("")
+ self.assertIn(f'src="{self.base_url}/../Archipelago/image.png"', html)
+
+
+class RenderMarkdownTest(unittest.TestCase):
+ """Tests that render_markdown does the right thing."""
+ base_url = "/static/generated/docs/some_game"
+
+ def test_relative_img_rewrite(self) -> None:
+ f = NamedTemporaryFile(delete=False)
+ try:
+ f.write("".encode("utf-8"))
+ f.close()
+ html = render_markdown(f.name, self.base_url)
+ self.assertIn(f'src="{self.base_url}/image.png"', html)
+ finally:
+ os.unlink(f.name)
+
+ def test_no_img_rewrite(self) -> None:
+ f = NamedTemporaryFile(delete=False)
+ try:
+ f.write("".encode("utf-8"))
+ f.close()
+ html = render_markdown(f.name)
+ self.assertIn(f'src="image.png"', html)
+ self.assertNotIn(self.base_url, html)
+ finally:
+ os.unlink(f.name)
diff --git a/test/webhost/test_option_presets.py b/test/webhost/test_option_presets.py
index efacddb22e6a..7f05de5d114b 100644
--- a/test/webhost/test_option_presets.py
+++ b/test/webhost/test_option_presets.py
@@ -2,7 +2,7 @@
from BaseClasses import PlandoOptions
from worlds import AutoWorldRegister
-from Options import OptionCounter, NamedRange, NumericOption, OptionList, OptionSet
+from Options import OptionCounter, NamedRange, NumericOption, OptionList, OptionSet, Visibility
class TestOptionPresets(unittest.TestCase):
@@ -19,6 +19,9 @@ def test_option_presets_have_valid_options(self):
# pass in all plando options in case a preset wants to require certain plando options
# for some reason
option.verify(world_type, "Test Player", PlandoOptions(sum(PlandoOptions)))
+ if not (Visibility.complex_ui in option.visibility or Visibility.simple_ui in option.visibility):
+ self.fail(f"'{option_name}' in preset '{preset_name}' for game '{game_name}' is not "
+ f"visible in any supported UI.")
supported_types = [NumericOption, OptionSet, OptionList, OptionCounter]
if not any([issubclass(option.__class__, t) for t in supported_types]):
self.fail(f"'{option_name}' in preset '{preset_name}' for game '{game_name}' "
diff --git a/worlds/AutoSNIClient.py b/worlds/AutoSNIClient.py
index 98243ce3fc97..d2558bb98dc6 100644
--- a/worlds/AutoSNIClient.py
+++ b/worlds/AutoSNIClient.py
@@ -1,14 +1,26 @@
from __future__ import annotations
import abc
+from bisect import bisect_right
+from dataclasses import dataclass
+import enum
import logging
-from typing import TYPE_CHECKING, ClassVar, Dict, Iterable, Tuple, Any, Optional, Union, TypeGuard
+from typing import (TYPE_CHECKING, Any, ClassVar, Dict, Generic, Iterable,
+ Optional, Sequence, Tuple, TypeGuard, TypeVar, Union)
+
from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components
if TYPE_CHECKING:
from SNIClient import SNIContext
+SNES_READ_CHUNK_SIZE = 2048
+"""
+note: SNI v0.0.101 currently has a bug where reads from
+RetroArch >2048 bytes will only return the last ~2048 bytes read.
+https://github.com/alttpo/sni/issues/51
+"""
+
component = Component('SNI Client', 'SNIClient', component_type=Type.CLIENT, file_identifier=SuffixIdentifier(".apsoe"),
description="A client for connecting to SNES consoles via Super Nintendo Interface.")
components.append(component)
@@ -91,3 +103,119 @@ async def deathlink_kill_player(self, ctx: SNIContext) -> None:
def on_package(self, ctx: SNIContext, cmd: str, args: Dict[str, Any]) -> None:
""" override this with code to handle packages from the server """
pass
+
+
+@dataclass(frozen=True, slots=True, order=True)
+class Read:
+ """ snes memory read - address and size in bytes """
+ address: int
+ size: int
+
+
+@dataclass(frozen=True, slots=True)
+class _MemRead:
+ location: Read
+ data: bytes
+
+
+_T_Enum = TypeVar("_T_Enum", bound=enum.Enum)
+
+
+class SnesData(Generic[_T_Enum]):
+ _ranges: Sequence[_MemRead]
+ """ sorted by address """
+
+ def __init__(self, ranges: Sequence[tuple[Read, bytes]]) -> None:
+ self._ranges = [_MemRead(r, d) for r, d in ranges]
+
+ def get(self, read: _T_Enum) -> bytes:
+ assert isinstance(read.value, Read), read.value
+ address = read.value.address
+ index = bisect_right(self._ranges, address, key=lambda r: r.location.address) - 1
+ assert index >= 0, (self._ranges, read.value)
+ mem_read = self._ranges[index]
+ sub_index = address - mem_read.location.address
+ return mem_read.data[sub_index:sub_index + read.value.size]
+
+
+class SnesReader(Generic[_T_Enum]):
+ """
+ how to use:
+ ```
+ from enum import Enum
+ from worlds.AutoSNIClient import Read, SNIClient, SnesReader
+
+ class MyGameMemory(Enum):
+ game_mode = Read(WRAM_START + 0x0998, 1)
+ send_queue = Read(SEND_QUEUE_START, 8 * 127)
+ ...
+
+ snes_reader = SnesReader(MyGameMemory)
+
+ snes_data = await snes_reader.read(ctx)
+ if snes_data is None:
+ snes_logger.info("error reading from snes")
+ return
+
+ game_mode = snes_data.get(MyGameMemory.game_mode)
+ ```
+ """
+ _ranges: Sequence[Read]
+ """ sorted by address """
+
+ def __init__(self, reads: type[_T_Enum]) -> None:
+ self._ranges = self._make_ranges(reads)
+
+ @staticmethod
+ def _make_ranges(reads: type[enum.Enum]) -> Sequence[Read]:
+
+ unprocessed_reads: list[Read] = []
+ for e in reads:
+ assert isinstance(e.value, Read), (reads.__name__, e, e.value)
+ unprocessed_reads.append(e.value)
+ unprocessed_reads.sort()
+
+ ranges: list[Read] = []
+ for read in unprocessed_reads:
+ # v end of the previous range
+ if len(ranges) == 0 or read.address - (ranges[-1].address + ranges[-1].size) > 255:
+ ranges.append(read)
+ else: # combine with previous range
+ chunk_address = ranges[-1].address
+ assert read.address >= chunk_address, "sort() didn't work? or something"
+ original_chunk_size = ranges[-1].size
+ new_size = max((read.address + read.size) - chunk_address,
+ original_chunk_size)
+ ranges[-1] = Read(chunk_address, new_size)
+ logging.debug(f"{len(ranges)=} {max(r.size for r in ranges)=}")
+ return ranges
+
+ async def read(self, ctx: "SNIContext") -> SnesData[_T_Enum] | None:
+ """
+ returns `None` if reading fails,
+ otherwise returns the data for the registered `Enum`
+ """
+ from SNIClient import snes_read
+
+ reads: list[tuple[Read, bytes]] = []
+ for r in self._ranges:
+ if r.size < SNES_READ_CHUNK_SIZE: # most common
+ response = await snes_read(ctx, r.address, r.size)
+ if response is None:
+ return None
+ reads.append((r, response))
+ else: # big read
+ # Problems were reported with big reads,
+ # so we chunk it into smaller pieces.
+ read_so_far = 0
+ collection: list[bytes] = []
+ while read_so_far < r.size:
+ remaining_size = r.size - read_so_far
+ chunk_size = min(SNES_READ_CHUNK_SIZE, remaining_size)
+ response = await snes_read(ctx, r.address + read_so_far, chunk_size)
+ if response is None:
+ return None
+ collection.append(response)
+ read_so_far += chunk_size
+ reads.append((r, b"".join(collection)))
+ return SnesData(reads)
diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py
index 1805b11a0924..e1435b3a680f 100644
--- a/worlds/AutoWorld.py
+++ b/worlds/AutoWorld.py
@@ -47,27 +47,31 @@ def settings(cls) -> Any: # actual type is defined in World
def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> AutoWorldRegister:
if "web" in dct:
assert isinstance(dct["web"], WebWorld), "WebWorld has to be instantiated."
- # filter out any events
- dct["item_name_to_id"] = {name: id for name, id in dct["item_name_to_id"].items() if id}
- dct["location_name_to_id"] = {name: id for name, id in dct["location_name_to_id"].items() if id}
- # build reverse lookups
- dct["item_id_to_name"] = {code: name for name, code in dct["item_name_to_id"].items()}
- dct["location_id_to_name"] = {code: name for name, code in dct["location_name_to_id"].items()}
-
- # build rest
- dct["item_names"] = frozenset(dct["item_name_to_id"])
- dct["item_name_groups"] = {group_name: frozenset(group_set) for group_name, group_set
- in dct.get("item_name_groups", {}).items()}
- dct["item_name_groups"]["Everything"] = dct["item_names"]
-
- dct["location_names"] = frozenset(dct["location_name_to_id"])
- dct["location_name_groups"] = {group_name: frozenset(group_set) for group_name, group_set
- in dct.get("location_name_groups", {}).items()}
- dct["location_name_groups"]["Everywhere"] = dct["location_names"]
- dct["all_item_and_group_names"] = frozenset(dct["item_names"] | set(dct.get("item_name_groups", {})))
-
- # move away from get_required_client_version function
+
if "game" in dct:
+ assert "item_name_to_id" in dct, f"{name}: item_name_to_id is required"
+ assert "location_name_to_id" in dct, f"{name}: location_name_to_id is required"
+
+ # filter out any events
+ dct["item_name_to_id"] = {name: id for name, id in dct["item_name_to_id"].items() if id}
+ dct["location_name_to_id"] = {name: id for name, id in dct["location_name_to_id"].items() if id}
+ # build reverse lookups
+ dct["item_id_to_name"] = {code: name for name, code in dct["item_name_to_id"].items()}
+ dct["location_id_to_name"] = {code: name for name, code in dct["location_name_to_id"].items()}
+
+ # build rest
+ dct["item_names"] = frozenset(dct["item_name_to_id"])
+ dct["item_name_groups"] = {group_name: frozenset(group_set) for group_name, group_set
+ in dct.get("item_name_groups", {}).items()}
+ dct["item_name_groups"]["Everything"] = dct["item_names"]
+
+ dct["location_names"] = frozenset(dct["location_name_to_id"])
+ dct["location_name_groups"] = {group_name: frozenset(group_set) for group_name, group_set
+ in dct.get("location_name_groups", {}).items()}
+ dct["location_name_groups"]["Everywhere"] = dct["location_names"]
+ dct["all_item_and_group_names"] = frozenset(dct["item_names"] | set(dct.get("item_name_groups", {})))
+
+ # move away from get_required_client_version function
assert "get_required_client_version" not in dct, f"{name}: required_client_version is an attribute now"
# set minimum required_client_version from bases
if "required_client_version" in dct and bases:
@@ -224,7 +228,7 @@ class WebWorld(metaclass=WebWorldRegister):
tutorials: List["Tutorial"]
"""docs folder will also be scanned for tutorial guides. Each Tutorial class is to be used for one guide."""
- theme = "grass"
+ theme: str = "grass"
"""Choose a theme for you /game/* pages.
Available: dirt, grass, grassFlowers, ice, jungle, ocean, partyTime, stone"""
diff --git a/worlds/Files.py b/worlds/Files.py
index a41424183f63..ddd1f4e1ce92 100644
--- a/worlds/Files.py
+++ b/worlds/Files.py
@@ -21,6 +21,10 @@
from Utils import Version
+class ImproperlyConfiguredAutoPatchError(Exception):
+ pass
+
+
class AutoPatchRegister(abc.ABCMeta):
patch_types: ClassVar[Dict[str, AutoPatchRegister]] = {}
file_endings: ClassVar[Dict[str, AutoPatchRegister]] = {}
@@ -30,8 +34,28 @@ def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> Aut
new_class = super().__new__(mcs, name, bases, dct)
if "game" in dct:
AutoPatchRegister.patch_types[dct["game"]] = new_class
- if not dct["patch_file_ending"]:
- raise Exception(f"Need an expected file ending for {name}")
+
+ if not callable(getattr(new_class, "patch", None)):
+ raise ImproperlyConfiguredAutoPatchError(
+ f"Container {new_class} uses metaclass AutoPatchRegister, but does not have a patch method defined."
+ )
+
+ patch_file_ending = dct.get("patch_file_ending")
+ if patch_file_ending == ".zip":
+ raise ImproperlyConfiguredAutoPatchError(
+ f'Auto patch container {new_class} uses file ending ".zip", which is not allowed.'
+ )
+ if patch_file_ending is None:
+ raise ImproperlyConfiguredAutoPatchError(
+ f"Need an expected file ending for auto patch container {new_class}"
+ )
+
+ existing_handler = AutoPatchRegister.file_endings.get(patch_file_ending)
+ if existing_handler:
+ raise ImproperlyConfiguredAutoPatchError(
+ f"Two auto patch containers are using the same file extension: {new_class}, {existing_handler}"
+ )
+
AutoPatchRegister.file_endings[dct["patch_file_ending"]] = new_class
return new_class
diff --git a/worlds/LauncherComponents.py b/worlds/LauncherComponents.py
index be58b048e5b5..a1bbacfcae78 100644
--- a/worlds/LauncherComponents.py
+++ b/worlds/LauncherComponents.py
@@ -5,7 +5,7 @@
from enum import Enum, auto
from typing import Optional, Callable, List, Iterable, Tuple
-from Utils import local_path, open_filename, is_frozen, is_kivy_running
+from Utils import local_path, open_filename, is_frozen, is_kivy_running, open_file, user_path
class Type(Enum):
@@ -204,6 +204,18 @@ def install_apworld(apworld_path: str = "") -> None:
Utils.messagebox("Install complete.", f"Installed APWorld from {source}.")
+def export_datapackage() -> None:
+ import json
+
+ from worlds import network_data_package
+
+ path = user_path("datapackage_export.json")
+ with open(path, "w") as f:
+ json.dump(network_data_package, f, indent=4)
+
+ open_file(path)
+
+
components: List[Component] = [
# Launcher
Component('Launcher', 'Launcher', component_type=Type.HIDDEN),
@@ -213,12 +225,12 @@ def install_apworld(apworld_path: str = "") -> None:
description="Host a generated multiworld on your computer."),
Component('Generate', 'Generate', cli=True,
description="Generate a multiworld with the YAMLs in the players folder."),
+ Component("Options Creator", "OptionsCreator", "ArchipelagoOptionsCreator", component_type=Type.TOOL,
+ description="Visual creator for Archipelago option files."),
Component("Install APWorld", func=install_apworld, file_identifier=SuffixIdentifier(".apworld"),
description="Install an APWorld to play games not included with Archipelago by default."),
Component('Text Client', 'CommonClient', 'ArchipelagoTextClient', func=launch_textclient,
description="Connect to a multiworld using the text client."),
- Component('Links Awakening DX Client', 'LinksAwakeningClient',
- file_identifier=SuffixIdentifier('.apladx')),
Component('LttP Adjuster', 'LttPAdjuster'),
# Ocarina of Time
Component('OoT Client', 'OoTClient',
@@ -232,8 +244,11 @@ def install_apworld(apworld_path: str = "") -> None:
Component('Zillion Client', 'ZillionClient',
file_identifier=SuffixIdentifier('.apzl')),
- #MegaMan Battle Network 3
- Component('MMBN3 Client', 'MMBN3Client', file_identifier=SuffixIdentifier('.apbn3'))
+ # MegaMan Battle Network 3
+ Component('MMBN3 Client', 'MMBN3Client', file_identifier=SuffixIdentifier('.apbn3')),
+
+ Component("Export Datapackage", func=export_datapackage, component_type=Type.TOOL,
+ description="Write item/location data for installed worlds to a file and open it."),
]
@@ -273,14 +288,15 @@ def _build_apworlds(*launch_args: str):
file_name = os.path.split(os.path.dirname(worldtype.__file__))[1]
world_directory = os.path.join("worlds", file_name)
if os.path.isfile(os.path.join(world_directory, "archipelago.json")):
- manifest = json.load(open(os.path.join(world_directory, "archipelago.json")))
+ with open(os.path.join(world_directory, "archipelago.json"), mode="r", encoding="utf-8") as manifest_file:
+ manifest = json.load(manifest_file)
assert "game" in manifest, (
- f"World directory {world_directory} has an archipelago.json manifest file, but it"
+ f"World directory {world_directory} has an archipelago.json manifest file, but it "
"does not define a \"game\"."
)
assert manifest["game"] == worldtype.game, (
- f"World directory {world_directory} has an archipelago.json manifest file, but value of the"
+ f"World directory {world_directory} has an archipelago.json manifest file, but value of the "
f"\"game\" field ({manifest['game']} does not equal the World class's game ({worldtype.game})."
)
else:
@@ -303,5 +319,5 @@ def _build_apworlds(*launch_args: str):
open_folder(apworlds_folder)
- components.append(Component('Build APWorlds', func=_build_apworlds, cli=True,
+ components.append(Component("Build APWorlds", func=_build_apworlds, cli=True,
description="Build APWorlds from loose-file world folders."))
diff --git a/worlds/__init__.py b/worlds/__init__.py
index b7ceb46a1e6f..72ac818198ca 100644
--- a/worlds/__init__.py
+++ b/worlds/__init__.py
@@ -122,7 +122,8 @@ def load(self) -> bool:
for dirpath, dirnames, filenames in os.walk(world_source.resolved_path):
for file in filenames:
if file.endswith("archipelago.json"):
- manifest = json.load(open(os.path.join(dirpath, file), "r"))
+ with open(os.path.join(dirpath, file), mode="r", encoding="utf-8") as manifest_file:
+ manifest = json.load(manifest_file)
break
if manifest:
break
diff --git a/worlds/_sc2common/bot/bot_ai_internal.py b/worlds/_sc2common/bot/bot_ai_internal.py
index 583c491dc2f6..1cdd3bda0682 100644
--- a/worlds/_sc2common/bot/bot_ai_internal.py
+++ b/worlds/_sc2common/bot/bot_ai_internal.py
@@ -9,7 +9,7 @@
from typing import TYPE_CHECKING, Any
from typing import Dict, Generator, Iterable, List, Set, Tuple, Union, final
-from s2clientprotocol import sc2api_pb2 as sc_pb
+from .proto import sc2api_pb2 as sc_pb
from .constants import (
IS_PLACEHOLDER,
diff --git a/worlds/_sc2common/bot/client.py b/worlds/_sc2common/bot/client.py
index a902c99d5594..bb4bc4c86659 100644
--- a/worlds/_sc2common/bot/client.py
+++ b/worlds/_sc2common/bot/client.py
@@ -4,11 +4,11 @@
from worlds._sc2common.bot import logger
-from s2clientprotocol import debug_pb2 as debug_pb
-from s2clientprotocol import query_pb2 as query_pb
-from s2clientprotocol import raw_pb2 as raw_pb
-from s2clientprotocol import sc2api_pb2 as sc_pb
-from s2clientprotocol import spatial_pb2 as spatial_pb
+from .proto import debug_pb2 as debug_pb
+from .proto import query_pb2 as query_pb
+from .proto import raw_pb2 as raw_pb
+from .proto import sc2api_pb2 as sc_pb
+from .proto import spatial_pb2 as spatial_pb
from .data import ActionResult, ChatChannel, Race, Result, Status
from .game_data import AbilityData, GameData
diff --git a/worlds/_sc2common/bot/controller.py b/worlds/_sc2common/bot/controller.py
index abb26ef8a9e5..8ddb6131e2d2 100644
--- a/worlds/_sc2common/bot/controller.py
+++ b/worlds/_sc2common/bot/controller.py
@@ -2,7 +2,7 @@
from pathlib import Path
from worlds._sc2common.bot import logger
-from s2clientprotocol import sc2api_pb2 as sc_pb
+from .proto import sc2api_pb2 as sc_pb
from .player import Computer
from .protocol import Protocol
diff --git a/worlds/_sc2common/bot/data.py b/worlds/_sc2common/bot/data.py
index 4c9b3b94a046..36681a5b87f8 100644
--- a/worlds/_sc2common/bot/data.py
+++ b/worlds/_sc2common/bot/data.py
@@ -7,11 +7,11 @@
"""
import enum
-from s2clientprotocol import common_pb2 as common_pb
-from s2clientprotocol import data_pb2 as data_pb
-from s2clientprotocol import error_pb2 as error_pb
-from s2clientprotocol import raw_pb2 as raw_pb
-from s2clientprotocol import sc2api_pb2 as sc_pb
+from .proto import common_pb2 as common_pb
+from .proto import data_pb2 as data_pb
+from .proto import error_pb2 as error_pb
+from .proto import raw_pb2 as raw_pb
+from .proto import sc2api_pb2 as sc_pb
CreateGameError = enum.Enum("CreateGameError", sc_pb.ResponseCreateGame.Error.items())
diff --git a/worlds/_sc2common/bot/main.py b/worlds/_sc2common/bot/main.py
index f18c56836166..5d40269ed30b 100644
--- a/worlds/_sc2common/bot/main.py
+++ b/worlds/_sc2common/bot/main.py
@@ -15,7 +15,7 @@
import portpicker
from aiohttp import ClientSession, ClientWebSocketResponse
from worlds._sc2common.bot import logger
-from s2clientprotocol import sc2api_pb2 as sc_pb
+from .proto import sc2api_pb2 as sc_pb
from .bot_ai import BotAI
from .client import Client
diff --git a/worlds/_sc2common/bot/position.py b/worlds/_sc2common/bot/position.py
index aca9a5105cbe..bde21b15c3f3 100644
--- a/worlds/_sc2common/bot/position.py
+++ b/worlds/_sc2common/bot/position.py
@@ -5,7 +5,7 @@
import random
from typing import TYPE_CHECKING, Iterable, List, Set, Tuple, Union
-from s2clientprotocol import common_pb2 as common_pb
+from .proto import common_pb2 as common_pb
if TYPE_CHECKING:
from .unit import Unit
diff --git a/worlds/_sc2common/bot/proto/__init__.py b/worlds/_sc2common/bot/proto/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/_sc2common/bot/proto/common_pb2.py b/worlds/_sc2common/bot/proto/common_pb2.py
new file mode 100644
index 000000000000..f84ad4d9ee85
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/common_pb2.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/common.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/common.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ds2clientprotocol/common.proto\x12\x0eSC2APIProtocol\">\n\x10\x41vailableAbility\x12\x12\n\nability_id\x18\x01 \x01(\x05\x12\x16\n\x0erequires_point\x18\x02 \x01(\x08\"X\n\tImageData\x12\x16\n\x0e\x62its_per_pixel\x18\x01 \x01(\x05\x12%\n\x04size\x18\x02 \x01(\x0b\x32\x17.SC2APIProtocol.Size2DI\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\"\x1e\n\x06PointI\x12\t\n\x01x\x18\x01 \x01(\x05\x12\t\n\x01y\x18\x02 \x01(\x05\"T\n\nRectangleI\x12\"\n\x02p0\x18\x01 \x01(\x0b\x32\x16.SC2APIProtocol.PointI\x12\"\n\x02p1\x18\x02 \x01(\x0b\x32\x16.SC2APIProtocol.PointI\"\x1f\n\x07Point2D\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\"(\n\x05Point\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\"\x1f\n\x07Size2DI\x12\t\n\x01x\x18\x01 \x01(\x05\x12\t\n\x01y\x18\x02 \x01(\x05*A\n\x04Race\x12\n\n\x06NoRace\x10\x00\x12\n\n\x06Terran\x10\x01\x12\x08\n\x04Zerg\x10\x02\x12\x0b\n\x07Protoss\x10\x03\x12\n\n\x06Random\x10\x04')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.common_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_RACE']._serialized_start=429
+ _globals['_RACE']._serialized_end=494
+ _globals['_AVAILABLEABILITY']._serialized_start=49
+ _globals['_AVAILABLEABILITY']._serialized_end=111
+ _globals['_IMAGEDATA']._serialized_start=113
+ _globals['_IMAGEDATA']._serialized_end=201
+ _globals['_POINTI']._serialized_start=203
+ _globals['_POINTI']._serialized_end=233
+ _globals['_RECTANGLEI']._serialized_start=235
+ _globals['_RECTANGLEI']._serialized_end=319
+ _globals['_POINT2D']._serialized_start=321
+ _globals['_POINT2D']._serialized_end=352
+ _globals['_POINT']._serialized_start=354
+ _globals['_POINT']._serialized_end=394
+ _globals['_SIZE2DI']._serialized_start=396
+ _globals['_SIZE2DI']._serialized_end=427
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/proto/data_pb2.py b/worlds/_sc2common/bot/proto/data_pb2.py
new file mode 100644
index 000000000000..6785ea3ed7dd
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/data_pb2.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/data.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/data.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import common_pb2 as s2clientprotocol_dot_common__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bs2clientprotocol/data.proto\x12\x0eSC2APIProtocol\x1a\x1ds2clientprotocol/common.proto\"\xc4\x03\n\x0b\x41\x62ilityData\x12\x12\n\nability_id\x18\x01 \x01(\r\x12\x11\n\tlink_name\x18\x02 \x01(\t\x12\x12\n\nlink_index\x18\x03 \x01(\r\x12\x13\n\x0b\x62utton_name\x18\x04 \x01(\t\x12\x15\n\rfriendly_name\x18\x05 \x01(\t\x12\x0e\n\x06hotkey\x18\x06 \x01(\t\x12\x1c\n\x14remaps_to_ability_id\x18\x07 \x01(\r\x12\x11\n\tavailable\x18\x08 \x01(\x08\x12\x32\n\x06target\x18\t \x01(\x0e\x32\".SC2APIProtocol.AbilityData.Target\x12\x15\n\rallow_minimap\x18\n \x01(\x08\x12\x16\n\x0e\x61llow_autocast\x18\x0b \x01(\x08\x12\x13\n\x0bis_building\x18\x0c \x01(\x08\x12\x18\n\x10\x66ootprint_radius\x18\r \x01(\x02\x12\x1c\n\x14is_instant_placement\x18\x0e \x01(\x08\x12\x12\n\ncast_range\x18\x0f \x01(\x02\"I\n\x06Target\x12\x08\n\x04None\x10\x01\x12\t\n\x05Point\x10\x02\x12\x08\n\x04Unit\x10\x03\x12\x0f\n\x0bPointOrUnit\x10\x04\x12\x0f\n\x0bPointOrNone\x10\x05\"J\n\x0b\x44\x61mageBonus\x12,\n\tattribute\x18\x01 \x01(\x0e\x32\x19.SC2APIProtocol.Attribute\x12\r\n\x05\x62onus\x18\x02 \x01(\x02\"\xd7\x01\n\x06Weapon\x12/\n\x04type\x18\x01 \x01(\x0e\x32!.SC2APIProtocol.Weapon.TargetType\x12\x0e\n\x06\x64\x61mage\x18\x02 \x01(\x02\x12\x31\n\x0c\x64\x61mage_bonus\x18\x03 \x03(\x0b\x32\x1b.SC2APIProtocol.DamageBonus\x12\x0f\n\x07\x61ttacks\x18\x04 \x01(\r\x12\r\n\x05range\x18\x05 \x01(\x02\x12\r\n\x05speed\x18\x06 \x01(\x02\"*\n\nTargetType\x12\n\n\x06Ground\x10\x01\x12\x07\n\x03\x41ir\x10\x02\x12\x07\n\x03\x41ny\x10\x03\"\x95\x04\n\x0cUnitTypeData\x12\x0f\n\x07unit_id\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x11\n\tavailable\x18\x03 \x01(\x08\x12\x12\n\ncargo_size\x18\x04 \x01(\r\x12\x14\n\x0cmineral_cost\x18\x0c \x01(\r\x12\x14\n\x0cvespene_cost\x18\r \x01(\r\x12\x15\n\rfood_required\x18\x0e \x01(\x02\x12\x15\n\rfood_provided\x18\x12 \x01(\x02\x12\x12\n\nability_id\x18\x0f \x01(\r\x12\"\n\x04race\x18\x10 \x01(\x0e\x32\x14.SC2APIProtocol.Race\x12\x12\n\nbuild_time\x18\x11 \x01(\x02\x12\x13\n\x0bhas_vespene\x18\x13 \x01(\x08\x12\x14\n\x0chas_minerals\x18\x14 \x01(\x08\x12\x13\n\x0bsight_range\x18\x19 \x01(\x02\x12\x12\n\ntech_alias\x18\x15 \x03(\r\x12\x12\n\nunit_alias\x18\x16 \x01(\r\x12\x18\n\x10tech_requirement\x18\x17 \x01(\r\x12\x18\n\x10require_attached\x18\x18 \x01(\x08\x12-\n\nattributes\x18\x08 \x03(\x0e\x32\x19.SC2APIProtocol.Attribute\x12\x16\n\x0emovement_speed\x18\t \x01(\x02\x12\r\n\x05\x61rmor\x18\n \x01(\x02\x12\'\n\x07weapons\x18\x0b \x03(\x0b\x32\x16.SC2APIProtocol.Weapon\"\x86\x01\n\x0bUpgradeData\x12\x12\n\nupgrade_id\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x14\n\x0cmineral_cost\x18\x03 \x01(\r\x12\x14\n\x0cvespene_cost\x18\x04 \x01(\r\x12\x15\n\rresearch_time\x18\x05 \x01(\x02\x12\x12\n\nability_id\x18\x06 \x01(\r\")\n\x08\x42uffData\x12\x0f\n\x07\x62uff_id\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\"T\n\nEffectData\x12\x11\n\teffect_id\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x15\n\rfriendly_name\x18\x03 \x01(\t\x12\x0e\n\x06radius\x18\x04 \x01(\x02*\x9e\x01\n\tAttribute\x12\t\n\x05Light\x10\x01\x12\x0b\n\x07\x41rmored\x10\x02\x12\x0e\n\nBiological\x10\x03\x12\x0e\n\nMechanical\x10\x04\x12\x0b\n\x07Robotic\x10\x05\x12\x0b\n\x07Psionic\x10\x06\x12\x0b\n\x07Massive\x10\x07\x12\r\n\tStructure\x10\x08\x12\t\n\x05Hover\x10\t\x12\n\n\x06Heroic\x10\n\x12\x0c\n\x08Summoned\x10\x0b')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.data_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_ATTRIBUTE']._serialized_start=1630
+ _globals['_ATTRIBUTE']._serialized_end=1788
+ _globals['_ABILITYDATA']._serialized_start=79
+ _globals['_ABILITYDATA']._serialized_end=531
+ _globals['_ABILITYDATA_TARGET']._serialized_start=458
+ _globals['_ABILITYDATA_TARGET']._serialized_end=531
+ _globals['_DAMAGEBONUS']._serialized_start=533
+ _globals['_DAMAGEBONUS']._serialized_end=607
+ _globals['_WEAPON']._serialized_start=610
+ _globals['_WEAPON']._serialized_end=825
+ _globals['_WEAPON_TARGETTYPE']._serialized_start=783
+ _globals['_WEAPON_TARGETTYPE']._serialized_end=825
+ _globals['_UNITTYPEDATA']._serialized_start=828
+ _globals['_UNITTYPEDATA']._serialized_end=1361
+ _globals['_UPGRADEDATA']._serialized_start=1364
+ _globals['_UPGRADEDATA']._serialized_end=1498
+ _globals['_BUFFDATA']._serialized_start=1500
+ _globals['_BUFFDATA']._serialized_end=1541
+ _globals['_EFFECTDATA']._serialized_start=1543
+ _globals['_EFFECTDATA']._serialized_end=1627
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/proto/debug_pb2.py b/worlds/_sc2common/bot/proto/debug_pb2.py
new file mode 100644
index 000000000000..6715c8b0f23c
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/debug_pb2.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/debug.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/debug.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import common_pb2 as s2clientprotocol_dot_common__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cs2clientprotocol/debug.proto\x12\x0eSC2APIProtocol\x1a\x1ds2clientprotocol/common.proto\"\xbb\x03\n\x0c\x44\x65\x62ugCommand\x12)\n\x04\x64raw\x18\x01 \x01(\x0b\x32\x19.SC2APIProtocol.DebugDrawH\x00\x12\x34\n\ngame_state\x18\x02 \x01(\x0e\x32\x1e.SC2APIProtocol.DebugGameStateH\x00\x12\x36\n\x0b\x63reate_unit\x18\x03 \x01(\x0b\x32\x1f.SC2APIProtocol.DebugCreateUnitH\x00\x12\x32\n\tkill_unit\x18\x04 \x01(\x0b\x32\x1d.SC2APIProtocol.DebugKillUnitH\x00\x12\x38\n\x0ctest_process\x18\x05 \x01(\x0b\x32 .SC2APIProtocol.DebugTestProcessH\x00\x12.\n\x05score\x18\x06 \x01(\x0b\x32\x1d.SC2APIProtocol.DebugSetScoreH\x00\x12\x30\n\x08\x65nd_game\x18\x07 \x01(\x0b\x32\x1c.SC2APIProtocol.DebugEndGameH\x00\x12\x37\n\nunit_value\x18\x08 \x01(\x0b\x32!.SC2APIProtocol.DebugSetUnitValueH\x00\x42\t\n\x07\x63ommand\"\xb5\x01\n\tDebugDraw\x12\'\n\x04text\x18\x01 \x03(\x0b\x32\x19.SC2APIProtocol.DebugText\x12(\n\x05lines\x18\x02 \x03(\x0b\x32\x19.SC2APIProtocol.DebugLine\x12\'\n\x05\x62oxes\x18\x03 \x03(\x0b\x32\x18.SC2APIProtocol.DebugBox\x12,\n\x07spheres\x18\x04 \x03(\x0b\x32\x1b.SC2APIProtocol.DebugSphere\"L\n\x04Line\x12!\n\x02p0\x18\x01 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12!\n\x02p1\x18\x02 \x01(\x0b\x32\x15.SC2APIProtocol.Point\"(\n\x05\x43olor\x12\t\n\x01r\x18\x01 \x01(\r\x12\t\n\x01g\x18\x02 \x01(\r\x12\t\n\x01\x62\x18\x03 \x01(\r\"\xa3\x01\n\tDebugText\x12$\n\x05\x63olor\x18\x01 \x01(\x0b\x32\x15.SC2APIProtocol.Color\x12\x0c\n\x04text\x18\x02 \x01(\t\x12*\n\x0bvirtual_pos\x18\x03 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12(\n\tworld_pos\x18\x04 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12\x0c\n\x04size\x18\x05 \x01(\r\"U\n\tDebugLine\x12$\n\x05\x63olor\x18\x01 \x01(\x0b\x32\x15.SC2APIProtocol.Color\x12\"\n\x04line\x18\x02 \x01(\x0b\x32\x14.SC2APIProtocol.Line\"x\n\x08\x44\x65\x62ugBox\x12$\n\x05\x63olor\x18\x01 \x01(\x0b\x32\x15.SC2APIProtocol.Color\x12\"\n\x03min\x18\x02 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12\"\n\x03max\x18\x03 \x01(\x0b\x32\x15.SC2APIProtocol.Point\"`\n\x0b\x44\x65\x62ugSphere\x12$\n\x05\x63olor\x18\x01 \x01(\x0b\x32\x15.SC2APIProtocol.Color\x12 \n\x01p\x18\x02 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12\t\n\x01r\x18\x03 \x01(\x02\"k\n\x0f\x44\x65\x62ugCreateUnit\x12\x11\n\tunit_type\x18\x01 \x01(\r\x12\r\n\x05owner\x18\x02 \x01(\x05\x12$\n\x03pos\x18\x03 \x01(\x0b\x32\x17.SC2APIProtocol.Point2D\x12\x10\n\x08quantity\x18\x04 \x01(\r\"\x1c\n\rDebugKillUnit\x12\x0b\n\x03tag\x18\x01 \x03(\x04\"\x80\x01\n\x10\x44\x65\x62ugTestProcess\x12\x33\n\x04test\x18\x01 \x01(\x0e\x32%.SC2APIProtocol.DebugTestProcess.Test\x12\x10\n\x08\x64\x65lay_ms\x18\x02 \x01(\x05\"%\n\x04Test\x12\x08\n\x04hang\x10\x01\x12\t\n\x05\x63rash\x10\x02\x12\x08\n\x04\x65xit\x10\x03\"\x1e\n\rDebugSetScore\x12\r\n\x05score\x18\x01 \x01(\x02\"z\n\x0c\x44\x65\x62ugEndGame\x12:\n\nend_result\x18\x01 \x01(\x0e\x32&.SC2APIProtocol.DebugEndGame.EndResult\".\n\tEndResult\x12\r\n\tSurrender\x10\x01\x12\x12\n\x0e\x44\x65\x63lareVictory\x10\x02\"\xa5\x01\n\x11\x44\x65\x62ugSetUnitValue\x12?\n\nunit_value\x18\x01 \x01(\x0e\x32+.SC2APIProtocol.DebugSetUnitValue.UnitValue\x12\r\n\x05value\x18\x02 \x01(\x02\x12\x10\n\x08unit_tag\x18\x03 \x01(\x04\".\n\tUnitValue\x12\n\n\x06\x45nergy\x10\x01\x12\x08\n\x04Life\x10\x02\x12\x0b\n\x07Shields\x10\x03*\xb2\x01\n\x0e\x44\x65\x62ugGameState\x12\x0c\n\x08show_map\x10\x01\x12\x11\n\rcontrol_enemy\x10\x02\x12\x08\n\x04\x66ood\x10\x03\x12\x08\n\x04\x66ree\x10\x04\x12\x11\n\rall_resources\x10\x05\x12\x07\n\x03god\x10\x06\x12\x0c\n\x08minerals\x10\x07\x12\x07\n\x03gas\x10\x08\x12\x0c\n\x08\x63ooldown\x10\t\x12\r\n\ttech_tree\x10\n\x12\x0b\n\x07upgrade\x10\x0b\x12\x0e\n\nfast_build\x10\x0c')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.debug_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_DEBUGGAMESTATE']._serialized_start=1897
+ _globals['_DEBUGGAMESTATE']._serialized_end=2075
+ _globals['_DEBUGCOMMAND']._serialized_start=80
+ _globals['_DEBUGCOMMAND']._serialized_end=523
+ _globals['_DEBUGDRAW']._serialized_start=526
+ _globals['_DEBUGDRAW']._serialized_end=707
+ _globals['_LINE']._serialized_start=709
+ _globals['_LINE']._serialized_end=785
+ _globals['_COLOR']._serialized_start=787
+ _globals['_COLOR']._serialized_end=827
+ _globals['_DEBUGTEXT']._serialized_start=830
+ _globals['_DEBUGTEXT']._serialized_end=993
+ _globals['_DEBUGLINE']._serialized_start=995
+ _globals['_DEBUGLINE']._serialized_end=1080
+ _globals['_DEBUGBOX']._serialized_start=1082
+ _globals['_DEBUGBOX']._serialized_end=1202
+ _globals['_DEBUGSPHERE']._serialized_start=1204
+ _globals['_DEBUGSPHERE']._serialized_end=1300
+ _globals['_DEBUGCREATEUNIT']._serialized_start=1302
+ _globals['_DEBUGCREATEUNIT']._serialized_end=1409
+ _globals['_DEBUGKILLUNIT']._serialized_start=1411
+ _globals['_DEBUGKILLUNIT']._serialized_end=1439
+ _globals['_DEBUGTESTPROCESS']._serialized_start=1442
+ _globals['_DEBUGTESTPROCESS']._serialized_end=1570
+ _globals['_DEBUGTESTPROCESS_TEST']._serialized_start=1533
+ _globals['_DEBUGTESTPROCESS_TEST']._serialized_end=1570
+ _globals['_DEBUGSETSCORE']._serialized_start=1572
+ _globals['_DEBUGSETSCORE']._serialized_end=1602
+ _globals['_DEBUGENDGAME']._serialized_start=1604
+ _globals['_DEBUGENDGAME']._serialized_end=1726
+ _globals['_DEBUGENDGAME_ENDRESULT']._serialized_start=1680
+ _globals['_DEBUGENDGAME_ENDRESULT']._serialized_end=1726
+ _globals['_DEBUGSETUNITVALUE']._serialized_start=1729
+ _globals['_DEBUGSETUNITVALUE']._serialized_end=1894
+ _globals['_DEBUGSETUNITVALUE_UNITVALUE']._serialized_start=1848
+ _globals['_DEBUGSETUNITVALUE_UNITVALUE']._serialized_end=1894
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/proto/error_pb2.py b/worlds/_sc2common/bot/proto/error_pb2.py
new file mode 100644
index 000000000000..d9c5709d74c1
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/error_pb2.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/error.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/error.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cs2clientprotocol/error.proto\x12\x0eSC2APIProtocol*\xa8-\n\x0c\x41\x63tionResult\x12\x0b\n\x07Success\x10\x01\x12\x10\n\x0cNotSupported\x10\x02\x12\t\n\x05\x45rror\x10\x03\x12\x16\n\x12\x43\x61ntQueueThatOrder\x10\x04\x12\t\n\x05Retry\x10\x05\x12\x0c\n\x08\x43ooldown\x10\x06\x12\x0f\n\x0bQueueIsFull\x10\x07\x12\x14\n\x10RallyQueueIsFull\x10\x08\x12\x15\n\x11NotEnoughMinerals\x10\t\x12\x14\n\x10NotEnoughVespene\x10\n\x12\x16\n\x12NotEnoughTerrazine\x10\x0b\x12\x13\n\x0fNotEnoughCustom\x10\x0c\x12\x11\n\rNotEnoughFood\x10\r\x12\x17\n\x13\x46oodUsageImpossible\x10\x0e\x12\x11\n\rNotEnoughLife\x10\x0f\x12\x14\n\x10NotEnoughShields\x10\x10\x12\x13\n\x0fNotEnoughEnergy\x10\x11\x12\x12\n\x0eLifeSuppressed\x10\x12\x12\x15\n\x11ShieldsSuppressed\x10\x13\x12\x14\n\x10\x45nergySuppressed\x10\x14\x12\x14\n\x10NotEnoughCharges\x10\x15\x12\x16\n\x12\x43\x61ntAddMoreCharges\x10\x16\x12\x13\n\x0fTooMuchMinerals\x10\x17\x12\x12\n\x0eTooMuchVespene\x10\x18\x12\x14\n\x10TooMuchTerrazine\x10\x19\x12\x11\n\rTooMuchCustom\x10\x1a\x12\x0f\n\x0bTooMuchFood\x10\x1b\x12\x0f\n\x0bTooMuchLife\x10\x1c\x12\x12\n\x0eTooMuchShields\x10\x1d\x12\x11\n\rTooMuchEnergy\x10\x1e\x12\x1a\n\x16MustTargetUnitWithLife\x10\x1f\x12\x1d\n\x19MustTargetUnitWithShields\x10 \x12\x1c\n\x18MustTargetUnitWithEnergy\x10!\x12\r\n\tCantTrade\x10\"\x12\r\n\tCantSpend\x10#\x12\x16\n\x12\x43\x61ntTargetThatUnit\x10$\x12\x17\n\x13\x43ouldntAllocateUnit\x10%\x12\x10\n\x0cUnitCantMove\x10&\x12\x1e\n\x1aTransportIsHoldingPosition\x10\'\x12\x1f\n\x1b\x42uildTechRequirementsNotMet\x10(\x12\x1d\n\x19\x43\x61ntFindPlacementLocation\x10)\x12\x13\n\x0f\x43\x61ntBuildOnThat\x10*\x12\x1e\n\x1a\x43\x61ntBuildTooCloseToDropOff\x10+\x12\x1c\n\x18\x43\x61ntBuildLocationInvalid\x10,\x12\x18\n\x14\x43\x61ntSeeBuildLocation\x10-\x12\"\n\x1e\x43\x61ntBuildTooCloseToCreepSource\x10.\x12 \n\x1c\x43\x61ntBuildTooCloseToResources\x10/\x12\x1c\n\x18\x43\x61ntBuildTooFarFromWater\x10\x30\x12\"\n\x1e\x43\x61ntBuildTooFarFromCreepSource\x10\x31\x12\'\n#CantBuildTooFarFromBuildPowerSource\x10\x32\x12\x1b\n\x17\x43\x61ntBuildOnDenseTerrain\x10\x33\x12\'\n#CantTrainTooFarFromTrainPowerSource\x10\x34\x12\x1b\n\x17\x43\x61ntLandLocationInvalid\x10\x35\x12\x17\n\x13\x43\x61ntSeeLandLocation\x10\x36\x12!\n\x1d\x43\x61ntLandTooCloseToCreepSource\x10\x37\x12\x1f\n\x1b\x43\x61ntLandTooCloseToResources\x10\x38\x12\x1b\n\x17\x43\x61ntLandTooFarFromWater\x10\x39\x12!\n\x1d\x43\x61ntLandTooFarFromCreepSource\x10:\x12&\n\"CantLandTooFarFromBuildPowerSource\x10;\x12&\n\"CantLandTooFarFromTrainPowerSource\x10<\x12\x1a\n\x16\x43\x61ntLandOnDenseTerrain\x10=\x12\x1b\n\x17\x41\x64\x64OnTooFarFromBuilding\x10>\x12\x1a\n\x16MustBuildRefineryFirst\x10?\x12\x1f\n\x1b\x42uildingIsUnderConstruction\x10@\x12\x13\n\x0f\x43\x61ntFindDropOff\x10\x41\x12\x1d\n\x19\x43\x61ntLoadOtherPlayersUnits\x10\x42\x12\x1b\n\x17NotEnoughRoomToLoadUnit\x10\x43\x12\x18\n\x14\x43\x61ntUnloadUnitsThere\x10\x44\x12\x18\n\x14\x43\x61ntWarpInUnitsThere\x10\x45\x12\x19\n\x15\x43\x61ntLoadImmobileUnits\x10\x46\x12\x1d\n\x19\x43\x61ntRechargeImmobileUnits\x10G\x12&\n\"CantRechargeUnderConstructionUnits\x10H\x12\x14\n\x10\x43\x61ntLoadThatUnit\x10I\x12\x13\n\x0fNoCargoToUnload\x10J\x12\x19\n\x15LoadAllNoTargetsFound\x10K\x12\x14\n\x10NotWhileOccupied\x10L\x12\x19\n\x15\x43\x61ntAttackWithoutAmmo\x10M\x12\x17\n\x13\x43\x61ntHoldAnyMoreAmmo\x10N\x12\x1a\n\x16TechRequirementsNotMet\x10O\x12\x19\n\x15MustLockdownUnitFirst\x10P\x12\x12\n\x0eMustTargetUnit\x10Q\x12\x17\n\x13MustTargetInventory\x10R\x12\x19\n\x15MustTargetVisibleUnit\x10S\x12\x1d\n\x19MustTargetVisibleLocation\x10T\x12\x1e\n\x1aMustTargetWalkableLocation\x10U\x12\x1a\n\x16MustTargetPawnableUnit\x10V\x12\x1a\n\x16YouCantControlThatUnit\x10W\x12\"\n\x1eYouCantIssueCommandsToThatUnit\x10X\x12\x17\n\x13MustTargetResources\x10Y\x12\x16\n\x12RequiresHealTarget\x10Z\x12\x18\n\x14RequiresRepairTarget\x10[\x12\x11\n\rNoItemsToDrop\x10\\\x12\x18\n\x14\x43\x61ntHoldAnyMoreItems\x10]\x12\x10\n\x0c\x43\x61ntHoldThat\x10^\x12\x18\n\x14TargetHasNoInventory\x10_\x12\x14\n\x10\x43\x61ntDropThisItem\x10`\x12\x14\n\x10\x43\x61ntMoveThisItem\x10\x61\x12\x14\n\x10\x43\x61ntPawnThisUnit\x10\x62\x12\x14\n\x10MustTargetCaster\x10\x63\x12\x14\n\x10\x43\x61ntTargetCaster\x10\x64\x12\x13\n\x0fMustTargetOuter\x10\x65\x12\x13\n\x0f\x43\x61ntTargetOuter\x10\x66\x12\x1a\n\x16MustTargetYourOwnUnits\x10g\x12\x1a\n\x16\x43\x61ntTargetYourOwnUnits\x10h\x12\x1b\n\x17MustTargetFriendlyUnits\x10i\x12\x1b\n\x17\x43\x61ntTargetFriendlyUnits\x10j\x12\x1a\n\x16MustTargetNeutralUnits\x10k\x12\x1a\n\x16\x43\x61ntTargetNeutralUnits\x10l\x12\x18\n\x14MustTargetEnemyUnits\x10m\x12\x18\n\x14\x43\x61ntTargetEnemyUnits\x10n\x12\x16\n\x12MustTargetAirUnits\x10o\x12\x16\n\x12\x43\x61ntTargetAirUnits\x10p\x12\x19\n\x15MustTargetGroundUnits\x10q\x12\x19\n\x15\x43\x61ntTargetGroundUnits\x10r\x12\x18\n\x14MustTargetStructures\x10s\x12\x18\n\x14\x43\x61ntTargetStructures\x10t\x12\x18\n\x14MustTargetLightUnits\x10u\x12\x18\n\x14\x43\x61ntTargetLightUnits\x10v\x12\x1a\n\x16MustTargetArmoredUnits\x10w\x12\x1a\n\x16\x43\x61ntTargetArmoredUnits\x10x\x12\x1d\n\x19MustTargetBiologicalUnits\x10y\x12\x1d\n\x19\x43\x61ntTargetBiologicalUnits\x10z\x12\x19\n\x15MustTargetHeroicUnits\x10{\x12\x19\n\x15\x43\x61ntTargetHeroicUnits\x10|\x12\x1a\n\x16MustTargetRoboticUnits\x10}\x12\x1a\n\x16\x43\x61ntTargetRoboticUnits\x10~\x12\x1d\n\x19MustTargetMechanicalUnits\x10\x7f\x12\x1e\n\x19\x43\x61ntTargetMechanicalUnits\x10\x80\x01\x12\x1b\n\x16MustTargetPsionicUnits\x10\x81\x01\x12\x1b\n\x16\x43\x61ntTargetPsionicUnits\x10\x82\x01\x12\x1b\n\x16MustTargetMassiveUnits\x10\x83\x01\x12\x1b\n\x16\x43\x61ntTargetMassiveUnits\x10\x84\x01\x12\x16\n\x11MustTargetMissile\x10\x85\x01\x12\x16\n\x11\x43\x61ntTargetMissile\x10\x86\x01\x12\x1a\n\x15MustTargetWorkerUnits\x10\x87\x01\x12\x1a\n\x15\x43\x61ntTargetWorkerUnits\x10\x88\x01\x12!\n\x1cMustTargetEnergyCapableUnits\x10\x89\x01\x12!\n\x1c\x43\x61ntTargetEnergyCapableUnits\x10\x8a\x01\x12!\n\x1cMustTargetShieldCapableUnits\x10\x8b\x01\x12!\n\x1c\x43\x61ntTargetShieldCapableUnits\x10\x8c\x01\x12\x15\n\x10MustTargetFlyers\x10\x8d\x01\x12\x15\n\x10\x43\x61ntTargetFlyers\x10\x8e\x01\x12\x1a\n\x15MustTargetBuriedUnits\x10\x8f\x01\x12\x1a\n\x15\x43\x61ntTargetBuriedUnits\x10\x90\x01\x12\x1b\n\x16MustTargetCloakedUnits\x10\x91\x01\x12\x1b\n\x16\x43\x61ntTargetCloakedUnits\x10\x92\x01\x12\"\n\x1dMustTargetUnitsInAStasisField\x10\x93\x01\x12\"\n\x1d\x43\x61ntTargetUnitsInAStasisField\x10\x94\x01\x12%\n MustTargetUnderConstructionUnits\x10\x95\x01\x12%\n CantTargetUnderConstructionUnits\x10\x96\x01\x12\x18\n\x13MustTargetDeadUnits\x10\x97\x01\x12\x18\n\x13\x43\x61ntTargetDeadUnits\x10\x98\x01\x12\x1d\n\x18MustTargetRevivableUnits\x10\x99\x01\x12\x1d\n\x18\x43\x61ntTargetRevivableUnits\x10\x9a\x01\x12\x1a\n\x15MustTargetHiddenUnits\x10\x9b\x01\x12\x1a\n\x15\x43\x61ntTargetHiddenUnits\x10\x9c\x01\x12\"\n\x1d\x43\x61ntRechargeOtherPlayersUnits\x10\x9d\x01\x12\x1d\n\x18MustTargetHallucinations\x10\x9e\x01\x12\x1d\n\x18\x43\x61ntTargetHallucinations\x10\x9f\x01\x12 \n\x1bMustTargetInvulnerableUnits\x10\xa0\x01\x12 \n\x1b\x43\x61ntTargetInvulnerableUnits\x10\xa1\x01\x12\x1c\n\x17MustTargetDetectedUnits\x10\xa2\x01\x12\x1c\n\x17\x43\x61ntTargetDetectedUnits\x10\xa3\x01\x12\x1d\n\x18\x43\x61ntTargetUnitWithEnergy\x10\xa4\x01\x12\x1e\n\x19\x43\x61ntTargetUnitWithShields\x10\xa5\x01\x12!\n\x1cMustTargetUncommandableUnits\x10\xa6\x01\x12!\n\x1c\x43\x61ntTargetUncommandableUnits\x10\xa7\x01\x12!\n\x1cMustTargetPreventDefeatUnits\x10\xa8\x01\x12!\n\x1c\x43\x61ntTargetPreventDefeatUnits\x10\xa9\x01\x12!\n\x1cMustTargetPreventRevealUnits\x10\xaa\x01\x12!\n\x1c\x43\x61ntTargetPreventRevealUnits\x10\xab\x01\x12\x1b\n\x16MustTargetPassiveUnits\x10\xac\x01\x12\x1b\n\x16\x43\x61ntTargetPassiveUnits\x10\xad\x01\x12\x1b\n\x16MustTargetStunnedUnits\x10\xae\x01\x12\x1b\n\x16\x43\x61ntTargetStunnedUnits\x10\xaf\x01\x12\x1c\n\x17MustTargetSummonedUnits\x10\xb0\x01\x12\x1c\n\x17\x43\x61ntTargetSummonedUnits\x10\xb1\x01\x12\x14\n\x0fMustTargetUser1\x10\xb2\x01\x12\x14\n\x0f\x43\x61ntTargetUser1\x10\xb3\x01\x12\x1f\n\x1aMustTargetUnstoppableUnits\x10\xb4\x01\x12\x1f\n\x1a\x43\x61ntTargetUnstoppableUnits\x10\xb5\x01\x12\x1d\n\x18MustTargetResistantUnits\x10\xb6\x01\x12\x1d\n\x18\x43\x61ntTargetResistantUnits\x10\xb7\x01\x12\x19\n\x14MustTargetDazedUnits\x10\xb8\x01\x12\x19\n\x14\x43\x61ntTargetDazedUnits\x10\xb9\x01\x12\x11\n\x0c\x43\x61ntLockdown\x10\xba\x01\x12\x14\n\x0f\x43\x61ntMindControl\x10\xbb\x01\x12\x1c\n\x17MustTargetDestructibles\x10\xbc\x01\x12\x1c\n\x17\x43\x61ntTargetDestructibles\x10\xbd\x01\x12\x14\n\x0fMustTargetItems\x10\xbe\x01\x12\x14\n\x0f\x43\x61ntTargetItems\x10\xbf\x01\x12\x18\n\x13NoCalldownAvailable\x10\xc0\x01\x12\x15\n\x10WaypointListFull\x10\xc1\x01\x12\x13\n\x0eMustTargetRace\x10\xc2\x01\x12\x13\n\x0e\x43\x61ntTargetRace\x10\xc3\x01\x12\x1b\n\x16MustTargetSimilarUnits\x10\xc4\x01\x12\x1b\n\x16\x43\x61ntTargetSimilarUnits\x10\xc5\x01\x12\x1a\n\x15\x43\x61ntFindEnoughTargets\x10\xc6\x01\x12\x19\n\x14\x41lreadySpawningLarva\x10\xc7\x01\x12!\n\x1c\x43\x61ntTargetExhaustedResources\x10\xc8\x01\x12\x13\n\x0e\x43\x61ntUseMinimap\x10\xc9\x01\x12\x15\n\x10\x43\x61ntUseInfoPanel\x10\xca\x01\x12\x15\n\x10OrderQueueIsFull\x10\xcb\x01\x12\x1c\n\x17\x43\x61ntHarvestThatResource\x10\xcc\x01\x12\x1a\n\x15HarvestersNotRequired\x10\xcd\x01\x12\x14\n\x0f\x41lreadyTargeted\x10\xce\x01\x12\x1e\n\x19\x43\x61ntAttackWeaponsDisabled\x10\xcf\x01\x12\x17\n\x12\x43ouldntReachTarget\x10\xd0\x01\x12\x17\n\x12TargetIsOutOfRange\x10\xd1\x01\x12\x15\n\x10TargetIsTooClose\x10\xd2\x01\x12\x15\n\x10TargetIsOutOfArc\x10\xd3\x01\x12\x1d\n\x18\x43\x61ntFindTeleportLocation\x10\xd4\x01\x12\x15\n\x10InvalidItemClass\x10\xd5\x01\x12\x18\n\x13\x43\x61ntFindCancelOrder\x10\xd6\x01')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.error_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_ACTIONRESULT']._serialized_start=49
+ _globals['_ACTIONRESULT']._serialized_end=5849
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/proto/query_pb2.py b/worlds/_sc2common/bot/proto/query_pb2.py
new file mode 100644
index 000000000000..40d09ecce156
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/query_pb2.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/query.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/query.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import common_pb2 as s2clientprotocol_dot_common__pb2
+from . import error_pb2 as s2clientprotocol_dot_error__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cs2clientprotocol/query.proto\x12\x0eSC2APIProtocol\x1a\x1ds2clientprotocol/common.proto\x1a\x1cs2clientprotocol/error.proto\"\xf0\x01\n\x0cRequestQuery\x12\x34\n\x07pathing\x18\x01 \x03(\x0b\x32#.SC2APIProtocol.RequestQueryPathing\x12\x41\n\tabilities\x18\x02 \x03(\x0b\x32..SC2APIProtocol.RequestQueryAvailableAbilities\x12\x41\n\nplacements\x18\x03 \x03(\x0b\x32-.SC2APIProtocol.RequestQueryBuildingPlacement\x12$\n\x1cignore_resource_requirements\x18\x04 \x01(\x08\"\xce\x01\n\rResponseQuery\x12\x35\n\x07pathing\x18\x01 \x03(\x0b\x32$.SC2APIProtocol.ResponseQueryPathing\x12\x42\n\tabilities\x18\x02 \x03(\x0b\x32/.SC2APIProtocol.ResponseQueryAvailableAbilities\x12\x42\n\nplacements\x18\x03 \x03(\x0b\x32..SC2APIProtocol.ResponseQueryBuildingPlacement\"\x8a\x01\n\x13RequestQueryPathing\x12,\n\tstart_pos\x18\x01 \x01(\x0b\x32\x17.SC2APIProtocol.Point2DH\x00\x12\x12\n\x08unit_tag\x18\x02 \x01(\x04H\x00\x12(\n\x07\x65nd_pos\x18\x03 \x01(\x0b\x32\x17.SC2APIProtocol.Point2DB\x07\n\x05start\"(\n\x14ResponseQueryPathing\x12\x10\n\x08\x64istance\x18\x01 \x01(\x02\"2\n\x1eRequestQueryAvailableAbilities\x12\x10\n\x08unit_tag\x18\x01 \x01(\x04\"~\n\x1fResponseQueryAvailableAbilities\x12\x33\n\tabilities\x18\x01 \x03(\x0b\x32 .SC2APIProtocol.AvailableAbility\x12\x10\n\x08unit_tag\x18\x02 \x01(\x04\x12\x14\n\x0cunit_type_id\x18\x03 \x01(\r\"z\n\x1dRequestQueryBuildingPlacement\x12\x12\n\nability_id\x18\x01 \x01(\x05\x12+\n\ntarget_pos\x18\x02 \x01(\x0b\x32\x17.SC2APIProtocol.Point2D\x12\x18\n\x10placing_unit_tag\x18\x03 \x01(\x04\"N\n\x1eResponseQueryBuildingPlacement\x12,\n\x06result\x18\x01 \x01(\x0e\x32\x1c.SC2APIProtocol.ActionResult')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.query_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_REQUESTQUERY']._serialized_start=110
+ _globals['_REQUESTQUERY']._serialized_end=350
+ _globals['_RESPONSEQUERY']._serialized_start=353
+ _globals['_RESPONSEQUERY']._serialized_end=559
+ _globals['_REQUESTQUERYPATHING']._serialized_start=562
+ _globals['_REQUESTQUERYPATHING']._serialized_end=700
+ _globals['_RESPONSEQUERYPATHING']._serialized_start=702
+ _globals['_RESPONSEQUERYPATHING']._serialized_end=742
+ _globals['_REQUESTQUERYAVAILABLEABILITIES']._serialized_start=744
+ _globals['_REQUESTQUERYAVAILABLEABILITIES']._serialized_end=794
+ _globals['_RESPONSEQUERYAVAILABLEABILITIES']._serialized_start=796
+ _globals['_RESPONSEQUERYAVAILABLEABILITIES']._serialized_end=922
+ _globals['_REQUESTQUERYBUILDINGPLACEMENT']._serialized_start=924
+ _globals['_REQUESTQUERYBUILDINGPLACEMENT']._serialized_end=1046
+ _globals['_RESPONSEQUERYBUILDINGPLACEMENT']._serialized_start=1048
+ _globals['_RESPONSEQUERYBUILDINGPLACEMENT']._serialized_end=1126
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/proto/raw_pb2.py b/worlds/_sc2common/bot/proto/raw_pb2.py
new file mode 100644
index 000000000000..3eb9ea182c89
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/raw_pb2.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/raw.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/raw.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import common_pb2 as s2clientprotocol_dot_common__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1as2clientprotocol/raw.proto\x12\x0eSC2APIProtocol\x1a\x1ds2clientprotocol/common.proto\"\xb1\x02\n\x08StartRaw\x12)\n\x08map_size\x18\x01 \x01(\x0b\x32\x17.SC2APIProtocol.Size2DI\x12/\n\x0cpathing_grid\x18\x02 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x31\n\x0eterrain_height\x18\x03 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x31\n\x0eplacement_grid\x18\x04 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x31\n\rplayable_area\x18\x05 \x01(\x0b\x32\x1a.SC2APIProtocol.RectangleI\x12\x30\n\x0fstart_locations\x18\x06 \x03(\x0b\x32\x17.SC2APIProtocol.Point2D\"\x86\x02\n\x0eObservationRaw\x12)\n\x06player\x18\x01 \x01(\x0b\x32\x19.SC2APIProtocol.PlayerRaw\x12#\n\x05units\x18\x02 \x03(\x0b\x32\x14.SC2APIProtocol.Unit\x12+\n\tmap_state\x18\x03 \x01(\x0b\x32\x18.SC2APIProtocol.MapState\x12$\n\x05\x65vent\x18\x04 \x01(\x0b\x32\x15.SC2APIProtocol.Event\x12\'\n\x07\x65\x66\x66\x65\x63ts\x18\x05 \x03(\x0b\x32\x16.SC2APIProtocol.Effect\x12(\n\x05radar\x18\x06 \x03(\x0b\x32\x19.SC2APIProtocol.RadarRing\"?\n\tRadarRing\x12\"\n\x03pos\x18\x01 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12\x0e\n\x06radius\x18\x02 \x01(\x02\"N\n\x0bPowerSource\x12\"\n\x03pos\x18\x01 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12\x0e\n\x06radius\x18\x02 \x01(\x02\x12\x0b\n\x03tag\x18\x03 \x01(\x04\"{\n\tPlayerRaw\x12\x32\n\rpower_sources\x18\x01 \x03(\x0b\x32\x1b.SC2APIProtocol.PowerSource\x12%\n\x06\x63\x61mera\x18\x02 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12\x13\n\x0bupgrade_ids\x18\x03 \x03(\r\"\x8f\x01\n\tUnitOrder\x12\x12\n\nability_id\x18\x01 \x01(\r\x12\x37\n\x16target_world_space_pos\x18\x02 \x01(\x0b\x32\x15.SC2APIProtocol.PointH\x00\x12\x19\n\x0ftarget_unit_tag\x18\x03 \x01(\x04H\x00\x12\x10\n\x08progress\x18\x04 \x01(\x02\x42\x08\n\x06target\"\x9b\x01\n\rPassengerUnit\x12\x0b\n\x03tag\x18\x01 \x01(\x04\x12\x0e\n\x06health\x18\x02 \x01(\x02\x12\x12\n\nhealth_max\x18\x03 \x01(\x02\x12\x0e\n\x06shield\x18\x04 \x01(\x02\x12\x12\n\nshield_max\x18\x07 \x01(\x02\x12\x0e\n\x06\x65nergy\x18\x05 \x01(\x02\x12\x12\n\nenergy_max\x18\x08 \x01(\x02\x12\x11\n\tunit_type\x18\x06 \x01(\r\"@\n\x0bRallyTarget\x12$\n\x05point\x18\x01 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12\x0b\n\x03tag\x18\x02 \x01(\x04\"\xf5\x08\n\x04Unit\x12\x31\n\x0c\x64isplay_type\x18\x01 \x01(\x0e\x32\x1b.SC2APIProtocol.DisplayType\x12*\n\x08\x61lliance\x18\x02 \x01(\x0e\x32\x18.SC2APIProtocol.Alliance\x12\x0b\n\x03tag\x18\x03 \x01(\x04\x12\x11\n\tunit_type\x18\x04 \x01(\r\x12\r\n\x05owner\x18\x05 \x01(\x05\x12\"\n\x03pos\x18\x06 \x01(\x0b\x32\x15.SC2APIProtocol.Point\x12\x0e\n\x06\x66\x61\x63ing\x18\x07 \x01(\x02\x12\x0e\n\x06radius\x18\x08 \x01(\x02\x12\x16\n\x0e\x62uild_progress\x18\t \x01(\x02\x12)\n\x05\x63loak\x18\n \x01(\x0e\x32\x1a.SC2APIProtocol.CloakState\x12\x10\n\x08\x62uff_ids\x18\x1b \x03(\r\x12\x14\n\x0c\x64\x65tect_range\x18\x1f \x01(\x02\x12\x13\n\x0bradar_range\x18 \x01(\x02\x12\x13\n\x0bis_selected\x18\x0b \x01(\x08\x12\x14\n\x0cis_on_screen\x18\x0c \x01(\x08\x12\x0f\n\x07is_blip\x18\r \x01(\x08\x12\x12\n\nis_powered\x18# \x01(\x08\x12\x11\n\tis_active\x18\' \x01(\x08\x12\x1c\n\x14\x61ttack_upgrade_level\x18( \x01(\x05\x12\x1b\n\x13\x61rmor_upgrade_level\x18) \x01(\x05\x12\x1c\n\x14shield_upgrade_level\x18* \x01(\x05\x12\x0e\n\x06health\x18\x0e \x01(\x02\x12\x12\n\nhealth_max\x18\x0f \x01(\x02\x12\x0e\n\x06shield\x18\x10 \x01(\x02\x12\x12\n\nshield_max\x18$ \x01(\x02\x12\x0e\n\x06\x65nergy\x18\x11 \x01(\x02\x12\x12\n\nenergy_max\x18% \x01(\x02\x12\x18\n\x10mineral_contents\x18\x12 \x01(\x05\x12\x18\n\x10vespene_contents\x18\x13 \x01(\x05\x12\x11\n\tis_flying\x18\x14 \x01(\x08\x12\x13\n\x0bis_burrowed\x18\x15 \x01(\x08\x12\x18\n\x10is_hallucination\x18& \x01(\x08\x12)\n\x06orders\x18\x16 \x03(\x0b\x32\x19.SC2APIProtocol.UnitOrder\x12\x12\n\nadd_on_tag\x18\x17 \x01(\x04\x12\x31\n\npassengers\x18\x18 \x03(\x0b\x32\x1d.SC2APIProtocol.PassengerUnit\x12\x19\n\x11\x63\x61rgo_space_taken\x18\x19 \x01(\x05\x12\x17\n\x0f\x63\x61rgo_space_max\x18\x1a \x01(\x05\x12\x1b\n\x13\x61ssigned_harvesters\x18\x1c \x01(\x05\x12\x18\n\x10ideal_harvesters\x18\x1d \x01(\x05\x12\x17\n\x0fweapon_cooldown\x18\x1e \x01(\x02\x12\x1a\n\x12\x65ngaged_target_tag\x18\" \x01(\x04\x12\x1c\n\x14\x62uff_duration_remain\x18+ \x01(\x05\x12\x19\n\x11\x62uff_duration_max\x18, \x01(\x05\x12\x32\n\rrally_targets\x18- \x03(\x0b\x32\x1b.SC2APIProtocol.RallyTarget\"c\n\x08MapState\x12-\n\nvisibility\x18\x01 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12(\n\x05\x63reep\x18\x02 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\"\x1b\n\x05\x45vent\x12\x12\n\ndead_units\x18\x01 \x03(\x04\"\x8c\x01\n\x06\x45\x66\x66\x65\x63t\x12\x11\n\teffect_id\x18\x01 \x01(\r\x12$\n\x03pos\x18\x02 \x03(\x0b\x32\x17.SC2APIProtocol.Point2D\x12*\n\x08\x61lliance\x18\x03 \x01(\x0e\x32\x18.SC2APIProtocol.Alliance\x12\r\n\x05owner\x18\x04 \x01(\x05\x12\x0e\n\x06radius\x18\x05 \x01(\x02\"\xd3\x01\n\tActionRaw\x12<\n\x0cunit_command\x18\x01 \x01(\x0b\x32$.SC2APIProtocol.ActionRawUnitCommandH\x00\x12:\n\x0b\x63\x61mera_move\x18\x02 \x01(\x0b\x32#.SC2APIProtocol.ActionRawCameraMoveH\x00\x12\x42\n\x0ftoggle_autocast\x18\x03 \x01(\x0b\x32\'.SC2APIProtocol.ActionRawToggleAutocastH\x00\x42\x08\n\x06\x61\x63tion\"\xb4\x01\n\x14\x41\x63tionRawUnitCommand\x12\x12\n\nability_id\x18\x01 \x01(\x05\x12\x39\n\x16target_world_space_pos\x18\x02 \x01(\x0b\x32\x17.SC2APIProtocol.Point2DH\x00\x12\x19\n\x0ftarget_unit_tag\x18\x03 \x01(\x04H\x00\x12\x11\n\tunit_tags\x18\x04 \x03(\x04\x12\x15\n\rqueue_command\x18\x05 \x01(\x08\x42\x08\n\x06target\"H\n\x13\x41\x63tionRawCameraMove\x12\x31\n\x12\x63\x65nter_world_space\x18\x01 \x01(\x0b\x32\x15.SC2APIProtocol.Point\"@\n\x17\x41\x63tionRawToggleAutocast\x12\x12\n\nability_id\x18\x01 \x01(\x05\x12\x11\n\tunit_tags\x18\x02 \x03(\x04*E\n\x0b\x44isplayType\x12\x0b\n\x07Visible\x10\x01\x12\x0c\n\x08Snapshot\x10\x02\x12\n\n\x06Hidden\x10\x03\x12\x0f\n\x0bPlaceholder\x10\x04*6\n\x08\x41lliance\x12\x08\n\x04Self\x10\x01\x12\x08\n\x04\x41lly\x10\x02\x12\x0b\n\x07Neutral\x10\x03\x12\t\n\x05\x45nemy\x10\x04*e\n\nCloakState\x12\x12\n\x0e\x43loakedUnknown\x10\x00\x12\x0b\n\x07\x43loaked\x10\x01\x12\x13\n\x0f\x43loakedDetected\x10\x02\x12\x0e\n\nNotCloaked\x10\x03\x12\x11\n\rCloakedAllied\x10\x04')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.raw_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_DISPLAYTYPE']._serialized_start=3244
+ _globals['_DISPLAYTYPE']._serialized_end=3313
+ _globals['_ALLIANCE']._serialized_start=3315
+ _globals['_ALLIANCE']._serialized_end=3369
+ _globals['_CLOAKSTATE']._serialized_start=3371
+ _globals['_CLOAKSTATE']._serialized_end=3472
+ _globals['_STARTRAW']._serialized_start=78
+ _globals['_STARTRAW']._serialized_end=383
+ _globals['_OBSERVATIONRAW']._serialized_start=386
+ _globals['_OBSERVATIONRAW']._serialized_end=648
+ _globals['_RADARRING']._serialized_start=650
+ _globals['_RADARRING']._serialized_end=713
+ _globals['_POWERSOURCE']._serialized_start=715
+ _globals['_POWERSOURCE']._serialized_end=793
+ _globals['_PLAYERRAW']._serialized_start=795
+ _globals['_PLAYERRAW']._serialized_end=918
+ _globals['_UNITORDER']._serialized_start=921
+ _globals['_UNITORDER']._serialized_end=1064
+ _globals['_PASSENGERUNIT']._serialized_start=1067
+ _globals['_PASSENGERUNIT']._serialized_end=1222
+ _globals['_RALLYTARGET']._serialized_start=1224
+ _globals['_RALLYTARGET']._serialized_end=1288
+ _globals['_UNIT']._serialized_start=1291
+ _globals['_UNIT']._serialized_end=2432
+ _globals['_MAPSTATE']._serialized_start=2434
+ _globals['_MAPSTATE']._serialized_end=2533
+ _globals['_EVENT']._serialized_start=2535
+ _globals['_EVENT']._serialized_end=2562
+ _globals['_EFFECT']._serialized_start=2565
+ _globals['_EFFECT']._serialized_end=2705
+ _globals['_ACTIONRAW']._serialized_start=2708
+ _globals['_ACTIONRAW']._serialized_end=2919
+ _globals['_ACTIONRAWUNITCOMMAND']._serialized_start=2922
+ _globals['_ACTIONRAWUNITCOMMAND']._serialized_end=3102
+ _globals['_ACTIONRAWCAMERAMOVE']._serialized_start=3104
+ _globals['_ACTIONRAWCAMERAMOVE']._serialized_end=3176
+ _globals['_ACTIONRAWTOGGLEAUTOCAST']._serialized_start=3178
+ _globals['_ACTIONRAWTOGGLEAUTOCAST']._serialized_end=3242
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/proto/sc2api_pb2.py b/worlds/_sc2common/bot/proto/sc2api_pb2.py
new file mode 100644
index 000000000000..55a4d010083f
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/sc2api_pb2.py
@@ -0,0 +1,197 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/sc2api.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/sc2api.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import common_pb2 as s2clientprotocol_dot_common__pb2
+from . import data_pb2 as s2clientprotocol_dot_data__pb2
+from . import debug_pb2 as s2clientprotocol_dot_debug__pb2
+from . import error_pb2 as s2clientprotocol_dot_error__pb2
+from . import query_pb2 as s2clientprotocol_dot_query__pb2
+from . import raw_pb2 as s2clientprotocol_dot_raw__pb2
+from . import score_pb2 as s2clientprotocol_dot_score__pb2
+from . import spatial_pb2 as s2clientprotocol_dot_spatial__pb2
+from . import ui_pb2 as s2clientprotocol_dot_ui__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ds2clientprotocol/sc2api.proto\x12\x0eSC2APIProtocol\x1a\x1ds2clientprotocol/common.proto\x1a\x1bs2clientprotocol/data.proto\x1a\x1cs2clientprotocol/debug.proto\x1a\x1cs2clientprotocol/error.proto\x1a\x1cs2clientprotocol/query.proto\x1a\x1as2clientprotocol/raw.proto\x1a\x1cs2clientprotocol/score.proto\x1a\x1es2clientprotocol/spatial.proto\x1a\x19s2clientprotocol/ui.proto\"\xc3\t\n\x07Request\x12\x38\n\x0b\x63reate_game\x18\x01 \x01(\x0b\x32!.SC2APIProtocol.RequestCreateGameH\x00\x12\x34\n\tjoin_game\x18\x02 \x01(\x0b\x32\x1f.SC2APIProtocol.RequestJoinGameH\x00\x12:\n\x0crestart_game\x18\x03 \x01(\x0b\x32\".SC2APIProtocol.RequestRestartGameH\x00\x12:\n\x0cstart_replay\x18\x04 \x01(\x0b\x32\".SC2APIProtocol.RequestStartReplayH\x00\x12\x36\n\nleave_game\x18\x05 \x01(\x0b\x32 .SC2APIProtocol.RequestLeaveGameH\x00\x12\x36\n\nquick_save\x18\x06 \x01(\x0b\x32 .SC2APIProtocol.RequestQuickSaveH\x00\x12\x36\n\nquick_load\x18\x07 \x01(\x0b\x32 .SC2APIProtocol.RequestQuickLoadH\x00\x12+\n\x04quit\x18\x08 \x01(\x0b\x32\x1b.SC2APIProtocol.RequestQuitH\x00\x12\x34\n\tgame_info\x18\t \x01(\x0b\x32\x1f.SC2APIProtocol.RequestGameInfoH\x00\x12\x39\n\x0bobservation\x18\n \x01(\x0b\x32\".SC2APIProtocol.RequestObservationH\x00\x12/\n\x06\x61\x63tion\x18\x0b \x01(\x0b\x32\x1d.SC2APIProtocol.RequestActionH\x00\x12;\n\nobs_action\x18\x15 \x01(\x0b\x32%.SC2APIProtocol.RequestObserverActionH\x00\x12+\n\x04step\x18\x0c \x01(\x0b\x32\x1b.SC2APIProtocol.RequestStepH\x00\x12+\n\x04\x64\x61ta\x18\r \x01(\x0b\x32\x1b.SC2APIProtocol.RequestDataH\x00\x12-\n\x05query\x18\x0e \x01(\x0b\x32\x1c.SC2APIProtocol.RequestQueryH\x00\x12\x38\n\x0bsave_replay\x18\x0f \x01(\x0b\x32!.SC2APIProtocol.RequestSaveReplayH\x00\x12\x38\n\x0bmap_command\x18\x16 \x01(\x0b\x32!.SC2APIProtocol.RequestMapCommandH\x00\x12\x38\n\x0breplay_info\x18\x10 \x01(\x0b\x32!.SC2APIProtocol.RequestReplayInfoH\x00\x12>\n\x0e\x61vailable_maps\x18\x11 \x01(\x0b\x32$.SC2APIProtocol.RequestAvailableMapsH\x00\x12\x32\n\x08save_map\x18\x12 \x01(\x0b\x32\x1e.SC2APIProtocol.RequestSaveMapH\x00\x12+\n\x04ping\x18\x13 \x01(\x0b\x32\x1b.SC2APIProtocol.RequestPingH\x00\x12-\n\x05\x64\x65\x62ug\x18\x14 \x01(\x0b\x32\x1c.SC2APIProtocol.RequestDebugH\x00\x12\n\n\x02id\x18\x61 \x01(\rB\t\n\x07request\"\x92\n\n\x08Response\x12\x39\n\x0b\x63reate_game\x18\x01 \x01(\x0b\x32\".SC2APIProtocol.ResponseCreateGameH\x00\x12\x35\n\tjoin_game\x18\x02 \x01(\x0b\x32 .SC2APIProtocol.ResponseJoinGameH\x00\x12;\n\x0crestart_game\x18\x03 \x01(\x0b\x32#.SC2APIProtocol.ResponseRestartGameH\x00\x12;\n\x0cstart_replay\x18\x04 \x01(\x0b\x32#.SC2APIProtocol.ResponseStartReplayH\x00\x12\x37\n\nleave_game\x18\x05 \x01(\x0b\x32!.SC2APIProtocol.ResponseLeaveGameH\x00\x12\x37\n\nquick_save\x18\x06 \x01(\x0b\x32!.SC2APIProtocol.ResponseQuickSaveH\x00\x12\x37\n\nquick_load\x18\x07 \x01(\x0b\x32!.SC2APIProtocol.ResponseQuickLoadH\x00\x12,\n\x04quit\x18\x08 \x01(\x0b\x32\x1c.SC2APIProtocol.ResponseQuitH\x00\x12\x35\n\tgame_info\x18\t \x01(\x0b\x32 .SC2APIProtocol.ResponseGameInfoH\x00\x12:\n\x0bobservation\x18\n \x01(\x0b\x32#.SC2APIProtocol.ResponseObservationH\x00\x12\x30\n\x06\x61\x63tion\x18\x0b \x01(\x0b\x32\x1e.SC2APIProtocol.ResponseActionH\x00\x12<\n\nobs_action\x18\x15 \x01(\x0b\x32&.SC2APIProtocol.ResponseObserverActionH\x00\x12,\n\x04step\x18\x0c \x01(\x0b\x32\x1c.SC2APIProtocol.ResponseStepH\x00\x12,\n\x04\x64\x61ta\x18\r \x01(\x0b\x32\x1c.SC2APIProtocol.ResponseDataH\x00\x12.\n\x05query\x18\x0e \x01(\x0b\x32\x1d.SC2APIProtocol.ResponseQueryH\x00\x12\x39\n\x0bsave_replay\x18\x0f \x01(\x0b\x32\".SC2APIProtocol.ResponseSaveReplayH\x00\x12\x39\n\x0breplay_info\x18\x10 \x01(\x0b\x32\".SC2APIProtocol.ResponseReplayInfoH\x00\x12?\n\x0e\x61vailable_maps\x18\x11 \x01(\x0b\x32%.SC2APIProtocol.ResponseAvailableMapsH\x00\x12\x33\n\x08save_map\x18\x12 \x01(\x0b\x32\x1f.SC2APIProtocol.ResponseSaveMapH\x00\x12\x39\n\x0bmap_command\x18\x16 \x01(\x0b\x32\".SC2APIProtocol.ResponseMapCommandH\x00\x12,\n\x04ping\x18\x13 \x01(\x0b\x32\x1c.SC2APIProtocol.ResponsePingH\x00\x12.\n\x05\x64\x65\x62ug\x18\x14 \x01(\x0b\x32\x1d.SC2APIProtocol.ResponseDebugH\x00\x12\n\n\x02id\x18\x61 \x01(\r\x12\r\n\x05\x65rror\x18\x62 \x03(\t\x12&\n\x06status\x18\x63 \x01(\x0e\x32\x16.SC2APIProtocol.StatusB\n\n\x08response\"\xd6\x01\n\x11RequestCreateGame\x12-\n\tlocal_map\x18\x01 \x01(\x0b\x32\x18.SC2APIProtocol.LocalMapH\x00\x12\x1c\n\x12\x62\x61ttlenet_map_name\x18\x02 \x01(\tH\x00\x12\x31\n\x0cplayer_setup\x18\x03 \x03(\x0b\x32\x1b.SC2APIProtocol.PlayerSetup\x12\x13\n\x0b\x64isable_fog\x18\x04 \x01(\x08\x12\x13\n\x0brandom_seed\x18\x05 \x01(\r\x12\x10\n\x08realtime\x18\x06 \x01(\x08\x42\x05\n\x03Map\".\n\x08LocalMap\x12\x10\n\x08map_path\x18\x01 \x01(\t\x12\x10\n\x08map_data\x18\x07 \x01(\x0c\"\x9c\x02\n\x12ResponseCreateGame\x12\x37\n\x05\x65rror\x18\x01 \x01(\x0e\x32(.SC2APIProtocol.ResponseCreateGame.Error\x12\x15\n\rerror_details\x18\x02 \x01(\t\"\xb5\x01\n\x05\x45rror\x12\x0e\n\nMissingMap\x10\x01\x12\x12\n\x0eInvalidMapPath\x10\x02\x12\x12\n\x0eInvalidMapData\x10\x03\x12\x12\n\x0eInvalidMapName\x10\x04\x12\x14\n\x10InvalidMapHandle\x10\x05\x12\x16\n\x12MissingPlayerSetup\x10\x06\x12\x16\n\x12InvalidPlayerSetup\x10\x07\x12\x1a\n\x16MultiplayerUnsupported\x10\x08\"\xb2\x02\n\x0fRequestJoinGame\x12$\n\x04race\x18\x01 \x01(\x0e\x32\x14.SC2APIProtocol.RaceH\x00\x12\x1c\n\x12observed_player_id\x18\x02 \x01(\rH\x00\x12\x31\n\x07options\x18\x03 \x01(\x0b\x32 .SC2APIProtocol.InterfaceOptions\x12-\n\x0cserver_ports\x18\x04 \x01(\x0b\x32\x17.SC2APIProtocol.PortSet\x12-\n\x0c\x63lient_ports\x18\x05 \x03(\x0b\x32\x17.SC2APIProtocol.PortSet\x12\x13\n\x0bshared_port\x18\x06 \x01(\x05\x12\x13\n\x0bplayer_name\x18\x07 \x01(\t\x12\x0f\n\x07host_ip\x18\x08 \x01(\tB\x0f\n\rparticipation\"/\n\x07PortSet\x12\x11\n\tgame_port\x18\x01 \x01(\x05\x12\x11\n\tbase_port\x18\x02 \x01(\x05\"\x82\x03\n\x10ResponseJoinGame\x12\x11\n\tplayer_id\x18\x01 \x01(\r\x12\x35\n\x05\x65rror\x18\x02 \x01(\x0e\x32&.SC2APIProtocol.ResponseJoinGame.Error\x12\x15\n\rerror_details\x18\x03 \x01(\t\"\x8c\x02\n\x05\x45rror\x12\x18\n\x14MissingParticipation\x10\x01\x12\x1b\n\x17InvalidObservedPlayerId\x10\x02\x12\x12\n\x0eMissingOptions\x10\x03\x12\x10\n\x0cMissingPorts\x10\x04\x12\x0c\n\x08GameFull\x10\x05\x12\x0f\n\x0bLaunchError\x10\x06\x12\x16\n\x12\x46\x65\x61tureUnsupported\x10\x07\x12\x12\n\x0eNoSpaceForUser\x10\x08\x12\x13\n\x0fMapDoesNotExist\x10\t\x12\x11\n\rCannotOpenMap\x10\n\x12\x11\n\rChecksumError\x10\x0b\x12\x10\n\x0cNetworkError\x10\x0c\x12\x0e\n\nOtherError\x10\r\"\x14\n\x12RequestRestartGame\"\x99\x01\n\x13ResponseRestartGame\x12\x38\n\x05\x65rror\x18\x01 \x01(\x0e\x32).SC2APIProtocol.ResponseRestartGame.Error\x12\x15\n\rerror_details\x18\x02 \x01(\t\x12\x17\n\x0fneed_hard_reset\x18\x03 \x01(\x08\"\x18\n\x05\x45rror\x12\x0f\n\x0bLaunchError\x10\x01\"\xeb\x01\n\x12RequestStartReplay\x12\x15\n\x0breplay_path\x18\x01 \x01(\tH\x00\x12\x15\n\x0breplay_data\x18\x05 \x01(\x0cH\x00\x12\x10\n\x08map_data\x18\x06 \x01(\x0c\x12\x1a\n\x12observed_player_id\x18\x02 \x01(\x05\x12\x31\n\x07options\x18\x03 \x01(\x0b\x32 .SC2APIProtocol.InterfaceOptions\x12\x13\n\x0b\x64isable_fog\x18\x04 \x01(\x08\x12\x10\n\x08realtime\x18\x07 \x01(\x08\x12\x15\n\rrecord_replay\x18\x08 \x01(\x08\x42\x08\n\x06replay\"\x87\x02\n\x13ResponseStartReplay\x12\x38\n\x05\x65rror\x18\x01 \x01(\x0e\x32).SC2APIProtocol.ResponseStartReplay.Error\x12\x15\n\rerror_details\x18\x02 \x01(\t\"\x9e\x01\n\x05\x45rror\x12\x11\n\rMissingReplay\x10\x01\x12\x15\n\x11InvalidReplayPath\x10\x02\x12\x15\n\x11InvalidReplayData\x10\x03\x12\x12\n\x0eInvalidMapData\x10\x04\x12\x1b\n\x17InvalidObservedPlayerId\x10\x05\x12\x12\n\x0eMissingOptions\x10\x06\x12\x0f\n\x0bLaunchError\x10\x07\"(\n\x11RequestMapCommand\x12\x13\n\x0btrigger_cmd\x18\x01 \x01(\t\"\x81\x01\n\x12ResponseMapCommand\x12\x37\n\x05\x65rror\x18\x01 \x01(\x0e\x32(.SC2APIProtocol.ResponseMapCommand.Error\x12\x15\n\rerror_details\x18\x02 \x01(\t\"\x1b\n\x05\x45rror\x12\x12\n\x0eNoTriggerError\x10\x01\"\x12\n\x10RequestLeaveGame\"\x13\n\x11ResponseLeaveGame\"\x12\n\x10RequestQuickSave\"\x13\n\x11ResponseQuickSave\"\x12\n\x10RequestQuickLoad\"\x13\n\x11ResponseQuickLoad\"\r\n\x0bRequestQuit\"\x0e\n\x0cResponseQuit\"\x11\n\x0fRequestGameInfo\"\xe0\x01\n\x10ResponseGameInfo\x12\x10\n\x08map_name\x18\x01 \x01(\t\x12\x11\n\tmod_names\x18\x06 \x03(\t\x12\x16\n\x0elocal_map_path\x18\x02 \x01(\t\x12/\n\x0bplayer_info\x18\x03 \x03(\x0b\x32\x1a.SC2APIProtocol.PlayerInfo\x12+\n\tstart_raw\x18\x04 \x01(\x0b\x32\x18.SC2APIProtocol.StartRaw\x12\x31\n\x07options\x18\x05 \x01(\x0b\x32 .SC2APIProtocol.InterfaceOptions\"<\n\x12RequestObservation\x12\x13\n\x0b\x64isable_fog\x18\x01 \x01(\x08\x12\x11\n\tgame_loop\x18\x02 \x01(\r\"\x85\x02\n\x13ResponseObservation\x12\'\n\x07\x61\x63tions\x18\x01 \x03(\x0b\x32\x16.SC2APIProtocol.Action\x12\x32\n\raction_errors\x18\x02 \x03(\x0b\x32\x1b.SC2APIProtocol.ActionError\x12\x30\n\x0bobservation\x18\x03 \x01(\x0b\x32\x1b.SC2APIProtocol.Observation\x12\x33\n\rplayer_result\x18\x04 \x03(\x0b\x32\x1c.SC2APIProtocol.PlayerResult\x12*\n\x04\x63hat\x18\x05 \x03(\x0b\x32\x1c.SC2APIProtocol.ChatReceived\"2\n\x0c\x43hatReceived\x12\x11\n\tplayer_id\x18\x01 \x01(\r\x12\x0f\n\x07message\x18\x02 \x01(\t\"8\n\rRequestAction\x12\'\n\x07\x61\x63tions\x18\x01 \x03(\x0b\x32\x16.SC2APIProtocol.Action\">\n\x0eResponseAction\x12,\n\x06result\x18\x01 \x03(\x0e\x32\x1c.SC2APIProtocol.ActionResult\"H\n\x15RequestObserverAction\x12/\n\x07\x61\x63tions\x18\x01 \x03(\x0b\x32\x1e.SC2APIProtocol.ObserverAction\"\x18\n\x16ResponseObserverAction\"\x1c\n\x0bRequestStep\x12\r\n\x05\x63ount\x18\x01 \x01(\r\"\'\n\x0cResponseStep\x12\x17\n\x0fsimulation_loop\x18\x01 \x01(\r\"o\n\x0bRequestData\x12\x12\n\nability_id\x18\x01 \x01(\x08\x12\x14\n\x0cunit_type_id\x18\x02 \x01(\x08\x12\x12\n\nupgrade_id\x18\x03 \x01(\x08\x12\x0f\n\x07\x62uff_id\x18\x04 \x01(\x08\x12\x11\n\teffect_id\x18\x05 \x01(\x08\"\xf0\x01\n\x0cResponseData\x12.\n\tabilities\x18\x01 \x03(\x0b\x32\x1b.SC2APIProtocol.AbilityData\x12+\n\x05units\x18\x02 \x03(\x0b\x32\x1c.SC2APIProtocol.UnitTypeData\x12-\n\x08upgrades\x18\x03 \x03(\x0b\x32\x1b.SC2APIProtocol.UpgradeData\x12\'\n\x05\x62uffs\x18\x04 \x03(\x0b\x32\x18.SC2APIProtocol.BuffData\x12+\n\x07\x65\x66\x66\x65\x63ts\x18\x05 \x03(\x0b\x32\x1a.SC2APIProtocol.EffectData\"\x13\n\x11RequestSaveReplay\"\"\n\x12ResponseSaveReplay\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"b\n\x11RequestReplayInfo\x12\x15\n\x0breplay_path\x18\x01 \x01(\tH\x00\x12\x15\n\x0breplay_data\x18\x02 \x01(\x0cH\x00\x12\x15\n\rdownload_data\x18\x03 \x01(\x08\x42\x08\n\x06replay\"\x9f\x01\n\x0fPlayerInfoExtra\x12/\n\x0bplayer_info\x18\x01 \x01(\x0b\x32\x1a.SC2APIProtocol.PlayerInfo\x12\x33\n\rplayer_result\x18\x02 \x01(\x0b\x32\x1c.SC2APIProtocol.PlayerResult\x12\x12\n\nplayer_mmr\x18\x03 \x01(\x05\x12\x12\n\nplayer_apm\x18\x04 \x01(\x05\"\xc3\x03\n\x12ResponseReplayInfo\x12\x10\n\x08map_name\x18\x01 \x01(\t\x12\x16\n\x0elocal_map_path\x18\x02 \x01(\t\x12\x34\n\x0bplayer_info\x18\x03 \x03(\x0b\x32\x1f.SC2APIProtocol.PlayerInfoExtra\x12\x1b\n\x13game_duration_loops\x18\x04 \x01(\r\x12\x1d\n\x15game_duration_seconds\x18\x05 \x01(\x02\x12\x14\n\x0cgame_version\x18\x06 \x01(\t\x12\x14\n\x0c\x64\x61ta_version\x18\x0b \x01(\t\x12\x12\n\ndata_build\x18\x07 \x01(\r\x12\x12\n\nbase_build\x18\x08 \x01(\r\x12\x37\n\x05\x65rror\x18\t \x01(\x0e\x32(.SC2APIProtocol.ResponseReplayInfo.Error\x12\x15\n\rerror_details\x18\n \x01(\t\"m\n\x05\x45rror\x12\x11\n\rMissingReplay\x10\x01\x12\x15\n\x11InvalidReplayPath\x10\x02\x12\x15\n\x11InvalidReplayData\x10\x03\x12\x10\n\x0cParsingError\x10\x04\x12\x11\n\rDownloadError\x10\x05\"\x16\n\x14RequestAvailableMaps\"M\n\x15ResponseAvailableMaps\x12\x17\n\x0flocal_map_paths\x18\x01 \x03(\t\x12\x1b\n\x13\x62\x61ttlenet_map_names\x18\x02 \x03(\t\"4\n\x0eRequestSaveMap\x12\x10\n\x08map_path\x18\x01 \x01(\t\x12\x10\n\x08map_data\x18\x02 \x01(\x0c\"d\n\x0fResponseSaveMap\x12\x34\n\x05\x65rror\x18\x01 \x01(\x0e\x32%.SC2APIProtocol.ResponseSaveMap.Error\"\x1b\n\x05\x45rror\x12\x12\n\x0eInvalidMapData\x10\x01\"\r\n\x0bRequestPing\"b\n\x0cResponsePing\x12\x14\n\x0cgame_version\x18\x01 \x01(\t\x12\x14\n\x0c\x64\x61ta_version\x18\x02 \x01(\t\x12\x12\n\ndata_build\x18\x03 \x01(\r\x12\x12\n\nbase_build\x18\x04 \x01(\r\";\n\x0cRequestDebug\x12+\n\x05\x64\x65\x62ug\x18\x01 \x03(\x0b\x32\x1c.SC2APIProtocol.DebugCommand\"\x0f\n\rResponseDebug\"\xcb\x01\n\x0bPlayerSetup\x12(\n\x04type\x18\x01 \x01(\x0e\x32\x1a.SC2APIProtocol.PlayerType\x12\"\n\x04race\x18\x02 \x01(\x0e\x32\x14.SC2APIProtocol.Race\x12.\n\ndifficulty\x18\x03 \x01(\x0e\x32\x1a.SC2APIProtocol.Difficulty\x12\x13\n\x0bplayer_name\x18\x04 \x01(\t\x12)\n\x08\x61i_build\x18\x05 \x01(\x0e\x32\x17.SC2APIProtocol.AIBuild\"\xc3\x01\n\x12SpatialCameraSetup\x12+\n\nresolution\x18\x02 \x01(\x0b\x32\x17.SC2APIProtocol.Size2DI\x12\x33\n\x12minimap_resolution\x18\x03 \x01(\x0b\x32\x17.SC2APIProtocol.Size2DI\x12\r\n\x05width\x18\x01 \x01(\x02\x12\x1d\n\x15\x63rop_to_playable_area\x18\x04 \x01(\x08\x12\x1d\n\x15\x61llow_cheating_layers\x18\x05 \x01(\x08\"\xaf\x02\n\x10InterfaceOptions\x12\x0b\n\x03raw\x18\x01 \x01(\x08\x12\r\n\x05score\x18\x02 \x01(\x08\x12\x39\n\rfeature_layer\x18\x03 \x01(\x0b\x32\".SC2APIProtocol.SpatialCameraSetup\x12\x32\n\x06render\x18\x04 \x01(\x0b\x32\".SC2APIProtocol.SpatialCameraSetup\x12\x14\n\x0cshow_cloaked\x18\x05 \x01(\x08\x12\x1d\n\x15show_burrowed_shadows\x18\t \x01(\x08\x12\x19\n\x11show_placeholders\x18\x08 \x01(\x08\x12\x1d\n\x15raw_affects_selection\x18\x06 \x01(\x08\x12!\n\x19raw_crop_to_playable_area\x18\x07 \x01(\x08\"\x92\x02\n\nPlayerInfo\x12\x11\n\tplayer_id\x18\x01 \x01(\r\x12(\n\x04type\x18\x02 \x01(\x0e\x32\x1a.SC2APIProtocol.PlayerType\x12,\n\x0erace_requested\x18\x03 \x01(\x0e\x32\x14.SC2APIProtocol.Race\x12)\n\x0brace_actual\x18\x04 \x01(\x0e\x32\x14.SC2APIProtocol.Race\x12.\n\ndifficulty\x18\x05 \x01(\x0e\x32\x1a.SC2APIProtocol.Difficulty\x12)\n\x08\x61i_build\x18\x07 \x01(\x0e\x32\x17.SC2APIProtocol.AIBuild\x12\x13\n\x0bplayer_name\x18\x06 \x01(\t\"\xef\x01\n\x0cPlayerCommon\x12\x11\n\tplayer_id\x18\x01 \x01(\r\x12\x10\n\x08minerals\x18\x02 \x01(\r\x12\x0f\n\x07vespene\x18\x03 \x01(\r\x12\x10\n\x08\x66ood_cap\x18\x04 \x01(\r\x12\x11\n\tfood_used\x18\x05 \x01(\r\x12\x11\n\tfood_army\x18\x06 \x01(\r\x12\x14\n\x0c\x66ood_workers\x18\x07 \x01(\r\x12\x19\n\x11idle_worker_count\x18\x08 \x01(\r\x12\x12\n\narmy_count\x18\t \x01(\r\x12\x17\n\x0fwarp_gate_count\x18\n \x01(\r\x12\x13\n\x0blarva_count\x18\x0b \x01(\r\"\xb6\x03\n\x0bObservation\x12\x11\n\tgame_loop\x18\t \x01(\r\x12\x33\n\rplayer_common\x18\x01 \x01(\x0b\x32\x1c.SC2APIProtocol.PlayerCommon\x12%\n\x06\x61lerts\x18\n \x03(\x0e\x32\x15.SC2APIProtocol.Alert\x12\x33\n\tabilities\x18\x03 \x03(\x0b\x32 .SC2APIProtocol.AvailableAbility\x12$\n\x05score\x18\x04 \x01(\x0b\x32\x15.SC2APIProtocol.Score\x12\x30\n\x08raw_data\x18\x05 \x01(\x0b\x32\x1e.SC2APIProtocol.ObservationRaw\x12\x43\n\x12\x66\x65\x61ture_layer_data\x18\x06 \x01(\x0b\x32\'.SC2APIProtocol.ObservationFeatureLayer\x12\x36\n\x0brender_data\x18\x07 \x01(\x0b\x32!.SC2APIProtocol.ObservationRender\x12.\n\x07ui_data\x18\x08 \x01(\x0b\x32\x1d.SC2APIProtocol.ObservationUI\"\x9b\x02\n\x06\x41\x63tion\x12-\n\naction_raw\x18\x01 \x01(\x0b\x32\x19.SC2APIProtocol.ActionRaw\x12;\n\x14\x61\x63tion_feature_layer\x18\x02 \x01(\x0b\x32\x1d.SC2APIProtocol.ActionSpatial\x12\x34\n\raction_render\x18\x03 \x01(\x0b\x32\x1d.SC2APIProtocol.ActionSpatial\x12+\n\taction_ui\x18\x04 \x01(\x0b\x32\x18.SC2APIProtocol.ActionUI\x12/\n\x0b\x61\x63tion_chat\x18\x06 \x01(\x0b\x32\x1a.SC2APIProtocol.ActionChat\x12\x11\n\tgame_loop\x18\x07 \x01(\r\"v\n\nActionChat\x12\x33\n\x07\x63hannel\x18\x01 \x01(\x0e\x32\".SC2APIProtocol.ActionChat.Channel\x12\x0f\n\x07message\x18\x02 \x01(\t\"\"\n\x07\x43hannel\x12\r\n\tBroadcast\x10\x01\x12\x08\n\x04Team\x10\x02\"a\n\x0b\x41\x63tionError\x12\x10\n\x08unit_tag\x18\x01 \x01(\x04\x12\x12\n\nability_id\x18\x02 \x01(\x04\x12,\n\x06result\x18\x03 \x01(\x0e\x32\x1c.SC2APIProtocol.ActionResult\"\xcc\x02\n\x0eObserverAction\x12M\n\x12player_perspective\x18\x01 \x01(\x0b\x32/.SC2APIProtocol.ActionObserverPlayerPerspectiveH\x00\x12?\n\x0b\x63\x61mera_move\x18\x02 \x01(\x0b\x32(.SC2APIProtocol.ActionObserverCameraMoveH\x00\x12P\n\x14\x63\x61mera_follow_player\x18\x03 \x01(\x0b\x32\x30.SC2APIProtocol.ActionObserverCameraFollowPlayerH\x00\x12N\n\x13\x63\x61mera_follow_units\x18\x04 \x01(\x0b\x32/.SC2APIProtocol.ActionObserverCameraFollowUnitsH\x00\x42\x08\n\x06\x61\x63tion\"4\n\x1f\x41\x63tionObserverPlayerPerspective\x12\x11\n\tplayer_id\x18\x01 \x01(\r\"X\n\x18\x41\x63tionObserverCameraMove\x12*\n\tworld_pos\x18\x01 \x01(\x0b\x32\x17.SC2APIProtocol.Point2D\x12\x10\n\x08\x64istance\x18\x02 \x01(\x02\"5\n ActionObserverCameraFollowPlayer\x12\x11\n\tplayer_id\x18\x01 \x01(\r\"4\n\x1f\x41\x63tionObserverCameraFollowUnits\x12\x11\n\tunit_tags\x18\x01 \x03(\x04\"I\n\x0cPlayerResult\x12\x11\n\tplayer_id\x18\x01 \x01(\r\x12&\n\x06result\x18\x02 \x01(\x0e\x32\x16.SC2APIProtocol.Result*c\n\x06Status\x12\x0c\n\x08launched\x10\x01\x12\r\n\tinit_game\x10\x02\x12\x0b\n\x07in_game\x10\x03\x12\r\n\tin_replay\x10\x04\x12\t\n\x05\x65nded\x10\x05\x12\x08\n\x04quit\x10\x06\x12\x0b\n\x07unknown\x10\x63*\x96\x01\n\nDifficulty\x12\x0c\n\x08VeryEasy\x10\x01\x12\x08\n\x04\x45\x61sy\x10\x02\x12\n\n\x06Medium\x10\x03\x12\x0e\n\nMediumHard\x10\x04\x12\x08\n\x04Hard\x10\x05\x12\n\n\x06Harder\x10\x06\x12\x0c\n\x08VeryHard\x10\x07\x12\x0f\n\x0b\x43heatVision\x10\x08\x12\x0e\n\nCheatMoney\x10\t\x12\x0f\n\x0b\x43heatInsane\x10\n*9\n\nPlayerType\x12\x0f\n\x0bParticipant\x10\x01\x12\x0c\n\x08\x43omputer\x10\x02\x12\x0c\n\x08Observer\x10\x03*O\n\x07\x41IBuild\x12\x0f\n\x0bRandomBuild\x10\x01\x12\x08\n\x04Rush\x10\x02\x12\n\n\x06Timing\x10\x03\x12\t\n\x05Power\x10\x04\x12\t\n\x05Macro\x10\x05\x12\x07\n\x03\x41ir\x10\x06*\xdb\x03\n\x05\x41lert\x12\x0e\n\nAlertError\x10\x03\x12\x11\n\rAddOnComplete\x10\x04\x12\x14\n\x10\x42uildingComplete\x10\x05\x12\x17\n\x13\x42uildingUnderAttack\x10\x06\x12\x10\n\x0cLarvaHatched\x10\x07\x12\x11\n\rMergeComplete\x10\x08\x12\x15\n\x11MineralsExhausted\x10\t\x12\x11\n\rMorphComplete\x10\n\x12\x16\n\x12MothershipComplete\x10\x0b\x12\x0f\n\x0bMULEExpired\x10\x0c\x12\x19\n\x15NuclearLaunchDetected\x10\x01\x12\x10\n\x0cNukeComplete\x10\r\x12\x15\n\x11NydusWormDetected\x10\x02\x12\x14\n\x10ResearchComplete\x10\x0e\x12\x0e\n\nTrainError\x10\x0f\x12\x15\n\x11TrainUnitComplete\x10\x10\x12\x17\n\x13TrainWorkerComplete\x10\x11\x12\x1a\n\x16TransformationComplete\x10\x12\x12\x13\n\x0fUnitUnderAttack\x10\x13\x12\x13\n\x0fUpgradeComplete\x10\x14\x12\x14\n\x10VespeneExhausted\x10\x15\x12\x12\n\x0eWarpInComplete\x10\x16*9\n\x06Result\x12\x0b\n\x07Victory\x10\x01\x12\n\n\x06\x44\x65\x66\x65\x61t\x10\x02\x12\x07\n\x03Tie\x10\x03\x12\r\n\tUndecided\x10\x04')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.sc2api_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_STATUS']._serialized_start=10484
+ _globals['_STATUS']._serialized_end=10583
+ _globals['_DIFFICULTY']._serialized_start=10586
+ _globals['_DIFFICULTY']._serialized_end=10736
+ _globals['_PLAYERTYPE']._serialized_start=10738
+ _globals['_PLAYERTYPE']._serialized_end=10795
+ _globals['_AIBUILD']._serialized_start=10797
+ _globals['_AIBUILD']._serialized_end=10876
+ _globals['_ALERT']._serialized_start=10879
+ _globals['_ALERT']._serialized_end=11354
+ _globals['_RESULT']._serialized_start=11356
+ _globals['_RESULT']._serialized_end=11413
+ _globals['_REQUEST']._serialized_start=317
+ _globals['_REQUEST']._serialized_end=1536
+ _globals['_RESPONSE']._serialized_start=1539
+ _globals['_RESPONSE']._serialized_end=2837
+ _globals['_REQUESTCREATEGAME']._serialized_start=2840
+ _globals['_REQUESTCREATEGAME']._serialized_end=3054
+ _globals['_LOCALMAP']._serialized_start=3056
+ _globals['_LOCALMAP']._serialized_end=3102
+ _globals['_RESPONSECREATEGAME']._serialized_start=3105
+ _globals['_RESPONSECREATEGAME']._serialized_end=3389
+ _globals['_RESPONSECREATEGAME_ERROR']._serialized_start=3208
+ _globals['_RESPONSECREATEGAME_ERROR']._serialized_end=3389
+ _globals['_REQUESTJOINGAME']._serialized_start=3392
+ _globals['_REQUESTJOINGAME']._serialized_end=3698
+ _globals['_PORTSET']._serialized_start=3700
+ _globals['_PORTSET']._serialized_end=3747
+ _globals['_RESPONSEJOINGAME']._serialized_start=3750
+ _globals['_RESPONSEJOINGAME']._serialized_end=4136
+ _globals['_RESPONSEJOINGAME_ERROR']._serialized_start=3868
+ _globals['_RESPONSEJOINGAME_ERROR']._serialized_end=4136
+ _globals['_REQUESTRESTARTGAME']._serialized_start=4138
+ _globals['_REQUESTRESTARTGAME']._serialized_end=4158
+ _globals['_RESPONSERESTARTGAME']._serialized_start=4161
+ _globals['_RESPONSERESTARTGAME']._serialized_end=4314
+ _globals['_RESPONSERESTARTGAME_ERROR']._serialized_start=4290
+ _globals['_RESPONSERESTARTGAME_ERROR']._serialized_end=4314
+ _globals['_REQUESTSTARTREPLAY']._serialized_start=4317
+ _globals['_REQUESTSTARTREPLAY']._serialized_end=4552
+ _globals['_RESPONSESTARTREPLAY']._serialized_start=4555
+ _globals['_RESPONSESTARTREPLAY']._serialized_end=4818
+ _globals['_RESPONSESTARTREPLAY_ERROR']._serialized_start=4660
+ _globals['_RESPONSESTARTREPLAY_ERROR']._serialized_end=4818
+ _globals['_REQUESTMAPCOMMAND']._serialized_start=4820
+ _globals['_REQUESTMAPCOMMAND']._serialized_end=4860
+ _globals['_RESPONSEMAPCOMMAND']._serialized_start=4863
+ _globals['_RESPONSEMAPCOMMAND']._serialized_end=4992
+ _globals['_RESPONSEMAPCOMMAND_ERROR']._serialized_start=4965
+ _globals['_RESPONSEMAPCOMMAND_ERROR']._serialized_end=4992
+ _globals['_REQUESTLEAVEGAME']._serialized_start=4994
+ _globals['_REQUESTLEAVEGAME']._serialized_end=5012
+ _globals['_RESPONSELEAVEGAME']._serialized_start=5014
+ _globals['_RESPONSELEAVEGAME']._serialized_end=5033
+ _globals['_REQUESTQUICKSAVE']._serialized_start=5035
+ _globals['_REQUESTQUICKSAVE']._serialized_end=5053
+ _globals['_RESPONSEQUICKSAVE']._serialized_start=5055
+ _globals['_RESPONSEQUICKSAVE']._serialized_end=5074
+ _globals['_REQUESTQUICKLOAD']._serialized_start=5076
+ _globals['_REQUESTQUICKLOAD']._serialized_end=5094
+ _globals['_RESPONSEQUICKLOAD']._serialized_start=5096
+ _globals['_RESPONSEQUICKLOAD']._serialized_end=5115
+ _globals['_REQUESTQUIT']._serialized_start=5117
+ _globals['_REQUESTQUIT']._serialized_end=5130
+ _globals['_RESPONSEQUIT']._serialized_start=5132
+ _globals['_RESPONSEQUIT']._serialized_end=5146
+ _globals['_REQUESTGAMEINFO']._serialized_start=5148
+ _globals['_REQUESTGAMEINFO']._serialized_end=5165
+ _globals['_RESPONSEGAMEINFO']._serialized_start=5168
+ _globals['_RESPONSEGAMEINFO']._serialized_end=5392
+ _globals['_REQUESTOBSERVATION']._serialized_start=5394
+ _globals['_REQUESTOBSERVATION']._serialized_end=5454
+ _globals['_RESPONSEOBSERVATION']._serialized_start=5457
+ _globals['_RESPONSEOBSERVATION']._serialized_end=5718
+ _globals['_CHATRECEIVED']._serialized_start=5720
+ _globals['_CHATRECEIVED']._serialized_end=5770
+ _globals['_REQUESTACTION']._serialized_start=5772
+ _globals['_REQUESTACTION']._serialized_end=5828
+ _globals['_RESPONSEACTION']._serialized_start=5830
+ _globals['_RESPONSEACTION']._serialized_end=5892
+ _globals['_REQUESTOBSERVERACTION']._serialized_start=5894
+ _globals['_REQUESTOBSERVERACTION']._serialized_end=5966
+ _globals['_RESPONSEOBSERVERACTION']._serialized_start=5968
+ _globals['_RESPONSEOBSERVERACTION']._serialized_end=5992
+ _globals['_REQUESTSTEP']._serialized_start=5994
+ _globals['_REQUESTSTEP']._serialized_end=6022
+ _globals['_RESPONSESTEP']._serialized_start=6024
+ _globals['_RESPONSESTEP']._serialized_end=6063
+ _globals['_REQUESTDATA']._serialized_start=6065
+ _globals['_REQUESTDATA']._serialized_end=6176
+ _globals['_RESPONSEDATA']._serialized_start=6179
+ _globals['_RESPONSEDATA']._serialized_end=6419
+ _globals['_REQUESTSAVEREPLAY']._serialized_start=6421
+ _globals['_REQUESTSAVEREPLAY']._serialized_end=6440
+ _globals['_RESPONSESAVEREPLAY']._serialized_start=6442
+ _globals['_RESPONSESAVEREPLAY']._serialized_end=6476
+ _globals['_REQUESTREPLAYINFO']._serialized_start=6478
+ _globals['_REQUESTREPLAYINFO']._serialized_end=6576
+ _globals['_PLAYERINFOEXTRA']._serialized_start=6579
+ _globals['_PLAYERINFOEXTRA']._serialized_end=6738
+ _globals['_RESPONSEREPLAYINFO']._serialized_start=6741
+ _globals['_RESPONSEREPLAYINFO']._serialized_end=7192
+ _globals['_RESPONSEREPLAYINFO_ERROR']._serialized_start=7083
+ _globals['_RESPONSEREPLAYINFO_ERROR']._serialized_end=7192
+ _globals['_REQUESTAVAILABLEMAPS']._serialized_start=7194
+ _globals['_REQUESTAVAILABLEMAPS']._serialized_end=7216
+ _globals['_RESPONSEAVAILABLEMAPS']._serialized_start=7218
+ _globals['_RESPONSEAVAILABLEMAPS']._serialized_end=7295
+ _globals['_REQUESTSAVEMAP']._serialized_start=7297
+ _globals['_REQUESTSAVEMAP']._serialized_end=7349
+ _globals['_RESPONSESAVEMAP']._serialized_start=7351
+ _globals['_RESPONSESAVEMAP']._serialized_end=7451
+ _globals['_RESPONSESAVEMAP_ERROR']._serialized_start=7424
+ _globals['_RESPONSESAVEMAP_ERROR']._serialized_end=7451
+ _globals['_REQUESTPING']._serialized_start=7453
+ _globals['_REQUESTPING']._serialized_end=7466
+ _globals['_RESPONSEPING']._serialized_start=7468
+ _globals['_RESPONSEPING']._serialized_end=7566
+ _globals['_REQUESTDEBUG']._serialized_start=7568
+ _globals['_REQUESTDEBUG']._serialized_end=7627
+ _globals['_RESPONSEDEBUG']._serialized_start=7629
+ _globals['_RESPONSEDEBUG']._serialized_end=7644
+ _globals['_PLAYERSETUP']._serialized_start=7647
+ _globals['_PLAYERSETUP']._serialized_end=7850
+ _globals['_SPATIALCAMERASETUP']._serialized_start=7853
+ _globals['_SPATIALCAMERASETUP']._serialized_end=8048
+ _globals['_INTERFACEOPTIONS']._serialized_start=8051
+ _globals['_INTERFACEOPTIONS']._serialized_end=8354
+ _globals['_PLAYERINFO']._serialized_start=8357
+ _globals['_PLAYERINFO']._serialized_end=8631
+ _globals['_PLAYERCOMMON']._serialized_start=8634
+ _globals['_PLAYERCOMMON']._serialized_end=8873
+ _globals['_OBSERVATION']._serialized_start=8876
+ _globals['_OBSERVATION']._serialized_end=9314
+ _globals['_ACTION']._serialized_start=9317
+ _globals['_ACTION']._serialized_end=9600
+ _globals['_ACTIONCHAT']._serialized_start=9602
+ _globals['_ACTIONCHAT']._serialized_end=9720
+ _globals['_ACTIONCHAT_CHANNEL']._serialized_start=9686
+ _globals['_ACTIONCHAT_CHANNEL']._serialized_end=9720
+ _globals['_ACTIONERROR']._serialized_start=9722
+ _globals['_ACTIONERROR']._serialized_end=9819
+ _globals['_OBSERVERACTION']._serialized_start=9822
+ _globals['_OBSERVERACTION']._serialized_end=10154
+ _globals['_ACTIONOBSERVERPLAYERPERSPECTIVE']._serialized_start=10156
+ _globals['_ACTIONOBSERVERPLAYERPERSPECTIVE']._serialized_end=10208
+ _globals['_ACTIONOBSERVERCAMERAMOVE']._serialized_start=10210
+ _globals['_ACTIONOBSERVERCAMERAMOVE']._serialized_end=10298
+ _globals['_ACTIONOBSERVERCAMERAFOLLOWPLAYER']._serialized_start=10300
+ _globals['_ACTIONOBSERVERCAMERAFOLLOWPLAYER']._serialized_end=10353
+ _globals['_ACTIONOBSERVERCAMERAFOLLOWUNITS']._serialized_start=10355
+ _globals['_ACTIONOBSERVERCAMERAFOLLOWUNITS']._serialized_end=10407
+ _globals['_PLAYERRESULT']._serialized_start=10409
+ _globals['_PLAYERRESULT']._serialized_end=10482
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/proto/score_pb2.py b/worlds/_sc2common/bot/proto/score_pb2.py
new file mode 100644
index 000000000000..904551647549
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/score_pb2.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/score.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/score.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cs2clientprotocol/score.proto\x12\x0eSC2APIProtocol\"\xa8\x01\n\x05Score\x12\x33\n\nscore_type\x18\x06 \x01(\x0e\x32\x1f.SC2APIProtocol.Score.ScoreType\x12\r\n\x05score\x18\x07 \x01(\x05\x12\x33\n\rscore_details\x18\x08 \x01(\x0b\x32\x1c.SC2APIProtocol.ScoreDetails\"&\n\tScoreType\x12\x0e\n\nCurriculum\x10\x01\x12\t\n\x05Melee\x10\x02\"h\n\x14\x43\x61tegoryScoreDetails\x12\x0c\n\x04none\x18\x01 \x01(\x02\x12\x0c\n\x04\x61rmy\x18\x02 \x01(\x02\x12\x0f\n\x07\x65\x63onomy\x18\x03 \x01(\x02\x12\x12\n\ntechnology\x18\x04 \x01(\x02\x12\x0f\n\x07upgrade\x18\x05 \x01(\x02\"B\n\x11VitalScoreDetails\x12\x0c\n\x04life\x18\x01 \x01(\x02\x12\x0f\n\x07shields\x18\x02 \x01(\x02\x12\x0e\n\x06\x65nergy\x18\x03 \x01(\x02\"\x8a\n\n\x0cScoreDetails\x12\x1c\n\x14idle_production_time\x18\x01 \x01(\x02\x12\x18\n\x10idle_worker_time\x18\x02 \x01(\x02\x12\x19\n\x11total_value_units\x18\x03 \x01(\x02\x12\x1e\n\x16total_value_structures\x18\x04 \x01(\x02\x12\x1a\n\x12killed_value_units\x18\x05 \x01(\x02\x12\x1f\n\x17killed_value_structures\x18\x06 \x01(\x02\x12\x1a\n\x12\x63ollected_minerals\x18\x07 \x01(\x02\x12\x19\n\x11\x63ollected_vespene\x18\x08 \x01(\x02\x12 \n\x18\x63ollection_rate_minerals\x18\t \x01(\x02\x12\x1f\n\x17\x63ollection_rate_vespene\x18\n \x01(\x02\x12\x16\n\x0espent_minerals\x18\x0b \x01(\x02\x12\x15\n\rspent_vespene\x18\x0c \x01(\x02\x12\x37\n\tfood_used\x18\r \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12=\n\x0fkilled_minerals\x18\x0e \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12<\n\x0ekilled_vespene\x18\x0f \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12;\n\rlost_minerals\x18\x10 \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12:\n\x0clost_vespene\x18\x11 \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12\x44\n\x16\x66riendly_fire_minerals\x18\x12 \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12\x43\n\x15\x66riendly_fire_vespene\x18\x13 \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12;\n\rused_minerals\x18\x14 \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12:\n\x0cused_vespene\x18\x15 \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12\x41\n\x13total_used_minerals\x18\x16 \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12@\n\x12total_used_vespene\x18\x17 \x01(\x0b\x32$.SC2APIProtocol.CategoryScoreDetails\x12=\n\x12total_damage_dealt\x18\x18 \x01(\x0b\x32!.SC2APIProtocol.VitalScoreDetails\x12=\n\x12total_damage_taken\x18\x19 \x01(\x0b\x32!.SC2APIProtocol.VitalScoreDetails\x12\x37\n\x0ctotal_healed\x18\x1a \x01(\x0b\x32!.SC2APIProtocol.VitalScoreDetails\x12\x13\n\x0b\x63urrent_apm\x18\x1b \x01(\x02\x12\x1d\n\x15\x63urrent_effective_apm\x18\x1c \x01(\x02')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.score_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_SCORE']._serialized_start=49
+ _globals['_SCORE']._serialized_end=217
+ _globals['_SCORE_SCORETYPE']._serialized_start=179
+ _globals['_SCORE_SCORETYPE']._serialized_end=217
+ _globals['_CATEGORYSCOREDETAILS']._serialized_start=219
+ _globals['_CATEGORYSCOREDETAILS']._serialized_end=323
+ _globals['_VITALSCOREDETAILS']._serialized_start=325
+ _globals['_VITALSCOREDETAILS']._serialized_end=391
+ _globals['_SCOREDETAILS']._serialized_start=394
+ _globals['_SCOREDETAILS']._serialized_end=1684
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/proto/spatial_pb2.py b/worlds/_sc2common/bot/proto/spatial_pb2.py
new file mode 100644
index 000000000000..e570ab210fb4
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/spatial_pb2.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/spatial.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/spatial.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from . import common_pb2 as s2clientprotocol_dot_common__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1es2clientprotocol/spatial.proto\x12\x0eSC2APIProtocol\x1a\x1ds2clientprotocol/common.proto\"\x88\x01\n\x17ObservationFeatureLayer\x12.\n\x07renders\x18\x01 \x01(\x0b\x32\x1d.SC2APIProtocol.FeatureLayers\x12=\n\x0fminimap_renders\x18\x02 \x01(\x0b\x32$.SC2APIProtocol.FeatureLayersMinimap\"\x9c\n\n\rFeatureLayers\x12-\n\nheight_map\x18\x01 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x31\n\x0evisibility_map\x18\x02 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12(\n\x05\x63reep\x18\x03 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12(\n\x05power\x18\x04 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12,\n\tplayer_id\x18\x05 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12,\n\tunit_type\x18\x06 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12+\n\x08selected\x18\x07 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x32\n\x0funit_hit_points\x18\x08 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x38\n\x15unit_hit_points_ratio\x18\x11 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12.\n\x0bunit_energy\x18\t \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x34\n\x11unit_energy_ratio\x18\x12 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12/\n\x0cunit_shields\x18\n \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x35\n\x12unit_shields_ratio\x18\x13 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x32\n\x0fplayer_relative\x18\x0b \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x32\n\x0funit_density_aa\x18\x0e \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12/\n\x0cunit_density\x18\x0f \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12*\n\x07\x65\x66\x66\x65\x63ts\x18\x14 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x31\n\x0ehallucinations\x18\x15 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12*\n\x07\x63loaked\x18\x16 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\'\n\x04\x62lip\x18\x17 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12(\n\x05\x62uffs\x18\x18 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x30\n\rbuff_duration\x18\x1a \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12)\n\x06\x61\x63tive\x18\x19 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x31\n\x0e\x62uild_progress\x18\x1b \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12,\n\tbuildable\x18\x1c \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12+\n\x08pathable\x18\x1d \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12.\n\x0bplaceholder\x18\x1e \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\"\x90\x04\n\x14\x46\x65\x61tureLayersMinimap\x12-\n\nheight_map\x18\x01 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x31\n\x0evisibility_map\x18\x02 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12(\n\x05\x63reep\x18\x03 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12)\n\x06\x63\x61mera\x18\x04 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12,\n\tplayer_id\x18\x05 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12\x32\n\x0fplayer_relative\x18\x06 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12+\n\x08selected\x18\x07 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12)\n\x06\x61lerts\x18\t \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12,\n\tbuildable\x18\n \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12+\n\x08pathable\x18\x0b \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12,\n\tunit_type\x18\x08 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\"g\n\x11ObservationRender\x12&\n\x03map\x18\x01 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\x12*\n\x07minimap\x18\x02 \x01(\x0b\x32\x19.SC2APIProtocol.ImageData\"\xbb\x02\n\rActionSpatial\x12@\n\x0cunit_command\x18\x01 \x01(\x0b\x32(.SC2APIProtocol.ActionSpatialUnitCommandH\x00\x12>\n\x0b\x63\x61mera_move\x18\x02 \x01(\x0b\x32\'.SC2APIProtocol.ActionSpatialCameraMoveH\x00\x12O\n\x14unit_selection_point\x18\x03 \x01(\x0b\x32/.SC2APIProtocol.ActionSpatialUnitSelectionPointH\x00\x12M\n\x13unit_selection_rect\x18\x04 \x01(\x0b\x32..SC2APIProtocol.ActionSpatialUnitSelectionRectH\x00\x42\x08\n\x06\x61\x63tion\"\xbe\x01\n\x18\x41\x63tionSpatialUnitCommand\x12\x12\n\nability_id\x18\x01 \x01(\x05\x12\x35\n\x13target_screen_coord\x18\x02 \x01(\x0b\x32\x16.SC2APIProtocol.PointIH\x00\x12\x36\n\x14target_minimap_coord\x18\x03 \x01(\x0b\x32\x16.SC2APIProtocol.PointIH\x00\x12\x15\n\rqueue_command\x18\x04 \x01(\x08\x42\x08\n\x06target\"I\n\x17\x41\x63tionSpatialCameraMove\x12.\n\x0e\x63\x65nter_minimap\x18\x01 \x01(\x0b\x32\x16.SC2APIProtocol.PointI\"\xda\x01\n\x1f\x41\x63tionSpatialUnitSelectionPoint\x12\x36\n\x16selection_screen_coord\x18\x01 \x01(\x0b\x32\x16.SC2APIProtocol.PointI\x12\x42\n\x04type\x18\x02 \x01(\x0e\x32\x34.SC2APIProtocol.ActionSpatialUnitSelectionPoint.Type\";\n\x04Type\x12\n\n\x06Select\x10\x01\x12\n\n\x06Toggle\x10\x02\x12\x0b\n\x07\x41llType\x10\x03\x12\x0e\n\nAddAllType\x10\x04\"s\n\x1e\x41\x63tionSpatialUnitSelectionRect\x12:\n\x16selection_screen_coord\x18\x01 \x03(\x0b\x32\x1a.SC2APIProtocol.RectangleI\x12\x15\n\rselection_add\x18\x02 \x01(\x08')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.spatial_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_OBSERVATIONFEATURELAYER']._serialized_start=82
+ _globals['_OBSERVATIONFEATURELAYER']._serialized_end=218
+ _globals['_FEATURELAYERS']._serialized_start=221
+ _globals['_FEATURELAYERS']._serialized_end=1529
+ _globals['_FEATURELAYERSMINIMAP']._serialized_start=1532
+ _globals['_FEATURELAYERSMINIMAP']._serialized_end=2060
+ _globals['_OBSERVATIONRENDER']._serialized_start=2062
+ _globals['_OBSERVATIONRENDER']._serialized_end=2165
+ _globals['_ACTIONSPATIAL']._serialized_start=2168
+ _globals['_ACTIONSPATIAL']._serialized_end=2483
+ _globals['_ACTIONSPATIALUNITCOMMAND']._serialized_start=2486
+ _globals['_ACTIONSPATIALUNITCOMMAND']._serialized_end=2676
+ _globals['_ACTIONSPATIALCAMERAMOVE']._serialized_start=2678
+ _globals['_ACTIONSPATIALCAMERAMOVE']._serialized_end=2751
+ _globals['_ACTIONSPATIALUNITSELECTIONPOINT']._serialized_start=2754
+ _globals['_ACTIONSPATIALUNITSELECTIONPOINT']._serialized_end=2972
+ _globals['_ACTIONSPATIALUNITSELECTIONPOINT_TYPE']._serialized_start=2913
+ _globals['_ACTIONSPATIALUNITSELECTIONPOINT_TYPE']._serialized_end=2972
+ _globals['_ACTIONSPATIALUNITSELECTIONRECT']._serialized_start=2974
+ _globals['_ACTIONSPATIALUNITSELECTIONRECT']._serialized_end=3089
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/proto/ui_pb2.py b/worlds/_sc2common/bot/proto/ui_pb2.py
new file mode 100644
index 000000000000..c905b77db12f
--- /dev/null
+++ b/worlds/_sc2common/bot/proto/ui_pb2.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: s2clientprotocol/ui.proto
+# Protobuf Python Version: 6.31.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+ _runtime_version.Domain.PUBLIC,
+ 6,
+ 31,
+ 1,
+ '',
+ 's2clientprotocol/ui.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19s2clientprotocol/ui.proto\x12\x0eSC2APIProtocol\"\x86\x02\n\rObservationUI\x12,\n\x06groups\x18\x01 \x03(\x0b\x32\x1c.SC2APIProtocol.ControlGroup\x12-\n\x06single\x18\x02 \x01(\x0b\x32\x1b.SC2APIProtocol.SinglePanelH\x00\x12+\n\x05multi\x18\x03 \x01(\x0b\x32\x1a.SC2APIProtocol.MultiPanelH\x00\x12+\n\x05\x63\x61rgo\x18\x04 \x01(\x0b\x32\x1a.SC2APIProtocol.CargoPanelH\x00\x12\x35\n\nproduction\x18\x05 \x01(\x0b\x32\x1f.SC2APIProtocol.ProductionPanelH\x00\x42\x07\n\x05panel\"T\n\x0c\x43ontrolGroup\x12\x1b\n\x13\x63ontrol_group_index\x18\x01 \x01(\r\x12\x18\n\x10leader_unit_type\x18\x02 \x01(\r\x12\r\n\x05\x63ount\x18\x03 \x01(\r\"\x85\x02\n\x08UnitInfo\x12\x11\n\tunit_type\x18\x01 \x01(\r\x12\x17\n\x0fplayer_relative\x18\x02 \x01(\r\x12\x0e\n\x06health\x18\x03 \x01(\x05\x12\x0f\n\x07shields\x18\x04 \x01(\x05\x12\x0e\n\x06\x65nergy\x18\x05 \x01(\x05\x12\x1d\n\x15transport_slots_taken\x18\x06 \x01(\x05\x12\x16\n\x0e\x62uild_progress\x18\x07 \x01(\x02\x12(\n\x06\x61\x64\x64_on\x18\x08 \x01(\x0b\x32\x18.SC2APIProtocol.UnitInfo\x12\x12\n\nmax_health\x18\t \x01(\x05\x12\x13\n\x0bmax_shields\x18\n \x01(\x05\x12\x12\n\nmax_energy\x18\x0b \x01(\x05\"\x9d\x01\n\x0bSinglePanel\x12&\n\x04unit\x18\x01 \x01(\x0b\x32\x18.SC2APIProtocol.UnitInfo\x12\x1c\n\x14\x61ttack_upgrade_level\x18\x02 \x01(\x05\x12\x1b\n\x13\x61rmor_upgrade_level\x18\x03 \x01(\x05\x12\x1c\n\x14shield_upgrade_level\x18\x04 \x01(\x05\x12\r\n\x05\x62uffs\x18\x05 \x03(\x05\"5\n\nMultiPanel\x12\'\n\x05units\x18\x01 \x03(\x0b\x32\x18.SC2APIProtocol.UnitInfo\"{\n\nCargoPanel\x12&\n\x04unit\x18\x01 \x01(\x0b\x32\x18.SC2APIProtocol.UnitInfo\x12,\n\npassengers\x18\x02 \x03(\x0b\x32\x18.SC2APIProtocol.UnitInfo\x12\x17\n\x0fslots_available\x18\x03 \x01(\x05\"7\n\tBuildItem\x12\x12\n\nability_id\x18\x01 \x01(\r\x12\x16\n\x0e\x62uild_progress\x18\x02 \x01(\x02\"\x9d\x01\n\x0fProductionPanel\x12&\n\x04unit\x18\x01 \x01(\x0b\x32\x18.SC2APIProtocol.UnitInfo\x12-\n\x0b\x62uild_queue\x18\x02 \x03(\x0b\x32\x18.SC2APIProtocol.UnitInfo\x12\x33\n\x10production_queue\x18\x03 \x03(\x0b\x32\x19.SC2APIProtocol.BuildItem\"\xda\x04\n\x08\x41\x63tionUI\x12;\n\rcontrol_group\x18\x01 \x01(\x0b\x32\".SC2APIProtocol.ActionControlGroupH\x00\x12\x37\n\x0bselect_army\x18\x02 \x01(\x0b\x32 .SC2APIProtocol.ActionSelectArmyH\x00\x12\x42\n\x11select_warp_gates\x18\x03 \x01(\x0b\x32%.SC2APIProtocol.ActionSelectWarpGatesH\x00\x12\x39\n\x0cselect_larva\x18\x04 \x01(\x0b\x32!.SC2APIProtocol.ActionSelectLarvaH\x00\x12\x44\n\x12select_idle_worker\x18\x05 \x01(\x0b\x32&.SC2APIProtocol.ActionSelectIdleWorkerH\x00\x12\x37\n\x0bmulti_panel\x18\x06 \x01(\x0b\x32 .SC2APIProtocol.ActionMultiPanelH\x00\x12=\n\x0b\x63\x61rgo_panel\x18\x07 \x01(\x0b\x32&.SC2APIProtocol.ActionCargoPanelUnloadH\x00\x12P\n\x10production_panel\x18\x08 \x01(\x0b\x32\x34.SC2APIProtocol.ActionProductionPanelRemoveFromQueueH\x00\x12?\n\x0ftoggle_autocast\x18\t \x01(\x0b\x32$.SC2APIProtocol.ActionToggleAutocastH\x00\x42\x08\n\x06\x61\x63tion\"\xd4\x01\n\x12\x41\x63tionControlGroup\x12\x45\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x35.SC2APIProtocol.ActionControlGroup.ControlGroupAction\x12\x1b\n\x13\x63ontrol_group_index\x18\x02 \x01(\r\"Z\n\x12\x43ontrolGroupAction\x12\n\n\x06Recall\x10\x01\x12\x07\n\x03Set\x10\x02\x12\n\n\x06\x41ppend\x10\x03\x12\x0f\n\x0bSetAndSteal\x10\x04\x12\x12\n\x0e\x41ppendAndSteal\x10\x05\")\n\x10\x41\x63tionSelectArmy\x12\x15\n\rselection_add\x18\x01 \x01(\x08\".\n\x15\x41\x63tionSelectWarpGates\x12\x15\n\rselection_add\x18\x01 \x01(\x08\"\x13\n\x11\x41\x63tionSelectLarva\"\x82\x01\n\x16\x41\x63tionSelectIdleWorker\x12\x39\n\x04type\x18\x01 \x01(\x0e\x32+.SC2APIProtocol.ActionSelectIdleWorker.Type\"-\n\x04Type\x12\x07\n\x03Set\x10\x01\x12\x07\n\x03\x41\x64\x64\x10\x02\x12\x07\n\x03\x41ll\x10\x03\x12\n\n\x06\x41\x64\x64\x41ll\x10\x04\"\xb3\x01\n\x10\x41\x63tionMultiPanel\x12\x33\n\x04type\x18\x01 \x01(\x0e\x32%.SC2APIProtocol.ActionMultiPanel.Type\x12\x12\n\nunit_index\x18\x02 \x01(\x05\"V\n\x04Type\x12\x10\n\x0cSingleSelect\x10\x01\x12\x10\n\x0c\x44\x65selectUnit\x10\x02\x12\x13\n\x0fSelectAllOfType\x10\x03\x12\x15\n\x11\x44\x65selectAllOfType\x10\x04\",\n\x16\x41\x63tionCargoPanelUnload\x12\x12\n\nunit_index\x18\x01 \x01(\x05\":\n$ActionProductionPanelRemoveFromQueue\x12\x12\n\nunit_index\x18\x01 \x01(\x05\"*\n\x14\x41\x63tionToggleAutocast\x12\x12\n\nability_id\x18\x01 \x01(\x05')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 's2clientprotocol.ui_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_OBSERVATIONUI']._serialized_start=46
+ _globals['_OBSERVATIONUI']._serialized_end=308
+ _globals['_CONTROLGROUP']._serialized_start=310
+ _globals['_CONTROLGROUP']._serialized_end=394
+ _globals['_UNITINFO']._serialized_start=397
+ _globals['_UNITINFO']._serialized_end=658
+ _globals['_SINGLEPANEL']._serialized_start=661
+ _globals['_SINGLEPANEL']._serialized_end=818
+ _globals['_MULTIPANEL']._serialized_start=820
+ _globals['_MULTIPANEL']._serialized_end=873
+ _globals['_CARGOPANEL']._serialized_start=875
+ _globals['_CARGOPANEL']._serialized_end=998
+ _globals['_BUILDITEM']._serialized_start=1000
+ _globals['_BUILDITEM']._serialized_end=1055
+ _globals['_PRODUCTIONPANEL']._serialized_start=1058
+ _globals['_PRODUCTIONPANEL']._serialized_end=1215
+ _globals['_ACTIONUI']._serialized_start=1218
+ _globals['_ACTIONUI']._serialized_end=1820
+ _globals['_ACTIONCONTROLGROUP']._serialized_start=1823
+ _globals['_ACTIONCONTROLGROUP']._serialized_end=2035
+ _globals['_ACTIONCONTROLGROUP_CONTROLGROUPACTION']._serialized_start=1945
+ _globals['_ACTIONCONTROLGROUP_CONTROLGROUPACTION']._serialized_end=2035
+ _globals['_ACTIONSELECTARMY']._serialized_start=2037
+ _globals['_ACTIONSELECTARMY']._serialized_end=2078
+ _globals['_ACTIONSELECTWARPGATES']._serialized_start=2080
+ _globals['_ACTIONSELECTWARPGATES']._serialized_end=2126
+ _globals['_ACTIONSELECTLARVA']._serialized_start=2128
+ _globals['_ACTIONSELECTLARVA']._serialized_end=2147
+ _globals['_ACTIONSELECTIDLEWORKER']._serialized_start=2150
+ _globals['_ACTIONSELECTIDLEWORKER']._serialized_end=2280
+ _globals['_ACTIONSELECTIDLEWORKER_TYPE']._serialized_start=2235
+ _globals['_ACTIONSELECTIDLEWORKER_TYPE']._serialized_end=2280
+ _globals['_ACTIONMULTIPANEL']._serialized_start=2283
+ _globals['_ACTIONMULTIPANEL']._serialized_end=2462
+ _globals['_ACTIONMULTIPANEL_TYPE']._serialized_start=2376
+ _globals['_ACTIONMULTIPANEL_TYPE']._serialized_end=2462
+ _globals['_ACTIONCARGOPANELUNLOAD']._serialized_start=2464
+ _globals['_ACTIONCARGOPANELUNLOAD']._serialized_end=2508
+ _globals['_ACTIONPRODUCTIONPANELREMOVEFROMQUEUE']._serialized_start=2510
+ _globals['_ACTIONPRODUCTIONPANELREMOVEFROMQUEUE']._serialized_end=2568
+ _globals['_ACTIONTOGGLEAUTOCAST']._serialized_start=2570
+ _globals['_ACTIONTOGGLEAUTOCAST']._serialized_end=2612
+# @@protoc_insertion_point(module_scope)
diff --git a/worlds/_sc2common/bot/protocol.py b/worlds/_sc2common/bot/protocol.py
index d2c48facb57d..46a7d59adcc1 100644
--- a/worlds/_sc2common/bot/protocol.py
+++ b/worlds/_sc2common/bot/protocol.py
@@ -4,7 +4,7 @@
from aiohttp import ClientWebSocketResponse
from worlds._sc2common.bot import logger
-from s2clientprotocol import sc2api_pb2 as sc_pb
+from .proto import sc2api_pb2 as sc_pb
from .data import Status
diff --git a/worlds/_sc2common/bot/proxy.py b/worlds/_sc2common/bot/proxy.py
index fa9f8537af56..0e389fbe1729 100644
--- a/worlds/_sc2common/bot/proxy.py
+++ b/worlds/_sc2common/bot/proxy.py
@@ -8,7 +8,7 @@
from aiohttp import WSMsgType, web
from worlds._sc2common.bot import logger
-from s2clientprotocol import sc2api_pb2 as sc_pb
+from .proto import sc2api_pb2 as sc_pb
from .controller import Controller
from .data import Result, Status
diff --git a/worlds/_sc2common/bot/renderer.py b/worlds/_sc2common/bot/renderer.py
index 2edc75d8441c..2211fcb10cf2 100644
--- a/worlds/_sc2common/bot/renderer.py
+++ b/worlds/_sc2common/bot/renderer.py
@@ -1,6 +1,6 @@
import datetime
-from s2clientprotocol import score_pb2 as score_pb
+from .proto import score_pb2 as score_pb
from .position import Point2
diff --git a/worlds/_sc2common/requirements.txt b/worlds/_sc2common/requirements.txt
index 1ec405a4ab52..e8499aa7dbf2 100644
--- a/worlds/_sc2common/requirements.txt
+++ b/worlds/_sc2common/requirements.txt
@@ -1,6 +1,5 @@
-s2clientprotocol>=5.0.11.90136.0
mpyq>=0.2.5
portpicker>=1.5.2
aiohttp>=3.8.4
loguru>=0.7.0
-protobuf==3.20.3
+protobuf==6.31.1
diff --git a/worlds/adventure/Rom.py b/worlds/adventure/Rom.py
index cb104c56d867..e603ed06bc0a 100644
--- a/worlds/adventure/Rom.py
+++ b/worlds/adventure/Rom.py
@@ -8,7 +8,7 @@
import Utils
from settings import get_settings
-from worlds.Files import APPatch, AutoPatchRegister
+from worlds.Files import APPatch
from .Locations import LocationData
ADVENTUREHASH: str = "157bddb7192754a45372be196797f284"
@@ -78,7 +78,7 @@ def get_dict(self):
return ret_dict
-class AdventureDeltaPatch(APPatch, metaclass=AutoPatchRegister):
+class AdventureDeltaPatch(APPatch):
hash = ADVENTUREHASH
game = "Adventure"
patch_file_ending = ".apadvn"
diff --git a/worlds/ahit/Client.py b/worlds/ahit/Client.py
index 64c1124fa853..6a65869da4e9 100644
--- a/worlds/ahit/Client.py
+++ b/worlds/ahit/Client.py
@@ -1,4 +1,6 @@
import asyncio
+import time
+
import Utils
import websockets
import functools
@@ -208,6 +210,9 @@ async def proxy(websocket, path: str = "/", ctx: AHITContext = None):
if not ctx.is_proxy_connected():
break
+ if msg["cmd"] == "Bounce" and msg.get("tags") == ["DeathLink"] and "data" in msg:
+ msg["data"]["time"] = time.time()
+
await ctx.send_msgs([msg])
except Exception as e:
diff --git a/worlds/ahit/Regions.py b/worlds/ahit/Regions.py
index 857c04f1d7fc..4c6c24be511f 100644
--- a/worlds/ahit/Regions.py
+++ b/worlds/ahit/Regions.py
@@ -243,7 +243,7 @@
"Time Rift - Mafia of Cooks",
"Time Rift - Dead Bird Studio",
"Time Rift - Sleepy Subcon",
- "Time Rift - Alpine Skyline"
+ "Time Rift - Alpine Skyline",
"Time Rift - Tour",
"Time Rift - Rumbi Factory",
]
diff --git a/worlds/alttp/Dungeons.py b/worlds/alttp/Dungeons.py
index 6b7da695934f..04d107f1ff1f 100644
--- a/worlds/alttp/Dungeons.py
+++ b/worlds/alttp/Dungeons.py
@@ -239,7 +239,7 @@ def fill_dungeons_restrictive(multiworld: MultiWorld):
multiworld.worlds[item.player].collect(all_state_base, item)
pre_fill_items = []
for player in in_dungeon_player_ids:
- pre_fill_items += multiworld.worlds[player].get_pre_fill_items()
+ pre_fill_items += [item for item in multiworld.worlds[player].get_pre_fill_items() if not item.crystal]
for item in in_dungeon_items:
try:
pre_fill_items.remove(item)
diff --git a/worlds/alttp/ItemPool.py b/worlds/alttp/ItemPool.py
index 53059c64bc00..6b0968f6e598 100644
--- a/worlds/alttp/ItemPool.py
+++ b/worlds/alttp/ItemPool.py
@@ -2,7 +2,7 @@
import logging
from BaseClasses import ItemClassification
-from Fill import FillError
+from Options import OptionError
from .SubClasses import ALttPLocation, LTTPRegion, LTTPRegionType
from .Shops import TakeAny, total_shop_slots, set_up_shops, shop_table_by_location, ShopType
@@ -263,7 +263,6 @@ def generate_itempool(world):
('Frog', 'Get Frog'),
('Missing Smith', 'Return Smith'),
('Floodgate', 'Open Floodgate'),
- ('Agahnim 1', 'Beat Agahnim 1'),
('Flute Activation Spot', 'Activated Flute'),
('Capacity Upgrade Shop', 'Capacity Upgrade Shop')
]
@@ -410,15 +409,16 @@ def generate_itempool(world):
pool_count = len(items)
new_items = ["Triforce Piece" for _ in range(additional_triforce_pieces)]
if world.options.shuffle_capacity_upgrades or world.options.bombless_start:
- progressive = world.options.progressive
- progressive = multiworld.random.choice([True, False]) if progressive == 'grouped_random' else progressive == 'on'
+ progressive = world.options.progressive.want_progressives(world.random)
if world.options.shuffle_capacity_upgrades == "on_combined":
new_items.append("Bomb Upgrade (50)")
elif world.options.shuffle_capacity_upgrades == "on":
new_items += ["Bomb Upgrade (+5)"] * 6
new_items.append("Bomb Upgrade (+5)" if progressive else "Bomb Upgrade (+10)")
- if world.options.shuffle_capacity_upgrades != "on_combined" and world.options.bombless_start:
- new_items.append("Bomb Upgrade (+5)" if progressive else "Bomb Upgrade (+10)")
+ if world.options.bombless_start:
+ new_items.append("Bomb Upgrade (+5)" if progressive else "Bomb Upgrade (+10)")
+ elif world.options.bombless_start:
+ new_items.append("Bomb Upgrade (+10)")
if world.options.shuffle_capacity_upgrades and not world.options.retro_bow:
if world.options.shuffle_capacity_upgrades == "on_combined":
@@ -466,6 +466,9 @@ def cut_item(items, item_to_cut, minimum_items):
items_were_cut = items_were_cut or cut_item(items, *reduce_item)
elif len(reduce_item) == 4:
items_were_cut = items_were_cut or condense_items(items, *reduce_item)
+ if reduce_item[0] == "Piece of Heart" and world.logical_heart_pieces:
+ world.logical_heart_pieces -= reduce_item[2]
+ world.logical_heart_containers += reduce_item[3]
elif len(reduce_item) == 1: # Bottles
bottles = [item for item in items if item.name in item_name_groups["Bottles"]]
if len(bottles) > 4:
@@ -476,7 +479,7 @@ def cut_item(items, item_to_cut, minimum_items):
if items_were_cut:
break
else:
- raise Exception(f"Failed to limit item pool size for player {player}")
+ raise OptionError(f"Failed to limit item pool size for player {player}")
if len(items) < pool_count:
items += removed_filler[len(items) - pool_count:]
diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py
index 6a5792d21a3f..2f62b3768238 100644
--- a/worlds/alttp/Rom.py
+++ b/worlds/alttp/Rom.py
@@ -183,7 +183,7 @@ def check_enemizer(enemizercli):
if getattr(check_enemizer, "done", None):
return
if not os.path.exists(enemizercli) and not os.path.exists(enemizercli + ".exe"):
- raise Exception(f"Enemizer not found at {enemizercli}, please install it."
+ raise Exception(f"Enemizer not found at {enemizercli}, please install it. "
f"Such as https://github.com/Ijwu/Enemizer/releases")
with check_lock:
@@ -1197,8 +1197,8 @@ def chunk(l, n):
0x51, 0x06, 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade
0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade
0x58, 0x01, 0x36 if local_world.options.retro_bow else 0x43, 0xFF, # silver arrows -> single arrow (red 20 in retro mode)
- 0x3E, difficulty.boss_heart_container_limit, 0x47, 0xff, # boss heart -> green 20
- 0x17, difficulty.heart_piece_limit, 0x47, 0xff, # piece of heart -> green 20
+ 0x3E, local_world.logical_heart_containers, 0x47, 0xff, # boss heart -> green 20
+ 0x17, local_world.logical_heart_pieces, 0x47, 0xff, # piece of heart -> green 20
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
])
diff --git a/worlds/alttp/Rules.py b/worlds/alttp/Rules.py
index a5b14e0c2da6..18e2965d8c5a 100644
--- a/worlds/alttp/Rules.py
+++ b/worlds/alttp/Rules.py
@@ -20,7 +20,7 @@
has_fire_source, has_hearts, has_melee_weapon,
has_misery_mire_medallion, has_sword, has_turtle_rock_medallion,
has_triforce_pieces, can_use_bombs, can_bomb_or_bonk,
- can_activate_crystal_switch)
+ can_activate_crystal_switch, can_kill_standard_start)
from .UnderworldGlitchRules import underworld_glitches_rules
@@ -1093,22 +1093,23 @@ def standard_rules(world, player):
if world.worlds[player].options.small_key_shuffle != small_key_shuffle.option_universal:
set_rule(world.get_location('Hyrule Castle - Boomerang Guard Key Drop', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 1)
- and can_kill_most_things(state, player, 2))
+ and can_kill_standard_start(state, player, 2))
set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 1)
- and can_kill_most_things(state, player, 1))
-
+ and can_kill_standard_start(state, player, 1))
+ set_rule(world.get_location('Hyrule Castle - Map Guard Key Drop', player),
+ lambda state: can_kill_standard_start(state, player, 1))
set_rule(world.get_location('Hyrule Castle - Big Key Drop', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 2))
set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 2)
and state.has('Big Key (Hyrule Castle)', player)
and (world.worlds[player].options.enemy_health in ("easy", "default")
- or can_kill_most_things(state, player, 1)))
+ or can_kill_standard_start(state, player, 1)))
set_rule(world.get_location('Sewers - Key Rat Key Drop', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 3)
- and can_kill_most_things(state, player, 1))
+ and can_kill_standard_start(state, player, 1))
else:
set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest', player),
lambda state: state.has('Big Key (Hyrule Castle)', player))
diff --git a/worlds/alttp/StateHelpers.py b/worlds/alttp/StateHelpers.py
index 98409c8a8d3c..2c1b5e2ab848 100644
--- a/worlds/alttp/StateHelpers.py
+++ b/worlds/alttp/StateHelpers.py
@@ -59,10 +59,11 @@ def has_hearts(state: CollectionState, player: int, count: int) -> int:
def heart_count(state: CollectionState, player: int) -> int:
# Warning: This only considers items that are marked as advancement items
- diff = state.multiworld.worlds[player].difficulty_requirements
- return min(state.count('Boss Heart Container', player), diff.boss_heart_container_limit) \
+ max_heart_pieces = state.multiworld.worlds[player].logical_heart_pieces
+ max_heart_containers = state.multiworld.worlds[player].logical_heart_containers
+ return min(state.count('Boss Heart Container', player), max_heart_containers) \
+ state.count('Sanctuary Heart Container', player) \
- + min(state.count('Piece of Heart', player), diff.heart_piece_limit) // 4 \
+ + min(state.count('Piece of Heart', player), max_heart_pieces) // 4 \
+ 3 # starting hearts
@@ -139,6 +140,16 @@ def can_kill_most_things(state: CollectionState, player: int, enemies: int = 5)
and can_use_bombs(state, player, enemies * 4)))
+def can_kill_standard_start(state: CollectionState, player: int, enemies: int = 5) -> bool:
+ # Enemizer does not randomize standard start enemies
+ return (has_melee_weapon(state, player)
+ or state.has('Cane of Somaria', player)
+ or (state.has('Cane of Byrna', player) and (enemies < 6 or can_extend_magic(state, player)))
+ or state.has_any(["Bow", "Progressive Bow"], player)
+ or state.has('Fire Rod', player)
+ or can_use_bombs(state, player, enemies)) # Escape assist is set
+
+
def can_get_good_bee(state: CollectionState, player: int) -> bool:
cave = state.multiworld.get_region('Good Bee Cave', player)
return (
diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py
index 4ee5b9d26640..2b99162837da 100644
--- a/worlds/alttp/__init__.py
+++ b/worlds/alttp/__init__.py
@@ -305,6 +305,8 @@ def __init__(self, *args, **kwargs):
self.required_medallions = ["Ether", "Quake"]
self.escape_assist = []
self.shops = []
+ self.logical_heart_containers = 10
+ self.logical_heart_pieces = 24
super(ALTTPWorld, self).__init__(*args, **kwargs)
@classmethod
@@ -384,6 +386,8 @@ def generate_early(self):
self.options.local_items.value |= self.dungeon_local_item_names
self.difficulty_requirements = difficulties[self.options.item_pool.current_key]
+ self.logical_heart_pieces = self.difficulty_requirements.heart_piece_limit
+ self.logical_heart_containers = self.difficulty_requirements.boss_heart_container_limit
# enforce pre-defined local items.
if self.options.goal in ["local_triforce_hunt", "local_ganon_triforce_hunt"]:
diff --git a/worlds/alttp/docs/multiworld_en.md b/worlds/alttp/docs/multiworld_en.md
index 5d7fc43e310f..be0310f911cc 100644
--- a/worlds/alttp/docs/multiworld_en.md
+++ b/worlds/alttp/docs/multiworld_en.md
@@ -88,9 +88,8 @@ You only have to do these steps once.
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
-
+ Network Command Port at 55355. \
+ 
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
diff --git a/worlds/alttp/docs/multiworld_es.md b/worlds/alttp/docs/multiworld_es.md
index eade0372ec6e..5cad0660842c 100644
--- a/worlds/alttp/docs/multiworld_es.md
+++ b/worlds/alttp/docs/multiworld_es.md
@@ -88,9 +88,8 @@ Sólo hay que seguir estos pasos una vez.
1. Comienza en la pantalla del menú principal de RetroArch.
2. Ve a Ajustes --> Interfaz de usario. Configura "Mostrar ajustes avanzados" en ON.
3. Ve a Ajustes --> Red. Pon "Comandos de red" en ON. (Se encuentra bajo Request Device 16.) Deja en 55355 el valor por defecto,
- el Puerto de comandos de red.
-
-
+ el Puerto de comandos de red. \
+ 
4. Ve a Menú principal --> Actualizador en línea --> Descargador de núcleos. Desplázate y selecciona "Nintendo - SNES /
SFC (bsnes-mercury Performance)".
diff --git a/worlds/alttp/docs/multiworld_fr.md b/worlds/alttp/docs/multiworld_fr.md
index 0638d843e810..d73638f9e7b4 100644
--- a/worlds/alttp/docs/multiworld_fr.md
+++ b/worlds/alttp/docs/multiworld_fr.md
@@ -89,9 +89,8 @@ Vous n'avez qu'à faire ces étapes qu'une fois.
1. Entrez dans le menu principal RetroArch
2. Allez dans Réglages --> Interface utilisateur. Mettez "Afficher les réglages avancés" sur ON.
3. Allez dans Réglages --> Réseau. Mettez "Commandes Réseau" sur ON. (trouvé sous Request Device 16.) Laissez le
-Port des commandes réseau à 555355.
-
-
+ Port des commandes réseau à 555355. \
+ 
4. Allez dans Menu Principal --> Mise à jour en ligne --> Téléchargement de cœurs. Descendez jusqu'a"Nintendo - SNES / SFC (bsnes-mercury Performance)" et
sélectionnez le.
diff --git a/worlds/alttp/docs/retroarch-network-commands-en.png b/worlds/alttp/docs/retroarch-network-commands-en.png
deleted file mode 100644
index fd97dfa097cd..000000000000
Binary files a/worlds/alttp/docs/retroarch-network-commands-en.png and /dev/null differ
diff --git a/worlds/alttp/docs/retroarch-network-commands-fr.png b/worlds/alttp/docs/retroarch-network-commands-fr.png
deleted file mode 100644
index 60eba5b1b0fb..000000000000
Binary files a/worlds/alttp/docs/retroarch-network-commands-fr.png and /dev/null differ
diff --git a/worlds/apquest/!READ_FIRST!.txt b/worlds/apquest/!READ_FIRST!.txt
new file mode 100644
index 000000000000..ff3d5fe51060
--- /dev/null
+++ b/worlds/apquest/!READ_FIRST!.txt
@@ -0,0 +1,46 @@
+This apworld is meant as a learning tool for new apworld devs.
+It is a completely standalone resource, but there will be links to additional resources when appropriate.
+
+#################
+# Prerequisites #
+#################
+
+APQuest will only explain how to write the generation-side code for your game, not how to write a client or mod for it.
+For a more zoomed out view of how to add a game to Archipelago, you can read this document:
+https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/adding%20games.md
+
+APQuest assumes you already vaguely know what an apworld is.
+If you don't know, read this first:
+https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/apworld%20specification.md
+
+To write an apworld, you need to be running Archipelago from source (Python) instead of using e.g. the .exe build.
+Here's an explanation for how to do that.
+https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/running%20from%20source.md
+
+#######################
+# How to read APQuest #
+#######################
+
+You'll want to start with __init__.py, then move to world.py.
+If you also want to learn how to write unit tests, go to test/__init__.py.
+
+You can ignore the game/ folder, it contains the actual game code, graphics and music.
+
+The client/ folder is NOT meant for teaching.
+While the client was written to the best of its author's ability, it does not meet the same standard as the world code.
+The client code is also lacking the explanatory comments.
+Copy from it at your own risk.
+
+###################
+# Further reading #
+###################
+
+APQuest is a very simple game, so not every edge case will be covered.
+The world API document goes a lot more in-depth on certain topics:
+https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/world%20api.md
+
+There is also the "APWorld dev FAQ" document with common emergent problems:
+https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/apworld_dev_faq.md
+
+In general, but especially if you want your apworld to be verified by core, you should follow our style guide:
+https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/style.md
diff --git a/worlds/apquest/__init__.py b/worlds/apquest/__init__.py
new file mode 100644
index 000000000000..6ef396ee90ab
--- /dev/null
+++ b/worlds/apquest/__init__.py
@@ -0,0 +1,12 @@
+# The first thing you should make for your world is an archipelago.json manifest file.
+# You can reference APQuest's, but you should change the "game" field (obviously),
+# and you should also change the "minimum_ap_version" - probably to the current value of Utils.__version__.
+
+# Apart from the regular apworld code that allows generating multiworld seeds with your game,
+# your apworld might have other "components" that should be launchable from the Archipelago Launcher.
+# You can ignore this for now. If you are specifically interested in components, you can read components.py.
+from . import components as components
+
+# The main thing we do in our __init__.py is importing our world class from our world.py to initialize it.
+# Obviously, this world class needs to exist first. For this, read world.py.
+from .world import APQuestWorld as APQuestWorld
diff --git a/worlds/apquest/archipelago.json b/worlds/apquest/archipelago.json
new file mode 100644
index 000000000000..6a6c3ddd02c3
--- /dev/null
+++ b/worlds/apquest/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "APQuest",
+ "minimum_ap_version": "0.6.4",
+ "world_version": "1.0.1",
+ "authors": ["NewSoupVi"]
+}
diff --git a/worlds/apquest/client/__init__.py b/worlds/apquest/client/__init__.py
new file mode 100644
index 000000000000..0cd1b86a4986
--- /dev/null
+++ b/worlds/apquest/client/__init__.py
@@ -0,0 +1,5 @@
+# !!! IMPORTANT !!!
+# The client implementation is *not* meant for teaching.
+# Obviously, it is written to the best of its author's abilities,
+# but it is not to the same standard as the rest of the apworld.
+# Copy things from here at your own risk.
diff --git a/worlds/apquest/client/ap_quest_client.kv b/worlds/apquest/client/ap_quest_client.kv
new file mode 100644
index 000000000000..9f4024e62a02
--- /dev/null
+++ b/worlds/apquest/client/ap_quest_client.kv
@@ -0,0 +1,56 @@
+:
+ size_hint: None, None
+ pos_hint: {"center_x": 0.5, "center_y": 0.5}
+ spacing: 0
+ padding: 0
+
+:
+ cols: 12
+ rows: 11
+ spacing: 0
+ padding: 0
+ size_hint: None, None
+ pos_hint: {"center_x": 0.5, "center_y": 0.5}
+
+:
+ RelativeLayout:
+ id: game_container
+
+:
+ Label:
+ markup: True
+ font_size: "20sp"
+ valign: "middle"
+ pos_hint: {"center_x": 0.5, "center_y": 0.5}
+ text:
+ """[b]Controls:[/b]
+
+ WASD or Arrow Keys to move
+ Space to attack or interact
+ C to fire available Confetti Cannons
+ Number Keys + Backspace for Math Trap\n
+
+ Rebinding controls might be added in the future :)"""
+
+:
+ orientation: "horizontal"
+ size_hint: 1, None
+ padding: 0
+ height: 50
+
+ Label:
+ size_hint: None, 1
+ text: "Volume:"
+
+ Slider:
+ id: volume_slider
+ size_hint: 1, 1
+ min: 0
+ max: 100
+ step: 1
+ value: 50
+ orientation: "horizontal"
+
+ Label:
+ size_hint: None, 1
+ text: str(int(volume_slider.value))
diff --git a/worlds/apquest/client/ap_quest_client.py b/worlds/apquest/client/ap_quest_client.py
new file mode 100644
index 000000000000..c1edc8edb4b2
--- /dev/null
+++ b/worlds/apquest/client/ap_quest_client.py
@@ -0,0 +1,290 @@
+import asyncio
+import sys
+from argparse import Namespace
+from enum import Enum
+from typing import TYPE_CHECKING, Any
+
+from CommonClient import CommonContext, gui_enabled, logger, server_loop
+from NetUtils import ClientStatus
+
+from ..game.events import ConfettiFired, LocationClearedEvent, MathProblemSolved, MathProblemStarted, VictoryEvent
+from ..game.game import Game
+from ..game.inputs import Input
+from ..game.items import Item
+from ..game.locations import Location
+from .game_manager import APQuestManager
+from .graphics import PlayerSprite
+from .item_quality import get_quality_for_network_item
+from .sounds import (
+ CONFETTI_CANNON,
+ ITEM_JINGLES,
+ MATH_PROBLEM_SOLVED_JINGLE,
+ MATH_PROBLEM_STARTED_JINGLE,
+ VICTORY_JINGLE,
+)
+
+if TYPE_CHECKING:
+ import kvui
+
+
+# !!! IMPORTANT !!!
+# The client implementation is *not* meant for teaching.
+# Obviously, it is written to the best of its author's abilities,
+# but it is not to the same standard as the rest of the apworld.
+# Copy things from here at your own risk.
+
+
+class ConnectionStatus(Enum):
+ NOT_CONNECTED = 0
+ SCOUTS_NOT_SENT = 1
+ SCOUTS_SENT = 2
+ GAME_RUNNING = 3
+
+
+class APQuestContext(CommonContext):
+ game = "APQuest"
+ items_handling = 0b111 # full remote
+
+ client_loop: asyncio.Task[None]
+
+ last_connected_slot: int | None = None
+
+ slot_data: dict[str, Any]
+
+ ap_quest_game: Game | None = None
+ hard_mode: bool = False
+ hammer: bool = False
+ extra_starting_chest: bool = False
+ player_sprite: PlayerSprite = PlayerSprite.HUMAN
+
+ connection_status: ConnectionStatus = ConnectionStatus.NOT_CONNECTED
+
+ highest_processed_item_index: int = 0
+ queued_locations: list[int]
+
+ delay_intro_song: bool
+
+ ui: APQuestManager
+
+ def __init__(
+ self, server_address: str | None = None, password: str | None = None, delay_intro_song: bool = False
+ ) -> None:
+ super().__init__(server_address, password)
+
+ self.queued_locations = []
+ self.slot_data = {}
+ self.delay_intro_song = delay_intro_song
+
+ async def server_auth(self, password_requested: bool = False) -> None:
+ if password_requested and not self.password:
+ self.ui.allow_intro_song()
+ await super().server_auth(password_requested)
+ await self.get_username()
+ await self.send_connect(game=self.game)
+
+ def handle_connection_loss(self, msg: str) -> None:
+ self.ui.allow_intro_song()
+ super().handle_connection_loss(msg)
+
+ async def connect(self, address: str | None = None) -> None:
+ self.ui.switch_to_regular_tab()
+ await super().connect(address)
+
+ async def apquest_loop(self) -> None:
+ while not self.exit_event.is_set():
+ if self.connection_status != ConnectionStatus.GAME_RUNNING:
+ if self.connection_status == ConnectionStatus.SCOUTS_NOT_SENT:
+ await self.send_msgs([{"cmd": "LocationScouts", "locations": self.server_locations}])
+ self.connection_status = ConnectionStatus.SCOUTS_SENT
+
+ await asyncio.sleep(0.1)
+ continue
+
+ if not self.ap_quest_game or not self.ap_quest_game.gameboard or not self.ap_quest_game.gameboard.ready:
+ await asyncio.sleep(0.1)
+ continue
+
+ try:
+ while self.queued_locations:
+ location = self.queued_locations.pop(0)
+ self.location_checked_side_effects(location)
+ self.locations_checked.add(location)
+ await self.check_locations({location})
+
+ rerender = False
+
+ new_items = self.items_received[self.highest_processed_item_index :]
+ for item in new_items:
+ self.highest_processed_item_index += 1
+ self.ap_quest_game.receive_item(item.item, item.location, item.player)
+ rerender = True
+
+ for new_remotely_cleared_location in self.checked_locations - self.locations_checked:
+ self.ap_quest_game.force_clear_location(new_remotely_cleared_location)
+ rerender = True
+
+ if rerender:
+ self.render()
+
+ if self.ap_quest_game.player.has_won and not self.finished_game:
+ await self.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
+ self.finished_game = True
+ except Exception as e:
+ logger.exception(e)
+
+ await asyncio.sleep(0.1)
+
+ def on_package(self, cmd: str, args: dict[str, Any]) -> None:
+ if cmd == "ConnectionRefused":
+ self.ui.allow_intro_song()
+
+ if cmd == "Connected":
+ if self.connection_status == ConnectionStatus.GAME_RUNNING:
+ # In a connection loss -> auto reconnect scenario, we can seamlessly keep going
+ return
+
+ self.last_connected_slot = self.slot
+
+ self.connection_status = ConnectionStatus.NOT_CONNECTED # for safety, it will get set again later
+
+ self.slot_data = args["slot_data"]
+ self.hard_mode = self.slot_data["hard_mode"]
+ self.hammer = self.slot_data["hammer"]
+ self.extra_starting_chest = self.slot_data["extra_starting_chest"]
+ try:
+ self.player_sprite = PlayerSprite(self.slot_data["player_sprite"])
+ except Exception as e:
+ logger.exception(e)
+ self.player_sprite = PlayerSprite.UNKNOWN
+
+ self.ap_quest_game = Game(self.hard_mode, self.hammer, self.extra_starting_chest)
+ self.highest_processed_item_index = 0
+ self.render()
+
+ self.connection_status = ConnectionStatus.SCOUTS_NOT_SENT
+ if cmd == "LocationInfo":
+ remote_item_graphic_overrides = {
+ Location(location): Item(network_item.item)
+ for location, network_item in self.locations_info.items()
+ if self.slot_info[network_item.player].game == self.game
+ }
+
+ assert self.ap_quest_game is not None
+ self.ap_quest_game.gameboard.fill_remote_location_content(remote_item_graphic_overrides)
+ self.render()
+ self.ui.game_view.bind_keyboard()
+
+ self.connection_status = ConnectionStatus.GAME_RUNNING
+ self.ui.game_started()
+
+ async def disconnect(self, *args: Any, **kwargs: Any) -> None:
+ self.finished_game = False
+ self.locations_checked = set()
+ self.connection_status = ConnectionStatus.NOT_CONNECTED
+ await super().disconnect(*args, **kwargs)
+
+ def render(self) -> None:
+ if self.ap_quest_game is None:
+ raise RuntimeError("Tried to render before self.ap_quest_game was initialized.")
+
+ self.ui.render(self.ap_quest_game, self.player_sprite)
+ self.handle_game_events()
+
+ def location_checked_side_effects(self, location: int) -> None:
+ network_item = self.locations_info[location]
+
+ if network_item.player == self.slot and network_item.item == Item.MATH_TRAP.value:
+ # In case of a local math trap, we only play the math trap trigger jingle
+ return
+
+ item_quality = get_quality_for_network_item(network_item)
+ self.play_jingle(ITEM_JINGLES[item_quality])
+
+ def play_jingle(self, audio_filename: str) -> None:
+ self.ui.play_jingle(audio_filename)
+
+ def handle_game_events(self) -> None:
+ if self.ap_quest_game is None:
+ return
+
+ while self.ap_quest_game.queued_events:
+ event = self.ap_quest_game.queued_events.pop(0)
+
+ if isinstance(event, LocationClearedEvent):
+ self.queued_locations.append(event.location_id)
+ continue
+
+ if isinstance(event, VictoryEvent):
+ self.play_jingle(VICTORY_JINGLE)
+ continue
+
+ if isinstance(event, ConfettiFired):
+ gameboard_x, gameboard_y = self.ap_quest_game.gameboard.size
+ gameboard_x += 1 # vertical item column
+ x = (event.x + 0.5) / gameboard_x
+ y = 1 - (event.y + 0.5) / gameboard_y # Kivy's y is bottom to top (ew)
+
+ self.ui.play_jingle(CONFETTI_CANNON)
+ self.ui.add_confetti((x, y), (self.slot_data["confetti_explosiveness"] + 1) * 5)
+ continue
+
+ if isinstance(event, MathProblemStarted):
+ self.play_jingle(MATH_PROBLEM_STARTED_JINGLE)
+ continue
+
+ if isinstance(event, MathProblemSolved):
+ self.play_jingle(MATH_PROBLEM_SOLVED_JINGLE)
+ continue
+
+ def input_and_rerender(self, input_key: Input) -> None:
+ if self.ap_quest_game is None:
+ return
+ if not self.ap_quest_game.gameboard.ready:
+ return
+ self.ap_quest_game.input(input_key)
+ self.render()
+
+ def make_gui(self) -> "type[kvui.GameManager]":
+ self.load_kv()
+ return APQuestManager
+
+ def load_kv(self) -> None:
+ import pkgutil
+
+ from kivy.lang import Builder
+
+ data = pkgutil.get_data(__name__, "ap_quest_client.kv")
+ if data is None:
+ raise RuntimeError("ap_quest_client.kv could not be loaded.")
+
+ Builder.load_string(data.decode())
+
+
+async def main(args: Namespace) -> None:
+ if not gui_enabled:
+ raise RuntimeError("APQuest cannot be played without gui.")
+
+ # Assume we shouldn't play the intro song in the auto-connect scenario, because the game will instantly start.
+ delay_intro_song = args.connect and args.name
+
+ ctx = APQuestContext(args.connect, args.password, delay_intro_song=delay_intro_song)
+ ctx.auth = args.name
+ ctx.server_task = asyncio.create_task(server_loop(ctx), name="server loop")
+
+ ctx.run_gui()
+ ctx.run_cli()
+
+ ctx.client_loop = asyncio.create_task(ctx.apquest_loop(), name="Client Loop")
+
+ await ctx.exit_event.wait()
+ await ctx.shutdown()
+
+
+def launch(*args: str) -> None:
+ from .launch import launch_ap_quest_client
+
+ launch_ap_quest_client(*args)
+
+
+if __name__ == "__main__":
+ launch(*sys.argv[1:])
diff --git a/worlds/apquest/client/custom_views.py b/worlds/apquest/client/custom_views.py
new file mode 100644
index 000000000000..4c1d28c73250
--- /dev/null
+++ b/worlds/apquest/client/custom_views.py
@@ -0,0 +1,256 @@
+from collections.abc import Callable
+from dataclasses import dataclass
+from math import sqrt
+from random import choice, random
+from typing import Any
+
+from kivy.core.window import Keyboard, Window
+from kivy.graphics import Color, Triangle
+from kivy.graphics.instructions import Canvas
+from kivy.input import MotionEvent
+from kivy.uix.boxlayout import BoxLayout
+from kivy.uix.gridlayout import GridLayout
+from kivymd.uix.recycleview import MDRecycleView
+
+from CommonClient import logger
+
+from ..game.inputs import Input
+
+
+INPUT_MAP = {
+ "up": Input.UP,
+ "w": Input.UP,
+ "down": Input.DOWN,
+ "s": Input.DOWN,
+ "right": Input.RIGHT,
+ "d": Input.RIGHT,
+ "left": Input.LEFT,
+ "a": Input.LEFT,
+ "spacebar": Input.ACTION,
+ "c": Input.CONFETTI,
+ "0": Input.ZERO,
+ "1": Input.ONE,
+ "2": Input.TWO,
+ "3": Input.THREE,
+ "4": Input.FOUR,
+ "5": Input.FIVE,
+ "6": Input.SIX,
+ "7": Input.SEVEN,
+ "8": Input.EIGHT,
+ "9": Input.NINE,
+ "backspace": Input.BACKSPACE,
+}
+
+
+class APQuestGameView(MDRecycleView):
+ _keyboard: Keyboard | None = None
+ input_function: Callable[[Input], None]
+
+ def __init__(self, input_function: Callable[[Input], None], **kwargs: Any) -> None:
+ super().__init__(**kwargs)
+ self.input_function = input_function
+ self.bind_keyboard()
+
+ def on_touch_down(self, touch: MotionEvent) -> None:
+ self.bind_keyboard()
+
+ def bind_keyboard(self) -> None:
+ if self._keyboard is not None:
+ return
+ self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
+ self._keyboard.bind(on_key_down=self._on_keyboard_down)
+
+ def _keyboard_closed(self) -> None:
+ if self._keyboard is None:
+ return
+ self._keyboard.unbind(on_key_down=self._on_keyboard_down)
+ self._keyboard = None
+
+ def _on_keyboard_down(self, _: Any, keycode: tuple[int, str], _1: Any, _2: Any) -> bool:
+ if keycode[1] in INPUT_MAP:
+ self.input_function(INPUT_MAP[keycode[1]])
+ return True
+
+
+class APQuestGrid(GridLayout):
+ def check_resize(self, _: int, _1: int) -> None:
+ parent_width, parent_height = self.parent.size
+
+ self_width_according_to_parent_height = parent_height * 12 / 11
+ self_height_according_to_parent_width = parent_height * 11 / 12
+
+ if self_width_according_to_parent_height > parent_width:
+ self.size = parent_width, self_height_according_to_parent_width
+ else:
+ self.size = self_width_according_to_parent_height, parent_height
+
+
+CONFETTI_COLORS = [
+ (220 / 255, 0, 212 / 255), # PINK
+ (0, 0, 252 / 255), # BLUE
+ (252 / 255, 220 / 255, 0), # YELLOW
+ (0, 184 / 255, 0), # GREEN
+ (252 / 255, 56 / 255, 0), # ORANGE
+]
+
+
+@dataclass
+class Confetti:
+ x_pos: float
+ y_pos: float
+ x_speed: float
+ y_speed: float
+ color: tuple[float, float, float]
+ life: float = 3
+
+ triangle1: Triangle | None = None
+ triangle2: Triangle | None = None
+ color_instruction: Color | None = None
+
+ def update_speed(self, dt: float) -> None:
+ if self.x_speed > 0:
+ self.x_speed -= 2.7 * dt
+ if self.x_speed < 0:
+ self.x_speed = 0
+ else:
+ self.x_speed += 2.7 * dt
+ if self.x_speed > 0:
+ self.x_speed = 0
+
+ if self.y_speed > -0.03:
+ self.y_speed -= 2.7 * dt
+ if self.y_speed < -0.03:
+ self.y_speed = -0.03
+ else:
+ self.y_speed += 2.7 * dt
+ if self.y_speed > -0.03:
+ self.y_speed = -0.03
+
+ def move(self, dt: float) -> None:
+ self.update_speed(dt)
+
+ if self.y_pos > 1:
+ self.y_pos = 1
+ self.y_speed = 0
+ if self.x_pos < 0.01:
+ self.x_pos = 0.01
+ self.x_speed = 0
+ if self.x_pos > 0.99:
+ self.x_pos = 0.99
+ self.x_speed = 0
+
+ self.x_pos += self.x_speed * dt
+ self.y_pos += self.y_speed * dt
+
+ def render(self, offset_x: float, offset_y: float, max_x: int, max_y: int) -> None:
+ if self.x_speed == 0 and self.y_speed == 0:
+ x_normalized, y_normalized = 0.0, 1.0
+ else:
+ speed_magnitude = sqrt(self.x_speed**2 + self.y_speed**2)
+ x_normalized, y_normalized = self.x_speed / speed_magnitude, self.y_speed / speed_magnitude
+
+ half_top_to_bottom = 0.006
+ half_left_to_right = 0.018
+
+ upwards_delta_x = x_normalized * half_top_to_bottom
+ upwards_delta_y = y_normalized * half_top_to_bottom
+ sideways_delta_x = y_normalized * half_left_to_right
+ sideways_delta_y = x_normalized * half_left_to_right
+
+ top_left_x, top_left_y = upwards_delta_x - sideways_delta_x, upwards_delta_y + sideways_delta_y
+ bottom_left_x, bottom_left_y = -upwards_delta_x - sideways_delta_x, -upwards_delta_y + sideways_delta_y
+ top_right_x, top_right_y = -bottom_left_x, -bottom_left_y
+ bottom_right_x, bottom_right_y = -top_left_x, -top_left_y
+
+ top_left_x, top_left_y = top_left_x + self.x_pos, top_left_y + self.y_pos
+ bottom_left_x, bottom_left_y = bottom_left_x + self.x_pos, bottom_left_y + self.y_pos
+ top_right_x, top_right_y = top_right_x + self.x_pos, top_right_y + self.y_pos
+ bottom_right_x, bottom_right_y = bottom_right_x + self.x_pos, bottom_right_y + self.y_pos
+
+ top_left_x, top_left_y = top_left_x * max_x + offset_x, top_left_y * max_y + offset_y
+ bottom_left_x, bottom_left_y = bottom_left_x * max_x + offset_x, bottom_left_y * max_y + offset_y
+ top_right_x, top_right_y = top_right_x * max_x + offset_x, top_right_y * max_y + offset_y
+ bottom_right_x, bottom_right_y = bottom_right_x * max_x + offset_x, bottom_right_y * max_y + offset_y
+
+ points1 = (top_left_x, top_left_y, top_right_x, top_right_y, bottom_left_x, bottom_left_y)
+ points2 = (bottom_right_x, bottom_right_y, top_right_x, top_right_y, bottom_left_x, bottom_left_y)
+
+ if self.color_instruction is None:
+ self.color_instruction = Color(*self.color)
+
+ if self.triangle1 is None:
+ self.triangle1 = Triangle(points=points1)
+ else:
+ self.triangle1.points = points1
+
+ if self.triangle2 is None:
+ self.triangle2 = Triangle(points=points2)
+ else:
+ self.triangle2.points = points2
+
+ def reduce_life(self, dt: float, canvas: Canvas) -> bool:
+ self.life -= dt
+
+ if self.life <= 0:
+ if self.color_instruction is not None:
+ canvas.remove(self.color_instruction)
+ if self.triangle1 is not None:
+ canvas.remove(self.triangle1)
+ if self.triangle2 is not None:
+ canvas.remove(self.triangle2)
+ return False
+
+ return True
+
+
+class ConfettiView(MDRecycleView):
+ confetti: list[Confetti]
+
+ def __init__(self, **kwargs: Any) -> None:
+ super().__init__(**kwargs)
+ self.confetti = []
+
+ def check_resize(self, _: int, _1: int) -> None:
+ parent_width, parent_height = self.parent.size
+
+ self_width_according_to_parent_height = parent_height * 12 / 11
+ self_height_according_to_parent_width = parent_height * 11 / 12
+
+ if self_width_according_to_parent_height > parent_width:
+ self.size = parent_width, self_height_according_to_parent_width
+ else:
+ self.size = self_width_according_to_parent_height, parent_height
+
+ def redraw_confetti(self, dt: float) -> None:
+ try:
+ with self.canvas:
+ for confetti in self.confetti:
+ confetti.move(dt)
+
+ self.confetti = [confetti for confetti in self.confetti if confetti.reduce_life(dt, self.canvas)]
+
+ for confetti in self.confetti:
+ confetti.render(self.pos[0], self.pos[1], self.size[0], self.size[1])
+ except Exception as e:
+ logger.exception(e)
+
+ def add_confetti(self, initial_position: tuple[float, float], amount: int) -> None:
+ for i in range(amount):
+ self.confetti.append(
+ Confetti(
+ initial_position[0],
+ initial_position[1],
+ random() * 3.2 - 1.6 - (initial_position[0] - 0.5) * 1.2,
+ random() * 3.2 - 1.3 - (initial_position[1] - 0.5) * 1.2,
+ choice(CONFETTI_COLORS),
+ 3 + i * 0.05,
+ )
+ )
+
+
+class VolumeSliderView(BoxLayout):
+ pass
+
+
+class APQuestControlsView(BoxLayout):
+ pass
diff --git a/worlds/apquest/client/game_manager.py b/worlds/apquest/client/game_manager.py
new file mode 100644
index 000000000000..86f4316d12a2
--- /dev/null
+++ b/worlds/apquest/client/game_manager.py
@@ -0,0 +1,200 @@
+from __future__ import annotations
+
+# isort: off
+from kvui import GameManager, MDNavigationItemBase
+
+# isort: on
+from typing import TYPE_CHECKING, Any
+
+from kivy.clock import Clock
+from kivy.uix.gridlayout import GridLayout
+from kivy.uix.image import Image
+from kivy.uix.layout import Layout
+from kivymd.uix.recycleview import MDRecycleView
+
+from ..game.game import Game
+from .custom_views import APQuestControlsView, APQuestGameView, APQuestGrid, ConfettiView, VolumeSliderView
+from .graphics import PlayerSprite, get_texture
+from .sounds import SoundManager
+
+if TYPE_CHECKING:
+ from .ap_quest_client import APQuestContext
+
+
+class APQuestManager(GameManager):
+ base_title = "APQuest for AP version"
+ ctx: APQuestContext
+
+ lower_game_grid: GridLayout
+ upper_game_grid: GridLayout
+
+ game_view: MDRecycleView
+ game_view_tab: MDNavigationItemBase
+
+ sound_manager: SoundManager
+
+ bottom_image_grid: list[list[Image]]
+ top_image_grid: list[list[Image]]
+ confetti_view: ConfettiView
+
+ bottom_grid_is_grass: bool
+
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
+ super().__init__(*args, **kwargs)
+ self.sound_manager = SoundManager()
+ self.sound_manager.allow_intro_to_play = not self.ctx.delay_intro_song
+ self.top_image_grid = []
+ self.bottom_image_grid = []
+ self.bottom_grid_is_grass = False
+
+ def allow_intro_song(self) -> None:
+ self.sound_manager.allow_intro_to_play = True
+
+ def add_confetti(self, position: tuple[float, float], amount: int) -> None:
+ self.confetti_view.add_confetti(position, amount)
+
+ def play_jingle(self, audio_filename: str) -> None:
+ self.sound_manager.play_jingle(audio_filename)
+
+ def switch_to_tab(self, desired_tab: MDNavigationItemBase) -> None:
+ if self.screens.current_tab == desired_tab:
+ return
+ self.screens.current_tab.active = False
+ self.screens.switch_screens(desired_tab)
+ desired_tab.active = True
+
+ def switch_to_game_tab(self) -> None:
+ self.switch_to_tab(self.game_view_tab)
+
+ def switch_to_regular_tab(self) -> None:
+ self.switch_to_tab(self.tabs.children[-1])
+
+ def game_started(self) -> None:
+ self.switch_to_game_tab()
+ self.sound_manager.game_started = True
+
+ def render(self, game: Game, player_sprite: PlayerSprite) -> None:
+ self.setup_game_grid_if_not_setup(game.gameboard.size)
+
+ # This calls game.render(), which needs to happen to update the state of math traps
+ self.render_gameboard(game, player_sprite)
+ # Only now can we check whether a math problem is active
+ self.render_background_game_grid(game.gameboard.size, game.active_math_problem is None)
+ self.sound_manager.math_trap_active = game.active_math_problem is not None
+
+ self.render_item_column(game)
+
+ def render_gameboard(self, game: Game, player_sprite: PlayerSprite) -> None:
+ rendered_gameboard = game.render()
+
+ for gameboard_row, image_row in zip(rendered_gameboard, self.top_image_grid, strict=False):
+ for graphic, image in zip(gameboard_row, image_row[:11], strict=False):
+ texture = get_texture(graphic, player_sprite)
+
+ if texture is None:
+ image.opacity = 0
+ image.texture = None
+ continue
+
+ image.texture = texture
+ image.opacity = 1
+
+ def render_item_column(self, game: Game) -> None:
+ rendered_item_column = game.render_health_and_inventory(vertical=True)
+ for item_graphic, image_row in zip(rendered_item_column, self.top_image_grid, strict=False):
+ image = image_row[-1]
+
+ texture = get_texture(item_graphic)
+ if texture is None:
+ image.opacity = 0
+ image.texture = None
+ continue
+
+ image.texture = texture
+ image.opacity = 1
+
+ def render_background_game_grid(self, size: tuple[int, int], grass: bool) -> None:
+ if grass == self.bottom_grid_is_grass:
+ return
+
+ for row in range(size[1]):
+ for column in range(size[0]):
+ image = self.bottom_image_grid[row][column]
+
+ if not grass:
+ image.color = (0.3, 0.3, 0.3)
+ image.texture = None
+ continue
+
+ boss_room = (row in (0, 1, 2) and (size[1] - column) in (1, 2, 3)) or (row, column) == (3, size[1] - 2)
+ if boss_room:
+ image.color = (0.45, 0.35, 0.1)
+ image.texture = None
+ continue
+ image.texture = get_texture("Grass")
+ image.color = (1.0, 1.0, 1.0)
+
+ self.bottom_grid_is_grass = grass
+
+ def setup_game_grid_if_not_setup(self, size: tuple[int, int]) -> None:
+ if self.upper_game_grid.children:
+ return
+
+ self.top_image_grid = []
+ self.bottom_image_grid = []
+
+ for _row in range(size[1]):
+ self.top_image_grid.append([])
+ self.bottom_image_grid.append([])
+
+ for _column in range(size[0]):
+ bottom_image = Image(fit_mode="fill", color=(0.3, 0.3, 0.3))
+ self.lower_game_grid.add_widget(bottom_image)
+ self.bottom_image_grid[-1].append(bottom_image)
+
+ top_image = Image(fit_mode="fill")
+ self.upper_game_grid.add_widget(top_image)
+ self.top_image_grid[-1].append(top_image)
+
+ # Right side: Inventory
+ image = Image(fit_mode="fill", color=(0.3, 0.3, 0.3))
+ self.lower_game_grid.add_widget(image)
+
+ image2 = Image(fit_mode="fill", opacity=0)
+ self.upper_game_grid.add_widget(image2)
+
+ self.top_image_grid[-1].append(image2)
+
+ def build(self) -> Layout:
+ container = super().build()
+
+ self.game_view = APQuestGameView(self.ctx.input_and_rerender)
+
+ self.game_view_tab = self.add_client_tab("APQuest", self.game_view)
+
+ controls = APQuestControlsView()
+
+ self.add_client_tab("Controls", controls)
+
+ game_container = self.game_view.ids["game_container"]
+ self.lower_game_grid = APQuestGrid()
+ self.upper_game_grid = APQuestGrid()
+ self.confetti_view = ConfettiView()
+ game_container.add_widget(self.lower_game_grid)
+ game_container.add_widget(self.upper_game_grid)
+ game_container.add_widget(self.confetti_view)
+
+ game_container.bind(size=self.lower_game_grid.check_resize)
+ game_container.bind(size=self.upper_game_grid.check_resize)
+ game_container.bind(size=self.confetti_view.check_resize)
+
+ volume_slider_container = VolumeSliderView()
+ volume_slider = volume_slider_container.ids["volume_slider"]
+ volume_slider.value = self.sound_manager.volume_percentage
+ volume_slider.bind(value=lambda _, new_volume: self.sound_manager.set_volume_percentage(new_volume))
+
+ self.grid.add_widget(volume_slider_container, index=3)
+
+ Clock.schedule_interval(lambda dt: self.confetti_view.redraw_confetti(dt), 1 / 60)
+
+ return container
diff --git a/worlds/apquest/client/graphics.py b/worlds/apquest/client/graphics.py
new file mode 100644
index 000000000000..535bf84ee852
--- /dev/null
+++ b/worlds/apquest/client/graphics.py
@@ -0,0 +1,180 @@
+import pkgutil
+from collections.abc import Buffer
+from enum import Enum
+from io import BytesIO
+from typing import Literal, NamedTuple, Protocol, cast
+
+from kivy.uix.image import CoreImage
+
+from CommonClient import logger
+
+from .. import game
+from ..game.graphics import Graphic
+
+
+# The import "from kivy.graphics.texture import Texture" does not work correctly.
+# We never need the class directly, so we need to use a protocol.
+class Texture(Protocol):
+ mag_filter: Literal["nearest"]
+
+ def get_region(self, x: int, y: int, w: int, h: int) -> "Texture": ...
+
+
+class RelatedTexture(NamedTuple):
+ base_texture_file: str
+ x: int
+ y: int
+ width: int
+ height: int
+
+
+IMAGE_GRAPHICS: dict[Graphic, str | RelatedTexture] = {
+ Graphic.WALL: RelatedTexture("inanimates.png", 16, 32, 16, 16),
+ Graphic.BREAKABLE_BLOCK: RelatedTexture("inanimates.png", 32, 32, 16, 16),
+ Graphic.CHEST: RelatedTexture("inanimates.png", 0, 16, 16, 16),
+ Graphic.BUSH: RelatedTexture("inanimates.png", 16, 16, 16, 16),
+ Graphic.KEY_DOOR: RelatedTexture("inanimates.png", 32, 16, 16, 16),
+ Graphic.BUTTON_NOT_ACTIVATED: RelatedTexture("inanimates.png", 0, 0, 16, 16),
+ Graphic.BUTTON_ACTIVATED: RelatedTexture("inanimates.png", 16, 0, 16, 16),
+ Graphic.BUTTON_DOOR: RelatedTexture("inanimates.png", 32, 0, 16, 16),
+
+ Graphic.NORMAL_ENEMY_1_HEALTH: RelatedTexture("normal_enemy.png", 0, 0, 16, 16),
+ Graphic.NORMAL_ENEMY_2_HEALTH: RelatedTexture("normal_enemy.png", 16, 0, 16, 16),
+
+ Graphic.BOSS_5_HEALTH: RelatedTexture("boss.png", 16, 16, 16, 16),
+ Graphic.BOSS_4_HEALTH: RelatedTexture("boss.png", 0, 16, 16, 16),
+ Graphic.BOSS_3_HEALTH: RelatedTexture("boss.png", 32, 32, 16, 16),
+ Graphic.BOSS_2_HEALTH: RelatedTexture("boss.png", 16, 32, 16, 16),
+ Graphic.BOSS_1_HEALTH: RelatedTexture("boss.png", 0, 32, 16, 16),
+
+ Graphic.EMPTY_HEART: RelatedTexture("hearts.png", 0, 0, 16, 16),
+ Graphic.HEART: RelatedTexture("hearts.png", 16, 0, 16, 16),
+ Graphic.HALF_HEART: RelatedTexture("hearts.png", 32, 0, 16, 16),
+
+ Graphic.REMOTE_ITEM: RelatedTexture("items.png", 0, 16, 16, 16),
+ Graphic.CONFETTI_CANNON: RelatedTexture("items.png", 16, 16, 16, 16),
+ Graphic.HAMMER: RelatedTexture("items.png", 32, 16, 16, 16),
+ Graphic.KEY: RelatedTexture("items.png", 0, 0, 16, 16),
+ Graphic.SHIELD: RelatedTexture("items.png", 16, 0, 16, 16),
+ Graphic.SWORD: RelatedTexture("items.png", 32, 0, 16, 16),
+
+ Graphic.ITEMS_TEXT: "items_text.png",
+
+ Graphic.ZERO: RelatedTexture("numbers.png", 0, 16, 16, 16),
+ Graphic.ONE: RelatedTexture("numbers.png", 16, 16, 16, 16),
+ Graphic.TWO: RelatedTexture("numbers.png", 32, 16, 16, 16),
+ Graphic.THREE: RelatedTexture("numbers.png", 48, 16, 16, 16),
+ Graphic.FOUR: RelatedTexture("numbers.png", 64, 16, 16, 16),
+ Graphic.FIVE: RelatedTexture("numbers.png", 0, 0, 16, 16),
+ Graphic.SIX: RelatedTexture("numbers.png", 16, 0, 16, 16),
+ Graphic.SEVEN: RelatedTexture("numbers.png", 32, 0, 16, 16),
+ Graphic.EIGHT: RelatedTexture("numbers.png", 48, 0, 16, 16),
+ Graphic.NINE: RelatedTexture("numbers.png", 64, 0, 16, 16),
+
+ Graphic.LETTER_A: RelatedTexture("letters.png", 0, 16, 16, 16),
+ Graphic.LETTER_E: RelatedTexture("letters.png", 16, 16, 16, 16),
+ Graphic.LETTER_H: RelatedTexture("letters.png", 32, 16, 16, 16),
+ Graphic.LETTER_I: RelatedTexture("letters.png", 0, 0, 16, 16),
+ Graphic.LETTER_M: RelatedTexture("letters.png", 16, 0, 16, 16),
+ Graphic.LETTER_T: RelatedTexture("letters.png", 32, 0, 16, 16),
+
+ Graphic.DIVIDE: RelatedTexture("symbols.png", 0, 16, 16, 16),
+ Graphic.EQUALS: RelatedTexture("symbols.png", 16, 16, 16, 16),
+ Graphic.MINUS: RelatedTexture("symbols.png", 32, 16, 16, 16),
+ Graphic.PLUS: RelatedTexture("symbols.png", 0, 0, 16, 16),
+ Graphic.TIMES: RelatedTexture("symbols.png", 16, 0, 16, 16),
+ Graphic.NO: RelatedTexture("symbols.png", 32, 0, 16, 16),
+
+ Graphic.UNKNOWN: RelatedTexture("symbols.png", 32, 0, 16, 16), # Same as "No"
+}
+
+BACKGROUND_TILE = RelatedTexture("inanimates.png", 0, 32, 16, 16)
+
+
+class PlayerSprite(Enum):
+ HUMAN = 0
+ DUCK = 1
+ HORSE = 2
+ CAT = 3
+ UNKNOWN = -1
+
+
+PLAYER_GRAPHICS = {
+ Graphic.PLAYER_DOWN: {
+ PlayerSprite.HUMAN: RelatedTexture("human.png", 0, 16, 16, 16),
+ PlayerSprite.DUCK: RelatedTexture("duck.png", 0, 16, 16, 16),
+ PlayerSprite.HORSE: RelatedTexture("horse.png", 0, 16, 16, 16),
+ PlayerSprite.CAT: RelatedTexture("cat.png", 0, 16, 16, 16),
+ },
+ Graphic.PLAYER_UP: {
+ PlayerSprite.HUMAN: RelatedTexture("human.png", 16, 0, 16, 16),
+ PlayerSprite.DUCK: RelatedTexture("duck.png", 16, 0, 16, 16),
+ PlayerSprite.HORSE: RelatedTexture("horse.png", 16, 0, 16, 16),
+ PlayerSprite.CAT: RelatedTexture("cat.png", 16, 0, 16, 16),
+ },
+ Graphic.PLAYER_LEFT: {
+ PlayerSprite.HUMAN: RelatedTexture("human.png", 16, 16, 16, 16),
+ PlayerSprite.DUCK: RelatedTexture("duck.png", 16, 16, 16, 16),
+ PlayerSprite.HORSE: RelatedTexture("horse.png", 16, 16, 16, 16),
+ PlayerSprite.CAT: RelatedTexture("cat.png", 16, 16, 16, 16),
+ },
+ Graphic.PLAYER_RIGHT: {
+ PlayerSprite.HUMAN: RelatedTexture("human.png", 0, 0, 16, 16),
+ PlayerSprite.DUCK: RelatedTexture("duck.png", 0, 0, 16, 16),
+ PlayerSprite.HORSE: RelatedTexture("horse.png", 0, 0, 16, 16),
+ PlayerSprite.CAT: RelatedTexture("cat.png", 0, 0, 16, 16),
+ },
+}
+
+ALL_GRAPHICS = [
+ BACKGROUND_TILE,
+ *IMAGE_GRAPHICS.values(),
+ *[graphic for sub_dict in PLAYER_GRAPHICS.values() for graphic in sub_dict.values()],
+]
+
+_textures: dict[str | RelatedTexture, Texture] = {}
+
+
+def get_texture_by_identifier(texture_identifier: str | RelatedTexture) -> Texture:
+ if texture_identifier in _textures:
+ return _textures[texture_identifier]
+
+ if isinstance(texture_identifier, str):
+ image_data = pkgutil.get_data(game.__name__, f"graphics/{texture_identifier}")
+ if image_data is None:
+ raise RuntimeError(f'Could not find file "graphics/{texture_identifier}" for texture {texture_identifier}')
+
+ image_bytes = BytesIO(cast(Buffer, image_data))
+ texture = cast(Texture, CoreImage(image_bytes, ext="png").texture)
+ texture.mag_filter = "nearest"
+ _textures[texture_identifier] = texture
+ return texture
+
+ base_texture_filename, x, y, w, h = texture_identifier
+
+ base_texture = get_texture_by_identifier(base_texture_filename)
+
+ sub_texture = base_texture.get_region(x, y, w, h)
+ sub_texture.mag_filter = "nearest"
+ _textures[texture_identifier] = sub_texture
+ return sub_texture
+
+
+def get_texture(graphic: Graphic | Literal["Grass"], player_sprite: PlayerSprite | None = None) -> Texture | None:
+ if graphic == Graphic.EMPTY:
+ return None
+
+ if graphic == "Grass":
+ return get_texture_by_identifier(BACKGROUND_TILE)
+
+ if graphic in IMAGE_GRAPHICS:
+ return get_texture_by_identifier(IMAGE_GRAPHICS[graphic])
+
+ if graphic in PLAYER_GRAPHICS:
+ if player_sprite is None:
+ raise ValueError("Tried to load a player graphic without specifying a player_sprite")
+
+ return get_texture_by_identifier(PLAYER_GRAPHICS[graphic][player_sprite])
+
+ logger.exception(f"Tried to load unknown graphic {graphic}.")
+ return get_texture(Graphic.UNKNOWN)
diff --git a/worlds/apquest/client/item_quality.py b/worlds/apquest/client/item_quality.py
new file mode 100644
index 000000000000..35a975d88604
--- /dev/null
+++ b/worlds/apquest/client/item_quality.py
@@ -0,0 +1,25 @@
+from enum import Enum
+
+from BaseClasses import ItemClassification
+from NetUtils import NetworkItem
+
+
+class ItemQuality(Enum):
+ FILLER = 0
+ TRAP = 1
+ USEFUL = 2
+ PROGRESSION = 3
+ PROGUSEFUL = 4
+
+
+def get_quality_for_network_item(network_item: NetworkItem) -> ItemQuality:
+ flags = ItemClassification(network_item.flags)
+ if ItemClassification.progression in flags:
+ if ItemClassification.useful in flags:
+ return ItemQuality.PROGUSEFUL
+ return ItemQuality.PROGRESSION
+ if ItemClassification.useful in flags:
+ return ItemQuality.USEFUL
+ if ItemClassification.trap in flags:
+ return ItemQuality.TRAP
+ return ItemQuality.FILLER
diff --git a/worlds/apquest/client/launch.py b/worlds/apquest/client/launch.py
new file mode 100644
index 000000000000..515cf395bedf
--- /dev/null
+++ b/worlds/apquest/client/launch.py
@@ -0,0 +1,27 @@
+import asyncio
+from collections.abc import Sequence
+
+import colorama
+
+from CommonClient import get_base_parser, handle_url_arg
+
+# !!! IMPORTANT !!!
+# The client implementation is *not* meant for teaching.
+# Obviously, it is written to the best of its author's abilities,
+# but it is not to the same standard as the rest of the apworld.
+# Copy things from here at your own risk.
+
+
+def launch_ap_quest_client(*args: Sequence[str]) -> None:
+ from .ap_quest_client import main
+
+ parser = get_base_parser()
+ parser.add_argument("--name", default=None, help="Slot Name to connect as.")
+ parser.add_argument("url", nargs="?", help="Archipelago connection url")
+
+ launch_args = handle_url_arg(parser.parse_args(args))
+
+ colorama.just_fix_windows_console()
+
+ asyncio.run(main(launch_args))
+ colorama.deinit()
diff --git a/worlds/apquest/client/sounds.py b/worlds/apquest/client/sounds.py
new file mode 100644
index 000000000000..f4c4ab769dc0
--- /dev/null
+++ b/worlds/apquest/client/sounds.py
@@ -0,0 +1,249 @@
+import asyncio
+import pkgutil
+from asyncio import Task
+from collections.abc import Buffer
+from pathlib import Path
+from typing import cast
+
+from kivy import Config
+from kivy.core.audio import Sound, SoundLoader
+
+from CommonClient import logger
+
+from .. import game
+from .item_quality import ItemQuality
+from .utils import make_data_directory
+
+ITEM_JINGLES = {
+ ItemQuality.PROGUSEFUL: "8bit ProgUseful.ogg",
+ ItemQuality.PROGRESSION: "8bit Progression.ogg",
+ ItemQuality.USEFUL: "8bit Useful.ogg",
+ ItemQuality.TRAP: "8bit Trap.ogg",
+ ItemQuality.FILLER: "8bit Filler.ogg",
+}
+
+CONFETTI_CANNON = "APQuest Confetti Cannon.ogg"
+MATH_PROBLEM_STARTED_JINGLE = "APQuest Math Problem Starter Jingle.ogg"
+MATH_PROBLEM_SOLVED_JINGLE = "APQuest Math Problem Solved Jingle.ogg"
+VICTORY_JINGLE = "8bit Victory.ogg"
+
+ALL_JINGLES = [
+ MATH_PROBLEM_SOLVED_JINGLE,
+ MATH_PROBLEM_STARTED_JINGLE,
+ CONFETTI_CANNON,
+ VICTORY_JINGLE,
+ *ITEM_JINGLES.values(),
+]
+
+BACKGROUND_MUSIC_INTRO = "APQuest Intro.ogg"
+BACKGROUND_MUSIC = "APQuest BGM.ogg"
+MATH_TIME_BACKGROUND_MUSIC = "APQuest Math BGM.ogg"
+
+ALL_BGM = [
+ BACKGROUND_MUSIC_INTRO,
+ BACKGROUND_MUSIC,
+ MATH_TIME_BACKGROUND_MUSIC,
+]
+
+ALL_SOUNDS = [
+ *ALL_JINGLES,
+ *ALL_BGM,
+]
+
+
+class SoundManager:
+ sound_paths: dict[str, Path]
+
+ jingles: dict[str, Sound]
+ bgm_songs: dict[str, Sound]
+
+ active_bgm_song: str = BACKGROUND_MUSIC_INTRO
+
+ current_background_music_volume: float = 1.0
+ background_music_target_volume: float = 0.0
+
+ background_music_task: Task[None] | None = None
+ background_music_last_position: int = 0
+
+ volume_percentage: int = 0
+
+ game_started: bool
+ math_trap_active: bool
+ allow_intro_to_play: bool
+
+ def __init__(self) -> None:
+ self.extract_sounds()
+ self.populate_sounds()
+
+ self.game_started = False
+ self.allow_intro_to_play = False
+ self.math_trap_active = False
+
+ self.ensure_config()
+
+ self.background_music_task = asyncio.create_task(self.sound_manager_loop())
+
+ def ensure_config(self) -> None:
+ Config.adddefaultsection("APQuest")
+ Config.setdefault("APQuest", "volume", 50)
+ self.set_volume_percentage(Config.getint("APQuest", "volume"))
+
+ async def sound_manager_loop(self) -> None:
+ while True:
+ self.update_background_music()
+ self.do_fade()
+ await asyncio.sleep(0.02)
+
+ def extract_sounds(self) -> None:
+ # Kivy appears to have no good way of loading audio from bytes.
+ # So, we have to extract it out of the .apworld first
+
+ sound_paths = {}
+
+ sound_directory = make_data_directory("sounds")
+
+ for sound in ALL_SOUNDS:
+ sound_file_location = sound_directory / sound
+
+ sound_paths[sound] = sound_file_location
+
+ if sound_file_location.exists():
+ continue
+
+ with open(sound_file_location, "wb") as sound_file:
+ data = pkgutil.get_data(game.__name__, f"audio/{sound}")
+ if data is None:
+ logger.exception(f"Unable to extract sound {sound} to Archipelago/data")
+ continue
+ sound_file.write(cast(Buffer, data))
+
+ self.sound_paths = sound_paths
+
+ def load_audio(self, sound_filename: str) -> Sound:
+ audio_path = self.sound_paths[sound_filename]
+
+ sound_object = SoundLoader.load(str(audio_path.absolute()))
+ sound_object.seek(0)
+ return sound_object
+
+ def populate_sounds(self) -> None:
+ try:
+ self.jingles = {sound_filename: self.load_audio(sound_filename) for sound_filename in ALL_JINGLES}
+ except Exception as e:
+ logger.exception(e)
+
+ try:
+ self.bgm_songs = {sound_filename: self.load_audio(sound_filename) for sound_filename in ALL_BGM}
+ for bgm_song in self.bgm_songs.values():
+ bgm_song.loop = True
+ bgm_song.seek(0)
+ except Exception as e:
+ logger.exception(e)
+
+ def play_jingle(self, audio_filename: str) -> None:
+ higher_priority_sound_is_playing = False
+
+ for sound_name, sound in self.jingles.items():
+ if higher_priority_sound_is_playing: # jingles are ordered by priority, lower priority gets eaten
+ sound.stop()
+ continue
+
+ if sound_name == audio_filename:
+ sound.play()
+ self.update_background_music()
+ higher_priority_sound_is_playing = True
+
+ elif sound.state == "play":
+ higher_priority_sound_is_playing = True
+
+ def update_background_music(self) -> None:
+ self.update_active_song()
+ if any(sound.state == "play" for sound in self.jingles.values()):
+ self.play_background_music(False)
+ else:
+ if self.math_trap_active:
+ # Don't fade math trap song, it ends up feeling better
+ self.play_background_music(True)
+ else:
+ self.fade_background_music(True)
+
+ def play_background_music(self, play: bool = True) -> None:
+ if play:
+ self.background_music_target_volume = 1
+ self.set_background_music_volume(1)
+ else:
+ self.background_music_target_volume = 0
+ self.set_background_music_volume(0)
+
+ def set_background_music_volume(self, volume: float) -> None:
+ self.current_background_music_volume = volume
+
+ for song_filename, song in self.bgm_songs.items():
+ if song_filename != self.active_bgm_song:
+ song.volume = 0
+ continue
+ song.volume = volume * self.volume_percentage / 100
+
+ def fade_background_music(self, fade_in: bool = True) -> None:
+ if fade_in:
+ self.background_music_target_volume = 1
+ else:
+ self.background_music_target_volume = 0
+
+ def set_volume_percentage(self, volume_percentage: float) -> None:
+ volume_percentage_int = int(volume_percentage)
+ if self.volume_percentage != volume_percentage:
+ self.volume_percentage = volume_percentage_int
+ Config.set("APQuest", "volume", volume_percentage_int)
+ Config.write()
+ self.set_background_music_volume(self.current_background_music_volume)
+
+ for jingle in self.jingles.values():
+ jingle.volume = self.volume_percentage / 100
+
+ def do_fade(self) -> None:
+ if self.current_background_music_volume > self.background_music_target_volume:
+ self.set_background_music_volume(max(0.0, self.current_background_music_volume - 0.02))
+ if self.current_background_music_volume < self.background_music_target_volume:
+ self.set_background_music_volume(min(1.0, self.current_background_music_volume + 0.02))
+
+ for song_filename, song in self.bgm_songs.items():
+ if song_filename != self.active_bgm_song:
+ if song_filename == BACKGROUND_MUSIC:
+ # It ends up feeling better if this just always continues playing quietly after being started.
+ # Even "fading in at a random spot" is better than restarting the song after a jingle / math trap.
+ if self.game_started and song.state == "stop":
+ song.play()
+ song.seek(0)
+ continue
+
+ song.stop()
+ song.seek(0)
+ continue
+
+ if self.active_bgm_song == BACKGROUND_MUSIC_INTRO and not self.allow_intro_to_play:
+ song.stop()
+ song.seek(0)
+ continue
+
+ if self.current_background_music_volume != 0:
+ if song.state == "stop":
+ song.play()
+ song.seek(0)
+
+ def update_active_song(self) -> None:
+ new_active_song = self.determine_correct_song()
+ if new_active_song == self.active_bgm_song:
+ return
+ self.active_bgm_song = new_active_song
+ # reevaluate song volumes
+ self.set_background_music_volume(self.current_background_music_volume)
+
+ def determine_correct_song(self) -> str:
+ if not self.game_started:
+ return BACKGROUND_MUSIC_INTRO
+
+ if self.math_trap_active:
+ return MATH_TIME_BACKGROUND_MUSIC
+
+ return BACKGROUND_MUSIC
diff --git a/worlds/apquest/client/utils.py b/worlds/apquest/client/utils.py
new file mode 100644
index 000000000000..362e5fed1188
--- /dev/null
+++ b/worlds/apquest/client/utils.py
@@ -0,0 +1,25 @@
+from pathlib import Path
+
+from Utils import user_path
+
+
+def make_data_directory(dir_name: str) -> Path:
+ root_directory = Path(user_path())
+ if not root_directory.exists():
+ raise FileNotFoundError(f"Unable to find AP directory {root_directory.absolute()}.")
+
+ data_directory = root_directory / "data"
+
+ specific_data_directory = data_directory / "apquest" / dir_name
+ specific_data_directory.mkdir(parents=True, exist_ok=True)
+
+ gitignore = specific_data_directory / ".gitignore"
+
+ with open(gitignore, "w") as f:
+ f.write(
+ """*
+!.gitignore
+"""
+ )
+
+ return specific_data_directory
diff --git a/worlds/apquest/components.py b/worlds/apquest/components.py
new file mode 100644
index 000000000000..eb25450d8ccc
--- /dev/null
+++ b/worlds/apquest/components.py
@@ -0,0 +1,33 @@
+from worlds.LauncherComponents import Component, Type, components, launch
+
+
+# The most common type of component is a client, but there are other components, such as sprite/palette adjusters.
+# (Note: Some worlds distribute their clients as separate, standalone programs,
+# while others include them in the apworld itself. Standalone clients are not an apworld component,
+# although you could make a component that e.g. auto-installs and launches the standalone client for the user.)
+# APQuest has a Python client inside the apworld that contains the entire game. This is a component.
+# APQuest will not teach you how to make a client or any other type of component.
+# However, let's quickly talk about how you register a component to be launchable from the Archipelago Launcher.
+# First, you'll need a function that takes a list of args (e.g. from the command line) that launches your component.
+def run_client(*args: str) -> None:
+ # Ideally, you should lazily import your component code so that it doesn't have to be loaded until necessary.
+ from .client.launch import launch_ap_quest_client
+
+ # Also, if your component has its own lifecycle, like if it is its own window that can be interacted with,
+ # you should use the LauncherComponents.launch helper (which itself calls launch_subprocess).
+ # This will create a subprocess for your component, launching it in a separate window from the Archipelago Launcher.
+ launch(launch_ap_quest_client, name="APQuest Client", args=args)
+
+
+# You then add this function as a component by appending a Component instance to LauncherComponents.components.
+# Now, it will show up in the Launcher with its display name,
+# and when the user clicks on the "Open" button, your function will be run.
+components.append(
+ Component(
+ "APQuest Client",
+ func=run_client,
+ game_name="APQuest",
+ component_type=Type.CLIENT,
+ supports_uri=True,
+ )
+)
diff --git a/worlds/apquest/docs/de_APQuest.md b/worlds/apquest/docs/de_APQuest.md
new file mode 100644
index 000000000000..82e383fa1600
--- /dev/null
+++ b/worlds/apquest/docs/de_APQuest.md
@@ -0,0 +1,78 @@
+# APQuest
+
+## Wo ist die Seite für die Einstellungen?
+
+Die [Seite für die Spielereinstellungen dieses Spiels](../player-options) enthält alle Optionen die man benötigt, um
+eine YAML-Datei zu konfigurieren und zu exportieren.
+
+## Was ist APQuest?
+
+APQuest ist ein Spiel, welches von NewSoupVi für Archipelago entwickelt wurde.
+Es ist ein minimalistisches 8bit-inspiriertes Abenteuerspiel mit gitterförmiger Bewegungssteuerung.
+APQuest ist ungefähr 20 Sekunden lang. Der Client kann aber nahtlos zwischen mehreren APQuest-Slots wechseln.
+Wenn du 10 APQuest-Slots in einer Multiworld haben willst, sollte das also problemlos möglich sein.if you want to have 10 of them, that should work pretty well.
+
+Ausschlaggebend ist bei APQuest, dass das gesamte Spiel in der .apworld enthalten ist.
+Wenn du also die .apworld in deine
+[Archipelago-Installation](https://github.com/ArchipelagoMW/Archipelago/releases/latest) installiert hast,
+kannst du APQuest spielen.
+
+## Warum existiert APQuest?
+
+APQuest ist als Beispiel-.apworld geschrieben, mit welchem neue .apworld-Entwickler lernen können, wie man eine
+.apworld schreibt.
+Der [APQuest-Quellcode](https://github.com/NewSoupVi/Archipelago/tree/apquest/worlds/apquest) enthält unzählige Kommentare und Beispiele, die erklären,
+wie jeder Teil der World-API funktioniert.
+Dabei nutzt er nur die modernsten API-Funktionen (Stand: 2025-08-24).
+
+Das sekundäre Ziel von APQuest ist, eine semi-minimale, generische .apworld zu sein, die Archipelago selbst gehört.
+Damit kann sie für Archipelagos Unit-Tests benutzt werden,
+ohne dass sich die Archipelago-Entwickler davor fürchten müssen, dass APQuest irgendwann gelöscht wird.
+
+Das dritte Ziel von APQuest ist, das erste "Spiel in einer .apworld" zu sein,
+wobei das ganze Spiel in Python und Kivy programmiert ist
+und innerhalb seines CommonClient-basierten Clients spielbar ist.
+Ich bin mir nicht ganz sicher, dass es wirklich das erste Spiel dieser Art ist, aber ich kenne bis jetzt keine anderen.
+
+## Wenn ich mich im APQuest-Client angemeldet habe, wie spiele ich dann das Spiel?
+
+WASD oder Pfeiltasten zum Bewegen.
+Leertaste, um dein Schwert zu schwingen (wenn du es hast) und um mit Objekten zu interagieren.
+C, um die Konfettikanone zu feuern.
+
+Öffne Kisten, zerhacke Büsche, öffne Türen, aktiviere Knöpfe, besiege Gegner.
+Sobald du den Drachen im oberen rechten Raum bezwingst, gewinnst du das Spiel.
+Das ist alles! Viel Spaß!
+
+## Ein Statement zum Besitz von APQuest
+
+APQuest ist mit der [MIT-Lizenz](https://opensource.org/license/mit) lizenziert,
+was heißt, dass es von jedem für jeden Zweck modifiziert und verbreitet werden kann.
+Archipelago hat jedoch seine eigenen Besitztumsstrukturen, die über der MIT-Lizenz stehen.
+Diese Strukturen machen es unklar,
+ob eine .apworld-Implementierung überhaupt permanent verlässlich in Archipelago bleibt.
+
+Im Zusammenhang mit diesen unverbindlichen, nicht gesetzlich verpflichtenden Besitztumsstrukturen
+mache ich die folgende Aussage.
+
+Ich, NewSoupVi, verzichte hiermit auf alle Rechte, APQuest aus Archipelago zu entfernen.
+Dies bezieht sich auf alle Teile von APQuest mit der Ausnahme der Musik und der Soundeffekte.
+Wenn ich die Töne entfernt haben möchte, muss ich dafür selbst einen PR öffnen.
+Dieser PR darf nur die Töne entfernen und muss APQuest intakt und spielbar halten.
+
+Solang ich der Maintainer von APQuest bin, möchte ich als solcher agieren.
+Das heißt, dass jegliche Änderungen an APQuest zuerst von mir genehmigt werden müssen.
+
+Wenn ich jedoch aufhöre, der Maintainer von APQuest zu sein,
+egal ob es mein eigener Wunsch war oder ich meinen Maintainer-Verantwortungen nicht mehr nachkomme,
+dann wird APQuest automatisch Eigentum der Core-Maintainer von Archipelago,
+die dann frei entscheiden können, was mit APQuest passieren soll.
+Es wäre mein Wunsch, dass wenn APQuest an eine andere Einzelperson übergeben wird,
+diese Person sich an ähnliche Eigentumsregelungen hält wie ich.
+
+Hoffentlich stellt dieses Statement sicher, dass APQuest für immer eine .apworld sein kann,
+auf die Archipelago sich verlassen kann.
+Wenn die Besitztumsstrukturen von Archipelago geändert werden,
+vertraue ich den Core-Maintainern (bzw. den Eigentümern von Archipelago generell) damit,
+angemessene Entscheidungen darüber zu treffen,
+wie dieses Statement im Kontext der neuen Regeln interpretiert werden sollte.
diff --git a/worlds/apquest/docs/en_APQuest.md b/worlds/apquest/docs/en_APQuest.md
new file mode 100644
index 000000000000..64eb1fa35a6d
--- /dev/null
+++ b/worlds/apquest/docs/en_APQuest.md
@@ -0,0 +1,69 @@
+# APQuest
+
+## Where is the options page?
+
+The [player options page for this game](../player-options) contains all the options you need to configure and export a
+config file.
+
+## What is APQuest?
+
+APQuest is an original game made entirely by NewSoupVi.
+It is a minimal 8bit-era inspired adventure game with grid-like movement.
+It is about 20 seconds long. However, the client can seamlessly switch between different slots,
+so if you want to have 10 of them, that should work pretty well.
+
+Crucially, this game is entirely integrated into the client sitting inside its .apworld.
+If you have the .apworld installed into your [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest)
+install, you can play APQuest.
+
+## Why does APQuest exist?
+
+APQuest is implemented to be an example .apworld that can be used as a learning tool for new .apworld developers.
+Its [source code](https://github.com/NewSoupVi/Archipelago/tree/apquest/worlds/apquest)
+contains countless comments explaining how each part of the World API works.
+Also, as of the writing of this setup guide (2025-08-24), it is up to date with all the modern Archipelago APIs.
+
+The secondary goal of APQuest is to be a semi-minimal generic world that is owned by Archipelago.
+This means it can be used for Archipelago's unit tests without fear of eventual removal.
+
+Finally, APQuest was designed to be the first ever "game inside an .apworld",
+where the entire game is coded in Python and Kivy and is playable from within its CommonClient-based Client.
+I'm not actually sure if it's the first, but I'm not aware of any others.
+
+## Once I'm inside the APQuest client, how do I actually play APQuest?
+
+WASD or Arrow Keys for movement.
+Space to swing your sword (if you have it) or interact with objects.
+C to fire the Confetti Cannon.
+
+Open chests, slash bushes, open doors, press buttons, defeat enemies.
+Once you beat the dragon in the top right room, you win.
+That's all there is! Have fun!
+
+## A statement on the ownership over APQuest
+
+APQuest is licensed using the [MIT license](https://opensource.org/license/mit),
+meaning it can be modified and redistributed by anyone for any purpose.
+However, Archipelago has its own ownership structures built ontop of the license.
+These ownership structures call into question whether any world implementation can permanently be relied on.
+
+In terms of these non-binding, non-legal Archipelago ownership structures, I will make the following statement.
+
+I, NewSoupVi, hereby relinquish any and all rights to remove APQuest from Archipelago.
+This applies to all parts of APQuest with the sole exception of the music and sounds.
+If I want the sounds to be removed, I must do so via a PR to the Archipelago repository myself.
+Said PR must keep APQuest intact and playable, just with the music removed.
+
+As long as I am the maintainer of APQuest, I wish to act as such.
+This means that any updates to APQuest must go through me.
+
+However, if I ever cease to be the maintainer of APQuest,
+due to my own wishes or because I fail to uphold the maintainership "contract",
+the maintainership of APQuest will go to the Core Maintainers of Archipelago, who may then decide what to do with it.
+They can decide freely, but if the maintainership goes to another singular person,
+it is my wish that this person adheres to a similar set of rules that I've laid out here for myself.
+
+Hopefully, this set of commitments should ensure that APQuest will forever be an apworld that can be relied on in Core.
+If the ownership structures of Archipelago change,
+I trust the Core Maintainers (or the owners in general) of Archipelago to make reasonable assumptions
+about how this statement should be reinterpreted to fit the new rules.
diff --git a/worlds/apquest/docs/setup_de.md b/worlds/apquest/docs/setup_de.md
new file mode 100644
index 000000000000..425d57d9e7d5
--- /dev/null
+++ b/worlds/apquest/docs/setup_de.md
@@ -0,0 +1,43 @@
+# APQuest Randomizer Setup-Anleitung
+
+## Benötigte Software
+
+- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest)
+- Die [APQuest-apworld](https://github.com/NewSoupVi/Archipelago/releases),
+ falls diese nicht mit deiner Version von Archipelago gebündelt ist.
+
+## Wie man spielt
+
+Zuerst brauchst du einen Raum, mit dem du dich verbinden kannst.
+Dafür musst du oder jemand den du kennst ein Spiel generieren.
+Dieser Schritt wird hier nicht erklärt, aber du kannst den
+[Archipelago Setup Guide](/tutorial/Archipelago/setup_en#generating-a-game) lesen.
+
+Du musst außerdem [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest) installiert haben
+und die [APQuest apworld](https://github.com/NewSoupVi/Archipelago/releases) darin installieren.
+
+Von hier ist es einfach, dich mit deinem Slot zu verbinden.
+
+### Webhost-Raum
+
+Wenn dein Raum auf einem WebHost läuft (z.B. [archipelago.gg](https://archipelago.gg))
+kannst du einfach auf deinen Namen in der Spielerliste klicken.
+Dies öffnet den Archipelago Launcher, welcher dich dann fragt,
+ob du den Text Client oder den APQuest Client öffnen willst.
+Wähle hier den APQuest Client. Der Rest sollte automatisch passieren, sodass du APQuest direkt spielen kannst.
+
+### Lokaler Server
+
+Falls für deinen Raum keine WebHost-Raumseite verfügbar ist, kannst du APQuest manuell starten.
+
+Öffne den Archipelago Launcher und finde den APQuest Client in der Komponentenliste. Klicke auf "Open".
+Nach einer kurzen Wartezeit sollte sich der APQuest Client öffnen.
+Tippe in der oberen Zeile die Server-Adresse ein und klicke dann auf "Connect".
+Gib deinen Spielernamen ein. Wenn ein Passwort existiert, tippe dieses auch ein.
+Du solltest jetzt verbunden sein und kannst APQuest spielen.
+
+## Slotwechsel
+
+Der APQuest Client kann zwischen verschiedenen Slots wechseln, ohne neugestartet werden zu müssen,
+
+Klicke einfach den "Disconnect"-Knopf. Dann verbinde dich mit dem anderen Raum / Slot.
diff --git a/worlds/apquest/docs/setup_en.md b/worlds/apquest/docs/setup_en.md
new file mode 100644
index 000000000000..2b66fa93b80a
--- /dev/null
+++ b/worlds/apquest/docs/setup_en.md
@@ -0,0 +1,42 @@
+# APQuest Randomizer Setup Guide
+
+## Required Software
+
+- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest)
+- [The APQuest apworld](https://github.com/NewSoupVi/Archipelago/releases),
+ if not bundled with your version of Archipelago
+
+## How to play
+
+First, you need a room to connect to. For this, you or someone you know has to generate a game.
+This will not be explained here,
+but you can check the [Archipelago Setup Guide](/tutorial/Archipelago/setup_en#generating-a-game).
+
+You also need to have [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest) installed
+and the [The APQuest apworld](https://github.com/NewSoupVi/Archipelago/releases) installed into Archipelago.
+
+From here, connecting to your APQuest slot is easy. There are two scenarios.
+
+### Webhost Room
+
+If your room is hosted on a WebHost (e.g. [archipelago.gg](https://archipelago.gg)),
+you should be able to simply click on your name in the player list.
+This will open the Archipelago Launcher
+and ask you whether you want to connect with the Text Client or the APQuest Client.
+Choose "APQuest Client". The rest should happen completely automatically and you should be able to play APQuest.
+
+### Locally hosted room
+
+If your room does not have a WebHost room page available, you can launch APQuest manually.
+
+Open the Archipelago Launcher, and then select the APQuest Client from the list.
+After a short while, the APQuest client should open.
+Enter the server address at the top and click "Connect".
+Then, enter your name. If a password exists, enter the password.
+You should now be connected and able to play APQuest.
+
+## Switching Rooms
+
+The APQuest Client can seamlessly switch rooms without restarting.
+
+Simply click the "Disconnect" button, then connect to a different slot/room.
diff --git a/worlds/apquest/game/__init__.py b/worlds/apquest/game/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/apquest/game/audio/8bit Filler.ogg b/worlds/apquest/game/audio/8bit Filler.ogg
new file mode 100644
index 000000000000..d16cfc7aba8b
Binary files /dev/null and b/worlds/apquest/game/audio/8bit Filler.ogg differ
diff --git a/worlds/apquest/game/audio/8bit ProgUseful.ogg b/worlds/apquest/game/audio/8bit ProgUseful.ogg
new file mode 100644
index 000000000000..bc092f771ad0
Binary files /dev/null and b/worlds/apquest/game/audio/8bit ProgUseful.ogg differ
diff --git a/worlds/apquest/game/audio/8bit Progression.ogg b/worlds/apquest/game/audio/8bit Progression.ogg
new file mode 100644
index 000000000000..a4d7ba871157
Binary files /dev/null and b/worlds/apquest/game/audio/8bit Progression.ogg differ
diff --git a/worlds/apquest/game/audio/8bit Trap.ogg b/worlds/apquest/game/audio/8bit Trap.ogg
new file mode 100644
index 000000000000..783f5fd3d3d3
Binary files /dev/null and b/worlds/apquest/game/audio/8bit Trap.ogg differ
diff --git a/worlds/apquest/game/audio/8bit Useful.ogg b/worlds/apquest/game/audio/8bit Useful.ogg
new file mode 100644
index 000000000000..ed333e9d93cf
Binary files /dev/null and b/worlds/apquest/game/audio/8bit Useful.ogg differ
diff --git a/worlds/apquest/game/audio/8bit Victory.ogg b/worlds/apquest/game/audio/8bit Victory.ogg
new file mode 100644
index 000000000000..5153af11cec7
Binary files /dev/null and b/worlds/apquest/game/audio/8bit Victory.ogg differ
diff --git a/worlds/apquest/game/audio/APQuest BGM.ogg b/worlds/apquest/game/audio/APQuest BGM.ogg
new file mode 100644
index 000000000000..8a6e542891c8
Binary files /dev/null and b/worlds/apquest/game/audio/APQuest BGM.ogg differ
diff --git a/worlds/apquest/game/audio/APQuest Confetti Cannon.ogg b/worlds/apquest/game/audio/APQuest Confetti Cannon.ogg
new file mode 100644
index 000000000000..4eafe62a734b
Binary files /dev/null and b/worlds/apquest/game/audio/APQuest Confetti Cannon.ogg differ
diff --git a/worlds/apquest/game/audio/APQuest Intro.ogg b/worlds/apquest/game/audio/APQuest Intro.ogg
new file mode 100644
index 000000000000..52c1fa89adbe
Binary files /dev/null and b/worlds/apquest/game/audio/APQuest Intro.ogg differ
diff --git a/worlds/apquest/game/audio/APQuest Math BGM.ogg b/worlds/apquest/game/audio/APQuest Math BGM.ogg
new file mode 100644
index 000000000000..5708f9cafb84
Binary files /dev/null and b/worlds/apquest/game/audio/APQuest Math BGM.ogg differ
diff --git a/worlds/apquest/game/audio/APQuest Math Problem Solved Jingle.ogg b/worlds/apquest/game/audio/APQuest Math Problem Solved Jingle.ogg
new file mode 100644
index 000000000000..f20e7a909e7d
Binary files /dev/null and b/worlds/apquest/game/audio/APQuest Math Problem Solved Jingle.ogg differ
diff --git a/worlds/apquest/game/audio/APQuest Math Problem Starter Jingle.ogg b/worlds/apquest/game/audio/APQuest Math Problem Starter Jingle.ogg
new file mode 100644
index 000000000000..7af05ee2a8ea
Binary files /dev/null and b/worlds/apquest/game/audio/APQuest Math Problem Starter Jingle.ogg differ
diff --git a/worlds/apquest/game/entities.py b/worlds/apquest/game/entities.py
new file mode 100644
index 000000000000..64b89206f6f6
--- /dev/null
+++ b/worlds/apquest/game/entities.py
@@ -0,0 +1,312 @@
+from __future__ import annotations
+
+from abc import abstractmethod
+from typing import TYPE_CHECKING, ClassVar
+
+from .graphics import Graphic
+from .items import ITEM_TO_GRAPHIC, Item
+from .locations import Location
+
+if TYPE_CHECKING:
+ from .player import Player
+
+
+class Entity:
+ solid: bool
+ graphic: Graphic
+
+
+class InteractableMixin:
+ @abstractmethod
+ def interact(self, player: Player) -> None:
+ pass
+
+
+class ActivatableMixin:
+ @abstractmethod
+ def activate(self, player: Player) -> None:
+ pass
+
+
+class LocationMixin:
+ location: Location
+ content: Item | None = None
+ remote: bool = False
+ has_given_content: bool = False
+
+ def force_clear(self) -> None:
+ if self.has_given_content:
+ return
+
+ self.has_given_content = True
+ self.content_success()
+
+ def give_content(self, player: Player) -> None:
+ if self.has_given_content:
+ return
+
+ if self.content is None:
+ self.content_failure()
+ return
+
+ if self.remote:
+ player.location_cleared(self.location.value)
+ else:
+ player.receive_item(self.content)
+
+ self.has_given_content = True
+ self.content_success()
+
+ def content_success(self) -> None:
+ pass
+
+ def content_failure(self) -> None:
+ pass
+
+
+class Empty(Entity):
+ solid = False
+ graphic = Graphic.EMPTY
+
+
+class Wall(Entity):
+ solid = True
+ graphic = Graphic.WALL
+
+
+class Chest(Entity, InteractableMixin, LocationMixin):
+ solid = True
+
+ is_open: bool = False
+
+ def __init__(self, location: Location) -> None:
+ self.location = location
+
+ def update_solidity(self) -> None:
+ self.solid = not self.has_given_content
+
+ def open(self) -> None:
+ self.is_open = True
+ self.update_solidity()
+
+ def interact(self, player: Player) -> None:
+ if self.has_given_content:
+ return
+
+ if self.is_open:
+ self.give_content(player)
+ return
+
+ self.open()
+
+ def content_success(self) -> None:
+ self.update_solidity()
+
+ def content_failure(self) -> None:
+ self.update_solidity()
+
+ @property
+ def graphic(self) -> Graphic:
+ if self.has_given_content:
+ return Graphic.EMPTY
+ if self.is_open:
+ if self.content is None:
+ return Graphic.EMPTY
+ return ITEM_TO_GRAPHIC[self.content]
+ return Graphic.CHEST
+
+
+class Door(Entity):
+ solid = True
+
+ is_open: bool = False
+
+ closed_graphic: ClassVar[Graphic]
+
+ def open(self) -> None:
+ self.is_open = True
+ self.solid = False
+
+ @property
+ def graphic(self) -> Graphic:
+ if self.is_open:
+ return Graphic.EMPTY
+ return self.closed_graphic
+
+
+class KeyDoor(Door, InteractableMixin):
+ closed_graphic = Graphic.KEY_DOOR
+
+ def interact(self, player: Player) -> None:
+ if self.is_open:
+ return
+
+ if not player.has_item(Item.KEY):
+ return
+
+ player.remove_item(Item.KEY)
+
+ self.open()
+
+
+class BreakableBlock(Door, InteractableMixin):
+ closed_graphic = Graphic.BREAKABLE_BLOCK
+
+ def interact(self, player: Player) -> None:
+ if self.is_open:
+ return
+
+ if not player.has_item(Item.HAMMER):
+ return
+
+ player.remove_item(Item.HAMMER)
+
+ self.open()
+
+
+class Bush(Door, InteractableMixin):
+ closed_graphic = Graphic.BUSH
+
+ def interact(self, player: Player) -> None:
+ if self.is_open:
+ return
+
+ if not player.has_item(Item.SWORD):
+ return
+
+ self.open()
+
+
+class Button(Entity, InteractableMixin):
+ solid = True
+
+ activates: ActivatableMixin
+ activated = False
+
+ def __init__(self, activates: ActivatableMixin) -> None:
+ self.activates = activates
+
+ def interact(self, player: Player) -> None:
+ if self.activated:
+ return
+
+ self.activated = True
+ self.activates.activate(player)
+
+ @property
+ def graphic(self) -> Graphic:
+ if self.activated:
+ return Graphic.BUTTON_ACTIVATED
+ return Graphic.BUTTON_NOT_ACTIVATED
+
+
+class ButtonDoor(Door, ActivatableMixin):
+ closed_graphic = Graphic.BUTTON_DOOR
+
+ def activate(self, player: Player) -> None:
+ self.is_open = True
+ self.solid = False
+
+
+class Enemy(Entity, InteractableMixin):
+ solid = True
+
+ current_health: int
+ max_health: int
+
+ dead: bool = False
+
+ enemy_graphic_by_health: ClassVar[dict[int, Graphic]] = {
+ 2: Graphic.NORMAL_ENEMY_2_HEALTH,
+ 1: Graphic.NORMAL_ENEMY_1_HEALTH,
+ }
+ enemy_default_graphic = Graphic.NORMAL_ENEMY_1_HEALTH
+
+ def __init__(self, max_health: int) -> None:
+ self.max_health = max_health
+ self.respawn()
+
+ def die(self) -> None:
+ self.dead = True
+ self.solid = False
+
+ def respawn(self) -> None:
+ self.dead = False
+ self.solid = True
+ self.heal_if_not_dead()
+
+ def heal_if_not_dead(self) -> None:
+ if self.dead:
+ return
+ self.current_health = self.max_health
+
+ def interact(self, player: Player) -> None:
+ if self.dead:
+ return
+
+ if player.has_item(Item.SWORD):
+ self.current_health = max(0, self.current_health - 1)
+
+ if self.current_health == 0:
+ if not self.dead:
+ self.die()
+ return
+
+ player.damage(2)
+
+ @property
+ def graphic(self) -> Graphic:
+ if self.dead:
+ return Graphic.EMPTY
+ return self.enemy_graphic_by_health.get(self.current_health, self.enemy_default_graphic)
+
+
+class EnemyWithLoot(Enemy, LocationMixin):
+ def __init__(self, max_health: int, location: Location) -> None:
+ super().__init__(max_health)
+ self.location = location
+
+ def die(self) -> None:
+ self.dead = True
+ self.solid = not self.has_given_content
+
+ def interact(self, player: Player) -> None:
+ if self.dead:
+ if not self.has_given_content:
+ self.give_content(player)
+ return
+
+ super().interact(player)
+
+ @property
+ def graphic(self) -> Graphic:
+ if self.dead and not self.has_given_content:
+ if self.content is None:
+ return Graphic.EMPTY
+ return ITEM_TO_GRAPHIC[self.content]
+ return super().graphic
+
+ def content_success(self) -> None:
+ self.die()
+
+ def content_failure(self) -> None:
+ self.die()
+
+
+class FinalBoss(Enemy):
+ enemy_graphic_by_health: ClassVar[dict[int, Graphic]] = {
+ 5: Graphic.BOSS_5_HEALTH,
+ 4: Graphic.BOSS_4_HEALTH,
+ 3: Graphic.BOSS_3_HEALTH,
+ 2: Graphic.BOSS_2_HEALTH,
+ 1: Graphic.BOSS_1_HEALTH,
+ }
+ enemy_default_graphic = Graphic.BOSS_1_HEALTH
+
+ def interact(self, player: Player) -> None:
+ dead_before = self.dead
+
+ super().interact(player)
+
+ if not dead_before and self.dead:
+ player.victory()
diff --git a/worlds/apquest/game/events.py b/worlds/apquest/game/events.py
new file mode 100644
index 000000000000..0517abc69be8
--- /dev/null
+++ b/worlds/apquest/game/events.py
@@ -0,0 +1,37 @@
+from dataclasses import dataclass
+
+
+@dataclass
+class Event:
+ pass
+
+
+@dataclass
+class LocationClearedEvent(Event):
+ location_id: int
+
+
+@dataclass
+class MathProblemStarted(Event):
+ pass
+
+
+@dataclass
+class MathProblemSolved(Event):
+ pass
+
+
+@dataclass
+class VictoryEvent(Event):
+ pass
+
+
+@dataclass
+class LocationalEvent(Event):
+ x: int
+ y: int
+
+
+@dataclass
+class ConfettiFired(LocationalEvent):
+ pass
diff --git a/worlds/apquest/game/game.py b/worlds/apquest/game/game.py
new file mode 100644
index 000000000000..709e74850ab3
--- /dev/null
+++ b/worlds/apquest/game/game.py
@@ -0,0 +1,203 @@
+from math import ceil
+from random import Random
+
+from .entities import InteractableMixin
+from .events import ConfettiFired, Event, MathProblemSolved, MathProblemStarted
+from .gameboard import Gameboard, create_gameboard
+from .generate_math_problem import MathProblem, generate_math_problem
+from .graphics import Graphic
+from .inputs import DIGIT_INPUTS_TO_DIGITS, Direction, Input
+from .items import ITEM_TO_GRAPHIC, Item, RemotelyReceivedItem
+from .locations import Location
+from .player import Player
+
+
+class Game:
+ player: Player
+ gameboard: Gameboard
+
+ random: Random
+
+ queued_events: list[Event]
+
+ active_math_problem: MathProblem | None
+ active_math_problem_input: list[int] | None
+
+ remotely_received_items: set[tuple[int, int, int]]
+
+ def __init__(
+ self, hard_mode: bool, hammer_exists: bool, extra_chest: bool, random_object: Random | None = None
+ ) -> None:
+ self.queued_events = []
+ self.gameboard = create_gameboard(hard_mode, hammer_exists, extra_chest)
+ self.player = Player(self.gameboard, self.queued_events.append)
+ self.active_math_problem = None
+ self.remotely_received_items = set()
+
+ if random_object is None:
+ self.random = Random()
+ else:
+ self.random = random_object
+
+ def render(self) -> tuple[tuple[Graphic, ...], ...]:
+ if self.active_math_problem is None and self.player.inventory[Item.MATH_TRAP]:
+ self.active_math_problem = generate_math_problem(self.random)
+ self.active_math_problem_input = []
+ self.player.remove_item(Item.MATH_TRAP)
+ self.queued_events.append(MathProblemStarted())
+ return self.gameboard.render_math_problem(
+ self.active_math_problem,
+ self.active_math_problem_input,
+ self.currently_typed_in_math_result,
+ )
+
+ if self.active_math_problem is not None and self.active_math_problem_input is not None:
+ return self.gameboard.render_math_problem(
+ self.active_math_problem, self.active_math_problem_input, self.currently_typed_in_math_result
+ )
+
+ return self.gameboard.render(self.player)
+
+ def render_health_and_inventory(self, vertical: bool = False) -> tuple[Graphic, ...]:
+ size = self.gameboard.size[1] if vertical else self.gameboard.size[0]
+
+ graphics_array = [Graphic.EMPTY] * size
+
+ item_back_index = size - 1
+ for item, amount in sorted(self.player.inventory.items(), key=lambda sort_item: sort_item[0].value):
+ for _ in range(amount):
+ if item_back_index == 3:
+ break
+ if item == Item.HEALTH_UPGRADE:
+ continue
+ if item == Item.MATH_TRAP:
+ continue
+
+ graphics_array[item_back_index] = ITEM_TO_GRAPHIC[item]
+ item_back_index -= 1
+ else:
+ continue
+ break
+
+ remaining_health = self.player.current_health
+ for i in range(min(item_back_index, ceil(self.player.max_health / 2))):
+ new_remaining_health = max(0, remaining_health - 2)
+ change = remaining_health - new_remaining_health
+ remaining_health = new_remaining_health
+
+ if change == 2:
+ graphics_array[i] = Graphic.HEART
+ elif change == 1:
+ graphics_array[i] = Graphic.HALF_HEART
+ elif change == 0:
+ graphics_array[i] = Graphic.EMPTY_HEART
+
+ return tuple(graphics_array)
+
+ def attempt_player_movement(self, direction: Direction) -> None:
+ self.player.facing = direction
+
+ delta_x, delta_y = direction.value
+ new_x, new_y = self.player.current_x + delta_x, self.player.current_y + delta_y
+
+ if not self.gameboard.get_entity_at(new_x, new_y).solid:
+ self.player.current_x = new_x
+ self.player.current_y = new_y
+
+ def attempt_interact(self) -> None:
+ delta_x, delta_y = self.player.facing.value
+ entity_x, entity_y = self.player.current_x + delta_x, self.player.current_y + delta_y
+
+ entity = self.gameboard.get_entity_at(entity_x, entity_y)
+
+ if isinstance(entity, InteractableMixin):
+ entity.interact(self.player)
+
+ def attempt_fire_confetti_cannon(self) -> None:
+ if self.player.has_item(Item.CONFETTI_CANNON):
+ self.player.remove_item(Item.CONFETTI_CANNON)
+ self.queued_events.append(ConfettiFired(self.player.current_x, self.player.current_y))
+
+ def math_problem_success(self) -> None:
+ self.active_math_problem = None
+ self.active_math_problem_input = None
+ self.queued_events.append(MathProblemSolved())
+
+ @property
+ def currently_typed_in_math_result(self) -> int | None:
+ if not self.active_math_problem_input:
+ return None
+
+ number = self.active_math_problem_input[-1]
+ if len(self.active_math_problem_input) == 2:
+ number += self.active_math_problem_input[0] * 10
+
+ return number
+
+ def check_math_problem_result(self) -> None:
+ if self.active_math_problem is None:
+ return
+
+ if self.currently_typed_in_math_result == self.active_math_problem.result:
+ self.math_problem_success()
+
+ def math_problem_input(self, input: int) -> None:
+ if self.active_math_problem_input is None or len(self.active_math_problem_input) >= 2:
+ return
+
+ self.active_math_problem_input.append(input)
+ self.check_math_problem_result()
+
+ def math_problem_delete(self) -> None:
+ if self.active_math_problem_input is None or len(self.active_math_problem_input) == 0:
+ return
+ self.active_math_problem_input.pop()
+ self.check_math_problem_result()
+
+ def input(self, input_key: Input) -> None:
+ if not self.gameboard.ready:
+ return
+
+ if input_key in DIGIT_INPUTS_TO_DIGITS:
+ self.math_problem_input(DIGIT_INPUTS_TO_DIGITS[input_key])
+ return
+ if input_key == Input.BACKSPACE:
+ self.math_problem_delete()
+ return
+
+ if input_key == Input.LEFT:
+ self.attempt_player_movement(Direction.LEFT)
+ return
+
+ if input_key == Input.UP:
+ self.attempt_player_movement(Direction.UP)
+ return
+
+ if input_key == Input.RIGHT:
+ self.attempt_player_movement(Direction.RIGHT)
+ return
+
+ if input_key == Input.DOWN:
+ self.attempt_player_movement(Direction.DOWN)
+ return
+
+ if input_key == Input.ACTION:
+ self.attempt_interact()
+ return
+
+ if input_key == Input.CONFETTI:
+ self.attempt_fire_confetti_cannon()
+ return
+
+ raise ValueError(f"Don't know input {input_key}")
+
+ def receive_item(self, remote_item_id: int, remote_location_id: int, remote_location_player: int) -> None:
+ remotely_received_item = RemotelyReceivedItem(remote_item_id, remote_location_id, remote_location_player)
+ if remotely_received_item in self.remotely_received_items:
+ return
+
+ self.player.receive_item(Item(remote_item_id))
+
+ def force_clear_location(self, location_id: int) -> None:
+ location = Location(location_id)
+ self.gameboard.force_clear_location(location)
diff --git a/worlds/apquest/game/gameboard.py b/worlds/apquest/game/gameboard.py
new file mode 100644
index 000000000000..ec97491c872f
--- /dev/null
+++ b/worlds/apquest/game/gameboard.py
@@ -0,0 +1,267 @@
+from __future__ import annotations
+
+import random
+from collections.abc import Iterable
+from typing import TYPE_CHECKING
+
+from .entities import (
+ BreakableBlock,
+ Bush,
+ Button,
+ ButtonDoor,
+ Chest,
+ Empty,
+ Enemy,
+ EnemyWithLoot,
+ Entity,
+ FinalBoss,
+ KeyDoor,
+ LocationMixin,
+ Wall,
+)
+from .generate_math_problem import MathProblem
+from .graphics import DIGIT_TO_GRAPHIC, DIGIT_TO_GRAPHIC_ZERO_EMPTY, MATH_PROBLEM_TYPE_TO_GRAPHIC, Graphic
+from .items import Item
+from .locations import DEFAULT_CONTENT, Location
+
+if TYPE_CHECKING:
+ from .player import Player
+
+
+class Gameboard:
+ gameboard: tuple[tuple[Entity, ...], ...]
+
+ hammer_exists: bool
+ content_filled: bool
+
+ remote_entity_by_location_id: dict[Location, LocationMixin]
+
+ def __init__(self, gameboard: tuple[tuple[Entity, ...], ...], hammer_exists: bool) -> None:
+ assert gameboard, "Gameboard is empty"
+ assert all(len(row) == len(gameboard[0]) for row in gameboard), "Not all rows have the same size"
+
+ self.gameboard = gameboard
+ self.hammer_exists = hammer_exists
+ self.content_filled = False
+ self.remote_entity_by_location_id = {}
+
+ def fill_default_location_content(self, trap_percentage: int = 0) -> None:
+ for entity in self.iterate_entities():
+ if isinstance(entity, LocationMixin):
+ if entity.location in DEFAULT_CONTENT:
+ content = DEFAULT_CONTENT[entity.location]
+ if content == Item.HAMMER and not self.hammer_exists:
+ content = Item.CONFETTI_CANNON
+
+ if content == Item.CONFETTI_CANNON:
+ if random.randrange(100) < trap_percentage:
+ content = Item.MATH_TRAP
+
+ entity.content = content
+
+ self.content_filled = True
+
+ def fill_remote_location_content(self, graphic_overrides: dict[Location, Item]) -> None:
+ for entity in self.iterate_entities():
+ if isinstance(entity, LocationMixin):
+ entity.content = graphic_overrides.get(entity.location, Item.REMOTE_ITEM)
+ entity.remote = True
+ self.remote_entity_by_location_id[entity.location] = entity
+
+ self.content_filled = True
+
+ def get_entity_at(self, x: int, y: int) -> Entity:
+ if x < 0 or x >= len(self.gameboard[0]):
+ return Wall()
+ if y < 0 or y >= len(self.gameboard):
+ return Wall()
+
+ return self.gameboard[y][x]
+
+ def iterate_entities(self) -> Iterable[Entity]:
+ for row in self.gameboard:
+ yield from row
+
+ def respawn_final_boss(self) -> None:
+ for entity in self.iterate_entities():
+ if isinstance(entity, FinalBoss):
+ entity.respawn()
+
+ def heal_alive_enemies(self) -> None:
+ for entity in self.iterate_entities():
+ if isinstance(entity, Enemy):
+ entity.heal_if_not_dead()
+
+ def render(self, player: Player) -> tuple[tuple[Graphic, ...], ...]:
+ graphics = []
+
+ for y, row in enumerate(self.gameboard):
+ graphics_row = []
+ for x, entity in enumerate(row):
+ if player.current_x == x and player.current_y == y:
+ graphics_row.append(player.render())
+ else:
+ graphics_row.append(entity.graphic)
+
+ graphics.append(tuple(graphics_row))
+
+ return tuple(graphics)
+
+ def render_math_problem(
+ self, problem: MathProblem, current_input_digits: list[int], current_input_int: int | None
+ ) -> tuple[tuple[Graphic, ...], ...]:
+ rows = len(self.gameboard)
+ columns = len(self.gameboard[0])
+
+ def pad_row(row: list[Graphic]) -> tuple[Graphic, ...]:
+ row = row.copy()
+ while len(row) < columns:
+ row = [Graphic.EMPTY, *row, Graphic.EMPTY]
+ while len(row) > columns:
+ row.pop()
+
+ return tuple(row)
+
+ empty_row = tuple([Graphic.EMPTY] * columns)
+
+ math_time_row = pad_row(
+ [
+ Graphic.LETTER_M,
+ Graphic.LETTER_A,
+ Graphic.LETTER_T,
+ Graphic.LETTER_H,
+ Graphic.EMPTY,
+ Graphic.LETTER_T,
+ Graphic.LETTER_I,
+ Graphic.LETTER_M,
+ Graphic.LETTER_E,
+ ]
+ )
+
+ num_1_first_digit = problem.num_1 // 10
+ num_1_second_digit = problem.num_1 % 10
+ num_2_first_digit = problem.num_2 // 10
+ num_2_second_digit = problem.num_2 % 10
+
+ math_problem_row = pad_row(
+ [
+ DIGIT_TO_GRAPHIC_ZERO_EMPTY[num_1_first_digit],
+ DIGIT_TO_GRAPHIC[num_1_second_digit],
+ Graphic.EMPTY,
+ MATH_PROBLEM_TYPE_TO_GRAPHIC[problem.problem_type],
+ Graphic.EMPTY,
+ DIGIT_TO_GRAPHIC_ZERO_EMPTY[num_2_first_digit],
+ DIGIT_TO_GRAPHIC[num_2_second_digit],
+ ]
+ )
+
+ display_digit_1 = None
+ display_digit_2 = None
+ if current_input_digits:
+ display_digit_1 = current_input_digits[0]
+ if len(current_input_digits) == 2:
+ display_digit_2 = current_input_digits[1]
+
+ result_row = pad_row(
+ [
+ Graphic.EQUALS,
+ Graphic.EMPTY,
+ DIGIT_TO_GRAPHIC[display_digit_1],
+ DIGIT_TO_GRAPHIC[display_digit_2],
+ Graphic.EMPTY,
+ Graphic.NO if len(current_input_digits) == 2 and current_input_int != problem.result else Graphic.EMPTY,
+ ]
+ )
+
+ output = [math_time_row, empty_row, math_problem_row, result_row]
+
+ while len(output) < rows:
+ output = [empty_row, *output, empty_row]
+ while len(output) > columns:
+ output.pop(0)
+
+ return tuple(output)
+
+ def force_clear_location(self, location: Location) -> None:
+ entity = self.remote_entity_by_location_id[location]
+ entity.force_clear()
+
+ @property
+ def ready(self) -> bool:
+ return self.content_filled
+
+ @property
+ def size(self) -> tuple[int, int]:
+ return len(self.gameboard[0]), len(self.gameboard)
+
+
+def create_gameboard(hard_mode: bool, hammer_exists: bool, extra_chest: bool) -> Gameboard:
+ boss_door = ButtonDoor()
+ boss_door_button = Button(boss_door)
+
+ key_door = KeyDoor()
+
+ top_middle_chest = Chest(Location.TOP_MIDDLE_CHEST)
+ left_room_chest = Chest(Location.TOP_LEFT_CHEST)
+ bottom_left_chest = Chest(Location.BOTTOM_LEFT_CHEST)
+ bottom_right_room_left_chest = Chest(Location.BOTTOM_RIGHT_ROOM_LEFT_CHEST)
+ bottom_right_room_right_chest = Chest(Location.BOTTOM_RIGHT_ROOM_RIGHT_CHEST)
+
+ bottom_left_extra_chest = Chest(Location.BOTTOM_LEFT_EXTRA_CHEST) if extra_chest else Empty()
+ wall_if_hammer = Wall() if hammer_exists else Empty()
+ breakable_block = BreakableBlock() if hammer_exists else Empty()
+
+ normal_enemy = EnemyWithLoot(2 if hard_mode else 1, Location.ENEMY_DROP)
+ boss = FinalBoss(5 if hard_mode else 3)
+
+ gameboard = (
+ (Empty(), Empty(), Empty(), Wall(), Empty(), Empty(), Empty(), Wall(), Empty(), Empty(), Empty()),
+ (
+ Empty(),
+ boss_door_button,
+ Empty(),
+ Wall(),
+ Empty(),
+ top_middle_chest,
+ Empty(),
+ Wall(),
+ Empty(),
+ boss,
+ Empty(),
+ ),
+ (Empty(), Empty(), Empty(), Wall(), Empty(), Empty(), Empty(), Wall(), Empty(), Empty(), Empty()),
+ (
+ Empty(),
+ left_room_chest,
+ Empty(),
+ Wall(),
+ wall_if_hammer,
+ breakable_block,
+ wall_if_hammer,
+ Wall(),
+ Wall(),
+ boss_door,
+ Wall(),
+ ),
+ (Empty(), Empty(), Empty(), Wall(), Empty(), Empty(), Empty(), Wall(), Empty(), Empty(), Empty()),
+ (Wall(), key_door, Wall(), Wall(), Empty(), Empty(), Empty(), Empty(), Empty(), normal_enemy, Empty()),
+ (Empty(), Empty(), Empty(), Empty(), Empty(), Empty(), Empty(), Wall(), Empty(), Empty(), Empty()),
+ (Empty(), bottom_left_extra_chest, Empty(), Empty(), Empty(), Empty(), Wall(), Wall(), Wall(), Wall(), Wall()),
+ (Empty(), Empty(), Empty(), Empty(), Empty(), Empty(), Wall(), Empty(), Empty(), Empty(), Empty()),
+ (
+ Empty(),
+ bottom_left_chest,
+ Empty(),
+ Empty(),
+ Empty(),
+ Empty(),
+ Bush(),
+ Empty(),
+ bottom_right_room_left_chest,
+ bottom_right_room_right_chest,
+ Empty(),
+ ),
+ (Empty(), Empty(), Empty(), Empty(), Empty(), Empty(), Wall(), Empty(), Empty(), Empty(), Empty()),
+ )
+
+ return Gameboard(gameboard, hammer_exists)
diff --git a/worlds/apquest/game/generate_math_problem.py b/worlds/apquest/game/generate_math_problem.py
new file mode 100644
index 000000000000..eb8ff0f01ec9
--- /dev/null
+++ b/worlds/apquest/game/generate_math_problem.py
@@ -0,0 +1,63 @@
+import random
+from collections.abc import Callable
+from enum import Enum
+from operator import add, mul, sub, truediv
+from typing import NamedTuple
+
+_random = random.Random()
+
+class NumberChoiceConstraints(NamedTuple):
+ num_1_min: int
+ num_1_max: int
+ num_2_min: int
+ num_2_max: int
+ commutative: bool
+ operator: Callable[[int, int], int | float]
+
+
+class MathProblemType(Enum):
+ PLUS = 1
+ MINUS = 2
+ TIMES = 3
+ DIVIDE = 4
+
+
+MATH_PROBLEM_CONSTRAINTS = {
+ MathProblemType.PLUS: NumberChoiceConstraints(1, 99, 1, 99, True, add),
+ MathProblemType.MINUS: NumberChoiceConstraints(2, 99, 1, 99, False, sub),
+ MathProblemType.TIMES: NumberChoiceConstraints(2, 10, 2, 50, True, mul),
+ MathProblemType.DIVIDE: NumberChoiceConstraints(4, 99, 2, 50, False, truediv),
+}
+
+
+class MathProblem(NamedTuple):
+ problem_type: MathProblemType
+ num_1: int
+ num_2: int
+ result: int
+
+
+def generate_math_problem(random_object: random.Random = _random) -> MathProblem:
+ problem_type: MathProblemType = random_object.choice(list(MathProblemType))
+ number_choice_constraints = MATH_PROBLEM_CONSTRAINTS[problem_type]
+
+ for _ in range(10000):
+ num_1 = random.randint(number_choice_constraints.num_1_min, number_choice_constraints.num_1_max)
+ num_2 = random.randint(number_choice_constraints.num_2_min, number_choice_constraints.num_2_max)
+
+ result = number_choice_constraints.operator(num_1, num_2)
+
+ result_int = int(result)
+ if not result_int == result:
+ continue
+
+ if result_int < 2 or result_int > 99:
+ continue
+
+ if number_choice_constraints.commutative:
+ if random.randint(0, 1):
+ num_1, num_2 = num_2, num_1
+
+ return MathProblem(problem_type, num_1, num_2, result_int)
+
+ return MathProblem(MathProblemType.PLUS, 1, 1, 2)
diff --git a/worlds/apquest/game/graphics.py b/worlds/apquest/game/graphics.py
new file mode 100644
index 000000000000..9279c6c7b4fa
--- /dev/null
+++ b/worlds/apquest/game/graphics.py
@@ -0,0 +1,99 @@
+from enum import Enum
+
+from .generate_math_problem import MathProblemType
+
+
+class Graphic(Enum):
+ EMPTY = 0
+ WALL = 1
+ BUTTON_NOT_ACTIVATED = 2
+ BUTTON_ACTIVATED = 3
+ KEY_DOOR = 4
+ BUTTON_DOOR = 5
+ CHEST = 6
+ BUSH = 7
+ BREAKABLE_BLOCK = 8
+
+ NORMAL_ENEMY_1_HEALTH = 10
+ NORMAL_ENEMY_2_HEALTH = 11
+
+ BOSS_1_HEALTH = 20
+ BOSS_2_HEALTH = 21
+ BOSS_3_HEALTH = 22
+ BOSS_4_HEALTH = 23
+ BOSS_5_HEALTH = 24
+
+ PLAYER_DOWN = 30
+ PLAYER_UP = 31
+ PLAYER_LEFT = 32
+ PLAYER_RIGHT = 33
+
+ KEY = 41
+ SWORD = 42
+ SHIELD = 43
+ HAMMER = 44
+
+ HEART = 50
+ HALF_HEART = 51
+ EMPTY_HEART = 52
+
+ CONFETTI_CANNON = 60
+
+ REMOTE_ITEM = 70
+
+ ITEMS_TEXT = 80
+
+ MATH_TRAP = CONFETTI_CANNON
+
+ ZERO = 1000
+ ONE = 1001
+ TWO = 1002
+ THREE = 1003
+ FOUR = 1004
+ FIVE = 1005
+ SIX = 1006
+ SEVEN = 1007
+ EIGHT = 1008
+ NINE = 1009
+
+ PLUS = 1100
+ MINUS = 1101
+ TIMES = 1102
+ DIVIDE = 1103
+
+ LETTER_A = 2000
+ LETTER_E = 2005
+ LETTER_H = 2008
+ LETTER_I = 2009
+ LETTER_M = 2013
+ LETTER_T = 2019
+
+ EQUALS = 2050
+ NO = 2060
+
+ UNKNOWN = -1
+
+
+DIGIT_TO_GRAPHIC = {
+ None: Graphic.EMPTY,
+ 0: Graphic.ZERO,
+ 1: Graphic.ONE,
+ 2: Graphic.TWO,
+ 3: Graphic.THREE,
+ 4: Graphic.FOUR,
+ 5: Graphic.FIVE,
+ 6: Graphic.SIX,
+ 7: Graphic.SEVEN,
+ 8: Graphic.EIGHT,
+ 9: Graphic.NINE,
+}
+
+DIGIT_TO_GRAPHIC_ZERO_EMPTY = DIGIT_TO_GRAPHIC.copy()
+DIGIT_TO_GRAPHIC_ZERO_EMPTY[0] = Graphic.EMPTY
+
+MATH_PROBLEM_TYPE_TO_GRAPHIC = {
+ MathProblemType.PLUS: Graphic.PLUS,
+ MathProblemType.MINUS: Graphic.MINUS,
+ MathProblemType.TIMES: Graphic.TIMES,
+ MathProblemType.DIVIDE: Graphic.DIVIDE,
+}
diff --git a/worlds/apquest/game/graphics/boss.png b/worlds/apquest/game/graphics/boss.png
new file mode 100644
index 000000000000..dbcda31048e2
Binary files /dev/null and b/worlds/apquest/game/graphics/boss.png differ
diff --git a/worlds/apquest/game/graphics/cat.png b/worlds/apquest/game/graphics/cat.png
new file mode 100644
index 000000000000..aa5e854753e0
Binary files /dev/null and b/worlds/apquest/game/graphics/cat.png differ
diff --git a/worlds/apquest/game/graphics/duck.png b/worlds/apquest/game/graphics/duck.png
new file mode 100644
index 000000000000..5e07b70dfc5f
Binary files /dev/null and b/worlds/apquest/game/graphics/duck.png differ
diff --git a/worlds/apquest/game/graphics/hearts.png b/worlds/apquest/game/graphics/hearts.png
new file mode 100644
index 000000000000..0ab344898f81
Binary files /dev/null and b/worlds/apquest/game/graphics/hearts.png differ
diff --git a/worlds/apquest/game/graphics/horse.png b/worlds/apquest/game/graphics/horse.png
new file mode 100644
index 000000000000..349c256688be
Binary files /dev/null and b/worlds/apquest/game/graphics/horse.png differ
diff --git a/worlds/apquest/game/graphics/human.png b/worlds/apquest/game/graphics/human.png
new file mode 100644
index 000000000000..6978bf0ac5ef
Binary files /dev/null and b/worlds/apquest/game/graphics/human.png differ
diff --git a/worlds/apquest/game/graphics/inanimates.png b/worlds/apquest/game/graphics/inanimates.png
new file mode 100644
index 000000000000..ba7c143b9822
Binary files /dev/null and b/worlds/apquest/game/graphics/inanimates.png differ
diff --git a/worlds/apquest/game/graphics/items.png b/worlds/apquest/game/graphics/items.png
new file mode 100644
index 000000000000..ce66feda7788
Binary files /dev/null and b/worlds/apquest/game/graphics/items.png differ
diff --git a/worlds/apquest/game/graphics/items_text.png b/worlds/apquest/game/graphics/items_text.png
new file mode 100644
index 000000000000..4b5a4f30ded3
Binary files /dev/null and b/worlds/apquest/game/graphics/items_text.png differ
diff --git a/worlds/apquest/game/graphics/letters.png b/worlds/apquest/game/graphics/letters.png
new file mode 100644
index 000000000000..8ec3bae065a8
Binary files /dev/null and b/worlds/apquest/game/graphics/letters.png differ
diff --git a/worlds/apquest/game/graphics/normal_enemy.png b/worlds/apquest/game/graphics/normal_enemy.png
new file mode 100644
index 000000000000..baed65d8cc33
Binary files /dev/null and b/worlds/apquest/game/graphics/normal_enemy.png differ
diff --git a/worlds/apquest/game/graphics/numbers.png b/worlds/apquest/game/graphics/numbers.png
new file mode 100644
index 000000000000..97e19bea85b6
Binary files /dev/null and b/worlds/apquest/game/graphics/numbers.png differ
diff --git a/worlds/apquest/game/graphics/symbols.png b/worlds/apquest/game/graphics/symbols.png
new file mode 100644
index 000000000000..6f9e0436994b
Binary files /dev/null and b/worlds/apquest/game/graphics/symbols.png differ
diff --git a/worlds/apquest/game/inputs.py b/worlds/apquest/game/inputs.py
new file mode 100644
index 000000000000..eb736a606f08
--- /dev/null
+++ b/worlds/apquest/game/inputs.py
@@ -0,0 +1,44 @@
+from enum import Enum
+
+
+class Direction(Enum):
+ LEFT = (-1, 0)
+ UP = (0, -1)
+ RIGHT = (1, 0)
+ DOWN = (0, 1)
+
+
+class Input(Enum):
+ LEFT = 1
+ UP = 2
+ RIGHT = 3
+ DOWN = 4
+ ACTION = 5
+ CONFETTI = 6
+
+ ZERO = 1000
+ ONE = 1001
+ TWO = 1002
+ THREE = 1003
+ FOUR = 1004
+ FIVE = 1005
+ SIX = 1006
+ SEVEN = 1007
+ EIGHT = 1008
+ NINE = 1009
+
+ BACKSPACE = 2000
+
+
+DIGIT_INPUTS_TO_DIGITS = {
+ Input.ZERO: 0,
+ Input.ONE: 1,
+ Input.TWO: 2,
+ Input.THREE: 3,
+ Input.FOUR: 4,
+ Input.FIVE: 5,
+ Input.SIX: 6,
+ Input.SEVEN: 7,
+ Input.EIGHT: 8,
+ Input.NINE: 9,
+}
diff --git a/worlds/apquest/game/items.py b/worlds/apquest/game/items.py
new file mode 100644
index 000000000000..a37d0e654f6b
--- /dev/null
+++ b/worlds/apquest/game/items.py
@@ -0,0 +1,38 @@
+from collections import defaultdict
+from enum import Enum
+from typing import NamedTuple
+
+from ..items import ITEM_NAME_TO_ID
+from .graphics import Graphic
+
+
+class Item(Enum):
+ KEY = ITEM_NAME_TO_ID["Key"]
+ SWORD = ITEM_NAME_TO_ID["Sword"]
+ SHIELD = ITEM_NAME_TO_ID["Shield"]
+ HAMMER = ITEM_NAME_TO_ID["Hammer"]
+ HEALTH_UPGRADE = ITEM_NAME_TO_ID["Health Upgrade"]
+ CONFETTI_CANNON = ITEM_NAME_TO_ID["Confetti Cannon"]
+ MATH_TRAP = ITEM_NAME_TO_ID["Math Trap"]
+ REMOTE_ITEM = -1
+
+
+class RemotelyReceivedItem(NamedTuple):
+ remote_item_id: int
+ remote_location_id: int
+ remote_location_player: int
+
+
+ITEM_TO_GRAPHIC = defaultdict(
+ lambda: Graphic.UNKNOWN,
+ {
+ Item.KEY: Graphic.KEY,
+ Item.SWORD: Graphic.SWORD,
+ Item.SHIELD: Graphic.SHIELD,
+ Item.HAMMER: Graphic.HAMMER,
+ Item.HEALTH_UPGRADE: Graphic.HEART,
+ Item.CONFETTI_CANNON: Graphic.CONFETTI_CANNON,
+ Item.REMOTE_ITEM: Graphic.REMOTE_ITEM,
+ Item.MATH_TRAP: Graphic.MATH_TRAP,
+ },
+)
diff --git a/worlds/apquest/game/locations.py b/worlds/apquest/game/locations.py
new file mode 100644
index 000000000000..b8aaa77e7c85
--- /dev/null
+++ b/worlds/apquest/game/locations.py
@@ -0,0 +1,25 @@
+from enum import Enum
+
+from ..locations import LOCATION_NAME_TO_ID
+from .items import Item
+
+
+class Location(Enum):
+ TOP_LEFT_CHEST = LOCATION_NAME_TO_ID["Top Left Room Chest"]
+ TOP_MIDDLE_CHEST = LOCATION_NAME_TO_ID["Top Middle Chest"]
+ BOTTOM_LEFT_CHEST = LOCATION_NAME_TO_ID["Bottom Left Chest"]
+ BOTTOM_LEFT_EXTRA_CHEST = LOCATION_NAME_TO_ID["Bottom Left Extra Chest"]
+ BOTTOM_RIGHT_ROOM_LEFT_CHEST = LOCATION_NAME_TO_ID["Bottom Right Room Left Chest"]
+ BOTTOM_RIGHT_ROOM_RIGHT_CHEST = LOCATION_NAME_TO_ID["Bottom Right Room Right Chest"]
+ ENEMY_DROP = LOCATION_NAME_TO_ID["Right Room Enemy Drop"]
+
+
+DEFAULT_CONTENT = {
+ Location.TOP_LEFT_CHEST: Item.HEALTH_UPGRADE,
+ Location.TOP_MIDDLE_CHEST: Item.HEALTH_UPGRADE,
+ Location.BOTTOM_LEFT_CHEST: Item.SWORD,
+ Location.BOTTOM_LEFT_EXTRA_CHEST: Item.CONFETTI_CANNON,
+ Location.BOTTOM_RIGHT_ROOM_LEFT_CHEST: Item.SHIELD,
+ Location.BOTTOM_RIGHT_ROOM_RIGHT_CHEST: Item.HAMMER,
+ Location.ENEMY_DROP: Item.KEY,
+}
diff --git a/worlds/apquest/game/play_in_console.py b/worlds/apquest/game/play_in_console.py
new file mode 100644
index 000000000000..9e07896f651c
--- /dev/null
+++ b/worlds/apquest/game/play_in_console.py
@@ -0,0 +1,143 @@
+from .events import ConfettiFired, MathProblemSolved
+
+try:
+ from pynput import keyboard
+ from pynput.keyboard import Key, KeyCode
+except ImportError as e:
+ raise ImportError("In order to play APQuest from console, you have to install pynput.") from e
+
+from .game import Game
+from .graphics import Graphic
+from .inputs import Input
+from .items import ITEM_TO_GRAPHIC
+
+graphic_to_char = {
+ Graphic.EMPTY: " ",
+ Graphic.WALL: "W",
+ Graphic.BUTTON_NOT_ACTIVATED: "B",
+ Graphic.BUTTON_ACTIVATED: "A",
+ Graphic.KEY_DOOR: "D",
+ Graphic.BUTTON_DOOR: "?",
+ Graphic.CHEST: "C",
+ Graphic.BUSH: "T",
+ Graphic.BREAKABLE_BLOCK: "~",
+ Graphic.NORMAL_ENEMY_2_HEALTH: "2",
+ Graphic.NORMAL_ENEMY_1_HEALTH: "1",
+ Graphic.BOSS_5_HEALTH: "5",
+ Graphic.BOSS_4_HEALTH: "4",
+ Graphic.BOSS_3_HEALTH: "3",
+ Graphic.BOSS_2_HEALTH: "2",
+ Graphic.BOSS_1_HEALTH: "1",
+ Graphic.PLAYER_DOWN: "v",
+ Graphic.PLAYER_UP: "^",
+ Graphic.PLAYER_LEFT: "<",
+ Graphic.PLAYER_RIGHT: ">",
+ Graphic.KEY: "K",
+ Graphic.SHIELD: "X",
+ Graphic.SWORD: "S",
+ Graphic.HAMMER: "H",
+ Graphic.HEART: "♡",
+ Graphic.CONFETTI_CANNON: "?",
+ Graphic.REMOTE_ITEM: "I",
+ Graphic.UNKNOWN: "ß",
+ Graphic.ZERO: "0",
+ Graphic.ONE: "1",
+ Graphic.TWO: "2",
+ Graphic.THREE: "3",
+ Graphic.FOUR: "4",
+ Graphic.FIVE: "5",
+ Graphic.SIX: "6",
+ Graphic.SEVEN: "7",
+ Graphic.EIGHT: "8",
+ Graphic.NINE: "9",
+ Graphic.PLUS: "+",
+ Graphic.MINUS: "-",
+ Graphic.TIMES: "x",
+ Graphic.DIVIDE: "/",
+ Graphic.LETTER_A: "A",
+ Graphic.LETTER_E: "E",
+ Graphic.LETTER_H: "H",
+ Graphic.LETTER_I: "I",
+ Graphic.LETTER_M: "M",
+ Graphic.LETTER_T: "T",
+ Graphic.EQUALS: "=",
+ Graphic.NO: "X",
+}
+
+KEY_CONVERSION = {
+ keyboard.KeyCode.from_char("w"): Input.UP,
+ Key.up: Input.UP,
+ keyboard.KeyCode.from_char("s"): Input.DOWN,
+ Key.down: Input.DOWN,
+ keyboard.KeyCode.from_char("a"): Input.LEFT,
+ Key.left: Input.LEFT,
+ keyboard.KeyCode.from_char("d"): Input.RIGHT,
+ Key.right: Input.RIGHT,
+ Key.space: Input.ACTION,
+ keyboard.KeyCode.from_char("c"): Input.CONFETTI,
+ keyboard.KeyCode.from_char("0"): Input.ZERO,
+ keyboard.KeyCode.from_char("1"): Input.ONE,
+ keyboard.KeyCode.from_char("2"): Input.TWO,
+ keyboard.KeyCode.from_char("3"): Input.THREE,
+ keyboard.KeyCode.from_char("4"): Input.FOUR,
+ keyboard.KeyCode.from_char("5"): Input.FIVE,
+ keyboard.KeyCode.from_char("6"): Input.SIX,
+ keyboard.KeyCode.from_char("7"): Input.SEVEN,
+ keyboard.KeyCode.from_char("8"): Input.EIGHT,
+ keyboard.KeyCode.from_char("9"): Input.NINE,
+ Key.backspace: Input.BACKSPACE,
+}
+
+
+def render_to_text(game: Game) -> str:
+ player = game.player
+ rendered_graphics = game.render()
+
+ output_string = f"Health: {player.current_health}/{player.max_health}\n"
+
+ inventory = []
+ for item, count in player.inventory.items():
+ inventory += [graphic_to_char[ITEM_TO_GRAPHIC[item]] for _ in range(count)]
+ inventory.sort()
+
+ output_string += f"Inventory: {', '.join(inventory)}\n"
+
+ if player.has_won:
+ output_string += "VICTORY!!!\n"
+
+ while game.queued_events:
+ next_event = game.queued_events.pop(0)
+ if isinstance(next_event, ConfettiFired):
+ output_string += "Confetti fired! You feel motivated :)\n"
+ if isinstance(next_event, MathProblemSolved):
+ output_string += "Math problem solved!\n"
+
+ for row in rendered_graphics:
+ output_string += " ".join(graphic_to_char[graphic] for graphic in row)
+ output_string += "\n"
+
+ return output_string
+
+
+if __name__ == "__main__":
+ hard_mode = input("Do you want to play hard mode? (Y/N)").lower().strip() in ("y", "yes")
+ hammer_exists = input("Do you want the hammer to exist in the game? (Y/N)").lower().strip() in ("y", "yes")
+ extra_chest = input("Do you want the extra starting chest to exist in the game?").lower().strip() in ("y", "yes")
+ math_trap_percentage = int(input("What should the percentage of math traps be?"))
+
+ game = Game(hard_mode, hammer_exists, extra_chest)
+ game.gameboard.fill_default_location_content(math_trap_percentage)
+
+ def input_and_rerender(input_key: Input) -> None:
+ game.input(input_key)
+ print(render_to_text(game))
+
+ def on_press(key: Key | KeyCode | None) -> None:
+ if key in KEY_CONVERSION:
+ input_and_rerender(KEY_CONVERSION[key])
+
+ print(render_to_text(game))
+
+ with keyboard.Listener(on_press=on_press) as listener:
+ while True:
+ listener.join()
diff --git a/worlds/apquest/game/player.py b/worlds/apquest/game/player.py
new file mode 100644
index 000000000000..ff0f45b367be
--- /dev/null
+++ b/worlds/apquest/game/player.py
@@ -0,0 +1,87 @@
+from collections import Counter
+from collections.abc import Callable
+
+from .events import Event, LocationClearedEvent, VictoryEvent
+from .gameboard import Gameboard
+from .graphics import Graphic
+from .inputs import Direction
+from .items import Item
+
+
+class Player:
+ current_x: int
+ current_y: int
+ current_health: int
+
+ has_won: bool = False
+
+ facing: Direction
+
+ inventory: Counter[Item]
+
+ gameboard: Gameboard
+ push_event: Callable[[Event], None]
+
+ def __init__(self, gameboard: Gameboard, push_event: Callable[[Event], None]) -> None:
+ self.gameboard = gameboard
+ self.inventory = Counter()
+ self.push_event = push_event
+ self.respawn()
+
+ def respawn(self) -> None:
+ self.current_x = 4
+ self.current_y = 9
+ self.current_health = self.max_health
+ self.facing = Direction.DOWN
+
+ @property
+ def max_health(self) -> int:
+ return 2 + 2 * self.inventory[Item.HEALTH_UPGRADE]
+
+ def render(self) -> Graphic:
+ if not self.gameboard.ready:
+ return Graphic.EMPTY
+
+ if self.facing == Direction.LEFT:
+ return Graphic.PLAYER_LEFT
+ if self.facing == Direction.UP:
+ return Graphic.PLAYER_UP
+ if self.facing == Direction.RIGHT:
+ return Graphic.PLAYER_RIGHT
+ return Graphic.PLAYER_DOWN
+
+ def receive_item(self, item: Item) -> None:
+ assert item != Item.REMOTE_ITEM, "Player should not directly receive the remote item"
+
+ self.inventory[item] += 1
+ if item == Item.HEALTH_UPGRADE:
+ self.current_health += 2
+
+ def has_item(self, item: Item) -> bool:
+ return self.inventory[item] > 0
+
+ def remove_item(self, item: Item) -> None:
+ self.inventory[item] -= 1
+
+ def damage(self, damage: int) -> None:
+ if self.has_item(Item.SHIELD):
+ damage = damage // 2
+
+ self.current_health = max(0, self.current_health - damage)
+
+ if self.current_health <= 0:
+ self.die()
+
+ def die(self) -> None:
+ self.respawn()
+ self.gameboard.respawn_final_boss()
+ self.gameboard.heal_alive_enemies()
+
+ def location_cleared(self, location_id: int) -> None:
+ event = LocationClearedEvent(location_id)
+ self.push_event(event)
+
+ def victory(self) -> None:
+ self.has_won = True
+ event = VictoryEvent()
+ self.push_event(event)
diff --git a/worlds/apquest/items.py b/worlds/apquest/items.py
new file mode 100644
index 000000000000..abe3dda70b8a
--- /dev/null
+++ b/worlds/apquest/items.py
@@ -0,0 +1,166 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from BaseClasses import Item, ItemClassification
+
+if TYPE_CHECKING:
+ from .world import APQuestWorld
+
+# Every item must have a unique integer ID associated with it.
+# We will have a lookup from item name to ID here that, in world.py, we will import and bind to the world class.
+# Even if an item doesn't exist on specific options, it must be present in this lookup.
+ITEM_NAME_TO_ID = {
+ "Key": 1,
+ "Sword": 2,
+ "Shield": 3,
+ "Hammer": 4,
+ "Health Upgrade": 5,
+ "Confetti Cannon": 6,
+ "Math Trap": 7,
+}
+
+# Items should have a defined default classification.
+# In our case, we will make a dictionary from item name to classification.
+DEFAULT_ITEM_CLASSIFICATIONS = {
+ "Key": ItemClassification.progression,
+ "Sword": ItemClassification.progression | ItemClassification.useful, # Items can have multiple classifications.
+ "Shield": ItemClassification.progression,
+ "Hammer": ItemClassification.progression,
+ "Health Upgrade": ItemClassification.useful,
+ "Confetti Cannon": ItemClassification.filler,
+ "Math Trap": ItemClassification.trap,
+}
+
+
+# Each Item instance must correctly report the "game" it belongs to.
+# To make this simple, it is common practice to subclass the basic Item class and override the "game" field.
+class APQuestItem(Item):
+ game = "APQuest"
+
+
+# Ontop of our regular itempool, our world must be able to create arbitrary amounts of filler as requested by core.
+# To do this, it must define a function called world.get_filler_item_name(), which we will define in world.py later.
+# For now, let's make a function that returns the name of a random filler item here in items.py.
+def get_random_filler_item_name(world: APQuestWorld) -> str:
+ # APQuest has an option called "trap_chance".
+ # This is the percentage chance that each filler item is a Math Trap instead of a Confetti Cannon.
+ # For this purpose, we need to use a random generator.
+
+ # IMPORTANT: Whenever you need to use a random generator, you must use world.random.
+ # This ensures that generating with the same generator seed twice yields the same output.
+ # DO NOT use a bare random object from Python's built-in random module.
+ if world.random.randint(0, 99) < world.options.trap_chance:
+ return "Math Trap"
+ return "Confetti Cannon"
+
+
+def create_item_with_correct_classification(world: APQuestWorld, name: str) -> APQuestItem:
+ # Our world class must have a create_item() function that can create any of our items by name at any time.
+ # So, we make this helper function that creates the item by name with the correct classification.
+ # Note: This function's content could just be the contents of world.create_item in world.py directly,
+ # but it seemed nicer to have it in its own function over here in items.py.
+ classification = DEFAULT_ITEM_CLASSIFICATIONS[name]
+
+ # It is perfectly normal and valid for an item's classification to differ based on the player's options.
+ # In our case, Health Upgrades are only relevant to logic (and thus labeled as "progression") in hard mode.
+ if name == "Health Upgrade" and world.options.hard_mode:
+ classification = ItemClassification.progression
+
+ return APQuestItem(name, classification, ITEM_NAME_TO_ID[name], world.player)
+
+
+# With those two helper functions defined, let's now get to actually creating and submitting our itempool.
+def create_all_items(world: APQuestWorld) -> None:
+ # This is the function in which we will create all the items that this world submits to the multiworld item pool.
+ # There must be exactly as many items as there are locations.
+ # In our case, there are either six or seven locations.
+ # We must make sure that when there are six locations, there are six items,
+ # and when there are seven locations, there are seven items.
+
+ # Creating items should generally be done via the world's create_item method.
+ # First, we create a list containing all the items that always exist.
+
+ itempool: list[Item] = [
+ world.create_item("Key"),
+ world.create_item("Sword"),
+ world.create_item("Shield"),
+ world.create_item("Health Upgrade"),
+ world.create_item("Health Upgrade"),
+ ]
+
+ # Some items may only exist if the player enables certain options.
+ # In our case, If the hammer option is enabled, the sixth item is the Hammer.
+ # Otherwise, we add a filler Confetti Cannon.
+ if world.options.hammer:
+ # Once again, it is important to stress that even though the Hammer doesn't always exist,
+ # it must be present in the worlds item_name_to_id.
+ # Whether it is actually in the itempool is determined purely by whether we create and add the item here.
+ itempool.append(world.create_item("Hammer"))
+
+ # Archipelago requires that each world submits as many locations as it submits items.
+ # This is where we can use our filler and trap items.
+ # APQuest has two of these: The Confetti Cannon and the Math Trap.
+ # (Unfortunately, Archipelago is a bit ambiguous about its terminology here:
+ # "filler" is an ItemClassification separate from "trap", but in a lot of its functions,
+ # Archipelago will use "filler" to just mean "an additional item created to fill out the itempool".
+ # "Filler" in this sense can technically have any ItemClassification,
+ # but most commonly ItemClassification.filler or ItemClassification.trap.
+ # Starting here, the word "filler" will be used to collectively refer to APQuest's Confetti Cannon and Math Trap,
+ # which are ItemClassification.filler and ItemClassification.trap respectively.)
+ # Creating filler items works the same as any other item. But there is a question:
+ # How many filler items do we actually need to create?
+ # In regions.py, we created either six or seven locations depending on the "extra_starting_chest" option.
+ # In this function, we have created five or six items depending on whether the "hammer" option is enabled.
+ # We *could* have a really complicated if-else tree checking the options again, but there is a better way.
+ # We can compare the size of our itempool so far to the number of locations in our world.
+
+ # The length of our itempool is easy to determine, since we have it as a list.
+ number_of_items = len(itempool)
+
+ # The number of locations is also easy to determine, but we have to be careful.
+ # Just calling len(world.get_locations()) would report an incorrect number, because of our *event locations*.
+ # What we actually want is the number of *unfilled* locations. Luckily, there is a helper method for this:
+ number_of_unfilled_locations = len(world.multiworld.get_unfilled_locations(world.player))
+
+ # Now, we just subtract the number of items from the number of locations to get the number of empty item slots.
+ needed_number_of_filler_items = number_of_unfilled_locations - number_of_items
+
+ # Finally, we create that many filler items and add them to the itempool.
+ # To create our filler, we could just use world.create_item("Confetti Cannon").
+ # But there is an alternative that works even better for most worlds, including APQuest.
+ # As discussed above, our world must have a get_filler_item_name() function defined,
+ # which must return the name of an infinitely repeatable filler item.
+ # Defining this function enables the use of a helper function called world.create_filler().
+ # You can just use this function directly to create as many filler items as you need to complete your itempool.
+ itempool += [world.create_filler() for _ in range(needed_number_of_filler_items)]
+
+ # But... is that the right option for your game? Let's explore that.
+ # For some games, the concepts of "regular itempool filler" and "additionally created filler" are different.
+ # These games might want / require specific amounts of specific filler items in their regular pool.
+ # To achieve this, they will have to intentionally create the correct quantities using world.create_item().
+ # They may still use world.create_filler() to fill up the rest of their itempool with "repeatable filler",
+ # after creating their "specific quantity" filler and still having room left over.
+
+ # But there are many other games which *only* have infinitely repeatable filler items.
+ # They don't care about specific amounts of specific filler items, instead only caring about the proportions.
+ # In this case, world.create_filler() can just be used for the entire filler itempool.
+ # APQuest is one of these games:
+ # Regardless of whether it's filler for the regular itempool or additional filler for item links / etc.,
+ # we always just want a Confetti Cannon or a Math Trap depending on the "trap_chance" option.
+ # We defined this behavior in our get_random_filler_item_name() function, which in world.py,
+ # we'll bind to world.get_filler_item_name(). So, we can just use world.create_filler() for all of our filler.
+
+ # Anyway. With our world's itempool finalized, we now need to submit it to the multiworld itempool.
+ # This is how the generator actually knows about the existence of our items.
+ world.multiworld.itempool += itempool
+
+ # Sometimes, you might want the player to start with certain items already in their inventory.
+ # These items are called "precollected items".
+ # They will be sent as soon as they connect for the first time (depending on your client's item handling flag).
+ # Players can add precollected items themselves via the generic "start_inventory" option.
+ # If you want to add your own precollected items, you can do so via world.push_precollected().
+ if world.options.start_with_one_confetti_cannon:
+ # We're adding a filler item, but you can also add progression items to the player's precollected inventory.
+ starting_confetti_cannon = world.create_item("Confetti Cannon")
+ world.push_precollected(starting_confetti_cannon)
diff --git a/worlds/apquest/locations.py b/worlds/apquest/locations.py
new file mode 100644
index 000000000000..553519fa6c8e
--- /dev/null
+++ b/worlds/apquest/locations.py
@@ -0,0 +1,136 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from BaseClasses import ItemClassification, Location
+
+from . import items
+
+if TYPE_CHECKING:
+ from .world import APQuestWorld
+
+# Every location must have a unique integer ID associated with it.
+# We will have a lookup from location name to ID here that, in world.py, we will import and bind to the world class.
+# Even if a location doesn't exist on specific options, it must be present in this lookup.
+LOCATION_NAME_TO_ID = {
+ "Top Left Room Chest": 1,
+ "Top Middle Chest": 2,
+ "Bottom Left Chest": 3,
+ "Bottom Left Extra Chest": 4,
+ "Bottom Right Room Left Chest": 5,
+ "Bottom Right Room Right Chest": 6,
+ # Location IDs don't need to be sequential, as long as they're unique and greater than 0.
+ "Right Room Enemy Drop": 10,
+}
+
+
+# Each Location instance must correctly report the "game" it belongs to.
+# To make this simple, it is common practice to subclass the basic Location class and override the "game" field.
+class APQuestLocation(Location):
+ game = "APQuest"
+
+
+# Let's make one more helper method before we begin actually creating locations.
+# Later on in the code, we'll want specific subsections of LOCATION_NAME_TO_ID.
+# To reduce the chance of copy-paste errors writing something like {"Chest": LOCATION_NAME_TO_ID["Chest"]},
+# let's make a helper method that takes a list of location names and returns them as a dict with their IDs.
+# Note: There is a minor typing quirk here. Some functions want location addresses to be an "int | None",
+# so while our function here only ever returns dict[str, int], we annotate it as dict[str, int | None].
+def get_location_names_with_ids(location_names: list[str]) -> dict[str, int | None]:
+ return {location_name: LOCATION_NAME_TO_ID[location_name] for location_name in location_names}
+
+
+def create_all_locations(world: APQuestWorld) -> None:
+ create_regular_locations(world)
+ create_events(world)
+
+
+def create_regular_locations(world: APQuestWorld) -> None:
+ # Finally, we need to put the Locations ("checks") into their regions.
+ # Once again, before we do anything, we can grab our regions we created by using world.get_region()
+ overworld = world.get_region("Overworld")
+ top_left_room = world.get_region("Top Left Room")
+ bottom_right_room = world.get_region("Bottom Right Room")
+ right_room = world.get_region("Right Room")
+
+ # One way to create locations is by just creating them directly via their constructor.
+ bottom_left_chest = APQuestLocation(
+ world.player, "Bottom Left Chest", world.location_name_to_id["Bottom Left Chest"], overworld
+ )
+
+ # You can then add them to the region.
+ overworld.locations.append(bottom_left_chest)
+
+ # A simpler way to do this is by using the region.add_locations helper.
+ # For this, you need to have a dict of location names to their IDs (i.e. a subset of location_name_to_id)
+ # Aha! So that's why we made that "get_location_names_with_ids" helper method earlier.
+ # You also need to pass your overridden Location class.
+ bottom_right_room_locations = get_location_names_with_ids(
+ ["Bottom Right Room Left Chest", "Bottom Right Room Right Chest"]
+ )
+ bottom_right_room.add_locations(bottom_right_room_locations, APQuestLocation)
+
+ top_left_room_locations = get_location_names_with_ids(["Top Left Room Chest"])
+ top_left_room.add_locations(top_left_room_locations, APQuestLocation)
+
+ right_room_locations = get_location_names_with_ids(["Right Room Enemy Drop"])
+ right_room.add_locations(right_room_locations, APQuestLocation)
+
+ # Locations may be in different regions depending on the player's options.
+ # In our case, the hammer option puts the Top Middle Chest into its own room called Top Middle Room.
+ top_middle_room_locations = get_location_names_with_ids(["Top Middle Chest"])
+ if world.options.hammer:
+ top_middle_room = world.get_region("Top Middle Room")
+ top_middle_room.add_locations(top_middle_room_locations, APQuestLocation)
+ else:
+ overworld.add_locations(top_middle_room_locations, APQuestLocation)
+
+ # Locations may exist only if the player enables certain options.
+ # In our case, the extra_starting_chest option adds the Bottom Left Extra Chest location.
+ if world.options.extra_starting_chest:
+ # Once again, it is important to stress that even though the Bottom Left Extra Chest location doesn't always
+ # exist, it must still always be present in the world's location_name_to_id.
+ # Whether the location actually exists in the seed is purely determined by whether we create and add it here.
+ bottom_left_extra_chest = get_location_names_with_ids(["Bottom Left Extra Chest"])
+ overworld.add_locations(bottom_left_extra_chest, APQuestLocation)
+
+
+def create_events(world: APQuestWorld) -> None:
+ # Sometimes, the player may perform in-game actions that allow them to progress which are not related to Items.
+ # In our case, the player must press a button in the top left room to open the final boss door.
+ # AP has something for this purpose: "Event locations" and "Event items".
+ # An event location is no different than a regular location, except it has the address "None".
+ # It is treated during generation like any other location, but then it is discarded.
+ # This location cannot be "sent" and its item cannot be "received", but the item can be used in logic rules.
+ # Since we are creating more locations and adding them to regions, we need to grab those regions again first.
+ top_left_room = world.get_region("Top Left Room")
+ final_boss_room = world.get_region("Final Boss Room")
+
+ # One way to create an event is simply to use one of the normal methods of creating a location.
+ button_in_top_left_room = APQuestLocation(world.player, "Top Left Room Button", None, top_left_room)
+ top_left_room.locations.append(button_in_top_left_room)
+
+ # We then need to put an event item onto the location.
+ # An event item is an item whose code is "None" (same as the event location's address),
+ # and whose classification is "progression". Item creation will be discussed more in items.py.
+ # Note: Usually, items are created in world.create_items(), which for us happens in items.py.
+ # However, when the location of an item is known ahead of time (as is the case with an event location/item pair),
+ # it is common practice to create the item when creating the location.
+ # Since locations also have to be finalized after world.create_regions(), which runs before world.create_items(),
+ # we'll create both the event location and the event item in our locations.py code.
+ button_item = items.APQuestItem("Top Left Room Button Pressed", ItemClassification.progression, None, world.player)
+ button_in_top_left_room.place_locked_item(button_item)
+
+ # A way simpler way to do create an event location/item pair is by using the region.create_event helper.
+ # Luckily, we have another event we want to create: The Victory event.
+ # We will use this event to track whether the player can win the game.
+ # The Victory event is a completely optional abstraction - This will be discussed more in set_rules().
+ final_boss_room.add_event(
+ "Final Boss Defeated", "Victory", location_type=APQuestLocation, item_type=items.APQuestItem
+ )
+
+ # If you create all your regions and locations line-by-line like this,
+ # the length of your create_regions might get out of hand.
+ # Many worlds use more data-driven approaches using dataclasses or NamedTuples.
+ # However, it is worth understanding how the actual creation of regions and locations works,
+ # That way, we're not just mindlessly copy-pasting! :)
diff --git a/worlds/apquest/options.py b/worlds/apquest/options.py
new file mode 100644
index 000000000000..00fe9ed34eaf
--- /dev/null
+++ b/worlds/apquest/options.py
@@ -0,0 +1,152 @@
+from dataclasses import dataclass
+
+from Options import Choice, OptionGroup, PerGameCommonOptions, Range, Toggle
+
+# In this file, we define the options the player can pick.
+# The most common types of options are Toggle, Range and Choice.
+
+# Options will be in the game's template yaml.
+# They will be represented by checkboxes, sliders etc. on the game's options page on the website.
+# (Note: Options can also be made invisible from either of these places by overriding Option.visibility.
+# APQuest doesn't have an example of this, but this can be used for secret / hidden / advanced options.)
+
+# For further reading on options, you can also read the Options API Document:
+# https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/options%20api.md
+
+
+# The first type of Option we'll discuss is the Toggle.
+# A toggle is an option that can either be on or off. This will be represented by a checkbox on the website.
+# The default for a toggle is "off".
+# If you want a toggle to be on by default, you can use the "DefaultOnToggle" class instead of the "Toggle" class.
+class HardMode(Toggle):
+ """
+ In hard mode, the basic enemy and the final boss will have more health.
+ The Health Upgrades become progression, as they are now required to beat the final boss.
+ """
+
+ # The docstring of an option is used as the description on the website and in the template yaml.
+
+ # You'll also want to set a display name, which will determine what the option is called on the website.
+ display_name = "Hard Mode"
+
+
+class Hammer(Toggle):
+ """
+ Adds another item to the itempool: The Hammer.
+ The top middle chest will now be locked behind a breakable wall, requiring the Hammer.
+ """
+
+ display_name = "Hammer"
+
+
+class ExtraStartingChest(Toggle):
+ """
+ Adds an extra chest in the bottom left, making room for an extra Confetti Cannon.
+ """
+
+ display_name = "Extra Starting Chest"
+
+
+class TrapChance(Range):
+ """
+ Percentage chance that any given Confetti Cannon will be replaced by a Math Trap.
+ """
+
+ display_name = "Trap Chance"
+
+ range_start = 0
+ range_end = 100
+ default = 0
+
+
+class StartWithOneConfettiCannon(Toggle):
+ """
+ Start with a confetti cannon already in your inventory.
+ Why? Because you deserve it. You get to celebrate yourself without doing any work first.
+ """
+
+ display_name = "Start With One Confetti Cannon"
+
+
+# A Range is a numeric option with a min and max value. This will be represented by a slider on the website.
+class ConfettiExplosiveness(Range):
+ """
+ How much confetti each use of a confetti cannon will fire.
+ """
+
+ display_name = "Confetti Explosiveness"
+
+ range_start = 0
+ range_end = 10
+
+ # Range options must define an explicit default value.
+ default = 3
+
+
+# A Choice is an option with multiple discrete choices. This will be represented by a dropdown on the website.
+class PlayerSprite(Choice):
+ """
+ The sprite that the player will have.
+ """
+
+ display_name = "Player Sprite"
+
+ option_human = 0
+ option_duck = 1
+ option_horse = 2
+ option_cat = 3
+
+ # Choice options must define an explicit default value.
+ default = option_human
+
+ # For choices, you can also define aliases.
+ # For example, we could make it so "player_sprite: kitty" resolves to "player_sprite: cat" like this:
+ alias_kitty = option_cat
+
+
+# We must now define a dataclass inheriting from PerGameCommonOptions that we put all our options in.
+# This is in the format "option_name_in_snake_case: OptionClassName".
+@dataclass
+class APQuestOptions(PerGameCommonOptions):
+ hard_mode: HardMode
+ hammer: Hammer
+ extra_starting_chest: ExtraStartingChest
+ start_with_one_confetti_cannon: StartWithOneConfettiCannon
+ trap_chance: TrapChance
+ confetti_explosiveness: ConfettiExplosiveness
+ player_sprite: PlayerSprite
+
+
+# If we want to group our options by similar type, we can do so as well. This looks nice on the website.
+option_groups = [
+ OptionGroup(
+ "Gameplay Options",
+ [HardMode, Hammer, ExtraStartingChest, StartWithOneConfettiCannon, TrapChance],
+ ),
+ OptionGroup(
+ "Aesthetic Options",
+ [ConfettiExplosiveness, PlayerSprite],
+ ),
+]
+
+# Finally, we can define some option presets if we want the player to be able to quickly choose a specific "mode".
+option_presets = {
+ "boring": {
+ "hard_mode": False,
+ "hammer": False,
+ "extra_starting_chest": False,
+ "start_with_one_confetti_cannon": False,
+ "trap_chance": 0,
+ "confetti_explosiveness": ConfettiExplosiveness.range_start,
+ "player_sprite": PlayerSprite.option_human,
+ },
+ "the true way to play": {
+ "hard_mode": True,
+ "hammer": True,
+ "extra_starting_chest": True,
+ "start_with_one_confetti_cannon": True,
+ "trap_chance": 50,
+ "confetti_explosiveness": ConfettiExplosiveness.range_end,
+ "player_sprite": PlayerSprite.option_duck,
+ },
+}
diff --git a/worlds/apquest/regions.py b/worlds/apquest/regions.py
new file mode 100644
index 000000000000..b002f551ffa1
--- /dev/null
+++ b/worlds/apquest/regions.py
@@ -0,0 +1,79 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from BaseClasses import Entrance, Region
+
+if TYPE_CHECKING:
+ from .world import APQuestWorld
+
+# A region is a container for locations ("checks"), which connects to other regions via "Entrance" objects.
+# Many games will model their Regions after physical in-game places, but you can also have more abstract regions.
+# For a location to be in logic, its containing region must be reachable.
+# The Entrances connecting regions can have rules - more on that in rules.py.
+# This makes regions especially useful for traversal logic ("Can the player reach this part of the map?")
+
+# Every location must be inside a region, and you must have at least one region.
+# This is why we create regions first, and then later we create the locations (in locations.py).
+
+
+def create_and_connect_regions(world: APQuestWorld) -> None:
+ create_all_regions(world)
+ connect_regions(world)
+
+
+def create_all_regions(world: APQuestWorld) -> None:
+ # Creating a region is as simple as calling the constructor of the Region class.
+ overworld = Region("Overworld", world.player, world.multiworld)
+ top_left_room = Region("Top Left Room", world.player, world.multiworld)
+ bottom_right_room = Region("Bottom Right Room", world.player, world.multiworld)
+ right_room = Region("Right Room", world.player, world.multiworld)
+ final_boss_room = Region("Final Boss Room", world.player, world.multiworld)
+
+ # Let's put all these regions in a list.
+ regions = [overworld, top_left_room, bottom_right_room, right_room, final_boss_room]
+
+ # Some regions may only exist if the player enables certain options.
+ # In our case, the Hammer locks the top middle chest in its own room if the hammer option is enabled.
+ if world.options.hammer:
+ top_middle_room = Region("Top Middle Room", world.player, world.multiworld)
+ regions.append(top_middle_room)
+
+ # We now need to add these regions to multiworld.regions so that AP knows about their existence.
+ world.multiworld.regions += regions
+
+
+def connect_regions(world: APQuestWorld) -> None:
+ # We have regions now, but still need to connect them to each other.
+ # But wait, we no longer have access to the region variables we created in create_all_regions()!
+ # Luckily, once you've submitted your regions to multiworld.regions,
+ # you can get them at any time using world.get_region(...).
+ overworld = world.get_region("Overworld")
+ top_left_room = world.get_region("Top Left Room")
+ bottom_right_room = world.get_region("Bottom Right Room")
+ right_room = world.get_region("Right Room")
+ final_boss_room = world.get_region("Final Boss Room")
+
+ # Okay, now we can get connecting. For this, we need to create Entrances.
+ # Entrances are inherently one-way, but crucially, AP assumes you can always return to the origin region.
+ # One way to create an Entrance is by calling the Entrance constructor.
+ overworld_to_bottom_right_room = Entrance(world.player, "Overworld to Bottom Right Room", parent=overworld)
+ overworld.exits.append(overworld_to_bottom_right_room)
+
+ # You can then connect the Entrance to the target region.
+ overworld_to_bottom_right_room.connect(bottom_right_room)
+
+ # An even easier way is to use the region.connect helper.
+ overworld.connect(right_room, "Overworld to Right Room")
+ right_room.connect(final_boss_room, "Right Room to Final Boss Room")
+
+ # The region.connect helper even allows adding a rule immediately.
+ # We'll talk more about rule creation in the set_all_rules() function in rules.py.
+ overworld.connect(top_left_room, "Overworld to Top Left Room", lambda state: state.has("Key", world.player))
+
+ # Some Entrances may only exist if the player enables certain options.
+ # In our case, the Hammer locks the top middle chest in its own room if the hammer option is enabled.
+ # In this case, we previously created an extra "Top Middle Room" region that we now need to connect to Overworld.
+ if world.options.hammer:
+ top_middle_room = world.get_region("Top Middle Room")
+ overworld.connect(top_middle_room, "Overworld to Top Middle Room")
diff --git a/worlds/apquest/rules.py b/worlds/apquest/rules.py
new file mode 100644
index 000000000000..533c33d5eac2
--- /dev/null
+++ b/worlds/apquest/rules.py
@@ -0,0 +1,131 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from BaseClasses import CollectionState
+from worlds.generic.Rules import add_rule, set_rule
+
+if TYPE_CHECKING:
+ from .world import APQuestWorld
+
+
+def set_all_rules(world: APQuestWorld) -> None:
+ # In order for AP to generate an item layout that is actually possible for the player to complete,
+ # we need to define rules for our Entrances and Locations.
+ # Note: Regions do not have rules, the Entrances connecting them do!
+ # We'll do entrances first, then locations, and then finally we set our victory condition.
+
+ set_all_entrance_rules(world)
+ set_all_location_rules(world)
+ set_completion_condition(world)
+
+
+def set_all_entrance_rules(world: APQuestWorld) -> None:
+ # First, we need to actually grab our entrances. Luckily, there is a helper method for this.
+ overworld_to_bottom_right_room = world.get_entrance("Overworld to Bottom Right Room")
+ overworld_to_top_left_room = world.get_entrance("Overworld to Top Left Room")
+ right_room_to_final_boss_room = world.get_entrance("Right Room to Final Boss Room")
+
+ # An access rule is a function. We can define this function like any other function.
+ # This function must accept exactly one parameter: A "CollectionState".
+ # A CollectionState describes the current progress of the players in the multiworld, i.e. what items they have,
+ # which regions they've reached, etc.
+ # In an access rule, we can ask whether the player has a collected a certain item.
+ # We can do this via the state.has(...) function.
+ # This function takes an item name, a player number, and an optional count parameter (more on that below)
+ # Since a rule only takes a CollectionState parameter, but we also need the player number in the state.has call,
+ # our function needs to be locally defined so that it has access to the player number from the outer scope.
+ # In our case, we are inside a function that has access to the "world" parameter, so we can use world.player.
+ def can_destroy_bush(state: CollectionState) -> bool:
+ return state.has("Sword", world.player)
+
+ # Now we can set our "can_destroy_bush" rule to our entrance which requires slashing a bush to clear the path.
+ # One way to set rules is via the set_rule() function, which works on both Entrances and Locations.
+ set_rule(overworld_to_bottom_right_room, can_destroy_bush)
+
+ # Because the function has to be defined locally, most worlds prefer the lambda syntax.
+ set_rule(overworld_to_top_left_room, lambda state: state.has("Key", world.player))
+
+ # Conditions can depend on event items.
+ set_rule(right_room_to_final_boss_room, lambda state: state.has("Top Left Room Button Pressed", world.player))
+
+ # Some entrance rules may only apply if the player enabled certain options.
+ # In our case, if the hammer option is enabled, we need to add the Hammer requirement to the Entrance from
+ # Overworld to the Top Middle Room.
+ if world.options.hammer:
+ overworld_to_top_middle_room = world.get_entrance("Overworld to Top Middle Room")
+ set_rule(overworld_to_top_middle_room, lambda state: state.has("Hammer", world.player))
+
+
+def set_all_location_rules(world: APQuestWorld) -> None:
+ # Location rules work no differently from Entrance rules.
+ # Most of our locations are chests that can simply be opened by walking up to them.
+ # Thus, their logical requirements are covered by the Entrance rules of the Entrances that were required to
+ # reach the region that the chest sits in.
+ # However, our two enemies work differently.
+ # Entering the room with the enemy is not enough, you also need to have enough combat items to be able to defeat it.
+ # So, we need to set requirements on the Locations themselves.
+ # Since combat is a bit more complicated, we'll use this chance to cover some advanced access rule concepts.
+
+ # Sometimes, you may want to have different rules depending on the player's chosen options.
+ # There is a wrong way to do this, and a right way to do this. Let's do the wrong way first.
+ right_room_enemy = world.get_location("Right Room Enemy Drop")
+
+ # DON'T DO THIS!!!!
+ set_rule(
+ right_room_enemy,
+ lambda state: (
+ state.has("Sword", world.player)
+ and (not world.options.hard_mode or state.has_any(("Shield", "Health Upgrade"), world.player))
+ ),
+ )
+ # DON'T DO THIS!!!!
+
+ # Now, what's actually wrong with this? It works perfectly fine, right?
+ # If hard mode disabled, Sword is enough. If hard mode is enabled, we also need a Shield or a Health Upgrade.
+ # The access rule we just wrote does this correctly, so what's the problem?
+ # The problem is performance.
+ # Most of your world code doesn't need to be perfectly performant, since it just runs once per slot.
+ # However, access rules in particular are by far the hottest code path in Archipelago.
+ # An access rule will potentially be called thousands or even millions of times over the course of one generation.
+ # As a result, access rules are the one place where it's really worth putting in some effort to optimize.
+ # What's the performance problem here?
+ # Every time our access rule is called, it has to evaluate whether world.options.hard_mode is True or False.
+ # Wouldn't it be better if in easy mode, the access rule only checked for Sword to begin with?
+ # Wouldn't it also be better if in hard mode, it already knew it had to check Shield and Health Upgrade as well?
+ # Well, we can achieve this by doing the "if world.options.hard_mode" check outside the set_rule call,
+ # and instead having two *different* set_rule calls depending on which case we're in.
+
+ if world.options.hard_mode:
+ # If you have multiple conditions, you can obviously chain them via "or" or "and".
+ # However, there are also the nice helper functions "state.has_any" and "state.has_all".
+ set_rule(
+ right_room_enemy,
+ lambda state: (
+ state.has("Sword", world.player) and state.has_any(("Shield", "Health Upgrade"), world.player)
+ ),
+ )
+ else:
+ set_rule(right_room_enemy, lambda state: state.has("Sword", world.player))
+
+ # Another way to chain multiple conditions is via the add_rule function.
+ # This makes the access rules a bit slower though, so it should only be used if your structure justifies it.
+ # In our case, it's pretty useful because hard mode and easy mode have different requirements.
+ final_boss = world.get_location("Final Boss Defeated")
+
+ # For the "known" requirements, it's still better to chain them using a normal "and" condition.
+ add_rule(final_boss, lambda state: state.has_all(("Sword", "Shield"), world.player))
+
+ if world.options.hard_mode:
+ # You can check for multiple copies of an item by using the optional count parameter of state.has().
+ add_rule(final_boss, lambda state: state.has("Health Upgrade", world.player, 2))
+
+
+def set_completion_condition(world: APQuestWorld) -> None:
+ # Finally, we need to set a completion condition for our world, defining what the player needs to win the game.
+ # You can just set a completion condition directly like any other condition, referencing items the player receives:
+ world.multiworld.completion_condition[world.player] = lambda state: state.has_all(("Sword", "Shield"), world.player)
+
+ # In our case, we went for the Victory event design pattern (see create_events() in locations.py).
+ # So lets undo what we just did, and instead set the completion condition to:
+ world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.player)
diff --git a/worlds/apquest/test/__init__.py b/worlds/apquest/test/__init__.py
new file mode 100644
index 000000000000..b2e5d0d383d7
--- /dev/null
+++ b/worlds/apquest/test/__init__.py
@@ -0,0 +1,7 @@
+# The __init__.py file of the test directory should be empty.
+# (Before you say it: Comments are fine, smart*ss ;D)
+
+# You'll want to start with reading bases.py.
+
+# If you want to read more about tests, there is also the "Tests" section of the World API document:
+# https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/world%20api.md#tests
diff --git a/worlds/apquest/test/bases.py b/worlds/apquest/test/bases.py
new file mode 100644
index 000000000000..641a85e2d9ef
--- /dev/null
+++ b/worlds/apquest/test/bases.py
@@ -0,0 +1,26 @@
+from test.bases import WorldTestBase
+
+from ..world import APQuestWorld
+
+# Tests are a big topic.
+# The testing API and the core code in general empower you to test all kinds of complicated custom behavior.
+# However, for APQuest, we'll stick to some of the more basic tests.
+
+
+# Most of your testing will probably be done using the generic WorldTestBase.
+# WorldTestBase is a class that performs a set of generic tests on your world using a given set of options.
+# It also enables you to write custom tests with a slew of generic helper functions.
+# The first thing you'll want to do is subclass it. You'll want to override "game" And "world" like this.
+class APQuestTestBase(WorldTestBase):
+ game = "APQuest"
+ world: APQuestWorld
+
+
+# The actual tests you write should be in files whose names start with "test_".
+# Ideally, you should group similar tests together in one file, where each file has some overarching significance.
+
+# The best order to read these tests in is:
+# 1. test_easy_mode.py
+# 2. test_hard_mode.py
+# 3. test_extra_starting_chest.py
+# 4. test_hammer.py
diff --git a/worlds/apquest/test/test_easy_mode.py b/worlds/apquest/test/test_easy_mode.py
new file mode 100644
index 000000000000..baf3055bbc89
--- /dev/null
+++ b/worlds/apquest/test/test_easy_mode.py
@@ -0,0 +1,106 @@
+from .bases import APQuestTestBase
+
+
+# When writing a test, you'll first need to subclass unittest.TestCase.
+# In our case, we'll subclass the APQuestTestBase we defined in bases.py.
+class TestEasyModeLogic(APQuestTestBase):
+ # Our test base is a subclass of WorldTestBase.
+ # WorldTestBase takes a dict of options and sets up a multiworld for you with a single world of your game.
+ # The world will have the options you specified.
+ options = {
+ "hard_mode": False,
+ # Options you don't specify will use their default values.
+ # It is good practice to specify every option that has an impact on your test, even when it's the default value.
+ # As such, we'll spell out that hard_mode is meant to be False.
+ # All other options in APQuest are cosmetic, so we don't need to list them.
+ }
+
+ # At this point, we could stop, and a few default tests would be run on our world.
+ # At the time of writing (2025-09-04), this includes the following tests:
+ # - If you have every item, every location can be reached
+ # - If you have no items, you can still reach something ("Sphere 1" is not empty)
+ # - The world successfully generates (Fill does not crash)
+
+ # This is already useful, but we also want to do our own tests.
+ # A test is a function whose name starts with "test".
+ def test_easy_mode_access(self) -> None:
+ # Inside a test, we can manually collect items, check access rules, etc.
+ # For example, we could check that the two early chests are already accessible despite us having no items.
+ # For the sake of structure, let's have every test item in its own subtest.
+ with self.subTest("Test checks accessible with nothing"):
+ bottom_left_chest = self.world.get_location("Bottom Left Chest")
+ top_middle_chest = self.world.get_location("Top Middle Chest")
+
+ # Since access rules have a "state" argument, we must pass our current CollectionState.
+ # Helpfully, since we're in a WorldTestBase, we can just use "self.multiworld.state".
+ self.assertTrue(bottom_left_chest.can_reach(self.multiworld.state))
+ self.assertTrue(top_middle_chest.can_reach(self.multiworld.state))
+
+ # Next, let's test that the top left room location requires the key to unlock the door.
+ with self.subTest("Test key is required to get top left chest"):
+ top_left_room_chest = self.world.get_location("Top Left Room Chest")
+
+ # Right now, this location should *not* be accessible, as we don't have the key yet.
+ self.assertFalse(top_left_room_chest.can_reach(self.multiworld.state))
+
+ # Now, let's collect the Key.
+ # For this, there is a handy helper function to collect items from the itempool.
+ # Keep in mind that while test functions are sectioned off from one another, subtests are not.
+ # Collecting this here means that the state will have the Key for all future subtests in this function.
+ self.collect_by_name("Key")
+
+ # The top left room chest should now be accessible.
+ self.assertTrue(top_left_room_chest.can_reach(self.multiworld.state))
+
+ # Next, let's test that you need the sword to access locations that require it (bush room and enemies).
+ with self.subTest("Test sword is required for enemy and bush locations"):
+ # Manually checking the dependency in the previous function was a bit of a hassle, wasn't it?
+ # Now we are checking four locations. It would be even longer as a result.
+ # Well, there is another option. It's the assertAccessDependency function of WorldTestBase.
+ self.assertAccessDependency(
+ [
+ "Bottom Right Room Right Chest",
+ "Bottom Right Room Left Chest",
+ "Right Room Enemy Drop",
+ "Final Boss Defeated", # Reminder: APQuest's victory condition uses this event location
+ ],
+ [["Sword"]],
+ )
+
+ # The assertAccessDependency function is a bit complicated, so let's discuss what it does.
+ # By default, the locations argument must contain *every* location that *hard-depends* on the items.
+ # So, in our case: If every item except Sword is collected, *exactly* these four locations are unreachable.
+
+ # The possible_items argument is initially more intuitive, but has some complexity as well.
+ # In our case, we only care about one item. But sometimes, we care about multiple items at once.
+ # This is why we pass a list of lists. We'll discuss this more when we test hard mode logic.
+
+ # Let's do one more test: That the key is required for the Button.
+ with self.subTest("Test that the Key is required to activate the Button"):
+ # The Button is not the only thing that depends on the Key.
+ # As explained above, the locations list must be exhaustive.
+ # Thus, we would have to add the "Top Left Room Chest" as well.
+ # However, we can set "only_check_listed" if we only want the Top Left Room Button location to be checked.
+ self.assertAccessDependency(
+ ["Top Left Room Button"],
+ [["Key"]],
+ only_check_listed=True,
+ )
+
+ def test_easy_mode_health_upgrades(self) -> None:
+ # For our second test, let's make sure that we have two Health Upgrades with the correct classification.
+
+ # We can find the Health Upgrades in the itempool like this:
+ health_upgrades = self.get_items_by_name("Health Upgrade")
+
+ # First, let's verify there's two of them.
+ with self.subTest("Test that there are two Health Upgrades in the pool"):
+ self.assertEqual(len(health_upgrades), 2)
+
+ # Then, let's verify that they have the useful classification and NOT the progression classification.
+ with self.subTest("Test that the Health Upgrades in the pool are useful, but not progression."):
+ # To check whether an item has a certain classification, you can use the following helper properties:
+ # item.filler, item.trap, item.useful and... item.advancement. No, not item.progression...
+ # (Just go with it, AP is old and has had many name changes over the years :D)
+ self.assertTrue(all(health_upgrade.useful for health_upgrade in health_upgrades))
+ self.assertFalse(any(health_upgrade.advancement for health_upgrade in health_upgrades))
diff --git a/worlds/apquest/test/test_extra_starting_chest.py b/worlds/apquest/test/test_extra_starting_chest.py
new file mode 100644
index 000000000000..22c01620b019
--- /dev/null
+++ b/worlds/apquest/test/test_extra_starting_chest.py
@@ -0,0 +1,39 @@
+from .bases import APQuestTestBase
+
+
+# Sometimes, you might want to test something with a specific option disabled, then with it enabled.
+# For this purpose, we'll just have two different TestCase classes.
+class TestExtraStartingChestOff(APQuestTestBase):
+ options = {
+ "extra_starting_chest": False,
+ }
+
+ # Hmm... This is just default options again.
+ # This would run all the default WorldTestBase tests a second time on default options. That's a bit wasteful.
+ # Luckily, there is a way to turn off the default tests for a WorldTestBase subclass:
+ run_default_tests = False
+
+ # Since the extra_starting_chest option is False, we'll verify that the Extra Starting Chest location doesn't exist.
+ def test_extra_starting_chest_doesnt_exit(self) -> None:
+ # Currently, the best way to check for the existence of a location is to try using get_location,
+ # then watch for the KeyError that is raised if the location doesn't exist.
+ # In a testing context, we can do this with TestCase.assertRaises.
+ self.assertRaises(KeyError, self.world.get_location, "Bottom Left Extra Chest")
+
+
+class TestExtraStartingChestOn(APQuestTestBase):
+ options = {
+ "extra_starting_chest": True,
+ }
+
+ # In this case, running the default tests is acceptable, since this is a unique options combination.
+
+ # Since the extra_starting_chest option is True, we'll verify that the Extra Starting Chest location exists.
+ def test_extra_starting_chest_exists(self) -> None:
+ # In this case, the location *should* exist, so world.get_location() should *not* KeyError.
+ # This is a bit awkward, because unittest.TestCase doesn't have an "assertNotRaises".
+ # So, we'll catch the KeyError ourselves, and then fail in the catch block with a custom message.
+ try:
+ self.world.get_location("Bottom Left Extra Chest")
+ except KeyError:
+ self.fail("Bottom Left Extra Chest should exist, but it doesn't.")
diff --git a/worlds/apquest/test/test_hammer.py b/worlds/apquest/test/test_hammer.py
new file mode 100644
index 000000000000..4bf55523b67b
--- /dev/null
+++ b/worlds/apquest/test/test_hammer.py
@@ -0,0 +1,64 @@
+from .bases import APQuestTestBase
+
+
+class TestHammerOff(APQuestTestBase):
+ options = {
+ "hammer": False,
+ }
+
+ # Once again, this is just default settings, so running the default tests would be wasteful.
+ run_default_tests = False
+
+ # The hammer option adds the Hammer item to the itempool.
+ # Since the hammer option is off in this TestCase, we have to verify that the Hammer is *not* in the itempool.
+ def test_hammer_doesnt_exist(self) -> None:
+ # An easy way to verify that an item is or is not in the itempool is by using WorldTestBase.get_items_by_name().
+ # This will return a list of all matching items, which we can check for its length.
+ hammers_in_itempool = self.get_items_by_name("Hammer")
+ self.assertEqual(len(hammers_in_itempool), 0)
+
+ # If the hammer option is not enabled, the Top Middle Chest should just be accessible with nothing.
+ def test_hammer_is_not_required_for_top_middle_chest(self) -> None:
+ # To check whether an item is required for a location, we would use self.assertAccessDependency.
+ # However, in this case, we want to check that the Hammer *isn't* required for the Top Middle Chest location.
+ # The robust way to do this is to collect every item into the state except for the Hammer,
+ # then assert that the location is reachable.
+ # Luckily, there is a helper for this: "collect_all_but".
+ self.collect_all_but("Hammer")
+
+ # Now, we manually check that the location is accessible using location.can_reach(state):
+ top_middle_chest_player_one = self.world.get_location("Top Middle Chest")
+ self.assertTrue(top_middle_chest_player_one.can_reach(self.multiworld.state))
+
+
+class TestHammerOn(APQuestTestBase):
+ options = {
+ "hammer": True,
+ }
+
+ # When the hammer option is on, the Hammer should exist in the itempool. Let's verify that.
+ def test_hammer_exists(self) -> None:
+ # Nothing new to say here, but I do want to take this opportunity to teach you some Python magic. :D
+ # In Python, when you check for the truth value of something that isn't a bool,
+ # it will be implicitly converted to a bool automatically.
+ # Which instances of a class convert to "False" and which convert to "True" is class-specific.
+ # In the case of lists (or containers in general), empty means False, and not-empty means True.
+ # bool([]) -> False
+ # bool([1, 2, 3]) -> True
+ # So, after grabbing all instances of the Hammer item from the itempool as a list ...
+ hammers_in_itempool = self.get_items_by_name("Hammer")
+
+ # ... instead of checking that the len() is 1, we can run this absolutely beautiful statement instead:
+ self.assertTrue(hammers_in_itempool)
+
+ # I love Python <3
+
+ # When the hammer option is on, the Hammer is required for the Top Middle Chest.
+ def test_hammer_is_required_for_top_middle_chest(self) -> None:
+ # This case is simple again: Just run self.assertAccessDependency()
+ self.assertAccessDependency(["Top Middle Chest"], [["Hammer"]])
+
+ # This unit test genuinely found an error in the world code when it was first written!
+ # The Hammer logic was not actually being correctly applied even if the hammer option was enabled,
+ # and the generator thought Top Middle Chest was considered accessible without the Hammer.
+ # This is why testing can be extremely valuable.
diff --git a/worlds/apquest/test/test_hard_mode.py b/worlds/apquest/test/test_hard_mode.py
new file mode 100644
index 000000000000..fc2f7acdf29e
--- /dev/null
+++ b/worlds/apquest/test/test_hard_mode.py
@@ -0,0 +1,117 @@
+from .bases import APQuestTestBase
+
+
+class TestHardMode(APQuestTestBase):
+ options = {
+ "hard_mode": True,
+ }
+
+ def test_hard_mode_access(self) -> None:
+ # For the sake of brevity, we won't repeat anything we tested in easy mode.
+ # Instead, let's use this opportunity to talk a bit more about assertAccessDependency.
+
+ # Let's take the Enemy Drop location.
+ # In hard mode, the Enemy has two health. One swipe of the Sword does not kill it.
+ # This means that the Enemy has a chance to attack you back.
+ # If you only have the Sword, this attack kills you. After respawning, the Enemy has full health again.
+ # However, if you have a Shield, you can block the attack (resulting in half damage).
+ # Alternatively, if you have found a Health Upgrade, you can tank an extra hit.
+
+ # Why is this important?
+ # If we called assertAccessDependency with ["Right Room Enemy Drop"] and [["Shield"]], it would actually *fail*.
+ # This is because "Right Room Enemy Drop" is beatable without "Shield" - You can use "Health Upgrade" instead.
+ # However, we can call assertAccessDependency with *both* items like this:
+
+ with self.subTest("Test that you need either Shield or Health Upgrade to beat the Right Room Enemy"):
+ self.assertAccessDependency(
+ ["Right Room Enemy Drop"],
+ [["Shield"], ["Health Upgrade"]],
+ only_check_listed=True,
+ )
+
+ # This tests that:
+ # 1. No Shield & No Health Upgrades -> Right Room Enemy Drop is not reachable.
+ # 2. Shield & No Health Upgrades -> Right Room Enemy Drop is reachable.
+ # 3. No Shield & All Health Upgrades -> Right Room Enemy Drop is reachable.
+
+ # Note: Every other item that isn't the Shield nor a Health Upgrade is collected into state.
+ # This even includes pre-placed items, which notably includes any event location/item pairs you created.
+ # In our case, it means we don't have to mention the Sword. By omitting it, it's assumed that we have it.
+
+ # This explains why the possible_items parameter is a list, but not why it's a list of lists.
+ # Let's look at the Final Boss Location. This location requires Sword, Shield, and both Health Upgrades.
+ # We could implement it like this:
+ with self.subTest("Test that the final boss isn't beatable without Sword, Shield, and both Health Upgrades"):
+ self.assertAccessDependency(
+ ["Final Boss Defeated"],
+ [["Sword", "Shield", "Health Upgrade"]],
+ only_check_listed=True,
+ )
+
+ # This would now test the following:
+ # 1. Without Sword, nor Shield, nor any Health Upgrades, the final boss is not beatable.
+ # 2. With Sword, Shield, and all Health Upgrades, the final boss is beatable.
+
+ # But, it's not really advisable to do this.
+ # Think about it: If we implemented our logic incorrectly and forgot to add the Shield requirement,
+ # this call would still pass. We'd rather make sure that each item individually is required:
+ for item in ["Sword", "Shield", "Health Upgrade"]:
+ with self.subTest(f"Test that the final boss requires {item}"):
+ self.assertAccessDependency(
+ ["Final Boss Defeated"],
+ [[item]],
+ only_check_listed=True,
+ )
+
+ # This now tests that:
+ # 1. Without Sword, you can't beat the Final Boss
+ # 2. With Sword, you can beat the Final Boss (if you have everything else)
+ # 3. Without Shield, you can't beat the Final Boss
+ # 4. With Shield, you can beat the Final Boss (if you have everything else)
+ # 5. Without Health Upgrades, you can't beat the Final Boss
+ # 6. With all Health Upgrades, you can beat the Final Boss (if you have everything else)
+
+ # 2., 4., and 6. are the exact same check, so it is a bit redundant.
+ # But crucially, we are ensuring that all three items are actually required.
+
+ # So that's not really why the inner elements are lists.
+ # So we ask again: Why are they lists? When is it ever useful?
+ # Fair warning: This is probably where you should stop reading this and skip to test_hard_mode_health_upgrades.
+ # But if you really want to know why:
+
+ # Having multiple elements in the inner lists is something that only comes up in more complex scenarios.
+ # APQuest doesn't have any of these scenarios, but let's imagine one for completeness' sake.
+ # Currently, the Enemy can be beaten with these item combinations:
+ # 1. Sword and Shield
+ # 2. Sword and Health Upgrade
+ # Let's say there was also a "Shield Bash". When using the Shield Bash, you cannot use the Shield to defend.
+ # This would mean there is a third valid combination:
+ # 3. Shield + Health Upgrade
+ # We have set up a scenario where none of the three items are enough on their own,
+ # but any combination of two items works.
+ # The best way to test this would be to call assertAccessDependency with:
+ # [["Sword", "Shield"], ["Sword", "Health Upgrade"], ["Shield", "Health Upgrade"]]
+ # If we omitted any item from any of the three sub-lists, the check would fail.
+ # This is because the item is still *mentioned* in one of the other two conditions,
+ # meaning it is not collected into state.
+ # Thus, this term cannot be simplified any further without testing something different to what we want to test.
+
+ # You can kinda think of assertAccessDependency as an OR(AND(item_list_1), AND(item_list_2), ...).
+ # Except this "AND" is a special "AND" which allows reducing each list to a single representative item.
+ # And also, the "OR" is special as well in that has to be exhaustive,
+ # where the set of completely unmentioned items must *not* be able to reach the location collectively.
+ # And *also*, each "AND" must be enough to access the location *out of the mentioned items*.
+ # ... I'm not sure this explanation helps anyone, but most of the time, you really don't have to think about it.
+
+ def test_hard_mode_health_upgrades(self) -> None:
+ # We'll also repeat our Health Upgrade test from the Easy Mode test case, but modified for Hard Mode.
+ # This will not be explained again here.
+
+ health_upgrades = self.get_items_by_name("Health Upgrade")
+
+ with self.subTest("Test that there are two Health Upgrades in the pool"):
+ self.assertEqual(len(health_upgrades), 2)
+
+ with self.subTest("Test that the Health Upgrades in the pool are progression, but not useful."):
+ self.assertFalse(any(health_upgrade.useful for health_upgrade in health_upgrades))
+ self.assertTrue(all(health_upgrade.advancement for health_upgrade in health_upgrades))
diff --git a/worlds/apquest/web_world.py b/worlds/apquest/web_world.py
new file mode 100644
index 000000000000..20a9a9ff18de
--- /dev/null
+++ b/worlds/apquest/web_world.py
@@ -0,0 +1,49 @@
+from BaseClasses import Tutorial
+from worlds.AutoWorld import WebWorld
+
+from .options import option_groups, option_presets
+
+
+# For our game to display correctly on the website, we need to define a WebWorld subclass.
+class APQuestWebWorld(WebWorld):
+ # We need to override the "game" field of the WebWorld superclass.
+ # This must be the same string as the regular World class.
+ game = "APQuest"
+
+ # Your game pages will have a visual theme (affecting e.g. the background image).
+ # You can choose between dirt, grass, grassFlowers, ice, jungle, ocean, partyTime, and stone.
+ theme = "grassFlowers"
+
+ # A WebWorld can have any number of tutorials, but should always have at least an English setup guide.
+ # Many WebWorlds just have one setup guide, but some have multiple, e.g. for different languages.
+ # We need to create a Tutorial object for every setup guide.
+ # In order, we need to provide a title, a description, a language, a filepath, a link, and authors.
+ # The filepath is relative to a "/docs/" directory in the root folder of your apworld.
+ # The "link" parameter is unused, but we still need to provide it.
+ setup_en = Tutorial(
+ "Multiworld Setup Guide",
+ "A guide to setting up APQuest for MultiWorld.",
+ "English",
+ "setup_en.md",
+ "setup/en",
+ ["NewSoupVi"],
+ )
+ # Let's have our setup guide in German as well.
+ # Do not translate the title and description!
+ # WebHost needs them to be the same to identify that it is the same tutorial.
+ # This lets it display the tutorials more compactly.
+ setup_de = Tutorial(
+ "Multiworld Setup Guide",
+ "A guide to setting up APQuest for MultiWorld.",
+ "German",
+ "setup_de.md",
+ "setup/de",
+ ["NewSoupVi"],
+ )
+
+ # We add these tutorials to our WebWorld by overriding the "tutorials" field.
+ tutorials = [setup_en, setup_de]
+
+ # If we have option groups and/or option presets, we need to specify these here as well.
+ option_groups = option_groups
+ options_presets = option_presets
diff --git a/worlds/apquest/world.py b/worlds/apquest/world.py
new file mode 100644
index 000000000000..4e89efc35e23
--- /dev/null
+++ b/worlds/apquest/world.py
@@ -0,0 +1,86 @@
+from collections.abc import Mapping
+from typing import Any
+
+# Imports of base Archipelago modules must be absolute.
+from worlds.AutoWorld import World
+
+# Imports of your world's files must be relative.
+from . import items, locations, regions, rules, web_world
+from . import options as apquest_options # rename due to a name conflict with World.options
+
+# APQuest will go through all the parts of the world api one step at a time,
+# with many examples and comments across multiple files.
+# If you'd rather read one continuous document, or just like reading multiple sources,
+# we also have this document specifying the entire world api:
+# https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/world%20api.md
+
+
+# The world class is the heart and soul of an apworld implementation.
+# It holds all the data and functions required to build the world and submit it to the multiworld generator.
+# You could have all your world code in just this one class, but for readability and better structure,
+# it is common to split up world functionality into multiple files.
+# This implementation in particular has the following additional files, each covering one topic:
+# regions.py, locations.py, rules.py, items.py, options.py and web_world.py.
+# It is recommended that you read these in that specific order, then come back to the world class.
+class APQuestWorld(World):
+ """
+ APQuest is a minimal 8bit-era inspired adventure game with grid-like movement.
+ Good games don't need more than six checks.
+ """
+
+ # The docstring should contain a description of the game, to be displayed on the WebHost.
+
+ # You must override the "game" field to say the name of the game.
+ game = "APQuest"
+
+ # The WebWorld is a definition class that governs how this world will be displayed on the website.
+ web = web_world.APQuestWebWorld()
+
+ # This is how we associate the options defined in our options.py with our world.
+ # (Note: options.py has been imported as "apquest_options" at the top of this file to avoid a name conflict)
+ options_dataclass = apquest_options.APQuestOptions
+ options: apquest_options.APQuestOptions # Common mistake: This has to be a colon (:), not an equals sign (=).
+
+ # Our world class must have a static location_name_to_id and item_name_to_id defined.
+ # We define these in regions.py and items.py respectively, so we just set them here.
+ location_name_to_id = locations.LOCATION_NAME_TO_ID
+ item_name_to_id = items.ITEM_NAME_TO_ID
+
+ # There is always one region that the generator starts from & assumes you can always go back to.
+ # This defaults to "Menu", but you can change it by overriding origin_region_name.
+ origin_region_name = "Overworld"
+
+ # Our world class must have certain functions ("steps") that get called during generation.
+ # The main ones are: create_regions, set_rules, create_items.
+ # For better structure and readability, we put each of these in their own file.
+ def create_regions(self) -> None:
+ regions.create_and_connect_regions(self)
+ locations.create_all_locations(self)
+
+ def set_rules(self) -> None:
+ rules.set_all_rules(self)
+
+ def create_items(self) -> None:
+ items.create_all_items(self)
+
+ # Our world class must also have a create_item function that can create any one of our items by name at any time.
+ # We also put this in a different file, the same one that create_items is in.
+ def create_item(self, name: str) -> items.APQuestItem:
+ return items.create_item_with_correct_classification(self, name)
+
+ # For features such as item links and panic-method start inventory, AP may ask your world to create extra filler.
+ # The way it does this is by calling get_filler_item_name.
+ # For this purpose, your world *must* have at least one infinitely repeatable item (usually filler).
+ # You must override this function and return this infinitely repeatable item's name.
+ # In our case, we defined a function called get_random_filler_item_name for this purpose in our items.py.
+ def get_filler_item_name(self) -> str:
+ return items.get_random_filler_item_name(self)
+
+ # There may be data that the game client will need to modify the behavior of the game.
+ # This is what slot_data exists for. Upon every client connection, the slot's slot_data is sent to the client.
+ # slot_data is just a dictionary using basic types, that will be converted to json when sent to the client.
+ def fill_slot_data(self) -> Mapping[str, Any]:
+ # If you need access to the player's chosen options on the client side, there is a helper for that.
+ return self.options.as_dict(
+ "hard_mode", "hammer", "extra_starting_chest", "confetti_explosiveness", "player_sprite"
+ )
diff --git a/worlds/apsudoku/docs/en_Sudoku.md b/worlds/apsudoku/docs/en_Sudoku.md
index e81f773e0291..b56af0de79f3 100644
--- a/worlds/apsudoku/docs/en_Sudoku.md
+++ b/worlds/apsudoku/docs/en_Sudoku.md
@@ -11,3 +11,5 @@ Play Sudoku puzzles of varying difficulties, earning a hint for each puzzle corr
## Where is the options page?
There is no options page; this game cannot be used in your .yamls. Instead, the client can connect to any slot in a multiworld.
+
+By using the connected room's Admin Password on the Admin Panel tab, you can configure some settings at any time to affect the entire room. This allows disabling hints entirely, as well as altering the hint odds for each difficulty.
diff --git a/worlds/apsudoku/docs/setup_en.md b/worlds/apsudoku/docs/setup_en.md
index ef5a87e0b058..f80cd4333fe1 100644
--- a/worlds/apsudoku/docs/setup_en.md
+++ b/worlds/apsudoku/docs/setup_en.md
@@ -11,7 +11,11 @@ Does not need to be added at the start of a seed, as it does not create any slot
## Installation Procedures
-Go to the latest release from the [APSudoku Releases page](https://github.com/APSudoku/APSudoku/releases/latest). Download and extract the appropriate file for your platform.
+### Windows / Linux
+Go to the latest release from the [github APSudoku Releases page](https://github.com/APSudoku/APSudoku/releases/latest). Download and extract the appropriate file for your platform.
+
+### Web
+Go to the [github pages](apsudoku.github.io) or [itch.io](https://emilyv99.itch.io/apsudoku) site, and play in the browser.
## Joining a MultiWorld Game
@@ -35,6 +39,14 @@ Info:
- You can also use the `Tracking` tab to view either a basic tracker or a valid [GodotAP tracker pack](https://github.com/EmilyV99/GodotAP/blob/main/tracker_packs/GET_PACKS.md)
- While connected, the number of "unhinted" locations for your slot is shown in the upper-left of the the `Sudoku` tab. (If this reads 0, no further hints can be earned for this slot, as every locations is already hinted)
- Click the various `?` buttons for information on controls/how to play
+
+## Admin Settings
+
+By using the connected room's Admin Password on the Admin Panel tab, you can configure some settings at any time to affect the entire room.
+
+- You can disable APSudoku for the entire room, preventing any hints from being granted.
+- You can customize the reward weights for each difficulty, making progression hints more or less likely, and/or adding a chance to get "no hint" after a solve.
+
## DeathLink Support
If `DeathLink` is enabled when you click `Connect`:
diff --git a/worlds/cccharles/__init__.py b/worlds/cccharles/__init__.py
index 6d40c0172d66..0888d8a8505d 100644
--- a/worlds/cccharles/__init__.py
+++ b/worlds/cccharles/__init__.py
@@ -4,7 +4,7 @@
from .Rules import set_rules
from .Regions import create_regions
from BaseClasses import Tutorial, ItemClassification
-from worlds.AutoWorld import World, WebWorld
+from worlds.AutoWorld import InvalidItemError, World, WebWorld
class CCCharlesWeb(WebWorld):
@@ -157,7 +157,7 @@ def create_item(self, name: str) -> CCCharlesItem:
case "Bug Spray":
classification = ItemClassification.progression
case _: # Should not occur
- raise Exception("Unexpected case met: classification cannot be set for unknown item \"" + name + "\"")
+ raise InvalidItemError("Unexpected case met: classification cannot be set for unknown item \"" + name + "\"")
return CCCharlesItem(name, classification, item_id, self.player)
diff --git a/worlds/cccharles/docs/en_Choo-Choo Charles.md b/worlds/cccharles/docs/en_Choo-Choo Charles.md
index 6d8ed39e03ea..f880865ff817 100644
--- a/worlds/cccharles/docs/en_Choo-Choo Charles.md
+++ b/worlds/cccharles/docs/en_Choo-Choo Charles.md
@@ -13,7 +13,7 @@ All scraps or any collectable item on the ground (except from Loot Crates) and i
Beating the evil train from Hell named "Charles".
## How is the game managed in Nightmare mode?
-At death, the player has to restart a brand-new game, giving him the choice to stay under the Nightmare mode or continuing with the Normal mode if considered too hard.
+At death, the player has to restart a brand-new game, giving them the choice to stay under the Nightmare Mode or continuing with the Classic Mode if considered too hard.
In this case, all collected items will be redistributed in the inventory and the missions states will be kept.
The Deathlink is not implemented yet. When this option will be available, a choice will be provided to:
* Disable the Deathlink
diff --git a/worlds/cccharles/docs/fr_Choo-Choo Charles.md b/worlds/cccharles/docs/fr_Choo-Choo Charles.md
index 42f0c48396c6..7d1a3f37d050 100644
--- a/worlds/cccharles/docs/fr_Choo-Choo Charles.md
+++ b/worlds/cccharles/docs/fr_Choo-Choo Charles.md
@@ -3,14 +3,14 @@
## Où est la page d'options ?
La [page d'options du joueur pour ce jeu](../player-options) contient toutes les options pour configurer et exporter un fichier de configuration yaml.
-## Qu'est ce que la randomisation fait au jeu ?
-Tous les débrits ou n'importe quel objet ramassable au sol (excepté les Caisses à Butin) et objets reçus par les missions de PNJs sont considérés comme emplacements à vérifier.
+## Qu'est-ce que la randomisation fait au jeu ?
+Tous les débris ou n'importe quel objet ramassable au sol (excepté les Caisses à Butin) et objets reçus par les missions de PNJs sont considérés comme emplacements à vérifier.
## Quel est le but de Choo-Choo Charles lorsqu'il est randomisé ?
Vaincre le train démoniaque de l'Enfer nommé "Charles".
-## Comment le jeu est-il géré en mode Nightmare ?
-À sa mort, le joueur doit relancer une toute nouvelle partie, lui donnant la possisilité de rester en mode Nightmare ou de poursuivre la partie en mode Normal s'il considère la partie trop difficile.
+## Comment le jeu est-il géré en Mode Cauchemar ?
+À sa mort, le joueur doit relancer une toute nouvelle partie, lui donnant la possibilité de rester en Mode Cauchemar ou de poursuivre la partie en Mode Classique s'il considère la partie trop difficile.
Dans ce cas, tous les objets collectés seront redistribués dans l'inventaire et les états des missions seront conservés.
Le Deathlink n'est pas implémenté pour l'instant. Lorsque cette option sera disponible, un choix sera fourni pour :
* Désactiver le Deathlink
@@ -18,7 +18,7 @@ Le Deathlink n'est pas implémenté pour l'instant. Lorsque cette option sera di
* Activer le Deathlink strict avec suppression de la sauvegarde lorsqu'un évènement Deathlink est reçu
## À quoi ressemble un objet d'un autre monde dans Choo-Choo Charles ?
-Les apparances des objets sont conservés.
+Les apparences des objets sont conservées.
Tout indice qui ne peut pas être représenté normalement dans le jeu est remplacé par l'Easter Egg "DeadDuck" miniaturisé qui peut être vu en dehors des limites murales physiques du jeu original.
## Comment le joueur est-il informé par une transmission d'objet et des indices ?
@@ -29,7 +29,7 @@ La même méthode est utilisée pour les indices.
Non, ceci est un travail en cours.
Les options suivantes seront possibles une fois les implémentations disponibles :
-À n'importe quel moment, le joueur peu appuyer sur l'une des touches suivantes pour afficher la console dans le jeu :
+À n'importe quel moment, le joueur peut appuyer sur l'une des touches suivantes pour afficher la console dans le jeu :
* "~" (qwerty)
* "²" (azerty)
* "F10"
diff --git a/worlds/cccharles/docs/setup_en.md b/worlds/cccharles/docs/setup_en.md
index 8df0188c031b..f5ab8425707d 100644
--- a/worlds/cccharles/docs/setup_en.md
+++ b/worlds/cccharles/docs/setup_en.md
@@ -19,7 +19,7 @@ The Mod can be installed and played by following these steps (see the [Mod Downl
2. Launch the game, if "OFFLINE" is visible in the upper-right corner of the screen, the Mod is working
### Create a Config (.yaml) File
-The purpose of a YAML file is described in the [Basic Multiworld Setup Guide](https://archipelago.gg/tutorial/Archipelago/setup/en#generating-a-game).
+The purpose of a YAML file is described in the [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en#generating-a-game).
The [Player Options page](/games/Choo-Choo%20Charles/player-options) allows to configure personal options and export a config YAML file.
@@ -27,7 +27,7 @@ The [Player Options page](/games/Choo-Choo%20Charles/player-options) allows to c
Before playing, it is highly recommended to check out the **[Known Issues](setup_en#known-issues)** section
* The game console must be opened to type Archipelago commands, press "F10" key or "`" (or "~") key in querty ("²" key in azerty)
* Type ``/connect `` with \ and \ found on the hosting Archipelago web page in the form ``archipelago.gg:XXXXX`` and ``CCCharles``
-* Disconnection is automatic at game closure but can be manually done with ``/disconnect``
+* Disconnection is automatic at game closure, but can be manually done with ``/disconnect``
## Hosting a MultiWorld or Single-Player Game
See the [Mod Download](setup_en#mod-download) section to get the **cccharles.apworld** file.
@@ -38,7 +38,7 @@ Follow these steps to host a remote multiplayer or a local single-player session
1. Double-click the **cccharles.apworld** to automatically install the world randomization logic
2. Put the **CCCharles.yaml** to **Archipelago/Players/** with the YAML of each player to host
3. Launch the Archipelago launcher and click "Generate" to configure a game with the YAMLs in **Archipelago/output/**
-4. For a multiplayer session, go to the [Archipelago HOST GAME page](https://archipelago.gg/uploads)
+4. For a multiplayer session, go to the [Archipelago HOST GAME page](/uploads)
5. Click "Upload File" and select the generated **AP_\.zip** in **Archipelago/output/**
6. Send the generated room page to each player
diff --git a/worlds/cccharles/docs/setup_fr.md b/worlds/cccharles/docs/setup_fr.md
index 386ec02587c6..6ffd0bda6ae2 100644
--- a/worlds/cccharles/docs/setup_fr.md
+++ b/worlds/cccharles/docs/setup_fr.md
@@ -4,7 +4,7 @@ Cette page est un guide simplifié de la [page du Mod Randomiseur Multiworld de
## Exigences et Logiciels Nécessaires
* Un ordinateur utilisant Windows (le Mod n'est pas utilisable sous Linux ou Mac)
* [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases)
-* Une copie légale du jeu original Choo-Choo Charles (peut être trouvé sur [Steam](https://store.steampowered.com/app/1766740/ChooChoo_Charles/)
+* Une copie légale du jeu original Choo-Choo Charles (peut être trouvé sur [Steam](https://store.steampowered.com/app/1766740/ChooChoo_Charles/))
## Installation du Mod pour jouer
### Téléchargement du Mod
@@ -19,7 +19,7 @@ Le Mod peut être installé et joué en suivant les étapes suivantes (voir la s
2. Lancer le jeu, si "OFFLINE" est visible dans le coin en haut à droite de l'écran, le Mod est actif
### Créer un Fichier de Configuration (.yaml)
-L'objectif d'un fichier YAML est décrit dans le [Guide d'Installation Basique du Multiworld](https://archipelago.gg/tutorial/Archipelago/setup/en#generating-a-game) (en anglais).
+L'objectif d'un fichier YAML est décrit dans le [Guide d'Installation Basique du Multiworld](/tutorial/Archipelago/setup/en#generating-a-game) (en anglais).
La [page d'Options Joueur](/games/Choo-Choo%20Charles/player-options) permet de configurer des options personnelles et exporter un fichier de configuration YAML.
@@ -27,7 +27,7 @@ La [page d'Options Joueur](/games/Choo-Choo%20Charles/player-options) permet de
Avant de jouer, il est fortement recommandé de consulter la section **[Problèmes Connus](setup_fr#probl%C3%A8mes-connus)**.
* La console du jeu doit être ouverte pour taper des commandes Archipelago, appuyer sur la touche "F10" ou "`" (ou "~") en querty (touche "²" en azerty)
* Taper ``/connect `` avec \ et \ trouvés sur la page web d'hébergement Archipelago sous la forme ``archipelago.gg:XXXXX`` et ``CCCharles``
-* La déconnexion est automatique à la fermeture du jeu mais peut être faite manuellement avec ``/disconnect``
+* La déconnexion est automatique à la fermeture du jeu, mais peut être faite manuellement avec ``/disconnect``
## Héberger une partie MultiWorld ou un Seul Joueur
Voir la section [Téléchargement du Mod](setup_fr#téléchargement-du-mod) pour récupérer le fichier **cccharles.apworld**.
@@ -38,8 +38,8 @@ Suivre ces étapes pour héberger une session multijoueur à distance ou locale
1. Double-cliquer sur **cccharles.apworld** pour installer automatiquement la logique de randomisation du monde
2. Placer le **CCCharles.yaml** dans **Archipelago/Players/** avec le YAML de chaque joueur à héberger
3. Exécuter le lanceur Archipelago et cliquer sur "Generate" pour configurer une partie avec les YAML dans **Archipelago/output/**
-4. Pour une session multijoueur, aller à la [page Archipelago HOST GAME](https://archipelago.gg/uploads)
-5. Cliquer sur "Upload File" et selectionner le **AP_\.zip** généré dans **Archipelago/output/**
+4. Pour une session multijoueur, aller à la [page Archipelago HOST GAME](/uploads)
+5. Cliquer sur "Upload File" et sélectionner le **AP_\.zip** généré dans **Archipelago/output/**
6. Envoyer la page de la partie générée à chaque joueur
Pour une session locale à un seul joueur, cliquer sur "Host" dans le lanceur Archipelago en utilisant **AP_\.zip** généré dans **Archipelago/output/**
diff --git a/worlds/celeste_open_world/data/CelesteLevelData.py b/worlds/celeste_open_world/data/CelesteLevelData.py
index 6ba43fc34d85..a9d21e46c092 100644
--- a/worlds/celeste_open_world/data/CelesteLevelData.py
+++ b/worlds/celeste_open_world/data/CelesteLevelData.py
@@ -2,6 +2,7 @@
from ..Levels import Level, Room, PreRegion, LevelLocation, RegionConnection, RoomConnection, Door, DoorDirection, LocationType
from ..Names import ItemName
+from collections import defaultdict
all_doors: dict[str, Door] = {
"0a_-1_east": Door("0a_-1_east", "0a_-1", DoorDirection.right, False, False),
@@ -5360,2652 +5361,2661 @@
}
+connections_by_region: defaultdict[str, list[RegionConnection]] = defaultdict(lambda: [])
+locations_by_region: defaultdict[str, list[LevelLocation]] = defaultdict(lambda: [])
+
+for _, connection in all_region_connections.items():
+ connections_by_region[connection.source_name].append(connection)
+
+for _, location in all_locations.items():
+ locations_by_region[location.region_name].append(location)
+
all_regions: dict[str, PreRegion] = {
- "0a_-1_main": PreRegion("0a_-1_main", "0a_-1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_-1_main"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_-1_main"]),
- "0a_-1_east": PreRegion("0a_-1_east", "0a_-1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_-1_east"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_-1_east"]),
+ "0a_-1_main": PreRegion("0a_-1_main", "0a_-1", connections_by_region["0a_-1_main"], locations_by_region["0a_-1_main"]),
+ "0a_-1_east": PreRegion("0a_-1_east", "0a_-1", connections_by_region["0a_-1_east"], locations_by_region["0a_-1_east"]),
- "0a_0_west": PreRegion("0a_0_west", "0a_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_0_west"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_0_west"]),
- "0a_0_main": PreRegion("0a_0_main", "0a_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_0_main"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_0_main"]),
- "0a_0_north": PreRegion("0a_0_north", "0a_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_0_north"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_0_north"]),
- "0a_0_east": PreRegion("0a_0_east", "0a_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_0_east"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_0_east"]),
+ "0a_0_west": PreRegion("0a_0_west", "0a_0", connections_by_region["0a_0_west"], locations_by_region["0a_0_west"]),
+ "0a_0_main": PreRegion("0a_0_main", "0a_0", connections_by_region["0a_0_main"], locations_by_region["0a_0_main"]),
+ "0a_0_north": PreRegion("0a_0_north", "0a_0", connections_by_region["0a_0_north"], locations_by_region["0a_0_north"]),
+ "0a_0_east": PreRegion("0a_0_east", "0a_0", connections_by_region["0a_0_east"], locations_by_region["0a_0_east"]),
- "0a_0b_south": PreRegion("0a_0b_south", "0a_0b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_0b_south"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_0b_south"]),
+ "0a_0b_south": PreRegion("0a_0b_south", "0a_0b", connections_by_region["0a_0b_south"], locations_by_region["0a_0b_south"]),
- "0a_1_west": PreRegion("0a_1_west", "0a_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_1_west"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_1_west"]),
- "0a_1_main": PreRegion("0a_1_main", "0a_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_1_main"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_1_main"]),
- "0a_1_east": PreRegion("0a_1_east", "0a_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_1_east"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_1_east"]),
+ "0a_1_west": PreRegion("0a_1_west", "0a_1", connections_by_region["0a_1_west"], locations_by_region["0a_1_west"]),
+ "0a_1_main": PreRegion("0a_1_main", "0a_1", connections_by_region["0a_1_main"], locations_by_region["0a_1_main"]),
+ "0a_1_east": PreRegion("0a_1_east", "0a_1", connections_by_region["0a_1_east"], locations_by_region["0a_1_east"]),
- "0a_2_west": PreRegion("0a_2_west", "0a_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_2_west"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_2_west"]),
- "0a_2_main": PreRegion("0a_2_main", "0a_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_2_main"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_2_main"]),
- "0a_2_east": PreRegion("0a_2_east", "0a_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_2_east"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_2_east"]),
+ "0a_2_west": PreRegion("0a_2_west", "0a_2", connections_by_region["0a_2_west"], locations_by_region["0a_2_west"]),
+ "0a_2_main": PreRegion("0a_2_main", "0a_2", connections_by_region["0a_2_main"], locations_by_region["0a_2_main"]),
+ "0a_2_east": PreRegion("0a_2_east", "0a_2", connections_by_region["0a_2_east"], locations_by_region["0a_2_east"]),
- "0a_3_west": PreRegion("0a_3_west", "0a_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_3_west"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_3_west"]),
- "0a_3_main": PreRegion("0a_3_main", "0a_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_3_main"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_3_main"]),
- "0a_3_east": PreRegion("0a_3_east", "0a_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "0a_3_east"], [loc for _, loc in all_locations.items() if loc.region_name == "0a_3_east"]),
+ "0a_3_west": PreRegion("0a_3_west", "0a_3", connections_by_region["0a_3_west"], locations_by_region["0a_3_west"]),
+ "0a_3_main": PreRegion("0a_3_main", "0a_3", connections_by_region["0a_3_main"], locations_by_region["0a_3_main"]),
+ "0a_3_east": PreRegion("0a_3_east", "0a_3", connections_by_region["0a_3_east"], locations_by_region["0a_3_east"]),
- "1a_1_main": PreRegion("1a_1_main", "1a_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_1_main"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_1_main"]),
- "1a_1_east": PreRegion("1a_1_east", "1a_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_1_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_1_east"]),
+ "1a_1_main": PreRegion("1a_1_main", "1a_1", connections_by_region["1a_1_main"], locations_by_region["1a_1_main"]),
+ "1a_1_east": PreRegion("1a_1_east", "1a_1", connections_by_region["1a_1_east"], locations_by_region["1a_1_east"]),
- "1a_2_west": PreRegion("1a_2_west", "1a_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_2_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_2_west"]),
- "1a_2_east": PreRegion("1a_2_east", "1a_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_2_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_2_east"]),
+ "1a_2_west": PreRegion("1a_2_west", "1a_2", connections_by_region["1a_2_west"], locations_by_region["1a_2_west"]),
+ "1a_2_east": PreRegion("1a_2_east", "1a_2", connections_by_region["1a_2_east"], locations_by_region["1a_2_east"]),
- "1a_3_west": PreRegion("1a_3_west", "1a_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_3_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_3_west"]),
- "1a_3_east": PreRegion("1a_3_east", "1a_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_3_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_3_east"]),
+ "1a_3_west": PreRegion("1a_3_west", "1a_3", connections_by_region["1a_3_west"], locations_by_region["1a_3_west"]),
+ "1a_3_east": PreRegion("1a_3_east", "1a_3", connections_by_region["1a_3_east"], locations_by_region["1a_3_east"]),
- "1a_4_west": PreRegion("1a_4_west", "1a_4", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_4_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_4_west"]),
- "1a_4_east": PreRegion("1a_4_east", "1a_4", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_4_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_4_east"]),
+ "1a_4_west": PreRegion("1a_4_west", "1a_4", connections_by_region["1a_4_west"], locations_by_region["1a_4_west"]),
+ "1a_4_east": PreRegion("1a_4_east", "1a_4", connections_by_region["1a_4_east"], locations_by_region["1a_4_east"]),
- "1a_3b_west": PreRegion("1a_3b_west", "1a_3b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_3b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_3b_west"]),
- "1a_3b_east": PreRegion("1a_3b_east", "1a_3b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_3b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_3b_east"]),
- "1a_3b_top": PreRegion("1a_3b_top", "1a_3b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_3b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_3b_top"]),
+ "1a_3b_west": PreRegion("1a_3b_west", "1a_3b", connections_by_region["1a_3b_west"], locations_by_region["1a_3b_west"]),
+ "1a_3b_east": PreRegion("1a_3b_east", "1a_3b", connections_by_region["1a_3b_east"], locations_by_region["1a_3b_east"]),
+ "1a_3b_top": PreRegion("1a_3b_top", "1a_3b", connections_by_region["1a_3b_top"], locations_by_region["1a_3b_top"]),
- "1a_5_bottom": PreRegion("1a_5_bottom", "1a_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_5_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_5_bottom"]),
- "1a_5_west": PreRegion("1a_5_west", "1a_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_5_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_5_west"]),
- "1a_5_north-west": PreRegion("1a_5_north-west", "1a_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_5_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_5_north-west"]),
- "1a_5_center": PreRegion("1a_5_center", "1a_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_5_center"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_5_center"]),
- "1a_5_south-east": PreRegion("1a_5_south-east", "1a_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_5_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_5_south-east"]),
- "1a_5_north-east": PreRegion("1a_5_north-east", "1a_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_5_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_5_north-east"]),
- "1a_5_top": PreRegion("1a_5_top", "1a_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_5_top"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_5_top"]),
+ "1a_5_bottom": PreRegion("1a_5_bottom", "1a_5", connections_by_region["1a_5_bottom"], locations_by_region["1a_5_bottom"]),
+ "1a_5_west": PreRegion("1a_5_west", "1a_5", connections_by_region["1a_5_west"], locations_by_region["1a_5_west"]),
+ "1a_5_north-west": PreRegion("1a_5_north-west", "1a_5", connections_by_region["1a_5_north-west"], locations_by_region["1a_5_north-west"]),
+ "1a_5_center": PreRegion("1a_5_center", "1a_5", connections_by_region["1a_5_center"], locations_by_region["1a_5_center"]),
+ "1a_5_south-east": PreRegion("1a_5_south-east", "1a_5", connections_by_region["1a_5_south-east"], locations_by_region["1a_5_south-east"]),
+ "1a_5_north-east": PreRegion("1a_5_north-east", "1a_5", connections_by_region["1a_5_north-east"], locations_by_region["1a_5_north-east"]),
+ "1a_5_top": PreRegion("1a_5_top", "1a_5", connections_by_region["1a_5_top"], locations_by_region["1a_5_top"]),
- "1a_5z_east": PreRegion("1a_5z_east", "1a_5z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_5z_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_5z_east"]),
+ "1a_5z_east": PreRegion("1a_5z_east", "1a_5z", connections_by_region["1a_5z_east"], locations_by_region["1a_5z_east"]),
- "1a_5a_west": PreRegion("1a_5a_west", "1a_5a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_5a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_5a_west"]),
+ "1a_5a_west": PreRegion("1a_5a_west", "1a_5a", connections_by_region["1a_5a_west"], locations_by_region["1a_5a_west"]),
- "1a_6_south-west": PreRegion("1a_6_south-west", "1a_6", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6_south-west"]),
- "1a_6_west": PreRegion("1a_6_west", "1a_6", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6_west"]),
- "1a_6_east": PreRegion("1a_6_east", "1a_6", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6_east"]),
+ "1a_6_south-west": PreRegion("1a_6_south-west", "1a_6", connections_by_region["1a_6_south-west"], locations_by_region["1a_6_south-west"]),
+ "1a_6_west": PreRegion("1a_6_west", "1a_6", connections_by_region["1a_6_west"], locations_by_region["1a_6_west"]),
+ "1a_6_east": PreRegion("1a_6_east", "1a_6", connections_by_region["1a_6_east"], locations_by_region["1a_6_east"]),
- "1a_6z_north-west": PreRegion("1a_6z_north-west", "1a_6z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6z_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6z_north-west"]),
- "1a_6z_west": PreRegion("1a_6z_west", "1a_6z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6z_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6z_west"]),
- "1a_6z_east": PreRegion("1a_6z_east", "1a_6z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6z_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6z_east"]),
+ "1a_6z_north-west": PreRegion("1a_6z_north-west", "1a_6z", connections_by_region["1a_6z_north-west"], locations_by_region["1a_6z_north-west"]),
+ "1a_6z_west": PreRegion("1a_6z_west", "1a_6z", connections_by_region["1a_6z_west"], locations_by_region["1a_6z_west"]),
+ "1a_6z_east": PreRegion("1a_6z_east", "1a_6z", connections_by_region["1a_6z_east"], locations_by_region["1a_6z_east"]),
- "1a_6zb_north-west": PreRegion("1a_6zb_north-west", "1a_6zb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6zb_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6zb_north-west"]),
- "1a_6zb_main": PreRegion("1a_6zb_main", "1a_6zb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6zb_main"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6zb_main"]),
- "1a_6zb_east": PreRegion("1a_6zb_east", "1a_6zb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6zb_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6zb_east"]),
+ "1a_6zb_north-west": PreRegion("1a_6zb_north-west", "1a_6zb", connections_by_region["1a_6zb_north-west"], locations_by_region["1a_6zb_north-west"]),
+ "1a_6zb_main": PreRegion("1a_6zb_main", "1a_6zb", connections_by_region["1a_6zb_main"], locations_by_region["1a_6zb_main"]),
+ "1a_6zb_east": PreRegion("1a_6zb_east", "1a_6zb", connections_by_region["1a_6zb_east"], locations_by_region["1a_6zb_east"]),
- "1a_7zb_west": PreRegion("1a_7zb_west", "1a_7zb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_7zb_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_7zb_west"]),
- "1a_7zb_east": PreRegion("1a_7zb_east", "1a_7zb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_7zb_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_7zb_east"]),
+ "1a_7zb_west": PreRegion("1a_7zb_west", "1a_7zb", connections_by_region["1a_7zb_west"], locations_by_region["1a_7zb_west"]),
+ "1a_7zb_east": PreRegion("1a_7zb_east", "1a_7zb", connections_by_region["1a_7zb_east"], locations_by_region["1a_7zb_east"]),
- "1a_6a_west": PreRegion("1a_6a_west", "1a_6a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6a_west"]),
- "1a_6a_east": PreRegion("1a_6a_east", "1a_6a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6a_east"]),
+ "1a_6a_west": PreRegion("1a_6a_west", "1a_6a", connections_by_region["1a_6a_west"], locations_by_region["1a_6a_west"]),
+ "1a_6a_east": PreRegion("1a_6a_east", "1a_6a", connections_by_region["1a_6a_east"], locations_by_region["1a_6a_east"]),
- "1a_6b_south-west": PreRegion("1a_6b_south-west", "1a_6b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6b_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6b_south-west"]),
- "1a_6b_north-west": PreRegion("1a_6b_north-west", "1a_6b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6b_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6b_north-west"]),
- "1a_6b_north-east": PreRegion("1a_6b_north-east", "1a_6b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6b_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6b_north-east"]),
+ "1a_6b_south-west": PreRegion("1a_6b_south-west", "1a_6b", connections_by_region["1a_6b_south-west"], locations_by_region["1a_6b_south-west"]),
+ "1a_6b_north-west": PreRegion("1a_6b_north-west", "1a_6b", connections_by_region["1a_6b_north-west"], locations_by_region["1a_6b_north-west"]),
+ "1a_6b_north-east": PreRegion("1a_6b_north-east", "1a_6b", connections_by_region["1a_6b_north-east"], locations_by_region["1a_6b_north-east"]),
- "1a_s0_west": PreRegion("1a_s0_west", "1a_s0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_s0_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_s0_west"]),
- "1a_s0_east": PreRegion("1a_s0_east", "1a_s0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_s0_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_s0_east"]),
+ "1a_s0_west": PreRegion("1a_s0_west", "1a_s0", connections_by_region["1a_s0_west"], locations_by_region["1a_s0_west"]),
+ "1a_s0_east": PreRegion("1a_s0_east", "1a_s0", connections_by_region["1a_s0_east"], locations_by_region["1a_s0_east"]),
- "1a_s1_east": PreRegion("1a_s1_east", "1a_s1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_s1_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_s1_east"]),
+ "1a_s1_east": PreRegion("1a_s1_east", "1a_s1", connections_by_region["1a_s1_east"], locations_by_region["1a_s1_east"]),
- "1a_6c_south-west": PreRegion("1a_6c_south-west", "1a_6c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6c_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6c_south-west"]),
- "1a_6c_north-west": PreRegion("1a_6c_north-west", "1a_6c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6c_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6c_north-west"]),
- "1a_6c_north-east": PreRegion("1a_6c_north-east", "1a_6c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_6c_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_6c_north-east"]),
+ "1a_6c_south-west": PreRegion("1a_6c_south-west", "1a_6c", connections_by_region["1a_6c_south-west"], locations_by_region["1a_6c_south-west"]),
+ "1a_6c_north-west": PreRegion("1a_6c_north-west", "1a_6c", connections_by_region["1a_6c_north-west"], locations_by_region["1a_6c_north-west"]),
+ "1a_6c_north-east": PreRegion("1a_6c_north-east", "1a_6c", connections_by_region["1a_6c_north-east"], locations_by_region["1a_6c_north-east"]),
- "1a_7_west": PreRegion("1a_7_west", "1a_7", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_7_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_7_west"]),
- "1a_7_east": PreRegion("1a_7_east", "1a_7", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_7_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_7_east"]),
+ "1a_7_west": PreRegion("1a_7_west", "1a_7", connections_by_region["1a_7_west"], locations_by_region["1a_7_west"]),
+ "1a_7_east": PreRegion("1a_7_east", "1a_7", connections_by_region["1a_7_east"], locations_by_region["1a_7_east"]),
- "1a_7z_bottom": PreRegion("1a_7z_bottom", "1a_7z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_7z_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_7z_bottom"]),
- "1a_7z_top": PreRegion("1a_7z_top", "1a_7z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_7z_top"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_7z_top"]),
+ "1a_7z_bottom": PreRegion("1a_7z_bottom", "1a_7z", connections_by_region["1a_7z_bottom"], locations_by_region["1a_7z_bottom"]),
+ "1a_7z_top": PreRegion("1a_7z_top", "1a_7z", connections_by_region["1a_7z_top"], locations_by_region["1a_7z_top"]),
- "1a_8z_bottom": PreRegion("1a_8z_bottom", "1a_8z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8z_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8z_bottom"]),
- "1a_8z_top": PreRegion("1a_8z_top", "1a_8z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8z_top"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8z_top"]),
+ "1a_8z_bottom": PreRegion("1a_8z_bottom", "1a_8z", connections_by_region["1a_8z_bottom"], locations_by_region["1a_8z_bottom"]),
+ "1a_8z_top": PreRegion("1a_8z_top", "1a_8z", connections_by_region["1a_8z_top"], locations_by_region["1a_8z_top"]),
- "1a_8zb_west": PreRegion("1a_8zb_west", "1a_8zb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8zb_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8zb_west"]),
- "1a_8zb_east": PreRegion("1a_8zb_east", "1a_8zb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8zb_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8zb_east"]),
+ "1a_8zb_west": PreRegion("1a_8zb_west", "1a_8zb", connections_by_region["1a_8zb_west"], locations_by_region["1a_8zb_west"]),
+ "1a_8zb_east": PreRegion("1a_8zb_east", "1a_8zb", connections_by_region["1a_8zb_east"], locations_by_region["1a_8zb_east"]),
- "1a_8_south-west": PreRegion("1a_8_south-west", "1a_8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8_south-west"]),
- "1a_8_west": PreRegion("1a_8_west", "1a_8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8_west"]),
- "1a_8_south": PreRegion("1a_8_south", "1a_8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8_south"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8_south"]),
- "1a_8_south-east": PreRegion("1a_8_south-east", "1a_8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8_south-east"]),
- "1a_8_north": PreRegion("1a_8_north", "1a_8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8_north"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8_north"]),
- "1a_8_north-east": PreRegion("1a_8_north-east", "1a_8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8_north-east"]),
+ "1a_8_south-west": PreRegion("1a_8_south-west", "1a_8", connections_by_region["1a_8_south-west"], locations_by_region["1a_8_south-west"]),
+ "1a_8_west": PreRegion("1a_8_west", "1a_8", connections_by_region["1a_8_west"], locations_by_region["1a_8_west"]),
+ "1a_8_south": PreRegion("1a_8_south", "1a_8", connections_by_region["1a_8_south"], locations_by_region["1a_8_south"]),
+ "1a_8_south-east": PreRegion("1a_8_south-east", "1a_8", connections_by_region["1a_8_south-east"], locations_by_region["1a_8_south-east"]),
+ "1a_8_north": PreRegion("1a_8_north", "1a_8", connections_by_region["1a_8_north"], locations_by_region["1a_8_north"]),
+ "1a_8_north-east": PreRegion("1a_8_north-east", "1a_8", connections_by_region["1a_8_north-east"], locations_by_region["1a_8_north-east"]),
- "1a_7a_east": PreRegion("1a_7a_east", "1a_7a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_7a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_7a_east"]),
- "1a_7a_west": PreRegion("1a_7a_west", "1a_7a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_7a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_7a_west"]),
+ "1a_7a_east": PreRegion("1a_7a_east", "1a_7a", connections_by_region["1a_7a_east"], locations_by_region["1a_7a_east"]),
+ "1a_7a_west": PreRegion("1a_7a_west", "1a_7a", connections_by_region["1a_7a_west"], locations_by_region["1a_7a_west"]),
- "1a_9z_east": PreRegion("1a_9z_east", "1a_9z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_9z_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_9z_east"]),
+ "1a_9z_east": PreRegion("1a_9z_east", "1a_9z", connections_by_region["1a_9z_east"], locations_by_region["1a_9z_east"]),
- "1a_8b_east": PreRegion("1a_8b_east", "1a_8b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8b_east"]),
- "1a_8b_west": PreRegion("1a_8b_west", "1a_8b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_8b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_8b_west"]),
+ "1a_8b_east": PreRegion("1a_8b_east", "1a_8b", connections_by_region["1a_8b_east"], locations_by_region["1a_8b_east"]),
+ "1a_8b_west": PreRegion("1a_8b_west", "1a_8b", connections_by_region["1a_8b_west"], locations_by_region["1a_8b_west"]),
- "1a_9_east": PreRegion("1a_9_east", "1a_9", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_9_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_9_east"]),
- "1a_9_west": PreRegion("1a_9_west", "1a_9", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_9_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_9_west"]),
+ "1a_9_east": PreRegion("1a_9_east", "1a_9", connections_by_region["1a_9_east"], locations_by_region["1a_9_east"]),
+ "1a_9_west": PreRegion("1a_9_west", "1a_9", connections_by_region["1a_9_west"], locations_by_region["1a_9_west"]),
- "1a_9b_east": PreRegion("1a_9b_east", "1a_9b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_9b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_9b_east"]),
- "1a_9b_north-east": PreRegion("1a_9b_north-east", "1a_9b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_9b_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_9b_north-east"]),
- "1a_9b_west": PreRegion("1a_9b_west", "1a_9b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_9b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_9b_west"]),
- "1a_9b_north-west": PreRegion("1a_9b_north-west", "1a_9b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_9b_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_9b_north-west"]),
+ "1a_9b_east": PreRegion("1a_9b_east", "1a_9b", connections_by_region["1a_9b_east"], locations_by_region["1a_9b_east"]),
+ "1a_9b_north-east": PreRegion("1a_9b_north-east", "1a_9b", connections_by_region["1a_9b_north-east"], locations_by_region["1a_9b_north-east"]),
+ "1a_9b_west": PreRegion("1a_9b_west", "1a_9b", connections_by_region["1a_9b_west"], locations_by_region["1a_9b_west"]),
+ "1a_9b_north-west": PreRegion("1a_9b_north-west", "1a_9b", connections_by_region["1a_9b_north-west"], locations_by_region["1a_9b_north-west"]),
- "1a_9c_west": PreRegion("1a_9c_west", "1a_9c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_9c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_9c_west"]),
+ "1a_9c_west": PreRegion("1a_9c_west", "1a_9c", connections_by_region["1a_9c_west"], locations_by_region["1a_9c_west"]),
- "1a_10_south-east": PreRegion("1a_10_south-east", "1a_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_10_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_10_south-east"]),
- "1a_10_south-west": PreRegion("1a_10_south-west", "1a_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_10_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_10_south-west"]),
- "1a_10_north-west": PreRegion("1a_10_north-west", "1a_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_10_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_10_north-west"]),
- "1a_10_north-east": PreRegion("1a_10_north-east", "1a_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_10_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_10_north-east"]),
+ "1a_10_south-east": PreRegion("1a_10_south-east", "1a_10", connections_by_region["1a_10_south-east"], locations_by_region["1a_10_south-east"]),
+ "1a_10_south-west": PreRegion("1a_10_south-west", "1a_10", connections_by_region["1a_10_south-west"], locations_by_region["1a_10_south-west"]),
+ "1a_10_north-west": PreRegion("1a_10_north-west", "1a_10", connections_by_region["1a_10_north-west"], locations_by_region["1a_10_north-west"]),
+ "1a_10_north-east": PreRegion("1a_10_north-east", "1a_10", connections_by_region["1a_10_north-east"], locations_by_region["1a_10_north-east"]),
- "1a_10z_west": PreRegion("1a_10z_west", "1a_10z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_10z_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_10z_west"]),
- "1a_10z_east": PreRegion("1a_10z_east", "1a_10z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_10z_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_10z_east"]),
+ "1a_10z_west": PreRegion("1a_10z_west", "1a_10z", connections_by_region["1a_10z_west"], locations_by_region["1a_10z_west"]),
+ "1a_10z_east": PreRegion("1a_10z_east", "1a_10z", connections_by_region["1a_10z_east"], locations_by_region["1a_10z_east"]),
- "1a_10zb_east": PreRegion("1a_10zb_east", "1a_10zb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_10zb_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_10zb_east"]),
+ "1a_10zb_east": PreRegion("1a_10zb_east", "1a_10zb", connections_by_region["1a_10zb_east"], locations_by_region["1a_10zb_east"]),
- "1a_11_south-east": PreRegion("1a_11_south-east", "1a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_11_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_11_south-east"]),
- "1a_11_south-west": PreRegion("1a_11_south-west", "1a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_11_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_11_south-west"]),
- "1a_11_north": PreRegion("1a_11_north", "1a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_11_north"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_11_north"]),
- "1a_11_west": PreRegion("1a_11_west", "1a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_11_west"]),
- "1a_11_south": PreRegion("1a_11_south", "1a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_11_south"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_11_south"]),
+ "1a_11_south-east": PreRegion("1a_11_south-east", "1a_11", connections_by_region["1a_11_south-east"], locations_by_region["1a_11_south-east"]),
+ "1a_11_south-west": PreRegion("1a_11_south-west", "1a_11", connections_by_region["1a_11_south-west"], locations_by_region["1a_11_south-west"]),
+ "1a_11_north": PreRegion("1a_11_north", "1a_11", connections_by_region["1a_11_north"], locations_by_region["1a_11_north"]),
+ "1a_11_west": PreRegion("1a_11_west", "1a_11", connections_by_region["1a_11_west"], locations_by_region["1a_11_west"]),
+ "1a_11_south": PreRegion("1a_11_south", "1a_11", connections_by_region["1a_11_south"], locations_by_region["1a_11_south"]),
- "1a_11z_east": PreRegion("1a_11z_east", "1a_11z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_11z_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_11z_east"]),
+ "1a_11z_east": PreRegion("1a_11z_east", "1a_11z", connections_by_region["1a_11z_east"], locations_by_region["1a_11z_east"]),
- "1a_10a_bottom": PreRegion("1a_10a_bottom", "1a_10a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_10a_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_10a_bottom"]),
- "1a_10a_top": PreRegion("1a_10a_top", "1a_10a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_10a_top"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_10a_top"]),
+ "1a_10a_bottom": PreRegion("1a_10a_bottom", "1a_10a", connections_by_region["1a_10a_bottom"], locations_by_region["1a_10a_bottom"]),
+ "1a_10a_top": PreRegion("1a_10a_top", "1a_10a", connections_by_region["1a_10a_top"], locations_by_region["1a_10a_top"]),
- "1a_12_south-west": PreRegion("1a_12_south-west", "1a_12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_12_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_12_south-west"]),
- "1a_12_north-west": PreRegion("1a_12_north-west", "1a_12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_12_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_12_north-west"]),
- "1a_12_east": PreRegion("1a_12_east", "1a_12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_12_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_12_east"]),
+ "1a_12_south-west": PreRegion("1a_12_south-west", "1a_12", connections_by_region["1a_12_south-west"], locations_by_region["1a_12_south-west"]),
+ "1a_12_north-west": PreRegion("1a_12_north-west", "1a_12", connections_by_region["1a_12_north-west"], locations_by_region["1a_12_north-west"]),
+ "1a_12_east": PreRegion("1a_12_east", "1a_12", connections_by_region["1a_12_east"], locations_by_region["1a_12_east"]),
- "1a_12z_east": PreRegion("1a_12z_east", "1a_12z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_12z_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_12z_east"]),
+ "1a_12z_east": PreRegion("1a_12z_east", "1a_12z", connections_by_region["1a_12z_east"], locations_by_region["1a_12z_east"]),
- "1a_12a_bottom": PreRegion("1a_12a_bottom", "1a_12a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_12a_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_12a_bottom"]),
- "1a_12a_top": PreRegion("1a_12a_top", "1a_12a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_12a_top"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_12a_top"]),
+ "1a_12a_bottom": PreRegion("1a_12a_bottom", "1a_12a", connections_by_region["1a_12a_bottom"], locations_by_region["1a_12a_bottom"]),
+ "1a_12a_top": PreRegion("1a_12a_top", "1a_12a", connections_by_region["1a_12a_top"], locations_by_region["1a_12a_top"]),
- "1a_end_south": PreRegion("1a_end_south", "1a_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_end_south"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_end_south"]),
- "1a_end_main": PreRegion("1a_end_main", "1a_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1a_end_main"], [loc for _, loc in all_locations.items() if loc.region_name == "1a_end_main"]),
+ "1a_end_south": PreRegion("1a_end_south", "1a_end", connections_by_region["1a_end_south"], locations_by_region["1a_end_south"]),
+ "1a_end_main": PreRegion("1a_end_main", "1a_end", connections_by_region["1a_end_main"], locations_by_region["1a_end_main"]),
- "1b_00_west": PreRegion("1b_00_west", "1b_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_00_west"]),
- "1b_00_east": PreRegion("1b_00_east", "1b_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_00_east"]),
+ "1b_00_west": PreRegion("1b_00_west", "1b_00", connections_by_region["1b_00_west"], locations_by_region["1b_00_west"]),
+ "1b_00_east": PreRegion("1b_00_east", "1b_00", connections_by_region["1b_00_east"], locations_by_region["1b_00_east"]),
- "1b_01_west": PreRegion("1b_01_west", "1b_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_01_west"]),
- "1b_01_east": PreRegion("1b_01_east", "1b_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_01_east"]),
+ "1b_01_west": PreRegion("1b_01_west", "1b_01", connections_by_region["1b_01_west"], locations_by_region["1b_01_west"]),
+ "1b_01_east": PreRegion("1b_01_east", "1b_01", connections_by_region["1b_01_east"], locations_by_region["1b_01_east"]),
- "1b_02_west": PreRegion("1b_02_west", "1b_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_02_west"]),
- "1b_02_east": PreRegion("1b_02_east", "1b_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_02_east"]),
+ "1b_02_west": PreRegion("1b_02_west", "1b_02", connections_by_region["1b_02_west"], locations_by_region["1b_02_west"]),
+ "1b_02_east": PreRegion("1b_02_east", "1b_02", connections_by_region["1b_02_east"], locations_by_region["1b_02_east"]),
- "1b_02b_west": PreRegion("1b_02b_west", "1b_02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_02b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_02b_west"]),
- "1b_02b_east": PreRegion("1b_02b_east", "1b_02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_02b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_02b_east"]),
+ "1b_02b_west": PreRegion("1b_02b_west", "1b_02b", connections_by_region["1b_02b_west"], locations_by_region["1b_02b_west"]),
+ "1b_02b_east": PreRegion("1b_02b_east", "1b_02b", connections_by_region["1b_02b_east"], locations_by_region["1b_02b_east"]),
- "1b_03_west": PreRegion("1b_03_west", "1b_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_03_west"]),
- "1b_03_east": PreRegion("1b_03_east", "1b_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_03_east"]),
+ "1b_03_west": PreRegion("1b_03_west", "1b_03", connections_by_region["1b_03_west"], locations_by_region["1b_03_west"]),
+ "1b_03_east": PreRegion("1b_03_east", "1b_03", connections_by_region["1b_03_east"], locations_by_region["1b_03_east"]),
- "1b_04_west": PreRegion("1b_04_west", "1b_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_04_west"]),
- "1b_04_east": PreRegion("1b_04_east", "1b_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_04_east"]),
+ "1b_04_west": PreRegion("1b_04_west", "1b_04", connections_by_region["1b_04_west"], locations_by_region["1b_04_west"]),
+ "1b_04_east": PreRegion("1b_04_east", "1b_04", connections_by_region["1b_04_east"], locations_by_region["1b_04_east"]),
- "1b_05_west": PreRegion("1b_05_west", "1b_05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_05_west"]),
- "1b_05_east": PreRegion("1b_05_east", "1b_05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_05_east"]),
+ "1b_05_west": PreRegion("1b_05_west", "1b_05", connections_by_region["1b_05_west"], locations_by_region["1b_05_west"]),
+ "1b_05_east": PreRegion("1b_05_east", "1b_05", connections_by_region["1b_05_east"], locations_by_region["1b_05_east"]),
- "1b_05b_west": PreRegion("1b_05b_west", "1b_05b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_05b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_05b_west"]),
- "1b_05b_east": PreRegion("1b_05b_east", "1b_05b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_05b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_05b_east"]),
+ "1b_05b_west": PreRegion("1b_05b_west", "1b_05b", connections_by_region["1b_05b_west"], locations_by_region["1b_05b_west"]),
+ "1b_05b_east": PreRegion("1b_05b_east", "1b_05b", connections_by_region["1b_05b_east"], locations_by_region["1b_05b_east"]),
- "1b_06_west": PreRegion("1b_06_west", "1b_06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_06_west"]),
- "1b_06_east": PreRegion("1b_06_east", "1b_06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_06_east"]),
+ "1b_06_west": PreRegion("1b_06_west", "1b_06", connections_by_region["1b_06_west"], locations_by_region["1b_06_west"]),
+ "1b_06_east": PreRegion("1b_06_east", "1b_06", connections_by_region["1b_06_east"], locations_by_region["1b_06_east"]),
- "1b_07_bottom": PreRegion("1b_07_bottom", "1b_07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_07_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_07_bottom"]),
- "1b_07_top": PreRegion("1b_07_top", "1b_07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_07_top"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_07_top"]),
+ "1b_07_bottom": PreRegion("1b_07_bottom", "1b_07", connections_by_region["1b_07_bottom"], locations_by_region["1b_07_bottom"]),
+ "1b_07_top": PreRegion("1b_07_top", "1b_07", connections_by_region["1b_07_top"], locations_by_region["1b_07_top"]),
- "1b_08_west": PreRegion("1b_08_west", "1b_08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_08_west"]),
- "1b_08_east": PreRegion("1b_08_east", "1b_08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_08_east"]),
+ "1b_08_west": PreRegion("1b_08_west", "1b_08", connections_by_region["1b_08_west"], locations_by_region["1b_08_west"]),
+ "1b_08_east": PreRegion("1b_08_east", "1b_08", connections_by_region["1b_08_east"], locations_by_region["1b_08_east"]),
- "1b_08b_west": PreRegion("1b_08b_west", "1b_08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_08b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_08b_west"]),
- "1b_08b_east": PreRegion("1b_08b_east", "1b_08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_08b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_08b_east"]),
+ "1b_08b_west": PreRegion("1b_08b_west", "1b_08b", connections_by_region["1b_08b_west"], locations_by_region["1b_08b_west"]),
+ "1b_08b_east": PreRegion("1b_08b_east", "1b_08b", connections_by_region["1b_08b_east"], locations_by_region["1b_08b_east"]),
- "1b_09_west": PreRegion("1b_09_west", "1b_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_09_west"]),
- "1b_09_east": PreRegion("1b_09_east", "1b_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_09_east"]),
+ "1b_09_west": PreRegion("1b_09_west", "1b_09", connections_by_region["1b_09_west"], locations_by_region["1b_09_west"]),
+ "1b_09_east": PreRegion("1b_09_east", "1b_09", connections_by_region["1b_09_east"], locations_by_region["1b_09_east"]),
- "1b_10_west": PreRegion("1b_10_west", "1b_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_10_west"]),
- "1b_10_east": PreRegion("1b_10_east", "1b_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_10_east"]),
+ "1b_10_west": PreRegion("1b_10_west", "1b_10", connections_by_region["1b_10_west"], locations_by_region["1b_10_west"]),
+ "1b_10_east": PreRegion("1b_10_east", "1b_10", connections_by_region["1b_10_east"], locations_by_region["1b_10_east"]),
- "1b_11_bottom": PreRegion("1b_11_bottom", "1b_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_11_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_11_bottom"]),
- "1b_11_top": PreRegion("1b_11_top", "1b_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_11_top"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_11_top"]),
+ "1b_11_bottom": PreRegion("1b_11_bottom", "1b_11", connections_by_region["1b_11_bottom"], locations_by_region["1b_11_bottom"]),
+ "1b_11_top": PreRegion("1b_11_top", "1b_11", connections_by_region["1b_11_top"], locations_by_region["1b_11_top"]),
- "1b_end_west": PreRegion("1b_end_west", "1b_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_end_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_end_west"]),
- "1b_end_goal": PreRegion("1b_end_goal", "1b_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1b_end_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "1b_end_goal"]),
+ "1b_end_west": PreRegion("1b_end_west", "1b_end", connections_by_region["1b_end_west"], locations_by_region["1b_end_west"]),
+ "1b_end_goal": PreRegion("1b_end_goal", "1b_end", connections_by_region["1b_end_goal"], locations_by_region["1b_end_goal"]),
- "1c_00_west": PreRegion("1c_00_west", "1c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1c_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1c_00_west"]),
- "1c_00_east": PreRegion("1c_00_east", "1c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1c_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1c_00_east"]),
+ "1c_00_west": PreRegion("1c_00_west", "1c_00", connections_by_region["1c_00_west"], locations_by_region["1c_00_west"]),
+ "1c_00_east": PreRegion("1c_00_east", "1c_00", connections_by_region["1c_00_east"], locations_by_region["1c_00_east"]),
- "1c_01_west": PreRegion("1c_01_west", "1c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1c_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1c_01_west"]),
- "1c_01_east": PreRegion("1c_01_east", "1c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1c_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "1c_01_east"]),
+ "1c_01_west": PreRegion("1c_01_west", "1c_01", connections_by_region["1c_01_west"], locations_by_region["1c_01_west"]),
+ "1c_01_east": PreRegion("1c_01_east", "1c_01", connections_by_region["1c_01_east"], locations_by_region["1c_01_east"]),
- "1c_02_west": PreRegion("1c_02_west", "1c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1c_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "1c_02_west"]),
- "1c_02_goal": PreRegion("1c_02_goal", "1c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "1c_02_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "1c_02_goal"]),
+ "1c_02_west": PreRegion("1c_02_west", "1c_02", connections_by_region["1c_02_west"], locations_by_region["1c_02_west"]),
+ "1c_02_goal": PreRegion("1c_02_goal", "1c_02", connections_by_region["1c_02_goal"], locations_by_region["1c_02_goal"]),
- "2a_start_main": PreRegion("2a_start_main", "2a_start", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_start_main"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_start_main"]),
- "2a_start_top": PreRegion("2a_start_top", "2a_start", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_start_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_start_top"]),
- "2a_start_east": PreRegion("2a_start_east", "2a_start", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_start_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_start_east"]),
+ "2a_start_main": PreRegion("2a_start_main", "2a_start", connections_by_region["2a_start_main"], locations_by_region["2a_start_main"]),
+ "2a_start_top": PreRegion("2a_start_top", "2a_start", connections_by_region["2a_start_top"], locations_by_region["2a_start_top"]),
+ "2a_start_east": PreRegion("2a_start_east", "2a_start", connections_by_region["2a_start_east"], locations_by_region["2a_start_east"]),
- "2a_s0_bottom": PreRegion("2a_s0_bottom", "2a_s0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_s0_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_s0_bottom"]),
- "2a_s0_top": PreRegion("2a_s0_top", "2a_s0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_s0_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_s0_top"]),
+ "2a_s0_bottom": PreRegion("2a_s0_bottom", "2a_s0", connections_by_region["2a_s0_bottom"], locations_by_region["2a_s0_bottom"]),
+ "2a_s0_top": PreRegion("2a_s0_top", "2a_s0", connections_by_region["2a_s0_top"], locations_by_region["2a_s0_top"]),
- "2a_s1_bottom": PreRegion("2a_s1_bottom", "2a_s1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_s1_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_s1_bottom"]),
- "2a_s1_top": PreRegion("2a_s1_top", "2a_s1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_s1_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_s1_top"]),
+ "2a_s1_bottom": PreRegion("2a_s1_bottom", "2a_s1", connections_by_region["2a_s1_bottom"], locations_by_region["2a_s1_bottom"]),
+ "2a_s1_top": PreRegion("2a_s1_top", "2a_s1", connections_by_region["2a_s1_top"], locations_by_region["2a_s1_top"]),
- "2a_s2_bottom": PreRegion("2a_s2_bottom", "2a_s2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_s2_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_s2_bottom"]),
+ "2a_s2_bottom": PreRegion("2a_s2_bottom", "2a_s2", connections_by_region["2a_s2_bottom"], locations_by_region["2a_s2_bottom"]),
- "2a_0_south-west": PreRegion("2a_0_south-west", "2a_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_0_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_0_south-west"]),
- "2a_0_south-east": PreRegion("2a_0_south-east", "2a_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_0_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_0_south-east"]),
- "2a_0_north-west": PreRegion("2a_0_north-west", "2a_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_0_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_0_north-west"]),
- "2a_0_north-east": PreRegion("2a_0_north-east", "2a_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_0_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_0_north-east"]),
+ "2a_0_south-west": PreRegion("2a_0_south-west", "2a_0", connections_by_region["2a_0_south-west"], locations_by_region["2a_0_south-west"]),
+ "2a_0_south-east": PreRegion("2a_0_south-east", "2a_0", connections_by_region["2a_0_south-east"], locations_by_region["2a_0_south-east"]),
+ "2a_0_north-west": PreRegion("2a_0_north-west", "2a_0", connections_by_region["2a_0_north-west"], locations_by_region["2a_0_north-west"]),
+ "2a_0_north-east": PreRegion("2a_0_north-east", "2a_0", connections_by_region["2a_0_north-east"], locations_by_region["2a_0_north-east"]),
- "2a_1_south-west": PreRegion("2a_1_south-west", "2a_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_1_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_1_south-west"]),
- "2a_1_south": PreRegion("2a_1_south", "2a_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_1_south"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_1_south"]),
- "2a_1_south-east": PreRegion("2a_1_south-east", "2a_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_1_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_1_south-east"]),
- "2a_1_north-west": PreRegion("2a_1_north-west", "2a_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_1_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_1_north-west"]),
+ "2a_1_south-west": PreRegion("2a_1_south-west", "2a_1", connections_by_region["2a_1_south-west"], locations_by_region["2a_1_south-west"]),
+ "2a_1_south": PreRegion("2a_1_south", "2a_1", connections_by_region["2a_1_south"], locations_by_region["2a_1_south"]),
+ "2a_1_south-east": PreRegion("2a_1_south-east", "2a_1", connections_by_region["2a_1_south-east"], locations_by_region["2a_1_south-east"]),
+ "2a_1_north-west": PreRegion("2a_1_north-west", "2a_1", connections_by_region["2a_1_north-west"], locations_by_region["2a_1_north-west"]),
- "2a_d0_north": PreRegion("2a_d0_north", "2a_d0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d0_north"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d0_north"]),
- "2a_d0_north-west": PreRegion("2a_d0_north-west", "2a_d0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d0_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d0_north-west"]),
- "2a_d0_west": PreRegion("2a_d0_west", "2a_d0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d0_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d0_west"]),
- "2a_d0_south-west": PreRegion("2a_d0_south-west", "2a_d0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d0_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d0_south-west"]),
- "2a_d0_south": PreRegion("2a_d0_south", "2a_d0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d0_south"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d0_south"]),
- "2a_d0_south-east": PreRegion("2a_d0_south-east", "2a_d0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d0_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d0_south-east"]),
- "2a_d0_east": PreRegion("2a_d0_east", "2a_d0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d0_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d0_east"]),
- "2a_d0_north-east": PreRegion("2a_d0_north-east", "2a_d0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d0_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d0_north-east"]),
+ "2a_d0_north": PreRegion("2a_d0_north", "2a_d0", connections_by_region["2a_d0_north"], locations_by_region["2a_d0_north"]),
+ "2a_d0_north-west": PreRegion("2a_d0_north-west", "2a_d0", connections_by_region["2a_d0_north-west"], locations_by_region["2a_d0_north-west"]),
+ "2a_d0_west": PreRegion("2a_d0_west", "2a_d0", connections_by_region["2a_d0_west"], locations_by_region["2a_d0_west"]),
+ "2a_d0_south-west": PreRegion("2a_d0_south-west", "2a_d0", connections_by_region["2a_d0_south-west"], locations_by_region["2a_d0_south-west"]),
+ "2a_d0_south": PreRegion("2a_d0_south", "2a_d0", connections_by_region["2a_d0_south"], locations_by_region["2a_d0_south"]),
+ "2a_d0_south-east": PreRegion("2a_d0_south-east", "2a_d0", connections_by_region["2a_d0_south-east"], locations_by_region["2a_d0_south-east"]),
+ "2a_d0_east": PreRegion("2a_d0_east", "2a_d0", connections_by_region["2a_d0_east"], locations_by_region["2a_d0_east"]),
+ "2a_d0_north-east": PreRegion("2a_d0_north-east", "2a_d0", connections_by_region["2a_d0_north-east"], locations_by_region["2a_d0_north-east"]),
- "2a_d7_west": PreRegion("2a_d7_west", "2a_d7", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d7_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d7_west"]),
- "2a_d7_east": PreRegion("2a_d7_east", "2a_d7", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d7_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d7_east"]),
+ "2a_d7_west": PreRegion("2a_d7_west", "2a_d7", connections_by_region["2a_d7_west"], locations_by_region["2a_d7_west"]),
+ "2a_d7_east": PreRegion("2a_d7_east", "2a_d7", connections_by_region["2a_d7_east"], locations_by_region["2a_d7_east"]),
- "2a_d8_west": PreRegion("2a_d8_west", "2a_d8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d8_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d8_west"]),
- "2a_d8_south-east": PreRegion("2a_d8_south-east", "2a_d8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d8_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d8_south-east"]),
- "2a_d8_north-east": PreRegion("2a_d8_north-east", "2a_d8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d8_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d8_north-east"]),
+ "2a_d8_west": PreRegion("2a_d8_west", "2a_d8", connections_by_region["2a_d8_west"], locations_by_region["2a_d8_west"]),
+ "2a_d8_south-east": PreRegion("2a_d8_south-east", "2a_d8", connections_by_region["2a_d8_south-east"], locations_by_region["2a_d8_south-east"]),
+ "2a_d8_north-east": PreRegion("2a_d8_north-east", "2a_d8", connections_by_region["2a_d8_north-east"], locations_by_region["2a_d8_north-east"]),
- "2a_d3_west": PreRegion("2a_d3_west", "2a_d3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d3_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d3_west"]),
- "2a_d3_north": PreRegion("2a_d3_north", "2a_d3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d3_north"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d3_north"]),
- "2a_d3_south": PreRegion("2a_d3_south", "2a_d3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d3_south"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d3_south"]),
+ "2a_d3_west": PreRegion("2a_d3_west", "2a_d3", connections_by_region["2a_d3_west"], locations_by_region["2a_d3_west"]),
+ "2a_d3_north": PreRegion("2a_d3_north", "2a_d3", connections_by_region["2a_d3_north"], locations_by_region["2a_d3_north"]),
+ "2a_d3_south": PreRegion("2a_d3_south", "2a_d3", connections_by_region["2a_d3_south"], locations_by_region["2a_d3_south"]),
- "2a_d2_west": PreRegion("2a_d2_west", "2a_d2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d2_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d2_west"]),
- "2a_d2_north-west": PreRegion("2a_d2_north-west", "2a_d2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d2_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d2_north-west"]),
- "2a_d2_east": PreRegion("2a_d2_east", "2a_d2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d2_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d2_east"]),
+ "2a_d2_west": PreRegion("2a_d2_west", "2a_d2", connections_by_region["2a_d2_west"], locations_by_region["2a_d2_west"]),
+ "2a_d2_north-west": PreRegion("2a_d2_north-west", "2a_d2", connections_by_region["2a_d2_north-west"], locations_by_region["2a_d2_north-west"]),
+ "2a_d2_east": PreRegion("2a_d2_east", "2a_d2", connections_by_region["2a_d2_east"], locations_by_region["2a_d2_east"]),
- "2a_d9_north-west": PreRegion("2a_d9_north-west", "2a_d9", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d9_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d9_north-west"]),
+ "2a_d9_north-west": PreRegion("2a_d9_north-west", "2a_d9", connections_by_region["2a_d9_north-west"], locations_by_region["2a_d9_north-west"]),
- "2a_d1_south-west": PreRegion("2a_d1_south-west", "2a_d1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d1_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d1_south-west"]),
- "2a_d1_south-east": PreRegion("2a_d1_south-east", "2a_d1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d1_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d1_south-east"]),
- "2a_d1_north-east": PreRegion("2a_d1_north-east", "2a_d1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d1_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d1_north-east"]),
+ "2a_d1_south-west": PreRegion("2a_d1_south-west", "2a_d1", connections_by_region["2a_d1_south-west"], locations_by_region["2a_d1_south-west"]),
+ "2a_d1_south-east": PreRegion("2a_d1_south-east", "2a_d1", connections_by_region["2a_d1_south-east"], locations_by_region["2a_d1_south-east"]),
+ "2a_d1_north-east": PreRegion("2a_d1_north-east", "2a_d1", connections_by_region["2a_d1_north-east"], locations_by_region["2a_d1_north-east"]),
- "2a_d6_west": PreRegion("2a_d6_west", "2a_d6", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d6_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d6_west"]),
- "2a_d6_east": PreRegion("2a_d6_east", "2a_d6", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d6_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d6_east"]),
+ "2a_d6_west": PreRegion("2a_d6_west", "2a_d6", connections_by_region["2a_d6_west"], locations_by_region["2a_d6_west"]),
+ "2a_d6_east": PreRegion("2a_d6_east", "2a_d6", connections_by_region["2a_d6_east"], locations_by_region["2a_d6_east"]),
- "2a_d4_west": PreRegion("2a_d4_west", "2a_d4", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d4_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d4_west"]),
- "2a_d4_east": PreRegion("2a_d4_east", "2a_d4", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d4_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d4_east"]),
- "2a_d4_south": PreRegion("2a_d4_south", "2a_d4", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d4_south"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d4_south"]),
+ "2a_d4_west": PreRegion("2a_d4_west", "2a_d4", connections_by_region["2a_d4_west"], locations_by_region["2a_d4_west"]),
+ "2a_d4_east": PreRegion("2a_d4_east", "2a_d4", connections_by_region["2a_d4_east"], locations_by_region["2a_d4_east"]),
+ "2a_d4_south": PreRegion("2a_d4_south", "2a_d4", connections_by_region["2a_d4_south"], locations_by_region["2a_d4_south"]),
- "2a_d5_west": PreRegion("2a_d5_west", "2a_d5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_d5_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_d5_west"]),
+ "2a_d5_west": PreRegion("2a_d5_west", "2a_d5", connections_by_region["2a_d5_west"], locations_by_region["2a_d5_west"]),
- "2a_3x_bottom": PreRegion("2a_3x_bottom", "2a_3x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_3x_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_3x_bottom"]),
- "2a_3x_top": PreRegion("2a_3x_top", "2a_3x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_3x_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_3x_top"]),
+ "2a_3x_bottom": PreRegion("2a_3x_bottom", "2a_3x", connections_by_region["2a_3x_bottom"], locations_by_region["2a_3x_bottom"]),
+ "2a_3x_top": PreRegion("2a_3x_top", "2a_3x", connections_by_region["2a_3x_top"], locations_by_region["2a_3x_top"]),
- "2a_3_bottom": PreRegion("2a_3_bottom", "2a_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_3_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_3_bottom"]),
- "2a_3_top": PreRegion("2a_3_top", "2a_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_3_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_3_top"]),
+ "2a_3_bottom": PreRegion("2a_3_bottom", "2a_3", connections_by_region["2a_3_bottom"], locations_by_region["2a_3_bottom"]),
+ "2a_3_top": PreRegion("2a_3_top", "2a_3", connections_by_region["2a_3_top"], locations_by_region["2a_3_top"]),
- "2a_4_bottom": PreRegion("2a_4_bottom", "2a_4", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_4_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_4_bottom"]),
- "2a_4_top": PreRegion("2a_4_top", "2a_4", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_4_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_4_top"]),
+ "2a_4_bottom": PreRegion("2a_4_bottom", "2a_4", connections_by_region["2a_4_bottom"], locations_by_region["2a_4_bottom"]),
+ "2a_4_top": PreRegion("2a_4_top", "2a_4", connections_by_region["2a_4_top"], locations_by_region["2a_4_top"]),
- "2a_5_bottom": PreRegion("2a_5_bottom", "2a_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_5_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_5_bottom"]),
- "2a_5_top": PreRegion("2a_5_top", "2a_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_5_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_5_top"]),
+ "2a_5_bottom": PreRegion("2a_5_bottom", "2a_5", connections_by_region["2a_5_bottom"], locations_by_region["2a_5_bottom"]),
+ "2a_5_top": PreRegion("2a_5_top", "2a_5", connections_by_region["2a_5_top"], locations_by_region["2a_5_top"]),
- "2a_6_bottom": PreRegion("2a_6_bottom", "2a_6", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_6_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_6_bottom"]),
- "2a_6_top": PreRegion("2a_6_top", "2a_6", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_6_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_6_top"]),
+ "2a_6_bottom": PreRegion("2a_6_bottom", "2a_6", connections_by_region["2a_6_bottom"], locations_by_region["2a_6_bottom"]),
+ "2a_6_top": PreRegion("2a_6_top", "2a_6", connections_by_region["2a_6_top"], locations_by_region["2a_6_top"]),
- "2a_7_bottom": PreRegion("2a_7_bottom", "2a_7", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_7_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_7_bottom"]),
- "2a_7_top": PreRegion("2a_7_top", "2a_7", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_7_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_7_top"]),
+ "2a_7_bottom": PreRegion("2a_7_bottom", "2a_7", connections_by_region["2a_7_bottom"], locations_by_region["2a_7_bottom"]),
+ "2a_7_top": PreRegion("2a_7_top", "2a_7", connections_by_region["2a_7_top"], locations_by_region["2a_7_top"]),
- "2a_8_bottom": PreRegion("2a_8_bottom", "2a_8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_8_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_8_bottom"]),
- "2a_8_top": PreRegion("2a_8_top", "2a_8", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_8_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_8_top"]),
+ "2a_8_bottom": PreRegion("2a_8_bottom", "2a_8", connections_by_region["2a_8_bottom"], locations_by_region["2a_8_bottom"]),
+ "2a_8_top": PreRegion("2a_8_top", "2a_8", connections_by_region["2a_8_top"], locations_by_region["2a_8_top"]),
- "2a_9_west": PreRegion("2a_9_west", "2a_9", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_9_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_9_west"]),
- "2a_9_north": PreRegion("2a_9_north", "2a_9", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_9_north"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_9_north"]),
- "2a_9_north-west": PreRegion("2a_9_north-west", "2a_9", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_9_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_9_north-west"]),
- "2a_9_south": PreRegion("2a_9_south", "2a_9", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_9_south"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_9_south"]),
- "2a_9_south-east": PreRegion("2a_9_south-east", "2a_9", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_9_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_9_south-east"]),
+ "2a_9_west": PreRegion("2a_9_west", "2a_9", connections_by_region["2a_9_west"], locations_by_region["2a_9_west"]),
+ "2a_9_north": PreRegion("2a_9_north", "2a_9", connections_by_region["2a_9_north"], locations_by_region["2a_9_north"]),
+ "2a_9_north-west": PreRegion("2a_9_north-west", "2a_9", connections_by_region["2a_9_north-west"], locations_by_region["2a_9_north-west"]),
+ "2a_9_south": PreRegion("2a_9_south", "2a_9", connections_by_region["2a_9_south"], locations_by_region["2a_9_south"]),
+ "2a_9_south-east": PreRegion("2a_9_south-east", "2a_9", connections_by_region["2a_9_south-east"], locations_by_region["2a_9_south-east"]),
- "2a_9b_east": PreRegion("2a_9b_east", "2a_9b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_9b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_9b_east"]),
- "2a_9b_west": PreRegion("2a_9b_west", "2a_9b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_9b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_9b_west"]),
+ "2a_9b_east": PreRegion("2a_9b_east", "2a_9b", connections_by_region["2a_9b_east"], locations_by_region["2a_9b_east"]),
+ "2a_9b_west": PreRegion("2a_9b_west", "2a_9b", connections_by_region["2a_9b_west"], locations_by_region["2a_9b_west"]),
- "2a_10_top": PreRegion("2a_10_top", "2a_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_10_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_10_top"]),
- "2a_10_bottom": PreRegion("2a_10_bottom", "2a_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_10_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_10_bottom"]),
+ "2a_10_top": PreRegion("2a_10_top", "2a_10", connections_by_region["2a_10_top"], locations_by_region["2a_10_top"]),
+ "2a_10_bottom": PreRegion("2a_10_bottom", "2a_10", connections_by_region["2a_10_bottom"], locations_by_region["2a_10_bottom"]),
- "2a_2_north-west": PreRegion("2a_2_north-west", "2a_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_2_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_2_north-west"]),
- "2a_2_south-west": PreRegion("2a_2_south-west", "2a_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_2_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_2_south-west"]),
- "2a_2_south-east": PreRegion("2a_2_south-east", "2a_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_2_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_2_south-east"]),
+ "2a_2_north-west": PreRegion("2a_2_north-west", "2a_2", connections_by_region["2a_2_north-west"], locations_by_region["2a_2_north-west"]),
+ "2a_2_south-west": PreRegion("2a_2_south-west", "2a_2", connections_by_region["2a_2_south-west"], locations_by_region["2a_2_south-west"]),
+ "2a_2_south-east": PreRegion("2a_2_south-east", "2a_2", connections_by_region["2a_2_south-east"], locations_by_region["2a_2_south-east"]),
- "2a_11_west": PreRegion("2a_11_west", "2a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_11_west"]),
- "2a_11_east": PreRegion("2a_11_east", "2a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_11_east"]),
+ "2a_11_west": PreRegion("2a_11_west", "2a_11", connections_by_region["2a_11_west"], locations_by_region["2a_11_west"]),
+ "2a_11_east": PreRegion("2a_11_east", "2a_11", connections_by_region["2a_11_east"], locations_by_region["2a_11_east"]),
- "2a_12b_west": PreRegion("2a_12b_west", "2a_12b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12b_west"]),
- "2a_12b_north": PreRegion("2a_12b_north", "2a_12b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12b_north"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12b_north"]),
- "2a_12b_south": PreRegion("2a_12b_south", "2a_12b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12b_south"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12b_south"]),
- "2a_12b_east": PreRegion("2a_12b_east", "2a_12b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12b_east"]),
- "2a_12b_south-east": PreRegion("2a_12b_south-east", "2a_12b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12b_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12b_south-east"]),
+ "2a_12b_west": PreRegion("2a_12b_west", "2a_12b", connections_by_region["2a_12b_west"], locations_by_region["2a_12b_west"]),
+ "2a_12b_north": PreRegion("2a_12b_north", "2a_12b", connections_by_region["2a_12b_north"], locations_by_region["2a_12b_north"]),
+ "2a_12b_south": PreRegion("2a_12b_south", "2a_12b", connections_by_region["2a_12b_south"], locations_by_region["2a_12b_south"]),
+ "2a_12b_east": PreRegion("2a_12b_east", "2a_12b", connections_by_region["2a_12b_east"], locations_by_region["2a_12b_east"]),
+ "2a_12b_south-east": PreRegion("2a_12b_south-east", "2a_12b", connections_by_region["2a_12b_south-east"], locations_by_region["2a_12b_south-east"]),
- "2a_12c_south": PreRegion("2a_12c_south", "2a_12c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12c_south"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12c_south"]),
+ "2a_12c_south": PreRegion("2a_12c_south", "2a_12c", connections_by_region["2a_12c_south"], locations_by_region["2a_12c_south"]),
- "2a_12d_north-west": PreRegion("2a_12d_north-west", "2a_12d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12d_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12d_north-west"]),
- "2a_12d_north": PreRegion("2a_12d_north", "2a_12d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12d_north"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12d_north"]),
+ "2a_12d_north-west": PreRegion("2a_12d_north-west", "2a_12d", connections_by_region["2a_12d_north-west"], locations_by_region["2a_12d_north-west"]),
+ "2a_12d_north": PreRegion("2a_12d_north", "2a_12d", connections_by_region["2a_12d_north"], locations_by_region["2a_12d_north"]),
- "2a_12_west": PreRegion("2a_12_west", "2a_12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12_west"]),
- "2a_12_east": PreRegion("2a_12_east", "2a_12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_12_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_12_east"]),
+ "2a_12_west": PreRegion("2a_12_west", "2a_12", connections_by_region["2a_12_west"], locations_by_region["2a_12_west"]),
+ "2a_12_east": PreRegion("2a_12_east", "2a_12", connections_by_region["2a_12_east"], locations_by_region["2a_12_east"]),
- "2a_13_west": PreRegion("2a_13_west", "2a_13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_13_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_13_west"]),
- "2a_13_phone": PreRegion("2a_13_phone", "2a_13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_13_phone"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_13_phone"]),
+ "2a_13_west": PreRegion("2a_13_west", "2a_13", connections_by_region["2a_13_west"], locations_by_region["2a_13_west"]),
+ "2a_13_phone": PreRegion("2a_13_phone", "2a_13", connections_by_region["2a_13_phone"], locations_by_region["2a_13_phone"]),
- "2a_end_0_main": PreRegion("2a_end_0_main", "2a_end_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_0_main"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_0_main"]),
- "2a_end_0_top": PreRegion("2a_end_0_top", "2a_end_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_0_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_0_top"]),
- "2a_end_0_east": PreRegion("2a_end_0_east", "2a_end_0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_0_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_0_east"]),
+ "2a_end_0_main": PreRegion("2a_end_0_main", "2a_end_0", connections_by_region["2a_end_0_main"], locations_by_region["2a_end_0_main"]),
+ "2a_end_0_top": PreRegion("2a_end_0_top", "2a_end_0", connections_by_region["2a_end_0_top"], locations_by_region["2a_end_0_top"]),
+ "2a_end_0_east": PreRegion("2a_end_0_east", "2a_end_0", connections_by_region["2a_end_0_east"], locations_by_region["2a_end_0_east"]),
- "2a_end_s0_bottom": PreRegion("2a_end_s0_bottom", "2a_end_s0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_s0_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_s0_bottom"]),
- "2a_end_s0_top": PreRegion("2a_end_s0_top", "2a_end_s0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_s0_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_s0_top"]),
+ "2a_end_s0_bottom": PreRegion("2a_end_s0_bottom", "2a_end_s0", connections_by_region["2a_end_s0_bottom"], locations_by_region["2a_end_s0_bottom"]),
+ "2a_end_s0_top": PreRegion("2a_end_s0_top", "2a_end_s0", connections_by_region["2a_end_s0_top"], locations_by_region["2a_end_s0_top"]),
- "2a_end_s1_bottom": PreRegion("2a_end_s1_bottom", "2a_end_s1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_s1_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_s1_bottom"]),
+ "2a_end_s1_bottom": PreRegion("2a_end_s1_bottom", "2a_end_s1", connections_by_region["2a_end_s1_bottom"], locations_by_region["2a_end_s1_bottom"]),
- "2a_end_1_west": PreRegion("2a_end_1_west", "2a_end_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_1_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_1_west"]),
- "2a_end_1_north-east": PreRegion("2a_end_1_north-east", "2a_end_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_1_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_1_north-east"]),
- "2a_end_1_east": PreRegion("2a_end_1_east", "2a_end_1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_1_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_1_east"]),
+ "2a_end_1_west": PreRegion("2a_end_1_west", "2a_end_1", connections_by_region["2a_end_1_west"], locations_by_region["2a_end_1_west"]),
+ "2a_end_1_north-east": PreRegion("2a_end_1_north-east", "2a_end_1", connections_by_region["2a_end_1_north-east"], locations_by_region["2a_end_1_north-east"]),
+ "2a_end_1_east": PreRegion("2a_end_1_east", "2a_end_1", connections_by_region["2a_end_1_east"], locations_by_region["2a_end_1_east"]),
- "2a_end_2_north-west": PreRegion("2a_end_2_north-west", "2a_end_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_2_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_2_north-west"]),
- "2a_end_2_west": PreRegion("2a_end_2_west", "2a_end_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_2_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_2_west"]),
- "2a_end_2_north-east": PreRegion("2a_end_2_north-east", "2a_end_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_2_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_2_north-east"]),
- "2a_end_2_east": PreRegion("2a_end_2_east", "2a_end_2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_2_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_2_east"]),
+ "2a_end_2_north-west": PreRegion("2a_end_2_north-west", "2a_end_2", connections_by_region["2a_end_2_north-west"], locations_by_region["2a_end_2_north-west"]),
+ "2a_end_2_west": PreRegion("2a_end_2_west", "2a_end_2", connections_by_region["2a_end_2_west"], locations_by_region["2a_end_2_west"]),
+ "2a_end_2_north-east": PreRegion("2a_end_2_north-east", "2a_end_2", connections_by_region["2a_end_2_north-east"], locations_by_region["2a_end_2_north-east"]),
+ "2a_end_2_east": PreRegion("2a_end_2_east", "2a_end_2", connections_by_region["2a_end_2_east"], locations_by_region["2a_end_2_east"]),
- "2a_end_3_north-west": PreRegion("2a_end_3_north-west", "2a_end_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_3_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_3_north-west"]),
- "2a_end_3_west": PreRegion("2a_end_3_west", "2a_end_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_3_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_3_west"]),
- "2a_end_3_east": PreRegion("2a_end_3_east", "2a_end_3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_3_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_3_east"]),
+ "2a_end_3_north-west": PreRegion("2a_end_3_north-west", "2a_end_3", connections_by_region["2a_end_3_north-west"], locations_by_region["2a_end_3_north-west"]),
+ "2a_end_3_west": PreRegion("2a_end_3_west", "2a_end_3", connections_by_region["2a_end_3_west"], locations_by_region["2a_end_3_west"]),
+ "2a_end_3_east": PreRegion("2a_end_3_east", "2a_end_3", connections_by_region["2a_end_3_east"], locations_by_region["2a_end_3_east"]),
- "2a_end_4_west": PreRegion("2a_end_4_west", "2a_end_4", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_4_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_4_west"]),
- "2a_end_4_east": PreRegion("2a_end_4_east", "2a_end_4", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_4_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_4_east"]),
+ "2a_end_4_west": PreRegion("2a_end_4_west", "2a_end_4", connections_by_region["2a_end_4_west"], locations_by_region["2a_end_4_west"]),
+ "2a_end_4_east": PreRegion("2a_end_4_east", "2a_end_4", connections_by_region["2a_end_4_east"], locations_by_region["2a_end_4_east"]),
- "2a_end_3b_west": PreRegion("2a_end_3b_west", "2a_end_3b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_3b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_3b_west"]),
- "2a_end_3b_north": PreRegion("2a_end_3b_north", "2a_end_3b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_3b_north"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_3b_north"]),
- "2a_end_3b_east": PreRegion("2a_end_3b_east", "2a_end_3b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_3b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_3b_east"]),
+ "2a_end_3b_west": PreRegion("2a_end_3b_west", "2a_end_3b", connections_by_region["2a_end_3b_west"], locations_by_region["2a_end_3b_west"]),
+ "2a_end_3b_north": PreRegion("2a_end_3b_north", "2a_end_3b", connections_by_region["2a_end_3b_north"], locations_by_region["2a_end_3b_north"]),
+ "2a_end_3b_east": PreRegion("2a_end_3b_east", "2a_end_3b", connections_by_region["2a_end_3b_east"], locations_by_region["2a_end_3b_east"]),
- "2a_end_3cb_bottom": PreRegion("2a_end_3cb_bottom", "2a_end_3cb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_3cb_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_3cb_bottom"]),
- "2a_end_3cb_top": PreRegion("2a_end_3cb_top", "2a_end_3cb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_3cb_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_3cb_top"]),
+ "2a_end_3cb_bottom": PreRegion("2a_end_3cb_bottom", "2a_end_3cb", connections_by_region["2a_end_3cb_bottom"], locations_by_region["2a_end_3cb_bottom"]),
+ "2a_end_3cb_top": PreRegion("2a_end_3cb_top", "2a_end_3cb", connections_by_region["2a_end_3cb_top"], locations_by_region["2a_end_3cb_top"]),
- "2a_end_3c_bottom": PreRegion("2a_end_3c_bottom", "2a_end_3c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_3c_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_3c_bottom"]),
+ "2a_end_3c_bottom": PreRegion("2a_end_3c_bottom", "2a_end_3c", connections_by_region["2a_end_3c_bottom"], locations_by_region["2a_end_3c_bottom"]),
- "2a_end_5_west": PreRegion("2a_end_5_west", "2a_end_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_5_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_5_west"]),
- "2a_end_5_east": PreRegion("2a_end_5_east", "2a_end_5", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_5_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_5_east"]),
+ "2a_end_5_west": PreRegion("2a_end_5_west", "2a_end_5", connections_by_region["2a_end_5_west"], locations_by_region["2a_end_5_west"]),
+ "2a_end_5_east": PreRegion("2a_end_5_east", "2a_end_5", connections_by_region["2a_end_5_east"], locations_by_region["2a_end_5_east"]),
- "2a_end_6_west": PreRegion("2a_end_6_west", "2a_end_6", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_6_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_6_west"]),
- "2a_end_6_main": PreRegion("2a_end_6_main", "2a_end_6", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2a_end_6_main"], [loc for _, loc in all_locations.items() if loc.region_name == "2a_end_6_main"]),
+ "2a_end_6_west": PreRegion("2a_end_6_west", "2a_end_6", connections_by_region["2a_end_6_west"], locations_by_region["2a_end_6_west"]),
+ "2a_end_6_main": PreRegion("2a_end_6_main", "2a_end_6", connections_by_region["2a_end_6_main"], locations_by_region["2a_end_6_main"]),
- "2b_start_west": PreRegion("2b_start_west", "2b_start", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_start_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_start_west"]),
- "2b_start_east": PreRegion("2b_start_east", "2b_start", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_start_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_start_east"]),
+ "2b_start_west": PreRegion("2b_start_west", "2b_start", connections_by_region["2b_start_west"], locations_by_region["2b_start_west"]),
+ "2b_start_east": PreRegion("2b_start_east", "2b_start", connections_by_region["2b_start_east"], locations_by_region["2b_start_east"]),
- "2b_00_west": PreRegion("2b_00_west", "2b_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_00_west"]),
- "2b_00_east": PreRegion("2b_00_east", "2b_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_00_east"]),
+ "2b_00_west": PreRegion("2b_00_west", "2b_00", connections_by_region["2b_00_west"], locations_by_region["2b_00_west"]),
+ "2b_00_east": PreRegion("2b_00_east", "2b_00", connections_by_region["2b_00_east"], locations_by_region["2b_00_east"]),
- "2b_01_west": PreRegion("2b_01_west", "2b_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_01_west"]),
- "2b_01_east": PreRegion("2b_01_east", "2b_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_01_east"]),
+ "2b_01_west": PreRegion("2b_01_west", "2b_01", connections_by_region["2b_01_west"], locations_by_region["2b_01_west"]),
+ "2b_01_east": PreRegion("2b_01_east", "2b_01", connections_by_region["2b_01_east"], locations_by_region["2b_01_east"]),
- "2b_01b_west": PreRegion("2b_01b_west", "2b_01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_01b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_01b_west"]),
- "2b_01b_east": PreRegion("2b_01b_east", "2b_01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_01b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_01b_east"]),
+ "2b_01b_west": PreRegion("2b_01b_west", "2b_01b", connections_by_region["2b_01b_west"], locations_by_region["2b_01b_west"]),
+ "2b_01b_east": PreRegion("2b_01b_east", "2b_01b", connections_by_region["2b_01b_east"], locations_by_region["2b_01b_east"]),
- "2b_02b_west": PreRegion("2b_02b_west", "2b_02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_02b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_02b_west"]),
- "2b_02b_east": PreRegion("2b_02b_east", "2b_02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_02b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_02b_east"]),
+ "2b_02b_west": PreRegion("2b_02b_west", "2b_02b", connections_by_region["2b_02b_west"], locations_by_region["2b_02b_west"]),
+ "2b_02b_east": PreRegion("2b_02b_east", "2b_02b", connections_by_region["2b_02b_east"], locations_by_region["2b_02b_east"]),
- "2b_02_west": PreRegion("2b_02_west", "2b_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_02_west"]),
- "2b_02_east": PreRegion("2b_02_east", "2b_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_02_east"]),
+ "2b_02_west": PreRegion("2b_02_west", "2b_02", connections_by_region["2b_02_west"], locations_by_region["2b_02_west"]),
+ "2b_02_east": PreRegion("2b_02_east", "2b_02", connections_by_region["2b_02_east"], locations_by_region["2b_02_east"]),
- "2b_03_west": PreRegion("2b_03_west", "2b_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_03_west"]),
- "2b_03_east": PreRegion("2b_03_east", "2b_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_03_east"]),
+ "2b_03_west": PreRegion("2b_03_west", "2b_03", connections_by_region["2b_03_west"], locations_by_region["2b_03_west"]),
+ "2b_03_east": PreRegion("2b_03_east", "2b_03", connections_by_region["2b_03_east"], locations_by_region["2b_03_east"]),
- "2b_04_bottom": PreRegion("2b_04_bottom", "2b_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_04_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_04_bottom"]),
- "2b_04_top": PreRegion("2b_04_top", "2b_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_04_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_04_top"]),
+ "2b_04_bottom": PreRegion("2b_04_bottom", "2b_04", connections_by_region["2b_04_bottom"], locations_by_region["2b_04_bottom"]),
+ "2b_04_top": PreRegion("2b_04_top", "2b_04", connections_by_region["2b_04_top"], locations_by_region["2b_04_top"]),
- "2b_05_bottom": PreRegion("2b_05_bottom", "2b_05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_05_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_05_bottom"]),
- "2b_05_top": PreRegion("2b_05_top", "2b_05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_05_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_05_top"]),
+ "2b_05_bottom": PreRegion("2b_05_bottom", "2b_05", connections_by_region["2b_05_bottom"], locations_by_region["2b_05_bottom"]),
+ "2b_05_top": PreRegion("2b_05_top", "2b_05", connections_by_region["2b_05_top"], locations_by_region["2b_05_top"]),
- "2b_06_west": PreRegion("2b_06_west", "2b_06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_06_west"]),
- "2b_06_east": PreRegion("2b_06_east", "2b_06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_06_east"]),
+ "2b_06_west": PreRegion("2b_06_west", "2b_06", connections_by_region["2b_06_west"], locations_by_region["2b_06_west"]),
+ "2b_06_east": PreRegion("2b_06_east", "2b_06", connections_by_region["2b_06_east"], locations_by_region["2b_06_east"]),
- "2b_07_bottom": PreRegion("2b_07_bottom", "2b_07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_07_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_07_bottom"]),
- "2b_07_top": PreRegion("2b_07_top", "2b_07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_07_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_07_top"]),
+ "2b_07_bottom": PreRegion("2b_07_bottom", "2b_07", connections_by_region["2b_07_bottom"], locations_by_region["2b_07_bottom"]),
+ "2b_07_top": PreRegion("2b_07_top", "2b_07", connections_by_region["2b_07_top"], locations_by_region["2b_07_top"]),
- "2b_08b_west": PreRegion("2b_08b_west", "2b_08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_08b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_08b_west"]),
- "2b_08b_east": PreRegion("2b_08b_east", "2b_08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_08b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_08b_east"]),
+ "2b_08b_west": PreRegion("2b_08b_west", "2b_08b", connections_by_region["2b_08b_west"], locations_by_region["2b_08b_west"]),
+ "2b_08b_east": PreRegion("2b_08b_east", "2b_08b", connections_by_region["2b_08b_east"], locations_by_region["2b_08b_east"]),
- "2b_08_west": PreRegion("2b_08_west", "2b_08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_08_west"]),
- "2b_08_east": PreRegion("2b_08_east", "2b_08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_08_east"]),
+ "2b_08_west": PreRegion("2b_08_west", "2b_08", connections_by_region["2b_08_west"], locations_by_region["2b_08_west"]),
+ "2b_08_east": PreRegion("2b_08_east", "2b_08", connections_by_region["2b_08_east"], locations_by_region["2b_08_east"]),
- "2b_09_west": PreRegion("2b_09_west", "2b_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_09_west"]),
- "2b_09_east": PreRegion("2b_09_east", "2b_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_09_east"]),
+ "2b_09_west": PreRegion("2b_09_west", "2b_09", connections_by_region["2b_09_west"], locations_by_region["2b_09_west"]),
+ "2b_09_east": PreRegion("2b_09_east", "2b_09", connections_by_region["2b_09_east"], locations_by_region["2b_09_east"]),
- "2b_10_west": PreRegion("2b_10_west", "2b_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_10_west"]),
- "2b_10_east": PreRegion("2b_10_east", "2b_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_10_east"]),
+ "2b_10_west": PreRegion("2b_10_west", "2b_10", connections_by_region["2b_10_west"], locations_by_region["2b_10_west"]),
+ "2b_10_east": PreRegion("2b_10_east", "2b_10", connections_by_region["2b_10_east"], locations_by_region["2b_10_east"]),
- "2b_11_bottom": PreRegion("2b_11_bottom", "2b_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_11_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_11_bottom"]),
- "2b_11_top": PreRegion("2b_11_top", "2b_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_11_top"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_11_top"]),
+ "2b_11_bottom": PreRegion("2b_11_bottom", "2b_11", connections_by_region["2b_11_bottom"], locations_by_region["2b_11_bottom"]),
+ "2b_11_top": PreRegion("2b_11_top", "2b_11", connections_by_region["2b_11_top"], locations_by_region["2b_11_top"]),
- "2b_end_west": PreRegion("2b_end_west", "2b_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_end_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_end_west"]),
- "2b_end_goal": PreRegion("2b_end_goal", "2b_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2b_end_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "2b_end_goal"]),
+ "2b_end_west": PreRegion("2b_end_west", "2b_end", connections_by_region["2b_end_west"], locations_by_region["2b_end_west"]),
+ "2b_end_goal": PreRegion("2b_end_goal", "2b_end", connections_by_region["2b_end_goal"], locations_by_region["2b_end_goal"]),
- "2c_00_west": PreRegion("2c_00_west", "2c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2c_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2c_00_west"]),
- "2c_00_east": PreRegion("2c_00_east", "2c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2c_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2c_00_east"]),
+ "2c_00_west": PreRegion("2c_00_west", "2c_00", connections_by_region["2c_00_west"], locations_by_region["2c_00_west"]),
+ "2c_00_east": PreRegion("2c_00_east", "2c_00", connections_by_region["2c_00_east"], locations_by_region["2c_00_east"]),
- "2c_01_west": PreRegion("2c_01_west", "2c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2c_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2c_01_west"]),
- "2c_01_east": PreRegion("2c_01_east", "2c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2c_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "2c_01_east"]),
+ "2c_01_west": PreRegion("2c_01_west", "2c_01", connections_by_region["2c_01_west"], locations_by_region["2c_01_west"]),
+ "2c_01_east": PreRegion("2c_01_east", "2c_01", connections_by_region["2c_01_east"], locations_by_region["2c_01_east"]),
- "2c_02_west": PreRegion("2c_02_west", "2c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2c_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "2c_02_west"]),
- "2c_02_goal": PreRegion("2c_02_goal", "2c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "2c_02_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "2c_02_goal"]),
+ "2c_02_west": PreRegion("2c_02_west", "2c_02", connections_by_region["2c_02_west"], locations_by_region["2c_02_west"]),
+ "2c_02_goal": PreRegion("2c_02_goal", "2c_02", connections_by_region["2c_02_goal"], locations_by_region["2c_02_goal"]),
- "3a_s0_main": PreRegion("3a_s0_main", "3a_s0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s0_main"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s0_main"]),
- "3a_s0_east": PreRegion("3a_s0_east", "3a_s0", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s0_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s0_east"]),
+ "3a_s0_main": PreRegion("3a_s0_main", "3a_s0", connections_by_region["3a_s0_main"], locations_by_region["3a_s0_main"]),
+ "3a_s0_east": PreRegion("3a_s0_east", "3a_s0", connections_by_region["3a_s0_east"], locations_by_region["3a_s0_east"]),
- "3a_s1_west": PreRegion("3a_s1_west", "3a_s1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s1_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s1_west"]),
- "3a_s1_east": PreRegion("3a_s1_east", "3a_s1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s1_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s1_east"]),
- "3a_s1_north-east": PreRegion("3a_s1_north-east", "3a_s1", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s1_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s1_north-east"]),
+ "3a_s1_west": PreRegion("3a_s1_west", "3a_s1", connections_by_region["3a_s1_west"], locations_by_region["3a_s1_west"]),
+ "3a_s1_east": PreRegion("3a_s1_east", "3a_s1", connections_by_region["3a_s1_east"], locations_by_region["3a_s1_east"]),
+ "3a_s1_north-east": PreRegion("3a_s1_north-east", "3a_s1", connections_by_region["3a_s1_north-east"], locations_by_region["3a_s1_north-east"]),
- "3a_s2_west": PreRegion("3a_s2_west", "3a_s2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s2_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s2_west"]),
- "3a_s2_north-west": PreRegion("3a_s2_north-west", "3a_s2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s2_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s2_north-west"]),
- "3a_s2_east": PreRegion("3a_s2_east", "3a_s2", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s2_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s2_east"]),
+ "3a_s2_west": PreRegion("3a_s2_west", "3a_s2", connections_by_region["3a_s2_west"], locations_by_region["3a_s2_west"]),
+ "3a_s2_north-west": PreRegion("3a_s2_north-west", "3a_s2", connections_by_region["3a_s2_north-west"], locations_by_region["3a_s2_north-west"]),
+ "3a_s2_east": PreRegion("3a_s2_east", "3a_s2", connections_by_region["3a_s2_east"], locations_by_region["3a_s2_east"]),
- "3a_s3_west": PreRegion("3a_s3_west", "3a_s3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s3_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s3_west"]),
- "3a_s3_north": PreRegion("3a_s3_north", "3a_s3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s3_north"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s3_north"]),
- "3a_s3_east": PreRegion("3a_s3_east", "3a_s3", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_s3_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_s3_east"]),
+ "3a_s3_west": PreRegion("3a_s3_west", "3a_s3", connections_by_region["3a_s3_west"], locations_by_region["3a_s3_west"]),
+ "3a_s3_north": PreRegion("3a_s3_north", "3a_s3", connections_by_region["3a_s3_north"], locations_by_region["3a_s3_north"]),
+ "3a_s3_east": PreRegion("3a_s3_east", "3a_s3", connections_by_region["3a_s3_east"], locations_by_region["3a_s3_east"]),
- "3a_0x-a_west": PreRegion("3a_0x-a_west", "3a_0x-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_0x-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_0x-a_west"]),
- "3a_0x-a_east": PreRegion("3a_0x-a_east", "3a_0x-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_0x-a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_0x-a_east"]),
+ "3a_0x-a_west": PreRegion("3a_0x-a_west", "3a_0x-a", connections_by_region["3a_0x-a_west"], locations_by_region["3a_0x-a_west"]),
+ "3a_0x-a_east": PreRegion("3a_0x-a_east", "3a_0x-a", connections_by_region["3a_0x-a_east"], locations_by_region["3a_0x-a_east"]),
- "3a_00-a_west": PreRegion("3a_00-a_west", "3a_00-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-a_west"]),
- "3a_00-a_east": PreRegion("3a_00-a_east", "3a_00-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-a_east"]),
+ "3a_00-a_west": PreRegion("3a_00-a_west", "3a_00-a", connections_by_region["3a_00-a_west"], locations_by_region["3a_00-a_west"]),
+ "3a_00-a_east": PreRegion("3a_00-a_east", "3a_00-a", connections_by_region["3a_00-a_east"], locations_by_region["3a_00-a_east"]),
- "3a_02-a_west": PreRegion("3a_02-a_west", "3a_02-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-a_west"]),
- "3a_02-a_top": PreRegion("3a_02-a_top", "3a_02-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-a_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-a_top"]),
- "3a_02-a_main": PreRegion("3a_02-a_main", "3a_02-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-a_main"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-a_main"]),
- "3a_02-a_east": PreRegion("3a_02-a_east", "3a_02-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-a_east"]),
+ "3a_02-a_west": PreRegion("3a_02-a_west", "3a_02-a", connections_by_region["3a_02-a_west"], locations_by_region["3a_02-a_west"]),
+ "3a_02-a_top": PreRegion("3a_02-a_top", "3a_02-a", connections_by_region["3a_02-a_top"], locations_by_region["3a_02-a_top"]),
+ "3a_02-a_main": PreRegion("3a_02-a_main", "3a_02-a", connections_by_region["3a_02-a_main"], locations_by_region["3a_02-a_main"]),
+ "3a_02-a_east": PreRegion("3a_02-a_east", "3a_02-a", connections_by_region["3a_02-a_east"], locations_by_region["3a_02-a_east"]),
- "3a_02-b_west": PreRegion("3a_02-b_west", "3a_02-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-b_west"]),
- "3a_02-b_east": PreRegion("3a_02-b_east", "3a_02-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-b_east"]),
- "3a_02-b_far-east": PreRegion("3a_02-b_far-east", "3a_02-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-b_far-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-b_far-east"]),
+ "3a_02-b_west": PreRegion("3a_02-b_west", "3a_02-b", connections_by_region["3a_02-b_west"], locations_by_region["3a_02-b_west"]),
+ "3a_02-b_east": PreRegion("3a_02-b_east", "3a_02-b", connections_by_region["3a_02-b_east"], locations_by_region["3a_02-b_east"]),
+ "3a_02-b_far-east": PreRegion("3a_02-b_far-east", "3a_02-b", connections_by_region["3a_02-b_far-east"], locations_by_region["3a_02-b_far-east"]),
- "3a_01-b_west": PreRegion("3a_01-b_west", "3a_01-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_01-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_01-b_west"]),
- "3a_01-b_north-west": PreRegion("3a_01-b_north-west", "3a_01-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_01-b_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_01-b_north-west"]),
- "3a_01-b_east": PreRegion("3a_01-b_east", "3a_01-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_01-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_01-b_east"]),
+ "3a_01-b_west": PreRegion("3a_01-b_west", "3a_01-b", connections_by_region["3a_01-b_west"], locations_by_region["3a_01-b_west"]),
+ "3a_01-b_north-west": PreRegion("3a_01-b_north-west", "3a_01-b", connections_by_region["3a_01-b_north-west"], locations_by_region["3a_01-b_north-west"]),
+ "3a_01-b_east": PreRegion("3a_01-b_east", "3a_01-b", connections_by_region["3a_01-b_east"], locations_by_region["3a_01-b_east"]),
- "3a_00-b_south-west": PreRegion("3a_00-b_south-west", "3a_00-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-b_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-b_south-west"]),
- "3a_00-b_south-east": PreRegion("3a_00-b_south-east", "3a_00-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-b_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-b_south-east"]),
- "3a_00-b_west": PreRegion("3a_00-b_west", "3a_00-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-b_west"]),
- "3a_00-b_north-west": PreRegion("3a_00-b_north-west", "3a_00-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-b_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-b_north-west"]),
- "3a_00-b_east": PreRegion("3a_00-b_east", "3a_00-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-b_east"]),
- "3a_00-b_north": PreRegion("3a_00-b_north", "3a_00-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-b_north"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-b_north"]),
+ "3a_00-b_south-west": PreRegion("3a_00-b_south-west", "3a_00-b", connections_by_region["3a_00-b_south-west"], locations_by_region["3a_00-b_south-west"]),
+ "3a_00-b_south-east": PreRegion("3a_00-b_south-east", "3a_00-b", connections_by_region["3a_00-b_south-east"], locations_by_region["3a_00-b_south-east"]),
+ "3a_00-b_west": PreRegion("3a_00-b_west", "3a_00-b", connections_by_region["3a_00-b_west"], locations_by_region["3a_00-b_west"]),
+ "3a_00-b_north-west": PreRegion("3a_00-b_north-west", "3a_00-b", connections_by_region["3a_00-b_north-west"], locations_by_region["3a_00-b_north-west"]),
+ "3a_00-b_east": PreRegion("3a_00-b_east", "3a_00-b", connections_by_region["3a_00-b_east"], locations_by_region["3a_00-b_east"]),
+ "3a_00-b_north": PreRegion("3a_00-b_north", "3a_00-b", connections_by_region["3a_00-b_north"], locations_by_region["3a_00-b_north"]),
- "3a_00-c_south-west": PreRegion("3a_00-c_south-west", "3a_00-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-c_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-c_south-west"]),
- "3a_00-c_south-east": PreRegion("3a_00-c_south-east", "3a_00-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-c_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-c_south-east"]),
- "3a_00-c_north-east": PreRegion("3a_00-c_north-east", "3a_00-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-c_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-c_north-east"]),
+ "3a_00-c_south-west": PreRegion("3a_00-c_south-west", "3a_00-c", connections_by_region["3a_00-c_south-west"], locations_by_region["3a_00-c_south-west"]),
+ "3a_00-c_south-east": PreRegion("3a_00-c_south-east", "3a_00-c", connections_by_region["3a_00-c_south-east"], locations_by_region["3a_00-c_south-east"]),
+ "3a_00-c_north-east": PreRegion("3a_00-c_north-east", "3a_00-c", connections_by_region["3a_00-c_north-east"], locations_by_region["3a_00-c_north-east"]),
- "3a_0x-b_west": PreRegion("3a_0x-b_west", "3a_0x-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_0x-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_0x-b_west"]),
- "3a_0x-b_south-east": PreRegion("3a_0x-b_south-east", "3a_0x-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_0x-b_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_0x-b_south-east"]),
- "3a_0x-b_north-east": PreRegion("3a_0x-b_north-east", "3a_0x-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_0x-b_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_0x-b_north-east"]),
+ "3a_0x-b_west": PreRegion("3a_0x-b_west", "3a_0x-b", connections_by_region["3a_0x-b_west"], locations_by_region["3a_0x-b_west"]),
+ "3a_0x-b_south-east": PreRegion("3a_0x-b_south-east", "3a_0x-b", connections_by_region["3a_0x-b_south-east"], locations_by_region["3a_0x-b_south-east"]),
+ "3a_0x-b_north-east": PreRegion("3a_0x-b_north-east", "3a_0x-b", connections_by_region["3a_0x-b_north-east"], locations_by_region["3a_0x-b_north-east"]),
- "3a_03-a_west": PreRegion("3a_03-a_west", "3a_03-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_03-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_03-a_west"]),
- "3a_03-a_top": PreRegion("3a_03-a_top", "3a_03-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_03-a_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_03-a_top"]),
- "3a_03-a_east": PreRegion("3a_03-a_east", "3a_03-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_03-a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_03-a_east"]),
+ "3a_03-a_west": PreRegion("3a_03-a_west", "3a_03-a", connections_by_region["3a_03-a_west"], locations_by_region["3a_03-a_west"]),
+ "3a_03-a_top": PreRegion("3a_03-a_top", "3a_03-a", connections_by_region["3a_03-a_top"], locations_by_region["3a_03-a_top"]),
+ "3a_03-a_east": PreRegion("3a_03-a_east", "3a_03-a", connections_by_region["3a_03-a_east"], locations_by_region["3a_03-a_east"]),
- "3a_04-b_west": PreRegion("3a_04-b_west", "3a_04-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_04-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_04-b_west"]),
- "3a_04-b_east": PreRegion("3a_04-b_east", "3a_04-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_04-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_04-b_east"]),
+ "3a_04-b_west": PreRegion("3a_04-b_west", "3a_04-b", connections_by_region["3a_04-b_west"], locations_by_region["3a_04-b_west"]),
+ "3a_04-b_east": PreRegion("3a_04-b_east", "3a_04-b", connections_by_region["3a_04-b_east"], locations_by_region["3a_04-b_east"]),
- "3a_05-a_west": PreRegion("3a_05-a_west", "3a_05-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_05-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_05-a_west"]),
- "3a_05-a_east": PreRegion("3a_05-a_east", "3a_05-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_05-a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_05-a_east"]),
+ "3a_05-a_west": PreRegion("3a_05-a_west", "3a_05-a", connections_by_region["3a_05-a_west"], locations_by_region["3a_05-a_west"]),
+ "3a_05-a_east": PreRegion("3a_05-a_east", "3a_05-a", connections_by_region["3a_05-a_east"], locations_by_region["3a_05-a_east"]),
- "3a_06-a_west": PreRegion("3a_06-a_west", "3a_06-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-a_west"]),
- "3a_06-a_east": PreRegion("3a_06-a_east", "3a_06-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-a_east"]),
+ "3a_06-a_west": PreRegion("3a_06-a_west", "3a_06-a", connections_by_region["3a_06-a_west"], locations_by_region["3a_06-a_west"]),
+ "3a_06-a_east": PreRegion("3a_06-a_east", "3a_06-a", connections_by_region["3a_06-a_east"], locations_by_region["3a_06-a_east"]),
- "3a_07-a_west": PreRegion("3a_07-a_west", "3a_07-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_07-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_07-a_west"]),
- "3a_07-a_top": PreRegion("3a_07-a_top", "3a_07-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_07-a_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_07-a_top"]),
- "3a_07-a_east": PreRegion("3a_07-a_east", "3a_07-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_07-a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_07-a_east"]),
+ "3a_07-a_west": PreRegion("3a_07-a_west", "3a_07-a", connections_by_region["3a_07-a_west"], locations_by_region["3a_07-a_west"]),
+ "3a_07-a_top": PreRegion("3a_07-a_top", "3a_07-a", connections_by_region["3a_07-a_top"], locations_by_region["3a_07-a_top"]),
+ "3a_07-a_east": PreRegion("3a_07-a_east", "3a_07-a", connections_by_region["3a_07-a_east"], locations_by_region["3a_07-a_east"]),
- "3a_07-b_bottom": PreRegion("3a_07-b_bottom", "3a_07-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_07-b_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_07-b_bottom"]),
- "3a_07-b_west": PreRegion("3a_07-b_west", "3a_07-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_07-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_07-b_west"]),
- "3a_07-b_top": PreRegion("3a_07-b_top", "3a_07-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_07-b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_07-b_top"]),
- "3a_07-b_east": PreRegion("3a_07-b_east", "3a_07-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_07-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_07-b_east"]),
+ "3a_07-b_bottom": PreRegion("3a_07-b_bottom", "3a_07-b", connections_by_region["3a_07-b_bottom"], locations_by_region["3a_07-b_bottom"]),
+ "3a_07-b_west": PreRegion("3a_07-b_west", "3a_07-b", connections_by_region["3a_07-b_west"], locations_by_region["3a_07-b_west"]),
+ "3a_07-b_top": PreRegion("3a_07-b_top", "3a_07-b", connections_by_region["3a_07-b_top"], locations_by_region["3a_07-b_top"]),
+ "3a_07-b_east": PreRegion("3a_07-b_east", "3a_07-b", connections_by_region["3a_07-b_east"], locations_by_region["3a_07-b_east"]),
- "3a_06-b_west": PreRegion("3a_06-b_west", "3a_06-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-b_west"]),
- "3a_06-b_east": PreRegion("3a_06-b_east", "3a_06-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-b_east"]),
+ "3a_06-b_west": PreRegion("3a_06-b_west", "3a_06-b", connections_by_region["3a_06-b_west"], locations_by_region["3a_06-b_west"]),
+ "3a_06-b_east": PreRegion("3a_06-b_east", "3a_06-b", connections_by_region["3a_06-b_east"], locations_by_region["3a_06-b_east"]),
- "3a_06-c_south-west": PreRegion("3a_06-c_south-west", "3a_06-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-c_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-c_south-west"]),
- "3a_06-c_north-west": PreRegion("3a_06-c_north-west", "3a_06-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-c_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-c_north-west"]),
- "3a_06-c_south-east": PreRegion("3a_06-c_south-east", "3a_06-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-c_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-c_south-east"]),
- "3a_06-c_east": PreRegion("3a_06-c_east", "3a_06-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-c_east"]),
+ "3a_06-c_south-west": PreRegion("3a_06-c_south-west", "3a_06-c", connections_by_region["3a_06-c_south-west"], locations_by_region["3a_06-c_south-west"]),
+ "3a_06-c_north-west": PreRegion("3a_06-c_north-west", "3a_06-c", connections_by_region["3a_06-c_north-west"], locations_by_region["3a_06-c_north-west"]),
+ "3a_06-c_south-east": PreRegion("3a_06-c_south-east", "3a_06-c", connections_by_region["3a_06-c_south-east"], locations_by_region["3a_06-c_south-east"]),
+ "3a_06-c_east": PreRegion("3a_06-c_east", "3a_06-c", connections_by_region["3a_06-c_east"], locations_by_region["3a_06-c_east"]),
- "3a_05-c_east": PreRegion("3a_05-c_east", "3a_05-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_05-c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_05-c_east"]),
+ "3a_05-c_east": PreRegion("3a_05-c_east", "3a_05-c", connections_by_region["3a_05-c_east"], locations_by_region["3a_05-c_east"]),
- "3a_08-c_west": PreRegion("3a_08-c_west", "3a_08-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-c_west"]),
- "3a_08-c_east": PreRegion("3a_08-c_east", "3a_08-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-c_east"]),
+ "3a_08-c_west": PreRegion("3a_08-c_west", "3a_08-c", connections_by_region["3a_08-c_west"], locations_by_region["3a_08-c_west"]),
+ "3a_08-c_east": PreRegion("3a_08-c_east", "3a_08-c", connections_by_region["3a_08-c_east"], locations_by_region["3a_08-c_east"]),
- "3a_08-b_west": PreRegion("3a_08-b_west", "3a_08-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-b_west"]),
- "3a_08-b_east": PreRegion("3a_08-b_east", "3a_08-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-b_east"]),
+ "3a_08-b_west": PreRegion("3a_08-b_west", "3a_08-b", connections_by_region["3a_08-b_west"], locations_by_region["3a_08-b_west"]),
+ "3a_08-b_east": PreRegion("3a_08-b_east", "3a_08-b", connections_by_region["3a_08-b_east"], locations_by_region["3a_08-b_east"]),
- "3a_08-a_west": PreRegion("3a_08-a_west", "3a_08-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-a_west"]),
- "3a_08-a_bottom": PreRegion("3a_08-a_bottom", "3a_08-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-a_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-a_bottom"]),
- "3a_08-a_east": PreRegion("3a_08-a_east", "3a_08-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-a_east"]),
+ "3a_08-a_west": PreRegion("3a_08-a_west", "3a_08-a", connections_by_region["3a_08-a_west"], locations_by_region["3a_08-a_west"]),
+ "3a_08-a_bottom": PreRegion("3a_08-a_bottom", "3a_08-a", connections_by_region["3a_08-a_bottom"], locations_by_region["3a_08-a_bottom"]),
+ "3a_08-a_east": PreRegion("3a_08-a_east", "3a_08-a", connections_by_region["3a_08-a_east"], locations_by_region["3a_08-a_east"]),
- "3a_09-b_west": PreRegion("3a_09-b_west", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_west"]),
- "3a_09-b_north-west": PreRegion("3a_09-b_north-west", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_north-west"]),
- "3a_09-b_center": PreRegion("3a_09-b_center", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_center"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_center"]),
- "3a_09-b_south-west": PreRegion("3a_09-b_south-west", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_south-west"]),
- "3a_09-b_south": PreRegion("3a_09-b_south", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_south"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_south"]),
- "3a_09-b_south-east": PreRegion("3a_09-b_south-east", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_south-east"]),
- "3a_09-b_east": PreRegion("3a_09-b_east", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_east"]),
- "3a_09-b_north-east-right": PreRegion("3a_09-b_north-east-right", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_north-east-right"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_north-east-right"]),
- "3a_09-b_north-east-top": PreRegion("3a_09-b_north-east-top", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_north-east-top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_north-east-top"]),
- "3a_09-b_north": PreRegion("3a_09-b_north", "3a_09-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-b_north"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-b_north"]),
+ "3a_09-b_west": PreRegion("3a_09-b_west", "3a_09-b", connections_by_region["3a_09-b_west"], locations_by_region["3a_09-b_west"]),
+ "3a_09-b_north-west": PreRegion("3a_09-b_north-west", "3a_09-b", connections_by_region["3a_09-b_north-west"], locations_by_region["3a_09-b_north-west"]),
+ "3a_09-b_center": PreRegion("3a_09-b_center", "3a_09-b", connections_by_region["3a_09-b_center"], locations_by_region["3a_09-b_center"]),
+ "3a_09-b_south-west": PreRegion("3a_09-b_south-west", "3a_09-b", connections_by_region["3a_09-b_south-west"], locations_by_region["3a_09-b_south-west"]),
+ "3a_09-b_south": PreRegion("3a_09-b_south", "3a_09-b", connections_by_region["3a_09-b_south"], locations_by_region["3a_09-b_south"]),
+ "3a_09-b_south-east": PreRegion("3a_09-b_south-east", "3a_09-b", connections_by_region["3a_09-b_south-east"], locations_by_region["3a_09-b_south-east"]),
+ "3a_09-b_east": PreRegion("3a_09-b_east", "3a_09-b", connections_by_region["3a_09-b_east"], locations_by_region["3a_09-b_east"]),
+ "3a_09-b_north-east-right": PreRegion("3a_09-b_north-east-right", "3a_09-b", connections_by_region["3a_09-b_north-east-right"], locations_by_region["3a_09-b_north-east-right"]),
+ "3a_09-b_north-east-top": PreRegion("3a_09-b_north-east-top", "3a_09-b", connections_by_region["3a_09-b_north-east-top"], locations_by_region["3a_09-b_north-east-top"]),
+ "3a_09-b_north": PreRegion("3a_09-b_north", "3a_09-b", connections_by_region["3a_09-b_north"], locations_by_region["3a_09-b_north"]),
- "3a_10-x_west": PreRegion("3a_10-x_west", "3a_10-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-x_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-x_west"]),
- "3a_10-x_south-east": PreRegion("3a_10-x_south-east", "3a_10-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-x_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-x_south-east"]),
- "3a_10-x_north-east-top": PreRegion("3a_10-x_north-east-top", "3a_10-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-x_north-east-top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-x_north-east-top"]),
- "3a_10-x_north-east-right": PreRegion("3a_10-x_north-east-right", "3a_10-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-x_north-east-right"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-x_north-east-right"]),
+ "3a_10-x_west": PreRegion("3a_10-x_west", "3a_10-x", connections_by_region["3a_10-x_west"], locations_by_region["3a_10-x_west"]),
+ "3a_10-x_south-east": PreRegion("3a_10-x_south-east", "3a_10-x", connections_by_region["3a_10-x_south-east"], locations_by_region["3a_10-x_south-east"]),
+ "3a_10-x_north-east-top": PreRegion("3a_10-x_north-east-top", "3a_10-x", connections_by_region["3a_10-x_north-east-top"], locations_by_region["3a_10-x_north-east-top"]),
+ "3a_10-x_north-east-right": PreRegion("3a_10-x_north-east-right", "3a_10-x", connections_by_region["3a_10-x_north-east-right"], locations_by_region["3a_10-x_north-east-right"]),
- "3a_11-x_west": PreRegion("3a_11-x_west", "3a_11-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-x_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-x_west"]),
- "3a_11-x_south": PreRegion("3a_11-x_south", "3a_11-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-x_south"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-x_south"]),
+ "3a_11-x_west": PreRegion("3a_11-x_west", "3a_11-x", connections_by_region["3a_11-x_west"], locations_by_region["3a_11-x_west"]),
+ "3a_11-x_south": PreRegion("3a_11-x_south", "3a_11-x", connections_by_region["3a_11-x_south"], locations_by_region["3a_11-x_south"]),
- "3a_11-y_west": PreRegion("3a_11-y_west", "3a_11-y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-y_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-y_west"]),
- "3a_11-y_east": PreRegion("3a_11-y_east", "3a_11-y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-y_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-y_east"]),
- "3a_11-y_south": PreRegion("3a_11-y_south", "3a_11-y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-y_south"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-y_south"]),
+ "3a_11-y_west": PreRegion("3a_11-y_west", "3a_11-y", connections_by_region["3a_11-y_west"], locations_by_region["3a_11-y_west"]),
+ "3a_11-y_east": PreRegion("3a_11-y_east", "3a_11-y", connections_by_region["3a_11-y_east"], locations_by_region["3a_11-y_east"]),
+ "3a_11-y_south": PreRegion("3a_11-y_south", "3a_11-y", connections_by_region["3a_11-y_south"], locations_by_region["3a_11-y_south"]),
- "3a_12-y_west": PreRegion("3a_12-y_west", "3a_12-y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-y_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-y_west"]),
+ "3a_12-y_west": PreRegion("3a_12-y_west", "3a_12-y", connections_by_region["3a_12-y_west"], locations_by_region["3a_12-y_west"]),
- "3a_11-z_west": PreRegion("3a_11-z_west", "3a_11-z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-z_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-z_west"]),
- "3a_11-z_east": PreRegion("3a_11-z_east", "3a_11-z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-z_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-z_east"]),
+ "3a_11-z_west": PreRegion("3a_11-z_west", "3a_11-z", connections_by_region["3a_11-z_west"], locations_by_region["3a_11-z_west"]),
+ "3a_11-z_east": PreRegion("3a_11-z_east", "3a_11-z", connections_by_region["3a_11-z_east"], locations_by_region["3a_11-z_east"]),
- "3a_10-z_bottom": PreRegion("3a_10-z_bottom", "3a_10-z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-z_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-z_bottom"]),
- "3a_10-z_top": PreRegion("3a_10-z_top", "3a_10-z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-z_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-z_top"]),
+ "3a_10-z_bottom": PreRegion("3a_10-z_bottom", "3a_10-z", connections_by_region["3a_10-z_bottom"], locations_by_region["3a_10-z_bottom"]),
+ "3a_10-z_top": PreRegion("3a_10-z_top", "3a_10-z", connections_by_region["3a_10-z_top"], locations_by_region["3a_10-z_top"]),
- "3a_10-y_bottom": PreRegion("3a_10-y_bottom", "3a_10-y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-y_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-y_bottom"]),
- "3a_10-y_top": PreRegion("3a_10-y_top", "3a_10-y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-y_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-y_top"]),
+ "3a_10-y_bottom": PreRegion("3a_10-y_bottom", "3a_10-y", connections_by_region["3a_10-y_bottom"], locations_by_region["3a_10-y_bottom"]),
+ "3a_10-y_top": PreRegion("3a_10-y_top", "3a_10-y", connections_by_region["3a_10-y_top"], locations_by_region["3a_10-y_top"]),
- "3a_10-c_south-east": PreRegion("3a_10-c_south-east", "3a_10-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-c_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-c_south-east"]),
- "3a_10-c_north-east": PreRegion("3a_10-c_north-east", "3a_10-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-c_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-c_north-east"]),
- "3a_10-c_north-west": PreRegion("3a_10-c_north-west", "3a_10-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-c_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-c_north-west"]),
- "3a_10-c_south-west": PreRegion("3a_10-c_south-west", "3a_10-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-c_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-c_south-west"]),
+ "3a_10-c_south-east": PreRegion("3a_10-c_south-east", "3a_10-c", connections_by_region["3a_10-c_south-east"], locations_by_region["3a_10-c_south-east"]),
+ "3a_10-c_north-east": PreRegion("3a_10-c_north-east", "3a_10-c", connections_by_region["3a_10-c_north-east"], locations_by_region["3a_10-c_north-east"]),
+ "3a_10-c_north-west": PreRegion("3a_10-c_north-west", "3a_10-c", connections_by_region["3a_10-c_north-west"], locations_by_region["3a_10-c_north-west"]),
+ "3a_10-c_south-west": PreRegion("3a_10-c_south-west", "3a_10-c", connections_by_region["3a_10-c_south-west"], locations_by_region["3a_10-c_south-west"]),
- "3a_11-c_west": PreRegion("3a_11-c_west", "3a_11-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-c_west"]),
- "3a_11-c_east": PreRegion("3a_11-c_east", "3a_11-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-c_east"]),
- "3a_11-c_south-east": PreRegion("3a_11-c_south-east", "3a_11-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-c_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-c_south-east"]),
- "3a_11-c_south-west": PreRegion("3a_11-c_south-west", "3a_11-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-c_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-c_south-west"]),
+ "3a_11-c_west": PreRegion("3a_11-c_west", "3a_11-c", connections_by_region["3a_11-c_west"], locations_by_region["3a_11-c_west"]),
+ "3a_11-c_east": PreRegion("3a_11-c_east", "3a_11-c", connections_by_region["3a_11-c_east"], locations_by_region["3a_11-c_east"]),
+ "3a_11-c_south-east": PreRegion("3a_11-c_south-east", "3a_11-c", connections_by_region["3a_11-c_south-east"], locations_by_region["3a_11-c_south-east"]),
+ "3a_11-c_south-west": PreRegion("3a_11-c_south-west", "3a_11-c", connections_by_region["3a_11-c_south-west"], locations_by_region["3a_11-c_south-west"]),
- "3a_12-c_west": PreRegion("3a_12-c_west", "3a_12-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-c_west"]),
- "3a_12-c_top": PreRegion("3a_12-c_top", "3a_12-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-c_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-c_top"]),
+ "3a_12-c_west": PreRegion("3a_12-c_west", "3a_12-c", connections_by_region["3a_12-c_west"], locations_by_region["3a_12-c_west"]),
+ "3a_12-c_top": PreRegion("3a_12-c_top", "3a_12-c", connections_by_region["3a_12-c_top"], locations_by_region["3a_12-c_top"]),
- "3a_12-d_bottom": PreRegion("3a_12-d_bottom", "3a_12-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-d_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-d_bottom"]),
- "3a_12-d_top": PreRegion("3a_12-d_top", "3a_12-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-d_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-d_top"]),
+ "3a_12-d_bottom": PreRegion("3a_12-d_bottom", "3a_12-d", connections_by_region["3a_12-d_bottom"], locations_by_region["3a_12-d_bottom"]),
+ "3a_12-d_top": PreRegion("3a_12-d_top", "3a_12-d", connections_by_region["3a_12-d_top"], locations_by_region["3a_12-d_top"]),
- "3a_11-d_west": PreRegion("3a_11-d_west", "3a_11-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-d_west"]),
- "3a_11-d_east": PreRegion("3a_11-d_east", "3a_11-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-d_east"]),
+ "3a_11-d_west": PreRegion("3a_11-d_west", "3a_11-d", connections_by_region["3a_11-d_west"], locations_by_region["3a_11-d_west"]),
+ "3a_11-d_east": PreRegion("3a_11-d_east", "3a_11-d", connections_by_region["3a_11-d_east"], locations_by_region["3a_11-d_east"]),
- "3a_10-d_west": PreRegion("3a_10-d_west", "3a_10-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-d_west"]),
- "3a_10-d_main": PreRegion("3a_10-d_main", "3a_10-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-d_main"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-d_main"]),
- "3a_10-d_east": PreRegion("3a_10-d_east", "3a_10-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_10-d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_10-d_east"]),
+ "3a_10-d_west": PreRegion("3a_10-d_west", "3a_10-d", connections_by_region["3a_10-d_west"], locations_by_region["3a_10-d_west"]),
+ "3a_10-d_main": PreRegion("3a_10-d_main", "3a_10-d", connections_by_region["3a_10-d_main"], locations_by_region["3a_10-d_main"]),
+ "3a_10-d_east": PreRegion("3a_10-d_east", "3a_10-d", connections_by_region["3a_10-d_east"], locations_by_region["3a_10-d_east"]),
- "3a_11-b_west": PreRegion("3a_11-b_west", "3a_11-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-b_west"]),
- "3a_11-b_north-west": PreRegion("3a_11-b_north-west", "3a_11-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-b_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-b_north-west"]),
- "3a_11-b_east": PreRegion("3a_11-b_east", "3a_11-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-b_east"]),
- "3a_11-b_north-east": PreRegion("3a_11-b_north-east", "3a_11-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-b_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-b_north-east"]),
+ "3a_11-b_west": PreRegion("3a_11-b_west", "3a_11-b", connections_by_region["3a_11-b_west"], locations_by_region["3a_11-b_west"]),
+ "3a_11-b_north-west": PreRegion("3a_11-b_north-west", "3a_11-b", connections_by_region["3a_11-b_north-west"], locations_by_region["3a_11-b_north-west"]),
+ "3a_11-b_east": PreRegion("3a_11-b_east", "3a_11-b", connections_by_region["3a_11-b_east"], locations_by_region["3a_11-b_east"]),
+ "3a_11-b_north-east": PreRegion("3a_11-b_north-east", "3a_11-b", connections_by_region["3a_11-b_north-east"], locations_by_region["3a_11-b_north-east"]),
- "3a_12-b_west": PreRegion("3a_12-b_west", "3a_12-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-b_west"]),
- "3a_12-b_east": PreRegion("3a_12-b_east", "3a_12-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-b_east"]),
+ "3a_12-b_west": PreRegion("3a_12-b_west", "3a_12-b", connections_by_region["3a_12-b_west"], locations_by_region["3a_12-b_west"]),
+ "3a_12-b_east": PreRegion("3a_12-b_east", "3a_12-b", connections_by_region["3a_12-b_east"], locations_by_region["3a_12-b_east"]),
- "3a_13-b_top": PreRegion("3a_13-b_top", "3a_13-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_13-b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_13-b_top"]),
- "3a_13-b_bottom": PreRegion("3a_13-b_bottom", "3a_13-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_13-b_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_13-b_bottom"]),
+ "3a_13-b_top": PreRegion("3a_13-b_top", "3a_13-b", connections_by_region["3a_13-b_top"], locations_by_region["3a_13-b_top"]),
+ "3a_13-b_bottom": PreRegion("3a_13-b_bottom", "3a_13-b", connections_by_region["3a_13-b_bottom"], locations_by_region["3a_13-b_bottom"]),
- "3a_13-a_west": PreRegion("3a_13-a_west", "3a_13-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_13-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_13-a_west"]),
- "3a_13-a_south-west": PreRegion("3a_13-a_south-west", "3a_13-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_13-a_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_13-a_south-west"]),
- "3a_13-a_east": PreRegion("3a_13-a_east", "3a_13-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_13-a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_13-a_east"]),
+ "3a_13-a_west": PreRegion("3a_13-a_west", "3a_13-a", connections_by_region["3a_13-a_west"], locations_by_region["3a_13-a_west"]),
+ "3a_13-a_south-west": PreRegion("3a_13-a_south-west", "3a_13-a", connections_by_region["3a_13-a_south-west"], locations_by_region["3a_13-a_south-west"]),
+ "3a_13-a_east": PreRegion("3a_13-a_east", "3a_13-a", connections_by_region["3a_13-a_east"], locations_by_region["3a_13-a_east"]),
- "3a_13-x_west": PreRegion("3a_13-x_west", "3a_13-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_13-x_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_13-x_west"]),
- "3a_13-x_east": PreRegion("3a_13-x_east", "3a_13-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_13-x_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_13-x_east"]),
+ "3a_13-x_west": PreRegion("3a_13-x_west", "3a_13-x", connections_by_region["3a_13-x_west"], locations_by_region["3a_13-x_west"]),
+ "3a_13-x_east": PreRegion("3a_13-x_east", "3a_13-x", connections_by_region["3a_13-x_east"], locations_by_region["3a_13-x_east"]),
- "3a_12-x_west": PreRegion("3a_12-x_west", "3a_12-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-x_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-x_west"]),
- "3a_12-x_north-east": PreRegion("3a_12-x_north-east", "3a_12-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-x_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-x_north-east"]),
- "3a_12-x_east": PreRegion("3a_12-x_east", "3a_12-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_12-x_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_12-x_east"]),
+ "3a_12-x_west": PreRegion("3a_12-x_west", "3a_12-x", connections_by_region["3a_12-x_west"], locations_by_region["3a_12-x_west"]),
+ "3a_12-x_north-east": PreRegion("3a_12-x_north-east", "3a_12-x", connections_by_region["3a_12-x_north-east"], locations_by_region["3a_12-x_north-east"]),
+ "3a_12-x_east": PreRegion("3a_12-x_east", "3a_12-x", connections_by_region["3a_12-x_east"], locations_by_region["3a_12-x_east"]),
- "3a_11-a_west": PreRegion("3a_11-a_west", "3a_11-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-a_west"]),
- "3a_11-a_south": PreRegion("3a_11-a_south", "3a_11-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-a_south"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-a_south"]),
- "3a_11-a_south-east-bottom": PreRegion("3a_11-a_south-east-bottom", "3a_11-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-a_south-east-bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-a_south-east-bottom"]),
- "3a_11-a_south-east-right": PreRegion("3a_11-a_south-east-right", "3a_11-a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_11-a_south-east-right"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_11-a_south-east-right"]),
+ "3a_11-a_west": PreRegion("3a_11-a_west", "3a_11-a", connections_by_region["3a_11-a_west"], locations_by_region["3a_11-a_west"]),
+ "3a_11-a_south": PreRegion("3a_11-a_south", "3a_11-a", connections_by_region["3a_11-a_south"], locations_by_region["3a_11-a_south"]),
+ "3a_11-a_south-east-bottom": PreRegion("3a_11-a_south-east-bottom", "3a_11-a", connections_by_region["3a_11-a_south-east-bottom"], locations_by_region["3a_11-a_south-east-bottom"]),
+ "3a_11-a_south-east-right": PreRegion("3a_11-a_south-east-right", "3a_11-a", connections_by_region["3a_11-a_south-east-right"], locations_by_region["3a_11-a_south-east-right"]),
- "3a_08-x_west": PreRegion("3a_08-x_west", "3a_08-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-x_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-x_west"]),
- "3a_08-x_east": PreRegion("3a_08-x_east", "3a_08-x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-x_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-x_east"]),
+ "3a_08-x_west": PreRegion("3a_08-x_west", "3a_08-x", connections_by_region["3a_08-x_west"], locations_by_region["3a_08-x_west"]),
+ "3a_08-x_east": PreRegion("3a_08-x_east", "3a_08-x", connections_by_region["3a_08-x_east"], locations_by_region["3a_08-x_east"]),
- "3a_09-d_bottom": PreRegion("3a_09-d_bottom", "3a_09-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-d_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-d_bottom"]),
- "3a_09-d_top": PreRegion("3a_09-d_top", "3a_09-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_09-d_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_09-d_top"]),
+ "3a_09-d_bottom": PreRegion("3a_09-d_bottom", "3a_09-d", connections_by_region["3a_09-d_bottom"], locations_by_region["3a_09-d_bottom"]),
+ "3a_09-d_top": PreRegion("3a_09-d_top", "3a_09-d", connections_by_region["3a_09-d_top"], locations_by_region["3a_09-d_top"]),
- "3a_08-d_west": PreRegion("3a_08-d_west", "3a_08-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-d_west"]),
- "3a_08-d_east": PreRegion("3a_08-d_east", "3a_08-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_08-d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_08-d_east"]),
+ "3a_08-d_west": PreRegion("3a_08-d_west", "3a_08-d", connections_by_region["3a_08-d_west"], locations_by_region["3a_08-d_west"]),
+ "3a_08-d_east": PreRegion("3a_08-d_east", "3a_08-d", connections_by_region["3a_08-d_east"], locations_by_region["3a_08-d_east"]),
- "3a_06-d_west": PreRegion("3a_06-d_west", "3a_06-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-d_west"]),
- "3a_06-d_east": PreRegion("3a_06-d_east", "3a_06-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_06-d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_06-d_east"]),
+ "3a_06-d_west": PreRegion("3a_06-d_west", "3a_06-d", connections_by_region["3a_06-d_west"], locations_by_region["3a_06-d_west"]),
+ "3a_06-d_east": PreRegion("3a_06-d_east", "3a_06-d", connections_by_region["3a_06-d_east"], locations_by_region["3a_06-d_east"]),
- "3a_04-d_west": PreRegion("3a_04-d_west", "3a_04-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_04-d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_04-d_west"]),
- "3a_04-d_south-west": PreRegion("3a_04-d_south-west", "3a_04-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_04-d_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_04-d_south-west"]),
- "3a_04-d_south": PreRegion("3a_04-d_south", "3a_04-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_04-d_south"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_04-d_south"]),
- "3a_04-d_east": PreRegion("3a_04-d_east", "3a_04-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_04-d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_04-d_east"]),
+ "3a_04-d_west": PreRegion("3a_04-d_west", "3a_04-d", connections_by_region["3a_04-d_west"], locations_by_region["3a_04-d_west"]),
+ "3a_04-d_south-west": PreRegion("3a_04-d_south-west", "3a_04-d", connections_by_region["3a_04-d_south-west"], locations_by_region["3a_04-d_south-west"]),
+ "3a_04-d_south": PreRegion("3a_04-d_south", "3a_04-d", connections_by_region["3a_04-d_south"], locations_by_region["3a_04-d_south"]),
+ "3a_04-d_east": PreRegion("3a_04-d_east", "3a_04-d", connections_by_region["3a_04-d_east"], locations_by_region["3a_04-d_east"]),
- "3a_04-c_west": PreRegion("3a_04-c_west", "3a_04-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_04-c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_04-c_west"]),
- "3a_04-c_north-west": PreRegion("3a_04-c_north-west", "3a_04-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_04-c_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_04-c_north-west"]),
- "3a_04-c_east": PreRegion("3a_04-c_east", "3a_04-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_04-c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_04-c_east"]),
+ "3a_04-c_west": PreRegion("3a_04-c_west", "3a_04-c", connections_by_region["3a_04-c_west"], locations_by_region["3a_04-c_west"]),
+ "3a_04-c_north-west": PreRegion("3a_04-c_north-west", "3a_04-c", connections_by_region["3a_04-c_north-west"], locations_by_region["3a_04-c_north-west"]),
+ "3a_04-c_east": PreRegion("3a_04-c_east", "3a_04-c", connections_by_region["3a_04-c_east"], locations_by_region["3a_04-c_east"]),
- "3a_02-c_west": PreRegion("3a_02-c_west", "3a_02-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-c_west"]),
- "3a_02-c_east": PreRegion("3a_02-c_east", "3a_02-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-c_east"]),
- "3a_02-c_south-east": PreRegion("3a_02-c_south-east", "3a_02-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-c_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-c_south-east"]),
+ "3a_02-c_west": PreRegion("3a_02-c_west", "3a_02-c", connections_by_region["3a_02-c_west"], locations_by_region["3a_02-c_west"]),
+ "3a_02-c_east": PreRegion("3a_02-c_east", "3a_02-c", connections_by_region["3a_02-c_east"], locations_by_region["3a_02-c_east"]),
+ "3a_02-c_south-east": PreRegion("3a_02-c_south-east", "3a_02-c", connections_by_region["3a_02-c_south-east"], locations_by_region["3a_02-c_south-east"]),
- "3a_03-b_west": PreRegion("3a_03-b_west", "3a_03-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_03-b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_03-b_west"]),
- "3a_03-b_east": PreRegion("3a_03-b_east", "3a_03-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_03-b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_03-b_east"]),
- "3a_03-b_north": PreRegion("3a_03-b_north", "3a_03-b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_03-b_north"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_03-b_north"]),
+ "3a_03-b_west": PreRegion("3a_03-b_west", "3a_03-b", connections_by_region["3a_03-b_west"], locations_by_region["3a_03-b_west"]),
+ "3a_03-b_east": PreRegion("3a_03-b_east", "3a_03-b", connections_by_region["3a_03-b_east"], locations_by_region["3a_03-b_east"]),
+ "3a_03-b_north": PreRegion("3a_03-b_north", "3a_03-b", connections_by_region["3a_03-b_north"], locations_by_region["3a_03-b_north"]),
- "3a_01-c_west": PreRegion("3a_01-c_west", "3a_01-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_01-c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_01-c_west"]),
- "3a_01-c_east": PreRegion("3a_01-c_east", "3a_01-c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_01-c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_01-c_east"]),
+ "3a_01-c_west": PreRegion("3a_01-c_west", "3a_01-c", connections_by_region["3a_01-c_west"], locations_by_region["3a_01-c_west"]),
+ "3a_01-c_east": PreRegion("3a_01-c_east", "3a_01-c", connections_by_region["3a_01-c_east"], locations_by_region["3a_01-c_east"]),
- "3a_02-d_west": PreRegion("3a_02-d_west", "3a_02-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-d_west"]),
- "3a_02-d_east": PreRegion("3a_02-d_east", "3a_02-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_02-d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_02-d_east"]),
+ "3a_02-d_west": PreRegion("3a_02-d_west", "3a_02-d", connections_by_region["3a_02-d_west"], locations_by_region["3a_02-d_west"]),
+ "3a_02-d_east": PreRegion("3a_02-d_east", "3a_02-d", connections_by_region["3a_02-d_east"], locations_by_region["3a_02-d_east"]),
- "3a_00-d_west": PreRegion("3a_00-d_west", "3a_00-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-d_west"]),
- "3a_00-d_east": PreRegion("3a_00-d_east", "3a_00-d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_00-d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_00-d_east"]),
+ "3a_00-d_west": PreRegion("3a_00-d_west", "3a_00-d", connections_by_region["3a_00-d_west"], locations_by_region["3a_00-d_west"]),
+ "3a_00-d_east": PreRegion("3a_00-d_east", "3a_00-d", connections_by_region["3a_00-d_east"], locations_by_region["3a_00-d_east"]),
- "3a_roof00_west": PreRegion("3a_roof00_west", "3a_roof00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof00_west"]),
- "3a_roof00_east": PreRegion("3a_roof00_east", "3a_roof00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof00_east"]),
+ "3a_roof00_west": PreRegion("3a_roof00_west", "3a_roof00", connections_by_region["3a_roof00_west"], locations_by_region["3a_roof00_west"]),
+ "3a_roof00_east": PreRegion("3a_roof00_east", "3a_roof00", connections_by_region["3a_roof00_east"], locations_by_region["3a_roof00_east"]),
- "3a_roof01_west": PreRegion("3a_roof01_west", "3a_roof01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof01_west"]),
- "3a_roof01_east": PreRegion("3a_roof01_east", "3a_roof01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof01_east"]),
+ "3a_roof01_west": PreRegion("3a_roof01_west", "3a_roof01", connections_by_region["3a_roof01_west"], locations_by_region["3a_roof01_west"]),
+ "3a_roof01_east": PreRegion("3a_roof01_east", "3a_roof01", connections_by_region["3a_roof01_east"], locations_by_region["3a_roof01_east"]),
- "3a_roof02_west": PreRegion("3a_roof02_west", "3a_roof02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof02_west"]),
- "3a_roof02_east": PreRegion("3a_roof02_east", "3a_roof02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof02_east"]),
+ "3a_roof02_west": PreRegion("3a_roof02_west", "3a_roof02", connections_by_region["3a_roof02_west"], locations_by_region["3a_roof02_west"]),
+ "3a_roof02_east": PreRegion("3a_roof02_east", "3a_roof02", connections_by_region["3a_roof02_east"], locations_by_region["3a_roof02_east"]),
- "3a_roof03_west": PreRegion("3a_roof03_west", "3a_roof03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof03_west"]),
- "3a_roof03_east": PreRegion("3a_roof03_east", "3a_roof03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof03_east"]),
+ "3a_roof03_west": PreRegion("3a_roof03_west", "3a_roof03", connections_by_region["3a_roof03_west"], locations_by_region["3a_roof03_west"]),
+ "3a_roof03_east": PreRegion("3a_roof03_east", "3a_roof03", connections_by_region["3a_roof03_east"], locations_by_region["3a_roof03_east"]),
- "3a_roof04_west": PreRegion("3a_roof04_west", "3a_roof04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof04_west"]),
- "3a_roof04_east": PreRegion("3a_roof04_east", "3a_roof04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof04_east"]),
+ "3a_roof04_west": PreRegion("3a_roof04_west", "3a_roof04", connections_by_region["3a_roof04_west"], locations_by_region["3a_roof04_west"]),
+ "3a_roof04_east": PreRegion("3a_roof04_east", "3a_roof04", connections_by_region["3a_roof04_east"], locations_by_region["3a_roof04_east"]),
- "3a_roof05_west": PreRegion("3a_roof05_west", "3a_roof05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof05_west"]),
- "3a_roof05_east": PreRegion("3a_roof05_east", "3a_roof05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof05_east"]),
+ "3a_roof05_west": PreRegion("3a_roof05_west", "3a_roof05", connections_by_region["3a_roof05_west"], locations_by_region["3a_roof05_west"]),
+ "3a_roof05_east": PreRegion("3a_roof05_east", "3a_roof05", connections_by_region["3a_roof05_east"], locations_by_region["3a_roof05_east"]),
- "3a_roof06b_west": PreRegion("3a_roof06b_west", "3a_roof06b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof06b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof06b_west"]),
- "3a_roof06b_east": PreRegion("3a_roof06b_east", "3a_roof06b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof06b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof06b_east"]),
+ "3a_roof06b_west": PreRegion("3a_roof06b_west", "3a_roof06b", connections_by_region["3a_roof06b_west"], locations_by_region["3a_roof06b_west"]),
+ "3a_roof06b_east": PreRegion("3a_roof06b_east", "3a_roof06b", connections_by_region["3a_roof06b_east"], locations_by_region["3a_roof06b_east"]),
- "3a_roof06_west": PreRegion("3a_roof06_west", "3a_roof06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof06_west"]),
- "3a_roof06_east": PreRegion("3a_roof06_east", "3a_roof06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof06_east"]),
+ "3a_roof06_west": PreRegion("3a_roof06_west", "3a_roof06", connections_by_region["3a_roof06_west"], locations_by_region["3a_roof06_west"]),
+ "3a_roof06_east": PreRegion("3a_roof06_east", "3a_roof06", connections_by_region["3a_roof06_east"], locations_by_region["3a_roof06_east"]),
- "3a_roof07_west": PreRegion("3a_roof07_west", "3a_roof07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof07_west"]),
- "3a_roof07_main": PreRegion("3a_roof07_main", "3a_roof07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3a_roof07_main"], [loc for _, loc in all_locations.items() if loc.region_name == "3a_roof07_main"]),
+ "3a_roof07_west": PreRegion("3a_roof07_west", "3a_roof07", connections_by_region["3a_roof07_west"], locations_by_region["3a_roof07_west"]),
+ "3a_roof07_main": PreRegion("3a_roof07_main", "3a_roof07", connections_by_region["3a_roof07_main"], locations_by_region["3a_roof07_main"]),
- "3b_00_west": PreRegion("3b_00_west", "3b_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_00_west"]),
- "3b_00_east": PreRegion("3b_00_east", "3b_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_00_east"]),
+ "3b_00_west": PreRegion("3b_00_west", "3b_00", connections_by_region["3b_00_west"], locations_by_region["3b_00_west"]),
+ "3b_00_east": PreRegion("3b_00_east", "3b_00", connections_by_region["3b_00_east"], locations_by_region["3b_00_east"]),
- "3b_back_east": PreRegion("3b_back_east", "3b_back", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_back_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_back_east"]),
+ "3b_back_east": PreRegion("3b_back_east", "3b_back", connections_by_region["3b_back_east"], locations_by_region["3b_back_east"]),
- "3b_01_west": PreRegion("3b_01_west", "3b_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_01_west"]),
- "3b_01_east": PreRegion("3b_01_east", "3b_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_01_east"]),
+ "3b_01_west": PreRegion("3b_01_west", "3b_01", connections_by_region["3b_01_west"], locations_by_region["3b_01_west"]),
+ "3b_01_east": PreRegion("3b_01_east", "3b_01", connections_by_region["3b_01_east"], locations_by_region["3b_01_east"]),
- "3b_02_west": PreRegion("3b_02_west", "3b_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_02_west"]),
- "3b_02_east": PreRegion("3b_02_east", "3b_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_02_east"]),
+ "3b_02_west": PreRegion("3b_02_west", "3b_02", connections_by_region["3b_02_west"], locations_by_region["3b_02_west"]),
+ "3b_02_east": PreRegion("3b_02_east", "3b_02", connections_by_region["3b_02_east"], locations_by_region["3b_02_east"]),
- "3b_03_west": PreRegion("3b_03_west", "3b_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_03_west"]),
- "3b_03_east": PreRegion("3b_03_east", "3b_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_03_east"]),
+ "3b_03_west": PreRegion("3b_03_west", "3b_03", connections_by_region["3b_03_west"], locations_by_region["3b_03_west"]),
+ "3b_03_east": PreRegion("3b_03_east", "3b_03", connections_by_region["3b_03_east"], locations_by_region["3b_03_east"]),
- "3b_04_west": PreRegion("3b_04_west", "3b_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_04_west"]),
- "3b_04_east": PreRegion("3b_04_east", "3b_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_04_east"]),
+ "3b_04_west": PreRegion("3b_04_west", "3b_04", connections_by_region["3b_04_west"], locations_by_region["3b_04_west"]),
+ "3b_04_east": PreRegion("3b_04_east", "3b_04", connections_by_region["3b_04_east"], locations_by_region["3b_04_east"]),
- "3b_05_west": PreRegion("3b_05_west", "3b_05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_05_west"]),
- "3b_05_east": PreRegion("3b_05_east", "3b_05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_05_east"]),
+ "3b_05_west": PreRegion("3b_05_west", "3b_05", connections_by_region["3b_05_west"], locations_by_region["3b_05_west"]),
+ "3b_05_east": PreRegion("3b_05_east", "3b_05", connections_by_region["3b_05_east"], locations_by_region["3b_05_east"]),
- "3b_06_west": PreRegion("3b_06_west", "3b_06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_06_west"]),
- "3b_06_east": PreRegion("3b_06_east", "3b_06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_06_east"]),
+ "3b_06_west": PreRegion("3b_06_west", "3b_06", connections_by_region["3b_06_west"], locations_by_region["3b_06_west"]),
+ "3b_06_east": PreRegion("3b_06_east", "3b_06", connections_by_region["3b_06_east"], locations_by_region["3b_06_east"]),
- "3b_07_west": PreRegion("3b_07_west", "3b_07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_07_west"]),
- "3b_07_east": PreRegion("3b_07_east", "3b_07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_07_east"]),
+ "3b_07_west": PreRegion("3b_07_west", "3b_07", connections_by_region["3b_07_west"], locations_by_region["3b_07_west"]),
+ "3b_07_east": PreRegion("3b_07_east", "3b_07", connections_by_region["3b_07_east"], locations_by_region["3b_07_east"]),
- "3b_08_bottom": PreRegion("3b_08_bottom", "3b_08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_08_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_08_bottom"]),
- "3b_08_top": PreRegion("3b_08_top", "3b_08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_08_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_08_top"]),
+ "3b_08_bottom": PreRegion("3b_08_bottom", "3b_08", connections_by_region["3b_08_bottom"], locations_by_region["3b_08_bottom"]),
+ "3b_08_top": PreRegion("3b_08_top", "3b_08", connections_by_region["3b_08_top"], locations_by_region["3b_08_top"]),
- "3b_09_west": PreRegion("3b_09_west", "3b_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_09_west"]),
- "3b_09_east": PreRegion("3b_09_east", "3b_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_09_east"]),
+ "3b_09_west": PreRegion("3b_09_west", "3b_09", connections_by_region["3b_09_west"], locations_by_region["3b_09_west"]),
+ "3b_09_east": PreRegion("3b_09_east", "3b_09", connections_by_region["3b_09_east"], locations_by_region["3b_09_east"]),
- "3b_10_west": PreRegion("3b_10_west", "3b_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_10_west"]),
- "3b_10_east": PreRegion("3b_10_east", "3b_10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_10_east"]),
+ "3b_10_west": PreRegion("3b_10_west", "3b_10", connections_by_region["3b_10_west"], locations_by_region["3b_10_west"]),
+ "3b_10_east": PreRegion("3b_10_east", "3b_10", connections_by_region["3b_10_east"], locations_by_region["3b_10_east"]),
- "3b_11_west": PreRegion("3b_11_west", "3b_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_11_west"]),
- "3b_11_east": PreRegion("3b_11_east", "3b_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_11_east"]),
+ "3b_11_west": PreRegion("3b_11_west", "3b_11", connections_by_region["3b_11_west"], locations_by_region["3b_11_west"]),
+ "3b_11_east": PreRegion("3b_11_east", "3b_11", connections_by_region["3b_11_east"], locations_by_region["3b_11_east"]),
- "3b_13_west": PreRegion("3b_13_west", "3b_13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_13_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_13_west"]),
- "3b_13_east": PreRegion("3b_13_east", "3b_13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_13_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_13_east"]),
+ "3b_13_west": PreRegion("3b_13_west", "3b_13", connections_by_region["3b_13_west"], locations_by_region["3b_13_west"]),
+ "3b_13_east": PreRegion("3b_13_east", "3b_13", connections_by_region["3b_13_east"], locations_by_region["3b_13_east"]),
- "3b_14_west": PreRegion("3b_14_west", "3b_14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_14_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_14_west"]),
- "3b_14_east": PreRegion("3b_14_east", "3b_14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_14_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_14_east"]),
+ "3b_14_west": PreRegion("3b_14_west", "3b_14", connections_by_region["3b_14_west"], locations_by_region["3b_14_west"]),
+ "3b_14_east": PreRegion("3b_14_east", "3b_14", connections_by_region["3b_14_east"], locations_by_region["3b_14_east"]),
- "3b_15_west": PreRegion("3b_15_west", "3b_15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_15_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_15_west"]),
- "3b_15_east": PreRegion("3b_15_east", "3b_15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_15_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_15_east"]),
+ "3b_15_west": PreRegion("3b_15_west", "3b_15", connections_by_region["3b_15_west"], locations_by_region["3b_15_west"]),
+ "3b_15_east": PreRegion("3b_15_east", "3b_15", connections_by_region["3b_15_east"], locations_by_region["3b_15_east"]),
- "3b_12_west": PreRegion("3b_12_west", "3b_12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_12_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_12_west"]),
- "3b_12_east": PreRegion("3b_12_east", "3b_12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_12_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_12_east"]),
+ "3b_12_west": PreRegion("3b_12_west", "3b_12", connections_by_region["3b_12_west"], locations_by_region["3b_12_west"]),
+ "3b_12_east": PreRegion("3b_12_east", "3b_12", connections_by_region["3b_12_east"], locations_by_region["3b_12_east"]),
- "3b_16_west": PreRegion("3b_16_west", "3b_16", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_16_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_16_west"]),
- "3b_16_top": PreRegion("3b_16_top", "3b_16", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_16_top"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_16_top"]),
+ "3b_16_west": PreRegion("3b_16_west", "3b_16", connections_by_region["3b_16_west"], locations_by_region["3b_16_west"]),
+ "3b_16_top": PreRegion("3b_16_top", "3b_16", connections_by_region["3b_16_top"], locations_by_region["3b_16_top"]),
- "3b_17_west": PreRegion("3b_17_west", "3b_17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_17_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_17_west"]),
- "3b_17_east": PreRegion("3b_17_east", "3b_17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_17_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_17_east"]),
+ "3b_17_west": PreRegion("3b_17_west", "3b_17", connections_by_region["3b_17_west"], locations_by_region["3b_17_west"]),
+ "3b_17_east": PreRegion("3b_17_east", "3b_17", connections_by_region["3b_17_east"], locations_by_region["3b_17_east"]),
- "3b_18_west": PreRegion("3b_18_west", "3b_18", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_18_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_18_west"]),
- "3b_18_east": PreRegion("3b_18_east", "3b_18", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_18_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_18_east"]),
+ "3b_18_west": PreRegion("3b_18_west", "3b_18", connections_by_region["3b_18_west"], locations_by_region["3b_18_west"]),
+ "3b_18_east": PreRegion("3b_18_east", "3b_18", connections_by_region["3b_18_east"], locations_by_region["3b_18_east"]),
- "3b_19_west": PreRegion("3b_19_west", "3b_19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_19_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_19_west"]),
- "3b_19_east": PreRegion("3b_19_east", "3b_19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_19_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_19_east"]),
+ "3b_19_west": PreRegion("3b_19_west", "3b_19", connections_by_region["3b_19_west"], locations_by_region["3b_19_west"]),
+ "3b_19_east": PreRegion("3b_19_east", "3b_19", connections_by_region["3b_19_east"], locations_by_region["3b_19_east"]),
- "3b_21_west": PreRegion("3b_21_west", "3b_21", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_21_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_21_west"]),
- "3b_21_east": PreRegion("3b_21_east", "3b_21", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_21_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_21_east"]),
+ "3b_21_west": PreRegion("3b_21_west", "3b_21", connections_by_region["3b_21_west"], locations_by_region["3b_21_west"]),
+ "3b_21_east": PreRegion("3b_21_east", "3b_21", connections_by_region["3b_21_east"], locations_by_region["3b_21_east"]),
- "3b_20_west": PreRegion("3b_20_west", "3b_20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_20_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_20_west"]),
- "3b_20_east": PreRegion("3b_20_east", "3b_20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_20_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_20_east"]),
+ "3b_20_west": PreRegion("3b_20_west", "3b_20", connections_by_region["3b_20_west"], locations_by_region["3b_20_west"]),
+ "3b_20_east": PreRegion("3b_20_east", "3b_20", connections_by_region["3b_20_east"], locations_by_region["3b_20_east"]),
- "3b_end_west": PreRegion("3b_end_west", "3b_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_end_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_end_west"]),
- "3b_end_goal": PreRegion("3b_end_goal", "3b_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3b_end_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "3b_end_goal"]),
+ "3b_end_west": PreRegion("3b_end_west", "3b_end", connections_by_region["3b_end_west"], locations_by_region["3b_end_west"]),
+ "3b_end_goal": PreRegion("3b_end_goal", "3b_end", connections_by_region["3b_end_goal"], locations_by_region["3b_end_goal"]),
- "3c_00_west": PreRegion("3c_00_west", "3c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3c_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3c_00_west"]),
- "3c_00_east": PreRegion("3c_00_east", "3c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3c_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3c_00_east"]),
+ "3c_00_west": PreRegion("3c_00_west", "3c_00", connections_by_region["3c_00_west"], locations_by_region["3c_00_west"]),
+ "3c_00_east": PreRegion("3c_00_east", "3c_00", connections_by_region["3c_00_east"], locations_by_region["3c_00_east"]),
- "3c_01_west": PreRegion("3c_01_west", "3c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3c_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3c_01_west"]),
- "3c_01_east": PreRegion("3c_01_east", "3c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3c_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "3c_01_east"]),
+ "3c_01_west": PreRegion("3c_01_west", "3c_01", connections_by_region["3c_01_west"], locations_by_region["3c_01_west"]),
+ "3c_01_east": PreRegion("3c_01_east", "3c_01", connections_by_region["3c_01_east"], locations_by_region["3c_01_east"]),
- "3c_02_west": PreRegion("3c_02_west", "3c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3c_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "3c_02_west"]),
- "3c_02_goal": PreRegion("3c_02_goal", "3c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "3c_02_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "3c_02_goal"]),
+ "3c_02_west": PreRegion("3c_02_west", "3c_02", connections_by_region["3c_02_west"], locations_by_region["3c_02_west"]),
+ "3c_02_goal": PreRegion("3c_02_goal", "3c_02", connections_by_region["3c_02_goal"], locations_by_region["3c_02_goal"]),
- "4a_a-00_west": PreRegion("4a_a-00_west", "4a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-00_west"]),
- "4a_a-00_east": PreRegion("4a_a-00_east", "4a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-00_east"]),
+ "4a_a-00_west": PreRegion("4a_a-00_west", "4a_a-00", connections_by_region["4a_a-00_west"], locations_by_region["4a_a-00_west"]),
+ "4a_a-00_east": PreRegion("4a_a-00_east", "4a_a-00", connections_by_region["4a_a-00_east"], locations_by_region["4a_a-00_east"]),
- "4a_a-01_west": PreRegion("4a_a-01_west", "4a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-01_west"]),
- "4a_a-01_east": PreRegion("4a_a-01_east", "4a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-01_east"]),
+ "4a_a-01_west": PreRegion("4a_a-01_west", "4a_a-01", connections_by_region["4a_a-01_west"], locations_by_region["4a_a-01_west"]),
+ "4a_a-01_east": PreRegion("4a_a-01_east", "4a_a-01", connections_by_region["4a_a-01_east"], locations_by_region["4a_a-01_east"]),
- "4a_a-01x_west": PreRegion("4a_a-01x_west", "4a_a-01x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-01x_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-01x_west"]),
- "4a_a-01x_east": PreRegion("4a_a-01x_east", "4a_a-01x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-01x_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-01x_east"]),
+ "4a_a-01x_west": PreRegion("4a_a-01x_west", "4a_a-01x", connections_by_region["4a_a-01x_west"], locations_by_region["4a_a-01x_west"]),
+ "4a_a-01x_east": PreRegion("4a_a-01x_east", "4a_a-01x", connections_by_region["4a_a-01x_east"], locations_by_region["4a_a-01x_east"]),
- "4a_a-02_west": PreRegion("4a_a-02_west", "4a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-02_west"]),
- "4a_a-02_east": PreRegion("4a_a-02_east", "4a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-02_east"]),
+ "4a_a-02_west": PreRegion("4a_a-02_west", "4a_a-02", connections_by_region["4a_a-02_west"], locations_by_region["4a_a-02_west"]),
+ "4a_a-02_east": PreRegion("4a_a-02_east", "4a_a-02", connections_by_region["4a_a-02_east"], locations_by_region["4a_a-02_east"]),
- "4a_a-03_west": PreRegion("4a_a-03_west", "4a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-03_west"]),
- "4a_a-03_east": PreRegion("4a_a-03_east", "4a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-03_east"]),
+ "4a_a-03_west": PreRegion("4a_a-03_west", "4a_a-03", connections_by_region["4a_a-03_west"], locations_by_region["4a_a-03_west"]),
+ "4a_a-03_east": PreRegion("4a_a-03_east", "4a_a-03", connections_by_region["4a_a-03_east"], locations_by_region["4a_a-03_east"]),
- "4a_a-04_west": PreRegion("4a_a-04_west", "4a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-04_west"]),
- "4a_a-04_east": PreRegion("4a_a-04_east", "4a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-04_east"]),
+ "4a_a-04_west": PreRegion("4a_a-04_west", "4a_a-04", connections_by_region["4a_a-04_west"], locations_by_region["4a_a-04_west"]),
+ "4a_a-04_east": PreRegion("4a_a-04_east", "4a_a-04", connections_by_region["4a_a-04_east"], locations_by_region["4a_a-04_east"]),
- "4a_a-05_west": PreRegion("4a_a-05_west", "4a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-05_west"]),
- "4a_a-05_east": PreRegion("4a_a-05_east", "4a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-05_east"]),
+ "4a_a-05_west": PreRegion("4a_a-05_west", "4a_a-05", connections_by_region["4a_a-05_west"], locations_by_region["4a_a-05_west"]),
+ "4a_a-05_east": PreRegion("4a_a-05_east", "4a_a-05", connections_by_region["4a_a-05_east"], locations_by_region["4a_a-05_east"]),
- "4a_a-06_west": PreRegion("4a_a-06_west", "4a_a-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-06_west"]),
- "4a_a-06_east": PreRegion("4a_a-06_east", "4a_a-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-06_east"]),
+ "4a_a-06_west": PreRegion("4a_a-06_west", "4a_a-06", connections_by_region["4a_a-06_west"], locations_by_region["4a_a-06_west"]),
+ "4a_a-06_east": PreRegion("4a_a-06_east", "4a_a-06", connections_by_region["4a_a-06_east"], locations_by_region["4a_a-06_east"]),
- "4a_a-07_west": PreRegion("4a_a-07_west", "4a_a-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-07_west"]),
- "4a_a-07_east": PreRegion("4a_a-07_east", "4a_a-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-07_east"]),
+ "4a_a-07_west": PreRegion("4a_a-07_west", "4a_a-07", connections_by_region["4a_a-07_west"], locations_by_region["4a_a-07_west"]),
+ "4a_a-07_east": PreRegion("4a_a-07_east", "4a_a-07", connections_by_region["4a_a-07_east"], locations_by_region["4a_a-07_east"]),
- "4a_a-08_west": PreRegion("4a_a-08_west", "4a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-08_west"]),
- "4a_a-08_north-west": PreRegion("4a_a-08_north-west", "4a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-08_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-08_north-west"]),
- "4a_a-08_east": PreRegion("4a_a-08_east", "4a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-08_east"]),
+ "4a_a-08_west": PreRegion("4a_a-08_west", "4a_a-08", connections_by_region["4a_a-08_west"], locations_by_region["4a_a-08_west"]),
+ "4a_a-08_north-west": PreRegion("4a_a-08_north-west", "4a_a-08", connections_by_region["4a_a-08_north-west"], locations_by_region["4a_a-08_north-west"]),
+ "4a_a-08_east": PreRegion("4a_a-08_east", "4a_a-08", connections_by_region["4a_a-08_east"], locations_by_region["4a_a-08_east"]),
- "4a_a-10_west": PreRegion("4a_a-10_west", "4a_a-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-10_west"]),
- "4a_a-10_east": PreRegion("4a_a-10_east", "4a_a-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-10_east"]),
+ "4a_a-10_west": PreRegion("4a_a-10_west", "4a_a-10", connections_by_region["4a_a-10_west"], locations_by_region["4a_a-10_west"]),
+ "4a_a-10_east": PreRegion("4a_a-10_east", "4a_a-10", connections_by_region["4a_a-10_east"], locations_by_region["4a_a-10_east"]),
- "4a_a-11_east": PreRegion("4a_a-11_east", "4a_a-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-11_east"]),
+ "4a_a-11_east": PreRegion("4a_a-11_east", "4a_a-11", connections_by_region["4a_a-11_east"], locations_by_region["4a_a-11_east"]),
- "4a_a-09_bottom": PreRegion("4a_a-09_bottom", "4a_a-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-09_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-09_bottom"]),
- "4a_a-09_top": PreRegion("4a_a-09_top", "4a_a-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_a-09_top"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_a-09_top"]),
+ "4a_a-09_bottom": PreRegion("4a_a-09_bottom", "4a_a-09", connections_by_region["4a_a-09_bottom"], locations_by_region["4a_a-09_bottom"]),
+ "4a_a-09_top": PreRegion("4a_a-09_top", "4a_a-09", connections_by_region["4a_a-09_top"], locations_by_region["4a_a-09_top"]),
- "4a_b-00_south": PreRegion("4a_b-00_south", "4a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-00_south"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-00_south"]),
- "4a_b-00_south-east": PreRegion("4a_b-00_south-east", "4a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-00_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-00_south-east"]),
- "4a_b-00_east": PreRegion("4a_b-00_east", "4a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-00_east"]),
- "4a_b-00_west": PreRegion("4a_b-00_west", "4a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-00_west"]),
- "4a_b-00_north-east": PreRegion("4a_b-00_north-east", "4a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-00_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-00_north-east"]),
- "4a_b-00_north-west": PreRegion("4a_b-00_north-west", "4a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-00_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-00_north-west"]),
- "4a_b-00_north": PreRegion("4a_b-00_north", "4a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-00_north"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-00_north"]),
+ "4a_b-00_south": PreRegion("4a_b-00_south", "4a_b-00", connections_by_region["4a_b-00_south"], locations_by_region["4a_b-00_south"]),
+ "4a_b-00_south-east": PreRegion("4a_b-00_south-east", "4a_b-00", connections_by_region["4a_b-00_south-east"], locations_by_region["4a_b-00_south-east"]),
+ "4a_b-00_east": PreRegion("4a_b-00_east", "4a_b-00", connections_by_region["4a_b-00_east"], locations_by_region["4a_b-00_east"]),
+ "4a_b-00_west": PreRegion("4a_b-00_west", "4a_b-00", connections_by_region["4a_b-00_west"], locations_by_region["4a_b-00_west"]),
+ "4a_b-00_north-east": PreRegion("4a_b-00_north-east", "4a_b-00", connections_by_region["4a_b-00_north-east"], locations_by_region["4a_b-00_north-east"]),
+ "4a_b-00_north-west": PreRegion("4a_b-00_north-west", "4a_b-00", connections_by_region["4a_b-00_north-west"], locations_by_region["4a_b-00_north-west"]),
+ "4a_b-00_north": PreRegion("4a_b-00_north", "4a_b-00", connections_by_region["4a_b-00_north"], locations_by_region["4a_b-00_north"]),
- "4a_b-01_west": PreRegion("4a_b-01_west", "4a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-01_west"]),
+ "4a_b-01_west": PreRegion("4a_b-01_west", "4a_b-01", connections_by_region["4a_b-01_west"], locations_by_region["4a_b-01_west"]),
- "4a_b-04_west": PreRegion("4a_b-04_west", "4a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-04_west"]),
- "4a_b-04_north-west": PreRegion("4a_b-04_north-west", "4a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-04_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-04_north-west"]),
- "4a_b-04_east": PreRegion("4a_b-04_east", "4a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-04_east"]),
+ "4a_b-04_west": PreRegion("4a_b-04_west", "4a_b-04", connections_by_region["4a_b-04_west"], locations_by_region["4a_b-04_west"]),
+ "4a_b-04_north-west": PreRegion("4a_b-04_north-west", "4a_b-04", connections_by_region["4a_b-04_north-west"], locations_by_region["4a_b-04_north-west"]),
+ "4a_b-04_east": PreRegion("4a_b-04_east", "4a_b-04", connections_by_region["4a_b-04_east"], locations_by_region["4a_b-04_east"]),
- "4a_b-06_west": PreRegion("4a_b-06_west", "4a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-06_west"]),
- "4a_b-06_east": PreRegion("4a_b-06_east", "4a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-06_east"]),
+ "4a_b-06_west": PreRegion("4a_b-06_west", "4a_b-06", connections_by_region["4a_b-06_west"], locations_by_region["4a_b-06_west"]),
+ "4a_b-06_east": PreRegion("4a_b-06_east", "4a_b-06", connections_by_region["4a_b-06_east"], locations_by_region["4a_b-06_east"]),
- "4a_b-07_west": PreRegion("4a_b-07_west", "4a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-07_west"]),
- "4a_b-07_east": PreRegion("4a_b-07_east", "4a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-07_east"]),
+ "4a_b-07_west": PreRegion("4a_b-07_west", "4a_b-07", connections_by_region["4a_b-07_west"], locations_by_region["4a_b-07_west"]),
+ "4a_b-07_east": PreRegion("4a_b-07_east", "4a_b-07", connections_by_region["4a_b-07_east"], locations_by_region["4a_b-07_east"]),
- "4a_b-03_west": PreRegion("4a_b-03_west", "4a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-03_west"]),
- "4a_b-03_east": PreRegion("4a_b-03_east", "4a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-03_east"]),
+ "4a_b-03_west": PreRegion("4a_b-03_west", "4a_b-03", connections_by_region["4a_b-03_west"], locations_by_region["4a_b-03_west"]),
+ "4a_b-03_east": PreRegion("4a_b-03_east", "4a_b-03", connections_by_region["4a_b-03_east"], locations_by_region["4a_b-03_east"]),
- "4a_b-02_south-west": PreRegion("4a_b-02_south-west", "4a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-02_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-02_south-west"]),
- "4a_b-02_north-west": PreRegion("4a_b-02_north-west", "4a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-02_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-02_north-west"]),
- "4a_b-02_north-east": PreRegion("4a_b-02_north-east", "4a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-02_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-02_north-east"]),
- "4a_b-02_north": PreRegion("4a_b-02_north", "4a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-02_north"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-02_north"]),
+ "4a_b-02_south-west": PreRegion("4a_b-02_south-west", "4a_b-02", connections_by_region["4a_b-02_south-west"], locations_by_region["4a_b-02_south-west"]),
+ "4a_b-02_north-west": PreRegion("4a_b-02_north-west", "4a_b-02", connections_by_region["4a_b-02_north-west"], locations_by_region["4a_b-02_north-west"]),
+ "4a_b-02_north-east": PreRegion("4a_b-02_north-east", "4a_b-02", connections_by_region["4a_b-02_north-east"], locations_by_region["4a_b-02_north-east"]),
+ "4a_b-02_north": PreRegion("4a_b-02_north", "4a_b-02", connections_by_region["4a_b-02_north"], locations_by_region["4a_b-02_north"]),
- "4a_b-sec_west": PreRegion("4a_b-sec_west", "4a_b-sec", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-sec_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-sec_west"]),
- "4a_b-sec_east": PreRegion("4a_b-sec_east", "4a_b-sec", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-sec_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-sec_east"]),
+ "4a_b-sec_west": PreRegion("4a_b-sec_west", "4a_b-sec", connections_by_region["4a_b-sec_west"], locations_by_region["4a_b-sec_west"]),
+ "4a_b-sec_east": PreRegion("4a_b-sec_east", "4a_b-sec", connections_by_region["4a_b-sec_east"], locations_by_region["4a_b-sec_east"]),
- "4a_b-secb_west": PreRegion("4a_b-secb_west", "4a_b-secb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-secb_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-secb_west"]),
+ "4a_b-secb_west": PreRegion("4a_b-secb_west", "4a_b-secb", connections_by_region["4a_b-secb_west"], locations_by_region["4a_b-secb_west"]),
- "4a_b-05_center": PreRegion("4a_b-05_center", "4a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-05_center"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-05_center"]),
- "4a_b-05_west": PreRegion("4a_b-05_west", "4a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-05_west"]),
- "4a_b-05_north-east": PreRegion("4a_b-05_north-east", "4a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-05_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-05_north-east"]),
- "4a_b-05_east": PreRegion("4a_b-05_east", "4a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-05_east"]),
+ "4a_b-05_center": PreRegion("4a_b-05_center", "4a_b-05", connections_by_region["4a_b-05_center"], locations_by_region["4a_b-05_center"]),
+ "4a_b-05_west": PreRegion("4a_b-05_west", "4a_b-05", connections_by_region["4a_b-05_west"], locations_by_region["4a_b-05_west"]),
+ "4a_b-05_north-east": PreRegion("4a_b-05_north-east", "4a_b-05", connections_by_region["4a_b-05_north-east"], locations_by_region["4a_b-05_north-east"]),
+ "4a_b-05_east": PreRegion("4a_b-05_east", "4a_b-05", connections_by_region["4a_b-05_east"], locations_by_region["4a_b-05_east"]),
- "4a_b-08b_west": PreRegion("4a_b-08b_west", "4a_b-08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-08b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-08b_west"]),
- "4a_b-08b_east": PreRegion("4a_b-08b_east", "4a_b-08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-08b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-08b_east"]),
+ "4a_b-08b_west": PreRegion("4a_b-08b_west", "4a_b-08b", connections_by_region["4a_b-08b_west"], locations_by_region["4a_b-08b_west"]),
+ "4a_b-08b_east": PreRegion("4a_b-08b_east", "4a_b-08b", connections_by_region["4a_b-08b_east"], locations_by_region["4a_b-08b_east"]),
- "4a_b-08_west": PreRegion("4a_b-08_west", "4a_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-08_west"]),
- "4a_b-08_east": PreRegion("4a_b-08_east", "4a_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_b-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_b-08_east"]),
+ "4a_b-08_west": PreRegion("4a_b-08_west", "4a_b-08", connections_by_region["4a_b-08_west"], locations_by_region["4a_b-08_west"]),
+ "4a_b-08_east": PreRegion("4a_b-08_east", "4a_b-08", connections_by_region["4a_b-08_east"], locations_by_region["4a_b-08_east"]),
- "4a_c-00_west": PreRegion("4a_c-00_west", "4a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-00_west"]),
- "4a_c-00_east": PreRegion("4a_c-00_east", "4a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-00_east"]),
- "4a_c-00_north-west": PreRegion("4a_c-00_north-west", "4a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-00_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-00_north-west"]),
+ "4a_c-00_west": PreRegion("4a_c-00_west", "4a_c-00", connections_by_region["4a_c-00_west"], locations_by_region["4a_c-00_west"]),
+ "4a_c-00_east": PreRegion("4a_c-00_east", "4a_c-00", connections_by_region["4a_c-00_east"], locations_by_region["4a_c-00_east"]),
+ "4a_c-00_north-west": PreRegion("4a_c-00_north-west", "4a_c-00", connections_by_region["4a_c-00_north-west"], locations_by_region["4a_c-00_north-west"]),
- "4a_c-01_east": PreRegion("4a_c-01_east", "4a_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-01_east"]),
+ "4a_c-01_east": PreRegion("4a_c-01_east", "4a_c-01", connections_by_region["4a_c-01_east"], locations_by_region["4a_c-01_east"]),
- "4a_c-02_west": PreRegion("4a_c-02_west", "4a_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-02_west"]),
- "4a_c-02_east": PreRegion("4a_c-02_east", "4a_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-02_east"]),
+ "4a_c-02_west": PreRegion("4a_c-02_west", "4a_c-02", connections_by_region["4a_c-02_west"], locations_by_region["4a_c-02_west"]),
+ "4a_c-02_east": PreRegion("4a_c-02_east", "4a_c-02", connections_by_region["4a_c-02_east"], locations_by_region["4a_c-02_east"]),
- "4a_c-04_west": PreRegion("4a_c-04_west", "4a_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-04_west"]),
- "4a_c-04_east": PreRegion("4a_c-04_east", "4a_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-04_east"]),
+ "4a_c-04_west": PreRegion("4a_c-04_west", "4a_c-04", connections_by_region["4a_c-04_west"], locations_by_region["4a_c-04_west"]),
+ "4a_c-04_east": PreRegion("4a_c-04_east", "4a_c-04", connections_by_region["4a_c-04_east"], locations_by_region["4a_c-04_east"]),
- "4a_c-05_west": PreRegion("4a_c-05_west", "4a_c-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-05_west"]),
- "4a_c-05_east": PreRegion("4a_c-05_east", "4a_c-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-05_east"]),
+ "4a_c-05_west": PreRegion("4a_c-05_west", "4a_c-05", connections_by_region["4a_c-05_west"], locations_by_region["4a_c-05_west"]),
+ "4a_c-05_east": PreRegion("4a_c-05_east", "4a_c-05", connections_by_region["4a_c-05_east"], locations_by_region["4a_c-05_east"]),
- "4a_c-06_bottom": PreRegion("4a_c-06_bottom", "4a_c-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-06_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-06_bottom"]),
- "4a_c-06_west": PreRegion("4a_c-06_west", "4a_c-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-06_west"]),
- "4a_c-06_top": PreRegion("4a_c-06_top", "4a_c-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-06_top"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-06_top"]),
+ "4a_c-06_bottom": PreRegion("4a_c-06_bottom", "4a_c-06", connections_by_region["4a_c-06_bottom"], locations_by_region["4a_c-06_bottom"]),
+ "4a_c-06_west": PreRegion("4a_c-06_west", "4a_c-06", connections_by_region["4a_c-06_west"], locations_by_region["4a_c-06_west"]),
+ "4a_c-06_top": PreRegion("4a_c-06_top", "4a_c-06", connections_by_region["4a_c-06_top"], locations_by_region["4a_c-06_top"]),
- "4a_c-06b_east": PreRegion("4a_c-06b_east", "4a_c-06b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-06b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-06b_east"]),
+ "4a_c-06b_east": PreRegion("4a_c-06b_east", "4a_c-06b", connections_by_region["4a_c-06b_east"], locations_by_region["4a_c-06b_east"]),
- "4a_c-09_west": PreRegion("4a_c-09_west", "4a_c-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-09_west"]),
- "4a_c-09_east": PreRegion("4a_c-09_east", "4a_c-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-09_east"]),
+ "4a_c-09_west": PreRegion("4a_c-09_west", "4a_c-09", connections_by_region["4a_c-09_west"], locations_by_region["4a_c-09_west"]),
+ "4a_c-09_east": PreRegion("4a_c-09_east", "4a_c-09", connections_by_region["4a_c-09_east"], locations_by_region["4a_c-09_east"]),
- "4a_c-07_west": PreRegion("4a_c-07_west", "4a_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-07_west"]),
- "4a_c-07_east": PreRegion("4a_c-07_east", "4a_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-07_east"]),
+ "4a_c-07_west": PreRegion("4a_c-07_west", "4a_c-07", connections_by_region["4a_c-07_west"], locations_by_region["4a_c-07_west"]),
+ "4a_c-07_east": PreRegion("4a_c-07_east", "4a_c-07", connections_by_region["4a_c-07_east"], locations_by_region["4a_c-07_east"]),
- "4a_c-08_bottom": PreRegion("4a_c-08_bottom", "4a_c-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-08_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-08_bottom"]),
- "4a_c-08_east": PreRegion("4a_c-08_east", "4a_c-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-08_east"]),
- "4a_c-08_top": PreRegion("4a_c-08_top", "4a_c-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-08_top"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-08_top"]),
+ "4a_c-08_bottom": PreRegion("4a_c-08_bottom", "4a_c-08", connections_by_region["4a_c-08_bottom"], locations_by_region["4a_c-08_bottom"]),
+ "4a_c-08_east": PreRegion("4a_c-08_east", "4a_c-08", connections_by_region["4a_c-08_east"], locations_by_region["4a_c-08_east"]),
+ "4a_c-08_top": PreRegion("4a_c-08_top", "4a_c-08", connections_by_region["4a_c-08_top"], locations_by_region["4a_c-08_top"]),
- "4a_c-10_bottom": PreRegion("4a_c-10_bottom", "4a_c-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-10_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-10_bottom"]),
- "4a_c-10_top": PreRegion("4a_c-10_top", "4a_c-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_c-10_top"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_c-10_top"]),
+ "4a_c-10_bottom": PreRegion("4a_c-10_bottom", "4a_c-10", connections_by_region["4a_c-10_bottom"], locations_by_region["4a_c-10_bottom"]),
+ "4a_c-10_top": PreRegion("4a_c-10_top", "4a_c-10", connections_by_region["4a_c-10_top"], locations_by_region["4a_c-10_top"]),
- "4a_d-00_west": PreRegion("4a_d-00_west", "4a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-00_west"]),
- "4a_d-00_south": PreRegion("4a_d-00_south", "4a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-00_south"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-00_south"]),
- "4a_d-00_east": PreRegion("4a_d-00_east", "4a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-00_east"]),
- "4a_d-00_north-west": PreRegion("4a_d-00_north-west", "4a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-00_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-00_north-west"]),
+ "4a_d-00_west": PreRegion("4a_d-00_west", "4a_d-00", connections_by_region["4a_d-00_west"], locations_by_region["4a_d-00_west"]),
+ "4a_d-00_south": PreRegion("4a_d-00_south", "4a_d-00", connections_by_region["4a_d-00_south"], locations_by_region["4a_d-00_south"]),
+ "4a_d-00_east": PreRegion("4a_d-00_east", "4a_d-00", connections_by_region["4a_d-00_east"], locations_by_region["4a_d-00_east"]),
+ "4a_d-00_north-west": PreRegion("4a_d-00_north-west", "4a_d-00", connections_by_region["4a_d-00_north-west"], locations_by_region["4a_d-00_north-west"]),
- "4a_d-00b_east": PreRegion("4a_d-00b_east", "4a_d-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-00b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-00b_east"]),
+ "4a_d-00b_east": PreRegion("4a_d-00b_east", "4a_d-00b", connections_by_region["4a_d-00b_east"], locations_by_region["4a_d-00b_east"]),
- "4a_d-01_west": PreRegion("4a_d-01_west", "4a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-01_west"]),
- "4a_d-01_east": PreRegion("4a_d-01_east", "4a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-01_east"]),
+ "4a_d-01_west": PreRegion("4a_d-01_west", "4a_d-01", connections_by_region["4a_d-01_west"], locations_by_region["4a_d-01_west"]),
+ "4a_d-01_east": PreRegion("4a_d-01_east", "4a_d-01", connections_by_region["4a_d-01_east"], locations_by_region["4a_d-01_east"]),
- "4a_d-02_west": PreRegion("4a_d-02_west", "4a_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-02_west"]),
- "4a_d-02_east": PreRegion("4a_d-02_east", "4a_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-02_east"]),
+ "4a_d-02_west": PreRegion("4a_d-02_west", "4a_d-02", connections_by_region["4a_d-02_west"], locations_by_region["4a_d-02_west"]),
+ "4a_d-02_east": PreRegion("4a_d-02_east", "4a_d-02", connections_by_region["4a_d-02_east"], locations_by_region["4a_d-02_east"]),
- "4a_d-03_west": PreRegion("4a_d-03_west", "4a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-03_west"]),
- "4a_d-03_east": PreRegion("4a_d-03_east", "4a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-03_east"]),
+ "4a_d-03_west": PreRegion("4a_d-03_west", "4a_d-03", connections_by_region["4a_d-03_west"], locations_by_region["4a_d-03_west"]),
+ "4a_d-03_east": PreRegion("4a_d-03_east", "4a_d-03", connections_by_region["4a_d-03_east"], locations_by_region["4a_d-03_east"]),
- "4a_d-04_west": PreRegion("4a_d-04_west", "4a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-04_west"]),
- "4a_d-04_east": PreRegion("4a_d-04_east", "4a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-04_east"]),
+ "4a_d-04_west": PreRegion("4a_d-04_west", "4a_d-04", connections_by_region["4a_d-04_west"], locations_by_region["4a_d-04_west"]),
+ "4a_d-04_east": PreRegion("4a_d-04_east", "4a_d-04", connections_by_region["4a_d-04_east"], locations_by_region["4a_d-04_east"]),
- "4a_d-05_west": PreRegion("4a_d-05_west", "4a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-05_west"]),
- "4a_d-05_east": PreRegion("4a_d-05_east", "4a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-05_east"]),
+ "4a_d-05_west": PreRegion("4a_d-05_west", "4a_d-05", connections_by_region["4a_d-05_west"], locations_by_region["4a_d-05_west"]),
+ "4a_d-05_east": PreRegion("4a_d-05_east", "4a_d-05", connections_by_region["4a_d-05_east"], locations_by_region["4a_d-05_east"]),
- "4a_d-06_west": PreRegion("4a_d-06_west", "4a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-06_west"]),
- "4a_d-06_east": PreRegion("4a_d-06_east", "4a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-06_east"]),
+ "4a_d-06_west": PreRegion("4a_d-06_west", "4a_d-06", connections_by_region["4a_d-06_west"], locations_by_region["4a_d-06_west"]),
+ "4a_d-06_east": PreRegion("4a_d-06_east", "4a_d-06", connections_by_region["4a_d-06_east"], locations_by_region["4a_d-06_east"]),
- "4a_d-07_west": PreRegion("4a_d-07_west", "4a_d-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-07_west"]),
- "4a_d-07_east": PreRegion("4a_d-07_east", "4a_d-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-07_east"]),
+ "4a_d-07_west": PreRegion("4a_d-07_west", "4a_d-07", connections_by_region["4a_d-07_west"], locations_by_region["4a_d-07_west"]),
+ "4a_d-07_east": PreRegion("4a_d-07_east", "4a_d-07", connections_by_region["4a_d-07_east"], locations_by_region["4a_d-07_east"]),
- "4a_d-08_west": PreRegion("4a_d-08_west", "4a_d-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-08_west"]),
- "4a_d-08_east": PreRegion("4a_d-08_east", "4a_d-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-08_east"]),
+ "4a_d-08_west": PreRegion("4a_d-08_west", "4a_d-08", connections_by_region["4a_d-08_west"], locations_by_region["4a_d-08_west"]),
+ "4a_d-08_east": PreRegion("4a_d-08_east", "4a_d-08", connections_by_region["4a_d-08_east"], locations_by_region["4a_d-08_east"]),
- "4a_d-09_west": PreRegion("4a_d-09_west", "4a_d-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-09_west"]),
- "4a_d-09_east": PreRegion("4a_d-09_east", "4a_d-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-09_east"]),
+ "4a_d-09_west": PreRegion("4a_d-09_west", "4a_d-09", connections_by_region["4a_d-09_west"], locations_by_region["4a_d-09_west"]),
+ "4a_d-09_east": PreRegion("4a_d-09_east", "4a_d-09", connections_by_region["4a_d-09_east"], locations_by_region["4a_d-09_east"]),
- "4a_d-10_west": PreRegion("4a_d-10_west", "4a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-10_west"]),
- "4a_d-10_goal": PreRegion("4a_d-10_goal", "4a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4a_d-10_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "4a_d-10_goal"]),
+ "4a_d-10_west": PreRegion("4a_d-10_west", "4a_d-10", connections_by_region["4a_d-10_west"], locations_by_region["4a_d-10_west"]),
+ "4a_d-10_goal": PreRegion("4a_d-10_goal", "4a_d-10", connections_by_region["4a_d-10_goal"], locations_by_region["4a_d-10_goal"]),
- "4b_a-00_west": PreRegion("4b_a-00_west", "4b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-00_west"]),
- "4b_a-00_east": PreRegion("4b_a-00_east", "4b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-00_east"]),
+ "4b_a-00_west": PreRegion("4b_a-00_west", "4b_a-00", connections_by_region["4b_a-00_west"], locations_by_region["4b_a-00_west"]),
+ "4b_a-00_east": PreRegion("4b_a-00_east", "4b_a-00", connections_by_region["4b_a-00_east"], locations_by_region["4b_a-00_east"]),
- "4b_a-01_west": PreRegion("4b_a-01_west", "4b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-01_west"]),
- "4b_a-01_east": PreRegion("4b_a-01_east", "4b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-01_east"]),
+ "4b_a-01_west": PreRegion("4b_a-01_west", "4b_a-01", connections_by_region["4b_a-01_west"], locations_by_region["4b_a-01_west"]),
+ "4b_a-01_east": PreRegion("4b_a-01_east", "4b_a-01", connections_by_region["4b_a-01_east"], locations_by_region["4b_a-01_east"]),
- "4b_a-02_west": PreRegion("4b_a-02_west", "4b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-02_west"]),
- "4b_a-02_east": PreRegion("4b_a-02_east", "4b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-02_east"]),
+ "4b_a-02_west": PreRegion("4b_a-02_west", "4b_a-02", connections_by_region["4b_a-02_west"], locations_by_region["4b_a-02_west"]),
+ "4b_a-02_east": PreRegion("4b_a-02_east", "4b_a-02", connections_by_region["4b_a-02_east"], locations_by_region["4b_a-02_east"]),
- "4b_a-03_west": PreRegion("4b_a-03_west", "4b_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-03_west"]),
- "4b_a-03_east": PreRegion("4b_a-03_east", "4b_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-03_east"]),
+ "4b_a-03_west": PreRegion("4b_a-03_west", "4b_a-03", connections_by_region["4b_a-03_west"], locations_by_region["4b_a-03_west"]),
+ "4b_a-03_east": PreRegion("4b_a-03_east", "4b_a-03", connections_by_region["4b_a-03_east"], locations_by_region["4b_a-03_east"]),
- "4b_a-04_west": PreRegion("4b_a-04_west", "4b_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-04_west"]),
- "4b_a-04_east": PreRegion("4b_a-04_east", "4b_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_a-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_a-04_east"]),
+ "4b_a-04_west": PreRegion("4b_a-04_west", "4b_a-04", connections_by_region["4b_a-04_west"], locations_by_region["4b_a-04_west"]),
+ "4b_a-04_east": PreRegion("4b_a-04_east", "4b_a-04", connections_by_region["4b_a-04_east"], locations_by_region["4b_a-04_east"]),
- "4b_b-00_west": PreRegion("4b_b-00_west", "4b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-00_west"]),
- "4b_b-00_east": PreRegion("4b_b-00_east", "4b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-00_east"]),
+ "4b_b-00_west": PreRegion("4b_b-00_west", "4b_b-00", connections_by_region["4b_b-00_west"], locations_by_region["4b_b-00_west"]),
+ "4b_b-00_east": PreRegion("4b_b-00_east", "4b_b-00", connections_by_region["4b_b-00_east"], locations_by_region["4b_b-00_east"]),
- "4b_b-01_west": PreRegion("4b_b-01_west", "4b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-01_west"]),
- "4b_b-01_east": PreRegion("4b_b-01_east", "4b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-01_east"]),
+ "4b_b-01_west": PreRegion("4b_b-01_west", "4b_b-01", connections_by_region["4b_b-01_west"], locations_by_region["4b_b-01_west"]),
+ "4b_b-01_east": PreRegion("4b_b-01_east", "4b_b-01", connections_by_region["4b_b-01_east"], locations_by_region["4b_b-01_east"]),
- "4b_b-02_bottom": PreRegion("4b_b-02_bottom", "4b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-02_bottom"]),
- "4b_b-02_top": PreRegion("4b_b-02_top", "4b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-02_top"]),
+ "4b_b-02_bottom": PreRegion("4b_b-02_bottom", "4b_b-02", connections_by_region["4b_b-02_bottom"], locations_by_region["4b_b-02_bottom"]),
+ "4b_b-02_top": PreRegion("4b_b-02_top", "4b_b-02", connections_by_region["4b_b-02_top"], locations_by_region["4b_b-02_top"]),
- "4b_b-03_west": PreRegion("4b_b-03_west", "4b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-03_west"]),
- "4b_b-03_east": PreRegion("4b_b-03_east", "4b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-03_east"]),
+ "4b_b-03_west": PreRegion("4b_b-03_west", "4b_b-03", connections_by_region["4b_b-03_west"], locations_by_region["4b_b-03_west"]),
+ "4b_b-03_east": PreRegion("4b_b-03_east", "4b_b-03", connections_by_region["4b_b-03_east"], locations_by_region["4b_b-03_east"]),
- "4b_b-04_west": PreRegion("4b_b-04_west", "4b_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-04_west"]),
- "4b_b-04_east": PreRegion("4b_b-04_east", "4b_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_b-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_b-04_east"]),
+ "4b_b-04_west": PreRegion("4b_b-04_west", "4b_b-04", connections_by_region["4b_b-04_west"], locations_by_region["4b_b-04_west"]),
+ "4b_b-04_east": PreRegion("4b_b-04_east", "4b_b-04", connections_by_region["4b_b-04_east"], locations_by_region["4b_b-04_east"]),
- "4b_c-00_west": PreRegion("4b_c-00_west", "4b_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-00_west"]),
- "4b_c-00_east": PreRegion("4b_c-00_east", "4b_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-00_east"]),
+ "4b_c-00_west": PreRegion("4b_c-00_west", "4b_c-00", connections_by_region["4b_c-00_west"], locations_by_region["4b_c-00_west"]),
+ "4b_c-00_east": PreRegion("4b_c-00_east", "4b_c-00", connections_by_region["4b_c-00_east"], locations_by_region["4b_c-00_east"]),
- "4b_c-01_west": PreRegion("4b_c-01_west", "4b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-01_west"]),
- "4b_c-01_east": PreRegion("4b_c-01_east", "4b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-01_east"]),
+ "4b_c-01_west": PreRegion("4b_c-01_west", "4b_c-01", connections_by_region["4b_c-01_west"], locations_by_region["4b_c-01_west"]),
+ "4b_c-01_east": PreRegion("4b_c-01_east", "4b_c-01", connections_by_region["4b_c-01_east"], locations_by_region["4b_c-01_east"]),
- "4b_c-02_west": PreRegion("4b_c-02_west", "4b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-02_west"]),
- "4b_c-02_east": PreRegion("4b_c-02_east", "4b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-02_east"]),
+ "4b_c-02_west": PreRegion("4b_c-02_west", "4b_c-02", connections_by_region["4b_c-02_west"], locations_by_region["4b_c-02_west"]),
+ "4b_c-02_east": PreRegion("4b_c-02_east", "4b_c-02", connections_by_region["4b_c-02_east"], locations_by_region["4b_c-02_east"]),
- "4b_c-03_bottom": PreRegion("4b_c-03_bottom", "4b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-03_bottom"]),
- "4b_c-03_top": PreRegion("4b_c-03_top", "4b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-03_top"]),
+ "4b_c-03_bottom": PreRegion("4b_c-03_bottom", "4b_c-03", connections_by_region["4b_c-03_bottom"], locations_by_region["4b_c-03_bottom"]),
+ "4b_c-03_top": PreRegion("4b_c-03_top", "4b_c-03", connections_by_region["4b_c-03_top"], locations_by_region["4b_c-03_top"]),
- "4b_c-04_west": PreRegion("4b_c-04_west", "4b_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-04_west"]),
- "4b_c-04_east": PreRegion("4b_c-04_east", "4b_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_c-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_c-04_east"]),
+ "4b_c-04_west": PreRegion("4b_c-04_west", "4b_c-04", connections_by_region["4b_c-04_west"], locations_by_region["4b_c-04_west"]),
+ "4b_c-04_east": PreRegion("4b_c-04_east", "4b_c-04", connections_by_region["4b_c-04_east"], locations_by_region["4b_c-04_east"]),
- "4b_d-00_west": PreRegion("4b_d-00_west", "4b_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_d-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_d-00_west"]),
- "4b_d-00_east": PreRegion("4b_d-00_east", "4b_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_d-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_d-00_east"]),
+ "4b_d-00_west": PreRegion("4b_d-00_west", "4b_d-00", connections_by_region["4b_d-00_west"], locations_by_region["4b_d-00_west"]),
+ "4b_d-00_east": PreRegion("4b_d-00_east", "4b_d-00", connections_by_region["4b_d-00_east"], locations_by_region["4b_d-00_east"]),
- "4b_d-01_west": PreRegion("4b_d-01_west", "4b_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_d-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_d-01_west"]),
- "4b_d-01_east": PreRegion("4b_d-01_east", "4b_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_d-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_d-01_east"]),
+ "4b_d-01_west": PreRegion("4b_d-01_west", "4b_d-01", connections_by_region["4b_d-01_west"], locations_by_region["4b_d-01_west"]),
+ "4b_d-01_east": PreRegion("4b_d-01_east", "4b_d-01", connections_by_region["4b_d-01_east"], locations_by_region["4b_d-01_east"]),
- "4b_d-02_west": PreRegion("4b_d-02_west", "4b_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_d-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_d-02_west"]),
- "4b_d-02_east": PreRegion("4b_d-02_east", "4b_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_d-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_d-02_east"]),
+ "4b_d-02_west": PreRegion("4b_d-02_west", "4b_d-02", connections_by_region["4b_d-02_west"], locations_by_region["4b_d-02_west"]),
+ "4b_d-02_east": PreRegion("4b_d-02_east", "4b_d-02", connections_by_region["4b_d-02_east"], locations_by_region["4b_d-02_east"]),
- "4b_d-03_west": PreRegion("4b_d-03_west", "4b_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_d-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_d-03_west"]),
- "4b_d-03_east": PreRegion("4b_d-03_east", "4b_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_d-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_d-03_east"]),
+ "4b_d-03_west": PreRegion("4b_d-03_west", "4b_d-03", connections_by_region["4b_d-03_west"], locations_by_region["4b_d-03_west"]),
+ "4b_d-03_east": PreRegion("4b_d-03_east", "4b_d-03", connections_by_region["4b_d-03_east"], locations_by_region["4b_d-03_east"]),
- "4b_end_west": PreRegion("4b_end_west", "4b_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_end_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_end_west"]),
- "4b_end_goal": PreRegion("4b_end_goal", "4b_end", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4b_end_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "4b_end_goal"]),
+ "4b_end_west": PreRegion("4b_end_west", "4b_end", connections_by_region["4b_end_west"], locations_by_region["4b_end_west"]),
+ "4b_end_goal": PreRegion("4b_end_goal", "4b_end", connections_by_region["4b_end_goal"], locations_by_region["4b_end_goal"]),
- "4c_00_west": PreRegion("4c_00_west", "4c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4c_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4c_00_west"]),
- "4c_00_east": PreRegion("4c_00_east", "4c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4c_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4c_00_east"]),
+ "4c_00_west": PreRegion("4c_00_west", "4c_00", connections_by_region["4c_00_west"], locations_by_region["4c_00_west"]),
+ "4c_00_east": PreRegion("4c_00_east", "4c_00", connections_by_region["4c_00_east"], locations_by_region["4c_00_east"]),
- "4c_01_west": PreRegion("4c_01_west", "4c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4c_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4c_01_west"]),
- "4c_01_east": PreRegion("4c_01_east", "4c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4c_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "4c_01_east"]),
+ "4c_01_west": PreRegion("4c_01_west", "4c_01", connections_by_region["4c_01_west"], locations_by_region["4c_01_west"]),
+ "4c_01_east": PreRegion("4c_01_east", "4c_01", connections_by_region["4c_01_east"], locations_by_region["4c_01_east"]),
- "4c_02_west": PreRegion("4c_02_west", "4c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4c_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "4c_02_west"]),
- "4c_02_goal": PreRegion("4c_02_goal", "4c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "4c_02_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "4c_02_goal"]),
+ "4c_02_west": PreRegion("4c_02_west", "4c_02", connections_by_region["4c_02_west"], locations_by_region["4c_02_west"]),
+ "4c_02_goal": PreRegion("4c_02_goal", "4c_02", connections_by_region["4c_02_goal"], locations_by_region["4c_02_goal"]),
- "5a_a-00b_west": PreRegion("5a_a-00b_west", "5a_a-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-00b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-00b_west"]),
- "5a_a-00b_east": PreRegion("5a_a-00b_east", "5a_a-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-00b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-00b_east"]),
+ "5a_a-00b_west": PreRegion("5a_a-00b_west", "5a_a-00b", connections_by_region["5a_a-00b_west"], locations_by_region["5a_a-00b_west"]),
+ "5a_a-00b_east": PreRegion("5a_a-00b_east", "5a_a-00b", connections_by_region["5a_a-00b_east"], locations_by_region["5a_a-00b_east"]),
- "5a_a-00x_east": PreRegion("5a_a-00x_east", "5a_a-00x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-00x_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-00x_east"]),
+ "5a_a-00x_east": PreRegion("5a_a-00x_east", "5a_a-00x", connections_by_region["5a_a-00x_east"], locations_by_region["5a_a-00x_east"]),
- "5a_a-00d_west": PreRegion("5a_a-00d_west", "5a_a-00d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-00d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-00d_west"]),
- "5a_a-00d_east": PreRegion("5a_a-00d_east", "5a_a-00d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-00d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-00d_east"]),
+ "5a_a-00d_west": PreRegion("5a_a-00d_west", "5a_a-00d", connections_by_region["5a_a-00d_west"], locations_by_region["5a_a-00d_west"]),
+ "5a_a-00d_east": PreRegion("5a_a-00d_east", "5a_a-00d", connections_by_region["5a_a-00d_east"], locations_by_region["5a_a-00d_east"]),
- "5a_a-00c_west": PreRegion("5a_a-00c_west", "5a_a-00c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-00c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-00c_west"]),
- "5a_a-00c_east": PreRegion("5a_a-00c_east", "5a_a-00c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-00c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-00c_east"]),
+ "5a_a-00c_west": PreRegion("5a_a-00c_west", "5a_a-00c", connections_by_region["5a_a-00c_west"], locations_by_region["5a_a-00c_west"]),
+ "5a_a-00c_east": PreRegion("5a_a-00c_east", "5a_a-00c", connections_by_region["5a_a-00c_east"], locations_by_region["5a_a-00c_east"]),
- "5a_a-00_west": PreRegion("5a_a-00_west", "5a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-00_west"]),
- "5a_a-00_east": PreRegion("5a_a-00_east", "5a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-00_east"]),
+ "5a_a-00_west": PreRegion("5a_a-00_west", "5a_a-00", connections_by_region["5a_a-00_west"], locations_by_region["5a_a-00_west"]),
+ "5a_a-00_east": PreRegion("5a_a-00_east", "5a_a-00", connections_by_region["5a_a-00_east"], locations_by_region["5a_a-00_east"]),
- "5a_a-01_west": PreRegion("5a_a-01_west", "5a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-01_west"]),
- "5a_a-01_center": PreRegion("5a_a-01_center", "5a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-01_center"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-01_center"]),
- "5a_a-01_east": PreRegion("5a_a-01_east", "5a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-01_east"]),
- "5a_a-01_south-west": PreRegion("5a_a-01_south-west", "5a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-01_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-01_south-west"]),
- "5a_a-01_south-east": PreRegion("5a_a-01_south-east", "5a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-01_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-01_south-east"]),
- "5a_a-01_north": PreRegion("5a_a-01_north", "5a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-01_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-01_north"]),
+ "5a_a-01_west": PreRegion("5a_a-01_west", "5a_a-01", connections_by_region["5a_a-01_west"], locations_by_region["5a_a-01_west"]),
+ "5a_a-01_center": PreRegion("5a_a-01_center", "5a_a-01", connections_by_region["5a_a-01_center"], locations_by_region["5a_a-01_center"]),
+ "5a_a-01_east": PreRegion("5a_a-01_east", "5a_a-01", connections_by_region["5a_a-01_east"], locations_by_region["5a_a-01_east"]),
+ "5a_a-01_south-west": PreRegion("5a_a-01_south-west", "5a_a-01", connections_by_region["5a_a-01_south-west"], locations_by_region["5a_a-01_south-west"]),
+ "5a_a-01_south-east": PreRegion("5a_a-01_south-east", "5a_a-01", connections_by_region["5a_a-01_south-east"], locations_by_region["5a_a-01_south-east"]),
+ "5a_a-01_north": PreRegion("5a_a-01_north", "5a_a-01", connections_by_region["5a_a-01_north"], locations_by_region["5a_a-01_north"]),
- "5a_a-02_west": PreRegion("5a_a-02_west", "5a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-02_west"]),
- "5a_a-02_north": PreRegion("5a_a-02_north", "5a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-02_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-02_north"]),
- "5a_a-02_south": PreRegion("5a_a-02_south", "5a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-02_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-02_south"]),
+ "5a_a-02_west": PreRegion("5a_a-02_west", "5a_a-02", connections_by_region["5a_a-02_west"], locations_by_region["5a_a-02_west"]),
+ "5a_a-02_north": PreRegion("5a_a-02_north", "5a_a-02", connections_by_region["5a_a-02_north"], locations_by_region["5a_a-02_north"]),
+ "5a_a-02_south": PreRegion("5a_a-02_south", "5a_a-02", connections_by_region["5a_a-02_south"], locations_by_region["5a_a-02_south"]),
- "5a_a-03_west": PreRegion("5a_a-03_west", "5a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-03_west"]),
- "5a_a-03_east": PreRegion("5a_a-03_east", "5a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-03_east"]),
+ "5a_a-03_west": PreRegion("5a_a-03_west", "5a_a-03", connections_by_region["5a_a-03_west"], locations_by_region["5a_a-03_west"]),
+ "5a_a-03_east": PreRegion("5a_a-03_east", "5a_a-03", connections_by_region["5a_a-03_east"], locations_by_region["5a_a-03_east"]),
- "5a_a-04_east": PreRegion("5a_a-04_east", "5a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-04_east"]),
- "5a_a-04_north": PreRegion("5a_a-04_north", "5a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-04_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-04_north"]),
- "5a_a-04_south": PreRegion("5a_a-04_south", "5a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-04_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-04_south"]),
+ "5a_a-04_east": PreRegion("5a_a-04_east", "5a_a-04", connections_by_region["5a_a-04_east"], locations_by_region["5a_a-04_east"]),
+ "5a_a-04_north": PreRegion("5a_a-04_north", "5a_a-04", connections_by_region["5a_a-04_north"], locations_by_region["5a_a-04_north"]),
+ "5a_a-04_south": PreRegion("5a_a-04_south", "5a_a-04", connections_by_region["5a_a-04_south"], locations_by_region["5a_a-04_south"]),
- "5a_a-05_north-west": PreRegion("5a_a-05_north-west", "5a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-05_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-05_north-west"]),
- "5a_a-05_center": PreRegion("5a_a-05_center", "5a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-05_center"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-05_center"]),
- "5a_a-05_north-east": PreRegion("5a_a-05_north-east", "5a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-05_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-05_north-east"]),
- "5a_a-05_south-west": PreRegion("5a_a-05_south-west", "5a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-05_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-05_south-west"]),
- "5a_a-05_south-east": PreRegion("5a_a-05_south-east", "5a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-05_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-05_south-east"]),
+ "5a_a-05_north-west": PreRegion("5a_a-05_north-west", "5a_a-05", connections_by_region["5a_a-05_north-west"], locations_by_region["5a_a-05_north-west"]),
+ "5a_a-05_center": PreRegion("5a_a-05_center", "5a_a-05", connections_by_region["5a_a-05_center"], locations_by_region["5a_a-05_center"]),
+ "5a_a-05_north-east": PreRegion("5a_a-05_north-east", "5a_a-05", connections_by_region["5a_a-05_north-east"], locations_by_region["5a_a-05_north-east"]),
+ "5a_a-05_south-west": PreRegion("5a_a-05_south-west", "5a_a-05", connections_by_region["5a_a-05_south-west"], locations_by_region["5a_a-05_south-west"]),
+ "5a_a-05_south-east": PreRegion("5a_a-05_south-east", "5a_a-05", connections_by_region["5a_a-05_south-east"], locations_by_region["5a_a-05_south-east"]),
- "5a_a-06_west": PreRegion("5a_a-06_west", "5a_a-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-06_west"]),
+ "5a_a-06_west": PreRegion("5a_a-06_west", "5a_a-06", connections_by_region["5a_a-06_west"], locations_by_region["5a_a-06_west"]),
- "5a_a-07_east": PreRegion("5a_a-07_east", "5a_a-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-07_east"]),
+ "5a_a-07_east": PreRegion("5a_a-07_east", "5a_a-07", connections_by_region["5a_a-07_east"], locations_by_region["5a_a-07_east"]),
- "5a_a-08_west": PreRegion("5a_a-08_west", "5a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-08_west"]),
- "5a_a-08_center": PreRegion("5a_a-08_center", "5a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-08_center"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-08_center"]),
- "5a_a-08_east": PreRegion("5a_a-08_east", "5a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-08_east"]),
- "5a_a-08_south": PreRegion("5a_a-08_south", "5a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-08_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-08_south"]),
- "5a_a-08_south-east": PreRegion("5a_a-08_south-east", "5a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-08_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-08_south-east"]),
- "5a_a-08_north-east": PreRegion("5a_a-08_north-east", "5a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-08_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-08_north-east"]),
- "5a_a-08_north": PreRegion("5a_a-08_north", "5a_a-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-08_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-08_north"]),
+ "5a_a-08_west": PreRegion("5a_a-08_west", "5a_a-08", connections_by_region["5a_a-08_west"], locations_by_region["5a_a-08_west"]),
+ "5a_a-08_center": PreRegion("5a_a-08_center", "5a_a-08", connections_by_region["5a_a-08_center"], locations_by_region["5a_a-08_center"]),
+ "5a_a-08_east": PreRegion("5a_a-08_east", "5a_a-08", connections_by_region["5a_a-08_east"], locations_by_region["5a_a-08_east"]),
+ "5a_a-08_south": PreRegion("5a_a-08_south", "5a_a-08", connections_by_region["5a_a-08_south"], locations_by_region["5a_a-08_south"]),
+ "5a_a-08_south-east": PreRegion("5a_a-08_south-east", "5a_a-08", connections_by_region["5a_a-08_south-east"], locations_by_region["5a_a-08_south-east"]),
+ "5a_a-08_north-east": PreRegion("5a_a-08_north-east", "5a_a-08", connections_by_region["5a_a-08_north-east"], locations_by_region["5a_a-08_north-east"]),
+ "5a_a-08_north": PreRegion("5a_a-08_north", "5a_a-08", connections_by_region["5a_a-08_north"], locations_by_region["5a_a-08_north"]),
- "5a_a-10_west": PreRegion("5a_a-10_west", "5a_a-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-10_west"]),
- "5a_a-10_east": PreRegion("5a_a-10_east", "5a_a-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-10_east"]),
+ "5a_a-10_west": PreRegion("5a_a-10_west", "5a_a-10", connections_by_region["5a_a-10_west"], locations_by_region["5a_a-10_west"]),
+ "5a_a-10_east": PreRegion("5a_a-10_east", "5a_a-10", connections_by_region["5a_a-10_east"], locations_by_region["5a_a-10_east"]),
- "5a_a-09_west": PreRegion("5a_a-09_west", "5a_a-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-09_west"]),
- "5a_a-09_east": PreRegion("5a_a-09_east", "5a_a-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-09_east"]),
+ "5a_a-09_west": PreRegion("5a_a-09_west", "5a_a-09", connections_by_region["5a_a-09_west"], locations_by_region["5a_a-09_west"]),
+ "5a_a-09_east": PreRegion("5a_a-09_east", "5a_a-09", connections_by_region["5a_a-09_east"], locations_by_region["5a_a-09_east"]),
- "5a_a-11_east": PreRegion("5a_a-11_east", "5a_a-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-11_east"]),
+ "5a_a-11_east": PreRegion("5a_a-11_east", "5a_a-11", connections_by_region["5a_a-11_east"], locations_by_region["5a_a-11_east"]),
- "5a_a-12_north-west": PreRegion("5a_a-12_north-west", "5a_a-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-12_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-12_north-west"]),
- "5a_a-12_west": PreRegion("5a_a-12_west", "5a_a-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-12_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-12_west"]),
- "5a_a-12_south-west": PreRegion("5a_a-12_south-west", "5a_a-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-12_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-12_south-west"]),
- "5a_a-12_east": PreRegion("5a_a-12_east", "5a_a-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-12_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-12_east"]),
+ "5a_a-12_north-west": PreRegion("5a_a-12_north-west", "5a_a-12", connections_by_region["5a_a-12_north-west"], locations_by_region["5a_a-12_north-west"]),
+ "5a_a-12_west": PreRegion("5a_a-12_west", "5a_a-12", connections_by_region["5a_a-12_west"], locations_by_region["5a_a-12_west"]),
+ "5a_a-12_south-west": PreRegion("5a_a-12_south-west", "5a_a-12", connections_by_region["5a_a-12_south-west"], locations_by_region["5a_a-12_south-west"]),
+ "5a_a-12_east": PreRegion("5a_a-12_east", "5a_a-12", connections_by_region["5a_a-12_east"], locations_by_region["5a_a-12_east"]),
- "5a_a-15_south": PreRegion("5a_a-15_south", "5a_a-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-15_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-15_south"]),
+ "5a_a-15_south": PreRegion("5a_a-15_south", "5a_a-15", connections_by_region["5a_a-15_south"], locations_by_region["5a_a-15_south"]),
- "5a_a-14_south": PreRegion("5a_a-14_south", "5a_a-14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-14_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-14_south"]),
+ "5a_a-14_south": PreRegion("5a_a-14_south", "5a_a-14", connections_by_region["5a_a-14_south"], locations_by_region["5a_a-14_south"]),
- "5a_a-13_west": PreRegion("5a_a-13_west", "5a_a-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-13_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-13_west"]),
- "5a_a-13_east": PreRegion("5a_a-13_east", "5a_a-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_a-13_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_a-13_east"]),
+ "5a_a-13_west": PreRegion("5a_a-13_west", "5a_a-13", connections_by_region["5a_a-13_west"], locations_by_region["5a_a-13_west"]),
+ "5a_a-13_east": PreRegion("5a_a-13_east", "5a_a-13", connections_by_region["5a_a-13_east"], locations_by_region["5a_a-13_east"]),
- "5a_b-00_west": PreRegion("5a_b-00_west", "5a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-00_west"]),
- "5a_b-00_north-west": PreRegion("5a_b-00_north-west", "5a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-00_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-00_north-west"]),
- "5a_b-00_east": PreRegion("5a_b-00_east", "5a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-00_east"]),
+ "5a_b-00_west": PreRegion("5a_b-00_west", "5a_b-00", connections_by_region["5a_b-00_west"], locations_by_region["5a_b-00_west"]),
+ "5a_b-00_north-west": PreRegion("5a_b-00_north-west", "5a_b-00", connections_by_region["5a_b-00_north-west"], locations_by_region["5a_b-00_north-west"]),
+ "5a_b-00_east": PreRegion("5a_b-00_east", "5a_b-00", connections_by_region["5a_b-00_east"], locations_by_region["5a_b-00_east"]),
- "5a_b-18_south": PreRegion("5a_b-18_south", "5a_b-18", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-18_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-18_south"]),
+ "5a_b-18_south": PreRegion("5a_b-18_south", "5a_b-18", connections_by_region["5a_b-18_south"], locations_by_region["5a_b-18_south"]),
- "5a_b-01_south-west": PreRegion("5a_b-01_south-west", "5a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01_south-west"]),
- "5a_b-01_center": PreRegion("5a_b-01_center", "5a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01_center"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01_center"]),
- "5a_b-01_west": PreRegion("5a_b-01_west", "5a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01_west"]),
- "5a_b-01_north-west": PreRegion("5a_b-01_north-west", "5a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01_north-west"]),
- "5a_b-01_north": PreRegion("5a_b-01_north", "5a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01_north"]),
- "5a_b-01_north-east": PreRegion("5a_b-01_north-east", "5a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01_north-east"]),
- "5a_b-01_east": PreRegion("5a_b-01_east", "5a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01_east"]),
- "5a_b-01_south-east": PreRegion("5a_b-01_south-east", "5a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01_south-east"]),
- "5a_b-01_south": PreRegion("5a_b-01_south", "5a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01_south"]),
+ "5a_b-01_south-west": PreRegion("5a_b-01_south-west", "5a_b-01", connections_by_region["5a_b-01_south-west"], locations_by_region["5a_b-01_south-west"]),
+ "5a_b-01_center": PreRegion("5a_b-01_center", "5a_b-01", connections_by_region["5a_b-01_center"], locations_by_region["5a_b-01_center"]),
+ "5a_b-01_west": PreRegion("5a_b-01_west", "5a_b-01", connections_by_region["5a_b-01_west"], locations_by_region["5a_b-01_west"]),
+ "5a_b-01_north-west": PreRegion("5a_b-01_north-west", "5a_b-01", connections_by_region["5a_b-01_north-west"], locations_by_region["5a_b-01_north-west"]),
+ "5a_b-01_north": PreRegion("5a_b-01_north", "5a_b-01", connections_by_region["5a_b-01_north"], locations_by_region["5a_b-01_north"]),
+ "5a_b-01_north-east": PreRegion("5a_b-01_north-east", "5a_b-01", connections_by_region["5a_b-01_north-east"], locations_by_region["5a_b-01_north-east"]),
+ "5a_b-01_east": PreRegion("5a_b-01_east", "5a_b-01", connections_by_region["5a_b-01_east"], locations_by_region["5a_b-01_east"]),
+ "5a_b-01_south-east": PreRegion("5a_b-01_south-east", "5a_b-01", connections_by_region["5a_b-01_south-east"], locations_by_region["5a_b-01_south-east"]),
+ "5a_b-01_south": PreRegion("5a_b-01_south", "5a_b-01", connections_by_region["5a_b-01_south"], locations_by_region["5a_b-01_south"]),
- "5a_b-01c_west": PreRegion("5a_b-01c_west", "5a_b-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01c_west"]),
- "5a_b-01c_east": PreRegion("5a_b-01c_east", "5a_b-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01c_east"]),
+ "5a_b-01c_west": PreRegion("5a_b-01c_west", "5a_b-01c", connections_by_region["5a_b-01c_west"], locations_by_region["5a_b-01c_west"]),
+ "5a_b-01c_east": PreRegion("5a_b-01c_east", "5a_b-01c", connections_by_region["5a_b-01c_east"], locations_by_region["5a_b-01c_east"]),
- "5a_b-20_north-west": PreRegion("5a_b-20_north-west", "5a_b-20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-20_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-20_north-west"]),
- "5a_b-20_west": PreRegion("5a_b-20_west", "5a_b-20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-20_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-20_west"]),
- "5a_b-20_south-west": PreRegion("5a_b-20_south-west", "5a_b-20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-20_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-20_south-west"]),
- "5a_b-20_south": PreRegion("5a_b-20_south", "5a_b-20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-20_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-20_south"]),
- "5a_b-20_east": PreRegion("5a_b-20_east", "5a_b-20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-20_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-20_east"]),
+ "5a_b-20_north-west": PreRegion("5a_b-20_north-west", "5a_b-20", connections_by_region["5a_b-20_north-west"], locations_by_region["5a_b-20_north-west"]),
+ "5a_b-20_west": PreRegion("5a_b-20_west", "5a_b-20", connections_by_region["5a_b-20_west"], locations_by_region["5a_b-20_west"]),
+ "5a_b-20_south-west": PreRegion("5a_b-20_south-west", "5a_b-20", connections_by_region["5a_b-20_south-west"], locations_by_region["5a_b-20_south-west"]),
+ "5a_b-20_south": PreRegion("5a_b-20_south", "5a_b-20", connections_by_region["5a_b-20_south"], locations_by_region["5a_b-20_south"]),
+ "5a_b-20_east": PreRegion("5a_b-20_east", "5a_b-20", connections_by_region["5a_b-20_east"], locations_by_region["5a_b-20_east"]),
- "5a_b-21_east": PreRegion("5a_b-21_east", "5a_b-21", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-21_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-21_east"]),
+ "5a_b-21_east": PreRegion("5a_b-21_east", "5a_b-21", connections_by_region["5a_b-21_east"], locations_by_region["5a_b-21_east"]),
- "5a_b-01b_west": PreRegion("5a_b-01b_west", "5a_b-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01b_west"]),
- "5a_b-01b_east": PreRegion("5a_b-01b_east", "5a_b-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-01b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-01b_east"]),
+ "5a_b-01b_west": PreRegion("5a_b-01b_west", "5a_b-01b", connections_by_region["5a_b-01b_west"], locations_by_region["5a_b-01b_west"]),
+ "5a_b-01b_east": PreRegion("5a_b-01b_east", "5a_b-01b", connections_by_region["5a_b-01b_east"], locations_by_region["5a_b-01b_east"]),
- "5a_b-02_center": PreRegion("5a_b-02_center", "5a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-02_center"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-02_center"]),
- "5a_b-02_west": PreRegion("5a_b-02_west", "5a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-02_west"]),
- "5a_b-02_north-west": PreRegion("5a_b-02_north-west", "5a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-02_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-02_north-west"]),
- "5a_b-02_north": PreRegion("5a_b-02_north", "5a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-02_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-02_north"]),
- "5a_b-02_north-east": PreRegion("5a_b-02_north-east", "5a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-02_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-02_north-east"]),
- "5a_b-02_east-upper": PreRegion("5a_b-02_east-upper", "5a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-02_east-upper"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-02_east-upper"]),
- "5a_b-02_east-lower": PreRegion("5a_b-02_east-lower", "5a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-02_east-lower"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-02_east-lower"]),
- "5a_b-02_south-east": PreRegion("5a_b-02_south-east", "5a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-02_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-02_south-east"]),
- "5a_b-02_south": PreRegion("5a_b-02_south", "5a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-02_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-02_south"]),
+ "5a_b-02_center": PreRegion("5a_b-02_center", "5a_b-02", connections_by_region["5a_b-02_center"], locations_by_region["5a_b-02_center"]),
+ "5a_b-02_west": PreRegion("5a_b-02_west", "5a_b-02", connections_by_region["5a_b-02_west"], locations_by_region["5a_b-02_west"]),
+ "5a_b-02_north-west": PreRegion("5a_b-02_north-west", "5a_b-02", connections_by_region["5a_b-02_north-west"], locations_by_region["5a_b-02_north-west"]),
+ "5a_b-02_north": PreRegion("5a_b-02_north", "5a_b-02", connections_by_region["5a_b-02_north"], locations_by_region["5a_b-02_north"]),
+ "5a_b-02_north-east": PreRegion("5a_b-02_north-east", "5a_b-02", connections_by_region["5a_b-02_north-east"], locations_by_region["5a_b-02_north-east"]),
+ "5a_b-02_east-upper": PreRegion("5a_b-02_east-upper", "5a_b-02", connections_by_region["5a_b-02_east-upper"], locations_by_region["5a_b-02_east-upper"]),
+ "5a_b-02_east-lower": PreRegion("5a_b-02_east-lower", "5a_b-02", connections_by_region["5a_b-02_east-lower"], locations_by_region["5a_b-02_east-lower"]),
+ "5a_b-02_south-east": PreRegion("5a_b-02_south-east", "5a_b-02", connections_by_region["5a_b-02_south-east"], locations_by_region["5a_b-02_south-east"]),
+ "5a_b-02_south": PreRegion("5a_b-02_south", "5a_b-02", connections_by_region["5a_b-02_south"], locations_by_region["5a_b-02_south"]),
- "5a_b-03_east": PreRegion("5a_b-03_east", "5a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-03_east"]),
+ "5a_b-03_east": PreRegion("5a_b-03_east", "5a_b-03", connections_by_region["5a_b-03_east"], locations_by_region["5a_b-03_east"]),
- "5a_b-05_west": PreRegion("5a_b-05_west", "5a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-05_west"]),
+ "5a_b-05_west": PreRegion("5a_b-05_west", "5a_b-05", connections_by_region["5a_b-05_west"], locations_by_region["5a_b-05_west"]),
- "5a_b-04_west": PreRegion("5a_b-04_west", "5a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-04_west"]),
- "5a_b-04_east": PreRegion("5a_b-04_east", "5a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-04_east"]),
- "5a_b-04_south": PreRegion("5a_b-04_south", "5a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-04_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-04_south"]),
+ "5a_b-04_west": PreRegion("5a_b-04_west", "5a_b-04", connections_by_region["5a_b-04_west"], locations_by_region["5a_b-04_west"]),
+ "5a_b-04_east": PreRegion("5a_b-04_east", "5a_b-04", connections_by_region["5a_b-04_east"], locations_by_region["5a_b-04_east"]),
+ "5a_b-04_south": PreRegion("5a_b-04_south", "5a_b-04", connections_by_region["5a_b-04_south"], locations_by_region["5a_b-04_south"]),
- "5a_b-07_north": PreRegion("5a_b-07_north", "5a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-07_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-07_north"]),
- "5a_b-07_south": PreRegion("5a_b-07_south", "5a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-07_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-07_south"]),
+ "5a_b-07_north": PreRegion("5a_b-07_north", "5a_b-07", connections_by_region["5a_b-07_north"], locations_by_region["5a_b-07_north"]),
+ "5a_b-07_south": PreRegion("5a_b-07_south", "5a_b-07", connections_by_region["5a_b-07_south"], locations_by_region["5a_b-07_south"]),
- "5a_b-08_west": PreRegion("5a_b-08_west", "5a_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-08_west"]),
- "5a_b-08_east": PreRegion("5a_b-08_east", "5a_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-08_east"]),
+ "5a_b-08_west": PreRegion("5a_b-08_west", "5a_b-08", connections_by_region["5a_b-08_west"], locations_by_region["5a_b-08_west"]),
+ "5a_b-08_east": PreRegion("5a_b-08_east", "5a_b-08", connections_by_region["5a_b-08_east"], locations_by_region["5a_b-08_east"]),
- "5a_b-09_north": PreRegion("5a_b-09_north", "5a_b-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-09_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-09_north"]),
- "5a_b-09_south": PreRegion("5a_b-09_south", "5a_b-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-09_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-09_south"]),
+ "5a_b-09_north": PreRegion("5a_b-09_north", "5a_b-09", connections_by_region["5a_b-09_north"], locations_by_region["5a_b-09_north"]),
+ "5a_b-09_south": PreRegion("5a_b-09_south", "5a_b-09", connections_by_region["5a_b-09_south"], locations_by_region["5a_b-09_south"]),
- "5a_b-10_east": PreRegion("5a_b-10_east", "5a_b-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-10_east"]),
+ "5a_b-10_east": PreRegion("5a_b-10_east", "5a_b-10", connections_by_region["5a_b-10_east"], locations_by_region["5a_b-10_east"]),
- "5a_b-11_north-west": PreRegion("5a_b-11_north-west", "5a_b-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-11_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-11_north-west"]),
- "5a_b-11_west": PreRegion("5a_b-11_west", "5a_b-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-11_west"]),
- "5a_b-11_south-west": PreRegion("5a_b-11_south-west", "5a_b-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-11_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-11_south-west"]),
- "5a_b-11_south-east": PreRegion("5a_b-11_south-east", "5a_b-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-11_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-11_south-east"]),
- "5a_b-11_east": PreRegion("5a_b-11_east", "5a_b-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-11_east"]),
+ "5a_b-11_north-west": PreRegion("5a_b-11_north-west", "5a_b-11", connections_by_region["5a_b-11_north-west"], locations_by_region["5a_b-11_north-west"]),
+ "5a_b-11_west": PreRegion("5a_b-11_west", "5a_b-11", connections_by_region["5a_b-11_west"], locations_by_region["5a_b-11_west"]),
+ "5a_b-11_south-west": PreRegion("5a_b-11_south-west", "5a_b-11", connections_by_region["5a_b-11_south-west"], locations_by_region["5a_b-11_south-west"]),
+ "5a_b-11_south-east": PreRegion("5a_b-11_south-east", "5a_b-11", connections_by_region["5a_b-11_south-east"], locations_by_region["5a_b-11_south-east"]),
+ "5a_b-11_east": PreRegion("5a_b-11_east", "5a_b-11", connections_by_region["5a_b-11_east"], locations_by_region["5a_b-11_east"]),
- "5a_b-12_west": PreRegion("5a_b-12_west", "5a_b-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-12_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-12_west"]),
- "5a_b-12_east": PreRegion("5a_b-12_east", "5a_b-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-12_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-12_east"]),
+ "5a_b-12_west": PreRegion("5a_b-12_west", "5a_b-12", connections_by_region["5a_b-12_west"], locations_by_region["5a_b-12_west"]),
+ "5a_b-12_east": PreRegion("5a_b-12_east", "5a_b-12", connections_by_region["5a_b-12_east"], locations_by_region["5a_b-12_east"]),
- "5a_b-13_west": PreRegion("5a_b-13_west", "5a_b-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-13_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-13_west"]),
- "5a_b-13_east": PreRegion("5a_b-13_east", "5a_b-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-13_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-13_east"]),
- "5a_b-13_north-east": PreRegion("5a_b-13_north-east", "5a_b-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-13_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-13_north-east"]),
+ "5a_b-13_west": PreRegion("5a_b-13_west", "5a_b-13", connections_by_region["5a_b-13_west"], locations_by_region["5a_b-13_west"]),
+ "5a_b-13_east": PreRegion("5a_b-13_east", "5a_b-13", connections_by_region["5a_b-13_east"], locations_by_region["5a_b-13_east"]),
+ "5a_b-13_north-east": PreRegion("5a_b-13_north-east", "5a_b-13", connections_by_region["5a_b-13_north-east"], locations_by_region["5a_b-13_north-east"]),
- "5a_b-17_west": PreRegion("5a_b-17_west", "5a_b-17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-17_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-17_west"]),
- "5a_b-17_east": PreRegion("5a_b-17_east", "5a_b-17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-17_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-17_east"]),
- "5a_b-17_north-west": PreRegion("5a_b-17_north-west", "5a_b-17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-17_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-17_north-west"]),
+ "5a_b-17_west": PreRegion("5a_b-17_west", "5a_b-17", connections_by_region["5a_b-17_west"], locations_by_region["5a_b-17_west"]),
+ "5a_b-17_east": PreRegion("5a_b-17_east", "5a_b-17", connections_by_region["5a_b-17_east"], locations_by_region["5a_b-17_east"]),
+ "5a_b-17_north-west": PreRegion("5a_b-17_north-west", "5a_b-17", connections_by_region["5a_b-17_north-west"], locations_by_region["5a_b-17_north-west"]),
- "5a_b-22_west": PreRegion("5a_b-22_west", "5a_b-22", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-22_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-22_west"]),
+ "5a_b-22_west": PreRegion("5a_b-22_west", "5a_b-22", connections_by_region["5a_b-22_west"], locations_by_region["5a_b-22_west"]),
- "5a_b-06_west": PreRegion("5a_b-06_west", "5a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-06_west"]),
- "5a_b-06_east": PreRegion("5a_b-06_east", "5a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-06_east"]),
- "5a_b-06_north-east": PreRegion("5a_b-06_north-east", "5a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-06_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-06_north-east"]),
+ "5a_b-06_west": PreRegion("5a_b-06_west", "5a_b-06", connections_by_region["5a_b-06_west"], locations_by_region["5a_b-06_west"]),
+ "5a_b-06_east": PreRegion("5a_b-06_east", "5a_b-06", connections_by_region["5a_b-06_east"], locations_by_region["5a_b-06_east"]),
+ "5a_b-06_north-east": PreRegion("5a_b-06_north-east", "5a_b-06", connections_by_region["5a_b-06_north-east"], locations_by_region["5a_b-06_north-east"]),
- "5a_b-19_west": PreRegion("5a_b-19_west", "5a_b-19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-19_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-19_west"]),
- "5a_b-19_east": PreRegion("5a_b-19_east", "5a_b-19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-19_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-19_east"]),
- "5a_b-19_north-west": PreRegion("5a_b-19_north-west", "5a_b-19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-19_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-19_north-west"]),
+ "5a_b-19_west": PreRegion("5a_b-19_west", "5a_b-19", connections_by_region["5a_b-19_west"], locations_by_region["5a_b-19_west"]),
+ "5a_b-19_east": PreRegion("5a_b-19_east", "5a_b-19", connections_by_region["5a_b-19_east"], locations_by_region["5a_b-19_east"]),
+ "5a_b-19_north-west": PreRegion("5a_b-19_north-west", "5a_b-19", connections_by_region["5a_b-19_north-west"], locations_by_region["5a_b-19_north-west"]),
- "5a_b-14_west": PreRegion("5a_b-14_west", "5a_b-14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-14_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-14_west"]),
- "5a_b-14_south": PreRegion("5a_b-14_south", "5a_b-14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-14_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-14_south"]),
- "5a_b-14_north": PreRegion("5a_b-14_north", "5a_b-14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-14_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-14_north"]),
+ "5a_b-14_west": PreRegion("5a_b-14_west", "5a_b-14", connections_by_region["5a_b-14_west"], locations_by_region["5a_b-14_west"]),
+ "5a_b-14_south": PreRegion("5a_b-14_south", "5a_b-14", connections_by_region["5a_b-14_south"], locations_by_region["5a_b-14_south"]),
+ "5a_b-14_north": PreRegion("5a_b-14_north", "5a_b-14", connections_by_region["5a_b-14_north"], locations_by_region["5a_b-14_north"]),
- "5a_b-15_west": PreRegion("5a_b-15_west", "5a_b-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-15_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-15_west"]),
+ "5a_b-15_west": PreRegion("5a_b-15_west", "5a_b-15", connections_by_region["5a_b-15_west"], locations_by_region["5a_b-15_west"]),
- "5a_b-16_bottom": PreRegion("5a_b-16_bottom", "5a_b-16", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-16_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-16_bottom"]),
- "5a_b-16_mirror": PreRegion("5a_b-16_mirror", "5a_b-16", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_b-16_mirror"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_b-16_mirror"]),
+ "5a_b-16_bottom": PreRegion("5a_b-16_bottom", "5a_b-16", connections_by_region["5a_b-16_bottom"], locations_by_region["5a_b-16_bottom"]),
+ "5a_b-16_mirror": PreRegion("5a_b-16_mirror", "5a_b-16", connections_by_region["5a_b-16_mirror"], locations_by_region["5a_b-16_mirror"]),
- "5a_void_east": PreRegion("5a_void_east", "5a_void", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_void_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_void_east"]),
- "5a_void_west": PreRegion("5a_void_west", "5a_void", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_void_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_void_west"]),
+ "5a_void_east": PreRegion("5a_void_east", "5a_void", connections_by_region["5a_void_east"], locations_by_region["5a_void_east"]),
+ "5a_void_west": PreRegion("5a_void_west", "5a_void", connections_by_region["5a_void_west"], locations_by_region["5a_void_west"]),
- "5a_c-00_bottom": PreRegion("5a_c-00_bottom", "5a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-00_bottom"]),
- "5a_c-00_top": PreRegion("5a_c-00_top", "5a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-00_top"]),
+ "5a_c-00_bottom": PreRegion("5a_c-00_bottom", "5a_c-00", connections_by_region["5a_c-00_bottom"], locations_by_region["5a_c-00_bottom"]),
+ "5a_c-00_top": PreRegion("5a_c-00_top", "5a_c-00", connections_by_region["5a_c-00_top"], locations_by_region["5a_c-00_top"]),
- "5a_c-01_west": PreRegion("5a_c-01_west", "5a_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-01_west"]),
- "5a_c-01_east": PreRegion("5a_c-01_east", "5a_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-01_east"]),
+ "5a_c-01_west": PreRegion("5a_c-01_west", "5a_c-01", connections_by_region["5a_c-01_west"], locations_by_region["5a_c-01_west"]),
+ "5a_c-01_east": PreRegion("5a_c-01_east", "5a_c-01", connections_by_region["5a_c-01_east"], locations_by_region["5a_c-01_east"]),
- "5a_c-01b_west": PreRegion("5a_c-01b_west", "5a_c-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-01b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-01b_west"]),
- "5a_c-01b_east": PreRegion("5a_c-01b_east", "5a_c-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-01b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-01b_east"]),
+ "5a_c-01b_west": PreRegion("5a_c-01b_west", "5a_c-01b", connections_by_region["5a_c-01b_west"], locations_by_region["5a_c-01b_west"]),
+ "5a_c-01b_east": PreRegion("5a_c-01b_east", "5a_c-01b", connections_by_region["5a_c-01b_east"], locations_by_region["5a_c-01b_east"]),
- "5a_c-01c_west": PreRegion("5a_c-01c_west", "5a_c-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-01c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-01c_west"]),
- "5a_c-01c_east": PreRegion("5a_c-01c_east", "5a_c-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-01c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-01c_east"]),
+ "5a_c-01c_west": PreRegion("5a_c-01c_west", "5a_c-01c", connections_by_region["5a_c-01c_west"], locations_by_region["5a_c-01c_west"]),
+ "5a_c-01c_east": PreRegion("5a_c-01c_east", "5a_c-01c", connections_by_region["5a_c-01c_east"], locations_by_region["5a_c-01c_east"]),
- "5a_c-08b_west": PreRegion("5a_c-08b_west", "5a_c-08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-08b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-08b_west"]),
- "5a_c-08b_east": PreRegion("5a_c-08b_east", "5a_c-08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-08b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-08b_east"]),
+ "5a_c-08b_west": PreRegion("5a_c-08b_west", "5a_c-08b", connections_by_region["5a_c-08b_west"], locations_by_region["5a_c-08b_west"]),
+ "5a_c-08b_east": PreRegion("5a_c-08b_east", "5a_c-08b", connections_by_region["5a_c-08b_east"], locations_by_region["5a_c-08b_east"]),
- "5a_c-08_west": PreRegion("5a_c-08_west", "5a_c-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-08_west"]),
- "5a_c-08_east": PreRegion("5a_c-08_east", "5a_c-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-08_east"]),
+ "5a_c-08_west": PreRegion("5a_c-08_west", "5a_c-08", connections_by_region["5a_c-08_west"], locations_by_region["5a_c-08_west"]),
+ "5a_c-08_east": PreRegion("5a_c-08_east", "5a_c-08", connections_by_region["5a_c-08_east"], locations_by_region["5a_c-08_east"]),
- "5a_c-10_west": PreRegion("5a_c-10_west", "5a_c-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-10_west"]),
- "5a_c-10_east": PreRegion("5a_c-10_east", "5a_c-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-10_east"]),
+ "5a_c-10_west": PreRegion("5a_c-10_west", "5a_c-10", connections_by_region["5a_c-10_west"], locations_by_region["5a_c-10_west"]),
+ "5a_c-10_east": PreRegion("5a_c-10_east", "5a_c-10", connections_by_region["5a_c-10_east"], locations_by_region["5a_c-10_east"]),
- "5a_c-12_west": PreRegion("5a_c-12_west", "5a_c-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-12_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-12_west"]),
- "5a_c-12_east": PreRegion("5a_c-12_east", "5a_c-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-12_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-12_east"]),
+ "5a_c-12_west": PreRegion("5a_c-12_west", "5a_c-12", connections_by_region["5a_c-12_west"], locations_by_region["5a_c-12_west"]),
+ "5a_c-12_east": PreRegion("5a_c-12_east", "5a_c-12", connections_by_region["5a_c-12_east"], locations_by_region["5a_c-12_east"]),
- "5a_c-07_west": PreRegion("5a_c-07_west", "5a_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-07_west"]),
- "5a_c-07_east": PreRegion("5a_c-07_east", "5a_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-07_east"]),
+ "5a_c-07_west": PreRegion("5a_c-07_west", "5a_c-07", connections_by_region["5a_c-07_west"], locations_by_region["5a_c-07_west"]),
+ "5a_c-07_east": PreRegion("5a_c-07_east", "5a_c-07", connections_by_region["5a_c-07_east"], locations_by_region["5a_c-07_east"]),
- "5a_c-11_west": PreRegion("5a_c-11_west", "5a_c-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-11_west"]),
- "5a_c-11_east": PreRegion("5a_c-11_east", "5a_c-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-11_east"]),
+ "5a_c-11_west": PreRegion("5a_c-11_west", "5a_c-11", connections_by_region["5a_c-11_west"], locations_by_region["5a_c-11_west"]),
+ "5a_c-11_east": PreRegion("5a_c-11_east", "5a_c-11", connections_by_region["5a_c-11_east"], locations_by_region["5a_c-11_east"]),
- "5a_c-09_west": PreRegion("5a_c-09_west", "5a_c-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-09_west"]),
- "5a_c-09_east": PreRegion("5a_c-09_east", "5a_c-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-09_east"]),
+ "5a_c-09_west": PreRegion("5a_c-09_west", "5a_c-09", connections_by_region["5a_c-09_west"], locations_by_region["5a_c-09_west"]),
+ "5a_c-09_east": PreRegion("5a_c-09_east", "5a_c-09", connections_by_region["5a_c-09_east"], locations_by_region["5a_c-09_east"]),
- "5a_c-13_west": PreRegion("5a_c-13_west", "5a_c-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-13_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-13_west"]),
- "5a_c-13_east": PreRegion("5a_c-13_east", "5a_c-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_c-13_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_c-13_east"]),
+ "5a_c-13_west": PreRegion("5a_c-13_west", "5a_c-13", connections_by_region["5a_c-13_west"], locations_by_region["5a_c-13_west"]),
+ "5a_c-13_east": PreRegion("5a_c-13_east", "5a_c-13", connections_by_region["5a_c-13_east"], locations_by_region["5a_c-13_east"]),
- "5a_d-00_south": PreRegion("5a_d-00_south", "5a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-00_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-00_south"]),
- "5a_d-00_north": PreRegion("5a_d-00_north", "5a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-00_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-00_north"]),
- "5a_d-00_west": PreRegion("5a_d-00_west", "5a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-00_west"]),
- "5a_d-00_east": PreRegion("5a_d-00_east", "5a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-00_east"]),
+ "5a_d-00_south": PreRegion("5a_d-00_south", "5a_d-00", connections_by_region["5a_d-00_south"], locations_by_region["5a_d-00_south"]),
+ "5a_d-00_north": PreRegion("5a_d-00_north", "5a_d-00", connections_by_region["5a_d-00_north"], locations_by_region["5a_d-00_north"]),
+ "5a_d-00_west": PreRegion("5a_d-00_west", "5a_d-00", connections_by_region["5a_d-00_west"], locations_by_region["5a_d-00_west"]),
+ "5a_d-00_east": PreRegion("5a_d-00_east", "5a_d-00", connections_by_region["5a_d-00_east"], locations_by_region["5a_d-00_east"]),
- "5a_d-01_south": PreRegion("5a_d-01_south", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_south"]),
- "5a_d-01_center": PreRegion("5a_d-01_center", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_center"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_center"]),
- "5a_d-01_south-west-left": PreRegion("5a_d-01_south-west-left", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_south-west-left"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_south-west-left"]),
- "5a_d-01_south-west-down": PreRegion("5a_d-01_south-west-down", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_south-west-down"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_south-west-down"]),
- "5a_d-01_south-east-right": PreRegion("5a_d-01_south-east-right", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_south-east-right"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_south-east-right"]),
- "5a_d-01_south-east-down": PreRegion("5a_d-01_south-east-down", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_south-east-down"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_south-east-down"]),
- "5a_d-01_west": PreRegion("5a_d-01_west", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_west"]),
- "5a_d-01_east": PreRegion("5a_d-01_east", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_east"]),
- "5a_d-01_north-west": PreRegion("5a_d-01_north-west", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_north-west"]),
- "5a_d-01_north-east": PreRegion("5a_d-01_north-east", "5a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-01_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-01_north-east"]),
+ "5a_d-01_south": PreRegion("5a_d-01_south", "5a_d-01", connections_by_region["5a_d-01_south"], locations_by_region["5a_d-01_south"]),
+ "5a_d-01_center": PreRegion("5a_d-01_center", "5a_d-01", connections_by_region["5a_d-01_center"], locations_by_region["5a_d-01_center"]),
+ "5a_d-01_south-west-left": PreRegion("5a_d-01_south-west-left", "5a_d-01", connections_by_region["5a_d-01_south-west-left"], locations_by_region["5a_d-01_south-west-left"]),
+ "5a_d-01_south-west-down": PreRegion("5a_d-01_south-west-down", "5a_d-01", connections_by_region["5a_d-01_south-west-down"], locations_by_region["5a_d-01_south-west-down"]),
+ "5a_d-01_south-east-right": PreRegion("5a_d-01_south-east-right", "5a_d-01", connections_by_region["5a_d-01_south-east-right"], locations_by_region["5a_d-01_south-east-right"]),
+ "5a_d-01_south-east-down": PreRegion("5a_d-01_south-east-down", "5a_d-01", connections_by_region["5a_d-01_south-east-down"], locations_by_region["5a_d-01_south-east-down"]),
+ "5a_d-01_west": PreRegion("5a_d-01_west", "5a_d-01", connections_by_region["5a_d-01_west"], locations_by_region["5a_d-01_west"]),
+ "5a_d-01_east": PreRegion("5a_d-01_east", "5a_d-01", connections_by_region["5a_d-01_east"], locations_by_region["5a_d-01_east"]),
+ "5a_d-01_north-west": PreRegion("5a_d-01_north-west", "5a_d-01", connections_by_region["5a_d-01_north-west"], locations_by_region["5a_d-01_north-west"]),
+ "5a_d-01_north-east": PreRegion("5a_d-01_north-east", "5a_d-01", connections_by_region["5a_d-01_north-east"], locations_by_region["5a_d-01_north-east"]),
- "5a_d-09_east": PreRegion("5a_d-09_east", "5a_d-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-09_east"]),
- "5a_d-09_west": PreRegion("5a_d-09_west", "5a_d-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-09_west"]),
+ "5a_d-09_east": PreRegion("5a_d-09_east", "5a_d-09", connections_by_region["5a_d-09_east"], locations_by_region["5a_d-09_east"]),
+ "5a_d-09_west": PreRegion("5a_d-09_west", "5a_d-09", connections_by_region["5a_d-09_west"], locations_by_region["5a_d-09_west"]),
- "5a_d-04_east": PreRegion("5a_d-04_east", "5a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-04_east"]),
- "5a_d-04_west": PreRegion("5a_d-04_west", "5a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-04_west"]),
- "5a_d-04_south-west-left": PreRegion("5a_d-04_south-west-left", "5a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-04_south-west-left"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-04_south-west-left"]),
- "5a_d-04_south-west-right": PreRegion("5a_d-04_south-west-right", "5a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-04_south-west-right"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-04_south-west-right"]),
- "5a_d-04_south-east": PreRegion("5a_d-04_south-east", "5a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-04_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-04_south-east"]),
- "5a_d-04_north": PreRegion("5a_d-04_north", "5a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-04_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-04_north"]),
+ "5a_d-04_east": PreRegion("5a_d-04_east", "5a_d-04", connections_by_region["5a_d-04_east"], locations_by_region["5a_d-04_east"]),
+ "5a_d-04_west": PreRegion("5a_d-04_west", "5a_d-04", connections_by_region["5a_d-04_west"], locations_by_region["5a_d-04_west"]),
+ "5a_d-04_south-west-left": PreRegion("5a_d-04_south-west-left", "5a_d-04", connections_by_region["5a_d-04_south-west-left"], locations_by_region["5a_d-04_south-west-left"]),
+ "5a_d-04_south-west-right": PreRegion("5a_d-04_south-west-right", "5a_d-04", connections_by_region["5a_d-04_south-west-right"], locations_by_region["5a_d-04_south-west-right"]),
+ "5a_d-04_south-east": PreRegion("5a_d-04_south-east", "5a_d-04", connections_by_region["5a_d-04_south-east"], locations_by_region["5a_d-04_south-east"]),
+ "5a_d-04_north": PreRegion("5a_d-04_north", "5a_d-04", connections_by_region["5a_d-04_north"], locations_by_region["5a_d-04_north"]),
- "5a_d-05_north": PreRegion("5a_d-05_north", "5a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-05_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-05_north"]),
- "5a_d-05_east": PreRegion("5a_d-05_east", "5a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-05_east"]),
- "5a_d-05_south": PreRegion("5a_d-05_south", "5a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-05_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-05_south"]),
- "5a_d-05_west": PreRegion("5a_d-05_west", "5a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-05_west"]),
+ "5a_d-05_north": PreRegion("5a_d-05_north", "5a_d-05", connections_by_region["5a_d-05_north"], locations_by_region["5a_d-05_north"]),
+ "5a_d-05_east": PreRegion("5a_d-05_east", "5a_d-05", connections_by_region["5a_d-05_east"], locations_by_region["5a_d-05_east"]),
+ "5a_d-05_south": PreRegion("5a_d-05_south", "5a_d-05", connections_by_region["5a_d-05_south"], locations_by_region["5a_d-05_south"]),
+ "5a_d-05_west": PreRegion("5a_d-05_west", "5a_d-05", connections_by_region["5a_d-05_west"], locations_by_region["5a_d-05_west"]),
- "5a_d-06_north-east": PreRegion("5a_d-06_north-east", "5a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-06_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-06_north-east"]),
- "5a_d-06_south-east": PreRegion("5a_d-06_south-east", "5a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-06_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-06_south-east"]),
- "5a_d-06_south-west": PreRegion("5a_d-06_south-west", "5a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-06_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-06_south-west"]),
- "5a_d-06_north-west": PreRegion("5a_d-06_north-west", "5a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-06_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-06_north-west"]),
+ "5a_d-06_north-east": PreRegion("5a_d-06_north-east", "5a_d-06", connections_by_region["5a_d-06_north-east"], locations_by_region["5a_d-06_north-east"]),
+ "5a_d-06_south-east": PreRegion("5a_d-06_south-east", "5a_d-06", connections_by_region["5a_d-06_south-east"], locations_by_region["5a_d-06_south-east"]),
+ "5a_d-06_south-west": PreRegion("5a_d-06_south-west", "5a_d-06", connections_by_region["5a_d-06_south-west"], locations_by_region["5a_d-06_south-west"]),
+ "5a_d-06_north-west": PreRegion("5a_d-06_north-west", "5a_d-06", connections_by_region["5a_d-06_north-west"], locations_by_region["5a_d-06_north-west"]),
- "5a_d-07_west": PreRegion("5a_d-07_west", "5a_d-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-07_west"]),
- "5a_d-07_north": PreRegion("5a_d-07_north", "5a_d-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-07_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-07_north"]),
+ "5a_d-07_west": PreRegion("5a_d-07_west", "5a_d-07", connections_by_region["5a_d-07_west"], locations_by_region["5a_d-07_west"]),
+ "5a_d-07_north": PreRegion("5a_d-07_north", "5a_d-07", connections_by_region["5a_d-07_north"], locations_by_region["5a_d-07_north"]),
- "5a_d-02_east": PreRegion("5a_d-02_east", "5a_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-02_east"]),
- "5a_d-02_west": PreRegion("5a_d-02_west", "5a_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-02_west"]),
+ "5a_d-02_east": PreRegion("5a_d-02_east", "5a_d-02", connections_by_region["5a_d-02_east"], locations_by_region["5a_d-02_east"]),
+ "5a_d-02_west": PreRegion("5a_d-02_west", "5a_d-02", connections_by_region["5a_d-02_west"], locations_by_region["5a_d-02_west"]),
- "5a_d-03_east": PreRegion("5a_d-03_east", "5a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-03_east"]),
- "5a_d-03_west": PreRegion("5a_d-03_west", "5a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-03_west"]),
+ "5a_d-03_east": PreRegion("5a_d-03_east", "5a_d-03", connections_by_region["5a_d-03_east"], locations_by_region["5a_d-03_east"]),
+ "5a_d-03_west": PreRegion("5a_d-03_west", "5a_d-03", connections_by_region["5a_d-03_west"], locations_by_region["5a_d-03_west"]),
- "5a_d-15_north-west": PreRegion("5a_d-15_north-west", "5a_d-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-15_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-15_north-west"]),
- "5a_d-15_center": PreRegion("5a_d-15_center", "5a_d-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-15_center"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-15_center"]),
- "5a_d-15_west": PreRegion("5a_d-15_west", "5a_d-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-15_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-15_west"]),
- "5a_d-15_south-west": PreRegion("5a_d-15_south-west", "5a_d-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-15_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-15_south-west"]),
- "5a_d-15_south": PreRegion("5a_d-15_south", "5a_d-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-15_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-15_south"]),
- "5a_d-15_south-east": PreRegion("5a_d-15_south-east", "5a_d-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-15_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-15_south-east"]),
+ "5a_d-15_north-west": PreRegion("5a_d-15_north-west", "5a_d-15", connections_by_region["5a_d-15_north-west"], locations_by_region["5a_d-15_north-west"]),
+ "5a_d-15_center": PreRegion("5a_d-15_center", "5a_d-15", connections_by_region["5a_d-15_center"], locations_by_region["5a_d-15_center"]),
+ "5a_d-15_west": PreRegion("5a_d-15_west", "5a_d-15", connections_by_region["5a_d-15_west"], locations_by_region["5a_d-15_west"]),
+ "5a_d-15_south-west": PreRegion("5a_d-15_south-west", "5a_d-15", connections_by_region["5a_d-15_south-west"], locations_by_region["5a_d-15_south-west"]),
+ "5a_d-15_south": PreRegion("5a_d-15_south", "5a_d-15", connections_by_region["5a_d-15_south"], locations_by_region["5a_d-15_south"]),
+ "5a_d-15_south-east": PreRegion("5a_d-15_south-east", "5a_d-15", connections_by_region["5a_d-15_south-east"], locations_by_region["5a_d-15_south-east"]),
- "5a_d-13_east": PreRegion("5a_d-13_east", "5a_d-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-13_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-13_east"]),
- "5a_d-13_west": PreRegion("5a_d-13_west", "5a_d-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-13_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-13_west"]),
+ "5a_d-13_east": PreRegion("5a_d-13_east", "5a_d-13", connections_by_region["5a_d-13_east"], locations_by_region["5a_d-13_east"]),
+ "5a_d-13_west": PreRegion("5a_d-13_west", "5a_d-13", connections_by_region["5a_d-13_west"], locations_by_region["5a_d-13_west"]),
- "5a_d-19b_south-east-right": PreRegion("5a_d-19b_south-east-right", "5a_d-19b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-19b_south-east-right"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-19b_south-east-right"]),
- "5a_d-19b_south-east-down": PreRegion("5a_d-19b_south-east-down", "5a_d-19b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-19b_south-east-down"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-19b_south-east-down"]),
- "5a_d-19b_south-west": PreRegion("5a_d-19b_south-west", "5a_d-19b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-19b_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-19b_south-west"]),
- "5a_d-19b_north-east": PreRegion("5a_d-19b_north-east", "5a_d-19b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-19b_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-19b_north-east"]),
+ "5a_d-19b_south-east-right": PreRegion("5a_d-19b_south-east-right", "5a_d-19b", connections_by_region["5a_d-19b_south-east-right"], locations_by_region["5a_d-19b_south-east-right"]),
+ "5a_d-19b_south-east-down": PreRegion("5a_d-19b_south-east-down", "5a_d-19b", connections_by_region["5a_d-19b_south-east-down"], locations_by_region["5a_d-19b_south-east-down"]),
+ "5a_d-19b_south-west": PreRegion("5a_d-19b_south-west", "5a_d-19b", connections_by_region["5a_d-19b_south-west"], locations_by_region["5a_d-19b_south-west"]),
+ "5a_d-19b_north-east": PreRegion("5a_d-19b_north-east", "5a_d-19b", connections_by_region["5a_d-19b_north-east"], locations_by_region["5a_d-19b_north-east"]),
- "5a_d-19_east": PreRegion("5a_d-19_east", "5a_d-19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-19_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-19_east"]),
- "5a_d-19_west": PreRegion("5a_d-19_west", "5a_d-19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-19_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-19_west"]),
+ "5a_d-19_east": PreRegion("5a_d-19_east", "5a_d-19", connections_by_region["5a_d-19_east"], locations_by_region["5a_d-19_east"]),
+ "5a_d-19_west": PreRegion("5a_d-19_west", "5a_d-19", connections_by_region["5a_d-19_west"], locations_by_region["5a_d-19_west"]),
- "5a_d-10_west": PreRegion("5a_d-10_west", "5a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-10_west"]),
- "5a_d-10_east": PreRegion("5a_d-10_east", "5a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-10_east"]),
+ "5a_d-10_west": PreRegion("5a_d-10_west", "5a_d-10", connections_by_region["5a_d-10_west"], locations_by_region["5a_d-10_west"]),
+ "5a_d-10_east": PreRegion("5a_d-10_east", "5a_d-10", connections_by_region["5a_d-10_east"], locations_by_region["5a_d-10_east"]),
- "5a_d-20_west": PreRegion("5a_d-20_west", "5a_d-20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-20_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-20_west"]),
- "5a_d-20_east": PreRegion("5a_d-20_east", "5a_d-20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_d-20_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_d-20_east"]),
+ "5a_d-20_west": PreRegion("5a_d-20_west", "5a_d-20", connections_by_region["5a_d-20_west"], locations_by_region["5a_d-20_west"]),
+ "5a_d-20_east": PreRegion("5a_d-20_east", "5a_d-20", connections_by_region["5a_d-20_east"], locations_by_region["5a_d-20_east"]),
- "5a_e-00_west": PreRegion("5a_e-00_west", "5a_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-00_west"]),
- "5a_e-00_east": PreRegion("5a_e-00_east", "5a_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-00_east"]),
+ "5a_e-00_west": PreRegion("5a_e-00_west", "5a_e-00", connections_by_region["5a_e-00_west"], locations_by_region["5a_e-00_west"]),
+ "5a_e-00_east": PreRegion("5a_e-00_east", "5a_e-00", connections_by_region["5a_e-00_east"], locations_by_region["5a_e-00_east"]),
- "5a_e-01_west": PreRegion("5a_e-01_west", "5a_e-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-01_west"]),
- "5a_e-01_east": PreRegion("5a_e-01_east", "5a_e-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-01_east"]),
+ "5a_e-01_west": PreRegion("5a_e-01_west", "5a_e-01", connections_by_region["5a_e-01_west"], locations_by_region["5a_e-01_west"]),
+ "5a_e-01_east": PreRegion("5a_e-01_east", "5a_e-01", connections_by_region["5a_e-01_east"], locations_by_region["5a_e-01_east"]),
- "5a_e-02_west": PreRegion("5a_e-02_west", "5a_e-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-02_west"]),
- "5a_e-02_east": PreRegion("5a_e-02_east", "5a_e-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-02_east"]),
+ "5a_e-02_west": PreRegion("5a_e-02_west", "5a_e-02", connections_by_region["5a_e-02_west"], locations_by_region["5a_e-02_west"]),
+ "5a_e-02_east": PreRegion("5a_e-02_east", "5a_e-02", connections_by_region["5a_e-02_east"], locations_by_region["5a_e-02_east"]),
- "5a_e-03_west": PreRegion("5a_e-03_west", "5a_e-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-03_west"]),
- "5a_e-03_east": PreRegion("5a_e-03_east", "5a_e-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-03_east"]),
+ "5a_e-03_west": PreRegion("5a_e-03_west", "5a_e-03", connections_by_region["5a_e-03_west"], locations_by_region["5a_e-03_west"]),
+ "5a_e-03_east": PreRegion("5a_e-03_east", "5a_e-03", connections_by_region["5a_e-03_east"], locations_by_region["5a_e-03_east"]),
- "5a_e-04_west": PreRegion("5a_e-04_west", "5a_e-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-04_west"]),
- "5a_e-04_east": PreRegion("5a_e-04_east", "5a_e-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-04_east"]),
+ "5a_e-04_west": PreRegion("5a_e-04_west", "5a_e-04", connections_by_region["5a_e-04_west"], locations_by_region["5a_e-04_west"]),
+ "5a_e-04_east": PreRegion("5a_e-04_east", "5a_e-04", connections_by_region["5a_e-04_east"], locations_by_region["5a_e-04_east"]),
- "5a_e-06_west": PreRegion("5a_e-06_west", "5a_e-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-06_west"]),
- "5a_e-06_east": PreRegion("5a_e-06_east", "5a_e-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-06_east"]),
+ "5a_e-06_west": PreRegion("5a_e-06_west", "5a_e-06", connections_by_region["5a_e-06_west"], locations_by_region["5a_e-06_west"]),
+ "5a_e-06_east": PreRegion("5a_e-06_east", "5a_e-06", connections_by_region["5a_e-06_east"], locations_by_region["5a_e-06_east"]),
- "5a_e-05_west": PreRegion("5a_e-05_west", "5a_e-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-05_west"]),
- "5a_e-05_east": PreRegion("5a_e-05_east", "5a_e-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-05_east"]),
+ "5a_e-05_west": PreRegion("5a_e-05_west", "5a_e-05", connections_by_region["5a_e-05_west"], locations_by_region["5a_e-05_west"]),
+ "5a_e-05_east": PreRegion("5a_e-05_east", "5a_e-05", connections_by_region["5a_e-05_east"], locations_by_region["5a_e-05_east"]),
- "5a_e-07_west": PreRegion("5a_e-07_west", "5a_e-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-07_west"]),
- "5a_e-07_east": PreRegion("5a_e-07_east", "5a_e-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-07_east"]),
+ "5a_e-07_west": PreRegion("5a_e-07_west", "5a_e-07", connections_by_region["5a_e-07_west"], locations_by_region["5a_e-07_west"]),
+ "5a_e-07_east": PreRegion("5a_e-07_east", "5a_e-07", connections_by_region["5a_e-07_east"], locations_by_region["5a_e-07_east"]),
- "5a_e-08_west": PreRegion("5a_e-08_west", "5a_e-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-08_west"]),
- "5a_e-08_east": PreRegion("5a_e-08_east", "5a_e-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-08_east"]),
+ "5a_e-08_west": PreRegion("5a_e-08_west", "5a_e-08", connections_by_region["5a_e-08_west"], locations_by_region["5a_e-08_west"]),
+ "5a_e-08_east": PreRegion("5a_e-08_east", "5a_e-08", connections_by_region["5a_e-08_east"], locations_by_region["5a_e-08_east"]),
- "5a_e-09_west": PreRegion("5a_e-09_west", "5a_e-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-09_west"]),
- "5a_e-09_east": PreRegion("5a_e-09_east", "5a_e-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-09_east"]),
+ "5a_e-09_west": PreRegion("5a_e-09_west", "5a_e-09", connections_by_region["5a_e-09_west"], locations_by_region["5a_e-09_west"]),
+ "5a_e-09_east": PreRegion("5a_e-09_east", "5a_e-09", connections_by_region["5a_e-09_east"], locations_by_region["5a_e-09_east"]),
- "5a_e-10_west": PreRegion("5a_e-10_west", "5a_e-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-10_west"]),
- "5a_e-10_east": PreRegion("5a_e-10_east", "5a_e-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-10_east"]),
+ "5a_e-10_west": PreRegion("5a_e-10_west", "5a_e-10", connections_by_region["5a_e-10_west"], locations_by_region["5a_e-10_west"]),
+ "5a_e-10_east": PreRegion("5a_e-10_east", "5a_e-10", connections_by_region["5a_e-10_east"], locations_by_region["5a_e-10_east"]),
- "5a_e-11_west": PreRegion("5a_e-11_west", "5a_e-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-11_west"]),
- "5a_e-11_goal": PreRegion("5a_e-11_goal", "5a_e-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5a_e-11_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "5a_e-11_goal"]),
+ "5a_e-11_west": PreRegion("5a_e-11_west", "5a_e-11", connections_by_region["5a_e-11_west"], locations_by_region["5a_e-11_west"]),
+ "5a_e-11_goal": PreRegion("5a_e-11_goal", "5a_e-11", connections_by_region["5a_e-11_goal"], locations_by_region["5a_e-11_goal"]),
- "5b_start_west": PreRegion("5b_start_west", "5b_start", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_start_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_start_west"]),
- "5b_start_east": PreRegion("5b_start_east", "5b_start", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_start_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_start_east"]),
+ "5b_start_west": PreRegion("5b_start_west", "5b_start", connections_by_region["5b_start_west"], locations_by_region["5b_start_west"]),
+ "5b_start_east": PreRegion("5b_start_east", "5b_start", connections_by_region["5b_start_east"], locations_by_region["5b_start_east"]),
- "5b_a-00_west": PreRegion("5b_a-00_west", "5b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_a-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_a-00_west"]),
- "5b_a-00_east": PreRegion("5b_a-00_east", "5b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_a-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_a-00_east"]),
+ "5b_a-00_west": PreRegion("5b_a-00_west", "5b_a-00", connections_by_region["5b_a-00_west"], locations_by_region["5b_a-00_west"]),
+ "5b_a-00_east": PreRegion("5b_a-00_east", "5b_a-00", connections_by_region["5b_a-00_east"], locations_by_region["5b_a-00_east"]),
- "5b_a-01_west": PreRegion("5b_a-01_west", "5b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_a-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_a-01_west"]),
- "5b_a-01_east": PreRegion("5b_a-01_east", "5b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_a-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_a-01_east"]),
+ "5b_a-01_west": PreRegion("5b_a-01_west", "5b_a-01", connections_by_region["5b_a-01_west"], locations_by_region["5b_a-01_west"]),
+ "5b_a-01_east": PreRegion("5b_a-01_east", "5b_a-01", connections_by_region["5b_a-01_east"], locations_by_region["5b_a-01_east"]),
- "5b_a-02_west": PreRegion("5b_a-02_west", "5b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_a-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_a-02_west"]),
- "5b_a-02_east": PreRegion("5b_a-02_east", "5b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_a-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_a-02_east"]),
+ "5b_a-02_west": PreRegion("5b_a-02_west", "5b_a-02", connections_by_region["5b_a-02_west"], locations_by_region["5b_a-02_west"]),
+ "5b_a-02_east": PreRegion("5b_a-02_east", "5b_a-02", connections_by_region["5b_a-02_east"], locations_by_region["5b_a-02_east"]),
- "5b_b-00_south": PreRegion("5b_b-00_south", "5b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-00_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-00_south"]),
- "5b_b-00_west": PreRegion("5b_b-00_west", "5b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-00_west"]),
- "5b_b-00_north": PreRegion("5b_b-00_north", "5b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-00_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-00_north"]),
- "5b_b-00_east": PreRegion("5b_b-00_east", "5b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-00_east"]),
+ "5b_b-00_south": PreRegion("5b_b-00_south", "5b_b-00", connections_by_region["5b_b-00_south"], locations_by_region["5b_b-00_south"]),
+ "5b_b-00_west": PreRegion("5b_b-00_west", "5b_b-00", connections_by_region["5b_b-00_west"], locations_by_region["5b_b-00_west"]),
+ "5b_b-00_north": PreRegion("5b_b-00_north", "5b_b-00", connections_by_region["5b_b-00_north"], locations_by_region["5b_b-00_north"]),
+ "5b_b-00_east": PreRegion("5b_b-00_east", "5b_b-00", connections_by_region["5b_b-00_east"], locations_by_region["5b_b-00_east"]),
- "5b_b-01_west": PreRegion("5b_b-01_west", "5b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-01_west"]),
- "5b_b-01_north": PreRegion("5b_b-01_north", "5b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-01_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-01_north"]),
- "5b_b-01_east": PreRegion("5b_b-01_east", "5b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-01_east"]),
+ "5b_b-01_west": PreRegion("5b_b-01_west", "5b_b-01", connections_by_region["5b_b-01_west"], locations_by_region["5b_b-01_west"]),
+ "5b_b-01_north": PreRegion("5b_b-01_north", "5b_b-01", connections_by_region["5b_b-01_north"], locations_by_region["5b_b-01_north"]),
+ "5b_b-01_east": PreRegion("5b_b-01_east", "5b_b-01", connections_by_region["5b_b-01_east"], locations_by_region["5b_b-01_east"]),
- "5b_b-04_east": PreRegion("5b_b-04_east", "5b_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-04_east"]),
- "5b_b-04_west": PreRegion("5b_b-04_west", "5b_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-04_west"]),
+ "5b_b-04_east": PreRegion("5b_b-04_east", "5b_b-04", connections_by_region["5b_b-04_east"], locations_by_region["5b_b-04_east"]),
+ "5b_b-04_west": PreRegion("5b_b-04_west", "5b_b-04", connections_by_region["5b_b-04_west"], locations_by_region["5b_b-04_west"]),
- "5b_b-02_south": PreRegion("5b_b-02_south", "5b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-02_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-02_south"]),
- "5b_b-02_center": PreRegion("5b_b-02_center", "5b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-02_center"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-02_center"]),
- "5b_b-02_north-west": PreRegion("5b_b-02_north-west", "5b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-02_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-02_north-west"]),
- "5b_b-02_north-east": PreRegion("5b_b-02_north-east", "5b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-02_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-02_north-east"]),
- "5b_b-02_north": PreRegion("5b_b-02_north", "5b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-02_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-02_north"]),
- "5b_b-02_south-west": PreRegion("5b_b-02_south-west", "5b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-02_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-02_south-west"]),
- "5b_b-02_south-east": PreRegion("5b_b-02_south-east", "5b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-02_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-02_south-east"]),
+ "5b_b-02_south": PreRegion("5b_b-02_south", "5b_b-02", connections_by_region["5b_b-02_south"], locations_by_region["5b_b-02_south"]),
+ "5b_b-02_center": PreRegion("5b_b-02_center", "5b_b-02", connections_by_region["5b_b-02_center"], locations_by_region["5b_b-02_center"]),
+ "5b_b-02_north-west": PreRegion("5b_b-02_north-west", "5b_b-02", connections_by_region["5b_b-02_north-west"], locations_by_region["5b_b-02_north-west"]),
+ "5b_b-02_north-east": PreRegion("5b_b-02_north-east", "5b_b-02", connections_by_region["5b_b-02_north-east"], locations_by_region["5b_b-02_north-east"]),
+ "5b_b-02_north": PreRegion("5b_b-02_north", "5b_b-02", connections_by_region["5b_b-02_north"], locations_by_region["5b_b-02_north"]),
+ "5b_b-02_south-west": PreRegion("5b_b-02_south-west", "5b_b-02", connections_by_region["5b_b-02_south-west"], locations_by_region["5b_b-02_south-west"]),
+ "5b_b-02_south-east": PreRegion("5b_b-02_south-east", "5b_b-02", connections_by_region["5b_b-02_south-east"], locations_by_region["5b_b-02_south-east"]),
- "5b_b-05_north": PreRegion("5b_b-05_north", "5b_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-05_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-05_north"]),
- "5b_b-05_south": PreRegion("5b_b-05_south", "5b_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-05_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-05_south"]),
+ "5b_b-05_north": PreRegion("5b_b-05_north", "5b_b-05", connections_by_region["5b_b-05_north"], locations_by_region["5b_b-05_north"]),
+ "5b_b-05_south": PreRegion("5b_b-05_south", "5b_b-05", connections_by_region["5b_b-05_south"], locations_by_region["5b_b-05_south"]),
- "5b_b-06_east": PreRegion("5b_b-06_east", "5b_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-06_east"]),
+ "5b_b-06_east": PreRegion("5b_b-06_east", "5b_b-06", connections_by_region["5b_b-06_east"], locations_by_region["5b_b-06_east"]),
- "5b_b-07_north": PreRegion("5b_b-07_north", "5b_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-07_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-07_north"]),
- "5b_b-07_south": PreRegion("5b_b-07_south", "5b_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-07_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-07_south"]),
+ "5b_b-07_north": PreRegion("5b_b-07_north", "5b_b-07", connections_by_region["5b_b-07_north"], locations_by_region["5b_b-07_north"]),
+ "5b_b-07_south": PreRegion("5b_b-07_south", "5b_b-07", connections_by_region["5b_b-07_south"], locations_by_region["5b_b-07_south"]),
- "5b_b-03_west": PreRegion("5b_b-03_west", "5b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-03_west"]),
- "5b_b-03_main": PreRegion("5b_b-03_main", "5b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-03_main"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-03_main"]),
- "5b_b-03_north": PreRegion("5b_b-03_north", "5b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-03_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-03_north"]),
- "5b_b-03_east": PreRegion("5b_b-03_east", "5b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-03_east"]),
+ "5b_b-03_west": PreRegion("5b_b-03_west", "5b_b-03", connections_by_region["5b_b-03_west"], locations_by_region["5b_b-03_west"]),
+ "5b_b-03_main": PreRegion("5b_b-03_main", "5b_b-03", connections_by_region["5b_b-03_main"], locations_by_region["5b_b-03_main"]),
+ "5b_b-03_north": PreRegion("5b_b-03_north", "5b_b-03", connections_by_region["5b_b-03_north"], locations_by_region["5b_b-03_north"]),
+ "5b_b-03_east": PreRegion("5b_b-03_east", "5b_b-03", connections_by_region["5b_b-03_east"], locations_by_region["5b_b-03_east"]),
- "5b_b-08_south": PreRegion("5b_b-08_south", "5b_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-08_south"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-08_south"]),
- "5b_b-08_north": PreRegion("5b_b-08_north", "5b_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-08_north"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-08_north"]),
- "5b_b-08_east": PreRegion("5b_b-08_east", "5b_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-08_east"]),
+ "5b_b-08_south": PreRegion("5b_b-08_south", "5b_b-08", connections_by_region["5b_b-08_south"], locations_by_region["5b_b-08_south"]),
+ "5b_b-08_north": PreRegion("5b_b-08_north", "5b_b-08", connections_by_region["5b_b-08_north"], locations_by_region["5b_b-08_north"]),
+ "5b_b-08_east": PreRegion("5b_b-08_east", "5b_b-08", connections_by_region["5b_b-08_east"], locations_by_region["5b_b-08_east"]),
- "5b_b-09_bottom": PreRegion("5b_b-09_bottom", "5b_b-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-09_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-09_bottom"]),
- "5b_b-09_mirror": PreRegion("5b_b-09_mirror", "5b_b-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_b-09_mirror"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_b-09_mirror"]),
+ "5b_b-09_bottom": PreRegion("5b_b-09_bottom", "5b_b-09", connections_by_region["5b_b-09_bottom"], locations_by_region["5b_b-09_bottom"]),
+ "5b_b-09_mirror": PreRegion("5b_b-09_mirror", "5b_b-09", connections_by_region["5b_b-09_mirror"], locations_by_region["5b_b-09_mirror"]),
- "5b_c-00_bottom": PreRegion("5b_c-00_bottom", "5b_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-00_bottom"]),
- "5b_c-00_mirror": PreRegion("5b_c-00_mirror", "5b_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-00_mirror"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-00_mirror"]),
+ "5b_c-00_bottom": PreRegion("5b_c-00_bottom", "5b_c-00", connections_by_region["5b_c-00_bottom"], locations_by_region["5b_c-00_bottom"]),
+ "5b_c-00_mirror": PreRegion("5b_c-00_mirror", "5b_c-00", connections_by_region["5b_c-00_mirror"], locations_by_region["5b_c-00_mirror"]),
- "5b_c-01_west": PreRegion("5b_c-01_west", "5b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-01_west"]),
- "5b_c-01_east": PreRegion("5b_c-01_east", "5b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-01_east"]),
+ "5b_c-01_west": PreRegion("5b_c-01_west", "5b_c-01", connections_by_region["5b_c-01_west"], locations_by_region["5b_c-01_west"]),
+ "5b_c-01_east": PreRegion("5b_c-01_east", "5b_c-01", connections_by_region["5b_c-01_east"], locations_by_region["5b_c-01_east"]),
- "5b_c-02_west": PreRegion("5b_c-02_west", "5b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-02_west"]),
- "5b_c-02_east": PreRegion("5b_c-02_east", "5b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-02_east"]),
+ "5b_c-02_west": PreRegion("5b_c-02_west", "5b_c-02", connections_by_region["5b_c-02_west"], locations_by_region["5b_c-02_west"]),
+ "5b_c-02_east": PreRegion("5b_c-02_east", "5b_c-02", connections_by_region["5b_c-02_east"], locations_by_region["5b_c-02_east"]),
- "5b_c-03_west": PreRegion("5b_c-03_west", "5b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-03_west"]),
- "5b_c-03_east": PreRegion("5b_c-03_east", "5b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-03_east"]),
+ "5b_c-03_west": PreRegion("5b_c-03_west", "5b_c-03", connections_by_region["5b_c-03_west"], locations_by_region["5b_c-03_west"]),
+ "5b_c-03_east": PreRegion("5b_c-03_east", "5b_c-03", connections_by_region["5b_c-03_east"], locations_by_region["5b_c-03_east"]),
- "5b_c-04_west": PreRegion("5b_c-04_west", "5b_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-04_west"]),
- "5b_c-04_east": PreRegion("5b_c-04_east", "5b_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_c-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_c-04_east"]),
+ "5b_c-04_west": PreRegion("5b_c-04_west", "5b_c-04", connections_by_region["5b_c-04_west"], locations_by_region["5b_c-04_west"]),
+ "5b_c-04_east": PreRegion("5b_c-04_east", "5b_c-04", connections_by_region["5b_c-04_east"], locations_by_region["5b_c-04_east"]),
- "5b_d-00_west": PreRegion("5b_d-00_west", "5b_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-00_west"]),
- "5b_d-00_east": PreRegion("5b_d-00_east", "5b_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-00_east"]),
+ "5b_d-00_west": PreRegion("5b_d-00_west", "5b_d-00", connections_by_region["5b_d-00_west"], locations_by_region["5b_d-00_west"]),
+ "5b_d-00_east": PreRegion("5b_d-00_east", "5b_d-00", connections_by_region["5b_d-00_east"], locations_by_region["5b_d-00_east"]),
- "5b_d-01_west": PreRegion("5b_d-01_west", "5b_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-01_west"]),
- "5b_d-01_east": PreRegion("5b_d-01_east", "5b_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-01_east"]),
+ "5b_d-01_west": PreRegion("5b_d-01_west", "5b_d-01", connections_by_region["5b_d-01_west"], locations_by_region["5b_d-01_west"]),
+ "5b_d-01_east": PreRegion("5b_d-01_east", "5b_d-01", connections_by_region["5b_d-01_east"], locations_by_region["5b_d-01_east"]),
- "5b_d-02_west": PreRegion("5b_d-02_west", "5b_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-02_west"]),
- "5b_d-02_east": PreRegion("5b_d-02_east", "5b_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-02_east"]),
+ "5b_d-02_west": PreRegion("5b_d-02_west", "5b_d-02", connections_by_region["5b_d-02_west"], locations_by_region["5b_d-02_west"]),
+ "5b_d-02_east": PreRegion("5b_d-02_east", "5b_d-02", connections_by_region["5b_d-02_east"], locations_by_region["5b_d-02_east"]),
- "5b_d-03_west": PreRegion("5b_d-03_west", "5b_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-03_west"]),
- "5b_d-03_east": PreRegion("5b_d-03_east", "5b_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-03_east"]),
+ "5b_d-03_west": PreRegion("5b_d-03_west", "5b_d-03", connections_by_region["5b_d-03_west"], locations_by_region["5b_d-03_west"]),
+ "5b_d-03_east": PreRegion("5b_d-03_east", "5b_d-03", connections_by_region["5b_d-03_east"], locations_by_region["5b_d-03_east"]),
- "5b_d-04_west": PreRegion("5b_d-04_west", "5b_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-04_west"]),
- "5b_d-04_east": PreRegion("5b_d-04_east", "5b_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-04_east"]),
+ "5b_d-04_west": PreRegion("5b_d-04_west", "5b_d-04", connections_by_region["5b_d-04_west"], locations_by_region["5b_d-04_west"]),
+ "5b_d-04_east": PreRegion("5b_d-04_east", "5b_d-04", connections_by_region["5b_d-04_east"], locations_by_region["5b_d-04_east"]),
- "5b_d-05_west": PreRegion("5b_d-05_west", "5b_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-05_west"]),
- "5b_d-05_goal": PreRegion("5b_d-05_goal", "5b_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5b_d-05_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "5b_d-05_goal"]),
+ "5b_d-05_west": PreRegion("5b_d-05_west", "5b_d-05", connections_by_region["5b_d-05_west"], locations_by_region["5b_d-05_west"]),
+ "5b_d-05_goal": PreRegion("5b_d-05_goal", "5b_d-05", connections_by_region["5b_d-05_goal"], locations_by_region["5b_d-05_goal"]),
- "5c_00_west": PreRegion("5c_00_west", "5c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5c_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5c_00_west"]),
- "5c_00_east": PreRegion("5c_00_east", "5c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5c_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5c_00_east"]),
+ "5c_00_west": PreRegion("5c_00_west", "5c_00", connections_by_region["5c_00_west"], locations_by_region["5c_00_west"]),
+ "5c_00_east": PreRegion("5c_00_east", "5c_00", connections_by_region["5c_00_east"], locations_by_region["5c_00_east"]),
- "5c_01_west": PreRegion("5c_01_west", "5c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5c_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5c_01_west"]),
- "5c_01_east": PreRegion("5c_01_east", "5c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5c_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "5c_01_east"]),
+ "5c_01_west": PreRegion("5c_01_west", "5c_01", connections_by_region["5c_01_west"], locations_by_region["5c_01_west"]),
+ "5c_01_east": PreRegion("5c_01_east", "5c_01", connections_by_region["5c_01_east"], locations_by_region["5c_01_east"]),
- "5c_02_west": PreRegion("5c_02_west", "5c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5c_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "5c_02_west"]),
- "5c_02_goal": PreRegion("5c_02_goal", "5c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "5c_02_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "5c_02_goal"]),
+ "5c_02_west": PreRegion("5c_02_west", "5c_02", connections_by_region["5c_02_west"], locations_by_region["5c_02_west"]),
+ "5c_02_goal": PreRegion("5c_02_goal", "5c_02", connections_by_region["5c_02_goal"], locations_by_region["5c_02_goal"]),
- "6a_00_west": PreRegion("6a_00_west", "6a_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_00_west"]),
- "6a_00_east": PreRegion("6a_00_east", "6a_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_00_east"]),
+ "6a_00_west": PreRegion("6a_00_west", "6a_00", connections_by_region["6a_00_west"], locations_by_region["6a_00_west"]),
+ "6a_00_east": PreRegion("6a_00_east", "6a_00", connections_by_region["6a_00_east"], locations_by_region["6a_00_east"]),
- "6a_01_bottom": PreRegion("6a_01_bottom", "6a_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_01_bottom"]),
- "6a_01_top": PreRegion("6a_01_top", "6a_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_01_top"]),
+ "6a_01_bottom": PreRegion("6a_01_bottom", "6a_01", connections_by_region["6a_01_bottom"], locations_by_region["6a_01_bottom"]),
+ "6a_01_top": PreRegion("6a_01_top", "6a_01", connections_by_region["6a_01_top"], locations_by_region["6a_01_top"]),
- "6a_02_bottom": PreRegion("6a_02_bottom", "6a_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_02_bottom"]),
- "6a_02_bottom-west": PreRegion("6a_02_bottom-west", "6a_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_02_bottom-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_02_bottom-west"]),
- "6a_02_top-west": PreRegion("6a_02_top-west", "6a_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_02_top-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_02_top-west"]),
- "6a_02_top": PreRegion("6a_02_top", "6a_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_02_top"]),
+ "6a_02_bottom": PreRegion("6a_02_bottom", "6a_02", connections_by_region["6a_02_bottom"], locations_by_region["6a_02_bottom"]),
+ "6a_02_bottom-west": PreRegion("6a_02_bottom-west", "6a_02", connections_by_region["6a_02_bottom-west"], locations_by_region["6a_02_bottom-west"]),
+ "6a_02_top-west": PreRegion("6a_02_top-west", "6a_02", connections_by_region["6a_02_top-west"], locations_by_region["6a_02_top-west"]),
+ "6a_02_top": PreRegion("6a_02_top", "6a_02", connections_by_region["6a_02_top"], locations_by_region["6a_02_top"]),
- "6a_03_bottom": PreRegion("6a_03_bottom", "6a_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_03_bottom"]),
- "6a_03_top": PreRegion("6a_03_top", "6a_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_03_top"]),
+ "6a_03_bottom": PreRegion("6a_03_bottom", "6a_03", connections_by_region["6a_03_bottom"], locations_by_region["6a_03_bottom"]),
+ "6a_03_top": PreRegion("6a_03_top", "6a_03", connections_by_region["6a_03_top"], locations_by_region["6a_03_top"]),
- "6a_02b_bottom": PreRegion("6a_02b_bottom", "6a_02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_02b_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_02b_bottom"]),
- "6a_02b_top": PreRegion("6a_02b_top", "6a_02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_02b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_02b_top"]),
+ "6a_02b_bottom": PreRegion("6a_02b_bottom", "6a_02b", connections_by_region["6a_02b_bottom"], locations_by_region["6a_02b_bottom"]),
+ "6a_02b_top": PreRegion("6a_02b_top", "6a_02b", connections_by_region["6a_02b_top"], locations_by_region["6a_02b_top"]),
- "6a_04_south": PreRegion("6a_04_south", "6a_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04_south"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04_south"]),
- "6a_04_south-west": PreRegion("6a_04_south-west", "6a_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04_south-west"]),
- "6a_04_south-east": PreRegion("6a_04_south-east", "6a_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04_south-east"]),
- "6a_04_east": PreRegion("6a_04_east", "6a_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04_east"]),
- "6a_04_north-west": PreRegion("6a_04_north-west", "6a_04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04_north-west"]),
+ "6a_04_south": PreRegion("6a_04_south", "6a_04", connections_by_region["6a_04_south"], locations_by_region["6a_04_south"]),
+ "6a_04_south-west": PreRegion("6a_04_south-west", "6a_04", connections_by_region["6a_04_south-west"], locations_by_region["6a_04_south-west"]),
+ "6a_04_south-east": PreRegion("6a_04_south-east", "6a_04", connections_by_region["6a_04_south-east"], locations_by_region["6a_04_south-east"]),
+ "6a_04_east": PreRegion("6a_04_east", "6a_04", connections_by_region["6a_04_east"], locations_by_region["6a_04_east"]),
+ "6a_04_north-west": PreRegion("6a_04_north-west", "6a_04", connections_by_region["6a_04_north-west"], locations_by_region["6a_04_north-west"]),
- "6a_04b_west": PreRegion("6a_04b_west", "6a_04b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04b_west"]),
- "6a_04b_east": PreRegion("6a_04b_east", "6a_04b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04b_east"]),
+ "6a_04b_west": PreRegion("6a_04b_west", "6a_04b", connections_by_region["6a_04b_west"], locations_by_region["6a_04b_west"]),
+ "6a_04b_east": PreRegion("6a_04b_east", "6a_04b", connections_by_region["6a_04b_east"], locations_by_region["6a_04b_east"]),
- "6a_04c_east": PreRegion("6a_04c_east", "6a_04c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04c_east"]),
+ "6a_04c_east": PreRegion("6a_04c_east", "6a_04c", connections_by_region["6a_04c_east"], locations_by_region["6a_04c_east"]),
- "6a_04d_west": PreRegion("6a_04d_west", "6a_04d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04d_west"]),
+ "6a_04d_west": PreRegion("6a_04d_west", "6a_04d", connections_by_region["6a_04d_west"], locations_by_region["6a_04d_west"]),
- "6a_04e_east": PreRegion("6a_04e_east", "6a_04e", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_04e_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_04e_east"]),
+ "6a_04e_east": PreRegion("6a_04e_east", "6a_04e", connections_by_region["6a_04e_east"], locations_by_region["6a_04e_east"]),
- "6a_05_west": PreRegion("6a_05_west", "6a_05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_05_west"]),
- "6a_05_east": PreRegion("6a_05_east", "6a_05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_05_east"]),
+ "6a_05_west": PreRegion("6a_05_west", "6a_05", connections_by_region["6a_05_west"], locations_by_region["6a_05_west"]),
+ "6a_05_east": PreRegion("6a_05_east", "6a_05", connections_by_region["6a_05_east"], locations_by_region["6a_05_east"]),
- "6a_06_west": PreRegion("6a_06_west", "6a_06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_06_west"]),
- "6a_06_east": PreRegion("6a_06_east", "6a_06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_06_east"]),
+ "6a_06_west": PreRegion("6a_06_west", "6a_06", connections_by_region["6a_06_west"], locations_by_region["6a_06_west"]),
+ "6a_06_east": PreRegion("6a_06_east", "6a_06", connections_by_region["6a_06_east"], locations_by_region["6a_06_east"]),
- "6a_07_west": PreRegion("6a_07_west", "6a_07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_07_west"]),
- "6a_07_east": PreRegion("6a_07_east", "6a_07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_07_east"]),
- "6a_07_north-east": PreRegion("6a_07_north-east", "6a_07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_07_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_07_north-east"]),
+ "6a_07_west": PreRegion("6a_07_west", "6a_07", connections_by_region["6a_07_west"], locations_by_region["6a_07_west"]),
+ "6a_07_east": PreRegion("6a_07_east", "6a_07", connections_by_region["6a_07_east"], locations_by_region["6a_07_east"]),
+ "6a_07_north-east": PreRegion("6a_07_north-east", "6a_07", connections_by_region["6a_07_north-east"], locations_by_region["6a_07_north-east"]),
- "6a_08a_west": PreRegion("6a_08a_west", "6a_08a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_08a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_08a_west"]),
- "6a_08a_east": PreRegion("6a_08a_east", "6a_08a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_08a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_08a_east"]),
+ "6a_08a_west": PreRegion("6a_08a_west", "6a_08a", connections_by_region["6a_08a_west"], locations_by_region["6a_08a_west"]),
+ "6a_08a_east": PreRegion("6a_08a_east", "6a_08a", connections_by_region["6a_08a_east"], locations_by_region["6a_08a_east"]),
- "6a_08b_west": PreRegion("6a_08b_west", "6a_08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_08b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_08b_west"]),
- "6a_08b_east": PreRegion("6a_08b_east", "6a_08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_08b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_08b_east"]),
+ "6a_08b_west": PreRegion("6a_08b_west", "6a_08b", connections_by_region["6a_08b_west"], locations_by_region["6a_08b_west"]),
+ "6a_08b_east": PreRegion("6a_08b_east", "6a_08b", connections_by_region["6a_08b_east"], locations_by_region["6a_08b_east"]),
- "6a_09_west": PreRegion("6a_09_west", "6a_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_09_west"]),
- "6a_09_north-west": PreRegion("6a_09_north-west", "6a_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_09_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_09_north-west"]),
- "6a_09_east": PreRegion("6a_09_east", "6a_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_09_east"]),
- "6a_09_north-east": PreRegion("6a_09_north-east", "6a_09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_09_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_09_north-east"]),
+ "6a_09_west": PreRegion("6a_09_west", "6a_09", connections_by_region["6a_09_west"], locations_by_region["6a_09_west"]),
+ "6a_09_north-west": PreRegion("6a_09_north-west", "6a_09", connections_by_region["6a_09_north-west"], locations_by_region["6a_09_north-west"]),
+ "6a_09_east": PreRegion("6a_09_east", "6a_09", connections_by_region["6a_09_east"], locations_by_region["6a_09_east"]),
+ "6a_09_north-east": PreRegion("6a_09_north-east", "6a_09", connections_by_region["6a_09_north-east"], locations_by_region["6a_09_north-east"]),
- "6a_10a_west": PreRegion("6a_10a_west", "6a_10a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_10a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_10a_west"]),
- "6a_10a_east": PreRegion("6a_10a_east", "6a_10a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_10a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_10a_east"]),
+ "6a_10a_west": PreRegion("6a_10a_west", "6a_10a", connections_by_region["6a_10a_west"], locations_by_region["6a_10a_west"]),
+ "6a_10a_east": PreRegion("6a_10a_east", "6a_10a", connections_by_region["6a_10a_east"], locations_by_region["6a_10a_east"]),
- "6a_10b_west": PreRegion("6a_10b_west", "6a_10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_10b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_10b_west"]),
- "6a_10b_east": PreRegion("6a_10b_east", "6a_10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_10b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_10b_east"]),
+ "6a_10b_west": PreRegion("6a_10b_west", "6a_10b", connections_by_region["6a_10b_west"], locations_by_region["6a_10b_west"]),
+ "6a_10b_east": PreRegion("6a_10b_east", "6a_10b", connections_by_region["6a_10b_east"], locations_by_region["6a_10b_east"]),
- "6a_11_west": PreRegion("6a_11_west", "6a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_11_west"]),
- "6a_11_north-west": PreRegion("6a_11_north-west", "6a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_11_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_11_north-west"]),
- "6a_11_east": PreRegion("6a_11_east", "6a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_11_east"]),
- "6a_11_north-east": PreRegion("6a_11_north-east", "6a_11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_11_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_11_north-east"]),
+ "6a_11_west": PreRegion("6a_11_west", "6a_11", connections_by_region["6a_11_west"], locations_by_region["6a_11_west"]),
+ "6a_11_north-west": PreRegion("6a_11_north-west", "6a_11", connections_by_region["6a_11_north-west"], locations_by_region["6a_11_north-west"]),
+ "6a_11_east": PreRegion("6a_11_east", "6a_11", connections_by_region["6a_11_east"], locations_by_region["6a_11_east"]),
+ "6a_11_north-east": PreRegion("6a_11_north-east", "6a_11", connections_by_region["6a_11_north-east"], locations_by_region["6a_11_north-east"]),
- "6a_12a_west": PreRegion("6a_12a_west", "6a_12a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_12a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_12a_west"]),
- "6a_12a_east": PreRegion("6a_12a_east", "6a_12a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_12a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_12a_east"]),
+ "6a_12a_west": PreRegion("6a_12a_west", "6a_12a", connections_by_region["6a_12a_west"], locations_by_region["6a_12a_west"]),
+ "6a_12a_east": PreRegion("6a_12a_east", "6a_12a", connections_by_region["6a_12a_east"], locations_by_region["6a_12a_east"]),
- "6a_12b_west": PreRegion("6a_12b_west", "6a_12b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_12b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_12b_west"]),
- "6a_12b_east": PreRegion("6a_12b_east", "6a_12b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_12b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_12b_east"]),
+ "6a_12b_west": PreRegion("6a_12b_west", "6a_12b", connections_by_region["6a_12b_west"], locations_by_region["6a_12b_west"]),
+ "6a_12b_east": PreRegion("6a_12b_east", "6a_12b", connections_by_region["6a_12b_east"], locations_by_region["6a_12b_east"]),
- "6a_13_west": PreRegion("6a_13_west", "6a_13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_13_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_13_west"]),
- "6a_13_north-west": PreRegion("6a_13_north-west", "6a_13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_13_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_13_north-west"]),
- "6a_13_east": PreRegion("6a_13_east", "6a_13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_13_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_13_east"]),
- "6a_13_north-east": PreRegion("6a_13_north-east", "6a_13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_13_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_13_north-east"]),
+ "6a_13_west": PreRegion("6a_13_west", "6a_13", connections_by_region["6a_13_west"], locations_by_region["6a_13_west"]),
+ "6a_13_north-west": PreRegion("6a_13_north-west", "6a_13", connections_by_region["6a_13_north-west"], locations_by_region["6a_13_north-west"]),
+ "6a_13_east": PreRegion("6a_13_east", "6a_13", connections_by_region["6a_13_east"], locations_by_region["6a_13_east"]),
+ "6a_13_north-east": PreRegion("6a_13_north-east", "6a_13", connections_by_region["6a_13_north-east"], locations_by_region["6a_13_north-east"]),
- "6a_14a_west": PreRegion("6a_14a_west", "6a_14a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_14a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_14a_west"]),
- "6a_14a_east": PreRegion("6a_14a_east", "6a_14a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_14a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_14a_east"]),
+ "6a_14a_west": PreRegion("6a_14a_west", "6a_14a", connections_by_region["6a_14a_west"], locations_by_region["6a_14a_west"]),
+ "6a_14a_east": PreRegion("6a_14a_east", "6a_14a", connections_by_region["6a_14a_east"], locations_by_region["6a_14a_east"]),
- "6a_14b_west": PreRegion("6a_14b_west", "6a_14b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_14b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_14b_west"]),
- "6a_14b_east": PreRegion("6a_14b_east", "6a_14b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_14b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_14b_east"]),
+ "6a_14b_west": PreRegion("6a_14b_west", "6a_14b", connections_by_region["6a_14b_west"], locations_by_region["6a_14b_west"]),
+ "6a_14b_east": PreRegion("6a_14b_east", "6a_14b", connections_by_region["6a_14b_east"], locations_by_region["6a_14b_east"]),
- "6a_15_west": PreRegion("6a_15_west", "6a_15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_15_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_15_west"]),
- "6a_15_north-west": PreRegion("6a_15_north-west", "6a_15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_15_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_15_north-west"]),
- "6a_15_east": PreRegion("6a_15_east", "6a_15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_15_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_15_east"]),
- "6a_15_north-east": PreRegion("6a_15_north-east", "6a_15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_15_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_15_north-east"]),
+ "6a_15_west": PreRegion("6a_15_west", "6a_15", connections_by_region["6a_15_west"], locations_by_region["6a_15_west"]),
+ "6a_15_north-west": PreRegion("6a_15_north-west", "6a_15", connections_by_region["6a_15_north-west"], locations_by_region["6a_15_north-west"]),
+ "6a_15_east": PreRegion("6a_15_east", "6a_15", connections_by_region["6a_15_east"], locations_by_region["6a_15_east"]),
+ "6a_15_north-east": PreRegion("6a_15_north-east", "6a_15", connections_by_region["6a_15_north-east"], locations_by_region["6a_15_north-east"]),
- "6a_16a_west": PreRegion("6a_16a_west", "6a_16a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_16a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_16a_west"]),
- "6a_16a_east": PreRegion("6a_16a_east", "6a_16a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_16a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_16a_east"]),
+ "6a_16a_west": PreRegion("6a_16a_west", "6a_16a", connections_by_region["6a_16a_west"], locations_by_region["6a_16a_west"]),
+ "6a_16a_east": PreRegion("6a_16a_east", "6a_16a", connections_by_region["6a_16a_east"], locations_by_region["6a_16a_east"]),
- "6a_16b_west": PreRegion("6a_16b_west", "6a_16b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_16b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_16b_west"]),
- "6a_16b_east": PreRegion("6a_16b_east", "6a_16b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_16b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_16b_east"]),
+ "6a_16b_west": PreRegion("6a_16b_west", "6a_16b", connections_by_region["6a_16b_west"], locations_by_region["6a_16b_west"]),
+ "6a_16b_east": PreRegion("6a_16b_east", "6a_16b", connections_by_region["6a_16b_east"], locations_by_region["6a_16b_east"]),
- "6a_17_west": PreRegion("6a_17_west", "6a_17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_17_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_17_west"]),
- "6a_17_north-west": PreRegion("6a_17_north-west", "6a_17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_17_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_17_north-west"]),
- "6a_17_east": PreRegion("6a_17_east", "6a_17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_17_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_17_east"]),
- "6a_17_north-east": PreRegion("6a_17_north-east", "6a_17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_17_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_17_north-east"]),
+ "6a_17_west": PreRegion("6a_17_west", "6a_17", connections_by_region["6a_17_west"], locations_by_region["6a_17_west"]),
+ "6a_17_north-west": PreRegion("6a_17_north-west", "6a_17", connections_by_region["6a_17_north-west"], locations_by_region["6a_17_north-west"]),
+ "6a_17_east": PreRegion("6a_17_east", "6a_17", connections_by_region["6a_17_east"], locations_by_region["6a_17_east"]),
+ "6a_17_north-east": PreRegion("6a_17_north-east", "6a_17", connections_by_region["6a_17_north-east"], locations_by_region["6a_17_north-east"]),
- "6a_18a_west": PreRegion("6a_18a_west", "6a_18a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_18a_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_18a_west"]),
- "6a_18a_east": PreRegion("6a_18a_east", "6a_18a", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_18a_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_18a_east"]),
+ "6a_18a_west": PreRegion("6a_18a_west", "6a_18a", connections_by_region["6a_18a_west"], locations_by_region["6a_18a_west"]),
+ "6a_18a_east": PreRegion("6a_18a_east", "6a_18a", connections_by_region["6a_18a_east"], locations_by_region["6a_18a_east"]),
- "6a_18b_west": PreRegion("6a_18b_west", "6a_18b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_18b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_18b_west"]),
- "6a_18b_east": PreRegion("6a_18b_east", "6a_18b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_18b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_18b_east"]),
+ "6a_18b_west": PreRegion("6a_18b_west", "6a_18b", connections_by_region["6a_18b_west"], locations_by_region["6a_18b_west"]),
+ "6a_18b_east": PreRegion("6a_18b_east", "6a_18b", connections_by_region["6a_18b_east"], locations_by_region["6a_18b_east"]),
- "6a_19_west": PreRegion("6a_19_west", "6a_19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_19_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_19_west"]),
- "6a_19_north-west": PreRegion("6a_19_north-west", "6a_19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_19_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_19_north-west"]),
- "6a_19_east": PreRegion("6a_19_east", "6a_19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_19_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_19_east"]),
+ "6a_19_west": PreRegion("6a_19_west", "6a_19", connections_by_region["6a_19_west"], locations_by_region["6a_19_west"]),
+ "6a_19_north-west": PreRegion("6a_19_north-west", "6a_19", connections_by_region["6a_19_north-west"], locations_by_region["6a_19_north-west"]),
+ "6a_19_east": PreRegion("6a_19_east", "6a_19", connections_by_region["6a_19_east"], locations_by_region["6a_19_east"]),
- "6a_20_west": PreRegion("6a_20_west", "6a_20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_20_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_20_west"]),
- "6a_20_east": PreRegion("6a_20_east", "6a_20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_20_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_20_east"]),
+ "6a_20_west": PreRegion("6a_20_west", "6a_20", connections_by_region["6a_20_west"], locations_by_region["6a_20_west"]),
+ "6a_20_east": PreRegion("6a_20_east", "6a_20", connections_by_region["6a_20_east"], locations_by_region["6a_20_east"]),
- "6a_b-00_west": PreRegion("6a_b-00_west", "6a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-00_west"]),
- "6a_b-00_east": PreRegion("6a_b-00_east", "6a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-00_east"]),
- "6a_b-00_top": PreRegion("6a_b-00_top", "6a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-00_top"]),
+ "6a_b-00_west": PreRegion("6a_b-00_west", "6a_b-00", connections_by_region["6a_b-00_west"], locations_by_region["6a_b-00_west"]),
+ "6a_b-00_east": PreRegion("6a_b-00_east", "6a_b-00", connections_by_region["6a_b-00_east"], locations_by_region["6a_b-00_east"]),
+ "6a_b-00_top": PreRegion("6a_b-00_top", "6a_b-00", connections_by_region["6a_b-00_top"], locations_by_region["6a_b-00_top"]),
- "6a_b-00b_bottom": PreRegion("6a_b-00b_bottom", "6a_b-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-00b_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-00b_bottom"]),
- "6a_b-00b_top": PreRegion("6a_b-00b_top", "6a_b-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-00b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-00b_top"]),
+ "6a_b-00b_bottom": PreRegion("6a_b-00b_bottom", "6a_b-00b", connections_by_region["6a_b-00b_bottom"], locations_by_region["6a_b-00b_bottom"]),
+ "6a_b-00b_top": PreRegion("6a_b-00b_top", "6a_b-00b", connections_by_region["6a_b-00b_top"], locations_by_region["6a_b-00b_top"]),
- "6a_b-00c_east": PreRegion("6a_b-00c_east", "6a_b-00c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-00c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-00c_east"]),
+ "6a_b-00c_east": PreRegion("6a_b-00c_east", "6a_b-00c", connections_by_region["6a_b-00c_east"], locations_by_region["6a_b-00c_east"]),
- "6a_b-01_west": PreRegion("6a_b-01_west", "6a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-01_west"]),
- "6a_b-01_east": PreRegion("6a_b-01_east", "6a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-01_east"]),
+ "6a_b-01_west": PreRegion("6a_b-01_west", "6a_b-01", connections_by_region["6a_b-01_west"], locations_by_region["6a_b-01_west"]),
+ "6a_b-01_east": PreRegion("6a_b-01_east", "6a_b-01", connections_by_region["6a_b-01_east"], locations_by_region["6a_b-01_east"]),
- "6a_b-02_top": PreRegion("6a_b-02_top", "6a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-02_top"]),
- "6a_b-02_bottom": PreRegion("6a_b-02_bottom", "6a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-02_bottom"]),
+ "6a_b-02_top": PreRegion("6a_b-02_top", "6a_b-02", connections_by_region["6a_b-02_top"], locations_by_region["6a_b-02_top"]),
+ "6a_b-02_bottom": PreRegion("6a_b-02_bottom", "6a_b-02", connections_by_region["6a_b-02_bottom"], locations_by_region["6a_b-02_bottom"]),
- "6a_b-02b_top": PreRegion("6a_b-02b_top", "6a_b-02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-02b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-02b_top"]),
- "6a_b-02b_bottom": PreRegion("6a_b-02b_bottom", "6a_b-02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-02b_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-02b_bottom"]),
+ "6a_b-02b_top": PreRegion("6a_b-02b_top", "6a_b-02b", connections_by_region["6a_b-02b_top"], locations_by_region["6a_b-02b_top"]),
+ "6a_b-02b_bottom": PreRegion("6a_b-02b_bottom", "6a_b-02b", connections_by_region["6a_b-02b_bottom"], locations_by_region["6a_b-02b_bottom"]),
- "6a_b-03_west": PreRegion("6a_b-03_west", "6a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-03_west"]),
- "6a_b-03_east": PreRegion("6a_b-03_east", "6a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_b-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_b-03_east"]),
+ "6a_b-03_west": PreRegion("6a_b-03_west", "6a_b-03", connections_by_region["6a_b-03_west"], locations_by_region["6a_b-03_west"]),
+ "6a_b-03_east": PreRegion("6a_b-03_east", "6a_b-03", connections_by_region["6a_b-03_east"], locations_by_region["6a_b-03_east"]),
- "6a_boss-00_west": PreRegion("6a_boss-00_west", "6a_boss-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-00_west"]),
- "6a_boss-00_east": PreRegion("6a_boss-00_east", "6a_boss-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-00_east"]),
+ "6a_boss-00_west": PreRegion("6a_boss-00_west", "6a_boss-00", connections_by_region["6a_boss-00_west"], locations_by_region["6a_boss-00_west"]),
+ "6a_boss-00_east": PreRegion("6a_boss-00_east", "6a_boss-00", connections_by_region["6a_boss-00_east"], locations_by_region["6a_boss-00_east"]),
- "6a_boss-01_west": PreRegion("6a_boss-01_west", "6a_boss-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-01_west"]),
- "6a_boss-01_east": PreRegion("6a_boss-01_east", "6a_boss-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-01_east"]),
+ "6a_boss-01_west": PreRegion("6a_boss-01_west", "6a_boss-01", connections_by_region["6a_boss-01_west"], locations_by_region["6a_boss-01_west"]),
+ "6a_boss-01_east": PreRegion("6a_boss-01_east", "6a_boss-01", connections_by_region["6a_boss-01_east"], locations_by_region["6a_boss-01_east"]),
- "6a_boss-02_west": PreRegion("6a_boss-02_west", "6a_boss-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-02_west"]),
- "6a_boss-02_east": PreRegion("6a_boss-02_east", "6a_boss-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-02_east"]),
+ "6a_boss-02_west": PreRegion("6a_boss-02_west", "6a_boss-02", connections_by_region["6a_boss-02_west"], locations_by_region["6a_boss-02_west"]),
+ "6a_boss-02_east": PreRegion("6a_boss-02_east", "6a_boss-02", connections_by_region["6a_boss-02_east"], locations_by_region["6a_boss-02_east"]),
- "6a_boss-03_west": PreRegion("6a_boss-03_west", "6a_boss-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-03_west"]),
- "6a_boss-03_east": PreRegion("6a_boss-03_east", "6a_boss-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-03_east"]),
+ "6a_boss-03_west": PreRegion("6a_boss-03_west", "6a_boss-03", connections_by_region["6a_boss-03_west"], locations_by_region["6a_boss-03_west"]),
+ "6a_boss-03_east": PreRegion("6a_boss-03_east", "6a_boss-03", connections_by_region["6a_boss-03_east"], locations_by_region["6a_boss-03_east"]),
- "6a_boss-04_west": PreRegion("6a_boss-04_west", "6a_boss-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-04_west"]),
- "6a_boss-04_east": PreRegion("6a_boss-04_east", "6a_boss-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-04_east"]),
+ "6a_boss-04_west": PreRegion("6a_boss-04_west", "6a_boss-04", connections_by_region["6a_boss-04_west"], locations_by_region["6a_boss-04_west"]),
+ "6a_boss-04_east": PreRegion("6a_boss-04_east", "6a_boss-04", connections_by_region["6a_boss-04_east"], locations_by_region["6a_boss-04_east"]),
- "6a_boss-05_west": PreRegion("6a_boss-05_west", "6a_boss-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-05_west"]),
- "6a_boss-05_east": PreRegion("6a_boss-05_east", "6a_boss-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-05_east"]),
+ "6a_boss-05_west": PreRegion("6a_boss-05_west", "6a_boss-05", connections_by_region["6a_boss-05_west"], locations_by_region["6a_boss-05_west"]),
+ "6a_boss-05_east": PreRegion("6a_boss-05_east", "6a_boss-05", connections_by_region["6a_boss-05_east"], locations_by_region["6a_boss-05_east"]),
- "6a_boss-06_west": PreRegion("6a_boss-06_west", "6a_boss-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-06_west"]),
- "6a_boss-06_east": PreRegion("6a_boss-06_east", "6a_boss-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-06_east"]),
+ "6a_boss-06_west": PreRegion("6a_boss-06_west", "6a_boss-06", connections_by_region["6a_boss-06_west"], locations_by_region["6a_boss-06_west"]),
+ "6a_boss-06_east": PreRegion("6a_boss-06_east", "6a_boss-06", connections_by_region["6a_boss-06_east"], locations_by_region["6a_boss-06_east"]),
- "6a_boss-07_west": PreRegion("6a_boss-07_west", "6a_boss-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-07_west"]),
- "6a_boss-07_east": PreRegion("6a_boss-07_east", "6a_boss-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-07_east"]),
+ "6a_boss-07_west": PreRegion("6a_boss-07_west", "6a_boss-07", connections_by_region["6a_boss-07_west"], locations_by_region["6a_boss-07_west"]),
+ "6a_boss-07_east": PreRegion("6a_boss-07_east", "6a_boss-07", connections_by_region["6a_boss-07_east"], locations_by_region["6a_boss-07_east"]),
- "6a_boss-08_west": PreRegion("6a_boss-08_west", "6a_boss-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-08_west"]),
- "6a_boss-08_east": PreRegion("6a_boss-08_east", "6a_boss-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-08_east"]),
+ "6a_boss-08_west": PreRegion("6a_boss-08_west", "6a_boss-08", connections_by_region["6a_boss-08_west"], locations_by_region["6a_boss-08_west"]),
+ "6a_boss-08_east": PreRegion("6a_boss-08_east", "6a_boss-08", connections_by_region["6a_boss-08_east"], locations_by_region["6a_boss-08_east"]),
- "6a_boss-09_west": PreRegion("6a_boss-09_west", "6a_boss-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-09_west"]),
- "6a_boss-09_east": PreRegion("6a_boss-09_east", "6a_boss-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-09_east"]),
+ "6a_boss-09_west": PreRegion("6a_boss-09_west", "6a_boss-09", connections_by_region["6a_boss-09_west"], locations_by_region["6a_boss-09_west"]),
+ "6a_boss-09_east": PreRegion("6a_boss-09_east", "6a_boss-09", connections_by_region["6a_boss-09_east"], locations_by_region["6a_boss-09_east"]),
- "6a_boss-10_west": PreRegion("6a_boss-10_west", "6a_boss-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-10_west"]),
- "6a_boss-10_east": PreRegion("6a_boss-10_east", "6a_boss-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-10_east"]),
+ "6a_boss-10_west": PreRegion("6a_boss-10_west", "6a_boss-10", connections_by_region["6a_boss-10_west"], locations_by_region["6a_boss-10_west"]),
+ "6a_boss-10_east": PreRegion("6a_boss-10_east", "6a_boss-10", connections_by_region["6a_boss-10_east"], locations_by_region["6a_boss-10_east"]),
- "6a_boss-11_west": PreRegion("6a_boss-11_west", "6a_boss-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-11_west"]),
- "6a_boss-11_east": PreRegion("6a_boss-11_east", "6a_boss-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-11_east"]),
+ "6a_boss-11_west": PreRegion("6a_boss-11_west", "6a_boss-11", connections_by_region["6a_boss-11_west"], locations_by_region["6a_boss-11_west"]),
+ "6a_boss-11_east": PreRegion("6a_boss-11_east", "6a_boss-11", connections_by_region["6a_boss-11_east"], locations_by_region["6a_boss-11_east"]),
- "6a_boss-12_west": PreRegion("6a_boss-12_west", "6a_boss-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-12_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-12_west"]),
- "6a_boss-12_east": PreRegion("6a_boss-12_east", "6a_boss-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-12_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-12_east"]),
+ "6a_boss-12_west": PreRegion("6a_boss-12_west", "6a_boss-12", connections_by_region["6a_boss-12_west"], locations_by_region["6a_boss-12_west"]),
+ "6a_boss-12_east": PreRegion("6a_boss-12_east", "6a_boss-12", connections_by_region["6a_boss-12_east"], locations_by_region["6a_boss-12_east"]),
- "6a_boss-13_west": PreRegion("6a_boss-13_west", "6a_boss-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-13_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-13_west"]),
- "6a_boss-13_east": PreRegion("6a_boss-13_east", "6a_boss-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-13_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-13_east"]),
+ "6a_boss-13_west": PreRegion("6a_boss-13_west", "6a_boss-13", connections_by_region["6a_boss-13_west"], locations_by_region["6a_boss-13_west"]),
+ "6a_boss-13_east": PreRegion("6a_boss-13_east", "6a_boss-13", connections_by_region["6a_boss-13_east"], locations_by_region["6a_boss-13_east"]),
- "6a_boss-14_west": PreRegion("6a_boss-14_west", "6a_boss-14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-14_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-14_west"]),
- "6a_boss-14_east": PreRegion("6a_boss-14_east", "6a_boss-14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-14_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-14_east"]),
+ "6a_boss-14_west": PreRegion("6a_boss-14_west", "6a_boss-14", connections_by_region["6a_boss-14_west"], locations_by_region["6a_boss-14_west"]),
+ "6a_boss-14_east": PreRegion("6a_boss-14_east", "6a_boss-14", connections_by_region["6a_boss-14_east"], locations_by_region["6a_boss-14_east"]),
- "6a_boss-15_west": PreRegion("6a_boss-15_west", "6a_boss-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-15_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-15_west"]),
- "6a_boss-15_east": PreRegion("6a_boss-15_east", "6a_boss-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-15_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-15_east"]),
+ "6a_boss-15_west": PreRegion("6a_boss-15_west", "6a_boss-15", connections_by_region["6a_boss-15_west"], locations_by_region["6a_boss-15_west"]),
+ "6a_boss-15_east": PreRegion("6a_boss-15_east", "6a_boss-15", connections_by_region["6a_boss-15_east"], locations_by_region["6a_boss-15_east"]),
- "6a_boss-16_west": PreRegion("6a_boss-16_west", "6a_boss-16", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-16_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-16_west"]),
- "6a_boss-16_east": PreRegion("6a_boss-16_east", "6a_boss-16", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-16_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-16_east"]),
+ "6a_boss-16_west": PreRegion("6a_boss-16_west", "6a_boss-16", connections_by_region["6a_boss-16_west"], locations_by_region["6a_boss-16_west"]),
+ "6a_boss-16_east": PreRegion("6a_boss-16_east", "6a_boss-16", connections_by_region["6a_boss-16_east"], locations_by_region["6a_boss-16_east"]),
- "6a_boss-17_west": PreRegion("6a_boss-17_west", "6a_boss-17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-17_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-17_west"]),
- "6a_boss-17_east": PreRegion("6a_boss-17_east", "6a_boss-17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-17_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-17_east"]),
+ "6a_boss-17_west": PreRegion("6a_boss-17_west", "6a_boss-17", connections_by_region["6a_boss-17_west"], locations_by_region["6a_boss-17_west"]),
+ "6a_boss-17_east": PreRegion("6a_boss-17_east", "6a_boss-17", connections_by_region["6a_boss-17_east"], locations_by_region["6a_boss-17_east"]),
- "6a_boss-18_west": PreRegion("6a_boss-18_west", "6a_boss-18", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-18_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-18_west"]),
- "6a_boss-18_east": PreRegion("6a_boss-18_east", "6a_boss-18", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-18_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-18_east"]),
+ "6a_boss-18_west": PreRegion("6a_boss-18_west", "6a_boss-18", connections_by_region["6a_boss-18_west"], locations_by_region["6a_boss-18_west"]),
+ "6a_boss-18_east": PreRegion("6a_boss-18_east", "6a_boss-18", connections_by_region["6a_boss-18_east"], locations_by_region["6a_boss-18_east"]),
- "6a_boss-19_west": PreRegion("6a_boss-19_west", "6a_boss-19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-19_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-19_west"]),
- "6a_boss-19_east": PreRegion("6a_boss-19_east", "6a_boss-19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-19_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-19_east"]),
+ "6a_boss-19_west": PreRegion("6a_boss-19_west", "6a_boss-19", connections_by_region["6a_boss-19_west"], locations_by_region["6a_boss-19_west"]),
+ "6a_boss-19_east": PreRegion("6a_boss-19_east", "6a_boss-19", connections_by_region["6a_boss-19_east"], locations_by_region["6a_boss-19_east"]),
- "6a_boss-20_west": PreRegion("6a_boss-20_west", "6a_boss-20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-20_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-20_west"]),
- "6a_boss-20_east": PreRegion("6a_boss-20_east", "6a_boss-20", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_boss-20_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_boss-20_east"]),
+ "6a_boss-20_west": PreRegion("6a_boss-20_west", "6a_boss-20", connections_by_region["6a_boss-20_west"], locations_by_region["6a_boss-20_west"]),
+ "6a_boss-20_east": PreRegion("6a_boss-20_east", "6a_boss-20", connections_by_region["6a_boss-20_east"], locations_by_region["6a_boss-20_east"]),
- "6a_after-00_bottom": PreRegion("6a_after-00_bottom", "6a_after-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_after-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_after-00_bottom"]),
- "6a_after-00_top": PreRegion("6a_after-00_top", "6a_after-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_after-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_after-00_top"]),
+ "6a_after-00_bottom": PreRegion("6a_after-00_bottom", "6a_after-00", connections_by_region["6a_after-00_bottom"], locations_by_region["6a_after-00_bottom"]),
+ "6a_after-00_top": PreRegion("6a_after-00_top", "6a_after-00", connections_by_region["6a_after-00_top"], locations_by_region["6a_after-00_top"]),
- "6a_after-01_bottom": PreRegion("6a_after-01_bottom", "6a_after-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_after-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_after-01_bottom"]),
- "6a_after-01_goal": PreRegion("6a_after-01_goal", "6a_after-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6a_after-01_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "6a_after-01_goal"]),
+ "6a_after-01_bottom": PreRegion("6a_after-01_bottom", "6a_after-01", connections_by_region["6a_after-01_bottom"], locations_by_region["6a_after-01_bottom"]),
+ "6a_after-01_goal": PreRegion("6a_after-01_goal", "6a_after-01", connections_by_region["6a_after-01_goal"], locations_by_region["6a_after-01_goal"]),
- "6b_a-00_bottom": PreRegion("6b_a-00_bottom", "6b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-00_bottom"]),
- "6b_a-00_top": PreRegion("6b_a-00_top", "6b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-00_top"]),
+ "6b_a-00_bottom": PreRegion("6b_a-00_bottom", "6b_a-00", connections_by_region["6b_a-00_bottom"], locations_by_region["6b_a-00_bottom"]),
+ "6b_a-00_top": PreRegion("6b_a-00_top", "6b_a-00", connections_by_region["6b_a-00_top"], locations_by_region["6b_a-00_top"]),
- "6b_a-01_bottom": PreRegion("6b_a-01_bottom", "6b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-01_bottom"]),
- "6b_a-01_top": PreRegion("6b_a-01_top", "6b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-01_top"]),
+ "6b_a-01_bottom": PreRegion("6b_a-01_bottom", "6b_a-01", connections_by_region["6b_a-01_bottom"], locations_by_region["6b_a-01_bottom"]),
+ "6b_a-01_top": PreRegion("6b_a-01_top", "6b_a-01", connections_by_region["6b_a-01_top"], locations_by_region["6b_a-01_top"]),
- "6b_a-02_bottom": PreRegion("6b_a-02_bottom", "6b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-02_bottom"]),
- "6b_a-02_top": PreRegion("6b_a-02_top", "6b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-02_top"]),
+ "6b_a-02_bottom": PreRegion("6b_a-02_bottom", "6b_a-02", connections_by_region["6b_a-02_bottom"], locations_by_region["6b_a-02_bottom"]),
+ "6b_a-02_top": PreRegion("6b_a-02_top", "6b_a-02", connections_by_region["6b_a-02_top"], locations_by_region["6b_a-02_top"]),
- "6b_a-03_west": PreRegion("6b_a-03_west", "6b_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-03_west"]),
- "6b_a-03_east": PreRegion("6b_a-03_east", "6b_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-03_east"]),
+ "6b_a-03_west": PreRegion("6b_a-03_west", "6b_a-03", connections_by_region["6b_a-03_west"], locations_by_region["6b_a-03_west"]),
+ "6b_a-03_east": PreRegion("6b_a-03_east", "6b_a-03", connections_by_region["6b_a-03_east"], locations_by_region["6b_a-03_east"]),
- "6b_a-04_west": PreRegion("6b_a-04_west", "6b_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-04_west"]),
- "6b_a-04_east": PreRegion("6b_a-04_east", "6b_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-04_east"]),
+ "6b_a-04_west": PreRegion("6b_a-04_west", "6b_a-04", connections_by_region["6b_a-04_west"], locations_by_region["6b_a-04_west"]),
+ "6b_a-04_east": PreRegion("6b_a-04_east", "6b_a-04", connections_by_region["6b_a-04_east"], locations_by_region["6b_a-04_east"]),
- "6b_a-05_west": PreRegion("6b_a-05_west", "6b_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-05_west"]),
- "6b_a-05_east": PreRegion("6b_a-05_east", "6b_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-05_east"]),
+ "6b_a-05_west": PreRegion("6b_a-05_west", "6b_a-05", connections_by_region["6b_a-05_west"], locations_by_region["6b_a-05_west"]),
+ "6b_a-05_east": PreRegion("6b_a-05_east", "6b_a-05", connections_by_region["6b_a-05_east"], locations_by_region["6b_a-05_east"]),
- "6b_a-06_west": PreRegion("6b_a-06_west", "6b_a-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-06_west"]),
- "6b_a-06_east": PreRegion("6b_a-06_east", "6b_a-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_a-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_a-06_east"]),
+ "6b_a-06_west": PreRegion("6b_a-06_west", "6b_a-06", connections_by_region["6b_a-06_west"], locations_by_region["6b_a-06_west"]),
+ "6b_a-06_east": PreRegion("6b_a-06_east", "6b_a-06", connections_by_region["6b_a-06_east"], locations_by_region["6b_a-06_east"]),
- "6b_b-00_west": PreRegion("6b_b-00_west", "6b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-00_west"]),
- "6b_b-00_east": PreRegion("6b_b-00_east", "6b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-00_east"]),
+ "6b_b-00_west": PreRegion("6b_b-00_west", "6b_b-00", connections_by_region["6b_b-00_west"], locations_by_region["6b_b-00_west"]),
+ "6b_b-00_east": PreRegion("6b_b-00_east", "6b_b-00", connections_by_region["6b_b-00_east"], locations_by_region["6b_b-00_east"]),
- "6b_b-01_top": PreRegion("6b_b-01_top", "6b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-01_top"]),
- "6b_b-01_bottom": PreRegion("6b_b-01_bottom", "6b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-01_bottom"]),
+ "6b_b-01_top": PreRegion("6b_b-01_top", "6b_b-01", connections_by_region["6b_b-01_top"], locations_by_region["6b_b-01_top"]),
+ "6b_b-01_bottom": PreRegion("6b_b-01_bottom", "6b_b-01", connections_by_region["6b_b-01_bottom"], locations_by_region["6b_b-01_bottom"]),
- "6b_b-02_top": PreRegion("6b_b-02_top", "6b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-02_top"]),
- "6b_b-02_bottom": PreRegion("6b_b-02_bottom", "6b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-02_bottom"]),
+ "6b_b-02_top": PreRegion("6b_b-02_top", "6b_b-02", connections_by_region["6b_b-02_top"], locations_by_region["6b_b-02_top"]),
+ "6b_b-02_bottom": PreRegion("6b_b-02_bottom", "6b_b-02", connections_by_region["6b_b-02_bottom"], locations_by_region["6b_b-02_bottom"]),
- "6b_b-03_top": PreRegion("6b_b-03_top", "6b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-03_top"]),
- "6b_b-03_bottom": PreRegion("6b_b-03_bottom", "6b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-03_bottom"]),
+ "6b_b-03_top": PreRegion("6b_b-03_top", "6b_b-03", connections_by_region["6b_b-03_top"], locations_by_region["6b_b-03_top"]),
+ "6b_b-03_bottom": PreRegion("6b_b-03_bottom", "6b_b-03", connections_by_region["6b_b-03_bottom"], locations_by_region["6b_b-03_bottom"]),
- "6b_b-04_top": PreRegion("6b_b-04_top", "6b_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-04_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-04_top"]),
- "6b_b-04_bottom": PreRegion("6b_b-04_bottom", "6b_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-04_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-04_bottom"]),
+ "6b_b-04_top": PreRegion("6b_b-04_top", "6b_b-04", connections_by_region["6b_b-04_top"], locations_by_region["6b_b-04_top"]),
+ "6b_b-04_bottom": PreRegion("6b_b-04_bottom", "6b_b-04", connections_by_region["6b_b-04_bottom"], locations_by_region["6b_b-04_bottom"]),
- "6b_b-05_top": PreRegion("6b_b-05_top", "6b_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-05_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-05_top"]),
- "6b_b-05_bottom": PreRegion("6b_b-05_bottom", "6b_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-05_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-05_bottom"]),
+ "6b_b-05_top": PreRegion("6b_b-05_top", "6b_b-05", connections_by_region["6b_b-05_top"], locations_by_region["6b_b-05_top"]),
+ "6b_b-05_bottom": PreRegion("6b_b-05_bottom", "6b_b-05", connections_by_region["6b_b-05_bottom"], locations_by_region["6b_b-05_bottom"]),
- "6b_b-06_top": PreRegion("6b_b-06_top", "6b_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-06_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-06_top"]),
- "6b_b-06_bottom": PreRegion("6b_b-06_bottom", "6b_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-06_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-06_bottom"]),
+ "6b_b-06_top": PreRegion("6b_b-06_top", "6b_b-06", connections_by_region["6b_b-06_top"], locations_by_region["6b_b-06_top"]),
+ "6b_b-06_bottom": PreRegion("6b_b-06_bottom", "6b_b-06", connections_by_region["6b_b-06_bottom"], locations_by_region["6b_b-06_bottom"]),
- "6b_b-07_top": PreRegion("6b_b-07_top", "6b_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-07_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-07_top"]),
- "6b_b-07_bottom": PreRegion("6b_b-07_bottom", "6b_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-07_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-07_bottom"]),
+ "6b_b-07_top": PreRegion("6b_b-07_top", "6b_b-07", connections_by_region["6b_b-07_top"], locations_by_region["6b_b-07_top"]),
+ "6b_b-07_bottom": PreRegion("6b_b-07_bottom", "6b_b-07", connections_by_region["6b_b-07_bottom"], locations_by_region["6b_b-07_bottom"]),
- "6b_b-08_top": PreRegion("6b_b-08_top", "6b_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-08_top"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-08_top"]),
- "6b_b-08_bottom": PreRegion("6b_b-08_bottom", "6b_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-08_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-08_bottom"]),
+ "6b_b-08_top": PreRegion("6b_b-08_top", "6b_b-08", connections_by_region["6b_b-08_top"], locations_by_region["6b_b-08_top"]),
+ "6b_b-08_bottom": PreRegion("6b_b-08_bottom", "6b_b-08", connections_by_region["6b_b-08_bottom"], locations_by_region["6b_b-08_bottom"]),
- "6b_b-10_west": PreRegion("6b_b-10_west", "6b_b-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-10_west"]),
- "6b_b-10_east": PreRegion("6b_b-10_east", "6b_b-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_b-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_b-10_east"]),
+ "6b_b-10_west": PreRegion("6b_b-10_west", "6b_b-10", connections_by_region["6b_b-10_west"], locations_by_region["6b_b-10_west"]),
+ "6b_b-10_east": PreRegion("6b_b-10_east", "6b_b-10", connections_by_region["6b_b-10_east"], locations_by_region["6b_b-10_east"]),
- "6b_c-00_west": PreRegion("6b_c-00_west", "6b_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-00_west"]),
- "6b_c-00_east": PreRegion("6b_c-00_east", "6b_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-00_east"]),
+ "6b_c-00_west": PreRegion("6b_c-00_west", "6b_c-00", connections_by_region["6b_c-00_west"], locations_by_region["6b_c-00_west"]),
+ "6b_c-00_east": PreRegion("6b_c-00_east", "6b_c-00", connections_by_region["6b_c-00_east"], locations_by_region["6b_c-00_east"]),
- "6b_c-01_west": PreRegion("6b_c-01_west", "6b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-01_west"]),
- "6b_c-01_east": PreRegion("6b_c-01_east", "6b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-01_east"]),
+ "6b_c-01_west": PreRegion("6b_c-01_west", "6b_c-01", connections_by_region["6b_c-01_west"], locations_by_region["6b_c-01_west"]),
+ "6b_c-01_east": PreRegion("6b_c-01_east", "6b_c-01", connections_by_region["6b_c-01_east"], locations_by_region["6b_c-01_east"]),
- "6b_c-02_west": PreRegion("6b_c-02_west", "6b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-02_west"]),
- "6b_c-02_east": PreRegion("6b_c-02_east", "6b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-02_east"]),
+ "6b_c-02_west": PreRegion("6b_c-02_west", "6b_c-02", connections_by_region["6b_c-02_west"], locations_by_region["6b_c-02_west"]),
+ "6b_c-02_east": PreRegion("6b_c-02_east", "6b_c-02", connections_by_region["6b_c-02_east"], locations_by_region["6b_c-02_east"]),
- "6b_c-03_west": PreRegion("6b_c-03_west", "6b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-03_west"]),
- "6b_c-03_east": PreRegion("6b_c-03_east", "6b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-03_east"]),
+ "6b_c-03_west": PreRegion("6b_c-03_west", "6b_c-03", connections_by_region["6b_c-03_west"], locations_by_region["6b_c-03_west"]),
+ "6b_c-03_east": PreRegion("6b_c-03_east", "6b_c-03", connections_by_region["6b_c-03_east"], locations_by_region["6b_c-03_east"]),
- "6b_c-04_west": PreRegion("6b_c-04_west", "6b_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-04_west"]),
- "6b_c-04_east": PreRegion("6b_c-04_east", "6b_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_c-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_c-04_east"]),
+ "6b_c-04_west": PreRegion("6b_c-04_west", "6b_c-04", connections_by_region["6b_c-04_west"], locations_by_region["6b_c-04_west"]),
+ "6b_c-04_east": PreRegion("6b_c-04_east", "6b_c-04", connections_by_region["6b_c-04_east"], locations_by_region["6b_c-04_east"]),
- "6b_d-00_west": PreRegion("6b_d-00_west", "6b_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-00_west"]),
- "6b_d-00_east": PreRegion("6b_d-00_east", "6b_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-00_east"]),
+ "6b_d-00_west": PreRegion("6b_d-00_west", "6b_d-00", connections_by_region["6b_d-00_west"], locations_by_region["6b_d-00_west"]),
+ "6b_d-00_east": PreRegion("6b_d-00_east", "6b_d-00", connections_by_region["6b_d-00_east"], locations_by_region["6b_d-00_east"]),
- "6b_d-01_west": PreRegion("6b_d-01_west", "6b_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-01_west"]),
- "6b_d-01_east": PreRegion("6b_d-01_east", "6b_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-01_east"]),
+ "6b_d-01_west": PreRegion("6b_d-01_west", "6b_d-01", connections_by_region["6b_d-01_west"], locations_by_region["6b_d-01_west"]),
+ "6b_d-01_east": PreRegion("6b_d-01_east", "6b_d-01", connections_by_region["6b_d-01_east"], locations_by_region["6b_d-01_east"]),
- "6b_d-02_west": PreRegion("6b_d-02_west", "6b_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-02_west"]),
- "6b_d-02_east": PreRegion("6b_d-02_east", "6b_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-02_east"]),
+ "6b_d-02_west": PreRegion("6b_d-02_west", "6b_d-02", connections_by_region["6b_d-02_west"], locations_by_region["6b_d-02_west"]),
+ "6b_d-02_east": PreRegion("6b_d-02_east", "6b_d-02", connections_by_region["6b_d-02_east"], locations_by_region["6b_d-02_east"]),
- "6b_d-03_west": PreRegion("6b_d-03_west", "6b_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-03_west"]),
- "6b_d-03_east": PreRegion("6b_d-03_east", "6b_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-03_east"]),
+ "6b_d-03_west": PreRegion("6b_d-03_west", "6b_d-03", connections_by_region["6b_d-03_west"], locations_by_region["6b_d-03_west"]),
+ "6b_d-03_east": PreRegion("6b_d-03_east", "6b_d-03", connections_by_region["6b_d-03_east"], locations_by_region["6b_d-03_east"]),
- "6b_d-04_west": PreRegion("6b_d-04_west", "6b_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-04_west"]),
- "6b_d-04_east": PreRegion("6b_d-04_east", "6b_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-04_east"]),
+ "6b_d-04_west": PreRegion("6b_d-04_west", "6b_d-04", connections_by_region["6b_d-04_west"], locations_by_region["6b_d-04_west"]),
+ "6b_d-04_east": PreRegion("6b_d-04_east", "6b_d-04", connections_by_region["6b_d-04_east"], locations_by_region["6b_d-04_east"]),
- "6b_d-05_west": PreRegion("6b_d-05_west", "6b_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-05_west"]),
- "6b_d-05_goal": PreRegion("6b_d-05_goal", "6b_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6b_d-05_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "6b_d-05_goal"]),
+ "6b_d-05_west": PreRegion("6b_d-05_west", "6b_d-05", connections_by_region["6b_d-05_west"], locations_by_region["6b_d-05_west"]),
+ "6b_d-05_goal": PreRegion("6b_d-05_goal", "6b_d-05", connections_by_region["6b_d-05_goal"], locations_by_region["6b_d-05_goal"]),
- "6c_00_west": PreRegion("6c_00_west", "6c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6c_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6c_00_west"]),
- "6c_00_east": PreRegion("6c_00_east", "6c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6c_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6c_00_east"]),
+ "6c_00_west": PreRegion("6c_00_west", "6c_00", connections_by_region["6c_00_west"], locations_by_region["6c_00_west"]),
+ "6c_00_east": PreRegion("6c_00_east", "6c_00", connections_by_region["6c_00_east"], locations_by_region["6c_00_east"]),
- "6c_01_west": PreRegion("6c_01_west", "6c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6c_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6c_01_west"]),
- "6c_01_east": PreRegion("6c_01_east", "6c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6c_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "6c_01_east"]),
+ "6c_01_west": PreRegion("6c_01_west", "6c_01", connections_by_region["6c_01_west"], locations_by_region["6c_01_west"]),
+ "6c_01_east": PreRegion("6c_01_east", "6c_01", connections_by_region["6c_01_east"], locations_by_region["6c_01_east"]),
- "6c_02_west": PreRegion("6c_02_west", "6c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6c_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "6c_02_west"]),
- "6c_02_goal": PreRegion("6c_02_goal", "6c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "6c_02_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "6c_02_goal"]),
+ "6c_02_west": PreRegion("6c_02_west", "6c_02", connections_by_region["6c_02_west"], locations_by_region["6c_02_west"]),
+ "6c_02_goal": PreRegion("6c_02_goal", "6c_02", connections_by_region["6c_02_goal"], locations_by_region["6c_02_goal"]),
- "7a_a-00_west": PreRegion("7a_a-00_west", "7a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-00_west"]),
- "7a_a-00_east": PreRegion("7a_a-00_east", "7a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-00_east"]),
+ "7a_a-00_west": PreRegion("7a_a-00_west", "7a_a-00", connections_by_region["7a_a-00_west"], locations_by_region["7a_a-00_west"]),
+ "7a_a-00_east": PreRegion("7a_a-00_east", "7a_a-00", connections_by_region["7a_a-00_east"], locations_by_region["7a_a-00_east"]),
- "7a_a-01_west": PreRegion("7a_a-01_west", "7a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-01_west"]),
- "7a_a-01_east": PreRegion("7a_a-01_east", "7a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-01_east"]),
+ "7a_a-01_west": PreRegion("7a_a-01_west", "7a_a-01", connections_by_region["7a_a-01_west"], locations_by_region["7a_a-01_west"]),
+ "7a_a-01_east": PreRegion("7a_a-01_east", "7a_a-01", connections_by_region["7a_a-01_east"], locations_by_region["7a_a-01_east"]),
- "7a_a-02_west": PreRegion("7a_a-02_west", "7a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-02_west"]),
- "7a_a-02_east": PreRegion("7a_a-02_east", "7a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-02_east"]),
- "7a_a-02_north": PreRegion("7a_a-02_north", "7a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-02_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-02_north"]),
- "7a_a-02_north-west": PreRegion("7a_a-02_north-west", "7a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-02_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-02_north-west"]),
+ "7a_a-02_west": PreRegion("7a_a-02_west", "7a_a-02", connections_by_region["7a_a-02_west"], locations_by_region["7a_a-02_west"]),
+ "7a_a-02_east": PreRegion("7a_a-02_east", "7a_a-02", connections_by_region["7a_a-02_east"], locations_by_region["7a_a-02_east"]),
+ "7a_a-02_north": PreRegion("7a_a-02_north", "7a_a-02", connections_by_region["7a_a-02_north"], locations_by_region["7a_a-02_north"]),
+ "7a_a-02_north-west": PreRegion("7a_a-02_north-west", "7a_a-02", connections_by_region["7a_a-02_north-west"], locations_by_region["7a_a-02_north-west"]),
- "7a_a-02b_east": PreRegion("7a_a-02b_east", "7a_a-02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-02b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-02b_east"]),
- "7a_a-02b_west": PreRegion("7a_a-02b_west", "7a_a-02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-02b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-02b_west"]),
+ "7a_a-02b_east": PreRegion("7a_a-02b_east", "7a_a-02b", connections_by_region["7a_a-02b_east"], locations_by_region["7a_a-02b_east"]),
+ "7a_a-02b_west": PreRegion("7a_a-02b_west", "7a_a-02b", connections_by_region["7a_a-02b_west"], locations_by_region["7a_a-02b_west"]),
- "7a_a-03_west": PreRegion("7a_a-03_west", "7a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-03_west"]),
- "7a_a-03_east": PreRegion("7a_a-03_east", "7a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-03_east"]),
+ "7a_a-03_west": PreRegion("7a_a-03_west", "7a_a-03", connections_by_region["7a_a-03_west"], locations_by_region["7a_a-03_west"]),
+ "7a_a-03_east": PreRegion("7a_a-03_east", "7a_a-03", connections_by_region["7a_a-03_east"], locations_by_region["7a_a-03_east"]),
- "7a_a-04_west": PreRegion("7a_a-04_west", "7a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-04_west"]),
- "7a_a-04_north": PreRegion("7a_a-04_north", "7a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-04_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-04_north"]),
- "7a_a-04_east": PreRegion("7a_a-04_east", "7a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-04_east"]),
+ "7a_a-04_west": PreRegion("7a_a-04_west", "7a_a-04", connections_by_region["7a_a-04_west"], locations_by_region["7a_a-04_west"]),
+ "7a_a-04_north": PreRegion("7a_a-04_north", "7a_a-04", connections_by_region["7a_a-04_north"], locations_by_region["7a_a-04_north"]),
+ "7a_a-04_east": PreRegion("7a_a-04_east", "7a_a-04", connections_by_region["7a_a-04_east"], locations_by_region["7a_a-04_east"]),
- "7a_a-04b_east": PreRegion("7a_a-04b_east", "7a_a-04b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-04b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-04b_east"]),
+ "7a_a-04b_east": PreRegion("7a_a-04b_east", "7a_a-04b", connections_by_region["7a_a-04b_east"], locations_by_region["7a_a-04b_east"]),
- "7a_a-05_west": PreRegion("7a_a-05_west", "7a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-05_west"]),
- "7a_a-05_east": PreRegion("7a_a-05_east", "7a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-05_east"]),
+ "7a_a-05_west": PreRegion("7a_a-05_west", "7a_a-05", connections_by_region["7a_a-05_west"], locations_by_region["7a_a-05_west"]),
+ "7a_a-05_east": PreRegion("7a_a-05_east", "7a_a-05", connections_by_region["7a_a-05_east"], locations_by_region["7a_a-05_east"]),
- "7a_a-06_bottom": PreRegion("7a_a-06_bottom", "7a_a-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-06_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-06_bottom"]),
- "7a_a-06_top": PreRegion("7a_a-06_top", "7a_a-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-06_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-06_top"]),
- "7a_a-06_top-side": PreRegion("7a_a-06_top-side", "7a_a-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_a-06_top-side"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_a-06_top-side"]),
+ "7a_a-06_bottom": PreRegion("7a_a-06_bottom", "7a_a-06", connections_by_region["7a_a-06_bottom"], locations_by_region["7a_a-06_bottom"]),
+ "7a_a-06_top": PreRegion("7a_a-06_top", "7a_a-06", connections_by_region["7a_a-06_top"], locations_by_region["7a_a-06_top"]),
+ "7a_a-06_top-side": PreRegion("7a_a-06_top-side", "7a_a-06", connections_by_region["7a_a-06_top-side"], locations_by_region["7a_a-06_top-side"]),
- "7a_b-00_bottom": PreRegion("7a_b-00_bottom", "7a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-00_bottom"]),
- "7a_b-00_top": PreRegion("7a_b-00_top", "7a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-00_top"]),
+ "7a_b-00_bottom": PreRegion("7a_b-00_bottom", "7a_b-00", connections_by_region["7a_b-00_bottom"], locations_by_region["7a_b-00_bottom"]),
+ "7a_b-00_top": PreRegion("7a_b-00_top", "7a_b-00", connections_by_region["7a_b-00_top"], locations_by_region["7a_b-00_top"]),
- "7a_b-01_west": PreRegion("7a_b-01_west", "7a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-01_west"]),
- "7a_b-01_east": PreRegion("7a_b-01_east", "7a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-01_east"]),
+ "7a_b-01_west": PreRegion("7a_b-01_west", "7a_b-01", connections_by_region["7a_b-01_west"], locations_by_region["7a_b-01_west"]),
+ "7a_b-01_east": PreRegion("7a_b-01_east", "7a_b-01", connections_by_region["7a_b-01_east"], locations_by_region["7a_b-01_east"]),
- "7a_b-02_south": PreRegion("7a_b-02_south", "7a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02_south"]),
- "7a_b-02_north-west": PreRegion("7a_b-02_north-west", "7a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02_north-west"]),
- "7a_b-02_north": PreRegion("7a_b-02_north", "7a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02_north"]),
- "7a_b-02_north-east": PreRegion("7a_b-02_north-east", "7a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02_north-east"]),
+ "7a_b-02_south": PreRegion("7a_b-02_south", "7a_b-02", connections_by_region["7a_b-02_south"], locations_by_region["7a_b-02_south"]),
+ "7a_b-02_north-west": PreRegion("7a_b-02_north-west", "7a_b-02", connections_by_region["7a_b-02_north-west"], locations_by_region["7a_b-02_north-west"]),
+ "7a_b-02_north": PreRegion("7a_b-02_north", "7a_b-02", connections_by_region["7a_b-02_north"], locations_by_region["7a_b-02_north"]),
+ "7a_b-02_north-east": PreRegion("7a_b-02_north-east", "7a_b-02", connections_by_region["7a_b-02_north-east"], locations_by_region["7a_b-02_north-east"]),
- "7a_b-02b_south": PreRegion("7a_b-02b_south", "7a_b-02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02b_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02b_south"]),
- "7a_b-02b_north-west": PreRegion("7a_b-02b_north-west", "7a_b-02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02b_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02b_north-west"]),
- "7a_b-02b_north-east": PreRegion("7a_b-02b_north-east", "7a_b-02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02b_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02b_north-east"]),
+ "7a_b-02b_south": PreRegion("7a_b-02b_south", "7a_b-02b", connections_by_region["7a_b-02b_south"], locations_by_region["7a_b-02b_south"]),
+ "7a_b-02b_north-west": PreRegion("7a_b-02b_north-west", "7a_b-02b", connections_by_region["7a_b-02b_north-west"], locations_by_region["7a_b-02b_north-west"]),
+ "7a_b-02b_north-east": PreRegion("7a_b-02b_north-east", "7a_b-02b", connections_by_region["7a_b-02b_north-east"], locations_by_region["7a_b-02b_north-east"]),
- "7a_b-02e_east": PreRegion("7a_b-02e_east", "7a_b-02e", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02e_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02e_east"]),
+ "7a_b-02e_east": PreRegion("7a_b-02e_east", "7a_b-02e", connections_by_region["7a_b-02e_east"], locations_by_region["7a_b-02e_east"]),
- "7a_b-02c_west": PreRegion("7a_b-02c_west", "7a_b-02c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02c_west"]),
- "7a_b-02c_east": PreRegion("7a_b-02c_east", "7a_b-02c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02c_east"]),
- "7a_b-02c_south-east": PreRegion("7a_b-02c_south-east", "7a_b-02c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02c_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02c_south-east"]),
+ "7a_b-02c_west": PreRegion("7a_b-02c_west", "7a_b-02c", connections_by_region["7a_b-02c_west"], locations_by_region["7a_b-02c_west"]),
+ "7a_b-02c_east": PreRegion("7a_b-02c_east", "7a_b-02c", connections_by_region["7a_b-02c_east"], locations_by_region["7a_b-02c_east"]),
+ "7a_b-02c_south-east": PreRegion("7a_b-02c_south-east", "7a_b-02c", connections_by_region["7a_b-02c_south-east"], locations_by_region["7a_b-02c_south-east"]),
- "7a_b-02d_north": PreRegion("7a_b-02d_north", "7a_b-02d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02d_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02d_north"]),
- "7a_b-02d_south": PreRegion("7a_b-02d_south", "7a_b-02d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-02d_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-02d_south"]),
+ "7a_b-02d_north": PreRegion("7a_b-02d_north", "7a_b-02d", connections_by_region["7a_b-02d_north"], locations_by_region["7a_b-02d_north"]),
+ "7a_b-02d_south": PreRegion("7a_b-02d_south", "7a_b-02d", connections_by_region["7a_b-02d_south"], locations_by_region["7a_b-02d_south"]),
- "7a_b-03_west": PreRegion("7a_b-03_west", "7a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-03_west"]),
- "7a_b-03_east": PreRegion("7a_b-03_east", "7a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-03_east"]),
- "7a_b-03_north": PreRegion("7a_b-03_north", "7a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-03_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-03_north"]),
+ "7a_b-03_west": PreRegion("7a_b-03_west", "7a_b-03", connections_by_region["7a_b-03_west"], locations_by_region["7a_b-03_west"]),
+ "7a_b-03_east": PreRegion("7a_b-03_east", "7a_b-03", connections_by_region["7a_b-03_east"], locations_by_region["7a_b-03_east"]),
+ "7a_b-03_north": PreRegion("7a_b-03_north", "7a_b-03", connections_by_region["7a_b-03_north"], locations_by_region["7a_b-03_north"]),
- "7a_b-04_west": PreRegion("7a_b-04_west", "7a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-04_west"]),
+ "7a_b-04_west": PreRegion("7a_b-04_west", "7a_b-04", connections_by_region["7a_b-04_west"], locations_by_region["7a_b-04_west"]),
- "7a_b-05_west": PreRegion("7a_b-05_west", "7a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-05_west"]),
- "7a_b-05_east": PreRegion("7a_b-05_east", "7a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-05_east"]),
- "7a_b-05_north-west": PreRegion("7a_b-05_north-west", "7a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-05_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-05_north-west"]),
+ "7a_b-05_west": PreRegion("7a_b-05_west", "7a_b-05", connections_by_region["7a_b-05_west"], locations_by_region["7a_b-05_west"]),
+ "7a_b-05_east": PreRegion("7a_b-05_east", "7a_b-05", connections_by_region["7a_b-05_east"], locations_by_region["7a_b-05_east"]),
+ "7a_b-05_north-west": PreRegion("7a_b-05_north-west", "7a_b-05", connections_by_region["7a_b-05_north-west"], locations_by_region["7a_b-05_north-west"]),
- "7a_b-06_west": PreRegion("7a_b-06_west", "7a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-06_west"]),
- "7a_b-06_east": PreRegion("7a_b-06_east", "7a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-06_east"]),
+ "7a_b-06_west": PreRegion("7a_b-06_west", "7a_b-06", connections_by_region["7a_b-06_west"], locations_by_region["7a_b-06_west"]),
+ "7a_b-06_east": PreRegion("7a_b-06_east", "7a_b-06", connections_by_region["7a_b-06_east"], locations_by_region["7a_b-06_east"]),
- "7a_b-07_west": PreRegion("7a_b-07_west", "7a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-07_west"]),
- "7a_b-07_east": PreRegion("7a_b-07_east", "7a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-07_east"]),
+ "7a_b-07_west": PreRegion("7a_b-07_west", "7a_b-07", connections_by_region["7a_b-07_west"], locations_by_region["7a_b-07_west"]),
+ "7a_b-07_east": PreRegion("7a_b-07_east", "7a_b-07", connections_by_region["7a_b-07_east"], locations_by_region["7a_b-07_east"]),
- "7a_b-08_west": PreRegion("7a_b-08_west", "7a_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-08_west"]),
- "7a_b-08_east": PreRegion("7a_b-08_east", "7a_b-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-08_east"]),
+ "7a_b-08_west": PreRegion("7a_b-08_west", "7a_b-08", connections_by_region["7a_b-08_west"], locations_by_region["7a_b-08_west"]),
+ "7a_b-08_east": PreRegion("7a_b-08_east", "7a_b-08", connections_by_region["7a_b-08_east"], locations_by_region["7a_b-08_east"]),
- "7a_b-09_bottom": PreRegion("7a_b-09_bottom", "7a_b-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-09_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-09_bottom"]),
- "7a_b-09_top": PreRegion("7a_b-09_top", "7a_b-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-09_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-09_top"]),
- "7a_b-09_top-side": PreRegion("7a_b-09_top-side", "7a_b-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_b-09_top-side"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_b-09_top-side"]),
+ "7a_b-09_bottom": PreRegion("7a_b-09_bottom", "7a_b-09", connections_by_region["7a_b-09_bottom"], locations_by_region["7a_b-09_bottom"]),
+ "7a_b-09_top": PreRegion("7a_b-09_top", "7a_b-09", connections_by_region["7a_b-09_top"], locations_by_region["7a_b-09_top"]),
+ "7a_b-09_top-side": PreRegion("7a_b-09_top-side", "7a_b-09", connections_by_region["7a_b-09_top-side"], locations_by_region["7a_b-09_top-side"]),
- "7a_c-00_west": PreRegion("7a_c-00_west", "7a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-00_west"]),
- "7a_c-00_east": PreRegion("7a_c-00_east", "7a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-00_east"]),
+ "7a_c-00_west": PreRegion("7a_c-00_west", "7a_c-00", connections_by_region["7a_c-00_west"], locations_by_region["7a_c-00_west"]),
+ "7a_c-00_east": PreRegion("7a_c-00_east", "7a_c-00", connections_by_region["7a_c-00_east"], locations_by_region["7a_c-00_east"]),
- "7a_c-01_bottom": PreRegion("7a_c-01_bottom", "7a_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-01_bottom"]),
- "7a_c-01_top": PreRegion("7a_c-01_top", "7a_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-01_top"]),
+ "7a_c-01_bottom": PreRegion("7a_c-01_bottom", "7a_c-01", connections_by_region["7a_c-01_bottom"], locations_by_region["7a_c-01_bottom"]),
+ "7a_c-01_top": PreRegion("7a_c-01_top", "7a_c-01", connections_by_region["7a_c-01_top"], locations_by_region["7a_c-01_top"]),
- "7a_c-02_bottom": PreRegion("7a_c-02_bottom", "7a_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-02_bottom"]),
- "7a_c-02_top": PreRegion("7a_c-02_top", "7a_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-02_top"]),
+ "7a_c-02_bottom": PreRegion("7a_c-02_bottom", "7a_c-02", connections_by_region["7a_c-02_bottom"], locations_by_region["7a_c-02_bottom"]),
+ "7a_c-02_top": PreRegion("7a_c-02_top", "7a_c-02", connections_by_region["7a_c-02_top"], locations_by_region["7a_c-02_top"]),
- "7a_c-03_south": PreRegion("7a_c-03_south", "7a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-03_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-03_south"]),
- "7a_c-03_west": PreRegion("7a_c-03_west", "7a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-03_west"]),
- "7a_c-03_east": PreRegion("7a_c-03_east", "7a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-03_east"]),
+ "7a_c-03_south": PreRegion("7a_c-03_south", "7a_c-03", connections_by_region["7a_c-03_south"], locations_by_region["7a_c-03_south"]),
+ "7a_c-03_west": PreRegion("7a_c-03_west", "7a_c-03", connections_by_region["7a_c-03_west"], locations_by_region["7a_c-03_west"]),
+ "7a_c-03_east": PreRegion("7a_c-03_east", "7a_c-03", connections_by_region["7a_c-03_east"], locations_by_region["7a_c-03_east"]),
- "7a_c-03b_east": PreRegion("7a_c-03b_east", "7a_c-03b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-03b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-03b_east"]),
+ "7a_c-03b_east": PreRegion("7a_c-03b_east", "7a_c-03b", connections_by_region["7a_c-03b_east"], locations_by_region["7a_c-03b_east"]),
- "7a_c-04_west": PreRegion("7a_c-04_west", "7a_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-04_west"]),
- "7a_c-04_north-west": PreRegion("7a_c-04_north-west", "7a_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-04_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-04_north-west"]),
- "7a_c-04_north-east": PreRegion("7a_c-04_north-east", "7a_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-04_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-04_north-east"]),
- "7a_c-04_east": PreRegion("7a_c-04_east", "7a_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-04_east"]),
+ "7a_c-04_west": PreRegion("7a_c-04_west", "7a_c-04", connections_by_region["7a_c-04_west"], locations_by_region["7a_c-04_west"]),
+ "7a_c-04_north-west": PreRegion("7a_c-04_north-west", "7a_c-04", connections_by_region["7a_c-04_north-west"], locations_by_region["7a_c-04_north-west"]),
+ "7a_c-04_north-east": PreRegion("7a_c-04_north-east", "7a_c-04", connections_by_region["7a_c-04_north-east"], locations_by_region["7a_c-04_north-east"]),
+ "7a_c-04_east": PreRegion("7a_c-04_east", "7a_c-04", connections_by_region["7a_c-04_east"], locations_by_region["7a_c-04_east"]),
- "7a_c-05_west": PreRegion("7a_c-05_west", "7a_c-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-05_west"]),
+ "7a_c-05_west": PreRegion("7a_c-05_west", "7a_c-05", connections_by_region["7a_c-05_west"], locations_by_region["7a_c-05_west"]),
- "7a_c-06_south": PreRegion("7a_c-06_south", "7a_c-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-06_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-06_south"]),
- "7a_c-06_north": PreRegion("7a_c-06_north", "7a_c-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-06_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-06_north"]),
- "7a_c-06_east": PreRegion("7a_c-06_east", "7a_c-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-06_east"]),
+ "7a_c-06_south": PreRegion("7a_c-06_south", "7a_c-06", connections_by_region["7a_c-06_south"], locations_by_region["7a_c-06_south"]),
+ "7a_c-06_north": PreRegion("7a_c-06_north", "7a_c-06", connections_by_region["7a_c-06_north"], locations_by_region["7a_c-06_north"]),
+ "7a_c-06_east": PreRegion("7a_c-06_east", "7a_c-06", connections_by_region["7a_c-06_east"], locations_by_region["7a_c-06_east"]),
- "7a_c-06b_south": PreRegion("7a_c-06b_south", "7a_c-06b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-06b_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-06b_south"]),
- "7a_c-06b_north": PreRegion("7a_c-06b_north", "7a_c-06b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-06b_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-06b_north"]),
- "7a_c-06b_west": PreRegion("7a_c-06b_west", "7a_c-06b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-06b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-06b_west"]),
- "7a_c-06b_east": PreRegion("7a_c-06b_east", "7a_c-06b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-06b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-06b_east"]),
+ "7a_c-06b_south": PreRegion("7a_c-06b_south", "7a_c-06b", connections_by_region["7a_c-06b_south"], locations_by_region["7a_c-06b_south"]),
+ "7a_c-06b_north": PreRegion("7a_c-06b_north", "7a_c-06b", connections_by_region["7a_c-06b_north"], locations_by_region["7a_c-06b_north"]),
+ "7a_c-06b_west": PreRegion("7a_c-06b_west", "7a_c-06b", connections_by_region["7a_c-06b_west"], locations_by_region["7a_c-06b_west"]),
+ "7a_c-06b_east": PreRegion("7a_c-06b_east", "7a_c-06b", connections_by_region["7a_c-06b_east"], locations_by_region["7a_c-06b_east"]),
- "7a_c-06c_west": PreRegion("7a_c-06c_west", "7a_c-06c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-06c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-06c_west"]),
+ "7a_c-06c_west": PreRegion("7a_c-06c_west", "7a_c-06c", connections_by_region["7a_c-06c_west"], locations_by_region["7a_c-06c_west"]),
- "7a_c-07_west": PreRegion("7a_c-07_west", "7a_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-07_west"]),
- "7a_c-07_south-west": PreRegion("7a_c-07_south-west", "7a_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-07_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-07_south-west"]),
- "7a_c-07_south-east": PreRegion("7a_c-07_south-east", "7a_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-07_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-07_south-east"]),
- "7a_c-07_east": PreRegion("7a_c-07_east", "7a_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-07_east"]),
+ "7a_c-07_west": PreRegion("7a_c-07_west", "7a_c-07", connections_by_region["7a_c-07_west"], locations_by_region["7a_c-07_west"]),
+ "7a_c-07_south-west": PreRegion("7a_c-07_south-west", "7a_c-07", connections_by_region["7a_c-07_south-west"], locations_by_region["7a_c-07_south-west"]),
+ "7a_c-07_south-east": PreRegion("7a_c-07_south-east", "7a_c-07", connections_by_region["7a_c-07_south-east"], locations_by_region["7a_c-07_south-east"]),
+ "7a_c-07_east": PreRegion("7a_c-07_east", "7a_c-07", connections_by_region["7a_c-07_east"], locations_by_region["7a_c-07_east"]),
- "7a_c-07b_east": PreRegion("7a_c-07b_east", "7a_c-07b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-07b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-07b_east"]),
+ "7a_c-07b_east": PreRegion("7a_c-07b_east", "7a_c-07b", connections_by_region["7a_c-07b_east"], locations_by_region["7a_c-07b_east"]),
- "7a_c-08_west": PreRegion("7a_c-08_west", "7a_c-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-08_west"]),
- "7a_c-08_east": PreRegion("7a_c-08_east", "7a_c-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-08_east"]),
+ "7a_c-08_west": PreRegion("7a_c-08_west", "7a_c-08", connections_by_region["7a_c-08_west"], locations_by_region["7a_c-08_west"]),
+ "7a_c-08_east": PreRegion("7a_c-08_east", "7a_c-08", connections_by_region["7a_c-08_east"], locations_by_region["7a_c-08_east"]),
- "7a_c-09_bottom": PreRegion("7a_c-09_bottom", "7a_c-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-09_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-09_bottom"]),
- "7a_c-09_top": PreRegion("7a_c-09_top", "7a_c-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_c-09_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_c-09_top"]),
+ "7a_c-09_bottom": PreRegion("7a_c-09_bottom", "7a_c-09", connections_by_region["7a_c-09_bottom"], locations_by_region["7a_c-09_bottom"]),
+ "7a_c-09_top": PreRegion("7a_c-09_top", "7a_c-09", connections_by_region["7a_c-09_top"], locations_by_region["7a_c-09_top"]),
- "7a_d-00_bottom": PreRegion("7a_d-00_bottom", "7a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-00_bottom"]),
- "7a_d-00_top": PreRegion("7a_d-00_top", "7a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-00_top"]),
+ "7a_d-00_bottom": PreRegion("7a_d-00_bottom", "7a_d-00", connections_by_region["7a_d-00_bottom"], locations_by_region["7a_d-00_bottom"]),
+ "7a_d-00_top": PreRegion("7a_d-00_top", "7a_d-00", connections_by_region["7a_d-00_top"], locations_by_region["7a_d-00_top"]),
- "7a_d-01_west": PreRegion("7a_d-01_west", "7a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01_west"]),
- "7a_d-01_east": PreRegion("7a_d-01_east", "7a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01_east"]),
+ "7a_d-01_west": PreRegion("7a_d-01_west", "7a_d-01", connections_by_region["7a_d-01_west"], locations_by_region["7a_d-01_west"]),
+ "7a_d-01_east": PreRegion("7a_d-01_east", "7a_d-01", connections_by_region["7a_d-01_east"], locations_by_region["7a_d-01_east"]),
- "7a_d-01b_west": PreRegion("7a_d-01b_west", "7a_d-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01b_west"]),
- "7a_d-01b_south-west": PreRegion("7a_d-01b_south-west", "7a_d-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01b_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01b_south-west"]),
- "7a_d-01b_east": PreRegion("7a_d-01b_east", "7a_d-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01b_east"]),
- "7a_d-01b_south-east": PreRegion("7a_d-01b_south-east", "7a_d-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01b_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01b_south-east"]),
+ "7a_d-01b_west": PreRegion("7a_d-01b_west", "7a_d-01b", connections_by_region["7a_d-01b_west"], locations_by_region["7a_d-01b_west"]),
+ "7a_d-01b_south-west": PreRegion("7a_d-01b_south-west", "7a_d-01b", connections_by_region["7a_d-01b_south-west"], locations_by_region["7a_d-01b_south-west"]),
+ "7a_d-01b_east": PreRegion("7a_d-01b_east", "7a_d-01b", connections_by_region["7a_d-01b_east"], locations_by_region["7a_d-01b_east"]),
+ "7a_d-01b_south-east": PreRegion("7a_d-01b_south-east", "7a_d-01b", connections_by_region["7a_d-01b_south-east"], locations_by_region["7a_d-01b_south-east"]),
- "7a_d-01c_west": PreRegion("7a_d-01c_west", "7a_d-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01c_west"]),
- "7a_d-01c_south": PreRegion("7a_d-01c_south", "7a_d-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01c_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01c_south"]),
- "7a_d-01c_east": PreRegion("7a_d-01c_east", "7a_d-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01c_east"]),
- "7a_d-01c_south-east": PreRegion("7a_d-01c_south-east", "7a_d-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01c_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01c_south-east"]),
+ "7a_d-01c_west": PreRegion("7a_d-01c_west", "7a_d-01c", connections_by_region["7a_d-01c_west"], locations_by_region["7a_d-01c_west"]),
+ "7a_d-01c_south": PreRegion("7a_d-01c_south", "7a_d-01c", connections_by_region["7a_d-01c_south"], locations_by_region["7a_d-01c_south"]),
+ "7a_d-01c_east": PreRegion("7a_d-01c_east", "7a_d-01c", connections_by_region["7a_d-01c_east"], locations_by_region["7a_d-01c_east"]),
+ "7a_d-01c_south-east": PreRegion("7a_d-01c_south-east", "7a_d-01c", connections_by_region["7a_d-01c_south-east"], locations_by_region["7a_d-01c_south-east"]),
- "7a_d-01d_west": PreRegion("7a_d-01d_west", "7a_d-01d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01d_west"]),
- "7a_d-01d_east": PreRegion("7a_d-01d_east", "7a_d-01d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-01d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-01d_east"]),
+ "7a_d-01d_west": PreRegion("7a_d-01d_west", "7a_d-01d", connections_by_region["7a_d-01d_west"], locations_by_region["7a_d-01d_west"]),
+ "7a_d-01d_east": PreRegion("7a_d-01d_east", "7a_d-01d", connections_by_region["7a_d-01d_east"], locations_by_region["7a_d-01d_east"]),
- "7a_d-02_west": PreRegion("7a_d-02_west", "7a_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-02_west"]),
- "7a_d-02_east": PreRegion("7a_d-02_east", "7a_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-02_east"]),
+ "7a_d-02_west": PreRegion("7a_d-02_west", "7a_d-02", connections_by_region["7a_d-02_west"], locations_by_region["7a_d-02_west"]),
+ "7a_d-02_east": PreRegion("7a_d-02_east", "7a_d-02", connections_by_region["7a_d-02_east"], locations_by_region["7a_d-02_east"]),
- "7a_d-03_west": PreRegion("7a_d-03_west", "7a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-03_west"]),
- "7a_d-03_north-west": PreRegion("7a_d-03_north-west", "7a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-03_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-03_north-west"]),
- "7a_d-03_east": PreRegion("7a_d-03_east", "7a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-03_east"]),
- "7a_d-03_north-east": PreRegion("7a_d-03_north-east", "7a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-03_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-03_north-east"]),
+ "7a_d-03_west": PreRegion("7a_d-03_west", "7a_d-03", connections_by_region["7a_d-03_west"], locations_by_region["7a_d-03_west"]),
+ "7a_d-03_north-west": PreRegion("7a_d-03_north-west", "7a_d-03", connections_by_region["7a_d-03_north-west"], locations_by_region["7a_d-03_north-west"]),
+ "7a_d-03_east": PreRegion("7a_d-03_east", "7a_d-03", connections_by_region["7a_d-03_east"], locations_by_region["7a_d-03_east"]),
+ "7a_d-03_north-east": PreRegion("7a_d-03_north-east", "7a_d-03", connections_by_region["7a_d-03_north-east"], locations_by_region["7a_d-03_north-east"]),
- "7a_d-03b_west": PreRegion("7a_d-03b_west", "7a_d-03b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-03b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-03b_west"]),
- "7a_d-03b_east": PreRegion("7a_d-03b_east", "7a_d-03b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-03b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-03b_east"]),
+ "7a_d-03b_west": PreRegion("7a_d-03b_west", "7a_d-03b", connections_by_region["7a_d-03b_west"], locations_by_region["7a_d-03b_west"]),
+ "7a_d-03b_east": PreRegion("7a_d-03b_east", "7a_d-03b", connections_by_region["7a_d-03b_east"], locations_by_region["7a_d-03b_east"]),
- "7a_d-04_west": PreRegion("7a_d-04_west", "7a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-04_west"]),
- "7a_d-04_east": PreRegion("7a_d-04_east", "7a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-04_east"]),
+ "7a_d-04_west": PreRegion("7a_d-04_west", "7a_d-04", connections_by_region["7a_d-04_west"], locations_by_region["7a_d-04_west"]),
+ "7a_d-04_east": PreRegion("7a_d-04_east", "7a_d-04", connections_by_region["7a_d-04_east"], locations_by_region["7a_d-04_east"]),
- "7a_d-05_west": PreRegion("7a_d-05_west", "7a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-05_west"]),
- "7a_d-05_north-east": PreRegion("7a_d-05_north-east", "7a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-05_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-05_north-east"]),
- "7a_d-05_east": PreRegion("7a_d-05_east", "7a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-05_east"]),
+ "7a_d-05_west": PreRegion("7a_d-05_west", "7a_d-05", connections_by_region["7a_d-05_west"], locations_by_region["7a_d-05_west"]),
+ "7a_d-05_north-east": PreRegion("7a_d-05_north-east", "7a_d-05", connections_by_region["7a_d-05_north-east"], locations_by_region["7a_d-05_north-east"]),
+ "7a_d-05_east": PreRegion("7a_d-05_east", "7a_d-05", connections_by_region["7a_d-05_east"], locations_by_region["7a_d-05_east"]),
- "7a_d-05b_west": PreRegion("7a_d-05b_west", "7a_d-05b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-05b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-05b_west"]),
+ "7a_d-05b_west": PreRegion("7a_d-05b_west", "7a_d-05b", connections_by_region["7a_d-05b_west"], locations_by_region["7a_d-05b_west"]),
- "7a_d-06_west": PreRegion("7a_d-06_west", "7a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-06_west"]),
- "7a_d-06_south-west": PreRegion("7a_d-06_south-west", "7a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-06_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-06_south-west"]),
- "7a_d-06_south-east": PreRegion("7a_d-06_south-east", "7a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-06_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-06_south-east"]),
- "7a_d-06_east": PreRegion("7a_d-06_east", "7a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-06_east"]),
+ "7a_d-06_west": PreRegion("7a_d-06_west", "7a_d-06", connections_by_region["7a_d-06_west"], locations_by_region["7a_d-06_west"]),
+ "7a_d-06_south-west": PreRegion("7a_d-06_south-west", "7a_d-06", connections_by_region["7a_d-06_south-west"], locations_by_region["7a_d-06_south-west"]),
+ "7a_d-06_south-east": PreRegion("7a_d-06_south-east", "7a_d-06", connections_by_region["7a_d-06_south-east"], locations_by_region["7a_d-06_south-east"]),
+ "7a_d-06_east": PreRegion("7a_d-06_east", "7a_d-06", connections_by_region["7a_d-06_east"], locations_by_region["7a_d-06_east"]),
- "7a_d-07_east": PreRegion("7a_d-07_east", "7a_d-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-07_east"]),
+ "7a_d-07_east": PreRegion("7a_d-07_east", "7a_d-07", connections_by_region["7a_d-07_east"], locations_by_region["7a_d-07_east"]),
- "7a_d-08_west": PreRegion("7a_d-08_west", "7a_d-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-08_west"]),
- "7a_d-08_east": PreRegion("7a_d-08_east", "7a_d-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-08_east"]),
+ "7a_d-08_west": PreRegion("7a_d-08_west", "7a_d-08", connections_by_region["7a_d-08_west"], locations_by_region["7a_d-08_west"]),
+ "7a_d-08_east": PreRegion("7a_d-08_east", "7a_d-08", connections_by_region["7a_d-08_east"], locations_by_region["7a_d-08_east"]),
- "7a_d-09_west": PreRegion("7a_d-09_west", "7a_d-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-09_west"]),
- "7a_d-09_east": PreRegion("7a_d-09_east", "7a_d-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-09_east"]),
+ "7a_d-09_west": PreRegion("7a_d-09_west", "7a_d-09", connections_by_region["7a_d-09_west"], locations_by_region["7a_d-09_west"]),
+ "7a_d-09_east": PreRegion("7a_d-09_east", "7a_d-09", connections_by_region["7a_d-09_east"], locations_by_region["7a_d-09_east"]),
- "7a_d-10_west": PreRegion("7a_d-10_west", "7a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-10_west"]),
- "7a_d-10_north-west": PreRegion("7a_d-10_north-west", "7a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-10_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-10_north-west"]),
- "7a_d-10_north": PreRegion("7a_d-10_north", "7a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-10_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-10_north"]),
- "7a_d-10_north-east": PreRegion("7a_d-10_north-east", "7a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-10_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-10_north-east"]),
- "7a_d-10_east": PreRegion("7a_d-10_east", "7a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-10_east"]),
+ "7a_d-10_west": PreRegion("7a_d-10_west", "7a_d-10", connections_by_region["7a_d-10_west"], locations_by_region["7a_d-10_west"]),
+ "7a_d-10_north-west": PreRegion("7a_d-10_north-west", "7a_d-10", connections_by_region["7a_d-10_north-west"], locations_by_region["7a_d-10_north-west"]),
+ "7a_d-10_north": PreRegion("7a_d-10_north", "7a_d-10", connections_by_region["7a_d-10_north"], locations_by_region["7a_d-10_north"]),
+ "7a_d-10_north-east": PreRegion("7a_d-10_north-east", "7a_d-10", connections_by_region["7a_d-10_north-east"], locations_by_region["7a_d-10_north-east"]),
+ "7a_d-10_east": PreRegion("7a_d-10_east", "7a_d-10", connections_by_region["7a_d-10_east"], locations_by_region["7a_d-10_east"]),
- "7a_d-10b_west": PreRegion("7a_d-10b_west", "7a_d-10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-10b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-10b_west"]),
- "7a_d-10b_east": PreRegion("7a_d-10b_east", "7a_d-10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-10b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-10b_east"]),
+ "7a_d-10b_west": PreRegion("7a_d-10b_west", "7a_d-10b", connections_by_region["7a_d-10b_west"], locations_by_region["7a_d-10b_west"]),
+ "7a_d-10b_east": PreRegion("7a_d-10b_east", "7a_d-10b", connections_by_region["7a_d-10b_east"], locations_by_region["7a_d-10b_east"]),
- "7a_d-11_bottom": PreRegion("7a_d-11_bottom", "7a_d-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-11_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-11_bottom"]),
- "7a_d-11_top": PreRegion("7a_d-11_top", "7a_d-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_d-11_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_d-11_top"]),
+ "7a_d-11_bottom": PreRegion("7a_d-11_bottom", "7a_d-11", connections_by_region["7a_d-11_bottom"], locations_by_region["7a_d-11_bottom"]),
+ "7a_d-11_top": PreRegion("7a_d-11_top", "7a_d-11", connections_by_region["7a_d-11_top"], locations_by_region["7a_d-11_top"]),
- "7a_e-00b_bottom": PreRegion("7a_e-00b_bottom", "7a_e-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-00b_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-00b_bottom"]),
- "7a_e-00b_top": PreRegion("7a_e-00b_top", "7a_e-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-00b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-00b_top"]),
+ "7a_e-00b_bottom": PreRegion("7a_e-00b_bottom", "7a_e-00b", connections_by_region["7a_e-00b_bottom"], locations_by_region["7a_e-00b_bottom"]),
+ "7a_e-00b_top": PreRegion("7a_e-00b_top", "7a_e-00b", connections_by_region["7a_e-00b_top"], locations_by_region["7a_e-00b_top"]),
- "7a_e-00_west": PreRegion("7a_e-00_west", "7a_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-00_west"]),
- "7a_e-00_south-west": PreRegion("7a_e-00_south-west", "7a_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-00_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-00_south-west"]),
- "7a_e-00_north-west": PreRegion("7a_e-00_north-west", "7a_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-00_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-00_north-west"]),
- "7a_e-00_east": PreRegion("7a_e-00_east", "7a_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-00_east"]),
+ "7a_e-00_west": PreRegion("7a_e-00_west", "7a_e-00", connections_by_region["7a_e-00_west"], locations_by_region["7a_e-00_west"]),
+ "7a_e-00_south-west": PreRegion("7a_e-00_south-west", "7a_e-00", connections_by_region["7a_e-00_south-west"], locations_by_region["7a_e-00_south-west"]),
+ "7a_e-00_north-west": PreRegion("7a_e-00_north-west", "7a_e-00", connections_by_region["7a_e-00_north-west"], locations_by_region["7a_e-00_north-west"]),
+ "7a_e-00_east": PreRegion("7a_e-00_east", "7a_e-00", connections_by_region["7a_e-00_east"], locations_by_region["7a_e-00_east"]),
- "7a_e-01_west": PreRegion("7a_e-01_west", "7a_e-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-01_west"]),
- "7a_e-01_north": PreRegion("7a_e-01_north", "7a_e-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-01_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-01_north"]),
- "7a_e-01_east": PreRegion("7a_e-01_east", "7a_e-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-01_east"]),
+ "7a_e-01_west": PreRegion("7a_e-01_west", "7a_e-01", connections_by_region["7a_e-01_west"], locations_by_region["7a_e-01_west"]),
+ "7a_e-01_north": PreRegion("7a_e-01_north", "7a_e-01", connections_by_region["7a_e-01_north"], locations_by_region["7a_e-01_north"]),
+ "7a_e-01_east": PreRegion("7a_e-01_east", "7a_e-01", connections_by_region["7a_e-01_east"], locations_by_region["7a_e-01_east"]),
- "7a_e-01b_west": PreRegion("7a_e-01b_west", "7a_e-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-01b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-01b_west"]),
- "7a_e-01b_east": PreRegion("7a_e-01b_east", "7a_e-01b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-01b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-01b_east"]),
+ "7a_e-01b_west": PreRegion("7a_e-01b_west", "7a_e-01b", connections_by_region["7a_e-01b_west"], locations_by_region["7a_e-01b_west"]),
+ "7a_e-01b_east": PreRegion("7a_e-01b_east", "7a_e-01b", connections_by_region["7a_e-01b_east"], locations_by_region["7a_e-01b_east"]),
- "7a_e-01c_west": PreRegion("7a_e-01c_west", "7a_e-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-01c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-01c_west"]),
- "7a_e-01c_east": PreRegion("7a_e-01c_east", "7a_e-01c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-01c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-01c_east"]),
+ "7a_e-01c_west": PreRegion("7a_e-01c_west", "7a_e-01c", connections_by_region["7a_e-01c_west"], locations_by_region["7a_e-01c_west"]),
+ "7a_e-01c_east": PreRegion("7a_e-01c_east", "7a_e-01c", connections_by_region["7a_e-01c_east"], locations_by_region["7a_e-01c_east"]),
- "7a_e-02_west": PreRegion("7a_e-02_west", "7a_e-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-02_west"]),
- "7a_e-02_east": PreRegion("7a_e-02_east", "7a_e-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-02_east"]),
+ "7a_e-02_west": PreRegion("7a_e-02_west", "7a_e-02", connections_by_region["7a_e-02_west"], locations_by_region["7a_e-02_west"]),
+ "7a_e-02_east": PreRegion("7a_e-02_east", "7a_e-02", connections_by_region["7a_e-02_east"], locations_by_region["7a_e-02_east"]),
- "7a_e-03_south-west": PreRegion("7a_e-03_south-west", "7a_e-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-03_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-03_south-west"]),
- "7a_e-03_west": PreRegion("7a_e-03_west", "7a_e-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-03_west"]),
- "7a_e-03_east": PreRegion("7a_e-03_east", "7a_e-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-03_east"]),
+ "7a_e-03_south-west": PreRegion("7a_e-03_south-west", "7a_e-03", connections_by_region["7a_e-03_south-west"], locations_by_region["7a_e-03_south-west"]),
+ "7a_e-03_west": PreRegion("7a_e-03_west", "7a_e-03", connections_by_region["7a_e-03_west"], locations_by_region["7a_e-03_west"]),
+ "7a_e-03_east": PreRegion("7a_e-03_east", "7a_e-03", connections_by_region["7a_e-03_east"], locations_by_region["7a_e-03_east"]),
- "7a_e-04_west": PreRegion("7a_e-04_west", "7a_e-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-04_west"]),
- "7a_e-04_east": PreRegion("7a_e-04_east", "7a_e-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-04_east"]),
+ "7a_e-04_west": PreRegion("7a_e-04_west", "7a_e-04", connections_by_region["7a_e-04_west"], locations_by_region["7a_e-04_west"]),
+ "7a_e-04_east": PreRegion("7a_e-04_east", "7a_e-04", connections_by_region["7a_e-04_east"], locations_by_region["7a_e-04_east"]),
- "7a_e-05_west": PreRegion("7a_e-05_west", "7a_e-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-05_west"]),
- "7a_e-05_east": PreRegion("7a_e-05_east", "7a_e-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-05_east"]),
+ "7a_e-05_west": PreRegion("7a_e-05_west", "7a_e-05", connections_by_region["7a_e-05_west"], locations_by_region["7a_e-05_west"]),
+ "7a_e-05_east": PreRegion("7a_e-05_east", "7a_e-05", connections_by_region["7a_e-05_east"], locations_by_region["7a_e-05_east"]),
- "7a_e-06_west": PreRegion("7a_e-06_west", "7a_e-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-06_west"]),
- "7a_e-06_east": PreRegion("7a_e-06_east", "7a_e-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-06_east"]),
+ "7a_e-06_west": PreRegion("7a_e-06_west", "7a_e-06", connections_by_region["7a_e-06_west"], locations_by_region["7a_e-06_west"]),
+ "7a_e-06_east": PreRegion("7a_e-06_east", "7a_e-06", connections_by_region["7a_e-06_east"], locations_by_region["7a_e-06_east"]),
- "7a_e-07_bottom": PreRegion("7a_e-07_bottom", "7a_e-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-07_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-07_bottom"]),
- "7a_e-07_top": PreRegion("7a_e-07_top", "7a_e-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-07_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-07_top"]),
+ "7a_e-07_bottom": PreRegion("7a_e-07_bottom", "7a_e-07", connections_by_region["7a_e-07_bottom"], locations_by_region["7a_e-07_bottom"]),
+ "7a_e-07_top": PreRegion("7a_e-07_top", "7a_e-07", connections_by_region["7a_e-07_top"], locations_by_region["7a_e-07_top"]),
- "7a_e-08_south": PreRegion("7a_e-08_south", "7a_e-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-08_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-08_south"]),
- "7a_e-08_west": PreRegion("7a_e-08_west", "7a_e-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-08_west"]),
- "7a_e-08_east": PreRegion("7a_e-08_east", "7a_e-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-08_east"]),
+ "7a_e-08_south": PreRegion("7a_e-08_south", "7a_e-08", connections_by_region["7a_e-08_south"], locations_by_region["7a_e-08_south"]),
+ "7a_e-08_west": PreRegion("7a_e-08_west", "7a_e-08", connections_by_region["7a_e-08_west"], locations_by_region["7a_e-08_west"]),
+ "7a_e-08_east": PreRegion("7a_e-08_east", "7a_e-08", connections_by_region["7a_e-08_east"], locations_by_region["7a_e-08_east"]),
- "7a_e-09_north": PreRegion("7a_e-09_north", "7a_e-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-09_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-09_north"]),
- "7a_e-09_east": PreRegion("7a_e-09_east", "7a_e-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-09_east"]),
+ "7a_e-09_north": PreRegion("7a_e-09_north", "7a_e-09", connections_by_region["7a_e-09_north"], locations_by_region["7a_e-09_north"]),
+ "7a_e-09_east": PreRegion("7a_e-09_east", "7a_e-09", connections_by_region["7a_e-09_east"], locations_by_region["7a_e-09_east"]),
- "7a_e-11_south": PreRegion("7a_e-11_south", "7a_e-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-11_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-11_south"]),
- "7a_e-11_north": PreRegion("7a_e-11_north", "7a_e-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-11_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-11_north"]),
- "7a_e-11_east": PreRegion("7a_e-11_east", "7a_e-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-11_east"]),
+ "7a_e-11_south": PreRegion("7a_e-11_south", "7a_e-11", connections_by_region["7a_e-11_south"], locations_by_region["7a_e-11_south"]),
+ "7a_e-11_north": PreRegion("7a_e-11_north", "7a_e-11", connections_by_region["7a_e-11_north"], locations_by_region["7a_e-11_north"]),
+ "7a_e-11_east": PreRegion("7a_e-11_east", "7a_e-11", connections_by_region["7a_e-11_east"], locations_by_region["7a_e-11_east"]),
- "7a_e-12_west": PreRegion("7a_e-12_west", "7a_e-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-12_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-12_west"]),
+ "7a_e-12_west": PreRegion("7a_e-12_west", "7a_e-12", connections_by_region["7a_e-12_west"], locations_by_region["7a_e-12_west"]),
- "7a_e-10_south": PreRegion("7a_e-10_south", "7a_e-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-10_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-10_south"]),
- "7a_e-10_north": PreRegion("7a_e-10_north", "7a_e-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-10_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-10_north"]),
- "7a_e-10_east": PreRegion("7a_e-10_east", "7a_e-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-10_east"]),
+ "7a_e-10_south": PreRegion("7a_e-10_south", "7a_e-10", connections_by_region["7a_e-10_south"], locations_by_region["7a_e-10_south"]),
+ "7a_e-10_north": PreRegion("7a_e-10_north", "7a_e-10", connections_by_region["7a_e-10_north"], locations_by_region["7a_e-10_north"]),
+ "7a_e-10_east": PreRegion("7a_e-10_east", "7a_e-10", connections_by_region["7a_e-10_east"], locations_by_region["7a_e-10_east"]),
- "7a_e-10b_west": PreRegion("7a_e-10b_west", "7a_e-10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-10b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-10b_west"]),
- "7a_e-10b_east": PreRegion("7a_e-10b_east", "7a_e-10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-10b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-10b_east"]),
+ "7a_e-10b_west": PreRegion("7a_e-10b_west", "7a_e-10b", connections_by_region["7a_e-10b_west"], locations_by_region["7a_e-10b_west"]),
+ "7a_e-10b_east": PreRegion("7a_e-10b_east", "7a_e-10b", connections_by_region["7a_e-10b_east"], locations_by_region["7a_e-10b_east"]),
- "7a_e-13_bottom": PreRegion("7a_e-13_bottom", "7a_e-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-13_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-13_bottom"]),
- "7a_e-13_top": PreRegion("7a_e-13_top", "7a_e-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_e-13_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_e-13_top"]),
+ "7a_e-13_bottom": PreRegion("7a_e-13_bottom", "7a_e-13", connections_by_region["7a_e-13_bottom"], locations_by_region["7a_e-13_bottom"]),
+ "7a_e-13_top": PreRegion("7a_e-13_top", "7a_e-13", connections_by_region["7a_e-13_top"], locations_by_region["7a_e-13_top"]),
- "7a_f-00_south": PreRegion("7a_f-00_south", "7a_f-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-00_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-00_south"]),
- "7a_f-00_west": PreRegion("7a_f-00_west", "7a_f-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-00_west"]),
- "7a_f-00_north-west": PreRegion("7a_f-00_north-west", "7a_f-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-00_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-00_north-west"]),
- "7a_f-00_north-east": PreRegion("7a_f-00_north-east", "7a_f-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-00_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-00_north-east"]),
- "7a_f-00_east": PreRegion("7a_f-00_east", "7a_f-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-00_east"]),
+ "7a_f-00_south": PreRegion("7a_f-00_south", "7a_f-00", connections_by_region["7a_f-00_south"], locations_by_region["7a_f-00_south"]),
+ "7a_f-00_west": PreRegion("7a_f-00_west", "7a_f-00", connections_by_region["7a_f-00_west"], locations_by_region["7a_f-00_west"]),
+ "7a_f-00_north-west": PreRegion("7a_f-00_north-west", "7a_f-00", connections_by_region["7a_f-00_north-west"], locations_by_region["7a_f-00_north-west"]),
+ "7a_f-00_north-east": PreRegion("7a_f-00_north-east", "7a_f-00", connections_by_region["7a_f-00_north-east"], locations_by_region["7a_f-00_north-east"]),
+ "7a_f-00_east": PreRegion("7a_f-00_east", "7a_f-00", connections_by_region["7a_f-00_east"], locations_by_region["7a_f-00_east"]),
- "7a_f-01_south": PreRegion("7a_f-01_south", "7a_f-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-01_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-01_south"]),
- "7a_f-01_north": PreRegion("7a_f-01_north", "7a_f-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-01_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-01_north"]),
+ "7a_f-01_south": PreRegion("7a_f-01_south", "7a_f-01", connections_by_region["7a_f-01_south"], locations_by_region["7a_f-01_south"]),
+ "7a_f-01_north": PreRegion("7a_f-01_north", "7a_f-01", connections_by_region["7a_f-01_north"], locations_by_region["7a_f-01_north"]),
- "7a_f-02_west": PreRegion("7a_f-02_west", "7a_f-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-02_west"]),
- "7a_f-02_north-west": PreRegion("7a_f-02_north-west", "7a_f-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-02_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-02_north-west"]),
- "7a_f-02_north-east": PreRegion("7a_f-02_north-east", "7a_f-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-02_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-02_north-east"]),
- "7a_f-02_east": PreRegion("7a_f-02_east", "7a_f-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-02_east"]),
+ "7a_f-02_west": PreRegion("7a_f-02_west", "7a_f-02", connections_by_region["7a_f-02_west"], locations_by_region["7a_f-02_west"]),
+ "7a_f-02_north-west": PreRegion("7a_f-02_north-west", "7a_f-02", connections_by_region["7a_f-02_north-west"], locations_by_region["7a_f-02_north-west"]),
+ "7a_f-02_north-east": PreRegion("7a_f-02_north-east", "7a_f-02", connections_by_region["7a_f-02_north-east"], locations_by_region["7a_f-02_north-east"]),
+ "7a_f-02_east": PreRegion("7a_f-02_east", "7a_f-02", connections_by_region["7a_f-02_east"], locations_by_region["7a_f-02_east"]),
- "7a_f-02b_west": PreRegion("7a_f-02b_west", "7a_f-02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-02b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-02b_west"]),
- "7a_f-02b_east": PreRegion("7a_f-02b_east", "7a_f-02b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-02b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-02b_east"]),
+ "7a_f-02b_west": PreRegion("7a_f-02b_west", "7a_f-02b", connections_by_region["7a_f-02b_west"], locations_by_region["7a_f-02b_west"]),
+ "7a_f-02b_east": PreRegion("7a_f-02b_east", "7a_f-02b", connections_by_region["7a_f-02b_east"], locations_by_region["7a_f-02b_east"]),
- "7a_f-04_west": PreRegion("7a_f-04_west", "7a_f-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-04_west"]),
- "7a_f-04_east": PreRegion("7a_f-04_east", "7a_f-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-04_east"]),
+ "7a_f-04_west": PreRegion("7a_f-04_west", "7a_f-04", connections_by_region["7a_f-04_west"], locations_by_region["7a_f-04_west"]),
+ "7a_f-04_east": PreRegion("7a_f-04_east", "7a_f-04", connections_by_region["7a_f-04_east"], locations_by_region["7a_f-04_east"]),
- "7a_f-03_west": PreRegion("7a_f-03_west", "7a_f-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-03_west"]),
- "7a_f-03_east": PreRegion("7a_f-03_east", "7a_f-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-03_east"]),
+ "7a_f-03_west": PreRegion("7a_f-03_west", "7a_f-03", connections_by_region["7a_f-03_west"], locations_by_region["7a_f-03_west"]),
+ "7a_f-03_east": PreRegion("7a_f-03_east", "7a_f-03", connections_by_region["7a_f-03_east"], locations_by_region["7a_f-03_east"]),
- "7a_f-05_west": PreRegion("7a_f-05_west", "7a_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-05_west"]),
- "7a_f-05_south-west": PreRegion("7a_f-05_south-west", "7a_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-05_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-05_south-west"]),
- "7a_f-05_north-west": PreRegion("7a_f-05_north-west", "7a_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-05_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-05_north-west"]),
- "7a_f-05_south": PreRegion("7a_f-05_south", "7a_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-05_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-05_south"]),
- "7a_f-05_north": PreRegion("7a_f-05_north", "7a_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-05_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-05_north"]),
- "7a_f-05_north-east": PreRegion("7a_f-05_north-east", "7a_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-05_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-05_north-east"]),
- "7a_f-05_south-east": PreRegion("7a_f-05_south-east", "7a_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-05_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-05_south-east"]),
- "7a_f-05_east": PreRegion("7a_f-05_east", "7a_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-05_east"]),
+ "7a_f-05_west": PreRegion("7a_f-05_west", "7a_f-05", connections_by_region["7a_f-05_west"], locations_by_region["7a_f-05_west"]),
+ "7a_f-05_south-west": PreRegion("7a_f-05_south-west", "7a_f-05", connections_by_region["7a_f-05_south-west"], locations_by_region["7a_f-05_south-west"]),
+ "7a_f-05_north-west": PreRegion("7a_f-05_north-west", "7a_f-05", connections_by_region["7a_f-05_north-west"], locations_by_region["7a_f-05_north-west"]),
+ "7a_f-05_south": PreRegion("7a_f-05_south", "7a_f-05", connections_by_region["7a_f-05_south"], locations_by_region["7a_f-05_south"]),
+ "7a_f-05_north": PreRegion("7a_f-05_north", "7a_f-05", connections_by_region["7a_f-05_north"], locations_by_region["7a_f-05_north"]),
+ "7a_f-05_north-east": PreRegion("7a_f-05_north-east", "7a_f-05", connections_by_region["7a_f-05_north-east"], locations_by_region["7a_f-05_north-east"]),
+ "7a_f-05_south-east": PreRegion("7a_f-05_south-east", "7a_f-05", connections_by_region["7a_f-05_south-east"], locations_by_region["7a_f-05_south-east"]),
+ "7a_f-05_east": PreRegion("7a_f-05_east", "7a_f-05", connections_by_region["7a_f-05_east"], locations_by_region["7a_f-05_east"]),
- "7a_f-06_north-west": PreRegion("7a_f-06_north-west", "7a_f-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-06_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-06_north-west"]),
- "7a_f-06_north": PreRegion("7a_f-06_north", "7a_f-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-06_north"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-06_north"]),
- "7a_f-06_north-east": PreRegion("7a_f-06_north-east", "7a_f-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-06_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-06_north-east"]),
+ "7a_f-06_north-west": PreRegion("7a_f-06_north-west", "7a_f-06", connections_by_region["7a_f-06_north-west"], locations_by_region["7a_f-06_north-west"]),
+ "7a_f-06_north": PreRegion("7a_f-06_north", "7a_f-06", connections_by_region["7a_f-06_north"], locations_by_region["7a_f-06_north"]),
+ "7a_f-06_north-east": PreRegion("7a_f-06_north-east", "7a_f-06", connections_by_region["7a_f-06_north-east"], locations_by_region["7a_f-06_north-east"]),
- "7a_f-07_west": PreRegion("7a_f-07_west", "7a_f-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-07_west"]),
- "7a_f-07_south-west": PreRegion("7a_f-07_south-west", "7a_f-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-07_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-07_south-west"]),
- "7a_f-07_south": PreRegion("7a_f-07_south", "7a_f-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-07_south"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-07_south"]),
- "7a_f-07_south-east": PreRegion("7a_f-07_south-east", "7a_f-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-07_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-07_south-east"]),
+ "7a_f-07_west": PreRegion("7a_f-07_west", "7a_f-07", connections_by_region["7a_f-07_west"], locations_by_region["7a_f-07_west"]),
+ "7a_f-07_south-west": PreRegion("7a_f-07_south-west", "7a_f-07", connections_by_region["7a_f-07_south-west"], locations_by_region["7a_f-07_south-west"]),
+ "7a_f-07_south": PreRegion("7a_f-07_south", "7a_f-07", connections_by_region["7a_f-07_south"], locations_by_region["7a_f-07_south"]),
+ "7a_f-07_south-east": PreRegion("7a_f-07_south-east", "7a_f-07", connections_by_region["7a_f-07_south-east"], locations_by_region["7a_f-07_south-east"]),
- "7a_f-08_west": PreRegion("7a_f-08_west", "7a_f-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-08_west"]),
- "7a_f-08_north-west": PreRegion("7a_f-08_north-west", "7a_f-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-08_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-08_north-west"]),
- "7a_f-08_east": PreRegion("7a_f-08_east", "7a_f-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-08_east"]),
+ "7a_f-08_west": PreRegion("7a_f-08_west", "7a_f-08", connections_by_region["7a_f-08_west"], locations_by_region["7a_f-08_west"]),
+ "7a_f-08_north-west": PreRegion("7a_f-08_north-west", "7a_f-08", connections_by_region["7a_f-08_north-west"], locations_by_region["7a_f-08_north-west"]),
+ "7a_f-08_east": PreRegion("7a_f-08_east", "7a_f-08", connections_by_region["7a_f-08_east"], locations_by_region["7a_f-08_east"]),
- "7a_f-08b_west": PreRegion("7a_f-08b_west", "7a_f-08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-08b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-08b_west"]),
- "7a_f-08b_east": PreRegion("7a_f-08b_east", "7a_f-08b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-08b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-08b_east"]),
+ "7a_f-08b_west": PreRegion("7a_f-08b_west", "7a_f-08b", connections_by_region["7a_f-08b_west"], locations_by_region["7a_f-08b_west"]),
+ "7a_f-08b_east": PreRegion("7a_f-08b_east", "7a_f-08b", connections_by_region["7a_f-08b_east"], locations_by_region["7a_f-08b_east"]),
- "7a_f-08d_west": PreRegion("7a_f-08d_west", "7a_f-08d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-08d_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-08d_west"]),
- "7a_f-08d_east": PreRegion("7a_f-08d_east", "7a_f-08d", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-08d_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-08d_east"]),
+ "7a_f-08d_west": PreRegion("7a_f-08d_west", "7a_f-08d", connections_by_region["7a_f-08d_west"], locations_by_region["7a_f-08d_west"]),
+ "7a_f-08d_east": PreRegion("7a_f-08d_east", "7a_f-08d", connections_by_region["7a_f-08d_east"], locations_by_region["7a_f-08d_east"]),
- "7a_f-08c_west": PreRegion("7a_f-08c_west", "7a_f-08c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-08c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-08c_west"]),
- "7a_f-08c_east": PreRegion("7a_f-08c_east", "7a_f-08c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-08c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-08c_east"]),
+ "7a_f-08c_west": PreRegion("7a_f-08c_west", "7a_f-08c", connections_by_region["7a_f-08c_west"], locations_by_region["7a_f-08c_west"]),
+ "7a_f-08c_east": PreRegion("7a_f-08c_east", "7a_f-08c", connections_by_region["7a_f-08c_east"], locations_by_region["7a_f-08c_east"]),
- "7a_f-09_west": PreRegion("7a_f-09_west", "7a_f-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-09_west"]),
- "7a_f-09_east": PreRegion("7a_f-09_east", "7a_f-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-09_east"]),
+ "7a_f-09_west": PreRegion("7a_f-09_west", "7a_f-09", connections_by_region["7a_f-09_west"], locations_by_region["7a_f-09_west"]),
+ "7a_f-09_east": PreRegion("7a_f-09_east", "7a_f-09", connections_by_region["7a_f-09_east"], locations_by_region["7a_f-09_east"]),
- "7a_f-10_west": PreRegion("7a_f-10_west", "7a_f-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-10_west"]),
- "7a_f-10_north-east": PreRegion("7a_f-10_north-east", "7a_f-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-10_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-10_north-east"]),
- "7a_f-10_east": PreRegion("7a_f-10_east", "7a_f-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-10_east"]),
+ "7a_f-10_west": PreRegion("7a_f-10_west", "7a_f-10", connections_by_region["7a_f-10_west"], locations_by_region["7a_f-10_west"]),
+ "7a_f-10_north-east": PreRegion("7a_f-10_north-east", "7a_f-10", connections_by_region["7a_f-10_north-east"], locations_by_region["7a_f-10_north-east"]),
+ "7a_f-10_east": PreRegion("7a_f-10_east", "7a_f-10", connections_by_region["7a_f-10_east"], locations_by_region["7a_f-10_east"]),
- "7a_f-10b_west": PreRegion("7a_f-10b_west", "7a_f-10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-10b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-10b_west"]),
- "7a_f-10b_east": PreRegion("7a_f-10b_east", "7a_f-10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-10b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-10b_east"]),
+ "7a_f-10b_west": PreRegion("7a_f-10b_west", "7a_f-10b", connections_by_region["7a_f-10b_west"], locations_by_region["7a_f-10b_west"]),
+ "7a_f-10b_east": PreRegion("7a_f-10b_east", "7a_f-10b", connections_by_region["7a_f-10b_east"], locations_by_region["7a_f-10b_east"]),
- "7a_f-11_bottom": PreRegion("7a_f-11_bottom", "7a_f-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-11_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-11_bottom"]),
- "7a_f-11_top": PreRegion("7a_f-11_top", "7a_f-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_f-11_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_f-11_top"]),
+ "7a_f-11_bottom": PreRegion("7a_f-11_bottom", "7a_f-11", connections_by_region["7a_f-11_bottom"], locations_by_region["7a_f-11_bottom"]),
+ "7a_f-11_top": PreRegion("7a_f-11_top", "7a_f-11", connections_by_region["7a_f-11_top"], locations_by_region["7a_f-11_top"]),
- "7a_g-00_bottom": PreRegion("7a_g-00_bottom", "7a_g-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-00_bottom"]),
- "7a_g-00_top": PreRegion("7a_g-00_top", "7a_g-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-00_top"]),
+ "7a_g-00_bottom": PreRegion("7a_g-00_bottom", "7a_g-00", connections_by_region["7a_g-00_bottom"], locations_by_region["7a_g-00_bottom"]),
+ "7a_g-00_top": PreRegion("7a_g-00_top", "7a_g-00", connections_by_region["7a_g-00_top"], locations_by_region["7a_g-00_top"]),
- "7a_g-00b_bottom": PreRegion("7a_g-00b_bottom", "7a_g-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-00b_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-00b_bottom"]),
- "7a_g-00b_c26": PreRegion("7a_g-00b_c26", "7a_g-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-00b_c26"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-00b_c26"]),
- "7a_g-00b_c24": PreRegion("7a_g-00b_c24", "7a_g-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-00b_c24"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-00b_c24"]),
- "7a_g-00b_c21": PreRegion("7a_g-00b_c21", "7a_g-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-00b_c21"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-00b_c21"]),
- "7a_g-00b_top": PreRegion("7a_g-00b_top", "7a_g-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-00b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-00b_top"]),
+ "7a_g-00b_bottom": PreRegion("7a_g-00b_bottom", "7a_g-00b", connections_by_region["7a_g-00b_bottom"], locations_by_region["7a_g-00b_bottom"]),
+ "7a_g-00b_c26": PreRegion("7a_g-00b_c26", "7a_g-00b", connections_by_region["7a_g-00b_c26"], locations_by_region["7a_g-00b_c26"]),
+ "7a_g-00b_c24": PreRegion("7a_g-00b_c24", "7a_g-00b", connections_by_region["7a_g-00b_c24"], locations_by_region["7a_g-00b_c24"]),
+ "7a_g-00b_c21": PreRegion("7a_g-00b_c21", "7a_g-00b", connections_by_region["7a_g-00b_c21"], locations_by_region["7a_g-00b_c21"]),
+ "7a_g-00b_top": PreRegion("7a_g-00b_top", "7a_g-00b", connections_by_region["7a_g-00b_top"], locations_by_region["7a_g-00b_top"]),
- "7a_g-01_bottom": PreRegion("7a_g-01_bottom", "7a_g-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-01_bottom"]),
- "7a_g-01_c18": PreRegion("7a_g-01_c18", "7a_g-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-01_c18"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-01_c18"]),
- "7a_g-01_c16": PreRegion("7a_g-01_c16", "7a_g-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-01_c16"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-01_c16"]),
- "7a_g-01_top": PreRegion("7a_g-01_top", "7a_g-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-01_top"]),
+ "7a_g-01_bottom": PreRegion("7a_g-01_bottom", "7a_g-01", connections_by_region["7a_g-01_bottom"], locations_by_region["7a_g-01_bottom"]),
+ "7a_g-01_c18": PreRegion("7a_g-01_c18", "7a_g-01", connections_by_region["7a_g-01_c18"], locations_by_region["7a_g-01_c18"]),
+ "7a_g-01_c16": PreRegion("7a_g-01_c16", "7a_g-01", connections_by_region["7a_g-01_c16"], locations_by_region["7a_g-01_c16"]),
+ "7a_g-01_top": PreRegion("7a_g-01_top", "7a_g-01", connections_by_region["7a_g-01_top"], locations_by_region["7a_g-01_top"]),
- "7a_g-02_bottom": PreRegion("7a_g-02_bottom", "7a_g-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-02_bottom"]),
- "7a_g-02_top": PreRegion("7a_g-02_top", "7a_g-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-02_top"]),
+ "7a_g-02_bottom": PreRegion("7a_g-02_bottom", "7a_g-02", connections_by_region["7a_g-02_bottom"], locations_by_region["7a_g-02_bottom"]),
+ "7a_g-02_top": PreRegion("7a_g-02_top", "7a_g-02", connections_by_region["7a_g-02_top"], locations_by_region["7a_g-02_top"]),
- "7a_g-03_bottom": PreRegion("7a_g-03_bottom", "7a_g-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-03_bottom"]),
- "7a_g-03_goal": PreRegion("7a_g-03_goal", "7a_g-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7a_g-03_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "7a_g-03_goal"]),
+ "7a_g-03_bottom": PreRegion("7a_g-03_bottom", "7a_g-03", connections_by_region["7a_g-03_bottom"], locations_by_region["7a_g-03_bottom"]),
+ "7a_g-03_goal": PreRegion("7a_g-03_goal", "7a_g-03", connections_by_region["7a_g-03_goal"], locations_by_region["7a_g-03_goal"]),
- "7b_a-00_west": PreRegion("7b_a-00_west", "7b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_a-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_a-00_west"]),
- "7b_a-00_east": PreRegion("7b_a-00_east", "7b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_a-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_a-00_east"]),
+ "7b_a-00_west": PreRegion("7b_a-00_west", "7b_a-00", connections_by_region["7b_a-00_west"], locations_by_region["7b_a-00_west"]),
+ "7b_a-00_east": PreRegion("7b_a-00_east", "7b_a-00", connections_by_region["7b_a-00_east"], locations_by_region["7b_a-00_east"]),
- "7b_a-01_west": PreRegion("7b_a-01_west", "7b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_a-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_a-01_west"]),
- "7b_a-01_east": PreRegion("7b_a-01_east", "7b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_a-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_a-01_east"]),
+ "7b_a-01_west": PreRegion("7b_a-01_west", "7b_a-01", connections_by_region["7b_a-01_west"], locations_by_region["7b_a-01_west"]),
+ "7b_a-01_east": PreRegion("7b_a-01_east", "7b_a-01", connections_by_region["7b_a-01_east"], locations_by_region["7b_a-01_east"]),
- "7b_a-02_west": PreRegion("7b_a-02_west", "7b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_a-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_a-02_west"]),
- "7b_a-02_east": PreRegion("7b_a-02_east", "7b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_a-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_a-02_east"]),
+ "7b_a-02_west": PreRegion("7b_a-02_west", "7b_a-02", connections_by_region["7b_a-02_west"], locations_by_region["7b_a-02_west"]),
+ "7b_a-02_east": PreRegion("7b_a-02_east", "7b_a-02", connections_by_region["7b_a-02_east"], locations_by_region["7b_a-02_east"]),
- "7b_a-03_bottom": PreRegion("7b_a-03_bottom", "7b_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_a-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_a-03_bottom"]),
- "7b_a-03_top": PreRegion("7b_a-03_top", "7b_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_a-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_a-03_top"]),
+ "7b_a-03_bottom": PreRegion("7b_a-03_bottom", "7b_a-03", connections_by_region["7b_a-03_bottom"], locations_by_region["7b_a-03_bottom"]),
+ "7b_a-03_top": PreRegion("7b_a-03_top", "7b_a-03", connections_by_region["7b_a-03_top"], locations_by_region["7b_a-03_top"]),
- "7b_b-00_bottom": PreRegion("7b_b-00_bottom", "7b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_b-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_b-00_bottom"]),
- "7b_b-00_top": PreRegion("7b_b-00_top", "7b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_b-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_b-00_top"]),
+ "7b_b-00_bottom": PreRegion("7b_b-00_bottom", "7b_b-00", connections_by_region["7b_b-00_bottom"], locations_by_region["7b_b-00_bottom"]),
+ "7b_b-00_top": PreRegion("7b_b-00_top", "7b_b-00", connections_by_region["7b_b-00_top"], locations_by_region["7b_b-00_top"]),
- "7b_b-01_bottom": PreRegion("7b_b-01_bottom", "7b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_b-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_b-01_bottom"]),
- "7b_b-01_top": PreRegion("7b_b-01_top", "7b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_b-01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_b-01_top"]),
+ "7b_b-01_bottom": PreRegion("7b_b-01_bottom", "7b_b-01", connections_by_region["7b_b-01_bottom"], locations_by_region["7b_b-01_bottom"]),
+ "7b_b-01_top": PreRegion("7b_b-01_top", "7b_b-01", connections_by_region["7b_b-01_top"], locations_by_region["7b_b-01_top"]),
- "7b_b-02_west": PreRegion("7b_b-02_west", "7b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_b-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_b-02_west"]),
- "7b_b-02_east": PreRegion("7b_b-02_east", "7b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_b-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_b-02_east"]),
+ "7b_b-02_west": PreRegion("7b_b-02_west", "7b_b-02", connections_by_region["7b_b-02_west"], locations_by_region["7b_b-02_west"]),
+ "7b_b-02_east": PreRegion("7b_b-02_east", "7b_b-02", connections_by_region["7b_b-02_east"], locations_by_region["7b_b-02_east"]),
- "7b_b-03_bottom": PreRegion("7b_b-03_bottom", "7b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_b-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_b-03_bottom"]),
- "7b_b-03_top": PreRegion("7b_b-03_top", "7b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_b-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_b-03_top"]),
+ "7b_b-03_bottom": PreRegion("7b_b-03_bottom", "7b_b-03", connections_by_region["7b_b-03_bottom"], locations_by_region["7b_b-03_bottom"]),
+ "7b_b-03_top": PreRegion("7b_b-03_top", "7b_b-03", connections_by_region["7b_b-03_top"], locations_by_region["7b_b-03_top"]),
- "7b_c-01_west": PreRegion("7b_c-01_west", "7b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_c-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_c-01_west"]),
- "7b_c-01_east": PreRegion("7b_c-01_east", "7b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_c-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_c-01_east"]),
+ "7b_c-01_west": PreRegion("7b_c-01_west", "7b_c-01", connections_by_region["7b_c-01_west"], locations_by_region["7b_c-01_west"]),
+ "7b_c-01_east": PreRegion("7b_c-01_east", "7b_c-01", connections_by_region["7b_c-01_east"], locations_by_region["7b_c-01_east"]),
- "7b_c-00_west": PreRegion("7b_c-00_west", "7b_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_c-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_c-00_west"]),
- "7b_c-00_east": PreRegion("7b_c-00_east", "7b_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_c-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_c-00_east"]),
+ "7b_c-00_west": PreRegion("7b_c-00_west", "7b_c-00", connections_by_region["7b_c-00_west"], locations_by_region["7b_c-00_west"]),
+ "7b_c-00_east": PreRegion("7b_c-00_east", "7b_c-00", connections_by_region["7b_c-00_east"], locations_by_region["7b_c-00_east"]),
- "7b_c-02_west": PreRegion("7b_c-02_west", "7b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_c-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_c-02_west"]),
- "7b_c-02_east": PreRegion("7b_c-02_east", "7b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_c-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_c-02_east"]),
+ "7b_c-02_west": PreRegion("7b_c-02_west", "7b_c-02", connections_by_region["7b_c-02_west"], locations_by_region["7b_c-02_west"]),
+ "7b_c-02_east": PreRegion("7b_c-02_east", "7b_c-02", connections_by_region["7b_c-02_east"], locations_by_region["7b_c-02_east"]),
- "7b_c-03_bottom": PreRegion("7b_c-03_bottom", "7b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_c-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_c-03_bottom"]),
- "7b_c-03_top": PreRegion("7b_c-03_top", "7b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_c-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_c-03_top"]),
+ "7b_c-03_bottom": PreRegion("7b_c-03_bottom", "7b_c-03", connections_by_region["7b_c-03_bottom"], locations_by_region["7b_c-03_bottom"]),
+ "7b_c-03_top": PreRegion("7b_c-03_top", "7b_c-03", connections_by_region["7b_c-03_top"], locations_by_region["7b_c-03_top"]),
- "7b_d-00_west": PreRegion("7b_d-00_west", "7b_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_d-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_d-00_west"]),
- "7b_d-00_east": PreRegion("7b_d-00_east", "7b_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_d-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_d-00_east"]),
+ "7b_d-00_west": PreRegion("7b_d-00_west", "7b_d-00", connections_by_region["7b_d-00_west"], locations_by_region["7b_d-00_west"]),
+ "7b_d-00_east": PreRegion("7b_d-00_east", "7b_d-00", connections_by_region["7b_d-00_east"], locations_by_region["7b_d-00_east"]),
- "7b_d-01_west": PreRegion("7b_d-01_west", "7b_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_d-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_d-01_west"]),
- "7b_d-01_east": PreRegion("7b_d-01_east", "7b_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_d-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_d-01_east"]),
+ "7b_d-01_west": PreRegion("7b_d-01_west", "7b_d-01", connections_by_region["7b_d-01_west"], locations_by_region["7b_d-01_west"]),
+ "7b_d-01_east": PreRegion("7b_d-01_east", "7b_d-01", connections_by_region["7b_d-01_east"], locations_by_region["7b_d-01_east"]),
- "7b_d-02_west": PreRegion("7b_d-02_west", "7b_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_d-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_d-02_west"]),
- "7b_d-02_east": PreRegion("7b_d-02_east", "7b_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_d-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_d-02_east"]),
+ "7b_d-02_west": PreRegion("7b_d-02_west", "7b_d-02", connections_by_region["7b_d-02_west"], locations_by_region["7b_d-02_west"]),
+ "7b_d-02_east": PreRegion("7b_d-02_east", "7b_d-02", connections_by_region["7b_d-02_east"], locations_by_region["7b_d-02_east"]),
- "7b_d-03_bottom": PreRegion("7b_d-03_bottom", "7b_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_d-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_d-03_bottom"]),
- "7b_d-03_top": PreRegion("7b_d-03_top", "7b_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_d-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_d-03_top"]),
+ "7b_d-03_bottom": PreRegion("7b_d-03_bottom", "7b_d-03", connections_by_region["7b_d-03_bottom"], locations_by_region["7b_d-03_bottom"]),
+ "7b_d-03_top": PreRegion("7b_d-03_top", "7b_d-03", connections_by_region["7b_d-03_top"], locations_by_region["7b_d-03_top"]),
- "7b_e-00_west": PreRegion("7b_e-00_west", "7b_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_e-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_e-00_west"]),
- "7b_e-00_east": PreRegion("7b_e-00_east", "7b_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_e-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_e-00_east"]),
+ "7b_e-00_west": PreRegion("7b_e-00_west", "7b_e-00", connections_by_region["7b_e-00_west"], locations_by_region["7b_e-00_west"]),
+ "7b_e-00_east": PreRegion("7b_e-00_east", "7b_e-00", connections_by_region["7b_e-00_east"], locations_by_region["7b_e-00_east"]),
- "7b_e-01_west": PreRegion("7b_e-01_west", "7b_e-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_e-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_e-01_west"]),
- "7b_e-01_east": PreRegion("7b_e-01_east", "7b_e-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_e-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_e-01_east"]),
+ "7b_e-01_west": PreRegion("7b_e-01_west", "7b_e-01", connections_by_region["7b_e-01_west"], locations_by_region["7b_e-01_west"]),
+ "7b_e-01_east": PreRegion("7b_e-01_east", "7b_e-01", connections_by_region["7b_e-01_east"], locations_by_region["7b_e-01_east"]),
- "7b_e-02_west": PreRegion("7b_e-02_west", "7b_e-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_e-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_e-02_west"]),
- "7b_e-02_east": PreRegion("7b_e-02_east", "7b_e-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_e-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_e-02_east"]),
+ "7b_e-02_west": PreRegion("7b_e-02_west", "7b_e-02", connections_by_region["7b_e-02_west"], locations_by_region["7b_e-02_west"]),
+ "7b_e-02_east": PreRegion("7b_e-02_east", "7b_e-02", connections_by_region["7b_e-02_east"], locations_by_region["7b_e-02_east"]),
- "7b_e-03_bottom": PreRegion("7b_e-03_bottom", "7b_e-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_e-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_e-03_bottom"]),
- "7b_e-03_top": PreRegion("7b_e-03_top", "7b_e-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_e-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_e-03_top"]),
+ "7b_e-03_bottom": PreRegion("7b_e-03_bottom", "7b_e-03", connections_by_region["7b_e-03_bottom"], locations_by_region["7b_e-03_bottom"]),
+ "7b_e-03_top": PreRegion("7b_e-03_top", "7b_e-03", connections_by_region["7b_e-03_top"], locations_by_region["7b_e-03_top"]),
- "7b_f-00_west": PreRegion("7b_f-00_west", "7b_f-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_f-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_f-00_west"]),
- "7b_f-00_east": PreRegion("7b_f-00_east", "7b_f-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_f-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_f-00_east"]),
+ "7b_f-00_west": PreRegion("7b_f-00_west", "7b_f-00", connections_by_region["7b_f-00_west"], locations_by_region["7b_f-00_west"]),
+ "7b_f-00_east": PreRegion("7b_f-00_east", "7b_f-00", connections_by_region["7b_f-00_east"], locations_by_region["7b_f-00_east"]),
- "7b_f-01_west": PreRegion("7b_f-01_west", "7b_f-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_f-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_f-01_west"]),
- "7b_f-01_east": PreRegion("7b_f-01_east", "7b_f-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_f-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_f-01_east"]),
+ "7b_f-01_west": PreRegion("7b_f-01_west", "7b_f-01", connections_by_region["7b_f-01_west"], locations_by_region["7b_f-01_west"]),
+ "7b_f-01_east": PreRegion("7b_f-01_east", "7b_f-01", connections_by_region["7b_f-01_east"], locations_by_region["7b_f-01_east"]),
- "7b_f-02_west": PreRegion("7b_f-02_west", "7b_f-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_f-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_f-02_west"]),
- "7b_f-02_east": PreRegion("7b_f-02_east", "7b_f-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_f-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_f-02_east"]),
+ "7b_f-02_west": PreRegion("7b_f-02_west", "7b_f-02", connections_by_region["7b_f-02_west"], locations_by_region["7b_f-02_west"]),
+ "7b_f-02_east": PreRegion("7b_f-02_east", "7b_f-02", connections_by_region["7b_f-02_east"], locations_by_region["7b_f-02_east"]),
- "7b_f-03_bottom": PreRegion("7b_f-03_bottom", "7b_f-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_f-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_f-03_bottom"]),
- "7b_f-03_top": PreRegion("7b_f-03_top", "7b_f-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_f-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_f-03_top"]),
+ "7b_f-03_bottom": PreRegion("7b_f-03_bottom", "7b_f-03", connections_by_region["7b_f-03_bottom"], locations_by_region["7b_f-03_bottom"]),
+ "7b_f-03_top": PreRegion("7b_f-03_top", "7b_f-03", connections_by_region["7b_f-03_top"], locations_by_region["7b_f-03_top"]),
- "7b_g-00_bottom": PreRegion("7b_g-00_bottom", "7b_g-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_g-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_g-00_bottom"]),
- "7b_g-00_top": PreRegion("7b_g-00_top", "7b_g-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_g-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_g-00_top"]),
+ "7b_g-00_bottom": PreRegion("7b_g-00_bottom", "7b_g-00", connections_by_region["7b_g-00_bottom"], locations_by_region["7b_g-00_bottom"]),
+ "7b_g-00_top": PreRegion("7b_g-00_top", "7b_g-00", connections_by_region["7b_g-00_top"], locations_by_region["7b_g-00_top"]),
- "7b_g-01_bottom": PreRegion("7b_g-01_bottom", "7b_g-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_g-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_g-01_bottom"]),
- "7b_g-01_top": PreRegion("7b_g-01_top", "7b_g-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_g-01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_g-01_top"]),
+ "7b_g-01_bottom": PreRegion("7b_g-01_bottom", "7b_g-01", connections_by_region["7b_g-01_bottom"], locations_by_region["7b_g-01_bottom"]),
+ "7b_g-01_top": PreRegion("7b_g-01_top", "7b_g-01", connections_by_region["7b_g-01_top"], locations_by_region["7b_g-01_top"]),
- "7b_g-02_bottom": PreRegion("7b_g-02_bottom", "7b_g-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_g-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_g-02_bottom"]),
- "7b_g-02_top": PreRegion("7b_g-02_top", "7b_g-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_g-02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_g-02_top"]),
+ "7b_g-02_bottom": PreRegion("7b_g-02_bottom", "7b_g-02", connections_by_region["7b_g-02_bottom"], locations_by_region["7b_g-02_bottom"]),
+ "7b_g-02_top": PreRegion("7b_g-02_top", "7b_g-02", connections_by_region["7b_g-02_top"], locations_by_region["7b_g-02_top"]),
- "7b_g-03_bottom": PreRegion("7b_g-03_bottom", "7b_g-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_g-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_g-03_bottom"]),
- "7b_g-03_goal": PreRegion("7b_g-03_goal", "7b_g-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7b_g-03_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "7b_g-03_goal"]),
+ "7b_g-03_bottom": PreRegion("7b_g-03_bottom", "7b_g-03", connections_by_region["7b_g-03_bottom"], locations_by_region["7b_g-03_bottom"]),
+ "7b_g-03_goal": PreRegion("7b_g-03_goal", "7b_g-03", connections_by_region["7b_g-03_goal"], locations_by_region["7b_g-03_goal"]),
- "7c_01_west": PreRegion("7c_01_west", "7c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7c_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7c_01_west"]),
- "7c_01_east": PreRegion("7c_01_east", "7c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7c_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7c_01_east"]),
+ "7c_01_west": PreRegion("7c_01_west", "7c_01", connections_by_region["7c_01_west"], locations_by_region["7c_01_west"]),
+ "7c_01_east": PreRegion("7c_01_east", "7c_01", connections_by_region["7c_01_east"], locations_by_region["7c_01_east"]),
- "7c_02_west": PreRegion("7c_02_west", "7c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7c_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7c_02_west"]),
- "7c_02_east": PreRegion("7c_02_east", "7c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7c_02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "7c_02_east"]),
+ "7c_02_west": PreRegion("7c_02_west", "7c_02", connections_by_region["7c_02_west"], locations_by_region["7c_02_west"]),
+ "7c_02_east": PreRegion("7c_02_east", "7c_02", connections_by_region["7c_02_east"], locations_by_region["7c_02_east"]),
- "7c_03_west": PreRegion("7c_03_west", "7c_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7c_03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "7c_03_west"]),
- "7c_03_goal": PreRegion("7c_03_goal", "7c_03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "7c_03_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "7c_03_goal"]),
+ "7c_03_west": PreRegion("7c_03_west", "7c_03", connections_by_region["7c_03_west"], locations_by_region["7c_03_west"]),
+ "7c_03_goal": PreRegion("7c_03_goal", "7c_03", connections_by_region["7c_03_goal"], locations_by_region["7c_03_goal"]),
- "8a_outside_east": PreRegion("8a_outside_east", "8a_outside", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "8a_outside_east"], [loc for _, loc in all_locations.items() if loc.region_name == "8a_outside_east"]),
+ "8a_outside_east": PreRegion("8a_outside_east", "8a_outside", connections_by_region["8a_outside_east"], locations_by_region["8a_outside_east"]),
- "8a_bridge_west": PreRegion("8a_bridge_west", "8a_bridge", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "8a_bridge_west"], [loc for _, loc in all_locations.items() if loc.region_name == "8a_bridge_west"]),
- "8a_bridge_east": PreRegion("8a_bridge_east", "8a_bridge", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "8a_bridge_east"], [loc for _, loc in all_locations.items() if loc.region_name == "8a_bridge_east"]),
+ "8a_bridge_west": PreRegion("8a_bridge_west", "8a_bridge", connections_by_region["8a_bridge_west"], locations_by_region["8a_bridge_west"]),
+ "8a_bridge_east": PreRegion("8a_bridge_east", "8a_bridge", connections_by_region["8a_bridge_east"], locations_by_region["8a_bridge_east"]),
- "8a_secret_west": PreRegion("8a_secret_west", "8a_secret", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "8a_secret_west"], [loc for _, loc in all_locations.items() if loc.region_name == "8a_secret_west"]),
+ "8a_secret_west": PreRegion("8a_secret_west", "8a_secret", connections_by_region["8a_secret_west"], locations_by_region["8a_secret_west"]),
- "9a_00_west": PreRegion("9a_00_west", "9a_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_00_west"]),
- "9a_00_east": PreRegion("9a_00_east", "9a_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_00_east"]),
+ "9a_00_west": PreRegion("9a_00_west", "9a_00", connections_by_region["9a_00_west"], locations_by_region["9a_00_west"]),
+ "9a_00_east": PreRegion("9a_00_east", "9a_00", connections_by_region["9a_00_east"], locations_by_region["9a_00_east"]),
- "9a_0x_east": PreRegion("9a_0x_east", "9a_0x", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_0x_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_0x_east"]),
+ "9a_0x_east": PreRegion("9a_0x_east", "9a_0x", connections_by_region["9a_0x_east"], locations_by_region["9a_0x_east"]),
- "9a_01_west": PreRegion("9a_01_west", "9a_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_01_west"]),
- "9a_01_east": PreRegion("9a_01_east", "9a_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_01_east"]),
+ "9a_01_west": PreRegion("9a_01_west", "9a_01", connections_by_region["9a_01_west"], locations_by_region["9a_01_west"]),
+ "9a_01_east": PreRegion("9a_01_east", "9a_01", connections_by_region["9a_01_east"], locations_by_region["9a_01_east"]),
- "9a_02_west": PreRegion("9a_02_west", "9a_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_02_west"]),
- "9a_02_east": PreRegion("9a_02_east", "9a_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_02_east"]),
+ "9a_02_west": PreRegion("9a_02_west", "9a_02", connections_by_region["9a_02_west"], locations_by_region["9a_02_west"]),
+ "9a_02_east": PreRegion("9a_02_east", "9a_02", connections_by_region["9a_02_east"], locations_by_region["9a_02_east"]),
- "9a_a-00_west": PreRegion("9a_a-00_west", "9a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_a-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_a-00_west"]),
- "9a_a-00_east": PreRegion("9a_a-00_east", "9a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_a-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_a-00_east"]),
+ "9a_a-00_west": PreRegion("9a_a-00_west", "9a_a-00", connections_by_region["9a_a-00_west"], locations_by_region["9a_a-00_west"]),
+ "9a_a-00_east": PreRegion("9a_a-00_east", "9a_a-00", connections_by_region["9a_a-00_east"], locations_by_region["9a_a-00_east"]),
- "9a_a-01_west": PreRegion("9a_a-01_west", "9a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_a-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_a-01_west"]),
- "9a_a-01_east": PreRegion("9a_a-01_east", "9a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_a-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_a-01_east"]),
+ "9a_a-01_west": PreRegion("9a_a-01_west", "9a_a-01", connections_by_region["9a_a-01_west"], locations_by_region["9a_a-01_west"]),
+ "9a_a-01_east": PreRegion("9a_a-01_east", "9a_a-01", connections_by_region["9a_a-01_east"], locations_by_region["9a_a-01_east"]),
- "9a_a-02_west": PreRegion("9a_a-02_west", "9a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_a-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_a-02_west"]),
- "9a_a-02_east": PreRegion("9a_a-02_east", "9a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_a-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_a-02_east"]),
+ "9a_a-02_west": PreRegion("9a_a-02_west", "9a_a-02", connections_by_region["9a_a-02_west"], locations_by_region["9a_a-02_west"]),
+ "9a_a-02_east": PreRegion("9a_a-02_east", "9a_a-02", connections_by_region["9a_a-02_east"], locations_by_region["9a_a-02_east"]),
- "9a_a-03_bottom": PreRegion("9a_a-03_bottom", "9a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_a-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_a-03_bottom"]),
- "9a_a-03_top": PreRegion("9a_a-03_top", "9a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_a-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_a-03_top"]),
+ "9a_a-03_bottom": PreRegion("9a_a-03_bottom", "9a_a-03", connections_by_region["9a_a-03_bottom"], locations_by_region["9a_a-03_bottom"]),
+ "9a_a-03_top": PreRegion("9a_a-03_top", "9a_a-03", connections_by_region["9a_a-03_top"], locations_by_region["9a_a-03_top"]),
- "9a_b-00_west": PreRegion("9a_b-00_west", "9a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-00_west"]),
- "9a_b-00_south": PreRegion("9a_b-00_south", "9a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-00_south"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-00_south"]),
- "9a_b-00_north": PreRegion("9a_b-00_north", "9a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-00_north"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-00_north"]),
- "9a_b-00_east": PreRegion("9a_b-00_east", "9a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-00_east"]),
+ "9a_b-00_west": PreRegion("9a_b-00_west", "9a_b-00", connections_by_region["9a_b-00_west"], locations_by_region["9a_b-00_west"]),
+ "9a_b-00_south": PreRegion("9a_b-00_south", "9a_b-00", connections_by_region["9a_b-00_south"], locations_by_region["9a_b-00_south"]),
+ "9a_b-00_north": PreRegion("9a_b-00_north", "9a_b-00", connections_by_region["9a_b-00_north"], locations_by_region["9a_b-00_north"]),
+ "9a_b-00_east": PreRegion("9a_b-00_east", "9a_b-00", connections_by_region["9a_b-00_east"], locations_by_region["9a_b-00_east"]),
- "9a_b-01_west": PreRegion("9a_b-01_west", "9a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-01_west"]),
- "9a_b-01_east": PreRegion("9a_b-01_east", "9a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-01_east"]),
+ "9a_b-01_west": PreRegion("9a_b-01_west", "9a_b-01", connections_by_region["9a_b-01_west"], locations_by_region["9a_b-01_west"]),
+ "9a_b-01_east": PreRegion("9a_b-01_east", "9a_b-01", connections_by_region["9a_b-01_east"], locations_by_region["9a_b-01_east"]),
- "9a_b-02_west": PreRegion("9a_b-02_west", "9a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-02_west"]),
- "9a_b-02_east": PreRegion("9a_b-02_east", "9a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-02_east"]),
+ "9a_b-02_west": PreRegion("9a_b-02_west", "9a_b-02", connections_by_region["9a_b-02_west"], locations_by_region["9a_b-02_west"]),
+ "9a_b-02_east": PreRegion("9a_b-02_east", "9a_b-02", connections_by_region["9a_b-02_east"], locations_by_region["9a_b-02_east"]),
- "9a_b-03_west": PreRegion("9a_b-03_west", "9a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-03_west"]),
- "9a_b-03_east": PreRegion("9a_b-03_east", "9a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-03_east"]),
+ "9a_b-03_west": PreRegion("9a_b-03_west", "9a_b-03", connections_by_region["9a_b-03_west"], locations_by_region["9a_b-03_west"]),
+ "9a_b-03_east": PreRegion("9a_b-03_east", "9a_b-03", connections_by_region["9a_b-03_east"], locations_by_region["9a_b-03_east"]),
- "9a_b-04_north-west": PreRegion("9a_b-04_north-west", "9a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-04_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-04_north-west"]),
- "9a_b-04_west": PreRegion("9a_b-04_west", "9a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-04_west"]),
- "9a_b-04_east": PreRegion("9a_b-04_east", "9a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-04_east"]),
+ "9a_b-04_north-west": PreRegion("9a_b-04_north-west", "9a_b-04", connections_by_region["9a_b-04_north-west"], locations_by_region["9a_b-04_north-west"]),
+ "9a_b-04_west": PreRegion("9a_b-04_west", "9a_b-04", connections_by_region["9a_b-04_west"], locations_by_region["9a_b-04_west"]),
+ "9a_b-04_east": PreRegion("9a_b-04_east", "9a_b-04", connections_by_region["9a_b-04_east"], locations_by_region["9a_b-04_east"]),
- "9a_b-05_west": PreRegion("9a_b-05_west", "9a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-05_west"]),
- "9a_b-05_east": PreRegion("9a_b-05_east", "9a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-05_east"]),
+ "9a_b-05_west": PreRegion("9a_b-05_west", "9a_b-05", connections_by_region["9a_b-05_west"], locations_by_region["9a_b-05_west"]),
+ "9a_b-05_east": PreRegion("9a_b-05_east", "9a_b-05", connections_by_region["9a_b-05_east"], locations_by_region["9a_b-05_east"]),
- "9a_b-06_east": PreRegion("9a_b-06_east", "9a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-06_east"]),
+ "9a_b-06_east": PreRegion("9a_b-06_east", "9a_b-06", connections_by_region["9a_b-06_east"], locations_by_region["9a_b-06_east"]),
- "9a_b-07b_bottom": PreRegion("9a_b-07b_bottom", "9a_b-07b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-07b_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-07b_bottom"]),
- "9a_b-07b_top": PreRegion("9a_b-07b_top", "9a_b-07b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-07b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-07b_top"]),
+ "9a_b-07b_bottom": PreRegion("9a_b-07b_bottom", "9a_b-07b", connections_by_region["9a_b-07b_bottom"], locations_by_region["9a_b-07b_bottom"]),
+ "9a_b-07b_top": PreRegion("9a_b-07b_top", "9a_b-07b", connections_by_region["9a_b-07b_top"], locations_by_region["9a_b-07b_top"]),
- "9a_b-07_bottom": PreRegion("9a_b-07_bottom", "9a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-07_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-07_bottom"]),
- "9a_b-07_top": PreRegion("9a_b-07_top", "9a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_b-07_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_b-07_top"]),
+ "9a_b-07_bottom": PreRegion("9a_b-07_bottom", "9a_b-07", connections_by_region["9a_b-07_bottom"], locations_by_region["9a_b-07_bottom"]),
+ "9a_b-07_top": PreRegion("9a_b-07_top", "9a_b-07", connections_by_region["9a_b-07_top"], locations_by_region["9a_b-07_top"]),
- "9a_c-00_west": PreRegion("9a_c-00_west", "9a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-00_west"]),
- "9a_c-00_north-east": PreRegion("9a_c-00_north-east", "9a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-00_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-00_north-east"]),
- "9a_c-00_east": PreRegion("9a_c-00_east", "9a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-00_east"]),
+ "9a_c-00_west": PreRegion("9a_c-00_west", "9a_c-00", connections_by_region["9a_c-00_west"], locations_by_region["9a_c-00_west"]),
+ "9a_c-00_north-east": PreRegion("9a_c-00_north-east", "9a_c-00", connections_by_region["9a_c-00_north-east"], locations_by_region["9a_c-00_north-east"]),
+ "9a_c-00_east": PreRegion("9a_c-00_east", "9a_c-00", connections_by_region["9a_c-00_east"], locations_by_region["9a_c-00_east"]),
- "9a_c-00b_west": PreRegion("9a_c-00b_west", "9a_c-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-00b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-00b_west"]),
+ "9a_c-00b_west": PreRegion("9a_c-00b_west", "9a_c-00b", connections_by_region["9a_c-00b_west"], locations_by_region["9a_c-00b_west"]),
- "9a_c-01_west": PreRegion("9a_c-01_west", "9a_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-01_west"]),
- "9a_c-01_east": PreRegion("9a_c-01_east", "9a_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-01_east"]),
+ "9a_c-01_west": PreRegion("9a_c-01_west", "9a_c-01", connections_by_region["9a_c-01_west"], locations_by_region["9a_c-01_west"]),
+ "9a_c-01_east": PreRegion("9a_c-01_east", "9a_c-01", connections_by_region["9a_c-01_east"], locations_by_region["9a_c-01_east"]),
- "9a_c-02_west": PreRegion("9a_c-02_west", "9a_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-02_west"]),
- "9a_c-02_east": PreRegion("9a_c-02_east", "9a_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-02_east"]),
+ "9a_c-02_west": PreRegion("9a_c-02_west", "9a_c-02", connections_by_region["9a_c-02_west"], locations_by_region["9a_c-02_west"]),
+ "9a_c-02_east": PreRegion("9a_c-02_east", "9a_c-02", connections_by_region["9a_c-02_east"], locations_by_region["9a_c-02_east"]),
- "9a_c-03_west": PreRegion("9a_c-03_west", "9a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-03_west"]),
- "9a_c-03_north-west": PreRegion("9a_c-03_north-west", "9a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-03_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-03_north-west"]),
- "9a_c-03_north": PreRegion("9a_c-03_north", "9a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-03_north"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-03_north"]),
- "9a_c-03_north-east": PreRegion("9a_c-03_north-east", "9a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-03_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-03_north-east"]),
- "9a_c-03_east": PreRegion("9a_c-03_east", "9a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-03_east"]),
+ "9a_c-03_west": PreRegion("9a_c-03_west", "9a_c-03", connections_by_region["9a_c-03_west"], locations_by_region["9a_c-03_west"]),
+ "9a_c-03_north-west": PreRegion("9a_c-03_north-west", "9a_c-03", connections_by_region["9a_c-03_north-west"], locations_by_region["9a_c-03_north-west"]),
+ "9a_c-03_north": PreRegion("9a_c-03_north", "9a_c-03", connections_by_region["9a_c-03_north"], locations_by_region["9a_c-03_north"]),
+ "9a_c-03_north-east": PreRegion("9a_c-03_north-east", "9a_c-03", connections_by_region["9a_c-03_north-east"], locations_by_region["9a_c-03_north-east"]),
+ "9a_c-03_east": PreRegion("9a_c-03_east", "9a_c-03", connections_by_region["9a_c-03_east"], locations_by_region["9a_c-03_east"]),
- "9a_c-03b_west": PreRegion("9a_c-03b_west", "9a_c-03b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-03b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-03b_west"]),
- "9a_c-03b_south": PreRegion("9a_c-03b_south", "9a_c-03b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-03b_south"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-03b_south"]),
- "9a_c-03b_east": PreRegion("9a_c-03b_east", "9a_c-03b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-03b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-03b_east"]),
+ "9a_c-03b_west": PreRegion("9a_c-03b_west", "9a_c-03b", connections_by_region["9a_c-03b_west"], locations_by_region["9a_c-03b_west"]),
+ "9a_c-03b_south": PreRegion("9a_c-03b_south", "9a_c-03b", connections_by_region["9a_c-03b_south"], locations_by_region["9a_c-03b_south"]),
+ "9a_c-03b_east": PreRegion("9a_c-03b_east", "9a_c-03b", connections_by_region["9a_c-03b_east"], locations_by_region["9a_c-03b_east"]),
- "9a_c-04_west": PreRegion("9a_c-04_west", "9a_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-04_west"]),
- "9a_c-04_east": PreRegion("9a_c-04_east", "9a_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_c-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_c-04_east"]),
+ "9a_c-04_west": PreRegion("9a_c-04_west", "9a_c-04", connections_by_region["9a_c-04_west"], locations_by_region["9a_c-04_west"]),
+ "9a_c-04_east": PreRegion("9a_c-04_east", "9a_c-04", connections_by_region["9a_c-04_east"], locations_by_region["9a_c-04_east"]),
- "9a_d-00_bottom": PreRegion("9a_d-00_bottom", "9a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-00_bottom"]),
- "9a_d-00_top": PreRegion("9a_d-00_top", "9a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-00_top"]),
+ "9a_d-00_bottom": PreRegion("9a_d-00_bottom", "9a_d-00", connections_by_region["9a_d-00_bottom"], locations_by_region["9a_d-00_bottom"]),
+ "9a_d-00_top": PreRegion("9a_d-00_top", "9a_d-00", connections_by_region["9a_d-00_top"], locations_by_region["9a_d-00_top"]),
- "9a_d-01_bottom": PreRegion("9a_d-01_bottom", "9a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-01_bottom"]),
- "9a_d-01_top": PreRegion("9a_d-01_top", "9a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-01_top"]),
+ "9a_d-01_bottom": PreRegion("9a_d-01_bottom", "9a_d-01", connections_by_region["9a_d-01_bottom"], locations_by_region["9a_d-01_bottom"]),
+ "9a_d-01_top": PreRegion("9a_d-01_top", "9a_d-01", connections_by_region["9a_d-01_top"], locations_by_region["9a_d-01_top"]),
- "9a_d-02_bottom": PreRegion("9a_d-02_bottom", "9a_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-02_bottom"]),
- "9a_d-02_top": PreRegion("9a_d-02_top", "9a_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-02_top"]),
+ "9a_d-02_bottom": PreRegion("9a_d-02_bottom", "9a_d-02", connections_by_region["9a_d-02_bottom"], locations_by_region["9a_d-02_bottom"]),
+ "9a_d-02_top": PreRegion("9a_d-02_top", "9a_d-02", connections_by_region["9a_d-02_top"], locations_by_region["9a_d-02_top"]),
- "9a_d-03_bottom": PreRegion("9a_d-03_bottom", "9a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-03_bottom"]),
- "9a_d-03_top": PreRegion("9a_d-03_top", "9a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-03_top"]),
+ "9a_d-03_bottom": PreRegion("9a_d-03_bottom", "9a_d-03", connections_by_region["9a_d-03_bottom"], locations_by_region["9a_d-03_bottom"]),
+ "9a_d-03_top": PreRegion("9a_d-03_top", "9a_d-03", connections_by_region["9a_d-03_top"], locations_by_region["9a_d-03_top"]),
- "9a_d-04_bottom": PreRegion("9a_d-04_bottom", "9a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-04_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-04_bottom"]),
- "9a_d-04_top": PreRegion("9a_d-04_top", "9a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-04_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-04_top"]),
+ "9a_d-04_bottom": PreRegion("9a_d-04_bottom", "9a_d-04", connections_by_region["9a_d-04_bottom"], locations_by_region["9a_d-04_bottom"]),
+ "9a_d-04_top": PreRegion("9a_d-04_top", "9a_d-04", connections_by_region["9a_d-04_top"], locations_by_region["9a_d-04_top"]),
- "9a_d-05_bottom": PreRegion("9a_d-05_bottom", "9a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-05_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-05_bottom"]),
- "9a_d-05_top": PreRegion("9a_d-05_top", "9a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-05_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-05_top"]),
+ "9a_d-05_bottom": PreRegion("9a_d-05_bottom", "9a_d-05", connections_by_region["9a_d-05_bottom"], locations_by_region["9a_d-05_bottom"]),
+ "9a_d-05_top": PreRegion("9a_d-05_top", "9a_d-05", connections_by_region["9a_d-05_top"], locations_by_region["9a_d-05_top"]),
- "9a_d-06_bottom": PreRegion("9a_d-06_bottom", "9a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-06_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-06_bottom"]),
- "9a_d-06_top": PreRegion("9a_d-06_top", "9a_d-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-06_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-06_top"]),
+ "9a_d-06_bottom": PreRegion("9a_d-06_bottom", "9a_d-06", connections_by_region["9a_d-06_bottom"], locations_by_region["9a_d-06_bottom"]),
+ "9a_d-06_top": PreRegion("9a_d-06_top", "9a_d-06", connections_by_region["9a_d-06_top"], locations_by_region["9a_d-06_top"]),
- "9a_d-07_bottom": PreRegion("9a_d-07_bottom", "9a_d-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-07_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-07_bottom"]),
- "9a_d-07_top": PreRegion("9a_d-07_top", "9a_d-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-07_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-07_top"]),
+ "9a_d-07_bottom": PreRegion("9a_d-07_bottom", "9a_d-07", connections_by_region["9a_d-07_bottom"], locations_by_region["9a_d-07_bottom"]),
+ "9a_d-07_top": PreRegion("9a_d-07_top", "9a_d-07", connections_by_region["9a_d-07_top"], locations_by_region["9a_d-07_top"]),
- "9a_d-08_west": PreRegion("9a_d-08_west", "9a_d-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-08_west"]),
- "9a_d-08_east": PreRegion("9a_d-08_east", "9a_d-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-08_east"]),
+ "9a_d-08_west": PreRegion("9a_d-08_west", "9a_d-08", connections_by_region["9a_d-08_west"], locations_by_region["9a_d-08_west"]),
+ "9a_d-08_east": PreRegion("9a_d-08_east", "9a_d-08", connections_by_region["9a_d-08_east"], locations_by_region["9a_d-08_east"]),
- "9a_d-09_west": PreRegion("9a_d-09_west", "9a_d-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-09_west"]),
- "9a_d-09_east": PreRegion("9a_d-09_east", "9a_d-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-09_east"]),
+ "9a_d-09_west": PreRegion("9a_d-09_west", "9a_d-09", connections_by_region["9a_d-09_west"], locations_by_region["9a_d-09_west"]),
+ "9a_d-09_east": PreRegion("9a_d-09_east", "9a_d-09", connections_by_region["9a_d-09_east"], locations_by_region["9a_d-09_east"]),
- "9a_d-10_west": PreRegion("9a_d-10_west", "9a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-10_west"]),
- "9a_d-10_east": PreRegion("9a_d-10_east", "9a_d-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-10_east"]),
+ "9a_d-10_west": PreRegion("9a_d-10_west", "9a_d-10", connections_by_region["9a_d-10_west"], locations_by_region["9a_d-10_west"]),
+ "9a_d-10_east": PreRegion("9a_d-10_east", "9a_d-10", connections_by_region["9a_d-10_east"], locations_by_region["9a_d-10_east"]),
- "9a_d-10b_west": PreRegion("9a_d-10b_west", "9a_d-10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-10b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-10b_west"]),
- "9a_d-10b_east": PreRegion("9a_d-10b_east", "9a_d-10b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-10b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-10b_east"]),
+ "9a_d-10b_west": PreRegion("9a_d-10b_west", "9a_d-10b", connections_by_region["9a_d-10b_west"], locations_by_region["9a_d-10b_west"]),
+ "9a_d-10b_east": PreRegion("9a_d-10b_east", "9a_d-10b", connections_by_region["9a_d-10b_east"], locations_by_region["9a_d-10b_east"]),
- "9a_d-10c_west": PreRegion("9a_d-10c_west", "9a_d-10c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-10c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-10c_west"]),
- "9a_d-10c_east": PreRegion("9a_d-10c_east", "9a_d-10c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-10c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-10c_east"]),
+ "9a_d-10c_west": PreRegion("9a_d-10c_west", "9a_d-10c", connections_by_region["9a_d-10c_west"], locations_by_region["9a_d-10c_west"]),
+ "9a_d-10c_east": PreRegion("9a_d-10c_east", "9a_d-10c", connections_by_region["9a_d-10c_east"], locations_by_region["9a_d-10c_east"]),
- "9a_d-11_west": PreRegion("9a_d-11_west", "9a_d-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-11_west"]),
- "9a_d-11_center": PreRegion("9a_d-11_center", "9a_d-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-11_center"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-11_center"]),
- "9a_d-11_east": PreRegion("9a_d-11_east", "9a_d-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_d-11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_d-11_east"]),
+ "9a_d-11_west": PreRegion("9a_d-11_west", "9a_d-11", connections_by_region["9a_d-11_west"], locations_by_region["9a_d-11_west"]),
+ "9a_d-11_center": PreRegion("9a_d-11_center", "9a_d-11", connections_by_region["9a_d-11_center"], locations_by_region["9a_d-11_center"]),
+ "9a_d-11_east": PreRegion("9a_d-11_east", "9a_d-11", connections_by_region["9a_d-11_east"], locations_by_region["9a_d-11_east"]),
- "9a_space_west": PreRegion("9a_space_west", "9a_space", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_space_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_space_west"]),
- "9a_space_goal": PreRegion("9a_space_goal", "9a_space", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9a_space_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "9a_space_goal"]),
+ "9a_space_west": PreRegion("9a_space_west", "9a_space", connections_by_region["9a_space_west"], locations_by_region["9a_space_west"]),
+ "9a_space_goal": PreRegion("9a_space_goal", "9a_space", connections_by_region["9a_space_goal"], locations_by_region["9a_space_goal"]),
- "9b_00_east": PreRegion("9b_00_east", "9b_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_00_east"]),
+ "9b_00_east": PreRegion("9b_00_east", "9b_00", connections_by_region["9b_00_east"], locations_by_region["9b_00_east"]),
- "9b_01_west": PreRegion("9b_01_west", "9b_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_01_west"]),
- "9b_01_east": PreRegion("9b_01_east", "9b_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_01_east"]),
+ "9b_01_west": PreRegion("9b_01_west", "9b_01", connections_by_region["9b_01_west"], locations_by_region["9b_01_west"]),
+ "9b_01_east": PreRegion("9b_01_east", "9b_01", connections_by_region["9b_01_east"], locations_by_region["9b_01_east"]),
- "9b_a-00_west": PreRegion("9b_a-00_west", "9b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-00_west"]),
- "9b_a-00_east": PreRegion("9b_a-00_east", "9b_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-00_east"]),
+ "9b_a-00_west": PreRegion("9b_a-00_west", "9b_a-00", connections_by_region["9b_a-00_west"], locations_by_region["9b_a-00_west"]),
+ "9b_a-00_east": PreRegion("9b_a-00_east", "9b_a-00", connections_by_region["9b_a-00_east"], locations_by_region["9b_a-00_east"]),
- "9b_a-01_west": PreRegion("9b_a-01_west", "9b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-01_west"]),
- "9b_a-01_east": PreRegion("9b_a-01_east", "9b_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-01_east"]),
+ "9b_a-01_west": PreRegion("9b_a-01_west", "9b_a-01", connections_by_region["9b_a-01_west"], locations_by_region["9b_a-01_west"]),
+ "9b_a-01_east": PreRegion("9b_a-01_east", "9b_a-01", connections_by_region["9b_a-01_east"], locations_by_region["9b_a-01_east"]),
- "9b_a-02_west": PreRegion("9b_a-02_west", "9b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-02_west"]),
- "9b_a-02_east": PreRegion("9b_a-02_east", "9b_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-02_east"]),
+ "9b_a-02_west": PreRegion("9b_a-02_west", "9b_a-02", connections_by_region["9b_a-02_west"], locations_by_region["9b_a-02_west"]),
+ "9b_a-02_east": PreRegion("9b_a-02_east", "9b_a-02", connections_by_region["9b_a-02_east"], locations_by_region["9b_a-02_east"]),
- "9b_a-03_west": PreRegion("9b_a-03_west", "9b_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-03_west"]),
- "9b_a-03_east": PreRegion("9b_a-03_east", "9b_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-03_east"]),
+ "9b_a-03_west": PreRegion("9b_a-03_west", "9b_a-03", connections_by_region["9b_a-03_west"], locations_by_region["9b_a-03_west"]),
+ "9b_a-03_east": PreRegion("9b_a-03_east", "9b_a-03", connections_by_region["9b_a-03_east"], locations_by_region["9b_a-03_east"]),
- "9b_a-04_west": PreRegion("9b_a-04_west", "9b_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-04_west"]),
- "9b_a-04_east": PreRegion("9b_a-04_east", "9b_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-04_east"]),
+ "9b_a-04_west": PreRegion("9b_a-04_west", "9b_a-04", connections_by_region["9b_a-04_west"], locations_by_region["9b_a-04_west"]),
+ "9b_a-04_east": PreRegion("9b_a-04_east", "9b_a-04", connections_by_region["9b_a-04_east"], locations_by_region["9b_a-04_east"]),
- "9b_a-05_west": PreRegion("9b_a-05_west", "9b_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-05_west"]),
- "9b_a-05_east": PreRegion("9b_a-05_east", "9b_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_a-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_a-05_east"]),
+ "9b_a-05_west": PreRegion("9b_a-05_west", "9b_a-05", connections_by_region["9b_a-05_west"], locations_by_region["9b_a-05_west"]),
+ "9b_a-05_east": PreRegion("9b_a-05_east", "9b_a-05", connections_by_region["9b_a-05_east"], locations_by_region["9b_a-05_east"]),
- "9b_b-00_west": PreRegion("9b_b-00_west", "9b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-00_west"]),
- "9b_b-00_east": PreRegion("9b_b-00_east", "9b_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-00_east"]),
+ "9b_b-00_west": PreRegion("9b_b-00_west", "9b_b-00", connections_by_region["9b_b-00_west"], locations_by_region["9b_b-00_west"]),
+ "9b_b-00_east": PreRegion("9b_b-00_east", "9b_b-00", connections_by_region["9b_b-00_east"], locations_by_region["9b_b-00_east"]),
- "9b_b-01_west": PreRegion("9b_b-01_west", "9b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-01_west"]),
- "9b_b-01_east": PreRegion("9b_b-01_east", "9b_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-01_east"]),
+ "9b_b-01_west": PreRegion("9b_b-01_west", "9b_b-01", connections_by_region["9b_b-01_west"], locations_by_region["9b_b-01_west"]),
+ "9b_b-01_east": PreRegion("9b_b-01_east", "9b_b-01", connections_by_region["9b_b-01_east"], locations_by_region["9b_b-01_east"]),
- "9b_b-02_west": PreRegion("9b_b-02_west", "9b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-02_west"]),
- "9b_b-02_east": PreRegion("9b_b-02_east", "9b_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-02_east"]),
+ "9b_b-02_west": PreRegion("9b_b-02_west", "9b_b-02", connections_by_region["9b_b-02_west"], locations_by_region["9b_b-02_west"]),
+ "9b_b-02_east": PreRegion("9b_b-02_east", "9b_b-02", connections_by_region["9b_b-02_east"], locations_by_region["9b_b-02_east"]),
- "9b_b-03_west": PreRegion("9b_b-03_west", "9b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-03_west"]),
- "9b_b-03_east": PreRegion("9b_b-03_east", "9b_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-03_east"]),
+ "9b_b-03_west": PreRegion("9b_b-03_west", "9b_b-03", connections_by_region["9b_b-03_west"], locations_by_region["9b_b-03_west"]),
+ "9b_b-03_east": PreRegion("9b_b-03_east", "9b_b-03", connections_by_region["9b_b-03_east"], locations_by_region["9b_b-03_east"]),
- "9b_b-04_west": PreRegion("9b_b-04_west", "9b_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-04_west"]),
- "9b_b-04_east": PreRegion("9b_b-04_east", "9b_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-04_east"]),
+ "9b_b-04_west": PreRegion("9b_b-04_west", "9b_b-04", connections_by_region["9b_b-04_west"], locations_by_region["9b_b-04_west"]),
+ "9b_b-04_east": PreRegion("9b_b-04_east", "9b_b-04", connections_by_region["9b_b-04_east"], locations_by_region["9b_b-04_east"]),
- "9b_b-05_west": PreRegion("9b_b-05_west", "9b_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-05_west"]),
- "9b_b-05_east": PreRegion("9b_b-05_east", "9b_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_b-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_b-05_east"]),
+ "9b_b-05_west": PreRegion("9b_b-05_west", "9b_b-05", connections_by_region["9b_b-05_west"], locations_by_region["9b_b-05_west"]),
+ "9b_b-05_east": PreRegion("9b_b-05_east", "9b_b-05", connections_by_region["9b_b-05_east"], locations_by_region["9b_b-05_east"]),
- "9b_c-01_bottom": PreRegion("9b_c-01_bottom", "9b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-01_bottom"]),
- "9b_c-01_top": PreRegion("9b_c-01_top", "9b_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-01_top"]),
+ "9b_c-01_bottom": PreRegion("9b_c-01_bottom", "9b_c-01", connections_by_region["9b_c-01_bottom"], locations_by_region["9b_c-01_bottom"]),
+ "9b_c-01_top": PreRegion("9b_c-01_top", "9b_c-01", connections_by_region["9b_c-01_top"], locations_by_region["9b_c-01_top"]),
- "9b_c-02_bottom": PreRegion("9b_c-02_bottom", "9b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-02_bottom"]),
- "9b_c-02_top": PreRegion("9b_c-02_top", "9b_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-02_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-02_top"]),
+ "9b_c-02_bottom": PreRegion("9b_c-02_bottom", "9b_c-02", connections_by_region["9b_c-02_bottom"], locations_by_region["9b_c-02_bottom"]),
+ "9b_c-02_top": PreRegion("9b_c-02_top", "9b_c-02", connections_by_region["9b_c-02_top"], locations_by_region["9b_c-02_top"]),
- "9b_c-03_bottom": PreRegion("9b_c-03_bottom", "9b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-03_bottom"]),
- "9b_c-03_top": PreRegion("9b_c-03_top", "9b_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-03_top"]),
+ "9b_c-03_bottom": PreRegion("9b_c-03_bottom", "9b_c-03", connections_by_region["9b_c-03_bottom"], locations_by_region["9b_c-03_bottom"]),
+ "9b_c-03_top": PreRegion("9b_c-03_top", "9b_c-03", connections_by_region["9b_c-03_top"], locations_by_region["9b_c-03_top"]),
- "9b_c-04_bottom": PreRegion("9b_c-04_bottom", "9b_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-04_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-04_bottom"]),
- "9b_c-04_top": PreRegion("9b_c-04_top", "9b_c-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-04_top"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-04_top"]),
+ "9b_c-04_bottom": PreRegion("9b_c-04_bottom", "9b_c-04", connections_by_region["9b_c-04_bottom"], locations_by_region["9b_c-04_bottom"]),
+ "9b_c-04_top": PreRegion("9b_c-04_top", "9b_c-04", connections_by_region["9b_c-04_top"], locations_by_region["9b_c-04_top"]),
- "9b_c-05_west": PreRegion("9b_c-05_west", "9b_c-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-05_west"]),
- "9b_c-05_east": PreRegion("9b_c-05_east", "9b_c-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-05_east"]),
+ "9b_c-05_west": PreRegion("9b_c-05_west", "9b_c-05", connections_by_region["9b_c-05_west"], locations_by_region["9b_c-05_west"]),
+ "9b_c-05_east": PreRegion("9b_c-05_east", "9b_c-05", connections_by_region["9b_c-05_east"], locations_by_region["9b_c-05_east"]),
- "9b_c-06_west": PreRegion("9b_c-06_west", "9b_c-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-06_west"]),
- "9b_c-06_east": PreRegion("9b_c-06_east", "9b_c-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-06_east"]),
+ "9b_c-06_west": PreRegion("9b_c-06_west", "9b_c-06", connections_by_region["9b_c-06_west"], locations_by_region["9b_c-06_west"]),
+ "9b_c-06_east": PreRegion("9b_c-06_east", "9b_c-06", connections_by_region["9b_c-06_east"], locations_by_region["9b_c-06_east"]),
- "9b_c-08_west": PreRegion("9b_c-08_west", "9b_c-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-08_west"]),
- "9b_c-08_east": PreRegion("9b_c-08_east", "9b_c-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-08_east"]),
+ "9b_c-08_west": PreRegion("9b_c-08_west", "9b_c-08", connections_by_region["9b_c-08_west"], locations_by_region["9b_c-08_west"]),
+ "9b_c-08_east": PreRegion("9b_c-08_east", "9b_c-08", connections_by_region["9b_c-08_east"], locations_by_region["9b_c-08_east"]),
- "9b_c-07_west": PreRegion("9b_c-07_west", "9b_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-07_west"]),
- "9b_c-07_east": PreRegion("9b_c-07_east", "9b_c-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_c-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_c-07_east"]),
+ "9b_c-07_west": PreRegion("9b_c-07_west", "9b_c-07", connections_by_region["9b_c-07_west"], locations_by_region["9b_c-07_west"]),
+ "9b_c-07_east": PreRegion("9b_c-07_east", "9b_c-07", connections_by_region["9b_c-07_east"], locations_by_region["9b_c-07_east"]),
- "9b_space_west": PreRegion("9b_space_west", "9b_space", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_space_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_space_west"]),
- "9b_space_goal": PreRegion("9b_space_goal", "9b_space", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9b_space_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "9b_space_goal"]),
+ "9b_space_west": PreRegion("9b_space_west", "9b_space", connections_by_region["9b_space_west"], locations_by_region["9b_space_west"]),
+ "9b_space_goal": PreRegion("9b_space_goal", "9b_space", connections_by_region["9b_space_goal"], locations_by_region["9b_space_goal"]),
- "9c_intro_west": PreRegion("9c_intro_west", "9c_intro", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9c_intro_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9c_intro_west"]),
- "9c_intro_east": PreRegion("9c_intro_east", "9c_intro", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9c_intro_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9c_intro_east"]),
+ "9c_intro_west": PreRegion("9c_intro_west", "9c_intro", connections_by_region["9c_intro_west"], locations_by_region["9c_intro_west"]),
+ "9c_intro_east": PreRegion("9c_intro_east", "9c_intro", connections_by_region["9c_intro_east"], locations_by_region["9c_intro_east"]),
- "9c_00_west": PreRegion("9c_00_west", "9c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9c_00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9c_00_west"]),
- "9c_00_east": PreRegion("9c_00_east", "9c_00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9c_00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9c_00_east"]),
+ "9c_00_west": PreRegion("9c_00_west", "9c_00", connections_by_region["9c_00_west"], locations_by_region["9c_00_west"]),
+ "9c_00_east": PreRegion("9c_00_east", "9c_00", connections_by_region["9c_00_east"], locations_by_region["9c_00_east"]),
- "9c_01_west": PreRegion("9c_01_west", "9c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9c_01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9c_01_west"]),
- "9c_01_east": PreRegion("9c_01_east", "9c_01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9c_01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "9c_01_east"]),
+ "9c_01_west": PreRegion("9c_01_west", "9c_01", connections_by_region["9c_01_west"], locations_by_region["9c_01_west"]),
+ "9c_01_east": PreRegion("9c_01_east", "9c_01", connections_by_region["9c_01_east"], locations_by_region["9c_01_east"]),
- "9c_02_west": PreRegion("9c_02_west", "9c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9c_02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "9c_02_west"]),
- "9c_02_goal": PreRegion("9c_02_goal", "9c_02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "9c_02_goal"], [loc for _, loc in all_locations.items() if loc.region_name == "9c_02_goal"]),
+ "9c_02_west": PreRegion("9c_02_west", "9c_02", connections_by_region["9c_02_west"], locations_by_region["9c_02_west"]),
+ "9c_02_goal": PreRegion("9c_02_goal", "9c_02", connections_by_region["9c_02_goal"], locations_by_region["9c_02_goal"]),
- "10a_intro-00-past_west": PreRegion("10a_intro-00-past_west", "10a_intro-00-past", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_intro-00-past_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_intro-00-past_west"]),
- "10a_intro-00-past_east": PreRegion("10a_intro-00-past_east", "10a_intro-00-past", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_intro-00-past_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_intro-00-past_east"]),
+ "10a_intro-00-past_west": PreRegion("10a_intro-00-past_west", "10a_intro-00-past", connections_by_region["10a_intro-00-past_west"], locations_by_region["10a_intro-00-past_west"]),
+ "10a_intro-00-past_east": PreRegion("10a_intro-00-past_east", "10a_intro-00-past", connections_by_region["10a_intro-00-past_east"], locations_by_region["10a_intro-00-past_east"]),
- "10a_intro-01-future_west": PreRegion("10a_intro-01-future_west", "10a_intro-01-future", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_intro-01-future_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_intro-01-future_west"]),
- "10a_intro-01-future_east": PreRegion("10a_intro-01-future_east", "10a_intro-01-future", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_intro-01-future_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_intro-01-future_east"]),
+ "10a_intro-01-future_west": PreRegion("10a_intro-01-future_west", "10a_intro-01-future", connections_by_region["10a_intro-01-future_west"], locations_by_region["10a_intro-01-future_west"]),
+ "10a_intro-01-future_east": PreRegion("10a_intro-01-future_east", "10a_intro-01-future", connections_by_region["10a_intro-01-future_east"], locations_by_region["10a_intro-01-future_east"]),
- "10a_intro-02-launch_bottom": PreRegion("10a_intro-02-launch_bottom", "10a_intro-02-launch", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_intro-02-launch_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_intro-02-launch_bottom"]),
- "10a_intro-02-launch_top": PreRegion("10a_intro-02-launch_top", "10a_intro-02-launch", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_intro-02-launch_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_intro-02-launch_top"]),
+ "10a_intro-02-launch_bottom": PreRegion("10a_intro-02-launch_bottom", "10a_intro-02-launch", connections_by_region["10a_intro-02-launch_bottom"], locations_by_region["10a_intro-02-launch_bottom"]),
+ "10a_intro-02-launch_top": PreRegion("10a_intro-02-launch_top", "10a_intro-02-launch", connections_by_region["10a_intro-02-launch_top"], locations_by_region["10a_intro-02-launch_top"]),
- "10a_intro-03-space_west": PreRegion("10a_intro-03-space_west", "10a_intro-03-space", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_intro-03-space_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_intro-03-space_west"]),
- "10a_intro-03-space_east": PreRegion("10a_intro-03-space_east", "10a_intro-03-space", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_intro-03-space_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_intro-03-space_east"]),
+ "10a_intro-03-space_west": PreRegion("10a_intro-03-space_west", "10a_intro-03-space", connections_by_region["10a_intro-03-space_west"], locations_by_region["10a_intro-03-space_west"]),
+ "10a_intro-03-space_east": PreRegion("10a_intro-03-space_east", "10a_intro-03-space", connections_by_region["10a_intro-03-space_east"], locations_by_region["10a_intro-03-space_east"]),
- "10a_a-00_west": PreRegion("10a_a-00_west", "10a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-00_west"]),
- "10a_a-00_east": PreRegion("10a_a-00_east", "10a_a-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-00_east"]),
+ "10a_a-00_west": PreRegion("10a_a-00_west", "10a_a-00", connections_by_region["10a_a-00_west"], locations_by_region["10a_a-00_west"]),
+ "10a_a-00_east": PreRegion("10a_a-00_east", "10a_a-00", connections_by_region["10a_a-00_east"], locations_by_region["10a_a-00_east"]),
- "10a_a-01_west": PreRegion("10a_a-01_west", "10a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-01_west"]),
- "10a_a-01_east": PreRegion("10a_a-01_east", "10a_a-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-01_east"]),
+ "10a_a-01_west": PreRegion("10a_a-01_west", "10a_a-01", connections_by_region["10a_a-01_west"], locations_by_region["10a_a-01_west"]),
+ "10a_a-01_east": PreRegion("10a_a-01_east", "10a_a-01", connections_by_region["10a_a-01_east"], locations_by_region["10a_a-01_east"]),
- "10a_a-02_west": PreRegion("10a_a-02_west", "10a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-02_west"]),
- "10a_a-02_east": PreRegion("10a_a-02_east", "10a_a-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-02_east"]),
+ "10a_a-02_west": PreRegion("10a_a-02_west", "10a_a-02", connections_by_region["10a_a-02_west"], locations_by_region["10a_a-02_west"]),
+ "10a_a-02_east": PreRegion("10a_a-02_east", "10a_a-02", connections_by_region["10a_a-02_east"], locations_by_region["10a_a-02_east"]),
- "10a_a-03_west": PreRegion("10a_a-03_west", "10a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-03_west"]),
- "10a_a-03_east": PreRegion("10a_a-03_east", "10a_a-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-03_east"]),
+ "10a_a-03_west": PreRegion("10a_a-03_west", "10a_a-03", connections_by_region["10a_a-03_west"], locations_by_region["10a_a-03_west"]),
+ "10a_a-03_east": PreRegion("10a_a-03_east", "10a_a-03", connections_by_region["10a_a-03_east"], locations_by_region["10a_a-03_east"]),
- "10a_a-04_west": PreRegion("10a_a-04_west", "10a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-04_west"]),
- "10a_a-04_east": PreRegion("10a_a-04_east", "10a_a-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-04_east"]),
+ "10a_a-04_west": PreRegion("10a_a-04_west", "10a_a-04", connections_by_region["10a_a-04_west"], locations_by_region["10a_a-04_west"]),
+ "10a_a-04_east": PreRegion("10a_a-04_east", "10a_a-04", connections_by_region["10a_a-04_east"], locations_by_region["10a_a-04_east"]),
- "10a_a-05_west": PreRegion("10a_a-05_west", "10a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-05_west"]),
- "10a_a-05_east": PreRegion("10a_a-05_east", "10a_a-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_a-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_a-05_east"]),
+ "10a_a-05_west": PreRegion("10a_a-05_west", "10a_a-05", connections_by_region["10a_a-05_west"], locations_by_region["10a_a-05_west"]),
+ "10a_a-05_east": PreRegion("10a_a-05_east", "10a_a-05", connections_by_region["10a_a-05_east"], locations_by_region["10a_a-05_east"]),
- "10a_b-00_west": PreRegion("10a_b-00_west", "10a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-00_west"]),
- "10a_b-00_east": PreRegion("10a_b-00_east", "10a_b-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-00_east"]),
+ "10a_b-00_west": PreRegion("10a_b-00_west", "10a_b-00", connections_by_region["10a_b-00_west"], locations_by_region["10a_b-00_west"]),
+ "10a_b-00_east": PreRegion("10a_b-00_east", "10a_b-00", connections_by_region["10a_b-00_east"], locations_by_region["10a_b-00_east"]),
- "10a_b-01_west": PreRegion("10a_b-01_west", "10a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-01_west"]),
- "10a_b-01_east": PreRegion("10a_b-01_east", "10a_b-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-01_east"]),
+ "10a_b-01_west": PreRegion("10a_b-01_west", "10a_b-01", connections_by_region["10a_b-01_west"], locations_by_region["10a_b-01_west"]),
+ "10a_b-01_east": PreRegion("10a_b-01_east", "10a_b-01", connections_by_region["10a_b-01_east"], locations_by_region["10a_b-01_east"]),
- "10a_b-02_west": PreRegion("10a_b-02_west", "10a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-02_west"]),
- "10a_b-02_east": PreRegion("10a_b-02_east", "10a_b-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-02_east"]),
+ "10a_b-02_west": PreRegion("10a_b-02_west", "10a_b-02", connections_by_region["10a_b-02_west"], locations_by_region["10a_b-02_west"]),
+ "10a_b-02_east": PreRegion("10a_b-02_east", "10a_b-02", connections_by_region["10a_b-02_east"], locations_by_region["10a_b-02_east"]),
- "10a_b-03_west": PreRegion("10a_b-03_west", "10a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-03_west"]),
- "10a_b-03_east": PreRegion("10a_b-03_east", "10a_b-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-03_east"]),
+ "10a_b-03_west": PreRegion("10a_b-03_west", "10a_b-03", connections_by_region["10a_b-03_west"], locations_by_region["10a_b-03_west"]),
+ "10a_b-03_east": PreRegion("10a_b-03_east", "10a_b-03", connections_by_region["10a_b-03_east"], locations_by_region["10a_b-03_east"]),
- "10a_b-04_west": PreRegion("10a_b-04_west", "10a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-04_west"]),
- "10a_b-04_east": PreRegion("10a_b-04_east", "10a_b-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-04_east"]),
+ "10a_b-04_west": PreRegion("10a_b-04_west", "10a_b-04", connections_by_region["10a_b-04_west"], locations_by_region["10a_b-04_west"]),
+ "10a_b-04_east": PreRegion("10a_b-04_east", "10a_b-04", connections_by_region["10a_b-04_east"], locations_by_region["10a_b-04_east"]),
- "10a_b-05_west": PreRegion("10a_b-05_west", "10a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-05_west"]),
- "10a_b-05_east": PreRegion("10a_b-05_east", "10a_b-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-05_east"]),
+ "10a_b-05_west": PreRegion("10a_b-05_west", "10a_b-05", connections_by_region["10a_b-05_west"], locations_by_region["10a_b-05_west"]),
+ "10a_b-05_east": PreRegion("10a_b-05_east", "10a_b-05", connections_by_region["10a_b-05_east"], locations_by_region["10a_b-05_east"]),
- "10a_b-06_west": PreRegion("10a_b-06_west", "10a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-06_west"]),
- "10a_b-06_east": PreRegion("10a_b-06_east", "10a_b-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-06_east"]),
+ "10a_b-06_west": PreRegion("10a_b-06_west", "10a_b-06", connections_by_region["10a_b-06_west"], locations_by_region["10a_b-06_west"]),
+ "10a_b-06_east": PreRegion("10a_b-06_east", "10a_b-06", connections_by_region["10a_b-06_east"], locations_by_region["10a_b-06_east"]),
- "10a_b-07_west": PreRegion("10a_b-07_west", "10a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-07_west"]),
- "10a_b-07_east": PreRegion("10a_b-07_east", "10a_b-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_b-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_b-07_east"]),
+ "10a_b-07_west": PreRegion("10a_b-07_west", "10a_b-07", connections_by_region["10a_b-07_west"], locations_by_region["10a_b-07_west"]),
+ "10a_b-07_east": PreRegion("10a_b-07_east", "10a_b-07", connections_by_region["10a_b-07_east"], locations_by_region["10a_b-07_east"]),
- "10a_c-00_west": PreRegion("10a_c-00_west", "10a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-00_west"]),
- "10a_c-00_east": PreRegion("10a_c-00_east", "10a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-00_east"]),
- "10a_c-00_north-east": PreRegion("10a_c-00_north-east", "10a_c-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-00_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-00_north-east"]),
+ "10a_c-00_west": PreRegion("10a_c-00_west", "10a_c-00", connections_by_region["10a_c-00_west"], locations_by_region["10a_c-00_west"]),
+ "10a_c-00_east": PreRegion("10a_c-00_east", "10a_c-00", connections_by_region["10a_c-00_east"], locations_by_region["10a_c-00_east"]),
+ "10a_c-00_north-east": PreRegion("10a_c-00_north-east", "10a_c-00", connections_by_region["10a_c-00_north-east"], locations_by_region["10a_c-00_north-east"]),
- "10a_c-00b_west": PreRegion("10a_c-00b_west", "10a_c-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-00b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-00b_west"]),
- "10a_c-00b_east": PreRegion("10a_c-00b_east", "10a_c-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-00b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-00b_east"]),
+ "10a_c-00b_west": PreRegion("10a_c-00b_west", "10a_c-00b", connections_by_region["10a_c-00b_west"], locations_by_region["10a_c-00b_west"]),
+ "10a_c-00b_east": PreRegion("10a_c-00b_east", "10a_c-00b", connections_by_region["10a_c-00b_east"], locations_by_region["10a_c-00b_east"]),
- "10a_c-01_west": PreRegion("10a_c-01_west", "10a_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-01_west"]),
- "10a_c-01_east": PreRegion("10a_c-01_east", "10a_c-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-01_east"]),
+ "10a_c-01_west": PreRegion("10a_c-01_west", "10a_c-01", connections_by_region["10a_c-01_west"], locations_by_region["10a_c-01_west"]),
+ "10a_c-01_east": PreRegion("10a_c-01_east", "10a_c-01", connections_by_region["10a_c-01_east"], locations_by_region["10a_c-01_east"]),
- "10a_c-02_west": PreRegion("10a_c-02_west", "10a_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-02_west"]),
- "10a_c-02_east": PreRegion("10a_c-02_east", "10a_c-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-02_east"]),
+ "10a_c-02_west": PreRegion("10a_c-02_west", "10a_c-02", connections_by_region["10a_c-02_west"], locations_by_region["10a_c-02_west"]),
+ "10a_c-02_east": PreRegion("10a_c-02_east", "10a_c-02", connections_by_region["10a_c-02_east"], locations_by_region["10a_c-02_east"]),
- "10a_c-alt-00_west": PreRegion("10a_c-alt-00_west", "10a_c-alt-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-alt-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-alt-00_west"]),
- "10a_c-alt-00_east": PreRegion("10a_c-alt-00_east", "10a_c-alt-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-alt-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-alt-00_east"]),
+ "10a_c-alt-00_west": PreRegion("10a_c-alt-00_west", "10a_c-alt-00", connections_by_region["10a_c-alt-00_west"], locations_by_region["10a_c-alt-00_west"]),
+ "10a_c-alt-00_east": PreRegion("10a_c-alt-00_east", "10a_c-alt-00", connections_by_region["10a_c-alt-00_east"], locations_by_region["10a_c-alt-00_east"]),
- "10a_c-alt-01_west": PreRegion("10a_c-alt-01_west", "10a_c-alt-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-alt-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-alt-01_west"]),
- "10a_c-alt-01_east": PreRegion("10a_c-alt-01_east", "10a_c-alt-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-alt-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-alt-01_east"]),
+ "10a_c-alt-01_west": PreRegion("10a_c-alt-01_west", "10a_c-alt-01", connections_by_region["10a_c-alt-01_west"], locations_by_region["10a_c-alt-01_west"]),
+ "10a_c-alt-01_east": PreRegion("10a_c-alt-01_east", "10a_c-alt-01", connections_by_region["10a_c-alt-01_east"], locations_by_region["10a_c-alt-01_east"]),
- "10a_c-03_south-west": PreRegion("10a_c-03_south-west", "10a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-03_south-west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-03_south-west"]),
- "10a_c-03_south": PreRegion("10a_c-03_south", "10a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-03_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-03_south"]),
- "10a_c-03_north": PreRegion("10a_c-03_north", "10a_c-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_c-03_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_c-03_north"]),
+ "10a_c-03_south-west": PreRegion("10a_c-03_south-west", "10a_c-03", connections_by_region["10a_c-03_south-west"], locations_by_region["10a_c-03_south-west"]),
+ "10a_c-03_south": PreRegion("10a_c-03_south", "10a_c-03", connections_by_region["10a_c-03_south"], locations_by_region["10a_c-03_south"]),
+ "10a_c-03_north": PreRegion("10a_c-03_north", "10a_c-03", connections_by_region["10a_c-03_north"], locations_by_region["10a_c-03_north"]),
- "10a_d-00_south": PreRegion("10a_d-00_south", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_south"]),
- "10a_d-00_north": PreRegion("10a_d-00_north", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_north"]),
- "10a_d-00_south-east": PreRegion("10a_d-00_south-east", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_south-east"]),
- "10a_d-00_north-west": PreRegion("10a_d-00_north-west", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_north-west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_north-west"]),
- "10a_d-00_breaker": PreRegion("10a_d-00_breaker", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_breaker"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_breaker"]),
- "10a_d-00_north-east-door": PreRegion("10a_d-00_north-east-door", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_north-east-door"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_north-east-door"]),
- "10a_d-00_south-east-door": PreRegion("10a_d-00_south-east-door", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_south-east-door"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_south-east-door"]),
- "10a_d-00_south-west-door": PreRegion("10a_d-00_south-west-door", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_south-west-door"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_south-west-door"]),
- "10a_d-00_west-door": PreRegion("10a_d-00_west-door", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_west-door"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_west-door"]),
- "10a_d-00_north-west-door": PreRegion("10a_d-00_north-west-door", "10a_d-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-00_north-west-door"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-00_north-west-door"]),
+ "10a_d-00_south": PreRegion("10a_d-00_south", "10a_d-00", connections_by_region["10a_d-00_south"], locations_by_region["10a_d-00_south"]),
+ "10a_d-00_north": PreRegion("10a_d-00_north", "10a_d-00", connections_by_region["10a_d-00_north"], locations_by_region["10a_d-00_north"]),
+ "10a_d-00_south-east": PreRegion("10a_d-00_south-east", "10a_d-00", connections_by_region["10a_d-00_south-east"], locations_by_region["10a_d-00_south-east"]),
+ "10a_d-00_north-west": PreRegion("10a_d-00_north-west", "10a_d-00", connections_by_region["10a_d-00_north-west"], locations_by_region["10a_d-00_north-west"]),
+ "10a_d-00_breaker": PreRegion("10a_d-00_breaker", "10a_d-00", connections_by_region["10a_d-00_breaker"], locations_by_region["10a_d-00_breaker"]),
+ "10a_d-00_north-east-door": PreRegion("10a_d-00_north-east-door", "10a_d-00", connections_by_region["10a_d-00_north-east-door"], locations_by_region["10a_d-00_north-east-door"]),
+ "10a_d-00_south-east-door": PreRegion("10a_d-00_south-east-door", "10a_d-00", connections_by_region["10a_d-00_south-east-door"], locations_by_region["10a_d-00_south-east-door"]),
+ "10a_d-00_south-west-door": PreRegion("10a_d-00_south-west-door", "10a_d-00", connections_by_region["10a_d-00_south-west-door"], locations_by_region["10a_d-00_south-west-door"]),
+ "10a_d-00_west-door": PreRegion("10a_d-00_west-door", "10a_d-00", connections_by_region["10a_d-00_west-door"], locations_by_region["10a_d-00_west-door"]),
+ "10a_d-00_north-west-door": PreRegion("10a_d-00_north-west-door", "10a_d-00", connections_by_region["10a_d-00_north-west-door"], locations_by_region["10a_d-00_north-west-door"]),
- "10a_d-04_west": PreRegion("10a_d-04_west", "10a_d-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-04_west"]),
+ "10a_d-04_west": PreRegion("10a_d-04_west", "10a_d-04", connections_by_region["10a_d-04_west"], locations_by_region["10a_d-04_west"]),
- "10a_d-03_west": PreRegion("10a_d-03_west", "10a_d-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-03_west"]),
+ "10a_d-03_west": PreRegion("10a_d-03_west", "10a_d-03", connections_by_region["10a_d-03_west"], locations_by_region["10a_d-03_west"]),
- "10a_d-01_east": PreRegion("10a_d-01_east", "10a_d-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-01_east"]),
+ "10a_d-01_east": PreRegion("10a_d-01_east", "10a_d-01", connections_by_region["10a_d-01_east"], locations_by_region["10a_d-01_east"]),
- "10a_d-02_bottom": PreRegion("10a_d-02_bottom", "10a_d-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-02_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-02_bottom"]),
+ "10a_d-02_bottom": PreRegion("10a_d-02_bottom", "10a_d-02", connections_by_region["10a_d-02_bottom"], locations_by_region["10a_d-02_bottom"]),
- "10a_d-05_west": PreRegion("10a_d-05_west", "10a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-05_west"]),
- "10a_d-05_south": PreRegion("10a_d-05_south", "10a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-05_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-05_south"]),
- "10a_d-05_north": PreRegion("10a_d-05_north", "10a_d-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_d-05_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_d-05_north"]),
+ "10a_d-05_west": PreRegion("10a_d-05_west", "10a_d-05", connections_by_region["10a_d-05_west"], locations_by_region["10a_d-05_west"]),
+ "10a_d-05_south": PreRegion("10a_d-05_south", "10a_d-05", connections_by_region["10a_d-05_south"], locations_by_region["10a_d-05_south"]),
+ "10a_d-05_north": PreRegion("10a_d-05_north", "10a_d-05", connections_by_region["10a_d-05_north"], locations_by_region["10a_d-05_north"]),
- "10a_e-00y_south": PreRegion("10a_e-00y_south", "10a_e-00y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00y_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00y_south"]),
- "10a_e-00y_south-east": PreRegion("10a_e-00y_south-east", "10a_e-00y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00y_south-east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00y_south-east"]),
- "10a_e-00y_north-east": PreRegion("10a_e-00y_north-east", "10a_e-00y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00y_north-east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00y_north-east"]),
- "10a_e-00y_north": PreRegion("10a_e-00y_north", "10a_e-00y", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00y_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00y_north"]),
+ "10a_e-00y_south": PreRegion("10a_e-00y_south", "10a_e-00y", connections_by_region["10a_e-00y_south"], locations_by_region["10a_e-00y_south"]),
+ "10a_e-00y_south-east": PreRegion("10a_e-00y_south-east", "10a_e-00y", connections_by_region["10a_e-00y_south-east"], locations_by_region["10a_e-00y_south-east"]),
+ "10a_e-00y_north-east": PreRegion("10a_e-00y_north-east", "10a_e-00y", connections_by_region["10a_e-00y_north-east"], locations_by_region["10a_e-00y_north-east"]),
+ "10a_e-00y_north": PreRegion("10a_e-00y_north", "10a_e-00y", connections_by_region["10a_e-00y_north"], locations_by_region["10a_e-00y_north"]),
- "10a_e-00yb_south": PreRegion("10a_e-00yb_south", "10a_e-00yb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00yb_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00yb_south"]),
- "10a_e-00yb_north": PreRegion("10a_e-00yb_north", "10a_e-00yb", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00yb_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00yb_north"]),
+ "10a_e-00yb_south": PreRegion("10a_e-00yb_south", "10a_e-00yb", connections_by_region["10a_e-00yb_south"], locations_by_region["10a_e-00yb_south"]),
+ "10a_e-00yb_north": PreRegion("10a_e-00yb_north", "10a_e-00yb", connections_by_region["10a_e-00yb_north"], locations_by_region["10a_e-00yb_north"]),
- "10a_e-00z_south": PreRegion("10a_e-00z_south", "10a_e-00z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00z_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00z_south"]),
- "10a_e-00z_north": PreRegion("10a_e-00z_north", "10a_e-00z", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00z_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00z_north"]),
+ "10a_e-00z_south": PreRegion("10a_e-00z_south", "10a_e-00z", connections_by_region["10a_e-00z_south"], locations_by_region["10a_e-00z_south"]),
+ "10a_e-00z_north": PreRegion("10a_e-00z_north", "10a_e-00z", connections_by_region["10a_e-00z_north"], locations_by_region["10a_e-00z_north"]),
- "10a_e-00_south": PreRegion("10a_e-00_south", "10a_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00_south"]),
- "10a_e-00_north": PreRegion("10a_e-00_north", "10a_e-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00_north"]),
+ "10a_e-00_south": PreRegion("10a_e-00_south", "10a_e-00", connections_by_region["10a_e-00_south"], locations_by_region["10a_e-00_south"]),
+ "10a_e-00_north": PreRegion("10a_e-00_north", "10a_e-00", connections_by_region["10a_e-00_north"], locations_by_region["10a_e-00_north"]),
- "10a_e-00b_south": PreRegion("10a_e-00b_south", "10a_e-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00b_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00b_south"]),
- "10a_e-00b_north": PreRegion("10a_e-00b_north", "10a_e-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-00b_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-00b_north"]),
+ "10a_e-00b_south": PreRegion("10a_e-00b_south", "10a_e-00b", connections_by_region["10a_e-00b_south"], locations_by_region["10a_e-00b_south"]),
+ "10a_e-00b_north": PreRegion("10a_e-00b_north", "10a_e-00b", connections_by_region["10a_e-00b_north"], locations_by_region["10a_e-00b_north"]),
- "10a_e-01_south": PreRegion("10a_e-01_south", "10a_e-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-01_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-01_south"]),
- "10a_e-01_north": PreRegion("10a_e-01_north", "10a_e-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-01_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-01_north"]),
+ "10a_e-01_south": PreRegion("10a_e-01_south", "10a_e-01", connections_by_region["10a_e-01_south"], locations_by_region["10a_e-01_south"]),
+ "10a_e-01_north": PreRegion("10a_e-01_north", "10a_e-01", connections_by_region["10a_e-01_north"], locations_by_region["10a_e-01_north"]),
- "10a_e-02_west": PreRegion("10a_e-02_west", "10a_e-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-02_west"]),
- "10a_e-02_east": PreRegion("10a_e-02_east", "10a_e-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-02_east"]),
+ "10a_e-02_west": PreRegion("10a_e-02_west", "10a_e-02", connections_by_region["10a_e-02_west"], locations_by_region["10a_e-02_west"]),
+ "10a_e-02_east": PreRegion("10a_e-02_east", "10a_e-02", connections_by_region["10a_e-02_east"], locations_by_region["10a_e-02_east"]),
- "10a_e-03_west": PreRegion("10a_e-03_west", "10a_e-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-03_west"]),
- "10a_e-03_east": PreRegion("10a_e-03_east", "10a_e-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-03_east"]),
+ "10a_e-03_west": PreRegion("10a_e-03_west", "10a_e-03", connections_by_region["10a_e-03_west"], locations_by_region["10a_e-03_west"]),
+ "10a_e-03_east": PreRegion("10a_e-03_east", "10a_e-03", connections_by_region["10a_e-03_east"], locations_by_region["10a_e-03_east"]),
- "10a_e-04_west": PreRegion("10a_e-04_west", "10a_e-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-04_west"]),
- "10a_e-04_east": PreRegion("10a_e-04_east", "10a_e-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-04_east"]),
+ "10a_e-04_west": PreRegion("10a_e-04_west", "10a_e-04", connections_by_region["10a_e-04_west"], locations_by_region["10a_e-04_west"]),
+ "10a_e-04_east": PreRegion("10a_e-04_east", "10a_e-04", connections_by_region["10a_e-04_east"], locations_by_region["10a_e-04_east"]),
- "10a_e-05_west": PreRegion("10a_e-05_west", "10a_e-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-05_west"]),
- "10a_e-05_east": PreRegion("10a_e-05_east", "10a_e-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-05_east"]),
+ "10a_e-05_west": PreRegion("10a_e-05_west", "10a_e-05", connections_by_region["10a_e-05_west"], locations_by_region["10a_e-05_west"]),
+ "10a_e-05_east": PreRegion("10a_e-05_east", "10a_e-05", connections_by_region["10a_e-05_east"], locations_by_region["10a_e-05_east"]),
- "10a_e-05b_west": PreRegion("10a_e-05b_west", "10a_e-05b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-05b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-05b_west"]),
- "10a_e-05b_east": PreRegion("10a_e-05b_east", "10a_e-05b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-05b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-05b_east"]),
+ "10a_e-05b_west": PreRegion("10a_e-05b_west", "10a_e-05b", connections_by_region["10a_e-05b_west"], locations_by_region["10a_e-05b_west"]),
+ "10a_e-05b_east": PreRegion("10a_e-05b_east", "10a_e-05b", connections_by_region["10a_e-05b_east"], locations_by_region["10a_e-05b_east"]),
- "10a_e-05c_west": PreRegion("10a_e-05c_west", "10a_e-05c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-05c_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-05c_west"]),
- "10a_e-05c_east": PreRegion("10a_e-05c_east", "10a_e-05c", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-05c_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-05c_east"]),
+ "10a_e-05c_west": PreRegion("10a_e-05c_west", "10a_e-05c", connections_by_region["10a_e-05c_west"], locations_by_region["10a_e-05c_west"]),
+ "10a_e-05c_east": PreRegion("10a_e-05c_east", "10a_e-05c", connections_by_region["10a_e-05c_east"], locations_by_region["10a_e-05c_east"]),
- "10a_e-06_west": PreRegion("10a_e-06_west", "10a_e-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-06_west"]),
- "10a_e-06_east": PreRegion("10a_e-06_east", "10a_e-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-06_east"]),
+ "10a_e-06_west": PreRegion("10a_e-06_west", "10a_e-06", connections_by_region["10a_e-06_west"], locations_by_region["10a_e-06_west"]),
+ "10a_e-06_east": PreRegion("10a_e-06_east", "10a_e-06", connections_by_region["10a_e-06_east"], locations_by_region["10a_e-06_east"]),
- "10a_e-07_west": PreRegion("10a_e-07_west", "10a_e-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-07_west"]),
- "10a_e-07_east": PreRegion("10a_e-07_east", "10a_e-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-07_east"]),
+ "10a_e-07_west": PreRegion("10a_e-07_west", "10a_e-07", connections_by_region["10a_e-07_west"], locations_by_region["10a_e-07_west"]),
+ "10a_e-07_east": PreRegion("10a_e-07_east", "10a_e-07", connections_by_region["10a_e-07_east"], locations_by_region["10a_e-07_east"]),
- "10a_e-08_west": PreRegion("10a_e-08_west", "10a_e-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-08_west"]),
- "10a_e-08_east": PreRegion("10a_e-08_east", "10a_e-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10a_e-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10a_e-08_east"]),
+ "10a_e-08_west": PreRegion("10a_e-08_west", "10a_e-08", connections_by_region["10a_e-08_west"], locations_by_region["10a_e-08_west"]),
+ "10a_e-08_east": PreRegion("10a_e-08_east", "10a_e-08", connections_by_region["10a_e-08_east"], locations_by_region["10a_e-08_east"]),
- "10b_f-door_west": PreRegion("10b_f-door_west", "10b_f-door", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-door_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-door_west"]),
- "10b_f-door_east": PreRegion("10b_f-door_east", "10b_f-door", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-door_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-door_east"]),
+ "10b_f-door_west": PreRegion("10b_f-door_west", "10b_f-door", connections_by_region["10b_f-door_west"], locations_by_region["10b_f-door_west"]),
+ "10b_f-door_east": PreRegion("10b_f-door_east", "10b_f-door", connections_by_region["10b_f-door_east"], locations_by_region["10b_f-door_east"]),
- "10b_f-00_west": PreRegion("10b_f-00_west", "10b_f-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-00_west"]),
- "10b_f-00_east": PreRegion("10b_f-00_east", "10b_f-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-00_east"]),
+ "10b_f-00_west": PreRegion("10b_f-00_west", "10b_f-00", connections_by_region["10b_f-00_west"], locations_by_region["10b_f-00_west"]),
+ "10b_f-00_east": PreRegion("10b_f-00_east", "10b_f-00", connections_by_region["10b_f-00_east"], locations_by_region["10b_f-00_east"]),
- "10b_f-01_west": PreRegion("10b_f-01_west", "10b_f-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-01_west"]),
- "10b_f-01_east": PreRegion("10b_f-01_east", "10b_f-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-01_east"]),
+ "10b_f-01_west": PreRegion("10b_f-01_west", "10b_f-01", connections_by_region["10b_f-01_west"], locations_by_region["10b_f-01_west"]),
+ "10b_f-01_east": PreRegion("10b_f-01_east", "10b_f-01", connections_by_region["10b_f-01_east"], locations_by_region["10b_f-01_east"]),
- "10b_f-02_west": PreRegion("10b_f-02_west", "10b_f-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-02_west"]),
- "10b_f-02_east": PreRegion("10b_f-02_east", "10b_f-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-02_east"]),
+ "10b_f-02_west": PreRegion("10b_f-02_west", "10b_f-02", connections_by_region["10b_f-02_west"], locations_by_region["10b_f-02_west"]),
+ "10b_f-02_east": PreRegion("10b_f-02_east", "10b_f-02", connections_by_region["10b_f-02_east"], locations_by_region["10b_f-02_east"]),
- "10b_f-03_west": PreRegion("10b_f-03_west", "10b_f-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-03_west"]),
- "10b_f-03_east": PreRegion("10b_f-03_east", "10b_f-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-03_east"]),
+ "10b_f-03_west": PreRegion("10b_f-03_west", "10b_f-03", connections_by_region["10b_f-03_west"], locations_by_region["10b_f-03_west"]),
+ "10b_f-03_east": PreRegion("10b_f-03_east", "10b_f-03", connections_by_region["10b_f-03_east"], locations_by_region["10b_f-03_east"]),
- "10b_f-04_west": PreRegion("10b_f-04_west", "10b_f-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-04_west"]),
- "10b_f-04_east": PreRegion("10b_f-04_east", "10b_f-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-04_east"]),
+ "10b_f-04_west": PreRegion("10b_f-04_west", "10b_f-04", connections_by_region["10b_f-04_west"], locations_by_region["10b_f-04_west"]),
+ "10b_f-04_east": PreRegion("10b_f-04_east", "10b_f-04", connections_by_region["10b_f-04_east"], locations_by_region["10b_f-04_east"]),
- "10b_f-05_west": PreRegion("10b_f-05_west", "10b_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-05_west"]),
- "10b_f-05_east": PreRegion("10b_f-05_east", "10b_f-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-05_east"]),
+ "10b_f-05_west": PreRegion("10b_f-05_west", "10b_f-05", connections_by_region["10b_f-05_west"], locations_by_region["10b_f-05_west"]),
+ "10b_f-05_east": PreRegion("10b_f-05_east", "10b_f-05", connections_by_region["10b_f-05_east"], locations_by_region["10b_f-05_east"]),
- "10b_f-06_west": PreRegion("10b_f-06_west", "10b_f-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-06_west"]),
- "10b_f-06_east": PreRegion("10b_f-06_east", "10b_f-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-06_east"]),
+ "10b_f-06_west": PreRegion("10b_f-06_west", "10b_f-06", connections_by_region["10b_f-06_west"], locations_by_region["10b_f-06_west"]),
+ "10b_f-06_east": PreRegion("10b_f-06_east", "10b_f-06", connections_by_region["10b_f-06_east"], locations_by_region["10b_f-06_east"]),
- "10b_f-07_west": PreRegion("10b_f-07_west", "10b_f-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-07_west"]),
- "10b_f-07_east": PreRegion("10b_f-07_east", "10b_f-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-07_east"]),
+ "10b_f-07_west": PreRegion("10b_f-07_west", "10b_f-07", connections_by_region["10b_f-07_west"], locations_by_region["10b_f-07_west"]),
+ "10b_f-07_east": PreRegion("10b_f-07_east", "10b_f-07", connections_by_region["10b_f-07_east"], locations_by_region["10b_f-07_east"]),
- "10b_f-08_west": PreRegion("10b_f-08_west", "10b_f-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-08_west"]),
- "10b_f-08_east": PreRegion("10b_f-08_east", "10b_f-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-08_east"]),
+ "10b_f-08_west": PreRegion("10b_f-08_west", "10b_f-08", connections_by_region["10b_f-08_west"], locations_by_region["10b_f-08_west"]),
+ "10b_f-08_east": PreRegion("10b_f-08_east", "10b_f-08", connections_by_region["10b_f-08_east"], locations_by_region["10b_f-08_east"]),
- "10b_f-09_west": PreRegion("10b_f-09_west", "10b_f-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-09_west"]),
- "10b_f-09_east": PreRegion("10b_f-09_east", "10b_f-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_f-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_f-09_east"]),
+ "10b_f-09_west": PreRegion("10b_f-09_west", "10b_f-09", connections_by_region["10b_f-09_west"], locations_by_region["10b_f-09_west"]),
+ "10b_f-09_east": PreRegion("10b_f-09_east", "10b_f-09", connections_by_region["10b_f-09_east"], locations_by_region["10b_f-09_east"]),
- "10b_g-00_bottom": PreRegion("10b_g-00_bottom", "10b_g-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-00_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-00_bottom"]),
- "10b_g-00_top": PreRegion("10b_g-00_top", "10b_g-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-00_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-00_top"]),
+ "10b_g-00_bottom": PreRegion("10b_g-00_bottom", "10b_g-00", connections_by_region["10b_g-00_bottom"], locations_by_region["10b_g-00_bottom"]),
+ "10b_g-00_top": PreRegion("10b_g-00_top", "10b_g-00", connections_by_region["10b_g-00_top"], locations_by_region["10b_g-00_top"]),
- "10b_g-01_bottom": PreRegion("10b_g-01_bottom", "10b_g-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-01_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-01_bottom"]),
- "10b_g-01_top": PreRegion("10b_g-01_top", "10b_g-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-01_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-01_top"]),
+ "10b_g-01_bottom": PreRegion("10b_g-01_bottom", "10b_g-01", connections_by_region["10b_g-01_bottom"], locations_by_region["10b_g-01_bottom"]),
+ "10b_g-01_top": PreRegion("10b_g-01_top", "10b_g-01", connections_by_region["10b_g-01_top"], locations_by_region["10b_g-01_top"]),
- "10b_g-03_bottom": PreRegion("10b_g-03_bottom", "10b_g-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-03_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-03_bottom"]),
- "10b_g-03_top": PreRegion("10b_g-03_top", "10b_g-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-03_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-03_top"]),
+ "10b_g-03_bottom": PreRegion("10b_g-03_bottom", "10b_g-03", connections_by_region["10b_g-03_bottom"], locations_by_region["10b_g-03_bottom"]),
+ "10b_g-03_top": PreRegion("10b_g-03_top", "10b_g-03", connections_by_region["10b_g-03_top"], locations_by_region["10b_g-03_top"]),
- "10b_g-02_west": PreRegion("10b_g-02_west", "10b_g-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-02_west"]),
- "10b_g-02_east": PreRegion("10b_g-02_east", "10b_g-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-02_east"]),
+ "10b_g-02_west": PreRegion("10b_g-02_west", "10b_g-02", connections_by_region["10b_g-02_west"], locations_by_region["10b_g-02_west"]),
+ "10b_g-02_east": PreRegion("10b_g-02_east", "10b_g-02", connections_by_region["10b_g-02_east"], locations_by_region["10b_g-02_east"]),
- "10b_g-04_west": PreRegion("10b_g-04_west", "10b_g-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-04_west"]),
- "10b_g-04_east": PreRegion("10b_g-04_east", "10b_g-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-04_east"]),
+ "10b_g-04_west": PreRegion("10b_g-04_west", "10b_g-04", connections_by_region["10b_g-04_west"], locations_by_region["10b_g-04_west"]),
+ "10b_g-04_east": PreRegion("10b_g-04_east", "10b_g-04", connections_by_region["10b_g-04_east"], locations_by_region["10b_g-04_east"]),
- "10b_g-05_west": PreRegion("10b_g-05_west", "10b_g-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-05_west"]),
- "10b_g-05_east": PreRegion("10b_g-05_east", "10b_g-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-05_east"]),
+ "10b_g-05_west": PreRegion("10b_g-05_west", "10b_g-05", connections_by_region["10b_g-05_west"], locations_by_region["10b_g-05_west"]),
+ "10b_g-05_east": PreRegion("10b_g-05_east", "10b_g-05", connections_by_region["10b_g-05_east"], locations_by_region["10b_g-05_east"]),
- "10b_g-06_west": PreRegion("10b_g-06_west", "10b_g-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-06_west"]),
- "10b_g-06_east": PreRegion("10b_g-06_east", "10b_g-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_g-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_g-06_east"]),
+ "10b_g-06_west": PreRegion("10b_g-06_west", "10b_g-06", connections_by_region["10b_g-06_west"], locations_by_region["10b_g-06_west"]),
+ "10b_g-06_east": PreRegion("10b_g-06_east", "10b_g-06", connections_by_region["10b_g-06_east"], locations_by_region["10b_g-06_east"]),
- "10b_h-00b_west": PreRegion("10b_h-00b_west", "10b_h-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-00b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-00b_west"]),
- "10b_h-00b_east": PreRegion("10b_h-00b_east", "10b_h-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-00b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-00b_east"]),
+ "10b_h-00b_west": PreRegion("10b_h-00b_west", "10b_h-00b", connections_by_region["10b_h-00b_west"], locations_by_region["10b_h-00b_west"]),
+ "10b_h-00b_east": PreRegion("10b_h-00b_east", "10b_h-00b", connections_by_region["10b_h-00b_east"], locations_by_region["10b_h-00b_east"]),
- "10b_h-00_west": PreRegion("10b_h-00_west", "10b_h-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-00_west"]),
- "10b_h-00_east": PreRegion("10b_h-00_east", "10b_h-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-00_east"]),
+ "10b_h-00_west": PreRegion("10b_h-00_west", "10b_h-00", connections_by_region["10b_h-00_west"], locations_by_region["10b_h-00_west"]),
+ "10b_h-00_east": PreRegion("10b_h-00_east", "10b_h-00", connections_by_region["10b_h-00_east"], locations_by_region["10b_h-00_east"]),
- "10b_h-01_west": PreRegion("10b_h-01_west", "10b_h-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-01_west"]),
- "10b_h-01_east": PreRegion("10b_h-01_east", "10b_h-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-01_east"]),
+ "10b_h-01_west": PreRegion("10b_h-01_west", "10b_h-01", connections_by_region["10b_h-01_west"], locations_by_region["10b_h-01_west"]),
+ "10b_h-01_east": PreRegion("10b_h-01_east", "10b_h-01", connections_by_region["10b_h-01_east"], locations_by_region["10b_h-01_east"]),
- "10b_h-02_west": PreRegion("10b_h-02_west", "10b_h-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-02_west"]),
- "10b_h-02_east": PreRegion("10b_h-02_east", "10b_h-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-02_east"]),
+ "10b_h-02_west": PreRegion("10b_h-02_west", "10b_h-02", connections_by_region["10b_h-02_west"], locations_by_region["10b_h-02_west"]),
+ "10b_h-02_east": PreRegion("10b_h-02_east", "10b_h-02", connections_by_region["10b_h-02_east"], locations_by_region["10b_h-02_east"]),
- "10b_h-03_west": PreRegion("10b_h-03_west", "10b_h-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-03_west"]),
- "10b_h-03_east": PreRegion("10b_h-03_east", "10b_h-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-03_east"]),
+ "10b_h-03_west": PreRegion("10b_h-03_west", "10b_h-03", connections_by_region["10b_h-03_west"], locations_by_region["10b_h-03_west"]),
+ "10b_h-03_east": PreRegion("10b_h-03_east", "10b_h-03", connections_by_region["10b_h-03_east"], locations_by_region["10b_h-03_east"]),
- "10b_h-03b_west": PreRegion("10b_h-03b_west", "10b_h-03b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-03b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-03b_west"]),
- "10b_h-03b_east": PreRegion("10b_h-03b_east", "10b_h-03b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-03b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-03b_east"]),
+ "10b_h-03b_west": PreRegion("10b_h-03b_west", "10b_h-03b", connections_by_region["10b_h-03b_west"], locations_by_region["10b_h-03b_west"]),
+ "10b_h-03b_east": PreRegion("10b_h-03b_east", "10b_h-03b", connections_by_region["10b_h-03b_east"], locations_by_region["10b_h-03b_east"]),
- "10b_h-04_top": PreRegion("10b_h-04_top", "10b_h-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-04_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-04_top"]),
- "10b_h-04_east": PreRegion("10b_h-04_east", "10b_h-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-04_east"]),
- "10b_h-04_bottom": PreRegion("10b_h-04_bottom", "10b_h-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-04_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-04_bottom"]),
+ "10b_h-04_top": PreRegion("10b_h-04_top", "10b_h-04", connections_by_region["10b_h-04_top"], locations_by_region["10b_h-04_top"]),
+ "10b_h-04_east": PreRegion("10b_h-04_east", "10b_h-04", connections_by_region["10b_h-04_east"], locations_by_region["10b_h-04_east"]),
+ "10b_h-04_bottom": PreRegion("10b_h-04_bottom", "10b_h-04", connections_by_region["10b_h-04_bottom"], locations_by_region["10b_h-04_bottom"]),
- "10b_h-04b_west": PreRegion("10b_h-04b_west", "10b_h-04b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-04b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-04b_west"]),
- "10b_h-04b_east": PreRegion("10b_h-04b_east", "10b_h-04b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-04b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-04b_east"]),
+ "10b_h-04b_west": PreRegion("10b_h-04b_west", "10b_h-04b", connections_by_region["10b_h-04b_west"], locations_by_region["10b_h-04b_west"]),
+ "10b_h-04b_east": PreRegion("10b_h-04b_east", "10b_h-04b", connections_by_region["10b_h-04b_east"], locations_by_region["10b_h-04b_east"]),
- "10b_h-05_west": PreRegion("10b_h-05_west", "10b_h-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-05_west"]),
- "10b_h-05_top": PreRegion("10b_h-05_top", "10b_h-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-05_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-05_top"]),
- "10b_h-05_east": PreRegion("10b_h-05_east", "10b_h-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-05_east"]),
+ "10b_h-05_west": PreRegion("10b_h-05_west", "10b_h-05", connections_by_region["10b_h-05_west"], locations_by_region["10b_h-05_west"]),
+ "10b_h-05_top": PreRegion("10b_h-05_top", "10b_h-05", connections_by_region["10b_h-05_top"], locations_by_region["10b_h-05_top"]),
+ "10b_h-05_east": PreRegion("10b_h-05_east", "10b_h-05", connections_by_region["10b_h-05_east"], locations_by_region["10b_h-05_east"]),
- "10b_h-06_west": PreRegion("10b_h-06_west", "10b_h-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-06_west"]),
- "10b_h-06_east": PreRegion("10b_h-06_east", "10b_h-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-06_east"]),
+ "10b_h-06_west": PreRegion("10b_h-06_west", "10b_h-06", connections_by_region["10b_h-06_west"], locations_by_region["10b_h-06_west"]),
+ "10b_h-06_east": PreRegion("10b_h-06_east", "10b_h-06", connections_by_region["10b_h-06_east"], locations_by_region["10b_h-06_east"]),
- "10b_h-06b_bottom": PreRegion("10b_h-06b_bottom", "10b_h-06b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-06b_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-06b_bottom"]),
- "10b_h-06b_top": PreRegion("10b_h-06b_top", "10b_h-06b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-06b_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-06b_top"]),
+ "10b_h-06b_bottom": PreRegion("10b_h-06b_bottom", "10b_h-06b", connections_by_region["10b_h-06b_bottom"], locations_by_region["10b_h-06b_bottom"]),
+ "10b_h-06b_top": PreRegion("10b_h-06b_top", "10b_h-06b", connections_by_region["10b_h-06b_top"], locations_by_region["10b_h-06b_top"]),
- "10b_h-07_west": PreRegion("10b_h-07_west", "10b_h-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-07_west"]),
- "10b_h-07_east": PreRegion("10b_h-07_east", "10b_h-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-07_east"]),
+ "10b_h-07_west": PreRegion("10b_h-07_west", "10b_h-07", connections_by_region["10b_h-07_west"], locations_by_region["10b_h-07_west"]),
+ "10b_h-07_east": PreRegion("10b_h-07_east", "10b_h-07", connections_by_region["10b_h-07_east"], locations_by_region["10b_h-07_east"]),
- "10b_h-08_west": PreRegion("10b_h-08_west", "10b_h-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-08_west"]),
- "10b_h-08_east": PreRegion("10b_h-08_east", "10b_h-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-08_east"]),
+ "10b_h-08_west": PreRegion("10b_h-08_west", "10b_h-08", connections_by_region["10b_h-08_west"], locations_by_region["10b_h-08_west"]),
+ "10b_h-08_east": PreRegion("10b_h-08_east", "10b_h-08", connections_by_region["10b_h-08_east"], locations_by_region["10b_h-08_east"]),
- "10b_h-09_west": PreRegion("10b_h-09_west", "10b_h-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-09_west"]),
- "10b_h-09_east": PreRegion("10b_h-09_east", "10b_h-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-09_east"]),
+ "10b_h-09_west": PreRegion("10b_h-09_west", "10b_h-09", connections_by_region["10b_h-09_west"], locations_by_region["10b_h-09_west"]),
+ "10b_h-09_east": PreRegion("10b_h-09_east", "10b_h-09", connections_by_region["10b_h-09_east"], locations_by_region["10b_h-09_east"]),
- "10b_h-10_west": PreRegion("10b_h-10_west", "10b_h-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-10_west"]),
- "10b_h-10_east": PreRegion("10b_h-10_east", "10b_h-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_h-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_h-10_east"]),
+ "10b_h-10_west": PreRegion("10b_h-10_west", "10b_h-10", connections_by_region["10b_h-10_west"], locations_by_region["10b_h-10_west"]),
+ "10b_h-10_east": PreRegion("10b_h-10_east", "10b_h-10", connections_by_region["10b_h-10_east"], locations_by_region["10b_h-10_east"]),
- "10b_i-00_west": PreRegion("10b_i-00_west", "10b_i-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-00_west"]),
- "10b_i-00_east": PreRegion("10b_i-00_east", "10b_i-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-00_east"]),
+ "10b_i-00_west": PreRegion("10b_i-00_west", "10b_i-00", connections_by_region["10b_i-00_west"], locations_by_region["10b_i-00_west"]),
+ "10b_i-00_east": PreRegion("10b_i-00_east", "10b_i-00", connections_by_region["10b_i-00_east"], locations_by_region["10b_i-00_east"]),
- "10b_i-00b_west": PreRegion("10b_i-00b_west", "10b_i-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-00b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-00b_west"]),
- "10b_i-00b_east": PreRegion("10b_i-00b_east", "10b_i-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-00b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-00b_east"]),
+ "10b_i-00b_west": PreRegion("10b_i-00b_west", "10b_i-00b", connections_by_region["10b_i-00b_west"], locations_by_region["10b_i-00b_west"]),
+ "10b_i-00b_east": PreRegion("10b_i-00b_east", "10b_i-00b", connections_by_region["10b_i-00b_east"], locations_by_region["10b_i-00b_east"]),
- "10b_i-01_west": PreRegion("10b_i-01_west", "10b_i-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-01_west"]),
- "10b_i-01_east": PreRegion("10b_i-01_east", "10b_i-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-01_east"]),
+ "10b_i-01_west": PreRegion("10b_i-01_west", "10b_i-01", connections_by_region["10b_i-01_west"], locations_by_region["10b_i-01_west"]),
+ "10b_i-01_east": PreRegion("10b_i-01_east", "10b_i-01", connections_by_region["10b_i-01_east"], locations_by_region["10b_i-01_east"]),
- "10b_i-02_west": PreRegion("10b_i-02_west", "10b_i-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-02_west"]),
- "10b_i-02_east": PreRegion("10b_i-02_east", "10b_i-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-02_east"]),
+ "10b_i-02_west": PreRegion("10b_i-02_west", "10b_i-02", connections_by_region["10b_i-02_west"], locations_by_region["10b_i-02_west"]),
+ "10b_i-02_east": PreRegion("10b_i-02_east", "10b_i-02", connections_by_region["10b_i-02_east"], locations_by_region["10b_i-02_east"]),
- "10b_i-03_west": PreRegion("10b_i-03_west", "10b_i-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-03_west"]),
- "10b_i-03_east": PreRegion("10b_i-03_east", "10b_i-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-03_east"]),
+ "10b_i-03_west": PreRegion("10b_i-03_west", "10b_i-03", connections_by_region["10b_i-03_west"], locations_by_region["10b_i-03_west"]),
+ "10b_i-03_east": PreRegion("10b_i-03_east", "10b_i-03", connections_by_region["10b_i-03_east"], locations_by_region["10b_i-03_east"]),
- "10b_i-04_west": PreRegion("10b_i-04_west", "10b_i-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-04_west"]),
- "10b_i-04_east": PreRegion("10b_i-04_east", "10b_i-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-04_east"]),
+ "10b_i-04_west": PreRegion("10b_i-04_west", "10b_i-04", connections_by_region["10b_i-04_west"], locations_by_region["10b_i-04_west"]),
+ "10b_i-04_east": PreRegion("10b_i-04_east", "10b_i-04", connections_by_region["10b_i-04_east"], locations_by_region["10b_i-04_east"]),
- "10b_i-05_west": PreRegion("10b_i-05_west", "10b_i-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-05_west"]),
- "10b_i-05_east": PreRegion("10b_i-05_east", "10b_i-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_i-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_i-05_east"]),
+ "10b_i-05_west": PreRegion("10b_i-05_west", "10b_i-05", connections_by_region["10b_i-05_west"], locations_by_region["10b_i-05_west"]),
+ "10b_i-05_east": PreRegion("10b_i-05_east", "10b_i-05", connections_by_region["10b_i-05_east"], locations_by_region["10b_i-05_east"]),
- "10b_j-00_west": PreRegion("10b_j-00_west", "10b_j-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-00_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-00_west"]),
- "10b_j-00_east": PreRegion("10b_j-00_east", "10b_j-00", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-00_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-00_east"]),
+ "10b_j-00_west": PreRegion("10b_j-00_west", "10b_j-00", connections_by_region["10b_j-00_west"], locations_by_region["10b_j-00_west"]),
+ "10b_j-00_east": PreRegion("10b_j-00_east", "10b_j-00", connections_by_region["10b_j-00_east"], locations_by_region["10b_j-00_east"]),
- "10b_j-00b_west": PreRegion("10b_j-00b_west", "10b_j-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-00b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-00b_west"]),
- "10b_j-00b_east": PreRegion("10b_j-00b_east", "10b_j-00b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-00b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-00b_east"]),
+ "10b_j-00b_west": PreRegion("10b_j-00b_west", "10b_j-00b", connections_by_region["10b_j-00b_west"], locations_by_region["10b_j-00b_west"]),
+ "10b_j-00b_east": PreRegion("10b_j-00b_east", "10b_j-00b", connections_by_region["10b_j-00b_east"], locations_by_region["10b_j-00b_east"]),
- "10b_j-01_west": PreRegion("10b_j-01_west", "10b_j-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-01_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-01_west"]),
- "10b_j-01_east": PreRegion("10b_j-01_east", "10b_j-01", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-01_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-01_east"]),
+ "10b_j-01_west": PreRegion("10b_j-01_west", "10b_j-01", connections_by_region["10b_j-01_west"], locations_by_region["10b_j-01_west"]),
+ "10b_j-01_east": PreRegion("10b_j-01_east", "10b_j-01", connections_by_region["10b_j-01_east"], locations_by_region["10b_j-01_east"]),
- "10b_j-02_west": PreRegion("10b_j-02_west", "10b_j-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-02_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-02_west"]),
- "10b_j-02_east": PreRegion("10b_j-02_east", "10b_j-02", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-02_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-02_east"]),
+ "10b_j-02_west": PreRegion("10b_j-02_west", "10b_j-02", connections_by_region["10b_j-02_west"], locations_by_region["10b_j-02_west"]),
+ "10b_j-02_east": PreRegion("10b_j-02_east", "10b_j-02", connections_by_region["10b_j-02_east"], locations_by_region["10b_j-02_east"]),
- "10b_j-03_west": PreRegion("10b_j-03_west", "10b_j-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-03_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-03_west"]),
- "10b_j-03_east": PreRegion("10b_j-03_east", "10b_j-03", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-03_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-03_east"]),
+ "10b_j-03_west": PreRegion("10b_j-03_west", "10b_j-03", connections_by_region["10b_j-03_west"], locations_by_region["10b_j-03_west"]),
+ "10b_j-03_east": PreRegion("10b_j-03_east", "10b_j-03", connections_by_region["10b_j-03_east"], locations_by_region["10b_j-03_east"]),
- "10b_j-04_west": PreRegion("10b_j-04_west", "10b_j-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-04_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-04_west"]),
- "10b_j-04_east": PreRegion("10b_j-04_east", "10b_j-04", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-04_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-04_east"]),
+ "10b_j-04_west": PreRegion("10b_j-04_west", "10b_j-04", connections_by_region["10b_j-04_west"], locations_by_region["10b_j-04_west"]),
+ "10b_j-04_east": PreRegion("10b_j-04_east", "10b_j-04", connections_by_region["10b_j-04_east"], locations_by_region["10b_j-04_east"]),
- "10b_j-05_west": PreRegion("10b_j-05_west", "10b_j-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-05_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-05_west"]),
- "10b_j-05_east": PreRegion("10b_j-05_east", "10b_j-05", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-05_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-05_east"]),
+ "10b_j-05_west": PreRegion("10b_j-05_west", "10b_j-05", connections_by_region["10b_j-05_west"], locations_by_region["10b_j-05_west"]),
+ "10b_j-05_east": PreRegion("10b_j-05_east", "10b_j-05", connections_by_region["10b_j-05_east"], locations_by_region["10b_j-05_east"]),
- "10b_j-06_west": PreRegion("10b_j-06_west", "10b_j-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-06_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-06_west"]),
- "10b_j-06_east": PreRegion("10b_j-06_east", "10b_j-06", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-06_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-06_east"]),
+ "10b_j-06_west": PreRegion("10b_j-06_west", "10b_j-06", connections_by_region["10b_j-06_west"], locations_by_region["10b_j-06_west"]),
+ "10b_j-06_east": PreRegion("10b_j-06_east", "10b_j-06", connections_by_region["10b_j-06_east"], locations_by_region["10b_j-06_east"]),
- "10b_j-07_west": PreRegion("10b_j-07_west", "10b_j-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-07_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-07_west"]),
- "10b_j-07_east": PreRegion("10b_j-07_east", "10b_j-07", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-07_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-07_east"]),
+ "10b_j-07_west": PreRegion("10b_j-07_west", "10b_j-07", connections_by_region["10b_j-07_west"], locations_by_region["10b_j-07_west"]),
+ "10b_j-07_east": PreRegion("10b_j-07_east", "10b_j-07", connections_by_region["10b_j-07_east"], locations_by_region["10b_j-07_east"]),
- "10b_j-08_west": PreRegion("10b_j-08_west", "10b_j-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-08_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-08_west"]),
- "10b_j-08_east": PreRegion("10b_j-08_east", "10b_j-08", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-08_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-08_east"]),
+ "10b_j-08_west": PreRegion("10b_j-08_west", "10b_j-08", connections_by_region["10b_j-08_west"], locations_by_region["10b_j-08_west"]),
+ "10b_j-08_east": PreRegion("10b_j-08_east", "10b_j-08", connections_by_region["10b_j-08_east"], locations_by_region["10b_j-08_east"]),
- "10b_j-09_west": PreRegion("10b_j-09_west", "10b_j-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-09_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-09_west"]),
- "10b_j-09_east": PreRegion("10b_j-09_east", "10b_j-09", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-09_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-09_east"]),
+ "10b_j-09_west": PreRegion("10b_j-09_west", "10b_j-09", connections_by_region["10b_j-09_west"], locations_by_region["10b_j-09_west"]),
+ "10b_j-09_east": PreRegion("10b_j-09_east", "10b_j-09", connections_by_region["10b_j-09_east"], locations_by_region["10b_j-09_east"]),
- "10b_j-10_west": PreRegion("10b_j-10_west", "10b_j-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-10_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-10_west"]),
- "10b_j-10_east": PreRegion("10b_j-10_east", "10b_j-10", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-10_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-10_east"]),
+ "10b_j-10_west": PreRegion("10b_j-10_west", "10b_j-10", connections_by_region["10b_j-10_west"], locations_by_region["10b_j-10_west"]),
+ "10b_j-10_east": PreRegion("10b_j-10_east", "10b_j-10", connections_by_region["10b_j-10_east"], locations_by_region["10b_j-10_east"]),
- "10b_j-11_west": PreRegion("10b_j-11_west", "10b_j-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-11_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-11_west"]),
- "10b_j-11_east": PreRegion("10b_j-11_east", "10b_j-11", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-11_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-11_east"]),
+ "10b_j-11_west": PreRegion("10b_j-11_west", "10b_j-11", connections_by_region["10b_j-11_west"], locations_by_region["10b_j-11_west"]),
+ "10b_j-11_east": PreRegion("10b_j-11_east", "10b_j-11", connections_by_region["10b_j-11_east"], locations_by_region["10b_j-11_east"]),
- "10b_j-12_west": PreRegion("10b_j-12_west", "10b_j-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-12_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-12_west"]),
- "10b_j-12_east": PreRegion("10b_j-12_east", "10b_j-12", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-12_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-12_east"]),
+ "10b_j-12_west": PreRegion("10b_j-12_west", "10b_j-12", connections_by_region["10b_j-12_west"], locations_by_region["10b_j-12_west"]),
+ "10b_j-12_east": PreRegion("10b_j-12_east", "10b_j-12", connections_by_region["10b_j-12_east"], locations_by_region["10b_j-12_east"]),
- "10b_j-13_west": PreRegion("10b_j-13_west", "10b_j-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-13_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-13_west"]),
- "10b_j-13_east": PreRegion("10b_j-13_east", "10b_j-13", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-13_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-13_east"]),
+ "10b_j-13_west": PreRegion("10b_j-13_west", "10b_j-13", connections_by_region["10b_j-13_west"], locations_by_region["10b_j-13_west"]),
+ "10b_j-13_east": PreRegion("10b_j-13_east", "10b_j-13", connections_by_region["10b_j-13_east"], locations_by_region["10b_j-13_east"]),
- "10b_j-14_west": PreRegion("10b_j-14_west", "10b_j-14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-14_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-14_west"]),
- "10b_j-14_east": PreRegion("10b_j-14_east", "10b_j-14", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-14_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-14_east"]),
+ "10b_j-14_west": PreRegion("10b_j-14_west", "10b_j-14", connections_by_region["10b_j-14_west"], locations_by_region["10b_j-14_west"]),
+ "10b_j-14_east": PreRegion("10b_j-14_east", "10b_j-14", connections_by_region["10b_j-14_east"], locations_by_region["10b_j-14_east"]),
- "10b_j-14b_west": PreRegion("10b_j-14b_west", "10b_j-14b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-14b_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-14b_west"]),
- "10b_j-14b_east": PreRegion("10b_j-14b_east", "10b_j-14b", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-14b_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-14b_east"]),
+ "10b_j-14b_west": PreRegion("10b_j-14b_west", "10b_j-14b", connections_by_region["10b_j-14b_west"], locations_by_region["10b_j-14b_west"]),
+ "10b_j-14b_east": PreRegion("10b_j-14b_east", "10b_j-14b", connections_by_region["10b_j-14b_east"], locations_by_region["10b_j-14b_east"]),
- "10b_j-15_west": PreRegion("10b_j-15_west", "10b_j-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-15_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-15_west"]),
- "10b_j-15_east": PreRegion("10b_j-15_east", "10b_j-15", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-15_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-15_east"]),
+ "10b_j-15_west": PreRegion("10b_j-15_west", "10b_j-15", connections_by_region["10b_j-15_west"], locations_by_region["10b_j-15_west"]),
+ "10b_j-15_east": PreRegion("10b_j-15_east", "10b_j-15", connections_by_region["10b_j-15_east"], locations_by_region["10b_j-15_east"]),
- "10b_j-16_west": PreRegion("10b_j-16_west", "10b_j-16", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-16_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-16_west"]),
- "10b_j-16_top": PreRegion("10b_j-16_top", "10b_j-16", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-16_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-16_top"]),
- "10b_j-16_east": PreRegion("10b_j-16_east", "10b_j-16", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-16_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-16_east"]),
+ "10b_j-16_west": PreRegion("10b_j-16_west", "10b_j-16", connections_by_region["10b_j-16_west"], locations_by_region["10b_j-16_west"]),
+ "10b_j-16_top": PreRegion("10b_j-16_top", "10b_j-16", connections_by_region["10b_j-16_top"], locations_by_region["10b_j-16_top"]),
+ "10b_j-16_east": PreRegion("10b_j-16_east", "10b_j-16", connections_by_region["10b_j-16_east"], locations_by_region["10b_j-16_east"]),
- "10b_j-17_south": PreRegion("10b_j-17_south", "10b_j-17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-17_south"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-17_south"]),
- "10b_j-17_west": PreRegion("10b_j-17_west", "10b_j-17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-17_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-17_west"]),
- "10b_j-17_north": PreRegion("10b_j-17_north", "10b_j-17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-17_north"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-17_north"]),
- "10b_j-17_east": PreRegion("10b_j-17_east", "10b_j-17", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-17_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-17_east"]),
+ "10b_j-17_south": PreRegion("10b_j-17_south", "10b_j-17", connections_by_region["10b_j-17_south"], locations_by_region["10b_j-17_south"]),
+ "10b_j-17_west": PreRegion("10b_j-17_west", "10b_j-17", connections_by_region["10b_j-17_west"], locations_by_region["10b_j-17_west"]),
+ "10b_j-17_north": PreRegion("10b_j-17_north", "10b_j-17", connections_by_region["10b_j-17_north"], locations_by_region["10b_j-17_north"]),
+ "10b_j-17_east": PreRegion("10b_j-17_east", "10b_j-17", connections_by_region["10b_j-17_east"], locations_by_region["10b_j-17_east"]),
- "10b_j-18_west": PreRegion("10b_j-18_west", "10b_j-18", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-18_west"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-18_west"]),
- "10b_j-18_east": PreRegion("10b_j-18_east", "10b_j-18", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-18_east"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-18_east"]),
+ "10b_j-18_west": PreRegion("10b_j-18_west", "10b_j-18", connections_by_region["10b_j-18_west"], locations_by_region["10b_j-18_west"]),
+ "10b_j-18_east": PreRegion("10b_j-18_east", "10b_j-18", connections_by_region["10b_j-18_east"], locations_by_region["10b_j-18_east"]),
- "10b_j-19_bottom": PreRegion("10b_j-19_bottom", "10b_j-19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-19_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-19_bottom"]),
- "10b_j-19_top": PreRegion("10b_j-19_top", "10b_j-19", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_j-19_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_j-19_top"]),
+ "10b_j-19_bottom": PreRegion("10b_j-19_bottom", "10b_j-19", connections_by_region["10b_j-19_bottom"], locations_by_region["10b_j-19_bottom"]),
+ "10b_j-19_top": PreRegion("10b_j-19_top", "10b_j-19", connections_by_region["10b_j-19_top"], locations_by_region["10b_j-19_top"]),
- "10b_GOAL_main": PreRegion("10b_GOAL_main", "10b_GOAL", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_GOAL_main"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_GOAL_main"]),
- "10b_GOAL_moon": PreRegion("10b_GOAL_moon", "10b_GOAL", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10b_GOAL_moon"], [loc for _, loc in all_locations.items() if loc.region_name == "10b_GOAL_moon"]),
+ "10b_GOAL_main": PreRegion("10b_GOAL_main", "10b_GOAL", connections_by_region["10b_GOAL_main"], locations_by_region["10b_GOAL_main"]),
+ "10b_GOAL_moon": PreRegion("10b_GOAL_moon", "10b_GOAL", connections_by_region["10b_GOAL_moon"], locations_by_region["10b_GOAL_moon"]),
- "10c_end-golden_bottom": PreRegion("10c_end-golden_bottom", "10c_end-golden", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10c_end-golden_bottom"], [loc for _, loc in all_locations.items() if loc.region_name == "10c_end-golden_bottom"]),
- "10c_end-golden_top": PreRegion("10c_end-golden_top", "10c_end-golden", [reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == "10c_end-golden_top"], [loc for _, loc in all_locations.items() if loc.region_name == "10c_end-golden_top"]),
+ "10c_end-golden_bottom": PreRegion("10c_end-golden_bottom", "10c_end-golden", connections_by_region["10c_end-golden_bottom"], locations_by_region["10c_end-golden_bottom"]),
+ "10c_end-golden_top": PreRegion("10c_end-golden_top", "10c_end-golden", connections_by_region["10c_end-golden_top"], locations_by_region["10c_end-golden_top"]),
}
@@ -8923,870 +8933,888 @@
}
+regions_by_room: defaultdict[str, list[PreRegion]] = defaultdict(lambda: [])
+doors_by_room: defaultdict[str, list[Door]] = defaultdict(lambda: [])
+
+for _, region in all_regions.items():
+ regions_by_room[region.room_name].append(region)
+
+for _, door in all_doors.items():
+ doors_by_room[door.room_name].append(door)
+
all_rooms: dict[str, Room] = {
- "0a_-1": Room("0a", "0a_-1", "Prologue - Room -1", [reg for _, reg in all_regions.items() if reg.room_name == "0a_-1"], [door for _, door in all_doors.items() if door.room_name == "0a_-1"]),
- "0a_0": Room("0a", "0a_0", "Prologue - Room 0", [reg for _, reg in all_regions.items() if reg.room_name == "0a_0"], [door for _, door in all_doors.items() if door.room_name == "0a_0"], "Start", "0a_0_west"),
- "0a_0b": Room("0a", "0a_0b", "Prologue - Room 0b", [reg for _, reg in all_regions.items() if reg.room_name == "0a_0b"], [door for _, door in all_doors.items() if door.room_name == "0a_0b"]),
- "0a_1": Room("0a", "0a_1", "Prologue - Room 1", [reg for _, reg in all_regions.items() if reg.room_name == "0a_1"], [door for _, door in all_doors.items() if door.room_name == "0a_1"]),
- "0a_2": Room("0a", "0a_2", "Prologue - Room 2", [reg for _, reg in all_regions.items() if reg.room_name == "0a_2"], [door for _, door in all_doors.items() if door.room_name == "0a_2"]),
- "0a_3": Room("0a", "0a_3", "Prologue - Room 3", [reg for _, reg in all_regions.items() if reg.room_name == "0a_3"], [door for _, door in all_doors.items() if door.room_name == "0a_3"]),
-
- "1a_1": Room("1a", "1a_1", "Forsaken City A - Room 1", [reg for _, reg in all_regions.items() if reg.room_name == "1a_1"], [door for _, door in all_doors.items() if door.room_name == "1a_1"], "Start", "1a_1_main"),
- "1a_2": Room("1a", "1a_2", "Forsaken City A - Room 2", [reg for _, reg in all_regions.items() if reg.room_name == "1a_2"], [door for _, door in all_doors.items() if door.room_name == "1a_2"]),
- "1a_3": Room("1a", "1a_3", "Forsaken City A - Room 3", [reg for _, reg in all_regions.items() if reg.room_name == "1a_3"], [door for _, door in all_doors.items() if door.room_name == "1a_3"]),
- "1a_4": Room("1a", "1a_4", "Forsaken City A - Room 4", [reg for _, reg in all_regions.items() if reg.room_name == "1a_4"], [door for _, door in all_doors.items() if door.room_name == "1a_4"]),
- "1a_3b": Room("1a", "1a_3b", "Forsaken City A - Room 3b", [reg for _, reg in all_regions.items() if reg.room_name == "1a_3b"], [door for _, door in all_doors.items() if door.room_name == "1a_3b"]),
- "1a_5": Room("1a", "1a_5", "Forsaken City A - Room 5", [reg for _, reg in all_regions.items() if reg.room_name == "1a_5"], [door for _, door in all_doors.items() if door.room_name == "1a_5"]),
- "1a_5z": Room("1a", "1a_5z", "Forsaken City A - Room 5z", [reg for _, reg in all_regions.items() if reg.room_name == "1a_5z"], [door for _, door in all_doors.items() if door.room_name == "1a_5z"]),
- "1a_5a": Room("1a", "1a_5a", "Forsaken City A - Room 5a", [reg for _, reg in all_regions.items() if reg.room_name == "1a_5a"], [door for _, door in all_doors.items() if door.room_name == "1a_5a"]),
- "1a_6": Room("1a", "1a_6", "Forsaken City A - Room 6", [reg for _, reg in all_regions.items() if reg.room_name == "1a_6"], [door for _, door in all_doors.items() if door.room_name == "1a_6"], "Crossing", "1a_6_south-west"),
- "1a_6z": Room("1a", "1a_6z", "Forsaken City A - Room 6z", [reg for _, reg in all_regions.items() if reg.room_name == "1a_6z"], [door for _, door in all_doors.items() if door.room_name == "1a_6z"]),
- "1a_6zb": Room("1a", "1a_6zb", "Forsaken City A - Room 6zb", [reg for _, reg in all_regions.items() if reg.room_name == "1a_6zb"], [door for _, door in all_doors.items() if door.room_name == "1a_6zb"]),
- "1a_7zb": Room("1a", "1a_7zb", "Forsaken City A - Room 7zb", [reg for _, reg in all_regions.items() if reg.room_name == "1a_7zb"], [door for _, door in all_doors.items() if door.room_name == "1a_7zb"]),
- "1a_6a": Room("1a", "1a_6a", "Forsaken City A - Room 6a", [reg for _, reg in all_regions.items() if reg.room_name == "1a_6a"], [door for _, door in all_doors.items() if door.room_name == "1a_6a"]),
- "1a_6b": Room("1a", "1a_6b", "Forsaken City A - Room 6b", [reg for _, reg in all_regions.items() if reg.room_name == "1a_6b"], [door for _, door in all_doors.items() if door.room_name == "1a_6b"]),
- "1a_s0": Room("1a", "1a_s0", "Forsaken City A - Room s0", [reg for _, reg in all_regions.items() if reg.room_name == "1a_s0"], [door for _, door in all_doors.items() if door.room_name == "1a_s0"]),
- "1a_s1": Room("1a", "1a_s1", "Forsaken City A - Room s1", [reg for _, reg in all_regions.items() if reg.room_name == "1a_s1"], [door for _, door in all_doors.items() if door.room_name == "1a_s1"]),
- "1a_6c": Room("1a", "1a_6c", "Forsaken City A - Room 6c", [reg for _, reg in all_regions.items() if reg.room_name == "1a_6c"], [door for _, door in all_doors.items() if door.room_name == "1a_6c"]),
- "1a_7": Room("1a", "1a_7", "Forsaken City A - Room 7", [reg for _, reg in all_regions.items() if reg.room_name == "1a_7"], [door for _, door in all_doors.items() if door.room_name == "1a_7"]),
- "1a_7z": Room("1a", "1a_7z", "Forsaken City A - Room 7z", [reg for _, reg in all_regions.items() if reg.room_name == "1a_7z"], [door for _, door in all_doors.items() if door.room_name == "1a_7z"]),
- "1a_8z": Room("1a", "1a_8z", "Forsaken City A - Room 8z", [reg for _, reg in all_regions.items() if reg.room_name == "1a_8z"], [door for _, door in all_doors.items() if door.room_name == "1a_8z"]),
- "1a_8zb": Room("1a", "1a_8zb", "Forsaken City A - Room 8zb", [reg for _, reg in all_regions.items() if reg.room_name == "1a_8zb"], [door for _, door in all_doors.items() if door.room_name == "1a_8zb"]),
- "1a_8": Room("1a", "1a_8", "Forsaken City A - Room 8", [reg for _, reg in all_regions.items() if reg.room_name == "1a_8"], [door for _, door in all_doors.items() if door.room_name == "1a_8"]),
- "1a_7a": Room("1a", "1a_7a", "Forsaken City A - Room 7a", [reg for _, reg in all_regions.items() if reg.room_name == "1a_7a"], [door for _, door in all_doors.items() if door.room_name == "1a_7a"]),
- "1a_9z": Room("1a", "1a_9z", "Forsaken City A - Room 9z", [reg for _, reg in all_regions.items() if reg.room_name == "1a_9z"], [door for _, door in all_doors.items() if door.room_name == "1a_9z"]),
- "1a_8b": Room("1a", "1a_8b", "Forsaken City A - Room 8b", [reg for _, reg in all_regions.items() if reg.room_name == "1a_8b"], [door for _, door in all_doors.items() if door.room_name == "1a_8b"]),
- "1a_9": Room("1a", "1a_9", "Forsaken City A - Room 9", [reg for _, reg in all_regions.items() if reg.room_name == "1a_9"], [door for _, door in all_doors.items() if door.room_name == "1a_9"]),
- "1a_9b": Room("1a", "1a_9b", "Forsaken City A - Room 9b", [reg for _, reg in all_regions.items() if reg.room_name == "1a_9b"], [door for _, door in all_doors.items() if door.room_name == "1a_9b"], "Chasm", "1a_9b_west"),
- "1a_9c": Room("1a", "1a_9c", "Forsaken City A - Room 9c", [reg for _, reg in all_regions.items() if reg.room_name == "1a_9c"], [door for _, door in all_doors.items() if door.room_name == "1a_9c"]),
- "1a_10": Room("1a", "1a_10", "Forsaken City A - Room 10", [reg for _, reg in all_regions.items() if reg.room_name == "1a_10"], [door for _, door in all_doors.items() if door.room_name == "1a_10"]),
- "1a_10z": Room("1a", "1a_10z", "Forsaken City A - Room 10z", [reg for _, reg in all_regions.items() if reg.room_name == "1a_10z"], [door for _, door in all_doors.items() if door.room_name == "1a_10z"]),
- "1a_10zb": Room("1a", "1a_10zb", "Forsaken City A - Room 10zb", [reg for _, reg in all_regions.items() if reg.room_name == "1a_10zb"], [door for _, door in all_doors.items() if door.room_name == "1a_10zb"]),
- "1a_11": Room("1a", "1a_11", "Forsaken City A - Room 11", [reg for _, reg in all_regions.items() if reg.room_name == "1a_11"], [door for _, door in all_doors.items() if door.room_name == "1a_11"]),
- "1a_11z": Room("1a", "1a_11z", "Forsaken City A - Room 11z", [reg for _, reg in all_regions.items() if reg.room_name == "1a_11z"], [door for _, door in all_doors.items() if door.room_name == "1a_11z"]),
- "1a_10a": Room("1a", "1a_10a", "Forsaken City A - Room 10a", [reg for _, reg in all_regions.items() if reg.room_name == "1a_10a"], [door for _, door in all_doors.items() if door.room_name == "1a_10a"]),
- "1a_12": Room("1a", "1a_12", "Forsaken City A - Room 12", [reg for _, reg in all_regions.items() if reg.room_name == "1a_12"], [door for _, door in all_doors.items() if door.room_name == "1a_12"]),
- "1a_12z": Room("1a", "1a_12z", "Forsaken City A - Room 12z", [reg for _, reg in all_regions.items() if reg.room_name == "1a_12z"], [door for _, door in all_doors.items() if door.room_name == "1a_12z"]),
- "1a_12a": Room("1a", "1a_12a", "Forsaken City A - Room 12a", [reg for _, reg in all_regions.items() if reg.room_name == "1a_12a"], [door for _, door in all_doors.items() if door.room_name == "1a_12a"]),
- "1a_end": Room("1a", "1a_end", "Forsaken City A - Room end", [reg for _, reg in all_regions.items() if reg.room_name == "1a_end"], [door for _, door in all_doors.items() if door.room_name == "1a_end"]),
-
- "1b_00": Room("1b", "1b_00", "Forsaken City B - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "1b_00"], [door for _, door in all_doors.items() if door.room_name == "1b_00"], "Start", "1b_00_west"),
- "1b_01": Room("1b", "1b_01", "Forsaken City B - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "1b_01"], [door for _, door in all_doors.items() if door.room_name == "1b_01"]),
- "1b_02": Room("1b", "1b_02", "Forsaken City B - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "1b_02"], [door for _, door in all_doors.items() if door.room_name == "1b_02"]),
- "1b_02b": Room("1b", "1b_02b", "Forsaken City B - Room 02b", [reg for _, reg in all_regions.items() if reg.room_name == "1b_02b"], [door for _, door in all_doors.items() if door.room_name == "1b_02b"]),
- "1b_03": Room("1b", "1b_03", "Forsaken City B - Room 03", [reg for _, reg in all_regions.items() if reg.room_name == "1b_03"], [door for _, door in all_doors.items() if door.room_name == "1b_03"]),
- "1b_04": Room("1b", "1b_04", "Forsaken City B - Room 04", [reg for _, reg in all_regions.items() if reg.room_name == "1b_04"], [door for _, door in all_doors.items() if door.room_name == "1b_04"], "Contraption", "1b_04_west"),
- "1b_05": Room("1b", "1b_05", "Forsaken City B - Room 05", [reg for _, reg in all_regions.items() if reg.room_name == "1b_05"], [door for _, door in all_doors.items() if door.room_name == "1b_05"]),
- "1b_05b": Room("1b", "1b_05b", "Forsaken City B - Room 05b", [reg for _, reg in all_regions.items() if reg.room_name == "1b_05b"], [door for _, door in all_doors.items() if door.room_name == "1b_05b"]),
- "1b_06": Room("1b", "1b_06", "Forsaken City B - Room 06", [reg for _, reg in all_regions.items() if reg.room_name == "1b_06"], [door for _, door in all_doors.items() if door.room_name == "1b_06"]),
- "1b_07": Room("1b", "1b_07", "Forsaken City B - Room 07", [reg for _, reg in all_regions.items() if reg.room_name == "1b_07"], [door for _, door in all_doors.items() if door.room_name == "1b_07"]),
- "1b_08": Room("1b", "1b_08", "Forsaken City B - Room 08", [reg for _, reg in all_regions.items() if reg.room_name == "1b_08"], [door for _, door in all_doors.items() if door.room_name == "1b_08"], "Scrap Pit", "1b_08_west"),
- "1b_08b": Room("1b", "1b_08b", "Forsaken City B - Room 08b", [reg for _, reg in all_regions.items() if reg.room_name == "1b_08b"], [door for _, door in all_doors.items() if door.room_name == "1b_08b"]),
- "1b_09": Room("1b", "1b_09", "Forsaken City B - Room 09", [reg for _, reg in all_regions.items() if reg.room_name == "1b_09"], [door for _, door in all_doors.items() if door.room_name == "1b_09"]),
- "1b_10": Room("1b", "1b_10", "Forsaken City B - Room 10", [reg for _, reg in all_regions.items() if reg.room_name == "1b_10"], [door for _, door in all_doors.items() if door.room_name == "1b_10"]),
- "1b_11": Room("1b", "1b_11", "Forsaken City B - Room 11", [reg for _, reg in all_regions.items() if reg.room_name == "1b_11"], [door for _, door in all_doors.items() if door.room_name == "1b_11"]),
- "1b_end": Room("1b", "1b_end", "Forsaken City B - Room end", [reg for _, reg in all_regions.items() if reg.room_name == "1b_end"], [door for _, door in all_doors.items() if door.room_name == "1b_end"]),
-
- "1c_00": Room("1c", "1c_00", "Forsaken City C - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "1c_00"], [door for _, door in all_doors.items() if door.room_name == "1c_00"], "Start", "1c_00_west"),
- "1c_01": Room("1c", "1c_01", "Forsaken City C - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "1c_01"], [door for _, door in all_doors.items() if door.room_name == "1c_01"]),
- "1c_02": Room("1c", "1c_02", "Forsaken City C - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "1c_02"], [door for _, door in all_doors.items() if door.room_name == "1c_02"]),
-
- "2a_start": Room("2a", "2a_start", "Old Site A - Room start", [reg for _, reg in all_regions.items() if reg.room_name == "2a_start"], [door for _, door in all_doors.items() if door.room_name == "2a_start"], "Start", "2a_start_main"),
- "2a_s0": Room("2a", "2a_s0", "Old Site A - Room s0", [reg for _, reg in all_regions.items() if reg.room_name == "2a_s0"], [door for _, door in all_doors.items() if door.room_name == "2a_s0"]),
- "2a_s1": Room("2a", "2a_s1", "Old Site A - Room s1", [reg for _, reg in all_regions.items() if reg.room_name == "2a_s1"], [door for _, door in all_doors.items() if door.room_name == "2a_s1"]),
- "2a_s2": Room("2a", "2a_s2", "Old Site A - Room s2", [reg for _, reg in all_regions.items() if reg.room_name == "2a_s2"], [door for _, door in all_doors.items() if door.room_name == "2a_s2"]),
- "2a_0": Room("2a", "2a_0", "Old Site A - Room 0", [reg for _, reg in all_regions.items() if reg.room_name == "2a_0"], [door for _, door in all_doors.items() if door.room_name == "2a_0"]),
- "2a_1": Room("2a", "2a_1", "Old Site A - Room 1", [reg for _, reg in all_regions.items() if reg.room_name == "2a_1"], [door for _, door in all_doors.items() if door.room_name == "2a_1"]),
- "2a_d0": Room("2a", "2a_d0", "Old Site A - Room d0", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d0"], [door for _, door in all_doors.items() if door.room_name == "2a_d0"]),
- "2a_d7": Room("2a", "2a_d7", "Old Site A - Room d7", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d7"], [door for _, door in all_doors.items() if door.room_name == "2a_d7"]),
- "2a_d8": Room("2a", "2a_d8", "Old Site A - Room d8", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d8"], [door for _, door in all_doors.items() if door.room_name == "2a_d8"]),
- "2a_d3": Room("2a", "2a_d3", "Old Site A - Room d3", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d3"], [door for _, door in all_doors.items() if door.room_name == "2a_d3"]),
- "2a_d2": Room("2a", "2a_d2", "Old Site A - Room d2", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d2"], [door for _, door in all_doors.items() if door.room_name == "2a_d2"]),
- "2a_d9": Room("2a", "2a_d9", "Old Site A - Room d9", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d9"], [door for _, door in all_doors.items() if door.room_name == "2a_d9"]),
- "2a_d1": Room("2a", "2a_d1", "Old Site A - Room d1", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d1"], [door for _, door in all_doors.items() if door.room_name == "2a_d1"]),
- "2a_d6": Room("2a", "2a_d6", "Old Site A - Room d6", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d6"], [door for _, door in all_doors.items() if door.room_name == "2a_d6"]),
- "2a_d4": Room("2a", "2a_d4", "Old Site A - Room d4", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d4"], [door for _, door in all_doors.items() if door.room_name == "2a_d4"]),
- "2a_d5": Room("2a", "2a_d5", "Old Site A - Room d5", [reg for _, reg in all_regions.items() if reg.room_name == "2a_d5"], [door for _, door in all_doors.items() if door.room_name == "2a_d5"]),
- "2a_3x": Room("2a", "2a_3x", "Old Site A - Room 3x", [reg for _, reg in all_regions.items() if reg.room_name == "2a_3x"], [door for _, door in all_doors.items() if door.room_name == "2a_3x"]),
- "2a_3": Room("2a", "2a_3", "Old Site A - Room 3", [reg for _, reg in all_regions.items() if reg.room_name == "2a_3"], [door for _, door in all_doors.items() if door.room_name == "2a_3"], "Intervention", "2a_3_bottom"),
- "2a_4": Room("2a", "2a_4", "Old Site A - Room 4", [reg for _, reg in all_regions.items() if reg.room_name == "2a_4"], [door for _, door in all_doors.items() if door.room_name == "2a_4"]),
- "2a_5": Room("2a", "2a_5", "Old Site A - Room 5", [reg for _, reg in all_regions.items() if reg.room_name == "2a_5"], [door for _, door in all_doors.items() if door.room_name == "2a_5"]),
- "2a_6": Room("2a", "2a_6", "Old Site A - Room 6", [reg for _, reg in all_regions.items() if reg.room_name == "2a_6"], [door for _, door in all_doors.items() if door.room_name == "2a_6"]),
- "2a_7": Room("2a", "2a_7", "Old Site A - Room 7", [reg for _, reg in all_regions.items() if reg.room_name == "2a_7"], [door for _, door in all_doors.items() if door.room_name == "2a_7"]),
- "2a_8": Room("2a", "2a_8", "Old Site A - Room 8", [reg for _, reg in all_regions.items() if reg.room_name == "2a_8"], [door for _, door in all_doors.items() if door.room_name == "2a_8"]),
- "2a_9": Room("2a", "2a_9", "Old Site A - Room 9", [reg for _, reg in all_regions.items() if reg.room_name == "2a_9"], [door for _, door in all_doors.items() if door.room_name == "2a_9"]),
- "2a_9b": Room("2a", "2a_9b", "Old Site A - Room 9b", [reg for _, reg in all_regions.items() if reg.room_name == "2a_9b"], [door for _, door in all_doors.items() if door.room_name == "2a_9b"]),
- "2a_10": Room("2a", "2a_10", "Old Site A - Room 10", [reg for _, reg in all_regions.items() if reg.room_name == "2a_10"], [door for _, door in all_doors.items() if door.room_name == "2a_10"]),
- "2a_2": Room("2a", "2a_2", "Old Site A - Room 2", [reg for _, reg in all_regions.items() if reg.room_name == "2a_2"], [door for _, door in all_doors.items() if door.room_name == "2a_2"]),
- "2a_11": Room("2a", "2a_11", "Old Site A - Room 11", [reg for _, reg in all_regions.items() if reg.room_name == "2a_11"], [door for _, door in all_doors.items() if door.room_name == "2a_11"]),
- "2a_12b": Room("2a", "2a_12b", "Old Site A - Room 12b", [reg for _, reg in all_regions.items() if reg.room_name == "2a_12b"], [door for _, door in all_doors.items() if door.room_name == "2a_12b"]),
- "2a_12c": Room("2a", "2a_12c", "Old Site A - Room 12c", [reg for _, reg in all_regions.items() if reg.room_name == "2a_12c"], [door for _, door in all_doors.items() if door.room_name == "2a_12c"]),
- "2a_12d": Room("2a", "2a_12d", "Old Site A - Room 12d", [reg for _, reg in all_regions.items() if reg.room_name == "2a_12d"], [door for _, door in all_doors.items() if door.room_name == "2a_12d"]),
- "2a_12": Room("2a", "2a_12", "Old Site A - Room 12", [reg for _, reg in all_regions.items() if reg.room_name == "2a_12"], [door for _, door in all_doors.items() if door.room_name == "2a_12"]),
- "2a_13": Room("2a", "2a_13", "Old Site A - Room 13", [reg for _, reg in all_regions.items() if reg.room_name == "2a_13"], [door for _, door in all_doors.items() if door.room_name == "2a_13"]),
- "2a_end_0": Room("2a", "2a_end_0", "Old Site A - Room end_0", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_0"], [door for _, door in all_doors.items() if door.room_name == "2a_end_0"]),
- "2a_end_s0": Room("2a", "2a_end_s0", "Old Site A - Room end_s0", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_s0"], [door for _, door in all_doors.items() if door.room_name == "2a_end_s0"]),
- "2a_end_s1": Room("2a", "2a_end_s1", "Old Site A - Room end_s1", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_s1"], [door for _, door in all_doors.items() if door.room_name == "2a_end_s1"]),
- "2a_end_1": Room("2a", "2a_end_1", "Old Site A - Room end_1", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_1"], [door for _, door in all_doors.items() if door.room_name == "2a_end_1"]),
- "2a_end_2": Room("2a", "2a_end_2", "Old Site A - Room end_2", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_2"], [door for _, door in all_doors.items() if door.room_name == "2a_end_2"]),
- "2a_end_3": Room("2a", "2a_end_3", "Old Site A - Room end_3", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_3"], [door for _, door in all_doors.items() if door.room_name == "2a_end_3"], "Awake", "2a_end_3_west"),
- "2a_end_4": Room("2a", "2a_end_4", "Old Site A - Room end_4", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_4"], [door for _, door in all_doors.items() if door.room_name == "2a_end_4"]),
- "2a_end_3b": Room("2a", "2a_end_3b", "Old Site A - Room end_3b", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_3b"], [door for _, door in all_doors.items() if door.room_name == "2a_end_3b"]),
- "2a_end_3cb": Room("2a", "2a_end_3cb", "Old Site A - Room end_3cb", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_3cb"], [door for _, door in all_doors.items() if door.room_name == "2a_end_3cb"]),
- "2a_end_3c": Room("2a", "2a_end_3c", "Old Site A - Room end_3c", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_3c"], [door for _, door in all_doors.items() if door.room_name == "2a_end_3c"]),
- "2a_end_5": Room("2a", "2a_end_5", "Old Site A - Room end_5", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_5"], [door for _, door in all_doors.items() if door.room_name == "2a_end_5"]),
- "2a_end_6": Room("2a", "2a_end_6", "Old Site A - Room end_6", [reg for _, reg in all_regions.items() if reg.room_name == "2a_end_6"], [door for _, door in all_doors.items() if door.room_name == "2a_end_6"]),
-
- "2b_start": Room("2b", "2b_start", "Old Site B - Room start", [reg for _, reg in all_regions.items() if reg.room_name == "2b_start"], [door for _, door in all_doors.items() if door.room_name == "2b_start"], "Start", "2b_start_west"),
- "2b_00": Room("2b", "2b_00", "Old Site B - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "2b_00"], [door for _, door in all_doors.items() if door.room_name == "2b_00"]),
- "2b_01": Room("2b", "2b_01", "Old Site B - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "2b_01"], [door for _, door in all_doors.items() if door.room_name == "2b_01"]),
- "2b_01b": Room("2b", "2b_01b", "Old Site B - Room 01b", [reg for _, reg in all_regions.items() if reg.room_name == "2b_01b"], [door for _, door in all_doors.items() if door.room_name == "2b_01b"]),
- "2b_02b": Room("2b", "2b_02b", "Old Site B - Room 02b", [reg for _, reg in all_regions.items() if reg.room_name == "2b_02b"], [door for _, door in all_doors.items() if door.room_name == "2b_02b"]),
- "2b_02": Room("2b", "2b_02", "Old Site B - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "2b_02"], [door for _, door in all_doors.items() if door.room_name == "2b_02"]),
- "2b_03": Room("2b", "2b_03", "Old Site B - Room 03", [reg for _, reg in all_regions.items() if reg.room_name == "2b_03"], [door for _, door in all_doors.items() if door.room_name == "2b_03"], "Combination Lock", "2b_03_west"),
- "2b_04": Room("2b", "2b_04", "Old Site B - Room 04", [reg for _, reg in all_regions.items() if reg.room_name == "2b_04"], [door for _, door in all_doors.items() if door.room_name == "2b_04"]),
- "2b_05": Room("2b", "2b_05", "Old Site B - Room 05", [reg for _, reg in all_regions.items() if reg.room_name == "2b_05"], [door for _, door in all_doors.items() if door.room_name == "2b_05"]),
- "2b_06": Room("2b", "2b_06", "Old Site B - Room 06", [reg for _, reg in all_regions.items() if reg.room_name == "2b_06"], [door for _, door in all_doors.items() if door.room_name == "2b_06"]),
- "2b_07": Room("2b", "2b_07", "Old Site B - Room 07", [reg for _, reg in all_regions.items() if reg.room_name == "2b_07"], [door for _, door in all_doors.items() if door.room_name == "2b_07"]),
- "2b_08b": Room("2b", "2b_08b", "Old Site B - Room 08b", [reg for _, reg in all_regions.items() if reg.room_name == "2b_08b"], [door for _, door in all_doors.items() if door.room_name == "2b_08b"], "Dream Altar", "2b_08b_west"),
- "2b_08": Room("2b", "2b_08", "Old Site B - Room 08", [reg for _, reg in all_regions.items() if reg.room_name == "2b_08"], [door for _, door in all_doors.items() if door.room_name == "2b_08"]),
- "2b_09": Room("2b", "2b_09", "Old Site B - Room 09", [reg for _, reg in all_regions.items() if reg.room_name == "2b_09"], [door for _, door in all_doors.items() if door.room_name == "2b_09"]),
- "2b_10": Room("2b", "2b_10", "Old Site B - Room 10", [reg for _, reg in all_regions.items() if reg.room_name == "2b_10"], [door for _, door in all_doors.items() if door.room_name == "2b_10"]),
- "2b_11": Room("2b", "2b_11", "Old Site B - Room 11", [reg for _, reg in all_regions.items() if reg.room_name == "2b_11"], [door for _, door in all_doors.items() if door.room_name == "2b_11"]),
- "2b_end": Room("2b", "2b_end", "Old Site B - Room end", [reg for _, reg in all_regions.items() if reg.room_name == "2b_end"], [door for _, door in all_doors.items() if door.room_name == "2b_end"]),
-
- "2c_00": Room("2c", "2c_00", "Old Site C - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "2c_00"], [door for _, door in all_doors.items() if door.room_name == "2c_00"], "Start", "2c_00_west"),
- "2c_01": Room("2c", "2c_01", "Old Site C - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "2c_01"], [door for _, door in all_doors.items() if door.room_name == "2c_01"]),
- "2c_02": Room("2c", "2c_02", "Old Site C - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "2c_02"], [door for _, door in all_doors.items() if door.room_name == "2c_02"]),
-
- "3a_s0": Room("3a", "3a_s0", "Celestial Resort A - Room s0", [reg for _, reg in all_regions.items() if reg.room_name == "3a_s0"], [door for _, door in all_doors.items() if door.room_name == "3a_s0"], "Start", "3a_s0_main"),
- "3a_s1": Room("3a", "3a_s1", "Celestial Resort A - Room s1", [reg for _, reg in all_regions.items() if reg.room_name == "3a_s1"], [door for _, door in all_doors.items() if door.room_name == "3a_s1"]),
- "3a_s2": Room("3a", "3a_s2", "Celestial Resort A - Room s2", [reg for _, reg in all_regions.items() if reg.room_name == "3a_s2"], [door for _, door in all_doors.items() if door.room_name == "3a_s2"]),
- "3a_s3": Room("3a", "3a_s3", "Celestial Resort A - Room s3", [reg for _, reg in all_regions.items() if reg.room_name == "3a_s3"], [door for _, door in all_doors.items() if door.room_name == "3a_s3"]),
- "3a_0x-a": Room("3a", "3a_0x-a", "Celestial Resort A - Room 0x-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_0x-a"], [door for _, door in all_doors.items() if door.room_name == "3a_0x-a"]),
- "3a_00-a": Room("3a", "3a_00-a", "Celestial Resort A - Room 00-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_00-a"], [door for _, door in all_doors.items() if door.room_name == "3a_00-a"]),
- "3a_02-a": Room("3a", "3a_02-a", "Celestial Resort A - Room 02-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_02-a"], [door for _, door in all_doors.items() if door.room_name == "3a_02-a"]),
- "3a_02-b": Room("3a", "3a_02-b", "Celestial Resort A - Room 02-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_02-b"], [door for _, door in all_doors.items() if door.room_name == "3a_02-b"]),
- "3a_01-b": Room("3a", "3a_01-b", "Celestial Resort A - Room 01-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_01-b"], [door for _, door in all_doors.items() if door.room_name == "3a_01-b"]),
- "3a_00-b": Room("3a", "3a_00-b", "Celestial Resort A - Room 00-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_00-b"], [door for _, door in all_doors.items() if door.room_name == "3a_00-b"]),
- "3a_00-c": Room("3a", "3a_00-c", "Celestial Resort A - Room 00-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_00-c"], [door for _, door in all_doors.items() if door.room_name == "3a_00-c"]),
- "3a_0x-b": Room("3a", "3a_0x-b", "Celestial Resort A - Room 0x-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_0x-b"], [door for _, door in all_doors.items() if door.room_name == "3a_0x-b"]),
- "3a_03-a": Room("3a", "3a_03-a", "Celestial Resort A - Room 03-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_03-a"], [door for _, door in all_doors.items() if door.room_name == "3a_03-a"]),
- "3a_04-b": Room("3a", "3a_04-b", "Celestial Resort A - Room 04-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_04-b"], [door for _, door in all_doors.items() if door.room_name == "3a_04-b"]),
- "3a_05-a": Room("3a", "3a_05-a", "Celestial Resort A - Room 05-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_05-a"], [door for _, door in all_doors.items() if door.room_name == "3a_05-a"]),
- "3a_06-a": Room("3a", "3a_06-a", "Celestial Resort A - Room 06-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_06-a"], [door for _, door in all_doors.items() if door.room_name == "3a_06-a"]),
- "3a_07-a": Room("3a", "3a_07-a", "Celestial Resort A - Room 07-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_07-a"], [door for _, door in all_doors.items() if door.room_name == "3a_07-a"]),
- "3a_07-b": Room("3a", "3a_07-b", "Celestial Resort A - Room 07-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_07-b"], [door for _, door in all_doors.items() if door.room_name == "3a_07-b"]),
- "3a_06-b": Room("3a", "3a_06-b", "Celestial Resort A - Room 06-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_06-b"], [door for _, door in all_doors.items() if door.room_name == "3a_06-b"]),
- "3a_06-c": Room("3a", "3a_06-c", "Celestial Resort A - Room 06-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_06-c"], [door for _, door in all_doors.items() if door.room_name == "3a_06-c"]),
- "3a_05-c": Room("3a", "3a_05-c", "Celestial Resort A - Room 05-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_05-c"], [door for _, door in all_doors.items() if door.room_name == "3a_05-c"]),
- "3a_08-c": Room("3a", "3a_08-c", "Celestial Resort A - Room 08-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_08-c"], [door for _, door in all_doors.items() if door.room_name == "3a_08-c"]),
- "3a_08-b": Room("3a", "3a_08-b", "Celestial Resort A - Room 08-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_08-b"], [door for _, door in all_doors.items() if door.room_name == "3a_08-b"]),
- "3a_08-a": Room("3a", "3a_08-a", "Celestial Resort A - Room 08-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_08-a"], [door for _, door in all_doors.items() if door.room_name == "3a_08-a"], "Huge Mess", "3a_08-a_west"),
- "3a_09-b": Room("3a", "3a_09-b", "Celestial Resort A - Room 09-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_09-b"], [door for _, door in all_doors.items() if door.room_name == "3a_09-b"]),
- "3a_10-x": Room("3a", "3a_10-x", "Celestial Resort A - Room 10-x", [reg for _, reg in all_regions.items() if reg.room_name == "3a_10-x"], [door for _, door in all_doors.items() if door.room_name == "3a_10-x"]),
- "3a_11-x": Room("3a", "3a_11-x", "Celestial Resort A - Room 11-x", [reg for _, reg in all_regions.items() if reg.room_name == "3a_11-x"], [door for _, door in all_doors.items() if door.room_name == "3a_11-x"]),
- "3a_11-y": Room("3a", "3a_11-y", "Celestial Resort A - Room 11-y", [reg for _, reg in all_regions.items() if reg.room_name == "3a_11-y"], [door for _, door in all_doors.items() if door.room_name == "3a_11-y"]),
- "3a_12-y": Room("3a", "3a_12-y", "Celestial Resort A - Room 12-y", [reg for _, reg in all_regions.items() if reg.room_name == "3a_12-y"], [door for _, door in all_doors.items() if door.room_name == "3a_12-y"]),
- "3a_11-z": Room("3a", "3a_11-z", "Celestial Resort A - Room 11-z", [reg for _, reg in all_regions.items() if reg.room_name == "3a_11-z"], [door for _, door in all_doors.items() if door.room_name == "3a_11-z"]),
- "3a_10-z": Room("3a", "3a_10-z", "Celestial Resort A - Room 10-z", [reg for _, reg in all_regions.items() if reg.room_name == "3a_10-z"], [door for _, door in all_doors.items() if door.room_name == "3a_10-z"]),
- "3a_10-y": Room("3a", "3a_10-y", "Celestial Resort A - Room 10-y", [reg for _, reg in all_regions.items() if reg.room_name == "3a_10-y"], [door for _, door in all_doors.items() if door.room_name == "3a_10-y"]),
- "3a_10-c": Room("3a", "3a_10-c", "Celestial Resort A - Room 10-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_10-c"], [door for _, door in all_doors.items() if door.room_name == "3a_10-c"]),
- "3a_11-c": Room("3a", "3a_11-c", "Celestial Resort A - Room 11-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_11-c"], [door for _, door in all_doors.items() if door.room_name == "3a_11-c"]),
- "3a_12-c": Room("3a", "3a_12-c", "Celestial Resort A - Room 12-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_12-c"], [door for _, door in all_doors.items() if door.room_name == "3a_12-c"]),
- "3a_12-d": Room("3a", "3a_12-d", "Celestial Resort A - Room 12-d", [reg for _, reg in all_regions.items() if reg.room_name == "3a_12-d"], [door for _, door in all_doors.items() if door.room_name == "3a_12-d"]),
- "3a_11-d": Room("3a", "3a_11-d", "Celestial Resort A - Room 11-d", [reg for _, reg in all_regions.items() if reg.room_name == "3a_11-d"], [door for _, door in all_doors.items() if door.room_name == "3a_11-d"]),
- "3a_10-d": Room("3a", "3a_10-d", "Celestial Resort A - Room 10-d", [reg for _, reg in all_regions.items() if reg.room_name == "3a_10-d"], [door for _, door in all_doors.items() if door.room_name == "3a_10-d"]),
- "3a_11-b": Room("3a", "3a_11-b", "Celestial Resort A - Room 11-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_11-b"], [door for _, door in all_doors.items() if door.room_name == "3a_11-b"]),
- "3a_12-b": Room("3a", "3a_12-b", "Celestial Resort A - Room 12-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_12-b"], [door for _, door in all_doors.items() if door.room_name == "3a_12-b"]),
- "3a_13-b": Room("3a", "3a_13-b", "Celestial Resort A - Room 13-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_13-b"], [door for _, door in all_doors.items() if door.room_name == "3a_13-b"]),
- "3a_13-a": Room("3a", "3a_13-a", "Celestial Resort A - Room 13-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_13-a"], [door for _, door in all_doors.items() if door.room_name == "3a_13-a"]),
- "3a_13-x": Room("3a", "3a_13-x", "Celestial Resort A - Room 13-x", [reg for _, reg in all_regions.items() if reg.room_name == "3a_13-x"], [door for _, door in all_doors.items() if door.room_name == "3a_13-x"]),
- "3a_12-x": Room("3a", "3a_12-x", "Celestial Resort A - Room 12-x", [reg for _, reg in all_regions.items() if reg.room_name == "3a_12-x"], [door for _, door in all_doors.items() if door.room_name == "3a_12-x"]),
- "3a_11-a": Room("3a", "3a_11-a", "Celestial Resort A - Room 11-a", [reg for _, reg in all_regions.items() if reg.room_name == "3a_11-a"], [door for _, door in all_doors.items() if door.room_name == "3a_11-a"]),
- "3a_08-x": Room("3a", "3a_08-x", "Celestial Resort A - Room 08-x", [reg for _, reg in all_regions.items() if reg.room_name == "3a_08-x"], [door for _, door in all_doors.items() if door.room_name == "3a_08-x"]),
- "3a_09-d": Room("3a", "3a_09-d", "Celestial Resort A - Room 09-d", [reg for _, reg in all_regions.items() if reg.room_name == "3a_09-d"], [door for _, door in all_doors.items() if door.room_name == "3a_09-d"], "Elevator Shaft", "3a_09-d_bottom"),
- "3a_08-d": Room("3a", "3a_08-d", "Celestial Resort A - Room 08-d", [reg for _, reg in all_regions.items() if reg.room_name == "3a_08-d"], [door for _, door in all_doors.items() if door.room_name == "3a_08-d"]),
- "3a_06-d": Room("3a", "3a_06-d", "Celestial Resort A - Room 06-d", [reg for _, reg in all_regions.items() if reg.room_name == "3a_06-d"], [door for _, door in all_doors.items() if door.room_name == "3a_06-d"]),
- "3a_04-d": Room("3a", "3a_04-d", "Celestial Resort A - Room 04-d", [reg for _, reg in all_regions.items() if reg.room_name == "3a_04-d"], [door for _, door in all_doors.items() if door.room_name == "3a_04-d"]),
- "3a_04-c": Room("3a", "3a_04-c", "Celestial Resort A - Room 04-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_04-c"], [door for _, door in all_doors.items() if door.room_name == "3a_04-c"]),
- "3a_02-c": Room("3a", "3a_02-c", "Celestial Resort A - Room 02-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_02-c"], [door for _, door in all_doors.items() if door.room_name == "3a_02-c"]),
- "3a_03-b": Room("3a", "3a_03-b", "Celestial Resort A - Room 03-b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_03-b"], [door for _, door in all_doors.items() if door.room_name == "3a_03-b"]),
- "3a_01-c": Room("3a", "3a_01-c", "Celestial Resort A - Room 01-c", [reg for _, reg in all_regions.items() if reg.room_name == "3a_01-c"], [door for _, door in all_doors.items() if door.room_name == "3a_01-c"]),
- "3a_02-d": Room("3a", "3a_02-d", "Celestial Resort A - Room 02-d", [reg for _, reg in all_regions.items() if reg.room_name == "3a_02-d"], [door for _, door in all_doors.items() if door.room_name == "3a_02-d"]),
- "3a_00-d": Room("3a", "3a_00-d", "Celestial Resort A - Room 00-d", [reg for _, reg in all_regions.items() if reg.room_name == "3a_00-d"], [door for _, door in all_doors.items() if door.room_name == "3a_00-d"], "Presidential Suite", "3a_00-d_east"),
- "3a_roof00": Room("3a", "3a_roof00", "Celestial Resort A - Room roof00", [reg for _, reg in all_regions.items() if reg.room_name == "3a_roof00"], [door for _, door in all_doors.items() if door.room_name == "3a_roof00"]),
- "3a_roof01": Room("3a", "3a_roof01", "Celestial Resort A - Room roof01", [reg for _, reg in all_regions.items() if reg.room_name == "3a_roof01"], [door for _, door in all_doors.items() if door.room_name == "3a_roof01"]),
- "3a_roof02": Room("3a", "3a_roof02", "Celestial Resort A - Room roof02", [reg for _, reg in all_regions.items() if reg.room_name == "3a_roof02"], [door for _, door in all_doors.items() if door.room_name == "3a_roof02"]),
- "3a_roof03": Room("3a", "3a_roof03", "Celestial Resort A - Room roof03", [reg for _, reg in all_regions.items() if reg.room_name == "3a_roof03"], [door for _, door in all_doors.items() if door.room_name == "3a_roof03"]),
- "3a_roof04": Room("3a", "3a_roof04", "Celestial Resort A - Room roof04", [reg for _, reg in all_regions.items() if reg.room_name == "3a_roof04"], [door for _, door in all_doors.items() if door.room_name == "3a_roof04"]),
- "3a_roof05": Room("3a", "3a_roof05", "Celestial Resort A - Room roof05", [reg for _, reg in all_regions.items() if reg.room_name == "3a_roof05"], [door for _, door in all_doors.items() if door.room_name == "3a_roof05"]),
- "3a_roof06b": Room("3a", "3a_roof06b", "Celestial Resort A - Room roof06b", [reg for _, reg in all_regions.items() if reg.room_name == "3a_roof06b"], [door for _, door in all_doors.items() if door.room_name == "3a_roof06b"]),
- "3a_roof06": Room("3a", "3a_roof06", "Celestial Resort A - Room roof06", [reg for _, reg in all_regions.items() if reg.room_name == "3a_roof06"], [door for _, door in all_doors.items() if door.room_name == "3a_roof06"]),
- "3a_roof07": Room("3a", "3a_roof07", "Celestial Resort A - Room roof07", [reg for _, reg in all_regions.items() if reg.room_name == "3a_roof07"], [door for _, door in all_doors.items() if door.room_name == "3a_roof07"]),
-
- "3b_00": Room("3b", "3b_00", "Celestial Resort B - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "3b_00"], [door for _, door in all_doors.items() if door.room_name == "3b_00"], "Start", "3b_00_west"),
- "3b_back": Room("3b", "3b_back", "Celestial Resort B - Room back", [reg for _, reg in all_regions.items() if reg.room_name == "3b_back"], [door for _, door in all_doors.items() if door.room_name == "3b_back"]),
- "3b_01": Room("3b", "3b_01", "Celestial Resort B - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "3b_01"], [door for _, door in all_doors.items() if door.room_name == "3b_01"]),
- "3b_02": Room("3b", "3b_02", "Celestial Resort B - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "3b_02"], [door for _, door in all_doors.items() if door.room_name == "3b_02"]),
- "3b_03": Room("3b", "3b_03", "Celestial Resort B - Room 03", [reg for _, reg in all_regions.items() if reg.room_name == "3b_03"], [door for _, door in all_doors.items() if door.room_name == "3b_03"]),
- "3b_04": Room("3b", "3b_04", "Celestial Resort B - Room 04", [reg for _, reg in all_regions.items() if reg.room_name == "3b_04"], [door for _, door in all_doors.items() if door.room_name == "3b_04"]),
- "3b_05": Room("3b", "3b_05", "Celestial Resort B - Room 05", [reg for _, reg in all_regions.items() if reg.room_name == "3b_05"], [door for _, door in all_doors.items() if door.room_name == "3b_05"]),
- "3b_06": Room("3b", "3b_06", "Celestial Resort B - Room 06", [reg for _, reg in all_regions.items() if reg.room_name == "3b_06"], [door for _, door in all_doors.items() if door.room_name == "3b_06"], "Staff Quarters", "3b_06_west"),
- "3b_07": Room("3b", "3b_07", "Celestial Resort B - Room 07", [reg for _, reg in all_regions.items() if reg.room_name == "3b_07"], [door for _, door in all_doors.items() if door.room_name == "3b_07"]),
- "3b_08": Room("3b", "3b_08", "Celestial Resort B - Room 08", [reg for _, reg in all_regions.items() if reg.room_name == "3b_08"], [door for _, door in all_doors.items() if door.room_name == "3b_08"]),
- "3b_09": Room("3b", "3b_09", "Celestial Resort B - Room 09", [reg for _, reg in all_regions.items() if reg.room_name == "3b_09"], [door for _, door in all_doors.items() if door.room_name == "3b_09"]),
- "3b_10": Room("3b", "3b_10", "Celestial Resort B - Room 10", [reg for _, reg in all_regions.items() if reg.room_name == "3b_10"], [door for _, door in all_doors.items() if door.room_name == "3b_10"]),
- "3b_11": Room("3b", "3b_11", "Celestial Resort B - Room 11", [reg for _, reg in all_regions.items() if reg.room_name == "3b_11"], [door for _, door in all_doors.items() if door.room_name == "3b_11"], "Library", "3b_11_west"),
- "3b_13": Room("3b", "3b_13", "Celestial Resort B - Room 13", [reg for _, reg in all_regions.items() if reg.room_name == "3b_13"], [door for _, door in all_doors.items() if door.room_name == "3b_13"]),
- "3b_14": Room("3b", "3b_14", "Celestial Resort B - Room 14", [reg for _, reg in all_regions.items() if reg.room_name == "3b_14"], [door for _, door in all_doors.items() if door.room_name == "3b_14"]),
- "3b_15": Room("3b", "3b_15", "Celestial Resort B - Room 15", [reg for _, reg in all_regions.items() if reg.room_name == "3b_15"], [door for _, door in all_doors.items() if door.room_name == "3b_15"]),
- "3b_12": Room("3b", "3b_12", "Celestial Resort B - Room 12", [reg for _, reg in all_regions.items() if reg.room_name == "3b_12"], [door for _, door in all_doors.items() if door.room_name == "3b_12"]),
- "3b_16": Room("3b", "3b_16", "Celestial Resort B - Room 16", [reg for _, reg in all_regions.items() if reg.room_name == "3b_16"], [door for _, door in all_doors.items() if door.room_name == "3b_16"], "Rooftop", "3b_16_west"),
- "3b_17": Room("3b", "3b_17", "Celestial Resort B - Room 17", [reg for _, reg in all_regions.items() if reg.room_name == "3b_17"], [door for _, door in all_doors.items() if door.room_name == "3b_17"]),
- "3b_18": Room("3b", "3b_18", "Celestial Resort B - Room 18", [reg for _, reg in all_regions.items() if reg.room_name == "3b_18"], [door for _, door in all_doors.items() if door.room_name == "3b_18"]),
- "3b_19": Room("3b", "3b_19", "Celestial Resort B - Room 19", [reg for _, reg in all_regions.items() if reg.room_name == "3b_19"], [door for _, door in all_doors.items() if door.room_name == "3b_19"]),
- "3b_21": Room("3b", "3b_21", "Celestial Resort B - Room 21", [reg for _, reg in all_regions.items() if reg.room_name == "3b_21"], [door for _, door in all_doors.items() if door.room_name == "3b_21"]),
- "3b_20": Room("3b", "3b_20", "Celestial Resort B - Room 20", [reg for _, reg in all_regions.items() if reg.room_name == "3b_20"], [door for _, door in all_doors.items() if door.room_name == "3b_20"]),
- "3b_end": Room("3b", "3b_end", "Celestial Resort B - Room end", [reg for _, reg in all_regions.items() if reg.room_name == "3b_end"], [door for _, door in all_doors.items() if door.room_name == "3b_end"]),
-
- "3c_00": Room("3c", "3c_00", "Celestial Resort C - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "3c_00"], [door for _, door in all_doors.items() if door.room_name == "3c_00"], "Start", "3c_00_west"),
- "3c_01": Room("3c", "3c_01", "Celestial Resort C - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "3c_01"], [door for _, door in all_doors.items() if door.room_name == "3c_01"]),
- "3c_02": Room("3c", "3c_02", "Celestial Resort C - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "3c_02"], [door for _, door in all_doors.items() if door.room_name == "3c_02"]),
-
- "4a_a-00": Room("4a", "4a_a-00", "Golden Ridge A - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-00"], [door for _, door in all_doors.items() if door.room_name == "4a_a-00"], "Start", "4a_a-00_west"),
- "4a_a-01": Room("4a", "4a_a-01", "Golden Ridge A - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-01"], [door for _, door in all_doors.items() if door.room_name == "4a_a-01"]),
- "4a_a-01x": Room("4a", "4a_a-01x", "Golden Ridge A - Room a-01x", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-01x"], [door for _, door in all_doors.items() if door.room_name == "4a_a-01x"]),
- "4a_a-02": Room("4a", "4a_a-02", "Golden Ridge A - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-02"], [door for _, door in all_doors.items() if door.room_name == "4a_a-02"]),
- "4a_a-03": Room("4a", "4a_a-03", "Golden Ridge A - Room a-03", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-03"], [door for _, door in all_doors.items() if door.room_name == "4a_a-03"]),
- "4a_a-04": Room("4a", "4a_a-04", "Golden Ridge A - Room a-04", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-04"], [door for _, door in all_doors.items() if door.room_name == "4a_a-04"]),
- "4a_a-05": Room("4a", "4a_a-05", "Golden Ridge A - Room a-05", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-05"], [door for _, door in all_doors.items() if door.room_name == "4a_a-05"]),
- "4a_a-06": Room("4a", "4a_a-06", "Golden Ridge A - Room a-06", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-06"], [door for _, door in all_doors.items() if door.room_name == "4a_a-06"]),
- "4a_a-07": Room("4a", "4a_a-07", "Golden Ridge A - Room a-07", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-07"], [door for _, door in all_doors.items() if door.room_name == "4a_a-07"]),
- "4a_a-08": Room("4a", "4a_a-08", "Golden Ridge A - Room a-08", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-08"], [door for _, door in all_doors.items() if door.room_name == "4a_a-08"]),
- "4a_a-10": Room("4a", "4a_a-10", "Golden Ridge A - Room a-10", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-10"], [door for _, door in all_doors.items() if door.room_name == "4a_a-10"]),
- "4a_a-11": Room("4a", "4a_a-11", "Golden Ridge A - Room a-11", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-11"], [door for _, door in all_doors.items() if door.room_name == "4a_a-11"]),
- "4a_a-09": Room("4a", "4a_a-09", "Golden Ridge A - Room a-09", [reg for _, reg in all_regions.items() if reg.room_name == "4a_a-09"], [door for _, door in all_doors.items() if door.room_name == "4a_a-09"]),
- "4a_b-00": Room("4a", "4a_b-00", "Golden Ridge A - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-00"], [door for _, door in all_doors.items() if door.room_name == "4a_b-00"], "Shrine", "4a_b-00_south"),
- "4a_b-01": Room("4a", "4a_b-01", "Golden Ridge A - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-01"], [door for _, door in all_doors.items() if door.room_name == "4a_b-01"]),
- "4a_b-04": Room("4a", "4a_b-04", "Golden Ridge A - Room b-04", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-04"], [door for _, door in all_doors.items() if door.room_name == "4a_b-04"]),
- "4a_b-06": Room("4a", "4a_b-06", "Golden Ridge A - Room b-06", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-06"], [door for _, door in all_doors.items() if door.room_name == "4a_b-06"]),
- "4a_b-07": Room("4a", "4a_b-07", "Golden Ridge A - Room b-07", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-07"], [door for _, door in all_doors.items() if door.room_name == "4a_b-07"]),
- "4a_b-03": Room("4a", "4a_b-03", "Golden Ridge A - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-03"], [door for _, door in all_doors.items() if door.room_name == "4a_b-03"]),
- "4a_b-02": Room("4a", "4a_b-02", "Golden Ridge A - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-02"], [door for _, door in all_doors.items() if door.room_name == "4a_b-02"]),
- "4a_b-sec": Room("4a", "4a_b-sec", "Golden Ridge A - Room b-sec", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-sec"], [door for _, door in all_doors.items() if door.room_name == "4a_b-sec"]),
- "4a_b-secb": Room("4a", "4a_b-secb", "Golden Ridge A - Room b-secb", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-secb"], [door for _, door in all_doors.items() if door.room_name == "4a_b-secb"]),
- "4a_b-05": Room("4a", "4a_b-05", "Golden Ridge A - Room b-05", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-05"], [door for _, door in all_doors.items() if door.room_name == "4a_b-05"]),
- "4a_b-08b": Room("4a", "4a_b-08b", "Golden Ridge A - Room b-08b", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-08b"], [door for _, door in all_doors.items() if door.room_name == "4a_b-08b"]),
- "4a_b-08": Room("4a", "4a_b-08", "Golden Ridge A - Room b-08", [reg for _, reg in all_regions.items() if reg.room_name == "4a_b-08"], [door for _, door in all_doors.items() if door.room_name == "4a_b-08"]),
- "4a_c-00": Room("4a", "4a_c-00", "Golden Ridge A - Room c-00", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-00"], [door for _, door in all_doors.items() if door.room_name == "4a_c-00"], "Old Trail", "4a_c-00_west"),
- "4a_c-01": Room("4a", "4a_c-01", "Golden Ridge A - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-01"], [door for _, door in all_doors.items() if door.room_name == "4a_c-01"]),
- "4a_c-02": Room("4a", "4a_c-02", "Golden Ridge A - Room c-02", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-02"], [door for _, door in all_doors.items() if door.room_name == "4a_c-02"]),
- "4a_c-04": Room("4a", "4a_c-04", "Golden Ridge A - Room c-04", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-04"], [door for _, door in all_doors.items() if door.room_name == "4a_c-04"]),
- "4a_c-05": Room("4a", "4a_c-05", "Golden Ridge A - Room c-05", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-05"], [door for _, door in all_doors.items() if door.room_name == "4a_c-05"]),
- "4a_c-06": Room("4a", "4a_c-06", "Golden Ridge A - Room c-06", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-06"], [door for _, door in all_doors.items() if door.room_name == "4a_c-06"]),
- "4a_c-06b": Room("4a", "4a_c-06b", "Golden Ridge A - Room c-06b", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-06b"], [door for _, door in all_doors.items() if door.room_name == "4a_c-06b"]),
- "4a_c-09": Room("4a", "4a_c-09", "Golden Ridge A - Room c-09", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-09"], [door for _, door in all_doors.items() if door.room_name == "4a_c-09"]),
- "4a_c-07": Room("4a", "4a_c-07", "Golden Ridge A - Room c-07", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-07"], [door for _, door in all_doors.items() if door.room_name == "4a_c-07"]),
- "4a_c-08": Room("4a", "4a_c-08", "Golden Ridge A - Room c-08", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-08"], [door for _, door in all_doors.items() if door.room_name == "4a_c-08"]),
- "4a_c-10": Room("4a", "4a_c-10", "Golden Ridge A - Room c-10", [reg for _, reg in all_regions.items() if reg.room_name == "4a_c-10"], [door for _, door in all_doors.items() if door.room_name == "4a_c-10"]),
- "4a_d-00": Room("4a", "4a_d-00", "Golden Ridge A - Room d-00", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-00"], [door for _, door in all_doors.items() if door.room_name == "4a_d-00"], "Cliff Face", "4a_d-00_west"),
- "4a_d-00b": Room("4a", "4a_d-00b", "Golden Ridge A - Room d-00b", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-00b"], [door for _, door in all_doors.items() if door.room_name == "4a_d-00b"]),
- "4a_d-01": Room("4a", "4a_d-01", "Golden Ridge A - Room d-01", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-01"], [door for _, door in all_doors.items() if door.room_name == "4a_d-01"]),
- "4a_d-02": Room("4a", "4a_d-02", "Golden Ridge A - Room d-02", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-02"], [door for _, door in all_doors.items() if door.room_name == "4a_d-02"]),
- "4a_d-03": Room("4a", "4a_d-03", "Golden Ridge A - Room d-03", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-03"], [door for _, door in all_doors.items() if door.room_name == "4a_d-03"]),
- "4a_d-04": Room("4a", "4a_d-04", "Golden Ridge A - Room d-04", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-04"], [door for _, door in all_doors.items() if door.room_name == "4a_d-04"]),
- "4a_d-05": Room("4a", "4a_d-05", "Golden Ridge A - Room d-05", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-05"], [door for _, door in all_doors.items() if door.room_name == "4a_d-05"]),
- "4a_d-06": Room("4a", "4a_d-06", "Golden Ridge A - Room d-06", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-06"], [door for _, door in all_doors.items() if door.room_name == "4a_d-06"]),
- "4a_d-07": Room("4a", "4a_d-07", "Golden Ridge A - Room d-07", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-07"], [door for _, door in all_doors.items() if door.room_name == "4a_d-07"]),
- "4a_d-08": Room("4a", "4a_d-08", "Golden Ridge A - Room d-08", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-08"], [door for _, door in all_doors.items() if door.room_name == "4a_d-08"]),
- "4a_d-09": Room("4a", "4a_d-09", "Golden Ridge A - Room d-09", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-09"], [door for _, door in all_doors.items() if door.room_name == "4a_d-09"]),
- "4a_d-10": Room("4a", "4a_d-10", "Golden Ridge A - Room d-10", [reg for _, reg in all_regions.items() if reg.room_name == "4a_d-10"], [door for _, door in all_doors.items() if door.room_name == "4a_d-10"]),
-
- "4b_a-00": Room("4b", "4b_a-00", "Golden Ridge B - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "4b_a-00"], [door for _, door in all_doors.items() if door.room_name == "4b_a-00"], "Start", "4b_a-00_west"),
- "4b_a-01": Room("4b", "4b_a-01", "Golden Ridge B - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "4b_a-01"], [door for _, door in all_doors.items() if door.room_name == "4b_a-01"]),
- "4b_a-02": Room("4b", "4b_a-02", "Golden Ridge B - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "4b_a-02"], [door for _, door in all_doors.items() if door.room_name == "4b_a-02"]),
- "4b_a-03": Room("4b", "4b_a-03", "Golden Ridge B - Room a-03", [reg for _, reg in all_regions.items() if reg.room_name == "4b_a-03"], [door for _, door in all_doors.items() if door.room_name == "4b_a-03"]),
- "4b_a-04": Room("4b", "4b_a-04", "Golden Ridge B - Room a-04", [reg for _, reg in all_regions.items() if reg.room_name == "4b_a-04"], [door for _, door in all_doors.items() if door.room_name == "4b_a-04"]),
- "4b_b-00": Room("4b", "4b_b-00", "Golden Ridge B - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "4b_b-00"], [door for _, door in all_doors.items() if door.room_name == "4b_b-00"], "Stepping Stones", "4b_b-00_west"),
- "4b_b-01": Room("4b", "4b_b-01", "Golden Ridge B - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "4b_b-01"], [door for _, door in all_doors.items() if door.room_name == "4b_b-01"]),
- "4b_b-02": Room("4b", "4b_b-02", "Golden Ridge B - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "4b_b-02"], [door for _, door in all_doors.items() if door.room_name == "4b_b-02"]),
- "4b_b-03": Room("4b", "4b_b-03", "Golden Ridge B - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "4b_b-03"], [door for _, door in all_doors.items() if door.room_name == "4b_b-03"]),
- "4b_b-04": Room("4b", "4b_b-04", "Golden Ridge B - Room b-04", [reg for _, reg in all_regions.items() if reg.room_name == "4b_b-04"], [door for _, door in all_doors.items() if door.room_name == "4b_b-04"]),
- "4b_c-00": Room("4b", "4b_c-00", "Golden Ridge B - Room c-00", [reg for _, reg in all_regions.items() if reg.room_name == "4b_c-00"], [door for _, door in all_doors.items() if door.room_name == "4b_c-00"], "Gusty Canyon", "4b_c-00_west"),
- "4b_c-01": Room("4b", "4b_c-01", "Golden Ridge B - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "4b_c-01"], [door for _, door in all_doors.items() if door.room_name == "4b_c-01"]),
- "4b_c-02": Room("4b", "4b_c-02", "Golden Ridge B - Room c-02", [reg for _, reg in all_regions.items() if reg.room_name == "4b_c-02"], [door for _, door in all_doors.items() if door.room_name == "4b_c-02"]),
- "4b_c-03": Room("4b", "4b_c-03", "Golden Ridge B - Room c-03", [reg for _, reg in all_regions.items() if reg.room_name == "4b_c-03"], [door for _, door in all_doors.items() if door.room_name == "4b_c-03"]),
- "4b_c-04": Room("4b", "4b_c-04", "Golden Ridge B - Room c-04", [reg for _, reg in all_regions.items() if reg.room_name == "4b_c-04"], [door for _, door in all_doors.items() if door.room_name == "4b_c-04"]),
- "4b_d-00": Room("4b", "4b_d-00", "Golden Ridge B - Room d-00", [reg for _, reg in all_regions.items() if reg.room_name == "4b_d-00"], [door for _, door in all_doors.items() if door.room_name == "4b_d-00"], "Eye of the Storm", "4b_d-00_west"),
- "4b_d-01": Room("4b", "4b_d-01", "Golden Ridge B - Room d-01", [reg for _, reg in all_regions.items() if reg.room_name == "4b_d-01"], [door for _, door in all_doors.items() if door.room_name == "4b_d-01"]),
- "4b_d-02": Room("4b", "4b_d-02", "Golden Ridge B - Room d-02", [reg for _, reg in all_regions.items() if reg.room_name == "4b_d-02"], [door for _, door in all_doors.items() if door.room_name == "4b_d-02"]),
- "4b_d-03": Room("4b", "4b_d-03", "Golden Ridge B - Room d-03", [reg for _, reg in all_regions.items() if reg.room_name == "4b_d-03"], [door for _, door in all_doors.items() if door.room_name == "4b_d-03"]),
- "4b_end": Room("4b", "4b_end", "Golden Ridge B - Room end", [reg for _, reg in all_regions.items() if reg.room_name == "4b_end"], [door for _, door in all_doors.items() if door.room_name == "4b_end"]),
-
- "4c_00": Room("4c", "4c_00", "Golden Ridge C - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "4c_00"], [door for _, door in all_doors.items() if door.room_name == "4c_00"], "Start", "4c_00_west"),
- "4c_01": Room("4c", "4c_01", "Golden Ridge C - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "4c_01"], [door for _, door in all_doors.items() if door.room_name == "4c_01"]),
- "4c_02": Room("4c", "4c_02", "Golden Ridge C - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "4c_02"], [door for _, door in all_doors.items() if door.room_name == "4c_02"]),
-
- "5a_a-00b": Room("5a", "5a_a-00b", "Mirror Temple A - Room a-00b", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-00b"], [door for _, door in all_doors.items() if door.room_name == "5a_a-00b"], "Start", "5a_a-00b_west"),
- "5a_a-00x": Room("5a", "5a_a-00x", "Mirror Temple A - Room a-00x", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-00x"], [door for _, door in all_doors.items() if door.room_name == "5a_a-00x"]),
- "5a_a-00d": Room("5a", "5a_a-00d", "Mirror Temple A - Room a-00d", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-00d"], [door for _, door in all_doors.items() if door.room_name == "5a_a-00d"]),
- "5a_a-00c": Room("5a", "5a_a-00c", "Mirror Temple A - Room a-00c", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-00c"], [door for _, door in all_doors.items() if door.room_name == "5a_a-00c"]),
- "5a_a-00": Room("5a", "5a_a-00", "Mirror Temple A - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-00"], [door for _, door in all_doors.items() if door.room_name == "5a_a-00"]),
- "5a_a-01": Room("5a", "5a_a-01", "Mirror Temple A - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-01"], [door for _, door in all_doors.items() if door.room_name == "5a_a-01"]),
- "5a_a-02": Room("5a", "5a_a-02", "Mirror Temple A - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-02"], [door for _, door in all_doors.items() if door.room_name == "5a_a-02"]),
- "5a_a-03": Room("5a", "5a_a-03", "Mirror Temple A - Room a-03", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-03"], [door for _, door in all_doors.items() if door.room_name == "5a_a-03"]),
- "5a_a-04": Room("5a", "5a_a-04", "Mirror Temple A - Room a-04", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-04"], [door for _, door in all_doors.items() if door.room_name == "5a_a-04"]),
- "5a_a-05": Room("5a", "5a_a-05", "Mirror Temple A - Room a-05", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-05"], [door for _, door in all_doors.items() if door.room_name == "5a_a-05"]),
- "5a_a-06": Room("5a", "5a_a-06", "Mirror Temple A - Room a-06", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-06"], [door for _, door in all_doors.items() if door.room_name == "5a_a-06"]),
- "5a_a-07": Room("5a", "5a_a-07", "Mirror Temple A - Room a-07", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-07"], [door for _, door in all_doors.items() if door.room_name == "5a_a-07"]),
- "5a_a-08": Room("5a", "5a_a-08", "Mirror Temple A - Room a-08", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-08"], [door for _, door in all_doors.items() if door.room_name == "5a_a-08"]),
- "5a_a-10": Room("5a", "5a_a-10", "Mirror Temple A - Room a-10", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-10"], [door for _, door in all_doors.items() if door.room_name == "5a_a-10"]),
- "5a_a-09": Room("5a", "5a_a-09", "Mirror Temple A - Room a-09", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-09"], [door for _, door in all_doors.items() if door.room_name == "5a_a-09"]),
- "5a_a-11": Room("5a", "5a_a-11", "Mirror Temple A - Room a-11", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-11"], [door for _, door in all_doors.items() if door.room_name == "5a_a-11"]),
- "5a_a-12": Room("5a", "5a_a-12", "Mirror Temple A - Room a-12", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-12"], [door for _, door in all_doors.items() if door.room_name == "5a_a-12"]),
- "5a_a-15": Room("5a", "5a_a-15", "Mirror Temple A - Room a-15", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-15"], [door for _, door in all_doors.items() if door.room_name == "5a_a-15"]),
- "5a_a-14": Room("5a", "5a_a-14", "Mirror Temple A - Room a-14", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-14"], [door for _, door in all_doors.items() if door.room_name == "5a_a-14"]),
- "5a_a-13": Room("5a", "5a_a-13", "Mirror Temple A - Room a-13", [reg for _, reg in all_regions.items() if reg.room_name == "5a_a-13"], [door for _, door in all_doors.items() if door.room_name == "5a_a-13"]),
- "5a_b-00": Room("5a", "5a_b-00", "Mirror Temple A - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-00"], [door for _, door in all_doors.items() if door.room_name == "5a_b-00"], "Depths", "5a_b-00_west"),
- "5a_b-18": Room("5a", "5a_b-18", "Mirror Temple A - Room b-18", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-18"], [door for _, door in all_doors.items() if door.room_name == "5a_b-18"]),
- "5a_b-01": Room("5a", "5a_b-01", "Mirror Temple A - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-01"], [door for _, door in all_doors.items() if door.room_name == "5a_b-01"]),
- "5a_b-01c": Room("5a", "5a_b-01c", "Mirror Temple A - Room b-01c", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-01c"], [door for _, door in all_doors.items() if door.room_name == "5a_b-01c"]),
- "5a_b-20": Room("5a", "5a_b-20", "Mirror Temple A - Room b-20", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-20"], [door for _, door in all_doors.items() if door.room_name == "5a_b-20"]),
- "5a_b-21": Room("5a", "5a_b-21", "Mirror Temple A - Room b-21", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-21"], [door for _, door in all_doors.items() if door.room_name == "5a_b-21"]),
- "5a_b-01b": Room("5a", "5a_b-01b", "Mirror Temple A - Room b-01b", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-01b"], [door for _, door in all_doors.items() if door.room_name == "5a_b-01b"]),
- "5a_b-02": Room("5a", "5a_b-02", "Mirror Temple A - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-02"], [door for _, door in all_doors.items() if door.room_name == "5a_b-02"]),
- "5a_b-03": Room("5a", "5a_b-03", "Mirror Temple A - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-03"], [door for _, door in all_doors.items() if door.room_name == "5a_b-03"]),
- "5a_b-05": Room("5a", "5a_b-05", "Mirror Temple A - Room b-05", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-05"], [door for _, door in all_doors.items() if door.room_name == "5a_b-05"]),
- "5a_b-04": Room("5a", "5a_b-04", "Mirror Temple A - Room b-04", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-04"], [door for _, door in all_doors.items() if door.room_name == "5a_b-04"]),
- "5a_b-07": Room("5a", "5a_b-07", "Mirror Temple A - Room b-07", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-07"], [door for _, door in all_doors.items() if door.room_name == "5a_b-07"]),
- "5a_b-08": Room("5a", "5a_b-08", "Mirror Temple A - Room b-08", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-08"], [door for _, door in all_doors.items() if door.room_name == "5a_b-08"]),
- "5a_b-09": Room("5a", "5a_b-09", "Mirror Temple A - Room b-09", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-09"], [door for _, door in all_doors.items() if door.room_name == "5a_b-09"]),
- "5a_b-10": Room("5a", "5a_b-10", "Mirror Temple A - Room b-10", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-10"], [door for _, door in all_doors.items() if door.room_name == "5a_b-10"]),
- "5a_b-11": Room("5a", "5a_b-11", "Mirror Temple A - Room b-11", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-11"], [door for _, door in all_doors.items() if door.room_name == "5a_b-11"]),
- "5a_b-12": Room("5a", "5a_b-12", "Mirror Temple A - Room b-12", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-12"], [door for _, door in all_doors.items() if door.room_name == "5a_b-12"]),
- "5a_b-13": Room("5a", "5a_b-13", "Mirror Temple A - Room b-13", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-13"], [door for _, door in all_doors.items() if door.room_name == "5a_b-13"]),
- "5a_b-17": Room("5a", "5a_b-17", "Mirror Temple A - Room b-17", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-17"], [door for _, door in all_doors.items() if door.room_name == "5a_b-17"]),
- "5a_b-22": Room("5a", "5a_b-22", "Mirror Temple A - Room b-22", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-22"], [door for _, door in all_doors.items() if door.room_name == "5a_b-22"]),
- "5a_b-06": Room("5a", "5a_b-06", "Mirror Temple A - Room b-06", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-06"], [door for _, door in all_doors.items() if door.room_name == "5a_b-06"]),
- "5a_b-19": Room("5a", "5a_b-19", "Mirror Temple A - Room b-19", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-19"], [door for _, door in all_doors.items() if door.room_name == "5a_b-19"]),
- "5a_b-14": Room("5a", "5a_b-14", "Mirror Temple A - Room b-14", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-14"], [door for _, door in all_doors.items() if door.room_name == "5a_b-14"]),
- "5a_b-15": Room("5a", "5a_b-15", "Mirror Temple A - Room b-15", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-15"], [door for _, door in all_doors.items() if door.room_name == "5a_b-15"]),
- "5a_b-16": Room("5a", "5a_b-16", "Mirror Temple A - Room b-16", [reg for _, reg in all_regions.items() if reg.room_name == "5a_b-16"], [door for _, door in all_doors.items() if door.room_name == "5a_b-16"]),
- "5a_void": Room("5a", "5a_void", "Mirror Temple A - Room void", [reg for _, reg in all_regions.items() if reg.room_name == "5a_void"], [door for _, door in all_doors.items() if door.room_name == "5a_void"]),
- "5a_c-00": Room("5a", "5a_c-00", "Mirror Temple A - Room c-00", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-00"], [door for _, door in all_doors.items() if door.room_name == "5a_c-00"], "Unravelling", "5a_c-00_top"),
- "5a_c-01": Room("5a", "5a_c-01", "Mirror Temple A - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-01"], [door for _, door in all_doors.items() if door.room_name == "5a_c-01"]),
- "5a_c-01b": Room("5a", "5a_c-01b", "Mirror Temple A - Room c-01b", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-01b"], [door for _, door in all_doors.items() if door.room_name == "5a_c-01b"]),
- "5a_c-01c": Room("5a", "5a_c-01c", "Mirror Temple A - Room c-01c", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-01c"], [door for _, door in all_doors.items() if door.room_name == "5a_c-01c"]),
- "5a_c-08b": Room("5a", "5a_c-08b", "Mirror Temple A - Room c-08b", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-08b"], [door for _, door in all_doors.items() if door.room_name == "5a_c-08b"]),
- "5a_c-08": Room("5a", "5a_c-08", "Mirror Temple A - Room c-08", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-08"], [door for _, door in all_doors.items() if door.room_name == "5a_c-08"]),
- "5a_c-10": Room("5a", "5a_c-10", "Mirror Temple A - Room c-10", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-10"], [door for _, door in all_doors.items() if door.room_name == "5a_c-10"]),
- "5a_c-12": Room("5a", "5a_c-12", "Mirror Temple A - Room c-12", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-12"], [door for _, door in all_doors.items() if door.room_name == "5a_c-12"]),
- "5a_c-07": Room("5a", "5a_c-07", "Mirror Temple A - Room c-07", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-07"], [door for _, door in all_doors.items() if door.room_name == "5a_c-07"]),
- "5a_c-11": Room("5a", "5a_c-11", "Mirror Temple A - Room c-11", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-11"], [door for _, door in all_doors.items() if door.room_name == "5a_c-11"]),
- "5a_c-09": Room("5a", "5a_c-09", "Mirror Temple A - Room c-09", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-09"], [door for _, door in all_doors.items() if door.room_name == "5a_c-09"]),
- "5a_c-13": Room("5a", "5a_c-13", "Mirror Temple A - Room c-13", [reg for _, reg in all_regions.items() if reg.room_name == "5a_c-13"], [door for _, door in all_doors.items() if door.room_name == "5a_c-13"]),
- "5a_d-00": Room("5a", "5a_d-00", "Mirror Temple A - Room d-00", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-00"], [door for _, door in all_doors.items() if door.room_name == "5a_d-00"], "Search", "5a_d-00_south"),
- "5a_d-01": Room("5a", "5a_d-01", "Mirror Temple A - Room d-01", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-01"], [door for _, door in all_doors.items() if door.room_name == "5a_d-01"]),
- "5a_d-09": Room("5a", "5a_d-09", "Mirror Temple A - Room d-09", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-09"], [door for _, door in all_doors.items() if door.room_name == "5a_d-09"]),
- "5a_d-04": Room("5a", "5a_d-04", "Mirror Temple A - Room d-04", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-04"], [door for _, door in all_doors.items() if door.room_name == "5a_d-04"]),
- "5a_d-05": Room("5a", "5a_d-05", "Mirror Temple A - Room d-05", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-05"], [door for _, door in all_doors.items() if door.room_name == "5a_d-05"]),
- "5a_d-06": Room("5a", "5a_d-06", "Mirror Temple A - Room d-06", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-06"], [door for _, door in all_doors.items() if door.room_name == "5a_d-06"]),
- "5a_d-07": Room("5a", "5a_d-07", "Mirror Temple A - Room d-07", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-07"], [door for _, door in all_doors.items() if door.room_name == "5a_d-07"]),
- "5a_d-02": Room("5a", "5a_d-02", "Mirror Temple A - Room d-02", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-02"], [door for _, door in all_doors.items() if door.room_name == "5a_d-02"]),
- "5a_d-03": Room("5a", "5a_d-03", "Mirror Temple A - Room d-03", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-03"], [door for _, door in all_doors.items() if door.room_name == "5a_d-03"]),
- "5a_d-15": Room("5a", "5a_d-15", "Mirror Temple A - Room d-15", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-15"], [door for _, door in all_doors.items() if door.room_name == "5a_d-15"]),
- "5a_d-13": Room("5a", "5a_d-13", "Mirror Temple A - Room d-13", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-13"], [door for _, door in all_doors.items() if door.room_name == "5a_d-13"]),
- "5a_d-19b": Room("5a", "5a_d-19b", "Mirror Temple A - Room d-19b", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-19b"], [door for _, door in all_doors.items() if door.room_name == "5a_d-19b"]),
- "5a_d-19": Room("5a", "5a_d-19", "Mirror Temple A - Room d-19", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-19"], [door for _, door in all_doors.items() if door.room_name == "5a_d-19"]),
- "5a_d-10": Room("5a", "5a_d-10", "Mirror Temple A - Room d-10", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-10"], [door for _, door in all_doors.items() if door.room_name == "5a_d-10"]),
- "5a_d-20": Room("5a", "5a_d-20", "Mirror Temple A - Room d-20", [reg for _, reg in all_regions.items() if reg.room_name == "5a_d-20"], [door for _, door in all_doors.items() if door.room_name == "5a_d-20"]),
- "5a_e-00": Room("5a", "5a_e-00", "Mirror Temple A - Room e-00", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-00"], [door for _, door in all_doors.items() if door.room_name == "5a_e-00"], "Rescue", "5a_e-00_west"),
- "5a_e-01": Room("5a", "5a_e-01", "Mirror Temple A - Room e-01", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-01"], [door for _, door in all_doors.items() if door.room_name == "5a_e-01"]),
- "5a_e-02": Room("5a", "5a_e-02", "Mirror Temple A - Room e-02", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-02"], [door for _, door in all_doors.items() if door.room_name == "5a_e-02"]),
- "5a_e-03": Room("5a", "5a_e-03", "Mirror Temple A - Room e-03", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-03"], [door for _, door in all_doors.items() if door.room_name == "5a_e-03"]),
- "5a_e-04": Room("5a", "5a_e-04", "Mirror Temple A - Room e-04", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-04"], [door for _, door in all_doors.items() if door.room_name == "5a_e-04"]),
- "5a_e-06": Room("5a", "5a_e-06", "Mirror Temple A - Room e-06", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-06"], [door for _, door in all_doors.items() if door.room_name == "5a_e-06"]),
- "5a_e-05": Room("5a", "5a_e-05", "Mirror Temple A - Room e-05", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-05"], [door for _, door in all_doors.items() if door.room_name == "5a_e-05"]),
- "5a_e-07": Room("5a", "5a_e-07", "Mirror Temple A - Room e-07", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-07"], [door for _, door in all_doors.items() if door.room_name == "5a_e-07"]),
- "5a_e-08": Room("5a", "5a_e-08", "Mirror Temple A - Room e-08", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-08"], [door for _, door in all_doors.items() if door.room_name == "5a_e-08"]),
- "5a_e-09": Room("5a", "5a_e-09", "Mirror Temple A - Room e-09", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-09"], [door for _, door in all_doors.items() if door.room_name == "5a_e-09"]),
- "5a_e-10": Room("5a", "5a_e-10", "Mirror Temple A - Room e-10", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-10"], [door for _, door in all_doors.items() if door.room_name == "5a_e-10"]),
- "5a_e-11": Room("5a", "5a_e-11", "Mirror Temple A - Room e-11", [reg for _, reg in all_regions.items() if reg.room_name == "5a_e-11"], [door for _, door in all_doors.items() if door.room_name == "5a_e-11"]),
-
- "5b_start": Room("5b", "5b_start", "Mirror Temple B - Room start", [reg for _, reg in all_regions.items() if reg.room_name == "5b_start"], [door for _, door in all_doors.items() if door.room_name == "5b_start"], "Start", "5b_start_west"),
- "5b_a-00": Room("5b", "5b_a-00", "Mirror Temple B - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "5b_a-00"], [door for _, door in all_doors.items() if door.room_name == "5b_a-00"]),
- "5b_a-01": Room("5b", "5b_a-01", "Mirror Temple B - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "5b_a-01"], [door for _, door in all_doors.items() if door.room_name == "5b_a-01"]),
- "5b_a-02": Room("5b", "5b_a-02", "Mirror Temple B - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "5b_a-02"], [door for _, door in all_doors.items() if door.room_name == "5b_a-02"]),
- "5b_b-00": Room("5b", "5b_b-00", "Mirror Temple B - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-00"], [door for _, door in all_doors.items() if door.room_name == "5b_b-00"], "Central Chamber", "5b_b-00_south"),
- "5b_b-01": Room("5b", "5b_b-01", "Mirror Temple B - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-01"], [door for _, door in all_doors.items() if door.room_name == "5b_b-01"]),
- "5b_b-04": Room("5b", "5b_b-04", "Mirror Temple B - Room b-04", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-04"], [door for _, door in all_doors.items() if door.room_name == "5b_b-04"]),
- "5b_b-02": Room("5b", "5b_b-02", "Mirror Temple B - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-02"], [door for _, door in all_doors.items() if door.room_name == "5b_b-02"]),
- "5b_b-05": Room("5b", "5b_b-05", "Mirror Temple B - Room b-05", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-05"], [door for _, door in all_doors.items() if door.room_name == "5b_b-05"]),
- "5b_b-06": Room("5b", "5b_b-06", "Mirror Temple B - Room b-06", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-06"], [door for _, door in all_doors.items() if door.room_name == "5b_b-06"]),
- "5b_b-07": Room("5b", "5b_b-07", "Mirror Temple B - Room b-07", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-07"], [door for _, door in all_doors.items() if door.room_name == "5b_b-07"]),
- "5b_b-03": Room("5b", "5b_b-03", "Mirror Temple B - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-03"], [door for _, door in all_doors.items() if door.room_name == "5b_b-03"]),
- "5b_b-08": Room("5b", "5b_b-08", "Mirror Temple B - Room b-08", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-08"], [door for _, door in all_doors.items() if door.room_name == "5b_b-08"]),
- "5b_b-09": Room("5b", "5b_b-09", "Mirror Temple B - Room b-09", [reg for _, reg in all_regions.items() if reg.room_name == "5b_b-09"], [door for _, door in all_doors.items() if door.room_name == "5b_b-09"]),
- "5b_c-00": Room("5b", "5b_c-00", "Mirror Temple B - Room c-00", [reg for _, reg in all_regions.items() if reg.room_name == "5b_c-00"], [door for _, door in all_doors.items() if door.room_name == "5b_c-00"], "Through the Mirror", "5b_c-00_mirror"),
- "5b_c-01": Room("5b", "5b_c-01", "Mirror Temple B - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "5b_c-01"], [door for _, door in all_doors.items() if door.room_name == "5b_c-01"]),
- "5b_c-02": Room("5b", "5b_c-02", "Mirror Temple B - Room c-02", [reg for _, reg in all_regions.items() if reg.room_name == "5b_c-02"], [door for _, door in all_doors.items() if door.room_name == "5b_c-02"]),
- "5b_c-03": Room("5b", "5b_c-03", "Mirror Temple B - Room c-03", [reg for _, reg in all_regions.items() if reg.room_name == "5b_c-03"], [door for _, door in all_doors.items() if door.room_name == "5b_c-03"]),
- "5b_c-04": Room("5b", "5b_c-04", "Mirror Temple B - Room c-04", [reg for _, reg in all_regions.items() if reg.room_name == "5b_c-04"], [door for _, door in all_doors.items() if door.room_name == "5b_c-04"]),
- "5b_d-00": Room("5b", "5b_d-00", "Mirror Temple B - Room d-00", [reg for _, reg in all_regions.items() if reg.room_name == "5b_d-00"], [door for _, door in all_doors.items() if door.room_name == "5b_d-00"], "Mix Master", "5b_d-00_west"),
- "5b_d-01": Room("5b", "5b_d-01", "Mirror Temple B - Room d-01", [reg for _, reg in all_regions.items() if reg.room_name == "5b_d-01"], [door for _, door in all_doors.items() if door.room_name == "5b_d-01"]),
- "5b_d-02": Room("5b", "5b_d-02", "Mirror Temple B - Room d-02", [reg for _, reg in all_regions.items() if reg.room_name == "5b_d-02"], [door for _, door in all_doors.items() if door.room_name == "5b_d-02"]),
- "5b_d-03": Room("5b", "5b_d-03", "Mirror Temple B - Room d-03", [reg for _, reg in all_regions.items() if reg.room_name == "5b_d-03"], [door for _, door in all_doors.items() if door.room_name == "5b_d-03"]),
- "5b_d-04": Room("5b", "5b_d-04", "Mirror Temple B - Room d-04", [reg for _, reg in all_regions.items() if reg.room_name == "5b_d-04"], [door for _, door in all_doors.items() if door.room_name == "5b_d-04"]),
- "5b_d-05": Room("5b", "5b_d-05", "Mirror Temple B - Room d-05", [reg for _, reg in all_regions.items() if reg.room_name == "5b_d-05"], [door for _, door in all_doors.items() if door.room_name == "5b_d-05"]),
-
- "5c_00": Room("5c", "5c_00", "Mirror Temple C - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "5c_00"], [door for _, door in all_doors.items() if door.room_name == "5c_00"], "Start", "5c_00_west"),
- "5c_01": Room("5c", "5c_01", "Mirror Temple C - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "5c_01"], [door for _, door in all_doors.items() if door.room_name == "5c_01"]),
- "5c_02": Room("5c", "5c_02", "Mirror Temple C - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "5c_02"], [door for _, door in all_doors.items() if door.room_name == "5c_02"]),
-
- "6a_00": Room("6a", "6a_00", "Reflection A - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "6a_00"], [door for _, door in all_doors.items() if door.room_name == "6a_00"], "Start", "6a_00_east"),
- "6a_01": Room("6a", "6a_01", "Reflection A - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "6a_01"], [door for _, door in all_doors.items() if door.room_name == "6a_01"]),
- "6a_02": Room("6a", "6a_02", "Reflection A - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "6a_02"], [door for _, door in all_doors.items() if door.room_name == "6a_02"]),
- "6a_03": Room("6a", "6a_03", "Reflection A - Room 03", [reg for _, reg in all_regions.items() if reg.room_name == "6a_03"], [door for _, door in all_doors.items() if door.room_name == "6a_03"]),
- "6a_02b": Room("6a", "6a_02b", "Reflection A - Room 02b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_02b"], [door for _, door in all_doors.items() if door.room_name == "6a_02b"]),
- "6a_04": Room("6a", "6a_04", "Reflection A - Room 04", [reg for _, reg in all_regions.items() if reg.room_name == "6a_04"], [door for _, door in all_doors.items() if door.room_name == "6a_04"], "Hollows", "6a_04_south"),
- "6a_04b": Room("6a", "6a_04b", "Reflection A - Room 04b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_04b"], [door for _, door in all_doors.items() if door.room_name == "6a_04b"]),
- "6a_04c": Room("6a", "6a_04c", "Reflection A - Room 04c", [reg for _, reg in all_regions.items() if reg.room_name == "6a_04c"], [door for _, door in all_doors.items() if door.room_name == "6a_04c"]),
- "6a_04d": Room("6a", "6a_04d", "Reflection A - Room 04d", [reg for _, reg in all_regions.items() if reg.room_name == "6a_04d"], [door for _, door in all_doors.items() if door.room_name == "6a_04d"]),
- "6a_04e": Room("6a", "6a_04e", "Reflection A - Room 04e", [reg for _, reg in all_regions.items() if reg.room_name == "6a_04e"], [door for _, door in all_doors.items() if door.room_name == "6a_04e"]),
- "6a_05": Room("6a", "6a_05", "Reflection A - Room 05", [reg for _, reg in all_regions.items() if reg.room_name == "6a_05"], [door for _, door in all_doors.items() if door.room_name == "6a_05"]),
- "6a_06": Room("6a", "6a_06", "Reflection A - Room 06", [reg for _, reg in all_regions.items() if reg.room_name == "6a_06"], [door for _, door in all_doors.items() if door.room_name == "6a_06"]),
- "6a_07": Room("6a", "6a_07", "Reflection A - Room 07", [reg for _, reg in all_regions.items() if reg.room_name == "6a_07"], [door for _, door in all_doors.items() if door.room_name == "6a_07"]),
- "6a_08a": Room("6a", "6a_08a", "Reflection A - Room 08a", [reg for _, reg in all_regions.items() if reg.room_name == "6a_08a"], [door for _, door in all_doors.items() if door.room_name == "6a_08a"]),
- "6a_08b": Room("6a", "6a_08b", "Reflection A - Room 08b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_08b"], [door for _, door in all_doors.items() if door.room_name == "6a_08b"]),
- "6a_09": Room("6a", "6a_09", "Reflection A - Room 09", [reg for _, reg in all_regions.items() if reg.room_name == "6a_09"], [door for _, door in all_doors.items() if door.room_name == "6a_09"]),
- "6a_10a": Room("6a", "6a_10a", "Reflection A - Room 10a", [reg for _, reg in all_regions.items() if reg.room_name == "6a_10a"], [door for _, door in all_doors.items() if door.room_name == "6a_10a"]),
- "6a_10b": Room("6a", "6a_10b", "Reflection A - Room 10b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_10b"], [door for _, door in all_doors.items() if door.room_name == "6a_10b"]),
- "6a_11": Room("6a", "6a_11", "Reflection A - Room 11", [reg for _, reg in all_regions.items() if reg.room_name == "6a_11"], [door for _, door in all_doors.items() if door.room_name == "6a_11"]),
- "6a_12a": Room("6a", "6a_12a", "Reflection A - Room 12a", [reg for _, reg in all_regions.items() if reg.room_name == "6a_12a"], [door for _, door in all_doors.items() if door.room_name == "6a_12a"]),
- "6a_12b": Room("6a", "6a_12b", "Reflection A - Room 12b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_12b"], [door for _, door in all_doors.items() if door.room_name == "6a_12b"]),
- "6a_13": Room("6a", "6a_13", "Reflection A - Room 13", [reg for _, reg in all_regions.items() if reg.room_name == "6a_13"], [door for _, door in all_doors.items() if door.room_name == "6a_13"]),
- "6a_14a": Room("6a", "6a_14a", "Reflection A - Room 14a", [reg for _, reg in all_regions.items() if reg.room_name == "6a_14a"], [door for _, door in all_doors.items() if door.room_name == "6a_14a"]),
- "6a_14b": Room("6a", "6a_14b", "Reflection A - Room 14b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_14b"], [door for _, door in all_doors.items() if door.room_name == "6a_14b"]),
- "6a_15": Room("6a", "6a_15", "Reflection A - Room 15", [reg for _, reg in all_regions.items() if reg.room_name == "6a_15"], [door for _, door in all_doors.items() if door.room_name == "6a_15"]),
- "6a_16a": Room("6a", "6a_16a", "Reflection A - Room 16a", [reg for _, reg in all_regions.items() if reg.room_name == "6a_16a"], [door for _, door in all_doors.items() if door.room_name == "6a_16a"]),
- "6a_16b": Room("6a", "6a_16b", "Reflection A - Room 16b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_16b"], [door for _, door in all_doors.items() if door.room_name == "6a_16b"]),
- "6a_17": Room("6a", "6a_17", "Reflection A - Room 17", [reg for _, reg in all_regions.items() if reg.room_name == "6a_17"], [door for _, door in all_doors.items() if door.room_name == "6a_17"]),
- "6a_18a": Room("6a", "6a_18a", "Reflection A - Room 18a", [reg for _, reg in all_regions.items() if reg.room_name == "6a_18a"], [door for _, door in all_doors.items() if door.room_name == "6a_18a"]),
- "6a_18b": Room("6a", "6a_18b", "Reflection A - Room 18b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_18b"], [door for _, door in all_doors.items() if door.room_name == "6a_18b"]),
- "6a_19": Room("6a", "6a_19", "Reflection A - Room 19", [reg for _, reg in all_regions.items() if reg.room_name == "6a_19"], [door for _, door in all_doors.items() if door.room_name == "6a_19"]),
- "6a_20": Room("6a", "6a_20", "Reflection A - Room 20", [reg for _, reg in all_regions.items() if reg.room_name == "6a_20"], [door for _, door in all_doors.items() if door.room_name == "6a_20"]),
- "6a_b-00": Room("6a", "6a_b-00", "Reflection A - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "6a_b-00"], [door for _, door in all_doors.items() if door.room_name == "6a_b-00"], "Reflection", "6a_b-00_west"),
- "6a_b-00b": Room("6a", "6a_b-00b", "Reflection A - Room b-00b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_b-00b"], [door for _, door in all_doors.items() if door.room_name == "6a_b-00b"]),
- "6a_b-00c": Room("6a", "6a_b-00c", "Reflection A - Room b-00c", [reg for _, reg in all_regions.items() if reg.room_name == "6a_b-00c"], [door for _, door in all_doors.items() if door.room_name == "6a_b-00c"]),
- "6a_b-01": Room("6a", "6a_b-01", "Reflection A - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "6a_b-01"], [door for _, door in all_doors.items() if door.room_name == "6a_b-01"]),
- "6a_b-02": Room("6a", "6a_b-02", "Reflection A - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "6a_b-02"], [door for _, door in all_doors.items() if door.room_name == "6a_b-02"]),
- "6a_b-02b": Room("6a", "6a_b-02b", "Reflection A - Room b-02b", [reg for _, reg in all_regions.items() if reg.room_name == "6a_b-02b"], [door for _, door in all_doors.items() if door.room_name == "6a_b-02b"]),
- "6a_b-03": Room("6a", "6a_b-03", "Reflection A - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "6a_b-03"], [door for _, door in all_doors.items() if door.room_name == "6a_b-03"]),
- "6a_boss-00": Room("6a", "6a_boss-00", "Reflection A - Room boss-00", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-00"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-00"], "Rock Bottom", "6a_boss-00_west"),
- "6a_boss-01": Room("6a", "6a_boss-01", "Reflection A - Room boss-01", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-01"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-01"]),
- "6a_boss-02": Room("6a", "6a_boss-02", "Reflection A - Room boss-02", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-02"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-02"]),
- "6a_boss-03": Room("6a", "6a_boss-03", "Reflection A - Room boss-03", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-03"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-03"]),
- "6a_boss-04": Room("6a", "6a_boss-04", "Reflection A - Room boss-04", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-04"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-04"]),
- "6a_boss-05": Room("6a", "6a_boss-05", "Reflection A - Room boss-05", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-05"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-05"]),
- "6a_boss-06": Room("6a", "6a_boss-06", "Reflection A - Room boss-06", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-06"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-06"]),
- "6a_boss-07": Room("6a", "6a_boss-07", "Reflection A - Room boss-07", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-07"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-07"]),
- "6a_boss-08": Room("6a", "6a_boss-08", "Reflection A - Room boss-08", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-08"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-08"]),
- "6a_boss-09": Room("6a", "6a_boss-09", "Reflection A - Room boss-09", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-09"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-09"]),
- "6a_boss-10": Room("6a", "6a_boss-10", "Reflection A - Room boss-10", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-10"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-10"]),
- "6a_boss-11": Room("6a", "6a_boss-11", "Reflection A - Room boss-11", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-11"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-11"]),
- "6a_boss-12": Room("6a", "6a_boss-12", "Reflection A - Room boss-12", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-12"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-12"]),
- "6a_boss-13": Room("6a", "6a_boss-13", "Reflection A - Room boss-13", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-13"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-13"]),
- "6a_boss-14": Room("6a", "6a_boss-14", "Reflection A - Room boss-14", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-14"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-14"]),
- "6a_boss-15": Room("6a", "6a_boss-15", "Reflection A - Room boss-15", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-15"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-15"]),
- "6a_boss-16": Room("6a", "6a_boss-16", "Reflection A - Room boss-16", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-16"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-16"]),
- "6a_boss-17": Room("6a", "6a_boss-17", "Reflection A - Room boss-17", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-17"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-17"]),
- "6a_boss-18": Room("6a", "6a_boss-18", "Reflection A - Room boss-18", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-18"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-18"]),
- "6a_boss-19": Room("6a", "6a_boss-19", "Reflection A - Room boss-19", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-19"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-19"]),
- "6a_boss-20": Room("6a", "6a_boss-20", "Reflection A - Room boss-20", [reg for _, reg in all_regions.items() if reg.room_name == "6a_boss-20"], [door for _, door in all_doors.items() if door.room_name == "6a_boss-20"]),
- "6a_after-00": Room("6a", "6a_after-00", "Reflection A - Room after-00", [reg for _, reg in all_regions.items() if reg.room_name == "6a_after-00"], [door for _, door in all_doors.items() if door.room_name == "6a_after-00"], "Resolution", "6a_after-00_bottom"),
- "6a_after-01": Room("6a", "6a_after-01", "Reflection A - Room after-01", [reg for _, reg in all_regions.items() if reg.room_name == "6a_after-01"], [door for _, door in all_doors.items() if door.room_name == "6a_after-01"]),
-
- "6b_a-00": Room("6b", "6b_a-00", "Reflection B - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "6b_a-00"], [door for _, door in all_doors.items() if door.room_name == "6b_a-00"], "Start", "6b_a-00_bottom"),
- "6b_a-01": Room("6b", "6b_a-01", "Reflection B - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "6b_a-01"], [door for _, door in all_doors.items() if door.room_name == "6b_a-01"]),
- "6b_a-02": Room("6b", "6b_a-02", "Reflection B - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "6b_a-02"], [door for _, door in all_doors.items() if door.room_name == "6b_a-02"]),
- "6b_a-03": Room("6b", "6b_a-03", "Reflection B - Room a-03", [reg for _, reg in all_regions.items() if reg.room_name == "6b_a-03"], [door for _, door in all_doors.items() if door.room_name == "6b_a-03"]),
- "6b_a-04": Room("6b", "6b_a-04", "Reflection B - Room a-04", [reg for _, reg in all_regions.items() if reg.room_name == "6b_a-04"], [door for _, door in all_doors.items() if door.room_name == "6b_a-04"]),
- "6b_a-05": Room("6b", "6b_a-05", "Reflection B - Room a-05", [reg for _, reg in all_regions.items() if reg.room_name == "6b_a-05"], [door for _, door in all_doors.items() if door.room_name == "6b_a-05"]),
- "6b_a-06": Room("6b", "6b_a-06", "Reflection B - Room a-06", [reg for _, reg in all_regions.items() if reg.room_name == "6b_a-06"], [door for _, door in all_doors.items() if door.room_name == "6b_a-06"]),
- "6b_b-00": Room("6b", "6b_b-00", "Reflection B - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-00"], [door for _, door in all_doors.items() if door.room_name == "6b_b-00"], "Reflection", "6b_b-00_west"),
- "6b_b-01": Room("6b", "6b_b-01", "Reflection B - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-01"], [door for _, door in all_doors.items() if door.room_name == "6b_b-01"]),
- "6b_b-02": Room("6b", "6b_b-02", "Reflection B - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-02"], [door for _, door in all_doors.items() if door.room_name == "6b_b-02"]),
- "6b_b-03": Room("6b", "6b_b-03", "Reflection B - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-03"], [door for _, door in all_doors.items() if door.room_name == "6b_b-03"]),
- "6b_b-04": Room("6b", "6b_b-04", "Reflection B - Room b-04", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-04"], [door for _, door in all_doors.items() if door.room_name == "6b_b-04"]),
- "6b_b-05": Room("6b", "6b_b-05", "Reflection B - Room b-05", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-05"], [door for _, door in all_doors.items() if door.room_name == "6b_b-05"]),
- "6b_b-06": Room("6b", "6b_b-06", "Reflection B - Room b-06", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-06"], [door for _, door in all_doors.items() if door.room_name == "6b_b-06"]),
- "6b_b-07": Room("6b", "6b_b-07", "Reflection B - Room b-07", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-07"], [door for _, door in all_doors.items() if door.room_name == "6b_b-07"]),
- "6b_b-08": Room("6b", "6b_b-08", "Reflection B - Room b-08", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-08"], [door for _, door in all_doors.items() if door.room_name == "6b_b-08"]),
- "6b_b-10": Room("6b", "6b_b-10", "Reflection B - Room b-10", [reg for _, reg in all_regions.items() if reg.room_name == "6b_b-10"], [door for _, door in all_doors.items() if door.room_name == "6b_b-10"]),
- "6b_c-00": Room("6b", "6b_c-00", "Reflection B - Room c-00", [reg for _, reg in all_regions.items() if reg.room_name == "6b_c-00"], [door for _, door in all_doors.items() if door.room_name == "6b_c-00"], "Rock Bottom", "6b_c-00_west"),
- "6b_c-01": Room("6b", "6b_c-01", "Reflection B - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "6b_c-01"], [door for _, door in all_doors.items() if door.room_name == "6b_c-01"]),
- "6b_c-02": Room("6b", "6b_c-02", "Reflection B - Room c-02", [reg for _, reg in all_regions.items() if reg.room_name == "6b_c-02"], [door for _, door in all_doors.items() if door.room_name == "6b_c-02"]),
- "6b_c-03": Room("6b", "6b_c-03", "Reflection B - Room c-03", [reg for _, reg in all_regions.items() if reg.room_name == "6b_c-03"], [door for _, door in all_doors.items() if door.room_name == "6b_c-03"]),
- "6b_c-04": Room("6b", "6b_c-04", "Reflection B - Room c-04", [reg for _, reg in all_regions.items() if reg.room_name == "6b_c-04"], [door for _, door in all_doors.items() if door.room_name == "6b_c-04"]),
- "6b_d-00": Room("6b", "6b_d-00", "Reflection B - Room d-00", [reg for _, reg in all_regions.items() if reg.room_name == "6b_d-00"], [door for _, door in all_doors.items() if door.room_name == "6b_d-00"], "Reprieve", "6b_d-00_west"),
- "6b_d-01": Room("6b", "6b_d-01", "Reflection B - Room d-01", [reg for _, reg in all_regions.items() if reg.room_name == "6b_d-01"], [door for _, door in all_doors.items() if door.room_name == "6b_d-01"]),
- "6b_d-02": Room("6b", "6b_d-02", "Reflection B - Room d-02", [reg for _, reg in all_regions.items() if reg.room_name == "6b_d-02"], [door for _, door in all_doors.items() if door.room_name == "6b_d-02"]),
- "6b_d-03": Room("6b", "6b_d-03", "Reflection B - Room d-03", [reg for _, reg in all_regions.items() if reg.room_name == "6b_d-03"], [door for _, door in all_doors.items() if door.room_name == "6b_d-03"]),
- "6b_d-04": Room("6b", "6b_d-04", "Reflection B - Room d-04", [reg for _, reg in all_regions.items() if reg.room_name == "6b_d-04"], [door for _, door in all_doors.items() if door.room_name == "6b_d-04"]),
- "6b_d-05": Room("6b", "6b_d-05", "Reflection B - Room d-05", [reg for _, reg in all_regions.items() if reg.room_name == "6b_d-05"], [door for _, door in all_doors.items() if door.room_name == "6b_d-05"]),
-
- "6c_00": Room("6c", "6c_00", "Reflection C - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "6c_00"], [door for _, door in all_doors.items() if door.room_name == "6c_00"], "Start", "6c_00_west"),
- "6c_01": Room("6c", "6c_01", "Reflection C - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "6c_01"], [door for _, door in all_doors.items() if door.room_name == "6c_01"]),
- "6c_02": Room("6c", "6c_02", "Reflection C - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "6c_02"], [door for _, door in all_doors.items() if door.room_name == "6c_02"]),
-
- "7a_a-00": Room("7a", "7a_a-00", "The Summit A - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "7a_a-00"], [door for _, door in all_doors.items() if door.room_name == "7a_a-00"], "Start", "7a_a-00_west"),
- "7a_a-01": Room("7a", "7a_a-01", "The Summit A - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "7a_a-01"], [door for _, door in all_doors.items() if door.room_name == "7a_a-01"]),
- "7a_a-02": Room("7a", "7a_a-02", "The Summit A - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "7a_a-02"], [door for _, door in all_doors.items() if door.room_name == "7a_a-02"]),
- "7a_a-02b": Room("7a", "7a_a-02b", "The Summit A - Room a-02b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_a-02b"], [door for _, door in all_doors.items() if door.room_name == "7a_a-02b"]),
- "7a_a-03": Room("7a", "7a_a-03", "The Summit A - Room a-03", [reg for _, reg in all_regions.items() if reg.room_name == "7a_a-03"], [door for _, door in all_doors.items() if door.room_name == "7a_a-03"]),
- "7a_a-04": Room("7a", "7a_a-04", "The Summit A - Room a-04", [reg for _, reg in all_regions.items() if reg.room_name == "7a_a-04"], [door for _, door in all_doors.items() if door.room_name == "7a_a-04"]),
- "7a_a-04b": Room("7a", "7a_a-04b", "The Summit A - Room a-04b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_a-04b"], [door for _, door in all_doors.items() if door.room_name == "7a_a-04b"]),
- "7a_a-05": Room("7a", "7a_a-05", "The Summit A - Room a-05", [reg for _, reg in all_regions.items() if reg.room_name == "7a_a-05"], [door for _, door in all_doors.items() if door.room_name == "7a_a-05"]),
- "7a_a-06": Room("7a", "7a_a-06", "The Summit A - Room a-06", [reg for _, reg in all_regions.items() if reg.room_name == "7a_a-06"], [door for _, door in all_doors.items() if door.room_name == "7a_a-06"]),
- "7a_b-00": Room("7a", "7a_b-00", "The Summit A - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-00"], [door for _, door in all_doors.items() if door.room_name == "7a_b-00"], "500 M", "7a_b-00_bottom"),
- "7a_b-01": Room("7a", "7a_b-01", "The Summit A - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-01"], [door for _, door in all_doors.items() if door.room_name == "7a_b-01"]),
- "7a_b-02": Room("7a", "7a_b-02", "The Summit A - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-02"], [door for _, door in all_doors.items() if door.room_name == "7a_b-02"]),
- "7a_b-02b": Room("7a", "7a_b-02b", "The Summit A - Room b-02b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-02b"], [door for _, door in all_doors.items() if door.room_name == "7a_b-02b"]),
- "7a_b-02e": Room("7a", "7a_b-02e", "The Summit A - Room b-02e", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-02e"], [door for _, door in all_doors.items() if door.room_name == "7a_b-02e"]),
- "7a_b-02c": Room("7a", "7a_b-02c", "The Summit A - Room b-02c", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-02c"], [door for _, door in all_doors.items() if door.room_name == "7a_b-02c"]),
- "7a_b-02d": Room("7a", "7a_b-02d", "The Summit A - Room b-02d", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-02d"], [door for _, door in all_doors.items() if door.room_name == "7a_b-02d"]),
- "7a_b-03": Room("7a", "7a_b-03", "The Summit A - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-03"], [door for _, door in all_doors.items() if door.room_name == "7a_b-03"]),
- "7a_b-04": Room("7a", "7a_b-04", "The Summit A - Room b-04", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-04"], [door for _, door in all_doors.items() if door.room_name == "7a_b-04"]),
- "7a_b-05": Room("7a", "7a_b-05", "The Summit A - Room b-05", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-05"], [door for _, door in all_doors.items() if door.room_name == "7a_b-05"]),
- "7a_b-06": Room("7a", "7a_b-06", "The Summit A - Room b-06", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-06"], [door for _, door in all_doors.items() if door.room_name == "7a_b-06"]),
- "7a_b-07": Room("7a", "7a_b-07", "The Summit A - Room b-07", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-07"], [door for _, door in all_doors.items() if door.room_name == "7a_b-07"]),
- "7a_b-08": Room("7a", "7a_b-08", "The Summit A - Room b-08", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-08"], [door for _, door in all_doors.items() if door.room_name == "7a_b-08"]),
- "7a_b-09": Room("7a", "7a_b-09", "The Summit A - Room b-09", [reg for _, reg in all_regions.items() if reg.room_name == "7a_b-09"], [door for _, door in all_doors.items() if door.room_name == "7a_b-09"]),
- "7a_c-00": Room("7a", "7a_c-00", "The Summit A - Room c-00", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-00"], [door for _, door in all_doors.items() if door.room_name == "7a_c-00"], "1000 M", "7a_c-00_west"),
- "7a_c-01": Room("7a", "7a_c-01", "The Summit A - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-01"], [door for _, door in all_doors.items() if door.room_name == "7a_c-01"]),
- "7a_c-02": Room("7a", "7a_c-02", "The Summit A - Room c-02", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-02"], [door for _, door in all_doors.items() if door.room_name == "7a_c-02"]),
- "7a_c-03": Room("7a", "7a_c-03", "The Summit A - Room c-03", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-03"], [door for _, door in all_doors.items() if door.room_name == "7a_c-03"]),
- "7a_c-03b": Room("7a", "7a_c-03b", "The Summit A - Room c-03b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-03b"], [door for _, door in all_doors.items() if door.room_name == "7a_c-03b"]),
- "7a_c-04": Room("7a", "7a_c-04", "The Summit A - Room c-04", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-04"], [door for _, door in all_doors.items() if door.room_name == "7a_c-04"]),
- "7a_c-05": Room("7a", "7a_c-05", "The Summit A - Room c-05", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-05"], [door for _, door in all_doors.items() if door.room_name == "7a_c-05"]),
- "7a_c-06": Room("7a", "7a_c-06", "The Summit A - Room c-06", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-06"], [door for _, door in all_doors.items() if door.room_name == "7a_c-06"]),
- "7a_c-06b": Room("7a", "7a_c-06b", "The Summit A - Room c-06b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-06b"], [door for _, door in all_doors.items() if door.room_name == "7a_c-06b"]),
- "7a_c-06c": Room("7a", "7a_c-06c", "The Summit A - Room c-06c", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-06c"], [door for _, door in all_doors.items() if door.room_name == "7a_c-06c"]),
- "7a_c-07": Room("7a", "7a_c-07", "The Summit A - Room c-07", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-07"], [door for _, door in all_doors.items() if door.room_name == "7a_c-07"]),
- "7a_c-07b": Room("7a", "7a_c-07b", "The Summit A - Room c-07b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-07b"], [door for _, door in all_doors.items() if door.room_name == "7a_c-07b"]),
- "7a_c-08": Room("7a", "7a_c-08", "The Summit A - Room c-08", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-08"], [door for _, door in all_doors.items() if door.room_name == "7a_c-08"]),
- "7a_c-09": Room("7a", "7a_c-09", "The Summit A - Room c-09", [reg for _, reg in all_regions.items() if reg.room_name == "7a_c-09"], [door for _, door in all_doors.items() if door.room_name == "7a_c-09"]),
- "7a_d-00": Room("7a", "7a_d-00", "The Summit A - Room d-00", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-00"], [door for _, door in all_doors.items() if door.room_name == "7a_d-00"], "1500 M", "7a_d-00_bottom"),
- "7a_d-01": Room("7a", "7a_d-01", "The Summit A - Room d-01", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-01"], [door for _, door in all_doors.items() if door.room_name == "7a_d-01"]),
- "7a_d-01b": Room("7a", "7a_d-01b", "The Summit A - Room d-01b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-01b"], [door for _, door in all_doors.items() if door.room_name == "7a_d-01b"]),
- "7a_d-01c": Room("7a", "7a_d-01c", "The Summit A - Room d-01c", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-01c"], [door for _, door in all_doors.items() if door.room_name == "7a_d-01c"]),
- "7a_d-01d": Room("7a", "7a_d-01d", "The Summit A - Room d-01d", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-01d"], [door for _, door in all_doors.items() if door.room_name == "7a_d-01d"]),
- "7a_d-02": Room("7a", "7a_d-02", "The Summit A - Room d-02", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-02"], [door for _, door in all_doors.items() if door.room_name == "7a_d-02"]),
- "7a_d-03": Room("7a", "7a_d-03", "The Summit A - Room d-03", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-03"], [door for _, door in all_doors.items() if door.room_name == "7a_d-03"]),
- "7a_d-03b": Room("7a", "7a_d-03b", "The Summit A - Room d-03b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-03b"], [door for _, door in all_doors.items() if door.room_name == "7a_d-03b"]),
- "7a_d-04": Room("7a", "7a_d-04", "The Summit A - Room d-04", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-04"], [door for _, door in all_doors.items() if door.room_name == "7a_d-04"]),
- "7a_d-05": Room("7a", "7a_d-05", "The Summit A - Room d-05", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-05"], [door for _, door in all_doors.items() if door.room_name == "7a_d-05"]),
- "7a_d-05b": Room("7a", "7a_d-05b", "The Summit A - Room d-05b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-05b"], [door for _, door in all_doors.items() if door.room_name == "7a_d-05b"]),
- "7a_d-06": Room("7a", "7a_d-06", "The Summit A - Room d-06", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-06"], [door for _, door in all_doors.items() if door.room_name == "7a_d-06"]),
- "7a_d-07": Room("7a", "7a_d-07", "The Summit A - Room d-07", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-07"], [door for _, door in all_doors.items() if door.room_name == "7a_d-07"]),
- "7a_d-08": Room("7a", "7a_d-08", "The Summit A - Room d-08", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-08"], [door for _, door in all_doors.items() if door.room_name == "7a_d-08"]),
- "7a_d-09": Room("7a", "7a_d-09", "The Summit A - Room d-09", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-09"], [door for _, door in all_doors.items() if door.room_name == "7a_d-09"]),
- "7a_d-10": Room("7a", "7a_d-10", "The Summit A - Room d-10", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-10"], [door for _, door in all_doors.items() if door.room_name == "7a_d-10"]),
- "7a_d-10b": Room("7a", "7a_d-10b", "The Summit A - Room d-10b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-10b"], [door for _, door in all_doors.items() if door.room_name == "7a_d-10b"]),
- "7a_d-11": Room("7a", "7a_d-11", "The Summit A - Room d-11", [reg for _, reg in all_regions.items() if reg.room_name == "7a_d-11"], [door for _, door in all_doors.items() if door.room_name == "7a_d-11"]),
- "7a_e-00b": Room("7a", "7a_e-00b", "The Summit A - Room e-00b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-00b"], [door for _, door in all_doors.items() if door.room_name == "7a_e-00b"], "2000 M", "7a_e-00b_bottom"),
- "7a_e-00": Room("7a", "7a_e-00", "The Summit A - Room e-00", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-00"], [door for _, door in all_doors.items() if door.room_name == "7a_e-00"]),
- "7a_e-01": Room("7a", "7a_e-01", "The Summit A - Room e-01", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-01"], [door for _, door in all_doors.items() if door.room_name == "7a_e-01"]),
- "7a_e-01b": Room("7a", "7a_e-01b", "The Summit A - Room e-01b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-01b"], [door for _, door in all_doors.items() if door.room_name == "7a_e-01b"]),
- "7a_e-01c": Room("7a", "7a_e-01c", "The Summit A - Room e-01c", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-01c"], [door for _, door in all_doors.items() if door.room_name == "7a_e-01c"]),
- "7a_e-02": Room("7a", "7a_e-02", "The Summit A - Room e-02", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-02"], [door for _, door in all_doors.items() if door.room_name == "7a_e-02"]),
- "7a_e-03": Room("7a", "7a_e-03", "The Summit A - Room e-03", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-03"], [door for _, door in all_doors.items() if door.room_name == "7a_e-03"]),
- "7a_e-04": Room("7a", "7a_e-04", "The Summit A - Room e-04", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-04"], [door for _, door in all_doors.items() if door.room_name == "7a_e-04"]),
- "7a_e-05": Room("7a", "7a_e-05", "The Summit A - Room e-05", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-05"], [door for _, door in all_doors.items() if door.room_name == "7a_e-05"]),
- "7a_e-06": Room("7a", "7a_e-06", "The Summit A - Room e-06", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-06"], [door for _, door in all_doors.items() if door.room_name == "7a_e-06"]),
- "7a_e-07": Room("7a", "7a_e-07", "The Summit A - Room e-07", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-07"], [door for _, door in all_doors.items() if door.room_name == "7a_e-07"]),
- "7a_e-08": Room("7a", "7a_e-08", "The Summit A - Room e-08", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-08"], [door for _, door in all_doors.items() if door.room_name == "7a_e-08"]),
- "7a_e-09": Room("7a", "7a_e-09", "The Summit A - Room e-09", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-09"], [door for _, door in all_doors.items() if door.room_name == "7a_e-09"]),
- "7a_e-11": Room("7a", "7a_e-11", "The Summit A - Room e-11", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-11"], [door for _, door in all_doors.items() if door.room_name == "7a_e-11"]),
- "7a_e-12": Room("7a", "7a_e-12", "The Summit A - Room e-12", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-12"], [door for _, door in all_doors.items() if door.room_name == "7a_e-12"]),
- "7a_e-10": Room("7a", "7a_e-10", "The Summit A - Room e-10", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-10"], [door for _, door in all_doors.items() if door.room_name == "7a_e-10"]),
- "7a_e-10b": Room("7a", "7a_e-10b", "The Summit A - Room e-10b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-10b"], [door for _, door in all_doors.items() if door.room_name == "7a_e-10b"]),
- "7a_e-13": Room("7a", "7a_e-13", "The Summit A - Room e-13", [reg for _, reg in all_regions.items() if reg.room_name == "7a_e-13"], [door for _, door in all_doors.items() if door.room_name == "7a_e-13"]),
- "7a_f-00": Room("7a", "7a_f-00", "The Summit A - Room f-00", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-00"], [door for _, door in all_doors.items() if door.room_name == "7a_f-00"], "2500 M", "7a_f-00_south"),
- "7a_f-01": Room("7a", "7a_f-01", "The Summit A - Room f-01", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-01"], [door for _, door in all_doors.items() if door.room_name == "7a_f-01"]),
- "7a_f-02": Room("7a", "7a_f-02", "The Summit A - Room f-02", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-02"], [door for _, door in all_doors.items() if door.room_name == "7a_f-02"]),
- "7a_f-02b": Room("7a", "7a_f-02b", "The Summit A - Room f-02b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-02b"], [door for _, door in all_doors.items() if door.room_name == "7a_f-02b"]),
- "7a_f-04": Room("7a", "7a_f-04", "The Summit A - Room f-04", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-04"], [door for _, door in all_doors.items() if door.room_name == "7a_f-04"]),
- "7a_f-03": Room("7a", "7a_f-03", "The Summit A - Room f-03", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-03"], [door for _, door in all_doors.items() if door.room_name == "7a_f-03"]),
- "7a_f-05": Room("7a", "7a_f-05", "The Summit A - Room f-05", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-05"], [door for _, door in all_doors.items() if door.room_name == "7a_f-05"]),
- "7a_f-06": Room("7a", "7a_f-06", "The Summit A - Room f-06", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-06"], [door for _, door in all_doors.items() if door.room_name == "7a_f-06"]),
- "7a_f-07": Room("7a", "7a_f-07", "The Summit A - Room f-07", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-07"], [door for _, door in all_doors.items() if door.room_name == "7a_f-07"]),
- "7a_f-08": Room("7a", "7a_f-08", "The Summit A - Room f-08", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-08"], [door for _, door in all_doors.items() if door.room_name == "7a_f-08"]),
- "7a_f-08b": Room("7a", "7a_f-08b", "The Summit A - Room f-08b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-08b"], [door for _, door in all_doors.items() if door.room_name == "7a_f-08b"]),
- "7a_f-08d": Room("7a", "7a_f-08d", "The Summit A - Room f-08d", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-08d"], [door for _, door in all_doors.items() if door.room_name == "7a_f-08d"]),
- "7a_f-08c": Room("7a", "7a_f-08c", "The Summit A - Room f-08c", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-08c"], [door for _, door in all_doors.items() if door.room_name == "7a_f-08c"]),
- "7a_f-09": Room("7a", "7a_f-09", "The Summit A - Room f-09", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-09"], [door for _, door in all_doors.items() if door.room_name == "7a_f-09"]),
- "7a_f-10": Room("7a", "7a_f-10", "The Summit A - Room f-10", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-10"], [door for _, door in all_doors.items() if door.room_name == "7a_f-10"]),
- "7a_f-10b": Room("7a", "7a_f-10b", "The Summit A - Room f-10b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-10b"], [door for _, door in all_doors.items() if door.room_name == "7a_f-10b"]),
- "7a_f-11": Room("7a", "7a_f-11", "The Summit A - Room f-11", [reg for _, reg in all_regions.items() if reg.room_name == "7a_f-11"], [door for _, door in all_doors.items() if door.room_name == "7a_f-11"]),
- "7a_g-00": Room("7a", "7a_g-00", "The Summit A - Room g-00", [reg for _, reg in all_regions.items() if reg.room_name == "7a_g-00"], [door for _, door in all_doors.items() if door.room_name == "7a_g-00"], "3000 M", "7a_g-00_bottom"),
- "7a_g-00b": Room("7a", "7a_g-00b", "The Summit A - Room g-00b", [reg for _, reg in all_regions.items() if reg.room_name == "7a_g-00b"], [door for _, door in all_doors.items() if door.room_name == "7a_g-00b"]),
- "7a_g-01": Room("7a", "7a_g-01", "The Summit A - Room g-01", [reg for _, reg in all_regions.items() if reg.room_name == "7a_g-01"], [door for _, door in all_doors.items() if door.room_name == "7a_g-01"]),
- "7a_g-02": Room("7a", "7a_g-02", "The Summit A - Room g-02", [reg for _, reg in all_regions.items() if reg.room_name == "7a_g-02"], [door for _, door in all_doors.items() if door.room_name == "7a_g-02"]),
- "7a_g-03": Room("7a", "7a_g-03", "The Summit A - Room g-03", [reg for _, reg in all_regions.items() if reg.room_name == "7a_g-03"], [door for _, door in all_doors.items() if door.room_name == "7a_g-03"]),
-
- "7b_a-00": Room("7b", "7b_a-00", "The Summit B - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "7b_a-00"], [door for _, door in all_doors.items() if door.room_name == "7b_a-00"], "Start", "7b_a-00_west"),
- "7b_a-01": Room("7b", "7b_a-01", "The Summit B - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "7b_a-01"], [door for _, door in all_doors.items() if door.room_name == "7b_a-01"]),
- "7b_a-02": Room("7b", "7b_a-02", "The Summit B - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "7b_a-02"], [door for _, door in all_doors.items() if door.room_name == "7b_a-02"]),
- "7b_a-03": Room("7b", "7b_a-03", "The Summit B - Room a-03", [reg for _, reg in all_regions.items() if reg.room_name == "7b_a-03"], [door for _, door in all_doors.items() if door.room_name == "7b_a-03"]),
- "7b_b-00": Room("7b", "7b_b-00", "The Summit B - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "7b_b-00"], [door for _, door in all_doors.items() if door.room_name == "7b_b-00"], "500 M", "7b_b-00_bottom"),
- "7b_b-01": Room("7b", "7b_b-01", "The Summit B - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "7b_b-01"], [door for _, door in all_doors.items() if door.room_name == "7b_b-01"]),
- "7b_b-02": Room("7b", "7b_b-02", "The Summit B - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "7b_b-02"], [door for _, door in all_doors.items() if door.room_name == "7b_b-02"]),
- "7b_b-03": Room("7b", "7b_b-03", "The Summit B - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "7b_b-03"], [door for _, door in all_doors.items() if door.room_name == "7b_b-03"]),
- "7b_c-01": Room("7b", "7b_c-01", "The Summit B - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "7b_c-01"], [door for _, door in all_doors.items() if door.room_name == "7b_c-01"], "1000 M", "7b_c-01_west"),
- "7b_c-00": Room("7b", "7b_c-00", "The Summit B - Room c-00", [reg for _, reg in all_regions.items() if reg.room_name == "7b_c-00"], [door for _, door in all_doors.items() if door.room_name == "7b_c-00"]),
- "7b_c-02": Room("7b", "7b_c-02", "The Summit B - Room c-02", [reg for _, reg in all_regions.items() if reg.room_name == "7b_c-02"], [door for _, door in all_doors.items() if door.room_name == "7b_c-02"]),
- "7b_c-03": Room("7b", "7b_c-03", "The Summit B - Room c-03", [reg for _, reg in all_regions.items() if reg.room_name == "7b_c-03"], [door for _, door in all_doors.items() if door.room_name == "7b_c-03"]),
- "7b_d-00": Room("7b", "7b_d-00", "The Summit B - Room d-00", [reg for _, reg in all_regions.items() if reg.room_name == "7b_d-00"], [door for _, door in all_doors.items() if door.room_name == "7b_d-00"], "1500 M", "7b_d-00_west"),
- "7b_d-01": Room("7b", "7b_d-01", "The Summit B - Room d-01", [reg for _, reg in all_regions.items() if reg.room_name == "7b_d-01"], [door for _, door in all_doors.items() if door.room_name == "7b_d-01"]),
- "7b_d-02": Room("7b", "7b_d-02", "The Summit B - Room d-02", [reg for _, reg in all_regions.items() if reg.room_name == "7b_d-02"], [door for _, door in all_doors.items() if door.room_name == "7b_d-02"]),
- "7b_d-03": Room("7b", "7b_d-03", "The Summit B - Room d-03", [reg for _, reg in all_regions.items() if reg.room_name == "7b_d-03"], [door for _, door in all_doors.items() if door.room_name == "7b_d-03"]),
- "7b_e-00": Room("7b", "7b_e-00", "The Summit B - Room e-00", [reg for _, reg in all_regions.items() if reg.room_name == "7b_e-00"], [door for _, door in all_doors.items() if door.room_name == "7b_e-00"], "2000 M", "7b_e-00_west"),
- "7b_e-01": Room("7b", "7b_e-01", "The Summit B - Room e-01", [reg for _, reg in all_regions.items() if reg.room_name == "7b_e-01"], [door for _, door in all_doors.items() if door.room_name == "7b_e-01"]),
- "7b_e-02": Room("7b", "7b_e-02", "The Summit B - Room e-02", [reg for _, reg in all_regions.items() if reg.room_name == "7b_e-02"], [door for _, door in all_doors.items() if door.room_name == "7b_e-02"]),
- "7b_e-03": Room("7b", "7b_e-03", "The Summit B - Room e-03", [reg for _, reg in all_regions.items() if reg.room_name == "7b_e-03"], [door for _, door in all_doors.items() if door.room_name == "7b_e-03"]),
- "7b_f-00": Room("7b", "7b_f-00", "The Summit B - Room f-00", [reg for _, reg in all_regions.items() if reg.room_name == "7b_f-00"], [door for _, door in all_doors.items() if door.room_name == "7b_f-00"], "2500 M", "7b_f-00_west"),
- "7b_f-01": Room("7b", "7b_f-01", "The Summit B - Room f-01", [reg for _, reg in all_regions.items() if reg.room_name == "7b_f-01"], [door for _, door in all_doors.items() if door.room_name == "7b_f-01"]),
- "7b_f-02": Room("7b", "7b_f-02", "The Summit B - Room f-02", [reg for _, reg in all_regions.items() if reg.room_name == "7b_f-02"], [door for _, door in all_doors.items() if door.room_name == "7b_f-02"]),
- "7b_f-03": Room("7b", "7b_f-03", "The Summit B - Room f-03", [reg for _, reg in all_regions.items() if reg.room_name == "7b_f-03"], [door for _, door in all_doors.items() if door.room_name == "7b_f-03"]),
- "7b_g-00": Room("7b", "7b_g-00", "The Summit B - Room g-00", [reg for _, reg in all_regions.items() if reg.room_name == "7b_g-00"], [door for _, door in all_doors.items() if door.room_name == "7b_g-00"], "3000 M", "7b_g-00_bottom"),
- "7b_g-01": Room("7b", "7b_g-01", "The Summit B - Room g-01", [reg for _, reg in all_regions.items() if reg.room_name == "7b_g-01"], [door for _, door in all_doors.items() if door.room_name == "7b_g-01"]),
- "7b_g-02": Room("7b", "7b_g-02", "The Summit B - Room g-02", [reg for _, reg in all_regions.items() if reg.room_name == "7b_g-02"], [door for _, door in all_doors.items() if door.room_name == "7b_g-02"]),
- "7b_g-03": Room("7b", "7b_g-03", "The Summit B - Room g-03", [reg for _, reg in all_regions.items() if reg.room_name == "7b_g-03"], [door for _, door in all_doors.items() if door.room_name == "7b_g-03"]),
-
- "7c_01": Room("7c", "7c_01", "The Summit C - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "7c_01"], [door for _, door in all_doors.items() if door.room_name == "7c_01"], "Start", "7c_01_west"),
- "7c_02": Room("7c", "7c_02", "The Summit C - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "7c_02"], [door for _, door in all_doors.items() if door.room_name == "7c_02"]),
- "7c_03": Room("7c", "7c_03", "The Summit C - Room 03", [reg for _, reg in all_regions.items() if reg.room_name == "7c_03"], [door for _, door in all_doors.items() if door.room_name == "7c_03"]),
-
- "8a_outside": Room("8a", "8a_outside", "Epilogue - Room outside", [reg for _, reg in all_regions.items() if reg.room_name == "8a_outside"], [door for _, door in all_doors.items() if door.room_name == "8a_outside"], "Start", "8a_outside_east"),
- "8a_bridge": Room("8a", "8a_bridge", "Epilogue - Room bridge", [reg for _, reg in all_regions.items() if reg.room_name == "8a_bridge"], [door for _, door in all_doors.items() if door.room_name == "8a_bridge"]),
- "8a_secret": Room("8a", "8a_secret", "Epilogue - Room secret", [reg for _, reg in all_regions.items() if reg.room_name == "8a_secret"], [door for _, door in all_doors.items() if door.room_name == "8a_secret"]),
-
- "9a_00": Room("9a", "9a_00", "Core A - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "9a_00"], [door for _, door in all_doors.items() if door.room_name == "9a_00"], "Start", "9a_00_west"),
- "9a_0x": Room("9a", "9a_0x", "Core A - Room 0x", [reg for _, reg in all_regions.items() if reg.room_name == "9a_0x"], [door for _, door in all_doors.items() if door.room_name == "9a_0x"]),
- "9a_01": Room("9a", "9a_01", "Core A - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "9a_01"], [door for _, door in all_doors.items() if door.room_name == "9a_01"]),
- "9a_02": Room("9a", "9a_02", "Core A - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "9a_02"], [door for _, door in all_doors.items() if door.room_name == "9a_02"]),
- "9a_a-00": Room("9a", "9a_a-00", "Core A - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "9a_a-00"], [door for _, door in all_doors.items() if door.room_name == "9a_a-00"], "Into the Core", "9a_a-00_west"),
- "9a_a-01": Room("9a", "9a_a-01", "Core A - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "9a_a-01"], [door for _, door in all_doors.items() if door.room_name == "9a_a-01"]),
- "9a_a-02": Room("9a", "9a_a-02", "Core A - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "9a_a-02"], [door for _, door in all_doors.items() if door.room_name == "9a_a-02"]),
- "9a_a-03": Room("9a", "9a_a-03", "Core A - Room a-03", [reg for _, reg in all_regions.items() if reg.room_name == "9a_a-03"], [door for _, door in all_doors.items() if door.room_name == "9a_a-03"]),
- "9a_b-00": Room("9a", "9a_b-00", "Core A - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "9a_b-00"], [door for _, door in all_doors.items() if door.room_name == "9a_b-00"]),
- "9a_b-01": Room("9a", "9a_b-01", "Core A - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "9a_b-01"], [door for _, door in all_doors.items() if door.room_name == "9a_b-01"]),
- "9a_b-02": Room("9a", "9a_b-02", "Core A - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "9a_b-02"], [door for _, door in all_doors.items() if door.room_name == "9a_b-02"]),
- "9a_b-03": Room("9a", "9a_b-03", "Core A - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "9a_b-03"], [door for _, door in all_doors.items() if door.room_name == "9a_b-03"]),
- "9a_b-04": Room("9a", "9a_b-04", "Core A - Room b-04", [reg for _, reg in all_regions.items() if reg.room_name == "9a_b-04"], [door for _, door in all_doors.items() if door.room_name == "9a_b-04"]),
- "9a_b-05": Room("9a", "9a_b-05", "Core A - Room b-05", [reg for _, reg in all_regions.items() if reg.room_name == "9a_b-05"], [door for _, door in all_doors.items() if door.room_name == "9a_b-05"]),
- "9a_b-06": Room("9a", "9a_b-06", "Core A - Room b-06", [reg for _, reg in all_regions.items() if reg.room_name == "9a_b-06"], [door for _, door in all_doors.items() if door.room_name == "9a_b-06"]),
- "9a_b-07b": Room("9a", "9a_b-07b", "Core A - Room b-07b", [reg for _, reg in all_regions.items() if reg.room_name == "9a_b-07b"], [door for _, door in all_doors.items() if door.room_name == "9a_b-07b"]),
- "9a_b-07": Room("9a", "9a_b-07", "Core A - Room b-07", [reg for _, reg in all_regions.items() if reg.room_name == "9a_b-07"], [door for _, door in all_doors.items() if door.room_name == "9a_b-07"]),
- "9a_c-00": Room("9a", "9a_c-00", "Core A - Room c-00", [reg for _, reg in all_regions.items() if reg.room_name == "9a_c-00"], [door for _, door in all_doors.items() if door.room_name == "9a_c-00"], "Hot and Cold", "9a_c-00_west"),
- "9a_c-00b": Room("9a", "9a_c-00b", "Core A - Room c-00b", [reg for _, reg in all_regions.items() if reg.room_name == "9a_c-00b"], [door for _, door in all_doors.items() if door.room_name == "9a_c-00b"]),
- "9a_c-01": Room("9a", "9a_c-01", "Core A - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "9a_c-01"], [door for _, door in all_doors.items() if door.room_name == "9a_c-01"]),
- "9a_c-02": Room("9a", "9a_c-02", "Core A - Room c-02", [reg for _, reg in all_regions.items() if reg.room_name == "9a_c-02"], [door for _, door in all_doors.items() if door.room_name == "9a_c-02"]),
- "9a_c-03": Room("9a", "9a_c-03", "Core A - Room c-03", [reg for _, reg in all_regions.items() if reg.room_name == "9a_c-03"], [door for _, door in all_doors.items() if door.room_name == "9a_c-03"]),
- "9a_c-03b": Room("9a", "9a_c-03b", "Core A - Room c-03b", [reg for _, reg in all_regions.items() if reg.room_name == "9a_c-03b"], [door for _, door in all_doors.items() if door.room_name == "9a_c-03b"]),
- "9a_c-04": Room("9a", "9a_c-04", "Core A - Room c-04", [reg for _, reg in all_regions.items() if reg.room_name == "9a_c-04"], [door for _, door in all_doors.items() if door.room_name == "9a_c-04"]),
- "9a_d-00": Room("9a", "9a_d-00", "Core A - Room d-00", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-00"], [door for _, door in all_doors.items() if door.room_name == "9a_d-00"], "Heart of the Mountain", "9a_d-00_bottom"),
- "9a_d-01": Room("9a", "9a_d-01", "Core A - Room d-01", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-01"], [door for _, door in all_doors.items() if door.room_name == "9a_d-01"]),
- "9a_d-02": Room("9a", "9a_d-02", "Core A - Room d-02", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-02"], [door for _, door in all_doors.items() if door.room_name == "9a_d-02"]),
- "9a_d-03": Room("9a", "9a_d-03", "Core A - Room d-03", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-03"], [door for _, door in all_doors.items() if door.room_name == "9a_d-03"]),
- "9a_d-04": Room("9a", "9a_d-04", "Core A - Room d-04", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-04"], [door for _, door in all_doors.items() if door.room_name == "9a_d-04"]),
- "9a_d-05": Room("9a", "9a_d-05", "Core A - Room d-05", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-05"], [door for _, door in all_doors.items() if door.room_name == "9a_d-05"]),
- "9a_d-06": Room("9a", "9a_d-06", "Core A - Room d-06", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-06"], [door for _, door in all_doors.items() if door.room_name == "9a_d-06"]),
- "9a_d-07": Room("9a", "9a_d-07", "Core A - Room d-07", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-07"], [door for _, door in all_doors.items() if door.room_name == "9a_d-07"]),
- "9a_d-08": Room("9a", "9a_d-08", "Core A - Room d-08", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-08"], [door for _, door in all_doors.items() if door.room_name == "9a_d-08"]),
- "9a_d-09": Room("9a", "9a_d-09", "Core A - Room d-09", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-09"], [door for _, door in all_doors.items() if door.room_name == "9a_d-09"]),
- "9a_d-10": Room("9a", "9a_d-10", "Core A - Room d-10", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-10"], [door for _, door in all_doors.items() if door.room_name == "9a_d-10"]),
- "9a_d-10b": Room("9a", "9a_d-10b", "Core A - Room d-10b", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-10b"], [door for _, door in all_doors.items() if door.room_name == "9a_d-10b"]),
- "9a_d-10c": Room("9a", "9a_d-10c", "Core A - Room d-10c", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-10c"], [door for _, door in all_doors.items() if door.room_name == "9a_d-10c"]),
- "9a_d-11": Room("9a", "9a_d-11", "Core A - Room d-11", [reg for _, reg in all_regions.items() if reg.room_name == "9a_d-11"], [door for _, door in all_doors.items() if door.room_name == "9a_d-11"]),
- "9a_space": Room("9a", "9a_space", "Core A - Room space", [reg for _, reg in all_regions.items() if reg.room_name == "9a_space"], [door for _, door in all_doors.items() if door.room_name == "9a_space"]),
-
- "9b_00": Room("9b", "9b_00", "Core B - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "9b_00"], [door for _, door in all_doors.items() if door.room_name == "9b_00"], "Start", "9b_00_east"),
- "9b_01": Room("9b", "9b_01", "Core B - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "9b_01"], [door for _, door in all_doors.items() if door.room_name == "9b_01"]),
- "9b_a-00": Room("9b", "9b_a-00", "Core B - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "9b_a-00"], [door for _, door in all_doors.items() if door.room_name == "9b_a-00"], "Into the Core", "9b_a-00_west"),
- "9b_a-01": Room("9b", "9b_a-01", "Core B - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "9b_a-01"], [door for _, door in all_doors.items() if door.room_name == "9b_a-01"]),
- "9b_a-02": Room("9b", "9b_a-02", "Core B - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "9b_a-02"], [door for _, door in all_doors.items() if door.room_name == "9b_a-02"]),
- "9b_a-03": Room("9b", "9b_a-03", "Core B - Room a-03", [reg for _, reg in all_regions.items() if reg.room_name == "9b_a-03"], [door for _, door in all_doors.items() if door.room_name == "9b_a-03"]),
- "9b_a-04": Room("9b", "9b_a-04", "Core B - Room a-04", [reg for _, reg in all_regions.items() if reg.room_name == "9b_a-04"], [door for _, door in all_doors.items() if door.room_name == "9b_a-04"]),
- "9b_a-05": Room("9b", "9b_a-05", "Core B - Room a-05", [reg for _, reg in all_regions.items() if reg.room_name == "9b_a-05"], [door for _, door in all_doors.items() if door.room_name == "9b_a-05"]),
- "9b_b-00": Room("9b", "9b_b-00", "Core B - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "9b_b-00"], [door for _, door in all_doors.items() if door.room_name == "9b_b-00"], "Burning or Freezing", "9b_b-00_west"),
- "9b_b-01": Room("9b", "9b_b-01", "Core B - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "9b_b-01"], [door for _, door in all_doors.items() if door.room_name == "9b_b-01"]),
- "9b_b-02": Room("9b", "9b_b-02", "Core B - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "9b_b-02"], [door for _, door in all_doors.items() if door.room_name == "9b_b-02"]),
- "9b_b-03": Room("9b", "9b_b-03", "Core B - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "9b_b-03"], [door for _, door in all_doors.items() if door.room_name == "9b_b-03"]),
- "9b_b-04": Room("9b", "9b_b-04", "Core B - Room b-04", [reg for _, reg in all_regions.items() if reg.room_name == "9b_b-04"], [door for _, door in all_doors.items() if door.room_name == "9b_b-04"]),
- "9b_b-05": Room("9b", "9b_b-05", "Core B - Room b-05", [reg for _, reg in all_regions.items() if reg.room_name == "9b_b-05"], [door for _, door in all_doors.items() if door.room_name == "9b_b-05"]),
- "9b_c-01": Room("9b", "9b_c-01", "Core B - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "9b_c-01"], [door for _, door in all_doors.items() if door.room_name == "9b_c-01"], "Heartbeat", "9b_c-01_bottom"),
- "9b_c-02": Room("9b", "9b_c-02", "Core B - Room c-02", [reg for _, reg in all_regions.items() if reg.room_name == "9b_c-02"], [door for _, door in all_doors.items() if door.room_name == "9b_c-02"]),
- "9b_c-03": Room("9b", "9b_c-03", "Core B - Room c-03", [reg for _, reg in all_regions.items() if reg.room_name == "9b_c-03"], [door for _, door in all_doors.items() if door.room_name == "9b_c-03"]),
- "9b_c-04": Room("9b", "9b_c-04", "Core B - Room c-04", [reg for _, reg in all_regions.items() if reg.room_name == "9b_c-04"], [door for _, door in all_doors.items() if door.room_name == "9b_c-04"]),
- "9b_c-05": Room("9b", "9b_c-05", "Core B - Room c-05", [reg for _, reg in all_regions.items() if reg.room_name == "9b_c-05"], [door for _, door in all_doors.items() if door.room_name == "9b_c-05"]),
- "9b_c-06": Room("9b", "9b_c-06", "Core B - Room c-06", [reg for _, reg in all_regions.items() if reg.room_name == "9b_c-06"], [door for _, door in all_doors.items() if door.room_name == "9b_c-06"]),
- "9b_c-08": Room("9b", "9b_c-08", "Core B - Room c-08", [reg for _, reg in all_regions.items() if reg.room_name == "9b_c-08"], [door for _, door in all_doors.items() if door.room_name == "9b_c-08"]),
- "9b_c-07": Room("9b", "9b_c-07", "Core B - Room c-07", [reg for _, reg in all_regions.items() if reg.room_name == "9b_c-07"], [door for _, door in all_doors.items() if door.room_name == "9b_c-07"]),
- "9b_space": Room("9b", "9b_space", "Core B - Room space", [reg for _, reg in all_regions.items() if reg.room_name == "9b_space"], [door for _, door in all_doors.items() if door.room_name == "9b_space"]),
-
- "9c_intro": Room("9c", "9c_intro", "Core C - Room intro", [reg for _, reg in all_regions.items() if reg.room_name == "9c_intro"], [door for _, door in all_doors.items() if door.room_name == "9c_intro"], "Start", "9c_intro_west"),
- "9c_00": Room("9c", "9c_00", "Core C - Room 00", [reg for _, reg in all_regions.items() if reg.room_name == "9c_00"], [door for _, door in all_doors.items() if door.room_name == "9c_00"]),
- "9c_01": Room("9c", "9c_01", "Core C - Room 01", [reg for _, reg in all_regions.items() if reg.room_name == "9c_01"], [door for _, door in all_doors.items() if door.room_name == "9c_01"]),
- "9c_02": Room("9c", "9c_02", "Core C - Room 02", [reg for _, reg in all_regions.items() if reg.room_name == "9c_02"], [door for _, door in all_doors.items() if door.room_name == "9c_02"]),
-
- "10a_intro-00-past": Room("10a", "10a_intro-00-past", "Farewell - Room intro-00-past", [reg for _, reg in all_regions.items() if reg.room_name == "10a_intro-00-past"], [door for _, door in all_doors.items() if door.room_name == "10a_intro-00-past"], "Start", "10a_intro-00-past_west"),
- "10a_intro-01-future": Room("10a", "10a_intro-01-future", "Farewell - Room intro-01-future", [reg for _, reg in all_regions.items() if reg.room_name == "10a_intro-01-future"], [door for _, door in all_doors.items() if door.room_name == "10a_intro-01-future"]),
- "10a_intro-02-launch": Room("10a", "10a_intro-02-launch", "Farewell - Room intro-02-launch", [reg for _, reg in all_regions.items() if reg.room_name == "10a_intro-02-launch"], [door for _, door in all_doors.items() if door.room_name == "10a_intro-02-launch"]),
- "10a_intro-03-space": Room("10a", "10a_intro-03-space", "Farewell - Room intro-03-space", [reg for _, reg in all_regions.items() if reg.room_name == "10a_intro-03-space"], [door for _, door in all_doors.items() if door.room_name == "10a_intro-03-space"]),
- "10a_a-00": Room("10a", "10a_a-00", "Farewell - Room a-00", [reg for _, reg in all_regions.items() if reg.room_name == "10a_a-00"], [door for _, door in all_doors.items() if door.room_name == "10a_a-00"], "Singular", "10a_a-00_west"),
- "10a_a-01": Room("10a", "10a_a-01", "Farewell - Room a-01", [reg for _, reg in all_regions.items() if reg.room_name == "10a_a-01"], [door for _, door in all_doors.items() if door.room_name == "10a_a-01"]),
- "10a_a-02": Room("10a", "10a_a-02", "Farewell - Room a-02", [reg for _, reg in all_regions.items() if reg.room_name == "10a_a-02"], [door for _, door in all_doors.items() if door.room_name == "10a_a-02"]),
- "10a_a-03": Room("10a", "10a_a-03", "Farewell - Room a-03", [reg for _, reg in all_regions.items() if reg.room_name == "10a_a-03"], [door for _, door in all_doors.items() if door.room_name == "10a_a-03"]),
- "10a_a-04": Room("10a", "10a_a-04", "Farewell - Room a-04", [reg for _, reg in all_regions.items() if reg.room_name == "10a_a-04"], [door for _, door in all_doors.items() if door.room_name == "10a_a-04"]),
- "10a_a-05": Room("10a", "10a_a-05", "Farewell - Room a-05", [reg for _, reg in all_regions.items() if reg.room_name == "10a_a-05"], [door for _, door in all_doors.items() if door.room_name == "10a_a-05"]),
- "10a_b-00": Room("10a", "10a_b-00", "Farewell - Room b-00", [reg for _, reg in all_regions.items() if reg.room_name == "10a_b-00"], [door for _, door in all_doors.items() if door.room_name == "10a_b-00"]),
- "10a_b-01": Room("10a", "10a_b-01", "Farewell - Room b-01", [reg for _, reg in all_regions.items() if reg.room_name == "10a_b-01"], [door for _, door in all_doors.items() if door.room_name == "10a_b-01"]),
- "10a_b-02": Room("10a", "10a_b-02", "Farewell - Room b-02", [reg for _, reg in all_regions.items() if reg.room_name == "10a_b-02"], [door for _, door in all_doors.items() if door.room_name == "10a_b-02"]),
- "10a_b-03": Room("10a", "10a_b-03", "Farewell - Room b-03", [reg for _, reg in all_regions.items() if reg.room_name == "10a_b-03"], [door for _, door in all_doors.items() if door.room_name == "10a_b-03"]),
- "10a_b-04": Room("10a", "10a_b-04", "Farewell - Room b-04", [reg for _, reg in all_regions.items() if reg.room_name == "10a_b-04"], [door for _, door in all_doors.items() if door.room_name == "10a_b-04"]),
- "10a_b-05": Room("10a", "10a_b-05", "Farewell - Room b-05", [reg for _, reg in all_regions.items() if reg.room_name == "10a_b-05"], [door for _, door in all_doors.items() if door.room_name == "10a_b-05"]),
- "10a_b-06": Room("10a", "10a_b-06", "Farewell - Room b-06", [reg for _, reg in all_regions.items() if reg.room_name == "10a_b-06"], [door for _, door in all_doors.items() if door.room_name == "10a_b-06"]),
- "10a_b-07": Room("10a", "10a_b-07", "Farewell - Room b-07", [reg for _, reg in all_regions.items() if reg.room_name == "10a_b-07"], [door for _, door in all_doors.items() if door.room_name == "10a_b-07"]),
- "10a_c-00": Room("10a", "10a_c-00", "Farewell - Room c-00", [reg for _, reg in all_regions.items() if reg.room_name == "10a_c-00"], [door for _, door in all_doors.items() if door.room_name == "10a_c-00"], "Power Source", "10a_c-00_west"),
- "10a_c-00b": Room("10a", "10a_c-00b", "Farewell - Room c-00b", [reg for _, reg in all_regions.items() if reg.room_name == "10a_c-00b"], [door for _, door in all_doors.items() if door.room_name == "10a_c-00b"]),
- "10a_c-01": Room("10a", "10a_c-01", "Farewell - Room c-01", [reg for _, reg in all_regions.items() if reg.room_name == "10a_c-01"], [door for _, door in all_doors.items() if door.room_name == "10a_c-01"]),
- "10a_c-02": Room("10a", "10a_c-02", "Farewell - Room c-02", [reg for _, reg in all_regions.items() if reg.room_name == "10a_c-02"], [door for _, door in all_doors.items() if door.room_name == "10a_c-02"]),
- "10a_c-alt-00": Room("10a", "10a_c-alt-00", "Farewell - Room c-alt-00", [reg for _, reg in all_regions.items() if reg.room_name == "10a_c-alt-00"], [door for _, door in all_doors.items() if door.room_name == "10a_c-alt-00"]),
- "10a_c-alt-01": Room("10a", "10a_c-alt-01", "Farewell - Room c-alt-01", [reg for _, reg in all_regions.items() if reg.room_name == "10a_c-alt-01"], [door for _, door in all_doors.items() if door.room_name == "10a_c-alt-01"]),
- "10a_c-03": Room("10a", "10a_c-03", "Farewell - Room c-03", [reg for _, reg in all_regions.items() if reg.room_name == "10a_c-03"], [door for _, door in all_doors.items() if door.room_name == "10a_c-03"]),
- "10a_d-00": Room("10a", "10a_d-00", "Farewell - Room d-00", [reg for _, reg in all_regions.items() if reg.room_name == "10a_d-00"], [door for _, door in all_doors.items() if door.room_name == "10a_d-00"]),
- "10a_d-04": Room("10a", "10a_d-04", "Farewell - Room d-04", [reg for _, reg in all_regions.items() if reg.room_name == "10a_d-04"], [door for _, door in all_doors.items() if door.room_name == "10a_d-04"]),
- "10a_d-03": Room("10a", "10a_d-03", "Farewell - Room d-03", [reg for _, reg in all_regions.items() if reg.room_name == "10a_d-03"], [door for _, door in all_doors.items() if door.room_name == "10a_d-03"]),
- "10a_d-01": Room("10a", "10a_d-01", "Farewell - Room d-01", [reg for _, reg in all_regions.items() if reg.room_name == "10a_d-01"], [door for _, door in all_doors.items() if door.room_name == "10a_d-01"]),
- "10a_d-02": Room("10a", "10a_d-02", "Farewell - Room d-02", [reg for _, reg in all_regions.items() if reg.room_name == "10a_d-02"], [door for _, door in all_doors.items() if door.room_name == "10a_d-02"]),
- "10a_d-05": Room("10a", "10a_d-05", "Farewell - Room d-05", [reg for _, reg in all_regions.items() if reg.room_name == "10a_d-05"], [door for _, door in all_doors.items() if door.room_name == "10a_d-05"]),
- "10a_e-00y": Room("10a", "10a_e-00y", "Farewell - Room e-00y", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-00y"], [door for _, door in all_doors.items() if door.room_name == "10a_e-00y"]),
- "10a_e-00yb": Room("10a", "10a_e-00yb", "Farewell - Room e-00yb", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-00yb"], [door for _, door in all_doors.items() if door.room_name == "10a_e-00yb"]),
- "10a_e-00z": Room("10a", "10a_e-00z", "Farewell - Room e-00z", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-00z"], [door for _, door in all_doors.items() if door.room_name == "10a_e-00z"], "Remembered", "10a_e-00z_south"),
- "10a_e-00": Room("10a", "10a_e-00", "Farewell - Room e-00", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-00"], [door for _, door in all_doors.items() if door.room_name == "10a_e-00"]),
- "10a_e-00b": Room("10a", "10a_e-00b", "Farewell - Room e-00b", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-00b"], [door for _, door in all_doors.items() if door.room_name == "10a_e-00b"]),
- "10a_e-01": Room("10a", "10a_e-01", "Farewell - Room e-01", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-01"], [door for _, door in all_doors.items() if door.room_name == "10a_e-01"]),
- "10a_e-02": Room("10a", "10a_e-02", "Farewell - Room e-02", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-02"], [door for _, door in all_doors.items() if door.room_name == "10a_e-02"]),
- "10a_e-03": Room("10a", "10a_e-03", "Farewell - Room e-03", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-03"], [door for _, door in all_doors.items() if door.room_name == "10a_e-03"]),
- "10a_e-04": Room("10a", "10a_e-04", "Farewell - Room e-04", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-04"], [door for _, door in all_doors.items() if door.room_name == "10a_e-04"]),
- "10a_e-05": Room("10a", "10a_e-05", "Farewell - Room e-05", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-05"], [door for _, door in all_doors.items() if door.room_name == "10a_e-05"]),
- "10a_e-05b": Room("10a", "10a_e-05b", "Farewell - Room e-05b", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-05b"], [door for _, door in all_doors.items() if door.room_name == "10a_e-05b"]),
- "10a_e-05c": Room("10a", "10a_e-05c", "Farewell - Room e-05c", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-05c"], [door for _, door in all_doors.items() if door.room_name == "10a_e-05c"]),
- "10a_e-06": Room("10a", "10a_e-06", "Farewell - Room e-06", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-06"], [door for _, door in all_doors.items() if door.room_name == "10a_e-06"]),
- "10a_e-07": Room("10a", "10a_e-07", "Farewell - Room e-07", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-07"], [door for _, door in all_doors.items() if door.room_name == "10a_e-07"]),
- "10a_e-08": Room("10a", "10a_e-08", "Farewell - Room e-08", [reg for _, reg in all_regions.items() if reg.room_name == "10a_e-08"], [door for _, door in all_doors.items() if door.room_name == "10a_e-08"]),
-
- "10b_f-door": Room("10b", "10b_f-door", "Farewell - Room f-door", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-door"], [door for _, door in all_doors.items() if door.room_name == "10b_f-door"], "Event Horizon", "10b_f-door_west"),
- "10b_f-00": Room("10b", "10b_f-00", "Farewell - Room f-00", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-00"], [door for _, door in all_doors.items() if door.room_name == "10b_f-00"]),
- "10b_f-01": Room("10b", "10b_f-01", "Farewell - Room f-01", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-01"], [door for _, door in all_doors.items() if door.room_name == "10b_f-01"]),
- "10b_f-02": Room("10b", "10b_f-02", "Farewell - Room f-02", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-02"], [door for _, door in all_doors.items() if door.room_name == "10b_f-02"]),
- "10b_f-03": Room("10b", "10b_f-03", "Farewell - Room f-03", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-03"], [door for _, door in all_doors.items() if door.room_name == "10b_f-03"]),
- "10b_f-04": Room("10b", "10b_f-04", "Farewell - Room f-04", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-04"], [door for _, door in all_doors.items() if door.room_name == "10b_f-04"]),
- "10b_f-05": Room("10b", "10b_f-05", "Farewell - Room f-05", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-05"], [door for _, door in all_doors.items() if door.room_name == "10b_f-05"]),
- "10b_f-06": Room("10b", "10b_f-06", "Farewell - Room f-06", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-06"], [door for _, door in all_doors.items() if door.room_name == "10b_f-06"]),
- "10b_f-07": Room("10b", "10b_f-07", "Farewell - Room f-07", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-07"], [door for _, door in all_doors.items() if door.room_name == "10b_f-07"]),
- "10b_f-08": Room("10b", "10b_f-08", "Farewell - Room f-08", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-08"], [door for _, door in all_doors.items() if door.room_name == "10b_f-08"]),
- "10b_f-09": Room("10b", "10b_f-09", "Farewell - Room f-09", [reg for _, reg in all_regions.items() if reg.room_name == "10b_f-09"], [door for _, door in all_doors.items() if door.room_name == "10b_f-09"]),
- "10b_g-00": Room("10b", "10b_g-00", "Farewell - Room g-00", [reg for _, reg in all_regions.items() if reg.room_name == "10b_g-00"], [door for _, door in all_doors.items() if door.room_name == "10b_g-00"]),
- "10b_g-01": Room("10b", "10b_g-01", "Farewell - Room g-01", [reg for _, reg in all_regions.items() if reg.room_name == "10b_g-01"], [door for _, door in all_doors.items() if door.room_name == "10b_g-01"]),
- "10b_g-03": Room("10b", "10b_g-03", "Farewell - Room g-03", [reg for _, reg in all_regions.items() if reg.room_name == "10b_g-03"], [door for _, door in all_doors.items() if door.room_name == "10b_g-03"]),
- "10b_g-02": Room("10b", "10b_g-02", "Farewell - Room g-02", [reg for _, reg in all_regions.items() if reg.room_name == "10b_g-02"], [door for _, door in all_doors.items() if door.room_name == "10b_g-02"]),
- "10b_g-04": Room("10b", "10b_g-04", "Farewell - Room g-04", [reg for _, reg in all_regions.items() if reg.room_name == "10b_g-04"], [door for _, door in all_doors.items() if door.room_name == "10b_g-04"]),
- "10b_g-05": Room("10b", "10b_g-05", "Farewell - Room g-05", [reg for _, reg in all_regions.items() if reg.room_name == "10b_g-05"], [door for _, door in all_doors.items() if door.room_name == "10b_g-05"]),
- "10b_g-06": Room("10b", "10b_g-06", "Farewell - Room g-06", [reg for _, reg in all_regions.items() if reg.room_name == "10b_g-06"], [door for _, door in all_doors.items() if door.room_name == "10b_g-06"]),
- "10b_h-00b": Room("10b", "10b_h-00b", "Farewell - Room h-00b", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-00b"], [door for _, door in all_doors.items() if door.room_name == "10b_h-00b"], "Determination", "10b_h-00b_west"),
- "10b_h-00": Room("10b", "10b_h-00", "Farewell - Room h-00", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-00"], [door for _, door in all_doors.items() if door.room_name == "10b_h-00"]),
- "10b_h-01": Room("10b", "10b_h-01", "Farewell - Room h-01", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-01"], [door for _, door in all_doors.items() if door.room_name == "10b_h-01"]),
- "10b_h-02": Room("10b", "10b_h-02", "Farewell - Room h-02", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-02"], [door for _, door in all_doors.items() if door.room_name == "10b_h-02"]),
- "10b_h-03": Room("10b", "10b_h-03", "Farewell - Room h-03", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-03"], [door for _, door in all_doors.items() if door.room_name == "10b_h-03"]),
- "10b_h-03b": Room("10b", "10b_h-03b", "Farewell - Room h-03b", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-03b"], [door for _, door in all_doors.items() if door.room_name == "10b_h-03b"]),
- "10b_h-04": Room("10b", "10b_h-04", "Farewell - Room h-04", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-04"], [door for _, door in all_doors.items() if door.room_name == "10b_h-04"]),
- "10b_h-04b": Room("10b", "10b_h-04b", "Farewell - Room h-04b", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-04b"], [door for _, door in all_doors.items() if door.room_name == "10b_h-04b"]),
- "10b_h-05": Room("10b", "10b_h-05", "Farewell - Room h-05", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-05"], [door for _, door in all_doors.items() if door.room_name == "10b_h-05"]),
- "10b_h-06": Room("10b", "10b_h-06", "Farewell - Room h-06", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-06"], [door for _, door in all_doors.items() if door.room_name == "10b_h-06"]),
- "10b_h-06b": Room("10b", "10b_h-06b", "Farewell - Room h-06b", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-06b"], [door for _, door in all_doors.items() if door.room_name == "10b_h-06b"]),
- "10b_h-07": Room("10b", "10b_h-07", "Farewell - Room h-07", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-07"], [door for _, door in all_doors.items() if door.room_name == "10b_h-07"]),
- "10b_h-08": Room("10b", "10b_h-08", "Farewell - Room h-08", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-08"], [door for _, door in all_doors.items() if door.room_name == "10b_h-08"]),
- "10b_h-09": Room("10b", "10b_h-09", "Farewell - Room h-09", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-09"], [door for _, door in all_doors.items() if door.room_name == "10b_h-09"]),
- "10b_h-10": Room("10b", "10b_h-10", "Farewell - Room h-10", [reg for _, reg in all_regions.items() if reg.room_name == "10b_h-10"], [door for _, door in all_doors.items() if door.room_name == "10b_h-10"]),
- "10b_i-00": Room("10b", "10b_i-00", "Farewell - Room i-00", [reg for _, reg in all_regions.items() if reg.room_name == "10b_i-00"], [door for _, door in all_doors.items() if door.room_name == "10b_i-00"], "Stubbornness", "10b_i-00_west"),
- "10b_i-00b": Room("10b", "10b_i-00b", "Farewell - Room i-00b", [reg for _, reg in all_regions.items() if reg.room_name == "10b_i-00b"], [door for _, door in all_doors.items() if door.room_name == "10b_i-00b"]),
- "10b_i-01": Room("10b", "10b_i-01", "Farewell - Room i-01", [reg for _, reg in all_regions.items() if reg.room_name == "10b_i-01"], [door for _, door in all_doors.items() if door.room_name == "10b_i-01"]),
- "10b_i-02": Room("10b", "10b_i-02", "Farewell - Room i-02", [reg for _, reg in all_regions.items() if reg.room_name == "10b_i-02"], [door for _, door in all_doors.items() if door.room_name == "10b_i-02"]),
- "10b_i-03": Room("10b", "10b_i-03", "Farewell - Room i-03", [reg for _, reg in all_regions.items() if reg.room_name == "10b_i-03"], [door for _, door in all_doors.items() if door.room_name == "10b_i-03"]),
- "10b_i-04": Room("10b", "10b_i-04", "Farewell - Room i-04", [reg for _, reg in all_regions.items() if reg.room_name == "10b_i-04"], [door for _, door in all_doors.items() if door.room_name == "10b_i-04"]),
- "10b_i-05": Room("10b", "10b_i-05", "Farewell - Room i-05", [reg for _, reg in all_regions.items() if reg.room_name == "10b_i-05"], [door for _, door in all_doors.items() if door.room_name == "10b_i-05"]),
- "10b_j-00": Room("10b", "10b_j-00", "Farewell - Room j-00", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-00"], [door for _, door in all_doors.items() if door.room_name == "10b_j-00"], "Reconciliation", "10b_j-00_west"),
- "10b_j-00b": Room("10b", "10b_j-00b", "Farewell - Room j-00b", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-00b"], [door for _, door in all_doors.items() if door.room_name == "10b_j-00b"]),
- "10b_j-01": Room("10b", "10b_j-01", "Farewell - Room j-01", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-01"], [door for _, door in all_doors.items() if door.room_name == "10b_j-01"]),
- "10b_j-02": Room("10b", "10b_j-02", "Farewell - Room j-02", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-02"], [door for _, door in all_doors.items() if door.room_name == "10b_j-02"]),
- "10b_j-03": Room("10b", "10b_j-03", "Farewell - Room j-03", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-03"], [door for _, door in all_doors.items() if door.room_name == "10b_j-03"]),
- "10b_j-04": Room("10b", "10b_j-04", "Farewell - Room j-04", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-04"], [door for _, door in all_doors.items() if door.room_name == "10b_j-04"]),
- "10b_j-05": Room("10b", "10b_j-05", "Farewell - Room j-05", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-05"], [door for _, door in all_doors.items() if door.room_name == "10b_j-05"]),
- "10b_j-06": Room("10b", "10b_j-06", "Farewell - Room j-06", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-06"], [door for _, door in all_doors.items() if door.room_name == "10b_j-06"]),
- "10b_j-07": Room("10b", "10b_j-07", "Farewell - Room j-07", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-07"], [door for _, door in all_doors.items() if door.room_name == "10b_j-07"]),
- "10b_j-08": Room("10b", "10b_j-08", "Farewell - Room j-08", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-08"], [door for _, door in all_doors.items() if door.room_name == "10b_j-08"]),
- "10b_j-09": Room("10b", "10b_j-09", "Farewell - Room j-09", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-09"], [door for _, door in all_doors.items() if door.room_name == "10b_j-09"]),
- "10b_j-10": Room("10b", "10b_j-10", "Farewell - Room j-10", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-10"], [door for _, door in all_doors.items() if door.room_name == "10b_j-10"]),
- "10b_j-11": Room("10b", "10b_j-11", "Farewell - Room j-11", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-11"], [door for _, door in all_doors.items() if door.room_name == "10b_j-11"]),
- "10b_j-12": Room("10b", "10b_j-12", "Farewell - Room j-12", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-12"], [door for _, door in all_doors.items() if door.room_name == "10b_j-12"]),
- "10b_j-13": Room("10b", "10b_j-13", "Farewell - Room j-13", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-13"], [door for _, door in all_doors.items() if door.room_name == "10b_j-13"]),
- "10b_j-14": Room("10b", "10b_j-14", "Farewell - Room j-14", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-14"], [door for _, door in all_doors.items() if door.room_name == "10b_j-14"]),
- "10b_j-14b": Room("10b", "10b_j-14b", "Farewell - Room j-14b", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-14b"], [door for _, door in all_doors.items() if door.room_name == "10b_j-14b"]),
- "10b_j-15": Room("10b", "10b_j-15", "Farewell - Room j-15", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-15"], [door for _, door in all_doors.items() if door.room_name == "10b_j-15"]),
- "10b_j-16": Room("10b", "10b_j-16", "Farewell - Room j-16", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-16"], [door for _, door in all_doors.items() if door.room_name == "10b_j-16"], "Farewell", "10b_j-16_west"),
- "10b_j-17": Room("10b", "10b_j-17", "Farewell - Room j-17", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-17"], [door for _, door in all_doors.items() if door.room_name == "10b_j-17"]),
- "10b_j-18": Room("10b", "10b_j-18", "Farewell - Room j-18", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-18"], [door for _, door in all_doors.items() if door.room_name == "10b_j-18"]),
- "10b_j-19": Room("10b", "10b_j-19", "Farewell - Room j-19", [reg for _, reg in all_regions.items() if reg.room_name == "10b_j-19"], [door for _, door in all_doors.items() if door.room_name == "10b_j-19"]),
- "10b_GOAL": Room("10b", "10b_GOAL", "Farewell - Room GOAL", [reg for _, reg in all_regions.items() if reg.room_name == "10b_GOAL"], [door for _, door in all_doors.items() if door.room_name == "10b_GOAL"]),
-
- "10c_end-golden": Room("10c", "10c_end-golden", "Farewell - Room end-golden", [reg for _, reg in all_regions.items() if reg.room_name == "10c_end-golden"], [door for _, door in all_doors.items() if door.room_name == "10c_end-golden"]),
+ "0a_-1": Room("0a", "0a_-1", "Prologue - Room -1", regions_by_room["0a_-1"], doors_by_room["0a_-1"]),
+ "0a_0": Room("0a", "0a_0", "Prologue - Room 0", regions_by_room["0a_0"], doors_by_room["0a_0"], "Start", "0a_0_west"),
+ "0a_0b": Room("0a", "0a_0b", "Prologue - Room 0b", regions_by_room["0a_0b"], doors_by_room["0a_0b"]),
+ "0a_1": Room("0a", "0a_1", "Prologue - Room 1", regions_by_room["0a_1"], doors_by_room["0a_1"]),
+ "0a_2": Room("0a", "0a_2", "Prologue - Room 2", regions_by_room["0a_2"], doors_by_room["0a_2"]),
+ "0a_3": Room("0a", "0a_3", "Prologue - Room 3", regions_by_room["0a_3"], doors_by_room["0a_3"]),
+
+ "1a_1": Room("1a", "1a_1", "Forsaken City A - Room 1", regions_by_room["1a_1"], doors_by_room["1a_1"], "Start", "1a_1_main"),
+ "1a_2": Room("1a", "1a_2", "Forsaken City A - Room 2", regions_by_room["1a_2"], doors_by_room["1a_2"]),
+ "1a_3": Room("1a", "1a_3", "Forsaken City A - Room 3", regions_by_room["1a_3"], doors_by_room["1a_3"]),
+ "1a_4": Room("1a", "1a_4", "Forsaken City A - Room 4", regions_by_room["1a_4"], doors_by_room["1a_4"]),
+ "1a_3b": Room("1a", "1a_3b", "Forsaken City A - Room 3b", regions_by_room["1a_3b"], doors_by_room["1a_3b"]),
+ "1a_5": Room("1a", "1a_5", "Forsaken City A - Room 5", regions_by_room["1a_5"], doors_by_room["1a_5"]),
+ "1a_5z": Room("1a", "1a_5z", "Forsaken City A - Room 5z", regions_by_room["1a_5z"], doors_by_room["1a_5z"]),
+ "1a_5a": Room("1a", "1a_5a", "Forsaken City A - Room 5a", regions_by_room["1a_5a"], doors_by_room["1a_5a"]),
+ "1a_6": Room("1a", "1a_6", "Forsaken City A - Room 6", regions_by_room["1a_6"], doors_by_room["1a_6"], "Crossing", "1a_6_south-west"),
+ "1a_6z": Room("1a", "1a_6z", "Forsaken City A - Room 6z", regions_by_room["1a_6z"], doors_by_room["1a_6z"]),
+ "1a_6zb": Room("1a", "1a_6zb", "Forsaken City A - Room 6zb", regions_by_room["1a_6zb"], doors_by_room["1a_6zb"]),
+ "1a_7zb": Room("1a", "1a_7zb", "Forsaken City A - Room 7zb", regions_by_room["1a_7zb"], doors_by_room["1a_7zb"]),
+ "1a_6a": Room("1a", "1a_6a", "Forsaken City A - Room 6a", regions_by_room["1a_6a"], doors_by_room["1a_6a"]),
+ "1a_6b": Room("1a", "1a_6b", "Forsaken City A - Room 6b", regions_by_room["1a_6b"], doors_by_room["1a_6b"]),
+ "1a_s0": Room("1a", "1a_s0", "Forsaken City A - Room s0", regions_by_room["1a_s0"], doors_by_room["1a_s0"]),
+ "1a_s1": Room("1a", "1a_s1", "Forsaken City A - Room s1", regions_by_room["1a_s1"], doors_by_room["1a_s1"]),
+ "1a_6c": Room("1a", "1a_6c", "Forsaken City A - Room 6c", regions_by_room["1a_6c"], doors_by_room["1a_6c"]),
+ "1a_7": Room("1a", "1a_7", "Forsaken City A - Room 7", regions_by_room["1a_7"], doors_by_room["1a_7"]),
+ "1a_7z": Room("1a", "1a_7z", "Forsaken City A - Room 7z", regions_by_room["1a_7z"], doors_by_room["1a_7z"]),
+ "1a_8z": Room("1a", "1a_8z", "Forsaken City A - Room 8z", regions_by_room["1a_8z"], doors_by_room["1a_8z"]),
+ "1a_8zb": Room("1a", "1a_8zb", "Forsaken City A - Room 8zb", regions_by_room["1a_8zb"], doors_by_room["1a_8zb"]),
+ "1a_8": Room("1a", "1a_8", "Forsaken City A - Room 8", regions_by_room["1a_8"], doors_by_room["1a_8"]),
+ "1a_7a": Room("1a", "1a_7a", "Forsaken City A - Room 7a", regions_by_room["1a_7a"], doors_by_room["1a_7a"]),
+ "1a_9z": Room("1a", "1a_9z", "Forsaken City A - Room 9z", regions_by_room["1a_9z"], doors_by_room["1a_9z"]),
+ "1a_8b": Room("1a", "1a_8b", "Forsaken City A - Room 8b", regions_by_room["1a_8b"], doors_by_room["1a_8b"]),
+ "1a_9": Room("1a", "1a_9", "Forsaken City A - Room 9", regions_by_room["1a_9"], doors_by_room["1a_9"]),
+ "1a_9b": Room("1a", "1a_9b", "Forsaken City A - Room 9b", regions_by_room["1a_9b"], doors_by_room["1a_9b"], "Chasm", "1a_9b_west"),
+ "1a_9c": Room("1a", "1a_9c", "Forsaken City A - Room 9c", regions_by_room["1a_9c"], doors_by_room["1a_9c"]),
+ "1a_10": Room("1a", "1a_10", "Forsaken City A - Room 10", regions_by_room["1a_10"], doors_by_room["1a_10"]),
+ "1a_10z": Room("1a", "1a_10z", "Forsaken City A - Room 10z", regions_by_room["1a_10z"], doors_by_room["1a_10z"]),
+ "1a_10zb": Room("1a", "1a_10zb", "Forsaken City A - Room 10zb", regions_by_room["1a_10zb"], doors_by_room["1a_10zb"]),
+ "1a_11": Room("1a", "1a_11", "Forsaken City A - Room 11", regions_by_room["1a_11"], doors_by_room["1a_11"]),
+ "1a_11z": Room("1a", "1a_11z", "Forsaken City A - Room 11z", regions_by_room["1a_11z"], doors_by_room["1a_11z"]),
+ "1a_10a": Room("1a", "1a_10a", "Forsaken City A - Room 10a", regions_by_room["1a_10a"], doors_by_room["1a_10a"]),
+ "1a_12": Room("1a", "1a_12", "Forsaken City A - Room 12", regions_by_room["1a_12"], doors_by_room["1a_12"]),
+ "1a_12z": Room("1a", "1a_12z", "Forsaken City A - Room 12z", regions_by_room["1a_12z"], doors_by_room["1a_12z"]),
+ "1a_12a": Room("1a", "1a_12a", "Forsaken City A - Room 12a", regions_by_room["1a_12a"], doors_by_room["1a_12a"]),
+ "1a_end": Room("1a", "1a_end", "Forsaken City A - Room end", regions_by_room["1a_end"], doors_by_room["1a_end"]),
+
+ "1b_00": Room("1b", "1b_00", "Forsaken City B - Room 00", regions_by_room["1b_00"], doors_by_room["1b_00"], "Start", "1b_00_west"),
+ "1b_01": Room("1b", "1b_01", "Forsaken City B - Room 01", regions_by_room["1b_01"], doors_by_room["1b_01"]),
+ "1b_02": Room("1b", "1b_02", "Forsaken City B - Room 02", regions_by_room["1b_02"], doors_by_room["1b_02"]),
+ "1b_02b": Room("1b", "1b_02b", "Forsaken City B - Room 02b", regions_by_room["1b_02b"], doors_by_room["1b_02b"]),
+ "1b_03": Room("1b", "1b_03", "Forsaken City B - Room 03", regions_by_room["1b_03"], doors_by_room["1b_03"]),
+ "1b_04": Room("1b", "1b_04", "Forsaken City B - Room 04", regions_by_room["1b_04"], doors_by_room["1b_04"], "Contraption", "1b_04_west"),
+ "1b_05": Room("1b", "1b_05", "Forsaken City B - Room 05", regions_by_room["1b_05"], doors_by_room["1b_05"]),
+ "1b_05b": Room("1b", "1b_05b", "Forsaken City B - Room 05b", regions_by_room["1b_05b"], doors_by_room["1b_05b"]),
+ "1b_06": Room("1b", "1b_06", "Forsaken City B - Room 06", regions_by_room["1b_06"], doors_by_room["1b_06"]),
+ "1b_07": Room("1b", "1b_07", "Forsaken City B - Room 07", regions_by_room["1b_07"], doors_by_room["1b_07"]),
+ "1b_08": Room("1b", "1b_08", "Forsaken City B - Room 08", regions_by_room["1b_08"], doors_by_room["1b_08"], "Scrap Pit", "1b_08_west"),
+ "1b_08b": Room("1b", "1b_08b", "Forsaken City B - Room 08b", regions_by_room["1b_08b"], doors_by_room["1b_08b"]),
+ "1b_09": Room("1b", "1b_09", "Forsaken City B - Room 09", regions_by_room["1b_09"], doors_by_room["1b_09"]),
+ "1b_10": Room("1b", "1b_10", "Forsaken City B - Room 10", regions_by_room["1b_10"], doors_by_room["1b_10"]),
+ "1b_11": Room("1b", "1b_11", "Forsaken City B - Room 11", regions_by_room["1b_11"], doors_by_room["1b_11"]),
+ "1b_end": Room("1b", "1b_end", "Forsaken City B - Room end", regions_by_room["1b_end"], doors_by_room["1b_end"]),
+
+ "1c_00": Room("1c", "1c_00", "Forsaken City C - Room 00", regions_by_room["1c_00"], doors_by_room["1c_00"], "Start", "1c_00_west"),
+ "1c_01": Room("1c", "1c_01", "Forsaken City C - Room 01", regions_by_room["1c_01"], doors_by_room["1c_01"]),
+ "1c_02": Room("1c", "1c_02", "Forsaken City C - Room 02", regions_by_room["1c_02"], doors_by_room["1c_02"]),
+
+ "2a_start": Room("2a", "2a_start", "Old Site A - Room start", regions_by_room["2a_start"], doors_by_room["2a_start"], "Start", "2a_start_main"),
+ "2a_s0": Room("2a", "2a_s0", "Old Site A - Room s0", regions_by_room["2a_s0"], doors_by_room["2a_s0"]),
+ "2a_s1": Room("2a", "2a_s1", "Old Site A - Room s1", regions_by_room["2a_s1"], doors_by_room["2a_s1"]),
+ "2a_s2": Room("2a", "2a_s2", "Old Site A - Room s2", regions_by_room["2a_s2"], doors_by_room["2a_s2"]),
+ "2a_0": Room("2a", "2a_0", "Old Site A - Room 0", regions_by_room["2a_0"], doors_by_room["2a_0"]),
+ "2a_1": Room("2a", "2a_1", "Old Site A - Room 1", regions_by_room["2a_1"], doors_by_room["2a_1"]),
+ "2a_d0": Room("2a", "2a_d0", "Old Site A - Room d0", regions_by_room["2a_d0"], doors_by_room["2a_d0"]),
+ "2a_d7": Room("2a", "2a_d7", "Old Site A - Room d7", regions_by_room["2a_d7"], doors_by_room["2a_d7"]),
+ "2a_d8": Room("2a", "2a_d8", "Old Site A - Room d8", regions_by_room["2a_d8"], doors_by_room["2a_d8"]),
+ "2a_d3": Room("2a", "2a_d3", "Old Site A - Room d3", regions_by_room["2a_d3"], doors_by_room["2a_d3"]),
+ "2a_d2": Room("2a", "2a_d2", "Old Site A - Room d2", regions_by_room["2a_d2"], doors_by_room["2a_d2"]),
+ "2a_d9": Room("2a", "2a_d9", "Old Site A - Room d9", regions_by_room["2a_d9"], doors_by_room["2a_d9"]),
+ "2a_d1": Room("2a", "2a_d1", "Old Site A - Room d1", regions_by_room["2a_d1"], doors_by_room["2a_d1"]),
+ "2a_d6": Room("2a", "2a_d6", "Old Site A - Room d6", regions_by_room["2a_d6"], doors_by_room["2a_d6"]),
+ "2a_d4": Room("2a", "2a_d4", "Old Site A - Room d4", regions_by_room["2a_d4"], doors_by_room["2a_d4"]),
+ "2a_d5": Room("2a", "2a_d5", "Old Site A - Room d5", regions_by_room["2a_d5"], doors_by_room["2a_d5"]),
+ "2a_3x": Room("2a", "2a_3x", "Old Site A - Room 3x", regions_by_room["2a_3x"], doors_by_room["2a_3x"]),
+ "2a_3": Room("2a", "2a_3", "Old Site A - Room 3", regions_by_room["2a_3"], doors_by_room["2a_3"], "Intervention", "2a_3_bottom"),
+ "2a_4": Room("2a", "2a_4", "Old Site A - Room 4", regions_by_room["2a_4"], doors_by_room["2a_4"]),
+ "2a_5": Room("2a", "2a_5", "Old Site A - Room 5", regions_by_room["2a_5"], doors_by_room["2a_5"]),
+ "2a_6": Room("2a", "2a_6", "Old Site A - Room 6", regions_by_room["2a_6"], doors_by_room["2a_6"]),
+ "2a_7": Room("2a", "2a_7", "Old Site A - Room 7", regions_by_room["2a_7"], doors_by_room["2a_7"]),
+ "2a_8": Room("2a", "2a_8", "Old Site A - Room 8", regions_by_room["2a_8"], doors_by_room["2a_8"]),
+ "2a_9": Room("2a", "2a_9", "Old Site A - Room 9", regions_by_room["2a_9"], doors_by_room["2a_9"]),
+ "2a_9b": Room("2a", "2a_9b", "Old Site A - Room 9b", regions_by_room["2a_9b"], doors_by_room["2a_9b"]),
+ "2a_10": Room("2a", "2a_10", "Old Site A - Room 10", regions_by_room["2a_10"], doors_by_room["2a_10"]),
+ "2a_2": Room("2a", "2a_2", "Old Site A - Room 2", regions_by_room["2a_2"], doors_by_room["2a_2"]),
+ "2a_11": Room("2a", "2a_11", "Old Site A - Room 11", regions_by_room["2a_11"], doors_by_room["2a_11"]),
+ "2a_12b": Room("2a", "2a_12b", "Old Site A - Room 12b", regions_by_room["2a_12b"], doors_by_room["2a_12b"]),
+ "2a_12c": Room("2a", "2a_12c", "Old Site A - Room 12c", regions_by_room["2a_12c"], doors_by_room["2a_12c"]),
+ "2a_12d": Room("2a", "2a_12d", "Old Site A - Room 12d", regions_by_room["2a_12d"], doors_by_room["2a_12d"]),
+ "2a_12": Room("2a", "2a_12", "Old Site A - Room 12", regions_by_room["2a_12"], doors_by_room["2a_12"]),
+ "2a_13": Room("2a", "2a_13", "Old Site A - Room 13", regions_by_room["2a_13"], doors_by_room["2a_13"]),
+ "2a_end_0": Room("2a", "2a_end_0", "Old Site A - Room end_0", regions_by_room["2a_end_0"], doors_by_room["2a_end_0"]),
+ "2a_end_s0": Room("2a", "2a_end_s0", "Old Site A - Room end_s0", regions_by_room["2a_end_s0"], doors_by_room["2a_end_s0"]),
+ "2a_end_s1": Room("2a", "2a_end_s1", "Old Site A - Room end_s1", regions_by_room["2a_end_s1"], doors_by_room["2a_end_s1"]),
+ "2a_end_1": Room("2a", "2a_end_1", "Old Site A - Room end_1", regions_by_room["2a_end_1"], doors_by_room["2a_end_1"]),
+ "2a_end_2": Room("2a", "2a_end_2", "Old Site A - Room end_2", regions_by_room["2a_end_2"], doors_by_room["2a_end_2"]),
+ "2a_end_3": Room("2a", "2a_end_3", "Old Site A - Room end_3", regions_by_room["2a_end_3"], doors_by_room["2a_end_3"], "Awake", "2a_end_3_west"),
+ "2a_end_4": Room("2a", "2a_end_4", "Old Site A - Room end_4", regions_by_room["2a_end_4"], doors_by_room["2a_end_4"]),
+ "2a_end_3b": Room("2a", "2a_end_3b", "Old Site A - Room end_3b", regions_by_room["2a_end_3b"], doors_by_room["2a_end_3b"]),
+ "2a_end_3cb": Room("2a", "2a_end_3cb", "Old Site A - Room end_3cb", regions_by_room["2a_end_3cb"], doors_by_room["2a_end_3cb"]),
+ "2a_end_3c": Room("2a", "2a_end_3c", "Old Site A - Room end_3c", regions_by_room["2a_end_3c"], doors_by_room["2a_end_3c"]),
+ "2a_end_5": Room("2a", "2a_end_5", "Old Site A - Room end_5", regions_by_room["2a_end_5"], doors_by_room["2a_end_5"]),
+ "2a_end_6": Room("2a", "2a_end_6", "Old Site A - Room end_6", regions_by_room["2a_end_6"], doors_by_room["2a_end_6"]),
+
+ "2b_start": Room("2b", "2b_start", "Old Site B - Room start", regions_by_room["2b_start"], doors_by_room["2b_start"], "Start", "2b_start_west"),
+ "2b_00": Room("2b", "2b_00", "Old Site B - Room 00", regions_by_room["2b_00"], doors_by_room["2b_00"]),
+ "2b_01": Room("2b", "2b_01", "Old Site B - Room 01", regions_by_room["2b_01"], doors_by_room["2b_01"]),
+ "2b_01b": Room("2b", "2b_01b", "Old Site B - Room 01b", regions_by_room["2b_01b"], doors_by_room["2b_01b"]),
+ "2b_02b": Room("2b", "2b_02b", "Old Site B - Room 02b", regions_by_room["2b_02b"], doors_by_room["2b_02b"]),
+ "2b_02": Room("2b", "2b_02", "Old Site B - Room 02", regions_by_room["2b_02"], doors_by_room["2b_02"]),
+ "2b_03": Room("2b", "2b_03", "Old Site B - Room 03", regions_by_room["2b_03"], doors_by_room["2b_03"], "Combination Lock", "2b_03_west"),
+ "2b_04": Room("2b", "2b_04", "Old Site B - Room 04", regions_by_room["2b_04"], doors_by_room["2b_04"]),
+ "2b_05": Room("2b", "2b_05", "Old Site B - Room 05", regions_by_room["2b_05"], doors_by_room["2b_05"]),
+ "2b_06": Room("2b", "2b_06", "Old Site B - Room 06", regions_by_room["2b_06"], doors_by_room["2b_06"]),
+ "2b_07": Room("2b", "2b_07", "Old Site B - Room 07", regions_by_room["2b_07"], doors_by_room["2b_07"]),
+ "2b_08b": Room("2b", "2b_08b", "Old Site B - Room 08b", regions_by_room["2b_08b"], doors_by_room["2b_08b"], "Dream Altar", "2b_08b_west"),
+ "2b_08": Room("2b", "2b_08", "Old Site B - Room 08", regions_by_room["2b_08"], doors_by_room["2b_08"]),
+ "2b_09": Room("2b", "2b_09", "Old Site B - Room 09", regions_by_room["2b_09"], doors_by_room["2b_09"]),
+ "2b_10": Room("2b", "2b_10", "Old Site B - Room 10", regions_by_room["2b_10"], doors_by_room["2b_10"]),
+ "2b_11": Room("2b", "2b_11", "Old Site B - Room 11", regions_by_room["2b_11"], doors_by_room["2b_11"]),
+ "2b_end": Room("2b", "2b_end", "Old Site B - Room end", regions_by_room["2b_end"], doors_by_room["2b_end"]),
+
+ "2c_00": Room("2c", "2c_00", "Old Site C - Room 00", regions_by_room["2c_00"], doors_by_room["2c_00"], "Start", "2c_00_west"),
+ "2c_01": Room("2c", "2c_01", "Old Site C - Room 01", regions_by_room["2c_01"], doors_by_room["2c_01"]),
+ "2c_02": Room("2c", "2c_02", "Old Site C - Room 02", regions_by_room["2c_02"], doors_by_room["2c_02"]),
+
+ "3a_s0": Room("3a", "3a_s0", "Celestial Resort A - Room s0", regions_by_room["3a_s0"], doors_by_room["3a_s0"], "Start", "3a_s0_main"),
+ "3a_s1": Room("3a", "3a_s1", "Celestial Resort A - Room s1", regions_by_room["3a_s1"], doors_by_room["3a_s1"]),
+ "3a_s2": Room("3a", "3a_s2", "Celestial Resort A - Room s2", regions_by_room["3a_s2"], doors_by_room["3a_s2"]),
+ "3a_s3": Room("3a", "3a_s3", "Celestial Resort A - Room s3", regions_by_room["3a_s3"], doors_by_room["3a_s3"]),
+ "3a_0x-a": Room("3a", "3a_0x-a", "Celestial Resort A - Room 0x-a", regions_by_room["3a_0x-a"], doors_by_room["3a_0x-a"]),
+ "3a_00-a": Room("3a", "3a_00-a", "Celestial Resort A - Room 00-a", regions_by_room["3a_00-a"], doors_by_room["3a_00-a"]),
+ "3a_02-a": Room("3a", "3a_02-a", "Celestial Resort A - Room 02-a", regions_by_room["3a_02-a"], doors_by_room["3a_02-a"]),
+ "3a_02-b": Room("3a", "3a_02-b", "Celestial Resort A - Room 02-b", regions_by_room["3a_02-b"], doors_by_room["3a_02-b"]),
+ "3a_01-b": Room("3a", "3a_01-b", "Celestial Resort A - Room 01-b", regions_by_room["3a_01-b"], doors_by_room["3a_01-b"]),
+ "3a_00-b": Room("3a", "3a_00-b", "Celestial Resort A - Room 00-b", regions_by_room["3a_00-b"], doors_by_room["3a_00-b"]),
+ "3a_00-c": Room("3a", "3a_00-c", "Celestial Resort A - Room 00-c", regions_by_room["3a_00-c"], doors_by_room["3a_00-c"]),
+ "3a_0x-b": Room("3a", "3a_0x-b", "Celestial Resort A - Room 0x-b", regions_by_room["3a_0x-b"], doors_by_room["3a_0x-b"]),
+ "3a_03-a": Room("3a", "3a_03-a", "Celestial Resort A - Room 03-a", regions_by_room["3a_03-a"], doors_by_room["3a_03-a"]),
+ "3a_04-b": Room("3a", "3a_04-b", "Celestial Resort A - Room 04-b", regions_by_room["3a_04-b"], doors_by_room["3a_04-b"]),
+ "3a_05-a": Room("3a", "3a_05-a", "Celestial Resort A - Room 05-a", regions_by_room["3a_05-a"], doors_by_room["3a_05-a"]),
+ "3a_06-a": Room("3a", "3a_06-a", "Celestial Resort A - Room 06-a", regions_by_room["3a_06-a"], doors_by_room["3a_06-a"]),
+ "3a_07-a": Room("3a", "3a_07-a", "Celestial Resort A - Room 07-a", regions_by_room["3a_07-a"], doors_by_room["3a_07-a"]),
+ "3a_07-b": Room("3a", "3a_07-b", "Celestial Resort A - Room 07-b", regions_by_room["3a_07-b"], doors_by_room["3a_07-b"]),
+ "3a_06-b": Room("3a", "3a_06-b", "Celestial Resort A - Room 06-b", regions_by_room["3a_06-b"], doors_by_room["3a_06-b"]),
+ "3a_06-c": Room("3a", "3a_06-c", "Celestial Resort A - Room 06-c", regions_by_room["3a_06-c"], doors_by_room["3a_06-c"]),
+ "3a_05-c": Room("3a", "3a_05-c", "Celestial Resort A - Room 05-c", regions_by_room["3a_05-c"], doors_by_room["3a_05-c"]),
+ "3a_08-c": Room("3a", "3a_08-c", "Celestial Resort A - Room 08-c", regions_by_room["3a_08-c"], doors_by_room["3a_08-c"]),
+ "3a_08-b": Room("3a", "3a_08-b", "Celestial Resort A - Room 08-b", regions_by_room["3a_08-b"], doors_by_room["3a_08-b"]),
+ "3a_08-a": Room("3a", "3a_08-a", "Celestial Resort A - Room 08-a", regions_by_room["3a_08-a"], doors_by_room["3a_08-a"], "Huge Mess", "3a_08-a_west"),
+ "3a_09-b": Room("3a", "3a_09-b", "Celestial Resort A - Room 09-b", regions_by_room["3a_09-b"], doors_by_room["3a_09-b"]),
+ "3a_10-x": Room("3a", "3a_10-x", "Celestial Resort A - Room 10-x", regions_by_room["3a_10-x"], doors_by_room["3a_10-x"]),
+ "3a_11-x": Room("3a", "3a_11-x", "Celestial Resort A - Room 11-x", regions_by_room["3a_11-x"], doors_by_room["3a_11-x"]),
+ "3a_11-y": Room("3a", "3a_11-y", "Celestial Resort A - Room 11-y", regions_by_room["3a_11-y"], doors_by_room["3a_11-y"]),
+ "3a_12-y": Room("3a", "3a_12-y", "Celestial Resort A - Room 12-y", regions_by_room["3a_12-y"], doors_by_room["3a_12-y"]),
+ "3a_11-z": Room("3a", "3a_11-z", "Celestial Resort A - Room 11-z", regions_by_room["3a_11-z"], doors_by_room["3a_11-z"]),
+ "3a_10-z": Room("3a", "3a_10-z", "Celestial Resort A - Room 10-z", regions_by_room["3a_10-z"], doors_by_room["3a_10-z"]),
+ "3a_10-y": Room("3a", "3a_10-y", "Celestial Resort A - Room 10-y", regions_by_room["3a_10-y"], doors_by_room["3a_10-y"]),
+ "3a_10-c": Room("3a", "3a_10-c", "Celestial Resort A - Room 10-c", regions_by_room["3a_10-c"], doors_by_room["3a_10-c"]),
+ "3a_11-c": Room("3a", "3a_11-c", "Celestial Resort A - Room 11-c", regions_by_room["3a_11-c"], doors_by_room["3a_11-c"]),
+ "3a_12-c": Room("3a", "3a_12-c", "Celestial Resort A - Room 12-c", regions_by_room["3a_12-c"], doors_by_room["3a_12-c"]),
+ "3a_12-d": Room("3a", "3a_12-d", "Celestial Resort A - Room 12-d", regions_by_room["3a_12-d"], doors_by_room["3a_12-d"]),
+ "3a_11-d": Room("3a", "3a_11-d", "Celestial Resort A - Room 11-d", regions_by_room["3a_11-d"], doors_by_room["3a_11-d"]),
+ "3a_10-d": Room("3a", "3a_10-d", "Celestial Resort A - Room 10-d", regions_by_room["3a_10-d"], doors_by_room["3a_10-d"]),
+ "3a_11-b": Room("3a", "3a_11-b", "Celestial Resort A - Room 11-b", regions_by_room["3a_11-b"], doors_by_room["3a_11-b"]),
+ "3a_12-b": Room("3a", "3a_12-b", "Celestial Resort A - Room 12-b", regions_by_room["3a_12-b"], doors_by_room["3a_12-b"]),
+ "3a_13-b": Room("3a", "3a_13-b", "Celestial Resort A - Room 13-b", regions_by_room["3a_13-b"], doors_by_room["3a_13-b"]),
+ "3a_13-a": Room("3a", "3a_13-a", "Celestial Resort A - Room 13-a", regions_by_room["3a_13-a"], doors_by_room["3a_13-a"]),
+ "3a_13-x": Room("3a", "3a_13-x", "Celestial Resort A - Room 13-x", regions_by_room["3a_13-x"], doors_by_room["3a_13-x"]),
+ "3a_12-x": Room("3a", "3a_12-x", "Celestial Resort A - Room 12-x", regions_by_room["3a_12-x"], doors_by_room["3a_12-x"]),
+ "3a_11-a": Room("3a", "3a_11-a", "Celestial Resort A - Room 11-a", regions_by_room["3a_11-a"], doors_by_room["3a_11-a"]),
+ "3a_08-x": Room("3a", "3a_08-x", "Celestial Resort A - Room 08-x", regions_by_room["3a_08-x"], doors_by_room["3a_08-x"]),
+ "3a_09-d": Room("3a", "3a_09-d", "Celestial Resort A - Room 09-d", regions_by_room["3a_09-d"], doors_by_room["3a_09-d"], "Elevator Shaft", "3a_09-d_bottom"),
+ "3a_08-d": Room("3a", "3a_08-d", "Celestial Resort A - Room 08-d", regions_by_room["3a_08-d"], doors_by_room["3a_08-d"]),
+ "3a_06-d": Room("3a", "3a_06-d", "Celestial Resort A - Room 06-d", regions_by_room["3a_06-d"], doors_by_room["3a_06-d"]),
+ "3a_04-d": Room("3a", "3a_04-d", "Celestial Resort A - Room 04-d", regions_by_room["3a_04-d"], doors_by_room["3a_04-d"]),
+ "3a_04-c": Room("3a", "3a_04-c", "Celestial Resort A - Room 04-c", regions_by_room["3a_04-c"], doors_by_room["3a_04-c"]),
+ "3a_02-c": Room("3a", "3a_02-c", "Celestial Resort A - Room 02-c", regions_by_room["3a_02-c"], doors_by_room["3a_02-c"]),
+ "3a_03-b": Room("3a", "3a_03-b", "Celestial Resort A - Room 03-b", regions_by_room["3a_03-b"], doors_by_room["3a_03-b"]),
+ "3a_01-c": Room("3a", "3a_01-c", "Celestial Resort A - Room 01-c", regions_by_room["3a_01-c"], doors_by_room["3a_01-c"]),
+ "3a_02-d": Room("3a", "3a_02-d", "Celestial Resort A - Room 02-d", regions_by_room["3a_02-d"], doors_by_room["3a_02-d"]),
+ "3a_00-d": Room("3a", "3a_00-d", "Celestial Resort A - Room 00-d", regions_by_room["3a_00-d"], doors_by_room["3a_00-d"], "Presidential Suite", "3a_00-d_east"),
+ "3a_roof00": Room("3a", "3a_roof00", "Celestial Resort A - Room roof00", regions_by_room["3a_roof00"], doors_by_room["3a_roof00"]),
+ "3a_roof01": Room("3a", "3a_roof01", "Celestial Resort A - Room roof01", regions_by_room["3a_roof01"], doors_by_room["3a_roof01"]),
+ "3a_roof02": Room("3a", "3a_roof02", "Celestial Resort A - Room roof02", regions_by_room["3a_roof02"], doors_by_room["3a_roof02"]),
+ "3a_roof03": Room("3a", "3a_roof03", "Celestial Resort A - Room roof03", regions_by_room["3a_roof03"], doors_by_room["3a_roof03"]),
+ "3a_roof04": Room("3a", "3a_roof04", "Celestial Resort A - Room roof04", regions_by_room["3a_roof04"], doors_by_room["3a_roof04"]),
+ "3a_roof05": Room("3a", "3a_roof05", "Celestial Resort A - Room roof05", regions_by_room["3a_roof05"], doors_by_room["3a_roof05"]),
+ "3a_roof06b": Room("3a", "3a_roof06b", "Celestial Resort A - Room roof06b", regions_by_room["3a_roof06b"], doors_by_room["3a_roof06b"]),
+ "3a_roof06": Room("3a", "3a_roof06", "Celestial Resort A - Room roof06", regions_by_room["3a_roof06"], doors_by_room["3a_roof06"]),
+ "3a_roof07": Room("3a", "3a_roof07", "Celestial Resort A - Room roof07", regions_by_room["3a_roof07"], doors_by_room["3a_roof07"]),
+
+ "3b_00": Room("3b", "3b_00", "Celestial Resort B - Room 00", regions_by_room["3b_00"], doors_by_room["3b_00"], "Start", "3b_00_west"),
+ "3b_back": Room("3b", "3b_back", "Celestial Resort B - Room back", regions_by_room["3b_back"], doors_by_room["3b_back"]),
+ "3b_01": Room("3b", "3b_01", "Celestial Resort B - Room 01", regions_by_room["3b_01"], doors_by_room["3b_01"]),
+ "3b_02": Room("3b", "3b_02", "Celestial Resort B - Room 02", regions_by_room["3b_02"], doors_by_room["3b_02"]),
+ "3b_03": Room("3b", "3b_03", "Celestial Resort B - Room 03", regions_by_room["3b_03"], doors_by_room["3b_03"]),
+ "3b_04": Room("3b", "3b_04", "Celestial Resort B - Room 04", regions_by_room["3b_04"], doors_by_room["3b_04"]),
+ "3b_05": Room("3b", "3b_05", "Celestial Resort B - Room 05", regions_by_room["3b_05"], doors_by_room["3b_05"]),
+ "3b_06": Room("3b", "3b_06", "Celestial Resort B - Room 06", regions_by_room["3b_06"], doors_by_room["3b_06"], "Staff Quarters", "3b_06_west"),
+ "3b_07": Room("3b", "3b_07", "Celestial Resort B - Room 07", regions_by_room["3b_07"], doors_by_room["3b_07"]),
+ "3b_08": Room("3b", "3b_08", "Celestial Resort B - Room 08", regions_by_room["3b_08"], doors_by_room["3b_08"]),
+ "3b_09": Room("3b", "3b_09", "Celestial Resort B - Room 09", regions_by_room["3b_09"], doors_by_room["3b_09"]),
+ "3b_10": Room("3b", "3b_10", "Celestial Resort B - Room 10", regions_by_room["3b_10"], doors_by_room["3b_10"]),
+ "3b_11": Room("3b", "3b_11", "Celestial Resort B - Room 11", regions_by_room["3b_11"], doors_by_room["3b_11"], "Library", "3b_11_west"),
+ "3b_13": Room("3b", "3b_13", "Celestial Resort B - Room 13", regions_by_room["3b_13"], doors_by_room["3b_13"]),
+ "3b_14": Room("3b", "3b_14", "Celestial Resort B - Room 14", regions_by_room["3b_14"], doors_by_room["3b_14"]),
+ "3b_15": Room("3b", "3b_15", "Celestial Resort B - Room 15", regions_by_room["3b_15"], doors_by_room["3b_15"]),
+ "3b_12": Room("3b", "3b_12", "Celestial Resort B - Room 12", regions_by_room["3b_12"], doors_by_room["3b_12"]),
+ "3b_16": Room("3b", "3b_16", "Celestial Resort B - Room 16", regions_by_room["3b_16"], doors_by_room["3b_16"], "Rooftop", "3b_16_west"),
+ "3b_17": Room("3b", "3b_17", "Celestial Resort B - Room 17", regions_by_room["3b_17"], doors_by_room["3b_17"]),
+ "3b_18": Room("3b", "3b_18", "Celestial Resort B - Room 18", regions_by_room["3b_18"], doors_by_room["3b_18"]),
+ "3b_19": Room("3b", "3b_19", "Celestial Resort B - Room 19", regions_by_room["3b_19"], doors_by_room["3b_19"]),
+ "3b_21": Room("3b", "3b_21", "Celestial Resort B - Room 21", regions_by_room["3b_21"], doors_by_room["3b_21"]),
+ "3b_20": Room("3b", "3b_20", "Celestial Resort B - Room 20", regions_by_room["3b_20"], doors_by_room["3b_20"]),
+ "3b_end": Room("3b", "3b_end", "Celestial Resort B - Room end", regions_by_room["3b_end"], doors_by_room["3b_end"]),
+
+ "3c_00": Room("3c", "3c_00", "Celestial Resort C - Room 00", regions_by_room["3c_00"], doors_by_room["3c_00"], "Start", "3c_00_west"),
+ "3c_01": Room("3c", "3c_01", "Celestial Resort C - Room 01", regions_by_room["3c_01"], doors_by_room["3c_01"]),
+ "3c_02": Room("3c", "3c_02", "Celestial Resort C - Room 02", regions_by_room["3c_02"], doors_by_room["3c_02"]),
+
+ "4a_a-00": Room("4a", "4a_a-00", "Golden Ridge A - Room a-00", regions_by_room["4a_a-00"], doors_by_room["4a_a-00"], "Start", "4a_a-00_west"),
+ "4a_a-01": Room("4a", "4a_a-01", "Golden Ridge A - Room a-01", regions_by_room["4a_a-01"], doors_by_room["4a_a-01"]),
+ "4a_a-01x": Room("4a", "4a_a-01x", "Golden Ridge A - Room a-01x", regions_by_room["4a_a-01x"], doors_by_room["4a_a-01x"]),
+ "4a_a-02": Room("4a", "4a_a-02", "Golden Ridge A - Room a-02", regions_by_room["4a_a-02"], doors_by_room["4a_a-02"]),
+ "4a_a-03": Room("4a", "4a_a-03", "Golden Ridge A - Room a-03", regions_by_room["4a_a-03"], doors_by_room["4a_a-03"]),
+ "4a_a-04": Room("4a", "4a_a-04", "Golden Ridge A - Room a-04", regions_by_room["4a_a-04"], doors_by_room["4a_a-04"]),
+ "4a_a-05": Room("4a", "4a_a-05", "Golden Ridge A - Room a-05", regions_by_room["4a_a-05"], doors_by_room["4a_a-05"]),
+ "4a_a-06": Room("4a", "4a_a-06", "Golden Ridge A - Room a-06", regions_by_room["4a_a-06"], doors_by_room["4a_a-06"]),
+ "4a_a-07": Room("4a", "4a_a-07", "Golden Ridge A - Room a-07", regions_by_room["4a_a-07"], doors_by_room["4a_a-07"]),
+ "4a_a-08": Room("4a", "4a_a-08", "Golden Ridge A - Room a-08", regions_by_room["4a_a-08"], doors_by_room["4a_a-08"]),
+ "4a_a-10": Room("4a", "4a_a-10", "Golden Ridge A - Room a-10", regions_by_room["4a_a-10"], doors_by_room["4a_a-10"]),
+ "4a_a-11": Room("4a", "4a_a-11", "Golden Ridge A - Room a-11", regions_by_room["4a_a-11"], doors_by_room["4a_a-11"]),
+ "4a_a-09": Room("4a", "4a_a-09", "Golden Ridge A - Room a-09", regions_by_room["4a_a-09"], doors_by_room["4a_a-09"]),
+ "4a_b-00": Room("4a", "4a_b-00", "Golden Ridge A - Room b-00", regions_by_room["4a_b-00"], doors_by_room["4a_b-00"], "Shrine", "4a_b-00_south"),
+ "4a_b-01": Room("4a", "4a_b-01", "Golden Ridge A - Room b-01", regions_by_room["4a_b-01"], doors_by_room["4a_b-01"]),
+ "4a_b-04": Room("4a", "4a_b-04", "Golden Ridge A - Room b-04", regions_by_room["4a_b-04"], doors_by_room["4a_b-04"]),
+ "4a_b-06": Room("4a", "4a_b-06", "Golden Ridge A - Room b-06", regions_by_room["4a_b-06"], doors_by_room["4a_b-06"]),
+ "4a_b-07": Room("4a", "4a_b-07", "Golden Ridge A - Room b-07", regions_by_room["4a_b-07"], doors_by_room["4a_b-07"]),
+ "4a_b-03": Room("4a", "4a_b-03", "Golden Ridge A - Room b-03", regions_by_room["4a_b-03"], doors_by_room["4a_b-03"]),
+ "4a_b-02": Room("4a", "4a_b-02", "Golden Ridge A - Room b-02", regions_by_room["4a_b-02"], doors_by_room["4a_b-02"]),
+ "4a_b-sec": Room("4a", "4a_b-sec", "Golden Ridge A - Room b-sec", regions_by_room["4a_b-sec"], doors_by_room["4a_b-sec"]),
+ "4a_b-secb": Room("4a", "4a_b-secb", "Golden Ridge A - Room b-secb", regions_by_room["4a_b-secb"], doors_by_room["4a_b-secb"]),
+ "4a_b-05": Room("4a", "4a_b-05", "Golden Ridge A - Room b-05", regions_by_room["4a_b-05"], doors_by_room["4a_b-05"]),
+ "4a_b-08b": Room("4a", "4a_b-08b", "Golden Ridge A - Room b-08b", regions_by_room["4a_b-08b"], doors_by_room["4a_b-08b"]),
+ "4a_b-08": Room("4a", "4a_b-08", "Golden Ridge A - Room b-08", regions_by_room["4a_b-08"], doors_by_room["4a_b-08"]),
+ "4a_c-00": Room("4a", "4a_c-00", "Golden Ridge A - Room c-00", regions_by_room["4a_c-00"], doors_by_room["4a_c-00"], "Old Trail", "4a_c-00_west"),
+ "4a_c-01": Room("4a", "4a_c-01", "Golden Ridge A - Room c-01", regions_by_room["4a_c-01"], doors_by_room["4a_c-01"]),
+ "4a_c-02": Room("4a", "4a_c-02", "Golden Ridge A - Room c-02", regions_by_room["4a_c-02"], doors_by_room["4a_c-02"]),
+ "4a_c-04": Room("4a", "4a_c-04", "Golden Ridge A - Room c-04", regions_by_room["4a_c-04"], doors_by_room["4a_c-04"]),
+ "4a_c-05": Room("4a", "4a_c-05", "Golden Ridge A - Room c-05", regions_by_room["4a_c-05"], doors_by_room["4a_c-05"]),
+ "4a_c-06": Room("4a", "4a_c-06", "Golden Ridge A - Room c-06", regions_by_room["4a_c-06"], doors_by_room["4a_c-06"]),
+ "4a_c-06b": Room("4a", "4a_c-06b", "Golden Ridge A - Room c-06b", regions_by_room["4a_c-06b"], doors_by_room["4a_c-06b"]),
+ "4a_c-09": Room("4a", "4a_c-09", "Golden Ridge A - Room c-09", regions_by_room["4a_c-09"], doors_by_room["4a_c-09"]),
+ "4a_c-07": Room("4a", "4a_c-07", "Golden Ridge A - Room c-07", regions_by_room["4a_c-07"], doors_by_room["4a_c-07"]),
+ "4a_c-08": Room("4a", "4a_c-08", "Golden Ridge A - Room c-08", regions_by_room["4a_c-08"], doors_by_room["4a_c-08"]),
+ "4a_c-10": Room("4a", "4a_c-10", "Golden Ridge A - Room c-10", regions_by_room["4a_c-10"], doors_by_room["4a_c-10"]),
+ "4a_d-00": Room("4a", "4a_d-00", "Golden Ridge A - Room d-00", regions_by_room["4a_d-00"], doors_by_room["4a_d-00"], "Cliff Face", "4a_d-00_west"),
+ "4a_d-00b": Room("4a", "4a_d-00b", "Golden Ridge A - Room d-00b", regions_by_room["4a_d-00b"], doors_by_room["4a_d-00b"]),
+ "4a_d-01": Room("4a", "4a_d-01", "Golden Ridge A - Room d-01", regions_by_room["4a_d-01"], doors_by_room["4a_d-01"]),
+ "4a_d-02": Room("4a", "4a_d-02", "Golden Ridge A - Room d-02", regions_by_room["4a_d-02"], doors_by_room["4a_d-02"]),
+ "4a_d-03": Room("4a", "4a_d-03", "Golden Ridge A - Room d-03", regions_by_room["4a_d-03"], doors_by_room["4a_d-03"]),
+ "4a_d-04": Room("4a", "4a_d-04", "Golden Ridge A - Room d-04", regions_by_room["4a_d-04"], doors_by_room["4a_d-04"]),
+ "4a_d-05": Room("4a", "4a_d-05", "Golden Ridge A - Room d-05", regions_by_room["4a_d-05"], doors_by_room["4a_d-05"]),
+ "4a_d-06": Room("4a", "4a_d-06", "Golden Ridge A - Room d-06", regions_by_room["4a_d-06"], doors_by_room["4a_d-06"]),
+ "4a_d-07": Room("4a", "4a_d-07", "Golden Ridge A - Room d-07", regions_by_room["4a_d-07"], doors_by_room["4a_d-07"]),
+ "4a_d-08": Room("4a", "4a_d-08", "Golden Ridge A - Room d-08", regions_by_room["4a_d-08"], doors_by_room["4a_d-08"]),
+ "4a_d-09": Room("4a", "4a_d-09", "Golden Ridge A - Room d-09", regions_by_room["4a_d-09"], doors_by_room["4a_d-09"]),
+ "4a_d-10": Room("4a", "4a_d-10", "Golden Ridge A - Room d-10", regions_by_room["4a_d-10"], doors_by_room["4a_d-10"]),
+
+ "4b_a-00": Room("4b", "4b_a-00", "Golden Ridge B - Room a-00", regions_by_room["4b_a-00"], doors_by_room["4b_a-00"], "Start", "4b_a-00_west"),
+ "4b_a-01": Room("4b", "4b_a-01", "Golden Ridge B - Room a-01", regions_by_room["4b_a-01"], doors_by_room["4b_a-01"]),
+ "4b_a-02": Room("4b", "4b_a-02", "Golden Ridge B - Room a-02", regions_by_room["4b_a-02"], doors_by_room["4b_a-02"]),
+ "4b_a-03": Room("4b", "4b_a-03", "Golden Ridge B - Room a-03", regions_by_room["4b_a-03"], doors_by_room["4b_a-03"]),
+ "4b_a-04": Room("4b", "4b_a-04", "Golden Ridge B - Room a-04", regions_by_room["4b_a-04"], doors_by_room["4b_a-04"]),
+ "4b_b-00": Room("4b", "4b_b-00", "Golden Ridge B - Room b-00", regions_by_room["4b_b-00"], doors_by_room["4b_b-00"], "Stepping Stones", "4b_b-00_west"),
+ "4b_b-01": Room("4b", "4b_b-01", "Golden Ridge B - Room b-01", regions_by_room["4b_b-01"], doors_by_room["4b_b-01"]),
+ "4b_b-02": Room("4b", "4b_b-02", "Golden Ridge B - Room b-02", regions_by_room["4b_b-02"], doors_by_room["4b_b-02"]),
+ "4b_b-03": Room("4b", "4b_b-03", "Golden Ridge B - Room b-03", regions_by_room["4b_b-03"], doors_by_room["4b_b-03"]),
+ "4b_b-04": Room("4b", "4b_b-04", "Golden Ridge B - Room b-04", regions_by_room["4b_b-04"], doors_by_room["4b_b-04"]),
+ "4b_c-00": Room("4b", "4b_c-00", "Golden Ridge B - Room c-00", regions_by_room["4b_c-00"], doors_by_room["4b_c-00"], "Gusty Canyon", "4b_c-00_west"),
+ "4b_c-01": Room("4b", "4b_c-01", "Golden Ridge B - Room c-01", regions_by_room["4b_c-01"], doors_by_room["4b_c-01"]),
+ "4b_c-02": Room("4b", "4b_c-02", "Golden Ridge B - Room c-02", regions_by_room["4b_c-02"], doors_by_room["4b_c-02"]),
+ "4b_c-03": Room("4b", "4b_c-03", "Golden Ridge B - Room c-03", regions_by_room["4b_c-03"], doors_by_room["4b_c-03"]),
+ "4b_c-04": Room("4b", "4b_c-04", "Golden Ridge B - Room c-04", regions_by_room["4b_c-04"], doors_by_room["4b_c-04"]),
+ "4b_d-00": Room("4b", "4b_d-00", "Golden Ridge B - Room d-00", regions_by_room["4b_d-00"], doors_by_room["4b_d-00"], "Eye of the Storm", "4b_d-00_west"),
+ "4b_d-01": Room("4b", "4b_d-01", "Golden Ridge B - Room d-01", regions_by_room["4b_d-01"], doors_by_room["4b_d-01"]),
+ "4b_d-02": Room("4b", "4b_d-02", "Golden Ridge B - Room d-02", regions_by_room["4b_d-02"], doors_by_room["4b_d-02"]),
+ "4b_d-03": Room("4b", "4b_d-03", "Golden Ridge B - Room d-03", regions_by_room["4b_d-03"], doors_by_room["4b_d-03"]),
+ "4b_end": Room("4b", "4b_end", "Golden Ridge B - Room end", regions_by_room["4b_end"], doors_by_room["4b_end"]),
+
+ "4c_00": Room("4c", "4c_00", "Golden Ridge C - Room 00", regions_by_room["4c_00"], doors_by_room["4c_00"], "Start", "4c_00_west"),
+ "4c_01": Room("4c", "4c_01", "Golden Ridge C - Room 01", regions_by_room["4c_01"], doors_by_room["4c_01"]),
+ "4c_02": Room("4c", "4c_02", "Golden Ridge C - Room 02", regions_by_room["4c_02"], doors_by_room["4c_02"]),
+
+ "5a_a-00b": Room("5a", "5a_a-00b", "Mirror Temple A - Room a-00b", regions_by_room["5a_a-00b"], doors_by_room["5a_a-00b"], "Start", "5a_a-00b_west"),
+ "5a_a-00x": Room("5a", "5a_a-00x", "Mirror Temple A - Room a-00x", regions_by_room["5a_a-00x"], doors_by_room["5a_a-00x"]),
+ "5a_a-00d": Room("5a", "5a_a-00d", "Mirror Temple A - Room a-00d", regions_by_room["5a_a-00d"], doors_by_room["5a_a-00d"]),
+ "5a_a-00c": Room("5a", "5a_a-00c", "Mirror Temple A - Room a-00c", regions_by_room["5a_a-00c"], doors_by_room["5a_a-00c"]),
+ "5a_a-00": Room("5a", "5a_a-00", "Mirror Temple A - Room a-00", regions_by_room["5a_a-00"], doors_by_room["5a_a-00"]),
+ "5a_a-01": Room("5a", "5a_a-01", "Mirror Temple A - Room a-01", regions_by_room["5a_a-01"], doors_by_room["5a_a-01"]),
+ "5a_a-02": Room("5a", "5a_a-02", "Mirror Temple A - Room a-02", regions_by_room["5a_a-02"], doors_by_room["5a_a-02"]),
+ "5a_a-03": Room("5a", "5a_a-03", "Mirror Temple A - Room a-03", regions_by_room["5a_a-03"], doors_by_room["5a_a-03"]),
+ "5a_a-04": Room("5a", "5a_a-04", "Mirror Temple A - Room a-04", regions_by_room["5a_a-04"], doors_by_room["5a_a-04"]),
+ "5a_a-05": Room("5a", "5a_a-05", "Mirror Temple A - Room a-05", regions_by_room["5a_a-05"], doors_by_room["5a_a-05"]),
+ "5a_a-06": Room("5a", "5a_a-06", "Mirror Temple A - Room a-06", regions_by_room["5a_a-06"], doors_by_room["5a_a-06"]),
+ "5a_a-07": Room("5a", "5a_a-07", "Mirror Temple A - Room a-07", regions_by_room["5a_a-07"], doors_by_room["5a_a-07"]),
+ "5a_a-08": Room("5a", "5a_a-08", "Mirror Temple A - Room a-08", regions_by_room["5a_a-08"], doors_by_room["5a_a-08"]),
+ "5a_a-10": Room("5a", "5a_a-10", "Mirror Temple A - Room a-10", regions_by_room["5a_a-10"], doors_by_room["5a_a-10"]),
+ "5a_a-09": Room("5a", "5a_a-09", "Mirror Temple A - Room a-09", regions_by_room["5a_a-09"], doors_by_room["5a_a-09"]),
+ "5a_a-11": Room("5a", "5a_a-11", "Mirror Temple A - Room a-11", regions_by_room["5a_a-11"], doors_by_room["5a_a-11"]),
+ "5a_a-12": Room("5a", "5a_a-12", "Mirror Temple A - Room a-12", regions_by_room["5a_a-12"], doors_by_room["5a_a-12"]),
+ "5a_a-15": Room("5a", "5a_a-15", "Mirror Temple A - Room a-15", regions_by_room["5a_a-15"], doors_by_room["5a_a-15"]),
+ "5a_a-14": Room("5a", "5a_a-14", "Mirror Temple A - Room a-14", regions_by_room["5a_a-14"], doors_by_room["5a_a-14"]),
+ "5a_a-13": Room("5a", "5a_a-13", "Mirror Temple A - Room a-13", regions_by_room["5a_a-13"], doors_by_room["5a_a-13"]),
+ "5a_b-00": Room("5a", "5a_b-00", "Mirror Temple A - Room b-00", regions_by_room["5a_b-00"], doors_by_room["5a_b-00"], "Depths", "5a_b-00_west"),
+ "5a_b-18": Room("5a", "5a_b-18", "Mirror Temple A - Room b-18", regions_by_room["5a_b-18"], doors_by_room["5a_b-18"]),
+ "5a_b-01": Room("5a", "5a_b-01", "Mirror Temple A - Room b-01", regions_by_room["5a_b-01"], doors_by_room["5a_b-01"]),
+ "5a_b-01c": Room("5a", "5a_b-01c", "Mirror Temple A - Room b-01c", regions_by_room["5a_b-01c"], doors_by_room["5a_b-01c"]),
+ "5a_b-20": Room("5a", "5a_b-20", "Mirror Temple A - Room b-20", regions_by_room["5a_b-20"], doors_by_room["5a_b-20"]),
+ "5a_b-21": Room("5a", "5a_b-21", "Mirror Temple A - Room b-21", regions_by_room["5a_b-21"], doors_by_room["5a_b-21"]),
+ "5a_b-01b": Room("5a", "5a_b-01b", "Mirror Temple A - Room b-01b", regions_by_room["5a_b-01b"], doors_by_room["5a_b-01b"]),
+ "5a_b-02": Room("5a", "5a_b-02", "Mirror Temple A - Room b-02", regions_by_room["5a_b-02"], doors_by_room["5a_b-02"]),
+ "5a_b-03": Room("5a", "5a_b-03", "Mirror Temple A - Room b-03", regions_by_room["5a_b-03"], doors_by_room["5a_b-03"]),
+ "5a_b-05": Room("5a", "5a_b-05", "Mirror Temple A - Room b-05", regions_by_room["5a_b-05"], doors_by_room["5a_b-05"]),
+ "5a_b-04": Room("5a", "5a_b-04", "Mirror Temple A - Room b-04", regions_by_room["5a_b-04"], doors_by_room["5a_b-04"]),
+ "5a_b-07": Room("5a", "5a_b-07", "Mirror Temple A - Room b-07", regions_by_room["5a_b-07"], doors_by_room["5a_b-07"]),
+ "5a_b-08": Room("5a", "5a_b-08", "Mirror Temple A - Room b-08", regions_by_room["5a_b-08"], doors_by_room["5a_b-08"]),
+ "5a_b-09": Room("5a", "5a_b-09", "Mirror Temple A - Room b-09", regions_by_room["5a_b-09"], doors_by_room["5a_b-09"]),
+ "5a_b-10": Room("5a", "5a_b-10", "Mirror Temple A - Room b-10", regions_by_room["5a_b-10"], doors_by_room["5a_b-10"]),
+ "5a_b-11": Room("5a", "5a_b-11", "Mirror Temple A - Room b-11", regions_by_room["5a_b-11"], doors_by_room["5a_b-11"]),
+ "5a_b-12": Room("5a", "5a_b-12", "Mirror Temple A - Room b-12", regions_by_room["5a_b-12"], doors_by_room["5a_b-12"]),
+ "5a_b-13": Room("5a", "5a_b-13", "Mirror Temple A - Room b-13", regions_by_room["5a_b-13"], doors_by_room["5a_b-13"]),
+ "5a_b-17": Room("5a", "5a_b-17", "Mirror Temple A - Room b-17", regions_by_room["5a_b-17"], doors_by_room["5a_b-17"]),
+ "5a_b-22": Room("5a", "5a_b-22", "Mirror Temple A - Room b-22", regions_by_room["5a_b-22"], doors_by_room["5a_b-22"]),
+ "5a_b-06": Room("5a", "5a_b-06", "Mirror Temple A - Room b-06", regions_by_room["5a_b-06"], doors_by_room["5a_b-06"]),
+ "5a_b-19": Room("5a", "5a_b-19", "Mirror Temple A - Room b-19", regions_by_room["5a_b-19"], doors_by_room["5a_b-19"]),
+ "5a_b-14": Room("5a", "5a_b-14", "Mirror Temple A - Room b-14", regions_by_room["5a_b-14"], doors_by_room["5a_b-14"]),
+ "5a_b-15": Room("5a", "5a_b-15", "Mirror Temple A - Room b-15", regions_by_room["5a_b-15"], doors_by_room["5a_b-15"]),
+ "5a_b-16": Room("5a", "5a_b-16", "Mirror Temple A - Room b-16", regions_by_room["5a_b-16"], doors_by_room["5a_b-16"]),
+ "5a_void": Room("5a", "5a_void", "Mirror Temple A - Room void", regions_by_room["5a_void"], doors_by_room["5a_void"]),
+ "5a_c-00": Room("5a", "5a_c-00", "Mirror Temple A - Room c-00", regions_by_room["5a_c-00"], doors_by_room["5a_c-00"], "Unravelling", "5a_c-00_top"),
+ "5a_c-01": Room("5a", "5a_c-01", "Mirror Temple A - Room c-01", regions_by_room["5a_c-01"], doors_by_room["5a_c-01"]),
+ "5a_c-01b": Room("5a", "5a_c-01b", "Mirror Temple A - Room c-01b", regions_by_room["5a_c-01b"], doors_by_room["5a_c-01b"]),
+ "5a_c-01c": Room("5a", "5a_c-01c", "Mirror Temple A - Room c-01c", regions_by_room["5a_c-01c"], doors_by_room["5a_c-01c"]),
+ "5a_c-08b": Room("5a", "5a_c-08b", "Mirror Temple A - Room c-08b", regions_by_room["5a_c-08b"], doors_by_room["5a_c-08b"]),
+ "5a_c-08": Room("5a", "5a_c-08", "Mirror Temple A - Room c-08", regions_by_room["5a_c-08"], doors_by_room["5a_c-08"]),
+ "5a_c-10": Room("5a", "5a_c-10", "Mirror Temple A - Room c-10", regions_by_room["5a_c-10"], doors_by_room["5a_c-10"]),
+ "5a_c-12": Room("5a", "5a_c-12", "Mirror Temple A - Room c-12", regions_by_room["5a_c-12"], doors_by_room["5a_c-12"]),
+ "5a_c-07": Room("5a", "5a_c-07", "Mirror Temple A - Room c-07", regions_by_room["5a_c-07"], doors_by_room["5a_c-07"]),
+ "5a_c-11": Room("5a", "5a_c-11", "Mirror Temple A - Room c-11", regions_by_room["5a_c-11"], doors_by_room["5a_c-11"]),
+ "5a_c-09": Room("5a", "5a_c-09", "Mirror Temple A - Room c-09", regions_by_room["5a_c-09"], doors_by_room["5a_c-09"]),
+ "5a_c-13": Room("5a", "5a_c-13", "Mirror Temple A - Room c-13", regions_by_room["5a_c-13"], doors_by_room["5a_c-13"]),
+ "5a_d-00": Room("5a", "5a_d-00", "Mirror Temple A - Room d-00", regions_by_room["5a_d-00"], doors_by_room["5a_d-00"], "Search", "5a_d-00_south"),
+ "5a_d-01": Room("5a", "5a_d-01", "Mirror Temple A - Room d-01", regions_by_room["5a_d-01"], doors_by_room["5a_d-01"]),
+ "5a_d-09": Room("5a", "5a_d-09", "Mirror Temple A - Room d-09", regions_by_room["5a_d-09"], doors_by_room["5a_d-09"]),
+ "5a_d-04": Room("5a", "5a_d-04", "Mirror Temple A - Room d-04", regions_by_room["5a_d-04"], doors_by_room["5a_d-04"]),
+ "5a_d-05": Room("5a", "5a_d-05", "Mirror Temple A - Room d-05", regions_by_room["5a_d-05"], doors_by_room["5a_d-05"]),
+ "5a_d-06": Room("5a", "5a_d-06", "Mirror Temple A - Room d-06", regions_by_room["5a_d-06"], doors_by_room["5a_d-06"]),
+ "5a_d-07": Room("5a", "5a_d-07", "Mirror Temple A - Room d-07", regions_by_room["5a_d-07"], doors_by_room["5a_d-07"]),
+ "5a_d-02": Room("5a", "5a_d-02", "Mirror Temple A - Room d-02", regions_by_room["5a_d-02"], doors_by_room["5a_d-02"]),
+ "5a_d-03": Room("5a", "5a_d-03", "Mirror Temple A - Room d-03", regions_by_room["5a_d-03"], doors_by_room["5a_d-03"]),
+ "5a_d-15": Room("5a", "5a_d-15", "Mirror Temple A - Room d-15", regions_by_room["5a_d-15"], doors_by_room["5a_d-15"]),
+ "5a_d-13": Room("5a", "5a_d-13", "Mirror Temple A - Room d-13", regions_by_room["5a_d-13"], doors_by_room["5a_d-13"]),
+ "5a_d-19b": Room("5a", "5a_d-19b", "Mirror Temple A - Room d-19b", regions_by_room["5a_d-19b"], doors_by_room["5a_d-19b"]),
+ "5a_d-19": Room("5a", "5a_d-19", "Mirror Temple A - Room d-19", regions_by_room["5a_d-19"], doors_by_room["5a_d-19"]),
+ "5a_d-10": Room("5a", "5a_d-10", "Mirror Temple A - Room d-10", regions_by_room["5a_d-10"], doors_by_room["5a_d-10"]),
+ "5a_d-20": Room("5a", "5a_d-20", "Mirror Temple A - Room d-20", regions_by_room["5a_d-20"], doors_by_room["5a_d-20"]),
+ "5a_e-00": Room("5a", "5a_e-00", "Mirror Temple A - Room e-00", regions_by_room["5a_e-00"], doors_by_room["5a_e-00"], "Rescue", "5a_e-00_west"),
+ "5a_e-01": Room("5a", "5a_e-01", "Mirror Temple A - Room e-01", regions_by_room["5a_e-01"], doors_by_room["5a_e-01"]),
+ "5a_e-02": Room("5a", "5a_e-02", "Mirror Temple A - Room e-02", regions_by_room["5a_e-02"], doors_by_room["5a_e-02"]),
+ "5a_e-03": Room("5a", "5a_e-03", "Mirror Temple A - Room e-03", regions_by_room["5a_e-03"], doors_by_room["5a_e-03"]),
+ "5a_e-04": Room("5a", "5a_e-04", "Mirror Temple A - Room e-04", regions_by_room["5a_e-04"], doors_by_room["5a_e-04"]),
+ "5a_e-06": Room("5a", "5a_e-06", "Mirror Temple A - Room e-06", regions_by_room["5a_e-06"], doors_by_room["5a_e-06"]),
+ "5a_e-05": Room("5a", "5a_e-05", "Mirror Temple A - Room e-05", regions_by_room["5a_e-05"], doors_by_room["5a_e-05"]),
+ "5a_e-07": Room("5a", "5a_e-07", "Mirror Temple A - Room e-07", regions_by_room["5a_e-07"], doors_by_room["5a_e-07"]),
+ "5a_e-08": Room("5a", "5a_e-08", "Mirror Temple A - Room e-08", regions_by_room["5a_e-08"], doors_by_room["5a_e-08"]),
+ "5a_e-09": Room("5a", "5a_e-09", "Mirror Temple A - Room e-09", regions_by_room["5a_e-09"], doors_by_room["5a_e-09"]),
+ "5a_e-10": Room("5a", "5a_e-10", "Mirror Temple A - Room e-10", regions_by_room["5a_e-10"], doors_by_room["5a_e-10"]),
+ "5a_e-11": Room("5a", "5a_e-11", "Mirror Temple A - Room e-11", regions_by_room["5a_e-11"], doors_by_room["5a_e-11"]),
+
+ "5b_start": Room("5b", "5b_start", "Mirror Temple B - Room start", regions_by_room["5b_start"], doors_by_room["5b_start"], "Start", "5b_start_west"),
+ "5b_a-00": Room("5b", "5b_a-00", "Mirror Temple B - Room a-00", regions_by_room["5b_a-00"], doors_by_room["5b_a-00"]),
+ "5b_a-01": Room("5b", "5b_a-01", "Mirror Temple B - Room a-01", regions_by_room["5b_a-01"], doors_by_room["5b_a-01"]),
+ "5b_a-02": Room("5b", "5b_a-02", "Mirror Temple B - Room a-02", regions_by_room["5b_a-02"], doors_by_room["5b_a-02"]),
+ "5b_b-00": Room("5b", "5b_b-00", "Mirror Temple B - Room b-00", regions_by_room["5b_b-00"], doors_by_room["5b_b-00"], "Central Chamber", "5b_b-00_south"),
+ "5b_b-01": Room("5b", "5b_b-01", "Mirror Temple B - Room b-01", regions_by_room["5b_b-01"], doors_by_room["5b_b-01"]),
+ "5b_b-04": Room("5b", "5b_b-04", "Mirror Temple B - Room b-04", regions_by_room["5b_b-04"], doors_by_room["5b_b-04"]),
+ "5b_b-02": Room("5b", "5b_b-02", "Mirror Temple B - Room b-02", regions_by_room["5b_b-02"], doors_by_room["5b_b-02"]),
+ "5b_b-05": Room("5b", "5b_b-05", "Mirror Temple B - Room b-05", regions_by_room["5b_b-05"], doors_by_room["5b_b-05"]),
+ "5b_b-06": Room("5b", "5b_b-06", "Mirror Temple B - Room b-06", regions_by_room["5b_b-06"], doors_by_room["5b_b-06"]),
+ "5b_b-07": Room("5b", "5b_b-07", "Mirror Temple B - Room b-07", regions_by_room["5b_b-07"], doors_by_room["5b_b-07"]),
+ "5b_b-03": Room("5b", "5b_b-03", "Mirror Temple B - Room b-03", regions_by_room["5b_b-03"], doors_by_room["5b_b-03"]),
+ "5b_b-08": Room("5b", "5b_b-08", "Mirror Temple B - Room b-08", regions_by_room["5b_b-08"], doors_by_room["5b_b-08"]),
+ "5b_b-09": Room("5b", "5b_b-09", "Mirror Temple B - Room b-09", regions_by_room["5b_b-09"], doors_by_room["5b_b-09"]),
+ "5b_c-00": Room("5b", "5b_c-00", "Mirror Temple B - Room c-00", regions_by_room["5b_c-00"], doors_by_room["5b_c-00"], "Through the Mirror", "5b_c-00_mirror"),
+ "5b_c-01": Room("5b", "5b_c-01", "Mirror Temple B - Room c-01", regions_by_room["5b_c-01"], doors_by_room["5b_c-01"]),
+ "5b_c-02": Room("5b", "5b_c-02", "Mirror Temple B - Room c-02", regions_by_room["5b_c-02"], doors_by_room["5b_c-02"]),
+ "5b_c-03": Room("5b", "5b_c-03", "Mirror Temple B - Room c-03", regions_by_room["5b_c-03"], doors_by_room["5b_c-03"]),
+ "5b_c-04": Room("5b", "5b_c-04", "Mirror Temple B - Room c-04", regions_by_room["5b_c-04"], doors_by_room["5b_c-04"]),
+ "5b_d-00": Room("5b", "5b_d-00", "Mirror Temple B - Room d-00", regions_by_room["5b_d-00"], doors_by_room["5b_d-00"], "Mix Master", "5b_d-00_west"),
+ "5b_d-01": Room("5b", "5b_d-01", "Mirror Temple B - Room d-01", regions_by_room["5b_d-01"], doors_by_room["5b_d-01"]),
+ "5b_d-02": Room("5b", "5b_d-02", "Mirror Temple B - Room d-02", regions_by_room["5b_d-02"], doors_by_room["5b_d-02"]),
+ "5b_d-03": Room("5b", "5b_d-03", "Mirror Temple B - Room d-03", regions_by_room["5b_d-03"], doors_by_room["5b_d-03"]),
+ "5b_d-04": Room("5b", "5b_d-04", "Mirror Temple B - Room d-04", regions_by_room["5b_d-04"], doors_by_room["5b_d-04"]),
+ "5b_d-05": Room("5b", "5b_d-05", "Mirror Temple B - Room d-05", regions_by_room["5b_d-05"], doors_by_room["5b_d-05"]),
+
+ "5c_00": Room("5c", "5c_00", "Mirror Temple C - Room 00", regions_by_room["5c_00"], doors_by_room["5c_00"], "Start", "5c_00_west"),
+ "5c_01": Room("5c", "5c_01", "Mirror Temple C - Room 01", regions_by_room["5c_01"], doors_by_room["5c_01"]),
+ "5c_02": Room("5c", "5c_02", "Mirror Temple C - Room 02", regions_by_room["5c_02"], doors_by_room["5c_02"]),
+
+ "6a_00": Room("6a", "6a_00", "Reflection A - Room 00", regions_by_room["6a_00"], doors_by_room["6a_00"], "Start", "6a_00_east"),
+ "6a_01": Room("6a", "6a_01", "Reflection A - Room 01", regions_by_room["6a_01"], doors_by_room["6a_01"]),
+ "6a_02": Room("6a", "6a_02", "Reflection A - Room 02", regions_by_room["6a_02"], doors_by_room["6a_02"]),
+ "6a_03": Room("6a", "6a_03", "Reflection A - Room 03", regions_by_room["6a_03"], doors_by_room["6a_03"]),
+ "6a_02b": Room("6a", "6a_02b", "Reflection A - Room 02b", regions_by_room["6a_02b"], doors_by_room["6a_02b"]),
+ "6a_04": Room("6a", "6a_04", "Reflection A - Room 04", regions_by_room["6a_04"], doors_by_room["6a_04"], "Hollows", "6a_04_south"),
+ "6a_04b": Room("6a", "6a_04b", "Reflection A - Room 04b", regions_by_room["6a_04b"], doors_by_room["6a_04b"]),
+ "6a_04c": Room("6a", "6a_04c", "Reflection A - Room 04c", regions_by_room["6a_04c"], doors_by_room["6a_04c"]),
+ "6a_04d": Room("6a", "6a_04d", "Reflection A - Room 04d", regions_by_room["6a_04d"], doors_by_room["6a_04d"]),
+ "6a_04e": Room("6a", "6a_04e", "Reflection A - Room 04e", regions_by_room["6a_04e"], doors_by_room["6a_04e"]),
+ "6a_05": Room("6a", "6a_05", "Reflection A - Room 05", regions_by_room["6a_05"], doors_by_room["6a_05"]),
+ "6a_06": Room("6a", "6a_06", "Reflection A - Room 06", regions_by_room["6a_06"], doors_by_room["6a_06"]),
+ "6a_07": Room("6a", "6a_07", "Reflection A - Room 07", regions_by_room["6a_07"], doors_by_room["6a_07"]),
+ "6a_08a": Room("6a", "6a_08a", "Reflection A - Room 08a", regions_by_room["6a_08a"], doors_by_room["6a_08a"]),
+ "6a_08b": Room("6a", "6a_08b", "Reflection A - Room 08b", regions_by_room["6a_08b"], doors_by_room["6a_08b"]),
+ "6a_09": Room("6a", "6a_09", "Reflection A - Room 09", regions_by_room["6a_09"], doors_by_room["6a_09"]),
+ "6a_10a": Room("6a", "6a_10a", "Reflection A - Room 10a", regions_by_room["6a_10a"], doors_by_room["6a_10a"]),
+ "6a_10b": Room("6a", "6a_10b", "Reflection A - Room 10b", regions_by_room["6a_10b"], doors_by_room["6a_10b"]),
+ "6a_11": Room("6a", "6a_11", "Reflection A - Room 11", regions_by_room["6a_11"], doors_by_room["6a_11"]),
+ "6a_12a": Room("6a", "6a_12a", "Reflection A - Room 12a", regions_by_room["6a_12a"], doors_by_room["6a_12a"]),
+ "6a_12b": Room("6a", "6a_12b", "Reflection A - Room 12b", regions_by_room["6a_12b"], doors_by_room["6a_12b"]),
+ "6a_13": Room("6a", "6a_13", "Reflection A - Room 13", regions_by_room["6a_13"], doors_by_room["6a_13"]),
+ "6a_14a": Room("6a", "6a_14a", "Reflection A - Room 14a", regions_by_room["6a_14a"], doors_by_room["6a_14a"]),
+ "6a_14b": Room("6a", "6a_14b", "Reflection A - Room 14b", regions_by_room["6a_14b"], doors_by_room["6a_14b"]),
+ "6a_15": Room("6a", "6a_15", "Reflection A - Room 15", regions_by_room["6a_15"], doors_by_room["6a_15"]),
+ "6a_16a": Room("6a", "6a_16a", "Reflection A - Room 16a", regions_by_room["6a_16a"], doors_by_room["6a_16a"]),
+ "6a_16b": Room("6a", "6a_16b", "Reflection A - Room 16b", regions_by_room["6a_16b"], doors_by_room["6a_16b"]),
+ "6a_17": Room("6a", "6a_17", "Reflection A - Room 17", regions_by_room["6a_17"], doors_by_room["6a_17"]),
+ "6a_18a": Room("6a", "6a_18a", "Reflection A - Room 18a", regions_by_room["6a_18a"], doors_by_room["6a_18a"]),
+ "6a_18b": Room("6a", "6a_18b", "Reflection A - Room 18b", regions_by_room["6a_18b"], doors_by_room["6a_18b"]),
+ "6a_19": Room("6a", "6a_19", "Reflection A - Room 19", regions_by_room["6a_19"], doors_by_room["6a_19"]),
+ "6a_20": Room("6a", "6a_20", "Reflection A - Room 20", regions_by_room["6a_20"], doors_by_room["6a_20"]),
+ "6a_b-00": Room("6a", "6a_b-00", "Reflection A - Room b-00", regions_by_room["6a_b-00"], doors_by_room["6a_b-00"], "Reflection", "6a_b-00_west"),
+ "6a_b-00b": Room("6a", "6a_b-00b", "Reflection A - Room b-00b", regions_by_room["6a_b-00b"], doors_by_room["6a_b-00b"]),
+ "6a_b-00c": Room("6a", "6a_b-00c", "Reflection A - Room b-00c", regions_by_room["6a_b-00c"], doors_by_room["6a_b-00c"]),
+ "6a_b-01": Room("6a", "6a_b-01", "Reflection A - Room b-01", regions_by_room["6a_b-01"], doors_by_room["6a_b-01"]),
+ "6a_b-02": Room("6a", "6a_b-02", "Reflection A - Room b-02", regions_by_room["6a_b-02"], doors_by_room["6a_b-02"]),
+ "6a_b-02b": Room("6a", "6a_b-02b", "Reflection A - Room b-02b", regions_by_room["6a_b-02b"], doors_by_room["6a_b-02b"]),
+ "6a_b-03": Room("6a", "6a_b-03", "Reflection A - Room b-03", regions_by_room["6a_b-03"], doors_by_room["6a_b-03"]),
+ "6a_boss-00": Room("6a", "6a_boss-00", "Reflection A - Room boss-00", regions_by_room["6a_boss-00"], doors_by_room["6a_boss-00"], "Rock Bottom", "6a_boss-00_west"),
+ "6a_boss-01": Room("6a", "6a_boss-01", "Reflection A - Room boss-01", regions_by_room["6a_boss-01"], doors_by_room["6a_boss-01"]),
+ "6a_boss-02": Room("6a", "6a_boss-02", "Reflection A - Room boss-02", regions_by_room["6a_boss-02"], doors_by_room["6a_boss-02"]),
+ "6a_boss-03": Room("6a", "6a_boss-03", "Reflection A - Room boss-03", regions_by_room["6a_boss-03"], doors_by_room["6a_boss-03"]),
+ "6a_boss-04": Room("6a", "6a_boss-04", "Reflection A - Room boss-04", regions_by_room["6a_boss-04"], doors_by_room["6a_boss-04"]),
+ "6a_boss-05": Room("6a", "6a_boss-05", "Reflection A - Room boss-05", regions_by_room["6a_boss-05"], doors_by_room["6a_boss-05"]),
+ "6a_boss-06": Room("6a", "6a_boss-06", "Reflection A - Room boss-06", regions_by_room["6a_boss-06"], doors_by_room["6a_boss-06"]),
+ "6a_boss-07": Room("6a", "6a_boss-07", "Reflection A - Room boss-07", regions_by_room["6a_boss-07"], doors_by_room["6a_boss-07"]),
+ "6a_boss-08": Room("6a", "6a_boss-08", "Reflection A - Room boss-08", regions_by_room["6a_boss-08"], doors_by_room["6a_boss-08"]),
+ "6a_boss-09": Room("6a", "6a_boss-09", "Reflection A - Room boss-09", regions_by_room["6a_boss-09"], doors_by_room["6a_boss-09"]),
+ "6a_boss-10": Room("6a", "6a_boss-10", "Reflection A - Room boss-10", regions_by_room["6a_boss-10"], doors_by_room["6a_boss-10"]),
+ "6a_boss-11": Room("6a", "6a_boss-11", "Reflection A - Room boss-11", regions_by_room["6a_boss-11"], doors_by_room["6a_boss-11"]),
+ "6a_boss-12": Room("6a", "6a_boss-12", "Reflection A - Room boss-12", regions_by_room["6a_boss-12"], doors_by_room["6a_boss-12"]),
+ "6a_boss-13": Room("6a", "6a_boss-13", "Reflection A - Room boss-13", regions_by_room["6a_boss-13"], doors_by_room["6a_boss-13"]),
+ "6a_boss-14": Room("6a", "6a_boss-14", "Reflection A - Room boss-14", regions_by_room["6a_boss-14"], doors_by_room["6a_boss-14"]),
+ "6a_boss-15": Room("6a", "6a_boss-15", "Reflection A - Room boss-15", regions_by_room["6a_boss-15"], doors_by_room["6a_boss-15"]),
+ "6a_boss-16": Room("6a", "6a_boss-16", "Reflection A - Room boss-16", regions_by_room["6a_boss-16"], doors_by_room["6a_boss-16"]),
+ "6a_boss-17": Room("6a", "6a_boss-17", "Reflection A - Room boss-17", regions_by_room["6a_boss-17"], doors_by_room["6a_boss-17"]),
+ "6a_boss-18": Room("6a", "6a_boss-18", "Reflection A - Room boss-18", regions_by_room["6a_boss-18"], doors_by_room["6a_boss-18"]),
+ "6a_boss-19": Room("6a", "6a_boss-19", "Reflection A - Room boss-19", regions_by_room["6a_boss-19"], doors_by_room["6a_boss-19"]),
+ "6a_boss-20": Room("6a", "6a_boss-20", "Reflection A - Room boss-20", regions_by_room["6a_boss-20"], doors_by_room["6a_boss-20"]),
+ "6a_after-00": Room("6a", "6a_after-00", "Reflection A - Room after-00", regions_by_room["6a_after-00"], doors_by_room["6a_after-00"], "Resolution", "6a_after-00_bottom"),
+ "6a_after-01": Room("6a", "6a_after-01", "Reflection A - Room after-01", regions_by_room["6a_after-01"], doors_by_room["6a_after-01"]),
+
+ "6b_a-00": Room("6b", "6b_a-00", "Reflection B - Room a-00", regions_by_room["6b_a-00"], doors_by_room["6b_a-00"], "Start", "6b_a-00_bottom"),
+ "6b_a-01": Room("6b", "6b_a-01", "Reflection B - Room a-01", regions_by_room["6b_a-01"], doors_by_room["6b_a-01"]),
+ "6b_a-02": Room("6b", "6b_a-02", "Reflection B - Room a-02", regions_by_room["6b_a-02"], doors_by_room["6b_a-02"]),
+ "6b_a-03": Room("6b", "6b_a-03", "Reflection B - Room a-03", regions_by_room["6b_a-03"], doors_by_room["6b_a-03"]),
+ "6b_a-04": Room("6b", "6b_a-04", "Reflection B - Room a-04", regions_by_room["6b_a-04"], doors_by_room["6b_a-04"]),
+ "6b_a-05": Room("6b", "6b_a-05", "Reflection B - Room a-05", regions_by_room["6b_a-05"], doors_by_room["6b_a-05"]),
+ "6b_a-06": Room("6b", "6b_a-06", "Reflection B - Room a-06", regions_by_room["6b_a-06"], doors_by_room["6b_a-06"]),
+ "6b_b-00": Room("6b", "6b_b-00", "Reflection B - Room b-00", regions_by_room["6b_b-00"], doors_by_room["6b_b-00"], "Reflection", "6b_b-00_west"),
+ "6b_b-01": Room("6b", "6b_b-01", "Reflection B - Room b-01", regions_by_room["6b_b-01"], doors_by_room["6b_b-01"]),
+ "6b_b-02": Room("6b", "6b_b-02", "Reflection B - Room b-02", regions_by_room["6b_b-02"], doors_by_room["6b_b-02"]),
+ "6b_b-03": Room("6b", "6b_b-03", "Reflection B - Room b-03", regions_by_room["6b_b-03"], doors_by_room["6b_b-03"]),
+ "6b_b-04": Room("6b", "6b_b-04", "Reflection B - Room b-04", regions_by_room["6b_b-04"], doors_by_room["6b_b-04"]),
+ "6b_b-05": Room("6b", "6b_b-05", "Reflection B - Room b-05", regions_by_room["6b_b-05"], doors_by_room["6b_b-05"]),
+ "6b_b-06": Room("6b", "6b_b-06", "Reflection B - Room b-06", regions_by_room["6b_b-06"], doors_by_room["6b_b-06"]),
+ "6b_b-07": Room("6b", "6b_b-07", "Reflection B - Room b-07", regions_by_room["6b_b-07"], doors_by_room["6b_b-07"]),
+ "6b_b-08": Room("6b", "6b_b-08", "Reflection B - Room b-08", regions_by_room["6b_b-08"], doors_by_room["6b_b-08"]),
+ "6b_b-10": Room("6b", "6b_b-10", "Reflection B - Room b-10", regions_by_room["6b_b-10"], doors_by_room["6b_b-10"]),
+ "6b_c-00": Room("6b", "6b_c-00", "Reflection B - Room c-00", regions_by_room["6b_c-00"], doors_by_room["6b_c-00"], "Rock Bottom", "6b_c-00_west"),
+ "6b_c-01": Room("6b", "6b_c-01", "Reflection B - Room c-01", regions_by_room["6b_c-01"], doors_by_room["6b_c-01"]),
+ "6b_c-02": Room("6b", "6b_c-02", "Reflection B - Room c-02", regions_by_room["6b_c-02"], doors_by_room["6b_c-02"]),
+ "6b_c-03": Room("6b", "6b_c-03", "Reflection B - Room c-03", regions_by_room["6b_c-03"], doors_by_room["6b_c-03"]),
+ "6b_c-04": Room("6b", "6b_c-04", "Reflection B - Room c-04", regions_by_room["6b_c-04"], doors_by_room["6b_c-04"]),
+ "6b_d-00": Room("6b", "6b_d-00", "Reflection B - Room d-00", regions_by_room["6b_d-00"], doors_by_room["6b_d-00"], "Reprieve", "6b_d-00_west"),
+ "6b_d-01": Room("6b", "6b_d-01", "Reflection B - Room d-01", regions_by_room["6b_d-01"], doors_by_room["6b_d-01"]),
+ "6b_d-02": Room("6b", "6b_d-02", "Reflection B - Room d-02", regions_by_room["6b_d-02"], doors_by_room["6b_d-02"]),
+ "6b_d-03": Room("6b", "6b_d-03", "Reflection B - Room d-03", regions_by_room["6b_d-03"], doors_by_room["6b_d-03"]),
+ "6b_d-04": Room("6b", "6b_d-04", "Reflection B - Room d-04", regions_by_room["6b_d-04"], doors_by_room["6b_d-04"]),
+ "6b_d-05": Room("6b", "6b_d-05", "Reflection B - Room d-05", regions_by_room["6b_d-05"], doors_by_room["6b_d-05"]),
+
+ "6c_00": Room("6c", "6c_00", "Reflection C - Room 00", regions_by_room["6c_00"], doors_by_room["6c_00"], "Start", "6c_00_west"),
+ "6c_01": Room("6c", "6c_01", "Reflection C - Room 01", regions_by_room["6c_01"], doors_by_room["6c_01"]),
+ "6c_02": Room("6c", "6c_02", "Reflection C - Room 02", regions_by_room["6c_02"], doors_by_room["6c_02"]),
+
+ "7a_a-00": Room("7a", "7a_a-00", "The Summit A - Room a-00", regions_by_room["7a_a-00"], doors_by_room["7a_a-00"], "Start", "7a_a-00_west"),
+ "7a_a-01": Room("7a", "7a_a-01", "The Summit A - Room a-01", regions_by_room["7a_a-01"], doors_by_room["7a_a-01"]),
+ "7a_a-02": Room("7a", "7a_a-02", "The Summit A - Room a-02", regions_by_room["7a_a-02"], doors_by_room["7a_a-02"]),
+ "7a_a-02b": Room("7a", "7a_a-02b", "The Summit A - Room a-02b", regions_by_room["7a_a-02b"], doors_by_room["7a_a-02b"]),
+ "7a_a-03": Room("7a", "7a_a-03", "The Summit A - Room a-03", regions_by_room["7a_a-03"], doors_by_room["7a_a-03"]),
+ "7a_a-04": Room("7a", "7a_a-04", "The Summit A - Room a-04", regions_by_room["7a_a-04"], doors_by_room["7a_a-04"]),
+ "7a_a-04b": Room("7a", "7a_a-04b", "The Summit A - Room a-04b", regions_by_room["7a_a-04b"], doors_by_room["7a_a-04b"]),
+ "7a_a-05": Room("7a", "7a_a-05", "The Summit A - Room a-05", regions_by_room["7a_a-05"], doors_by_room["7a_a-05"]),
+ "7a_a-06": Room("7a", "7a_a-06", "The Summit A - Room a-06", regions_by_room["7a_a-06"], doors_by_room["7a_a-06"]),
+ "7a_b-00": Room("7a", "7a_b-00", "The Summit A - Room b-00", regions_by_room["7a_b-00"], doors_by_room["7a_b-00"], "500 M", "7a_b-00_bottom"),
+ "7a_b-01": Room("7a", "7a_b-01", "The Summit A - Room b-01", regions_by_room["7a_b-01"], doors_by_room["7a_b-01"]),
+ "7a_b-02": Room("7a", "7a_b-02", "The Summit A - Room b-02", regions_by_room["7a_b-02"], doors_by_room["7a_b-02"]),
+ "7a_b-02b": Room("7a", "7a_b-02b", "The Summit A - Room b-02b", regions_by_room["7a_b-02b"], doors_by_room["7a_b-02b"]),
+ "7a_b-02e": Room("7a", "7a_b-02e", "The Summit A - Room b-02e", regions_by_room["7a_b-02e"], doors_by_room["7a_b-02e"]),
+ "7a_b-02c": Room("7a", "7a_b-02c", "The Summit A - Room b-02c", regions_by_room["7a_b-02c"], doors_by_room["7a_b-02c"]),
+ "7a_b-02d": Room("7a", "7a_b-02d", "The Summit A - Room b-02d", regions_by_room["7a_b-02d"], doors_by_room["7a_b-02d"]),
+ "7a_b-03": Room("7a", "7a_b-03", "The Summit A - Room b-03", regions_by_room["7a_b-03"], doors_by_room["7a_b-03"]),
+ "7a_b-04": Room("7a", "7a_b-04", "The Summit A - Room b-04", regions_by_room["7a_b-04"], doors_by_room["7a_b-04"]),
+ "7a_b-05": Room("7a", "7a_b-05", "The Summit A - Room b-05", regions_by_room["7a_b-05"], doors_by_room["7a_b-05"]),
+ "7a_b-06": Room("7a", "7a_b-06", "The Summit A - Room b-06", regions_by_room["7a_b-06"], doors_by_room["7a_b-06"]),
+ "7a_b-07": Room("7a", "7a_b-07", "The Summit A - Room b-07", regions_by_room["7a_b-07"], doors_by_room["7a_b-07"]),
+ "7a_b-08": Room("7a", "7a_b-08", "The Summit A - Room b-08", regions_by_room["7a_b-08"], doors_by_room["7a_b-08"]),
+ "7a_b-09": Room("7a", "7a_b-09", "The Summit A - Room b-09", regions_by_room["7a_b-09"], doors_by_room["7a_b-09"]),
+ "7a_c-00": Room("7a", "7a_c-00", "The Summit A - Room c-00", regions_by_room["7a_c-00"], doors_by_room["7a_c-00"], "1000 M", "7a_c-00_west"),
+ "7a_c-01": Room("7a", "7a_c-01", "The Summit A - Room c-01", regions_by_room["7a_c-01"], doors_by_room["7a_c-01"]),
+ "7a_c-02": Room("7a", "7a_c-02", "The Summit A - Room c-02", regions_by_room["7a_c-02"], doors_by_room["7a_c-02"]),
+ "7a_c-03": Room("7a", "7a_c-03", "The Summit A - Room c-03", regions_by_room["7a_c-03"], doors_by_room["7a_c-03"]),
+ "7a_c-03b": Room("7a", "7a_c-03b", "The Summit A - Room c-03b", regions_by_room["7a_c-03b"], doors_by_room["7a_c-03b"]),
+ "7a_c-04": Room("7a", "7a_c-04", "The Summit A - Room c-04", regions_by_room["7a_c-04"], doors_by_room["7a_c-04"]),
+ "7a_c-05": Room("7a", "7a_c-05", "The Summit A - Room c-05", regions_by_room["7a_c-05"], doors_by_room["7a_c-05"]),
+ "7a_c-06": Room("7a", "7a_c-06", "The Summit A - Room c-06", regions_by_room["7a_c-06"], doors_by_room["7a_c-06"]),
+ "7a_c-06b": Room("7a", "7a_c-06b", "The Summit A - Room c-06b", regions_by_room["7a_c-06b"], doors_by_room["7a_c-06b"]),
+ "7a_c-06c": Room("7a", "7a_c-06c", "The Summit A - Room c-06c", regions_by_room["7a_c-06c"], doors_by_room["7a_c-06c"]),
+ "7a_c-07": Room("7a", "7a_c-07", "The Summit A - Room c-07", regions_by_room["7a_c-07"], doors_by_room["7a_c-07"]),
+ "7a_c-07b": Room("7a", "7a_c-07b", "The Summit A - Room c-07b", regions_by_room["7a_c-07b"], doors_by_room["7a_c-07b"]),
+ "7a_c-08": Room("7a", "7a_c-08", "The Summit A - Room c-08", regions_by_room["7a_c-08"], doors_by_room["7a_c-08"]),
+ "7a_c-09": Room("7a", "7a_c-09", "The Summit A - Room c-09", regions_by_room["7a_c-09"], doors_by_room["7a_c-09"]),
+ "7a_d-00": Room("7a", "7a_d-00", "The Summit A - Room d-00", regions_by_room["7a_d-00"], doors_by_room["7a_d-00"], "1500 M", "7a_d-00_bottom"),
+ "7a_d-01": Room("7a", "7a_d-01", "The Summit A - Room d-01", regions_by_room["7a_d-01"], doors_by_room["7a_d-01"]),
+ "7a_d-01b": Room("7a", "7a_d-01b", "The Summit A - Room d-01b", regions_by_room["7a_d-01b"], doors_by_room["7a_d-01b"]),
+ "7a_d-01c": Room("7a", "7a_d-01c", "The Summit A - Room d-01c", regions_by_room["7a_d-01c"], doors_by_room["7a_d-01c"]),
+ "7a_d-01d": Room("7a", "7a_d-01d", "The Summit A - Room d-01d", regions_by_room["7a_d-01d"], doors_by_room["7a_d-01d"]),
+ "7a_d-02": Room("7a", "7a_d-02", "The Summit A - Room d-02", regions_by_room["7a_d-02"], doors_by_room["7a_d-02"]),
+ "7a_d-03": Room("7a", "7a_d-03", "The Summit A - Room d-03", regions_by_room["7a_d-03"], doors_by_room["7a_d-03"]),
+ "7a_d-03b": Room("7a", "7a_d-03b", "The Summit A - Room d-03b", regions_by_room["7a_d-03b"], doors_by_room["7a_d-03b"]),
+ "7a_d-04": Room("7a", "7a_d-04", "The Summit A - Room d-04", regions_by_room["7a_d-04"], doors_by_room["7a_d-04"]),
+ "7a_d-05": Room("7a", "7a_d-05", "The Summit A - Room d-05", regions_by_room["7a_d-05"], doors_by_room["7a_d-05"]),
+ "7a_d-05b": Room("7a", "7a_d-05b", "The Summit A - Room d-05b", regions_by_room["7a_d-05b"], doors_by_room["7a_d-05b"]),
+ "7a_d-06": Room("7a", "7a_d-06", "The Summit A - Room d-06", regions_by_room["7a_d-06"], doors_by_room["7a_d-06"]),
+ "7a_d-07": Room("7a", "7a_d-07", "The Summit A - Room d-07", regions_by_room["7a_d-07"], doors_by_room["7a_d-07"]),
+ "7a_d-08": Room("7a", "7a_d-08", "The Summit A - Room d-08", regions_by_room["7a_d-08"], doors_by_room["7a_d-08"]),
+ "7a_d-09": Room("7a", "7a_d-09", "The Summit A - Room d-09", regions_by_room["7a_d-09"], doors_by_room["7a_d-09"]),
+ "7a_d-10": Room("7a", "7a_d-10", "The Summit A - Room d-10", regions_by_room["7a_d-10"], doors_by_room["7a_d-10"]),
+ "7a_d-10b": Room("7a", "7a_d-10b", "The Summit A - Room d-10b", regions_by_room["7a_d-10b"], doors_by_room["7a_d-10b"]),
+ "7a_d-11": Room("7a", "7a_d-11", "The Summit A - Room d-11", regions_by_room["7a_d-11"], doors_by_room["7a_d-11"]),
+ "7a_e-00b": Room("7a", "7a_e-00b", "The Summit A - Room e-00b", regions_by_room["7a_e-00b"], doors_by_room["7a_e-00b"], "2000 M", "7a_e-00b_bottom"),
+ "7a_e-00": Room("7a", "7a_e-00", "The Summit A - Room e-00", regions_by_room["7a_e-00"], doors_by_room["7a_e-00"]),
+ "7a_e-01": Room("7a", "7a_e-01", "The Summit A - Room e-01", regions_by_room["7a_e-01"], doors_by_room["7a_e-01"]),
+ "7a_e-01b": Room("7a", "7a_e-01b", "The Summit A - Room e-01b", regions_by_room["7a_e-01b"], doors_by_room["7a_e-01b"]),
+ "7a_e-01c": Room("7a", "7a_e-01c", "The Summit A - Room e-01c", regions_by_room["7a_e-01c"], doors_by_room["7a_e-01c"]),
+ "7a_e-02": Room("7a", "7a_e-02", "The Summit A - Room e-02", regions_by_room["7a_e-02"], doors_by_room["7a_e-02"]),
+ "7a_e-03": Room("7a", "7a_e-03", "The Summit A - Room e-03", regions_by_room["7a_e-03"], doors_by_room["7a_e-03"]),
+ "7a_e-04": Room("7a", "7a_e-04", "The Summit A - Room e-04", regions_by_room["7a_e-04"], doors_by_room["7a_e-04"]),
+ "7a_e-05": Room("7a", "7a_e-05", "The Summit A - Room e-05", regions_by_room["7a_e-05"], doors_by_room["7a_e-05"]),
+ "7a_e-06": Room("7a", "7a_e-06", "The Summit A - Room e-06", regions_by_room["7a_e-06"], doors_by_room["7a_e-06"]),
+ "7a_e-07": Room("7a", "7a_e-07", "The Summit A - Room e-07", regions_by_room["7a_e-07"], doors_by_room["7a_e-07"]),
+ "7a_e-08": Room("7a", "7a_e-08", "The Summit A - Room e-08", regions_by_room["7a_e-08"], doors_by_room["7a_e-08"]),
+ "7a_e-09": Room("7a", "7a_e-09", "The Summit A - Room e-09", regions_by_room["7a_e-09"], doors_by_room["7a_e-09"]),
+ "7a_e-11": Room("7a", "7a_e-11", "The Summit A - Room e-11", regions_by_room["7a_e-11"], doors_by_room["7a_e-11"]),
+ "7a_e-12": Room("7a", "7a_e-12", "The Summit A - Room e-12", regions_by_room["7a_e-12"], doors_by_room["7a_e-12"]),
+ "7a_e-10": Room("7a", "7a_e-10", "The Summit A - Room e-10", regions_by_room["7a_e-10"], doors_by_room["7a_e-10"]),
+ "7a_e-10b": Room("7a", "7a_e-10b", "The Summit A - Room e-10b", regions_by_room["7a_e-10b"], doors_by_room["7a_e-10b"]),
+ "7a_e-13": Room("7a", "7a_e-13", "The Summit A - Room e-13", regions_by_room["7a_e-13"], doors_by_room["7a_e-13"]),
+ "7a_f-00": Room("7a", "7a_f-00", "The Summit A - Room f-00", regions_by_room["7a_f-00"], doors_by_room["7a_f-00"], "2500 M", "7a_f-00_south"),
+ "7a_f-01": Room("7a", "7a_f-01", "The Summit A - Room f-01", regions_by_room["7a_f-01"], doors_by_room["7a_f-01"]),
+ "7a_f-02": Room("7a", "7a_f-02", "The Summit A - Room f-02", regions_by_room["7a_f-02"], doors_by_room["7a_f-02"]),
+ "7a_f-02b": Room("7a", "7a_f-02b", "The Summit A - Room f-02b", regions_by_room["7a_f-02b"], doors_by_room["7a_f-02b"]),
+ "7a_f-04": Room("7a", "7a_f-04", "The Summit A - Room f-04", regions_by_room["7a_f-04"], doors_by_room["7a_f-04"]),
+ "7a_f-03": Room("7a", "7a_f-03", "The Summit A - Room f-03", regions_by_room["7a_f-03"], doors_by_room["7a_f-03"]),
+ "7a_f-05": Room("7a", "7a_f-05", "The Summit A - Room f-05", regions_by_room["7a_f-05"], doors_by_room["7a_f-05"]),
+ "7a_f-06": Room("7a", "7a_f-06", "The Summit A - Room f-06", regions_by_room["7a_f-06"], doors_by_room["7a_f-06"]),
+ "7a_f-07": Room("7a", "7a_f-07", "The Summit A - Room f-07", regions_by_room["7a_f-07"], doors_by_room["7a_f-07"]),
+ "7a_f-08": Room("7a", "7a_f-08", "The Summit A - Room f-08", regions_by_room["7a_f-08"], doors_by_room["7a_f-08"]),
+ "7a_f-08b": Room("7a", "7a_f-08b", "The Summit A - Room f-08b", regions_by_room["7a_f-08b"], doors_by_room["7a_f-08b"]),
+ "7a_f-08d": Room("7a", "7a_f-08d", "The Summit A - Room f-08d", regions_by_room["7a_f-08d"], doors_by_room["7a_f-08d"]),
+ "7a_f-08c": Room("7a", "7a_f-08c", "The Summit A - Room f-08c", regions_by_room["7a_f-08c"], doors_by_room["7a_f-08c"]),
+ "7a_f-09": Room("7a", "7a_f-09", "The Summit A - Room f-09", regions_by_room["7a_f-09"], doors_by_room["7a_f-09"]),
+ "7a_f-10": Room("7a", "7a_f-10", "The Summit A - Room f-10", regions_by_room["7a_f-10"], doors_by_room["7a_f-10"]),
+ "7a_f-10b": Room("7a", "7a_f-10b", "The Summit A - Room f-10b", regions_by_room["7a_f-10b"], doors_by_room["7a_f-10b"]),
+ "7a_f-11": Room("7a", "7a_f-11", "The Summit A - Room f-11", regions_by_room["7a_f-11"], doors_by_room["7a_f-11"]),
+ "7a_g-00": Room("7a", "7a_g-00", "The Summit A - Room g-00", regions_by_room["7a_g-00"], doors_by_room["7a_g-00"], "3000 M", "7a_g-00_bottom"),
+ "7a_g-00b": Room("7a", "7a_g-00b", "The Summit A - Room g-00b", regions_by_room["7a_g-00b"], doors_by_room["7a_g-00b"]),
+ "7a_g-01": Room("7a", "7a_g-01", "The Summit A - Room g-01", regions_by_room["7a_g-01"], doors_by_room["7a_g-01"]),
+ "7a_g-02": Room("7a", "7a_g-02", "The Summit A - Room g-02", regions_by_room["7a_g-02"], doors_by_room["7a_g-02"]),
+ "7a_g-03": Room("7a", "7a_g-03", "The Summit A - Room g-03", regions_by_room["7a_g-03"], doors_by_room["7a_g-03"]),
+
+ "7b_a-00": Room("7b", "7b_a-00", "The Summit B - Room a-00", regions_by_room["7b_a-00"], doors_by_room["7b_a-00"], "Start", "7b_a-00_west"),
+ "7b_a-01": Room("7b", "7b_a-01", "The Summit B - Room a-01", regions_by_room["7b_a-01"], doors_by_room["7b_a-01"]),
+ "7b_a-02": Room("7b", "7b_a-02", "The Summit B - Room a-02", regions_by_room["7b_a-02"], doors_by_room["7b_a-02"]),
+ "7b_a-03": Room("7b", "7b_a-03", "The Summit B - Room a-03", regions_by_room["7b_a-03"], doors_by_room["7b_a-03"]),
+ "7b_b-00": Room("7b", "7b_b-00", "The Summit B - Room b-00", regions_by_room["7b_b-00"], doors_by_room["7b_b-00"], "500 M", "7b_b-00_bottom"),
+ "7b_b-01": Room("7b", "7b_b-01", "The Summit B - Room b-01", regions_by_room["7b_b-01"], doors_by_room["7b_b-01"]),
+ "7b_b-02": Room("7b", "7b_b-02", "The Summit B - Room b-02", regions_by_room["7b_b-02"], doors_by_room["7b_b-02"]),
+ "7b_b-03": Room("7b", "7b_b-03", "The Summit B - Room b-03", regions_by_room["7b_b-03"], doors_by_room["7b_b-03"]),
+ "7b_c-01": Room("7b", "7b_c-01", "The Summit B - Room c-01", regions_by_room["7b_c-01"], doors_by_room["7b_c-01"], "1000 M", "7b_c-01_west"),
+ "7b_c-00": Room("7b", "7b_c-00", "The Summit B - Room c-00", regions_by_room["7b_c-00"], doors_by_room["7b_c-00"]),
+ "7b_c-02": Room("7b", "7b_c-02", "The Summit B - Room c-02", regions_by_room["7b_c-02"], doors_by_room["7b_c-02"]),
+ "7b_c-03": Room("7b", "7b_c-03", "The Summit B - Room c-03", regions_by_room["7b_c-03"], doors_by_room["7b_c-03"]),
+ "7b_d-00": Room("7b", "7b_d-00", "The Summit B - Room d-00", regions_by_room["7b_d-00"], doors_by_room["7b_d-00"], "1500 M", "7b_d-00_west"),
+ "7b_d-01": Room("7b", "7b_d-01", "The Summit B - Room d-01", regions_by_room["7b_d-01"], doors_by_room["7b_d-01"]),
+ "7b_d-02": Room("7b", "7b_d-02", "The Summit B - Room d-02", regions_by_room["7b_d-02"], doors_by_room["7b_d-02"]),
+ "7b_d-03": Room("7b", "7b_d-03", "The Summit B - Room d-03", regions_by_room["7b_d-03"], doors_by_room["7b_d-03"]),
+ "7b_e-00": Room("7b", "7b_e-00", "The Summit B - Room e-00", regions_by_room["7b_e-00"], doors_by_room["7b_e-00"], "2000 M", "7b_e-00_west"),
+ "7b_e-01": Room("7b", "7b_e-01", "The Summit B - Room e-01", regions_by_room["7b_e-01"], doors_by_room["7b_e-01"]),
+ "7b_e-02": Room("7b", "7b_e-02", "The Summit B - Room e-02", regions_by_room["7b_e-02"], doors_by_room["7b_e-02"]),
+ "7b_e-03": Room("7b", "7b_e-03", "The Summit B - Room e-03", regions_by_room["7b_e-03"], doors_by_room["7b_e-03"]),
+ "7b_f-00": Room("7b", "7b_f-00", "The Summit B - Room f-00", regions_by_room["7b_f-00"], doors_by_room["7b_f-00"], "2500 M", "7b_f-00_west"),
+ "7b_f-01": Room("7b", "7b_f-01", "The Summit B - Room f-01", regions_by_room["7b_f-01"], doors_by_room["7b_f-01"]),
+ "7b_f-02": Room("7b", "7b_f-02", "The Summit B - Room f-02", regions_by_room["7b_f-02"], doors_by_room["7b_f-02"]),
+ "7b_f-03": Room("7b", "7b_f-03", "The Summit B - Room f-03", regions_by_room["7b_f-03"], doors_by_room["7b_f-03"]),
+ "7b_g-00": Room("7b", "7b_g-00", "The Summit B - Room g-00", regions_by_room["7b_g-00"], doors_by_room["7b_g-00"], "3000 M", "7b_g-00_bottom"),
+ "7b_g-01": Room("7b", "7b_g-01", "The Summit B - Room g-01", regions_by_room["7b_g-01"], doors_by_room["7b_g-01"]),
+ "7b_g-02": Room("7b", "7b_g-02", "The Summit B - Room g-02", regions_by_room["7b_g-02"], doors_by_room["7b_g-02"]),
+ "7b_g-03": Room("7b", "7b_g-03", "The Summit B - Room g-03", regions_by_room["7b_g-03"], doors_by_room["7b_g-03"]),
+
+ "7c_01": Room("7c", "7c_01", "The Summit C - Room 01", regions_by_room["7c_01"], doors_by_room["7c_01"], "Start", "7c_01_west"),
+ "7c_02": Room("7c", "7c_02", "The Summit C - Room 02", regions_by_room["7c_02"], doors_by_room["7c_02"]),
+ "7c_03": Room("7c", "7c_03", "The Summit C - Room 03", regions_by_room["7c_03"], doors_by_room["7c_03"]),
+
+ "8a_outside": Room("8a", "8a_outside", "Epilogue - Room outside", regions_by_room["8a_outside"], doors_by_room["8a_outside"], "Start", "8a_outside_east"),
+ "8a_bridge": Room("8a", "8a_bridge", "Epilogue - Room bridge", regions_by_room["8a_bridge"], doors_by_room["8a_bridge"]),
+ "8a_secret": Room("8a", "8a_secret", "Epilogue - Room secret", regions_by_room["8a_secret"], doors_by_room["8a_secret"]),
+
+ "9a_00": Room("9a", "9a_00", "Core A - Room 00", regions_by_room["9a_00"], doors_by_room["9a_00"], "Start", "9a_00_west"),
+ "9a_0x": Room("9a", "9a_0x", "Core A - Room 0x", regions_by_room["9a_0x"], doors_by_room["9a_0x"]),
+ "9a_01": Room("9a", "9a_01", "Core A - Room 01", regions_by_room["9a_01"], doors_by_room["9a_01"]),
+ "9a_02": Room("9a", "9a_02", "Core A - Room 02", regions_by_room["9a_02"], doors_by_room["9a_02"]),
+ "9a_a-00": Room("9a", "9a_a-00", "Core A - Room a-00", regions_by_room["9a_a-00"], doors_by_room["9a_a-00"], "Into the Core", "9a_a-00_west"),
+ "9a_a-01": Room("9a", "9a_a-01", "Core A - Room a-01", regions_by_room["9a_a-01"], doors_by_room["9a_a-01"]),
+ "9a_a-02": Room("9a", "9a_a-02", "Core A - Room a-02", regions_by_room["9a_a-02"], doors_by_room["9a_a-02"]),
+ "9a_a-03": Room("9a", "9a_a-03", "Core A - Room a-03", regions_by_room["9a_a-03"], doors_by_room["9a_a-03"]),
+ "9a_b-00": Room("9a", "9a_b-00", "Core A - Room b-00", regions_by_room["9a_b-00"], doors_by_room["9a_b-00"]),
+ "9a_b-01": Room("9a", "9a_b-01", "Core A - Room b-01", regions_by_room["9a_b-01"], doors_by_room["9a_b-01"]),
+ "9a_b-02": Room("9a", "9a_b-02", "Core A - Room b-02", regions_by_room["9a_b-02"], doors_by_room["9a_b-02"]),
+ "9a_b-03": Room("9a", "9a_b-03", "Core A - Room b-03", regions_by_room["9a_b-03"], doors_by_room["9a_b-03"]),
+ "9a_b-04": Room("9a", "9a_b-04", "Core A - Room b-04", regions_by_room["9a_b-04"], doors_by_room["9a_b-04"]),
+ "9a_b-05": Room("9a", "9a_b-05", "Core A - Room b-05", regions_by_room["9a_b-05"], doors_by_room["9a_b-05"]),
+ "9a_b-06": Room("9a", "9a_b-06", "Core A - Room b-06", regions_by_room["9a_b-06"], doors_by_room["9a_b-06"]),
+ "9a_b-07b": Room("9a", "9a_b-07b", "Core A - Room b-07b", regions_by_room["9a_b-07b"], doors_by_room["9a_b-07b"]),
+ "9a_b-07": Room("9a", "9a_b-07", "Core A - Room b-07", regions_by_room["9a_b-07"], doors_by_room["9a_b-07"]),
+ "9a_c-00": Room("9a", "9a_c-00", "Core A - Room c-00", regions_by_room["9a_c-00"], doors_by_room["9a_c-00"], "Hot and Cold", "9a_c-00_west"),
+ "9a_c-00b": Room("9a", "9a_c-00b", "Core A - Room c-00b", regions_by_room["9a_c-00b"], doors_by_room["9a_c-00b"]),
+ "9a_c-01": Room("9a", "9a_c-01", "Core A - Room c-01", regions_by_room["9a_c-01"], doors_by_room["9a_c-01"]),
+ "9a_c-02": Room("9a", "9a_c-02", "Core A - Room c-02", regions_by_room["9a_c-02"], doors_by_room["9a_c-02"]),
+ "9a_c-03": Room("9a", "9a_c-03", "Core A - Room c-03", regions_by_room["9a_c-03"], doors_by_room["9a_c-03"]),
+ "9a_c-03b": Room("9a", "9a_c-03b", "Core A - Room c-03b", regions_by_room["9a_c-03b"], doors_by_room["9a_c-03b"]),
+ "9a_c-04": Room("9a", "9a_c-04", "Core A - Room c-04", regions_by_room["9a_c-04"], doors_by_room["9a_c-04"]),
+ "9a_d-00": Room("9a", "9a_d-00", "Core A - Room d-00", regions_by_room["9a_d-00"], doors_by_room["9a_d-00"], "Heart of the Mountain", "9a_d-00_bottom"),
+ "9a_d-01": Room("9a", "9a_d-01", "Core A - Room d-01", regions_by_room["9a_d-01"], doors_by_room["9a_d-01"]),
+ "9a_d-02": Room("9a", "9a_d-02", "Core A - Room d-02", regions_by_room["9a_d-02"], doors_by_room["9a_d-02"]),
+ "9a_d-03": Room("9a", "9a_d-03", "Core A - Room d-03", regions_by_room["9a_d-03"], doors_by_room["9a_d-03"]),
+ "9a_d-04": Room("9a", "9a_d-04", "Core A - Room d-04", regions_by_room["9a_d-04"], doors_by_room["9a_d-04"]),
+ "9a_d-05": Room("9a", "9a_d-05", "Core A - Room d-05", regions_by_room["9a_d-05"], doors_by_room["9a_d-05"]),
+ "9a_d-06": Room("9a", "9a_d-06", "Core A - Room d-06", regions_by_room["9a_d-06"], doors_by_room["9a_d-06"]),
+ "9a_d-07": Room("9a", "9a_d-07", "Core A - Room d-07", regions_by_room["9a_d-07"], doors_by_room["9a_d-07"]),
+ "9a_d-08": Room("9a", "9a_d-08", "Core A - Room d-08", regions_by_room["9a_d-08"], doors_by_room["9a_d-08"]),
+ "9a_d-09": Room("9a", "9a_d-09", "Core A - Room d-09", regions_by_room["9a_d-09"], doors_by_room["9a_d-09"]),
+ "9a_d-10": Room("9a", "9a_d-10", "Core A - Room d-10", regions_by_room["9a_d-10"], doors_by_room["9a_d-10"]),
+ "9a_d-10b": Room("9a", "9a_d-10b", "Core A - Room d-10b", regions_by_room["9a_d-10b"], doors_by_room["9a_d-10b"]),
+ "9a_d-10c": Room("9a", "9a_d-10c", "Core A - Room d-10c", regions_by_room["9a_d-10c"], doors_by_room["9a_d-10c"]),
+ "9a_d-11": Room("9a", "9a_d-11", "Core A - Room d-11", regions_by_room["9a_d-11"], doors_by_room["9a_d-11"]),
+ "9a_space": Room("9a", "9a_space", "Core A - Room space", regions_by_room["9a_space"], doors_by_room["9a_space"]),
+
+ "9b_00": Room("9b", "9b_00", "Core B - Room 00", regions_by_room["9b_00"], doors_by_room["9b_00"], "Start", "9b_00_east"),
+ "9b_01": Room("9b", "9b_01", "Core B - Room 01", regions_by_room["9b_01"], doors_by_room["9b_01"]),
+ "9b_a-00": Room("9b", "9b_a-00", "Core B - Room a-00", regions_by_room["9b_a-00"], doors_by_room["9b_a-00"], "Into the Core", "9b_a-00_west"),
+ "9b_a-01": Room("9b", "9b_a-01", "Core B - Room a-01", regions_by_room["9b_a-01"], doors_by_room["9b_a-01"]),
+ "9b_a-02": Room("9b", "9b_a-02", "Core B - Room a-02", regions_by_room["9b_a-02"], doors_by_room["9b_a-02"]),
+ "9b_a-03": Room("9b", "9b_a-03", "Core B - Room a-03", regions_by_room["9b_a-03"], doors_by_room["9b_a-03"]),
+ "9b_a-04": Room("9b", "9b_a-04", "Core B - Room a-04", regions_by_room["9b_a-04"], doors_by_room["9b_a-04"]),
+ "9b_a-05": Room("9b", "9b_a-05", "Core B - Room a-05", regions_by_room["9b_a-05"], doors_by_room["9b_a-05"]),
+ "9b_b-00": Room("9b", "9b_b-00", "Core B - Room b-00", regions_by_room["9b_b-00"], doors_by_room["9b_b-00"], "Burning or Freezing", "9b_b-00_west"),
+ "9b_b-01": Room("9b", "9b_b-01", "Core B - Room b-01", regions_by_room["9b_b-01"], doors_by_room["9b_b-01"]),
+ "9b_b-02": Room("9b", "9b_b-02", "Core B - Room b-02", regions_by_room["9b_b-02"], doors_by_room["9b_b-02"]),
+ "9b_b-03": Room("9b", "9b_b-03", "Core B - Room b-03", regions_by_room["9b_b-03"], doors_by_room["9b_b-03"]),
+ "9b_b-04": Room("9b", "9b_b-04", "Core B - Room b-04", regions_by_room["9b_b-04"], doors_by_room["9b_b-04"]),
+ "9b_b-05": Room("9b", "9b_b-05", "Core B - Room b-05", regions_by_room["9b_b-05"], doors_by_room["9b_b-05"]),
+ "9b_c-01": Room("9b", "9b_c-01", "Core B - Room c-01", regions_by_room["9b_c-01"], doors_by_room["9b_c-01"], "Heartbeat", "9b_c-01_bottom"),
+ "9b_c-02": Room("9b", "9b_c-02", "Core B - Room c-02", regions_by_room["9b_c-02"], doors_by_room["9b_c-02"]),
+ "9b_c-03": Room("9b", "9b_c-03", "Core B - Room c-03", regions_by_room["9b_c-03"], doors_by_room["9b_c-03"]),
+ "9b_c-04": Room("9b", "9b_c-04", "Core B - Room c-04", regions_by_room["9b_c-04"], doors_by_room["9b_c-04"]),
+ "9b_c-05": Room("9b", "9b_c-05", "Core B - Room c-05", regions_by_room["9b_c-05"], doors_by_room["9b_c-05"]),
+ "9b_c-06": Room("9b", "9b_c-06", "Core B - Room c-06", regions_by_room["9b_c-06"], doors_by_room["9b_c-06"]),
+ "9b_c-08": Room("9b", "9b_c-08", "Core B - Room c-08", regions_by_room["9b_c-08"], doors_by_room["9b_c-08"]),
+ "9b_c-07": Room("9b", "9b_c-07", "Core B - Room c-07", regions_by_room["9b_c-07"], doors_by_room["9b_c-07"]),
+ "9b_space": Room("9b", "9b_space", "Core B - Room space", regions_by_room["9b_space"], doors_by_room["9b_space"]),
+
+ "9c_intro": Room("9c", "9c_intro", "Core C - Room intro", regions_by_room["9c_intro"], doors_by_room["9c_intro"], "Start", "9c_intro_west"),
+ "9c_00": Room("9c", "9c_00", "Core C - Room 00", regions_by_room["9c_00"], doors_by_room["9c_00"]),
+ "9c_01": Room("9c", "9c_01", "Core C - Room 01", regions_by_room["9c_01"], doors_by_room["9c_01"]),
+ "9c_02": Room("9c", "9c_02", "Core C - Room 02", regions_by_room["9c_02"], doors_by_room["9c_02"]),
+
+ "10a_intro-00-past": Room("10a", "10a_intro-00-past", "Farewell - Room intro-00-past", regions_by_room["10a_intro-00-past"], doors_by_room["10a_intro-00-past"], "Start", "10a_intro-00-past_west"),
+ "10a_intro-01-future": Room("10a", "10a_intro-01-future", "Farewell - Room intro-01-future", regions_by_room["10a_intro-01-future"], doors_by_room["10a_intro-01-future"]),
+ "10a_intro-02-launch": Room("10a", "10a_intro-02-launch", "Farewell - Room intro-02-launch", regions_by_room["10a_intro-02-launch"], doors_by_room["10a_intro-02-launch"]),
+ "10a_intro-03-space": Room("10a", "10a_intro-03-space", "Farewell - Room intro-03-space", regions_by_room["10a_intro-03-space"], doors_by_room["10a_intro-03-space"]),
+ "10a_a-00": Room("10a", "10a_a-00", "Farewell - Room a-00", regions_by_room["10a_a-00"], doors_by_room["10a_a-00"], "Singular", "10a_a-00_west"),
+ "10a_a-01": Room("10a", "10a_a-01", "Farewell - Room a-01", regions_by_room["10a_a-01"], doors_by_room["10a_a-01"]),
+ "10a_a-02": Room("10a", "10a_a-02", "Farewell - Room a-02", regions_by_room["10a_a-02"], doors_by_room["10a_a-02"]),
+ "10a_a-03": Room("10a", "10a_a-03", "Farewell - Room a-03", regions_by_room["10a_a-03"], doors_by_room["10a_a-03"]),
+ "10a_a-04": Room("10a", "10a_a-04", "Farewell - Room a-04", regions_by_room["10a_a-04"], doors_by_room["10a_a-04"]),
+ "10a_a-05": Room("10a", "10a_a-05", "Farewell - Room a-05", regions_by_room["10a_a-05"], doors_by_room["10a_a-05"]),
+ "10a_b-00": Room("10a", "10a_b-00", "Farewell - Room b-00", regions_by_room["10a_b-00"], doors_by_room["10a_b-00"]),
+ "10a_b-01": Room("10a", "10a_b-01", "Farewell - Room b-01", regions_by_room["10a_b-01"], doors_by_room["10a_b-01"]),
+ "10a_b-02": Room("10a", "10a_b-02", "Farewell - Room b-02", regions_by_room["10a_b-02"], doors_by_room["10a_b-02"]),
+ "10a_b-03": Room("10a", "10a_b-03", "Farewell - Room b-03", regions_by_room["10a_b-03"], doors_by_room["10a_b-03"]),
+ "10a_b-04": Room("10a", "10a_b-04", "Farewell - Room b-04", regions_by_room["10a_b-04"], doors_by_room["10a_b-04"]),
+ "10a_b-05": Room("10a", "10a_b-05", "Farewell - Room b-05", regions_by_room["10a_b-05"], doors_by_room["10a_b-05"]),
+ "10a_b-06": Room("10a", "10a_b-06", "Farewell - Room b-06", regions_by_room["10a_b-06"], doors_by_room["10a_b-06"]),
+ "10a_b-07": Room("10a", "10a_b-07", "Farewell - Room b-07", regions_by_room["10a_b-07"], doors_by_room["10a_b-07"]),
+ "10a_c-00": Room("10a", "10a_c-00", "Farewell - Room c-00", regions_by_room["10a_c-00"], doors_by_room["10a_c-00"], "Power Source", "10a_c-00_west"),
+ "10a_c-00b": Room("10a", "10a_c-00b", "Farewell - Room c-00b", regions_by_room["10a_c-00b"], doors_by_room["10a_c-00b"]),
+ "10a_c-01": Room("10a", "10a_c-01", "Farewell - Room c-01", regions_by_room["10a_c-01"], doors_by_room["10a_c-01"]),
+ "10a_c-02": Room("10a", "10a_c-02", "Farewell - Room c-02", regions_by_room["10a_c-02"], doors_by_room["10a_c-02"]),
+ "10a_c-alt-00": Room("10a", "10a_c-alt-00", "Farewell - Room c-alt-00", regions_by_room["10a_c-alt-00"], doors_by_room["10a_c-alt-00"]),
+ "10a_c-alt-01": Room("10a", "10a_c-alt-01", "Farewell - Room c-alt-01", regions_by_room["10a_c-alt-01"], doors_by_room["10a_c-alt-01"]),
+ "10a_c-03": Room("10a", "10a_c-03", "Farewell - Room c-03", regions_by_room["10a_c-03"], doors_by_room["10a_c-03"]),
+ "10a_d-00": Room("10a", "10a_d-00", "Farewell - Room d-00", regions_by_room["10a_d-00"], doors_by_room["10a_d-00"]),
+ "10a_d-04": Room("10a", "10a_d-04", "Farewell - Room d-04", regions_by_room["10a_d-04"], doors_by_room["10a_d-04"]),
+ "10a_d-03": Room("10a", "10a_d-03", "Farewell - Room d-03", regions_by_room["10a_d-03"], doors_by_room["10a_d-03"]),
+ "10a_d-01": Room("10a", "10a_d-01", "Farewell - Room d-01", regions_by_room["10a_d-01"], doors_by_room["10a_d-01"]),
+ "10a_d-02": Room("10a", "10a_d-02", "Farewell - Room d-02", regions_by_room["10a_d-02"], doors_by_room["10a_d-02"]),
+ "10a_d-05": Room("10a", "10a_d-05", "Farewell - Room d-05", regions_by_room["10a_d-05"], doors_by_room["10a_d-05"]),
+ "10a_e-00y": Room("10a", "10a_e-00y", "Farewell - Room e-00y", regions_by_room["10a_e-00y"], doors_by_room["10a_e-00y"]),
+ "10a_e-00yb": Room("10a", "10a_e-00yb", "Farewell - Room e-00yb", regions_by_room["10a_e-00yb"], doors_by_room["10a_e-00yb"]),
+ "10a_e-00z": Room("10a", "10a_e-00z", "Farewell - Room e-00z", regions_by_room["10a_e-00z"], doors_by_room["10a_e-00z"], "Remembered", "10a_e-00z_south"),
+ "10a_e-00": Room("10a", "10a_e-00", "Farewell - Room e-00", regions_by_room["10a_e-00"], doors_by_room["10a_e-00"]),
+ "10a_e-00b": Room("10a", "10a_e-00b", "Farewell - Room e-00b", regions_by_room["10a_e-00b"], doors_by_room["10a_e-00b"]),
+ "10a_e-01": Room("10a", "10a_e-01", "Farewell - Room e-01", regions_by_room["10a_e-01"], doors_by_room["10a_e-01"]),
+ "10a_e-02": Room("10a", "10a_e-02", "Farewell - Room e-02", regions_by_room["10a_e-02"], doors_by_room["10a_e-02"]),
+ "10a_e-03": Room("10a", "10a_e-03", "Farewell - Room e-03", regions_by_room["10a_e-03"], doors_by_room["10a_e-03"]),
+ "10a_e-04": Room("10a", "10a_e-04", "Farewell - Room e-04", regions_by_room["10a_e-04"], doors_by_room["10a_e-04"]),
+ "10a_e-05": Room("10a", "10a_e-05", "Farewell - Room e-05", regions_by_room["10a_e-05"], doors_by_room["10a_e-05"]),
+ "10a_e-05b": Room("10a", "10a_e-05b", "Farewell - Room e-05b", regions_by_room["10a_e-05b"], doors_by_room["10a_e-05b"]),
+ "10a_e-05c": Room("10a", "10a_e-05c", "Farewell - Room e-05c", regions_by_room["10a_e-05c"], doors_by_room["10a_e-05c"]),
+ "10a_e-06": Room("10a", "10a_e-06", "Farewell - Room e-06", regions_by_room["10a_e-06"], doors_by_room["10a_e-06"]),
+ "10a_e-07": Room("10a", "10a_e-07", "Farewell - Room e-07", regions_by_room["10a_e-07"], doors_by_room["10a_e-07"]),
+ "10a_e-08": Room("10a", "10a_e-08", "Farewell - Room e-08", regions_by_room["10a_e-08"], doors_by_room["10a_e-08"]),
+
+ "10b_f-door": Room("10b", "10b_f-door", "Farewell - Room f-door", regions_by_room["10b_f-door"], doors_by_room["10b_f-door"], "Event Horizon", "10b_f-door_west"),
+ "10b_f-00": Room("10b", "10b_f-00", "Farewell - Room f-00", regions_by_room["10b_f-00"], doors_by_room["10b_f-00"]),
+ "10b_f-01": Room("10b", "10b_f-01", "Farewell - Room f-01", regions_by_room["10b_f-01"], doors_by_room["10b_f-01"]),
+ "10b_f-02": Room("10b", "10b_f-02", "Farewell - Room f-02", regions_by_room["10b_f-02"], doors_by_room["10b_f-02"]),
+ "10b_f-03": Room("10b", "10b_f-03", "Farewell - Room f-03", regions_by_room["10b_f-03"], doors_by_room["10b_f-03"]),
+ "10b_f-04": Room("10b", "10b_f-04", "Farewell - Room f-04", regions_by_room["10b_f-04"], doors_by_room["10b_f-04"]),
+ "10b_f-05": Room("10b", "10b_f-05", "Farewell - Room f-05", regions_by_room["10b_f-05"], doors_by_room["10b_f-05"]),
+ "10b_f-06": Room("10b", "10b_f-06", "Farewell - Room f-06", regions_by_room["10b_f-06"], doors_by_room["10b_f-06"]),
+ "10b_f-07": Room("10b", "10b_f-07", "Farewell - Room f-07", regions_by_room["10b_f-07"], doors_by_room["10b_f-07"]),
+ "10b_f-08": Room("10b", "10b_f-08", "Farewell - Room f-08", regions_by_room["10b_f-08"], doors_by_room["10b_f-08"]),
+ "10b_f-09": Room("10b", "10b_f-09", "Farewell - Room f-09", regions_by_room["10b_f-09"], doors_by_room["10b_f-09"]),
+ "10b_g-00": Room("10b", "10b_g-00", "Farewell - Room g-00", regions_by_room["10b_g-00"], doors_by_room["10b_g-00"]),
+ "10b_g-01": Room("10b", "10b_g-01", "Farewell - Room g-01", regions_by_room["10b_g-01"], doors_by_room["10b_g-01"]),
+ "10b_g-03": Room("10b", "10b_g-03", "Farewell - Room g-03", regions_by_room["10b_g-03"], doors_by_room["10b_g-03"]),
+ "10b_g-02": Room("10b", "10b_g-02", "Farewell - Room g-02", regions_by_room["10b_g-02"], doors_by_room["10b_g-02"]),
+ "10b_g-04": Room("10b", "10b_g-04", "Farewell - Room g-04", regions_by_room["10b_g-04"], doors_by_room["10b_g-04"]),
+ "10b_g-05": Room("10b", "10b_g-05", "Farewell - Room g-05", regions_by_room["10b_g-05"], doors_by_room["10b_g-05"]),
+ "10b_g-06": Room("10b", "10b_g-06", "Farewell - Room g-06", regions_by_room["10b_g-06"], doors_by_room["10b_g-06"]),
+ "10b_h-00b": Room("10b", "10b_h-00b", "Farewell - Room h-00b", regions_by_room["10b_h-00b"], doors_by_room["10b_h-00b"], "Determination", "10b_h-00b_west"),
+ "10b_h-00": Room("10b", "10b_h-00", "Farewell - Room h-00", regions_by_room["10b_h-00"], doors_by_room["10b_h-00"]),
+ "10b_h-01": Room("10b", "10b_h-01", "Farewell - Room h-01", regions_by_room["10b_h-01"], doors_by_room["10b_h-01"]),
+ "10b_h-02": Room("10b", "10b_h-02", "Farewell - Room h-02", regions_by_room["10b_h-02"], doors_by_room["10b_h-02"]),
+ "10b_h-03": Room("10b", "10b_h-03", "Farewell - Room h-03", regions_by_room["10b_h-03"], doors_by_room["10b_h-03"]),
+ "10b_h-03b": Room("10b", "10b_h-03b", "Farewell - Room h-03b", regions_by_room["10b_h-03b"], doors_by_room["10b_h-03b"]),
+ "10b_h-04": Room("10b", "10b_h-04", "Farewell - Room h-04", regions_by_room["10b_h-04"], doors_by_room["10b_h-04"]),
+ "10b_h-04b": Room("10b", "10b_h-04b", "Farewell - Room h-04b", regions_by_room["10b_h-04b"], doors_by_room["10b_h-04b"]),
+ "10b_h-05": Room("10b", "10b_h-05", "Farewell - Room h-05", regions_by_room["10b_h-05"], doors_by_room["10b_h-05"]),
+ "10b_h-06": Room("10b", "10b_h-06", "Farewell - Room h-06", regions_by_room["10b_h-06"], doors_by_room["10b_h-06"]),
+ "10b_h-06b": Room("10b", "10b_h-06b", "Farewell - Room h-06b", regions_by_room["10b_h-06b"], doors_by_room["10b_h-06b"]),
+ "10b_h-07": Room("10b", "10b_h-07", "Farewell - Room h-07", regions_by_room["10b_h-07"], doors_by_room["10b_h-07"]),
+ "10b_h-08": Room("10b", "10b_h-08", "Farewell - Room h-08", regions_by_room["10b_h-08"], doors_by_room["10b_h-08"]),
+ "10b_h-09": Room("10b", "10b_h-09", "Farewell - Room h-09", regions_by_room["10b_h-09"], doors_by_room["10b_h-09"]),
+ "10b_h-10": Room("10b", "10b_h-10", "Farewell - Room h-10", regions_by_room["10b_h-10"], doors_by_room["10b_h-10"]),
+ "10b_i-00": Room("10b", "10b_i-00", "Farewell - Room i-00", regions_by_room["10b_i-00"], doors_by_room["10b_i-00"], "Stubbornness", "10b_i-00_west"),
+ "10b_i-00b": Room("10b", "10b_i-00b", "Farewell - Room i-00b", regions_by_room["10b_i-00b"], doors_by_room["10b_i-00b"]),
+ "10b_i-01": Room("10b", "10b_i-01", "Farewell - Room i-01", regions_by_room["10b_i-01"], doors_by_room["10b_i-01"]),
+ "10b_i-02": Room("10b", "10b_i-02", "Farewell - Room i-02", regions_by_room["10b_i-02"], doors_by_room["10b_i-02"]),
+ "10b_i-03": Room("10b", "10b_i-03", "Farewell - Room i-03", regions_by_room["10b_i-03"], doors_by_room["10b_i-03"]),
+ "10b_i-04": Room("10b", "10b_i-04", "Farewell - Room i-04", regions_by_room["10b_i-04"], doors_by_room["10b_i-04"]),
+ "10b_i-05": Room("10b", "10b_i-05", "Farewell - Room i-05", regions_by_room["10b_i-05"], doors_by_room["10b_i-05"]),
+ "10b_j-00": Room("10b", "10b_j-00", "Farewell - Room j-00", regions_by_room["10b_j-00"], doors_by_room["10b_j-00"], "Reconciliation", "10b_j-00_west"),
+ "10b_j-00b": Room("10b", "10b_j-00b", "Farewell - Room j-00b", regions_by_room["10b_j-00b"], doors_by_room["10b_j-00b"]),
+ "10b_j-01": Room("10b", "10b_j-01", "Farewell - Room j-01", regions_by_room["10b_j-01"], doors_by_room["10b_j-01"]),
+ "10b_j-02": Room("10b", "10b_j-02", "Farewell - Room j-02", regions_by_room["10b_j-02"], doors_by_room["10b_j-02"]),
+ "10b_j-03": Room("10b", "10b_j-03", "Farewell - Room j-03", regions_by_room["10b_j-03"], doors_by_room["10b_j-03"]),
+ "10b_j-04": Room("10b", "10b_j-04", "Farewell - Room j-04", regions_by_room["10b_j-04"], doors_by_room["10b_j-04"]),
+ "10b_j-05": Room("10b", "10b_j-05", "Farewell - Room j-05", regions_by_room["10b_j-05"], doors_by_room["10b_j-05"]),
+ "10b_j-06": Room("10b", "10b_j-06", "Farewell - Room j-06", regions_by_room["10b_j-06"], doors_by_room["10b_j-06"]),
+ "10b_j-07": Room("10b", "10b_j-07", "Farewell - Room j-07", regions_by_room["10b_j-07"], doors_by_room["10b_j-07"]),
+ "10b_j-08": Room("10b", "10b_j-08", "Farewell - Room j-08", regions_by_room["10b_j-08"], doors_by_room["10b_j-08"]),
+ "10b_j-09": Room("10b", "10b_j-09", "Farewell - Room j-09", regions_by_room["10b_j-09"], doors_by_room["10b_j-09"]),
+ "10b_j-10": Room("10b", "10b_j-10", "Farewell - Room j-10", regions_by_room["10b_j-10"], doors_by_room["10b_j-10"]),
+ "10b_j-11": Room("10b", "10b_j-11", "Farewell - Room j-11", regions_by_room["10b_j-11"], doors_by_room["10b_j-11"]),
+ "10b_j-12": Room("10b", "10b_j-12", "Farewell - Room j-12", regions_by_room["10b_j-12"], doors_by_room["10b_j-12"]),
+ "10b_j-13": Room("10b", "10b_j-13", "Farewell - Room j-13", regions_by_room["10b_j-13"], doors_by_room["10b_j-13"]),
+ "10b_j-14": Room("10b", "10b_j-14", "Farewell - Room j-14", regions_by_room["10b_j-14"], doors_by_room["10b_j-14"]),
+ "10b_j-14b": Room("10b", "10b_j-14b", "Farewell - Room j-14b", regions_by_room["10b_j-14b"], doors_by_room["10b_j-14b"]),
+ "10b_j-15": Room("10b", "10b_j-15", "Farewell - Room j-15", regions_by_room["10b_j-15"], doors_by_room["10b_j-15"]),
+ "10b_j-16": Room("10b", "10b_j-16", "Farewell - Room j-16", regions_by_room["10b_j-16"], doors_by_room["10b_j-16"], "Farewell", "10b_j-16_west"),
+ "10b_j-17": Room("10b", "10b_j-17", "Farewell - Room j-17", regions_by_room["10b_j-17"], doors_by_room["10b_j-17"]),
+ "10b_j-18": Room("10b", "10b_j-18", "Farewell - Room j-18", regions_by_room["10b_j-18"], doors_by_room["10b_j-18"]),
+ "10b_j-19": Room("10b", "10b_j-19", "Farewell - Room j-19", regions_by_room["10b_j-19"], doors_by_room["10b_j-19"]),
+ "10b_GOAL": Room("10b", "10b_GOAL", "Farewell - Room GOAL", regions_by_room["10b_GOAL"], doors_by_room["10b_GOAL"]),
+
+ "10c_end-golden": Room("10c", "10c_end-golden", "Farewell - Room end-golden", regions_by_room["10c_end-golden"], doors_by_room["10c_end-golden"]),
}
+rooms_by_level: defaultdict[str, list[Room]] = defaultdict(lambda: [])
+room_cons_by_level: defaultdict[str, list[RoomConnection]] = defaultdict(lambda: [])
+
+for _, room in all_rooms.items():
+ rooms_by_level[room.level_name].append(room)
+
+for _, room_con in all_room_connections.items():
+ room_cons_by_level[room_con.level_name].append(room_con)
+
all_levels: dict[str, Level] = {
- "0a": Level("0a", "Prologue", [room for _, room in all_rooms.items() if room.level_name == "0a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "0a"]),
- "1a": Level("1a", "Forsaken City A", [room for _, room in all_rooms.items() if room.level_name == "1a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "1a"]),
- "1b": Level("1b", "Forsaken City B", [room for _, room in all_rooms.items() if room.level_name == "1b"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "1b"]),
- "1c": Level("1c", "Forsaken City C", [room for _, room in all_rooms.items() if room.level_name == "1c"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "1c"]),
- "2a": Level("2a", "Old Site A", [room for _, room in all_rooms.items() if room.level_name == "2a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "2a"]),
- "2b": Level("2b", "Old Site B", [room for _, room in all_rooms.items() if room.level_name == "2b"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "2b"]),
- "2c": Level("2c", "Old Site C", [room for _, room in all_rooms.items() if room.level_name == "2c"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "2c"]),
- "3a": Level("3a", "Celestial Resort A", [room for _, room in all_rooms.items() if room.level_name == "3a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "3a"]),
- "3b": Level("3b", "Celestial Resort B", [room for _, room in all_rooms.items() if room.level_name == "3b"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "3b"]),
- "3c": Level("3c", "Celestial Resort C", [room for _, room in all_rooms.items() if room.level_name == "3c"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "3c"]),
- "4a": Level("4a", "Golden Ridge A", [room for _, room in all_rooms.items() if room.level_name == "4a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "4a"]),
- "4b": Level("4b", "Golden Ridge B", [room for _, room in all_rooms.items() if room.level_name == "4b"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "4b"]),
- "4c": Level("4c", "Golden Ridge C", [room for _, room in all_rooms.items() if room.level_name == "4c"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "4c"]),
- "5a": Level("5a", "Mirror Temple A", [room for _, room in all_rooms.items() if room.level_name == "5a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "5a"]),
- "5b": Level("5b", "Mirror Temple B", [room for _, room in all_rooms.items() if room.level_name == "5b"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "5b"]),
- "5c": Level("5c", "Mirror Temple C", [room for _, room in all_rooms.items() if room.level_name == "5c"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "5c"]),
- "6a": Level("6a", "Reflection A", [room for _, room in all_rooms.items() if room.level_name == "6a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "6a"]),
- "6b": Level("6b", "Reflection B", [room for _, room in all_rooms.items() if room.level_name == "6b"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "6b"]),
- "6c": Level("6c", "Reflection C", [room for _, room in all_rooms.items() if room.level_name == "6c"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "6c"]),
- "7a": Level("7a", "The Summit A", [room for _, room in all_rooms.items() if room.level_name == "7a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "7a"]),
- "7b": Level("7b", "The Summit B", [room for _, room in all_rooms.items() if room.level_name == "7b"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "7b"]),
- "7c": Level("7c", "The Summit C", [room for _, room in all_rooms.items() if room.level_name == "7c"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "7c"]),
- "8a": Level("8a", "Epilogue", [room for _, room in all_rooms.items() if room.level_name == "8a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "8a"]),
- "9a": Level("9a", "Core A", [room for _, room in all_rooms.items() if room.level_name == "9a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "9a"]),
- "9b": Level("9b", "Core B", [room for _, room in all_rooms.items() if room.level_name == "9b"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "9b"]),
- "9c": Level("9c", "Core C", [room for _, room in all_rooms.items() if room.level_name == "9c"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "9c"]),
- "10a": Level("10a", "Farewell", [room for _, room in all_rooms.items() if room.level_name == "10a"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "10a"]),
- "10b": Level("10b", "Farewell", [room for _, room in all_rooms.items() if room.level_name == "10b"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "10b"]),
- "10c": Level("10c", "Farewell", [room for _, room in all_rooms.items() if room.level_name == "10c"], [room_con for _, room_con in all_room_connections.items() if room_con.level_name == "10c"]),
+ "0a": Level("0a", "Prologue", rooms_by_level["0a"], room_cons_by_level["0a"]),
+ "1a": Level("1a", "Forsaken City A", rooms_by_level["1a"], room_cons_by_level["1a"]),
+ "1b": Level("1b", "Forsaken City B", rooms_by_level["1b"], room_cons_by_level["1b"]),
+ "1c": Level("1c", "Forsaken City C", rooms_by_level["1c"], room_cons_by_level["1c"]),
+ "2a": Level("2a", "Old Site A", rooms_by_level["2a"], room_cons_by_level["2a"]),
+ "2b": Level("2b", "Old Site B", rooms_by_level["2b"], room_cons_by_level["2b"]),
+ "2c": Level("2c", "Old Site C", rooms_by_level["2c"], room_cons_by_level["2c"]),
+ "3a": Level("3a", "Celestial Resort A", rooms_by_level["3a"], room_cons_by_level["3a"]),
+ "3b": Level("3b", "Celestial Resort B", rooms_by_level["3b"], room_cons_by_level["3b"]),
+ "3c": Level("3c", "Celestial Resort C", rooms_by_level["3c"], room_cons_by_level["3c"]),
+ "4a": Level("4a", "Golden Ridge A", rooms_by_level["4a"], room_cons_by_level["4a"]),
+ "4b": Level("4b", "Golden Ridge B", rooms_by_level["4b"], room_cons_by_level["4b"]),
+ "4c": Level("4c", "Golden Ridge C", rooms_by_level["4c"], room_cons_by_level["4c"]),
+ "5a": Level("5a", "Mirror Temple A", rooms_by_level["5a"], room_cons_by_level["5a"]),
+ "5b": Level("5b", "Mirror Temple B", rooms_by_level["5b"], room_cons_by_level["5b"]),
+ "5c": Level("5c", "Mirror Temple C", rooms_by_level["5c"], room_cons_by_level["5c"]),
+ "6a": Level("6a", "Reflection A", rooms_by_level["6a"], room_cons_by_level["6a"]),
+ "6b": Level("6b", "Reflection B", rooms_by_level["6b"], room_cons_by_level["6b"]),
+ "6c": Level("6c", "Reflection C", rooms_by_level["6c"], room_cons_by_level["6c"]),
+ "7a": Level("7a", "The Summit A", rooms_by_level["7a"], room_cons_by_level["7a"]),
+ "7b": Level("7b", "The Summit B", rooms_by_level["7b"], room_cons_by_level["7b"]),
+ "7c": Level("7c", "The Summit C", rooms_by_level["7c"], room_cons_by_level["7c"]),
+ "8a": Level("8a", "Epilogue", rooms_by_level["8a"], room_cons_by_level["8a"]),
+ "9a": Level("9a", "Core A", rooms_by_level["9a"], room_cons_by_level["9a"]),
+ "9b": Level("9b", "Core B", rooms_by_level["9b"], room_cons_by_level["9b"]),
+ "9c": Level("9c", "Core C", rooms_by_level["9c"], room_cons_by_level["9c"]),
+ "10a": Level("10a", "Farewell", rooms_by_level["10a"], room_cons_by_level["10a"]),
+ "10b": Level("10b", "Farewell", rooms_by_level["10b"], room_cons_by_level["10b"]),
+ "10c": Level("10c", "Farewell", rooms_by_level["10c"], room_cons_by_level["10c"]),
}
diff --git a/worlds/celeste_open_world/data/ParseData.py b/worlds/celeste_open_world/data/ParseData.py
index b9f8f47267ef..c75ddfefc3ff 100644
--- a/worlds/celeste_open_world/data/ParseData.py
+++ b/worlds/celeste_open_world/data/ParseData.py
@@ -18,8 +18,8 @@
for level in level_data["levels"]:
level_str = (f" \"{level['name']}\": Level(\"{level['name']}\", "
f"\"{level['display_name']}\", "
- f"[room for _, room in all_rooms.items() if room.level_name == \"{level['name']}\"], "
- f"[room_con for _, room_con in all_room_connections.items() if room_con.level_name == \"{level['name']}\"]),"
+ f"rooms_by_level[\"{level['name']}\"], "
+ f"room_cons_by_level[\"{level['name']}\"]),"
)
all_levels.append(level_str)
@@ -31,8 +31,8 @@
room_str = (f" \"{room_full_name}\": Room(\"{level['name']}\", "
f"\"{room_full_name}\", \"{room_full_display_name}\", "
- f"[reg for _, reg in all_regions.items() if reg.room_name == \"{room_full_name}\"], "
- f"[door for _, door in all_doors.items() if door.room_name == \"{room_full_name}\"]"
+ f"regions_by_room[\"{room_full_name}\"], "
+ f"doors_by_room[\"{room_full_name}\"]"
)
if "checkpoint" in room and room["checkpoint"] != "":
@@ -47,8 +47,8 @@
region_str = (f" \"{region_full_name}\": PreRegion(\"{region_full_name}\", "
f"\"{room_full_name}\", "
- f"[reg_con for _, reg_con in all_region_connections.items() if reg_con.source_name == \"{region_full_name}\"], "
- f"[loc for _, loc in all_locations.items() if loc.region_name == \"{region_full_name}\"]),"
+ f"connections_by_region[\"{region_full_name}\"], "
+ f"locations_by_region[\"{region_full_name}\"]),"
)
all_regions.append(region_str)
@@ -150,6 +150,7 @@
print("")
print("from ..Levels import Level, Room, PreRegion, LevelLocation, RegionConnection, RoomConnection, Door, DoorDirection, LocationType")
print("from ..Names import ItemName")
+ print(f"from collections import defaultdict")
print("")
print("all_doors: dict[str, Door] = {")
for line in all_doors:
@@ -166,6 +167,15 @@
print(line)
print("}")
print("")
+ print("connections_by_region: defaultdict[str, list[RegionConnection]] = defaultdict(lambda: [])")
+ print("locations_by_region: defaultdict[str, list[LevelLocation]] = defaultdict(lambda: [])")
+ print("")
+ print("for _, connection in all_region_connections.items():")
+ print(" connections_by_region[connection.source_name].append(connection)")
+ print("")
+ print("for _, location in all_locations.items():")
+ print(" locations_by_region[location.region_name].append(location)")
+ print("")
print("all_regions: dict[str, PreRegion] = {")
for line in all_regions:
print(line)
@@ -176,11 +186,29 @@
print(line)
print("}")
print("")
+ print("regions_by_room: defaultdict[str, list[PreRegion]] = defaultdict(lambda: [])")
+ print("doors_by_room: defaultdict[str, list[Door]] = defaultdict(lambda: [])")
+ print("")
+ print("for _, region in all_regions.items():")
+ print(" regions_by_room[region.room_name].append(region)")
+ print("")
+ print("for _, door in all_doors.items():")
+ print(" doors_by_room[door.room_name].append(door)")
+ print("")
print("all_rooms: dict[str, Room] = {")
for line in all_rooms:
print(line)
print("}")
print("")
+ print("rooms_by_level: defaultdict[str, list[Room]] = defaultdict(lambda: [])")
+ print("room_cons_by_level: defaultdict[str, list[RoomConnection]] = defaultdict(lambda: [])")
+ print("")
+ print("for _, room in all_rooms.items():")
+ print(" rooms_by_level[room.level_name].append(room)")
+ print("")
+ print("for _, room_con in all_room_connections.items():")
+ print(" room_cons_by_level[room_con.level_name].append(room_con)")
+ print("")
print("all_levels: dict[str, Level] = {")
for line in all_levels:
print(line)
diff --git a/worlds/celeste_open_world/docs/en_Celeste (Open World).md b/worlds/celeste_open_world/docs/en_Celeste (Open World).md
index c27c4d6480da..6330bd9bff22 100644
--- a/worlds/celeste_open_world/docs/en_Celeste (Open World).md
+++ b/worlds/celeste_open_world/docs/en_Celeste (Open World).md
@@ -95,4 +95,4 @@ Additionally, the following locations can optionally be included in the Location
## How can I get started?
-To get started playing Celeste (Open World) in Archipelago, [go to the setup guide for this game](../../../tutorial/Celeste%20(Open%20World)/guide/en)
+To get started playing Celeste (Open World) in Archipelago, [go to the setup guide for this game](/tutorial/Celeste%20%28Open%20World%29/guide_en)
diff --git a/worlds/civ_6/Container.py b/worlds/civ_6/Container.py
index a5790c1ec474..433145f42fdd 100644
--- a/worlds/civ_6/Container.py
+++ b/worlds/civ_6/Container.py
@@ -44,9 +44,10 @@ def write_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
opened_zipfile.writestr(filename, yml)
super().write_contents(opened_zipfile)
+
def sanitize_value(value: str) -> str:
- """Removes values that can cause issues in XML"""
- return value.replace('"', "'").replace('&', 'and')
+ """Removes values that can cause issues in XML"""
+ return value.replace('"', "'").replace('&', 'and').replace('{', '').replace('}', '')
def get_cost(world: 'CivVIWorld', location: CivVILocationData) -> int:
@@ -87,8 +88,10 @@ def generate_new_items(world: 'CivVIWorld') -> str:
boost_civics = []
if world.options.boostsanity:
- boost_techs = [location for location in locations if location.location_type == CivVICheckType.BOOST and location.name.split("_")[1] == "TECH"]
- boost_civics = [location for location in locations if location.location_type == CivVICheckType.BOOST and location.name.split("_")[1] == "CIVIC"]
+ boost_techs = [location for location in locations if location.location_type ==
+ CivVICheckType.BOOST and location.name.split("_")[1] == "TECH"]
+ boost_civics = [location for location in locations if location.location_type ==
+ CivVICheckType.BOOST and location.name.split("_")[1] == "CIVIC"]
techs += boost_techs
civics += boost_civics
diff --git a/worlds/civ_6/ItemData.py b/worlds/civ_6/ItemData.py
index 5f3c16a9b1ba..7ed1d713e30c 100644
--- a/worlds/civ_6/ItemData.py
+++ b/worlds/civ_6/ItemData.py
@@ -20,6 +20,7 @@ class CivVIBoostData:
Prereq: List[str]
PrereqRequiredCount: int
Classification: str
+ EraRequired: bool = False
class GoodyHutRewardData(TypedDict):
diff --git a/worlds/civ_6/Locations.py b/worlds/civ_6/Locations.py
index 71f29f1cfc89..7da824dfe8e4 100644
--- a/worlds/civ_6/Locations.py
+++ b/worlds/civ_6/Locations.py
@@ -150,7 +150,10 @@ def generate_era_location_table() -> Dict[str, Dict[str, CivVILocationData]]:
location = CivVILocationData(
boost.Type, 0, 0, id_base, boost.EraType, CivVICheckType.BOOST
)
- era_locations["ERA_ANCIENT"][boost.Type] = location
+ # If EraRequired is True, place the boost in its actual era
+ # Otherwise, place it in ERA_ANCIENT for early access
+ target_era = boost.EraType if boost.EraRequired else "ERA_ANCIENT"
+ era_locations[target_era][boost.Type] = location
id_base += 1
return era_locations
diff --git a/worlds/civ_6/data/boosts.py b/worlds/civ_6/data/boosts.py
index 49cedfdfd991..d0bdecc0558c 100644
--- a/worlds/civ_6/data/boosts.py
+++ b/worlds/civ_6/data/boosts.py
@@ -210,8 +210,8 @@
CivVIBoostData(
"BOOST_TECH_SQUARE_RIGGING",
"ERA_RENAISSANCE",
- ["TECH_GUNPOWDER"],
- 1,
+ ["TECH_GUNPOWDER", "TECH_MILITARY_ENGINEERING", "TECH_MINING"],
+ 3,
"DEFAULT",
),
CivVIBoostData(
@@ -252,15 +252,15 @@
CivVIBoostData(
"BOOST_TECH_BALLISTICS",
"ERA_INDUSTRIAL",
- ["TECH_SIEGE_TACTICS", "TECH_MILITARY_ENGINEERING"],
- 2,
+ ["TECH_SIEGE_TACTICS", "TECH_MILITARY_ENGINEERING", "TECH_BRONZE_WORKING"],
+ 3,
"DEFAULT",
),
CivVIBoostData(
"BOOST_TECH_MILITARY_SCIENCE",
"ERA_INDUSTRIAL",
- ["TECH_STIRRUPS"],
- 1,
+ ["TECH_BRONZE_WORKING", "TECH_STIRRUPS", "TECH_MINING"],
+ 3,
"DEFAULT",
),
CivVIBoostData(
@@ -301,8 +301,8 @@
CivVIBoostData(
"BOOST_TECH_REPLACEABLE_PARTS",
"ERA_MODERN",
- ["TECH_MILITARY_SCIENCE"],
- 1,
+ ["TECH_MILITARY_SCIENCE", "TECH_MINING"],
+ 2,
"DEFAULT",
),
CivVIBoostData(
@@ -343,8 +343,8 @@
CivVIBoostData(
"BOOST_TECH_ADVANCED_FLIGHT",
"ERA_ATOMIC",
- ["TECH_FLIGHT"],
- 1,
+ ["TECH_FLIGHT", "TECH_REFINING", "TECH_MINING"],
+ 3,
"DEFAULT",
),
CivVIBoostData(
@@ -436,8 +436,8 @@
CivVIBoostData(
"BOOST_TECH_COMPOSITES",
"ERA_INFORMATION",
- ["TECH_COMBUSTION"],
- 1,
+ ["TECH_COMBUSTION", "TECH_REFINING", "TECH_MINING"],
+ 3,
"DEFAULT",
),
CivVIBoostData(
@@ -470,7 +470,7 @@
"TECH_ELECTRICITY",
"TECH_NUCLEAR_FISSION",
],
- 1,
+ 4,
"DEFAULT",
),
CivVIBoostData(
@@ -651,10 +651,11 @@
),
CivVIBoostData(
"BOOST_CIVIC_FEUDALISM",
- "ERA_MEDIEVAL",
+ "ERA_CLASSICAL",
[],
0,
"DEFAULT",
+ True,
),
CivVIBoostData(
"BOOST_CIVIC_CIVIL_SERVICE",
@@ -662,6 +663,7 @@
[],
0,
"DEFAULT",
+ True,
),
CivVIBoostData(
"BOOST_CIVIC_MERCENARIES",
@@ -790,6 +792,7 @@
[],
0,
"DEFAULT",
+ True
),
CivVIBoostData(
"BOOST_CIVIC_CONSERVATION",
@@ -885,6 +888,7 @@
["TECH_ROCKETRY"],
1,
"DEFAULT",
+ True
),
CivVIBoostData(
"BOOST_CIVIC_GLOBALIZATION",
diff --git a/worlds/civ_6/test/TestBoostsanity.py b/worlds/civ_6/test/TestBoostsanity.py
index 6efed6c66e25..54ad74cb08ad 100644
--- a/worlds/civ_6/test/TestBoostsanity.py
+++ b/worlds/civ_6/test/TestBoostsanity.py
@@ -105,3 +105,78 @@ def test_boosts_are_not_included(self) -> None:
if "BOOST" in location.name:
found_locations += 1
self.assertEqual(found_locations, 0)
+
+
+class TestBoostsanityEraRequired(CivVITestBase):
+ options = {
+ "boostsanity": "true",
+ "progression_style": "none",
+ "shuffle_goody_hut_rewards": "false",
+ }
+
+ def test_era_required_boosts_not_accessible_early(self) -> None:
+ # BOOST_CIVIC_FEUDALISM has EraRequired=True and ERA_CLASSICAL
+ # It should NOT be accessible in Ancient era
+ self.assertFalse(self.can_reach_location("BOOST_CIVIC_FEUDALISM"))
+
+ # BOOST_CIVIC_URBANIZATION has EraRequired=True and ERA_INDUSTRIAL
+ # It should NOT be accessible in Ancient era
+ self.assertFalse(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
+
+ # BOOST_CIVIC_SPACE_RACE has EraRequired=True and ERA_ATOMIC
+ # It should NOT be accessible in Ancient era
+ self.assertFalse(self.can_reach_location("BOOST_CIVIC_SPACE_RACE"))
+
+ # Regular boosts without EraRequired should be accessible
+ self.assertTrue(self.can_reach_location("BOOST_TECH_SAILING"))
+ self.assertTrue(self.can_reach_location("BOOST_CIVIC_MILITARY_TRADITION"))
+
+ def test_era_required_boosts_accessible_in_correct_era(self) -> None:
+ # Collect items to reach Classical era
+ self.collect_by_name(["Mining", "Bronze Working", "Astrology", "Writing",
+ "Irrigation", "Sailing", "Animal Husbandry",
+ "State Workforce", "Foreign Trade"])
+
+ # BOOST_CIVIC_FEUDALISM should now be accessible in Classical era
+ self.assertTrue(self.can_reach_location("BOOST_CIVIC_FEUDALISM"))
+
+ # BOOST_CIVIC_URBANIZATION still not accessible (requires Industrial)
+ self.assertFalse(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
+
+ # Collect more items to reach Industrial era
+ self.collect_all_but(["TECH_ROCKETRY"])
+
+ # Now BOOST_CIVIC_URBANIZATION should be accessible
+ self.assertTrue(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
+
+
+class TestBoostsanityEraRequiredWithProgression(CivVITestBase):
+ options = {
+ "boostsanity": "true",
+ "progression_style": "eras_and_districts",
+ "shuffle_goody_hut_rewards": "false",
+ }
+
+ def test_era_required_with_progressive_eras(self) -> None:
+ # Collect all items except Progressive Era
+ self.collect_all_but(["Progressive Era"])
+
+ # Even with all other items, era-required boosts should not be accessible
+ self.assertFalse(self.can_reach_location("BOOST_CIVIC_FEUDALISM"))
+ self.assertFalse(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
+
+ # Collect enough Progressive Era items to reach Classical (needs 2)
+ self.collect(self.get_item_by_name("Progressive Era"))
+ self.collect(self.get_item_by_name("Progressive Era"))
+
+ # BOOST_CIVIC_FEUDALISM should now be accessible
+ self.assertTrue(self.can_reach_location("BOOST_CIVIC_FEUDALISM"))
+
+ # But BOOST_CIVIC_URBANIZATION still requires Industrial era (needs 5 total)
+ self.assertFalse(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
+
+ # Collect 3 more Progressive Era items to reach Industrial
+ self.collect_by_name(["Progressive Era", "Progressive Era", "Progressive Era"])
+
+ # Now BOOST_CIVIC_URBANIZATION should be accessible
+ self.assertTrue(self.can_reach_location("BOOST_CIVIC_URBANIZATION"))
diff --git a/worlds/cv64/aesthetics.py b/worlds/cv64/aesthetics.py
index 66709174d837..5bfe94368498 100644
--- a/worlds/cv64/aesthetics.py
+++ b/worlds/cv64/aesthetics.py
@@ -214,7 +214,7 @@
"\"Banshee Boomerang.\"",
0x10: "No weapon triangle\n"
"advantages with this.",
- 0x12: "It looks sus? Trust me,"
+ 0x12: "It looks sus? Trust me,\n"
"my wares are genuine.",
0x15: "This non-volatile kind\n"
"is safe to handle.",
diff --git a/worlds/cv64/data/patches.py b/worlds/cv64/data/patches.py
index 4a964612f03e..160c9c73e1f1 100644
--- a/worlds/cv64/data/patches.py
+++ b/worlds/cv64/data/patches.py
@@ -2034,7 +2034,7 @@
0x24090000, # ADDIU T1, R0, 0x0000 <- Starting Ice Traps
0xA1099BE2, # SB T1, 0x9BE2 (T0)
0x240C0000, # ADDIU T4, R0, 0x0000
- 0x240D0022, # ADDIU T5, R0, 0x0022
+ 0x240D0023, # ADDIU T5, R0, 0x0023
0x11AC0007, # BEQ T5, T4, [forward 0x07]
0x3C0A8040, # LUI T2, 0x8040
0x014C5021, # ADDU T2, T2, T4
diff --git a/worlds/cv64/docs/setup_en.md b/worlds/cv64/docs/setup_en.md
index 707618a1eba5..51a91bbad15a 100644
--- a/worlds/cv64/docs/setup_en.md
+++ b/worlds/cv64/docs/setup_en.md
@@ -30,7 +30,7 @@ the White Jewels.
1. Create your options file (YAML). You can make one on the
[Castlevania 64 options page](../../../games/Castlevania%2064/player-options).
-2. Follow the general Archipelago instructions for [generating a game](../../Archipelago/setup/en#generating-a-game).
+2. Follow the general Archipelago instructions for [generating a game](/tutorial/Archipelago/setup_en#generating-a-game).
This will generate an output file for you. Your patch file will have the `.apcv64` file extension.
3. Open `ArchipelagoLauncher.exe`
4. Select "Open Patch" on the left side and select your patch file.
diff --git a/worlds/cv64/rom.py b/worlds/cv64/rom.py
index a40d3ab30034..4c31dc148a35 100644
--- a/worlds/cv64/rom.py
+++ b/worlds/cv64/rom.py
@@ -609,8 +609,8 @@ def apply_patches(caller: APProcedurePatch, rom: bytes, options_file: str) -> by
# Shimmy speed increase hack
if options["increase_shimmy_speed"]:
- rom_data.write_int32(0x97EB4, 0x803FE9F0)
- rom_data.write_int32s(0xBFE9F0, patches.shimmy_speed_modifier)
+ rom_data.write_int32(0x97EB4, 0x803FEA20)
+ rom_data.write_int32s(0xBFEA20, patches.shimmy_speed_modifier)
# Disable landing fall damage
if options["fall_guard"]:
@@ -1030,7 +1030,7 @@ def get_base_rom_bytes(file_name: str = "") -> bytes:
basemd5 = hashlib.md5()
basemd5.update(base_rom_bytes)
if CV64_US_10_HASH != basemd5.hexdigest():
- raise Exception("Supplied Base Rom does not match known MD5 for Castlevania 64 US 1.0."
+ raise Exception("Supplied Base Rom does not match known MD5 for Castlevania 64 US 1.0. "
"Get the correct game and version, then dump it.")
setattr(get_base_rom_bytes, "base_rom_bytes", base_rom_bytes)
return base_rom_bytes
diff --git a/worlds/cvcotm/client.py b/worlds/cvcotm/client.py
index 36019cd60ecc..74a853bbaacf 100644
--- a/worlds/cvcotm/client.py
+++ b/worlds/cvcotm/client.py
@@ -247,6 +247,10 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
await bizhawk.write(ctx.bizhawk_ctx, [(QUEUED_TEXTBOX_1_ADDRESS, [0 for _ in range(12)], "EWRAM")])
return
+ # If the player doesn't have Dash Boots for whatever reason, put them in their inventory now.
+ if not magic_items_array[0]:
+ await bizhawk.write(ctx.bizhawk_ctx, [(MAGIC_ITEMS_ARRAY_START, [1], "EWRAM")])
+
# Enable DeathLink if it's in our slot_data.
if "DeathLink" not in ctx.tags and ctx.slot_data["death_link"]:
await ctx.update_death_link(True)
diff --git a/worlds/cvcotm/docs/setup_en.md b/worlds/cvcotm/docs/setup_en.md
index 4c34dcb836ef..f968ee01a537 100644
--- a/worlds/cvcotm/docs/setup_en.md
+++ b/worlds/cvcotm/docs/setup_en.md
@@ -28,7 +28,7 @@ clear it.
## Generating and Patching a Game
1. Create your settings file (YAML). You can make one on the [Castlevania: Circle of the Moon options page](../../../games/Castlevania%20-%20Circle%20of%20the%20Moon/player-options).
-2. Follow the general Archipelago instructions for [generating a game](../../Archipelago/setup/en#generating-a-game).
+2. Follow the general Archipelago instructions for [generating a game](/tutorial/Archipelago/setup_en#generating-a-game).
This will generate an output file for you. Your patch file will have the `.apcvcotm` file extension.
3. Open `ArchipelagoLauncher.exe`.
4. Select "Open Patch" on the left side and select your patch file.
diff --git a/worlds/cvcotm/items.py b/worlds/cvcotm/items.py
index bce2b3fc0c4d..edcbd1696623 100644
--- a/worlds/cvcotm/items.py
+++ b/worlds/cvcotm/items.py
@@ -110,7 +110,7 @@ def get_item_counts(world: "CVCotMWorld") -> Dict[ItemClassification, Dict[str,
# If Halve DSS Cards Placed is on, determine which cards we will exclude here.
if world.options.halve_dss_cards_placed:
- excluded_cards = list(ACTION_CARDS.union(ATTRIBUTE_CARDS))
+ excluded_cards = sorted(ACTION_CARDS.union(ATTRIBUTE_CARDS))
has_freeze_action = False
has_freeze_attr = False
diff --git a/worlds/cvcotm/rom.py b/worlds/cvcotm/rom.py
index 350829292b34..a1b41ba31dfe 100644
--- a/worlds/cvcotm/rom.py
+++ b/worlds/cvcotm/rom.py
@@ -586,7 +586,7 @@ def get_base_rom_bytes(file_name: str = "") -> bytes:
basemd5.update(base_rom_bytes)
# if basemd5.hexdigest() not in [CVCOTM_CT_US_HASH, CVCOTM_AC_US_HASH, CVCOTM_VC_US_HASH]:
if basemd5.hexdigest() not in [CVCOTM_CT_US_HASH, CVCOTM_AC_US_HASH]:
- raise Exception("Supplied Base ROM does not match known MD5s for Castlevania: Circle of the Moon USA."
+ raise Exception("Supplied Base ROM does not match known MD5s for Castlevania: Circle of the Moon USA. "
"Get the correct game and version, then dump it.")
setattr(get_base_rom_bytes, "base_rom_bytes", base_rom_bytes)
return base_rom_bytes
diff --git a/worlds/dark_souls_3/__init__.py b/worlds/dark_souls_3/__init__.py
index a1ad2f6a7066..8c9716c03d5a 100644
--- a/worlds/dark_souls_3/__init__.py
+++ b/worlds/dark_souls_3/__init__.py
@@ -90,6 +90,22 @@ def generate_early(self) -> None:
self.created_regions = set()
self.all_excluded_locations.update(self.options.exclude_locations.value)
+ # This code doesn't work because tests don't verify options
+ # Don't consider disabled locations to be AP-excluded
+ # if not self.options.enable_dlc:
+ # self.options.exclude_locations.value = {
+ # location
+ # for location in self.options.exclude_locations
+ # if not location_dictionary[location].dlc
+ # }
+
+ # if not self.options.enable_ngp:
+ # self.options.exclude_locations.value = {
+ # location for
+ # location in self.options.exclude_locations
+ # if not location_dictionary[location].ngp
+ # }
+
# Inform Universal Tracker where Yhorm is being randomized to.
if hasattr(self.multiworld, "re_gen_passthrough"):
if "Dark Souls III" in self.multiworld.re_gen_passthrough:
@@ -264,6 +280,13 @@ def create_region(self, region_name, location_table) -> Region:
):
new_location.progress_type = LocationProgressType.EXCLUDED
else:
+ # Don't consider non-randomized locations to be AP-excluded
+ if location.name in excluded:
+ excluded.remove(location.name)
+ # Only remove from all_excluded if excluded does not have priority over missable
+ if not (self.options.missable_location_behavior < self.options.excluded_location_behavior):
+ self.all_excluded_locations.remove(location.name)
+
# Don't allow missable duplicates of progression items to be expected progression.
if location.name in self.missable_dupe_prog_locs: continue
@@ -283,11 +306,6 @@ def create_region(self, region_name, location_table) -> Region:
parent = new_region,
)
new_location.place_locked_item(event_item)
- if location.name in excluded:
- excluded.remove(location.name)
- # Only remove from all_excluded if excluded does not have priority over missable
- if not (self.options.missable_location_behavior < self.options.excluded_location_behavior):
- self.all_excluded_locations.remove(location.name)
new_region.locations.append(new_location)
@@ -1357,7 +1375,7 @@ def write_spoiler(self, spoiler_handle: TextIO) -> None:
if self.yhorm_location != default_yhorm_location:
text += f"\nYhorm takes the place of {self.yhorm_location.name} in {self.player_name}'s world\n"
- if self.options.excluded_location_behavior == "allow_useful":
+ if self.options.excluded_location_behavior != "forbid_useful":
text += f"\n{self.player_name}'s world excluded: {sorted(self.all_excluded_locations)}\n"
if text:
diff --git a/worlds/dkc3/docs/setup_en.md b/worlds/dkc3/docs/setup_en.md
index c832bcd702e9..8a0372ac2618 100644
--- a/worlds/dkc3/docs/setup_en.md
+++ b/worlds/dkc3/docs/setup_en.md
@@ -111,9 +111,8 @@ You only have to do these steps once. Note, RetroArch 1.9.x will not work as it
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
-
+ Network Command Port at 55355. \
+ 
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
diff --git a/worlds/dlcquest/Items.py b/worlds/dlcquest/Items.py
index 5496885a74f7..380c54290ec9 100644
--- a/worlds/dlcquest/Items.py
+++ b/worlds/dlcquest/Items.py
@@ -2,6 +2,7 @@
import enum
import math
from dataclasses import dataclass, field
+from functools import reduce
from random import Random
from typing import Dict, List, Set
@@ -61,7 +62,7 @@ def load_item_csv():
item_reader = csv.DictReader(file)
for item in item_reader:
id = int(item["id"]) if item["id"] else None
- classification = ItemClassification[item["classification"]]
+ classification = reduce((lambda a, b: a | b), {ItemClassification[str_classification] for str_classification in item["classification"].split(",")})
groups = {Group[group] for group in item["groups"].split(",") if group}
items.append(ItemData(id, item["name"], classification, groups))
return items
diff --git a/worlds/dlcquest/data/items.csv b/worlds/dlcquest/data/items.csv
index 82150254b3c1..7d9fdcf364a5 100644
--- a/worlds/dlcquest/data/items.csv
+++ b/worlds/dlcquest/data/items.csv
@@ -22,7 +22,7 @@ id,name,classification,groups
20,Wall Jump Pack,progression,"DLC,Freemium"
21,Health Bar Pack,useful,"DLC,Freemium"
22,Parallax Pack,filler,"DLC,Freemium"
-23,Harmless Plants Pack,progression,"DLC,Freemium"
+23,Harmless Plants Pack,"progression,trap","DLC,Freemium"
24,Death of Comedy Pack,progression,"DLC,Freemium"
25,Canadian Dialog Pack,filler,"DLC,Freemium"
26,DLC NPC Pack,progression,"DLC,Freemium"
diff --git a/worlds/earthbound/Client.py b/worlds/earthbound/Client.py
new file mode 100644
index 000000000000..5bd76d3e8a92
--- /dev/null
+++ b/worlds/earthbound/Client.py
@@ -0,0 +1,516 @@
+import logging
+import struct
+import typing
+import time
+import uuid
+from struct import pack
+from .game_data.local_data import client_specials, world_version, hint_bits, item_id_table, money_id_table
+from .game_data.text_data import text_encoder
+from .gifting.gift_tags import gift_properties
+from .gifting.trait_parser import wanted_traits, trait_interpreter, gift_exclusions
+
+from NetUtils import ClientStatus, color
+from worlds.AutoSNIClient import SNIClient
+
+if typing.TYPE_CHECKING:
+ from SNIClient import SNIContext
+else:
+ SNIContext = typing.Any
+
+snes_logger = logging.getLogger("SNES")
+
+ROM_START = 0x000000
+WRAM_START = 0xF50000
+WRAM_SIZE = 0x20000
+SRAM_START = 0xE00000
+
+EB_ROMHASH_START = 0x00FFC0
+WORLD_VERSION = 0x3FF0A0
+ROMHASH_SIZE = 0x15
+
+ITEM_MODE = ROM_START + 0x04FD76
+
+ITEMQUEUE_HIGH = WRAM_START + 0xB576
+ITEM_RECEIVED = WRAM_START + 0xB570
+SPECIAL_RECEIVED = WRAM_START + 0xB572
+MONEY_RECIVED = WRAM_START + 0xB5F1
+SAVE_FILE = WRAM_START + 0xB4A1
+GIYGAS_CLEAR = WRAM_START + 0x9C11
+GAME_CLEAR = WRAM_START + 0x9C85
+OPEN_WINDOW = WRAM_START + 0x8958
+MELODY_TABLE = WRAM_START + 0x9C1E
+EARTH_POWER_FLAG = WRAM_START + 0x9C82
+CUR_SCENE = WRAM_START + 0x97B8
+IS_IN_BATTLE = WRAM_START + 0x9643
+DEATHLINK_ENABLED = ROM_START + 0x04FD74
+DEATHLINK_TYPE = ROM_START + 0x04FD75
+IS_CURRENTLY_DEAD = WRAM_START + 0xB582
+GOT_DEATH_FROM_SERVER = WRAM_START + 0xB583
+PLAYER_JUST_DIED_SEND_DEATHLINK = WRAM_START + 0xB584
+IS_ABLE_TO_RECEIVE_DEATHLINKS = WRAM_START + 0xB585
+CHAR_COUNT = WRAM_START + 0x98A4
+OSS_FLAG = WRAM_START + 0x5D98
+HINT_SCOUNT_IDS = ROM_START + 0x310250
+SCOUTED_HINT_FLAGS = WRAM_START + 0xB621
+MONEY_IN_BANK = WRAM_START + 0x9835
+IS_ENERGYLINK_ENABLED = ROM_START + 0x04FD78
+already_tried_to_connect = False
+
+
+class EarthBoundClient(SNIClient):
+ game = "EarthBound"
+ patch_suffix = ".apeb"
+ most_recent_connect: str = ""
+ client_version: str = world_version
+ hint_list: list[int] = []
+ hinted_shop_locations: list[int] = []
+
+ async def deathlink_kill_player(self, ctx: "SNIContext") -> None:
+ from SNIClient import DeathState, snes_buffered_write, snes_flush_writes, snes_read
+ battle_hp = {
+ 1: WRAM_START + 0x9FBF,
+ 2: WRAM_START + 0xA00D,
+ 3: WRAM_START + 0xA05B,
+ 4: WRAM_START + 0xA0A9,
+ }
+
+ active_hp = {
+ 1: WRAM_START + 0x9A15,
+ 2: WRAM_START + 0x9A74,
+ 3: WRAM_START + 0x9AD3,
+ 4: WRAM_START + 0x9B32,
+ }
+
+ scrolling_hp = {
+ 1: WRAM_START + 0x9A13,
+ 2: WRAM_START + 0x9A72,
+ 3: WRAM_START + 0x9AD1,
+ 4: WRAM_START + 0x9B30,
+ }
+
+ deathlink_mode = await snes_read(ctx, DEATHLINK_TYPE, 1)
+ oss_flag = await snes_read(ctx, OSS_FLAG, 1)
+ is_currently_dead = await snes_read(ctx, IS_CURRENTLY_DEAD, 1)
+ can_receive_deathlinks = await snes_read(ctx, IS_ABLE_TO_RECEIVE_DEATHLINKS, 1)
+ is_in_battle = await snes_read(ctx, IS_IN_BATTLE, 1)
+ char_count = await snes_read(ctx, CHAR_COUNT, 1)
+ snes_buffered_write(ctx, GOT_DEATH_FROM_SERVER, bytes([0x01]))
+ text_open = await snes_read(ctx, OPEN_WINDOW, 1)
+
+ if text_open is None: #Catch None reads from client jank????????
+ return
+
+ if is_currently_dead[0] != 0x00 or can_receive_deathlinks[0] == 0x00:
+ return
+
+ # If suppression is set and we're not in a battle dont do deathlinks
+ if oss_flag[0] != 0x00 and is_in_battle[0] == 0x00:
+ return
+
+ # Prevent overworld deaths while a menu is open
+ if not is_in_battle[0] and text_open[0] != 0xFF:
+ return
+
+ for i in range(char_count[0]):
+ w_cur_char = WRAM_START + 0x986F + i
+ current_char = await snes_read(ctx, w_cur_char, 1)
+ snes_buffered_write(ctx, active_hp[current_char[0]], bytes([0x00, 0x00]))
+ snes_buffered_write(ctx, battle_hp[i + 1], bytes([0x00, 0x00]))
+ if deathlink_mode[0] == 0 or is_in_battle[0] == 0:
+ # This should be the check for instant or mercy. Write the value, call it here
+ snes_buffered_write(ctx, scrolling_hp[current_char[0]], bytes([0x00, 0x00]))
+ await snes_flush_writes(ctx)
+ ctx.death_state = DeathState.dead
+ ctx.last_death_link = time.time()
+
+ def on_package(self, ctx, cmd: str, args: dict[str, typing.Any]) -> None:
+ super().on_package(ctx, cmd, args)
+
+ if cmd == "Connected":
+ self.slot_data = args.get("slot_data", None)
+
+ async def validate_rom(self, ctx: "SNIContext") -> bool:
+ from SNIClient import snes_read
+
+ rom_name = await snes_read(ctx, EB_ROMHASH_START, ROMHASH_SIZE)
+ apworld_version = await snes_read(ctx, WORLD_VERSION, 16)
+
+ item_handling = await snes_read(ctx, ITEM_MODE, 1)
+ if rom_name is None or rom_name[:6] != b"MOM2AP":
+ return False
+
+ apworld_version = apworld_version.decode("utf-8").strip("\x00")
+ if apworld_version != self.most_recent_connect and apworld_version != self.client_version:
+ ctx.gui_error("Bad Version", f"EarthBound APWorld version {self.client_version} does not match generated version {apworld_version}")
+ self.most_recent_connect = apworld_version
+ return False
+
+ ctx.game = self.game
+ if item_handling[0] == 0x00:
+ ctx.items_handling = 0b001
+ else:
+ ctx.items_handling = 0b111
+ ctx.rom = rom_name
+
+ death_link = await snes_read(ctx, DEATHLINK_ENABLED, 1)
+ if death_link:
+ await ctx.update_death_link(bool(death_link[0] & 0b1))
+ return True
+
+ async def game_watcher(self, ctx: "SNIContext") -> None:
+ from SNIClient import snes_buffered_write, snes_flush_writes, snes_read, snes_write
+ giygas_clear = await snes_read(ctx, GIYGAS_CLEAR, 0x1)
+ game_clear = await snes_read(ctx, GAME_CLEAR, 0x1)
+ item_received = await snes_read(ctx, ITEM_RECEIVED, 0x1)
+ special_received = await snes_read(ctx, SPECIAL_RECEIVED, 0x1)
+ money_received = await snes_read(ctx, MONEY_RECIVED, 0x2)
+ save_num = await snes_read(ctx, SAVE_FILE, 0x1)
+ text_open = await snes_read(ctx, OPEN_WINDOW, 1)
+ melody_table = await snes_read(ctx, MELODY_TABLE, 2)
+ earth_power_absorbed = await snes_read(ctx, EARTH_POWER_FLAG, 1)
+ cur_script = await snes_read(ctx, CUR_SCENE, 1)
+ rom = await snes_read(ctx, EB_ROMHASH_START, ROMHASH_SIZE)
+ scouted_hint_flags = await snes_read(ctx, SCOUTED_HINT_FLAGS, 1)
+ gift_target = await snes_read(ctx, WRAM_START + 0xB5E7, 2)
+ outbound_gifts = await snes_read(ctx, WRAM_START + 0x31D0, 1)
+ shop_scout = await snes_read(ctx, WRAM_START + 0x0770, 1)
+ shop_scouts_enabled = await snes_read(ctx, ROM_START + 0x04FD77, 1)
+ outgoing_energy = await snes_read(ctx, MONEY_IN_BANK, 4)
+ if rom != ctx.rom:
+ ctx.rom = None
+ return
+
+ if giygas_clear[0] & 0x01 == 0x01: # Are we in the epilogue
+ return
+
+ if save_num[0] == 0x00: # If on the title screen
+ return
+
+ if ctx.slot is None:
+ return
+
+ if outgoing_energy is None: #None Catcher
+ return
+
+
+ if f"GiftBoxes;{ctx.team}" not in ctx.stored_data:
+ await ctx.send_msgs([{
+ "cmd": "SetNotify",
+ "keys": [f"GiftBoxes;{ctx.team}"]
+ }])
+
+ # GIFTING DATA
+ if f"GiftBox;{ctx.team};{ctx.slot}" not in ctx.stored_data:
+ local_giftbox = {
+ str(ctx.slot): {
+ "is_open": True,
+ "accepts_any_gift": False,
+ "desired_traits": wanted_traits,
+ "minimum_gift_data_version": 2,
+ "maximum_gift_data_version": 3}}
+ await ctx.send_msgs([{
+ "cmd": "Set",
+ "key": f"GiftBoxes;{ctx.team}",
+ "want_reply": False,
+ "default": {},
+ "operations": [{"operation": "update", "value": local_giftbox}]
+ }])
+
+ await ctx.send_msgs([{
+ "cmd": "Get",
+ "keys": [f"GiftBox;{ctx.team};{ctx.slot}"]
+ }])
+
+ await ctx.send_msgs([{
+ "cmd": "SetNotify",
+ "keys": [f"GiftBox;{ctx.team};{ctx.slot}", f"GiftBoxes;{ctx.team}"]
+ }])
+
+ inbox = ctx.stored_data.get(f"GiftBox;{ctx.team};{ctx.slot}")
+ motherbox = ctx.stored_data.get(f"GiftBoxes;{ctx.team}")
+ if inbox:
+ gift_item_name = "None"
+ key, gift = next(iter(inbox.items()))
+ if "item_name" in gift or "ItemName" in gift:
+ gift_item_name = gift.get("item_name", gift.get("ItemName"))
+ if gift_item_name in item_id_table and gift_item_name not in gift_exclusions:
+ # If the name matches an EB item, convert it to one (even if not coming from EB)
+ item = item_id_table[gift_item_name]
+ else:
+ item = trait_interpreter(gift)
+
+ inbox_queue = await snes_read(ctx, WRAM_START + 0x3200, 1)
+ # Pause if the receiver queue is full
+ if not inbox_queue[0]:
+ await snes_write(ctx, [(WRAM_START + 0x3200, bytes([item]))])
+ inbox.pop(key)
+ await ctx.send_msgs([{
+ "cmd": "Set",
+ "key": f"GiftBox;{ctx.team};{ctx.slot}",
+ "want_reply": False,
+ "default": {},
+ "operations": [{"operation": "pop", "value": key}]
+ }])
+
+ # We're in the Gift selection menu. This should write the selected player's name into RAM
+ # for parsing.
+ # TODO; CHECK A SETNOTIFY HERE
+ gift_target = int.from_bytes(gift_target, byteorder="little")
+
+ # Giftbox checking for the gift menu UI
+ if gift_target != 0x00 and motherbox is not None:
+ gift_recipient = str(gift_target)
+ recip_name = ctx.player_names[gift_target]
+ recip_name = get_alias(recip_name, ctx.slot_info[gift_target].name)
+ recip_name = text_encoder(recip_name, 20)
+ if gift_recipient in motherbox:
+ if "IsOpen" in motherbox[gift_recipient]:
+ motherbox[gift_recipient]["is_open"] = motherbox[gift_recipient].pop("IsOpen")
+
+ if gift_recipient in motherbox and motherbox[gift_recipient]["is_open"]:
+ recip_name.extend(text_encoder(" (Open)", 20))
+ else:
+ recip_name.extend(text_encoder(" (Closed)", 20))
+ recip_name.append(0x00)
+ await snes_write(ctx, [(WRAM_START + 0xFF80, recip_name)])
+ await snes_write(ctx, [(WRAM_START + 0xB5E7, bytes([0x00, 0x00]))])
+ await snes_write(ctx, [(WRAM_START + 0xB573, bytes([0x00, 0x00]))])
+
+ gift_flag_byte = await snes_read(ctx, WRAM_START + 0xB622, 1)
+ gift_flag_byte = gift_flag_byte[0] | 0x04
+ await snes_write(ctx, [(WRAM_START + 0xB622, bytes([gift_flag_byte]))])
+
+ if outbound_gifts[0] != 0x00 and motherbox is not None:
+ gift_buffer = await snes_read(ctx, WRAM_START + 0x31D1, 3)
+ gift_item_id = gift_buffer[0]
+ gift = gift_properties[gift_item_id]
+ recipient = struct.unpack("H", gift_buffer[-2:])
+ if str(recipient[0]) in motherbox:
+ # Check if the player's box is open, refund if not
+ if "IsOpen" in motherbox[str(recipient[0])]:
+ # Does the recipient 0 thing work if > 255? Will need some testing.
+ motherbox[str(recipient[0])]["is_open"] = motherbox[str(recipient[0])].pop("IsOpen")
+
+ if "AcceptsAnyGift" in motherbox[str(recipient[0])]:
+ motherbox[str(recipient[0])]["accepts_any_gift"] = motherbox[str(recipient[0])].pop("AcceptsAnyGift")
+
+ if "DesiredTraits" in motherbox[str(recipient[0])]:
+ motherbox[str(recipient[0])]["desired_traits"] = motherbox[str(recipient[0])].pop("DesiredTraits")
+
+ if "Trait" in motherbox[str(recipient[0])]["desired_traits"]:
+ motherbox[str(recipient[0])]["desired_traits"]["trait"] = motherbox[str(recipient[0])]["desired_traits"].pop("Trait")
+
+ if str(recipient[0]) in motherbox and motherbox[str(recipient[0])]["is_open"] and (any(
+ motherbox[str(recipient[0])]["accepts_any_gift"] or
+ trait["trait"] in motherbox[str(recipient[0])]["desired_traits"] for trait in gift.traits)):
+ was_refunded = False
+ recipient = recipient[0]
+ else:
+ was_refunded = True
+ recipient = ctx.slot
+ guid = str(uuid.uuid4())
+ outgoing_gift = {
+ guid: {
+ "id": guid,
+ "item_name": gift.name,
+ "amount": 1,
+ "item_value": gift.value,
+ "traits": gift.traits,
+ "sender_slot": ctx.slot,
+ "receiver_slot": recipient,
+ "sender_team": ctx.team,
+ "receiver_team": ctx.team, # ??? Should be Receive slot team?
+ "is_refund": was_refunded}}
+
+ await ctx.send_msgs([{
+ "cmd": "Set",
+ "key": f"GiftBox;{ctx.team};{recipient}", # Receiver team here too
+ "want_reply": True,
+ "default": {},
+ "operations": [{"operation": "update", "value": outgoing_gift}]
+ }])
+
+ gift_queue = await snes_read(ctx, WRAM_START + 0x31D4, 0x21)
+ # shuffle the entire queue down 3 bytes
+ outbox_full_byte = await snes_read(ctx, WRAM_START + 0xB622, 1)
+
+ await snes_write(ctx, [(WRAM_START + 0x31D1, gift_queue)])
+ await snes_write(ctx, [(WRAM_START + 0x31D0, bytes([outbound_gifts[0] - 1]))])
+ outbox_full_byte = outbox_full_byte[0] & ~0x08
+ await snes_write(ctx, [(WRAM_START + 0xB622, bytes([outbox_full_byte]))])
+
+ if (game_clear[0] & 0x01 == 0x01) and not ctx.finished_game: # Goal should ignore the item queue and textbox check
+ await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
+ ctx.finished_game = True
+
+ for i in range(6):
+ if scouted_hint_flags[0] & hint_bits[i]:
+ if i not in self.hint_list:
+ scoutable_hint = await snes_read(ctx, HINT_SCOUNT_IDS + (i * 3), 3)
+ if not scoutable_hint[2]:
+ scoutable_hint = (int.from_bytes(scoutable_hint[:2], byteorder="little") + 0xEB0000)
+ self.hint_list.append(i)
+ await ctx.send_msgs([{"cmd": "CreateHints", "locations": [scoutable_hint], "player": ctx.player}])
+ else:
+ hint = self.slot_data['hint_man_hints'][i]
+ await ctx.send_msgs([{"cmd": "CreateHints", "locations": [hint[0]], "player": hint[1]}])
+ self.hint_list.append(i)
+
+ if shop_scout[0] and shop_scouts_enabled[0]:
+ shop_slots = []
+ for i in range(7):
+ slot_id = (0xEB0FF9 + (shop_scout[0] * 7) + i)
+ if slot_id in ctx.server_locations and slot_id not in self.hinted_shop_locations:
+ shop_slots.append(slot_id)
+
+ if shop_slots:
+ if shop_scouts_enabled[0] == 2:
+ await ctx.send_msgs([{"cmd": "CreateHints", "locations": shop_slots, "player": ctx.slot}])
+ await snes_write(ctx, [(WRAM_START + 0x0770, bytes([0x00]))])
+ else:
+ prog_shops = []
+ await ctx.send_msgs([{"cmd": "LocationScouts", "locations": shop_slots, "create_as_hint": 0}])
+ for location in shop_slots:
+ if location in ctx.locations_info:
+ self.hinted_shop_locations.append(location)
+ if ctx.locations_info[location].flags & 0x01:
+ prog_shops.append(location)
+ if prog_shops:
+ await ctx.send_msgs([{"cmd": "CreateHints", "locations": prog_shops, "player": ctx.slot}])
+
+ melody_data = f"{ctx.team}_{ctx.slot}_melody_status"
+ earth_power_data = f"{ctx.team}_{ctx.slot}_earthpower"
+ current_melodies = int.from_bytes(melody_table, "little")
+ earth_power_state = int.from_bytes(earth_power_absorbed, "little")
+
+ if melody_data not in ctx.stored_data or (ctx.stored_data[melody_data] != current_melodies) or (ctx.stored_data[earth_power_data] != earth_power_state):
+ await ctx.send_msgs([{
+ "cmd": "Set",
+ "key": melody_data,
+ "default": None,
+ "want_reply": True,
+ "operations": [{"operation": "replace", "value": int.from_bytes(melody_table, "little")}]},
+ {
+ "cmd": "Set",
+ "key": earth_power_data,
+ "default": None,
+ "want_reply": True,
+ "operations": [{"operation": "replace", "value": int.from_bytes(earth_power_absorbed, "little")}]
+ }])
+
+ # death link handling goes here
+ if "DeathLink" in ctx.tags and ctx.last_death_link + 1 < time.time():
+ send_deathlink = await snes_read(ctx, PLAYER_JUST_DIED_SEND_DEATHLINK, 1)
+ currently_dead = send_deathlink[0] != 0x00
+ if send_deathlink[0] != 0x00:
+ snes_buffered_write(ctx, PLAYER_JUST_DIED_SEND_DEATHLINK, bytes([0x00]))
+ await ctx.handle_deathlink_state(currently_dead)
+
+ new_checks = []
+ from .game_data.local_data import check_table
+
+ location_ram_data = await snes_read(ctx, WRAM_START + 0x9C00, 0x88)
+ shop_location_flags = await snes_read(ctx, WRAM_START + 0xB721, 0x41)
+ for loc_id, loc_data in check_table.items():
+ if loc_id not in ctx.locations_checked:
+ if loc_id >= 0xEB1000:
+ data = shop_location_flags[loc_data[0]]
+ else:
+ data = location_ram_data[loc_data[0]]
+ masked_data = data & (1 << loc_data[1])
+ bit_set = masked_data != 0
+ invert_bit = ((len(loc_data) >= 3) and loc_data[2])
+ if bit_set != invert_bit and loc_id in ctx.server_locations:
+ if text_open[0] == 0xFF or shop_scout[0]: # Don't check locations while in a textbox
+ new_checks.append(loc_id)
+
+ for new_check_id in new_checks:
+ ctx.locations_checked.add(new_check_id)
+ location = ctx.location_names.lookup_in_slot(new_check_id)
+ snes_logger.info(
+ f'New Check: {location} ({len(ctx.locations_checked)}/{len(ctx.missing_locations) + len(ctx.checked_locations)})')
+ await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": [new_check_id]}])
+ await snes_write(ctx, [(WRAM_START + 0x0770, bytes([0]))])
+
+ if item_received[0] or special_received[0] != 0x00 or money_received[0] != 0x00: # If processing any item from the server
+ return
+
+ is_energylink_enabled = await snes_read(ctx, IS_ENERGYLINK_ENABLED, 1)
+ is_requesting_energy = await snes_read(ctx, WRAM_START + 0x0790, 1)
+ energy_withdrawal = await snes_read(ctx, WRAM_START + 0x0796, 4)
+ ctx.set_notify(f"EnergyLink{ctx.team}")
+ energy = ctx.stored_data.get(f"EnergyLink{ctx.team}", 0)
+ exchange_rate = 1000000
+ if is_energylink_enabled[0]:
+
+ deposited_energy = int.from_bytes(outgoing_energy, byteorder="little")
+ if deposited_energy:
+ deposited_energy *= exchange_rate
+ await snes_write(ctx, [(MONEY_IN_BANK, (0x00).to_bytes(4, byteorder="little"))])
+ await ctx.send_msgs([{
+ "cmd": "Set", "key": f"EnergyLink{ctx.team}", "slot": ctx.slot, "operations":
+ [{"operation": "add", "value": deposited_energy},
+ {"operation": "max", "value": 0}]}])
+
+ if is_requesting_energy[0] and energy: # This is just to pull the current number for a display.
+ energy //= exchange_rate
+ if energy > 9999999:
+ energy = 9999999
+ cap_flag = await snes_read(ctx, WRAM_START + 0xB623, 1)
+ cap_flag = int.from_bytes(cap_flag)
+ cap_flag |= 0x20
+ await snes_write(ctx, [(WRAM_START + 0xB623, cap_flag.to_bytes(1, byteorder="little"))])
+
+ await snes_write(ctx, [(WRAM_START + 0x0792, int(energy).to_bytes(4, byteorder="little"))])
+ await snes_write(ctx, [(WRAM_START + 0x0790, (0x00).to_bytes(1, byteorder="little"))])
+
+ if any(energy_withdrawal) and energy:
+ withdrawal = int.from_bytes(energy_withdrawal, byteorder="little")
+ withdrawal *= exchange_rate
+ energy = ctx.stored_data.get(f"EnergyLink{ctx.team}", 0) # Refresh the value
+
+ if withdrawal > energy:
+ energy_success = 2
+ withdrawal = energy
+ else:
+ energy_success = 1
+
+ await snes_write(ctx, [(WRAM_START + 0x97D0, (withdrawal // exchange_rate).to_bytes(4, byteorder="little"))])
+ await snes_write(ctx, [(WRAM_START + 0x0796, (0x00).to_bytes(4, byteorder="little"))])
+ await ctx.send_msgs([{
+ "cmd": "Set", "key": f"EnergyLink{ctx.team}", "slot": ctx.slot, "operations":
+ [{"operation": "add", "value": (withdrawal * -1)},
+ {"operation": "max", "value": 0}]}])
+ await snes_write(ctx, [(WRAM_START + 0x079A, energy_success.to_bytes(1, byteorder="little"))]) # Signal the game to continue
+
+ if cur_script[0]: # Stop items during cutscenes
+ return
+
+ recv_count = await snes_read(ctx, ITEMQUEUE_HIGH, 2)
+ recv_index = struct.unpack("H", recv_count)[0]
+ if recv_index < len(ctx.items_received):
+ item = ctx.items_received[recv_index]
+ item_id = (item.item - 0xEB0000)
+ recv_index += 1
+ logging.info('Received %s from %s (%s) (%d/%d in list)' % (
+ color(ctx.item_names.lookup_in_slot(item.item), "red", "bold"),
+ color(ctx.player_names[item.player], 'yellow'),
+ ctx.location_names.lookup_in_slot(item.location, item.player), recv_index, len(ctx.items_received)))
+
+ snes_buffered_write(ctx, ITEMQUEUE_HIGH, pack("H", recv_index))
+ if item_id <= 0xFD:
+ snes_buffered_write(ctx, WRAM_START + 0xB570, bytes([item_id]))
+ elif item_id in money_id_table:
+ snes_buffered_write(ctx, WRAM_START + 0xB5F1, bytes([list(money_id_table).index(item_id) + 1]))
+ else:
+ snes_buffered_write(ctx, WRAM_START + 0xB572, bytes([client_specials[item_id]]))
+
+ await snes_flush_writes(ctx)
+
+
+def get_alias(alias: str, slot_name: str) -> str:
+ try:
+ index = alias.index(f" ({slot_name}")
+ except ValueError:
+ return alias
+ return alias[:index]
diff --git a/worlds/earthbound/Items.py b/worlds/earthbound/Items.py
new file mode 100644
index 000000000000..ed57aced1111
--- /dev/null
+++ b/worlds/earthbound/Items.py
@@ -0,0 +1,337 @@
+from typing import Dict, Set, NamedTuple, Optional
+from BaseClasses import ItemClassification
+
+
+class ItemData(NamedTuple):
+ category: str
+ code: Optional[int]
+ classification: ItemClassification
+ amount: int = 1
+
+
+item_table: Dict[str, ItemData] = {
+ "Franklin Badge": ItemData("Key Items", 0xEB0001, ItemClassification.progression),
+ "Teddy Bear": ItemData("Characters", 0xEB0002, ItemClassification.filler, 0),
+ "Super Plush Bear": ItemData("Characters", 0xEB0003, ItemClassification.useful, 0),
+
+ "Broken Machine": ItemData("Broken Items", 0xEB0004, ItemClassification.useful, 0),
+ "Broken Gadget": ItemData("Jeff Weapons", 0xEB0005, ItemClassification.useful, 0),
+ "Broken Air Gun": ItemData("Jeff Weapons", 0xEB0006, ItemClassification.filler, 0),
+ "Broken Spray Can": ItemData("Broken Items", 0xEB0007, ItemClassification.filler, 0),
+ "Broken Laser": ItemData("Jeff Weapons", 0xEB0008, ItemClassification.useful, 0),
+ "Broken Iron": ItemData("Broken Items", 0xEB0009, ItemClassification.filler, 0),
+ "Broken Pipe": ItemData("Broken Items", 0xEB000A, ItemClassification.useful, 0),
+ "Broken Cannon": ItemData("Jeff Weapons", 0xEB000B, ItemClassification.useful, 0),
+ "Broken Tube": ItemData("Broken Items", 0xEB000C, ItemClassification.useful, 0),
+ "Broken Bazooka": ItemData("Broken Items", 0xEB000D, ItemClassification.useful, 0),
+ "Broken Trumpet": ItemData("Broken Items", 0xEB000E, ItemClassification.filler, 0),
+ "Broken Harmonica": ItemData("Jeff Weapons", 0xEB000F, ItemClassification.useful, 0),
+ "Broken Antenna": ItemData("Jeff Weapons", 0xEB0010, ItemClassification.useful, 0),
+
+ "Cracked Bat": ItemData("Ness Weapons", 0xEB0011, ItemClassification.filler, 0),
+ "Tee Ball Bat": ItemData("Ness Weapons", 0xEB0012, ItemClassification.filler, 0),
+ "Sand Lot Bat": ItemData("Ness Weapons", 0xEB0013, ItemClassification.filler, 0),
+ "Minor League Bat": ItemData("Ness Weapons", 0xEB0014, ItemClassification.filler, 0),
+ "Mr. Baseball Bat": ItemData("Ness Weapons", 0xEB0015, ItemClassification.useful, 0),
+ "Big League Bat": ItemData("Ness Weapons", 0xEB00D5, ItemClassification.useful, 0),
+ "Hall of Fame Bat": ItemData("Ness Weapons", 0xEB0017, ItemClassification.useful, 0),
+ "Magicant Bat": ItemData("Ness Weapons", 0xEB0018, ItemClassification.useful),
+ "Legendary Bat": ItemData("Ness Weapons", 0xEB0019, ItemClassification.useful),
+ "Gutsy Bat": ItemData("Ness Weapons", 0xEB001A, ItemClassification.useful, 0),
+ "Casey Bat": ItemData("Ness Weapons", 0xEB001B, ItemClassification.filler, 0),
+
+ "Fry Pan": ItemData("Paula Weapons", 0xEB001C, ItemClassification.filler, 0),
+ "Thick Fry Pan": ItemData("Paula Weapons", 0xEB001D, ItemClassification.filler, 0),
+ "Deluxe Fry Pan": ItemData("Paula Weapons", 0xEB001E, ItemClassification.filler, 0),
+ "Chef's Fry Pan": ItemData("Paula Weapons", 0xEB001F, ItemClassification.useful, 0),
+ "French Fry Pan": ItemData("Paula Weapons", 0xEB0020, ItemClassification.useful, 0),
+ "Magic Fry Pan": ItemData("Paula Weapons", 0xEB0021, ItemClassification.useful, 0),
+ "Holy Fry Pan": ItemData("Paula Weapons", 0xEB0022, ItemClassification.useful, 0),
+
+ "Sword of Kings": ItemData("Poo Weapons", 0xEB0023, ItemClassification.useful, 0),
+
+ "Pop Gun": ItemData("Jeff Weapons", 0xEB0024, ItemClassification.filler),
+ "Stun Gun": ItemData("Jeff Weapons", 0xEB0025, ItemClassification.filler),
+ "Toy Air Gun": ItemData("Jeff Weapons", 0xEB0026, ItemClassification.filler, 0),
+ "Magnum Air Gun": ItemData("Jeff Weapons", 0xEB0027, ItemClassification.filler, 0),
+ "Zip Gun": ItemData("Jeff Weapons", 0xEB0028, ItemClassification.filler, 0),
+ "Laser Gun": ItemData("Jeff Weapons", 0xEB0029, ItemClassification.filler, 0),
+ "Hyper Beam": ItemData("Jeff Weapons", 0xEB002A, ItemClassification.useful, 0),
+ "Crusher Beam": ItemData("Jeff Weapons", 0xEB002B, ItemClassification.useful, 0),
+ "Spectrum Beam": ItemData("Jeff Weapons", 0xEB002C, ItemClassification.useful, 0),
+ "Death Ray": ItemData("Jeff Weapons", 0xEB002D, ItemClassification.useful),
+ "Baddest Beam": ItemData("Jeff Weapons", 0xEB002E, ItemClassification.useful, 0),
+ "Moon Beam Gun": ItemData("Jeff Weapons", 0xEB002F, ItemClassification.useful),
+ "Gaia Beam": ItemData("Jeff Weapons", 0xEB0030, ItemClassification.useful, 0),
+
+ "Yo-yo": ItemData("Alt Weapons", 0xEB0031, ItemClassification.filler, 0),
+ "Slingshot": ItemData("Alt Weapons", 0xEB0032, ItemClassification.filler, 0),
+ "Bionic Slingshot": ItemData("Alt Weapons", 0xEB0033, ItemClassification.filler, 0),
+ "Trick Yo-yo": ItemData("Alt Weapons", 0xEB0034, ItemClassification.filler, 0),
+ "Combat Yo-yo": ItemData("Alt Weapons", 0xEB0035, ItemClassification.filler, 0),
+
+ "Travel Charm": ItemData("Body Equipment", 0xEB0036, ItemClassification.filler),
+ "Great Charm": ItemData("Body Equipment", 0xEB0037, ItemClassification.filler),
+ "Crystal Charm": ItemData("Body Equipment", 0xEB0038, ItemClassification.filler, 0),
+ "Rabbit's Foot": ItemData("Body Equipment", 0xEB0039, ItemClassification.useful),
+ "Flame Pendant": ItemData("Body Equipment", 0xEB003A, ItemClassification.useful),
+ "Rain Pendant": ItemData("Body Equipment", 0xEB003B, ItemClassification.useful),
+ "Night Pendant": ItemData("Body Equipment", 0xEB003C, ItemClassification.useful),
+ "Sea Pendant": ItemData("Body Equipment", 0xEB003D, ItemClassification.useful),
+ "Star Pendant": ItemData("Body Equipment", 0xEB003E, ItemClassification.useful, 0),
+ "Cloak of Kings": ItemData("Poo Equipment", 0xEB003F, ItemClassification.useful),
+
+ "Cheap Bracelet": ItemData("Arm Equipment", 0xEB0040, ItemClassification.filler, 0),
+ "Copper Bracelet": ItemData("Arm Equipment", 0xEB0041, ItemClassification.filler, 0),
+ "Silver Bracelet": ItemData("Arm Equipment", 0xEB0042, ItemClassification.filler, 0),
+ "Gold Bracelet": ItemData("Arm Equipment", 0xEB0043, ItemClassification.filler, 0),
+ "Platinum Band": ItemData("Arm Equipment", 0xEB00D8, ItemClassification.useful),
+ "Diamond Band": ItemData("Arm Equipment", 0xEB00D9, ItemClassification.useful),
+ "Pixie's Bracelet": ItemData("Arm Equipment", 0xEB0046, ItemClassification.useful),
+ "Cherub's Band": ItemData("Arm Equipment", 0xEB0047, ItemClassification.useful),
+ "Goddess Band": ItemData("Arm Equipment", 0xEB0048, ItemClassification.useful),
+ "Bracer of Kings": ItemData("Poo Equipment", 0xEB0049, ItemClassification.useful),
+
+ "Baseball Cap": ItemData("Other Equipment", 0xEB004A, ItemClassification.filler, 0),
+ "Holmes Hat": ItemData("Other Equipment", 0xEB004B, ItemClassification.filler, 0),
+ "Mr. Baseball Cap": ItemData("Other Equipment", 0xEB004C, ItemClassification.filler, 0),
+ "Hard Hat": ItemData("Other Equipment", 0xEB004D, ItemClassification.filler, 0),
+ "Ribbon": ItemData("Ribbons", 0xEB004E, ItemClassification.filler, 0),
+ "Red Ribbon": ItemData("Ribbons", 0xEB004F, ItemClassification.filler, 0),
+ "Goddess Ribbon": ItemData("Ribbons", 0xEB0050, ItemClassification.useful, 0),
+ "Coin of Slumber": ItemData("Other Equipment", 0xEB0051, ItemClassification.useful),
+ "Coin of Defense": ItemData("Other Equipment", 0xEB0052, ItemClassification.useful, 0),
+ "Lucky Coin": ItemData("Other Equipment", 0xEB0053, ItemClassification.useful, 0),
+ "Talisman Coin": ItemData("Other Equipment", 0xEB0054, ItemClassification.useful, 0),
+ "Shiny Coin": ItemData("Other Equipment", 0xEB0055, ItemClassification.useful, 0),
+ "Souvenir Coin": ItemData("Other Equipment", 0xEB0056, ItemClassification.useful),
+ "Diadem of Kings": ItemData("Poo Equipment", 0xEB0057, ItemClassification.useful),
+
+ "Cookie": ItemData("Food", 0xEB0058, ItemClassification.filler, 0),
+ "Bag of Fries": ItemData("Food", 0xEB0059, ItemClassification.filler, 0),
+ "Hamburger": ItemData("Food", 0xEB005A, ItemClassification.filler, 0),
+ "Boiled Egg": ItemData("Food", 0xEB005B, ItemClassification.filler, 0),
+ "Fresh Egg": ItemData("Food", 0xEB005C, ItemClassification.filler, 0),
+ "Picnic Lunch": ItemData("Food", 0xEB005D, ItemClassification.filler, 0),
+ "Pasta di Summers": ItemData("Food", 0xEB005E, ItemClassification.filler, 0),
+ "Pizza": ItemData("Food", 0xEB005F, ItemClassification.filler, 0),
+ "Chef's Special": ItemData("Food", 0xEB0060, ItemClassification.filler, 0),
+ "Large Pizza": ItemData("Food", 0xEB0061, ItemClassification.filler, 0),
+ "PSI Caramel": ItemData("Food", 0xEB0062, ItemClassification.useful, 0),
+ "Magic Truffle": ItemData("Food", 0xEB0063, ItemClassification.useful, 0),
+ "Brain Food Lunch": ItemData("Food", 0xEB0064, ItemClassification.useful, 0),
+ "Rock Candy": ItemData("Food", 0xEB0065, ItemClassification.useful, 0),
+ "Croissant": ItemData("Food", 0xEB0066, ItemClassification.filler, 0),
+ "Bread Roll": ItemData("Food", 0xEB0067, ItemClassification.filler, 0),
+
+ "Pak of Bubble Gum": ItemData("Key Items", 0xEB0068, ItemClassification.progression),
+ "Jar of Fly Honey": ItemData("Key Items", 0xEB0069, ItemClassification.progression),
+
+ "Can of Fruit Juice": ItemData("Food", 0xEB006A, ItemClassification.filler, 0),
+ "Royal Iced Tea": ItemData("Food", 0xEB006B, ItemClassification.filler, 0),
+ "Protein Drink": ItemData("Food", 0xEB006C, ItemClassification.filler, 0),
+ "Kraken Soup": ItemData("Food", 0xEB006D, ItemClassification.filler, 0),
+ "Bottle of Water": ItemData("Food", 0xEB006E, ItemClassification.filler, 0),
+ "Cold Remedy": ItemData("Status Heal", 0xEB006F, ItemClassification.filler, 0),
+ "Vial of Serum": ItemData("Status Heal", 0xEB0070, ItemClassification.filler, 0),
+ "IQ Capsule": ItemData("Food", 0xEB0071, ItemClassification.useful, 0),
+ "Guts Capsule": ItemData("Food", 0xEB0072, ItemClassification.useful, 0),
+ "Speed Capsule": ItemData("Food", 0xEB0073, ItemClassification.useful, 0),
+ "Vital Capsule": ItemData("Food", 0xEB0074, ItemClassification.useful, 0),
+ "Luck Capsule": ItemData("Food", 0xEB0075, ItemClassification.useful, 0),
+ "Ketchup Packet": ItemData("Condiments", 0xEB0076, ItemClassification.filler, 0),
+ "Sugar Packet": ItemData("Condiments", 0xEB0077, ItemClassification.filler, 0),
+ "Tin of Cocoa": ItemData("Condiments", 0xEB0078, ItemClassification.filler, 0),
+ "Carton of Cream": ItemData("Condiments", 0xEB0079, ItemClassification.filler, 0),
+ "Sprig of Parsley": ItemData("Condiments", 0xEB007A, ItemClassification.filler, 0),
+ "Jar of Hot Sauce": ItemData("Condiments", 0xEB007B, ItemClassification.filler, 0),
+ "Salt Packet": ItemData("Condiments", 0xEB007C, ItemClassification.filler, 0),
+ "Tiny Key": ItemData("Key Items", 0xEB007D, ItemClassification.progression), # Progressive Gun
+ "Jar of Delisauce": ItemData("Condiments", 0xEB007E, ItemClassification.useful, 0),
+ "Wet Towel": ItemData("Status Heal", 0xEB007F, ItemClassification.filler, 0),
+ "Refreshing Herb": ItemData("Status Heal", 0xEB0080, ItemClassification.useful, 0),
+ "Secret Herb": ItemData("Status Heal", 0xEB0081, ItemClassification.useful, 0),
+ "Horn of Life": ItemData("Status Heal", 0xEB0082, ItemClassification.useful, 0),
+
+ "Counter-PSI Unit": ItemData("Jeff Items", 0xEB0083, ItemClassification.useful, 0),
+ "Shield Killer": ItemData("Jeff Items", 0xEB0084, ItemClassification.useful, 0),
+ "Bazooka": ItemData("Jeff Items", 0xEB0085, ItemClassification.useful, 0),
+ "Heavy Bazooka": ItemData("Jeff Items", 0xEB0086, ItemClassification.useful, 0),
+ "HP-Sucker": ItemData("Jeff Items", 0xEB0087, ItemClassification.useful),
+ "Hungry HP-Sucker": ItemData("Jeff Items", 0xEB0088, ItemClassification.useful, 0),
+ "Xterminator Spray": ItemData("Battle Items", 0xEB0089, ItemClassification.useful, 0),
+ "Slime Generator": ItemData("Jeff Items", 0xEB008A, ItemClassification.useful, 0),
+ "Yogurt Dispenser": ItemData("Key Items", 0xEB008B, ItemClassification.progression),
+
+ "Ruler": ItemData("Battle Items", 0xEB008C, ItemClassification.filler, 0),
+ "Snake Bag": ItemData("Battle Items", 0xEB008D, ItemClassification.filler, 0),
+ "Mummy Wrap": ItemData("Battle Items", 0xEB008E, ItemClassification.filler, 0),
+ "Protractor": ItemData("Battle Items", 0xEB008F, ItemClassification.filler, 0),
+ "Bottle Rocket": ItemData("Jeff Items", 0xEB0090, ItemClassification.filler, 0),
+ "Big Bottle Rocket": ItemData("Jeff Items", 0xEB0091, ItemClassification.useful, 0),
+ "Multi Bottle Rocket": ItemData("Jeff Items", 0xEB0092, ItemClassification.useful, 0),
+ "Bomb": ItemData("Battle Items", 0xEB0093, ItemClassification.filler, 0),
+ "Super Bomb": ItemData("Battle Items", 0xEB0094, ItemClassification.useful, 0),
+ "Insecticide Spray": ItemData("Battle Items", 0xEB0095, ItemClassification.filler, 0),
+ "Rust Promoter": ItemData("Battle Items", 0xEB0096, ItemClassification.filler, 0),
+ "Rust Promoter DX": ItemData("Battle Items", 0xEB0097, ItemClassification.useful, 0),
+ "Pair of Dirty Socks": ItemData("Battle Items", 0xEB0098, ItemClassification.filler, 0),
+ "Stag Beetle": ItemData("Battle Items", 0xEB0099, ItemClassification.filler, 0),
+ "Toothbrush": ItemData("Battle Items", 0xEB009A, ItemClassification.filler, 0),
+ "Handbag Strap": ItemData("Battle Items", 0xEB009B, ItemClassification.filler, 0),
+ "Pharaoh's Curse": ItemData("Battle Items", 0xEB009C, ItemClassification.filler, 0),
+ "Defense Shower": ItemData("Battle Items", 0xEB009D, ItemClassification.useful, 0),
+
+ "UFO Engine": ItemData("Key Items", 0xEB009E, ItemClassification.progression),
+ "Sudden Guts Pill": ItemData("Battle Items", 0xEB009F, ItemClassification.useful, 0),
+ "Bag of Dragonite": ItemData("Battle Items", 0xEB00A0, ItemClassification.useful, 0),
+ "Defense Spray": ItemData("Battle Items", 0xEB00A1, ItemClassification.filler, 0),
+
+ "Piggy Nose": ItemData("Key Items", 0xEB00A2, ItemClassification.progression),
+ "For Sale Sign": ItemData("Field Items", 0xEB00A3, ItemClassification.filler),
+ "Shyness Book": ItemData("Key Items", 0xEB00A4, ItemClassification.progression),
+ "Picture Postcard": ItemData("Field Items", 0xEB00A5, ItemClassification.filler, 0),
+ "King Banana": ItemData("Key Items", 0xEB00A6, ItemClassification.progression),
+ "Letter For Tony": ItemData("Key Items", 0xEB00A7, ItemClassification.progression),
+ "Chick": ItemData("Field Items", 0xEB00A8, ItemClassification.filler, 0),
+ "Chicken": ItemData("Field Items", 0xEB00A9, ItemClassification.filler, 0),
+ "Key to the Shack": ItemData("Key Items", 0xEB00AA, ItemClassification.progression),
+ "Key to the Cabin": ItemData("Key Items", 0xEB00AB, ItemClassification.progression),
+ "Bad Key Machine": ItemData("Key Items", 0xEB00AC, ItemClassification.progression),
+ # "Archipelago Item": ItemData("Key Items", 0xEB00AD, ItemClassification.progression, 0),
+ "Zombie Paper": ItemData("Key Items", 0xEB00AE, ItemClassification.progression),
+ "Hawk Eye": ItemData("Key Items", 0xEB00AF, ItemClassification.progression),
+ "Bicycle": ItemData("Key Items", 0xEB00B0, ItemClassification.useful),
+ "ATM Card": ItemData("Key Items", 0xEB00B1, ItemClassification.progression, 0),
+ "Show Ticket": ItemData("Key Items", 0xEB00B2, ItemClassification.filler, 0),
+ "Tenda Lavapants": ItemData("Key Items", 0xEB00B3, ItemClassification.progression), # Progressive Bat
+ "Wad of Bills": ItemData("Key Items", 0xEB00B4, ItemClassification.progression),
+ "Warp Pad": ItemData("Key Items", 0xEB00B5, ItemClassification.progression, 0),
+ "Diamond": ItemData("Key Items", 0xEB00B6, ItemClassification.progression),
+ "Signed Banana": ItemData("Key Items", 0xEB00B7, ItemClassification.progression),
+ "Pencil Eraser": ItemData("Key Items", 0xEB00B8, ItemClassification.progression),
+ "Hieroglyph Copy": ItemData("Key Items", 0xEB00B9, ItemClassification.progression),
+ "Meteotite": ItemData("Field Items", 0xEB00BA, ItemClassification.useful, 0),
+ "Contact Lens": ItemData("Key Items", 0xEB00BB, ItemClassification.progression),
+
+ "Hand-Aid": ItemData("Food", 0xEB00BC, ItemClassification.useful),
+ "Trout Yogurt": ItemData("Food", 0xEB00BD, ItemClassification.filler, 0),
+ "Banana": ItemData("Food", 0xEB00BE, ItemClassification.filler, 0),
+ "Calorie Stick": ItemData("Food", 0xEB00BF, ItemClassification.filler, 0),
+ "Key to the Tower": ItemData("Key Items", 0xEB00C0, ItemClassification.progression),
+ "Meteorite Piece": ItemData("Key Items", 0xEB00C1, ItemClassification.progression),
+
+ "Earth Pendant": ItemData("Body Equipment", 0xEB00C2, ItemClassification.useful, 0),
+ "Neutralizer": ItemData("Jeff Items", 0xEB00C3, ItemClassification.useful),
+ "Sound Stone": ItemData("Key Items", 0xEB00C4, ItemClassification.progression, 0),
+ "Exit Mouse": ItemData("Key Items", 0xEB00C5, ItemClassification.useful, 0),
+
+ "Gelato de Resort": ItemData("Food", 0xEB00C6, ItemClassification.filler, 0),
+ "Snake": ItemData("Battle Items", 0xEB00C7, ItemClassification.filler, 0),
+ "Viper": ItemData("Battle Items", 0xEB00C8, ItemClassification.filler, 0),
+ "Brain Stone": ItemData("Battle Items", 0xEB00C9, ItemClassification.filler),
+ "Police Badge": ItemData("Key Items", 0xEB00CA, ItemClassification.progression),
+ "Mining Permit": ItemData("Key Items", 0xEB00CB, ItemClassification.progression),
+ "Suporma": ItemData("Field Items", 0xEB00CC, ItemClassification.trap),
+ "Key to the Locker": ItemData("Key Items", 0xEB00CD, ItemClassification.progression),
+ "Insignificant Item": ItemData("Key Items", 0xEB00CE, ItemClassification.progression),
+ "Magic Tart": ItemData("Food", 0xEB00CF, ItemClassification.useful, 0),
+ "Tiny Ruby": ItemData("Key Items", 0xEB00D0, ItemClassification.progression),
+ "Monkey's Love": ItemData("Battle Items", 0xEB00D1, ItemClassification.useful),
+ "Eraser Eraser": ItemData("Key Items", 0xEB00D2, ItemClassification.progression),
+ "Tendakraut": ItemData("Key Items", 0xEB00D3, ItemClassification.progression),
+
+ "T-Rex's Bat": ItemData("Ness Weapons", 0xEB00D4, ItemClassification.useful, 0),
+ # "Big League Bat": ItemData("Ness Weapons", 0xEB0016, ItemClassification.useful, 0), Summers copy
+ "Ultimate Bat": ItemData("Ness Weapons", 0xEB00D6, ItemClassification.useful, 0),
+ "Double Beam": ItemData("Jeff Weapons", 0xEB00D7, ItemClassification.useful, 0),
+ # "Platinum Band": ItemData("Arm Equipment", 0xEB00D8, ItemClassification.useful, 0), Summers copy
+ # "Diamond Band": ItemData("Arm Equipment", 0xEB00D9, ItemClassification.useful, 0), Summers Copy
+ "Defense Ribbon": ItemData("Ribbons", 0xEB00DA, ItemClassification.useful, 0),
+ "Talisman Ribbon": ItemData("Ribbons", 0xEB00DB, ItemClassification.useful),
+ "Saturn Ribbon": ItemData("Ribbons", 0xEB00DC, ItemClassification.useful),
+ "Coin of Silence": ItemData("Other Equipment", 0xEB00DD, ItemClassification.useful, 0),
+ "Charm Coin": ItemData("Other Equipment", 0xEB00DE, ItemClassification.useful, 0),
+
+ "Cup of Noodles": ItemData("Food", 0xEB00DF, ItemClassification.filler, 0),
+ "Repel Sandwich": ItemData("Food", 0xEB00E0, ItemClassification.useful, 0),
+ "Repel Superwich": ItemData("Food", 0xEB00E1, ItemClassification.useful, 0),
+ "Lucky Sandwich": ItemData("Food", 0xEB00E2, ItemClassification.useful, 0),
+ "Progressive Bat": ItemData("Progressive Equipment", 0xEB00E3, ItemClassification.useful, 0),
+ "Progressive Fry Pan": ItemData("Progressive Equipment", 0xEB00E4, ItemClassification.useful, 0),
+ "Progressive Gun": ItemData("Progressive Equipment", 0xEB00E5, ItemClassification.useful, 0),
+ "Progressive Bracelet": ItemData("Progressive Equipment", 0xEB00E6, ItemClassification.useful, 0),
+ "Progressive Other": ItemData("Progressive Equipment", 0xEB00E7, ItemClassification.useful, 0),
+
+ "Cup of Coffee": ItemData("Food", 0xEB00E8, ItemClassification.filler, 0),
+ "Double Burger": ItemData("Food", 0xEB00E9, ItemClassification.filler, 0),
+ "Peanut Cheese Bar": ItemData("Food", 0xEB00EA, ItemClassification.filler, 0),
+ "Piggy Jelly": ItemData("Food", 0xEB00EB, ItemClassification.filler, 0),
+ "Bowl of Rice Gruel": ItemData("Food", 0xEB00EC, ItemClassification.filler, 0),
+ "Bean Croquette": ItemData("Food", 0xEB00ED, ItemClassification.filler, 0),
+ "Molokheiya Soup": ItemData("Food", 0xEB00EE, ItemClassification.filler, 0),
+ "Plain Roll": ItemData("Food", 0xEB00EF, ItemClassification.filler, 0),
+ "Kabob": ItemData("Food", 0xEB00F0, ItemClassification.filler, 0),
+ "Plain Yogurt": ItemData("Food", 0xEB00F1, ItemClassification.filler, 0),
+ "Beef Jerky": ItemData("Food", 0xEB00F2, ItemClassification.filler, 0),
+ "Mammoth Burger": ItemData("Food", 0xEB00F3, ItemClassification.filler, 0),
+ "Spicy Jerky": ItemData("Food", 0xEB00F4, ItemClassification.filler, 0),
+ "Luxury Jerky": ItemData("Food", 0xEB00F5, ItemClassification.filler, 0),
+ "Bottle of DXwater": ItemData("Food", 0xEB00F6, ItemClassification.useful, 0),
+ "Magic Pudding": ItemData("Food", 0xEB00F7, ItemClassification.useful, 0),
+
+ "Non-Stick Frypan": ItemData("Paula Weapons", 0xEB00F8, ItemClassification.useful, 0),
+ "Mr. Saturn Coin": ItemData("Other Equipment", 0xEB00F9, ItemClassification.useful),
+ "Meteornium": ItemData("Field Items", 0xEB00FA, ItemClassification.useful, 0),
+ "Popsicle": ItemData("Food", 0xEB00FB, ItemClassification.filler, 0),
+ "Cup of Lifenoodles": ItemData("Status Heal", 0xEB00FC, ItemClassification.useful, 0),
+ "Carrot Key": ItemData("Key Items", 0xEB00FD, ItemClassification.progression),
+
+ "Onett Teleport": ItemData("PSI", 0xEB00FE, ItemClassification.progression),
+ "Twoson Teleport": ItemData("PSI", 0xEB00FF, ItemClassification.progression),
+ "Happy-Happy Village Teleport": ItemData("PSI", 0xEB0100, ItemClassification.progression),
+ "Threed Teleport": ItemData("PSI", 0xEB0101, ItemClassification.progression),
+ "Saturn Valley Teleport": ItemData("PSI", 0xEB0102, ItemClassification.progression),
+ "Dusty Dunes Teleport": ItemData("PSI", 0xEB0103, ItemClassification.progression),
+ "Fourside Teleport": ItemData("PSI", 0xEB0104, ItemClassification.progression),
+ "Winters Teleport": ItemData("PSI", 0xEB0105, ItemClassification.progression),
+ "Summers Teleport": ItemData("PSI", 0xEB0106, ItemClassification.progression),
+ "Scaraba Teleport": ItemData("PSI", 0xEB0107, ItemClassification.progression),
+ "Dalaam Teleport": ItemData("PSI", 0xEB0108, ItemClassification.progression),
+ "Deep Darkness Teleport": ItemData("PSI", 0xEB0109, ItemClassification.progression),
+ "Tenda Village Teleport": ItemData("PSI", 0xEB010A, ItemClassification.progression),
+ "Lost Underworld Teleport": ItemData("PSI", 0xEB010B, ItemClassification.progression),
+ "Progressive Poo PSI": ItemData("PSI", 0xEB010C, ItemClassification.useful, 2),
+ "Magicant Teleport": ItemData("PSI", 0xEB010D, ItemClassification.progression),
+
+ "Paula": ItemData("Characters", 0xEB010E, ItemClassification.progression),
+ "Jeff": ItemData("Characters", 0xEB010F, ItemClassification.progression),
+ "Poo": ItemData("Characters", 0xEB0110, ItemClassification.progression),
+ "Flying Man": ItemData("Characters", 0xEB0111, ItemClassification.useful),
+ "Ness": ItemData("Characters", 0xEB0112, ItemClassification.progression),
+ "Photograph": ItemData("Photos", 0xEB0113, ItemClassification.trap, 0),
+
+ "$10": ItemData("Money", 0xEB0114, ItemClassification.filler, 0),
+ "$100": ItemData("Money", 0xEB0115, ItemClassification.filler, 0),
+ "$1000": ItemData("Money", 0xEB0116, ItemClassification.useful, 0),
+
+ 'Threed Tunnels Clear': ItemData('Events', None, ItemClassification.progression, 0),
+ 'Submarine to Deep Darkness': ItemData('Events', None, ItemClassification.progression, 0),
+ 'Melody': ItemData('Events', None, ItemClassification.progression, 0),
+ 'Saved Earth': ItemData('Events', None, ItemClassification.progression, 0),
+ "Power of the Earth": ItemData("Events", None, ItemClassification.progression, 0),
+ "Alternate Goal": ItemData("Events", None, ItemClassification.useful, 0),
+ "Valley Bridge Repair": ItemData("Events", None, ItemClassification.progression, 0),
+ "Magicant Unlock": ItemData("Events", None, ItemClassification.progression, 0),
+ "ATM Access": ItemData("Events", None, ItemClassification.progression, 0)
+}
+
+
+def get_item_names_per_category() -> Dict[str, Set[str]]:
+ categories: Dict[str, Set[str]] = {}
+
+ for name, data in item_table.items():
+ if data.category != "Events":
+ categories.setdefault(data.category, set()).add(name)
+
+ return categories
diff --git a/worlds/earthbound/Locations.py b/worlds/earthbound/Locations.py
new file mode 100644
index 000000000000..8b5e5f78cd39
--- /dev/null
+++ b/worlds/earthbound/Locations.py
@@ -0,0 +1,594 @@
+from typing import List, Optional, NamedTuple, TYPE_CHECKING
+from .Options import MagicantMode, ShopRandomizer
+
+if TYPE_CHECKING:
+ from . import EarthBoundWorld
+
+
+class LocationData(NamedTuple):
+ region: str
+ name: str
+ code: Optional[int]
+
+
+def get_locations(world: "EarthBoundWorld") -> List[LocationData]:
+
+ location_table: List[LocationData] = [
+ LocationData("Northern Onett", "Onett - Tracy Gift", 0xEB0000),
+ LocationData("Northern Onett", "Onett - Tracy's Room Present", 0xEB0001),
+ LocationData("Northern Onett", "Onett - Hilltop Present", 0xEB0002),
+ LocationData("Northern Onett", "Onett - Meteor Item", 0xEB0003),
+ LocationData("Northern Onett", "Onett - Buzz Buzz", 0xEB0004),
+ LocationData("Northern Onett", "Onett - Mani Mani Statue", 0xEB0005),
+ LocationData("Onett", "Onett - Library Counter", 0xEB0006),
+ LocationData("Onett", "Onett - Library Bookshelf", 0xEB0007),
+ LocationData("Onett", "Onett - Burger Shop Trashcan", 0xEB0008),
+ LocationData("Onett", "Onett - Treehouse Guy", 0xEB0009),
+ LocationData("Onett", "Onett - South Road Present", 0xEB000A),
+ LocationData("Onett", "Onett - Hotel Trashcan", 0xEB000B),
+ LocationData("Onett", "Onett - Arcade Trashcan", 0xEB000C),
+ LocationData("Onett", "Onett - Mayor Pirkle", 0xEB000D),
+ LocationData("Onett", "Onett - Traveling Entertainer", 0xEB000E),
+ LocationData("Giant Step", "Giant Step - First Cave Present", 0xEB000F),
+ LocationData("Giant Step", "Giant Step - Floor 2 Cave Present", 0xEB0010),
+ LocationData("Giant Step", "Giant Step - Floor 3 Present", 0xEB0011),
+ LocationData("Twoson", "Twoson - Bike Shop Rental", 0xEB0012),
+ LocationData("Twoson", "Twoson - Antique Shop", 0xEB0013),
+ LocationData("Twoson", "Twoson - Paula's Room Present", 0xEB0014),
+ LocationData("Twoson", "Twoson - Apple Kid Trashcan", 0xEB0015),
+ LocationData("Twoson", "Twoson - South of Town Present", 0xEB0016),
+ LocationData("Twoson", "Twoson - Orange Kid Donation", 0xEB0017),
+ LocationData("Twoson", "Twoson - Apple Kid Invention", 0xEB0018),
+ LocationData("Twoson", "Twoson - Apple Kid's Mouse", 0xEB0019),
+ LocationData("Twoson", "Twoson - Paula's Mother", 0xEB001A),
+ LocationData("Everdred's House", "Twoson - Everdred Meeting", 0xEB001B),
+ LocationData("Twoson", "Twoson - Insignificant Location", 0xEB001C),
+ LocationData("Peaceful Rest Valley", "Peaceful Rest Valley - Split Hill Present", 0xEB001D),
+ LocationData("Peaceful Rest Valley", "Peaceful Rest Valley - Hill Nook Present", 0xEB001E),
+ LocationData("Peaceful Rest Valley", "Peaceful Rest Valley - South of Bridge Present", 0xEB001F),
+ LocationData("Peaceful Rest Valley", "Peaceful Rest Valley - Dead End Present", 0xEB0020),
+ LocationData("Peaceful Rest Valley", "Peaceful Rest Valley - River Overlook Present", 0xEB0021),
+ LocationData("Peaceful Rest Valley", "Peaceful Rest Valley - North Side Present", 0xEB0022),
+ LocationData("Happy-Happy Village", "Happy-Happy Village - Donation Lady", 0xEB0023),
+ LocationData("Happy-Happy HQ", "Happy-Happy Village - Right HQ Present", 0xEB0024),
+ LocationData("Happy-Happy HQ", "Happy-Happy Village - Left HQ Present", 0xEB0025),
+ LocationData("Happy-Happy Village", "Happy-Happy Village - Prisoner Item", 0xEB0026),
+ LocationData("Happy-Happy Village", "Happy-Happy Village - Prisoner", 0xEB0027),
+ LocationData("Happy-Happy HQ", "Happy-Happy Village - Defeat Carpainter", 0xEB0028),
+ LocationData("Lilliput Steps", "Lilliput Steps - Southwest Pool Present", 0xEB0029),
+ LocationData("Lilliput Steps", "Lilliput Steps - East Cliff Present", 0xEB002A),
+ LocationData("Lilliput Steps", "Lilliput Steps - North Stream Present", 0xEB002B),
+ LocationData("Boogey Tent", "Threed - Boogey Tent Trashcan", 0xEB002C),
+ LocationData("Threed", "Threed - Cemetery Trashcan", 0xEB002D),
+ LocationData("Threed", "Threed - Downtown Trashcan", 0xEB002E),
+ LocationData("Threed", "Threed - East Side Trashcan", 0xEB002F),
+ LocationData("Threed", "Threed - Northeast Shack Trashcan", 0xEB0030),
+ LocationData("Threed", "Threed - Hospital Drawer", 0xEB0031),
+ LocationData("Threed", "Threed - Zombie Prisoner", 0xEB0032),
+ LocationData("Threed Underground", "Threed Underground - Left Coffin", 0xEB0033),
+ LocationData("Threed Underground", "Threed Underground - Right Coffin", 0xEB0034),
+ LocationData("Grapefruit Falls", "Grapefruit Falls - South Present", 0xEB0035),
+ LocationData("Grapefruit Falls", "Grapefruit Falls - North Present", 0xEB0036),
+ LocationData("Grapefruit Falls", "Grapefruit Falls - Saturn Cave Present", 0xEB0037),
+ LocationData("Saturn Valley", "Saturn Valley - Ladder Present", 0xEB0038),
+ LocationData("Saturn Valley", "Saturn Valley - Trashcan #1", 0xEB0039),
+ LocationData("Saturn Valley", "Saturn Valley - Trashcan #2", 0xEB003A),
+ LocationData("Saturn Valley", "Saturn Valley - Trashcan #3", 0xEB003B),
+ LocationData("Upper Saturn Valley", "Saturn Valley - Saturn Coffee", 0xEB003C),
+ LocationData("Saturn Valley", "Saturn Valley - Post Belch Gift #1", 0xEB003D),
+ LocationData("Saturn Valley", "Saturn Valley - Post Belch Gift #2", 0xEB003E),
+ LocationData("Saturn Valley", "Saturn Valley - Post Belch Gift #3", 0xEB003F),
+ LocationData("Milky Well", "Milky Well - Cavern Present", 0xEB0040),
+ LocationData("Belch's Factory", "Belch's Factory - Top Right Room Trashcan", 0xEB0041),
+ LocationData("Belch's Factory", "Belch's Factory - Pit Room Trashcan #1", 0xEB0042),
+ LocationData("Belch's Factory", "Belch's Factory - Pit Room Trashcan #2", 0xEB0043),
+ LocationData("Belch's Factory", "Belch's Factory - Balcony Room Trashcan #1", 0xEB0044),
+ LocationData("Belch's Factory", "Belch's Factory - Balcony Room Trashcan #2", 0xEB0045),
+ LocationData("Belch's Factory", "Belch's Factory - Balcony Room Trashcan #3", 0xEB0046),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Northwest Corner Present", 0xEB0047),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - South Side Present", 0xEB0048),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Surrounding Rocks Present", 0xEB0049),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Black Sesame Present", 0xEB004A),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Oasis Present", 0xEB004B),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Northeast Corner Present", 0xEB004C),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - North Central Present", 0xEB004D),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Shining Spot", 0xEB004E),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - East Peninsula Present", 0xEB004F),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Mine Reward", 0xEB0050),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Many Present Room Present #1", 0xEB0051),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Many Present Room Present #2", 0xEB0052),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Many Present Room Present #3", 0xEB0053),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Many Present Room Present #4", 0xEB0054),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Many Present Room Present #5", 0xEB0055),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Many Present Room Present #6", 0xEB0056),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Many Present Room Present #7", 0xEB0057),
+ LocationData("Scaraba", "Scaraba - Snake Bag Salesman", 0xEB0058),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Upper Right Locker", 0xEB0059),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Upper Left Locker", 0xEB005A),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Bottom Right Locker", 0xEB005B),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Bottom Left Locker", 0xEB005C),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Maxwell Item", 0xEB005D),
+ LocationData("Snow Wood Boarding School", "Snow Wood - Bedroom", 0xEB005E),
+ LocationData("Winters", "Winters - Drugstore Saleswoman", 0xEB005F),
+ LocationData("Brickroad Maze", "Brick Road Maze - Top Path Present", 0xEB0060),
+ LocationData("Brickroad Maze", "Brick Road Maze - Guarded Present", 0xEB0061),
+ LocationData("Brickroad Maze", "Brick Road Maze - Out of the Way Present", 0xEB0062),
+ LocationData("Brickroad Maze", "Brick Road Maze - Alcove Present", 0xEB0063),
+ LocationData("Brickroad Maze", "Brick Road Maze - Near Exit Present", 0xEB0064),
+ LocationData("Rainy Circle", "Rainy Circle - Isolated Present", 0xEB0065),
+ LocationData("Rainy Circle", "Rainy Circle - East Cliff Present", 0xEB0066),
+ LocationData("Rainy Circle", "Rainy Circle - Near Ropes Present", 0xEB0067),
+ LocationData("Andonuts Lab Area", "Andonuts Lab - Present", 0xEB0068),
+ LocationData("Andonuts Lab Area", "Andonuts Lab - Mouse", 0xEB0069),
+ LocationData("Stonehenge Base", "Stonehenge - Purple Maze Present", 0xEB006A),
+ LocationData("Stonehenge Base", "Stonehenge - Dead End Present", 0xEB006B),
+ LocationData("Stonehenge Base", "Stonehenge - Near End of the Maze Present", 0xEB006C),
+ LocationData("Stonehenge Base", "Stonehenge - Bridge Room East Balcony Present", 0xEB006D),
+ LocationData("Stonehenge Base", "Stonehenge - Bridge Room Lower Present", 0xEB006E),
+ LocationData("Stonehenge Base", "Stonehenge - Flashing Room Right Path Present", 0xEB006F),
+ LocationData("Stonehenge Base", "Stonehenge - Flashing Room Center Present", 0xEB0070),
+ LocationData("Stonehenge Base", "Stonehenge - Flashing Room Upper Present", 0xEB0071),
+ LocationData("Stonehenge Base", "Stonehenge - Kidnapped Mr. Saturn", 0xEB0072),
+ LocationData("Stonehenge Base", "Stonehenge - Tony Item", 0xEB0073),
+ LocationData("Gold Mine", "Gold Mine - Mouse Crossroad Present #1", 0xEB0074),
+ LocationData("Gold Mine", "Gold Mine - Mouse Crossroad Present #2", 0xEB0075),
+ LocationData("Gold Mine", "Gold Mine - B1F Lonely Mole Present", 0xEB0076),
+ LocationData("Gold Mine", "Gold Mine - South Hall Present", 0xEB0077),
+ LocationData("Gold Mine", "Gold Mine - South Corner Present", 0xEB0078),
+ LocationData("Gold Mine", "Gold Mine - South Mole Present #1", 0xEB0079),
+ LocationData("Gold Mine", "Gold Mine - South Mole Present #2", 0xEB007A),
+ LocationData("Gold Mine", "Gold Mine - North Crossroad Detour Present", 0xEB007B),
+ LocationData("Gold Mine", "Gold Mine - North Mole Present", 0xEB007C),
+ LocationData("Gold Mine", "Gold Mine - West Mole Present", 0xEB007D),
+ LocationData("Gold Mine", "Gold Mine - B1F Isolated Present", 0xEB007E),
+ LocationData("Gold Mine", "Gold Mine - West Crossroad Detour Present", 0xEB007F),
+ LocationData("Gold Mine", "Gold Mine - B1F Junction Present", 0xEB0080),
+ LocationData("Gold Mine", "Gold Mine - B1F Junction Mole Present", 0xEB0081),
+ LocationData("Monkey Caves", "Monkey Caves - 1F Right Chest", 0xEB00F1),
+ LocationData("Monkey Caves", "Monkey Caves - 1F Left Chest", 0xEB00F2),
+ LocationData("Monkey Caves", "Monkey Caves - West 2F Left Chest", 0xEB00F3),
+ LocationData("Monkey Caves", "Monkey Caves - West 2F Right Chest #1", 0xEB00F4),
+ LocationData("Monkey Caves", "Monkey Caves - West 2F Right Chest #2", 0xEB00F5),
+ LocationData("Monkey Caves", "Monkey Caves - East 2F Left Chest", 0xEB00F6),
+ LocationData("Monkey Caves", "Monkey Caves - East 2F Right Chest", 0xEB00F7),
+ LocationData("Monkey Caves", "Monkey Caves - East West 3F Right Chest #1", 0xEB00F8),
+ LocationData("Monkey Caves", "Monkey Caves - East West 3F Right Chest #2", 0xEB00F9),
+ LocationData("Monkey Caves", "Monkey Caves - West End Chest", 0xEB0082),
+ LocationData("Monkey Caves", "Monkey Caves - West End Trashcan", 0xEB0083),
+ LocationData("Monkey Caves", "Monkey Caves - East End Chest", 0xEB0084),
+ LocationData("Monkey Caves", "Monkey Caves - East End Trashcan", 0xEB0085),
+ LocationData("Monkey Caves", "Monkey Caves - Bow Monkey Gift", 0xEB0086),
+ LocationData("Monkey Caves", "Monkey Caves - Talah Rama Chest #1", 0xEB0087),
+ LocationData("Monkey Caves", "Monkey Caves - Talah Rama Chest #2", 0xEB0088),
+ LocationData("Monkey Caves", "Monkey Caves - Talah Rama Gift", 0xEB0089),
+ LocationData("Monkey Caves", "Monkey Caves - Monkey Power", 0xEB008A),
+ LocationData("Fourside", "Fourside - Venus Gift", 0xEB008B),
+ LocationData("Moonside", "Fourside - Post-Moonside Delivery", 0xEB008C),
+ LocationData("Fourside", "Fourside - Bakery 2F Gift", 0xEB008D),
+ LocationData("Moonside", "Moonside - Two Trees Present", 0xEB008E),
+ LocationData("Moonside", "Moonside - East Island Present", 0xEB008F),
+ LocationData("Moonside", "Moonside - Businessman Present", 0xEB0090),
+ LocationData("Moonside", "Moonside - West Island Present", 0xEB0091),
+ LocationData("Moonside", "Moonside - Hospital Present", 0xEB0092),
+ LocationData("Fourside Dept. Store", "Fourside - Department Store Blackout", 0xEB0093),
+ LocationData("Magnet Hill", "Magnet Hill - West Entrance Trashcan", 0xEB0094),
+ LocationData("Magnet Hill", "Magnet Hill - First Room Free Door Trashcan", 0xEB0095),
+ LocationData("Magnet Hill", "Magnet Hill - First Room Barrel Door Trashcan", 0xEB0096),
+ LocationData("Magnet Hill", "Magnet Hill - Second Room Dead End Trashcan", 0xEB0097),
+ LocationData("Magnet Hill", "Magnet Hill - Final Room Door Trashcan", 0xEB0098),
+ LocationData("Magnet Hill", "Fourside - Magnet Hill Chest", 0xEB0099),
+ LocationData("Monotoli Building", "Monotoli Building - One Table Present", 0xEB009A),
+ LocationData("Monotoli Building", "Monotoli Building - Two Table Present", 0xEB009B),
+ LocationData("Monotoli Building", "Monotoli Building - Electra Gift", 0xEB009C),
+ LocationData("Monotoli Building", "Monotoli Building - Monotoli Gift", 0xEB009D),
+ LocationData("Monotoli Building", "Monotoli Building - Monotoli Character", 0xEB009E),
+ LocationData("Summers Museum", "Summers - Museum Item", 0xEB009F),
+ LocationData("Summers", "Summers - Magic Cake", 0xEB00A0),
+ LocationData("Dalaam", "Dalaam - Throne Room Chest #1", 0xEB00A1),
+ LocationData("Dalaam", "Dalaam - Throne Room Chest #2", 0xEB00A2),
+ LocationData("Dalaam", "Dalaam - Throne Room Chest #3", 0xEB00A3),
+ LocationData("Dalaam", "Dalaam - Trial of Mu", 0xEB00A4),
+ LocationData("Dalaam", "Dalaam - Restaurant Chest #1", 0xEB00A5),
+ LocationData("Dalaam", "Dalaam - Restaurant Chest #2", 0xEB00A6),
+ LocationData("Dalaam", "Dalaam - Do Do Guy's House Chest", 0xEB00A7),
+ LocationData("Dalaam", "Dalaam - Upper House Chest", 0xEB00A8),
+ LocationData("Dalaam", "Dalaam - Throne Character", 0xEB00A9),
+ LocationData("Ness's Mind", "Poo - Starting Item", 0xEB00AA),
+ LocationData("Pink Cloud", "Pink Cloud - Three Holes Present", 0xEB00AB),
+ LocationData("Pink Cloud", "Pink Cloud - Left Hole Present", 0xEB00AC),
+ LocationData("Pink Cloud", "Pink Cloud - Ground Floor Present", 0xEB00AD),
+ LocationData("Pyramid", "Pyramid - Anteroom Sarcophagus", 0xEB00AE),
+ LocationData("Pyramid", "Pyramid - Northwest Door Sarcophagus", 0xEB00AF),
+ LocationData("Pyramid", "Pyramid - Hallway Sarcophagus #1", 0xEB00B0),
+ LocationData("Pyramid", "Pyramid - Hallway Sarcophagus #2", 0xEB00B1),
+ LocationData("Pyramid", "Pyramid - Switch Room Sarcophagus", 0xEB00B2),
+ LocationData("Pyramid", "Pyramid - Pedestal Item", 0xEB00B3),
+ LocationData("Pyramid", "Pyramid - Way Out Sarcophagus", 0xEB00B4),
+ LocationData("Southern Scaraba", "Scaraba - Star Master", 0xEB00B5),
+ LocationData("Southern Scaraba", "Scaraba - Key Holder", 0xEB00B6),
+ LocationData("Dungeon Man", "Dungeon Man - 1F Dead End Present", 0xEB00B7),
+ LocationData("Dungeon Man", "Dungeon Man - 1F Long Walk Present", 0xEB00B8),
+ LocationData("Dungeon Man", "Dungeon Man - 1F Disappointing Present", 0xEB00B9),
+ LocationData("Dungeon Man", "Dungeon Man - 1F Opinion Present", 0xEB00BA),
+ LocationData("Dungeon Man", "Dungeon Man - 1F No Sign Present", 0xEB00BB),
+ LocationData("Dungeon Man", "Dungeon Man - 2F Unnecessary Billboard Present", 0xEB00BC),
+ LocationData("Dungeon Man", "Dungeon Man - 2F Dungeon Exploration Present", 0xEB00BD),
+ LocationData("Dungeon Man", "Dungeon Man - 2F South Ledge Present", 0xEB00BE),
+ LocationData("Dungeon Man", "Dungeon Man - 2F North Alcove Present", 0xEB00BF),
+ LocationData("Dungeon Man", "Dungeon Man - 3F Present", 0xEB00C0),
+ LocationData("Dungeon Man", "Dungeon Man - 2F Hole Present", 0xEB00C1),
+ LocationData("Dungeon Man", "Dungeon Man - 1F Exit Ledge Present", 0xEB00C2),
+ LocationData("Deep Darkness", "Deep Darkness - Teleporting Monkey", 0xEB00C3),
+ LocationData("Deep Darkness", "Deep Darkness - Crest of Darkness Present", 0xEB00C4),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Helicopter Present", 0xEB00C5),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Yellow Bird Present", 0xEB00C6),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Swamp Present", 0xEB00C7),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Corner Present", 0xEB00C8),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Alcove Present", 0xEB00C9),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - North Alcove Truffle", 0xEB00CA),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Near Land Truffle", 0xEB00CB),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Present Truffle", 0xEB00CC),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Village Truffle", 0xEB00CD),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Entrance Truffle", 0xEB00CE),
+ LocationData("Deep Darkness Darkness", "Deep Darkness - Barf Character", 0xEB00CF),
+ LocationData("Tenda Village", "Tenda Village - Trashcan", 0xEB00D0),
+ LocationData("Tenda Village", "Tenda Village - Tenda Tea", 0xEB00D1),
+ LocationData("Tenda Village", "Tenda Village - Tenda Gift", 0xEB00D2),
+ LocationData("Tenda Village", "Tenda Village - Tenda Gift #2", 0xEB00D3),
+ LocationData("Lumine Hall", "Lumine Hall - B1F Non-Talkative Rock Present", 0xEB00D4),
+ LocationData("Lumine Hall", "Lumine Hall - 1F North Path Present", 0xEB00D5),
+ LocationData("Lumine Hall", "Lumine Hall - B1F Thankful Rock Corner Present", 0xEB00D6),
+ LocationData("Lumine Hall", "Lumine Hall - B1F Thankful Rock Junction Present", 0xEB00D7),
+ LocationData("Lumine Hall", "Lumine Hall - 1F Above Belly Button Present", 0xEB00D8),
+ LocationData("Lumine Hall", "Lumine Hall - B1F Belly Button Present", 0xEB00D9),
+ LocationData("Lumine Hall", "Lumine Hall - 1F Near Exit Present", 0xEB00DA),
+ LocationData("Lumine Hall", "Lumine Hall - 1F Dead End Present", 0xEB00DB),
+ LocationData("Lumine Hall", "Lumine Hall - B1F West Alcove Present", 0xEB00DC),
+ LocationData("Lost Underworld", "Lost Underworld - Talking Rock", 0xEB00DD),
+ LocationData("Lost Underworld", "Lost Underworld - East Present", 0xEB00DE),
+ LocationData("Lost Underworld", "Lost Underworld - Northeast Present", 0xEB00DF),
+ LocationData("Lost Underworld", "Lost Underworld - Northeast of Tenda Tribe Present", 0xEB00E0),
+ LocationData("Lost Underworld", "Lost Underworld - Southwest of Tenda Tribe Present", 0xEB00E1),
+ LocationData("Lost Underworld", "Lost Underworld - Evacuation Present", 0xEB00E2),
+ LocationData("Fire Spring", "Fire Spring - 1st Cave Present", 0xEB00E3),
+ LocationData("Fire Spring", "Fire Spring - East Corner Present", 0xEB00E4),
+ LocationData("Fire Spring", "Fire Spring - Volcano Present", 0xEB00E5),
+ LocationData("Fire Spring", "Fire Spring - Lone Cave Present", 0xEB00E6),
+ LocationData("Fire Spring", "Fire Spring - Upper Volcano Present", 0xEB00E7),
+ LocationData("Cave of the Present", "Cave of the Present - Star Master", 0xEB00EE),
+ LocationData("Cave of the Present", "Cave of the Present - Broken Phase Distorter", 0xEB00EF),
+
+ LocationData("Happy-Happy HQ", "Carpainter Defeated", None),
+ LocationData("Belch's Factory", "Belch Defeated", None),
+ LocationData("Dungeon Man", "Dungeon Man Submarine", None),
+ LocationData("Giant Step", "Giant Step Sanctuary", None),
+ LocationData("Lilliput Steps", "Lilliput Steps Sanctuary", None),
+ LocationData("Milky Well", "Milky Well Sanctuary", None),
+ LocationData("Rainy Circle", "Rainy Circle Sanctuary", None),
+ LocationData("Magnet Hill", "Magnet Hill Sanctuary", None),
+ LocationData("Pink Cloud", "Pink Cloud Sanctuary", None),
+ LocationData("Lumine Hall", "Lumine Hall Sanctuary", None),
+ LocationData("Fire Spring", "Fire Spring Sanctuary", None),
+ LocationData("Ness's Mind", "Sanctuary Goal", None),
+ LocationData("Global ATM Access", "Any ATM", None)
+ ]
+
+ if world.options.giygas_required:
+ location_table += [
+ LocationData("Cave of the Past", "Cave of the Past - Present", 0xEB00F0),
+ LocationData("Endgame", "Giygas", None),
+ ]
+
+ if world.options.alternate_sanctuary_goal:
+ location_table += [
+ LocationData("Ness's Mind", "+2 Sanctuaries", None)
+ ]
+
+ if world.options.magicant_mode in range(1, 3):
+ location_table += [
+ LocationData("Sea of Eden", "Magicant - Ness's Nightmare", None),
+ ]
+
+ if not world.options.magicant_mode:
+ location_table += [
+ LocationData("Sea of Eden", "Magicant - Ness's Nightmare", 0xEB00ED),
+ ]
+
+ if world.options.magicant_mode < MagicantMode.option_alternate_goal:
+ location_table += [
+ LocationData("Magicant", "Magicant - Ness's Gift", 0xEB00E8),
+ LocationData("Magicant", "Magicant - Present Near Ness", 0xEB00E9),
+ LocationData("Magicant", "Magicant - Lonely Present", 0xEB00EA),
+ LocationData("Magicant", "Magicant - North Present", 0xEB00EB),
+ LocationData("Magicant", "Magicant - Hills Present", 0xEB00EC),
+ LocationData("Magicant", "Magicant - Town Present", 0xEB00FA)
+ ]
+ if world.options.magicant_mode == MagicantMode.option_alternate_goal:
+ location_table += [
+ LocationData("Ness's Mind", "+1 Sanctuary", None)
+ ]
+
+ if world.options.shop_randomizer == ShopRandomizer.option_shopsanity:
+ location_table += [
+ LocationData("Onett", "Onett Drugstore - Right Counter Slot 1", 0xeb1000),
+ LocationData("Onett", "Onett Drugstore - Right Counter Slot 2", 0xeb1001),
+ LocationData("Onett", "Onett Drugstore - Right Counter Slot 3", 0xeb1002),
+ LocationData("Onett", "Onett Drugstore - Right Counter Slot 4", 0xeb1003),
+ LocationData("Onett", "Onett Drugstore - Right Counter Slot 5", 0xeb1004),
+ LocationData("Onett", "Onett Drugstore - Left Counter", 0xeb1007),
+ LocationData("Summers", "Summers - Beach Cart", 0xeb100e),
+ LocationData("Onett", "Onett Burger Shop - Slot 1", 0xeb1015),
+ LocationData("Onett", "Onett Burger Shop - Slot 2", 0xeb1016),
+ LocationData("Onett", "Onett Burger Shop - Slot 3", 0xeb1017),
+ LocationData("Onett", "Onett Burger Shop - Slot 4", 0xeb1018),
+ LocationData("Onett", "Onett Bakery - Slot 1", 0xeb101c),
+ LocationData("Onett", "Onett Bakery - Slot 2", 0xeb101d),
+ LocationData("Onett", "Onett Bakery - Slot 3", 0xeb101e),
+ LocationData("Onett", "Onett Bakery - Slot 4", 0xeb101f),
+ LocationData("Twoson", "Twoson Department Store Burger Shop - Slot 1", 0xeb1023),
+ LocationData("Twoson", "Twoson Department Store Burger Shop - Slot 2", 0xeb1024),
+ LocationData("Twoson", "Twoson Department Store Burger Shop - Slot 3", 0xeb1025),
+ LocationData("Twoson", "Twoson Department Store Burger Shop - Slot 4", 0xeb1026),
+ LocationData("Twoson", "Twoson Department Store Bakery - Slot 1", 0xeb102a),
+ LocationData("Twoson", "Twoson Department Store Bakery - Slot 2", 0xeb102b),
+ LocationData("Twoson", "Twoson Department Store Bakery - Slot 3", 0xeb102c),
+ LocationData("Twoson", "Twoson Department Store Bakery - Slot 4", 0xeb102d),
+ LocationData("Twoson", "Twoson Department Store Top Floor - Right Counter Slot 1", 0xeb1031),
+ LocationData("Twoson", "Twoson Department Store Top Floor - Right Counter Slot 2", 0xeb1032),
+ LocationData("Twoson", "Twoson Department Store Top Floor - Right Counter Slot 3", 0xeb1033),
+ LocationData("Twoson", "Twoson Department Store Top Floor - Right Counter Slot 4", 0xeb1034),
+ LocationData("Twoson", "Twoson Department Store Top Floor - Right Counter Slot 5", 0xeb1035),
+ LocationData("Twoson", "Twoson Department Store Top Floor - Right Counter Slot 6", 0xeb1036),
+ LocationData("Twoson", "Twoson Department Store Top Floor - Left Counter Slot 1", 0xeb1038),
+ LocationData("Twoson", "Twoson Department Store Top Floor - Left Counter Slot 2", 0xeb1039),
+ LocationData("Summers", "Summers - Magic Cake Cart Shop Slot", 0xeb103f),
+ LocationData("Twoson", "Burglin Park Junk Shop - Slot 1", 0xeb1046),
+ LocationData("Twoson", "Burglin Park Junk Shop - Slot 2", 0xeb1047),
+ LocationData("Twoson", "Burglin Park Junk Shop - Slot 3", 0xeb1048),
+ LocationData("Twoson", "Burglin Park Junk Shop - Slot 4", 0xeb1049),
+ LocationData("Twoson", "Burglin Park Junk Shop - Slot 5", 0xeb104a),
+ LocationData("Twoson", "Burglin Park Junk Shop - Slot 6", 0xeb104b),
+ LocationData("Twoson", "Burglin Park Bread Stand - Slot 1", 0xeb105b),
+ LocationData("Twoson", "Burglin Park Bread Stand - Slot 2", 0xeb105c),
+ LocationData("Twoson", "Burglin Park Bread Stand - Slot 3", 0xeb105d),
+ LocationData("Twoson", "Burglin Park Bread Stand - Slot 4", 0xeb105e),
+ LocationData("Twoson", "Burglin Park Bread Stand - Slot 5", 0xeb105f),
+ LocationData("Twoson", "Burglin Park Bread Stand - Slot 6", 0xeb1060),
+ LocationData("Twoson", "Burglin Park - Banana Stand", 0xeb1062),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Right Counter Slot 1", 0xeb1069),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Right Counter Slot 2", 0xeb106a),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Right Counter Slot 3", 0xeb106b),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Right Counter Slot 4", 0xeb106c),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Right Counter Slot 5", 0xeb106d),
+ LocationData("Threed", "Threed Drugstore - Right Counter Slot 1", 0xeb1070),
+ LocationData("Threed", "Threed Drugstore - Right Counter Slot 2", 0xeb1071),
+ LocationData("Threed", "Threed Drugstore - Right Counter Slot 3", 0xeb1072),
+ LocationData("Threed", "Threed Drugstore - Right Counter Slot 4", 0xeb1073),
+ LocationData("Threed", "Threed Drugstore - Right Counter Slot 5", 0xeb1074),
+ LocationData("Threed", "Threed Drugstore - Left Counter Slot 1", 0xeb1077),
+ LocationData("Threed", "Threed Drugstore - Left Counter Slot 2", 0xeb1078),
+ LocationData("Threed", "Threed Drugstore - Left Counter Slot 3", 0xeb1079),
+ LocationData("Threed", "Threed Drugstore - Left Counter Slot 4", 0xeb107a),
+ LocationData("Threed", "Threed Drugstore - Left Counter Slot 5", 0xeb107b),
+ LocationData("Threed", "Threed - Arms Dealer Slot 1", 0xeb107e),
+ LocationData("Threed", "Threed - Arms Dealer Slot 2", 0xeb107f),
+ LocationData("Threed", "Threed - Arms Dealer Slot 3", 0xeb1080),
+ LocationData("Threed", "Threed - Arms Dealer Slot 4", 0xeb1081),
+ LocationData("Threed", "Threed Bakery - Slot 1", 0xeb1085),
+ LocationData("Threed", "Threed Bakery - Slot 2", 0xeb1086),
+ LocationData("Threed", "Threed Bakery - Slot 3", 0xeb1087),
+ LocationData("Threed", "Threed Bakery - Slot 4", 0xeb1088),
+ LocationData("Threed", "Threed Bakery - Slot 5", 0xeb1089),
+ LocationData("Threed", "Threed Bakery - Slot 6", 0xeb108a),
+ LocationData("Threed", "Threed Bakery - Slot 7", 0xeb108b),
+ LocationData("Scaraba", "Scaraba - Expensive Water Guy", 0xeb108c),
+ LocationData("Winters", "Winters Drugstore - Slot 1", 0xeb1093),
+ LocationData("Winters", "Winters Drugstore - Slot 2", 0xeb1094),
+ LocationData("Winters", "Winters Drugstore - Slot 3", 0xeb1095),
+ LocationData("Winters", "Winters Drugstore - Slot 4", 0xeb1096),
+ LocationData("Winters", "Winters Drugstore - Slot 5", 0xeb1097),
+ LocationData("Winters", "Winters Drugstore - Slot 6", 0xeb1098),
+ LocationData("Winters", "Winters Drugstore - Slot 7", 0xeb1099),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Center Saturn Slot 1", 0xeb109a),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Center Saturn Slot 2", 0xeb109b),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Center Saturn Slot 3", 0xeb109c),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Center Saturn Slot 4", 0xeb109d),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Center Saturn Slot 5", 0xeb109e),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Counter Slot 1", 0xeb10a1),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Counter Slot 2", 0xeb10a2),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Counter Slot 3", 0xeb10a3),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Counter Slot 4", 0xeb10a4),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Counter Slot 5", 0xeb10a5),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Arms Dealer Slot 1", 0xeb10a8),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Arms Dealer Slot 2", 0xeb10a9),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Arms Dealer Slot 3", 0xeb10aa),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Arms Dealer Slot 4", 0xeb10ab),
+ LocationData("Fourside", "Fourside Bakery - Slot 1", 0xeb10af),
+ LocationData("Fourside", "Fourside Bakery - Slot 2", 0xeb10b0),
+ LocationData("Fourside", "Fourside Bakery - Slot 3", 0xeb10b1),
+ LocationData("Fourside", "Fourside Bakery - Slot 4", 0xeb10b2),
+ LocationData("Fourside", "Fourside Bakery - Slot 5", 0xeb10b3),
+ LocationData("Fourside", "Fourside Bakery - Slot 6", 0xeb10b4),
+ LocationData("Fourside", "Fourside Department Store - Tool Shop Slot 1", 0xeb10b6),
+ LocationData("Fourside", "Fourside Department Store - Tool Shop Slot 2", 0xeb10b7),
+ LocationData("Fourside", "Fourside Department Store - Tool Shop Slot 3", 0xeb10b8),
+ LocationData("Fourside", "Fourside Department Store - Tool Shop Slot 4", 0xeb10b9),
+ LocationData("Fourside", "Fourside Department Store - Tool Shop Slot 5", 0xeb10ba),
+ LocationData("Fourside", "Fourside Department Store - Tool Shop Slot 6", 0xeb10bb),
+ LocationData("Fourside", "Fourside Department Store - Tool Shop Slot 7", 0xeb10bc),
+ LocationData("Fourside", "Fourside Department Store - Shop Shop Slot 1", 0xeb10bd),
+ LocationData("Fourside", "Fourside Department Store - Shop Shop Slot 2", 0xeb10be),
+ LocationData("Fourside", "Fourside Department Store - Shop Shop Slot 3", 0xeb10bf),
+ LocationData("Fourside", "Fourside Department Store - Shop Shop Slot 4", 0xeb10c0),
+ LocationData("Fourside", "Fourside Department Store - Food Shop Slot 1", 0xeb10c4),
+ LocationData("Fourside", "Fourside Department Store - Food Shop Slot 2", 0xeb10c5),
+ LocationData("Fourside", "Fourside Department Store - Food Shop Slot 3", 0xeb10c6),
+ LocationData("Fourside", "Fourside Department Store - Food Shop Slot 4", 0xeb10c7),
+ LocationData("Fourside", "Fourside Department Store - Food Shop Slot 5", 0xeb10c8),
+ LocationData("Fourside", "Fourside Department Store - 2F Cart Slot 1", 0xeb10cb),
+ LocationData("Fourside", "Fourside Department Store - 2F Cart Slot 2", 0xeb10cc),
+ LocationData("Fourside", "Fourside Department Store - 2F Cart Slot 3", 0xeb10cd),
+ LocationData("Fourside", "Fourside Department Store - 2F Cart Slot 4", 0xeb10ce),
+ LocationData("Fourside", "Fourside Department Store - 2F Cart Slot 5", 0xeb10cf),
+ LocationData("Fourside", "Fourside Department Store - 2F Cart Slot 6", 0xeb10d0),
+ LocationData("Fourside", "Fourside Department Store - 2F Cart Slot 7", 0xeb10d1),
+ LocationData("Fourside", "Fourside Department Store - Toys Shop Slot 1", 0xeb10d2),
+ LocationData("Fourside", "Fourside Department Store - Toys Shop Slot 2", 0xeb10d3),
+ LocationData("Fourside", "Fourside Department Store - Toys Shop Slot 3", 0xeb10d4),
+ LocationData("Fourside", "Fourside Department Store - Toys Shop Slot 4", 0xeb10d5),
+ LocationData("Fourside", "Fourside Department Store - Toys Shop Slot 5", 0xeb10d6),
+ LocationData("Fourside", "Fourside Department Store - Toys Shop Slot 6", 0xeb10d7),
+ LocationData("Fourside", "Fourside Department Store - Sports Shop Slot 1", 0xeb10d9),
+ LocationData("Fourside", "Fourside Department Store - Sports Shop Slot 2", 0xeb10da),
+ LocationData("Fourside", "Fourside Department Store - Sports Shop Slot 3", 0xeb10db),
+ LocationData("Fourside", "Fourside Department Store - Sports Shop Slot 4", 0xeb10dc),
+ LocationData("Fourside", "Fourside Department Store - Burger Shop Slot 1", 0xeb10e0),
+ LocationData("Fourside", "Fourside Department Store - Burger Shop Slot 2", 0xeb10e1),
+ LocationData("Fourside", "Fourside Department Store - Burger Shop Slot 3", 0xeb10e2),
+ LocationData("Fourside", "Fourside Department Store - Burger Shop Slot 4", 0xeb10e3),
+ LocationData("Fourside", "Fourside Department Store - Burger Shop Slot 5", 0xeb10e4),
+ LocationData("Fourside", "Fourside Department Store - Arms Dealer Slot 1", 0xeb10e7),
+ LocationData("Fourside", "Fourside Department Store - Arms Dealer Slot 2", 0xeb10e8),
+ LocationData("Fourside", "Fourside Department Store - Arms Dealer Slot 3", 0xeb10e9),
+ LocationData("Fourside", "Fourside Department Store - Arms Dealer Slot 4", 0xeb10ea),
+ LocationData("Fourside", "Fourside Department Store - Arms Dealer Slot 5", 0xeb10eb),
+ LocationData("Fourside", "Fourside - Northeast Alley Junk Shop Slot 1", 0xeb10ee),
+ LocationData("Fourside", "Fourside - Northeast Alley Junk Shop Slot 2", 0xeb10ef),
+ LocationData("Fourside", "Fourside - Northeast Alley Junk Shop Slot 3", 0xeb10f0),
+ LocationData("Fourside", "Fourside - Northeast Alley Junk Shop Slot 4", 0xeb10f1),
+ LocationData("Summers", "Summers - Scam Shop Slot 1", 0xeb1103),
+ LocationData("Summers", "Summers - Scam Shop Slot 2", 0xeb1104),
+ LocationData("Summers", "Summers - Scam Shop Slot 3", 0xeb1105),
+ LocationData("Summers", "Summers - Scam Shop Slot 4", 0xeb1106),
+ LocationData("Summers", "Summers - Scam Shop Slot 5", 0xeb1107),
+ LocationData("Summers", "Summers - Scam Shop Slot 6", 0xeb1108),
+ LocationData("Summers", "Summers - Scam Shop Slot 7", 0xeb1109),
+ LocationData("Summers", "Summers Harbor - Shop Slot 1", 0xeb110a),
+ LocationData("Summers", "Summers Harbor - Shop Slot 2", 0xeb110b),
+ LocationData("Summers", "Summers Harbor - Shop Slot 3", 0xeb110c),
+ LocationData("Summers", "Summers Harbor - Shop Slot 4", 0xeb110d),
+ LocationData("Summers", "Summers Harbor - Shop Slot 5", 0xeb110e),
+ LocationData("Summers", "Summers Harbor - Shop Slot 6", 0xeb110f),
+ LocationData("Summers", "Summers Harbor - Shop Slot 7", 0xeb1110),
+ LocationData("Summers", "Summers Restaurant - Slot 1", 0xeb1111),
+ LocationData("Summers", "Summers Restaurant - Slot 2", 0xeb1112),
+ LocationData("Summers", "Summers Restaurant - Slot 3", 0xeb1113),
+ LocationData("Summers", "Summers Restaurant - Slot 4", 0xeb1114),
+ LocationData("Summers", "Summers Restaurant - Slot 5", 0xeb1115),
+ LocationData("Summers", "Summers Restaurant - Slot 6", 0xeb1116),
+ LocationData("Scaraba", "Scaraba - Indoors Shop Slot 1", 0xeb1118),
+ LocationData("Scaraba", "Scaraba - Indoors Shop Slot 2", 0xeb1119),
+ LocationData("Scaraba", "Scaraba - Indoors Shop Slot 3", 0xeb111a),
+ LocationData("Scaraba", "Scaraba - Indoors Shop Slot 4", 0xeb111b),
+ LocationData("Scaraba", "Scaraba - Indoors Shop Slot 5", 0xeb111c),
+ LocationData("Scaraba", "Scaraba - Indoors Shop Slot 6", 0xeb111d),
+ LocationData("Scaraba", "Scaraba Bazaar - Red Snake Carpet Slot 1", 0xeb1126),
+ LocationData("Scaraba", "Scaraba Bazaar - Red Snake Carpet Slot 2", 0xeb1127),
+ LocationData("Scaraba", "Scaraba Bazaar - Red Snake Carpet Slot 3", 0xeb1128),
+ LocationData("Scaraba", "Scaraba Bazaar - Bottom Left Carpet Slot 1", 0xeb112d),
+ LocationData("Scaraba", "Scaraba Bazaar - Bottom Left Carpet Slot 2", 0xeb112e),
+ LocationData("Scaraba", "Scaraba Bazaar - Bottom Left Carpet Slot 3", 0xeb112f),
+ LocationData("Scaraba", "Scaraba Bazaar - Bottom Left Carpet Slot 4", 0xeb1130),
+ LocationData("Scaraba", "Scaraba Bazaar - Bottom Left Carpet Slot 5", 0xeb1131),
+ LocationData("Scaraba", "Scaraba Bazaar - Bottom Left Carpet Slot 6", 0xeb1132),
+ LocationData("Scaraba", "Scaraba Hotel - Arms Dealer Slot 1", 0xeb1134),
+ LocationData("Scaraba", "Scaraba Hotel - Arms Dealer Slot 2", 0xeb1135),
+ LocationData("Scaraba", "Scaraba Hotel - Arms Dealer Slot 3", 0xeb1136),
+ LocationData("Scaraba", "Scaraba Hotel - Arms Dealer Slot 4", 0xeb1137),
+ LocationData("Deep Darkness", "Deep Darkness - Businessman Slot 1", 0xeb113b),
+ LocationData("Deep Darkness", "Deep Darkness - Businessman Slot 2", 0xeb113c),
+ LocationData("Deep Darkness", "Deep Darkness - Businessman Slot 3", 0xeb113d),
+ LocationData("Deep Darkness", "Deep Darkness - Businessman Slot 4", 0xeb113e),
+ LocationData("Deep Darkness", "Deep Darkness - Businessman Slot 5", 0xeb113f),
+ LocationData("Deep Darkness", "Deep Darkness - Businessman Slot 6", 0xeb1140),
+ LocationData("Deep Darkness", "Deep Darkness - Businessman Slot 7", 0xeb1141),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Post-Belch Saturn Slot 1", 0xeb1157),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Post-Belch Saturn Slot 2", 0xeb1158),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Post-Belch Saturn Slot 3", 0xeb1159),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Post-Belch Saturn Slot 4", 0xeb115a),
+ LocationData("Southern Scaraba", "Scaraba - Southern Camel Shop Slot 1", 0xeb115e),
+ LocationData("Southern Scaraba", "Scaraba - Southern Camel Shop Slot 2", 0xeb115f),
+ LocationData("Southern Scaraba", "Scaraba - Southern Camel Shop Slot 3", 0xeb1160),
+ LocationData("Southern Scaraba", "Scaraba - Southern Camel Shop Slot 4", 0xeb1161),
+ LocationData("Southern Scaraba", "Scaraba - Southern Camel Shop Slot 5", 0xeb1162),
+ LocationData("Southern Scaraba", "Scaraba - Southern Camel Shop Slot 6", 0xeb1163),
+ LocationData("Southern Scaraba", "Scaraba - Southern Camel Shop Slot 7", 0xeb1164),
+ LocationData("Deep Darkness", "Deep Darkness - Arms Dealer Slot 1", 0xeb1165),
+ LocationData("Deep Darkness", "Deep Darkness - Arms Dealer Slot 2", 0xeb1166),
+ LocationData("Deep Darkness", "Deep Darkness - Arms Dealer Slot 3", 0xeb1167),
+ LocationData("Deep Darkness", "Deep Darkness - Arms Dealer Slot 4", 0xeb1168),
+ LocationData("Lost Underworld", "Lost Underworld - Tenda Camp Shop Slot 1", 0xeb116c),
+ LocationData("Lost Underworld", "Lost Underworld - Tenda Camp Shop Slot 2", 0xeb116d),
+ LocationData("Lost Underworld", "Lost Underworld - Tenda Camp Shop Slot 3", 0xeb116e),
+ LocationData("Lost Underworld", "Lost Underworld - Tenda Camp Shop Slot 4", 0xeb116f),
+ LocationData("Lost Underworld", "Lost Underworld - Tenda Camp Shop Slot 5", 0xeb1170),
+ LocationData("Lost Underworld", "Lost Underworld - Tenda Camp Shop Slot 6", 0xeb1171),
+ LocationData("Lost Underworld", "Lost Underworld - Tenda Camp Shop Slot 7", 0xeb1172),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Left Counter Slot 1", 0xeb117a),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Left Counter Slot 2", 0xeb117b),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Left Counter Slot 3", 0xeb117c),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Left Counter Slot 4", 0xeb117d),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Left Counter Slot 5", 0xeb117e),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Left Counter Slot 6", 0xeb117f),
+ LocationData("Happy-Happy Village", "Happy-Happy Village Drugstore - Left Counter Slot 7", 0xeb1180),
+ LocationData("Grapefruit Falls", "Grapefruit Falls - Hiker Shop Slot 1", 0xeb1181),
+ LocationData("Grapefruit Falls", "Grapefruit Falls - Hiker Shop Slot 2", 0xeb1182),
+ LocationData("Grapefruit Falls", "Grapefruit Falls - Hiker Shop Slot 3", 0xeb1183),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Top Saturn Slot 1", 0xeb1188),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Top Saturn Slot 2", 0xeb1189),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Top Saturn Slot 3", 0xeb118a),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Top Saturn Slot 4", 0xeb118b),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Top Saturn Slot 5", 0xeb118c),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Top Saturn Slot 6", 0xeb118d),
+ LocationData("Saturn Valley", "Saturn Valley Shop - Top Saturn Slot 7", 0xeb118e),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Left Shop Slot 1", 0xeb118f),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Left Shop Slot 2", 0xeb1190),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Left Shop Slot 3", 0xeb1191),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Left Shop Slot 4", 0xeb1192),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Left Shop Slot 5", 0xeb1193),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Left Shop Slot 6", 0xeb1194),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes Drugstore - Left Shop Slot 7", 0xeb1195),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Mine Food Cart Slot 1", 0xeb1196),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Mine Food Cart Slot 2", 0xeb1197),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Mine Food Cart Slot 3", 0xeb1198),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Mine Food Cart Slot 4", 0xeb1199),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Mine Food Cart Slot 5", 0xeb119a),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Mine Food Cart Slot 6", 0xeb119b),
+ LocationData("Dusty Dunes Desert", "Dusty Dunes - Mine Food Cart Slot 7", 0xeb119c),
+ LocationData("Moonside", "Moonside Hotel - Shop Slot 1", 0xeb119d),
+ LocationData("Moonside", "Moonside Hotel - Shop Slot 2", 0xeb119e),
+ LocationData("Moonside", "Moonside Hotel - Shop Slot 3", 0xeb119f),
+ LocationData("Moonside", "Moonside Hotel - Shop Slot 4", 0xeb11a0),
+ LocationData("Moonside", "Moonside Hotel - Shop Slot 5", 0xeb11a1),
+ LocationData("Dalaam", "Dalaam Restaurant - Slot 1", 0xeb11a4),
+ LocationData("Dalaam", "Dalaam Restaurant - Slot 2", 0xeb11a5),
+ LocationData("Dalaam", "Dalaam Restaurant - Slot 3", 0xeb11a6),
+ LocationData("Dalaam", "Dalaam Restaurant - Slot 4", 0xeb11a7),
+ LocationData("Scaraba", "Scaraba Bazaar - Delicacy Shop Slot 1", 0xeb11ab),
+ LocationData("Scaraba", "Scaraba Bazaar - Delicacy Shop Slot 2", 0xeb11ac),
+ LocationData("Scaraba", "Scaraba Bazaar - Delicacy Shop Slot 3", 0xeb11ad),
+ LocationData("Scaraba", "Scaraba Bazaar - Delicacy Shop Slot 4", 0xeb11ae),
+ LocationData("Scaraba", "Scaraba Bazaar - Delicacy Shop Slot 5", 0xeb11af),
+ LocationData("Scaraba", "Scaraba Bazaar - Delicacy Shop Slot 6", 0xeb11b0),
+ LocationData("Scaraba", "Scaraba Bazaar - Delicacy Shop Slot 7", 0xeb11b1),
+ LocationData("Common Condiment Shop", "Twoson/Scaraba - Shared Condiment Shop Slot 1", 0xeb11b2),
+ LocationData("Common Condiment Shop", "Twoson/Scaraba - Shared Condiment Shop Slot 2", 0xeb11b3),
+ LocationData("Common Condiment Shop", "Twoson/Scaraba - Shared Condiment Shop Slot 3", 0xeb11b4),
+ LocationData("Common Condiment Shop", "Twoson/Scaraba - Shared Condiment Shop Slot 4", 0xeb11b5),
+ LocationData("Common Condiment Shop", "Twoson/Scaraba - Shared Condiment Shop Slot 5", 0xeb11b6),
+ LocationData("Common Condiment Shop", "Twoson/Scaraba - Shared Condiment Shop Slot 6", 0xeb11b7),
+ LocationData("Common Condiment Shop", "Twoson/Scaraba - Shared Condiment Shop Slot 7", 0xeb11b8),
+ LocationData("Andonuts Lab Area", "Andonuts Lab - Caveman Shop Slot 1", 0xeb11c0),
+ LocationData("Andonuts Lab Area", "Andonuts Lab - Caveman Shop Slot 2", 0xeb11c1),
+ LocationData("Andonuts Lab Area", "Andonuts Lab - Caveman Shop Slot 3", 0xeb11c2),
+ LocationData("Andonuts Lab Area", "Andonuts Lab - Caveman Shop Slot 4", 0xeb11c3),
+ LocationData("Andonuts Lab Area", "Andonuts Lab - Caveman Shop Slot 5", 0xeb11c4)
+ ]
+
+ if world.options.magicant_mode < MagicantMode.option_alternate_goal:
+ location_table += [
+ LocationData("Magicant", "Magicant - Shop Slot 1", 0xeb10f5),
+ LocationData("Magicant", "Magicant - Shop Slot 2", 0xeb10f6)
+ ]
+
+ return location_table
diff --git a/worlds/earthbound/Options.py b/worlds/earthbound/Options.py
new file mode 100644
index 000000000000..40dd90d633d5
--- /dev/null
+++ b/worlds/earthbound/Options.py
@@ -0,0 +1,625 @@
+from dataclasses import dataclass
+from Options import (Toggle, DefaultOnToggle, DeathLink, Choice, Range, PerGameCommonOptions, StartInventoryPool,
+ OptionGroup, FreeText, Visibility, PlandoBosses)
+from .modules.boss_shuffle import boss_plando_keys
+
+
+class GiygasRequired(DefaultOnToggle):
+ """If enabled, your goal will be to defeat Giygas at the Cave of the Past.
+ If disabled, your goal will either complete automatically upon completing
+ enough Sanctuaries, or completing Magicant if it is required."""
+ display_name = "Giygas Required"
+
+
+class SanctuariesRequired(Range):
+ """How many of the eight "Your Sanctuary" locations are required to be cleared."""
+ display_name = "Required Sanctuaries"
+ range_start = 1
+ range_end = 8
+ default = 4
+
+
+class SanctuaryAltGoal(Toggle):
+ """If enabled, you will be able to win by completing 2 more Sanctuaries than are required.
+ Does nothing if 7 or more Sanctuaries are required, or if Magicant and Giygas are not required."""
+ display_name = "Sanctuary Alternate Goal"
+
+
+class MagicantMode(Choice):
+ """PSI Location: You will be able to find a Magicant teleport item. Ness's Nightmare contains a PSI location, and no stat boost.
+ Required: You will unlock the Magicant Teleport upon reaching your Sanctuary goal. If Giygas is required, beating Ness's Nightmare will unlock the Cave of the Past and grant a party-wide stat boost. Otherwise, Ness's Nightmare will finish your game.
+ Alternate Goal: You will unlock the Magicant Teleport upon reaching one more Sanctuary than required. Beating Ness's Nightmare will finish your game. Does nothing if Giygas is not required, or if 8 Sanctuaries are required. Magicant locations are removed from the multiworld, but contain random junk for yourself.
+ Optional Boost: You will be able to find a Magicant teleport item. Beating Ness's Nightmare will grant a party-wide stat boost. Magicant locations are removed from the multiworld, but contain random junk for yourself.
+ Removed: Magicant will be completely inacessible."""
+ display_name = "Magicant Mode"
+ option_psi_location = 0
+ option_required = 1
+ option_alternate_goal = 2
+ option_optional_boost = 3
+ option_removed = 4
+ default = 0
+
+
+class MonkeyCavesMode(Choice):
+ """Chests: Items required to finish the Monkey Caves will be forcibly placed on the chests that can be found in-between rooms of the monkey caves. The "reward" locations, usually found at the end of a branch, are still random. If you waste chest items, they will need to be replaced via the methods in hunt mode.
+ Hunt: Items required to finish the Monkey Caves will needsell you every minor item needed to be found outside. They can be obtained from the Dusty Dunes drugstore, the Fourside department store, and the pizza shop in either Twoson or Threed.
+ Shop: The monkey outside the Monkey Caves will sell every item needed to complete the caves and is not affected by shop randomization.
+ Solved: The Monkey Caves monkeys will already be moved out of the way and not require any items."""
+ display_name = "Monkey Caves Mode"
+ option_chests = 0
+ option_hunt = 1
+ option_shop = 2
+ option_solved = 3
+ default = 1
+
+
+class ShortenPrayers(DefaultOnToggle):
+ """If enabled, the Prayer cutscenes while fighting Giygas will be skipped, excluding the final one."""
+ display_name = "Skip Prayer Sequences"
+
+
+class RandomStartLocation(Toggle):
+ """If disabled, you will always start at Ness's house with no teleports unlocked.
+ If enabled, you will start at a random teleport destination with one teleport unlocked.
+ Additionally, you will need to fight Captain Strong to access the north part of Onett if this is enabled."""
+ display_name = "Random Starting Location"
+
+
+class LocalTeleports(Toggle):
+ """Forces all teleports and Poo PSI to be placed locally in your world."""
+ display_name = "Local Teleports"
+
+
+class CharacterShuffle(Choice):
+ """Shuffled: Characters will be shuffled amongst Character Locations. Extra locations will have Flying Man, a Teddy Bear, or a Super Plush Bear.
+ Anywhere: Characters can be found anywhere in the multiworld, and character locations will have regular checks.
+ See the Game Page for more information on Character Locations."""
+ display_name = "Character Shuffle"
+ option_shuffled = 0
+ option_anywhere = 1
+ default = 0
+
+
+class PSIShuffle(Choice):
+ """None: Characters will learn their normal PSI skills.
+ Basic: Offensive and Assist PSI will be shuffled. Recovery PSI is not modified. Ness's Favorite Thing will be named Wave in other slots.
+ Extended: Basic shuffle, but includes Jeff gadgets and some combat items.
+ See the Game Page for more information."""
+ display_name = "PSI Shuffle"
+ option_none = 0
+ option_basic = 1
+ option_extended = 2
+
+
+class BossShuffle(PlandoBosses):
+ """Shuffles boss encounters amongst each other."""
+ display_name = "Boss Shuffle"
+
+ option_false = 0
+ option_true = 1
+ default = 0
+ bosses = boss_plando_keys
+ locations = boss_plando_keys
+ duplicate_bosses = False
+
+ @classmethod
+ def can_place_boss(cls, boss: str, location: str) -> bool:
+ return True
+
+
+class DecoupleDiamondDog(Toggle):
+ """Shuffles Diamond Dog as a boss separate from Carbon Dog. Carbon Dog will transform into a random boss.
+ Does nothing if Boss Shuffle is disabled."""
+ display_name = "Decouple Diamond Dog"
+
+
+class ShuffleGiygas(Toggle):
+ """Adds the standalone Giygas fight to the shuffled boss pool.
+ This only applies to the second phase Giygas. The prayer fight is not affected.
+ Does nothing if Boss Shuffle is disabled."""
+ display_name = "Add Giygas to Boss Pool"
+
+
+class BanFlashFavorite(Toggle):
+ """If enabled, allows PSI Flash to be shuffled onto the Favorite Thing PSI slot. Can be quite annoying early-game.
+ Does nothing if PSI Shuffle is set to None."""
+ display_name = "Flash as Favorite"
+
+
+class PreFixItems(Toggle):
+ """If enabled, broken items in the multiworld pool will be replaced with their fixed versions.
+ This does not affect any items that are not placed by the multiworld."""
+ display_name = "Prefixed Items"
+
+
+class AutoscaleParty(Toggle):
+ """If enabled, joining party members will be scaled to roughly the level of the sphere they were obtained in."""
+ display_name = "Autoscale Party Members"
+
+
+class ProgressiveWeapons(Toggle):
+ """If enabled, Bats, Fry Pans, and Guns will be progressive. Does not apply to items dropped by enemies or found in shops."""
+ display_name = "Progressive Weapons"
+
+
+class ProgressiveArmor(Toggle):
+ """If enabled, Bracelets and items for the Other slot besides Ribbons will be progressive. Does not apply to items dropped by enemies or found in shops."""
+ display_name = "Progressive Armor"
+
+
+class PresentSprites(DefaultOnToggle):
+ """If enabled, Presents, Trash cans, and chests will have their appearance modified to be indicative of the item they contain."""
+ display_name = "Match Present Sprites"
+
+
+class NoAPPresents(Toggle):
+ """If enabled, present that contain items for other players will appear as EarthBound presents (trashcan, present, and chest) instead of Archipelago boxes.
+ Does nothing if Presents Match Contents is disabled."""
+
+
+class ShuffleDrops(Toggle):
+ """If enabled, enemies will drop random filler items. This does not put checks on enemy drops.
+ Drop rates are unchanged."""
+ display_name = "Shuffle Drops"
+
+
+class RandomFranklinBadge(Toggle):
+ """If enabled, the Franklin Badge will reflect a randomly selected attack type. The type can be determined from the item's name, as well as the help
+ text for it. The badge's function outside of battle will not change, and neither will its name outside of the game itself."""
+ display_name = "Franklin Badge Protection"
+
+
+class CommonWeight(Range):
+ """Weight for placing a common filler item."""
+ display_name = "Common Filler Weight"
+ range_start = 1
+ range_end = 100
+ default = 80
+
+
+class UncommonWeight(Range):
+ """Weight for placing an uncommon filler item."""
+ display_name = "Uncommon Filler Weight"
+ range_start = 1
+ range_end = 100
+ default = 30
+
+
+class RareWeight(Range):
+ """Weight for placing a rare filler item."""
+ display_name = "Rare Filler Weight"
+ range_start = 0
+ range_end = 100
+ default = 5
+
+
+class MoneyWeight(Range):
+ """Weight for placing money in the item pool."""
+ display_name = "Money Weight"
+ range_start = 0
+ range_end = 100
+ default = 0
+
+
+class ExperienceModifier(Range):
+ """Percentage of EXP enemies give you. 100 is vanilla, after scaling, and 300 is x3."""
+ display_name = "Experience Percentage"
+ range_start = 100
+ range_end = 300
+ default = 150
+
+
+class StartingMoney(Range):
+ """How much money you start with."""
+ display_name = "Starting Money"
+ range_start = 0
+ range_end = 99999
+ default = 20
+
+
+class EasyDeaths(DefaultOnToggle):
+ """Fully revives and heals all party members after death. If off, only Ness will be healed with 0 PP."""
+ display_name = "Easy Deaths"
+
+
+class RandomFlavors(DefaultOnToggle):
+ """Randomizes the non-plain window color options."""
+ display_name = "Random Flavors"
+
+
+class DeathLinkMode(Choice):
+ """Controls how receiving a Deathlink functions in battle.
+ Instant: The player will be instantly defeated.
+ Mortal: All characters will receieve mortal damage. The player will not be able to heal until the battle is finished.
+ Mortal Mercy: All characters will receieve mortal damage, but the player will be able to heal it before they die.
+ Regardless of this setting, receiving a deathlink outside of battle will always instantly defeat the player."""
+ display_name = "Death Link Mode"
+ option_instant = 0
+ option_mortal = 1
+ option_mortal_mercy = 2
+ default = 1
+
+
+class RandomBattleBG(Toggle):
+ """Generates random battle backgrounds."""
+ display_name = "Randomize Battle Backgrounds"
+
+
+class RandomSwirlColors(Toggle):
+ """Generates random colors for pre-battle swirls."""
+ display_name = "Randomize Swirl Colors"
+
+
+class RemoteItems(Toggle):
+ """If enabled, you will receive your own items from the server upon collecting them, rather than locally.
+ This allows co-op within the same game, and protects against loss of save data.
+ However, you will not be able to play offline if this is enabled."""
+ display_name = "Remote Items"
+
+
+class PlandoLumineHallText(FreeText):
+ """Set text to be displayed at Lumine Hall. If nothing is entered, random community-submitted text will be selected instead."""
+ display_name = "Lumine Hall Text Plando"
+ visibility = Visibility.none
+
+
+class Armorizer(Choice):
+ """All equippable armor will have randomly generated attributes. This includes who can equip it, elemental resistance (and how strong that resistance is),
+ defense, and the secondary stat it increases (Either Luck or Speed, depending on armor slot.) Choosing "Help!" from the Goods menu will give you exact details
+ on that piece of equipment.
+ Keep Type: Equipment will keep its original equipment slot. If Progressive Armor is enabled, you will get armor with progressively higher defense.
+ Chaos: Equipment will have a randomly selected slot. It will try to respect the defense progressively, but the type may not match the type received."""
+ display_name = "Armorizer"
+ option_off = 0
+ option_keep_type = 1
+ option_chaos = 2
+ default = 0
+
+
+class Weaponizer(Choice):
+ """All weapons will have randomly generated attributes. This includes offense, guts boost, and miss rate.
+ Keep Type: Equipment will keep the character that was originally able to use it. If Progressive Weapons is enabled, you will get weapons with progressively higher offense.
+ Chaos: Equipment will be able to be equipped by a randomly selected character. It will try to respect the offense progresively, but the type may not match the type recieved.
+ The Tee Ball Bat will always be a weapon for Ness."""
+ display_name = "Weaponizer"
+ option_off = 0
+ option_keep_type = 1
+ option_chaos = 2
+ default = 0
+
+
+class ElementChance(Range):
+ """Percent chance for any given Body/Other equipment to have elemental protection.
+ Affects Armorizer only."""
+ display_name = "Elemental Resistance Chance"
+ range_start = 1
+ range_end = 50
+ default = 15
+
+
+class NoFreeSancs(Toggle):
+ """If enabled, the entrance to Lilliput Steps and Fire Spring will be locked and require extra key items to access.
+ These items are the Tiny Key and Tenda Lavapants, respectively."""
+ display_name = "No Free Sanctuaries"
+
+
+class RandomizeFanfares(Choice):
+ """Randomizes fanfares."""
+ display_name = "Randomize Fanfares"
+ option_off = 0
+ option_on = 1
+ option_on_no_sound_stone_fanfares = 2
+ default = 0
+
+
+class RandomizeBattleMusic(Toggle):
+ """Randomizes in-battle songs."""
+ display_name = "Randomize Battle Music"
+
+
+class RandomizeOverworldMusic(Choice):
+ """Randomizes music on the overworld. Some sound effects might sound weird.
+ Normal: Does not randomize music.
+ Match Type: Music will be randomized with similar song categories (Town, dungeon, etc.)
+ Full: Overworld music will be randomized disregarding categories."""
+ display_name = "Overworld Music Randomizer"
+ option_normal = 0
+ option_match_type = 1
+ option_full = 2
+ default = 0
+
+
+class RandomizePSIPalettes(Choice):
+ """Randomizes the colors of PSI spells.
+ Normal: Doesn't randomize PSI colors.
+ Shuffled: PSI spell palettes are swapped around with each other.
+ Randomized: PSI spells use completely random colors."""
+ display_name = "Random PSI Palettes"
+ option_normal = 0
+ option_shuffled = 1
+ option_randomized = 2
+ default = 0
+
+
+class ShopRandomizer(Choice):
+ """Randomizes items in shops.
+ Local Filler: Shops contain only random items for yourself and are not checks.
+ Shopsanity. Every shop slot in the game contains a Multiworld location. ONLY ENABLE SHOPSANITY IF YOU KNOW WHAT YOU ARE DOING."""
+ display_name = "Shop Randomizer"
+ option_off = 0
+ option_local_filler = 1
+ option_shopsanity = 2
+ default = 0
+
+
+class ScoutShopChecks(Choice):
+ """Scouts Shop checks when you open a shop. Only affects shops in Shopsanity mode."""
+ display_name = "Scout Shop Checks"
+ option_off = 0
+ option_progression_only = 1
+ option_all = 2
+ default = 1
+
+
+class StartingCharacter(Choice):
+ """Sets which character you start as. Each character will always start with the ability to teleport,
+ and the ATM card. Ness will not be required to fight Sanctuary bosses."""
+ display_name = "Starting Character"
+ option_Ness = 0
+ option_Paula = 1
+ option_Jeff = 2
+ option_Poo = 3
+ default = 0
+
+
+class EquipamizerStatCap(DefaultOnToggle):
+ """If enabled, the highest value that Equipamizer can roll for a piece of equipment's
+ main stat will be capped. 80 for armor, 125 for weapons.
+ If disabled, the main stat can potentially roll up to 128."""
+ display_name = "Equipamizer Stat Cap"
+
+
+class MoneyDropMultiplier(Range):
+ """Multiplies money dropped by enemies by the chosen value."""
+ display_name = "Money Drop Multiplier"
+ range_start = 1
+ range_end = 100
+ default = 1
+
+
+class EnemyShuffle(Toggle):
+ """Shuffles Non-boss enemies amongst each other."""
+ display_name = "Enemy Shuffle"
+
+
+class SkipEpilogue(Toggle):
+ """If enabled, the choice to play the epilogue after beating Giygas will be removed, and you will
+ go directly to the credits. This option is mainly for no-release seeds where checks could be
+ potentially spoiled in the open-access epilogue."""
+ display_name = "Skip Epilogue"
+ visibility = Visibility.template
+
+
+class EnergyLink(Toggle):
+ """If enabled, the money in the ATM will be linked across the Archipelago Server.
+ This requires a server connection to be used, but won't break offline play."""
+ display_name = "Energy Link"
+
+
+class DungeonShuffle(Toggle):
+ """Shuffles Dungeon entrances amongst each other."""
+ display_name = "Dungeon Shuffle"
+
+
+class PhotoCount(Range):
+ """How many Photograph traps are placed in the item pool."""
+ display_name = "Photos in pool"
+ range_start = 0
+ range_end = 32
+ default = 20
+
+
+class EasyCombat(Toggle):
+ """Automatically halves all scaled enemy levels."""
+ display_name = "Easy Combat"
+
+
+class EnemizerStats(Toggle):
+ """Randomizes base stats and level of non-boss enemies."""
+ display_name = "Randomize Enemy Stats"
+
+
+class EnemizerAttacks(Toggle):
+ """Randomizes attacks of non-boss enemies."""
+ display_name = "Randomize Enemy Attacks"
+
+
+class EnemizerAttributes(Toggle):
+ """Randomizes most attributes of non-boss enemies."""
+ display_name = "Randomize Enemy Attributes"
+
+
+class RandomMapColors(Choice):
+ """Randomizes map colors.
+ Normal: Uses normal colors
+ Nice: Uses generally good looking palettes for maps with little artifacting.
+ Ugly: Allows map palettes with artifacting or colors that may not look good.
+ Nonsense: Allows really bad palettes or heavy artifacting."""
+ display_name = "Shuffle Map Palettes"
+ option_normal = 0
+ option_nice = 1
+ option_ugly = 2
+ option_nonsense = 3
+ default = 0
+
+class SafeFinalBoss(DefaultOnToggle):
+ """Prevents specific difficult bosses from being randomized onto Heavily Armed Pokey's boss slot.
+ Only affects Boss Shuffle, and does not affect Phase 2 Giygas if Boss Shuffle Add Giygas is enabled."""
+ display_name = "Safe Final Boss"
+
+
+@dataclass
+class EBOptions(PerGameCommonOptions):
+ giygas_required: GiygasRequired
+ sanctuaries_required: SanctuariesRequired
+ skip_prayer_sequences: ShortenPrayers
+ random_start_location: RandomStartLocation
+ alternate_sanctuary_goal: SanctuaryAltGoal
+ magicant_mode: MagicantMode
+ monkey_caves_mode: MonkeyCavesMode
+ local_teleports: LocalTeleports
+ character_shuffle: CharacterShuffle
+ starting_character: StartingCharacter
+ psi_shuffle: PSIShuffle
+ allow_flash_as_favorite_thing: BanFlashFavorite
+ enemy_shuffle: EnemyShuffle
+ boss_shuffle: BossShuffle
+ decouple_diamond_dog: DecoupleDiamondDog
+ boss_shuffle_add_giygas: ShuffleGiygas
+ safe_final_boss: SafeFinalBoss
+ randomize_enemy_attributes: EnemizerAttributes
+ randomize_enemy_stats: EnemizerStats
+ randomize_enemy_attacks: EnemizerAttacks
+ experience_modifier: ExperienceModifier
+ money_drop_multiplier: MoneyDropMultiplier
+ starting_money: StartingMoney
+ easy_deaths: EasyDeaths
+ easy_combat: EasyCombat
+ progressive_weapons: ProgressiveWeapons
+ progressive_armor: ProgressiveArmor
+ armorizer: Armorizer
+ weaponizer: Weaponizer
+ armorizer_resistance_chance: ElementChance
+ equipamizer_cap_stats: EquipamizerStatCap
+ auto_scale_party_members: AutoscaleParty
+ remote_items: RemoteItems
+ random_flavors: RandomFlavors
+ random_battle_backgrounds: RandomBattleBG
+ random_swirl_colors: RandomSwirlColors
+ presents_match_contents: PresentSprites
+ nonlocal_items_use_local_presents: NoAPPresents
+ prefixed_items: PreFixItems
+ total_photos: PhotoCount
+ randomize_franklinbadge_protection: RandomFranklinBadge
+ shuffle_enemy_drops: ShuffleDrops
+ common_filler_weight: CommonWeight
+ uncommon_filler_weight: UncommonWeight
+ rare_filler_weight: RareWeight
+ money_weight: MoneyWeight
+ plando_lumine_hall_text: PlandoLumineHallText
+ no_free_sanctuaries: NoFreeSancs
+ randomize_overworld_music: RandomizeOverworldMusic
+ randomize_battle_music: RandomizeBattleMusic
+ randomize_fanfares: RandomizeFanfares
+ randomize_psi_palettes: RandomizePSIPalettes
+ map_palette_shuffle: RandomMapColors
+ shop_randomizer: ShopRandomizer
+ scout_shop_checks: ScoutShopChecks
+ dungeon_shuffle: DungeonShuffle
+ skip_epilogue: SkipEpilogue
+ start_inventory_from_pool: StartInventoryPool
+ death_link: DeathLink
+ death_link_mode: DeathLinkMode
+ energy_link: EnergyLink
+
+
+eb_option_groups = [
+ OptionGroup("Goal Settings", [
+ GiygasRequired,
+ SanctuariesRequired,
+ SanctuaryAltGoal
+ ]),
+
+ OptionGroup("Item Settings", [
+ LocalTeleports,
+ CharacterShuffle,
+ ProgressiveWeapons,
+ ProgressiveArmor,
+ RandomFranklinBadge,
+ CommonWeight,
+ UncommonWeight,
+ RareWeight,
+ MoneyWeight,
+ PreFixItems,
+ PhotoCount
+ ]),
+
+ OptionGroup("Equipamizer", [
+ Armorizer,
+ Weaponizer,
+ ElementChance,
+ EquipamizerStatCap
+ ]),
+
+ OptionGroup("World Modes", [
+ RandomStartLocation,
+ MagicantMode,
+ MonkeyCavesMode,
+ NoFreeSancs,
+ StartingCharacter
+ ]),
+
+ OptionGroup("PSI Randomization", [
+ PSIShuffle,
+ BanFlashFavorite
+ ]),
+
+ OptionGroup("Enemy Randomization", [
+ EnemyShuffle,
+ BossShuffle,
+ SafeFinalBoss,
+ DecoupleDiamondDog,
+ ShuffleGiygas,
+ ExperienceModifier,
+ ShuffleDrops,
+ MoneyDropMultiplier
+ ]),
+
+ OptionGroup("Enemizer", [
+ EnemizerAttributes,
+ EnemizerAttacks,
+ EnemizerStats
+ ]),
+
+ OptionGroup("Shop Randomization", [
+ ShopRandomizer,
+ ScoutShopChecks
+ ]),
+
+ OptionGroup("Entrance Randomization", [
+ DungeonShuffle
+ ]),
+
+ OptionGroup("Convenience Settings", [
+ ShortenPrayers,
+ EasyDeaths,
+ StartingMoney,
+ RemoteItems,
+ AutoscaleParty,
+ SkipEpilogue,
+ EasyCombat
+ ]),
+
+ OptionGroup("Aesthetic Settings", [
+ RandomFlavors,
+ RandomSwirlColors,
+ RandomBattleBG,
+ RandomMapColors,
+ PresentSprites,
+ NoAPPresents,
+ RandomizePSIPalettes,
+ PlandoLumineHallText
+ ]),
+
+ OptionGroup("Music Randomizer", [
+ RandomizeOverworldMusic,
+ RandomizeBattleMusic,
+ RandomizeFanfares
+ ]),
+
+ OptionGroup("Multiplayer Features", [
+ DeathLink,
+ DeathLinkMode,
+ EnergyLink
+ ])
+]
diff --git a/worlds/earthbound/Regions.py b/worlds/earthbound/Regions.py
new file mode 100644
index 000000000000..b5bea536b7cb
--- /dev/null
+++ b/worlds/earthbound/Regions.py
@@ -0,0 +1,279 @@
+from typing import List, Dict, TYPE_CHECKING, Optional
+from BaseClasses import Region, Location
+from .Locations import LocationData
+from .Options import MagicantMode
+if TYPE_CHECKING:
+ from . import EarthBoundWorld
+
+
+class EBLocation(Location):
+ game: str = "EarthBound"
+
+
+def init_areas(world: "EarthBoundWorld", locations: list[LocationData]) -> None:
+ multiworld = world.multiworld
+ player = world.player
+
+ locations_per_region = get_locations_per_region(locations)
+
+ regions = [
+ create_region(world, player, locations_per_region, "Menu"),
+ create_region(world, player, locations_per_region, "Ness's Mind"),
+ create_region(world, player, locations_per_region, "Global ATM Access"),
+ create_region(world, player, locations_per_region, "Northern Onett"),
+ create_region(world, player, locations_per_region, "Onett"),
+ create_region(world, player, locations_per_region, "Arcade"),
+ create_region(world, player, locations_per_region, "Giant Step"),
+ create_region(world, player, locations_per_region, "Twoson"),
+ create_region(world, player, locations_per_region, "Common Condiment Shop"),
+ create_region(world, player, locations_per_region, "Everdred's House"),
+ create_region(world, player, locations_per_region, "Peaceful Rest Valley"),
+ create_region(world, player, locations_per_region, "Happy-Happy Village"),
+ create_region(world, player, locations_per_region, "Happy-Happy HQ"),
+ create_region(world, player, locations_per_region, "Lilliput Steps"),
+ create_region(world, player, locations_per_region, "Threed"),
+ create_region(world, player, locations_per_region, "Threed Underground"),
+ create_region(world, player, locations_per_region, "Boogey Tent"),
+ create_region(world, player, locations_per_region, "Grapefruit Falls"),
+ create_region(world, player, locations_per_region, "Belch's Factory"),
+ create_region(world, player, locations_per_region, "Saturn Valley"),
+ create_region(world, player, locations_per_region, "Upper Saturn Valley"),
+ create_region(world, player, locations_per_region, "Milky Well"),
+ create_region(world, player, locations_per_region, "Dusty Dunes Desert"),
+ create_region(world, player, locations_per_region, "Gold Mine"),
+ create_region(world, player, locations_per_region, "Monkey Caves"),
+ create_region(world, player, locations_per_region, "Fourside"),
+ create_region(world, player, locations_per_region, "Moonside"),
+ create_region(world, player, locations_per_region, "Fourside Dept. Store"),
+ create_region(world, player, locations_per_region, "Magnet Hill"),
+ create_region(world, player, locations_per_region, "Monotoli Building"),
+ create_region(world, player, locations_per_region, "Winters"),
+ create_region(world, player, locations_per_region, "Snow Wood Boarding School"),
+ create_region(world, player, locations_per_region, "Southern Winters"),
+ create_region(world, player, locations_per_region, "Brickroad Maze"),
+ create_region(world, player, locations_per_region, "Rainy Circle"),
+ create_region(world, player, locations_per_region, "Andonuts Lab Area"),
+ create_region(world, player, locations_per_region, "Stonehenge Base"),
+ create_region(world, player, locations_per_region, "Summers"),
+ create_region(world, player, locations_per_region, "Summers Museum"),
+ create_region(world, player, locations_per_region, "Dalaam"),
+ create_region(world, player, locations_per_region, "Pink Cloud"),
+ create_region(world, player, locations_per_region, "Scaraba"),
+ create_region(world, player, locations_per_region, "Pyramid"),
+ create_region(world, player, locations_per_region, "Southern Scaraba"),
+ create_region(world, player, locations_per_region, "Dungeon Man"),
+ create_region(world, player, locations_per_region, "Deep Darkness"),
+ create_region(world, player, locations_per_region, "Deep Darkness Darkness"),
+ create_region(world, player, locations_per_region, "Tenda Village"),
+ create_region(world, player, locations_per_region, "Lumine Hall"),
+ create_region(world, player, locations_per_region, "Lost Underworld"),
+ create_region(world, player, locations_per_region, "Fire Spring"),
+ create_region(world, player, locations_per_region, "Magicant"),
+ create_region(world, player, locations_per_region, "Sea of Eden"),
+ create_region(world, player, locations_per_region, "Cave of the Present")
+
+ ]
+ if world.options.giygas_required:
+ regions.extend([
+ create_region(world, player, locations_per_region, "Cave of the Past"),
+ create_region(world, player, locations_per_region, "Endgame")
+ ])
+ multiworld.regions += regions
+
+def connect_area_exits(world: "EarthBoundWorld"):
+ multiworld = world.multiworld
+ player = world.player
+
+ connect_menu_region(world)
+ arcade_connection = world.dungeon_connections["Arcade"]
+ giant_step_connection = world.dungeon_connections["Giant Step"]
+ lilliput_steps_connection = world.dungeon_connections["Lilliput Steps"]
+ happy_happy_hq_connection = world.dungeon_connections["Happy-Happy HQ"]
+ belch_factory_connection = world.dungeon_connections["Belch's Factory"]
+ milky_well_connection = world.dungeon_connections["Milky Well"]
+ gold_mine_connection = world.dungeon_connections["Gold Mine"]
+ moonside_connection = world.dungeon_connections["Moonside"]
+ monotoli_building_connection = world.dungeon_connections["Monotoli Building"]
+ magnet_hill_connection = world.dungeon_connections["Magnet Hill"]
+ pink_cloud_connection = world.dungeon_connections["Pink Cloud"]
+ pyramid_connection = world.dungeon_connections["Pyramid"]
+ dungeon_man_connection = world.dungeon_connections["Dungeon Man"]
+ rainy_circle_connection = world.dungeon_connections["Rainy Circle"]
+ stonehenge_connection = world.dungeon_connections["Stonehenge Base"]
+ lumine_hall_connection = world.dungeon_connections["Lumine Hall"]
+ fire_spring_connection = world.dungeon_connections["Fire Spring"]
+ sea_of_eden_connection = world.dungeon_connections["Sea of Eden"]
+ brickroad_maze_connection = world.dungeon_connections["Brickroad Maze"]
+
+ multiworld.get_region("Ness's Mind", player).add_exits(["Onett", "Twoson", "Happy-Happy Village", "Threed", "Saturn Valley", "Dusty Dunes Desert", "Fourside", "Winters", "Summers", "Dalaam", "Scaraba", "Deep Darkness", "Tenda Village", "Lost Underworld", "Magicant"],
+ {"Onett": lambda state: state.has("Onett Teleport", player),
+ "Twoson": lambda state: state.has("Twoson Teleport", player),
+ "Happy-Happy Village": lambda state: state.has("Happy-Happy Village Teleport", player),
+ "Threed": lambda state: state.has("Threed Teleport", player),
+ "Saturn Valley": lambda state: state.has("Saturn Valley Teleport", player),
+ "Dusty Dunes Desert": lambda state: state.has("Dusty Dunes Teleport", player),
+ "Fourside": lambda state: state.has("Fourside Teleport", player),
+ "Winters": lambda state: state.has("Winters Teleport", player),
+ "Summers": lambda state: state.has("Summers Teleport", player),
+ "Dalaam": lambda state: state.has("Dalaam Teleport", player),
+ "Scaraba": lambda state: state.has("Scaraba Teleport", player),
+ "Deep Darkness": lambda state: state.has("Deep Darkness Teleport", player),
+ "Tenda Village": lambda state: state.has("Tenda Village Teleport", player),
+ "Lost Underworld": lambda state: state.has("Lost Underworld Teleport", player),
+ "Magicant": lambda state: state.has_any({"Magicant Teleport", "Magicant Unlock"}, player)})
+ multiworld.get_region("Northern Onett", player).add_exits(["Onett"])
+
+ multiworld.get_region("Onett", player).add_exits([giant_step_connection, "Twoson", "Northern Onett", "Global ATM Access", arcade_connection],
+ {giant_step_connection: lambda state: state.has("Key to the Shack", player),
+ "Twoson": lambda state: state.has("Police Badge", player),
+ "Northern Onett": lambda state: state.has("Police Badge", player)})
+
+ multiworld.get_region("Twoson", player).add_exits(["Onett", "Peaceful Rest Valley", "Threed", "Everdred's House", "Global ATM Access", "Common Condiment Shop"],
+ {"Onett": lambda state: state.has("Police Badge", player),
+ "Peaceful Rest Valley": lambda state: state.has_any({"Pencil Eraser", "Valley Bridge Repair"}, player),
+ "Threed": lambda state: state.has_any({"Threed Tunnels Clear", "Wad of Bills"}, player),
+ "Everdred's House": lambda state: state.has("Paula", player)})
+
+ multiworld.get_region("Peaceful Rest Valley", player).add_exits(["Twoson", "Happy-Happy Village"],
+ {"Twoson": lambda state: state.has_any({"Pencil Eraser", "Valley Bridge Repair"}, player)})
+
+ multiworld.get_region("Happy-Happy Village", player).add_exits(["Peaceful Rest Valley", lilliput_steps_connection, "Global ATM Access", happy_happy_hq_connection])
+
+ multiworld.get_region("Threed", player).add_exits(["Twoson", "Dusty Dunes Desert", "Andonuts Lab Area", "Threed Underground", "Boogey Tent", "Global ATM Access"],
+ {"Twoson": lambda state: state.has("Threed Tunnels Clear", player),
+ "Dusty Dunes Desert": lambda state: state.has("Threed Tunnels Clear", player),
+ "Andonuts Lab Area": lambda state: state.has_all({"UFO Engine", "Bad Key Machine"}, player),
+ "Threed Underground": lambda state: state.has("Zombie Paper", player),
+ "Boogey Tent": lambda state: state.has("Jeff", player)})
+
+ multiworld.get_region("Threed Underground", player).add_exits(["Grapefruit Falls"])
+
+ multiworld.get_region("Grapefruit Falls", player).add_exits([belch_factory_connection, "Saturn Valley", "Threed Underground"],
+ {belch_factory_connection: lambda state: state.has("Jar of Fly Honey", player)})
+
+ multiworld.get_region(belch_factory_connection, player).add_exits(["Upper Saturn Valley"],
+ {"Upper Saturn Valley": lambda state: state.has("Threed Tunnels Clear", player)})
+
+ multiworld.get_region("Saturn Valley", player).add_exits(["Grapefruit Falls", "Cave of the Present", "Global ATM Access"],
+ {"Cave of the Present": lambda state: state.has("Meteorite Piece", player)})
+
+ multiworld.get_region("Upper Saturn Valley", player).add_exits([milky_well_connection, "Saturn Valley"])
+
+ multiworld.get_region("Dusty Dunes Desert", player).add_exits(["Threed", "Monkey Caves", gold_mine_connection, "Fourside", "Global ATM Access"],
+ {"Threed": lambda state: state.has("Threed Tunnels Clear", player),
+ "Monkey Caves": lambda state: state.has("King Banana", player),
+ gold_mine_connection: lambda state: state.has("Mining Permit", player)})
+
+ multiworld.get_region("Fourside", player).add_exits(["Dusty Dunes Desert", monotoli_building_connection, magnet_hill_connection, "Threed", "Fourside Dept. Store", "Global ATM Access", moonside_connection],
+ {monotoli_building_connection: lambda state: state.has("Yogurt Dispenser", player),
+ magnet_hill_connection: lambda state: state.has("Signed Banana", player),
+ "Threed": lambda state: state.has("Diamond", player),
+ "Fourside Dept. Store": lambda state: state.has("Jeff", player)})
+
+ multiworld.get_region("Moonside", player).add_exits(["Global ATM Access"])
+
+ multiworld.get_region("Summers", player).add_exits(["Scaraba", "Summers Museum", "Global ATM Access"],
+ {"Summers Museum": lambda state: state.has("Tiny Ruby", player)})
+
+ multiworld.get_region("Winters", player).add_exits(["Snow Wood Boarding School", "Southern Winters", "Global ATM Access"],
+ {"Snow Wood Boarding School": lambda state: state.has("Letter For Tony", player),
+ "Southern Winters": lambda state: state.has("Pak of Bubble Gum", player)})
+
+ multiworld.get_region("Southern Winters", player).add_exits([brickroad_maze_connection])
+
+ multiworld.get_region(brickroad_maze_connection, player).add_exits(["Southern Winters", rainy_circle_connection])
+
+ multiworld.get_region(rainy_circle_connection, player).add_exits([brickroad_maze_connection, "Andonuts Lab Area"])
+
+ multiworld.get_region("Andonuts Lab Area", player).add_exits([stonehenge_connection, "Winters", rainy_circle_connection],
+ {stonehenge_connection: lambda state: state.has("Eraser Eraser", player)})
+
+ multiworld.get_region("Dalaam", player).add_exits([pink_cloud_connection],
+ {pink_cloud_connection: lambda state: state.has("Carrot Key", player)})
+
+ multiworld.get_region("Scaraba", player).add_exits([pyramid_connection, "Global ATM Access", "Common Condiment Shop"],
+ {pyramid_connection: lambda state: state.has("Hieroglyph Copy", player)})
+
+ multiworld.get_region(pyramid_connection, player).add_exits(["Southern Scaraba"])
+
+ multiworld.get_region("Southern Scaraba", player).add_exits([dungeon_man_connection],
+ {dungeon_man_connection: lambda state: state.has_any({"Key to the Tower"}, player)})
+
+ multiworld.get_region("Dungeon Man", player).add_exits(["Deep Darkness"],
+ {"Deep Darkness": lambda state: state.has("Submarine to Deep Darkness", player)})
+
+ multiworld.get_region("Deep Darkness", player).add_exits(["Deep Darkness Darkness"],
+ {"Deep Darkness Darkness": lambda state: state.has("Hawk Eye", player)})
+
+ multiworld.get_region("Deep Darkness Darkness", player).add_exits(["Tenda Village", "Deep Darkness"])
+
+ multiworld.get_region("Tenda Village", player).add_exits([lumine_hall_connection, "Deep Darkness Darkness"],
+ {lumine_hall_connection: lambda state: state.has("Shyness Book", player),
+ "Deep Darkness Darkness": lambda state: state.has_all({"Shyness Book", "Hawk Eye"}, player)})
+
+ multiworld.get_region("Lumine Hall", player).add_exits(["Lost Underworld"])
+
+ multiworld.get_region("Lost Underworld", player).add_exits([fire_spring_connection])
+
+ if world.options.giygas_required:
+ multiworld.get_region("Cave of the Present", player).add_exits(["Cave of the Past"],
+ {"Cave of the Past": lambda state: state.has("Power of the Earth", player)})
+
+ multiworld.get_region("Cave of the Past", player).add_exits(["Endgame"],
+ {"Endgame": lambda state: state.has("Paula", player)})
+
+ if world.options.magicant_mode < MagicantMode.option_optional_boost: # 3
+ multiworld.get_region("Magicant", player).add_exits(["Global ATM Access", sea_of_eden_connection],
+ {sea_of_eden_connection: lambda state: state.has("Ness", player)})
+
+
+def create_location(player: int, location_data: LocationData, region: Region) -> Location:
+ location = EBLocation(player, location_data.name, location_data.code, region)
+
+ return location
+
+
+def create_region(world: "EarthBoundWorld", player: int, locations_per_region: Dict[str, List[LocationData]], name: str) -> Region:
+ region = Region(name, player, world.multiworld)
+
+ if name in locations_per_region:
+ for location_data in locations_per_region[name]:
+ location = create_location(player, location_data, region)
+ region.locations.append(location)
+
+ return region
+
+
+def get_locations_per_region(locations: List[LocationData]) -> Dict[str, List[LocationData]]:
+ per_region: Dict[str, List[LocationData]] = {}
+
+ for location in locations:
+ per_region.setdefault(location.region, []).append(location)
+
+ return per_region
+
+
+def connect_menu_region(world: "EarthBoundWorld") -> None:
+ starting_region_list = {
+ 0: "Northern Onett",
+ 1: "Onett",
+ 2: "Twoson",
+ 3: "Happy-Happy Village",
+ 4: "Threed",
+ 5: "Saturn Valley",
+ 6: "Fourside",
+ 7: "Winters",
+ 8: "Summers",
+ 9: "Dalaam",
+ 10: "Scaraba",
+ 11: "Deep Darkness",
+ 12: "Tenda Village",
+ 13: "Lost Underworld",
+ 14: "Magicant"
+ }
+
+ world.starting_region = starting_region_list[world.start_location]
+ world.multiworld.get_region("Menu", world.player).add_exits([world.starting_region, "Ness's Mind"],
+ {"Ness's Mind": lambda state: state.has_any({"Ness", "Paula", "Jeff", "Poo"}, world.player),
+ world.starting_region: lambda state: state.has_any({"Ness", "Paula", "Jeff", "Poo"}, world.player)})
+
\ No newline at end of file
diff --git a/worlds/earthbound/Rom.py b/worlds/earthbound/Rom.py
new file mode 100644
index 000000000000..af23200e1459
--- /dev/null
+++ b/worlds/earthbound/Rom.py
@@ -0,0 +1,848 @@
+import hashlib
+import os
+import Utils
+import typing
+import struct
+import settings
+from worlds.Files import APProcedurePatch, APTokenMixin, APTokenTypes, APPatchExtension
+from .game_data import local_data
+from .game_data.battle_bg_data import battle_bg_bpp
+from .modules.psi_shuffle import write_psi
+from .game_data.text_data import barf_text, text_encoder
+from .modules.flavor_data import flavor_data, vanilla_flavor_pointers
+from .modules.hint_data import parse_hint_data
+from .modules.enemy_data import scale_enemies
+from .modules.area_scaling import calculate_scaling
+from .modules.boss_shuffle import write_bosses
+from .modules.equipamizer import randomize_armor, randomize_weapons
+from .modules.music_rando import music_randomizer
+from .modules.palette_shuffle import randomize_psi_palettes, map_palette_shuffle
+from .modules.shopsanity import write_shop_checks
+from .modules.enemy_shuffler import apply_enemy_shuffle
+from .modules.dungeon_er import write_dungeon_entrances
+# from .modules.foodamizer import randomize_food
+from .modules.enemizer.randomize_enemy_attributes import randomize_enemy_attributes
+from .modules.enemizer.randomize_enemy_stats import randomize_enemy_stats
+from .modules.enemizer.randomize_enemy_attacks import randomize_enemy_attacks
+from .game_data.static_location_data import location_groups
+from BaseClasses import ItemClassification
+from typing import TYPE_CHECKING, Sequence
+from logging import warning
+# from .local_data import local_locations
+
+if TYPE_CHECKING:
+ from . import EarthBoundWorld
+
+item_id_table = local_data.item_id_table
+location_dialogue = local_data.location_dialogue
+present_locations = local_data.present_locations
+psi_locations = local_data.psi_locations
+npc_locations = local_data.npc_locations
+character_locations = local_data.character_locations
+special_name_table = local_data.special_name_table
+item_space_checks = local_data.item_space_checks
+local_present_types = local_data.local_present_types
+present_text_pointers = local_data.present_text_pointers
+psi_item_table = local_data.psi_item_table
+character_item_table = local_data.character_item_table
+party_id_nums = local_data.party_id_nums
+starting_psi_table = local_data.starting_psi_table
+badge_names = local_data.badge_names
+world_version = local_data.world_version
+protection_checks = local_data.protection_checks
+protection_text = local_data.protection_text
+nonlocal_present_types = local_data.nonlocal_present_types
+ap_text_pntrs = local_data.ap_text_pntrs
+money_item_table = local_data.money_item_table
+
+valid_hashes = ["a864b2e5c141d2dec1c4cbed75a42a85", # Cartridge
+ "6d71ccc8e2afda15d011348291afdf4f"] # VC
+
+
+class LocalRom(object):
+
+ def __init__(self, file: bytes, name: str | None = None) -> None:
+ self.file = bytearray(file)
+ self.name = name
+
+ def read_byte(self, offset: int) -> int:
+ return self.file[offset]
+
+ def read_bytes(self, offset: int, length: int) -> bytes:
+ return self.file[offset:offset + length]
+
+ def write_bytes(self, offset: int, values: Sequence[int]) -> None:
+ self.file[offset:offset + len(values)] = values
+
+ def get_bytes(self) -> bytes:
+ return bytes(self.file)
+
+
+def patch_rom(world: "EarthBoundWorld", rom: LocalRom, player: int) -> None:
+ rom.copy_bytes(0x1578DD, 0x3E, 0x34A060) # Threed/Saturn teleport move
+ rom.copy_bytes(0x15791B, 0xF8, 0x157959)
+
+ rom.copy_bytes(0x34A000, 0x1F, 0x1578DD)
+ rom.copy_bytes(0x34A020, 0x1F, 0x15793A)
+ rom.copy_bytes(0x34A040, 0x1F, 0x157A51)
+ rom.copy_bytes(0x34A060, 0x3E, 0x1578FC)
+ rom.copy_bytes(0x15ED4B, 0x06, 0x15F1FB)
+ rom.copy_bytes(0x1A7FA7, 0xBF, 0x389900)
+
+ starting_area_coordinates = {
+ 0: [0x50, 0x04, 0xB5, 0x1F], # North Onett
+ 1: [0x52, 0x06, 0x4C, 0x1F], # Onett
+ 2: [0xEF, 0x22, 0x41, 0x1F], # Twoson
+ 3: [0x53, 0x06, 0x85, 0x1D], # Happy Happy
+ 4: [0x55, 0x24, 0x69, 0x1D], # Threed
+ 5: [0x60, 0x1D, 0x30, 0x01], # Saturn Valley
+ 6: [0xAB, 0x10, 0xF3, 0x09], # Fourside
+ 7: [0xE3, 0x09, 0xA3, 0x1D], # Winters
+ 8: [0xCB, 0x24, 0x7B, 0x1E], # Summers
+ 9: [0xD0, 0x1E, 0x31, 0x1D], # Dalaam
+ 10: [0xC7, 0x1F, 0x37, 0x19], # Scaraba
+ 11: [0xDD, 0x1B, 0xB7, 0x17], # Deep Darkness
+ 12: [0xD0, 0x25, 0x47, 0x18], # Tenda Village
+ 13: [0x9C, 0x00, 0x84, 0x17], # Lost Underworld
+ 14: [0x4B, 0x11, 0xAD, 0x18] # Magicant
+ }
+
+ starting_levels = {
+ "Ness": 0x15F5FB,
+ "Paula": 0x15F60F,
+ "Jeff": 0x15F623,
+ "Poo": 0x15F637
+ }
+
+ atm_card_slots = {
+ "Ness": 0x15F5FF,
+ "Paula": 0x15F613,
+ "Jeff": 0x15F629,
+ "Poo": 0x15F63B
+ }
+
+ starting_weapon = {
+ "Ness": [0x15F600, 0x12],
+ "Paula": [0x15F615, 0x1C],
+ "Jeff": [0x15F62A, 0x24]
+ }
+
+ teleport_learnlevel = {
+ "Ness": [0x158D53, 0x158D62],
+ "Paula": [0x158D54, 0x158D63],
+ "Poo": [0x158D55, 0x158D64]
+ }
+ world.start_items = []
+ world.handled_locations = []
+
+ for item in world.multiworld.precollected_items[world.player]:
+ world.start_items.append(item.name)
+
+ if world.options.random_start_location:
+ rom.write_bytes(0x0F96C2, bytearray([0x69, 0x00]))
+ rom.write_bytes(0x0F9618, bytearray([0x69, 0x00]))
+ rom.write_bytes(0x0F9629, bytearray([0x69, 0x00])) # Block Northern Onett
+ else:
+ rom.write_bytes(0x00B66A, bytearray([0x06])) # Fix starting direction
+
+ rom.write_bytes(0x01FE9B, bytearray(starting_area_coordinates[world.start_location][0:2]))
+ rom.write_bytes(0x01FE9E, bytearray(starting_area_coordinates[world.start_location][2:4])) # Start position
+
+ rom.write_bytes(0x01FE91, bytearray(starting_area_coordinates[world.start_location][0:2]))
+ rom.write_bytes(0x01FE8B, bytearray(starting_area_coordinates[world.start_location][2:4])) # Respawn position
+
+ if world.options.skip_epilogue:
+ rom.write_bytes(0x09C4D4, struct.pack("I", 0xEEA437))
+
+ if world.starting_character == "Poo":
+ rom.write_bytes(starting_levels[world.starting_character], bytearray([0x06]))
+ else:
+ rom.write_bytes(starting_levels[world.starting_character], bytearray([0x03]))
+ rom.write_bytes(atm_card_slots[world.starting_character], bytearray([0xB1]))
+ if world.starting_character != "Ness":
+ rom.write_bytes(atm_card_slots["Ness"], bytearray([0x58]))
+ if world.starting_character != "Poo":
+ rom.write_bytes(starting_weapon[world.starting_character][0], bytearray([starting_weapon[world.starting_character][1]]))
+
+ if world.starting_character != "Jeff":
+ for i in range(2):
+ rom.write_bytes(teleport_learnlevel[world.starting_character][i - 1], bytearray([0x01]))
+ else:
+ rom.write_bytes(0x15F62B, bytearray([0xB5]))
+
+ if world.options.alternate_sanctuary_goal:
+ rom.write_bytes(0x04FD72, bytearray([world.options.sanctuaries_required.value + 2]))
+ else:
+ rom.write_bytes(0x04FD72, bytearray([0xFF]))
+
+ if not world.options.giygas_required:
+ rom.write_bytes(0x2E9C29, bytearray([0x10, 0xA5]))
+
+ if world.options.magicant_mode == 2:
+ rom.write_bytes(0x04FD71, bytearray([world.options.sanctuaries_required.value + 1]))
+ rom.write_bytes(0x2EA26A, bytearray([0x0A, 0x10, 0xA5, 0xEE])) # Alt goal magicant sets the credits
+ elif world.options.magicant_mode == 1:
+ rom.write_bytes(0x2E9C29, bytearray([0x00, 0xA5]))
+ if world.options.giygas_required:
+ rom.write_bytes(0x2EA26A, bytearray([0x08, 0xD9, 0x9B, 0xEE])) # Give stat boost if magicant + giygas required
+ else:
+ rom.write_bytes(0x2EA26A, bytearray([0x0A, 0x10, 0xA5, 0xEE])) # If no giygas, set credits
+ elif world.options.magicant_mode == 3:
+ rom.write_bytes(0x2EA26A, bytearray([0x08, 0x0F, 0x9C, 0xEE])) # Give only stat boost if set to boost
+
+ rom.write_bytes(0x04FD74, bytearray([world.options.death_link.value]))
+ rom.write_bytes(0x04FD75, bytearray([world.options.death_link_mode.value]))
+ rom.write_bytes(0x04FD76, bytearray([world.options.remote_items.value]))
+ rom.write_bytes(0x04FD78, bytearray([world.options.energy_link.value]))
+
+ if world.options.death_link_mode != 1:
+ rom.write_bytes(0x2FFDFE, bytearray([0x80])) # Mercy healing
+ rom.write_bytes(0x2FFE30, bytearray([0x80])) # Mercy text
+ rom.write_bytes(0x2FFE59, bytearray([0x80])) # Mercy revive
+ # IF YOU ADD ASM, CHANGE THESE OR THE GAME WILL CRASH
+
+ if world.options.monkey_caves_mode == 2:
+ rom.write_bytes(0x062B87, bytearray([0x0A, 0x28, 0xCA, 0xEE]))
+ elif world.options.monkey_caves_mode == 3:
+ rom.write_bytes(0x0F1388, bytearray([0x03, 0xCA, 0xEE]))
+
+ if world.options.no_free_sanctuaries:
+ rom.write_bytes(0x0F09F2, bytearray([0x15, 0x84])) # Lock Lilliput steps with flag $0415
+ rom.write_bytes(0x0F09EE, struct.pack("I", 0xEEF790)) # Lilliput door script
+
+ rom.write_bytes(0x0F23D2, bytearray([0x16, 0x84])) # Lock Fire Spring with flag $0146
+ rom.write_bytes(0x0F23CE, struct.pack("I", 0xEEF946)) # Fire Spring door script
+
+ rom.write_bytes(0x04FD70, bytearray([world.options.sanctuaries_required.value]))
+ shop_checks = []
+
+ for location in world.multiworld.get_locations(player):
+ if location.address:
+ receiver_name = world.multiworld.get_player_name(location.item.player)
+ name = location.name
+ if world.options.remote_items:
+ item = "Remote Item"
+ else:
+ item = location.item.name
+ item_name_loc = (((location.address - 0xEB0000) * 128) + 0x3F0000)
+ # todo; replace with the encoder function
+ item_text = text_encoder(location.item.name, 128)
+ item_text.extend([0x00])
+ player_name_loc = (((location.address - 0xEB0000) * 48) + 0x3F8000)
+ player_text = text_encoder(receiver_name, 48)
+ # Locations over this address are Shopsanity locations and handled in the shopsanity module
+ if location.address < 0xEB1000:
+ rom.write_bytes(item_name_loc, bytearray(item_text))
+ rom.write_bytes(player_name_loc, bytearray(player_text))
+
+ if item not in item_id_table or location.item.player != location.player:
+ item_id = 0xAD
+ else:
+ item_id = item_id_table[item]
+
+ if name in location_dialogue:
+ for i in range(len(location_dialogue[name])):
+ if location.item.player != location.player or item == "Remote Item":
+ rom.write_bytes(location_dialogue[name][i] - 1, bytearray([0x17, location.address - 0xEB0000]))
+ elif item in item_id_table:
+ rom.write_bytes(location_dialogue[name][i], bytearray([item_id]))
+ elif item in psi_item_table or item in character_item_table:
+ rom.write_bytes(location_dialogue[name][i] - 1, bytearray([0x16, special_name_table[item][0]]))
+ elif item in money_item_table:
+ rom.write_bytes(location_dialogue[name][i] - 1, bytearray([0x16, (0x16 + list(money_item_table).index(item))]))
+
+ if name in present_locations:
+ world.handled_locations.append(name)
+ if item == "Nothing": # I can change this to "In nothing_table" later todo: make it so nonlocal items do not follow this table
+ rom.write_bytes(present_locations[name], bytearray([0x00, 0x00, 0x01]))
+ elif location.item.player != location.player or item == "Remote Item":
+ rom.write_bytes(present_locations[name], bytearray([item_id, 0x00, 0x00, (location.address - 0xEB0000)]))
+ elif item in item_id_table:
+ rom.write_bytes(present_locations[name], bytearray([item_id, 0x00]))
+ elif item in psi_item_table:
+ rom.write_bytes(present_locations[name], bytearray([psi_item_table[item], 0x00, 0x02]))
+ elif item in character_item_table:
+ rom.write_bytes(present_locations[name], bytearray([character_item_table[item][0], 0x00, 0x03]))
+ elif item in money_item_table:
+ rom.write_bytes(present_locations[name], struct.pack("H", money_item_table[item]))
+ rom.write_bytes(present_locations[name] + 2, bytearray([0x01]))
+
+ if name in npc_locations:
+ world.handled_locations.append(name)
+ for i in range(len(npc_locations[name])):
+ if item in item_id_table or location.item.player != location.player or item == "Remote Item":
+ rom.write_bytes(npc_locations[name][i], bytearray([item_id]))
+ elif item in psi_item_table or item in character_item_table:
+ rom.write_bytes(npc_locations[name][i] - 3, bytearray([0x0E, 0x00, 0x0E, (special_name_table[item][0] + 1)]))
+ rom.write_bytes(npc_locations[name][i] + 2, bytearray([0xA5, 0xAA, 0xEE]))
+ elif item in money_item_table:
+ rom.write_bytes(npc_locations[name][i] - 3, bytearray([0x1D, 0x25]))
+ rom.write_bytes(npc_locations[name][i] - 1, struct.pack("H", money_item_table[item]))
+ rom.write_bytes(npc_locations[name][i] + 2, bytearray([0x00, 0xF0, 0xF3]))
+
+ if name in psi_locations:
+ world.handled_locations.append(name)
+ if item in special_name_table and location.item.player == location.player and item != "Remote Item":
+ rom.write_bytes(psi_locations[name][0], special_name_table[item][1].to_bytes(3, byteorder="little"))
+ rom.write_bytes(psi_locations[name][0] + 4, bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+ elif item in money_item_table and location.item.player == location.player:
+ rom.write_bytes(psi_locations[name][0] - 1, bytearray([0x1D, 0x25]))
+ rom.write_bytes(psi_locations[name][0] + 1, struct.pack("H", money_item_table[item]))
+ rom.write_bytes(psi_locations[name][0] + 3, bytearray([0x08, 0x26, 0xF0, 0xF3, 0x00, 0x03, 0x00]))
+ else:
+ rom.write_bytes(psi_locations[name][0], bytearray(psi_locations[name][1:4]))
+ rom.write_bytes(psi_locations[name][4], bytearray([item_id]))
+
+ if name in character_locations:
+ world.handled_locations.append(name)
+ if item in character_item_table and location.item.player == location.player and item != "Remote Item":
+ rom.write_bytes(character_locations[name][0], special_name_table[item][1].to_bytes(3, byteorder="little"))
+ if name == "Snow Wood - Bedroom": # Use lying down sprites for the bedroom check
+ rom.write_bytes(character_locations[name][1], struct.pack("H", character_item_table[item][2]))
+ rom.write_bytes(0x0FB0D8, bytearray([0x06]))
+ else:
+ rom.write_bytes(character_locations[name][1], struct.pack("H", character_item_table[item][1]))
+ elif item in psi_item_table and location.item.player == location.player:
+ rom.write_bytes(character_locations[name][0], (special_name_table[item][1] + 1).to_bytes(3, byteorder="little"))
+ rom.write_bytes(character_locations[name][1], bytearray([0x62]))
+ rom.write_bytes(character_locations[name][2], bytearray([0x70, 0xF9, 0xD5]))
+ elif item in money_item_table and location.item.player == location.player:
+ rom.write_bytes(character_locations[name][2] - 1, bytearray([0x1D, 0x25]))
+ rom.write_bytes(character_locations[name][2] + 1, struct.pack("H", money_item_table[item]))
+ rom.write_bytes(character_locations[name][0] - 2, bytearray([0x01]))
+ rom.write_bytes(character_locations[name][0], bytearray([0x4B, 0xF0, 0xF3]))
+ rom.write_bytes(character_locations[name][1], bytearray([0x97]))
+ else:
+ rom.write_bytes(character_locations[name][0], bytearray(character_locations[name][4:7]))
+ if location.item.name in ["Ness", "Paula", "Jeff", "Poo"]:
+ rom.write_bytes(character_locations[name][1], bytearray([character_item_table[location.item.name][1]]))
+ else:
+ rom.write_bytes(character_locations[name][1], bytearray([0x97]))
+ rom.write_bytes(character_locations[name][2], bytearray([0x18, 0xF9, 0xD5]))
+ rom.write_bytes(character_locations[name][3], bytearray([item_id]))
+ if name == "Deep Darkness - Barf Character":
+ if item in character_item_table:
+ rom.write_bytes(0x2EA0E2, bytearray(barf_text[item][0:3]))
+ rom.write_bytes(0x2EA0E8, bytearray(barf_text[item][3:6]))
+ elif item in psi_item_table and location.item.player == location.player:
+ rom.write_bytes(0x2EA0E2, bytearray([0x98, 0xC3, 0xEE]))
+ rom.write_bytes(0x2EA0E8, bytearray([0xF7, 0xC4, 0xEE]))
+ else:
+ rom.write_bytes(0x2EA0E2, bytearray([0x6A, 0xC3, 0xEE]))
+ rom.write_bytes(0x2EA0E8, bytearray([0xB4, 0xC4, 0xEE]))
+
+ if name == "Poo - Starting Item":
+ world.handled_locations.append(name)
+ if item in item_id_table and location.item.player == location.player and item != "Remote Item":
+ rom.write_bytes(0x15F63C, bytearray([item_id]))
+ else:
+ rom.write_bytes(0x15F63C, bytearray([0x00])) # Don't give anything if the item doesn't have a tangible ID
+
+ if item in special_name_table and location.item.player == location.player: # Apply a special script if teleport or character
+ rom.write_bytes(0x15F765, special_name_table[item][1].to_bytes(3, byteorder="little")) # This might be offset, check if it is
+ rom.write_bytes(0x2EC618, bytearray([(special_name_table[item][0] + 1)]))
+ rom.write_bytes(0x2EC61A, bytearray([0xA5, 0xAA, 0xEE]))
+ rom.write_bytes(0x2EC613, bytearray([0x03, 0x01]))
+
+ if item in money_item_table and location.item.player == location.player:
+ rom.write_bytes(0x15F764, bytearray([0x1D, 0x08]))
+ rom.write_bytes(0x15F766, struct.pack("H", money_item_table[item]))
+ rom.write_bytes(0x15F768, bytearray([0x01]))
+
+ if location.address >= 0xEB1000:
+ world.handled_locations.append(name)
+ shop_checks.append(location)
+
+ if name not in world.handled_locations:
+ warning(f"{name} not placed in {world.multiworld.get_player_name(world.player)}'s EarthBound world. Something went wrong here.")
+
+ if name in item_space_checks:
+ if item not in item_id_table or location.item.player != location.player:
+ if len(item_space_checks[name]) == 4:
+ rom.write_bytes(item_space_checks[name][0], bytearray(item_space_checks[name][1:4]))
+ else:
+ rom.write_bytes(item_space_checks[name][0], bytearray(item_space_checks[name][1:4]))
+ rom.write_bytes(item_space_checks[name][4], bytearray(item_space_checks[name][5:8]))
+
+ if name in present_locations and "Lost Underworld" not in name and world.options.presents_match_contents:
+ if ItemClassification.trap in location.item.classification:
+ world.present_type = "trap"
+ elif ItemClassification.progression in location.item.classification:
+ world.present_type = "progression"
+ elif ItemClassification.useful in location.item.classification:
+ world.present_type = "useful"
+ else:
+ world.present_type = "filler"
+
+ if location.item.player == world.player or world.options.nonlocal_items_use_local_presents:
+ rom.write_bytes(present_locations[name] - 12, bytearray(local_present_types[world.present_type]))
+ if name != "Threed - Boogey Tent Trashcan":
+ rom.write_bytes(present_locations[name] - 4, bytearray(present_text_pointers[world.present_type]))
+ else:
+ rom.write_bytes(present_locations[name] - 12, bytearray(nonlocal_present_types[world.present_type]))
+ if name != "Threed - Boogey Tent Trashcan":
+ if world.present_type == "progression":
+ rom.write_bytes(present_locations[name] - 4, struct.pack("I", world.random.choice(ap_text_pntrs)))
+ elif world.present_type == "trap":
+ rom.write_bytes(present_locations[name] - 4, bytearray([0x8D, 0xce, 0xee]))
+ else:
+ rom.write_bytes(present_locations[name] - 4, bytearray([0xc1, 0xcd, 0xee]))
+
+ location = world.multiworld.get_location("Twoson - Bike Shop Rental", world.player)
+ if location.item.name in item_id_table:
+ item_id = item_id_table[location.item.name]
+ else:
+ item_id = 0xAD
+
+ rom.write_bytes(0x0800C4, bytearray([item_id])) # Bike shop
+ rom.write_bytes(0x0802EA, bytearray([item_id]))
+
+ rom.write_bytes(0x2EA05C, bytearray([item_id_table[world.slime_pile_wanted_item]]))
+ rom.write_bytes(0x2F61F6, bytearray([item_id_table[world.slime_pile_wanted_item]]))
+
+ hintable_locations = [
+ location for location in world.multiworld.get_locations()
+ if location.player == world.player or location.item.player == world.player
+ ]
+ world.hint_pointer = 0x0000
+ world.hint_number = 0
+ for index, hint in enumerate(world.in_game_hint_types):
+ if hint == "item_at_location":
+ for location in hintable_locations:
+ if location.name == world.hinted_locations[index] and location.player == world.player:
+ parse_hint_data(world, location, rom, hint, index)
+
+ elif hint == "region_progression_check":
+ world.progression_count = 0
+ for location in hintable_locations:
+ if location.name in location_groups[world.hinted_regions[index]] and location.player == world.player:
+ if ItemClassification.progression in location.item.classification:
+ world.progression_count += 1
+ world.hinted_area = world.hinted_regions[index] # im doing a little sneaky
+ parse_hint_data(world, location, rom, hint, index)
+
+ elif hint == "hint_for_good_item" or hint == "prog_item_at_region":
+ hintable_locations_2 = []
+ for location in hintable_locations:
+ if location.item.name == world.hinted_items[index] and location.item.player == world.player:
+ hintable_locations_2.append(location)
+ if hintable_locations_2 == []:
+ # This is just failsafe behavior
+ warning(f"Warning: Unable to create local hint for {world.hinted_items[index]} for "
+ + f"{world.multiworld.get_player_name(world.player)}'s EarthBound world."
+ + " Please report this.")
+ location = world.random.choice(hintable_locations)
+ else:
+ location = world.random.choice(hintable_locations_2)
+ parse_hint_data(world, location, rom, hint, index)
+
+ elif hint == "item_in_local_region":
+ for location in hintable_locations:
+ if location.name == world.hinted_locations[index] and location.player == world.player:
+ parse_hint_data(world, location, rom, hint, index)
+ else:
+ location = "null"
+ parse_hint_data(world, location, rom, hint, index)
+
+ for location in hintable_locations:
+ if location.item.name == "Paula":
+ world.paula_region = location.parent_region
+
+ if location.item.name == "Jeff":
+ world.jeff_region = location.parent_region
+
+ if location.item.name == "Poo":
+ world.poo_region = location.parent_region
+
+ if world.options.skip_prayer_sequences:
+ rom.write_bytes(0x07BC96, bytearray([0x02]))
+ rom.write_bytes(0x07BA2C, bytearray([0x02]))
+ rom.write_bytes(0x07BAC7, bytearray([0x02]))
+ rom.write_bytes(0x07BB38, bytearray([0x02]))
+ rom.write_bytes(0x07BBF3, bytearray([0x02]))
+ rom.write_bytes(0x07BC56, bytearray([0x02]))
+ rom.write_bytes(0x07B9A1, bytearray([0x1f, 0xeb, 0xff, 0x02, 0x1f, 0x1f, 0xca, 0x01, 0x06, 0x1f, 0x1f, 0x72, 0x01, 0x06, 0x02])) # Clean up overworld stuff
+
+ if world.options.easy_deaths:
+ rom.write_bytes(0x2EBFF9, bytearray([0x0A]))
+ rom.write_bytes(0x04C7CE, bytearray([0x5C, 0x8A, 0xFB, 0xEF])) # Jump to code that restores the party
+ rom.write_bytes(0x04C7D4, bytearray([0xEA, 0xEA, 0xEA]))
+ # rom.write_bytes(0x04C7DA, bytearray([0xEA, 0xEA]))#Stop the game from zeroing stuff
+ rom.write_bytes(0x0912F2, bytearray([0x0A, 0xFE, 0xBF, 0xEE]))
+ rom.write_bytes(0x2EBFFE, bytearray([0x00, 0x1B, 0x04, 0x15, 0x38, 0x1F, 0x81, 0xFF, 0xFF, 0x1B, 0x04, 0x0A, 0xF7, 0x12, 0xC9])) # Hospitals = 0$
+ rom.write_bytes(0x04C822, bytearray([0xEA, 0xEA, 0xEA, 0xEA]))
+ rom.write_bytes(0x04C7F7, bytearray([0x00, 0x00])) # Stop "rounding up" the money
+
+ if world.options.magicant_mode >= 2:
+ rom.write_bytes(0x077629, bytearray([item_id_table[world.magicant_junk[0]]]))
+ rom.write_bytes(0x077614, bytearray([item_id_table[world.magicant_junk[0]]]))
+ rom.write_bytes(0x0FF25C, bytearray([item_id_table[world.magicant_junk[1]]]))
+ rom.write_bytes(0x0FF27E, bytearray([item_id_table[world.magicant_junk[2]]]))
+ rom.write_bytes(0x0FF28F, bytearray([item_id_table[world.magicant_junk[3]]]))
+ rom.write_bytes(0x0FF2A0, bytearray([item_id_table[world.magicant_junk[4]]]))
+ rom.write_bytes(0x0FF26D, bytearray([item_id_table[world.magicant_junk[5]]]))
+
+ rom.write_bytes(0x02EC1AA, bytearray([world.options.sanctuaries_required.value]))
+ if world.options.alternate_sanctuary_goal and world.options.giygas_required:
+ rom.write_bytes(0x02EC1E2, bytearray([0xFD, 0xC1, 0xEE]))
+
+ if world.options.magicant_mode == 1 and world.options.giygas_required: # Apple kid text
+ rom.write_bytes(0x2EC1D8, bytearray([0x33, 0xC2, 0xEE]))
+ elif world.options.magicant_mode == 2:
+ rom.write_bytes(0x2EC1D8, bytearray([0x6A, 0xC2, 0xEE]))
+
+ if not world.options.giygas_required:
+ rom.write_bytes(0x2EC164, bytearray([0xE8, 0xF0, 0xEE]))
+ rom.write_bytes(0x02EC1E2, bytearray([0x40, 0xC1, 0xEE]))
+ rom.write_bytes(0x02EC1E2, bytearray([0x40, 0xC1, 0xEE]))
+
+ flavor_address = 0x3FAF10
+ for i in range(4):
+ rom.copy_bytes(world.flavor_pointer[i], 2, 0x34B110 + (2 * i))
+
+ rom.copy_bytes(0x202008, 0x100, 0x34B000)
+ for i in range(4):
+ if world.available_flavors[i] not in ["Mint flavor", "Strawberry flavor", "Banana flavor", "Peanut flavor"]:
+ rom.write_bytes(flavor_address, bytearray(world.flavor_text[i]))
+ flavor_addr = flavor_address - 0x3F0000
+ flavor_addr = struct.pack("H", flavor_addr)
+ rom.write_bytes(world.flavor_pointer[i], flavor_addr)
+ rom.write_bytes(world.flavor_pointer[i] + 5, bytearray([0xFF]))
+ flavor_address += len(world.flavor_text[i])
+ rom.write_bytes(0x202008 + (0x40 * i), bytearray(flavor_data[world.available_flavors[i]]))
+ else:
+ rom.copy_bytes(vanilla_flavor_pointers[world.available_flavors[i]][1], 0x40, 0x202008 + (0x40 * i))
+ rom.copy_bytes(vanilla_flavor_pointers[world.available_flavors[i]][2], 2, world.flavor_pointer[i])
+
+ rom.write_bytes(0x048037, bytearray(world.lumine_text))
+ starting_item_address = 0
+ starting_psi = 0
+ starting_char = 0
+ starting_psi_types = []
+ starting_character_count = []
+ starting_inventory_pointers = {
+ "Ness": 0x99F3,
+ "Paula": 0x9A53,
+ "Jeff": 0x9AB4,
+ "Poo": 0x9B0F
+ }
+
+ starting_inv_amounts = {
+ "Ness": 0x0B,
+ "Paula": 0x0A,
+ "Jeff": 0x08,
+ "Poo": 0x0C
+ }
+
+ location = world.multiworld.get_location("Poo - Starting Item", world.player)
+ if world.starting_character == "Poo" and location.item.name in item_id_table and location.item.player == world.player:
+ starting_inventory_pointers["Poo"] = 0x9B10
+ starting_inv_amounts["Poo"] = 0x0B
+ rom.write_bytes(0x16FB66, struct.pack("H", starting_inventory_pointers[world.starting_character]))
+ rom.write_bytes(0x16FB68, struct.pack("H", starting_inv_amounts[world.starting_character]))
+
+ for item in world.multiworld.precollected_items[player]:
+ if item.name == world.starting_character: # Write the starting character
+ rom.write_bytes(0x00B672, bytearray([world.options.starting_character.value + 1]))
+
+ if world.options.remote_items:
+ continue
+
+ if item.name == "Photograph":
+ rom.write_bytes(0x17FEA8, bytearray([0x01]))
+
+ if item.name == "Poo" and world.multiworld.get_location("Poo - Starting Item", world.player).item.name in special_name_table and item.player == world.player:
+ world.multiworld.push_precollected(world.multiworld.get_location("Poo - Starting Item", world.player).item)
+ elif item.name == "Poo" and world.multiworld.get_location("Poo - Starting Item", world.player).item.name in money_item_table:
+ world.starting_money += money_item_table[world.multiworld.get_location("Poo - Starting Item", world.player).item.name]
+
+ # if item.name == "Poo" and world.multiworld.get_location("Poo - Starting Item", world.player).item.name == "Photograph":
+ # rom.write_bytes(0x17FEA9, bytearray([0x01]))
+
+ if item.name in ["Progressive Bat", "Progressive Fry Pan", "Progressive Gun", "Progressive Bracelet",
+ "Progressive Other"]:
+ old_item_name = item.name
+ item.name = world.progressive_item_groups[item.name][world.start_prog_counts[item.name]]
+ if world.start_prog_counts[old_item_name] != len(world.progressive_item_groups[old_item_name]) - 1:
+ world.start_prog_counts[old_item_name] += 1
+
+ if item.name in item_id_table:
+ rom.write_bytes(0x375000 + starting_item_address, bytearray([item_id_table[item.name]]))
+ starting_item_address += 1
+ elif item.name in psi_item_table:
+ if item.name != "Progressive Poo PSI":
+ if item.name not in starting_psi_types:
+ rom.write_bytes(0x17FC7C + starting_psi, bytearray([starting_psi_table[item.name]]))
+ starting_psi_types.append(item.name)
+ starting_psi += 1
+ else:
+ if starting_psi_types.count(item.name) < 2:
+ rom.write_bytes(0x17FC7C + starting_psi, bytearray([starting_psi_table[item.name]]))
+ starting_psi_types.append(item.name)
+ starting_psi += 1
+ elif item.name in character_item_table and item.name != "Photograph":
+ if item.name not in starting_character_count:
+ rom.write_bytes(0x17FC8D + starting_char, bytearray([party_id_nums[item.name]]))
+ starting_character_count.append(item.name)
+ starting_char += 1
+ elif item.name in money_item_table:
+ world.starting_money += money_item_table[item]
+
+ if world.options.random_battle_backgrounds:
+ bpp2_bgs = [bg_id for bg_id, bpp in battle_bg_bpp.items() if bpp == 2]
+ bpp4_bgs = [bg_id for bg_id, bpp in battle_bg_bpp.items() if bpp == 4]
+ for i in range(483):
+ world.flipped_bg = world.random.randint(0, 100)
+ if i == 480:
+ drawn_background = struct.pack("H", 0x00E3)
+ else:
+ drawn_background = struct.pack("H", world.random.randint(0x01, 0x0146))
+
+ if battle_bg_bpp[struct.unpack("H", drawn_background)[0]] == 4:
+ drawn_background_2 = struct.pack("H", 0x0000)
+ else:
+ drawn_background_2 = struct.pack("H", world.random.choice(bpp2_bgs))
+ #print(f"ello mate we are doing background {i} at {hex(0xCBD89A + (i * 4))}, the background is {drawn_background[0]}.")
+ if world.flipped_bg > 33 or drawn_background not in bpp2_bgs:
+ rom.write_bytes(0x0BD89A + (i * 4), drawn_background)
+ rom.write_bytes(0x0BD89C + (i * 4), drawn_background_2)
+ else:
+ rom.write_bytes(0x0BD89A + (i * 4), drawn_background_2)
+ rom.write_bytes(0x0BD89C + (i * 4), drawn_background)
+
+ rom.write_bytes(0x00B5F1, struct.pack("H", world.random.choice(bpp4_bgs)))
+
+ if world.options.random_swirl_colors:
+ if world.random.random() < 0.5:
+ rom.write_bytes(0x02E98A, bytearray([0x7F])) # Color math mode
+ rom.write_bytes(0x02E996, bytearray([0x3F]))
+
+ rom.write_bytes(0x300240, bytearray([world.random.randint(0x00, 0x1F)])) # Normal swirls
+ rom.write_bytes(0x300245, bytearray([world.random.randint(0x00, 0x1F)]))
+ rom.write_bytes(0x30024A, bytearray([world.random.randint(0x00, 0x1F)]))
+
+ rom.write_bytes(0x300253, bytearray([world.random.randint(0x00, 0x1F)])) # Green swirls
+ rom.write_bytes(0x300258, bytearray([world.random.randint(0x00, 0x1F)]))
+ rom.write_bytes(0x30025D, bytearray([world.random.randint(0x00, 0x1F)]))
+
+ rom.write_bytes(0x300269, bytearray([world.random.randint(0x00, 0x1F)])) # Red swirls
+ rom.write_bytes(0x30026E, bytearray([world.random.randint(0x00, 0x1F)]))
+ rom.write_bytes(0x300273, bytearray([world.random.randint(0x00, 0x1F)]))
+
+ if not world.options.prefixed_items:
+ rom.write_bytes(0x15F9DC, bytearray([0x06]))
+ rom.write_bytes(0x15F9DE, bytearray([0x08]))
+ rom.write_bytes(0x15F9E0, bytearray([0x05]))
+ rom.write_bytes(0x15F9E2, bytearray([0x0B]))
+ rom.write_bytes(0x15F9E4, bytearray([0x0F]))
+ rom.write_bytes(0x15F9E6, bytearray([0x10]))
+ # change if necessary
+
+ if world.options.psi_shuffle:
+ write_psi(world, rom)
+
+ world.description_pointer = 0x1000
+ if world.options.armorizer:
+ randomize_armor(world, rom)
+
+ if world.options.weaponizer:
+ randomize_weapons(world, rom)
+
+ music_randomizer(world, rom)
+ if world.options.map_palette_shuffle:
+ map_palette_shuffle(world, rom)
+ if world.options.randomize_psi_palettes:
+ randomize_psi_palettes(world, rom)
+
+ if world.options.randomize_enemy_attributes:
+ randomize_enemy_attributes(world, rom)
+
+ if world.options.randomize_enemy_stats:
+ randomize_enemy_stats(world, rom)
+
+ if world.options.randomize_enemy_attacks:
+ randomize_enemy_attacks(world, rom)
+
+ apply_enemy_shuffle(world, rom)
+ # randomize_food(world,rom)
+ write_bosses(world, rom)
+ if world.options.dungeon_shuffle:
+ write_dungeon_entrances(world, rom)
+
+ world.get_all_spheres.wait()
+ calculate_scaling(world)
+ if world.options.shop_randomizer:
+ write_shop_checks(world, rom, shop_checks)
+
+ scale_enemies(world, rom)
+ world.badge_name = badge_names[world.franklin_protection]
+ world.badge_name = text_encoder(world.badge_name, 23)
+ world.badge_name.extend([0x00])
+ world.starting_money = min(world.starting_money, 99999)
+ world.starting_money = struct.pack(' bytes:
+ return get_base_rom_bytes()
+
+ def write_bytes(self, offset: int, value: typing.Iterable[int]) -> None:
+ self.write_token(APTokenTypes.WRITE, offset, bytes(value))
+
+ def copy_bytes(self, source: int, amount: int, destination: int) -> None:
+ self.write_token(APTokenTypes.COPY, destination, (amount, source))
+
+
+class EBPatchExtensions(APPatchExtension):
+ game = "EarthBound"
+
+ @staticmethod
+ def repoint_vanilla_tables(caller: APProcedurePatch, rom: bytes) -> bytes:
+ rom = LocalRom(rom)
+ version_check = rom.read_bytes(0x3FF0A0, 16)
+ version_check = version_check.split(b'\x00', 1)[0]
+ version_check_str = version_check.decode("ascii")
+ client_version = world_version
+ if client_version != version_check_str and version_check_str != "":
+ raise Exception(f"Error! Patch generated on EarthBound APWorld version {version_check_str} doesn't match client version {client_version}! " +
+ f"Please use EarthBound APWorld version {version_check_str} for patching.")
+ elif version_check_str == "":
+ raise Exception(f"Error! Patch generated on old EarthBound APWorld version, doesn't match client version {client_version}! " +
+ f"Please verify you are using the same APWorld as the generator.")
+
+ for action_number in range(0x013F):
+ current_action = rom.read_bytes(0x157B68 + (12 * action_number), 12)
+ rom.write_bytes(0x3FAFB0 + (12 * action_number), current_action)
+
+ for psi_number in range(0x35):
+ current_action = rom.read_bytes(0x158A50 + (15 * psi_number), 15)
+ rom.write_bytes(0x350000 + (15 * psi_number), current_action)
+
+ psi_text_table = rom.read_bytes(0x158D7A, (25 * 17))
+ rom.write_bytes(0x3B0500, psi_text_table)
+
+ psi_anim_config = rom.read_bytes(0x0CF04D, 0x0198)
+ rom.write_bytes(0x360000, psi_anim_config)
+
+ psi_anim_pointers = rom.read_bytes(0x0CF58F, 0x088)
+ rom.write_bytes(0x360400, psi_anim_pointers)
+
+ psi_anim_palettes = rom.read_bytes(0x0CF47F, 0x0110)
+ rom.write_bytes(0x360600, psi_anim_palettes)
+
+ for psi_number in range(0x32):
+ psi_anim = rom.read_bytes(0x2F8583 + (0x04 * psi_number), 4)
+ rom.write_bytes(0x3B0003 + (4 * psi_number), psi_anim)
+ rom.write_bytes(0x3B0003, bytearray([0x4C]))
+ # rom.write_bytes(0x3B0002, bytearray([0x45]))
+
+ main_font_data = rom.read_bytes(0x210C7A, 96)
+ main_font_gfx = rom.read_bytes(0x210CDA, 0x0C00)
+ saturn_font_data = rom.read_bytes(0x201359, 96)
+ saturn_font_gfx = rom.read_bytes(0x2013B9, 0x0C00)
+ letter_n = rom.read_bytes(0x21169F, 6)
+ letter_a = rom.read_bytes(0x2114FF, 6)
+ letter_e = rom.read_bytes(0x21157F, 6)
+ saturn_a = rom.read_bytes(0x2017DD, 9)
+ saturn_n = rom.read_bytes(0x20197E, 8)
+ saturn_e = rom.read_bytes(0x20185C, 24)
+
+ accent_tilde = rom.read_bytes(0x2118A1, 2)
+ saturn_tilde = rom.read_bytes(0x201F7F, 2)
+
+ rom.write_bytes(0x3A0000, main_font_data)
+ rom.write_bytes(0x3C0000, main_font_gfx)
+
+ rom.write_bytes(0x3A0100, saturn_font_data)
+ rom.write_bytes(0x3C1000, saturn_font_gfx)
+ rom.write_bytes(0x3C0D25, letter_n) # Setup n
+ rom.write_bytes(0x3C0D45, letter_a) # Setup a
+ rom.write_bytes(0x3C0D65, letter_e)
+ rom.write_bytes(0x3C0D22, accent_tilde)
+
+ rom.write_bytes(0x3C1D25, saturn_n) # Setup n
+ rom.write_bytes(0x3C1D45, saturn_a)
+ rom.write_bytes(0x3C1D63, saturn_e)
+ rom.write_bytes(0x3C1D22, saturn_tilde)
+
+ # ---------------------------------------
+ ness_level = rom.read_bytes(0x15F5FB, 1)
+ paula_level = rom.read_bytes(0x15f60f, 1)
+ jeff_level = rom.read_bytes(0x15f623, 1)
+ poo_level = rom.read_bytes(0x15f637, 1)
+
+ ness_start_exp = rom.read_bytes(0x158F49 + (ness_level[0] * 4), 4)
+ paula_start_exp = rom.read_bytes(0x1590D9 + (paula_level[0] * 4), 4)
+ jeff_start_exp = rom.read_bytes(0x159269 + (jeff_level[0] * 4), 4)
+ poo_start_exp = rom.read_bytes(0x1593F9 + (poo_level[0] * 4), 4)
+
+ rom.write_bytes(0x17FD40, ness_start_exp)
+ rom.write_bytes(0x17FD44, paula_start_exp)
+ rom.write_bytes(0x17FD48, jeff_start_exp)
+ rom.write_bytes(0x17FD4C, poo_start_exp)
+ return rom.get_bytes()
+
+
+def get_base_rom_bytes(file_name: str = "") -> bytes:
+ base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
+ if not base_rom_bytes:
+ file_name = get_base_rom_path(file_name)
+ base_rom_bytes = bytes(Utils.read_snes_rom(open(file_name, "rb")))
+
+ basemd5 = hashlib.md5()
+ basemd5.update(base_rom_bytes)
+ if basemd5.hexdigest() not in valid_hashes:
+ raise Exception('Supplied Base Rom does not match known MD5 for US(1.0) release. '
+ 'Get the correct game and version, then dump it')
+ get_base_rom_bytes.base_rom_bytes = base_rom_bytes
+ return base_rom_bytes
+
+
+def get_base_rom_path(file_name: str = "") -> str:
+ options: settings.Settings = settings.get_settings()
+ if not file_name:
+ file_name = options["earthbound_options"]["rom_file"]
+ if not os.path.exists(file_name):
+ file_name = Utils.user_path(file_name)
+ return file_name
+
+
+# Fix hint text, I have a special idea where I can give it info on a random region
diff --git a/worlds/earthbound/Rules.py b/worlds/earthbound/Rules.py
new file mode 100644
index 000000000000..102886867639
--- /dev/null
+++ b/worlds/earthbound/Rules.py
@@ -0,0 +1,128 @@
+from worlds.generic.Rules import set_rule, forbid_items_for_player, add_rule
+from typing import TYPE_CHECKING
+from .Options import ShopRandomizer, MonkeyCavesMode
+if TYPE_CHECKING:
+ from . import EarthBoundWorld
+
+
+def set_location_rules(world: "EarthBoundWorld") -> None:
+ player = world.player
+ twoson_paula_room_present = world.get_location("Twoson - Paula's Room Present")
+ can_buy_pizza = world.get_location("Threed - Downtown Trashcan")
+
+ set_rule(world.multiworld.get_location("Onett - Traveling Entertainer", player), lambda state: state.has("Key to the Shack", player))
+ set_rule(world.multiworld.get_location("Onett - South Road Present", player), lambda state: state.has("Police Badge", player))
+ set_rule(world.multiworld.get_location("Onett - Tracy Gift", player), lambda state: state.has("Ness", player))
+ set_rule(world.multiworld.get_location("Twoson - Paula's Mother", player), lambda state: state.has("Paula", player))
+ set_rule(world.multiworld.get_location("Twoson - Everdred Meeting", player), lambda state: state.has("Paula", player))
+ set_rule(world.multiworld.get_location("Twoson - Insignificant Location", player), lambda state: state.has("Insignificant Item", player))
+ set_rule(world.multiworld.get_location("Happy-Happy Village - Defeat Carpainter", player), lambda state: state.has("Franklin Badge", player))
+ set_rule(world.multiworld.get_location("Carpainter Defeated", player), lambda state: state.has("Franklin Badge", player))
+ set_rule(world.multiworld.get_location("Happy-Happy Village - Prisoner", player), lambda state: state.has("Key to the Cabin", player))
+ set_rule(world.multiworld.get_location("Threed - Boogey Tent Trashcan", player), lambda state: state.has("Jeff", player))
+ set_rule(world.multiworld.get_location("Threed - Zombie Prisoner", player), lambda state: state.has("Bad Key Machine", player))
+ set_rule(world.multiworld.get_location("Saturn Valley - Post Belch Gift #1", player), lambda state: state.has("Threed Tunnels Clear", player))
+ set_rule(world.multiworld.get_location("Saturn Valley - Post Belch Gift #2", player), lambda state: state.has("Threed Tunnels Clear", player))
+ set_rule(world.multiworld.get_location("Saturn Valley - Post Belch Gift #3", player), lambda state: state.has("Threed Tunnels Clear", player))
+ set_rule(world.multiworld.get_location("Saturn Valley - Saturn Coffee", player), lambda state: state.has("Threed Tunnels Clear", player))
+ set_rule(world.multiworld.get_location("Monkey Caves - Talah Rama Chest #1", player), lambda state: state.has("Pencil Eraser", player))
+ set_rule(world.multiworld.get_location("Monkey Caves - Talah Rama Chest #2", player), lambda state: state.has("Pencil Eraser", player))
+ set_rule(world.multiworld.get_location("Monkey Caves - Talah Rama Gift", player), lambda state: state.has("Pencil Eraser", player))
+ set_rule(world.multiworld.get_location("Monkey Caves - Monkey Power", player), lambda state: state.has("Pencil Eraser", player))
+ set_rule(world.multiworld.get_location("Dusty Dunes - Mine Reward", player), lambda state: state.can_reach_region("Gold Mine", player))
+ set_rule(world.multiworld.get_location("Snow Wood - Upper Right Locker", player), lambda state: state.has("Key to the Locker", player))
+ set_rule(world.multiworld.get_location("Snow Wood - Upper Left Locker", player), lambda state: state.has("Key to the Locker", player))
+ set_rule(world.multiworld.get_location("Snow Wood - Bottom Right Locker", player), lambda state: state.has("Key to the Locker", player))
+ set_rule(world.multiworld.get_location("Snow Wood - Bottom Left Locker", player), lambda state: state.has("Key to the Locker", player))
+ set_rule(world.multiworld.get_location("Fourside - Bakery 2F Gift", player), lambda state: state.has("Contact Lens", player))
+ set_rule(world.multiworld.get_location("Fourside - Department Store Blackout", player), lambda state: state.has("Jeff", player))
+ set_rule(world.multiworld.get_location("Fourside - Venus Gift", player), lambda state: state.has("Diamond", player))
+ set_rule(world.multiworld.get_location("Summers - Museum Item", player), lambda state: state.has("Tiny Ruby", player))
+ set_rule(world.multiworld.get_location("Dalaam - Trial of Mu", player), lambda state: state.has("Poo", player))
+ set_rule(world.multiworld.get_location("Poo - Starting Item", player), lambda state: state.has("Poo", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - North Alcove Truffle", player), lambda state: state.has("Piggy Nose", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Near Land Truffle", player), lambda state: state.has("Piggy Nose", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Present Truffle", player), lambda state: state.has("Piggy Nose", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Village Truffle", player), lambda state: state.has("Piggy Nose", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Entrance Truffle", player), lambda state: state.has("Piggy Nose", player))
+ set_rule(world.multiworld.get_location("Tenda Village - Tenda Tea", player), lambda state: state.has("Shyness Book", player))
+ set_rule(world.multiworld.get_location("Tenda Village - Tenda Gift", player), lambda state: state.has("Shyness Book", player))
+ set_rule(world.multiworld.get_location("Tenda Village - Tenda Gift #2", player), lambda state: state.has("Shyness Book", player))
+ set_rule(world.multiworld.get_location("Lost Underworld - Talking Rock", player), lambda state: state.has("Tendakraut", player))
+ set_rule(world.multiworld.get_location("Sanctuary Goal", player), lambda state: state.has("Melody", player, world.options.sanctuaries_required.value))
+ forbid_items_for_player(world.multiworld.get_location("Poo - Starting Item", player), {"Poo"}, player)
+ forbid_items_for_player(world.multiworld.get_location("Poo - Starting Item", player), {"Progressive Bat"}, player)
+ forbid_items_for_player(world.multiworld.get_location("Poo - Starting Item", player), {"Progressive Gun"}, player)
+ forbid_items_for_player(world.multiworld.get_location("Poo - Starting Item", player), {"Progressive Fry Pan"}, player)
+ forbid_items_for_player(world.multiworld.get_location("Poo - Starting Item", player), {"Progressive Bracelet"}, player)
+ forbid_items_for_player(world.multiworld.get_location("Poo - Starting Item", player), {"Progressive Other"}, player)
+
+ if world.options.giygas_required:
+ set_rule(world.multiworld.get_location("Giygas", player), lambda state: state.has("Paula", player))
+
+ if world.options.monkey_caves_mode < MonkeyCavesMode.option_shop: # 2
+ set_rule(world.multiworld.get_location("Monkey Caves - West 2F Left Chest", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+ set_rule(world.multiworld.get_location("Monkey Caves - East 2F Left Chest", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+ set_rule(world.multiworld.get_location("Monkey Caves - East End Chest", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+ set_rule(world.multiworld.get_location("Monkey Caves - East End Trashcan", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+ set_rule(world.multiworld.get_location("Monkey Caves - East West 3F Right Chest #1", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+ set_rule(world.multiworld.get_location("Monkey Caves - East West 3F Right Chest #2", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+
+ add_rule(world.multiworld.get_location("Monkey Caves - Talah Rama Chest #1", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+ add_rule(world.multiworld.get_location("Monkey Caves - Talah Rama Chest #2", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+ add_rule(world.multiworld.get_location("Monkey Caves - Talah Rama Gift", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+ add_rule(world.multiworld.get_location("Monkey Caves - Monkey Power", player), lambda state: (twoson_paula_room_present.can_reach(state) or can_buy_pizza.can_reach(state)))
+
+ if world.options.no_free_sanctuaries:
+ lilliput_steps = world.multiworld.get_entrance(f"Happy-Happy Village -> {world.dungeon_connections['Lilliput Steps']}", player)
+ fire_spring = world.multiworld.get_entrance(f"Lost Underworld -> {world.dungeon_connections['Fire Spring']}", player)
+ add_rule(fire_spring, lambda state: state.has("Tenda Lavapants", player))
+ add_rule(lilliput_steps, lambda state: state.has("Tiny Key", player))
+
+ if world.options.shop_randomizer == ShopRandomizer.option_shopsanity: # 2
+ set_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 1", player), lambda state: state.has("Tendakraut", player))
+ set_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 2", player), lambda state: state.has("Tendakraut", player))
+ set_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 3", player), lambda state: state.has("Tendakraut", player))
+ set_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 4", player), lambda state: state.has("Tendakraut", player))
+ set_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 5", player), lambda state: state.has("Tendakraut", player))
+ set_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 6", player), lambda state: state.has("Tendakraut", player))
+ set_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 7", player), lambda state: state.has("Tendakraut", player))
+
+ set_rule(world.multiworld.get_location("Dusty Dunes - Mine Food Cart Slot 1", player), lambda state: state.has("Mining Permit", player))
+ set_rule(world.multiworld.get_location("Dusty Dunes - Mine Food Cart Slot 2", player), lambda state: state.has("Mining Permit", player))
+ set_rule(world.multiworld.get_location("Dusty Dunes - Mine Food Cart Slot 3", player), lambda state: state.has("Mining Permit", player))
+ set_rule(world.multiworld.get_location("Dusty Dunes - Mine Food Cart Slot 4", player), lambda state: state.has("Mining Permit", player))
+ set_rule(world.multiworld.get_location("Dusty Dunes - Mine Food Cart Slot 5", player), lambda state: state.has("Mining Permit", player))
+ set_rule(world.multiworld.get_location("Dusty Dunes - Mine Food Cart Slot 6", player), lambda state: state.has("Mining Permit", player))
+ set_rule(world.multiworld.get_location("Dusty Dunes - Mine Food Cart Slot 7", player), lambda state: state.has("Mining Permit", player))
+
+ set_rule(world.multiworld.get_location("Saturn Valley Shop - Post-Belch Saturn Slot 1", player), lambda state: state.has("Threed Tunnels Clear", player))
+ set_rule(world.multiworld.get_location("Saturn Valley Shop - Post-Belch Saturn Slot 2", player), lambda state: state.has("Threed Tunnels Clear", player))
+ set_rule(world.multiworld.get_location("Saturn Valley Shop - Post-Belch Saturn Slot 3", player), lambda state: state.has("Threed Tunnels Clear", player))
+ set_rule(world.multiworld.get_location("Saturn Valley Shop - Post-Belch Saturn Slot 4", player), lambda state: state.has("Threed Tunnels Clear", player))
+
+ set_rule(world.multiworld.get_location("Deep Darkness - Arms Dealer Slot 1", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Arms Dealer Slot 2", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Arms Dealer Slot 3", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Arms Dealer Slot 4", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Businessman Slot 1", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Businessman Slot 2", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Businessman Slot 3", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Businessman Slot 4", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Businessman Slot 5", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Businessman Slot 6", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Deep Darkness - Businessman Slot 7", player), lambda state: state.has("ATM Access", player))
+
+ add_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 1", player), lambda state: state.has("ATM Access", player))
+ add_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 2", player), lambda state: state.has("ATM Access", player))
+ add_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 3", player), lambda state: state.has("ATM Access", player))
+ add_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 4", player), lambda state: state.has("ATM Access", player))
+ add_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 5", player), lambda state: state.has("ATM Access", player))
+ add_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 6", player), lambda state: state.has("ATM Access", player))
+ add_rule(world.multiworld.get_location("Lost Underworld - Tenda Camp Shop Slot 7", player), lambda state: state.has("ATM Access", player))
+
+ set_rule(world.multiworld.get_location("Dalaam Restaurant - Slot 1", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Dalaam Restaurant - Slot 2", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Dalaam Restaurant - Slot 3", player), lambda state: state.has("ATM Access", player))
+ set_rule(world.multiworld.get_location("Dalaam Restaurant - Slot 4", player), lambda state: state.has("ATM Access", player))
+
\ No newline at end of file
diff --git a/worlds/earthbound/__init__.py b/worlds/earthbound/__init__.py
new file mode 100644
index 000000000000..3f2a1dc39c7f
--- /dev/null
+++ b/worlds/earthbound/__init__.py
@@ -0,0 +1,609 @@
+import os
+import typing
+import threading
+import pkgutil
+from typing import List, Set, Dict, TextIO, Tuple
+
+from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification
+from Fill import fill_restrictive
+from worlds.AutoWorld import World, WebWorld
+import itertools
+import settings
+from .Items import get_item_names_per_category, item_table
+from .Locations import get_locations
+from .Regions import init_areas, connect_area_exits
+from .Options import EBOptions, eb_option_groups
+from .setup_game import setup_gamevars, place_static_items
+from .modules.enemy_data import initialize_enemies
+from .modules.flavor_data import create_flavors
+from .game_data.local_data import item_id_table, world_version
+from .modules.hint_data import setup_hints
+from .game_data.text_data import spoiler_psi, spoiler_starts, spoiler_badges
+from .Client import EarthBoundClient
+from .Rules import set_location_rules
+from .Rom import patch_rom, EBProcPatch, valid_hashes
+from .game_data.static_location_data import location_ids, location_groups
+from .modules.equipamizer import EBArmor, EBWeapon
+from .modules.boss_shuffle import BossData, SlotInfo
+from worlds.generic.Rules import add_item_rule
+from Options import OptionError
+
+
+class EBSettings(settings.Group):
+ class RomFile(settings.SNESRomPath):
+ """File name of the EarthBound US ROM"""
+ description = "EarthBound ROM File"
+ copy_to = "EarthBound.sfc"
+ md5s = valid_hashes
+
+ rom_file: RomFile = RomFile(RomFile.copy_to)
+
+
+class EBWeb(WebWorld):
+ theme = "ocean"
+
+ setup_en = Tutorial(
+ "Multiworld Setup Guide",
+ "A guide to setting up the EarthBound randomizer"
+ "and connecting to an Archipelago server.",
+ "English",
+ "setup_en.md",
+ "setup/en",
+ ["Pink Switch"]
+ )
+
+
+ tutorials = [setup_en]
+
+ option_groups = eb_option_groups
+ # option_presets = eb_option_presets
+
+class EBItem(Item):
+ game: str = "EarthBound"
+
+
+class EarthBoundWorld(World):
+ """EarthBound is a contemporary-themed JRPG. Take four psychically-endowed children
+ across the world in search of 8 Melodies to defeat Giygas, the cosmic evil."""
+
+ game = "EarthBound"
+ option_definitions = EBOptions
+ data_version = 1
+ required_client_version = (0, 5, 0)
+
+ item_name_to_id = {item: data.code for item, data in item_table.items() if data.code}
+ location_name_to_id = location_ids
+ item_name_groups = get_item_names_per_category()
+ location_name_groups = location_groups
+
+ web = EBWeb()
+ settings: typing.ClassVar[EBSettings]
+ # topology_present = True
+
+ options_dataclass = EBOptions
+ options: EBOptions
+
+ locked_locations: List[str]
+ location_cache: List[Location]
+
+ def __init__(self, multiworld: MultiWorld, player: int):
+ self.rom_name_available_event = threading.Event()
+ super().__init__(multiworld, player)
+
+ self.locked_locations = []
+ self.location_cache = []
+ self.event_count = 8
+ self.progressive_filler_bats: int = 0
+ self.progressive_filler_pans: int = 0
+ self.progressive_filler_guns: int = 0
+ self.progressive_filler_bracelets: int = 0
+ self.progressive_filler_other: int = 0
+ self.world_version: str = world_version
+ self.armor_list = Dict[str, EBArmor]
+ self.weapon_list = Dict[str, EBWeapon]
+ self.boss_slots = Dict[str, SlotInfo]
+ self.boss_info = Dict[str, BossData]
+ self.starting_character: str | None = None
+ self.locals = []
+ self.rom_name = None
+ self.starting_area_teleport = None
+ self.common_gear = []
+ self.uncommon_gear = []
+ self.rare_gear = []
+ self.get_all_spheres = threading.Event()
+ self.boss_list: List[str] = []
+ self.starting_region = str
+ self.start_location = int
+ self.dungeon_connections: dict[str, str] = {}
+ self.has_generated_output: bool = False
+ self.hint_man_hints: list[tuple[int | str, player]] = []
+
+ self.common_items = [
+ "Cookie",
+ "Bag of Fries",
+ "Teddy Bear",
+ "Hamburger",
+ "Boiled Egg",
+ "Fresh Egg",
+ "Picnic Lunch",
+ "Croissant",
+ "Bread Roll",
+ "Can of Fruit Juice",
+ "Royal Iced Tea",
+ "Protein Drink",
+ "Bottle of Water",
+ "Cold Remedy",
+ "Vial of Serum",
+ "Ketchup Packet",
+ "Sugar Packet",
+ "Tin of Cocoa",
+ "Carton of Cream",
+ "Sprig of Parsley",
+ "Jar of Hot Sauce",
+ "Salt Packet",
+ "Wet Towel",
+ "Refreshing Herb",
+ "Ruler",
+ "Protractor",
+ "Insecticide Spray",
+ "Rust Promoter",
+ "Stag Beetle",
+ "Toothbrush",
+ "Handbag Strap",
+ "Chick",
+ "Chicken",
+ "Trout Yogurt",
+ "Banana",
+ "Calorie Stick",
+ "Gelato de Resort",
+ "Snake",
+ "Cup of Noodles",
+ "Cup of Coffee",
+ "Double Burger",
+ "Bean Croquette",
+ "Molokheiya Soup",
+ "Plain Roll",
+ "Magic Tart",
+ "PSI Caramel",
+ "Popsicle",
+ "Bottle Rocket"
+ ]
+
+ self.common_gear = [
+ "Yo-yo",
+ "Slingshot",
+ "Travel Charm",
+ "Great Charm",
+ "Ribbon",
+ "Red Ribbon"
+ ]
+
+ self.uncommon_items = [
+ "Pasta di Summers",
+ "Pizza",
+ "Chef's Special",
+ "Super Plush Bear",
+ "Jar of Delisauce",
+ "Secret Herb",
+ "Xterminator Spray",
+ "Snake Bag",
+ "Bomb",
+ "Rust Promoter DX",
+ "Pair of Dirty Socks",
+ "Mummy Wrap",
+ "Pharaoh's Curse",
+ "Sudden Guts Pill",
+ "Picture Postcard",
+ "Viper",
+ "Repel Sandwich",
+ "Lucky Sandwich",
+ "Peanut Cheese Bar",
+ "Bowl of Rice Gruel",
+ "Kabob",
+ "Plain Yogurt",
+ "Beef Jerky",
+ "Mammoth Burger",
+ "Bottle of DXwater",
+ "Magic Pudding",
+ "Big Bottle Rocket",
+ "Bazooka",
+ "Meteornium"
+
+ ]
+
+ self.uncommon_gear = [
+ "Trick Yo-yo",
+ "Bionic Slingshot",
+ "Crystal Charm",
+ "Defense Ribbon",
+ "Earth Pendant",
+ "Flame Pendant",
+ "Rain Pendant",
+ "Night Pendant"
+ ]
+
+ self.rare_items = [
+ "Large Pizza",
+ "Magic Truffle",
+ "Brain Food Lunch",
+ "Rock Candy",
+ "Kraken Soup",
+ "IQ Capsule",
+ "Guts Capsule",
+ "Speed Capsule",
+ "Vital Capsule",
+ "Luck Capsule",
+ "Horn of Life",
+ "Multi Bottle Rocket",
+ "Super Bomb",
+ "Bag of Dragonite",
+ "Meteotite",
+ "Repel Superwich",
+ "Piggy Jelly",
+ "Spicy Jerky",
+ "Luxury Jerky",
+ "Cup of Lifenoodles"
+ ]
+
+ self.rare_gear = [
+ "Combat Yo-yo",
+ "Sword of Kings",
+ "Sea Pendant",
+ "Star Pendant",
+ "Goddess Ribbon"
+ ]
+
+ self.money = [
+ "$10",
+ "$100",
+ "$1000"
+ ]
+
+ def generate_early(self) -> None: # Todo: place locked items in generate_early
+ self.starting_character = self.options.starting_character.current_key.capitalize()
+ self.locals = []
+ local_space_count = 0
+ max_counts = {
+ "Ness": 12,
+ "Paula": 11,
+ "Jeff": 9,
+ "Poo": 12
+ }
+
+ max_count = max_counts[self.starting_character]
+ for item_name, amount in itertools.chain(self.options.start_inventory.items(), self.options.start_inventory_from_pool.items()):
+ if item_name in item_id_table:
+ local_space_count += amount
+ if local_space_count > max_count and not self.options.remote_items:
+ player = self.multiworld.get_player_name(self.player)
+ raise OptionError(f"{player}: starting inventory cannot place more than {max_count} items into 'Goods' for {self.starting_character}. Attempted to place {local_space_count} Goods items.")
+
+ setup_gamevars(self)
+ create_flavors(self)
+ initialize_enemies(self)
+
+ if not self.options.character_shuffle:
+ self.options.local_items.value.update(["Paula", "Jeff", "Poo", "Flying Man"])
+ self.event_count += 6
+
+ if self.options.local_teleports:
+ self.options.local_items.value |= self.item_name_groups["PSI"]
+
+ def create_regions(self) -> None:
+ init_areas(self, get_locations(self))
+ connect_area_exits(self)
+ place_static_items(self)
+
+ def create_items(self) -> None:
+ pool = self.get_item_pool(self.get_excluded_items())
+ self.fill_item_pool(pool)
+
+ self.multiworld.itempool += pool
+
+ def set_rules(self) -> None:
+ set_location_rules(self)
+ self.multiworld.completion_condition[self.player] = lambda state: state.has('Saved Earth', self.player)
+
+ def pre_fill(self) -> None:
+ prefill_locations = []
+ prefill_items = []
+
+ if not self.options.character_shuffle:
+ main_characters = ["Ness", "Paula", "Jeff", "Poo"]
+ for character in main_characters:
+ if character != self.starting_character:
+ prefill_items.append(self.create_item(character))
+
+ prefill_items.extend([
+ self.create_item("Flying Man"),
+ self.create_item("Teddy Bear"),
+ self.create_item("Super Plush Bear")
+ ])
+
+ prefill_locations.extend([
+ self.multiworld.get_location("Happy-Happy Village - Prisoner", self.player),
+ self.multiworld.get_location("Threed - Zombie Prisoner", self.player),
+ self.multiworld.get_location("Snow Wood - Bedroom", self.player),
+ self.multiworld.get_location("Monotoli Building - Monotoli Character", self.player),
+ self.multiworld.get_location("Dalaam - Throne Character", self.player),
+ self.multiworld.get_location("Deep Darkness - Barf Character", self.player),
+ ])
+ self.random.shuffle(prefill_locations)
+ add_item_rule(self.multiworld.get_location("Happy-Happy Village - Prisoner", self.player), lambda item: item.name in self.item_name_groups["Characters"])
+ add_item_rule(self.multiworld.get_location("Threed - Zombie Prisoner", self.player), lambda item: item.name in self.item_name_groups["Characters"])
+ add_item_rule(self.multiworld.get_location("Snow Wood - Bedroom", self.player), lambda item: item.name in self.item_name_groups["Characters"])
+ add_item_rule(self.multiworld.get_location("Monotoli Building - Monotoli Character", self.player), lambda item: item.name in self.item_name_groups["Characters"])
+ add_item_rule(self.multiworld.get_location("Dalaam - Throne Character", self.player), lambda item: item.name in self.item_name_groups["Characters"])
+ add_item_rule(self.multiworld.get_location("Deep Darkness - Barf Character", self.player), lambda item: item.name in self.item_name_groups["Characters"])
+
+ fill_restrictive(self.multiworld, self.multiworld.get_all_state(False, collect_pre_fill_items=False), prefill_locations, prefill_items, True, True)
+ setup_hints(self)
+
+ def get_pre_fill_items(self) -> list[Item]:
+ characters = ["Ness", "Paula", "Jeff", "Poo"]
+ prefill_items = []
+ for character in characters:
+ if character != self.starting_character:
+ prefill_items.append(self.create_item(f"{character}"))
+ return prefill_items
+
+ @classmethod
+ def stage_generate_output(cls, multiworld: MultiWorld, output_directory: str) -> None:
+ try:
+ multiworld.earthbound_locations_by_sphere = list(multiworld.get_spheres())
+ except Exception:
+ raise
+ finally:
+ for world in multiworld.get_game_worlds("EarthBound"):
+ world.get_all_spheres.set()
+
+ def generate_output(self, output_directory: str) -> None:
+ self.has_generated_output = True # Make sure data defined in generate output doesn't get added to spoiler only mode
+ try:
+ patch = EBProcPatch(player=self.player, player_name=self.multiworld.player_name[self.player])
+ patch.write_file("earthbound_basepatch.bsdiff4", pkgutil.get_data(__name__, "src/earthbound_basepatch.bsdiff4"))
+ patch_rom(self, patch, self.player)
+
+ self.rom_name = patch.name
+
+ patch.write(os.path.join(output_directory,
+ f"{self.multiworld.get_out_file_name_base(self.player)}{patch.patch_file_ending}"))
+ except Exception:
+ raise
+ finally:
+ self.rom_name_available_event.set() # make sure threading continues and errors are collected
+
+ def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]) -> None:
+ if self.options.dungeon_shuffle:
+ dungeon_entrances = {}
+ dungeon_mapping = {}
+ for dungeon in self.dungeon_connections:
+ dungeon_entrances[self.dungeon_connections[dungeon]] = dungeon
+
+ for dungeon in dungeon_entrances:
+ for location in self.get_region(dungeon).locations:
+ if location.address:
+ dungeon_mapping[location.address] = dungeon_entrances[dungeon]
+
+ hint_data[self.player] = dungeon_mapping
+
+ def fill_slot_data(self) -> Dict[str, typing.Any]:
+ return {
+ "starting_area": self.start_location,
+ "pizza_logic": self.options.monkey_caves_mode.value,
+ "free_sancs": self.options.no_free_sanctuaries.value,
+ "shopsanity": self.options.shop_randomizer.value,
+ "hint_man_hints": self.hint_man_hints
+ }
+
+ def modify_multidata(self, multidata: dict) -> None:
+ import base64
+ # wait for self.rom_name to be available.
+ self.rom_name_available_event.wait()
+ rom_name = getattr(self, "rom_name", None)
+ if rom_name:
+ new_name = base64.b64encode(bytes(self.rom_name)).decode()
+ multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
+
+ def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
+ spoiler_handle.write(f"\nStarting Location: {spoiler_starts[self.start_location]}\n")
+ spoiler_handle.write(f"Franklin Badge Protection: {spoiler_badges[self.franklin_protection]}\n")
+ if self.options.psi_shuffle:
+ spoiler_handle.write("\nPSI Shuffle:\n")
+ spoiler_handle.write(f" Favorite Thing PSI Slot: {spoiler_psi[self.offensive_psi_slots[0]]}\n")
+ spoiler_handle.write(f" Ness Offensive PSI Middle Slot: {spoiler_psi[self.offensive_psi_slots[1]]}\n")
+ spoiler_handle.write(f" Paula Offensive PSI Top Slot: {spoiler_psi[self.offensive_psi_slots[2]]}\n")
+ spoiler_handle.write(f" Paula/Poo Offensive PSI Middle Slot: {spoiler_psi[self.offensive_psi_slots[3]]}\n")
+ spoiler_handle.write(f" Paula/Poo Offensive PSI Bottom Slot: {spoiler_psi[self.offensive_psi_slots[4]]}\n")
+ spoiler_handle.write(f" Poo Progressive PSI Slot: {spoiler_psi[self.offensive_psi_slots[5]]}\n")
+
+ spoiler_handle.write(f" Ness/Poo Shield Slot: {spoiler_psi[self.shield_slots[0]]}\n")
+ spoiler_handle.write(f" Paula Shield Slot: {spoiler_psi[self.shield_slots[1]]}\n")
+
+ spoiler_handle.write(f" Ness Assist PSI Middle Slot: {spoiler_psi[self.assist_psi_slots[0]]}\n")
+ spoiler_handle.write(f" Ness Assist PSI Bottom Slot: {spoiler_psi[self.assist_psi_slots[1]]}\n")
+ spoiler_handle.write(f" Paula Assist PSI Middle Slot: {spoiler_psi[self.assist_psi_slots[2]]}\n")
+ spoiler_handle.write(f" Paula Assist PSI Bottom Slot: {spoiler_psi[self.assist_psi_slots[3]]}\n")
+ spoiler_handle.write(f" Poo Assist PSI Slot: {spoiler_psi[self.assist_psi_slots[4]]}\n")
+ if self.options.psi_shuffle == 2:
+ spoiler_handle.write(f" Bomb/Bazooka Slot: {spoiler_psi[self.jeff_offense_items[0]]}\n")
+ spoiler_handle.write(f" Bottle Rocket Slot: {spoiler_psi[self.jeff_offense_items[1]]}\n")
+
+ spoiler_handle.write(f" Spray Can Slot: {spoiler_psi[self.jeff_assist_items[0]]}\n")
+ spoiler_handle.write(f" Multi-Level Gadget Slot 1: {spoiler_psi[self.jeff_assist_items[1]]}\n")
+ spoiler_handle.write(f" Single-Level Gadget Slot 1: {spoiler_psi[self.jeff_assist_items[2]]}\n")
+ spoiler_handle.write(f" Single-Level Gadget Slot 2: {spoiler_psi[self.jeff_assist_items[3]]}\n")
+ spoiler_handle.write(f" Multi-Level Gadget Slot 2: {spoiler_psi[self.jeff_assist_items[4]]}\n")
+
+ if self.options.boss_shuffle:
+ spoiler_handle.write("\nBoss Randomization:\n" +
+ f" Frank => {self.boss_list[0]}\n" +
+ f" Frankystein Mark II => {self.boss_list[1]}\n" +
+ f" Titanic Ant => {self.boss_list[2]}\n" +
+ f" Captain Strong => {self.boss_list[3]}\n" +
+ f" Everdred => {self.boss_list[4]}\n" +
+ f" Mr. Carpainter => {self.boss_list[5]}\n" +
+ f" Mondo Mole => {self.boss_list[6]}\n" +
+ f" Boogey Tent => {self.boss_list[7]}\n" +
+ f" Mini Barf => {self.boss_list[8]}\n" +
+ f" Master Belch => {self.boss_list[9]}\n" +
+ f" Trillionage Sprout => {self.boss_list[10]}\n" +
+ f" Guardian Digger => {self.boss_list[11]}\n" +
+ f" Dept. Store Spook => {self.boss_list[12]}\n" +
+ f" Evil Mani-Mani => {self.boss_list[13]}\n" +
+ f" Clumsy Robot => {self.boss_list[14]}\n" +
+ f" Shrooom! => {self.boss_list[15]}\n" +
+ f" Plague Rat of Doom => {self.boss_list[16]}\n" +
+ f" Thunder and Storm => {self.boss_list[17]}\n" +
+ f" Kraken => {self.boss_list[18]}\n" +
+ f" Guardian General => {self.boss_list[19]}\n" +
+ f" Master Barf => {self.boss_list[20]}\n" +
+ f" Starman Deluxe => {self.boss_list[21]}\n" +
+ f" Electro Specter => {self.boss_list[22]}\n" +
+ f" Carbon Dog => {self.boss_list[23]}\n" +
+ f" Ness's Nightmare => {self.boss_list[24]}\n" +
+ f" Heavily Armed Pokey => {self.boss_list[25]}\n" +
+ f" Starman Junior => {self.boss_list[26]}\n" +
+ f" Diamond Dog => {self.boss_list[27]}\n" +
+ f" Giygas (Phase 2) => {self.boss_list[28]}\n")
+
+ if self.options.dungeon_shuffle:
+ spoiler_handle.write("\nDungeon Entrances:\n")
+ for dungeon in self.dungeon_connections:
+ spoiler_handle.write(
+ f" {dungeon} => {self.dungeon_connections[dungeon]}\n"
+ )
+
+ if self.has_generated_output:
+ spoiler_handle.write("\nArea Levels:\n")
+ spoiler_excluded_areas = ["Ness's Mind", "Global ATM Access", "Common Condiment Shop"]
+ for area in self.area_levels:
+ if area not in spoiler_excluded_areas:
+ spoiler_handle.write(f" {area}: Level {self.area_levels[area]}\n")
+
+ def create_item(self, name: str) -> EBItem:
+ data = item_table[name]
+ return EBItem(name, data.classification, data.code, self.player)
+
+ def get_filler_item_name(self) -> str: # Todo: make this suck less
+ weights = {"rare": self.options.rare_filler_weight.value, "uncommon": self.options.uncommon_filler_weight.value, "common": self.options.common_filler_weight.value,
+ "rare_gear": int(self.options.rare_filler_weight.value * 0.5), "uncommon_gear": int(self.options.uncommon_filler_weight.value * 0.5),
+ "common_gear": int(self.options.common_filler_weight.value * 0.5), "money": self.options.money_weight.value}
+
+ filler_type = self.random.choices(list(weights), weights=list(weights.values()), k=1)[0]
+ weight_table = {
+ "common": self.common_items,
+ "common_gear": self.common_gear,
+ "uncommon": self.uncommon_items,
+ "uncommon_gear": self.uncommon_gear,
+ "rare": self.rare_items,
+ "rare_gear": self.rare_gear,
+ "money": self.money
+ }
+ return self.random.choice(weight_table[filler_type])
+
+ def get_excluded_items(self) -> Set[str]:
+ excluded_items: Set[str] = set()
+ excluded_items.add(self.starting_character)
+ starting_area_to_teleport = ["Onett Teleport", "Onett Teleport", "Twoson Teleport", "Happy-Happy Village Teleport",
+ "Threed Teleport", "Saturn Valley Teleport", "Fourside Teleport", "Winters Teleport",
+ "Summers Teleport", "Dalaam Teleport", "Scaraba Teleport", "Deep Darkness Teleport",
+ "Tenda Village Teleport", "Lost Underworld Teleport", "Magicant Teleport"]
+ self.starting_area_teleport = starting_area_to_teleport[self.start_location]
+ excluded_items.add(self.starting_area_teleport)
+ if self.options.random_start_location:
+ excluded_items.add(self.starting_teleport)
+
+ if self.options.magicant_mode not in [0, 3]:
+ excluded_items.add("Magicant Teleport")
+
+ if not self.options.character_shuffle:
+ excluded_items.add("Ness")
+ excluded_items.add("Paula")
+ excluded_items.add("Jeff")
+ excluded_items.add("Poo")
+ excluded_items.add("Flying Man")
+
+ if self.options.progressive_weapons:
+ excluded_items.add("Magicant Bat")
+ excluded_items.add("Legendary Bat")
+ excluded_items.add("Pop Gun")
+ excluded_items.add("Stun Gun")
+ excluded_items.add("Death Ray")
+ excluded_items.add("Moon Beam Gun")
+
+ if self.options.progressive_armor:
+ excluded_items.add("Platinum Band")
+ excluded_items.add("Diamond Band")
+ excluded_items.add("Pixie's Bracelet")
+ excluded_items.add("Cherub's Band")
+ excluded_items.add("Goddess Band")
+ excluded_items.add("Coin of Slumber")
+ excluded_items.add("Souvenir Coin")
+ excluded_items.add("Mr. Saturn Coin")
+
+ if not self.options.no_free_sanctuaries:
+ excluded_items.add("Tiny Key")
+ excluded_items.add("Tenda Lavapants")
+
+ return excluded_items
+
+ def set_classifications(self, name: str) -> Item:
+ data = item_table[name]
+ item = Item(name, data.classification, data.code, self.player)
+
+ if name == "Magicant Teleport" and self.options.magicant_mode == 3:
+ item.classification = ItemClassification.useful
+ return item
+
+ def fill_item_pool(self, pool: List[Item]) -> None:
+ item_to_counts = {
+ "Progressive Bat": self.progressive_filler_bats,
+ "Progressive Fry Pan": self.progressive_filler_pans,
+ "Progressive Gun": self.progressive_filler_guns,
+ "Progressive Bracelet": self.progressive_filler_bracelets,
+ "Progressive Other": self.progressive_filler_other
+ }
+
+ max_filler_counts = {
+ "Progressive Bat": 8,
+ "Progressive Fry Pan": 9,
+ "Progressive Gun": 6,
+ "Progressive Bracelet": 6,
+ "Progressive Other": 10
+ }
+
+ for _ in range(len(self.multiworld.get_unfilled_locations(self.player)) - len(pool) - self.event_count): # Change to fix event count
+ item = self.set_classifications(self.get_filler_item_name())
+ if item.name in ["Progressive Bat", "Progressive Fry Pan", "Progressive Other",
+ "Progressive Gun", "Progressive Bracelet"]:
+ item_to_counts[item.name] += 1
+
+ if item_to_counts[item.name] >= max_filler_counts[item.name]:
+ self.common_gear = [x for x in self.common_gear if x != item.name]
+ self.uncommon_gear = [x for x in self.uncommon_gear if x != item.name]
+ self.rare_gear = [x for x in self.rare_gear if x != item.name]
+ pool.append(item)
+
+ def get_item_pool(self, excluded_items: Set[str]) -> List[Item]:
+ pool: List[Item] = []
+
+ for name, data in item_table.items():
+ if name not in excluded_items:
+ for _ in range(data.amount):
+ item = self.set_classifications(name)
+ pool.append(item)
+
+ if self.options.progressive_weapons:
+ for i in range(2):
+ pool.append(self.set_classifications("Progressive Bat"))
+ for i in range(4):
+ pool.append(self.set_classifications("Progressive Gun"))
+
+ if self.options.progressive_armor:
+ for i in range(5):
+ pool.append(self.set_classifications("Progressive Bracelet"))
+ for i in range(3):
+ pool.append(self.set_classifications("Progressive Other"))
+
+ return pool
diff --git a/worlds/earthbound/archipelago.json b/worlds/earthbound/archipelago.json
new file mode 100644
index 000000000000..1cfdc01f50ed
--- /dev/null
+++ b/worlds/earthbound/archipelago.json
@@ -0,0 +1,4 @@
+{"game": "EarthBound",
+"authors": ["Pink Switch"],
+"minimum_ap_version": "0.6.3",
+"world_version": "4.3.1"}
diff --git a/worlds/earthbound/docs/en_EarthBound.md b/worlds/earthbound/docs/en_EarthBound.md
new file mode 100644
index 000000000000..4043aa6d73a6
--- /dev/null
+++ b/worlds/earthbound/docs/en_EarthBound.md
@@ -0,0 +1,124 @@
+# EarthBound
+
+## Where is the options page?
+
+The [player options page for this game](../player-options) contains all the options you need to configure and export a config file.
+
+## What does randomization do to this game?
+
+Randomization of EarthBound randomizes all items, from presents, PSI Locations, Character Locations, and gifts from NPCs.
+
+## What is the goal of EarthBound when randomized?
+By default, the goal is to reach the defined number of Sanctuaries, and then defeat Giygas at the Cave of the Past.
+If Giygas is disabled, the game will end once the player reaches the required sanctuary number.
+If Magicant is set to "required", players will need to defeat Ness's Nightmare in Magicant, unlocked upon reaching their required
+Sanctuaries, before they can win.
+Optionally, the player can set Alternate Goal conditions, on 1 or 2 sanctuaries more than required, but these will never be the expected
+goal.
+
+You can check your goals by speaking to Apple Kid in the Cave of the Present.
+
+
+## What items and locations get shuffled?
+
+Locations consist of PSI Locations, Character Locations, presents, and gifts from NPCs.
+
+PSI Locations are locations tied to psychic events in the normal game, usually corresponding to the Sanctuaries, PSI Teleport, or PSI Starstorm.
+
+PSI Locations:
+- Checking the Mani-Mani Statue in Onett
+- Speaking with Buzz Buzz
+- Drinking the Coffee at Saturn Valley
+- Defeating the Dept. Store Spook in Fourside
+- Speaking with the Monkey with Talah Rama
+- Eating the Magic Cake in Summers
+- Completing Poo's Trial of Mu in Dalaam
+- Speaking with the Star Master outside the Pyramid in Scaraba
+- Drinking the Tea at Tenda Village
+- Speaking with the Talking Rock in the Lost Underworld
+- Speaking with the Star Master in the Cave of the Present
+- Defeating Ness's Nightmare (Only if the Magicant Mode option is set to Psi location)
+
+Character Locations:
+ - The cabin just outside Happy-Happy Village
+ - The graveyard prison in Threed
+ - The second floor bedroom in the Snow Wood Boarding School
+ - Monotoli's office in Fourside
+ - The Throne Room in Dalaam
+ - Defeating Master Barf in Deep Darkness
+
+
+## Which items can be in another player's world?
+
+Items you can find for EarthBound include PSI for Poo, characters, teleport destinations, money, and just about any regular or key item.
+A few new key items have been added to replace certain plot flags.
+
+## What does another world's item look like in EarthBound?
+When collecting an item for another player, the item and player are identified as the receiver. If Shopsanity is enabled, items for other players
+in shops will display the player they are for below the shop window. When attempting to purchase an item for another player, the full name of the item and its receiver
+will be displayed to confirm. If the 'Presents Match Contents' option is enabled, presents will display the Archipelago symbol if they contain an item for another player.
+
+## What happens when I receive an item?
+When you receive an item, the sound effect of an item being received will play, and it will be put in your inventory. If your inventory is full, it will automatically be put in storage.
+!If storage is full, you will not be able to receive any more items until you clear room! If you receive PSI, the PSI learn fanfare plays instead. If you receive a character, a cutscene
+will play.
+
+## My inventory is full and there's items I can't find!
+You can press R at any time on the overworld to access a pocket storage menu that allows you to store, withdraw from storage, and toss unnessecary items within one menu.
+
+## What about Escargo Express?
+Escargo Express's item storage has been replaced with a cross-game gift delivery system. From any phone, you can call Escargo Express to receive or send Gift items to other players.
+In order for players to be eligible, they have to have gifting enabled. Your gift inbox can hold 69 gifted items at a time, and up to 10 items can be queued to be sent to other players.
+
+## Hints?
+In most major towns, you can find a helpful Hint Man who will give hints about your game... for a price.
+The location of the Hint Man will be marked on your map, viewable at all times with X.
+
+## Repel Sandwiches?
+Skip Sandwiches (And Skip Sandwich DXs) have been replaced with an item called the Repel Sandwich (and Repel Superwich).
+Repel Sandwiches will prevent normal enemies from spawning for a short time. The help text can tell you more.
+
+## Is there a run button?
+You can hold down the Y button to run.
+
+## I returned to Moonside after beating the Mani-Mani statue and now I can't leave!
+Check behind the cafe counter as you would in Fourside. A sparkle indicates the general area.
+
+## How do I get to the undeground prison in Threed?
+Use the Bad Key Machine on the door from the outside.
+
+## How do I get from Onett to Twoson?
+The captain at the Police Station might be convinced to remove the barricade...
+
+## How do I get rid of the ghosts in the road tunnels?
+The ghosts will vanish after Master Belch is defeated, irregardless of anything else.
+
+## How do I get back to Andonuts's Lab after using the Sky Runner?
+You can either go back to the underground prison or talk to Bubble Monkey, regardless of if you have helped him before.
+
+## My game automatically rolls credits after getting to Lumine Hall, but I need to get to the Lost Underworld
+Once you defeat the boss of Lumine Hall, the Lost Underworld teleport is automatically unlocked. You will need to leave and warp there, though.
+
+## How do I get into the Tenda Camp in the Lost Underworld?
+Bring them Tendakraut. They tend to notice it if you walk up to the gate with it already in your inventory.
+
+## I started as Jeff. Can I teleport?
+Jeff starts with an item called the "Warp pad", which is an inventory item that functions as Teleport. It can safely be tucked into Key Item storage.
+
+## Community Suggestions
+Community suggestions for certain things are being accepted. You can find submissions below.
+
+## Lumine Hall Text Scroll
+https://docs.google.com/forms/d/e/1FAIpQLSff7FR0mxtQcZ3d87BoZcRMggBxqGYk2fwmY_ibVnR1sXXQWw/viewform
+
+## Window Flavor Colors
+https://docs.google.com/forms/d/e/1FAIpQLSd17Bp4NYGHJqadaYprKaEXFW4EgbUYSPKxGoyufhArb1BbYw/viewform
+
+## Joke Hints
+https://docs.google.com/forms/d/e/1FAIpQLScHWK0wHPCmDgHYh8juTrntsyVqEdzYWGCScLdEs21oZCa2mQ/viewform
+
+## Equipment Randomizer
+https://docs.google.com/forms/d/e/1FAIpQLSdy-aczL4LmMDirbeD-s5N6u8ae0G2BSrhGfCxOqFhZa_x48A/viewform
+
+## Enemy Randomizer
+https://docs.google.com/forms/d/e/1FAIpQLSdF0aFoIwCO7t7OpaObTE_b6dAgLpXC2_8fS-SjdgJDjAQJ6A/viewform
\ No newline at end of file
diff --git a/worlds/earthbound/docs/setup_en.md b/worlds/earthbound/docs/setup_en.md
new file mode 100644
index 000000000000..3a2f0b5375c4
--- /dev/null
+++ b/worlds/earthbound/docs/setup_en.md
@@ -0,0 +1,128 @@
+# EarthBound Archipelago Randomizer Setup Guide
+
+## Required Software
+
+- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases).
+
+
+- Hardware or software capable of loading and playing SNES ROM files
+ - An emulator capable of connecting to SNI such as:
+ - snes9x-rr from: [snes9x rr](https://github.com/gocha/snes9x-rr/releases),
+ - BizHawk from: [TASVideos](https://tasvideos.org/BizHawk)
+ - snes9x-nwa from: [snes9x nwa](https://github.com/Skarsnik/snes9x-emunwa/releases)
+ - RetroArch 1.10.3 or newer from: RetroArch Website
+ - You can additionally use the FX Pack Pro, however some issues with it have been reported and it is not officially recommended.
+
+- Your legally obtained EarthBound English ROM file, probably named `EarthBound (USA).sfc`
+
+## Optional Software
+- EarthBound Archipelago Poptracker Pack
+ - PopTracker from: [PopTracker Releases Page](https://github.com/black-sliver/PopTracker/releases/)
+ - EarthBound Archipelago PopTracker pack from: [EarthBound AP Tracker Releases Page](https://github.com/PinkSwitch/earthbound_poptracker/releases/tag/ebpoptracker1.0)
+ - The EarthBound Sprite Injector Tool (For custom sprite usage): [EarthBound Sprite Injector](https://github.com/PinkSwitch/eb-sprite-injector/releases/)
+
+## Installation Procedures
+
+### Windows Setup
+
+1. Download and install Archipelago from the link above, making sure to install the most recent version.
+2. During generation/patching, you will be asked to locate your base ROM file. This is your EarthBound ROM file.
+3. If you are using an emulator, you should assign your Lua capable emulator as your default program for launching ROM
+ files.
+ 1. Extract your emulator's folder to your Desktop, or somewhere you will remember.
+ 2. Right-click on a ROM file and select **Open with...**
+ 3. Check the box next to **Always use this app to open .sfc files**
+ 4. Scroll to the bottom of the list and click the grey text **Look for another App on this PC**
+ 5. Browse for your emulator's `.exe` file and click **Open**. This file should be located inside the folder you
+ extracted in step one.
+
+## Create a Config (.yaml) File
+
+### What is a config file and why do I need one?
+
+See the guide on setting up a basic YAML at the Archipelago setup
+guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
+
+### Where do I get a config file?
+
+The Player Options page on the website allows you to configure your personal options and export a config file from
+them.
+
+### Verifying your config file
+
+If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML
+validator page: [YAML Validation page](/mysterycheck)
+
+## Joining a MultiWorld Game
+
+### Obtain your patch file and create your ROM
+
+When you join a multiworld game, you will be asked to provide your config file to whomever is hosting. Once that is done,
+the host will provide you with either a link to download your patch file, or with a zip file containing everyone's patch
+files. Your patch file should have a `.apeb` extension.
+
+Put your patch file on your desktop or somewhere convenient, and double click it. This should automatically launch the
+client, and will also create your ROM in the same place as your patch file.
+
+### Connect to the client
+
+#### With an emulator
+
+When the client launched automatically, SNI should have also automatically launched in the background. If this is its
+first time launching, you may be prompted to allow it to communicate through the Windows Firewall.
+
+##### snes9x-rr
+
+1. Load your ROM file if it hasn't already been loaded.
+2. Click on the File menu and hover on **Lua Scripting**
+3. Click on **New Lua Script Window...**
+4. In the new window, click **Browse...**
+5. Select the connector lua file included with your client
+ - Look in the Archipelago folder for `/SNI/lua/Connector.lua`.
+6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of
+the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install.
+
+##### BizHawk
+
+1. Ensure you have the BSNES core loaded. This is done with the main menubar, under:
+ - (≤ 2.8) `Config` 〉 `Cores` 〉 `SNES` 〉 `BSNES`
+ - (≥ 2.9) `Config` 〉 `Preferred Cores` 〉 `SNES` 〉 `BSNESv115+`
+2. Load your ROM file if it hasn't already been loaded.
+ If you changed your core preference after loading the ROM, don't forget to reload it (default hotkey: Ctrl+R).
+3. Drag+drop the `Connector.lua` file included with your client onto the main EmuHawk window.
+ - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the
+ emulator is 64-bit or 32-bit. Please note the most recent versions of BizHawk are 64-bit only.
+ - You could instead open the Lua Console manually, click `Script` 〉 `Open Script`, and navigate to `Connector.lua`
+ with the file picker.
+
+
+
+### Connect to the Archipelago Server
+
+The patch file which launched your client should have automatically connected you to the AP Server. There are a few
+reasons this may not happen however, including if the game is hosted on the website but was generated elsewhere. If the
+client window shows "Server Status: Not Connected", simply ask the host for the address of the server, and copy/paste it
+into the "Server" input field then press enter.
+
+The client will attempt to reconnect to the new server address, and should momentarily show "Server Status: Connected".
+
+### Play the game
+
+When the client shows both SNES Device and Server as connected, you're ready to begin playing. Congratulations on
+successfully joining a multiworld game!
+
+## Hosting a MultiWorld game
+
+The recommended way to host a game is to use our hosting service. The process is relatively simple:
+
+1. Collect config files from your players.
+2. Create a zip file containing your players' config files.
+3. Upload that zip file to the Generate page above.
+ - Generate page: [WebHost Seed Generation Page](/generate)
+4. Wait a moment while the seed is generated.
+5. When the seed is generated, you will be redirected to a "Seed Info" page.
+6. Click "Create New Room". This will take you to the server page. Provide the link to this page to your players, so
+ they may download their patch files from there.
+7. Note that a link to a MultiWorld Tracker is at the top of the room page. The tracker shows the progress of all
+ players in the game. Any observers may also be given the link to this page.
+8. Once all players have joined, you may begin playing.
diff --git a/worlds/earthbound/game_data/__init__.py b/worlds/earthbound/game_data/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/earthbound/game_data/battle_bg_data.py b/worlds/earthbound/game_data/battle_bg_data.py
new file mode 100644
index 000000000000..faafea4c0692
--- /dev/null
+++ b/worlds/earthbound/game_data/battle_bg_data.py
@@ -0,0 +1,329 @@
+battle_bg_bpp = {
+ 0x00: 2,
+ 0x01: 4,
+ 0x02: 4,
+ 0x03: 4,
+ 0x04: 4,
+ 0x05: 4,
+ 0x06: 4,
+ 0x07: 4,
+ 0x08: 4,
+ 0x09: 4,
+ 0x0a: 4,
+ 0x0b: 4,
+ 0x0c: 4,
+ 0x0d: 4,
+ 0x0e: 4,
+ 0x0f: 4,
+ 0x10: 4,
+ 0x11: 4,
+ 0x12: 4,
+ 0x13: 4,
+ 0x14: 4,
+ 0x15: 4,
+ 0x16: 4,
+ 0x17: 4,
+ 0x18: 4,
+ 0x19: 4,
+ 0x1a: 4,
+ 0x1b: 4,
+ 0x1c: 4,
+ 0x1d: 4,
+ 0x1e: 4,
+ 0x1f: 4,
+ 0x20: 4,
+ 0x21: 4,
+ 0x22: 4,
+ 0x23: 4,
+ 0x24: 4,
+ 0x25: 4,
+ 0x26: 4,
+ 0x27: 4,
+ 0x28: 4,
+ 0x29: 4,
+ 0x2a: 4,
+ 0x2b: 4,
+ 0x2c: 4,
+ 0x2d: 2,
+ 0x2e: 4,
+ 0x2f: 4,
+ 0x30: 4,
+ 0x31: 4,
+ 0x32: 4,
+ 0x33: 4,
+ 0x34: 4,
+ 0x35: 4,
+ 0x36: 4,
+ 0x37: 4,
+ 0x38: 4,
+ 0x39: 4,
+ 0x3a: 4,
+ 0x3b: 4,
+ 0x3c: 4,
+ 0x3d: 4,
+ 0x3e: 4,
+ 0x3f: 4,
+ 0x40: 4,
+ 0x41: 4,
+ 0x42: 4,
+ 0x43: 4,
+ 0x44: 4,
+ 0x45: 4,
+ 0x46: 4,
+ 0x47: 4,
+ 0x48: 4,
+ 0x49: 4,
+ 0x4a: 4,
+ 0x4b: 4,
+ 0x4c: 4,
+ 0x4d: 4,
+ 0x4e: 4,
+ 0x4f: 4,
+ 0x50: 4,
+ 0x51: 4,
+ 0x52: 4,
+ 0x53: 4,
+ 0x54: 4,
+ 0x55: 4,
+ 0x56: 4,
+ 0x57: 4,
+ 0x58: 4,
+ 0x59: 4,
+ 0x5a: 4,
+ 0x5b: 4,
+ 0x5c: 4,
+ 0x5d: 4,
+ 0x5e: 4,
+ 0x5f: 4,
+ 0x60: 4,
+ 0x61: 4,
+ 0x62: 4,
+ 0x63: 4,
+ 0x64: 4,
+ 0x65: 4,
+ 0x66: 4,
+ 0x67: 4,
+ 0x68: 4,
+ 0x69: 4,
+ 0x6a: 4,
+ 0x6b: 4,
+ 0x6c: 4,
+ 0x6d: 4,
+ 0x6e: 4,
+ 0x6f: 4,
+ 0x70: 4,
+ 0x71: 4,
+ 0x72: 4,
+ 0x73: 4,
+ 0x74: 4,
+ 0x75: 4,
+ 0x76: 4,
+ 0x77: 4,
+ 0x78: 4,
+ 0x79: 4,
+ 0x7a: 4,
+ 0x7b: 4,
+ 0x7c: 4,
+ 0x7d: 4,
+ 0x7e: 4,
+ 0x7f: 4,
+ 0x80: 4,
+ 0x81: 4,
+ 0x82: 4,
+ 0x83: 4,
+ 0x84: 4,
+ 0x85: 4,
+ 0x86: 4,
+ 0x87: 4,
+ 0x88: 4,
+ 0x89: 4,
+ 0x8a: 4,
+ 0x8b: 4,
+ 0x8c: 4,
+ 0x8d: 4,
+ 0x8e: 4,
+ 0x8f: 4,
+ 0x90: 4,
+ 0x91: 4,
+ 0x92: 4,
+ 0x93: 4,
+ 0x94: 4,
+ 0x95: 4,
+ 0x96: 4,
+ 0x97: 4,
+ 0x98: 4,
+ 0x99: 4,
+ 0x9a: 4,
+ 0x9b: 4,
+ 0x9c: 4,
+ 0x9d: 4,
+ 0x9e: 4,
+ 0x9f: 4,
+ 0xa0: 4,
+ 0xa1: 4,
+ 0xa2: 4,
+ 0xa3: 2,
+ 0xa4: 2,
+ 0xa5: 2,
+ 0xa6: 2,
+ 0xa7: 2,
+ 0xa8: 2,
+ 0xa9: 2,
+ 0xaa: 2,
+ 0xab: 2,
+ 0xac: 2,
+ 0xad: 2,
+ 0xae: 2,
+ 0xaf: 2,
+ 0xb0: 2,
+ 0xb1: 2,
+ 0xb2: 2,
+ 0xb3: 2,
+ 0xb4: 2,
+ 0xb5: 2,
+ 0xb6: 2,
+ 0xb7: 2,
+ 0xb8: 2,
+ 0xb9: 2,
+ 0xba: 2,
+ 0xbb: 2,
+ 0xbc: 2,
+ 0xbd: 4,
+ 0xbe: 2,
+ 0xbf: 2,
+ 0xc0: 2,
+ 0xc1: 2,
+ 0xc2: 2,
+ 0xc3: 4,
+ 0xc4: 4,
+ 0xc5: 4,
+ 0xc6: 4,
+ 0xc7: 4,
+ 0xc8: 2,
+ 0xc9: 2,
+ 0xca: 2,
+ 0xcb: 2,
+ 0xcc: 2,
+ 0xcd: 2,
+ 0xce: 2,
+ 0xcf: 2,
+ 0xd0: 4,
+ 0xd1: 4,
+ 0xd2: 2,
+ 0xd3: 2,
+ 0xd4: 2,
+ 0xd5: 2,
+ 0xd6: 2,
+ 0xd7: 2,
+ 0xd8: 2,
+ 0xd9: 2,
+ 0xda: 2,
+ 0xdb: 2,
+ 0xdc: 4,
+ 0xdd: 2,
+ 0xde: 2,
+ 0xdf: 2,
+ 0xe0: 2,
+ 0xe1: 4,
+ 0xe2: 4,
+ 0xe3: 4,
+ 0xe4: 4,
+ 0xe5: 4,
+ 0xe6: 4,
+ 0xe7: 4,
+ 0xe8: 4,
+ 0xe9: 4,
+ 0xea: 4,
+ 0xeb: 4,
+ 0xec: 4,
+ 0xed: 4,
+ 0xee: 4,
+ 0xef: 4,
+ 0xf0: 4,
+ 0xf1: 4,
+ 0xf2: 4,
+ 0xf3: 4,
+ 0xf4: 4,
+ 0xf5: 4,
+ 0xf6: 4,
+ 0xf7: 4,
+ 0xf8: 2,
+ 0xf9: 4,
+ 0xfa: 4,
+ 0xfb: 4,
+ 0xfc: 2,
+ 0xfd: 4,
+ 0xfe: 4,
+ 0xff: 4,
+ 0x100: 4,
+ 0x101: 4,
+ 0x102: 2,
+ 0x103: 4,
+ 0x104: 4,
+ 0x105: 4,
+ 0x106: 4,
+ 0x107: 2,
+ 0x108: 2,
+ 0x109: 4,
+ 0x10a: 2,
+ 0x10b: 2,
+ 0x10c: 4,
+ 0x10d: 2,
+ 0x10e: 2,
+ 0x10f: 4,
+ 0x110: 2,
+ 0x111: 2,
+ 0x112: 2,
+ 0x113: 2,
+ 0x114: 2,
+ 0x115: 2,
+ 0x116: 4,
+ 0x117: 4,
+ 0x118: 4,
+ 0x119: 2,
+ 0x11a: 2,
+ 0x11b: 4,
+ 0x11c: 4,
+ 0x11d: 4,
+ 0x11e: 4,
+ 0x11f: 4,
+ 0x120: 4,
+ 0x121: 4,
+ 0x122: 4,
+ 0x123: 4,
+ 0x124: 2,
+ 0x125: 2,
+ 0x126: 4,
+ 0x127: 4,
+ 0x128: 4,
+ 0x129: 4,
+ 0x12a: 4,
+ 0x12b: 4,
+ 0x12c: 4,
+ 0x12d: 4,
+ 0x12e: 4,
+ 0x12f: 4,
+ 0x130: 4,
+ 0x131: 4,
+ 0x132: 2,
+ 0x133: 2,
+ 0x134: 4,
+ 0x135: 4,
+ 0x136: 4,
+ 0x137: 4,
+ 0x138: 4,
+ 0x139: 4,
+ 0x13a: 4,
+ 0x13b: 4,
+ 0x13c: 4,
+ 0x13d: 4,
+ 0x13e: 4,
+ 0x13f: 4,
+ 0x140: 4,
+ 0x141: 4,
+ 0x142: 4,
+ 0x143: 4,
+ 0x144: 4,
+ 0x145: 4,
+ 0x146: 2
+}
diff --git a/worlds/earthbound/game_data/local_data.py b/worlds/earthbound/game_data/local_data.py
new file mode 100644
index 000000000000..c5a85b27332f
--- /dev/null
+++ b/worlds/earthbound/game_data/local_data.py
@@ -0,0 +1,1406 @@
+world_version = "4.3.1"
+
+check_table = {
+ 0xEB0000: [0x11, 3],
+ 0xEB0001: [0x6C, 4],
+ 0xEB0002: [0x6B, 7],
+ 0xEB0003: [0x11, 6],
+ 0xEB0004: [0x84, 3],
+ 0xEB0005: [0x82, 1],
+ 0xEB0006: [0x47, 6],
+ 0xEB0007: [0x13, 0],
+ 0xEB0008: [0x6C, 0],
+ 0xEB0009: [0x11, 4],
+ 0xEB000A: [0x6C, 1],
+ 0xEB000B: [0x6C, 2],
+ 0xEB000C: [0x6C, 3],
+ 0xEB000D: [0x11, 7],
+ 0xEB000E: [0x11, 5],
+ 0xEB000F: [0x6C, 6],
+ 0xEB0010: [0x6C, 7],
+ 0xEB0011: [0x6C, 5],
+ 0xEB0012: [0x12, 0],
+ 0xEB0013: [0x5B, 5],
+ 0xEB0014: [0x6D, 1],
+ 0xEB0015: [0x6D, 2],
+ 0xEB0016: [0x6D, 0],
+ 0xEB0017: [0x4C, 5],
+ 0xEB0018: [0x12, 2],
+ 0xEB0019: [0x12, 1],
+ 0xEB001A: [0x12, 3],
+ 0xEB001B: [0x12, 4],
+ 0xEB001C: [0x85, 2],
+ 0xEB001D: [0x6E, 0],
+ 0xEB001E: [0x6D, 5],
+ 0xEB001F: [0x6D, 7],
+ 0xEB0020: [0x6E, 1],
+ 0xEB0021: [0x6E, 2],
+ 0xEB0022: [0x6D, 6],
+ 0xEB0023: [0x83, 7],
+ 0xEB0024: [0x6D, 4],
+ 0xEB0025: [0x6D, 3],
+ 0xEB0026: [0x12, 5],
+ 0xEB0027: [0x85, 4],
+ 0xEB0028: [0x17, 1],
+ 0xEB0029: [0x6E, 4],
+ 0xEB002A: [0x6E, 5],
+ 0xEB002B: [0x6E, 3],
+ 0xEB002C: [0x12, 6],
+ 0xEB002D: [0x6E, 6],
+ 0xEB002E: [0x6F, 0],
+ 0xEB002F: [0x6F, 1],
+ 0xEB0030: [0x6E, 7],
+ 0xEB0031: [0x66, 2],
+ 0xEB0032: [0x0D, 6],
+ 0xEB0033: [0x6F, 2],
+ 0xEB0034: [0x6F, 3],
+ 0xEB0035: [0x73, 4],
+ 0xEB0036: [0x73, 3],
+ 0xEB0037: [0x73, 5],
+ 0xEB0038: [0x73, 6],
+ 0xEB0039: [0x73, 7],
+ 0xEB003A: [0x74, 0],
+ 0xEB003B: [0x74, 1],
+ 0xEB003C: [0x84, 5],
+ 0xEB003D: [0x1D, 2],
+ 0xEB003E: [0x1D, 3],
+ 0xEB003F: [0x1D, 4],
+ 0xEB0040: [0x73, 2],
+ 0xEB0041: [0x72, 5],
+ 0xEB0042: [0x73, 1],
+ 0xEB0043: [0x72, 4],
+ 0xEB0044: [0x73, 0],
+ 0xEB0045: [0x72, 7],
+ 0xEB0046: [0x72, 6],
+ 0xEB0047: [0x76, 2],
+ 0xEB0048: [0x76, 7],
+ 0xEB0049: [0x76, 1],
+ 0xEB004A: [0x76, 4],
+ 0xEB004B: [0x76, 5],
+ 0xEB004C: [0x76, 6],
+ 0xEB004D: [0x76, 3],
+ 0xEB004E: [0x38, 0],
+ 0xEB004F: [0x77, 0],
+ 0xEB0050: [0x13, 1],
+ 0xEB0051: [0x70, 0],
+ 0xEB0052: [0x6F, 5],
+ 0xEB0053: [0x6F, 7],
+ 0xEB0054: [0x70, 2],
+ 0xEB0055: [0x70, 1],
+ 0xEB0056: [0x6F, 6],
+ 0xEB0057: [0x6F, 4],
+ 0xEB0058: [0x85, 1],
+ 0xEB0059: [0x12, 7],
+ 0xEB005A: [0x86, 3],
+ 0xEB005B: [0x0A, 6],
+ 0xEB005C: [0x31, 5],
+ 0xEB005D: [0x53, 4],
+ 0xEB005E: [0x17, 7],
+ 0xEB005F: [0x30, 2],
+ 0xEB0060: [0x71, 0],
+ 0xEB0061: [0x70, 6],
+ 0xEB0062: [0x70, 4],
+ 0xEB0063: [0x70, 7],
+ 0xEB0064: [0x70, 5],
+ 0xEB0065: [0x71, 3],
+ 0xEB0066: [0x71, 1],
+ 0xEB0067: [0x71, 2],
+ 0xEB0068: [0x70, 3],
+ 0xEB0069: [0x55, 1],
+ 0xEB006A: [0x72, 3],
+ 0xEB006B: [0x71, 5],
+ 0xEB006C: [0x72, 1],
+ 0xEB006D: [0x71, 6],
+ 0xEB006E: [0x71, 7],
+ 0xEB006F: [0x71, 4],
+ 0xEB0070: [0x72, 0],
+ 0xEB0071: [0x72, 2],
+ 0xEB0072: [0x58, 3],
+ 0xEB0073: [0x83, 4],
+ 0xEB0074: [0x78, 5],
+ 0xEB0075: [0x78, 3],
+ 0xEB0076: [0x77, 3],
+ 0xEB0077: [0x78, 6],
+ 0xEB0078: [0x78, 2],
+ 0xEB0079: [0x77, 5],
+ 0xEB007A: [0x77, 2],
+ 0xEB007B: [0x77, 7],
+ 0xEB007C: [0x77, 4],
+ 0xEB007D: [0x77, 1],
+ 0xEB007E: [0x78, 1],
+ 0xEB007F: [0x78, 0],
+ 0xEB0080: [0x78, 4],
+ 0xEB0081: [0x77, 6],
+ 0xEB0082: [0x74, 2],
+ 0xEB0083: [0x74, 3],
+ 0xEB0084: [0x74, 4],
+ 0xEB0085: [0x74, 5],
+ 0xEB0086: [0x41, 7],
+ 0xEB0087: [0x74, 6],
+ 0xEB0088: [0x74, 7],
+ 0xEB0089: [0x4D, 1],
+ 0xEB008A: [0x54, 4],
+ 0xEB00F1: [0x75, 1],
+ 0xEB00F2: [0x75, 0],
+ 0xEB00F3: [0x75, 2],
+ 0xEB00F4: [0x75, 4],
+ 0xEB00F5: [0x75, 3],
+ 0xEB00F6: [0x75, 5],
+ 0xEB00F7: [0x75, 6],
+ 0xEB00F8: [0x76, 0],
+ 0xEB00F9: [0x75, 7],
+ 0xEB008B: [0x19, 3],
+ 0xEB008C: [0x83, 6],
+ 0xEB008D: [0x63, 7],
+ 0xEB008E: [0x79, 2],
+ 0xEB008F: [0x79, 3],
+ 0xEB0090: [0x79, 6],
+ 0xEB0091: [0x79, 5],
+ 0xEB0092: [0x79, 4],
+ 0xEB0093: [0x19, 2],
+ 0xEB0094: [0x7A, 2],
+ 0xEB0095: [0x7A, 3],
+ 0xEB0096: [0x7A, 1],
+ 0xEB0097: [0x79, 7],
+ 0xEB0098: [0x7A, 0],
+ 0xEB0099: [0x78, 7],
+ 0xEB009A: [0x79, 0],
+ 0xEB009B: [0x79, 1],
+ 0xEB009C: [0x19, 4],
+ 0xEB009D: [0x64, 4],
+ 0xEB009E: [0x5A, 7],
+ 0xEB009F: [0x32, 6],
+ 0xEB00A0: [0x0F, 4],
+ 0xEB00A1: [0x7C, 6],
+ 0xEB00A2: [0x7C, 7],
+ 0xEB00A3: [0x7D, 0],
+ 0xEB00A4: [0x2A, 0],
+ 0xEB00A5: [0x7D, 2],
+ 0xEB00A6: [0x7D, 4],
+ 0xEB00A7: [0x7D, 3],
+ 0xEB00A8: [0x7D, 1],
+ 0xEB00A9: [0x57, 4],
+ 0xEB00AA: [0x09, 7],
+ 0xEB00AB: [0x7D, 6],
+ 0xEB00AC: [0x7D, 5],
+ 0xEB00AD: [0x7D, 7],
+ 0xEB00AE: [0x7A, 6],
+ 0xEB00AF: [0x7A, 4],
+ 0xEB00B0: [0x7A, 5],
+ 0xEB00B1: [0x7B, 0],
+ 0xEB00B2: [0x7B, 1],
+ 0xEB00B3: [0x33, 3],
+ 0xEB00B4: [0x7A, 7],
+ 0xEB00B5: [0x83, 0],
+ 0xEB00B6: [0x31, 7],
+ 0xEB00B7: [0x7B, 6],
+ 0xEB00B8: [0x7B, 2],
+ 0xEB00B9: [0x7C, 5],
+ 0xEB00BA: [0x7C, 0],
+ 0xEB00BB: [0x7C, 1],
+ 0xEB00BC: [0x7C, 2],
+ 0xEB00BD: [0x7B, 4],
+ 0xEB00BE: [0x7B, 7],
+ 0xEB00BF: [0x7B, 5],
+ 0xEB00C0: [0x7C, 4],
+ 0xEB00C1: [0x7C, 3],
+ 0xEB00C2: [0x7B, 3],
+ 0xEB00C3: [0x59, 1],
+ 0xEB00C4: [0x7E, 4],
+ 0xEB00C5: [0x7E, 3],
+ 0xEB00C6: [0x7E, 2],
+ 0xEB00C7: [0x7E, 5],
+ 0xEB00C8: [0x7E, 0],
+ 0xEB00C9: [0x7E, 1],
+ 0xEB00CA: [0x3E, 7],
+ 0xEB00CB: [0x3F, 1],
+ 0xEB00CC: [0x3F, 0],
+ 0xEB00CD: [0x3F, 2],
+ 0xEB00CE: [0x3E, 6],
+ 0xEB00CF: [0x84, 7],
+ 0xEB00D0: [0x7F, 7],
+ 0xEB00D1: [0x83, 1],
+ 0xEB00D2: [0x82, 6],
+ 0xEB00D3: [0x13, 3],
+ 0xEB00D4: [0x7F, 6],
+ 0xEB00D5: [0x7F, 2],
+ 0xEB00D6: [0x7F, 0],
+ 0xEB00D7: [0x7F, 5],
+ 0xEB00D8: [0x7E, 7],
+ 0xEB00D9: [0x7F, 3],
+ 0xEB00DA: [0x7E, 6],
+ 0xEB00DB: [0x7F, 1],
+ 0xEB00DC: [0x7F, 4],
+ 0xEB00DD: [0x3C, 6],
+ 0xEB00DE: [0x80, 1],
+ 0xEB00DF: [0x80, 2],
+ 0xEB00E0: [0x80, 3],
+ 0xEB00E1: [0x80, 4],
+ 0xEB00E2: [0x80, 0],
+ 0xEB00E3: [0x81, 1],
+ 0xEB00E4: [0x80, 7],
+ 0xEB00E5: [0x81, 0],
+ 0xEB00E6: [0x80, 6],
+ 0xEB00E7: [0x80, 5],
+ 0xEB00E8: [0x13, 4],
+ 0xEB00E9: [0x81, 2],
+ 0xEB00EA: [0x81, 4],
+ 0xEB00EB: [0x81, 5],
+ 0xEB00EC: [0x81, 6],
+ 0xEB00ED: [0x86, 1],
+ 0xEB00EE: [0x84, 4],
+ 0xEB00EF: [0x86, 4],
+ 0xEB00F0: [0x81, 7],
+ 0xEB00FA: [0x81, 3],
+ # Shop checks
+ 0xeb1000: [0x00, 0],
+ 0xeb1001: [0x00, 1],
+ 0xeb1002: [0x00, 2],
+ 0xeb1003: [0x00, 3],
+ 0xeb1004: [0x00, 4],
+ 0xeb1007: [0x01, 0],
+ 0xeb100e: [0x02, 0],
+ 0xeb1015: [0x03, 0],
+ 0xeb1016: [0x03, 1],
+ 0xeb1017: [0x03, 2],
+ 0xeb1018: [0x03, 3],
+ 0xeb101c: [0x04, 0],
+ 0xeb101d: [0x04, 1],
+ 0xeb101e: [0x04, 2],
+ 0xeb101f: [0x04, 3],
+ 0xeb1023: [0x05, 0],
+ 0xeb1024: [0x05, 1],
+ 0xeb1025: [0x05, 2],
+ 0xeb1026: [0x05, 3],
+ 0xeb102a: [0x06, 0],
+ 0xeb102b: [0x06, 1],
+ 0xeb102c: [0x06, 2],
+ 0xeb102d: [0x06, 3],
+ 0xeb1031: [0x07, 0],
+ 0xeb1032: [0x07, 1],
+ 0xeb1033: [0x07, 2],
+ 0xeb1034: [0x07, 3],
+ 0xeb1035: [0x07, 4],
+ 0xeb1036: [0x07, 5],
+ 0xeb1038: [0x08, 0],
+ 0xeb1039: [0x08, 1],
+ 0xeb103f: [0x09, 0],
+ 0xeb1046: [0x0a, 0],
+ 0xeb1047: [0x0a, 1],
+ 0xeb1048: [0x0a, 2],
+ 0xeb1049: [0x0a, 3],
+ 0xeb104a: [0x0a, 4],
+ 0xeb104b: [0x0a, 5],
+ 0xeb105b: [0x0d, 0],
+ 0xeb105c: [0x0d, 1],
+ 0xeb105d: [0x0d, 2],
+ 0xeb105e: [0x0d, 3],
+ 0xeb105f: [0x0d, 4],
+ 0xeb1060: [0x0d, 5],
+ 0xeb1062: [0x0e, 0],
+ 0xeb1069: [0x0f, 0],
+ 0xeb106a: [0x0f, 1],
+ 0xeb106b: [0x0f, 2],
+ 0xeb106c: [0x0f, 3],
+ 0xeb106d: [0x0f, 4],
+ 0xeb1070: [0x10, 0],
+ 0xeb1071: [0x10, 1],
+ 0xeb1072: [0x10, 2],
+ 0xeb1073: [0x10, 3],
+ 0xeb1074: [0x10, 4],
+ 0xeb1077: [0x11, 0],
+ 0xeb1078: [0x11, 1],
+ 0xeb1079: [0x11, 2],
+ 0xeb107a: [0x11, 3],
+ 0xeb107b: [0x11, 4],
+ 0xeb107e: [0x12, 0],
+ 0xeb107f: [0x12, 1],
+ 0xeb1080: [0x12, 2],
+ 0xeb1081: [0x12, 3],
+ 0xeb1085: [0x13, 0],
+ 0xeb1086: [0x13, 1],
+ 0xeb1087: [0x13, 2],
+ 0xeb1088: [0x13, 3],
+ 0xeb1089: [0x13, 4],
+ 0xeb108a: [0x13, 5],
+ 0xeb108b: [0x13, 6],
+ 0xeb108c: [0x14, 0],
+ 0xeb1093: [0x15, 0],
+ 0xeb1094: [0x15, 1],
+ 0xeb1095: [0x15, 2],
+ 0xeb1096: [0x15, 3],
+ 0xeb1097: [0x15, 4],
+ 0xeb1098: [0x15, 5],
+ 0xeb1099: [0x15, 6],
+ 0xeb109a: [0x16, 0],
+ 0xeb109b: [0x16, 1],
+ 0xeb109c: [0x16, 2],
+ 0xeb109d: [0x16, 3],
+ 0xeb109e: [0x16, 4],
+ 0xeb10a1: [0x17, 0],
+ 0xeb10a2: [0x17, 1],
+ 0xeb10a3: [0x17, 2],
+ 0xeb10a4: [0x17, 3],
+ 0xeb10a5: [0x17, 4],
+ 0xeb10a8: [0x18, 0],
+ 0xeb10a9: [0x18, 1],
+ 0xeb10aa: [0x18, 2],
+ 0xeb10ab: [0x18, 3],
+ 0xeb10af: [0x19, 0],
+ 0xeb10b0: [0x19, 1],
+ 0xeb10b1: [0x19, 2],
+ 0xeb10b2: [0x19, 3],
+ 0xeb10b3: [0x19, 4],
+ 0xeb10b4: [0x19, 5],
+ 0xeb10b6: [0x1a, 0],
+ 0xeb10b7: [0x1a, 1],
+ 0xeb10b8: [0x1a, 2],
+ 0xeb10b9: [0x1a, 3],
+ 0xeb10ba: [0x1a, 4],
+ 0xeb10bb: [0x1a, 5],
+ 0xeb10bc: [0x1a, 6],
+ 0xeb10bd: [0x1b, 0],
+ 0xeb10be: [0x1b, 1],
+ 0xeb10bf: [0x1b, 2],
+ 0xeb10c0: [0x1b, 3],
+ 0xeb10c4: [0x1c, 0],
+ 0xeb10c5: [0x1c, 1],
+ 0xeb10c6: [0x1c, 2],
+ 0xeb10c7: [0x1c, 3],
+ 0xeb10c8: [0x1c, 4],
+ 0xeb10cb: [0x1d, 0],
+ 0xeb10cc: [0x1d, 1],
+ 0xeb10cd: [0x1d, 2],
+ 0xeb10ce: [0x1d, 3],
+ 0xeb10cf: [0x1d, 4],
+ 0xeb10d0: [0x1d, 5],
+ 0xeb10d1: [0x1d, 6],
+ 0xeb10d2: [0x1e, 0],
+ 0xeb10d3: [0x1e, 1],
+ 0xeb10d4: [0x1e, 2],
+ 0xeb10d5: [0x1e, 3],
+ 0xeb10d6: [0x1e, 4],
+ 0xeb10d7: [0x1e, 5],
+ 0xeb10d9: [0x1f, 0],
+ 0xeb10da: [0x1f, 1],
+ 0xeb10db: [0x1f, 2],
+ 0xeb10dc: [0x1f, 3],
+ 0xeb10e0: [0x20, 0],
+ 0xeb10e1: [0x20, 1],
+ 0xeb10e2: [0x20, 2],
+ 0xeb10e3: [0x20, 3],
+ 0xeb10e4: [0x20, 4],
+ 0xeb10e7: [0x21, 0],
+ 0xeb10e8: [0x21, 1],
+ 0xeb10e9: [0x21, 2],
+ 0xeb10ea: [0x21, 3],
+ 0xeb10eb: [0x21, 4],
+ 0xeb10ee: [0x22, 0],
+ 0xeb10ef: [0x22, 1],
+ 0xeb10f0: [0x22, 2],
+ 0xeb10f1: [0x22, 3],
+ 0xeb10f5: [0x23, 0],
+ 0xeb10f6: [0x23, 1],
+ 0xeb1103: [0x25, 0],
+ 0xeb1104: [0x25, 1],
+ 0xeb1105: [0x25, 2],
+ 0xeb1106: [0x25, 3],
+ 0xeb1107: [0x25, 4],
+ 0xeb1108: [0x25, 5],
+ 0xeb1109: [0x25, 6],
+ 0xeb110a: [0x26, 0],
+ 0xeb110b: [0x26, 1],
+ 0xeb110c: [0x26, 2],
+ 0xeb110d: [0x26, 3],
+ 0xeb110e: [0x26, 4],
+ 0xeb110f: [0x26, 5],
+ 0xeb1110: [0x26, 6],
+ 0xeb1111: [0x27, 0],
+ 0xeb1112: [0x27, 1],
+ 0xeb1113: [0x27, 2],
+ 0xeb1114: [0x27, 3],
+ 0xeb1115: [0x27, 4],
+ 0xeb1116: [0x27, 5],
+ 0xeb1118: [0x28, 0],
+ 0xeb1119: [0x28, 1],
+ 0xeb111a: [0x28, 2],
+ 0xeb111b: [0x28, 3],
+ 0xeb111c: [0x28, 4],
+ 0xeb111d: [0x28, 5],
+ 0xeb1126: [0x2a, 0],
+ 0xeb1127: [0x2a, 1],
+ 0xeb1128: [0x2a, 2],
+ 0xeb112d: [0x2b, 0],
+ 0xeb112e: [0x2b, 1],
+ 0xeb112f: [0x2b, 2],
+ 0xeb1130: [0x2b, 3],
+ 0xeb1131: [0x2b, 4],
+ 0xeb1132: [0x2b, 5],
+ 0xeb1134: [0x2c, 0],
+ 0xeb1135: [0x2c, 1],
+ 0xeb1136: [0x2c, 2],
+ 0xeb1137: [0x2c, 3],
+ 0xeb113b: [0x2d, 0],
+ 0xeb113c: [0x2d, 1],
+ 0xeb113d: [0x2d, 2],
+ 0xeb113e: [0x2d, 3],
+ 0xeb113f: [0x2d, 4],
+ 0xeb1140: [0x2d, 5],
+ 0xeb1141: [0x2d, 6],
+ 0xeb1157: [0x31, 0],
+ 0xeb1158: [0x31, 1],
+ 0xeb1159: [0x31, 2],
+ 0xeb115a: [0x31, 3],
+ 0xeb115e: [0x32, 0],
+ 0xeb115f: [0x32, 1],
+ 0xeb1160: [0x32, 2],
+ 0xeb1161: [0x32, 3],
+ 0xeb1162: [0x32, 4],
+ 0xeb1163: [0x32, 5],
+ 0xeb1164: [0x32, 6],
+ 0xeb1165: [0x33, 0],
+ 0xeb1166: [0x33, 1],
+ 0xeb1167: [0x33, 2],
+ 0xeb1168: [0x33, 3],
+ 0xeb116c: [0x34, 0],
+ 0xeb116d: [0x34, 1],
+ 0xeb116e: [0x34, 2],
+ 0xeb116f: [0x34, 3],
+ 0xeb1170: [0x34, 4],
+ 0xeb1171: [0x34, 5],
+ 0xeb1172: [0x34, 6],
+ 0xeb117a: [0x36, 0],
+ 0xeb117b: [0x36, 1],
+ 0xeb117c: [0x36, 2],
+ 0xeb117d: [0x36, 3],
+ 0xeb117e: [0x36, 4],
+ 0xeb117f: [0x36, 5],
+ 0xeb1180: [0x36, 6],
+ 0xeb1181: [0x37, 0],
+ 0xeb1182: [0x37, 1],
+ 0xeb1183: [0x37, 2],
+ 0xeb1188: [0x38, 0],
+ 0xeb1189: [0x38, 1],
+ 0xeb118a: [0x38, 2],
+ 0xeb118b: [0x38, 3],
+ 0xeb118c: [0x38, 4],
+ 0xeb118d: [0x38, 5],
+ 0xeb118e: [0x38, 6],
+ 0xeb118f: [0x39, 0],
+ 0xeb1190: [0x39, 1],
+ 0xeb1191: [0x39, 2],
+ 0xeb1192: [0x39, 3],
+ 0xeb1193: [0x39, 4],
+ 0xeb1194: [0x39, 5],
+ 0xeb1195: [0x39, 6],
+ 0xeb1196: [0x3a, 0],
+ 0xeb1197: [0x3a, 1],
+ 0xeb1198: [0x3a, 2],
+ 0xeb1199: [0x3a, 3],
+ 0xeb119a: [0x3a, 4],
+ 0xeb119b: [0x3a, 5],
+ 0xeb119c: [0x3a, 6],
+ 0xeb119d: [0x3b, 0],
+ 0xeb119e: [0x3b, 1],
+ 0xeb119f: [0x3b, 2],
+ 0xeb11a0: [0x3b, 3],
+ 0xeb11a1: [0x3b, 4],
+ 0xeb11a4: [0x3c, 0],
+ 0xeb11a5: [0x3c, 1],
+ 0xeb11a6: [0x3c, 2],
+ 0xeb11a7: [0x3c, 3],
+ 0xeb11ab: [0x3d, 0],
+ 0xeb11ac: [0x3d, 1],
+ 0xeb11ad: [0x3d, 2],
+ 0xeb11ae: [0x3d, 3],
+ 0xeb11af: [0x3d, 4],
+ 0xeb11b0: [0x3d, 5],
+ 0xeb11b1: [0x3d, 6],
+ 0xeb11b2: [0x3e, 0],
+ 0xeb11b3: [0x3e, 1],
+ 0xeb11b4: [0x3e, 2],
+ 0xeb11b5: [0x3e, 3],
+ 0xeb11b6: [0x3e, 4],
+ 0xeb11b7: [0x3e, 5],
+ 0xeb11b8: [0x3e, 6],
+ 0xeb11c0: [0x40, 0],
+ 0xeb11c1: [0x40, 1],
+ 0xeb11c2: [0x40, 2],
+ 0xeb11c3: [0x40, 3],
+ 0xeb11c4: [0x40, 4]
+}
+
+location_dialogue = { # Locations which just need to print the name of the item
+ "Onett - Tracy Gift": [0x07616B, 0x330478],
+ "Onett - Meteor Item": [0x0862C3],
+ "Onett - Library Counter": [0x05EC0C],
+ "Onett - Library Bookshelf": [0x2EBA66],
+ "Onett - Treehouse Guy": [0x073F34],
+ "Onett - Mayor Pirkle": [0x0661D6, 0x072D4B, 0x072BE9],
+ "Onett - Traveling Entertainer": [0x07476C],
+ "Twoson - Bike Shop Rental": [0x080046, 0x08008F],
+ "Twoson - Orange Kid Donation": [0x08134A, 0x0813B9, 0x3303E3],
+ "Twoson - Apple Kid Invention": [0x081F89, 0x081FF3],
+ "Twoson - Apple Kid's Mouse": [0x2EBA86],
+ "Twoson - Antique Shop": [0x093E5F, 0x093F0A],
+ "Twoson - Paula's Mother": [0x08061B, 0x0807F7],
+ "Twoson - Everdred Meeting": [0x095FA2],
+ "Twoson - Insignificant Location": [0x094EB1],
+ "Happy-Happy Village - Donation Lady": [0x2EC578, 0x2EC580],
+ "Happy-Happy Village - Prisoner Item": [0x2F95FE, 0x33001E, 0x079646],
+ "Happy-Happy Village - Defeat Carpainter": [0x098215],
+ "Threed - Hospital Drawer": [0x08686C],
+ "Saturn Valley - Post Belch Gift #1": [0x07EAAA],
+ "Saturn Valley - Post Belch Gift #2": [0x07EACE],
+ "Saturn Valley - Post Belch Gift #3": [0x07EAF2],
+ "Dusty Dunes - Shining Spot": [0x060D8D],
+ "Dusty Dunes - Mine Reward": [0x06038A, 0x06123A, 0x06053F],
+ "Snow Wood - Maxwell Item": [0x06AB89],
+ "Scaraba - Snake Bag Salesman": [0x2EA713, 0x057116],
+ "Winters - Drugstore Saleswoman": [0x07A223, 0x06AEB8],
+ "Andonuts Lab - Mouse": [0x06C840],
+ "Stonehenge - Tony Item": [0x2FFA0C],
+ "Stonehenge - Kidnapped Mr. Saturn": [0x06C061],
+ "Monkey Caves - Bow Monkey Gift": [0x0625E7],
+ "Monkey Caves - Talah Rama Gift": [0x060E77],
+ "Fourside - Venus Gift": [0x06D643],
+ "Fourside - Post-Moonside Delivery": [0x07AA04, 0x3310E6],
+ "Fourside - Bakery 2F Gift": [0x06DDAC],
+ "Monotoli Building - Monotoli Gift": [0x06EAA9],
+ "Monotoli Building - Electra Gift": [0x06E9A9],
+ "Summers - Museum Item": [0x08ABB8],
+ "Pyramid - Pedestal Item": [0x2EBA92],
+ "Scaraba - Key Holder": [0x2EBA9E],
+ "Deep Darkness - Teleporting Monkey": [0x2EBEE2],
+ "Deep Darkness - North Alcove Truffle": [0x3315CE],
+ "Deep Darkness - Near Land Truffle": [0x33162A],
+ "Deep Darkness - Present Truffle": [0x3315FC],
+ "Deep Darkness - Village Truffle": [0x331658],
+ "Deep Darkness - Entrance Truffle": [0x3315A0],
+ "Tenda Village - Tenda Gift": [0x09DA6C, 0x09DA79],
+ "Tenda Village - Tenda Gift #2": [0x09D7FD],
+ "Magicant - Ness's Gift": [0x077614],
+ "Cave of the Present - Broken Phase Distorter": [0x2F58FC],
+ "Onett - Buzz Buzz": [0x2EAA43],
+ "Onett - Mani Mani Statue": [0x2EA99E],
+ "Deep Darkness - Barf Character": [0x2EB664],
+ "Dalaam - Throne Character": [0x2EB4E2],
+ "Monotoli Building - Monotoli Character": [0x2EB2C3],
+ "Snow Wood - Bedroom": [0x2EB10D],
+ "Threed - Zombie Prisoner": [0x2EAFB1, 0x2EC9E1],
+ "Happy-Happy Village - Prisoner": [0x2EBABA],
+ "Saturn Valley - Saturn Coffee": [0x2EB008],
+ "Monkey Caves - Monkey Power": [0x2EB1A1],
+ "Fourside - Department Store Blackout": [0x2EBFE8, 0x2ED155],
+ "Summers - Magic Cake": [0x2EBFB4],
+ "Scaraba - Star Master": [0x2EBFA7],
+ "Dalaam - Trial of Mu": [0x2EBF95, 0x2EBF88],
+ "Tenda Village - Tenda Tea": [0x2EB6CC],
+ "Lost Underworld - Talking Rock": [0x2EB751],
+ "Cave of the Present - Star Master": [0x2EB8DF],
+ "Magicant - Ness's Nightmare": [0x2EB803, 0x2EB87C],
+ "Dungeon Man - 1F Exit Ledge Present": [0x057A55],
+ "Snow Wood - Upper Right Locker": [0x2EFB11],
+ "Snow Wood - Upper Left Locker": [0x2EFB50],
+ "Snow Wood - Bottom Right Locker": [0x2EFB8F],
+ "Snow Wood - Bottom Left Locker": [0x2EFBCE]
+}
+
+npc_locations = { # Locations given to you by an NPC
+ "Onett - Tracy Gift": [0x07617B],
+ "Onett - Meteor Item": [0x086304],
+ "Onett - Library Counter": [0x07339C],
+ "Onett - Library Bookshelf": [0x087E7B],
+ "Onett - Treehouse Guy": [0x073F78],
+ "Onett - Mayor Pirkle": [0x072D61],
+ "Onett - Traveling Entertainer": [0x07477C],
+ "Twoson - Bike Shop Rental": [0x0801A5],
+ "Twoson - Orange Kid Donation": [0x081362],
+ "Twoson - Apple Kid's Mouse": [0x0819DF],
+ "Twoson - Apple Kid Invention": [0x082015],
+ "Twoson - Antique Shop": [0x2EAEFD],
+ "Twoson - Paula's Mother": [0x08062F, 0x08081F],
+ "Twoson - Everdred Meeting": [0x096052],
+ "Twoson - Insignificant Location": [0x094ED1],
+ "Happy-Happy Village - Donation Lady": [0x098A97, 0x0893B6],
+ "Happy-Happy Village - Prisoner Item": [0x2F9664, 0x2EAF41, 0x2F9707],
+ "Happy-Happy Village - Defeat Carpainter": [0x09826F],
+ "Threed - Hospital Drawer": [0x086883],
+ "Saturn Valley - Post Belch Gift #1": [0x07EAB2],
+ "Saturn Valley - Post Belch Gift #2": [0x07EAD6],
+ "Saturn Valley - Post Belch Gift #3": [0x07EAFA],
+ "Dusty Dunes - Shining Spot": [0x060DAD],
+ "Dusty Dunes - Mine Reward": [0x06058F],
+ "Snow Wood - Maxwell Item": [0x06AC73],
+ "Scaraba - Snake Bag Salesman": [0x0570AC],
+ "Winters - Drugstore Saleswoman": [0x06AEC7],
+ "Andonuts Lab - Mouse": [0x06C898],
+ "Stonehenge - Kidnapped Mr. Saturn": [0x06C076],
+ "Stonehenge - Tony Item": [0x2FFA78],
+ "Monkey Caves - Bow Monkey Gift": [0x062636],
+ "Monkey Caves - Talah Rama Gift": [0x060FEB],
+ "Fourside - Venus Gift": [0x2EB1CE],
+ "Fourside - Post-Moonside Delivery": [0x07AAC4, 0x07B598, 0x331101],
+ "Fourside - Bakery 2F Gift": [0x06DE56],
+ "Monotoli Building - Electra Gift": [0x2EB29B],
+ "Monotoli Building - Monotoli Gift": [0x06EB3C],
+ "Summers - Museum Item": [0x08ABF2],
+ "Pyramid - Pedestal Item": [0x0931DF],
+ "Scaraba - Key Holder": [0x0576C1],
+ "Deep Darkness - Teleporting Monkey": [0x09E50F, 0x09E55F],
+ "Deep Darkness - North Alcove Truffle": [0x3315E5],
+ "Deep Darkness - Near Land Truffle": [0x331641],
+ "Deep Darkness - Present Truffle": [0x331613],
+ "Deep Darkness - Village Truffle": [0x33166F],
+ "Deep Darkness - Entrance Truffle": [0x3315B7],
+ "Tenda Village - Tenda Gift": [0x09DAF0],
+ "Tenda Village - Tenda Gift #2": [0x09D814],
+ "Magicant - Ness's Gift": [0x077629],
+ "Cave of the Present - Broken Phase Distorter": [0x2F590C],
+ "Dungeon Man - 1F Exit Ledge Present": [0x057A6B],
+ "Snow Wood - Upper Right Locker": [0x2EFB2A],
+ "Snow Wood - Upper Left Locker": [0x2EFB69],
+ "Snow Wood - Bottom Right Locker": [0x2EFBA8],
+ "Snow Wood - Bottom Left Locker": [0x2EFBE7]
+}
+
+
+present_locations = { # Field presents
+ "Onett - Tracy's Room Present": 0x0FE756,
+ "Onett - Hilltop Present": 0x0FE701,
+ "Onett - Burger Shop Trashcan": 0x0FE712,
+ "Onett - South Road Present": 0x0FE723,
+ "Onett - Hotel Trashcan": 0x0FE734,
+ "Onett - Arcade Trashcan": 0x0FE745,
+ "Giant Step - First Cave Present": 0x0FE778,
+ "Giant Step - Floor 2 Cave Present": 0x0FE789,
+ "Giant Step - Floor 3 Present": 0x0FE767,
+ "Twoson - Paula's Room Present": 0x0FE7AB,
+ "Twoson - Apple Kid Trashcan": 0x0FE7BC,
+ "Twoson - South of Town Present": 0x0FE79A,
+ "Peaceful Rest Valley - Split Hill Present": 0x0FE822,
+ "Peaceful Rest Valley - Hill Nook Present": 0x0FE7EF,
+ "Peaceful Rest Valley - South of Bridge Present": 0x0FE811,
+ "Peaceful Rest Valley - Dead End Present": 0x0FE833,
+ "Peaceful Rest Valley - River Overlook Present": 0x0FE844,
+ "Peaceful Rest Valley - North Side Present": 0x0FE800,
+ "Happy-Happy Village - Right HQ Present": 0x0FE7DE,
+ "Happy-Happy Village - Left HQ Present": 0x0FE7CD,
+ "Lilliput Steps - Southwest Pool Present": 0x0FE866,
+ "Lilliput Steps - East Cliff Present": 0x0FE877,
+ "Lilliput Steps - North Stream Present": 0x0FE855,
+ "Threed - Boogey Tent Trashcan": 0x0FB038,
+ "Threed - Cemetery Trashcan": 0x0FE888,
+ "Threed - Downtown Trashcan": 0x0FE8AA,
+ "Threed - East Side Trashcan": 0x0FE8BB,
+ "Threed - Northeast Shack Trashcan": 0x0FE899,
+ "Threed Underground - Left Coffin": 0x0FE8CC,
+ "Threed Underground - Right Coffin": 0x0FE8DD,
+ "Grapefruit Falls - South Present": 0x0FEB0E,
+ "Grapefruit Falls - North Present": 0x0FEAFD,
+ "Grapefruit Falls - Saturn Cave Present": 0x0FEB1F,
+ "Saturn Valley - Ladder Present": 0x0FEB30,
+ "Saturn Valley - Trashcan #1": 0x0FEB41,
+ "Saturn Valley - Trashcan #2": 0x0FEB52,
+ "Saturn Valley - Trashcan #3": 0x0FEB63,
+ "Milky Well - Cavern Present": 0x0FEAEC,
+ "Belch's Factory - Top Right Room Trashcan": 0x0FEA97,
+ "Belch's Factory - Pit Room Trashcan #1": 0x0FEADB,
+ "Belch's Factory - Pit Room Trashcan #2": 0x0FEA86,
+ "Belch's Factory - Balcony Room Trashcan #1": 0x0FEACA,
+ "Belch's Factory - Balcony Room Trashcan #2": 0x0FEAB9,
+ "Belch's Factory - Balcony Room Trashcan #3": 0x0FEAA8,
+ "Dusty Dunes - Northwest Corner Present": 0x0FEC84,
+ "Dusty Dunes - South Side Present": 0x0FECD9,
+ "Dusty Dunes - Surrounding Rocks Present": 0x0FEC73,
+ "Dusty Dunes - Black Sesame Present": 0x0FECA6,
+ "Dusty Dunes - Oasis Present": 0x0FECB7,
+ "Dusty Dunes - Northeast Corner Present": 0x0FECC8,
+ "Dusty Dunes - North Central Present": 0x0FEC95,
+ "Dusty Dunes - East Peninsula Present": 0x0FECEA,
+ "Snow Wood - Many Present Room Present #1": 0x0FE932,
+ "Snow Wood - Many Present Room Present #2": 0x0FE8FF,
+ "Snow Wood - Many Present Room Present #3": 0x0FE921,
+ "Snow Wood - Many Present Room Present #4": 0x0FE954,
+ "Snow Wood - Many Present Room Present #5": 0x0FE943,
+ "Snow Wood - Many Present Room Present #6": 0x0FE910,
+ "Snow Wood - Many Present Room Present #7": 0x0FE8EE,
+ "Brick Road Maze - Top Path Present": 0x0FE9BA,
+ "Brick Road Maze - Guarded Present": 0x0FE998,
+ "Brick Road Maze - Out of the Way Present": 0x0FE976,
+ "Brick Road Maze - Alcove Present": 0x0FE9A9,
+ "Brick Road Maze - Near Exit Present": 0x0FE987,
+ "Rainy Circle - Isolated Present": 0x0FE9ED,
+ "Rainy Circle - East Cliff Present": 0x0FE9CB,
+ "Rainy Circle - Near Ropes Present": 0x0FE9DC,
+ "Andonuts Lab - Present": 0x0FE965,
+ "Stonehenge - Purple Maze Present": 0x0FEA75,
+ "Stonehenge - Dead End Present": 0x0FEA0F,
+ "Stonehenge - Near End of the Maze Present": 0x0FEA53,
+ "Stonehenge - Bridge Room East Balcony Present": 0x0FEA20,
+ "Stonehenge - Bridge Room Lower Present": 0x0FEA31,
+ "Stonehenge - Flashing Room Right Path Present": 0x0FE9FE,
+ "Stonehenge - Flashing Room Center Present": 0x0FEA42,
+ "Stonehenge - Flashing Room Upper Present": 0x0FEA64,
+ "Gold Mine - Mouse Crossroad Present #1": 0x0FEDC7,
+ "Gold Mine - Mouse Crossroad Present #2": 0x0FEDA5,
+ "Gold Mine - B1F Lonely Mole Present": 0x0FED1D,
+ "Gold Mine - South Hall Present": 0x0FEDD8,
+ "Gold Mine - South Corner Present": 0x0FED94,
+ "Gold Mine - South Mole Present #1": 0x0FED3F,
+ "Gold Mine - South Mole Present #2": 0x0FED0C,
+ "Gold Mine - North Crossroad Detour Present": 0x0FED61,
+ "Gold Mine - North Mole Present": 0x0FED2E,
+ "Gold Mine - West Mole Present": 0x0FECFB,
+ "Gold Mine - B1F Isolated Present": 0x0FED83,
+ "Gold Mine - West Crossroad Detour Present": 0x0FED72,
+ "Gold Mine - B1F Junction Present": 0x0FEDB6,
+ "Gold Mine - B1F Junction Mole Present": 0x0FED50,
+ "Monkey Caves - 1F Right Chest": 0x0FEBEB,
+ "Monkey Caves - 1F Left Chest": 0x0FEBDA,
+ "Monkey Caves - West 2F Left Chest": 0x0FEBFC,
+ "Monkey Caves - West 2F Right Chest #1": 0x0FEC1E,
+ "Monkey Caves - West 2F Right Chest #2": 0x0FEC0D,
+ "Monkey Caves - East 2F Left Chest": 0x0FEC2F,
+ "Monkey Caves - East 2F Right Chest": 0x0FEC40,
+ "Monkey Caves - East West 3F Right Chest #1": 0x0FEC62,
+ "Monkey Caves - East West 3F Right Chest #2": 0x0FEC51,
+ "Monkey Caves - West End Chest": 0x0FEB74,
+ "Monkey Caves - West End Trashcan": 0x0FEB85,
+ "Monkey Caves - East End Chest": 0x0FEB96,
+ "Monkey Caves - East End Trashcan": 0x0FEBA7,
+ "Monkey Caves - Talah Rama Chest #1": 0x0FEBB8,
+ "Monkey Caves - Talah Rama Chest #2": 0x0FEBC9,
+ "Moonside - Two Trees Present": 0x0FEE1C,
+ "Moonside - East Island Present": 0x0FEE2D,
+ "Moonside - Businessman Present": 0x0FEE60,
+ "Moonside - West Island Present": 0x0FEE4F,
+ "Moonside - Hospital Present": 0x0FEE3E,
+ "Magnet Hill - West Entrance Trashcan": 0x0FEEA4,
+ "Magnet Hill - First Room Free Door Trashcan": 0x0FEEB5,
+ "Magnet Hill - First Room Barrel Door Trashcan": 0x0FEE93,
+ "Magnet Hill - Second Room Dead End Trashcan": 0x0FEE71,
+ "Magnet Hill - Final Room Door Trashcan": 0x0FEE82,
+ "Fourside - Magnet Hill Chest": 0x0FEDE9,
+ "Monotoli Building - One Table Present": 0x0FEDFA,
+ "Monotoli Building - Two Table Present": 0x0FEE0B,
+ "Dalaam - Throne Room Chest #1": 0x0FEFF8,
+ "Dalaam - Throne Room Chest #2": 0x0FF009,
+ "Dalaam - Throne Room Chest #3": 0x0FF01A,
+ "Dalaam - Restaurant Chest #1": 0x0FF03C,
+ "Dalaam - Restaurant Chest #2": 0x0FF05E,
+ "Dalaam - Do Do Guy's House Chest": 0x0FF04D,
+ "Dalaam - Upper House Chest": 0x0FF02B,
+ "Pink Cloud - Three Holes Present": 0x0FF080,
+ "Pink Cloud - Left Hole Present": 0x0FF06F,
+ "Pink Cloud - Ground Floor Present": 0x0FF091,
+ "Pyramid - Anteroom Sarcophagus": 0x0FEEE8,
+ "Pyramid - Northwest Door Sarcophagus": 0x0FEEC6,
+ "Pyramid - Hallway Sarcophagus #1": 0x0FEED7,
+ "Pyramid - Hallway Sarcophagus #2": 0x0FEF0A,
+ "Pyramid - Switch Room Sarcophagus": 0x0FEF1B,
+ "Pyramid - Way Out Sarcophagus": 0x0FEEF9,
+ "Dungeon Man - 1F Dead End Present": 0x0FEF70,
+ "Dungeon Man - 1F Long Walk Present": 0x0FEF2C,
+ "Dungeon Man - 1F Disappointing Present": 0x0FEFE7,
+ "Dungeon Man - 1F Opinion Present": 0x0FEF92,
+ "Dungeon Man - 1F No Sign Present": 0x0FEFA3,
+ "Dungeon Man - 2F Unnecessary Billboard Present": 0x0FEFB4,
+ "Dungeon Man - 2F Dungeon Exploration Present": 0x0FEF4E,
+ "Dungeon Man - 2F South Ledge Present": 0x0FEF81,
+ "Dungeon Man - 2F North Alcove Present": 0x0FEF5F,
+ "Dungeon Man - 3F Present": 0x0FEFD6,
+ "Dungeon Man - 2F Hole Present": 0x0FEFC5,
+ "Dungeon Man - 1F Exit Ledge Present": 0x0FEF3D,
+ "Deep Darkness - Crest of Darkness Present": 0x0FF0E6,
+ "Deep Darkness - Helicopter Present": 0x0FF0D5,
+ "Deep Darkness - Yellow Bird Present": 0x0FF0C4,
+ "Deep Darkness - Swamp Present": 0x0FF0F7,
+ "Deep Darkness - Corner Present": 0x0FF0A2,
+ "Deep Darkness - Alcove Present": 0x0FF0B3,
+ "Tenda Village - Trashcan": 0x0FF1A1,
+ "Lumine Hall - B1F Non-Talkative Rock Present": 0x0FF190,
+ "Lumine Hall - 1F North Path Present": 0x0FF14C,
+ "Lumine Hall - B1F Thankful Rock Corner Present": 0x0FF12A,
+ "Lumine Hall - B1F Thankful Rock Junction Present": 0x0FF17F,
+ "Lumine Hall - 1F Above Belly Button Present": 0x0FF119,
+ "Lumine Hall - B1F Belly Button Present": 0x0FF15D,
+ "Lumine Hall - 1F Near Exit Present": 0x0FF108,
+ "Lumine Hall - 1F Dead End Present": 0x0FF13B,
+ "Lumine Hall - B1F West Alcove Present": 0x0FF16E,
+ "Lost Underworld - East Present": 0x0FF1C3,
+ "Lost Underworld - Northeast Present": 0x0FF1D4,
+ "Lost Underworld - Northeast of Tenda Tribe Present": 0x0FF1E5,
+ "Lost Underworld - Southwest of Tenda Tribe Present": 0x0FF1F6,
+ "Lost Underworld - Evacuation Present": 0x0FF1B2,
+ "Fire Spring - 1st Cave Present": 0x0FF24B,
+ "Fire Spring - East Corner Present": 0x0FF229,
+ "Fire Spring - Volcano Present": 0x0FF23A,
+ "Fire Spring - Lone Cave Present": 0x0FF218,
+ "Fire Spring - Upper Volcano Present": 0x0FF207,
+ "Magicant - Present Near Ness": 0x0FF25C,
+ "Magicant - Lonely Present": 0x0FF27E,
+ "Magicant - North Present": 0x0FF28F,
+ "Magicant - Hills Present": 0x0FF2A0,
+ "Magicant - Town Present": 0x0FF26D,
+ "Cave of the Past - Present": 0x0FF2B1
+}
+
+psi_locations = {
+ "Onett - Mani Mani Statue": [0x2EA976, 0x90, 0xA9, 0xEE, 0x2EA9B9],
+ "Onett - Buzz Buzz": [0x2EAA04, 0x12, 0xAA, 0xEE, 0x2EAA49],
+ "Saturn Valley - Saturn Coffee": [0x2EAFE3, 0xF1, 0xAF, 0xEE, 0x2EB030],
+ "Monkey Caves - Monkey Power": [0x2EB12E, 0x68, 0xB1, 0xEE, 0x2EB1B1],
+ "Fourside - Department Store Blackout": [0x2EB22F, 0x66, 0xB2, 0xEE, 0x2EBFEF],
+ "Summers - Magic Cake": [0x2EB311, 0x1F, 0xB3, 0xEE, 0x2EB3A1],
+ "Dalaam - Trial of Mu": [0x2EB3FA, 0x08, 0xB4, 0xEE, 0x2EB443],
+ "Scaraba - Star Master": [0x2EB521, 0x32, 0xB5, 0xEE, 0x2EBFAD],
+ "Tenda Village - Tenda Tea": [0x2EB6A9, 0xB7, 0xB6, 0xEE, 0x2EB6E7],
+ "Lost Underworld - Talking Rock": [0x2EB72D, 0x3B, 0xB7, 0xEE, 0x2EB770],
+ "Magicant - Ness's Nightmare": [0x2EB7B4, 0xC7, 0xB7, 0xEE, 0x2EB812],
+ "Cave of the Present - Star Master": [0x2EB8C8, 0xD6, 0xB8, 0xEE, 0x2EB8EE]
+}
+
+character_locations = {
+ "Happy-Happy Village - Prisoner": [0x2EBAA9, 0x0FA68B, 0x2EBAA4, 0x2EBAC9, 0xB8, 0xBA, 0xEE],
+ "Threed - Zombie Prisoner": [0x2EAF7F, 0x0FA977, 0x2EAF7A, 0x2EAFC5, 0xAF, 0xAF, 0xEE],
+ "Snow Wood - Bedroom": [0x2EB0DA, 0x0FB0D6, 0x2EB0D5, 0x2EB11C, 0x0B, 0xB1, 0xEE],
+ "Monotoli Building - Monotoli Character": [0x2EB2B2, 0x0FC37F, 0x2EB2AD, 0x2EB2D2, 0xC1, 0xB2, 0xEE],
+ "Dalaam - Throne Character": [0x2EB4D1, 0x0FD20A, 0x2EB4CC, 0x2EB4F1, 0xE0, 0xB4, 0xEE],
+ "Deep Darkness - Barf Character": [0x2EB654, 0x0FE0C8, 0x2EB64F, 0x2EB673, 0x62, 0xB6, 0xEE]
+}
+
+item_id_table = {
+ "Franklin Badge": 0x01,
+ "Teddy Bear": 0x02,
+ "Super Plush Bear": 0x03,
+ "Broken Machine": 0x04,
+ "Broken Gadget": 0x05,
+ "Broken Air Gun": 0x06,
+ "Broken Spray Can": 0x07,
+ "Broken Laser": 0x08,
+ "Broken Iron": 0x09,
+ "Broken Pipe": 0x0A,
+ "Broken Cannon": 0x0B,
+ "Broken Tube": 0x0C,
+ "Broken Bazooka": 0x0D,
+ "Broken Trumpet": 0x0E,
+ "Broken Harmonica": 0x0F,
+ "Broken Antenna": 0x10,
+ "Cracked Bat": 0x11,
+ "Tee Ball Bat": 0x12,
+ "Sand Lot Bat": 0x13,
+ "Minor League Bat": 0x14,
+ "Mr. Baseball Bat": 0x15,
+ "Big League Bat": 0xD5,
+ "Hall of Fame Bat": 0x17,
+ "Magicant Bat": 0x18,
+ "Legendary Bat": 0x19,
+ "Gutsy Bat": 0x1A,
+ "Casey Bat": 0x1B,
+ "Fry Pan": 0x1C,
+ "Thick Fry Pan": 0x1D,
+ "Deluxe Fry Pan": 0x1E,
+ "Chef's Fry Pan": 0x1F,
+ "French Fry Pan": 0x20,
+ "Magic Fry Pan": 0x21,
+ "Holy Fry Pan": 0x22,
+ "Sword of Kings": 0x23,
+ "Pop Gun": 0x24,
+ "Stun Gun": 0x25,
+ "Toy Air Gun": 0x26,
+ "Magnum Air Gun": 0x27,
+ "Zip Gun": 0x28,
+ "Laser Gun": 0x29,
+ "Hyper Beam": 0x2A,
+ "Crusher Beam": 0x2B,
+ "Spectrum Beam": 0x2C,
+ "Death Ray": 0x2D,
+ "Baddest Beam": 0x2E,
+ "Moon Beam Gun": 0x2F,
+ "Gaia Beam": 0x30,
+ "Yo-yo": 0x31,
+ "Slingshot": 0x32,
+ "Bionic Slingshot": 0x33,
+ "Trick Yo-yo": 0x34,
+ "Combat Yo-yo": 0x35,
+ "Travel Charm": 0x36,
+ "Great Charm": 0x37,
+ "Crystal Charm": 0x38,
+ "Rabbit's Foot": 0x39,
+ "Flame Pendant": 0x3A,
+ "Rain Pendant": 0x3B,
+ "Night Pendant": 0x3C,
+ "Sea Pendant": 0x3D,
+ "Star Pendant": 0x3E,
+ "Cloak of Kings": 0x3F,
+ "Cheap Bracelet": 0x40,
+ "Copper Bracelet": 0x41,
+ "Silver Bracelet": 0x42,
+ "Gold Bracelet": 0x43,
+ "Platinum Band": 0xD8,
+ "Diamond Band": 0xD9,
+ "Pixie's Bracelet": 0x46,
+ "Cherub's Band": 0x47,
+ "Goddess Band": 0x48,
+ "Bracer of Kings": 0x49,
+ "Baseball Cap": 0x4A,
+ "Holmes Hat": 0x4B,
+ "Mr. Baseball Cap": 0x4C,
+ "Hard Hat": 0x4D,
+ "Ribbon": 0x4E,
+ "Red Ribbon": 0x4F,
+ "Goddess Ribbon": 0x50,
+ "Coin of Slumber": 0x51,
+ "Coin of Defense": 0x52,
+ "Lucky Coin": 0x53,
+ "Talisman Coin": 0x54,
+ "Shiny Coin": 0x55,
+ "Souvenir Coin": 0x56,
+ "Diadem of Kings": 0x57,
+ "Cookie": 0x58,
+ "Bag of Fries": 0x59,
+ "Hamburger": 0x5A,
+ "Boiled Egg": 0x5B,
+ "Fresh Egg": 0x5C,
+ "Picnic Lunch": 0x5D,
+ "Pasta di Summers": 0x5E,
+ "Pizza": 0x5F,
+ "Chef's Special": 0x60,
+ "Large Pizza": 0x61,
+ "PSI Caramel": 0x62,
+ "Magic Truffle": 0x63,
+ "Brain Food Lunch": 0x64,
+ "Rock Candy": 0x65,
+ "Croissant": 0x66,
+ "Bread Roll": 0x67,
+ "Pak of Bubble Gum": 0x68,
+ "Jar of Fly Honey": 0x69,
+ "Can of Fruit Juice": 0x6A,
+ "Royal Iced Tea": 0x6B,
+ "Protein Drink": 0x6C,
+ "Kraken Soup": 0x6D,
+ "Bottle of Water": 0x6E,
+ "Cold Remedy": 0x6F,
+ "Vial of Serum": 0x70,
+ "IQ Capsule": 0x71,
+ "Guts Capsule": 0x72,
+ "Speed Capsule": 0x73,
+ "Vital Capsule": 0x74,
+ "Luck Capsule": 0x75,
+ "Ketchup Packet": 0x76,
+ "Sugar Packet": 0x77,
+ "Tin of Cocoa": 0x78,
+ "Carton of Cream": 0x79,
+ "Sprig of Parsley": 0x7A,
+ "Jar of Hot Sauce": 0x7B,
+ "Salt Packet": 0x7C,
+ "Tiny Key": 0x7D,
+ "Jar of Delisauce": 0x7E,
+ "Wet Towel": 0x7F,
+ "Refreshing Herb": 0x80,
+ "Secret Herb": 0x81,
+ "Horn of Life": 0x82,
+ "Counter-PSI Unit": 0x83,
+ "Shield Killer": 0x84,
+ "Bazooka": 0x85,
+ "Heavy Bazooka": 0x86,
+ "HP-Sucker": 0x87,
+ "Hungry HP-Sucker": 0x88,
+ "Xterminator Spray": 0x89,
+ "Slime Generator": 0x8A,
+ "Yogurt Dispenser": 0x8B,
+ "Ruler": 0x8C,
+ "Snake Bag": 0x8D,
+ "Mummy Wrap": 0x8E,
+ "Protractor": 0x8F,
+ "Bottle Rocket": 0x90,
+ "Big Bottle Rocket": 0x91,
+ "Multi Bottle Rocket": 0x92,
+ "Bomb": 0x93,
+ "Super Bomb": 0x94,
+ "Insecticide Spray": 0x95,
+ "Rust Promoter": 0x96,
+ "Rust Promoter DX": 0x97,
+ "Pair of Dirty Socks": 0x98,
+ "Stag Beetle": 0x99,
+ "Toothbrush": 0x9A,
+ "Handbag Strap": 0x9B,
+ "Pharaoh's Curse": 0x9C,
+ "Defense Shower": 0x9D,
+ "UFO Engine": 0x9E,
+ "Sudden Guts Pill": 0x9F,
+ "Bag of Dragonite": 0xA0,
+ "Defense Spray": 0xA1,
+ "Piggy Nose": 0xA2,
+ "For Sale Sign": 0xA3,
+ "Shyness Book": 0xA4,
+ "Picture Postcard": 0xA5,
+ "King Banana": 0xA6,
+ "Letter For Tony": 0xA7,
+ "Chick": 0xA8,
+ "Chicken": 0xA9,
+ "Key to the Shack": 0xAA,
+ "Key to the Cabin": 0xAB,
+ "Bad Key Machine": 0xAC,
+ "Zombie Paper": 0xAE,
+ "Hawk Eye": 0xAF,
+ "Bicycle": 0xB0,
+ "ATM Card": 0xB1,
+ "Show Ticket": 0xB2,
+ "Tenda Lavapants": 0xB3,
+ "Wad of Bills": 0xB4,
+ "Receiver Phone": 0xB5,
+ "Diamond": 0xB6,
+ "Signed Banana": 0xB7,
+ "Pencil Eraser": 0xB8,
+ "Hieroglyph Copy": 0xB9,
+ "Meteotite": 0xBA,
+ "Contact Lens": 0xBB,
+ "Hand-Aid": 0xBC,
+ "Trout Yogurt": 0xBD,
+ "Banana": 0xBE,
+ "Calorie Stick": 0xBF,
+ "Key to the Tower": 0xC0,
+ "Meteorite Piece": 0xC1,
+ "Earth Pendant": 0xC2,
+ "Neutralizer": 0xC3,
+ "Sound Stone": 0xC4,
+ "Exit Mouse": 0xC5,
+ "Gelato de Resort": 0xC6,
+ "Snake": 0xC7,
+ "Viper": 0xC8,
+ "Brain Stone": 0xC9,
+ "Police Badge": 0xCA,
+ "Mining Permit": 0xCB,
+ "Suporma": 0xCC,
+ "Key to the Locker": 0xCD,
+ "Insignificant Item": 0xCE,
+ "Magic Tart": 0xCF,
+ "Tiny Ruby": 0xD0,
+ "Monkey's Love": 0xD1,
+ "Eraser Eraser": 0xD2,
+ "Tendakraut": 0xD3,
+ "T-Rex's Bat": 0xD4,
+ "Ultimate Bat": 0xD6,
+ "Double Beam": 0xD7,
+ "Defense Ribbon": 0xDA,
+ "Talisman Ribbon": 0xDB,
+ "Saturn Ribbon": 0xDC,
+ "Coin of Silence": 0xDD,
+ "Charm Coin": 0xDE,
+ "Cup of Noodles": 0xDF,
+ "Repel Sandwich": 0xE0,
+ "Repel Superwich": 0xE1,
+ "Lucky Sandwich": 0xE2,
+ "Progressive Bat": 0xE3,
+ "Progressive Fry Pan": 0xE4,
+ "Progressive Gun": 0xE5,
+ "Progressive Bracelet": 0xE6,
+ "Progressive Other": 0xE7,
+ "Cup of Coffee": 0xE8,
+ "Double Burger": 0xE9,
+ "Peanut Cheese Bar": 0xEA,
+ "Piggy Jelly": 0xEB,
+ "Bowl of Rice Gruel": 0xEC,
+ "Bean Croquette": 0xED,
+ "Molokheiya Soup": 0xEE,
+ "Plain Roll": 0xEF,
+ "Kabob": 0xF0,
+ "Plain Yogurt": 0xF1,
+ "Beef Jerky": 0xF2,
+ "Mammoth Burger": 0xF3,
+ "Spicy Jerky": 0xF4,
+ "Luxury Jerky": 0xF5,
+ "Bottle of DXwater": 0xF6,
+ "Magic Pudding": 0xF7,
+ "Non-Stick Frypan": 0xF8,
+ "Mr. Saturn Coin": 0xF9,
+ "Meteornium": 0xFA,
+ "Popsicle": 0xFB,
+ "Cup of Lifenoodles": 0xFC,
+ "Carrot Key": 0xFD
+}
+
+psi_item_table = {
+ "Onett Teleport": 0x01,
+ "Twoson Teleport": 0x02,
+ "Happy-Happy Village Teleport": 0x03,
+ "Threed Teleport": 0x04,
+ "Saturn Valley Teleport": 0x05,
+ "Dusty Dunes Teleport": 0x06,
+ "Fourside Teleport": 0x07,
+ "Winters Teleport": 0x08,
+ "Summers Teleport": 0x09,
+ "Scaraba Teleport": 0x0A,
+ "Dalaam Teleport": 0x0B,
+ "Deep Darkness Teleport": 0x0C,
+ "Tenda Village Teleport": 0x0D,
+ "Lost Underworld Teleport": 0x0E,
+ "Magicant Teleport": 0x0F,
+ "Progressive Poo PSI": 0x10
+}
+
+character_item_table = {
+ "Paula": [0x01, 0x02, 0x0189, 0x06],
+ "Jeff": [0x02, 0x03, 0x018A, 0x06],
+ "Poo": [0x03, 0x04, 0x018B, 0x06],
+ "Flying Man": [0x04, 0x27, 0x0027, 0x04],
+ "Ness": [0x05, 0x00, 0x0010, 0x01, 0x06], # the last 2 might not be needed anymore
+ "Photograph": [0x06, 0x0153, 0x01C1]
+}
+
+party_id_nums = {
+ "Paula": 0x02,
+ "Jeff": 0x03,
+ "Poo": 0x04,
+ "Flying Man": 0x0B,
+ "Ness": 0x01
+}
+
+special_name_table = {
+ "Onett Teleport": [0x00, 0xEE9210],
+ "Twoson Teleport": [0x01, 0xEE9240],
+ "Happy-Happy Village Teleport": [0x02, 0xEE9271],
+ "Threed Teleport": [0x03, 0xEE92AF],
+ "Saturn Valley Teleport": [0x04, 0xEE92E0],
+ "Dusty Dunes Teleport": [0x05, 0xEE9318],
+ "Fourside Teleport": [0x06, 0xEE9355],
+ "Winters Teleport": [0x07, 0xEE9388],
+ "Summers Teleport": [0x08, 0xEE93BA],
+ "Scaraba Teleport": [0x09, 0xEE93EC],
+ "Dalaam Teleport": [0x0A, 0xEE941E],
+ "Deep Darkness Teleport": [0x0B, 0xEE944F],
+ "Tenda Village Teleport": [0x0C, 0xEE948B],
+ "Lost Underworld Teleport": [0x0D, 0xEE94C3],
+ "Progressive Poo PSI": [0x0E, 0xEE9554],
+ "Paula": [0x0F, 0xD5F830],
+ "Jeff": [0x10, 0xD5F837],
+ "Poo": [0x11, 0xD5F83E],
+ "Flying Man": [0x12, 0xD5F845],
+ "Magicant Teleport": [0x13, 0xEE9501],
+ "Ness": [0x14, 0xD5F849],
+ "Photograph": [0x15, 0xF3FFE0]
+}
+
+client_specials = {
+ 0x00FE: 0x01,
+ 0x00FF: 0x02,
+ 0x0100: 0x0D,
+ 0x0101: 0x03,
+ 0x0102: 0x05,
+ 0x0103: 0x0E,
+ 0x0104: 0x06,
+ 0x0105: 0x04,
+ 0x0106: 0x07,
+ 0x0107: 0x09,
+ 0x0108: 0x08,
+ 0x0109: 0x0A,
+ 0x010A: 0x0B,
+ 0x010B: 0x0C,
+ 0x010C: 0x14,
+ 0x010D: 0x0F,
+ 0x010E: 0x10,
+ 0x010F: 0x11,
+ 0x0110: 0x12,
+ 0x0111: 0x13,
+ 0x0112: 0x15,
+ 0x0113: 0x16
+}
+
+
+starting_psi_table = {
+ "Onett Teleport": 0x01,
+ "Twoson Teleport": 0x02,
+ "Threed Teleport": 0x03,
+ "Winters Teleport": 0x04,
+ "Saturn Valley Teleport": 0x05,
+ "Fourside Teleport": 0x06,
+ "Summers Teleport": 0x07,
+ "Dalaam Teleport": 0x08,
+ "Scaraba Teleport": 0x09,
+ "Deep Darkness Teleport": 0x0A,
+ "Tenda Village Teleport": 0x0B,
+ "Lost Underworld Teleport": 0x0C,
+ "Happy-Happy Village Teleport": 0x0D,
+ "Dusty Dunes Teleport": 0x0E,
+ "Magicant Teleport": 0x0F,
+ "Progressive Poo PSI": 0x10
+}
+
+# Todo; convert the list shit to a Struct.pack!
+item_space_checks = {
+ "Onett - Tracy Gift": [0x076174, 0x78, 0x61, 0xC7],
+ "Onett - Meteor Item": [0x0862FD, 0x01, 0x63, 0xC8],
+ "Onett - Library Counter": [0x05ECA7, 0xAB, 0xEC, 0xC5],
+ "Onett - Library Bookshelf": [0x087E74, 0x78, 0x7E, 0xC3],
+ "Onett - Treehouse Guy": [0x073F71, 0x75, 0x3F, 0xC7],
+ "Onett - Mayor Pirkle": [0x072D5A, 0x5E, 0x2D, 0xC7],
+ "Onett - Traveling Entertainer": [0x074775, 0x79, 0x74, 0xC7],
+ "Twoson - Bike Shop Rental": [0x08019E, 0xA2, 0x01, 0xC8],
+ "Twoson - Orange Kid Donation": [0x08135B, 0x5F, 0x13, 0xC8],
+ "Twoson - Apple Kid's Mouse": [0x0819D8, 0xDC, 0x19, 0xC8],
+ "Twoson - Apple Kid Invention": [0x08200E, 0x11, 0x20, 0xC8],
+ "Twoson - Antique Shop": [0x093EE3, 0xE7, 0x3E, 0xC9],
+ "Twoson - Paula's Mother": [0x080628, 0x2C, 0x0C, 0xC8],
+ "Twoson - Everdred Meeting": [0x09604B, 0x4F, 0x60, 0xC9],
+ "Happy-Happy Village - Donation Lady": [0x098A90, 0x94, 0x8A, 0xC9, 0x0893AF, 0xB3, 0x93, 0xC8],
+ "Happy-Happy Village - Prisoner Item": [0x2F965D, 0x61, 0x96, 0xEF, 0x2EAF3A, 0x3E, 0xAF, 0xEE],
+ "Happy-Happy Village - Defeat Carpainter": [0x098268, 0x6C, 0x82, 0xC9],
+ "Threed - Hospital Drawer": [0x08687C, 0x80, 0x68, 0xC8],
+ "Threed - Boogey Tent Trashcan": [0x2EF640, 0x44, 0xF6, 0xEE],
+ "Saturn Valley - Post Belch Gift #1": [0x2EC049, 0x4D, 0xC0, 0xEE],
+ "Saturn Valley - Post Belch Gift #2": [0x2EC063, 0x67, 0xC0, 0xEE],
+ "Saturn Valley - Post Belch Gift #3": [0x2EC056, 0x5A, 0xC0, 0xEE],
+ "Dusty Dunes - Shining Spot": [0x060DA1, 0xA5, 0x0D, 0xC6],
+ "Dusty Dunes - Mine Reward": [0x060588, 0x8C, 0x05, 0xC6],
+ "Snow Wood - Maxwell Item": [0x06AC6C, 0x70, 0xAC, 0xC6],
+ "Scaraba - Snake Bag Salesman": [0x057024, 0x28, 0x70, 0xC5],
+ "Winters - Drugstore Saleswoman": [0x2EF625, 0x29, 0xF6, 0xEE],
+ "Andonuts Lab - Mouse": [0x06C891, 0x95, 0xC8, 0xC6],
+ "Stonehenge - Kidnapped Mr. Saturn": [0x06C06F, 0x73, 0xC0, 0xC6],
+ "Stonehenge - Tony Item": [0x2FFA71, 0x7F, 0xFA, 0xEF],
+ "Monkey Caves - Bow Monkey Gift": [0x0625F1, 0xF5, 0x25, 0xC6],
+ "Monkey Caves - Talah Rama Gift": [0x060FE1, 0xE5, 0x0F, 0xC6],
+ "Fourside - Venus Gift": [0x2EB1C7, 0xCB, 0xB1, 0xEE],
+ "Fourside - Post-Moonside Delivery": [0x07AABD, 0xC1, 0xAA, 0xC7, 0x07B57C, 0x80, 0xB5, 0xC7],
+ "Monotoli Building - Electra Gift": [0x06E99C, 0xA0, 0xE9, 0xC6],
+ "Monotoli Building - Monotoli Gift": [0x06EB35, 0x39, 0xEB, 0xC6],
+ "Summers - Museum Item": [0x08ABEB, 0xEF, 0xAB, 0xC8],
+ "Pyramid - Pedestal Item": [0x0931D8, 0xDC, 0x31, 0xC9],
+ "Scaraba - Key Holder": [0x0576B9, 0xBD, 0x76, 0xC5], # Extra break?
+ "Deep Darkness - Teleporting Monkey": [0x09E508, 0x0C, 0xE5, 0xC9],
+ "Deep Darkness - North Alcove Truffle": [0x3315DE, 0xE2, 0x15, 0xF3],
+ "Deep Darkness - Near Land Truffle": [0x33163A, 0x3E, 0x16, 0xF3],
+ "Deep Darkness - Present Truffle": [0x33160C, 0x10, 0x16, 0xF3],
+ "Deep Darkness - Village Truffle": [0x331668, 0x6C, 0x16, 0xF3],
+ "Deep Darkness - Entrance Truffle": [0x3315B0, 0xB4, 0x15, 0xF3],
+ "Tenda Village - Tenda Gift #2": [0x09D80D, 0x11, 0xD8, 0xC9],
+ "Magicant - Ness's Gift": [0x077622, 0x26, 0x76, 0xC7],
+ "Cave of the Present - Broken Phase Distorter": [0x2F5905, 0x09, 0x59, 0xEF],
+ "Onett - Mani Mani Statue": [0x2EBEA6, 0xAA, 0xBE, 0xEE],
+ "Onett - Buzz Buzz": [0x2EAA4C, 0x50, 0xAA, 0xEE],
+ "Saturn Valley - Saturn Coffee": [0x2EB029, 0x2D, 0xB0, 0xEE],
+ "Monkey Caves - Monkey Power": [0x2EB1AA, 0xAE, 0xB1, 0xEE],
+ "Fourside - Department Store Blackout": [0x2EB26B, 0x6F, 0xB2, 0xEE],
+ "Summers - Magic Cake": [0x2EB39A, 0x9E, 0xB3, 0xEE],
+ "Dalaam - Trial of Mu": [0x2EB43C, 0x40, 0xB4, 0xEE],
+ "Scaraba - Star Master": [0x2EB537, 0x3B, 0xB5, 0xEE],
+ "Tenda Village - Tenda Tea": [0x2EB6E0, 0xE4, 0xB6, 0xEE],
+ "Lost Underworld - Talking Rock": [0x2EB769, 0x6D, 0xB7, 0xEE],
+ "Magicant - Ness's Nightmare": [0x2EB80B, 0x0F, 0xB8, 0xEE],
+ "Cave of the Present - Star Master": [0x2EB8E7, 0xEB, 0xB8, 0xEE],
+ "Happy-Happy Village - Prisoner": [0x2EBAC2, 0xC6, 0xBA, 0xEE],
+ "Threed - Zombie Prisoner": [0x2EAF85, 0x89, 0xAF, 0xEE],
+ "Dalaam - Throne Character": [0x2EB4EA, 0xEE, 0xB4, 0xEE],
+ "Snow Wood - Upper Right Locker": [0x2EFB23, 0x27, 0xFB, 0xEE],
+ "Snow Wood - Upper Left Locker": [0x2EFB62, 0x66, 0xFB, 0xEE],
+ "Snow Wood - Bottom Right Locker": [0x2EFBA1, 0xA5, 0xFB, 0xEE],
+ "Snow Wood - Bottom Left Locker": [0x2EFBE0, 0xE4, 0xFB, 0xEE],
+ "Snow Wood - Bedroom": [0x2EB0E0, 0xE4, 0xB0, 0xEE],
+ "Monotoli Building - Monotoli Character": [0x2EB2B8, 0xBC, 0xB2, 0xEE],
+ "Deep Darkness - Barf Character": [0x2EB65A, 0x5E, 0xB6, 0xEE]
+}
+
+protection_checks = {
+ "thunder": [0x0297D2],
+ "fire": [0x2FFC35, 0x2FFC77],
+ "freeze": [0x2FFC56],
+ "flash": [0x2FFC9B],
+ "starstorm": [0x2FFCCD],
+ "special": [0x2FFC1E],
+ "explosive": [0x2FFCED]
+}
+
+badge_names = {
+ "thunder": "Franklin badge",
+ "fire": "Bunsen badge",
+ "freeze": "Thilorier badge",
+ "flash": "Cowen badge",
+ "starstorm": "Leavitt badge",
+ "special": "Mesmer badge",
+ "explosive": "Nobel badge"
+}
+
+protection_text = {
+ "thunder": [0xEEC911, 0xEEC95F],
+ "fire": [0xEEC91C, 0xEEC969],
+ "freeze": [0xEEC922, 0xEEC96E],
+ "flash": [0xEEC928, 0xEEC972],
+ "starstorm": [0xEEC92F, 0xEEC978],
+ "special": [0xEEC93A, 0xEEC982],
+ "explosive": [0xEEC94A, 0xEEC989]
+}
+
+local_present_types = {
+ "filler": [0xd6, 0x00],
+ "progression": [0xE9, 0x00],
+ "useful": [0xC3, 0x00],
+ "trap": [0xE9, 0x00],
+ "progression_skip_balancing": [0xE9, 0x00]
+}
+
+nonlocal_present_types = {
+ "filler": [0x98, 0x01],
+ "progression": [0x90, 0x01],
+ "useful": [0x98, 0x01],
+ "trap": [0x90, 0x01],
+ "progression_skip_balancing": [0x90, 0x01]
+
+}
+
+present_text_pointers = {
+ "filler": [0x5b, 0xd8, 0xc7],
+ "progression": [0x67, 0xd8, 0xc7],
+ "useful": [0x4F, 0xD8, 0xC7],
+ "progression_skip_balancing": [0x67, 0xd8, 0xc7],
+ "trap": [0x67, 0xd8, 0xc7]
+}
+
+ap_text_pntrs = [
+ 0xEECDCD, # ce
+ 0xEECDD9, # cf
+ 0xEECDE5, # d0
+ 0xEECDF1, # e7
+ 0xEECDFD, # e8
+ 0xEECE09, # e9
+ 0xEECE15, # ea
+ 0xEECE21, # eb
+ 0xEECE2D, # ec
+ 0xEECE39, # ed
+ 0xEECE45, # ee
+ 0xEECE51, # ef
+ 0xEECE5D, # f0
+ 0xEECE69, # f1
+ 0xEECE75, # f2
+ 0xEECE81, # f3
+ # [0x8D, 0xCE, 0xEE] #trap text
+]
+
+hint_bits = [
+ 0x04,
+ 0x08,
+ 0x10,
+ 0x20,
+ 0x40,
+ 0x80
+]
+
+money_item_table = {
+ "$10": 10,
+ "$100": 100,
+ "$1000": 1000
+}
+
+money_id_table = {
+ 0x114: 10,
+ 0x115: 100,
+ 0x116: 1000
+}
+
+# 0: Action pointer, call the actual item being given
+# 1: Sprite ID
+# 2: Secondary routine pointer, used for items and psi
+# 3: Inventory item give
+# 4: Standard item routine address
+# For characters
diff --git a/worlds/earthbound/game_data/palettes_organized.py b/worlds/earthbound/game_data/palettes_organized.py
new file mode 100644
index 000000000000..aaf9276279b1
--- /dev/null
+++ b/worlds/earthbound/game_data/palettes_organized.py
@@ -0,0 +1,11066 @@
+map_palettes = {
+ "Pyramid": 0,
+ "Lost Underworld": 1,
+ "Cave of the Past": 2,
+ "Giygas's Lair": 3,
+ "Onett": 4,
+ # "Onett Nighttime": 5,
+ # "Onett Flashback": 6,
+ "Twoson": 7,
+ "Happy-Happy Village": 8,
+ "Happy-Happy Clear": 9,
+ "Tunnel Outdoors": 10,
+ "Threed": 11,
+ "Threed Daytime": 12,
+ "Fourside": 13,
+ "Moonside": 14,
+ "Magicant": 15,
+ "Tunnels": 16,
+ "Magicant Interiors": 17,
+ "Tents": 18,
+ "Peaceful Rest Valley": 19,
+ "Grapefruit Falls": 20,
+ "Saturn Valley": 21,
+ "Deep Darkness": 22,
+ "Lilliput Steps Sanctuary": 23,
+ "Milky Well": 24,
+ "Deep Darkness Darkness": 25,
+ "Summers": 26,
+ "Dusty Dunes Desert": 27,
+ "The Sky": 28,
+ "Dalaam": 29,
+ "Dalaam Palace": 30,
+ "Cave of the Present": 31,
+ "Pink Cloud Sanctuary": 32,
+ "The Sea": 33,
+ # "????": 34,
+ "Monotoli Offices": 35,
+ "Onett Hotel": 36,
+ "Threed Red-Wall House": 37,
+ "Electra's Room": 38,
+ "Threed Green-Wall House": 39,
+ "Threed Blue-Wall House": 40,
+ "Pokey's House": 41,
+ "Moonside Hospital": 42,
+ "Onett Green-Wall House": 43,
+ "Onett Blue-Wall House": 44,
+ "Onett Seafoam-Wall House": 45,
+ "Onett Peach-Wall House": 46,
+ "Onett Light Blue-Wall House": 47,
+ "Onett Pink-Wall House": 48,
+ "Onett White-Wall House": 49,
+ # "Onett Flashback House": 50,
+ "Hospitals": 51,
+ "Apple Kid's House": 52,
+ "Tracy's Room": 53,
+ "Orange Kid's House": 54,
+ "Everdred's House": 55,
+ "Twoson Green-Wall House": 56,
+ "Ness's Room": 57,
+ "Police Station Backroom": 58,
+ "Winters": 59,
+ "Rainy Circle Sanctuary": 60,
+ # "Winters Nighttime": 61,
+ # "Ness's House Nighttime": 62,
+ "Hospital Rooms": 63,
+ "Twoson Peach-Wall House": 64,
+ "Twoson Pink-Wall House": 65,
+ "Twoson Gray-Wall House": 66,
+ "Gold Office": 67,
+ "Happy-Happy Guardhouse": 68,
+ "Happy-Happy Hospital": 69,
+ "Happy-Happy Hospital Clear": 70,
+ "Onett Town Hall": 71,
+ "Onett Hotel Bedrooms": 72,
+ "Fourside Hotel Bedrooms": 73,
+ "Happy-Happy Saturn House": 74,
+ "Happy-Happy Hotel": 75,
+ "Twoson Hotel Bedrooms": 76,
+ "Threed Hotel Bedrooms": 77,
+ "Happy-Happy Hotel Clear": 78,
+ "Monotoli Building": 79,
+ "Paula's House": 80,
+ "Paula's Room": 81,
+ "Ness's House": 82,
+ "Twoson Hotel": 83,
+ "Threed Hotel": 84,
+ "Fourside Hotel": 85,
+ "Monotoli Elevators": 86,
+ "Fourside Dept. Store Office": 87,
+ "Onett Library": 88,
+ "Paula's House Rooms": 89,
+ "Onett Police Station": 90,
+ "Scaraba": 91,
+ "Dalaam House": 92,
+ "Scaraba House": 93,
+ "Happy-Happy HQ": 94,
+ "Fourside Cafe": 95,
+ "Moonside Cafe": 96,
+ "Scaraba Hospital": 97,
+ "Scaraba Shop": 98,
+ "Twoson Dept. Store": 99,
+ "Fourside Dept. Store": 100,
+ "Bakery": 101,
+ "Mach Pizza": 102,
+ "Burger Shop": 103,
+ "Onett Drugstore": 104,
+ "Threed Drugstore": 105,
+ "Winters Drugstore": 106,
+ "Chaos Theater": 107,
+ "Fourside Museum": 108,
+ "Summers Hotel": 109,
+ "Summers Shop": 110,
+ # "Topolla Theater Stage": 111,
+ "Snow Wood Boarding School": 112,
+ # "Chaos Theater Stage": 113,
+ "Summers Harbor House": 114,
+ "Summers Restaurant": 115,
+ "Moonside Museum": 116,
+ "Stoic Club": 117,
+ "Andonuts Lab": 118,
+ "Topolla Theater": 119,
+ "Summers Museum": 120,
+ "Onett Arcade": 121,
+ "Twoson Bus Station": 122,
+ "Twoson Bike Shop": 123,
+ "Hint Shop": 124,
+ "Fourside Dept. Store Rooms": 125,
+ "Threed Tent": 126,
+ "Onett Treehouse": 127,
+ "Lier's House": 128,
+ "Run-down House": 129,
+ "Paula's Cabin": 130,
+ "Dusty Dunes Drugstore": 131,
+ "Dusty Dunes Miner Shack": 132,
+ "Traveler's Shack": 133,
+ "Lumine Hall": 134,
+ "Connector Caves": 135,
+ "Lumine Hall Sanctuary": 136,
+ "Fire Spring": 137,
+ "Sea of Eden": 138,
+ "Monkey Caves Treasure Rooms": 139,
+ "Dungeon Man": 140,
+ "Threed Underground": 141,
+ "Lier's Cave": 142,
+ "Stonehenge Balcony Room": 143,
+ "Stonehenge Balcony Off": 144,
+ "Brickroad Maze": 145,
+ "Rainy Circle": 146,
+ "Belch's Factory": 147,
+ "Milky Well Caves": 148,
+ "Lilliput Steps": 149,
+ "Monkey Caves Hallway": 150,
+ "Giant Step": 151,
+ "Pink Cloud": 152,
+ "Saturn Caves": 153,
+ "Stonehenge Tunnels": 154,
+ # "Stonehenge Flashing Room": 155,
+ # "????": 156,
+ "Gold Mine": 157,
+ "Sewer": 158,
+ "Tenda Village": 159,
+ "Fourside Dept. Store Blackout": 160,
+ "Fourside Dept. Store Blackout Rooms": 161,
+ "Magicant Manly Fish": 162,
+ "Magicant Ness": 163,
+ # "Magicant Tracy (Unused)": 164,
+ "Magicant Rabbit": 165,
+ "Magicant Belch": 166,
+ "Magicant Mom": 167
+}
+
+# To anyone reviewing this, you can just close and ignore these.
+
+nice_palettes = {
+ "Pyramid": [
+ 0,
+ 6,
+ 26,
+ 31,
+ 33,
+ 34,
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 57,
+ 62,
+ 67,
+ 68,
+ 69,
+ 70,
+ 75,
+ 78,
+ 79,
+ 82,
+ 84,
+ 85,
+ 87,
+ 92,
+ 93,
+ 95,
+ 99,
+ 100,
+ 101,
+ 107,
+ 109,
+ 110,
+ 111,
+ 112,
+ 113,
+ 118,
+ 119,
+ 124,
+ 128,
+ 129,
+ 130,
+ 131,
+ 132,
+ 133,
+ 155,
+ 156,
+ 159,
+ 160
+ ],
+ "Lost Underworld": [
+ 1,
+ 22,
+ 62,
+ 79,
+ 86,
+ 107,
+ 114,
+ 130,
+ 133,
+ 134,
+ 141,
+ 154,
+ 156
+ ],
+ "Cave of the Past": [
+ 2,
+ 42,
+ 62,
+ 79,
+ 86,
+ 92,
+ 101,
+ 107,
+ 123,
+ 137,
+ 140,
+ 142,
+ 146,
+ 157
+ ],
+ "Giygas's Lair": [
+ 0,
+ 3,
+ 30,
+ 33,
+ 36,
+ 37,
+ 50,
+ 67,
+ 83,
+ 84,
+ 111,
+ 128,
+ 132,
+ 139,
+ 160,
+ 164
+ ],
+ "Onett": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 21,
+ 22,
+ 42,
+ 105,
+ 106,
+ 107,
+ 109,
+ 116
+ ],
+ "Twoson": [
+ 4,
+ 5,
+ 7,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 22,
+ 42,
+ 59,
+ 62,
+ 63,
+ 107,
+ 116
+ ],
+ "Happy-Happy Village": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 21,
+ 22,
+ 42,
+ 47,
+ 59,
+ 62
+ ],
+ "Happy-Happy Clear": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 21,
+ 22,
+ 42,
+ 47,
+ 59,
+ 62
+ ],
+ "Tunnel Outdoors": [
+ 1,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 42,
+ 59,
+ 62,
+ 63,
+ 79,
+ 86,
+ 92,
+ 95,
+ 96,
+ 104,
+ 105,
+ 106,
+ 107,
+ 109,
+ 116,
+ 141,
+ 154
+ ],
+ "Threed": [
+ 4,
+ 5,
+ 7,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 42,
+ 62,
+ 116
+ ],
+ "Threed Daytime": [
+ 4,
+ 5,
+ 7,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 42,
+ 62,
+ 116
+ ],
+ "Fourside": [
+ 4,
+ 5,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 42,
+ 109,
+ 116
+ ],
+ "Moonside": [
+ 4,
+ 5,
+ 7,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 21,
+ 22,
+ 42,
+ 63,
+ 109,
+ 116
+ ],
+ "Magicant": [
+ 15,
+ 42,
+ 63,
+ 79,
+ 89,
+ 97,
+ 112,
+ 115,
+ 116,
+ 118,
+ 162,
+ 163,
+ 164,
+ 165,
+ 166,
+ 167
+ ],
+ "Tunnels": [
+ 0,
+ 1,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 16,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 33,
+ 38,
+ 40,
+ 41,
+ 43,
+ 48,
+ 49,
+ 57,
+ 59,
+ 61,
+ 62,
+ 63,
+ 68,
+ 70,
+ 71,
+ 75,
+ 78,
+ 82,
+ 85,
+ 86,
+ 90,
+ 92,
+ 94,
+ 95,
+ 99,
+ 100,
+ 101,
+ 104,
+ 105,
+ 111,
+ 117,
+ 119,
+ 120,
+ 128,
+ 129,
+ 131,
+ 135,
+ 136,
+ 137,
+ 140,
+ 141,
+ 142,
+ 146,
+ 147,
+ 148,
+ 149,
+ 150,
+ 151,
+ 152,
+ 153,
+ 154,
+ 156,
+ 157,
+ 158,
+ 159
+ ],
+ "Magicant Interiors": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 25,
+ 26,
+ 27,
+ 32,
+ 33,
+ 34,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 44,
+ 51,
+ 52,
+ 54,
+ 58,
+ 62,
+ 71,
+ 75,
+ 79,
+ 81,
+ 82,
+ 86,
+ 88,
+ 91,
+ 101,
+ 103,
+ 105,
+ 107,
+ 109,
+ 112,
+ 115,
+ 116,
+ 120,
+ 123,
+ 132,
+ 146,
+ 147,
+ 157,
+ 160,
+ 162,
+ 163,
+ 164,
+ 165,
+ 166,
+ 167
+ ],
+ "Tents": [
+ 18,
+ 20,
+ 29,
+ 42,
+ 63,
+ 79,
+ 80,
+ 93,
+ 107,
+ 109,
+ 116,
+ 146,
+ 147,
+ 157
+ ],
+ "Peaceful Rest Valley": [
+ 4,
+ 5,
+ 13,
+ 14,
+ 19,
+ 21,
+ 22,
+ 42,
+ 62,
+ 63,
+ 82,
+ 116
+ ],
+ "Grapefruit Falls": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 42,
+ 62,
+ 63,
+ 101,
+ 109,
+ 112,
+ 133
+ ],
+ "Saturn Valley": [
+ 4,
+ 5,
+ 7,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 21,
+ 22,
+ 42,
+ 59,
+ 63,
+ 116
+ ],
+ "Deep Darkness": [
+ 22,
+ 42,
+ 47,
+ 62,
+ 112,
+ 116
+ ],
+ "Lilliput Steps Sanctuary": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 42,
+ 59,
+ 61,
+ 62,
+ 63,
+ 101,
+ 112
+ ],
+ "Milky Well": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 12,
+ 13,
+ 14,
+ 21,
+ 22,
+ 24,
+ 42,
+ 59,
+ 62,
+ 63,
+ 116
+ ],
+ "Deep Darkness Darkness": [
+ 25
+ ],
+ "Summers": [
+ 14,
+ 26,
+ 27,
+ 42,
+ 57,
+ 59,
+ 62,
+ 91,
+ 93,
+ 107,
+ 109,
+ 112,
+ 115,
+ 116,
+ 118
+ ],
+ "Dusty Dunes Desert": [
+ 27,
+ 38,
+ 39,
+ 42,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 68,
+ 71,
+ 75,
+ 78,
+ 80,
+ 90,
+ 92,
+ 93,
+ 104,
+ 109,
+ 112,
+ 115,
+ 116,
+ 119,
+ 120,
+ 130,
+ 133,
+ 163
+ ],
+ "The Sky": [
+ 0,
+ 1,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 27,
+ 28,
+ 31,
+ 32,
+ 33,
+ 34,
+ 35,
+ 36,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 45,
+ 46,
+ 47,
+ 48,
+ 49,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 55,
+ 56,
+ 57,
+ 58,
+ 59,
+ 62,
+ 65,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 74,
+ 75,
+ 76,
+ 77,
+ 78,
+ 79,
+ 80,
+ 81,
+ 82,
+ 83,
+ 84,
+ 85,
+ 86,
+ 87,
+ 88,
+ 89,
+ 90,
+ 91,
+ 92,
+ 93,
+ 94,
+ 95,
+ 96,
+ 97,
+ 98,
+ 99,
+ 100,
+ 101,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107,
+ 108,
+ 109,
+ 110,
+ 111,
+ 112,
+ 113,
+ 114,
+ 115,
+ 117,
+ 118,
+ 119,
+ 120,
+ 121,
+ 122,
+ 123,
+ 124,
+ 125,
+ 126,
+ 127,
+ 128,
+ 129,
+ 130,
+ 131,
+ 132,
+ 133,
+ 134,
+ 139,
+ 140,
+ 141,
+ 154,
+ 159,
+ 160,
+ 161,
+ 162,
+ 165,
+ 166
+ ],
+ "Dalaam": [
+ 14,
+ 22,
+ 29,
+ 32,
+ 42,
+ 62,
+ 109,
+ 115,
+ 116
+ ],
+ "Dalaam Palace": [
+ 3,
+ 14,
+ 30,
+ 31,
+ 132,
+ 162
+ ],
+ "Cave of the Present": [
+ 31,
+ 72
+ ],
+ "Pink Cloud Sanctuary": [
+ 9,
+ 32,
+ 42,
+ 62,
+ 73,
+ 87,
+ 93,
+ 98,
+ 99,
+ 100,
+ 103,
+ 109,
+ 119,
+ 120,
+ 130
+ ],
+ "The Sea": [
+ 9,
+ 33,
+ 34,
+ 42,
+ 107,
+ 109,
+ 112,
+ 116,
+ 118
+ ],
+ "Monotoli Offices": [
+ 35,
+ 38,
+ 39,
+ 41,
+ 42,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 63,
+ 71,
+ 80,
+ 82,
+ 88,
+ 90,
+ 102,
+ 104,
+ 107,
+ 109,
+ 116,
+ 120,
+ 128
+ ],
+ "Onett Hotel": [
+ 14,
+ 36,
+ 42,
+ 44,
+ 51,
+ 57,
+ 58,
+ 62,
+ 63,
+ 65,
+ 70,
+ 71,
+ 74,
+ 79,
+ 102,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Threed Red-Wall House": [
+ 14,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 70,
+ 71,
+ 75,
+ 78,
+ 81,
+ 82,
+ 90,
+ 104,
+ 112,
+ 116,
+ 120
+ ],
+ "Electra's Room": [
+ 14,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 48,
+ 50,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 73,
+ 75,
+ 76,
+ 78,
+ 80,
+ 81,
+ 82,
+ 90,
+ 93,
+ 109,
+ 112,
+ 116,
+ 118,
+ 119,
+ 120
+ ],
+ "Threed Green-Wall House": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 53,
+ 57,
+ 62,
+ 63,
+ 70,
+ 71,
+ 75,
+ 78,
+ 82,
+ 90,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Threed Blue-Wall House": [
+ 14,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 66,
+ 67,
+ 69,
+ 70,
+ 71,
+ 74,
+ 75,
+ 78,
+ 81,
+ 88,
+ 90,
+ 107,
+ 109,
+ 112,
+ 116,
+ 118,
+ 120
+ ],
+ "Pokey's House": [
+ 14,
+ 38,
+ 41,
+ 42,
+ 44,
+ 57,
+ 70,
+ 71,
+ 75,
+ 78,
+ 90,
+ 109,
+ 112,
+ 115,
+ 116,
+ 120
+ ],
+ "Moonside Hospital": [
+ 14,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 73,
+ 74,
+ 75,
+ 76,
+ 78,
+ 80,
+ 81,
+ 82,
+ 88,
+ 90,
+ 91,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Onett Green-Wall House": [
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 66,
+ 70,
+ 71,
+ 75,
+ 78,
+ 80,
+ 90,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Onett Blue-Wall House": [
+ 38,
+ 41,
+ 42,
+ 43,
+ 44,
+ 51,
+ 53,
+ 57,
+ 62,
+ 63,
+ 70,
+ 71,
+ 75,
+ 78,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Onett Seafoam-Wall House": [
+ 42,
+ 45,
+ 50,
+ 51,
+ 54,
+ 56,
+ 62,
+ 63,
+ 66,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Onett Peach-Wall House": [
+ 36,
+ 42,
+ 46,
+ 50,
+ 55,
+ 56,
+ 62,
+ 65,
+ 66,
+ 67,
+ 71,
+ 75,
+ 76,
+ 78,
+ 90,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Onett Light Blue-Wall House": [
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 70,
+ 71,
+ 75,
+ 78,
+ 79,
+ 82,
+ 83,
+ 85,
+ 86,
+ 90,
+ 104,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Onett Pink-Wall House": [
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 48,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 73,
+ 75,
+ 76,
+ 78,
+ 81,
+ 88,
+ 90,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Onett White-Wall House": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 48,
+ 49,
+ 50,
+ 51,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 67,
+ 71,
+ 72,
+ 73,
+ 74,
+ 75,
+ 76,
+ 78,
+ 79,
+ 81,
+ 83,
+ 86,
+ 87,
+ 109,
+ 112,
+ 116
+ ],
+ "Hospitals": [
+ 41,
+ 42,
+ 51,
+ 53,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 68,
+ 71,
+ 82,
+ 88,
+ 90,
+ 93,
+ 107,
+ 109,
+ 112,
+ 115,
+ 116,
+ 119,
+ 120
+ ],
+ "Apple Kid's House": [
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 66,
+ 69,
+ 70,
+ 71,
+ 75,
+ 78,
+ 80,
+ 81,
+ 82,
+ 88,
+ 90,
+ 102,
+ 109,
+ 116,
+ 119,
+ 120
+ ],
+ "Tracy's Room": [
+ 38,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 66,
+ 70,
+ 71,
+ 75,
+ 78,
+ 90,
+ 109,
+ 112,
+ 115,
+ 116,
+ 118,
+ 120
+ ],
+ "Orange Kid's House": [
+ 42,
+ 47,
+ 50,
+ 51,
+ 54,
+ 62,
+ 63,
+ 70,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Everdred's House": [
+ 36,
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 55,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 67,
+ 68,
+ 70,
+ 71,
+ 75,
+ 76,
+ 78,
+ 82,
+ 83,
+ 90,
+ 109,
+ 112,
+ 116,
+ 118,
+ 119,
+ 120
+ ],
+ "Twoson Green-Wall House": [
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 66,
+ 70,
+ 71,
+ 75,
+ 78,
+ 90,
+ 109,
+ 112,
+ 115,
+ 116,
+ 120
+ ],
+ "Ness's Room": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 68,
+ 71,
+ 75,
+ 76,
+ 78,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Police Station Backroom": [
+ 6,
+ 9,
+ 14,
+ 36,
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 45,
+ 46,
+ 48,
+ 49,
+ 50,
+ 51,
+ 53,
+ 55,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 73,
+ 74,
+ 75,
+ 76,
+ 78,
+ 79,
+ 80,
+ 81,
+ 82,
+ 83,
+ 84,
+ 85,
+ 86,
+ 87,
+ 88,
+ 90,
+ 102,
+ 103,
+ 104,
+ 107,
+ 109,
+ 112,
+ 116,
+ 118,
+ 119,
+ 120,
+ 128,
+ 131,
+ 132
+ ],
+ "Winters": [
+ 4,
+ 5,
+ 7,
+ 9,
+ 12,
+ 13,
+ 14,
+ 21,
+ 22,
+ 42,
+ 59,
+ 60,
+ 61,
+ 62,
+ 63,
+ 82,
+ 109,
+ 116,
+ 162
+ ],
+ "Rainy Circle Sanctuary": [
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 15,
+ 17,
+ 19,
+ 20,
+ 21,
+ 22,
+ 26,
+ 27,
+ 42,
+ 50,
+ 51,
+ 59,
+ 61,
+ 62,
+ 63,
+ 79,
+ 86,
+ 96,
+ 97,
+ 101,
+ 105,
+ 107,
+ 108,
+ 116,
+ 118,
+ 123,
+ 167
+ ],
+ "Hospital Rooms": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 66,
+ 70,
+ 71,
+ 75,
+ 78,
+ 81,
+ 88,
+ 90,
+ 109,
+ 112,
+ 116
+ ],
+ "Twoson Peach-Wall House": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 71,
+ 72,
+ 75,
+ 76,
+ 78,
+ 90,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Twoson Pink-Wall House": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 68,
+ 70,
+ 71,
+ 72,
+ 73,
+ 75,
+ 76,
+ 78,
+ 90,
+ 109,
+ 112,
+ 115,
+ 116,
+ 120
+ ],
+ "Twoson Gray-Wall House": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 66,
+ 70,
+ 71,
+ 75,
+ 78,
+ 81,
+ 109,
+ 112,
+ 116
+ ],
+ "Gold Office": [
+ 42,
+ 47,
+ 50,
+ 51,
+ 57,
+ 62,
+ 63,
+ 67,
+ 71,
+ 75,
+ 78,
+ 83,
+ 90,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Happy-Happy Guardhouse": [
+ 14,
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 68,
+ 70,
+ 71,
+ 72,
+ 73,
+ 75,
+ 76,
+ 78,
+ 81,
+ 90,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Happy-Happy Hospital": [
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 54,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 69,
+ 70,
+ 71,
+ 75,
+ 78,
+ 81,
+ 90,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Happy-Happy Hospital Clear": [
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 54,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 69,
+ 70,
+ 71,
+ 75,
+ 78,
+ 81,
+ 90,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Onett Town Hall": [
+ 14,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 54,
+ 57,
+ 58,
+ 63,
+ 66,
+ 67,
+ 68,
+ 70,
+ 71,
+ 75,
+ 83,
+ 88,
+ 90,
+ 100,
+ 102,
+ 104,
+ 105,
+ 107,
+ 109,
+ 112,
+ 116,
+ 118,
+ 120
+ ],
+ "Onett Hotel Bedrooms": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 68,
+ 70,
+ 71,
+ 72,
+ 73,
+ 75,
+ 76,
+ 78,
+ 81,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Fourside Hotel Bedrooms": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 48,
+ 50,
+ 51,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 73,
+ 75,
+ 76,
+ 77,
+ 78,
+ 81,
+ 88,
+ 90,
+ 102,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Happy-Happy Saturn House": [
+ 42,
+ 56,
+ 62,
+ 74,
+ 75,
+ 90,
+ 109,
+ 112,
+ 116,
+ 119,
+ 120
+ ],
+ "Happy-Happy Hotel": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 71,
+ 75,
+ 78,
+ 90,
+ 109,
+ 112,
+ 116
+ ],
+ "Twoson Hotel Bedrooms": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 68,
+ 70,
+ 71,
+ 75,
+ 76,
+ 78,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Threed Hotel Bedrooms": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 48,
+ 50,
+ 51,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 73,
+ 75,
+ 76,
+ 77,
+ 78,
+ 81,
+ 88,
+ 90,
+ 102,
+ 109,
+ 112,
+ 120
+ ],
+ "Happy-Happy Hotel Clear": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 51,
+ 53,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 71,
+ 75,
+ 78,
+ 90,
+ 109,
+ 112,
+ 116
+ ],
+ "Monotoli Building": [
+ 38,
+ 42,
+ 51,
+ 57,
+ 62,
+ 63,
+ 79,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Paula's House": [
+ 42,
+ 51,
+ 63,
+ 80,
+ 109,
+ 112,
+ 116,
+ 118,
+ 120
+ ],
+ "Paula's Room": [
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 62,
+ 63,
+ 66,
+ 69,
+ 70,
+ 71,
+ 75,
+ 78,
+ 81,
+ 90,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Ness's House": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 50,
+ 51,
+ 53,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 71,
+ 82,
+ 90,
+ 109,
+ 112,
+ 116
+ ],
+ "Twoson Hotel": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 44,
+ 50,
+ 51,
+ 53,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 70,
+ 71,
+ 72,
+ 75,
+ 83,
+ 90,
+ 109,
+ 112,
+ 116,
+ 118,
+ 120
+ ],
+ "Threed Hotel": [
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 70,
+ 71,
+ 75,
+ 78,
+ 79,
+ 83,
+ 84,
+ 88,
+ 90,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Fourside Hotel": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 70,
+ 71,
+ 75,
+ 78,
+ 79,
+ 82,
+ 83,
+ 85,
+ 86,
+ 90,
+ 104,
+ 109,
+ 112,
+ 116,
+ 118,
+ 119,
+ 120
+ ],
+ "Monotoli Elevators": [
+ 6,
+ 7,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 22,
+ 36,
+ 37,
+ 41,
+ 42,
+ 51,
+ 53,
+ 54,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 68,
+ 69,
+ 70,
+ 79,
+ 82,
+ 83,
+ 84,
+ 85,
+ 86,
+ 88,
+ 89,
+ 90,
+ 92,
+ 93,
+ 98,
+ 99,
+ 101,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107,
+ 109,
+ 112,
+ 116,
+ 119,
+ 120,
+ 123,
+ 127,
+ 132,
+ 160
+ ],
+ "Fourside Dept. Store Office": [
+ 42,
+ 45,
+ 50,
+ 57,
+ 62,
+ 66,
+ 67,
+ 87,
+ 88,
+ 109,
+ 112,
+ 116,
+ 120,
+ 161
+ ],
+ "Onett Library": [
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 57,
+ 58,
+ 62,
+ 63,
+ 70,
+ 71,
+ 75,
+ 78,
+ 88,
+ 90,
+ 102,
+ 107,
+ 109,
+ 112,
+ 116,
+ 118,
+ 120
+ ],
+ "Paula's House Rooms": [
+ 42,
+ 51,
+ 63,
+ 89,
+ 109,
+ 112,
+ 115,
+ 116,
+ 120
+ ],
+ "Onett Police Station": [
+ 14,
+ 41,
+ 42,
+ 50,
+ 62,
+ 63,
+ 66,
+ 71,
+ 75,
+ 78,
+ 82,
+ 90,
+ 102,
+ 104,
+ 105,
+ 106,
+ 107,
+ 109,
+ 112,
+ 116,
+ 119,
+ 120
+ ],
+ "Scaraba": [
+ 27,
+ 42,
+ 47,
+ 56,
+ 63,
+ 66,
+ 70,
+ 71,
+ 75,
+ 78,
+ 91,
+ 108,
+ 109,
+ 112,
+ 115,
+ 116,
+ 118,
+ 120,
+ 133
+ ],
+ "Dalaam House": [
+ 42,
+ 63,
+ 91,
+ 92,
+ 107,
+ 109,
+ 112,
+ 115,
+ 116,
+ 120
+ ],
+ "Scaraba House": [
+ 36,
+ 42,
+ 47,
+ 51,
+ 63,
+ 93,
+ 108,
+ 112,
+ 116,
+ 120
+ ],
+ "Happy-Happy HQ": [
+ 14,
+ 21,
+ 42,
+ 71,
+ 74,
+ 75,
+ 78,
+ 85,
+ 90,
+ 94,
+ 107,
+ 108,
+ 109,
+ 110,
+ 112,
+ 114,
+ 115,
+ 116,
+ 118,
+ 119,
+ 120
+ ],
+ "Fourside Cafe": [
+ 42,
+ 74,
+ 75,
+ 78,
+ 90,
+ 93,
+ 95,
+ 96,
+ 107,
+ 109,
+ 111,
+ 112,
+ 113,
+ 114,
+ 115,
+ 116,
+ 118,
+ 119,
+ 120
+ ],
+ "Moonside Cafe": [
+ 14,
+ 42,
+ 71,
+ 75,
+ 78,
+ 90,
+ 93,
+ 95,
+ 96,
+ 107,
+ 108,
+ 109,
+ 111,
+ 112,
+ 113,
+ 114,
+ 115,
+ 116,
+ 118,
+ 119,
+ 120
+ ],
+ "Scaraba Hospital": [
+ 42,
+ 44,
+ 57,
+ 79,
+ 97,
+ 109,
+ 112,
+ 116
+ ],
+ "Scaraba Shop": [
+ 42,
+ 50,
+ 53,
+ 57,
+ 62,
+ 71,
+ 72,
+ 73,
+ 76,
+ 90,
+ 98,
+ 104,
+ 109,
+ 120
+ ],
+ "Twoson Dept. Store": [
+ 13,
+ 21,
+ 27,
+ 42,
+ 62,
+ 63,
+ 99,
+ 100,
+ 103,
+ 104,
+ 106,
+ 107,
+ 109,
+ 112,
+ 116,
+ 121,
+ 123,
+ 130,
+ 132,
+ 160
+ ],
+ "Fourside Dept. Store": [
+ 13,
+ 21,
+ 62,
+ 100,
+ 107,
+ 109,
+ 121,
+ 123,
+ 160
+ ],
+ "Bakery": [
+ 42,
+ 99,
+ 100,
+ 101,
+ 103,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120,
+ 160
+ ],
+ "Mach Pizza": [
+ 13,
+ 19,
+ 42,
+ 50,
+ 102,
+ 109,
+ 116
+ ],
+ "Burger Shop": [
+ 42,
+ 62,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107,
+ 109,
+ 112,
+ 119,
+ 120
+ ],
+ "Onett Drugstore": [
+ 13,
+ 99,
+ 100,
+ 101,
+ 104,
+ 105,
+ 106,
+ 109,
+ 116,
+ 160
+ ],
+ "Threed Drugstore": [
+ 13,
+ 101,
+ 103,
+ 104,
+ 105,
+ 106,
+ 109,
+ 116,
+ 160
+ ],
+ "Winters Drugstore": [
+ 13,
+ 62,
+ 99,
+ 100,
+ 101,
+ 104,
+ 105,
+ 106,
+ 109,
+ 122,
+ 160
+ ],
+ "Chaos Theater": [
+ 14,
+ 42,
+ 71,
+ 75,
+ 78,
+ 90,
+ 93,
+ 96,
+ 107,
+ 109,
+ 112,
+ 115,
+ 116,
+ 118,
+ 119,
+ 120,
+ 130
+ ],
+ "Fourside Museum": [
+ 42,
+ 108,
+ 109,
+ 116
+ ],
+ "Summers Hotel": [
+ 14,
+ 42,
+ 107,
+ 109,
+ 112,
+ 115,
+ 116,
+ 118,
+ 120
+ ],
+ "Summers Shop": [
+ 14,
+ 42,
+ 107,
+ 109,
+ 110,
+ 112,
+ 114,
+ 115,
+ 116,
+ 118,
+ 120
+ ],
+ "Snow Wood Boarding School": [
+ 14,
+ 42,
+ 57,
+ 71,
+ 90,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120
+ ],
+ "Summers Harbor House": [
+ 42,
+ 107,
+ 109,
+ 112,
+ 114,
+ 115,
+ 116,
+ 120
+ ],
+ "Summers Restaurant": [
+ 14,
+ 42,
+ 74,
+ 75,
+ 78,
+ 90,
+ 93,
+ 107,
+ 109,
+ 112,
+ 113,
+ 115,
+ 116,
+ 118,
+ 119,
+ 120
+ ],
+ "Moonside Museum": [
+ 42,
+ 109,
+ 112,
+ 116,
+ 119,
+ 120
+ ],
+ "Stoic Club": [
+ 14,
+ 41,
+ 42,
+ 57,
+ 70,
+ 71,
+ 75,
+ 78,
+ 90,
+ 93,
+ 95,
+ 96,
+ 107,
+ 109,
+ 111,
+ 112,
+ 114,
+ 115,
+ 116,
+ 117,
+ 118,
+ 119,
+ 120,
+ 130
+ ],
+ "Andonuts Lab": [
+ 14,
+ 41,
+ 42,
+ 57,
+ 75,
+ 78,
+ 90,
+ 93,
+ 107,
+ 109,
+ 112,
+ 116,
+ 118,
+ 119,
+ 120
+ ],
+ "Topolla Theater": [
+ 42,
+ 75,
+ 78,
+ 90,
+ 93,
+ 107,
+ 109,
+ 112,
+ 115,
+ 116,
+ 119,
+ 120
+ ],
+ "Summers Museum": [
+ 42,
+ 78,
+ 90,
+ 107,
+ 109,
+ 112,
+ 115,
+ 120
+ ],
+ "Onett Arcade": [
+ 14,
+ 42,
+ 109,
+ 112,
+ 116,
+ 121
+ ],
+ "Twoson Bus Station": [
+ 42,
+ 50,
+ 51,
+ 62,
+ 67,
+ 71,
+ 86,
+ 104,
+ 118,
+ 122,
+ 123
+ ],
+ "Twoson Bike Shop": [
+ 13,
+ 27,
+ 62,
+ 91,
+ 100,
+ 101,
+ 103,
+ 104,
+ 105,
+ 106,
+ 109,
+ 116,
+ 123
+ ],
+ "Hint Shop": [
+ 14,
+ 42,
+ 47,
+ 51,
+ 63,
+ 79,
+ 80,
+ 91,
+ 93,
+ 94,
+ 107,
+ 108,
+ 109,
+ 110,
+ 112,
+ 114,
+ 115,
+ 116,
+ 118,
+ 120,
+ 122,
+ 124,
+ 133,
+ 159
+ ],
+ "Fourside Dept. Store Rooms": [
+ 62,
+ 90,
+ 95,
+ 96,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 116,
+ 161
+ ],
+ "Threed Tent": [
+ 26,
+ 42,
+ 107,
+ 109,
+ 115,
+ 120,
+ 126
+ ],
+ "Onett Treehouse": [
+ 6,
+ 9,
+ 36,
+ 41,
+ 42,
+ 62,
+ 63,
+ 66,
+ 67,
+ 79,
+ 86,
+ 93,
+ 99,
+ 103,
+ 104,
+ 107,
+ 112,
+ 116,
+ 119,
+ 120,
+ 127,
+ 132,
+ 133
+ ],
+ "Lier's House": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 50,
+ 51,
+ 53,
+ 54,
+ 57,
+ 58,
+ 62,
+ 63,
+ 68,
+ 70,
+ 71,
+ 82,
+ 88,
+ 90,
+ 102,
+ 107,
+ 109,
+ 112,
+ 115,
+ 116,
+ 120,
+ 128,
+ 130,
+ 132
+ ],
+ "Run-down House": [
+ 14,
+ 42,
+ 62,
+ 109,
+ 116,
+ 129
+ ],
+ "Paula's Cabin": [
+ 42,
+ 50,
+ 62,
+ 82,
+ 93,
+ 109,
+ 112,
+ 119,
+ 120
+ ],
+ "Dusty Dunes Drugstore": [
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 62,
+ 102,
+ 107,
+ 109,
+ 112,
+ 131,
+ 132
+ ],
+ "Dusty Dunes Miner Shack": [
+ 38,
+ 39,
+ 41,
+ 42,
+ 43,
+ 44,
+ 50,
+ 53,
+ 57,
+ 58,
+ 62,
+ 68,
+ 70,
+ 71,
+ 74,
+ 75,
+ 76,
+ 82,
+ 90,
+ 102,
+ 107,
+ 109,
+ 112,
+ 116,
+ 120,
+ 132
+ ],
+ "Traveler's Shack": [
+ 42,
+ 63,
+ 109,
+ 112,
+ 115,
+ 133
+ ],
+ "Lumine Hall": [
+ 134,
+ 136,
+ 140
+ ],
+ "Connector Caves": [
+ 0,
+ 33,
+ 34,
+ 110,
+ 134,
+ 135,
+ 136,
+ 137,
+ 140,
+ 141,
+ 145,
+ 149,
+ 150,
+ 153,
+ 154,
+ 156,
+ 158
+ ],
+ "Lumine Hall Sanctuary": [
+ 110,
+ 134,
+ 136,
+ 137,
+ 140,
+ 150,
+ 153
+ ],
+ "Fire Spring": [
+ 134,
+ 136,
+ 137,
+ 140,
+ 150,
+ 153
+ ],
+ "Sea of Eden": [
+ 42,
+ 70,
+ 91,
+ 107,
+ 109,
+ 112,
+ 115,
+ 138
+ ],
+ "Monkey Caves Treasure Rooms": [
+ 139
+ ],
+ "Dungeon Man": [
+ 19,
+ 42,
+ 107,
+ 109,
+ 110,
+ 114,
+ 115,
+ 116,
+ 134,
+ 136,
+ 140,
+ 159
+ ],
+ "Threed Underground": [
+ 1,
+ 21,
+ 71,
+ 75,
+ 78,
+ 79,
+ 80,
+ 85,
+ 95,
+ 97,
+ 107,
+ 108,
+ 109,
+ 110,
+ 112,
+ 114,
+ 115,
+ 119,
+ 120,
+ 141,
+ 154
+ ],
+ "Lier's Cave": [
+ 42,
+ 107,
+ 109,
+ 112,
+ 114,
+ 115,
+ 116,
+ 142,
+ 146,
+ 151,
+ 157,
+ 159
+ ],
+ "Stonehenge Balcony Room": [
+ 5,
+ 19,
+ 21,
+ 42,
+ 51,
+ 54,
+ 57,
+ 62,
+ 63,
+ 80,
+ 102,
+ 107,
+ 109,
+ 112,
+ 115,
+ 120,
+ 143,
+ 144,
+ 146,
+ 147,
+ 148,
+ 157
+ ],
+ "Stonehenge Balcony Off": [
+ 5,
+ 19,
+ 21,
+ 42,
+ 51,
+ 54,
+ 57,
+ 62,
+ 63,
+ 80,
+ 102,
+ 107,
+ 109,
+ 112,
+ 115,
+ 120,
+ 143,
+ 144,
+ 146,
+ 147,
+ 148,
+ 157
+ ],
+ "Brickroad Maze": [
+ 0,
+ 1,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 10,
+ 14,
+ 16,
+ 19,
+ 20,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 33,
+ 41,
+ 42,
+ 43,
+ 45,
+ 47,
+ 53,
+ 57,
+ 59,
+ 61,
+ 62,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 75,
+ 79,
+ 85,
+ 91,
+ 92,
+ 93,
+ 96,
+ 97,
+ 99,
+ 100,
+ 101,
+ 106,
+ 108,
+ 111,
+ 112,
+ 113,
+ 114,
+ 115,
+ 116,
+ 118,
+ 119,
+ 126,
+ 128,
+ 130,
+ 133,
+ 134,
+ 135,
+ 136,
+ 137,
+ 140,
+ 141,
+ 142,
+ 145,
+ 146,
+ 147,
+ 148,
+ 149,
+ 150,
+ 151,
+ 152,
+ 153,
+ 154,
+ 156,
+ 157,
+ 158,
+ 159
+ ],
+ "Rainy Circle": [
+ 42,
+ 107,
+ 109,
+ 112,
+ 114,
+ 115,
+ 146
+ ],
+ "Belch's Factory": [
+ 5,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 38,
+ 42,
+ 51,
+ 52,
+ 54,
+ 63,
+ 80,
+ 88,
+ 89,
+ 102,
+ 107,
+ 109,
+ 112,
+ 115,
+ 118,
+ 120,
+ 143,
+ 144,
+ 146,
+ 147,
+ 157,
+ 160
+ ],
+ "Milky Well Caves": [
+ 14,
+ 38,
+ 41,
+ 42,
+ 57,
+ 71,
+ 75,
+ 78,
+ 93,
+ 107,
+ 109,
+ 112,
+ 115,
+ 119,
+ 120,
+ 146,
+ 148
+ ],
+ "Lilliput Steps": [
+ 9,
+ 26,
+ 41,
+ 42,
+ 57,
+ 71,
+ 75,
+ 78,
+ 79,
+ 80,
+ 85,
+ 86,
+ 93,
+ 97,
+ 107,
+ 108,
+ 109,
+ 112,
+ 115,
+ 116,
+ 118,
+ 119,
+ 120,
+ 140,
+ 146,
+ 148,
+ 149,
+ 157,
+ 158
+ ],
+ "Monkey Caves Hallway": [
+ 42,
+ 62,
+ 79,
+ 107,
+ 109,
+ 110,
+ 112,
+ 115,
+ 116,
+ 120,
+ 133,
+ 134,
+ 136,
+ 137,
+ 140,
+ 142,
+ 146,
+ 150,
+ 151,
+ 153,
+ 157
+ ],
+ "Giant Step": [
+ 14,
+ 22,
+ 25,
+ 42,
+ 53,
+ 62,
+ 79,
+ 80,
+ 107,
+ 109,
+ 110,
+ 112,
+ 115,
+ 116,
+ 124,
+ 129,
+ 134,
+ 136,
+ 137,
+ 140,
+ 142,
+ 146,
+ 150,
+ 151,
+ 153,
+ 157,
+ 159
+ ],
+ "Pink Cloud": [
+ 0,
+ 16,
+ 19,
+ 20,
+ 26,
+ 27,
+ 38,
+ 41,
+ 42,
+ 43,
+ 47,
+ 59,
+ 62,
+ 63,
+ 67,
+ 69,
+ 70,
+ 71,
+ 75,
+ 78,
+ 79,
+ 80,
+ 82,
+ 85,
+ 90,
+ 91,
+ 93,
+ 95,
+ 104,
+ 105,
+ 106,
+ 107,
+ 108,
+ 109,
+ 112,
+ 113,
+ 114,
+ 115,
+ 118,
+ 119,
+ 120,
+ 124,
+ 127,
+ 130,
+ 133,
+ 134,
+ 135,
+ 136,
+ 137,
+ 140,
+ 141,
+ 142,
+ 145,
+ 146,
+ 147,
+ 148,
+ 149,
+ 150,
+ 151,
+ 152,
+ 153,
+ 154,
+ 155,
+ 157,
+ 158,
+ 159
+ ],
+ "Saturn Caves": [
+ 22,
+ 42,
+ 53,
+ 62,
+ 79,
+ 80,
+ 94,
+ 107,
+ 109,
+ 110,
+ 112,
+ 114,
+ 115,
+ 116,
+ 118,
+ 120,
+ 133,
+ 134,
+ 136,
+ 137,
+ 140,
+ 142,
+ 146,
+ 150,
+ 151,
+ 153,
+ 157
+ ],
+ "Stonehenge Tunnels": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 13,
+ 19,
+ 21,
+ 26,
+ 51,
+ 58,
+ 62,
+ 63,
+ 66,
+ 67,
+ 70,
+ 75,
+ 79,
+ 82,
+ 83,
+ 84,
+ 85,
+ 88,
+ 89,
+ 94,
+ 107,
+ 111,
+ 112,
+ 113,
+ 115,
+ 123,
+ 124,
+ 126,
+ 127,
+ 128,
+ 129,
+ 130,
+ 131,
+ 132,
+ 141,
+ 154,
+ 159
+ ],
+ "Gold Mine": [
+ 42,
+ 107,
+ 109,
+ 112,
+ 115,
+ 118,
+ 119,
+ 120,
+ 146,
+ 157
+ ],
+ "Sewer": [
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 16,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 25,
+ 26,
+ 27,
+ 33,
+ 34,
+ 38,
+ 41,
+ 42,
+ 43,
+ 53,
+ 57,
+ 58,
+ 62,
+ 63,
+ 67,
+ 68,
+ 70,
+ 71,
+ 75,
+ 81,
+ 84,
+ 85,
+ 86,
+ 89,
+ 90,
+ 91,
+ 92,
+ 93,
+ 96,
+ 97,
+ 101,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107,
+ 108,
+ 109,
+ 112,
+ 113,
+ 115,
+ 116,
+ 118,
+ 119,
+ 120,
+ 122,
+ 123,
+ 126,
+ 127,
+ 128,
+ 130,
+ 140,
+ 146,
+ 147,
+ 148,
+ 149,
+ 157,
+ 158,
+ 160,
+ 164
+ ],
+ "Tenda Village": [
+ 21,
+ 42,
+ 71,
+ 75,
+ 78,
+ 80,
+ 93,
+ 107,
+ 108,
+ 109,
+ 112,
+ 114,
+ 115,
+ 116,
+ 118,
+ 120,
+ 133,
+ 159
+ ],
+ "Fourside Dept. Store Blackout": [
+ 13,
+ 21,
+ 62,
+ 100,
+ 107,
+ 109,
+ 121,
+ 123,
+ 160
+ ],
+ "Fourside Dept. Store Blackout Rooms": [
+ 42,
+ 50,
+ 62,
+ 104,
+ 112,
+ 116,
+ 161
+ ],
+ "Magicant Manly Fish": [
+ 15,
+ 42,
+ 63,
+ 79,
+ 89,
+ 97,
+ 112,
+ 115,
+ 116,
+ 118,
+ 162,
+ 163,
+ 164,
+ 165,
+ 166,
+ 167
+ ],
+ "Magicant Ness": [
+ 15,
+ 42,
+ 63,
+ 79,
+ 89,
+ 97,
+ 112,
+ 115,
+ 116,
+ 118,
+ 162,
+ 163,
+ 164,
+ 165,
+ 166,
+ 167
+ ],
+ "Magicant Rabbit": [
+ 15,
+ 42,
+ 63,
+ 79,
+ 89,
+ 97,
+ 112,
+ 115,
+ 116,
+ 118,
+ 162,
+ 163,
+ 164,
+ 165,
+ 166,
+ 167
+ ],
+ "Magicant Belch": [
+ 15,
+ 42,
+ 63,
+ 79,
+ 89,
+ 97,
+ 112,
+ 115,
+ 116,
+ 118,
+ 162,
+ 163,
+ 164,
+ 165,
+ 166,
+ 167
+ ],
+ "Magicant Mom": [
+ 15,
+ 42,
+ 63,
+ 79,
+ 89,
+ 97,
+ 112,
+ 115,
+ 116,
+ 118,
+ 162,
+ 163,
+ 164,
+ 165,
+ 166,
+ 167
+ ]
+}
+
+ugly_palettes = {
+ "Pyramid": [
+ 1,
+ 3,
+ 5,
+ 7,
+ 8,
+ 9,
+ 13,
+ 14,
+ 15,
+ 19,
+ 21,
+ 22,
+ 35,
+ 49,
+ 56,
+ 58,
+ 59,
+ 60,
+ 61,
+ 63,
+ 71,
+ 74,
+ 83,
+ 86,
+ 89,
+ 91,
+ 97,
+ 102,
+ 104,
+ 105,
+ 108,
+ 115,
+ 116,
+ 117,
+ 120,
+ 123,
+ 126,
+ 127,
+ 134,
+ 166
+ ],
+ "Lost Underworld": [
+ 0,
+ 8,
+ 14,
+ 15,
+ 21,
+ 33,
+ 34,
+ 38,
+ 41,
+ 42,
+ 50,
+ 53,
+ 58,
+ 59,
+ 63,
+ 65,
+ 69,
+ 82,
+ 83,
+ 87,
+ 90,
+ 95,
+ 97,
+ 104,
+ 105,
+ 118,
+ 120,
+ 123,
+ 126,
+ 127,
+ 128,
+ 129,
+ 131,
+ 136,
+ 140,
+ 159,
+ 160
+ ],
+ "Cave of the Past": [
+ 4,
+ 5,
+ 13,
+ 16,
+ 18,
+ 19,
+ 22,
+ 33,
+ 38,
+ 39,
+ 44,
+ 47,
+ 51,
+ 57,
+ 63,
+ 67,
+ 68,
+ 70,
+ 75,
+ 78,
+ 82,
+ 87,
+ 91,
+ 97,
+ 111,
+ 114,
+ 121,
+ 128,
+ 131,
+ 134,
+ 136,
+ 141,
+ 150,
+ 153,
+ 156,
+ 161
+ ],
+ "Giygas's Lair": [
+ 5,
+ 6,
+ 10,
+ 11,
+ 12,
+ 26,
+ 31,
+ 34,
+ 58,
+ 62,
+ 63,
+ 79,
+ 82,
+ 95,
+ 99,
+ 107,
+ 117,
+ 124,
+ 129,
+ 131,
+ 154
+ ],
+ "Onett": [
+ 0,
+ 1,
+ 2,
+ 6,
+ 8,
+ 26,
+ 27,
+ 38,
+ 41,
+ 43,
+ 51,
+ 53,
+ 59,
+ 61,
+ 62,
+ 63,
+ 70,
+ 82,
+ 83,
+ 91,
+ 92,
+ 93,
+ 99,
+ 104,
+ 108,
+ 112,
+ 120,
+ 126,
+ 130,
+ 132,
+ 133
+ ],
+ "Twoson": [
+ 6,
+ 8,
+ 10,
+ 19,
+ 21,
+ 38,
+ 47,
+ 51,
+ 57,
+ 82,
+ 91,
+ 96,
+ 104
+ ],
+ "Happy-Happy Village": [
+ 10,
+ 19,
+ 53,
+ 61,
+ 63,
+ 89,
+ 107,
+ 109,
+ 112,
+ 116,
+ 124,
+ 130,
+ 133
+ ],
+ "Happy-Happy Clear": [
+ 10,
+ 19,
+ 53,
+ 61,
+ 63,
+ 89,
+ 107,
+ 109,
+ 112,
+ 116,
+ 124,
+ 130,
+ 133
+ ],
+ "Tunnel Outdoors": [
+ 0,
+ 2,
+ 27,
+ 33,
+ 34,
+ 38,
+ 39,
+ 41,
+ 43,
+ 47,
+ 51,
+ 57,
+ 58,
+ 61,
+ 67,
+ 70,
+ 71,
+ 91,
+ 93,
+ 99,
+ 100,
+ 101,
+ 108,
+ 112,
+ 113,
+ 121,
+ 126,
+ 127,
+ 129,
+ 134,
+ 136,
+ 140,
+ 156,
+ 160,
+ 164
+ ],
+ "Threed": [
+ 8,
+ 10,
+ 19,
+ 21,
+ 22,
+ 26,
+ 47,
+ 59,
+ 63,
+ 104,
+ 105,
+ 107,
+ 109,
+ 112,
+ 133
+ ],
+ "Threed Daytime": [
+ 8,
+ 10,
+ 19,
+ 21,
+ 22,
+ 26,
+ 47,
+ 59,
+ 63,
+ 104,
+ 105,
+ 107,
+ 109,
+ 112,
+ 133
+ ],
+ "Fourside": [
+ 7,
+ 8,
+ 21,
+ 22,
+ 53,
+ 54,
+ 57,
+ 59,
+ 62,
+ 104,
+ 105,
+ 106,
+ 107,
+ 131,
+ 132,
+ 133,
+ 162
+ ],
+ "Moonside": [
+ 8,
+ 10,
+ 39,
+ 41,
+ 54,
+ 59,
+ 61,
+ 64,
+ 67,
+ 81,
+ 107,
+ 132,
+ 163,
+ 164
+ ],
+ "Magicant": [
+ 4,
+ 5,
+ 9,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 32,
+ 36,
+ 38,
+ 39,
+ 41,
+ 44,
+ 46,
+ 47,
+ 48,
+ 49,
+ 51,
+ 54,
+ 55,
+ 56,
+ 57,
+ 59,
+ 65,
+ 66,
+ 72,
+ 75,
+ 76,
+ 80,
+ 81,
+ 86,
+ 92,
+ 98,
+ 101,
+ 104,
+ 108,
+ 109,
+ 120,
+ 130,
+ 133
+ ],
+ "Tunnels": [
+ 2,
+ 4,
+ 5,
+ 7,
+ 13,
+ 27,
+ 34,
+ 35,
+ 42,
+ 52,
+ 53,
+ 54,
+ 58,
+ 66,
+ 69,
+ 74,
+ 79,
+ 80,
+ 87,
+ 91,
+ 93,
+ 107,
+ 113,
+ 114,
+ 115,
+ 118,
+ 121,
+ 127,
+ 133,
+ 162,
+ 164
+ ],
+ "Magicant Interiors": [
+ 29,
+ 36,
+ 37,
+ 43,
+ 50,
+ 59,
+ 63,
+ 64,
+ 67,
+ 74,
+ 78,
+ 83,
+ 97,
+ 98,
+ 126
+ ],
+ "Tents": [
+ 4,
+ 5,
+ 6,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 19,
+ 22,
+ 26,
+ 32,
+ 38,
+ 39,
+ 41,
+ 43,
+ 44,
+ 45,
+ 47,
+ 52,
+ 53,
+ 54,
+ 57,
+ 58,
+ 59,
+ 61,
+ 62,
+ 66,
+ 68,
+ 70,
+ 75,
+ 78,
+ 81,
+ 88,
+ 100,
+ 101,
+ 102,
+ 103,
+ 108,
+ 112,
+ 113,
+ 119,
+ 120,
+ 126,
+ 128,
+ 133,
+ 162
+ ],
+ "Peaceful Rest Valley": [
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 25,
+ 38,
+ 39,
+ 47,
+ 59,
+ 61,
+ 79,
+ 91,
+ 101,
+ 105,
+ 107,
+ 109,
+ 112
+ ],
+ "Grapefruit Falls": [
+ 2,
+ 6,
+ 10,
+ 15,
+ 26,
+ 27,
+ 47,
+ 57,
+ 59,
+ 61,
+ 79,
+ 90,
+ 91,
+ 94,
+ 99,
+ 107,
+ 108,
+ 146
+ ],
+ "Saturn Valley": [
+ 8,
+ 10,
+ 19,
+ 61,
+ 62,
+ 89,
+ 105,
+ 107,
+ 109,
+ 112,
+ 164
+ ],
+ "Deep Darkness": [
+ 4,
+ 5,
+ 8,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 26,
+ 27,
+ 41,
+ 44,
+ 61,
+ 63,
+ 70,
+ 71,
+ 82,
+ 106,
+ 107,
+ 109,
+ 115,
+ 129
+ ],
+ "Lilliput Steps Sanctuary": [
+ 10,
+ 51,
+ 57,
+ 58,
+ 71,
+ 92,
+ 94,
+ 107,
+ 109
+ ],
+ "Milky Well": [
+ 10,
+ 11,
+ 19,
+ 51,
+ 58,
+ 61,
+ 71,
+ 82,
+ 107,
+ 146
+ ],
+ "Deep Darkness Darkness": [
+ 25
+ ],
+ "Summers": [
+ 4,
+ 5,
+ 19,
+ 21,
+ 38,
+ 41,
+ 43,
+ 50,
+ 51,
+ 58,
+ 61,
+ 63,
+ 70,
+ 71,
+ 80,
+ 89,
+ 104
+ ],
+ "Dusty Dunes Desert": [
+ 5,
+ 14,
+ 15,
+ 19,
+ 21,
+ 22,
+ 26,
+ 41,
+ 43,
+ 44,
+ 50,
+ 65,
+ 67,
+ 70,
+ 82,
+ 83,
+ 91,
+ 99,
+ 100,
+ 105,
+ 106,
+ 108,
+ 118,
+ 164,
+ 165,
+ 167
+ ],
+ "The Sky": [
+ 30,
+ 63,
+ 64,
+ 136,
+ 164
+ ],
+ "Dalaam": [
+ 4,
+ 5,
+ 9,
+ 13,
+ 36,
+ 47,
+ 48,
+ 50,
+ 56,
+ 64,
+ 65,
+ 66,
+ 68,
+ 70,
+ 73,
+ 78,
+ 81,
+ 82,
+ 89,
+ 105,
+ 112,
+ 126,
+ 160
+ ],
+ "Dalaam Palace": [
+ 0,
+ 1,
+ 2,
+ 19,
+ 21,
+ 22,
+ 25,
+ 36,
+ 42,
+ 59,
+ 62,
+ 63,
+ 67,
+ 68,
+ 69,
+ 70,
+ 75,
+ 82,
+ 85,
+ 86,
+ 90,
+ 92,
+ 93,
+ 94,
+ 95,
+ 116,
+ 126,
+ 131,
+ 140,
+ 141,
+ 160
+ ],
+ "Cave of the Present": [
+ 14,
+ 22,
+ 25,
+ 39,
+ 73,
+ 97,
+ 100,
+ 105,
+ 106,
+ 110,
+ 132
+ ],
+ "Pink Cloud Sanctuary": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 12,
+ 13,
+ 50,
+ 56,
+ 57,
+ 64,
+ 68,
+ 72,
+ 80,
+ 81,
+ 82,
+ 83,
+ 91,
+ 102,
+ 104,
+ 105,
+ 106,
+ 111,
+ 112,
+ 115,
+ 118,
+ 121,
+ 126,
+ 129
+ ],
+ "The Sea": [
+ 6,
+ 41,
+ 44,
+ 63,
+ 70,
+ 75,
+ 78,
+ 79,
+ 86,
+ 90,
+ 115,
+ 120
+ ],
+ "Monotoli Offices": [
+ 9,
+ 11,
+ 13,
+ 14,
+ 19,
+ 21,
+ 27,
+ 36,
+ 37,
+ 40,
+ 43,
+ 44,
+ 47,
+ 48,
+ 52,
+ 54,
+ 55,
+ 64,
+ 65,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 72,
+ 73,
+ 74,
+ 75,
+ 77,
+ 78,
+ 81,
+ 83,
+ 91,
+ 105,
+ 106,
+ 115,
+ 118,
+ 130,
+ 131,
+ 132
+ ],
+ "Onett Hotel": [
+ 13,
+ 27,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 46,
+ 47,
+ 49,
+ 50,
+ 52,
+ 53,
+ 54,
+ 56,
+ 64,
+ 66,
+ 67,
+ 68,
+ 72,
+ 73,
+ 75,
+ 76,
+ 81,
+ 83,
+ 84,
+ 88,
+ 89,
+ 100,
+ 101
+ ],
+ "Threed Red-Wall House": [
+ 47,
+ 54,
+ 67,
+ 68,
+ 69,
+ 83,
+ 102,
+ 105,
+ 106,
+ 107
+ ],
+ "Electra's Room": [
+ 21,
+ 27,
+ 35,
+ 36,
+ 37,
+ 51,
+ 52,
+ 67,
+ 83,
+ 88,
+ 91,
+ 102,
+ 103,
+ 104,
+ 107,
+ 130
+ ],
+ "Threed Green-Wall House": [
+ 14,
+ 40,
+ 47,
+ 51,
+ 67,
+ 88,
+ 107,
+ 115
+ ],
+ "Threed Blue-Wall House": [
+ 13,
+ 21,
+ 27,
+ 68,
+ 82,
+ 83,
+ 93,
+ 99,
+ 100,
+ 104,
+ 105,
+ 119,
+ 130
+ ],
+ "Pokey's House": [
+ 27,
+ 40,
+ 43,
+ 47,
+ 52,
+ 53,
+ 54,
+ 56,
+ 58,
+ 62,
+ 63,
+ 69,
+ 81,
+ 83,
+ 88,
+ 99,
+ 102,
+ 104,
+ 106,
+ 107,
+ 118,
+ 119,
+ 121,
+ 130
+ ],
+ "Moonside Hospital": [
+ 7,
+ 9,
+ 10,
+ 12,
+ 13,
+ 22,
+ 26,
+ 27,
+ 37,
+ 72,
+ 83,
+ 89,
+ 93,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107,
+ 115,
+ 118,
+ 119
+ ],
+ "Onett Green-Wall House": [
+ 13,
+ 14,
+ 27,
+ 47,
+ 54,
+ 67,
+ 68,
+ 69,
+ 74,
+ 81,
+ 82,
+ 91,
+ 99,
+ 104,
+ 106,
+ 107,
+ 119,
+ 126,
+ 130
+ ],
+ "Onett Blue-Wall House": [
+ 27,
+ 37,
+ 39,
+ 40,
+ 47,
+ 50,
+ 52,
+ 54,
+ 56,
+ 58,
+ 67,
+ 68,
+ 74,
+ 82,
+ 89,
+ 90,
+ 91,
+ 115
+ ],
+ "Onett Seafoam-Wall House": [
+ 14,
+ 27,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 49,
+ 52,
+ 53,
+ 57,
+ 58,
+ 67,
+ 70,
+ 71,
+ 74,
+ 75,
+ 78,
+ 79,
+ 81,
+ 83,
+ 88,
+ 90,
+ 91
+ ],
+ "Onett Peach-Wall House": [
+ 14,
+ 27,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 48,
+ 49,
+ 51,
+ 52,
+ 53,
+ 54,
+ 57,
+ 58,
+ 63,
+ 64,
+ 68,
+ 70,
+ 72,
+ 73,
+ 74,
+ 81,
+ 83,
+ 87,
+ 88,
+ 89,
+ 91,
+ 102,
+ 104,
+ 107
+ ],
+ "Onett Light Blue-Wall House": [
+ 14,
+ 27,
+ 37,
+ 45,
+ 69,
+ 88,
+ 91,
+ 115
+ ],
+ "Onett Pink-Wall House": [
+ 14,
+ 27,
+ 36,
+ 37,
+ 67,
+ 74,
+ 80,
+ 82,
+ 83,
+ 89,
+ 91,
+ 104,
+ 105,
+ 106,
+ 107
+ ],
+ "Onett White-Wall House": [
+ 14,
+ 21,
+ 36,
+ 37,
+ 40,
+ 45,
+ 46,
+ 52,
+ 55,
+ 68,
+ 69,
+ 70,
+ 85,
+ 88,
+ 89,
+ 90,
+ 91,
+ 101,
+ 102,
+ 104,
+ 107
+ ],
+ "Hospitals": [
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 22,
+ 38,
+ 39,
+ 43,
+ 50,
+ 54,
+ 70,
+ 91,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106
+ ],
+ "Apple Kid's House": [
+ 4,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 27,
+ 36,
+ 37,
+ 74,
+ 93,
+ 99,
+ 100,
+ 103,
+ 104,
+ 106,
+ 107,
+ 112,
+ 115,
+ 128
+ ],
+ "Tracy's Room": [
+ 14,
+ 27,
+ 39,
+ 40,
+ 47,
+ 48,
+ 67,
+ 69,
+ 74,
+ 80,
+ 81,
+ 82,
+ 83,
+ 88,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103,
+ 104,
+ 105
+ ],
+ "Orange Kid's House": [
+ 14,
+ 27,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 48,
+ 52,
+ 53,
+ 56,
+ 57,
+ 58,
+ 66,
+ 69,
+ 71,
+ 75,
+ 78,
+ 81,
+ 83,
+ 88,
+ 91,
+ 102,
+ 105,
+ 106,
+ 115
+ ],
+ "Everdred's House": [
+ 14,
+ 26,
+ 27,
+ 37,
+ 40,
+ 47,
+ 72,
+ 73,
+ 88,
+ 89,
+ 91,
+ 93,
+ 102,
+ 104,
+ 107,
+ 115,
+ 121
+ ],
+ "Twoson Green-Wall House": [
+ 13,
+ 14,
+ 27,
+ 52,
+ 67,
+ 69,
+ 74,
+ 81,
+ 88,
+ 104,
+ 105,
+ 106
+ ],
+ "Ness's Room": [
+ 14,
+ 27,
+ 36,
+ 40,
+ 47,
+ 54,
+ 55,
+ 67,
+ 70,
+ 72,
+ 73,
+ 74,
+ 82,
+ 88,
+ 90,
+ 102,
+ 107,
+ 115,
+ 119
+ ],
+ "Police Station Backroom": [
+ 2,
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 35,
+ 37,
+ 40,
+ 47,
+ 52,
+ 54,
+ 77,
+ 89,
+ 91,
+ 99,
+ 100,
+ 101,
+ 105,
+ 106,
+ 115,
+ 121,
+ 130
+ ],
+ "Winters": [
+ 8,
+ 11,
+ 15,
+ 19,
+ 20,
+ 26,
+ 27,
+ 32,
+ 37,
+ 38,
+ 39,
+ 41,
+ 47,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 57,
+ 58,
+ 65,
+ 67,
+ 69,
+ 71,
+ 75,
+ 78,
+ 80,
+ 81,
+ 87,
+ 88,
+ 91,
+ 93,
+ 104,
+ 107,
+ 108,
+ 112,
+ 115,
+ 119,
+ 121,
+ 129,
+ 131,
+ 146,
+ 160,
+ 161,
+ 163,
+ 164,
+ 166
+ ],
+ "Rainy Circle Sanctuary": [
+ 4,
+ 18,
+ 32,
+ 35,
+ 36,
+ 37,
+ 38,
+ 39,
+ 41,
+ 44,
+ 46,
+ 47,
+ 52,
+ 53,
+ 56,
+ 57,
+ 58,
+ 64,
+ 65,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 73,
+ 74,
+ 75,
+ 76,
+ 77,
+ 78,
+ 80,
+ 81,
+ 82,
+ 83,
+ 85,
+ 87,
+ 91,
+ 98,
+ 104,
+ 111,
+ 121
+ ],
+ "Hospital Rooms": [
+ 14,
+ 27,
+ 37,
+ 40,
+ 51,
+ 52,
+ 68,
+ 69,
+ 80,
+ 82,
+ 83,
+ 91,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 115,
+ 119
+ ],
+ "Twoson Peach-Wall House": [
+ 14,
+ 27,
+ 36,
+ 37,
+ 40,
+ 47,
+ 52,
+ 54,
+ 55,
+ 67,
+ 68,
+ 70,
+ 73,
+ 74,
+ 82,
+ 83,
+ 88,
+ 89,
+ 91,
+ 98,
+ 102
+ ],
+ "Twoson Pink-Wall House": [
+ 14,
+ 36,
+ 40,
+ 47,
+ 52,
+ 54,
+ 67,
+ 69,
+ 74,
+ 81,
+ 88,
+ 89,
+ 91,
+ 107,
+ 118
+ ],
+ "Twoson Gray-Wall House": [
+ 14,
+ 27,
+ 37,
+ 40,
+ 52,
+ 54,
+ 67,
+ 69,
+ 74,
+ 82,
+ 88,
+ 90,
+ 104,
+ 106,
+ 118,
+ 120
+ ],
+ "Gold Office": [
+ 14,
+ 27,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 52,
+ 53,
+ 54,
+ 56,
+ 58,
+ 66,
+ 68,
+ 69,
+ 70,
+ 74,
+ 89,
+ 107
+ ],
+ "Happy-Happy Guardhouse": [
+ 36,
+ 37,
+ 40,
+ 48,
+ 52,
+ 55,
+ 67,
+ 74,
+ 80,
+ 88,
+ 89,
+ 91,
+ 102,
+ 105,
+ 106,
+ 107
+ ],
+ "Happy-Happy Hospital": [
+ 14,
+ 27,
+ 37,
+ 52,
+ 56,
+ 66,
+ 68,
+ 82,
+ 88,
+ 93,
+ 99,
+ 100,
+ 103,
+ 104,
+ 106,
+ 130
+ ],
+ "Happy-Happy Hospital Clear": [
+ 14,
+ 27,
+ 37,
+ 52,
+ 56,
+ 66,
+ 68,
+ 82,
+ 88,
+ 93,
+ 99,
+ 100,
+ 103,
+ 104,
+ 106,
+ 130
+ ],
+ "Onett Town Hall": [
+ 13,
+ 27,
+ 38,
+ 40,
+ 47,
+ 52,
+ 56,
+ 69,
+ 74,
+ 80,
+ 81,
+ 82,
+ 89,
+ 91,
+ 103
+ ],
+ "Onett Hotel Bedrooms": [
+ 14,
+ 27,
+ 36,
+ 37,
+ 40,
+ 48,
+ 52,
+ 67,
+ 69,
+ 74,
+ 83,
+ 89,
+ 90,
+ 91,
+ 104,
+ 105
+ ],
+ "Fourside Hotel Bedrooms": [
+ 9,
+ 14,
+ 36,
+ 37,
+ 40,
+ 52,
+ 67,
+ 104,
+ 105,
+ 106,
+ 107,
+ 115,
+ 128,
+ 132
+ ],
+ "Happy-Happy Saturn House": [
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 54,
+ 57,
+ 63,
+ 66,
+ 67,
+ 70,
+ 71,
+ 78,
+ 81,
+ 82,
+ 88,
+ 91,
+ 102,
+ 103,
+ 107,
+ 115
+ ],
+ "Happy-Happy Hotel": [
+ 14,
+ 40,
+ 47,
+ 52,
+ 54,
+ 67,
+ 70,
+ 89,
+ 91,
+ 104,
+ 105,
+ 106,
+ 115,
+ 120
+ ],
+ "Twoson Hotel Bedrooms": [
+ 27,
+ 36,
+ 37,
+ 40,
+ 47,
+ 48,
+ 52,
+ 54,
+ 67,
+ 72,
+ 73,
+ 81,
+ 83,
+ 88,
+ 89,
+ 90,
+ 91,
+ 115
+ ],
+ "Threed Hotel Bedrooms": [
+ 14,
+ 40,
+ 47,
+ 52,
+ 67,
+ 74,
+ 104,
+ 106,
+ 115,
+ 116
+ ],
+ "Happy-Happy Hotel Clear": [
+ 14,
+ 40,
+ 47,
+ 52,
+ 54,
+ 67,
+ 70,
+ 89,
+ 91,
+ 104,
+ 105,
+ 106,
+ 115,
+ 120
+ ],
+ "Monotoli Building": [
+ 14,
+ 21,
+ 27,
+ 37,
+ 40,
+ 41,
+ 43,
+ 44,
+ 45,
+ 50,
+ 53,
+ 54,
+ 56,
+ 58,
+ 66,
+ 67,
+ 70,
+ 71,
+ 75,
+ 78,
+ 82,
+ 83,
+ 85,
+ 86,
+ 89,
+ 90,
+ 104,
+ 105,
+ 106,
+ 107,
+ 115
+ ],
+ "Paula's House": [
+ 27,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 50,
+ 53,
+ 56,
+ 57,
+ 62,
+ 64,
+ 65,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 73,
+ 74,
+ 75,
+ 76,
+ 78,
+ 81,
+ 83,
+ 88,
+ 89,
+ 90,
+ 91,
+ 93,
+ 98,
+ 102,
+ 103,
+ 104,
+ 107,
+ 115
+ ],
+ "Paula's Room": [
+ 14,
+ 27,
+ 37,
+ 68,
+ 80,
+ 82,
+ 88,
+ 91,
+ 102,
+ 107
+ ],
+ "Ness's House": [
+ 14,
+ 26,
+ 27,
+ 37,
+ 40,
+ 52,
+ 54,
+ 69,
+ 70,
+ 83,
+ 88,
+ 104,
+ 105,
+ 106,
+ 107,
+ 115,
+ 120
+ ],
+ "Twoson Hotel": [
+ 14,
+ 40,
+ 43,
+ 47,
+ 52,
+ 54,
+ 56,
+ 66,
+ 68,
+ 78,
+ 82,
+ 88,
+ 104,
+ 107
+ ],
+ "Threed Hotel": [
+ 6,
+ 13,
+ 14,
+ 27,
+ 38,
+ 40,
+ 69,
+ 85,
+ 86,
+ 100,
+ 101,
+ 104,
+ 105,
+ 106,
+ 118,
+ 160
+ ],
+ "Fourside Hotel": [
+ 14,
+ 35,
+ 37,
+ 40,
+ 54,
+ 69,
+ 89,
+ 93,
+ 105,
+ 106,
+ 107
+ ],
+ "Monotoli Elevators": [
+ 4,
+ 5,
+ 8,
+ 14,
+ 21,
+ 26,
+ 27,
+ 35,
+ 38,
+ 39,
+ 40,
+ 43,
+ 49,
+ 50,
+ 52,
+ 59,
+ 61,
+ 64,
+ 65,
+ 66,
+ 71,
+ 72,
+ 73,
+ 74,
+ 75,
+ 76,
+ 77,
+ 78,
+ 80,
+ 81,
+ 87,
+ 91,
+ 97,
+ 100,
+ 115,
+ 117,
+ 118,
+ 122,
+ 131,
+ 163,
+ 164,
+ 165
+ ],
+ "Fourside Dept. Store Office": [
+ 14,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 51,
+ 53,
+ 54,
+ 63,
+ 68,
+ 70,
+ 71,
+ 75,
+ 78,
+ 81,
+ 82,
+ 90,
+ 91,
+ 118
+ ],
+ "Onett Library": [
+ 12,
+ 13,
+ 14,
+ 27,
+ 38,
+ 40,
+ 52,
+ 53,
+ 54,
+ 56,
+ 66,
+ 67,
+ 74,
+ 81,
+ 104,
+ 105,
+ 106
+ ],
+ "Paula's House Rooms": [
+ 14,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 50,
+ 52,
+ 54,
+ 57,
+ 58,
+ 62,
+ 67,
+ 68,
+ 70,
+ 71,
+ 74,
+ 75,
+ 78,
+ 81,
+ 83,
+ 102,
+ 104,
+ 105,
+ 106
+ ],
+ "Onett Police Station": [
+ 37,
+ 38,
+ 39,
+ 40,
+ 43,
+ 44,
+ 47,
+ 51,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 67,
+ 68,
+ 69,
+ 70,
+ 74,
+ 80,
+ 81,
+ 83,
+ 88,
+ 89,
+ 91,
+ 93,
+ 99,
+ 100,
+ 103,
+ 114,
+ 115,
+ 117,
+ 121
+ ],
+ "Scaraba": [
+ 5,
+ 7,
+ 9,
+ 10,
+ 14,
+ 19,
+ 21,
+ 22,
+ 26,
+ 41,
+ 44,
+ 51,
+ 52,
+ 53,
+ 54,
+ 57,
+ 58,
+ 62,
+ 67,
+ 68,
+ 74,
+ 93,
+ 104,
+ 121,
+ 130
+ ],
+ "Dalaam House": [
+ 14,
+ 27,
+ 38,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 54,
+ 57,
+ 58,
+ 62,
+ 66,
+ 67,
+ 71,
+ 74,
+ 75,
+ 78,
+ 81,
+ 88,
+ 89,
+ 93,
+ 102,
+ 104,
+ 105,
+ 106
+ ],
+ "Scaraba House": [
+ 41,
+ 44,
+ 50,
+ 56,
+ 62,
+ 66,
+ 67,
+ 71,
+ 75,
+ 76,
+ 78,
+ 81,
+ 91,
+ 98,
+ 115
+ ],
+ "Happy-Happy HQ": [
+ 6,
+ 8,
+ 9,
+ 11,
+ 13,
+ 22,
+ 27,
+ 38,
+ 41,
+ 43,
+ 44,
+ 57,
+ 62,
+ 63,
+ 68,
+ 70,
+ 79,
+ 80,
+ 86,
+ 88,
+ 91,
+ 93,
+ 97,
+ 104,
+ 105,
+ 106,
+ 117,
+ 130,
+ 133
+ ],
+ "Fourside Cafe": [
+ 14,
+ 21,
+ 41,
+ 43,
+ 44,
+ 47,
+ 51,
+ 54,
+ 57,
+ 62,
+ 63,
+ 71,
+ 82,
+ 108
+ ],
+ "Moonside Cafe": [
+ 38,
+ 41,
+ 57,
+ 62,
+ 63,
+ 67,
+ 70,
+ 91
+ ],
+ "Scaraba Hospital": [
+ 26,
+ 38,
+ 41,
+ 47,
+ 54,
+ 56,
+ 58,
+ 62,
+ 63,
+ 66,
+ 67,
+ 71,
+ 75,
+ 78,
+ 80,
+ 82,
+ 85,
+ 86,
+ 90,
+ 107,
+ 115,
+ 120,
+ 130
+ ],
+ "Scaraba Shop": [
+ 26,
+ 38,
+ 41,
+ 44,
+ 47,
+ 54,
+ 56,
+ 63,
+ 64,
+ 74,
+ 78,
+ 106,
+ 107,
+ 116
+ ],
+ "Twoson Dept. Store": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 14,
+ 19,
+ 22,
+ 26,
+ 40,
+ 41,
+ 47,
+ 50,
+ 51,
+ 58,
+ 59,
+ 70,
+ 75,
+ 78,
+ 82,
+ 83,
+ 88,
+ 90,
+ 91,
+ 93,
+ 105,
+ 115,
+ 120,
+ 163
+ ],
+ "Fourside Dept. Store": [
+ 9,
+ 19,
+ 27,
+ 42,
+ 47,
+ 50,
+ 51,
+ 58,
+ 59,
+ 63,
+ 70,
+ 71,
+ 75,
+ 78,
+ 88,
+ 90,
+ 91,
+ 99,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 112,
+ 115,
+ 120
+ ],
+ "Bakery": [
+ 9,
+ 14,
+ 41,
+ 58,
+ 62,
+ 63,
+ 67,
+ 75,
+ 78,
+ 79,
+ 80,
+ 82,
+ 83,
+ 85,
+ 86,
+ 88,
+ 89,
+ 90,
+ 93,
+ 102,
+ 104,
+ 105,
+ 106,
+ 108,
+ 115,
+ 119,
+ 121,
+ 122,
+ 123
+ ],
+ "Mach Pizza": [
+ 14,
+ 21,
+ 27,
+ 40,
+ 41,
+ 58,
+ 62,
+ 70,
+ 71,
+ 75,
+ 90,
+ 91,
+ 100,
+ 103,
+ 105,
+ 106,
+ 107,
+ 112,
+ 120,
+ 132,
+ 160
+ ],
+ "Burger Shop": [
+ 7,
+ 13,
+ 22,
+ 27,
+ 41,
+ 47,
+ 50,
+ 51,
+ 54,
+ 58,
+ 63,
+ 67,
+ 70,
+ 71,
+ 78,
+ 88,
+ 90,
+ 93,
+ 99,
+ 100,
+ 102,
+ 116,
+ 118,
+ 132
+ ],
+ "Onett Drugstore": [
+ 14,
+ 41,
+ 42,
+ 62,
+ 67,
+ 70,
+ 75,
+ 78,
+ 79,
+ 82,
+ 86,
+ 103,
+ 112,
+ 120
+ ],
+ "Threed Drugstore": [
+ 9,
+ 14,
+ 42,
+ 62,
+ 67,
+ 79,
+ 82,
+ 85,
+ 86,
+ 88,
+ 99,
+ 100,
+ 107,
+ 112,
+ 120,
+ 123
+ ],
+ "Winters Drugstore": [
+ 9,
+ 14,
+ 41,
+ 42,
+ 50,
+ 51,
+ 58,
+ 79,
+ 85,
+ 86,
+ 88,
+ 90,
+ 103,
+ 107,
+ 112,
+ 115,
+ 116,
+ 123
+ ],
+ "Chaos Theater": [
+ 38,
+ 41,
+ 43,
+ 57,
+ 88,
+ 114,
+ 121,
+ 133
+ ],
+ "Fourside Museum": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 38,
+ 41,
+ 47,
+ 57,
+ 62,
+ 63,
+ 66,
+ 70,
+ 71,
+ 75,
+ 78,
+ 82,
+ 93,
+ 99,
+ 100,
+ 104,
+ 105,
+ 106,
+ 111,
+ 112,
+ 114,
+ 115,
+ 117,
+ 118,
+ 119,
+ 120,
+ 130
+ ],
+ "Summers Hotel": [
+ 38,
+ 41,
+ 57,
+ 62,
+ 63,
+ 67,
+ 70,
+ 71,
+ 75,
+ 78,
+ 91,
+ 133
+ ],
+ "Summers Shop": [
+ 57,
+ 62,
+ 63,
+ 71,
+ 78,
+ 79,
+ 86,
+ 93,
+ 94,
+ 101,
+ 133
+ ],
+ "Snow Wood Boarding School": [
+ 26,
+ 38,
+ 41,
+ 43,
+ 44,
+ 62,
+ 70,
+ 75,
+ 78,
+ 115,
+ 118,
+ 119
+ ],
+ "Summers Harbor House": [
+ 22,
+ 41,
+ 44,
+ 62,
+ 63,
+ 71,
+ 74,
+ 75,
+ 78,
+ 91,
+ 118,
+ 133
+ ],
+ "Summers Restaurant": [
+ 22,
+ 38,
+ 41,
+ 57,
+ 71,
+ 80,
+ 96,
+ 103,
+ 111,
+ 130,
+ 133
+ ],
+ "Moonside Museum": [
+ 14,
+ 38,
+ 41,
+ 43,
+ 44,
+ 52,
+ 53,
+ 54,
+ 57,
+ 62,
+ 63,
+ 66,
+ 70,
+ 71,
+ 74,
+ 75,
+ 78,
+ 88,
+ 90,
+ 93,
+ 96,
+ 105,
+ 106,
+ 107,
+ 115
+ ],
+ "Stoic Club": [
+ 8,
+ 9,
+ 27,
+ 38,
+ 44,
+ 47,
+ 53,
+ 62,
+ 63,
+ 67,
+ 69,
+ 88,
+ 91,
+ 104,
+ 106,
+ 113
+ ],
+ "Andonuts Lab": [
+ 4,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 21,
+ 26,
+ 38,
+ 43,
+ 47,
+ 53,
+ 62,
+ 63,
+ 69,
+ 70,
+ 71,
+ 91,
+ 100,
+ 103,
+ 104,
+ 105,
+ 106,
+ 121
+ ],
+ "Topolla Theater": [
+ 14,
+ 22,
+ 38,
+ 41,
+ 44,
+ 57,
+ 63,
+ 70,
+ 71,
+ 74,
+ 91,
+ 96,
+ 118
+ ],
+ "Summers Museum": [
+ 8,
+ 9,
+ 14,
+ 21,
+ 38,
+ 41,
+ 44,
+ 57,
+ 62,
+ 71,
+ 75,
+ 88,
+ 116
+ ],
+ "Onett Arcade": [
+ 9,
+ 21,
+ 22,
+ 27,
+ 41,
+ 50,
+ 51,
+ 53,
+ 54,
+ 62,
+ 102,
+ 104,
+ 106,
+ 107,
+ 130
+ ],
+ "Twoson Bus Station": [
+ 22,
+ 63,
+ 66,
+ 79,
+ 90,
+ 99,
+ 100,
+ 103,
+ 105,
+ 106,
+ 109,
+ 116,
+ 119
+ ],
+ "Twoson Bike Shop": [
+ 9,
+ 12,
+ 42,
+ 47,
+ 58,
+ 79,
+ 99,
+ 107,
+ 112,
+ 115,
+ 119,
+ 120,
+ 121,
+ 126,
+ 160
+ ],
+ "Hint Shop": [
+ 4,
+ 5,
+ 11,
+ 12,
+ 26,
+ 27,
+ 38,
+ 40,
+ 41,
+ 43,
+ 44,
+ 52,
+ 54,
+ 57,
+ 58,
+ 62,
+ 66,
+ 67,
+ 71,
+ 74,
+ 75,
+ 81,
+ 84,
+ 85,
+ 97,
+ 99,
+ 101,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 111,
+ 121,
+ 123,
+ 126,
+ 134
+ ],
+ "Fourside Dept. Store Rooms": [
+ 7,
+ 10,
+ 27,
+ 37,
+ 41,
+ 47,
+ 50,
+ 51,
+ 54,
+ 56,
+ 58,
+ 63,
+ 68,
+ 70,
+ 71,
+ 88,
+ 107,
+ 108,
+ 109,
+ 112,
+ 117,
+ 119,
+ 120
+ ],
+ "Threed Tent": [
+ 7,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 51,
+ 59,
+ 68,
+ 80,
+ 93,
+ 99,
+ 100,
+ 104,
+ 105,
+ 106,
+ 112,
+ 118
+ ],
+ "Onett Treehouse": [
+ 19,
+ 21,
+ 22,
+ 35,
+ 37,
+ 38,
+ 43,
+ 44,
+ 51,
+ 54,
+ 56,
+ 58,
+ 64,
+ 65,
+ 68,
+ 70,
+ 71,
+ 74,
+ 78,
+ 83,
+ 84,
+ 85,
+ 89,
+ 91,
+ 92,
+ 98,
+ 100,
+ 102,
+ 105,
+ 106,
+ 108,
+ 109,
+ 118,
+ 123,
+ 128,
+ 131
+ ],
+ "Lier's House": [
+ 11,
+ 14,
+ 27,
+ 36,
+ 44,
+ 47,
+ 48,
+ 52,
+ 64,
+ 65,
+ 66,
+ 67,
+ 69,
+ 72,
+ 73,
+ 74,
+ 75,
+ 76,
+ 77,
+ 78,
+ 80,
+ 81,
+ 83,
+ 89,
+ 103,
+ 104,
+ 105,
+ 106
+ ],
+ "Run-down House": [
+ 38,
+ 43,
+ 44,
+ 47,
+ 50,
+ 51,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 63,
+ 70,
+ 71,
+ 74,
+ 75,
+ 78,
+ 81,
+ 82,
+ 90,
+ 91,
+ 93,
+ 108,
+ 112,
+ 120,
+ 130,
+ 133
+ ],
+ "Paula's Cabin": [
+ 21,
+ 27,
+ 35,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 47,
+ 51,
+ 52,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 63,
+ 66,
+ 71,
+ 80,
+ 81,
+ 88,
+ 90,
+ 91,
+ 98,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107,
+ 115,
+ 116
+ ],
+ "Dusty Dunes Drugstore": [
+ 14,
+ 26,
+ 27,
+ 37,
+ 38,
+ 40,
+ 47,
+ 54,
+ 57,
+ 58,
+ 63,
+ 70,
+ 75,
+ 82,
+ 88,
+ 90,
+ 91,
+ 93,
+ 103,
+ 104,
+ 105,
+ 106,
+ 115,
+ 116,
+ 119,
+ 120
+ ],
+ "Dusty Dunes Miner Shack": [
+ 9,
+ 11,
+ 14,
+ 27,
+ 36,
+ 37,
+ 40,
+ 47,
+ 48,
+ 51,
+ 52,
+ 54,
+ 56,
+ 63,
+ 65,
+ 66,
+ 67,
+ 73,
+ 77,
+ 78,
+ 80,
+ 81,
+ 83,
+ 88,
+ 89,
+ 91,
+ 103,
+ 106,
+ 115,
+ 119,
+ 128,
+ 130
+ ],
+ "Traveler's Shack": [
+ 5,
+ 14,
+ 41,
+ 43,
+ 54,
+ 56,
+ 62,
+ 66,
+ 70,
+ 71,
+ 74,
+ 75,
+ 78,
+ 90,
+ 91,
+ 107,
+ 120,
+ 130
+ ],
+ "Lumine Hall": [
+ 25,
+ 34,
+ 62,
+ 79,
+ 86,
+ 92,
+ 94,
+ 97,
+ 107,
+ 110,
+ 114,
+ 120,
+ 121,
+ 133,
+ 159
+ ],
+ "Connector Caves": [
+ 23,
+ 26,
+ 41,
+ 53,
+ 62,
+ 63,
+ 67,
+ 69,
+ 71,
+ 75,
+ 82,
+ 91,
+ 97,
+ 99,
+ 100,
+ 101,
+ 105,
+ 106,
+ 107,
+ 109,
+ 112,
+ 113,
+ 119,
+ 124,
+ 127,
+ 129,
+ 130,
+ 142,
+ 146,
+ 148,
+ 151,
+ 152,
+ 157,
+ 159
+ ],
+ "Lumine Hall Sanctuary": [
+ 0,
+ 1,
+ 20,
+ 25,
+ 33,
+ 34,
+ 42,
+ 62,
+ 79,
+ 91,
+ 92,
+ 97,
+ 109,
+ 114,
+ 129,
+ 133,
+ 141,
+ 142,
+ 146,
+ 147,
+ 149,
+ 156,
+ 164
+ ],
+ "Fire Spring": [
+ 26,
+ 41,
+ 59,
+ 62,
+ 91,
+ 107,
+ 108,
+ 109,
+ 110,
+ 114,
+ 142,
+ 146,
+ 151,
+ 157,
+ 159
+ ],
+ "Sea of Eden": [
+ 19,
+ 26,
+ 27,
+ 29,
+ 41,
+ 56,
+ 57,
+ 62,
+ 69,
+ 74,
+ 79,
+ 93,
+ 108,
+ 111,
+ 118,
+ 120,
+ 163,
+ 164
+ ],
+ "Monkey Caves Treasure Rooms": [
+ 0,
+ 31,
+ 42,
+ 56,
+ 67,
+ 92,
+ 94,
+ 97,
+ 107,
+ 111,
+ 114,
+ 116,
+ 127,
+ 134,
+ 136,
+ 140,
+ 141,
+ 154,
+ 160,
+ 162
+ ],
+ "Dungeon Man": [
+ 0,
+ 1,
+ 4,
+ 5,
+ 12,
+ 21,
+ 22,
+ 33,
+ 41,
+ 43,
+ 47,
+ 59,
+ 62,
+ 67,
+ 70,
+ 94,
+ 112,
+ 118,
+ 126,
+ 137,
+ 141,
+ 147,
+ 150,
+ 153,
+ 154,
+ 157
+ ],
+ "Threed Underground": [
+ 0,
+ 8,
+ 22,
+ 25,
+ 41,
+ 44,
+ 62,
+ 74,
+ 90,
+ 93,
+ 94,
+ 111,
+ 117,
+ 118,
+ 124,
+ 127,
+ 130,
+ 140,
+ 159
+ ],
+ "Lier's Cave": [
+ 14,
+ 16,
+ 19,
+ 22,
+ 38,
+ 41,
+ 43,
+ 53,
+ 56,
+ 57,
+ 58,
+ 59,
+ 62,
+ 63,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 75,
+ 78,
+ 118,
+ 120,
+ 121,
+ 123,
+ 130,
+ 133
+ ],
+ "Stonehenge Balcony Room": [
+ 4,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 22,
+ 26,
+ 38,
+ 40,
+ 41,
+ 43,
+ 50,
+ 52,
+ 53,
+ 56,
+ 58,
+ 66,
+ 68,
+ 70,
+ 71,
+ 75,
+ 78,
+ 81,
+ 88,
+ 89,
+ 91,
+ 93,
+ 103,
+ 105,
+ 106,
+ 160
+ ],
+ "Stonehenge Balcony Off": [
+ 4,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 22,
+ 26,
+ 38,
+ 40,
+ 41,
+ 43,
+ 50,
+ 52,
+ 53,
+ 56,
+ 58,
+ 66,
+ 68,
+ 70,
+ 71,
+ 75,
+ 78,
+ 81,
+ 88,
+ 89,
+ 91,
+ 93,
+ 103,
+ 105,
+ 106,
+ 160
+ ],
+ "Brickroad Maze": [
+ 2,
+ 11,
+ 12,
+ 13,
+ 21,
+ 34,
+ 35,
+ 38,
+ 44,
+ 49,
+ 52,
+ 63,
+ 66,
+ 74,
+ 78,
+ 87,
+ 95,
+ 103,
+ 105,
+ 107,
+ 120,
+ 121,
+ 129,
+ 131,
+ 164
+ ],
+ "Rainy Circle": [
+ 14,
+ 19,
+ 21,
+ 38,
+ 41,
+ 53,
+ 62,
+ 80,
+ 91,
+ 116,
+ 120,
+ 130,
+ 133
+ ],
+ "Belch's Factory": [
+ 4,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 40,
+ 41,
+ 43,
+ 44,
+ 53,
+ 56,
+ 57,
+ 58,
+ 64,
+ 65,
+ 66,
+ 70,
+ 71,
+ 72,
+ 73,
+ 77,
+ 78,
+ 81,
+ 82,
+ 91,
+ 93,
+ 103,
+ 104,
+ 105,
+ 106,
+ 108,
+ 121
+ ],
+ "Milky Well Caves": [
+ 44,
+ 63,
+ 74,
+ 80,
+ 90,
+ 118,
+ 130,
+ 133
+ ],
+ "Lilliput Steps": [
+ 6,
+ 10,
+ 11,
+ 14,
+ 33,
+ 34,
+ 38,
+ 43,
+ 44,
+ 53,
+ 58,
+ 62,
+ 63,
+ 67,
+ 69,
+ 70,
+ 74,
+ 81,
+ 87,
+ 91,
+ 113,
+ 118,
+ 130,
+ 133,
+ 160
+ ],
+ "Monkey Caves Hallway": [
+ 16,
+ 20,
+ 22,
+ 23,
+ 24,
+ 27,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 49,
+ 53,
+ 54,
+ 57,
+ 63,
+ 66,
+ 67,
+ 69,
+ 70,
+ 71,
+ 74,
+ 75,
+ 78,
+ 80,
+ 86,
+ 88,
+ 91,
+ 94,
+ 114,
+ 118,
+ 124,
+ 130,
+ 159
+ ],
+ "Giant Step": [
+ 12,
+ 16,
+ 19,
+ 23,
+ 24,
+ 44,
+ 97,
+ 108,
+ 114,
+ 120
+ ],
+ "Pink Cloud": [
+ 1,
+ 2,
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 13,
+ 21,
+ 22,
+ 23,
+ 33,
+ 34,
+ 40,
+ 44,
+ 45,
+ 53,
+ 56,
+ 57,
+ 58,
+ 68,
+ 81,
+ 87,
+ 94,
+ 99,
+ 100,
+ 101,
+ 110,
+ 117,
+ 122,
+ 123,
+ 126,
+ 129,
+ 156,
+ 160,
+ 164
+ ],
+ "Saturn Caves": [
+ 2,
+ 14,
+ 16,
+ 19,
+ 20,
+ 24,
+ 26,
+ 38,
+ 41,
+ 43,
+ 44,
+ 45,
+ 47,
+ 54,
+ 57,
+ 58,
+ 63,
+ 66,
+ 67,
+ 69,
+ 70,
+ 74,
+ 75,
+ 81,
+ 82,
+ 87,
+ 91,
+ 97,
+ 99,
+ 100,
+ 101,
+ 117,
+ 147,
+ 159
+ ],
+ "Stonehenge Tunnels": [
+ 0,
+ 12,
+ 22,
+ 33,
+ 34,
+ 35,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 45,
+ 47,
+ 50,
+ 53,
+ 54,
+ 56,
+ 57,
+ 59,
+ 69,
+ 71,
+ 78,
+ 81,
+ 86,
+ 90,
+ 91,
+ 92,
+ 93,
+ 95,
+ 97,
+ 100,
+ 101,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 108,
+ 109,
+ 114,
+ 117,
+ 118,
+ 119,
+ 120,
+ 121,
+ 122,
+ 133,
+ 136,
+ 155,
+ 156
+ ],
+ "Gold Mine": [
+ 14,
+ 38,
+ 41,
+ 43,
+ 44,
+ 47,
+ 52,
+ 53,
+ 54,
+ 57,
+ 62,
+ 63,
+ 71,
+ 74,
+ 75,
+ 78,
+ 80,
+ 93,
+ 114,
+ 129,
+ 147
+ ],
+ "Sewer": [
+ 0,
+ 1,
+ 2,
+ 4,
+ 5,
+ 23,
+ 24,
+ 35,
+ 39,
+ 44,
+ 47,
+ 51,
+ 59,
+ 61,
+ 69,
+ 82,
+ 99,
+ 100,
+ 102,
+ 121,
+ 129,
+ 135,
+ 156,
+ 162,
+ 166
+ ],
+ "Tenda Village": [
+ 5,
+ 8,
+ 12,
+ 13,
+ 14,
+ 19,
+ 22,
+ 38,
+ 41,
+ 43,
+ 44,
+ 52,
+ 53,
+ 54,
+ 57,
+ 60,
+ 62,
+ 63,
+ 66,
+ 67,
+ 88,
+ 91,
+ 106,
+ 121,
+ 128,
+ 129,
+ 130,
+ 160,
+ 164
+ ],
+ "Fourside Dept. Store Blackout": [
+ 9,
+ 19,
+ 27,
+ 42,
+ 47,
+ 50,
+ 51,
+ 58,
+ 59,
+ 63,
+ 70,
+ 71,
+ 75,
+ 78,
+ 88,
+ 90,
+ 91,
+ 99,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 112,
+ 115,
+ 120
+ ],
+ "Fourside Dept. Store Blackout Rooms": [
+ 41,
+ 47,
+ 51,
+ 54,
+ 56,
+ 58,
+ 63,
+ 66,
+ 70,
+ 71,
+ 78,
+ 88,
+ 90,
+ 107,
+ 109,
+ 120
+ ],
+ "Magicant Manly Fish": [
+ 4,
+ 5,
+ 9,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 32,
+ 36,
+ 38,
+ 39,
+ 41,
+ 44,
+ 46,
+ 47,
+ 48,
+ 49,
+ 51,
+ 54,
+ 55,
+ 56,
+ 57,
+ 59,
+ 65,
+ 66,
+ 72,
+ 75,
+ 76,
+ 80,
+ 81,
+ 86,
+ 92,
+ 98,
+ 101,
+ 104,
+ 108,
+ 109,
+ 120,
+ 130,
+ 133
+ ],
+ "Magicant Ness": [
+ 4,
+ 5,
+ 9,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 32,
+ 36,
+ 38,
+ 39,
+ 41,
+ 44,
+ 46,
+ 47,
+ 48,
+ 49,
+ 51,
+ 54,
+ 55,
+ 56,
+ 57,
+ 59,
+ 65,
+ 66,
+ 72,
+ 75,
+ 76,
+ 80,
+ 81,
+ 86,
+ 92,
+ 98,
+ 101,
+ 104,
+ 108,
+ 109,
+ 120,
+ 130,
+ 133
+ ],
+ "Magicant Rabbit": [
+ 4,
+ 5,
+ 9,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 32,
+ 36,
+ 38,
+ 39,
+ 41,
+ 44,
+ 46,
+ 47,
+ 48,
+ 49,
+ 51,
+ 54,
+ 55,
+ 56,
+ 57,
+ 59,
+ 65,
+ 66,
+ 72,
+ 75,
+ 76,
+ 80,
+ 81,
+ 86,
+ 92,
+ 98,
+ 101,
+ 104,
+ 108,
+ 109,
+ 120,
+ 130,
+ 133
+ ],
+ "Magicant Belch": [
+ 4,
+ 5,
+ 9,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 32,
+ 36,
+ 38,
+ 39,
+ 41,
+ 44,
+ 46,
+ 47,
+ 48,
+ 49,
+ 51,
+ 54,
+ 55,
+ 56,
+ 57,
+ 59,
+ 65,
+ 66,
+ 72,
+ 75,
+ 76,
+ 80,
+ 81,
+ 86,
+ 92,
+ 98,
+ 101,
+ 104,
+ 108,
+ 109,
+ 120,
+ 130,
+ 133
+ ],
+ "Magicant Mom": [
+ 4,
+ 5,
+ 9,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 32,
+ 36,
+ 38,
+ 39,
+ 41,
+ 44,
+ 46,
+ 47,
+ 48,
+ 49,
+ 51,
+ 54,
+ 55,
+ 56,
+ 57,
+ 59,
+ 65,
+ 66,
+ 72,
+ 75,
+ 76,
+ 80,
+ 81,
+ 86,
+ 92,
+ 98,
+ 101,
+ 104,
+ 108,
+ 109,
+ 120,
+ 130,
+ 133
+ ]
+
+}
+
+nonsense_palettes = {
+ "Pyramid": [
+ 2,
+ 4,
+ 11,
+ 12,
+ 20,
+ 27,
+ 30,
+ 32,
+ 37,
+ 40,
+ 45,
+ 52,
+ 54,
+ 65,
+ 66,
+ 80,
+ 81,
+ 88,
+ 90,
+ 94,
+ 103,
+ 106,
+ 121,
+ 122,
+ 140,
+ 141,
+ 154,
+ 162,
+ 164,
+ 167
+ ],
+ "Lost Underworld": [
+ 4,
+ 5,
+ 6,
+ 9,
+ 11,
+ 12,
+ 13,
+ 26,
+ 39,
+ 47,
+ 51,
+ 57,
+ 67,
+ 68,
+ 70,
+ 71,
+ 75,
+ 85,
+ 94,
+ 100,
+ 108,
+ 111,
+ 112,
+ 113,
+ 119,
+ 163,
+ 164,
+ 166
+ ],
+ "Cave of the Past": [
+ 0,
+ 1,
+ 8,
+ 11,
+ 12,
+ 20,
+ 23,
+ 24,
+ 27,
+ 41,
+ 43,
+ 50,
+ 53,
+ 74,
+ 94,
+ 126,
+ 159,
+ 163
+ ],
+ "Giygas's Lair": [
+ 4,
+ 8,
+ 14,
+ 20,
+ 21,
+ 27,
+ 35,
+ 54,
+ 59,
+ 64,
+ 92,
+ 94,
+ 108,
+ 109,
+ 114,
+ 118,
+ 121,
+ 136,
+ 140,
+ 141,
+ 156
+ ],
+ "Onett": [
+ 34,
+ 39,
+ 47,
+ 57,
+ 58,
+ 65,
+ 67,
+ 68,
+ 69,
+ 74,
+ 75,
+ 111,
+ 114,
+ 117,
+ 118,
+ 121,
+ 129,
+ 131,
+ 160,
+ 161,
+ 162,
+ 164
+ ],
+ "Twoson": [
+ 1,
+ 2,
+ 26,
+ 27,
+ 39,
+ 58,
+ 68,
+ 74,
+ 88,
+ 89,
+ 90,
+ 93,
+ 99
+ ],
+ "Happy-Happy Village": [
+ 27,
+ 38,
+ 39,
+ 41,
+ 58,
+ 88,
+ 91,
+ 106,
+ 121,
+ 160
+ ],
+ "Happy-Happy Clear": [
+ 27,
+ 38,
+ 39,
+ 41,
+ 58,
+ 88,
+ 91,
+ 106,
+ 121,
+ 160
+ ],
+ "Tunnel Outdoors": [
+ 44,
+ 53,
+ 68,
+ 74,
+ 85,
+ 94,
+ 114,
+ 128,
+ 159
+ ],
+ "Threed": [
+ 27,
+ 38,
+ 39,
+ 41,
+ 43,
+ 44,
+ 51,
+ 61,
+ 67,
+ 68,
+ 82,
+ 88,
+ 91,
+ 99,
+ 102,
+ 121,
+ 126,
+ 160,
+ 164
+ ],
+ "Threed Daytime": [
+ 27,
+ 38,
+ 39,
+ 41,
+ 43,
+ 44,
+ 51,
+ 61,
+ 67,
+ 68,
+ 82,
+ 88,
+ 91,
+ 99,
+ 102,
+ 121,
+ 126,
+ 160,
+ 164
+ ],
+ "Fourside": [
+ 19,
+ 26,
+ 27,
+ 32,
+ 38,
+ 39,
+ 40,
+ 41,
+ 47,
+ 50,
+ 51,
+ 52,
+ 61,
+ 63,
+ 67,
+ 68,
+ 71,
+ 74,
+ 75,
+ 81,
+ 91,
+ 100,
+ 103,
+ 112,
+ 121,
+ 130,
+ 160,
+ 163
+ ],
+ "Moonside": [
+ 19,
+ 26,
+ 27,
+ 51,
+ 52,
+ 53,
+ 56,
+ 62,
+ 65,
+ 69,
+ 74,
+ 76,
+ 81,
+ 82,
+ 88,
+ 91,
+ 99,
+ 100,
+ 112,
+ 121,
+ 126,
+ 130,
+ 133,
+ 166
+ ],
+ "Magicant": [
+ 7,
+ 8,
+ 10,
+ 12,
+ 14,
+ 27,
+ 37,
+ 43,
+ 50,
+ 79,
+ 82,
+ 99,
+ 100,
+ 103,
+ 107,
+ 121,
+ 122,
+ 123,
+ 129,
+ 131,
+ 146,
+ 160
+ ],
+ "Tunnels": [
+ 6,
+ 15,
+ 17,
+ 126,
+ 145
+ ],
+ "Magicant Interiors": [
+ 61,
+ 68,
+ 84,
+ 121,
+ 140,
+ 143,
+ 144
+ ],
+ "Tents": [
+ 7,
+ 21,
+ 25,
+ 27,
+ 33,
+ 34,
+ 37,
+ 40,
+ 50,
+ 71,
+ 82,
+ 85,
+ 89,
+ 91,
+ 97,
+ 99,
+ 114,
+ 115,
+ 118,
+ 123,
+ 130
+ ],
+ "Peaceful Rest Valley": [
+ 2,
+ 26,
+ 27,
+ 41,
+ 43,
+ 51,
+ 53,
+ 58,
+ 67,
+ 70,
+ 71,
+ 74,
+ 86,
+ 92,
+ 99,
+ 100,
+ 104,
+ 121,
+ 123,
+ 133,
+ 160
+ ],
+ "Grapefruit Falls": [
+ 38,
+ 39,
+ 41,
+ 43,
+ 58,
+ 67,
+ 71,
+ 74,
+ 89,
+ 93,
+ 117,
+ 121,
+ 123,
+ 124,
+ 129,
+ 136,
+ 140,
+ 151,
+ 159,
+ 160,
+ 164
+ ],
+ "Saturn Valley": [
+ 26,
+ 27,
+ 39,
+ 41,
+ 47,
+ 51,
+ 53,
+ 58,
+ 67,
+ 70,
+ 74,
+ 82,
+ 88,
+ 90,
+ 91,
+ 93,
+ 99,
+ 103,
+ 104,
+ 108,
+ 121,
+ 126,
+ 160,
+ 161
+ ],
+ "Deep Darkness": [
+ 7,
+ 10,
+ 21,
+ 43,
+ 51,
+ 57,
+ 58,
+ 59,
+ 74,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103,
+ 104,
+ 114,
+ 120,
+ 121,
+ 160,
+ 163,
+ 164,
+ 165,
+ 167
+ ],
+ "Lilliput Steps Sanctuary": [
+ 2,
+ 6,
+ 15,
+ 26,
+ 27,
+ 39,
+ 41,
+ 47,
+ 68,
+ 74,
+ 79,
+ 82,
+ 90,
+ 91,
+ 93,
+ 100,
+ 106,
+ 130,
+ 131,
+ 132,
+ 133,
+ 160,
+ 164,
+ 165
+ ],
+ "Milky Well": [
+ 15,
+ 26,
+ 27,
+ 38,
+ 39,
+ 41,
+ 43,
+ 47,
+ 57,
+ 70,
+ 74,
+ 91,
+ 93,
+ 105,
+ 109,
+ 126,
+ 131,
+ 133
+ ],
+ "Deep Darkness Darkness": [
+ 25
+ ],
+ "Summers": [
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 22,
+ 52,
+ 53,
+ 82,
+ 88,
+ 102,
+ 126,
+ 160,
+ 164,
+ 165
+ ],
+ "Dusty Dunes Desert": [
+ 4,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 37,
+ 40,
+ 52,
+ 54,
+ 60,
+ 74,
+ 81,
+ 89,
+ 103,
+ 107,
+ 114,
+ 121,
+ 123,
+ 160,
+ 161,
+ 166
+ ],
+ "The Sky": [
+ 135
+ ],
+ "Dalaam": [
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 15,
+ 19,
+ 21,
+ 26,
+ 27,
+ 39,
+ 40,
+ 41,
+ 43,
+ 52,
+ 63,
+ 74,
+ 88,
+ 90,
+ 91,
+ 98,
+ 99,
+ 100,
+ 106,
+ 107,
+ 121
+ ],
+ "Dalaam Palace": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 26,
+ 27,
+ 33,
+ 34,
+ 35,
+ 37,
+ 66,
+ 71,
+ 79,
+ 80,
+ 91,
+ 99,
+ 100,
+ 101,
+ 104,
+ 105,
+ 106,
+ 107,
+ 108,
+ 109,
+ 110,
+ 111,
+ 112,
+ 113,
+ 114,
+ 115,
+ 117,
+ 118,
+ 119,
+ 120,
+ 136
+ ],
+ "Cave of the Present": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 27,
+ 33,
+ 34,
+ 40,
+ 41,
+ 42,
+ 53,
+ 54,
+ 62,
+ 63,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 79,
+ 86,
+ 88,
+ 90,
+ 91,
+ 93,
+ 94,
+ 99,
+ 101,
+ 104,
+ 107,
+ 109,
+ 112,
+ 134
+ ],
+ "Pink Cloud Sanctuary": [
+ 11,
+ 19,
+ 21,
+ 22,
+ 27,
+ 35,
+ 36,
+ 37,
+ 41,
+ 43,
+ 44,
+ 47,
+ 48,
+ 52,
+ 54,
+ 55,
+ 59,
+ 65,
+ 66,
+ 107
+ ],
+ "The Sea": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 27,
+ 47,
+ 53,
+ 54,
+ 58,
+ 62,
+ 68,
+ 71,
+ 74,
+ 85,
+ 88,
+ 91,
+ 97,
+ 99,
+ 100,
+ 101,
+ 103,
+ 104,
+ 105,
+ 106,
+ 121
+ ],
+ "Monotoli Offices": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 12,
+ 22,
+ 26,
+ 93,
+ 98,
+ 99,
+ 112,
+ 113,
+ 121,
+ 123
+ ],
+ "Onett Hotel": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 19,
+ 21,
+ 22,
+ 26,
+ 80,
+ 91,
+ 99,
+ 104,
+ 105,
+ 106
+ ],
+ "Threed Red-Wall House": [
+ 52,
+ 74,
+ 88,
+ 91,
+ 99,
+ 100,
+ 119,
+ 121,
+ 130
+ ],
+ "Electra's Room": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 22,
+ 26,
+ 96,
+ 121
+ ],
+ "Threed Green-Wall House": [
+ 37,
+ 52,
+ 54,
+ 56,
+ 58,
+ 68,
+ 69,
+ 83,
+ 102,
+ 121
+ ],
+ "Threed Blue-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 19,
+ 22,
+ 26,
+ 37,
+ 59,
+ 80,
+ 91,
+ 96,
+ 106,
+ 121,
+ 126
+ ],
+ "Pokey's House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 59,
+ 68,
+ 91,
+ 105,
+ 114,
+ 117,
+ 126,
+ 133
+ ],
+ "Moonside Hospital": [
+ 4,
+ 5,
+ 8,
+ 11,
+ 19,
+ 21,
+ 59,
+ 98,
+ 99,
+ 100,
+ 111,
+ 114,
+ 121,
+ 123,
+ 126,
+ 130,
+ 133
+ ],
+ "Onett Green-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 26,
+ 52,
+ 59,
+ 96
+ ],
+ "Onett Blue-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 21,
+ 22,
+ 26,
+ 59,
+ 69,
+ 99,
+ 100,
+ 103,
+ 104,
+ 105,
+ 106
+ ],
+ "Onett Seafoam-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 59,
+ 99,
+ 100,
+ 101,
+ 102,
+ 104,
+ 105,
+ 118
+ ],
+ "Onett Peach-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 45,
+ 69,
+ 99,
+ 100,
+ 103,
+ 105,
+ 106
+ ],
+ "Onett Light Blue-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 99,
+ 100,
+ 102,
+ 103,
+ 106
+ ],
+ "Onett Pink-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 26,
+ 59,
+ 99,
+ 100,
+ 102,
+ 103
+ ],
+ "Onett White-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 22,
+ 26,
+ 27,
+ 99,
+ 100,
+ 105,
+ 106,
+ 126
+ ],
+ "Hospitals": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 21,
+ 26,
+ 27,
+ 52,
+ 99,
+ 100
+ ],
+ "Apple Kid's House": [
+ 5,
+ 9,
+ 19,
+ 21,
+ 22,
+ 26,
+ 68,
+ 91
+ ],
+ "Tracy's Room": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 37,
+ 52,
+ 54,
+ 106
+ ],
+ "Orange Kid's House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 99,
+ 100,
+ 104,
+ 118
+ ],
+ "Everdred's House": [
+ 2,
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 48,
+ 52,
+ 54,
+ 59,
+ 69,
+ 74,
+ 80,
+ 81,
+ 99,
+ 100,
+ 103,
+ 105,
+ 106,
+ 123
+ ],
+ "Twoson Green-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 19,
+ 21,
+ 26,
+ 99,
+ 100,
+ 102,
+ 103
+ ],
+ "Ness's Room": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 48,
+ 52,
+ 69,
+ 91,
+ 99
+ ],
+ "Police Station Backroom": [
+ 1
+ ],
+ "Winters": [
+ 3,
+ 6,
+ 40,
+ 43,
+ 68,
+ 74,
+ 89,
+ 90,
+ 99,
+ 100,
+ 102,
+ 103,
+ 111,
+ 123,
+ 133,
+ 148,
+ 157,
+ 167
+ ],
+ "Rainy Circle Sanctuary": [
+ 16,
+ 29,
+ 33,
+ 92,
+ 93
+ ],
+ "Hospital Rooms": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 98,
+ 99,
+ 100
+ ],
+ "Twoson Peach-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 99,
+ 100,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107
+ ],
+ "Twoson Pink-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 48,
+ 98,
+ 99,
+ 100,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106
+ ],
+ "Twoson Gray-Wall House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103,
+ 105
+ ],
+ "Gold Office": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 88,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 121,
+ 126
+ ],
+ "Happy-Happy Guardhouse": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 59,
+ 98,
+ 99,
+ 100,
+ 104
+ ],
+ "Happy-Happy Hospital": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 59,
+ 91
+ ],
+ "Happy-Happy Hospital Clear": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 59,
+ 91
+ ],
+ "Onett Town Hall": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 19,
+ 21,
+ 22,
+ 25,
+ 26,
+ 59,
+ 61,
+ 62,
+ 121,
+ 133
+ ],
+ "Onett Hotel Bedrooms": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 99,
+ 100,
+ 102,
+ 103,
+ 106
+ ],
+ "Fourside Hotel Bedrooms": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 59,
+ 91,
+ 99,
+ 100,
+ 103,
+ 121
+ ],
+ "Happy-Happy Saturn House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 99,
+ 100,
+ 104,
+ 105,
+ 106,
+ 126
+ ],
+ "Happy-Happy Hotel": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 26,
+ 27,
+ 74,
+ 88,
+ 99,
+ 100,
+ 102,
+ 103
+ ],
+ "Twoson Hotel Bedrooms": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 21,
+ 22,
+ 26,
+ 59,
+ 74,
+ 99,
+ 100,
+ 102,
+ 105,
+ 106
+ ],
+ "Threed Hotel Bedrooms": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 59,
+ 99,
+ 100
+ ],
+ "Happy-Happy Hotel Clear": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 26,
+ 27,
+ 74,
+ 88,
+ 99,
+ 100,
+ 102,
+ 103
+ ],
+ "Monotoli Building": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 22,
+ 26,
+ 52,
+ 91,
+ 99,
+ 100,
+ 101,
+ 102,
+ 103
+ ],
+ "Paula's House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 21,
+ 22,
+ 26,
+ 48,
+ 52,
+ 54,
+ 99,
+ 100,
+ 105,
+ 106,
+ 121
+ ],
+ "Paula's Room": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 59,
+ 99,
+ 100,
+ 103,
+ 104,
+ 105,
+ 106,
+ 126
+ ],
+ "Ness's House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 59,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103,
+ 121,
+ 123
+ ],
+ "Twoson Hotel": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 69,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103,
+ 105,
+ 106,
+ 126,
+ 130
+ ],
+ "Threed Hotel": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 19,
+ 21,
+ 22,
+ 26,
+ 59,
+ 91,
+ 164
+ ],
+ "Fourside Hotel": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103
+ ],
+ "Monotoli Elevators": [
+ 96,
+ 121,
+ 126
+ ],
+ "Fourside Dept. Store Office": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 99,
+ 100,
+ 101,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106
+ ],
+ "Onett Library": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 19,
+ 21,
+ 22,
+ 26,
+ 89,
+ 91,
+ 99,
+ 100
+ ],
+ "Paula's House Rooms": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 59,
+ 91,
+ 99,
+ 100,
+ 107
+ ],
+ "Onett Police Station": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 52,
+ 59,
+ 126,
+ 133
+ ],
+ "Scaraba": [
+ 4,
+ 8,
+ 11,
+ 12,
+ 13,
+ 38,
+ 43,
+ 83,
+ 100,
+ 101,
+ 107,
+ 126,
+ 129,
+ 164
+ ],
+ "Dalaam House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 59
+ ],
+ "Scaraba House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 83,
+ 99,
+ 100,
+ 104,
+ 105,
+ 106
+ ],
+ "Happy-Happy HQ": [
+ 2,
+ 4,
+ 5,
+ 7,
+ 10,
+ 12,
+ 19,
+ 25,
+ 26,
+ 33,
+ 34,
+ 40,
+ 45,
+ 47,
+ 49,
+ 52,
+ 53,
+ 54,
+ 58,
+ 59,
+ 61,
+ 66,
+ 67,
+ 69,
+ 81,
+ 82,
+ 87,
+ 92,
+ 99,
+ 100,
+ 101,
+ 103,
+ 121,
+ 122,
+ 123,
+ 126,
+ 129,
+ 160,
+ 164,
+ 166
+ ],
+ "Fourside Cafe": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 19,
+ 22,
+ 26,
+ 27,
+ 35,
+ 40,
+ 58,
+ 91,
+ 99,
+ 100,
+ 103,
+ 104,
+ 105,
+ 106
+ ],
+ "Moonside Cafe": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 43,
+ 44,
+ 47,
+ 52,
+ 54,
+ 59,
+ 100,
+ 103,
+ 104,
+ 105,
+ 106
+ ],
+ "Scaraba Hospital": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 27,
+ 91,
+ 99,
+ 100,
+ 101,
+ 104,
+ 105,
+ 106
+ ],
+ "Scaraba Shop": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 14,
+ 19,
+ 21,
+ 22,
+ 27,
+ 36,
+ 37,
+ 39,
+ 40,
+ 43,
+ 52,
+ 65,
+ 66,
+ 68,
+ 69,
+ 70,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103,
+ 112,
+ 115
+ ],
+ "Twoson Dept. Store": [
+ 56,
+ 66,
+ 67,
+ 69,
+ 97,
+ 108,
+ 161,
+ 162,
+ 164
+ ],
+ "Fourside Dept. Store": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 26,
+ 41
+ ],
+ "Bakery": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 27,
+ 47,
+ 50,
+ 51,
+ 54,
+ 56,
+ 91,
+ 126
+ ],
+ "Mach Pizza": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 22,
+ 26,
+ 47,
+ 54,
+ 59,
+ 63,
+ 66,
+ 82,
+ 88,
+ 98,
+ 99,
+ 123
+ ],
+ "Burger Shop": [
+ 4,
+ 5,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 19,
+ 21,
+ 26,
+ 80,
+ 82,
+ 83,
+ 89
+ ],
+ "Onett Drugstore": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 19,
+ 21,
+ 22,
+ 27,
+ 47,
+ 60,
+ 63,
+ 68,
+ 91
+ ],
+ "Threed Drugstore": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 19,
+ 21,
+ 22,
+ 27,
+ 41,
+ 47,
+ 51,
+ 63,
+ 70,
+ 78,
+ 90,
+ 91
+ ],
+ "Winters Drugstore": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 19,
+ 21,
+ 22,
+ 25,
+ 27,
+ 47,
+ 59,
+ 60,
+ 61,
+ 63,
+ 68,
+ 70,
+ 75,
+ 78,
+ 80,
+ 82,
+ 83,
+ 91,
+ 121,
+ 126
+ ],
+ "Chaos Theater": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 40,
+ 44,
+ 52,
+ 53,
+ 54,
+ 58,
+ 63,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 74,
+ 81,
+ 82,
+ 91,
+ 99,
+ 100,
+ 103,
+ 104,
+ 105,
+ 106,
+ 123
+ ],
+ "Fourside Museum": [
+ 19,
+ 21,
+ 22,
+ 27,
+ 53,
+ 91
+ ],
+ "Summers Hotel": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 40,
+ 43,
+ 47,
+ 52,
+ 53,
+ 54,
+ 56,
+ 100,
+ 104,
+ 105,
+ 106,
+ 121,
+ 126
+ ],
+ "Summers Shop": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 38,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 53,
+ 54,
+ 58,
+ 66,
+ 68,
+ 69,
+ 70,
+ 74,
+ 75,
+ 88,
+ 91,
+ 99,
+ 100,
+ 105,
+ 106
+ ],
+ "Snow Wood Boarding School": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 27,
+ 50,
+ 51,
+ 52,
+ 53,
+ 58,
+ 59,
+ 63,
+ 81,
+ 82,
+ 88,
+ 91,
+ 99,
+ 100,
+ 105,
+ 106
+ ],
+ "Summers Harbor House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 21,
+ 26,
+ 27,
+ 40,
+ 43,
+ 47,
+ 52,
+ 54,
+ 57,
+ 67,
+ 70,
+ 100,
+ 104,
+ 105,
+ 106,
+ 121,
+ 126,
+ 164
+ ],
+ "Summers Restaurant": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 26,
+ 27,
+ 35,
+ 43,
+ 44,
+ 53,
+ 54,
+ 56,
+ 58,
+ 59,
+ 62,
+ 63,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 81,
+ 82,
+ 88,
+ 91,
+ 99,
+ 100,
+ 104,
+ 105,
+ 106,
+ 108,
+ 117,
+ 126
+ ],
+ "Moonside Museum": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 35,
+ 40,
+ 47,
+ 58,
+ 82,
+ 91,
+ 99,
+ 100,
+ 103,
+ 104
+ ],
+ "Stoic Club": [
+ 4,
+ 5,
+ 7,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 100
+ ],
+ "Andonuts Lab": [
+ 5,
+ 19,
+ 22,
+ 27,
+ 35,
+ 40,
+ 68,
+ 99,
+ 130
+ ],
+ "Topolla Theater": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 26,
+ 27,
+ 35,
+ 40,
+ 43,
+ 47,
+ 53,
+ 56,
+ 58,
+ 62,
+ 68,
+ 69,
+ 80,
+ 81,
+ 82,
+ 88,
+ 99,
+ 100,
+ 104,
+ 105,
+ 106
+ ],
+ "Summers Museum": [
+ 4,
+ 5,
+ 7,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 22,
+ 26,
+ 27,
+ 43,
+ 47,
+ 53,
+ 59,
+ 63,
+ 67,
+ 70,
+ 82,
+ 91,
+ 99,
+ 100,
+ 103,
+ 104,
+ 105,
+ 106,
+ 126,
+ 130,
+ 133
+ ],
+ "Onett Arcade": [
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 26,
+ 39,
+ 40,
+ 43,
+ 57,
+ 58,
+ 59,
+ 63,
+ 70,
+ 71,
+ 78,
+ 80,
+ 82,
+ 88,
+ 89,
+ 90,
+ 91,
+ 99,
+ 100,
+ 103
+ ],
+ "Twoson Bus Station": [
+ 22,
+ 63,
+ 66,
+ 79,
+ 90,
+ 94,
+ 99,
+ 100,
+ 103,
+ 105,
+ 106,
+ 109,
+ 116,
+ 119
+ ],
+ "Twoson Bike Shop": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 19,
+ 21,
+ 22,
+ 41,
+ 63,
+ 70,
+ 71,
+ 75,
+ 78,
+ 82,
+ 90
+ ],
+ "Hint Shop": [
+ 7,
+ 8,
+ 9,
+ 10,
+ 13,
+ 19,
+ 50,
+ 53,
+ 82,
+ 86
+ ],
+ "Fourside Dept. Store Rooms": [
+ 2,
+ 4,
+ 5,
+ 8,
+ 9,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 21,
+ 22,
+ 26,
+ 35,
+ 36,
+ 59,
+ 99,
+ 100
+ ],
+ "Threed Tent": [
+ 4,
+ 5,
+ 8,
+ 19,
+ 21,
+ 22,
+ 27,
+ 40,
+ 41,
+ 63,
+ 88,
+ 89,
+ 91,
+ 102,
+ 121,
+ 160
+ ],
+ "Onett Treehouse": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 11,
+ 12,
+ 13,
+ 14,
+ 26,
+ 53,
+ 80,
+ 90,
+ 101,
+ 130
+ ],
+ "Lier's House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 37,
+ 40,
+ 59,
+ 91,
+ 99,
+ 100
+ ],
+ "Run-down House": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 41,
+ 99,
+ 100,
+ 103,
+ 105,
+ 106,
+ 107,
+ 126
+ ],
+ "Paula's Cabin": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 19,
+ 22,
+ 26,
+ 29,
+ 99,
+ 100,
+ 126
+ ],
+ "Dusty Dunes Drugstore": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 52,
+ 53,
+ 56,
+ 74,
+ 83,
+ 89,
+ 99,
+ 100
+ ],
+ "Dusty Dunes Miner Shack": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 12,
+ 13,
+ 19,
+ 21
+ ],
+ "Traveler's Shack": [
+ 4,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 38,
+ 40,
+ 47,
+ 52,
+ 59,
+ 99,
+ 100,
+ 105,
+ 106
+ ],
+ "Lumine Hall": [
+ 1,
+ 14,
+ 18,
+ 21,
+ 26,
+ 33,
+ 40,
+ 41,
+ 44,
+ 47,
+ 54,
+ 61,
+ 68,
+ 70,
+ 74,
+ 75,
+ 101,
+ 104,
+ 109,
+ 111,
+ 115,
+ 118,
+ 126,
+ 128,
+ 162,
+ 164
+ ],
+ "Connector Caves": [
+ 1,
+ 2,
+ 7,
+ 11,
+ 12,
+ 13,
+ 15,
+ 19,
+ 20,
+ 21,
+ 22,
+ 24,
+ 27,
+ 38,
+ 43,
+ 44,
+ 49,
+ 52,
+ 57,
+ 59,
+ 70,
+ 74,
+ 78,
+ 80,
+ 81,
+ 94,
+ 108,
+ 115,
+ 117,
+ 118,
+ 120,
+ 121,
+ 122,
+ 123,
+ 126,
+ 147
+ ],
+ "Lumine Hall Sanctuary": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 16,
+ 18,
+ 19,
+ 21,
+ 22,
+ 23,
+ 24,
+ 26,
+ 27,
+ 38,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 52,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 63,
+ 67,
+ 68,
+ 69,
+ 70,
+ 74,
+ 75,
+ 78,
+ 81,
+ 82,
+ 83,
+ 85,
+ 86,
+ 87,
+ 88,
+ 90,
+ 93,
+ 99,
+ 100,
+ 101,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107,
+ 108,
+ 112,
+ 115,
+ 117,
+ 118,
+ 119,
+ 120,
+ 121,
+ 126,
+ 128,
+ 162
+ ],
+ "Fire Spring": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 16,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 27,
+ 43,
+ 44,
+ 63,
+ 70,
+ 71,
+ 75,
+ 78,
+ 99,
+ 100,
+ 104,
+ 105,
+ 112,
+ 121
+ ],
+ "Sea of Eden": [
+ 4,
+ 5,
+ 7,
+ 9,
+ 12,
+ 15,
+ 21,
+ 22,
+ 40,
+ 52,
+ 126
+ ],
+ "Monkey Caves Treasure Rooms": [
+ 1,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 33,
+ 34,
+ 35,
+ 36,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 43,
+ 44,
+ 47,
+ 51,
+ 52,
+ 53,
+ 55,
+ 63,
+ 64,
+ 71,
+ 72,
+ 73,
+ 82,
+ 83,
+ 88,
+ 89,
+ 90,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 108,
+ 109,
+ 110,
+ 112,
+ 113,
+ 119,
+ 120,
+ 126,
+ 130,
+ 131,
+ 132,
+ 133
+ ],
+ "Dungeon Man": [
+ 2,
+ 7,
+ 11,
+ 14,
+ 26,
+ 29,
+ 58,
+ 93
+ ],
+ "Threed Underground": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 26,
+ 27,
+ 33,
+ 35,
+ 38,
+ 40,
+ 42,
+ 45,
+ 47,
+ 52,
+ 53,
+ 54,
+ 56,
+ 57,
+ 58,
+ 63,
+ 66,
+ 67,
+ 69,
+ 70,
+ 91,
+ 99,
+ 100,
+ 104,
+ 105,
+ 106,
+ 156
+ ],
+ "Lier's Cave": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 21,
+ 26,
+ 27,
+ 44,
+ 47,
+ 52,
+ 74,
+ 80,
+ 81,
+ 91,
+ 99,
+ 100,
+ 104,
+ 105,
+ 106,
+ 126
+ ],
+ "Stonehenge Balcony Room": [
+ 16,
+ 67,
+ 99,
+ 100,
+ 118,
+ 164
+ ],
+ "Stonehenge Balcony Off": [
+ 16,
+ 67,
+ 99,
+ 100,
+ 118,
+ 164
+ ],
+ "Brickroad Maze": [
+ 18,
+ 54,
+ 58
+ ],
+ "Rainy Circle": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 22,
+ 26,
+ 27,
+ 43,
+ 44,
+ 47,
+ 52,
+ 54,
+ 56,
+ 57,
+ 59,
+ 63,
+ 66,
+ 67,
+ 69,
+ 70,
+ 71,
+ 74,
+ 75,
+ 78,
+ 81,
+ 99,
+ 100,
+ 104,
+ 105,
+ 106,
+ 126,
+ 160
+ ],
+ "Belch's Factory": [
+ 14,
+ 15,
+ 16,
+ 29,
+ 98,
+ 99,
+ 100,
+ 116,
+ 126
+ ],
+ "Milky Well Caves": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 35,
+ 40,
+ 43,
+ 47,
+ 52,
+ 53,
+ 54,
+ 56,
+ 62,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 81,
+ 82,
+ 91,
+ 129
+ ],
+ "Lilliput Steps": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 12,
+ 13,
+ 19,
+ 20,
+ 21,
+ 22,
+ 27,
+ 35,
+ 40,
+ 59,
+ 82,
+ 99,
+ 100,
+ 101,
+ 103,
+ 104,
+ 105,
+ 106,
+ 126
+ ],
+ "Monkey Caves Hallway": [
+ 2,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 38,
+ 99,
+ 100,
+ 101,
+ 104,
+ 106
+ ],
+ "Giant Step": [
+ 1,
+ 2,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 13,
+ 15,
+ 20,
+ 21,
+ 26,
+ 43,
+ 45,
+ 47,
+ 49
+ ],
+ "Pink Cloud": [
+ 6,
+ 35,
+ 74,
+ 121
+ ],
+ "Saturn Caves": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 21,
+ 23,
+ 27,
+ 52,
+ 56,
+ 71,
+ 78,
+ 126
+ ],
+ "Stonehenge Tunnels": [
+ 1,
+ 27,
+ 42,
+ 52,
+ 74,
+ 99
+ ],
+ "Gold Mine": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 16,
+ 19,
+ 21,
+ 22,
+ 26,
+ 27,
+ 40,
+ 56,
+ 66,
+ 67,
+ 88,
+ 91,
+ 100,
+ 103,
+ 104,
+ 105,
+ 106,
+ 117,
+ 123
+ ],
+ "Sewer": [
+ 32,
+ 74,
+ 79,
+ 80,
+ 111
+ ],
+ "Tenda Village": [
+ 4,
+ 7,
+ 11,
+ 15,
+ 26,
+ 27,
+ 29,
+ 32,
+ 40,
+ 47,
+ 56,
+ 58,
+ 65,
+ 68,
+ 74,
+ 92,
+ 99,
+ 102,
+ 104,
+ 105,
+ 123,
+ 126,
+ 162
+ ],
+ "Fourside Dept. Store Blackout": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 10,
+ 11,
+ 12,
+ 26,
+ 41
+ ],
+ "Fourside Dept. Store Blackout Rooms": [
+ 4,
+ 5,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 19,
+ 21,
+ 22,
+ 27,
+ 37,
+ 59,
+ 82,
+ 91,
+ 99,
+ 100,
+ 102,
+ 103,
+ 105,
+ 106
+ ],
+ "Magicant Manly Fish": [
+ 7,
+ 8,
+ 10,
+ 12,
+ 14,
+ 27,
+ 37,
+ 43,
+ 50,
+ 79,
+ 82,
+ 99,
+ 100,
+ 103,
+ 107,
+ 121,
+ 122,
+ 123,
+ 129,
+ 131,
+ 146,
+ 160
+ ],
+ "Magicant Ness": [
+ 7,
+ 8,
+ 10,
+ 12,
+ 14,
+ 27,
+ 37,
+ 43,
+ 50,
+ 79,
+ 82,
+ 99,
+ 100,
+ 103,
+ 107,
+ 121,
+ 122,
+ 123,
+ 129,
+ 131,
+ 146,
+ 160
+ ],
+ "Magicant Rabbit": [
+ 7,
+ 8,
+ 10,
+ 12,
+ 14,
+ 27,
+ 37,
+ 43,
+ 50,
+ 79,
+ 82,
+ 99,
+ 100,
+ 103,
+ 107,
+ 121,
+ 122,
+ 123,
+ 129,
+ 131,
+ 146,
+ 160
+ ],
+ "Magicant Belch": [
+ 7,
+ 8,
+ 10,
+ 12,
+ 14,
+ 27,
+ 37,
+ 43,
+ 50,
+ 79,
+ 82,
+ 99,
+ 100,
+ 103,
+ 107,
+ 121,
+ 122,
+ 123,
+ 129,
+ 131,
+ 146,
+ 160
+ ],
+ "Magicant Mom": [
+ 7,
+ 8,
+ 10,
+ 12,
+ 14,
+ 27,
+ 37,
+ 43,
+ 50,
+ 79,
+ 82,
+ 99,
+ 100,
+ 103,
+ 107,
+ 121,
+ 122,
+ 123,
+ 129,
+ 131,
+ 146,
+ 160
+ ]
+}
diff --git a/worlds/earthbound/game_data/static_location_data.py b/worlds/earthbound/game_data/static_location_data.py
new file mode 100644
index 000000000000..b04d854d108b
--- /dev/null
+++ b/worlds/earthbound/game_data/static_location_data.py
@@ -0,0 +1,1407 @@
+location_ids = {
+ "Onett - Tracy Gift": 0xEB0000,
+ "Onett - Tracy's Room Present": 0xEB0001,
+ "Onett - Hilltop Present": 0xEB0002,
+ "Onett - Meteor Item": 0xEB0003,
+ "Onett - Buzz Buzz": 0xEB0004,
+ "Onett - Mani Mani Statue": 0xEB0005,
+ "Onett - Library Counter": 0xEB0006,
+ "Onett - Library Bookshelf": 0xEB0007,
+ "Onett - Burger Shop Trashcan": 0xEB0008,
+ "Onett - Treehouse Guy": 0xEB0009,
+ "Onett - South Road Present": 0xEB000A,
+ "Onett - Hotel Trashcan": 0xEB000B,
+ "Onett - Arcade Trashcan": 0xEB000C,
+ "Onett - Mayor Pirkle": 0xEB000D,
+ "Onett - Traveling Entertainer": 0xEB000E,
+ "Giant Step - First Cave Present": 0xEB000F,
+ "Giant Step - Floor 2 Cave Present": 0xEB0010,
+ "Giant Step - Floor 3 Present": 0xEB0011,
+ "Twoson - Bike Shop Rental": 0xEB0012,
+ "Twoson - Antique Shop": 0xEB0013,
+ "Twoson - Paula's Room Present": 0xEB0014,
+ "Twoson - Apple Kid Trashcan": 0xEB0015,
+ "Twoson - South of Town Present": 0xEB0016,
+ "Twoson - Orange Kid Donation": 0xEB0017,
+ "Twoson - Apple Kid Invention": 0xEB0018,
+ "Twoson - Apple Kid's Mouse": 0xEB0019,
+ "Twoson - Paula's Mother": 0xEB001A,
+ "Twoson - Everdred Meeting": 0xEB001B,
+ "Twoson - Insignificant Location": 0xEB001C,
+ "Peaceful Rest Valley - Split Hill Present": 0xEB001D,
+ "Peaceful Rest Valley - Hill Nook Present": 0xEB001E,
+ "Peaceful Rest Valley - South of Bridge Present": 0xEB001F,
+ "Peaceful Rest Valley - Dead End Present": 0xEB0020,
+ "Peaceful Rest Valley - River Overlook Present": 0xEB0021,
+ "Peaceful Rest Valley - North Side Present": 0xEB0022,
+ "Happy-Happy Village - Donation Lady": 0xEB0023,
+ "Happy-Happy Village - Right HQ Present": 0xEB0024,
+ "Happy-Happy Village - Left HQ Present": 0xEB0025,
+ "Happy-Happy Village - Prisoner Item": 0xEB0026,
+ "Happy-Happy Village - Prisoner": 0xEB0027,
+ "Happy-Happy Village - Defeat Carpainter": 0xEB0028,
+ "Lilliput Steps - Southwest Pool Present": 0xEB0029,
+ "Lilliput Steps - East Cliff Present": 0xEB002A,
+ "Lilliput Steps - North Stream Present": 0xEB002B,
+ "Threed - Boogey Tent Trashcan": 0xEB002C,
+ "Threed - Cemetery Trashcan": 0xEB002D,
+ "Threed - Downtown Trashcan": 0xEB002E,
+ "Threed - East Side Trashcan": 0xEB002F,
+ "Threed - Northeast Shack Trashcan": 0xEB0030,
+ "Threed - Hospital Drawer": 0xEB0031,
+ "Threed - Zombie Prisoner": 0xEB0032,
+ "Threed Underground - Left Coffin": 0xEB0033,
+ "Threed Underground - Right Coffin": 0xEB0034,
+ "Grapefruit Falls - South Present": 0xEB0035,
+ "Grapefruit Falls - North Present": 0xEB0036,
+ "Grapefruit Falls - Saturn Cave Present": 0xEB0037,
+ "Saturn Valley - Ladder Present": 0xEB0038,
+ "Saturn Valley - Trashcan #1": 0xEB0039,
+ "Saturn Valley - Trashcan #2": 0xEB003A,
+ "Saturn Valley - Trashcan #3": 0xEB003B,
+ "Saturn Valley - Saturn Coffee": 0xEB003C,
+ "Saturn Valley - Post Belch Gift #1": 0xEB003D,
+ "Saturn Valley - Post Belch Gift #2": 0xEB003E,
+ "Saturn Valley - Post Belch Gift #3": 0xEB003F,
+ "Milky Well - Cavern Present": 0xEB0040,
+ "Belch's Factory - Top Right Room Trashcan": 0xEB0041,
+ "Belch's Factory - Pit Room Trashcan #1": 0xEB0042,
+ "Belch's Factory - Pit Room Trashcan #2": 0xEB0043,
+ "Belch's Factory - Balcony Room Trashcan #1": 0xEB0044,
+ "Belch's Factory - Balcony Room Trashcan #2": 0xEB0045,
+ "Belch's Factory - Balcony Room Trashcan #3": 0xEB0046,
+ "Dusty Dunes - Northwest Corner Present": 0xEB0047,
+ "Dusty Dunes - South Side Present": 0xEB0048,
+ "Dusty Dunes - Surrounding Rocks Present": 0xEB0049,
+ "Dusty Dunes - Black Sesame Present": 0xEB004A,
+ "Dusty Dunes - Oasis Present": 0xEB004B,
+ "Dusty Dunes - Northeast Corner Present": 0xEB004C,
+ "Dusty Dunes - North Central Present": 0xEB004D,
+ "Dusty Dunes - Shining Spot": 0xEB004E,
+ "Dusty Dunes - East Peninsula Present": 0xEB004F,
+ "Dusty Dunes - Mine Reward": 0xEB0050,
+ "Snow Wood - Many Present Room Present #1": 0xEB0051,
+ "Snow Wood - Many Present Room Present #2": 0xEB0052,
+ "Snow Wood - Many Present Room Present #3": 0xEB0053,
+ "Snow Wood - Many Present Room Present #4": 0xEB0054,
+ "Snow Wood - Many Present Room Present #5": 0xEB0055,
+ "Snow Wood - Many Present Room Present #6": 0xEB0056,
+ "Snow Wood - Many Present Room Present #7": 0xEB0057,
+ "Scaraba - Snake Bag Salesman": 0xEB0058,
+ "Snow Wood - Upper Right Locker": 0xEB0059,
+ "Snow Wood - Upper Left Locker": 0xEB005A,
+ "Snow Wood - Bottom Right Locker": 0xEB005B,
+ "Snow Wood - Bottom Left Locker": 0xEB005C,
+ "Snow Wood - Maxwell Item": 0xEB005D,
+ "Snow Wood - Bedroom": 0xEB005E,
+ "Winters - Drugstore Saleswoman": 0xEB005F,
+ "Brick Road Maze - Top Path Present": 0xEB0060,
+ "Brick Road Maze - Guarded Present": 0xEB0061,
+ "Brick Road Maze - Out of the Way Present": 0xEB0062,
+ "Brick Road Maze - Alcove Present": 0xEB0063,
+ "Brick Road Maze - Near Exit Present": 0xEB0064,
+ "Rainy Circle - Isolated Present": 0xEB0065,
+ "Rainy Circle - East Cliff Present": 0xEB0066,
+ "Rainy Circle - Near Ropes Present": 0xEB0067,
+ "Andonuts Lab - Present": 0xEB0068,
+ "Andonuts Lab - Mouse": 0xEB0069,
+ "Stonehenge - Purple Maze Present": 0xEB006A,
+ "Stonehenge - Dead End Present": 0xEB006B,
+ "Stonehenge - Near End of the Maze Present": 0xEB006C,
+ "Stonehenge - Bridge Room East Balcony Present": 0xEB006D,
+ "Stonehenge - Bridge Room Lower Present": 0xEB006E,
+ "Stonehenge - Flashing Room Right Path Present": 0xEB006F,
+ "Stonehenge - Flashing Room Center Present": 0xEB0070,
+ "Stonehenge - Flashing Room Upper Present": 0xEB0071,
+ "Stonehenge - Kidnapped Mr. Saturn": 0xEB0072,
+ "Stonehenge - Tony Item": 0xEB0073,
+ "Gold Mine - Mouse Crossroad Present #1": 0xEB0074,
+ "Gold Mine - Mouse Crossroad Present #2": 0xEB0075,
+ "Gold Mine - B1F Lonely Mole Present": 0xEB0076,
+ "Gold Mine - South Hall Present": 0xEB0077,
+ "Gold Mine - South Corner Present": 0xEB0078,
+ "Gold Mine - South Mole Present #1": 0xEB0079,
+ "Gold Mine - South Mole Present #2": 0xEB007A,
+ "Gold Mine - North Crossroad Detour Present": 0xEB007B,
+ "Gold Mine - North Mole Present": 0xEB007C,
+ "Gold Mine - West Mole Present": 0xEB007D,
+ "Gold Mine - B1F Isolated Present": 0xEB007E,
+ "Gold Mine - West Crossroad Detour Present": 0xEB007F,
+ "Gold Mine - B1F Junction Present": 0xEB0080,
+ "Gold Mine - B1F Junction Mole Present": 0xEB0081,
+ "Monkey Caves - 1F Right Chest": 0xEB00F1,
+ "Monkey Caves - 1F Left Chest": 0xEB00F2,
+ "Monkey Caves - West 2F Left Chest": 0xEB00F3,
+ "Monkey Caves - West 2F Right Chest #1": 0xEB00F4,
+ "Monkey Caves - West 2F Right Chest #2": 0xEB00F5,
+ "Monkey Caves - East 2F Left Chest": 0xEB00F6,
+ "Monkey Caves - East 2F Right Chest": 0xEB00F7,
+ "Monkey Caves - East West 3F Right Chest #1": 0xEB00F8,
+ "Monkey Caves - East West 3F Right Chest #2": 0xEB00F9,
+ "Monkey Caves - West End Chest": 0xEB0082,
+ "Monkey Caves - West End Trashcan": 0xEB0083,
+ "Monkey Caves - East End Chest": 0xEB0084,
+ "Monkey Caves - East End Trashcan": 0xEB0085,
+ "Monkey Caves - Bow Monkey Gift": 0xEB0086,
+ "Monkey Caves - Talah Rama Chest #1": 0xEB0087,
+ "Monkey Caves - Talah Rama Chest #2": 0xEB0088,
+ "Monkey Caves - Talah Rama Gift": 0xEB0089,
+ "Monkey Caves - Monkey Power": 0xEB008A,
+ "Fourside - Venus Gift": 0xEB008B,
+ "Fourside - Post-Moonside Delivery": 0xEB008C,
+ "Fourside - Bakery 2F Gift": 0xEB008D,
+ "Moonside - Two Trees Present": 0xEB008E,
+ "Moonside - East Island Present": 0xEB008F,
+ "Moonside - Businessman Present": 0xEB0090,
+ "Moonside - West Island Present": 0xEB0091,
+ "Moonside - Hospital Present": 0xEB0092,
+ "Fourside - Department Store Blackout": 0xEB0093,
+ "Magnet Hill - West Entrance Trashcan": 0xEB0094,
+ "Magnet Hill - First Room Free Door Trashcan": 0xEB0095,
+ "Magnet Hill - First Room Barrel Door Trashcan": 0xEB0096,
+ "Magnet Hill - Second Room Dead End Trashcan": 0xEB0097,
+ "Magnet Hill - Final Room Door Trashcan": 0xEB0098,
+ "Fourside - Magnet Hill Chest": 0xEB0099,
+ "Monotoli Building - One Table Present": 0xEB009A,
+ "Monotoli Building - Two Table Present": 0xEB009B,
+ "Monotoli Building - Electra Gift": 0xEB009C,
+ "Monotoli Building - Monotoli Gift": 0xEB009D,
+ "Monotoli Building - Monotoli Character": 0xEB009E,
+ "Summers - Museum Item": 0xEB009F,
+ "Summers - Magic Cake": 0xEB00A0,
+ "Dalaam - Throne Room Chest #1": 0xEB00A1,
+ "Dalaam - Throne Room Chest #2": 0xEB00A2,
+ "Dalaam - Throne Room Chest #3": 0xEB00A3,
+ "Dalaam - Trial of Mu": 0xEB00A4,
+ "Dalaam - Restaurant Chest #1": 0xEB00A5,
+ "Dalaam - Restaurant Chest #2": 0xEB00A6,
+ "Dalaam - Do Do Guy's House Chest": 0xEB00A7,
+ "Dalaam - Upper House Chest": 0xEB00A8,
+ "Dalaam - Throne Character": 0xEB00A9,
+ "Poo - Starting Item": 0xEB00AA,
+ "Pink Cloud - Three Holes Present": 0xEB00AB,
+ "Pink Cloud - Left Hole Present": 0xEB00AC,
+ "Pink Cloud - Ground Floor Present": 0xEB00AD,
+ "Pyramid - Anteroom Sarcophagus": 0xEB00AE,
+ "Pyramid - Northwest Door Sarcophagus": 0xEB00AF,
+ "Pyramid - Hallway Sarcophagus #1": 0xEB00B0,
+ "Pyramid - Hallway Sarcophagus #2": 0xEB00B1,
+ "Pyramid - Switch Room Sarcophagus": 0xEB00B2,
+ "Pyramid - Pedestal Item": 0xEB00B3,
+ "Pyramid - Way Out Sarcophagus": 0xEB00B4,
+ "Scaraba - Star Master": 0xEB00B5,
+ "Scaraba - Key Holder": 0xEB00B6,
+ "Dungeon Man - 1F Dead End Present": 0xEB00B7,
+ "Dungeon Man - 1F Long Walk Present": 0xEB00B8,
+ "Dungeon Man - 1F Disappointing Present": 0xEB00B9,
+ "Dungeon Man - 1F Opinion Present": 0xEB00BA,
+ "Dungeon Man - 1F No Sign Present": 0xEB00BB,
+ "Dungeon Man - 2F Unnecessary Billboard Present": 0xEB00BC,
+ "Dungeon Man - 2F Dungeon Exploration Present": 0xEB00BD,
+ "Dungeon Man - 2F South Ledge Present": 0xEB00BE,
+ "Dungeon Man - 2F North Alcove Present": 0xEB00BF,
+ "Dungeon Man - 3F Present": 0xEB00C0,
+ "Dungeon Man - 2F Hole Present": 0xEB00C1,
+ "Dungeon Man - 1F Exit Ledge Present": 0xEB00C2,
+ "Deep Darkness - Teleporting Monkey": 0xEB00C3,
+ "Deep Darkness - Crest of Darkness Present": 0xEB00C4,
+ "Deep Darkness - Helicopter Present": 0xEB00C5,
+ "Deep Darkness - Yellow Bird Present": 0xEB00C6,
+ "Deep Darkness - Swamp Present": 0xEB00C7,
+ "Deep Darkness - Corner Present": 0xEB00C8,
+ "Deep Darkness - Alcove Present": 0xEB00C9,
+ "Deep Darkness - North Alcove Truffle": 0xEB00CA,
+ "Deep Darkness - Near Land Truffle": 0xEB00CB,
+ "Deep Darkness - Present Truffle": 0xEB00CC,
+ "Deep Darkness - Village Truffle": 0xEB00CD,
+ "Deep Darkness - Entrance Truffle": 0xEB00CE,
+ "Deep Darkness - Barf Character": 0xEB00CF,
+ "Tenda Village - Trashcan": 0xEB00D0,
+ "Tenda Village - Tenda Tea": 0xEB00D1,
+ "Tenda Village - Tenda Gift": 0xEB00D2,
+ "Tenda Village - Tenda Gift #2": 0xEB00D3,
+ "Lumine Hall - B1F Non-Talkative Rock Present": 0xEB00D4,
+ "Lumine Hall - 1F North Path Present": 0xEB00D5,
+ "Lumine Hall - B1F Thankful Rock Corner Present": 0xEB00D6,
+ "Lumine Hall - B1F Thankful Rock Junction Present": 0xEB00D7,
+ "Lumine Hall - 1F Above Belly Button Present": 0xEB00D8,
+ "Lumine Hall - B1F Belly Button Present": 0xEB00D9,
+ "Lumine Hall - 1F Near Exit Present": 0xEB00DA,
+ "Lumine Hall - 1F Dead End Present": 0xEB00DB,
+ "Lumine Hall - B1F West Alcove Present": 0xEB00DC,
+ "Lost Underworld - Talking Rock": 0xEB00DD,
+ "Lost Underworld - East Present": 0xEB00DE,
+ "Lost Underworld - Northeast Present": 0xEB00DF,
+ "Lost Underworld - Northeast of Tenda Tribe Present": 0xEB00E0,
+ "Lost Underworld - Southwest of Tenda Tribe Present": 0xEB00E1,
+ "Lost Underworld - Evacuation Present": 0xEB00E2,
+ "Fire Spring - 1st Cave Present": 0xEB00E3,
+ "Fire Spring - East Corner Present": 0xEB00E4,
+ "Fire Spring - Volcano Present": 0xEB00E5,
+ "Fire Spring - Lone Cave Present": 0xEB00E6,
+ "Fire Spring - Upper Volcano Present": 0xEB00E7,
+ "Magicant - Ness's Gift": 0xEB00E8,
+ "Magicant - Present Near Ness": 0xEB00E9,
+ "Magicant - Lonely Present": 0xEB00EA,
+ "Magicant - North Present": 0xEB00EB,
+ "Magicant - Hills Present": 0xEB00EC,
+ "Magicant - Town Present": 0xEB00FA,
+ "Magicant - Ness's Nightmare": 0xEB00ED,
+ "Cave of the Present - Star Master": 0xEB00EE,
+ "Cave of the Present - Broken Phase Distorter": 0xEB00EF,
+ "Cave of the Past - Present": 0xEB00F0,
+ ##########################################
+ # Shop checks are denoted by an ID of
+ # 0x1000 | Each shop has 7 allocated ID spaces in memory
+ "Onett Drugstore - Right Counter Slot 1": 0xEB1000,
+ "Onett Drugstore - Right Counter Slot 2": 0xEB1001,
+ "Onett Drugstore - Right Counter Slot 3": 0xEB1002,
+ "Onett Drugstore - Right Counter Slot 4": 0xEB1003,
+ "Onett Drugstore - Right Counter Slot 5": 0xEB1004,
+
+ "Onett Drugstore - Left Counter": 0xEB1007,
+
+ "Summers - Beach Cart": 0xEB100E,
+
+ "Onett Burger Shop - Slot 1": 0xEB1015,
+ "Onett Burger Shop - Slot 2": 0xEB1016,
+ "Onett Burger Shop - Slot 3": 0xEB1017,
+ "Onett Burger Shop - Slot 4": 0xEB1018,
+
+ "Onett Bakery - Slot 1": 0xEB101C,
+ "Onett Bakery - Slot 2": 0xEB101D,
+ "Onett Bakery - Slot 3": 0xEB101E,
+ "Onett Bakery - Slot 4": 0xEB101F,
+
+ "Twoson Department Store Burger Shop - Slot 1": 0xEB1023,
+ "Twoson Department Store Burger Shop - Slot 2": 0xEB1024,
+ "Twoson Department Store Burger Shop - Slot 3": 0xEB1025,
+ "Twoson Department Store Burger Shop - Slot 4": 0xEB1026,
+
+ "Twoson Department Store Bakery - Slot 1": 0xEB102A, # Force This slot needs to always be a local food item
+ "Twoson Department Store Bakery - Slot 2": 0xEB102B,
+ "Twoson Department Store Bakery - Slot 3": 0xEB102C,
+ "Twoson Department Store Bakery - Slot 4": 0xEB102D,
+
+ "Twoson Department Store Top Floor - Right Counter Slot 1": 0xEB1031,
+ "Twoson Department Store Top Floor - Right Counter Slot 2": 0xEB1032,
+ "Twoson Department Store Top Floor - Right Counter Slot 3": 0xEB1033,
+ "Twoson Department Store Top Floor - Right Counter Slot 4": 0xEB1034,
+ "Twoson Department Store Top Floor - Right Counter Slot 5": 0xEB1035,
+ "Twoson Department Store Top Floor - Right Counter Slot 6": 0xEB1036,
+
+ "Twoson Department Store Top Floor - Left Counter Slot 1": 0xEB1038,
+ "Twoson Department Store Top Floor - Left Counter Slot 2": 0xEB1039,
+
+ "Summers - Magic Cake Cart Shop Slot": 0xEB103F,
+
+ "Burglin Park Junk Shop - Slot 1": 0xEB1046,
+ "Burglin Park Junk Shop - Slot 2": 0xEB1047,
+ "Burglin Park Junk Shop - Slot 3": 0xEB1048,
+ "Burglin Park Junk Shop - Slot 4": 0xEB1049,
+ "Burglin Park Junk Shop - Slot 5": 0xEB104A,
+ "Burglin Park Junk Shop - Slot 6": 0xEB104B,
+
+ "Burglin Park Bread Stand - Slot 1": 0xEB105B,
+ "Burglin Park Bread Stand - Slot 2": 0xEB105C,
+ "Burglin Park Bread Stand - Slot 3": 0xEB105D,
+ "Burglin Park Bread Stand - Slot 4": 0xEB105E,
+ "Burglin Park Bread Stand - Slot 5": 0xEB105F,
+ "Burglin Park Bread Stand - Slot 6": 0xEB1060,
+
+ "Burglin Park - Banana Stand": 0xEB1062,
+
+ "Happy-Happy Village Drugstore - Right Counter Slot 1": 0xEB1069,
+ "Happy-Happy Village Drugstore - Right Counter Slot 2": 0xEB106A,
+ "Happy-Happy Village Drugstore - Right Counter Slot 3": 0xEB106B,
+ "Happy-Happy Village Drugstore - Right Counter Slot 4": 0xEB106C,
+ "Happy-Happy Village Drugstore - Right Counter Slot 5": 0xEB106D,
+
+ "Threed Drugstore - Right Counter Slot 1": 0xEB1070,
+ "Threed Drugstore - Right Counter Slot 2": 0xEB1071,
+ "Threed Drugstore - Right Counter Slot 3": 0xEB1072,
+ "Threed Drugstore - Right Counter Slot 4": 0xEB1073,
+ "Threed Drugstore - Right Counter Slot 5": 0xEB1074,
+
+ "Threed Drugstore - Left Counter Slot 1": 0xEB1077,
+ "Threed Drugstore - Left Counter Slot 2": 0xEB1078,
+ "Threed Drugstore - Left Counter Slot 3": 0xEB1079,
+ "Threed Drugstore - Left Counter Slot 4": 0xEB107A,
+ "Threed Drugstore - Left Counter Slot 5": 0xEB107B,
+
+ "Threed - Arms Dealer Slot 1": 0xEB107E,
+ "Threed - Arms Dealer Slot 2": 0xEB107F,
+ "Threed - Arms Dealer Slot 3": 0xEB1080,
+ "Threed - Arms Dealer Slot 4": 0xEB1081,
+
+ "Threed Bakery - Slot 1": 0xEB1085,
+ "Threed Bakery - Slot 2": 0xEB1086,
+ "Threed Bakery - Slot 3": 0xEB1087,
+ "Threed Bakery - Slot 4": 0xEB1088,
+ "Threed Bakery - Slot 5": 0xEB1089,
+ "Threed Bakery - Slot 6": 0xEB108A,
+ "Threed Bakery - Slot 7": 0xEB108B,
+
+ "Scaraba - Expensive Water Guy": 0xEB108C,
+
+ "Winters Drugstore - Slot 1": 0xEB1093,
+ "Winters Drugstore - Slot 2": 0xEB1094,
+ "Winters Drugstore - Slot 3": 0xEB1095,
+ "Winters Drugstore - Slot 4": 0xEB1096,
+ "Winters Drugstore - Slot 5": 0xEB1097,
+ "Winters Drugstore - Slot 6": 0xEB1098,
+ "Winters Drugstore - Slot 7": 0xEB1099,
+
+ "Saturn Valley Shop - Center Saturn Slot 1": 0xEB109A,
+ "Saturn Valley Shop - Center Saturn Slot 2": 0xEB109B,
+ "Saturn Valley Shop - Center Saturn Slot 3": 0xEB109C,
+ "Saturn Valley Shop - Center Saturn Slot 4": 0xEB109D,
+ "Saturn Valley Shop - Center Saturn Slot 5": 0xEB109E,
+
+ "Dusty Dunes Drugstore - Counter Slot 1": 0xEB10A1,
+ "Dusty Dunes Drugstore - Counter Slot 2": 0xEB10A2,
+ "Dusty Dunes Drugstore - Counter Slot 3": 0xEB10A3,
+ "Dusty Dunes Drugstore - Counter Slot 4": 0xEB10A4,
+ "Dusty Dunes Drugstore - Counter Slot 5": 0xEB10A5,
+
+ "Dusty Dunes - Arms Dealer Slot 1": 0xEB10A8,
+ "Dusty Dunes - Arms Dealer Slot 2": 0xEB10A9,
+ "Dusty Dunes - Arms Dealer Slot 3": 0xEB10AA,
+ "Dusty Dunes - Arms Dealer Slot 4": 0xEB10AB,
+
+ "Fourside Bakery - Slot 1": 0xEB10AF,
+ "Fourside Bakery - Slot 2": 0xEB10B0,
+ "Fourside Bakery - Slot 3": 0xEB10B1,
+ "Fourside Bakery - Slot 4": 0xEB10B2, # Force this slot to be repel sandwich in monkey caves mode
+ "Fourside Bakery - Slot 5": 0xEB10B3,
+ "Fourside Bakery - Slot 6": 0xEB10B4,
+
+ "Fourside Department Store - Tool Shop Slot 1": 0xEB10B6,
+ "Fourside Department Store - Tool Shop Slot 2": 0xEB10B7,
+ "Fourside Department Store - Tool Shop Slot 3": 0xEB10B8,
+ "Fourside Department Store - Tool Shop Slot 4": 0xEB10B9,
+ "Fourside Department Store - Tool Shop Slot 5": 0xEB10BA,
+ "Fourside Department Store - Tool Shop Slot 6": 0xEB10BB,
+ "Fourside Department Store - Tool Shop Slot 7": 0xEB10BC, # Force this slot to be ruler in monkey caves mode
+
+ "Fourside Department Store - Shop Shop Slot 1": 0xEB10BD,
+ "Fourside Department Store - Shop Shop Slot 2": 0xEB10BE,
+ "Fourside Department Store - Shop Shop Slot 3": 0xEB10BF, # Force this slot to be Protein drink in monkey caves mod
+ "Fourside Department Store - Shop Shop Slot 4": 0xEB10C0,
+
+ "Fourside Department Store - Food Shop Slot 1": 0xEB10C4,
+ "Fourside Department Store - Food Shop Slot 2": 0xEB10C5,
+ "Fourside Department Store - Food Shop Slot 3": 0xEB10C6,
+ "Fourside Department Store - Food Shop Slot 4": 0xEB10C7,
+ "Fourside Department Store - Food Shop Slot 5": 0xEB10C8, # Force this slot to be Picnic lunch in monkey caves mode
+
+ "Fourside Department Store - 2F Cart Slot 1": 0xEB10CB,
+ "Fourside Department Store - 2F Cart Slot 2": 0xEB10CC,
+ "Fourside Department Store - 2F Cart Slot 3": 0xEB10CD,
+ "Fourside Department Store - 2F Cart Slot 4": 0xEB10CE,
+ "Fourside Department Store - 2F Cart Slot 5": 0xEB10CF,
+ "Fourside Department Store - 2F Cart Slot 6": 0xEB10D0,
+ "Fourside Department Store - 2F Cart Slot 7": 0xEB10D1,
+
+ "Fourside Department Store - Toys Shop Slot 1": 0xEB10D2,
+ "Fourside Department Store - Toys Shop Slot 2": 0xEB10D3,
+ "Fourside Department Store - Toys Shop Slot 3": 0xEB10D4,
+ "Fourside Department Store - Toys Shop Slot 4": 0xEB10D5,
+ "Fourside Department Store - Toys Shop Slot 5": 0xEB10D6,
+ "Fourside Department Store - Toys Shop Slot 6": 0xEB10D7,
+
+ "Fourside Department Store - Sports Shop Slot 1": 0xEB10D9,
+ "Fourside Department Store - Sports Shop Slot 2": 0xEB10DA,
+ "Fourside Department Store - Sports Shop Slot 3": 0xEB10DB,
+ "Fourside Department Store - Sports Shop Slot 4": 0xEB10DC,
+
+ "Fourside Department Store - Burger Shop Slot 1": 0xEB10E0,
+ "Fourside Department Store - Burger Shop Slot 2": 0xEB10E1,
+ "Fourside Department Store - Burger Shop Slot 3": 0xEB10E2,
+ "Fourside Department Store - Burger Shop Slot 4": 0xEB10E3, # Force this slot to be Hamburger
+ "Fourside Department Store - Burger Shop Slot 5": 0xEB10E4,
+
+ "Fourside Department Store - Arms Dealer Slot 1": 0xEB10E7,
+ "Fourside Department Store - Arms Dealer Slot 2": 0xEB10E8,
+ "Fourside Department Store - Arms Dealer Slot 3": 0xEB10E9,
+ "Fourside Department Store - Arms Dealer Slot 4": 0xEB10EA,
+ "Fourside Department Store - Arms Dealer Slot 5": 0xEB10EB,
+
+ "Fourside - Northeast Alley Junk Shop Slot 1": 0xEB10EE,
+ "Fourside - Northeast Alley Junk Shop Slot 2": 0xEB10EF,
+ "Fourside - Northeast Alley Junk Shop Slot 3": 0xEB10F0,
+ "Fourside - Northeast Alley Junk Shop Slot 4": 0xEB10F1,
+
+ "Magicant - Shop Slot 1": 0xEB10F5,
+ "Magicant - Shop Slot 2": 0xEB10F6,
+
+ "Summers - Scam Shop Slot 1": 0xEB1103,
+ "Summers - Scam Shop Slot 2": 0xEB1104,
+ "Summers - Scam Shop Slot 3": 0xEB1105,
+ "Summers - Scam Shop Slot 4": 0xEB1106,
+ "Summers - Scam Shop Slot 5": 0xEB1107,
+ "Summers - Scam Shop Slot 6": 0xEB1108,
+ "Summers - Scam Shop Slot 7": 0xEB1109,
+
+ "Summers Harbor - Shop Slot 1": 0xEB110A,
+ "Summers Harbor - Shop Slot 2": 0xEB110B,
+ "Summers Harbor - Shop Slot 3": 0xEB110C,
+ "Summers Harbor - Shop Slot 4": 0xEB110D,
+ "Summers Harbor - Shop Slot 5": 0xEB110E,
+ "Summers Harbor - Shop Slot 6": 0xEB110F,
+ "Summers Harbor - Shop Slot 7": 0xEB1110,
+
+ "Summers Restaurant - Slot 1": 0xEB1111,
+ "Summers Restaurant - Slot 2": 0xEB1112,
+ "Summers Restaurant - Slot 3": 0xEB1113,
+ "Summers Restaurant - Slot 4": 0xEB1114,
+ "Summers Restaurant - Slot 5": 0xEB1115,
+ "Summers Restaurant - Slot 6": 0xEB1116,
+
+ "Scaraba - Indoors Shop Slot 1": 0xEB1118,
+ "Scaraba - Indoors Shop Slot 2": 0xEB1119,
+ "Scaraba - Indoors Shop Slot 3": 0xEB111A,
+ "Scaraba - Indoors Shop Slot 4": 0xEB111B,
+ "Scaraba - Indoors Shop Slot 5": 0xEB111C,
+ "Scaraba - Indoors Shop Slot 6": 0xEB111D,
+
+ "Scaraba Bazaar - Red Snake Carpet Slot 1": 0xEB1126,
+ "Scaraba Bazaar - Red Snake Carpet Slot 2": 0xEB1127,
+ "Scaraba Bazaar - Red Snake Carpet Slot 3": 0xEB1128,
+
+ "Scaraba Bazaar - Bottom Left Carpet Slot 1": 0xEB112D,
+ "Scaraba Bazaar - Bottom Left Carpet Slot 2": 0xEB112E,
+ "Scaraba Bazaar - Bottom Left Carpet Slot 3": 0xEB112F,
+ "Scaraba Bazaar - Bottom Left Carpet Slot 4": 0xEB1130,
+ "Scaraba Bazaar - Bottom Left Carpet Slot 5": 0xEB1131,
+ "Scaraba Bazaar - Bottom Left Carpet Slot 6": 0xEB1132,
+
+ "Scaraba Hotel - Arms Dealer Slot 1": 0xEB1134,
+ "Scaraba Hotel - Arms Dealer Slot 2": 0xEB1135,
+ "Scaraba Hotel - Arms Dealer Slot 3": 0xEB1136,
+ "Scaraba Hotel - Arms Dealer Slot 4": 0xEB1137,
+
+ "Deep Darkness - Businessman Slot 1": 0xEB113B,
+ "Deep Darkness - Businessman Slot 2": 0xEB113C,
+ "Deep Darkness - Businessman Slot 3": 0xEB113D,
+ "Deep Darkness - Businessman Slot 4": 0xEB113E,
+ "Deep Darkness - Businessman Slot 5": 0xEB113F,
+ "Deep Darkness - Businessman Slot 6": 0xEB1140,
+ "Deep Darkness - Businessman Slot 7": 0xEB1141,
+
+ "Saturn Valley Shop - Post-Belch Saturn Slot 1": 0xEB1157,
+ "Saturn Valley Shop - Post-Belch Saturn Slot 2": 0xEB1158,
+ "Saturn Valley Shop - Post-Belch Saturn Slot 3": 0xEB1159,
+ "Saturn Valley Shop - Post-Belch Saturn Slot 4": 0xEB115A,
+
+ "Scaraba - Southern Camel Shop Slot 1": 0xEB115E,
+ "Scaraba - Southern Camel Shop Slot 2": 0xEB115F,
+ "Scaraba - Southern Camel Shop Slot 3": 0xEB1160,
+ "Scaraba - Southern Camel Shop Slot 4": 0xEB1161,
+ "Scaraba - Southern Camel Shop Slot 5": 0xEB1162,
+ "Scaraba - Southern Camel Shop Slot 6": 0xEB1163,
+ "Scaraba - Southern Camel Shop Slot 7": 0xEB1164,
+
+ "Deep Darkness - Arms Dealer Slot 1": 0xEB1165,
+ "Deep Darkness - Arms Dealer Slot 2": 0xEB1166,
+ "Deep Darkness - Arms Dealer Slot 3": 0xEB1167,
+ "Deep Darkness - Arms Dealer Slot 4": 0xEB1168,
+
+ "Lost Underworld - Tenda Camp Shop Slot 1": 0xEB116C,
+ "Lost Underworld - Tenda Camp Shop Slot 2": 0xEB116D,
+ "Lost Underworld - Tenda Camp Shop Slot 3": 0xEB116E,
+ "Lost Underworld - Tenda Camp Shop Slot 4": 0xEB116F,
+ "Lost Underworld - Tenda Camp Shop Slot 5": 0xEB1170,
+ "Lost Underworld - Tenda Camp Shop Slot 6": 0xEB1171,
+ "Lost Underworld - Tenda Camp Shop Slot 7": 0xEB1172,
+
+ "Happy-Happy Village Drugstore - Left Counter Slot 1": 0xEB117A,
+ "Happy-Happy Village Drugstore - Left Counter Slot 2": 0xEB117B,
+ "Happy-Happy Village Drugstore - Left Counter Slot 3": 0xEB117C,
+ "Happy-Happy Village Drugstore - Left Counter Slot 4": 0xEB117D,
+ "Happy-Happy Village Drugstore - Left Counter Slot 5": 0xEB117E,
+ "Happy-Happy Village Drugstore - Left Counter Slot 6": 0xEB117F,
+ "Happy-Happy Village Drugstore - Left Counter Slot 7": 0xEB1180,
+
+ "Grapefruit Falls - Hiker Shop Slot 1": 0xEB1181,
+ "Grapefruit Falls - Hiker Shop Slot 2": 0xEB1182,
+ "Grapefruit Falls - Hiker Shop Slot 3": 0xEB1183,
+
+ "Saturn Valley Shop - Top Saturn Slot 1": 0xEB1188,
+ "Saturn Valley Shop - Top Saturn Slot 2": 0xEB1189,
+ "Saturn Valley Shop - Top Saturn Slot 3": 0xEB118A,
+ "Saturn Valley Shop - Top Saturn Slot 4": 0xEB118B,
+ "Saturn Valley Shop - Top Saturn Slot 5": 0xEB118C,
+ "Saturn Valley Shop - Top Saturn Slot 6": 0xEB118D,
+ "Saturn Valley Shop - Top Saturn Slot 7": 0xEB118E,
+
+ "Dusty Dunes Drugstore - Left Shop Slot 1": 0xEB118F, # Force this slot to be a wet towel in monkey caves
+ "Dusty Dunes Drugstore - Left Shop Slot 2": 0xEB1190,
+ "Dusty Dunes Drugstore - Left Shop Slot 3": 0xEB1191,
+ "Dusty Dunes Drugstore - Left Shop Slot 4": 0xEB1192,
+ "Dusty Dunes Drugstore - Left Shop Slot 5": 0xEB1193,
+ "Dusty Dunes Drugstore - Left Shop Slot 6": 0xEB1194,
+ "Dusty Dunes Drugstore - Left Shop Slot 7": 0xEB1195,
+
+ "Dusty Dunes - Mine Food Cart Slot 1": 0xEB1196,
+ "Dusty Dunes - Mine Food Cart Slot 2": 0xEB1197,
+ "Dusty Dunes - Mine Food Cart Slot 3": 0xEB1198,
+ "Dusty Dunes - Mine Food Cart Slot 4": 0xEB1199,
+ "Dusty Dunes - Mine Food Cart Slot 5": 0xEB119A,
+ "Dusty Dunes - Mine Food Cart Slot 6": 0xEB119B,
+ "Dusty Dunes - Mine Food Cart Slot 7": 0xEB119C,
+
+ "Moonside Hotel - Shop Slot 1": 0xEB119D,
+ "Moonside Hotel - Shop Slot 2": 0xEB119E,
+ "Moonside Hotel - Shop Slot 3": 0xEB119F,
+ "Moonside Hotel - Shop Slot 4": 0xEB11A0,
+ "Moonside Hotel - Shop Slot 5": 0xEB11A1,
+
+ "Dalaam Restaurant - Slot 1": 0xEB11A4,
+ "Dalaam Restaurant - Slot 2": 0xEB11A5,
+ "Dalaam Restaurant - Slot 3": 0xEB11A6,
+ "Dalaam Restaurant - Slot 4": 0xEB11A7,
+
+ "Scaraba Bazaar - Delicacy Shop Slot 1": 0xEB11AB,
+ "Scaraba Bazaar - Delicacy Shop Slot 2": 0xEB11AC,
+ "Scaraba Bazaar - Delicacy Shop Slot 3": 0xEB11AD,
+ "Scaraba Bazaar - Delicacy Shop Slot 4": 0xEB11AE,
+ "Scaraba Bazaar - Delicacy Shop Slot 5": 0xEB11AF,
+ "Scaraba Bazaar - Delicacy Shop Slot 6": 0xEB11B0,
+ "Scaraba Bazaar - Delicacy Shop Slot 7": 0xEB11B1,
+
+ "Twoson/Scaraba - Shared Condiment Shop Slot 1": 0xEB11B2,
+ "Twoson/Scaraba - Shared Condiment Shop Slot 2": 0xEB11B3,
+ "Twoson/Scaraba - Shared Condiment Shop Slot 3": 0xEB11B4,
+ "Twoson/Scaraba - Shared Condiment Shop Slot 4": 0xEB11B5,
+ "Twoson/Scaraba - Shared Condiment Shop Slot 5": 0xEB11B6,
+ "Twoson/Scaraba - Shared Condiment Shop Slot 6": 0xEB11B7,
+ "Twoson/Scaraba - Shared Condiment Shop Slot 7": 0xEB11B8,
+
+ # Make sure this shop uses the earlier one
+ "Andonuts Lab - Caveman Shop Slot 1": 0xEB11C0,
+ "Andonuts Lab - Caveman Shop Slot 2": 0xEB11C1,
+ "Andonuts Lab - Caveman Shop Slot 3": 0xEB11C2,
+ "Andonuts Lab - Caveman Shop Slot 4": 0xEB11C3,
+ "Andonuts Lab - Caveman Shop Slot 5": 0xEB11C4
+}
+
+
+location_groups = {
+ "Poo's Inventory": {
+ "Poo - Starting Item"
+ },
+
+ "Northern Onett": {
+ "Onett - Tracy Gift",
+ "Onett - Tracy's Room Present",
+ "Onett - Meteor Item",
+ "Onett - Buzz Buzz",
+ "Onett - Mani Mani Statue",
+ "Onett - Hilltop Present"
+ },
+
+ "Onett": {
+ "Onett - Library Counter",
+ "Onett - Library Bookshelf",
+ "Onett - Burger Shop Trashcan",
+ "Onett - Treehouse Guy",
+ "Onett - South Road Present",
+ "Onett - Hotel Trashcan",
+ "Onett - Mayor Pirkle",
+ "Onett - Traveling Entertainer",
+ "Onett - Arcade Trashcan",
+ "Onett Drugstore - Right Counter Slot 1",
+ "Onett Drugstore - Right Counter Slot 2",
+ "Onett Drugstore - Right Counter Slot 3",
+ "Onett Drugstore - Right Counter Slot 4",
+ "Onett Drugstore - Right Counter Slot 5",
+ "Onett Drugstore - Left Counter",
+ "Onett Burger Shop - Slot 1",
+ "Onett Burger Shop - Slot 2",
+ "Onett Burger Shop - Slot 3",
+ "Onett Burger Shop - Slot 4",
+ "Onett Bakery - Slot 1",
+ "Onett Bakery - Slot 2",
+ "Onett Bakery - Slot 3",
+ "Onett Bakery - Slot 4"
+ },
+
+ "Giant Step": {
+ "Giant Step - First Cave Present",
+ "Giant Step - Floor 2 Cave Present",
+ "Giant Step - Floor 3 Present"
+ },
+
+ "Twoson": {
+ "Twoson - Bike Shop Rental",
+ "Twoson - Antique Shop",
+ "Twoson - Paula's Room Present",
+ "Twoson - Apple Kid Trashcan",
+ "Twoson - South of Town Present",
+ "Twoson - Orange Kid Donation",
+ "Twoson - Apple Kid Invention",
+ "Twoson - Apple Kid's Mouse",
+ "Twoson - Paula's Mother",
+ "Twoson - Everdred Meeting",
+ "Twoson - Insignificant Location",
+ "Twoson Department Store Burger Shop - Slot 1",
+ "Twoson Department Store Burger Shop - Slot 2",
+ "Twoson Department Store Burger Shop - Slot 3",
+ "Twoson Department Store Burger Shop - Slot 4",
+ "Twoson Department Store Bakery - Slot 1",
+ "Twoson Department Store Bakery - Slot 2",
+ "Twoson Department Store Bakery - Slot 3",
+ "Twoson Department Store Bakery - Slot 4",
+ "Twoson Department Store Top Floor - Right Counter Slot 1",
+ "Twoson Department Store Top Floor - Right Counter Slot 2",
+ "Twoson Department Store Top Floor - Right Counter Slot 3",
+ "Twoson Department Store Top Floor - Right Counter Slot 4",
+ "Twoson Department Store Top Floor - Right Counter Slot 5",
+ "Twoson Department Store Top Floor - Right Counter Slot 6",
+ "Twoson Department Store Top Floor - Left Counter Slot 1",
+ "Twoson Department Store Top Floor - Left Counter Slot 2",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 1",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 2",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 3",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 4",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 5",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 6",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 7"
+ },
+
+ "Peaceful Rest Valley": {
+ "Peaceful Rest Valley - Split Hill Present",
+ "Peaceful Rest Valley - Hill Nook Present",
+ "Peaceful Rest Valley - South of Bridge Present",
+ "Peaceful Rest Valley - Dead End Present",
+ "Peaceful Rest Valley - River Overlook Present",
+ "Peaceful Rest Valley - North Side Present"
+ },
+
+ "Happy-Happy Village": {
+ "Happy-Happy Village - Donation Lady",
+ "Happy-Happy Village - Prisoner Item",
+ "Happy-Happy Village - Prisoner",
+ "Happy-Happy Village Drugstore - Right Counter Slot 1",
+ "Happy-Happy Village Drugstore - Right Counter Slot 2",
+ "Happy-Happy Village Drugstore - Right Counter Slot 3",
+ "Happy-Happy Village Drugstore - Right Counter Slot 4",
+ "Happy-Happy Village Drugstore - Right Counter Slot 5",
+ "Happy-Happy Village Drugstore - Left Counter Slot 1",
+ "Happy-Happy Village Drugstore - Left Counter Slot 2",
+ "Happy-Happy Village Drugstore - Left Counter Slot 3",
+ "Happy-Happy Village Drugstore - Left Counter Slot 4",
+ "Happy-Happy Village Drugstore - Left Counter Slot 5",
+ "Happy-Happy Village Drugstore - Left Counter Slot 6",
+ "Happy-Happy Village Drugstore - Left Counter Slot 7"
+ },
+
+ "the Happy-Happy HQ": {
+ "Happy-Happy Village - Right HQ Present",
+ "Happy-Happy Village - Left HQ Present",
+ "Happy-Happy Village - Defeat Carpainter"
+ },
+
+ "Lilliput Steps": {
+ "Lilliput Steps - Southwest Pool Present",
+ "Lilliput Steps - East Cliff Present",
+ "Lilliput Steps - North Stream Present"
+ },
+
+ "Threed": {
+ "Threed - Boogey Tent Trashcan",
+ "Threed - Cemetery Trashcan",
+ "Threed - Downtown Trashcan",
+ "Threed - East Side Trashcan",
+ "Threed - Northeast Shack Trashcan",
+ "Threed - Hospital Drawer",
+ "Threed - Zombie Prisoner",
+ "Threed Drugstore - Right Counter Slot 1",
+ "Threed Drugstore - Right Counter Slot 2",
+ "Threed Drugstore - Right Counter Slot 3",
+ "Threed Drugstore - Right Counter Slot 4",
+ "Threed Drugstore - Right Counter Slot 5",
+ "Threed Drugstore - Left Counter Slot 1",
+ "Threed Drugstore - Left Counter Slot 2",
+ "Threed Drugstore - Left Counter Slot 3",
+ "Threed Drugstore - Left Counter Slot 4",
+ "Threed Drugstore - Left Counter Slot 5",
+ "Threed - Arms Dealer Slot 1",
+ "Threed - Arms Dealer Slot 2",
+ "Threed - Arms Dealer Slot 3",
+ "Threed - Arms Dealer Slot 4",
+ "Threed Bakery - Slot 1",
+ "Threed Bakery - Slot 2",
+ "Threed Bakery - Slot 3",
+ "Threed Bakery - Slot 4",
+ "Threed Bakery - Slot 5",
+ "Threed Bakery - Slot 6",
+ "Threed Bakery - Slot 7"
+ },
+
+ "the Threed Underground Passage": {
+ "Threed Underground - Left Coffin",
+ "Threed Underground - Right Coffin"
+ },
+
+ "Grapefruit Falls": {
+ "Grapefruit Falls - South Present",
+ "Grapefruit Falls - North Present",
+ "Grapefruit Falls - Saturn Cave Present",
+ "Grapefruit Falls - Hiker Shop Slot 1",
+ "Grapefruit Falls - Hiker Shop Slot 2",
+ "Grapefruit Falls - Hiker Shop Slot 3"
+ },
+
+ "Saturn Valley": {
+ "Saturn Valley - Ladder Present",
+ "Saturn Valley - Trashcan #1",
+ "Saturn Valley - Trashcan #2",
+ "Saturn Valley - Trashcan #3",
+ "Saturn Valley - Saturn Coffee",
+ "Saturn Valley - Post Belch Gift #1",
+ "Saturn Valley - Post Belch Gift #2",
+ "Saturn Valley - Post Belch Gift #3",
+ "Saturn Valley Shop - Center Saturn Slot 1",
+ "Saturn Valley Shop - Center Saturn Slot 2",
+ "Saturn Valley Shop - Center Saturn Slot 3",
+ "Saturn Valley Shop - Center Saturn Slot 4",
+ "Saturn Valley Shop - Center Saturn Slot 5",
+ "Saturn Valley Shop - Top Saturn Slot 1",
+ "Saturn Valley Shop - Top Saturn Slot 2",
+ "Saturn Valley Shop - Top Saturn Slot 3",
+ "Saturn Valley Shop - Top Saturn Slot 4",
+ "Saturn Valley Shop - Top Saturn Slot 5",
+ "Saturn Valley Shop - Top Saturn Slot 6",
+ "Saturn Valley Shop - Top Saturn Slot 7",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 1",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 2",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 3",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 4"
+ },
+
+ "Milky Well": {
+ "Milky Well - Cavern Present" # this is a very good location group :^)
+ },
+
+ "Belch's Factory": {
+ "Belch's Factory - Top Right Room Trashcan",
+ "Belch's Factory - Pit Room Trashcan #1",
+ "Belch's Factory - Pit Room Trashcan #2",
+ "Belch's Factory - Balcony Room Trashcan #1",
+ "Belch's Factory - Balcony Room Trashcan #2",
+ "Belch's Factory - Balcony Room Trashcan #3"
+ },
+
+ "Dusty Dunes Desert": {
+ "Dusty Dunes - Northwest Corner Present",
+ "Dusty Dunes - South Side Present",
+ "Dusty Dunes - Surrounding Rocks Present",
+ "Dusty Dunes - Black Sesame Present",
+ "Dusty Dunes - Oasis Present",
+ "Dusty Dunes - Northeast Corner Present",
+ "Dusty Dunes - North Central Present",
+ "Dusty Dunes - Shining Spot",
+ "Dusty Dunes - East Peninsula Present",
+ "Dusty Dunes - Mine Reward",
+ "Dusty Dunes Drugstore - Left Shop Slot 1",
+ "Dusty Dunes Drugstore - Left Shop Slot 2",
+ "Dusty Dunes Drugstore - Left Shop Slot 3",
+ "Dusty Dunes Drugstore - Left Shop Slot 4",
+ "Dusty Dunes Drugstore - Left Shop Slot 5",
+ "Dusty Dunes Drugstore - Left Shop Slot 6",
+ "Dusty Dunes Drugstore - Left Shop Slot 7",
+ "Dusty Dunes - Mine Food Cart Slot 1",
+ "Dusty Dunes - Mine Food Cart Slot 2",
+ "Dusty Dunes - Mine Food Cart Slot 3",
+ "Dusty Dunes - Mine Food Cart Slot 4",
+ "Dusty Dunes - Mine Food Cart Slot 5",
+ "Dusty Dunes - Mine Food Cart Slot 6",
+ "Dusty Dunes - Mine Food Cart Slot 7",
+ "Dusty Dunes Drugstore - Counter Slot 1",
+ "Dusty Dunes Drugstore - Counter Slot 2",
+ "Dusty Dunes Drugstore - Counter Slot 3",
+ "Dusty Dunes Drugstore - Counter Slot 4",
+ "Dusty Dunes Drugstore - Counter Slot 5",
+ "Dusty Dunes - Arms Dealer Slot 1",
+ "Dusty Dunes - Arms Dealer Slot 2",
+ "Dusty Dunes - Arms Dealer Slot 3",
+ "Dusty Dunes - Arms Dealer Slot 4"
+ },
+
+ "the Snow Wood locker room": {
+ "Snow Wood - Upper Right Locker",
+ "Snow Wood - Upper Left Locker",
+ "Snow Wood - Bottom Right Locker",
+ "Snow Wood - Bottom Left Locker"
+ },
+
+ "Snow Wood Boarding School": {
+ "Snow Wood - Many Present Room Present #1",
+ "Snow Wood - Many Present Room Present #2",
+ "Snow Wood - Many Present Room Present #3",
+ "Snow Wood - Many Present Room Present #4",
+ "Snow Wood - Many Present Room Present #5",
+ "Snow Wood - Many Present Room Present #6",
+ "Snow Wood - Many Present Room Present #7",
+ "Snow Wood - Maxwell Item",
+ "Snow Wood - Bedroom",
+ "Snow Wood - Upper Right Locker",
+ "Snow Wood - Upper Left Locker",
+ "Snow Wood - Bottom Right Locker",
+ "Snow Wood - Bottom Left Locker"
+ },
+
+ "Winters": {
+ "Winters - Drugstore Saleswoman",
+ "Winters Drugstore - Slot 1",
+ "Winters Drugstore - Slot 2",
+ "Winters Drugstore - Slot 3",
+ "Winters Drugstore - Slot 4",
+ "Winters Drugstore - Slot 5",
+ "Winters Drugstore - Slot 6",
+ "Winters Drugstore - Slot 7"
+ },
+
+ "the Brick Road Maze": {
+ "Brick Road Maze - Top Path Present",
+ "Brick Road Maze - Guarded Present",
+ "Brick Road Maze - Out of the Way Present",
+ "Brick Road Maze - Alcove Present",
+ "Brick Road Maze - Near Exit Present"
+ },
+
+ "Rainy Circle": {
+ "Rainy Circle - Isolated Present",
+ "Rainy Circle - East Cliff Present",
+ "Rainy Circle - Near Ropes Present"
+ },
+
+ "Dr. Andonuts's Lab": {
+ "Andonuts Lab - Present",
+ "Andonuts Lab - Mouse",
+ "Andonuts Lab - Caveman Shop Slot 1",
+ "Andonuts Lab - Caveman Shop Slot 2",
+ "Andonuts Lab - Caveman Shop Slot 3",
+ "Andonuts Lab - Caveman Shop Slot 4",
+ "Andonuts Lab - Caveman Shop Slot 5"
+ },
+
+ "Stonehenge Maze Area": {
+ "Stonehenge - Purple Maze Present",
+ "Stonehenge - Dead End Present",
+ "Stonehenge - Near End of the Maze Present"
+ },
+
+ "Stonehenge Bridge Area": {
+ "Stonehenge - Bridge Room East Balcony Present",
+ "Stonehenge - Bridge Room Lower Present"
+ },
+
+ "Stonehenge Flashing Area": {
+ "Stonehenge - Flashing Room Right Path Present",
+ "Stonehenge - Flashing Room Center Present",
+ "Stonehenge - Flashing Room Upper Present"
+ },
+
+ "Stonehenge Tube Room": {
+ "Stonehenge - Kidnapped Mr. Saturn",
+ "Stonehenge - Tony Item"
+ },
+
+ "Gold Mine 1F": {
+ "Gold Mine - Mouse Crossroad Present #1",
+ "Gold Mine - Mouse Crossroad Present #2",
+ "Gold Mine - South Hall Present",
+ "Gold Mine - South Corner Present",
+ "Gold Mine - South Mole Present #1",
+ "Gold Mine - South Mole Present #2",
+ "Gold Mine - North Crossroad Detour Present",
+ "Gold Mine - North Mole Present",
+ "Gold Mine - West Mole Present",
+ "Gold Mine - West Crossroad Detour Present"
+
+ },
+
+ "Gold Mine B1F": {
+ "Gold Mine - B1F Lonely Mole Present",
+ "Gold Mine - B1F Isolated Present",
+ "Gold Mine - B1F Junction Present",
+ "Gold Mine - B1F Junction Mole Present"
+
+ },
+
+ "Monkey Caves West Wing": {
+ "Monkey Caves - 1F Left Chest",
+ "Monkey Caves - West 2F Left Chest",
+ "Monkey Caves - West 2F Right Chest #1",
+ "Monkey Caves - West 2F Right Chest #2",
+ "Monkey Caves - West End Chest",
+ "Monkey Caves - West End Trashcan",
+
+
+ },
+
+ "Monkey Caves East Wing": {
+ "Monkey Caves - 1F Right Chest",
+ "Monkey Caves - East 2F Left Chest",
+ "Monkey Caves - East 2F Right Chest",
+ "Monkey Caves - East West 3F Right Chest #1",
+ "Monkey Caves - East West 3F Right Chest #2",
+ "Monkey Caves - East End Chest",
+ "Monkey Caves - East End Trashcan",
+ "Monkey Caves - Bow Monkey Gift"
+
+ },
+
+ "Monkey Caves Talah Rama Room": {
+ "Monkey Caves - Talah Rama Chest #1",
+ "Monkey Caves - Talah Rama Chest #2",
+ "Monkey Caves - Talah Rama Gift",
+ "Monkey Caves - Monkey Power"
+
+ },
+
+ "Fourside": {
+ "Fourside - Venus Gift",
+ "Fourside - Bakery 2F Gift",
+ "Fourside - Department Store Blackout",
+ "Fourside Department Store - Tool Shop Slot 1",
+ "Fourside Department Store - Tool Shop Slot 2",
+ "Fourside Department Store - Tool Shop Slot 3",
+ "Fourside Department Store - Tool Shop Slot 4",
+ "Fourside Department Store - Tool Shop Slot 5",
+ "Fourside Department Store - Tool Shop Slot 6",
+ "Fourside Department Store - Tool Shop Slot 7",
+ "Fourside Department Store - Shop Shop Slot 1",
+ "Fourside Department Store - Shop Shop Slot 2",
+ "Fourside Department Store - Shop Shop Slot 3",
+ "Fourside Department Store - Shop Shop Slot 4",
+ "Fourside Department Store - Food Shop Slot 1",
+ "Fourside Department Store - Food Shop Slot 2",
+ "Fourside Department Store - Food Shop Slot 3",
+ "Fourside Department Store - Food Shop Slot 4",
+ "Fourside Department Store - Food Shop Slot 5",
+ "Fourside Department Store - 2F Cart Slot 1",
+ "Fourside Department Store - 2F Cart Slot 2",
+ "Fourside Department Store - 2F Cart Slot 3",
+ "Fourside Department Store - 2F Cart Slot 4",
+ "Fourside Department Store - 2F Cart Slot 5",
+ "Fourside Department Store - 2F Cart Slot 6",
+ "Fourside Department Store - 2F Cart Slot 7",
+ "Fourside Department Store - Toys Shop Slot 1",
+ "Fourside Department Store - Toys Shop Slot 2",
+ "Fourside Department Store - Toys Shop Slot 3",
+ "Fourside Department Store - Toys Shop Slot 4",
+ "Fourside Department Store - Toys Shop Slot 5",
+ "Fourside Department Store - Toys Shop Slot 6",
+ "Fourside Department Store - Sports Shop Slot 1",
+ "Fourside Department Store - Sports Shop Slot 2",
+ "Fourside Department Store - Sports Shop Slot 3",
+ "Fourside Department Store - Sports Shop Slot 4",
+ "Fourside Department Store - Burger Shop Slot 1",
+ "Fourside Department Store - Burger Shop Slot 2",
+ "Fourside Department Store - Burger Shop Slot 3",
+ "Fourside Department Store - Burger Shop Slot 4",
+ "Fourside Department Store - Burger Shop Slot 5",
+ "Fourside Department Store - Arms Dealer Slot 1",
+ "Fourside Department Store - Arms Dealer Slot 2",
+ "Fourside Department Store - Arms Dealer Slot 3",
+ "Fourside Department Store - Arms Dealer Slot 4",
+ "Fourside Department Store - Arms Dealer Slot 5",
+ "Fourside - Northeast Alley Junk Shop Slot 1",
+ "Fourside - Northeast Alley Junk Shop Slot 2",
+ "Fourside - Northeast Alley Junk Shop Slot 3",
+ "Fourside - Northeast Alley Junk Shop Slot 4",
+ "Fourside Bakery - Slot 1",
+ "Fourside Bakery - Slot 2",
+ "Fourside Bakery - Slot 3",
+ "Fourside Bakery - Slot 4",
+ "Fourside Bakery - Slot 5",
+ "Fourside Bakery - Slot 6"
+ },
+
+ "Moonside": {
+ "Moonside - Two Trees Present",
+ "Moonside - East Island Present",
+ "Moonside - Businessman Present",
+ "Moonside - West Island Present",
+ "Moonside - Hospital Present",
+ "Fourside - Post-Moonside Delivery",
+ "Moonside Hotel - Shop Slot 1",
+ "Moonside Hotel - Shop Slot 2",
+ "Moonside Hotel - Shop Slot 3",
+ "Moonside Hotel - Shop Slot 4",
+ "Moonside Hotel - Shop Slot 5"
+
+ },
+
+ "Magnet Hill": {
+ "Magnet Hill - West Entrance Trashcan",
+ "Magnet Hill - First Room Free Door Trashcan",
+ "Magnet Hill - First Room Barrel Door Trashcan",
+ "Magnet Hill - Second Room Dead End Trashcan",
+ "Magnet Hill - Final Room Door Trashcan",
+ "Fourside - Magnet Hill Chest"
+
+ },
+
+ "Monotoli Building": {
+ "Monotoli Building - One Table Present",
+ "Monotoli Building - Two Table Present",
+ "Monotoli Building - Electra Gift",
+ "Monotoli Building - Monotoli Gift",
+ "Monotoli Building - Monotoli Character"
+
+ },
+
+ "Summers": {
+ "Summers - Museum Item",
+ "Summers - Magic Cake",
+ "Summers - Beach Cart",
+ "Summers - Magic Cake Cart Shop Slot",
+ "Summers - Scam Shop Slot 1",
+ "Summers - Scam Shop Slot 2",
+ "Summers - Scam Shop Slot 3",
+ "Summers - Scam Shop Slot 4",
+ "Summers - Scam Shop Slot 5",
+ "Summers - Scam Shop Slot 6",
+ "Summers - Scam Shop Slot 7",
+ "Summers Harbor - Shop Slot 1",
+ "Summers Harbor - Shop Slot 2",
+ "Summers Harbor - Shop Slot 3",
+ "Summers Harbor - Shop Slot 4",
+ "Summers Harbor - Shop Slot 5",
+ "Summers Harbor - Shop Slot 6",
+ "Summers Harbor - Shop Slot 7",
+ "Summers Restaurant - Slot 1",
+ "Summers Restaurant - Slot 2",
+ "Summers Restaurant - Slot 3",
+ "Summers Restaurant - Slot 4",
+ "Summers Restaurant - Slot 5",
+ "Summers Restaurant - Slot 6"
+ },
+
+ "Dalaam": {
+ "Dalaam - Throne Room Chest #1",
+ "Dalaam - Throne Room Chest #2",
+ "Dalaam - Throne Room Chest #3",
+ "Dalaam - Trial of Mu",
+ "Dalaam - Restaurant Chest #1",
+ "Dalaam - Restaurant Chest #2",
+ "Dalaam - Do Do Guy's House Chest",
+ "Dalaam - Upper House Chest",
+ "Dalaam - Throne Character",
+ "Dalaam Restaurant - Slot 1",
+ "Dalaam Restaurant - Slot 2",
+ "Dalaam Restaurant - Slot 3",
+ "Dalaam Restaurant - Slot 4"
+
+ },
+
+ "Pink Cloud": {
+ "Pink Cloud - Three Holes Present",
+ "Pink Cloud - Left Hole Present",
+ "Pink Cloud - Ground Floor Present"
+ },
+
+ "Scaraba": {
+ "Scaraba - Snake Bag Salesman",
+ "Scaraba - Star Master",
+ "Scaraba - Key Holder",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 1",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 2",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 3",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 4",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 5",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 6",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 7",
+ "Scaraba - Expensive Water Guy",
+ "Scaraba - Indoors Shop Slot 1",
+ "Scaraba - Indoors Shop Slot 2",
+ "Scaraba - Indoors Shop Slot 3",
+ "Scaraba - Indoors Shop Slot 4",
+ "Scaraba - Indoors Shop Slot 5",
+ "Scaraba - Indoors Shop Slot 6",
+ "Scaraba Hotel - Arms Dealer Slot 1",
+ "Scaraba Hotel - Arms Dealer Slot 2",
+ "Scaraba Hotel - Arms Dealer Slot 3",
+ "Scaraba Hotel - Arms Dealer Slot 4",
+ "Scaraba - Southern Camel Shop Slot 1",
+ "Scaraba - Southern Camel Shop Slot 2",
+ "Scaraba - Southern Camel Shop Slot 3",
+ "Scaraba - Southern Camel Shop Slot 4",
+ "Scaraba - Southern Camel Shop Slot 5",
+ "Scaraba - Southern Camel Shop Slot 6",
+ "Scaraba - Southern Camel Shop Slot 7"
+ },
+
+ "the Pyramid": {
+ "Pyramid - Anteroom Sarcophagus",
+ "Pyramid - Northwest Door Sarcophagus",
+ "Pyramid - Hallway Sarcophagus #1",
+ "Pyramid - Hallway Sarcophagus #2",
+ "Pyramid - Switch Room Sarcophagus",
+ "Pyramid - Pedestal Item",
+ "Pyramid - Way Out Sarcophagus"
+ },
+
+ "Dungeon Man 1F": {
+ "Dungeon Man - 1F Dead End Present",
+ "Dungeon Man - 1F Long Walk Present",
+ "Dungeon Man - 1F Disappointing Present",
+ "Dungeon Man - 1F Opinion Present",
+ "Dungeon Man - 1F No Sign Present",
+ "Dungeon Man - 1F Exit Ledge Present"
+ },
+
+ "Dungeon Man 2F": {
+ "Dungeon Man - 2F Unnecessary Billboard Present",
+ "Dungeon Man - 2F Dungeon Exploration Present",
+ "Dungeon Man - 2F South Ledge Present",
+ "Dungeon Man - 2F North Alcove Present",
+ "Dungeon Man - 2F Hole Present"
+ },
+
+ "Dungeon Man 3F": {
+ "Dungeon Man - 3F Present"
+ },
+
+ "Deep Darkness": {
+ "Deep Darkness - Teleporting Monkey",
+ "Deep Darkness - Crest of Darkness Present",
+ "Deep Darkness - Helicopter Present",
+ "Deep Darkness - Yellow Bird Present",
+ "Deep Darkness - Swamp Present",
+ "Deep Darkness - Corner Present",
+ "Deep Darkness - Alcove Present",
+ "Deep Darkness - Barf Character",
+ "Deep Darkness - Businessman Slot 1",
+ "Deep Darkness - Businessman Slot 2",
+ "Deep Darkness - Businessman Slot 3",
+ "Deep Darkness - Businessman Slot 4",
+ "Deep Darkness - Businessman Slot 5",
+ "Deep Darkness - Businessman Slot 6",
+ "Deep Darkness - Businessman Slot 7",
+ "Deep Darkness - Arms Dealer Slot 1",
+ "Deep Darkness - Arms Dealer Slot 2",
+ "Deep Darkness - Arms Dealer Slot 3",
+ "Deep Darkness - Arms Dealer Slot 4"
+ },
+
+ "Deep Darkness Truffles": {
+ "Deep Darkness - North Alcove Truffle",
+ "Deep Darkness - Near Land Truffle",
+ "Deep Darkness - Present Truffle",
+ "Deep Darkness - Village Truffle",
+ "Deep Darkness - Entrance Truffle"
+ },
+
+ "Tenda Village": {
+ "Tenda Village - Trashcan",
+ "Tenda Village - Tenda Tea",
+ "Tenda Village - Tenda Gift",
+ "Tenda Village - Tenda Gift #2"
+ },
+
+ "Lumine Hall 1F": {
+ "Lumine Hall - 1F North Path Present",
+ "Lumine Hall - 1F Above Belly Button Present",
+ "Lumine Hall - 1F Near Exit Present",
+ "Lumine Hall - 1F Dead End Present"
+ },
+
+ "Lumine Hall B1F": {
+ "Lumine Hall - B1F Non-Talkative Rock Present",
+ "Lumine Hall - B1F Thankful Rock Corner Present",
+ "Lumine Hall - B1F Thankful Rock Junction Present",
+ "Lumine Hall - B1F Belly Button Present",
+ "Lumine Hall - B1F West Alcove Present"
+ },
+
+ "Lost Underworld": {
+ "Lost Underworld - Talking Rock",
+ "Lost Underworld - East Present",
+ "Lost Underworld - Northeast Present",
+ "Lost Underworld - Northeast of Tenda Tribe Present",
+ "Lost Underworld - Southwest of Tenda Tribe Present",
+ "Lost Underworld - Evacuation Present",
+ "Lost Underworld - Tenda Camp Shop Slot 1",
+ "Lost Underworld - Tenda Camp Shop Slot 2",
+ "Lost Underworld - Tenda Camp Shop Slot 3",
+ "Lost Underworld - Tenda Camp Shop Slot 4",
+ "Lost Underworld - Tenda Camp Shop Slot 5",
+ "Lost Underworld - Tenda Camp Shop Slot 6",
+ "Lost Underworld - Tenda Camp Shop Slot 7"
+ },
+
+ "Fire Spring": {
+ "Fire Spring - 1st Cave Present",
+ "Fire Spring - East Corner Present",
+ "Fire Spring - Volcano Present",
+ "Fire Spring - Lone Cave Present",
+ "Fire Spring - Upper Volcano Present"
+ },
+
+ "Magicant": {
+ "Magicant - Ness's Gift",
+ "Magicant - Present Near Ness",
+ "Magicant - Lonely Present",
+ "Magicant - North Present",
+ "Magicant - Hills Present",
+ "Magicant - Town Present",
+ "Magicant - Shop Slot 1",
+ "Magicant - Shop Slot 2"
+ },
+
+
+ "Sea of Eden": {
+ "Magicant - Ness's Nightmare"
+ },
+
+ "Cave of the Present": {
+ "Cave of the Present - Star Master",
+ "Cave of the Present - Broken Phase Distorter"
+ },
+
+ "Cave of the Past": {
+ "Cave of the Past - Present"
+ },
+
+ "Burglin Park": {
+ "Burglin Park Junk Shop - Slot 1",
+ "Burglin Park Junk Shop - Slot 2",
+ "Burglin Park Junk Shop - Slot 3",
+ "Burglin Park Junk Shop - Slot 4",
+ "Burglin Park Junk Shop - Slot 5",
+ "Burglin Park Junk Shop - Slot 6",
+ "Burglin Park Bread Stand - Slot 1",
+ "Burglin Park Bread Stand - Slot 2",
+ "Burglin Park Bread Stand - Slot 3",
+ "Burglin Park Bread Stand - Slot 4",
+ "Burglin Park Bread Stand - Slot 5",
+ "Burglin Park Bread Stand - Slot 6",
+ "Burglin Park - Banana Stand",
+ "Twoson - Everdred Meeting",
+ "Twoson - Apple Kid Invention",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 1",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 2",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 3",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 4",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 5",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 6",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 7"
+ },
+
+ "the Scaraba Bazaar": {
+ "Scaraba Bazaar - Delicacy Shop Slot 1",
+ "Scaraba Bazaar - Delicacy Shop Slot 2",
+ "Scaraba Bazaar - Delicacy Shop Slot 3",
+ "Scaraba Bazaar - Delicacy Shop Slot 4",
+ "Scaraba Bazaar - Delicacy Shop Slot 5",
+ "Scaraba Bazaar - Delicacy Shop Slot 6",
+ "Scaraba Bazaar - Delicacy Shop Slot 7",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 1",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 2",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 3",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 4",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 5",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 6",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 7",
+ "Scaraba Bazaar - Red Snake Carpet Slot 1",
+ "Scaraba Bazaar - Red Snake Carpet Slot 2",
+ "Scaraba Bazaar - Red Snake Carpet Slot 3",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 1",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 2",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 3",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 4",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 5",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 6"
+ },
+
+ "the Twoson Department Store": {
+ "Twoson Department Store Burger Shop - Slot 1",
+ "Twoson Department Store Burger Shop - Slot 2",
+ "Twoson Department Store Burger Shop - Slot 3",
+ "Twoson Department Store Burger Shop - Slot 4",
+ "Twoson Department Store Bakery - Slot 1",
+ "Twoson Department Store Bakery - Slot 2",
+ "Twoson Department Store Bakery - Slot 3",
+ "Twoson Department Store Bakery - Slot 4",
+ "Twoson Department Store Top Floor - Right Counter Slot 1",
+ "Twoson Department Store Top Floor - Right Counter Slot 2",
+ "Twoson Department Store Top Floor - Right Counter Slot 3",
+ "Twoson Department Store Top Floor - Right Counter Slot 4",
+ "Twoson Department Store Top Floor - Right Counter Slot 5",
+ "Twoson Department Store Top Floor - Right Counter Slot 6",
+ "Twoson Department Store Top Floor - Left Counter Slot 1",
+ "Twoson Department Store Top Floor - Left Counter Slot 2"
+ },
+
+ "the Fourside Department Store": {
+ "Fourside Department Store - Tool Shop Slot 1",
+ "Fourside Department Store - Tool Shop Slot 2",
+ "Fourside Department Store - Tool Shop Slot 3",
+ "Fourside Department Store - Tool Shop Slot 4",
+ "Fourside Department Store - Tool Shop Slot 5",
+ "Fourside Department Store - Tool Shop Slot 6",
+ "Fourside Department Store - Tool Shop Slot 7",
+ "Fourside Department Store - Shop Shop Slot 1",
+ "Fourside Department Store - Shop Shop Slot 2",
+ "Fourside Department Store - Shop Shop Slot 3",
+ "Fourside Department Store - Shop Shop Slot 4",
+ "Fourside Department Store - Food Shop Slot 1",
+ "Fourside Department Store - Food Shop Slot 2",
+ "Fourside Department Store - Food Shop Slot 3",
+ "Fourside Department Store - Food Shop Slot 4",
+ "Fourside Department Store - Food Shop Slot 5",
+ "Fourside Department Store - 2F Cart Slot 1",
+ "Fourside Department Store - 2F Cart Slot 2",
+ "Fourside Department Store - 2F Cart Slot 3",
+ "Fourside Department Store - 2F Cart Slot 4",
+ "Fourside Department Store - 2F Cart Slot 5",
+ "Fourside Department Store - 2F Cart Slot 6",
+ "Fourside Department Store - 2F Cart Slot 7",
+ "Fourside Department Store - Toys Shop Slot 1",
+ "Fourside Department Store - Toys Shop Slot 2",
+ "Fourside Department Store - Toys Shop Slot 3",
+ "Fourside Department Store - Toys Shop Slot 4",
+ "Fourside Department Store - Toys Shop Slot 5",
+ "Fourside Department Store - Toys Shop Slot 6",
+ "Fourside Department Store - Sports Shop Slot 1",
+ "Fourside Department Store - Sports Shop Slot 2",
+ "Fourside Department Store - Sports Shop Slot 3",
+ "Fourside Department Store - Sports Shop Slot 4",
+ "Fourside Department Store - Burger Shop Slot 1",
+ "Fourside Department Store - Burger Shop Slot 2",
+ "Fourside Department Store - Burger Shop Slot 3",
+ "Fourside Department Store - Burger Shop Slot 4",
+ "Fourside Department Store - Burger Shop Slot 5",
+ "Fourside Department Store - Arms Dealer Slot 1",
+ "Fourside Department Store - Arms Dealer Slot 2",
+ "Fourside Department Store - Arms Dealer Slot 3",
+ "Fourside Department Store - Arms Dealer Slot 4",
+ "Fourside Department Store - Arms Dealer Slot 5",
+ "Fourside - Department Store Blackout"
+ },
+
+ "the Saturn Valley Shop": {
+ "Saturn Valley Shop - Center Saturn Slot 1",
+ "Saturn Valley Shop - Center Saturn Slot 2",
+ "Saturn Valley Shop - Center Saturn Slot 3",
+ "Saturn Valley Shop - Center Saturn Slot 4",
+ "Saturn Valley Shop - Center Saturn Slot 5",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 1",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 2",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 3",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 4",
+ "Saturn Valley Shop - Top Saturn Slot 1",
+ "Saturn Valley Shop - Top Saturn Slot 2",
+ "Saturn Valley Shop - Top Saturn Slot 3",
+ "Saturn Valley Shop - Top Saturn Slot 4",
+ "Saturn Valley Shop - Top Saturn Slot 5",
+ "Saturn Valley Shop - Top Saturn Slot 6",
+ "Saturn Valley Shop - Top Saturn Slot 7"
+ }
+}
diff --git a/worlds/earthbound/game_data/text_data.py b/worlds/earthbound/game_data/text_data.py
new file mode 100644
index 000000000000..7ec7e856e3d7
--- /dev/null
+++ b/worlds/earthbound/game_data/text_data.py
@@ -0,0 +1,297 @@
+region_text = {
+ "Northern Onett": "Onett",
+ "Onett": "Onett",
+ "Giant Step": "Giant Step",
+ "Twoson": "Twoson",
+ "Peaceful Rest Valley": "Peaceful Rest Valley",
+ "Happy-Happy Village": "Happy-Happy Village",
+ "Lilliput Steps": "Lilliput Steps",
+ "Threed": "Threed",
+ "Belch's Factory": "Belch's factory",
+ "Saturn Valley": "Saturn Valley",
+ "Upper Saturn Valley": "Saturn Valley",
+ "Milky Well": "Milky Well",
+ "Dusty Dunes Desert": "the Dusty Dunes Desert",
+ "Gold Mine": "the gold mine",
+ "Monkey Caves": "the monkey caves",
+ "Fourside": "Fourside",
+ "Magnet Hill": "Fourside's sewers",
+ "Monotoli Building": "the Monotoli Building",
+ "Winters": "Winters",
+ "Snow Wood Boarding School": "the Snow Wood Boarding School",
+ "Southern Winters": "Winters",
+ "Rainy Circle": "Rainy Circle",
+ "Stonehenge Base": "Stonehenge",
+ "Summers": "Summers",
+ "Dalaam": "Dalaam",
+ "Pink Cloud": "Pink Cloud",
+ "Scaraba": "Scaraba",
+ "Pyramid": "the Pyramid",
+ "Southern Scaraba": "Scaraba",
+ "Dungeon Man": "Dungeon Man",
+ "Deep Darkness": "the Deep Darkness",
+ "Tenda Village": "Tenda Village",
+ "Lumine Hall": "Lumine Hall",
+ "Lost Underworld": "the Lost Underworld",
+ "Fire Spring": "Fire Spring",
+ "Magicant": "Magicant",
+ "Cave of the Present": "Cave of the Past",
+ "Cave of the Past": "Cave of the Past"
+}
+
+barf_text = {
+ "Paula": [0xE0, 0xC2, 0xEE, 0xBD, 0xC3, 0xEE],
+ "Jeff": [0xFA, 0xC2, 0xEE, 0x10, 0xC4, 0xEE],
+ "Poo": [0x1A, 0x74, 0xEF, 0x3E, 0xC4, 0xEE],
+ "Flying Man": [0x17, 0xC3, 0xEE, 0x95, 0xC4, 0xEE],
+ "Ness": [0x16, 0x07, 0xF3, 0x2B, 0x07, 0xF3],
+ "Photograph": [0x2F, 0x99, 0xC9, 0x2F, 0x99, 0xC9]
+}
+
+eb_text_table = {" ": ([0x50]), "!": ([0x51]), '"': ([0x52]), "■": ([0x53]), "$": ([0x54]), "%": ([0x55]),
+ "°": ([0x56]), "'": ([0x57]), "(": ([0x58]), ")": ([0x59]), "*": ([0x5A]), "+": ([0x5B]),
+ ",": ([0x5C]), "-": ([0x5D]), ".": ([0x5E]), "/": ([0x5F]), "0": ([0x60]), "1": ([0x61]),
+ "2": ([0x62]), "3": ([0x63]), "4": ([0x64]), "5": ([0x65]), "6": ([0x66]), "7": ([0x67]),
+ "8": ([0x68]), "9": ([0x69]), ":": ([0x6A]), ";": ([0x6B]), "“": ([0x6C]), "=": ([0x6D]),
+ "”": ([0x6E]), "?": ([0x6F]), "@": ([0x70]), "A": ([0x71]), "B": ([0x72]), "C": ([0x73]),
+ "D": ([0x74]), "E": ([0x75]), "F": ([0x76]), "G": ([0x77]), "H": ([0x78]), "I": ([0x79]),
+ "J": ([0x7A]), "K": ([0x7B]), "L": ([0x7C]), "M": ([0x7D]), "N": ([0x7E]), "O": ([0x7F]),
+ "P": ([0x80]), "Q": ([0x81]), "R": ([0x82]), "S": ([0x83]), "T": ([0x84]), "U": ([0x85]),
+ "V": ([0x86]), "W": ([0x87]), "X": ([0x88]), "Y": ([0x89]), "Z": ([0x8A]), "α": ([0x8B]),
+ "β": ([0x8C]), "γ": ([0x8D]), "Σ": ([0x8E]), "Ω": ([0x8F]), "a": ([0x91]), "b": ([0x92]),
+ "c": ([0x93]), "d": ([0x94]), "e": ([0x95]), "f": ([0x96]), "g": ([0x97]), "h": ([0x98]),
+ "i": ([0x99]), "j": ([0x9A]), "k": ([0x9B]), "l": ([0x9C]), "m": ([0x9D]), "n": ([0x9E]),
+ "o": ([0x9F]), "p": ([0xA0]), "q": ([0xA1]), "r": ([0xA2]), "s": ([0xA3]), "t": ([0xA4]),
+ "u": ([0xA5]), "v": ([0xA6]), "w": ([0xA7]), "x": ([0xA8]), "y": ([0xA9]), "z": ([0xAA]),
+ "[": ([0xAB]), "♪": ([0xAC]), "]": ([0xAD]), "~": ([0xAE]), "◯": ([0xAF]), "_": ([0xB0]),
+ "<": ([0xB1]), " ": ([0x90]), "&": ([0xB6]), "#": ([0xB7]), "\n": ([0x03, 0x00]), "ñ": ([0xB9]),
+ "ä": ([0xBA]), "é": ([0xBB])}
+# 0x90 is a half-width space
+
+pixel_width_table = {" ": 2, "!": 2, '"': 3, "■": 2, "$": 5, "%": 9,
+ "°": 7, "'": 2, "(": 3, ")": 3, "*": 3, "+": 5,
+ ",": 2, "-": 2, ".": 2, "/": 4, "0": 5, "1": 5,
+ "2": 5, "3": 5, "4": 5, "5": 5, "6": 5, "7": 5,
+ "8": 5, "9": 5, ":": 2, ";": 3, "“": 3, "=": 5,
+ "”": 3, "?": 4, "@": 5, "A": 6, "B": 5, "C": 5,
+ "D": 5, "E": 4, "F": 4, "G": 5, "H": 5, "I": 1,
+ "J": 4, "K": 5, "L": 4, "M": 7, "N": 5, "O": 5,
+ "P": 5, "Q": 5, "R": 5, "S": 5, "T": 5, "U": 5,
+ "V": 6, "W": 7, "X": 5, "Y": 5, "Z": 4, "α": 5,
+ "β": 4, "γ": 6, "Σ": 4, "Ω": 5, "a": 4, "b": 4,
+ "c": 4, "d": 4, "e": 4, "f": 3, "g": 4, "h": 4,
+ "i": 1, "j": 2, "k": 4, "l": 1, "m": 7, "n": 4,
+ "o": 4, "p": 4, "q": 4, "r": 3, "s": 4, "t": 3,
+ "u": 4, "v": 5, "w": 7, "x": 4, "y": 4, "z": 4,
+ "[": 2, "♪": 5, "]": 2, "~": 6, "◯": 7, "_": 5,
+ "<": 4, " ": 1, "&": 6, "#": 6, "ñ": 6, "ä": 5,
+ "é": 5}
+
+
+lumine_hall_text = [
+ "You paid 350 bucks for this seed",
+ "You could be playing Mario is Missing!",
+ "youtu.be/dQw4w9WgXcQ",
+ "Please help I am in BK mode",
+ "But anyways, its not about the glitch, its not about that its a video game, its because you don't care about how this is affecting other people, your heartless, you can apparently live with yourself with this.",
+ "Do you still remember your pin? It's the price of a cheese pizza and and a large soda back where I used to work; Panucci's Pizza.",
+ "Did you remember to clear your sphere 1?",
+ "You should use a tracker, personally I recommend Poptracker",
+ "You should try Mario Is Missing AP, its fun and funny.",
+ "Despite everything, you're still you.",
+ "Bababooey",
+ "Reticulating Splines",
+ "You only have one shot.",
+ "human.. i remember you're genocides.",
+ '"May your woes be many, and your days few."',
+ "For you, the day Pokey graced your village was the most important day of your life. But for me, it was Tuesday.",
+ "To shreds you say?",
+ "Hey player, I'm in BK, can you get my item at Starman Super Drop Sword of Kings? It's my go mode.",
+ "Why did you make me do this? You're fighting so you can watch everyone around you die! Think, Player Name! You'll outlast every fragile, insignificant being on this planet.",
+ "Is this thing on? Testing. Oh hey, its me, Tony! Is Jeff doing okay? I worry about him a lot, and he's like, on my mind a lot. Maybe stop by Winters sometime! Pretty please?",
+ "If you have your club card, please scan it now.",
+ "Come on Reggie, give us MOTHER 3!",
+ "Growing up, Man I spent hours of my life stomping................................................................................................................................................ Koopas.",
+ "We've been trying to reach you about your car's extended warranty...",
+ "YOU MUST CONSTRUCT ADDITIONAL PYLONS!!!",
+ "It all start on the day the Happy Happyists attacked...",
+ "You just lost the game.",
+ "ERROR 404: HALL TEXT NOT FOUND.",
+ "Long ago, two races ruled over Earth: HUMANS and MONSTERS. One day, war broke out between the two races. After a long battle, the humans were victorious. They sealed the monsters underground with a magic spell.",
+ "Once upon a time, a LEGEND was whispered among shadows. It was a LEGEND of HOPE. It was a LEGEND of DREAMS. It was a LEGEND of LIGHT. It was a LEGEND of DARK.",
+ "OwO? What's This?",
+ "What about now it's time to rock with the Bickedy Buck Buzz Buzz! What about now it's time to rock with the Bickedy Buck Buzz Buzz! Bum to the bum to the bum to the bass to the bum to the boom to the Buzz Buzz!",
+ "HOW ARE YOU GENTLEMEN !! ALL YOUR BASE ARE BELONG TO US.",
+ "You're just the delivery boy. Sorry, your parts over now. Here, go play hero with this.",
+ "My friends are my power, and I'm theirs!",
+ "Did somebody mention the Door to Darkness?",
+ "We hope you'll be a great asset to the company. Great asset to the company. Asset-Great-Great-Asset to the Company.",
+ "So thanks, goat-bunny-cat lady.",
+ "It's dangerous to go alone, take this!",
+ "The rabbit virus got so happy that it exploded!!",
+ "Steve tried to swim in lava.",
+ "The flow of time is always cruel... its speed seems different for each person, but no one can change it... A thing that does not change with time is a memory of younger days...",
+ "Hah! You better have BURN HEAL!",
+ "..and so she left, heart still racing.",
+ "I've found you faker! Faker? I think you're the fake hedgehog around here. In fact, you're not even good enough be my fake. I'll make you eat those words!",
+ "Don't ask about Mother 3 AP.",
+ "Hello. This is Punk-Sure. I've been trying to reach you about your bicycle's extended warranty.",
+ "Who's that kid with blue hair whose name sounds like evil? Who suspiciously showed up to say we'd die like wretches? Surely HE'S not the bad guy, right? Nah, not when he has such a cool violin. Perish the thought!",
+ "I wonder if xfisjmg1 is going to release another Ninten Speaks video anytime soon?",
+ "weedle",
+ "hi",
+ "Look Ma, I'm in Lumine Hall!",
+ "Did you check Aginah?",
+ "Eat at Joe's",
+ "FOR A GOOD TIME, CALL 555-...",
+ "!hint Moon Pearl",
+ "'); DROP TABLE Lumine Hall Text;--",
+ "No, no! We can't! This baby is a demon child!",
+ "Rhydon Earthquake vs. Mr. Mime: 169-199 (59.7 - 70.3%) -- guaranteed 2HKO",
+ "Dakota",
+ "Ness, I am your father.",
+ "Hello, it's your dad. You've been out there for a long time now... It may be none of my business, but don't you think it would be a good idea if you took a break?",
+ "RIP AND TEAR",
+ "Sorry Ness, Pokey died of ligma",
+ "A horrible chill goes down your spine...",
+ "You feel an evil presence watching you...",
+ "Going to BK, BRB.",
+ "My favorite Mr. Saturn... It... cute... lovely... smart... plus... amazing... you think so? Hug it... when... sleeping... warm and cuddly... spectacular... ravishing... Oops! Look at the time! I kept you too long!",
+ "THE CLUMSY ROBOT IN NAME ONLY, FOR IT IS NEITHER.",
+ "The Arcana is the means by which all is revealed.",
+ "Say the password! ",
+ "Hello? Is there anybody in there? Just nod if you can hear me. Is there anyone home?",
+ "I bought my sneakers from a drug dealer in Fourside. I don't know what they were laced with but I've been tripping all day.",
+ "Sans Is Ness",
+ "Your Goal This Game: Join Happy Happyism",
+ "There's only junk items that way.",
+ "GET OUT WHILE YOU STILL CAN",
+ "Why isn't my SNI connecting?",
+ "It's 10 p.m. Do you know where your kids are?",
+ "If they censored what the adults were drinking to coffee, then what did Mr. Saturn give Ness originally?",
+ "Fourside Frappuccino. Work can wait.",
+ "Let's do an Archibald seed !",
+ "WARNING! Scheduled Maintenance in 4 Minutes...",
+ "I'm still stuck in BK",
+ "John Madden! John Madden! John Madden! John Madden! John Madden! John Madden! John Madden! ",
+ "Did you know that by reading this message you are wasting time not getting checks?",
+ "Your text here! Check the game's design thread on the Archipelago Server for how to submit!",
+ "WITNESS JOKE HERE",
+ "Also try The Witness!",
+ "Also try Adventure!",
+ "Also try DOOM 1993!",
+ "Also try Super Mario World!",
+ "Also try Yoshi's Island!",
+ "Also try Castlevania 64!",
+ "Hi Mom! I'm on AP!",
+ "Looking for hot multiworlds in your area?",
+ "Paleontologists do it in the dirt.",
+ "Wait... something seems off... Things seem more random than usual...",
+ "Shoutouts to SimpleFlips.",
+ "How long have you been BK?",
+ "Also try PK Scramble!",
+ "Also try Ancient Cave!",
+ "I am playing this on AP version 1.0 and it is amazing.",
+ "WHAT A HORRIBLE NIGHT TO HAVE A CURSE.",
+ "It's so jover",
+ "The National Weather Service has issued a severe thunderstorm warning for the following areas, starting at 7:49 PM Eastern Daylight Time and ending at 7:49 PM Eastern Daylight Time.",
+ "Where is everyone? I've done most of my checks and I still can't find my friends, it's so lonely here.",
+ "Can you hurry up and go to Ness's Nightmare it has my Morph Ball for my Super Metroid seed Thanks in advance Berserker",
+ "gottem",
+ "Dear Ness, please come to the house. I've baked a steak for you. Yours truly, mom.",
+ "Phar why",
+ "Tell Figment to check POD!",
+ "Congratulations, you're the Lumine Hall's 100th visitor! Click here for $2 off your next order at the Onett Burger Shop!",
+ "Well, that's never happened before...",
+ "99 bottles of DXWater on the wall, 99 bottles of DXWater. Take one down, pass it around, 98 bottles of DXWater on the wall!",
+ "After all these years. Finally, I have them all.",
+ "If there were two guys on the moon and one killed the other with a rock would that be screwed up or what",
+ "Help, I'm trapped in a Lumine Hall factory!",
+ "They say that Ice Palace can be found at Ice Palace...",
+ "Hey Mom, I'm in AP!",
+ "It says “gullible” on the wall",
+ "64 bits! 32 bits! 16 bits! 8 bits! 4 bits! 2 bits! 1 bit! Half bit! Quarter bit! THE WRIST GAME!!!",
+ "Where did YOU learn to teleport?",
+ "My statistics show about 70% of kids these days don't stop to read bulletin boards anymore. Well, I had to shell out a lot of money for this, but let's see you ignore THIS one, suckers! ...Brick Road",
+ "I'm Ness... I could go for a burger right about now... That girl from Twoson sure is cute... I wonder if I should ask her out on a date... W...wha...? What's...? H-HEY! These thoughts are PRIVATE!",
+ "Chris, when is Parasite Eve in AP?",
+ "Praise the Fool!",
+ "Am I the only one who likes Sonic Unleashed?"
+]
+
+spoiler_psi = {
+ "Special": "PSI Rockin",
+ "Fire": "PSI Fire",
+ "Freeze": "PSI Freeze",
+ "Thunder": "PSI Thunder",
+ "Flash": "PSI Flash",
+ "Starstorm": "PSI Starstorm",
+ "Shield": "Shield",
+ "PSI Shield": "PSI Shield",
+ "Offense Up": "Offense up",
+ "Defense Down": "Defense down",
+ "Hypnosis": "Hypnosis",
+ "Paralysis": "Paralysis",
+ "Brainshock": "Brainshock",
+ "Blast": "PSI Blast",
+ "Missile": "PSI Missile",
+ "Stop": "Stop",
+ "Drain": "Drain",
+ "Defense up": "Defense up",
+ "Neutralize": "Neutralize",
+ "Disable": "Disable"
+}
+
+spoiler_starts = [
+ "Ness's House",
+ "Onett Downtown",
+ "Twoson",
+ "Happy-Happy Village",
+ "Threed",
+ "Saturn Valley",
+ "Fourside",
+ "Winters",
+ "Summers",
+ "Dalaam",
+ "Scaraba",
+ "Deep Darkness",
+ "Tenda Village",
+ "Lost Underworld",
+ "Magicant"
+]
+
+spoiler_badges = {
+ "special": "Rockin",
+ "fire": "Fire",
+ "freeze": "Freeze",
+ "flash": "Flash",
+ "starstorm": "Starstorm",
+ "explosive": "Bomb",
+ "thunder": "Thunder",
+}
+
+
+def text_encoder(text: str, textcap: int) -> bytearray:
+ """Return an encoded bytearray of in-game text from a string. Unknown characters will be replaced with a ?.
+ textcap is the maximum allowed length of the text."""
+ encoded_text = bytearray()
+ for char in text[:textcap]:
+ if char in eb_text_table:
+ encoded_text.extend(eb_text_table[char])
+ else:
+ encoded_text.extend([0x6F])
+ return encoded_text
+
+
+def calc_pixel_width(text: str) -> int:
+ """Return the in-game width of a string. EarthBound uses a VWF, and some text strings
+ need to be shortened to fit within in-game menus."""
+ width = 0
+ for char in text:
+ if char in pixel_width_table:
+ width += pixel_width_table[char]
+ else:
+ width += 4
+ width += 1
+ width -= 1
+ return width
diff --git a/worlds/earthbound/gifting/__init__.py b/worlds/earthbound/gifting/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/earthbound/gifting/gift_tags.py b/worlds/earthbound/gifting/gift_tags.py
new file mode 100644
index 000000000000..8aff19866e9b
--- /dev/null
+++ b/worlds/earthbound/gifting/gift_tags.py
@@ -0,0 +1,606 @@
+from dataclasses import dataclass
+from typing import List
+
+gift_qualities = {
+ "Super Plush Bear": {"MeatShield": 3},
+ "Cracked Bat": {"Weapon": 0.07},
+ "Tee Ball Bat": {"Weapon": 0.14},
+ "Sand Lot Bat": {"Weapon": 0.27},
+ "Minor League Bat": {"Weapon": 0.48},
+ "Mr. Baseball Bat": {"Weapon": 0.70},
+ "Big League Bat": {"Weapon": 0.90},
+ "Hall of Fame Bat": {"Weapon": 1.14},
+ "Magicant Bat": {"Weapon": 1.48},
+ "Legendary Bat": {"Weapon": 2.03},
+ "Gutsy Bat": {"Weapon": 4.0},
+ "Casey Bat": {"Weapon": 0.1},
+ "T-Rex's Bat": {"Weapon": 0.88},
+ "Ultimate Bat": {"Weapon": 1.25},
+
+ "Fry Pan": {"Weapon": 0.24},
+ "Thick Fry Pan": {"Weapon": 0.48},
+ "Deluxe Fry Pan": {"Weapon": 0.73},
+ "Chef's Fry Pan": {"Weapon": 0.97},
+ "Non-Stick Frypan": {"Weapon": 1.19},
+ "French Fry Pan": {"Weapon": 1.46},
+ "Holy Fry Pan": {"Weapon": 1.90},
+ "Magic Frypan": {"Weapon": 2.5},
+
+ "Pop Gun": {"Weapon": 0.25},
+ "Stun Gun": {"Weapon": 0.34},
+ "Toy Air Gun": {"Weapon": 0.50},
+ "Magnum Air Gun": {"Weapon": 0.57},
+ "Zip Gun": {"Weapon": 0.63},
+ "Laser Gun": {"Weapon": 0.76},
+ "Hyper Beam": {"Weapon": 0.92},
+ "Double Beam": {"Weapon": 1.04},
+ "Crusher Beam": {"Weapon": 1.14},
+ "Spectrum Beam": {"Weapon": 1.24},
+ "Death Ray": {"Weapon": 1.42},
+ "Baddest Beam": {"Weapon": 1.55},
+ "Moon Beam Gun": {"Weapon": 1.74},
+ "Gaia Beam": {"Weapon": 1.98},
+ "Sword of Kings": {"Weapon": 2.00},
+
+ "Bag of Fries": {"Heal": 0.24},
+ "Banana": {"Heal": 0.22},
+ "Bean Croquette": {"Heal": 0.42},
+ "Beef Jerky": {"Heal": 1.50},
+ "Boiled Egg": {"Heal": 0.42},
+ "Bowl of Rice Gruel": {"Heal": 2.16},
+ "Brain Food Lunch": {"Heal": 3.00, "Mana": 1.42},
+ "Bread Roll": {"Heal": 0.30},
+ "Calorie Stick": {"Heal": 0.60},
+ "Can of Fruit Juice": {"Heal": 0.06},
+ "Chef's Special": {"Heal": 2.16},
+ "Cookie": {"Heal": 0.06},
+ "Croissant": {"Heal": 0.60},
+ "Cup of Coffee": {"Heal": 0.12},
+ "Cup of Noodles": {"Heal": 0.42},
+ "Double Burger": {"Heal": 0.96},
+ "Fresh Egg": {"Heal": 0.84},
+ "Hamburger": {"Heal": 0.48},
+ "Kabob": {"Heal": 1.26},
+ "Kraken Soup": {"Heal": 9.99},
+ "Large Pizza": {"Heal": 2.40},
+ "Lucky Sandwich": {"Heal": 0.60, "Mana": 0.57},
+ "Luxury Jerky": {"Heal": 3.00},
+ "Mammoth Burger": {"Heal": 2.05},
+ "Molokheiya Soup": {"Heal": 0.84},
+ "Pasta di Summers": {"Heal": 1.10},
+ "Peanut Cheese Bar": {"Heal": 1.00},
+ "Picnic Lunch": {"Heal": 0.80},
+ "Piggy Jelly": {"Heal": 3.00},
+ "Pizza": {"Heal": 1.20},
+ "Plain Roll": {"Heal": 0.80},
+ "Plain Yogurt": {"Heal": 1.60},
+ "Popsicle": {"Heal": 0.18},
+ "Protein Drink": {"Heal": 0.80},
+ "Royal Iced Tea": {"Heal": 0.60},
+ "Repel Sandwich": {"Heal": 0.06},
+ "Repel Superwich": {"Heal": 0.06},
+ "Spicy Jerky": {"Heal": 2.52},
+ "Trout Yogurt": {"Heal": 0.30},
+ "Hand-Aid": {"Heal": 9.99},
+
+ "Cold Remedy": {"Cure": 0.10},
+ "Vial of Serum": {"Cure": 0.25},
+ "Wet Towel": {"Cure": 0.50},
+ "Refreshing Herb": {"Cure": 1.00},
+ "Secret Herb": {"Cure": 2.00, "Life": 0.50},
+ "Horn of Life": {"Cure": 3.00, "Life": 1.00},
+ "Cup of Lifenoodles": {"Cure": 3.00, "Life": 1.00},
+
+ "Bottle of Water": {"Mana": 0.28},
+ "Bottle of DXwater": {"Mana": 1.14},
+ "Magic Pudding": {"Mana": 1.14},
+ "Magic Tart": {"Mana": 0.57},
+ "Magic Truffle": {"Mana": 2.28},
+ "PSI Caramel": {"Mana": 0.57},
+
+ "Cheap Bracelet": {"Armor": 0.12},
+ "Copper Bracelet": {"Armor": 0.25},
+ "Silver Bracelet": {"Armor": 0.37},
+ "Gold Bracelet": {"Armor": 0.75},
+ "Platinum Band": {"Armor": 1.00},
+ "Diamond Band": {"Armor": 1.25},
+ "Pixie's Bracelet": {"Armor": 1.50},
+ "Cherub's Band": {"Armor": 1.75},
+ "Goddess Band": {"Armor": 2.00},
+ "Bracer of Kings": {"Armor": 0.87, "Fire": 0.50},
+
+ "Ribbon": {"Armor": 0.35},
+ "Red Ribbon": {"Armor": 0.43},
+ "Defense Ribbon": {"Armor": 0.70},
+ "Talisman Ribbon": {"Armor": 1.05},
+ "Saturn Ribbon": {"Armor": 1.57},
+ "Goddess Ribbon": {"Armor": 1.92},
+
+ "Yo-yo": {"Weapon": 0.06},
+ "Slingshot": {"Weapon": 0.12},
+ "Bionic Slingshot": {"Weapon": 0.32},
+ "Trick Yo-yo": {"Weapon": 0.46},
+ "Combat Yo-yo": {"Weapon": 0.54},
+
+ "Travel Charm": {"AntiNumb": 0.25},
+ "Great Charm": {"Armor": 0.01, "AntiNumb": 0.50},
+ "Crystal Charm": {"Armor": 0.02, "AntiNumb": 1.00},
+ "Rabbit's Foot": {"Armor": 0.03, "AntiNumb": 1.00, "Speed": 2.00},
+
+ "Flame Pendant": {"Armor": 1.00, "Fire": 1.00},
+ "Rain Pendant": {"Armor": 1.00, "Ice": 1.00},
+ "Night Pendant": {"Armor": 1.00, "Light": 1.00},
+ "Sea Pendant": {"Armor": 2.00, "Fire": 1.00, "Ice": 1.00, "Light": 1.00},
+ "Star Pendant": {"Armor": 3.00, "Fire": 1.00, "Ice": 1.00, "Light": 1.00, "AntiNumb": 1.00},
+ "Earth Pendant": {"Armor": 1.50, "Fire": 0.50, "Ice": 0.50, "Light": 0.50},
+ "Cloak of Kings": {"Armor": 0.50},
+
+ "Shield Killer": {"Neutralizing": 0.50},
+ "Neutralizer": {"Neutralizing": 1.00},
+ "HP-Sucker": {"Draining": 0.50},
+ "Hungry HP-Sucker": {"Draining": 1.00},
+
+ "Baseball Cap": {"Armor": 0.13},
+ "Holmes Hat": {"Armor": 0.27},
+ "Mr. Baseball Cap": {"Armor": 0.16},
+ "Hard Hat": {"Armor": 0.41},
+ "Coin of Slumber": {"Armor": 0.83},
+ "Coin of Defense": {"Armor": 1.11},
+ "Lucky Coin": {"Armor": 1.38},
+ "Talisman Coin": {"Armor": 1.66},
+ "Shiny Coin": {"Armor": 1.94},
+ "Souvenir Coin": {"Armor": 2.22},
+ "Coin of Silence": {"Armor": 1.25},
+ "Mr. Saturn Coin": {"Armor": 1.30},
+ "Diadem of Kngs": {"Armor": 0.50, "Fire": 0.25, "Ice": 0.25, "Light": 0.25, "AntiNumb": 0.25}
+}
+
+
+@dataclass
+class EarthBoundGift:
+ name: str
+ value: int
+ traits: list[str]
+
+
+def make_trait(trait: str, name: str) -> dict[str, str | int]:
+ if name in gift_qualities and trait in gift_qualities[name]:
+ quality = gift_qualities[name][trait]
+ else:
+ quality = None
+
+ if quality:
+ return {"trait": trait, "quality": quality}
+ else:
+ return {"trait": trait}
+
+
+def make_default_traits(traits: list[str], name: str) -> list[dict[str, str | int]]:
+ return [make_trait(trait, name) for trait in traits]
+
+
+def create_gift(name: str, value: int, traits: list[str]) -> EarthBoundGift:
+ """Create a Gift with the specified tag and values."""
+ return EarthBoundGift(name, value, make_default_traits(traits, name))
+
+
+gift_properties = {
+ 2: create_gift("Teddy Bear", 178, ["Toy", "Doll"]),
+
+ 3: create_gift("Super Plush Bear", 1198, ["Toy", "Doll"]),
+
+ 4: create_gift("Broken Machine", 0, ["Broken", "Machine", "Metal", "Material", "Resource", "Trash"]),
+
+ 5: create_gift("Broken Gadget", 109, ["Broken", "Machine", "Metal", "Material", "Resource", "Trash"]),
+
+ 6: create_gift("Broken Air Gun", 0, ["Broken", "Machine", "Metal", "Material", "Resource", "Trash"]),
+
+ 7: create_gift("Broken Spray Can", 189, ["Broken", "Machine", "Metal", "Material", "Resource", "Trash"]),
+
+ 8: create_gift("Broken Laser", 0, ["Broken", "Machine", "Metal", "Material", "Resource", "Trash"]),
+
+ 9: create_gift("Broken Iron", 149, ["Broken", "Machine", "Metal", "Material", "Resource", "Trash"]),
+
+ 10: create_gift("Broken Pipe", 149, ["Broken", "Machine", "Metal", "Material", "Resource", "Pipe", "Trash"]),
+
+ 11: create_gift("Broken Cannon", 218, ["Broken", "Machine", "Metal", "Material", "Resource", "Trash"]),
+
+ 12: create_gift("Broken Tube", 0, ["Broken", "Machine", "Metal", "Material", "Resource", "Trash"]),
+
+ 13: create_gift("Broken Bazooka", 0, ["Broken", "Machine", "Metal", "Material", "Resource", "Weapon", "Trash"]),
+
+ 14: create_gift("Broken Trumpet", 0, ["Broken", "Machine", "Metal", "Material", "Resource", "Instrument",
+ "Trash"]),
+
+ 15: create_gift("Broken Harmonica", 0, ["Broken", "Machine", "Metal", "Material", "Resource", "Instrument",
+ "Trash"]),
+
+ 16: create_gift("Broken Antenna", 0, ["Broken", "Machine", "Metal", "Material", "Resource", "Trash"]),
+
+ 17: create_gift("Cracked Bat", 18, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 18: create_gift("Tee Ball Bat", 48, ["MeleeWeapon", "Metal", "Toy", "Weapon"]),
+
+ 19: create_gift("Sand Lot Bat", 98, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 20: create_gift("Minor League Bat", 399, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 21: create_gift("Mr. Baseball Bat", 498, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 22: create_gift("Big League Bat", 3080, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 23: create_gift("Hall of Fame Bat", 1880, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 24: create_gift("Magicant Bat", 0, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon", "Dreamlike"]),
+
+ 25: create_gift("Legendary Bat", 0, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon", "Legendary"]),
+
+ 26: create_gift("Gutsy Bat", 0, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon", "Guts"]),
+
+ 27: create_gift("Casey Bat", 0, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 28: create_gift("Fry Pan", 0, ["MeleeWeapon", "Metal", "Tool", "Weapon"]),
+
+ 29: create_gift("Thick Fry Pan", 0, ["MeleeWeapon", "Metal", "Tool", "Weapon"]),
+
+ 30: create_gift("Deluxe Fry Pan", 0, ["MeleeWeapon", "Metal", "Tool", "Weapon"]),
+
+ 31: create_gift("Chef's Fry Pan", 0, ["MeleeWeapon", "Metal", "Tool", "Weapon"]),
+
+ 32: create_gift("French Fry Pan", 0, ["MeleeWeapon", "Metal", "Tool", "Weapon"]),
+
+ 33: create_gift("Magic Fry Pan", 0, ["MeleeWeapon", "Metal", "Tool", "Weapon"]),
+
+ 34: create_gift("Holy Fry Pan", 0, ["MeleeWeapon", "Metal", "Tool", "Weapon"]),
+
+ 35: create_gift("Sword of Kings", 0, ["Weapon"]),
+
+ 36: create_gift("Pop Gun", 0, ["RangedWeapon", "Toy", "Weapon"]),
+
+ 37: create_gift("Stun Gun", 0, ["RangedWeapon", "Weapon"]),
+
+ 38: create_gift("Toy Air Gun", 0, ["RangedWeapon", "Toy", "Weapon"]),
+
+ 39: create_gift("Magnum Air Gun", 0, ["RangedWeapon", "Toy", "Weapon"]),
+
+ 40: create_gift("Zip Gun", 0, ["RangedWeapon", "Toy", "Weapon"]),
+
+ 41: create_gift("Laser Gun", 0, ["RangedWeapon", "Beam", "Weapon"]),
+
+ 42: create_gift("Hyper Beam", 0, ["RangedWeapon", "Beam", "Weapon"]),
+
+ 43: create_gift("Crusher Beam", 0, ["RangedWeapon", "Beam", "Weapon"]),
+
+ 44: create_gift("Spectrum Beam", 0, ["RangedWeapon", "Beam", "Weapon"]),
+
+ 45: create_gift("Death Ray", 0, ["RangedWeapon", "Beam", "Weapon"]),
+
+ 46: create_gift("Baddest Beam", 0, ["RangedWeapon", "Beam", "Weapon"]),
+
+ 47: create_gift("Moon Beam Gun", 0, ["RangedWeapon", "Beam", "Weapon"]),
+
+ 48: create_gift("Gaia Beam", 0, ["RangedWeapon", "Beam", "Weapon"]),
+
+ 49: create_gift("Yo-yo", 0, ["RangedWeapon", "Toy", "Weapon"]),
+
+ 50: create_gift("Slingshot", 0, ["RangedWeapon", "Toy", "Weapon"]),
+
+ 51: create_gift("Bionic Slingshot", 0, ["RangedWeapon", "Toy", "Weapon"]),
+
+ 52: create_gift("Trick Yo-yo", 0, ["RangedWeapon", "Toy", "Weapon"]),
+
+ 53: create_gift("Combat Yo-yo", 0, ["RangedWeapon", "Toy", "Weapon"]),
+
+ 54: create_gift("Travel Charm", 0, ["Jewelry"]),
+
+ 55: create_gift("Great Charm", 0, ["Jewelry", "Defense", "Armor"]),
+
+ 56: create_gift("Crystal Charm", 0, ["Jewelry", "Defense", "Armor", "Crystal"]),
+
+ 57: create_gift("Rabbit's Foot", 0, ["Armor", "Jewelry", "Defense", "Speed"]),
+
+ 58: create_gift("Flame Pendant", 0, ["Armor", "Jewelry", "Defense", "Fire"]),
+
+ 59: create_gift("Rain Pendant", 0, ["Armor", "Jewelry", "Defense", "Water"]),
+
+ 60: create_gift("Night Pendant", 0, ["Armor", "Jewelry", "Defense", "Light"]),
+
+ 61: create_gift("Sea Pendant", 0, ["Armor", "Jewelry", "Defense", "Light", "Fire", "Water"]),
+
+ 62: create_gift("Star Pendant", 0, ["Armor", "Jewelry", "Defense", "Light", "Fire", "Water"]),
+
+ 63: create_gift("Cloak of Kings", 0, ["Armor", "Jewelry", "Defense"]),
+
+ 64: create_gift("Cheap Bracelet", 0, ["Armor", "Jewelry", "Defense", "Plastic"]),
+
+ 65: create_gift("Copper Bracelet", 0, ["Armor", "Jewelry", "Defense", "Copper"]),
+
+ 66: create_gift("Silver Bracelet", 0, ["Armor", "Jewelry", "Defense", "Silver"]),
+
+ 67: create_gift("Gold Bracelet", 0, ["Armor", "Jewelry", "Defense", "Gold"]),
+
+ 68: create_gift("Platinum Band", 0, ["Armor", "Jewelry", "Defense", "Platinum"]),
+
+ 69: create_gift("Diamond Band", 0, ["Armor", "Jewelry", "Defense", "Diamond"]),
+
+ 70: create_gift("Pixie's Bracelet", 0, ["Armor", "Jewelry", "Defense"]),
+
+ 71: create_gift("Cherub's Band", 0, ["Armor", "Jewelry", "Defense"]),
+
+ 72: create_gift("Goddess Band", 0, ["Armor", "Jewelry", "Defense"]),
+
+ 73: create_gift("Bracer of Kings", 0, ["Armor", "Jewelry", "Defense", "Fire"]),
+
+ 74: create_gift("Baseball Cap", 0, ["Armor", "Baseball", "Defense", "Hat"]),
+
+ 75: create_gift("Holmes Hat", 0, ["Armor", "Defense", "Hat"]),
+
+ 76: create_gift("Mr. Baseball Cap", 0, ["Armor", "Defense", "Hat", "Baseball"]),
+
+ 77: create_gift("Hard Hat", 0, ["Armor", "Defense", "Hat"]),
+
+ 78: create_gift("Ribbon", 0, ["Armor", "Cloth", "Defense"]),
+
+ 79: create_gift("Red Ribbon", 0, ["Armor", "Cloth", "Defense", "Red"]),
+
+ 80: create_gift("Goddess Ribbon", 0, ["Armor", "Cloth", "Defense"]),
+
+ 81: create_gift("Coin of Slumber", 0, ["Armor", "Defense"]),
+
+ 82: create_gift("Coin of Defense", 0, ["Armor", "Defense"]),
+
+ 83: create_gift("Lucky Coin", 0, ["Armor", "Defense", "Luck"]),
+
+ 84: create_gift("Talisman Coin", 0, ["Armor", "Defense"]),
+
+ 85: create_gift("Shiny Coin", 0, ["Armor", "Defense"]),
+
+ 86: create_gift("Souvenir Coin", 0, ["Armor", "Defense"]),
+
+ 87: create_gift("Diadem of Kings", 0, ["Armor", "Jewelry", "Defense", "Fire", "Water", "Light"]),
+
+ 88: create_gift("Cookie", 7, ["Confectionary", "Comsumable", "Heal", "Food"]),
+
+ 89: create_gift("Bag of Fries", 8, ["FastFood", "Comsumable", "Heal", "Food"]),
+
+ 90: create_gift("Hamburger", 14, ["FastFood", "Comsumable", "Heal", "Food", "Beef", "Meat"]),
+
+ 91: create_gift("Boiled Egg", 0, ["Egg", "Comsumable", "Heal", "Food", "White"]),
+
+ 92: create_gift("Fresh Egg", 0, ["Egg", "Comsumable", "Heal", "Food", "White"]),
+
+ 93: create_gift("Picnic Lunch", 24, ["Comsumable", "Heal", "Food"]),
+
+ 94: create_gift("Pasta di Summers", 0, ["Pasta", "Comsumable", "Heal", "Food", "Cooking"]),
+
+ 95: create_gift("Pizza", 0, ["Comsumable", "Heal", "Food"]),
+
+ 96: create_gift("Chef's Special", 0, ["Comsumable", "Heal", "Food"]),
+
+ 97: create_gift("Large Pizza", 0, ["Comsumable", "Heal", "Food"]),
+
+ 98: create_gift("PSI Caramel", 0, ["Comsumable", "Mana", "Food", "Candy"]),
+
+ 99: create_gift("Magic Truffle", 0, ["Comsumable", "Mana", "Food", "Candy"]),
+
+ 100: create_gift("Brain Food Lunch", 0, ["Comsumable", "Mana", "Food", "Heal", "ExoticFood"]),
+
+ 101: create_gift("Rock Candy", 0, ["Comsumable", "Candy", "Food", "Buff"]),
+
+ 102: create_gift("Croissant", 0, ["Comsumable", "Food", "Heal", "Bread"]),
+
+ 103: create_gift("Bread Roll", 0, ["Comsumable", "Food", "Heal", "Bread"]),
+
+ 106: create_gift("Can of Fruit Juice", 0, ["Comsumable", "Heal", "Drink", "Liquid", "Fruit", "Juice"]),
+
+ 107: create_gift("Royal Iced Tea", 0, ["Comsumable", "Heal", "Drink", "Liquid"]),
+
+ 108: create_gift("Protein Drink", 0, ["Comsumable", "Heal", "Drink", "Liquid"]),
+
+ 109: create_gift("Kraken Soup", 0, ["Comsumable", "Heal", "Food", "Liquid", "Cooking"]),
+
+ 110: create_gift("Bottle of Water", 0, ["Comsumable", "Mana", "Drink", "Liquid", "Water"]),
+
+ 111: create_gift("Cold Remedy", 0, ["Comsumable", "Medicine", "Drink", "Liquid", "Cure"]),
+
+ 112: create_gift("Vial of Serum", 0, ["Comsumable", "Medicine", "Drink", "Liquid", "Cure"]),
+
+ 113: create_gift("IQ Capsule", 0, ["Comsumable", "Medicine", "IQ", "Buff"]),
+
+ 114: create_gift("Guts Capsule", 0, ["Comsumable", "Medicine", "Guts", "Buff"]),
+
+ 115: create_gift("Speed Capsule", 0, ["Comsumable", "Medicine", "Speed", "Buff"]),
+
+ 116: create_gift("Vital Capsule", 0, ["Comsumable", "Medicine", "Buff", "Life"]),
+
+ 117: create_gift("Luck Capsule", 0, ["Comsumable", "Medicine", "Buff", "Luck"]),
+
+ 118: create_gift("Ketchup Packet", 0, ["Comsumable", "Heal", "Food", "Condiment", "Red"]),
+
+ 119: create_gift("Sugar Packet", 0, ["Comsumable", "Heal", "Food", "Condiment", "White"]),
+
+ 120: create_gift("Tin of Cocoa", 0, ["Comsumable", "Heal", "Food", "Condiment", "Brown", "Chocolate"]),
+
+ 121: create_gift("Carton of Cream", 0, ["Comsumable", "Heal", "Food", "Condiment", "White", "Liquid"]),
+
+ 122: create_gift("Sprig of Parsley", 0, ["Comsumable", "Heal", "Food", "Condiment", "Green", "Plant"]),
+
+ 123: create_gift("Jar of Hot Sauce", 0, ["Comsumable", "Heal", "Food", "Condiment", "Orange", "Spicy"]),
+
+ 124: create_gift("Salt Packet", 0, ["Comsumable", "Heal", "Food", "Condiment", "White", "Salted"]),
+
+ 126: create_gift("Jar of Delisauce", 0, ["Comsumable", "Heal", "Food", "Condiment", "Green"]),
+
+ 127: create_gift("Wet Towel", 0, ["Comsumable", "Cure"]),
+
+ 128: create_gift("Refreshing Herb", 0, ["Comsumable", "Cure", "Food", "Herb"]),
+
+ 129: create_gift("Secret Herb", 0, ["Comsumable", "Cure", "Food", "Life", "Herb"]),
+
+ 130: create_gift("Horn of Life", 0, ["Comsumable", "Cure", "Life"]),
+
+ 131: create_gift("Counter-PSI Unit", 0, ["Machine", "Electronics", "Metal"]),
+
+ 132: create_gift("Shield Killer", 0, ["Machine", "Electronics", "Metal", "Neutralizing"]),
+
+ 133: create_gift("Bazooka", 0, ["Machine", "Weapon", "Electronics", "Explosive", "RangedWeapon"]),
+
+ 134: create_gift("Heavy Bazooka", 0, ["Machine", "Weapon", "Electronics", "Explosive", "RangedWeapon"]),
+
+ 135: create_gift("HP-Sucker", 0, ["Machine", "Draining", "Electronics"]),
+
+ 136: create_gift("Hungry HP-Sucker", 0, ["Machine", "Draining", "Electronics"]),
+
+ 137: create_gift("Xterminator Spray", 0, ["Can", "Metal", "Insect", "Weapon", "Chemicals"]),
+
+ 138: create_gift("Slime Generator", 0, ["Machine", "Slime", "Electronics"]),
+
+ 140: create_gift("Ruler", 0, ["Long", "Wood", "Trash", "IQ"]),
+
+ 141: create_gift("Snake Bag", 0, ["Animal", "Container", "Throwing"]),
+
+ 142: create_gift("Mummy Wrap", 0, ["Ancient", "Paper", "Weapon", "Throwing", "Consumable"]),
+
+ 143: create_gift("Protractor", 0, ["Metal", "Trash", "IQ"]),
+
+ 144: create_gift("Bottle Rocket", 0, ["Weapon", "Explosive", "Rocket", "Fireworks", "Consumable"]),
+
+ 145: create_gift("Big Bottle Rocket", 0, ["Weapon", "Explosive", "Rocket", "Fireworks", "Consumable"]),
+
+ 146: create_gift("Multi Bottle Rocket", 0, ["Weapon", "Explosive", "Rocket", "Fireworks", "Consumable"]),
+
+ 147: create_gift("Bomb", 0, ["Weapon", "Explosive", "Throwing", "Consumable", "Bomb"]),
+
+ 148: create_gift("Super Bomb", 0, ["Weapon", "Explosive", "Throwing", "Consumable", "Bomb"]),
+
+ 149: create_gift("Insecticide Spray", 0, ["Can", "Metal", "Insect", "Weapon", "Consumable", "Chemicals"]),
+
+ 150: create_gift("Rust Promoter", 0, ["Can", "Metal", "Rusting", "Weapon", "Consumable", "Chemicals"]),
+
+ 151: create_gift("Rust Promoter DX", 0, ["Can", "Metal", "Rusting", "Weapon", "Consumable", "Chemicals", "Insect"]),
+
+ 152: create_gift("Pair of Dirty Socks", 0, ["Consumable", "Throwing", "Stinky", "Clothing"]),
+
+ 153: create_gift("Stag Beetle", 0, ["Consumable", "Throwing", "Animal", "Insect"]),
+
+ 154: create_gift("Toothbrush", 0, ["Consumable", "Tool"]),
+
+ 155: create_gift("Handbag Strap", 0, ["Consumable", "Weapon", "Throwing", "Leather"]),
+
+ 156: create_gift("Pharaoh's Curse", 0, ["Consumable", "Weapon", "Throwing", "Goo", "Slime", "Poison", "Chemicals"]),
+
+ 157: create_gift("Defense Shower", 0, ["Can", "Machine", "Chemicals", "Buff", "Defense", "Liquid"]),
+
+ 159: create_gift("Sudden Guts Pill", 0, ["Consumable", "Guts", "Buff"]),
+
+ 160: create_gift("Bag of Dragonite", 0, ["Consumable", "Weapon", "Powder"]),
+
+ 161: create_gift("Defense Spray", 0, ["Can", "Consumable", "Chemicals", "Buff", "Defense", "Liquid"]),
+
+ 165: create_gift("Picture Postcard", 0, ["Paper", "Trash"]),
+
+ 168: create_gift("Chick", 0, ["Animal", "Yellow", "Chicken"]),
+
+ 169: create_gift("Chicken", 0, ["Animal", "White", "Chicken"]),
+
+ 186: create_gift("Meteotite", 0, ["Mineral", "Artifact", "Brown", "Gem"]),
+
+ 188: create_gift("Hand-Aid", 0, ["Consumable", "Heal", "Cloth"]),
+
+ 189: create_gift("Trout Yogurt", 0, ["Consumable", "Heal", "Food", "Fish", "Dairy"]),
+
+ 190: create_gift("Banana", 0, ["Consumable", "Heal", "Food", "Fruit", "Yellow"]),
+
+ 191: create_gift("Calorie Stick", 0, ["Consumable", "Heal", "Food", "Jerky"]),
+
+ 194: create_gift("Earth Pendant", 0, ["Armor", "Jewelry", "Fire", "Ice", "Light"]),
+
+ 195: create_gift("Neutralizer", 0, ["Machine", "Electronics", "Metal", "Neutralizing"]),
+
+ 198: create_gift("Gelato de Resort", 0, ["Consumable", "Food", "Heal", "Dairy", "FrozenFood"]),
+
+ 199: create_gift("Snake", 0, ["Animal", "Weapon", "Throwing", "Consumable"]),
+
+ 200: create_gift("Viper", 0, ["Animal", "Weapon", "Throwing", "Poison", "Consumable"]),
+
+ 201: create_gift("Brain Stone", 0, ["Stone", "Mineral", "Trash"]),
+
+ 207: create_gift("Magic Tart", 0, ["Food", "Consumable", "Mana", "Confectionary"]),
+
+ 209: create_gift("Monkey's Love", 0, ["Weapon", "Animal"]),
+
+ 212: create_gift("T-Rex's Bat", 0, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 213: create_gift("Big League Bat", 0, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 214: create_gift("Ultimate Bat", 0, ["MeleeWeapon", "Wood", "Baseball", "Toy", "Weapon"]),
+
+ 215: create_gift("Double Beam", 0, ["RangedWeapon", "Gun", "Beam", "Weapon"]),
+
+ 216: create_gift("Platinum Band", 0, ["Armor", "Defense", "Platinum", "Jewelry"]),
+
+ 217: create_gift("Diamond Band", 0, ["Armor", "Defense", "Diamond", "Jewelry"]),
+
+ 218: create_gift("Defense Ribbon", 0, ["Armor", "Cloth", "Defense"]),
+
+ 219: create_gift("Talisman Ribbon", 0, ["Armor", "Cloth", "Defense"]),
+
+ 220: create_gift("Saturn Ribbon", 0, ["Armor", "Cloth", "Defense"]),
+
+ 221: create_gift("Coin of Silence", 0, ["Armor", "Defense"]),
+
+ 222: create_gift("Charm Coin", 0, ["Armor", "Defense"]),
+
+ 223: create_gift("Cup of Noodles", 0, ["Food", "Consumable", "Heal", "FastFood", "Pasta"]),
+
+ 224: create_gift("Repel Sandwich", 0, ["Food", "Consumable", "Heal", "Repellant", "Sandwich"]),
+
+ 225: create_gift("Repel Superwich", 0, ["Food", "Consumable", "Heal", "Repellant", "Sandwich"]),
+
+ 226: create_gift("Lucky Sandwich", 0, ["Food", "Consumable", "Heal", "Luck", "Mana", "Sandwich"]),
+
+ 232: create_gift("Cup of Coffee", 0, ["Drink", "Consumable", "Heal", "Liquid", "Coffee"]),
+
+ 233: create_gift("Double Burger", 0, ["FastFood", "Comsumable", "Heal", "Food", "Beef", "Meat"]),
+
+ 234: create_gift("Peanut Cheese Bar", 0, ["Comsumable", "Heal", "Food", "Candy", "ExoticFood"]),
+
+ 235: create_gift("Piggy Jelly", 0, ["Comsumable", "Heal", "Food", "ExoticFood", "Gelatin", "Jelly"]),
+
+ 236: create_gift("Bowl of Rice Gruel", 0, ["Comsumable", "Heal", "Food", "Cooking", "ExoticFood", "Liquid"]),
+
+ 237: create_gift("Bean Croquette", 0, ["Comsumable", "Heal", "Food", "Cooking", "ExoticFood"]),
+
+ 238: create_gift("Molokheiya Soup", 0, ["Comsumable", "Heal", "Food", "Cooking", "ExoticFood", "Vegetable",
+ "Liquid"]),
+
+ 239: create_gift("Plain Roll", 0, ["Comsumable", "Heal", "Food", "Bread"]),
+
+ 240: create_gift("Kabob", 0, ["Comsumable", "Heal", "Food", "ExoticFood", "Meat"]),
+
+ 241: create_gift("Plain Yogurt", 0, ["Comsumable", "Heal", "Food", "Slime", "Dairy"]),
+
+ 242: create_gift("Beef Jerky", 0, ["Comsumable", "Heal", "Food", "Meat", "Dried", "Jerky"]),
+
+ 243: create_gift("Mammoth Burger", 0, ["FastFood", "Comsumable", "Heal", "Food", "Beef", "Meat"]),
+
+ 244: create_gift("Spicy Jerky", 0, ["Comsumable", "Heal", "Food", "Meat", "Dried", "Jerky", "Spicy"]),
+
+ 245: create_gift("Luxury Jerky", 0, ["Comsumable", "Heal", "Food", "Meat", "Dried", "Jerky", "Luxury"]),
+
+ 246: create_gift("Bottle of DXwater", 0, ["Comsumable", "Mana", "Drink", "Liquid", "Water"]),
+
+ 247: create_gift("Magic Pudding", 0, ["Comsumable", "Mana", "Food", "Candy"]),
+
+ 248: create_gift("Non-Stick Frypan", 0, ["MeleeWeapon", "Metal", "Tool", "Weapon"]),
+
+ 249: create_gift("Mr. Saturn Coin", 0, ["Armor", "Defense"]),
+
+ 250: create_gift("Meteornium", 0, ["Mineral", "Artifact", "SpaceMineral"]),
+
+ 251: create_gift("Popsicle", 0, ["Consumable", "Food", "Heal", "FrozenFood"]),
+
+ 252: create_gift("Cup of Lifenoodles", 0, ["Consumable", "Food", "Cure", "Life", "Pasta"])
+ # Todo; separate traits for GoodWeapon and BadWeapon
+ # Todo; Satus heals should be Medicine, Cure
+}
diff --git a/worlds/earthbound/gifting/trait_parser.py b/worlds/earthbound/gifting/trait_parser.py
new file mode 100644
index 000000000000..c721be44ab0a
--- /dev/null
+++ b/worlds/earthbound/gifting/trait_parser.py
@@ -0,0 +1,454 @@
+from ..game_data.local_data import item_id_table
+import random
+from typing import Any
+
+gift_exclusions = [
+ "Franklin Badge",
+ "Pak of Bubble Gum",
+ "Jar of Fly Honey",
+ "Tiny Key",
+ "Yogurt Dispenser",
+ "UFO Engine",
+ "Piggy Nose",
+ "Shyness Book",
+ "King Banana",
+ "Letter For Tony",
+ "Key to the Shack",
+ "Key to the Cabin",
+ "Bad Key Machine",
+ "Zombie Paper",
+ "Hawk Eye",
+ "ATM Card",
+ "Show Ticket",
+ "Tenda Lavapants",
+ "Wad of Bills",
+ "Receiver Phone",
+ "Diamond",
+ "Signed Banana",
+ "Pencil Eraser",
+ "Hieroglyph Copy",
+ "Contact Lens",
+ "Key to the Tower",
+ "Meteorite Piece",
+ "Sound Stone",
+ "Police Badge",
+ "Mining Permit",
+ "Key to the Locker",
+ "Insignificant Item",
+ "Tiny Ruby",
+ "Eraser Eraser",
+ "Tendakraut",
+ "Progressive Bat",
+ "Progressive Fry Pan",
+ "Progressive Gun",
+ "Progressive Bracelet",
+ "Progressive Other",
+ "Carrot Key"
+]
+
+wanted_traits = [
+ "Armor",
+ "Weapon",
+ "Cure",
+ "Bomb",
+ "Mana",
+ "Heal",
+ "Life",
+ "Neutralizing",
+ "Draining",
+ "Beef",
+ "Jerky",
+ "Egg",
+ "Chicken",
+ "Spicy",
+ "Broken",
+ "Pasta",
+ "Pizza",
+ "Condiment",
+ "Dairy",
+ "AnimalProduct",
+ "Copper",
+ "Silver",
+ "Gold",
+ "Diamond",
+ "Plastic",
+ "Herb",
+ "Repellant",
+ "Slime",
+ "Animal",
+ "Juice",
+ "Meat",
+ "Water",
+ "Drink",
+ "FastFood",
+ "Bread",
+ "FrozenFood",
+ "Fruit",
+ "Toy",
+ "Salted",
+ "Speed",
+ "Guts",
+ "Luck",
+ "Doll",
+ "Legendary",
+ "Buff",
+ "Pipe",
+ "Hat",
+ "Trash",
+ "ExoticFood",
+ "Insect",
+ "Fire",
+ "Ice",
+ "Light",
+ "Food",
+ "Consumable",
+ "Electronics",
+ "Candy",
+ "Medicine",
+ "Coffee",
+ "Artifact",
+ "Fireworks",
+ "Confectionary",
+ "Explosive",
+ "Jewelry",
+ "Rock",
+ "Metal"
+]
+
+# If these traits are in the item, then pick randomly from the results
+# If multiple fit, pick the combined highest quality.
+secondary_trait_list = {
+ "Beef": ["Hamburger", "Double Burger", "Mammoth Burger", "Beef Jerky"],
+ "Jerky": ["Beef Jerky", "Spicy Jerky", "Luxury Jerky"],
+ "Egg": ["Fresh Egg", "Boiled Egg"],
+ "Chicken": ["Chicken"],
+ "Spicy": ["Jar of Hot Sauce", "Spicy Jerky"],
+ "Broken": ["Broken Machine", "Broken Gadget", "Broken Air Gun", "Broken Spray Can",
+ "Broken Laser", "Broken Iron", "Broken Pipe", "Broken Cannon", "Broken Tube",
+ "Broken Bazooka", "Broken Trumpet", "Broken Harmonica", "Broken Antenna"],
+ "Pasta": ["Pasta di Summers", "Cup of Noodles", "Cup of Lifenoodles"],
+ "Pizza": ["Pizza", "Large Pizza"],
+ "Condiment": ["Ketchup Packet", "Sugar Packet", "Salt Packet", "Tin of Cocoa",
+ "Carton of Cream", "Sprig of Parsley", "Jar of Delisauce", "Jar of Hot Sauce"],
+ "Dairy": ["Plain Yogurt", "Trout Yogurt", "Gelato de Resort"],
+ "AnimalProduct": ["Fresh Egg"],
+ "Copper": ["Copper Bracelet"],
+ "Silver": ["Silver Bracelet"],
+ "Gold": ["Gold Bracelet"],
+ "Diamond": ["Diamond Band"],
+ "Plastic": ["Cheap Bracelet", "Bottle of Water", "Bottle of DXwater"],
+ "Herb": ["Refreshing Herb", "Secret Herb"],
+ "Repellant": ["Repel Sandwich", "Repel Superwich"],
+ "Slime": ["Slime Generator"],
+ "Animal": ["Chicken", "Chick", "Snake", "Viper"],
+ "Juice": ["Can of Fruit Juice"],
+ "Meat": ["Hamburger", "Double Burger", "Mammoth Burger", "Beef Jerky",
+ "Spicy Jerky", "Luxury Jerky", "Kabob"],
+ "Water": ["Bottle of Water", "Bottle of DXwater"],
+ "Drink": ["Bottle of Water", "Bottle of DXwater", "Cup of Coffee", "Can of Fruit Juice", "Protein Drink",
+ "Royal Iced Tea"],
+ "FastFood": ["Hamburger", "Ketchup Packet", "Double Burger", "Bag of Fries"],
+ "Bread": ["Plain Roll", "Bread Roll", "Croissant"],
+ "FrozenFood": ["Popsicle", "Gelato de Resort"],
+ "Fruit": ["Banana", "Can of Fruit Juice"],
+ "Toy": ["Toy Air Gun", "Teddy Bear", "Super Plush Bear", "Yo-yo", "Slingshot"],
+ "Salted": ["Salt Packet"],
+ "Speed": ["Speed Capsule", "Rabbit's Foot"],
+ "Guts": ["Guts Capsule", "Sudden Guts Pill", "Gutsy Bat"],
+ "Luck": ["Lucky Coin", "Luck Capsule", "Lucky Sandwich"],
+ "Doll": ["Teddy Bear", "Super Plush Bear"],
+ "Legendary": ["Legendary Bat"],
+ "Buff": ["Sudden Guts Pill", "Guts Capsule", "Speed Capsule", "IQ Capsule", "Luck Capsule", "Vital Capsule",
+ "Defense Spray", "Defense Shower", "Rock Candy"],
+ "Pipe": ["HP-Sucker", "Hungry HP-Sucker", "Broken Pipe"],
+ "Hat": ["Holmes Hat", "Hard Hat", "Baseball Cap", "Mr. Baseball Cap"],
+ "Trash": ["Broken Machine", "Broken Gadget", "Broken Air Gun", "Broken Spray Can",
+ "Broken Laser", "Broken Iron", "Broken Pipe", "Broken Cannon", "Broken Tube",
+ "Broken Bazooka", "Broken Trumpet", "Broken Harmonica", "Broken Antenna",
+ "Ruler", "Pair of Dirty Socks", "Protractor"],
+ "ExoticFood": ["Piggy Jelly", "Peanut Cheese Bar", "Bowl of Rice Gruel",
+ "Molokheiya Soup", "Kabob", "Bean Croquette", "Brain Food Lunch"],
+ "Insect": ["Insecticde Spray", "Xterminator Spray", "Stag Beetle"],
+ "Fire": ["Flame Pendant"],
+ "Ice": ["Rain Pendant"],
+ "Light": ["Night Pendant"],
+ "Electronics": ["Slime Generator", "Shield Killer", "Neutralizer", "Defense Shower", "Counter-PSI Unit",
+ "HP-Sucker", "Hungry HP-Sucker"],
+ "Candy": ["PSI Caramel", "Magic Truffle", "Rock Candy", "Magic Pudding", "Peanut Cheese Bar"],
+ "Medicine": ["Vial of Serum", "Cold Remedy", "IQ Capsule", "Guts Capsule", "Speed Capsule", "Vital Capsule", "Luck Capsule"],
+ "Coffee": ["Cup of Coffee"],
+ "Artifact": ["Metotite", "Meteornium"],
+ "Fireworks": ["Bottle Rocket", "Big Bottle Rocket", "Multi Bottle Rocket"],
+ "Confectionary": ["Cookie", "Magic Tart"],
+ "Explosive": ["Bottle Rocket", "Big Bottle Rocket", "Multi Bottle Rocket", "Heavy Bazooka",
+ "Bazooka", "Bomb", "Super Bomb"],
+ "Jewelry": ["Cheap Bracelet", "Copper Bracelet", "Silver Bracelet", "Gold Bracelet",
+ "Platinum Band", "Diamond Band", "Flame Pendant", "Sea Pendant", "Star Pendant", "Earth Pendant",
+ "Rain Pendant", "Night Pendant"],
+ "Rock": ["Rock Candy", "Brain Stone"],
+ "Metal": ["Broken Machine", "Broken Gadget", "Broken Air Gun", "Broken Spray Can",
+ "Broken Laser", "Broken Iron", "Broken Pipe", "Broken Cannon", "Broken Tube",
+ "Broken Bazooka", "Broken Trumpet", "Broken Harmonica", "Broken Antenna", "Slime Generator",
+ "Fry Pan", "Magic Fry Pan", "Thick Fry Pan", "Deluxe Fry Pan", "Chef's Fry Pan",
+ "French Fry Pan", "Holy Fry Pan", "Non-Stick Frypan"],
+
+ "Food": ["Cookie", "Bag of Fries", "Hamburger", "Boiled Egg", "Fresh Egg", "Picnic Lunch",
+ "Pasta di Summers", "Pizza", "Chef's Special", "Large Pizza", "PSI Caramel", "Magic Truffle",
+ "Brain Food Lunch", "Rock Candy", "Croissant", "Bread Roll", "Kraken Soup",
+ "Trout Yogurt", "Banana", "Calorie Stick", "Gelato de Resort", "Magic Tart",
+ "Cup of Noodles", "Repel Sandwich", "Repel Superwich", "Lucky Sandwich", "Double Burger",
+ "Peanut Cheese Bar", "Piggy Jelly", "Bowl of Rice Gruel", "Bean Croquette",
+ "Molokheiya Soup", "Plain Roll", "Kabob", "Plain Yogurt", "Beef Jerky",
+ "Mammoth Burger", "Spicy Jerky", "Luxury Jerky", "Magic Pudding",
+ "Popsicle"]
+
+}
+
+tertiary_trait_list = {
+ "Consumable": ["Cookie", "Bag of Fries", "Hamburger", "Boiled Egg", "Fresh Egg", "Picnic Lunch",
+ "Pasta di Summers", "Pizza", "Chef's Special", "Large Pizza", "PSI Caramel", "Magic Truffle",
+ "Brain Food Lunch", "Rock Candy", "Croissant", "Bread Roll", "Kraken Soup",
+ "Trout Yogurt", "Banana", "Calorie Stick", "Gelato de Resort", "Magic Tart",
+ "Cup of Noodles", "Repel Sandwich", "Repel Superwich", "Lucky Sandwich", "Double Burger",
+ "Peanut Cheese Bar", "Piggy Jelly", "Bowl of Rice Gruel", "Bean Croquette",
+ "Molokheiya Soup", "Plain Roll", "Kabob", "Plain Yogurt", "Beef Jerky",
+ "Mammoth Burger", "Spicy Jerky", "Luxury Jerky", "Magic Pudding",
+ "Popsicle", "Can of Fruit Juice", "Royal Iced Tea", "Protein Drink",
+ "Bottle of Water", "Cold Remedy", "Vial of Serum", "IQ Capsule",
+ "Guts Capsule", "Speed Capsule", "Vital Capsule", "Luck Capsule",
+ "Ketchup Packet", "Sugar Packet", "Tin of Cocoa", "Carton of Cream", "Sprig of Parsley",
+ "Jar of Hot Sauce", "Salt Packet", "Jar of Delisauce", "Wet Towel", "Refreshing Herb",
+ "Secret Herb", "Horn of Life", "Mummy Wrap", "Bottle Rocket", "Big Bottle Rocket",
+ "Multi Bottle Rocket", "Bomb", "Super Bomb", "Insecticide Spray", "Rust Promoter",
+ "Rust Promoter DX", "Pair of Dirty Socks", "Stag Beetle", "Toothbrush",
+ "Handbag Strap", "Pharaoh's Curse", "Sudden Guts Pill", "Bag of Dragonite",
+ "Defense Spray", "Chick", "Chicken", "Hand-Aid", "Snake", "Viper",
+ "Cup of Coffee", "Bottle of DXwater", "Cup of Lifenoodles"]
+}
+
+scaled_traits = [
+ "Armor",
+ "Weapon",
+ "Cure",
+ "Bomb",
+ "Mana",
+ "Heal",
+ "Life",
+ "Neutralizing",
+ "Draining"
+]
+
+gift_by_quality = {
+ "Heal": {
+ 0.06: "Cookie",
+ 0.08: "Can of Fruit Juice",
+ 0.12: "Cup of Coffee",
+ 0.18: "Popsicle",
+ 0.22: "Banana",
+ 0.24: "Bag of Fries",
+ 0.30: "Trout Yogurt",
+ 0.35: "Bread Roll",
+ 0.42: "Bean Croquette",
+ 0.43: "Cup of Noodles",
+ 0.45: "Boiled Egg",
+ 0.48: "Hamburger",
+ 0.60: "Royal Iced Tea",
+ 0.63: "Calorie Stick",
+ 0.65: "Croissant",
+ 0.70: "Lucky Sandwich",
+ 0.80: "Picnic Lunch",
+ 0.82: "Plain Roll",
+ 0.84: "Fresh Egg",
+ 0.88: "Molokheiya Soup",
+ 0.96: "Double Burger",
+ 1.00: "Peanut Cheese Bar",
+ 1.10: "Pasta di Summers",
+ 1.20: "Pizza",
+ 1.26: "Kabob",
+ 1.50: "Beef Jerky",
+ 1.60: "Plain Yogurt",
+ 2.05: "Mammoth Burger",
+ 2.16: "Bowl of Rice Gruel",
+ 2.20: "Chef's Special",
+ 2.52: "Spicy Jerky",
+ 2.40: "Large Pizza",
+ 3.00: "Piggy Jelly",
+ 3.10: "Luxury Jerky",
+ 3.50: "Brain Food Lunch",
+ 4.00: "Kraken Soup",
+ 4.01: "Hand-Aid"
+ },
+
+ "Armor": {
+ 0.05: "Travel Charm",
+ 0.10: "Great Charm",
+ 0.12: "Cheap Bracelet",
+ 0.13: "Baseball Cap",
+ 0.14: "Mr. Baseball Cap",
+ 0.24: "Copper Bracelet",
+ 0.26: "Holmes Hat",
+ 0.20: "Crystal Charm",
+ 0.36: "Silver Bracelet",
+ 0.38: "Hard Hat",
+ 0.48: "Ribbon",
+ 0.50: "Diadem of Kings",
+ 0.60: "Red Ribbon",
+ 0.73: "Gold Bracelet",
+ 0.75: "Bracer of Kings",
+ 0.78: "Coin of Slumber",
+ 0.97: "Platinum Band",
+ 0.98: "Defense Ribbon",
+ 0.99: "Coin of Defense",
+ 1.00: "Cloak of Kings",
+ 1.21: "Diamond Band",
+ 1.25: "Lucky Coin",
+ 1.46: "Pixie's Bracelet",
+ 1.48: "Talisman Coin",
+ 1.50: "Talisman Ribbon",
+ 1.70: "Cherub's Band",
+ 1.75: "Shiny Coin",
+ 1.95: "Goddess Band",
+ 2.00: "Souvenir Coin",
+ 2.19: "Saturn Ribbon",
+ 2.68: "Goddess Ribbon"
+ },
+
+ "Draining": {
+ 0.50: "HP-Sucker",
+ 1.00: "Hungry HP-Sucker"
+ },
+
+ "Bomb": {
+ 0.50: "Bomb",
+ 1.00: "Super Bomb"
+ },
+
+ "Neutralizing": {
+ 0.50: "Shield Killer",
+ 1.00: "Neutralizer"
+ },
+
+ "Cure": {
+ 0.10: "Cold Remedy",
+ 0.25: "Vial of Serum",
+ 0.50: "Wet Towel",
+ 1.00: "Refreshing Herb",
+ 2.00: "Secret Herb",
+ 3.00: "Horn of Life",
+ 3.01: "Cup of Lifenoodles"
+ },
+
+ "Life": {
+ 0.50: "Secret Herb",
+ 1.00: "Cup of Lifenoodles",
+ 1.01: "Horn of Life",
+ },
+
+ "Weapon": {
+ 0.01: "Casey Bat",
+ 0.04: "Cracked Bat",
+ 0.11: "Yo-yo",
+ 0.15: "Tee Ball Bat",
+ 0.19: "Fy Pan",
+ 0.23: "Slingshot",
+ 0.28: "Sand Lot Bat",
+ 0.30: "Pop Gun",
+ 0.38: "Thick Fry Pan",
+ 0.40: "Bionic Slingshot",
+ 0.46: "Stun Gun",
+ 0.50: "Minor League Bat",
+ 0.57: "Deluxe Fry Pan",
+ 0.61: "Toy Air Gun",
+ 0.69: "Magnum Air Gun",
+ 0.73: "Mr. Baseball Bat",
+ 0.76: "Chef's Fry Pan",
+ 0.78: "Zip Gun",
+ 0.88: "Trick Yo-yo",
+ 0.92: "Laser Gun",
+ 0.95: "T-Rex's Bat",
+ 0.96: "Non-Stick Frypan",
+ 1.00: "Big League Bat",
+ 1.03: "Combat Yo-yo",
+ 1.11: "Hyper Beam",
+ 1.15: "French Fry Pan",
+ 1.19: "Hall of Fame Bat",
+ 1.26: "Double Beam",
+ 1.32: "Ultimate Bat",
+ 1.38: "Crusher Beam",
+ 1.50: "Spectrum Beam",
+ 1.53: "Magicant Bat",
+ 1.73: "Death Ray",
+ 1.80: "Sword of Kings",
+ 1.88: "Baddest Beam",
+ 2.00: "Magic Fry Pan",
+ 2.11: "Legendary Bat",
+ 2.15: "Moon Beam Gun",
+ 2.40: "Holy Fry Pan",
+ 2.45: "Gaia Beam",
+ 2.55: "Gutsy Bat",
+ },
+
+ "Mana": {
+ 0.28: "Bottle of Water",
+ 0.50: "PSI Caramel",
+ 0.51: "Magic Tart",
+ 1.14: "Bottle of DXwater",
+ 1.20: "Magic Pudding",
+ 2.28: "Magic Truffle"
+ }
+}
+
+
+def trait_interpreter(gift: dict[str, Any]) -> int:
+ """Converts received gifts into in-game items.
+ If the item name perfectly matches an in-game item, that item will be received.
+ If any of the traits can be scaled i.e. a healing item, the gift will be converted into an item of roughly that value.
+ If any of the traits are not scaled, but are in the secondary trait list i.e. a food item, it will be converted into a random appropriate item.
+ If none of the traits are applicable, return a random consumable."""
+ item = None
+ trait_list = []
+ got_trait = False
+ if "Traits" in gift:
+ gift["traits"] = gift.pop("Traits")
+
+ for trait in gift["traits"]:
+ if "Trait" in trait:
+ trait["trait"] = trait.pop("Trait")
+
+ if "Quality" in trait:
+ trait["quality"] = trait.pop("Quality")
+
+ if "quality" not in trait:
+ trait["quality"] = 1
+
+ trait_list.append(trait["trait"])
+ if trait["trait"] in scaled_traits:
+ item_quality_table = gift_by_quality[trait["trait"]]
+ quality = min(item_quality_table.keys(), key=lambda x: abs(x - trait["quality"]))
+ item = item_quality_table[quality]
+ got_trait = True
+ break
+
+ if not got_trait:
+ for trait in trait_list:
+ if trait in secondary_trait_list:
+ item = random.choice(secondary_trait_list[trait])
+ got_trait = True
+ break
+
+ if not got_trait:
+ for trait in trait_list:
+ if trait in tertiary_trait_list:
+ item = random.choice(tertiary_trait_list[trait])
+ break
+
+ if item is not None:
+ item = item_id_table[item]
+ else:
+ item = random.choice(secondary_trait_list["Consumable"])
+ item = item_id_table[item]
+ return item
+
+
+# IF trait is in special traits, give that item.
+# Else if the trait is in a Scaled trait (Food, Armor, etc., then break them up by scaling)
diff --git a/worlds/earthbound/modules/__init__.py b/worlds/earthbound/modules/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/earthbound/modules/area_scaling.py b/worlds/earthbound/modules/area_scaling.py
new file mode 100644
index 000000000000..a29380f9a0ee
--- /dev/null
+++ b/worlds/earthbound/modules/area_scaling.py
@@ -0,0 +1,603 @@
+from ..modules.enemy_data import combat_regions
+from ..Options import MagicantMode
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from .. import EarthBoundWorld
+
+
+expected_level_gains = {
+ "Ness's Mind": 0,
+ "Global ATM Access": 0,
+ "Northern Onett": 1,
+ "Onett": 1,
+ "Arcade": 0,
+ "Giant Step": 2,
+ "Twoson": 0,
+ "Everdred's House": 1,
+ "Common Condiment Shop": 0,
+ "Peaceful Rest Valley": 2,
+ "Happy-Happy Village": 0,
+ "Happy-Happy HQ": 1,
+ "Lilliput Steps": 2,
+ "Threed": 0,
+ "Boogey Tent": 0,
+ "Threed Underground": 2,
+ "Grapefruit Falls": 1,
+ "Saturn Valley": 0,
+ "Belch's Factory": 2,
+ "Upper Saturn Valley": 0,
+ "Milky Well": 3,
+ "Dusty Dunes Desert": 2,
+ "Gold Mine": 2,
+ "Monkey Caves": 2,
+ "Fourside": 0,
+ "Fourside Dept. Store": 1,
+ "Moonside": 1,
+ "Monotoli Building": 2,
+ "Magnet Hill": 2,
+ "Winters": 2,
+ "Snow Wood Boarding School": 0,
+ "Southern Winters": 1,
+ "Brickroad Maze": 1,
+ "Andonuts Lab Area": 0,
+ "Rainy Circle": 2,
+ "Stonehenge Base": 2,
+ "Summers": 0,
+ "Summers Museum": 0,
+ "Dalaam": 0,
+ "Pink Cloud": 2,
+ "Scaraba": 0,
+ "Pyramid": 2,
+ "Southern Scaraba": 1,
+ "Dungeon Man": 2,
+ "Deep Darkness": 0,
+ "Deep Darkness Darkness": 2,
+ "Tenda Village": 0,
+ "Lumine Hall": 2,
+ "Lost Underworld": 2,
+ "Fire Spring": 2,
+ "Magicant": 1,
+ "Sea of Eden": 1,
+ "Cave of the Present": 0,
+ "Cave of the Past": 2,
+ "Endgame": 0
+}
+
+locations_with_item_requirements = [
+ "Onett - Traveling Entertainer",
+ "Onett - South Road Present",
+ "Onett - Tracy Gift",
+ "Twoson - Paula's Mother",
+ "Twoson - Everdred Meeting",
+ "Twoson - Insignificant Location",
+ "Happy-Happy Village - Defeat Carpainter",
+ "Happy-Happy Village - Prisoner",
+ "Threed - Boogey Tent Trashcan",
+ "Threed - Zombie Prisoner",
+ "Saturn Valley - Post Belch Gift #1",
+ "Saturn Valley - Post Belch Gift #2",
+ "Saturn Valley - Post Belch Gift #3",
+ "Saturn Valley - Saturn Coffee",
+ "Monkey Caves - Talah Rama Chest #1",
+ "Monkey Caves - Talah Rama Chest #2",
+ "Monkey Caves - Talah Rama Gift",
+ "Monkey Caves - Monkey Power",
+ "Dusty Dunes - Mine Reward",
+ "Snow Wood - Upper Right Locker",
+ "Snow Wood - Upper Left Locker",
+ "Snow Wood - Bottom Right Locker",
+ "Snow Wood - Bottom Left Locker",
+ "Fourside - Bakery 2F Gift",
+ "Fourside - Department Store Blackout",
+ "Fourside - Venus Gift",
+ "Summers - Museum Item",
+ "Dalaam - Trial of Mu",
+ "Deep Darkness - North Alcove Truffle",
+ "Deep Darkness - Near Land Truffle",
+ "Deep Darkness - Present Truffle",
+ "Deep Darkness - Village Truffle",
+ "Deep Darkness - Entrance Truffle",
+ "Tenda Village - Tenda Tea",
+ "Tenda Village - Tenda Gift",
+ "Tenda Village - Tenda Gift #2",
+ "Lost Underworld - Talking Rock",
+ "Lost Underworld - Tenda Camp Shop Slot 1",
+ "Lost Underworld - Tenda Camp Shop Slot 2",
+ "Lost Underworld - Tenda Camp Shop Slot 3",
+ "Lost Underworld - Tenda Camp Shop Slot 4",
+ "Lost Underworld - Tenda Camp Shop Slot 5",
+ "Lost Underworld - Tenda Camp Shop Slot 6",
+ "Lost Underworld - Tenda Camp Shop Slot 7",
+ "Dusty Dunes - Mine Food Cart Slot 1",
+ "Dusty Dunes - Mine Food Cart Slot 2",
+ "Dusty Dunes - Mine Food Cart Slot 3",
+ "Dusty Dunes - Mine Food Cart Slot 4",
+ "Dusty Dunes - Mine Food Cart Slot 5",
+ "Dusty Dunes - Mine Food Cart Slot 6",
+ "Dusty Dunes - Mine Food Cart Slot 7",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 1",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 2",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 3",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 4",
+ "Deep Darkness - Arms Dealer Slot 1",
+ "Deep Darkness - Arms Dealer Slot 2",
+ "Deep Darkness - Arms Dealer Slot 3",
+ "Deep Darkness - Arms Dealer Slot 4",
+ "Deep Darkness - Businessman Slot 1",
+ "Deep Darkness - Businessman Slot 2",
+ "Deep Darkness - Businessman Slot 3",
+ "Deep Darkness - Businessman Slot 4",
+ "Deep Darkness - Businessman Slot 5",
+ "Deep Darkness - Businessman Slot 6",
+ "Deep Darkness - Businessman Slot 7",
+ "Dalaam Restaurant - Slot 1",
+ "Dalaam Restaurant - Slot 2",
+ "Dalaam Restaurant - Slot 3",
+ "Dalaam Restaurant - Slot 4"]
+
+
+def calculate_scaling(world: "EarthBoundWorld") -> None:
+ """Calculates the individual scaled level of each region/major area."""
+ arcade = world.dungeon_connections["Arcade"]
+ giant_step = world.dungeon_connections["Giant Step"]
+ lilliput_steps = world.dungeon_connections["Lilliput Steps"]
+ happy_happy_hq = world.dungeon_connections["Happy-Happy HQ"]
+ belch_factory = world.dungeon_connections["Belch's Factory"]
+ milky_well = world.dungeon_connections["Milky Well"]
+ gold_mine = world.dungeon_connections["Gold Mine"]
+ monotoli_building = world.dungeon_connections["Monotoli Building"]
+ magnet_hill = world.dungeon_connections["Magnet Hill"]
+ moonside = world.dungeon_connections["Moonside"]
+ pink_cloud = world.dungeon_connections["Pink Cloud"]
+ rainy_circle = world.dungeon_connections["Rainy Circle"]
+ stonehenge_base = world.dungeon_connections["Stonehenge Base"]
+ brickroad_maze = world.dungeon_connections["Brickroad Maze"]
+ pyramid = world.dungeon_connections["Pyramid"]
+ dungeon_man = world.dungeon_connections["Dungeon Man"]
+ lumine_hall = world.dungeon_connections["Lumine Hall"]
+ fire_spring = world.dungeon_connections["Fire Spring"]
+ sea_of_eden = world.dungeon_connections["Sea of Eden"]
+ world.area_exits = {
+ "Ness's Mind": ["Onett", "Twoson", "Happy-Happy Village", "Threed", "Saturn Valley", "Dusty Dunes Desert",
+ "Fourside", "Winters", "Summers", "Dalaam", "Scaraba", "Deep Darkness", "Tenda Village",
+ "Lost Underworld", "Magicant"],
+ "Northern Onett": ["Onett"],
+ "Onett": ["Northern Onett", "Twoson", giant_step, arcade, "Global ATM Access"],
+ arcade: [arcade],
+ "Giant Step": ["Giant Step"],
+ "Twoson": ["Onett", "Peaceful Rest Valley", "Threed", "Everdred's House", "Common Condiment Shop", "Global ATM Access"],
+ "Everdred's House": ["Everdred's House"],
+ "Peaceful Rest Valley": ["Twoson", "Happy-Happy Village"],
+ "Happy-Happy Village": ["Peaceful Rest Valley", lilliput_steps, happy_happy_hq, "Global ATM Access"],
+ "Happy-Happy HQ": ["Happy-Happy HQ"],
+ "Lilliput Steps": ["Lilliput Steps"],
+ "Threed": ["Twoson", "Dusty Dunes Desert", "Andonuts Lab Area", "Threed Underground", "Boogey Tent", "Winters", "Global ATM Access"],
+ "Boogey Tent": ["Boogey Tent"],
+ "Threed Underground": ["Grapefruit Falls"],
+ "Grapefruit Falls": [belch_factory, "Saturn Valley", "Threed Underground"],
+ "Saturn Valley": ["Grapefruit Falls", "Cave of the Present", "Global ATM Access"],
+ belch_factory: ["Upper Saturn Valley"],
+ "Upper Saturn Valley": ["Saturn Valley", world.dungeon_connections["Milky Well"]],
+ "Milky Well": ["Milky Well"],
+ "Dusty Dunes Desert": ["Threed", "Monkey Caves", gold_mine, "Fourside", "Global ATM Access"],
+ "Monkey Caves": ["Monkey Caves"],
+ "Gold Mine": ["Gold Mine"],
+ "Fourside": ["Dusty Dunes Desert", monotoli_building, magnet_hill, "Threed", "Fourside Dept. Store", moonside, "Global ATM Access"],
+ "Moonside": ["Moonside", "Global ATM Access"],
+ "Monotoli Building": ["Monotoli Building"],
+ "Fourside Dept. Store": ["Fourside Dept. Store"],
+ "Magnet Hill": ["Magnet Hill"],
+ "Winters": ["Snow Wood Boarding School", "Southern Winters", "Global ATM Access"],
+ "Snow Wood Boarding School": ["Snow Wood Boarding School"],
+ "Southern Winters": [brickroad_maze],
+ brickroad_maze: [rainy_circle, "Southern Winters"],
+ "Stonehenge Base": ["Stonehenge Base"],
+ rainy_circle: [brickroad_maze, "Andonuts Lab Area"],
+ "Andonuts Lab Area": [rainy_circle, "Winters", stonehenge_base],
+ "Summers": ["Scaraba", "Summers Museum", "Global ATM Access"],
+ "Summers Museum": ["Summers Museum"],
+ "Dalaam": [pink_cloud],
+ "Pink Cloud": ["Pink Cloud"],
+ "Scaraba": [pyramid, "Common Condiment Shop", "Global ATM Access"],
+ pyramid: ["Southern Scaraba"],
+ "Southern Scaraba": [dungeon_man],
+ "Dungeon Man": ["Deep Darkness"],
+ "Deep Darkness": ["Deep Darkness Darkness"],
+ "Deep Darkness Darkness": ["Tenda Village", "Deep Darkness"],
+ "Tenda Village": [lumine_hall, "Deep Darkness Darkness"],
+ "Lumine Hall": ["Lost Underworld"],
+ "Lost Underworld": [fire_spring],
+ "Fire Spring": ["Fire Spring"],
+ "Magicant": [sea_of_eden, "Global ATM Access"],
+ "Sea of Eden": ["Sea of Eden"],
+ "Cave of the Present": ["Cave of the Past"],
+ "Cave of the Past": ["Endgame"],
+ "Endgame": ["Endgame"],
+ "Global ATM Access": ["Global ATM Access"],
+ "Common Condiment Shop": ["Common Condiment Shop"]
+ }
+
+ world.area_rules = {
+ "Ness's Mind": {"Onett": [["Onett Teleport"]],
+ "Twoson": [["Twoson Teleport"]],
+ "Happy-Happy Village": [["Happy-Happy Village Teleport"]],
+ "Threed": [["Threed Teleport"]],
+ "Saturn Valley": [["Saturn Valley Teleport"]],
+ "Dusty Dunes Desert": [["Dusty Dunes Teleport"]],
+ "Fourside": [["Fourside Teleport"]],
+ "Winters": [["Winters Teleport"]],
+ "Summers": [["Summers Teleport"]],
+ "Dalaam": [["Dalaam Teleport"]],
+ "Scaraba": [["Scaraba Teleport"]],
+ "Deep Darkness": [["Deep Darkness Teleport"]],
+ "Tenda Village": [["Tenda Village Teleport"]],
+ "Lost Underworld": [["Lost Underworld Teleport"]],
+ "Magicant": [["Magicant Teleport"], ["Magicant Unlock"]]
+ },
+
+ "Northern Onett": {"Onett": [["Nothing"]]},
+ "Onett":
+ {"Northern Onett": [["Police Badge"]],
+ "Twoson": [["Police Badge"]],
+ giant_step: [["Key to the Shack"]],
+ arcade: [["Nothing"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ arcade: {arcade: [["Nothing"]]},
+ "Giant Step": {"Giant Step": [["Nothing"]]},
+
+ "Twoson": {"Onett": [["Police Badge"]],
+ "Peaceful Rest Valley": [["Pencil Eraser"], ["Valley Bridge Repair"]],
+ "Threed": [["Wad of Bills"], ["Threed Tunnels Clear"]],
+ "Everdred's House": [["Paula"]],
+ "Common Condiment Shop": [["Nothing"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ "Everdred's House": {"Everdred's House": [["Nothing"]]},
+
+ "Peaceful Rest Valley": {"Twoson": [["Pencil Eraser"], ["Valley Bridge Repair"]],
+ "Happy-Happy Village": [["Nothing"]]},
+
+ "Happy-Happy Village": {"Peaceful Rest Valley": [["Nothing"]],
+ lilliput_steps: [["Nothing"]],
+ happy_happy_hq: [["Nothing"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ "Happy-Happy HQ": {"Happy-Happy HQ": [["Nothing"]]},
+
+ "Lilliput Steps": {"Lilliput Steps": [["Nothing"]]},
+
+ "Threed": {"Twoson": [["Threed Tunnels Clear"]],
+ "Dusty Dunes Desert": [["Threed Tunnels Clear"]],
+ "Andonuts Lab Area": [["UFO Engine", "Bad Key Machine"]],
+ "Threed Underground": [["Zombie Paper"]],
+ "Boogey Tent": [["Jeff"]],
+ "Winters": [["UFO Engine", "Bad Key Machine"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ "Boogey Tent": {"Boogey Tent": [["Nothing"]]},
+
+ "Threed Underground": {"Grapefruit Falls": [["Nothing"]]},
+
+ "Grapefruit Falls": {belch_factory: [["Jar of Fly Honey"]],
+ "Saturn Valley": [["Nothing"]],
+ "Threed Underground": [["Nothing"]]},
+
+ "Saturn Valley": {"Grapefruit Falls": [["Nothing"]],
+ "Cave of the Present": [["Meteorite Piece"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ belch_factory: {"Upper Saturn Valley": [["Threed Tunnels Clear"]]},
+
+ "Upper Saturn Valley": {"Saturn Valley": [["Nothing"]],
+ milky_well: [["Nothing"]]},
+
+ "Milky Well": {"Milky Well": [["Nothing"]]},
+
+ "Dusty Dunes Desert": {"Threed": [["Threed Tunnels Clear"]],
+ "Monkey Caves": [["King Banana"]],
+ gold_mine: [["Mining Permit"]],
+ "Fourside": [["Nothing"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ "Monkey Caves": {"Monkey Caves": [["Nothing"]]},
+
+ "Gold Mine": {"Gold Mine": [["Nothing"]]},
+
+ "Fourside": {"Dusty Dunes Desert": [["Nothing"]],
+ monotoli_building: [["Yogurt Dispenser"]],
+ "Threed": [["Diamond"]],
+ magnet_hill: [["Signed Banana"]],
+ "Fourside Dept. Store": [["Jeff"]],
+ moonside: [["Nothing"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ "Monotoli Building": {"Monotoli Building": [["Nothing"]]},
+
+ "Moonside": {"Moonside": [["Nothing"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ "Fourside Dept. Store": {"Fourside Dept. Store": [["Nothing"]]},
+
+ "Magnet Hill": {"Magnet Hill": [["Nothing"]]},
+
+ "Winters": {"Snow Wood Boarding School": [["Letter For Tony"]],
+ "Southern Winters": [["Pak of Bubble Gum"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ "Snow Wood Boarding School": {"Snow Wood Boarding School": [["Nothing"]]},
+
+ "Southern Winters": {brickroad_maze: [["Nothing"]]},
+
+ brickroad_maze: {rainy_circle: [["Nothing"]],
+ "Southern Winters": [["Nothing"]],
+ brickroad_maze: [["Nothing"]]},
+
+ rainy_circle: {rainy_circle: [["Nothing"]],
+ "Andonuts Lab Area": [["Nothing"]],
+ brickroad_maze: [["Nothing"]]},
+
+ "Andonuts Lab Area": {rainy_circle: [["Nothing"]],
+ stonehenge_base: [["Eraser Eraser"]],
+ "Winters": [["Nothing"]]},
+
+ "Stonehenge Base": {"Stonehenge Base": [["Nothing"]]},
+
+ "Summers": {"Scaraba": [["Nothing"]],
+ "Summers Museum": [["Tiny Ruby"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ "Summers Museum": {"Summers Museum": [["Nothing"]]},
+
+ "Dalaam": {pink_cloud: [["Carrot Key"]]},
+
+ "Pink Cloud": {"Pink Cloud": [["Nothing"]]},
+
+ "Scaraba": {pyramid: [["Hieroglyph Copy"]],
+ "Common Condiment Shop": [["Nothing"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ pyramid: {"Southern Scaraba": [["Nothing"]]},
+
+ "Southern Scaraba": {dungeon_man: [["Key to the Tower"]]},
+
+ "Dungeon Man": {"Deep Darkness": [["Submarine to Deep Darkness"]]},
+
+ "Deep Darkness": {"Deep Darkness Darkness": [["Hawk Eye"]]},
+
+ "Deep Darkness Darkness": {"Tenda Village": [["Nothing"]],
+ "Deep Darkness": [["Nothing"]]},
+
+ "Tenda Village": {lumine_hall: [["Shyness Book"]],
+ "Deep Darkness Darkness": [["Hawk Eye"]]},
+
+ "Lumine Hall": {"Lost Underworld": [["Nothing"]]},
+
+ "Lost Underworld": {fire_spring: [["Nothing"]]},
+
+ "Fire Spring": {"Fire Spring": [["Nothing"]]},
+
+ "Magicant": {sea_of_eden: [["Ness"]],
+ "Global ATM Access": [["Nothing"]]},
+
+ "Sea of Eden": {"Sea of Eden": [["Nothing"]]},
+
+ "Cave of the Present": {"Cave of the Past": [["Power of the Earth"]]},
+
+ "Cave of the Past": {"Endgame": [["Paula"]]},
+
+ "Endgame": {"Endgame": [["Nothing"]]},
+
+ "Common Condiment Shop": {"Common Condiment Shop": [["Nothing"]]},
+
+ "Global ATM Access": {"Global ATM Access": [["Nothing"]]}
+
+ }
+
+ teleports = {
+ "Onett Teleport": "Onett",
+ "Twoson Teleport": "Twoson",
+ "Happy-Happy Village Teleport": "Happy-Happy Village",
+ "Threed Teleport": "Threed",
+ "Saturn Valley Teleport": "Saturn Valley",
+ "Dusty Dunes Teleport": "Dusty Dunes Desert",
+ "Fourside Teleport": "Fourside",
+ "Winters Teleport": "Winters",
+ "Summers Teleport": "Summers",
+ "Scaraba Teleport": "Scaraba",
+ "Dalaam Teleport": "Dalaam",
+ "Deep Darkness Teleport": "Deep Darkness",
+ "Tenda Village Teleport": "Tenda Village",
+ "Lost Underworld Teleport": "Lost Underworld",
+ "Magicant Teleport": "Magicant"
+ }
+
+ if world.options.no_free_sanctuaries:
+ world.area_rules["Happy-Happy Village"][lilliput_steps] = [["Tiny Key"]]
+ world.area_rules["Lost Underworld"][fire_spring] = [["Tenda Lavapants"]]
+ else:
+ world.area_rules["Happy-Happy Village"][lilliput_steps] = [["Nothing"]]
+ world.area_rules["Lost Underworld"][fire_spring] = [["Nothing"]]
+
+ inventory = {0: ["Nothing"]} # Nothing means no item needed for connection
+ item_regions = {}
+
+ for item in world.multiworld.precollected_items[world.player]:
+ inventory[0].append(item.name)
+
+ unconnected_regions = [world.starting_region, "Ness's Mind"]
+ world.accessible_regions = [world.starting_region, "Ness's Mind"]
+ if world.options.random_start_location:
+ unconnected_regions.append(teleports[world.starting_teleport])
+ world.accessible_regions.append(teleports[world.starting_teleport])
+
+ world.scaled_area_order = []
+ passed_connections = []
+ local_prog = []
+ ness_scaled = False
+ paula_scaled = False
+ jeff_scaled = False
+ poo_scaled = False
+ badge_scaled = False
+ scaled_chars = {
+ "Ness": ness_scaled,
+ "Paula": paula_scaled,
+ "Jeff": jeff_scaled,
+ "Poo": poo_scaled
+ }
+
+ sphere_count = 0
+ last_region = "Ness's Mind"
+ regions_that_were_already_scaled = []
+ early_regions = []
+ world.Ness_region = "Ness's Mind"
+ world.Paula_region = "Ness's Mind"
+ world.Jeff_region = "Ness's Mind"
+ world.Poo_region = "Ness's Mind"
+ world.Badge_region = "Ness's Mind"
+ for item in world.multiworld.precollected_items[world.player]:
+ if item.name in ["Ness", "Paula", "Jeff", "Poo"]:
+ scaled_chars[item.name] = True
+
+ if item.name == "Franklin Badge":
+ badge_scaled = True
+
+ for num, sphere in enumerate(world.multiworld.earthbound_locations_by_sphere):
+ if num + 1 not in inventory:
+ inventory[num + 1] = []
+
+ for location in sorted(sphere):
+ if num == 0:
+ if location.parent_region.name not in world.accessible_regions and location.player == world.player:
+ early_regions.append(location.parent_region.name)
+ world.accessible_regions.append(location.parent_region.name)
+ unconnected_regions.append(location.parent_region.name)
+
+ if location.item.player == world.player and location.item.advancement:
+ inventory[num + 1].append(location.item.name)
+ if location.player == world.player:
+ local_prog.append(location.item.name)
+ if location.item.name not in item_regions:
+ item_regions[location.item.name] = []
+ item_regions[location.item.name].append(location.parent_region.name)
+
+ # TODO; all areas have levels now, so I can skip the combat regions check
+ if location.player == world.player and location.parent_region.name in combat_regions and (
+ location.parent_region.name not in regions_that_were_already_scaled):
+ last_region = location.parent_region.name
+
+ regions_that_were_already_scaled.append(last_region)
+
+ if location.item.player == world.player and location.item.name == "Ness" and not scaled_chars["Ness"]:
+ if location.parent_region.name in combat_regions and (location.player == world.player) and (
+ location.name not in locations_with_item_requirements):
+ world.Ness_region = location.parent_region.name
+ else:
+ world.Ness_region = last_region
+ scaled_chars["Ness"] = True
+
+ if location.item.player == world.player and location.item.name == "Paula" and not scaled_chars["Paula"]:
+ if location.parent_region.name in combat_regions and (location.player == world.player) and (
+ location.name not in locations_with_item_requirements):
+ world.Paula_region = location.parent_region.name
+ else:
+ world.Paula_region = last_region
+ scaled_chars["Paula"] = True
+
+ if location.item.player == world.player and location.item.name == "Jeff" and not scaled_chars["Jeff"]:
+ if location.parent_region.name in combat_regions and (location.player == world.player) and (
+ location.name not in locations_with_item_requirements):
+ world.Jeff_region = location.parent_region.name
+ else:
+ world.Jeff_region = last_region
+ scaled_chars["Jeff"] = True
+
+ if location.item.player == world.player and location.item.name == "Poo" and not scaled_chars["Poo"]:
+ if location.parent_region.name in combat_regions and (location.player == world.player) and (
+ location.name not in locations_with_item_requirements):
+ world.Poo_region = location.parent_region.name
+ else:
+ world.Poo_region = last_region
+ scaled_chars["Poo"] = True
+
+ if location.item.player == world.player and location.item.name == "Franklin Badge" and not badge_scaled:
+ if location.parent_region.name in combat_regions and (location.player == world.player) and (
+ location.name not in locations_with_item_requirements):
+ world.Badge_region = location.parent_region.name
+ else:
+ world.Badge_region = last_region
+ badge_scaled = True
+
+ sphere_count = num
+
+ for item in range(1, len(inventory)):
+ if item in inventory:
+ inventory[item] = inventory[item - 1] + inventory[item]
+ else:
+ inventory[item] = inventory[item - 1]
+
+ for i in range(sphere_count):
+ # Ness's mind needs to be calculated last, always. (Players are more likely to walk around
+ # and explore areas than suddenly leave with a teleport)
+ # Shuffle it to the end of the list on each loop so it gets deprioritized
+ # Is there a better way to do this?
+ if "Ness's Mind" in unconnected_regions:
+ unconnected_regions.remove("Ness's Mind")
+ unconnected_regions.append("Ness's Mind") # probably do this differently earlier
+ for region in unconnected_regions:
+ for connection in world.area_exits[region]:
+ if f"{region} -> {connection}" not in passed_connections:
+ for rule_set in world.area_rules[region][connection]:
+ # check if this sphere has the items needed to make this connection
+ if all(item in inventory[i] for item in rule_set):
+ passed_connections.append(f"{region} -> {connection}")
+ if connection not in world.accessible_regions:
+ world.accessible_regions.append(connection)
+ unconnected_regions.append(connection)
+ else:
+ world.area_exits[region].remove(connection)
+ if "Endgame" in unconnected_regions:
+ unconnected_regions.remove("Endgame")
+ unconnected_regions.insert(0, "Endgame")
+
+ for region in world.multiworld.get_regions(world.player):
+ if region.name not in world.accessible_regions and region.name != "Menu":
+ world.accessible_regions.append(region.name)
+
+ if world.options.magicant_mode == MagicantMode.option_alternate_goal and world.options.giygas_required:
+ # If magicant is an alternate goal it should be scaled after Giygas
+ world.accessible_regions.remove("Magicant")
+ world.accessible_regions.append("Sea of Eden")
+ world.accessible_regions.insert(world.accessible_regions.index("Endgame") + 1, "Magicant")
+ elif world.options.magicant_mode == MagicantMode.option_optional_boost and world.options.giygas_required:
+ world.accessible_regions.insert(world.accessible_regions.index("Endgame") - 1, "Magicant")
+ elif world.options.magicant_mode == MagicantMode.option_optional_boost and not world.options.giygas_required:
+ # Just add it to the end of scaling
+ world.accessible_regions.append("Magicant")
+ world.accessible_regions.append("Sea of Eden")
+
+ # calculate which areas need to have enemies scaled
+ for region in world.accessible_regions:
+ if region in world.regional_enemies:
+ world.scaled_area_order.append(region)
+
+ current_level = 1
+ world.area_levels = {}
+ for region in world.accessible_regions:
+ world.area_levels[region] = current_level
+ current_level += expected_level_gains[region]
+
+ if world.Ness_region == "Ness's Mind":
+ world.Ness_region = world.scaled_area_order[0]
+
+ if world.Paula_region == "Ness's Mind":
+ world.Paula_region = world.scaled_area_order[0]
+
+ if world.Jeff_region == "Ness's Mind":
+ world.Jeff_region = world.scaled_area_order[0]
+
+ if world.Poo_region == "Ness's Mind":
+ world.Poo_region = world.scaled_area_order[0]
+
+ if world.Badge_region == "Ness's Mind":
+ world.Badge_region = world.scaled_area_order[0]
diff --git a/worlds/earthbound/modules/boss_shuffle.py b/worlds/earthbound/modules/boss_shuffle.py
new file mode 100644
index 000000000000..3a6e8708eb30
--- /dev/null
+++ b/worlds/earthbound/modules/boss_shuffle.py
@@ -0,0 +1,411 @@
+from typing import NamedTuple, TYPE_CHECKING
+from logging import warning
+import struct
+if TYPE_CHECKING:
+ from .. import EarthBoundWorld
+ from ..Rom import LocalRom
+import struct
+
+boss_sprite_pointers = {
+ "Frank": 0xEF2B69,
+ "Frankystein Mark II": 0xEF43F9,
+ "Titanic Ant": 0xEF3B57,
+ "Captain Strong": 0xEF23A9,
+ "Everdred": 0xEF2BCD,
+ "Mr. Carpainter": 0xEF2BFF,
+ "Mondo Mole": 0xEF4557,
+ "Boogey Tent": 0xEF3748,
+ "Mini Barf": 0xEF3B89,
+ "Master Belch": 0xEF3CD6,
+ "Trillionage Sprout": 0xEF3BBB,
+ "Guardian Digger": 0xEF45BB,
+ "Dept. Store Spook": 0xEF495F,
+ "Evil Mani-Mani": 0xEF395F,
+ "Clumsy Robot": 0xEF45A2,
+ "Shrooom!": 0xEF392B,
+ "Plague Rat of Doom": 0xEF4570,
+ "Thunder and Storm": 0xEF3C70,
+ "Kraken": 0xEF3991,
+ "Guardian General": 0xEF3C3C,
+ "Master Barf": 0xEF39F5,
+ "Starman Deluxe": 0xEF3A59,
+ "Electro Specter": 0xEF45ED,
+ "Carbon Dog": 0xEF3D08,
+ "Ness's Nightmare": 0xEF395F,
+ "Heavily Armed Pokey": 0xEF49AA,
+ "Starman Junior": 0xEF3A59,
+ "Diamond Dog": 0xEF3D08,
+ "Giygas": 0xEF40F2
+
+}
+
+boss_plando_keys = {
+ "Frank",
+ "Frankystein Mark II",
+ "Frankystein",
+ "Captain Strong",
+ "Strong",
+ "Everdred",
+ "Mr. Carpainter",
+ "Mr Carpainter",
+ "Carpainter",
+ "Mondo Mole",
+ "Boogey Tent",
+ "Mini Barf",
+ "Master Belch",
+ "Belch",
+ "Trillionage Sprout",
+ "Guardian Digger",
+ "Dept. Store Spook",
+ "Dept Store Spook",
+ "Evil Mani Mani",
+ "Mani Mani",
+ "Clumsy Robot",
+ "Shrooom!",
+ "Shrooom",
+ "Shroom!",
+ "Shroooooom!",
+ "Shroom",
+ "Plague Rat of Doom",
+ "Thunder and Storm",
+ "Kraken",
+ "The Kraken",
+ "Guardian General",
+ "Master Barf",
+ "Starman Deluxe",
+ "Starman DX",
+ "Electro Specter",
+ "Carbon Dog",
+ "Ness's Nightmare",
+ "Nesss Nightmare",
+ "Heavily Armed Pokey",
+ "Pokey"
+ "Starman Junior",
+ "Diamond Dog",
+ "Giygas"
+}
+
+boss_typo_key = {
+ "Frankystein": "Frankystein Mark II",
+ "Strong": "Captain Strong",
+ "Mr Carpainter": "Mr. Carpainter",
+ "Carpainter": "Mr. Carpainter",
+ "Belch": "Master Belch",
+ "Dept Store Spook": "Dept. Store Spook",
+ "Evil Mani Mani": "Evil Mani-Mani",
+ "Mani Mani": "Evil Mani-Mani",
+ "Shroom": "Shrooom!",
+ "Shrooom": "Shrooom!",
+ "Shroom!": "Shrooom!",
+ "Shroooooom!": "Shrooom!",
+ "The Kraken": "Kraken",
+ "Starman DX": "Starman Deluxe",
+ "Nesss Nightmare": "Ness's Nightmare",
+ "Pokey": "Heavily Armed Pokey"
+}
+
+banned_transformations = ["Master Belch", "Master Barf", "Kraken", "Heavily Armed Pokey"]
+hard_final_bosses = ["Carbon Dog", "Kraken", "Clumsy Robot", "Starman Junior", "Starman Deluxe", "Giygas", "Thunder and Storm", "Electro Specter",
+ "Evil Mani-Mani", "Ness's Nightmare", "Shrooom!", "Master Belch"]
+
+
+class SlotInfo(NamedTuple):
+ sprite_addrs: list[int]
+ short_names: list[int]
+ long_names: list[int]
+ battle_data: list[int]
+
+
+class BossData(NamedTuple):
+ sprite_pointer: int
+ short_name_pointer: int
+ long_name_pointer: int
+ battle_group: int
+ enemy_id: int
+ music: int
+
+
+def initialize_bosses(world: "EarthBoundWorld") -> None:
+ from ..Options import BossShuffle
+
+ world.boss_list = [
+ "Frank",
+ "Frankystein Mark II",
+ "Titanic Ant",
+ "Captain Strong",
+ "Everdred",
+ "Mr. Carpainter",
+ "Mondo Mole",
+ "Boogey Tent",
+ "Mini Barf",
+ "Master Belch",
+ "Trillionage Sprout",
+ "Guardian Digger",
+ "Dept. Store Spook",
+ "Evil Mani-Mani",
+ "Clumsy Robot",
+ "Shrooom!",
+ "Plague Rat of Doom",
+ "Thunder and Storm",
+ "Kraken",
+ "Guardian General",
+ "Master Barf",
+ "Starman Deluxe",
+ "Electro Specter",
+ "Carbon Dog",
+ "Ness's Nightmare",
+ "Heavily Armed Pokey",
+ "Starman Junior",
+ "Diamond Dog",
+ "Giygas"
+ ]
+
+ world.boss_slots = {
+ "Frank": SlotInfo([0x0F9338],
+ [0x066111, 0x066198, 0x0661AC],
+ [0x065F11, 0x065F20, 0x066482, 0x0660C5, 0x0746E2, 0x074BC1, 0x074E1D],
+ [0x0683FF]),
+ "Frankystein Mark II": SlotInfo([0x0F96F0], [], [0x066146, 0x06648B, 0x0664FC], [0x068406]),
+ "Titanic Ant": SlotInfo([], [], [], [0x06840D]),
+ "Captain Strong": SlotInfo([], [0x5FC2B, 0x05FCF7, 0x065F88, 0x066085],
+ [0x05FC59, 0x3317DB], [0x068468]),
+ "Everdred": SlotInfo([0x0F9A64, 0x0F9FB4], [0x2EEEEA], [0x095C70], [0x06846F]),
+ "Mr. Carpainter": SlotInfo([0x0FA27E, 0x0FA5D0],
+ [0x0990DA, 0x0684D0],
+ [0x0993DB, 0x09945E, 0x099311, 0x099364, 0x098EF6, 0x099143, 0x099028,
+ 0x0983BB, 0x09840C, 0x09835B, 0x09056F, 0x0794EC],
+ [0x0684FD]),
+ "Mondo Mole": SlotInfo([], [], [], [0x068414]),
+ "Boogey Tent": SlotInfo([0x0FACEB], [], [], [0x06853C]),
+ "Mini Barf": SlotInfo([0x0FB0B4], [], [], [0x2F9515]),
+ "Master Belch": SlotInfo([0x0FB7CF],
+ [0x09E64D, 0x09E690, 0x2EEED7, 0x08EF21, 0x08EF38],
+ [0x2F6297, 0x2F62B3, 0x2F6910, 0x2F6973],
+ [0x068558]),
+ "Trillionage Sprout": SlotInfo([], [], [], [0x068422]),
+ "Guardian Digger": SlotInfo([0x0FC11B, 0x0FC0B5, 0x0FC12C, 0x0FC0D7, 0x0FC0C6],
+ [],
+ [],
+ [0x06858E, 0x068595, 0x06859C, 0x0685A3, 0x0685AA]),
+ "Dept. Store Spook": SlotInfo([0x0FC803], [], [], [0x06855F]),
+ "Evil Mani-Mani": SlotInfo([0x0FE6E4], [], [0x0978AD, 0x09782D, 0x097998], [0x068587]),
+ "Clumsy Robot": SlotInfo([0x0FC429], [], [], [0x06856D]),
+ "Shrooom!": SlotInfo([], [], [], [0x06841B]),
+ "Plague Rat of Doom": SlotInfo([], [], [], [0x068429]),
+ "Thunder and Storm": SlotInfo([], [], [], [0x068430]),
+ "Kraken": SlotInfo([0x092CD0, 0x0FE370, 0x0FE381, 0x0FE392, 0x092D13],
+ [0x092D4D],
+ [0x086061, 0x086139, 0x08B430, 0x08B6FC, 0x08B8B4, 0x08B591, 0x09AB2B],
+ [0x0685B1, 0x2F9472, 0x2F9491, 0x2F94B0]),
+ "Guardian General": SlotInfo([0x0FD7E2], [], [], [0x2F9453]),
+ "Master Barf": SlotInfo([0x0FDB23], [], [], [0x068574]),
+ "Starman Deluxe": SlotInfo([0x0FB626], [], [0x092C29], [0x2F942F]),
+ "Electro Specter": SlotInfo([], [], [], [0x068437]),
+ "Carbon Dog": SlotInfo([], [], [], [0x06843E]),
+ "Ness's Nightmare": SlotInfo([0x0FE3B4], [], [], [0x068580]),
+ "Heavily Armed Pokey": SlotInfo([0x09C2EC], [0x2EEEC3, 0x2EEECC], [], []),
+ "Starman Junior": SlotInfo([], [], [], []),
+ "Diamond Dog": SlotInfo([], [], [], []),
+ "Giygas": SlotInfo([0x09C2BF, 0x09C2E5], [0x2EF0A9], [0x2EF09F], [])
+ }
+
+ world.boss_info = {
+ "Frank": BossData(0x0099, 0xEEEEBC, 0xEEEEBC, 0x01C0, 0x83, 0x64),
+ "Frankystein Mark II": BossData(0x0191, 0xEEEF0A, 0xEEEEF6, 0x01C1, 0x82, 0x66),
+ "Titanic Ant": BossData(0x0139, 0xEEEF1E, 0xEEEF16, 0x01C2, 0x25, 0x67),
+ "Captain Strong": BossData(0x004B, 0xEEEF2A, 0xEEEF22, 0x01C4, 0xE4, 0x66),
+ "Everdred": BossData(0x009D, 0xEEEF31, 0xEEEF31, 0x01C5, 0x6E, 0x62),
+ "Mr. Carpainter": BossData(0x009F, 0xEEEF3E, 0xEEEF3A, 0x01C6, 0x1A, 0x94),
+ "Mondo Mole": BossData(0x019F, 0xEEEF4F, 0xEEEF49, 0x01C7, 0x29, 0x67),
+ "Boogey Tent": BossData(0x0110, 0xEEEF5B, 0xEEEF54, 0x01CA, 0x66, 0x66),
+ "Mini Barf": BossData(0x013B, 0xEEEF65, 0xEEEF60, 0x01E2, 0xE2, 0x63),
+ "Master Belch": BossData(0x0148, 0xEEEF71, 0xEEEF6A, 0x01C8, 0x5D, 0x63),
+ "Trillionage Sprout": BossData(0x013D, 0xEEEF83, 0xEEEF77, 0x01C9, 0x5A, 0x67),
+ "Guardian Digger": BossData(0x01A3, 0xEEEF93, 0xEEEF8A, 0x01CB, 0x2A, 0x67),
+ "Dept. Store Spook": BossData(0x01C7, 0xEEEFA6, 0xEEEF9A, 0x01CC, 0x02, 0x66),
+ "Evil Mani-Mani": BossData(0x0125, 0xEEEFC1, 0xEEEFAC, 0x01CD, 0x89, 0x94),
+ "Clumsy Robot": BossData(0x01A2, 0xEEEFD2, 0xEEEFCB, 0x01CE, 0x92, 0x94),
+ "Shrooom!": BossData(0x0123, 0xEEEFD8, 0xEEEFD8, 0x01D1, 0x27, 0x67),
+ "Plague Rat of Doom": BossData(0x01A0, 0xEEEFEE, 0xEEEFE0, 0x01CF, 0x28, 0x67),
+ "Thunder and Storm": BossData(0x0144, 0xEEEFFF, 0xEEEFF3, 0x01D0, 0x80, 0x68),
+ "Kraken": BossData(0x0127, 0xEEF005, 0xEEF005, 0x01D3, 0x31, 0x68),
+ "Guardian General": BossData(0x0142, 0xEEF015, 0xEEF00C, 0x01D4, 0x49, 0x67),
+ "Master Barf": BossData(0x012B, 0xEEEF65, 0xEEF01D, 0x01D5, 0x5F, 0x63),
+ "Starman Deluxe": BossData(0x012F, 0xEEF034, 0xEEF029, 0x01D2, 0x4A, 0x61),
+ "Electro Specter": BossData(0x01A5, 0xEEF043, 0xEEF03B, 0x01D6, 0x74, 0x68),
+ "Carbon Dog": BossData(0x014A, 0xEEF052, 0xEEF04B, 0x01D7, 0x1B, 0x67),
+ "Ness's Nightmare": BossData(0x0125, 0xEEF070, 0xEEF06A, 0x01D8, 0x15, 0x94),
+ "Heavily Armed Pokey": BossData(0x01CA, 0xEEF064, 0xEEF056, 0x000E, 0xD8, 0x69),
+ "Starman Junior": BossData(0x012F, 0xEEF082, 0xEEF07A, 0x01DA, 0xD6, 0x94),
+ "Diamond Dog": BossData(0x014A, 0xEEF052, 0xEEF089, 0x01D9, 0x53, 0x61),
+ "Giygas": BossData(0x0172, 0xEEF095, 0xEEF095, 0x01DD, 0xDC, 0x49)
+ }
+
+ if world.options.skip_prayer_sequences:
+ # Boss shuffle sprites needs to apply to the skip prayer cleanup too
+ world.boss_slots["Giygas"].sprite_addrs.append(0x07B9AC)
+ world.boss_slots["Heavily Armed Pokey"].sprite_addrs.append(0x07B9A7)
+
+ # mole/rat text
+ # todo; Giygas sprites/text
+
+ world.boss_slot_order = world.boss_list.copy()
+ if type(world.options.boss_shuffle.value) == str:
+ boss_plando = world.options.boss_shuffle.value.split(";")
+ shuffle_result = boss_plando.pop()
+ else:
+ boss_plando = []
+ shuffle_result = world.options.boss_shuffle.value
+
+ if shuffle_result == "true" or shuffle_result == 1:
+ world.random.shuffle(world.boss_list)
+
+ if not world.options.decouple_diamond_dog:
+ world.boss_list.remove("Diamond Dog")
+ insert_index = 28 if not world.options.boss_shuffle_add_giygas else 27
+ world.boss_list.insert(insert_index, "Diamond Dog")
+
+ if not world.options.boss_shuffle_add_giygas:
+ world.boss_list.remove("Giygas")
+ world.boss_list.insert(29, "Giygas")
+
+ if world.options.safe_final_boss:
+ while world.boss_list[25] in hard_final_bosses:
+ i = world.random.randrange(len(world.boss_list))
+ if (world.boss_list[i] == "Diamond Dog" and not world.options.decouple_diamond_dog) or (
+ world.boss_list[i] == "Giygas" and not world.options.boss_shuffle_add_giygas
+ ):
+ continue
+ world.boss_list[25], world.boss_list[i] = world.boss_list[i], world.boss_list[25]
+
+ did_plando_diam_dog = False
+ diamond_dog_plando_slot = None
+
+ for item in boss_plando:
+ boss_block = item.split("-")
+ boss = boss_block[0].title() # Boss is what's being placed
+ slot = boss_block[1].title() # SLot is where the boss is going.
+ if boss in boss_typo_key:
+ boss = boss_typo_key[boss]
+
+ if slot in boss_typo_key:
+ slot = boss_typo_key[boss]
+
+ if slot == "Diamond Dog":
+ did_plando_diam_dog = True
+ diamond_dog_plando_slot = boss
+
+ old_index = world.boss_list.index(boss) # This should be the slot where the chosen boss currently is
+ new_index = world.boss_slot_order.index(slot) # Boss slots should use the original position
+
+ world.boss_list[old_index] = world.boss_list[new_index] # We want to replace the boss that was originally there with the boss we're swapping with
+ world.boss_list[new_index] = boss
+
+ if world.boss_list[25] == "Carbon Dog" and world.boss_list[27] in banned_transformations:
+ if did_plando_diam_dog:
+ warning(f"""Unable to plando {diamond_dog_plando_slot} for {world.multiworld.get_player_name(world.player)}'s EarthBound world.
+This boss cannot be placed onto Diamond Dog's slot if Carbon Dog is on Heavily Armed Pokey's slot.
+This message is likely the result of randomization and can be safely ignored.""") # Why is this spacing the only way to get the message to render legibly
+ original_boss = world.boss_list[27]
+ transformation_replacement = world.random.randint(0, 24)
+ while world.boss_list[transformation_replacement] in banned_transformations:
+ transformation_replacement = world.random.randint(0, 24)
+ world.boss_list[27] = world.boss_list[transformation_replacement]
+ world.boss_list[transformation_replacement] = original_boss
+
+def write_bosses(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ rom.write_bytes(0x15E527, bytearray([0x00, 0x00])) # Blank out Pokey's end battle action
+ rom.write_bytes(0x15B8B9, bytearray([0x00, 0x00]))
+ rom.write_bytes(0x15DD13, bytearray([0x00, 0x00])) # Blank out barf's end battle script
+ rom.write_bytes(0x15E69F, bytearray([0x00, 0x00])) # Blank giygas
+ if world.boss_list[25] == "Carbon Dog": # Heavily armed Pokey
+ pokey_adjust = 27
+ else:
+ pokey_adjust = 25
+
+ rom.write_bytes(world.enemies[world.boss_list[pokey_adjust]].address + 78, bytearray([0x13, 0x01]))
+ for i in range(1, world.enemies[world.boss_list[pokey_adjust]].attack_extensions):
+ enemy_new = f"{world.enemies[world.boss_list[pokey_adjust]].name} ({i + 1})"
+ rom.write_bytes(world.enemies[enemy_new].address + 78, bytearray([0x13, 0x01]))
+
+ if world.boss_list[20] == "Carbon Dog": # Master Barf
+ barf_adjust = 27
+ else:
+ barf_adjust = 20
+
+ rom.write_bytes(world.enemies[world.boss_list[barf_adjust]].address + 78, bytearray([0xF4, 0x00]))
+ for i in range(1, world.enemies[world.boss_list[barf_adjust]].attack_extensions):
+ enemy_new = f"{world.enemies[world.boss_list[barf_adjust]].name} ({i + 1})"
+ rom.write_bytes(world.enemies[enemy_new].address + 78, bytearray([0xF4, 0x00]))
+
+ if world.boss_list[28] == "Carbon Dog": # Giygas 2
+ # I should probably just hard stop Carbon Dog from being here
+ giygas_2_adjust = 27 # Set to the diamond dog slot
+ else:
+
+ giygas_2_adjust = 28
+
+ rom.write_bytes(world.enemies[world.boss_list[giygas_2_adjust]].address + 78, bytearray([0x16, 0x01]))
+ for i in range(1, world.enemies[world.boss_list[giygas_2_adjust]].attack_extensions):
+ enemy_new = f"{world.enemies[world.boss_list[giygas_2_adjust]].name} ({i + 1})"
+ rom.write_bytes(world.enemies[enemy_new].address + 78, bytearray([0x16, 0x01]))
+
+ if world.boss_list[25] != "Heavily Armed Pokey":
+ rom.write_bytes(0x15E50A, bytearray([0x19, 0x6E, 0xEF]))
+ rom.write_bytes(0x15E4FE, bytearray([0x70, 0x11, 0x01])) # Add to the scaling list?
+
+ for slot, boss in enumerate(world.boss_slot_order):
+ for address in world.boss_slots[boss].sprite_addrs: # sprite
+ rom.write_bytes(address, struct.pack("H", world.boss_info[world.boss_list[slot]].sprite_pointer))
+
+ for address in world.boss_slots[boss].short_names: # short name
+ rom.write_bytes(address, struct.pack("I", world.boss_info[world.boss_list[slot]].short_name_pointer))
+
+ for address in world.boss_slots[boss].long_names: # long name
+ rom.write_bytes(address, struct.pack("I", world.boss_info[world.boss_list[slot]].long_name_pointer))
+
+ for address in world.boss_slots[boss].battle_data: # battle
+ rom.write_bytes(address, struct.pack("H", world.boss_info[world.boss_list[slot]].battle_group))
+
+ rom.write_bytes(0x10DF7F, struct.pack("H", world.boss_info[world.boss_list[25]].enemy_id))
+ rom.write_bytes(0x10DF86, struct.pack("H", world.boss_info[world.boss_list[25]].enemy_id))
+ # rom.write_bytes(0x10DF8D, struct.pack("H", world.boss_info[world.boss_list[25]].enemy_id))
+ rom.write_bytes(0x10DFA2, struct.pack("H", world.boss_info[world.boss_list[25]].enemy_id))
+ rom.write_bytes(0x10D563, struct.pack("H", world.boss_info[world.boss_list[25]].enemy_id))
+ rom.write_bytes(world.enemies[world.boss_list[25]].address + 91, bytearray([0x00])) # Row of the enemy
+
+ rom.write_bytes(0x10DF83, struct.pack("H", world.boss_info[world.boss_list[28]].enemy_id))
+ rom.write_bytes(0x02C4FD, struct.pack("H", world.boss_info[world.boss_list[28]].enemy_id))
+ rom.write_bytes(0x10D560, struct.pack("H", world.boss_info[world.boss_list[28]].enemy_id))
+
+ rom.write_bytes(0x159FC7, struct.pack("H", world.boss_info[world.boss_list[27]].enemy_id))
+ rom.write_bytes(0x15D5C1, struct.pack("H", world.boss_info[world.boss_list[27]].enemy_id))
+ # carbon dog's transformation
+ rom.write_bytes(0x10DF69, struct.pack("H", world.boss_info[world.boss_list[27]].enemy_id))
+ rom.write_bytes(0x02C503, bytearray([world.boss_info[world.boss_list[28]].music])) # music
+
+ rom.write_bytes(0x2F188F, struct.pack("I", boss_sprite_pointers[world.boss_list[3]]))
+
+ rom.write_bytes(0x0302CE, struct.pack("H", 0x0154))
+ rom.write_bytes(0x05F870, struct.pack("H", 0x0154))
+ rom.write_bytes(0x0F8E3D, struct.pack("H", 0x0154))
+ rom.write_bytes(0x05F886, struct.pack("H", 0x0154))
+ rom.write_bytes(0x05F8A1, struct.pack("H", 0x0154))
+ rom.write_bytes(0x05F8E3, struct.pack("H", 0x0154))
+ rom.write_bytes(0x05FB0E, struct.pack("H", 0x0154))
+ rom.write_bytes(0x05FBFC, struct.pack("H", 0x0154))
+ rom.write_bytes(0x05FD08, struct.pack("H", 0x0154))
+ rom.write_bytes(0x05FD5C, struct.pack("H", 0x0154))
+ rom.copy_bytes(0x10DF7B, 6, 0x2FFF10)
+ if world.boss_list[25] == "Carbon Dog":
+ rom.write_bytes(0x2FFF16, bytearray([0x00])) # Count of enemies
+ rom.write_bytes(0x2FFF17, struct.pack("H", world.boss_info[world.boss_list[27]].enemy_id)) # Add diamond dog
+ rom.write_bytes(0x2FFF19, bytearray([0xFF]))
+ rom.write_bytes(world.enemies[world.boss_list[27]].address + 91, bytearray([0x00])) # Force to front row
+ elif world.boss_list[25] == "Giygas":
+ rom.write_bytes(0x0121DF, bytearray([0x00]))
+ rom.write_bytes(0x2FFF16, bytearray([0xFF]))
+ else:
+ rom.write_bytes(0x2FFF16, bytearray([0xFF]))
+
+ # c2c505 sets the song
diff --git a/worlds/earthbound/modules/dungeon_er.py b/worlds/earthbound/modules/dungeon_er.py
new file mode 100644
index 000000000000..34bf429556bd
--- /dev/null
+++ b/worlds/earthbound/modules/dungeon_er.py
@@ -0,0 +1,212 @@
+import struct
+from typing import Optional, TYPE_CHECKING
+from dataclasses import dataclass
+from ..game_data.local_data import item_id_table
+from typing import Optional, TYPE_CHECKING
+from dataclasses import dataclass
+from ..game_data.local_data import item_id_table
+if TYPE_CHECKING:
+ from .. import EarthBoundWorld
+ from ..Rom import LocalRom
+
+
+@dataclass
+class EBDungeonDoor:
+ address: int
+ copyaddress: int
+ direction: int
+ is_script: bool = False # Script warps invert the x and y coordinate
+
+
+def shuffle_dungeons(world: "EarthBoundWorld") -> None:
+ # Is the dept. store a dungeon
+ single_exit_dungeons = [
+ "Giant Step",
+ "Happy-Happy HQ",
+ "Lilliput Steps",
+ "Milky Well",
+ "Gold Mine",
+ "Moonside",
+ "Monotoli Building",
+ "Magnet Hill",
+ "Pink Cloud",
+ "Dungeon Man",
+ "Stonehenge Base",
+ "Lumine Hall",
+ "Fire Spring",
+ "Sea of Eden"
+ ]
+
+ double_exit_dungeons = [
+ "Arcade",
+ "Brickroad Maze",
+ "Rainy Circle",
+ "Belch's Factory",
+ "Pyramid"
+ ]
+
+ world.dungeon_connections = {
+ "Arcade": "Arcade",
+ "Giant Step": "Giant Step",
+ "Happy-Happy HQ": "Happy-Happy HQ",
+ "Lilliput Steps": "Lilliput Steps",
+ "Belch's Factory": "Belch's Factory",
+ "Milky Well": "Milky Well",
+ "Gold Mine": "Gold Mine",
+ "Moonside": "Moonside",
+ "Monotoli Building": "Monotoli Building",
+ "Magnet Hill": "Magnet Hill",
+ "Pink Cloud": "Pink Cloud",
+ "Dungeon Man": "Dungeon Man",
+ "Stonehenge Base": "Stonehenge Base",
+ "Brickroad Maze": "Brickroad Maze",
+ "Rainy Circle": "Rainy Circle",
+ "Pyramid": "Pyramid",
+ "Lumine Hall": "Lumine Hall",
+ "Fire Spring": "Fire Spring",
+ "Sea of Eden": "Sea of Eden"
+ }
+ if world.options.magicant_mode:
+ # Don't shuffle Magicant when it's important
+ single_exit_dungeons.remove("Sea of Eden")
+
+ shuffled_single_dungeons = single_exit_dungeons.copy()
+ shuffled_double_dungeons = double_exit_dungeons.copy()
+
+ if world.options.dungeon_shuffle:
+ world.random.shuffle(shuffled_single_dungeons)
+ world.random.shuffle(shuffled_double_dungeons)
+
+ for index, entrance in enumerate(single_exit_dungeons):
+ world.dungeon_connections[entrance] = shuffled_single_dungeons[index]
+
+ for index, entrance in enumerate(double_exit_dungeons):
+ world.dungeon_connections[entrance] = shuffled_double_dungeons[index]
+
+
+def write_dungeon_entrances(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ dungeon_entrances = {
+ "Arcade": ["Arcade Entrance", "Arcade Exit", "Arcade Back Exit", "Arcade Back Entrance"],
+ "Giant Step": ["Giant Step Entrance", "Giant Step Exit"],
+ "Happy-Happy HQ": ["Happy-Happy HQ Entrance", "Happy-Happy HQ Exit"],
+ "Lilliput Steps": ["Lilliput Steps Entrance", "Lilliput Steps Exit"],
+ "Belch's Factory": ["Factory Script Warp", "Factory Exit", "Factory Back Exit", "Factory Back Entrance"],
+ "Milky Well": ["Milky Well Entrance", "Milky Well Exit"],
+ "Gold Mine": ["Mine Entrance", "Mine Exit"],
+ "Monotoli Building": ["Monotoli Entrance", "Monotoli Exit"],
+ "Moonside": ["Cafe Entrance", "Cafe Exit"],
+ "Brickroad Maze": ["Maze Entrance", "Maze Exit", "Maze Back Exit", "Maze Back Entrance"],
+ "Rainy Circle": ["Rainy Entrance", "Rainy Exit", "Rainy Back Exit", "Rainy Back Entrance"],
+ "Magnet Hill": ["Sewer Entrance", "Sewer Exit"],
+ "Pink Cloud": ["Pink Cloud Entrance", "Pink Cloud Exit"],
+ "Pyramid": ["Pyramid Entrance", "Pyramid Exit", "Pyramid Back Exit", "Pyramid Back Entrance"],
+ "Dungeon Man": ["D.M. Entrance Script", "D.M. Exit Script"],
+ "Stonehenge Base": ["Stonehenge Entrance", "Stonehenge Exit"],
+ "Lumine Hall": ["Lumine Entrance", "Lumine Exit"],
+ "Fire Spring": ["Fire Spring Entrance", "Fire Spring Exit"],
+ "Sea of Eden": ["Sea Entrance Script", "Sea Exit Script"]
+ }
+
+ all_dungeon_doors = {
+ "Arcade Entrance": EBDungeonDoor(0x0F00CC, 0x321000, 7),
+ "Arcade Exit": EBDungeonDoor(0x0F029A, 0x321010, 5),
+ "Arcade Back Exit": EBDungeonDoor(0x0F026E, 0x321020, 1),
+ "Arcade Back Entrance": EBDungeonDoor(0x0F00C1, 0x321030, 5),
+ "Giant Step Entrance": EBDungeonDoor(0x0F0032, 0x321040, 7),
+ "Giant Step Exit": EBDungeonDoor(0x0F04B5, 0x321050, 5),
+ "Happy-Happy HQ Entrance": EBDungeonDoor(0x0F09E9, 0x321060, 7),
+ "Happy-Happy HQ Exit": EBDungeonDoor(0x0F0A99, 0x321070, 5),
+ "Lilliput Steps Entrance": EBDungeonDoor(0x0F09F4, 0x321080, 3),
+ "Lilliput Steps Exit": EBDungeonDoor(0x0F0B1A, 0x321090, 7),
+ "Factory Entrance": EBDungeonDoor(0x0F1277, 0x3210A0, 5),
+ "Factory Script Warp": EBDungeonDoor(0x15EECB, 0x3210B0, 5, True),
+ "Factory Exit": EBDungeonDoor(0x0F1159, 0x3210C0, 5),
+ "Factory Back Exit": EBDungeonDoor(0x0F11BC, 0x3210D0, 5),
+ "Factory Back Entrance": EBDungeonDoor(0x0F11FE, 0x3210E0, 3),
+ "Milky Well Entrance": EBDungeonDoor(0x0F12E9, 0x3210F0, 3),
+ "Milky Well Exit": EBDungeonDoor(0x0F11E8, 0x321100, 5),
+ "Mine Entrance": EBDungeonDoor(0x0F1378, 0x321110, 7),
+ "Mine Exit": EBDungeonDoor(0x0F1400, 0x321120, 5),
+ "Cafe Entrance": EBDungeonDoor(0x0F165D, 0x321150, 3),
+ "Cafe Exit": EBDungeonDoor(0x0F1A25, 0x321160, 7),
+ "Monotoli Entrance": EBDungeonDoor(0x0F1928, 0x321170, 7),
+ "Monotoli Exit": EBDungeonDoor(0x0F1862, 0x321180, 3),
+ "Maze Entrance": EBDungeonDoor(0x0F0EB6, 0x321190, 3),
+ "Maze Exit": EBDungeonDoor(0x0F0FD8, 0x3211A0, 5),
+ "Maze Back Exit": EBDungeonDoor(0x0F0FE3, 0x3211B0, 5),
+ "Maze Back Entrance": EBDungeonDoor(0x0F0EC1, 0x3211C0, 7),
+ "Rainy Entrance": EBDungeonDoor(0x0F0ED7, 0x3211D0, 7),
+ "Rainy Exit": EBDungeonDoor(0x0F1030, 0x3211E0, 5),
+ "Rainy Back Exit": EBDungeonDoor(0x0F0FEE, 0x3211F0, 5),
+ "Rainy Back Entrance": EBDungeonDoor(0x0F0EAB, 0x321200, 3),
+ "Sewer Entrance": EBDungeonDoor(0x0F1A3B, 0x321210, 5),
+ "Sewer Exit": EBDungeonDoor(0x0F1A9E, 0x321220, 1),
+ "Pink Cloud Entrance": EBDungeonDoor(0x0F1E32, 0x321230, 7),
+ "Pink Cloud Exit": EBDungeonDoor(0x0F1EAB, 0x321240, 5),
+ "Pyramid Entrance": EBDungeonDoor(0x0F1F3A, 0x321250, 3),
+ "Pyramid Exit": EBDungeonDoor(0x0F1FA9, 0x321260, 5),
+ "Pyramid Back Exit": EBDungeonDoor(0x0F20E8, 0x321290, 5),
+ "Pyramid Back Entrance": EBDungeonDoor(0x0F1F45, 0x3212A0, 3),
+ "D.M. Entrance Script": EBDungeonDoor(0x15F0A3, 0x321270, 7, True),
+ "D.M. Exit Script": EBDungeonDoor(0x15F0CB, 0x321280, 5, True),
+ "Stonehenge Entrance": EBDungeonDoor(0x0F105C, 0x3212B0, 7),
+ "Stonehenge Exit": EBDungeonDoor(0x0F1072, 0x3212C0, 3),
+ "Lumine Entrance": EBDungeonDoor(0x0F239C, 0x3212D0, 7),
+ "Lumine Exit": EBDungeonDoor(0x0F2318, 0x3212E0, 3),
+ "Fire Spring Entrance": EBDungeonDoor(0x0F23D4, 0x3212F0, 3),
+ "Fire Spring Exit": EBDungeonDoor(0x0F2437, 0x321300, 5),
+ "Sea Entrance Script": EBDungeonDoor(0x15F25B, 0x321310, 3, True),
+ "Sea Exit Script": EBDungeonDoor(0x15ECEB, 0x321320, 5, True),
+ "Post-Nightmare Script": EBDungeonDoor(0x15ED4B, 0x321330, 5, True),
+ "Carpainter Failure Script": EBDungeonDoor(0x15EEF3, 0x321340, 7, True)
+ }
+
+ paired_doors = {}
+
+ for door in all_dungeon_doors:
+ rom.copy_bytes(all_dungeon_doors[door].address, 6, all_dungeon_doors[door].copyaddress) # Copy 6 bytes at the source of the door to the destination of the door
+
+ for door in world.dungeon_connections:
+ for index, entrance in enumerate(dungeon_entrances[door]):
+ if "Exit" in entrance:
+ paired_doors[dungeon_entrances[world.dungeon_connections[door]][index]] = entrance
+ else:
+ paired_doors[entrance] = dungeon_entrances[world.dungeon_connections[door]][index]
+
+ paired_doors["Post-Nightmare Script"] = paired_doors["Sea Exit Script"]
+ paired_doors["Carpainter Failure Script"] = paired_doors["Happy-Happy HQ Exit"]
+
+ for door in paired_doors:
+ destination = all_dungeon_doors[paired_doors[door]]
+ source = all_dungeon_doors[door]
+ if source.is_script:
+ if destination.is_script:
+ rom.copy_bytes(destination.copyaddress, 6, source.address)
+ else:
+ rom.copy_bytes(destination.copyaddress + 2, 2, source.address)
+ rom.copy_bytes(destination.copyaddress, 2, source.address + 2)
+ rom.write_bytes(source.address + 4, bytearray([destination.direction]))
+ rom.copy_bytes(destination.copyaddress + 4, 1, source.address + 5)
+ else:
+ if destination.is_script:
+ rom.copy_bytes(destination.copyaddress + 2, 2, source.address)
+ rom.copy_bytes(destination.copyaddress, 2, source.address + 2)
+ rom.copy_bytes(destination.copyaddress + 5, 1, source.address + 4)
+ else:
+ rom.copy_bytes(destination.copyaddress, 5, source.address)
+
+ rom.write_bytes(0x101664, struct.pack("H", 0x041F)) # Flag controlling the Saturn Valley ladder
+ rom.write_bytes(0x0F19C7, struct.pack("I", 0xF3104C)) # Replacement for the Moonside deliveryman
+ rom.write_bytes(0x0F0A93, struct.pack("I", 0x000000)) # Skip Pokey walking up after HHHQ
+ rom.write_bytes(0x086DFC, struct.pack("H", 0x00C7)) # Flag-independent Moonside entry
+ rom.write_bytes(0x0F1657, struct.pack("I", 0xF311A7)) # Fourside Cafe Door Script
+ rom.write_bytes(0x0F165B, struct.pack("H", 0x8091)) # Lock Cafe
+ rom.write_bytes(0x0FC8C6, struct.pack("I", 0xF3120B)) # Everdred script
+ rom.write_bytes(0x10784A, struct.pack("H", 0x0000)) # Mook spawn in stonehenge anteroom
+ rom.write_bytes(0x0FC51E, bytearray([0x00])) # Moonside sparkle always active
+
+ rom.write_bytes(0x0FA4D6, bytearray([0xC7, 0x00, 0x01]))
+
+ moonside_reward = world.multiworld.get_location("Fourside - Post-Moonside Delivery", world.player).item
+ if (moonside_reward.player != world.player) or world.options.remote_items or moonside_reward.name not in item_id_table:
+ rom.write_bytes(0x3310F7, struct.pack("I", 0xF310FB))
diff --git a/worlds/earthbound/modules/enemizer/__init__.py b/worlds/earthbound/modules/enemizer/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/earthbound/modules/enemizer/enemy_attributes.py b/worlds/earthbound/modules/enemizer/enemy_attributes.py
new file mode 100644
index 000000000000..af4b2d30d71e
--- /dev/null
+++ b/worlds/earthbound/modules/enemizer/enemy_attributes.py
@@ -0,0 +1,1024 @@
+enemy_species = [
+ "Cultist",
+ "Spook",
+ "Frog",
+ "Buffalo",
+ "Antoid",
+ "Mushroom",
+ "Fungus",
+ "Mobile Sprout",
+ "Fire Plug",
+ "Record",
+ "Power Robot",
+ "Reactor Robot",
+ "Hieroglyph",
+ "Asp Hieroglyph",
+ "Swoosh",
+ "Menace",
+ "Spirit",
+ "Elemental",
+ "Party Man",
+ "Reveler",
+ "Local Guy",
+ "Hippie",
+ "Bear",
+ "Bear Seven",
+ "Moldyman",
+ "Mite",
+ "Lady",
+ "Ant",
+ "Coffee Cup",
+ "Dice",
+ "Little Pile",
+ "Kraken",
+ "Robo",
+ "Cop",
+ "Coil Snake",
+ "Batty",
+ "Roach",
+ "Sign",
+ "Shambler",
+ "Punk",
+ "Skelpion",
+ "Starman",
+ "Sphere",
+ "Royal Guard",
+ "General",
+ "Zombie",
+ "Possessor",
+ "Dog",
+ "Oak",
+ "Octobot",
+ "Duck",
+ "Clock",
+ "Wolf",
+ "Musica",
+ "Pile of Puke",
+ "Kiss of Death",
+ "Eel",
+ "Tangoo",
+ "Arachnid",
+ "Demon",
+ "Fobby",
+ "Foppy",
+ "Booka",
+ "Mook",
+ "Ghost",
+ "Slug",
+ "Mouse",
+ "Bomb",
+ "Tom",
+ "Sam",
+ "Fish",
+ "Fish's Brother",
+ "Kid",
+ "Boy",
+ "Art",
+ "Shattered Man",
+ "Orb",
+ "Man Junior",
+ "Li'l UFO",
+ "UFO",
+ "Guy",
+ "Taxi",
+ "Molecule",
+ "Protoplasm",
+ "Robot",
+ "Psycho",
+ "Goat",
+ "Mole",
+ "Flame",
+ "Petunia",
+ "Ranboob",
+ "Noose",
+ "Pump",
+ "Crocodile",
+ "Fly",
+ "Crow",
+ "Caterpillar",
+ "Eye",
+ "Worm",
+ "Statue",
+ "Rat of Doom",
+ "Specter",
+ "Chief",
+ "Bot",
+ "Gangster",
+ "Storm",
+ "Dino",
+ "Painter",
+ "Burglar",
+ "Tank",
+]
+
+enemy_adjectives = [
+ "",
+ "Insane ",
+ "Dept. Store ",
+ "Armored ",
+ "Bad ",
+ "Black ",
+ "Red ",
+ "Ramblin' ",
+ "Struttin' ",
+ "Tough ",
+ "Enraged ",
+ "Mystical ",
+ "Atomic ",
+ "Nuclear ",
+ "Guardian ",
+ "Lethal ",
+ "Electro ",
+ "Conducting ",
+ "Evil ",
+ "Annoying Old ",
+ "Annoying ",
+ "Unassuming ",
+ "New Age Retro ",
+ "Carbon ",
+ "Mighty ",
+ "Putrid ",
+ "Thunder ",
+ "Cranky ",
+ "Extra Cranky ",
+ "Titanic ",
+ "Gigantic ",
+ "Mondo ",
+ "Scalding ",
+ "Loaded ",
+ "Slimy ",
+ "Even Slimier ",
+ "Bionic ",
+ "Spinning ",
+ "Whirling ",
+ "Hyper Spinning ",
+ "Coil ",
+ "Thirsty ",
+ "Mr. ",
+ "Elder ",
+ "Violent ",
+ "Filthy Attack ",
+ "Crazed ",
+ "Wooly ",
+ "Wild 'n Wooly ",
+ "Skate ",
+ "Dread ",
+ "Super ",
+ "Ghost of ",
+ "Smilin' ",
+ "Uncontrollable ",
+ "Petrified ",
+ "Urban ",
+ "Zombie ",
+ "Crooked ",
+ "Over Zealous ",
+ "Territorial ",
+ "Hostile Elder ",
+ "Diamond ",
+ "Marauder ",
+ "Military ",
+ "Mechanical ",
+ "Ultimate ",
+ "Mad ",
+ "Dali's ",
+ "Trillionage ",
+ "Desert ",
+ "Big ",
+ "French ",
+ "Zap ",
+ "Squatter ",
+ "Crested ",
+ "Great Crested ",
+ "Lesser ",
+ "Smelly ",
+ "Stinky ",
+ "Attack ",
+ "Pit Bull ",
+ "Rowdy ",
+ "Deadly ",
+ "Care Free ",
+ "Electro ",
+ "Handsome ",
+ "Manly ",
+ "Runaway ",
+ "Trick or Trick ",
+ "Cave ",
+ "Abstract ",
+ "Shattered ",
+ "Fierce ",
+ "Ego ",
+ "Yes ",
+ "Cute ",
+ "Beautiful ",
+ "Pogo ",
+ "Tough ",
+ "Worthless ",
+ "Sentry ",
+ "Heavily Armed ",
+ "Psychic ",
+ "Major Psychic ",
+ "Gruff ",
+ "Clumsy ",
+ "Soul Consuming ",
+ "Demonic ",
+ "High-class ",
+ "Robo-",
+ "Plain ",
+ "Strong ",
+ "Hard ",
+ "No Good ",
+ "Mostly Bad ",
+ "Spiteful ",
+ "Farm ",
+ "Criminal ",
+ "Master Criminal ",
+ "Final ",
+ "Plague ",
+ "Disingenuous ",
+ "Disgusting ",
+ "Inadequate ",
+ "Dr. ",
+ "Electronic ",
+ "Jealous ",
+ "Bald ",
+ "Baked ",
+ "Magic ",
+ "Polar ",
+ "Depressed ",
+ "Clean ",
+ "Elderly ",
+ "Banned ",
+ "Grumpy ",
+ "Whelmed ",
+ "Loud ",
+ "Mushy ",
+ "Randomized ",
+ "Homesick ",
+ "Mecha ",
+ "Ramblin' Evil ",
+ "Master ",
+ "Secret ",
+ "Platinum ",
+ "Armed ",
+ "Condemned ",
+ "Rad ",
+ "Hangry ",
+ "Moonsidian ",
+ "No. 3 ",
+ "Legal ",
+ "Copyrighted ",
+ "Cyanotic ",
+ "Serendipitous ",
+ "Disgruntled "
+]
+
+battle_sprites = {
+ "Antoid": [0x01],
+ "Dog": [0x02, 0x42],
+ "Frog": [0x03],
+ "Sphere": [0x04],
+ "Mushroom": [0x05],
+ "Bomb": [0x06],
+ "Octobot": [0x07],
+ "Musica": [0x08],
+ "Ranboob": [0x09],
+ "Moldyman": [0x0A],
+ "Fungus": [0x0B],
+ "Booka": [0x0C],
+ "Shambler": [0x0D],
+ "Dino": [0x0E, 0x41],
+ "Art": [0x0F],
+ "Crow": [0x10],
+ "Fobby": [0x11],
+ "Foppy": [0x11],
+ "Duck": [0x12],
+ "Batty": [0x13],
+ "Mouse": [0x14],
+ "Kiss of Death": [0x15],
+ "Swoosh": [0x16],
+ "Petunia": [0x17],
+ "Flame": [0x18],
+ "Coil Snake": [0x19],
+ "Sign": [0x1A],
+ "Kid": [0x1B],
+ "Sam": [0x1C],
+ "Tom": [0x1C],
+ "Psycho": [0x1D],
+ "Mite": [0x1E],
+ "Fish": [0x1F],
+ "Fish's Brother": [0x1F],
+ "Burglar": [0x20],
+ "Painter": [0x21],
+ "Party Man": [0x22],
+ "Reveler": [0x22],
+ "Local Guy": [0x23],
+ "Menace": [0x24],
+ "Spirit": [0x24],
+ "Gangster": [0x25],
+ "Guy": [0x26],
+ "Storm": [0x27],
+ # Carbon
+ "Kraken": [0x29],
+ # Pokey
+ "Li'l UFO": [0x2B],
+ "Coffee Cup": [0x2C],
+ "Molecule": [0x2D],
+ "Record": [0x2E],
+ "Slug": [0x2F],
+ "Elemental": [0x30],
+ "UFO": [0x31],
+ "Possessor": [0x32],
+ "Cop": [0x33],
+ "Robo": [0x34],
+ "Ghost": [0x35],
+ "Robot": [0x36],
+ "Starman": [0x37, 0x6A],
+ "Shattered Man": [0x38],
+ "Specter": [0x39],
+ "Dice": [0x3A],
+ "Fire Plug": [0x3B],
+ "Protoplasm": [0x3C],
+ "Mook": [0x3D],
+ "Spook": [0x3D],
+ "Pump": [0x3E],
+ "Cultist": [0x3F],
+ "Man Junior": [0x40],
+ "Asp Hieroglyph": [0x43],
+ "Eel": [0x44],
+ "Chief": [0x45],
+ "Hieroglyph": [0x46],
+ "Bot": [0x47],
+ "Zombie": [0x48],
+ "Ant": [0x49],
+ "Buffalo": [0x4A],
+ "Rat of Doom": [0x4B],
+ # iamond Dog
+ "Arachnid": [0x4D],
+ "Mole": [0x4E],
+ "Statue": [0x4F],
+ "Roach": [0x50],
+ "Noose": [0x51],
+ "Tangoo": [0x52],
+ # "Digger": [0x53],
+ "Crocodile": [0x54],
+ "Taxi": [0x55],
+ "Demon": [0x56],
+ "General": [0x57],
+ "Royal Guard": [0x57],
+ # Trillionage sprout
+ "Fly": [0x59],
+ "Mobile Sprout": [0x5A],
+ "Little Pile": [0x5B],
+ "Skelpion": [0x5C],
+ "Clock": [0x5D],
+ "Goat": [0x5E],
+ "Oak": [0x5F],
+ "Punk": [0x60, 0x64],
+ "Tank": [0x61],
+ "Pile of Puke": [0x62],
+ "Lady": [0x63],
+ "Boy": [0x65],
+ "Orb": [0x66],
+ "Wolf": [0x67],
+ "Bear": [0x68],
+ "Bear Seven": [0x68],
+ "Power Robot": [0x69],
+ "Reactor Robot": [0x69],
+ "Hippie": [0x6B],
+ "Worm": [0x6D],
+ "Caterpillar": [0x6D],
+ "Eye": [0x6E]
+
+
+}
+
+field_sprites = [
+ 0x00,
+ 0x013C,
+ 0x014A,
+ 0x0118,
+ 0x012E,
+ 0x0123,
+ 0x0115,
+ 0x0132,
+ 0xC3,
+ 0x0124,
+ 0x011D,
+ 0x0123,
+ 0x0128,
+ 0x0132,
+ 0x012A,
+ 0x012C,
+ 0x011A,
+ 0x0116,
+ 0x011F,
+ 0x0112,
+ 0x01A0,
+ 0x0144,
+ 0x0114,
+ 0x0122,
+ 0x0117,
+ 0x011B,
+ 0x0135,
+ 0x01BC,
+ 0x011E,
+ 0x0117,
+ 0x0144,
+ 0x0120,
+ 0x9D,
+ 0x9F,
+ 0x0187,
+ 0x0184,
+ 0x0144,
+ 0x99,
+ 0x0186,
+ 0x0144,
+ 0x014A,
+ 0x0132,
+ 0x2C,
+ 0x0130,
+ 0xC3,
+ 0x0115,
+ 0xC3,
+ 0x013C,
+ 0x0117,
+ 0x0130,
+ 0x0131,
+ 0x0182,
+ 0x0132,
+ 0x011D,
+ 0x0136,
+ 0x012F,
+ 0x0133,
+ 0x0144,
+ 0xC3,
+ 0x0117,
+ 0xC3,
+ 0x0132,
+ 0x0117,
+ 0x65,
+ 0x011C,
+ 0x0138,
+ 0x016C,
+ 0x0140,
+ 0x012D,
+ 0x0183,
+ 0x0145,
+ 0x01A2,
+ 0x0134,
+ 0x0139,
+ 0x0137,
+ 0x01A0,
+ 0x014A,
+ 0x013A,
+ 0x019F,
+ 0x0125,
+ 0x013A,
+ 0x0143,
+ 0x0144,
+ 0x019F,
+ 0x014C,
+ 0x0121,
+ 0x0132,
+ 0x0142,
+ 0x013D,
+ 0x013E,
+ 0x013D,
+ 0x013B,
+ 0x013F,
+ 0x0146,
+ 0x0126,
+ 0x0129,
+ 0x011C,
+ 0x0191,
+ 0x0148,
+ 0x0188,
+ 0x011C,
+ 0x0149,
+ 0x0147,
+ 0x014A,
+ 0x014B,
+ 0x0132,
+ 0x0132,
+ 0x0185,
+ 0x0132,
+ 0x01A1,
+ 0x0132,
+]
+
+excluded_enemies = [
+ "Dept. Store Spook",
+ "Ness's Nightmare",
+ "Mr. Carpainter",
+ "Carbon Dog",
+ "Titanic Ant",
+ "Shrooom!",
+ "Plague Rat of Doom",
+ "Mondo Mole",
+ "Guardian Digger",
+ "Kraken",
+ "Guardian General",
+ "Starman Deluxe",
+ "Diamond Dog",
+ "Trillionage Sprout",
+ "Master Belch",
+ "Master Barf",
+ "Electro Specter",
+ "Boogey Tent",
+ "Everdred",
+ "Thunder and Storm",
+ "Frankystein Mark II",
+ "Frank",
+ "Evil Mani-Mani",
+ "Clumsy Robot",
+ "Dept. Store Spook (2)",
+ "Ness's Nightmare (2)",
+ "Mr. Carpainter (2)",
+ "Carbon Dog (2)",
+ "Chomposaur (2)",
+ "Titanic Ant (2)",
+ "Gigantic Ant (2)",
+ "Shrooom! (2)",
+ "Plague Rat of Doom (2)",
+ "Mondo Mole (2)",
+ "Guardian Digger (2)",
+ "Kraken (2)",
+ "Starman (2)",
+ "Starman Super (2)",
+ "Ghost of Starman (2)",
+ "Starman Deluxe (2)",
+ "Final Starman (2)",
+ "Diamond Dog (2)",
+ "Trillionage Sprout (2)",
+ "Master Belch (2)",
+ "Master Barf (2)",
+ "Boogey Tent (2)",
+ "Everdred (2)",
+ "Electro Specter (2)",
+ "Thunder and Storm (2)",
+ "Frankystein Mark II (2)",
+ "Evil Mani-Mani (2)",
+ "Clumsy Robot (2)",
+ "Robo-pump (2)",
+ "Guardian General (2)",
+ "Struttin' Evil Mushroom (2)",
+ "Runaway Dog (2)",
+ "Tiny Li'l Ghost",
+ "Starman Junior",
+ "Buzz Buzz",
+ "Heavily Armed Pokey",
+ "Giygas (2)",
+ "Giygas (3)",
+ "Giygas (4)",
+ "Giygas (5)",
+ "Mini Barf",
+ "Captain Strong",
+ "Giygas (6)",
+ "Clumsy Robot (3)",
+ "Giygas"
+]
+
+robots = [
+ "Power Robot",
+ "Reactor Robot",
+ "Robo",
+ "Sign",
+ "Shambler",
+ "Sphere",
+ "Octobot",
+ "Bomb",
+ "Li'l UFO",
+ "UFO",
+ "Taxi",
+ "Bot",
+ "Pump",
+ "Robot"
+]
+
+insects = [
+ "Antoid",
+ "Ant",
+ "Arachnid",
+ "Roach",
+ "Skelpion",
+ "Slug",
+ "Fly",
+ "Caterpillar",
+ "Worm"
+]
+
+movement_patterns = {
+ 0x65: 21,
+ 0x99: 21,
+ 0x9D: 21,
+ 0x9F: 21,
+ 0xC3: 31,
+ 0x0112: 25,
+ 0x01A2: 27,
+ 0x0114: 27,
+ 0x0115: 27,
+ 0x0116: 21,
+ 0x0117: 27,
+ 0x0118: 21,
+ 0x011A: 25,
+ 0x011B: 20,
+ 0x011C: 21,
+ 0x011D: 24,
+ 0x011E: 24,
+ 0x011F: 21,
+ 0x0120: 21,
+ 0x0121: 30,
+ 0x0122: 29,
+ 0x0123: 24,
+ 0x0124: 21,
+ 0x0125: 21,
+ 0x0126: 22,
+ 0x0128: 20,
+ 0x0129: 29,
+ 0x012A: 29,
+ 0x012C: 27,
+ 0x012D: 20,
+ 0x012E: 23,
+ 0x012F: 26,
+ 0x0130: 23,
+ 0x0131: 27,
+ 0x0132: 23,
+ 0x0133: 24,
+ 0x0134: 21,
+ 0x0135: 28,
+ 0x0136: 30,
+ 0x0137: 29,
+ 0x0138: 29,
+ 0x0139: 20,
+ 0x013A: 20,
+ 0x013B: 20,
+ 0x013C: 20,
+ 0x013D: 24,
+ 0x013E: 23,
+ 0x013F: 20,
+ 0x0140: 21,
+ 0x0142: 28,
+ 0x0143: 21,
+ 0x0144: 23,
+ 0x0145: 21,
+ 0x0146: 27,
+ 0x0147: 30,
+ 0x0148: 29,
+ 0x0149: 29,
+ 0x014A: 22,
+ 0x014B: 29,
+ 0x014C: 29,
+ 0x016C: 22,
+ 0x0182: 21,
+ 0x0183: 21,
+ 0x0184: 21,
+ 0x0185: 21,
+ 0x0186: 21,
+ 0x0187: 21,
+ 0x0188: 21,
+ 0x0191: 30,
+ 0x019F: 20,
+ 0x01A0: 20,
+ 0x01A1: 27,
+ 0x01BC: 21,
+}
+
+start_texts = [
+ 0xEF7858, # attacked
+ 0xEF7866, # blocked the way
+ 0xEF7879, # came after you
+ 0xEF788B, # trapped you
+ 0xEF789C, # you encounter
+ 0xEF78AB, # you meet
+ 0xEF78B8, # you engage
+ 0xEF78C7, # you confront
+]
+
+death_texts = [
+ 0xEF6D71,
+ 0xEF6D83, # Stopped moving
+ 0xEF6D96,
+ 0xEF6DA7, # disappeared
+ 0xEF6DB8, # melted into thin air
+ 0xEF6DD8, # broken into pieces
+ 0xEF6DF0, # destroyed
+ 0xEF6E03, # totally scrapped
+ 0xEF6E19, # turned back to normal
+ 0xEF6E31, # dust of the earth
+
+]
+
+
+weakness_table = {
+ "Fire": {
+ "Frog": 0,
+ "Buffalo": 0,
+ "Antoid": 0,
+ "Mushroom": 0,
+ "Fungus": 0,
+ "Mobile Sprout": 0,
+ "Fire Plug": 3,
+ "Record": 0,
+ "Power Robot": 3,
+ "Reactor Robot": 3,
+ "Hieroglyph": 0,
+ "Asp Hieroglyph": 2,
+ "Swoosh": 2,
+ "Elemental": 0,
+ "Moldyman": 3,
+ "Mite": 0,
+ "Ant": 0,
+ "Coffee Cup": 2,
+ "Dice": 0,
+ "Kraken": 2,
+ "Robo": 3,
+ "Roach": 0,
+ "Sign": 2,
+ "Shambler": 2,
+ "Skelpion": 0,
+ "Sphere": 0,
+ "Zombie": 0,
+ "Possessor": 3,
+ "Dog": 0,
+ "Oak": 0,
+ "Octobot": 0,
+ "Duck": 0,
+ "Wolf": 0,
+ "Eel": 0,
+ "Arachnid": 0,
+ "Demon": 0,
+ "Fobby": 0,
+ "Foppy": 0,
+ "Booka": 3,
+ "Mook": 0,
+ "Ghost": 0,
+ "Slug": 0,
+ "Mouse": 0,
+ "Bomb": 2,
+ "Tom": 0,
+ "Sam": 0,
+ "Fish": 3,
+ "Kid": 0,
+ "Boy": 0,
+ "Shattered Man": 2,
+ "Orb": 2,
+ "Li'l UFO": 0,
+ "UFO": 2,
+ "Taxi": 2,
+ "Molecule": 0,
+ "Protoplasm": 0,
+ "Robot": 2,
+ "Psycho": 3,
+ "Goat": 0,
+ "Flame": 3,
+ "Petunia": 0,
+ "Ranboob": 0,
+ "Noose": 0,
+ "Pump": 2,
+ "Crocodile": 2,
+ "Fly": 0,
+ "Crow": 0,
+ "Caterpillar": 0,
+ "Eye": 3,
+ "Worm": 0,
+ "Bot": 2,
+ "Gangster": 0,
+ "Tank": 0
+ },
+
+ "Freeze": {
+ "Frog": 0,
+ "Antoid": 0,
+ "Mushroom": 0,
+ "Fungus": 2,
+ "Mobile Sprout": 0,
+ "Fire Plug": 0,
+ "Power Robot": 0,
+ "Hieroglyph": 2,
+ "Asp Hieroglyph": 0,
+ "Elemental": 3,
+ "Moldyman": 0,
+ "Mite": 0,
+ "Ant": 0,
+ "Coffee Cup": 0,
+ "Dice": 0,
+ "Robo": 0,
+ "Coil Snake": 0,
+ "Roach": 0,
+ "Skelpion": 0,
+ "Sphere": 3,
+ "Royal Guard": 2,
+ "General": 2,
+ "Zombie": 3,
+ "Possessor": 0,
+ "Dog": 0,
+ "Oak": 2,
+ "Octobot": 2,
+ "Duck": 0,
+ "Wolf": 0,
+ "Pile of Puke": 2,
+ "Arachnid": 0,
+ "Demon": 3,
+ "Fobby": 0,
+ "Foppy": 0,
+ "Mook": 3,
+ "Ghost": 2,
+ "Slug": 0,
+ "Mouse": 0,
+ "Tom": 2,
+ "Sam": 2,
+ "Fish's Brother": 3,
+ "Kid": 0,
+ "Boy": 3,
+ "Orb": 0,
+ "Li'l UFO": 2,
+ "UFO": 2,
+ "Taxi": 2,
+ "Molecule": 0,
+ "Protoplasm": 0,
+ "Robot": 2,
+ "Goat": 0,
+ "Flame": 0,
+ "Petunia": 3,
+ "Ranboob": 2,
+ "Noose": 2,
+ "Pump": 2,
+ "Fly": 0,
+ "Crow": 0,
+ "Caterpillar": 0,
+ "Eye": 0,
+ "Worm": 0,
+ "Rat of Doom": 2,
+ "Bot": 2,
+ "Gangster": 0,
+ "Tank": 0
+ },
+
+ "Flash": {
+ "Fire Plug": 3,
+ "Record": 3,
+ "Power Robot": 2,
+ "Reactor Robot": 2,
+ "Swoosh": 0,
+ "Menace": 3,
+ "Spirit": 3,
+ "Elemental": 0,
+ "Mite": 3,
+ "Coffee Cup": 2,
+ "Dice": 2,
+ "Sign": 3,
+ "Starman": 2,
+ "Sphere": 3,
+ "Zombie": 0,
+ "Possessor": 0,
+ "Dog": 0,
+ "Octobot": 3,
+ "Musica": 2,
+ "Kiss of Death": 2,
+ "Tangoo": 0,
+ "Ghost": 0,
+ "Tom": 0,
+ "Sam": 0,
+ "Fish's Brother": 2,
+ "Kid": 0,
+ "Orb": 2,
+ "Li'l UFO": 2,
+ "UFO": 2,
+ "Taxi": 2,
+ "Molecule": 2,
+ "Robot": 3,
+ "Mole": 0,
+ "Pump": 2,
+ "Crow": 0,
+ "Caterpillar": 0,
+ "Eye": 3,
+ "Worm": 0,
+ "Specter": 3,
+ "Bot": 2,
+ "Storm": 0,
+ "Tank": 2
+ },
+
+ "Paralysis": {
+ "Spook": 2,
+ "Buffalo": 0,
+ "Antoid": 0,
+ "Fungus": 3,
+ "Mobile Sprout": 0,
+ "Power Robot": 3,
+ "Reactor Robot": 3,
+ "Asp Hieroglyph": 0,
+ "Elemental": 3,
+ "Bear": 2,
+ "Bear Seven": 2,
+ "Moldyman": 2,
+ "Dice": 0,
+ "Little Pile": 0,
+ "Coil Snake": 0,
+ "Batty": 0,
+ "Roach": 0,
+ "Starman": 2,
+ "Royal Guard": 0,
+ "General": 2,
+ "Clock": 3,
+ "Arachnid": 0,
+ "Demon": 3,
+ "Fobby": 0,
+ "Foppy": 0,
+ "Booka": 2,
+ "Mook": 2,
+ "Ghost": 0,
+ "Slug": 0,
+ "Fish's Brother": 2,
+ "Boy": 3,
+ "Art": 0,
+ "Shattered Man": 0,
+ "Orb": 3,
+ "Taxi": 0,
+ "Molecule": 0,
+ "Protoplasm": 0,
+ "Psycho": 2,
+ "Mole": 0,
+ "Petunia": 3,
+ "Fly": 0,
+ "Crow": 0,
+ "Caterpillar": 0,
+ "Eye": 2,
+ "Worm": 0,
+ "Statue": 3,
+ "Rat of Doom": 2,
+ "Specter": 3,
+ "Storm": 3,
+ "Dino": 2,
+ "Tank": 2
+ },
+
+ "Hypnosis": {
+ "Buffalo": 2,
+ "Antoid": 0,
+ "Mushroom": 3,
+ "Mobile Sprout": 3,
+ "Fire Plug": 3,
+ "Record": 3,
+ "Power Robot": 2,
+ "Reactor Robot": 2,
+ "Hieroglyph": 0,
+ "Asp Hieroglyph": 0,
+ "Menace": 2,
+ "Spirit": 2,
+ "Elemental": 0,
+ "Moldyman": 0,
+ "Coffee Cup": 2,
+ "Dice": 3,
+ "Kraken": 2,
+ "Coil Snake": 0,
+ "Batty": 3,
+ "Roach": 3,
+ "Sign": 0,
+ "Skelpion": 0,
+ "Sphere": 0,
+ "Dog": 0,
+ "Octobot": 2,
+ "Duck": 2,
+ "Clock": 0,
+ "Pile of Puke": 2,
+ "Kiss of Death": 0,
+ "Eel": 2,
+ "Arachnid": 3,
+ "Demon": 2,
+ "Fobby": 3,
+ "Foppy": 3,
+ "Booka": 0,
+ "Mook": 0,
+ "Ghost": 0,
+ "Slug": 3,
+ "Mouse": 2,
+ "Bomb": 2,
+ "Tom": 3,
+ "Fish": 2,
+ "Fish's Brother": 2,
+ "Kid": 0,
+ "Boy": 2,
+ "Art": 2,
+ "Orb": 2,
+ "Li'l UFO": 3,
+ "UFO": 3,
+ "Taxi": 2,
+ "Molecule": 3,
+ "Protoplasm": 3,
+ "Psycho": 3,
+ "Goat": 0,
+ "Mole": 2,
+ "Flame": 2,
+ "Petunia": 2,
+ "Noose": 3,
+ "Pump": 2,
+ "Fly": 3,
+ "Caterpillar": 0,
+ "Worm": 0,
+ "Rat of Doom": 2,
+ "Specter": 2,
+ "Bot": 2,
+ "Tank": 2
+ }
+}
diff --git a/worlds/earthbound/modules/enemizer/randomize_enemy_attacks.py b/worlds/earthbound/modules/enemizer/randomize_enemy_attacks.py
new file mode 100644
index 000000000000..01745a8fe4d4
--- /dev/null
+++ b/worlds/earthbound/modules/enemizer/randomize_enemy_attacks.py
@@ -0,0 +1,236 @@
+import struct
+from .enemy_attributes import excluded_enemies
+from ..enemy_data import spell_breaks
+from ..enemy_shuffler import enemy_ids
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from ... import EarthBoundWorld
+ from ...Rom import LocalRom
+
+battle_actions = { # Actions in camel case are scaled
+ "Attack": 0x04,
+ "Shoot": 0x05,
+ "Spy": 0x06,
+ "Defend": 0x08,
+ "None": 0x09,
+ "Magnet Alpha": 0x36,
+ "Magnet Omega": 0x37,
+ "Call": 0x3E,
+ "Sow Seeds": 0x3F,
+ "Steal": 0x42,
+ "Freeze in Time": 0x43,
+ "diamond_eyes": 0x44,
+ "Strange Beam": 0x46,
+ "nauseous_breath": 0x47,
+ "poison_stinger": 0x48,
+ "kiss_of_death": 0x49,
+ "Arctic Breath": 0x4A,
+ "Mushroom Spores": 0x4B,
+ "Possess": 0x4C,
+ "Sprinkle Powder": 0x4D,
+ "Mold Spores": 0x4E,
+ "Binding Attack": 0x4F,
+ "Sticky Mucus": 0x50,
+ "Spew Fly Honey": 0x51,
+ "Shoot Silk": 0x52,
+ "Say Something Scary": 0x53,
+ "Do Something Mysterious": 0x54,
+ "Disrupt Sense": 0x55,
+ "Size Up Situation": 0x56,
+ "Exhale Stinky": 0x57,
+ "summon_storm": 0x58,
+ "scalding_espresso": 0x59,
+ "Haunting Melody": 0x5A,
+ "extinguishing_blast": 0x5B,
+ "crashing_boom_bang": 0x5C,
+ "spray_fire": 0x5D,
+ "breathe_fire": 0x5E,
+ "Spin Around": 0x5F,
+ "Lose Temper": 0x60,
+ "Say Nasty": 0x61,
+ "Vacuum Attack": 0x62,
+ # "Replenish Fuel",
+ "poisonous_fangs": 0x64,
+ # Dizzy Missile
+ "Continuous Attack": 0x66,
+ "Guard": 0x67,
+ "flaming_fireball": 0x68,
+ "Intertwine": 0x69,
+ "Crushing Chop": 0x6A,
+ "Submission Hold": 0x6B,
+ "Rev and Accelerate": 0x6C,
+ "Brandish a Knife": 0x6D,
+ "Tear Into": 0x6E,
+ "Bite": 0x6F,
+ "Claw with Nails": 0x70,
+ "Swing Tail": 0x71,
+ "Lunge": 0x72,
+ "Shopping Bag": 0x73,
+ "Swing Club": 0x74,
+ "Tornado": 0x75,
+ "Spray Water": 0x76,
+ "Flash a Menacing Smile": 0x77,
+ "Laugh Hysterically": 78,
+ "Edge Closer": 0x79,
+ "Whisper 3": 0x7A,
+ "Murmur 2": 0x7B,
+ "Mutter 1": 0x7C,
+ "Fall down": 0x7D,
+ "Be Absentminded": 0x7E,
+ "Burst of Steam": 0x7F,
+ "Wobble": 0x80,
+ "Reel": 0x81,
+ "Big Grin": 0x82,
+ "Take Breath": 0x83,
+ "Greeting": 0x84,
+ "Howl": 0x85,
+ "Tick Tock": 0x86,
+ # "Eat Food": 0x8A,
+ "PSI Food": 0x8E,
+ # Counter PSI: 0x9F World.gadget actions?
+ # Shield Killer
+ # HP Sucker
+ "Yogurt Dispenser": 0xAA,
+ "Toothbrush": 0xAE,
+ "Sudden Guts": 0xB6,
+ "Ruler": 0xBA,
+ "Protractor": 0xBB,
+ "Fly Honey": 0xBD,
+ "glorious_light": 0xC9,
+ "electrical_shock": 0xCA,
+ "paralyzing_pollen": 0xCB,
+ "Icy Hand": 0xCC,
+ "poison_flute": 0xCD,
+ "Exhaust Fumes": 0xCE,
+ "Laugh Maniacally": 0xCF,
+ "Breathe Flute": 0xD0,
+ "Leap and Spread Wings": 0xD1,
+ "Become Friendly": 0xD2,
+ "Rumble": 0xD3,
+ "Give Hug": 0xD4,
+ "hacking_cough": 0xD5,
+ "Misery Attack": 0xD6,
+ "Paint Attack": 0xD7,
+ "Come Out Swinging": 0xD8,
+ "Scratch with Claws": 0xD9,
+ "Peck at Eyes": 0xDA,
+ "Ram and Trample": 0xDB,
+ "Punch": 0xDC,
+ "Spit Seeds": 0xDD,
+ "Fire a Beam": 0xDE,
+ "Spear": 0xDF,
+ "Stomp with Foot": 0xE0,
+ "Hula Hoop": 0xE1,
+ "Charge Forward": 0xE2,
+ "Shred on Skateboard": 0xE3,
+ "diamond_bite": 0xE4,
+ "Grumble": 0xE5,
+ "Lecture": 0xE6,
+ "Scow": 0xE7,
+ "Vent Odor": 0xE8,
+ "Shout": 0xE9,
+ "Shriek": 0xEA,
+ "Knit Brow": 0xEB,
+ "scatter_spores": 0xED,
+ "Bite Attack": 0xEE,
+ "stuffiness_beam": 0xF1,
+ "Coil Around": 0xF2,
+ "Emit Light": 0xF8,
+ "Homesick": 0xFB,
+ "Fake PSI": 0x0100,
+ "Bark": 0x0108,
+ "Chant": 0x0109,
+ "Scratch Head": 0x010B,
+ "Discharge Gas": 0x0111,
+ "Monkey Love": 0x011B,
+ "Lost Bolts": 0x013A,
+ "Clean Area": 0x013C,
+ "Want Battery": 0x013D,
+ "throw_bomb": 0x01FC,
+ "shoot_rocket": 0x0203
+
+}
+
+needs_argument = {
+ "PSI Food": [0xCF, 0xF7, 0x6E, 0xF6, 0x63, 0x62],
+ "Ruler": [0x8C],
+ "Protractor": [0x8F],
+ "Toothbrush": [0x9A],
+ "Monkey Love": [0xD1],
+ "Sudden Guts": [0x9F],
+ "Shield Alpha": [0x1F],
+ "Shield Beta": [0x21],
+ "PSI Shield Alpha": [0x23],
+ "PSI Shield Beta": [0x25],
+ "Offense Up Alpha": [0x27],
+ "Offense Up Omega": [0x28],
+ "Defense Down Alpha": [0x29],
+ "Defense Down Omega": [0x2A],
+ "Hypnosis Alpha": [0x2B],
+ "Hypnosis Omega": [0x2C],
+ "Brainshock": [0x31],
+ "Magnet Alpha": [0x2D],
+ "Magnet Omega": [0x2E],
+ "Yogurt Dispenser": [0x8B],
+ "Fake PSI": [0x55, 0x04, 0x14, 0x16, 0x30, 0x32, 0x4D, 0x57]
+}
+
+psi_actions = {
+ "special": 0x0A,
+ "fire": 0x0E,
+ "freeze": 0x12,
+ "thunder": 0x16,
+ "flash": 0x1A,
+ "starstorm": 0x1E,
+ "lifeup": 0x20,
+ "healing": 0x24,
+ "Shield Alpha": 0x28,
+ "Shield Beta": 0x29,
+ "PSI Shield Alpha": 0x2C,
+ "PSI Shield Beta": 0x2D,
+ "Offense Up Alpha": 0x30,
+ "Offense Up Omega": 0x31,
+ "Defense Down Alpha": 0x32,
+ "Defense Down Omega": 0x33,
+ "Hypnosis Alpha": 0x34,
+ "Hypnosis Omega": 0x35,
+ "paralysis": 0x38,
+ "Brainshock": 0x3A,
+ "blast": 0x01A4,
+ "missile": 0x01A8
+}
+
+
+
+def randomize_enemy_attacks(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ """Generates random attacks for enemies.
+ Certain attacks need to have an argument variable attached.
+ PSI moves have a 19% chance of being rolled only if the enemy has a non-zero max PP stat."""
+ for enemy in world.enemies:
+ if enemy not in excluded_enemies:
+ enemy_ai = world.random.randint(0, 3)
+ world.enemy_psi[enemy] = ["null", "null", "null", "null"]
+ max_calls = 0
+ for i in range(4):
+ if world.enemies[enemy].pp and world.random.randint(1, 100) < 20:
+ attack = world.random.choice(list(psi_actions.keys()))
+ attack_id = psi_actions[attack]
+ else:
+ attack = world.random.choice(list(battle_actions.keys()))
+ attack_id = battle_actions[attack]
+ if attack in spell_breaks:
+ world.enemy_psi[enemy][i] = attack
+
+ if attack in needs_argument:
+ argument = world.random.choice(needs_argument[attack])
+ elif attack in ["Sow Seeds", "Call"]:
+ argument = enemy_ids[enemy]
+ max_calls = world.random.randint(1, 4)
+ else:
+ argument = 0
+
+ rom.write_bytes(world.enemies[enemy].address + (0x46 + (i * 2)), struct.pack("H", attack_id))
+ rom.write_bytes(world.enemies[enemy].address + (0x50 + (i)), bytearray([argument]))
+ rom.write_bytes(world.enemies[enemy].address + 0x5C, bytearray([max_calls]))
+ rom.write_bytes(world.enemies[enemy].address + 0x45, bytearray([enemy_ai]))
+ # Todo; attack extenders?
diff --git a/worlds/earthbound/modules/enemizer/randomize_enemy_attributes.py b/worlds/earthbound/modules/enemizer/randomize_enemy_attributes.py
new file mode 100644
index 000000000000..b38200c30f49
--- /dev/null
+++ b/worlds/earthbound/modules/enemizer/randomize_enemy_attributes.py
@@ -0,0 +1,143 @@
+import struct
+from .enemy_attributes import (enemy_species, enemy_adjectives, battle_sprites, field_sprites, excluded_enemies,
+ insects, robots, movement_patterns, start_texts, death_texts, weakness_table)
+from ...game_data.text_data import calc_pixel_width, text_encoder
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from ... import EarthBoundWorld
+ from ...Rom import LocalRom
+shield_statuses = [
+ "phys_1",
+ "phys_2",
+ "psi_1",
+ "psi_2"
+]
+
+battle_songs = [
+ 0x60,
+ 0x61,
+ 0x62,
+ 0x63,
+ 0x64,
+ 0x65,
+ 0x66,
+ 0x67,
+ 0x68,
+ 0x69,
+ 0x8D,
+ 0x94
+]
+
+
+def randomize_enemy_attributes(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ """Randomizes various attributes of enemies. This includes the name,
+ gender, sprite, color, etc. Data can be found in enemy_attributes."""
+ taken_names = []
+ for enemy in world.enemies:
+ if enemy not in excluded_enemies and " (" not in enemy:
+ new_name = "FFFFFFFFFFFFFFFFFFFFFFFFFF"
+ pixel_width = calc_pixel_width(new_name)
+ species = "Null"
+ while not (len(new_name) <= 25 and new_name not in taken_names and pixel_width <= 95):
+ species = world.random.choice(enemy_species)
+ adjective = world.random.choice(enemy_adjectives)
+ new_name = adjective + species
+ pixel_width = calc_pixel_width(new_name)
+ taken_names.append(new_name)
+ sprite = world.random.choice(battle_sprites[species])
+ field_sprite = field_sprites[sprite]
+ world.enemy_sprites[enemy] = field_sprite
+ movement_pattern = movement_patterns[field_sprite]
+ palette = world.random.randint(1, 31)
+ gender = world.random.randint(1, 3)
+ if species in robots:
+ enemy_type = 2
+ elif species in insects:
+ enemy_type = 1
+ else:
+ enemy_type = 0
+ row = world.random.randint(0, 1)
+ mirror_chance = world.random.randint(0, 100)
+ start_text = world.random.choice(start_texts)
+ death_text = world.random.choice(death_texts)
+ if species in ["Power Robot", "Reactor Robot", "Sphere"]:
+ death_action = 0x0040
+ elif species == "Oak":
+ death_action = 0x0041
+ else:
+ death_action = 0x0000
+ music = world.random.choice(battle_songs)
+ drop_rate = world.random.randint(0, 7)
+ base_drop = world.random.choice(world.filler_drops)
+ if world.random.randint(1, 100) < 6:
+ status = world.random.randint(1, 7)
+ if status < 5:
+ world.enemies[enemy].has_shield = shield_statuses[status - 1]
+ else:
+ status = 0
+
+ if species in ["Party Man", "Reveler"]:
+ miss_rate = 6
+ elif species == "Boy":
+ miss_rate = 8
+ elif species == "Bot":
+ miss_rate = 5
+ else:
+ miss_rate = world.random.randint(0, 4)
+
+ fire_weakness = get_weakness("Fire", species)
+
+ freeze_weakness = get_weakness("Freeze", species)
+
+ flash_weakness = get_weakness("Flash", species)
+
+ paralysis_weakness = get_weakness("Paralysis", species)
+
+ hypnosis_weakness = get_weakness("Hypnosis", species)
+
+ address = world.enemies[enemy].address
+ new_name = text_encoder(new_name, 0x18)
+ if len(new_name) < 0x18:
+ new_name.extend([0x00])
+
+ if world.enemies[enemy].attack_extensions > 1:
+ num_enemies = world.enemies[enemy].attack_extensions
+ else:
+ num_enemies = 1
+
+ for i in range(num_enemies):
+ rom.write_bytes(address, bytearray([0x01]))
+ rom.write_bytes(address + 1, new_name)
+ rom.write_bytes(address + 0x1A, bytearray([gender]))
+ rom.write_bytes(address + 0x1B, bytearray([enemy_type]))
+ rom.write_bytes(address + 0x1C, struct.pack("H", sprite))
+ rom.write_bytes(address + 0x1E, struct.pack("H", field_sprite))
+ rom.write_bytes(address + 0x2B, struct.pack("H", movement_pattern))
+ rom.write_bytes(address + 0x2D, struct.pack("I", start_text))
+ rom.write_bytes(address + 0x31, struct.pack("I", death_text))
+ rom.write_bytes(address + 0x35, bytearray([palette]))
+ rom.write_bytes(address + 0x37, bytearray([music]))
+ rom.write_bytes(address + 0x3F, bytearray([fire_weakness]))
+ rom.write_bytes(address + 0x40, bytearray([freeze_weakness]))
+ rom.write_bytes(address + 0x41, bytearray([flash_weakness]))
+ rom.write_bytes(address + 0x42, bytearray([paralysis_weakness]))
+ rom.write_bytes(address + 0x43, bytearray([hypnosis_weakness]))
+ rom.write_bytes(address + 0x43, bytearray([miss_rate]))
+ rom.write_bytes(address + 0x4E, struct.pack("H", death_action))
+ rom.write_bytes(address + 0x57, bytearray([drop_rate]))
+ rom.write_bytes(address + 0x58, bytearray([base_drop]))
+ rom.write_bytes(address + 0x59, bytearray([status]))
+ rom.write_bytes(address + 0x5B, bytearray([row]))
+ rom.write_bytes(address + 0x5D, bytearray([mirror_chance]))
+ if world.enemies[enemy].attack_extensions > 1:
+ address = world.enemies[f"{enemy} ({i + 2})"].address
+ enemy = f"{enemy} ({i + 2})"
+
+
+def get_weakness(element: str, species: str) -> int:
+ """Returns a weakness to given element, given the enemy's base species."""
+ if species in weakness_table[element]:
+ weakness = weakness_table[element][species]
+ else:
+ weakness = 1
+ return weakness
diff --git a/worlds/earthbound/modules/enemizer/randomize_enemy_stats.py b/worlds/earthbound/modules/enemizer/randomize_enemy_stats.py
new file mode 100644
index 000000000000..382919aa4803
--- /dev/null
+++ b/worlds/earthbound/modules/enemizer/randomize_enemy_stats.py
@@ -0,0 +1,68 @@
+from .enemy_attributes import excluded_enemies
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from ... import EarthBoundWorld
+ from ...Rom import LocalRom
+
+
+class EnemyStatCopy:
+ def __init__(self, hp: int, exp: int, money: int, speed: int, offense: int,
+ defense: int, level: int, guts: int, luck: int):
+ self.hp = hp
+ self.exp = exp
+ self.money = money
+ self.speed = speed
+ self.offense = offense
+ self.defense = defense
+ self.level = level
+ self.guts = guts
+ self.luck = luck
+
+
+def randomize_enemy_stats(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ """Randomizes enemy stats. It does not actually randomize them, rather it gives enemies the stats of a random other enemy.
+ Enemies have a 19% chance to have PP."""
+ stat_copies = {}
+ for enemy in world.enemies:
+ if enemy not in excluded_enemies:
+ stat_copies[enemy] = EnemyStatCopy(
+ hp=world.enemies[enemy].hp,
+ exp=world.enemies[enemy].exp,
+ money=world.enemies[enemy].money,
+ speed=world.enemies[enemy].speed,
+ offense=world.enemies[enemy].offense,
+ defense=world.enemies[enemy].defense,
+ level=world.enemies[enemy].level,
+ guts=world.enemies[enemy].guts,
+ luck=world.enemies[enemy].luck
+ )
+
+ for enemy in world.enemies:
+ if enemy not in excluded_enemies:
+ copied_stat_base = world.random.choice(list(stat_copies))
+ world.enemies[enemy].hp = stat_copies[copied_stat_base].hp
+ if world.random.randint(1, 100) < 20:
+ world.enemies[enemy].pp = int(world.random.randint(10, 500) / 2)
+ else:
+ world.enemies[enemy].pp = 0
+ world.enemies[enemy].offense = stat_copies[copied_stat_base].offense
+ world.enemies[enemy].defense = stat_copies[copied_stat_base].defense
+ world.enemies[enemy].speed = stat_copies[copied_stat_base].speed
+ world.enemies[enemy].level = stat_copies[copied_stat_base].level
+ world.enemies[enemy].exp = stat_copies[copied_stat_base].exp
+ world.enemies[enemy].money = stat_copies[copied_stat_base].money
+ world.enemies[enemy].guts = stat_copies[copied_stat_base].guts
+ world.enemies[enemy].luck = stat_copies[copied_stat_base].luck
+ rom.write_bytes(world.enemies[enemy].address + 0x3D, bytearray([world.enemies[enemy].guts]))
+ rom.write_bytes(world.enemies[enemy].address + 0x3E, bytearray([world.enemies[enemy].luck]))
+ if world.enemies[enemy].attack_extensions > 0:
+ world.enemies[f"{enemy} (2)"].hp = world.enemies[enemy].hp
+ world.enemies[f"{enemy} (2)"].pp = world.enemies[enemy].pp
+ world.enemies[f"{enemy} (2)"].offense = world.enemies[enemy].offense
+ world.enemies[f"{enemy} (2)"].defense = world.enemies[enemy].defense
+ world.enemies[f"{enemy} (2)"].speed = world.enemies[enemy].speed
+ world.enemies[f"{enemy} (2)"].level = world.enemies[enemy].level
+ world.enemies[f"{enemy} (2)"].exp = world.enemies[enemy].exp
+ world.enemies[f"{enemy} (2)"].money = world.enemies[enemy].money
+ rom.write_bytes(world.enemies[f"{enemy} (2)"].address + 0x3D, bytearray([world.enemies[enemy].guts]))
+ rom.write_bytes(world.enemies[f"{enemy} (2)"].address + 0x3E, bytearray([world.enemies[enemy].luck]))
diff --git a/worlds/earthbound/modules/enemy_data.py b/worlds/earthbound/modules/enemy_data.py
new file mode 100644
index 000000000000..210a38645fe3
--- /dev/null
+++ b/worlds/earthbound/modules/enemy_data.py
@@ -0,0 +1,1320 @@
+from typing import Dict
+from collections import Counter
+import struct
+import math
+from typing import TYPE_CHECKING
+from logging import warning
+if TYPE_CHECKING:
+ from .. import EarthBoundWorld
+ from ..Rom import LocalRom
+
+
+
+class EarthBoundEnemy:
+ def __init__(self, name: str, address: int, hp: int, pp: int, exp: int, money: int, speed: int, offense: int,
+ defense: int, level: int, guts: int, luck: int, is_scaled: bool, shield: str | None = None,
+ attack_extensions: int = 0):
+ self.name = name
+ self.address = address
+ self.hp = hp
+ self.pp = pp
+ self.exp = exp
+ self.money = money
+ self.speed = speed
+ self.offense = offense
+ self.defense = defense
+ self.level = level
+ self.guts = guts
+ self.luck = luck
+ self.is_scaled = is_scaled
+ self.shield = shield
+ self.attack_extensions = attack_extensions
+
+
+def initialize_enemies(world: "EarthBoundWorld") -> None:
+ world.enemy_psi = {
+ "Dept. Store Spook": ["freeze", "fire", "lifeup", "null"],
+ "Dept. Store Spook (2)": ["null", "null", "freeze", "null"],
+ "Black Antoid": ["null", "null", "null", "lifeup"],
+ "Struttin' Evil Mushroom": ["null", "null", "null", "scatter_spores"],
+ "Mobile Sprout": ["null", "null", "null", "lifeup"],
+ "Tough Mobile Sprout": ["null", "null", "null", "lifeup"],
+ "Mystical Record": ["null", "null", "lifeup", "null"],
+ "Guardian Hieroglyph": ["hacking_cough", "thunder_minus", "flash", "thunder"],
+ "Electro Swoosh": ["null", "electrical_shock", "electrical_shock", "null"],
+ "Conducting Menace": ["flash_minus", "flash", "thunder_minus", "thunder"],
+ "Conducting Spirit": ["flash_minus", "flash", "thunder_minus", "thunder"],
+ "Ness's Nightmare": ["null", "special", "glorious_light", "null"],
+ "Mr. Carpainter": ["crashing_boom_bang", "lifeup", "null", "null"],
+ "Carbon Dog": ["flaming_fireball", "null", "null", "null"],
+ "Thunder Mite": ["thunder_minus", "thunder_minus", "thunder", "thunder"],
+ "Chomposaur": ["fire", "fire", "null", "null"],
+ "Gigantic Ant": ["null", "poison_stinger", "null", "null"],
+ "Shrooom!": ["null", "lifeup", "scatter_spores", "null"],
+ "Plague Rat of Doom": ["poisonous_fangs", "null", "null", "null"],
+ "Mondo Mole": ["lifeup", "null", "null", "null"],
+ "Scalding Coffee Cup": ["scalding_espresso", "scalding_espresso", "scalding_espresso", "scalding_espresso"],
+ "Arachnid!": ["null", "null", "null", "poison_stinger"],
+ "Arachnid!!!": ["poison_stinger", "null", "null", "null"],
+ "Slimy Little Pile": ["hacking_cough", "null", "null", "null"],
+ "Even Slimier Little Pile": ["null", "null", "null", "hacking_cough"],
+ "Kraken": ["breathe_fire", "null", "crashing_boom_bang", "null"],
+ "Bionic Kraken": ["null", "crashing_boom_bang", "breathe_fire", "null"], # Generate tornado?
+ "Spinning Robo": ["null", "null", "stuffiness_beam", "null"],
+ "Whirling Robo": ["null", "stuffiness_beam", "null", "null"],
+ "Thirsty Coil Snake": ["poisonous_fangs", "null", "null", "null"],
+ "Crazed Sign": ["null", "null", "paralysis", "null"],
+ "Wooly Shambler": ["null", "null", "null", "flash"],
+ "Wild 'n Wooly Shambler": ["null", "null", "null", "flash"],
+ "Skelpion": ["null", "poison_stinger", "null", "thunder"],
+ "Dread Skelpion": ["poison_stinger", "null", "thunder", "thunder"],
+ "Ghost of Starman": ["starstorm_minus", "null", "null", "null"],
+ "Smilin' Sphere": ["null", "fire", "null", "null"],
+ "Uncontrollable Sphere": ["null", "fire", "fire", "null"],
+ "Starman Deluxe": ["null", "null", "starstorm", "null"],
+ "Final Starman": ["null", "null", "starstorm", "null"],
+ "Urban Zombie": ["null", "null", "nauseous_breath", "null"],
+ "Zombie Dog": ["null", "null", "null", "poisonous_fangs"],
+ "Diamond Dog": ["null", "null", "null", "glorious_light"],
+ "Trillionage Sprout": ["null", "null", "flash", "paralysis"],
+ "Musica": ["electrical_shock", "null", "null", "electrical_shock"],
+ "Desert Wolf": ["null", "null", "null", "poisonous_fangs"],
+ "Master Belch": ["nauseous_breath", "nauseous_breath", "null", "null"],
+ "Big Pile of Puke": ["null", "hacking_cough", "null", "nauseous_breath"],
+ "Master Barf": ["nauseous_breath", "null", "null", "null"],
+ "Kiss of Death": ["null", "null", "kiss_of_death", "null"],
+ "French Kiss of Death": ["kiss_of_death", "kiss_of_death", "kiss_of_death", "kiss_of_death"],
+ "Zap Eel": ["electrical_shock", "electrical_shock", "electrical_shock", "electrical_shock"],
+ "Tangoo": ["null", "null", "poison_flute", "null"],
+ "Squatter Demon": ["poisonous_fangs", "diamond_bite", "null", "null"],
+ "Lesser Mook": ["freeze", "freeze", "null", "diamond_eyes"],
+ "Mook Senior": ["freeze", "fire", "lifeup", "diamond_eyes"],
+ "Smelly Ghost": ["null", "null", "lifeup", "null"],
+ "Deadly Mouse": ["poisonous_fangs", "null", "null", "null"],
+ "Care Free Bomb": ["throw_bomb_minus", "throw_bomb_minus", "throw_bomb_minus", "throw_bomb"],
+ "Electro Specter": ["electrical_shock", "null", "electrical_shock", "null"],
+ "Smilin' Sam": ["null", "null", "null", "lifeup"],
+ "Manly Fish's Brother": ["null", "paralysis", "freeze", "healing"],
+ "Thunder and Storm": ["crashing_boom_bang", "null", "null", "null"],
+ "Cute Li'l UFO": ["null", "null", "null", "lifeup"],
+ "Beautiful UFO": ["null", "null", "null", "lifeup"],
+ "Evil Mani-Mani": ["null", "paralysis", "null", "null"],
+ "Mr. Molecule": ["thunder", "flash", "fire", "freeze"],
+ "Sentry Robot": ["null", "null", "null", "shoot_rocket"],
+ "Psychic Psycho": ["fire", "fire", "fire", "fire"],
+ "Major Psychic Psycho": ["fire", "null", "paralysis", "fire"],
+ "Soul Consuming Flame": ["null", "breathe_fire", "flaming_fireball", "spray_fire"],
+ "Demonic Petunia": ["null", "extinguishing_blast", "null", "paralyzing_pollen"],
+ "Li'l UFO": ["null", "null", "stuffiness_beam", "null"],
+ "Robo-Pump (2)": ["throw_bomb", "null", "null", "null"],
+ "Ness's Nightmare (2)": ["special", "lifeup", "special", "null"],
+ "Mr. Carpainter (2)": ["crashing_boom_bang", "null", "null", "null"],
+ "Carbon Dog (2)": ["spray_fire", "null", "null", "null"],
+ "Gigantic Ant (2)": ["paralysis", "null", "null", "null"],
+ "Chomposaur (2)": ["null", "null", "fire", "fire"],
+ "Guardian Digger (2)": ["null", "null", "null", "lifeup"],
+ "Kraken (2)": ["flash", "breathe_fire", "null", "null"], # tornado?
+ "Starman Super (2)": ["null", "healing", "null", "null"],
+ "Ghost of Starman (2)": ["null", "null", "starstorm", "null"],
+ "Final Starman (2)": ["starstorm", "null", "healing", "null"],
+ "Diamond Dog (2)": ["null", "null", "null", "diamond_bite"],
+ "Trillionage Sprout (2)": ["null", "null", "diamond_eyes", "null"],
+ "Master Belch (2)": ["nauseous_breath", "null", "null", "null"],
+ "Master Barf (2)": ["null", "null", "nauseous_breath", "null"],
+ "Boogey Tent (2)": ["null", "null", "flash", "null"],
+ "Electro Specter (2)": ["null", "null", "electrical_shock", "null"],
+ "Thunder and Storm (2)": ["summon_storm", "null", "crashing_boom_bang", "null"],
+ "Evil Mani-Mani (2)": ["null", "null", "glorious_light", "null"],
+ "Black Antoid (2)": ["lifeup", "lifeup", "lifeup", "lifeup"],
+ "Giygas (2)": ["special", "special", "special", "special"],
+ "Farm Zombie": ["null", "null", "nauseous_breath", "null"],
+ "Criminal Caterpillar": ["fire", "fire", "fire", "fire"],
+ "Evil Eye": ["null", "diamond_eyes", "paralysis", "null"],
+ "Master Criminal Worm": ["fire", "fire", "fire", "fire"],
+ "Giygas": ["giygas_phase2_thunder", "giygas_phase2_freeze", "giygas_phase2_flash", "null"],
+ "Giygas (5)": ["giygas_phase3_thunder", "giygas_phase3_freeze", "giygas_phase3_flash", "null"],
+ "Giygas (6)": ["giygas_phase4_thunder", "giygas_phase4_freeze", "giygas_phase4_flash", "null"],
+ "Starman Junior": ["fire", "null", "freeze", "fire"],
+ "Ultimate Octobot": ["null", "stuffiness_beam", "null", "stuffiness_beam"],
+ "Mechanical Octobot": ["null", "null", "null", "stuffiness_beam"]
+ }
+
+ world.enemy_sprites = {
+ "Insane Cultist": 0x0065,
+ "Armored Frog": 0x0118,
+ "Bad Buffalo": 0x0137,
+ "Black Antoid": 0x013C,
+ "Black Antoid (2)": 0x013C,
+ "Red Antoid": 0x013C,
+ "Ramblin' Evil Mushroom": 0x0123,
+ "Struttin' Evil Mushroom": 0x0123,
+ "Mobile Sprout": 0x013D,
+ "Tough Mobile Sprout": 0x013D,
+ "Enraged Fire Plug": 0x0117,
+ "Mystical Record": 0x00C3,
+ "Atomic Power Robot": 0x0132,
+ "Nuclear Reactor Robot": 0x0132,
+ "Guardian Hieroglyph": 0x0145,
+ "Lethal Asp Hieroglyph": 0x0140,
+ "Electro Swoosh": 0x0114,
+ "Conducting Menace": 0x0144,
+ "Conducting Spirit": 0x0144,
+ "Evil Elemental": 0x0117,
+ "Annoying Old Party Man": 0x0187,
+ "Annoying Reveler": 0x0187,
+ "Unassuming Local Guy": 0x0045,
+ "New Age Retro Hippie": 0x0169,
+ "Mighty Bear": 0x014B,
+ "Mighty Bear Seven": 0x014B,
+ "Putrid Moldyman": 0x011D,
+ "Thunder Mite": 0x0144,
+ "Cranky Lady": 0x0188,
+ "Extra Cranky Lady": 0x0188,
+ "Wetnosaur": 0x012A,
+ "Chomposaur": 0x0138,
+ "Gigantic Ant": 0x0139,
+ "Scalding Coffee Cup": 0x00C3,
+ "Loaded Dice": 0x00C3,
+ "Slimy Little Pile": 0x013B,
+ "Even Slimier Little Pile": 0x013B,
+ "Arachnid!": 0x013A,
+ "Arachnid!!!": 0x013A,
+ "Bionic Kraken": 0x0132,
+ "Spinning Robo": 0x0132,
+ "Whirling Robo": 0x0132,
+ "Hyper Spinning Robo": 0x0132,
+ "Cop": 0x004A,
+ "Coil Snake": 0x011B,
+ "Thirsty Coil Snake": 0x011B,
+ "Mr. Batty": 0x0112,
+ "Elder Batty": 0x0112,
+ "Violent Roach": 0x013A,
+ "Filthy Attack Roach": 0x013A,
+ "Crazed Sign": 0x0135,
+ "Wooly Shambler": 0x0132,
+ "Wild 'n Wooly Shambler": 0x0132,
+ "Skate Punk": 0x011C,
+ "Skelpion": 0x013F,
+ "Dread Skelpion": 0x013F,
+ "Starman": 0x012F,
+ "Starman Super": 0x012F,
+ "Ghost of Starman": 0x0132,
+ "Smilin' Sphere": 0x012E,
+ "Uncontrollable Sphere": 0x012E,
+ "Petrified Royal Guard": 0x0142,
+ "Final Starman": 0x0132,
+ "Urban Zombie": 0x0134,
+ "Zombie Possessor": 0x0131,
+ "Zombie Dog": 0x016C,
+ "Over Zealous Cop": 0x0182,
+ "Territorial Oak": 0x0129,
+ "Hostile Elder Oak": 0x0129,
+ "Marauder Octobot": 0x0132,
+ "Military Octobot": 0x0132,
+ "Mechanical Octobot": 0x0132,
+ "Ultimate Octobot": 0x0132,
+ "Mad Duck": 0x011F,
+ "Dali's Clock": 0x0146,
+ "Musica": 0x00C3,
+ "Desert Wolf": 0x014A,
+ "Big Pile of Puke": 0x0148,
+ "Kiss of Death": 0x0144,
+ "French Kiss of Death": 0x0115,
+ "Foppy": 0x0116,
+ "Fobby": 0x0116,
+ "Zap Eel": 0x012D,
+ "Tangoo": 0x0144,
+ "Squatter Demon": 0x0132,
+ "Crested Booka": 0x0128,
+ "Great Crested Booka": 0x0128,
+ "Lesser Mook": 0x0132,
+ "Mook Senior": 0x0132,
+ "Smelly Ghost": 0x011D,
+ "Stinky Ghost": 0x011D,
+ "Attack Slug": 0x013C,
+ "Pit Bull Slug": 0x013C,
+ "Rowdy Mouse": 0x01A0,
+ "Deadly Mouse": 0x01A0,
+ "Care Free Bomb": 0x0115,
+ "Handsome Tom": 0x011E,
+ "Smilin' Sam": 0x011E,
+ "Manly Fish": 0x0120,
+ "Manly Fish's Brother": 0x0120,
+ "Runaway Dog": 0x014A,
+ "Trick or Trick Kid": 0x01BC,
+ "Cave Boy": 0x0149,
+ "Abstract Art": 0x012C,
+ "Shattered Man": 0x0142,
+ "Fierce Shattered Man": 0x0133,
+ "Ego Orb": 0x0147,
+ "Yes Man Junior": 0x011C,
+ "Cute Li'l UFO": 0x0130,
+ "Beautiful UFO": 0x0130,
+ "Pogo Punk": 0x011C,
+ "Tough Guy": 0x0186,
+ "Mad Taxi": 0x0121,
+ "Mr. Molecule": 0x0115,
+ "Worthless Protoplasm": 0x00C3,
+ "Sentry Robot": 0x0136,
+ "Psychic Psycho": 0x0117,
+ "Major Psychic Psycho": 0x0117,
+ "Mole Playing Rough": 0x019F,
+ "Gruff Goat": 0x0126,
+ "Soul Consuming Flame": 0x0117,
+ "Demonic Petunia": 0x0122,
+ "Ranboob": 0x0124,
+ "Li'l UFO": 0x0130,
+ "High-class UFO": 0x0130,
+ "Noose Man": 0x0143,
+ "Robo-pump": 0x0117,
+ "Plain Crocodile": 0x014C,
+ "Strong Crocodile": 0x014C,
+ "Hard Crocodile": 0x014C,
+ "No Good Fly": 0x013E,
+ "Mostly Bad Fly": 0x013E,
+ "Spiteful Crow": 0x011A,
+ "Farm Zombie": 0x0134,
+ "Criminal Caterpillar": 0x01A1,
+ "Evil Eye": 0x0132,
+ "Master Criminal Worm": 0x01CD,
+ "Loaded Dice 2": 0x00C3,
+}
+
+ world.enemies = {
+ "Insane Cultist": EarthBoundEnemy("Insane Cultist", 0x1595e7, 94, 0, 353, 33, 8, 19, 25, 13, 20, 64, False),
+ "Dept. Store Spook": EarthBoundEnemy("Dept. Store Spook", 0x159645, 610, 290, 24291, 1648, 19, 82, 135, 42, 24, 62, False, None, 2),
+ "Armored Frog": EarthBoundEnemy("Armored Frog", 0x1596a3, 202, 0, 1566, 77, 7, 37, 108, 22, 5, 8, False),
+ "Bad Buffalo": EarthBoundEnemy("Bad Buffalo", 0x159701, 341, 0, 4108, 172, 11, 64, 104, 34, 5, 5, False),
+ "Black Antoid": EarthBoundEnemy("Black Antoid", 0x15975f, 34, 25, 37, 7, 4, 14, 13, 7, 3, 0, False),
+ "Red Antoid": EarthBoundEnemy("Red Antoid", 0x1597bd, 112, 30, 1175, 35, 10, 29, 27, 20, 4, 0, False),
+ "Ramblin' Evil Mushroom": EarthBoundEnemy("Ramblin' Evil Mushroom", 0x15981b, 60, 0, 95, 15, 5, 15, 10, 7, 5, 1, False),
+ "Struttin' Evil Mushroom": EarthBoundEnemy("Struttin' Evil Mushroom", 0x159879, 157, 0, 1492, 95, 28, 29, 22, 17, 7, 1, False),
+ "Mobile Sprout": EarthBoundEnemy("Mobile Sprout", 0x1598d7, 79, 9, 133, 13, 6, 17, 12, 10, 5, 1, False),
+ "Tough Mobile Sprout": EarthBoundEnemy("Tough Mobile Sprout", 0x159935, 179, 13, 1865, 119, 18, 33, 27, 21, 6, 1, False),
+ "Enraged Fire Plug": EarthBoundEnemy("Enraged Fire Plug", 0x159993, 309, 0, 4321, 346, 14, 60, 81, 32, 5, 4, False),
+ "Mystical Record": EarthBoundEnemy("Mystical Record", 0x1599f1, 263, 35, 2736, 310, 20, 63, 78, 33, 12, 7, False),
+ "Atomic Power Robot": EarthBoundEnemy("Atomic Power Robot", 0x159a4f, 594, 0, 26937, 730, 25, 119, 133, 56, 8, 12, False),
+ "Nuclear Reactor Robot": EarthBoundEnemy("Nuclear Reactor Robot", 0x159aad, 798, 0, 53142, 820, 46, 142, 185, 64, 8, 12, False),
+ "Guardian Hieroglyph": EarthBoundEnemy("Guardian Hieroglyph", 0x159b0b, 470, 126, 13064, 470, 20, 94, 106, 48, 20, 38, False),
+ "Lethal Asp Hieroglyph": EarthBoundEnemy("Lethal Asp Hieroglyph", 0x159b69, 458, 0, 11321, 625, 21, 89, 94, 46, 5, 36, False),
+ "Electro Swoosh": EarthBoundEnemy("Electro Swoosh", 0x159bc7, 543, 338, 17075, 791, 40, 140, 156, 62, 5, 10, False),
+ "Conducting Menace": EarthBoundEnemy("Conducting Menace", 0x159c25, 445, 238, 14792, 574, 20, 107, 107, 52, 5, 8, False),
+ "Conducting Spirit": EarthBoundEnemy("Conducting Spirit", 0x159c83, 587, 329, 30390, 804, 26, 130, 139, 59, 5, 8, False),
+ "Evil Elemental": EarthBoundEnemy("Evil Elemental", 0x159ce1, 564, 0, 35737, 853, 30, 121, 136, 57, 7, 16, False),
+ "Ness's Nightmare": EarthBoundEnemy("Ness's Nightmare", 0x159d3f, 1654, 882, 89004, 4442, 31, 172, 253, 71, 1, 80, False, "psi_1", 2),
+ "Annoying Old Party Man": EarthBoundEnemy("Annoying Old Party Man", 0x159d9d, 99, 0, 130, 32, 6, 20, 25, 13, 50, 15, False),
+ "Annoying Reveler": EarthBoundEnemy("Annoying Reveler", 0x159dfb, 288, 0, 2373, 268, 17, 58, 77, 31, 50, 15, False),
+ "Unassuming Local Guy": EarthBoundEnemy("Unassuming Local Guy", 0x159e59, 73, 0, 146, 19, 5, 18, 13, 9, 1, 14, False),
+ "New Age Retro Hippie": EarthBoundEnemy("New Age Retro Hippie", 0x159eb7, 87, 0, 160, 23, 5, 19, 14, 11, 10, 16, False),
+ "Mr. Carpainter": EarthBoundEnemy("Mr. Carpainter", 0x159f15, 262, 70, 1412, 195, 8, 33, 45, 21, 13, 72, False, None, 2),
+ "Carbon Dog": EarthBoundEnemy("Carbon Dog", 0x159f73, 1672, 0, 0, 0, 31, 159, 174, 70, 52, 53, False, None, 2),
+ "Mighty Bear": EarthBoundEnemy("Mighty Bear", 0x159fd1, 167, 0, 609, 49, 7, 29, 31, 16, 1, 5, False),
+ "Mighty Bear Seven": EarthBoundEnemy("Mighty Bear Seven", 0x15a02f, 367, 0, 8884, 440, 11, 85, 76, 42, 1, 4, False),
+ "Putrid Moldyman": EarthBoundEnemy("Putrid Moldyman", 0x15a08d, 203, 0, 830, 53, 9, 36, 41, 21, 5, 17, False),
+ "Thunder Mite": EarthBoundEnemy("Thunder Mite", 0x15a0eb, 293, 200, 10798, 430, 20, 85, 83, 43, 13, 8, False),
+ "Cranky Lady": EarthBoundEnemy("Cranky Lady", 0x15a149, 95, 0, 200, 17, 6, 16, 18, 8, 3, 32, False),
+ "Extra Cranky Lady": EarthBoundEnemy("Extra Cranky Lady", 0x15a1a7, 277, 0, 3651, 134, 17, 48, 70, 27, 5, 32, False),
+ "Wetnosaur": EarthBoundEnemy("Wetnosaur", 0x15a263, 1030, 0, 33098, 745, 17, 126, 172, 59, 2, 16, False),
+ "Chomposaur": EarthBoundEnemy("Chomposaur", 0x15a2c1, 1288, 320, 44378, 896, 17, 139, 183, 62, 3, 16, False, "phys_2", 2),
+ "Titanic Ant": EarthBoundEnemy("Titanic Ant", 0x15a31f, 235, 102, 685, 150, 6, 19, 23, 13, 9, 72, False, None, 2),
+ "Gigantic Ant": EarthBoundEnemy("Gigantic Ant", 0x15a37d, 308, 81, 3980, 304, 17, 54, 112, 30, 5, 6, False, None, 2),
+ "Shrooom!": EarthBoundEnemy("Shrooom!", 0x15a3db, 1700, 112, 96323, 4086, 18, 95, 154, 48, 32, 72, False, None, 2),
+ "Plague Rat of Doom": EarthBoundEnemy("Plague Rat of Doom", 0x15a439, 1827, 60, 115272, 4464, 19, 71, 180, 47, 250, 45, False, None, 2),
+ "Mondo Mole": EarthBoundEnemy("Mondo Mole", 0x15a497, 498, 161, 5791, 400, 9, 37, 50, 23, 15, 36, False, None, 2),
+ "Guardian Digger": EarthBoundEnemy("Guardian Digger", 0x15a4f5, 386, 110, 17301, 1467, 17, 59, 129, 32, 21, 55, False, "phys_2", 2),
+ "Scalding Coffee Cup": EarthBoundEnemy("Scalding Coffee Cup", 0x15a553, 190, 0, 2462, 280, 23, 55, 20, 30, 5, 1, False),
+ "Loaded Dice": EarthBoundEnemy("Loaded Dice", 0x15a5b1, 307, 0, 10672, 703, 77, 146, 113, 59, 75, 6, False, None),
+ "Slimy Little Pile": EarthBoundEnemy("Slimy Little Pile", 0x15a60f, 224, 0, 1978, 124, 15, 42, 61, 24, 7, 38, False),
+ "Even Slimier Little Pile": EarthBoundEnemy("Even Slimier Little Pile", 0x15a66d, 326, 0, 15075, 579, 22, 103, 101, 49, 9, 38, False),
+ "Arachnid!": EarthBoundEnemy("Arachnid!", 0x15a6cb, 216, 0, 4933, 296, 23, 61, 30, 32, 3, 0, False),
+ "Arachnid!!!": EarthBoundEnemy("Arachnid!!!", 0x15a729, 344, 0, 10449, 412, 20, 87, 86, 45, 4, 0, False),
+ "Kraken": EarthBoundEnemy("Kraken", 0x15a787, 1097, 176, 79267, 3049, 21, 105, 166, 54, 1, 32, False, None, 2),
+ "Bionic Kraken": EarthBoundEnemy("Bionic Kraken", 0x15a7e5, 900, 60, 50308, 960, 42, 155, 195, 70, 1, 32, False),
+ "Spinning Robo": EarthBoundEnemy("Spinning Robo", 0x15a843, 113, 17, 297, 21, 7, 21, 22, 14, 5, 12, False),
+ "Whirling Robo": EarthBoundEnemy("Whirling Robo", 0x15a8a1, 374, 36, 5782, 256, 18, 78, 90, 39, 5, 12, False),
+ "Hyper Spinning Robo": EarthBoundEnemy("Hyper Spinning Robo", 0x15a8ff, 553, 83, 28866, 756, 28, 122, 130, 56, 5, 12, False),
+ "Cop": EarthBoundEnemy("Cop", 0x15a95d, 75, 0, 86, 18, 5, 15, 18, 7, 7, 16, False),
+ "Coil Snake": EarthBoundEnemy("Coil Snake", 0x15a9bb, 18, 0, 1, 4, 2, 3, 4, 1, 0, 6, False),
+ "Thirsty Coil Snake": EarthBoundEnemy("Thirsty Coil Snake", 0x15aa19, 270, 0, 2786, 276, 18, 52, 80, 28, 5, 7, False),
+ "Mr. Batty": EarthBoundEnemy("Mr. Batty", 0x15aa77, 86, 0, 304, 30, 29, 25, 5, 15, 4, 3, False),
+ "Elder Batty": EarthBoundEnemy("Elder Batty", 0x15aad5, 294, 0, 4177, 371, 33, 66, 72, 35, 8, 4, False),
+ "Violent Roach": EarthBoundEnemy("Violent Roach", 0x15ab33, 209, 0, 1757, 80, 35, 30, 26, 18, 9, 24, False),
+ "Filthy Attack Roach": EarthBoundEnemy("Filthy Attack Roach", 0x15ab91, 399, 0, 10543, 432, 77, 84, 33, 42, 9, 24, False),
+ "Crazed Sign": EarthBoundEnemy("Crazed Sign", 0x15abef, 295, 98, 3618, 244, 17, 64, 96, 34, 5, 11, False),
+ "Wooly Shambler": EarthBoundEnemy("Wooly Shambler", 0x15ac4d, 391, 140, 5397, 458, 18, 81, 91, 40, 5, 63, False),
+ "Wild 'n Wooly Shambler": EarthBoundEnemy("Wild 'n Wooly Shambler", 0x15acab, 722, 212, 33818, 906, 38, 144, 171, 65, 5, 63, False),
+ "Skate Punk": EarthBoundEnemy("Skate Punk", 0x15ad09, 31, 0, 12, 17, 5, 7, 8, 3, 0, 13, False),
+ "Skelpion": EarthBoundEnemy("Skelpion", 0x15ad67, 137, 21, 1823, 140, 37, 41, 23, 24, 80, 7, False),
+ "Dread Skelpion": EarthBoundEnemy("Dread Skelpion", 0x15adc5, 214, 125, 9908, 609, 40, 82, 57, 41, 88, 8, False),
+ "Starman": EarthBoundEnemy("Starman", 0x15ae23, 545, 155, 23396, 720, 24, 103, 126, 55, 25, 16, False, None, 2),
+ "Starman Super": EarthBoundEnemy("Starman Super", 0x15ae81, 568, 310, 30145, 735, 24, 112, 129, 56, 25, 16, False, "psi_2", 1),
+ "Ghost of Starman": EarthBoundEnemy("Ghost of Starman", 0x15aedf, 750, 462, 48695, 807, 46, 152, 170, 68, 43, 16, False, None, 2),
+ "Smilin' Sphere": EarthBoundEnemy("Smilin' Sphere", 0x15af3d, 233, 60, 2218, 191, 17, 50, 65, 27, 5, 13, False),
+ "Uncontrollable Sphere": EarthBoundEnemy("Uncontrollable Sphere", 0x15af9b, 577, 180, 20389, 796, 27, 116, 134, 56, 5, 15, False, "psi_1"),
+ "Petrified Royal Guard": EarthBoundEnemy("Petrified Royal Guard", 0x15aff9, 573, 0, 19163, 628, 12, 106, 173, 53, 5, 5, False),
+ "Guardian General": EarthBoundEnemy("Guardian General", 0x15b057, 831, 6, 95390, 3235, 21, 109, 214, 55, 1, 7, False, None, 2),
+ "Starman Deluxe": EarthBoundEnemy("Starman Deluxe", 0x15b0b5, 1400, 418, 160524, 3827, 27, 143, 186, 65, 43, 21, False, "psi_2", 2),
+ "Final Starman": EarthBoundEnemy("Final Starman", 0x15b113, 840, 860, 61929, 915, 47, 178, 187, 71, 25, 24, False, "psi_2", 2),
+ "Urban Zombie": EarthBoundEnemy("Urban Zombie", 0x15b171, 171, 0, 700, 58, 10, 31, 24, 19, 15, 24, False),
+ "Zombie Possessor": EarthBoundEnemy("Zombie Possessor", 0x15b1cf, 176, 0, 950, 81, 30, 28, 19, 17, 9, 6, False),
+ "Zombie Dog": EarthBoundEnemy("Zombie Dog", 0x15b22d, 210, 0, 1354, 54, 30, 39, 51, 22, 10, 11, False),
+ "Over Zealous Cop": EarthBoundEnemy("Over Zealous Cop", 0x15b2e9, 325, 0, 7448, 420, 18, 69, 75, 36, 7, 16, False),
+ "Territorial Oak": EarthBoundEnemy("Territorial Oak", 0x15b347, 145, 41, 356, 29, 5, 26, 30, 15, 9, 4, False),
+ "Hostile Elder Oak": EarthBoundEnemy("Hostile Elder Oak", 0x15b3a5, 609, 76, 17567, 690, 14, 134, 146, 59, 11, 5, False),
+ "Diamond Dog": EarthBoundEnemy("Diamond Dog", 0x15b403, 3344, 154, 337738, 6968, 31, 167, 230, 70, 10, 47, False, "phys_2", 2),
+ "Marauder Octobot": EarthBoundEnemy("Marauder Octobot", 0x15b461, 482, 0, 14475, 499, 23, 99, 121, 49, 8, 24, False),
+ "Military Octobot": EarthBoundEnemy("Military Octobot", 0x15b4bf, 604, 0, 25607, 637, 26, 138, 147, 61, 8, 18, False),
+ "Mechanical Octobot": EarthBoundEnemy("Mechanical Octobot", 0x15b51d, 768, 0, 41738, 744, 43, 147, 176, 66, 8, 24, False),
+ "Ultimate Octobot": EarthBoundEnemy("Ultimate Octobot", 0x15b57b, 792, 0, 47876, 815, 44, 163, 181, 70, 8, 24, False),
+ "Mad Duck": EarthBoundEnemy("Mad Duck", 0x15b5d9, 51, 0, 41, 12, 30, 12, 24, 8, 5, 1, False),
+ "Dali's Clock": EarthBoundEnemy("Dali's Clock", 0x15b637, 296, 0, 2503, 314, 4, 65, 66, 34, 5, 4, False),
+ "Trillionage Sprout": EarthBoundEnemy("Trillionage Sprout", 0x15b695, 1048, 240, 30303, 1358, 16, 54, 88, 29, 21, 71, False, None, 2),
+ "Musica": EarthBoundEnemy("Musica", 0x15b6f3, 292, 0, 3748, 341, 21, 69, 85, 35, 20, 8, False),
+ "Desert Wolf": EarthBoundEnemy("Desert Wolf", 0x15b751, 247, 0, 3740, 114, 33, 57, 67, 30, 2, 11, False),
+ "Master Belch": EarthBoundEnemy("Master Belch", 0x15b7af, 650, 0, 12509, 664, 16, 50, 88, 27, 20, 61, False, None, 2), # Real one
+ "Big Pile of Puke": EarthBoundEnemy("Big Pile of Puke", 0x15b80d, 631, 0, 19659, 728, 16, 120, 158, 57, 26, 32, False),
+ "Master Barf": EarthBoundEnemy("Master Barf", 0x15b86b, 1319, 0, 125056, 3536, 24, 136, 177, 60, 39, 64, False, None, 2),
+ "Kiss of Death": EarthBoundEnemy("Kiss of Death", 0x15b8c9, 333, 0, 10354, 528, 19, 91, 100, 46, 7, 16, False),
+ "French Kiss of Death": EarthBoundEnemy("French Kiss of Death", 0x15b927, 588, 0, 19210, 879, 30, 160, 160, 70, 7, 16, False),
+ "Foppy": EarthBoundEnemy("Foppy", 0x15b985, 120, 10, 1311, 93, 1, 29, 9, 16, 5, 3, False),
+ "Fobby": EarthBoundEnemy("Fobby", 0x15b9e3, 240, 19, 18348, 620, 5, 98, 84, 48, 5, 3, False),
+ "Zap Eel": EarthBoundEnemy("Zap Eel", 0x15ba41, 370, 0, 12170, 611, 29, 97, 93, 48, 5, 8, False),
+ "Tangoo": EarthBoundEnemy("Tangoo", 0x15ba9f, 371, 5, 14718, 572, 19, 96, 99, 48, 20, 16, False),
+ "Boogey Tent": EarthBoundEnemy("Boogey Tent", 0x15bafd, 579, 56, 5500, 407, 10, 43, 69, 25, 16, 32, False, None, 2),
+ "Squatter Demon": EarthBoundEnemy("Squatter Demon", 0x15bb5b, 774, 60, 48311, 897, 45, 158, 192, 69, 25, 32, False),
+ "Crested Booka": EarthBoundEnemy("Crested Booka", 0x15bbb9, 265, 0, 3011, 130, 17, 53, 73, 28, 24, 37, False),
+ "Great Crested Booka": EarthBoundEnemy("Great Crested Booka", 0x15bc17, 452, 0, 16365, 604, 20, 100, 110, 49, 28, 40, False),
+ "Lesser Mook": EarthBoundEnemy("Lesser Mook", 0x15bc75, 401, 190, 7640, 467, 17, 76, 102, 39, 7, 16, False),
+ "Mook Senior": EarthBoundEnemy("Mook Senior", 0x15bcd3, 501, 700, 21056, 715, 25, 108, 122, 54, 7, 16, False),
+ "Smelly Ghost": EarthBoundEnemy("Smelly Ghost", 0x15bd31, 194, 50, 606, 71, 10, 35, 89, 21, 2, 9, False),
+ "Stinky Ghost": EarthBoundEnemy("Stinky Ghost", 0x15bd8f, 444, 0, 13179, 541, 18, 90, 179, 46, 4, 7, False),
+ "Everdred": EarthBoundEnemy("Everdred", 0x15bded, 182, 0, 986, 171, 6, 25, 35, 15, 10, 40, False, None, 2),
+ "Attack Slug": EarthBoundEnemy("Attack Slug", 0x15be4b, 30, 6, 27, 6, 1, 9, 2, 5, 0, 3, False),
+ "Pit Bull Slug": EarthBoundEnemy("Pit Bull Slug", 0x15bea9, 217, 11, 9994, 543, 2, 79, 77, 39, 5, 7, False),
+ "Rowdy Mouse": EarthBoundEnemy("Rowdy Mouse", 0x15bf07, 36, 0, 34, 9, 5, 7, 20, 6, 225, 2, False),
+ "Deadly Mouse": EarthBoundEnemy("Deadly Mouse", 0x15bf65, 416, 0, 9225, 406, 18, 63, 98, 38, 225, 13, False),
+ "Care Free Bomb": EarthBoundEnemy("Care Free Bomb", 0x15bfc3, 504, 0, 14941, 641, 31, 135, 215, 60, 15, 8, False),
+ "Electro Specter": EarthBoundEnemy("Electro Specter", 0x15c021, 3092, 80, 261637, 6564, 29, 148, 203, 67, 47, 56, False, "psi_2", 2),
+ "Handsome Tom": EarthBoundEnemy("Handsome Tom", 0x15c07f, 133, 16, 520, 45, 11, 27, 25, 16, 5, 8, False),
+ "Smilin' Sam": EarthBoundEnemy("Smilin' Sam", 0x15c0dd, 161, 55, 712, 48, 17, 34, 44, 20, 16, 16, False),
+ "Manly Fish": EarthBoundEnemy("Manly Fish", 0x15c13b, 500, 0, 15826, 624, 22, 83, 114, 42, 9, 20, False),
+ "Manly Fish's Brother": EarthBoundEnemy("Manly Fish's Brother", 0x15c199, 526, 210, 15970, 686, 24, 114, 123, 56, 11, 24, False),
+ "Runaway Dog": EarthBoundEnemy("Runaway Dog", 0x15c1f7, 21, 0, 4, 3, 26, 4, 5, 2, 0, 1, False),
+ "Trick or Trick Kid": EarthBoundEnemy("Trick or Trick Kid", 0x15c255, 142, 0, 570, 47, 7, 30, 37, 18, 12, 12, False),
+ "Cave Boy": EarthBoundEnemy("Cave Boy", 0x15c2b3, 314, 0, 618, 17, 79, 21, 33, 14, 0, 80, False),
+ "Abstract Art": EarthBoundEnemy("Abstract Art", 0x15c311, 301, 60, 4361, 255, 19, 67, 79, 35, 7, 7, False),
+ "Shattered Man": EarthBoundEnemy("Shattered Man", 0x15c36f, 694, 0, 44690, 2630, 18, 104, 138, 51, 25, 38, False),
+ "Fierce Shattered Man": EarthBoundEnemy("Fierce Shattered Man", 0x15c3cd, 516, 0, 17423, 577, 12, 101, 116, 50, 5, 4, False),
+ "Ego Orb": EarthBoundEnemy("Ego Orb", 0x15c42b, 592, 0, 24180, 836, 17, 125, 140, 58, 1, 8, False),
+ "Thunder and Storm": EarthBoundEnemy("Thunder and Storm", 0x15c489, 2065, 70, 129026, 4736, 21, 111, 178, 56, 35, 55, False, None, 2),
+ "Yes Man Junior": EarthBoundEnemy("Yes Man Junior", 0x15c4e7, 33, 0, 13, 18, 4, 8, 9, 4, 0, 14, False),
+ "Frankystein Mark II": EarthBoundEnemy("Frankystein Mark II", 0x15c545, 91, 0, 76, 31, 4, 15, 18, 7, 0, 40, False, None, 2),
+ "Frank": EarthBoundEnemy("Frank", 0x15c5a3, 63, 0, 50, 48, 7, 12, 17, 6, 5, 32, False),
+ "Cute Li'l UFO": EarthBoundEnemy("Cute Li'l UFO", 0x15c601, 162, 25, 1519, 110, 58, 49, 32, 27, 1, 70, False),
+ "Beautiful UFO": EarthBoundEnemy("Beautiful UFO", 0x15c65f, 339, 15, 8257, 426, 59, 86, 87, 44, 1, 71, False),
+ "Pogo Punk": EarthBoundEnemy("Pogo Punk", 0x15c6bd, 35, 0, 15, 18, 3, 8, 10, 4, 0, 15, False),
+ "Tough Guy": EarthBoundEnemy("Tough Guy", 0x15c71b, 342, 0, 9310, 525, 18, 72, 92, 37, 20, 16, False),
+ "Mad Taxi": EarthBoundEnemy("Mad Taxi", 0x15c779, 253, 0, 2336, 216, 38, 53, 68, 28, 5, 8, False),
+ "Evil Mani-Mani": EarthBoundEnemy("Evil Mani-Mani", 0x15c7d7, 860, 88, 28139, 1852, 15, 86, 145, 45, 1, 80, False, None, 2),
+ "Mr. Molecule": EarthBoundEnemy("Mr. Molecule", 0x15c835, 280, 21, 8708, 659, 18, 118, 97, 56, 5, 4, False),
+ "Worthless Protoplasm": EarthBoundEnemy("Worthless Protoplasm", 0x15c893, 38, 0, 17, 11, 27, 11, 21, 7, 0, 1, False),
+ "Sentry Robot": EarthBoundEnemy("Sentry Robot", 0x15c8f1, 372, 0, 5034, 392, 17, 77, 105, 39, 10, 4, False),
+ # "Heavily Armed Pokey ": EarthBoundEnemy("Heavily Armed Pokey", 0x15c94f, 1746, 999, 0, 0, 51, 150, 274, 72, False), Unused
+ "Psychic Psycho": EarthBoundEnemy("Psychic Psycho", 0x15c9ad, 591, 252, 30094, 682, 30, 124, 144, 58, 1, 24, False),
+ "Major Psychic Psycho": EarthBoundEnemy("Major Psychic Psycho", 0x15ca0b, 618, 574, 39247, 862, 31, 145, 152, 65, 1, 24, False),
+ "Mole Playing Rough": EarthBoundEnemy("Mole Playing Rough", 0x15ca69, 103, 0, 456, 36, 9, 22, 28, 14, 2, 8, False),
+ "Gruff Goat": EarthBoundEnemy("Gruff Goat", 0x15cac7, 45, 0, 20, 9, 12, 8, 23, 7, 0, 16, False),
+ "Clumsy Robot": EarthBoundEnemy("Clumsy Robot", 0x15cb25, 962, 0, 32378, 2081, 83, 88, 137, 46, 30, 49, False, "psi_1", 3),
+ "Soul Consuming Flame": EarthBoundEnemy("Soul Consuming Flame", 0x15cb83, 602, 0, 37618, 768, 30, 131, 262, 59, 14, 8, False),
+ "Demonic Petunia": EarthBoundEnemy("Demonic Petunia", 0x15cbe1, 478, 0, 15171, 724, 26, 102, 111, 50, 5, 9, False),
+ "Ranboob": EarthBoundEnemy("Ranboob", 0x15cc3f, 232, 42, 2486, 158, 20, 41, 63, 24, 1, 9, False),
+ "Li'l UFO": EarthBoundEnemy("Li'l UFO", 0x15cc9d, 82, 0, 223, 14, 53, 18, 17, 12, 13, 8, False),
+ "High-class UFO": EarthBoundEnemy("High-class UFO", 0x15ccfb, 433, 72, 12385, 456, 60, 93, 103, 47, 15, 24, False, "phys_1"),
+ "Noose Man": EarthBoundEnemy("Noose Man", 0x15cd59, 231, 0, 1990, 220, 18, 47, 52, 26, 5, 4, False),
+ "Robo-pump": EarthBoundEnemy("Robo-pump", 0x15cdb7, 431, 0, 4797, 349, 19, 70, 113, 36, 5, 4, False, None, 2),
+ "Plain Crocodile": EarthBoundEnemy("Plain Crocodile", 0x15ce15, 234, 0, 1928, 62, 10, 40, 55, 24, 1, 5, False),
+ "Strong Crocodile": EarthBoundEnemy("Strong Crocodile", 0x15ce73, 417, 0, 10122, 495, 17, 85, 131, 43, 5, 6, False),
+ "Hard Crocodile": EarthBoundEnemy("Hard Crocodile", 0x15ced1, 522, 0, 19484, 692, 23, 110, 128, 55, 10, 4, False),
+ "No Good Fly": EarthBoundEnemy("No Good Fly", 0x15cf2f, 100, 0, 415, 26, 10, 23, 13, 15, 3, 0, False),
+ "Mostly Bad Fly": EarthBoundEnemy("Mostly Bad Fly", 0x15cf8d, 141, 0, 1116, 84, 15, 32, 16, 19, 4, 0, False),
+ "Spiteful Crow": EarthBoundEnemy("Spiteful Crow", 0x15cfeb, 24, 0, 3, 5, 77, 5, 3, 3, 0, 1, False),
+ # "Master Belch": EarthBoundEnemy("Master Belch", 0x15d397, 650, 0, 12509, 664, 16, 50, 88, 27, False), Unused
+ # "Insane Cultist (2)": EarthBoundEnemy("Insane Cultist", 0x15d3f5, 94, 0, 353, 33, 8, 19, 25, 13, False),
+ "Dept. Store Spook (2)": EarthBoundEnemy("Dept. Store Spook (2)", 0x15d453, 610, 290, 24291, 1648, 19, 82, 135, 42, 24, 62, False),
+ "Ness's Nightmare (2)": EarthBoundEnemy("Ness's Nightmare (2)", 0x15d4b1, 1654, 882, 89004, 4442, 31, 172, 253, 71, 1, 80, False),
+ "Mr. Carpainter (2)": EarthBoundEnemy("Mr. Carpainter (2)", 0x15d50f, 262, 70, 1412, 195, 8, 33, 45, 21, 13, 72, False),
+ "Carbon Dog (2)": EarthBoundEnemy("Carbon Dog (2)", 0x15d56d, 1672, 0, 0, 0, 31, 159, 174, 70, 52, 53, False),
+ "Chomposaur (2)": EarthBoundEnemy("Chomposaur (2)", 0x15d5cb, 1288, 320, 44378, 896, 17, 139, 183, 62, 3, 16, False),
+ "Titanic Ant (2)": EarthBoundEnemy("Titanic Ant (2)", 0x15d629, 235, 102, 685, 150, 6, 19, 23, 13, 9, 72, False),
+ "Gigantic Ant (2)": EarthBoundEnemy("Gigantic Ant (2)", 0x15d687, 308, 81, 3980, 304, 17, 54, 112, 30, 5, 6, False),
+ "Shrooom! (2)": EarthBoundEnemy("Shrooom! (2)", 0x15d6e5, 1700, 112, 96323, 4086, 18, 95, 154, 48, 32, 72, False),
+ "Plague Rat of Doom (2)": EarthBoundEnemy("Plague Rat of Doom (2)", 0x15d743, 1827, 60, 115272, 4464, 19, 71, 180, 47, 250, 45, False),
+ "Mondo Mole (2)": EarthBoundEnemy("Mondo Mole (2)", 0x15d7a1, 498, 161, 5791, 400, 9, 37, 50, 23, 15, 36, False),
+ "Guardian Digger (2)": EarthBoundEnemy("Guardian Digger (2)", 0x15d7ff, 386, 110, 17301, 1467, 17, 59, 129, 32, 21, 55, False),
+ "Kraken (2)": EarthBoundEnemy("Kraken (2)", 0x15d85d, 1097, 176, 79267, 3049, 21, 105, 166, 54, 1, 32, False),
+ # "Bionic Kraken (2)": EarthBoundEnemy("Bionic Kraken", 0x15d8bb, 900, 60, 50308, 960, 42, 155, 195, 70, False),
+ "Starman (2)": EarthBoundEnemy("Starman (2)", 0x15d919, 545, 155, 23396, 720, 24, 103, 126, 55, 25, 16, False),
+ "Starman Super (2)": EarthBoundEnemy("Starman Super (2)", 0x15d977, 568, 310, 30145, 735, 24, 112, 129, 56, 25, 16, False),
+ "Ghost of Starman (2)": EarthBoundEnemy("Ghost of Starman (2)", 0x15d9d5, 750, 462, 48695, 807, 46, 152, 170, 68, 43, 16, False),
+ "Starman Deluxe (2)": EarthBoundEnemy("Starman Deluxe (2)", 0x15da33, 1400, 418, 160524, 3827, 27, 143, 186, 65, 43, 21, False),
+ "Final Starman (2)": EarthBoundEnemy("Final Starman (2)", 0x15da91, 840, 860, 61929, 915, 47, 178, 187, 71, 25, 24, False),
+ "Diamond Dog (2)": EarthBoundEnemy("Diamond Dog (2)", 0x15db4d, 3344, 154, 337738, 6968, 31, 167, 230, 70, 15, 24, False),
+ "Trillionage Sprout (2)": EarthBoundEnemy("Trillionage Sprout (2)", 0x15dbab, 1048, 240, 30303, 1358, 16, 54, 88, 29, 21, 71, False),
+ "Master Belch (2)": EarthBoundEnemy("Master Belch (2)", 0x15dc09, 650, 0, 12509, 664, 16, 50, 88, 27, 20, 61, False),
+ "Master Barf (2)": EarthBoundEnemy("Master Barf (2)", 0x15dcc5, 1319, 0, 125056, 3536, 24, 136, 177, 60, 39, 64, False),
+ "Loaded Dice 2": EarthBoundEnemy("Loaded Dice 2", 0x15dd23, 307, 0, 10672, 703, 77, 146, 113, 59, 75, 6, False),
+ "Boogey Tent (2)": EarthBoundEnemy("Boogey Tent (2)", 0x15dddf, 579, 56, 5500, 407, 10, 43, 69, 25, 16, 32, False),
+ "Everdred (2)": EarthBoundEnemy("Everdred (2)", 0x15de9b, 182, 0, 986, 171, 6, 25, 35, 15, 10, 40, False),
+ "Electro Specter (2)": EarthBoundEnemy("Electro Specter (2)", 0x15def9, 3092, 80, 261637, 6564, 29, 148, 203, 67, 47, 56, False),
+ "Thunder and Storm (2)": EarthBoundEnemy("Thunder and Storm (2)", 0x15df57, 2065, 70, 129026, 4736, 21, 111, 178, 56, 35, 55, False),
+ "Frankystein Mark II (2)": EarthBoundEnemy("Frankystein Mark II (2)", 0x15dfb5, 91, 0, 76, 31, 4, 15, 18, 7, 0, 40, False),
+ "Evil Mani-Mani (2)": EarthBoundEnemy("Evil Mani-Mani (2)", 0x15e013, 860, 88, 28139, 1852, 15, 86, 145, 45, 1, 80, False),
+ # "Heavily Armed Pokey": EarthBoundEnemy("Heavily Armed Pokey", 0x15e071, 1746, 999, 0, 0, 51, 150, 274, 72, False),
+ "Clumsy Robot (2)": EarthBoundEnemy("Clumsy Robot (2)", 0x15e0cf, 962, 0, 32378, 2081, 83, 88, 137, 46, 30, 49, False),
+ "Robo-pump (2)": EarthBoundEnemy("Robo-pump (2)", 0x15e12d, 431, 0, 4797, 349, 19, 70, 113, 36, 5, 4, False),
+ "Guardian General (2)": EarthBoundEnemy("Guardian General (2)", 0x15e1e9, 831, 6, 95390, 3235, 21, 109, 214, 55, 1, 7, False),
+ "Black Antoid (2)": EarthBoundEnemy("Black Antoid (2)", 0x15e247, 34, 25, 37, 7, 4, 14, 13, 7, 3, 0, False), # Separate enemy used in the titanic ant fight
+ "Runaway Dog (2)": EarthBoundEnemy("Runaway Dog", 0x15e303, 21, 0, 4, 3, 26, 4, 5, 73, 0, 1, False),
+ # "Cave Boy (2)": EarthBoundEnemy("Cave Boy (2)", 0x15e361, 314, 0, 618, 17, 5, 21, 33, 11, False),
+ "Tiny Li'l Ghost": EarthBoundEnemy("Tiny Li'l Ghost", 0x15e3bf, 90, 0, 1, 162, 100, 19, 7, 18, 25, 24, False),
+ "Starman Junior": EarthBoundEnemy("Starman Junior", 0x15e41d, 200, 999, 16, 20, 1, 11, 10, 6, 0, 80, False),
+ "Buzz Buzz": EarthBoundEnemy("Buzz Buzz", 0x15e47b, 2000, 999, 0, 0, 100, 40, 92, 20, 1, 80, False),
+ "Heavily Armed Pokey": EarthBoundEnemy("Heavily Armed Pokey", 0x15e4d9, 2000, 999, 70000, 0, 60, 145, 255, 80, 5, 255, False),
+ # "Heavily Armed Pokey": EarthBoundEnemy("Heavily Armed Pokey", 0x15e537, 1746, 999, 0, 0, 51, 150, 274, 72, False), Cutscene?
+ "Giygas (2)": EarthBoundEnemy("Giygas (2)", 0x15e595, 9999, 999, 70000, 0, 80, 255, 255, 80, 5, 255, False),
+ "Giygas (3)": EarthBoundEnemy("Giygas (3)", 0x15e5f3, 9999, 0, 0, 0, 80, 255, 255, 80, 5, 255, False),
+ "Giygas": EarthBoundEnemy("Giygas", 0x15e651, 2000, 0, 0, 0, 80, 255, 255, 80, 5, 255, False),
+ "Giygas (5)": EarthBoundEnemy("Giygas (5)", 0x15e6af, 9999, 0, 0, 0, 80, 255, 255, 80, 5, 255, False),
+ "Farm Zombie": EarthBoundEnemy("Farm Zombie", 0x15e70d, 171, 0, 700, 58, 10, 31, 24, 19, 15, 24, False),
+ "Criminal Caterpillar": EarthBoundEnemy("Criminal Caterpillar", 0x15e76b, 250, 168, 30384, 0, 134, 37, 16, 23, 0, 0, False),
+ "Evil Eye": EarthBoundEnemy("Evil Eye", 0x15e7c9, 720, 400, 46376, 896, 38, 141, 162, 63, 25, 16, False),
+ "Mini Barf": EarthBoundEnemy("Mini Barf", 0x15e885, 616, 0, 7521, 460, 10, 45, 71, 26, 19, 30, False),
+ "Master Criminal Worm": EarthBoundEnemy("Master Criminal Worm", 0x15e8e3, 377, 300, 82570, 0, 136, 73, 40, 37, 0, 0, False),
+ "Captain Strong": EarthBoundEnemy("Captain Strong", 0x15e941, 140, 0, 492, 159, 15, 20, 24, 13, 8, 18, False),
+ "Giygas (6)": EarthBoundEnemy("Giygas (6)", 0x15e99f, 9999, 0, 0, 0, 80, 255, 127, 80, 5, 255, False),
+ "Clumsy Robot (3)": EarthBoundEnemy("Clumsy Robot (3)", 0x15e9fd, 962, 0, 32378, 2081, 83, 88, 137, 46, 30, 49, False)
+ }
+
+ shuffled_enemies = {}
+ for enemy in world.acting_enemy_list:
+ shuffled_enemies[enemy] = world.enemies[world.acting_enemy_list[enemy]]
+
+ flunkies = {
+ "Titanic Ant": shuffled_enemies["Black Antoid (2)"],
+ "Master Belch": shuffled_enemies["Slimy Little Pile"],
+ "Trillionage Sprout": shuffled_enemies["Tough Mobile Sprout"],
+ "Master Barf": shuffled_enemies["Even Slimier Little Pile"],
+ "Starman Deluxe": [shuffled_enemies["Starman"], shuffled_enemies["Starman Super"]],
+ "Carbon Dog": world.enemies[world.boss_list[27]], # This should be the enemy that gets shuffled WITH carbon dog, right? Fix???
+ "Skate Punk": [shuffled_enemies["Pogo Punk"], shuffled_enemies["Yes Man Junior"]],
+ "Loaded Dice": [shuffled_enemies["Care Free Bomb"], shuffled_enemies["Beautiful UFO"], shuffled_enemies["High-class UFO"]],
+ "Loaded Dice 2": [shuffled_enemies["Electro Swoosh"], shuffled_enemies["Fobby"], shuffled_enemies["Uncontrollable Sphere"]],
+ "Starman Super": [shuffled_enemies["Starman"]]
+ }
+
+ world.regional_enemies = {"Northern Onett": {shuffled_enemies["Spiteful Crow"], shuffled_enemies["Runaway Dog"], shuffled_enemies["Coil Snake"]},
+ "Onett": {shuffled_enemies["Pogo Punk"], shuffled_enemies["Skate Punk"], shuffled_enemies["Yes Man Junior"], world.enemies[world.boss_list[0]], world.enemies[world.boss_list[1]]},
+ "Arcade": {shuffled_enemies["Pogo Punk"], shuffled_enemies["Skate Punk"], shuffled_enemies["Yes Man Junior"]},
+ "Giant Step": {shuffled_enemies["Attack Slug"], shuffled_enemies["Black Antoid"], shuffled_enemies["Rowdy Mouse"], world.enemies[world.boss_list[2]]},
+ "Twoson": {shuffled_enemies["Black Antoid"], shuffled_enemies["Cop"], world.enemies[world.boss_list[3]], shuffled_enemies["Ramblin' Evil Mushroom"],
+ shuffled_enemies["Annoying Old Party Man"], shuffled_enemies["Cranky Lady"], shuffled_enemies["Mobile Sprout"], shuffled_enemies["New Age Retro Hippie"], shuffled_enemies["Unassuming Local Guy"],
+ shuffled_enemies["Runaway Dog"]},
+ "Everdred's House": {world.enemies[world.boss_list[4]]},
+ "Peaceful Rest Valley": {shuffled_enemies["Li'l UFO"], shuffled_enemies["Mobile Sprout"], shuffled_enemies["Spinning Robo"], shuffled_enemies["Territorial Oak"]},
+ "Happy-Happy Village": {shuffled_enemies["Coil Snake"], shuffled_enemies["Insane Cultist"], shuffled_enemies["Spiteful Crow"], shuffled_enemies["Unassuming Local Guy"]},
+ "Happy-Happy HQ": {shuffled_enemies["Insane Cultist"], world.enemies[world.boss_list[5]]},
+ "Lilliput Steps": {shuffled_enemies["Mighty Bear"], shuffled_enemies["Mole Playing Rough"], shuffled_enemies["Mr. Batty"], world.enemies[world.boss_list[6]]},
+ "Threed": {shuffled_enemies["Coil Snake"], shuffled_enemies["Handsome Tom"], shuffled_enemies["Smilin' Sam"], shuffled_enemies["Trick or Trick Kid"],
+ shuffled_enemies["Zombie Dog"], shuffled_enemies["Putrid Moldyman"], shuffled_enemies["Smelly Ghost"]},
+ "Threed Underground": {shuffled_enemies["No Good Fly"], shuffled_enemies["Urban Zombie"], shuffled_enemies["Zombie Possessor"], world.enemies[world.boss_list[8]], shuffled_enemies["Zombie Dog"]},
+ "Grapefruit Falls": {shuffled_enemies["Armored Frog"], shuffled_enemies["Black Antoid"], shuffled_enemies["Coil Snake"], shuffled_enemies["Farm Zombie"],
+ shuffled_enemies["Plain Crocodile"], shuffled_enemies["Red Antoid"], shuffled_enemies["Violent Roach"], shuffled_enemies["Mad Duck"], shuffled_enemies["Black Antoid (2)"]},
+ "Belch's Factory": {shuffled_enemies["Farm Zombie"], shuffled_enemies["Foppy"], shuffled_enemies["Mostly Bad Fly"], shuffled_enemies["Slimy Little Pile"], world.enemies[world.boss_list[9]]},
+ "Milky Well": {shuffled_enemies["Mad Duck"], shuffled_enemies["Ranboob"], shuffled_enemies["Struttin' Evil Mushroom"], shuffled_enemies["Tough Mobile Sprout"], world.enemies[world.boss_list[10]]},
+ "Dusty Dunes Desert": {shuffled_enemies["Bad Buffalo"], shuffled_enemies["Crested Booka"], shuffled_enemies["Criminal Caterpillar"], shuffled_enemies["Cute Li'l UFO"], shuffled_enemies["Desert Wolf"], shuffled_enemies["Mole Playing Rough"],
+ shuffled_enemies["Skelpion"], shuffled_enemies["Smilin' Sphere"]},
+ "Fourside": {shuffled_enemies["Annoying Reveler"], shuffled_enemies["Crazed Sign"], shuffled_enemies["Extra Cranky Lady"], shuffled_enemies["Mad Taxi"]},
+ "Moonside": {shuffled_enemies["Abstract Art"], shuffled_enemies["Dali's Clock"], shuffled_enemies["Enraged Fire Plug"], shuffled_enemies["Robo-pump"], world.enemies[world.boss_list[13]]},
+ "Gold Mine": {shuffled_enemies["Gigantic Ant"], shuffled_enemies["Mad Duck"], shuffled_enemies["Noose Man"], shuffled_enemies["Thirsty Coil Snake"], world.enemies[world.boss_list[11]]},
+ "Fourside Dept. Store": {shuffled_enemies["Musica"], shuffled_enemies["Mystical Record"], shuffled_enemies["Scalding Coffee Cup"], world.enemies[world.boss_list[12]]},
+ "Monkey Caves": {shuffled_enemies["Struttin' Evil Mushroom"], shuffled_enemies["Tough Mobile Sprout"]},
+ "Monotoli Building": {shuffled_enemies["Sentry Robot"], world.enemies[world.boss_list[14]]},
+ "Rainy Circle": {shuffled_enemies["Arachnid!"], shuffled_enemies["Elder Batty"], shuffled_enemies["Strong Crocodile"], world.enemies[world.boss_list[15]]},
+ "Andonuts Lab Area": {shuffled_enemies["Cave Boy"], shuffled_enemies["Mighty Bear Seven"]},
+ "Summers": {shuffled_enemies["Crazed Sign"], shuffled_enemies["Mad Taxi"], shuffled_enemies["Mole Playing Rough"], shuffled_enemies["Over Zealous Cop"], shuffled_enemies["Tough Guy"], world.enemies[world.boss_list[18]]},
+ "Summers Museum": {shuffled_enemies["Shattered Man"]},
+ "Magnet Hill": {shuffled_enemies["Deadly Mouse"], shuffled_enemies["Filthy Attack Roach"], shuffled_enemies["Stinky Ghost"], world.enemies[world.boss_list[16]]},
+ "Pink Cloud": {shuffled_enemies["Conducting Menace"], shuffled_enemies["Kiss of Death"], shuffled_enemies["Tangoo"], shuffled_enemies["Thunder Mite"], world.enemies[world.boss_list[17]]},
+ "Scaraba": {shuffled_enemies["Beautiful UFO"], shuffled_enemies["Dread Skelpion"], shuffled_enemies["Great Crested Booka"], shuffled_enemies["High-class UFO"], shuffled_enemies["Master Criminal Worm"]},
+ "Pyramid": {shuffled_enemies["Arachnid!!!"], shuffled_enemies["Fierce Shattered Man"], shuffled_enemies["Guardian Hieroglyph"], shuffled_enemies["Lethal Asp Hieroglyph"], shuffled_enemies["Petrified Royal Guard"],
+ world.enemies[world.boss_list[19]]},
+ "Southern Scaraba": {shuffled_enemies["Beautiful UFO"], shuffled_enemies["High-class UFO"], shuffled_enemies["Marauder Octobot"]},
+ "Dungeon Man": {shuffled_enemies["Dali's Clock"], shuffled_enemies["Mystical Record"], shuffled_enemies["Lesser Mook"], shuffled_enemies["Mystical Record"], shuffled_enemies["Scalding Coffee Cup"], shuffled_enemies["Worthless Protoplasm"], shuffled_enemies["Cute Li'l UFO"]},
+ "Deep Darkness": {shuffled_enemies["Mole Playing Rough"]},
+ "Winters": {shuffled_enemies["Lesser Mook"], shuffled_enemies["Whirling Robo"], shuffled_enemies["Wooly Shambler"]},
+ "Deep Darkness Darkness": {shuffled_enemies["Big Pile of Puke"], shuffled_enemies["Demonic Petunia"], shuffled_enemies["Even Slimier Little Pile"], shuffled_enemies["Hard Crocodile"], shuffled_enemies["Hostile Elder Oak"],
+ shuffled_enemies["Manly Fish"], shuffled_enemies["Manly Fish's Brother"], shuffled_enemies["Pit Bull Slug"], shuffled_enemies["Zap Eel"], world.enemies[world.boss_list[20]]},
+ "Boogey Tent": {world.enemies[world.boss_list[7]]},
+ "Southern Winters": {shuffled_enemies["Lesser Mook"], shuffled_enemies["Whirling Robo"], shuffled_enemies["Wooly Shambler"]},
+ "Brickroad Maze": {shuffled_enemies["Rowdy Mouse"], shuffled_enemies["Worthless Protoplasm"], shuffled_enemies["Mad Duck"]},
+ "Stonehenge Base": {shuffled_enemies["Atomic Power Robot"], shuffled_enemies["Military Octobot"], shuffled_enemies["Mook Senior"], shuffled_enemies["Starman"], shuffled_enemies["Starman Super"], world.enemies[world.boss_list[21]]},
+ "Lumine Hall": {shuffled_enemies["Conducting Spirit"], shuffled_enemies["Fobby"], shuffled_enemies["Hyper Spinning Robo"], shuffled_enemies["Uncontrollable Sphere"], world.enemies[world.boss_list[22]]},
+ "Lost Underworld": {shuffled_enemies["Chomposaur"], shuffled_enemies["Ego Orb"], shuffled_enemies["Wetnosaur"]},
+ "Fire Spring": {shuffled_enemies["Evil Elemental"], shuffled_enemies["Major Psychic Psycho"], shuffled_enemies["Psychic Psycho"], shuffled_enemies["Soul Consuming Flame"], world.enemies[world.boss_list[23]]},
+ "Magicant": {shuffled_enemies["Care Free Bomb"], shuffled_enemies["Electro Swoosh"], shuffled_enemies["French Kiss of Death"], shuffled_enemies["Loaded Dice"], shuffled_enemies["Mr. Molecule"], shuffled_enemies["Loaded Dice 2"]},
+ "Sea of Eden": {world.enemies[world.boss_list[18]], world.enemies[world.boss_list[24]]},
+ "Cave of the Past": {shuffled_enemies["Bionic Kraken"], shuffled_enemies["Final Starman"], shuffled_enemies["Ghost of Starman"], shuffled_enemies["Nuclear Reactor Robot"], shuffled_enemies["Squatter Demon"],
+ shuffled_enemies["Ultimate Octobot"], shuffled_enemies["Wild 'n Wooly Shambler"]},
+ "Endgame": {world.enemies[world.boss_list[25]], world.enemies["Giygas (2)"], world.enemies[world.boss_list[28]], world.enemies["Giygas (3)"], world.enemies["Giygas (5)"], world.enemies["Giygas (6)"]},
+
+ }
+
+ if world.options.randomize_enemy_attacks:
+ del flunkies["Loaded Dice 2"]
+ del flunkies["Skate Punk"]
+ del flunkies["Loaded Dice"]
+ del flunkies["Starman Super"]
+
+ for region in world.regional_enemies:
+ enemy_list = world.regional_enemies[region]
+ updated_list = set(enemy_list)
+ for enemy in enemy_list:
+ if enemy.name == "Carbon Dog":
+ updated_list.add(world.enemies[world.boss_list[27]])
+ for i in range(1, world.enemies[world.boss_list[27]].attack_extensions):
+ updated_list.add(world.enemies[f"{world.enemies[world.boss_list[27]].name} ({i + 1})"])
+ # todo; option to not have in Giygas/Mine
+
+ if enemy.name in flunkies:
+ # Can I just always update instead of doing an add? Would probably need to make singulars a list of one item...
+ if enemy.name in ["Starman Deluxe", "Skate Punk", "Loaded Dice", "Loaded Dice 2", "Starman Super"]:
+ updated_list.update(flunkies[enemy.name])
+ else:
+ updated_list.add(flunkies[enemy.name])
+
+ if enemy.attack_extensions > 1:
+ for i in range(1, enemy.attack_extensions):
+ updated_list.add(world.enemies[f"{enemy.name} ({i + 1})"])
+ world.regional_enemies[region] = updated_list
+
+
+combat_regions = [
+ "Northern Onett",
+ "Onett",
+ "Arcade",
+ "Giant Step",
+ "Twoson",
+ "Happy-Happy Village",
+ "Happy-Happy HQ"
+ "Lilliput Steps",
+ "Winters",
+ "Threed",
+ "Milky Well",
+ "Dusty Dunes Desert",
+ "Fourside",
+ "Moonside",
+ "Gold Mine",
+ "Monkey Caves",
+ "Monotoli Building",
+ "Rainy Circle",
+ "Summers",
+ "Magnet Hill",
+ "Pink Cloud",
+ "Scaraba",
+ "Pyramid",
+ "Southern Scaraba",
+ "Dungeon Man",
+ "Deep Darkness",
+ "Deep Darkness Darkness",
+ "Stonehenge Base",
+ "Lumine Hall",
+ "Lost Underworld",
+ "Fire Spring",
+ "Magicant",
+ "Cave of the Past",
+ "Endgame",
+ "Grapefruit Falls",
+ "Peaceful Rest Valley",
+ "Everdred's House",
+ "Belch's Factory",
+ "Southern Winters",
+ "Brickroad Maze"
+ "Summers Museum",
+ "Fourside Dept. Store",
+ "Threed Underground",
+ "Boogey Tent"
+]
+
+
+levels = [
+ 1, # north onett
+ 2, # south onett
+ 3, # giant step
+ 5, # twoson
+ 7, # everdred
+ 9, # peaceful rest
+ 10, # happy happy
+ 12, # lilliput steps
+ 13, # threed
+ 14, # threed caverns
+ 15, # grapefruit falls
+ 17, # belch base
+ 18, # milky well
+ 19, # duty dunes
+ 21, # fourside
+ 23, # gold mine
+ 24, # dept store
+ 25, # monkey cabves
+ 26, # monotoli building
+ 28, # winters
+ 29, # southern winters
+ 31, # rainy circle
+ 32, # summers
+ 33, # museum
+ 35, # Moonside
+ 36, # magnet hill
+ 38, # pink cloud
+ 39, # scaraba
+ 42, # pyramid
+ 43, # scaraba south
+ 45, # dungeon man
+ 47, # deep darkness
+ 49, # deep darkness swamp
+ 51, # Happy-Happy HQ
+ 52, # stonehenge
+ 54, # Arcade
+ 56, # lumine hall
+ 59, # lost underworld
+ 61, # fire spring
+ 63, # magicant
+ 65, # cave of the past
+ 68, # Sea of Eden
+ 70,
+ 73,
+ 74,
+ 75] # gigyas
+
+spell_breaks: dict[str, dict[int, str]] = {
+ "freeze": {8: "zeta", 12: "epsilon", 20: "delta", 25: "lambda", 40: "alpha", 65: "beta", 70: "gamma", 100: "omega"},
+ "fire": {5: "zeta", 10: "epsilon", 20: "alpha", 50: "beta", 70: "gamma", 100: "omega"}, # zeta needs to do less damage
+ "lifeup": {20: "alpha", 100: "beta"},
+ "thunder": {5: "zeta", 10: "epsilon", 15: "delta", 20: "lambda", 35: "alpha", 45: "beta", 60: "gamma", 100: "omega"},
+ "flash": {25: "alpha", 60: "beta", 70: "gamma", 100: "omega"},
+ "special": {5: "zeta", 10: "epsilon", 30: "alpha", 65: "beta", 80: "gamma", 100: "omega"},
+ "healing": {20: "alpha", 40: "beta", 60: "gamma", 100: "omega"},
+ "starstorm": {5: "zeta", 12: "epsilon", 20: "delta", 45: "lambda", 70: "alpha", 100: "beta"},
+ "diamond_eyes": {35: "alpha", 40: "beta", 100: "gamma"},
+ "nauseous_breath": {25: "alpha", 100: "beta"},
+ "poison_stinger": {25: "alpha", 100: "beta"},
+ "kiss_of_death": {25: "alpha", 100: "beta"},
+ "summon_storm": {25: "alpha", 45: "beta", 60: "gamma", 100: "omega"},
+ "scalding_espresso": {5: "zeta", 10: "epsilon", 20: "alpha", 30: "beta", 40: "gamma", 100: "omega"},
+ "extinguishing_blast": {5: "zeta", 10: "epsilon", 20: "alpha", 30: "beta", 40: "gamma", 100: "omega"},
+ "spray_fire": {5: "zeta", 10: "epsilon", 20: "alpha", 30: "beta", 40: "gamma", 100: "omega"},
+ "breathe_fire": {5: "zeta", 10: "epsilon", 20: "alpha", 30: "beta", 40: "gamma", 100: "omega"},
+ "poisonous_fangs": {25: "alpha", 100: "beta"},
+ "flaming_fireball": {5: "zeta", 10: "epsilon", 20: "alpha", 30: "beta", 40: "gamma", 100: "omega"},
+ "glorious_light": {25: "alpha", 45: "beta", 60: "gamma", 100: "omega"},
+ "poison_flute": {25: "alpha", 100: "beta"},
+ "diamond_bite": {35: "alpha", 40: "beta", 100: "gamma"},
+ "scatter_spores": {25: "alpha", 100: "beta"},
+ "hacking_cough": {25: "alpha", 100: "beta"},
+ "stuffiness_beam": {25: "alpha", 100: "beta"},
+ "crashing_boom_bang": {5: "zeta", 10: "epsilon", 15: "delta", 20: "lambda", 35: "alpha", 45: "beta", 60: "gamma", 100: "omega"},
+ "paralysis": {30: "lambda", 60: "alpha", 100: "omega"},
+ "electrical_shock": {5: "zeta", 10: "epsilon", 15: "delta", 20: "lambda", 35: "alpha", 45: "beta", 60: "gamma", 100: "omega"},
+ "giygas_phase2_thunder": {5: "zeta", 10: "epsilon", 15: "delta", 20: "lambda", 35: "alpha", 100: "beta"},
+ "giygas_phase3_thunder": {5: "zeta", 10: "epsilon", 15: "delta", 20: "lambda", 35: "alpha", 100: "beta"},
+ "giygas_phase4_thunder": {5: "zeta", 10: "epsilon", 15: "delta", 20: "lambda", 35: "alpha", 100: "beta"},
+ "giygas_phase2_freeze": {8: "zeta", 12: "epsilon", 20: "delta", 25: "lambda", 100: "alpha"},
+ "giygas_phase3_freeze": {8: "zeta", 12: "epsilon", 20: "delta", 25: "lambda", 100: "alpha"},
+ "giygas_phase4_freeze": {8: "zeta", 12: "epsilon", 20: "delta", 25: "lambda", 100: "alpha"},
+ "giygas_phase2_flash": {25: "alpha", 60: "beta", 100: "gamma"},
+ "giygas_phase3_flash": {25: "alpha", 60: "beta", 100: "gamma"},
+ "giygas_phase4_flash": {25: "alpha", 60: "beta", 100: "gamma"},
+ "thunder_minus": {10: "zeta", 15: "epsilon", 20: "delta", 35: "lambda", 45: "alpha", 60: "beta", 100: "gamma", 200: "omega"},
+ "starstorm_minus": {12: "zeta", 20: "epsilon", 45: "delta", 70: "lambda", 100: "alpha", 200: "beta"},
+ "flash_minus": {60: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "blast": {10: "zeta", 20: "epsilon", 30: "alpha", 40: "beta", 50: "gamma", 100: "omega"},
+ "missile": {5: "zeta", 12: "epsilon", 20: "alpha", 50: "beta", 73: "gamma", 100: "omega"},
+ "throw_bomb": {10: "zeta", 20: "epsilon", 30: "alpha", 40: "beta", 50: "gamma", 100: "omega"},
+ "throw_bomb_minus": {20: "zeta", 30: "epsilon", 40: "alpha", 50: "beta", 100: "gamma", 200: "omega"},
+ "shoot_rocket": {5: "zeta", 12: "epsilon", 20: "alpha", 50: "beta", 73: "gamma", 100: "omega"},
+ "paralyzing_pollen": {30: "lambda", 60: "alpha", 100: "omega"},
+
+ "electrical_shock_minus": {10: "zeta", 15: "epsilon", 20: "delta", 35: "lambda", 45: "alpha", 60: "beta", 100: "gamma", 200: "omega"},
+ "crashing_boom_bang_minus": {10: "zeta", 15: "epsilon", 20: "delta", 35: "lambda", 45: "alpha", 60: "beta", 100: "gamma", 200: "omega"},
+ "giygas_phase2_thunder_minus": {10: "zeta", 15: "epsilon", 20: "delta", 35: "lambda", 100: "alpha", 200: "beta"},
+ "giygas_phase3_thunder_minus": {10: "zeta", 15: "epsilon", 20: "delta", 35: "lambda", 100: "alpha", 200: "beta"},
+ "giygas_phase4_thunder_minus": {10: "zeta", 15: "epsilon", 20: "delta", 35: "lambda", 100: "alpha", 200: "beta"},
+ "thunder_minus_minus": {15: "zeta", 20: "epsilon", 35: "delta", 45: "lambda", 60: "alpha", 100: "beta", 200: "gamma", 300: "omega"},
+
+ "starstorm_minus_minus": {20: "zeta", 45: "epsilon", 70: "delta", 100: "lambda", 200: "alpha", 300: "beta"},
+
+ "giygas_phase2_flash_minus": {60: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "giygas_phase3_flash_minus": {60: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "giygas_phase4_flash_minus": {60: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "summon_storm_minus": {60: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "glorious_light_minus": {60: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "flash_minus_minus": {70: "alpha", 100: "beta", 200: "gamma", 300: "omega"},
+
+ "fire_minus": {10: "zeta", 20: "epsilon", 50: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "spray_fire_minus": {10: "zeta", 20: "epsilon", 50: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "breathe_fire_minus": {10: "zeta", 20: "epsilon", 50: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "scalding_espresso_minus": {10: "zeta", 20: "epsilon", 50: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "flaming_fireball_minus": {10: "zeta", 20: "epsilon", 50: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "extinguishing_blast_minus": {10: "zeta", 20: "epsilon", 50: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+
+ "throw_bomb_minus_minus": {30: "zeta", 40: "epsilon", 50: "alpha", 100: "beta", 200: "gamma", 300: "omega"},
+
+ "shoot_rocket_minus": {12: "zeta", 20: "epsilon", 50: "alpha", 73: "beta", 100: "gamma", 200: "omega"},
+
+ "freeze_minus": {12: "zeta", 20: "epsilon", 25: "delta", 40: "lambda", 65: "alpha", 70: "beta", 100: "gamma", 200: "omega"},
+ "giygas_phase2_freeze_minus": {12: "zeta", 20: "epsilon", 25: "delta", 100: "lambda", 200: "alpha"},
+ "giygas_phase3_freeze_minus": {12: "zeta", 20: "epsilon", 25: "delta", 100: "lambda", 200: "alpha"},
+ "giygas_phase4_freeze_minus": {12: "zeta", 20: "epsilon", 25: "delta", 100: "lambda", 200: "alpha"},
+
+ "special_minus": {10: "zeta", 30: "epsilon", 65: "alpha", 80: "beta", 100: "gamma", 200: "omega"},
+ "blast_minus": {20: "zeta", 30: "epsilon", 40: "alpha", 50: "beta", 100: "gamma", 200: "omega"},
+
+}
+
+
+def get_psi_levels(level: int, breaks: dict[str, dict[int, str]]):
+ for top_level, psi_val in breaks.items():
+ if level <= top_level:
+ return psi_val
+ return breaks[max(breaks)]
+
+
+spell_elements = {
+ "thunder": "thunder",
+ "giygas_phase2_thunder": "thunder",
+ "giygas_phase3_thunder": "thunder",
+ "giygas_phase4_thunder": "thunder",
+ "crashing_boom_bang": "thunder",
+ "electrical_shock": "thunder",
+ "thunder_minus": "thunder",
+
+ "freeze": "freeze",
+ "giygas_phase2_freeze": "freeze",
+ "giygas_phase3_freeze": "freeze",
+ "giygas_phase4_freeze": "freeze",
+
+ "fire": "fire",
+ "scalding_espresson": "fire",
+ "extinguishing_blast": "fire",
+ "spray_fire": "fire",
+ "breathe_fire": "fire",
+ "flaming_fireball": "fire",
+
+ "flash": "flash",
+ "summon_storm": "flash",
+ "glorious_light": "flash",
+ "giygas_phase2_flash": "flash",
+ "giygas_phase3_flash": "flash",
+ "giygas_phase4_flash": "flash",
+ "flash_minus": "flash",
+
+ "starstorm": "starstorm",
+ "starstorm_minus": "starstorm",
+
+ "special": "special",
+
+ "blast": "explosive",
+ "throw_bomb": "explosive",
+ "throw_bomb_minus": "explosive"
+}
+
+spell_data = {
+ "freeze": {
+ "zeta": [0x62, 0x01, 0x37],
+ "epsilon": [0x63, 0x01, 0x38],
+ "delta": [0x64, 0x01, 0x39],
+ "lambda": [0x65, 0x01, 0x3A],
+ "alpha": [0x12, 0x00, 0x09],
+ "beta": [0x13, 0x00, 0x0A],
+ "gamma": [0x14, 0x00, 0x0B],
+ "omega": [0x15, 0x00, 0x0C]
+ },
+ "fire": {
+ "zeta": [0x60, 0x01, 0x35],
+ "epsilon": [0x61, 0x01, 0x36],
+ "alpha": [0x0E, 0x00, 0x05],
+ "beta": [0x0F, 0x00, 0x06],
+ "gamma": [0x10, 0x00, 0x07],
+ "omega": [0x11, 0x00, 0x08]
+ },
+ "lifeup": {
+ "alpha": [0x20, 0x00, 0x17],
+ "beta": [0x21, 0x00, 0x18]
+ },
+ "flash": {
+ "alpha": [0x1A, 0x00, 0x11],
+ "beta": [0x1B, 0x00, 0x12],
+ "gamma": [0x1C, 0x00, 0x13],
+ "omega": [0x1D, 0x00, 0x14]
+ },
+ "thunder": {
+ "zeta": [0x69, 0x01, 0x3E],
+ "epsilon": [0x6A, 0x01, 0x3F],
+ "delta": [0x6B, 0x01, 0x40],
+ "lambda": [0x6C, 0x01, 0x41],
+ "alpha": [0x16, 0x00, 0x0D],
+ "beta": [0x17, 0x00, 0x0E],
+ "gamma": [0x18, 0x00, 0x0F],
+ "omega": [0x19, 0x00, 0x10]
+ },
+ "special": {
+ "zeta": [0x66, 0x01, 0x3B],
+ "epsilon": [0x67, 0x01, 0x3C],
+ "alpha": [0x0A, 0x00, 0x01],
+ "beta": [0x0B, 0x00, 0x02],
+ "gamma": [0x0C, 0x00, 0x03],
+ "omega": [0x0D, 0x00, 0x04]
+ },
+ "healing": {
+ "alpha": [0x24, 0x00, 0x1B],
+ "beta": [0x25, 0x00, 0x1C],
+ "gamma": [0x26, 0x00, 0x1D],
+ "omega": [0x27, 0x00, 0x1E]
+ },
+ "starstorm": {
+ "zeta": [0x6D, 0x01, 0x42],
+ "epsilon": [0x6E, 0x01, 0x43],
+ "delta": [0x6F, 0x01, 0x44],
+ "lambda": [0x70, 0x01, 0x45],
+ "alpha": [0x1E, 0x00, 0x15],
+ "beta": [0x1F, 0x00, 0x16]
+ },
+ "scatter_spores": {
+ "alpha": [0x3F, 0x01, 0x00],
+ "beta": [0xED, 0x00, 0x00],
+ },
+ "nauseous_breath": {
+ "alpha": [0x4A, 0x00, 0x00],
+ "beta": [0x47, 0x00, 0x00]
+ },
+ "diamond_eyes": {
+ "alpha": [0x40, 0x01, 0x00],
+ "beta": [0x41, 0x01, 0x00],
+ "gamma": [0x44, 0x00, 0x00]
+ },
+ "glorious_light": {
+ "alpha": [0x42, 0x01, 0x00],
+ "beta": [0x43, 0x01, 0x00],
+ "gamma": [0xC9, 0x00, 0x00],
+ "omega": [0x44, 0x01, 0x00],
+ },
+ "flaming_fireball": {
+ "zeta": [0x73, 0x01, 0x00],
+ "epsilon": [0x74, 0x01, 0x00],
+ "alpha": [0x47, 0x01, 0x00],
+ "beta": [0x46, 0x01, 0x00],
+ "gamma": [0x45, 0x01, 0x00],
+ "omega": [0x68, 0x00, 0x00],
+ },
+ "breathe_fire": {
+ "zeta": [0x71, 0x01, 0x00],
+ "epsilon": [0x72, 0x01, 0x00],
+ "alpha": [0x48, 0x01, 0x00],
+ "beta": [0x49, 0x01, 0x00],
+ "gamma": [0x5E, 0x00, 0x00],
+ "omega": [0x4A, 0x01, 0x00],
+ },
+ "spray_fire": {
+ "zeta": [0x75, 0x01, 0x00],
+ "epsilon": [0x76, 0x01, 0x00],
+ "alpha": [0x4B, 0x01, 0x00],
+ "beta": [0x4C, 0x01, 0x00],
+ "gamma": [0x4D, 0x01, 0x00],
+ "omega": [0x5D, 0x00, 0x00],
+ },
+ "paralysis": {
+ "lambda": [0x68, 0x01, 0x3D],
+ "alpha": [0x38, 0x00, 0x2F],
+ "omega": [0x39, 0x00, 0x30]
+ },
+ "poisonous_fangs": {
+ "alpha": [0x4E, 0x01, 0x00],
+ "beta": [0x64, 0x00, 0x00]
+ },
+ "poison_stinger": {
+ "alpha": [0x4F, 0x01, 0x00],
+ "beta": [0x48, 0x00, 0x00]
+ },
+ "crashing_boom_bang": {
+ "zeta": [0x7D, 0x01, 0x00],
+ "epsilon": [0x7E, 0x01, 0x00],
+ "delta": [0x7F, 0x01, 0x00],
+ "lambda": [0x80, 0x01, 0x00],
+ "alpha": [0x50, 0x01, 0x00],
+ "beta": [0x5C, 0x00, 0x00],
+ "gamma": [0x51, 0x01, 0x00],
+ "omega": [0x52, 0x01, 0x00]
+ },
+
+ "electrical_shock": {
+ "zeta": [0x79, 0x01, 0x00],
+ "epsilon": [0x7A, 0x01, 0x00],
+ "delta": [0x7C, 0x01, 0x00],
+ "lambda": [0x7B, 0x01, 0x00],
+ "alpha": [0x53, 0x01, 0x00],
+ "beta": [0xCA, 0x00, 0x00],
+ "gamma": [0x54, 0x01, 0x00],
+ "omega": [0x55, 0x01, 0x00]
+ },
+
+ "scalding_espresso": {
+ "zeta": [0x77, 0x01, 0x00],
+ "epsilon": [0x78, 0x01, 0x00],
+ "alpha": [0x59, 0x00, 0x00],
+ "beta": [0x56, 0x01, 0x00],
+ "gamma": [0x57, 0x01, 0x00],
+ "omega": [0x58, 0x01, 0x00]
+ },
+ "extinguishing_blast": {
+ "zeta": [0x81, 0x01, 0x00],
+ "epsilon": [0x82, 0x01, 0x00],
+ "alpha": [0x59, 0x01, 0x00],
+ "beta": [0x5A, 0x01, 0x00],
+ "gamma": [0x5B, 0x00, 0x00],
+ "omega": [0x5B, 0x01, 0x00]
+ },
+
+ "diamond_bite": {
+ "alpha": [0x5C, 0x01, 0x00],
+ "beta": [0x5D, 0x01, 0x00],
+ "gamma": [0xE4, 0x00, 0x00]
+ },
+ "poison_flute": {
+ "alpha": [0x5E, 0x01, 0x00],
+ "beta": [0xCD, 0x00, 0x00]
+ },
+ "kiss_of_death": {
+ "alpha": [0x5F, 0x01, 0x00],
+ "beta": [0x49, 0x00, 0x00]
+ },
+ "stuffiness_beam": {
+ "alpha": [0xF1, 0x00, 0x00],
+ "beta": [0x45, 0x00, 0x00]
+ },
+ "hacking_cough": {
+ "alpha": [0xD5, 0x00, 0x00],
+ "beta": [0x57, 0x00, 0x00]
+ },
+ "giygas_phase2_thunder": {
+ "zeta": [0x87, 0x01, 0x00],
+ "epsilon": [0x86, 0x01, 0x00],
+ "delta": [0x85, 0x01, 0x00],
+ "lambda": [0x84, 0x01, 0x00],
+ "alpha": [0x83, 0x01, 0x00],
+ "beta": [0x12, 0x01, 0x00]
+ },
+
+ "giygas_phase3_thunder": {
+ "zeta": [0x8C, 0x01, 0x00],
+ "epsilon": [0x8B, 0x01, 0x00],
+ "delta": [0x8A, 0x01, 0x00],
+ "lambda": [0x89, 0x01, 0x00],
+ "alpha": [0x88, 0x01, 0x00],
+ "beta": [0x2E, 0x01, 0x00]
+ },
+
+ "giygas_phase4_thunder": {
+ "zeta": [0x91, 0x01, 0x00],
+ "epsilon": [0x90, 0x01, 0x00],
+ "delta": [0x8F, 0x01, 0x00],
+ "lambda": [0x8E, 0x01, 0x00],
+ "alpha": [0x8D, 0x01, 0x00],
+ "beta": [0x31, 0x01, 0x00]
+ },
+
+
+ "giygas_phase2_freeze": {
+ "zeta": [0x92, 0x01, 0x00],
+ "epsilon": [0x93, 0x01, 0x00],
+ "delta": [0x94, 0x01, 0x00],
+ "lambda": [0x95, 0x01, 0x00],
+ "alpha": [0x2C, 0x01, 0x00]
+ },
+
+ "giygas_phase3_freeze": {
+ "zeta": [0x96, 0x01, 0x00],
+ "epsilon": [0x97, 0x01, 0x00],
+ "delta": [0x98, 0x01, 0x00],
+ "lambda": [0x99, 0x01, 0x00],
+ "alpha": [0x2F, 0x01, 0x00]
+ },
+
+ "giygas_phase4_freeze": {
+ "zeta": [0x9A, 0x01, 0x00],
+ "epsilon": [0x9B, 0x01, 0x00],
+ "delta": [0x9C, 0x01, 0x00],
+ "lambda": [0x9D, 0x01, 0x00],
+ "alpha": [0x32, 0x01, 0x00]
+ },
+
+
+ "giygas_phase2_flash": {
+ "alpha": [0x9E, 0x01, 0x00],
+ "beta": [0x9F, 0x01, 0x00],
+ "gamma": [0x2D, 0x01, 0x00]
+ },
+
+ "giygas_phase3_flash": {
+ "alpha": [0xA0, 0x01, 0x00],
+ "beta": [0xA1, 0x01, 0x00],
+ "gamma": [0x30, 0x01, 0x00]
+ },
+
+ "giygas_phase4_flash": {
+ "alpha": [0xA2, 0x01, 0x00],
+ "beta": [0xA3, 0x01, 0x00],
+ "gamma": [0x33, 0x01, 0x00]
+ },
+
+ "explosion_damage": {
+ "alpha": [0xA7, 0x00, 0x00],
+ "beta": [0xA3, 0x01, 0x00],
+ "gamma": [0x33, 0x01, 0x00]
+ },
+ "thunder_minus": {
+ "zeta": [0x69, 0x01, 0x3E],
+ "epsilon": [0x6A, 0x01, 0x3F],
+ "delta": [0x6B, 0x01, 0x40],
+ "lambda": [0x6C, 0x01, 0x41],
+ "alpha": [0x16, 0x00, 0x0D],
+ "beta": [0x17, 0x00, 0x0E],
+ "gamma": [0x18, 0x00, 0x0F],
+ "omega": [0x19, 0x00, 0x10]
+ },
+
+ "flash_minus": {
+ "alpha": [0x1A, 0x00, 0x11],
+ "beta": [0x1B, 0x00, 0x12],
+ "gamma": [0x1C, 0x00, 0x13],
+ "omega": [0x1D, 0x00, 0x14]
+ },
+
+ "starstorm_minus": {
+ "zeta": [0x6D, 0x01, 0x42],
+ "epsilon": [0x6E, 0x01, 0x43],
+ "delta": [0x6F, 0x01, 0x44],
+ "lambda": [0x70, 0x01, 0x45],
+ "alpha": [0x1E, 0x00, 0x15],
+ "beta": [0x1F, 0x00, 0x16]
+ },
+ "blast": {
+ "zeta": [0xF7, 0x01, 0x58],
+ "epsilon": [0xF8, 0x01, 0x59],
+ "alpha": [0xA4, 0x01, 0x46],
+ "beta": [0xA5, 0x01, 0x47],
+ "gamma": [0xA6, 0x01, 0x48],
+ "omega": [0xA7, 0x01, 0x48]
+ },
+ "missile": {
+ "zeta": [0xF9, 0x01, 0x5A],
+ "epsilon": [0xFA, 0x01, 0x5B],
+ "alpha": [0xA8, 0x01, 0x4A],
+ "beta": [0xA9, 0x01, 0x4B],
+ "gamma": [0xAA, 0x01, 0x4C],
+ "omega": [0xAB, 0x01, 0x4D]
+ },
+ "summon_storm": {
+ "alpha": [0xF4, 0x01, 0x00],
+ "beta": [0xF5, 0x01, 0x00],
+ "gamma": [0x58, 0x00, 0x00],
+ "omega": [0xF6, 0x01, 0x00]
+ },
+ "throw_bomb": {
+ "zeta": [0xFC, 0x01, 0x00],
+ "epsilon": [0xFB, 0x01, 0x00],
+ "alpha": [0xFD, 0x01, 0x00],
+ "beta": [0xFE, 0x01, 0x00],
+ "gamma": [0xFF, 0x01, 0x00],
+ "omega": [0x00, 0x02, 0x00]
+ },
+
+ "throw_bomb_minus": {
+ "zeta": [0xFC, 0x01, 0x00],
+ "epsilon": [0xFB, 0x01, 0x00],
+ "alpha": [0xFD, 0x01, 0x00],
+ "beta": [0xFE, 0x01, 0x00],
+ "gamma": [0xFF, 0x01, 0x00],
+ "omega": [0x00, 0x02, 0x00]
+ },
+ "shoot_rocket": {
+ "zeta": [0x01, 0x02, 0x00],
+ "epsilon": [0x02, 0x02, 0x00],
+ "alpha": [0x03, 0x02, 0x00],
+ "beta": [0x04, 0x02, 0x00],
+ "gamma": [0x05, 0x02, 0x00],
+ "omega": [0x06, 0x02, 0x00]
+ },
+
+ "paralyzing_pollen": {
+ "lambda": [0x07, 0x02, 0x00],
+ "alpha": [0xCB, 0x00, 0x00],
+ "omega": [0x08, 0x02, 0x00]
+ },
+
+
+}
+
+shield_table = {
+ "disabled": 0x00,
+ "phys_1": 0x03,
+ "phys_2": 0x04,
+ "psi_1": 0x01,
+ "psi_2": 0x02
+}
+
+
+def assumed_player_speed_for_level(level: int) -> int:
+ return 2 + 58 * (level - 1) / 80
+
+
+def scale_enemy_speed(enemy: EarthBoundEnemy, new_level: int) -> int:
+ normal_dodge_chance = (2 * enemy.speed - assumed_player_speed_for_level(enemy.level)) / 500
+
+ enemy_scaled_speed = (normal_dodge_chance * 500 + assumed_player_speed_for_level(new_level)) / 2
+ return enemy_scaled_speed
+
+
+def scale_exp_2(base_exp: int, base_level: int, new_level: int, world: "EarthBoundWorld") -> int:
+ base_scaled_exp = calculate_exp(base_level)
+ scaled_exp = calculate_exp(new_level)
+ new_exp = base_exp * scaled_exp / base_scaled_exp
+ new_exp = max(new_exp, scaled_exp) # maybe remove? if early scaled
+ new_exp = math.ceil(new_exp * world.options.experience_modifier / 100)
+ return new_exp
+
+
+def calculate_exp(level: int) -> float:
+ if level > 30:
+ return 1000 * math.exp(0.05 * level)
+ else:
+ return 50 * math.exp(0.15 * level)
+ # return 10 * math.exp(0.2 * level) if not boosted
+
+
+def scale_shield(level: int, shield: str | None) -> str:
+ if shield is not None:
+ if level < 10:
+ enemy_shield = "disabled"
+ elif shield in ["phys_1", "phys_2"]:
+ if level < 25:
+ enemy_shield = "phys_1"
+ else:
+ enemy_shield = "phys_2"
+ elif shield in ["psi_1", "psi_2"]:
+ if level < 25:
+ enemy_shield = "psi_1"
+ else:
+ enemy_shield = "psi_2"
+ else:
+ warning(f"Could not scale shield, found initial value of {shield}, this is probably a typo")
+ enemy_shield = "disabled"
+ return enemy_shield
+
+
+guardian_text = [
+ 0xEEFAA0,
+ 0xEEFAA6,
+ 0xEEFAAD,
+ 0xEEFAB3,
+ 0xEEFABA,
+ 0xEEFAC0,
+ 0xEEFAC6,
+ 0xEEFACE
+]
+
+guardian_intro = {
+ "Giant Step": 0x066699,
+ "Lilliput Steps": 0x2F97CB,
+ "Milky Well": 0x2F67C3,
+ "Rainy Circle": 0x2EFAD6,
+ "Magnet Hill": 0x083D4D,
+ "Pink Cloud": 0x09D2E3,
+ "Lumine Hall": 0x09E2A4,
+ "Fire Spring": 0x2EFADF
+}
+
+
+def scale_enemies(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ additional_party_members = 0
+ if world.options.auto_scale_party_members:
+ if world.starting_character != "Ness":
+ rom.write_bytes(0x15F5FB, bytearray([max(levels[world.scaled_area_order.index(world.Ness_region)] + world.random.randint(-3, 3), 1)]))
+
+ if world.starting_character != "Paula":
+ rom.write_bytes(0x15F60F, bytearray([max(levels[world.scaled_area_order.index(world.Paula_region)] + world.random.randint(-3, 3), 1)])) # Paula starting level
+
+ if world.starting_character != "Jeff":
+ rom.write_bytes(0x15F623, bytearray([max(levels[world.scaled_area_order.index(world.Jeff_region)] + world.random.randint(-3, 3), 1)])) # Jeff starting level
+
+ if world.starting_character != "Poo":
+ rom.write_bytes(0x15F637, bytearray([max(levels[world.scaled_area_order.index(world.Poo_region)] + world.random.randint(-3, 3), 1)])) # Poo starting level
+
+ melody_number = 1
+ has_badge = False
+ c = Counter([world.Ness_region, world.Paula_region, world.Jeff_region, world.Poo_region])
+ # for region, level in zip(world.scaled_area_order, levels):
+ for region in world.scaled_area_order:
+ level = world.area_levels[region]
+ if world.options.easy_combat:
+ level = max(1, int(level / 2))
+ if region in ["Giant Step", "Lilliput Steps", "Milky Well",
+ "Rainy Circle", "Magnet Hill", "Pink Cloud",
+ "Lumine Hall", "Fire Spring"]:
+ rom.write_bytes(guardian_intro[region], struct.pack("I", guardian_text[melody_number - 1]))
+ melody_number += 1
+
+ if region == world.Badge_region:
+ has_badge = True
+
+ additional_party_members += c[region]
+ for enemy in world.regional_enemies[region]:
+ if enemy.is_scaled is False:
+ enemy_hp = int(enemy.hp * level / enemy.level)
+ if not world.options.easy_combat:
+ enemy_hp = int(enemy_hp + (enemy_hp * (0.10 * (additional_party_members - 1))))
+ enemy_pp = int(enemy.pp * level / enemy.level)
+ enemy_exp = int(scale_exp_2(enemy.exp, enemy.level, level, world))
+ enemy_money = min(65535, int((enemy.money * level / enemy.level) * world.options.money_drop_multiplier))
+ enemy_speed = max(2, int(scale_enemy_speed(enemy, level)))
+ enemy_offense = int(enemy.offense * level / enemy.level)
+ enemy_defense = int(enemy.defense * level / enemy.level)
+ enemy_level = int(enemy.level * level / enemy.level)
+ enemy_shield = scale_shield(level, enemy.shield)
+ rom.write_bytes(enemy.address + 33, enemy_hp.to_bytes(2, "little"))
+ rom.write_bytes(enemy.address + 35, enemy_pp.to_bytes(2, "little"))
+ rom.write_bytes(enemy.address + 37, enemy_exp.to_bytes(4, "little"))
+ rom.write_bytes(enemy.address + 41, enemy_money.to_bytes(2, "little"))
+ rom.write_bytes(enemy.address + 60, enemy_speed.to_bytes(1, "little"))
+ rom.write_bytes(enemy.address + 56, enemy_offense.to_bytes(2, "little"))
+ rom.write_bytes(enemy.address + 58, enemy_defense.to_bytes(2, "little"))
+ rom.write_bytes(enemy.address + 54, enemy_level.to_bytes(1, "little"))
+ if enemy.shield is not None:
+ rom.write_bytes(enemy.address + 89, shield_table[enemy_shield].to_bytes(1, "little"))
+
+ if enemy.name in world.enemy_psi:
+ for index, spell in [(i, s) for i, s in enumerate(world.enemy_psi[enemy.name]) if s != "null"]:
+ if spell == "special":
+ spell = world.offensive_psi_slots[0].lower()
+
+ if spell in spell_elements:
+ element = spell_elements[spell]
+ else:
+ element = "None"
+
+ if element == world.franklin_protection and not has_badge:
+ spell = f"{spell}_minus"
+ psi_level = get_psi_levels(level, spell_breaks[spell])
+ filtered_spell = spell.replace("_minus", "")
+ rom.write_bytes(enemy.address + 70 + (index * 2), bytearray(spell_data[filtered_spell][psi_level][0:2]))
+ rom.write_bytes(enemy.address + 80 + index, bytearray([spell_data[filtered_spell][psi_level][2]]))
+ if world.options.shuffle_enemy_drops:
+ rom.write_bytes(enemy.address + 88, bytearray([world.random.choice(world.filler_drops)]))
+ enemy.is_scaled = True
diff --git a/worlds/earthbound/modules/enemy_shuffler.py b/worlds/earthbound/modules/enemy_shuffler.py
new file mode 100644
index 000000000000..9751cb225c48
--- /dev/null
+++ b/worlds/earthbound/modules/enemy_shuffler.py
@@ -0,0 +1,1372 @@
+import struct
+from ..game_data.text_data import text_encoder
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from .. import EarthBoundWorld
+ from ..Rom import LocalRom
+
+enemy_ids = {
+ "Insane Cultist": 0x01,
+ "Armored Frog": 0x03,
+ "Bad Buffalo": 0x04,
+ "Black Antoid": 0x05,
+ "Red Antoid": 0x06,
+ "Ramblin' Evil Mushroom": 0x07,
+ "Struttin' Evil Mushroom": 0x08,
+ "Mobile Sprout": 0x09,
+ "Tough Mobile Sprout": 0x0a,
+ "Enraged Fire Plug": 0x0b,
+ "Mystical Record": 0x0c,
+ "Atomic Power Robot": 0x0d,
+ "Nuclear Reactor Robot": 0x0e,
+ "Guardian Hieroglyph": 0x0f,
+ "Lethal Asp Hieroglyph": 0x10,
+ "Electro Swoosh": 0x11,
+ "Conducting Menace": 0x12,
+ "Conducting Spirit": 0x13,
+ "Evil Elemental": 0x14,
+ "Annoying Old Party Man": 0x16,
+ "Annoying Reveler": 0x17,
+ "Unassuming Local Guy": 0x18,
+ "New Age Retro Hippie": 0x19,
+ "Mighty Bear": 0x1c,
+ "Mighty Bear Seven": 0x1d,
+ "Putrid Moldyman": 0x1e,
+ "Thunder Mite": 0x1f,
+ "Cranky Lady": 0x20,
+ "Extra Cranky Lady": 0x21,
+ "Wetnosaur": 0x23,
+ "Chomposaur": 0x24,
+ "Gigantic Ant": 0x26,
+ "Scalding Coffee Cup": 0x2b,
+ "Loaded Dice": 0x2c,
+ "Slimy Little Pile": 0x2d,
+ "Even Slimier Little Pile": 0x2e,
+ "Arachnid!": 0x2f,
+ "Arachnid!!!": 0x30,
+ "Kraken": 0x31,
+ "Bionic Kraken": 0x32,
+ "Spinning Robo": 0x33,
+ "Whirling Robo": 0x34,
+ "Hyper Spinning Robo": 0x35,
+ "Cop": 0x36,
+ "Coil Snake": 0x37,
+ "Thirsty Coil Snake": 0x38,
+ "Mr. Batty": 0x39,
+ "Elder Batty": 0x3a,
+ "Violent Roach": 0x3b,
+ "Filthy Attack Roach": 0x3c,
+ "Crazed Sign": 0x3d,
+ "Wooly Shambler": 0x3e,
+ "Wild 'n Wooly Shambler": 0x3f,
+ "Skate Punk": 0x40,
+ "Skelpion": 0x41,
+ "Dread Skelpion": 0x42,
+ "Starman": 0x43,
+ "Starman Super": 0x44,
+ "Ghost of Starman": 0x45,
+ "Smilin' Sphere": 0x46,
+ "Uncontrollable Sphere": 0x47,
+ "Petrified Royal Guard": 0x48,
+ "Final Starman": 0x4b,
+ "Urban Zombie": 0x4c,
+ "Zombie Possessor": 0x4d,
+ "Zombie Dog": 0x4e,
+ "Over Zealous Cop": 0x50,
+ "Territorial Oak": 0x51,
+ "Hostile Elder Oak": 0x52,
+ "Marauder Octobot": 0x54,
+ "Military Octobot": 0x55,
+ "Mechanical Octobot": 0x56,
+ "Ultimate Octobot": 0x57,
+ "Mad Duck": 0x58,
+ "Dali's Clock": 0x59,
+ "Musica": 0x5b,
+ "Desert Wolf": 0x5c,
+ "Big Pile of Puke": 0x5e,
+ "Kiss of Death": 0x60,
+ "French Kiss of Death": 0x61,
+ "Foppy": 0x62,
+ "Fobby": 0x63,
+ "Zap Eel": 0x64,
+ "Tangoo": 0x65,
+ "Squatter Demon": 0x67,
+ "Crested Booka": 0x68,
+ "Great Crested Booka": 0x69,
+ "Lesser Mook": 0x6a,
+ "Mook Senior": 0x6b,
+ "Smelly Ghost": 0x6c,
+ "Stinky Ghost": 0x6d,
+ "Everdred": 0x6e,
+ "Attack Slug": 0x6f,
+ "Pit Bull Slug": 0x70,
+ "Rowdy Mouse": 0x71,
+ "Deadly Mouse": 0x72,
+ "Care Free Bomb": 0x73,
+ "Handsome Tom": 0x75,
+ "Smilin' Sam": 0x76,
+ "Manly Fish": 0x77,
+ "Manly Fish's Brother": 0x78,
+ "Runaway Dog": 0x79,
+ "Trick or Trick Kid": 0x7a,
+ "Abstract Art": 0x7c,
+ "Shattered Man": 0x7d,
+ "Fierce Shattered Man": 0x7e,
+ "Ego Orb": 0x7f,
+ "Yes Man Junior": 0x81,
+ "Frank": 0x83,
+ "Cute Li'l UFO": 0x84,
+ "Beautiful UFO": 0x85,
+ "Pogo Punk": 0x86,
+ "Tough Guy": 0x87,
+ "Mad Taxi": 0x88,
+ "Mr. Molecule": 0x8a,
+ "Worthless Protoplasm": 0x8b,
+ "Sentry Robot": 0x8c,
+ "Psychic Psycho": 0x8e,
+ "Major Psychic Psycho": 0x8f,
+ "Mole Playing Rough": 0x90,
+ "Gruff Goat": 0x91,
+ "Clumsy Robot": 0x92,
+ "Soul Consuming Flame": 0x93,
+ "Demonic Petunia": 0x94,
+ "Ranboob": 0x95,
+ "Li'l UFO": 0x96,
+ "High-class UFO": 0x97,
+ "Noose Man": 0x98,
+ "Robo-pump": 0x99,
+ "Plain Crocodile": 0x9a,
+ "Strong Crocodile": 0x9b,
+ "Hard Crocodile": 0x9c,
+ "No Good Fly": 0x9d,
+ "Mostly Bad Fly": 0x9e,
+ "Spiteful Crow": 0x9f,
+ "Loaded Dice 2": 0xC3,
+ "Black Antoid (2)": 0xD1,
+ "Cave Boy": 0x7B,
+ "Farm Zombie": 0xde,
+ "Criminal Caterpillar": 0xdf,
+ "Evil Eye": 0xe0,
+ "Master Criminal Worm": 0xe3
+}
+
+base_enemy_table = [
+ "Insane Cultist",
+ "Armored Frog",
+ "Bad Buffalo",
+ "Black Antoid",
+ "Red Antoid",
+ "Ramblin' Evil Mushroom",
+ "Struttin' Evil Mushroom",
+ "Mobile Sprout",
+ "Tough Mobile Sprout",
+ "Enraged Fire Plug",
+ "Mystical Record",
+ "Atomic Power Robot",
+ "Nuclear Reactor Robot",
+ "Guardian Hieroglyph",
+ "Lethal Asp Hieroglyph",
+ "Electro Swoosh",
+ "Conducting Menace",
+ "Conducting Spirit",
+ "Evil Elemental",
+ "Annoying Old Party Man",
+ "Annoying Reveler",
+ "Unassuming Local Guy",
+ "New Age Retro Hippie",
+ "Mighty Bear",
+ "Mighty Bear Seven",
+ "Putrid Moldyman",
+ "Thunder Mite",
+ "Cranky Lady",
+ "Extra Cranky Lady",
+ "Wetnosaur",
+ "Chomposaur",
+ "Gigantic Ant",
+ "Scalding Coffee Cup",
+ "Loaded Dice",
+ "Slimy Little Pile",
+ "Even Slimier Little Pile",
+ "Arachnid!",
+ "Arachnid!!!",
+ "Bionic Kraken",
+ "Spinning Robo",
+ "Whirling Robo",
+ "Hyper Spinning Robo",
+ "Cop",
+ "Coil Snake",
+ "Thirsty Coil Snake",
+ "Mr. Batty",
+ "Elder Batty",
+ "Violent Roach",
+ "Filthy Attack Roach",
+ "Crazed Sign",
+ "Wooly Shambler",
+ "Wild 'n Wooly Shambler",
+ "Skate Punk",
+ "Skelpion",
+ "Dread Skelpion",
+ "Starman",
+ "Starman Super",
+ "Ghost of Starman",
+ "Smilin' Sphere",
+ "Uncontrollable Sphere",
+ "Petrified Royal Guard",
+ "Final Starman",
+ "Urban Zombie",
+ "Zombie Possessor",
+ "Zombie Dog",
+ "Over Zealous Cop",
+ "Territorial Oak",
+ "Hostile Elder Oak",
+ "Marauder Octobot",
+ "Military Octobot",
+ "Mechanical Octobot",
+ "Ultimate Octobot",
+ "Mad Duck",
+ "Dali's Clock",
+ "Musica",
+ "Desert Wolf",
+ "Big Pile of Puke",
+ "Kiss of Death",
+ "French Kiss of Death",
+ "Foppy",
+ "Fobby",
+ "Zap Eel",
+ "Tangoo",
+ "Squatter Demon",
+ "Crested Booka",
+ "Great Crested Booka",
+ "Lesser Mook",
+ "Mook Senior",
+ "Smelly Ghost",
+ "Stinky Ghost",
+ "Attack Slug",
+ "Pit Bull Slug",
+ "Rowdy Mouse",
+ "Deadly Mouse",
+ "Care Free Bomb",
+ "Handsome Tom",
+ "Smilin' Sam",
+ "Manly Fish",
+ "Manly Fish's Brother",
+ "Runaway Dog",
+ "Trick or Trick Kid",
+ "Abstract Art",
+ "Shattered Man",
+ "Fierce Shattered Man",
+ "Ego Orb",
+ "Yes Man Junior",
+ "Cute Li'l UFO",
+ "Beautiful UFO",
+ "Pogo Punk",
+ "Tough Guy",
+ "Mad Taxi",
+ "Mr. Molecule",
+ "Worthless Protoplasm",
+ "Sentry Robot",
+ "Psychic Psycho",
+ "Major Psychic Psycho",
+ "Mole Playing Rough",
+ "Gruff Goat",
+ "Soul Consuming Flame",
+ "Demonic Petunia",
+ "Ranboob",
+ "Li'l UFO",
+ "High-class UFO",
+ "Noose Man",
+ "Robo-pump",
+ "Plain Crocodile",
+ "Strong Crocodile",
+ "Hard Crocodile",
+ "No Good Fly",
+ "Mostly Bad Fly",
+ "Spiteful Crow",
+ "Black Antoid (2)",
+ "Struttin' Evil Mushroom",
+ "Cave Boy",
+ "Farm Zombie",
+ "Criminal Caterpillar",
+ "Evil Eye",
+ "Master Criminal Worm",
+ "Loaded Dice 2"
+]
+
+enemy_descriptions = {
+ "Insane Cultist": "@They are friendly if you give them blue gifts.",
+ "Armored Frog": "@Their hard shell makes them very resistant.",
+ "Bad Buffalo": "@It really is a bad kind of buffalo.",
+ "Black Antoid": "@Be careful not to step on him.",
+ "Black Antoid (2)": "@Be careful not to step on him.",
+ "Red Antoid": "@Collectors dig that bright red hue.",
+ "Ramblin' Evil Mushroom": "@He is a really fun guy.",
+ "Struttin' Evil Mushroom": "@Watch the spores!",
+ "Mobile Sprout": "@The next evolution of plant.",
+ "Tough Mobile Sprout": "@This plant is actually made of stainless steel.",
+ "Enraged Fire Plug": "@A little hot-tempered.",
+ "Mystical Record": "@It has all your least favorite songs.",
+ "Atomic Power Robot": "@Handle with care!",
+ "Nuclear Reactor Robot": "@Handle with care!",
+ "Guardian Hieroglyph": "@I found him at the Pyramid north of here.",
+ "Lethal Asp Hieroglyph": "@I found him at the Pyramid north of here.",
+ "Electro Swoosh": "@Don't stare directly at it.",
+ "Conducting Menace": "@Shield your eyes from his light.",
+ "Conducting Spirit": "@Shield your eyes from his light.",
+ "Evil Elemental": "@What is the element of evil?",
+ "Annoying Old Party Man": "@He really knows how to throw a good party.",
+ "Annoying Reveler": "@This is my friend, Dave.",
+ "Unassuming Local Guy": "@What is he so unassuming about?",
+ "New Age Retro Hippie": "@I like him better than the Old Age Modern Urbanite.",
+ "Mighty Bear": "@He's stronger than the average bear.",
+ "Mighty Bear Seven": "@I wonder what happened to two through six?",
+ "Putrid Moldyman": "@I think this one's kinda cute.",
+ "Thunder Mite": "@He mite rain on your parade.",
+ "Cranky Lady": "@There aren't enough stores in the desert.",
+ "Extra Cranky Lady": "@There aren't enough stores in the desert.",
+ "Wetnosaur": "@Reminds me of a fossil I saw once.",
+ "Chomposaur": "@Reminds me of a fossil I saw once.",
+ "Gigantic Ant": "@No relation to the Titanic Ant.",
+ "Scalding Coffee Cup": "@It's a little too hot for me.",
+ "Loaded Dice": "@Always fun at parties.",
+ "Even Slimier Little Pile": "@Smells worse than the Slimy Little Pile.",
+ "Arachnid!": "@I think he just lives here.",
+ "Arachnid!!!": "@How did it get the extra exclamation points?",
+ "Bionic Kraken": "@The bionics make better soup.",
+ "Spinning Robo": "@Look at it go!",
+ "Whirling Robo": "@Look at it go!",
+ "Hyper Spinning Robo": "@Look at it go!",
+ "Cop": "@He said I wasn't paying taxes.",
+ "Coil Snake": "@Not a tool for modding.",
+ "Thirsty Coil Snake": "@What could he be thirsting for?",
+ "Mr. Batty": "@They make for good pets if you're careful.",
+ "Elder Batty": "@They make for good pets if you're careful.",
+ "Violent Roach": "@They make for good pets.",
+ "Filthy Attack Roach": "@They make for good pets.",
+ "Crazed Sign": "@That actually seems like a pretty reasonable speed limit.",
+ "Wooly Shambler": "@These are not actually Earth sheep.",
+ "Wild 'n Wooly Shambler": "@These are not actually Earth sheep.",
+ "Skate Punk": "@I saw him do a sick flip once.",
+ "Skelpion": "@How can scorpions shoot lightning?",
+ "Dread Skelpion": "@How can scorpions shoot lightning?",
+ "Starman": "@Famous for their fan-made websites.",
+ "Starman Super": "@My own personal sword farm.",
+ "Ghost of Starman": "@Are Starmen aliens or robots, anyways?",
+ "Smilin' Sphere": "@I don't like how it smiles at me.",
+ "Uncontrollable Sphere": "@Fortunately, these ones are controlled",
+ "Petrified Royal Guard": "@He isn't guarding this place.",
+ "Final Starman": "@Watch yourself around this one.",
+ "Urban Zombie": "@An import from the big city.",
+ "Zombie Possessor": "@Did you hear something just now?",
+ "Zombie Dog": "@He's more dog than zombie.",
+ "Over Zealous Cop": "@He is overstaying his welcome.",
+ "Territorial Oak": "@Don't worry, this one is just a model.",
+ "Hostile Elder Oak": "@Don't worry, this one is just a model.",
+ "Marauder Octobot": "@Watch out for your valuables!",
+ "Military Octobot": "@Watch out for your valuables!",
+ "Mechanical Octobot": "@Watch out for your valuables!",
+ "Ultimate Octobot": "@Watch out for your valuables!",
+ "Dali's Clock": "@I can never tell what time he's showing.",
+ "Musica": "@His soothing music puts me to sleep.",
+ "Desert Wolf": "@He is a surprisingly good pet.",
+ "Big Pile of Puke": "@How did this one get in here?",
+ "Kiss of Death": "@Despite the name, I wouldn't recommend kissing it.",
+ "French Kiss of Death": "@Despite the name, I wouldn't recommend kissing it.",
+ "Foppy": "@I can't even tell what this thing is.",
+ "Fobby": "@I wonder what color they are?",
+ "Zap Eel": "@How can eels live on land, anyway?",
+ "Tangoo": "@Watch out for his poisonous breath.",
+ "Squatter Demon": "@I don't know what to say about this one.",
+ "Crested Booka": "@The classic desert animal.",
+ "Great Crested Booka": "@The classic desert animal.",
+ "Lesser Mook": "@These aliens live here.",
+ "Mook Senior": "@These aliens live here.",
+ "Smelly Ghost": "@This exhibit needs some air freshener.",
+ "Stinky Ghost": "@This exhibit needs some air freshener.",
+ "Attack Slug": "@Cute when alone.",
+ "Pit Bull Slug": "@It is neither pit bull nor slug.",
+ "Rowdy Mouse": "@They're good pets if you don't let them SMAAAASH you.",
+ "Deadly Mouse": "@They're good pets if you don't let them SMAAAASH you.",
+ "Care Free Bomb": "@Don't get too close!",
+ "Handsome Tom": "@Not really all that handsome.",
+ "Smilin' Sam": "@He usually doesn't smile.",
+ "Manly Fish": "@Not related to Manly Fish's Brother.",
+ "Manly Fish's Brother": "@They are not actually related.",
+ "Runaway Dog": "@Don't worry, this one is adopted.",
+ "Trick or Trick Kid": "@He likes to play Clique.",
+ "Cave Boy": "@Not quite a Cave Man.",
+ "Abstract Art": "@Looks good in any gallery.",
+ "Shattered Man": "@He doesn't look so shattered to me.",
+ "Fierce Shattered Man": "@He doesn't look so shattered to me.",
+ "Ego Orb": "@I'll get back to you on that one.",
+ "Yes Man Junior": "@Not that bad of a guy.",
+ "Cute Li'l UFO": "@The ribbon is actually biological.",
+ "Beautiful UFO": "@Personally, I'm a bigger fan of the Li'l UFO.",
+ "Pogo Punk": "@He knows some sick tricks.",
+ "Tough Guy": "@Likes to chew gum and kick butt.",
+ "Mad Taxi": "@It has surprisingly good mental health.",
+ "Mr. Molecule": "@His whole family is here.",
+ "Worthless Protoplasm": "@He's actually kind of worthwhile.",
+ "Sentry Robot": "@Built by a crafty engineer.",
+ "Psychic Psycho": "@Watch out for Fire attacks!",
+ "Major Psychic Psycho": "@Watch out for Fire attacks!",
+ "Mole Playing Rough": "@They like to show up in the most random places.",
+ "Soul Consuming Flame": "@This one can't affect me.",
+ "Demonic Petunia": "@Looks beautiful from a distance.",
+ "Ranboob": "@Yeah I've got nothing to say on this one.",
+ "Li'l UFO": "@Personally, I'm a bigger fan of the High-class UFO.",
+ "High-class UFO": "@Personally, I'm a bigger fan of the Beautiful UFO.",
+ "Noose Man": "@How was this approved for a family game?",
+ "Robo-pump": "@It causes fires more than it puts them out.",
+ "Plain Crocodile": "@Also known as the vanilla crocodile.",
+ "Strong Crocodile": "@Stronger than the weak crocodile",
+ "Hard Crocodile": "@Slightly harder than the soft crocodile.",
+ "No Good Fly": "@They are often mistaken for bees.",
+ "Mostly Bad Fly": "@He's not all bad.",
+ "Spiteful Crow": "@Keep your valuables in your pocket!",
+ "Farm Zombie": "@Easily subdued by plants.",
+ "Criminal Caterpillar": "@He is wanted in seventeen countries for his crimes.",
+ "Evil Eye": "@Third-year staring contest champion.",
+ "Master Criminal Worm": "@He is wanted in seventeen countries for his crimes.",
+ "Loaded Dice 2": "@Always dangerous at parties.",
+}
+
+can_walkthrough = [
+ 0x0045,
+ 0x004A,
+ 0x0065,
+ 0x00C3,
+ 0x0112,
+ 0x0114,
+ 0x0115,
+ 0x0116,
+ 0x0117,
+ 0x0118,
+ 0x011A,
+ 0x011B,
+ 0x011C,
+ 0x011D,
+ 0x011E,
+ 0x011F,
+ 0x0120,
+ 0x0123,
+ 0x0124,
+ 0x012D,
+ 0x012E,
+ 0x012F,
+ 0x0130,
+ 0x0131,
+ 0x0132,
+ 0x0133,
+ 0x0134,
+ 0x0135,
+ 0x0136,
+ 0x0139,
+ 0x013A,
+ 0x013B,
+ 0x013C,
+ 0x013D,
+ 0x013E,
+ 0x013F,
+ 0x0140,
+ 0x0142,
+ 0x0143,
+ 0x0144,
+ 0x0145,
+ 0x0146,
+ 0x0169,
+ 0x0182,
+ 0x0185,
+ 0x0186,
+ 0x0187,
+ 0x0188,
+ 0x019F,
+ 0x01A0,
+ 0x01A1,
+ 0x01BC,
+ 0x01CD
+]
+
+
+def shuffle_enemies(world: "EarthBoundWorld") -> None:
+ """Shuffles the global enemy table."""
+ world.acting_enemy_list = {}
+ shuffled_enemies = base_enemy_table.copy()
+ if world.options.enemy_shuffle:
+ world.random.shuffle(shuffled_enemies)
+ for index, enemy in enumerate(shuffled_enemies):
+ world.acting_enemy_list[base_enemy_table[index]] = enemy
+
+
+def apply_enemy_shuffle(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ """Writes the shuffled enemy table into ROM."""
+ rom.write_bytes(0x10d54d, bytearray([enemy_ids[world.acting_enemy_list["Spiteful Crow"]]]))
+ rom.write_bytes(0x10d551, bytearray([enemy_ids[world.acting_enemy_list["Runaway Dog"]]]))
+ rom.write_bytes(0x10d555, bytearray([enemy_ids[world.acting_enemy_list["Coil Snake"]]]))
+ rom.write_bytes(0x10d559, bytearray([enemy_ids[world.acting_enemy_list["Ghost of Starman"]]]))
+ rom.write_bytes(0x10d55c, bytearray([enemy_ids[world.acting_enemy_list["Evil Eye"]]]))
+ rom.write_bytes(0x10d567, bytearray([enemy_ids[world.acting_enemy_list["Evil Eye"]]]))
+ rom.write_bytes(0x10d56a, bytearray([enemy_ids[world.acting_enemy_list["Mechanical Octobot"]]]))
+ rom.write_bytes(0x10d56e, bytearray([enemy_ids[world.acting_enemy_list["Ghost of Starman"]]]))
+ rom.write_bytes(0x10d571, bytearray([enemy_ids[world.acting_enemy_list["Mechanical Octobot"]]]))
+ rom.write_bytes(0x10d574, bytearray([enemy_ids[world.acting_enemy_list["Evil Eye"]]]))
+ rom.write_bytes(0x10d578, bytearray([enemy_ids[world.acting_enemy_list["Skate Punk"]]]))
+ rom.write_bytes(0x10d57b, bytearray([enemy_ids[world.acting_enemy_list["Yes Man Junior"]]]))
+ rom.write_bytes(0x10d57e, bytearray([enemy_ids[world.acting_enemy_list["Pogo Punk"]]]))
+ rom.write_bytes(0x10d582, bytearray([enemy_ids[world.acting_enemy_list["Skate Punk"]]]))
+ rom.write_bytes(0x10d585, bytearray([enemy_ids[world.acting_enemy_list["Pogo Punk"]]]))
+ rom.write_bytes(0x10d588, bytearray([enemy_ids[world.acting_enemy_list["Yes Man Junior"]]]))
+ rom.write_bytes(0x10d58c, bytearray([enemy_ids[world.acting_enemy_list["Pogo Punk"]]]))
+ rom.write_bytes(0x10d58f, bytearray([enemy_ids[world.acting_enemy_list["Yes Man Junior"]]]))
+ rom.write_bytes(0x10d593, bytearray([enemy_ids[world.acting_enemy_list["Skate Punk"]]]))
+ rom.write_bytes(0x10d596, bytearray([enemy_ids[world.acting_enemy_list["Yes Man Junior"]]]))
+ rom.write_bytes(0x10d599, bytearray([enemy_ids[world.acting_enemy_list["Pogo Punk"]]]))
+ rom.write_bytes(0x10d5a1, bytearray([enemy_ids[world.acting_enemy_list["Spiteful Crow"]]]))
+ rom.write_bytes(0x10d5a9, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10d5ac, bytearray([enemy_ids[world.acting_enemy_list["Whirling Robo"]]]))
+ rom.write_bytes(0x10d5b0, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10d5b3, bytearray([enemy_ids[world.acting_enemy_list["Wooly Shambler"]]]))
+ rom.write_bytes(0x10d5b7, bytearray([enemy_ids[world.acting_enemy_list["Wooly Shambler"]]]))
+ rom.write_bytes(0x10d5ba, bytearray([enemy_ids[world.acting_enemy_list["Whirling Robo"]]]))
+ rom.write_bytes(0x10d5be, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10d5c1, bytearray([enemy_ids[world.acting_enemy_list["Whirling Robo"]]]))
+ rom.write_bytes(0x10d5c4, bytearray([enemy_ids[world.acting_enemy_list["Wooly Shambler"]]]))
+ rom.write_bytes(0x10d5c8, bytearray([enemy_ids[world.acting_enemy_list["Spiteful Crow"]]]))
+ rom.write_bytes(0x10d5cc, bytearray([enemy_ids[world.acting_enemy_list["Gruff Goat"]]]))
+ rom.write_bytes(0x10d5d4, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10d5d7, bytearray([enemy_ids[world.acting_enemy_list["Whirling Robo"]]]))
+ rom.write_bytes(0x10d5db, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10d5de, bytearray([enemy_ids[world.acting_enemy_list["Wooly Shambler"]]]))
+ rom.write_bytes(0x10d5e2, bytearray([enemy_ids[world.acting_enemy_list["Wooly Shambler"]]]))
+ rom.write_bytes(0x10d5e5, bytearray([enemy_ids[world.acting_enemy_list["Whirling Robo"]]]))
+ rom.write_bytes(0x10d5e9, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10d5ec, bytearray([enemy_ids[world.acting_enemy_list["Wooly Shambler"]]]))
+ rom.write_bytes(0x10d5ef, bytearray([enemy_ids[world.acting_enemy_list["Whirling Robo"]]]))
+ rom.write_bytes(0x10d5f7, bytearray([enemy_ids[world.acting_enemy_list["Cave Boy"]]]))
+ rom.write_bytes(0x10d5fa, bytearray([enemy_ids[world.acting_enemy_list["Mighty Bear Seven"]]]))
+ rom.write_bytes(0x10d5fe, bytearray([enemy_ids[world.acting_enemy_list["Cave Boy"]]]))
+ rom.write_bytes(0x10d601, bytearray([enemy_ids[world.acting_enemy_list["Mighty Bear Seven"]]]))
+ rom.write_bytes(0x10d605, bytearray([enemy_ids[world.acting_enemy_list["Cave Boy"]]]))
+ rom.write_bytes(0x10d608, bytearray([enemy_ids[world.acting_enemy_list["Mighty Bear Seven"]]]))
+ rom.write_bytes(0x10d60c, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid"]]]))
+ rom.write_bytes(0x10d610, bytearray([enemy_ids[world.acting_enemy_list["Ramblin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10d614, bytearray([enemy_ids[world.acting_enemy_list["Ramblin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10d618, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid"]]]))
+ rom.write_bytes(0x10d61b, bytearray([enemy_ids[world.acting_enemy_list["Ramblin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10d61f, bytearray([enemy_ids[world.acting_enemy_list["Ramblin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10d623, bytearray([enemy_ids[world.acting_enemy_list["Ramblin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10d627, bytearray([enemy_ids[world.acting_enemy_list["Runaway Dog"]]]))
+ rom.write_bytes(0x10d62a, bytearray([enemy_ids[world.acting_enemy_list["Cop"]]]))
+ rom.write_bytes(0x10d62e, bytearray([enemy_ids[world.acting_enemy_list["Cranky Lady"]]]))
+ rom.write_bytes(0x10d632, bytearray([enemy_ids[world.acting_enemy_list["Annoying Old Party Man"]]]))
+ rom.write_bytes(0x10d636, bytearray([enemy_ids[world.acting_enemy_list["Unassuming Local Guy"]]]))
+ rom.write_bytes(0x10d63a, bytearray([enemy_ids[world.acting_enemy_list["New Age Retro Hippie"]]]))
+ rom.write_bytes(0x10d63e, bytearray([enemy_ids[world.acting_enemy_list["Ramblin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10d641, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10d645, bytearray([enemy_ids[world.acting_enemy_list["Ramblin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10d649, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10d64c, bytearray([enemy_ids[world.acting_enemy_list["Territorial Oak"]]]))
+ rom.write_bytes(0x10d650, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10d653, bytearray([enemy_ids[world.acting_enemy_list["Territorial Oak"]]]))
+ rom.write_bytes(0x10d657, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10d65a, bytearray([enemy_ids[world.acting_enemy_list["Li'l UFO"]]]))
+ rom.write_bytes(0x10d65e, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10d662, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10d666, bytearray([enemy_ids[world.acting_enemy_list["Territorial Oak"]]]))
+ rom.write_bytes(0x10d66a, bytearray([enemy_ids[world.acting_enemy_list["Spinning Robo"]]]))
+ rom.write_bytes(0x10d66e, bytearray([enemy_ids[world.acting_enemy_list["Li'l UFO"]]]))
+ rom.write_bytes(0x10d672, bytearray([enemy_ids[world.acting_enemy_list["Li'l UFO"]]]))
+ rom.write_bytes(0x10d675, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10d679, bytearray([enemy_ids[world.acting_enemy_list["Li'l UFO"]]]))
+ rom.write_bytes(0x10d67c, bytearray([enemy_ids[world.acting_enemy_list["Spinning Robo"]]]))
+ rom.write_bytes(0x10d680, bytearray([enemy_ids[world.acting_enemy_list["Li'l UFO"]]]))
+ rom.write_bytes(0x10d684, bytearray([enemy_ids[world.acting_enemy_list["Insane Cultist"]]]))
+ rom.write_bytes(0x10d688, bytearray([enemy_ids[world.acting_enemy_list["Insane Cultist"]]]))
+ rom.write_bytes(0x10d68c, bytearray([enemy_ids[world.acting_enemy_list["Spiteful Crow"]]]))
+ rom.write_bytes(0x10d690, bytearray([enemy_ids[world.acting_enemy_list["Spiteful Crow"]]]))
+ rom.write_bytes(0x10d694, bytearray([enemy_ids[world.acting_enemy_list["Cranky Lady"]]]))
+ rom.write_bytes(0x10d698, bytearray([enemy_ids[world.acting_enemy_list["Annoying Old Party Man"]]]))
+ rom.write_bytes(0x10d69c, bytearray([enemy_ids[world.acting_enemy_list["New Age Retro Hippie"]]]))
+ rom.write_bytes(0x10d6a0, bytearray([enemy_ids[world.acting_enemy_list["Trick or Trick Kid"]]]))
+ rom.write_bytes(0x10d6a3, bytearray([enemy_ids[world.acting_enemy_list["Handsome Tom"]]]))
+ rom.write_bytes(0x10d6a7, bytearray([enemy_ids[world.acting_enemy_list["Trick or Trick Kid"]]]))
+ rom.write_bytes(0x10d6ab, bytearray([enemy_ids[world.acting_enemy_list["Handsome Tom"]]]))
+ rom.write_bytes(0x10d6ae, bytearray([enemy_ids[world.acting_enemy_list["Smilin' Sam"]]]))
+ rom.write_bytes(0x10d6b2, bytearray([enemy_ids[world.acting_enemy_list["Handsome Tom"]]]))
+ rom.write_bytes(0x10d6b6, bytearray([enemy_ids[world.acting_enemy_list["Smilin' Sam"]]]))
+ rom.write_bytes(0x10d6ba, bytearray([enemy_ids[world.acting_enemy_list["Urban Zombie"]]]))
+ rom.write_bytes(0x10d6be, bytearray([enemy_ids[world.acting_enemy_list["Zombie Possessor"]]]))
+ rom.write_bytes(0x10d6c1, bytearray([enemy_ids[world.acting_enemy_list["Urban Zombie"]]]))
+ rom.write_bytes(0x10d6c5, bytearray([enemy_ids[world.acting_enemy_list["No Good Fly"]]]))
+ rom.write_bytes(0x10d6c8, bytearray([enemy_ids[world.acting_enemy_list["Putrid Moldyman"]]]))
+ rom.write_bytes(0x10d6cc, bytearray([enemy_ids[world.acting_enemy_list["Putrid Moldyman"]]]))
+ rom.write_bytes(0x10d6cf, bytearray([enemy_ids[world.acting_enemy_list["Smelly Ghost"]]]))
+ rom.write_bytes(0x10d6d3, bytearray([enemy_ids[world.acting_enemy_list["Zombie Possessor"]]]))
+ rom.write_bytes(0x10d6d6, bytearray([enemy_ids[world.acting_enemy_list["Putrid Moldyman"]]]))
+ rom.write_bytes(0x10d6da, bytearray([enemy_ids[world.acting_enemy_list["Zombie Possessor"]]]))
+ rom.write_bytes(0x10d6dd, bytearray([enemy_ids[world.acting_enemy_list["Smelly Ghost"]]]))
+ rom.write_bytes(0x10d6e1, bytearray([enemy_ids[world.acting_enemy_list["Putrid Moldyman"]]]))
+ rom.write_bytes(0x10d6e4, bytearray([enemy_ids[world.acting_enemy_list["Smelly Ghost"]]]))
+ rom.write_bytes(0x10d6e8, bytearray([enemy_ids[world.acting_enemy_list["No Good Fly"]]]))
+ rom.write_bytes(0x10d6eb, bytearray([enemy_ids[world.acting_enemy_list["Putrid Moldyman"]]]))
+ rom.write_bytes(0x10d6ef, bytearray([enemy_ids[world.acting_enemy_list["No Good Fly"]]]))
+ rom.write_bytes(0x10d6f2, bytearray([enemy_ids[world.acting_enemy_list["Smelly Ghost"]]]))
+ rom.write_bytes(0x10d6f6, bytearray([enemy_ids[world.acting_enemy_list["No Good Fly"]]]))
+ rom.write_bytes(0x10d6fa, bytearray([enemy_ids[world.acting_enemy_list["Zombie Dog"]]]))
+ rom.write_bytes(0x10d6fe, bytearray([enemy_ids[world.acting_enemy_list["Zombie Dog"]]]))
+ rom.write_bytes(0x10d701, bytearray([enemy_ids[world.acting_enemy_list["No Good Fly"]]]))
+ rom.write_bytes(0x10d705, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d70c, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d713, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d716, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d719, bytearray([enemy_ids[world.acting_enemy_list["Farm Zombie"]]]))
+ rom.write_bytes(0x10d71d, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d720, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d724, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d727, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d72b, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d72e, bytearray([enemy_ids[world.acting_enemy_list["Farm Zombie"]]]))
+ rom.write_bytes(0x10d732, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d735, bytearray([enemy_ids[world.acting_enemy_list["Farm Zombie"]]]))
+ rom.write_bytes(0x10d739, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d73d, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d740, bytearray([enemy_ids[world.acting_enemy_list["Farm Zombie"]]]))
+ rom.write_bytes(0x10d744, bytearray([enemy_ids[world.acting_enemy_list["Plain Crocodile"]]]))
+ rom.write_bytes(0x10d747, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d74b, bytearray([enemy_ids[world.acting_enemy_list["Plain Crocodile"]]]))
+ rom.write_bytes(0x10d74e, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d751, bytearray([enemy_ids[world.acting_enemy_list["Farm Zombie"]]]))
+ rom.write_bytes(0x10d755, bytearray([enemy_ids[world.acting_enemy_list["Plain Crocodile"]]]))
+ rom.write_bytes(0x10d758, bytearray([enemy_ids[world.acting_enemy_list["Farm Zombie"]]]))
+ rom.write_bytes(0x10d75c, bytearray([enemy_ids[world.acting_enemy_list["Plain Crocodile"]]]))
+ rom.write_bytes(0x10d75f, bytearray([enemy_ids[world.acting_enemy_list["Farm Zombie"]]]))
+ rom.write_bytes(0x10d763, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d769, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d76d, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d770, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid (2)"]]]))
+ rom.write_bytes(0x10d766, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid (2)"]]]))
+ rom.write_bytes(0x10d774, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d777, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid (2)"]]]))
+ rom.write_bytes(0x10d77b, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d77e, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d781, bytearray([enemy_ids[world.acting_enemy_list["Plain Crocodile"]]]))
+ rom.write_bytes(0x10d785, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d788, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d78c, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d78f, bytearray([enemy_ids[world.acting_enemy_list["Plain Crocodile"]]]))
+ rom.write_bytes(0x10d793, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d796, bytearray([enemy_ids[world.acting_enemy_list["Plain Crocodile"]]]))
+ rom.write_bytes(0x10d79a, bytearray([enemy_ids[world.acting_enemy_list["Red Antoid"]]]))
+ rom.write_bytes(0x10d79d, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d7a1, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d7a5, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d7a9, bytearray([enemy_ids[world.acting_enemy_list["Plain Crocodile"]]]))
+ rom.write_bytes(0x10d7ac, bytearray([enemy_ids[world.acting_enemy_list["Armored Frog"]]]))
+ rom.write_bytes(0x10d7b0, bytearray([enemy_ids[world.acting_enemy_list["Plain Crocodile"]]]))
+ rom.write_bytes(0x10d7b4, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10d7b8, bytearray([enemy_ids[world.acting_enemy_list["Violent Roach"]]]))
+ rom.write_bytes(0x10d7bc, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10d7c0, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10d7c3, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10d7c7, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10d7cb, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10d7ce, bytearray([enemy_ids[world.acting_enemy_list["Struttin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10d7d1, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10d7d5, bytearray([enemy_ids[world.acting_enemy_list["Bad Buffalo"]]]))
+ rom.write_bytes(0x10d7d8, bytearray([enemy_ids[world.acting_enemy_list["Desert Wolf"]]]))
+ rom.write_bytes(0x10d7dc, bytearray([enemy_ids[world.acting_enemy_list["Bad Buffalo"]]]))
+ rom.write_bytes(0x10d7df, bytearray([enemy_ids[world.acting_enemy_list["Smilin' Sphere"]]]))
+ rom.write_bytes(0x10d7e3, bytearray([enemy_ids[world.acting_enemy_list["Bad Buffalo"]]]))
+ rom.write_bytes(0x10d7e7, bytearray([enemy_ids[world.acting_enemy_list["Desert Wolf"]]]))
+ rom.write_bytes(0x10d7eb, bytearray([enemy_ids[world.acting_enemy_list["Skelpion"]]]))
+ rom.write_bytes(0x10d7ee, bytearray([enemy_ids[world.acting_enemy_list["Cute Li'l UFO"]]]))
+ rom.write_bytes(0x10d7f2, bytearray([enemy_ids[world.acting_enemy_list["Skelpion"]]]))
+ rom.write_bytes(0x10d7f5, bytearray([enemy_ids[world.acting_enemy_list["Smilin' Sphere"]]]))
+ rom.write_bytes(0x10d7f9, bytearray([enemy_ids[world.acting_enemy_list["Skelpion"]]]))
+ rom.write_bytes(0x10d7fc, bytearray([enemy_ids[world.acting_enemy_list["Smilin' Sphere"]]]))
+ rom.write_bytes(0x10d7ff, bytearray([enemy_ids[world.acting_enemy_list["Cute Li'l UFO"]]]))
+ rom.write_bytes(0x10d803, bytearray([enemy_ids[world.acting_enemy_list["Skelpion"]]]))
+ rom.write_bytes(0x10d806, bytearray([enemy_ids[world.acting_enemy_list["Crested Booka"]]]))
+ rom.write_bytes(0x10d80a, bytearray([enemy_ids[world.acting_enemy_list["Skelpion"]]]))
+ rom.write_bytes(0x10d80e, bytearray([enemy_ids[world.acting_enemy_list["Skelpion"]]]))
+ rom.write_bytes(0x10d812, bytearray([enemy_ids[world.acting_enemy_list["Crested Booka"]]]))
+ rom.write_bytes(0x10d815, bytearray([enemy_ids[world.acting_enemy_list["Bad Buffalo"]]]))
+ rom.write_bytes(0x10d819, bytearray([enemy_ids[world.acting_enemy_list["Crested Booka"]]]))
+ rom.write_bytes(0x10d81c, bytearray([enemy_ids[world.acting_enemy_list["Desert Wolf"]]]))
+ rom.write_bytes(0x10d820, bytearray([enemy_ids[world.acting_enemy_list["Crested Booka"]]]))
+ rom.write_bytes(0x10d823, bytearray([enemy_ids[world.acting_enemy_list["Cute Li'l UFO"]]]))
+ rom.write_bytes(0x10d826, bytearray([enemy_ids[world.acting_enemy_list["Smilin' Sphere"]]]))
+ rom.write_bytes(0x10d82a, bytearray([enemy_ids[world.acting_enemy_list["Crested Booka"]]]))
+ rom.write_bytes(0x10d82d, bytearray([enemy_ids[world.acting_enemy_list["Cute Li'l UFO"]]]))
+ rom.write_bytes(0x10d831, bytearray([enemy_ids[world.acting_enemy_list["Crested Booka"]]]))
+ rom.write_bytes(0x10d834, bytearray([enemy_ids[world.acting_enemy_list["Smilin' Sphere"]]]))
+ rom.write_bytes(0x10d838, bytearray([enemy_ids[world.acting_enemy_list["Mad Taxi"]]]))
+ rom.write_bytes(0x10d83c, bytearray([enemy_ids[world.acting_enemy_list["Extra Cranky Lady"]]]))
+ rom.write_bytes(0x10d840, bytearray([enemy_ids[world.acting_enemy_list["Annoying Reveler"]]]))
+ rom.write_bytes(0x10d844, bytearray([enemy_ids[world.acting_enemy_list["Crazed Sign"]]]))
+ rom.write_bytes(0x10d848, bytearray([enemy_ids[world.acting_enemy_list["Dali's Clock"]]]))
+ rom.write_bytes(0x10d84c, bytearray([enemy_ids[world.acting_enemy_list["Enraged Fire Plug"]]]))
+ rom.write_bytes(0x10d850, bytearray([enemy_ids[world.acting_enemy_list["Enraged Fire Plug"]]]))
+ rom.write_bytes(0x10d854, bytearray([enemy_ids[world.acting_enemy_list["Abstract Art"]]]))
+ rom.write_bytes(0x10d858, bytearray([enemy_ids[world.acting_enemy_list["Robo-pump"]]]))
+ rom.write_bytes(0x10d85c, bytearray([enemy_ids[world.acting_enemy_list["Robo-pump"]]]))
+ rom.write_bytes(0x10d85f, bytearray([enemy_ids[world.acting_enemy_list["Enraged Fire Plug"]]]))
+ rom.write_bytes(0x10d863, bytearray([enemy_ids[world.acting_enemy_list["Mad Taxi"]]]))
+ rom.write_bytes(0x10d867, bytearray([enemy_ids[world.acting_enemy_list["Mad Taxi"]]]))
+ rom.write_bytes(0x10d86a, bytearray([enemy_ids[world.acting_enemy_list["Crazed Sign"]]]))
+ rom.write_bytes(0x10d86e, bytearray([enemy_ids[world.acting_enemy_list["Crazed Sign"]]]))
+ rom.write_bytes(0x10d872, bytearray([enemy_ids[world.acting_enemy_list["Tough Guy"]]]))
+ rom.write_bytes(0x10d876, bytearray([enemy_ids[world.acting_enemy_list["Over Zealous Cop"]]]))
+ rom.write_bytes(0x10d879, bytearray([enemy_ids[world.acting_enemy_list["Tough Guy"]]]))
+ rom.write_bytes(0x10d87d, bytearray([enemy_ids[world.acting_enemy_list["Over Zealous Cop"]]]))
+ rom.write_bytes(0x10d881, bytearray([enemy_ids[world.acting_enemy_list["High-class UFO"]]]))
+ rom.write_bytes(0x10d884, bytearray([enemy_ids[world.acting_enemy_list["Beautiful UFO"]]]))
+ rom.write_bytes(0x10d888, bytearray([enemy_ids[world.acting_enemy_list["High-class UFO"]]]))
+ rom.write_bytes(0x10d88c, bytearray([enemy_ids[world.acting_enemy_list["High-class UFO"]]]))
+ rom.write_bytes(0x10d890, bytearray([enemy_ids[world.acting_enemy_list["Beautiful UFO"]]]))
+ rom.write_bytes(0x10d894, bytearray([enemy_ids[world.acting_enemy_list["Dread Skelpion"]]]))
+ rom.write_bytes(0x10d897, bytearray([enemy_ids[world.acting_enemy_list["Great Crested Booka"]]]))
+ rom.write_bytes(0x10d89b, bytearray([enemy_ids[world.acting_enemy_list["Dread Skelpion"]]]))
+ rom.write_bytes(0x10d89f, bytearray([enemy_ids[world.acting_enemy_list["Great Crested Booka"]]]))
+ rom.write_bytes(0x10d8a3, bytearray([enemy_ids[world.acting_enemy_list["Great Crested Booka"]]]))
+ rom.write_bytes(0x10d8a6, bytearray([enemy_ids[world.acting_enemy_list["Dread Skelpion"]]]))
+ rom.write_bytes(0x10d8aa, bytearray([enemy_ids[world.acting_enemy_list["Great Crested Booka"]]]))
+ rom.write_bytes(0x10d8ae, bytearray([enemy_ids[world.acting_enemy_list["High-class UFO"]]]))
+ rom.write_bytes(0x10d8b1, bytearray([enemy_ids[world.acting_enemy_list["Beautiful UFO"]]]))
+ rom.write_bytes(0x10d8b5, bytearray([enemy_ids[world.acting_enemy_list["High-class UFO"]]]))
+ rom.write_bytes(0x10d8b8, bytearray([enemy_ids[world.acting_enemy_list["Marauder Octobot"]]]))
+ rom.write_bytes(0x10d8bb, bytearray([enemy_ids[world.acting_enemy_list["Beautiful UFO"]]]))
+ rom.write_bytes(0x10d8bf, bytearray([enemy_ids[world.acting_enemy_list["High-class UFO"]]]))
+ rom.write_bytes(0x10d8c2, bytearray([enemy_ids[world.acting_enemy_list["Marauder Octobot"]]]))
+ rom.write_bytes(0x10d8c6, bytearray([enemy_ids[world.acting_enemy_list["Beautiful UFO"]]]))
+ rom.write_bytes(0x10d8ca, bytearray([enemy_ids[world.acting_enemy_list["Marauder Octobot"]]]))
+ rom.write_bytes(0x10d8ce, bytearray([enemy_ids[world.acting_enemy_list["Hostile Elder Oak"]]]))
+ rom.write_bytes(0x10d8d2, bytearray([enemy_ids[world.acting_enemy_list["Pit Bull Slug"]]]))
+ rom.write_bytes(0x10d8d6, bytearray([enemy_ids[world.acting_enemy_list["Demonic Petunia"]]]))
+ rom.write_bytes(0x10d8da, bytearray([enemy_ids[world.acting_enemy_list["Big Pile of Puke"]]]))
+ rom.write_bytes(0x10d8dd, bytearray([enemy_ids[world.acting_enemy_list["Even Slimier Little Pile"]]]))
+ rom.write_bytes(0x10d8e1, bytearray([enemy_ids[world.acting_enemy_list["Big Pile of Puke"]]]))
+ rom.write_bytes(0x10d8e4, bytearray([enemy_ids[world.acting_enemy_list["Even Slimier Little Pile"]]]))
+ rom.write_bytes(0x10d8e8, bytearray([enemy_ids[world.acting_enemy_list["Hostile Elder Oak"]]]))
+ rom.write_bytes(0x10d8ec, bytearray([enemy_ids[world.acting_enemy_list["Zap Eel"]]]))
+ rom.write_bytes(0x10d8ef, bytearray([enemy_ids[world.acting_enemy_list["Hard Crocodile"]]]))
+ rom.write_bytes(0x10d8f3, bytearray([enemy_ids[world.acting_enemy_list["Zap Eel"]]]))
+ rom.write_bytes(0x10d8f7, bytearray([enemy_ids[world.acting_enemy_list["Even Slimier Little Pile"]]]))
+ rom.write_bytes(0x10d8fa, bytearray([enemy_ids[world.acting_enemy_list["Zap Eel"]]]))
+ rom.write_bytes(0x10d8fe, bytearray([enemy_ids[world.acting_enemy_list["Even Slimier Little Pile"]]]))
+ rom.write_bytes(0x10d901, bytearray([enemy_ids[world.acting_enemy_list["Hard Crocodile"]]]))
+ rom.write_bytes(0x10d905, bytearray([enemy_ids[world.acting_enemy_list["Even Slimier Little Pile"]]]))
+ rom.write_bytes(0x10d908, bytearray([enemy_ids[world.acting_enemy_list["Manly Fish"]]]))
+ rom.write_bytes(0x10d90c, bytearray([enemy_ids[world.acting_enemy_list["Manly Fish"]]]))
+ rom.write_bytes(0x10d90f, bytearray([enemy_ids[world.acting_enemy_list["Hard Crocodile"]]]))
+ rom.write_bytes(0x10d913, bytearray([enemy_ids[world.acting_enemy_list["Manly Fish"]]]))
+ rom.write_bytes(0x10d916, bytearray([enemy_ids[world.acting_enemy_list["Manly Fish's Brother"]]]))
+ rom.write_bytes(0x10d91a, bytearray([enemy_ids[world.acting_enemy_list["Pit Bull Slug"]]]))
+ rom.write_bytes(0x10d91e, bytearray([enemy_ids[world.acting_enemy_list["Demonic Petunia"]]]))
+ rom.write_bytes(0x10d922, bytearray([enemy_ids[world.acting_enemy_list["Demonic Petunia"]]]))
+ rom.write_bytes(0x10d925, bytearray([enemy_ids[world.acting_enemy_list["Hostile Elder Oak"]]]))
+ rom.write_bytes(0x10d929, bytearray([enemy_ids[world.acting_enemy_list["Wetnosaur"]]]))
+ rom.write_bytes(0x10d92d, bytearray([enemy_ids[world.acting_enemy_list["Chomposaur"]]]))
+ rom.write_bytes(0x10d931, bytearray([enemy_ids[world.acting_enemy_list["Ego Orb"]]]))
+ rom.write_bytes(0x10d935, bytearray([enemy_ids[world.acting_enemy_list["Care Free Bomb"]]]))
+ rom.write_bytes(0x10d938, bytearray([enemy_ids[world.acting_enemy_list["Mr. Molecule"]]]))
+ rom.write_bytes(0x10d93c, bytearray([enemy_ids[world.acting_enemy_list["Care Free Bomb"]]]))
+ rom.write_bytes(0x10d940, bytearray([enemy_ids[world.acting_enemy_list["French Kiss of Death"]]]))
+ rom.write_bytes(0x10d944, bytearray([enemy_ids[world.acting_enemy_list["Loaded Dice"]]]))
+ rom.write_bytes(0x10d947, bytearray([enemy_ids[world.acting_enemy_list["Care Free Bomb"]]]))
+ rom.write_bytes(0x10d94a, bytearray([enemy_ids[world.acting_enemy_list["Beautiful UFO"]]]))
+ rom.write_bytes(0x10d94d, bytearray([enemy_ids[world.acting_enemy_list["High-class UFO"]]]))
+
+ rom.write_bytes(0x10d951, bytearray([enemy_ids[world.acting_enemy_list["Loaded Dice 2"]]]))
+ rom.write_bytes(0x10d954, bytearray([enemy_ids[world.acting_enemy_list["Electro Swoosh"]]]))
+ rom.write_bytes(0x10d957, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10d95a, bytearray([enemy_ids[world.acting_enemy_list["Uncontrollable Sphere"]]]))
+ rom.write_bytes(0x10d95e, bytearray([enemy_ids[world.acting_enemy_list["Electro Swoosh"]]]))
+ rom.write_bytes(0x10d962, bytearray([enemy_ids[world.acting_enemy_list["Electro Swoosh"]]]))
+ rom.write_bytes(0x10d965, bytearray([enemy_ids[world.acting_enemy_list["French Kiss of Death"]]]))
+ rom.write_bytes(0x10d969, bytearray([enemy_ids[world.acting_enemy_list["Mr. Molecule"]]]))
+ rom.write_bytes(0x10d96d, bytearray([enemy_ids[world.acting_enemy_list["Mr. Molecule"]]]))
+ rom.write_bytes(0x10d971, bytearray([enemy_ids[world.acting_enemy_list["Rowdy Mouse"]]]))
+ rom.write_bytes(0x10d974, bytearray([enemy_ids[world.acting_enemy_list["Attack Slug"]]]))
+ rom.write_bytes(0x10d978, bytearray([enemy_ids[world.acting_enemy_list["Rowdy Mouse"]]]))
+ rom.write_bytes(0x10d97f, bytearray([enemy_ids[world.acting_enemy_list["Rowdy Mouse"]]]))
+ rom.write_bytes(0x10d98b, bytearray([enemy_ids[world.acting_enemy_list["Attack Slug"]]]))
+ rom.write_bytes(0x10d98f, bytearray([enemy_ids[world.acting_enemy_list["Attack Slug"]]]))
+ rom.write_bytes(0x10d993, bytearray([enemy_ids[world.acting_enemy_list["Elder Batty"]]]))
+ rom.write_bytes(0x10d996, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!"]]]))
+ rom.write_bytes(0x10d99a, bytearray([enemy_ids[world.acting_enemy_list["Elder Batty"]]]))
+ rom.write_bytes(0x10d99e, bytearray([enemy_ids[world.acting_enemy_list["Elder Batty"]]]))
+ rom.write_bytes(0x10d9a1, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!"]]]))
+ rom.write_bytes(0x10d9a5, bytearray([enemy_ids[world.acting_enemy_list["Elder Batty"]]]))
+ rom.write_bytes(0x10d9a8, bytearray([enemy_ids[world.acting_enemy_list["Strong Crocodile"]]]))
+ rom.write_bytes(0x10d9ac, bytearray([enemy_ids[world.acting_enemy_list["Strong Crocodile"]]]))
+ rom.write_bytes(0x10d9af, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!"]]]))
+ rom.write_bytes(0x10d9b3, bytearray([enemy_ids[world.acting_enemy_list["Strong Crocodile"]]]))
+ rom.write_bytes(0x10d9b7, bytearray([enemy_ids[world.acting_enemy_list["Strong Crocodile"]]]))
+ rom.write_bytes(0x10d9bb, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!"]]]))
+ rom.write_bytes(0x10d9bf, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid"]]]))
+ rom.write_bytes(0x10d9c2, bytearray([enemy_ids[world.acting_enemy_list["Rowdy Mouse"]]]))
+ rom.write_bytes(0x10d9c6, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid"]]]))
+ rom.write_bytes(0x10d9c9, bytearray([enemy_ids[world.acting_enemy_list["Attack Slug"]]]))
+ rom.write_bytes(0x10d9cd, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid"]]]))
+ rom.write_bytes(0x10d9d1, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid"]]]))
+ rom.write_bytes(0x10d9d4, bytearray([enemy_ids[world.acting_enemy_list["Rowdy Mouse"]]]))
+ rom.write_bytes(0x10d9d8, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid"]]]))
+ rom.write_bytes(0x10d9dc, bytearray([enemy_ids[world.acting_enemy_list["Rowdy Mouse"]]]))
+ rom.write_bytes(0x10d9e0, bytearray([enemy_ids[world.acting_enemy_list["Rowdy Mouse"]]]))
+ rom.write_bytes(0x10d9e4, bytearray([enemy_ids[world.acting_enemy_list["Attack Slug"]]]))
+ rom.write_bytes(0x10d9e8, bytearray([enemy_ids[world.acting_enemy_list["Attack Slug"]]]))
+ rom.write_bytes(0x10d9ec, bytearray([enemy_ids[world.acting_enemy_list["Mole Playing Rough"]]]))
+ rom.write_bytes(0x10d9f0, bytearray([enemy_ids[world.acting_enemy_list["Mr. Batty"]]]))
+ rom.write_bytes(0x10d9f4, bytearray([enemy_ids[world.acting_enemy_list["Mr. Batty"]]]))
+ rom.write_bytes(0x10d9f7, bytearray([enemy_ids[world.acting_enemy_list["Mole Playing Rough"]]]))
+ rom.write_bytes(0x10d9fb, bytearray([enemy_ids[world.acting_enemy_list["Mr. Batty"]]]))
+ rom.write_bytes(0x10d9fe, bytearray([enemy_ids[world.acting_enemy_list["Mighty Bear"]]]))
+ rom.write_bytes(0x10da02, bytearray([enemy_ids[world.acting_enemy_list["Mr. Batty"]]]))
+ rom.write_bytes(0x10da06, bytearray([enemy_ids[world.acting_enemy_list["Mole Playing Rough"]]]))
+ rom.write_bytes(0x10da0a, bytearray([enemy_ids[world.acting_enemy_list["Mole Playing Rough"]]]))
+ rom.write_bytes(0x10da0d, bytearray([enemy_ids[world.acting_enemy_list["Mighty Bear"]]]))
+ rom.write_bytes(0x10da11, bytearray([enemy_ids[world.acting_enemy_list["Mighty Bear"]]]))
+ rom.write_bytes(0x10da15, bytearray([enemy_ids[world.acting_enemy_list["Mighty Bear"]]]))
+ rom.write_bytes(0x10da18, bytearray([enemy_ids[world.acting_enemy_list["Mr. Batty"]]]))
+ rom.write_bytes(0x10da1c, bytearray([enemy_ids[world.acting_enemy_list["Urban Zombie"]]]))
+ rom.write_bytes(0x10da1f, bytearray([enemy_ids[world.acting_enemy_list["Zombie Dog"]]]))
+ rom.write_bytes(0x10da23, bytearray([enemy_ids[world.acting_enemy_list["Urban Zombie"]]]))
+ rom.write_bytes(0x10da27, bytearray([enemy_ids[world.acting_enemy_list["Zombie Dog"]]]))
+ rom.write_bytes(0x10da2a, bytearray([enemy_ids[world.acting_enemy_list["No Good Fly"]]]))
+ rom.write_bytes(0x10da2e, bytearray([enemy_ids[world.acting_enemy_list["Zombie Possessor"]]]))
+ rom.write_bytes(0x10da32, bytearray([enemy_ids[world.acting_enemy_list["Zombie Possessor"]]]))
+ rom.write_bytes(0x10da35, bytearray([enemy_ids[world.acting_enemy_list["Urban Zombie"]]]))
+ rom.write_bytes(0x10da39, bytearray([enemy_ids[world.acting_enemy_list["Zombie Possessor"]]]))
+ rom.write_bytes(0x10da3c, bytearray([enemy_ids[world.acting_enemy_list["Urban Zombie"]]]))
+ rom.write_bytes(0x10da40, bytearray([enemy_ids[world.acting_enemy_list["Zombie Possessor"]]]))
+ rom.write_bytes(0x10da44, bytearray([enemy_ids[world.acting_enemy_list["Violent Roach"]]]))
+ rom.write_bytes(0x10da48, bytearray([enemy_ids[world.acting_enemy_list["Foppy"]]]))
+ rom.write_bytes(0x10da4c, bytearray([enemy_ids[world.acting_enemy_list["Foppy"]]]))
+ rom.write_bytes(0x10da50, bytearray([enemy_ids[world.acting_enemy_list["Foppy"]]]))
+ rom.write_bytes(0x10da54, bytearray([enemy_ids[world.acting_enemy_list["Foppy"]]]))
+ rom.write_bytes(0x10da58, bytearray([enemy_ids[world.acting_enemy_list["Foppy"]]]))
+ rom.write_bytes(0x10da5c, bytearray([enemy_ids[world.acting_enemy_list["Mostly Bad Fly"]]]))
+ rom.write_bytes(0x10da60, bytearray([enemy_ids[world.acting_enemy_list["Slimy Little Pile"]]]))
+ rom.write_bytes(0x10da63, bytearray([enemy_ids[world.acting_enemy_list["Mostly Bad Fly"]]]))
+ rom.write_bytes(0x10da67, bytearray([enemy_ids[world.acting_enemy_list["Slimy Little Pile"]]]))
+ rom.write_bytes(0x10da6a, bytearray([enemy_ids[world.acting_enemy_list["Mostly Bad Fly"]]]))
+ rom.write_bytes(0x10da6e, bytearray([enemy_ids[world.acting_enemy_list["Slimy Little Pile"]]]))
+ rom.write_bytes(0x10da72, bytearray([enemy_ids[world.acting_enemy_list["Slimy Little Pile"]]]))
+ rom.write_bytes(0x10da76, bytearray([enemy_ids[world.acting_enemy_list["Slimy Little Pile"]]]))
+ rom.write_bytes(0x10da7a, bytearray([enemy_ids[world.acting_enemy_list["Farm Zombie"]]]))
+ rom.write_bytes(0x10da7e, bytearray([enemy_ids[world.acting_enemy_list["Farm Zombie"]]]))
+ rom.write_bytes(0x10da82, bytearray([enemy_ids[world.acting_enemy_list["Struttin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10da85, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10da89, bytearray([enemy_ids[world.acting_enemy_list["Struttin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10da8c, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10da8f, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10da93, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10da97, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10da9a, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10da9e, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10daa1, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10daa5, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10daa8, bytearray([enemy_ids[world.acting_enemy_list["Struttin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10daac, bytearray([enemy_ids[world.acting_enemy_list["Struttin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10daaf, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10dab2, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10dab6, bytearray([enemy_ids[world.acting_enemy_list["Struttin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10dab9, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10dabc, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10dac0, bytearray([enemy_ids[world.acting_enemy_list["Ranboob"]]]))
+ rom.write_bytes(0x10dac4, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10dac7, bytearray([enemy_ids[world.acting_enemy_list["Struttin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10dacb, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10dace, bytearray([enemy_ids[world.acting_enemy_list["Struttin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10dad2, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10dad6, bytearray([enemy_ids[world.acting_enemy_list["Struttin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10dada, bytearray([enemy_ids[world.acting_enemy_list["Gigantic Ant"]]]))
+ rom.write_bytes(0x10dade, bytearray([enemy_ids[world.acting_enemy_list["Gigantic Ant"]]]))
+ rom.write_bytes(0x10dae2, bytearray([enemy_ids[world.acting_enemy_list["Thirsty Coil Snake"]]]))
+ rom.write_bytes(0x10dae5, bytearray([enemy_ids[world.acting_enemy_list["Gigantic Ant"]]]))
+ rom.write_bytes(0x10dae9, bytearray([enemy_ids[world.acting_enemy_list["Thirsty Coil Snake"]]]))
+ rom.write_bytes(0x10daed, bytearray([enemy_ids[world.acting_enemy_list["Thirsty Coil Snake"]]]))
+ rom.write_bytes(0x10daf1, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10daf4, bytearray([enemy_ids[world.acting_enemy_list["Thirsty Coil Snake"]]]))
+ rom.write_bytes(0x10daf8, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10dafc, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10daff, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10db03, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10db06, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10db0a, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10db0d, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10db11, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10db14, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10db18, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10db1b, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10db1f, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10db22, bytearray([enemy_ids[world.acting_enemy_list["Gigantic Ant"]]]))
+ rom.write_bytes(0x10db26, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10db29, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10db2d, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10db30, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10db34, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10db38, bytearray([enemy_ids[world.acting_enemy_list["Noose Man"]]]))
+ rom.write_bytes(0x10db3b, bytearray([enemy_ids[world.acting_enemy_list["Gigantic Ant"]]]))
+ rom.write_bytes(0x10db3f, bytearray([enemy_ids[world.acting_enemy_list["Musica"]]]))
+ rom.write_bytes(0x10db42, bytearray([enemy_ids[world.acting_enemy_list["Mystical Record"]]]))
+ rom.write_bytes(0x10db46, bytearray([enemy_ids[world.acting_enemy_list["Musica"]]]))
+ rom.write_bytes(0x10db49, bytearray([enemy_ids[world.acting_enemy_list["Mystical Record"]]]))
+ rom.write_bytes(0x10db4d, bytearray([enemy_ids[world.acting_enemy_list["Musica"]]]))
+ rom.write_bytes(0x10db51, bytearray([enemy_ids[world.acting_enemy_list["Scalding Coffee Cup"]]]))
+ rom.write_bytes(0x10db54, bytearray([enemy_ids[world.acting_enemy_list["Mystical Record"]]]))
+ rom.write_bytes(0x10db58, bytearray([enemy_ids[world.acting_enemy_list["Scalding Coffee Cup"]]]))
+ rom.write_bytes(0x10db5b, bytearray([enemy_ids[world.acting_enemy_list["Mystical Record"]]]))
+ rom.write_bytes(0x10db5f, bytearray([enemy_ids[world.acting_enemy_list["Scalding Coffee Cup"]]]))
+ rom.write_bytes(0x10db63, bytearray([enemy_ids[world.acting_enemy_list["Mystical Record"]]]))
+ rom.write_bytes(0x10db67, bytearray([enemy_ids[world.acting_enemy_list["Stinky Ghost"]]]))
+ rom.write_bytes(0x10db6b, bytearray([enemy_ids[world.acting_enemy_list["Deadly Mouse"]]]))
+ rom.write_bytes(0x10db6e, bytearray([enemy_ids[world.acting_enemy_list["Stinky Ghost"]]]))
+ rom.write_bytes(0x10db72, bytearray([enemy_ids[world.acting_enemy_list["Filthy Attack Roach"]]]))
+ rom.write_bytes(0x10db76, bytearray([enemy_ids[world.acting_enemy_list["Filthy Attack Roach"]]]))
+ rom.write_bytes(0x10db7a, bytearray([enemy_ids[world.acting_enemy_list["Deadly Mouse"]]]))
+ rom.write_bytes(0x10db7e, bytearray([enemy_ids[world.acting_enemy_list["Deadly Mouse"]]]))
+ rom.write_bytes(0x10db81, bytearray([enemy_ids[world.acting_enemy_list["Stinky Ghost"]]]))
+ rom.write_bytes(0x10db85, bytearray([enemy_ids[world.acting_enemy_list["Stinky Ghost"]]]))
+ rom.write_bytes(0x10db89, bytearray([enemy_ids[world.acting_enemy_list["Deadly Mouse"]]]))
+ rom.write_bytes(0x10db8c, bytearray([enemy_ids[world.acting_enemy_list["Stinky Ghost"]]]))
+ rom.write_bytes(0x10db90, bytearray([enemy_ids[world.acting_enemy_list["Filthy Attack Roach"]]]))
+ rom.write_bytes(0x10db94, bytearray([enemy_ids[world.acting_enemy_list["Filthy Attack Roach"]]]))
+ rom.write_bytes(0x10db98, bytearray([enemy_ids[world.acting_enemy_list["Filthy Attack Roach"]]]))
+ rom.write_bytes(0x10db9c, bytearray([enemy_ids[world.acting_enemy_list["Filthy Attack Roach"]]]))
+ rom.write_bytes(0x10dba0, bytearray([enemy_ids[world.acting_enemy_list["Deadly Mouse"]]]))
+ rom.write_bytes(0x10dba4, bytearray([enemy_ids[world.acting_enemy_list["Deadly Mouse"]]]))
+ rom.write_bytes(0x10dba8, bytearray([enemy_ids[world.acting_enemy_list["Thunder Mite"]]]))
+ rom.write_bytes(0x10dbac, bytearray([enemy_ids[world.acting_enemy_list["Tangoo"]]]))
+ rom.write_bytes(0x10dbb0, bytearray([enemy_ids[world.acting_enemy_list["Thunder Mite"]]]))
+ rom.write_bytes(0x10dbb3, bytearray([enemy_ids[world.acting_enemy_list["Tangoo"]]]))
+ rom.write_bytes(0x10dbb7, bytearray([enemy_ids[world.acting_enemy_list["Tangoo"]]]))
+ rom.write_bytes(0x10dbba, bytearray([enemy_ids[world.acting_enemy_list["Kiss of Death"]]]))
+ rom.write_bytes(0x10dbbe, bytearray([enemy_ids[world.acting_enemy_list["Thunder Mite"]]]))
+ rom.write_bytes(0x10dbc1, bytearray([enemy_ids[world.acting_enemy_list["Tangoo"]]]))
+ rom.write_bytes(0x10dbc5, bytearray([enemy_ids[world.acting_enemy_list["Tangoo"]]]))
+ rom.write_bytes(0x10dbc9, bytearray([enemy_ids[world.acting_enemy_list["Thunder Mite"]]]))
+ rom.write_bytes(0x10dbcd, bytearray([enemy_ids[world.acting_enemy_list["Conducting Menace"]]]))
+ rom.write_bytes(0x10dbd0, bytearray([enemy_ids[world.acting_enemy_list["Tangoo"]]]))
+ rom.write_bytes(0x10dbd4, bytearray([enemy_ids[world.acting_enemy_list["Conducting Menace"]]]))
+ rom.write_bytes(0x10dbd8, bytearray([enemy_ids[world.acting_enemy_list["Kiss of Death"]]]))
+ rom.write_bytes(0x10dbdc, bytearray([enemy_ids[world.acting_enemy_list["Kiss of Death"]]]))
+ rom.write_bytes(0x10dbdf, bytearray([enemy_ids[world.acting_enemy_list["Conducting Menace"]]]))
+ rom.write_bytes(0x10dbe3, bytearray([enemy_ids[world.acting_enemy_list["Conducting Menace"]]]))
+ rom.write_bytes(0x10dbe7, bytearray([enemy_ids[world.acting_enemy_list["Conducting Menace"]]]))
+ rom.write_bytes(0x10dbea, bytearray([enemy_ids[world.acting_enemy_list["Tangoo"]]]))
+ rom.write_bytes(0x10dbee, bytearray([enemy_ids[world.acting_enemy_list["Conducting Menace"]]]))
+ rom.write_bytes(0x10dbf1, bytearray([enemy_ids[world.acting_enemy_list["Tangoo"]]]))
+ rom.write_bytes(0x10dbf4, bytearray([enemy_ids[world.acting_enemy_list["Kiss of Death"]]]))
+ rom.write_bytes(0x10dbf8, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dbfc, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc00, bytearray([enemy_ids[world.acting_enemy_list["Fierce Shattered Man"]]]))
+ rom.write_bytes(0x10dc04, bytearray([enemy_ids[world.acting_enemy_list["Fierce Shattered Man"]]]))
+ rom.write_bytes(0x10dc07, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc0b, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc0f, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc13, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc17, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc1b, bytearray([enemy_ids[world.acting_enemy_list["Petrified Royal Guard"]]]))
+ rom.write_bytes(0x10dc1e, bytearray([enemy_ids[world.acting_enemy_list["Lethal Asp Hieroglyph"]]]))
+ rom.write_bytes(0x10dc22, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc25, bytearray([enemy_ids[world.acting_enemy_list["Guardian Hieroglyph"]]]))
+ rom.write_bytes(0x10dc29, bytearray([enemy_ids[world.acting_enemy_list["Fierce Shattered Man"]]]))
+ rom.write_bytes(0x10dc2d, bytearray([enemy_ids[world.acting_enemy_list["Fierce Shattered Man"]]]))
+ rom.write_bytes(0x10dc30, bytearray([enemy_ids[world.acting_enemy_list["Petrified Royal Guard"]]]))
+ rom.write_bytes(0x10dc34, bytearray([enemy_ids[world.acting_enemy_list["Petrified Royal Guard"]]]))
+ rom.write_bytes(0x10dc38, bytearray([enemy_ids[world.acting_enemy_list["Lethal Asp Hieroglyph"]]]))
+ rom.write_bytes(0x10dc3c, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc40, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc44, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc48, bytearray([enemy_ids[world.acting_enemy_list["Arachnid!!!"]]]))
+ rom.write_bytes(0x10dc4c, bytearray([enemy_ids[world.acting_enemy_list["Fierce Shattered Man"]]]))
+ rom.write_bytes(0x10dc50, bytearray([enemy_ids[world.acting_enemy_list["Fierce Shattered Man"]]]))
+ rom.write_bytes(0x10dc53, bytearray([enemy_ids[world.acting_enemy_list["Petrified Royal Guard"]]]))
+ rom.write_bytes(0x10dc57, bytearray([enemy_ids[world.acting_enemy_list["Petrified Royal Guard"]]]))
+ rom.write_bytes(0x10dc5a, bytearray([enemy_ids[world.acting_enemy_list["Lethal Asp Hieroglyph"]]]))
+ rom.write_bytes(0x10dc5e, bytearray([enemy_ids[world.acting_enemy_list["Fierce Shattered Man"]]]))
+ rom.write_bytes(0x10dc61, bytearray([enemy_ids[world.acting_enemy_list["Guardian Hieroglyph"]]]))
+ rom.write_bytes(0x10dc65, bytearray([enemy_ids[world.acting_enemy_list["Scalding Coffee Cup"]]]))
+ rom.write_bytes(0x10dc68, bytearray([enemy_ids[world.acting_enemy_list["Mystical Record"]]]))
+ rom.write_bytes(0x10dc6b, bytearray([enemy_ids[world.acting_enemy_list["Worthless Protoplasm"]]]))
+ rom.write_bytes(0x10dc6f, bytearray([enemy_ids[world.acting_enemy_list["Cute Li'l UFO"]]]))
+ rom.write_bytes(0x10dc73, bytearray([enemy_ids[world.acting_enemy_list["Cute Li'l UFO"]]]))
+ rom.write_bytes(0x10dc77, bytearray([enemy_ids[world.acting_enemy_list["Dali's Clock"]]]))
+ rom.write_bytes(0x10dc7b, bytearray([enemy_ids[world.acting_enemy_list["Robo-pump"]]]))
+ rom.write_bytes(0x10dc7e, bytearray([enemy_ids[world.acting_enemy_list["Enraged Fire Plug"]]]))
+ rom.write_bytes(0x10dc82, bytearray([enemy_ids[world.acting_enemy_list["Robo-pump"]]]))
+ rom.write_bytes(0x10dc86, bytearray([enemy_ids[world.acting_enemy_list["Enraged Fire Plug"]]]))
+ rom.write_bytes(0x10dc8a, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10dc8e, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10dc92, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10dc95, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dc99, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dc9d, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dca1, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10dca5, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10dca9, bytearray([enemy_ids[world.acting_enemy_list["Lesser Mook"]]]))
+ rom.write_bytes(0x10dcad, bytearray([enemy_ids[world.acting_enemy_list["Worthless Protoplasm"]]]))
+ rom.write_bytes(0x10dcb1, bytearray([enemy_ids[world.acting_enemy_list["Worthless Protoplasm"]]]))
+ rom.write_bytes(0x10dcb5, bytearray([enemy_ids[world.acting_enemy_list["Worthless Protoplasm"]]]))
+ rom.write_bytes(0x10dcb9, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dcbd, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dcc1, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dcc5, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dcc9, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dccd, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dcd1, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dcd5, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dcd8, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dcdc, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dcdf, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dce3, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dce7, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dceb, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dcee, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dcf2, bytearray([enemy_ids[world.acting_enemy_list["Mook Senior"]]]))
+ rom.write_bytes(0x10dcf5, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dcf9, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dcfd, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd00, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dd04, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd07, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dd0b, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10dd0f, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dd12, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10dd16, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10dd19, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd1d, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dd20, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10dd24, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd27, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dd2a, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10dd2e, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd31, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10dd34, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10dd38, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10dd3b, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd3f, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10dd42, bytearray([enemy_ids[world.acting_enemy_list["Military Octobot"]]]))
+ rom.write_bytes(0x10dd46, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd49, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10dd4d, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd50, bytearray([enemy_ids[world.acting_enemy_list["Military Octobot"]]]))
+ rom.write_bytes(0x10dd54, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd57, bytearray([enemy_ids[world.acting_enemy_list["Military Octobot"]]]))
+ rom.write_bytes(0x10dd5b, bytearray([enemy_ids[world.acting_enemy_list["Atomic Power Robot"]]]))
+ rom.write_bytes(0x10dd5e, bytearray([enemy_ids[world.acting_enemy_list["Military Octobot"]]]))
+ rom.write_bytes(0x10dd62, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd66, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd6a, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd6e, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd72, bytearray([enemy_ids[world.acting_enemy_list["Uncontrollable Sphere"]]]))
+ rom.write_bytes(0x10dd75, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd79, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd7d, bytearray([enemy_ids[world.acting_enemy_list["Hyper Spinning Robo"]]]))
+ rom.write_bytes(0x10dd80, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd84, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd88, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd8c, bytearray([enemy_ids[world.acting_enemy_list["Conducting Spirit"]]]))
+ rom.write_bytes(0x10dd8f, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x10dd93, bytearray([enemy_ids[world.acting_enemy_list["Uncontrollable Sphere"]]]))
+ rom.write_bytes(0x10dd96, bytearray([enemy_ids[world.acting_enemy_list["Conducting Spirit"]]]))
+ rom.write_bytes(0x10dd9a, bytearray([enemy_ids[world.acting_enemy_list["Uncontrollable Sphere"]]]))
+ rom.write_bytes(0x10dd9e, bytearray([enemy_ids[world.acting_enemy_list["Conducting Spirit"]]]))
+ rom.write_bytes(0x10dda2, bytearray([enemy_ids[world.acting_enemy_list["Conducting Spirit"]]]))
+ rom.write_bytes(0x10dda6, bytearray([enemy_ids[world.acting_enemy_list["Hyper Spinning Robo"]]]))
+ rom.write_bytes(0x10dda9, bytearray([enemy_ids[world.acting_enemy_list["Conducting Spirit"]]]))
+ rom.write_bytes(0x10ddad, bytearray([enemy_ids[world.acting_enemy_list["Conducting Spirit"]]]))
+ rom.write_bytes(0x10ddb1, bytearray([enemy_ids[world.acting_enemy_list["Uncontrollable Sphere"]]]))
+ rom.write_bytes(0x10ddb4, bytearray([enemy_ids[world.acting_enemy_list["Conducting Spirit"]]]))
+ rom.write_bytes(0x10ddb8, bytearray([enemy_ids[world.acting_enemy_list["Hyper Spinning Robo"]]]))
+ rom.write_bytes(0x10ddbb, bytearray([enemy_ids[world.acting_enemy_list["Conducting Spirit"]]]))
+ rom.write_bytes(0x10ddbf, bytearray([enemy_ids[world.acting_enemy_list["Hyper Spinning Robo"]]]))
+ rom.write_bytes(0x10ddc3, bytearray([enemy_ids[world.acting_enemy_list["Hyper Spinning Robo"]]]))
+ rom.write_bytes(0x10ddc6, bytearray([enemy_ids[world.acting_enemy_list["Uncontrollable Sphere"]]]))
+ rom.write_bytes(0x10ddca, bytearray([enemy_ids[world.acting_enemy_list["Soul Consuming Flame"]]]))
+ rom.write_bytes(0x10ddce, bytearray([enemy_ids[world.acting_enemy_list["Psychic Psycho"]]]))
+ rom.write_bytes(0x10ddd2, bytearray([enemy_ids[world.acting_enemy_list["Evil Elemental"]]]))
+ rom.write_bytes(0x10ddd6, bytearray([enemy_ids[world.acting_enemy_list["Evil Elemental"]]]))
+ rom.write_bytes(0x10ddd9, bytearray([enemy_ids[world.acting_enemy_list["Soul Consuming Flame"]]]))
+ rom.write_bytes(0x10dddd, bytearray([enemy_ids[world.acting_enemy_list["Evil Elemental"]]]))
+ rom.write_bytes(0x10dde0, bytearray([enemy_ids[world.acting_enemy_list["Soul Consuming Flame"]]]))
+ rom.write_bytes(0x10dde4, bytearray([enemy_ids[world.acting_enemy_list["Evil Elemental"]]]))
+ rom.write_bytes(0x10dde8, bytearray([enemy_ids[world.acting_enemy_list["Evil Elemental"]]]))
+ rom.write_bytes(0x10ddeb, bytearray([enemy_ids[world.acting_enemy_list["Soul Consuming Flame"]]]))
+ rom.write_bytes(0x10ddef, bytearray([enemy_ids[world.acting_enemy_list["Soul Consuming Flame"]]]))
+ rom.write_bytes(0x10ddf3, bytearray([enemy_ids[world.acting_enemy_list["Soul Consuming Flame"]]]))
+ rom.write_bytes(0x10ddf7, bytearray([enemy_ids[world.acting_enemy_list["Psychic Psycho"]]]))
+ rom.write_bytes(0x10ddfa, bytearray([enemy_ids[world.acting_enemy_list["Major Psychic Psycho"]]]))
+ rom.write_bytes(0x10ddfe, bytearray([enemy_ids[world.acting_enemy_list["Evil Elemental"]]]))
+ rom.write_bytes(0x10de01, bytearray([enemy_ids[world.acting_enemy_list["Psychic Psycho"]]]))
+ rom.write_bytes(0x10de05, bytearray([enemy_ids[world.acting_enemy_list["Soul Consuming Flame"]]]))
+ rom.write_bytes(0x10de08, bytearray([enemy_ids[world.acting_enemy_list["Major Psychic Psycho"]]]))
+ rom.write_bytes(0x10de0c, bytearray([enemy_ids[world.acting_enemy_list["Soul Consuming Flame"]]]))
+ rom.write_bytes(0x10de0f, bytearray([enemy_ids[world.acting_enemy_list["Major Psychic Psycho"]]]))
+ rom.write_bytes(0x10de13, bytearray([enemy_ids[world.acting_enemy_list["Soul Consuming Flame"]]]))
+ rom.write_bytes(0x10de17, bytearray([enemy_ids[world.acting_enemy_list["Ghost of Starman"]]]))
+ rom.write_bytes(0x10de1a, bytearray([enemy_ids[world.acting_enemy_list["Nuclear Reactor Robot"]]]))
+ rom.write_bytes(0x10de1e, bytearray([enemy_ids[world.acting_enemy_list["Wild 'n Wooly Shambler"]]]))
+ rom.write_bytes(0x10de21, bytearray([enemy_ids[world.acting_enemy_list["Ultimate Octobot"]]]))
+ rom.write_bytes(0x10de25, bytearray([enemy_ids[world.acting_enemy_list["Ultimate Octobot"]]]))
+ rom.write_bytes(0x10de28, bytearray([enemy_ids[world.acting_enemy_list["Nuclear Reactor Robot"]]]))
+ rom.write_bytes(0x10de2c, bytearray([enemy_ids[world.acting_enemy_list["Squatter Demon"]]]))
+ rom.write_bytes(0x10de30, bytearray([enemy_ids[world.acting_enemy_list["Ghost of Starman"]]]))
+ rom.write_bytes(0x10de33, bytearray([enemy_ids[world.acting_enemy_list["Wild 'n Wooly Shambler"]]]))
+ rom.write_bytes(0x10de37, bytearray([enemy_ids[world.acting_enemy_list["Ghost of Starman"]]]))
+ rom.write_bytes(0x10de3a, bytearray([enemy_ids[world.acting_enemy_list["Nuclear Reactor Robot"]]]))
+ rom.write_bytes(0x10de3e, bytearray([enemy_ids[world.acting_enemy_list["Nuclear Reactor Robot"]]]))
+ rom.write_bytes(0x10de41, bytearray([enemy_ids[world.acting_enemy_list["Final Starman"]]]))
+ rom.write_bytes(0x10de45, bytearray([enemy_ids[world.acting_enemy_list["Ghost of Starman"]]]))
+ rom.write_bytes(0x10de48, bytearray([enemy_ids[world.acting_enemy_list["Final Starman"]]]))
+ rom.write_bytes(0x10de4c, bytearray([enemy_ids[world.acting_enemy_list["Ghost of Starman"]]]))
+ rom.write_bytes(0x10de4f, bytearray([enemy_ids[world.acting_enemy_list["Final Starman"]]]))
+ rom.write_bytes(0x10de52, bytearray([enemy_ids[world.acting_enemy_list["Nuclear Reactor Robot"]]]))
+ rom.write_bytes(0x10de56, bytearray([enemy_ids[world.acting_enemy_list["Bionic Kraken"]]]))
+ rom.write_bytes(0x10de5a, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10de5d, bytearray([enemy_ids[world.acting_enemy_list["Ramblin' Evil Mushroom"]]]))
+ rom.write_bytes(0x10de61, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10de65, bytearray([enemy_ids[world.acting_enemy_list["Mobile Sprout"]]]))
+ rom.write_bytes(0x10de69, bytearray([enemy_ids[world.acting_enemy_list["Insane Cultist"]]]))
+ rom.write_bytes(0x10de6d, bytearray([enemy_ids[world.acting_enemy_list["Insane Cultist"]]]))
+ rom.write_bytes(0x10de71, bytearray([enemy_ids[world.acting_enemy_list["Zombie Possessor"]]]))
+ rom.write_bytes(0x10de75, bytearray([enemy_ids[world.acting_enemy_list["Mr. Batty"]]]))
+ rom.write_bytes(0x10de79, bytearray([enemy_ids[world.acting_enemy_list["Mr. Batty"]]]))
+ rom.write_bytes(0x10de7c, bytearray([enemy_ids[world.acting_enemy_list["Zombie Possessor"]]]))
+ rom.write_bytes(0x10de80, bytearray([enemy_ids[world.acting_enemy_list["Skelpion"]]]))
+ rom.write_bytes(0x10de84, bytearray([enemy_ids[world.acting_enemy_list["Mad Taxi"]]]))
+ rom.write_bytes(0x10de88, bytearray([enemy_ids[world.acting_enemy_list["Criminal Caterpillar"]]]))
+ rom.write_bytes(0x10de8c, bytearray([enemy_ids[world.acting_enemy_list["Master Criminal Worm"]]]))
+ rom.write_bytes(0x10de94, bytearray([enemy_ids[world.acting_enemy_list["Coil Snake"]]]))
+ rom.write_bytes(0x10de98, bytearray([enemy_ids[world.acting_enemy_list["Coil Snake"]]]))
+ rom.write_bytes(0x10de9c, bytearray([enemy_ids[world.acting_enemy_list["Mole Playing Rough"]]]))
+ rom.write_bytes(0x10dea0, bytearray([enemy_ids[world.acting_enemy_list["Mole Playing Rough"]]]))
+ rom.write_bytes(0x10dea4, bytearray([enemy_ids[world.acting_enemy_list["Mole Playing Rough"]]]))
+ rom.write_bytes(0x10dea8, bytearray([enemy_ids[world.acting_enemy_list["Skate Punk"]]]))
+ rom.write_bytes(0x10deab, bytearray([enemy_ids[world.acting_enemy_list["Pogo Punk"]]]))
+ rom.write_bytes(0x10deae, bytearray([enemy_ids[world.acting_enemy_list["Yes Man Junior"]]]))
+ rom.write_bytes(0x10deb2, bytearray([enemy_ids[world.acting_enemy_list["Pogo Punk"]]]))
+ rom.write_bytes(0x10deb6, bytearray([enemy_ids[world.acting_enemy_list["Yes Man Junior"]]]))
+ rom.write_bytes(0x10deba, bytearray([enemy_ids[world.acting_enemy_list["Insane Cultist"]]]))
+ rom.write_bytes(0x10debe, bytearray([enemy_ids[world.acting_enemy_list["Unassuming Local Guy"]]]))
+ rom.write_bytes(0x10dec2, bytearray([enemy_ids[world.acting_enemy_list["Insane Cultist"]]]))
+ rom.write_bytes(0x10dec5, bytearray([enemy_ids[world.acting_enemy_list["Spiteful Crow"]]]))
+ rom.write_bytes(0x10dec9, bytearray([enemy_ids[world.acting_enemy_list["Slimy Little Pile"]]]))
+ rom.write_bytes(0x10decd, bytearray([enemy_ids[world.acting_enemy_list["Enraged Fire Plug"]]]))
+ rom.write_bytes(0x10ded1, bytearray([enemy_ids[world.acting_enemy_list["Robo-pump"]]]))
+ rom.write_bytes(0x10ded5, bytearray([enemy_ids[world.acting_enemy_list["Abstract Art"]]]))
+ rom.write_bytes(0x10ded9, bytearray([enemy_ids[world.acting_enemy_list["Sentry Robot"]]]))
+ rom.write_bytes(0x10dedd, bytearray([enemy_ids[world.acting_enemy_list["Shattered Man"]]]))
+ rom.write_bytes(0x10dee1, bytearray([enemy_ids[world.acting_enemy_list["Guardian Hieroglyph"]]]))
+ rom.write_bytes(0x10dee5, bytearray([enemy_ids[world.acting_enemy_list["Lethal Asp Hieroglyph"]]]))
+ rom.write_bytes(0x10dee9, bytearray([enemy_ids[world.acting_enemy_list["Mad Duck"]]]))
+ rom.write_bytes(0x10deed, bytearray([enemy_ids[world.acting_enemy_list["Worthless Protoplasm"]]]))
+ rom.write_bytes(0x10def1, bytearray([enemy_ids[world.acting_enemy_list["Rowdy Mouse"]]]))
+ rom.write_bytes(0x10dF00, bytearray([enemy_ids[world.acting_enemy_list["Black Antoid (2)"]]]))
+ rom.write_bytes(0x10df04, bytearray([enemy_ids[world.acting_enemy_list["Cop"]]]))
+ rom.write_bytes(0x10df1b, bytearray([enemy_ids[world.acting_enemy_list["Slimy Little Pile"]]]))
+ rom.write_bytes(0x10df22, bytearray([enemy_ids[world.acting_enemy_list["Tough Mobile Sprout"]]]))
+ rom.write_bytes(0x10df31, bytearray([enemy_ids[world.acting_enemy_list["Insane Cultist"]]]))
+ rom.write_bytes(0x10df4c, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]]))
+ rom.write_bytes(0x10df4f, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+ rom.write_bytes(0x10df5e, bytearray([enemy_ids[world.acting_enemy_list["Even Slimier Little Pile"]]]))
+
+ # Calls for help
+
+ if not world.options.randomize_enemy_attacks:
+ rom.write_bytes(0x15A601, bytearray([enemy_ids[world.acting_enemy_list["Care Free Bomb"]]])) # Loaded Dice
+ rom.write_bytes(0x15A602, bytearray([enemy_ids[world.acting_enemy_list["Beautiful UFO"]]]))
+ rom.write_bytes(0x15A603, bytearray([enemy_ids[world.acting_enemy_list["High-class UFO"]]]))
+ rom.write_bytes(0x15A604, bytearray([enemy_ids[world.acting_enemy_list["Care Free Bomb"]]]))
+
+ rom.write_bytes(0x15DD73, bytearray([enemy_ids[world.acting_enemy_list["Electro Swoosh"]]])) # Loaded Dice 2
+ rom.write_bytes(0x15DD74, bytearray([enemy_ids[world.acting_enemy_list["Fobby"]]]))
+ rom.write_bytes(0x15DD75, bytearray([enemy_ids[world.acting_enemy_list["Uncontrollable Sphere"]]]))
+ rom.write_bytes(0x15DD76, bytearray([enemy_ids[world.acting_enemy_list["Electro Swoosh"]]]))
+
+ rom.write_bytes(0x15AD5B, bytearray([enemy_ids[world.acting_enemy_list["Yes Man Junior"]]])) # Skate Punk
+ rom.write_bytes(0x15AD5C, bytearray([enemy_ids[world.acting_enemy_list["Pogo Punk"]]]))
+
+ rom.write_bytes(0x15AED2, bytearray([enemy_ids[world.acting_enemy_list["Starman"]]])) # S Super
+
+ rom.write_bytes(0x15B108, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]])) # SDX
+ rom.write_bytes(0x15DA86, bytearray([enemy_ids[world.acting_enemy_list["Starman Super"]]]))
+
+ rom.write_bytes(0x15DC5B, bytearray([enemy_ids[world.acting_enemy_list["Slimy Little Pile"]]])) # Belch
+ rom.write_bytes(0x15B801, bytearray([enemy_ids[world.acting_enemy_list["Slimy Little Pile"]]]))
+
+ rom.write_bytes(0x15DD15, bytearray([enemy_ids[world.acting_enemy_list["Even Slimier Little Pile"]]])) # Barf
+
+ rom.write_bytes(0x0F92F4, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Skate Punk"]]))
+ rom.write_bytes(0x0F9305, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Pogo Punk"]]))
+ rom.write_bytes(0x0F8997, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Yes Man Junior"]]))
+ rom.write_bytes(0x0F89A8, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Pogo Punk"]]))
+ rom.write_bytes(0x0F89B9, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Skate Punk"]]))
+ rom.write_bytes(0x0F89CA, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Pogo Punk"]]))
+ rom.write_bytes(0x0F89DB, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Yes Man Junior"]]))
+ rom.write_bytes(0x0F89FD, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Skate Punk"]]))
+
+ rom.write_bytes(0x0FA2A0, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA2B1, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA2C2, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA2D3, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA2E4, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA2F5, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA306, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA317, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA328, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA339, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA34A, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA35B, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA36C, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA37D, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA38E, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA39F, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA427, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA6BE, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA6CF, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA6E0, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Spiteful Crow"]]))
+ rom.write_bytes(0x0FA4E2, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Unassuming Local Guy"]]))
+ rom.write_bytes(0x0FA3B0, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA3C1, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+ rom.write_bytes(0x0FA3D2, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]]))
+
+ rom.write_bytes(0x0FCE85, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Shattered Man"]]))
+ rom.write_bytes(0x0FCE96, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Shattered Man"]]))
+
+ rom.write_bytes(0x0FB6D0, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Rowdy Mouse"]]))
+ rom.write_bytes(0x0FB6E1, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Rowdy Mouse"]]))
+ rom.write_bytes(0x0FB6AE, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Worthless Protoplasm"]]))
+ rom.write_bytes(0x0FB6BF, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Worthless Protoplasm"]]))
+ rom.write_bytes(0x0FB68C, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Mad Duck"]]))
+ rom.write_bytes(0x0FB69D, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Mad Duck"]]))
+
+ rom.write_bytes(0x0FD716, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD727, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD738, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Lethal Asp Hieroglyph"]]))
+ rom.write_bytes(0x0FD749, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Lethal Asp Hieroglyph"]]))
+ rom.write_bytes(0x0FD75A, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD76B, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD77C, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD78D, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Lethal Asp Hieroglyph"]]))
+ rom.write_bytes(0x0FD79E, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD7AF, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Lethal Asp Hieroglyph"]]))
+ rom.write_bytes(0x0FD7C0, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD7D1, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Lethal Asp Hieroglyph"]]))
+ rom.write_bytes(0x0FD7F3, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD804, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD815, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD826, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Lethal Asp Hieroglyph"]]))
+ rom.write_bytes(0x0FD837, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD848, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD859, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Lethal Asp Hieroglyph"]]))
+ rom.write_bytes(0x0FD86A, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD87B, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD88C, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Guardian Hieroglyph"]]))
+ rom.write_bytes(0x0FD89D, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Lethal Asp Hieroglyph"]]))
+ rom.write_bytes(0x0FD8AE, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Lethal Asp Hieroglyph"]]))
+
+ rom.write_bytes(0x0F8E1B, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Cop"]]))
+ rom.write_bytes(0x0F8E2C, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Cop"]]))
+ rom.write_bytes(0x0F8E5F, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Cop"]]))
+ rom.write_bytes(0x0F8E70, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Cop"]]))
+ rom.write_bytes(0x0F8E81, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Cop"]]))
+ rom.write_bytes(0x0F8E92, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Cop"]]))
+ rom.write_bytes(0x0F8EA3, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Cop"]]))
+ rom.write_bytes(0x0F9316, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Cop"]]))
+ rom.write_bytes(0x0F9327, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Cop"]]))
+
+ rom.write_bytes(0x0FE409, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Robo-pump"]]))
+ rom.write_bytes(0x0FE491, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Robo-pump"]]))
+ rom.write_bytes(0x0FE43C, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Abstract Art"]]))
+
+ rom.write_bytes(0x0FD9F1, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Mad Duck"]]))
+ rom.write_bytes(0x0FDA02, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Mad Duck"]]))
+ rom.write_bytes(0x0FDA13, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Mad Duck"]]))
+ rom.write_bytes(0x0FDA24, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Slimy Little Pile"]]))
+ rom.write_bytes(0x0FDA35, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Slimy Little Pile"]]))
+ rom.write_bytes(0x0FDA46, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Slimy Little Pile"]]))
+ rom.write_bytes(0x0FDA57, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Gruff Goat"]]))
+ rom.write_bytes(0x0FDA68, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Gruff Goat"]]))
+
+ rom.write_bytes(0x0FB736, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Slimy Little Pile"]]))
+ rom.write_bytes(0x0FB747, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Slimy Little Pile"]]))
+ rom.write_bytes(0x0FB758, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Slimy Little Pile"]]))
+ rom.write_bytes(0x0FB769, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Slimy Little Pile"]]))
+ rom.write_bytes(0x0FB77A, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Slimy Little Pile"]]))
+
+ rom.write_bytes(0x0FC3E5, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Sentry Robot"]]))
+ rom.write_bytes(0x0FC3F6, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Sentry Robot"]]))
+ rom.write_bytes(0x0FC407, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Sentry Robot"]]))
+ rom.write_bytes(0x0FC418, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Sentry Robot"]]))
+ rom.write_bytes(0x0FC48F, struct.pack("H", world.enemy_sprites[world.acting_enemy_list["Sentry Robot"]]))
+
+ dungeon_zoo = [
+ "Mad Duck",
+ "Gruff Goat",
+ "Slimy Little Pile"
+ ]
+
+ name_addresses = [
+ 0x330544,
+ 0x330555,
+ 0x330566
+ ]
+
+ desc_addresses = [
+ 0x33054B,
+ 0x33055C,
+ 0x33056D
+ ]
+
+ normal_pointers = {
+ "Mad Duck": 0xC8796C,
+ "Gruff Goat": 0xC879A3,
+ "Slimy Little Pile": 0xC87A31
+ }
+
+ pointer = 0x313000
+
+ if world.enemy_sprites[world.acting_enemy_list["Insane Cultist"]] not in can_walkthrough:
+ rom.write_bytes(0x0983D2, bytearray([0x1f, 0x1e, 0x7A, 0x01, 0x00, 0x00, 0x00, 0x00]))
+ rom.write_bytes(0x098459, bytearray([0x1f, 0x1e, 0x7B, 0x01, 0x00, 0x00, 0x00, 0x00]))
+ rom.write_bytes(0x0984D1, bytearray([0x1f, 0x1e, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00]))
+ rom.write_bytes(0x09855F, bytearray([0x1f, 0x1e, 0x7D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00]))
+ rom.write_bytes(0x0985CE, bytearray([0x1f, 0x1e, 0x7E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00]))
+ rom.write_bytes(0x098625, bytearray([0x1f, 0x1e, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00]))
+ rom.write_bytes(0x09869E, bytearray([0x1f, 0x1e, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00]))
+
+ rom.write_bytes(0x098375, bytearray([0x44]))
+ rom.write_bytes(0x098425, bytearray([0x44]))
+ rom.write_bytes(0x098480, bytearray([0x44]))
+ rom.write_bytes(0x098518, bytearray([0x44]))
+ rom.write_bytes(0x098599, bytearray([0x44]))
+ rom.write_bytes(0x098603, bytearray([0x44]))
+ rom.write_bytes(0x09864C, bytearray([0x44]))
+
+ for i in range(3):
+
+ name = text_encoder(world.acting_enemy_list[dungeon_zoo[i]].replace(" (2)", ""), 255)
+ name.append(0x02)
+
+ if world.acting_enemy_list[dungeon_zoo[i]] not in dungeon_zoo:
+ text = text_encoder(enemy_descriptions[world.acting_enemy_list[dungeon_zoo[i]]], 255)
+ text.append(0x02)
+ else:
+ text = text_encoder("ERROR", 255)
+ rom.write_bytes(pointer, name)
+ rom.write_bytes(name_addresses[i], struct.pack("I", pointer + 0xC00000))
+ pointer += len(name)
+ rom.write_bytes(pointer, text)
+ if world.acting_enemy_list[dungeon_zoo[i]] not in dungeon_zoo:
+ rom.write_bytes(desc_addresses[i], struct.pack("I", pointer + 0xC00000))
+ else:
+ rom.write_bytes(desc_addresses[i], struct.pack("I",
+ normal_pointers[world.acting_enemy_list[dungeon_zoo[i]]]))
+ pointer += len(text)
+ # Todo; action scripts for npc enemies?
diff --git a/worlds/earthbound/modules/equipamizer.py b/worlds/earthbound/modules/equipamizer.py
new file mode 100644
index 000000000000..5c7bdebd7deb
--- /dev/null
+++ b/worlds/earthbound/modules/equipamizer.py
@@ -0,0 +1,994 @@
+from dataclasses import dataclass
+import copy
+from ..game_data.text_data import text_encoder, calc_pixel_width
+from ..Options import Armorizer, Weaponizer
+from operator import attrgetter
+import struct
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from .. import EarthBoundWorld
+ from ..Rom import LocalRom
+
+@dataclass
+class EBArmor:
+ address: int
+ equip_type: str
+ defense: int = 0
+ aux_stat: int = 0
+ poo_def: int = 0
+ flash_res: int = 0
+ freeze_res: int = 0
+ fire_res: int = 0
+ par_res: int = 0
+ sleep_res: int = 0
+ name: str = "None"
+ can_equip: str = "All"
+ total_resistance = 0
+ double_price_item: str = "None"
+
+
+@dataclass
+class EBWeapon:
+ can_equip: str
+ equip_type: str
+ address: int
+ name: str = "None"
+ offense: int = 0
+ aux_stat: int = 0
+ poo_off: int = 0
+ miss_rate: int = 0
+ double_price_item: str = "None"
+
+
+def roll_resistances(world: "EarthBoundWorld", element: str, armor: EBArmor) -> None:
+ chance = world.random.randint(0, 100)
+ if chance < world.options.armorizer_resistance_chance.value:
+ setattr(armor, element, world.random.randint(1, 3))
+ else:
+ setattr(armor, element, 0)
+
+
+def price_weapons(world: "EarthBoundWorld", weapons: list[EBWeapon], rom: "LocalRom") -> None:
+ for index, weapon in enumerate(weapons):
+ if weapon.can_equip == "Poo":
+ price = 10 * weapon.poo_off
+ else:
+ price = 10 * weapon.offense
+
+ if price > 300:
+ price = price * 2
+
+ price += (20 * weapon.aux_stat)
+ price -= (50 * weapon.miss_rate)
+ price += world.random.randint(-20, 20)
+ price = max(5, price)
+ rom.write_bytes((weapon.address + 26), struct.pack("H", price))
+ if weapon.double_price_item in summers_addresses:
+ price = min(0xFFFF, price * 2)
+ rom.write_bytes((summers_addresses[weapon.double_price_item] + 26), struct.pack("H", price))
+
+
+def price_armors(world: "EarthBoundWorld", armor_pricing_list: list[EBArmor], rom: "LocalRom") -> None:
+ for index, armor in enumerate(armor_pricing_list):
+ if armor.can_equip == "Poo":
+ price = 10 * armor.poo_def
+ else:
+ price = 10 * armor.defense
+
+ if price > 300:
+ price = price * 2
+ price += (20 * armor.aux_stat)
+ price += (50 * armor.fire_res)
+ price += (50 * armor.freeze_res)
+ price += (50 * armor.flash_res)
+ price += (50 * armor.par_res)
+ price += world.random.randint(-20, 20)
+ price = max(5, price)
+ rom.write_bytes((armor.address + 26), struct.pack("H", price))
+ if armor.double_price_item in summers_addresses:
+ price = min(0xFFFF, price * 2)
+ rom.write_bytes((summers_addresses[armor.double_price_item] + 26), struct.pack("H", price))
+
+
+def apply_progressive_weapons(world: "EarthBoundWorld", weapons: list[str], progressives: list[str], rom: "LocalRom") -> None:
+ for index, item in enumerate(weapons):
+ weapon = world.weapon_list[item]
+ weapon.offense = progressives[index].offense
+ rom.write_bytes(weapon.address + 31, bytearray([weapon.offense]))
+
+
+def apply_progressive_armor(world: "EarthBoundWorld", armors: list[str], progressives: list[str], rom: "LocalRom") -> None:
+ for index, item in enumerate(armors):
+ armor = world.armor_list[item]
+ armor.defense = progressives[index].defense
+ rom.write_bytes(armor.address + 31, bytearray([armor.defense]))
+
+
+adjectives = [
+ "Hard",
+ "Wild",
+ "Boring",
+ "Lavish",
+ "Grouchy",
+ "Elastic",
+ "Unsightly",
+ "Long",
+ "Wide",
+ "Cheap",
+ "Copper",
+ "Silver",
+ "Gold",
+ "Platinum",
+ "Diamond",
+ "Jade",
+ "Ruby",
+ "Sapphire",
+ "Pearl",
+ "Dull",
+ "Cold",
+ "Fair",
+ "Awful",
+ "Bad",
+ "Dry",
+ "Wet",
+ "Shiny",
+ "Damp",
+ "Elite",
+ "Beefy",
+ "Better",
+ "Alright",
+ "Okay",
+ "Metal",
+ "Pixie's",
+ "Cherub's",
+ "Demon's",
+ "Goddess",
+ "Sprite's",
+ "Fairy's",
+ "Devil's",
+ "Best",
+ "Spiteful",
+ "Travel",
+ "Great",
+ "Crystal",
+ "Baseball",
+ "Holmes",
+ "Red",
+ "Talisman",
+ "Defense",
+ "Mr. Saturn",
+ "Slumber",
+ "Lucky",
+ "Shiny",
+ "Souvenir",
+ "Silence",
+ "Ultimate",
+ "Charm",
+ "Saturn",
+ "Tenda",
+ "Sturdy",
+ "Sleek",
+ "Green",
+ "Blue",
+ "White",
+ "Yellow",
+ "Azure",
+ "Emerald",
+ "Handmade",
+ "Hank's",
+ "Real",
+ "Peace",
+ "Magic",
+ "Protect",
+ "Brass",
+ "Cursed",
+ "Rabbit's",
+ "Odd",
+ "Cheese",
+ "Casual",
+ "Silk",
+ "Gutsy",
+ "Hyper",
+ "Crusher",
+ "Thick",
+ "Deluxe",
+ "Chef's",
+ "Cracked",
+ "Plastic",
+ "Cotton",
+ "Mr. Baseball",
+ "Razor",
+ "Gilded",
+ "Master",
+ "Fighter's",
+ "Worn",
+ "Magicant",
+ "Happy",
+ "Well-Done",
+ "Rare",
+ "Gnarly",
+ "Wicked",
+ "Bionic",
+ "Combat",
+ "Tee ball",
+ "Sand lot",
+ "Minor league",
+ "Big league",
+ "Hall of fame",
+ "Famous",
+ "Legendary",
+ "Casey",
+ "French",
+ "Holy",
+ "Pop",
+ "Zip",
+ "Gaia",
+ "Baddest",
+ "Death",
+ "Spectrum",
+ "Laser",
+ "Moon",
+ "Toy",
+ "Magnum",
+ "Stun",
+ "Trick",
+ "Dirty",
+ "Washed",
+ "Laundered",
+ "Fresh",
+ "New",
+ "Old",
+ "Alien",
+ "T-rex's",
+ "Double",
+ "Non-stick",
+ "Football",
+ "Tennis",
+ "Golf",
+ "Hockey",
+ "Burnt",
+ "Boiled"
+]
+
+char_nums = {
+ "Ness": 0x01,
+ "Paula": 0x02,
+ "Jeff": 0x03,
+ "Poo": 0x04
+}
+
+usage_bytes = {
+ "All": 0x0F,
+ "Ness": 0x01,
+ "Paula": 0x02,
+ "Jeff": 0x04,
+ "Poo": 0x08
+}
+
+type_bytes = {
+ "body": 0x14,
+ "arm": 0x18,
+ "other": 0x1C,
+ "Bash": 0x10,
+ "Shoot": 0x11
+}
+
+summers_addresses = {
+ "Platinum Band": 0x155A5C,
+ "Diamond Band": 0x155A83,
+ "Big League Bat": 0x15535A
+}
+
+royal_names = [
+ "of kings",
+ "of dukes",
+ "of princes",
+ "of barons",
+ "of lords",
+ "of sultans",
+ "of counts",
+ "of England"
+]
+
+
+def randomize_armor(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ if world.options.equipamizer_cap_stats:
+ armor_caps = {
+ "body": 30,
+ "arm": 80,
+ "other": 110
+
+ }
+ else:
+ armor_caps = {
+ "body": 127,
+ "arm": 127,
+ "other": 127
+ }
+
+ other_adjectives = adjectives.copy()
+ arm_adjectives = adjectives.copy()
+ body_adjectives = adjectives.copy()
+ taken_names = []
+
+ armor_dict = {
+ "arm": arm_adjectives,
+ "body": body_adjectives,
+ "other": other_adjectives
+ }
+
+ equalized_names = [
+ "Mild",
+ "Earth",
+ "Sea"
+ ]
+
+ ult_names = [
+ "Day",
+ "Sun",
+ "Star"
+ ]
+
+ elemental_names = {
+ "Flash": [
+ "Dark",
+ "Cloud",
+ "Night"
+ ],
+ "Freeze": [
+ "Puddle",
+ "Drizzle",
+ "Rain"
+ ],
+
+ "Fire": [
+ "Smoke",
+ "Ember",
+ "Flame"
+ ]
+ }
+
+ plain_elemental_names = [
+ "Dark",
+ "Cloud",
+ "Night",
+ "Puddle",
+ "Drizzle",
+ "Rain",
+ "Smoke",
+ "Ember",
+ "Flame",
+ "Mild",
+ "Earth",
+ "Sea",
+ "Day",
+ "Sun",
+ "Star"
+ ]
+
+ all_armor = [
+ "Travel Charm",
+ "Great Charm",
+ "Crystal Charm",
+ "Rabbit's Foot",
+ "Flame Pendant",
+ "Rain Pendant",
+ "Night Pendant",
+ "Sea Pendant",
+ "Star Pendant",
+ "Cloak of Kings",
+ "Cheap Bracelet",
+ "Copper Bracelet",
+ "Silver Bracelet",
+ "Gold Bracelet",
+ "Platinum Band",
+ "Diamond Band",
+ "Pixie's Bracelet",
+ "Cherub's Band",
+ "Goddess Band",
+ "Bracer of Kings",
+ "Baseball Cap",
+ "Mr. Baseball Cap",
+ "Holmes Hat",
+ "Hard Hat",
+ "Coin of Slumber",
+ "Coin of Defense",
+ "Coin of Silence",
+ "Mr. Saturn Coin",
+ "Charm Coin",
+ "Lucky Coin",
+ "Talisman Coin",
+ "Shiny Coin",
+ "Souvenir Coin",
+ "Diadem of Kings",
+ "Earth Pendant",
+ "Saturn Ribbon",
+ "Ribbon",
+ "Red Ribbon",
+ "Defense Ribbon",
+ "Talisman Ribbon",
+ "Goddess Ribbon",
+ ]
+
+ char_armor_names = {
+ "Ness": {
+ "body": "tee",
+ "arm": "mitt",
+ "other": "pack"
+ },
+
+ "Paula": {
+ "body": "dress",
+ "arm": "ring",
+ "other": "ribbon"
+ },
+
+ "Jeff": {
+ "body": "tie",
+ "arm": "watch",
+ "other": "glasses"
+ },
+ }
+
+ aux_stat = {
+ "arm": "Luck",
+ "body": "Speed",
+ "other": "Luck"
+ }
+
+ armor_names = {
+ "body": ["pendant", "charm", "foot", "brooch", "shirt",
+ "amulet", "cloak", "suit", "plate", "vest", "coat", "jersey", "poncho"],
+ "arm": ["bracelet", "band", "bracer", "gauntlet", "sleeve", "glove", "bangle", "armlet", "sweatband"],
+ "other": ["cap", "hat", "coin", "crown", "diadem", "helmet", "mask", "wig", "pants", "jeans", "greaves", "boot"]
+ }
+
+ res_strength = [
+ ", just a little bit",
+ " somewhat",
+ ""
+ ]
+
+ progressive_bracelets = [
+ ]
+
+ progressive_others = [
+ ]
+
+ world.armor_list = {
+ "Travel Charm": EBArmor(0x15583A, "body"),
+ "Great Charm": EBArmor(0x155861, "body"),
+ "Crystal Charm": EBArmor(0x155888, "body"),
+ "Rabbit's Foot": EBArmor(0x1558AF, "body"),
+ "Flame Pendant": EBArmor(0x1558D6, "body"),
+ "Rain Pendant": EBArmor(0x1558FD, "body"),
+ "Night Pendant": EBArmor(0x155924, "body"),
+ "Sea Pendant": EBArmor(0x15594B, "body"),
+ "Star Pendant": EBArmor(0x155972, "body"),
+ "Cloak of Kings": EBArmor(0x155999, "body"),
+ "Cheap Bracelet": EBArmor(0x1559C0, "arm"),
+ "Copper Bracelet": EBArmor(0x1559E7, "arm"),
+ "Silver Bracelet": EBArmor(0x155A0E, "arm"),
+ "Gold Bracelet": EBArmor(0x155A35, "arm"),
+ "Platinum Band": EBArmor(0x1570E8, "arm"),
+ "Diamond Band": EBArmor(0x15710F, "arm"),
+ "Pixie's Bracelet": EBArmor(0x155AAA, "arm"),
+ "Cherub's Band": EBArmor(0x155AD1, "arm"),
+ "Goddess Band": EBArmor(0x155AF8, "arm"),
+ "Bracer of Kings": EBArmor(0x155B1F, "arm"),
+ "Baseball Cap": EBArmor(0x155B46, "other"),
+ "Mr. Baseball Cap": EBArmor(0x155B94, "other"),
+ "Holmes Hat": EBArmor(0x155B6D, "other"),
+ "Hard Hat": EBArmor(0x155BBB, "other"),
+ "Ribbon": EBArmor(0x155BE2, "other"),
+ "Red Ribbon": EBArmor(0x155C09, "other"),
+ "Goddess Ribbon": EBArmor(0x155C30, "other"),
+ "Coin of Slumber": EBArmor(0x155C57, "other"),
+ "Coin of Defense": EBArmor(0x155C7E, "other"),
+ "Coin of Silence": EBArmor(0x1571AB, "other"),
+ "Mr. Saturn Coin": EBArmor(0x1575EF, "other"),
+ "Lucky Coin": EBArmor(0x155CA5, "other"),
+ "Charm Coin": EBArmor(0x1571D2, "other"),
+ "Talisman Coin": EBArmor(0x155CCC, "other"),
+ "Shiny Coin": EBArmor(0x155CF3, "other"),
+ "Souvenir Coin": EBArmor(0x155D1A, "other"),
+ "Diadem of Kings": EBArmor(0x155D41, "other"),
+ "Earth Pendant": EBArmor(0x156D8E, "body"),
+ "Defense Ribbon": EBArmor(0x157136, "other"),
+ "Talisman Ribbon": EBArmor(0x15715D, "other"),
+ "Saturn Ribbon": EBArmor(0x157184, "other"),
+ }
+
+ for item in all_armor:
+ armor = world.armor_list[item]
+ if world.options.armorizer == Armorizer.option_chaos:
+ armor.equip_type = world.random.choice(["arm", "body", "other"])
+
+ if armor.equip_type == "arm":
+ progressive_bracelets.append(item)
+ elif armor.equip_type == "other" and armor.can_equip == "All":
+ progressive_others.append(item)
+
+ if item in summers_addresses:
+ armor.double_price_item = item
+ armor.defense = world.random.randint(1, armor_caps[armor.equip_type])
+
+ chance = world.random.randint(0, 100)
+ if chance < 8:
+ armor.aux_stat = world.random.randint(1, 127)
+ else:
+ armor.aux_stat = 0
+
+ if armor.equip_type != "arm":
+ roll_resistances(world, "flash_res", armor)
+ roll_resistances(world, "freeze_res", armor)
+ roll_resistances(world, "fire_res", armor)
+ roll_resistances(world, "par_res", armor)
+ armor.sleep_res = 0
+ else:
+ armor.flash_res = 0
+ armor.freeze_res = 0
+ armor.fire_res = 0
+ armor.par_res = 0
+ # Only Arm gear can have sleep resistance; arm gear cannot have elemental resistance
+ roll_resistances(world, "sleep_res", armor)
+
+ if armor.flash_res + armor.freeze_res + armor.fire_res == 0:
+ # If no resistances are active use a normal name
+ front_name = world.random.choice(armor_dict[armor.equip_type])
+ armor_dict[armor.equip_type].remove(front_name)
+ elif armor.flash_res == armor.freeze_res == armor.fire_res:
+ # Get a combined name for the level
+ front_name = equalized_names[armor.flash_res - 1]
+ elif armor.par_res == armor.flash_res == armor.freeze_res == armor.fire_res:
+ # Should be used if Paralysis + the others all succeed
+ front_name = ult_names[armor.flash_res - 1]
+ else:
+ # If resistances are inequal, use the strongest as the name and pull a name from its strength
+ # If 2 are equal pick a random of them
+ names = ("Flash", "Freeze", "Fire")
+ strengths = (armor.flash_res, armor.freeze_res, armor.fire_res)
+ best_elements = [(name, strength) for name, strength in zip(names, strengths) if strength == max(strengths)]
+ best_name, best_strength = world.random.choice(best_elements)
+ front_name = elemental_names[best_name][best_strength - 1]
+
+ chance = world.random.randint(0, 100)
+ if chance < 10:
+ armor.can_equip = world.random.choice(["Ness", "Paula", "Jeff", "Poo"])
+ else:
+ armor.can_equip = "All"
+
+ if armor.can_equip == "Poo":
+ back_name = world.random.choice(royal_names)
+ front_name = world.random.choice(armor_names[armor.equip_type]).capitalize()
+ elif armor.can_equip in ["Ness", "Paula", "Jeff"]:
+ back_name = char_armor_names[armor.can_equip][armor.equip_type]
+ else:
+ back_name = world.random.choice(armor_names[armor.equip_type])
+
+ armor.name = front_name + " " + back_name
+ if armor.name in taken_names:
+ front_name = world.random.choice(armor_dict[armor.equip_type])
+ armor_dict[armor.equip_type].remove(front_name)
+ armor.name = front_name + " " + back_name
+
+ pixel_length = calc_pixel_width(armor.name)
+ first_armor = False
+ if armor.can_equip == "Poo":
+ names_to_try = royal_names.copy()
+ else:
+ names_to_try = armor_names[armor.equip_type].copy()
+
+ while pixel_length > 70 or armor.name in taken_names:
+ # First we replace any spaces with half-width spaces, a common tech used in vanilla to fix long names
+ if first_armor is False:
+ armor.name = armor.name.replace(" ", " ")
+ first_armor = True
+ else:
+ if names_to_try and front_name not in plain_elemental_names:
+ # If it's still too long, change the second part of the name to try and roll a shorter name
+ back_name = world.random.choice(names_to_try)
+ names_to_try.remove(back_name)
+ else:
+ # If it's *STILL* too long, chop a letter off the end of the front
+ front_name = front_name[:-1]
+ if front_name == "":
+ # we ran out of letters rip
+ front_name = "Long"
+ first_armor = False
+ armor.name = front_name + " " + back_name
+
+ pixel_length = calc_pixel_width(armor.name)
+
+ taken_names.append(armor.name)
+ armor.total_resistance = (1 * armor.fire_res) + (4 * armor.freeze_res) + (16 * armor.flash_res) + (64 * armor.par_res)
+ rom.write_bytes(armor.address + 28, bytearray([usage_bytes[armor.can_equip]]))
+ rom.write_bytes(armor.address + 25, bytearray([type_bytes[armor.equip_type]]))
+
+ sortable_armor = copy.deepcopy(world.armor_list)
+ sorted_armor = sorted(sortable_armor.values(), key=attrgetter("defense"))
+
+ sorted_arm_gear = [armor for armor in sorted_armor if armor.equip_type == "arm"]
+ sorted_body_gear = [armor for armor in sorted_armor if armor.equip_type == "body"]
+ sorted_other_gear = [armor for armor in sorted_armor if armor.equip_type == "other"]
+
+ sorts = [
+ sorted_arm_gear,
+ sorted_other_gear,
+ sorted_body_gear,
+ ]
+
+ prog_armors = [
+ progressive_bracelets,
+ progressive_others
+ ]
+
+ if world.options.progressive_armor:
+ for i in range(2):
+ apply_progressive_armor(world, prog_armors[i], sorts[i], rom)
+
+ for i in range(3):
+ price_armors(world, sorts[i], rom)
+
+ for item in all_armor:
+ armor = world.armor_list[item]
+
+ if armor.can_equip != "Poo":
+ armor.poo_def = 216 # defense is signed, all non-kings equipment has this value
+ else:
+ armor.poo_def = armor.defense
+
+ rom.write_bytes(armor.address + 31, bytearray([armor.defense, armor.poo_def, armor.aux_stat, armor.total_resistance]))
+
+ item_name = text_encoder(armor.name, 25)
+ item_name.extend([0x00])
+
+ description = f" “{armor.name}”\n"
+ if armor.can_equip != "All":
+ description += f"@♪'s {armor.equip_type} equipment.\n"
+ else:
+ if armor.equip_type == "other":
+ description += "@Must be equipped as “other”.\n"
+ else:
+ description += f"@Must be equipped on your {armor.equip_type}.\n"
+
+ if armor.can_equip == "Poo":
+ description += f"@+{armor.poo_def} Defense.\n"
+ else:
+ description += f"@+{armor.defense} Defense.\n"
+ if armor.aux_stat > 0:
+ description += f"@+{armor.aux_stat} {aux_stat[armor.equip_type]}. \n"
+
+ if armor.flash_res > 0:
+ description += f"@Protects against Flash attacks{res_strength[armor.flash_res - 1]}.\n"
+
+ if armor.freeze_res > 0:
+ description += f"@Protects against Freeze attacks{res_strength[armor.freeze_res - 1]}.\n"
+
+ if armor.fire_res > 0:
+ description += f"@Protects against Fire attacks{res_strength[armor.fire_res - 1]}.\n"
+
+ if armor.par_res > 0:
+ description += f"@Protects against Paralysis{res_strength[armor.par_res - 1]}.\n"
+
+ if armor.sleep_res > 0:
+ description += f"@Protects against Sleep{res_strength[armor.sleep_res - 1]}.\n"
+
+ description = text_encoder(description, 0x100)
+ description = description[:-2]
+ description.extend([0x13, 0x02])
+
+ if armor.can_equip != "All":
+ index = description.index(0xAC)
+ description[index:index + 1] = bytearray([0x1C, 0x02, char_nums[armor.can_equip]])
+
+ rom.write_bytes(armor.address, item_name)
+ rom.write_bytes((0x310000 + world.description_pointer), description)
+ rom.write_bytes((armor.address + 35), struct.pack("I", (0xF10000 + world.description_pointer)))
+ if item in ["Platinum Band", "Diamond Band"]:
+ rom.write_bytes(summers_addresses[item] + 28, bytearray([usage_bytes[armor.can_equip]]))
+ rom.write_bytes(summers_addresses[item] + 31, bytearray([armor.defense, armor.poo_def, armor.aux_stat, armor.total_resistance]))
+ rom.write_bytes(summers_addresses[item] + 25, bytearray([type_bytes[armor.equip_type]]))
+ rom.write_bytes(summers_addresses[item], item_name)
+ rom.write_bytes((summers_addresses[item] + 35), struct.pack("I", (0xF10000 + world.description_pointer)))
+ world.description_pointer += len(description)
+
+
+def randomize_weapons(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ if world.options.equipamizer_cap_stats:
+ weapon_cap = 120
+ else:
+ weapon_cap = 127
+
+ weapon_names = {
+ "Ness": ["bat", "stick", "club", "board", "racket", "cue", "pole", "paddle"],
+ "Paula": ["fry pan", "frypan", "skillet", "whisk", "saucepan", "pin"],
+ "Jeff": ["gun", "beam", "air gun", "beam gun", "cannon", "blaster", "pistol", "revolver", "shotgun", "rifle"],
+ "Poo": ["Sword", "Katana", "Knife", "Scissor", "Cutter", "Blade", "Chisel", "Saw", "Axe", "Scalpel", "Sabre"],
+ "All": ["yo-yo", "slingshot", "boomerang", "chakram", "bow"]
+ }
+
+ taken_names = []
+
+ miss_rates = {
+ "Ness": 1,
+ "Paula": 1,
+ "Jeff": 0,
+ "Poo": 0,
+ "All": 3
+ }
+
+ progressive_bats = [
+ ]
+
+ progressive_pans = [
+ ]
+
+ progressive_guns = [
+ ]
+
+ progressive_alls = [
+ ]
+
+ starting_weapons = {
+ "Ness": "Tee Ball Bat",
+ "Paula": "Fry Pan",
+ "Jeff": "Pop Gun",
+ "Poo": "None"
+ }
+
+ starting_weapon = starting_weapons[world.starting_character]
+
+ world.weapon_list = {
+ "Cracked Bat": EBWeapon("Ness", "Bash", 0x155297),
+ "Tee Ball Bat": EBWeapon("Ness", "Bash", 0x1552BE),
+ "Sand Lot Bat": EBWeapon("Ness", "Bash", 0x1552E5),
+ "Minor League Bat": EBWeapon("Ness", "Bash", 0x15530C),
+ "Mr. Baseball Bat": EBWeapon("Ness", "Bash", 0x155333),
+ "Big League Bat": EBWeapon("Ness", "Bash", 0x157073),
+ "Hall of Fame Bat": EBWeapon("Ness", "Bash", 0x155381),
+ "Magicant Bat": EBWeapon("Ness", "Bash", 0x1553A8),
+ "Legendary Bat": EBWeapon("Ness", "Bash", 0x1553CF),
+ "Gutsy Bat": EBWeapon("Ness", "Bash", 0x1553F6),
+ "Casey Bat": EBWeapon("Ness", "Bash", 0x15541D),
+ "Fry Pan": EBWeapon("Paula", "Bash", 0x155444),
+ "Thick Fry Pan": EBWeapon("Paula", "Bash", 0x15546B),
+ "Deluxe Fry Pan": EBWeapon("Paula", "Bash", 0x155492),
+ "Chef's Fry Pan": EBWeapon("Paula", "Bash", 0x1554B9),
+ "French Fry Pan": EBWeapon("Paula", "Bash", 0x1554E0),
+ "Magic Fry Pan": EBWeapon("Paula", "Bash", 0x155507),
+ "Holy Fry Pan": EBWeapon("Paula", "Bash", 0x15552E),
+ "Sword of Kings": EBWeapon("Poo", "Bash", 0x155555),
+ "Pop Gun": EBWeapon("Jeff", "Shoot", 0x15557C),
+ "Stun Gun": EBWeapon("Jeff", "Shoot", 0x1555A3),
+ "Toy Air Gun": EBWeapon("Jeff", "Shoot", 0x1555CA),
+ "Magnum Air Gun": EBWeapon("Jeff", "Shoot", 0x1555F1),
+ "Zip Gun": EBWeapon("Jeff", "Shoot", 0x155618),
+ "Laser Gun": EBWeapon("Jeff", "Shoot", 0x15563F),
+ "Hyper Beam": EBWeapon("Jeff", "Shoot", 0x155666),
+ "Crusher Beam": EBWeapon("Jeff", "Shoot", 0x15568D),
+ "Spectrum Beam": EBWeapon("Jeff", "Shoot", 0x1556B4),
+ "Death Ray": EBWeapon("Jeff", "Shoot", 0x1556DB),
+ "Baddest Beam": EBWeapon("Jeff", "Shoot", 0x155702),
+ "Moon Beam Gun": EBWeapon("Jeff", "Shoot", 0x155729),
+ "Gaia Beam": EBWeapon("Jeff", "Shoot", 0x155750),
+ "Yo-yo": EBWeapon("All", "Shoot", 0x155777),
+ "Slingshot": EBWeapon("All", "Shoot", 0x15579E),
+ "Bionic Slingshot": EBWeapon("All", "Shoot", 0x1557C5),
+ "Trick Yo-yo": EBWeapon("All", "Shoot", 0x1557EC),
+ "Combat Yo-yo": EBWeapon("All", "Shoot", 0x155813),
+ "T-Rex's Bat": EBWeapon("Ness", "Bash", 0x15704C),
+ "Ultimate Bat": EBWeapon("Ness", "Bash", 0x15709A),
+ "Double Beam": EBWeapon("Jeff", "Shoot", 0x1570C1),
+ "Non-stick Frypan": EBWeapon("Paula", "Bash", 0x1575C8)
+ }
+
+ all_weapons = [
+ "Cracked Bat",
+ "Tee Ball Bat",
+ "Sand Lot Bat",
+ "Minor League Bat",
+ "Mr. Baseball Bat",
+ "T-Rex's Bat",
+ "Big League Bat",
+ "Hall of Fame Bat",
+ "Ultimate Bat",
+ "Casey Bat",
+ "Magicant Bat",
+ "Legendary Bat",
+ "Gutsy Bat",
+
+ "Fry Pan",
+ "Thick Fry Pan",
+ "Deluxe Fry Pan",
+ "Chef's Fry Pan",
+ "Non-stick Frypan",
+ "French Fry Pan",
+ "Holy Fry Pan",
+ "Magic Fry Pan",
+
+ "Sword of Kings",
+ "Pop Gun",
+ "Stun Gun",
+ "Toy Air Gun",
+ "Magnum Air Gun",
+ "Zip Gun",
+ "Laser Gun",
+ "Hyper Beam",
+ "Double Beam",
+ "Crusher Beam",
+ "Spectrum Beam",
+ "Death Ray",
+ "Baddest Beam",
+ "Moon Beam Gun",
+ "Gaia Beam",
+ "Yo-yo",
+ "Slingshot",
+ "Bionic Slingshot",
+ "Trick Yo-yo",
+ "Combat Yo-yo"
+ ]
+
+ for item in all_weapons:
+ weapon = world.weapon_list[item]
+
+ if world.options.weaponizer == Weaponizer.option_chaos:
+ chance = world.random.randint(1, 100)
+ if chance < 8:
+ weapon.can_equip = "All"
+ else:
+ weapon.can_equip = world.random.choice(["Ness", "Paula", "Jeff", "Poo"])
+
+ if item == starting_weapon:
+ weapon.can_equip = world.starting_character
+
+ if item in summers_addresses:
+ weapon.double_price_item = item
+
+ if weapon.can_equip == "Ness":
+ progressive_bats.append(item)
+ elif weapon.can_equip == "Paula":
+ progressive_pans.append(item)
+ elif weapon.can_equip == "Jeff":
+ progressive_guns.append(item)
+
+ if item == starting_weapon and not world.options.progressive_weapons: # Todo; remove not progressive weapons
+ weapon.offense = 10
+ else:
+ if world.options.progressive_weapons:
+ weapon.offense = world.random.randint(10, weapon_cap)
+ else:
+ weapon.offense = world.random.randint(1, weapon_cap)
+
+ if weapon.can_equip == "Poo":
+ front_name = world.random.choice(weapon_names[weapon.can_equip])
+ back_name = world.random.choice(royal_names)
+ else:
+ front_name = world.random.choice(adjectives)
+ back_name = world.random.choice(weapon_names[weapon.can_equip])
+
+ chance = world.random.randint(0, 100)
+ if chance < 8:
+ weapon.aux_stat = world.random.randint(1, 127)
+ else:
+ weapon.aux_stat = 0
+
+ if weapon.can_equip in ["Jeff", "All"]:
+ weapon.equip_type = "Shoot"
+ else:
+ weapon.equip_type = "Bash"
+
+ chance = world.random.randint(1, 100)
+ if chance < 4 and item != starting_weapon:
+ weapon.miss_rate = 12
+ else:
+ weapon.miss_rate = miss_rates[weapon.can_equip]
+
+ weapon.name = front_name + " " + back_name
+
+ pixel_length = calc_pixel_width(weapon.name)
+ half_space = False
+
+ if weapon.can_equip == "Poo":
+ names_to_try = royal_names.copy()
+ else:
+ names_to_try = weapon_names[weapon.can_equip].copy()
+
+ while pixel_length > 70 or weapon.name in taken_names:
+ # First we replace any spaces with half-width spaces, a common tech used in vanilla to fix long names
+ if half_space is False:
+ weapon.name = weapon.name.replace(" ", " ")
+ half_space = True
+ else:
+ if names_to_try:
+ # If it's still too long, change the second part of the name to try and roll a shorter name
+ back_name = world.random.choice(names_to_try)
+ names_to_try.remove(back_name)
+ else:
+ # If it's *STILL* too long, chop a letter off the end of the front
+ front_name = front_name[:-1]
+ if front_name == "":
+ # we ran out of letters rip
+ front_name = "Long"
+ half_space = False
+ weapon.name = front_name + " " + back_name
+ pixel_length = calc_pixel_width(weapon.name)
+ rom.write_bytes(weapon.address + 28, bytearray([usage_bytes[weapon.can_equip]]))
+ rom.write_bytes(weapon.address + 25, bytearray([type_bytes[weapon.equip_type]]))
+ taken_names.append(weapon.name)
+
+ sortable_weapons = copy.deepcopy(world.weapon_list)
+ sorted_weapons = sorted(sortable_weapons.values(), key=attrgetter("offense"))
+
+ sorted_bats = [weapon for weapon in sorted_weapons if weapon.can_equip == "Ness"]
+ sorted_pans = [weapon for weapon in sorted_weapons if weapon.can_equip == "Paula"]
+ sorted_guns = [weapon for weapon in sorted_weapons if weapon.can_equip == "Jeff"]
+ sorted_swords = [weapon for weapon in sorted_weapons if weapon.can_equip == "Poo"]
+ sorted_alls = [weapon for weapon in sorted_weapons if weapon.can_equip == "All"]
+
+ sorts = [
+ sorted_bats,
+ sorted_pans,
+ sorted_guns,
+ sorted_alls,
+ sorted_swords
+ ]
+
+ prog_weapons = [
+ progressive_bats,
+ progressive_pans,
+ progressive_guns,
+ progressive_alls
+ ]
+
+ for i in range(5):
+ price_weapons(world, sorts[i], rom)
+
+ if world.options.progressive_weapons:
+ for i in range(4):
+ apply_progressive_weapons(world, prog_weapons[i], sorts[i], rom)
+
+ for item in all_weapons:
+ weapon = world.weapon_list[item]
+
+ if weapon.can_equip == "Poo":
+ weapon.poo_off = weapon.offense
+ else:
+ weapon.poo_off = 250
+
+ rom.write_bytes(weapon.address + 31, bytearray([
+ weapon.offense, weapon.poo_off, weapon.aux_stat, weapon.miss_rate]))
+
+ item_name = text_encoder(weapon.name, 25)
+ item_name.extend([0x00])
+
+ description = f" “{weapon.name}”\n"
+ if weapon.can_equip != "All":
+ description += f"@♪ can equip this weapon.\n"
+
+ description += f"@+{weapon.offense} Offense.\n"
+ if weapon.aux_stat > 0:
+ description += f"@+{weapon.aux_stat} Guts.\n"
+
+ if weapon.miss_rate == 12:
+ description += "@If you use this, you might just whiff.\n"
+
+ description = text_encoder(description, 0x100)
+ description = description[:-2]
+ description.extend([0x13, 0x02])
+
+ if weapon.can_equip != "All":
+ index = description.index(0xAC)
+ description[index:index + 1] = bytearray([0x1C, 0x02, char_nums[weapon.can_equip]])
+
+ rom.write_bytes(weapon.address, item_name)
+ rom.write_bytes((0x310000 + world.description_pointer), description)
+ rom.write_bytes((weapon.address + 35), struct.pack("I", (0xF10000 + world.description_pointer)))
+ if item == "Big League Bat":
+ rom.write_bytes(summers_addresses[item] + 28, bytearray([usage_bytes[weapon.can_equip]]))
+ rom.write_bytes(summers_addresses[item] + 31, bytearray([weapon.offense, weapon.poo_off, weapon.aux_stat, weapon.miss_rate]))
+ rom.write_bytes(summers_addresses[item] + 25, bytearray([type_bytes[weapon.equip_type]]))
+ rom.write_bytes(summers_addresses[item], item_name)
+ rom.write_bytes((summers_addresses[item] + 35), struct.pack("I", (0xF10000 + world.description_pointer)))
+ world.description_pointer += len(description)
+
diff --git a/worlds/earthbound/modules/flavor_data.py b/worlds/earthbound/modules/flavor_data.py
new file mode 100644
index 000000000000..f04f23558628
--- /dev/null
+++ b/worlds/earthbound/modules/flavor_data.py
@@ -0,0 +1,328 @@
+from ..game_data.text_data import eb_text_table
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from .. import EarthBoundWorld
+
+random_flavors = [
+ "Mint flavor",
+ "Strawberry flavor",
+ "Banana flavor",
+ "Peanut flavor",
+ "Crystal flavor",
+ "Cherry flavor",
+ "Grape flavor",
+ "Blueberry flavor",
+ "Handheld flavor",
+ "Rotten flavor",
+ "Golden flavor",
+ "Herbal flavor",
+ "Cherry Cola flavor",
+ "Salt and Pepper flavor",
+ "Pizza flavor",
+ "Cotton Candy flavor",
+ "Potato flavor",
+ "Lemon-lime flavor",
+ "Raisin flavor",
+ "Chocolate flavor",
+ "Ginger Ale flavor",
+ "Orange Cream flavor",
+ "Choco Cream flavor",
+ "Doomed flavor",
+ "Farm flavor",
+ "Negative flavor",
+ "Astral flavor",
+ "Sunflower flavor",
+ "Encore flavor",
+ "Experimental flavor",
+ "Blueblue flavor",
+ "Deeppurple flavor",
+ "Rivalry flavor",
+ "Sugar flavor",
+ "Faith flavor",
+ "Wisdom flavor",
+ "Hamburger flavor",
+ "Spearmint flavor",
+ "Tuna flavor",
+ "Milk flavor",
+ "Peppermint flavor",
+ "Toothpaste flavor",
+ "Barbecue flavor",
+ "Lavender flavor",
+ "Apple flavor",
+ "Peach flavor",
+ "Bubblegum flavor",
+ "Orange flavor",
+ "Toilet flavor",
+ "Lemonade flavor",
+ "Garlic flavor"
+]
+
+
+flavor_data = {
+ "Cherry flavor": [0x00, 0x00, 0xde, 0x5f, 0x9c, 0x2d, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0xf2, 0x41, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xbf, 0x57, 0xda, 0x28, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xba, 0x49, 0x3f, 0x56, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x44, 0x06, 0x44, 0x2a, 0x80, 0x3b, 0x7f, 0x67, 0xda, 0x28, 0xaa, 0x28],
+
+ "Grape flavor": [0x00, 0x00, 0xde, 0x5f, 0x9c, 0x2d, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0x2f, 0x4a, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xbf, 0x57, 0x13, 0x4c, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xc8, 0x40, 0x2f, 0x55, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x44, 0x30, 0x44, 0x30, 0x80, 0x3b, 0x5f, 0x63, 0xb2, 0x69, 0xc7, 0x28],
+
+ "Blueberry flavor": [0x00, 0x00, 0xde, 0x5f, 0x9c, 0x2d, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0x4d, 0x4a, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xbf, 0x57, 0x86, 0x7d, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x04, 0x41, 0x04, 0x56, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xbd, 0x77, 0x63, 0x30, 0x63, 0x30, 0x80, 0x3b, 0xb9, 0x77, 0xa4, 0x55, 0xc4, 0x28],
+
+ "Handheld flavor": [0x00, 0x00, 0xff, 0x7f, 0xab, 0x29, 0xa3, 0x0c, 0x80, 0x3b, 0xff, 0x77, 0x6f, 0x36, 0xa3, 0x0c,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xd4, 0x4e, 0xeb, 0x25, 0xa3, 0x0c,
+ 0x80, 0x3b, 0xff, 0x77, 0xec, 0x2d, 0x57, 0x5f, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xff, 0x77, 0x89, 0x25, 0xa3, 0x0c, 0x80, 0x3b, 0x50, 0x3e, 0xab, 0x29, 0x47, 0x1d],
+
+ "Rotten flavor": [0x00, 0x00, 0xbf, 0x57, 0x9c, 0x2d, 0x25, 0x14, 0x80, 0x3b, 0xa8, 0x42, 0x84, 0x31, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xbe, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0x16, 0x56, 0x84, 0x18, 0xc7, 0x24, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0x16, 0x56, 0x10, 0x20, 0x10, 0x20, 0x80, 0x3b, 0x18, 0x42, 0x8a, 0x30, 0x44, 0x20],
+
+ "Golden flavor": [0x00, 0x00, 0xbf, 0x57, 0x9c, 0x2d, 0x25, 0x14, 0x80, 0x3b, 0xbf, 0x4b, 0x18, 0x22, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xbe, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0xff, 0x6f, 0x50, 0x08, 0x58, 0x11, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xff, 0x6f, 0x9f, 0x30, 0x9f, 0x30, 0x80, 0x3b, 0xff, 0x63, 0xfd, 0x2a, 0xaa, 0x1c],
+
+ "Herbal flavor": [0x00, 0x00, 0xbf, 0x57, 0x9c, 0x2d, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0x6d, 0x46, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xbe, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0xdd, 0x6f, 0x86, 0x21, 0x69, 0x22, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xdd, 0x6f, 0x48, 0x6e, 0x48, 0x6e, 0x80, 0x3b, 0xba, 0x63, 0xaa, 0x2a, 0x06, 0x29],
+
+ "Cherry Cola flavor": [0x00, 0x00, 0xde, 0x5f, 0x9c, 0x2d, 0x08, 0x14, 0x80, 0x3b, 0x9f, 0x2d, 0x76, 0x24, 0x08, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xbf, 0x57, 0x9f, 0x2d, 0x08, 0x14,
+ 0x80, 0x3b, 0xff, 0x5e, 0x73, 0x04, 0x89, 0x00, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xff, 0x5e, 0x89, 0x00, 0x89, 0x00, 0x80, 0x3b, 0x9f, 0x2d, 0x89, 0x00, 0x08, 0x14],
+
+ "Salt and Pepper flavor": [0x00, 0x00, 0xff, 0x7f, 0x39, 0x67, 0xa5, 0x20, 0x80, 0x3b, 0xff, 0x77, 0x0d, 0x4e, 0xa5, 0x20,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0x51, 0x4e, 0xdd, 0x7f, 0xa5, 0x20,
+ 0x00, 0x00, 0xff, 0x7f, 0x31, 0x52, 0x59, 0x77, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xff, 0x7f, 0x00, 0x00, 0x40, 0x10, 0x80, 0x3b, 0x30, 0x46, 0x6a, 0x2d, 0x83, 0x14],
+
+ "Pizza flavor": [0x00, 0x00, 0xde, 0x5f, 0x39, 0x67, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0x1e, 0x0e, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x00, 0x00, 0x7f, 0x5b, 0xb5, 0x10, 0xbe, 0x2a, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x52, 0x79, 0x52, 0x79, 0x80, 0x3b, 0x7f, 0x5b, 0xbe, 0x2a, 0xc7, 0x28],
+
+ "Cotton Candy flavor": [0x00, 0x00, 0xde, 0x5f, 0x3f, 0x7f, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0x6a, 0x56, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x1b, 0x1c, 0x1b, 0x1c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x00, 0x00, 0x9f, 0x73, 0xf1, 0x62, 0xfe, 0x3d, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x52, 0x79, 0x52, 0x79, 0x80, 0x3b, 0x9f, 0x73, 0xfe, 0x3d, 0xc7, 0x28],
+
+ "Crystal flavor": [0x00, 0x00, 0xde, 0x5f, 0x8c, 0x31, 0x02, 0x28, 0x80, 0x3b, 0xff, 0x77, 0x6d, 0x3a, 0x02, 0x28,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x02, 0x28,
+ 0x80, 0x3b, 0xff, 0x7f, 0x00, 0x40, 0x00, 0x40, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x52, 0x79, 0x52, 0x79, 0x80, 0x3b, 0xad, 0x35, 0x5a, 0x6b, 0xc6, 0x18],
+
+ "Potato flavor": [0x00, 0x00, 0xde, 0x5f, 0x3f, 0x67, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x7b, 0x55, 0x4e, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0x48, 0x10, 0x57, 0x4a, 0x1b, 0x57, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0x48, 0x10, 0x18, 0x19, 0x18, 0x15, 0x80, 0x3b, 0xff, 0x73, 0x1c, 0x57, 0xca, 0x28],
+
+ "Lemon-lime flavor": [0x00, 0x00, 0xde, 0x5f, 0x3f, 0x67, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x5b, 0x9e, 0x17, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x25, 0x0a, 0x25, 0x14,
+ 0x80, 0x3b, 0x25, 0x0a, 0xfd, 0x37, 0xaf, 0x17, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0x25, 0x0a, 0xcf, 0x17, 0xcf, 0x17, 0x80, 0x3b, 0x25, 0x0a, 0xcf, 0x17, 0xc7, 0x28],
+
+ "Raisin flavor": [0x00, 0x00, 0xde, 0x5f, 0x3f, 0x67, 0x25, 0x14, 0x80, 0x3b, 0x94, 0x61, 0x0a, 0x41, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0xfb, 0x6e, 0x44, 0x18, 0xc7, 0x20, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xfb, 0x6e, 0x95, 0x20, 0x95, 0x20, 0x80, 0x3b, 0xfb, 0x6e, 0x0c, 0x55, 0x84, 0x20],
+
+ "Chocolate flavor": [0x00, 0x00, 0xde, 0x5f, 0x8e, 0x09, 0x43, 0x0c, 0x80, 0x3b, 0x37, 0x3a, 0x10, 0x21, 0x43, 0x0c,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x43, 0x0c,
+ 0x80, 0x3b, 0x3c, 0x57, 0x25, 0x0c, 0x8a, 0x10, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0x3c, 0x57, 0x38, 0x32, 0x38, 0x32, 0x80, 0x3b, 0xd2, 0x31, 0x88, 0x14, 0x43, 0x0c],
+
+ "Ginger Ale flavor": [0x00, 0x00, 0xde, 0x5f, 0x8e, 0x09, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x6f, 0x6d, 0x3a, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xbf, 0x57, 0xed, 0x12, 0x25, 0x14,
+ 0x80, 0x3b, 0x9f, 0x57, 0xed, 0x12, 0x80, 0x11, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0x9f, 0x57, 0xb6, 0x09, 0xb6, 0x09, 0x80, 0x3b, 0x9f, 0x57, 0x80, 0x11, 0xe0, 0x00],
+
+ "Orange Cream flavor": [0x00, 0x00, 0xde, 0x5f, 0x8e, 0x09, 0x25, 0x14, 0x80, 0x3b, 0x9f, 0x6b, 0x9f, 0x2a, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xbf, 0x57, 0x5f, 0x12, 0x25, 0x14,
+ 0x80, 0x3b, 0xda, 0x09, 0x3f, 0x53, 0x5f, 0x12, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xda, 0x09, 0x3f, 0x53, 0x3f, 0x53, 0x80, 0x3b, 0x9f, 0x6b, 0x5f, 0x12, 0x89, 0x00],
+
+ "Amethyst flavor": [0x00, 0x00, 0xde, 0x5f, 0x8e, 0x09, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0xf2, 0x41, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xdf, 0x67, 0x3c, 0x78, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x73, 0xaf, 0x40, 0x3c, 0x78, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xde, 0x73, 0x1f, 0x68, 0x1f, 0x68, 0x80, 0x3b, 0x7f, 0x67, 0x3c, 0x78, 0xaf, 0x40],
+
+ "Choco Cream flavor": [0x00, 0x00, 0xde, 0x5f, 0x8e, 0x09, 0x25, 0x14, 0x28, 0x21, 0xee, 0x39, 0x08, 0x25, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xdf, 0x67, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0xc8, 0x28, 0x18, 0x6b, 0xdf, 0x77, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xc8, 0x28, 0x70, 0x4b, 0x70, 0x4b, 0x80, 0x3b, 0xdf, 0x7b, 0xf8, 0x5e, 0x06, 0x29],
+
+ "Doomed flavor": [0x00, 0x00, 0x1f, 0x00, 0x8e, 0x09, 0x25, 0x14, 0x00, 0x00, 0x19, 0x00, 0x0a, 0x00, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xdf, 0x67, 0x1f, 0x00, 0x25, 0x14,
+ 0x80, 0x3b, 0x1f, 0x00, 0x4a, 0x29, 0x2a, 0x29, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0x19, 0x18, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3b, 0xef, 0x3d, 0x6b, 0x2d, 0x84, 0x10],
+
+ "Farm flavor": [0x00, 0x00, 0xbd, 0x32, 0x8e, 0x09, 0x4a, 0x04, 0x00, 0x00, 0xde, 0x32, 0x1a, 0x1e, 0x4a, 0x04,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xdf, 0x67, 0x7f, 0x02, 0x4a, 0x04,
+ 0x80, 0x3b, 0x4a, 0x00, 0x1f, 0x3b, 0x9d, 0x2e, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0x5f, 0x43, 0xbb, 0x3a, 0xbb, 0x3a, 0x80, 0x3b, 0x5e, 0x02, 0x36, 0x01, 0xf0, 0x04],
+
+ "Inverted flavor": [0x00, 0x00, 0x00, 0x00, 0x8e, 0x09, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xff, 0x7f, 0x00, 0x00, 0xff, 0x7f,
+ 0x80, 0x3b, 0x00, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0x80, 0x3b, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x00],
+
+ "Negative flavor": [0x00, 0x00, 0xff, 0x7f, 0x8e, 0x09, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x00,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, 0x00,
+ 0x80, 0x3b, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3b, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00],
+
+ "Astral flavor": [0x00, 0x00, 0x5b, 0x7f, 0x8e, 0x09, 0x21, 0x14, 0x00, 0x00, 0xff, 0x77, 0xed, 0x58, 0x21, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xfa, 0x7a, 0x92, 0x6c, 0x21, 0x14,
+ 0x80, 0x3b, 0x5b, 0x7f, 0xed, 0x58, 0x87, 0x38, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0x5b, 0x7f, 0xd3, 0x79, 0xd3, 0x79, 0x80, 0x3b, 0xfa, 0x7a, 0xa8, 0x38, 0x21, 0x1c],
+
+ "Sunflower flavor": [0x00, 0x00, 0xdf, 0x63, 0x8e, 0x09, 0x65, 0x04, 0x00, 0x00, 0xbf, 0x57, 0xb3, 0x15, 0x65, 0x04,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xdf, 0x57, 0x9a, 0x12, 0x65, 0x04,
+ 0x80, 0x3b, 0xdf, 0x63, 0x1c, 0x37, 0xdf, 0x37, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xdf, 0x63, 0x70, 0x09, 0x70, 0x09, 0x80, 0x3b, 0xdf, 0x57, 0xdb, 0x26, 0x4e, 0x15],
+
+ "Encore flavor": [0x00, 0x00, 0xff, 0x7f, 0x73, 0x4e, 0x84, 0x1c, 0x00, 0x00, 0xbd, 0x7f, 0x8b, 0x45, 0x84, 0x1c,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0x5f, 0x6f, 0x4a, 0x59, 0x84, 0x1c,
+ 0x80, 0x3b, 0xff, 0x7f, 0x12, 0x7e, 0xbf, 0x27, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xff, 0x7f, 0x7f, 0x3c, 0x7f, 0x3c, 0x80, 0x3b, 0x5f, 0x6f, 0x7e, 0x40, 0x31, 0x1c],
+
+ "Experimental flavor": [0x00, 0x00, 0x9e, 0x5f, 0x80, 0x65, 0x64, 0x08, 0x00, 0x00, 0xff, 0x77, 0x2c, 0x15, 0x64, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0x35, 0x7f, 0x0a, 0x7a, 0x64, 0x08,
+ 0x80, 0x3b, 0x9e, 0x5f, 0x50, 0x19, 0xec, 0x10, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0x9e, 0x5f, 0x4a, 0x7a, 0x4a, 0x7a, 0x80, 0x3b, 0x35, 0x7f, 0x47, 0x76, 0xe4, 0x38],
+
+ "Blueblue flavor": [0x00, 0x00, 0x97, 0x7b, 0x00, 0x4c, 0x21, 0x14, 0x00, 0x00, 0x0c, 0x66, 0xc3, 0x28, 0x21, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0x08, 0x6e, 0x24, 0x55, 0x21, 0x14,
+ 0x80, 0x3b, 0x97, 0x7b, 0x82, 0x20, 0x62, 0x45, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0x97, 0x7b, 0x27, 0x71, 0x27, 0x71, 0x80, 0x3b, 0x08, 0x6e, 0x43, 0x49, 0x82, 0x1c],
+
+ "Deeppurple flavor": [0x00, 0x00, 0xfb, 0x7a, 0x13, 0x4c, 0x25, 0x10, 0x00, 0x00, 0x16, 0x69, 0x8b, 0x40, 0x25, 0x10,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0xeb, 0x33, 0x73, 0x70, 0x2b, 0x44, 0x21, 0x14,
+ 0x80, 0x3b, 0xfb, 0x7a, 0x29, 0x30, 0x4c, 0x44, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xfb, 0x7a, 0x17, 0x6c, 0x17, 0x6c, 0x80, 0x3b, 0x73, 0x70, 0x6c, 0x48, 0x45, 0x1c],
+
+ "Rivalry flavor": [0x00, 0x00, 0xfe, 0x5e, 0x0c, 0x7c, 0x25, 0x04, 0x00, 0x00, 0x5e, 0x1d, 0x70, 0x0c, 0x25, 0x04,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0xeb, 0x33, 0x15, 0x7f, 0xb3, 0x14, 0x25, 0x04,
+ 0x80, 0x3b, 0xfe, 0x5e, 0xea, 0x59, 0x6d, 0x6a, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xfe, 0x5e, 0xc0, 0x6d, 0xc0, 0x6d, 0x80, 0x3b, 0x15, 0x7f, 0x86, 0x55, 0xe4, 0x34],
+
+ "Sugar flavor": [0x00, 0x00, 0x9c, 0x73, 0x39, 0x67, 0x63, 0x0c, 0x00, 0x00, 0x9c, 0x73, 0x52, 0x4a, 0x63, 0x0c,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0xeb, 0x33, 0x9c, 0x73, 0x4a, 0x29, 0x63, 0x0c,
+ 0x80, 0x3b, 0x42, 0x08, 0xff, 0x7f, 0x39, 0x67, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0x9c, 0x73, 0x42, 0x08, 0x42, 0x08, 0xe0, 0x33, 0x9c, 0x73, 0xf7, 0x5e, 0x8c, 0x31],
+
+ "Faith flavor": [0x00, 0x00, 0xfe, 0x77, 0x3f, 0x67, 0x45, 0x10, 0x00, 0x00, 0xbe, 0x63, 0x7b, 0x3e, 0x45, 0x10,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0xeb, 0x33, 0x5d, 0x43, 0x9f, 0x40, 0x45, 0x10,
+ 0xec, 0x33, 0xfe, 0x77, 0xdd, 0x72, 0x3c, 0x66, 0x80, 0x3b, 0x4a, 0x29, 0x08, 0x21, 0xa5, 0x14,
+ 0x00, 0x00, 0xdf, 0x77, 0x1d, 0x28, 0x1d, 0x28, 0xe0, 0x33, 0xdf, 0x6a, 0x9f, 0x51, 0x4e, 0x1c],
+
+ "Wisdom flavor": [0x00, 0x00, 0xfe, 0x77, 0x80, 0x19, 0x40, 0x04, 0x00, 0x00, 0xb3, 0x46, 0x89, 0x1d, 0x40, 0x04,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0xeb, 0x33, 0xb3, 0x46, 0x20, 0x1d, 0x40, 0x04,
+ 0xec, 0x33, 0xc1, 0x10, 0x24, 0x2e, 0x80, 0x25, 0x80, 0x3b, 0x4a, 0x29, 0x08, 0x21, 0xa5, 0x14,
+ 0x00, 0x00, 0xfe, 0x77, 0x29, 0x25, 0x29, 0x25, 0xe0, 0x33, 0xcc, 0x46, 0x24, 0x2e, 0x20, 0x11],
+
+ "Hamburger flavor": [0x00, 0x00, 0xbf, 0x6b, 0x3f, 0x67, 0x25, 0x14, 0x80, 0x3b, 0xdc, 0x36, 0xd1, 0x19, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x6b, 0xfd, 0x1a, 0x25, 0x14,
+ 0x80, 0x3b, 0x00, 0x00, 0x54, 0x08, 0x9f, 0x2f, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xff, 0x77, 0x0d, 0x1b, 0x0d, 0x1b, 0x80, 0x3b, 0x50, 0x11, 0x16, 0x2e, 0x85, 0x10],
+
+ "Spearmint flavor": [0x00, 0x00, 0xde, 0x5f, 0x07, 0x1f, 0x25, 0x14, 0x00, 0x00, 0xff, 0x77, 0x8f, 0x3e, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xbf, 0x57, 0x4e, 0x7d, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xa6, 0x1e, 0x6e, 0x3f, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xde, 0x5f, 0x50, 0x71, 0x50, 0x71, 0x80, 0x3b, 0xf9, 0x67, 0x4d, 0x3b, 0x88, 0x24],
+
+ "Tuna flavor": [0x00, 0x00, 0xde, 0x5f, 0x07, 0x1f, 0x25, 0x14, 0x00, 0x00, 0xff, 0x77, 0x93, 0x42, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xbf, 0x57, 0x4e, 0x7d, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0x58, 0x29, 0x14, 0x21, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xde, 0x5f, 0x52, 0x79, 0x52, 0x79, 0x80, 0x3b, 0x1f, 0x63, 0x9b, 0x31, 0x68, 0x14],
+
+ "Milk flavor": [0x00, 0x00, 0xde, 0x5f, 0x07, 0x1f, 0x25, 0x14, 0x00, 0x00, 0xff, 0x77, 0x73, 0x4e, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xbf, 0x57, 0x4e, 0x7d, 0x25, 0x14,
+ 0x80, 0x3b, 0x42, 0x08, 0xb5, 0x4e, 0x7b, 0x67, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xde, 0x5f, 0x52, 0x79, 0x52, 0x79, 0x80, 0x3b, 0xbd, 0x77, 0xf7, 0x5e, 0xc7, 0x28],
+
+ "Peppermint flavor": [0x00, 0x00, 0xfe, 0x73, 0xde, 0x5f, 0x25, 0x14, 0x45, 0x1a, 0x7f, 0x4e, 0xd9, 0x00, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xff, 0x7f, 0xd9, 0x00, 0x25, 0x14,
+ 0x80, 0x3b, 0x42, 0x08, 0xff, 0x7f, 0xd9, 0x00, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xff, 0x7f, 0x13, 0x18, 0x13, 0x00, 0x80, 0x3b, 0xff, 0x7f, 0xdf, 0x18, 0x68, 0x14],
+
+ "Toothpaste flavor": [0x00, 0x00, 0xfe, 0x73, 0xde, 0x5f, 0x25, 0x14, 0x45, 0x1a, 0xf3, 0x7f, 0x20, 0x7f, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xff, 0x7f, 0x20, 0x7f, 0x25, 0x14,
+ 0x80, 0x3b, 0x42, 0x08, 0xec, 0x7f, 0x26, 0x7f, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xff, 0x7f, 0x66, 0x7e, 0x66, 0x7e, 0x80, 0x3b, 0xf3, 0x7f, 0x20, 0x67, 0x68, 0x14],
+
+ "Barbecue flavor": [0x00, 0x00, 0xde, 0x5f, 0x4c, 0x00, 0x06, 0x00, 0x45, 0x1a, 0xd3, 0x00, 0x4c, 0x00, 0x06, 0x00,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xd3, 0x00, 0x0c, 0x00, 0x06, 0x00,
+ 0x80, 0x3b, 0xff, 0x7f, 0x0c, 0x00, 0x99, 0x01, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0x7f, 0x02, 0x4c, 0x00, 0x4c, 0x00, 0x80, 0x3b, 0xb0, 0x14, 0x4c, 0x00, 0x06, 0x00],
+
+ "Lavender flavor": [0x00, 0x00, 0xde, 0x5f, 0x39, 0x7f, 0x25, 0x14, 0x45, 0x1a, 0xff, 0x77, 0x93, 0x3e, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x00, 0x00, 0xbf, 0x57, 0x79, 0x7e, 0x25, 0x14,
+ 0x80, 0x3b, 0x86, 0x10, 0x39, 0x7f, 0x3f, 0x7f, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x00, 0x00, 0xde, 0x5f, 0x73, 0x7e, 0x73, 0x7e, 0x80, 0x3b, 0x3f, 0x7f, 0x39, 0x7f, 0x25, 0x14],
+
+ "Apple flavor": [0x00, 0x00, 0xde, 0x5f, 0x1F, 0x00, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0x6d, 0x3a, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xdf, 0x18, 0x19, 0x00, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x52, 0x79, 0x52, 0x79, 0x80, 0x3b, 0x3f, 0x67, 0xdf, 0x18, 0xc7, 0x28],
+
+ "Orange flavor": [0x00, 0x00, 0xde, 0x5f, 0x9F, 0x01, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0x6d, 0x3a, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0x0c, 0x00, 0x9f, 0x19, 0x7f, 0x1a, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x79, 0x02, 0x79, 0x02, 0x80, 0x3b, 0x3f, 0x67, 0xff, 0x2a, 0xc7, 0x28],
+
+ "Toilet flavor": [0x00, 0x00, 0xde, 0x5f, 0xff, 0x7f, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x67, 0x93, 0x01, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0x32, 0x01, 0x33, 0x7f, 0x66, 0x7e, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0x8c, 0x01, 0x3f, 0x1b, 0x3f, 0x1b, 0x80, 0x3b, 0xff, 0x7f, 0x9c, 0x7f, 0xc7, 0x28],
+
+ "Peach flavor": [0x00, 0x00, 0xde, 0x5f, 0x3f, 0x1b, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x77, 0x6d, 0x3a, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0xc6, 0x00, 0x7f, 0x32, 0x3f, 0x33, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x52, 0x79, 0x52, 0x79, 0x80, 0x3b, 0x3f, 0x4f, 0x7f, 0x32, 0xc7, 0x28],
+
+ "Bubblegum flavor": [0x00, 0x00, 0xde, 0x5f, 0x3f, 0x67, 0x25, 0x14, 0x80, 0x3b, 0x3f, 0x67, 0x7f, 0x7e, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xbf, 0x57, 0x4f, 0x79, 0x25, 0x14,
+ 0x80, 0x3b, 0x86, 0x10, 0x3f, 0x7f, 0x79, 0x7e, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0x86, 0x10, 0x9f, 0x65, 0x9f, 0x65, 0x80, 0x3b, 0x3f, 0x7f, 0x7f, 0x7e, 0xc7, 0x28],
+
+ "Lemonade flavor": [0x00, 0x00, 0xde, 0x5f, 0x3f, 0x67, 0x25, 0x14, 0x80, 0x3b, 0xde, 0x63, 0x1e, 0x5f, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0x3f, 0x67, 0x4e, 0x7d, 0x25, 0x14,
+ 0x80, 0x3b, 0xc6, 0x00, 0x1e, 0x5f, 0xff, 0x4f, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x52, 0x79, 0x52, 0x79, 0x80, 0x3b, 0x9e, 0x5f, 0x1e, 0x5f, 0xc7, 0x28],
+
+ "Garlic flavor": [0x00, 0x00, 0xff, 0x03, 0xff, 0x03, 0x25, 0x14, 0x80, 0x3b, 0xff, 0x33, 0x19, 0x64, 0x25, 0x14,
+ 0x80, 0x3b, 0xde, 0x5f, 0xbf, 0x4c, 0xbf, 0x4c, 0x80, 0x3b, 0xff, 0x1b, 0x13, 0x4c, 0x25, 0x14,
+ 0x80, 0x3b, 0xc6, 0x00, 0x79, 0x02, 0x3f, 0x1b, 0x80, 0x3b, 0xe7, 0x1c, 0xa5, 0x14, 0x42, 0x08,
+ 0x80, 0x3b, 0xde, 0x5f, 0x13, 0x4c, 0x13, 0x4c, 0x80, 0x3b, 0xff, 0x33, 0x19, 0x64, 0xc7, 0x28]
+
+}
+
+vanilla_flavor_pointers = {
+ "Mint flavor": [0x202008, 0x34B000, 0x34B110],
+ "Strawberry flavor": [0x202048, 0x34B040, 0x34B112],
+ "Banana flavor": [0x202088, 0x34B080, 0x34B114],
+ "Peanut flavor": [0x2020C8, 0x34B0C0, 0x34B116]
+}
+
+
+def create_flavors(world: "EarthBoundWorld") -> None:
+ """Shuffle flavors (textbox palettes). Vanilla flavors are copied from the ROM itself and not stored here."""
+ world.flavor_text = []
+ world.flavor_pointer = [0x01F72B, 0x01F746, 0x01F761, 0x01F77C]
+ flavor_num = 0
+ for flavor in world.available_flavors:
+ encoded_flavor = []
+ for char in flavor:
+ encoded_flavor.extend(eb_text_table[char])
+ encoded_flavor.extend([0x00])
+ world.flavor_text.append(encoded_flavor)
+ flavor_num += 1
diff --git a/worlds/earthbound/modules/hint_data.py b/worlds/earthbound/modules/hint_data.py
new file mode 100644
index 000000000000..033894c2209a
--- /dev/null
+++ b/worlds/earthbound/modules/hint_data.py
@@ -0,0 +1,365 @@
+from ..game_data.local_data import item_id_table, character_item_table, party_id_nums
+from ..game_data.text_data import text_encoder
+from ..game_data.static_location_data import location_groups
+from ..modules.shopsanity import shop_locations
+from ..Options import ShopRandomizer, MagicantMode
+import struct
+from BaseClasses import Location
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from .. import EarthBoundWorld
+ from ..Rom import LocalRom
+
+def setup_hints(world: "EarthBoundWorld") -> None:
+ hint_types = [
+ # gives a hint for a specific out of the way location in this player's world, regardless of what item it is
+ "item_at_location",
+ "region_progression_check", # woth or foolish hint, checks specific location groups of this world so as to be more helpful.
+ "hint_for_good_item", # gives the exact location and sender of a good item for the local player
+ "item_in_local_region", # Hints a random item that can be found in a specific local location group
+ "prog_item_at_region", # Hints the region that a good item can be found for this player
+ "joke_hint", # Doesn't hint anything
+ "dungeon_location" # Hints what dungeon can be found at a specific entrance
+ ]
+ world.in_game_hint_types = []
+ world.hinted_locations = {}
+ world.hinted_items = {}
+ world.hinted_regions = {}
+ world.hinted_dungeons = {}
+
+ # may not need to be world.
+ world.local_hintable_locations = [
+ "Onett - Mayor Pirkle",
+ "Onett - South Road Present",
+ "Onett - Meteor Item",
+ "Onett - Treehouse Guy",
+ "Twoson - Orange Kid Donation",
+ "Twoson - Everdred Meeting",
+ "Twoson - Apple Kid Invention",
+ "Fourside - Post-Moonside Delivery",
+ "Lost Underworld - Talking Rock",
+ "Dungeon Man - 2F Hole Present",
+ "Poo - Starting Item",
+ "Summers - Magic Cake",
+ "Deep Darkness - Teleporting Monkey",
+ "Twoson - Insignificant Location",
+ "Peaceful Rest Valley - North Side Present",
+ "Twoson - Paula's Mother",
+ "Happy-Happy Village - Prisoner",
+ "Threed - Boogey Tent Trashcan",
+ "Threed - Zombie Prisoner",
+ "Grapefruit Falls - Saturn Cave Present",
+ "Saturn Valley - Saturn Coffee",
+ "Dusty Dunes - South Side Present",
+ "Stonehenge - Tony Item",
+ "Stonehenge - Kidnapped Mr. Saturn",
+ "Stonehenge - Dead End Present",
+ "Stonehenge - Near End of the Maze Present",
+ "Stonehenge - Bridge Room East Balcony Present",
+ "Gold Mine - B1F Isolated Present",
+ "Fourside - Venus Gift",
+ "Fourside - Bakery 2F Gift",
+ "Fourside - Department Store Blackout",
+ "Monotoli Building - Monotoli Character",
+ "Dungeon Man - 1F Exit Ledge Present",
+ "Deep Darkness - Barf Character",
+ "Lumine Hall - B1F West Alcove Present",
+ "Cave of the Present - Star Master",
+ "Cave of the Present - Broken Phase Distorter",
+ "Fire Spring - 1st Cave Present",
+ "Tenda Village - Tenda Tea",
+ "Deep Darkness - Barf Character",
+ "Dalaam - Trial of Mu",
+ "Pyramid - Northwest Door Sarcophagus"
+ ]
+
+ world.local_hintable_items = [
+ "Franklin Badge",
+ "Key to the Shack",
+ "Key to the Cabin",
+ "Key to the Tower",
+ "Key to the Locker",
+ "Bad Key Machine",
+ "Pencil Eraser",
+ "Eraser Eraser",
+ "UFO Engine",
+ "Yogurt Dispenser",
+ "Zombie Paper",
+ "King Banana",
+ "Signed Banana",
+ "Tendakraut",
+ "Jar of Fly Honey",
+ "Wad of Bills",
+ "Tiny Ruby",
+ "Diamond",
+ "Meteorite Piece",
+ "Hieroglyph Copy",
+ "Piggy Nose",
+ "Carrot Key",
+ "Police Badge",
+ "Letter For Tony",
+ "Mining Permit",
+ "Contact Lens",
+ "Insignificant Item",
+ "Pak of Bubble Gum",
+ "Sea Pendant",
+ "Shyness Book",
+ "Hawk Eye",
+ "Ness",
+ "Paula",
+ "Jeff",
+ "Poo",
+ "Onett Teleport",
+ "Twoson Teleport",
+ "Happy-Happy Village Teleport",
+ "Threed Teleport",
+ "Saturn Valley Teleport",
+ "Dusty Dunes Teleport",
+ "Fourside Teleport",
+ "Winters Teleport",
+ "Summers Teleport",
+ "Scaraba Teleport",
+ "Dalaam Teleport",
+ "Deep Darkness Teleport",
+ "Tenda Village Teleport",
+ "Lost Underworld Teleport"
+ ]
+
+ world.joke_hints = [
+ "you can find 6 hint shops around the world.",
+ "if you want to hint for an item, use !hint in your text client!",
+ "the mouse at the Fourside Department Store knows what the Spook has.",
+ "my business may be a scam.",
+ "there's a guy near Threed's hotel who saw the Zombies take someone away.",
+ "you can use the Y button to run or mash through text.",
+ "you can submit custom window flavors in the game's Archipelago thread.",
+ "you can buy a ruler at the Fourside Department Store.",
+ "you can store a lot of items in your backpack with the R button.",
+ "Giygas's guard may actually be from another seed...",
+ "if you have multiple of something important, you can throw away the extras.",
+ "the chicken crossed the road because it was trying to use Teleport α.",
+ "Figment's Ice Palace can probably be found in Ice Palace.",
+ "you can talk to the hint shop owners for a hint.",
+ "deliveryman are bad at using keys.",
+ "PK scramble is a pretty good time.",
+ "Apple Kid researched the Power of the Earth with Dr. Andonuts.",
+ "you can find beta Archipelago games on the Archipelago discord.",
+ "you can randomize EarthBound with Archipelago.",
+ "hint prices double with each one bought.",
+ "you probably should have kept your money.",
+ "there's a secret option to plando Lumine Hall's text.",
+ "this isn't a very good hint.",
+ "some hints are good hints.\n@But not this one."
+ ]
+
+ hintable_location_groups = location_groups.copy()
+
+ if world.options.shop_randomizer != ShopRandomizer.option_shopsanity:
+ hintable_location_groups["Onett"] = hintable_location_groups["Onett"] - shop_locations
+ hintable_location_groups["Twoson"] = hintable_location_groups["Twoson"] - shop_locations
+ hintable_location_groups["Happy-Happy Village"] = hintable_location_groups["Happy-Happy Village"] - shop_locations
+ hintable_location_groups["Threed"] = hintable_location_groups["Threed"] - shop_locations
+ hintable_location_groups["Grapefruit Falls"] = hintable_location_groups["Grapefruit Falls"] - shop_locations
+ hintable_location_groups["Saturn Valley"] = hintable_location_groups["Saturn Valley"] - shop_locations
+ hintable_location_groups["Dusty Dunes Desert"] = hintable_location_groups["Dusty Dunes Desert"] - shop_locations
+ hintable_location_groups["Winters"] = hintable_location_groups["Winters"] - shop_locations
+ hintable_location_groups["Dr. Andonuts's Lab"] = hintable_location_groups["Dr. Andonuts's Lab"] - shop_locations
+ hintable_location_groups["Fourside"] = hintable_location_groups["Fourside"] - shop_locations
+ hintable_location_groups["Moonside"] = hintable_location_groups["Moonside"] - shop_locations
+ hintable_location_groups["Summers"] = hintable_location_groups["Summers"] - shop_locations
+ hintable_location_groups["Dalaam"] = hintable_location_groups["Dalaam"] - shop_locations
+ hintable_location_groups["Scaraba"] = hintable_location_groups["Scaraba"] - shop_locations
+ hintable_location_groups["Deep Darkness"] = hintable_location_groups["Deep Darkness"] - shop_locations
+ hintable_location_groups["Lost Underworld"] = hintable_location_groups["Lost Underworld"] - shop_locations
+ hintable_location_groups["Magicant"] = hintable_location_groups["Magicant"] - shop_locations
+
+ del hintable_location_groups["Burglin Park"]
+ del hintable_location_groups["the Scaraba Bazaar"]
+ del hintable_location_groups["the Twoson Department Store"]
+ del hintable_location_groups["the Fourside Department Store"]
+ del hintable_location_groups["the Saturn Valley Shop"]
+
+ if world.options.magicant_mode >= 2:
+ del hintable_location_groups["Magicant"]
+
+ if not world.options.giygas_required:
+ del hintable_location_groups["Cave of the Past"]
+
+ if world.options.magicant_mode.value in [0, 3]:
+ world.local_hintable_items.append("Magicant Teleport")
+
+ for item in world.multiworld.precollected_items[world.player]:
+ if item.name in world.local_hintable_items:
+ world.local_hintable_items.remove(item.name)
+
+ for item in world.options.start_hints.value:
+ if item in world.local_hintable_items:
+ world.local_hintable_items.remove(item)
+
+ if world.starting_area_teleport in world.local_hintable_items:
+ world.local_hintable_items.remove(world.starting_area_teleport)
+
+ if world.local_hintable_items == []:
+ hint_types.remove("hint_for_good_item")
+ hint_types.remove("prog_item_at_region")
+
+ if not world.options.dungeon_shuffle:
+ hint_types.remove("dungeon_location")
+
+ if world.options.giygas_required:
+ world.local_hintable_locations.append("Cave of the Past - Present")
+
+ if world.options.magicant_mode == MagicantMode.option_psi_location:
+ world.local_hintable_locations.append("Magicant - Ness's Nightmare")
+
+ for i in range(6):
+ world.in_game_hint_types.append(world.random.choice(hint_types))
+
+ for index, hint in enumerate(world.in_game_hint_types):
+ if hint == "item_at_location":
+ location = world.random.choice(world.local_hintable_locations)
+ world.hinted_locations[index] = location
+
+ elif hint == "region_progression_check":
+ group, group_locs = world.random.choice(list(hintable_location_groups.items()))
+ world.hinted_regions[index] = group
+
+ elif hint == "hint_for_good_item" or hint == "prog_item_at_region":
+ item = world.random.choice(world.local_hintable_items)
+ world.hinted_items[index] = item
+
+ elif hint == "item_in_local_region":
+ group, group_locs = world.random.choice(list(hintable_location_groups.items()))
+ location = world.random.choice(sorted(group_locs))
+ world.hinted_regions[index] = group
+ world.hinted_locations[index] = location
+
+ elif hint == "dungeon_location":
+ dungeon = world.random.choice(list(world.dungeon_connections.keys()))
+ world.hinted_dungeons[index] = dungeon
+
+
+def parse_hint_data(world: "EarthBoundWorld", location: Location, rom: "LocalRom", hint: str, index: int) -> None:
+ if hint == "item_at_location":
+ if world.player == location.item.player and location.item.name in character_item_table and location.item.name != "Photograph":
+ player_text = "your friend "
+ item_text = bytearray([0x1C, 0x02, party_id_nums[location.item.name]]) # In-game text command to display party member names
+ elif world.player == location.item.player:
+ player_text = "your "
+ if location.item.name in item_id_table:
+ item_text = bytearray([0x1C, 0x05, item_id_table[location.item.name]]) # In-game text command to display item names
+ else:
+ # if the item doesn't have a name (e.g it's PSI)
+ item_text = text_encoder(location.item.name, 128)
+ else:
+ player_text = f"{world.multiworld.get_player_name(location.item.player)}'s "
+ item_text = text_encoder(location.item.name, 128)
+
+ player_text = text_encoder(player_text, 255)
+ location_text = text_encoder(f" can be found at\n@{location.name}.", 255)
+ text = player_text + item_text + location_text
+ # [player]'s [item] can be found at [location].
+ text.append(0x02)
+
+ elif hint == "region_progression_check":
+ if world.progression_count == 1:
+ text = f"you can find {world.progression_count} important item at {world.hinted_area}."
+ else:
+ text = f"you can find {world.progression_count} important items at {world.hinted_area}."
+ text = text_encoder(text, 255)
+ text.append(0x02)
+
+ elif hint == "hint_for_good_item" or hint == "prog_item_at_region" or hint == "item_in_local_region":
+ if location.item.name in character_item_table and location.item.player == world.player and location.item.name != "Photograph":
+ item_text = text_encoder("your friend ", 255)
+ item_text.extend([0x1C, 0x02, party_id_nums[location.item.name]])
+ elif location.item.name in item_id_table and location.item.player == world.player:
+ item_text = text_encoder("your ", 255)
+ item_text.extend([0x1C, 0x05, item_id_table[location.item.name]])
+ elif location.item.player == world.player:
+ item_text = text_encoder(f"your {location.item.name}", 128)
+ else:
+ item_text = f"{world.multiworld.get_player_name(location.item.player)}'s {location.item.name}"
+ item_text = text_encoder(item_text, 255)
+ item_text.extend(text_encoder(" can be found ", 255))
+
+ if location.player != world.player:
+ player_text = text_encoder(f"by {world.multiworld.get_player_name(location.player)}\n", 255)
+ else:
+ player_text = text_encoder("\n", 255)
+
+ if hint == "hint_for_good_item":
+ location_text = text_encoder(f"@at {location.name}.", 255)
+ # your [item] can be found by [player] at [location]
+
+ else:
+ location_name_groups = world.multiworld.worlds[location.player].location_name_groups
+ possible_location_groups = [
+ group_name for group_name, group_locations in location_name_groups.items()
+ if location.name in group_locations and group_name != "Everywhere"
+ ]
+ if not possible_location_groups:
+ if location.parent_region.name == "Menu":
+ area = ""
+ else:
+ area = f" near {location.parent_region.name}"
+ else:
+ area = f" near {world.random.choice(possible_location_groups)}"
+ location_text = text_encoder(f"@somewhere{area}.", 255)
+ # your [item] can be found by [player] somewhere near [location group]
+ text = item_text + player_text + location_text
+ text.append(0x02)
+
+ elif hint == "joke_hint":
+ text = world.random.choice(world.joke_hints)
+ text = text_encoder(text, 255)
+ text.append(0x02)
+
+ elif hint == "dungeon_location":
+ dungeon = world.hinted_dungeons[index]
+ text = f"{dungeon} leads to {world.dungeon_connections[dungeon]}."
+ text = text_encoder(text, 255)
+ text.append(0x02)
+
+ else:
+ text = 0x00
+
+ hint_addresses = [
+ 0x070376,
+ 0x0703A8,
+ 0x0703DA,
+ 0x07040C,
+ 0x07043E,
+ 0x070470
+ ]
+
+ scoutable_hint_addresses = [
+ 0x2EF3D5,
+ 0x2EF3EB,
+ 0x2EF401,
+ 0x2EF417,
+ 0x2EF42D,
+ 0x2EF443
+ ]
+ rom.write_bytes(0x310000 + world.hint_pointer, text)
+ rom.write_bytes(hint_addresses[world.hint_number], struct.pack("I", 0xF10000 + world.hint_pointer))
+
+ if hint in ["item_at_location", "hint_for_good_item"]:
+ rom.write_bytes(scoutable_hint_addresses[world.hint_number], struct.pack("I", 0xEEF451))
+ rom.write_bytes(0x310252 + (world.hint_number * 3), bytearray([0x01]))
+ world.hint_man_hints.append((location.address, location.player))
+ else:
+ rom.write_bytes(scoutable_hint_addresses[world.hint_number], struct.pack("I", 0xEEF4B2))
+ world.hint_man_hints.append(("NULL", 0))
+
+ world.hint_pointer = world.hint_pointer + len(text)
+ world.hint_number += 1
+
+ # Word on the street is that PLAYER's ITEM can be found at LOCATION
+ # Word on the street is that REGION has X important items
+ # Word on the street is that your ITEM can be found by PLAYER at LOCATION
+ # Word on the street is that PLAYER's ITEM can be found somewhere near REGION...
+ # Word on the street is that your ITEM can be found somewhere near REGION...
+ # char item hint?
+ # That's all for today.
+ # Like text part 1, extend 0x1C 0x05 0xItem Item, extend (the rest of the string)
diff --git a/worlds/earthbound/modules/music_rando.py b/worlds/earthbound/modules/music_rando.py
new file mode 100644
index 000000000000..4b3cb82d8d19
--- /dev/null
+++ b/worlds/earthbound/modules/music_rando.py
@@ -0,0 +1,305 @@
+from ..Options import RandomizeOverworldMusic, RandomizeFanfares
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from . import EarthBoundWorld
+ from .Rom import LocalRom
+
+town_songs = [
+ 0x2E,
+ 0x2F,
+ 0x30,
+ 0x38,
+ 0x3B,
+ 0x3D,
+ 0x41,
+ 0x42,
+ 0x52,
+ 0x80,
+ 0x81,
+ 0x82
+]
+
+overworld_songs = [
+ 0x1D,
+ 0x2C,
+ 0x33,
+ 0x34,
+ 0x35,
+ 0x36,
+ 0x3E,
+ 0x40,
+ 0x43,
+ 0x44,
+ 0x6B,
+ 0x72,
+ 0x79,
+ 0x88,
+ 0x92,
+ 0x97,
+ 0x99,
+ 0x9A,
+ 0x9F
+ ]
+
+dungeon_songs = [
+ 0x28,
+ 0x29,
+ 0x2A,
+ 0x2B,
+ 0x2D,
+ 0x31,
+ 0x32,
+ 0x37,
+ 0x2D,
+ 0x3F,
+ 0x46,
+ 0x6A,
+ 0x6C,
+ 0x75,
+ 0x84,
+ 0x85
+]
+
+interior_songs = [
+ 0x10,
+ 0x11,
+ 0x12,
+ 0x13,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x18,
+ 0x19,
+ 0x1A,
+ 0x1C,
+ 0x39,
+ 0x70,
+ 0x78,
+ 0x7D,
+ 0x83,
+ 0x8C,
+ 0x98
+ ]
+
+cutscene_songs = [
+ 0x3A,
+ 0x3C,
+ 0x48,
+ 0x4C,
+ 0x4D,
+ 0x4E,
+ 0x4F,
+ 0x50,
+ 0x51,
+ 0x53,
+ 0x55,
+ 0x56,
+ 0x57,
+ 0x58,
+ 0x59,
+ 0x5C,
+ 0x77,
+ 0x86,
+ 0x90,
+ 0x9B,
+ 0xA9,
+ 0xAA,
+ 0xAB,
+ 0xAC,
+ 0xBB
+]
+
+wakeup_songs = [
+ 0x1B,
+ 0x5A,
+ 0xAD,
+ 0xB2,
+ 0xB5,
+ 0xBC,
+]
+
+rare_songs = [
+ 0x1E,
+ 0x1F,
+ 0x45,
+ 0x47,
+ 0x70,
+ 0x74,
+ 0x76,
+ 0x96,
+
+]
+
+non_randomized = [
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x06,
+ 0x07,
+ 0x0A,
+ 0x0D,
+ 0x0E,
+ 0x20,
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x4B,
+ 0x5D,
+ 0x5E,
+ 0x5F,
+ 0x73,
+ 0x7A,
+ 0x87,
+ 0x89,
+ 0x8A,
+ 0x8B,
+ 0x9D,
+ 0xAE,
+ 0xAF,
+ 0xB6,
+ 0xBE,
+ 0xBF
+]
+
+battle_songs = [
+ 0x49,
+ 0x4A,
+ 0x60,
+ 0x61,
+ 0x62,
+ 0x63,
+ 0x64,
+ 0x65,
+ 0x66,
+ 0x67,
+ 0x68,
+ 0x69,
+ 0x8D,
+ 0x94,
+ 0x95,
+ 0xB9,
+ 0xBA
+]
+
+
+def music_randomizer(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ fanfares = [
+ 0x05,
+ 0x08,
+ 0x09,
+ 0x0B,
+ 0x0C,
+ 0x0F,
+ 0x54,
+ 0x5B,
+ 0x6D,
+ 0x6E,
+ 0x6F,
+ 0x71,
+ 0x7B,
+ 0x7C,
+ 0x7E,
+ 0x7F,
+ 0xB7,
+ 0xB8,
+ 0x8E,
+ 0x8F,
+ 0x91,
+ 0x93,
+ 0x9C,
+ 0x9E,
+ 0xA0,
+ 0xA1,
+ 0xA2,
+ 0xA3,
+ 0xA4,
+ 0xA5,
+ 0xA6,
+ 0xA7,
+ 0xA8,
+ 0xB0,
+ 0xB1,
+ 0xB3,
+ 0xB4,
+ 0xB7,
+ 0xB8,
+ 0xBD
+ ]
+
+ # Todo; options here
+ global_tracklist = list(range(0xC0)) # Initialize the list; this ideally should be vanilla
+
+ if world.options.randomize_fanfares == RandomizeFanfares.option_on_no_sound_stone_fanfares:
+ for i in range(9):
+ fanfares.remove(0xA0 + i)
+
+ if world.options.randomize_fanfares:
+ shuffled_fanfares = fanfares.copy()
+ world.random.shuffle(shuffled_fanfares)
+ for track_id, song in enumerate(fanfares):
+ global_tracklist[song] = shuffled_fanfares[track_id]
+
+ if world.options.randomize_battle_music:
+ shuffled_battle_songs = battle_songs.copy()
+ world.random.shuffle(shuffled_battle_songs)
+ for track_id, song in enumerate(battle_songs):
+ global_tracklist[song] = shuffled_battle_songs[track_id]
+
+ if world.options.randomize_overworld_music == RandomizeOverworldMusic.option_match_type:
+ shuffled_town_songs = town_songs.copy()
+ shuffled_overworld_songs = overworld_songs.copy()
+ shuffled_interior_songs = interior_songs.copy()
+ shuffled_dungeon_songs = dungeon_songs.copy()
+ shuffled_cutscene_songs = cutscene_songs.copy()
+ shuffled_rare_songs = rare_songs.copy()
+ shuffled_wakeup_songs = wakeup_songs.copy()
+
+ world.random.shuffle(shuffled_town_songs)
+ world.random.shuffle(shuffled_overworld_songs)
+ world.random.shuffle(shuffled_interior_songs)
+ world.random.shuffle(shuffled_dungeon_songs)
+ world.random.shuffle(shuffled_cutscene_songs)
+ world.random.shuffle(shuffled_rare_songs)
+ world.random.shuffle(shuffled_wakeup_songs)
+
+ for track_id, song in enumerate(town_songs):
+ global_tracklist[song] = shuffled_town_songs[track_id]
+
+ for track_id, song in enumerate(overworld_songs):
+ global_tracklist[song] = shuffled_overworld_songs[track_id]
+
+ for track_id, song in enumerate(interior_songs):
+ global_tracklist[song] = shuffled_interior_songs[track_id]
+
+ for track_id, song in enumerate(dungeon_songs):
+ global_tracklist[song] = shuffled_dungeon_songs[track_id]
+
+ for track_id, song in enumerate(cutscene_songs):
+ global_tracklist[song] = shuffled_cutscene_songs[track_id]
+
+ for track_id, song in enumerate(rare_songs):
+ global_tracklist[song] = shuffled_rare_songs[track_id]
+
+ for track_id, song in enumerate(wakeup_songs):
+ global_tracklist[song] = shuffled_wakeup_songs[track_id]
+
+ elif world.options.randomize_overworld_music == RandomizeOverworldMusic.option_full:
+ all_overworld_songs = (town_songs + overworld_songs + interior_songs +
+ dungeon_songs + cutscene_songs + rare_songs + wakeup_songs)
+
+ shuffled_overworld_songs = all_overworld_songs.copy()
+ world.random.shuffle(shuffled_overworld_songs)
+
+ for track_id, song in enumerate(all_overworld_songs):
+ global_tracklist[song] = shuffled_overworld_songs[track_id]
+
+ rom.write_bytes(0x17FDA0, bytearray(global_tracklist))
+
+
+# Should the Melodies be fanfares?
diff --git a/worlds/earthbound/modules/palette_shuffle.py b/worlds/earthbound/modules/palette_shuffle.py
new file mode 100644
index 000000000000..0ecb81af8e3e
--- /dev/null
+++ b/worlds/earthbound/modules/palette_shuffle.py
@@ -0,0 +1,52 @@
+from ..game_data.palettes_organized import map_palettes, nice_palettes, ugly_palettes, nonsense_palettes
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from . import EarthBoundWorld
+ from .Rom import LocalRom
+
+
+event_palettes = {
+ "Happy-Happy Village": 0x8367,
+ "Threed": 0x85A7,
+ "Deep Darkness": 0x8F67
+ }
+
+
+def randomize_psi_palettes(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ spell_palettes = []
+ for i in range(34):
+ spell_palettes.append(0x0CF47F + (i * 8))
+
+ for i in range(7):
+ spell_palettes.append(0x360710 + (i * 8))
+
+ shuffled_palettes = spell_palettes.copy()
+
+ if world.options.randomize_psi_palettes == 1:
+ world.random.shuffle(shuffled_palettes)
+
+ elif world.options.randomize_psi_palettes == 2:
+ for i in range(0x010E):
+ rom.write_bytes(0x0CF47F + i, bytearray([world.random.randint(0x00, 0xFF)]))
+
+ for i in range(0x50):
+ rom.write_bytes(0x36F710 + i, bytearray([world.random.randint(0x00, 0xFF)]))
+
+ for index, pointer in enumerate(spell_palettes):
+ rom.copy_bytes(pointer, 8, shuffled_palettes[index])
+
+
+def map_palette_shuffle(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ for i in range(168):
+ rom.copy_bytes(0x1A7CA7 + (i * 192), 191, 0x381000 + (i * 192))
+
+ for item in map_palettes:
+ choosable_palettes = nice_palettes[item]
+ if world.options.map_palette_shuffle > 1:
+ choosable_palettes += ugly_palettes[item]
+ if world.options.map_palette_shuffle > 2:
+ choosable_palettes += nonsense_palettes[item]
+
+ chosen_palette = world.random.choice(choosable_palettes)
+ rom.copy_bytes(0x381002 + (chosen_palette * 192), 29, 0x1A7CA9 + (map_palettes[item] * 192))
+ rom.copy_bytes(0x381022 + (chosen_palette * 192), 157, 0x1A7CC9 + (map_palettes[item] * 192)) # The event palette pointer is between these 2 blocks
diff --git a/worlds/earthbound/modules/psi_shuffle.py b/worlds/earthbound/modules/psi_shuffle.py
new file mode 100644
index 000000000000..2be424ad9181
--- /dev/null
+++ b/worlds/earthbound/modules/psi_shuffle.py
@@ -0,0 +1,515 @@
+import struct
+from ..Options import PSIShuffle
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from . import EarthBoundWorld
+ from .Rom import LocalRom
+
+
+def shuffle_psi(world: "EarthBoundWorld") -> None:
+ world.offensive_psi_slots = [
+ "Special",
+ "Flash",
+ "Fire",
+ "Freeze",
+ "Thunder",
+ "Starstorm",
+ "Blast",
+ "Missile"
+ ]
+
+ world.assist_psi_slots = [
+ "Hypnosis",
+ "Paralysis",
+ "Offense Up",
+ "Defense Down",
+ "Brainshock",
+ "Defense up",
+ "Drain",
+ "Disable",
+ "Stop",
+ "Neutralize"
+ ]
+
+ world.shield_slots = [
+ "Shield",
+ "PSI Shield"
+ ]
+
+ world.jeff_offense_items = []
+ world.jeff_assist_items = []
+
+ world.psi_address = {
+ "Special": [0x158A5F, 4],
+ "Flash": [0x158B4F, 4],
+ "Fire": [0x158A9B, 4],
+ "Freeze": [0x158AD7, 4],
+ "Thunder": [0x158B13, 4],
+ "Starstorm": [0x3503FC, 4],
+
+ "Shield": [0x158C21, 4],
+ "PSI Shield": [0x158C5D, 4],
+
+ "Hypnosis": [0x158CD5, 2],
+ "Paralysis": [0x158D11, 2],
+ "Offense Up": [0x158C99, 2],
+ "Defense Down": [0x158CB7, 2],
+ "Brainshock": [0x158D2F, 2],
+
+ "Blast": [0x35041A, 4],
+ "Missile": [0x350456, 4],
+
+ "Defense up": [0x350492, 2],
+ "Drain": [0x3504B0, 2],
+ "Disable": [0x3504Ce, 2],
+ "Stop": [0x3504EC, 2],
+ "Neutralize": [0x35050A, 2],
+ }
+
+ if world.options.psi_shuffle:
+ world.random.shuffle(world.offensive_psi_slots)
+
+ if world.options.psi_shuffle != PSIShuffle.option_extended:
+ adjust_psi_list(world.offensive_psi_slots, "Blast", 7)
+ adjust_psi_list(world.offensive_psi_slots, "Missile", 7)
+
+ if not world.options.allow_flash_as_favorite_thing:
+ if world.offensive_psi_slots[0] == "Flash":
+ adjust_psi_list(world.offensive_psi_slots, "Flash", world.random.randint(1, 5)) # Randomize which slot gets Flash
+
+ world.random.shuffle(world.assist_psi_slots)
+
+ if world.options.psi_shuffle != PSIShuffle.option_extended:
+ adjust_psi_list(world.assist_psi_slots, "Defense up", 10)
+ adjust_psi_list(world.assist_psi_slots, "Drain", 10)
+ adjust_psi_list(world.assist_psi_slots, "Disable", 10)
+ adjust_psi_list(world.assist_psi_slots, "Stop", 10)
+ adjust_psi_list(world.assist_psi_slots, "Neutralize", 10)
+
+ world.jeff_offense_items.extend(world.offensive_psi_slots[-2:])
+ world.jeff_assist_items.extend(world.assist_psi_slots[-5:])
+ world.offensive_psi_slots = world.offensive_psi_slots[:-2]
+ world.assist_psi_slots = world.assist_psi_slots[:-5]
+
+ world.random.shuffle(world.shield_slots)
+
+ shield_data = {key: world.psi_address[key] for key in world.shield_slots}
+ assist_data = {key: world.psi_address[key] for key in world.assist_psi_slots}
+ assist_data_plus = {key: world.psi_address[key] for key in world.jeff_assist_items}
+ offense_data_plus = {key: world.psi_address[key] for key in world.jeff_offense_items}
+
+ world.psi_address = {key: world.psi_address[key] for key in world.offensive_psi_slots}
+ world.psi_address.update(shield_data)
+ world.psi_address.update(assist_data)
+ world.psi_address.update(offense_data_plus)
+ world.psi_address.update(assist_data_plus)
+
+ world.psi_slot_data = [
+ [[0x09, 0x00], [0x0B, 0x00], [0x0D, 0x00], [0x0F, 0x00]], # Special
+ [[0x09, 0x01], [0x0B, 0x01], [0x0D, 0x01], [0x0F, 0x01]], # Flash
+ [[0x09, 0x00], [0x0B, 0x00], [0x0D, 0x00], [0x0F, 0x00]], # Fire
+ [[0x09, 0x01], [0x0B, 0x01], [0x0D, 0x01], [0x0F, 0x01]], # Freeze
+ [[0x09, 0x02], [0x0B, 0x02], [0x0D, 0x02], [0x0F, 0x02]], # Thunder
+ [[0x09, 0x00], [0x0B, 0x00], [0x0D, 0x00], [0x0F, 0x00]], # Starstorm
+
+ [[0x09, 0x00], [0x0B, 0x00], [0x0D, 0x00], [0x0F, 0x00]], # Shield
+ [[0x09, 0x00], [0x0B, 0x00], [0x0D, 0x00], [0x0F, 0x00]], # PSI Shield
+
+ [[0x09, 0x01], [0x0B, 0x01]], # Hypnosis
+ [[0x09, 0x02], [0x0B, 0x02]], # Paralysis
+ [[0x09, 0x01], [0x0B, 0x01]], # Offense Up
+ [[0x09, 0x02], [0x0B, 0x02]], # Defense Down
+ [[0x09, 0x01], [0x0B, 0x01]], # Brainshock
+
+ [[0x09, 0x00], [0x0B, 0x00], [0x0D, 0x00], [0x0F, 0x00]], # Blast
+ [[0x09, 0x00], [0x0B, 0x00], [0x0D, 0x00], [0x0F, 0x00]], # Missile
+
+ [[0x09, 0x01], [0x0B, 0x01]], # Defense up
+ [[0x09, 0x02], [0x0B, 0x02]], # Drain
+ [[0x09, 0x01], [0x0B, 0x01]], # Disable
+ [[0x09, 0x02], [0x0B, 0x02]], # Stop
+ [[0x09, 0x01], [0x0B, 0x01]], # Neutralize
+ ]
+
+ world.psi_level_data = [
+ [[0x08, 0x00, 0x00], [0x16, 0x00, 0x00], [0x31, 0x00, 0x00], [0x4B, 0x00, 0x00]], # Special
+ [[0x12, 0x00, 0x00], [0x26, 0x00, 0x00], [0x3D, 0x00, 0x00], [0x43, 0x00, 0x00]], # Flash
+ [[0x00, 0x03, 0x00], [0x00, 0x13, 0x00], [0x00, 0x25, 0x00], [0x00, 0x40, 0x00]], # Fire
+ [[0x00, 0x01, 0x01], [0x00, 0x0B, 0x01], [0x00, 0x1F, 0x21], [0x00, 0x2E, 0x00]], # Freeze
+ [[0x00, 0x08, 0x01], [0x00, 0x19, 0x01], [0x00, 0x39, 0x29], [0x00, 0x00, 0x37]], # Thunder
+ [[0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00]], # Starstorm
+
+ [[0x0C, 0x00, 0x0E], [0x00, 0x00, 0x0F], [0x22, 0x00, 0x10], [0x00, 0x00, 0x33]], # Shield
+ [[0x00, 0x06, 0x00], [0x00, 0x1B, 0x00], [0x00, 0x33, 0x00], [0x00, 0x3C, 0x00]], # PSI Shield
+
+ [[0x04, 0x00, 0x00], [0x1B, 0x00, 0x00]], # Hypnosis
+ [[0x0E, 0x00, 0x00], [0x1D, 0x00, 0x00]], # Paralysis
+ [[0x00, 0x15, 0x00], [0x00, 0x28, 0x00]], # Offense Up
+ [[0x00, 0x1D, 0x00], [0x00, 0x36, 0x00]], # Defense Down
+ [[0x00, 0x00, 0x18], [0x00, 0x00, 0x2C]], # Brainshock
+
+ # Level-up data needs to be zeroed out for these slots
+ [[0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00]], # Blast
+ [[0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00]], # Missile
+
+ [[0x00, 0x00, 0x00], [0x00, 0x00, 0x00]], # Defense up
+ [[0x00, 0x00, 0x00], [0x00, 0x00, 0x00]], # Drain
+ [[0x00, 0x00, 0x00], [0x00, 0x00, 0x00]], # Disable
+ [[0x00, 0x00, 0x00], [0x00, 0x00, 0x00]], # Stop
+ [[0x00, 0x00, 0x00], [0x00, 0x00, 0x00]] # Neutralize
+
+ ]
+
+ world.bomb_names = {
+ "Special": ["Psycho bomb", "Mad psycho bomb", "Psywave emitter", "Psywave blaster", "Broken radio"],
+ "Flash": ["Smoke bomb", "Flashbang", "Flash pan", "Digital camera", "Broken camera"],
+ "Freeze": ["Ice bomb", "Dry ice bomb", "Frost ray", "Freeze ray", "Broken minifridge"],
+ "Fire": ["Fire bomb", "Napalm bomb", "Blowtorch", "Flamethrower", "Broken grill"],
+ "Thunder": ["Electric bomb", "EMP bomb", "Shock coil", "Tesla coil", "Broken spring"],
+ "Starstorm": ["Comet bomb", "Nova bomb", "Meteor radar", "Star radar", "Broken radar"],
+ "Blast": ["Bomb", "Super bomb", "Bazooka", "Heavy bazooka", "Broken bazooka"],
+ "Missile": ["Rocket", "Nitro rocket", "Missile launcher", "Nitro launcher", "Broken vacuum"]
+ }
+
+ world.rocket_names = {
+ "Special": ["Psionic shard", "Psionic orb", "Psionic crystal"],
+ "Flash": ["Flashbulb", "Bright bulb", "Magna bulb"],
+ "Freeze": ["LN2 bottle", "LN2 jug", "LN2 bucket"],
+ "Fire": ["Flare", "Big flare", "Blitz flare"],
+ "Thunder": ["Sparkler", "Big sparkler", "Mega sparkler"],
+ "Starstorm": ["Meteor missile", "Star missile", "Nova missile"],
+ "Blast": ["Firecracker", "Big firecracker", "Super firecracker"],
+ "Missile": ["Bottle rocket", "Big bottle rocket", "Multi bottle rocket"]
+ }
+
+ world.spray_names = {
+ "Hypnosis": "Chloroform spray",
+ "Paralysis": "Nerve spray",
+ "Offense Up": "Offense spray",
+ "Defense Down": "Weakness spray",
+ "Brainshock": "Confusion spray",
+ "Defense up": "Defense spray",
+ "Drain": "HP-straw",
+ "Disable": "Distraction spray",
+ "Stop": "Slime spray",
+ "Neutralize": "Shield-off spray"
+ }
+ world.broken_gadgets = {
+ "Hypnosis": ["Broken watch", "Broken screen"],
+ "Paralysis": ["Broken razor", "Broken fan"],
+ "Offense Up": ["Broken faucet", "Broken tuba"],
+ "Defense Down": ["Broken sprinkler", "Broken trombone"],
+ "Brainshock": ["Broken toaster", "Broken fryer"],
+ "Defense up": ["Broken nozzle", "Broken trumpet"],
+ "Drain": ["Broken hose", "Broken tube"],
+ "Disable": ["Broken machine", "Broken device"],
+ "Stop": ["Broken iron", "Broken steamer"],
+ "Neutralize": ["Broken pipe", "Broken board"]
+ }
+
+ world.broken_desc = {
+ "Hypnosis": [0x00EEEBA4, 0x00EEEBCC],
+ "Paralysis": [0x00EEEC06, 0x00EEEC28],
+ "Offense Up": [0x00EEEC4D, 0x00EEEC75],
+ "Defense Down": [0x00EEECA5, 0x00EEECCB],
+ "Brainshock": [0x00EEED00, 0x00EEED2B],
+ "Defense up": [0x00EEED5A, 0x00C53929],
+ "Drain": [0x00EEED8A, 0x00C538E0],
+ "Disable": [0x00C53772, 0x00EEEDB2],
+ "Stop": [0x00C53870, 0x00EEEE03],
+ "Neutralize": [0x00C53897, 0x00EEEE2F]
+ }
+
+ world.gadget_names = {
+ "Hypnosis": ["Hypno pendulum", "Hypno screen"],
+ "Paralysis": ["Nerve taser", "Nerve ray"],
+ "Offense Up": ["Offensalizer", "Offense shower"],
+ "Defense Down": ["Weakalizer", "Weakness shower"],
+ "Brainshock": ["Mind jammer", "Mind fryer"],
+ "Defense up": ["Defensalizer", "Defense shower"],
+ "Drain": ["HP-sucker", "Hungry HP-sucker"],
+ "Disable": ["Counter-PSI unit", "PSI-nullifier unit"],
+ "Stop": ["Slime generator", "Slime blaster"],
+ "Neutralize": ["Shield killer", "Neutralizer"]
+ }
+
+ world.starstorm_address = {
+ "Special": [0x002D, 0x003C],
+ "Flash": [0x011D, 0x012C],
+ "Fire": [0x0069, 0x0078],
+ "Freeze": [0x00A5, 0x00B4],
+ "Thunder": [0x00E1, 0x00F0],
+ "Starstorm": [0x013B, 0x014A],
+ "Blast": [0x0438, 0x0447],
+ "Missile": [0x0474, 0x0483]
+ }
+
+ world.starstorm_spell_id = {
+ "Special": [0x03, 0x04],
+ "Flash": [0x13, 0x14],
+ "Fire": [0x07, 0x08],
+ "Freeze": [0x0B, 0x0C],
+ "Thunder": [0x0F, 0x10],
+ "Starstorm": [0x15, 0x16],
+ "Blast": [0x48, 0x49],
+ "Missile": [0x4C, 0x4D]
+ }
+
+ world.jeff_addresses = [
+ 0x156665, # Bomb
+ 0x15668C, # Super Bomb
+ 0x156443, # Bazooka
+ 0x15646A, # Heavy bazooka
+ 0x1551FB, # broken bazooka
+
+ 0x1565F0, # Bottle Rocket
+ 0x156617, # Big Bottle Rocket
+ 0x15663E, # multi Bottle Rocket
+ ]
+
+ world.gadget_addresses = [
+ [0, 0x1567EB], # defense shower
+ [0x156491, 0x1564B8], # HP-Sucker
+ [0x1563F5], # Counter-PSI Unit
+ [0x156506], # Slime Generator
+ [0x15641C, 0x156DB5], # Shield Killer
+ ]
+
+ world.broken_gadget_addresses = [
+ 0x155222,
+ 0x1551D4,
+ 0x15509C,
+ 0x15515F,
+ 0x155186
+ ]
+
+ world.bomb_desc = {
+ "Special": [0x00EED4D8, 0x00EED521, 0x00EEDA4F, 0x00EEDA6B, 0x00EEDCFB],
+ "Flash": [0x00EED538, 0x00EED5AB, 0x00EEDA87, 0x00EEDAD7, 0x00EEDD1D],
+ "Fire": [0x00EED659, 0x00EED6CC, 0x00EEDB37, 0x00EEDB68, 0x00EEDD40],
+ "Freeze": [0x00EED744, 0x00EED79D, 0x00EEDB85, 0x00EEDBA2, 0x00EEDD64],
+ "Thunder": [0x00EED822, 0x00EED88E, 0x00EEDBBF, 0x00EEDBE4, 0x00EEDDA0],
+ "Starstorm": [0x00EED4D8, 0x00EED521, 0x00EEDA4F, 0x00EEDCD2, 0x00EEDDC3],
+ "Blast": [0x00C54EA7, 0x00C54EF7, 0x00C54AA1, 0x00C54AF5, 0x00C53908],
+ "Missile": [0x00EED9A5, 0x00EED9FF, 0x00EEDC09, 0x00EEDC40, 0x00EEDDEE]
+ }
+
+ world.rocket_desc = {
+ "Special": [0x00EEDE25, 0x00EEDE41, 0x00EEDE80],
+ "Flash": [0x00EEDEDC, 0x00EEDF37, 0x00EEDFAE],
+ "Fire": [0x00EEE013, 0x00EEE031, 0x00EEE061],
+ "Freeze": [0x00EEE098, 0x00EEE0B5, 0x00EEE11E],
+ "Thunder": [0x00EEE190, 0x00EEE1B6, 0x00EEE220],
+ "Starstorm": [0x00EEE28B, 0x00EEE2A7, 0x00EEE2DB],
+ "Blast": [0x00EEE344, 0x00EEE35B, 0x00EEE36E],
+ "Missile": [0x00C54E01, 0x00C54E20, 0x00C54E54]
+ }
+
+ world.spray_desc = {
+ "Hypnosis": 0x00EEE653,
+ "Paralysis": 0x00EEE688,
+ "Offense Up": 0x00EEE6B9,
+ "Defense Down": 0x00EEE705,
+ "Brainshock": 0x00EEE751,
+ "Defense up": 0x00C2558D,
+ "Drain": 0x00EEE802,
+ "Disable": 0x00EEE838,
+ "Stop": 0x00EEE7D0,
+ "Neutralize": 0x00EEE78F
+ }
+
+ world.gadget_desc = {
+ "Hypnosis": [0x00EEE87B, 0x00EEE8AA],
+ "Paralysis": [0x00EEE8DA, 0x00EEE905],
+ "Offense Up": [0x00EEE933, 0x00EEE97F],
+ "Defense Down": [0x00EEE9C2, 0x00EEEA0E],
+ "Brainshock": [0x00EEEA9D, 0x00EEEAD2],
+ "Defense up": [0x00EEEA51, 0x00C5519F],
+ "Drain": [0x00C54B67, 0x00C54BB0],
+ "Disable": [0x00C54A3A, 0x00EEEB5E],
+ "Stop": [0x00C54C32, 0x00EEEB09],
+ "Neutralize": [0x00C54A6C, 0x00C559D9]
+ }
+
+ world.bomb_actions = {
+ "Special": [0x01AC, 0x01AD, 0x01CF, 0x01D0],
+ "Flash": [0x01AE, 0x01AF, 0x01D1, 0x01D2],
+ "Fire": [0x01B0, 0x01B1, 0x01D3, 0x01D4],
+ "Freeze": [0x01B2, 0x01B3, 0x01D5, 0x01D6],
+ "Thunder": [0x01B4, 0x01B5, 0x01D7, 0x01D8],
+ "Starstorm": [0x01B6, 0x01B7, 0x01D9, 0x01DA],
+ "Blast": [0x00A7, 0x00A8, 0x0136, 0x0137],
+ "Missile": [0x01B8, 0x01B9, 0x01DB, 0x01DC]
+ }
+
+ world.missile_actions = {
+ "Special": [0x01BA, 0x01BB, 0x01BC],
+ "Flash": [0x01BD, 0x01BE, 0x01BF],
+ "Fire": [0x01C0, 0x01C1, 0x01C2],
+ "Freeze": [0x01C3, 0x01C4, 0x01C5],
+ "Thunder": [0x01C6, 0x01C7, 0x01C8],
+ "Starstorm": [0x01C9, 0x01CA, 0x01CB],
+ "Blast": [0x01CC, 0x01CD, 0x01CE],
+ "Missile": [0x00A3, 0x00A4, 0x00A5]
+ }
+
+ world.gadget_actions = {
+ "Hypnosis": [0x01E7, 0x01E8],
+ "Paralysis": [0x01E9, 0x01EA],
+ "Offense Up": [0x01EB, 0x01EC],
+ "Defense Down": [0x01ED, 0x01EE],
+ "Brainshock": [0x01EF, 0x01F0],
+ "Defense up": [0x00B7, 0x00B8],
+ "Drain": [0x00A1, 0x00B0],
+ "Disable": [0x009F, 0x01F1],
+ "Stop": [0x00A9, 0x01F2],
+ "Neutralize": [0x00A0, 0x00F7]
+ }
+
+ world.jeff_item_counts = [
+ 5, # Bomb
+ 3 # Bottle Rocket
+ ]
+
+ world.gadget_counts = [
+ 1, # defense shower
+ 2, # HP-Sucker
+ 1, # Counter PSI unit
+ 1, # Slime generator
+ 2 # Neutralizer
+ ]
+
+ world.gadget_ids = [
+ [0x00, 0x9D],
+ [0x87, 0x88],
+ [0x83],
+ [0x8A],
+ [0x84, 0xC3]
+ ]
+
+ world.broken_gadget_ids = [
+ 0x0E, # Broken trumpet
+ 0x0C, # Broken tube
+ 0x04, # Broken machine
+ 0x09, # Broken iron
+ 0x0A # Broken pipe
+ ]
+
+ world.jeff_item_names = [
+ world.bomb_names,
+ world.rocket_names
+ ]
+
+ world.jeff_help_text = [
+ world.bomb_desc,
+ world.rocket_desc
+ ]
+
+ world.attack_types = [
+ world.bomb_actions,
+ world.missile_actions
+ ]
+
+
+def write_psi(world: "EarthBoundWorld", rom: "LocalRom") -> None:
+ from ..game_data.text_data import text_encoder
+ psi_num = 0
+ for spell, (address, levels) in world.psi_address.items():
+ for i in range(levels):
+ rom.write_bytes(address + 9, bytearray(world.psi_slot_data[psi_num][i]))
+ rom.write_bytes(address + 6, bytearray(world.psi_level_data[psi_num][i]))
+ if psi_num == 0:
+ rom.write_bytes(address, bytearray([0x01]))
+ elif psi_num == 5 and i > 1:
+ rom.write_bytes(0x01C4AB + (0x9E * (i - 2)), struct.pack("H", world.starstorm_address[spell][i - 2])) # Menu spells
+ rom.write_bytes(0x01C536 + (0x78 * (i - 2)), bytearray([world.starstorm_spell_id[spell][i - 2]])) # What spell is controlled by the PSI flags
+ rom.write_bytes(0x2E957F + (0x11 * (i - 2)), bytearray([world.starstorm_spell_id[spell][i - 2]])) # The global Poo PSI text
+ rom.write_bytes(address + 9, bytearray(world.psi_slot_data[psi_num][i - 2]))
+
+ if spell == "Special" and psi_num != 0:
+ rom.write_bytes(address, bytearray([0x12]))
+
+ address += 15
+ if spell == "Starstorm" and i == 1:
+ address = 0x158B8B
+ # todo; expanded psi
+ # todo; animation for Starstorm L/D
+ # todo; swap enemy actions for Special?
+ # todo; cleanup stuff
+ psi_num += 1
+
+ # rom.write_bytes(0x2E957F + (0x11 * (i - 2)), bytearray([world.starstorm_spell_id[spell][i - 2]]))
+ # Starstorm spell for the item locally
+ rom.write_bytes(0x2EAE2E, bytearray([world.starstorm_spell_id[world.offensive_psi_slots[5]][0]]))
+ rom.write_bytes(0x2EAE38, bytearray([world.starstorm_spell_id[world.offensive_psi_slots[5]][1]]))
+
+ jeff_item_num = 0
+ jeff_item_index = 0
+ for item in world.jeff_offense_items:
+ for i in range(world.jeff_item_counts[jeff_item_num]):
+ address = world.jeff_addresses[jeff_item_index]
+ jeff_item_index += 1
+ name = world.jeff_item_names[jeff_item_num][item][i]
+ description = world.jeff_help_text[jeff_item_num][item][i]
+ name_encoded = text_encoder(name, 22)
+ name_encoded.extend(([0x00]))
+ rom.write_bytes(address, name_encoded)
+ rom.write_bytes(address + 35, struct.pack("I", description))
+ if "Broken" not in name: # broken items don't need attack data
+ attack = world.attack_types[jeff_item_num][item][i]
+ rom.write_bytes(address + 29, struct.pack("H", attack))
+ jeff_item_num += 1
+ jeff_item_num = 0
+ name = world.spray_names[world.jeff_assist_items[0]]
+ name_encoded = text_encoder(name, 22)
+ name_encoded.extend(([0x00]))
+ description = world.spray_desc[world.jeff_assist_items[0]]
+ address = 0x156887
+ action = world.gadget_actions[world.jeff_assist_items[0]][0]
+ rom.write_bytes(address, name_encoded)
+ rom.write_bytes(address + 35, struct.pack("I", description))
+ rom.write_bytes(address + 29, struct.pack("H", action))
+
+ for item in world.jeff_assist_items:
+ for i in range(world.gadget_counts[jeff_item_num]):
+ if jeff_item_num == 0:
+ i = 1
+ name = world.gadget_names[item][i]
+ name_encoded = text_encoder(name, 22)
+ name_encoded.extend(([0x00]))
+ description = world.gadget_desc[item][i]
+ action = world.gadget_actions[world.jeff_assist_items[jeff_item_num]][i]
+ address = world.gadget_addresses[jeff_item_num][i]
+ rom.write_bytes(address, name_encoded)
+ rom.write_bytes(address + 35, struct.pack("I", description))
+ rom.write_bytes(address + 29, struct.pack("H", action))
+ rom.write_bytes(description - 0xC00000 + 5, bytearray([world.gadget_ids[jeff_item_num][i]]))
+ jeff_item_num += 1
+
+ for i in range(5):
+ item = world.jeff_assist_items[i]
+ if i < 2:
+ level = 1
+ else:
+ level = 0
+ address = world.broken_gadget_addresses[i]
+ name = world.broken_gadgets[item][level]
+ description = world.broken_desc[item][level]
+ name_encoded = text_encoder(name, 22)
+ name_encoded.extend(([0x00]))
+ rom.write_bytes(address, name_encoded)
+ rom.write_bytes(address + 35, struct.pack("I", description))
+ rom.write_bytes(description - 0xC00000 + 5, bytearray([world.broken_gadget_ids[i]]))
+
+ rom.write_bytes(0x15A8EB, bytearray(struct.pack("H", world.gadget_actions[world.jeff_assist_items[4]][1])))
+ rom.write_bytes(0x15BB45, bytearray(struct.pack("H", world.gadget_actions[world.jeff_assist_items[0]][0])))
+ rom.write_bytes(0x15BBA5, bytearray(struct.pack("H", world.gadget_actions[world.jeff_assist_items[4]][0])))
+ rom.write_bytes(0x15DF41, bytearray(struct.pack("H", world.gadget_actions[world.jeff_assist_items[4]][0])))
+ rom.write_bytes(0x15DF3F, bytearray(struct.pack("H", world.gadget_actions[world.jeff_assist_items[4]][1])))
+ rom.write_bytes(0x15BA2B, bytearray(struct.pack("H", world.gadget_actions[world.jeff_assist_items[1]][0])))
+ rom.write_bytes(0x15C06D, bytearray(struct.pack("H", world.gadget_actions[world.jeff_assist_items[1]][1])))
+
+
+def adjust_psi_list(psi_input: list[str], spell: str, index: int) -> None:
+ """Move a spell in the PSI table to a different entry/slot"""
+ psi_input.insert(index, (psi_input.pop(psi_input.index(spell))))
diff --git a/worlds/earthbound/modules/shopsanity.py b/worlds/earthbound/modules/shopsanity.py
new file mode 100644
index 000000000000..26ca52c1d862
--- /dev/null
+++ b/worlds/earthbound/modules/shopsanity.py
@@ -0,0 +1,449 @@
+from ..game_data.local_data import psi_item_table, character_item_table, special_name_table, item_id_table, money_item_table
+from ..game_data.text_data import calc_pixel_width, text_encoder
+from ..game_data.static_location_data import location_ids
+from ..Options import ShopRandomizer, MagicantMode, MonkeyCavesMode
+from BaseClasses import ItemClassification, Location
+import struct
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from . import EarthBoundWorld
+ from .Rom import LocalRom
+
+high_purchase_areas = {
+ "Twoson",
+ "Fourside",
+ "Scaraba",
+ "Andonuts Lab Area" # long walk with no atm
+}
+
+shop_locations = {
+ "Onett Drugstore - Right Counter Slot 1",
+ "Onett Drugstore - Right Counter Slot 2",
+ "Onett Drugstore - Right Counter Slot 3",
+ "Onett Drugstore - Right Counter Slot 4",
+ "Onett Drugstore - Right Counter Slot 5",
+ "Onett Drugstore - Left Counter",
+ "Summers - Beach Cart",
+ "Onett Burger Shop - Slot 1",
+ "Onett Burger Shop - Slot 2",
+ "Onett Burger Shop - Slot 3",
+ "Onett Burger Shop - Slot 4",
+ "Onett Bakery - Slot 1",
+ "Onett Bakery - Slot 2",
+ "Onett Bakery - Slot 3",
+ "Onett Bakery - Slot 4",
+ "Twoson Department Store Burger Shop - Slot 1",
+ "Twoson Department Store Burger Shop - Slot 2",
+ "Twoson Department Store Burger Shop - Slot 3",
+ "Twoson Department Store Burger Shop - Slot 4",
+ "Twoson Department Store Bakery - Slot 1",
+ "Twoson Department Store Bakery - Slot 2",
+ "Twoson Department Store Bakery - Slot 3",
+ "Twoson Department Store Bakery - Slot 4",
+ "Twoson Department Store Top Floor - Right Counter Slot 1",
+ "Twoson Department Store Top Floor - Right Counter Slot 2",
+ "Twoson Department Store Top Floor - Right Counter Slot 3",
+ "Twoson Department Store Top Floor - Right Counter Slot 4",
+ "Twoson Department Store Top Floor - Right Counter Slot 5",
+ "Twoson Department Store Top Floor - Right Counter Slot 6",
+ "Twoson Department Store Top Floor - Left Counter Slot 1",
+ "Summers - Magic Cake Cart Shop Slot",
+ "Burglin Park Junk Shop - Slot 1",
+ "Burglin Park Junk Shop - Slot 2",
+ "Burglin Park Junk Shop - Slot 3",
+ "Burglin Park Junk Shop - Slot 4",
+ "Burglin Park Junk Shop - Slot 5",
+ "Burglin Park Junk Shop - Slot 6",
+ "Burglin Park Bread Stand - Slot 1",
+ "Burglin Park Bread Stand - Slot 2",
+ "Burglin Park Bread Stand - Slot 3",
+ "Burglin Park Bread Stand - Slot 4",
+ "Burglin Park Bread Stand - Slot 5",
+ "Burglin Park Bread Stand - Slot 6",
+ "Burglin Park - Banana Stand",
+ "Happy-Happy Village Drugstore - Right Counter Slot 1",
+ "Happy-Happy Village Drugstore - Right Counter Slot 2",
+ "Happy-Happy Village Drugstore - Right Counter Slot 3",
+ "Happy-Happy Village Drugstore - Right Counter Slot 4",
+ "Happy-Happy Village Drugstore - Right Counter Slot 5",
+ "Threed Drugstore - Right Counter Slot 1",
+ "Threed Drugstore - Right Counter Slot 2",
+ "Threed Drugstore - Right Counter Slot 3",
+ "Threed Drugstore - Right Counter Slot 4",
+ "Threed Drugstore - Right Counter Slot 5",
+ "Threed Drugstore - Left Counter Slot 1",
+ "Threed Drugstore - Left Counter Slot 2",
+ "Threed Drugstore - Left Counter Slot 3",
+ "Threed Drugstore - Left Counter Slot 4",
+ "Threed Drugstore - Left Counter Slot 5",
+ "Threed - Arms Dealer Slot 1",
+ "Threed - Arms Dealer Slot 2",
+ "Threed - Arms Dealer Slot 3",
+ "Threed - Arms Dealer Slot 4",
+ "Threed Bakery - Slot 1",
+ "Threed Bakery - Slot 2",
+ "Threed Bakery - Slot 3",
+ "Threed Bakery - Slot 4",
+ "Threed Bakery - Slot 5",
+ "Threed Bakery - Slot 6",
+ "Threed Bakery - Slot 7",
+ "Scaraba - Expensive Water Guy",
+ "Winters Drugstore - Slot 1",
+ "Winters Drugstore - Slot 2",
+ "Winters Drugstore - Slot 3",
+ "Winters Drugstore - Slot 4",
+ "Winters Drugstore - Slot 5",
+ "Winters Drugstore - Slot 6",
+ "Winters Drugstore - Slot 7",
+ "Saturn Valley Shop - Center Saturn Slot 1",
+ "Saturn Valley Shop - Center Saturn Slot 2",
+ "Saturn Valley Shop - Center Saturn Slot 3",
+ "Saturn Valley Shop - Center Saturn Slot 4",
+ "Saturn Valley Shop - Center Saturn Slot 5",
+ "Dusty Dunes Drugstore - Counter Slot 1",
+ "Dusty Dunes Drugstore - Counter Slot 2",
+ "Dusty Dunes Drugstore - Counter Slot 3",
+ "Dusty Dunes Drugstore - Counter Slot 4",
+ "Dusty Dunes Drugstore - Counter Slot 5",
+ "Dusty Dunes - Arms Dealer Slot 1",
+ "Dusty Dunes - Arms Dealer Slot 2",
+ "Dusty Dunes - Arms Dealer Slot 3",
+ "Dusty Dunes - Arms Dealer Slot 4",
+ "Fourside Bakery - Slot 1",
+ "Fourside Bakery - Slot 2",
+ "Fourside Bakery - Slot 3",
+ "Fourside Bakery - Slot 4",
+ "Fourside Bakery - Slot 5",
+ "Fourside Bakery - Slot 6",
+ "Fourside Department Store - Tool Shop Slot 1",
+ "Fourside Department Store - Tool Shop Slot 2",
+ "Fourside Department Store - Tool Shop Slot 3",
+ "Fourside Department Store - Tool Shop Slot 4",
+ "Fourside Department Store - Tool Shop Slot 5",
+ "Fourside Department Store - Tool Shop Slot 6",
+ "Fourside Department Store - Tool Shop Slot 7",
+ "Fourside Department Store - Shop Shop Slot 1",
+ "Fourside Department Store - Shop Shop Slot 2",
+ "Fourside Department Store - Shop Shop Slot 3",
+ "Fourside Department Store - Shop Shop Slot 4",
+ "Fourside Department Store - Food Shop Slot 1",
+ "Fourside Department Store - Food Shop Slot 2",
+ "Fourside Department Store - Food Shop Slot 3",
+ "Fourside Department Store - Food Shop Slot 4",
+ "Fourside Department Store - Food Shop Slot 5",
+ "Fourside Department Store - 2F Cart Slot 1",
+ "Fourside Department Store - 2F Cart Slot 2",
+ "Fourside Department Store - 2F Cart Slot 3",
+ "Fourside Department Store - 2F Cart Slot 4",
+ "Fourside Department Store - 2F Cart Slot 5",
+ "Fourside Department Store - 2F Cart Slot 6",
+ "Fourside Department Store - 2F Cart Slot 7",
+ "Fourside Department Store - Toys Shop Slot 1",
+ "Fourside Department Store - Toys Shop Slot 2",
+ "Fourside Department Store - Toys Shop Slot 3",
+ "Fourside Department Store - Toys Shop Slot 4",
+ "Fourside Department Store - Toys Shop Slot 5",
+ "Fourside Department Store - Toys Shop Slot 6",
+ "Fourside Department Store - Sports Shop Slot 1",
+ "Fourside Department Store - Sports Shop Slot 2",
+ "Fourside Department Store - Sports Shop Slot 3",
+ "Fourside Department Store - Sports Shop Slot 4",
+ "Fourside Department Store - Burger Shop Slot 1",
+ "Fourside Department Store - Burger Shop Slot 2",
+ "Fourside Department Store - Burger Shop Slot 3",
+ "Fourside Department Store - Burger Shop Slot 4",
+ "Fourside Department Store - Burger Shop Slot 5",
+ "Fourside Department Store - Arms Dealer Slot 1",
+ "Fourside Department Store - Arms Dealer Slot 2",
+ "Fourside Department Store - Arms Dealer Slot 3",
+ "Fourside Department Store - Arms Dealer Slot 4",
+ "Fourside Department Store - Arms Dealer Slot 5",
+ "Fourside - Northeast Alley Junk Shop Slot 1",
+ "Fourside - Northeast Alley Junk Shop Slot 2",
+ "Fourside - Northeast Alley Junk Shop Slot 3",
+ "Fourside - Northeast Alley Junk Shop Slot 4",
+ "Magicant - Shop Slot 1",
+ "Magicant - Shop Slot 2",
+ "Summers - Scam Shop Slot 1",
+ "Summers - Scam Shop Slot 2",
+ "Summers - Scam Shop Slot 3",
+ "Summers - Scam Shop Slot 4",
+ "Summers - Scam Shop Slot 5",
+ "Summers - Scam Shop Slot 6",
+ "Summers - Scam Shop Slot 7",
+ "Summers Harbor - Shop Slot 1",
+ "Summers Harbor - Shop Slot 2",
+ "Summers Harbor - Shop Slot 3",
+ "Summers Harbor - Shop Slot 4",
+ "Summers Harbor - Shop Slot 5",
+ "Summers Harbor - Shop Slot 6",
+ "Summers Harbor - Shop Slot 7",
+ "Summers Restaurant - Slot 1",
+ "Summers Restaurant - Slot 2",
+ "Summers Restaurant - Slot 3",
+ "Summers Restaurant - Slot 4",
+ "Summers Restaurant - Slot 5",
+ "Summers Restaurant - Slot 6",
+ "Scaraba - Indoors Shop Slot 1",
+ "Scaraba - Indoors Shop Slot 2",
+ "Scaraba - Indoors Shop Slot 3",
+ "Scaraba - Indoors Shop Slot 4",
+ "Scaraba - Indoors Shop Slot 5",
+ "Scaraba - Indoors Shop Slot 6",
+ "Scaraba Bazaar - Red Snake Carpet Slot 1",
+ "Scaraba Bazaar - Red Snake Carpet Slot 2",
+ "Scaraba Bazaar - Red Snake Carpet Slot 3",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 1",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 2",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 3",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 4",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 5",
+ "Scaraba Bazaar - Bottom Left Carpet Slot 6",
+ "Scaraba Hotel - Arms Dealer Slot 1",
+ "Scaraba Hotel - Arms Dealer Slot 2",
+ "Scaraba Hotel - Arms Dealer Slot 3",
+ "Scaraba Hotel - Arms Dealer Slot 4",
+ "Deep Darkness - Businessman Slot 1",
+ "Deep Darkness - Businessman Slot 2",
+ "Deep Darkness - Businessman Slot 3",
+ "Deep Darkness - Businessman Slot 4",
+ "Deep Darkness - Businessman Slot 5",
+ "Deep Darkness - Businessman Slot 6",
+ "Deep Darkness - Businessman Slot 7",
+ "Happy-Happy Village - Trust Shop Slot 1",
+ "Happy-Happy Village - Trust Shop Slot 2",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 1",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 2",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 3",
+ "Saturn Valley Shop - Post-Belch Saturn Slot 4",
+ "Scaraba - Southern Camel Shop Slot 1",
+ "Scaraba - Southern Camel Shop Slot 2",
+ "Scaraba - Southern Camel Shop Slot 3",
+ "Scaraba - Southern Camel Shop Slot 4",
+ "Scaraba - Southern Camel Shop Slot 5",
+ "Scaraba - Southern Camel Shop Slot 6",
+ "Scaraba - Southern Camel Shop Slot 7",
+ "Deep Darkness - Arms Dealer Slot 1",
+ "Deep Darkness - Arms Dealer Slot 2",
+ "Deep Darkness - Arms Dealer Slot 3",
+ "Deep Darkness - Arms Dealer Slot 4",
+ "Lost Underworld - Tenda Camp Shop Slot 1",
+ "Lost Underworld - Tenda Camp Shop Slot 2",
+ "Lost Underworld - Tenda Camp Shop Slot 3",
+ "Lost Underworld - Tenda Camp Shop Slot 4",
+ "Lost Underworld - Tenda Camp Shop Slot 5",
+ "Lost Underworld - Tenda Camp Shop Slot 6",
+ "Lost Underworld - Tenda Camp Shop Slot 7",
+ "Happy-Happy Village Drugstore - Left Counter Slot 1",
+ "Happy-Happy Village Drugstore - Left Counter Slot 2",
+ "Happy-Happy Village Drugstore - Left Counter Slot 3",
+ "Happy-Happy Village Drugstore - Left Counter Slot 4",
+ "Happy-Happy Village Drugstore - Left Counter Slot 5",
+ "Happy-Happy Village Drugstore - Left Counter Slot 6",
+ "Happy-Happy Village Drugstore - Left Counter Slot 7",
+ "Grapefruit Falls - Hiker Shop Slot 1",
+ "Grapefruit Falls - Hiker Shop Slot 2",
+ "Grapefruit Falls - Hiker Shop Slot 3",
+ "Saturn Valley Shop - Top Saturn Slot 1",
+ "Saturn Valley Shop - Top Saturn Slot 2",
+ "Saturn Valley Shop - Top Saturn Slot 3",
+ "Saturn Valley Shop - Top Saturn Slot 4",
+ "Saturn Valley Shop - Top Saturn Slot 5",
+ "Saturn Valley Shop - Top Saturn Slot 6",
+ "Saturn Valley Shop - Top Saturn Slot 7",
+ "Dusty Dunes Drugstore - Left Shop Slot 1",
+ "Dusty Dunes Drugstore - Left Shop Slot 2",
+ "Dusty Dunes Drugstore - Left Shop Slot 3",
+ "Dusty Dunes Drugstore - Left Shop Slot 4",
+ "Dusty Dunes Drugstore - Left Shop Slot 5",
+ "Dusty Dunes Drugstore - Left Shop Slot 6",
+ "Dusty Dunes Drugstore - Left Shop Slot 7",
+ "Dusty Dunes - Mine Food Cart Slot 1",
+ "Dusty Dunes - Mine Food Cart Slot 2",
+ "Dusty Dunes - Mine Food Cart Slot 3",
+ "Dusty Dunes - Mine Food Cart Slot 4",
+ "Dusty Dunes - Mine Food Cart Slot 5",
+ "Dusty Dunes - Mine Food Cart Slot 6",
+ "Dusty Dunes - Mine Food Cart Slot 7",
+ "Moonside Hotel - Shop Slot 1",
+ "Moonside Hotel - Shop Slot 2",
+ "Moonside Hotel - Shop Slot 3",
+ "Moonside Hotel - Shop Slot 4",
+ "Moonside Hotel - Shop Slot 5",
+ "Dalaam Restaurant - Slot 1",
+ "Dalaam Restaurant - Slot 2",
+ "Dalaam Restaurant - Slot 3",
+ "Dalaam Restaurant - Slot 4",
+ "Scaraba Bazaar - Delicacy Shop Slot 1",
+ "Scaraba Bazaar - Delicacy Shop Slot 2",
+ "Scaraba Bazaar - Delicacy Shop Slot 3",
+ "Scaraba Bazaar - Delicacy Shop Slot 4",
+ "Scaraba Bazaar - Delicacy Shop Slot 5",
+ "Scaraba Bazaar - Delicacy Shop Slot 6",
+ "Scaraba Bazaar - Delicacy Shop Slot 7",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 1",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 2",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 3",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 4",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 5",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 6",
+ "Twoson/Scaraba - Shared Condiment Shop Slot 7",
+ "Andonuts Lab - Caveman Shop Slot 1",
+ "Andonuts Lab - Caveman Shop Slot 2",
+ "Andonuts Lab - Caveman Shop Slot 3",
+ "Andonuts Lab - Caveman Shop Slot 4",
+ "Andonuts Lab - Caveman Shop Slot 5"
+}
+
+
+def write_shop_checks(world: "EarthBoundWorld", rom: "LocalRom", shop_checks: list[Location]) -> None:
+ unsellable_filler_prices = {
+ "Broken Machine": 150,
+ "Broken Air Gun": 110,
+ "Broken Laser": 250,
+ "Broken Pipe": 250,
+ "Broken Tube": 800,
+ "Broken Bazooka": 900,
+ "Broken Trumpet": 500,
+ "Broken Harmonica": 1500,
+ "Counter-PSI Unit": 300,
+ "Shield Killer": 500,
+ "Heavy Bazooka": 1800,
+ "Hungry HP-Sucker": 1600,
+ "Defense Shower": 1000,
+ "Neutralizer": 5000,
+ "Brain Stone": 2,
+ "Monkey's Love": 2
+ }
+ # Unique non-progression items that have no price by default. If they're on a shop,
+ # give them a base price. (prog items are handled by the "price" variable)
+ # Equipamizer already assigns prices to Equipment with no price, so they can be exempt.
+ if not world.options.armorizer:
+ unsellable_filler_prices["Cloak of Kings"] = 2000
+ unsellable_filler_prices["Diadem of Kings"] = 1500
+ unsellable_filler_prices["Bracer of Kings"] = 3500
+
+ if not world.options.weaponizer:
+ unsellable_filler_prices["Magicant Bat"] = 3000
+ unsellable_filler_prices["Legendary Bat"] = 5000
+ unsellable_filler_prices["Magnum Air Gun"] = 220
+ unsellable_filler_prices["Laser Gun"] = 500
+ unsellable_filler_prices["Baddest Beam"] = 3000
+
+ if world.options.shop_randomizer == ShopRandomizer.option_shopsanity:
+ rom.write_bytes(0x04FD77, bytearray([world.options.scout_shop_checks]))
+ for location in shop_checks:
+
+ flag = location.address - 0xEB1000
+ if location.item.player == world.player:
+ if world.options.remote_items:
+ if location.item.name in special_name_table or location.item.name in money_item_table:
+ item_type = 0x04
+ item_id = 0xAD
+ else:
+ item_type = 0x05
+ item_id = item_id_table[location.item.name]
+ else:
+ if location.item.name in psi_item_table:
+ item_type = 0x01
+ item_id = psi_item_table[location.item.name]
+ elif location.item.name == "Photograph" and location.item.player == world.player:
+ item_type = 0x06
+ item_id = 0xAD
+ elif location.item.name in character_item_table:
+ item_type = 0x02
+ item_id = character_item_table[location.item.name][0]
+ elif location.item.name in money_item_table:
+ item_type = 0x07
+ item_id = list(money_item_table).index(location.item.name) + 1
+ else:
+ item_type = 0x00
+ item_id = item_id_table[location.item.name]
+
+ if location.item.name in unsellable_filler_prices and location.item.player == world.player:
+ rom.write_bytes(0x15501A + (item_id_table[location.item.name] * 39),
+ struct.pack("H", unsellable_filler_prices[location.item.name]))
+
+ else:
+ item_type = 0x04
+ item_id = 0xAD
+
+ if ItemClassification.trap in location.item.classification:
+ price = 0
+ else:
+ price = world.random.randint(1, (75 * world.area_levels[location.parent_region.name]))
+
+ if location.parent_region.name in high_purchase_areas:
+ price = int(price / 1.5)
+
+ item_struct = struct.pack(' 78:
+ menu_item_name = menu_item_name[:-1]
+ pixel_length = calc_pixel_width(menu_item_name)
+ menu_item_name = text_encoder(menu_item_name, 0x30)
+ player_name = text_encoder(world.multiworld.get_player_name(location.item.player), 16)
+ player_name.append(0x00)
+ rom.write_bytes(0x341190 + (flag * 0x30), menu_item_name)
+ rom.write_bytes(0x3466D0 + (flag * 0x11), player_name)
+ rom.write_bytes(0x351100 + (flag * 127), menu_long_name)
+
+ rom.write_bytes(0x019DE5, struct.pack("I", 0xF007805C)) # Build the shop menus
+ rom.write_bytes(0x019E23, struct.pack("I", 0xF008465C)) # Display the item name
+ rom.write_bytes(0x019E8F, struct.pack("I", 0xF0094E5C)) # Display the item price
+ rom.write_bytes(0x011AC6, struct.pack("I", 0xF009985C)) # display the player name
+ rom.write_bytes(0x019EDD, struct.pack("I", 0xF00AA45C)) # Transfer the used data and player selection into a script for processing
+ rom.write_bytes(0x019ED3, struct.pack("I", 0xF00ADD5C)) # Display SOLD OUT
+ rom.write_bytes(0x019B66, struct.pack("I", 0xF00B0B5C)) # Prevent items for other players flashing the "you can equip this"
+ rom.write_bytes(0x019DA0, struct.pack("I", 0xF00B275C)) # Preserve the greyed out HP/PP palette
+
+ rom.write_bytes(0x05E0A9, struct.pack("I", 0xF4900008)) # Compare the price of the item with money on hand
+ rom.write_bytes(0x05E0B6, struct.pack("I", 0xF4905808)) # Display the item we bought and ask to confirm
+ # The player bought the item; set a flag and give it to them
+ rom.write_bytes(0x05E0CE, struct.pack("I", 0xF493C30A))
+ rom.write_bytes(0x05E0C8, struct.pack("I", 0xF493C3))
+ rom.write_bytes(0x05DF1E, struct.pack("I", 0xF496140A))
+ # Prevent the game from checking inventory space if not needed
+ rom.write_bytes(0x05E029, struct.pack("I", 0xF496340A))
+ rom.write_bytes(0x05E04C, struct.pack("I", 0xF496590A))
+ rom.write_bytes(0x05E1AE, struct.pack("I", 0xF00EB8)) # Post-shop cleanup
+
+ rom.write_bytes(0x05E1A5, struct.pack("I", 0xF00EAA))
+ rom.write_bytes(0x05E119, struct.pack("I", 0xF00EB1))
+ rom.write_bytes(0x05E0F2, struct.pack("I", 0xF00EC0))
+
+ rom.write_bytes(0x3407E0, bytearray([item_id_table[world.filler_shop[0]], 0x00, 0x00, 0x00, 0x49, 0x01]))
+ rom.write_bytes(0x3407E6, bytearray([item_id_table[world.filler_shop[1]], 0x00, 0x00, 0x00, 0x4A, 0x01]))
+
+ rom.write_bytes(0x3408DC, bytearray([0xE0, 0x00, 0x00, 0x00, 0xFF, 0xFF]))
+ rom.write_bytes(0x3408E2, bytearray([0x5D, 0x00, 0x00, 0x00, 0xFF, 0xFF]))
+ rom.write_bytes(0x3408E8, bytearray([0x5A, 0x00, 0x00, 0x00, 0xFF, 0xFF]))
+ rom.write_bytes(0x3408EE, bytearray([0x7F, 0x00, 0x00, 0x00, 0xFF, 0xFF]))
+ rom.write_bytes(0x3408F4, bytearray([0x5F, 0x00, 0x00, 0x00, 0xFF, 0xFF]))
+ rom.write_bytes(0x3408FA, bytearray([0x6C, 0x00, 0x00, 0x00, 0xFF, 0xFF]))
+ rom.write_bytes(0x340900, bytearray([0x8C, 0x00, 0x00, 0x00, 0xFF, 0xFF]))
+
+ if world.options.magicant_mode >= MagicantMode.option_alternate_goal: # 2
+ rom.write_bytes(0x3405E8, bytearray([item_id_table[world.magicant_junk[6]], 0x00, 0x00, 0x00, 0xF5, 0x00]))
+ rom.write_bytes(0x3405EE, bytearray([item_id_table[world.magicant_junk[7]], 0x00, 0x00, 0x00, 0xF6, 0x00]))
+ else:
+ filler_shop_items = world.filler_drops.copy()
+ filler_shop_items = [x for x in filler_shop_items if x not in [227, 228, 229, 230, 231, 0]]
+ for location in location_ids:
+ if location_ids[location] >= 0xEB1000:
+ slot = location_ids[location] - 0xEB1000
+ rom.write_bytes(0x1576B9 + slot, bytearray([world.random.choice(filler_shop_items)]))
+ rom.write_bytes(0x1576e3, bytearray([0xEF]))
+ rom.write_bytes(0x15779c, bytearray([0x5A]))
+ if world.options.monkey_caves_mode < MonkeyCavesMode.option_shop: # 2
+ rom.write_bytes(0x15776b, bytearray([0xE0]))
+ rom.write_bytes(0x157775, bytearray([0x8C]))
+ rom.write_bytes(0x157778, bytearray([0x6C]))
+ rom.write_bytes(0x157781, bytearray([0x5D]))
+ rom.write_bytes(0x157848, bytearray([0x7F])) # DD Drugstore left counter 1
+ rom.write_bytes(0x157802, bytearray([world.random.choice(filler_shop_items)]))
+ rom.write_bytes(0x157803, bytearray([world.random.choice(filler_shop_items)]))
diff --git a/worlds/earthbound/setup_game.py b/worlds/earthbound/setup_game.py
new file mode 100644
index 000000000000..19ba990c79f4
--- /dev/null
+++ b/worlds/earthbound/setup_game.py
@@ -0,0 +1,501 @@
+from .modules.flavor_data import random_flavors
+from .game_data.text_data import lumine_hall_text, eb_text_table, text_encoder
+from .game_data.local_data import item_id_table
+from .modules.psi_shuffle import shuffle_psi
+from .modules.boss_shuffle import initialize_bosses
+from .modules.enemy_shuffler import shuffle_enemies
+from .modules.dungeon_er import shuffle_dungeons
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+ from . import EarthBoundWorld
+
+def setup_gamevars(world: "EarthBoundWorld") -> None:
+ """Initialize or roll most world variables"""
+
+ world.slime_pile_wanted_item = world.random.choice([
+ "Cookie",
+ "Bag of Fries",
+ "Hamburger",
+ "Boiled Egg",
+ "Fresh Egg",
+ "Picnic Lunch",
+ "Pasta di Summers",
+ "Pizza",
+ "Chef's Special",
+ "Large Pizza",
+ "PSI Caramel",
+ "Magic Truffle",
+ "Brain Food Lunch",
+ "Rock Candy",
+ "Croissant",
+ "Bread Roll",
+ "Can of Fruit Juice",
+ "Royal Iced Tea",
+ "Protein Drink",
+ "Kraken Soup",
+ "Bottle of Water",
+ "Cold Remedy",
+ "Vial of Serum",
+ "IQ Capsule",
+ "Guts Capsule",
+ "Speed Capsule",
+ "Vital Capsule",
+ "Luck Capsule",
+ "Ketchup Packet",
+ "Sugar Packet",
+ "Tin of Cocoa",
+ "Carton of Cream",
+ "Sprig of Parsley",
+ "Jar of Hot Sauce",
+ "Salt Packet",
+ "Jar of Delisauce",
+ "Trout Yogurt",
+ "Banana",
+ "Calorie Stick",
+ "Gelato de Resort",
+ "Magic Tart",
+ "Cup of Noodles",
+ "Repel Sandwich",
+ "Repel Superwich",
+ "Lucky Sandwich",
+ "Cup of Coffee",
+ "Double Burger",
+ "Mammoth Burger",
+ "Peanut Cheese Bar",
+ "Piggy Jelly",
+ "Bowl of Rice Gruel",
+ "Bean Croquette",
+ "Molokheiya Soup",
+ "Plain Roll",
+ "Kabob",
+ "Plain Yogurt",
+ "Beef Jerky",
+ "Spicy Jerky",
+ "Luxury Jerky",
+ "Bottle of DXwater",
+ "Magic Pudding",
+ "Popsicle"])
+
+ if world.options.progressive_weapons:
+ for i in range(3):
+ world.common_gear.append("Progressive Bat")
+ world.common_gear.append("Progressive Gun")
+ world.common_gear.append("Progressive Fry Pan")
+ for i in range(3):
+ world.uncommon_gear.append("Progressive Bat")
+ world.uncommon_gear.append("Progressive Gun")
+ world.uncommon_gear.append("Progressive Fry Pan")
+ world.rare_gear.append("Progressive Bat")
+ world.rare_gear.append("Progressive Gun")
+ world.rare_gear.append("Progressive Fry Pan")
+ else:
+ world.common_gear.extend([
+ "Cracked Bat",
+ "Tee Ball Bat",
+ "Sand Lot Bat",
+ "Minor League Bat",
+ "Fry Pan",
+ "Thick Fry Pan",
+ "Deluxe Fry Pan",
+ "Toy Air Gun",
+ "Zip Gun"
+ ])
+
+ world.uncommon_gear.extend([
+ "Mr. Baseball Bat",
+ "T-Rex's Bat",
+ "Big League Bat",
+ "Chef's Fry Pan",
+ "Non-Stick Frypan",
+ "French Fry Pan",
+ "Hyper Beam",
+ "Crusher Beam"
+ ])
+
+ world.rare_gear.extend([
+ "Hall of Fame Bat",
+ "Ultimate Bat",
+ "Gutsy Bat",
+ "Casey Bat",
+ "Holy Fry Pan",
+ "Magic Fry Pan"
+ ])
+
+ if world.options.progressive_armor:
+ for i in range(3):
+ world.common_gear.append("Progressive Bracelet")
+ world.common_gear.append("Progressive Other")
+ for i in range(3):
+ world.uncommon_gear.append("Progressive Bracelet")
+ world.uncommon_gear.append("Progressive Other")
+ world.rare_gear.append("Progressive Bracelet")
+ world.rare_gear.append("Progressive Other")
+ else:
+ world.common_gear.extend([
+ "Cheap Bracelet",
+ "Copper Bracelet",
+ "Baseball Cap",
+ "Mr. Baseball Cap",
+ "Holmes Hat",
+ "Hard Hat",
+ "Coin of Defense"
+ ])
+
+ world.uncommon_gear.extend([
+ "Platinum Band",
+ "Diamond Band",
+ "Lucky Coin",
+ "Silver Bracelet",
+ "Gold Bracelet",
+ "Coin of Slumber",
+ "Coin of Silence"
+ ])
+
+ world.rare_gear.extend([
+ "Talisman Coin",
+ "Shiny Coin",
+ "Charm Coin"
+ ])
+
+ world.multiworld.push_precollected(world.create_item(world.starting_character))
+
+ valid_starts = 14
+ if world.options.magicant_mode != 00:
+ valid_starts -= 1
+
+ if world.options.random_start_location:
+ world.start_location = world.random.randint(1, valid_starts)
+ else:
+ world.start_location = 0
+
+ if world.options.prefixed_items:
+ world.multiworld.itempool.append(world.create_item("Counter-PSI Unit"))
+ world.multiworld.itempool.append(world.create_item("Shield Killer"))
+ world.multiworld.itempool.append(world.create_item("Hungry HP-Sucker"))
+ world.multiworld.itempool.append(world.create_item("Defense Shower"))
+ world.multiworld.itempool.append(world.create_item("Heavy Bazooka"))
+ world.common_items.append("Defense Spray")
+ world.uncommon_items.append("Slime Generator")
+ if world.options.progressive_weapons:
+ for i in range(3):
+ world.multiworld.itempool.append(world.create_item("Progressive Gun"))
+ world.common_gear.append("Progressive Gun")
+ world.uncommon_gear.append("Progressive Gun")
+ world.rare_gear.append("Progressive Gun")
+ else:
+ world.multiworld.itempool.append(world.create_item("Magnum Air Gun"))
+ world.multiworld.itempool.append(world.create_item("Laser Gun"))
+ world.multiworld.itempool.append(world.create_item("Baddest Beam"))
+ world.uncommon_gear.append("Spectrum Beam")
+ world.rare_gear.append("Gaia Beam")
+ world.common_gear.append("Double Beam")
+ else:
+ world.multiworld.itempool.append(world.create_item("Broken Machine"))
+ world.multiworld.itempool.append(world.create_item("Broken Pipe"))
+ world.multiworld.itempool.append(world.create_item("Broken Tube"))
+ world.multiworld.itempool.append(world.create_item("Broken Trumpet"))
+ world.multiworld.itempool.append(world.create_item("Broken Bazooka"))
+ world.common_items.append("Broken Spray Can")
+ world.uncommon_items.append("Broken Iron")
+
+ if world.options.progressive_weapons:
+ for i in range(3):
+ world.multiworld.itempool.append(world.create_item("Progressive Gun"))
+ world.common_gear.append("Progressive Gun")
+ world.uncommon_gear.append("Progressive Gun")
+ world.rare_gear.append("Progressive Gun")
+ else:
+ world.multiworld.itempool.append(world.create_item("Broken Air Gun"))
+ world.multiworld.itempool.append(world.create_item("Broken Laser"))
+ world.multiworld.itempool.append(world.create_item("Broken Harmonica"))
+ world.common_gear.append("Broken Gadget")
+ world.uncommon_gear.append("Broken Cannon")
+ world.rare_gear.append("Broken Antenna")
+
+ for i in range(world.options.total_photos):
+ world.multiworld.itempool.append(world.create_item("Photograph"))
+ world.event_count += 1
+
+ world.franklinbadge_elements = [
+ "thunder",
+ "fire",
+ "freeze",
+ "flash",
+ "starstorm",
+ "special",
+ "explosive"
+ ]
+
+ world.starting_progressive_bats = 0
+ world.starting_progressive_pans = 0
+ world.starting_progressive_guns = 0
+ world.starting_progressive_bracelets = 0
+ world.starting_progressive_others = 0
+
+ if world.options.prefixed_items:
+ world.broken_guns = [
+ "Magnum Air Gun",
+ "Laser Gun",
+ "Double Beam",
+ "Spectrum Beam",
+ "Baddest Beam",
+ "Gaia Beam"
+ ]
+ else:
+ world.broken_guns = [
+ "Broken Air Gun",
+ "Broken Laser",
+ "Broken Gadget",
+ "Broken Cannon",
+ "Broken Harmonica",
+ "Broken Antenna"
+ ]
+
+ world.bats = [
+ "Sand Lot Bat",
+ "Minor League Bat",
+ "Mr. Baseball Bat",
+ "T-Rex's Bat",
+ "Big League Bat",
+ "Hall of Fame Bat",
+ "Casey Bat",
+ "Magicant Bat",
+ "Legendary Bat"
+ ]
+
+ world.pans = [
+ "Fry Pan",
+ "Thick Fry Pan",
+ "Deluxe Fry Pan",
+ "Chef's Fry Pan",
+ "Non-Stick Fry Pan",
+ "French Fry Pan",
+ "Holy Fry Pan",
+ "Magic Fry Pan"
+ ]
+
+ world.guns = [
+ "Pop Gun",
+ "Stun Gun",
+ "Toy Air Gun",
+ world.broken_guns[0],
+ "Zip Gun",
+ world.broken_guns[1],
+ "Hyper Beam",
+ world.broken_guns[2],
+ "Crusher Beam",
+ world.broken_guns[3],
+ "Death Ray",
+ world.broken_guns[4],
+ "Moon Beam Gun",
+ world.broken_guns[5]
+ ]
+
+ world.bracelets = [
+ "Cheap Bracelet",
+ "Copper Bracelet",
+ "Silver Bracelet",
+ "Gold Bracelet",
+ "Platinum Band",
+ "Diamond Band",
+ "Pixie's Bracelet",
+ "Cherub's Band",
+ "Goddess Band"
+ ]
+
+ world.others = [
+ "Baseball Cap",
+ "Mr. Baseball Cap",
+ "Holmes Hat",
+ "Hard Hat",
+ "Coin of Slumber",
+ "Coin of Defense",
+ "Coin of Slience"
+ "Mr. Saturn Coin",
+ "Charm Coin",
+ "Lucky Coin",
+ "Talisman Coin",
+ "Shiny Coin",
+ "Souvenir Coin"
+
+ ]
+
+ world.progressive_item_groups = {
+ "Progressive Bat": world.bats,
+ "Progressive Fry Pan": world.pans,
+ "Progressive Gun": world.guns,
+ "Progressive Bracelet": world.bracelets,
+ "Progressive Other": world.others
+ }
+
+ world.start_prog_counts = {
+ "Progressive Bat": world.starting_progressive_bats,
+ "Progressive Fry Pan": world.starting_progressive_pans,
+ "Progressive Gun": world.starting_progressive_guns,
+ "Progressive Bracelet": world.starting_progressive_bracelets,
+ "Progressive Other": world.starting_progressive_others
+ }
+
+ if world.options.randomize_franklinbadge_protection:
+ world.franklin_protection = world.random.choice(world.franklinbadge_elements)
+ else:
+ world.franklin_protection = "thunder"
+
+ if world.options.random_start_location:
+ world.valid_teleports = [
+ "Onett Teleport",
+ "Twoson Teleport",
+ "Happy-Happy Village Teleport",
+ "Threed Teleport",
+ "Saturn Valley Teleport",
+ "Fourside Teleport",
+ "Winters Teleport",
+ "Summers Teleport",
+ "Dalaam Teleport",
+ "Scaraba Teleport",
+ "Deep Darkness Teleport",
+ "Tenda Village Teleport",
+ "Lost Underworld Teleport"
+ ]
+
+ if world.options.magicant_mode == 0:
+ world.valid_teleports.append("Magicant Teleport")
+
+ del world.valid_teleports[world.start_location - 1]
+
+ world.starting_teleport = world.random.choice(world.valid_teleports)
+ world.multiworld.push_precollected(world.create_item(world.starting_teleport))
+
+ filler_items = (world.common_items + world.uncommon_items + world.rare_items + world.common_gear +
+ world.uncommon_gear + world.rare_gear)
+
+ if world.options.progressive_weapons:
+ remove_items = {"Progressive Bat", "Progressive Fry Pan", "Progressive Gun"}
+ filler_items = [item for item in filler_items if item not in remove_items]
+
+ if world.options.progressive_armor:
+ remove_items = {"Progressive Bracelet", "Progressive Other"}
+ filler_items = [item for item in filler_items if item not in remove_items]
+
+ world.filler_drops = [item_id_table[i] for i in filler_items if i in item_id_table]
+ world.filler_drops.append(0x00)
+ if world.options.prefixed_items:
+ world.filler_drops.extend([0xA1, 0xD7, 0x8A, 0x2C, 0x30])
+ else:
+ world.filler_drops.extend([0x07, 0x05, 0x09, 0x0B, 0x10])
+
+ world.filler_shop = []
+ if world.options.magicant_mode.value >= 2:
+ world.magicant_junk = []
+ for i in range(8):
+ world.magicant_junk.append(world.random.choice(filler_items))
+ for i in range(2):
+ world.filler_shop.append(world.random.choice(filler_items))
+
+ world.available_flavors = []
+ if world.options.random_flavors:
+ for i in range(4):
+ world.available_flavors = world.random.sample(random_flavors, 4)
+ else:
+ world.available_flavors = [
+ "Mint flavor",
+ "Strawberry flavor",
+ "Banana flavor",
+ "Peanut flavor"
+ ]
+
+ world.lumine_text = []
+ world.prayer_player = []
+ if world.options.plando_lumine_hall_text == "":
+ lumine_str = world.random.choice(lumine_hall_text)
+ else:
+ lumine_str = world.options.plando_lumine_hall_text.value
+
+ for char in lumine_str[:213]:
+ world.lumine_text.extend(eb_text_table[char])
+ world.lumine_text.extend([0x00])
+ world.starting_money = world.options.starting_money.value
+
+ # todo; move to text converter
+ prayer_player = world.multiworld.get_player_name(world.random.randint(1, world.multiworld.players))
+ for char in prayer_player[:24]:
+ if char in eb_text_table:
+ world.prayer_player.extend(eb_text_table[char])
+ else:
+ world.prayer_player.extend([0x6F])
+ world.prayer_player.extend([0x00])
+
+ world.credits_player = world.multiworld.get_player_name(world.player)
+ world.credits_player = text_encoder(world.credits_player, 16)
+ world.credits_player.extend([0x00])
+ shuffle_psi(world)
+ initialize_bosses(world)
+ shuffle_enemies(world)
+ shuffle_dungeons(world)
+
+
+def place_static_items(world: "EarthBoundWorld") -> None:
+ """Places all locked items. Some are events. Some are filler items that
+ need to be placed depending on certain settings."""
+
+ world.get_location("Belch Defeated").place_locked_item(world.create_item("Threed Tunnels Clear"))
+ world.get_location("Dungeon Man Submarine").place_locked_item(world.create_item("Submarine to Deep Darkness"))
+ world.get_location("Any ATM").place_locked_item(world.create_item("ATM Access"))
+
+ world.get_location("Giant Step Sanctuary").place_locked_item(world.create_item("Melody"))
+ world.get_location("Lilliput Steps Sanctuary").place_locked_item(world.create_item("Melody"))
+ world.get_location("Milky Well Sanctuary").place_locked_item(world.create_item("Melody"))
+ world.get_location("Rainy Circle Sanctuary").place_locked_item(world.create_item("Melody"))
+ world.get_location("Magnet Hill Sanctuary").place_locked_item(world.create_item("Melody"))
+ world.get_location("Pink Cloud Sanctuary").place_locked_item(world.create_item("Melody"))
+ world.get_location("Lumine Hall Sanctuary").place_locked_item(world.create_item("Melody"))
+ world.get_location("Fire Spring Sanctuary").place_locked_item(world.create_item("Melody"))
+
+ world.get_location("Carpainter Defeated").place_locked_item(world.create_item("Valley Bridge Repair"))
+
+ if world.options.giygas_required:
+ world.get_location("Giygas").place_locked_item(world.create_item("Saved Earth")) # Normal final boss
+ if world.options.magicant_mode == 1:
+ # If required magicant
+ world.get_location("Magicant - Ness's Nightmare").place_locked_item(world.create_item("Power of the Earth"))
+ world.get_location("Sanctuary Goal").place_locked_item(world.create_item("Magicant Unlock"))
+ else:
+ # If not required, place this condition on sanctuary goal
+ world.get_location("Sanctuary Goal").place_locked_item(world.create_item("Power of the Earth"))
+ else:
+ if world.options.magicant_mode == 1:
+ # If Magicant required but not Giygas, place goal
+ world.get_location("Magicant - Ness's Nightmare").place_locked_item(world.create_item("Saved Earth"))
+ world.get_location("Sanctuary Goal").place_locked_item(world.create_item("Magicant Unlock"))
+ else:
+ # If neither final boss, place goal
+ world.get_location("Sanctuary Goal").place_locked_item(world.create_item("Saved Earth"))
+
+ if world.options.alternate_sanctuary_goal:
+ world.get_location("+2 Sanctuaries").place_locked_item(world.create_item("Alternate Goal"))
+
+ if world.options.magicant_mode == 2:
+ world.get_location("+1 Sanctuary").place_locked_item(world.create_item("Magicant Unlock"))
+ world.get_location("Magicant - Ness's Nightmare").place_locked_item(world.create_item("Alternate Goal"))
+
+ if not world.options.monkey_caves_mode:
+ world.get_location("Monkey Caves - 1F Right Chest").place_locked_item(world.create_item("Wet Towel"))
+ world.get_location("Monkey Caves - 1F Left Chest").place_locked_item(world.create_item("Pizza"))
+ world.get_location("Monkey Caves - West 2F Left Chest").place_locked_item(world.create_item("Pizza"))
+ world.get_location("Monkey Caves - West 2F Right Chest #1").place_locked_item(world.create_item("Hamburger"))
+ world.get_location("Monkey Caves - West 2F Right Chest #2").place_locked_item(world.create_item("Ruler"))
+ world.get_location("Monkey Caves - East 2F Left Chest").place_locked_item(world.create_item("Protein Drink"))
+ world.get_location("Monkey Caves - East 2F Right Chest").place_locked_item(world.create_item("Hamburger"))
+ world.get_location("Monkey Caves - East West 3F Right Chest #1").place_locked_item(world.create_item("Hamburger"))
+ world.get_location("Monkey Caves - East West 3F Right Chest #2").place_locked_item(world.create_item("Picnic Lunch"))
+
+ if world.options.shop_randomizer == 2:
+ world.get_location("Twoson Department Store Bakery - Slot 1").place_locked_item(world.create_item("Plain Roll"))
+ world.get_location("Fourside Department Store - Burger Shop Slot 4").place_locked_item(world.create_item("Hamburger"))
+
+ if world.options.monkey_caves_mode < 2:
+ world.get_location("Fourside Bakery - Slot 4").place_locked_item(world.create_item("Repel Sandwich"))
+ world.get_location("Fourside Department Store - Tool Shop Slot 7").place_locked_item(world.create_item("Ruler"))
+ world.get_location("Fourside Department Store - Shop Shop Slot 3").place_locked_item(world.create_item("Protein Drink"))
+ world.get_location("Fourside Department Store - Food Shop Slot 5").place_locked_item(world.create_item("Picnic Lunch"))
+ world.get_location("Dusty Dunes Drugstore - Left Shop Slot 1").place_locked_item(world.create_item("Wet Towel"))
diff --git a/worlds/earthbound/src/earthbound_basepatch.bsdiff4 b/worlds/earthbound/src/earthbound_basepatch.bsdiff4
new file mode 100644
index 000000000000..9ba2b74a04d9
Binary files /dev/null and b/worlds/earthbound/src/earthbound_basepatch.bsdiff4 differ
diff --git a/worlds/earthbound/src/eb.asm b/worlds/earthbound/src/eb.asm
new file mode 100644
index 000000000000..1ae0aff35e6a
--- /dev/null
+++ b/worlds/earthbound/src/eb.asm
@@ -0,0 +1,18902 @@
+hirom
+ORG $C67F00
+CharLastInvSlot:
+dw $99FE,$9A5D,$9ABC,$9B1B
+
+TeleSectorID:
+dw $FFFF,$5A85,$5A91,$5AAD,$5AED,$5B09,$5B31,$5AD1,$5B4D,$5B5D,$5B79,$5B91,$5B99,$5BAD,$5AB1,$5B89,$5B29,$5A8D
+
+TeleFlags:
+dw $0001,$0002,$1000,$0004,$0010,$2000,$0020,$0008,$0040,$0100,$0080,$0400,$0800,$4000,$0004,$0200,$0020,$1000
+
+TeleportFlags:
+dw $0000,$00D1,$00D2,$00D3,$00D4,$00D5,$00D6,$00D7,$00D8,$00D9,$00DA,$00DB,$00DC,$00DD,$00DE,$00DF
+
+StartingScripts:
+dw $0000,$0068,$01A6,$0205,$01D5,$0014,$0022,$0064,$0177,$005E,$0062,$00C7,$00C8,$012F,$001F,$0279,$00AF,$0031,$000E,$011F,$0088,$00C9,$0095,$003B,$003C,$0220,$0137,$0027,$01C2,$0225,$000F
+
+UnsetScripts:
+dw $006B,$ED02,$000B,$005F
+
+CharUnlockPointers:
+dw $F830,$F837,$F83E,$F845,$F955,$F849, $F850
+
+ORG $03FE10
+db $1C,$02,$FE,$50,$9A,$9F,$99,$9E,$95,$94,$50,$A9,$9F,$A5,$A2,$50,$A0,$91,$A2,$A4,$A9,$51,$1F,$00,$00,$0B,$10,$78,$10,$78,$10,$78,$10,$3C,$1F,$03,$13,$02
+
+SoundStoneScripts:
+dw $00B6,$00B7,$00B8,$00B9,$00BA,$00BB,$00BC,$00BD
+
+ORG $D56A5B
+db $71,$a2,$93,$98,$99,$a0,$95,$9c,$91,$97,$9f,$50,$79,$a4,$95,$9d
+
+ORG $C4FD80
+db $03,$00,$e5,$04,$88,$80,$e2,$04,$c0,$38,$D3,$00,$72,$a4 ;Tenda Village door. adds guard
+db $01,$00,$DF,$01,$E5,$05 ;Threed cemetary hallway. Locks the door to the pit.
+db $02,$00,$9F,$00,$20,$4F,$A0,$00,$70,$00 ;Scraba south of pyramid. Adds shortcut NPCs
+db $02,$00,$c5, $02, $f8, $58, $6f, $02, $50, $00; Desert West tunnel. Adds ghost
+
+ORG $C4FE74
+db $01, $00, $25, $05, $41, $75
+
+ORG $CF9788
+db $01,$61,$00,$06,$5E,$02,$20,$01,$01,$26,$70,$C6,$00 ;Tenda guard NPC
+
+ORG $CF9415
+db $04,$01
+
+ORG $CF941C
+db $00, $0A, $00, $61
+
+ORG $C4FD50
+NewSectorPointers:
+dw $FD80,$FD8E,$FD94,$FD9E,$FDA8,$FDC2,$FDD0,$FE74,$FE80,$FEE0,$FEEA,$FF00, $FF0A
+
+ORG $D5F880
+SpecialNameTable:
+dw $A810, $A81F, $A82F, $A84C, $A85C, $A873, $A888, $A89A, $A8AB, $A8BC, $A8CD, $A8DD, $A8F4, $A90B, $A924, $A938, $A93E, $A943, $A947, #SpecialTexMagicant
+dw #SpecialTexNess
+dw #SpecialTexPhotoGuy
+dw #DisplayDollars10
+dw #DisplayDollars100
+dw #DisplayDollars1000
+
+
+
+ORG $D7FC7C
+StartingTeleports:
+
+ORG $D7FC8D
+StartingChars:
+
+macro FUNCTION_PROLOGUE(locals)
+ rep #$31
+ phd ; PUSH current frame pointer
+ pha ; Always push, in case this function takes an arg
+ tdc
+ adc.w #$FFEE
+ tcd
+ pla ; POP possible arg
+endmacro
+
+;2ff4b0 is freespace
+
+
+ORG $C04D46
+JML GetItemRemote
+
+ORG $C1FEAE
+JML InitIntro
+
+ORG $C06982
+JML SetTeleFlags
+
+ORG $C0D622
+JML IgnoreTeleportEnemies
+
+ORG $C0B8EE
+JML GetRemoteTeleport
+
+ORG $C14FD7
+JML GetCharNameFromServ
+
+ORG $C146D6
+JML GetPlayerName
+
+ORG $C14FDB
+JML LoadPlayerName
+
+ORG $C1938F
+JML DrawPlayerName
+
+ORG $C2165E
+JML SetMelodyCount
+
+ORG $C0B904
+JML PocketStorage
+
+ORG $C14D05
+JML SkipArchiSpaceCheck
+
+ORG $C156A9
+JML CheckArchiItemInv
+
+ORG $C19222
+JML GetServerItemName
+
+ORG $C02264
+JML AddNewNPCSectors
+
+ORG $C48402
+JML GetLumiText
+
+ORG $C18B5D
+JML DelArchFromInv
+
+ORG $C07960
+JML SetAnimSpeed
+
+ORG $C22A35
+;JML SaveAPData
+
+ORG $C14F56
+JML ExtraKeyItems
+
+ORG $C4844C
+JML GetLumiTextMain
+
+ORG $C486F4
+JML GetLumiTextSub
+
+ORG $C18B55
+;JML TestArchiItemSpace
+
+ORG $C1F06B
+JML LoadAPData
+
+ORG $C17DD4
+JML SpecialNameDirect
+
+ORG $C2164A
+JML RepelEnemies
+
+ORG $C1FEB2
+JML GetStartingData
+
+ORG $C1FEB6
+JML GetStartingNames
+
+ORG $C40BCE
+JML ResetGame
+
+ORG $C04AA6
+JML ClearNameOnBattle
+
+ORG $C1F299
+JSL CopyAPData
+
+ORG $C1F3B3
+JSL DeleteAPData
+
+ORG $C07902
+JMP MagicantSoloAnimSpeedFix
+
+;;;;;;;;;;;;;;;;;;;;;;;;;
+;Franklin badge checks
+ORG $C29549
+JML ReflectSpecial
+
+ORG $C29597
+JML ReflectFire
+
+ORG $C2901E
+JML ReflectFireBall
+
+ORG $C295F4
+JML ReflectFreeze
+
+ORG $C298B6
+JML ReflectFlash
+
+ORG $C29A90
+JML ReflectStarstorm
+
+ORG $C2A666
+JML ReflectExplode
+
+ORG $C2A66B
+JML SkipExplosionDeathReflect
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C0B967
+JSL PlayerJustDied
+
+ORG $C272C8
+JML CheckDeathHealing
+
+ORG $C272D0
+JML OverrideDeathlinkText
+
+ORG $C273B0
+JML OverrideDeathlinkHeal
+
+ORG $C25F16
+JSL SendDeathInBattle
+;Deathlink stuff
+;;;;;;;;;;;;;;;;;;;;;
+ORG $C18A1D
+JML ClearStoredAPItem
+
+ORG $C2AD2A
+JML PrayerReflectIgnore
+
+ORG $C1915F
+JML GetProgressiveGoingtoStorage
+
+ORG $C2E8ED
+JML GetNewSwirlColorNormal
+
+ORG $C2E91D
+JML GetNewSwirlColorGood
+
+ORG $C2E93A
+JML GetNewSwirlColorBad
+
+ORG $C2E94B
+JML PokeySwirl
+
+ORG $C0F67A
+JML GetSeedPlayer
+
+ORG $C1ED67
+JML DrawWorldVersion
+
+ORG $C1FD57
+JML Set24BitStartingEXP
+
+ORG $C0946C
+JML TransferGiftToInv
+
+ORG $C19A62
+JML GetDynamicWindowTitle
+
+ORG $C4FBBD
+JML GetRandomizedTrack
+
+ORG $C105C9
+JML LoadExpandedWindowTable
+
+ORG $C2A5B2
+JSL early_missile_damage
+
+ORG $C4F2E8
+JML ReadDynamicPhotoMapPositionY
+
+ORG $C4F2F1
+JML ReadDynamicPhotoMapPositionX
+
+ORG $C4F3E7
+JML ReadDynamicPhotoCharPosX
+
+ORG $C4F400
+JML ReadDynamicPhotoCharPosY
+
+ORG $C46D80
+JML SpawnDynamicPhotomanX
+
+ORG $C46D93
+JML SpawnDynamicPhotomanY
+
+ORG $C4F429
+JML IncrementCurPhotoCount
+
+ORG $C15F3A
+JML CheckTotalEnergy
+
+ORG $C192DE
+JML DisplayEnergy
+
+ORG $C2EF3D
+JML LoadEnemyToVram
+
+ORG $C2BDC1
+JML AllowDynamicCallEnemyLoad
+
+ORG $C2EFA2
+JML AllowEnemyToSpawn
+
+ORG $C2EFC6
+JML ForceLoadSingle
+
+ORG $C2EEEF
+JML InitializeLoadingEnemies
+
+ORG $C2EFF7
+JSL SetupVramForSpriteLoad
+
+ORG $C1F3D0
+JSL CloseVersionWindow
+
+ORG $C27F2C
+JSL FixGiygasReflect
+
+ORG $C13660
+JML HandleUnusableWarpPad
+
+ORG $C0089A
+JML LoadPhotoColor
+
+ORG $00FA22
+JML GetRemoteMoney
+
+ORG $C2A03F
+JML ClearStatusVars
+
+;new jmls
+
+
+ORG $C1FEBC
+LDA #$0000
+;Skip flyover
+
+ORG $C02F30
+LDA $0066
+AND #$00FF
+BIT #$0040
+BNE $03
+
+ORG $C031B8
+LDA $0066
+AND #$00FF
+BIT #$0040
+BNE $03
+
+ORG $D57E3B
+db $00
+
+ORG $D57E47
+db $00
+
+ORG $D7BAAE
+db $04
+
+ORG $D7BAAC
+db $04
+
+ORG $D7BAB0
+db $04
+
+ORG $D7BA28
+db $04
+
+ORG $D7BA68
+dw $0004,$0004
+
+ORG $D7BAA6
+dw $0004,$0004
+
+ORG $D7BB66
+dw $0004,$0004
+
+ORG $D7BB6C
+dw $0004,$0004
+
+ORG $D7BAE6
+dw $0004,$0004,$0004,$0004,$0004
+
+ORG $D7BB26
+dw $0004,$0004,$0004,$0004,$0004
+
+ORG $D7BAF0
+db $04
+
+ORG $D7BB30
+db $04
+
+ORG $D7BB70
+db $04
+
+; Flags for Tenda VIllage, was $02, now $00 to disable mice
+ORG $D7B200
+dw $0000, $0000
+
+ORG $D7B240
+dw $0000, $0000, $0000
+
+ORG $D7B280
+dw $0000, $0000, $0000
+
+ORG $D7B2C0
+dw $0000, $0000, $0000
+
+ORG $C13CF4
+NOP
+NOP
+
+ORG $C04575
+LDA #$0000
+
+ORG $C02F45
+NOP
+NOP
+NOP
+
+ORG $C031CD
+NOP
+NOP
+NOP
+
+ORG $C1FEBA
+NOP
+NOP
+
+ORG $C3E0E5
+dw $00F0
+
+ORG $C3E11C
+dw $A8E6
+
+ORG $C45631
+;db $00
+
+ORG $CF2DAC
+db $38
+
+ORG $CF2DB1
+db $38
+
+ORG $CF2DBB
+db $38
+
+ORG $CF2DBB
+db $38
+
+ORG $CF2DC0
+db $38
+
+ORG $CF2DC5
+db $38
+
+ORG $CF2DCA
+db $38
+
+ORG $CF2DD4
+db $38
+
+ORG $CF2ECD
+db $38
+
+ORG $CF2ED2
+db $38
+
+ORG $CF2ED7
+db $38
+
+ORG $CF2EDC
+db $38
+
+ORG $CF2EF0
+db $38
+
+ORG $CF2EEB
+db $38
+
+ORG $CF2EE1
+db $38
+
+ORG $CF2EE6
+db $38
+
+ORG $CF2EF5
+db $38
+
+ORG $C86281
+db $D1
+
+ORG $CF975D
+db $00
+
+ORG $C66A9F
+db $0A,$63,$A9,$EE
+
+ORG $C73948
+db $49
+
+ORG $C68C3F
+db $02
+
+ORG $C7BF86
+db $0A,$12,$9C,$EE,$00 ;Giant step redirect
+
+ORG $C7C056
+db $0A,$12,$9C,$EE,$00 ;Lilliput Steps redirect
+
+ORG $C7C1F5
+db $0A,$12,$9C,$EE,$00 ;Milky Well redirect
+
+ORG $C7C126
+db $0A, $12, $9C, $EE; Rainy Circle Redirect
+
+ORG $C7C2C0
+db $0A, $12, $9C, $EE ; Magnet Hill Redirect
+
+ORG $C7C388
+db $0A,$12,$9C,$EE ;Pink Cloud Redirect
+
+ORG $C7C476
+db $0A,$12,$9C,$EE ;Lumine Hall redirect
+
+ORG $C7C47A
+db $0A, $C6, $A0, $EE
+
+ORG $EEA0C6
+db $1F, $15, $6a, $00, $60, $01, $01, $0A, $80, $C4, $C7; Fix stupid lumine hole stuff
+
+ORG $C7C538
+db $0A,$12,$9C,$EE ;Fire Spring redirect
+
+ORG $C5F228
+db $07,$D2,$03,$1B,$03,$40,$F2,$C5,$00,$1F,$02,$71,$13,$18,$04,$10,$20,$04,$D2,$03,$0A,$71,$92,$FE,$13,$02
+
+ORG $C95AA1
+db $D2,$00
+
+ORG $C95AB7
+db $00
+
+ORG $C95B5B
+db $07
+
+ORG $CFA96D
+db $01
+
+ORG $CFA98F
+db $01
+
+ORG $C91C68
+db $0A, $C1, $1C, $C9
+
+ORG $C7699E
+db $1D, $05, $FF, $CA, $1B, $02, $07, $42, $C7, $00, $70, $58, $1C, $02, $01, $50
+db $98, $91, $9E, $94, $95, $94, $50, $9F, $A6, $95, $A2, $50, $A4, $98, $95, $50
+db $1C, $05, $CA, $5E, $59, $1F, $02, $76, $10, $40, $00, $70, $89, $7F, $85, $50
+db $98, $91, $94, $50, $99, $A4, $50, $A4, $98, $99, $A3, $50, $A7, $98, $9F, $9C
+db $95, $50, $A4, $99, $9D, $95, $6F, $51, $03, $00, $70, $7A, $A5, $A3, $A4, $50
+db $A7, $98, $95, $9E, $50, $79, $50, $A7, $91, $A3, $50, $91, $92, $9F, $A5, $A4
+db $50, $A4, $9F, $00, $50, $50, $97, $9F, $50, $97, $95, $A4, $50, $9C, $A5, $9E
+db $93, $98, $5C, $50, $A4, $9F, $9F, $5E, $5E, $5E, $03, $00, $70, $73, $9F, $9D
+db $95, $50, $A7, $99, $A4, $98, $50, $9D, $95, $5E, $5E, $5E, $03, $50, $79, $57
+db $9C, $9C, $50, $A3, $98, $9F, $A7, $50, $A9, $9F, $A5, $00, $50, $50, $A3, $9F
+db $9D, $95, $50, $A2, $95, $91, $9C, $50, $6C, $A0, $A2, $9F, $97, $A2, $95, $A3
+db $A3, $99, $9F, $9E, $6E, $5C, $50, $91, $9C, $A2, $99, $97, $98, $A4, $51, $13
+db $1D, $01, $FF, $CA, $04, $1B, $02, $0A, $F1, $6A, $C7, $00 ;Captain strong pre-fight text
+
+ORG $C76A6D
+db $70, $75, $A6, $95, $9E, $50, $91, $96, $A4, $95, $A2, $50, $91, $9C, $9C, $50
+db $A4, $98, $99, $A3, $50, $A4, $99, $9D, $95, $5C, $10, $14, $50, $50, $79, $57
+db $9D, $50, $83, $84, $79, $7C, $7C, $50, $99, $9E, $50, $72, $7B, $51, $13, $0A
+db $F1, $6A, $C7, $00, $C7, $00, $A7 ;Captain Strong rematch text
+
+ORG $C74207
+db $07, $69, $00, $1B, $03, $0B, $6B, $C7, $00, $07, $1B, $02, $1B, $03, $6D, $6A
+db $C7, $00, $70, $77, $A2, $A2, $5E, $03, $50, $79, $57, $A6, $95, $50, $92, $95
+db $95, $9E, $50, $A3, $A4, $A5, $93, $9B, $50, $99, $9E, $50, $72, $7B, $50, $9D
+db $9F, $94, $95, $50, $96, $9F, $A2, $50, $10, $10, $A4, $98, $A2, $95, $95, $50
+db $10, $10, $98, $9F, $A5, $A2, $A3, $5C, $03, $00, $50, $50, $A7, $91, $99, $A4
+db $99, $9E, $97, $50, $9F, $9E, $50, $9D, $A9, $50, $1C, $05, $CA, $51, $00, $70
+db $7C, $95, $A4, $50, $9D, $95, $50, $9B, $9E, $9F, $A7, $50, $99, $96, $50, $A9
+db $9F, $A5, $50, $A3, $95, $95, $50, $99, $A4, $5E, $03, $00, $70, $7D, $91, $A9
+db $92, $95, $50, $79, $50, $A3, $98, $9F, $A5, $9C, $94, $57, $A6, $95, $50, $A0
+db $9C, $91, $A9, $95, $94, $50, $86, $86, $86, $86, $86, $86, $50, $99, $9E, $A3
+db $A4, $95, $91, $94, $5E, $5E, $5E, $13, $02 ;Captain strong normal text
+
+ORG $EEC593
+db $70, $71, $93, $A4, $A5, $91, $9C, $9C, $A9, $5C, $50, $79, $57, $9D, $50, $9F
+db $9e, $95, $50, $9f, $96, $50, $a4, $98, $95, $50, $aa, $9f, $9d, $92, $99, $95
+db $a3, $5e, $03, $00, $70, $79, $50, $a7, $91, $a3, $50, $a3, $9f, $50, $92, $91
+db $94, $50, $91, $A4, $50, $92, $95, $99, $9E, $97, $50, $91, $50, $AA, $9F, $9D
+db $92, $99, $95, $5C, $10, $15, $50, $A4, $98, $95, $A9, $50, $A4, $98, $A2, $95
+db $a7, $50, $9d, $95, $50, $99, $9e, $50, $98, $95, $a2, $95, $51, $13, $02 ;Threed zombie meme text
+
+ORG $EE9A94
+db $70, $7f, $98, $5c, $50, $98, $95, $a9, $5c, $10, $15, $50, $1c, $02, $01, $5e
+db $03, $00, $70, $79, $50, $94, $a2, $9f, $a6, $95, $50, $a4, $98, $95, $50, $83
+db $9b, $a9, $50, $82, $a5, $9e, $9e, $95, $a2, $50, $98, $95, $a2, $95, $50, $a4
+db $9f, $50, $a3, $91, $a6, $95, $50, $a9, $9f, $a5, $50, $96, $a2, $9f, $9d, $50
+db $a4, $98, $95, $50, $aa, $9f, $9d, $92, $99, $95, $a3, $5c, $03, $00, $70, $92
+db $a5, $a4, $50, $99, $a4, $50, $9c, $9f, $9f, $9b, $a3, $50, $9c, $99, $9b, $95
+db $50, $79, $50, $a7, $91, $a3, $50, $a4, $9f, $9f, $50, $95, $91, $a2, $9c, $a9
+db $5e, $03, $00, $70, $7f, $98, $5c, $50, $a7, $95, $9c, $9c, $5e, $03, $00, $70
+db $79, $50, $97, $a5, $95, $a3, $a3, $50, $a9, $9f, $a5, $50, $a0, $a2, $9f, $92
+db $91, $92, $9c, $a9, $50, $a3, $98, $9f, $a5, $9c, $94, $9e, $57, $a4, $50, $a4
+db $a2, $a5, $a3, $a4, $50, $a2, $91, $9e, $94, $9f, $9d, $50, $a0, $a3, $a9, $93
+db $98, $99, $93, $50, $9d, $95, $a3, $a3, $91, $97, $95, $a3, $5e, $03, $00, $70
+db $75, $a3, $a0, $95, $93, $99, $91, $9c, $9c, $a9, $50, $9f, $9e, $95, $a3, $50
+db $96, $a2, $9f, $9d, $50, $aa, $9f, $9d, $92, $99, $95, $a3, $5e, $03, $00, $70
+db $84, $98, $91, $9e, $9b, $a3, $50, $96, $9f, $a2, $50, $a3, $91, $a6, $99, $9e
+db $97, $50, $9d, $95, $5c, $10, $15, $50, $92, $a9, $50, $a4, $98, $95, $50, $a7
+db $91, $a9, $5e, $03, $00, $0A, $79, $AF, $EE, $00, $1b, $03, $B3, $9B, $EE, $00
+db $1f, $1e, $e1, $01, $06, $04, $2f, $00, $02;Threed character
+
+ORG $D5F710
+PaulaUnlocktext:
+db $08
+dd $C7966C
+db $18, $04, $1f, $11, $02, $1f, $00, $00, $0b
+db $10, $78, $50, $08, $10, $F8, $D5, $00, $1f, $03, $18, $01, $01, $70, $1c, $02
+db $02, $50, $9a, $9f, $16, $b9, $94, $15, $a8, $03, $18, $04, $02; Paula Join
+
+PooUnlockText:
+db $08
+dd $C929CD
+db $18, $04, $1f, $11, $04, $1f, $00, $00, $0b, $10, $78, $50
+db $08, $10, $F8, $D5, $00, $1f, $03, $18, $01, $01, $70, $1c, $02, $04, $50, $9a
+db $9f, $16, $b9, $94, $15, $a8, $03, $01, $08, $6B, $F7, $D5, $FF, $18, $04, $02; Poo join
+
+ORG $C884D0
+JeffUnlockText:
+db $08
+dd $C88533
+db $13, $18, $04, $1f, $00, $00, $0b, $10, $78, $50, $08, $10
+db $F8, $D5, $00, $1f, $03, $18, $01, $01, $70, $1c, $02, $03, $50, $9a, $9f, $16
+db $b9, $94, $15, $a8, $03, $18, $04, $1f, $11, $03, $02; Jeff join
+
+FlyingManUnlockText:
+db $08
+dd $C76FCE
+db $18, $04, $08, $1A, $F8, $D5, $00, $1F, $11, $0B, $02; FlyingMan join
+
+ORG $C75C33
+db $70, $79, $57, $9d, $50, $a4, $98, $95, $50, $1c, $05, $03, $51, $03, $00, $70
+db $79, $50, $9b, $9e, $9f, $a7, $50, $79, $50, $9d, $91, $a9, $50, $9e, $9f, $a4
+db $50, $9c, $9f, $9f, $9b, $50, $9c, $99, $9b, $95, $50, $9d, $a5, $93, $98, $5c
+db $10, $15, $50, $92, $a5, $a4, $50, $79, $57, $9d, $50, $a3, $a4, $a2, $9f, $9e
+db $97, $95, $a2, $50, $a4, $98, $91, $9e, $50, $a4, $98, $95, $50, $91, $a6, $95
+db $a2, $91, $97, $95, $50, $92, $95, $91, $a2, $51, $03, $1d, $03, $ff, $1b, $02
+db $bb, $5c, $c7, $00, $00, $50, $50, $58, $84, $98, $95, $50, $1c, $05, $03, $50
+db $9a, $9f, $99, $9e, $a3, $50, $a9, $9f, $a5, $5e, $59, $1f, $02, $10, $10, $30
+db $1d, $00, $ff, $03, $1f, $41, $13, $02, $00, $70, $89, $9f, $a5, $50, $94, $9f
+db $9e, $57, $a4, $50, $98, $91, $a6, $95, $50, $a2, $9f, $9f, $9d, $50, $96, $9f
+db $a2, $50, $9d, $95, $5c, $10, $15, $50, $a4, $98, $9f, $a5, $97, $98, $5e, $10
+db $20, $50, $79, $57, $9c, $9c, $50, $a7, $91, $99, $a4, $50, $98, $95, $a2, $95
+db $50, $a5, $9e, $a4, $99, $9c, $50, $a9, $9f, $a5, $50, $9d, $91, $9b, $95, $50
+db $a3, $9f, $9d, $95, $50, $a3, $a0, $91, $93, $95, $5e, $07, $68, $00, $13, $02; SuperBear join
+
+db $70, $84, $98, $91, $9e, $9b, $50, $a9, $9f, $a5, $50, $96, $9f, $a2, $50, $96
+db $99, $9e, $94, $99, $9e, $97, $50, $9d, $95, $5e, $03, $00, $70, $79, $50, $9b
+db $9e, $9f, $a7, $50, $a4, $98, $99, $a3, $50, $9d, $91, $a9, $50, $92, $95, $50
+db $98, $91, $a2, $94, $50, $a4, $9f, $50, $92, $95, $9c, $99, $95, $a6, $95, $5c
+db $10, $15, $50, $92, $a5, $a4, $50, $79, $57, $9d, $50, $91, $93, $a4, $a5, $91
+db $9c, $9c, $a9, $50, $91, $50, $a2, $95, $9c, $99, $93, $50, $96, $a2, $9f, $9d
+db $50, $91, $50, $96, $91, $a2, $5d, $91, $a7, $91, $a9, $50, $a7, $9f, $a2, $9c
+db $94, $5e, $03, $00, $70, $7d, $a9, $50, $a4, $a2, $a5, $95, $50, $96, $9f, $a2
+db $9d, $50, $99, $a3, $50, $a4, $98, $91, $a4, $50, $9f, $96, $50, $91, $50, $1c
+db $05, $ad, $5e, $03, $00, $70, $79, $9e, $50, $91, $9e, $9f, $a4, $98, $95, $a2
+db $50, $a4, $99, $9d, $95, $50, $91, $9e, $94, $50, $a0, $9c, $91, $93, $95, $5c
+db $10, $15, $50, $a7, $95, $50, $9d, $91, $a9, $50, $9d, $95, $95, $a4, $50, $91
+db $97, $91, $99, $9e, $5e, $03, $00, $70, $85, $9e, $a4, $99, $9c, $50, $a4, $98
+db $95, $9e, $5c, $10, $15, $50, $1c, $02, $fe, $50, $a3, $95, $9e, $94, $a3, $50
+db $a4, $98, $95, $99, $a2, $50, $a2, $95, $97, $91, $a2, $94, $a3, $51, $13, $02;AP Item join
+
+ORG $C91D5E
+db $70, $7c, $9f, $9f, $9b, $a3, $50, $9c, $99, $9b, $95, $50, $99, $a4, $57, $a3, $50
+db $a2, $95, $91, $94, $a9, $50, $a4, $9f, $50, $97, $9f, $50, $a4, $9f, $50, $87
+db $99, $9e, $a4, $95, $a2, $a3, $5e, $03, $00, $70, $0a, $33, $1e, $c9, $00;Fixed Sky Runner
+
+ORG $C91CC1
+db $0A, $03,$5E,$C7,$00
+
+ORG $C91E94
+;db $ed, $1d, $05, $ff, $9e, $1d, $01, $ff, $9e, $0A, $2C, $20, $C9, $00
+
+ORG $CFA949
+db $47,$00,$01
+
+ORG $CFA93A
+db $02
+
+ORG $CF8915
+db $20,$A8
+
+ORG $CFA977
+db $03,$00
+
+ORG $CFA976
+db $01
+
+ORG $CFA97E
+db $01,$94,$9A,$EE
+
+ORG $CFA979
+db $04,$5E,$02
+
+ORG $C73236
+db $0a, $0b, $EB,$C5,$03,$00 ;Library redirect
+
+ORG $D5F02B
+db $e0,$00,$90,$02,$02,$1e,$00
+
+ORG $D5F033
+db $e0,$00,$80,$02,$04,$21,$00
+
+ORG $CF942D
+db $00,$D6,$00,$61
+
+ORG $61000A
+db $70, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $03, $00, $70, $79, $50, $93, $91, $9e, $50, $a4, $a2, $91, $9e, $a3
+db $a0, $9f, $a2, $a4, $50, $a9, $9f, $a5, $50, $91, $93, $a2, $9f, $a3, $a3, $50
+db $a4, $98, $95, $50, $a2, $99, $a6, $95, $a2, $5e, $03, $07, $d9, $03, $1b, $03
+db $bd, $00, $61, $00, $00, $70, $78, $9f, $a7, $95, $a6, $95, $a2, $5c, $10, $15
+db $50, $99, $a4, $50, $a3, $95, $95, $9d, $a3, $50, $a9, $9f, $a5, $50, $91, $a2
+db $95, $50, $9e, $9f, $a4, $50, $a9, $95, $a4, $50, $a2, $95, $91, $94, $a9, $5e
+db $03, $00, $70, $73, $9f, $9d, $95, $50, $92, $91, $93, $9b, $50, $a7, $98, $95
+db $9e, $50, $a9, $9f, $a5, $50, $98, $91, $a6, $95, $50, $93, $9f, $9e, $a1, $a5
+db $95, $a2, $95, $94, $50, $a4, $98, $95, $50, $a0, $a9, $a2, $91, $9d, $99, $94
+db $5e, $13, $02, $00, $70, $71, $9e, $94, $50, $91, $a7, $91, $a9, $50, $a7, $95
+db $50, $97, $9f, $51, $10, $20, $18, $04, $1f, $21, $90, $02; Rock text
+
+db $70, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $03, $00, $70, $79, $57, $9d, $50, $9e, $9f, $a4, $50, $a6, $95, $a2
+db $a9, $50, $a0, $a2, $9f, $a5, $94, $50, $9f, $96, $50, $9d, $a9, $50, $9e, $91
+db $9d, $95, $5e, $03, $00, $70, $87, $91, $9e, $a4, $50, $a4, $9f, $50, $93, $a2
+db $9f, $a3, $a3, $50, $a4, $98, $95, $50, $a2, $99, $a6, $95, $a2, $6f, $03, $19
+db $02, $89, $95, $a3, $02, $19, $02, $7e, $9f, $02, $00, $1c, $07, $02, $11, $09
+db $02, $55, $01, $61, $00, $6e, $01, $61, $00, $0a, $6e, $01, $61, $00, $00, $12
+db $70, $71, $9e, $94, $50, $98, $95, $a2, $95, $50, $a7, $95, $50, $97, $9f, $51
+db $10, $20, $18, $04, $1f, $21, $91, $02, $12, $70, $7f, $9b, $91, $a9, $5e, $10
+db $20, $50, $79, $57, $9c, $9c, $50, $92, $95, $50, $98, $95, $a2, $95, $5c, $10
+db $0a, $50, $99, $96, $50, $a9, $9f, $a5, $50, $93, $98, $91, $9e, $97, $95, $50
+db $a9, $9f, $a5, $a2, $50, $9d, $99, $9e, $94, $5e, $13, $02; John text
+
+db $16, $50, $5e, $03, $00, $08, $10, $f7, $d5, $00, $1b, $03, $32, $02, $fe, $00
+db $1f, $1e, $44, $04, $06, $04, $7D, $02, $02; Dalaam Character text
+
+db $0e, $01, $0d, $01, $1d, $19, $00, $1b, $03, $E8, $01, $61, $00, $19, $10
+db $00, $0b, $04, $1b, $03, $d9, $01, $61, $00, $0f, $0a, $BD, $01, $61, $00, $04
+db $D8, $00, $1f, $15, $6a, $00, $53, $01, $01, $0a, $0d, $22, $c9, $00, $02; Trial of Mu
+
+ORG $C9D61C
+db $0A, $A2, $01, $61, $00; Dalaam Char redir
+
+ORG $C92203
+db $0A, $BB, $01, $61, $00
+
+ORG $C5EB0B
+db $07,$ff,$01,$1b,$03,$14,$ed,$c5,$00,$70,$89,$9f,$a5,$50,$a7,$91
+db $9e,$a4,$50,$91,$50,$9d,$91,$a0,$6f,$03,$00,$70,$87,$95,$50,$94
+db $9F, $9E, $57, $A4, $50, $98, $91, $A6, $95, $50, $9D, $91, $A0, $A3, $50, $91
+db $9E, $A9, $9D, $9F, $A2, $95, $5C, $10, $10, $50, $9B, $99, $94, $5E, $03, $00
+db $70, $75, $A6, $95, $A2, $A9, $9F, $9E, $95, $50, $9A, $A5, $A3, $A4, $50, $A5
+db $a3, $95, $a3, $50, $80, $9f, $a0, $a4, $a2, $91, $93, $9b, $95, $a2, $50, $a4
+db $98, $95, $A3, $95, $50, $94, $91, $A9, $A3, $5E, $03, $00, $70, $75, $A6, $95
+db $9E, $50, $99, $96, $50, $A9, $9F, $A5, $50, $94, $9F, $9E, $57, $A4, $5C, $10
+db $13, $50, $A9, $9F, $A5, $50, $93, $91, $9E, $50, $9A, $A5, $A3, $A4, $50, $A0
+db $a2, $95, $a3, $a3, $50, $88, $50, $a4, $9f, $50, $a2, $95, $9d, $95, $9d, $92
+db $95, $A2, $50, $A7, $98, $91, $A4, $50, $A4, $98, $95, $50, $9D, $91, $A0, $50
+db $a7, $9f, $a5, $9c, $94, $50, $9c, $9f, $9f, $9b, $50, $9c, $99, $9b, $95, $5c
+db $10, $15, $50, $99, $96, $50, $A9, $9F, $A5, $50, $94, $99, $94, $50, $98, $91
+db $a6, $95, $50, $9f, $9e, $95, $5e, $03, $00, $70, $87, $95, $50, $94, $9f, $5c
+db $10, $15, $50, $98, $9F, $A7, $95, $A6, $95, $A2, $5C, $10, $15, $50, $98, $91
+db $a6, $95, $50, $a2, $95, $9E, $A4, $91, $9C, $A3, $50, $96, $9F, $A2, $50, $1C
+db $05, $05, $A3, $5E, $03, $00, $70, $87, $9F, $A5, $9C, $94, $50, $A9, $9F, $A5
+db $50, $9C, $99, $9B, $95, $50, $9F, $9E, $95, $6F, $03, $00, $19, $02, $89, $95
+db $a3, $02, $19, $02, $7e, $9f, $02, $1c, $07, $02, $11, $12, $09, $02, $78, $ec
+db $c5, $00, $41, $ec, $c5, $00, $70, $89, $95, $91, $98, $5c, $10, $15, $50, $99
+db $a4, $57, $a3, $50, $a0, $a2, $9F, $92, $91, $92, $9C, $A9, $50, $95, $91, $A3
+db $99, $95, $A2, $50, $A4, $9F, $50, $94, $9F, $A7, $9E, $9C, $9F, $91, $94, $5C
+db $50, $10, $10, $91, $9E, $A9, $A7, $91, $A9, $A3, $5E, $13, $02, $70, $7F, $9B
+db $91, $A9, $5E, $50, $10, $15, $76, $95, $95, $9C, $50, $96, $A2, $95, $95, $50
+db $a4, $9F, $50, $a2, $95, $a4, $A5, $A2, $9E, $50, $99, $A4, $50, $a7, $98, $95
+db $9E, $95, $A6, $95, $A2, $5E, $13, $1D, $03, $FF, $1B, $02, $B0, $EC, $C5, $00
+db $0A, $99, $33, $C7, $00, $00, $70, $5E, $5E, $5E, $79, $A3, $50, $A7, $98, $91
+db $a4, $50, $79, $50, $a7, $9f, $a5, $9c, $94, $50, $a3, $91, $a9, $5c, $10, $15
+db $50, $99, $96, $50, $A9, $9F, $A5, $50, $98, $91, $94, $50, $A2, $9F, $9F, $9D
+db $50, $A4, $9F, $50, $98, $9F, $9C, $94, $50, $99, $A4, $5E, $03, $00, $70, $83
+db $9F, $A2, $A2, $A9, $5C, $10, $15, $50, $9B, $99, $94, $5E, $50, $89, $9F, $A5
+db $57, $9C, $9C, $50, $98, $91, $A6, $95, $50, $A4, $9F, $50, $9D, $91, $9B, $95
+db $50, $A3, $A0, $91, $93, $95, $5E, $13, $02, $70, $87, $95, $57, $A2, $95, $50
+db $98, $9F, $A3, $A4, $99, $9E, $97, $50, $91, $50, $73, $9C, $99, $A1, $A5, $95
+db $50, $A4, $9F, $A5, $A2, $9E, $91, $9D, $95, $9E, $A4, $50, $9F, $9E, $50, $76
+db $a2, $99, $94, $91, $a9, $50, $a4, $9f, $50, $92, $9f, $9f, $a3, $a4, $50, $91
+db $a4, $a4, $95, $9e, $94, $91, $9e, $93, $95, $5e, $03, $00, $70, $87, $95, $50
+db $94, $9F, $9E, $57, $A4, $50, $98, $91, $A6, $95, $50, $9D, $91, $9E, $A9, $50
+db $a0, $91, $a2, $a4, $99, $93, $99, $a0, $91, $9e, $a4, $a3, $50, $a9, $95, $a4
+db $5C, $10, $15, $50, $A4, $98, $9F, $A5, $97, $98, $5E, $13, $02 ;Library text
+
+ORG $C8EB99
+db $0A, $AD, $71, $C6, $00
+
+ORG $C671AD
+db $03, $0e, $01, $0d, $01, $1d, $19, $00, $1b, $03, $cc, $71, $c6, $00, $19, $10
+db $00, $0b, $03, $1b, $03, $1C, $72, $c6, $00, $0f, $0a, $b0, $71, $c6, $00, $00
+db $70, $79, $57, $9d, $50, $91, $50, $a4, $95, $9e, $a4, $50, $a4, $98, $91, $a4
+db $50, $95, $91, $a4, $a3, $50, $9e, $95, $a2, $94, $a3, $51, $03, $00, $70, $89
+db $9f, $a5, $50, $94, $9f, $9e, $57, $a4, $50, $9c, $9f, $9f, $9b, $50, $9c, $99
+db $9b, $95, $50, $91, $50, $9e, $95, $a2, $94, $5c, $10, $15, $50, $a3, $9f, $50
+db $83, $73, $82, $71, $7d, $51, $03, $18, $04, $1f, $03, $05, $0B, $00, $02, $18
+db $02, $10, $01, $0a, $9e, $eb, $c8, $00, $30, $16, $b0, $51, $6e, $13, $70, $02; Boogey tent text
+
+db $07, $47, $00, $1b, $02, $86, $72, $c6, $00, $18, $01, $01, $70, $84, $98, $95
+db $50, $9d, $91, $a3, $a4, $95, $a2, $50, $99, $a3, $50, $97, $9f, $9e, $95, $6f
+db $03, $00, $70, $10, $20, $5e, $10, $20, $5e, $10, $20, $5e, $79, $50, $94, $9f
+db $9e, $57, $a4, $50, $92, $95, $9c, $99, $95, $a6, $95, $50, $a9, $9f, $a5, $5c
+db $10, $15, $50, $a3, $9f, $50, $a7, $95, $57, $a2, $95, $50, $a3, $a4, $91, $a9
+db $99, $9e, $97, $50, $a0, $a5, $a4, $5e, $13, $02; Zombie belch text
+
+ORG $CF66F5
+db $03
+
+ORG $C8ECA3
+db $0A, $2D, $72, $C6
+;have to check the zombie pointer text bn
+
+
+
+ORG $C5F1FA
+db $02, $70, $71, $9E, $A9, $92, $9F, $94, $A9, $50, $A7, $91, $9E, $A4, $50, $91
+db $50, $A0, $95, $91, $9E, $A5, $A4, $6F, $10, $20, $1F, $1E, $2E, $00, $08, $1F
+db $02, $2A, $18, $04, $10, $80, $05, $2F, $01, $0A, $00, $AA, $EE ;Buzz Buzz text
+
+ORG $EE9210
+OnettTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd OnettLeaderName
+OnettLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $7F, $9E, $95, $A4, $A4, $51, $59, $1F, $02, $67, $04, $D1, $00, $13, $02 ;Onett
+
+TwosonTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd TwosonLeaderName
+TwosonLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $84, $A7, $9F, $A3, $9F, $9E, $51, $59, $1F, $02, $67, $04, $D2, $00, $13
+db $02 ;Twoson
+
+HappyTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd HappyLeaderName
+HappyLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $78, $91, $A0, $A0, $A9, $5D, $78, $91, $A0, $A0, $A9, $50, $86, $99, $9C
+db $9C, $91, $97, $95, $51, $59, $1F, $02, $67, $04, $DD, $00, $13, $02 ;Happy-Happy
+
+ThreedTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd ThreedLeaderName
+ThreedLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $84, $98, $A2, $95, $95, $94, $51, $59, $1F, $02, $67, $04, $D3, $00, $13
+db $02 ;Threed
+
+SaturnTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd SaturnLeaderName
+SaturnLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $83, $91, $A4, $A5, $A2, $9E, $50, $86, $91, $9C, $9C, $95, $A9, $51, $59
+db $1F, $02, $67, $04, $D5, $00, $13, $02 ;Saturn Valley
+
+DunesTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd DunesLeaderName
+DunesLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $74, $A5, $A3, $A4, $A9, $50, $74, $A5, $9E, $95, $A3, $50, $74, $95, $A3
+db $95, $A2, $A4, $51, $59, $1F, $02, $67, $04, $DE, $00, $13, $02;Dusty Dunes
+
+FoursTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd FoursLeaderName
+FoursLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $76, $9F, $A5, $A2, $A3, $99, $94, $95, $51, $59, $1F, $02, $67, $04, $D6
+db $00, $13, $02; Fourside
+
+WintersTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd WintersLeaderName
+WintersLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $87, $99, $9E, $A4, $95, $A2, $A3, $51, $59, $1F, $02, $67, $04, $D4, $00
+db $13, $02 ;Winters
+
+SummersTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd SummersLeaderName
+SummersLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $83, $A5, $9D, $9D, $95, $A2, $A3, $51, $59, $1F, $02, $67, $04, $D7, $00
+db $13, $02 ;Summers
+
+ScarabaTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd ScarabaLeaderName
+ScarabaLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $83, $93, $91, $A2, $91, $92, $91, $51, $59, $1F, $02, $67, $04, $D9, $00
+db $13, $02 ;Scaraba
+
+DalaamTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd DalaamLeaderName
+DalaamLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $74, $91, $9C, $91, $91, $9D, $51, $59, $1F, $02, $67, $04, $D8, $00, $13
+db $02 ;Dalaam
+
+DarkTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd DarkLeaderName
+DarkLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $A4, $98, $95, $50, $74, $95, $95, $A0, $50, $74, $91, $A2, $9B, $9E, $95
+db $A3, $A3, $51, $59, $1F, $02, $67, $04, $DA, $00, $13, $02; Deep Darkness
+
+TendaTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd TendaLeaderName
+TendaLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $84, $95, $9E, $94, $91, $50, $86, $99, $9C, $9C, $91, $97, $95, $51, $59
+db $1F, $02, $67, $04, $DB, $00, $13, $02 ;Tenda Village
+
+UnderworldTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd UnderLeaderName
+UnderLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $A4, $98, $95, $50, $7C, $9F, $A3, $A4, $50, $85, $9E, $94, $95, $A2, $A7
+db $9F, $A2, $9C, $94, $51, $59, $1F, $02, $67, $04, $DC, $00, $13, $02; Lost Underworld
+
+MagicantTeleTex:
+db $18, $01, $01, $01
+db $0A
+dd MagicLeaderName
+MagicLeaderDone:
+db $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $A4, $9F, $50, $A4, $95, $9C, $95, $A0, $9F, $A2, $A4, $50, $A4, $9F
+db $50, $7D, $91, $97, $99, $93, $91, $9E, $A4, $51, $59, $1F, $02, $67, $04, $DF
+db $00, $13, $02 ;Magicant
+
+db $18, $01, $01, $01, $70, $58, $1C, $02, $01, $50, $9C, $95, $91, $A2, $9E, $95
+db $94, $50, $9E, $9F, $A4, $98, $99, $9E, $97, $51, $59, $1F, $02, $79, $13, $02;Nothing
+
+PooPsiTex:
+db $18, $01, $01, $01, $70, $58, $1C, $02, $04, $50, $A2, $95, $91, $9C, $99, $AA
+db $95, $94, $50, $A4, $98, $95, $50, $A0, $9F, $A7, $95, $A2, $50, $9F, $96, $50
+db $07, $D1, $03, $1B, $03, $8E, $95, $EE, $FF, $1C, $12, $15, $51, $59, $1F, $02
+db $67, $1F, $71, $04, $02, $04, $D1, $03, $13, $02, $1C, $12, $16, $51, $59, $1F
+db $02, $67, $1F, $71, $04, $03, $13, $02; Starstorm
+
+ORG $EEA500 ;You Win
+db $08, $01, $95, $EE, $00, $18, $00, $02, $00, $00, $00, $00, $00, $00, $00, $00; I assume the first part of this to be the automated Magicant teleport?
+
+ORG $EE9C12
+db $07, $D5, $03, $1B, $03, $10, $A5, $EE, $00, $07, $D4, $03, $1B, $03, $00, $A5
+db $EE, $00, $07, $D3, $03, $1B, $03, $D0, $9B, $EE, $00, $18, $00, $00, $02 ;Sanc text
+
+ORG $EE9620
+db $18, $01, $01, $19, $02, $83, $A4, $9F, $A2, $95, $02, $19, $02, $84, $91, $9B
+db $95, $02, $19, $02, $74, $95, $9C, $95, $A4, $95, $02, $1C, $07, $03, $11, $09
+db $03, $50, $96, $EE, $00, $99, $96, $EE, $00, $84, $C6, $EE, $00, $18, $04, $02
+db $08, $31, $E4, $C5, $00, $1B, $02, $20, $96, $EE, $00, $1D, $0C, $01, $01, $18
+db $07, $02, $00, $00, $00, $00, $1B, $03, $6C, $96, $EE, $00, $08, $56, $E4
+db $C5, $00, $01, $1B, $02, $20, $96, $EE, $00, $1B, $00, $0A
+dl StorageStoreCheck
+db $0A
+dd StorageStoreCheck
+db $00
+BackFromCheck:
+db $1B, $01, $19, $19, $00, $00, $1B, $01, $1D, $12, $00
+db $00, $01, $0A, $50, $96, $EE, $18, $04, $02, $02, $0E, $01, $19, $14, $1B, $02
+db $20, $96, $EE, $00, $1A, $07, $01, $1B, $02, $D6, $96, $EE, $00, $1B, $04, $1B
+db $00, $18, $02, $08, $4A, $E5, $C5, $00, $1B, $02, $99, $96, $EE, $00, $1B, $05, $18
+db $03, $0D, $18, $00, $18, $03, $01, $1B, $06, $1D, $13, $00, $00, $19, $19
+db $00, $00, $00, $0A, $99, $96, $EE, $18, $04, $0A, $20, $96
+db $EE, $00
+db $08, $F8, $C6, $EE, $00, $01, $1B, $02, $84, $C6, $EE, $00, $1B, $00, $19, $19
+db $00, $00, $1D, $0B, $00, $1B, $02, $DD, $96, $EE, $00, $1B, $01, $1D, $0F, $00
+db $00, $01, $0A, $DD, $96, $EE, $00, $18, $04, $02, $DD, $96, $EE, $18, $04 ;Pocket Storage menu
+
+ORG $C67026
+db $07, $9B, $00, $1B, $02, $35, $D7, $C9, $00, $1D, $05, $FF, $AF, $1B, $03, $7A
+db $70, $C6, $00, $70, $7D, $95, $5E, $03, $00, $70, $77, $A5, $91, $A2, $94, $5E
+db $03, $00, $70, $74, $91, $A2, $9B, $9E, $95, $A3, $A3, $50, $A4, $98, $99, $A3
+db $50, $A7, $91, $A9, $5E, $03, $00, $70, $72, $A2, $99, $9E, $97, $50, $1C, $05
+db $AF, $5E, $03, $00, $70, $84, $98, $95, $9E, $50, $A7, $95, $50, $A4, $91, $9C
+db $9B, $5E, $13, $02, $70, $89, $9F, $A5, $50, $92, $A2, $99, $9E, $97, $50, $1C
+db $05, $AF, $5E, $03, $00, $70, $7D, $95, $5E, $03, $00, $70, $74, $99, $A3, $A0
+db $95, $9C, $50, $94, $91, $A2, $9B, $9E, $95, $A3, $A3, $5E, $03, $00, $70, $89
+db $9F, $A5, $50, $A7, $91, $99, $A4, $5E, $03, $00, $50, $50, $1F, $02, $76, $58
+db $84, $98, $95, $50, $84, $95, $9E, $94, $91, $50, $A4, $9F, $9F, $9B, $50, $A4
+db $98, $95, $50, $78, $91, $A7, $9B, $50, $95, $A9, $95, $50, $91, $9E, $94, $50
+db $a2, $a5, $a3, $98, $95, $94, $50, $9F, $A5, $A4, $50, $A4, $98, $95, $50, $94
+db $9F, $9F, $A2, $5E, $5E, $5E, $59, $13, $18, $04, $1F, $1E, $D3, $00, $06, $10
+db $20, $1F, $02, $5F, $10, $C0, $04, $20, $01, $1D, $01, $FF, $AF, $02 ;Tenda guard text
+
+ORG $EE9871
+db $07, $DA, $03, $1B, $03, $EA, $95, $EE, $00, $04, $DA, $03, $50, $50, $1F, $02
+db $44, $10, $60, $18, $01, $01, $58, $84, $98, $99, $A3, $50, $99, $A3, $50, $A3
+db $9F, $9D, $95, $50, $A3, $A4, $A2, $91, $9E, $97, $95, $50, $A4, $95, $91, $10
+db $30, $5E, $10, $30, $5E, $10, $30, $5E, $59, $13, $18, $04, $04, $DA, $03, $FE
+db $0A, $AF, $92, $EE, $1F, $02, $44, $10, $60, $18, $01, $01, $50, $50, $58, $89
+db $9F, $A5, $57, $A6, $95, $50, $97, $9F, $A4, $A4, $95, $9E, $50, $A5, $A3, $95
+db $94, $50, $A4, $9F, $50, $A4, $98, $95, $50, $A4, $91, $A3, $A4, $95, $5E, $59 ;Tea text
+db $13, $02
+
+db $18, $01, $01, $50, $50, $58, $83, $95, $9e, $a4, $50, $91, $50, $1c, $05, $ad
+db $50, $a4, $9f, $50, $1c, $02, $01, $51, $59, $1f, $02, $66, $10, $20, $13, $02; Got AP item, for PSI checks
+
+ORG $C9DC18
+db $0A,$A6,$95,$EE,$00
+
+ORG $CF2375
+db $00,$00,$00,$00,$20,$81 ;Tenda door
+
+ORG $CF2396
+db $00,$00,$00,$00,$00,$00
+
+ORG $CF0D87
+db $E5,$FA,$C6,$00,$1D,$82 ;Lock threed zombie door
+
+ORG $CF9426
+db $66
+
+ORG $C483BE
+LDA #$AAAA
+
+ORG $CF61EB
+dw $0001
+
+ORG $CF6B59
+dw $0002
+
+ORG $C9DAFD
+db $00
+
+ORG $C9DAFF
+dw $03D7
+
+db $02
+
+ORG $C557F0
+db $70, $79, $a4, $50, $91, $a0, $a0, $95, $91, $a2, $a3, $50, $a4, $9f, $50, $92
+db $95, $50, $91, $50, $a2, $95, $a6, $99, $95, $a7, $50, $96, $9f, $a2, $50, $91
+db $50, $83, $93, $91, $a2, $91, $92, $91, $5d, $a4, $98, $95, $9d, $95, $94, $50
+db $a2, $95, $a3, $a4, $91, $a5, $a2, $91, $9e, $a4, $5e, $13, $02
+
+ORG $EFA0DC
+db $70, $84, $98, $95, $50, $98, $99, $95, $a2, $9f, $97, $9c, $a9, $a0, $98, $a3
+db $50, $94, $95, $a0, $99, $93, $a4, $50, $91, $50, $a2, $95, $a6, $99, $95, $a7
+db $50, $96, $9f, $a2, $50, $91, $50, $a2, $95, $a3, $a4, $91, $a5, $a2, $91, $9e
+db $a4, $50, $9f, $a7, $9e, $95, $94, $50, $92, $a9, $50, $a4, $98, $95, $50, $83
+db $a0, $98, $99, $9e, $a8, $5e, $03, $00, $70, $84, $98, $95, $50, $a2, $95, $a6
+db $99, $95, $a7, $50, $97, $9f, $95, $a3, $50, $9f, $9e, $50, $91, $a4, $50, $9c
+db $95, $9e, $97, $a4, $98, $50, $91, $92, $9f, $a5, $a4, $50, $a4, $98, $95, $50
+db $a0, $9f, $9f, $a2, $50, $a1, $a5, $91, $9c, $99, $a4, $a9, $50, $9f, $96, $50
+db $a4, $98, $95, $50, $96, $9f, $9f, $94, $50, $92, $95, $99, $9e, $97, $50, $a3
+db $95, $a2, $a6, $95, $94, $5c, $10, $15, $50, $91, $9e, $94, $50, $91, $a0, $a0
+db $95, $91, $a2, $a3, $50, $a4, $9f, $50, $92, $95, $50, $a3, $99, $97, $9e, $95
+db $94, $50, $92, $a9, $50, $6C, $82, $91, $9d, $50, $82, $91, $99, $a3, $99, $9e
+db $5e, $6E, $13, $02
+
+ORG $C87209
+db $1d, $05, $ff, $b9, $1b, $02, $20, $72, $c8, $00, $0a, $17, $72, $c8
+
+ORG $CF5DAB
+db $3F
+
+ORG $C573C1
+db $0A,$DC,$73,$C5,$00
+
+ORG $C573DC
+;CLEANUP: can I subrotuine here
+db $18, $01, $01, $70, $83, $9f, $5c, $50, $9e, $9f, $a7, $50, $79, $50, $a7, $99
+db $9c, $9c, $50, $a4, $95, $91, $93, $98, $50, $a9, $9f, $a5, $50, $a4, $98, $95
+db $50, $a3, $95, $93, $a2, $95, $a4, $50, $9f, $96, $50, $9d, $a9, $50, $a0, $9f
+db $a7, $95, $a2, $5e, $5e, $5e, $03, $00, $70, $89, $9f, $a5, $50, $9d, $a5, $a3
+db $a4, $50, $9c, $95, $91, $a2, $9e, $50, $a4, $98, $99, $a3, $50, $a0, $9f, $a7
+db $95, $a2, $5c, $10, $15, $50, $95, $a6, $95, $9e, $50, $99, $96, $50, $a9, $9f
+db $a5, $50, $94, $9f, $9e, $57, $a4, $50, $a7, $91, $9e, $a4, $50, $a4, $9f, $5e
+db $03, $08, $54, $95, $FE, $00, $04, $d9, $03, $02, $9b, $32, $c9, $00
+
+ORG $C93284
+db $0A,$AB,$32,$C9,$00
+
+ORG $CF7B49
+db $7E
+
+ORG $CF794B
+db $00
+
+ORG $C9AF4B
+db $D9,$00
+
+ORG $CFDA7E
+db $D8,$03,$02; Dungeon Man signs
+
+ORG $CFDA8F
+db $D8,$03,$01; Dungeon Man signs
+
+ORG $CFD908
+db $D8, $03, $01;Submarine Visibility
+
+ORG $C57C2E
+db $04, $d8, $03, $0a, $39, $7c, $c5, $6f, $7c
+
+ORG $C57C3D
+db $01
+
+ORG $C93529
+db $01
+
+ORG $C5796B
+db $D9
+;;;;;;;;;;;;;;;;;;;
+ORG $C9C999
+db $D8, $00
+
+ORG $C9CBC0
+db $18,$01,$01, $0A,$9F,$C9,$C9
+
+ORG $C9CA28
+db $02
+
+ORG $CFD211
+db $01
+
+ORG $C87220
+;Sphinx tetx
+db $18, $01, $01
+db $70, $89, $9f, $a5, $57, $a6, $95, $50, $92, $a2, $9f, $a5, $97, $98, $a4, $50
+db $a4, $98, $95, $50, $91, $9e, $93, $99, $95, $9e, $a4, $50, $9d, $91, $9e, $a5
+db $a3, $93, $a2, $99, $a0, $a4, $a3, $5e, $10, $20, $50, $89, $9f, $a5, $50, $9d
+db $91, $a9, $50, $a0, $a2, $9f, $93, $95, $95, $94, $5e, $13, $08, $15, $30, $c9
+db $00, $04, $56, $01, $02
+
+
+ORG $C5749A
+db $0A,$F3,$74,$C5,$00
+
+ORG $C87203
+db $56
+
+ORG $C92967
+db $87, $9f, $a7, $51, $50, $7c, $9f, $9f, $9b, $50, $91, $a4, $50, $a4, $98, $99
+db $a3, $50, $97, $a5, $a9, $50, $a3, $a4, $91, $9e, $94, $99, $9e, $97, $50, $9f
+db $9e, $50, $91, $50, $a2, $9f, $93, $9b, $51, $13, $08, $18, $96, $fe, $00, $18
+db $04, $02; Mu training text
+
+ORG $D5F810
+db $10, $78, $10, $78, $10, $3C, $1D, $19, $01, $02, $1D, $19, $01, $0A, $B9, $82, $C6, $00
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C78641
+
+db $0A, $A0, $E8, $C5, $00; Desert bus text
+
+ORG $CF6219
+db $04, $00; Desert tunnel
+
+ORG $CFB2E5
+db $31,$01,$02,$2E,$00,$47,$00
+
+ORG $C3AF4E
+db $19, $50, $FE
+
+ORG $C3FE50
+db $42, $2f, $a8, $c0, $1a, $5c, $bb, $1a, $64, $FE, $42, $8d, $a8, $c0, $C3, $00
+db $96, $FE, $09, $42
+
+db $0e, $05, $12, $00, $42, $43, $a9, $c0, $ff, $42, $85, $a6, $c0, $80, $01, $1a
+db $44, $ab, $06, $01, $42, $43, $a9, $c0, $ff, $42, $c6, $a8, $c0, $0a, $64, $FE
+db $1b, $23
+
+db $06, $01, $42, $43, $a9, $c0, $ff, $42, $c6, $a8, $c0, $0a, $64, $FE, $1b, $23
+
+ORG $C3FE96
+db $18, $01, $01, $1f, $02, $60, $70, $87, $9f, $9f, $9f, $9f, $51, $10, $14, $17
+db $83, $9f, $9f, $9f, $51, $10, $0f, $17, $83, $9f, $9f, $9f, $9f, $9f, $9f, $9f
+db $51, $03, $00, $70, $58, $77, $9f, $15, $85, $51, $10, $0f, $50, $50, $77, $9f
+db $15, $85, $51, $59, $03, $18, $04, $1f, $15, $31, $01, $63, $00, $01, $1f, $15
+db $31, $01, $64, $00, $01, $1f, $61, $1f, $21, $41, $02
+
+ORG $C5E8A0
+db $07, $47, $00, $1b, $03, $4a, $e9, $c5, $00, $70, $58, $71, $50, $9e, $9f, $a4
+db $99, $93, $95, $50, $99, $a3, $50, $a0, $9f, $a3, $a4, $95, $94, $50, $9f, $9e
+db $50, $a4, $98, $95, $50, $92, $a5, $a3, $50, $a3, $93, $98, $95, $94, $a5, $9c
+db $95, $5e, $03, $00, $70, $74, $a5, $95, $50, $a4, $9f, $50, $a2, $95, $a0, $9f
+db $a2, $a4, $a3, $50, $9f, $96, $50, $a0, $91, $a2, $91, $9e, $9f, $a2, $9d, $91
+db $9c, $50, $91, $93, $a4, $99, $a6, $99, $a4, $a9, $5c, $10, $15, $50, $92, $a5
+db $a3, $50, $a2, $9f, $a5, $a4, $95, $a3, $50, $a4, $9f, $50, $84, $98, $a2, $95
+db $95, $94, $50, $98, $91, $a6, $95, $50, $92, $95, $95, $9e, $50, $a4, $95, $9d
+db $a0, $9f, $a2, $91, $a2, $99, $9c, $a9, $50, $a3, $a5, $a3, $a0, $95, $9e, $94
+db $95, $94, $5e, $03, $00, $70, $5d, $76, $9f, $a5, $a2, $a3, $99, $94, $95, $50
+db $72, $a5, $a3, $50, $73, $9f, $5e, $59, $13, $02, $70, $58, $1c, $02, $00, $0a
+db $46, $86, $c7, $00
+
+ORG $D5EDB3
+db $35,$00,$e5,$04,$03,$20
+
+ORG $CFBE73
+db $85, $00, $02, $0A, $00, $E9, $02, $01, $07, $1F, $C6
+
+ORG $CFBE89
+db $DE, $00, $01
+
+ORG $C61F0E
+db $0A, $D6, $1C, $C8
+
+ORG $C81CD6
+db $70, $85, $9b, $99, $95, $50, $85, $9b, $17, $fc, $9b, $99, $95, $03, $00, $70
+db $58, $79, $50, $91, $9d, $50, $7d, $91, $9e, $50, $7b, $5e, $50, $7d, $91, $9e
+db $5e, $59, $03, $00, $70, $85, $9b, $99, $95, $50, $85, $9b, $17, $fc, $9b, $99
+db $95, $03, $00, $70, $58, $84, $91, $9c, $91, $50, $82, $91, $9d, $91, $98, $50
+db $91, $a3, $9b, $95, $94, $50, $9d, $95, $50, $a4, $9f, $50, $97, $a5, $91, $a2
+db $94, $50, $a4, $98, $99, $a3, $50, $94, $9f, $9f, $a2, $5e, $59, $03, $00, $70
+db $85, $9b, $99, $95, $50, $85, $9b, $17, $fc, $9b, $99, $95, $03, $00, $70, $58
+db $79, $96, $50, $a9, $9f, $a5, $50, $97, $99, $a6, $95, $50, $9d, $95, $50, $a4
+db $98, $95, $50, $1c, $05, $a6, $5c, $10, $05, $50, $79, $57, $9c, $9c, $50, $9c
+db $95, $a4, $50, $a9, $9f, $a5, $50, $a4, $98, $a2, $9f, $a5, $97, $98, $5e, $59
+db $0a, $84, $1f, $c6
+
+ORG $C61FFA
+db $1D
+
+ORG $C62005
+db $E9, $02
+
+ORG $C61F08
+db $E9, $02
+
+ORG $CFC032
+db $E9, $02
+
+ORG $CF15E3
+db $01
+
+ORG $C627D8
+db $0A, $4F, $51, $C9
+
+ORG $C9514F
+db $70, $58, $79, $50, $a7, $9f, $a5, $9c, $94, $50, $a4, $95, $91, $93, $98, $50
+db $a9, $9f, $a5, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $91, $a4, $99, $9f
+db $9e, $5c, $10, $0f, $50, $92, $a5, $a4, $50, $99, $a4, $50, $a3, $95, $95, $9d
+db $a3, $50, $a9, $9f, $a5, $50, $91, $9c, $a2, $95, $91, $94, $a9, $50, $9b, $9e
+db $9f, $a7, $50, $99, $a4, $51, $59, $07, $65, $02, $1b, $03, $ec, $51, $c9, $00
+db $03, $00, $70, $7b, $a9, $99, $9b, $99, $50, $9b, $91, $03, $00, $70, $58, $79
+db $9e, $a3, $a4, $95, $91, $94, $5c, $50, $79, $57, $9c, $9c, $50, $a4, $95, $91
+db $93, $98, $50, $a9, $9f, $a5, $50, $a4, $98, $99, $a3, $51, $59, $13, $18, $04
+db $1f, $02, $32, $10, $20, $1f, $02, $26, $10, $30, $1f, $02, $31, $10, $10, $1f
+db $02, $41, $10, $55, $08, $55, $93, $fe, $00, $04, $65, $02, $02, $13, $02
+
+ORG $CFBC05
+db $00
+
+
+;Dusty Dunes Miner
+ORG $C60365
+db $1d, $05, $ff, $cb, $1b, $03, $01, $04, $c6, $00, $70, $78, $95, $a9, $5e, $03
+db $00, $70, $79, $50, $a7, $91, $a3, $50, $9d, $99, $9e, $99, $9e, $97, $50, $96
+db $9f, $a2, $50, $1c, $05, $5a, $a3, $5c, $10, $10, $50, $92, $a5, $a4, $50, $79
+db $50, $9c, $9f, $a3, $a4, $50, $9d, $a9, $50, $a0, $95, $a2, $9d, $99, $a4, $50
+db $92, $95, $93, $91, $a5, $a3, $95, $50, $79, $57, $9d, $50, $6C, $9e, $9f, $a4
+db $50, $9c, $99, $93, $95, $9e, $a3, $95, $94, $6E, $5e, $03, $00, $70, $79, $96
+db $50, $a9, $9f, $a5, $50, $92, $a2, $99, $9e, $97, $50, $9d, $95, $50, $91, $50
+db $a0, $95, $a2, $9d, $99, $a4, $5c, $10, $10, $50, $79, $57, $9c, $9c, $50, $a3
+db $98, $91, $a2, $95, $50, $a7, $98, $91, $a4, $95, $a6, $95, $a2, $50, $9c, $9f
+db $9f, $a4, $50, $79, $50, $96, $99, $9e, $94, $5e, $13, $02, $70, $89, $9f, $a5
+db $57, $a6, $95, $50, $97, $9f, $a4, $50, $91, $50, $9d, $99, $9e, $99, $9e, $97
+db $50, $a0, $95, $a2, $9d, $99, $a4, $51, $1f, $02, $76, $10, $20, $1d, $01, $ff
+db $cb, $71, $9c, $a2, $99, $97, $98, $a4, $5c, $50, $9e, $95, $a7, $50, $96, $9f
+db $a2, $95, $9d, $91, $9e, $5e, $03, $00, $70, $84, $98, $95, $a2, $95, $57, $a3
+db $50, $9e, $9f, $50, $a4, $99, $9d, $95, $50, $a4, $9f, $50, $a7, $91, $a3, $a4
+db $95, $5c, $10, $10, $50, $a3, $9f, $50, $a7, $95, $57, $a2, $95, $50, $a2, $a5
+db $9e, $9e, $99, $9e, $97, $50, $91, $a4, $50, $96, $a5, $9c, $9c, $50, $a0, $9f
+db $a7, $95, $a2, $51, $03, $00, $04, $ae, $00, $18, $04, $1f, $21, $23, $1f, $e5
+db $ff, $1f, $16, $f8, $02, $01, $18, $01, $01, $70, $78, $9f, $9f, $51, $03, $00
+db $70, $84, $9f, $9c, $94, $50, $a9, $9f, $a5, $50, $99, $a4, $57, $94, $50, $92
+db $95, $50, $a1, $a5, $99, $93, $9b, $51, $03, $00, $70, $84, $98, $95, $a2, $95
+db $57, $a3, $50, $9a, $a5, $a3, $a4, $50, $9f, $9e, $95, $50, $a0, $a2, $9f, $92
+db $9c, $95, $9d, $5e, $5e, $5e, $03, $00, $0a, $28, $12, $c6, $00
+
+ORG $C61229
+db $79, $50, $91, $9c, $9d, $9f, $a3, $a4, $50, $96, $9f, $a5, $9e, $94, $50, $1C
+db $05, $70, $a3, $5c, $50, $92, $a5, $a4
+
+ORG $D5ECC3
+db $c0,$01,$b5,$04,$03,$1d
+
+ORG $C60351
+db $48, $00, $0B, $05, $C6
+
+ORG $C60358
+db $AE, $00, $28, $12, $C6
+
+ORG $C6050B
+db $70, $89, $9f, $a5, $50, $94, $99, $94, $50, $99, $a4, $51, $03, $00, $70, $87
+db $98, $99, $9c, $95, $50, $a9, $9f, $a5, $50, $a7, $95, $a2, $95, $50, $94, $9f
+db $a7, $9e, $50, $a4, $98, $95, $a2, $95, $5c, $50, $79, $50, $a3, $91, $a7, $50
+db $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97, $50, $97, $9c, $99, $9d, $9d, $95
+db $a2, $99, $9e, $97, $50, $99, $9e, $50, $a4, $98, $95, $50, $93, $91, $a6, $95
+db $50, $a7, $91, $9c, $9c, $5e, $03, $00, $70, $71, $a3, $50, $a0, $a2, $9f, $9d
+db $99, $a3, $95, $94, $5c, $50, $a9, $9f, $a5, $50, $93, $91, $9e, $50, $98, $91
+db $a6, $95, $50, $99, $a4, $5e, $03, $1d, $00, $ff, $b6, $1b, $02, $ac, $05, $c6
+db $00, $1d, $0e, $ff, $01, $08, $b6, $dc, $c7, $ff, $0a, $a7, $05, $C6, $50, $97
+db $9f, $a4, $50, $a4, $98, $95, $50, $1c, $05, $b6, $51, $59, $04, $5a, $00, $13
+db $02, $70, $89, $9f, $a5, $50, $94, $9f, $9e, $57, $a4, $50, $98, $91, $a6, $95
+db $50, $91, $9e, $a9, $50, $a2, $9f, $9f, $9d, $50, $96, $9f, $a2, $50, $99, $a4
+db $5c, $50, $a4, $98, $9f, $a5, $97, $98, $5e, $13, $02; Miner
+
+ORG $C6034A
+db $5A, $00, $5F, $00, $C6
+
+ORG $C6005F
+db $70, $79, $50, $98, $9f, $a0, $95, $50, $a4, $98, $95, $50, $93, $99, $a4, $a9
+db $50, $94, $9f, $95, $a3, $9e, $57, $a4, $50, $96, $99, $9e, $94, $50, $9f, $a5
+db $a4, $50, $91, $92, $9f, $a5, $a4, $50, $a4, $98, $99, $a3, $50, $98, $9f, $9c
+db $95, $5e, $5e, $5e, $13, $02
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C8B53F
+db $70, $7d, $a9, $50, $a7, $99, $96, $95, $50, $a7, $91, $9e, $a4, $a3, $50, $a4
+db $9f, $50, $a3, $a4, $91, $a9, $50, $98, $95, $a2, $95, $50, $91, $9e, $94, $50
+db $a3, $a4, $91, $a2, $95, $50, $91, $a4, $50, $91, $50, $a2, $9f, $93, $9b, $50
+db $91, $9c, $9c, $50, $94, $91, $a9, $5e, $03, $00, $70, $7f, $9e, $50, $a4, $9f
+db $a0, $50, $9f, $96, $50, $a4, $98, $91, $a4, $5c, $10, $07, $50, $a4, $98, $95
+db $50, $7b, $a2, $91, $9b, $95, $9e, $50, $98, $91, $a3, $50, $92, $95, $95, $9e
+db $50, $a4, $95, $a2, $a2, $9f, $a2, $99, $aa, $99, $9e, $97, $50, $a4, $98, $95
+db $50, $a3, $95, $91, $a3, $5e, $03, $00, $70, $79, $57, $9d, $50, $9e, $9f, $a4
+db $50, $98, $91, $a0, $a0, $a9, $50, $91, $92, $9f, $a5, $a4, $50, $99, $a4, $5c
+db $10, $08, $50, $92, $a5, $a4, $50, $79, $50, $98, $91, $a6, $95, $50, $a4, $9f
+db $50, $94, $9f, $50, $9d, $a9, $50, $9a, $9f, $92, $50, $91, $a3, $50, $91, $50
+db $a3, $91, $99, $9c, $9f, $a2, $5e, $03, $00, $70, $87, $91, $9e, $a4, $50, $a4
+db $9f, $50, $a3, $91, $99, $9c, $50, $a4, $9f, $50, $83, $93, $91, $a2, $91, $92
+db $91, $6f, $03, $00, $0a, $7b, $b7, $c8; Summers sailor
+
+ORG $C8B52B
+db $DB, $03
+
+ORG $C8B532
+db $69, $02
+
+ORG $C68955
+db $70, $78, $95, $9c, $9c, $9f, $5c, $10, $07, $50, $99, $a3, $50, $a4, $98, $99
+db $a3, $50, $19, $10, $01, $1b, $00, $1c, $02, $ff, $6f, $03, $00, $70, $83, $9f
+db $a2, $a2, $a9, $50, $96, $9f, $a2, $50, $93, $9f, $9e, $a4, $91, $93, $a4, $99
+db $9e, $97, $50, $a9, $9f, $a5, $50, $9c, $99, $9b, $95, $50, $a4, $98, $99, $a3
+db $5c, $03, $00, $70, $92, $a5, $a4, $50, $79, $50, $a2, $95, $91, $9c, $9c, $a9
+db $50, $9e, $95, $95, $94, $50, $a9, $9f, $a5, $50, $a4, $9f, $50, $93, $91, $a4
+db $93, $98, $50, $a5, $a0, $50, $9f, $9e, $50, $a9, $9f, $a5, $a2, $50, $91, $a3
+db $a9, $9e, $93, $50, $a0, $a2, $9f, $97, $a2, $95, $a3, $a3, $5e, $03, $00, $70
+db $79, $50, $9b, $9e, $9f, $a7, $50, $a9, $9f, $a5, $57, $a2, $95, $50, $92, $a5
+db $a3, $a9, $50, $a3, $91, $a6, $99, $9e, $97, $50, $a4, $98, $95, $50, $a7, $9f
+db $a2, $9c, $94, $50, $91, $9e, $94, $50, $91, $9c, $9c, $5c, $10, $08, $50, $92
+db $a5, $a4, $50, $79, $57, $9d, $50, $a3, $a4, $99, $9c, $9c, $50, $99, $9e, $50
+db $72, $7b, $5e, $03, $00, $70, $74, $9f, $9e, $57, $a4, $50, $a7, $9f, $a2, $a2
+db $a9, $5d, $10, $10, $50, $79, $50, $98, $99, $9e, $a4, $95, $94, $50, $9d, $9f
+db $a2, $95, $50, $99, $a4, $95, $9d, $a3, $5e, $50, $79, $50, $9a, $a5, $a3, $a4
+db $50, $9e, $95, $95, $94, $50, $9d, $a9, $5e, $5e, $5e, $03, $00, $70, $87, $98
+db $91, $a4, $6f, $03, $00, $70, $87, $98, $91, $a4, $6f, $03, $00, $70, $84, $98
+db $99, $a3, $50, $99, $a3, $50, $a4, $98, $95, $50, $a7, $a2, $9f, $9e, $97, $50
+db $9d, $a5, $9c, $a4, $99, $a7, $9f, $a2, $9c, $94, $6f, $03, $00, $70, $7f, $9f
+db $a0, $a3, $51, $03, $00, $70, $77, $9f, $a4, $a4, $91, $50, $97, $9f, $51, $0A
+db $A3, $8A, $C6, $a6, $95, $a2, $50, $a4, $91, $9c, $9b, $95, $94, $51, $03, $00
+db $70, $5a, $72, $95, $95, $95, $95, $95, $a0, $5a, $13, $18, $04, $1f, $03, $04
+db $11, $04, $02; Summers Tony Call
+
+ORG $C8B9BC
+db $D7, $00
+
+ORG $C8A020
+db $3D, $00
+
+ORG $C77EE8
+db $19, $1F, $1B, $04, $70, $0A, $BC, $7E, $C7
+
+ORG $D56FCC
+db $4F
+
+ORG $C8A992
+db $7F, $AA
+
+ORG $CF1DCB
+db $DC, $A0, $EF
+
+ORG $C8AAE6
+db $0A, $3D, $6E, $C8
+
+ORG $C86E3D
+db $07, $57, $01, $1b, $03, $ed, $aa, $c8, $00, $0a, $b0, $ab, $c8
+
+ORG $C8B226
+db $04
+
+ORG $C920C5
+db $08, $01, $95, $fe, $00, $18, $04, $1f, $21, $24, $1f, $ec, $ff, $06, $02; Get magic cake item
+
+ORG $D5ECCB
+db $89, $02, $63, $01, $05, $00; Warp to summers cart
+
+ORG $C8B199
+db $3D; Magic Tart shop
+
+ORG $C92E90
+db $0A, $50, $06, $61
+
+ORG $610650
+db $0e, $01, $0d, $01, $1d, $19, $00, $1b, $03, $6e, $06, $61, $00, $19, $10, $00
+db $0b, $04, $1b, $03, $87, $08, $61, $00, $0f, $0a, $52, $06, $61, $00, $0e, $01
+db $0d, $01, $1d, $19, $00, $1b, $03, $8c, $06, $61, $00, $19, $10, $00, $0b, $03
+db $1b, $03, $bd, $07, $61, $00, $0f, $0a, $70, $06, $61, $00, $0e, $01, $0d, $01
+db $1d, $19, $00, $1b, $03, $aa, $06, $61, $00, $19, $10, $00, $0b, $02, $1b, $03
+db $38, $07, $61, $00, $0f, $0a, $8e, $06, $61, $00, $70, $83, $9f, $5e, $5e, $5e
+db $50, $a9, $9f, $a5, $57, $a2, $95, $50, $96, $a2, $9f, $9d, $50, $7f, $9e, $95
+db $a4, $a4, $6f, $03, $00, $70, $89, $9f, $a5, $50, $9d, $a5, $a3, $a4, $50, $92
+db $95, $50, $9f, $9e, $50, $91, $50, $a0, $a2, $95, $a4, $a4, $a9, $50, $99, $9d
+db $a0, $9f, $a2, $a4, $91, $9e, $a4, $50, $9a, $9f, $a5, $a2, $9e, $95, $a9, $5c
+db $10, $07, $50, $a4, $9f, $50, $93, $9f, $9d, $95, $50, $a3, $9f, $50, $96, $91
+db $a2, $5e, $03, $00, $70, $79, $50, $92, $95, $a4, $50, $a9, $9f, $a5, $50, $9d
+db $a5, $a3, $a4, $50, $9d, $99, $a3, $a3, $50, $98, $9f, $9d, $95, $50, $95, $a6
+db $95, $a2, $a9, $50, $9f, $9e, $93, $95, $50, $99, $9e, $50, $91, $a7, $98, $99
+db $9c, $95, $51, $03, $0a, $88, $2f, $c9, $08, $7f, $dc, $c7, $00, $1b, $03, $aa
+db $06, $61, $00
+db $0A
+dl PaulaBoatText
+NotSoloPaula:
+db $5e, $5e, $50, $a4, $98, $91, $a4, $50, $97
+db $99, $a2, $9c, $50, $9f, $a6, $95, $a2, $50, $a4, $98, $95, $a2, $95, $5e, $5e
+db $5e, $03, $00, $70, $a9, $9f, $a5, $a2, $50, $92, $a5, $94, $94, $a9, $5c, $10
+db $07, $50, $a2, $99, $97, $98, $a4, $6f, $5e, $5e, $5e, $10, $14, $50, $89, $9f
+db $a5, $50, $a4, $a7, $9f, $50, $9f, $9e, $50, $91, $50, $a2, $9f, $9d, $91, $9e
+db $a4, $99, $93, $50, $a6, $91, $93, $91, $a4, $99, $9f, $9e, $6f, $03, $00, $70
+db $78, $91, $51, $10, $07, $50, $79, $57, $9d, $50, $9a, $a5, $a3, $a4, $50, $9b
+db $99, $94, $94, $99, $9e, $97, $51, $03, $00, $0a, $88, $2f, $c9, $08, $7f, $dc
+db $c7, $00, $1b, $03, $8c, $06, $61, $00
+db $0A
+dl JeffBoatText
+NotSoloJeff:
+db $5e, $5e, $50, $a4
+db $98, $91, $a4, $50, $97, $a5, $a9, $50, $a7, $99, $a4, $98, $50, $a4, $98, $95
+db $50, $97, $9c, $91, $a3, $a3, $95, $a3, $5e, $5e, $5e, $03, $00, $70, $a9, $9f
+db $a5, $a2, $50, $92, $a5, $94, $94, $a9, $5c, $10, $07, $50, $a2, $99, $97, $98
+db $a4, $6f, $5e, $5e, $5e, $10, $14, $50, $78, $95, $50, $93, $91, $9d, $95, $50
+db $96, $a2, $9f, $9d, $50, $87, $99, $9e, $a4, $95, $a2, $a3, $6f, $03, $00, $70
+db $84, $98, $91, $a4, $57, $a3, $50, $a0, $a2, $95, $a4, $a4, $a9, $50, $96, $91
+db $a2, $50, $91, $a7, $91, $a9, $5e, $03, $00, $70, $89, $9f, $a5, $50, $97, $a5
+db $a9, $a3, $50, $9d, $a5, $a3, $a4, $50, $92, $95, $50, $97, $9f, $9f, $94, $50
+db $96, $a2, $99, $95, $9e, $94, $a3, $51, $03, $00, $70, $7d, $91, $a9, $92, $95
+db $50, $98, $95, $50, $93, $9f, $a5, $9c, $94, $50, $96, $99, $a8, $50, $a4, $98
+db $95, $50, $95, $9e, $97, $99, $9e, $95, $50, $96, $9f, $a2, $50, $9d, $95, $6f
+db $03, $00, $0a, $88, $2f, $c9, $00, $08, $7f, $dc, $c7, $00, $1b, $03, $6e, $06
+db $61, $00, $0a, $9e, $2e, $c9
+
+ORG $C92CDC
+db $db, $03
+
+ORG $C31001
+db $db, $03
+
+ORG $C3107B
+db $db, $03
+
+ORG $C92DDB
+db $db, $03
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $CFB3FC
+db $00
+
+ORG $CFB3F5
+db $3D
+
+ORG $D015BA
+db $C0, $FF
+
+ORG $D0FFC0
+db $DC, $83, $02, $00, $1d, $01, $AA, $00, $21, $01, $b5; Winters gate map change
+
+ORG $C6CF2D; Snow Wood guard
+db $0A, $73, $5F, $C7
+
+ORG $C75F73
+db $06, $dc, $03, $4a, $f0, $c5, $00, $70, $7d, $91, $a8, $a7, $95, $9c, $9c, $50
+db $93, $91, $a5, $97, $98, $a4, $50, $9d, $95, $50, $93, $98, $95, $91, $a4, $99
+db $9e, $97, $50, $9f, $9e, $50, $91, $50, $9d, $91, $a4, $98, $50, $a4, $95, $a3
+db $a4, $5c, $10, $07, $50, $a3, $9f, $50, $98, $95, $50, $9d, $91, $94, $95, $50
+db $9d, $95, $50, $a3, $a4, $91, $9e, $94, $50, $9f, $a5, $a4, $50, $98, $95, $a2
+db $95, $5e, $03, $1d, $05, $ff, $a7, $1b, $03, $89, $ef, $c5, $ff, $00, $0e, $01
+db $0d, $01, $1d, $19, $00, $1b, $03, $f0, $5f, $c7, $00, $19, $10, $00, $0b, $03
+db $1b, $03, $79, $60, $c7, $00, $0f, $0a, $d3, $5f, $c7, $00, $00, $70, $79, $57
+db $9d, $50, $a3, $9f, $a2, $a2, $a9, $5c, $10, $07, $50, $19, $10, $01, $1b, $04
+db $1c, $02, $00, $5c, $03, $00, $70, $92, $a5, $a4, $50, $7d, $91, $a8, $a7, $95
+db $9c, $9c, $50, $a3, $91, $a9, $a3, $50, $79, $50, $93, $91, $9e, $57, $a4, $50
+db $9c, $95, $a4, $50, $91, $9e, $a9, $9f, $9e, $95, $50, $99, $9e, $a4, $9f, $50
+db $a4, $98, $95, $50, $a3, $93, $98, $9f, $9f, $9c, $50, $a4, $98, $91, $a4, $57
+db $a3, $50, $9e, $9f, $a4, $50, $91, $50, $a2, $95, $97, $99, $a3, $a4, $95, $a2
+db $95, $94, $50, $a3, $a4, $a5, $94, $95, $9e, $a4, $5e, $03, $00, $70, $84, $98
+db $9f, $a3, $95, $50, $91, $a2, $95, $50, $a4, $98, $95, $50, $a2, $a5, $9c, $95
+db $a3, $5e, $5e, $5e, $13, $02, $0e, $03, $08, $7f, $dc, $c7, $00, $1b, $03, $f0
+db $5f, $c7, $00, $70, $78, $95, $a9, $5c, $50, $1c, $02, $03, $5e, $50, $7d, $91
+db $a8, $a7, $95, $9c, $9c, $50, $a3, $91, $a9, $a3, $50, $a9, $9f, $a5, $57, $a2
+db $95, $50, $a3, $a5, $a3, $a0, $95, $9e, $94, $95, $94, $50, $a5, $9e, $a4, $99
+db $9c, $50, $a4, $98, $95, $50, $a7, $9f, $a2, $9c, $94, $50, $99, $a3, $50, $a3
+db $91, $a6, $95, $94, $5e, $03, $00, $70, $7e, $9f, $5c, $10, $08, $50, $9e, $9f
+db $5c, $10, $08, $50, $a9, $9f, $a5, $57, $a2, $95, $50, $9e, $9f, $a4, $50, $99
+db $9e, $50, $a4, $a2, $9f, $a5, $92, $9c, $95, $50, $9f, $a2, $50, $91, $9e, $a9
+db $a4, $98, $99, $9e, $97, $5e, $03, $0a, $3e, $ef, $c5
+
+ORG $C5EF3E
+db $00, $70, $78, $95, $50, $9a, $a5, $a3, $a4, $50, $94, $9f, $95, $a3, $9e, $57
+db $a4, $50, $a7, $91, $9e, $a4, $50, $a9, $9f, $a5, $50, $a4, $9f, $50, $a7, $9f
+db $a2, $a2, $a9, $50, $91, $92, $9f, $a5, $a4, $50, $a3, $a4, $a5, $94, $a9, $99
+db $9e, $97, $50, $a7, $98, $99, $9c, $95, $50, $9f, $9e, $50, $91, $9e, $50, $91
+db $94, $a6, $95, $9e, $a4, $a5, $a2, $95, $5e, $13, $02, $00, $70, $87, $98, $91
+db $a4, $57, $a3, $50, $a4, $98, $91, $a4, $6f, $03, $00, $70, $89, $9f, $a5, $50
+db $98, $91, $a6, $95, $50, $91, $50, $9c, $95, $a4, $a4, $95, $a2, $50, $a4, $9f
+db $50, $94, $a2, $9f, $a0, $50, $9f, $96, $96, $50, $96, $9f, $a2, $50, $84, $9f
+db $9e, $a9, $6f, $03, $00, $70, $87, $95, $9c, $9c, $10, $30, $5e, $10, $30, $5e
+db $10, $30, $5e, $50, $79, $50, $97, $a5, $95, $a3, $a3, $50, $99, $a4, $57, $a3
+db $50, $91, $9c, $a2, $99, $97, $98, $a4, $50, $a4, $9f, $50, $9c, $95, $a4, $50
+db $a9, $9f, $a5, $50, $99, $9e, $50, $a3, $9f, $50, $a9, $9f, $a5, $50, $93, $91
+db $9e, $50, $94, $a2, $9f, $a0, $50, $99, $a4, $50, $9f, $96, $96, $5e, $10, $30
+db $5e, $10, $30, $5e, $10, $30, $03, $00, $70, $7a, $a5, $a3, $a4, $50, $a3, $a4
+db $91, $a9, $50, $9f, $a5, $a4, $50, $9f, $96, $50, $a4, $a2, $9f, $a5, $92, $9c
+db $95, $51, $03, $18, $04, $04, $dc, $03, $1f, $e9, $7f, $02, $1f, $16, $7f, $02
+db $01, $1f, $02, $77, $1f, $15, $2e, $00, $07, $01, $01, $02, $70, $78, $a5, $a2
+db $a2, $a9, $50, $a5, $a0, $50, $91, $9e, $94, $50, $94, $9f, $50, $a7, $98, $91
+db $a4, $50, $a9, $9f, $a5, $50, $9e, $95, $95, $94, $50, $a4, $9f, $51, $13, $02
+
+ORG $CF6027
+db $7D ;Boarding school event music
+
+ORG $C6FA03
+db $FF ;Skips broken key text
+
+ORG $C6FA0A
+db $CD ;Opens locker with locker key
+
+ORG $C6FA26
+db $CD
+
+ORG $C6FA2A
+db $75
+
+ORG $C6F98C
+db $0a, $26, $FE, $C4
+
+ORG $C6F932
+db $0a, $41, $FE, $C4
+
+ORG $C6F905
+db $0a, $F0, $FD, $C4
+
+ORG $C6F95F
+db $0a, $0B, $FE, $C4
+
+ORG $C4FDF0
+db $07, $58, $00, $1b, $03, $93, $fa, $c6, $00, $08, $0b, $f9, $c6, $00, $1d, $03; FIX TEXT JUMPS!!!!
+db $ff, $1b, $02, $0A, $FE, $C4, $00, $04, $58, $00, $02, $07, $F4, $03, $1b, $03
+db $93, $fa, $c6, $00, $08, $65, $f9, $c6, $00, $1d, $03, $ff, $1b, $02, $0a, $FE
+db $C4, $00, $04, $F4, $03, $02, $07, $17, $00, $1b, $03, $93, $fa, $c6, $00, $08
+db $92, $f9, $c6, $00, $1d, $03, $ff, $1b, $02, $0a, $FE, $C4, $00, $04, $17, $00
+db $02, $07, $4e, $01, $1b, $03, $93, $fa, $c6, $00, $08, $38, $f9, $c6, $00, $1d
+db $03, $ff, $1b, $02, $0A, $FE, $C4, $00, $04, $4e, $01, $02; Fix locker checks
+
+ORG $C6AA44
+db $70, $7f, $98, $5c, $10, $0f, $50, $a9, $9f, $a5, $50, $a3, $a4, $91, $a2, $a4
+db $9c, $95, $94, $50, $9d, $95, $5e, $03, $00, $70, $79, $57, $9d, $50, $a7, $9f
+db $a2, $9b, $99, $9e, $97, $50, $9f, $9e, $50, $71, $a2, $93, $98, $99, $a0, $95
+db $9c, $91, $97, $9f, $50, $99, $9e, $a4, $95, $97, $a2, $91, $a4, $99, $9f, $9e
+db $50, $96, $9f, $a2, $50, $a4, $98, $95, $50, $83, $a5, $a0, $95, $a2, $50, $72
+db $91, $92, $a9, $50, $7d, $91, $97, $99, $93, $50, $74, $a2, $95, $91, $9d, $50
+db $72, $9f, $a9, $5c, $03, $00, $70, $92, $a5, $a4, $50, $99, $a4, $50, $99, $a3
+db $9e, $57, $a4, $50, $97, $9f, $99, $9e, $97, $50, $a6, $95, $a2, $a9, $50, $a7
+db $95, $9c, $9c, $5e, $03, $00, $70, $79, $96, $50, $9f, $9e, $9c, $a9, $50, $80
+db $98, $91, $a2, $50, $a7, $95, $a2, $95, $50, $98, $95, $a2, $95, $5e, $03, $00
+db $70, $78, $95, $57, $94, $50, $92, $95, $50, $91, $92, $9c, $95, $50, $a4, $9f
+db $50, $a0, $a2, $9f, $a6, $99, $94, $95, $50, $91, $50, $97, $a2, $95, $91, $a4
+db $50, $94, $95, $91, $9c, $50, $9f, $96, $50, $98, $95, $9c, $a0, $5e, $03, $00
+db $70, $71, $9e, $a9, $a7, $91, $a9, $a3, $5c, $10, $0f, $50, $79, $57, $94, $50
+db $9c, $9f, $a6, $95, $50, $a4, $9f, $50, $98, $95, $9c, $a0, $50, $a9, $9f, $a5
+db $50, $a7, $99, $a4, $98, $50, $a9, $9f, $a5, $a2, $50, $91, $94, $a6, $95, $9e
+db $a4, $a5, $a2, $95, $5c, $03, $00, $70, $92, $a5, $a4, $50, $79, $57, $9d, $50
+db $93, $9f, $9d, $9d, $99, $a4, $a4, $95, $94, $50, $a4, $9f, $50, $9d, $a9, $50
+db $a7, $9f, $a2, $9b, $5e, $50, $79, $50, $93, $91, $9e, $50, $91, $a4, $50, $9c
+db $95, $91, $a3, $a4, $50, $97, $99, $a6, $95, $50, $a9, $9f, $a5, $50, $a4, $98
+db $99, $a3, $50, $1c, $05, $01, $5e, $03, $0a, $d5, $ab, $c6, $00
+
+ORG $C6ABD5; Maxwell
+db $00, $1d, $00, $ff, $cd, $1b, $02, $da, $ac, $c6, $00, $1d, $03, $ff, $1d, $0e
+db $00, $cd, $08, $b6, $dc, $c7, $00, $03, $00, $70, $77, $9f, $9f, $94, $50, $9c
+db $a5, $93, $9b, $50, $9f, $a5, $a4, $50, $a4, $98, $95, $a2, $95, $51, $13, $04
+db $5d, $02, $02
+
+ORG $CFB163
+db $49, $00, $02; Consistency for gate man
+
+ORG $C6CDAF
+db $70, $79, $50, $93, $91, $9e, $57, $a4, $50, $92, $16, $ad, $15, $23, $15, $91
+db $94, $9f, $15, $0a, $a3, $9f, $50, $9e, $16, $a6, $17, $17, $13, $02; Cookie guy
+
+ORG $CFB0CA
+db $49, $00, $02; Tony in bed
+
+ORG $CFB0D6
+db $00, $00, $04, $5E, $02
+
+ORG $CFB0DE
+db $10, $99, $EE
+
+ORG $EE9910
+db $70, $71, $a2, $95, $50, $a9, $9f, $a5, $50, $9c, $9f, $9f, $9b, $99, $9e, $97
+db $50, $96, $9f, $a2, $50, $84, $9f, $9e, $a9, $6f, $03, $00, $70, $78, $95, $57
+db $a3, $50, $9e, $9f, $a4, $50, $98, $95, $a2, $95, $50, $a2, $99, $97, $98, $a4
+db $50, $9e, $9f, $a7, $5e, $10, $10, $50, $78, $95, $50, $a3, $91, $99, $94, $50
+db $98, $95, $50, $a7, $91, $a3, $50, $97, $9f, $99, $9e, $97, $50, $a4, $9f, $50
+db $99, $9e, $a6, $95, $a3, $a4, $99, $97, $91, $a4, $95, $50, $83, $a4, $9f, $9e
+db $95, $98, $95, $9e, $97, $95, $5c, $03, $00, $70, $92, $a5, $a4, $50, $9e, $95
+db $a6, $95, $a2, $50, $93, $91, $9d, $95, $50, $92, $91, $93, $9b, $5e, $5e, $5e
+db $03, $00, $70, $79, $57, $9d, $50, $a3, $a4, $91, $a2, $a4, $99, $9e, $97, $50
+db $a4, $9f, $50, $97, $95, $a4, $50, $91, $50, $9c, $99, $a4, $a4, $9c, $95, $50
+db $a7, $9f, $a2, $a2, $99, $95, $94, $5e, $03, $00, $70, $79, $96, $50, $a9, $9f
+db $a5, $50, $94, $9f, $9e, $57, $a4, $50, $9d, $99, $9e, $94, $5c, $50, $79, $57
+db $9d, $50, $93, $9f, $9d, $99, $9e, $97, $50, $a7, $99, $a4, $98, $50, $a9, $9f
+db $a5, $51, $03, $00, $0A, $D4, $B0, $EE, $00, $1b, $03, $F7, $99, $EE, $00, $1f
+db $1e, $50, $02, $06, $04, $80, $00, $02; Winters character slot
+
+ORG $D808FB
+;db $82 Winters bed FIX THIS!!!!!!
+
+ORG $C7A1BA
+db $02; Leaving Snow Wood hotspot. also free text?
+
+ORG $C6AE5C
+db $0A, $A3, $AE, $C6
+
+ORG $CFB1FC
+db $49, $00, $02
+
+ORG $C6AA30
+db $5D, $02
+
+ORG $C6AD75
+db $0A, $BB, $A1, $C7
+
+ORG $C7A1BB
+db $70, $87, $95, $57, $a2, $95, $50, $9e, $9f, $a4, $50, $a3, $95, $9c, $9c, $99
+db $9e, $97, $50, $9d, $9f, $9e, $9b, $95, $a9, $a3, $50, $91, $9e, $a9, $9d, $9f
+db $a2, $95, $5e, $03, $00, $70, $84, $98, $95, $50, $9c, $91, $a3, $a4, $50, $9f
+db $9e, $95, $50, $a7, $95, $50, $98, $91, $94, $50, $a2, $91, $9e, $50, $91, $a7
+db $91, $a9, $50, $a4, $9f, $50, $a4, $98, $95, $50, $a3, $9f, $a5, $a4, $98, $5e
+db $03, $00, $70, $71, $9c, $9c, $50, $79, $50, $98, $91, $a6, $95, $50, $99, $a3
+db $50, $a4, $98, $99, $a3, $50, $1c, $05, $68, $5e, $03, $00, $70, $79, $a4, $50
+db $93, $9f, $a3, $a4, $a3, $50, $91, $50, $92, $a5, $93, $9b, $5e, $03, $00, $70
+db $83, $a4, $99, $9c, $9c, $50, $a7, $91, $9e, $a4, $50, $99, $a4, $6f, $0a, $e8
+db $ad, $c6, $03
+
+ORG $C6AE07
+db $0A, $4D, $A2, $C7
+
+ORG $C7A24D
+db $70, $7f, $9b, $91, $a9, $5c, $10, $0f, $50, $92, $a5, $a4, $50, $79, $50, $93
+db $91, $9e, $57, $a4, $50, $97, $a5, $a2, $91, $9e, $a4, $95, $95, $50, $99, $a4
+db $57, $9c, $9c, $50, $92, $95, $50, $98, $95, $a2, $95, $50, $96, $9f, $a2, $95
+db $a6, $95, $a2, $5e, $5e, $5e, $13, $02
+
+ORG $C6AEC3
+db $0A, $D7, $AE, $C6
+
+ORG $C6AE77
+db $70, $79, $a4, $57, $a3, $50, $9e, $9f, $a4, $50, $a3, $95, $9c, $9c, $99, $9e
+db $97, $5c, $10, $0f, $50, $a3, $9f, $50, $a4, $98, $99, $a3, $50, $9f, $9e, $95
+db $57, $a3, $50, $96, $a2, $95, $95, $5e, $10, $00
+
+ORG $CFB363
+db $00
+
+ORG $C6B18D
+db $06, $76, $02, $77, $b2, $c6, $00, $70, $7b, $99, $5d, $9b, $9b, $a9, $95, $5d
+db $9b, $a9, $95, $5d, $9b, $a9, $95, $5d, $9b, $99, $99, $99, $5e, $03, $00, $70
+db $58, $79, $50, $94, $9f, $9e, $57, $a4, $50, $98, $91, $a6, $95, $50, $91, $9e
+db $a9, $50, $16, $e6, $10, $05, $95, $10, $05, $95, $10, $05, $95, $a7, $15, $1b
+db $97, $a5, $9d, $5e, $10, $10, $50, $73, $91, $9e, $50, $79, $50, $98, $91, $a6
+db $95, $50, $a3, $9f, $9d, $95, $50, $9f, $96, $50, $a9, $9f, $a5, $a2, $a3, $6f
+db $59, $03, $00, $19, $02, $89, $95, $a3, $02, $19, $02, $7e, $9f, $02, $1c, $07
+db $02, $11, $09, $02, $2d, $b2, $c6, $00, $09, $b2, $c6, $00, $12, $70, $7b, $95
+db $95, $50, $9b, $95, $95, $5e, $03, $00, $70, $58, $89, $9f, $a5, $50, $9d, $91
+db $9b, $95, $50, $9d, $95, $50, $a3, $9f, $50, $a3, $91, $94, $51, $59, $13, $02
+db $1d, $05, $ff, $68, $1b, $02, $02, $b2, $c6, $00, $1d, $01, $ff, $68, $1f, $02
+db $76, $10, $10, $12, $70, $58, $84, $98, $95, $50, $9d, $9f, $9e, $9b, $95, $a9
+db $50, $a4, $9f, $9f, $9b, $50, $a4, $98, $95, $50, $95, $9e, $a4, $99, $a2, $95
+db $50, $a0, $91, $93, $9b, $50, $96, $a2, $9f, $9d, $19, $10, $01, $1b, $04, $50
+db $1c, $02, $00, $5e, $59, $03, $00, $04, $76, $02, $70, $7b, $99, $5d, $9b, $9b
+db $a9, $95, $5d, $9b, $a9, $95, $5d, $9b, $a9, $95, $5d, $9b, $99, $99, $99, $5e
+db $03, $00, $0a, $17, $bc, $c6; Bubble monkey gum check
+
+ORG $C6BC00
+db $0A, $8D, $B1, $C6
+
+ORG $C99B52
+db $49
+
+ORG $CFB262
+db $B2, $01, $02
+
+ORG $CFB25D
+db $b6
+
+ORG $CFB284
+db $B2, $01, $02; Saturn in lab
+
+ORG $CF7459
+db $02, $00, $69, $02, $68, $98, $BB, $00
+
+ORG $CFB275
+db $00
+
+ORG $CFB27F
+db $AB
+
+ORG $CFB282
+db $B2
+
+ORG $CFB265
+db $99, $C0, $C6
+
+ORG $CFB287
+db $35, $C0, $C6
+
+ORG $CFB527
+db $8F, $00
+
+ORG $CFB5D1
+db $64, $00
+
+ORG $CFB53D
+db $49, $00, $02
+
+ORG $CFB5EA
+db $14, $F6, $2F
+
+ORG $C6C7DA
+db $49
+
+ORG $2FF600
+db $05, $FE, $02, $06, $de, $03, $97, $F7, $2F, $00, $06, $b2, $01, $98, $f7, $2f
+db $00, $02
+
+ORG $CF1111
+db $00, $F6, $2F
+
+ORG $C6C7E7
+db $70, $7d, $a9, $50, $9d, $91, $a3, $a4, $95, $a2, $5c, $10, $0f, $50, $71, $a0
+db $a0, $9c, $95, $50, $7b, $99, $94, $5c, $10, $0f, $50, $98, $91, $a3, $50, $93
+db $9f, $9d, $a0, $9c, $95, $a4, $95, $94, $50, $98, $99, $a3, $50, $a5, $9c, $a4
+db $99, $9d, $91, $a4, $95, $50, $99, $9e, $a6, $95, $9e, $a4, $99, $9f, $9e, $5e
+db $03, $00, $70, $84, $98, $91, $a4, $57, $a3, $50, $a2, $99, $97, $98, $a4, $5e
+db $10, $10, $50, $84, $98, $95, $50, $1c, $05, $e0, $5e, $03, $00, $70, $79, $50
+db $91, $93, $a4, $a5, $91, $9c, $9c, $a9, $50, $93, $9f, $9e, $a4, $a2, $99, $92
+db $a5, $a4, $95, $94, $50, $91, $9e, $50, $99, $9e, $a6, $91, $9c, $a5, $91, $92
+db $9c, $95, $50, $91, $9d, $9f, $a5, $9e, $a4, $50, $a4, $9f, $50, $a4, $98, $95
+db $50, $a0, $a2, $9f, $9a, $95, $93, $a4, $5e, $5e, $5e, $03, $0a, $0c, $bf, $c6
+
+ORG $C6BF0C
+db $00, $70, $71, $96, $a4, $95, $a2, $50, $a4, $98, $91, $a4, $5c, $10, $0f, $50
+db $98, $95, $50, $a3, $91, $99, $94, $50, $98, $95, $50, $a7, $91, $a3, $50, $98
+db $95, $91, $94, $99, $9e, $97, $50, $98, $9f, $9d, $95, $50, $a4, $9f, $50, $84
+db $a7, $9f, $a3, $9f, $9e, $5e, $03, $00, $70, $79, $50, $92, $95, $9c, $99, $95
+db $a6, $95, $50, $98, $95, $50, $a7, $91, $9e, $a4, $95, $94, $50, $a9, $9f, $a5
+db $50, $a4, $9f, $50, $98, $91, $a6, $95, $50, $98, $99, $a3, $50, $99, $9e, $a6
+db $95, $9e, $a4, $99, $9f, $9e, $5e, $03, $0a, $8c, $c8, $c6; Apple kid mouse text
+
+ORG $C6C099
+db $0A, $50, $F9, $2F
+
+ORG $2FF950
+db $06, $dd, $03, $35, $fb, $2f, $00, $70, $19, $10, $01, $1b, $04, $1c, $02, $00
+db $5c, $10, $0f, $50, $92, $a5, $94, $94, $a9, $51, $10, $10, $50, $89, $9f, $a5
+db $50, $a3, $91, $a6, $95, $94, $50, $a5, $a3, $51, $03, $00, $70, $79, $50, $a7
+db $91, $a3, $50, $a3, $a4, $a5, $94, $a9, $99, $9e, $97, $5c, $10, $0f, $50, $a7
+db $98, $95, $9e, $50, $79, $50, $98, $95, $91, $a2, $94, $50, $a4, $98, $99, $a3
+db $50, $a7, $95, $99, $a2, $94, $50, $9e, $9f, $99, $a3, $95, $50, $9f, $a5, $a4
+db $a3, $99, $94, $95, $5e, $5e, $5e, $03, $00, $70, $79, $50, $a7, $95, $9e, $a4
+db $50, $a4, $9f, $50, $93, $98, $95, $93, $9b, $50, $99, $a4, $50, $9f, $a5, $a4
+db $5c, $10, $0f, $50, $91, $9e, $94, $50, $a4, $98, $95, $9e, $50, $79, $50, $a7
+db $9f, $a5, $9e, $94, $50, $a5, $a0, $50, $98, $95, $a2, $95, $5e, $03, $00, $70
+db $78, $9d, $6f, $10, $15, $50, $89, $9f, $a5, $57, $a2, $95, $50, $98, $95, $a2
+db $95, $50, $96, $9f, $a2, $50, $a4, $98, $95, $50, $1c, $05, $e0, $6f, $03, $00
+db $70, $7f, $98, $51, $50, $79, $50, $9d, $a5, $a3, $a4, $50, $98, $91, $a6, $95
+db $50, $a4, $91, $9b, $95, $9e, $50, $99, $a4, $50, $a7, $99, $a4, $98, $50, $9d
+db $95, $5e, $03, $00, $70, $79, $50, $94, $9f, $9e, $57, $a4, $50, $9e, $95, $95
+db $94, $50, $99, $a4, $50, $91, $9e, $a9, $9d, $9f, $a2, $95, $5c, $50, $a4, $98
+db $9f, $a5, $97, $98, $5c, $10, $0f, $50, $a3, $9f, $50, $a9, $9f, $a5, $50, $93
+db $91, $9e, $50, $98, $91, $a6, $95, $50, $99, $a4, $5e, $03, $00, $70, $1d, $19
+db $00, $1d, $00, $ff, $ad, $1b, $02, $95, $fa, $2f, $00, $1f, $02, $74, $10, $10
+db $1c, $02, $fe, $50, $97, $9f, $a4, $50, $a4, $98, $95, $50, $1c, $05, $e0, $51
+db $04, $dd, $03, $13, $02, $7f, $98, $5e, $5e, $5e, $10, $10, $50, $89, $9f, $a5
+db $50, $94, $9f, $9e, $57, $a4, $50, $98, $91, $a6, $95, $50, $95, $9e, $9f, $a5
+db $97, $98, $50, $a2, $9f, $9f, $9d, $50, $96, $9f, $a2, $50, $99, $a4, $5e, $03
+db $00, $70, $87, $95, $9c, $9c, $5c, $10, $0f, $50, $79, $57, $9c, $9c, $50, $98
+db $91, $9e, $97, $50, $9f, $9e, $50, $a4, $9f, $50, $99, $a4, $50, $96, $9f, $a2
+db $50, $9e, $9f, $a7, $5e, $03, $00, $70, $79, $96, $50, $a9, $9f, $a5, $50, $a7
+db $91, $9e, $a4, $50, $a4, $9f, $50, $9d, $95, $95, $a4, $50, $a5, $a0, $50, $a7
+db $99, $a4, $98, $50, $9d, $95, $50, $9c, $91, $a4, $95, $a2, $5c, $50, $10, $0f
+db $50, $79, $57, $9c, $9c, $50, $92, $95, $50, $91, $a2, $9f, $a5, $9e, $94, $50
+db $74, $a2, $5e, $50, $71, $9e, $94, $9f, $9e, $a5, $a4, $a3, $57, $a3, $50, $9c
+db $91, $92, $5e, $13, $02, $70, $79, $57, $a6, $95, $50, $92, $95, $95, $9e, $50
+db $a4, $98, $99, $9e, $9b, $99, $9e, $97, $50, $91, $92, $9f, $a5, $a4, $50, $6C
+db $71, $a2, $93, $98, $99, $a0, $95, $9c, $91, $97, $9f, $6E, $50, $9c, $91, $a4
+db $95, $9c, $a9, $5e, $13, $02; Stonehenge item
+
+ORG $CFB5E7
+db $49, $00, $02
+
+ORG $CFB5E2
+db $8F
+
+ORG $CF0EC6
+db $86, $f7, $2f
+
+ORG $CF111C
+db $86, $F7, $2F
+
+ORG $2FF786
+db $02, $de, $03, $97, $f7, $2f, $00, $06, $b2, $01, $98, $f7, $2f, $00, $04, $d8
+db $02, $02, $05, $d8, $02, $02
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $CF5B2B
+db $2F; Everdred music
+
+ORG $CF1678
+db $00, $00, $00, $00, $00, $00; Dept. Store entrance
+
+ORG $D4E300
+db $1F, $1F, $20, $3F, $5F, $40, $5F, $40, $5F, $50, $5F, $41, $5E, $43, $5D, $47
+db $1F, $1F, $3F, $3F, $60, $60, $7F, $7F, $7F, $7F, $7F, $7F, $7F, $7E, $7F, $7D
+db $FC, $FC, $06, $FE, $EA, $0E, $FA, $0E, $FA, $2E, $FA, $8E, $FA, $8E, $FA, $0E
+db $FC, $FC, $FE, $FE, $1E, $1E, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FE
+db $58, $4F, $5C, $47, $5E, $43, $5D, $47, $5F, $56, $5F, $40, $7F, $7F, $00, $00
+db $7F, $78, $7F, $7C, $7F, $7E, $7F, $7D, $7F, $7F, $7F, $7F, $7F, $7F, $00, $00
+db $FA, $8E, $7A, $CE, $FA, $8E, $FA, $0E, $FA, $2E, $FA, $0E, $FC, $FC, $00, $00
+db $FE, $FE, $FE, $7E, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FC, $FC, $00, $00
+db $1F, $1F, $20, $3F, $5F, $40, $5F, $40, $5F, $50, $5F, $41, $5E, $43, $5D, $47
+db $1F, $1F, $3F, $3F, $60, $60, $7F, $7F, $7F, $7F, $7F, $7F, $7F, $7E, $7F, $7D
+db $FC, $FC, $06, $FE, $EA, $0E, $FA, $0E, $FA, $2E, $FA, $8E, $FA, $8E, $FA, $0E
+db $FC, $FC, $FE, $FE, $1E, $1E, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FE
+db $58, $4F, $5C, $47, $5E, $43, $5D, $47, $5F, $56, $5F, $40, $7F, $7F, $00, $00
+db $7F, $78, $7F, $7C, $7F, $7E, $7F, $7D, $7F, $7F, $7F, $7F, $7F, $7F, $00, $00
+db $FA, $8E, $7A, $CE, $FA, $8E, $FA, $0E, $FA, $2E, $FA, $0E, $FC, $FC, $00, $00
+db $FE, $FE, $FE, $7E, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FE, $FC, $FC, $00, $00
+
+ORG $D49760
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $3f, $3f, $7f, $7f
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $3f, $00, $7f
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $fc, $fc, $f8, $f8
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $fc, $00, $f8
+
+ORG $6F3F1A
+db $0E
+
+ORG $6F4091
+db $0E
+
+ORG $CF7F39
+db $6C, $68
+
+ORG $C4FDA8
+db $06, $00, $a9, $03, $70, $88, $aa, $03, $70, $a0, $98, $03, $80, $f8, $BE, $02
+db $6c, $68, $bf, $02, $7A, $68, $A3, $03, $98, $88; Electric box stuff
+
+ORG $CF688D
+db $05, $00
+
+ORG $CFB834
+db $03, $6E, $01
+
+ORG $CFB83C
+db $00, $10, $97, $EE
+
+ORG $08957A
+db $02
+
+ORG $CFB829
+db $00, $00, $00
+
+ORG $08958D
+db $06, $39, $01, $d5, $95, $08, $00, $1f, $07, $02, $10, $0a, $1f, $01, $00, $1f
+db $e1, $1f, $00, $01, $10, $14, $1f, $e1, $14, $01, $01, $10, $14, $1f, $e1, $1f
+db $00, $01, $10, $01, $1f, $e1, $14, $01, $01, $10, $01, $1f, $e1, $1f, $00, $01
+db $10, $20, $04, $39, $00, $04, $39, $01, $04, $2b, $02, $1f, $1e, $98, $03, $06
+db $1f, $1e, $a3, $03, $06, $1f, $03, $02, $1f, $07, $02, $10, $0a, $1f, $01, $00
+db $1f, $e1, $14, $01, $01, $10, $14, $1f, $e1, $1f, $00, $01, $10, $14, $1f, $e1
+db $14, $01, $01, $10, $01, $1f, $e1, $1f, $00, $01, $10, $01, $1f, $e1, $14, $01
+db $01, $10, $20, $05, $39, $00, $05, $39, $01, $05, $2b, $02, $1f, $03, $02, $95; Electric box event
+
+ORG $EE9710
+db $06, $8B, $00, $4d, $98, $EE, $00, $0e, $01, $0d, $01, $1d, $19, $00, $1b, $03
+db $35, $97, $EE, $00, $19, $10, $00, $0b, $03, $1b, $03, $79, $97, $EE, $00, $0f
+db $0a, $19, $97, $EE, $00, $70, $58, $79, $a4, $57, $a3, $50, $91, $9e, $50, $95
+db $9c, $95, $93, $a4, $a2, $99, $93, $91, $9c, $50, $92, $9f, $a8, $5e, $10, $10
+db $50, $79, $a4, $50, $a7, $9f, $a5, $9c, $94, $50, $a4, $91, $9b, $95, $50, $91
+db $50, $97, $95, $9e, $99, $a5, $a3, $50, $a4, $9f, $50, $9f, $a0, $95, $a2, $91
+db $a4, $95, $50, $99, $a4, $5e, $59, $13, $02, $0e, $03, $08, $7f, $dc, $c7, $00
+db $1b, $03, $35, $97, $EE, $00, $70, $58, $1c, $02, $03, $50, $9f, $a0, $95, $9e
+db $95, $94, $50, $a4, $98, $95, $50, $95, $9c, $95, $93, $a4, $a2, $99, $93, $91
+db $9c, $50, $92, $9f, $a8, $5e, $1f, $02, $75, $10, $15, $00, $70, $84, $98, $95
+db $a2, $95, $57, $a3, $50, $91, $9e, $50, $99, $9d, $a0, $9f, $a2, $a4, $91, $9e
+db $a4, $5d, $9c, $9f, $9f, $9b, $99, $9e, $97, $50, $a3, $a7, $99, $a4, $93, $98
+db $50, $98, $95, $a2, $95, $5e, $10, $10, $50, $80, $a5, $9c, $9c, $50, $99, $a4
+db $6f, $00, $19, $02, $89, $95, $a3, $02, $19, $02, $7e, $9f, $02, $1c, $07, $02
+db $11, $09, $02, $20, $98, $EE, $00, $fb, $97, $EE, $00, $12, $70, $19, $10, $01
+db $1b, $04, $1c, $02, $00, $50, $94, $99, $94, $50, $9e, $9f, $a4, $50, $a0, $a5
+db $9c, $9c, $50, $a4, $98, $95, $50, $a3, $a7, $99, $a4, $93, $98, $5e, $13, $02
+db $12, $70, $19, $10, $01, $1b, $04, $1c, $02, $00, $50, $a0, $a5, $9c, $9c, $95
+db $94, $50, $a4, $98, $95, $50, $a3, $a7, $99, $a4, $93, $98, $5e, $1f, $02, $77
+db $10, $15, $1f, $02, $75, $10, $20, $18, $04, $0a, $8d, $95, $08, $70, $58, $84
+db $98, $95, $50, $95, $9c, $95, $93, $a4, $a2, $99, $93, $91, $9c, $50, $92, $9f
+db $a8, $50, $99, $a3, $50, $9c, $9f, $93, $9b, $95, $94, $5e, $5e, $5e, $59, $13
+db $02; Electric box text
+
+ORG $CFB824
+db $5F, $01
+
+ORG $C8972A
+db $70, $58, $79, $50, $98, $95, $91, $a2, $94, $50, $a4, $98, $91, $a4, $50, $91
+db $50, $a3, $93, $91, $a2, $a9, $50, $9d, $9f, $9e, $a3, $a4, $95, $a2, $50, $9c
+db $99, $a6, $95, $a3, $50, $9f, $9e, $50, $a4, $98, $95, $50, $a4, $9f, $a0, $50
+db $96, $9c, $9f, $9f, $a2, $5e, $03, $00, $70, $84, $98, $95, $a9, $50, $a3, $91
+db $a9, $50, $99, $a4, $50, $9f, $9e, $9c, $a9, $50, $93, $9f, $9d, $95, $a3, $50
+db $9f, $a5, $a4, $50, $a7, $98, $95, $9e, $50, $a4, $98, $95, $50, $9c, $99, $97
+db $98, $a4, $a3, $50, $91, $a2, $95, $50, $a4, $a5, $a2, $9e, $95, $94, $50, $9f
+db $96, $96, $5e, $5e, $5e, $03, $00, $70, $79, $50, $92, $95, $a4, $50, $98, $95
+db $50, $98, $91, $a3, $50, $a3, $9f, $9d, $95, $50, $97, $9f, $9f, $94, $50, $a4
+db $a2, $95, $91, $a3, $a5, $a2, $95, $5e, $5e, $5e, $50, $10, $10, $50, $79, $50
+db $97, $a5, $95, $a3, $a3, $50, $99, $a4, $57, $a3, $50, $91, $50, $9d, $9f, $a5
+db $a3, $95, $57, $a3, $50, $a3, $99, $a8, $a4, $98, $50, $a3, $95, $9e, $a3, $95
+db $5e, $59, $13, $02; Mouse hint
+
+ORG $C82CFC
+db $0A, $F9, $99, $EE
+
+ORG $EE99F9
+db $70, $5e, $5e, $5e, $79, $96, $50, $a9, $9f, $a5, $57, $a2, $95, $50, $98, $95
+db $a2, $95, $50, $96, $9f, $a2, $50, $91, $50, $a4, $a2, $95, $91, $a3, $a5, $a2
+db $95, $5c, $50, $a4, $98, $95, $a2, $95, $57, $a3, $50, $9e, $9f, $9e, $95, $50
+db $a4, $9f, $50, $92, $95, $50, $96, $9f, $a5, $9e, $94, $5e, $5e, $5e, $03, $00
+db $70, $7d, $a9, $50, $a0, $9f, $a7, $95, $a2, $50, $99, $a3, $50, $91, $50, $93
+db $a5, $a2, $a3, $95, $5e, $5e, $5e, $03, $00, $70, $71, $9e, $94, $50, $79, $50
+db $92, $95, $a3, $a4, $9f, $a7, $50, $a4, $98, $91, $a4, $50, $93, $a5, $a2, $a3
+db $95, $50, $9f, $9e, $a4, $9f, $50, $a9, $9f, $a5, $51, $03, $00, $70, $77, $a7
+db $91, $91, $91, $91, $91, $a2, $a2, $a2, $a2, $97, $98, $51, $51, $03, $18, $04
+db $10, $30, $08, $40, $92, $fe, $00, $0a, $50, $2d, $c8; Dept store spook
+
+ORG $CFC51C
+db $91, $00, $02
+
+ORG $C86DFE
+db $14, $6E, $C8
+
+ORG $CF1B8F
+db $AE, $9B, $EE
+
+ORG $EE9BAE
+db $08, $82, $90, $c6, $00, $19, $10, $01, $1b, $04, $70, $6f, $10, $1e, $6f, $10
+db $1e, $6f, $10, $13, $1f, $21, $9c, $19, $26, $00, $02; Moonside warp
+
+ORG $CFE6E9
+db $4B, $00, $01
+
+ORG $C9A08E
+db $0A, $93, $A0, $C9
+
+ORG $CFC9CE
+db $40, $00
+
+ORG $C7B550
+db $0A, $DC, $B6, $C7
+
+ORG $C7B709
+db $0A
+
+ORG $C7A9B6
+db $70, $89, $9f, $a5, $57, $a2, $95, $50, $75, $94, $a5, $91, $a2, $94, $9f, $5c
+db $10, $0f, $50, $a2, $99, $97, $98, $a4, $6f, $03, $00, $70, $79, $5c, $10, $0f
+db $50, $a5, $98, $5c, $10, $0f, $50, $98, $91, $a6, $95, $50, $91, $50, $a0, $91
+db $93, $9b, $91, $97, $95, $50, $96, $9f, $a2, $50, $a9, $9f, $a5, $5e, $5e, $5e
+db $10, $10, $50, $79, $a4, $57, $a3, $50, $a4, $98, $95, $50, $1c, $05, $ac, $50
+db $a9, $9f, $a5, $50, $9f, $a2, $94, $95, $a2, $95, $94, $5c, $10, $0f, $50, $a2
+db $99, $97, $98, $a4, $6f, $03, $00, $70, $83, $a0, $95, $93, $99, $91, $9c, $50
+db $94, $95, $9c, $99, $a6, $95, $a2, $a9, $5c, $10, $0f, $50, $9f, $a2, $50, $a3
+db $9f, $9d, $95, $a4, $98, $99, $9e, $97, $5e, $10, $10, $50, $7e, $9f, $6f, $10
+db $10, $50, $87, $95, $9c, $9c, $5c, $10, $10, $50, $a5, $98, $5e, $5e, $5e, $03
+db $00, $70, $83, $99, $9e, $93, $95, $50, $79, $57, $9d, $50, $91, $9c, $a2, $95
+db $91, $94, $a9, $50, $98, $95, $a2, $95, $5c, $10, $0f, $50, $9c, $95, $a4, $57
+db $a3, $50, $9a, $a5, $a3, $a4, $50, $a3, $91, $a9, $50, $99, $a4, $50, $99, $a3
+db $50, $a9, $9f, $a5, $5e, $03, $00, $70, $71, $98, $95, $9d, $5e, $5e, $5e, $10
+db $10, $50, $78, $95, $a2, $95, $57, $a3, $50, $a9, $9f, $a5, $a2, $50, $a0, $91
+db $93, $9b, $91, $97, $95, $5c, $50, $6C, $75, $94, $a5, $91, $a2, $94, $9f, $5e
+db $6E, $03, $1d, $00, $ff, $ac, $1b, $02, $e2, $aa, $c7, $00, $1f, $02, $74, $00
+db $10, $30, $70, $58, $1c, $02, $fe, $50, $97, $9f, $a4, $50, $a4, $98, $95, $50
+db $1c, $05, $ac, $5e, $59, $13, $18, $04, $0a, $0F, $b7, $c7, $00, $70, $7f, $98
+db $5c, $10, $0f, $50, $a9, $9f, $a5, $50, $94, $9f, $9e, $57, $a4, $50, $98, $91
+db $a6, $95, $50, $91, $9e, $a9, $50, $a2, $9f, $9f, $9d, $50, $a4, $9f, $50, $93
+db $91, $a2, $a2, $a9, $50, $99, $a4, $5e, $10, $10, $50, $87, $95, $9c, $9c, $5c
+db $10, $0f, $50, $79, $57, $9d, $50, $9e, $9f, $a4, $50, $a4, $91, $9b, $99, $9e
+db $97, $50, $a4, $98, $99, $a3, $50, $a4, $98, $99, $9e, $97, $50, $92, $91, $93
+db $9b, $5e, $03, $0a, $E7, $B1, $EE; Moonside item NPC
+
+ORG $C7B719
+db $05, $4F, $02, $02
+
+ORG $C7B716
+db $04, $DF, $03
+
+ORG $EEB1E7
+db $01
+db $70, $79, $57, $9c, $9c, $50, $a7, $91, $99, $a4, $50, $a5, $9e, $a4, $99, $9c
+db $50, $a9, $9f, $a5, $50, $93, $91, $9e, $50, $a4, $91, $9b, $95, $50, $99, $a4
+db $5e, $10, $10, $50, $89, $95, $91, $98, $5e, $03, $18, $04, $1f, $f2, $40, $00
+db $7c, $02, $1f, $61, $04, $50, $02, $05, $4f, $02, $1f, $1f, $40, $00, $06, $1f
+db $17, $c8, $03, $5e, $02, $06, $02
+
+ORG $C374DD
+db $2E
+
+ORG $C374C4
+db $07
+
+ORG $C7B576
+db $1d, $00, $ff, $ac, $1b, $02, $df, $b5, $c7, $00, $70, $78, $95, $a2, $95, $57
+db $a3, $50, $a9, $9f, $a5, $a2, $50, $a0, $91, $93, $9b, $91, $97, $95, $5e, $00
+db $1f, $02, $74, $10, $20, $70, $58, $1c, $02, $ff, $50, $97, $9f, $a4, $50, $a4
+db $98, $95, $50, $1c, $05, $ac, $51, $59, $03, $00, $70, $7d, $a9, $50, $9a, $9f
+db $92, $50, $99, $a3, $50, $94, $9f, $9e, $95, $5e, $10, $10, $50, $79, $57, $9d
+db $50, $9f, $a5, $a4, $a4, $91, $50, $98, $95, $a2, $95, $51, $13, $05, $50, $02
+db $04, $df, $03, $18, $04, $0a, $0F, $b7, $c7, $70, $89, $9f, $a5, $50, $83, $84
+db $79, $7c, $7c, $50, $94, $9f, $9e, $57, $a4, $50, $98, $91, $a6, $95, $50, $95
+db $9e, $9f, $a5, $97, $98, $50, $a2, $9f, $9f, $9d, $5e, $10, $10, $50, $79, $50
+db $97, $a5, $95, $a3, $a3, $50, $a9, $9f, $a5, $50, $9d, $a5, $a3, $a4, $50, $9e
+db $9f, $a4, $50, $a7, $91, $9e, $a4, $50, $a4, $98, $99, $a3, $50, $a0, $91, $93
+db $9b, $91, $97, $95, $5e, $5e, $5e, $13, $02; Moonside item failure text
+
+ORG $CFC9D1
+db $5E, $02
+
+ORG $CFC9C2
+db $49, $00
+
+ORG $C292AB
+NOP
+NOP
+
+ORG $C7B70E
+db $02, $1f, $f2, $40, $00, $7f, $02, $1f, $61, $04, $df, $03, $05, $4f, $02, $02
+
+ORG $D5E11D
+db $00
+
+ORG $CFC43F
+db $49, $00
+
+ORG $CFC450
+db $49, $00
+
+ORG $CFC461
+db $49, $00
+
+ORG $CFC472
+db $49, $00
+
+ORG $CFC483
+db $49, $00
+
+ORG $CF60C1
+db $68
+
+ORG $CFC373
+db $49, $00, $02
+
+ORG $CFC397
+db $00
+
+ORG $C6EF20
+db $0A, $6F, $EE, $C6
+
+ORG $C6EE6F
+db $00, $08, $8a, $85, $c8, $00, $1b, $03, $83, $ee, $c6, $00, $04, $98, $02, $1f
+db $1e, $69, $03, $06, $02; Paula in Fourside
+
+ORG $CFC384
+db $98, $02
+
+ORG $CFC3D9
+db $49, $00
+
+ORG $CFC3C8
+db $49, $00
+
+ORG $CF1897
+db $49
+
+ORG $CF188D
+db $00
+
+ORG $C6EA3C
+db $E5, $02
+
+ORG $C6EA72
+db $70, $83, $9f, $50, $9d, $a5, $93, $98, $50, $9d, $99, $a3, $96, $9f, $a2, $a4
+db $a5, $9e, $95, $50, $98, $91, $a3, $50, $92, $95, $96, $91, $9c, $9c, $95, $9e
+db $50, $9d, $95, $50, $a3, $99, $9e, $93, $95, $50, $79, $50, $a4, $9f, $9f, $9b
+db $50, $a4, $98, $95, $50, $1c, $05, $32, $5e, $03, $00, $70, $79, $50, $94, $9f
+db $9e, $57, $a4, $50, $a7, $91, $9e, $a4, $50, $99, $a4, $50, $91, $9e, $a9, $9d
+db $9f, $a2, $95, $5e, $10, $10, $50, $79, $57, $9d, $50, $a2, $95, $91, $9c, $9c
+db $a9, $50, $9a, $a5, $a3, $a4, $50, $91, $50, $a2, $95, $97, $a5, $9c, $91, $a2
+db $50, $9f, $9c, $94, $50, $9d, $91, $9e, $5e, $5e, $5e, $03, $00, $70, $80, $9c
+db $95, $91, $a3, $95, $5c, $10, $0f, $50, $a4, $91, $9b, $95, $50, $a4, $98, $99
+db $a3, $50, $91, $a3, $50, $91, $9e, $50, $91, $a0, $9f, $9c, $9f, $97, $a9, $50
+db $96, $9f, $a2, $50, $91, $9e, $a9, $50, $a4, $a2, $9f, $a5, $92, $9c, $95, $50
+db $79, $57, $a6, $95, $50, $93, $91, $a5, $a3, $95, $94, $5e, $03, $00, $1d, $00
+db $ff, $32, $1b, $02, $59, $eb, $c6, $00, $1f, $02, $74, $10, $20, $70, $58, $1b
+db $04, $1c, $02, $00, $50, $97, $9f, $a4, $50, $a4, $98, $95, $50, $1c, $05, $32
+db $5e, $59, $04, $e5, $02, $13, $02, $70, $7d, $91, $a9, $92, $95, $50, $9e, $9f
+db $a4, $5e, $10, $10, $50, $79, $57, $9c, $9c, $50, $98, $9f, $9c, $94, $50, $9f
+db $9e, $a4, $9f, $50, $99, $a4, $50, $a5, $9e, $a4, $99, $9c, $50, $a9, $9f, $a5
+db $50, $98, $91, $a6, $95, $50, $a2, $9f, $9f, $9d, $50, $96, $9f, $a2, $50, $99
+db $a4, $5e, $5e, $5e, $13, $02; Monotoli item
+
+ORG $C6EE2F
+db $74, $9f, $50, $79, $50, $9c, $9f, $9f, $9b, $50, $97, $9f, $9f, $94, $50, $99
+db $9e, $50, $a4, $98, $99, $a3, $50, $98, $91, $a4, $6f, $13, $02
+
+ORG $C6D3A2
+db $C1, $D3, $C6
+
+ORG $C6D3C0
+db $12
+
+ORG $C88BEC
+db $70, $87, $95, $57, $a2, $95, $50, $98, $95, $91, $94, $95, $94, $50, $a4, $9f
+db $50, $84, $98, $a2, $95, $95, $94, $5e, $50, $76, $95, $95, $9c, $50, $96, $a2
+db $95, $95, $50, $a4, $9f, $50, $a4, $91, $97, $50, $91, $9c, $9f, $9e, $97, $51
+db $0a, $21, $8c, $c8; Runaway five text 2
+
+ORG $C88C40
+db $70, $84, $98, $91, $a4, $50, $9c, $91, $94, $a9, $50, $99, $9e, $50, $a4, $98
+db $95, $50, $9c, $9f, $92, $92, $a9, $5e, $5e, $5e, $10, $10, $50, $9e, $9f, $a7
+db $50, $a3, $98, $95, $50, $9c, $9f, $9f, $9b, $a3, $50, $9c, $99, $9b, $95, $50
+db $91, $50, $a3, $a4, $91, $a2, $51, $03, $18, $04, $1f, $ea, $b9, $00, $15, $73
+db $16, $4e, $50, $1f, $61, $1f, $f2, $ba, $00, $d9, $01, $16, $4e, $1f, $f2, $bb
+db $00, $d9, $01, $15, $73, $50, $1f, $61, $10, $e0, $04, $3d, $01, $05, $18, $02
+db $1f, $03, $04, $93, $00, $02; Rest of the runaway 5 cutscene
+
+ORG $CF674B
+db $06, $00
+
+ORG $C4FDC2
+db $03, $00, $54, $03, $88, $b0, $53, $03, $90, $98, $48, $03, $40, $D0; Venus outside door
+
+ORG $CFC14E
+db $A5, $00, $03, $5E, $02
+
+ORG $CF663D
+db $07
+
+ORG $C4FDD0
+db $02, $00, $e2, $03, $ff, $ff, $c0, $03, $e0, $e0
+
+ORG $CF17D8
+db $00, $00, $00
+
+ORG $CF776D
+db $02, $00, $b7, $03, $70, $c0, $d8, $03, $78, $48
+
+ORG $C88CA6
+db $04, $0b, $00, $1f, $41, $05, $04, $47, $00, $03, $00, $18, $04, $0a, $0a, $9c
+db $c8
+
+ORG $C89C06
+db $0A, $A6, $8C, $C8
+
+ORG $C89C96
+db $0A, $B7, $8C, $C8
+
+ORG $C88CB7
+db $07, $76, $01, $1b, $03, $c3, $8c, $c8, $00, $05, $47, $00, $1f, $15, $f3, $00
+db $c9, $00, $ff, $0a, $9d, $9c, $c8;Runaway five day bus
+
+ORG $C89CE6
+db $CF, $8C, $C8
+
+ORG $C88CCF
+db $06, $47, $00, $0a, $9d, $c8, $00, $0a, $c8, $9c, $c7
+
+ORG $C89D96
+db $18, $04, $02
+
+ORG $C99D48
+db $3B, $9D, $C9;Disable venus theater
+
+ORG $C99D4C
+db $0A, $38, $92, $C8;Runaway five theater text
+
+ORG $C89238
+db $18, $01, $01, $70, $58, $71, $50, $93, $a2, $9f, $a7, $94, $50, $9f, $96, $50
+db $9e, $9f, $99, $a3, $a9, $50, $96, $91, $9e, $a3, $50, $a0, $a2, $95, $a6, $95
+db $9e, $a4, $a3, $50, $a4, $98, $95, $50, $94, $9f, $9f, $a2, $50, $96, $a2, $9f
+db $9d, $50, $9f, $a0, $95, $9e, $99, $9e, $97, $5e, $59, $13, $18, $04, $02; Runaway five theater text
+
+ORG $CF19EC
+db $D6, $00
+
+ORG $C6D5AE
+db $70, $84, $98, $91, $9e, $9b, $a3, $50, $96, $9f, $a2, $50, $9b, $99, $93, $9b
+db $99, $9e, $97, $50, $a4, $98, $9f, $a3, $95, $50, $93, $98, $a5, $9d, $a0, $a3
+db $5e, $10, $0f, $5e, $10, $0f, $5e, $10, $0f, $50, $79, $50, $9d, $95, $91, $9e
+db $5c, $10, $10, $50, $a4, $98, $95, $50, $82, $a5, $9e, $91, $a7, $91, $a9, $50
+db $76, $99, $a6, $95, $50, $9f, $a5, $a4, $a4, $91, $50, $98, $95, $a2, $95, $5e
+db $03, $00, $70, $7e, $9f, $a7, $50, $79, $57, $9d, $50, $96, $a2, $95, $95, $50
+db $a4, $9f, $50, $a3, $a4, $91, $a2, $a4, $50, $9d, $a9, $50, $a3, $9f, $9c, $9f
+db $50, $93, $91, $a2, $95, $95, $a2, $5e, $10, $14, $50, $87, $98, $a9, $50, $94
+db $9f, $9e, $57, $a4, $50, $a9, $9f, $a5, $50, $a4, $91, $9b, $95, $50, $a4, $98
+db $99, $a3, $50, $1c, $05, $11, $50, $91, $a3, $50, $91, $50, $97, $99, $96, $a4
+db $6f, $03, $01, $70, $79, $a4, $57, $a3, $50, $a3, $99, $97, $9e, $95, $94, $5c
+db $10, $0f, $50, $95, $a6, $95, $9e, $5e, $03, $1d, $00, $ff, $03, $0a, $77, $92
+db $c8
+
+ORG $C89277
+db $1b, $03, $c2, $92, $c8, $00, $01, $70, $84, $9f, $9f, $50, $92, $91, $94, $5e
+db $10, $0f, $50, $a9, $9f, $a5, $57, $a2, $95, $50, $93, $91, $a2, $a2, $a9, $99
+db $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $50, $9a, $a5, $9e, $9b
+db $5e, $10, $0f, $50, $77, $95, $a4, $50, $a2, $99, $94, $50, $9f, $96, $50, $a3
+db $9f, $9d, $95, $a4, $98, $99, $9e, $97, $51, $13, $02, $00, $1f, $02, $74, $10
+db $20, $70, $58, $1b, $04, $1c, $02, $00, $50, $97, $9f, $a4, $50, $a4, $98, $95
+db $50, $1c, $05, $11, $5e, $59, $03, $04, $8c, $00, $05, $93, $00, $70, $87, $95
+db $9c, $9c, $5c, $10, $0a, $50, $79, $57, $9d, $50, $9f, $9e, $5e, $10, $10, $50
+db $83, $95, $95, $50, $a9, $9f, $a5, $50, $91, $a2, $9f, $a5, $9e, $94, $51, $03
+db $18, $04, $1f, $16, $48, $03, $01, $1f, $02, $08, $10, $0a, $1f, $1e, $48, $03
+db $01, $1f, $02, $09, $02; Venus text
+
+ORG $CFC153
+db $93, $00, $02
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $EF95E9
+db $83, $9f, $9d, $95, $92, $9f, $94, $a9, $50, $9c, $95, $96, $a4, $50, $a4, $98
+db $99, $a3, $50, $1c, $05, $11, $50, $99, $9e, $50, $98, $95, $a2, $95, $5e, $10
+db $0f, $50, $7d, $91, $a9, $92, $95, $50, $99, $a4, $50, $a7, $99, $9c, $9c, $50
+db $98, $95, $9c, $a0, $50, $a9, $9f, $a5, $5e, $03, $0a, $58, $96, $ef
+
+ORG $EF969D
+db $0A, $1C, $93, $C8
+
+ORG $C8931C
+db $95, $50, $a9, $9f, $a5, $50, $96, $99, $9e, $94, $50, $a4, $98, $95, $50, $9b
+db $95, $a9, $5e, $03, $0a, $ae, $96, $ef
+
+ORG $EF96CC
+db $02; Paula text script
+
+ORG $EF952F
+db $EC, $03, $29, $96, $C7
+
+ORG $C7961A
+db $0A, $08, $AF, $EE, $00, $18, $01, $01, $06, $56, $00
+dd CabinPostItemRescue
+db $70
+db $72, $a9, $50, $a4, $98, $95, $50, $a7, $91, $a9, $5c, $10, $0a, $50, $79, $50
+db $96, $9f, $a5, $9e, $94, $50, $a4, $98, $99, $a3, $1c, $05, $11, $50, $98, $95
+db $a2, $95, $5e, $10, $0f, $0A, $34, $AF, $EE, $34, $AF, $EE, $1b, $02, $34, $93
+db $c8, $00, $10, $0f, $50, $79, $57, $94, $50, $9c, $99, $9b, $0A
+dl CabinTextFixNew
+
+ORG $C796BB
+db $02; End Paula
+
+ORG $CF09E7
+db $49; Always enable happy-happy building
+
+ORG $C98A64
+db $0A, $34, $93, $C8
+
+ORG $C89334
+db $06, $e0, $03, $44, $93, $c8, $ff, $16, $1a, $a2, $15, $94, $0a, $69, $8a, $c9
+db $70, $84, $98, $91, $9e, $9b, $50, $a9, $9f, $a5, $50, $96, $9f, $a2, $50, $a9
+db $9f, $a5, $a2, $50, $97, $95, $9e, $95, $a2, $9f, $a3, $99, $a4, $a9, $5e, $13
+db $02, $04, $e0, $03, $0a, $7a, $90, $c7
+
+ORG $C98AAE
+db $65, $93, $C8
+
+ORG $C98B1F
+db $0A, $6C, $93, $C8
+
+ORG $C8936C
+db $06, $e0, $03, $dc, $93, $c8, $ff, $5e, $10, $0f, $50, $79, $57, $94, $50, $9c
+db $99, $9b, $95, $50, $a4, $9f, $50, $97, $99, $a6, $95, $50, $a9, $9f, $a5, $50
+db $a4, $98, $99, $a3, $50, $a3, $9f, $a5, $a6, $95, $9e, $99, $a2, $50, $9f, $96
+db $50, $9f, $a5, $a2, $50, $a6, $99, $9c, $9c, $91, $97, $95, $5e, $03, $1d, $03
+db $ff, $1b, $02, $e1, $93, $c8, $ff, $1d, $0e, $ff, $11, $08, $cf, $dc, $c7, $ff
+db $0a, $d5, $93, $c8, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $04, $e0, $03, $13, $18, $04, $02
+db $5e, $13, $18, $04, $02, $70, $89, $9f, $a5, $57, $a2, $95, $50, $93, $91, $a2
+db $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $50, $a3
+db $a4, $a5, $96, $96, $5e, $13, $18, $04, $02, $05; Donations text
+
+ORG $C98207
+db $70, $78, $95, $a2, $95, $57, $a3, $50, $91, $50, $a3, $a9, $9d, $92, $9f, $9c
+db $50, $9f, $96, $50, $9d, $a9, $50, $a2, $95, $9d, $9f, $a2, $a3, $95, $5e, $10
+db $0f, $01, $50, $50, $84, $91, $9b, $95, $50, $99, $a4, $50, $91, $9e, $94, $50
+db $97, $9f, $5e, $0a, $46, $82, $c9; Carpainter text
+
+ORG $C9A143
+db $49
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $CF068E
+db $49
+
+ORG $C99ACC
+db $02
+
+ORG $C799D7
+db $0A, $DD, $99, $C7
+
+ORG $C79A5E
+db $0A, $05, $94, $C8
+
+ORG $C89405
+db $05, $06, $00, $1f, $61, $05, $18, $02, $1f, $03, $02; Runaway five stuff
+
+ORG $C79BEC
+db $0A, $10, $94, $C8
+
+ORG $C89410
+db $04, $0b, $00, $70, $7f, $17, $34, $0a, $f0, $9b, $c7; Bus enemy flag
+
+ORG $C79C7A
+db $CF, $8C, $C8; Bus direct
+
+ORG $C95C0E
+db $0A, $1B, $94, $C8
+
+ORG $C95C66
+db $0A, $E4, $9C, $EE; Everdred flag
+
+ORG $C8941B
+db $06, $42, $00, $F0, $9C, $EE, $00, $0a, $2a, $5c, $c9, $19, $10, $01, $0b, $02
+db $1b, $03, $44, $94, $c8, $00, $19, $10, $02, $0b, $02, $1b, $03, $86, $94, $c8
+db $00, $17, $fe, $91, $98, $0a, $de, $5c, $c9, $70, $84, $98, $91, $a4, $50, $9d
+db $a5, $a3, $a4, $50, $92, $95, $50, $a7, $98, $a9, $50, $a9, $9f, $a5, $50, $a2
+db $95, $a3, $93, $a5, $95, $94, $50, $a9, $9f, $a5, $a2, $a3, $95, $9c, $96, $5e
+db $10, $0f, $50, $87, $98, $a9, $50, $94, $9f, $9e, $57, $a4, $50, $a9, $9f, $a5
+db $50, $93, $9f, $9d, $95, $50, $99, $9e, $6f, $13, $02, $0a, $40, $9C, $EE
+
+ORG $C95CDA
+db $0A, $26, $94, $C8
+
+ORG $C95CFC
+db $79, $50, $98, $95, $91, $a2, $50, $a3, $98, $95, $57, $a3, $50, $9e, $9f, $a4
+db $50, $91, $a2, $9f, $a5, $9e, $94, $50, $98, $95, $a2, $95, $5c, $10, $0a, $50
+db $a4, $98, $9f, $a5, $97, $98, $5e, $0a, $9c, $5d, $c9
+
+ORG $EE9C40
+db $08, $7f, $dc, $c7, $00, $1b, $03, $87, $9C, $EE, $00, $70, $84, $98, $91, $a4
+db $50, $9d, $a5, $a3, $a4, $50, $92, $95, $50, $a7, $98, $a9, $50, $a3, $98, $95
+db $57, $a3, $50, $a7, $99, $a4, $98, $50, $a9, $9f, $a5, $5e, $10, $0f, $50, $87
+db $98, $a9, $50, $94, $9f, $9e, $57, $a4, $50, $a9, $9f, $a5, $50, $93, $9f, $9d
+db $95, $50, $99, $9e, $6f, $13, $02, $70, $79, $50, $98, $95, $91, $a2, $94, $50
+db $a3, $9f, $9d, $95, $92, $9f, $94, $a9, $50, $a2, $95, $a3, $93, $a5, $95, $94
+db $50, $98, $95, $a2, $5e, $10, $0f, $50, $83, $98, $95, $57, $a3, $50, $9e, $9f
+db $a4, $50, $a7, $99, $a4, $98, $50, $a9, $9f, $a5, $5c, $10, $0a, $50, $99, $a3
+db $50, $a3, $98, $95, $6f, $03, $00, $70, $73, $9f, $9d, $95, $50, $92, $91, $93
+db $9b, $50, $99, $96, $50, $a9, $9f, $a5, $50, $96, $99, $9e, $94, $50, $98, $95
+db $a2, $5e, $13, $02, $18, $01, $01, $17, $fe, $04, $42, $00, $0a, $6b, $5c, $c9
+db $06, $55, $00, $d7, $60, $c9, $00, $06, $e1, $03, $02, $9D, $EE, $ff, $0a, $c6
+db $5d, $c9, $19, $10, $01, $0b, $02, $1b, $03, $1C, $9D, $EE, $ff, $19, $10, $02
+db $0b, $02, $1b, $03, $42, $9D, $EE, $ff, $0a, $a5, $5e, $c9, $70, $79, $a4, $57
+db $a3, $50, $97, $9f, $9f, $94, $50, $a4, $9f, $50, $a3, $95, $95, $50, $a9, $9f
+db $a5, $5c, $10, $0a, $50, $1c, $02, $02, $5e, $10, $0f, $00, $50, $50, $0a, $18
+db $5f, $c9, $0a, $0a, $5f, $c9; Everdred item text
+
+ORG $C95F9E
+db $91, $50, $1c, $05, $11, $5e, $0a, $ab, $5f, $c9
+
+ORG $C818D4
+db $04, $2A, $00, $02; Apple kid mouse
+
+ORG $C7916C
+db $02
+
+ORG $CF083B
+db $00, $00
+
+ORG $C818BF
+db $0A, $46, $9D, $EE
+
+ORG $EE9D46
+db $50, $79, $57, $9d, $50, $a7, $9f, $a2, $9b, $99, $9e, $97, $50, $9f, $9e, $50
+db $a4, $98, $99, $a3, $50, $93, $9f, $9f, $9c, $50, $99, $9e, $a6, $95, $9e, $a4
+db $99, $9f, $9e, $50, $a4, $98, $91, $a4, $50, $9c, $95, $a4, $a3, $50, $a9, $9f
+db $a5, $50, $a0, $9c, $91, $a9, $50, $a6, $99, $94, $95, $9f, $50, $97, $91, $9d
+db $95, $a3, $5c, $03, $50, $00, $70, $95, $a8, $93, $95, $a0, $a4, $50, $a4, $98
+db $95, $50, $99, $a4, $95, $9d, $a3, $50, $91, $a2, $95, $50, $99, $9e, $50, $a2
+db $91, $9e, $94, $9f, $9d, $50, $a0, $9c, $91, $93, $95, $a3, $5e, $10, $15, $01
+db $50, $50, $89, $9f, $a5, $50, $93, $91, $9e, $50, $95, $a6, $95, $9e, $50, $a0
+db $9c, $91, $a9, $50, $99, $a4, $50, $a7, $99, $a4, $98, $50, $9f, $a4, $98, $95
+db $a2, $50, $a0, $95, $9f, $a0, $9c, $95, $50, $9f, $a2, $50, $97, $91, $9d, $95
+db $a3, $5e, $03, $00, $70, $79, $57, $9d, $50, $93, $91, $9c, $9c, $99, $9e, $97
+db $50, $99, $a4, $50, $10, $0a, $6C, $79, $a3, $9c, $91, $9e, $94, $a3, $6E, $5e
+db $10, $0a, $50, $87, $91, $9e, $9e, $91, $50, $a4, $a2, $a9, $50, $99, $a4, $5e
+db $5e, $5e, $6f, $10, $10, $50, $7e, $9f, $5c, $50, $79, $50, $97, $a5, $95, $a3
+db $a3, $50, $9e, $9f, $a4, $5e, $03, $01, $70, $87, $95, $9c, $9c, $5c, $50, $79
+db $57, $9c, $9c, $50, $92, $95, $50, $9f, $a6, $95, $a2, $50, $91, $a4, $50, $72
+db $a5, $a2, $97, $9c, $99, $9e, $50, $80, $91, $a2, $9b, $50, $99, $96, $50, $a9
+db $9f, $a5, $50, $9e, $95, $95, $94, $50, $9d, $95, $5e, $10, $0f, $50, $72, $a9
+db $50, $a4, $98, $95, $50, $a7, $91, $a9, $5c, $10, $0a, $50, $a9, $9f, $a5, $50
+db $a3, $98, $9f, $a5, $9c, $94, $50, $93, $98, $95, $93, $9b, $50, $9f, $a5, $a4
+db $50, $a4, $98, $95, $50, $9d, $9f, $a5, $a3, $95, $5e, $10, $10, $50, $78, $95
+db $50, $9c, $9f, $9f, $9b, $a3, $50, $9c, $99, $9b, $95, $50, $98, $95, $50, $a7
+db $91, $9e, $a4, $a3, $50, $a4, $9f, $50, $97, $99, $a6, $95, $50, $a9, $9f, $a5
+db $50, $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97, $5e, $13, $04, $2a, $00, $04
+db $70, $00, $02; Apple Kid text
+
+ORG $CF809F
+db $69, $01, $d0, $60
+
+ORG $CF8091
+db $04
+
+ORG $CF9828
+db $00
+
+ORG $C804EE
+db $E1, $03
+;;;;;;;;;;;;;;;;;;;;;;
+ORG $C87E3F
+db $D1
+
+ORG $CF6C1B
+db $04
+
+ORG $CF6D1F
+db $00
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $CFB9D2
+db $74, $01, $01
+
+ORG $CFB9E3
+db $74, $01, $01
+
+ORG $CFBAC0
+db $E3, $03, $01; Phase Distorter 2
+
+;ORG $CFBABE
+;db $1F; Always make the Phase Distorter enterable
+
+ORG $C7F182
+db $84, $98, $95, $50, $80, $98, $91, $a3, $95, $50, $74, $99, $a3, $a4, $9f, $a2
+db $a4, $95, $a2, $50, $62, $50, $99, $a3, $50, $9e, $95, $91, $a2, $9c, $a9, $50
+db $93, $9f, $9d, $a0, $9c, $95, $a4, $95, $5e, $10, $0f, $50, $89, $9f, $a5, $50
+db $93, $91, $9e, $50, $a4, $a2, $a9, $50, $a4, $9f, $50, $a5, $a3, $95, $50, $99
+db $a4, $5c, $10, $0a, $50, $92, $a5, $a4, $50, $79, $50, $94, $9f, $a5, $92, $a4
+db $50, $99, $a4, $50, $a7, $99, $9c, $9c, $50, $a7, $9f, $a2, $9b, $50, $99, $9e
+db $50, $a4, $98, $99, $a3, $50, $a3, $a4, $91, $a4, $95, $5e, $5e, $5e, $03, $00
+db $70, $79, $96, $50, $9f, $9e, $9c, $a9, $50, $a7, $95, $50, $98, $91, $94, $50
+db $a3, $9f, $9d, $95, $50, $8a, $95, $a8, $9f, $9e, $a9, $a4, $95, $5e, $5e, $5e
+db $13, $1f, $f1, $e5, $02, $1f, $01, $02; Andonutes Text 1
+
+ORG $C9B94E
+db $78, $9d, $9d, $9d, $5e, $5e, $5e, $50, $79, $50, $94, $99, $94, $9e, $57, $a4
+db $50, $a4, $98, $99, $9e, $9b, $50, $99, $a4, $50, $a7, $9f, $a5, $9c, $94, $50
+db $a7, $9f, $a2, $9b, $5e, $5e, $5e, $03, $00, $70, $79, $96, $50, $9f, $9e, $9c
+db $a9, $50, $a7, $95, $50, $98, $91, $94, $50, $91, $50, $a0, $99, $95, $93, $95
+db $50, $9f, $96, $50, $91, $50, $9d, $95, $a4, $95, $9f, $a2, $99, $a4, $95, $5c
+db $10, $0a, $50, $79, $50, $93, $9f, $a5, $9c, $94, $50, $a3, $a9, $9e, $a4, $98
+db $95, $a3, $99, $aa, $95, $50, $a4, $98, $95, $50, $8a, $95, $a8, $9f, $9e, $a9
+db $a4, $95, $50, $9e, $95, $95, $94, $95, $94, $50, $a4, $9f, $50, $93, $9f, $9d
+db $a0, $9c, $95, $a4, $95, $50, $99, $a4, $5e, $13, $18, $04, $04, $E3, $03, $02; Andonuts Text 2
+
+ORG $C7F17B
+db $E3, $03; Andonuts Text 3 flag
+
+ORG $C7F3F4
+db $04, $85, $00, $1f, $f1, $e5, $02, $1f, $01, $04, $7E, $01, $04, $47, $01, $04
+db $4A, $01, $04, $4B, $01, $04, $49, $01, $02
+
+ORG $C6306B
+db $0A, $C9, $9E, $EE
+
+ORG $EE9EC9
+db $19, $02, $73, $91, $9c, $9c, $02, $19, $02, $87, $91, $a2, $a0, $02, $1c, $07
+db $03, $11, $09, $03, $e1, $15, $c9, $00, $a8, $30, $c6, $00, $ea, $9e, $ee, $00
+db $02, $18, $04, $04, $07, $03, $1f, $17, $00, $00, $b8, $01, $01, $04, $e3, $03
+db $05, $4c, $01, $1f, $eb, $ff, $06, $04, $f8, $02, $1f, $03, $1f, $1e, $fa, $04
+db $09, $10, $b4, $1f, $21, $e5, $1f, $e5, $ff, $15, $73, $16, $3e, $10, $64, $1f
+db $17, $e5, $02, $B9, $01, $04, $10, $50, $08, $6b, $dd, $c7, $00, $05, $f8, $02
+db $1f, $03, $1f, $ec, $ff, $01, $1f, $15, $6a, $00, $52, $01, $01, $08, $b8, $b4
+db $c9, $00, $19, $26, $01, $1f, $66, $01, $07, $3c, $bc, $c9, $00, $1f, $66, $02
+db $29, $17, $59, $ef, $00, $05, $e3, $03, $1E, $01, $e5, $02, $f1, $01, $02; Phase Distorter 2 Menu
+
+ORG $C33595
+db $06, $02, $19, $AA, $A2
+
+ORG $C9BBD6
+db $00, $00, $00, $00
+
+ORG $C9BBDB
+db $1f, $15, $6a, $00, $50, $01, $01, $50, $50, $08, $b8, $b4, $c9, $00, $1f, $eb
+db $ff, $06, $04, $f8, $02, $1f, $03, $1f, $1e, $e5, $02, $09, $10, $b4, $1f, $21
+db $5e, $1f, $e5, $ff, $15, $73, $16, $3e, $10, $64, $1f, $17, $fa, $04, $51, $01
+db $04, $10, $50, $08, $6b, $dd, $c7, $00, $05, $f8, $02, $1f, $03, $1f, $ec, $ff
+db $01, $1f, $15, $6a, $00, $52, $01, $01, $08, $b8, $b4, $c9
+
+ORG $C9BC28
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+ORG $D5F2D3
+db $30, $00, $B6
+
+ORG $CFBAC3
+;db $6D, $FE, $C4; Valley phase distorter
+
+ORG $CF6253
+db $08, $00
+
+ORG $CFE102
+db $00
+
+ORG $CFE103
+dd StarMasterText
+
+ORG $CFE0FB
+db $62, $00, $03
+
+ORG $C76F96
+
+ORG $C9BD69
+db $70, $77, $9f, $9f, $94, $50, $9e, $95, $a7, $a3, $5c, $10, $0a, $50, $95, $a6
+db $95, $a2, $a9, $9f, $9e, $95, $51, $03, $00, $70, $79, $57, $a6, $95, $50, $9a
+db $a5, $a3, $a4, $50, $96, $99, $9e, $99, $a3, $98, $95, $94, $50, $9d, $a9, $50
+db $9c, $91, $a4, $95, $a3, $a4, $50, $99, $9e, $a6, $95, $9e, $a4, $99, $9f, $9e
+db $5c, $10, $0a, $50, $a4, $98, $95, $50, $80, $98, $91, $a3, $95, $50, $74, $99
+db $a3, $a4, $9f, $a2, $a4, $95, $a2, $50, $63, $5e, $03, $00, $70, $85, $a3, $99
+db $9e, $97, $50, $a4, $98, $95, $50, $80, $98, $91, $a3, $95, $50, $74, $99, $a3
+db $a4, $9f, $a2, $a4, $95, $a2, $50, $63, $5c, $10, $0a, $50, $a9, $9f, $a5, $57
+db $9c, $9c, $50, $92, $95, $50, $91, $92, $9c, $95, $50, $a4, $9f, $50, $a4, $a2
+db $91, $a6, $95, $9c, $50, $99, $9e, $a4, $9f, $50, $a4, $98, $95, $50, $a0, $91
+db $a3, $a4, $50, $a4, $9f, $50, $93, $9f, $9e, $96, $a2, $9f, $9e, $a4, $50, $a4
+db $98, $95, $50, $a5, $9c, $a4, $99, $9d, $91, $a4, $95, $50, $95, $a6, $99, $9c
+db $5e, $03, $00, $70, $85, $9e, $96, $9f, $a2, $a4, $a5, $9e, $91, $a4, $95, $9c
+db $a9, $5c, $10, $0a, $50, $99, $a4, $50, $93, $91, $9e, $50, $9f, $9e, $9c, $a9
+db $50, $92, $95, $50, $a5, $a3, $95, $94, $50, $92, $a9, $50, $a3, $9f, $9d, $95
+db $9f, $9e, $95, $50, $a7, $98, $9f, $50, $98, $91, $a3, $50, $91, $92, $a3, $9f
+db $a2, $92, $95, $94, $50, $a4, $98, $95, $50, $a0, $9f, $a7, $95, $a2, $50, $9f
+db $96, $50, $a4, $98, $95, $50, $75, $91, $a2, $a4, $98, $50, $99, $9e, $a4, $9f
+db $50, $a4, $98, $95, $99, $a2, $50, $98, $95, $91, $a2, $a4, $5e, $03, $06, $d6
+db $03, $c5, $be, $c9, $ff, $00, $70, $7f, $98, $5c, $10, $0a, $50, $99, $96, $50
+db $9f, $9e, $9c, $a9, $50, $79, $50, $a7, $95, $a2, $95, $50, $a3, $a4, $99, $9c
+db $9c, $50, $a9, $9f, $a5, $9e, $97, $5e, $5e, $5e, $13, $02, $00, $70, $87, $98
+db $91, $6f, $10, $0f, $50, $89, $9f, $a5, $50, $a3, $91, $a9, $50, $a9, $9f, $a5
+db $50, $91, $9c, $a2, $95, $91, $94, $a9, $50, $98, $91, $a6, $95, $6f, $03, $00
+db $70, $87, $95, $9c, $9c, $5c, $10, $0a, $50, $a4, $98, $95, $9e, $5c, $10, $05
+db $50, $9c, $95, $a4, $57, $a3, $50, $97, $95, $a4, $50, $a3, $a4, $91, $a2, $a4
+db $95, $94, $51, $03, $0a, $32, $c0, $c9
+
+ORG $C8886A
+db $0A, $5A, $9F, $EE;Belch Base
+
+ORG $EE9F5A
+db $70, $89, $9f, $a5, $50, $97, $9f, $a4, $50, $a4, $98, $95, $50, $97, $9f, $9f
+db $94, $a3, $6f, $03, $00, $1d, $05, $ff, $69, $1b, $03, $f5, $9f, $ee, $ff, $70
+db $7e, $9f, $6f, $10, $0f, $50, $74, $9f, $50, $a9, $9f, $a5, $50, $a4, $98, $99
+db $9e, $9b, $50, $a7, $95, $57, $a2, $95, $50, $a3, $a4, $a5, $a0, $99, $94, $50
+db $95, $9e, $9f, $a5, $97, $98, $50, $a4, $9f, $50, $9c, $95, $a4, $50, $a3, $9f
+db $9d, $95, $50, $a0, $a5, $9e, $9b, $50, $99, $9e, $5c, $10, $07, $50, $9a, $a5
+db $a3, $a4, $50, $96, $9f, $a2, $50, $a7, $91, $99, $a4, $99, $9e, $97, $50, $63
+db $50, $9d, $99, $9e, $a5, $a4, $95, $a3, $6f, $03, $00, $70, $77, $99, $a6, $95
+db $50, $a5, $a3, $50, $76, $9c, $a9, $50, $78, $9f, $9e, $95, $a9, $50, $9f, $a2
+db $50, $92, $95, $91, $a4, $50, $99, $a4, $51, $13, $02, $10, $0f, $18, $04, $1f
+db $02, $76, $18, $01, $01, $19, $10, $01, $1b, $04, $70, $58, $1c, $02, $00, $50
+db $98, $91, $9e, $94, $95, $94, $50, $9f, $a6, $95, $a2, $50, $a4, $98, $95, $50
+db $1c, $05, $69, $5e, $59, $03, $00, $70, $7f, $9b, $91, $a9, $5e, $10, $0f, $50
+db $89, $9f, $a5, $50, $93, $91, $9e, $50, $93, $9f, $9d, $95, $50, $99, $9e, $5e
+db $03, $1d, $01, $ff, $69, $0a, $9f, $88, $c8
+
+ORG $C9AA92
+db $49
+
+ORG $EF617B
+db $0A, $43, $A0, $EE
+
+ORG $EEA043
+db $18, $01, $01, $70, $74, $99, $94, $50, $a9, $9f, $a5, $50, $92, $a2, $99, $9e
+db $97, $50, $a4, $98, $95, $50, $6C, $1C, $05, $01, $0a, $64, $a0, $ee, $00, $00
+db $00, $6E, $6f, $03, $00, $70, $79, $a4, $57, $a3, $50, $a4, $98, $95, $50, $a0
+db $95, $a2, $96, $95, $93, $a4, $50, $a3, $9e, $91, $93, $9b, $50, $96, $9f, $a2
+db $50, $a0, $9c, $9f, $a4, $a4, $99, $9e, $97, $50, $a7, $9f, $a2, $9c, $94, $50
+db $94, $9f, $9d, $99, $9e, $91, $a4, $99, $9f, $9e, $5e, $03, $00, $0a, $b0, $61
+db $ef
+
+ORG $EF61F6
+db $D3
+
+ORG $C27F5C
+db $00
+
+ORG $C27F60
+db $00
+
+ORG $EF65ED
+db $70, $84, $98, $95, $50, $97, $98, $9f, $a3, $a4, $a3, $50, $91, $a2, $9f, $a5
+db $9e, $94, $50, $84, $98, $a2, $95, $95, $94, $50, $a7, $99, $9c, $9c, $50, $a6
+db $91, $9e, $99, $a3, $98, $5e, $0a, $18, $66, $ef; Belch text
+
+ORG $C7EE67
+db $0A, $F8, $F9, $C7
+
+ORG $C7F9F8
+db $06, $e6, $03, $35, $fa, $c7, $00, $1f, $02, $44, $10, $20, $18, $01, $01, $70
+db $58, $87, $9f, $a7, $51, $10, $0f, $50, $84, $98, $91, $a4, $57, $a3, $50, $91
+db $50, $a3, $a4, $a2, $9f, $9e, $97, $50, $91, $96, $a4, $95, $a2, $a4, $91, $a3
+db $95, $5e, $5e, $5e, $59, $03, $0A, $E2, $AF, $EE, $71, $92, $ee, $1f, $02, $44
+db $10, $20, $18, $01, $01, $70, $58, $89, $9f, $a5, $57, $a6, $95, $50, $97, $9f
+db $a4, $a4, $95, $9e, $50, $a5, $a3, $95, $94, $50, $a4, $9f, $50, $99, $a4, $5e
+db $59, $13, $02; Saturn coffee
+
+ORG $CF66ED
+db $00, $00; Milky well npcs
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C9C6B8
+db $0A, $A4, $A0, $EE
+
+ORG $EEA0A4
+db $1d, $05, $ff, $d3, $1b, $03, $aF, $a0, $ee, $ff, $02, $1f, $1a, $03, $05, $03
+db $0a, $bd, $c6, $c9; Tenda Gate
+
+ORG $D57041
+db $2F; Tendakraut storage
+
+ORG $CFDFD1
+db $B8, $A0, $EE
+
+ORG $EEA0B8
+db $1d, $05, $ff, $d3, $1b, $03, $cd, $c6, $c9, $ff, $0a, $9e, $c5, $c7
+
+ORG $C9DE1C
+db $0A, $27, $DE, $C9
+
+ORG $C9DEA0
+db $70, $79, $50, $93, $91, $9e, $57, $a4, $50, $91, $93, $a4, $a5, $91, $9c, $9c
+db $a9, $50, $a4, $91, $9c, $9b, $5e, $03, $00, $70, $82, $9f, $93, $9b, $a3, $50
+db $93, $91, $9e, $57, $a4, $50, $a4, $91, $9c, $9b, $5c, $10, $09, $50, $a7, $95
+db $99, $a2, $94, $9f, $5e, $03, $18, $04, $10, $20, $04, $a7, $01, $0a, $4f, $94
+db $ee; Talking rock text
+
+ORG $C9B0C4
+db $00, $00, $00
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $D586E0
+db $39, $90, $C2;Skip the party add when beating master barf
+
+ORG $EF7415
+db $0A, $E0, $A0, $EE
+
+ORG $EEA0E0
+db $00, $08, $c8, $f8, $d6, $00, $14, $08, $26, $fa, $d6, $ff, $04, $e7, $03, $1F
+db $17, $22, $05, $5E, $02, $01, $02
+
+ORG $EEC2E0
+db $70, $83, $a5, $94, $94, $95, $9e, $9c, $a9, $5c, $50, $1c, $02, $02, $50, $91
+db $a0, $a0, $95, $91, $a2, $95, $94, $51, $03, $02, $70, $76, $a2, $9f, $9d, $50
+db $a4, $98, $95, $50, $a3, $9b, $a9, $5c, $50, $1c, $02, $03, $50, $91, $a0, $a0
+db $95, $91, $a2, $95, $94, $51, $02, $70, $83, $a5, $94, $94, $95, $9e, $9c, $a9
+db $5c, $50, $1c, $02, $0b, $50, $9d, $91, $a4, $95, $a2, $99, $91, $9c, $99, $aa
+db $95, $94, $51, $02, $70, $1c, $05, $02, $50, $a2, $9f, $a3, $95, $50, $96, $a2
+db $9f, $9d, $50, $a4, $98, $95, $50, $a3, $a7, $91, $9d, $a0, $51, $02, $70, $1c
+db $05, $03, $50, $94, $91, $a3, $98, $95, $94, $50, $96, $a2, $9f, $9d, $50, $a4
+db $98, $95, $50, $a4, $a2, $95, $95, $a3, $51, $02, $70, $83, $a5, $94, $94, $95
+db $9e, $9c, $a9, $5c, $50, $91, $50, $7d, $91, $93, $98, $50, $80, $99, $aa, $aa
+db $91, $50, $94, $95, $9c, $99, $a6, $95, $a2, $a9, $50, $9d, $91, $9e, $50, $91
+db $a2, $a2, $99, $a6, $95, $94, $51, $02, $70, $83, $a5, $94, $94, $95, $9e, $9c
+db $a9, $5c, $50, $91, $50, $83, $a4, $91, $a2, $50, $7d, $91, $a3, $a4, $95, $a2
+db $50, $a3, $a7, $9f, $9f, $a0, $95, $94, $50, $99, $9e, $51, $02, $70, $1c, $02
+db $02, $50, $a0, $a2, $91, $a9, $95, $94, $50, $96, $a2, $9f, $9d, $50, $a4, $98
+db $95, $50, $92, $9f, $a4, $a4, $9f, $9d, $50, $9f, $96, $50, $98, $95, $a2, $50
+db $98, $95, $91, $a2, $a4, $51, $1f, $02, $1c, $14, $00, $70, $1C, $0E, $0A, $F7
+db $C3, $EE, $50, $72, $91, $a2, $96, $50, $a3, $91, $9e, $9b, $50, $99, $9e, $a4
+db $9f, $50, $a4, $98, $95, $50, $a3, $a7, $91, $9d, $a0, $51, $10, $20, $02, $00
+db $70, $1c, $02, $03, $50, $96, $99, $a2, $95, $94, $50, $a4, $98, $95, $50, $85
+db $9c, $a4, $a2, $91, $50, $7d, $95, $97, $91, $50, $72, $91, $aa, $9f, $9f, $9b
+db $91, $51, $1f, $02, $4a, $10, $10, $1f, $02, $1f, $10, $30, $02, $00, $08, $3b
+db $74, $ef, $ff, $70, $1f, $02, $37, $1c, $02, $04, $50, $a4, $a2, $99, $95, $94
+db $50, $01, $50, $50, $1c, $12, $15, $51, $08, $82, $88, $ef, $ff, $02, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $02, $00, $70, $1c, $02, $0b, $50, $94, $99, $94, $50, $91, $50
+db $9d, $91, $93, $98, $9f, $50, $93, $9f, $9d, $92, $9f, $50, $9b, $99, $93, $9b
+db $51, $14, $02, $00, $70, $84, $98, $95, $50, $a0, $99, $aa, $aa, $91, $50, $97
+db $a5, $a9, $50, $a4, $98, $a2, $95, $a7, $50, $a3, $93, $91, $9c, $94, $99, $9e
+db $97, $50, $98, $9f, $a4, $50, $a0, $99, $aa, $aa, $91, $50, $91, $a4, $50, $1C
+db $0D, $0A, $EA, $C4, $EE, $50, $72, $91, $a2, $96, $51, $1f, $02, $11, $10, $30
+db $1f, $02, $2f, $10, $20, $02, $00, $70, $83, $a4, $91, $a2, $50, $7d, $91, $a3
+db $a4, $95, $a2, $50, $a4, $a2, $99, $95, $94, $50, $1c, $12, $16, $51, $1f, $02
+db $37, $10, $10, $08, $b8, $88, $ef, $ff, $02, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00; Master Barf defeat text
+
+ORG $CF694D
+db $09, $00
+
+ORG $C4FE80
+db $02, $00, $cd, $04, $b8, $e0, $22, $05, $a0, $c0;Barf NPC
+
+ORG $CFE0C8
+db $00, $00, $02
+
+ORG $CFE0CD
+db $E7, $03, $02, $00, $A1, $EE
+
+ORG $C9E746
+db $7A, $FA, $D6
+
+ORG $D6FA7A
+db $1F, $15, $00, $00, $BA, $01, $01, $0A, $70, $85, $C6
+
+ORG $EEA100
+db $70, $7f, $98, $5c, $10, $0a, $50, $98, $95, $a9, $5e, $10, $0f, $50, $79, $50
+db $93, $91, $9d, $95, $50, $92, $95, $93, $91, $a5, $a3, $95, $50, $99, $a4, $50
+db $9c, $9f, $9f, $9b, $95, $94, $50, $9c, $99, $9b, $95, $50, $a9, $9f, $a5, $50
+db $a7, $95, $a2, $95, $50, $99, $9e, $50, $91, $50, $a2, $9f, $a5, $97, $98, $50
+db $a3, $a0, $9f, $a4, $5e, $03, $00, $08, $8a, $85, $c8, $ff, $1b, $03, $5d, $a1
+db $ee, $ff, $05, $e7, $03, $04, $e8, $03, $1f, $1e, $22, $05, $01, $02
+
+ORG $C9E850
+db $0A, $58, $E7, $C9
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $CFE0BF
+db $5F, $A1, $EE
+
+ORG $CFE0F2
+db $A3, $A1, $EE
+
+ORG $CFE0E1
+db $E8, $A1, $EE
+
+ORG $EEA15F
+db $70, $79, $57, $9d, $50, $a9, $9f, $a5, $a2, $50, $93, $9f, $a5, $a2, $91, $97
+db $95, $5e, $03, $00, $70, $79, $57, $9d, $50, $a4, $9f, $9f, $50, $a3, $93, $91
+db $a2, $95, $94, $50, $a4, $9f, $50, $96, $99, $97, $98, $a4, $5c, $10, $0a, $50
+db $a3, $9f, $50, $79, $57, $9c, $9c, $50, $a3, $a4, $91, $a9, $50, $98, $95, $a2
+db $95, $5e, $13, $02, $70, $79, $57, $9d, $50, $a9, $9f, $a5, $a2, $50, $a0, $9f
+db $a7, $95, $a2, $5e, $03, $00, $70, $79, $57, $9d, $50, $a4, $9f, $9f, $50, $9d
+db $a5, $93, $98, $50, $9f, $96, $50, $91, $50, $a7, $95, $91, $9b, $9c, $99, $9e
+db $97, $5c, $10, $0a, $50, $a3, $9f, $50, $79, $57, $9c, $9c, $50, $a3, $a4, $91
+db $a9, $50, $98, $95, $a2, $95, $5e, $13, $02, $70, $79, $57, $9d, $50, $a9, $9f
+db $a5, $a2, $50, $a7, $99, $a3, $94, $9f, $9d, $5e, $03, $00, $70, $79, $57, $94
+db $50, $9a, $a5, $a3, $a4, $50, $9d, $91, $9b, $95, $50, $91, $50, $96, $9f, $9f
+db $9c, $50, $9f, $96, $50, $9d, $a9, $a3, $95, $9c, $96, $5c, $10, $0a, $50, $a3
+db $9f, $50, $79, $57, $9c, $9c, $50, $a3, $a4, $91, $a9, $50, $98, $95, $a2, $95
+db $5e, $13, $02; Flying men text
+
+ORG $CF8369
+db $03, $00, $23, $05, $d8, $48, $21, $05, $e0, $90, $24, $05, $f0, $58
+
+ORG $C779A1
+db $0A, $33, $A2, $EE
+
+ORG $EEA233
+db $08, $f4, $15, $c9, $00, $1f, $02, $25, $10, $5a, $1f, $1e, $4e, $05, $02, $10
+db $e0, $18, $01, $01, $70, $58, $1c, $02, $01, $50, $93, $9f, $9e, $a1, $a5, $95
+db $a2, $95, $94, $50, $98, $99, $a3, $50, $99, $9e, $9e, $95, $a2, $50, $95, $a6
+db $99, $9c, $51, $59, $03, $18, $04, $08, $d9, $9b, $ee, $ff, $08, $a7, $79, $c7
+db $ff, $1f, $21, $34, $02;Magicant nightmare?
+
+ORG $C77B44
+db $87, $95, $50, $a3, $98, $9f, $a5, $9c, $94, $50, $97, $9f, $50, $a4, $9f, $50
+db $a4, $98, $95, $50, $73, $91, $a6, $95, $50, $9f, $96, $50, $a4, $98, $95, $50
+db $80, $91, $a3, $a4, $50, $91, $9e, $94, $50, $95, $9e, $94, $50, $a4, $98, $99
+db $a3, $5e, $03, $0a, $3e, $7c, $c7
+
+ORG $C7E352
+db $18, $04, $02
+
+;;;;;;;;;;;;;;;;;;;;;
+ORG $EEA515
+db $18, $04, $18, $01, $31, $1f, $00, $ff, $05, $50, $50, $50, $50, $50, $50, $50
+db $50, $50, $50, $50, $50, $04, $e9, $03, $1c, $08, $02, $10, $a0, $03, $18, $04
+db $08, $6b, $dd, $c7, $00, $1f, $00, $00, $5e, $1f, $14, $05, $06, $ff, $ff, $ff
+db $ff, $ff, $ff, $1f, $eb, $02, $06, $1f, $15, $02, $00, $0d, $03, $01, $10, $01
+db $1f, $f2, $02, $00, $0a, $00, $1f, $eb, $01, $06, $1f, $15, $01, $00, $0c, $03
+db $01, $10, $01, $1f, $f2, $01, $00, $0a, $00, $10, $78, $10, $78, $10, $78, $10
+db $78, $10, $78, $10, $3c, $1f, $15, $92, $00, $06, $03, $01, $10, $3c, $10, $64
+db $1f, $15, $6a, $00, $7a, $02, $01, $1f, $61, $1f, $41, $0b, $08, $6b, $dd, $c7
+db $00, $1f, $eb, $ff, $06, $1f, $21, $e1, $1f, $00, $00, $5f, $1f, $15, $8f, $00
+db $f8, $02, $01, $1f, $61, $1f, $15, $58, $01, $f9, $02, $01, $1f, $61, $1f, $15
+db $c1, $01, $fa, $02, $01, $1f, $61, $1f, $41, $0c, $1f, $50, $1F, $EB, $FF, $00
+db $0a, $c9, $a5, $ee, $1F, $21, $51, $0A, $81, $C9, $C9; Ending
+
+
+ORG $C39F17
+db $42, $A0, $FE, $C4; Paula robot
+
+ORG $C39F22
+db $42, $B5, $FE, $c4; Jeff robot
+
+ORG $C39F2D
+db $42, $CA, $FE, $c4; Poo robot
+
+ORG $C9C07F
+db $70, $7f, $98, $5c, $10, $0a, $50, $79, $50, $91, $9c, $9d, $9f, $a3, $a4, $50
+db $96, $9f, $a2, $97, $9f, $a4, $50, $a4, $9f, $50, $9d, $95, $9e, $a4, $99, $9f
+db $9e, $50, $a4, $98, $91, $a4, $50, $79, $50, $98, $91, $94, $50, $a4, $9f, $50
+db $a0, $a5, $a4, $50, $a9, $9f, $a5, $a2, $50, $92, $a2, $91, $99, $9e, $a3, $50
+db $99, $9e, $a4, $9f, $50, $a2, $9f, $92, $9f, $a4, $a3, $5e, $03, $00, $70, $80
+db $a2, $9f, $92, $91, $92, $9c, $a9, $5c, $50, $91, $a4, $50, $9c, $95, $91, $a3
+db $a4, $5e, $03, $00, $70, $78, $95, $a9, $5c, $10, $06, $50, $a7, $98, $91, $a4
+db $57, $a3, $50, $a4, $98, $99, $a3, $50, $92, $a5, $a4, $a4, $9f, $9e, $50, $94
+db $9f, $6f, $03, $04, $1d, $01, $1f, $02, $10, $1f, $01, $ff, $10, $20, $0a, $1d
+db $c2, $c9; Phase distorter 3 present
+
+ORG $C63092
+db $0A, $80, $A2, $EE
+
+ORG $EEA280
+db $19, $02, $73, $91, $9c, $9c, $02, $19, $02, $87, $91, $a2, $a0, $02, $1c, $07
+db $03, $11, $09, $03, $e1, $15, $c9, $00, $a8, $30, $c6, $00, $a1, $a2, $ee, $00
+db $02, $18, $04, $04, $07, $03, $1f, $17, $00, $00, $b8, $01, $01, $04, $e3, $03
+db $05, $4c, $01, $1f, $eb, $ff, $06, $04, $f8, $02, $1f, $00, $10, $5b, $1f, $1e
+db $1e, $05, $09, $10, $b4, $05, $74, $01, $1f, $21, $e5, $1f, $e5, $ff, $15, $73
+db $16, $3e, $10, $64, $1f, $17, $e5, $02, $b9, $01, $04, $10, $50, $08, $6b, $dd
+db $c7, $00, $05, $f8, $02, $1f, $03, $1f, $ec, $ff, $01, $1f, $15, $6a, $00, $52
+db $01, $01, $08, $b8, $b4, $c9, $00, $19, $26, $01, $1f, $66, $01, $07, $3c, $bc
+db $c9, $00, $1f, $66, $02, $29, $17, $59, $ef, $00, $05, $e3, $03, $1e, $01, $e5
+db $02, $02; Phase distorter 3 menu
+
+ORG $CFDE39
+db $12, $A3, $EE
+
+ORG $EEA312
+db $06, $1d, $01, $1d, $c2, $c9, $ff, $0a, $9e, $c5, $c7
+
+ORG $CFDE38
+db $00
+
+ORG $C7F166
+db $1D, $01
+
+ORG $C7F634
+db $70, $87, $95, $9c, $93, $9f, $9d, $95, $50, $92, $91, $93, $9b, $51, $10, $15
+db $50, $78, $9f, $a7, $50, $94, $99, $94, $50, $99, $a4, $50, $96, $95, $95, $9c
+db $50, $a4, $a2, $91, $a6, $95, $9c, $99, $9e, $97, $50, $a4, $98, $a2, $9f, $a5
+db $97, $98, $50, $a4, $99, $9d, $95, $6f, $03, $00, $70, $84, $9f, $50, $a3, $98
+db $a2, $95, $94, $a3, $5c, $10, $0a, $50, $a9, $9f, $a5, $50, $a3, $91, $a9, $6f
+db $13, $02; Andonuts 4
+
+ORG $CF250D
+db $1D, $A3, $EE
+
+ORG $EEA31D
+db $06, $e1, $03, $3a, $af, $c9, $ff, $18, $01, $01, $70, $58, $89, $9f, $a5, $57
+db $a2, $95, $50, $9f, $a6, $95, $a2, $93, $9f, $9d, $95, $50, $a7, $99, $a4, $98
+db $50, $a4, $98, $95, $50, $96, $95, $95, $9c, $99, $9e, $97, $50, $a4, $98, $91
+db $a4, $50, $a9, $9f, $a5, $50, $a3, $98, $9f, $a5, $9c, $94, $9e, $57, $a4, $50
+db $a0, $a2, $9f, $93, $95, $95, $94, $50, $a7, $99, $a4, $98, $9f, $a5, $a4, $50
+db $1c, $02, $02, $5e, $5e, $5e, $59, $13, $18, $04, $0a, $3a, $af, $c9, $02; Paula warning
+
+
+ORG $C9C4D3
+db $0A, $7C, $A3, $EE
+
+ORG $EEA37C
+db $18, $01, $01, $70, $73, $9f, $9e, $97, $a2, $91, $a4, $a5, $9c, $91, $a4, $99
+db $9f, $9e, $a3, $51, $03, $00, $70, $77, $99, $a9, $97, $91, $a3, $50, $98, $91
+db $a3, $50, $92, $95, $95, $9e, $50, $94, $95, $96, $95, $91, $a4, $95, $94, $5c
+db $10, $0a, $50, $91, $9e, $94, $50, $a4, $98, $95, $50, $75, $91, $a2, $a4, $98
+db $50, $99, $a3, $50, $a3, $91, $96, $95, $5e, $03, $00, $70, $87, $9f, $a5, $9c
+db $94, $50, $a9, $9f, $a5, $50, $9c, $99, $9b, $95, $50, $a4, $9f, $50, $a3, $9b
+db $99, $a0, $50, $a4, $9f, $50, $a4, $98, $95, $50, $93, $a2, $95, $94, $99, $a4
+db $a3, $5c, $10, $0a, $50, $9f, $a2, $50, $95, $a8, $a0, $9c, $9f, $a2, $95, $50
+db $a4, $98, $95, $50, $95, $a0, $99, $9c, $9f, $97, $a5, $95, $6f, $00, $19, $02
+db $73, $a2, $95, $94, $99, $a4, $a3, $02, $19, $02, $75, $a0, $99, $9c, $9f, $97
+db $a5, $95, $02, $1c, $07, $02, $11, $09, $02, $37, $a4, $ee, $00, $d0, $a5, $ee
+db $00, $12, $0a, $0a, $a4, $ee, $1f, $e4, $01, $00, $05, $18, $04, $1f, $e4, $02
+db $00, $05, $1f, $e4, $04, $00, $05, $1f, $ea, $01, $00, $1f, $f2, $01, $00, $5e
+db $02, $1f, $ea, $02, $00, $1f, $f2, $02, $00, $5e, $02, $1f, $ea, $03, $00, $1f
+db $f2, $03, $00, $5e, $02, $1f, $ea, $04, $00, $1f, $f2, $04, $00, $5e, $02, $1f
+db $00, $00, $5e, $5e, $0a, $6e, $a5, $ee; Credits choice
+
+ORG $EEA5D0
+db $0A
+dl FixEpilogueWinters
+db $d2, $00
+BackfromEpilogueFix:
+db $04, $d3, $00, $04, $d5, $00, $04, $d6, $00, $04
+db $d7, $00, $04, $d8, $00, $04, $d9, $00, $04, $da, $00, $04, $db, $00, $04, $dc
+db $00, $04, $dd, $00, $04, $de, $00, $04, $df, $00, $04, $69, $00, $12, $70, $80
+db $9c, $95, $91, $a3, $95, $50, $9b, $95, $95, $a0, $50, $99, $9e, $50, $9d, $99
+db $9e, $94, $50, $a4, $98, $91, $a4, $50, $a3, $9f, $9d, $95, $50, $a3, $95, $a1
+db $a5, $95, $9e, $93, $95, $a3, $50, $9d, $91, $a9, $50, $9e, $9f, $a4, $50, $a7
+db $9f, $a2, $9b, $50, $9f, $a2, $50, $92, $95, $50, $91, $a6, $91, $99, $9c, $91
+db $92, $9c, $95, $5e, $03, $00, $70, $7c, $9f, $93, $91, $a4, $99, $9f, $9e, $a3
+db $50, $93, $9f, $9c, $9c, $95, $93, $a4, $95, $94, $50, $a7, $99, $9c, $9c, $50
+db $9e, $9f, $a4, $50, $92, $95, $50, $93, $9f, $a5, $9e, $a4, $95, $94, $5c, $10
+db $0a, $50, $91, $9e, $94, $50, $a9, $9f, $a5, $50, $a7, $99, $9c, $9c, $50, $9e
+db $95, $95, $94, $50, $a4, $9f, $50, $a2, $95, $a3, $a4, $91, $a2, $a4, $50, $96
+db $a2, $9f, $9d, $50, $a9, $9f, $a5, $a2, $50, $a0, $a2, $95, $a6, $99, $9f, $a5
+db $a3, $50, $a3, $91, $a6, $95, $50, $a4, $9f, $50, $a2, $95, $a3, $a5, $9d, $95
+db $50, $9e, $9f, $a2, $9d, $91, $9c, $50, $a0, $9c, $91, $a9, $5e, $03, $00, $70
+db $84, $9f, $50, $a6, $99, $95, $a7, $50, $a4, $98, $95, $50, $93, $a2, $95, $94
+db $99, $a4, $a3, $5c, $10, $0a, $50, $a2, $95, $a4, $a5, $a2, $9e, $50, $a4, $9f
+db $50, $1c, $02, $01, $57, $a3, $50, $98, $9f, $a5, $a3, $95, $50, $99, $9e, $50
+db $7f, $9e, $95, $a4, $a4, $5e, $03, $18, $04, $0a, $ef, $c4, $c9; Epilogue choice
+
+ORG $C9C582
+db $0a, $87, $c5, $c9
+
+ORG $C48535
+LDA #$00FF;Lumine hall hardcoded length
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;Get rid of the NPCS outside monotoli building. I already fixed it once I can do it again. Also remove the hotspot?
+
+
+;think about stuff like NPCs saying items...
+
+;;;;;;;;;;;;;;;;;;;;;;;
+
+ORG $EE9BD0
+db $07, $D6, $03, $1B, $03, $03, $BB, $EE, $00, $18, $01, $01, $01, $70, $58, $1C
+db $02, $01, $50, $91, $92, $A3, $9F, $A2, $92, $95, $94, $50, $A4, $98, $95, $50
+db $A0, $9F, $A7, $95, $A2, $50, $9F, $96, $50, $A4, $98, $95, $50, $75, $91, $A2
+db $A4, $98, $51, $59, $04, $D6, $03, $1F, $02, $67, $10, $20, $13, $18, $00, $02 ;Earth's power text
+
+ORG $C55593
+db $70, $79, $96, $50, $a9, $9f, $a5, $50, $a3, $95, $95, $50, $a4, $98, $99, $a3
+db $5c, $50, $a4, $95, $9c, $9c, $50, $80, $99, $9e, $9b, $50, $83, $a7, $99, $a4
+db $93, $98, $5e
+
+;;;;;;;;;;;;;;;;;;;; only if start pos is not onett
+;ORG $CF96C2
+;dw $0069
+
+;ORG $CF9618
+;dw $0069
+
+;ORG $CF9629
+;dw $0069
+;;;;;;;;;;;;;;;
+ORG $D579F0 ; New Tenda Village coordinates
+dw $0035
+dw $0020
+
+ORG $F4A000
+db $78, $91, $a0, $a0, $a9, $5d, $78, $91, $a0, $a0, $a9, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $dd, $00, $f4, $01, $cb, $03, $FF; Happy-Happy Teleport
+
+db $74, $a5, $a3, $a4, $a9, $50, $74, $a5, $9e, $95, $a3, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $de, $00, $a0, $00, $e3, $04, $FF; Dusty Dunes
+
+db $7d, $91, $97, $99, $93, $91, $9e, $a4, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $df, $00, $f2, $02, $28, $02, $FF; Magicant Teleport
+
+ORG $F4C000
+ShopItemNamesDeluxe:
+.10:
+db $54, $61, $60, $00
+.100:
+db $54, $61, $60, $60, $00
+.1000:
+db $54, $61, $60, $60, $60, $00
+
+.MoneyNames:
+dw $0000
+dw .10
+dw .100
+dw .1000
+
+
+
+ORG $00F8D0
+GetItemRemote:
+SEP #$20
+STZ $0770
+STZ $B582
+STZ $B583
+STZ $B5E7
+LDA #$01
+STA $B585
+REP #$20
+PHX
+SEP #$10
+LDX $B570
+BEQ SkipItemGet
+JSR CalcCharSpace
+CMP #$69
+BEQ SkipItemGet
+SEP #$10
+REP #$20
+LDA $B571
+AND #$00FF
+LDX $B570
+JSL $C18BC6
+STZ $B570
+LDA #$0074
+JSL $C0ABE0
+SkipItemGet:
+REP #$30
+LDA $987F
+LDY #$0008
+PLX
+JML $C04D4C
+
+CalcCharSpace:
+LDY #$00
+SEP #$20
+STZ $B571
+CheckNextChar:
+LDA $986F,Y
+JSR CheckCharInv
+CPY $98A4
+BEQ CharacterInvalid
+BRA CheckNextChar
+CharacterInvalid:
+JSL KeyItemBlockerAndCheckProg
+BEQ SendItemToStorage
+LDA #$69
+RTS
+SendItemToStorage:
+LDA $B570
+;KEY ITEM CHECK GOES HERE
+JSL CheckIfKeyItem
+JSL StoreItem
+REP #$20
+STZ $3272
+LDA #$0074
+JSL $C0ABE0
+SEP #$20
+LDA #$69
+STZ $B570
+RTS
+CheckCharInv:
+STA $B571
+DEC
+ASL
+TAX
+REP #$20
+LDA CharLastInvSlot,X
+REP #$10
+TAX
+SEP #$20
+LDA $0000,X
+BNE CharInvFull
+PLX
+CharInvFull:
+SEP #$10
+INY
+RTS
+
+InitIntro:
+REP #$20
+PHX
+LDX #$0000
+SetScript:
+LDA StartingScripts,X
+PHX
+JSL $C2165E
+PLX
+INX
+INX
+CPX #$003E
+BNE SetScript
+PHY
+LDY #$0000
+UnsetNextScript:
+TYX
+LDA UnsetScripts,X
+TXY
+LDX #$0000
+PHY
+JSL $C2165E
+PLY
+INY
+INY
+CPY #$000A
+BEQ EndUnsetScripts
+BRA UnsetNextScript
+EndUnsetScripts:
+PLY
+PLX
+SEP #$20
+LDA #$09
+STA $9839
+REP #$20
+JML $C1FEB2
+
+SetTeleFlags:
+LDA $06
+PHX
+LDX #$0000
+CheckTeleFlag:
+CMP TeleSectorID,X
+BEQ UnlockTeleport
+CPX #$0028
+BEQ EndTeleCheck
+INX
+INX
+BRA CheckTeleFlag
+EndTeleCheck:
+PLX
+STA $5E38
+JML LoadCurrentPhotoColor
+UnlockTeleport:
+PHA
+LDA TeleFlags,X
+TSB $9C22
+PLA
+BRA EndTeleCheck
+
+IgnoreTeleportEnemies:
+LDA $9F3F
+AND #$00FF
+BNE SkipCollision
+JSL $C0D15C
+JML $C0D626
+SkipCollision:
+JML $C0D62B
+
+GetRemoteTeleport:
+LDA $B572
+AND #$00FF
+BEQ EndGetPSI
+CMP #$0010
+BCS GetChara
+PHX
+ASL
+TAX
+LDA TeleportFlags,X
+JSL $C2165E
+LDA #$0067
+JSL $C0ABE0
+PLX
+BRA EndGetPSI
+GetChara:
+JSR UnlockCharacter
+EndGetPSI:
+STZ $B572
+STZ $FF40
+LDA $006D
+AND #$A000
+JML GetRemoteMoney
+
+UnlockCharacter:
+PHX
+SEC
+SBC #$0010
+ASL
+STA $B572
+%FUNCTION_PROLOGUE(18)
+JSL $C0943C
+PHX
+LDX $B572
+LDA CharUnlockPointers,X
+PLX
+;STZ $B572
+STA $0E
+LDA #$00D5
+STA $10
+LDA #$0001
+JSL $C1DD47 ;Display textbox
+JSL $C186B1 ;Overworld text
+JSL $C09451 ;Resume sprites
+PLD
+PLX
+RTS
+
+
+GetCharNameFromServ:
+REP #$31
+CPX #$00FE
+BNE SkipSpecialNameLoad
+SEP #$10
+LDX $B572
+REP #$10
+SkipSpecialNameLoad:
+PHD
+PHA
+JML $C14FDB
+
+GetPlayerName:
+PHA
+LDA $FF40
+BEQ GetNormalItem
+PLA
+STZ $FF40
+LDA #$00AD
+BRA ForceAPItem
+GetNormalItem:
+PLA
+ForceAPItem:
+CMP #$00AD
+BNE SetPlayerNameFlag
+INC $B573
+INC $B579
+SetPlayerNameFlag:
+JSL CheckifNameProgressive
+LDA #$0000
+JML $C146DC
+
+LoadPlayerName:
+TDC
+ADC #$FFF2
+PHA
+LDA $B573
+AND #$00FF
+BEQ SkipPlayerName
+LDX #$001F
+SkipPlayerName:
+PLA
+JML $C14FDF
+
+DrawPlayerName:
+PHA
+LDA $B573
+AND #$00FF
+BEQ DrawNormalName
+PLA
+LDA $B579
+AND #$00FF
+BEQ SkipPriorityName
+STZ $B579
+BRA DontSkipPrior
+SkipPriorityName:
+STZ $B573
+DontSkipPrior:
+LDA #$FF50
+STA $0E
+LDA #$007E
+STA $10
+JML $C19399
+
+DrawNormalName:
+PLA
+ADC $06
+STA $06
+JML $C19393
+
+SetMelodyCount:
+PHX
+LDX #$0000
+CheckSoundStoneScript:
+CMP SoundStoneScripts,X
+BEQ IncrementMelody
+INX
+INX
+CPX #$0010
+BEQ NormalEvent
+BRA CheckSoundStoneScript
+IncrementMelody:
+INC $B575
+SEP #$20
+PHA
+LDA $B575
+CMP $C4FD70
+BCC SkipSanctuaryTrigger
+PHA
+LDA #$04
+TSB $9C82
+PLA
+SkipSanctuaryTrigger:
+CMP $C4FD71
+BCC SkipMagicantTrigger
+PHA
+LDA #$08
+TSB $9C82
+PLA
+SkipMagicantTrigger:
+CMP $C4FD72
+BCC ReturnEvent
+LDA #$10
+TSB $9C82
+ReturnEvent:
+PLA
+NormalEvent:
+REP #$20
+PLX
+REP #$31
+DEC
+PHA
+LSR
+JML SetFlagProceed
+
+PocketStorage:
+LDA $0065
+AND #$00FF
+BIT #$0010
+BEQ SkipDropMenu
+PHX
+PHY
+%FUNCTION_PROLOGUE(18)
+JSL $C0943C
+LDA #$9620
+STA $0E
+LDA #$00EE
+STA $10
+JSL $C186B1
+JSL $C09451
+JSL $C1DD59 ;Close textbox
+PLD
+PLY
+PLX
+SkipDropMenu:
+LDA $006D
+AND #$0040
+JML $C0B90A
+
+SkipArchiSpaceCheck:
+PHA
+LDA $B573
+AND #$00FF
+BNE SkipSpaceCheck
+PLA
+JSL $C4572B
+JML $C14D09
+SkipSpaceCheck:
+PLA
+LDA #$00FF
+JML $C14D09
+
+CheckArchiItemInv:
+LDX $14
+CPX #$00AD
+BEQ SkipInvAdd
+JSL $C18BC6
+SkipInvAdd:
+JML $C156AD
+
+GetServerItemName:
+LDA $B573
+AND #$00FF
+BEQ WriteNormalName
+LDA #$FF80
+STA $06
+LDA #$007E
+STA $08
+LDA #$0000
+JML $C1922E
+WriteNormalName:
+LDA #$5000
+STA $06
+JML $C19227
+
+AddNewNPCSectors:
+BNE EntryIsntNull
+JML $C0255A
+EntryIsntNull:
+STA $06
+AND #$FF00
+BEQ CheckNPCSector
+JML $C0226B
+CheckNPCSector:
+PHX
+LDA $06
+DEC
+ASL
+TAX
+LDA NewSectorPointers,X
+PLX
+STA $06
+LDA #$00C4
+STA $08
+JML $C0227C
+
+GetLumiText:
+LDA $C48037,X
+AND #$00FF
+BEQ EndLumiText
+JML $C48409
+EndLumiText:
+JML $C48717
+
+DelArchFromInv:
+LDY $0E
+TYA
+SEP #$20
+CMP #$AD
+BEQ SkipArchiWrite
+JML GetProgressiveItems
+SkipArchiWrite:
+JML $C18B65
+
+SetAnimSpeed:
+PHX
+LDA $0066
+LDX #$0000
+AND #$00FF
+BIT #$0040
+BEQ NotRunning
+LDA #$0003
+BRA SetRunSpeed
+NotRunning:
+LDA #$0008
+SetRunSpeed:
+STA $0E
+CPX #$000A
+BEQ EndRun
+INX #2
+BRA SetRunSpeed
+EndRun:
+PLX
+JML $C07966
+
+SaveAPData:
+PHA
+PHX
+PHY
+PHB
+LDA $B4A1; Save file num
+AND #$00FF
+TAX
+LDA #$7E00
+CalcSaveSpot:
+CPX #$0000
+BEQ RunDataSave
+CLC
+ADC #$0010
+DEX
+BRA CalcSaveSpot
+RunDataSave:
+TAY
+LDA #$000F
+LDX #$B570
+MVN $7E20
+;storage
+LDA #$0063
+LDX #$B590
+LDY #$7E40
+MVN $7E20
+PLB
+PLY
+PLX
+PLA
+JSL $EF0A4D
+RTL
+
+ExtraKeyItems:
+LDA $D55000,X
+BEQ CheckKeys
+JML $C14F5A
+CheckKeys:
+JML DeleteExtraKeyItem
+LDA $06
+AND #$00FF
+LDX #$0000
+CheckNextItemStash:
+LDY #$0000
+CheckNextItem:
+SEP #$20
+CMP $99F1,X
+REP #$20
+BEQ FoundItemCopy
+BackToItems:
+INY
+INX
+CPY #$000E
+BNE CheckNextItem
+CPX #$012B;Change this condition?
+BEQ CheckStorageForKeys
+PHA
+TXA
+INC $B58B
+LDA $98A4
+AND #$00FF
+DEC
+CMP $B58B
+BCS .CheckNextChar
+PLA
+JMP CheckStorageForKeys
+.CheckNextChar:
+LDA $B58B
+AND #$00FF
+TAX
+LDA $986F,X
+AND #$00FF
+ASL
+TAX
+LDA InvPointers,X
+TAX
+PLA
+BRA CheckNextItemStash
+FoundItemCopy:
+PHA
+LDA $B580
+AND #$00FF
+BNE RemoveItem
+PLA
+INC $B580
+BRA BackToItems
+PLA
+RemoveItem:
+LDA #$0010
+BRA EndKeyCheck
+CheckStorageForKeys:
+LDX #$0000
+CheckStorageSlot:
+SEP #$20
+CMP $B590,X
+REP #$20
+BEQ FoundItemCopy2
+CPX #$0023
+BEQ EndStorageCheck
+BackToStorage:
+INX
+BRA CheckStorageSlot
+EndStorageCheck:
+LDA #$0000
+EndKeyCheck:
+PLY
+PLX
+STZ $B580
+JML $C14F5A
+FoundItemCopy2:
+PHA
+LDA $B580
+AND #$00FF
+BNE RemoveItem
+PLA
+INC $B580
+BRA BackToStorage
+
+TestArchiItemSpace:
+LDA $0E
+AND #$00FF
+CMP #$00AD
+BEQ .SkipSpaceCheck
+LDA $0000,X
+.SkipSpaceCheck
+JML $C18B5B
+
+GetLumiTextMain:
+LDA $C48037
+AND #$00FF
+BEQ .EndLumiText
+JML $C4845A
+.EndLumiText:
+JML $C48717
+
+GetLumiTextSub:
+LDA $C4803B,X
+AND #$00FF
+BEQ .EndLumiText
+JML $C486FB
+.EndLumiText:
+JML $C48717
+
+LoadAPData:
+LDA $B4A1
+AND #$00FF
+PHA
+PHX
+PHY
+PHB
+TAX
+LDA #$7E00
+CheckFileNum:
+CPX #$0000
+BEQ GotFileData
+CLC
+ADC #$0010
+DEX
+BRA CheckFileNum
+GotFileData:
+TAX
+LDY #$B570
+LDA #$000F
+MVN $207E
+
+LDY #$B590
+LDX #$7E40
+LDA #$0063
+MVN $207E
+PLB
+PLY
+PLX
+PLA
+JML $C1F071
+;Each 10 bytes should be saved per file
+
+SpecialNameDirect:
+CMP #$0007
+BNE NotSeven
+JML $C17E71
+NotSeven:
+CMP #$0016
+BEQ GetSpecialName
+CMP #$0017
+BEQ PullAPName
+CMP #$00B7
+BEQ SilentAPName
+JML CheckMoreSpecialCommands
+GetSpecialName:
+JSR PrintSpecialName
+LDA #$0000
+JML $C17F0F
+PullAPName:
+INC $FF40
+JSR APCC;Move name data
+LDA #$0000
+JML $C17E65
+SilentAPName:
+INC $FF40
+JSR APC2
+LDA #$0000
+JML $C17F0F
+
+
+
+APCC:
+rep #$31
+phd
+pha
+tdc
+adc #$ffee
+tcd
+pla
+; read ARGS_COUNT bytes of args into $97BA
+ldy #$0001
+jsl R_Peek_Parameter_Bytes
+LDA $97BA
+AND #$00FF
+XBA
+JSL MoveItemNames
+PLD
+RTS
+
+APC2:
+rep #$31
+phd
+pha
+tdc
+adc #$ffee
+tcd
+pla
+; read ARGS_COUNT bytes of args into $97BA
+ldy #$0001
+jsr R_Read_Parameter_Bytes
+LDA $97BA
+AND #$00FF
+XBA
+JSL MoveItemNames
+PLD
+RTS
+
+PrintSpecialName:
+ rep #$31
+ phd
+ pha
+ tdc
+ adc #$ffee
+ tcd
+ pla
+ ; read ARGS_COUNT bytes of args into $97BA
+ ldy #$0001
+ jsr R_Read_Parameter_Bytes
+ LDA $97BA
+ AND #$00FF
+ ASL
+ PHX
+ TAX
+ LDA SpecialNameTable,X
+PLX
+ sta $0e
+ lda.w #$00EE
+ sta $10
+ ; load maximum length of the string into A
+ lda #$ffff
+ ; call "printStringAutoNewline"
+ jsl $C447FB
+ ; clean up stack frame and return
+ pld
+ rts
+
+ R_Read_Parameter_Bytes:
+rep #$31
+phd
+tdc
+adc #$ffee
+tcd
+; First things first: get the text pointer
+lda $97b8
+sta $00
+asl ; 2
+adc $00 ; 3
+asl ; 6
+asl ; 12
+adc $00 ; 13
+asl ; 26
+adc $00 ; 27
+; We now have [0x97b8] * 27 in A.
+sta $00
+; Load the text pointer from the table at 0x7E96AA into 0xE.
+tax
+lda $96aa,x
+sta $0e
+lda $96ac,x
+sta $10
+; Load amount into X. Also store into 0x02 for later use
+sty $02
+tyx
+; The memcpy routine copies words. If we have an odd number of
+; bytes, it will round down. We add 1 so that it effectively rounds up.
+inx
+lda #$97ba
+; Copy it: X bytes from [0x0e] to (A).
+jsl $c08ed2
+; Zero out upper bytes of cc_argv
+ldy $02
+tya
+lsr ; Get lower order bit in carry, and branch if 0
+lda #$0000
+bcc .aligned
+; Align Y to next word
+sep #$20
+sta $97ba,y
+rep #$31
+bra .unaligned
+.loop:
+sta $97ba,y
+iny
+.unaligned:
+iny
+.aligned:
+cpy #$0016
+bmi .loop
+.end:
+; Adjust the text pointer and store back into table.
+ldx $00
+lda $0e
+clc
+adc $02
+sta $96aa,x
+lda $10
+sta $96ac,x
+pld
+rts
+
+
+ORG $C0FF00
+MagicantSoloAnimSpeedFix:
+PHA
+JSL SetMagicantAnimSpeeds
+PLA
+JMP $79EA
+
+
+
+ORG $C1FFF1
+StoreItem:
+JSR $913D
+RTL
+GetItemName:
+JSR $9216
+RTL
+
+org $C1FEC9
+ LDA $98B6
+ AND #$00FF
+ ASL
+ TAX
+ LDA Table_HpPpRollSpeedLo-2,x
+ STA $9627
+ LDA #$0001
+ STA $9629
+ LDA Table_FFNIB-2,x
+ ; $C1FEE2
+ STA $964B
+ ; Load text speed
+ LDA.l Table_FramesPerChar-2,x
+ STA $9625
+ BRA $2B
+ ; $C1FEEE
+Table_FramesPerChar:
+ ; Base game values
+ ;.word 0x100
+ ;.word 0x200
+ ;.word 0x300
+ ; Better values (imo)
+ dw $0AA
+ dw $100
+ dw $280
+ ; $C1FEF4
+
+WaitTextOneTick:
+ ; Load leftover portion of # of frames to wait
+ ; $C1FEF4
+ LDA $962B
+ ; Add our text speed
+ CLC
+ ADC $9625
+ TAY
+ ; Save fractional portion back to memory
+ AND #$00FF
+ STA $962B
+ ; $C1FF02
+ ; Determine the integer number of frames to wait
+ TYA
+ XBA
+ AND #$00FF
+ BEQ FrameWaitDone
+ STA $00
+FrameWaitLoop:
+ JSL $C12DD5
+ DEC $00
+ ; $C1FF11
+ BNE FrameWaitLoop
+FrameWaitDone:
+ RTL
+ ; $C1FF14
+
+ ; space before $C1FF19 is free
+
+org $C10D4B
+ JSL WaitTextOneTick
+ BRA $0D
+
+org $C44055
+ JSL WaitTextOneTick
+ BRA $0D
+
+org $C444E3
+ JSL WaitTextOneTick
+ BRA $0D
+
+
+org $C3FB1F
+; The data was previously here, we're modifying the values
+; and breaking them into separate lo/hi word tables...
+; except the hi word is always 1, so no need to store it.
+; we'll use the space to store the FFNIB values
+Table_HpPpRollSpeedLo:
+ ; Base game values
+ ;.word 0x2000
+ ;.word 0x1800
+ ;.word 0x1000
+ ; Better values (imo)
+ dw 4480
+ dw 2480
+ dw 1480
+
+Table_FFNIB:
+ ; Base game values
+ ;.word 30
+ ;.word 60
+ ;.word 0
+ ; Better values (imo)
+ dw 20
+ dw 30
+ dw 0
+
+
+ORG $C308E5
+ db $42
+ dl $C4FDE0
+ db $00; Screen reload code
+
+
+ORG $C4FDE0
+reload_row_gfx:
+LDX $987B
+LDA $9877
+JML $C012ED
+
+ORG $C33572
+db $42
+dl $C4FE60
+db $00
+
+ORG $C4FE60
+spawn_saturn_pd:
+LDA #$0180
+STA $9E2D
+LDA #$1DBB
+STA $9E2F
+RTL
+
+db $18, $04, $0A, $D3, $BB, $C9
+
+ORG $C335B5
+db $42
+dl $C4FE90
+db $00
+
+ORG $C4FE90
+spawn_barf_char:
+LDA #$13C1
+STA $9E2D
+LDA #$1DA1
+STA $9E2F
+RTL
+
+ORG $C4FEA0
+check_paula:
+SEP #$20
+LDA $9C84
+BIT #$01
+REP #$20
+BEQ skip_paula
+JML $C0A98B
+skip_paula:
+LDY #$9F22
+STY $94
+RTL
+
+check_jeff:
+SEP #$20
+LDA $9C84
+BIT #$02
+REP #$20
+BEQ skip_jeff
+JML $C0A98B
+skip_jeff:
+LDY #$9F2D
+STY $94
+RTL
+
+check_poo:
+SEP #$20
+LDA $9C09
+BIT #$80
+REP #$20
+BEQ skip_poo
+JML $C0A98B
+skip_poo:
+LDY #$9F38
+STY $94
+RTL
+
+ORG $D5F5FB
+db $03, $00; Starting level
+
+ORG $D5F600
+db $4A
+
+ORG $D5F830
+db $04, $e1, $03, $0a
+dl PaulaUnlocktext
+
+db $04, $e2, $03, $0a
+dl JeffUnlockText
+
+db $04, $10, $00, $0a
+dl PooUnlockText
+
+db $0A
+dl FlyingManUnlockText
+
+NessFlagText:
+db $04, $17, $04
+db $0A
+dl NessUnlockText
+
+db $0A
+dl DynamicPhotoSetter
+
+ORG $C66AC1
+db $68; Tracy text
+
+ORG $D577EB
+db $8E; Scaraba Pig Nose Shop
+
+ORG $C570A2
+db $0A, $FD, $A6, $EE
+
+ORG $EEA6FD
+db $17, $b5, $03, $00, $04, $ea, $03, $0a, $a6, $70, $c5
+
+ORG $C56F80
+db $0A, $08, $A7, $EE
+
+ORG $EEA708
+db $06, $ea, $03, $18, $a7, $ee, $ff, $15, $14, $1c, $05, $8d, $0a, $85, $6f, $c5
+db $70, $71, $98, $5c, $10, $0a, $50, $79, $50, $91, $9d, $50, $a3, $9f, $9c, $94
+db $50, $9f, $a5, $a4, $5e, $10, $0f, $50, $83, $9f, $50, $a3, $9f, $a2, $a2, $a9
+db $5e, $13, $02; Snake Bagsman
+
+;Present stuff
+ORG $C132D5
+LDX $06
+PHX
+LDX $08
+PHX
+STA $0E
+STZ $10
+JSR $0489
+PLA
+STA $08
+PLA
+INC
+INC
+STA $06
+JML FindMovData
+GotMovData:
+STZ $10
+JSR $045D
+BRA $3A
+
+ORG $C7D8A5
+db $0A, $40, $A7, $EE
+
+ORG $EEA740
+db $09, $03, $ee, $d8, $c7, $00, $F4, $BA, $ee, $00, $FE, $BA, $ee, $00, $0a, $ad
+db $d8, $c7, $70, $71, $50, $a7, $95, $9c, $9c, $50, $9f, $96, $50, $a0, $a3, $a9
+db $93, $98, $99, $93, $50, $95, $9e, $95, $a2, $97, $a9, $50, $a3, $a0, $a2, $a5
+db $9e, $97, $50, $a5, $a0, $50, $96, $a2, $9f, $9d, $50, $99, $9e, $a3, $99, $94
+db $95, $51, $03, $1b, $04, $09, $10, $11, $92, $ee, $ff, $41, $92, $ee, $ff, $73
+db $92, $ee, $ff, $b0, $92, $ee, $ff, $e1, $92, $ee, $ff, $19, $93, $ee, $ff, $56
+db $93, $ee, $ff, $89, $93, $ee, $ff, $bb, $93, $ee, $ff, $ed, $93, $ee, $ff, $1f
+db $94, $ee, $ff, $50, $94, $ee, $ff, $8c, $94, $ee, $ff, $c4, $94, $ee, $ff, $02
+db $95, $ee, $ff, $55, $95, $ee, $ff, $55, $95, $ee, $ff, $02, $70, $71, $50, $a6
+db $9f, $99, $93, $95, $50, $a2, $91, $9e, $97, $50, $9f, $a5, $a4, $50, $96, $a2
+db $9f, $9d, $50, $a7, $99, $a4, $98, $99, $9e, $0A, $F5, $A7, $EE, $50, $a0, $a2
+db $95, $a3, $95, $9e, $a4, $51, $03, $00, $1b, $04, $09, $04, $30, $f8, $d5, $ff
+db $37, $f8, $d5, $ff, $3e, $f8, $d5, $ff, $45, $f8, $d5, $ff, $0A
+dl ExtraPresentCheck
+
+ORG $EEA810
+db $7f, $9e, $95, $a4, $a4, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $84
+db $a7, $9f, $a3, $9f, $9e, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $78
+db $91, $a0, $a0, $a9, $5d, $78, $91, $a0, $a0, $a9, $50, $86, $99, $9c, $9c, $91
+db $97, $95, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $84, $98, $a2, $95
+db $95, $94, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $83, $91, $a4, $a5
+db $a2, $9e, $50, $86, $91, $9c, $9c, $95, $a9, $50, $84, $95, $9c, $95, $a0, $9f
+db $a2, $a4, $00, $74, $a5, $a3, $a4, $a9, $50, $74, $a5, $9e, $95, $a3, $50, $84
+db $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $76, $9f, $a5, $a2, $a3, $99, $94, $95
+db $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $87, $99, $9e, $a4, $95, $a2
+db $a3, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $83, $a5, $9d, $9d, $95
+db $a2, $a3, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $83, $93, $91, $a2
+db $91, $92, $91, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $74, $91, $9c
+db $91, $91, $9d, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $74, $95, $95
+db $a0, $50, $74, $91, $a2, $9b, $9e, $95, $a3, $a3, $50, $84, $95, $9c, $95, $a0
+db $9f, $a2, $a4, $00, $84, $95, $9e, $94, $91, $50, $86, $99, $9c, $9c, $91, $97
+db $95, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00, $7c, $9f, $a3, $a4, $50
+db $85, $9e, $94, $95, $a2, $a7, $9f, $a2, $9c, $94, $50, $84, $95, $9c, $95, $a0
+db $9f, $a2, $a4, $00, $80, $a2, $9f, $97, $a2, $95, $a3, $a3, $99, $a6, $95, $50
+db $80, $9f, $9f, $50, $80, $83, $79, $00, $80, $91, $a5, $9c, $91, $00, $7a, $95
+db $96, $96, $00, $80, $9f, $9f, $00, $76, $9c, $a9, $99, $9e, $97, $50, $7d, $91
+db $9e, $00, $7D, $91, $97, $99, $93, $91, $9E, $A4, $50, $84, $95, $9C, $95, $A0
+db $9F, $A2, $A4, $00
+
+ORG $EEA963
+db $03, $06, $d2, $03, $6c, $a9, $ee, $ff, $1f, $02, $71, $18, $04, $10, $80, $18
+db $01, $01, $08, $90, $a9, $ee, $ff, $1b, $03, $83, $a9, $ee, $ff, $04, $d2, $03
+db $02
+
+ORG $EEA990
+db $70, $84, $98, $95, $a2, $95, $50, $99, $a3, $50, $91, $50, $1c, $05, $01, $50
+db $99, $9e, $50, $a4, $98, $95, $50, $a3, $a4, $91, $a4, $a5, $95, $57, $a3, $50
+db $95, $a9, $95, $5e, $03, $00, $1d, $00, $FF, $01, $1b, $03, $ef, $a9, $ee, $ff
+db $70, $72, $a5, $a4, $19, $10, $01, $1b, $04, $50, $1c, $02, $00, $50, $99, $a3
+db $50, $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5
+db $93, $98, $50, $a3, $a4, $a5, $96, $96, $5e, $1f, $81, $00, $01, $13, $02, $1b
+db $04, $1f, $02, $74, $10, $30, $08, $58, $d9, $c7, $ff, $1f, $81, $ff, $ff, $02
+
+ORG $EEAA00
+db $18, $01, $01, $08, $12, $aa, $ee, $ff, $1b, $03, $11, $aa, $ee, $ff, $04, $e4
+db $03, $02, $1f, $17, $2e, $00, $0a, $00, $01, $70, $72, $95, $96, $9f, $a2, $95
+db $50, $79, $50, $97, $9f, $5c, $10, $0a, $50, $79, $50, $a7, $91, $9e, $a4, $50
+db $a9, $9f, $a5, $50, $a4, $9f, $50, $98, $91, $a6, $95, $50, $a4, $98, $99, $a3
+db $50, $1c, $05, $01, $5e, $03, $1d, $0e, $ff, $01, $1b, $02, $60, $aa, $ee, $ff
+db $08, $cf, $dc, $c7, $ff, $03, $1f, $1e, $2e, $00, $01, $1f, $81, $ff, $ff, $02
+db $00, $70, $72, $a5, $a4, $5c, $10, $0a, $50, $a9, $9f, $a5, $57, $a2, $95, $50
+db $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5, $93
+db $98, $50, $a3, $a4, $a5, $96, $96, $5e, $03, $04, $2f, $01, $02; Buzz Buzz 2
+
+ORG $C78C32
+db $0A, $8D, $AA, $EE
+
+ORG $EEAA8D
+db $06, $76, $01, $9d, $aa, $ee, $ff, $05, $16, $02, $1f, $03, $0a, $ce, $88, $c8
+db $50, $18, $01, $01, $0a, $36, $8c, $c7
+
+ORG $C888D1
+db $70, $87, $98, $9f, $91, $98, $5e, $10, $0f, $50, $84, $98, $91, $a4, $50, $a4
+db $a5, $9e, $9e, $95, $9c, $57, $a3, $50, $98, $91, $a5, $9e, $a4, $95, $94, $5e
+db $03, $00, $70, $79, $57, $9d, $50, $9e, $9f, $a4, $50, $97, $9f, $99, $9e, $97
+db $50, $a4, $98, $91, $a4, $50, $a7, $91, $a9, $5e, $50, $10, $0f, $7d, $a9, $50
+db $9a, $9f, $92, $50, $91, $99, $9e, $57, $a4, $50, $a7, $9f, $a2, $a4, $98, $50
+db $a4, $98, $99, $a3, $51, $03, $00, $70, $89, $9f, $a5, $50, $97, $9f, $50, $9f
+db $9e, $50, $a9, $9f, $a5, $a2, $50, $9f, $a7, $9e, $51, $03, $1F, $41, $06, $00; Bus 2
+
+ORG $EEAAA5
+db $0D, $01, $0D, $01, $1b, $04, $09, $14, $fe, $aa, $ee, $00, $30, $ab, $ee, $00
+db $63, $ab, $ee, $00, $a3, $ab, $ee, $00, $d6, $ab, $ee, $00, $10, $ac, $ee, $00
+db $4f, $ac, $ee, $00, $84, $ac, $ee, $00, $b8, $ac, $ee, $00, $ec, $ac, $ee, $00
+db $20, $ad, $ee, $00, $53, $ad, $ee, $00, $8d, $ad, $ee, $00, $c7, $ad, $ee, $00
+db $07, $ae, $ee, $00, $40, $ae, $ee, $00, $62, $ae, $ee, $00, $84, $ae, $ee, $00
+db $a6, $ae, $ee, $00, $c5, $ae, $ee, $00, $02, $1f, $02, $67, $10, $30, $01, $70
+db $58, $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e, $95, $94, $50, $98, $9f, $a7
+db $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $50, $a4, $9f, $50
+db $7f, $9e, $95, $a4, $a4, $51, $59, $04, $d1, $00, $02, $1f, $02, $67, $10, $30
+db $01, $70, $58, $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e, $95, $94, $50, $98
+db $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $50, $a4
+db $9f, $50, $84, $a7, $9f, $a3, $9f, $9e, $51, $59, $04, $d2, $00, $02, $1f, $02
+db $67, $10, $30, $01, $70, $58, $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e, $95
+db $94, $50, $98, $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2
+db $a4, $50, $a4, $9f, $50, $78, $91, $a0, $a0, $a9, $5d, $78, $91, $a0, $a0, $a9
+db $50, $86, $99, $9c, $9c, $91, $97, $95, $51, $59, $04, $dd, $00, $02, $1f, $02
+db $67, $10, $30, $01, $70, $58, $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e, $95
+db $94, $50, $98, $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2
+db $a4, $50, $a4, $9f, $50, $84, $98, $a2, $95, $95, $94, $51, $59, $04, $d3, $00
+db $02, $1f, $02, $67, $10, $30, $01, $70, $58, $1c, $02, $01, $50, $9c, $95, $91
+db $a2, $9e, $95, $94, $50, $98, $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95
+db $a0, $9f, $a2, $a4, $50, $a4, $9f, $50, $83, $91, $a4, $a5, $a2, $9e, $50, $86
+db $91, $9c, $9c, $95, $a9, $51, $59, $04, $d5, $00, $02, $1f, $02, $67, $10, $30
+db $01, $70, $58, $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e, $95, $94, $50, $98
+db $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $50, $a4
+db $9f, $50, $74, $a5, $a3, $a4, $a9, $50, $74, $a5, $9e, $95, $a3, $50, $74, $95
+db $a3, $95, $a2, $a4, $51, $59, $04, $de, $00, $02, $1f, $02, $67, $10, $30, $01
+db $70, $58, $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e, $95, $94, $50, $98, $9f
+db $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $50, $a4, $9f
+db $50, $76, $9f, $a5, $a2, $a3, $99, $94, $95, $51, $59, $04, $d6, $00, $02, $1f
+db $02, $67, $10, $30, $01, $70, $58, $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e
+db $95, $94, $50, $98, $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f
+db $a2, $a4, $50, $a4, $9f, $50, $87, $99, $9e, $a4, $95, $a2, $a3, $51, $59, $04
+db $d4, $00, $02, $1f, $02, $67, $10, $30, $01, $70, $58, $1c, $02, $01, $50, $9c
+db $95, $91, $a2, $9e, $95, $94, $50, $98, $9f, $a7, $50, $a4, $9f, $50, $a4, $95
+db $9c, $95, $a0, $9f, $a2, $a4, $50, $a4, $9f, $50, $83, $a5, $9d, $9d, $95, $a2
+db $a3, $51, $59, $04, $d7, $00, $02, $1f, $02, $67, $10, $30, $01, $70, $58, $1c
+db $02, $01, $50, $9c, $95, $91, $a2, $9e, $95, $94, $50, $98, $9f, $a7, $50, $a4
+db $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $50, $a4, $9f, $50, $83, $93
+db $91, $a2, $91, $92, $91, $51, $59, $04, $d9, $00, $02, $1f, $02, $67, $10, $30
+db $01, $70, $58, $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e, $95, $94, $50, $98
+db $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $50, $a4
+db $9f, $50, $74, $91, $9c, $91, $91, $9d, $51, $59, $04, $d8, $00, $02, $1f, $02
+db $67, $10, $30, $01, $70, $58, $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e, $95
+db $94, $50, $98, $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2
+db $a4, $50, $a4, $9f, $50, $74, $95, $95, $a0, $50, $74, $91, $a2, $9b, $9e, $95
+db $a3, $a3, $51, $59, $04, $da, $00, $02, $1f, $02, $67, $10, $30, $01, $70, $58
+db $1c, $02, $01, $50, $9c, $95, $91, $a2, $9e, $95, $94, $50, $98, $9f, $a7, $50
+db $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $50, $a4, $9f, $50, $84
+db $95, $9e, $94, $91, $50, $86, $99, $9c, $9c, $91, $97, $95, $51, $59, $04, $db
+db $00, $02, $1f, $02, $67, $10, $30, $01, $70, $58, $1c, $02, $01, $50, $9c, $95
+db $91, $a2, $9e, $95, $94, $50, $98, $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c
+db $95, $a0, $9f, $a2, $a4, $50, $a4, $9f, $50, $a4, $98, $95, $50, $7c, $9f, $a3
+db $a4, $50, $85, $9e, $94, $95, $a2, $a7, $9f, $a2, $9c, $94, $51, $59, $04, $dc
+db $00, $02, $1f, $02, $67, $10, $30, $01, $70, $58, $1c, $02, $04, $50, $9c, $95
+db $91, $a2, $9e, $95, $94, $50, $98, $9f, $a7, $50, $a4, $9f, $50, $a5, $a3, $95
+db $06, $d1, $03, $36, $ae, $ee, $ff, $1c, $12, $15, $51, $59, $0A
+dl SetNpcPooPSIFlag
+db $02, $1c, $12, $16, $51, $59, $1f, $71, $04, $03, $02
+db $0A
+dl NPCPaulaTex
+NpcPaulaReturn:
+db $10
+db $85, $01, $1f, $03, $70, $58, $1c, $02, $02, $50, $9a, $9f, $99, $9e, $95, $94
+db $50, $a9, $9f, $a5, $5e, $59, $04, $e1, $03, $1f, $11, $02, $02
+
+db $0A
+dl NPCJeffTex
+NpcJeffReturn:
+db $10, $85, $01, $1f, $03, $70, $58, $1c, $02, $03, $50, $9a, $9f, $99, $9e
+db $95, $94, $50, $a9, $9f, $a5, $5e, $59, $04, $e2, $03, $1f, $11, $03, $02, $1f
+db $00, $00, $7b, $10, $85, $00, $1f, $03, $70, $58, $1c, $02, $04, $50, $9a, $9f
+db $99, $9e, $95, $94, $50, $a9, $9f, $a5, $5e, $59, $04, $10, $00, $1f, $11, $04
+db $02
+
+db $0A
+dl NPCPooTex
+NpcPooReturn:
+db $10, $85, $01, $1f, $03, $70, $58, $1c, $02, $0b, $50
+db $9a, $9f, $99, $9e, $95, $94, $50, $a9, $9f, $a5, $5e, $59, $1f, $11, $0b, $02
+db $1f, $02, $67, $10, $30, $01, $70, $58, $1c, $02, $01, $50, $9c, $95, $91, $a2
+db $9e, $95, $94, $50, $98, $9f, $a7, $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0
+db $9f, $a2, $a4, $50, $a4, $9f, $50, $7d, $91, $97, $99, $93, $91, $9e, $a4, $51
+db $59, $04, $DF, $00, $02; MASSIVE NPC PSI table
+
+ORG $C87E7C
+db $08, $b6, $dc, $c7, $00, $0D, $01
+
+ORG $C86305
+db $08, $b6, $dc, $c7, $00, $01, $01
+
+ORG $C66309
+db $19, $10, $01, $1b, $04
+
+ORG $C661D0
+db $a4, $98, $95, $50, $1c, $05, $01, $5e, $03, $00, $70, $80, $99, $a2, $9b, $9c
+db $95, $5c, $10, $0f, $50, $a4, $98, $95, $50, $7d, $91, $a9, $9f, $a2, $50, $9f
+db $96, $50, $7f, $9e, $95, $a4, $a4, $5c, $10, $15, $50, $a3, $98, $9f, $a5, $9c
+db $94, $50, $98, $91, $a6, $95, $50, $99, $a4, $5e, $03, $00, $70, $79, $96, $50
+db $a9, $9f, $a5, $50, $9e, $95, $95, $94, $50, $99, $a4, $5c, $10, $0f, $50, $97
+db $9f, $50, $a4, $91, $9c, $9b, $50, $a4, $9f, $50, $98, $99, $9d, $5e, $03, $00
+db $0a, $09, $63, $c6, $ff
+
+ORG $C72D49
+db $1c, $05, $01, $5e, $0a, $53, $2d, $c7
+
+ORG $C72D62
+db $08, $b6, $dc, $c7, $00, $01, $01
+
+ORG $C80021
+db $49
+
+ORG $C8001A
+db $49
+
+ORG $C80005
+db $01, $01, $01, $01, $01, $01
+
+ORG $C80044
+db $1c, $05, $01, $0a, $4b, $00, $c8
+
+ORG $C93E5D
+db $1c, $05, $01, $0a, $6b, $3e, $c9
+
+ORG $C93EE7
+db $01, $01, $01, $01, $01, $01
+
+ORG $C93F2A
+db $0A, $FA, $AE, $EE
+
+ORG $EEAEFA
+db $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $13, $04, $9e, $02, $02
+
+ORG $C80619
+db $1c, $05, $01, $0a, $20, $06, $c8
+
+ORG $C94E76
+db $0A, $4D, $BE, $EE
+
+ORG $EEBE4D
+db $50, $a4, $15, $d7, $04, $eb, $03, $0a, $7a, $4e, $c9
+
+ORG $C7617C
+db $08, $cf, $dc, $c7, $00, $04, $4c, $00
+
+ORG $EF96D3
+db $01, $01, $01
+
+ORG $D5F8E0
+db $70, $79, $50, $98, $91, $a6, $95, $50, $93, $9f, $9d, $95, $50, $a4, $9f, $50
+db $a4, $95, $91, $93, $98, $50, $a9, $9f, $a5, $50, $a4, $98, $99, $a3, $50, $a0
+db $9f, $a7, $95, $a2, $5e, $50, $71, $93, $93, $95, $a0, $a4, $50, $99, $a4, $50
+db $9e, $9f, $a7, $51, $03, $02, $ff, $ff, $70, $74, $95, $10, $05, $95, $10, $05
+db $95, $10, $05, $95, $10, $05, $95, $10, $05, $95, $9c, $99, $a6, $95, $a2, $a9
+db $50, $96, $9f, $a2, $50, $9f, $9e, $95, $19, $10, $01, $1b, $04, $50, $1c, $02
+db $00, $51, $03, $00, $70, $7f, $9e, $95, $50, $9f, $a2, $94, $95, $a2, $50, $9f
+db $96, $50, $91, $50, $02
+
+ORG $EEAF08
+db $08, $33, $af, $ee, $00, $01, $08, $30, $f8, $d5, $ff, $00, $1b, $03, $ff, $ff
+db $ff, $ff, $0a, $1f, $96, $c7, $1d, $0e, $ff, $01, $1b, $03, $29, $af, $ee, $ff
+db $02, $08, $b6, $dc, $c7, $ff, $03, $1f, $81, $ff, $ff, $02
+
+ORG $EEAF34
+db $03, $1d, $03, $ff, $1b, $02, $4d, $af, $ee, $ff, $1d, $0e, $ff, $11, $08, $cf
+db $dc, $c7, $ff, $03, $0a
+dl CabinTextJump
+db $c7, $00, $70, $89, $9f, $a5, $57, $a2
+db $95, $50, $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d
+db $a5, $93, $98, $50, $a3, $a4, $a5, $96, $96, $5e, $03, $1f, $f1, $b5, $01, $0a
+db $00, $04, $ec, $03, $02
+
+ORG $CFA690
+db $ed, $03
+
+ORG $CFA6A1
+db $ed, $03
+
+ORG $CFA67F
+db $EC, $03
+
+ORG $CF8865
+db $4a, $6f
+
+ORG $CFA68E
+db $0A
+
+ORG $C796B8
+db $02
+
+ORG $CFB02B
+db $02
+
+ORG $EEAF79
+db $08, $78, $AF, $EE, $ff, $08, $af, $af, $ee, $ff, $1b, $03, $8d, $af, $ee, $ff
+db $0a, $a4, $9b, $ee, $70, $89, $9f, $a5, $57, $a2, $95, $50, $93, $91, $a2, $a2
+db $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $50, $a3, $a4
+db $a5, $96, $96, $5e, $13, $02, $1c, $05, $01, $51, $03, $1d, $03, $ff, $1b, $02
+db $c2, $af, $ee, $ff, $1f, $81, $01, $02, $02, $1d, $0e, $ff, $01, $08, $cf, $dc
+db $c7, $ff, $03, $1f, $81, $ff, $ff, $02
+
+ORG $CFB034
+db $d1, $af, $ee
+
+ORG $EEAFD1
+db $1b, $00, $0a, $cb, $da, $c7, $1b, $01, $08, $40, $a7, $ee, $ff
+db $0A
+dl fix_boogey_tent
+
+ORG $C7DAEB
+db $0a, $d7, $af, $ee
+
+ORG $C7A300
+db $02
+
+ORG $EEAFE2
+db $08, $f1, $af, $ee, $ff, $1b, $02, $3d, $b0, $ee, $ff, $04, $e6, $03, $02, $01
+db $70, $87, $98, $9f, $91, $98, $51, $10, $0f, $50, $84, $98, $95, $a2, $95, $57
+db $a3, $50, $91, $50, $1c, $05, $01, $50, $91, $a4, $50, $a4, $98, $95, $50, $92
+db $9f, $a4, $a4, $9f, $9d, $50, $9f, $96, $50, $a4, $98, $95, $50, $93, $a5, $a0
+db $51, $03, $1d, $03, $ff, $1b, $02, $38, $b0, $ee, $ff, $1d, $0e, $ff, $01, $08
+db $cf, $dc, $c7, $ff, $13, $02, $1f, $81, $ff, $ff, $02, $01, $70, $72, $a5, $a4
+db $5c, $10, $0a, $50, $19, $10, $01, $1b, $04, $1c, $02, $00, $50, $99, $a3, $50
+db $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5, $93
+db $98, $50, $a3, $a4, $a5, $96, $96, $5e, $13, $02
+
+ORG $C7EA9B
+db $04, $ab, $00, $01, $15, $3d, $17, $75, $15, $23, $00, $50, $50, $1c, $05, $00
+db $5e, $03, $1f, $30, $1d, $0e, $00, $fc, $08, $cf, $dc, $c7, $ff, $03, $01, $0a
+db $00, $ea, $c7
+
+ORG $C7EABF
+db $04, $aC, $00, $01, $15, $3d, $17, $75, $15, $23, $00, $50, $50, $1c, $05, $00
+db $5e, $03, $1f, $30, $1d, $0e, $00, $fc, $08, $cf, $dc, $c7, $ff, $03, $01, $0a
+db $00, $ea, $c7
+
+ORG $C7EAE3
+db $04, $ad, $00, $01, $15, $3d, $17, $75, $15, $23, $00, $50, $50, $1c, $05, $00
+db $5e, $03, $1f, $30, $1d, $0e, $00, $fc, $08, $cf, $dc, $c7, $ff, $03, $01, $0a
+db $00, $ea, $c7
+
+ORG $C7EA95
+db $BF
+
+ORG $C7EA8E
+db $E3
+
+ORG $C60D8A
+db $50, $1c, $05, $01, $0a, $99, $0d, $c6
+
+ORG $C60DA5
+db $1f, $1e, $1a, $03, $06, $1d, $0e, $ff, $bb
+
+ORG $C6F8FD
+db $6c, $b0, $ee
+
+ORG $C6FA30
+db $02
+
+ORG $EEB06C
+db $08, $0c, $fa, $c6, $ff, $06, $58, $00, $6b, $b0, $ee, $ff, $0e, $02, $0d, $01
+db $1b, $04, $0e, $03, $0d, $01, $0a, $40, $a7, $ee
+
+ORG $C6F957
+db $86, $b0, $ee
+
+ORG $EEB086
+db $08, $0c, $fa, $c6, $ff, $06, $67, $00, $6b, $b0, $ee, $ff, $0e, $02, $0d, $01
+db $1b, $04, $0e, $03, $0d, $01, $0a, $40, $a7, $ee
+
+ORG $C6F984
+db $A0, $B0, $EE
+
+ORG $EEB0A0
+db $08, $0c, $fa, $c6, $ff, $06, $17, $00, $6b, $b0, $ee, $ff, $0e, $02, $0d, $01
+db $1b, $04, $0e, $03, $0d, $01, $0a, $40, $a7, $ee
+
+ORG $C4FE40
+db $02
+
+ORG $C6F92A
+db $BA, $B0, $EE
+
+ORG $EEB0BA
+db $08, $0c, $fa, $c6, $ff, $06, $4e, $01, $6b, $b0, $ee, $ff, $0e, $02, $0d, $01
+db $1b, $04, $0e, $03, $0d, $01, $0a, $40, $a7, $ee
+
+ORG $C6AB8D
+db $67, $AC
+
+ORG $C6AC79
+db $0A, $EC, $AB, $C6
+
+ORG $EEB0D4
+db $08, $27, $b1, $ee, $ff, $08, $0b, $b1, $ee, $ff, $1b, $03, $e8, $b0, $ee, $ff
+db $0a, $ef, $99, $ee, $01, $70, $89, $9f, $a5, $57, $a2, $95, $50, $93, $91, $a2
+db $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $50, $a3
+db $a4, $a5, $96, $96, $5e, $13, $02, $1c, $05, $02, $51, $03, $1d, $03, $ff, $1b
+db $02, $28, $b1, $ee, $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $13, $1f
+db $81, $ff, $ff, $02, $1f, $81, $01, $01, $02
+
+ORG $C570A6
+db $1d, $03, $ff, $1d, $0e, $00, $8d, $08, $cf, $dc, $c7, $ff, $13, $02
+
+ORG $C6AEAC
+db $01, $01, $01, $01
+
+ORG $C6AEC3
+db $03, $1d, $0e, $ff, $01, $08, $b6, $dc, $c7, $ff, $0a, $dc, $ae, $c6
+
+ORG $2FFA6C
+db $1d, $03, $ff, $1b, $02, $94, $fa, $2f, $ff, $1d, $0e, $ff, $01, $08, $b6, $dc
+db $c7, $ff, $03, $04, $dd, $03, $02, $00, $00, $00, $00, $00, $00, $00, $00, $00
+db $00, $00, $00, $00, $00, $00, $00, $00, $70
+
+ORG $C625F5
+db $17, $fd, $a5, $50, $85, $9b, $a9, $a5, $9b, $a5, $99, $6f, $03, $00, $17, $73
+db $91, $a4, $15, $cb, $16, $fc, $a3, $17, $c7, $17, $07, $94, $99, $94, $15, $6c
+db $6f, $10, $14, $17, $9c, $a5, $a4, $5c, $17, $3f, $17, $6d, $15, $88, $91, $a4
+db $17, $35, $93, $a4, $17, $c9, $5c, $15, $66, $15, $b1, $6f, $59, $03, $1d, $0e
+db $ff, $a6
+
+ORG $C60FE5
+db $01, $01, $01, $1d, $0e, $00, $8b
+
+ORG $C951E3
+db $0A, $2D, $B1, $EE
+
+ORG $EEB12D
+db $08, $68, $b1, $ee, $ff, $1b, $02, $3c, $b1, $ee, $ff, $04, $65, $02, $02, $00
+db $70, $7b, $a9, $95, $95, $9b, $a9, $9f, $9b, $a9, $91, $03, $00, $70, $58, $89
+db $9f, $a5, $57, $a6, $95, $50, $97, $9f, $a4, $50, $a4, $9f, $9f, $50, $9d, $a5
+db $93, $98, $50, $9a, $a5, $9e, $9b, $51, $59, $13, $02, $18, $01, $01, $70, $7b
+db $95, $95, $9b, $a9, $9f, $9f, $9b, $a9, $91, $9b, $a9, $95, $91, $9b, $03, $00
+db $70, $58, $7a, $a5, $a3, $a4, $50, $9b, $99, $94, $94, $99, $9e, $97, $51, $50
+db $79, $57, $9c, $9c, $50, $97, $99, $a6, $95, $50, $a9, $9f, $a5, $50, $a4, $98
+db $99, $a3, $1c, $05, $01, $5e, $59, $03, $1d, $03, $ff, $1b, $02, $bd, $b1, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $1f, $81, $01, $01, $03, $02
+db $1f, $81, $ff, $ff, $02
+
+ORG $C99D3E
+db $D6
+
+ORG $C6D667
+db $0A, $C2, $B1, $EE
+
+ORG $C61014
+db $83, $9f, $50, $97, $9f, $9f, $94, $50, $9c, $a5, $93, $9b, $50, $a7, $99, $a4
+db $98, $50, $a4, $98, $91, $a4, $5e, $13, $0a, $e4, $10, $c6
+
+ORG $C60FF3
+db $04, $2a, $02;Set the flag
+
+ORG $EEB1C2
+db $1d, $03, $ff, $1b, $02, $7d, $92, $c8, $ff, $1d, $0e, $ff, $01, $08, $b6, $dc
+db $c7, $ff, $03, $00, $0a, $de, $92, $c8
+
+ORG $EF66AC
+db $0A, $DA, $B1, $EE
+
+ORG $EEB1DA
+db $04, $76, $01, $04, $4e, $02, $04, $6f, $02, $0a, $b2, $66, $EF
+
+ORG $C7AAB8
+db $1d, $03, $ff, $1b, $02, $e2, $aa, $c7, $ff, $1d, $0e, $ff, $04, $08, $b6, $dc
+db $c7, $ff, $03, $18, $04, $0a, $0f, $b7, $c7
+
+ORG $C7B595
+db $1d, $0e, $ff, $01, $08, $b6, $dc, $c7, $ff, $03, $0a, $af, $b5, $c7
+
+ORG $C7B576
+db $1d, $03, $ff, $01
+
+ORG $EE9A8B
+db $0A, $2E, $B2, $EE
+
+ORG $EEB22E
+db $08, $66, $b2, $ee, $ff, $1b, $03, $3d, $b2, $ee, $ff, $0a, $90, $9a, $ee, $04
+db $ee, $03, $18, $01, $01, $70, $87, $98, $91, $a4, $6f, $51, $50, $89, $9f, $a5
+db $57, $a2, $95, $50, $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $9f
+db $50, $9d, $a5, $93, $98, $51, $13, $02, $1d, $03, $ff, $1b, $02, $81, $b2, $ee
+db $ff, $18, $01, $01, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $1f, $81
+db $ff, $ff, $02, $1f, $81, $01, $01, $02
+
+ORG $CFC80B
+db $86, $B2, $EE
+
+ORG $EEB286
+db $06, $8b, $00, $88, $c5, $c7, $00, $06, $ee, $03, $2e, $b2, $ee, $00, $0a, $6b
+db $2c, $c8
+
+ORG $EEB298
+db $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $02
+
+ORG $C6E9D0
+db $98, $b2, $ee
+
+ORG $C6E9A3
+db $00
+
+ORG $C29298
+RTL
+
+ORG $C6EB30
+db $1d, $03, $ff, $1b, $02, $59, $eb, $c6, $00, $1d, $0e, $ff, $01, $08, $b6, $dc
+db $c7, $ff, $0a, $54, $eb, $c6
+
+ORG $C6EF1F
+db $03, $0A, $AB, $B2, $EE
+
+ORG $EEB2AB
+db $01, $08, $2c, $b2, $ee, $ff, $08, $c1, $b2, $ee, $ff, $1b, $03, $e3, $b2, $ee
+db $ff, $0a, $7b, $ee, $c6, $00, $1c, $05, $01, $51, $03, $1d, $03, $ff, $1b, $02
+db $de, $b2, $ee, $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $1f, $81
+db $ff, $ff, $02, $1f, $81, $01, $01, $02, $01, $70, $89, $9f, $a5, $57, $a2, $95
+db $50, $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5
+db $93, $98, $51, $03, $02
+
+ORG $C8B19F
+db $04, $0B, $00
+
+ORG $C920B8
+db $0A, $00, $B3, $EE
+
+ORG $EEB300
+db $1f, $21, $51, $1f, $e5, $ff, $1f, $21, $24, $1f, $e5, $ff, $1f, $ec, $ff, $06
+db $08, $1f, $b3, $ee, $ff, $1b, $03, $f3, $b3, $ee, $ff, $0A
+dl ActivateCakeEnemies
+db $10
+db $20, $1f, $1a, $2d, $04, $03, $1f, $16, $2d, $04, $01, $1f, $e6, $2d, $04, $10
+db $1a, $1f, $1b, $2d, $04, $18, $01, $01, $70, $89, $9f, $a5, $57, $a2, $95, $50
+db $96, $99, $9e, $91, $9c, $9c, $a9, $50, $91, $a7, $91, $9b, $95, $5e, $03, $00
+db $70, $89, $9f, $a5, $50, $a7, $95, $a2, $95, $50, $9f, $a5, $a4, $50, $96, $9f
+db $a2, $50, $a1, $a5, $99, $a4, $95, $50, $91, $a7, $98, $99, $9c, $95, $5e, $5e
+db $5e, $10, $0f, $50, $84, $91, $9b, $95, $50, $a4, $98, $99, $a3, $50, $91, $a3
+db $50, $91, $9e, $50, $91, $a0, $9f, $9c, $9f, $97, $a9, $50, $96, $a2, $9f, $9d
+db $50, $9d, $95, $5e, $03, $1d, $03, $ff, $1b, $02, $ad, $b3, $ee, $ff, $1d, $0e
+db $ff, $01, $08, $b6, $dc, $c7, $ff, $03, $1f, $81, $ff, $ff, $02, $01, $70, $89
+db $9f, $a5, $57, $a2, $95, $50, $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50, $a4
+db $9f, $9f, $50, $9d, $a5, $93, $98, $50, $a3, $a4, $a5, $96, $96, $51, $03, $00
+db $70, $79, $57, $9c, $9c, $50, $98, $9f, $9c, $94, $50, $9f, $9e, $a4, $9f, $50
+db $99, $a4, $50, $96, $9f, $a2, $50, $9e, $9f, $a7, $5e, $03, $04, $ef, $03, $1f
+db $81, $01, $01, $02
+
+ORG $C8B226
+db $01, $01, $01
+
+ORG $C8B192
+db $EF, $03
+
+ORG $C8B22F
+db $1d, $03, $ff, $1b, $02, $ad, $b3, $ee, $ff, $05, $ef, $03, $04, $3d, $00, $0a
+db $50, $b3, $ee
+
+ORG $C928FB
+db $01, $01
+
+ORG $C92992
+db $F4, $B3, $EE
+
+ORG $EEB3F4
+db $01, $1f, $81, $ff, $ff, $08, $08, $b4, $ee, $ff, $1b, $03, $87, $b4, $ee, $ff
+db $04, $11, $01, $02, $01, $70, $84, $98, $95, $50, $9d, $91, $a3, $a4, $95, $a2
+db $50, $98, $91, $a3, $50, $95, $9e, $a4, $a2, $a5, $a3, $a4, $95, $94, $50, $9d
+db $95, $50, $a4, $9f, $50, $94, $95, $9c, $99, $a6, $95, $a2, $50, $a4, $98, $99
+db $a3, $51, $03, $1d, $03, $ff, $1b, $02, $4f, $b4, $ee, $ff, $1d, $0e, $ff, $01
+db $08, $cf, $dc, $c7, $ff, $03, $1f, $81, $ff, $ff, $02, $01, $70, $89, $9f, $a5
+db $50, $98, $91, $a6, $95, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $51, $03
+db $00, $70, $84, $98, $95, $50, $9d, $91, $a3, $a4, $95, $a2, $50, $a7, $99, $9c
+db $9c, $50, $98, $9f, $9c, $94, $50, $9f, $9e, $a4, $9f, $50, $99, $a4, $5e, $04
+db $F0, $03, $03, $02
+
+ORG $C9C992
+db $f0, $03, $88, $b4, $ee
+
+ORG $EEB488
+db $70, $89, $9f, $a5, $50, $98, $91, $a6, $95, $50, $9c, $95, $96, $a4, $50, $a4
+db $98, $99, $a3, $5e, $1d, $03, $ff, $1b, $02, $b0, $b4, $ee, $ff, $05, $f0, $03
+db $04, $11, $01, $0a, $40, $b4, $ee, $ff, $01, $70, $89, $9f, $a5, $50, $98, $91
+db $a6, $95, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $5e, $13, $02
+
+ORG $6101A7
+db $0A, $C6, $B4, $EE
+
+ORG $EEB4C6
+db $1f, $81, $ff, $ff, $01, $08, $c5, $b4, $ee, $ff, $08, $e0, $b4, $ee, $ff, $1b
+db $03, $02, $b5, $ee, $ff, $0a, $b2, $01, $61, $00, $1c, $05, $01, $51, $03, $1d
+db $03, $ff, $1b, $02, $fd, $b4, $ee, $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7
+db $ff, $03, $1f, $81, $ff, $ff, $02, $1f, $81, $01, $01, $02, $01, $70, $89, $9f
+db $a5, $57, $a6, $95, $50, $97, $9f, $a4, $50, $a4, $9f, $9f, $50, $9d, $a5, $93
+db $98, $50, $a3, $a4, $a5, $96, $96, $51, $13, $02
+
+ORG $C8720E
+db $03
+
+ORG $CF6733
+db $0A, $00
+
+ORG $C4FEE0
+db $02, $00, $87, $04, $a9, $9e, $7d, $04, $20, $58
+
+ORG $D5F9A0
+StarstormBits:
+db $02, $04
+
+CharFlags:
+dw $03E1, $003E2, $0010, $0068, $0417
+
+ORG $CFD67D
+db $62, $00, $04, $0A, $00
+
+ORG $CFD685
+db $67, $32, $C9
+
+ORG $C5744D
+db $0A, $20, $B5, $EE
+
+ORG $C9327F
+db $0A
+
+ORG $EEB520
+db $08, $32, $b5, $ee, $ff, $1b, $03, $49, $b5, $ee, $ff, $04, $d9, $03, $0a, $84
+db $32, $c9, $1d, $03, $ff, $1b, $02, $4a, $b5, $ee, $ff, $1d, $0e, $ff, $01, $08
+db $cf, $dc, $c7, $ff, $03, $1f, $81, $ff, $ff, $02, $01, $70, $77, $95, $a4, $50
+db $a2, $99, $94, $50, $9f, $96, $50, $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97
+db $5c, $50, $96, $99, $a2, $a3, $a4, $5e, $13, $1f, $81, $01, $01, $02
+
+ORG $CFD682
+db $d9, $03, $01
+
+ORG $CF20E2
+db $00, $00, $00;Post-pyramid door script. In case I need to flag the rock later.
+
+ORG $CF672F
+db $00, $00
+
+ORG $C576C2
+db $08, $b6, $dc, $c7, $00, $1b, $05
+
+ORG $C9E510
+db $08, $cf, $dc, $c7, $00, $1b, $05
+
+ORG $C9E7A0
+dl Truffle1
+
+ORG $EEB56E
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $02
+
+ORG $C9E7B9
+dl Truffle2
+
+ORG $EEB59A
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $02
+
+ORG $C9E7D2
+dl Truffle3
+
+ORG $EEB5C6
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $02
+
+ORG $C9E7EB
+dl Truffle4
+
+ORG $EEB5F2
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $02
+
+ORG $C9E804
+dl Truffle5
+
+ORG $EEB61E
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $02
+
+ORG $C9E7AA
+db $01, $01, $01, $01, $01
+
+ORG $C9E838
+db $02
+
+ORG $EEA147
+db $0A, $4A, $B6, $EE
+
+ORG $EEB64A
+db $1f, $81, $ff, $ff, $08, $3a, $b6, $ee, $ff, $08, $62, $b6, $ee, $ff, $1b, $03
+db $3a, $b6, $ee, $ff, $0a, $52, $a1, $ee, $1c, $05, $01, $51, $03, $1d, $03, $ff
+db $1b, $02, $7f, $b6, $ee, $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03
+db $1f, $81, $ff, $ff, $02, $01, $70, $89, $9f, $a5, $57, $a6, $95, $50, $97, $9f
+db $a4, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $50, $a3, $a4, $a5, $96, $96
+db $51, $1f, $81, $01, $01, $13, $02
+
+ORG $C9E750
+db $0a, $57, $e7, $c9
+
+ORG $C9DAB2
+db $1b, $05, $1f, $02, $74, $10, $3c, $18, $01, $01, $70, $1c, $02, $01, $50, $9c
+db $16, $74, $15, $32, $9c, $95, $91, $94, $95, $a2, $15, $09, $84, $16, $74, $91
+db $16, $ef, $99, $92, $15, $15, $92, $17, $37, $50, $6c, $7f, $17, $82, $17, $93
+db $15, $1b, $83, $98, $a9, $16, $09, $5e, $6e, $03, $00, $1d, $0e, $ff, $d3
+
+ORG $EE95A6
+db $70, $0a, $71, $98, $ee
+
+ORG $EE98AD
+db $0a, $a1, $b6, $ee
+
+ORG $EEB6A1
+db $18, $01, $01, $1f, $81, $ff, $ff, $08, $b7, $b6, $ee, $ff, $1b, $03, $b6, $b6
+db $ee, $ff, $04, $da, $03, $02, $70, $58, $78, $95, $a9, $51, $10, $0f, $50, $84
+db $98, $95, $a2, $95, $57, $a3, $50, $91, $50, $1c, $05, $01, $50, $99, $9e, $50
+db $a4, $98, $95, $50, $93, $a5, $a0, $51, $59, $03, $1d, $03, $ff, $1b, $02, $f3
+db $b6, $ee, $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $1f, $81, $ff
+db $ff, $02, $01, $70, $58, $72, $a5, $a4, $5c, $10, $0a, $50, $a9, $9f, $a5, $50
+db $a7, $95, $a2, $95, $50, $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50, $a4, $9f
+db $9f, $50, $9d, $a5, $93, $98, $50, $a3, $a4, $a5, $96, $96, $5e, $59, $03, $1f
+db $81, $01, $01, $02
+
+ORG $EE987A
+db $01, $01, $01, $1f, $02, $44, $10, $60, $18, $01, $01, $01, $70
+
+ORG $EE98BD
+db $01, $70
+
+ORG $EE9876
+db $B5, $98, $EE
+
+ORG $C9DEDA
+db $0A, $25, $B7, $EE
+
+ORG $EEB725
+db $18, $01, $01, $1f, $81, $ff, $ff, $08, $3b, $b7, $ee, $ff, $1b, $03, $ad, $b7
+db $ee, $ff, $04, $a7, $01, $02, $70, $58, $78, $95, $a9, $51, $10, $0f, $50, $84
+db $98, $95, $a2, $95, $50, $99, $a3, $50, $91, $50, $1c, $05, $01, $50, $a5, $9e
+db $94, $95, $a2, $50, $a4, $98, $95, $50, $a2, $9f, $93, $9b, $51, $59, $03, $1d
+db $03, $ff, $1b, $02, $7c, $b7, $ee, $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7
+db $ff, $03, $1f, $81, $ff, $ff, $02, $01, $70, $58, $72, $a5, $a4, $5c, $10, $0a
+db $50, $a9, $9f, $a5, $50, $a7, $95, $a2, $95, $50, $93, $91, $a2, $a2, $a9, $99
+db $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $50, $a3, $a4, $a5, $96
+db $96, $5e, $59, $13, $1f, $81, $01, $01, $02
+
+ORG $C77612
+db $1c, $05, $01
+
+ORG $EEA26A
+db $0a, $ae, $b7, $ee
+
+ORG $EEB7AE
+db $01, $1f, $81, $ff, $ff, $08, $06, $bb, $ee, $ff, $1b, $03, $ff, $ff, $ff, $ff
+db $18, $04, $04, $f2, $03, $0a, $74, $a2, $ee, $70, $72, $a9, $50, $93, $9c, $95
+db $91, $a2, $99, $9e, $97, $50, $98, $99, $a3, $50, $9d, $99, $9e, $94, $5c, $10
+db $0a, $50, $1c, $02, $01, $50, $a2, $95, $9d, $95, $9d, $92, $95, $a2, $95, $94
+db $50, $a7, $98, $95, $a2, $95, $50, $98, $95, $50, $9c, $95, $96, $a4, $50, $a4
+db $98, $95, $50, $1c, $05, $01, $51, $03, $1d, $03, $ff, $1b, $02, $1E, $b8, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $b6, $dc, $c7, $ff, $03, $1f, $81, $ff, $ff, $02
+db $01, $70, $58, $79, $a4, $57, $a3, $50, $98, $95, $a2, $95, $50, $99, $9e, $50
+db $7d, $91, $97, $99, $93, $91, $9e, $a4, $5c, $10, $0a, $50, $a7, $99, $a4, $98
+db $50, $84, $a2, $91, $93, $a9, $5e, $59, $03, $04, $f1, $03, $1f, $81, $ff, $ff
+db $02
+
+ORG $C772FF
+db $0a, $51, $b8, $ee
+
+ORG $EEB851
+db $06, $f1, $03, $61, $b8, $ee, $ff, $70, $72, $a2, $15, $5d, $0a, $04, $73, $c7
+db $70, $72, $a2, $9f, $a4, $98, $95, $a2, $51, $03, $00, $70, $79, $50, $96, $9f
+db $a5, $9e, $94, $50, $a9, $9f, $a5, $a2, $50, $1c, $05, $01, $5e, $03, $00, $1d
+db $03, $ff, $1b, $02, $8d, $b8, $ee, $ff, $0a, $0F, $b8, $ee, $01, $70, $72, $a5
+db $a4, $50, $a9, $9f, $a5, $57, $a6, $95, $50, $97, $9f, $a4, $50, $a4, $9f, $9f
+db $50, $9d, $a5, $93, $98, $50, $a3, $a4, $a5, $96, $96, $51, $13
+
+ORG $EF5917
+db $02
+
+ORG $C9BC48
+db $02
+
+ORG $CFBAC3
+db $24, $C0, $EE
+
+ORG $EEB8AE
+db $06, $86, $00, $6d, $fe, $c4, $ff, $02
+
+ORG $C9B9DA
+db $02
+
+ORG $C7F348
+db $0A, $B6, $B8, $EE
+
+ORG $EEB8B6
+db $04, $a9, $00, $1f, $f1, $e5, $02, $1e, $01, $0a, $f4, $f3, $c7
+
+ORG $EEB8C3
+db $1f, $81, $ff, $ff, $08, $d6, $b8, $ee, $ff, $1b, $03, $be, $b8, $ee, $ff, $04
+db $e5, $03, $02, $01, $70, $84, $98, $99, $a3, $50, $1c, $05, $01, $5e, $03, $1d
+db $03, $ff, $1b, $02, $f7, $b8, $ee, $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7
+db $ff, $03, $1f, $81, $ff, $ff, $02, $01, $70, $7e, $9f, $a4, $50, $9e, $9f, $a7
+db $5e, $03, $00, $70, $89, $9f, $a5, $50, $91, $a2, $95, $50, $93, $91, $a2, $a2
+db $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $5e, $13, $1f
+db $81, $01, $01, $02
+
+ORG $EEB927
+db $70, $71, $50, $9d, $a9, $a3, $a4, $95, $a2, $99, $9f, $a5, $a3, $50, $9d, $91
+db $93, $98, $99, $9e, $95, $50, $a0, $91, $a2, $a4, $5e, $03, $00, $70, $84, $98
+db $95, $50, $a7, $9f, $a2, $94, $a3, $5c, $10, $0a, $50, $6C, $83, $7b, $89, $50
+db $82, $85, $7e, $7e, $75, $82, $50, $7d, $7b, $5e, $50, $61, $6E, $50, $91, $a2
+db $95, $50, $95, $9d, $92, $9c, $91, $aa, $9f, $9e, $95, $94, $50, $9f, $9e, $50
+db $a4, $98, $95, $50, $a3, $99, $94, $95, $5e, $13, $02
+
+ORG $C551D6
+db $0a, $27, $b9, $ee
+
+ORG $D56812
+db $85, $76, $7f, $50, $95, $9e, $97, $99, $9e, $95, $00
+
+ORG $D5682F
+db $01, $00
+
+ORG $D5682E
+db $04
+
+ORG $D568C8
+db $00, $00
+
+ORG $D56971
+db $7c, $95, $a4, $a4, $95, $a2, $50, $96, $9f, $a2, $50, $84, $9f, $9e, $a9, $00
+
+ORG $EEB982
+db $70, $71, $50, $9c, $95, $a4, $a4, $95, $a2, $50, $91, $94, $94, $a2, $95, $a3
+db $a3, $95, $94, $50, $a4, $9f, $50, $84, $9f, $9e, $a9, $50, $96, $a2, $9f, $9d
+db $50, $91, $50, $a3, $a4, $a5, $94, $95, $9e, $a4, $50, $91, $a4, $50, $83, $9e
+db $9f, $a7, $50, $87, $9f, $9f, $94, $5e, $13, $02
+
+ORG $C6FDC1
+db $74, $95, $91, $a2, $50, $84, $9f, $9e, $a9, $5c, $10, $0a, $50, $a7, $95, $50
+db $a3, $98, $9f, $a5, $9c, $94, $50, $97, $95, $a4, $50, $a4, $9f, $97, $95, $a4
+db $98, $95, $a2, $50, $91, $9e, $94, $50, $a0, $9c, $91, $a9, $50, $91, $9e, $50
+db $71, $a2, $93, $98, $99, $a0, $95, $9c, $91, $97, $9f, $50, $a3, $95, $95, $94
+db $50, $a3, $9f, $9d, $95, $a4, $99, $9d, $95, $5e, $03, $00, $70, $79, $50, $a4
+db $98, $99, $9e, $9b, $50, $7d, $91, $a8, $a7, $95, $9c, $9c, $50, $a3, $91, $99
+db $94, $50, $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97, $50, $91, $92, $9f, $a5
+db $a4, $50, $99, $a4, $50, $9c, $91, $a3, $a4, $50, $9e, $99, $97, $98, $a4, $5e
+db $13, $02, $7d, $a7, $91, $99, $15, $b5, $a3, $95, $15, $b2, $16, $e6, $95, $a2
+db $17, $30
+
+ORG $C5549F
+db $0a, $82, $b9, $ee
+
+ORG $C55CD7
+db $70, $71, $50, $92, $a2, $91, $a3, $a3, $50, $a0, $9f, $9c, $99, $93, $95, $9d
+db $91, $9e, $57, $a3, $50, $92, $91, $94, $97, $95, $50, $92, $95, $91, $a2, $99
+db $9e, $97, $50, $a4, $98, $95, $50, $a3, $a9, $9d, $92, $9f, $9c, $50, $9f, $96
+db $50, $a4, $98, $95, $50, $7f, $9e, $95, $a4, $a4, $50, $80, $9f, $9c, $99, $93
+db $95, $50, $74, $95, $a0, $91, $a2, $a4, $9d, $95, $9e, $a4, $5e, $13, $02
+
+ORG $D56EC6
+db $80, $9f, $9c, $99, $93, $95, $50, $92, $91, $94, $97, $95
+
+ORG $D56EE3
+db $01, $00
+
+ORG $D56F0A
+db $01, $00
+
+ORG $D56EED
+db $7d, $99, $9e, $99, $9e, $97, $50, $a0, $95, $a2, $9d, $99, $a4, $00
+
+ORG $C55D37
+db $0a, $bc, $b9, $ee
+
+ORG $EEB9BC
+db $70, $71, $50, $aa, $9f, $9e, $99, $9e, $97, $50, $a0, $95, $a2, $9d, $99, $a4
+db $50, $91, $9c, $9c, $9f, $a7, $99, $9e, $97, $50, $a4, $98, $95, $50, $93, $9f
+db $9e, $a3, $a4, $a2, $a5, $93, $a4, $99, $9f, $9e, $50, $9f, $96, $50, $91, $50
+db $9d, $99, $9e, $95, $50, $99, $9e, $50, $a4, $98, $95, $50, $74, $a5, $a3, $a4
+db $a9, $50, $74, $a5, $9e, $95, $a3, $50, $74, $95, $a3, $95, $a2, $a4, $5e, $03
+db $00, $70, $79, $a4, $50, $92, $95, $91, $a2, $a3, $50, $91, $50, $76, $9f, $a5
+db $a2, $a3, $99, $94, $95, $50, $9c, $95, $97, $99, $a3, $9c, $91, $a4, $99, $9f
+db $9e, $50, $a3, $a4, $91, $9d, $a0, $50, $9f, $96, $50, $94, $a5, $92, $99, $9f
+db $a5, $a3, $50, $9c, $95, $97, $99, $a4, $99, $9d, $91, $93, $a9, $5e, $13, $02
+
+ORG $D57220
+db $82, $95, $a0, $95, $9c, $50, $a3, $91, $9e, $94, $a7, $99, $93, $98
+
+ORG $C5601B
+db $82, $95, $a0, $95, $9c, $a3, $50, $a9, $9f, $a5, $a2, $50, $95, $9e, $95, $9d
+db $99, $95, $a3
+
+ORG $C5605E
+db $82, $95, $a0, $95, $9c, $a3, $50, $a9, $9f, $a5, $a2, $50, $95, $9e, $95, $9d
+db $99, $95, $a3
+
+ORG $D57247
+db $82, $95, $a0, $95, $9c, $50, $a3, $a5, $a0, $95, $a2, $a7, $99, $93, $98, $00
+
+ORG $D5F955
+db $08, $54, $95, $ee, $ff, $18, $04, $02
+
+ORG $C862BE
+db $50, $91, $50, $1c, $05, $01
+
+ORG $00FFD7
+db $12
+
+ORG $FFEEEE
+db $A4
+
+ORG $FFFFFF
+db $A4
+
+;db "how come inserting some random ass data into the game makes bsdiff respect my tokens" PUT THIS BACK WHEN PATCHING
+
+ORG $EEBA50
+db $70, $58, $78, $95, $a9, $51, $10, $0f, $50, $84, $98, $95, $a2, $95, $50, $99
+db $a3, $50, $91, $50, $1c, $05, $01, $50, $9f, $9e, $50, $a4, $98, $95, $50, $a3
+db $98, $95, $9c, $96, $51, $59, $03, $0a, $6e, $7e, $c8
+
+ORG $C87E4C
+db $0a, $50, $ba, $ee
+
+ORG $EEBA80
+db $a4, $16, $55, $50, $1c, $05, $01, $5e, $0a, $bc, $19, $c8
+
+ORG $C819B8
+db $0a, $80, $ba, $ee
+
+ORG $EEBA8D
+db $50, $91, $50, $1c, $05, $01, $50, $9f, $15, $7a, $0a, $94, $31, $c9
+
+ORG $C9318F
+db $0a, $8d, $ba, $ee
+
+ORG $CF2095
+db $93, $20, $cf
+
+ORG $CF61D5
+db $D1, $80;Music flag for the lilliput void
+
+ORG $CF2099
+dw $01D9
+
+
+ORG $D6FAA0
+MoveItemNames:
+PHB
+PHA
+PHX
+PHY
+XBA
+SEP #$10
+TAX
+PHX
+LDA #$0000
+CheckDataNum:
+CPX #$00
+BEQ PullData
+DEX
+CLC
+ADC #$0080
+BRA CheckDataNum
+PullData:
+REP #$10
+CLC
+TAX
+LDA #$007F
+LDY #$FF80
+MVN $FF7E
+SEP #$10
+PLX
+LDA #$0000
+CheckNameIndex:
+CPX #$00
+BEQ PullPlayerName
+DEX
+CLC
+ADC #$0030
+BRA CheckNameIndex
+PullPlayerName:
+REP #$10
+CLC
+ADC #$8000
+TAX
+LDA #$002F
+LDY #$FF50
+MVN $FF7E
+PLY
+PLX
+PLA
+PLB
+RTL
+
+FindMovData:
+LDA [$06]
+STA $0E
+JSL MoveItemNames
+JML GotMovData
+; A far copy of $C19216
+
+
+ R_Peek_Parameter_Bytes:
+rep #$31
+phd
+tdc
+adc #$ffee
+tcd
+; First things first: get the text pointer
+lda $97b8
+sta $00
+asl ; 2
+adc $00 ; 3
+asl ; 6
+asl ; 12
+adc $00 ; 13
+asl ; 26
+adc $00 ; 27
+; We now have [0x97b8] * 27 in A.
+sta $00
+; Load the text pointer from the table at 0x7E96AA into 0xE.
+tax
+lda $96aa,x
+sta $0e
+lda $96ac,x
+sta $10
+; Load amount into X. Also store into 0x02 for later use
+sty $02
+tyx
+; The memcpy routine copies words. If we have an odd number of
+; bytes, it will round down. We add 1 so that it effectively rounds up.
+inx
+lda #$97ba
+; Copy it: X bytes from [0x0e] to (A).
+jsl $c08ed2
+; Zero out upper bytes of cc_argv
+ldy $02
+tya
+lsr ; Get lower order bit in carry, and branch if 0
+lda #$0000
+bcc .aligned
+; Align Y to next word
+sep #$20
+sta $97ba,y
+rep #$31
+bra .unaligned
+.loop:
+sta $97ba,y
+iny
+.unaligned:
+iny
+.aligned:
+cpy #$0016
+bmi .loop
+.end:
+pld
+rtl
+
+RepelEnemies:
+REP #$31
+DEC
+TAY
+CMP #$000A
+BEQ CheckRepels
+CheckNormal:
+JML ExitRepelCheck
+CheckRepels:
+PHA
+LDA $9E3C
+BEQ RepelsEmpty
+PLA
+JML ExitRepelCheck_ForceFlagOn
+RepelsEmpty:
+PLA
+BRA CheckNormal
+
+ORG $C57675
+db $0a, $9b, $ba, $ee
+
+ORG $EEBA9B
+db $50, $1c, $05, $01, $0a, $79, $76, $c5
+
+ORG $C6DD98
+db $50, $70, $78, $95, $a2, $95, $57, $a3, $50, $9d, $a9, $50, $9c, $a5, $93, $9b
+db $a9, $50, $1c, $05, $01, $5e, $10, $0f, $50, $79, $50, $98, $91, $a6, $95, $50
+db $9f, $9e, $9c, $a9, $50, $a5, $a3, $95, $94, $50, $99, $a4, $50, $9f, $9e, $93
+db $95, $50, $a3, $99, $9e, $93, $95, $50, $a4, $98, $95, $50, $9c, $91, $a3, $a4
+db $50, $a7, $91, $a3, $98, $99, $9e, $97, $5e, $03, $00, $0a, $2c, $de, $c6
+
+ORG $C6E9A7
+db $1c, $05, $01, $0a, $b9, $e9, $c6
+
+ORG $C8ABB4
+db $91, $50, $1c, $05, $01, $0a, $c8, $ab, $c8
+
+ORG $C6C05F
+db $1c, $05, $01, $0a, $66, $c0, $c6
+
+ORG $C60E49
+db $70, $87, $98, $91, $a4, $50, $91, $a2, $95, $50, $a9, $9f, $a5, $50, $94, $9f
+db $99, $9e, $97, $50, $99, $9e, $50, $9d, $a9, $50, $93, $91, $a6, $95, $6f, $03
+db $00, $70, $84, $91, $9b, $95, $50, $a4, $98, $99, $a3, $50, $1c, $05, $01, $50
+db $91, $9e, $94, $50, $9c, $95, $91, $a6, $95, $5e, $03, $0a, $dc, $0f, $c6
+
+ORG $C9820F
+db $a4, $98, $95, $50, $1c, $05, $01, $5e, $0a, $26, $82, $c9
+
+ORG $EEAF08
+db $0a, $a3, $ba, $ee
+
+ORG $EEBAA3
+db $08, $4b, $ba, $ee, $ff, $08, $b8, $ba, $ee, $ff, $1b, $03, $4b, $ba, $ee, $ff
+db $0a, $1a, $af, $ee, $ff, $1c, $05, $01, $51, $03, $1d, $03, $ff, $1b, $02, $d5
+db $ba, $ee, $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $1f, $81, $ff
+db $ff, $02, $00, $70, $89, $9f, $a5, $50, $98, $91, $a6, $95, $50, $a4, $9f, $9f
+db $50, $9d, $a5, $93, $98, $50, $9a, $a5, $9e, $9b, $5e, $13, $1f, $81, $01, $01
+db $02
+
+ORG $EEBAF4
+db $01, $0a, $52, $a7, $ee
+
+ORG $EEBAFE
+db $01, $0a, $cc, $a7, $ee
+
+ORG $EEAE84
+db $01, $08, $3e, $f8, $d5, $01, $01
+
+ORG $C1F72F
+;LDA #$00FF
+
+ORG $C1F74A
+;LDA #$00FF
+
+ORG $C1F765
+;LDA #$00FF
+
+ORG $C1F780
+;LDA #$00FF
+
+;ORG $C1F72A
+;LDA #$AF10;Flavor pointer
+
+ORG $C1FE8A
+LDA #$1FB8
+
+ORG $C1FE90
+LDA #$0450
+
+ORG $C4FD70
+db $FF, $FF, $FF
+
+ORG $EEBB03
+db $18, $04, $02
+
+ORG $EEBB06
+db $18, $01, $01, $0A, $C7, $B7, $EE
+
+ORG $C7DFE9
+db $0A, $0D, $BB, $EE
+
+;Magicant stat boosts
+ORG $EEBB0D
+db $1d, $19, $02, $1b, $03, $31, $bb, $ee, $ff, $1d, $19, $03, $1b, $03, $c5, $bb
+db $ee, $ff, $1d, $19, $04, $1b, $03, $9d, $bc, $ee, $ff, $1d, $19, $05, $1b, $03
+db $a4, $bd, $ee, $ff, $18, $01, $01, $19, $10, $01, $1b, $04, $70, $1c, $02, $00
+db $08, $ea, $fb, $d6, $ff, $00, $70, $19, $10, $01, $1b, $04, $08, $67, $fc, $d6
+db $ff, $00, $70, $19, $10, $01, $1b, $04, $08, $e9, $fc, $d6, $ff, $00, $70, $19
+db $10, $01, $1b, $04, $08, $62, $fd, $d6, $ff, $00, $70, $19, $10, $01, $1b, $04
+db $08, $e1, $fd, $d6, $ff, $00, $70, $19, $10, $01, $1b, $04, $08, $5b, $fe, $d6
+db $ff, $00, $70, $19, $10, $01, $1b, $04, $08, $d6, $fe, $d6, $ff, $00, $70, $19
+db $10, $01, $1b, $04, $08, $4f, $ff, $d6, $ff, $18, $04, $1f, $41, $0e, $10, $1e
+db $18, $01, $0e, $70, $1c, $02, $01, $50, $97, $91, $99, $9e, $95, $94, $50, $62
+db $60, $60, $5c, $60, $60, $60, $50, $95, $a8, $a0, $5e, $03, $1e, $09, $01, $40
+db $d0, $03, $00, $18, $04, $10, $13, $02, $18, $01, $01, $19, $10, $01, $1b, $04
+db $70, $1c, $02, $00, $08, $ea, $fb, $d6, $ff, $00, $70, $19, $10, $02, $1b, $04
+db $08, $67, $fc, $d6, $ff, $00, $70, $19, $10, $01, $1b, $04, $08, $e9, $fc, $d6
+db $ff, $00, $70, $19, $10, $02, $1b, $04, $08, $62, $fd, $d6, $ff, $00, $70, $19
+db $10, $01, $1b, $04, $08, $e1, $fd, $d6, $ff, $00, $70, $19, $10, $02, $1b, $04
+db $08, $5b, $fe, $d6, $ff, $00, $70, $19, $10, $01, $1b, $04, $08, $d6, $fe, $d6
+db $ff, $00, $70, $19, $10, $02, $1b, $04, $08, $4f, $ff, $d6, $ff, $18, $04, $1f
+db $41, $0e, $10, $1e, $18, $01, $0e, $70, $1c, $02, $01, $50, $91, $9e, $94, $50
+db $19, $10, $02, $1b, $04, $1c, $02, $00, $50, $97, $91, $99, $9e, $95, $94, $50
+db $61, $60, $60, $5c, $60, $60, $60, $50, $95, $a8, $a0, $5e, $03, $1e, $09, $01
+db $a0, $86, $01, $00, $19, $10, $02, $1b, $04, $09, $04, $ff, $ff, $ff, $ff, $79
+db $bc, $ee, $22, $85, $bc, $ee, $33, $91, $bc, $ee, $44, $02, $1e, $09, $02, $a0
+db $86, $01, $00, $18, $04, $10, $13, $02, $1e, $09, $03, $a0, $86, $01, $00, $18
+db $04, $10, $13, $02, $1e, $09, $04, $a0, $86, $01, $00, $18, $04, $10, $13, $02
+db $18, $01, $01, $19, $10, $01, $1b, $04, $70, $1c, $02, $00, $08, $ea, $fb, $d6
+db $ff, $00, $70, $19, $10, $02, $1b, $04, $08, $67, $fc, $d6, $ff, $00, $70, $19
+db $10, $03, $1b, $04, $08, $e9, $fc, $d6, $ff, $00, $70, $19, $10, $01, $1b, $04
+db $08, $62, $fd, $d6, $ff, $00, $70, $19, $10, $02, $1b, $04, $08, $e1, $fd, $d6
+db $ff, $00, $70, $19, $10, $03, $1b, $04, $08, $5b, $fe, $d6, $ff, $00, $70, $19
+db $10, $01, $1b, $04, $08, $d6, $fe, $d6, $ff, $00, $70, $19, $10, $02, $1b, $04
+db $08, $4f, $ff, $d6, $ff, $18, $04, $1f, $41, $0e, $10, $1e, $18, $01, $0e, $70
+db $75, $a6, $95, $a2, $a9, $9f, $9e, $95, $50, $97, $91, $99, $9e, $95, $94, $50
+db $66, $66, $5c, $60, $60, $60, $50, $95, $a8, $a0, $5e, $03, $1e, $09, $01, $d0
+db $01, $01, $00, $19, $10, $02, $01, $01, $09, $04, $ff, $ff, $ff, $ff, $47, $bd
+db $ee, $11, $53, $bd, $ee, $22, $5f, $bd, $ee, $33, $1e, $09, $02, $d0, $01, $01
+db $00, $0a, $6b, $bd, $ee, $ff, $1e, $09, $03, $d0, $01, $01, $00, $0a, $6b, $bd
+db $ee, $ff, $1e, $09, $04, $d0, $01, $01, $00, $0a, $6b, $bd, $ee, $ff, $19, $10
+db $03, $09, $04, $ff, $ff, $ff, $ff, $80, $bd, $ee, $22, $8c, $bd, $ee, $33, $98
+db $bd, $ee, $44, $1e, $09, $02, $d0, $01, $01, $00, $18, $04, $10, $13, $02, $1e
+db $09, $03, $d0, $01, $01, $00, $18, $04, $10, $13, $02, $1e, $09, $04, $d0, $01
+db $01, $00, $18, $04, $10, $13, $02, $18, $01, $01, $70, $1c, $02, $01, $08, $ea
+db $fb, $d6, $ff, $00, $70, $19, $10, $02, $1b, $04, $08, $67, $fc, $d6, $ff, $00
+db $70, $19, $10, $03, $1b, $04, $08, $e9, $fc, $d6, $ff, $00, $70, $19, $10, $04
+db $1b, $04, $08, $62, $fd, $d6, $ff, $00, $70, $19, $10, $01, $1b, $04, $08, $e1
+db $fd, $d6, $ff, $00, $70, $19, $10, $02, $1b, $04, $08, $5b, $fe, $d6, $ff, $00
+db $70, $19, $10, $03, $1b, $04, $08, $d6, $fe, $d6, $ff, $00, $70, $19, $10, $04
+db $1b, $04, $08, $4f, $ff, $d6, $ff, $18, $04, $1f, $41, $0e, $10, $1e, $18, $01
+db $0e, $70, $75, $a6, $95, $a2, $a9, $9f, $9e, $95, $50, $97, $91, $99, $9e, $95
+db $94, $50, $65, $60, $5c, $60, $60, $60, $50, $95, $a8, $a0, $5e, $03, $1e, $09
+db $01, $50, $c3, $00, $00, $1e, $09, $02, $50, $c3, $00, $00, $1e, $09, $03, $50
+db $c3, $00, $00, $1e, $09, $04, $50, $c3, $00, $00, $00, $18, $04, $10, $13, $02
+
+ORG $D6FBEA
+db $50, $a7, $91, $a3, $50, $96, $99, $9c, $9c, $95, $94, $50, $a7, $99, $a4, $98
+db $50, $a4, $98, $95, $50, $80, $9f, $a7, $95, $a2, $50, $9f, $96, $50, $a4, $98
+db $95, $50, $77, $99, $91, $9e, $a4, $50, $83, $a4, $95, $a0, $51, $03, $00, $70
+db $1c, $02, $00, $57, $a3, $50, $83, $a0, $95, $95, $94, $50, $99, $9e, $93, $a2
+db $95, $91, $a3, $95, $94, $50, $92, $a9, $50, $65, $51, $1f, $02, $28, $1e, $0c
+db $00, $05, $03, $00, $70, $1c, $02, $00, $57, $a3, $50, $86, $99, $a4, $91, $9c
+db $99, $a4, $a9, $50, $99, $9e, $93, $a2, $95, $91, $a3, $95, $94, $50, $92, $a9
+db $50, $65, $51, $1f, $02, $28, $1e, $0d, $00, $05, $03, $02, $02, $1c, $02, $00
+db $50, $a7, $91, $a3, $50, $96, $99, $9c, $9c, $95, $94, $50, $a7, $99, $a4, $98
+db $50, $a4, $98, $95, $50, $80, $9f, $a7, $95, $a2, $50, $9f, $96, $50, $a4, $98
+db $95, $50, $7c, $99, $9c, $9c, $99, $a0, $a5, $a4, $50, $83, $a4, $95, $a0, $a3
+db $51, $03, $00, $70, $1c, $02, $00, $57, $a3, $50, $77, $a5, $a4, $a3, $50, $99
+db $9e, $93, $a2, $95, $91, $a3, $95, $94, $50, $92, $a9, $50, $65, $51, $1f, $02
+db $28, $1e, $0b, $00, $05, $03, $00, $70, $1c, $02, $00, $57, $a3, $50, $86, $99
+db $a4, $91, $9c, $99, $a4, $a9, $50, $99, $9e, $93, $a2, $95, $91, $a3, $95, $94
+db $50, $92, $a9, $50, $65, $51, $1f, $02, $28, $1e, $0d, $00, $05, $03, $02, $1c
+db $02, $00, $50, $a7, $91, $a3, $50, $96, $99, $9c, $9c, $95, $94, $50, $a7, $99
+db $a4, $98, $50, $a4, $98, $95, $50, $80, $9f, $a7, $95, $a2, $50, $9f, $96, $50
+db $a4, $98, $95, $50, $7d, $99, $9c, $9b, $a9, $50, $87, $95, $9c, $9c, $51, $03
+db $00, $70, $1c, $02, $00, $57, $a3, $50, $83, $a0, $95, $95, $94, $50, $99, $9e
+db $93, $a2, $95, $91, $a3, $95, $94, $50, $92, $a9, $50, $65, $51, $1f, $02, $28
+db $1e, $0c, $00, $05, $03, $00, $70, $1c, $02, $00, $57, $a3, $50, $79, $81, $50
+db $99, $9e, $93, $a2, $95, $91, $a3, $95, $94, $50, $92, $a9, $50, $65, $51, $1f
+db $02, $28, $1e, $0a, $00, $05, $03, $02, $1c, $02, $00, $50, $a7, $91, $a3, $50
+db $96, $99, $9c, $9c, $95, $94, $50, $a7, $99, $a4, $98, $50, $a4, $98, $95, $50
+db $80, $9f, $a7, $95, $a2, $50, $9f, $96, $50, $a4, $98, $95, $50, $7d, $91, $97
+db $9e, $95, $a4, $50, $78, $99, $9c, $9c, $51, $03, $00, $70, $1c, $02, $00, $57
+db $a3, $50, $86, $99, $a4, $91, $9c, $99, $a4, $a9, $50, $99, $9e, $93, $a2, $95
+db $91, $a3, $95, $94, $50, $92, $a9, $50, $65, $51, $1f, $02, $28, $1e, $0d, $00
+db $05, $03, $00, $70, $1c, $02, $00, $57, $a3, $50, $7c, $a5, $93, $9b, $50, $99
+db $9e, $93, $a2, $95, $91, $a3, $95, $94, $50, $92, $a9, $50, $65, $51, $1f, $02
+db $28, $1e, $0e, $00, $05, $03, $02, $1c, $02, $00, $50, $a7, $91, $a3, $50, $96
+db $99, $9c, $9c, $95, $94, $50, $a7, $99, $a4, $98, $50, $a4, $98, $95, $50, $80
+db $9f, $a7, $95, $a2, $50, $9f, $96, $50, $a4, $98, $95, $50, $82, $91, $99, $9e
+db $a9, $50, $73, $99, $a2, $93, $9c, $95, $51, $03, $00, $70, $1c, $02, $00, $57
+db $a3, $50, $77, $a5, $a4, $a3, $50, $99, $9e, $93, $a2, $95, $91, $a3, $95, $94
+db $50, $92, $a9, $50, $65, $51, $1f, $02, $28, $1e, $0b, $00, $05, $03, $00, $70
+db $1c, $02, $00, $57, $a3, $50, $79, $81, $50, $99, $9e, $93, $a2, $95, $91, $a3
+db $95, $94, $50, $92, $a9, $50, $65, $51, $1f, $02, $28, $1e, $0a, $00, $05, $03
+db $02, $1c, $02, $00, $50, $a7, $91, $a3, $50, $96, $99, $9c, $9c, $95, $94, $50
+db $a7, $99, $a4, $98, $50, $a4, $98, $95, $50, $80, $9f, $a7, $95, $a2, $50, $9f
+db $96, $50, $a4, $98, $95, $50, $80, $99, $9e, $9b, $50, $73, $9c, $9f, $a5, $94
+db $51, $03, $00, $70, $1c, $02, $00, $57, $a3, $50, $83, $a0, $95, $95, $94, $50
+db $99, $9e, $93, $a2, $95, $91, $a3, $95, $94, $50, $92, $a9, $50, $65, $51, $1f
+db $02, $28, $1e, $0c, $00, $05, $03, $00, $70, $1c, $02, $00, $57, $a3, $50, $77
+db $a5, $a4, $a3, $50, $99, $9e, $93, $a2, $95, $91, $a3, $95, $94, $50, $92, $a9
+db $50, $65, $51, $1f, $02, $28, $1e, $0b, $00, $05, $03, $02, $1c, $02, $00, $50
+db $a7, $91, $a3, $50, $96, $99, $9c, $9c, $95, $94, $50, $a7, $99, $a4, $98, $50
+db $a4, $98, $95, $50, $80, $9f, $a7, $95, $a2, $50, $9f, $96, $50, $a4, $98, $95
+db $50, $7c, $a5, $9d, $99, $9e, $95, $50, $78, $91, $9c, $9c, $51, $03, $00, $70
+db $1c, $02, $00, $57, $a3, $50, $7c, $a5, $93, $9b, $50, $99, $9e, $93, $a2, $95
+db $91, $a3, $95, $94, $50, $92, $a9, $50, $65, $51, $1f, $02, $28, $1e, $0e, $00
+db $05, $03, $00, $70, $1c, $02, $00, $57, $a3, $50, $79, $81, $50, $99, $9e, $93
+db $a2, $95, $91, $a3, $95, $94, $50, $92, $a9, $50, $65, $51, $1f, $02, $28, $1e
+db $0a, $00, $05, $03, $02, $1c, $02, $00, $50, $a7, $91, $a3, $50, $96, $99, $9c
+db $9c, $95, $94, $50, $a7, $99, $a4, $98, $50, $a4, $98, $95, $50, $80, $9f, $a7
+db $95, $a2, $50, $9f, $96, $50, $a4, $98, $95, $50, $76, $99, $a2, $95, $50, $83
+db $a0, $a2, $99, $9e, $97, $51, $03, $00, $70, $1c, $02, $00, $57, $a3, $50, $83
+db $a0, $95, $95, $94, $50, $99, $9e, $93, $a2, $95, $91, $a3, $95, $94, $50, $92
+db $a9, $50, $65, $51, $1f, $02, $28, $1e, $0c, $00, $05, $03, $00, $70, $1c, $02
+db $00, $57, $a3, $50, $7c, $a5, $93, $9b, $50, $99, $9e, $93, $a2, $95, $91, $a3
+db $95, $94, $50, $92, $a9, $50, $65, $51, $1f, $02, $28, $1e, $0e, $00, $05, $03
+db $02
+
+ORG $EEBE58
+db $1f, $02, $74, $10, $3c, $18, $04, $1f, $02, $66, $10, $64, $18, $01, $01, $18
+db $01, $01, $70, $58, $84, $95, $9e, $94, $91, $50, $73, $98, $99, $95, $96, $50
+db $97, $9f, $a4, $50, $a4, $98, $95, $50, $0A, $6A, $C6, $EE, $59, $03, $0a, $ed
+db $da, $c9
+
+ORG $C9DAB4
+db $0A, $58, $BE, $EE
+
+ORG $EEA9AF
+db $0A, $9A, $BE, $EE
+
+ORG $EEBE8A
+db $50, $99, $9e, $50, $a4, $98, $95, $50, $a3, $a4, $91, $a4, $a5, $95, $57, $a3
+db $50, $95, $a9, $95, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $c0, $a9, $ee, $ff
+db $0a, $b6, $a9, $ee
+
+ORG $EEBEAE
+db $1d, $03, $ff, $1b, $02, $25, $eb, $c7, $ff, $0a, $00, $ea, $c7, $1d, $03, $ff
+db $1b, $02, $25, $eb, $c7, $ff, $0a, $00, $ea, $c7
+
+ORG $C6053B
+db $91, $50, $1c, $05, $01, $50, $0a, $45, $05, $c6
+
+ORG $C9E3E7
+db $0A, $C8, $BE, $EE
+
+ORG $EEBEC8
+db $15, $6c, $6f, $51, $03, $00, $70, $79, $57, $9c, $9c, $50, $97, $99, $a6, $95
+db $50, $a9, $9f, $a5, $50, $9d, $a9, $50, $1c, $05, $01, $51, $01, $0a, $ec, $e3
+db $c9
+
+ORG $C7EA80
+db $0A, $2F, $C0, $EE
+;fix
+ORG $EEC02F
+db $06, $ad, $00, $ff, $ff, $ff, $ff, $06, $ac, $00, $51, $C0, $ee, $ff, $06, $ab
+db $00, $5E, $C0, $ee, $ff, $1d, $03, $ff, $1b, $02, $25, $eb, $c7, $ff, $0a, $89
+db $ea, $c7, $1d, $03, $ff, $1b, $02, $25, $eb, $c7, $ff, $0a, $89, $ea, $c7, $1d
+db $03, $ff, $1b, $02, $25, $eb, $c7, $ff, $0a, $89, $ea, $c7
+
+ORG $EEAFB4
+db $0A, $04, $BF, $EE
+
+ORG $EEAFBC
+db $01
+
+ORG $EEBF04
+db $1d, $03, $ff, $1b, $02, $bc, $af, $ee, $ff, $0a, $c2, $af, $ee
+
+ORG $EEBF11
+db $06, $2C, $01, $81, $bf, $ee, $ff, $18, $01, $01, $70, $58, $87, $98, $9f, $91
+db $98, $51, $10, $0f, $50, $84, $98, $95, $a2, $95, $57, $a3, $50, $91, $50, $aa
+db $9f, $9d, $92, $99, $95, $50, $94, $9f, $99, $9e, $97, $50, $99, $a3, $9f, $9d
+db $95, $a4, $a2, $99, $93, $50, $95, $a8, $95, $a2, $93, $99, $a3, $95, $a3, $50
+db $9f, $9e, $50, $a4, $98, $95, $50, $95, $9e, $a4, $a2, $91, $9e, $93, $95, $51
+db $03, $00, $70, $72, $95, $a4, $a4, $95, $a2, $50, $93, $9f, $9d, $95, $50, $92
+db $91, $93, $9b, $50, $9c, $91, $a4, $95, $a2, $5e, $5e, $5e, $59, $13, $18, $04
+db $02
+
+ORG $CF0CF8
+db $11, $bf, $ee, $ff, $2C, $81
+
+ORG $C887F5
+db $00, $00, $00
+
+ORG $C88840
+db $00, $00, $00
+
+ORG $EEB53B
+db $0A, $9C, $BF, $EE
+
+ORG $EEB498
+db $0A, $82, $BF, $EE
+
+ORG $EEBF82
+db $98, $99, $a3, $50, $1c, $05, $01, $5e, $03, $0a, $9c, $b4, $ee
+
+ORG $EEB432
+db $0A, $8F, $BF, $EE
+
+ORG $EEBF8F
+db $98, $99, $a3, $50, $1c, $05, $01, $51, $03, $0a, $37, $b4, $ee
+
+ORG $EEBF9C
+db $01, $70, $79, $a4, $57, $a3, $50, $91, $50, $1c, $05, $01, $5e, $03, $1d, $0e
+db $ff, $01, $0a, $3f, $b5, $ee
+
+ORG $EEB37E
+db $0A, $B2, $BF, $EE
+
+ORG $EEBFB2
+db $1c, $05, $01, $50, $91, $a3, $50, $91, $0a, $82, $b3, $ee
+
+ORG $EEB082
+db $0A, $BE, $BF, $EE
+
+ORG $EEBFBE
+db $01, $01, $01, $0a, $40, $a7, $ee
+
+ORG $EEB09C
+db $0A, $C5, $BF, $EE
+
+ORG $EEBFC5
+db $01, $01, $01, $0a, $40, $a7, $ee
+
+ORG $EEB0B6
+db $0A, $CC, $BF, $EE
+
+ORG $EEBFCC
+db $01, $01, $01, $0a, $40, $a7, $ee
+
+ORG $EEB0D0
+db $0A, $D3, $BF, $EE
+
+ORG $EEBFD3
+db $01, $01, $01, $0a, $40, $a7, $ee
+
+ORG $EEB26F
+db $0A, $DA, $BF, $EE
+
+ORG $EEBFDA
+db $18, $01, $01, $70, $58, $79, $a4, $57, $a3, $50, $91, $50, $1c, $05, $01, $5e
+db $59, $03, $1d, $0e, $ff, $01, $0a, $76, $b2, $ee
+
+ORG $EFFB66
+GetStartingNames:
+PHB
+PHX
+PHY
+LDA $D7FCD0
+STA $9831
+LDA $D7FCD2
+STA $9833
+LDA #$0017
+LDX #$FCE0
+LDY #$9801
+MVN $D77E
+PLY
+PLX
+PLB
+JML GiveTeddyOnLoad
+
+ResetStatus:
+STZ $9A3B
+STZ $9A9A
+STZ $9AF9
+JML $C4C7D4
+
+ResetGame:
+JSL $C087CE
+ResetRoutine:
+STZ $F640
+ResetTimer:
+INC $F640
+LDA $F640
+CMP #$FFFF
+BEQ EndReset
+BRA ResetTimer
+EndReset:
+INC $F642
+LDA $F642
+CMP #$0005
+BEQ ReallyReset
+BRA ResetRoutine
+ReallyReset:
+REP #$20
+LDA #$0000
+TCD
+LDA #$01FF
+TCS
+LDA #$0000
+LDX #$0000
+LDY #$0000
+CLC
+XCE
+SEP #$35
+REP #$CA
+JML $C08000
+
+ClearStorageMenu:
+JSL $EF0115
+LDA $8958
+AND #$00FF
+CMP #$000D
+BNE EndStorageCheck2
+PHY
+JSL $C3E7E3
+JSL $C1DD53
+PLY
+EndStorageCheck2:
+RTL
+
+AutoText:
+LDA $0069
+AND #$4000;Is Y held?
+BEQ .HoldY
+RTL
+.HoldY:
+LDA $006D
+AND #$E0A0
+RTL
+
+LostScriptedFight:
+STZ $5D98
+LDA #$0001
+PLD
+RTL
+
+ClearNameOnBattle:
+LDA #$0001
+STA $5D98
+STZ $B573
+RTL
+;;;;;;;;;;;;;;;;
+;Franklin badge stuff
+ReflectSpecial:
+LDX #$00FF
+LDA $12
+PHA
+PHX
+JSR CheckFranklinBadge
+CMP #$0000
+BEQ .Reflect
+JSR ReflectAttack
+.Reflect:
+PLX
+PLA
+JML $C2954E
+
+ReflectFire:
+LDA $0E
+PHA
+LDA $0F
+PHA
+JSR CheckFranklinBadge
+CMP #$0000
+BEQ .Reflect
+JSR ReflectAttack
+.Reflect:
+PLA
+STA $0F
+PLA
+STA $0E
+LDX $A972
+LDA $003A,X
+JML $C2959D
+
+ReflectFreeze:
+LDA $0E
+PHA
+LDA $0F
+PHA
+JSR CheckFranklinBadge
+CMP #$0000
+BEQ .Reflect
+JSR ReflectAttack
+.Reflect:
+PLA
+STA $0F
+PLA
+STA $0E
+LDX $A972
+LDA $0038,X
+JML $C295FA
+
+ReflectFireBall:
+LDA $0E
+PHA
+LDA $0F
+PHA
+JSR CheckFranklinBadge
+CMP #$0000
+BEQ .Reflect
+JSR ReflectAttack
+.Reflect:
+PLA
+STA $0F
+PLA
+STA $0E
+LDX $A972
+LDA $003A,X
+AND #$00FF
+JML $C29024
+
+ReflectFlash:
+LDA $0E
+PHA
+LDA $0F
+PHA
+JSR CheckFranklinBadge
+CMP #$0000
+BEQ .Reflect
+LDA $B58A
+AND #$00FF
+BNE ReflectPrayClear
+JSR ReflectAttack
+.Reflect:
+ReflectFlash.Reflect:
+PLA
+STA $0F
+PLA
+STA $0E
+LDX $A972
+SEP #$20
+JML $C298BB
+ReflectPrayClear:
+STZ $B58A
+BRA ReflectFlash.Reflect
+
+ReflectStarstorm:
+CMP #$0000
+BNE StarstormEnd
+LDA $0E
+PHA
+LDA $0F
+PHA
+JSR CheckFranklinBadge
+CMP #$0000
+BEQ .Reflect
+JSR ReflectAttack
+.Reflect:
+PLA
+STA $0F
+PLA
+STA $0E
+JML $C29A95
+StarstormEnd:
+JML $C29AA4
+
+ReflectExplode:
+PHX
+LDA $0E
+PHA
+LDA $0F
+PHA
+JSR CheckFranklinBadge
+CMP #$0000
+BEQ .Reflect
+JSR ReflectAttack
+SEP #$20
+LDA #$02
+STA $AA96
+REP #$20
+.Reflect:
+PLA
+STA $0F
+PLA
+STA $0E
+LDA #$0000
+STA $04
+PLX
+JML $C2A66B
+
+CheckFranklinBadge:
+LDX $A972
+REP #$20
+LDA $000E,X
+AND #$00FF
+BNE NotPlayerChar
+LDX #$0001
+STX $16
+LDX $A972
+LDA $0010,X
+AND #$00FF
+INC
+LDX $16
+JSL $C45683
+RTS
+NotPlayerChar:
+LDA #$0000
+RTS
+
+ReflectAttack:
+;LDA $AA96
+;AND #$00FF
+;BNE SkipReflect
+LDA #$7160
+STA $0E
+LDA #$00EF
+STA $10
+JSL $C1DC1C
+LDA #$0001
+STA $AA96
+JSL DealReflect
+RTS
+SkipReflect:
+STZ $AA96
+SkipSuperreflect:
+RTS
+
+SkipExplosionDeathReflect:
+LDA $AA90; Is death attack?
+AND #$00FF
+BEQ .ForceExplode
+LDA $AA96
+AND #$00FF
+CMP #$0002
+BEQ .SkipExplode; Is already reflected
+.ForceExplode
+LDA $1A
+JML ExplodeReturn
+.SkipExplode:
+JML $C2A6A9
+;;;;;;;;;;;;;;;;;
+CopyAPData:
+JSL $EF0C15
+PHA
+PHX
+PHB
+LDY #$0000
+LDX $16
+.CheckCurFile:
+LDA #$7E00
+.CheckZero:
+CPX #$0000
+BEQ .GotSaveDest
+CLC
+ADC #$0010
+DEX
+BRA .CheckZero
+.GotSaveDest
+CPY #$0001
+BEQ .PerformCopy
+PHA
+LDA $B4A1
+AND #$00FF
+TAX
+INY
+BRA .CheckCurFile
+.PerformCopy:
+TAX
+PLA
+TAY
+LDA #$000F
+MVN $2020
+PLB
+PLX
+PLA
+RTL
+
+DeleteAPData:
+JSL $EF0BFA
+LDA $B4A1
+AND #$00FF
+TAX
+LDA #$7E00
+.CheckSave:
+CPX #$0000
+BEQ .GotSave
+CLC
+ADC #$0010
+DEX
+BRA .CheckSave
+.GotSave:
+PHX
+PHY
+PHB
+TAY
+LDX #$FF70
+LDA #$000F
+MVN $FF20
+PLB
+PLY
+PLX
+RTL
+
+PlayerJustDied:
+SEP #$20
+LDA $B582; If the player just died in battle, dont send any more deaths
+BNE SkipSendingDeath
+;In this case, we died on the overworld
+INC $B582;Player is currently dead
+LDA $B583;Did the player just get killed by a deathlink death?
+BNE SkipSendingDeath
+INC $B584;Tell the serveer we died
+SkipSendingDeath:
+STZ $B583
+REP #$20
+JSL $C4C718
+RTL
+
+CheckDeathHealing:
+PHX
+PHY
+LDX $A972
+LDA $000E,X
+AND #$00FF
+BNE NotHealPlayer;1 if enemy
+LDA $B583;Deathlink from server- if mortal mode, this will always return 1 if the death was from a link
+AND #$00FF
+BEQ ResetDeathLinkHeal;Mortal no mercy mode will put this here; else, it will be a BRA
+PLY
+PLX
+STY $16
+TYX
+LDA $02
+JML $C272D0
+NotHealPlayer:
+PLY
+PLX
+STY $16
+TYX
+LDA $02
+JML $C272CD
+ResetDeathLinkHeal:
+SEP #$20
+STZ $B583;This means that the player healed, and the death is not solely from the deathlink anymore
+REP #$20
+BRA NotHealPlayer
+
+OverrideDeathlinkText:
+LDX $A972
+LDA $000E,X
+AND #$00FF
+BNE .NotPlayer
+LDA $B583;
+AND #$0FF
+BEQ .NotPlayer;If the player is not currently in a deathlink, act normal. This will be a BRA if mercy mode is ON
+LDA #$CD0C
+STA $0E
+LDA #$00EE
+STA $10
+JML $C272E4
+.NotPlayer:
+LDX $02
+LDY $16
+JML $C272D4
+
+OverrideDeathlinkHeal:
+LDX $A972
+LDA $000E,X
+AND #$00FF
+BNE NotPlayerForRevive
+LDA $B583
+AND #$00FF
+BEQ ReviveClearDeathlinkFlag;Change to BRA if mercy mode ON
+LDA #$CD0C
+STA $0E
+LDA #$00EE
+STA $10
+JSL $C1DC1C
+JML $C2754B
+NotPlayerForRevive:
+JSL $C1DC1C
+JML $C273B4
+ReviveClearDeathlinkFlag:
+SEP #$20
+STZ $B583
+REP #$20
+BRA NotPlayerForRevive
+
+SendDeathInBattle:
+SEP #$20
+INC $B582;Player is currently dead
+LDA $B583;Did the player just get killed by a deathlink death?
+BNE .SkipSendingDeath
+INC $B584;Is the player sending a death
+.SkipSendingDeath:
+STZ $B583
+REP #$20
+JSL $C1DC1C
+RTL
+
+ClearStoredAPItem:
+LDA $06
+CMP #$EA7F
+BNE SkipAPCheck
+LDA $08
+AND #$00FF
+CMP #$00C7
+BNE SkipAPCheck
+STZ $B573
+STZ $B579
+SkipAPCheck:
+LDX #$0000
+LDA #$0001
+JML $C18A23
+
+PrayerReflectIgnore:
+LDA #$0001
+STA $B58A
+LDA $C4A2F9,X
+JML $C2AD2E
+
+ORG $C2FFE0
+DealReflect:
+JSR $7E8A
+RTL
+
+ExplodeReturn:
+JSR $6A44
+JML $C2A670
+;;;;;;;;;;;;;;;;;;;;
+;old code
+
+
+
+
+
+
+;;;;;;;;;;;;;;;;
+ORG $C7DE7E
+db $F4, $BF, $EE
+
+ORG $EEBFF4
+db $08, $11, $de, $c7, $00, $02, $7B, $C6, $EE, $02
+
+
+;If easy death:
+
+ORG $EEBFF9
+;db $1E
+
+ORG $C4C7CE
+;JML ResetStatus
+
+ORG $C4C7D4
+;NOP
+;NOP
+;NOP
+
+ORG $C912F2
+;db $0A, $FE, $BF, $EE
+
+ORG $EEBFFE
+db $1b, $04, $15, $38, $1f, $81, $ff, $ff, $1b, $04, $0a, $f7, $12, $c9
+
+
+org $D8D35A
+dw new_collision
+
+org $D8D354
+dw new_collision
+
+org $D8FFF0
+new_collision:
+db $80, $80, $80, $80, $80, $80, $80, $80, $80, $80, $80, $82, $00, $00, $00, $00
+
+ORG $C79609
+db $0A, $0D, $C0, $EE
+
+ORG $EEC00D
+db $50, $99, $9d, $91, $1f, $81, $ff, $ff, $0a, $0D, $96, $C7
+
+ORG $D5F970
+db $1F, $81, $FF, $FF, $0A, $E0, $F8, $D5
+
+ORG $C79640
+db $0A, $19, $C0, $EE
+
+ORG $EEC019
+db $a4, $98, $99, $a3, $50, $0a, $44, $96, $c7
+
+ORG $C8A2D1
+db $00, $00, $00; summers phonecall oss
+
+ORG $EEC022
+db $96, $c7, $06, $86, $00, $6d, $fe, $c4, $ff, $0a, $9e, $c5, $c7
+
+ORG $C9C27A
+db $02; Future past apple kid. Eventually change with seed-related text
+
+ORG $CFDE16
+db $00;Broken Phase distorter not appearing
+
+ORG $D56F7C
+db $00
+
+ORG $C7EA03
+db $49
+
+ORG $D57775
+db $8C
+
+ORG $C95BA7
+db $0A, $70, $C0, $EE
+
+ORG $EEC070
+db $06, $77, $00, $1e, $c1, $ee, $ff, $70, $89, $95, $a0, $5e, $50, $10, $0f, $84
+db $98, $95, $50, $82, $a5, $9e, $91, $a7, $91, $a9, $50, $76, $99, $a6, $95, $50
+db $9f, $a7, $95, $50, $9d, $95, $50, $91, $50, $9c, $9f, $a4, $50, $9f, $96, $50
+db $9d, $9f, $9e, $95, $a9, $5e, $03, $00, $70, $84, $98, $95, $a9, $57, $9c, $9c
+db $50, $a0, $a2, $9f, $92, $91, $92, $9c, $a9, $50, $92, $95, $50, $98, $95, $a2
+db $95, $50, $9c, $9f, $9e, $97, $95, $a2, $50, $a4, $98, $91, $9e, $50, $79, $57
+db $a6, $95, $50, $92, $95, $95, $9e, $50, $99, $9e, $50, $72, $7b, $5c, $10, $0a
+db $50, $a5, $9e, $9c, $95, $a3, $a3, $50, $a9, $9f, $a5, $50, $a7, $91, $9e, $a4
+db $50, $a4, $9f, $50, $a0, $91, $a9, $50, $9f, $96, $96, $50, $a4, $98, $95, $99
+db $a2, $50, $94, $95, $92, $a4, $5e, $5e, $5e, $50, $78, $95, $98, $5c, $10, $0a
+db $50, $98, $95, $98, $5c, $10, $0a, $50, $98, $95, $98, $5e, $13, $02, $70, $83
+db $98, $9f, $9f, $a4, $5e, $03, $00, $70, $79, $57, $9d, $50, $a3, $a4, $99, $9c
+db $9c, $50, $99, $9e, $50, $72, $7b, $5c, $10, $0a, $50, $a4, $9f, $9f, $5e, $13
+db $02; Poochyfud text
+
+ORG $C9C262
+db $0a, $41, $c1, $ee
+
+ORG $EEC141
+db $70, $79, $50, $a2, $95, $91, $94, $50, $a3, $9f, $9d, $95, $a7, $98, $95, $a2
+db $95, $5c, $10, $0a, $50, $a4, $98, $91, $a4, $50, $a9, $9f, $a5, $50, $93, $91
+db $9e, $50, $08, $b2, $f0, $ee, $ff, $0a, $98, $c1, $ee, $95, $50, $a0, $9f, $a7
+db $95, $a2, $50, $9f, $96, $50, $a4, $98, $95, $50, $75, $91, $a2, $a4, $98, $50
+db $99, $a4, $a3, $95, $9c, $96, $50, $99, $9e, $a4, $9f, $50, $a9, $9f, $a5, $a2
+db $50, $98, $95, $91, $a2, $a4, $5c, $03, $00, $70, $92, $a9, $50, $a0, $a5, $a2
+db $99, $96, $a9, $99, $9e, $97, $50, $1c, $0a, $01, $00, $00, $00, $50, $9f, $96
+db $50, $a4, $98, $95, $50, $6C, $10, $06, $89, $9f, $a5, $a2, $50, $83, $91, $9e
+db $93, $a4, $a5, $91, $a2, $a9, $6E, $10, $06, $50, $9c, $9f, $93, $91, $a4, $99
+db $9f, $9e, $a3, $5c, $03, $00, $08, $40, $c1, $ee, $ff, $08, $40, $c1, $ee, $ff
+db $08, $40, $c1, $ee, $ff, $70, $a7, $98, $91, $a4, $95, $a6, $95, $a2, $50, $a4
+db $98, $91, $a4, $50, $9d, $95, $91, $9e, $a3, $5e, $13, $02, $70, $9f, $a2, $50
+db $a4, $98, $91, $a4, $50, $a9, $9f, $a5, $50, $93, $91, $9e, $50, $a3, $91, $a6
+db $95, $50, $a4, $98, $95, $50, $a7, $9f, $a2, $9c, $94, $50, $92, $a9, $50, $a0
+db $a5, $a2, $99, $96, $a9, $99, $9e, $97, $50, $62, $50, $9d, $9f, $a2, $95, $5c
+db $03, $02, $70, $91, $9e, $94, $50, $a4, $98, $91, $a4, $50, $a9, $9f, $a5, $50
+db $9e, $95, $95, $94, $50, $a4, $9f, $50, $a0, $a5, $a2, $97, $95, $50, $a4, $98
+db $95, $50, $95, $a6, $99, $9c, $50, $96, $a2, $9f, $9d, $50, $a9, $9f, $a5, $a2
+db $50, $9d, $99, $9e, $94, $5c, $03, $01, $02, $70, $9f, $a2, $50, $a4, $98, $91
+db $a4, $50, $a9, $9f, $a5, $50, $93, $91, $9e, $50, $a3, $91, $a6, $95, $50, $a4
+db $98, $95, $50, $a7, $9f, $a2, $9c, $94, $50, $92, $a9, $50, $a0, $a5, $a2, $99
+db $96, $a9, $99, $9e, $97, $50, $61, $50, $9d, $9f, $a2, $95, $50, $91, $9e, $94
+db $50, $a0, $a5, $a2, $97, $99, $9e, $97, $50, $a9, $9f, $a5, $a2, $50, $9d, $99
+db $9e, $94, $57, $a3, $50, $95, $a6, $99, $9c, $5c, $03, $01, $02
+
+ORG $C86E26
+db $10
+
+ORG $EEB227
+db $0A, $BE, $C2, $EE
+
+ORG $EEC2BE
+db $1f, $17, $c8, $03, $5e, $02, $06, $02
+
+ORG $C9D5B8
+db $0A, $C6, $C2, $EE
+
+ORG $EEC2C6
+db $50, $16, $6f, $51, $08, $58, $24, $c9, $ff, $0a, $bc, $d5, $c9
+
+ORG $CF213D
+db $C7, $00
+
+ORG $EEA0E6
+db $01
+
+ORG $EEB7C7
+db $0A, $4A, $C5, $EE
+
+ORG $EEC54A
+db $18, $01, $01, $70, $72, $a9, $50, $0a, $cb, $b7, $ee
+
+ORG $C8FF31
+db $0A, $55, $C5, $EE
+
+ORG $EEC555
+db $04, $e9, $03, $18, $00, $10, $3c, $0a, $35, $ff, $c8
+
+ORG $C8FF86
+db $0A, $60, $C5, $EE
+
+ORG $EEC560
+db $9f, $a4, $98, $95, $a2, $50, $9d, $a5, $9c, $a4, $99, $a7, $9f, $a2, $9c, $94
+db $0a, $8b, $ff, $c8
+
+ORG $C98A7F
+db $0a, $74, $c5, $ee
+
+ORG $EEC574
+db $91, $50, $1c, $05, $01, $0a, $87, $8a, $c9
+
+ORG $C89390
+db $0a, $7d, $c5, $ee
+
+ORG $EEC57d
+db $50, $1c, $05, $01, $50, $91, $a3, $50, $91, $50, $a3, $9f, $a5, $a6, $95, $9e
+db $99, $a2, $0a, $99, $93, $c8
+
+ORG $CF9829
+db $00
+
+ORG $C6DB04
+db $49
+
+ORG $C7C0CD
+dd EndScript; Fix rainy circle playing twice
+
+ORG $C9D95E
+db $01, $01, $01
+
+ORG $C7BFF5
+db $94, $BF, $C7
+
+ORG $EF6CA2
+db $04, $cc, $00, $1f, $12, $0a, $1f, $12, $0b, $02; stop flying man from living
+
+ORG $C6AE51
+db $01, $01, $01, $01
+
+ORG $CFA94C
+db $93, $C5, $EE
+
+ORG $00FFE6
+db $F0, $FE
+
+ORG $00FEF0
+;Crash handler
+SEP #$20
+STZ $B4A1
+JML RenderCrashScreen; Jump to anti-piracy screen
+
+ORG $EEAAF1
+db $F2, $C5, $EE
+
+ORG $EEC5F2
+db $0A
+dl PooNPCTexReal
+PooReturnReal:
+db $10, $85, $01, $1f, $03, $70, $58, $1c, $02, $04, $50, $9a
+db $9f, $99, $9e, $95, $94, $50, $a9, $9f, $a5, $5e, $59, $04, $10, $00, $1f, $11
+db $04, $03, $01, $0e, $00, $0e, $01, $08, $f1, $c5, $ee, $ff, $02
+;db $04, $0e, $01, $0e, $00, $0e, $01, $08, $f1, $c5, $ee, $ff, $02
+
+ORG $C3E27C
+db $0C
+
+ORG $C6B233
+db $1f, $c6, $ee
+
+ORG $EEC61F
+db $12, $70, $7b, $95, $95, $50, $9b, $a9, $9f, $51, $03, $01, $70, $58, $89, $9f
+db $a5, $50, $94, $9f, $9e, $57, $a4, $50, $98, $91, $a6, $95, $50, $91, $9e, $a9
+db $01, $50, $50, $16, $e6, $10, $05, $95, $10, $05, $95, $10, $05, $95, $a7, $15
+db $1b, $97, $a5, $9d, $51, $59, $10, $10, $12, $02
+
+ORG $C932C9
+db $02
+
+ORG $EEC659
+db $7d, $91, $97, $99, $93, $91, $9e, $a4, $50, $84, $95, $9c, $95, $a0, $9f, $a2
+db $a4
+
+ORG $C60582
+db $01, $1d, $03, $ff
+
+ORG $C6E9A0
+db $01, $01, $01, $01
+
+ORG $C97980
+db $4B
+
+ORG $EF58C8
+db $F5, $03
+
+ORG $EF5913
+db $F5, $03
+
+ORG $C81A97
+db $49
+
+ORG $EEB08C
+db $f4, $03
+
+ORG $EEC66A
+;;;TODO: Test the Shyness Book dialogue after getting an AP item? this
+db $0A, $91, $BF, $C6, $84, $be
+db $ee
+
+ORG $C6D3A2
+db $ba
+
+ORG $C7D35A
+db $49
+
+ORG $C3E064
+db $27, $00
+
+ORG $EEC67B
+db $1e, $00, $ff, $64, $1e, $04, $ff, $64, $02
+
+ORG $D56FCC
+db $2F;Store tiny ruby
+
+ORG $D5528A
+db $84, $0e ;broken antenna price
+
+ORG $D5576A
+db $08, $1d; Gaia beam price
+
+ORG $D5556F
+db $02, $00; Sword of Kings
+
+ORG $CF11BA
+db $47, $80
+;;;;;;;;;;;;;;;;;;;;;
+;New storage
+ORG $C17A9D
+JML CheckGiftBox7
+
+ORG $C1911B
+JML CheckGiftBox8
+
+ORG $C19AB9
+JML CheckGiftBox1
+
+ORG $C191BE
+JML CheckGiftBox4
+
+ORG $C191C9
+JML CheckGiftBox6
+
+ORG $C19151
+JML CheckGiftBox9
+
+ORG $C191D3
+JML CheckGiftBox3
+
+ORG $C191E5
+CMP #$0045
+
+ORG $C19B1E
+CMP #$0045
+
+ORG $C190F9
+LDA #$0045
+
+ORG $C19170
+LDA #$0045
+
+ORG $C19B3B
+JSL ClearStorageMenu
+
+ORG $C191EC
+JML CheckGiftBox5
+
+ORG $C15B29
+JML CheckGiftBox2
+
+ORG $C11E25
+;JSL ClearStoragePage
+
+ORG $EEC684
+db $18, $01, $29, $19, $02, $79, $9e, $a6, $95, $9e, $a4, $9f, $a2, $a9, $02, $19
+db $02, $83, $a4, $9f, $a2, $91, $97, $95, $02, $1c, $07, $02, $11, $09, $02, $dc
+db $96, $ee, $11, $b1, $c6, $ee, $22, $18, $00, $0a, $20, $96, $ee, $0e, $01, $19
+db $14, $1b, $02, $84, $c6, $ee, $ff, $18, $02, $1a, $07, $18, $03, $0d, $18, $00
+db $18, $03, $29, $1b, $02, $eb, $c6, $ee, $00, $1b, $04, $1b, $05, $19, $1a, $00
+db $1b, $04, $1d, $0b, $00, $1b, $02, $bd, $c6, $ee, $ff, $1b, $06, $1b, $0c, $19
+db $1c, $ff, $00, $0a, $b1, $c6, $ee, $18, $03, $0d, $18, $00, $18, $03, $29, $12
+db $0a, $87, $c6, $ee, $1c, $04, $1d, $19, $02, $1b, $03, $DB, $C8, $EE, $00, $1a
+db $01, $84, $e5, $c5, $00, $8b, $e5, $c5, $00, $92, $e5, $c5, $00, $99, $e5, $c5
+db $00, $00, $0d, $00, $1b, $02, $30, $C7, $EE, $00, $19, $1b, $02, $1b, $02, $F8
+db $C6, $EE, $00, $18, $09, $02, $1b, $02, $F8, $C6, $EE, $00, $18, $03, $02, $18
+db $00, $18, $03, $29, $1b, $04, $02, $00
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;Twoson police officer
+ORG $CF67AF
+db $0b, $00
+
+ORG $C4FEEA
+db $02, $00, $57, $01, $b0, $f8, $9b, $00, $45, $5c
+
+ORG $CF93D6
+db $69, $00, $01
+
+ORG $CF93D9
+db $3b, $c7, $ee, $00, $DF, $EE, $EE
+
+ORG $EEC73B
+db $70, $7f, $9e, $95, $a4, $a4, $6f, $10, $0f, $50, $83, $9f, $a2, $a2, $a9, $5c
+db $50, $a4, $98, $95, $50, $a2, $9f, $91, $94, $50, $99, $a3, $50, $93, $9c, $9f
+db $a3, $95, $94, $50, $a5, $9e, $a4, $99, $9c, $50, $96, $a5, $a2, $a4, $98, $95
+db $a2, $50, $9e, $9f, $a4, $99, $93, $95, $5e, $03, $00, $70, $73, $91, $a0, $a4
+db $91, $99, $9e, $50, $83, $a4, $a2, $9f, $9e, $97, $50, $9c, $9f, $a3, $a4, $50
+db $98, $99, $a3, $50, $92, $91, $94, $97, $95, $50, $91, $97, $91, $99, $9e, $5c
+db $10, $08, $50, $a3, $9f, $50, $a4, $98, $99, $a3, $50, $91, $a2, $95, $91, $50
+db $99, $a3, $50, $a5, $9e, $94, $95, $a2, $50, $9c, $9f, $93, $9b, $94, $9f, $a7
+db $9e, $50, $a5, $9e, $a4, $99, $9c, $50, $99, $a4, $57, $a3, $50, $96, $9f, $a5
+db $9e, $94, $5e, $13, $02, $19, $19, $00, $00, $1b, $04, $0b, $CA, $1b, $02, $b6
+db $c6, $c7, $ff, $1d, $01, $ff, $ca, $1f, $02, $76, $18, $04, $18, $01, $01, $70
+db $78, $95, $a9, $51, $10, $0f, $50, $84, $98, $91, $a4, $57, $a3, $50, $a4, $98
+db $95, $50, $93, $91, $a0, $a4, $91, $99, $9e, $57, $a3, $50, $92, $91, $94, $97
+db $95, $51, $03, $00, $70, $84, $98, $95, $50, $a2, $9f, $91, $94, $50, $a4, $9f
+db $50, $7f, $9e, $95, $a4, $a4, $50, $93, $91, $9e, $50, $9e, $9f, $a7, $50, $92
+db $95, $50, $9f, $a0, $95, $9e, $95, $94, $50, $91, $97, $91, $99, $9e, $5e, $10
+db $0f, $50, $84, $98, $91, $9e, $9b, $50, $a9, $9f, $a5, $50, $96, $9f, $a2, $50
+db $a9, $9f, $a5, $a2, $50, $93, $9f, $9f, $a0, $95, $a2, $91, $a4, $99, $9f, $9e
+db $5e, $03, $18, $04, $04, $69, $00, $1f, $16, $9b, $00, $02, $10, $04, $1f, $1e
+db $9b, $00, $00, $02
+
+ORG $D56EDF
+db $3B;Item type for the Police Badge
+
+ORG $CF6277
+db $00, $00; Remove police from Onett
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C577A0
+db $0A, $6F, $C8, $EE
+
+ORG $EEC86F
+db $04, $5e, $02, $1d, $01, $ff, $c0, $0a, $a4, $77, $c5
+
+ORG $C6FB13
+db $7A, $C8, $EE
+
+ORG $EEC87A
+db $08, $33, $fb, $c6, $ff, $1d, $01, $ff, $ac, $02
+
+ORG $C8720F
+db $84, $C8, $EE
+
+ORG $EEC884
+db $1d, $01, $ff, $b9, $0a, $20, $72, $c8
+
+ORG $C57076
+db $01, $01, $01, $01
+
+ORG $C9FD1A
+db $0A, $8C, $C8, $EE
+
+ORG $EEC88C
+db $a4, $51, $03, $00, $70, $79, $9e, $50, $99, $a4, $a3, $50, $9c, $91, $a3, $a4
+db $50, $9d, $9f, $9d, $95, $9e, $a4, $a3, $5c, $10, $0a, $50, $a4, $98, $95, $50
+db $95, $a2, $91, $a3, $95, $a2, $50, $a5, $a3, $95, $94, $50, $a4, $98, $95, $50
+db $75, $a2, $91, $a3, $95, $a2, $50, $75, $a2, $91, $a3, $95, $a2, $50, $75, $a2
+db $91, $a3, $95, $a2, $51, $59, $03, $1d, $01, $ff, $d2, $0a, $1e, $fd, $c9
+
+ORG $C8255E
+db $00, $00
+
+ORG $C636E5
+db $0a, $00, $00, $F2; Disables Escargo phone call
+
+ORG $C8A01F
+db $02
+
+ORG $EEC8DB
+db $19, $10, $01, $0d, $00, $1b, $05, $18, $02, $1b, $06, $1a, $05, $02, $00, $1b
+db $06, $18, $09, $02, $1b, $05, $18, $03, $02, $18, $00, $18, $03, $29, $1b, $06
+db $1b, $02, $c2, $e4, $c5, $00, $1b, $04, $02, $02
+;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C10119
+JSL AutoText
+BRA $00
+
+ORG $C1015C
+JSL AutoText
+BRA $00
+
+ORG $C10224
+JSL AutoText
+BRA $00
+
+ORG $C10278
+JSL AutoText
+BRA $00
+
+ORG $C101D8
+JSL AutoText
+BRA $00
+;;;;;;;;;;;;;;;;;;;;;;;;;
+;Fix OSS Bug
+ORG $C22FD1
+JML LostScriptedFight
+NOP
+
+ORG $C22FEC
+JML LostScriptedFight
+NOP
+;;;;;;;;;;;;;;;;;;;
+ORG $C1F80D
+;JSL PrintVersionNumber
+
+ORG $C57948
+db $49, $00; Dungeon man if submarine is used
+
+ORG $C5796B
+db $D8, $03
+
+ORG $C5794F
+db $D8, $03
+
+ORG $C57A26
+db $06, $9c, $03, $55, $79, $c5, $ff, $70, $78, $95, $a2, $95, $5c, $10, $0a, $50
+db $79, $50, $92, $95, $9c, $99, $95, $a6, $95, $50, $a9, $9f, $a5, $50, $98, $91
+db $a6, $95, $50, $9c, $95, $96, $a4, $50, $a4, $98, $99, $a3, $50, $1c, $05, $01
+db $50, $92, $95, $98, $99, $9e, $94, $5e, $03, $1d, $03, $ff, $1b, $02, $76, $7a
+db $c5, $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03, $04, $9c, $03, $02
+db $00, $70, $89, $9f, $a5, $50, $a3, $98, $9f, $a5, $9c, $94, $50, $93, $9f, $9d
+db $95, $50, $92, $91, $93, $9b, $50, $a7, $98, $95, $9e, $50, $a9, $9f, $a5, $50
+db $98, $91, $a6, $95, $50, $9d, $9f, $a2, $95, $50, $a2, $9f, $9f, $9d, $5e, $13
+db $02
+
+ORG $C5373A
+db $0a, $05, $c9, $ee
+
+ORG $EEC905
+db $a3, $50, $91, $08, $3a, $c9, $ee, $ff, $0a, $41, $37, $c5, $50, $9c, $99, $97
+db $98, $a4, $9e, $99, $9e, $97, $02, $50, $96, $99, $a2, $95, $02, $9e, $50, $99
+db $93, $95, $02, $50, $96, $9c, $91, $a3, $98, $02, $50, $a3, $a4, $91, $a2, $a3
+db $a4, $9f, $a2, $9d, $02, $50, $a0, $a3, $a9, $93, $98, $99, $93, $50, $95, $9e
+db $95, $a2, $97, $a9, $02, $9e, $50, $95, $a8, $a0, $9c, $9f, $a3, $99, $a6, $95
+db $02
+
+ORG $EF717B
+db $0a, $56, $c9, $ee
+
+ORG $EEC956
+db $08, $82, $c9, $ee, $ff, $0a, $80, $71, $ef, $9c, $99, $97, $98, $a4, $9e, $99
+db $9e, $97, $02, $96, $99, $a2, $95, $02, $99, $93, $95, $02, $9c, $99, $97, $98
+db $a4, $02, $a3, $a4, $91, $a2, $a3, $a4, $9f, $a2, $9d, $02, $95, $9e, $95, $a2
+db $97, $a9, $02, $92, $9c, $91, $a3, $a4, $02
+
+ORG $EF7163
+db $1c, $05, $01, $0a, $70, $71, $ef
+
+ORG $C8361C
+db $70, $7f, $98, $98, $98, $5c, $10, $08, $50, $91, $98, $98, $98, $5c, $50, $9f
+db $a5, $93, $98, $5e, $5e, $5e, $03, $00, $70, $79, $50, $a7, $91, $a3, $50, $a4
+db $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $50, $9c, $9f, $9f, $9b, $50, $91, $a4
+db $50, $a4, $98, $91, $a4, $50, $a3, $a4, $91, $a4, $a5, $95, $50, $92, $95, $98
+db $99, $9e, $94, $50, $a4, $98, $95, $50, $93, $9f, $a5, $9e, $a4, $95, $a2, $5c
+db $10, $08, $50, $a7, $98, $95, $9e, $50, $79, $50, $a3, $a0, $a2, $91, $99, $9e
+db $95, $94, $50, $9d, $a9, $50, $91, $9e, $9b, $9c, $95, $5e, $5e, $5e, $03, $00
+db $70, $89, $95, $a3, $5e, $5e, $5e, $50, $79, $57, $9d, $50, $96, $99, $9e, $95
+db $5e, $10, $08, $50, $7a, $a5, $a3, $a4, $50, $98, $91, $a6, $95, $50, $a4, $9f
+db $50, $a7, $91, $9c, $9b, $50, $99, $a4, $50, $9f, $96, $96, $5e, $03, $00, $70
+db $71, $9e, $a9, $a7, $91, $a9, $a3, $5c, $10, $08, $50, $a9, $9f, $a5, $50, $a3
+db $98, $9f, $a5, $9c, $94, $50, $a2, $95, $91, $9c, $9c, $a9, $50, $93, $98, $95
+db $93, $9b, $10, $0e, $50, $92, $95, $98, $99, $9e, $94, $10, $0e, $50, $a4, $98
+db $95, $50, $93, $9f, $a5, $9e, $a4, $95, $a2, $10, $0e, $50, $91, $a4, $50, $a4
+db $98, $95, $50, $93, $91, $96, $95, $5e, $03, $00, $0a, $d8, $39, $c8; Everdred fourside text
+
+ORG $CFAE24
+db $00, $8F, $C9, $EE
+
+ORG $EEC98F
+db $70, $7e, $9f, $a4, $50, $a4, $9f, $9f, $50, $9c, $9f, $9e, $97, $50, $91, $97
+db $9f, $5c, $03, $00, $70, $79, $50, $98, $95, $91, $a2, $94, $50, $a3, $9f, $9d
+db $95, $50, $aa, $9f, $9d, $92, $99, $95, $a3, $50, $a4, $91, $9c, $9b, $99, $9e
+db $97, $50, $91, $92, $9f, $a5, $a4, $50, $98, $9f, $a7, $50, $a4, $98, $95, $a9
+db $50, $9c, $9f, $93, $9b, $95, $94, $50, $a3, $9f, $9d, $95, $01, $50, $50, $6C
+db $1c, $05, $01, $6E, $50, $99, $9e, $50, $a4, $98, $95, $50, $93, $95, $9d, $95
+db $a4, $95, $a2, $a9, $5e, $03, $00, $70, $78, $9f, $a7, $50, $91, $a7, $96, $a5
+db $9c, $51, $13, $02
+
+ORG $CFA5D5
+db $7A, $00, $02
+
+ORG $CFA272
+db $7a, $00, $01
+
+ORG $CFA283
+db $7a, $00, $01
+
+ORG $D5F63B
+db $6E
+
+ORG $EECA03
+db $04, $c3, $01, $04, $c4, $01, $04, $c5, $01, $04, $c6, $01, $04, $c7, $01, $04
+db $c8, $01, $04, $c9, $01, $04, $ca, $01, $04, $cc, $01, $04, $cd, $01, $04, $ce
+db $01, $0a, $0b, $b1, $c9
+
+ORG $EECA28
+db $70, $7b, $9f, $9b, $a5, $99, $9b, $9f, $9b, $91, $9b, $95, $9b, $95, $51, $03
+db $00, $70, $58, $77, $95, $a4, $50, $a9, $9f, $a5, $a2, $50, $7d, $9f, $9e, $9b
+db $95, $a9, $50, $74, $95, $9c, $a5, $a8, $95, $50, $92, $a5, $9e, $94, $9c, $95
+db $a3, $51, $59, $03, $00, $70, $7b, $9f, $9b, $a5, $9b, $99, $50, $9b, $95, $95
+db $9b, $9f, $9b, $91, $99, $50, $9b, $9f, $9b, $9f, $9b, $9f, $50, $54, $65, $60
+db $60, $51, $03, $00, $70, $58, $71, $9e, $a9, $a4, $98, $99, $9e, $97, $50, $91
+db $50, $9d, $9f, $9e, $9b, $95, $a9, $50, $93, $9f, $a5, $9c, $94, $50, $a7, $91
+db $9e, $a4, $5c, $10, $06, $50, $9a, $a5, $a3, $a4, $50, $54, $65, $60, $60, $51
+db $59, $03, $00, $70, $7b, $9f, $9b, $a5, $9b, $91, $91, $99, $50, $9b, $9f, $9b
+db $91, $50, $54, $65, $60, $60, $6f, $03, $00, $70, $58, $87, $91, $9e, $a4, $50
+db $91, $50, $7d, $9f, $9e, $9b, $95, $a9, $50, $74, $95, $9c, $a5, $a8, $95, $50
+db $92, $a5, $9e, $94, $9c, $95, $50, $96, $9f, $a2, $50, $54, $65, $60, $60, $6f
+db $59, $03, $00, $1c, $04, $18, $0a, $19, $02, $89, $95, $a3, $02, $19, $02, $7e
+db $9f, $02, $1c, $07, $02, $11, $09, $02, $28, $cb, $ee, $11, $08, $cb, $ee, $22
+db $12, $70, $7b, $9f, $9b, $9f, $9b, $91, $9b, $5e, $03, $00, $70, $58, $89, $9f
+db $a5, $57, $9c, $9c, $50, $92, $95, $50, $92, $91, $93, $9b, $5e, $59, $13, $02
+db $1d, $09, $f4, $01, $1b, $02, $53, $cb, $ee, $ff, $12, $70, $7b, $99, $9b, $99
+db $99, $a9, $91, $99, $9b, $9f, $51, $03, $00, $70, $58, $89, $9f, $a5, $57, $a2
+db $95, $50, $92, $a2, $9f, $9b, $95, $51, $59, $13, $02, $0e, $00, $0d, $01, $1d
+db $00, $ff, $fe, $1b, $02, $bf, $cb, $ee, $ff, $1d, $00, $ff, $fe, $1b, $02, $bf
+db $cb, $ee, $ff, $1d, $00, $ff, $fe, $1b, $02, $bf, $cb, $ee, $ff, $1d, $00, $ff
+db $fe, $1b, $02, $bf, $cb, $ee, $ff, $1d, $00, $ff, $fe, $1b, $02, $bf, $cb, $ee
+db $ff, $1d, $00, $ff, $fe, $1b, $02, $bf, $cb, $ee, $ff, $1d, $00, $ff, $fe, $1b
+db $02, $bf, $cb, $ee, $ff, $1d, $00, $ff, $fe, $1b, $02, $bf, $cb, $ee, $ff, $1d
+db $00, $ff, $fe, $1b, $02, $bf, $cb, $ee, $ff, $1d, $00, $ff, $fe, $1b, $02, $bf
+db $cb, $ee, $ff, $0a, $35, $cc, $ee, $12, $70, $58, $87, $98, $9f, $91, $98, $51
+db $10, $0f, $50, $89, $9f, $a5, $50, $92, $95, $a4, $a4, $95, $a2, $50, $9d, $91
+db $9b, $95, $50, $a3, $a5, $a2, $95, $50, $a9, $9f, $a5, $50, $93, $91, $9e, $50
+db $98, $9f, $9c, $94, $50, $61, $60, $50, $a4, $98, $99, $9e, $97, $a3, $5c, $10
+db $06, $50, $92, $a5, $94, $94, $a9, $51, $59, $13, $08, $0c, $cc, $ee, $ff, $1d
+db $08, $f4, $01, $02, $1d, $01, $ff, $fe, $1d, $01, $ff, $fe, $1d, $01, $ff, $fe
+db $1d, $01, $ff, $fe, $1d, $01, $ff, $fe, $1d, $01, $ff, $fe, $1d, $01, $ff, $fe
+db $1d, $01, $ff, $fe, $1d, $01, $ff, $fe, $1d, $01, $ff, $fe, $02, $08, $0c, $cc
+db $ee, $ff, $1f, $02, $78, $18, $0a, $12, $70, $7b, $a5, $9b, $99, $9b, $9f, $9b
+db $91, $50, $9b, $99, $99, $a9, $9b, $5e, $03, $00, $70, $58, $7d, $a5, $93, $98
+db $50, $9f, $92, $9c, $99, $97, $95, $94, $5e, $59, $03, $1d, $0e, $ff, $e0, $1d
+db $0e, $ff, $5d, $1d, $0e, $ff, $5f, $1d, $0e, $ff, $5f, $1d, $0e, $ff, $7f, $1d
+db $0e, $ff, $5a, $1d, $0e, $ff, $5a, $1d, $0e, $ff, $5a, $1d, $0e, $ff, $6c, $1d
+db $0e, $ff, $8c, $1f, $02, $74, $10, $10, $1f, $02, $74, $10, $10, $1f, $02, $74
+db $10, $10, $1f, $02, $74, $10, $10, $1f, $02, $74, $10, $10, $1f, $02, $74, $10
+db $10, $1f, $02, $74, $10, $10, $1f, $02, $74, $10, $10, $1f, $02, $74, $10, $10
+db $1f, $02, $74, $10, $10, $02; Monkey caves Monkey Bundle
+
+ORG $C807F5
+db $1c, $05, $01, $0a, $fc, $07, $c8
+
+ORG $C57A14
+db $02
+
+ORG $C98276
+db $0A, $BE, $CC, $EE
+
+ORG $EECCBE
+db $18, $04, $04, $7a, $00, $05, $f6, $03, $02
+
+ORG $C98025
+db $F6, $03
+
+ORG $C9827D
+db $F6, $03; Carpainter stuff
+
+ORG $C97633
+db $02
+
+ORG $C86DFC
+;db $7A, $00, $C7, $CC, $EE
+
+ORG $EECCC7
+db $70, $58, $83, $9f, $a5, $9e, $94, $a3, $50, $9c, $99, $9b, $95, $50, $a3, $9f
+db $9d, $95, $9f, $9e, $95, $57, $a3, $50, $9f, $a5, $a4, $a3, $99, $94, $95, $5e
+db $03, $00, $70, $72, $95, $a4, $a4, $95, $a2, $50, $93, $9f, $9d, $95, $50, $92
+db $91, $93, $9b, $50, $9c, $91, $a4, $95, $a2, $5e, $59, $13, $02;Fix re-entering moonside. Do I need this? Doesn't it set the checkpoint when clearing moonside?
+
+ORG $C58ED1
+db $0A
+dl DynamicPhotoSetter
+
+ORG $EECD04
+db $18, $04, $1f, $e1, $00, $5c, $01, $02
+
+ORG $EECD0C
+db $01, $70, $72, $a5, $a4, $50, $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97, $50
+db $a0, $a2, $95, $a6, $95, $9e, $a4, $95, $94, $50, $99, $a4, $50, $96, $a2, $9f
+db $9d, $50, $a7, $9f, $a2, $9b, $99, $9e, $97, $51, $03, $02, $00; Deathlink heal tex
+;;;;;;;;;;;;;;;;
+ORG $D50700
+db $0f, $0f, $10, $10, $20, $3f, $40, $40, $ff, $ff, $80, $86, $80, $8f, $80, $8f
+db $0f, $0f, $10, $1f, $20, $20, $40, $7f, $ff, $ff, $80, $ff, $80, $ff, $b0, $ff
+db $ff, $ff, $03, $03, $07, $ff, $0b, $0f, $fb, $ff, $1b, $1f, $1b, $1f, $1b, $1f
+db $ff, $ff, $03, $ff, $05, $05, $0d, $f9, $f5, $f1, $15, $f1, $15, $f1, $d5, $31
+db $80, $86, $81, $b1, $83, $fb, $87, $f9, $8f, $b1, $8f, $80, $87, $81, $ff, $ff
+db $f9, $fe, $f9, $ff, $fb, $ff, $ff, $ff, $bf, $ff, $8f, $ff, $87, $ff, $ff, $ff
+db $9b, $9f, $db, $df, $fb, $ff, $fb, $df, $da, $de, $1c, $1c, $d8, $d8, $f0, $f0
+db $f5, $91, $f5, $d1, $f5, $f1, $d5, $f1, $d6, $f2, $14, $f4, $d8, $f8, $f0, $f0;ProgBox
+
+ORG $D517c0
+db $0f, $0f, $1f, $1f, $3f, $3f, $7f, $7f, $ff, $ff, $80, $80, $80, $80, $c0, $c0
+db $0f, $0f, $10, $10, $20, $20, $40, $40, $ff, $ff, $80, $ff, $80, $ff, $80, $bf
+db $ff, $ff, $03, $ff, $07, $ff, $0b, $ff, $fb, $ff, $1b, $1f, $1b, $1f, $3b, $3f
+db $ff, $ff, $ff, $03, $fd, $05, $fd, $09, $f5, $f1, $15, $f1, $15, $f1, $15, $d1
+db $bf, $ff, $81, $b1, $83, $fb, $87, $f9, $8f, $b1, $8f, $80, $87, $81, $ff, $ff
+db $c0, $80, $f9, $cf, $fb, $ff, $ff, $ff, $bf, $ff, $8f, $ff, $87, $ff, $ff, $ff
+db $db, $ff, $1b, $df, $fb, $ff, $fb, $df, $da, $de, $1c, $1c, $d8, $d8, $f0, $f0
+db $35, $11, $f5, $11, $f5, $f1, $d5, $f1, $d6, $f2, $14, $f4, $d8, $f8, $f0, $f0;ProgBox open sprite
+
+ORG $D53240
+db $0f, $0f, $10, $10, $20, $3f, $40, $40, $ff, $ff, $80, $86, $80, $8f, $80, $8f
+db $0f, $0f, $10, $1f, $20, $20, $40, $7f, $ff, $ff, $80, $ff, $80, $ff, $b0, $ff
+db $ff, $ff, $03, $03, $07, $ff, $0b, $0f, $fb, $ff, $1b, $1f, $1b, $1f, $1b, $1f
+db $ff, $ff, $03, $ff, $05, $05, $0d, $f9, $f5, $f1, $15, $f1, $15, $f1, $d5, $31
+db $80, $86, $80, $b0, $81, $f8, $87, $f8, $8f, $b0, $8f, $80, $86, $80, $ff, $ff
+db $f9, $fe, $f9, $fe, $f8, $ff, $fe, $ff, $bf, $ff, $8f, $ff, $86, $ff, $ff, $ff
+db $1b, $1f, $db, $1f, $fb, $1f, $fb, $1f, $da, $1e, $1c, $1c, $18, $18, $f0, $f0
+db $f5, $11, $35, $d1, $15, $f1, $15, $f1, $16, $f2, $14, $f4, $18, $f8, $f0, $f0;APBox
+
+ORG $D540C0
+db $ff, $ff, $c0, $ff, $e0, $ff, $d0, $ff, $df, $ff, $d8, $f8, $d8, $f8, $dc, $fc
+db $ff, $ff, $ff, $c0, $bf, $a0, $bf, $90, $af, $8f, $a8, $8f, $a8, $8f, $a8, $8b
+db $f0, $f0, $f8, $f8, $fc, $fc, $fe, $fe, $ff, $ff, $01, $01, $01, $01, $03, $03
+db $f0, $f0, $08, $08, $04, $04, $02, $02, $ff, $ff, $01, $ff, $01, $ff, $01, $fd
+db $db, $ff, $d8, $fb, $df, $f8, $df, $f8, $5b, $78, $38, $38, $18, $18, $0f, $0f
+db $ac, $88, $af, $88, $a8, $8f, $a8, $8f, $68, $4f, $28, $2f, $18, $1f, $0f, $0f
+db $fd, $ff, $01, $0d, $81, $1f, $e1, $1f, $f1, $0d, $f1, $01, $61, $01, $ff, $ff
+db $03, $01, $9f, $73, $1f, $ff, $7f, $ff, $fd, $ff, $f1, $ff, $61, $ff, $ff, $ff;ApBox open sprite
+
+ORG $EF43E3
+db $0A
+
+ORG $EF44AB
+db $0A
+
+ORG $EF43E9
+db $c0, $17
+
+ORG $EF44B1
+db $c1, $40
+
+ORG $EECD38
+db $06, $e2, $00, $ec, $d9, $c7, $00, $06, $cd, $00, $99, $ce, $ee, $ff, $06, $ce
+db $00, $99, $ce, $ee, $ff, $06, $cf, $00, $99, $ce, $ee, $ff, $06, $d0, $00, $99
+db $ce, $ee, $ff, $06, $e7, $00, $99, $ce, $ee, $ff, $06, $e8, $00, $99, $ce, $ee
+db $ff, $06, $e9, $00, $99, $ce, $ee, $ff, $06, $eA, $00, $99, $ce, $ee, $ff, $06
+db $EB, $00, $99, $ce, $ee, $ff, $06, $eC, $00, $99, $ce, $ee, $ff, $06, $ED, $00
+db $99, $ce, $ee, $ff, $06, $eE, $00, $99, $ce, $ee, $ff, $06, $eF, $00, $99, $ce
+db $ee, $ff, $06, $F0, $00, $99, $ce, $ee, $ff, $06, $F1, $00, $99, $ce, $ee, $ff
+db $06, $F2, $00, $99, $ce, $ee, $ff, $06, $F3, $00, $99, $ce, $ee, $ff, $06, $F4
+db $00, $99, $ce, $ee, $ff, $0a, $14, $d9, $c7;New present table
+
+;macro needs to go here?
+db $04, $cd, $00, $08, $7f, $d8, $c7, $00, $05, $cd, $00, $02
+db $04, $ce, $00, $08, $7f, $d8, $c7, $00, $05, $ce, $00, $02
+db $04, $CF, $00, $08, $7f, $d8, $c7, $00, $05, $CF, $00, $02
+db $04, $D0, $00, $08, $7f, $d8, $c7, $00, $05, $D0, $00, $02
+db $04, $E7, $00, $08, $7f, $d8, $c7, $00, $05, $E7, $00, $02
+db $04, $E8, $00, $08, $7f, $d8, $c7, $00, $05, $E8, $00, $02
+db $04, $E9, $00, $08, $7f, $d8, $c7, $00, $05, $E9, $00, $02
+db $04, $EA, $00, $08, $7f, $d8, $c7, $00, $05, $EA, $00, $02
+db $04, $EB, $00, $08, $7f, $d8, $c7, $00, $05, $EB, $00, $02
+db $04, $EC, $00, $08, $7f, $d8, $c7, $00, $05, $EC, $00, $02
+db $04, $ED, $00, $08, $7f, $d8, $c7, $00, $05, $ED, $00, $02
+db $04, $EE, $00, $08, $7f, $d8, $c7, $00, $05, $EE, $00, $02
+db $04, $EF, $00, $08, $7f, $d8, $c7, $00, $05, $EF, $00, $02
+db $04, $F0, $00, $08, $7f, $d8, $c7, $00, $05, $F0, $00, $02
+db $04, $F1, $00, $08, $7f, $d8, $c7, $00, $05, $F1, $00, $02
+db $04, $F2, $00, $08, $7f, $d8, $c7, $00, $05, $F2, $00, $02
+db $04, $F3, $00, $08, $7f, $d8, $c7, $00, $05, $F3, $00, $02
+db $04, $F4, $00, $08, $7f, $d8, $c7, $00, $05, $F4, $00, $02;Present checks
+
+db $70, $1c, $02, $00, $50, $9f, $a0, $95, $9e, $95, $94, $50, $a4, $98, $95, $50
+db $92, $9f, $a8, $5e, $02;Ap box normal
+
+db $06, $e2, $00, $fc, $d9, $c7, $00, $06, $ce, $00, $29, $CF, $EE, $ff, $06, $cf
+db $00, $47, $CF, $EE, $ff, $06, $d0, $00, $57, $CF, $EE, $ff, $06, $e7, $00, $8A
+db $CF, $EE, $ff, $06, $e8, $00, $9A, $CF, $EE, $ff, $06, $e9, $00, $BD, $CF, $EE
+db $ff, $06, $ea, $00, $CB, $CF, $EE, $ff, $06, $eb, $00, $F2, $CF, $EE, $ff, $06
+db $ec, $00, $08, $D0, $EE, $ff, $06, $ed, $00, $1D, $D0, $EE, $ff, $06, $ee, $00
+db $32, $D0, $EE, $ff, $06, $EF, $00, $4D, $D0, $EE, $ff, $06, $f0, $00, $5e, $d0
+db $ee, $ff, $06, $f1, $00, $76, $D0, $EE, $ff, $06, $f2, $00, $9E, $D0, $EE, $ff
+db $06, $f3, $00, $AE, $D0, $EE, $ff, $0a, $d5, $d0, $EE
+
+db $01, $10, $14, $70, $79, $a3, $50, $a4, $98, $99, $a3, $50, $95, $a6, $95, $9e
+db $50, $a2, $91, $9e, $94, $9f, $9d, $99, $aa, $95, $94, $6f, $03, $02;is this even randomized
+
+db $01, $10, $14, $70, $7f, $9f, $98, $50, $9c, $91, $50, $9c, $91, $51, $03, $02;ooh la la
+
+db $01, $10, $14, $70, $7e, $9f, $a7, $50, $a4, $98, $95, $a2, $95, $57, $a3, $50
+db $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97, $50, $a9, $9f, $a5, $50, $94, $9f
+db $9e, $57, $a4, $50, $a3, $95, $95, $50, $95, $a6, $95, $a2, $a9, $94, $91, $a9
+db $51, $03, $02
+
+db $01, $10, $14, $70, $87, $78, $71, $71, $71, $71, $71, $84, $6f, $51, $03, $02;what
+
+db $01, $10, $14, $70, $87, $95, $9c, $9c, $5c, $10, $05, $50, $a3, $9c, $91, $a0
+db $50, $9d, $95, $50, $a7, $99, $a4, $98, $50, $91, $50, $a4, $a2, $9f, $a5, $a4
+db $51, $03, $02;trout
+
+db $01, $10, $14, $70, $7a, $91, $93, $9b, $a0, $9f, $a4, $51, $03, $02;jackpot
+
+db $01, $10, $14, $70, $72, $a5, $a4, $50, $99, $a4, $50, $a7, $91, $a3, $50, $95
+db $9d, $a0, $a4, $a9, $51, $03, $01, $70, $7a, $a5, $a3, $a4, $50, $9b, $99, $94
+db $94, $99, $9e, $97, $51, $03, $02
+
+db $01, $10, $14, $70, $78, $9f, $a4, $50, $94, $99, $97, $97, $99, $a4, $a9, $50
+db $94, $9f, $97, $51, $03, $02
+
+db $01, $10, $14, $70, $77, $a2, $95, $91, $a4, $50, $97, $91, $94, $aa, $9f, $9f
+db $9b, $a3, $51, $03, $02
+
+db $01, $10, $14, $70, $87, $98, $9f, $91, $98, $5c, $10, $03, $50, $9e, $95, $9c
+db $9c, $a9, $51, $03, $02
+
+db $01, $10, $14, $70, $87, $98, $91, $a4, $50, $94, $9f, $50, $a7, $95, $50, $98
+db $91, $a6, $95, $50, $98, $95, $a2, $95, $6f, $03, $02
+
+db $01, $10, $14, $70, $78, $85, $78, $78, $78, $78, $78, $78, $78, $6f, $51, $03
+db $02
+
+db $01, $10, $14, $70, $7e, $9f, $a7, $50, $a7, $95, $57, $a2, $95, $50, $93, $9f
+db $9f, $9b, $99, $9e, $97, $51, $03, $02
+
+db $01, $10, $14, $70, $79, $50, $9e, $95, $a6, $95, $a2, $50, $a3, $a5, $a3, $a0
+db $95, $93, $a4, $95, $94, $50, $99, $a4, $50, $93, $9f, $a5, $9c, $94, $50, $98
+db $91, $a0, $a0, $95, $9e, $51, $03, $02
+
+db $01, $10, $14, $70, $7f, $98, $98, $98, $98, $50, $a9, $95, $a3, $51, $03, $02; oh yes
+
+db $01, $10, $14, $70, $79, $57, $a6, $95, $50, $a3, $95, $95, $9e, $50, $a4, $98
+db $99, $a3, $50, $92, $95, $96, $9f, $a2, $95, $5e, $03, $02; seen this before
+
+db $06, $e5, $00, $b6, $da, $c7, $ff, $0a, $fd, $d9, $c7
+
+db $06, $F4, $00, $E0, $D0, $EE, $FF, $0a, $31, $d9, $c7; trap
+
+db $01, $10, $14, $70, $89, $9f, $a5, $50, $91, $a2, $95, $50, $91, $50, $96, $9f, $9f
+db $9c, $51, $03, $02; trap text
+
+ORG $C7D90D
+db $0A, $38, $CD, $EE
+
+ORG $C7D92A
+db $0A, $AE, $CE, $EE
+
+ORG $C7D9E4
+db $0A, $CA, $D0, $EE
+
+ORG $C8972A
+db $0A, $F5, $D0, $EE
+
+ORG $C897C1
+db $0A, $48, $D1, $EE
+
+ORG $C7FA20
+db $0A, $5E, $D1, $EE
+
+ORG $EED0F5
+db $06, $8b, $00, $04, $d1, $ee, $ff, $70, $58, $79, $50, $0a, $2e, $97, $c8, $70
+db $58, $79, $50, $98, $95, $91, $a2, $94, $50, $a4, $98, $91, $a4, $50, $91, $50
+db $a3, $93, $91, $a2, $a9, $50, $9d, $9f, $9e, $a3, $a4, $95, $a2, $50, $94, $9f
+db $95, $a3, $9e, $57, $a4, $50, $9c, $99, $a6, $95, $50, $9f, $9e, $50, $a4, $98
+db $95, $50, $a4, $9f, $a0, $50, $96, $9c, $9f, $9f, $a2, $5e, $03, $00, $70, $0a
+db $c8, $97, $c8
+
+
+db $5c, $50, $10, $10, $9c, $99, $9b, $95, $50, $91, $50, $1c, $05, $11, $5e, $03
+db $00, $70, $0a, $c8, $97, $c8
+;;;;;;;;;;;;;;;
+db $91, $96, $A4, $95, $A2, $A4, $91, $A3, $A4, $95, $0A, $29, $FA, $C7
+
+ORG $C79CB7
+db $D3, $00
+
+ORG $C79D59
+db $0A, $EF, $DC, $EE
+
+ORG $EED16C
+db $08, $df, $69, $c5, $00, $01, $70, $79, $a4, $50, $94, $95, $91, $9c, $a3, $50
+db $91, $92, $9f, $a5, $a4, $50, $68, $60, $50, $a0, $9f, $99, $9e, $a4, $a3, $50
+db $9f, $96, $50, $94, $91, $9d, $91, $97, $95, $50, $a4, $9f, $50, $95, $91, $93
+db $98, $50, $95, $9e, $95, $9d, $a9, $5e, $02
+
+db $08, $df, $69, $c5, $00, $01, $70, $79, $a4, $50, $94, $95, $91, $9c, $a3, $50
+db $91, $92, $9f, $a5, $a4, $50, $61, $68, $60, $50, $a0, $9f, $99, $9e, $a4, $a3
+db $50, $9f, $96, $50, $94, $91, $9d, $91, $97, $95, $50, $a4, $9f, $50, $95, $91
+db $93, $98, $50, $95, $9e, $95, $9d, $a9, $5e, $02
+
+db $70, $71, $50, $98, $91, $a2, $a3, $98, $50, $92, $9c, $91, $a3, $a4, $50, $9f
+db $96, $50, $95, $9e, $95, $a2, $97, $a9, $50, $95, $9e, $97, $a5, $9c, $96, $a3
+db $50, $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $5c, $50, $99, $9e, $96, $9c
+db $99, $93, $a4, $99, $9e, $97, $50, $91, $92, $9f, $a5, $a4, $02, $08, $df, $d1
+db $ee, $ff, $50, $69, $60, $50, $a0, $9f, $99, $9e, $a4, $a3, $50, $9f, $96, $50
+db $94, $91, $9d, $91, $97, $95, $5e, $01, $70, $79, $a4, $50, $9d, $91, $a9, $50
+db $99, $9e, $96, $9c, $99, $93, $a4, $50, $91, $92, $9f, $a5, $a4, $50, $98, $91
+db $9c, $96, $50, $91, $a3, $50, $9d, $a5, $93, $98, $50, $a4, $9f, $50, $9f, $a4
+db $98, $95, $a2, $50, $95, $9e, $95, $9d, $99, $95, $a3, $5e, $02, $08, $df, $d1
+db $ee, $ff, $50, $61, $68, $60, $50, $a0, $9f, $99, $9e, $a4, $a3, $50, $9f, $96
+db $50, $94, $91, $9d, $91, $97, $95, $5e, $01, $70, $79, $a4, $50, $9d, $91, $a9
+db $50, $99, $9e, $96, $9c, $99, $93, $a4, $50, $91, $92, $9f, $a5, $a4, $50, $98
+db $91, $9c, $96, $50, $91, $a3, $50, $9d, $a5, $93, $98, $50, $a4, $9f, $50, $9f
+db $a4, $98, $95, $a2, $50, $95, $9e, $95, $9d, $99, $95, $a3, $5e, $02, $08, $df
+db $d1, $ee, $ff, $50, $62, $67, $60, $50, $a0, $9f, $99, $9e, $a4, $a3, $50, $9f
+db $96, $50, $94, $91, $9d, $91, $97, $95, $5e, $01, $70, $79, $a4, $50, $9d, $91
+db $a9, $50, $99, $9e, $96, $9c, $99, $93, $a4, $50, $91, $92, $9f, $a5, $a4, $50
+db $98, $91, $9c, $96, $50, $91, $a3, $50, $9d, $a5, $93, $98, $50, $a4, $9f, $50
+db $9f, $a4, $98, $95, $a2, $50, $95, $9e, $95, $9d, $99, $95, $a3, $5e, $02, $08
+db $df, $d1, $ee, $ff, $50, $63, $66, $60, $50, $a0, $9f, $99, $9e, $a4, $a3, $50
+db $9f, $96, $50, $94, $91, $9d, $91, $97, $95, $5e, $01, $70, $79, $a4, $50, $9d
+db $91, $a9, $50, $99, $9e, $96, $9c, $99, $93, $a4, $50, $91, $92, $9f, $a5, $a4
+db $50, $98, $91, $9c, $96, $50, $91, $a3, $50, $9d, $a5, $93, $98, $50, $a4, $9f
+db $50, $9f, $a4, $98, $95, $a2, $50, $95, $9e, $95, $9d, $99, $95, $a3, $5e, $02;PSI blast help text
+
+db $70, $71, $50, $a3, $a0, $91, $a2, $9b, $50, $9f, $96, $50, $95, $9e, $95, $a2, $97
+db $a9, $50, $99, $a3, $50, $a3, $98, $9f, $a4, $50, $91, $a4, $50, $9f, $9e, $95
+db $50, $95, $9e, $95, $9d, $a9, $5c, $50, $99, $9e, $96, $9c, $99, $93, $a4, $99
+db $9e, $97, $50, $91, $92, $9f, $a5, $a4, $50, $61, $62, $60, $50, $a0, $9f, $99
+db $9e, $a4, $a3, $50, $9f, $96, $50, $94, $91, $9d, $91, $97, $95, $5e, $01, $70
+db $84, $98, $95, $50, $96, $91, $a3, $a4, $95, $a2, $50, $a9, $9f, $a5, $50, $91
+db $a2, $95, $5c, $50, $a4, $98, $95, $50, $9d, $9f, $a2, $95, $50, $9c, $99, $9b
+db $95, $9c, $a9, $50, $99, $a4, $50, $99, $a3, $50, $96, $9f, $a2, $50, $a4, $98
+db $95, $50, $a3, $a0, $91, $a2, $9b, $50, $a4, $9f, $50, $98, $99, $a4, $5e, $02
+db $70, $7f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $99, $a3, $50, $a3, $a4
+db $a2, $a5, $93, $9b, $50, $92, $a9, $50, $02, $a3, $a0, $91, $a2, $9b, $a3, $50
+db $9f, $96, $50, $95, $9e, $95, $a2, $97, $a9, $50, $96, $9f, $a2, $50, $91, $92
+db $9f, $a5, $a4, $50, $61, $62, $60, $50, $a0, $9f, $99, $9e, $a4, $a3, $50, $9f
+db $96, $50, $94, $91, $9d, $91, $97, $95, $50, $95, $91, $93, $98, $5e, $01, $70
+db $84, $98, $95, $50, $a3, $a0, $91, $a2, $9b, $a3, $50, $91, $a2, $95, $50, $9d
+db $9f, $a2, $95, $50, $9c, $99, $9b, $95, $9c, $a9, $50, $a4, $9f, $50, $98, $99
+db $a4, $50, $a4, $98, $95, $50, $96, $91, $a3, $a4, $95, $a2, $50, $a9, $9f, $a5
+db $50, $91, $a2, $95, $50, $a4, $98, $91, $9e, $50, $a4, $98, $95, $50, $a4, $91
+db $a2, $97, $95, $a4, $5e, $02, $08, $f0, $d3, $ee, $ff, $63, $50, $08, $09, $d4
+db $ee, $ff, $02, $08, $f0, $d3, $ee, $ff, $65, $50, $08, $09, $d4, $ee, $ff, $02
+db $08, $f0, $d3, $ee, $ff, $61, $65, $50, $08, $09, $d4, $ee, $ff, $02;PSI Missile help text
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $70, $74, $91, $9d, $91, $97, $95, $a3, $50, $91, $9c, $9c, $50, $95, $9e, $95
+db $9d, $99, $95, $a3, $50, $a7, $98, $95, $9e, $50, $a5, $a3, $95, $94, $50, $99
+db $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $02, $01, $50, $6c, $1c, $05, $93
+db $6e, $03, $00, $08, $ae, $d4, $ee, $ff, $03, $00, $08, $ab, $6d, $c5, $00, $13
+db $02;psycho bomb
+
+db $70, $77, $a2, $95, $91, $a4, $9c, $a9, $50, $94, $91, $9d, $91, $97, $95, $a3
+db $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3, $50, $a7, $98, $95
+db $9e, $50, $a5, $a3, $95, $94, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95
+db $5e, $02, $01, $50, $6c, $1c, $05, $94, $6e, $03, $00, $08, $ef, $d4, $ee, $ff
+db $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Mad psycho bomb
+
+db $01, $50, $6c, $1c, $05, $93, $6e, $03, $00, $70, $87, $98, $95, $9e, $50, $a5
+db $a3, $95, $94, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5c, $50, $99
+db $a4, $50, $93, $91, $9e, $50, $9d, $91, $9b, $95, $50, $91, $9c, $9c, $50, $95
+db $9e, $95, $9d, $99, $95, $a3, $50, $93, $a2, $a9, $50, $a5, $9e, $93, $9f, $9e
+db $a4, $a2, $9f, $9c, $9c, $91, $92, $9c, $a9, $5c, $03, $00, $70, $9f, $a2, $50
+db $9f, $93, $93, $91, $a3, $99, $9f, $9e, $91, $9c, $9c, $a9, $50, $96, $95, $95
+db $9c, $50, $a3, $a4, $a2, $91, $9e, $97, $95, $5e, $03, $00, $08, $ab, $6d, $c5
+db $00, $13, $02;Flash bomb
+
+db $01, $50, $6c, $1c, $05, $94, $6e, $03, $00, $70, $87, $98, $95, $9e, $50, $a5
+db $a3, $95, $94, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5c, $50, $99
+db $a4, $50, $93, $91, $9e, $50, $9d, $91, $9b, $95, $50, $91, $9c, $9c, $50, $95
+db $9e, $95, $9d, $99, $95, $a3, $50, $93, $a2, $a9, $50, $a5, $9e, $93, $9f, $9e
+db $a4, $a2, $9f, $9c, $9c, $91, $92, $9c, $a9, $5c, $03, $00, $70, $91, $9e, $94
+db $50, $a7, $99, $9c, $9c, $50, $9f, $96, $a4, $95, $9e, $50, $94, $95, $a3, $a4
+db $a2, $9f, $a9, $50, $91, $9e, $50, $95, $9e, $95, $9d, $a9, $50, $99, $9e, $50
+db $91, $50, $a3, $99, $9e, $97, $9c, $95, $50, $a3, $a4, $a2, $99, $9b, $95, $5e
+db $03, $08, $ab, $6d, $c5, $00, $13, $02;Flash bomb 2
+
+db $74, $95, $91, $9c, $a3, $50, $96, $99, $a2, $95, $50, $94, $91, $9d, $91, $97
+db $95, $50, $a4, $9f, $50, $91, $50, $a2, $9f, $a7, $50, $9f, $96, $50, $95, $9e
+db $95, $9d, $99, $95, $a3, $02, $01, $50, $6c, $1c, $05, $93, $6e, $03, $00, $70
+db $08, $33, $d6, $ee, $ff, $50, $a7, $98, $95, $9e, $50, $a5, $a3, $95, $94, $50
+db $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $ab, $6d, $c5
+db $00, $13, $02;Fire bomb
+
+db $74, $95, $91, $9c, $a3, $50, $98, $95, $91, $a6, $a9, $5d, $94, $a5, $a4, $a9
+db $50, $96, $99, $a2, $95, $50, $94, $91, $9d, $91, $97, $95, $50, $a4, $9f, $50
+db $91, $50, $a2, $9f, $a7, $50, $9f, $96, $50, $95, $9e, $95, $9d, $99, $95, $a3
+db $50, $a7, $98, $95, $9e, $50, $a5, $a3, $95, $94, $50, $99, $9e, $50, $92, $91
+db $a4, $a4, $9c, $95, $5e, $02, $01, $50, $6c, $1c, $05, $94, $6e, $03, $00, $70
+db $08, $86, $d6, $ee, $ff, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Fire bomb 2
+
+db $74, $95, $91, $9c, $a3, $50, $99, $93, $95, $50, $94, $91, $9d, $91, $97, $95
+db $50, $a4, $9f, $50, $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $a7, $98
+db $95, $9e, $50, $a5, $a3, $95, $94, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c
+db $95, $5e, $02, $7f, $93, $93, $91, $a3, $99, $9f, $9e, $91, $9c, $9c, $a9, $5c
+db $50, $99, $a4, $50, $9d, $91, $a9, $50, $96, $a2, $95, $95, $aa, $95, $50, $a4
+db $98, $95, $50, $95, $9e, $95, $9d, $a9, $50, $a3, $9f, $9c, $99, $94, $5e, $02
+db $01, $50, $6c, $1c, $05, $93, $6e, $03, $00, $70, $08, $e4, $d6, $ee, $ff, $03
+db $00, $70, $08, $17, $d7, $ee, $ff, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Ice bomb
+
+db $74, $95, $91, $9c, $a3, $50, $98, $95, $91, $a6, $a9, $50, $99, $93, $95, $50
+db $94, $91, $9d, $91, $97, $95, $50, $a4, $9f, $50, $9f, $9e, $95, $50, $95, $9e
+db $95, $9d, $a9, $50, $a7, $98, $95, $9e, $50, $a5, $a3, $95, $94, $50, $99, $9e
+db $50, $92, $91, $a4, $a4, $9c, $95, $5e, $02, $01, $50, $6c, $1c, $05, $94, $6e
+db $03, $00, $70, $08, $64, $d7, $ee, $ff, $03, $00, $70, $08, $17, $d7, $ee, $ff
+db $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Ice bomb 2
+
+db $87, $98, $95, $9e, $50, $a5, $a3, $95, $94, $50, $99, $9e, $50, $92, $91, $a4
+db $a4, $9c, $95, $5c, $50, $a4, $98, $95, $50, $95, $9e, $95, $9d, $a9, $50, $99
+db $a3, $50, $a3, $a4, $a2, $a5, $93, $9b, $50, $a7, $99, $a4, $98, $50, $91, $50
+db $9c, $99, $97, $98, $a4, $99, $9e, $97, $50, $92, $9f, $9c, $a4, $5e, $02, $79
+db $a4, $50, $9d, $91, $a9, $50, $9d, $99, $a3, $a3, $50, $99, $96, $50, $a4, $98
+db $95, $a2, $95, $50, $91, $a2, $95, $50, $96, $95, $a7, $50, $95, $9e, $95, $9d
+db $99, $95, $a3, $5e, $02, $01, $50, $6c, $1c, $05, $93, $6e, $03, $00, $70, $08
+db $bd, $d7, $ee, $ff, $03, $00, $01, $70, $08, $fc, $d7, $ee, $ff, $03, $00, $08
+db $ab, $6d, $c5, $00, $13, $02;Thunder bomb
+
+db $87, $98, $95, $9e, $50, $a5, $a3, $95, $94, $50, $99, $9e, $50, $92, $91, $a4
+db $a4, $9c, $95, $5c, $50, $a4, $98, $95, $50, $95, $9e, $95, $9d, $a9, $50, $99
+db $a3, $50, $a3, $a4, $a2, $a5, $93, $9b, $50, $a7, $99, $a4, $98, $50, $91, $50
+db $9c, $99, $97, $98, $a4, $99, $9e, $97, $50, $92, $9f, $9c, $a4, $50, $a4, $98
+db $a2, $95, $95, $50, $a4, $99, $9d, $95, $a3, $5e, $02, $01, $50, $6c, $1c, $05
+db $94, $6e, $03, $00, $70, $08, $43, $d8, $ee, $ff, $03, $00, $70, $08, $fc, $d7
+db $ee, $ff, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02; Thunder bomb 2
+
+db $01, $50, $6c, $1c, $05, $93, $6e, $03, $00, $70, $71, $50, $a0, $9f, $a7, $95
+db $a2, $96, $a5, $9c, $50, $a7, $95, $91, $a0, $9f, $9e, $50, $a4, $98, $91, $a4
+db $50, $94, $95, $91, $9c, $a3, $50, $98, $95, $91, $a6, $a9, $50, $98, $95, $91
+db $a6, $a9, $50, $94, $91, $9d, $91, $97, $95, $50, $a4, $9f, $50, $91, $9c, $9c
+db $50, $95, $9e, $95, $9d, $99, $95, $a3, $50, $a7, $98, $95, $9e, $50, $a5, $a3
+db $95, $94, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08
+db $ab, $6d, $c5, $00, $13, $02;Star bomb 1
+
+db $01, $50, $6c, $1c, $05, $94, $6e, $03, $00, $70, $84, $98, $95, $50, $a3, $a4
+db $a2, $9f, $9e, $97, $95, $a3, $a4, $50, $a7, $95, $91, $a0, $9f, $9e, $50, $91
+db $a2, $9f, $a5, $9e, $94, $5e, $03, $00, $70, $74, $95, $91, $9c, $a3, $50, $95
+db $a8, $a4, $a2, $95, $9d, $95, $50, $94, $91, $9d, $91, $97, $95, $50, $a4, $9f
+db $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3, $50, $a7, $98, $95
+db $9e, $50, $a5, $a3, $95, $94, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95
+db $5e, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;star bomb 2
+
+db $74, $91, $9d, $91, $97, $95, $a3, $50, $9f, $9e, $95, $50, $95, $9e, $95, $9d
+db $a9, $50, $a7, $98, $95, $9e, $50, $a5, $a3, $95, $94, $50, $99, $9e, $50, $92
+db $91, $a4, $a4, $9c, $95, $5e, $02, $01, $50, $6c, $1c, $05, $94, $6e, $03, $00
+db $70, $08, $7e, $d9, $ee, $ff, $03, $00, $70, $79, $a4, $50, $9d, $91, $a9, $50
+db $92, $95, $50, $91, $50, $94, $a5, $94, $5e, $03, $00, $08, $ab, $6d, $c5, $00
+db $13, $02;Missile bomb 1
+
+db $77, $a2, $95, $91, $a4, $9c, $a9, $50, $94, $91, $9d, $91, $97, $95, $a3, $50
+db $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $a7, $98, $95, $9e, $50, $a5
+db $a3, $95, $94, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $02, $01
+db $50, $6c, $1c, $05, $94, $6e, $03, $00, $70, $08, $d0, $d9, $ee, $ff, $03, $00
+db $70, $79, $a4, $50, $99, $a3, $50, $9d, $9f, $a2, $95, $50, $95, $96, $96, $95
+db $93, $a4, $99, $a6, $95, $50, $91, $97, $91, $99, $9e, $a3, $a4, $50, $95, $9e
+db $95, $9d, $99, $95, $a3, $50, $a3, $9c, $9f, $a7, $95, $a2, $50, $a4, $98, $91
+db $9e, $50, $a9, $9f, $a5, $5e, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Missile bomb 2
+
+db $01, $50, $6c, $1c, $05, $85, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $08, $ae
+db $d4, $ee, $ff, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Psychic bazooka 1
+
+db $01, $50, $6c, $1c, $05, $86, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $08, $ef
+db $d4, $ee, $ff, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Psychic bazooka 2
+
+db $01, $50, $6c, $1c, $05, $85, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $79
+db $a4, $50, $93, $91, $9e, $50, $9d, $91, $9b, $95, $50, $a4, $98, $95, $50, $95
+db $9e, $95, $9d, $a9, $50, $93, $a2, $a9, $5c, $50, $9f, $a2, $50, $9f, $93, $93
+db $91, $a3, $99, $9f, $9e, $91, $9c, $9c, $a9, $50, $96, $95, $95, $9c, $50, $a3
+db $a4, $a2, $91, $9e, $97, $95, $5e, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Flash bazooka 1
+
+db $01, $50, $6c, $1c, $05, $86, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $75
+db $9d, $99, $a4, $a3, $50, $91, $50, $98, $95, $91, $a6, $a9, $5d, $94, $a5, $a4
+db $a9, $50, $96, $9c, $91, $a3, $98, $50, $a4, $98, $91, $a4, $50, $98, $91, $a3
+db $50, $91, $50, $98, $99, $97, $98, $50, $93, $98, $91, $9e, $93, $95, $50, $9f
+db $96, $50, $9b, $9e, $9f, $93, $9b, $99, $9e, $97, $50, $9f, $a5, $a4, $50, $95
+db $9e, $95, $9d, $99, $95, $a3, $5e, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Flash bazooka 2
+
+db $01, $50, $6c, $1c, $05, $85, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $08
+db $33, $d6, $ee, $ff, $50, $a7, $98, $95, $9e, $50, $a5, $a3, $95, $94, $50, $99
+db $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $08, $9b, $6d, $c5, $ff, $13
+db $02;Fire bazooka 1
+
+db $01, $50, $6c, $1c, $05, $86, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $08
+db $86, $d6, $ee, $ff, $03, $00, $08, $9b, $6d, $c5, $ff, $13, $02;Fire bazooka 2
+
+db $01, $50, $6c, $1c, $05, $85, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $08
+db $e4, $d6, $ee, $ff, $03, $00, $08, $9b, $6d, $c5, $ff, $13, $02;Freeze bazooka 1
+
+db $01, $50, $6c, $1c, $05, $86, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $08
+db $64, $d7, $ee, $ff, $03, $00, $08, $9b, $6d, $c5, $ff, $13, $02;Freeze bazooka 2
+
+db $01, $50, $6c, $1c, $05, $85, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $08
+db $bd, $d7, $ee, $ff, $03, $00, $70, $08, $fc, $d7, $ee, $ff, $03, $00, $08, $9b
+db $6d, $c5, $ff, $13, $02;Thunder bazooka 1
+
+db $01, $50, $6c, $1c, $05, $86, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $08
+db $43, $d8, $ee, $ff, $03, $00, $70, $08, $fc, $d7, $ee, $ff, $03, $00, $08, $9b
+db $6d, $c5, $ff, $13, $02;Thunder bazooka 2
+
+db $01, $50, $6c, $1c, $05, $85, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $08
+db $7e, $d9, $ee, $ff, $03, $00, $70, $79, $a4, $50, $9d, $91, $a9, $50, $9e, $9f
+db $a4, $50, $91, $9c, $a7, $91, $a9, $a3, $50, $a7, $9f, $a2, $9b, $5e, $03, $00
+db $08, $9b, $6d, $c5, $ff, $13, $02;Missile bazooka 1
+
+db $01, $50, $6c, $1c, $05, $86, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $08
+db $d0, $d9, $ee, $ff, $03, $00, $70, $7d, $9f, $a2, $95, $50, $9d, $99, $a3, $a3
+db $99, $9c, $95, $a3, $50, $91, $a2, $95, $50, $9c, $99, $9b, $95, $9c, $a9, $50
+db $a4, $9f, $50, $98, $99, $a4, $50, $99, $96, $50, $a9, $9f, $a5, $50, $91, $a2
+db $95, $50, $96, $91, $a3, $a4, $95, $a2, $50, $a4, $98, $91, $9e, $50, $a4, $98
+db $95, $50, $95, $9e, $95, $9d, $a9, $5e, $03, $00, $08, $9b, $6d, $c5, $ff, $13
+db $02;Missile bazooka 2
+
+db $78, $95, $91, $a6, $99, $9c, $a9, $50, $94, $91, $9d, $91, $97, $95, $a3, $50
+db $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3, $50, $a7, $98, $95, $9e
+db $50, $a5, $a3, $95, $94, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e
+db $02, $01, $50, $6c, $1c, $05, $86, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70
+db $08, $a1, $dc, $ee, $ff, $03, $00, $08, $9b, $6d, $c5, $ff, $13, $02;Starstorm bazooka 2
+
+db $15, $03, $03, $18, $04, $05, $0B, $00, $0A, $5E, $9D, $C7;Not bazooka, fix enemies disappearing on rnwy5
+
+db $01, $50, $6c, $1c, $05, $0d, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b
+db $95, $9e, $50, $a2, $91, $94, $99, $9f, $5e, $00, $03, $08, $6f, $6b, $c5, $00
+db $13, $02;Broken radio
+
+db $01, $50, $6c, $1c, $05, $0d, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b
+db $95, $9e, $50, $93, $91, $9d, $95, $a2, $91, $5e, $03, $00, $08, $6f, $6b, $c5
+db $00, $13, $02;Broken camera
+
+db $01, $50, $6c, $1c, $05, $0d, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b
+db $95, $9e, $50, $a4, $9f, $91, $a3, $a4, $95, $a2, $5e, $03, $00, $08, $6f, $6b
+db $c5, $00, $13, $02;Broken toaster
+
+db $01, $50, $6c, $1c, $05, $0d, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b
+db $95, $9e, $50, $93, $9f, $9f, $9c, $99, $9e, $97, $50, $a5, $9e, $99, $a4, $50
+db $96, $a2, $9f, $9d, $50, $91, $50, $9d, $99, $9e, $99, $50, $96, $a2, $99, $94
+db $97, $95, $5e, $03, $00, $08, $6f, $6b, $c5, $00, $13, $02;Broken minifridge
+
+db $01, $50, $6c, $1c, $05, $0d, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b
+db $95, $9e, $50, $a3, $a0, $a2, $99, $9e, $97, $5e, $03, $00, $08, $6f, $6b, $c5
+db $00, $13, $02;Broken spring
+
+db $01, $50, $6c, $1c, $05, $0d, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b
+db $95, $9e, $50, $98, $91, $9e, $94, $98, $95, $9c, $94, $50, $a2, $91, $94, $91
+db $a2, $5e, $03, $00, $08, $6f, $6b, $c5, $00, $13, $02;Broken radar
+
+db $01, $50, $6c, $1c, $05, $0d, $6e, $03, $00, $70, $71, $50, $a0, $91, $a2, $a4
+db $50, $96, $a2, $9f, $9d, $50, $91, $50, $92, $a2, $9f, $9b, $95, $9e, $50, $a6
+db $91, $93, $a5, $a5, $9d, $50, $93, $9c, $95, $91, $9e, $95, $a2, $5e, $03, $00
+db $08, $6f, $6b, $c5, $00, $13, $02;Broken vacuum
+
+db $01, $50, $6c, $1c, $05, $90, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $08, $ae
+db $d4, $ee, $ff, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Special BR
+
+db $01, $50, $6c, $1c, $05, $91, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $72
+db $9c, $91, $a3, $a4, $a3, $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95
+db $a3, $50, $a7, $99, $a4, $98, $50, $a0, $a3, $a9, $93, $98, $99, $93, $50, $95
+db $9e, $95, $a2, $97, $a9, $5e, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Special BBR
+
+db $01, $50, $6C, $1c, $05, $92, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $85, $9e, $9c, $95
+db $91, $a3, $98, $95, $a3, $50, $91, $50, $a7, $91, $a6, $95, $50, $9f, $96, $50
+db $a0, $a3, $a9, $93, $98, $99, $93, $50, $95, $9e, $95, $a2, $97, $a9, $50, $a4
+db $98, $91, $a4, $50, $97, $a2, $95, $91, $a4, $9c, $a9, $50, $94, $91, $9d, $91
+db $97, $95, $a3, $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3, $5e
+db $03, $00, $08, $ab, $6d, $c5, $ff, $13, $02;Special MBR
+
+db $01, $50, $6c, $1c, $05, $90, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $7d
+db $91, $9b, $95, $a3, $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3
+db $50, $93, $a2, $a9, $50, $a5, $9e, $93, $9f, $9e, $a4, $a2, $9f, $9c, $9c, $91
+db $92, $9c, $a9, $5c, $50, $9f, $a2, $50, $9f, $93, $93, $91, $a3, $99, $9f, $9e
+db $91, $9c, $9c, $a9, $50, $96, $95, $95, $9c, $50, $a3, $a4, $a2, $91, $9e, $97
+db $95, $5e, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Flash BR
+
+db $01, $50, $6c, $1c, $05, $91, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $75
+db $9d, $99, $a4, $95, $a3, $50, $91, $50, $92, $a2, $99, $97, $98, $a4, $50, $96
+db $9c, $91, $a3, $98, $50, $a4, $98, $91, $a4, $50, $93, $91, $9e, $50, $9d, $91
+db $9b, $95, $50, $95, $9e, $95, $9d, $99, $95, $a3, $50, $93, $a2, $a9, $50, $a5
+db $9e, $93, $9f, $9e, $a4, $a2, $9f, $9c, $9c, $91, $92, $9c, $a9, $5c, $03, $00
+db $70, $9f, $a2, $50, $9f, $93, $93, $91, $a3, $99, $9f, $9e, $91, $9c, $9c, $a9
+db $50, $92, $95, $50, $94, $95, $a3, $a4, $a2, $9f, $a9, $95, $94, $5e, $03, $00
+db $08, $ab, $6d, $c5, $ff, $13, $02;Flash BBR
+
+db $01, $50, $6c, $1c, $05, $92, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $75
+db $9d, $99, $a4, $a3, $50, $91, $50, $97, $9c, $9f, $a2, $99, $9f, $a5, $a3, $50
+db $9c, $99, $97, $98, $a4, $50, $a4, $98, $91, $a4, $50, $98, $91, $a3, $50, $91
+db $50, $98, $99, $97, $98, $50, $a0, $a2, $9f, $92, $91, $92, $99, $9c, $99, $a4
+db $a9, $50, $9f, $96, $50, $94, $95, $a3, $a4, $a2, $9f, $a9, $99, $9e, $97, $50
+db $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3, $5e, $03, $00, $08, $ab
+db $6d, $c5, $ff, $13, $02;Flash MBR
+
+db $01, $50, $6c, $1c, $05, $90, $6e, $03, $00, $08, $b7, $6d, $c5, $03, $70, $08
+db $33, $d6, $ee, $ff, $5e, $03, $00, $08, $ab, $6d, $c5, $ff, $13, $02;Fire BR
+
+db $01, $50, $6c, $1c, $05, $91, $6e, $03, $00, $08, $b7, $6d, $c5, $ff, $70, $82
+db $9f, $91, $a3, $a4, $a3, $50, $91, $50, $a2, $9f, $a7, $50, $9f, $96, $50, $95
+db $9e, $95, $9d, $99, $95, $a3, $5e, $03, $00, $08, $ab, $6d, $c5, $ff, $13, $02;Fire BBR
+
+db $01, $50, $6c, $1c, $05, $92, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $82
+db $95, $91, $9c, $9c, $a9, $50, $a2, $9f, $91, $a3, $a4, $a3, $50, $91, $50, $a2
+db $9f, $a7, $50, $9f, $96, $50, $95, $9e, $95, $9d, $99, $95, $a3, $5e, $03, $00
+db $08, $ab, $6d, $c5, $ff, $13, $02;Fire MBR
+
+db $01, $50, $6c, $1c, $05, $90, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $08
+db $e4, $d6, $ee, $ff, $03, $00, $08, $ab, $6d, $c5, $ff, $13, $02;Freeze BR
+
+db $01, $50, $6c, $1c, $05, $91, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $83
+db $a0, $9c, $91, $a3, $98, $95, $a3, $50, $99, $93, $95, $5d, $93, $9f, $9c, $94
+db $50, $9e, $99, $a4, $a2, $9f, $97, $95, $9e, $50, $9f, $9e, $50, $91, $9e, $50
+db $95, $9e, $95, $9d, $a9, $5e, $03, $00, $70, $7f, $93, $93, $91, $a3, $99, $9f
+db $9e, $91, $9c, $9c, $a9, $5c, $50, $99, $a4, $50, $9d, $91, $a9, $50, $96, $a2
+db $95, $95, $aa, $95, $50, $a4, $98, $95, $9d, $50, $a3, $9f, $9c, $99, $94, $5e
+db $03, $00, $08, $ab, $6d, $c5, $ff, $13, $02;Freeze BBR
+
+db $01, $50, $6c, $1c, $05, $92, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $83
+db $a0, $9c, $91, $a3, $98, $95, $a3, $50, $91, $50, $9c, $9f, $a4, $50, $9f, $96
+db $50, $99, $93, $95, $5d, $93, $9f, $9c, $94, $50, $9e, $99, $a4, $a2, $9f, $97
+db $95, $9e, $50, $9f, $9e, $50, $91, $9e, $50, $95, $9e, $95, $9d, $a9, $5e, $03
+db $00, $70, $7f, $93, $93, $91, $a3, $99, $9f, $9e, $91, $9c, $9c, $a9, $5c, $50
+db $99, $a4, $50, $9d, $91, $a9, $50, $96, $a2, $95, $95, $aa, $95, $50, $a4, $98
+db $95, $9d, $50, $a3, $9f, $9c, $99, $94, $5e, $03, $00, $08, $ab, $6d, $c5, $ff
+db $13, $02;Freeze MBR
+
+db $01, $50, $6c, $1c, $05, $90, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $08
+db $bd, $d7, $ee, $ff, $03, $00, $01, $70, $08, $fc, $d7, $ee, $ff, $03, $00, $08
+db $ab, $6d, $c5, $00, $13, $02;Thunder BR
+
+db $01, $50, $6c, $1c, $05, $91, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $83
+db $98, $9f, $9f, $a4, $a3, $50, $a4, $a7, $9f, $50, $a3, $a0, $91, $a2, $9b, $a3
+db $50, $9f, $a5, $a4, $50, $a4, $9f, $a7, $91, $a2, $94, $a3, $50, $a4, $98, $95
+db $50, $95, $9e, $95, $9d, $a9, $5e, $03, $00, $70, $84, $98, $95, $a9, $50, $9d
+db $91, $a9, $50, $9d, $99, $a3, $a3, $50, $99, $96, $50, $a4, $98, $95, $a2, $95
+db $50, $91, $a2, $95, $50, $96, $95, $a7, $50, $95, $9e, $95, $9d, $99, $95, $a3
+db $5e, $03, $00, $08, $ab, $6d, $c5, $ff, $13, $02;Thunder BBR
+
+db $01, $50, $6c, $1c, $05, $92, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $83
+db $98, $9f, $9f, $a4, $a3, $50, $96, $9f, $a5, $a2, $50, $a3, $a0, $91, $a2, $9b
+db $a3, $50, $9f, $a5, $a4, $50, $a4, $9f, $a7, $91, $a2, $94, $a3, $50, $a4, $98
+db $95, $50, $95, $9e, $95, $9d, $a9, $5e, $03, $00, $70, $84, $98, $95, $a9, $50
+db $9d, $91, $a9, $50, $9d, $99, $a3, $a3, $50, $99, $96, $50, $a4, $98, $95, $a2
+db $95, $50, $91, $a2, $95, $50, $96, $95, $a7, $50, $95, $9e, $95, $9d, $99, $95
+db $a3, $5e, $03, $00, $08, $ab, $6d, $c5, $ff, $13, $02;Thunder MBR
+
+db $01, $50, $6c, $1c, $05, $90, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $08, $ae
+db $d4, $ee, $ff, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Starstorm BR
+
+db $01, $50, $6c, $1c, $05, $91, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $82
+db $91, $99, $9e, $a3, $50, $a3, $a4, $91, $a2, $a3, $50, $94, $9f, $a7, $9e, $50
+db $9f, $9e, $50, $95, $9e, $95, $9d, $99, $95, $a3, $5e, $03, $00, $08, $ab, $6d
+db $c5, $ff, $13, $02;Starstorm BBR
+
+db $01, $50, $6c, $1c, $05, $92, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $72
+db $9f, $9d, $92, $91, $a2, $94, $a3, $50, $95, $9e, $95, $9d, $99, $95, $a3, $50
+db $a7, $99, $a4, $98, $50, $91, $50, $a4, $9f, $a2, $a2, $95, $9e, $a4, $50, $9f
+db $96, $50, $a3, $a4, $91, $a2, $a3, $5e, $03, $00, $70, $80, $9f, $a3, $a3, $99
+db $92, $9c, $a9, $50, $a4, $98, $95, $50, $a3, $a4, $a2, $9f, $9e, $97, $95, $a3
+db $a4, $50, $a7, $95, $91, $a0, $9f, $9e, $50, $91, $a2, $9f, $a5, $9e, $94, $5e
+db $03, $00, $08, $ab, $6d, $c5, $ff, $13, $02;Starstorm MBR
+
+db $01, $50, $6c, $1c, $05, $90, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $08, $c5
+db $6d, $c5, $ff, $0a, $b5, $4e, $c5;Bomb BR
+
+db $01, $50, $6c, $1c, $05, $91, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $0a, $00
+db $4f, $c5;Bomb BBR
+
+db $01, $50, $6c, $1c, $05, $92, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $75
+db $9e, $97, $a5, $9c, $96, $a3, $50, $91, $9e, $50, $95, $9e, $95, $9d, $a9, $50
+db $99, $9e, $50, $91, $50, $9d, $91, $a3, $a3, $99, $a6, $95, $50, $95, $a8, $a0
+db $9c, $9f, $a3, $99, $9f, $9e, $5e, $03, $00, $70, $84, $98, $95, $50, $92, $9c
+db $91, $a3, $a4, $50, $99, $a3, $50, $a0, $9f, $a7, $95, $a2, $96, $a5, $9c, $50
+db $95, $9e, $9f, $a5, $97, $98, $50, $a4, $9f, $50, $91, $96, $96, $95, $93, $a4
+db $50, $9e, $95, $91, $a2, $92, $a9, $50, $95, $9e, $95, $9d, $99, $95, $a3, $5e
+db $03, $00, $08, $ab, $6d, $c5, $ff, $13, $02;Bomb MBR
+
+db $70, $79, $9e, $93, $a2, $95, $91, $a3, $95, $50, $9f, $9e, $95, $50, $a0, $95
+db $a2, $a3, $9f, $9e, $57, $a3, $50, $74, $95, $96, $95, $9e, $a3, $95, $50, $96
+db $9f, $a2, $50, $a4, $98, $95, $50, $94, $a5, $a2, $91, $a4, $99, $9f, $9e, $50
+db $9f, $96, $50, $a4, $98, $95, $50, $93, $a5, $a2, $a2, $95, $9e, $a4, $50, $92
+db $91, $a4, $a4, $9c, $95, $5e, $00, $08, $0d, $6b, $c5, $ff, $02;def up a
+
+db $70, $79, $9e, $93, $a2, $95, $91, $a3, $95, $50, $95, $a6, $95, $a2, $a9, $9f
+db $9e, $95, $57, $a3, $50, $74, $95, $96, $95, $9e, $a3, $95, $50, $96, $9f, $a2
+db $50, $a4, $98, $95, $50, $94, $a5, $a2, $91, $a4, $99, $9f, $9e, $50, $9f, $96
+db $50, $a4, $98, $95, $50, $93, $a5, $a2, $a2, $95, $9e, $a4, $50, $92, $91, $a4
+db $a4, $9c, $95, $5e, $00, $08, $0d, $6b, $c5, $ff, $02;def up o
+
+db $70, $74, $a2, $91, $99, $9e, $a3, $50, $78, $80, $50, $96, $a2, $9f, $9d, $50
+db $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $5e, $00, $70, $84, $98, $95, $50
+db $9d, $9f, $a2, $95, $50, $78, $80, $50, $91, $9e, $50, $95, $9e, $95, $9d, $a9
+db $50, $98, $91, $a3, $5c, $50, $a4, $98, $95, $50, $9d, $9f, $a2, $95, $50, $a9
+db $9f, $a5, $50, $97, $95, $a4, $5e, $02;Drain a
+
+db $70, $74, $a2, $91, $99, $9e, $a3, $50, $a3, $9f, $9d, $95, $50, $78, $80, $50
+db $96, $a2, $9f, $9d, $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3
+db $5e, $00, $70, $84, $98, $95, $50, $9d, $9f, $a2, $95, $50, $78, $80, $50, $a4
+db $98, $95, $50, $95, $9e, $95, $9d, $99, $95, $a3, $50, $98, $91, $a6, $95, $5c
+db $50, $a4, $98, $95, $50, $9d, $9f, $a2, $95, $50, $a9, $9f, $a5, $50, $97, $95
+db $a4, $5e, $02;Drain o
+
+db $70, $7d, $91, $9b, $95, $a3, $50, $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9
+db $50, $a5, $9e, $91, $92, $9c, $95, $50, $a4, $9f, $50, $93, $9f, $9e, $93, $95
+db $9e, $a4, $a2, $91, $a4, $95, $50, $96, $9f, $a2, $50, $91, $50, $a3, $98, $9f
+db $a2, $a4, $50, $a4, $99, $9d, $95, $5e, $02;disable a
+
+db $70, $7d, $91, $9b, $95, $a3, $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99
+db $95, $a3, $50, $a5, $9e, $91, $92, $9c, $95, $50, $a4, $9f, $50, $93, $9f, $9e
+db $93, $95, $9e, $a4, $a2, $91, $a4, $95, $50, $96, $9f, $a2, $50, $91, $50, $a3
+db $98, $9f, $a2, $a4, $50, $a4, $99, $9d, $95, $5e, $02;disable o
+
+db $70, $83, $9f, $9c, $99, $94, $99, $96, $99, $95, $a3, $50, $9f, $9e, $95, $50
+db $95, $9e, $95, $9d, $a9, $5e, $02;Stop a
+
+db $70, $83, $9f, $9c, $99, $94, $99, $96, $99, $95, $a3, $50, $91, $9c, $9c, $50
+db $95, $9e, $95, $9d, $99, $95, $a3, $5e, $02;stop o
+
+db $70, $82, $95, $9d, $9f, $a6, $95, $a3, $50, $91, $50, $a3, $98, $99, $95, $9c
+db $94, $50, $96, $a2, $9f, $9d, $50, $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9
+db $5e, $00, $70, $79, $a4, $50, $9d, $91, $a9, $50, $9f, $93, $93, $91, $a3, $99
+db $9f, $9e, $91, $9c, $9c, $a9, $50, $96, $91, $99, $9c, $5e, $02;Neutralize a
+
+db $70, $82, $95, $9d, $9f, $a6, $95, $a3, $50, $a3, $98, $99, $95, $9c, $94, $a3
+db $50, $96, $a2, $9f, $9d, $50, $95, $a6, $95, $a2, $a9, $9f, $9e, $95, $50, $99
+db $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $00, $70, $82, $95, $a3, $95, $a4
+db $a3, $50, $91, $92, $99, $9c, $99, $a4, $a9, $50, $93, $98, $91, $9e, $97, $95
+db $a3, $50, $9f, $96, $50, $95, $a6, $95, $a2, $a9, $9f, $9e, $95, $50, $99, $9e
+db $50, $92, $91, $a4, $a4, $9c, $95, $5e, $02;Neutralize o
+;;;;;;;;;;;;;;;;;;;;
+db $01, $50, $6c, $1c, $05, $a1, $6e, $03, $00, $70, $80, $a5, $a4, $a3, $50, $9f
+db $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $a4, $9f, $50, $a3, $9c, $95, $95
+db $a0, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $ab
+db $6d, $c5, $ff, $13, $02;sleep spray
+
+
+db $01, $50, $6c, $1c, $05, $a1, $6e, $03, $00, $70, $80, $91, $a2, $91, $9c, $a9
+db $aa, $95, $a3, $50, $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $99, $9e
+db $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $ab, $6d, $c5, $00, $13
+db $02;para spray
+
+db $01, $50, $6c, $1c, $05, $a1, $6e, $03, $00, $70, $79, $9e, $93, $a2, $95, $91
+db $a3, $95, $50, $9f, $9e, $95, $50, $9f, $96, $50, $a4, $98, $95, $50, $96, $a2
+db $99, $95, $9e, $94, $a3, $50, $7f, $96, $96, $95, $9e, $a3, $95, $50, $94, $a5
+db $a2, $99, $9e, $97, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $0d
+db $6b, $c5, $00, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;off up spray
+
+db $01, $50, $6c, $1c, $05, $a1, $6e, $03, $00, $70, $74, $95, $93, $a2, $95, $91
+db $a3, $95, $50, $9f, $9e, $95, $50, $9f, $96, $50, $a4, $98, $95, $50, $95, $9e
+db $95, $9d, $99, $95, $a3, $50, $74, $95, $96, $95, $9e, $a3, $95, $50, $94, $a5
+db $a2, $99, $9e, $97, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $0d
+db $6b, $c5, $00, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;Def down spray
+
+db $01, $50, $6c, $1c, $05, $a1, $6e, $03, $00, $70, $7d, $91, $9b, $95, $a3, $50
+db $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $96, $95, $95, $9c, $50, $a3
+db $a4, $a2, $91, $9e, $97, $95, $50, $94, $a5, $a2, $99, $9e, $97, $50, $92, $91
+db $a4, $a4, $9c, $95, $5e, $03, $00, $08, $ab, $6d, $c5, $00, $13, $02;brainshock spray
+
+db $01, $50, $6c, $1c, $05, $a1, $6e, $03, $00, $70, $82, $95, $9d, $9f, $a6, $95
+db $a3, $50, $91, $50, $a3, $98, $99, $95, $9c, $94, $50, $96, $a2, $9f, $9d, $50
+db $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $94, $a5, $a2, $99, $9e, $97
+db $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $ab, $6d, $c5, $00, $13
+db $02;Shield off spray
+
+db $01, $50, $6c, $1c, $05, $a1, $6e, $03, $00, $70, $83, $9f, $9c, $99, $94, $99
+db $96, $99, $95, $a3, $50, $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $99
+db $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $ab, $6d, $c5, $00
+db $13, $02;Stop spray
+
+db $01, $50, $6c, $1c, $05, $a1, $6e, $03, $00, $70, $74, $a2, $91, $99, $9e, $a3
+db $50, $78, $80, $50, $96, $a2, $9f, $9d, $50, $9f, $9e, $95, $50, $95, $9e, $95
+db $9d, $a9, $50, $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08
+db $ab, $6d, $c5, $00, $13, $02;HP-Drain spray
+
+db $01, $50, $6c, $1c, $05, $a1, $6e, $03, $00, $70, $7d, $91, $9b, $95, $a3, $50
+db $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $a5, $9e, $91, $92, $9c, $95
+db $50, $a4, $9f, $50, $93, $9f, $9e, $93, $95, $9e, $a4, $a2, $91, $a4, $95, $50
+db $99, $9e, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $ab, $6d, $c5
+db $00, $13, $02;Disable spray
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $80
+db $a5, $a4, $a3, $50, $91, $9e, $50, $95, $9e, $95, $9d, $a9, $50, $a4, $9f, $50
+db $a3, $9c, $95, $95, $a0, $5e, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Hypno gadget a
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $80
+db $a5, $a4, $a3, $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $a9, $50, $a4, $9f
+db $50, $a3, $9c, $95, $95, $a0, $5e, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Hypno gadget o
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $80
+db $91, $a2, $91, $9c, $a9, $aa, $95, $a3, $50, $91, $9e, $50, $95, $9e, $95, $9d
+db $a9, $5e, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Para a
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $80
+db $91, $a2, $91, $9c, $a9, $aa, $95, $a3, $50, $91, $9c, $9c, $50, $95, $9e, $95
+db $9d, $99, $95, $a3, $5e, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Para o
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $79, $9e, $93, $a2, $95, $91
+db $a3, $95, $50, $9f, $9e, $95, $50, $9f, $96, $50, $a4, $98, $95, $50, $96, $a2
+db $99, $95, $9e, $94, $a3, $50, $7f, $96, $96, $95, $9e, $a3, $95, $50, $94, $a5
+db $a2, $99, $9e, $97, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $0d
+db $6b, $c5, $00, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Offense up a
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $79, $9e, $93, $a2, $95, $91
+db $a3, $95, $a3, $50, $a4, $98, $95, $50, $7f, $96, $96, $95, $9e, $a3, $95, $50
+db $9f, $96, $50, $91, $9c, $9c, $50, $a4, $98, $95, $50, $96, $a2, $99, $95, $9e
+db $94, $a3, $5e, $03, $00, $08, $0d, $6b, $c5, $00, $03, $00, $08, $9b, $6d, $c5
+db $00, $13, $02;Offense up o
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $74, $95, $93, $a2, $95, $91
+db $a3, $95, $50, $9f, $9e, $95, $50, $9f, $96, $50, $a4, $98, $95, $50, $95, $9e
+db $95, $9d, $99, $95, $a3, $50, $74, $95, $96, $95, $9e, $a3, $95, $50, $94, $a5
+db $a2, $99, $9e, $97, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $0d
+db $6b, $c5, $00, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Def down a
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $74, $95, $93, $a2, $95, $91
+db $a3, $95, $a3, $50, $a4, $98, $95, $50, $74, $95, $96, $95, $9e, $a3, $95, $50
+db $9f, $96, $50, $91, $9c, $9c, $50, $a4, $98, $95, $50, $95, $9e, $95, $9d, $99
+db $95, $a3, $5e, $03, $00, $08, $0d, $6b, $c5, $00, $03, $00, $08, $9b, $6d, $c5
+db $00, $13, $02;Def down o
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $79, $9e, $93, $a2, $95, $91
+db $a3, $95, $50, $9f, $9e, $95, $50, $9f, $96, $50, $a4, $98, $95, $50, $96, $a2
+db $99, $95, $9e, $94, $a3, $50, $74, $95, $96, $95, $9e, $a3, $95, $50, $94, $a5
+db $a2, $99, $9e, $97, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08, $0d
+db $6b, $c5, $00, $03, $00, $08, $9b, $6d, $c5, $00, $13, $02;Def up a
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $7d
+db $91, $9b, $95, $a3, $50, $9f, $9e, $95, $50, $95, $9e, $95, $9d, $a9, $50, $96
+db $95, $95, $9c, $50, $a3, $a4, $a2, $91, $9e, $97, $95, $5e, $03, $00, $08, $9b
+db $6d, $c5, $00, $13, $02;Brainshock a
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $7d
+db $91, $9b, $95, $a3, $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3
+db $50, $96, $95, $95, $9c, $50, $a3, $a4, $a2, $91, $9e, $97, $95, $5e, $03, $00
+db $08, $9b, $6d, $c5, $00, $13, $02;Brainshock o
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $72
+db $9c, $91, $a3, $a4, $a3, $50, $91, $50, $a3, $a4, $99, $93, $9b, $a9, $50, $a3
+db $a5, $92, $a3, $a4, $91, $9e, $93, $95, $50, $a4, $98, $91, $a4, $50, $a3, $a4
+db $9f, $a0, $a3, $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3, $50
+db $96, $a2, $9f, $9d, $50, $9d, $9f, $a6, $99, $9e, $97, $5e, $03, $00, $08, $9b
+db $6d, $c5, $00, $13, $02;Slime o
+
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $08, $b7, $6d, $c5, $00, $70, $74
+db $99, $a3, $a2, $a5, $a0, $a4, $a3, $50, $a4, $98, $95, $50, $80, $83, $79, $50
+db $9f, $96, $50, $91, $9c, $9c, $50, $95, $9e, $95, $9d, $99, $95, $a3, $50, $94
+db $a5, $a2, $99, $9e, $97, $50, $92, $91, $a4, $a4, $9c, $95, $5e, $03, $00, $08
+db $9b, $6d, $c5, $00, $13, $02;Disable o
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b
+db $95, $9e, $50, $a0, $9f, $93, $9b, $95, $a4, $a7, $91, $a4, $93, $98, $5e, $03
+db $00, $08, $6f, $6b, $c5, $00, $13, $02, $01, $50, $6c, $1c, $05, $01, $6e, $03
+db $00, $70, $71, $50, $92, $a2, $9f, $9b, $95, $9e, $50, $a3, $93, $a2, $95, $95
+db $9e, $50, $96, $a2, $9f, $9d, $50, $91, $50, $98, $91, $9e, $94, $98, $95, $9c
+db $94, $50, $94, $95, $a6, $99, $93, $95, $5e, $03, $00, $08, $6f, $6b, $c5, $00
+db $13, $02, $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $71, $50, $92, $a2
+db $9f, $9b, $95, $9e, $50, $a2, $91, $aa, $9f, $a2, $5e, $03, $00, $08, $6f, $6b
+db $c5, $00, $13, $02, $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $71, $50
+db $92, $a2, $9f, $9b, $95, $9e, $50, $94, $95, $a3, $9b, $50, $96, $91, $9e, $5e
+db $03, $00, $08, $6f, $6b, $c5, $00, $13, $02, $01, $50, $6c, $1c, $05, $01, $6e
+db $03, $00, $70, $71, $50, $92, $a2, $9f, $9b, $95, $9e, $50, $96, $91, $a5, $93
+db $95, $a4, $50, $98, $95, $91, $94, $5e, $03, $00, $08, $6f, $6b, $c5, $00, $13
+db $02, $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $71, $50, $a0, $99, $95
+db $93, $95, $50, $92, $a2, $9f, $9b, $95, $9e, $50, $9f, $96, $96, $50, $9f, $96
+db $50, $91, $50, $a4, $a5, $92, $91, $5e, $03, $00, $08, $6f, $6b, $c5, $00, $13
+db $02, $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f
+db $9b, $95, $9e, $50, $a3, $a0, $a2, $99, $9e, $9b, $9c, $95, $a2, $5e, $03, $00
+db $08, $6f, $6b, $c5, $00, $13, $02, $01, $50, $6c, $1c, $05, $01, $6e, $03, $00
+db $70, $83, $9f, $9d, $95, $50, $a0, $91, $a2, $a4, $50, $92, $95, $9c, $9f, $9e
+db $97, $99, $9e, $97, $50, $a4, $9f, $50, $91, $50, $a4, $a2, $9f, $9d, $92, $9f
+db $9e, $95, $5e, $03, $00, $08, $6f, $6b, $c5, $00, $13, $02, $01, $50, $6c, $1c
+db $05, $01, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b, $95, $9e, $50, $9d
+db $99, $93, $a2, $9f, $a7, $91, $a6, $95, $50, $9f, $a6, $95, $9e, $5e, $03, $00
+db $08, $6f, $6b, $c5, $00, $13, $02, $01, $50, $6c, $1c, $05, $01, $6e, $03, $00
+db $70, $80, $91, $a2, $a4, $50, $9f, $96, $50, $91, $50, $92, $a2, $9f, $9b, $95
+db $9e, $50, $94, $95, $95, $a0, $50, $96, $a2, $a9, $95, $a2, $5e, $03, $00, $08
+db $6f, $6b, $c5, $00, $13, $02, $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70
+db $71, $50, $9e, $9f, $aa, $aa, $9c, $95, $50, $96, $a2, $9f, $9d, $50, $91, $50
+db $a7, $91, $a4, $95, $a2, $99, $9e, $97, $50, $93, $91, $9e, $5e, $03, $00, $08
+db $6f, $6b, $c5, $00, $13, $02, $01, $50, $6c, $1c, $05, $01, $6e, $03, $00, $70
+db $71, $50, $92, $a2, $9f, $9b, $95, $9e, $50, $97, $91, $a2, $94, $95, $9e, $50
+db $98, $9f, $a3, $95, $5e, $03, $00, $08, $6f, $6b, $c5, $00, $13, $02, $01, $50
+db $6c, $1c, $05, $01, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b, $95, $9e
+db $50, $94, $95, $a6, $99, $93, $95, $50, $9f, $96, $50, $a3, $9f, $9d, $95, $50
+db $a3, $9f, $a2, $a4, $5e, $03, $00, $70, $79, $a4, $a3, $50, $96, $a5, $9e, $93
+db $a4, $99, $9f, $9e, $50, $99, $a3, $50, $99, $9e, $94, $99, $a3, $93, $95, $a2
+db $9e, $99, $92, $9c, $95, $5e, $03, $00, $08, $6f, $6b, $c5, $00, $13, $02, $01
+db $50, $6c, $1c, $05, $01, $6e, $03, $00, $70, $71, $50, $92, $a2, $9f, $9b, $95
+db $9e, $50, $93, $9c, $9f, $a4, $98, $95, $a3, $50, $a3, $a4, $95, $91, $9d, $95
+db $a2, $5e, $03, $00, $08, $6f, $6b, $c5, $00, $13, $02, $01, $50, $6c, $1c, $05
+db $01, $6e, $03, $00, $70, $80, $99, $95, $93, $95, $a3, $50, $9f, $96, $50, $91
+db $50, $92, $a2, $9f, $9b, $95, $9e, $50, $9d, $9f, $a4, $98, $95, $a2, $92, $9f
+db $91, $a2, $94, $5e, $03, $00, $08, $6f, $6b, $c5, $00, $13, $02;Descriptions for broken items
+
+db $97, $95, $9e, $95, $a2, $9f, $a3, $99, $a4, $a9, $5e, $13, $18, $04, $0a, $7a
+db $90, $c7
+
+db $18, $01, $01, $0A, $2D, $B1, $EE
+
+db $a9, $9f, $a5, $a2, $50, $9c, $9f, $97, $99, $93, $91, $9c, $50, $72, $7b, $50
+db $9d, $9f, $94, $95, $0a, $b8, $1f, $c8
+
+db $50, $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97, $0a, $c4, $8a, $c9
+
+db $00, $0A, $a3, $C8, $C6
+
+db $5E, $0A, $66, $C0, $C6
+
+db $1f, $31, $0a, $81, $c0, $c6
+
+db $00, $0A, $F1, $10, $C6
+
+;;;;;;;;;;;;;;;;;;;;
+
+ORG $D57295
+db $80, $A2, $9F, $97, $A2, $95, $A3, $A3, $99, $A6, $95, $50, $92, $91, $A4, $00
+
+ORG $D572BC
+db $80, $a2, $9f, $97, $a2, $95, $a3, $a3, $99, $a6, $95, $50, $a0, $91, $9e, $00
+
+ORG $D572E3
+db $80, $A2, $9F, $97, $A2, $95, $A3, $A3, $99, $A6, $95, $50, $97, $A5, $9E, $00
+
+ORG $D5730A
+db $80, $a2, $9f, $97, $a2, $95, $a3, $5e, $50, $92, $a2, $91, $93, $95, $9c
+db $95, $a4, $00
+
+ORG $D57331
+db $80, $A2, $9F, $97, $A2, $95, $A3, $A3, $99, $A6, $95, $50, $9F, $A4, $98, $95
+db $A2, $00
+
+ORG $C5E3BF
+db $FE; Lucky sandwiches no longer randomize themselves
+
+ORG $D5728B
+dw $01F3 ;Lucky Sandwich new action
+
+ORG $C89358
+db $0A, $61, $EE, $EE
+
+ORG $C951E3
+db $0A, $73, $EE, $EE
+
+ORG $C72BE7
+db $1c, $05, $01, $0a, $fe, $2b, $c7
+
+ORG $C8008D
+db $1c, $05, $01, $50
+
+ORG $C81FA3
+db $0A, $7A, $EE, $EE
+
+ORG $C81FF1
+db $1c, $05, $01, $a3, $5e, $07, $00, $00
+
+ORG $C93F08
+db $1c, $05, $01, $6e, $0a, $15, $3f, $c9
+
+ORG $C98ABC
+db $0a, $92, $ee, $ee
+
+ORG $C6AEB4
+db $91, $50, $1c, $05, $01, $0a, $c2, $ae, $c6
+
+ORG $C6C891
+db $A0, $EE, $EE
+
+ORG $C6C063
+db $a5, $ee, $EE
+
+ORG $C6C06F
+db $aa, $ee, $ee
+
+ORG $C60FE1
+db $B0, $EE, $EE
+
+ORG $C61123
+db $1c, $05, $01, $0a, $2f, $11, $c6
+
+ORG $C9E84E
+db $0a, $5a, $e7, $c9
+
+ORG $CF097A
+db $00, $00;Threed tunnel door flag
+
+ORG $C7C41F
+db $CD, $C3; Lumine hole double flag
+
+ORG $EEA9B6
+db $1d, $0e, $ff, $01, $0a, $ef, $a9, $ee
+
+ORG $C9E2F1
+db $0A, $B5, $EE, $EE
+
+ORG $EEEEB5
+db $04, $C4, $00, $04, $DC, $00, $02
+
+ORG $C87205
+db $9e, $c5, $c7 ;sphinx post-pyramid
+
+ORG $CF8E44
+db $01
+
+ORG $CF8E42
+db $69, $00
+
+ORG $D014C0
+db $80
+;Color Math on swirls
+ORG $C2E989
+;LDX #$007F
+
+ORG $C2E995
+;LDX #$003F
+
+ORG $C5770D
+db $A3, $9F, $A5, $A4, $98
+
+ORG $C57665
+db $a3, $9f, $a5, $a4
+
+;;;;;;;;;;;;;;;;;
+;New battle entry for Heavily Armed Pokey
+ORG $CBD8D2
+db $DC, $00;Battle Background
+
+ORG $D0D5A5
+db $d8, $00;Pokey battle
+
+ORG $D5E533
+db $01
+;;;;;;;;;;;;
+;new entry for Diamond Dog
+ORG $D0DF70
+db $01, $53, $00, $ff
+
+ORG $CBDFFE
+db $37, $01
+
+ORG $D5E688
+db $49
+;;;;;;;;;;;;;;;;;;;;;;;;;
+;entry for giygas phase 2 fix
+ORG $C2C505
+LDA #$0005;Set the group number
+
+ORG $D0D562
+db $00
+
+ORG $D0C63C
+db $00
+
+ORG $CBD8AE
+db $e0, $00, $df, $00
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C91EAD
+db $0a, $fc, $1e, $c9
+
+ORG $C3CF10
+db $04
+;sky runner event stuff
+
+ORG $C91E56
+db $0A, $60, $F2, $EE
+
+ORG $C3CEFE
+db $D8
+
+ORG $C91F11
+db $1F, $00, $00, $6E
+db $10, $F0, $10, $40, $0A, $FE, $F1, $EE
+
+ORG $CF95F6
+db $EC, $02, $02
+
+ORG $CF95F1
+db $0D
+
+ORG $CF6C31
+db $03; Remove sky runner npc from Onett
+
+ORG $C6BBF9
+db $0A, $24, $F2, $EE
+
+;;;;;;;;;
+ORG $EFA455
+db $0A, $3A, $F1, $EE
+;;;;;;;;;;;;;;
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;Frank
+ORG $C65F1F
+db $08, $bc, $ee, $ee
+
+ORG $C65F10
+db $08, $bc, $ee, $ee
+
+ORG $C66481
+db $08, $bc, $ee, $ee
+
+ORG $C660C4
+db $08, $bc, $ee, $ee
+
+ORG $C66110
+db $08, $bc, $ee, $ee
+
+ORG $C66197
+db $08, $bc, $ee, $ee
+
+ORG $C661AB
+db $08, $bc, $ee, $ee
+
+ORG $C746E1
+db $08, $bc, $ee, $ee
+
+ORG $C74BC0
+db $08, $bc, $ee, $ee
+
+ORG $C74E1C
+db $08, $bc, $ee, $ee
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;FSTM2
+ORG $C66145
+db $08, $bc, $ee, $ee, $ff, $0a, $56, $61, $c6
+
+ORG $C6648A
+db $08, $bc, $ee, $ee, $ff, $0a, $9b, $64, $c6
+
+ORG $C664FB
+db $08, $bc, $ee, $ee, $ff, $0a, $0c, $65, $c6
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;Captain Strong
+ORG $C5FC28
+db $50, $50, $08, $bc, $ee, $ee, $ff
+
+ORG $C5FC58
+db $08, $bc, $ee, $ee, $ff, $0a, $65, $fc, $c5
+
+ORG $C5FCF6
+db $08, $bc, $ee, $ee, $ff, $0f
+
+ORG $C65F87
+db $08, $bc, $ee, $ee, $ff, $0f
+
+ORG $C66084
+db $08, $bc, $ee, $ee, $ff, $0f
+;;;;;;;;;;;;;;;;
+;Everdred
+ORG $C95C6F
+db $08, $bc, $ee, $ee, $ff, $0f
+
+ORG $C8065C
+db $0a, $e9, $ee, $ee
+;;;;;;;;;;;;;;;;;;;
+;Carpainter
+ORG $C993DA
+db $08, $bc, $ee, $ee, $ff, $0a, $e3, $93, $c9
+
+ORG $C9945D
+db $08, $bc, $ee, $ee, $ff, $0a, $66, $94, $c9
+
+ORG $C99310
+db $08, $bc, $ee, $ee, $ff, $0a, $19, $93, $c9
+
+ORG $C99363
+db $08, $bc, $ee, $ee, $ff, $0a, $6c, $93, $c9
+
+ORG $C990D9
+db $08, $bc, $ee, $ee, $ff, $0f, $0f
+
+ORG $C98EF5
+db $08, $bc, $ee, $ee, $ff, $0a, $fe, $8e, $c9
+
+ORG $C99142
+db $08, $bc, $ee, $ee, $ff, $0a, $4b, $91, $c9
+
+ORG $C99027
+db $08, $bc, $ee, $ee, $ff, $0a, $30, $90, $c9
+
+ORG $C983BA
+db $08, $bc, $ee, $ee, $ff, $0a, $c3, $83, $c9
+
+ORG $C9840B
+db $08, $bc, $ee, $ee, $ff, $0a, $14, $84, $c9
+
+ORG $C9835A
+db $08, $bc, $ee, $ee, $ff, $0a, $63, $83, $c9
+
+ORG $C684CF
+db $08, $bc, $ee, $ee, $ff, $0f, $0f
+
+ORG $C9056E
+db $08, $bc, $ee, $ee, $ff, $0a, $77, $05, $c9
+
+ORG $C794EB
+db $08, $bc, $ee, $ee, $ff, $0a, $f4, $94, $c7
+;;;;;;;;;;;;;
+;Belch
+ORG $C8EF20
+db $08, $bc, $ee, $ee, $ff
+
+ORG $C8EF37
+db $08, $bc, $ee, $ee, $ff
+
+ORG $EF6296
+db $08, $bc, $ee, $ee, $ff, $0a, $9f, $62, $ef
+
+ORG $EF62B2
+db $08, $bc, $ee, $ee, $ff, $0a, $bb, $62, $ef
+
+ORG $EF690F
+db $08, $bc, $ee, $ee, $ff, $0a, $18, $69, $ef
+
+ORG $EF6972
+db $08, $bc, $ee, $ee, $ff, $0a, $7b, $69, $ef
+
+
+
+;Mani Mani
+ORG $C978AC
+db $08, $bc, $ee, $ee, $ff, $50, $99, $a3, $50, $0a, $bb, $78, $c9
+
+ORG $C9782C
+db $08, $bc, $ee, $ee, $ff, $50, $99, $a3, $50, $0a, $3b, $78, $c9
+
+ORG $C97997
+db $08, $bc, $ee, $ee, $ff, $0a, $a3, $79, $c9, $c9
+
+
+;;;;;;;;;;;;;;;
+;Kraken
+ORG $C86060
+db $08, $bc, $ee, $ee, $ff
+
+ORG $C86138
+db $08, $bc, $ee, $ee, $ff
+
+ORG $C8B42F
+db $08, $bc, $ee, $ee
+
+ORG $C8B8B3
+db $08, $bc, $ee, $ee, $ff
+
+ORG $C8B6FB
+db $08, $bc, $ee, $ee, $ff
+
+ORG $C8B590
+db $08, $bc, $ee, $ee, $ff, $0f
+
+ORG $C9AB2A
+db $08, $bc, $ee, $ee, $ff
+
+ORG $C92D4C
+db $08, $bc, $ee, $ee, $ff
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;Master Belch
+ORG $C9E64C
+db $08, $bc, $ee, $ee, $ff
+
+ORG $C9E68F
+db $08, $bc, $ee, $ee, $ff
+
+ORG $C9E6C1
+db $0a, $d4, $ee, $ee, $FF
+
+;;;;;;;;;;;;;
+;Starman DX
+ORG $C92C28
+db $08, $bc, $ee, $ee, $ff, $0a, $31, $2c, $c9
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;Pokey
+ORG $C57E39
+db $0a, $c2, $ee, $ee
+
+ORG $C57E57
+db $0a, $cb, $ee, $ee
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C8FC59
+db $0A, $9C, $F0, $EE
+
+ORG $C57F4A
+db $0A, $A7, $F0, $EE
+
+
+ORG $C76186
+db $0A
+dl tracy_backpack_tex
+
+ORG $C64690
+dl tracy_backpack_twice_tex
+
+ORG $C6AE42
+db $0A
+dl winters_drugstore_space
+
+ORG $C6AEA3
+db $0A
+dl winters_drugstore_space2
+
+
+;;;;;;;;;;;;;;;
+;hint stuff
+
+ORG $CF8C25
+dl onett_hint_man ;Onett hint guy text pointer
+
+ORG $CF9C04
+dl twoson_hint_man
+
+ORG $CFAB6C
+dl threed_hint_man
+
+ORG $CFC9F8
+dl fourside_hint_man
+
+ORG $CFD0AD
+dl summers_hint_man
+
+ORG $CFD61F
+dl scaraba_hint_man
+
+ORG $C726D8
+db $02
+
+ORG $C701A0
+db $49
+
+ORG $C701C6
+db $08
+dd display_hint_price
+db $0F
+
+ORG $C7032E
+db $70, $87, $9f, $a2, $94, $50, $9f, $9e, $50, $a4, $98, $95, $50, $a3, $a4, $a2
+db $95, $95, $a4, $50, $99, $a3, $50, $a4, $98, $91, $a4, $50
+
+db $06, $05, $04
+dd display_onett_hint
+
+db $06, $06, $04
+dd display_twoson_hint
+
+db $06, $07, $04
+dd display_threed_hint
+
+db $06, $08, $04
+dd display_fourside_hint
+
+db $06, $09, $04
+dd display_summers_hint
+
+db $06, $0A, $04
+dd display_scaraba_hint
+db $02
+
+display_onett_hint:
+db $08
+dd hint_error_message;replace with generated pointer
+db $03
+db $08
+dd push_onett_hint; replace with a $02 if not pushable
+db $06, $F9, $03
+dd .onett_hint_twice
+db $08
+dd count_hints
+.onett_hint_twice:
+db $04, $F9, $03
+db $01
+db $06, $04, $04
+dd $C72348 ;Direct here if we're out of hints
+dd $C726CC08
+db $FF
+db $03, $01
+db $08
+dd post_hint_text
+db $05, $05, $04
+db $02
+
+display_twoson_hint:
+db $08
+dd hint_error_message;replace with generated pointer
+db $03
+db $08
+dd push_twoson_hint; replace with a $02 if not pushable
+db $06, $FA, $03
+dd .twoson_hint_twice
+db $08
+dd count_hints
+.twoson_hint_twice:
+db $04, $FA, $03
+db $01
+db $06, $04, $04
+dd $C72348 ;Direct here if we're out of hints
+dd $C726CC08
+db $FF
+db $03, $01
+db $08
+dd post_hint_text
+db $05, $06, $04
+db $02
+
+display_threed_hint:
+db $08
+dd hint_error_message;replace with generated pointer
+db $03
+db $08
+dd push_threed_hint; replace with a $02 if not pushable
+db $06, $FB, $03
+dd .threed_hint_twice
+db $08
+dd count_hints
+.threed_hint_twice:
+db $04, $FB, $03
+db $01
+db $06, $04, $04
+dd $C72348 ;Direct here if we're out of hints
+dd $C726CC08
+db $FF
+db $03, $01
+db $08
+dd post_hint_text
+db $05, $07, $04
+db $02
+
+display_fourside_hint:
+db $08
+dd hint_error_message;replace with generated pointer
+db $03
+db $08
+dd push_fourside_hint; replace with a $02 if not pushable
+db $06, $FC, $03
+dd .fourside_hint_twice
+db $08
+dd count_hints
+.fourside_hint_twice:
+db $04, $FC, $03
+db $01
+db $06, $04, $04
+dd $C72348 ;Direct here if we're out of hints
+dd $C726CC08
+db $FF
+db $03, $01
+db $08
+dd post_hint_text
+db $05, $08, $04
+db $02
+
+display_summers_hint:
+db $08
+dd hint_error_message;replace with generated pointer
+db $03
+db $08
+dd push_summers_hint; replace with a $02 if not pushable
+db $06, $FD, $03
+dd .summers_hint_twice
+db $08
+dd count_hints
+.summers_hint_twice:
+db $04, $FD, $03
+db $01
+db $06, $04, $04
+dd $C72348 ;Direct here if we're out of hints
+dd $C726CC08
+db $FF
+db $03, $01
+db $08
+dd post_hint_text
+db $05, $09, $04
+db $02
+
+display_scaraba_hint:
+db $08
+dd hint_error_message;replace with generated pointer
+db $03
+db $08
+dd push_scaraba_hint; replace with a $02 if not pushable
+db $06, $FE, $03
+dd .scaraba_hint_twice
+db $08
+dd count_hints
+.scaraba_hint_twice:
+db $04, $FE, $03
+db $01
+db $06, $04, $04
+dd $C72348 ;Direct here if we're out of hints
+dd $C726CC08
+db $FF
+db $03, $01
+db $08
+dd post_hint_text
+db $05, $0A, $04
+db $02
+
+ORG $FCFFA0
+db $75, $91, $a2, $a4, $98, $72, $9f, $a5, $9e, $94, $50, $71, $a2, $93, $98, $99
+db $a0, $95, $9c, $91, $97, $9f, $50, $a6, $95, $a2, $a3, $99, $9f, $9e, $50, $02
+
+ORG $E1F4CA
+db $f9, $03; Onett hint shop flag
+
+ORG $E1F4F3
+db $fa, $03; Twoson hint shop flag
+
+ORG $E1F521
+db $fb, $03; Threed hint shop flag
+
+ORG $E1F545
+db $fc, $03; Fourside hint shop flag
+
+ORG $E1F57E
+db $fd, $03; Summers hint shop flag
+
+ORG $E1F55F
+db $fe, $03; Scaraba hint shop flag
+
+;Giant Step
+ORG $D6FC38
+db $08
+dd boost_speed_dynamic
+
+ORG $D6FC60
+db $08
+dd boost_vitality_dynamic
+;Lilliput Steps
+ORG $D6FCBB
+db $08
+dd boost_guts_dynamic
+
+ORG $D6FCE3
+db $08
+dd boost_vitality_dynamic
+;Milky Well
+ORG $D6FD3A
+db $08
+dd boost_speed_dynamic
+
+ORG $D6FD5C
+db $08
+dd boost_iq_dynamic
+;Magnet Hill
+ORG $D6FDB7
+db $08
+dd boost_vitality_dynamic
+
+ORG $D6FDDB
+db $08
+dd boost_luck_dynamic
+;Rainy Circle
+ORG $D6FE33
+db $08
+dd boost_guts_dynamic
+
+ORG $D6FE55
+db $08
+dd boost_iq_dynamic
+;Pink Cloud
+
+ORG $D6FEAC
+db $08
+dd boost_speed_dynamic
+
+ORG $D6FED0
+db $08
+dd boost_guts_dynamic
+;Lumine Hall
+ORG $D6FF27
+db $08
+dd boost_luck_dynamic
+
+ORG $D6FF49
+db $08
+dd boost_iq_dynamic
+;Fire Spring
+ORG $D6FFA1
+db $08
+dd boost_speed_dynamic
+
+ORG $D6FFC5
+db $08
+dd boost_luck_dynamic
+
+ORG $C99A9D
+db $a1, $9a, $c9
+
+ORG $C769A8
+db $0A, $3C, $F7, $EE
+
+ORG $C10E16
+LDA #$0098
+
+ORG $EF79F3
+db $4C, $F7, $EE
+
+ORG $D7FD30
+ExpPointers:
+dw $FFFF
+dw $FD40
+dw $FD44
+dw $FD48
+dw $FD4C
+
+ORG $D7FD40
+dd $00000011
+dd $00000000
+dd $00000000
+dd $000016EF
+
+PlayerCount: ;Write this value during generation
+dw $0001
+dw $0000
+
+BannedItemList:
+db $01, $68, $69, $7D, $8B, $9E, $A2, $A6, $A7, $AA, $AB, $AC, $AD, $AE
+db $AF, $B1, $B2, $B3, $B4, $B5, $B6, $B7, $B8, $B9, $BB, $C0, $C1, $C4
+db $C5, $CA, $CB, $CC, $CD, $CE, $D0, $D2, $D3, $E3, $E4, $E5, $E6, $E7
+db $FD, $B0, $A3, $C5, $A4
+
+StorageTitlePointers:
+dw $FF90, $FF70
+
+StorageTitleLength:
+dw $000C, $0010
+
+StorageNumberStart:
+dw $9CA9, $9CAE
+
+ORG $C9B132
+db $11, $04; Tony phone call flag
+
+ORG $C37A90
+db $90;Dept. Store starts farther right. Used for Escargo Express
+
+ORG $C37AB1
+db $30
+
+ORG $FCFF90
+db $77, $99, $96, $A4, $50, $79, $9E, $92, $9F, $A8
+
+ORG $D5630B
+db $84, $99, $9e, $a9, $50, $9b, $95, $a9, $00; Tiny key
+
+ORG $D5632E
+dd TinyKeyDesc
+
+ORG $D56B45
+db $84, $95, $9E, $94, $91, $50, $9C, $91, $A6, $91, $A0, $91, $9E, $A4, $A3, $00
+
+ORG $D56B68
+dd LavapantsDesc
+
+ORG $C6FED6
+db $0A
+dl LavaPantsHandleItem
+
+ORG $D56B61
+db $0F
+
+ORG $C7BF60
+db $08
+dl RememberedTex
+db $FF
+
+db $0A
+dl $C7BF71
+
+ORG $C7C02B
+db $08
+dl RememberedTex
+db $FF
+
+db $0A
+dl $C7C03C
+
+ORG $C7C0FD
+db $08
+dl RememberedTex
+db $FF
+
+db $0A
+dl $C7C10E
+
+ORG $C7C1CE
+db $08
+dl RememberedTex
+db $FF
+
+db $0A
+dl $C7C1DF
+
+ORG $C7C299
+db $08
+dl RememberedTex
+db $FF
+
+db $0A
+dl $C7C2AA
+
+ORG $C7C360
+db $08
+dl RememberedTex
+db $FF
+
+db $0A
+dl $C7C371
+
+ORG $C7C44F
+db $08
+dl RememberedTex
+db $FF
+
+db $0A
+dl $C7C460
+
+ORG $C7C511
+db $08
+dl RememberedTex
+db $FF
+
+db $0A
+dl $C7C522
+
+ORG $FCFF70
+db $79, $9D, $A0, $9F, $A2, $A4, $91, $9E, $A4, $50, $77, $9F, $9F, $94, $A3
+
+ORG $EE9645
+dd SetupStorageTakeMenu
+
+ORG $EE96D6
+db $0A
+dl InitStorageMen
+
+ORG $EE969F
+dl InitStorageMen
+
+ORG $EE968C
+db $0A
+dl CheckItemOnStore
+
+ORG $C66698
+db $08
+dd SancFirst
+
+ORG $C6BEA7
+db $0A
+dl RainyCircleIntro
+
+ORG $C83D4C
+db $08
+dd SancFifth
+
+ORG $C9D2E2
+db $08
+dd SancSixth
+
+ORG $C9E2A3
+db $08
+dd SancSeventh
+
+ORG $EF5813
+db $0A
+dl FireSpringIntro
+
+ORG $EF67C2
+db $08
+dd SancThird
+
+ORG $EF97CA
+db $08
+dd SancSecond
+
+ORG $EE96B7
+db $0A
+dl CheckAbsoluteStorage
+
+ORG $D7FDA0
+MusicTrackList:
+db $00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $0a, $0b, $0c, $0d, $0e, $0f
+db $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $1a, $1b, $1c, $1d, $1e, $1f
+db $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $2a, $2b, $2c, $2d, $2e, $2f
+db $30, $31, $32, $33, $34, $35, $36, $37, $38, $39, $3a, $3b, $3c, $3d, $3e, $3f
+db $40, $41, $42, $43, $44, $45, $46, $47, $48, $49, $4a, $4b, $4c, $4d, $4e, $4f
+db $50, $51, $52, $53, $54, $55, $56, $57, $58, $59, $5a, $5b, $5c, $5d, $5e, $5f
+db $60, $61, $62, $63, $64, $65, $66, $67, $68, $69, $6a, $6b, $6c, $6d, $6e, $6f
+db $70, $71, $72, $73, $74, $75, $76, $77, $78, $79, $7a, $7b, $7c, $7d, $7e, $7f
+db $80, $81, $82, $83, $84, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8d, $8e, $8f
+db $90, $91, $92, $93, $94, $95, $96, $97, $98, $99, $9a, $9b, $9c, $9d, $9e, $9f
+db $a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9, $aa, $ab, $ac, $ad, $ae, $af
+db $b0, $b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8, $b9, $ba, $bb, $bc, $bd, $be, $bf
+
+BanListNoProgs:
+db $01, $68, $69, $7D, $8B, $9E, $A2, $A6, $A7, $AA, $AB, $AC, $AD, $AE
+db $AF, $B1, $B2, $B3, $B4, $B5, $B6, $B7, $B8, $B9, $BB, $C0, $C1, $C4
+db $C5, $CA, $CB, $CC, $CD, $CE, $D0, $D2, $D3, $FD, $B0, $A3, $C5, $A4
+
+ORG $D56F2E
+db $00
+
+ORG $D56B93
+db $87, $91, $a2, $a0, $50, $a0, $91, $94, $00; Change the Receiver phone to Sold Out
+
+
+ORG $F41000
+ShopItemNames:
+.OnettTeleport:
+db $7F, $9E, $95, $A4, $A4, $50, $84, $95, $9C, $95, $A0, $9F, $A2, $A4, $00
+.TwosonTeleport:
+db $84, $A7, $9F, $A3, $9F, $9E, $50, $84, $95, $9C, $95, $A0, $9F, $A2, $A4, $00
+.HappyTeleport:
+db $78, $78, $86, $50, $84, $95, $9C, $95, $A0, $9F, $A2, $A4, $00
+.ThreedTeleport:
+db $84, $98, $A2, $95, $95, $94, $50, $84, $95, $9C, $95, $A0, $9F, $A2, $A4, $00
+.SaturnTeleport:
+db $83, $91, $a4, $a5, $a2, $9e, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00
+.DesertTeleport:
+db $74, $A5, $9E, $95, $A3, $50, $84, $95, $9C, $95, $A0, $9F, $A2, $A4, $00
+.FoursideTeleport:
+db $76, $9F, $A5, $A2, $A3, $99, $94, $95, $50, $84, $95, $9C, $95, $A0, $9F, $A2
+db $A4, $00
+.WintersTeleport:
+db $87, $99, $9E, $A4, $95, $A2, $A3, $50, $84, $95, $9C, $95, $A0, $9F, $A2, $A4
+db $00
+.SummersTeleport:
+db $83, $A5, $9D, $9D, $95, $A2, $A3, $50, $84, $95, $9C, $95, $A0, $9F, $A2, $A4
+db $00
+.ScarabaTeleport:
+db $83, $93, $91, $A2, $91, $92, $91, $50, $84, $95, $9C, $95, $A0, $9F, $A2, $A4
+db $00
+.DalaamTeleport:
+db $74, $91, $9C, $91, $91, $9D, $50, $84, $95, $9C, $95, $A0, $9F, $A2, $A4, $00
+.DarkTeleport:
+db $74, $91, $a2, $9b, $9e, $95, $a3, $a3, $50, $84, $95, $9c, $95, $a0, $9f, $a2
+db $a4, $00
+.TendaTeleport:
+db $84, $95, $9e, $94, $91, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00
+.UnderworldTeleport:
+db $7c, $5e, $50, $85, $87, $50, $84, $95, $9c, $95, $a0, $9f, $a2, $a4, $00
+.MgntTeleport:
+db $7d, $91, $97, $99, $93, $91, $9e, $a4, $50, $84, $95, $9c, $95, $a0, $9f, $a2
+db $a4, $00
+.PooPSI:
+db $80, $9F, $9F, $50, $80, $83, $79, $00
+.Paula:
+db $80, $91, $A5, $9C, $91, $00
+.Jeff:
+db $7A, $95, $96, $96, $00
+.Poo:
+db $80, $9F, $9F, $00
+.FlnMn:
+db $76, $9C, $A9, $99, $9E, $97, $50, $7D, $91, $9E, $00
+.Ness:
+db $7E, $95, $A3, $A3, $00
+.SoldOut:
+db $83, $7F, $7C, $74, $50, $7F, $85, $84, $00
+.TeleportNames:
+dw $0000
+dw .OnettTeleport
+dw .TwosonTeleport
+dw .HappyTeleport
+dw .ThreedTeleport
+dw .SaturnTeleport
+dw .DesertTeleport
+dw .FoursideTeleport
+dw .WintersTeleport
+dw .SummersTeleport
+dw .ScarabaTeleport
+dw .DalaamTeleport
+dw .DarkTeleport
+dw .TendaTeleport
+dw .UnderworldTeleport
+dw .MgntTeleport
+dw .PooPSI
+.CharNames:
+dw $0000
+dw .Paula
+dw .Jeff
+dw .Poo
+dw .FlnMn
+dw .Ness
+
+.PlayerText:
+db $18, $01, $0B, $1c, $02, $01, $02
+
+ShopFlagBits:
+db $01
+db $02
+db $04
+db $08
+db $10
+db $20
+db $40
+db $80
+
+ORG $CFB891
+db $00
+
+ORG $CFB8F5
+db $47
+
+ORG $D7FEA0
+ExtraWindowData:
+dw $0005, $0009, $001A, $0004
+
+HasStartingPhoto:
+db $00
+
+HasPooStartPhoto:
+db $00
+
+PhotoFlags:
+dw $02BA
+dw $02BB
+
+MoneyAmounts:
+dw $0000
+dw $000A
+dw $0064
+dw $03E8
+
+ORG $C3E2A8
+;contemplate switching to 1
+dw $000C, $0010, $0013, $0004
+
+ORG $CFB115
+DD OpenLockers_TopRightLocker
+
+ORG $CFB126
+DD OpenLockers_TopLeftLocker
+
+ORG $CFB137
+DD OpenLockers_BottomRightLocker
+
+ORG $CFB159
+DD OpenLockers_BottomLeftLocker
+
+ORG $CFB148
+DD OpenLockers_MiddleLocker
+
+ORG $CFA990
+dd fixed_skyrunner_redirect
+
+ORG $C5D793
+db $F6
+
+ORG $C64FA5
+db $FF
+
+ORG $EEAAA5
+db $0A
+dl NpcNessGiveOverride
+
+ORG $C79693
+db $02
+;Paula text og script
+
+ORG $C92A38
+dd $C92A3C
+
+ORG $C76FDA
+db $0A
+dl $C76FEE
+
+ORG $C885AC
+db $02
+
+ORG $D56BAF
+db $04
+
+ORG $C55715
+db $0A
+dl WarpPadDescription
+
+ORG $C6FFBC
+db $0A
+dl WarpPadUsage
+
+ORG $C8FD5B
+db $0A
+dl PokeyCharCount
+
+ORG $CFE045
+dw $041B
+
+ORG $CFE056
+dw $041A
+
+ORG $CFE067
+dw $0419
+
+ORG $CFE078
+dw $0418
+
+ORG $C8FFF0
+db $0A
+dl DisplayRobots
+
+ORG $C811B3
+db $0A
+dl OrangeKidCrowdfund
+
+ORG $C66680
+db $84, $66
+
+ORG $C6BE82
+db $86, $BE
+
+ORG $C6BE8F
+db $93, $BE
+
+ORG $C76119
+db $0A
+dl TracyNessText
+
+ORG $CFE367
+dd MagicantTentacleERText
+
+ORG $C87963
+db $0A
+dl DuckSignTex
+
+ORG $C87997
+db $0A
+dl GoatSignTex
+
+ORG $C87A1F
+db $0A
+dl SlimeSignTex
+
+ORG $C8798A
+db $02
+
+ORG $C879BA
+db $02
+
+ORG $C87A45
+db $02
+
+ORG $C92E9E
+db $0A
+dl PooBoatText
+NotSoloPoo:
+
+ORG $C39F0C
+db $42
+dl CheckNessRobot
+
+ORG $F30080
+db $0A, $C3, $B8, $EE
+
+ORG $C7BF32
+db $0A
+dl GiantStepNessText
+
+ORG $C7BF5C
+db $0A
+dl GiantStepNessName
+
+ORG $C7BFFC
+db $0A
+dl LilliputNessText
+
+ORG $C7C027
+db $0A
+dl LilliputNessName
+
+ORG $C7C18F
+db $0A
+dl MilkyNessText
+
+ORG $C7C1CA
+db $0A
+dl MilkyNessName
+
+ORG $C7C0D4
+db $0A
+dl RainyNessText
+
+ORG $C7C0F9
+db $0A
+dl RainyNessName
+
+ORG $C7C26C
+db $0A
+dl MagnetNessText
+
+ORG $C7C295
+db $0A
+dl MagnetNessName
+
+ORG $C7C32B
+db $0A
+dl CloudNessText
+
+ORG $C7C35C
+db $0A
+dl CloudNessName
+
+ORG $C7C426
+db $0A
+dl LumineNessText
+
+ORG $C7C44B
+db $0A
+dl LumineNessName
+
+ORG $C7C4DD
+db $0A
+dl FireNessText
+
+ORG $C7C50D
+db $0A
+dl FireNessName
+
+ORG $EF97B2
+dd $EF97B6
+
+ORG $EF67AA
+dd $EF67AE
+
+ORG $C83D34
+dd $C83D38
+
+ORG $C9D2CA
+dd $C9D2CE
+
+ORG $C9E28B
+dd $C9E28F
+
+ORG $EF57FB
+dd $EF57FF
+
+ORG $C8854C
+db $0A
+dl $C88561
+
+ORG $EE9BDD
+db $0A
+dl EarthPowerLeader
+
+ORG $EEAB04
+db $0A
+dd OnettTeleNPCName
+OnettTeleNPCBack:
+
+ORG $EEAB36
+db $0A
+dd TwosonTeleNPCName
+TwosonTeleNPCBack:
+
+ORG $EEAB69
+db $0A
+dd HappyTeleNPCName
+HappyTeleNPCBack:
+
+ORG $EEABA9
+db $0A
+dd ThreedTeleNPCName
+ThreedTeleNPCBack:
+
+ORG $EEABDC
+db $0A
+dd SaturnTeleNPCName
+SaturnTeleNPCBack:
+
+ORG $EEAC16
+db $0A
+dd DunesTeleNPCName
+DunesTeleNPCBack:
+
+ORG $EEAC55
+db $0A
+dd FoursTeleNPCName
+FoursTeleNPCBack:
+
+ORG $EEAC8A
+db $0A
+dd WintersTeleNPCName
+WintersTeleNPCBack:
+
+ORG $EEACBE
+db $0A
+dd SummersTeleNPCName
+SummersTeleNPCBack:
+
+ORG $EEACF2
+db $0A
+dd ScarabaTeleNPCName
+ScarabaTeleNPCBack:
+
+ORG $EEAD26
+db $0A
+dd DalaamTeleNPCName
+DalaamTeleNPCBack:
+
+ORG $EEAD59
+db $0A
+dd DarkTeleNPCName
+DarkTeleNPCBack:
+
+ORG $EEAD93
+db $0A
+dd TendaTeleNPCName
+TendaTeleNPCBack:
+
+ORG $EEADCD
+db $0A
+dd UnderTeleNPCName
+UnderTeleNPCBack:
+
+ORG $EEAECB
+db $0A
+dd MagicTeleNPCName
+MagicTeleNPCBack:
+
+ORG $C630A8
+db $08
+dd ClearAndCallDad
+
+ORG $C91D47
+db $02
+
+ORG $61000B
+db $0A
+dl rock_text
+
+ORG $6100D7
+db $0A
+dl man_text
+
+ORG $C76074
+db $0A
+dl NessTonyHint
+
+ORG $C5EF84
+db $0A
+dl JeffTonyHint
+
+ORG $EED7ED
+db $0A
+dl FixLightningTypo
+
+ORG $EED873
+db $0A
+dl FixLightningTypo2
+
+ORG $C62E6D
+db $0A
+dl HandleEnergyLinkFromATM
+
+ORG $C62D30
+db $0A
+dl InitializeEnergyLink
+
+ORG $C68829 ;Disable PRV photo
+db $02
+
+ORG $C68876 ;Disable threed photo
+db $02
+
+ORG $C6886C ;Disable SV photo
+db $02
+
+ORG $C68927 ;Disable Deep Dark photo
+db $02
+
+ORG $C68847 ;Disable Winters photo
+db $02
+
+ORG $C87ADC ;Disable Brickroad photo
+db $02
+
+ORG $C688B9 ;Disable Dept store photo
+db $02
+
+ORG $C6889B ;Disable Museum photo
+db $02
+
+ORG $C60CC9 ;sesaphoto
+db $02
+
+ORG $C60C86 ;sesaphoto
+db $02
+
+ORG $C68887 ;Disable gold mine photo
+db $02
+
+ORG $C688FF ;Disable Summers photo
+db $02
+
+ORG $C688CD ;Disable Dalaam photo
+db $02
+
+ORG $2FF614
+db $0A
+dl StonehengePhotoText
+
+ORG $C62FD6
+db $08
+dd ShowDepositEnergy
+
+ORG $CFDF49
+dd UnderworldATM
+
+ORG $CFDBA2
+dd DarknessATM
+
+ORG $EF188B
+dd $EF2A4F
+
+ORG $D57B05
+db $C7
+
+ORG $C6332A
+db $0A
+dl ShowDadEnergy
+
+ORG $C63229
+db $0A
+dl PingDadEnergy
+
+ORG $CF1203
+dd SetSaturnLadderFlag
+
+;ORG $D01664
+;db $1F, $04 Flag for the Saturn Valley Ladder
+
+;ORG $CF19C7
+;dd JackieMoonsideSetup Only in dungeon er
+
+;ORG $CFC8C6
+;dd EverDeadText ; Only in dungeon ER
+
+;ORG $CF1657
+;dd CafeEverdredText ; Only in dungeon ER
+
+ORG $CFC50E
+dd JackieItemCheck
+
+ORG $CF0648
+dd UnlockOnettTeleport
+
+ORG $CF09E3
+dd $C9B10B
+
+ORG $CF1922
+dd $C9B10B
+
+ORG $CF00C6
+dd $C9B10B
+
+ORG $CF00BB
+dd $C9B10B
+
+ORG $CF2396
+dd $C9B10B
+
+ORG $CFBC06
+dd MinerDialogue
+
+ORG $C6122B
+db $0A
+dl MinerText2
+
+ORG $C93267
+db $0A
+dl $C9327F
+
+;ORG $D0D4E5
+;dd PokeyBattleExpanded
+
+;ORG $EFFF10
+;PokeyBattleExpanded:
+
+ORG $CF6BF1
+db $6D, $CF
+
+ORG $D5AD53
+dw $0209
+dw $0209; Skate Punk
+
+ORG $D5DD69 ; Loaded Dice
+dw $0209
+dw $0209
+dw $0209
+dw $0209
+
+ORG $D5A5F7 ; Loaded Dice
+dw $0209
+dw $0209
+dw $0209
+dw $0209
+
+ORG $D5AEC9 ; Starman Super
+dw $0209
+
+ORG $D03CA4 ; Remove bugged plate
+db $00
+
+ORG $CF0457
+dd OnettHotelSetTeleport
+
+ORG $EECA28
+db $0A
+dl MonkeyCaveShop
+
+ORG $C50051
+dd MonkeyShopGreeting
+
+ORG $C50519
+dd MonkeyShopBuyLine
+
+ORG $C50E1F
+dd MonkeyShopNotEnoughMoney
+
+ORG $C506B1
+dd MonkeyShopSelectItem
+
+ORG $C50849
+dd MonkeyShopPickCarrier
+
+ORG $C50381
+dd MonkeyShopNoBuy
+
+ORG $C50C87
+dd MonkeyShopCancelBuy
+
+ORG $C501E9
+dd MonkeyShopExitThanks
+
+ORG $C515B9
+dd MonkeyShopThank
+
+ORG $C519CB
+dd MonkeyShopCantCarry
+
+ORG $C51751
+dd MonkeyShopBuyAnother
+
+ORG $C50FB7
+dd MonkeyShopCantCarrySingle
+
+ORG $D5782C
+db $E0 ; Repel Sandwich
+db $5D
+db $5A
+db $7F
+db $5F
+db $6C ; Protein Drink
+db $8C ; Ruler
+
+ORG $C74C11
+dd PostFrankGuardText
+
+ORG $D7B200
+db $00
+
+;Tenda lower floor
+ORG $D7B708
+db $80
+
+ORG $D7B748
+db $80
+
+; Tenda Inn
+ORG $D7C4F0
+db $80
+
+ORG $C772F8 ; Magicant Mom
+db $CA
+
+ORG $C1D6B5
+LDA #$03D6
+
+ORG $C7FB56
+db $0A
+dl AddNessToEpilogue
+
+ORG $C9C356
+db $00, $00, $00
+
+ORG $C9C359
+db $00, $00, $00, $00
+
+ORG $CFB374
+db $00
+
+ORG $CF666B
+db $0C, $00
+
+ORG $C4FF00
+db $02, $00, $77, $02, $60, $c8, $AC, $02, $60, $dd ; Winters brickroad and phone
+
+ORG $C5E572
+db $00, $00 ; Fix vanilla storage bug
+
+ORG $C9AF4B
+db $C7, $00
+
+ORG $D56AE9
+db $3B
+
+ORG $C9FD46
+db $0A
+dl NewBikeText
+
+ORG $C6BFA3
+db $5E, $0A
+dl $EEBE84
+
+ORG $C8630A
+db $90, $90
+
+ORG $EEC613
+db $90, $90
+
+ORG $D6FB66
+StartingInvetory:
+dw $99F3
+
+StartingInvAmounts:
+dw $000B
+
+ORG $EF79DE
+db $0A
+dl FixYouWonAlignment
+
+ORG $C7AC44
+db $0A
+dl FixPhotoManLoad
+
+ORG $C33BF8
+db $09
+
+ORG $C33BB8
+db $00
+
+ORG $C7AC9B
+db $0A
+dl PhotomanLeave
+
+ORG $C3765F
+db $03
+dd PhotoManActionScript
+
+ORG $C701F6
+db $0A
+dl DenyHintsSaidNo
+
+ORG $C72664
+db $0A
+dl DenyHintsNoMoney
+
+ORG $CFA96E
+dd fixed_skyrunner_redirect
+
+ORG $C7C4D6
+dd EndScript
+
+ORG $C7C188
+dd EndScript
+
+ORG $C7C324
+dd EndScript
+
+ORG $CFC517
+dw $010D
+
+ORG $CF7E97
+db $B0, $90
+
+ORG $C4FF0A
+db $06, $00, $4f, $05, $40, $60, $50, $05, $60, $78, $53, $05, $68, $28, $51, $05
+db $68, $a0, $52, $05, $78, $60, $81, $03, $32, $74
+
+ORG $CF66D9
+dw $000D
+
+ORG $CFC51A
+dw $0284
+
+ORG $C37698
+db $03
+dd DisableCollAndAnim
+
+ORG $DA82A7
+db $44
+
+ORG $DAB4E7
+db $44
+
+ORG $DAB067
+db $44
+
+ORG $C14936
+JSL LeaveMoneyInMemory
+
+ORG $C17F21
+JML CheckExtraAddCommands
+
+ORG $C005EF
+LDA #$9900
+nop
+
+ORG $C005F5
+LDA #$00F8
+nop
+
+ORG $CFA93B
+dd RideSkycicle
+
+ORG $CFA933
+db $D3
+
+ORG $CF8919
+db $30, $68
+
+ORG $CFA932
+db $02
+
+ORG $C7CB74
+db $C7, $00
+
+ORG $D7A90C
+db $EC
+
+ORG $D68463
+;db $FF
+
+ORG $C8A8E3
+dd CheckForRubyPoo
+
+ORG $C80C40
+db $E1, $03
+
+ORG $C91EA8; TEST THIS!!!! SKY RUNNER FIX
+db $d2
+
+ORG $C99CCD
+db $0A
+dl DisableOSSOnLumine
+
+ORG $C0D668 ;Fix vanilla ambush detection
+LDA $9889
+
+ORG $EEC777
+db $73, $98, $99, $95, $96, $50, $0A
+dl TwosonCopBossText
+
+ORG $EEBDA4
+db $0A
+dl SetupNessForMagicantBoost
+
+
+;New data table go here
+
+
+;;;;;;;;;;;;;boss names
+ORG $EEEEBC
+db $76, $a2, $91, $9e, $9b, $02; Frank
+
+db $08, $bc, $ee, $ee, $ff, $0a, $3d, $7e, $c5; Pokey name text
+db $08, $bc, $ee, $ee, $ff, $0a, $5B, $7e, $c5; Pokey name text
+db $50, $50, $08, $bc, $ee, $ee, $ff, $0a, $c7, $e6, $c9; Belch Name next
+
+db $05, $27, $00, $05, $C2, $01, $0A, $D0, $C7, $EE
+
+db $08, $bc, $ee, $ee, $ff, $50, $9f, $96, $50, $0a, $64, $06, $c8
+
+db $76, $A2, $91, $9E, $9B, $A9, $A3, $A4, $95, $99, $9E, $50, $7D, $91, $A2, $9B
+db $50, $79, $79, $02;Frankystein Mark II
+
+db $76, $a2, $91, $9e, $9b, $a9, $a3, $a4, $95, $99, $9e, $02;Frankystein
+
+db $84, $99, $A4, $91, $9E, $99, $93, $50, $71, $9E, $A4, $02; Titanic Ant
+
+db $73, $91, $a0, $a4, $91, $99, $9e, $50, $83, $a4, $a2, $9f, $9e, $97, $02;Captain Strong
+
+db $75, $a6, $95, $a2, $94, $a2, $95, $94, $02;Everdred
+
+db $7d, $a2, $5e, $50, $73, $91, $a2, $a0, $91, $99, $9e, $a4, $95, $a2, $02;Carpainter
+
+db $7d, $9f, $9e, $94, $9f, $50, $7d, $9f, $9c, $95, $02;Mondo Mole
+
+db $72, $9f, $9f, $97, $95, $a9, $50, $84, $95, $9e, $a4, $02;Boogey Tent
+
+db $7d, $99, $9e, $99, $50, $72, $91, $a2, $96, $02; Mini Barf
+
+db $7d, $91, $a3, $a4, $95, $a2, $50, $72, $95, $9c, $93, $98, $02;Belch
+
+db $84, $a2, $99, $9c, $9c, $99, $9f, $9e, $91, $97, $95, $50, $83, $a0, $a2, $9f
+db $a5, $a4, $02;Trillionage sprout
+
+db $77, $a5, $91, $a2, $94, $99, $91, $9e, $50, $74, $99, $97, $97, $95, $a2, $02;uardian digger
+
+db $74, $95, $a0, $a4, $5e, $50, $83, $a4, $9f, $a2, $95, $50, $83, $a0, $9f, $9f
+db $9b, $02;Dept store
+
+db $a4, $98, $95, $50, $7d, $91, $9e, $99, $50, $7d, $91, $9e, $99, $50, $83, $a4
+db $91, $a4, $a5, $95, $02;MMS
+
+db $7d, $91, $9e, $99, $50, $7d, $91, $9e, $99, $02;Mani Mani
+
+db $73, $9c, $a5, $9d, $a3, $a9, $50, $82, $9f, $92, $9f, $a4, $02;Clumsy Robot
+
+db $83, $98, $a2, $9f, $9f, $9f, $9d, $02;Shroom
+
+db $80, $9c, $91, $97, $a5, $95, $50, $82, $91, $a4, $50, $9f, $96, $50, $74, $9f
+db $9f, $9d, $02;Rat
+
+db $84, $98, $a5, $9e, $94, $95, $a2, $50, $91, $9e, $94, $50, $83, $a4, $9f, $a2
+db $9d, $02;Thunderstorm
+
+db $7b, $a2, $91, $9b, $95, $9e, $02;Kraken
+
+db $77, $a5, $91, $a2, $94, $99, $91, $9e, $50, $77, $95, $9e, $95, $a2, $91, $9c
+db $02;General
+
+db $7d, $91, $a3, $a4, $95, $a2, $50, $72, $91, $a2, $96, $02;Barf
+
+db $83, $a4, $91, $a2, $9d, $91, $9e, $50, $74, $88, $02;DX
+
+db $74, $95, $9C, $A5, $A8, $95, $02;Deluxe
+
+db $75, $9C, $95, $93, $A4, $A2, $9F, $50, $83, $A0, $95, $93, $A4, $95, $A2, $02;Specter
+
+db $73, $91, $a2, $92, $9f, $9e, $50, $74, $9f, $97, $02;Carbon Dog
+
+db $78, $95, $91, $a6, $99, $9c, $a9, $50, $71, $a2, $9d, $95, $94, $50, $80, $9f
+db $9b, $95, $a9, $02;Heavily Pokey
+
+db $1c, $02, $01, $57, $a3, $50, $7e, $99, $97, $98, $a4, $9d, $91, $a2, $95, $02;Nightmare
+
+db $83, $a4, $91, $a2, $9d, $91, $9e, $50, $7a, $a5, $9e, $99, $9f, $a2, $02;Junior
+
+db $74, $99, $91, $9d, $9f, $9e, $94, $50, $74, $9f, $97, $02;Diamond Dog
+
+db $77, $99, $A9, $97, $91, $A3, $02; giygas
+
+db $00, $70, $08, $bc, $ee, $ee, $ff, $0a, $5d, $fc, $c8
+
+db $50, $08, $bc, $ee, $ee, $ff, $5e, $0a, $4e, $7f, $c5
+;;;;;;;;;;;;;;;;;;;;;;;;
+;Absorb the power of the earth
+db $91, $92, $a3, $9f, $a2, $92, $50, $a4, $98, $95, $50, $a0, $9f, $a7, $95, $a2
+db $50, $9f, $96, $50, $a4, $98, $95, $50, $75, $91, $a2, $a4, $98, $50, $99, $a4
+db $a3, $95, $9c, $96, $50, $99, $9e, $a4, $9f, $50, $a9, $9f, $a5, $a2, $50, $98
+db $95, $91, $a2, $a4, $5c, $02
+
+;Purge giygas's evil
+db $a0, $a5, $a2, $97, $95, $50, $77, $99, $a9, $97, $91, $a3, $57, $a3, $50, $95
+db $a6, $99, $9c, $50, $96, $a2, $9f, $9d, $50, $a4, $98, $95, $50, $a7, $9f, $a2
+db $9c, $94, $50, $9f, $9e, $93, $95, $50, $91, $9e, $94, $50, $96, $9f, $a2, $50
+db $91, $9c, $9c, $5c, $02
+
+db $18, $04
+;patch moves stuff here
+
+
+ORG $EEF136
+db $0A, $42, $1E, $C9
+
+display_melodies:
+db $93, $9b, $6a, $18, $05, $4e, $00, $07, $b6, $00, $1B, $03
+dd .has_giant_step
+db $08
+dd .display_dot
+db $0A
+dl .check_lilliput
+.has_giant_step:
+db $08
+dd .display_melody
+.check_lilliput:
+db $07, $B7, $00, $1B, $03
+dd .has_lilliput_steps
+db $08
+dd .display_dot
+db $0A
+dl .check_milky
+.has_lilliput_steps:
+db $08
+dd .display_melody
+.check_milky
+db $07, $B9, $00, $1B, $03
+dd .has_milky_well
+db $08
+dd .display_dot
+db $0A
+dl .check_rainy_circle
+.has_milky_well:
+db $08
+dd .display_melody
+.check_rainy_circle:
+db $07, $b8, $00, $1B, $03
+dd .has_rainy_circle
+db $08
+dd .display_dot
+db $0A
+dl .check_magnet_hill
+.has_rainy_circle:
+db $08
+dd .display_melody
+.check_magnet_hill:
+db $07, $BA, $00, $1B, $03
+dd .has_magnet_hill
+db $08
+dd .display_dot
+db $0A
+dl .check_pink_cloud
+.has_magnet_hill:
+db $08
+dd .display_melody
+.check_pink_cloud:
+db $07, $BB, $00, $1b, $03
+dd .has_pink_cloud
+db $08
+dd .display_dot
+db $0A
+dl .check_lumine_hall
+.has_pink_cloud:
+db $08
+dd .display_melody
+.check_lumine_hall:
+db $07, $BC, $00, $1B, $03
+dd .has_lumine_hall
+db $08
+dd .display_dot
+db $0A
+dl .check_fire_spring
+.has_lumine_hall:
+db $08
+dd .display_melody
+.check_fire_spring:
+db $07, $BD, $00, $1B, $03
+dd .has_fire_spring
+db $08
+dd .display_dot
+db $0A
+dl .done_melodies
+.has_fire_spring:
+db $08
+dd .display_melody
+.done_melodies:
+db $02
+
+.display_dot:
+db $70, $02
+
+.display_melody:
+db $AC, $02
+;;;;;;;;;;;;;;;;;;;;
+;sky runner stuff
+db $1F, $E5, $FF
+db $04, $EC, $02
+db $05, $8F, $02
+db $1F, $21, $DA
+db $10, $30
+db $1F, $1E, $BB, $00, $00, $10, $02
+db $1F, $EC, $FF, $00
+db $19, $26, $FF
+db $05, $EC, $02;Unset the flag
+db $04, $30, $00; remove the old sky runner
+db $05, $0B, $00
+db $02
+
+db $07, $30, $00, $1b, $02, $00, $bc, $c6, $ff, $06, $76, $02, $77, $b2, $c6, $00
+db $70, $7b, $99, $5d, $9b, $95, $99, $5d, $9b, $a9, $95, $5d, $9b, $a9, $95, $5d
+db $9b, $99, $99, $99, $5e, $03, $00, $70, $58, $89, $95, $a0, $5c, $10, $0f, $50
+db $79, $50, $96, $9f, $a5, $9e, $94, $50, $0a, $22, $bc, $c6
+
+db $1f, $eb, $ff, $06, $04, $0B, $00, $0A, $5A, $1E, $C9
+;;;;;;;;;;;;;;
+;hint guy
+onett_hint_man:
+db $04, $05, $04
+db $06, $f9, $03
+dd $FFC726DA
+dd $C701440A
+
+twoson_hint_man:
+db $04, $06, $04
+db $06, $fA, $03
+dd $FFC726DA
+dd $C7014F0A
+
+threed_hint_man:
+db $04, $07, $04
+db $06, $fB, $03
+dd $FFC726DA
+dd $C7015A0A
+
+fourside_hint_man:
+db $04, $08, $04
+db $06, $fc, $03
+dd $FFC726DA
+dd $C701650A
+
+summers_hint_man:
+db $04, $09, $04
+db $06, $fD, $03
+dd $FFC726DA
+dd $C701760A
+
+scaraba_hint_man:
+db $04, $0A, $04
+db $06, $fe, $03
+dd $FFC726DA
+dd $C701870A
+
+display_hint_price:
+
+db $06, $03, $04
+dd bought_five_hints
+
+db $06, $02, $04
+dd bought_four_hints
+
+db $06, $01, $04
+dd bought_three_hints
+
+db $06, $00, $04
+dd bought_two_hints
+
+db $06, $FF, $03
+dd bought_one_hint
+
+db $61, $60, $60
+db $0E, $64
+db $0D, $01, $02
+
+bought_one_hint:
+db $62, $60, $60
+db $0E, $C8
+db $0D, $01, $02
+
+bought_two_hints:
+db $64, $60, $60
+db $08
+dd get_2bytenum_part1
+db $1D, $08, $90, $01
+db $1B, $00, $1D, $09, $90, $01
+db $08
+dd get_2bytenum_part2
+db $02
+
+bought_three_hints:
+db $68, $60, $60
+db $08
+dd get_2bytenum_part1
+db $1D, $08, $20, $03
+db $1B, $00, $1D, $09, $20, $03
+db $08
+dd get_2bytenum_part2
+db $02
+
+bought_four_hints:
+db $61, $66, $60, $60
+db $08
+dd get_2bytenum_part1
+db $1D, $08, $40, $06
+db $1B, $00, $1D, $09, $40, $06
+db $08
+dd get_2bytenum_part2
+db $02
+
+bought_five_hints:
+db $63, $62, $60, $60
+db $08
+dd get_2bytenum_part1
+db $1D, $08, $80, $0C
+db $1B, $00, $1D, $09, $80, $0C
+db $08
+dd get_2bytenum_part2
+db $02
+
+get_2bytenum_part1:
+db $19, $27, $06, $1b, $04, $1d, $09, $00, $00, $02
+
+get_2bytenum_part2:
+db $1d, $08, $00, $00, $1b, $01, $1b, $04, $02
+
+hint_error_message:
+db $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97, $50, $a7, $95, $9e, $a4, $50, $a4
+db $95, $a2, $a2, $99, $92, $9c, $a9, $50, $a7, $a2, $9f, $9e, $97, $50, $98, $95
+db $a2, $95, $5e, $02
+
+count_hints:
+db $06, $04, $04
+dd .end_hints
+db $06, 03, $04;if we have 5
+dd .set_sixth_hint
+db $06, $02, $04;if we have 4
+dd .set_fifth_hint
+db $06, $01, $04;if we have 3
+dd .set_fourth_hint
+db $06, $00, $04
+dd .set_third_hint; if we have 2
+db $06, $FF, $03
+dd .set_second_hint
+db $04, $FF, $03, $02
+
+
+.set_second_hint:
+db $04, $00, $04
+db $02
+.set_third_hint:
+db $04, $01, $04
+db $02
+.set_fourth_hint:
+db $04, $02, $04
+db $02
+.set_fifth_hint:
+db $04, $03, $04
+db $02
+.set_sixth_hint:
+db $04, $04, $04
+.end_hints:
+db $02
+
+push_onett_hint:
+db $06, $0B, $04
+dd .skip_hint
+db $08
+dd server_hint_text
+db $1B, $02
+dd .skip_hint
+db $04, $0B, $04
+.skip_hint:
+db $02
+
+push_twoson_hint:
+db $06, $0C, $04
+dd .skip_hint
+db $08
+dd server_hint_text
+db $1B, $02
+dd .skip_hint
+db $04, $0C, $04
+.skip_hint:
+db $02
+
+push_threed_hint:
+db $06, $0D, $04
+dd .skip_hint
+db $08
+dd server_hint_text
+db $1B, $02
+dd .skip_hint
+db $04, $0D, $04
+.skip_hint:
+db $02
+
+push_fourside_hint:
+db $06, $0E, $04
+dd .skip_hint
+db $08
+dd server_hint_text
+db $1B, $02
+dd .skip_hint
+db $04, $0E, $04
+.skip_hint:
+db $02
+
+push_summers_hint:
+db $06, $0F, $04
+dd .skip_hint
+db $08
+dd server_hint_text
+db $1B, $02
+dd .skip_hint
+db $04, $0F, $04
+.skip_hint:
+db $02
+
+push_scaraba_hint:
+db $06, $10, $04
+dd .skip_hint
+db $08
+dd server_hint_text
+db $1B, $02
+dd .skip_hint
+db $04, $10, $04
+.skip_hint:
+db $02
+
+
+server_hint_text:
+db $01, $70, $76, $9f, $a2, $50, $91, $9e, $50, $95, $a8, $a4, $a2, $91, $50, $54
+db $65, $60, $60, $5c, $10, $03, $50, $79, $57, $9c, $9c, $50, $a4, $95, $9c, $9c
+db $50, $a4, $98, $95, $50, $a3, $95, $a2, $a6, $95, $a2, $50, $91, $92, $9f, $a5
+db $a4, $50, $a4, $98, $99, $a3, $50, $98, $99, $9e, $a4, $5e, $03, $01, $70, $87
+db $98, $91, $a4, $50, $94, $9f, $50, $a9, $9f, $a5, $50, $a3, $91, $a9, $6f, $01
+db $08, $4d, $dd, $c7, $ff, $11, $12
+
+db $09, $02
+dd pushhint_saidyes
+dd pushhint_no
+
+pushhint_no:
+db $1D, $19, $01
+db $02
+
+pushhint_saidyes:
+db $19, $27, $06, $1b, $04, $1d, $09, $00, $00, $1d, $08, $F4, $01, $1b, $00, $1d
+db $09, $F4, $01, $1d, $08, $00, $00, $1b, $01, $1b, $04
+db $1D, $14, $00, $00, $00, $00
+db $1B, $03
+dd pushhint_nomoney
+db $1D, $09, $00, $00
+db $18, $0A
+db $1F, $02, $78
+db $1D, $19, $FA
+db $02
+pushhint_nomoney:
+db $12, $70, $89, $9f, $a5, $50, $94, $9f, $9e, $57, $a4, $50, $98, $91, $a6, $95
+db $50, $95, $9e, $9f, $a5, $97, $98, $50, $9d, $9f, $9e, $95, $a9, $50, $96, $9f
+db $a2, $50, $a4, $98, $91, $a4, $50, $a0, $91, $93, $9b, $91, $97, $95, $5e, $03
+db $0A
+dd pushhint_no
+
+post_hint_text:
+db $70, $79, $96, $50, $a9, $9f, $a5, $50, $a7, $91, $9e, $a4, $50, $91, $9e, $9f, $a4
+db $98, $95, $a2, $50, $98, $99, $9e, $a4, $5c, $10, $02, $50, $96, $99, $9e, $94
+db $50, $9d, $95, $50, $91, $a4, $50, $9f, $9e, $95, $50, $9f, $96, $50, $9d, $a9
+db $50, $9f, $a4, $98, $95, $a2, $50, $a3, $98, $9f, $a0, $a3, $5e, $13, $02
+
+tracy_backpack_tex:
+db $70, $89, $9f, $a5, $50, $93, $91, $9e, $50, $91, $9c, $a3, $9f, $50, $9c, $95
+db $91, $a6, $95, $50, $91, $9e, $a9, $a4, $98, $99, $9e, $97, $50, $a9, $9f, $a5
+db $50, $94, $9f, $9e, $57, $a4, $50, $9e, $95, $95, $94, $50, $99, $9e, $50, $a9
+db $9f, $a5, $a2, $50, $92, $91, $93, $9b, $a0, $91, $93, $9b, $5e, $03, $01, $70
+db $89, $9f, $a5, $50, $93, $91, $9e, $50, $9f, $a0, $95, $9e, $50, $99, $a4, $50
+db $a7, $99, $a4, $98, $50, $a4, $98, $95, $50, $82, $50, $92, $a5, $a4, $a4, $9f
+db $9e, $51, $13, $02
+
+tracy_backpack_twice_tex:
+db $70, $74, $99, $94, $50, $a9, $9f, $a5, $50, $96, $9f, $a2, $97, $95, $a4, $50
+db $91, $9c, $a2, $95, $91, $94, $a9, $5c, $50, $92, $99, $97, $50, $92, $a2, $9f
+db $6f, $03, $01, $70, $89, $9f, $a5, $50, $93, $91, $9e, $50, $a3, $a4, $9f, $a2
+db $95, $50, $99, $a4, $95, $9d, $a3, $50, $a7, $99, $a4, $98, $50, $a4, $98, $95
+db $50, $82, $50, $92, $a5, $a4, $a4, $9f, $9e, $51, $13, $02
+
+winters_drugstore_space:
+db $08
+dd drugstore_spacecheck
+db $1B, $02
+dd $FFC6AEE1
+dd $C6AE4B0A
+
+winters_drugstore_space2:
+db $08
+dd drugstore_spacecheck
+db $1B, $02
+dd $FFC6AEE1
+dd $C6AEAC0A
+
+drugstore_spacecheck:
+db $1D, $03, $FF, $1B, $02
+dd .no_space
+db $1D, $19, $FE
+db $02
+.no_space:
+db $1D, $19, $01
+db $02
+
+fix_boogey_tent:
+db $1B, $02
+dd .no_space
+db $04, $57, $00
+db $02
+.no_space:
+db $04, $9D, $02
+db $02
+;Newtext
+
+boost_speed_dynamic:
+db $1B, $04
+db $1F, $C0, $04
+dd boost_ness_speed
+dd boost_paula_speed
+dd boost_jeff_speed
+dd boost_poo_speed
+db $1b, $04, $02
+
+
+boost_vitality_dynamic:
+db $1B, $04
+db $1F, $C0, $04
+dd boost_ness_vitality
+dd boost_paula_vitality
+dd boost_jeff_vitality
+dd boost_poo_vitality
+db $1b, $04, $02
+
+boost_guts_dynamic:
+db $1B, $04
+db $1F, $C0, $04
+dd boost_ness_guts
+dd boost_paula_guts
+dd boost_jeff_guts
+dd boost_poo_guts
+db $1b, $04, $02
+
+boost_iq_dynamic:
+db $1B, $04
+db $1F, $C0, $04
+dd boost_ness_iq
+dd boost_paula_iq
+dd boost_jeff_iq
+dd boost_poo_iq
+db $1b, $04, $02
+
+boost_luck_dynamic:
+db $1B, $04
+db $1F, $C0, $04
+dd boost_ness_luck
+dd boost_paula_luck
+dd boost_jeff_luck
+dd boost_poo_luck
+db $1b, $04, $02
+
+boost_ness_speed:
+db $1E, $0C, $01, $05, $03, $02
+
+boost_paula_speed:
+db $1E, $0C, $02, $05, $03, $02
+
+boost_jeff_speed:
+db $1E, $0C, $03, $05, $03, $02
+
+boost_poo_speed:
+db $1E, $0C, $04, $05, $03, $02
+;;;;;;;;;;
+boost_ness_vitality:
+db $1E, $0D, $01, $05, $03, $02
+
+boost_paula_vitality:
+db $1E, $0D, $02, $05, $03, $02
+
+boost_jeff_vitality:
+db $1E, $0D, $03, $05, $03, $02
+
+boost_poo_vitality:
+db $1E, $0D, $04, $05, $03, $02
+;;;;;;;;;;;;;;
+boost_ness_guts:
+db $1E, $0B, $01, $05, $03, $02
+
+boost_paula_guts:
+db $1E, $0B, $02, $05, $03, $02
+
+boost_jeff_guts:
+db $1E, $0B, $03, $05, $03, $02
+
+boost_poo_guts:
+db $1E, $0B, $04, $05, $03, $02
+;;;;;;;;
+boost_ness_iq:
+db $1E, $0A, $01, $05, $03, $02
+
+boost_paula_iq:
+db $1E, $0A, $02, $05, $03, $02
+
+boost_jeff_iq:
+db $1E, $0A, $03, $05, $03, $02
+
+boost_poo_iq:
+db $1E, $0A, $04, $05, $03, $02
+;;;;;;;;;;;;
+boost_ness_luck:
+db $1E, $0E, $01, $05, $03, $02
+
+boost_paula_luck:
+db $1E, $0E, $02, $05, $03, $02
+
+boost_jeff_luck:
+db $1E, $0E, $03, $05, $03, $02
+
+boost_poo_luck:
+db $1E, $0E, $04, $05, $03, $02
+
+db $06, $69, $00
+dd $00C74207
+db $0A
+dl LeaderHandinBadge
+db $01
+db $0A, $AD, $69, $C7
+
+;new text go here
+db $19, $10, $01
+db $1B, $04
+db $0E, $00
+db $08
+db $7F, $DC, $C7, $00
+db $1b, $02
+dd $00C7E6D7
+db $7E, $9F, $92, $9F, $94, $A9, $02
+
+PrintPlayerNum:
+db $1B, $06
+db $1C, $0A, $00, $00, $00, $00, $5E, $50, $02
+
+PlayerNameText:
+db $06, $13, $04
+dd .PrintPlayerName
+db $1B, $06
+db $80, $9C, $91, $A9, $95, $A2, $1C, $0A, $00, $00, $00, $00, $50, $50, $B8, $02
+.PrintPlayerName:
+db $1C, $05, $AD, $50, $50, $B8, $02
+
+LilliputDoor:
+db $06, $15, $04
+dd .LilliputUnlocked
+db $18, $01, $01
+db $70, $7f, $9e, $50, $93, $9c, $9f, $a3, $95, $a2, $50, $99, $9e, $a3, $a0, $95
+db $93, $a4, $99, $9f, $9e, $5c, $10, $05, $50, $99, $a4, $50, $91, $93, $a4, $a5
+db $91, $9c, $9c, $a9, $50, $91, $a0, $a0, $95, $91, $a2, $a3, $50, $a4, $9f, $50
+db $92, $95, $50, $a0, $91, $99, $9e, $a4, $95, $94, $50, $9f, $9e, $5e, $5e, $5e
+db $03, $01, $70, $7c, $9f, $9f, $9b, $99, $9e, $97, $50, $93, $9c, $9f, $a3, $95
+db $a2, $5c, $10, $04, $50, $a4, $98, $95, $a2, $95, $50, $91, $a0, $a0, $95, $91
+db $a2, $a3, $50, $a4, $9f, $50, $92, $95, $50, $91, $50, $a4, $99, $9e, $a9, $50
+db $9b, $95, $a9, $98, $9f, $9c, $95, $51
+db $1D, $05, $FF, $7D
+db $1B, $03
+dd .UnlockDoor
+db $13
+db $18, $04
+db $02
+.UnlockDoor:
+db $03, $01, $70
+db $19, $10, $01
+db $1B, $04
+db $1C, $02, $00
+db $50
+db $a5, $9e, $9c, $9f, $93, $9b, $95, $94, $50, $a4, $98, $95, $50, $94, $9f, $9f
+db $a2, $50, $a5, $a3, $99, $9e, $97, $50, $a4, $98, $95, $50, $1c, $05, $7d, $51, $1f
+db $02, $76, $03, $01, $70, $84, $98, $95, $50, $94, $9f, $9f, $a2, $50, $9f, $a0
+db $95, $9e, $95, $94, $51
+db $03
+db $1D, $01, $FF, $7D
+db $04, $15, $04
+db $18, $04
+.LilliputUnlocked:
+db $0A, $0B, $B1, $C9
+
+TinyKeyDesc:
+db $01, $50, $6c, $1c, $05, $7d, $6e, $03, $00, $70, $71, $50, $9b, $95, $a9, $50
+db $a3, $9f, $50, $a3, $9d, $91, $9c, $9c, $5c, $50, $a9, $9f, $a5, $50, $93, $91
+db $9e, $50, $92, $91, $a2, $95, $9c, $a9, $50, $a3, $95, $95, $50, $99, $a4, $5e
+db $03, $01, $70, $79, $a4, $57, $a3, $50, $93, $9f, $a6, $95, $a2, $95, $94, $50
+db $99, $9e, $50, $92, $9c, $a5, $95, $50, $a0, $91, $99, $9e, $a4, $5e, $5e, $5e
+db $13, $02
+
+LavapantsDesc:
+db $01, $50, $6c, $1c, $05, $B3, $6e, $03, $00, $70, $7c, $91, $a6, $91, $5d, $a0
+db $a2, $9f, $9f, $96, $50, $a0, $91, $9e, $a4, $a3, $50, $a0, $91, $99, $9e, $a3
+db $a4, $91, $9b, $99, $9e, $97, $9c, $a9, $50, $96, $9f, $a2, $97, $95, $94, $50
+db $92, $a9, $50, $91, $50, $84, $95, $9e, $94, $91, $5e, $03, $01, $70, $84, $98
+db $95, $a9, $50, $95, $a6, $95, $9e, $50, $a7, $9f, $a2, $9b, $50, $a5, $9e, $94
+db $95, $a2, $a7, $91, $a4, $95, $a2, $51, $03, $01, $70, $58, $74, $9f, $95, $a3
+db $50, $9e, $9f, $a4, $50, $a0, $a2, $9f, $a4, $95, $93, $a4, $50, $91, $97, $91
+db $99, $9e, $a3, $a4, $50, $98, $95, $91, $a4, $50, $9f, $a2, $50, $96, $99, $a2
+db $95, $5e, $13, $02
+
+FireSpringDoor:
+db $06, $16, $04
+dd .FireSpringUnlocked
+db $18, $01, $01
+db $70, $87, $98, $9f, $91, $98, $51, $03, $01, $70, $79, $a4, $57, $a3, $50, $a3
+db $9f, $50, $98, $9f, $a4, $5c, $10, $05, $50, $a9, $9f, $a5, $50, $93, $9f, $a5
+db $9c, $94, $50, $9d, $95, $9c, $a4, $51
+db $1D, $05, $FF, $B3, $03
+db $1B, $03
+dd .UnlockDoor
+
+db $01, $70, $72, $95, $a4, $a4, $95
+db $a2, $50, $93, $9f, $9d, $95, $50, $92, $91, $93, $9b, $50, $a7, $99, $a4, $98
+db $50, $a3, $9f, $9d, $95, $50, $9c, $91, $a6, $91, $5d, $a0, $a2, $9f, $9f, $96
+db $50, $a0, $91, $9e, $a4, $a3, $5e, $5e, $5e
+;Todo; Somewhere in this, place the jump to UnlockDoor after "It's so hot you could melt"
+db $13
+db $18, $04
+db $02
+.UnlockDoor:
+db $08
+dd LeaderPantsText
+db $1D, $01, $FF, $B3 ;Todo. Add check for presence of item....? Find a way to stop this if we already used tenda pants
+db $18, $04
+.FireSpringUnlocked
+db $0A, $0B, $B1, $C9
+
+LavaPantsUseTxt:
+db $01, $70
+db $1C, $0D
+db $1B, $04
+.DoorPantsTex:
+db $50
+db $a5, $a3, $95, $94, $50, $a4, $98, $95, $50, $1c, $05, $b3, $51
+db $1F, $02, $73
+db $14
+db $01, $70, $7E, $9F, $A7, $5C, $50
+db $08
+dd $00C7E602
+db $50
+db $93, $91, $9e, $50, $95, $91, $a3, $99, $9c, $a9, $50, $a7, $99, $a4, $98, $a3
+db $a4, $91, $9e, $94, $50, $a4, $98, $95, $50, $9c, $91, $a6, $91, $50, $99, $9e
+db $50, $76, $99, $a2, $95, $50, $83, $a0, $a2, $99, $9e, $97, $5e
+db $14
+db $18, $04
+db $04, $16, $04
+db $02
+
+RememberedTex:
+db $50, $A2, $95, $9D, $95, $9D, $92, $95, $A2, $95, $94, $50
+db $A4, $98, $95, $50, $02
+
+SetupStorageTakeMenu:
+db $18, $01, $29
+db $19, $02
+db $83, $a4, $9f, $a2, $91, $97, $95, $02
+db $19, $02
+db $7b, $95, $a9, $50, $79, $a4, $95, $9d, $a3, $02
+db $1C, $07, $02
+db $11
+db $09, $02
+dd .Storage
+dd .KeyStorage
+db $0A
+dl $EEC6AB
+.Storage:
+;db $18, $00
+db $18, $03, $29
+db $0A
+dl $EE9699
+.KeyStorage:
+db $1C, $1D, $01
+db $0A
+dl .Storage
+
+InitStorageMen:
+db $18, $03, $0D
+db $18, $00
+db $1C, $1E, $01
+db $0A
+dl SetupStorageTakeMenu
+
+CheckItemOnStore:
+db $1B, $00
+db $19, $19, $00, $00
+db $1B, $05
+db $1C, $1F, $01
+db $1B, $01
+db $1d, $12, $00, $00
+db $1C, $1E, $01
+db $0A
+dl $EE9690
+
+ORG $EEFAA0
+SancFirst:
+db $96, $99, $A2, $A3, $A4, $02
+
+SancSecond:
+db $A3, $95, $93, $9F, $9E, $94, $02
+
+SancThird:
+db $A4, $98, $99, $A2, $94, $02
+
+SancFourth:
+db $96, $9F, $A5, $A2, $A4, $98, $02
+
+SancFifth:
+db $96, $99, $96, $A4, $98, $02
+
+SancSixth:
+db $A3, $99, $A8, $A4, $98, $02
+
+SancSeventh:
+db $A3, $95, $A6, $95, $9E, $A4, $98, $02
+
+SancEighth:
+db $95, $99, $97, $98, $A4, $98, $02
+
+RainyCircleIntro:
+db $08
+dd SancFourth
+db $0A
+dl $C6BEAC
+
+FireSpringIntro:
+db $08
+dd SancEighth
+db $0A
+dl $EF5817
+
+CheckAbsoluteStorage:
+db $1B, $06
+db $18, $03, $01
+db $1B, $02
+dd $00EE9699
+db $0A
+dl $EE96BD
+
+OpenLockers:
+.TopRightLocker:
+db $1B, $00
+db $08
+dd .CheckIfUseKeytoLocker
+db $1B, $02
+dd $C6FB5D
+db $06, $58, $00
+dd $C7D9FD
+db $08
+dd .LockerItemAwaitsText
+db $1C, $05, $01
+db $50
+db $99, $9E, $A3, $99, $94, $95, $51, $03, $01
+db $08
+dd .CheckForLockerItemSpace
+db $1B, $02
+dd $C685BB
+db $1D, $0E, $FF, $11
+db $08
+dd .LockerGetItemText
+db $03
+db $04, $58, $00
+db $02
+
+.TopLeftLocker:
+db $1B, $00
+db $08
+dd .CheckIfUseKeytoLocker
+db $1B, $02
+dd $C6FB5D
+db $06, $F4, $03
+dd $C7D9FD
+db $08
+dd .LockerItemAwaitsText
+db $1C, $05, $01
+db $50
+db $99, $9E, $A3, $99, $94, $95, $51, $03, $01
+db $08
+dd .CheckForLockerItemSpace
+db $1B, $02
+dd $C685BB
+db $1D, $0E, $FF, $11
+db $08
+dd .LockerGetItemText
+db $03
+db $04, $F4, $03
+db $02
+
+.BottomRightLocker:
+db $1B, $00
+db $08
+dd .CheckIfUseKeytoLocker
+db $1B, $02
+dd $C6FB5D
+db $06, $17, $00
+dd $C7D9FD
+db $08
+dd .LockerItemAwaitsText
+db $1C, $05, $01
+db $50
+db $99, $9E, $A3, $99, $94, $95, $51, $03, $01
+db $08
+dd .CheckForLockerItemSpace
+db $1B, $02
+dd $C685BB
+db $1D, $0E, $FF, $11
+db $08
+dd .LockerGetItemText
+db $03
+db $04, $17, $00
+db $02
+
+.BottomLeftLocker:
+db $1B, $00
+db $08
+dd .CheckIfUseKeytoLocker
+db $1B, $02
+dd $C6FB5D
+db $06, $4E, $01
+dd $C7D9FD
+db $08
+dd .LockerItemAwaitsText
+db $1C, $05, $01
+db $50
+db $99, $9E, $A3, $99, $94, $95, $51, $03, $01
+db $08
+dd .CheckForLockerItemSpace
+db $1B, $02
+dd $C685BB
+db $1D, $0E, $FF, $11
+db $08
+dd .LockerGetItemText
+db $03
+db $04, $4E, $01
+db $02
+
+.CheckIfUseKeytoLocker:
+db $08
+dd .CheckItemUsage
+db $0B, $CD
+db $0A
+dd CheckForLockerKey
+db $02
+
+.CheckItemUsage:
+db $19, $19, $00, $00
+db $1B, $04
+db $02
+
+.OpenLockerText:
+db $70, $19, $10, $01
+db $1C, $02, $00
+db $50, $9f, $a0, $95, $9e, $95, $94, $50, $a4, $98, $95, $50, $9c, $9f, $93, $9b
+db $95, $a2, $50, $a5, $a3, $99, $9e, $97, $50, $99, $a4, $a3, $50, $9b, $95, $a9
+db $51
+db $1f, $02, $75, $03, $02
+
+.CheckForLockerItemSpace:
+db $1D, $03, $FF
+db $02
+
+.LockerGetItemText:
+db $08
+dd $C7DCCF
+db $02
+
+.MiddleLocker:
+db $70, $84, $98, $95, $50, $9c, $9f, $93, $9b, $50, $99, $a3, $50, $92, $a5, $a3
+db $a4, $95, $94, $5e, $13, $02
+
+.LockerItemAwaitsText:
+db $01
+db $70
+db $78, $95, $a9, $51, $10, $10, $50, $84, $98, $95, $a2, $95, $50, $99, $a3, $50
+db $91, $50, $02
+
+fixed_skyrunner_redirect:
+db $18, $04
+db $1D, $05, $FF, $9E
+db $1B, $02
+dd broken_skyrunner_text
+db $1D, $01, $FF, $9E
+db $0A
+dl fixed_skyrunner_text
+
+SpecialTexMagicant:
+db $7D, $91, $97, $99, $93, $91, $9E, $A4, $50, $84, $95, $9C, $95, $A0, $9F, $A2
+db $A4, $00
+
+SpecialTexNess:
+db $7E, $95, $A3, $A3, $00
+
+SpecialTexPhotoGuy:
+db $A0, $98, $9F, $A4, $9F, $97, $A2, $91, $A0, $98, $00
+
+NpcNessGiveOverride:
+db $0D, $01
+db $1B, $04
+db $1B, $00
+db $0B, $15
+db $1B, $03
+dd .GiveNess
+db $1B, $01
+db $0B, $16
+db $1B, $03
+dd PhotoHandler
+db $1B, $01
+db $0A
+dl $EEAAAB
+.GiveNess:
+db $1C, $04
+db $1F, $00, $00
+db $7b, $10, $85, $01, $1f, $03, $70, $58
+db $1c, $02, $01
+db $50, $9a, $9f, $99, $9e, $95, $94, $50, $a9, $9f, $a5, $5e, $59
+db $04, $17, $04
+db $1f, $11, $01, $02, $01
+db $02
+
+NessUnlockText:
+db $70, $78, $95, $a9, $51, $03, $01, $70, $79, $57, $9d, $50, $1c, $02, $01, $51
+db $03, $01, $70, $79, $a4, $57, $a3, $50, $97, $a2, $95, $91, $a4, $50, $a4, $9f
+db $50, $96, $99, $9e, $91, $9c, $9c, $a9, $50, $9d, $95, $95, $a4, $50, $a9, $9f
+db $a5, $50, $97, $a5, $a9, $a3, $51, $13
+db $18, $04, $1f, $00, $00, $0b, $10, $78, $50, $08, $10, $F8, $D5, $00, $1f, $03
+db $18, $01, $01, $70, $1c, $02, $01, $50, $9a, $9f, $16, $b9, $94, $15, $a8, $03
+db $18, $04, $1f, $11, $01, $02
+db $02
+
+SetNpcPooPSIFlag:
+db $04, $D1, $03
+db $1f, $71, $04, $02, $02
+
+DisplayDollars10:
+db $54, $61, $60, $00
+
+DisplayDollars100:
+db $54, $61, $60, $60, $00
+
+DisplayDollars1000:
+db $54, $61, $60, $60, $60, $00
+
+
+;FOR TESTING!!!!
+;ORG $CF09EE
+;dl $EEf790
+
+;ORG $CF09F2
+;db $15, $84
+
+;ORG $CF23D2
+;db $16, $84
+
+;ORG $CF23CE
+;dd FireSpringDoor
+;;;;;;;;;;;;;;;;;
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C1C84A
+JSR $C80E
+
+ORG $C1C7FD
+CLC
+ ADC #psi_letters
+ STA $0E
+ lda #$00FD
+ sta $10
+STZ $12
+STZ $14
+BRA $10
+JSL sort_psi_menu
+JMP $163C
+
+ORG $F00000
+sort_psi_menu:
+REP #$31
+PHD
+TDC
+ADC #$FFF8
+TCD
+LDA $8958
+CMP #$FFFF
+BNE .nojump
+JMP .Return
+.nojump:
+ASL
+TAX
+LDA $88E4,X
+LDY #$0052
+JSL $C08FF7
+TAY
+LDA $867B,Y
+STA $00
+JSL $C10C49
+DEC
+BNE $06
+LDA #$FFFF
+JMP .Return
+BMI $F8
+STA $02
+LDA $00
+LDY #$002D
+JSL $C08FF7
+CLC
+ADC #$89D4
+STA $04
+LDX $04
+TXA
+.selsort_outer:
+LDY $02
+STY $06
+CLC
+ADC #$002D
+TAY
+LDA $000A,X
+CMP $000A,Y
+BNE $06
+LDA $0008,X
+CMP $0008,Y
+BCC $01
+TYX
+TYA
+DEC $06
+BNE $E5
+CPX $04
+BEQ .selsort_outer_no_swap
+LDY $04
+SEP #$20
+LDA #$27
+STA $00
+.swap_loop:
+LDA $0006,X
+XBA
+LDA $0006,Y
+STA $0006,X
+XBA
+STA $0006,Y
+INX
+INY
+DEC $00
+BNE .swap_loop
+REP #$20
+.selsort_outer_no_swap:
+LDA $04
+CLC
+ADC #$002D
+STA $04
+TAX
+DEC $02
+BNE .selsort_outer
+LDA #$0000
+.Return:
+PLD
+RTL
+
+GetProgressiveItems:
+JSR SwapItemForProg
+STA $0000,X
+JML $C18B65
+
+GetProgressiveGoingtoStorage:
+AND #$00FF
+SEP #$20
+JSR SwapItemForProg
+STA $0000,X
+JML $C19164
+
+CheckifNameProgressive:
+SEP #$20
+CMP #$E3
+BCC .NormalItem
+CMP #$E8
+BCS .NormalItem
+JSR SwapNameForProg
+.NormalItem:
+REP #$20
+JML GetItemName
+
+SwapItemForProg:
+CMP #$E3
+BCC .NormalItem
+CMP #$E8
+BCS .NormalItem
+SEC
+SBC #$E3
+PHX
+TAX
+LDA $B5E0,X ;Progressive item count
+CMP ProgCaps,X
+BCS .CappedItem
+INC $B5E0,X
+BRA .GetItem
+.CappedItem:
+LDA $B5E0,X
+.GetItem:
+SEP #$10
+CPX #$00
+BEQ .GetProgBat
+CPX #$01
+BEQ .GetProgPan
+CPX #$02
+BEQ .GetProgGun
+CPX #$03
+BEQ .GetProgBrace
+CPX #$04
+BEQ .GetProgOther
+.GotProgItem:
+REP #$10
+PLX
+.NormalItem:
+RTS
+.GetProgBat:
+TAX
+LDA ProgBats,X
+JMP .GotProgItem
+.GetProgPan:
+TAX
+LDA ProgPans,X
+JMP .GotProgItem
+.GetProgGun:
+TAX
+LDA ProgGuns,X
+JMP .GotProgItem
+.GetProgBrace
+TAX
+LDA ProgBracelets,X
+JMP .GotProgItem
+.GetProgOther
+TAX
+LDA ProgOther,X
+JMP .GotProgItem
+
+SwapNameForProg:
+SEC
+SBC #$E3
+PHX
+TAX
+LDA $B5E0,X ;Progressive item count
+SEP #$10
+CPX #$00
+BEQ .GetProgBat
+CPX #$01
+BEQ .GetProgPan
+CPX #$02
+BEQ .GetProgGun
+CPX #$03
+BEQ .GetProgBrace
+CPX #$04
+BEQ .GetProgOther
+.GotProgItem:
+REP #$10
+PLX
+RTS
+.GetProgBat:
+TAX
+LDA ProgBats,X
+JMP .GotProgItem
+.GetProgPan:
+TAX
+LDA ProgPans,X
+JMP .GotProgItem
+.GetProgGun:
+TAX
+LDA ProgGuns,X
+JMP .GotProgItem
+.GetProgBrace
+TAX
+LDA ProgBracelets,X
+JMP .GotProgItem
+.GetProgOther
+TAX
+LDA ProgOther,X
+JMP .GotProgItem
+
+NewLuckySandwich:
+REP #$31
+LDA #$0010
+JSL .RandBankC2
+CMP #$0007
+BCC .60HPRS
+LDA #$0009
+JSL .RandBankC2
+CMP #$0004
+BCC .240HPRS
+LDA #$0005
+JSL .RandBankC2
+CMP #$0003
+BCC .FullHPRS
+LDA #$0002
+JSL .RandBankC2
+CMP #$0000
+BEQ .5PP
+LDA #$0003
+JSL .RandBankC2
+BCS .20PP
+BRA .FullHeal
+.60HPRS:
+LDA #$003C
+JSL .MultBankC2
+TAX
+LDA $A972
+JSL .RestHP
+BRA .Return
+.240HPRS:
+LDA #$00F0
+JSL .MultBankC2
+TAX
+LDA $A972
+JSL .RestHP
+BRA .Return
+.FullHPRS
+LDX #$0F27
+LDA $A972
+JSL .RestHP
+BRA .Return
+.5PP:
+LDA #$0005
+JSL .MultBankC2
+TAX
+LDA $A972
+JSL .RestPP
+BRA .Return
+.20PP:
+LDA #$0014
+JSL .MultBankC2
+TAX
+LDA $A972
+JSL .RestPP
+BRA .Return
+.FullHeal:
+LDX #$0F27
+LDA $A972
+JSL .RestHP
+LDX $A972
+LDA $001B,X
+TAX
+LDA $A972
+JSL .RestPP
+.Return:
+RTL
+
+.RandBankC2:
+PHY
+LDY #$6A2C
+JSL goto_bank_c2
+PLY
+RTL
+
+.MultBankC2:
+PHY
+LDY #$6AFC
+JSL goto_bank_c2
+PLY
+RTL
+
+.RestHP:
+PHY
+LDY #$7293
+JSL goto_bank_c2
+PLY
+RTL
+
+.RestPP:
+PHY
+LDY #$7317
+JSL goto_bank_c2
+PLY
+RTL
+
+GetNewSwirlColorNormal:
+LDA #$0004
+STA $14
+LDA #$0004
+STA $04
+LDY #$0000
+STY $12
+JML $C2E8F9
+
+GetNewSwirlColorGood:
+LDA #$001C
+STA $14
+LDA #$0005
+STA $04
+LDY #$000C
+STY $12
+LDA #$0006
+JML $C2E92F
+
+GetNewSwirlColorBad:
+LDA #$0000
+STA $14
+LDA #$001F
+STA $04
+LDY #$001F
+STY $12
+JML $C2E944
+
+PokeySwirl:
+LDA $4A8C
+CMP #$000E
+BEQ .BossSwirl
+CMP #$01C0
+BCC .NormalSwirl
+.BossSwirl:
+JML $C2E953
+.NormalSwirl:
+JML $C2E964
+;;;;;;;;;;;;
+GetSeedPlayer:
+PHY
+PHB
+LDA #$0010
+LDX #$FD00
+LDY #$9801
+MVN $D77E
+PLB
+PLY
+LDX #$9801
+STX $15
+JML $C0F67F
+
+DrawWorldVersion:
+LDA #$0024
+JSL $C1DD47
+LDA #$FFA0
+STA $0E
+LDA #$00FC
+STA $10
+;FCFFE0 stores the world version
+JSL $C186B1
+
+LDA #$0013
+JSL $C1DD47;draw the file select box
+JML $C1ED6D
+
+Set24BitStartingEXP:
+PHX
+LDA $24
+AND #$00FF
+ASL
+TAX
+LDA ExpPointers,X
+STA $06
+LDA #$00D7
+STA $08
+LDA [$06]
+BEQ .ZeroCheck
+.HasExp:
+TAX
+INC $06
+INC $06
+LDA [$06]
+STA $08
+STX $06
+PLX
+JML $C1FD61
+.ZeroCheck:
+PHA
+INC $06
+INC $06
+LDA [$06]
+BEQ .NoEXP
+PLA
+DEC $06
+DEC $06
+BRA .HasExp
+.NoEXP:
+PLA
+PLX
+DEC $06
+DEC $06
+JML $C1FD72
+
+CheckMoreSpecialCommands:
+CMP #$0018
+BEQ .PrintPlayerMenu
+CMP #$0019
+BEQ .CheckIfIsBanned
+CMP #$001A
+BEQ .SendGiftPacket
+CMP #$001B
+BEQ .PrintGiftInbox
+CMP #$001C
+BEQ .CountGifts
+CMP #$001D
+BEQ .SwaptoKeyStorage
+CMP #$001E
+BEQ .ClearKeyStorage
+CMP #$001F
+BEQ .StoreKeyItem
+CMP #$0020
+BEQ .SetShopFlag
+CMP #$0021
+BEQ .ClearAPName
+JML CheckMoreMoreCommands
+.CheckIfIsBanned:
+JMP .CompareBannedItemList
+.SendGiftPacket:
+JMP .LongSendGiftPacket
+.PrintGiftInbox:
+JMP SwapToGiftInventory
+.CountGifts:
+JMP CountGifts
+.SwaptoKeyStorage:
+JMP SwapKeyStorage
+.StoreKeyItem:
+JMP StoreKeyItem
+.ClearKeyStorage:
+JMP ClearKeyStorage
+.SetShopFlag:
+JMP SetShopFlag
+.ClearAPName:
+JMP ClearAPNameFlag
+
+.PrintPlayerMenu:
+JSL $C3E4D4
+LDA #$0035
+JSL $C1DD47 ;open_window
+
+LDA #$0002
+LDY #$0EB3
+JSL goto_bank_c1
+
+LDA #$0082
+LDY #$0EB3
+JSL goto_bank_c1
+LDA #$0001
+STA $97D0
+.ResetText:
+STZ $B5E9
+LDA $B622
+AND #$FFFB
+STA $B622
+LDA $97D0
+STA $B5E7
+LDA #$00EE
+STA $10
+LDA.w #PrintPlayerNum
+STA $0E
+JSL $C186B1
+
+.PressNothing:
+LDA #$0000
+.CheckServerLoop:
+STA $B58E
+LDA $B58E
+CMP #$0032
+BEQ .ExitServerLoop
+
+LDA $B622
+AND #$00FF
+AND #$0004
+BNE .ExitServerLoop
+LDA $B58E
+INC
+PHA
+JSL $C08756
+PLA
+BRA .CheckServerLoop
+.ExitServerLoop:
+LDA #$00EE
+STA $10
+LDA.w #PlayerNameText
+STA $0E
+JSL $C3E4D4
+JSL $C186B1
+JSL $C3E4CA
+.LoopWin:
+JSL $C12DD5
+
+LDA $0066
+AND #$0008
+BNE .PressedUp
+LDA $0066
+AND #$0004
+BNE .PressedDown
+LDA $0066
+AND #$0001
+BNE .PressedRight
+
+LDA $0066
+AND #$0002
+BNE .PressedLeft
+JMP .SpecialInput
+.PressedUp:
+LDA #$0035
+JSL $C1DD47
+LDA $97D0
+CMP PlayerCount
+BCS .MaxPlayers
+INC
+.MaxPlayers:
+DEC
+CMP PlayerCount
+BCS .OverCap
+INC
+STA $97D0
+.OverCap:
+JMP .ResetText
+.PressedDown:
+LDA #$0035
+JSL $C1DD47
+LDA $97D0
+CMP #$0001
+BEQ .MaxPlayers
+DEC
+BRA .MaxPlayers
+.PressedRight:
+LDA #$0035
+JSL $C1DD47
+LDA $97D0
+CMP PlayerCount
+BCS .MaxPlayers
+CLC
+ADC #$000A
+JMP .MaxPlayers
+.PressedLeft:
+LDA #$0035
+JSL $C1DD47
+LDA $97D0
+CMP #$000B
+BCC .MaxPlayers
+SEC
+SBC #$000A
+BRA .MaxPlayers
+.SpecialInput:
+LDA $006D
+AND #$00A0
+BNE .Confirm
+LDA $006D
+AND #$A000
+BNE .Decline
+JMP .LoopWin
+.Confirm:
+LDA #$0001
+STA $97CC
+JSL $C1DD59
+
+LDA $97D0
+STA $B5E9
+LDA #$0000
+JML $C17F0F
+
+.Decline:
+JSL $C1DD59
+STZ $97CC
+LDA #$0000
+JML $C17F0F
+
+.CompareBannedItemList:
+REP #$31
+PHD
+TDC
+ADC #$FFEE
+TCD
+STZ $B573
+LDY #$03DB
+JSL goto_bank_c1
+LDA $06
+SEP #$20
+LDX #$0000
+.check_banlist:
+CMP BannedItemList,X
+BEQ .BannedItem
+INX
+CPX #$002F
+BCC .check_banlist
+REP #$20
+LDA #$0001
+BRA .exit
+.BannedItem:
+REP #$20
+LDA #$0000
+.exit:
+STA $0E
+STZ $10
+LDY #$045C
+JSL goto_bank_c1
+LDA #$0000
+PLD
+JML $C17F0F
+
+
+.LongSendGiftPacket:
+LDA $31D0
+AND #$00FF
+CMP #$0009
+BCC .GiftBufferNotFull
+LDA $B622
+ORA #$0008
+STA $B622
+
+;Set flag here
+.GiftBufferNotFull:
+INC $31D0
+LDX #$0000
+.CheckBuffer:
+LDA $31D1,X
+BEQ .InsertGiftToBuffer
+INX #3
+BRA .CheckBuffer
+
+.InsertGiftToBuffer:
+SEP #$20
+LDA $97D0
+STA $31D1,X
+REP #$20
+LDA $B5E9
+STA $31D2,X
+LDA #$0000
+JML $C17F0F
+
+TransferGiftToInv:
+PHA
+PHX
+SEP #$20
+LDA $3270;Set this byte if we opened the Receiving menu
+BNE .NoGift
+LDA $3200
+BEQ .NoGift
+LDX #$0000
+.CheckGiftSlots:
+LDA $3201,X
+BEQ .EmptySlot
+INX
+CPX #$0047
+BEQ .NoGift
+BRA .CheckGiftSlots
+.EmptySlot:
+LDA $3200
+STA $3201,X
+STZ $3200
+.NoGift:
+REP #$20
+PLX
+PLA
+JML $809470
+
+SwapToGiftInventory:
+LDA $3272
+BEQ .SetSwapInv
+STZ $3272
+BRA .ClearSwapInv
+.SetSwapInv:
+LDA #$0001
+STA $3272
+.ClearSwapInv:
+LDA #$0000
+JML $C17F0F
+
+CountGifts:
+STZ $97D0
+STZ $97CC
+SEP #$20
+LDA $3201
+BEQ .Exit
+LDA #$01
+STA $97D0
+LDX #$0000
+.CheckGiftCount:
+LDA $3201,X
+BEQ .Exit
+INC $97CC
+INX
+BRA .CheckGiftCount
+
+.Exit:
+REP #$20
+LDA #$0000
+JML $C17F0F
+
+
+GetDynamicWindowTitle:
+LDA $3272
+BEQ .GetStorage
+DEC
+ASL
+TAX
+LDA StorageTitlePointers,X
+STA $0E
+LDA #$00FC
+STA $10
+LDA StorageNumberStart,X
+STA $18
+LDA StorageTitleLength,X
+AND #$00FF
+TAX
+LDA #$9C9F
+JML $C19A6D
+.GetStorage:
+LDA #$00C4
+STA $10
+JML $C19A67
+
+CheckGiftBox1:;
+LDA $3272
+BEQ .Storage
+CMP #$0002
+BEQ .KeyStorage
+LDA $3201,X
+BRA .GiftBox
+.Storage:
+LDA $B590,X
+.GiftBox:
+AND #$00FF
+JML $C19ABF
+.KeyStorage:
+LDA $3280,X
+BRA .GiftBox
+
+
+CheckGiftBox2:
+LDA $3272
+BEQ .Storage
+CMP #$02
+BEQ .KeyStorage
+LDA $3201,X
+BRA .GiftBox
+.Storage:
+LDA $B590,X
+.GiftBox:
+STA $06
+JML $C15B2E
+.KeyStorage:
+LDA $3280,X
+BRA .GiftBox
+
+CheckGiftBox3:
+LDA $3272
+BEQ .Storage
+CMP #$02
+BEQ .KeyStorage
+LDA $3202,X
+BRA .GiftBox
+.Storage:
+LDA $B591,X
+.GiftBox:
+STA $00
+JML $C191D8
+.KeyStorage:
+LDA $3281,X
+BRA .GiftBox
+
+CheckGiftBox4:
+LDA $3272
+BEQ .Storage
+CMP #$02
+BEQ .KeyStorage
+LDA $3201,X
+BRA .Giftbox
+.Storage:
+LDA $B590,X
+.Giftbox
+STA $01
+JML $C191C3
+.KeyStorage:
+LDA $3280,X
+BRA .Giftbox
+
+
+CheckGiftBox5:
+LDA $3272
+BEQ .Storage
+CMP #$02
+BEQ .KeyStorage
+STZ $3201,X
+BRA .Giftbox
+.Storage:
+STZ $B590,X
+.Giftbox:
+REP #$20
+JML $C191F1
+.KeyStorage:
+STZ $3280,X
+BRA .Giftbox
+
+CheckGiftBox6:
+PHA
+LDA $3272
+BEQ .Storage
+CMP #$02
+BEQ .KeyStorage
+PLA
+STA $3201,X
+BRA .GiftBox
+.Storage:
+PLA
+STA $B590,X
+.GiftBox:
+REP #$20
+JML $C191CE
+.KeyStorage:
+PLA
+STA $3280,X
+BRA .GiftBox
+
+CheckIfKeyItem:
+PHX
+LDX #$00
+.Check:
+CMP BanListNoProgs,X
+BEQ .IsKeyItem
+INX
+CPX #$2A
+BEQ .NormalItem
+BRA .Check
+.IsKeyItem:
+PHA
+LDA #$02
+STA $3272
+PLA
+.NormalItem:
+PLX
+RTL
+
+CheckGiftBox7:
+LDA $3272
+BEQ .Storage
+CMP #$02
+BEQ .KeyStorage
+LDA $3201,X
+BRA .GiftBox
+.Storage:
+LDA $B590,X
+.GiftBox:
+STA $06
+JML $C17AA2
+.KeyStorage:
+LDA $3280,X
+BRA .GiftBox
+
+CheckGiftBox8:
+LDA $3272
+BEQ .Storage
+CMP #$0002
+BEQ .KeyStorage
+LDA $3201,X
+BRA .GiftBox
+.Storage:
+LDA $B590,X
+.GiftBox:
+AND #$00FF
+JML $C19121
+.KeyStorage:
+LDA $3280,X
+BRA .GiftBox
+
+CheckGiftBox9:
+PHA
+LDA $3272
+BEQ .Storage
+CMP #$0002
+BEQ .KeyStorage
+PLA
+CLC
+ADC #$3201
+BRA .GiftBox
+.Storage:
+PLA
+CLC
+ADC #$B590
+.GiftBox:
+TAX
+JML $C19156
+.KeyStorage:
+PLA
+CLC
+ADC #$3280
+BRA .GiftBox
+
+SwapKeyStorage:
+LDA $3272
+BNE .ClearStorage
+LDA #$0002
+STA $3272
+.Cleared:
+LDA #$0000
+JML $C17F0F
+.ClearStorage
+STZ $3272
+BRA .Cleared
+
+ClearKeyStorage:
+STZ $3272
+LDA #$0000
+JML $C17F0F
+
+StoreKeyItem:
+LDA $97D0
+SEP #$20
+LDX #$0000
+.CheckBans:
+CMP BannedItemList,X
+BEQ .IsKeyItem
+INX
+CPX #$002F
+BEQ .Done
+BRA .CheckBans
+.IsKeyItem:
+LDA #$02
+STA $3272
+BRA .Done
+.Done:
+REP #$20
+LDA #$0000
+JML $C17F0F
+
+KeyItemBlocker:
+PHX
+LDA $B570
+LDX #$00
+.Check:
+CMP BannedItemList,X
+BEQ .IsKeyItem
+INX
+CPX #$2F
+BEQ .Done
+BRA .Check
+.Done:
+PLX
+LDA $B5D4
+RTL
+.IsKeyItem:
+PLX
+LDA $32C4
+RTL
+
+SetShopFlag:
+STZ $B573
+LDA $0734 ; If I change this to $0734 will it fix my flag bug?
+LDX #$0000
+.CheckShopID:
+CMP #$0007
+BCC .GotShopID
+SEC
+SBC #$0007
+INX
+BRA .CheckShopID
+.GotShopID:
+PHX
+LDX #$0000
+.CheckBit:
+CMP #$0000
+BEQ .GotBit
+DEC
+INX
+BRA .CheckBit
+.GotBit:
+SEP #$20
+LDA ShopFlagBits,X
+PLX
+ORA $B721,X
+STA $B721,X
+REP #$20
+LDA #$0000
+JML $C17F0F
+
+ClearAPNameFlag:
+STZ $B573
+LDA #$0000
+JML $C17F0F
+
+APShopHandler:
+LDA $1E
+STA $0770
+TAX
+LDA #$0000
+.CheckShop:
+CPX #$0000
+BEQ .GotShopNum
+CLC
+ADC #$002A
+DEX
+BRA .CheckShop
+.GotShopNum:
+PHA
+LDX $04
+LDA #$0000
+.CheckSlot:
+CPX #$0000
+BEQ .AddSlotVal
+DEX
+CLC
+ADC #$0006
+BRA .CheckSlot
+.AddSlotVal:
+STA $3274
+PLA
+CLC
+ADC $3274
+;At this point we should know what item we're looking at
+TAX
+LDA $F40000,X
+AND #$00FF
+TAY
+STY $1A
+BNE .ValidShopItem
+JML $C19E93
+.ValidShopItem:
+PHA
+LDA $F40003,X
+AND #$00FF
+STA $3274
+CMP #$0004
+BEQ .GrabOffWorldItemName
+CMP #$0005
+BEQ .GrabOffWorldItemName
+CMP #$0006
+BEQ .GrabOffWorldItemName
+LDA $F40004,X
+STA $0734
+LDA $F40000,X
+.APItemReturn:
+AND #$00FF
+STA $0732
+LDA $F40001,X
+STA $0730
+PLA
+JSL CheckItemBoughtFlag
+CMP #$0000
+BEQ .ItemNotBought
+JSR HandleBoughtItem
+.ItemNotBought:
+JML $C19E06
+.GrabOffWorldItemName:
+PHB
+PHX
+PHY
+LDA $F40004,X
+STA $0734
+;AND #$00FF
+TAX
+LDA #$1190
+..CompareID:
+CPX #$0000
+BEQ ..GotName
+DEX
+CLC
+ADC #$0030
+BRA ..CompareID
+..GotName:
+TAX
+LDA #$007F
+LDY #$FF80
+MVN $F47E
+PLY
+PLX
+PLB
+LDA $F40003,X
+AND #$00FF
+CMP #$0005
+BEQ .Remote
+LDA #$00AD
+JMP .APItemReturn
+.Remote:
+LDA $F40000,X
+AND #$00FF
+JMP .APItemReturn
+
+
+GetAPShopName:
+PHA
+LDA $3274
+LDX $04
+STA $0740,X
+TXA
+ASL
+TAX
+LDA $0734
+STA $0748,X
+LDA $0730
+STA $0756,X
+LDA $3274
+BEQ .NormalItemName
+CMP #$0001
+BEQ .GetTeleportName
+CMP #$0002
+BEQ .GetCharacterName
+CMP #$0003
+BEQ .GetSoldOutName
+CMP #$0004
+BEQ .GetArchipelagoName
+CMP #$0005
+BEQ .GetArchipelagoName
+CMP #$0006
+BEQ .GetPhotoName
+CMP #$0007
+BEQ .GetMoneyName
+
+.NormalItemName:
+PLA
+ADC $06
+STA $06
+STA $0E
+JML $C19E29
+.GetTeleportName:
+PLA
+LDA $0732
+ASL
+TAX
+LDA ShopItemNames_TeleportNames,X
+STA $06
+STA $0E
+LDA #$00F4
+STA $08
+JML $C19E29
+.GetCharacterName:
+PLA
+LDA $0732
+ASL
+TAX
+LDA ShopItemNames_CharNames,X
+STA $06
+STA $0E
+LDA #$00F4
+STA $08
+JML $C19E29
+.GetArchipelagoName:
+PLA
+LDA #$FF80
+STA $06
+STA $0E
+LDA #$007E
+STA $08
+JML $C19E29
+.GetSoldOutName:
+PLA
+LDA #ShopItemNames_SoldOut
+STA $06
+STA $0E
+LDA #$00F4
+STA $08
+JML $C19E29
+.GetPhotoName:
+PLA
+LDA #PhotoShopText
+STA $06
+STA $0E
+LDA #$00F3
+STA $08
+JML $C19E29
+.GetMoneyName:
+PLA
+LDA $0732
+ASL
+TAX
+LDA ShopItemNamesDeluxe_MoneyNames,X
+STA $06
+STA $0E
+LDA #$00F4
+STA $08
+JML $C19E29
+
+
+CheckItemBoughtFlag:
+PHX
+LDA $04
+SEP #$20
+TAX
+LDA ShopFlagBits,X
+REP #$20
+PHA
+LDA $1E
+TAX
+PLA
+AND $B720,X
+PLX
+RTL
+
+HandleBoughtItem:
+LDA $3274
+BEQ .LocalItemChecker
+CMP #$0005
+BEQ .LocalItemChecker
+;This is a teleport/char/nonlocal item. Can never buy this again.
+.BannedItem:
+REP #$20
+LDA #$0003
+STA $3274
+RTS
+.LocalItemChecker:
+LDA $0732
+LDX #$0000
+SEP #$20
+.CheckItem:
+CMP BannedItemList,X
+BEQ .BannedItem
+INX
+CPX #$002F
+BEQ .NotBanned
+BRA .CheckItem
+.NotBanned:
+REP #$20
+STZ $3274
+RTS
+
+GetAPShopPrice:
+LDA $3274
+CMP #$0003
+BEQ .SoldOut
+CMP #$0000
+BEQ .NormalItem
+CMP #$0005
+BEQ .NormalItem
+.DisplaySpecialPrice:
+LDA $0730
+STA $06
+STA $0E
+.DisplayPrice:
+JSL $C4507A
+.SoldOut:
+JML $C19E93
+
+.NormalItem:
+LDA $0732
+JSR CheckBanlist
+BEQ .DisplaySpecialPrice
+BRA .DisplayPrice
+
+CheckBanlist:
+SEP #$20
+LDX #$0000
+.Check:
+CMP BannedItemList,X
+BEQ .Ban
+CPX #$002F
+BEQ .Done
+INX
+BRA .Check
+.Done:
+REP #$20
+LDA #$0001
+RTS
+.Ban:
+REP #$20
+LDA #$0000
+RTS
+
+DisplayAPPlayer:
+LDA $0724
+BEQ .DontCloseNameWin
+LDA $06
+STA $0720
+LDA $08
+STA $0722
+LDA #$000B
+STA $8958
+PHX
+PHY
+JSL $C1DD59 ;Switch to textbox first...
+PLY
+PLX
+STZ $0724
+LDA #$000C
+STA $8958
+LDA $0720
+STA $06
+LDA $0722
+STA $08
+.DontCloseNameWin:
+LDA $0000,Y
+STA $06
+LDA $8958
+CMP #$000C
+BNE .End
+BRA .DontEnd
+.End:
+JMP .End2
+.DontEnd:
+PHX
+LDA $20
+AND #$00FF
+TAX
+.GotSlot:
+LDA $0740,X
+AND #$00FF
+STA $3274
+PHX
+ TXA
+ ASL
+ TAX
+ LDA $0756,X
+ STA $0730
+ LDA $0748,X
+ STA $0734
+PLX
+LDA $3274
+AND #$00FF
+CMP #$0003
+BEQ .SoldOutVar
+STZ $0736
+CMP #$0004
+BCC .DontDisplayPlayerName
+CMP #$0006
+BCS .DontDisplayPlayerName
+LDA #$0001
+STA $B573
+STA $0724
+LDA $06
+STA $0720
+LDA $08
+STA $0722
+LDA #ShopItemNames_PlayerText
+STA $0E
+LDA #$00F4
+STA $10
+PHY
+JSR MoveShopPlayerName
+JSL $C3E4D4 ; Instant print on
+JSL $C186B1 ;Overworld text
+JSL $C3E4CA
+PLY
+STZ $B573
+LDA $0720
+STA $06
+LDA $0722
+STA $08
+;LDA #$000B
+;JSL $C1DD47
+.DontDisplayPlayerName:
+PLX
+JML $C11ACB
+.SoldOutVar:
+LDA #$0001
+STA $0736
+JMP .DontDisplayPlayerName
+.End2:
+JML $C11ACB
+
+MoveShopPlayerName:
+PHB
+PHX
+PHY
+PHA
+TXA
+ASL
+TAX
+LDA $0748,X
+TAX
+PHX
+LDA #$66D0
+.CheckNameSlot:
+CPX #$0000
+BEQ .MoveName
+DEX
+CLC
+ADC #$0011
+BRA .CheckNameSlot
+
+.MoveName:
+TAX
+LDA #$0010
+LDY #$FF50
+MVN $F47E
+PLX
+LDA #$1100
+.CheckSlotName:
+CPX #$0000
+BEQ .MoveItem
+DEX
+CLC
+ADC #$007F
+BRA .CheckSlotName
+.MoveItem:
+TAX
+LDA #$002F
+LDY #$FF80
+MVN $F57E
+PLA
+PLY
+PLX
+PLB
+RTS
+
+TransferOutOfMenu:
+STZ $B573
+JSL $C3E4CA
+;Todo; we need to transfer the item Type and item Price out of here. Also the item Flag. Store these in globals.
+LDA $3274
+STA $97CC
+STZ $0724
+lda $0730
+STA $97D0
+STZ $97D2
+LDA $0734
+STA $97D4
+LDA #$000B
+STA $8958
+PHX
+PHY
+JSL $C1DD59
+PLY
+PLX
+LDA #$0001
+STA $8958
+LDX $1A
+JML $C19EE3
+
+CheckIfBuyable:
+LDA $1A
+BEQ .done
+LDA $0736
+BEQ .done
+LDA $1A
+JSL $EF016F
+LDA #$00FF
+TSB $5E79
+LDA #$0005
+JSL $C0ABE0
+JML $C19EC7
+.done:
+STZ $0736
+JSL $C1DD59
+LDA #$9C8A
+JML $C19ED9
+
+OverrideShopWindowFX:
+PHA
+LDA $3274
+BEQ .NormalItem
+CMP #$0005
+BEQ .NormalItem
+PLA
+LDA #$0001
+JML $C19B6A
+.NormalItem:
+PLA
+JSL $C3EE14
+JML $C19B6A
+
+PreserveWindowPalette:
+PHA
+TDC
+PHA
+LDA #$1DDE
+TCD
+LDA $1A
+BEQ .CancelMenu
+PLA
+TCD
+LDA $0736
+BNE .SoldOut
+.Done:
+PLA
+JSL $C08ED2
+JML $C19DA4
+.SoldOut:
+PLA
+JML $C19DA4
+.CancelMenu:
+PLA
+TCD
+BRA .Done
+
+GetRandomizedTrack:
+%FUNCTION_PROLOGUE(18)
+TAX
+LDA MusicTrackList,X
+AND #$00FF
+JML $C4FBC7
+
+SetMagicantAnimSpeeds:
+LDA $99DC
+CMP #$0003
+BEQ .Paralysis
+LDA $0066
+AND #$0040
+BEQ .Walking
+LDA #$0005
+BRA .SetSpeed
+.Walking:
+LDA #$0008
+.SetSpeed:
+STA $0F42
+RTL
+.Paralysis:
+LDA #$0038
+BRA .SetSpeed
+
+LoadExpandedWindowTable:
+TYA
+CMP #$0035
+BCS .ExpandedWindow
+LDA #$E250
+STA $06
+.Done:
+JML $C105CE
+.ExpandedWindow:
+SEC
+SBC #$0035
+TAY
+LDA #ExtraWindowData
+STA $06
+LDA #$00D7
+STA $08
+JML $C105D3
+
+CheckMoreMoreCommands:
+CMP #$0022
+BEQ CheckIfCanWarp
+CMP #$0023
+BEQ IncTotalPhotos
+CMP #$0024
+BEQ SendRequestForEnergy
+CMP #$0025
+BEQ AuthenticateEnergy
+CMP #$0026
+BEQ GotServerEnergyNum
+CMP #$0027
+BEQ CheckifELOn
+JML CheckMoreMoreMoreCommands
+
+CheckIfCanWarp:
+PHX
+LDX $987B
+LDA $9877
+JSL $C00AA1
+PLX
+AND #$0080
+BEQ .CantWarp
+LDA #$0001
+.CantWarp:
+STA $97CC
+LDA #$0000
+JML $C17F0F
+
+IncTotalPhotos:
+INC $00D7
+LDA #$0000
+JML $C17F0F
+
+SendRequestForEnergy:
+STZ $0792
+STZ $0794
+STZ $079A
+LDA #$0001
+STA $0790
+LDA #$0000
+JML $C17F0F
+
+AuthenticateEnergy:
+JMP AuthenticateEnergyLong
+
+GotServerEnergyNum:
+JMP GotServerEnergyLong
+
+CheckifELOn:
+JMP CheckifELLong
+
+CheckNessRobot:
+SEP #$20
+LDA $B622
+BIT #$40
+REP #$20
+BEQ skip_ness
+JML $C0A98B
+skip_ness:
+LDY #$9F17
+STY $94
+RTL
+
+DeleteExtraKeyItem:
+PHX
+PHY
+STZ $B58B
+LDY #$0001
+.CheckNextCharacter:
+LDX #$0000
+JSR .GetCharacterInventory
+PHY
+LDA $06
+LDY #$0000
+.CheckNextSlot:
+SEP #$20
+CMP $9992,X
+REP #$20
+BEQ .LogKeyItem
+.IgnoreOriginalItem:
+INX
+INY
+CPY #$000E
+BNE .CheckNextSlot
+LDA $B58E
+CMP $98A4
+BEQ .Done
+PLY
+INY
+BRA .CheckNextCharacter
+
+.LogKeyItem:
+PHA
+LDA $B58B
+BNE .TossDuplicateItem
+PLA
+INC $B58B
+JMP .IgnoreOriginalItem
+.Done:
+LDA $06
+LDX #$0000
+.CheckStorage:
+SEP #$20
+CMP $3280,X
+REP #$20
+BEQ .TossDuplicateItemNoPull
+INX
+CPX #$0045
+BNE .CheckStorage
+
+.REALLYdone:
+PLY
+JMP CheckNormalStorage
+.ReallyReallyDoneTheItems:
+JML EndKeyCheck
+.TossDuplicateItem:
+PLA
+PLY
+JML RemoveItem
+
+.TossDuplicateItemNoPull:
+PLY
+JML RemoveItem
+
+.GetCharacterInventory:
+PHY
+LDA $986E,Y ; get the character
+AND #$00FF
+TAY
+TXA
+..Check:
+CPY #$0000
+BEQ ..Done
+CLC
+ADC #$005F
+DEY
+BRA ..Check
+..Done:
+PLY
+TAX
+STY $B58E
+RTS
+
+ReadDynamicPhotoMapPositionY:
+PHX
+LDA $00D4
+AND #$00FF
+ASL
+ASL
+TAX
+LDA $00DF,X
+PLX
+JML $C4F2ED
+
+ReadDynamicPhotoMapPositionX:
+PHX
+LDA $00D4
+AND #$00FF
+ASL
+ASL
+TAX
+LDA $00DD,X
+PLX
+JML $C4F2F6
+
+ReadDynamicPhotoCharPosY:
+PHX
+LDA $00D4
+AND #$00FF
+ASL
+ASL
+TAX
+PHY
+LDY #$6A2C
+LDA #$0040
+PHX
+JSL goto_bank_c2
+PLX
+PLY
+STA $00D5
+LDA $00DF,X
+SEC
+SBC #$0020
+CLC
+ADC $00D5
+PLX
+JML $C4F405
+
+ReadDynamicPhotoCharPosX:
+PHX
+LDA $00D4
+AND #$00FF
+ASL
+ASL
+TAX
+PHY
+LDY #$6A2C
+LDA #$0040
+PHX
+JSL goto_bank_c2
+PLX
+PLY
+STA $00D5
+LDA $00DD,X
+SEC
+SBC #$0020
+CLC
+ADC $00D5
+PLX
+JML $C4F3EC
+
+SpawnDynamicPhotomanX:
+PHX
+PHY
+LDA $00D7
+ASL
+ASL
+TAX
+LDA $9877
+STA $00DD,X
+LDA $00D9
+LDY #$6A2C
+LDA #$0010
+JSL goto_bank_c2
+CLC
+ADC $9877
+PLY
+PLX
+STA $00DB
+JML SavePhotoColor
+
+SpawnDynamicPhotomanY:
+PHX
+LDA $00D7
+ASL
+ASL
+TAX
+LDA $987B
+STA $00DF,X
+PHA
+LDA $B623
+AND #$00FF
+AND #$0008
+BEQ .NormalOffset
+PLA
+CLC
+ADC #$0200
+BRA .Offscreen
+.NormalOffset:
+PLA
+CLC
+ADC #$0050
+.Offscreen:
+PLX
+JML $C46D98
+
+IncrementCurPhotoCount:
+LDX #$0001
+STX $1C
+INC $00D4
+JML $C4F42E
+
+CheckTotalEnergy:
+LDA $08
+STA $0C
+LDA $B623
+AND #$00FF
+AND #$0010
+BEQ .CheckBank
+LDA $0792
+STA $06
+LDA $0794
+STA $08
+JML $C15F48
+.CheckBank:
+JML $C15F3E
+
+DisplayEnergy:
+TAY
+LDA $0000,Y
+PHA
+LDA $B623
+AND #$00FF
+AND #$0010
+BEQ .DontDisp
+PLA
+LDA $0792
+STA $06
+LDA $0794
+STA $08
+JML $C192E9
+.DontDisp:
+PLA
+JML $C192E2
+
+AuthenticateEnergyLong:
+PHX
+LDA $97D0
+STA $0796
+LDA $97D2
+STA $0798
+LDX #$00B4
+.CountFrames:
+LDA $079A
+AND #$00FF
+BNE .WithdrawSuccess
+PHX
+JSL $C08756
+PLX
+DEX
+CPX #$0000
+BNE .CountFrames
+PLX
+LDA #$0000
+STA $97D0
+STA $0796
+STA $0797
+STA $97D2
+.Done:
+LDA #$0000
+JML $C17F0F
+.WithdrawSuccess:
+PLX
+STA $97CC
+LDA #$0000
+BRA .Done
+
+GotServerEnergyLong:
+LDA $0790
+STA $97CC
+LDA #$0000
+JML $C17F0F
+
+CheckEndPhotos:
+PHX
+LDA HasStartingPhoto
+AND #$00FF
+BEQ .NoStartPhoto
+LDA $00D7
+AND #$00FF
+ASL
+ASL
+TAX
+LDA $C1FE9E
+STA $00DD,X
+LDA $C1FE9B
+STA $00DF,X
+LDA $00D7
+ASL
+TAX
+LDA PhotoFlags,X
+LDX #$0001
+JSL $C2165E
+LDA $C0B672
+AND #$00FF
+STA $98CB
+INC $00D7
+.NoStartPhoto:
+LDA HasPooStartPhoto
+AND #$00FF
+BEQ .NoPooPhoto
+LDA $00D7
+AND #$00FF
+ASL
+ASL
+TAX
+LDA $C1FE9E
+STA $00DD,X
+LDA $C1FE9B
+STA $00DF,X
+LDA $00D7
+ASL
+TAX
+LDA PhotoFlags,X
+LDX #$0001
+JSL $C2165E
+LDA $00D7
+CMP #$0001
+BEQ .SpawnPoo2
+LDA #$0004
+STA $98CB
+.SpawnPoo2:
+LDA #$0004
+STA $98D4
+INC $00D7
+
+.NoPooPhoto:
+PLX
+JML $C1FEB6
+
+CheckifELLong:
+LDA $C4FD78
+AND #$00FF
+STA $97CC
+LDA #$0000
+JML $C17F0F
+
+SetFlagOnEquipConfirmSolo:
+db $1C, $20, $01
+db $0A
+dl $C5E1E0
+
+SetFlagOnEquipConfirm:
+db $1C, $20, $01
+db $0A
+dl $C50990
+
+ForcePurchaseCheck:
+db $1D, $19, $01
+db $0A
+dd CancelBuyRemoveName
+
+ForceSetSoloCheck:
+db $1C, $20, $01
+db $0A
+dl $C5E1E0
+
+LoadEnemyToVram:
+LDA $0780
+BEQ .LoadNormal
+LDA $9D11
+AND #$00FF
+BRA .LoadCustom
+.LoadNormal:
+LDA [$0A]
+.LoadCustom:
+LDY #$005E
+PHX
+LDX $AAB4
+SEP #$20
+STA $0782,X
+REP #$20
+PLX
+JML $C2EF42
+
+AllowDynamicCallEnemyLoad:
+CMP #$00FF
+BEQ .cantcall
+JML $C2BDA3
+.cantcall:
+LDA $26
+PHX
+LDX #$0000
+SEP #$20
+.Check:
+CMP $0782,X
+BEQ .Loaded
+CPX #$0003
+BEQ .DontOverride
+INX
+BRA .Check
+.DontOverride:
+REP #$20
+PLX
+JML $C2BDC6
+.Loaded:
+REP #$20
+PLX
+JML $C2BDEC
+
+AllowEnemyToSpawn:
+LDA $0780
+BEQ .DontOverride
+LDA $9D11
+AND #$00FF
+BRA .Done
+.DontOverride:
+LDA [$0A]
+.Done:
+STA $AABE,X
+JML $C2EFA7
+
+ForceLoadSingle:
+LDA [$1A],y
+AND #$00FF
+PHA
+LDA $0780
+BNE .ForceMode
+.Load:
+PLA
+JML $C2EFCB
+
+.ForceMode:
+LDA $B5ED
+BNE .Done
+INC $B5ED
+BRA .Load
+.Done:
+PLA
+JML $C2EFD4
+
+InitializeLoadingEnemies:
+STZ $0780
+STZ $0782
+STZ $0784
+STZ $AAB4
+STZ $AAB2
+JML $C2EEF5
+
+SetupVramForSpriteLoad:
+PHA
+LDA $0780
+BEQ .NormalLoad
+LDA $AAB4
+CMP #$04
+BCS .NormalLoad
+PLA
+JSL $C085B7
+SEP #$20
+LDA #$0F
+STA $00000D
+RTL
+.NormalLoad:
+PLA
+JML $C08616
+
+CloseVersionWindow:
+LDA #$0024
+JSL $C3E521
+JML $C3E4D4
+
+FixGiygasReflectBroken:
+PHA
+LDA $AA96
+BEQ .NotReflection
+LDA #$A26A
+STA $A972
+.NotReflection:
+JML $C23D05
+
+CheckNormalStorage:
+PHY
+LDA $06
+LDX #$0000
+.CheckStorage:
+SEP #$20
+CMP $B590,X
+REP #$20
+BEQ .TossDuplicateItemNoPull
+INX
+CPX #$0045
+BNE .CheckStorage
+PLY
+LDA #$0000
+JML DeleteExtraKeyItem_ReallyReallyDoneTheItems
+.TossDuplicateItemNoPull:
+PLY
+JML RemoveItem
+
+GetStartingData:
+PHB
+LDA StartingInvetory
+TAY
+LDX #$5000
+LDA StartingInvAmounts
+MVN $F77E
+PLB
+LDA #$0000
+LDX #$0000
+CheckTeleport:
+LDA StartingTeleports,X
+AND #$00FF
+BEQ EndStartTeleports
+CMP #$0010
+BNE NormalTeleport
+JMP GetStarstorm
+NormalTeleport:
+PHX
+ASL
+TAX
+LDA TeleportFlags,X
+JSL $C2165E
+PLX
+INX
+BRA CheckTeleport
+EndStartTeleports:
+STZ $FF41
+LDA #$0000
+LDX #$0000
+CheckChars:
+LDA StartingChars,X
+AND #$00FF
+BEQ EndStartData
+PHX
+PHA
+CMP #$0001
+BNE .NotNess
+LDA #$0006
+.NotNess:
+DEC
+DEC
+ASL
+TAX
+LDA CharFlags,X
+LDX #$0001
+JSL $C2165E
+PLA
+JSL $C228F8
+PLX
+INX
+BRA CheckChars
+EndStartData:
+JML CheckEndPhotos
+GetStarstorm:
+PHX
+LDA $FF41
+AND #$00FF
+CMP #$0002
+BNE .StarstormYeah
+DEC $FF41
+.StarstormYeah:
+TAX
+LDA StarstormBits,X
+AND #$00FF
+TSB $9839
+PLX
+INX
+INC $FF41
+JMP CheckTeleport
+
+KeyItemBlockerAndCheckProg:
+LDA $B570
+CMP #$E3
+BCC .NormalItem
+CMP #$E8
+BCS .NormalItem
+LDA $B5D4
+RTL
+.NormalItem:
+JML KeyItemBlocker
+
+CheckMoreMoreMoreCommands:
+CMP #$0028
+BEQ StorePartyDirections
+CMP #$0029
+BEQ LoadPartyDirections
+JML $C17DDC
+
+StorePartyDirections:
+PHB
+PHX
+PHY
+LDX #$2B26
+LDY #$B43E
+LDA #$000A
+MVN $7E7E
+PLY
+PLX
+PLB
+LDA #$0000
+JML $C17F0F
+
+LoadPartyDirections:
+PHY
+PHX
+.Ness:
+JMP LoadPartyMemberDirectionsFixed
+ LDX $B43E
+ JSL $C46363
+.Paula:
+ LDA #$0002
+ LDX $B440
+ JSL $C46363
+.Jeff:
+ LDA #$0003
+ LDX $B442
+ JSL $C46363
+.Poo:
+ LDA #$0004
+ LDX $B444
+ JSL $C46363
+.Temp1:
+LDA $B446
+STA $2B2E
+LDA $B448
+STA $2B30
+ LDA $98A4
+ ASL
+ TAX
+ LDA $9897,X
+ BEQ .Temp2
+ ASL
+ TAX
+ LDA $2AF6,x
+ STZ $2AF6,X
+ TAX
+ PHX
+ LDA $98A4
+ TAX
+ LDA $988B,X
+ AND #$00FF
+ PLX
+ JSL $C46363 ; not working
+.Temp2:
+ LDA $98A4
+ INC
+ ASL
+ TAX
+ LDA $9897,X
+ BEQ .Done
+ ASL
+ TAX
+ LDA $2AF6,x
+ STZ $2AF6,X
+ TAX
+ PHX
+ LDA $98A4
+ INC
+ TAX
+ LDA $988B,X
+ AND #$00FF
+ PLX
+ JSL $C46363 ;not working
+.Done:
+PLY
+PLX
+LDA #$0000
+JML $C17F0F
+
+GiveTeddyOnLoad:
+PHX
+PHY
+LDA #$0000
+LDX #$0000
+LDY #$0000
+SEP #$20
+LDA $98A3
+BNE .End
+LDA $C0B672
+TAX
+REP #$20
+LDA #$99F1
+.CheckChar:
+CPX #$0001
+BEQ .GotChar
+CLC
+ADC #$005F
+DEX
+BRA .CheckChar
+.GotChar:
+TAX
+SEP #$20
+.CheckSlot:
+LDA $0000,X
+BEQ .End
+CMP #$02
+BEQ .GiveTeddy
+CMP #$03
+BEQ .GiveSuper
+.KeepGoing:
+CPY #$000D
+BEQ .End
+INY
+INX
+BRA .CheckSlot
+.End:
+REP #$20
+PLY
+PLX
+JML $C1FEBA
+.GiveTeddy:
+INC $B44A
+PHA
+PHX
+PHY
+REP #$20
+LDA #$0010
+JSL $C228F8
+SEP #$20
+PLY
+PLX
+PLA
+BRA .KeepGoing
+.GiveSuper:
+REP #$20
+LDA $B44A
+BNE .ReplaceBear
+.AddSuper:
+LDA #$0011
+JSL $C228F8
+BRA .End
+.ReplaceBear:
+LDA #$0010
+INC $98A3
+JSL $C229BB
+DEC $98A3
+BRA .AddSuper
+
+FixGiygasReflect:
+PHA
+LDA $AA96
+BEQ .NotReflection
+LDA #$A26A
+STA $A972
+.NotReflection:
+PLA
+JML $C23D05
+
+HandleUnusableWarpPad:
+PHX
+LDA $1D
+TAX
+LDA $1F
+AND #$00FF
+JSL $C3E977
+PLX
+CMP #$00B5
+BNE .NormalItem
+LDA #$0000
+BRA .UseItem
+.NormalItem:
+LDA $06
+.UseItem:
+DEC
+LDY #$005F
+JML $C13666
+
+LoadCurrentPhotoColor:
+PHX
+LDX #$0000
+.Check:
+CMP PhotocolorTable,X
+BEQ .GotPhotoColor
+CPX #$0156
+BEQ .NotInTable
+INX
+INX
+BRA .Check
+.GotPhotoColor:
+PHA
+LDA GoodPhotoColors,X
+.ForceLoad0:
+AND #$00FF
+SEP #$20
+STA $B44C
+REP #$20
+PLA
+PLX
+JML $C06987
+.NotInTable:
+PHA
+LDA #$0010
+BRA .ForceLoad0
+
+SavePhotoColor:
+PHA
+PHX
+LDA $00D7
+TAX
+SEP #$20
+LDA $B44C
+STA $0160,X
+REP #$20
+PLX
+PLA
+JML $C46D85
+
+LoadPhotoColor:
+PHX
+LDA $00D4
+AND #$00FF
+TAX
+LDA $0160,X
+AND #$00FF
+TAX
+LDA #$0000
+.Check:
+CPX #$0000
+BEQ .Done
+CLC
+ADC #$00C0
+DEX
+BRA .Check
+.Done:
+PLX
+JML $C0089E
+
+RenderCrashScreen:
+STZ $002B
+.CheckForFrame:
+ LDA $002B
+ BEQ .CheckForFrame
+ STZ $002B
+ STZ $4100
+ JSL $C08726
+ REP #$31
+ LDA #$1EE0
+ TCD
+ LDA #$00F8
+ STA $10
+ LDA #$9000
+ STA $0E
+ LDA #$0000
+ LDX #$01B0
+ LDY #$6100
+ JSL $C08616
+ LDA #$0000
+ LDX #$7C00
+ LDY #$6000
+ JSL $C08E1C
+ LDA #$06FF
+ LDX #$9700
+ LDY #$7DFE
+ MVN $F87E
+
+
+ LDA #$0460
+ LDX #$9200
+ LDY #$7F16
+ MVN $F87E
+JSL $C2038B
+LDA #$0004
+STA $001A
+ LDA #$000F
+ LDX #$0002
+ JSL $C0886C
+.Loop:
+JSL $C08756
+LDA $006A
+AND #$0010
+BNE .Reset
+BRA .Loop
+.Reset:
+JML ReallyReset
+
+LeaveMoneyInMemory:
+LDA $B5EF
+BNE .KeepInMem
+JML $C22214
+.KeepInMem:
+STZ $B5EF
+LDA $06
+STA $97D0
+JML $C22214
+
+
+CheckExtraAddCommands:
+CMP #$0001
+BNE .NotOne
+JML $C18012
+.NotOne:
+CMP #$0025
+BEQ .SaveMoney
+JML $C17F29
+.SaveMoney:
+INC $B5EF
+JML $C1803C
+
+
+GetRemoteMoney:
+LDA $B5F1
+BEQ .Done
+PHA
+LDA #$0074
+JSL $C0ABE0
+PLA
+ASL
+TAX
+LDA MoneyAmounts,X
+STA $0E
+LDA #$0000
+STA $10
+JSL $C22214
+STZ $B5F1
+.Done:
+LDA $006D
+AND #$A000
+JML $C0B8F4
+
+ClearStatusVars:
+LDA #$766E
+STA $0E
+STZ $B587
+STZ $B588
+JML $C2A044
+
+LoadPartyMemberDirectionsFixed:
+ phy
+ phx
+ LDA #$000B
+ LDY #$2B26
+ LDX #$B43E
+ MVN $7E7E
+ ldy #$0000
+ bra .loopcond
+.loop:
+ phy
+ ; Update entity sprite frame (the thing that the "set direction" function at C46363 does after storing to the table of directions)
+ jsl $C0A780
+ ply
+ iny
+ iny
+.loopcond:
+ ; Have we gotten through all 6 possible party members?
+ cpy #$000C
+ bcs .loopend
+ ; If not, have we gotten through all valid party members? (according to the entity index list)
+ lda $9897,y
+ and #$00FF
+ bne .loop
+.loopend:
+ ply
+ plx
+ lda #$0000
+ jml $C17F0F
+
+
+
+;new code go here
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;
+;ANYTHING BETWEEN THIS BREAK AND THE NEXT NEEDS TO GET COMMENTED OUT!
+ORG $C19DE5
+;JML APShopHandler;This JML is only in AP patch
+
+ORG $C19E23
+;JML GetAPShopName
+
+ORG $C19E8F
+;JML GetAPShopPrice
+
+ORG $C11AC6
+;JML DisplayAPPlayer
+
+ORG $C19EDD
+;JML TransferOutOfMenu
+
+org $C19ED3
+;JML CheckIfBuyable
+;NOP
+;NOP
+
+ORG $C19B66
+;JML OverrideShopWindowFX
+
+ORG $C5E0A9
+;db $08
+;dd CheckShopsanityPrice
+
+ORG $C19DA0
+;JML PreserveWindowPalette
+
+ORG $C5E0B6
+;db $08
+;dd BoughtShopsanityItemScript
+
+ORG $C5E0CE
+;db $0A
+;dl ShopsanityPurchaseHandler
+
+ORG $C5E0C8
+;dl ShopsanityPurchaseHandler
+
+ORG $C5DF1E
+;db $0A
+;dl OverrideSpaceCheckOnSpecialItem
+
+ORG $C5E029
+;db $0A
+;dl OverrideSpaceCheckOnSpecialItem_nosell
+
+ORG $C5E1AE
+;dd ForcePurchaseCheck
+
+;ALWAYS HAVE THESE
+ORG $C50A6A
+db $0A
+dl BackupShopEquipText
+
+ORG $C50B4C
+db $0A
+dl BackupShopSellText
+
+ORG $C50C2E
+db $0A
+dl BackupShopCantEquip
+;
+
+ORG $C5E04C
+;db $0A
+;dl OverrideSpaceCheckOnSpecialItem_oneslot
+
+ORG $C5E1A5
+;dd SetFlagOnEquipConfirmSolo
+
+ORG $C5E119
+;dd SetFlagOnEquipConfirm
+
+ORG $C5E0F2
+;dd ForceSetSoloCheck
+
+
+
+
+ORG $F403F0
+;Item ID, 2-byte price, Item Type, 2-bytelocation ID/flag number
+;db $05, $00, $00, $02, $00, $00; Non-remote local item. Franklin Badge.
+;db $5A, $00, $00, $01, $01, $00; Non-remote local Teleport.
+;db $96, $00, $00, $05, $02, $00; A remote regular item
+;db $01, $ff, $ff, $02, $03, $00; Non-remote local Character
+;db $ad, $ff, $ff, $04, $04, $00; Item that the player already bought and got the flag for
+;db $ad, $ff, $ff, $04, $05, $00; Item for another player
+;db $01, $ff, $ff, $05, $06, $00; A remote Key Item
+
+ORG $F400D2
+;db $11
+;Type 0- Normal local item
+;Type 1- Teleport/PSI
+;type 2- Character
+;type 3- item that's already been bought- SOLD OUT
+;type 4; Item for another player
+;type 5- Remote local item
+;type 6- Photograph
+
+;ORG $F41280
+;db $73, $9F, $9F, $9C, $50, $79, $A4, $95, $9D, $00
+
+;ORG $F411F0
+;db $82, $a5, $a3, $a4, $50, $80, $a2, $9f, $9d, $9f, $a4, $95, $a2, $00
+
+;ORG $F412B0
+;db $76, $A2, $91, $9E, $9B, $9C, $99, $9E, $50, $72, $91, $94, $97, $95, $00
+
+;ORG $F45022
+;db $80, $9C, $91, $A9, $95, $A2, $50, $61, $00
+
+;ORG $F45055
+;db $80, $9C, $91, $A9, $95, $A2, $50, $62, $00
+;Player names should be 16 bytes, followed by a zero
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $F49000
+CheckShopsanityPrice:
+db $01, $01
+db $1B, $06
+db $1B, $02
+dd .NormalItem
+.SpecialPrice:
+db $0B, $05
+db $1B, $03
+dd .RemoteItem
+.CheckRemotePrice:
+db $08
+dd $C5E254
+db $1B, $03
+dd .CantAffordSpecial
+db $1B, $01
+db $1B, $04
+db $1B, $00
+db $1d, $19, $01
+db $02
+
+.NormalItem:
+;Write a code here that checks if the item is in the banlist
+db $1B, $01
+db $1B, $04
+db $1C, $19, $01
+db $1B, $03
+dd .RegularPrice
+db $1B, $06
+db $0A
+dl .SpecialPrice
+.RegularPrice:
+db $1B, $01
+db $0A
+dl $C5E24B
+.CantAffordSpecial:
+db $02
+.RemoteItem:
+db $1B, $01
+db $1B, $04
+db $1C, $19, $01
+db $1B, $02
+dd .SpecialPriceRemote
+db $0A
+dl .RegularPrice
+.SpecialPriceRemote:
+db $1B, $06
+db $0A
+dl .CheckRemotePrice
+
+
+BoughtShopsanityItemScript:
+db $1B, $00
+db $1B, $06
+db $1B, $02
+dd .NormalItem
+db $0b, $06
+db $1B, $03
+dd .Photo
+db $70
+db $0A
+dl .CheckSaturnText
+.NonSaturnTetx:
+db $83, $9f, $5c, $10, $02, $50, $a9, $9f, $a5, $50, $a7, $91, $9e, $a4, $50
+.BackSaturnText:
+db $1B, $06
+db $0B, $07
+db $1B, $03
+dd .Money
+
+db $1B, $06
+db $09, $05
+dd .Teleport
+dd .Character
+dd .Error
+dd .OffWorld
+dd .RemoteLocal
+db $02
+.NormalItem:
+db $1B, $01
+db $0A
+dl $C50660
+
+.Teleport:
+ db $1B, $01
+ db $1B, $04
+ db $0B, $10
+ db $1B, $03
+ dd ..PooPsi
+ db $1B, $01
+ db $06
+ db $F8, $00
+ dd .TeleportActiveSaturn
+ db $06
+ db $F9, $00
+ dd .TeleportActiveSaturn
+ db $06
+ db $FA, $00
+ dd .TeleportActiveSaturn
+ db $a4, $98, $95, $50, $91, $92, $99, $9c, $99, $a4, $a9, $50, $a4, $9f, $50, $a4
+ db $95, $9c, $95, $a0, $9f, $a2, $a4, $50, $a4, $9f, $50
+ ..ContinueTeleport:
+ db $08
+ dd ..TeleportShopNameTable
+ db $6F
+ db $02
+
+ ..PooPsi:
+ db $06
+ db $F8, $00
+ dd .PooPsiSaturn
+ db $06
+ db $F9, $00
+ dd .PooPsiSaturn
+ db $06
+ db $FA, $00
+ dd .PooPsiSaturn
+ db $a4, $9f, $50, $9c, $95, $91, $a2, $9e, $50, $91, $50, $9e, $95, $a7, $50, $80
+ db $83, $79, $50, $91, $92, $99, $9c, $99, $a4, $a9, $6f, $02
+
+ ..TeleportShopNameTable:
+ db $1B, $04
+ db $09, $0F
+ dd .ShopOnett
+ dd .ShopTwoson
+ dd .ShopHHV
+ dd .ShopThreed
+ dd .ShopSaturn
+ dd .ShopDes
+ dd .ShopFours
+ dd .ShopWinter
+ dd .ShopSummer
+ dd .ShopScar
+ dd .ShopDalam
+ dd .ShopDark
+ dd .ShopTen
+ dd .ShopUnder
+ dd .ShopMagic
+ db $02
+
+.Character:
+ db $1B, $01
+ db $1B, $04
+ db $06
+ db $F8, $00
+ dd .CharacterSaturn
+ db $06
+ db $F9, $00
+ dd .CharacterSaturn
+ db $06
+ db $FA, $00
+ dd .CharacterSaturn
+ db $a4, $9f, $50, $98, $99, $a2, $95, $50, $9d, $a9, $50, $91, $a0, $a0, $a2, $95
+ db $9e, $a4, $99, $93, $95, $5c, $50
+ ..ContinueCharacter
+ db $08
+ dd ..CharShopNameTable
+ db $6F
+ db $02
+
+ ..CharShopNameTable:
+ db $09, $05
+ dd .Paula
+ dd .Jeff
+ dd .Poo
+ dd .FlnMn
+ dd .Ness
+ db $02
+
+.Error:
+db $07, $a5, $98, $50, $9f, $98, $51, $50, $79, $96, $50, $a9, $9f, $a5, $57, $a2
+db $95, $50, $a3, $95, $95, $99, $9e, $97, $50, $a4, $98, $99, $a3, $50, $a4, $95
+db $a8, $a4, $5c, $03, $00, $70, $50, $99, $a4, $50, $9d, $95, $91, $9e, $a3, $50
+db $a3, $9f, $9d, $95, $92, $9f, $94, $a9, $50, $a3, $93, $a2, $95, $a7, $95, $94
+db $50, $a5, $a0, $50, $a4, $98, $99, $a3, $50, $a3, $93, $a2, $99, $a0, $a4, $51
+db $50, $80, $9c, $95, $91, $a3, $95, $50, $a2, $95, $a0, $9f, $a2, $a4, $50, $a4
+db $98, $99, $a3, $50, $91, $a3, $50, $91, $50, $92, $a5, $97, $51, $51, $02
+
+.OffWorld:
+db $1B, $01
+ db $06
+ db $F8, $00
+ dd .OffworldSaturn
+ db $06
+ db $F9, $00
+ dd .OffworldSaturn
+ db $06
+ db $FA, $00
+ dd .OffworldSaturn
+db $A4, $98, $95, $50
+db $1C, $05, $00, $50
+db $96, $9F, $A2, $50
+db $1C, $02, $00
+db $6F, $02
+
+.RemoteLocal:
+db $1B, $01
+ db $06
+ db $F8, $00
+ dd .RemoteSaturn
+ db $06
+ db $F9, $00
+ dd .RemoteSaturn
+ db $06
+ db $FA, $00
+ dd .RemoteSaturn
+db $A4, $98, $95, $50
+db $1C, $05, $AD, $50
+db $96, $9F, $A2, $50
+db $1C, $02, $00
+db $6F, $02
+
+.Photo:
+db $02
+
+.Money:
+db $1B, $01
+db $1B, $04
+db $09, $03
+dd ..10
+dd ..100
+dd ..1000
+db $02
+
+..10:
+db $54, $61, $60, $6F, $02
+..100:
+db $54, $61, $60, $60, $6F, $02
+..1000:
+db $54, $61, $60, $60, $60, $6F, $02
+
+
+.TeleportActiveSaturn:
+db $97, $9F, $50, $A4, $9F, $50
+db $0A
+dl .Teleport_ContinueTeleport
+
+.PooPsiSaturn:
+db $9E, $95, $A7, $50, $80, $83, $79, $6F, $02
+
+.CharacterSaturn:
+db $92, $A5, $A9, $50
+db $0A
+dl .Character_ContinueCharacter
+
+.OffworldSaturn:
+db $1C, $05, $00, $50
+db $96, $9F, $A2, $50
+db $1C, $02, $00
+db $6F, $02
+
+.RemoteSaturn:
+db $1C, $05, $AD, $50
+db $96, $9F, $A2, $50
+db $1C, $02, $00
+db $6F, $02
+
+.ShopOnett:
+db $7F, $9E, $95, $A4, $A4, $02
+.ShopTwoson:
+db $84, $A7, $9F, $A3, $9F, $9E, $02
+.ShopHHV:
+db $78, $91, $a0, $a0, $a9, $5d, $78, $91, $a0, $a0, $a9, $50, $86, $99, $9c, $9c
+db $91, $97, $95, $02
+.ShopThreed:
+db $84, $98, $A2, $95, $95, $94, $02
+.ShopSaturn:
+db $83, $91, $A4, $A5, $A2, $9E, $50, $86, $91, $9C, $9C, $95, $A9, $02
+db $91, $97, $95, $02
+.ShopDes:
+db $a4, $98, $95, $50, $74, $a5, $a3, $a4, $a9, $50, $74, $a5, $9e, $95, $a3, $50
+db $74, $95, $a3, $95, $a2, $a4, $02
+.ShopFours:
+db $76, $9F, $A5, $A2, $A3, $99, $94, $95, $02
+.ShopWinter:
+db $87, $99, $9E, $A4, $95, $A2, $A3, $02
+.ShopSummer:
+db $83, $A5, $9D, $9D, $95, $A2, $A3, $02
+.ShopScar:
+db $83, $93, $91, $A2, $91, $92, $91, $02
+.ShopDalam:
+db $74, $91, $9C, $91, $91, $9D, $02
+.ShopDark:
+db $a4, $98, $95, $50, $74, $95, $95, $a0, $50, $74, $91, $a2, $9b, $9e, $95, $a3
+db $a3, $02
+.ShopTen:
+db $a4, $98, $95, $50, $84, $95, $9e, $94, $91, $50, $86, $99, $9c, $9c, $91, $97
+db $95, $02
+.ShopUnder:
+db $a4, $98, $95, $50, $7c, $9f, $a3, $a4, $50, $85, $9e, $94, $95, $a2, $a7, $9f
+db $a2, $9c, $94, $02
+.ShopMagic:
+db $7D, $91, $97, $99, $93, $91, $9E, $A4, $02
+
+.Paula:
+db $1C, $02, $02, $02
+.Jeff:
+db $1C, $02, $03, $02
+.Poo:
+db $1C, $02, $04, $02
+.FlnMn:
+db $76, $9C, $A9, $99, $9E, $97, $50, $7D, $91, $9E, $02
+.Ness:
+db $1C, $02, $01, $02
+
+.CheckSaturnText:
+db $06
+db $F8, $00
+dd .SaturnText
+db $06
+db $F9, $00
+dd .SaturnText
+db $06
+db $FA, $00
+dd .SaturnText
+db $0A
+dl .NonSaturnTetx
+.SaturnText:
+db $1F, $31
+db $89, $9F, $A5, $50, $A7, $91, $9E
+db $A4, $50
+db $0A
+dl .BackSaturnText
+
+ShopsanityPurchaseHandler:
+;Set the flag here, probably?
+db $1B, $06
+db $0B, $06
+db $1B, $03
+dd .Photo
+db $1B, $06
+db $1B, $02
+dd .NormalItem
+db $01
+db $1F, $30
+db $06
+db $93, $02
+dd .SpecialMoonsideFlagHandler
+db $19, $02
+db $89, $95, $A3, $02
+db $19, $02
+db $7E, $9F, $02
+db $1C, $07, $02, $11
+db $12
+db $09, $02
+dd .Purchase
+dd .Cancel
+.Cancel:
+db $0A
+dl $C5E1AD
+.Purchase:
+db $1B, $06
+db $0B, $05
+db $1B, $03
+dd .PurchaseRemoteItem
+db $1B, $06
+.ConfirmRemotePurchase:
+db $1D, $09
+dw $0000
+db $18, $0A
+db $08
+dd $C5D835
+db $1B, $06
+db $08
+dd .BoughtSpecialItemGiveToPlayer
+;Set flag here!!!!
+db $0A
+dl $C50198
+db $02
+.NormalItem:
+db $1B, $01
+db $1C, $19, $01
+db $1B, $03
+dd .NotSpecial
+db $19, $20
+db $0B, $01
+db $1B, $03
+dd .GiveLeader
+db $1B, $01
+db $08
+dd $C507F8
+db $08
+dd $C5E52E
+db $1B, $02
+dd $C5E1AD
+
+db $1B, $04
+db $1D, $03, $00
+db $1B, $02
+dd $C5E1B7
+db $1C, $20, $01
+.LeaderGiven:
+db $09, $04
+dd .GiveNess
+dd .GivePaula
+dd .GiveJeff
+dd .GivePoo
+.GiveNess:
+db $1B, $01
+db $1D, $0E, $01, $00
+db $0A
+dl .FinishGive
+.GivePaula:
+db $1B, $01
+db $1D, $0E, $02, $00
+db $0A
+dl .FinishGive
+.GiveJeff:
+db $1B, $01
+db $1D, $0E, $03, $00
+db $0A
+dl .FinishGive
+.GivePoo:
+db $1B, $01
+db $1D, $0E, $04, $00
+db $0A
+dl .FinishGive
+.FinishGive:
+db $04, $91, $02
+db $1B, $06
+db $1D, $09
+dw $0000
+db $18, $0A
+db $08
+dd $C5D835
+db $08
+dd $C51568
+db $02
+.NotSpecial:
+db $19, $20
+db $0B, $01
+db $1B, $03
+dd .HandleSoloNonSpecialBuy
+
+db $1B, $01
+db $08
+dd $C507F8
+db $08
+dd $C5E0D3
+db $1B, $02
+dd .skipflag
+db $1C, $20, $01
+.skipflag:
+db $02
+.GiveLeader:
+db $1C, $20, $01
+db $19, $10, $01
+db $0A
+dl .LeaderGiven
+.HandleSoloNonSpecialBuy:
+db $1D, $03, $FF
+db $1B, $02
+dd $C5DF89
+;db $1C, $20, $01
+db $0A
+dl $C5E0DE
+.BoughtSpecialItemGiveToPlayer:
+db $1C, $20, $01
+db $04, $91, $02
+db $09, $07
+dd ..Teleport
+dd ..Character
+dd ..Error
+dd ..OffWorld
+dd ..RemoteLocal
+dd ..Error
+dd ..Money
+
+
+
+..Error:
+..OffWorld:
+..RemoteLocal:
+db $02
+
+..Teleport:
+db $1B, $01
+db $1B, $04
+db $09, $10
+dd OnettTeleTex+$1
+dd TwosonTeleTex+$1
+dd HappyTeleTex+$1
+dd ThreedTeleTex+$1
+dd SaturnTeleTex+$1
+dd DunesTeleTex+$1
+dd FoursTeleTex+$1
+dd WintersTeleTex+$1
+dd SummersTeleTex+$1
+dd ScarabaTeleTex+$1
+dd DalaamTeleTex+$1
+dd DarkTeleTex+$1
+dd TendaTeleTex+$1
+dd UnderworldTeleTex+$1
+dd MagicantTeleTex+$1
+dd PooPsiTex+$1
+db $02
+
+..Money:
+db $10, $10
+db $1B, $01
+db $1B, $04
+db $09, $03
+dd ...10
+dd ...100
+dd ...1000
+db $02
+...10:
+db $1D, $25, $0A, $00
+db $08
+dd DisplayAndGetMoney
+db $02
+
+...100:
+db $1D, $25, $64, $00
+db $08
+dd DisplayAndGetMoney
+db $02
+
+...1000:
+db $1D, $25, $E8, $03
+db $08
+dd DisplayAndGetMoney
+db $02
+
+..Character:
+db $1B, $01
+db $1B, $04
+db $09, $05
+dd .Paula
+dd .Jeff
+dd .Poo
+dd .FlnMn
+dd .Ness
+db $02
+.Paula:
+db $08
+dd $D5F830
+db $18, $01, $01
+db $02
+.Jeff:
+db $08
+dd $D5F837
+db $18, $01, $01
+db $02
+.Poo:
+db $08
+dd $D5F83E
+db $18, $01, $01
+db $02
+.FlnMn:
+db $08
+dd $D5F845
+db $18, $01, $01
+db $02
+.Ness:
+db $08
+dd $D5F849
+db $18, $01, $01
+db $02
+
+.PurchaseRemoteItem:
+db $1B, $01
+db $1c, $19, $01
+db $1B, $02
+dd .RemoteSpecialPurchase
+
+db $1B, $01
+db $1D, $0A, $00
+db $1B, $04
+db $0A
+dl .ConfirmRemotePurchase
+.RemoteSpecialPurchase:
+db $1B, $06
+db $0A
+dl .ConfirmRemotePurchase
+.Photo:
+db $1C, $28, $01
+db $1B, $06
+db $04, $91, $02
+db $1C, $20, $01
+db $08
+dd DynamicPhotoSetter
+db $1C, $29, $01
+db $18, $01, $01
+db $02
+
+.SpecialMoonsideFlagHandler:
+db $19, $02
+db $89, $95, $A3, $02
+db $19, $02
+db $7E, $9F, $02
+db $1C, $07, $02, $11
+db $12
+db $09, $02
+dd .Cancel
+dd .Purchase
+db $0A
+dl .Purchase
+
+
+OverrideSpaceCheckOnSpecialItem:
+db $1B, $06
+db $1B, $02
+dd .CheckSpace
+db $0A
+dl $C5DF27
+.CheckSpace:
+db $1d, $03, $ff
+db $1b, $02
+dd $C5Df89
+db $0A
+dl $C5DF27
+
+CancelBuyRemoveName:
+db $1C, $21, $01
+db $0A
+dl $C50C36
+
+OverrideSpaceCheckOnSpecialItem_nosell:
+db $1B, $06
+db $1B, $02
+dd .CheckSpace
+db $0A
+dl $C5E05C
+.CheckSpace:
+db $1d, $03, $ff
+db $1b, $02
+dd $C50F66
+db $0A
+dl $C5DF27
+
+BackupShopEquipText:
+db $0A
+dl $C5B910
+
+BackupShopSellText:
+db $0A
+dl $C52189
+
+BackupShopCantEquip:
+db $0A
+dl $C5B94D
+
+OverrideSpaceCheckOnSpecialItem_oneslot:
+db $1B, $06
+db $1B, $02
+dd .CheckSpace
+db $0A
+dl $C5E05C
+.CheckSpace:
+db $1d, $03, $ff
+db $1b, $02
+dd $C5E0A3
+db $0A
+dl $C5E05C
+
+
+;ORG $f40930
+;db $01, $00, $01, $01, $00, $00, $01, $00, $01
+
+
+
+;set flags
+;give the item
+
+
+
+;First we should probably check if it's a special item?
+;1E is the shop ID
+;04 is thre slot number
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;BATTLE ACTION STUFF WEE-WOO WEE-WOO
+
+macro LoadAddress06(address)
+ LDA #$AFB0
+ STA $06
+ lda #$00FF
+ sta $08
+endmacro
+
+macro LoadAddress0A(address)
+ LDA #$AFB0
+ STA $0A
+ lda #$00FF
+ sta $0C
+endmacro
+
+macro LoadAddress18(address)
+ LDA #$AFB0
+ STA $18
+ lda #$00FF
+ sta $1A
+endmacro
+
+ORG $FFAFB0
+
+ORG $C11FF1
+LDA $FFAFB0,X
+
+ORG $C1B6E4
+LDA $FFAFB0,X
+
+ORG $C1B80E
+LDA $FFAFB0,X
+
+ORG $C1C9E7
+LDA $FFAFB0,X
+
+ORG $C1CD55
+LDA $FFAFB0,X
+
+ORG $C1CDCF
+LDA $FFAFB0,X
+
+ORG $C244D4
+LDA $FFAFB0,X
+
+ORG $C256EE
+LDA $FFAFB0,X
+
+ORG $C257E4
+LDA $FFAFB0,X
+
+ORG $C25A12
+LDA $FFAFB0,X
+
+ORG $C25A8A
+LDA $FFAFB0,X
+
+ORG $C25B4E
+LDA $FFAFB0,X
+
+ORG $C25B9D
+LDA $FFAFB0,X
+
+ORG $C2699F
+LDA $FFAFB0,X
+
+ORG $C29447
+LDA $FFAFB0,X
+
+ORG $C1ADC8
+%LoadAddress06(BattleActions)
+
+ORG $C1AFFD
+%LoadAddress0A(BattleActions)
+
+ORG $C1B04F
+%LoadAddress0A(BattleActions)
+
+ORG $C1B0CB
+%LoadAddress0A(BattleActions)
+
+ORG $C1B138
+%LoadAddress0A(BattleActions)
+
+ORG $C1B1A7
+%LoadAddress0A(BattleActions)
+
+ORG $C1B259
+%LoadAddress0A(BattleActions)
+
+ORG $C1B371
+%LoadAddress18(BattleActions)
+
+ORG $C1B466
+%LoadAddress0A(BattleActions)
+
+ORG $C1B89C
+%LoadAddress0A(BattleActions)
+
+ORG $C1B8E7
+%LoadAddress06(BattleActions)
+
+ORG $C1B9AD
+%LoadAddress0A(BattleActions)
+
+ORG $C1C913
+%LoadAddress0A(BattleActions)
+
+ORG $C1CC9B
+%LoadAddress06(BattleActions)
+
+ORG $C1DF64
+%LoadAddress06(BattleActions)
+
+ORG $C1DFB8
+%LoadAddress06(BattleActions)
+
+ORG $C2451F
+%LoadAddress06(BattleActions)
+
+ORG $C2582E
+%LoadAddress06(BattleActions)
+
+ORG $C25C32
+%LoadAddress0A(BattleActions)
+
+ORG $C25CEB
+%LoadAddress0A(BattleActions)
+
+ORG $C2794D
+%LoadAddress0A(BattleActions)
+
+ORG $FFBEA4
+;START OF NEW BATTLE ACTIONS
+db $00, $01, $05, $00, $30, $9c, $ef, $00, $6D, $8b, $c2, $00;cold spores
+
+db $00, $01, $05, $00
+dd regular_eyes
+dd $00C28CF1;Glared with Regular Eyes
+
+db $00, $01, $05, $00
+dd creepy_eyes
+dd $00C29FFE;Glared with Creepy eyes
+
+db $00, $04, $05, $00
+dd gentle_light
+dd $00C29987;Emit gentle light
+
+db $00, $04, $05, $00
+dd harsh_light
+dd $00C299AE;Emit harsh light
+
+db $00, $04, $05, $00
+dd radiant_light
+dd $00C29A35;Emit radiant light
+
+db $00, $01, $05, $00
+dd fireball_gamma
+dd flaming_fireball_gamma
+
+db $00, $01, $05, $00
+dd fireball_beta
+dd flaming_fireball_beta
+
+db $00, $01, $05, $00
+dd fireball_alpha
+dd flaming_fireball_alpha
+
+db $00, $04, $05, $00
+dd breathe_fire_alpha
+dd $00C295AB;
+
+db $00, $04, $05, $00
+dd breathe_fire_beta
+dd $00C295B4;
+
+db $00, $04, $05, $00
+dd breathe_fire_omega
+dd $00C295C6;
+
+db $00, $04, $05, $00
+dd spray_fire_alpha
+dd $00C295AB;
+
+db $00, $04, $05, $00
+dd spray_fire_beta
+dd $00C295B4;
+
+db $00, $04, $05, $00
+dd spray_fire_gamma
+dd $00C295BD;
+
+db $00, $01, $02, $00
+dd dirty_fangs
+dd dirty_fang_asm;
+
+db $00, $01, $05, $00
+dd dirty_stinger
+dd $00C28B6D;
+
+db $00, $04, $05, $00
+dd fizzling_crack_zap
+dd $00C29871;
+
+db $00, $04, $05, $00
+dd thunder_boom_bang
+dd $00C29889;
+
+db $00, $04, $05, $00
+dd shocking_blast_burst
+dd $00C29895;
+
+db $00, $04, $05, $00
+dd static_shock
+dd $00C29871;
+
+db $00, $04, $05, $00
+dd charged_shock
+dd $00C29889;
+
+db $00, $04, $05, $00
+dd high_voltage_shock
+dd $00C29895;
+
+db $00, $04, $05, $00
+dd espresso_beta
+dd $00C295B4;
+
+db $00, $04, $05, $00
+dd espresso_gamma
+dd $00C295BD;
+
+db $00, $04, $05, $00
+dd espresso_omega
+dd $00C295C6;
+
+db $00, $04, $05, $00
+dd extinguish_alpha
+dd $00C295AB;
+
+db $00, $04, $05, $00
+dd extinguish_beta
+dd $00C295B4;
+
+db $00, $04, $05, $00
+dd extinguish_omega
+dd $00C295C6;
+
+db $00, $01, $02, $00
+dd solid_byte
+dd $00C2A5EC;
+
+db $00, $01, $02, $00
+dd paralyzing_byte
+dd paralyz_byte_asm;
+
+db $00, $01, $05, $00
+dd dirty_flute
+dd $00C28B6D;
+
+db $00, $01, $05, $00
+dd dirty_kiss
+dd $00C28B6D;
+
+db $00, $03, $03, $01;fire zeta
+dd $00EF8543
+dd fire_zeta
+
+db $00, $03, $03, $03;fire epsilon
+dd $00EF8543
+dd fire_epsilon
+
+db $00, $01, $03, $01;Freeze zeta
+dd $00EF8543
+dd freeze_zeta
+
+db $00, $01, $03, $01;Freeze epsilon
+dd $00EF8543
+dd freeze_epsilon
+
+db $00, $01, $03, $02;Freeze delta
+dd $00EF8543
+dd freeze_delta
+
+db $00, $01, $03, $02;Freeze lambda
+dd $00EF8543
+dd freeze_lambda
+
+db $00, $04, $03, $03;Special zeta
+dd $00EF8543
+dd special_zeta
+
+db $00, $04, $03, $05;Special epsilon
+dd $00EF8543
+dd special_epsilon
+
+db $00, $01, $03, $04;Paralysis Delta
+dd $00EF8543
+dd paralyz_delta
+
+db $00, $04, $03, $01;Thunder zeta
+dd $00EF8543
+dd thunder_zeta
+
+db $00, $04, $03, $01;Thunder epsilon
+dd $00EF8543
+dd thunder_epsilon
+
+db $00, $04, $03, $01;Thunder lambda
+dd $00EF8543
+dd thunder_delta
+
+db $00, $04, $03, $01;Thunder delta
+dd $00EF8543
+dd thunder_lambda
+
+db $00, $04, $03, $01;starstorm zeta
+dd $00EF8543
+dd starstorm_zeta
+
+db $00, $04, $03, $03;starstorm epsilon
+dd $00EF8543
+dd starstorm_epsilon
+
+db $00, $04, $03, $06;starstorm delta
+dd $00EF8543
+dd starstorm_delta
+
+db $00, $04, $03, $0C;starstorm lambda
+dd $00EF8543
+dd starstorm_lambda
+
+db $00, $04, $03, $00
+dd warm_breath
+dd fire_zeta
+
+db $00, $04, $03, $00
+dd sizzle_breath
+dd fire_epsilon
+
+db $00, $01, $05, $00
+dd fireball_zeta
+dd flaming_fireball_zeta
+
+db $00, $01, $05, $00
+dd fireball_epsilon
+dd flaming_fireball_epsilon
+
+db $00, $04, $05, $00
+dd spray_fire_zeta
+dd fire_zeta
+
+db $00, $04, $05, $00
+dd spray_fire_epsilon
+dd fire_epsilon
+
+db $00, $04, $05, $00
+dd lukewarm_espresso
+dd fire_zeta;
+
+db $00, $04, $05, $00
+dd piping_espresso
+dd fire_epsilon;
+
+db $00, $04, $05, $00
+dd tingling_jolt
+dd thunder_zeta;
+
+db $00, $04, $05, $00
+dd minor_zap
+dd thunder_epsilon
+
+db $00, $04, $05, $00
+dd dull_shock
+dd thunder_delta;
+
+db $00, $04, $05, $00
+dd lv_shock
+dd thunder_lambda;
+
+db $00, $04, $05, $00
+dd fizzled_zap
+dd thunder_zeta;
+
+db $00, $04, $05, $00
+dd crackling_whoosh_bam
+dd thunder_epsilon
+
+db $00, $04, $05, $00
+dd sparking_swish_bang
+dd thunder_delta;
+
+db $00, $04, $05, $00
+dd jolting_shock_zap
+dd thunder_lambda;
+
+db $00, $04, $05, $00
+dd extinguish_zeta
+dd fire_zeta;
+
+db $00, $04, $05, $00
+dd extinguish_epsilon
+dd fire_epsilon;
+
+db $00, $04, $05, $00
+dd $00ef8f17
+dd $00C29871;Giygas phase 2 thunder 1
+
+db $00, $04, $05, $00
+dd $00ef8f17
+dd thunder_lambda
+
+db $00, $04, $05, $00
+dd $00EF8F17
+dd thunder_delta
+
+db $00, $04, $05, $00
+dd $00EF8F17
+dd thunder_epsilon
+
+db $00, $04, $05, $00
+dd $00EF8F17
+dd thunder_zeta
+;;;;;;;;;;;;;;;;;;;;;;;;
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd $00C29871
+
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd thunder_lambda
+
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd thunder_delta
+
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd thunder_epsilon
+
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd thunder_zeta
+;;;;;;;;;;;;;;;;;;;;
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd $00C29871
+
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd thunder_lambda
+
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd thunder_delta
+
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd thunder_epsilon
+
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd thunder_zeta
+;;;;;;;;;;;;;;;
+;freeze giygas
+db $00, $04, $05, $00
+dd $00EF8F17
+dd freeze_zeta
+
+db $00, $04, $05, $00
+dd $00EF8F17
+dd freeze_epsilon
+
+db $00, $04, $05, $00
+dd $00EF8F17
+dd freeze_delta
+
+db $00, $04, $05, $00
+dd $00EF8F17
+dd freeze_lambda
+;;;;;;;;;;;;;;;;;;;;
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd freeze_zeta
+
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd freeze_epsilon
+
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd freeze_delta
+
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd freeze_lambda
+;;;;;;;;;;;;;;;;;
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd freeze_zeta
+
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd freeze_epsilon
+
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd freeze_delta
+
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd freeze_lambda
+;;;;;;;;;;
+db $00, $04, $05, $00
+dd $00EF8F17
+dd $00C29987
+
+db $00, $04, $05, $00
+dd $00EF8F17
+dd $00C299AE
+;;;;;;;;;;;
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd $00C29987
+
+db $00, $04, $05, $00
+dd $00EF8EE2
+dd $00C299AE
+;;;;;;;;;;;;;;;;
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd $00C29987
+
+db $00, $04, $05, $00
+dd $00EF8F4A
+dd $00C299AE
+;;;;;;;;;;;;
+db $00, $01, $03, $05
+dd $00EF8543;Blast alpha
+dd blast_alpha
+
+db $00, $01, $03, $0F
+dd $00EF8543;Blast beta
+dd blast_beta
+
+db $00, $01, $03, $14
+dd $00EF8543;Blast gamma
+dd blast_gamma
+
+db $00, $01, $03, $1E
+dd $00EF8543;Blast omega
+dd blast_omega
+;;;;;;;;;;;;;;;;;;
+db $00, $01, $03, $05
+dd $00EF8543;Missile alpha
+dd missile_alpha
+
+db $00, $01, $03, $0F
+dd $00EF8543;Missile beta
+dd missile_beta
+
+db $00, $01, $03, $1E
+dd $00EF8543;Missile gamma
+dd missile_gamma
+
+db $00, $01, $03, $64
+dd $00EF8543;Missile omega
+dd missile_omega
+;;;;;;;;;;;;;;;;;
+;01ac
+db $00, $04, $04, $00
+dd $00c97eb7
+dd $00c29556
+
+;01AD
+db $00, $04, $04, $00
+dd $00c97eb7
+dd $00c29568
+
+;flash bomb
+db $00, $04, $04, $00
+dd $00c97eb7
+dd $00c29987
+
+db $00, $04, $04, $00
+dd $00c97eb7
+dd $00c299ef
+
+;fire bomb
+db $00, $03, $04, $00
+dd $00c97eb7
+dd $00c295ab
+
+db $00, $03, $04, $00
+dd $00c97eb7
+dd $00c295bd
+;freeze bomb
+db $00, $01, $04, $00
+dd $00c97eb7
+dd $00c29647
+
+db $00, $01, $04, $00
+dd $00c97eb7
+dd $00c29659
+;thunder bomb
+db $00, $04, $04, $00
+dd $00c97eb7
+dd $00c29871
+
+db $00, $04, $04, $00
+dd $00c97eb7
+dd $00c29889
+;Starstorm bomb
+db $00, $04, $04, $00
+dd $00c97eb7
+dd starstorm_delta
+
+db $00, $04, $04, $00
+dd $00c97eb7
+dd $00c29aa6
+;Missile bomb
+db $00, $01, $04, $00
+dd $00c97eb7
+dd $00c2a5d1
+
+db $00, $01, $04, $00
+dd $00c97eb7
+dd $00c2a5da
+;;;;;;;;;;;;
+;Special missile
+db $00, $04, $04, $00
+dd $00c97ddc
+dd $00c29556
+
+db $00, $04, $04, $00
+dd $00c97df5
+dd $00c2955f
+
+db $00, $04, $04, $00
+dd $00c97e24
+dd $00c29571
+;Flash missile
+db $00, $04, $04, $00
+dd $00c97ddc
+dd $00c29987
+
+db $00, $04, $04, $00
+dd $00c97df5
+dd $00c299ae
+
+db $00, $04, $04, $00
+dd $00c97e24
+dd $00c29a35
+;Fire missile
+db $00, $03, $04, $00
+dd $00c97ddc
+dd $00c295ab
+
+db $00, $03, $04, $00
+dd $00c97df5
+dd $00c295b4
+
+db $00, $03, $04, $00
+dd $00c97e24
+dd $00c295c6
+;freeze missile
+db $00, $01, $04, $00
+dd $00c97ddc
+dd $00c29647
+
+db $00, $01, $04, $00
+dd $00c97df5
+dd $00c29650
+
+db $00, $01, $04, $00
+dd $00c97e24
+dd $00c29662
+;thunder missile
+db $00, $04, $04, $00
+dd $00c97ddc
+dd $00c29871
+
+db $00, $04, $04, $00
+dd $00c97df5
+dd $00c2987d
+
+db $00, $04, $04, $00
+dd $00c97e24
+dd $00c29895
+;Star missile
+db $00, $04, $04, $00
+dd $00c97ddc
+dd starstorm_delta
+
+db $00, $04, $04, $00
+dd $00c97df5
+dd starstorm_lambda
+
+db $00, $04, $04, $00
+dd $00c97e24
+dd $00c29aaf
+;Bomb missile
+db $00, $01, $04, $00
+dd $00c97ddc
+dd $00c2a818
+
+db $00, $01, $04, $00
+dd $00c97df5
+dd blast_beta
+
+db $00, $01, $04, $00
+dd $00c97e24
+dd blast_omega
+;;;;;;;;;;;;;;;;;;;;;;
+;bazookas
+db $00, $04, $04, $00
+dd $00c97e9e
+dd $00c29556
+
+db $00, $04, $04, $00
+dd $00c97e9e
+dd $00c29568
+
+;flash bomb
+db $00, $04, $04, $00
+dd $00cc97e9e
+dd $00c29987
+
+db $00, $04, $04, $00
+dd $00c97e9e
+dd $00c299ef
+
+;fire bomb
+db $00, $03, $04, $00
+dd $00c97e9e
+dd $00c295ab
+
+db $00, $03, $04, $00
+dd $00c97e9e
+dd $00c295bd
+;freeze bomb
+db $00, $01, $04, $00
+dd $00c97e9e
+dd $00c29647
+
+db $00, $01, $04, $00
+dd $00c97e9e
+dd $00c29659
+;thunder bomb
+db $00, $04, $04, $00
+dd $00c97e9e
+dd $00c29871
+
+db $00, $04, $04, $00
+dd $00c97e9e
+dd $00c29889
+;Starstorm bomb
+db $00, $04, $04, $00
+dd $00c97e9e
+dd starstorm_delta
+
+db $00, $04, $04, $00
+dd $00c97e9e
+dd $00c29aa6
+;Missile bomb
+db $00, $01, $04, $00
+dd $00c97e9e
+dd $00c2a5d1
+
+db $00, $01, $04, $00
+dd $00c97e9e
+dd $00c2a5da
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;New assist PSI
+;Defense Up Alpha
+db $01, 01, $03, $07
+dd $00EF8543
+dd $00c2aac6
+;Defense Up Omega
+db $01, $04, $03, $15
+dd $00EF8543
+dd $00c2ab0d
+;Drain Alpha
+db $00, $01, $03, $08
+dd $00EF8543
+dd $00c2a46b
+;Drain Omega
+db $00, $04, $03, $18
+dd $00EF8543
+dd $00c2a507
+;Disable Alpha
+db $00, $01, $03, $06
+dd $00EF8543
+dd $00c2a3d1
+;Disable Omega
+db $00, $04, $03, $12
+dd $00EF8543
+dd $00c2a3d1
+;Stop Alpha
+db $00, $01, $03, $05
+dd $00EF8543
+dd $00c28cf1
+;Stop Omega
+db $00, $04, $03, $0F
+dd $00EF8543
+dd $00c28cf1
+
+;Neutralize Alpha
+db $00, $01, $03, $0A
+dd $00EF8543
+dd $00c2a422
+;Neutralize Omega
+db $00, $04, $03, $1E
+dd $00EF8543
+dd $00c290c6
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;Action 01E7
+db $00, $01, $04, $00
+dd hypnosis_alpha_item_anim
+dd $00c29f06
+
+db $00, $04, $04, $00
+dd hypnosis_omega_item_anim
+dd $00c29f57
+;;;;;;;;;;;;;;;;;;;;;;;
+db $00, $01, $04, $00
+dd paralysis_alpha_item_anim
+dd $00c29ffe
+
+db $00, $04, $04, $00
+dd paralysis_omega_item_anim
+dd $00c2a04f
+;;;;;;;;;;;;;;;;;;;;;;;
+;TODO: MAKE NEW ITEM ANIM CODES FOR THE EXTENDED ASSISTS
+;Offense Up
+db $01, $01, $04, $00
+dd $00ef8e27
+dd $00c29e38
+
+db $01, $04, $04, $00
+dd $00ef8e27
+dd $00c29e7f
+;;;;;;;;;;;;;;;;;;;;;;;
+db $00, $01, $04, $00
+dd defdown_alpha_item_anim
+dd $00cc29e86
+
+db $00, $04, $04, $00
+dd defdown_omega_item_anim
+dd $00cc29eff
+;;;;;;;;;;;;;;;;;;;;;
+db $00, $01, $04, $00
+dd brainshock_alpha_item_anim
+dd $00c2a056
+
+db $00, $04, $04, $00
+dd brainshock_omega_item_anim
+dd $00c2a0a7
+;;;;;;;;;;;;;;;;;;;;
+;new jeffer items go here?
+db $00, $04, $04, $00
+dd $00ef8e3c
+dd $00c2a3d1
+
+db $00, $04, $04, $00
+dd $00c97d75
+dd $00c28cf1
+;;;;;;;;;;
+;01F3
+db $01, $01, $04, $00
+dd $00c97b6b
+dd NewLuckySandwich
+;;;;;;;;;;;;;;;
+;Unique text for these?
+db $00, $04, $05, $00
+dd $00ef9e05
+dd $00C29987
+
+db $00, $04, $05, $00
+dd $00ef9e05
+dd $00C299AE
+
+db $00, $04, $05, $00
+dd $00ef9e05
+dd $00C299EF
+;;;;;;;;;;
+db $00, $01, $03, $01
+dd $00EF8543;Blast Zeta
+dd blast_zeta
+
+db $00, $01, $03, $03
+dd $00EF8543;Blast Epsilon
+dd blast_epsilon
+;;;;;;;;;;;;;;;;;;;;;;;;
+db $00, $01, $03, $01
+dd $00EF8543;Missile Zeta
+dd missile_zeta
+
+db $00, $01, $03, $03
+dd $00EF8543;Missile Epsilon
+dd missile_epsilon
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $00, $01, $01, $00
+dd throwbomb_zeta
+dd blast_zeta
+
+db $00, $01, $01, $00
+dd throwbomb_epsilon
+dd blast_epsilon
+
+db $00, $01, $01, $00
+dd throwbomb_alpha
+dd blast_alpha
+
+db $00, $01, $01, $00
+dd throwbomb_beta
+dd blast_beta
+
+db $00, $01, $01, $00
+dd throwbomb_gamma
+dd blast_gamma
+
+db $00, $01, $01, $00
+dd throwbomb_omega
+dd blast_omega
+;;;;;;;;;;;;;;;;;;;;;;;;;
+db $00, $01, $01, $00
+dd shootrocket_zeta
+dd missile_zeta
+
+db $00, $01, $01, $00
+dd shootrocket_epsilon
+dd missile_epsilon
+
+db $00, $01, $01, $00
+dd shootrocket_alpha
+dd missile_alpha
+
+db $00, $01, $01, $00
+dd shootrocket_beta
+dd missile_beta
+
+db $00, $01, $01, $00
+dd shootrocket_gamma
+dd missile_gamma
+
+db $00, $01, $01, $00
+dd shootrocket_omega
+dd missile_omega
+;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $00, $01, $05, $00
+dd paralyz_pollen_lambda
+dd $C28CF1
+
+db $00, $04, $05, $00
+dd paralyz_pollen_omega
+dd $C28A92
+;;;;;;;;;;;;;;;;
+db $00, $00, $05, $00
+dd $EF9A47
+dd code_test_load_enemyvram
+
+
+;New battle text
+ORG $FE0000
+regular_eyes:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $97, $9c, $91, $a2, $95, $94, $50, $a7
+db $99, $a4, $98, $50, $99, $a4, $a3, $50, $a2, $95, $97, $a5, $9c, $91, $a2, $50
+db $95, $a9, $95, $a3, $51, $03, $10, $01, $02;Glared with regular eyes!
+
+creepy_eyes:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $97, $9c, $91, $a2, $95, $94, $50, $a7
+db $99, $a4, $98, $50, $99, $a4, $a3, $50, $93, $a2, $95, $95, $a0, $a9, $50, $95
+db $a9, $95, $a3, $51, $03, $10, $01, $02;glared with its creepy eyes!
+
+gentle_light:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $95, $9d, $99, $a4, $a4, $95, $94, $50
+db $91, $50, $97, $95, $9e, $a4, $9c, $95, $50, $9c, $99, $97, $98, $a4, $51, $03
+db $02
+
+harsh_light:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $95, $9d, $99, $a4, $a4, $95, $94, $50
+db $91, $50, $98, $91, $a2, $a3, $98, $50, $9c, $99, $97, $98, $a4, $51, $03, $02
+
+radiant_light:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $95, $9d, $99, $a4, $a4, $95, $94, $50
+db $91, $50, $a2, $91, $94, $99, $91, $9e, $a4, $50, $9c, $99, $97, $98, $a4, $51
+db $03, $02
+
+fireball_gamma:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $95, $a7, $95, $94, $50, $9f
+db $a5, $a4, $50, $91, $50, $98, $9f, $a4, $50, $96, $99, $a2, $95, $92, $91, $9c
+db $9c, $51, $1f, $02, $4b, $03, $10, $01, $02
+
+fireball_beta:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $95, $a7, $95, $94, $50, $9f
+db $a5, $a4, $50, $91, $50, $a3, $9d, $9f, $9c, $94, $95, $a2, $99, $9e, $97, $50
+db $96, $99, $a2, $95, $92, $91, $9c, $9c, $51, $1f, $02, $4b, $03, $10, $01, $02
+
+fireball_alpha:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $95, $a7, $95, $94, $50, $9f
+db $a5, $a4, $50, $91, $50, $9c, $99, $a4, $a4, $9c, $95, $50, $92, $99, $a4, $50
+db $9f, $96, $50, $96, $99, $a2, $95, $51, $1f, $02, $4b, $03, $10, $01, $02
+
+breathe_fire_alpha:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $92, $a2, $95, $91, $a4, $98, $95, $94
+db $50, $91, $50, $98, $9f, $a4, $50, $92, $a2, $95, $91, $a4, $98, $51, $1f, $02
+db $4b, $03, $10, $01, $02
+
+breathe_fire_beta:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $92, $a2, $95, $91, $a4, $98, $95, $94
+db $50, $91, $50, $92, $a5, $a2, $a3, $a4, $50, $9f, $96, $50, $98, $95, $91, $a4
+db $51, $1f, $02, $4b, $03, $10, $01, $02
+
+breathe_fire_omega:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $92, $a2, $95, $91, $a4, $98, $95, $94
+db $50, $a7, $98, $99, $a4, $95, $5d, $98, $9f, $a4, $50, $96, $99, $a2, $95, $51
+db $1f, $02, $4b, $03, $10, $01, $02
+
+spray_fire_alpha:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $9f, $a5, $a4
+db $50, $91, $50, $9c, $99, $9e, $95, $50, $9f, $96, $50, $96, $99, $a2, $95, $51
+db $1f, $02, $4b, $03, $10, $01, $02
+
+spray_fire_beta:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $9f, $a5, $a4
+db $50, $91, $50, $92, $99, $a4, $50, $9f, $96, $50, $96, $99, $a2, $95, $51, $1f
+db $02, $4b, $03, $10, $01, $02
+
+spray_fire_gamma:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $9f, $a5, $a4
+db $50, $91, $50, $a3, $a4, $a2, $95, $91, $9d, $50, $9f, $96, $50, $96, $99, $a2
+db $95, $51, $1f, $02, $4b, $03, $10, $01, $02
+
+dirty_fangs:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a4, $9f, $9f, $9b, $50, $91, $50, $92
+db $99, $a4, $95, $50, $a5, $a3, $99, $9e, $97, $50, $99, $a4, $a3, $50, $94, $99
+db $a2, $a4, $a9, $50, $96, $91, $9e, $97, $a3, $51, $03, $02
+
+dirty_stinger:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a4, $a5, $9e, $97, $50, $a7, $99
+db $a4, $98, $50, $99, $a4, $a3, $50, $94, $99, $a2, $a4, $a9, $50, $a3, $a4, $99
+db $9e, $97, $95, $a2, $51, $03, $10, $01, $02
+
+fizzling_crack_zap:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $76
+db $99, $aa, $aa, $9c, $99, $9e, $97, $50, $73, $a2, $91, $93, $9b, $50, $8a, $91
+db $a0, $50, $91, $a4, $a4, $91, $93, $9b, $51, $03, $02
+
+thunder_boom_bang:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $84
+db $98, $a5, $9e, $94, $95, $a2, $99, $9e, $97, $50, $72, $9f, $9f, $9d, $50, $72
+db $91, $9e, $97, $50, $91, $a4, $a4, $91, $93, $9b, $51, $03, $02
+
+shocking_blast_burst:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $83
+db $98, $9f, $93, $9b, $99, $9e, $97, $50, $72, $9c, $91, $a3, $a4, $50, $72, $a5
+db $a2, $a3, $a4, $50, $91, $a4, $a4, $91, $93, $9b, $51, $03, $02
+
+static_shock:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50
+db $a3, $a4, $91, $a4, $99, $93, $50, $a3, $98, $9f, $93, $9b, $50, $91, $a4, $a4
+db $91, $93, $9b, $51, $03, $02
+
+charged_shock:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50
+db $93, $98, $91, $a2, $97, $95, $94, $50, $a3, $98, $9f, $93, $9b, $50, $91, $a4
+db $a4, $91, $93, $9b, $51, $03, $02
+
+high_voltage_shock:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50
+db $98, $99, $97, $98, $5d, $a6, $9f, $9c, $a4, $91, $97, $95, $50, $a3, $98, $9f
+db $93, $9b, $50, $91, $a4, $a4, $91, $93, $9b, $51, $03, $02
+
+espresso_beta:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $99, $9c, $9c, $95, $94, $50
+db $a3, $9f, $9d, $95, $50, $92, $9c, $99, $a3, $a4, $95, $a2, $99, $9e, $97, $50
+db $98, $9f, $a4, $50, $95, $a3, $a0, $a2, $95, $a3, $a3, $9f, $51, $03, $02
+
+espresso_gamma:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $99, $9c, $9c, $95, $94, $50
+db $a3, $9f, $9d, $95, $50, $92, $9f, $99, $9c, $99, $9e, $97, $50, $98, $9f, $a4
+db $50, $95, $a3, $a0, $a2, $95, $a3, $a3, $9f, $51, $03, $02
+
+espresso_omega:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $99, $9c, $9c, $95, $94, $50
+db $91, $50, $a7, $98, $9f, $9c, $95, $50, $9a, $a5, $97, $50, $9f, $96, $50, $9e
+db $a5, $93, $9c, $95, $91, $a2, $50, $99, $9e, $96, $95, $a2, $9e, $9f, $50, $95
+db $a3, $a0, $a2, $95, $a3, $a3, $9f, $51, $03, $02
+
+extinguish_alpha:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $94, $99, $a3, $a0, $95, $9e, $a3, $95
+db $94, $50, $91, $9e, $50, $95, $a8, $a4, $99, $9e, $97, $a5, $99, $a3, $98, $99
+db $9e, $97, $50, $92, $a5, $a2, $a3, $a4, $51, $1f, $02, $58, $03, $10, $01, $02
+
+extinguish_beta:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $94, $99, $a3, $a0, $95, $9e, $a3, $95
+db $94, $50, $91, $9e, $50, $95, $a8, $a4, $99, $9e, $97, $a5, $99, $a3, $98, $99
+db $9e, $97, $50, $96, $9c, $91, $a2, $95, $51, $1f, $02, $58, $03, $10, $01, $02
+
+extinguish_omega:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $94, $99, $a3, $a0, $95, $9e, $a3, $95
+db $94, $50, $91, $9e, $50, $95, $a8, $a4, $99, $9e, $97, $a5, $99, $a3, $98, $99
+db $9e, $97, $50, $95, $a8, $a0, $9c, $9f, $a3, $99, $9f, $9e, $51, $1f, $02, $58
+db $03, $10, $01, $02
+
+solid_byte:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $92, $99, $a4, $50, $a9, $9f, $a5, $50
+db $a3, $9f, $96, $a4, $51, $03, $02
+
+paralyzing_byte:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $92, $99, $a4, $50, $a9, $9f, $a5, $50
+db $a7, $99, $a4, $98, $50, $91, $50, $93, $a2, $a5, $9e, $93, $98, $99, $9e, $97
+db $50, $93, $98, $9f, $9d, $a0, $51, $03, $02
+
+dirty_flute:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a0, $9c, $91, $a9, $95, $94, $50, $91
+db $50, $96, $9c, $a5, $a4, $95, $50, $a7, $99, $a4, $98, $50, $99, $a4, $a3, $50
+db $94, $99, $a3, $97, $a5, $a3, $a4, $99, $9e, $97, $50, $92, $a2, $95, $91, $a4
+db $98, $51, $03, $02
+
+dirty_kiss:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $97, $91, $a6, $95, $50, $a4, $98, $95
+db $50, $a3, $9d, $9f, $9f, $93, $98, $50, $9f, $96, $50, $a3, $99, $93, $9b, $9e
+db $95, $a3, $a3, $51, $03, $10, $01, $02
+
+warm_breath:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $92, $a2, $95, $91, $a4, $98, $95, $94
+db $50, $a7, $91, $a2, $9d, $9c, $a9, $50, $9f, $9e, $50, $a9, $9f, $a5, $51, $1F
+db $02, $4b, $03, $10, $01, $02
+
+sizzle_breath:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $92, $a2, $95, $91, $a4, $98, $95, $94
+db $50, $91, $50, $a3, $99, $aa, $aa, $9c, $99, $9e, $97, $50, $98, $9f, $a4, $50
+db $92, $a2, $95, $91, $a4, $98, $51, $1F, $02, $4b, $03, $10, $01, $02
+
+fireball_zeta:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $95, $a7, $95, $94, $50, $9f
+db $a5, $a4, $50, $98, $9f, $a4, $50, $95, $9d, $92, $95, $a2, $a3, $51, $1f, $02
+db $4b, $03, $10, $01, $02
+
+fireball_epsilon:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $95, $a7, $95, $94, $50, $9f
+db $a5, $a4, $50, $91, $50, $96, $9c, $99, $93, $9b, $95, $a2, $99, $9e, $97, $50
+db $96, $99, $a2, $95, $92, $91, $9c, $9c, $51, $1f, $02, $4b, $03, $10, $01, $02
+
+spray_fire_zeta:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $9f, $a5, $a4
+db $50, $91, $50, $a3, $a0, $9c, $91, $a3, $98, $50, $9f, $96, $50, $96, $99, $a2
+db $95, $51, $1f, $02, $4b, $03, $10, $01, $02
+
+spray_fire_epsilon:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $9f, $a5, $a4
+db $50, $91, $50, $92, $a5, $a2, $a3, $a4, $50, $9f, $96, $50, $96, $99, $a2, $95
+db $51, $1f, $02, $4b, $03, $10, $01, $02
+
+lukewarm_espresso:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $99, $9c, $9c, $95, $94, $50
+db $a3, $9f, $9d, $95, $50, $9c, $a5, $9b, $95, $a7, $91, $a2, $9d, $50, $95, $a3
+db $a0, $a2, $95, $a3, $a3, $9f, $51, $03, $02
+
+piping_espresso:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a3, $a0, $99, $9c, $9c, $95, $94, $50
+db $a3, $9f, $9d, $95, $50, $a0, $99, $a0, $99, $9e, $97, $50, $98, $9f, $a4, $50
+db $95, $a3, $a0, $a2, $95, $a3, $a3, $9f, $51, $03, $02
+
+tingling_jolt:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $a4
+db $99, $9e, $97, $9c, $99, $9e, $97, $50, $9a, $9f, $9c, $a4, $50, $91, $a4, $a4
+db $91, $93, $9b, $51, $03, $02
+
+minor_zap:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $9d
+db $99, $9e, $9f, $a2, $50, $aa, $91, $a0, $50, $91, $a4, $a4, $91, $93, $9b, $51
+db $03, $02
+
+dull_shock:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $94
+db $a5, $9c, $9c, $50, $a3, $98, $9f, $93, $9b, $50, $91, $a4, $a4, $91, $93, $9b
+db $51, $03, $02
+
+lv_shock:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $69
+db $5d, $a6, $9f, $9c, $a4, $50, $a3, $98, $9f, $93, $9b, $50, $91, $a4, $a4, $91
+db $93, $9b, $51, $03, $02
+
+fizzled_zap:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $76
+db $99, $aa, $aa, $9c, $99, $9e, $97, $50, $87, $98, $99, $96, $96, $50, $8a, $91
+db $a0, $50, $91, $a4, $a4, $91, $93, $9b, $51, $03, $02
+
+crackling_whoosh_bam:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $73
+db $a2, $91, $93, $9b, $9c, $99, $9e, $97, $50, $87, $98, $9f, $9f, $a3, $98, $50
+db $72, $91, $9d, $50, $91, $a4, $a4, $91, $93, $9b, $51, $03, $02
+
+sparking_swish_bang:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $83
+db $a0, $91, $a2, $9b, $99, $9e, $97, $50, $83, $a7, $99, $a3, $98, $50, $72, $91
+db $9e, $97, $50, $91, $a4, $a4, $91, $93, $9b, $51, $03, $02
+
+jolting_shock_zap:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $a5, $a3, $95, $94, $50, $91, $50, $7a
+db $9f, $9c, $a4, $99, $9e, $97, $50, $83, $98, $9f, $93, $9b, $50, $8a, $91, $a0
+db $50, $91, $a4, $a4, $91, $93, $9b, $51, $03, $02
+
+extinguish_zeta:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $94, $99, $a3, $a0, $95, $9e, $a3, $95
+db $94, $50, $91, $9e, $50, $95, $a8, $a4, $99, $9e, $97, $a5, $99, $a3, $98, $99
+db $9e, $97, $50, $a0, $a5, $96, $96, $51, $1f, $02, $58, $03, $10, $01, $02
+
+extinguish_epsilon:
+db $01, $1f, $02, $19, $70, $1c, $0d, $50, $94, $99, $a3, $a0, $95, $9e, $a3, $95
+db $94, $50, $91, $9e, $50, $95, $a8, $a4, $99, $9e, $97, $a5, $99, $a3, $98, $99
+db $9e, $97, $50, $a3, $a0, $9c, $91, $a3, $98, $51, $1f, $02, $58, $03, $10, $01
+db $02
+
+throwbomb_zeta:
+db $01, $1F, $02, $19
+db $70, $1c, $0d, $50, $a4, $a2, $99, $95, $94, $50, $a4, $9f, $50, $a4, $98, $a2
+db $9f, $a7, $50, $91, $50, $92, $9f, $9d, $92, $5c, $10, $05, $50, $92, $a5, $a4
+db $50, $a4, $a2, $99, $a0, $a0, $95, $94, $50, $9f, $a6, $95, $a2, $50, $a9, $9f
+db $a5, $51
+db $1F, $02, $11
+db $03, $02
+
+throwbomb_epsilon:
+db $01, $1F, $02, $19
+db $70, $1c, $0d, $50, $a4, $98, $a2, $95, $a7, $50, $91, $50, $a4, $9f, $a9
+db $50, $92, $9f, $9d, $92, $51
+db $1F, $02, $11, $03
+db $02
+
+throwbomb_alpha:
+db $01, $1F, $02, $19
+db $70, $1c, $0d, $50, $a4, $98, $a2, $95, $a7, $50, $91, $50, $92, $9f, $9d
+db $92, $51
+db $1F, $02, $11, $03
+db $02
+
+throwbomb_beta:
+db $01, $1F, $02, $19
+db $01, $70, $1c, $0d, $50, $a4, $98, $a2, $95, $a7, $50, $91, $50, $92, $99, $97
+db $50, $92, $9f, $9d, $92, $51
+db $1F, $02, $11, $03
+db $02
+
+throwbomb_gamma:
+db $01, $1F, $02, $19
+db $70, $1c, $0d, $50, $a4, $98, $a2, $95, $a7, $50, $91, $50, $a3, $a5, $a0
+db $95, $a2, $50, $92, $9f, $9d, $92, $51
+db $1F, $02, $11, $03
+db $02
+
+throwbomb_omega:
+db $01, $1F, $02, $19
+db $70, $1c, $0d, $50, $a4, $98, $a2, $95, $a7, $50, $91, $50, $98, $91, $9e, $94
+db $98, $95, $9c, $94, $50, $9e, $a5, $93, $9c, $95, $91, $a2, $50, $92, $9f, $9d
+db $92, $51
+db $1F, $02, $11, $03
+db $02
+
+shootrocket_zeta:
+db $01, $1F, $02, $19
+db $01, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $91, $50, $94, $a5, $94, $50
+db $92, $9f, $a4, $a4, $9c, $95, $50, $a2, $9f, $93, $9b, $95, $a4, $51
+db $1F, $02, $46, $03
+db $02
+
+shootrocket_epsilon:
+db $01, $1F, $02, $19
+db $01, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $91, $50, $a2, $9f, $9d, $91
+db $9e, $50, $93, $91, $9e, $94, $9c, $95, $51
+db $1F, $02, $46, $03
+db $02
+
+shootrocket_alpha:
+db $01, $1F, $02, $19
+db $01, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $91, $50, $92, $9f, $a4, $a4
+db $9c, $95, $50, $a2, $9f, $93, $9b, $95, $a4, $51
+db $1F, $02, $46
+db $03
+db $02
+
+shootrocket_beta:
+db $01, $1F, $02, $19
+db $01, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $91, $50, $94, $9f, $a5, $92
+db $9c, $95, $50, $92, $9f, $a4, $a4, $9c, $95, $50, $a2, $9f, $93, $9b, $95, $a4
+db $51
+db $1F, $02, $46, $03
+db $02
+
+shootrocket_gamma:
+db $01, $1F, $02, $19
+db $01, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $91, $50, $92, $99, $97, $50
+db $92, $9f, $a4, $a4, $9c, $95, $50, $a2, $9f, $93, $9b, $95, $a4, $51
+db $1F, $02, $46, $03
+db $02
+
+shootrocket_omega:
+db $01, $1F, $02, $19
+db $01, $70, $1c, $0d, $50, $a3, $98, $9f, $a4, $50, $91, $50, $a2, $95, $91, $9c
+db $50, $97, $a5, $9e, $51
+db $1F, $02, $46, $03
+db $02
+
+paralyz_pollen_lambda:
+db $01, $1F, $02, $19
+db $01, $70, $1C, $0D, $50
+db $97, $9f, $a4, $50, $91, $50, $9c, $99, $a4, $a4, $9c, $95, $50, $a0, $9f, $9c
+db $9c, $95, $9e, $50, $9f, $9e, $50, $a9, $9f, $a5, $51
+db $1F, $02, $52
+db $03
+db $02
+
+paralyz_pollen_omega:
+db $01, $1F, $02, $19
+db $01, $70, $1C, $0D, $50
+db $97, $9f, $a4, $50, $91, $50, $92, $a5, $9e, $93, $98, $50, $9f, $96, $50, $a0
+db $9f, $9c, $9c, $95, $9e, $50, $95, $a6, $95, $a2, $a9, $a7, $98, $95, $a2, $95
+db $51
+db $1F, $02, $52
+db $03
+db $02
+
+vram_test_message:
+db $01, $70
+db $77, $9F, $50, $97, $9F, $50, $97, $91, $94, $97, $95, $A4, $50, $94, $A9, $9E
+db $91, $9D, $99, $93, $50, $A6, $A2, $91, $9D, $51, $03, $02
+
+;New battle code
+ORG $FD0000
+flaming_fireball_gamma:
+REP #$31
+PHD
+TDC
+ADC #$FFF0
+TCD
+LDA #$00FA
+JML $C29016
+
+flaming_fireball_beta:
+REP #$31
+PHD
+TDC
+ADC #$FFF0
+TCD
+LDA #$0096
+JML $C29016
+
+flaming_fireball_alpha:
+REP #$31
+PHD
+TDC
+ADC #$FFF0
+TCD
+LDA #$005A
+JML $C29016
+
+check_status_types:
+LDA $B587
+AND #$00FF
+PHA
+TAX
+LDA StatusChecks,X
+AND #$00FF
+TAY
+PLA
+ASL
+TAX
+LDA StatusText,X
+STA $B588
+LDX #$0000
+SEP #$20
+STZ $B587
+REP #$20
+JML $C28FCE
+
+check_diamond_types:
+LDA $B587
+AND #$00FF
+PHA
+TAX
+LDA DiamondChecks,X
+AND #$00FF
+TAY
+PLA
+ASL
+TAX
+LDA DiamondText,X
+STA $B588
+LDX #$0000
+SEP #$20
+STZ $B587
+REP #$20
+JML $C28FCE
+
+check_status_text:
+LDA $B588
+STZ $B588
+STA $0E
+JML $C28FDE
+
+dirty_fang_asm:
+LDA #$0001
+STA $B587
+STA $B588
+JML $C28F97
+
+paralyz_byte_asm:
+LDA #$0001
+STA $B587
+STA $B588
+JML $C2916E
+
+fire_zeta:
+REP #$31
+LDA #$0014
+JML $C295B0
+
+fire_epsilon:
+REP #$31
+LDA #$0028
+JML $C295B0
+
+freeze_zeta:
+REP #$31
+LDA #$0010
+JML $C2964C
+
+freeze_epsilon:
+REP #$31
+LDA #$0020
+JML $C2964C
+
+freeze_delta:
+REP #$31
+LDA #$003C
+JML $C2964C
+
+freeze_lambda:
+REP #$31
+LDA #$0064
+JML $C2964C
+
+special_zeta:
+REP #$31
+LDA #$000F
+JML $C2955B
+
+special_epsilon:
+REP #$31
+LDA #$001E
+JML $C2955B
+
+paralyz_delta:
+LDA #$0003
+STA $B587
+STA $B588
+JML $C29FFE
+
+para_stun:
+LDA $B587
+AND #$00FF
+BEQ .paralyze
+LDY #$0004
+LDX #$0002
+BRA .stun
+.paralyze:
+LDY #$0003
+LDX #$0000
+.stun:
+JML $C2A024
+
+para_text:
+LDA $B587
+AND #$00FF
+BEQ .paralyze
+LDA #$6BEF
+BRA .stun
+.paralyze:
+LDA #$6AE0
+.stun:
+STA $0E
+STZ $B587
+STZ $B588
+JML $C2A034
+
+thunder_zeta:
+REP #$31
+LDX #$0001
+LDA #$0010
+JML $C29879
+
+thunder_epsilon:
+REP #$31
+LDX #$0002
+LDA #$0010
+JML $C29879
+
+thunder_delta:
+REP #$31
+LDX #$0003
+LDA #$0010
+JML $C29879
+
+thunder_lambda:
+REP #$31
+LDX #$0004
+LDA #$0010
+JML $C29879
+
+starstorm_zeta:
+REP #$31
+LDA #$0016
+JML $C29AAB
+
+starstorm_epsilon:
+REP #$31
+LDA #$002D
+JML $C29AAB
+
+starstorm_delta:
+REP #$31
+LDA #$005A
+JML $C29AAB
+
+starstorm_lambda:
+REP #$31
+LDA #$00B4
+JML $C29AAB
+
+flaming_fireball_zeta:
+REP #$31
+PHD
+TDC
+ADC #$FFF0
+TCD
+LDA #$0014
+JML $C29016
+
+flaming_fireball_epsilon:
+REP #$31
+PHD
+TDC
+ADC #$FFF0
+TCD
+LDA #$002D
+JML $C29016
+
+blast_alpha:
+REP #$31
+LDA #$005A
+JSR blast_common
+RTL
+
+blast_beta:
+REP #$31
+LDA #$00B4
+JSR blast_common
+RTL
+
+blast_gamma:
+REP #$31
+LDA #$010E
+JSR blast_common
+RTL
+
+blast_omega:
+REP #$31
+LDA #$0168
+JSR blast_common
+RTL
+
+missile_alpha:
+REP #$31
+LDA #$0001
+JSR missile_common
+RTL
+
+missile_beta:
+REP #$31
+LDA #$0003
+JSR missile_common
+RTL
+
+missile_gamma:
+REP #$31
+LDA #$0005
+JSR missile_common
+RTL
+
+missile_omega:
+REP #$31
+LDA #$0014
+JSR missile_common
+RTL
+
+
+
+blast_common:
+REP #$31
+PHD
+PHA
+TDC
+ADC #$FFEA
+TCD
+PLA
+TAX
+STX $1A
+LDY #$941C
+JSL goto_bank_c2
+CMP #$0000
+BNE .Return
+LDY #$A657
+LDA $1A
+JSL goto_bank_c2
+
+.Return:
+STZ $AA96
+PLD
+RTS
+
+goto_bank_c2:
+STA $00BC
+STY $00BE
+PEA $00B7
+SEP #$20
+LDA #$C2
+PHA
+REP #$20
+PHY
+LDA $00BC
+LDY $00BE
+RTL
+
+goto_bank_c1:
+STA $00BC
+STY $00BE
+PEA $0034
+SEP #$20
+LDA #$C1
+PHA
+REP #$20
+PHY
+LDA $00BC
+LDY $00BE
+RTL
+
+missile_common:
+REP #$31
+PHD
+PHA
+TDC
+ADC #$FFEA
+TCD
+PLA
+TAX
+STX $1A
+LDY #$941C
+JSL goto_bank_c2
+CMP #$0000
+BNE .Return
+LDY #$A579
+LDA $1A
+JSL goto_bank_c2
+.Return:
+STZ $AA96
+PLD
+RTS
+
+;Y has the jump address
+
+
+
+psi_letters:
+dw $008B ;alpha
+dw $008C ;beta
+dw $008D ;gamma
+dw $008E ;sigma
+dw $008F ;omega
+
+dw $00B2 ;Zeta
+dw $00B3 ;Epsilon
+dw $00B4 ;Delta
+dw $00B5 ;Lambda
+
+blast_zeta:
+REP #$31
+LDA #$0016
+JSR blast_common
+RTL
+
+blast_epsilon:
+REP #$31
+LDA #$002D
+JSR blast_common
+RTL
+
+missile_zeta:
+REP #$31
+LDA #$0001
+STA $B5EB
+JSR missile_common
+STZ $B5EB
+RTL
+
+missile_epsilon:
+REP #$31
+LDA #$0003
+STA $B5EB
+JSR missile_common
+STZ $B5EB
+RTL
+
+early_missile_damage:
+PHA
+LDA $B5EB
+BEQ late_missile_damage
+LDY #$0019
+late_missile_damage:
+PLA
+JML $C08FF7
+
+code_test_load_enemyvram:
+REP #$31
+LDA $AAB4
+CMP #$0004
+BCS .skip_vram_load ; 4 is the max enemies the game can handle. If we already loaded 4, ignore
+LDA $9D11
+AND #$00FF
+SEP #$20
+CMP $0782
+BEQ .skip_vram_load
+CMP $0783
+BEQ .skip_vram_load
+CMP $0784
+BEQ .skip_vram_load
+CMP $0785
+BEQ .skip_vram_load ;If the enemy is already loaded, don't load it into a new slot
+REP #$20
+JSR refresh_all_enemy_sprites ;Enemy sprites get cleared by things like PSI; they will corrupt if not refreshed
+LDA #$0090
+LDY #$005E
+INC $0780
+JSL goto_vramload
+.skip_vram_load:
+REP #$31
+JSL $C2C145 ;Do the enemy call
+STZ $0780
+STZ $B5ED
+RTL
+
+goto_vramload:
+REP #$31
+PHD
+TDC
+ADC #$FFE2
+TCD
+JML $C2EEF5
+
+refresh_all_enemy_sprites:
+LDA $AAB2
+STA $B5F5
+LDA $AAB4
+STA $B5F3
+STZ $AAB4
+STZ $AAB2
+LDA #$0000
+PHA
+.LoadNext:
+TAX
+LDA $0782,X
+AND #$00FF
+JSR get_enemy_sprite
+LDY #$EAE9
+JSL goto_bank_c2
+PLA
+INC
+CMP $B5F3
+BEQ .Done
+PHA
+BRA .LoadNext
+
+.Done:
+LDA $B5F5
+STA $AAB2
+LDA $B5F3
+STA $AAB4
+RTS
+
+get_enemy_sprite:
+TAX
+LDA #$0000
+.CheckNext:
+CPX #$0000
+BEQ .done
+DEX
+CLC
+ADC #$005E
+BRA .CheckNext
+.done:
+TAX
+LDA $D595A5,X
+RTS
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ORG $C28FC8
+JML check_status_types
+
+ORG $C28FD9
+JML check_status_text
+
+ORG $C291B6
+JML check_diamond_types
+
+ORG $C2A01E
+JML para_stun
+
+ORG $C2A02F
+JML para_text
+
+ORG $D5F9B0
+StatusChecks:
+db $05, $07
+
+DiamondChecks:
+db $02, $03
+
+StatusText:
+dw $6B18, $6B2F
+
+DiamondText:
+dw $6AC7, $6AE0
+
+InvPointers:
+dw $0000, $005F, $005F, $00BE, $011D
+
+ProgBats:
+db $13, $14, $15, $D4, $16, $17, $D6, $1B, $18, $19, $1A
+
+ProgPans:
+db $1C, $1D, $1E, $1F, $F8, $20, $22, $21
+
+ProgGuns:
+db $24, $25, $26, $27, $28, $29, $2A, $D7, $2B, $2C, $2D, $2E, $2F, $30
+;REPLACE BROKEN ONES IF NOT PREFIXED
+
+ProgBracelets:
+db $40, $41, $42, $43, $44, $45, $46, $47, $48
+
+ProgOther:
+db $4A, $4C, $4B, $4D, $51, $52, $DD, $F9, $DE, $53, $54, $55, $56
+
+ProgCaps:
+db $0A, $07, $0D, $08, $0C
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;Repointed PSI table
+macro PSIAddress06(address)
+ LDA #$0000
+ STA $06
+ lda #$00F5
+ sta $08
+endmacro
+
+macro PSIAddress0A(address)
+ LDA #$0000
+ STA $0A
+ lda #$00F5
+ sta $0C
+endmacro
+
+ORG $C1C4AA
+ LDA #$013B
+ STA $06
+ lda #$00F5
+ sta $08;Change for PSI shuffle- this is Poo's toplevel offensive psi
+
+ORG $C1C548
+ LDA #$014A
+ STA $06
+ lda #$00F5
+ sta $08;Change for PSI shuffle- this is Poo's toplevel offensive psi
+
+ORG $C1B694
+%PSIAddress06(NewPSI)
+
+ORG $C1B7CF
+%PSIAddress06(NewPSI)
+
+ORG $C1B8F9
+%PSIAddress0A(NewPSI)
+
+ORG $C1BB34
+%PSIAddress0A(NewPSI)
+
+ORG $C1B8BB
+LDA AllocPsiTable,X
+
+ORG $C1B9CC
+LDA AllocPsiTable,X
+
+ORG $C1C243
+%PSIAddress06(NewPSI)
+
+ORG $C1C2AD
+%PSIAddress06(NewPSI)
+
+ORG $C1C6E4
+%PSIAddress06(NewPSI)
+
+ORG $C1C8D6
+%PSIAddress06(NewPSI)
+
+ORG $C1C9D6
+LDA AllocPsiTable,X
+
+ORG $C1CADF
+LDA AllocPsiTable,X
+
+ORG $C1CA12
+%PSIAddress06(NewPSI)
+
+ORG $C1CCB6
+LDA AllocPsiTable,X
+
+ORG $C1CD49
+LDA AllocPsiTable,X
+
+ORG $C1CD8F
+%PSIAddress06(NewPSI)
+
+ORG $C1CE3B
+LDA AllocPsiTable,X
+
+ORG $C1D7D6
+%PSIAddress06(NewPSI)
+
+ORG $C1D839
+%PSIAddress06(NewPSI)
+
+ORG $C1D89B
+%PSIAddress06(NewPSI)
+
+ORG $C45EFE
+LDA AllocPsiTable,X
+
+ORG $C45F1B
+LDA AllocPsiTable,X
+
+ORG $C45F38
+LDA AllocPsiTable,X
+
+ORG $F5031B
+db $02, $06, $00;fire z
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $02, $07, $00;Fire ep
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+;;;;;;;;;;;;;;;;;;;;;;;;;
+db $03, $06, $00;freeze z
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $03, $07, $00;freeze e
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $03, $08, $00;freeze d
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $03, $09, $00;freeze l
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+;;;;;;;;;;;;;;;;;;;;;;;;
+db $01, $06, $00;special z
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $01, $07, $00;special e
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+;;;;;;;;;;;;;;;;;;;;;;;;
+db $0f, $09, $00;paralyze l
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+;;;;;;;;;;;;;;;;;;;;;;;;
+db $04, $06, $00;thunder z
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $04, $07, $00;thunder e
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $04, $08, $00;thunder d
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $04, $09, $00;thunder l
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $06, $06, $00;starstorm z
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $06, $07, $00;starstorm e
+db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
+
+db $06, $08, $01;starstorm d
+db $02, $6F, $01, $00, $00, $00, $09, $00, $6C, $D1, $EE, $00
+
+db $06, $09, $01;starstorm l
+db $02, $70, $01, $00, $00, $00, $0B, $00, $A5, $D1, $EE, $00
+
+db $13, $01, $01;blast a
+db $02, $A4, $01, $00, $00, $00, $09, $00, $1C, $D2, $EE, $FF
+
+db $13, $02, $01;blast b
+db $02, $A5, $01, $00, $00, $00, $0B, $00, $6C, $D2, $EE, $FF
+
+db $13, $03, $01;blast g
+db $02, $A6, $01, $00, $00, $00, $0D, $00, $BD, $D2, $EE, $FF
+
+db $13, $05, $01;blast o
+db $02, $A7, $01, $00, $00, $00, $0F, $00, $0E, $D3, $EE, $FF
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $14, $01, $01;missile a
+db $02, $A8, $01, $00, $00, $00, $09, $00, $5F, $D3, $EE, $FF
+
+db $14, $02, $01;missile b
+db $02, $A9, $01, $00, $00, $00, $0B, $00, $86, $D4, $EE, $FF
+
+db $14, $03, $01;missile g
+db $02, $AA, $01, $00, $00, $00, $0D, $00, $93, $D4, $EE, $FF
+
+db $14, $05, $01;missile o
+db $02, $AB, $01, $00, $00, $00, $0F, $00, $A0, $D4, $EE, $FF
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $15, $01, $04;defense up a
+db $02, $DD, $01, $00, $00, $00, $09, $00, $E6, $E3, $EE, $FF
+
+db $15, $05, $04;defense up o
+db $02, $DE, $01, $00, $00, $00, $09, $00, $33, $E4, $EE, $FF
+;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $16, $01, $04;drain a
+db $02, $DF, $01, $00, $00, $00, $09, $00, $7E, $E4, $EE, $FF
+
+db $16, $05, $04;drain o
+db $02, $E0, $01, $00, $00, $00, $09, $00, $C6, $E4, $EE, $FF
+;;;;;;;;;;;;;;;;;;;;;;;;
+db $17, $01, $04;disable a
+db $02, $E1, $01, $00, $00, $00, $09, $00, $19, $E5, $EE, $FF
+
+db $17, $05, $04;disable o
+db $02, $E2, $01, $00, $00, $00, $09, $00, $52, $E5, $EE, $FF
+;;;;;;;;;;;;;;;;;;;;;;
+db $18, $01, $04;stop a
+db $02, $E3, $01, $00, $00, $00, $09, $00, $8D, $E5, $EE, $FF
+
+db $18, $05, $04;stop o
+db $02, $E4, $01, $00, $00, $00, $09, $00, $A4, $E5, $EE, $FF
+;;;;;;;;;;;;;;;;;;;;;;;
+db $19, $01, $04;neutralize a
+db $02, $E5, $01, $00, $00, $00, $09, $00, $BD, $E5, $EE, $FF
+
+db $19, $05, $04;neutralize o
+db $02, $E6, $01, $00, $00, $00, $09, $00, $FA, $E5, $EE, $FF
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $13, $06, $01;blast z
+db $02, $A4, $01, $00, $00, $00, $09, $00, $1C, $D2, $EE, $FF
+
+db $13, $07, $01;blast e
+db $02, $A5, $01, $00, $00, $00, $0B, $00, $6C, $D2, $EE, $FF
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+db $14, $06, $01;missile z
+db $02, $A8, $01, $00, $00, $00, $09, $00, $5F, $D3, $EE, $FF
+
+db $14, $07, $01;missile e
+db $02, $A8, $01, $00, $00, $00, $09, $00, $5F, $D3, $EE, $FF
+
+
+;;;;;;;;;;;;;;;
+;PSI ANIMATIONS
+ORG $F50000
+;Table of all PSI attacks
+AllocPsiTable:
+
+ORG $EF8580
+db $0A, $00, $00, $FB
+
+ORG $FB0000
+db $1F, $C0, $5B; number of anims
+
+ORG $FB00CB
+dd $00EF88E9; Teleport 1. change to a $02
+dd $00EF88E9;Teleport 2. change to a $02
+
+dd $00EF874A ;Fire zeta
+dd $00EF874A ; Fire epsilon
+
+dd $00EF87AC ;Freeze zeta
+dd $00EF87AC ;Freeze epsilon
+dd $00EF87AC ;Freeze Delta
+dd $00EF87AC ;Freeze Lambda
+
+dd $00EF864C ;Special zeta
+dd $00EF864C ;Special epsilon
+
+dd $00EF899C ;Paralysis delta
+
+dd $00EF8813 ;thunder zeta
+dd $00EF8813 ;thunder epsilon
+dd $00EF8813 ;thunder Delta
+dd $00EF8813 ;thunder Lambda
+
+dd $00EF8882;Starstorm zeta
+dd $00EF8882;starstorm epsilon
+dd $00EF8882 ;starstorm lambda
+dd $00EF8882 ;starstorm delta
+
+dd blast_alpha_anim;Blast alpha
+dd blast_beta_anim;Blast beta
+dd blast_gamma_anim;Blast gamma
+dd blast_omega_anim;Blast gamma
+
+dd missile_alpha_anim
+dd missile_beta_anim
+dd missile_gamma_anim
+dd missile_omega_anim
+
+dd $00EF88E9;Defense up alpha
+dd $00EF88E9;Def up omega
+
+dd drain_alpha_anim;Drain alpha
+dd drain_omega_anim;Drain omega
+
+dd disable_anim;Disable alpha
+dd disable_anim;Disable omega
+
+dd $00EF88E9;Stop alpha
+dd $00EF88E9;Stop omega
+
+dd neutralize_anim;Neutralize alpha
+dd $00EF88E9;Neutralize omega
+
+dd blast_alpha_anim;Blast alpha
+dd blast_alpha_anim;Blast beta
+
+dd missile_alpha_anim;Missile Zeta
+dd missile_alpha_anim;Missile Epsilon
+
+db $02
+
+PSINameTable:
+
+;Extended PSI names
+ORG $FB06A9
+db $80, $83, $79, $50, $87, $91, $a6, $95, $50;PSI Wave
+
+ORG $FB06C2
+db $80, $83, $79, $50, $72, $9C, $91, $A3, $A4, $50;PSI Blast
+
+ORG $FB06DB
+db $80, $83, $79, $50, $7D, $99, $A3, $A3, $99, $9C, $95, $50;PSI Missile
+
+ORG $FB06F4
+db $74, $95, $96, $95, $9E, $A3, $95, $50, $A5, $A0, $50;Defense up
+
+ORG $FB070D
+db $74, $A2, $91, $99, $9E, $50;Drain
+
+ORG $FB0726
+db $74, $99, $A3, $91, $92, $9C, $95, $50;Disable
+
+ORG $FB073F
+db $83, $A4, $9F, $A0, $50;Stop
+
+ORG $FB0758
+db $7E, $95, $A5, $A4, $A2, $91, $9C, $99, $AA, $95, $50;Neutralize
+
+ORG $FB0771
+
+;These are the *script commands* that play the new PSI animations.
+blast_alpha_anim:
+db $1f, $02, $34, $1c, $13, $00, $37, $10, $17, $1f, $02, $5b, $10, $10, $1f, $02
+db $33, $10, $14, $1c, $13, $25, $00, $10, $01, $10, $01, $02
+
+blast_beta_anim:
+db $1f, $02, $34, $1c, $13, $00, $38, $10, $17, $1f, $02, $5b, $10, $10, $1f, $02
+db $33, $10, $14, $1c, $13, $25, $00, $10, $0f, $10, $10, $02
+
+blast_gamma_anim:
+db $1f, $02, $34, $1c, $13, $00, $39, $10, $17, $1f, $02, $5b, $10, $10, $1f, $02
+db $33, $10, $14, $1c, $13, $25, $00, $10, $0f, $10, $10, $02
+
+blast_omega_anim:
+db $1f, $02, $34, $1c, $13, $00, $3A, $10, $17, $1f, $02, $5b, $10, $10, $1f, $02
+db $33, $10, $14, $1c, $13, $25, $00, $10, $0f, $10, $10, $02
+
+missile_alpha_anim:
+db $1f, $02, $46, $1c, $13, $00, $3B, $10, $04, $1f, $02, $01, $1c, $13, $2b, $00
+db $10, $0f, $02
+
+missile_beta_anim:
+db $1f, $02, $46, $10, $05, $1f, $02, $46, $10, $05, $1f, $02, $46, $1c, $13, $00
+db $3c, $10, $04, $1f, $02, $01, $1c, $13, $2b, $00, $10, $0f, $02
+
+missile_gamma_anim:
+db $1f, $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1f
+db $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1c, $13, $00, $3d, $10, $04, $1f
+db $02, $01, $1c, $13, $2b, $00, $02
+
+missile_omega_anim:
+db $1f, $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1f
+db $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1f, $02
+db $46, $10, $02, $1f, $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1f, $02, $46
+db $10, $02, $1f, $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1f, $02, $46, $10
+db $02, $1f, $02, $46, $10, $02, $1f, $02, $46, $10, $02, $1c, $13, $00, $3e, $10
+db $04, $1f, $02, $01, $1c, $13, $2b, $00, $02
+
+drain_alpha_anim:
+db $1f, $02, $3b, $1c, $13, $00, $04, $10, $3c, $03, $02
+
+drain_omega_anim:
+db $1f, $02, $3b, $1c, $13, $00, $1e, $10, $3c, $03, $02
+
+disable_anim:
+db $1f, $02, $36, $10, $1e, $1c, $13, $00, $01, $10, $3c, $03, $02
+
+neutralize_anim:
+db $1f, $02, $63, $1c, $13, $00, $1b, $10, $3c, $03, $02
+
+hypnosis_alpha_item_anim:
+db $01, $70, $1c, $0d, $19, $1f, $1b, $04, $16, $ac, $16, $47, $00, $50, $50, $1c
+db $05, $00, $51, $03, $0a, $5c, $89, $ef
+
+hypnosis_omega_item_anim:
+db $01, $70, $1c, $0d, $19, $1f, $1b, $04, $16, $ac, $16, $47, $00, $50, $50, $1c
+db $05, $00, $51, $03, $0a, $6c, $89, $ef
+
+paralysis_alpha_item_anim:
+db $01, $70, $1c, $0d, $19, $1f, $1b, $04, $16, $ac, $16, $47, $00, $50, $50, $1c
+db $05, $00, $51, $03, $0a, $9c, $89, $ef
+
+paralysis_omega_item_anim:
+db $01, $70, $1c, $0d, $19, $1f, $1b, $04, $16, $ac, $16, $47, $00, $50, $50, $1c
+db $05, $00, $51, $03, $0a, $ac, $89, $ef
+
+defdown_alpha_item_anim:
+db $01, $70, $1c, $0d, $19, $1f, $1b, $04, $16, $ac, $16, $47, $00, $50, $50, $1c
+db $05, $00, $51, $03, $0a, $3c, $89, $ef
+
+defdown_omega_item_anim:
+db $01, $70, $1c, $0d, $19, $1f, $1b, $04, $16, $ac, $16, $47, $00, $50, $50, $1c
+db $05, $00, $51, $03, $0a, $4c, $89, $ef
+
+brainshock_alpha_item_anim:
+db $01, $70, $1c, $0d, $19, $1f, $1b, $04, $16, $ac, $16, $47, $00, $50, $50, $1c
+db $05, $00, $51, $03, $0a, $bc, $89, $ef
+
+brainshock_omega_item_anim:
+db $01, $70, $1c, $0d, $19, $1f, $1b, $04, $16, $ac, $16, $47, $00, $50, $50, $1c
+db $05, $00, $51, $03, $0a, $ce, $89, $ef
+
+
+ORG $C1C423
+LDA #$0500
+STA $06
+LDA #$00FB
+STA $08
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;psi symbol extensions
+macro GreekLetter06(address)
+ LDA.w #psi_letters
+ STA $06
+ lda #$00FD
+ sta $08
+endmacro
+
+ORG $C1C4F8
+%GreekLetter06(address)
+
+ORG $C1C569
+%GreekLetter06(address)
+
+ORG $C1C695
+%GreekLetter06(address)
+
+ORG $C1C78D
+%GreekLetter06(address)
+
+ORG $C1C7FE
+;%GreekLetter06(address)
+
+ORG $c1ca53
+%GreekLetter06(address)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;font stuff
+ORG $C3F054
+dd FontDataTable
+
+ORG $C3F058
+dd FontGFXTable
+
+ORG $C3F060
+dd SaturnFontTable
+
+ORG $C3F064
+dd SaturnFontGFX
+
+ORG $FA0000
+FontDataTable:
+
+ORG $FA0060
+FontExtensionData:
+;Font Widths
+db $05; _
+db $04 ;<
+db $06 ;Ζ
+db $03 ;ε
+db $04 ;δ
+db $05 ;λ
+db $06 ;&
+db $06 ;#
+db $05 ;arrows
+db $06 ;n
+db $05 ;ä
+db $05 ;é
+
+ORG $FA0100
+SaturnFontTable:
+
+ORG $FA0160
+SaturnExtensionData:
+db $0A; _
+db $08;<
+db $0B
+db $0B
+db $0B
+db $0B
+db $0D
+db $0B
+db $0B
+db $09
+db $08
+db $0B
+
+ORG $FC0000
+FontGFXTable:
+
+ORG $FC0C00
+incbin main_font_extended.bin
+
+
+ORG $FC1000
+SaturnFontGFX:
+
+ORG $FC1C00
+incbin saturn_font_extended.bin
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;PSI Animations
+ORG $C3F98D
+JMP $FE00
+
+ORG $C3FE00
+ CMP #$FFFF
+ BEQ $18
+ CMP #$0023
+ BCC $0B
+ CMP #$0036
+ BCS $03
+ JMP $F99B
+ SBC #$0014
+ STA $02
+ LDA $02
+ JSL $C2E116
+ JMP $FAC7
+
+ORG $C2E34F
+ LDA #$0000
+ STA $06
+ LDA #$00F6
+ STA $08
+
+ORG $C2E461
+ LDA #$0400
+ STA $06
+ LDA #$00F6
+ STA $08
+
+ORG $C2E2F4
+ LDA #$0600
+ STA $06
+ LDA #$00F6
+ STA $08
+
+ORG $C2E154
+dl new_psi_anims
+
+ORG $C2E1BB
+dl new_psi_anims
+
+ORG $C2E50A
+dl new_psi_anims
+
+ORG $F60000
+new_psi_anims:
+
+ORG $F60198
+;Headers for new PSI. Blast alpha.
+db $25, $AC, $04, $03, $01, $03, $0e, $00, $19, $32, $9c, $0a
+;Blast beta
+db $25, $AC, $04, $03, $01, $03, $0e, $00, $19, $32, $9c, $0a
+;Blast Gamma
+db $25, $AC, $04, $03, $01, $03, $10, $00, $19, $32, $9c, $0a
+;Blast Omega
+db $25, $AC, $04, $03, $01, $03, $10, $00, $19, $32, $9c, $0a
+
+;Missile
+db $27, $DB, $02, $03, $01, $03, $06, $00, $05, $19, $d4, $4b
+db $27, $DB, $02, $03, $01, $03, $0B, $00, $05, $19, $d4, $4b
+db $27, $DB, $02, $03, $01, $03, $0A, $00, $05, $19, $d4, $4b
+db $27, $DB, $02, $03, $01, $03, $0E, $00, $05, $19, $d4, $4b
+
+ORG $F60400
+animation_pointer_table:
+
+ORG $F60488
+dd $00F70000;Blast A
+dd $00F701F7; Blast b
+dd $00F70413; Blast g
+dd $00F70679; Blast o
+dd $00F70953; Missile a
+dd $00F70A4F; Missile B
+dd $00F70B85; Missile g
+dd $00F70CCE; Missile o
+
+
+ORG $F60600
+animation_palette_table:
+
+ORG $F60710
+db $00, $00, $9c, $0a, $75, $19, $bf, $63;Blast palette
+db $00, $00, $9c, $0a, $75, $19, $bf, $63;Blast palette
+db $00, $00, $9c, $0a, $75, $19, $bf, $63;Blast Gamma
+db $00, $00, $9c, $0a, $75, $19, $bf, $63;Blast Omega
+
+db $00, $00, $d4, $4b, $e5, $09, $ff, $7f;Missile alpha
+db $00, $00, $d4, $4b, $e5, $09, $ff, $7f
+db $00, $00, $d4, $4b, $e5, $09, $ff, $7f
+db $00, $00, $d4, $4b, $e5, $09, $ff, $7f; Missile omega
+
+ORG $F70000
+incbin psi_animations.bin ;this is specifically new animations i drew
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;expanded SRAM
+sramchunk0_pointer = $97F5
+sram_chunk0_size = $0493
+
+sramchunk1_pointer = $B570
+sramchunk1_size = $0010; ap related stuff
+
+sram_chunk2_pointer = $B590
+sramchunk2_size = $0080 ;Expanded storage space
+
+sram_chunk3_pointer = $B620
+sramchunk3_size = $01C0; Expanded flags
+
+sramchunk4_pointer = $31D0
+sramchunk4_size = $0100
+
+sramchunk5_pointer = $000D4
+sramchunk5_size = $00AC
+
+!save_size = #$AA0
+!save_bytes = #$A80
+
+check_corruption = $7E9F79
+memcopy = $C08EED
+mult16 = $C09032
+erase_save = $EF05A9
+copy_save = $EF06A2
+validate_checksum = $EF07C0
+save_game = $EF088F
+save_checksum_1 = $EF0734
+save_checksum_2 = $EF077B
+
+save_chunk_sizes = $EF0A52
+save_chunk_pointers = $EF0A5C
+
+;delete save
+ORG $EF05B3
+LDY !save_size
+
+ORG $EF05DD
+LDX !save_size
+
+ORG $EF063D
+LDY !save_size
+
+ORG $EF069B
+CPX #$0003
+
+;copy save
+ORG $EF06B8
+LDY !save_size
+
+ORG $EF06E2
+LDY !save_size
+
+ORG $EF072B
+LDA !save_size
+;;;;;;;;;
+ORG $EF073E
+LDY !save_size
+
+ORG $EF0773
+CMP !save_bytes
+;;;;;;;;;;;;;;;;;;;
+ORG $EF0785
+LDY !save_size
+
+ORG $EF079F
+CalcChecksum:
+LDY #$0000
+STY $02
+ .Loop:
+ LDA [$06],y
+ EOR $02
+ STA $02
+ INY
+ INY
+ CPY !save_bytes
+ BCC .Loop
+PLD
+RTS
+;;;;;;;;;;;;;;;
+ORG $EF07DB
+LDY !save_size
+
+ORG $EF082F
+CheckCorruptedSave:
+STA $10
+JSR validate_checksum
+CMP #$0000
+BEQ .Return
+.SaveCorrupted:
+LDA $10
+JSR erase_save
+LDX $10
+SEP #$20
+LDA $EF05A6,X
+ORA.w check_corruption
+STA.w check_corruption
+REP #$20
+.Return:
+PLD
+RTS
+
+ORG $EF0899
+SaveGame:
+STA $20
+.RetrySave:
+LDX $00A7
+LDY $00A9
+STX $99C9
+STY $99CB
+LDY !save_size
+JSL mult16
+STA $16
+CLC
+ADC #$6020
+STA $0E
+LDA #$0030
+STA $10
+LDA #$007E
+STA $14
+LDX #$0000
+.ChunkCheck:
+LDA.l save_chunk_pointer_table,X
+BEQ .EndofChunk
+STA $12
+LDA.l save_chunk_size_table,x
+JSL memcopy
+LDA.l save_chunk_size_table,x
+ADC $0E
+STA $0E
+INX
+INX
+BRA .ChunkCheck
+.EndofChunk:
+LDA $20
+JSR save_checksum_1
+STA $1A
+LDA $20
+JSR save_checksum_1
+CMP $1A
+BEQ .Valid
+JMP .RetrySave
+.Valid:
+LDX $16
+STA $30601C,X
+
+LDA $20
+JSR save_checksum_2
+STA $1A
+LDA $20
+JSR save_checksum_2
+CMP $1A
+BEQ .Valid2
+JMP .RetrySave
+.Valid2:
+LDX $16
+STA $30601E,X
+PLD
+RTS
+
+ORG $EF0A4D
+JSR save_game
+RTL
+NOP
+
+;;;;;;;;;;;;;;;;;;;;;
+ORG $EF0A72
+LoadGame:
+LDY !save_size
+JSL mult16
+CLC
+ADC #$6020
+STA $12
+LDA #$0030
+STA $14
+LDA #$007E
+STA $10
+LDX #$0000
+.ChunkCheck:
+LDA.l save_chunk_pointer_table,X
+BEQ .ChunkEnd
+STA $0E
+LDA.l save_chunk_size_table,X
+JSL memcopy
+LDA.l save_chunk_size_table,X
+ADC $12
+STA $12
+INX
+INX
+BRA .ChunkCheck
+.ChunkEnd:
+LDA $99C9
+STA $00A7
+LDA $99CB
+STA $00A9
+PLD
+RTL
+
+ORG $EF0BA6
+LDA !save_bytes
+
+ORG $EF0BB8
+CMP !save_bytes
+
+ORG $EF0BFA
+JSR erase_save
+RTL
+
+ORG $EF0C15
+JSR copy_save
+RTL
+;;;;;;;;;;;;;;;
+;expand event flags
+
+new_flag_table = $7EB620
+new_flag_pointer_0x80 = $7EB5A0
+flag_size = $01C0
+total_flag_bytes = $0240
+
+
+ORG $C21628
+GetEventFlag:
+JML RepelEnemies
+ExitRepelCheck:
+AND #$0007
+TAX
+LDA $C4562F,X
+PHA
+TYA
+LSR
+LSR
+LSR
+TAX
+PLA
+CPX.w #total_flag_bytes
+BMI .outofrange
+JMP .GetFlagReturnZero
+.outofrange:
+CPX #$0080
+BPL .Inrange
+AND $9C08,X
+BRA .loadflag
+.Inrange:
+AND.w new_flag_pointer_0x80,x
+.loadflag:
+AND #$00FF
+BEQ .GetFlagReturnZero
+.ForceFlagOn:
+LDA #$0001
+RTL
+.GetFlagReturnZero:
+LDA #$0000
+RTL
+NOP
+
+SetFlag:
+JML SetMelodyCount
+SetFlagProceed:
+LSR
+LSR
+CMP.w #total_flag_bytes
+BMI .outofrange
+PLA
+JMP $1659
+.outofrange:
+CMP #$0080
+CLC
+BPL .addflags
+ADC #$9C08
+BRA .label
+.addflags:
+ADC.w #new_flag_pointer_0x80
+.label
+TAY
+PLA
+PHX
+AND #$0007
+TAX
+LDA $C4562F,x
+AND #$00FF
+PLX
+BEQ .WriteFlag
+ORA $0000,Y
+BRA .Store
+.WriteFlag
+EOR #$FFFF
+AND $0000,Y
+.Store:
+STA $0000,y
+AND #$00FF
+RTL
+NOP
+NOP
+NOP
+NOP
+NOP
+NOP
+NOP
+NOP
+NOP
+NOP
+NOP
+NOP
+NOP
+RTL
+
+ORG $F20000
+;Escargo express intro text
+ExpressText:
+db $70, $84, $98, $91, $9e, $9b, $50, $a9, $9f, $a5, $50, $96, $9f, $a2, $50, $93
+db $91, $9c, $9c, $99, $9e, $97, $50, $75, $a3, $93, $91, $a2, $97, $9f, $50, $75
+db $a8, $a0, $a2, $95, $a3, $a3, $51, $03, $00, $70, $85, $9e, $96, $9f, $a2, $a4
+db $a5, $9e, $91, $a4, $95, $9c, $a9, $5c, $10, $04, $50, $9f, $a5, $a2, $50, $99
+db $a4, $95, $9d, $50, $a3, $a4, $9f, $a2, $91, $97, $95, $50, $a3, $95, $a2, $a6
+db $99, $93, $95, $a3, $50, $98, $91, $a6, $95, $50, $92, $95, $95, $9e, $50, $a3
+db $a5, $a3, $a0, $95, $9e, $94, $95, $94, $5e, $03, $00, $70, $79, $96, $50, $a9
+db $9f, $a5, $50, $9e, $95, $95, $94, $50, $a4, $9f, $50, $a3, $a4, $9f, $a2, $95
+db $50, $91, $9e, $50, $99, $a4, $95, $9d, $5c, $50, $a0, $9c, $95, $91, $a3, $95
+db $50, $98, $91, $9e, $97, $50, $a5, $a0, $50, $91, $9e, $94, $50, $a0, $a2, $95
+db $a3, $a3, $50, $a4, $98, $95, $50, $82, $50, $92, $a5, $a4, $a4, $9f, $9e, $5e
+db $03, $00
+
+escg_mainmen:
+db $70, $79, $96, $50, $a9, $9f, $a5, $50, $91, $a2, $95, $50, $93, $91, $9c, $9c
+db $99, $9e, $97, $50, $91, $92, $9f, $a5, $a4, $50, $9f, $a5, $a2, $50, $9f, $96
+db $96, $5d, $a7, $9f, $a2, $9c, $94, $50, $99, $a4, $95, $9d, $50, $94, $95, $9c
+db $99, $a6, $95, $a2, $a9, $50, $a3, $95, $a2, $a6, $99, $93, $95, $5c, $03, $00
+db $70, $a3, $a0, $95, $93, $99, $96, $a9, $50, $a9, $9f, $a5, $a2, $50, $a2, $95
+db $a1, $a5, $95, $a3, $a4, $50, $9e, $9f, $a7, $5e, $03, $00
+db $0A
+dl Gift_menu_Gift_menu_init
+db $19, $02
+db $77, $99, $96, $a4, $99, $9e, $97, $02
+db $19, $02
+db $83, $95, $a2, $a6, $95, $a2, $50, $a3, $a4, $9f, $a2, $91, $97, $95, $02
+db $1C, $07, $02, $11
+db $09, $02
+dd Gift_menu
+dd Server_Storage
+db $12
+db $0A
+dl $C75929
+Gift_menu:
+ db $12
+ db $08
+ dd escg_inquire
+ .Gift_menu_init:
+ db $01
+ db $19, $02
+ db $83, $95, $9E, $94, $50, $97, $99, $96, $A4, $02
+ db $19, $02
+ db $82, $95, $93, $95, $99, $a6, $95, $50, $97, $99, $96, $a4, $02
+ db $1C, $07, $02, $11
+ db $09, $02
+ dd .SendGiftText
+ dd .GetGiftText
+ db $12
+ db $01
+ db $0A
+ dl $C75929
+ .SendGiftText:
+ db $06, $14, $04
+ dd .GiftOutboxFull
+
+ ;Todo; write a way here to check whether or not our Gift Buffer is full
+ db $0E, $01
+ db $0D, $01
+ db $1B, $05
+ db $12
+ db $70, $87, $98, $9f, $50, $a7, $9f, $a5, $9c, $94, $50, $a9, $9f, $a5, $50, $9c
+ db $99, $9b, $95, $50, $a4, $9f, $50, $a3, $95, $9e, $94, $50, $91, $50, $97, $99
+ db $96, $a4, $50, $a4, $9f, $6f
+ db $1C, $18, $00
+ db $18, $03, $01, $01
+ db $1B, $06
+ db $1b, $02
+ dd Gift_menu
+ ;;;;;;;;;;;;;
+ .open_gift_selector:
+ db $70, $7f, $9b, $91, $a9, $5e, $10, $10, $50, $87, $98, $91, $a4, $50, $a7, $9f
+ db $a5, $9c, $94, $50, $a9, $9f, $a5, $50, $9c, $99, $9b, $95, $50, $a4, $9f, $50
+ db $a3, $95, $9e, $94, $6f, $03
+ .gift_item_picker:
+ db $08, $56, $e4, $c5, $ff
+ db $1B, $02
+ dd .SendGiftText ; The player pressed b
+ db $1B, $05
+ db $19, $19, $00, $00 ;Return the item name we selected
+ db $1B, $00 ;Store all current memory values
+ db $1C, $19, $01
+ db $70
+ db $10, $0A
+ db $1B, $02
+ dd .banned_item
+ .gift_okay:
+ db $1B, $01 ; Get the memory we used earlier
+
+ db $89, $9f, $a5, $57, $94, $50, $9c, $99, $9b, $95, $50, $a4, $9f, $50, $a3, $95
+ db $9e, $94, $50, $a4, $98, $95, $50, $1c, $05, $00, $6f
+ db $01
+ db $19, $02
+ db $89, $95, $A3, $02
+ db $19, $02
+ db $7E, $9F, $02
+ db $1C, $07, $02
+ db $11
+ db $12
+ db $09, $02
+ dd .send_gift_to_gift_buffer
+ dd .declined_to_send_gift
+ db $0A
+ dl .declined_to_send_gift
+ .send_gift_to_gift_buffer:
+ db $18, $04
+ db $1C, $28, $01
+ db $1F, $00, $01, $59
+ db $1f, $15, $87, $00, $99, $02, $01
+ db $10, $05
+ db $1F, $13, $FF, $03
+ db $10, $20
+ db $1F, $03
+ db $1C, $29, $01
+ db $18, $01, $01
+ db $1b, $06
+ db $1F, $02, $76
+ db $10, $15
+ db $1D, $0F, $00, $00
+ db $1B, $05
+ db $1C, $1A, $01
+ db $70
+ db $89, $9f, $a5, $a2, $50, $97, $99, $96, $a4, $50, $a0, $91, $93, $9b, $91, $97
+ db $95, $50, $98, $91, $a3, $50, $92, $95, $95, $9e, $50, $93, $9f, $9e, $96, $99
+ db $a2, $9d, $95, $94, $5e, $10, $10, $50, $79, $a4, $50, $a7, $99, $9c, $9c, $50
+ db $92, $95, $50, $a3, $95, $9e, $a4, $50, $a3, $98, $9f, $a2, $a4, $9c, $a9, $5e
+ db $03, $01, $70, $71, $9e, $50, $71, $a2, $93, $98, $99, $a0, $95, $9c, $91, $97
+ db $9f, $50, $a3, $95, $a2, $a6, $95, $a2, $50, $93, $9f, $9e, $9e, $95, $93, $a4
+ db $99, $9f, $9e, $50, $99, $a3, $50, $a2, $95, $a1, $a5, $99, $a2, $95, $94, $50
+ db $a4, $9f, $50, $a3, $95, $9e, $94, $50, $97, $99, $96, $a4, $a3, $5e, $03, $01
+ db $70, $79, $96, $50, $a9, $9f, $a5, $a2, $50, $97, $99, $96, $a4, $50, $99, $a3
+ db $50, $a5, $9e, $91, $92, $9c, $95, $50, $a4, $9f, $50, $92, $95, $50, $91, $93
+ db $93, $95, $a0, $a4, $95, $94, $5c, $50, $a9, $9f, $a5, $50, $a7, $99, $9c, $9c
+ db $50, $92, $95, $50, $91, $92, $9c, $95, $50, $a4, $9f, $50, $a2, $95, $a4, $a2
+ db $99, $95, $a6, $95, $50, $99, $a4, $50, $98, $95, $a2, $95, $5e, $03
+ db $01, $70
+ db $87, $9f, $a5, $9c, $94, $50, $a9, $9f, $a5, $50, $9c, $99, $9b, $95, $50, $a4
+ db $9f, $50, $a3, $95, $9e, $94, $50, $91, $9e, $9f, $a4, $98, $95, $a2, $50, $97
+ db $99, $96, $a4, $6f
+ db $01
+ db $19, $02
+ db $89, $95, $A3, $02
+ db $19, $02
+ db $7E, $9F, $02
+ db $1C, $07, $02
+ db $11
+ db $12
+ db $09, $02
+ dd .SendGiftText
+ dd escg_mainmen
+ db $0A
+ dl escg_mainmen
+
+ db $02
+ .declined_to_send_gift:
+ db $70, $10, $0, $84, $98, $95, $9e, $5c, $10, $05, $50, $a7, $9f, $a5, $9c, $94
+ db $50, $a9, $9f, $a5, $50, $9c, $99, $9b, $95, $50, $a4, $9f, $50, $a3, $95, $9e
+ db $94, $50, $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97, $50, $95, $9c, $a3, $95
+ db $6f
+ db $01
+ db $19, $02
+ db $89, $95, $A3, $02
+ db $19, $02
+ db $7E, $9F, $02
+ db $19, $02
+ db $72, $A9, $95, $02
+ db $1C, $07, $03
+ db $11
+ db $12
+ db $09, $03
+ dd .open_gift_selector
+ dd .parse_gift_okay
+ dd $C7DE3D
+ .parse_gift_okay:
+ db $70
+ db $0A
+ dl .gift_okay
+
+ .banned_item:
+ db $79, $57, $9d, $50, $a3, $9f, $a2, $a2, $a9, $5c, $10, $06, $50, $92, $a5, $a4
+ db $50, $a4, $98, $99, $a3, $50, $99, $a4, $95, $9d, $50, $93, $91, $9e, $9e, $9f
+ db $a4, $50, $a0, $9f, $a3, $a3, $99, $92, $9c, $a9, $50, $92, $95, $50, $97, $99
+ db $a6, $95, $9e, $50, $91, $a7, $91, $a9, $5e, $03, $00, $70, $80, $9c, $95, $91
+ db $a3, $95, $50, $93, $98, $9f, $9f, $a3, $95, $50, $a3, $9f, $9d, $95, $a4, $98
+ db $99, $9e, $97, $50, $95, $9c, $a3, $95, $5e, $03
+ db $0A
+ dl .gift_item_picker
+ .GetGiftText:
+ db $1C, $21, $12
+ db $12
+ db $1C, $1C, $01
+ db $1B, $06
+ db $1B, $02
+ dd .NoGiftsToReceive
+ db $01
+ db $1B, $00
+ db $0B, $01
+ db $1b, $03
+ dd .SingularGiftText
+ db $1B, $01
+ db $1B, $04
+ db $70
+ db $84, $98, $95, $a2, $95, $50, $91, $a2, $95, $50, $93, $a5, $a2, $a2, $95, $9e
+ db $a4, $9c, $a9, $50, $1c, $0a, $00, $00, $00, $00, $50, $97, $99, $96, $a4, $a3
+ db $50, $99, $9e, $50, $a9, $9f, $a5, $a2, $50, $99, $9e, $92, $9f, $a8, $5e, $03
+ db $01, $70, $87, $98, $91, $a4, $50, $a7, $9f, $a5, $9c, $94, $50, $a9, $9f, $a5
+ db $50, $9c, $99, $9b, $95, $6f
+ .GiftUnionizer:
+ db $03
+ db $1C, $1B, $01
+ db $1A, $07
+ db $1B, $05
+ db $18, $03, $0D
+ db $18, $00
+ db $18, $03, $01
+ db $1B, $06
+ db $1B, $02
+ dd .return_from_get
+ db $1B, $04
+
+ db $19, $1A, $00
+ db $1B, $05
+ .GetGiftChar:
+ db $0E, $00
+ db $1B, $04
+ db $70, $87, $9f, $a5, $9c, $94, $50, $a9, $9f, $a5, $50, $9c, $99, $9b, $95, $50
+ db $a4, $98, $95, $50, $1c, $05, $00, $6f
+ db $01
+ db $19, $02
+ db $7B, $95, $95, $A0, $02
+ db $19, $02
+ db $84, $9F, $A3, $A3, $02
+ db $19, 02
+ db $72, $91, $93, $9B, $02
+ db $1C, $07, $03
+ db $11
+ db $09, $03
+ dd .AcceptGift
+ dd .TossGift
+ dd .return_from_get
+ db $0A
+ dl .return_from_get
+
+ db $02
+
+ .GiftOutboxFull:
+ db $12
+ db $01
+ db $70, $79, $57, $9d, $50, $a3, $9f, $a2, $a2, $a9, $5c, $10, $04, $50, $92, $a5
+ db $a4, $50, $a9, $9f, $a5, $a2, $50, $97, $99, $96, $a4, $50, $a1, $a5, $95, $a5
+ db $95, $50, $99, $a3, $50, $96, $a5, $9c, $9c, $5e, $03, $01, $70, $80, $9c, $95
+ db $91, $a3, $95, $50, $93, $9f, $9e, $9e, $95, $93, $a4, $50, $a4, $9f, $50, $91
+ db $9e, $50, $71, $a2, $93, $98, $99, $a0, $95, $9c, $91, $97, $9f, $50, $a3, $95
+ db $a2, $a6, $95, $a2, $50, $a3, $9f, $50, $a9, $9f, $a5, $a2, $50, $97, $99, $96
+ db $a4, $a3, $50, $93, $91, $9e, $50, $92, $95, $50, $a0, $a2, $9f, $93, $95, $a3
+ db $a3, $95, $94, $5e, $03
+ db $01
+ db $0A
+ dl Gift_menu
+
+ .NoGiftsToReceive:
+ db $70, $79, $57, $9d, $50, $a3, $9f, $a2, $a2, $a9, $5c, $10, $05, $50, $92, $a5
+ db $a4, $50, $a9, $9f, $a5, $50, $94, $9f, $50, $9e, $9f, $a4, $50, $93, $a5, $a2
+ db $a2, $95, $9e, $a4, $9c, $a9, $50, $98, $91, $a6, $95, $50, $91, $9e, $a9, $50
+ db $97, $99, $96, $a4, $a3, $50, $99, $9e, $50, $a9, $9f, $a5, $a2, $50, $99, $9e
+ db $92, $9f, $a8, $5e, $03
+ db $01
+ db $0A
+ dl Gift_menu
+
+ .SingularGiftText:
+ db $1B, $01
+ db $70
+ db $84, $98, $95, $a2, $95, $50, $99, $a3, $50, $93, $a5, $a2, $a2, $95, $9e, $a4
+ db $9c, $a9, $50, $9f, $9e, $95, $50, $97, $99, $96, $a4, $50, $99, $9e, $50, $a9
+ db $9f, $a5, $a2, $50, $99, $9e, $92, $9f, $a8, $5e
+ db $0A
+ dl .GiftUnionizer
+
+ .TossGift:
+ db $12
+ db $1F, $02, $76
+ db $10, $10
+ db $70, $79, $a4, $57, $9c, $9c, $50, $92, $95, $50, $97, $9f, $9f, $94, $50, $91
+ db $a3, $50, $97, $9f, $9e, $95, $5e
+ db $1B, $06
+ db $19, $1C, $FF, $00
+ db $03, $01
+ db $0A
+ dl .return_from_get
+
+ .AcceptGift:
+ db $12
+ db $1D, $03, $FF
+ db $1B, $02
+ dd .InventoryFull
+ db $70, $87, $98, $9f, $50, $a3, $98, $9f, $a5, $9c, $94, $50, $a4, $91, $9b, $95
+ db $50, $a4, $98, $99, $a3, $6f, $03
+ db $08
+ dd $FFC5E52E
+ db $1B, $02
+ dd .ClearGetGiftChar
+ db $1B, $04
+ db $1D, $03, $00
+ db $1B, $02
+ dd .SelectedCharFull
+ db $18, $04
+ db $1C, $28, $01
+ db $1F, $00, $01, $59
+ db $1f, $15, $87, $00, $99, $02, $01
+ db $10, $05
+ db $1F, $13, $FF, $03
+ db $10, $20
+ db $1F, $03
+ db $01, $01, $01
+ db $1C, $29, $01
+ db $18, $01, $01
+ db $1F, $02, $74
+
+ db $1B, $04
+ db $0D, $01
+
+ db $1D, $13, $00, $00
+ db $70, $84, $91, $9b, $95, $50, $97, $9f, $9f, $94, $50, $93, $91, $a2, $95, $50
+ db $9f, $96, $50, $99, $a4, $5e, $03
+ db $01
+ db $0A
+ dl .return_from_get
+
+ .return_from_get:
+ db $12
+ db $1C, $1B, $01
+ db $0A
+ dl Gift_menu
+
+ .InventoryFull:
+ db $70, $79, $57, $9d, $50, $a3, $9f, $a2, $a2, $a9, $5c, $10, $05, $50, $92, $a5
+ db $a4, $50, $a9, $9f, $a5, $50, $93, $91, $9e, $9e, $9f, $a4, $50, $93, $91, $a2
+ db $a2, $a9, $50, $91, $9e, $a9, $50, $9d, $9f, $a2, $95, $50, $99, $a4, $95, $9d
+ db $a3, $5e, $03, $01
+ db $0A
+ dl .return_from_get
+
+ .SelectedCharFull:
+ db $70, $79, $57, $9d, $50, $a3, $9f, $a2, $a2, $a9, $5c, $10, $04, $50, $92, $a5
+ db $a4, $50, $a4, $98, $95, $a9, $50, $93, $91, $9e, $9e, $9f, $a4, $50, $93, $91
+ db $a2, $a2, $a9, $50, $91, $9e, $a9, $a4, $98, $99, $9e, $97, $50, $95, $9c, $a3
+ db $95, $5e, $03
+ db $01
+ db $1B, $06
+ db $0A
+ dl .GetGiftChar
+
+ .ClearGetGiftChar:
+ db $1B, $06
+ db $0A
+ dl .GetGiftChar
+
+
+Server_Storage:
+db $02
+
+escg_inquire:
+db $70, $87, $98, $91, $a4, $50, $a7, $9f, $a5, $9c, $94, $50, $a9, $9f, $a5, $50
+db $9c, $99, $9b, $95, $50, $a4, $9f, $50, $94, $9f, $6f, $02
+;;;;;;;;;;;;;;;;;;;;;;
+ORG $F30000
+CabinTextFixNew:
+db $95, $50, $a9, $9f
+db $a5, $50, $a4, $9f, $50, $98, $91, $a6, $95, $50, $99, $a4, $5e, $03, $00, $1f
+db $02, $74, $10, $20, $1b, $04, $70, $58, $1c, $02, $00, $50, $97, $9f
+CabinTextJump:
+db $04, $56
+db $00, $04, $2D, $00
+CabinPostItemRescue:
+db $04, $EC, $03, $04, $ED, $03, $18, $04, $0a, $a4, $96, $c7
+
+StarMasterText:
+db $06, $e5, $03
+dd .GotCheck
+db $70, $71, $a4, $50, $9c, $91, $a3, $a4, $5c
+db $10, $0a, $50, $a9, $9f, $a5, $57, $a6, $95, $50, $91, $a2, $a2, $99, $a6, $95
+db $94, $5e, $10, $0f, $50, $79, $50, $9e, $9f, $a7, $50, $9f, $96, $96, $95, $a2
+db $50, $a9, $9f, $a5, $50, $9d, $a9, $50, $a5, $9c, $a4, $99, $9d, $91, $a4, $95
+db $50, $9b, $9e, $9f, $a7, $9c, $95, $94, $97, $95, $5e, $03, $04, $e5, $03, $0a
+db $c7, $b8, $ee
+.GotCheck:
+db $70, $74, $9f, $50, $a9, $9f, $a5, $50, $9b, $9e, $9f, $a7, $50
+db $98, $9f, $a7, $50, $a4, $9f, $50, $97, $95, $a4, $50, $9f, $a5, $a4, $50, $9f
+db $96, $50, $98, $95, $a2, $95, $6f, $13, $02
+
+TeddyJoin:
+db $70, $89, $9f, $a5, $50, $93, $91, $9e, $50, $93, $91, $9c, $9c, $50, $9d, $95
+db $50, $84, $95, $94, $94, $a9, $5e, $03, $00, $70, $84, $95, $94, $94, $a9, $50
+db $72, $95, $91, $a2, $5e, $03, $00, $70, $79, $a4, $50, $99, $a3, $50, $9d, $a9
+db $50, $94, $95, $a3, $a4, $99, $9e, $a9, $50, $a4, $9f, $50, $a3, $91, $a6, $95
+db $50, $a4, $98, $95, $50, $a7, $9f, $a2, $9c, $94, $5c, $10, $20, $50, $91, $9e
+db $94, $50, $79, $50, $a7, $99, $9c, $9c, $50, $91, $9c, $9c, $9f, $a7, $50, $a9
+db $9f, $a5, $50, $a4, $9f, $50, $92, $95, $50, $9d, $a9, $50, $95, $a3, $93, $9f
+db $a2, $a4, $51, $03, $1d, $03, $ff, $1b, $03, $6A, $86, $c8, $00, $00, $70, $87
+db $95, $9c, $9c, $5c, $50, $79, $50, $a7, $9f, $a5, $9c, $94, $5c, $10, $15, $50
+db $95, $a8, $93, $95, $a0, $a4, $50, $a9, $9f, $a5, $57, $a2, $95, $50, $93, $91
+db $a2, $a2, $a9, $99, $9e, $97, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $50
+db $a3, $a4, $a5, $96, $96, $5e, $07, $68, $00, $13, $02, $a9, $5c, $15, $25, $9e, $51, $03, $18
+db $04, $10, $01, $1f, $11, $03, $1f, $eb, $99, $9e, $97, $50, $a4, $9f, $9f, $50
+db $9d, $a5, $93, $98, $50, $a3, $a4, $a5, $96, $96, $5e, $13, $02, $00, $50, $50
+db $58, $84, $98, $95, $50, $1c, $05, $02, $50, $9a, $9f, $99, $9e, $a3, $50, $a9
+db $9f, $a5, $5e, $59, $1f, $02, $10, $10, $30, $1d, $00, $ff, $02, $13, $1f, $41
+db $13, $02; TeddyBear join
+
+PresentNessCheck:
+db $0B, $05
+db $1B, $03
+dd NessFlagText
+db $02
+
+WarpPadDescription:
+db $71, $50, $98, $91, $9e, $94, $98, $95, $9c, $94, $50, $a4, $95, $9c, $95, $a0
+db $9f, $a2, $a4, $95, $a2, $50, $92, $a5, $99, $9c, $a4, $50, $92, $a9, $50, $a4
+db $98, $95, $50, $97, $95, $9e, $99, $a5, $a3, $5c, $50, $1c, $02, $03, $5e, $03
+db $00, $70, $89, $9f, $a5, $50, $93, $91, $9e, $50, $a5, $a3, $95, $50, $99, $a4
+db $50, $a4, $9f, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $50, $a4, $9f, $50
+db $9b, $9e, $9f, $a7, $9e, $50, $9c, $9f, $93, $91, $a4, $99, $9f, $9e, $a3, $5e
+db $13, $02
+
+WarpPadUsage:
+db $1C, $22, $01
+db $1B, $06
+db $1B, $03
+dd .CantWarp
+db $06, $F2, $02
+dd $C7C865
+db $70
+db $83, $75, $7c, $75, $73, $84, $50, $87, $71, $82, $80, $50, $7d, $7f, $74, $75
+db $01
+db $19, $02
+db $71, $02
+db $19, $02
+db $72, $02
+db $1C, $07, $02
+db $11
+db $12
+db $09, $02
+dd .WarpAccept
+dd .WarpAccept
+db $02
+.WarpAccept:
+db $70, $79, $7e, $80, $85, $84, $50, $89, $7f, $85, $82, $50, $74, $75, $83, $84
+db $79, $7e, $71, $84, $79, $7f, $7e, $10, $06
+db $1B, $04
+db $1A, $0B
+db $1F, $20, $00, $00, $02
+
+.CantWarp:
+db $70, $84, $98, $95, $50, $a3, $99, $97, $9e, $91, $9c, $50, $99, $a3, $50, $9a
+db $91, $9d, $9d, $95, $94, $5c, $10, $05, $50, $a3, $9f, $50, $a4, $98, $95, $50
+db $1c, $05, $b5, $50, $93, $91, $9e, $9e, $9f, $a4, $50, $92, $95, $50, $a5, $a3
+db $95, $94, $50, $98, $95, $a2, $95, $5e, $13, $02
+
+
+PokeyCharCount:
+db $08
+dd .CountChars
+db $0A
+dl $C8FD5F
+
+.CountChars:
+db $19, $20
+db $09, $04
+dd .One
+dd .Two
+dd .Three
+dd .Four
+.One:
+db $02
+.Two:
+db $A4, $A7, $9F, $50, $02
+.Three:
+db $A4, $98, $A2, $95, $95, $50, $02
+.Four:
+db $96, $9F, $A5, $A2, $50, $02
+
+DisplayRobots:
+db $18, $04
+db $07, $17, $04
+db $1B, $02
+dd .NoNess
+db $04, $18, $04
+.NoNess:
+db $07, $E1, $03
+db $1B, $02
+dd .NoPaula
+db $04, $19, $04
+
+.NoPaula:
+db $07, $E2, $03
+db $1B, $02
+dd .NoJeff
+db $04, $1A, $04
+.NoJeff:
+db $07, $10, $00
+db $1B, $02
+dd .NoPoo
+db $04, $1B, $04
+.NoPoo:
+db $02
+
+OrangeKidCrowdfund:
+db $01, $70
+db $84, $98, $91, $a4, $57, $a3, $50, $a7, $98, $a9, $50, $79, $50, $94, $95, $93
+db $99, $94, $95, $94, $50, $a4, $9f, $50, $9f, $a0, $95, $9e, $50, $91, $50, $93
+db $a2, $9f, $a7, $94, $96, $a5, $9e, $94, $99, $9e, $97, $50, $93, $91, $9d, $a0
+db $91, $99, $97, $9e, $50, $96, $9f, $a2, $50, $9d, $a9, $50, $99, $9e, $a6, $95
+db $9e, $a4, $99, $9f, $9e, $a3, $5e, $03, $00, $70, $79, $96, $50, $a9, $9f, $a5
+db $50, $a3, $a0, $95, $9e, $94, $50, $54, $62, $60, $60, $50, $a2, $99, $97, $98
+db $a4, $50, $9e, $9f, $a7, $5c, $10, $05, $a9, $9f, $a5, $57, $9c, $9c, $50, $9a
+db $9f, $99, $9e, $50, $a4, $98, $95, $50, $7f, $a2, $91, $9e, $97, $95, $50, $7a
+db $a5, $99, $93, $95, $50, $a4, $99, $95, $a2, $03, $00, $70, $91, $9e, $94, $50
+db $97, $95, $a4, $50, $95, $a8, $93, $9c, $a5, $a3, $99, $a6, $95, $50, $95, $91
+db $a2, $9c, $a9, $50, $91, $93, $93, $95, $a3, $a3, $50, $a4, $9f, $50, $9d, $a9
+db $50, $9c, $91, $a4, $95, $a3, $a4, $50, $99, $9e, $a6, $95, $9e, $a4, $99, $9f
+db $9e, $5c, $50, $a4, $98, $95, $50, $1c, $05, $01, $5e, $03, $00, $70, $87, $98
+db $91, $a4, $50, $94, $9f, $50, $a9, $9f, $a5, $50, $a3, $91, $a9, $6f
+db $0A
+dl $C812AE
+
+NPCPaulaTex:
+db $1C, $04
+db $1f, $00, $00, $7b
+db $0A
+dl NpcPaulaReturn
+NPCJeffTex:
+db $1C, $04
+db $1f, $00, $00, $7b
+db $0A
+dl NpcJeffReturn
+NPCPooTex:
+db $1C, $04
+db $1f, $00, $00, $7b
+db $0A
+dl NpcPooReturn
+PooNPCTexReal:
+db $1C, $04
+db $1f, $00, $00, $7b
+db $0A
+dl PooReturnReal
+
+TracyNessText:
+db $06, $17, $04
+dd $C76120
+db $70, $87, $98, $a9, $50, $91, $a2, $95, $50, $a9, $9f, $a5, $50, $99, $9e, $50
+db $9d, $a9, $50, $a2, $9f, $9f, $9d, $6f, $03, $00, $70, $7d, $91, $a9, $92, $95
+db $50, $99, $96, $50, $79, $50, $9b, $9e, $95, $a7, $50, $1c, $02, $01, $50, $a7
+db $91, $a3, $50, $9f, $9b, $91, $a9, $5c, $10, $05, $50, $79, $57, $94, $50, $97
+db $99, $a6, $95, $50, $a9, $9f, $a5, $50, $9d, $a9, $50, $1c, $05, $01, $5e, $03
+db $00, $70, $7f, $98, $5c, $50, $a7, $95, $9c, $9c, $5e, $03, $00, $70, $79, $96
+db $50, $a9, $9f, $a5, $50, $a7, $91, $9e, $9e, $91, $50, $93, $a2, $91, $9d, $50
+db $a3, $9f, $9d, $95, $a4, $98, $99, $9e, $97, $50, $99, $9e, $50, $a9, $9f, $a5
+db $a2, $50, $a0, $9f, $93, $9b, $95, $a4, $a3, $5c, $10, $04, $50, $9a, $a5, $a3
+db $a4, $50, $a5, $a3, $95, $50, $a4, $98, $95, $50, $82, $50, $92, $a5, $a4, $a4
+db $9f, $9e, $5e, $13, $02
+
+MagicantTentacleText:
+db $06, $17, $04
+dd $C77D9F
+db $70, $58, $71, $50, $a6, $9f, $99, $93, $95, $50, $93, $a2, $95, $95, $a0, $a3
+db $50, $99, $9e, $a4, $9f, $50
+db $0A
+dl MagicantTentaGetNessName
+db $02, $00
+MagicantNameReturn:
+db $57, $a3, $50, $9d
+db $99, $9e, $94, $5e, $5e, $5e, $03, $00, $70, $79, $a4, $50, $a3, $91, $a9, $a3
+db $5c, $10, $08, $50, $6c, $1c, $02, $01, $50, $9d, $a5, $a3, $a4, $50, $92, $95
+db $50, $a0, $a2, $95, $a3, $95, $9e, $a4, $50, $a4, $9f, $50, $a0, $a2, $9f, $93
+db $95, $95, $94, $50, $92, $95, $a9, $9f, $9e, $94, $50, $a4, $98, $99, $a3, $50
+db $a0, $9f, $99, $9e, $a4, $5e, $5e, $5e, $6e, $59, $13, $02
+
+DuckSignTex:
+db $08
+dd $C87994
+db $6E, $01
+db $08
+dd $C87994
+db $03
+db $0A
+dl $C8798B
+
+GoatSignTex:
+db $08
+dd $C87994
+db $6E, $01
+db $08
+dd $C87994
+db $03
+db $0A
+dl $C879BB
+
+SlimeSignTex:
+db $08
+dd $C87994
+db $6E, $01
+db $08
+dd $C87994
+db $03
+db $0A
+dl $C87A46
+
+JeffBoatText:
+db $19, $20
+db $0B, $01
+db $1B, $03
+dd JeffBoatSolo
+db $70, $83, $9f, $5e
+db $0A
+dl NotSoloJeff
+JeffBoatSolo:
+db $70
+db $83, $9f, $5c, $50, $a9, $9f, $a5, $57, $a2, $95, $50, $a0, $a2, $95, $a4, $a4
+db $a9, $50, $a3, $9d, $91, $a2, $a4, $5c, $50, $95, $98, $6f, $03, $00, $70, $84
+db $98, $99, $a3, $50, $92, $9f, $91, $a4, $57, $a3, $50, $91, $50, $9c, $99, $a4
+db $a4, $9c, $95, $50, $a2, $99, $93, $9b, $95, $a4, $a9, $5c, $50, $9d, $91, $a9
+db $92, $95, $50, $a9, $9f, $a5, $50, $93, $9f, $a5, $9c, $94, $50, $97, $99, $a6
+db $95, $50, $99, $a4, $50, $91, $9e, $50, $a5, $a0, $97, $a2, $91, $94, $95, $51
+db $03, $00
+db $0A
+dl $C92F88
+
+PooBoatText:
+db $19, $20
+db $0B, $01
+db $1B, $03
+dd PooBoatSolo
+db $70, $83, $17, $8b
+db $0A
+dl NotSoloPoo
+PooBoatSolo:
+db $70, $78, $95, $a9, $5c, $50, $a7, $98, $91, $a4, $57, $a3, $50, $a7, $99, $a4
+db $98, $50, $a4, $98, $95, $50, $a3, $95, $a2, $99, $9f, $a5, $a3, $50, $96, $91
+db $93, $95, $6f, $03, $00, $70, $89, $9f, $a5, $50, $97, $9f, $a4, $a4, $91, $50
+db $9c, $99, $97, $98, $a4, $95, $9e, $50, $a5, $a0, $50, $91, $50, $9c, $99, $a4
+db $a4, $9c, $95, $51, $03, $00, $70, $71, $50, $9c, $9f, $9f, $9b, $50, $9c, $99
+db $9b, $95, $50, $a4, $98, $91, $a4, $50, $93, $9f, $a5, $9c, $94, $50, $a3, $93
+db $91, $a2, $95, $50, $91, $50, $a3, $95, $91, $50, $9d, $9f, $9e, $a3, $a4, $95
+db $a2, $51
+db $03, $00
+db $0A
+dl $C92F88
+
+PaulaBoatText:
+db $19, $20
+db $0B, 01
+db $1B, $03
+dd PaulaBoatSolo
+db $70, $83, $9f, $5e
+db $0A
+dl NotSoloPaula
+PaulaBoatSolo:
+db $70, $87, $98, $91, $a4, $57, $a3, $50, $a4, $98, $91, $a4, $50, $93, $a2, $91
+db $93, $9b, $9c, $99, $9e, $97, $50, $95, $9e, $95, $a2, $97, $a9, $50, $91, $a4
+db $50, $a9, $9f, $a5, $a2, $50, $96, $99, $9e, $97, $95, $a2, $a4, $99, $a0, $a3
+db $6f, $03, $00, $70, $89, $9f, $a5, $50, $a3, $95, $95, $9d, $50, $91, $9c, $9d
+db $9f, $a3, $a4, $50, $a2, $95, $91, $94, $a9, $50, $96, $9f, $a2, $50, $91, $9e
+db $a9, $a4, $98, $99, $9e, $97, $51, $03, $00, $70, $7d, $91, $a9, $92, $95, $50
+db $a4, $98, $95, $50, $9d, $9f, $9e, $a3, $a4, $95, $a2, $a3, $50, $a3, $98, $9f
+db $a5, $9c, $94, $50, $92, $95, $50, $a2, $95, $91, $94, $a9, $50, $96, $9f, $a2
+db $50, $89, $7f, $85, $51
+db $03, $00
+db $0A
+dl $C92F88
+
+NessBarfAppear:
+db $70, $1c, $02, $01, $50, $a4, $95, $9c, $95, $a0, $9f, $a2, $a4, $95, $94, $50
+db $99, $9e, $51, $03, $02
+
+NessBarfAttack:
+db $70
+db $1c, $02, $01, $50, $a4, $a2, $99, $95, $94, $50, $80, $83, $79, $50, $82, $9f
+db $93, $9b, $99, $9e, $51
+db $03, $00
+db $70, $1F, $02, $37
+db $1C, $02, $01
+db $50, $A4, $A2, $99, $95, $94, $50
+db $1C, $12, $01, $51
+db $08
+dd $EF864C
+db $02
+
+GiantStepNessText:
+db $08
+dd NessSancDiscover
+db $0A
+dl $C7BF36
+
+LilliputNessText:
+db $08
+dd NessSancDiscover
+db $0A
+dl $C7C000
+
+MilkyNessText:
+db $08
+dd NessSancDiscover
+db $0A
+dl $C7C193
+
+RainyNessText:
+db $08
+dd NessSancDiscover
+db $0A
+dl $C7C0D8
+
+MagnetNessText:
+db $1C, $21, $01
+db $08
+dd NessSancDiscover
+db $0A
+dl $C7C270
+
+CloudNessText:
+db $08
+dd NessSancDiscover
+db $0A
+dl $C7C32F
+
+LumineNessText:
+db $08
+dd NessSancDiscover
+db $0A
+dl $C7C42A
+
+FireNessText:
+db $08
+dd NessSancDiscover
+db $0A
+dl $C7C4E1
+
+NessSancDiscover:
+db $70
+db $06, $17, $04
+dd .NessPresent
+db $83, $9F, $9D, $95, $A7, $98, $95, $A2, $95, $5C, $50, $10, $0A
+.NessPresent:
+db $1c, $02, $01
+db $02
+
+GiantStepNessName:
+db $08
+dd PrintLeader
+db $0A
+dl $C7BF60
+
+LilliputNessName:
+db $08
+dd PrintLeader
+db $0A
+dl $C7C02B
+
+MilkyNessName:
+db $08
+dd PrintLeader
+db $0A
+dl $C7C1CE
+
+RainyNessName:
+db $08
+dd PrintLeader
+db $0A
+dl $C7C0FD
+
+MagnetNessName:
+db $08
+dd PrintLeader
+db $0A
+dl $C7C299
+
+CloudNessName:
+db $08
+dd PrintLeader
+db $0A
+dl $C7C360
+
+LumineNessName:
+db $08
+dd PrintLeader
+db $0A
+dl $C7C44F
+
+FireNessName:
+db $08
+dd PrintLeader
+db $0A
+dl $C7C511
+
+EarthPowerLeader:
+db $08
+dd PrintLeader
+db $0A
+dl $EE9BE2
+
+OnettLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl OnettLeaderDone
+
+TwosonLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl TwosonLeaderDone
+
+HappyLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl HappyLeaderDone
+
+ThreedLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl ThreedLeaderDone
+
+SaturnLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl SaturnLeaderDone
+
+DunesLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl DunesLeaderDone
+
+FoursLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl FoursLeaderDone
+
+WintersLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl WintersLeaderDone
+
+SummersLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl SummersLeaderDone
+
+ScarabaLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl ScarabaLeaderDone
+
+DalaamLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl DalaamLeaderDone
+
+DarkLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl DarkLeaderDone
+
+TendaLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl TendaLeaderDone
+
+UnderLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl UnderLeaderDone
+
+MagicLeaderName:
+db $08
+dd PrintLeader2
+db $0A
+dl MagicLeaderDone
+
+OnettTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl OnettTeleNPCBack
+
+TwosonTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl TwosonTeleNPCBack
+
+HappyTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl HappyTeleNPCBack
+
+ThreedTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl ThreedTeleNPCBack
+
+SaturnTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl SaturnTeleNPCBack
+
+DunesTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl DunesTeleNPCBack
+
+FoursTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl FoursTeleNPCBack
+
+WintersTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl WintersTeleNPCBack
+
+SummersTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl SummersTeleNPCBack
+
+ScarabaTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl ScarabaTeleNPCBack
+
+DalaamTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl DalaamTeleNPCBack
+
+DarkTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl DarkTeleNPCBack
+
+TendaTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl TendaTeleNPCBack
+
+UnderTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl UnderTeleNPCBack
+
+MagicTeleNPCName:
+db $08
+dd PrintLeader2
+db $0A
+dl MagicTeleNPCBack
+
+PrintLeader:
+db $70
+db $19, $10, $01
+db $1B, $04
+db $1C, $02, $00
+db $02
+
+PrintLeader2:
+db $70
+db $58
+db $19, $10, $01
+db $1B, $04
+db $1C, $02, $00
+db $02
+
+DebugPalettePicker:
+db $18, $04, $1f, $e1, $00, $5c, $01, $02
+
+ClearAndCallDad:
+db $12
+db $0A
+dl $C63229
+
+fixed_skyrunner_text:
+db $04, $D4, $00
+db $10, $20, $1f, $02, $76, $10, $03, $18, $01, $01, $19, $10, $01, $1b, $04, $70
+db $1c, $02, $00, $50, $93, $a2, $91, $9d, $9d, $95, $94, $50, $a4, $98, $95, $50
+db $95, $9e, $97, $99, $9e, $95, $50, $99, $9e, $a4, $9f, $50, $a0, $9c, $91, $93
+db $95, $51, $03
+db $18, $04
+db $08
+dd $C91D30
+db $0A
+dl $C91E42
+
+broken_skyrunner_text:
+db $18, $01, $01
+db $70, $84, $98, $95, $50, $9d, $91, $93, $98, $99, $9e, $95, $50, $91, $a0, $a0
+db $95, $91, $a2, $a3, $50, $a4, $9f, $50, $92, $95, $50, $92, $a2, $9f, $9b, $95
+db $9e, $5e, $03, $00, $70, $80, $95, $a2, $98, $91, $a0, $a3, $50, $91, $50, $a2
+db $95, $a0, $9c, $91, $93, $95, $9d, $95, $9e, $a4, $50, $95, $9e, $97, $99, $9e
+db $95, $50, $93, $9f, $a5, $9c, $94, $50, $92, $95, $50, $96, $9f, $a5, $9e, $94
+db $5e, $10, $10, $5e, $10, $10, $5e, $10, $10, $6f, $1f, $02, $72, $13, $02
+db $02
+
+rock_text:
+db $79, $57, $9d, $50, $9e, $9f, $a4, $50, $91, $50, $a2, $9f, $93, $9b, $5e
+db $10, $10, $50, $79, $57, $9d, $50, $91, $93, $a4, $a5, $91, $9c, $9c, $a9, $50
+db $9a, $a5, $a3, $a4, $50, $91, $50, $a2, $9f, $93, $9b, $5d, $a3, $98, $91, $a0
+db $95, $94, $50, $9d, $91, $9e, $5e
+db $0A
+dl $61002C
+
+man_text:
+db $58, $7f, $9e, $50, $93, $9c, $9f, $a3, $95, $a2, $50, $99, $9e, $a3, $a0, $95
+db $93, $a4, $99, $9f, $9e, $5c, $10, $0a, $50, $99, $a4, $50, $91, $93, $a4, $a5
+db $91, $9c, $9c, $a9, $50, $91, $a0, $a0, $95, $91, $a2, $a3, $50, $a4, $9f, $50
+db $92, $95, $50, $91, $50, $9d, $91, $9e, $5d, $a3, $98, $91, $a0, $95, $94, $50
+db $a2, $9f, $93, $9b, $5e, $59
+db $0A
+dl $6100F8
+
+NessTonyHint:
+db $5E, $5E, $5E, $03
+db $08
+dd TonyLetterHintText
+db $02
+
+JeffTonyHint:
+db $a2, $95, $5e, $03
+db $08
+dd TonyLetterHintText
+db $02
+
+TonyLetterHintText:
+db $00, $70, $78, $9f, $a7, $95, $a6, $95, $a2, $5c, $50, $79, $50, $10, $06, $93
+db $9f, $a5, $9c, $94, $10, $06, $50, $a0, $a2, $9f, $92, $91, $92, $9c, $a9, $50
+db $9c, $95, $a4, $50, $a9, $9f, $a5, $50, $99, $9e, $5c, $03, $00, $70, $99, $96
+db $50, $a9, $9f, $a5, $50, $98, $91, $94, $50, $a3, $9f, $9d, $95, $a4, $98, $99
+db $9e, $97, $50, $99, $9d, $a0, $9f, $a2, $a4, $91, $9e, $a4, $50, $a4, $9f, $50
+db $94, $a2, $9f, $a0, $50, $9f, $96, $96, $5c, $50, $9c, $99, $9b, $95, $50, $91
+db $50, $1c, $05, $a7, $5e, $5e, $5e, $1f, $02, $72, $13, $02
+
+DynamicPhotoSetter:
+db $18, $04
+db $06
+db $BA, $02
+dd .CheckPhoto2
+db $0A
+dl .SetPhoto1
+
+.CheckPhoto2:
+db $06
+db $BB, $02
+dd .CheckPhoto3
+db $0A
+dl .SetPhoto2
+
+.CheckPhoto3:
+db $06
+db $BC, $02
+dd .CheckPhoto4
+db $0A
+dl .SetPhoto3
+
+.CheckPhoto4:
+db $06
+db $BD, $02
+dd .CheckPhoto5
+db $0A
+dl .SetPhoto4
+
+.CheckPhoto5:
+db $06
+db $BE, $02
+dd .CheckPhoto6
+db $0A
+dl .SetPhoto5
+
+.CheckPhoto6:
+db $06
+db $BF, $02
+dd .CheckPhoto7
+db $0A
+dl .SetPhoto6
+
+.CheckPhoto7:
+db $06
+db $C0, $02
+dd .CheckPhoto8
+db $0A
+dl .SetPhoto7
+
+.CheckPhoto8:
+db $06
+db $C1, $02
+dd .CheckPhoto9
+db $0A
+dl .SetPhoto8
+
+.CheckPhoto9:
+db $06
+db $C2, $02
+dd .CheckPhoto10
+db $0A
+dl .SetPhoto9
+
+.CheckPhoto10:
+db $06
+db $C3, $02
+dd .CheckPhoto11
+db $0A
+dl .SetPhoto10
+
+.CheckPhoto11:
+db $06
+db $C4, $02
+dd .CheckPhoto12
+db $0A
+dl .SetPhoto11
+
+.CheckPhoto12:
+db $06
+db $C5, $02
+dd .CheckPhoto13
+db $0A
+dl .SetPhoto12
+
+.CheckPhoto13:
+db $06
+db $C6, $02
+dd .CheckPhoto14
+db $0A
+dl .SetPhoto13
+
+.CheckPhoto14:
+db $06
+db $C7, $02
+dd .CheckPhoto15
+db $0A
+dl .SetPhoto14
+
+.CheckPhoto15:
+db $06
+db $C8, $02
+dd .CheckPhoto16
+db $0A
+dl .SetPhoto15
+
+.CheckPhoto16:
+db $06
+db $C9, $02
+dd .CheckPhoto17
+db $0A
+dl .SetPhoto16
+
+.CheckPhoto17:
+db $06
+db $CA, $02
+dd .CheckPhoto18
+db $0A
+dl .SetPhoto17
+
+.CheckPhoto18:
+db $06
+db $CB, $02
+dd .CheckPhoto19
+db $0A
+dl .SetPhoto18
+
+.CheckPhoto19:
+db $06
+db $CC, $02
+dd .CheckPhoto20
+db $0A
+dl .SetPhoto19
+
+.CheckPhoto20:
+db $06
+db $CD, $02
+dd .CheckPhoto21
+db $0A
+dl .SetPhoto20
+
+.CheckPhoto21:
+db $06
+db $CE, $02
+dd .CheckPhoto22
+db $0A
+dl .SetPhoto21
+
+.CheckPhoto22:
+db $06
+db $CF, $02
+dd .CheckPhoto23
+db $0A
+dl .SetPhoto22
+
+.CheckPhoto23;
+db $06
+db $D0, $02
+dd .CheckPhoto24
+db $0A
+dl .SetPhoto23
+
+.CheckPhoto24:
+db $06
+db $D1, $02
+dd .CheckPhoto25
+db $0A
+dl .SetPhoto24
+
+.CheckPhoto25:
+db $06
+db $D2, $02
+dd .CheckPhoto26
+db $0A
+dl .SetPhoto25
+
+.CheckPhoto26:
+db $06
+db $D3, $02
+dd .CheckPhoto27
+db $0A
+dl .SetPhoto26
+
+.CheckPhoto27:
+db $06
+db $D4, $02
+dd .CheckPhoto28
+db $0A
+dl .SetPhoto27
+
+.CheckPhoto28:
+db $06
+db $D5, $02
+dd .CheckPhoto29
+db $0A
+dl .SetPhoto28
+
+.CheckPhoto29:
+db $06
+db $D6, $02
+dd .CheckPhoto30
+db $0A
+dl .SetPhoto29
+
+.CheckPhoto30:
+db $06
+db $D7, $02
+dd .CheckPhoto31
+db $0A
+dl .SetPhoto30
+
+.CheckPhoto31:
+db $06
+db $D8, $02
+dd .CheckPhoto32
+db $0A
+dl .SetPhoto31
+
+.CheckPhoto32:
+db $06
+db $D9, $02
+dd .DoneScript
+db $0A
+dl .SetPhoto32
+.DoneScript:
+;script here
+
+db $02
+.SetPhoto1:
+db $1F, $D2, $01, $1C, $23
+db $04, $BA, $02
+db $02
+.SetPhoto2:
+db $1F, $D2, $02, $1C, $23
+db $04, $BB, $02
+db $02
+.SetPhoto3:
+db $1F, $D2, $03, $1C, $23
+db $04, $BC, $02
+db $02
+.SetPhoto4:
+db $1F, $D2, $04, $1C, $23
+db $04, $BD, $02
+db $02
+.SetPhoto5:
+db $1F, $D2, $05, $1C, $23
+db $04, $BE, $02
+db $02
+.SetPhoto6:
+db $1F, $D2, $06, $1C, $23
+db $04, $BF, $02
+db $02
+.SetPhoto7:
+db $1F, $D2, $07, $1C, $23
+db $04, $C0, $02
+db $02
+.SetPhoto8:
+db $1F, $D2, $08, $1C, $23
+db $04, $C1, $02
+db $02
+.SetPhoto9:
+db $1F, $D2, $09, $1C, $23
+db $04, $C2, $02
+db $02
+.SetPhoto10:
+db $1F, $D2, $0A, $1C, $23
+db $04, $C3, $02
+db $02
+.SetPhoto11:
+db $1F, $D2, $0B, $1C, $23
+db $04, $C4, $02
+db $02
+.SetPhoto12:
+db $1F, $D2, $0C, $1C, $23
+db $04, $C5, $02
+db $02
+.SetPhoto13:
+db $1F, $D2, $0D, $1C, $23
+db $04, $C6, $02
+db $02
+.SetPhoto14:
+db $1F, $D2, $0E, $1C, $23
+db $04, $C7, $02
+db $02
+.SetPhoto15:
+db $1F, $D2, $0F, $1C, $23
+db $04, $C8, $02
+db $02
+.SetPhoto16:
+db $1F, $D2, $10, $1C, $23
+db $04, $C9, $02
+db $02
+.SetPhoto17:
+db $1F, $D2, $11, $1C, $23
+db $04, $CA, $02
+db $02
+.SetPhoto18:
+db $1F, $D2, $12, $1C, $23
+db $04, $CB, $02
+db $02
+.SetPhoto19:
+db $1F, $D2, $13, $1C, $23
+db $04, $CC, $02
+db $02
+.SetPhoto20:
+db $1F, $D2, $14, $1C, $23
+db $04, $CD, $02
+db $02
+.SetPhoto21:
+db $1F, $D2, $15, $1C, $23
+db $04, $CE, $02
+db $02
+.SetPhoto22:
+db $1F, $D2, $16, $1C, $23
+db $04, $CF, $02
+db $02
+.SetPhoto23:
+db $1F, $D2, $17, $1C, $23
+db $04, $D0, $02
+db $02
+.SetPhoto24:
+db $1F, $D2, $18, $1C, $23
+db $04, $D1, $02
+db $02
+.SetPhoto25:
+db $1F, $D2, $19, $1C, $23
+db $04, $D2, $02
+db $02
+.SetPhoto26:
+db $1F, $D2, $1A, $1C, $23
+db $04, $D3, $02
+db $02
+.SetPhoto27:
+db $1F, $D2, $1B, $1C, $23
+db $04, $D4, $02
+db $02
+.SetPhoto28:
+db $1F, $D2, $1C, $1C, $23
+db $04, $D5, $02
+db $02
+.SetPhoto29:
+db $1F, $D2, $1D, $1C, $23
+db $04, $D6, $02
+db $02
+.SetPhoto30:
+db $1F, $D2, $1E, $1C, $23
+db $04, $D7, $02
+db $02
+.SetPhoto31:
+db $1F, $D2, $1F, $1C, $23
+db $04, $D8, $02
+db $02
+.SetPhoto32:
+db $1F, $D2, $20, $1C, $23
+db $04, $D9, $02
+db $02
+
+PhotoHandler:
+db $08
+dd DynamicPhotoSetter
+db $18, $01, $01
+db $70
+db $87, $98, $91, $a4, $50, $91, $50, $9e, $99, $93, $95, $50, $9d, $95, $9d, $9f
+db $a2, $a9, $51, $02
+db $02
+
+PhotoPresentText:
+db $04
+db $1C, $04
+db $08
+dd DynamicPhotoSetter
+db $05
+db $1C, $04
+db $02
+
+FixLightningTypo:
+db $9C, $99, $97, $98, $A4, $9E, $99, $9E, $97
+db $0A
+dl $EED7F5
+
+FixLightningTypo2:
+db $9C, $99, $97, $98, $A4, $9E, $99, $9E, $97
+db $0A
+dl $EED87B
+
+HandleEnergyLinkFromATM:
+db $05, $1D, $04
+db $1D, $17, $01, $00, $00, $00
+db $1B, $02
+dd $C62E79
+;Check for the energy flag here
+db $1C, $26, $01
+db $1B, $06
+db $1B, $03
+dd $C62F49
+
+db $04, $1D, $04
+db $1D, $17, $01, $00, $00, $00
+db $05, $1D, $04
+db $1B, $03
+dd $C62F49
+;Energy has been successfully donwloaded
+db $70
+db $84, $98, $95, $50, $a3, $95, $a2, $a6, $95, $a2, $50, $99, $a3, $50, $93, $a5
+db $a2, $a2, $95, $9e, $a4, $9c, $a9, $50, $a3, $a4, $9f, $a2, $99, $9e, $97, $50
+db $06, $1E, $04
+dd .OverCap
+;Cap is 9999999
+.DisplayMoney:
+db $54
+db $1B, $06
+;Argumentary has the amount
+db $04, $1D, $04
+db $1C, $01, $07, $5e, $03
+db $05, $1D, $04
+db $01
+db $04, $1D, $04
+db $08
+dd $C62F9E
+db $05, $1D, $04
+db $1B, $02
+dd $C62D8A
+db $0D, $00
+db $04, $1D, $04
+db $1D, $17, $00, $00, $00, $00
+db $05, $1D, $04
+db $1B, $03
+dd $C62EEF
+db $1B, $05
+db $70
+db $71, $a5, $a4, $98, $95, $9e, $a4, $99, $93, $91, $a4, $99, $9e, $97, $50, $a2
+db $95, $a1, $a5, $95, $a3, $a4, $5c, $50, $a0, $9c, $95, $91, $a3, $95, $50, $a7
+db $91, $99, $a4, $5e, $5e, $5e
+db $1C, $25, $01
+db $1B, $06
+db $1B, $02
+dd .FailedToAuthenticate
+db $09, $02
+dd .AuthenticateSuccessful
+dd .AuthenticateSuccessOverTotal
+db $02
+
+.OverCap:
+db $05, $1E, $04
+db $9D, $9F, $A2, $95, $50, $A4, $98, $91, $9E, $50
+db $0A
+dl .DisplayMoney
+.FailedToAuthenticate:
+db $70, $76, $91, $99, $9c, $95, $94, $50, $a4, $9f, $50, $91, $a5, $a4, $98, $95
+db $9e, $a4, $99, $93, $91, $a4, $95, $50, $a4, $9f, $50, $a3, $95, $a2, $a6, $95
+db $a2, $5e, $03, $00, $70, $80, $9c, $95, $91, $a3, $95, $50, $a4, $a2, $a9, $50
+db $91, $97, $91, $99, $9e, $5e, $03, $02
+.AuthenticateSuccessful:
+db $1B, $06
+db $0A
+dl $C62EA6
+db $02
+
+.AuthenticateSuccessOverTotal:
+db $70, $71, $9d, $9f, $a5, $9e, $a4, $50, $a2, $95, $a1, $a5, $95, $a3, $a4, $95
+db $94, $50, $95, $a8, $93, $95, $95, $94, $a3, $50, $a4, $98, $95, $50, $91, $9d
+db $9f, $a5, $9e, $a4, $50, $99, $9e, $50, $a4, $98, $95, $50, $a3, $95, $a2, $a6
+db $95, $a2, $5e, $03, $00, $70, $87, $99, $a4, $98, $94, $a2, $91, $a7, $99, $9e
+db $97, $50, $54, $1c, $0a, $00, $00, $00, $00, $50, $99, $9e, $a3, $a4, $95, $91, $94
+db $5e, $03, $01
+db $0A
+dl $C62EA6
+
+InitializeEnergyLink:
+db $0e, $00, $0d, $01
+db $1C, $24, $01
+db $0A
+dl $C62D34
+
+CheckForLockerKey:
+db $1B, $02
+dd .LockerFail
+db $08
+dl OpenLockers_OpenLockerText
+db $19, $20
+db $02
+.LockerFail:
+db $0B, $FE
+db $02
+
+PhotoBarfTextArrive:
+db $02
+
+PhotoBarfTextTake:
+db $08
+dd DynamicPhotoSetter
+db $02
+
+StonehengePhotoText:
+db $70, $83, $9f, $a2, $a2, $a9, $5c, $50, $79, $50, $94, $9f, $9e, $57, $a4, $50
+db $98, $91, $a6, $95, $50, $9d, $a9, $50, $93, $91, $9d, $95, $a2, $91, $50, $a7
+db $99, $a4, $98, $50, $9d, $95, $5e, $13, $02
+
+ShowDepositEnergy:
+db $05, $1D, $04
+db $1D, $17, $01, $00, $00, $00 ; Check if we have at least one dollar
+db $1B, $02
+dd $C63004
+db $04, $1D, $04
+db $0A
+dl $C63004
+
+ShowDadEnergy:
+db $00, $70, $54
+db $05, $1D, $04
+db $1D, $17, $01, $00, $00, $00 ; Check if we have at least one dollar
+db $1B, $02
+dd .BankMoney
+db $04, $1D, $04
+.BankMoney:
+db $1C, $01, $07
+db $05, $1D, $04
+db $0A
+dl $C63330
+
+PingDadEnergy:
+db $1C, $24, $01
+db $06, $04, $03
+dd $C75929
+db $0A
+dl $C63230
+
+;The server is currently storing [Energy]
+
+RuralEnergyText:
+db $70, $89, $9f, $a5, $50, $a7, $91, $9e, $9e, $91, $50, $93, $98, $95, $93, $9b
+db $50, $98, $9f, $a7, $50, $9d, $a5, $93, $98, $50, $9d, $9f, $9e, $95, $a9, $50
+db $99, $a3, $50, $9f, $9e, $50, $a4, $98, $95, $50, $a3, $95, $a2, $a6, $95, $a2
+db $6f, $03, $00, $70, $74, $9f, $50, $79, $50, $9c, $9f, $9f, $9b, $50, $9c, $99
+db $9b, $95, $50, $79, $50, $9b, $9e, $9f, $a7, $50, $a7, $98, $91, $a4, $50, $91
+db $50, $a3, $95, $a2, $a6, $95, $a2, $50, $99, $a3, $6f, $13, $02
+
+DarknessATM:
+db $1C, $27, $01
+db $1B, $06
+db $1B, $02
+dd $C9EC74
+db $1D, $17, $01, $00, $00, $00
+db $1B, $03
+dd RuralEnergyText
+db $0A
+dl $C9EC74
+
+UnderworldATM:
+db $1C, $27, $01
+db $1B, $06
+db $1B, $02
+dd $EF6049
+db $1D, $17, $01, $00, $00, $00
+db $1B, $03
+dd RuralEnergyText
+db $0A
+dl $EF6049
+
+PhotoShopText:
+db $80, $98, $9f, $a4, $9f, $5d, $9f, $a0, $00
+
+SetSaturnLadderFlag:
+db $04
+db $1F, $04
+db $02
+
+JackieMoonsideSetup:
+db $1F, $63
+dd JackieMoonsideGive
+db $0A
+dl $C9A08B
+
+JackieMoonsideGive:
+db $05, $4F, $02
+db $1F, $1A, $80, $03, $03
+db $1F, $16, $80, $03, $03
+db $10, $30
+db $1F, $1B, $80, $03
+db $18, $01, $01
+.JackieGiveItem:
+db $70, $71, $50, $a3, $a4, $a2, $91, $9e, $97, $95, $50, $9d, $91, $9e, $50, $9a
+db $a5, $a3, $a4, $50, $93, $91, $9d, $95, $50, $92, $91, $a2, $97, $99, $9e, $97
+db $50, $99, $9e, $50, $98, $95, $a2, $95, $5c, $10, $0b, $50, $9c, $9f, $9f, $9b
+db $99, $9e, $97, $50, $96, $9f, $a2, $50, $a9, $9f, $a5, $5e, $5e, $5e, $03, $00
+db $70, $79, $a3, $50, $98, $95, $50, $91, $50, $96, $a2, $99, $95, $9e, $94, $50
+db $9f, $96, $50, $a9, $9f, $a5, $a2, $a3, $6f, $10, $16, $50, $78, $95, $50, $94
+db $95, $9d, $91, $9e, $94, $95, $94, $50, $79, $50, $97, $99, $a6, $95, $50, $a9
+db $9f, $a5, $50, $a4, $98, $99, $a3, $50, $1c, $05, $01, $50, $91, $9e, $94, $50
+db $9c, $95, $96, $a4, $5e, $03
+db $1D, $03, $FF
+db $1B, $02
+dd .FullInv
+db $05, $20, $04
+db $1D, $0E, $FF, $04, $08
+dd $C7DCB6
+db $04, $DF, $03
+db $03
+db $18, $04, $02
+.FullInv:
+db $00, $70
+db $83, $99, $9e, $93, $95, $50, $a9, $9f, $a5, $a2, $50, $a0, $9f, $93, $9b, $95
+db $a4, $a3, $50, $91, $a2, $95, $50, $a3, $9f, $50, $96, $a5, $9c, $9c, $5c, $10
+db $0a, $50, $79, $57, $9c, $9c, $50, $98, $9f, $9c, $94, $50, $9f, $9e, $50, $a4
+db $9f, $50, $99, $a4, $50, $96, $9f, $a2, $50, $9e, $9f, $a7, $5e
+db $04, $20, $04
+db $03
+db $18, $04
+db $02
+
+JackieItemCheck:
+db $06
+db $20, $04
+dd JackieMoonsideGive_JackieGiveItem
+db $0A
+dl $c6f175
+
+UnlockOnettTeleport:
+db $04, $D1, $00
+db $02
+
+MagicantTentacleERText:
+db $07
+db $17, $04
+db $1B, $02
+dd .Ness
+db $1F, $63
+dd $C9B112
+.Ness:
+db $0A
+dl MagicantTentacleText
+
+MinerDialogue:
+db $06
+db $AE, $00 ;If the mine is open...
+dd $C60349;Proceed as normal
+;If the mine is closed
+db $06
+db $5A, $00 ;and we got the check
+dd $C60365 ;Run the permit script
+db $06
+db $48, $00 ;If the check is primed...
+dd $C6050B ;Give it to the player
+db $0A
+dl $C60365 ;Else run the permit script
+
+MinerText2:
+db $06, $5A, $00
+dd .Already
+db $91, $9C, $9D, $9F, $A3, $A4
+db $0A
+dl $C61231
+.Already:
+db $98, $91, $94
+db $0A
+dl $C61231
+
+CafeEverdredText:
+db $06, $91, $00
+dd .Done
+db $18, $01, $01
+db $70, $58, $71, $a2, $95, $50, $a9, $9f, $a5, $50, $a2, $95, $91, $9c, $9c, $a9
+db $50, $97, $9f, $99, $9e, $97, $50, $a4, $9f, $50, $9a, $a5, $a3, $a4, $50, $9c
+db $95, $91, $a6, $95, $50, $98, $99, $9d, $50, $9c, $a9, $99, $9e, $97, $50, $9f
+db $9e, $50, $a4, $98, $95, $50, $a3, $a4, $a2, $95, $95, $a4, $5E, $5E, $5E, $6f, $59, $03
+db $18, $04, $02
+.Done:
+db $1F, $63
+dd $C9B112
+db $02
+
+LeaderHandinBadge:
+db $70, $58
+db $19, $10, $01
+db $1B, $04
+db $1C, $02, $00
+db $0A
+dl $EEF748
+
+EverDeadText:
+db $70, $87, $98, $a9, $50, $91, $a2, $95, $50, $a9, $9f, $a5, $50, $99, $9e, $a4
+db $95, $a2, $a2, $a5, $a0, $a4, $99, $9e, $97, $50, $9d, $a9, $50, $a3, $a4, $a2
+db $95, $95, $a4, $50, $a9, $9f, $97, $91, $6f, $03, $01, $70, $85, $97, $98, $5c
+db $50, $99, $a4, $57, $a3, $50, $9e, $9f, $a4, $50, $95, $a6, $95, $9e, $50, $a2
+db $95, $9c, $91, $a8, $99, $9e, $97, $50, $9e, $9f, $a7, $5e, $13
+db $0A
+dl $C839EB
+
+StorageStoreCheck:
+db $1B, $05
+db $19, $19, $00, $00
+db $1C, $19, $01
+db $1B, $03
+dd .CheckRegular
+db $1C, $1D, $01
+.CheckRegular:
+db $1B, $06
+db $1D, $0C, $00, $00
+db $1B, $03
+dd .ClearAndFull
+db $0A
+dl BackFromCheck
+.ClearAndFull:
+db $1C, $1E, $01
+db $0A
+dd $EE9650
+
+ActivateCakeEnemies:
+db $05, $0B, $00
+db $04, $3d, $00, $02
+
+FixEpilogueWinters:
+db $04, $d1, $00
+db $04, $d2, $00
+db $04, $d4, $00
+db $0A
+dl BackfromEpilogueFix
+
+OnettHotelSetTeleport:
+db $04
+db $D1, $00
+db $0A
+dl $C9B11E
+
+MonkeyCaveShop:
+db $04, $ED, $00
+db $08
+dd $C5DFB1
+db $05, $ED, $00
+db $02
+
+MonkeyShopGreeting: ; todo: write this
+db $70, $85, $9b, $99, $9b, $99, $50, $85, $9b, $a5, $51, $03, $00, $70, $58, $87
+db $95, $9c, $93, $9f, $9d, $95, $50, $a4, $9f, $50, $7d, $9f, $9e, $9b, $95, $a9
+db $50, $7d, $91, $a2, $a4, $5e, $59, $03, $02
+db $02
+
+MonkeyShopBuyLine:
+db $70, $7b, $a5, $50, $7b, $99, $9b, $a5, $51, $03, $00, $70, $58, $87, $98, $91
+db $a4, $50, $a7, $9f, $a5, $9c, $94, $50, $a9, $9f, $a5, $50, $9c, $99, $9b, $95
+db $6f, $03, $02
+
+MonkeyShopNotEnoughMoney:
+db $70, $85, $9b, $99, $50, $a5, $9b, $a5, $50, $a5, $9b, $a5, $9c, $95, $9c, $95
+db $51, $03, $00, $70, $58, $89, $9f, $a5, $50, $91, $a2, $95, $50, $a4, $9f, $9f
+db $50, $a0, $9f, $9f, $a2, $50, $96, $9f, $a2, $50, $a4, $98, $99, $a3, $50, $99
+db $a4, $95, $9d, $51, $59, $03, $02
+
+MonkeyShopSelectItem:
+db $70, $7b, $99, $99, $9b, $a5, $9b, $91, $50, $1c, $05, $00, $6f, $03, $00, $70
+db $58, $89, $9f, $a5, $50, $a7, $91, $9e, $a4, $50, $a4, $98, $95, $50, $1c, $05
+db $00, $6f, $59, $03, $02
+
+MonkeyShopPickCarrier:
+db $70, $7b, $a9, $a5, $9b, $a9, $9b, $99, $9b, $95, $9b, $99, $a5, $6f, $03, $00
+db $70, $58, $87, $98, $9f, $57, $a3, $50, $a4, $91, $9b, $99, $9e, $97, $50, $99
+db $a4, $6f, $59, $03, $02
+
+MonkeyShopCancelBuy:
+db $70, $85, $9b, $99, $91, $a9, $50, $a5, $9b, $99, $5e, $03, $00, $70, $58, $7f
+db $98, $5c, $10, $05, $50, $9f, $9b, $91, $a9, $5e, $59, $03, $02
+
+MonkeyShopThank:
+db $70, $85, $9b, $a5, $9b, $a5, $9b, $a5, $51, $03, $00, $70, $58, $84, $98, $91
+db $9e, $9b, $a3, $51, $59, $03, $02
+
+MonkeyShopNoBuy:
+db $70, $7b, $a9, $99, $99, $99, $95, $9b, $95, $9b, $95, $9b, $95, $6f, $03, $00
+db $70, $58, $89, $9f, $a5, $50, $94, $9f, $9e, $57, $a4, $50, $a7, $91, $9e, $a4
+db $50, $a4, $9f, $50, $92, $a5, $a9, $50, $96, $a2, $9f, $9d, $50, $91, $50, $9d
+db $9f, $9e, $9b, $95, $a9, $6f, $59, $03, $02
+
+MonkeyShopCantCarrySingle:
+db $70, $7b, $95, $95, $9b, $99, $9b, $99, $95, $95, $51, $03, $00, $70, $58, $89
+db $9f, $a5, $50, $91, $a2, $95, $50, $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50
+db $a7, $91, $a9, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $51, $59, $03, $02
+
+MonkeyShopExitThanks:
+db $70, $85, $9b, $99, $9b, $99, $9b, $99, $51, $03, $00, $70, $58, $73, $9f, $9d
+db $95, $50, $91, $97, $91, $99, $9e, $51, $59, $03, $02
+
+MonkeyShopBuyAnother:
+db $70, $85, $9b, $a9, $a5, $99, $9b, $a5, $a5, $a5, $a9, $95, $50, $7b, $a9, $95
+db $a5, $95, $9b, $a5, $9b, $9f, $5e, $03, $00, $70, $58, $87, $9f, $a5, $9c, $94
+db $50, $a9, $9f, $a5, $50, $9c, $99, $9b, $95, $50, $91, $9e, $a9, $a4, $98, $99
+db $9e, $97, $50, $95, $9c, $a3, $95, $6f, $59, $03, $00, $02
+
+MonkeyShopCantCarry:
+db $70, $1c, $02, $00, $50, $9b, $99, $9b, $99, $95, $95, $51, $03, $00, $70, $58, $1c
+db $02, $00, $50, $99, $a3, $50, $93, $91, $a2, $a2, $a9, $99, $9e, $97, $50, $a7
+db $91, $a9, $50, $a4, $9f, $9f, $50, $9d, $a5, $93, $98, $51, $59, $03, $00, $70
+db $7b, $a9, $95, $9b, $99, $9b, $99, $9f, $9b, $95, $6f, $03, $00, $70, $58, $87
+db $99, $9c, $9c, $50, $a3, $9f, $9d, $95, $9f, $9e, $95, $50, $95, $9c, $a3, $95
+db $50, $a4, $91, $9b, $95, $50, $99, $a4, $6f, $59, $03, $00, $02
+
+PostFrankGuardText:
+db $70, $7a, $a5, $a3, $a4, $50, $57, $93, $91, $a5, $a3, $95, $50, $a9, $9f, $a5
+db $50, $92, $95, $91, $a4, $50, $a5, $a0, $50, $9f, $a5, $a2, $50, $92, $9f, $a3
+db $a3, $5c, $10, $05, $50, $94, $9f, $95, $a3, $9e, $57, $a4, $50, $9d, $95, $91
+db $9e, $50, $79, $57, $9c, $9c, $50, $9d, $9f, $a6, $95, $50, $96, $9f, $a2, $50
+db $a0, $a5, $9e, $9b, $a3, $50, $9c, $99, $9b, $95, $50, $a9, $9f, $a5, $51, $03
+db $0A
+dl $C74C58
+
+MagicantTentaGetNessName:
+db $19, $10, $01, $1B, $04, $1C, $02, $00
+db $0A
+dl MagicantNameReturn
+
+AddNessToEpilogue:
+db $1F, $11, $01
+db $18, $01, $01, $70
+db $0A
+dl $C7FB5A
+
+NewBikeText:
+db $1C, $22
+db $1B, $06
+db $1B, $03
+dd $C7C6F1
+db $19, $10, $01
+db $0B, $01
+db $1B, $02
+dd .OtherCharBike
+db $70, $1C, $0D, $50
+db $0A
+dl $C9FD4A
+.OtherCharBike:
+db $70, $1C, $0D, $50
+db $a4, $a2, $99, $95, $94, $50, $a4, $9f, $50, $a2, $99, $94, $95, $50, $91, $50
+db $92, $99, $93, $a9, $93, $9c, $95, $5c, $03, $01, $70, $92, $a5, $a4, $50
+db $1C, $14, $01
+db $08
+dd $C7E602
+db $50
+db $94
+db $99, $94, $9e, $57, $a4, $50, $9b, $9e, $9f, $a7, $50, $98, $9f, $a7, $5e, $13
+db $02
+
+Truffle1:
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03
+db $19, $20
+db $02
+
+Truffle2:
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03
+db $19, $20
+db $02
+
+Truffle3:
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03
+db $19, $20
+db $02
+
+Truffle4:
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03
+db $19, $20
+db $02
+
+Truffle5:
+db $17, $79, $51, $03, $00, $70, $84, $15, $78, $91, $50, $1c, $05, $63, $50, $91
+db $16, $0e, $17, $cc, $a4, $5e, $03, $00, $1d, $03, $ff, $1b, $02, $8a, $b5, $ee
+db $ff, $1d, $0e, $ff, $01, $08, $cf, $dc, $c7, $ff, $03
+db $19, $20
+db $02
+
+FixYouWonAlignment:
+db $15, $1D
+db $18, $05, $06, $01
+db $1C, $08, $02
+db $0A
+dl $EF79E3
+
+FixPhotoManLoad:
+db $1f, $f2, $8f, $00, $c4, $01, $0a
+dl $C7AC4D
+
+PhotomanLeave:
+db $1F, $F2, $8F, $00, $83, $02
+db $0A
+dl $C7ACA4
+
+PhotoManActionScript:
+db $25, $0C, $A0
+db $03
+dd $C33B53
+
+UnsetDenyHints:
+db $06, $05, $04
+dd .DenyOnett
+db $06, $06, $04
+dd .DenyTwoson
+db $06, $07, $04
+dd .DenyThreed
+db $06, $08, $04
+dd .DenyFourside
+db $06, $09, $04
+dd .DenySummers
+db $06, $0A, $04
+dd .DenyScaraba
+db $02
+.DenyOnett:
+db $05, $05, $04
+db $02
+.DenyTwoson:
+db $05, $06, $04
+db $02
+.DenyThreed:
+db $05, $07, $04
+db $02
+.DenyFourside:
+db $05, $08, $04
+db $02
+.DenySummers:
+db $05, $09, $04
+db $02
+.DenyScaraba:
+db $05, $0A, $04
+db $02
+
+DenyHintsNoMoney:
+db $08
+dd UnsetDenyHints
+db $15, $d0, $a9, $15, $20
+db $0A
+dl $C72669
+
+DenyHintsSaidNo:
+db $08
+dd UnsetDenyHints
+db $17, $f3, $15, $10
+db $0A
+dl $C701FA
+
+LeaderPantsText:
+db $01, $70
+db $19, $10, $01
+db $1B, $04
+db $1C, $02, $00
+db $0A
+dl LavaPantsUseTxt_DoorPantsTex
+
+EndScript:
+db $02
+
+DisableCollAndAnim:
+db $42
+dl $C0A82F
+db $03
+dl $C3A09F
+
+RideSkycicle:
+db $70, $58, $87, $95, $9c, $9c, $5c, $10, $0a, $90, $99, $a4, $57, $a3, $90, $9e
+db $9f, $a4, $90, $a4, $98, $95, $50, $83, $9b, $a9, $50, $82, $a5, $9e, $9e, $95
+db $a2, $5e, $03, $01, $70, $82, $99, $94, $95, $50, $99, $a4, $50, $91, $9e, $a9
+db $a7, $91, $a9, $a3, $6f, $59, $01
+db $19, $02
+db $89, $95, $A3, $02
+db $19, $02
+db $7E, $9F, $02
+db $1C, $07, $02
+db $11
+db $09, $02
+dd .Ride
+dd .DontRide
+db $0A
+dl .DontRide
+.Ride:
+db $12
+db $70, $58
+db $08
+dd $C7E6D7
+db $50
+db $a2, $9f, $94, $95, $50, $a4, $98, $95, $50, $83, $9b, $a9, $93, $99, $93, $9c
+db $95, $5e, $59, $1f, $02, $73, $10, $30
+db $18, $04
+db $1F, $21, $DA
+db $02
+.DontRide:
+db $12
+db $70, $58, $89, $95, $91, $98, $5c, $10, $0a, $50, $79, $50, $a7, $9f, $a5, $9c
+db $94, $9e, $57, $a4, $5C, $50, $95, $99, $a4, $98, $95, $a2, $5e, $59, $13, $02
+
+CheckForRubyPoo:
+db $1D, $05, $FF, $D0 ; Check for tiny ruby
+db $1B, $03
+dd $C8A997
+db $0A
+dl $C8A8E7
+
+LavaPantsHandleItem:
+db $1B, $00
+db $08
+dd LavaPantsUseTxt
+db $1B, $01
+db $1B, $0F, $00, $00
+db $02
+
+DisableOSSOnLumine:
+db $1f, $eb, $ff, $06
+db $1F, $41, $06
+db $0A
+dl $C99CD1
+
+TwosonCopBossText:
+db $08
+dd $EEEEBC
+db $0A
+dl $EEC785
+
+SetupNessForMagicantBoost:
+db $18, $01, $01
+db $19, $10, $01
+db $70
+db $0A
+dl $EEBDA8
+
+;ORG $C7617D
+;dd DisplayAndGetMoney
+
+;ORG $C62B87
+;db $0a, $28, $ca, $ee ; Test, delete later
+
+;New New Text
+
+ORG $F3FFE0
+db $04, $1C, $04
+db $08
+dd DynamicPhotoSetter
+db $05, $1C, $04
+db $1D, $19, $01, $02
+
+ExtraPresentCheck:
+db $1B, $00
+db $0B, $06
+db $1B, 03
+dd PhotoPresentText
+db $1B, $01
+db $0A
+dl PresentNessCheck
+;dl
+
+ORG $F3FEE0
+save_chunk_size_table:
+dw sram_chunk0_size
+dw sramchunk1_size
+dw sramchunk2_size
+dw sramchunk3_size
+dw sramchunk4_size
+dw sramchunk5_size
+save_chunk_pointer_table:
+dw sramchunk0_pointer
+dw sramchunk1_pointer
+dw sram_chunk2_pointer
+dw sram_chunk3_pointer
+dw sramchunk4_pointer
+dw sramchunk5_pointer
+dw $0000
+
+ORG $F80000
+PhotocolorTable:
+dw $5A39 ; Blank
+dw $5A49 ; Giant Step as well????
+dw $5A4D ; Giant Step
+dw $5A59 ; Onett
+dw $5A85 ; Twoson
+dw $5A8D ; Happy-Happy Free
+dw $5A91 ; Happy-Happy
+dw $5A99 ; Peaceful Rest Valley
+dw $5AB1 ; Threed
+dw $5AC1 ; Rain Circle as well?????
+dw $5AC5 ; Rainy Circle
+dw $5AAD ; Threed Daytime
+dw $5AD1 ; Winters
+dw $5AED ; Saturn Valley
+dw $5AF5 ; Grapefruit Falls
+dw $5B09 ; Dusty Dunes
+dw $5B19 ; Magnet Hill as well?????
+dw $5B1D ; Magnet Hill
+dw $5B29 ; Fourside (Make sure start-of-seed fourside isnt different)
+dw $5B4D ; Summers
+dw $5B5D ; Scaraba Bazaar
+dw $5B6D ; Scaraba Desert
+dw $5B79 ; Dalaam
+dw $5B89 ; Deep Darkness
+dw $5B91 ;Tenda Village
+dw $5B99 ; Lost Underworld
+dw $5BA1 ; Memory house
+dw $5BAD ; Magicant
+dw $5BB1 ; Deep Magicant
+dw $5BB5 ; Sea of Eden
+dw $5BC1 ; Cave of the Present
+dw $5BCD ; Cave of the Past
+dw $5BD1 ; Giygas's Lair
+dw $5C09 ; Lilliput Steps
+dw $5C0D ; Lilliput steps as well????
+dw $5C2D ; Milky Well as well????
+dw $5C31 ; Milky Well
+dw $5C35 ; Milky Well outdoors
+dw $5C51 ; Pink Cloud as well????
+dw $5C55 ; Pink Cloud
+dw $5C65 ; Lumine Hall
+dw $5C69 ; Lumine Hall as well????
+dw $5D71 ; Threed Underground
+dw $5C8D ; Sea
+dw $5C9D ; Three-Two Tunnel
+dw $5CB1 ; Threed-Desert tunnel
+dw $5CC5 ; Desert Tunnel
+dw $5CD5 ; Fourside Tunnel
+dw $5CDD ; PRV Connector Cave
+dw $5CE5 ; Saturn Cave
+dw $5CED ; Giant Step Cave
+dw $5CF5 ; Lilliput Steps cave
+dw $5CFD ; Rainy Circle Cave
+dw $5D05 ; Milky Well Caves
+dw $5D0D ; Sewers
+dw $5D15 ; Pink Cloud Cave
+dw $5D1D ; Lumine Hall Cave
+dw $5D31 ; Fire Spring Cave
+dw $5D39 ; Lier's Cave
+dw $5D49 ; Bricroad's maze
+dw $5D51 ; Stonehenge Entrance
+dw $5D59 ; Stonehenge Balcony
+dw $5D61 ; Stonehenge Flashing room
+dw $5D79 ; Belch's Factory
+dw $5D81 ; Monkey Caves
+dw $5D95 ; Gold Mine
+dw $5D99 ; Moonside
+dw $5DA9 ; Pyramid
+dw $5DB1 ; Dungeon Man
+dw $5DB5 ; Arcade
+dw $5DBD ; Onett Drugstore
+dw $5DD1 ; Ness's House
+dw $5DF1 ; Burger shop
+dw $5DF9 ; Onett Hint House
+dw $5E01 ; Onett Bakery
+dw $5E09 ; Pokey's House
+dw $5E1D ; Onett Hotel
+dw $5E29 ; Police Station
+dw $5E31 ; Town Hall
+dw $5E39 ; Onett Green House
+dw $5E41 ; Onett Blue House
+dw $5E49 ; Onett seafoam house
+dw $5E51 ; Left of Hint House
+dw $5E61 ; Onett Pink House
+dw $5E71 ; Library
+dw $5E79 ; Lier's House
+dw $5E81 ; Onett hospital
+dw $5E91 ; Treehouse
+dw $5E99 ; Traveler shack
+dw $5EA1 ; Apple Kid
+dw $5EA9 ; Orange Kid
+dw $5EB1 ; Bike Shop
+dw $5EB9 ; Twoson Dept Store
+dw $5EC1 ; Everdred's House
+dw $5EC9 ; Bus Station
+dw $5ED1 ; Twoson Orange Roof
+dw $5ED9 ; Twoson Hint House
+dw $5EE9 ; Chaos Theater
+dw $5EF5 ; Twoson Hotel
+dw $5EFD ; Twoson Pizza
+dw $5F05 ; Twoson North Left House
+dw $5F0D ; Twoson North Right House
+dw $5F15 ; Twoson Cat House
+dw $5F1D ; Twoson Hospital
+dw $5F29 ; Paula's House
+dw $5F31 ; HH House
+dw $5F35 ; HH House
+dw $5F3D ; Happy HQ
+dw $5F41 ; Happy HQ
+dw $5F49 ; Happy Drugstore
+dw $5F4D ; Happy Drugstore
+dw $5F61 ; Happy Hospital
+dw $5F65 ; Happy Hospital
+dw $5F6D ; Happy Sturn House
+dw $5F55 ; Happy Hotel
+dw $5F59 ; Happy Hotel
+dw $5F7D ; Saturn Hotel
+dw $5F85 ; Saturn Hospital
+dw $5F8D ; Saturn Shop
+dw $5F95 ; Cabin
+dw $5F9D ; Threed Drugstore
+dw $5FA1 ; Threed Drugstore Scary
+dw $5FA9 ; Threed Hint Shop
+dw $5FAD ; Threed Hint Scary
+dw $5FB5 ; Threed Bakery
+dw $5FB9 ; Threed Bakery Scary
+dw $5FC9 ; Threed Hotel Scary
+dw $5FD5 ; Threed Pizza Scary
+dw $5FED ; Threed Red House Scary
+dw $600D ; Tent
+dw $6011 ; Tent Scary
+dw $6019 ; Threed Hospital
+dw $601D ; Threed Hospital Scary
+dw $6025 ; Snow Wood
+dw $6031 ; Winters drugstore
+dw $603D ; Andonuts Lab
+dw $6051 ; Winters tents
+dw $6059 ; Miner shack
+dw $6061 ; DD Drugstore
+dw $6069 ; Fourside Dept Store
+dw $607D ; Monotoli Building
+dw $608D ; Monotoli Gold Room
+dw $609D ; Monotoli upstairs
+dw $60A5 ; Monotoli Elevator
+dw $60D5 ; Monotoli Offices?
+dw $60C1 ; Monotoli penthouse
+dw $60E1 ; Topolla Theater
+dw $60F1 ; Fourside Bakery
+dw $60F5 ; Fourside Hotel
+dw $6101 ; Fourside Hotel
+dw $6109 ; Fourside Cafe
+dw $6115 ; Fourside Museum
+dw $611D ; Fourside Hospital
+dw $6125 ; Stoic Club
+dw $612D ; Summers Shop
+dw $6141 ; Summers Restaurant
+dw $6139 ; Summers Hotel
+dw $6149 ; Summers Museum
+dw $6151 ; Summers Hospital
+dw $6159 ; Toto Shop
+dw $617D ; Scaraba Shop
+dw $6189 ; Scaraba Hotel
+dw $6191 ; Scaraba House
+dw $6199 ; Scaraba Hospital
+dw $6161 ; Toto House
+dw $6169 ; Dalaam Palace
+dw $6175 ; Dalaam House
+dw $61A9 ; Magicant Shop
+dw $61B1 ; Magicant House
+dw $61BD ; Flying Man House
+dw $61C1 ; Moonside Hotel
+dw $61C9 ; Sky
+;todo here; Hotel wakeups
+;Saturn house when bad carpainter
+;run down onett house
+;tala rama
+;threed pizza shops
+;fire spring sanc
+;Mouse House
+
+GoodPhotoColors:
+dw $0000 ; Blank
+dw $0000 ; Giant Step as well????
+dw $0000 ; Giant Step
+dw $0000 ; Onett
+dw $0000 ; Twoson
+dw $0007 ; Happy-Happy Free
+dw $0007 ; Happy-Happy
+dw $0003 ; Peaceful Rest Valley
+dw $0006 ; Threed
+dw $0004 ; Rain Circle as well?????
+dw $0004 ; Rainy Circle
+dw $0006 ; Threed Daytime
+dw $0004 ; Winters
+dw $0008 ; Saturn Valley
+dw $0007 ; Grapefruit Falls
+dw $0009 ; Dusty Dunes
+dw $0009 ; Magnet Hill as well?????
+dw $0009 ; Magnet Hill
+dw $0009 ; Fourside (Make sure start-of-seed fourside isnt different)
+dw $000F ; Summers
+dw $0011 ; Scaraba Bazaar
+dw $0011 ; Scaraba Desert
+dw $000E ; Dalaam
+dw $0012 ; Deep Darkness
+dw $0013 ; Tenda Village
+dw $0012 ; Lost Underworld
+dw $0010 ; Memory house
+dw $0007 ; Magicant
+dw $0010 ; Deep Magicant
+dw $0010 ; Sea of Eden
+dw $0012 ; Cave of the Present
+dw $000F ; Cave of the Past
+dw $0005 ; Giygas's Lair
+dw $0002 ; Lilliput Steps
+dw $0002 ; Lilliput steps as well????
+dw $0008 ; Milky Well as well????
+dw $0008 ; Milky Well
+dw $0008 ; Milky Well outdoors
+dw $0002 ; Pink Cloud as well????
+dw $0002 ; Pink Cloud
+dw $0005 ; Lumine Hall
+dw $0005 ; Lumine Hall as well????
+dw $000B ; Threed Underground
+dw $0010 ; Sea
+dw $0000 ; Three-Two Tunnel
+dw $0000 ; Threed-Desert tunnel
+dw $0000 ; Desert Tunnel
+dw $0000 ; Fourside Tunnel
+dw $0005 ; PRV Connector Cave
+dw $0005 ; Saturn Cave
+dw $0010 ; Giant Step Cave
+dw $0010 ; Lilliput Steps cave
+dw $0010 ; Rainy Circle Cave
+dw $0010 ; Milky Well Caves
+dw $0011 ; Sewers
+dw $0005 ; Pink Cloud Cave
+dw $0013 ; Lumine Hall Cave
+dw $0013 ; Fire Spring Cave
+dw $0010 ; Lier's Cave
+dw $0005 ; Bricroad's maze
+dw $000A ; Stonehenge Entrance
+dw $0004 ; Stonehenge Balcony
+dw $0010 ; Stonehenge Flashing room
+dw $0002 ; Belch's Factory
+dw $0010 ; Monkey Caves
+dw $0010 ; Gold Mine
+dw $0007 ; Moonside
+dw $0013 ; Pyramid
+dw $0013 ; Dungeon Man
+dw $0008 ; Arcade
+dw $000C ; Onett Drugstore
+dw $0010 ; Ness's House
+dw $000C ; Burger shop
+dw $0010 ; Onett Hint House
+dw $000C ; Onett Bakery
+dw $0010 ; Pokey's House
+dw $0010 ; Onett Hotel
+dw $0009 ; Police Station
+dw $0010 ; Town Hall
+dw $0010 ; Onett Green House
+dw $0010 ; Onett Blue House
+dw $0010 ; Onett seafoam house
+dw $0010 ; Left of Hint House
+dw $0010 ; Onett Pink House
+dw $0009 ; Library
+dw $0010 ; Lier's House
+dw $0010 ; Onett hospital
+dw $000A ; Treehouse
+dw $0010 ; Traveler shack
+dw $0009 ; Apple Kid
+dw $0010 ; Orange Kid
+dw $000C ; Bike Shop
+dw $000C ; Twoson Dept Store
+dw $0010 ; Everdred's House
+dw $0012 ; Bus Station
+dw $0010 ; Twoson Orange Roof
+dw $0010 ; Twoson Hint House
+dw $000B ; Chaos Theater
+dw $0010 ; Twoson Hotel
+dw $0010 ; Twoson Pizza
+dw $0010 ; Twoson North Left House
+dw $0010 ; Twoson North Right House
+dw $0010 ; Twoson Cat House
+dw $0010 ; Twoson Hospital
+dw $0010 ; Paula's House
+dw $0010 ; HH House
+dw $0010 ; HH House
+dw $0008 ; Happy HQ
+dw $0008 ; Happy HQ
+dw $000C ; Happy Drugstore
+dw $000C ; Happy Drugstore
+dw $0010 ; Happy Hospital
+dw $0010 ; Happy Hospital
+dw $0010 ; Happy Sturn House
+dw $0010 ; Happy Hotel
+dw $0010 ; Happy Hotel
+dw $0010 ; Saturn Hotel
+dw $0010 ; Saturn Hospital
+dw $0010 ; Saturn Shop
+dw $0010 ; Cabin
+dw $000C ; Threed Drugstore
+dw $000C ; Threed Drugstore Scary
+dw $0010 ; Threed Hint Shop
+dw $0010 ; Threed Hint Scary
+dw $000C; Threed Bakery
+dw $000C ; Threed Bakery Scary
+dw $0010 ; Threed Hotel Scary
+dw $000C ; Threed Pizza Scary
+dw $0010 ; Threed Red House Scary
+dw $0010 ; Tent
+dw $0010 ; Tent Scary
+dw $0010 ; Threed Hospital
+dw $0010 ; Threed Hospital Scary
+dw $0010 ; Snow Wood
+dw $000C ; Winters drugstore
+dw $000B ; Andonuts Lab
+dw $0010 ; Winters tents
+dw $0010 ; Miner shack
+dw $0010 ; DD Drugstore
+dw $000C ; Fourside Dept Store
+dw $0010 ; Monotoli Building
+dw $0010 ; Monotoli Gold Room
+dw $0010 ; Monotoli upstairs
+dw $0010 ; Monotoli Elevator
+dw $0010 ; Monotoli Offices?
+dw $0010 ; Monotoli penthouse
+dw $0010 ; Topolla Theater
+dw $000C ; Fourside Bakery
+dw $000C ; Fourside Hotel
+dw $000C ; Fourside Hotel
+dw $000B ; Fourside Cafe
+dw $000B ; Fourside Museum
+dw $0010 ; Fourside Hospital
+dw $0010 ; Stoic Club
+dw $0010 ; Summers Shop
+dw $0010 ; Summers Restaurant
+dw $000B ; Summers Hotel
+dw $0010 ; Summers Museum
+dw $0010 ; Summers Hospital
+dw $0010 ; Toto Shop
+dw $0010 ; Scaraba Shop
+dw $0010 ; Scaraba Hotel
+dw $0010 ; Scaraba House
+dw $0010 ; Scaraba Hospital
+dw $0010 ; Toto House
+dw $000D ; Dalaam Palace
+dw $0010 ; Dalaam House
+dw $0000 ; Magicant Shop
+dw $0000 ; Magicant House
+dw $0000 ; Flying Man House
+dw $0010 ; Moonside Hotel
+dw $0013 ; Sky
+
+ORG $F89000
+incbin CrashFont.bin
+
+ORG $F89200
+incbin crashscreen.bin
+
+ORG $F3F000
+DisplayAndGetMoney:
+db $01
+db $1F, $02, $74
+db $18, $0A
+db $18, $03, $01
+db $10, $3C
+db $19, $10, $01
+db $1B, $04
+db $70, $58, $1C, $02, $00, $50, $97, $9F, $A4, $50, $54, $1B, $06
+db $1C, $0A, $00, $00, $00, $00
+db $5E, $59
+db $02
+
+DisplayAndGetMoneyPSI:
+db $18, $0A
+db $18, $01, $01
+db $19, $10, $01
+db $1B, $04
+db $70, $58, $1C, $02, $00, $50, $97, $9F, $A4, $50, $54, $1B, $06
+db $1C, $0A, $00, $00, $00, $00
+db $5E, $59
+db $1F, $02, $74
+db $10, $3C
+db $02
+
+DisplayAndGetMoneyPause:
+db $18, $0A
+db $18, $03, $01
+db $19, $10, $01
+db $1B, $04
+db $70, $58, $1C, $02, $00, $50, $97, $9F, $A4, $50, $54, $1B, $06
+db $1C, $0A, $00, $00, $00, $00
+db $5E, $59
+db $1F, $02, $74
+db $10, $3C
+db $03
+db $1D, $19, $01
+db $02
+
+;ORG $EEB0DA
+;dd DisplayAndGetMoneyPause
+
+;ORG $EEAA08
+;dd DisplayAndGetMoneyPSI
+
+
+
+;todo, PSI rockin?
+
+
+
+
+
+
+;ORG $C93B60
+;db $B1, $63, $03;Font test, delete later. Also update the saturn font
+
+
+;ORG $D59CD3
+;db $42;spell type test
+
+;ORG $D59CC9
+;dw $016D
+
+
+
+;;;;;;;;;;;NAME STUFF PLS DELETE
+;ORG $C1F984
+;LDA #$0006;character limit in the name screen
+
+;ORG $C193BA
+;LDA #$0006;Character limit on are you sure screen
+
+;ORG $C19924
+;LDX #$0006; Character menu header, goods/status
+
+
+;B580 reserved for new names?
+;;;;;;;;;;;;;;;;;;
+
+
+
+;ORG $C07482
+;JSL FixTentBug
+
+;ORG $FFFBE0
+;FixTentBug:
+;CMP #$0400
+;BCS .Bail
+;CPX #$0500
+;BCS .Bail
+;STY $10
+;STA $0E
+;RTL
+;.Bail:
+;SEP #$20
+;PLA
+;PLX
+;LDA #$FF
+;PLD
+;RTL
\ No newline at end of file
diff --git a/worlds/earthbound/src/new psi animations/PSI Blast Alpha.gif b/worlds/earthbound/src/new psi animations/PSI Blast Alpha.gif
new file mode 100644
index 000000000000..f969150a8071
Binary files /dev/null and b/worlds/earthbound/src/new psi animations/PSI Blast Alpha.gif differ
diff --git a/worlds/earthbound/src/new psi animations/PSI Blast Beta.gif b/worlds/earthbound/src/new psi animations/PSI Blast Beta.gif
new file mode 100644
index 000000000000..018038694635
Binary files /dev/null and b/worlds/earthbound/src/new psi animations/PSI Blast Beta.gif differ
diff --git a/worlds/earthbound/src/new psi animations/PSI Blast Gamma.gif b/worlds/earthbound/src/new psi animations/PSI Blast Gamma.gif
new file mode 100644
index 000000000000..1fc2134e24f1
Binary files /dev/null and b/worlds/earthbound/src/new psi animations/PSI Blast Gamma.gif differ
diff --git a/worlds/earthbound/src/new psi animations/PSI Blast Omega.gif b/worlds/earthbound/src/new psi animations/PSI Blast Omega.gif
new file mode 100644
index 000000000000..27b1f32cb486
Binary files /dev/null and b/worlds/earthbound/src/new psi animations/PSI Blast Omega.gif differ
diff --git a/worlds/earthbound/src/new psi animations/PSI Missile Alpha.gif b/worlds/earthbound/src/new psi animations/PSI Missile Alpha.gif
new file mode 100644
index 000000000000..69c5ecc9ebbd
Binary files /dev/null and b/worlds/earthbound/src/new psi animations/PSI Missile Alpha.gif differ
diff --git a/worlds/earthbound/src/new psi animations/PSI Missile Gamma.gif b/worlds/earthbound/src/new psi animations/PSI Missile Gamma.gif
new file mode 100644
index 000000000000..7e846415c602
Binary files /dev/null and b/worlds/earthbound/src/new psi animations/PSI Missile Gamma.gif differ
diff --git a/worlds/earthbound/src/new psi animations/PSI Missile Omega.gif b/worlds/earthbound/src/new psi animations/PSI Missile Omega.gif
new file mode 100644
index 000000000000..66114dfe09ef
Binary files /dev/null and b/worlds/earthbound/src/new psi animations/PSI Missile Omega.gif differ
diff --git a/worlds/earthbound/src/new psi animations/PSI Missile beta.gif b/worlds/earthbound/src/new psi animations/PSI Missile beta.gif
new file mode 100644
index 000000000000..6b0931231870
Binary files /dev/null and b/worlds/earthbound/src/new psi animations/PSI Missile beta.gif differ
diff --git a/worlds/earthbound/test/__init__.py b/worlds/earthbound/test/__init__.py
new file mode 100644
index 000000000000..12f2a83e2825
--- /dev/null
+++ b/worlds/earthbound/test/__init__.py
@@ -0,0 +1,6 @@
+from test.bases import WorldTestBase
+
+
+class EarthBoundTestBase(WorldTestBase):
+ game = "EarthBound"
+ player = 1
diff --git a/worlds/earthbound/test/test_access.py b/worlds/earthbound/test/test_access.py
new file mode 100644
index 000000000000..c2c98ea0bea3
--- /dev/null
+++ b/worlds/earthbound/test/test_access.py
@@ -0,0 +1,66 @@
+from . import EarthBoundTestBase
+
+
+class TestPSIShuffle(EarthBoundTestBase):
+ options = {
+ "PSIShuffle": 1
+ }
+
+
+class TestExtendedPSIShuffle(EarthBoundTestBase):
+ options = {
+ "PSIShuffle": 2
+ }
+
+
+class TestBossShuffle(EarthBoundTestBase):
+ options = {
+ "BossShuffle": 1
+ }
+
+
+class TestBossShuffleWithDD(EarthBoundTestBase):
+ options = {
+ "BossShuffle": 1,
+ "DecoupleDiamondDog": 1
+ }
+
+
+class TestBossShuffleGiygas(EarthBoundTestBase):
+ options = {
+ "BossShuffle": 1,
+ "ShuffleGiygas": 1
+ }
+
+
+class TestBossShuffleFull(EarthBoundTestBase):
+ options = {
+ "BossShuffle": 1,
+ "ShuffleGiygas": 1,
+ "DecoupleDiamondDog": 1
+ }
+
+
+class TestShopChecks(EarthBoundTestBase):
+ options = {
+ "ShopRandomizer": 2,
+ }
+
+
+class TestDungeons(EarthBoundTestBase):
+ options = {
+ "DungeonShuffle": True,
+ }
+
+
+class TestEnemizer(EarthBoundTestBase):
+ options = {
+ "EnemizerStats": True,
+ "EnemizerAttacks": True,
+ "EnemizerAttributes": True,
+ }
+
+class TestMapPalettes(EarthBoundTestBase):
+ options = {
+ "RandomMapColors": 3,
+ }
\ No newline at end of file
diff --git a/worlds/factorio/Client.py b/worlds/factorio/Client.py
old mode 100644
new mode 100755
index 51eb487f8465..beec7c434e93
--- a/worlds/factorio/Client.py
+++ b/worlds/factorio/Client.py
@@ -56,6 +56,10 @@ def _cmd_toggle_send_filter(self):
"""Toggle filtering of item sends that get displayed in-game to only those that involve you."""
self.ctx.toggle_filter_item_sends()
+ def _cmd_toggle_connection_change_filter(self):
+ """Toggle filtering of Connected/Disconnected players."""
+ self.ctx.toggle_filter_connection_changes()
+
def _cmd_toggle_chat(self):
"""Toggle sending of chat messages from players on the Factorio server to Archipelago."""
self.ctx.toggle_bridge_chat_out()
@@ -82,7 +86,7 @@ class FactorioContext(CommonContext):
# updated by spinup server
mod_version: Version = Version(0, 0, 0)
- def __init__(self, server_address, password, filter_item_sends: bool, bridge_chat_out: bool,
+ def __init__(self, server_address, password, filter_connection_changes: bool, filter_item_sends: bool, bridge_chat_out: bool,
rcon_port: int, rcon_password: str, server_settings_path: str | None,
factorio_server_args: tuple[str, ...]):
super(FactorioContext, self).__init__(server_address, password)
@@ -94,6 +98,7 @@ def __init__(self, server_address, password, filter_item_sends: bool, bridge_cha
self.factorio_json_text_parser = FactorioJSONtoTextParser(self)
self.energy_link_increment = 0
self.last_deplete = 0
+ self.filter_connection_changes: bool = filter_connection_changes
self.filter_item_sends: bool = filter_item_sends
self.multiplayer: bool = False # whether multiple different players have connected
self.bridge_chat_out: bool = bridge_chat_out
@@ -130,6 +135,7 @@ def on_print(self, args: dict):
def on_print_json(self, args: dict):
if self.rcon_client:
if (not self.filter_item_sends or not self.is_uninteresting_item_send(args)) \
+ and (not self.filter_connection_changes or not self.is_connection_change(args)) \
and not self.is_echoed_chat(args):
text = self.factorio_json_text_parser(copy.deepcopy(args["data"]))
if not text.startswith(
@@ -222,6 +228,15 @@ def toggle_filter_item_sends(self) -> None:
logger.info(announcement)
self.print_to_game(announcement)
+ def toggle_filter_connection_changes(self) -> None:
+ self.filter_connection_changes = not self.filter_connection_changes
+ if self.filter_connection_changes:
+ announcement = "Connection changes are now filtered."
+ else:
+ announcement = "Connection changes are no longer filtered."
+ logger.info(announcement)
+ self.print_to_game(announcement)
+
def toggle_bridge_chat_out(self) -> None:
self.bridge_chat_out = not self.bridge_chat_out
if self.bridge_chat_out:
@@ -388,6 +403,9 @@ async def factorio_server_watcher(ctx: FactorioContext):
elif re.match(r"^[0-9.]+ Script @[^ ]+\.lua:\d+: Player command toggle-ap-send-filter$", msg):
factorio_server_logger.debug(msg)
ctx.toggle_filter_item_sends()
+ elif re.match(r"^[0-9.]+ Script @[^ ]+\.lua:\d+: Player command toggle-ap-connection-change-filter$", msg):
+ factorio_server_logger.debug(msg)
+ ctx.toggle_filter_connection_changes()
elif re.match(r"^[0-9.]+ Script @[^ ]+\.lua:\d+: Player command toggle-ap-chat$", msg):
factorio_server_logger.debug(msg)
ctx.toggle_bridge_chat_out()
@@ -566,7 +584,7 @@ def launch(*new_args: str):
# args handling
parser = get_base_parser(description="Optional arguments to Factorio Client follow. "
- "Remaining arguments get passed into bound Factorio instance."
+ "Remaining arguments get passed into bound Factorio instance. "
"Refer to Factorio --help for those.")
parser.add_argument('--rcon-port', default='24242', type=int, help='Port to use to communicate with Factorio')
parser.add_argument('--rcon-password', help='Password to authenticate with RCON.')
@@ -586,6 +604,7 @@ def launch(*new_args: str):
raise FileNotFoundError(f"Could not find file {server_settings} for server_settings. Aborting.")
initial_filter_item_sends = bool(settings.filter_item_sends)
+ initial_filter_connection_changes = bool(settings.filter_connection_changes)
initial_bridge_chat_out = bool(settings.bridge_chat_out)
if not os.path.exists(os.path.dirname(executable)):
@@ -600,7 +619,7 @@ def launch(*new_args: str):
asyncio.run(main(lambda: FactorioContext(
args.connect, args.password,
- initial_filter_item_sends, initial_bridge_chat_out,
+ initial_filter_connection_changes, initial_filter_item_sends, initial_bridge_chat_out,
rcon_port, rcon_password, server_settings, rest
)))
colorama.deinit()
diff --git a/worlds/factorio/Locations.py b/worlds/factorio/Locations.py
index 52f0954cba30..844739b16fcb 100644
--- a/worlds/factorio/Locations.py
+++ b/worlds/factorio/Locations.py
@@ -1,6 +1,6 @@
from typing import Dict, List
-from .Technologies import factorio_base_id
+from .Technologies import factorio_base_id, recipes
from .Options import MaxSciencePack
@@ -21,5 +21,18 @@ def make_pools() -> Dict[str, List[str]]:
location_table.update({name: ap_id for ap_id, name in enumerate(pool, start=end_id)})
end_id += len(pool)
+craftsanity_locations = []
+valid_items = []
+item_category = {}
+for recipe_name, recipe in recipes.items():
+ if not recipe_name.endswith(("-barrel", "-science-pack")):
+ for result in recipe.products:
+ if result not in valid_items:
+ valid_items.append(result)
+for i, item in enumerate(valid_items, start=end_id):
+ location_table[f"Craft {item}"] = i
+ craftsanity_locations.append(f"Craft {item}")
+ end_id += 1
+
assert end_id - len(location_table) == factorio_base_id
del pool
diff --git a/worlds/factorio/Mod.py b/worlds/factorio/Mod.py
index 3cc156112d32..00ef0d866a30 100644
--- a/worlds/factorio/Mod.py
+++ b/worlds/factorio/Mod.py
@@ -112,7 +112,7 @@ def load_template(name: str):
settings_template = template_env.get_template("settings.lua")
# get data for templates
locations = [(location, location.item)
- for location in world.science_locations]
+ for location in world.science_locations + world.craftsanity_locations]
mod_name = f"AP-{multiworld.seed_name}-P{player}-{multiworld.get_file_safe_player_name(player)}"
versioned_mod_name = mod_name + "_" + Utils.__version__
diff --git a/worlds/factorio/Options.py b/worlds/factorio/Options.py
index 0a789669d5d6..70880aaaf90c 100644
--- a/worlds/factorio/Options.py
+++ b/worlds/factorio/Options.py
@@ -6,7 +6,7 @@
from schema import Schema, Optional, And, Or, SchemaError
from Options import Choice, OptionDict, OptionSet, DefaultOnToggle, Range, DeathLink, Toggle, \
- StartInventoryPool, PerGameCommonOptions, OptionGroup
+ StartInventoryPool, PerGameCommonOptions, OptionGroup, NamedRange
# schema helpers
@@ -60,6 +60,20 @@ class Goal(Choice):
default = 0
+class CraftSanity(NamedRange):
+ """Choose a number of researches to require crafting a specific item rather than with science packs.
+ May be capped based on the total number of locations.
+ There will always be at least 2 Science Pack research locations for automation and logistics, and 1 for rocket-silo
+ if the Rocket Silo option is not set to Spawn."""
+ display_name = "CraftSanity"
+ default = 0
+ range_start = 0
+ range_end = 183
+ special_range_names = {
+ "disabled": 0
+ }
+
+
class TechCost(Range):
range_start = 1
range_end = 10000
@@ -345,6 +359,7 @@ class FactorioWorldGen(OptionDict):
"seed": None,
"starting_area": 1,
"peaceful_mode": False,
+ "no_enemies_mode": False,
"cliff_settings": {
"name": "cliff",
"cliff_elevation_0": 10,
@@ -394,6 +409,7 @@ class FactorioWorldGen(OptionDict):
Optional("height"): And(int, lambda n: n >= 0),
Optional("starting_area"): FloatRange(0.166, 6),
Optional("peaceful_mode"): LuaBool,
+ Optional("no_enemies_mode"): LuaBool,
Optional("cliff_settings"): {
"name": str, "cliff_elevation_0": FloatRange(0, 99),
"cliff_elevation_interval": FloatRange(0.066, 241), # 40/frequency
@@ -473,6 +489,7 @@ class EnergyLink(Toggle):
class FactorioOptions(PerGameCommonOptions):
max_science_pack: MaxSciencePack
goal: Goal
+ craftsanity: CraftSanity
tech_tree_layout: TechTreeLayout
min_tech_cost: MinTechCost
max_tech_cost: MaxTechCost
diff --git a/worlds/factorio/Technologies.py b/worlds/factorio/Technologies.py
index 192cd1fefb82..551276fbe3ea 100644
--- a/worlds/factorio/Technologies.py
+++ b/worlds/factorio/Technologies.py
@@ -334,14 +334,15 @@ def recursively_get_unlocking_technologies(ingredient_name, _done=None, unlock_f
recursively_get_unlocking_technologies(ingredient_name, unlock_func=unlock)))
-def get_rocket_requirements(silo_recipe: Optional[Recipe], part_recipe: Recipe,
+def get_rocket_requirements(silo_recipe: Optional[Recipe], part_recipe: Optional[Recipe],
satellite_recipe: Optional[Recipe], cargo_landing_pad_recipe: Optional[Recipe]) -> Set[str]:
techs = set()
if silo_recipe:
for ingredient in silo_recipe.ingredients:
techs |= recursively_get_unlocking_technologies(ingredient)
- for ingredient in part_recipe.ingredients:
- techs |= recursively_get_unlocking_technologies(ingredient)
+ if part_recipe:
+ for ingredient in part_recipe.ingredients:
+ techs |= recursively_get_unlocking_technologies(ingredient)
if cargo_landing_pad_recipe:
for ingredient in cargo_landing_pad_recipe.ingredients:
techs |= recursively_get_unlocking_technologies(ingredient)
diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py
index 8dc654099bac..bdc375f679a2 100644
--- a/worlds/factorio/__init__.py
+++ b/worlds/factorio/__init__.py
@@ -9,7 +9,7 @@
from worlds.AutoWorld import World, WebWorld
from worlds.LauncherComponents import Component, components, Type, launch as launch_component
from worlds.generic import Rules
-from .Locations import location_pools, location_table
+from .Locations import location_pools, location_table, craftsanity_locations
from .Mod import generate_mod
from .Options import (FactorioOptions, MaxSciencePack, Silo, Satellite, TechTreeInformation, Goal,
TechCostDistribution, option_groups)
@@ -88,6 +88,7 @@ class Factorio(World):
skip_silo: bool = False
origin_region_name = "Nauvis"
science_locations: typing.List[FactorioScienceLocation]
+ craftsanity_locations: typing.List[FactorioCraftsanityLocation]
removed_technologies: typing.Set[str]
settings: typing.ClassVar[FactorioSettings]
trap_names: tuple[str] = ("Evolution", "Attack", "Teleport", "Grenade", "Cluster Grenade", "Artillery",
@@ -100,6 +101,7 @@ def __init__(self, world, player: int):
self.advancement_technologies = set()
self.custom_recipes = {}
self.science_locations = []
+ self.craftsanity_locations = []
self.tech_tree_layout_prerequisites = {}
generate_output = generate_mod
@@ -127,17 +129,42 @@ def create_regions(self):
location_pool = []
+ craftsanity_pool = [craft for craft in craftsanity_locations
+ if self.options.silo != Silo.option_spawn
+ or craft not in ["Craft rocket-silo", "Craft cargo-landing-pad"]]
+ # Ensure at least 2 science pack locations for automation and logistics, and 1 more for rocket-silo
+ # if it is not pre-spawned
+ craftsanity_count = min(self.options.craftsanity.value, len(craftsanity_pool),
+ location_count - (2 if self.options.silo == Silo.option_spawn else 3))
+
+ location_count -= craftsanity_count
+
for pack in sorted(self.options.max_science_pack.get_allowed_packs()):
location_pool.extend(location_pools[pack])
try:
- location_names = random.sample(location_pool, location_count)
+ # Ensure there are two "AP-1-" locations for automation and logistics, and one max science pack location
+ # for rocket-silo if it is not pre-spawned
+ max_science_pack_number = len(self.options.max_science_pack.get_allowed_packs())
+ science_location_names = None
+ while (not science_location_names or
+ len([location for location in science_location_names if location.startswith("AP-1-")]) < 2
+ or (self.options.silo != Silo.option_spawn and len([location for location in science_location_names
+ if location.startswith(f"AP-{max_science_pack_number}")]) < 1)):
+ science_location_names = random.sample(location_pool, location_count)
+ craftsanity_location_names = random.sample(craftsanity_pool, craftsanity_count)
+
except ValueError as e:
# should be "ValueError: Sample larger than population or is negative"
raise Exception("Too many traps for too few locations. Either decrease the trap count, "
f"or increase the location count (higher max science pack). (Player {self.player})") from e
self.science_locations = [FactorioScienceLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis)
- for loc_name in location_names]
+ for loc_name in science_location_names]
+
+ self.craftsanity_locations = [FactorioCraftsanityLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis)
+ for loc_name in craftsanity_location_names]
+
+
distribution: TechCostDistribution = self.options.tech_cost_distribution
min_cost = self.options.min_tech_cost.value
max_cost = self.options.max_tech_cost.value
@@ -159,6 +186,7 @@ def sorter(loc: FactorioScienceLocation):
location.count = rand_values[i]
del rand_values
nauvis.locations.extend(self.science_locations)
+ nauvis.locations.extend(self.craftsanity_locations)
location = FactorioLocation(player, "Rocket Launch", None, nauvis)
nauvis.locations.append(location)
event = FactorioItem("Victory", ItemClassification.progression, None, player)
@@ -188,7 +216,7 @@ def create_items(self) -> None:
loc: FactorioScienceLocation
if self.options.tech_tree_information == TechTreeInformation.option_full:
# mark all locations as pre-hinted
- for loc in self.science_locations:
+ for loc in self.science_locations + self.craftsanity_locations:
loc.revealed = True
if self.skip_silo:
self.removed_technologies |= {"rocket-silo"}
@@ -236,6 +264,23 @@ def set_rules(self):
location.access_rule = lambda state, ingredient=ingredient: \
all(state.has(technology.name, player) for technology in required_technologies[ingredient])
+ for location in self.craftsanity_locations:
+ if location.crafted_item == "crude-oil":
+ recipe = recipes["pumpjack"]
+ elif location.crafted_item in recipes:
+ recipe = recipes[location.crafted_item]
+ else:
+ for recipe_name, recipe in recipes.items():
+ if recipe_name.endswith("-barrel"):
+ continue
+ if location.crafted_item in recipe.products:
+ break
+ else:
+ raise Exception(
+ f"No recipe found for {location.crafted_item} for Craftsanity for player {self.player}")
+ location.access_rule = lambda state, recipe=recipe: \
+ state.has_all({technology.name for technology in recipe.recursive_unlocking_technologies}, player)
+
for location in self.science_locations:
Rules.set_rule(location, lambda state, ingredients=frozenset(location.ingredients):
all(state.has(f"Automated {ingredient}", player) for ingredient in ingredients))
@@ -250,10 +295,11 @@ def set_rules(self):
silo_recipe = self.get_recipe("rocket-silo")
cargo_pad_recipe = self.get_recipe("cargo-landing-pad")
part_recipe = self.custom_recipes["rocket-part"]
- satellite_recipe = None
- if self.options.goal == Goal.option_satellite:
- satellite_recipe = self.get_recipe("satellite")
- victory_tech_names = get_rocket_requirements(silo_recipe, part_recipe, satellite_recipe, cargo_pad_recipe)
+ satellite_recipe = self.get_recipe("satellite")
+ victory_tech_names = get_rocket_requirements(
+ silo_recipe, part_recipe,
+ satellite_recipe if self.options.goal == Goal.option_satellite else None,
+ cargo_pad_recipe)
if self.options.silo == Silo.option_spawn:
victory_tech_names -= {"rocket-silo"}
else:
@@ -263,6 +309,46 @@ def set_rules(self):
victory_tech_names)
self.multiworld.completion_condition[player] = lambda state: state.has('Victory', player)
+ if "Craft rocket-silo" in self.multiworld.regions.location_cache[self.player]:
+ victory_tech_names_r = get_rocket_requirements(silo_recipe, None, None, None)
+ if self.options.silo == Silo.option_spawn:
+ victory_tech_names_r -= {"rocket-silo"}
+ else:
+ victory_tech_names_r |= {"rocket-silo"}
+ self.get_location("Craft rocket-silo").access_rule = lambda state: all(state.has(technology, player)
+ for technology in
+ victory_tech_names_r)
+
+ if "Craft rocket-part" in self.multiworld.regions.location_cache[self.player]:
+ victory_tech_names_p = get_rocket_requirements(silo_recipe, part_recipe, None, None)
+ if self.options.silo == Silo.option_spawn:
+ victory_tech_names_p -= {"rocket-silo"}
+ else:
+ victory_tech_names_p |= {"rocket-silo"}
+ self.get_location("Craft rocket-part").access_rule = lambda state: all(state.has(technology, player)
+ for technology in
+ victory_tech_names_p)
+
+ if "Craft satellite" in self.multiworld.regions.location_cache[self.player]:
+ victory_tech_names_s = get_rocket_requirements(None, None, satellite_recipe, None)
+ if self.options.silo == Silo.option_spawn:
+ victory_tech_names_s -= {"rocket-silo"}
+ else:
+ victory_tech_names_s |= {"rocket-silo"}
+ self.get_location("Craft satellite").access_rule = lambda state: all(state.has(technology, player)
+ for technology in
+ victory_tech_names_s)
+
+ if "Craft cargo-landing-pad" in self.multiworld.regions.location_cache[self.player]:
+ victory_tech_names_c = get_rocket_requirements(None, None, None, cargo_pad_recipe)
+ if self.options.silo == Silo.option_spawn:
+ victory_tech_names_c -= {"rocket-silo"}
+ else:
+ victory_tech_names_c |= {"rocket-silo"}
+ self.get_location("Craft cargo-landing-pad").access_rule = lambda state: all(state.has(technology, player)
+ for technology in
+ victory_tech_names_c)
+
def get_recipe(self, name: str) -> Recipe:
return self.custom_recipes[name] if name in self.custom_recipes \
else next(iter(all_product_sources.get(name)))
@@ -486,9 +572,17 @@ def set_custom_recipes(self):
needed_recipes = self.options.max_science_pack.get_allowed_packs() | {"rocket-part"}
if self.options.silo != Silo.option_spawn:
needed_recipes |= {"rocket-silo", "cargo-landing-pad"}
- if self.options.goal.value == Goal.option_satellite:
+ if (self.options.goal.value == Goal.option_satellite
+ or "Craft satellite" in self.multiworld.regions.location_cache[self.player]):
needed_recipes |= {"satellite"}
+ needed_items = {location.crafted_item for location in self.craftsanity_locations}
+ for recipe_name, recipe in recipes.items():
+ for product in recipe.products:
+ if product in needed_items:
+ self.advancement_technologies |= {tech.name for tech in recipe.recursive_unlocking_technologies}
+ break
+
for recipe in needed_recipes:
recipe = self.custom_recipes.get(recipe, recipes[recipe])
self.advancement_technologies |= {tech.name for tech in recipe.recursive_unlocking_technologies}
@@ -520,9 +614,23 @@ class FactorioLocation(Location):
game: str = Factorio.game
+class FactorioCraftsanityLocation(FactorioLocation):
+ ingredients = {}
+ count = 0
+ revealed = False
+
+ def __init__(self, player: int, name: str, address: int, parent: Region):
+ super(FactorioCraftsanityLocation, self).__init__(player, name, address, parent)
+
+ @property
+ def crafted_item(self):
+ return " ".join(self.name.split(" ")[1:])
+
+
class FactorioScienceLocation(FactorioLocation):
complexity: int
revealed: bool = False
+ crafted_item = None
# Factorio technology properties:
ingredients: typing.Dict[str, int]
diff --git a/worlds/factorio/data/mod_template/control.lua b/worlds/factorio/data/mod_template/control.lua
index cd0c00e9877f..3f754d925ce9 100644
--- a/worlds/factorio/data/mod_template/control.lua
+++ b/worlds/factorio/data/mod_template/control.lua
@@ -859,6 +859,10 @@ commands.add_command("toggle-ap-send-filter", "Toggle filtering of item sends th
log("Player command toggle-ap-send-filter") -- notifies client
end)
+commands.add_command("toggle-ap-connection-change-filter", "Toggle filtering of players joining or parting", function(call)
+ log("Player command toggle-ap-connection-change-filter") -- notifies client
+end)
+
commands.add_command("toggle-ap-chat", "Toggle sending of chat messages from players on the Factorio server to Archipelago.", function(call)
log("Player command toggle-ap-chat") -- notifies client
end)
diff --git a/worlds/factorio/data/mod_template/data-final-fixes.lua b/worlds/factorio/data/mod_template/data-final-fixes.lua
index 8092062bc3f2..2ddcd8d8ab60 100644
--- a/worlds/factorio/data/mod_template/data-final-fixes.lua
+++ b/worlds/factorio/data/mod_template/data-final-fixes.lua
@@ -63,22 +63,6 @@ template_tech.upgrade = false
template_tech.effects = {}
template_tech.prerequisites = {}
-{%- if max_science_pack < 6 %}
- technologies["space-science-pack"].effects = {}
- {%- if max_science_pack == 0 %}
- table.insert (technologies["automation"].effects, {type = "unlock-recipe", recipe = "satellite"})
- {%- elif max_science_pack == 1 %}
- table.insert (technologies["logistic-science-pack"].effects, {type = "unlock-recipe", recipe = "satellite"})
- {%- elif max_science_pack == 2 %}
- table.insert (technologies["military-science-pack"].effects, {type = "unlock-recipe", recipe = "satellite"})
- {%- elif max_science_pack == 3 %}
- table.insert (technologies["chemical-science-pack"].effects, {type = "unlock-recipe", recipe = "satellite"})
- {%- elif max_science_pack == 4 %}
- table.insert (technologies["production-science-pack"].effects, {type = "unlock-recipe", recipe = "satellite"})
- {%- elif max_science_pack == 5 %}
- table.insert (technologies["utility-science-pack"].effects, {type = "unlock-recipe", recipe = "satellite"})
- {% endif %}
-{% endif %}
{%- if silo == 2 %}
data.raw["recipe"]["rocket-silo"].enabled = true
{% endif %}
@@ -169,9 +153,16 @@ technologies["{{ original_tech_name }}"].hidden_in_factoriopedia = true
{#- the tech researched by the local player #}
new_tree_copy = table.deepcopy(template_tech)
new_tree_copy.name = "ap-{{ location.address }}-"{# use AP ID #}
+{% if location.crafted_item is not none %}
+new_tree_copy.research_trigger = {
+ type = "{{ 'craft-fluid' if location.crafted_item in liquids else 'craft-item' }}",
+ {{ 'fluid' if location.crafted_item in liquids else 'item' }} = {{ variable_to_lua(location.crafted_item) }}
+}
+new_tree_copy.unit = nil
+{% else %}
new_tree_copy.unit.count = {{ location.count }}
new_tree_copy.unit.ingredients = {{ variable_to_lua(location.factorio_ingredients) }}
-
+{% endif %}
{%- if location.revealed and item.name in base_tech_table -%}
{#- copy Factorio Technology Icon #}
copy_factorio_icon(new_tree_copy, "{{ item.name }}")
diff --git a/worlds/factorio/docs/connect-to-ap-server.png b/worlds/factorio/docs/connect-to-ap-server.png
index 40078a803605..e1ab02839870 100644
Binary files a/worlds/factorio/docs/connect-to-ap-server.png and b/worlds/factorio/docs/connect-to-ap-server.png differ
diff --git a/worlds/factorio/docs/factorio-download.png b/worlds/factorio/docs/factorio-download.png
index 6d5b17f4b886..b59875110a05 100644
Binary files a/worlds/factorio/docs/factorio-download.png and b/worlds/factorio/docs/factorio-download.png differ
diff --git a/worlds/factorio/docs/setup_en.md b/worlds/factorio/docs/setup_en.md
index 0b4ccee0d7f2..1cf5c09d6a4f 100644
--- a/worlds/factorio/docs/setup_en.md
+++ b/worlds/factorio/docs/setup_en.md
@@ -92,7 +92,7 @@ appropriate to your operating system, and extract the folder to a convenient loc
Archipelago is to place the extracted game folder into the `Archipelago` directory and rename it to just be "Factorio".
-
+
Next, you should launch your Factorio Server by running `factorio.exe`, which is located at: `bin/x64/factorio.exe`. You
will be asked to log in to your Factorio account using the same credentials you used on Factorio's website. After you
@@ -122,7 +122,7 @@ This allows you to host your own Factorio game.
Archipelago if you chose to include it during the installation process.
6. Enter `/connect [server-address]` into the input box at the bottom of the Archipelago Client and press "Enter"
-
+
7. Launch your Factorio Client
8. Click on "Multiplayer" in the main menu
@@ -179,6 +179,18 @@ factorio_options:
filter_item_sends: true
```
+### filter_connection_changes
+
+By default, all player joined and player stopped tracking notifications are displayed in-game. In larger async seeds this may become overly spammy.
+To hide all connection changes, do one of the following:
+- Type `/toggle-ap-connection-change-filter` in-game
+- Type `/toggle_connection_change_filter` in the Achipelago Client
+- In your `host.yaml` set
+```
+factorio_options:
+ filter_connection_changes: true
+```
+
### bridge_chat_out
By default, in-game chat is bridged to Archipelago. If you prefer to be able to speak privately, you can disable this
feature by doing one of the following:
diff --git a/worlds/factorio/settings.py b/worlds/factorio/settings.py
index a2296e7395a0..15d6f637aa3d 100644
--- a/worlds/factorio/settings.py
+++ b/worlds/factorio/settings.py
@@ -19,8 +19,12 @@ class FilterItemSends(settings.Bool):
class BridgeChatOut(settings.Bool):
"""Whether to send chat messages from players on the Factorio server to Archipelago."""
+
+ class FilterConnectionChanges(settings.Bool):
+ """Whether to filter connection changes displayed in-game."""
executable: Executable = Executable("factorio/bin/x64/factorio")
server_settings: typing.Optional[ServerSettings] = None
filter_item_sends: typing.Union[FilterItemSends, bool] = False
+ filter_connection_changes: typing.Union[FilterConnectionChanges, bool] = False
bridge_chat_out: typing.Union[BridgeChatOut, bool] = True
diff --git a/worlds/ff1/Client.py b/worlds/ff1/Client.py
index a4279afd3a4a..4b21e2ac5e6c 100644
--- a/worlds/ff1/Client.py
+++ b/worlds/ff1/Client.py
@@ -16,6 +16,7 @@
rom_name_location = 0x07FFE3
+player_name_location = 0x07BCC0
locations_array_start = 0x200
locations_array_length = 0x100
items_obtained = 0x03
@@ -111,6 +112,12 @@ async def validate_rom(self, ctx: "BizHawkClientContext") -> bool:
return True
+ async def set_auth(self, ctx: "BizHawkClientContext") -> None:
+ auth_raw = (await bizhawk.read(
+ ctx.bizhawk_ctx,
+ [(player_name_location, 0x40, self.rom)]))[0]
+ ctx.auth = str(auth_raw, "utf-8").replace("\x00", "").strip()
+
async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
if ctx.server is None:
return
@@ -204,7 +211,7 @@ async def received_items_check(self, ctx: "BizHawkClientContext") -> None:
write_list.append((location, [0], self.sram))
elif current_item_name in no_overworld_items:
if current_item_name == "Sigil":
- location = 0x28
+ location = 0x2B
else:
location = 0x12
write_list.append((location, [1], self.sram))
diff --git a/worlds/ff1/data/locations.json b/worlds/ff1/data/locations.json
index 2f465a78970e..a1664d442fbd 100644
--- a/worlds/ff1/data/locations.json
+++ b/worlds/ff1/data/locations.json
@@ -253,5 +253,17 @@
"CubeBot": 529,
"Sarda": 525,
"Fairy": 531,
- "Lefein": 527
+ "Lefein": 527,
+ "DeepDungeon32B_Chest144": 401,
+ "DeepDungeon30B_Chest145": 402,
+ "DeepDungeon29B_Chest146": 403,
+ "DeepDungeon29B_Chest147": 404,
+ "DeepDungeon40B_Chest186": 443,
+ "DeepDungeon38B_Chest188": 445,
+ "DeepDungeon36B_Chest189": 446,
+ "DeepDungeon33B_Chest190": 447,
+ "DeepDungeon40B_Chest191": 448,
+ "DeepDungeon41B_Chest192": 449,
+ "DeepDungeon34B_Chest193": 450,
+ "DeepDungeon39B_Chest194": 451
}
diff --git a/worlds/ffmq/Regions.py b/worlds/ffmq/Regions.py
index 4e26be1653a6..7bf773f5cf9e 100644
--- a/worlds/ffmq/Regions.py
+++ b/worlds/ffmq/Regions.py
@@ -155,6 +155,10 @@ def check_foresta(region):
return True
check_foresta(loc.parent_region)
+ if self.options.map_shuffle or self.options.crest_shuffle:
+ process_rules(self.multiworld.get_entrance("Subregion Frozen Fields to Subregion Aquaria", self.player),
+ ["SummerAquaria"])
+
if self.options.logic == "friendly":
process_rules(self.multiworld.get_entrance("Overworld - Ice Pyramid", self.player),
["MagicMirror"])
diff --git a/worlds/ffmq/docs/setup_en.md b/worlds/ffmq/docs/setup_en.md
index 77569c93f0c8..2c8eb9a142c4 100644
--- a/worlds/ffmq/docs/setup_en.md
+++ b/worlds/ffmq/docs/setup_en.md
@@ -115,9 +115,8 @@ You only have to do these steps once. Note, RetroArch 1.9.x will not work as it
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
-
+ Network Command Port at 55355. \
+ 
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
diff --git a/worlds/ffmq/docs/setup_fr.md b/worlds/ffmq/docs/setup_fr.md
index 12ea41c6b3a0..570e18d0b257 100644
--- a/worlds/ffmq/docs/setup_fr.md
+++ b/worlds/ffmq/docs/setup_fr.md
@@ -123,10 +123,8 @@ Vous ne devez faire ces étapes qu'une fois. À noter que RetroArch 1.9.x ne fon
1. Entrez dans le menu principal de RetroArch.
2. Allez dans Settings --> User Interface. Activez l'option "Show Advanced Settings".
3. Allez dans Settings --> Network. Activez l'option "Network Commands", qui se trouve sous "Request Device 16".
- Laissez le "Network Command Port" à sa valeur par defaut, qui devrait être 55355.
-
-
-
+ Laissez le "Network Command Port" à sa valeur par defaut, qui devrait être 55355. \
+ 
4. Allez dans le Menu Principal --> Online Updater --> Core Downloader. Trouvez et sélectionnez "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
diff --git a/worlds/ffx/__init__.py b/worlds/ffx/__init__.py
new file mode 100644
index 000000000000..3a2e99220af0
--- /dev/null
+++ b/worlds/ffx/__init__.py
@@ -0,0 +1,191 @@
+"""
+Archipelago World definition for Final Fantasy X
+"""
+
+from typing import ClassVar, Any, Optional
+from random import Random
+from settings import Group, FilePath
+
+from BaseClasses import Tutorial, Item, ItemClassification, LocationProgressType
+from worlds.AutoWorld import WebWorld, World
+from Utils import visualize_regions
+
+from .client import FFXClient
+
+from .items import create_item_label_to_code_map, item_table, key_items, filler_items, AllItems, FFXItem, \
+ party_member_items, stat_abilities, skill_abilities, region_unlock_items, trap_items, equip_items
+from .locations import create_location_label_to_id_map, FFXLocation, allLocations
+from .regions import create_regions
+from .options import FFXOptions
+from .generate import generate_output
+from .rules import set_rules, world_battle_levels
+from .ut import tracker_world, setup_options_from_slot_data
+
+
+class FFXWebWorld(WebWorld):
+ """
+ Webhost info for Final Fantasy X
+ """
+ theme = "grass"
+ setup_en = Tutorial(
+ "Multiworld Setup Guide",
+ "A guide to playing Final Fantasy X with Archipelago.",
+ "English",
+ "setup_en.md",
+ "setup/en",
+ ["Rurusachi"]
+ )
+
+ tutorials = [setup_en]
+
+
+class FFXSettings(Group):
+ class UTPoptrackerPath(FilePath):
+ """Path to the user's FFX Poptracker Pack."""
+ description = "FFX Poptracker Pack zip file"
+ required = False
+
+ ut_poptracker_path: UTPoptrackerPath | str = UTPoptrackerPath()
+
+
+class FFXWorld(World):
+ """
+ Final Fantasy X is a game
+ """
+ game = "Final Fantasy X"
+ web = FFXWebWorld()
+ topology_present = False
+
+ settings_key = "ffx_options"
+ settings: ClassVar[FFXSettings]
+
+ options_dataclass = FFXOptions
+ options: FFXOptions
+
+ required_client_version = (0, 4, 4)
+
+ item_name_to_id = create_item_label_to_code_map()
+ location_name_to_id = create_location_label_to_id_map()
+ explicit_indirect_conditions = False
+
+ # Universal Tracker
+ tracker_world = tracker_world
+ ut_can_gen_without_yaml = True
+ using_ut: bool
+
+ @staticmethod
+ def interpret_slot_data(slot_data: dict[str, Any]) -> dict[str, Any]:
+ return slot_data
+
+ def generate_early(self) -> None:
+ setup_options_from_slot_data(self)
+
+ def get_filler_item_name(self) -> str:
+ filler = [x.itemName for x in filler_items]
+ return self.random.choice(filler)
+
+ def create_regions(self) -> None:
+ create_regions(self, self.player)
+
+ def create_items(self):
+
+ required_items = []
+
+ for item in key_items:
+ required_items.append(item.itemName)
+
+ # Progressive celestial weapons and Brotherhood
+ for item in equip_items:
+ if item.progression == ItemClassification.progression:
+ if item.itemID & 0x0FFF == 0x0001:
+ # Brotherhood
+ required_items.extend([item.itemName]*2)
+ else:
+ # Celestial
+ required_items.extend([item.itemName]*3)
+
+ # for item in skill_abilities:
+ # required_items.append(item.itemName)
+ #
+ # for item in stat_abilities:
+ # required_items.extend([item.itemName for _ in range(1)])
+
+ possible_starting_regions = [f"Region: {region}" for region, level in world_battle_levels.items() if
+ level <= min(self.options.logic_difficulty.value, 3)]
+ starting_region = self.random.choice(possible_starting_regions)
+
+ self.multiworld.push_precollected(self.create_item(starting_region))
+ for item in region_unlock_items:
+ if item.itemName != starting_region:
+ required_items.append(item.itemName)
+
+ starting_character = party_member_items[0]
+
+ self.multiworld.push_precollected(self.create_item(starting_character.itemName))
+ for party_member in party_member_items:
+ if party_member == starting_character:
+ continue
+ required_items.append(party_member.itemName)
+
+ unfilled_locations = len(self.multiworld.get_unfilled_locations(self.player))
+
+ items_remaining = unfilled_locations - len(required_items)
+
+ for _ in self.options.exclude_locations.value:
+ self.multiworld.itempool.append(self.create_filler())
+ items_remaining -= 1
+
+ traps_remaining = int(items_remaining * self.options.trap_percentage.value / 100)
+ items_remaining = items_remaining - traps_remaining
+
+ for itemName in required_items:
+ self.multiworld.itempool.append(self.create_item(itemName))
+
+ useful_items = []
+ for item in AllItems:
+ if item.progression == ItemClassification.useful:
+ useful_items += [item.itemName]
+
+ self.random.shuffle(useful_items)
+
+ traps = [trap.itemName for trap in self.random.choices(trap_items, k=traps_remaining)]
+
+ for trap in traps:
+ self.multiworld.itempool.append(self.create_item(trap))
+
+ for i in range(items_remaining):
+ if i > len(useful_items) - 1:
+ self.multiworld.itempool.append(self.create_filler())
+ else:
+ self.multiworld.itempool.append(self.create_item(useful_items[i]))
+
+ def create_item(self, name: str) -> Item:
+ item = item_table[name]
+ return FFXItem(item.itemName, item.progression, item.itemID, self.player)
+
+ def set_rules(self) -> None:
+ set_rules(self)
+
+ def generate_basic(self) -> None:
+ pass
+
+ def fill_slot_data(self) -> dict[str, Any]:
+ slot_data = {
+ "SeedId": self.multiworld.get_out_file_name_base(self.player),
+ # Options
+ "goal_requirement": self.options.goal_requirement.value,
+ "required_party_members": self.options.required_party_members.value,
+ "sphere_grid_randomization": self.options.sphere_grid_randomization.value,
+ "super_bosses": self.options.super_bosses.value,
+ "mini_games": self.options.mini_games.value,
+ "logic_difficulty": self.options.logic_difficulty.value,
+ "recruit_sanity": self.options.recruit_sanity.value
+ }
+ return slot_data
+
+ def generate_output(self, output_directory: str) -> None:
+
+ # Visualize regions
+ visualize_regions(self.multiworld.get_region("Menu", self.player), f"ffx {self.player}.puml", show_entrance_names=True)
+
+ generate_output(self, self.player, output_directory)
\ No newline at end of file
diff --git a/worlds/ffx/archipelago.json b/worlds/ffx/archipelago.json
new file mode 100644
index 000000000000..68edb9de47c9
--- /dev/null
+++ b/worlds/ffx/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Final Fantasy X",
+ "authors": [ "Rurusachi" ],
+ "minimum_ap_version": "0.6.4",
+ "world_version": "0.1.10"
+}
\ No newline at end of file
diff --git a/worlds/ffx/client.py b/worlds/ffx/client.py
new file mode 100644
index 000000000000..9b8017f250a7
--- /dev/null
+++ b/worlds/ffx/client.py
@@ -0,0 +1,12 @@
+from typing import TYPE_CHECKING, Dict, Set, Tuple, List
+import struct
+
+from NetUtils import ClientStatus
+
+import asyncio
+import Utils
+import Patch
+from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess
+
+class FFXClient:
+ pass
diff --git a/worlds/ffx/data/regions.json b/worlds/ffx/data/regions.json
new file mode 100644
index 000000000000..3c5a28950f2f
--- /dev/null
+++ b/worlds/ffx/data/regions.json
@@ -0,0 +1,878 @@
+[
+ {
+ "name": "Baaj Temple 1st visit",
+ "id": 0,
+ "treasures": [0, 1, 2, 3, 6, 7, 219, 213],
+ "party_members": [],
+ "bosses": [0],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [1],
+ "rules": ["Baaj Temple"]
+ },
+ {
+ "name": "Al Bhed Ship",
+ "id": 1,
+ "treasures": [296],
+ "party_members": [],
+ "bosses": [1],
+ "overdrives": [],
+ "other": [1],
+ "recruits": [],
+ "leads_to": [2],
+ "rules": []
+ },
+ {
+ "name": "Baaj Temple 2nd visit",
+ "id": 2,
+ "treasures": [204, 205, 5],
+ "party_members": [13],
+ "bosses": [48],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": ["Geosgaeno"]
+ },
+
+ {
+ "name": "Besaid Island 1st visit",
+ "id": 3,
+ "treasures": [268, 9, 283, 285, 284, 282, 90, 91, 92, 13, 14, 215, 216, 15, 459, 290, 288, 287, 289, 286],
+ "party_members": [1, 4, 5, 8],
+ "bosses": [],
+ "overdrives": [21],
+ "other": [0, 2],
+ "recruits": [19],
+ "leads_to": [4],
+ "rules": ["Besaid"]
+ },
+ {
+ "name": "S.S. Liki 1st visit",
+ "id": 4,
+ "treasures": [16],
+ "party_members": [3],
+ "bosses": [3, 4],
+ "overdrives": [],
+ "other": [3],
+ "recruits": [],
+ "leads_to": [5],
+ "rules": ["Sin Fin", "Sinspawn Echuilles"]
+ },
+ {
+ "name": "Besaid Island 2nd visit",
+ "id": 5,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [2],
+ "overdrives": [],
+ "other": [28],
+ "recruits": [],
+ "leads_to": [6],
+ "rules": []
+ },
+ {
+ "name": "S.S. Liki 2nd visit",
+ "id": 6,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [29],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Kilika 1st visit: Pre-Geneaux",
+ "id": 7,
+ "treasures": [17, 18, 27, 28, 293, 295, 29, 291, 292],
+ "party_members": [],
+ "bosses": [5],
+ "overdrives": [],
+ "other": [4],
+ "recruits": [18],
+ "leads_to": [8],
+ "rules": ["Kilika"]
+ },
+
+ {
+ "name": "Kilika 1st visit: Post-Geneaux",
+ "id": 8,
+ "treasures": [19],
+ "party_members": [9],
+ "bosses": [6],
+ "overdrives": [],
+ "other": [],
+ "recruits": [8],
+ "leads_to": [9],
+ "rules": ["Sinspawn Geneaux"]
+ },
+ {
+ "name": "S.S. Winno 1st visit",
+ "id": 9,
+ "treasures": [31],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [5, 36],
+ "recruits": [5],
+ "leads_to": [10],
+ "rules": []
+ },
+ {
+ "name": "Kilika 2nd visit",
+ "id": 10,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [11],
+ "rules": []
+ },
+ {
+ "name": "S.S. Winno 2nd visit",
+ "id": 11,
+ "treasures": [365],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Luca 1st visit: Pre-Oblitzerator",
+ "id": 12,
+ "treasures": [36, 33, 34, 32, 35, 206, 37],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [6, 7],
+ "recruits": [],
+ "leads_to": [13],
+ "rules": ["Luca"]
+ },
+ {
+ "name": "Luca 1st visit: Post-Oblitzerator",
+ "id": 13,
+ "treasures": [497, 93, 271, 244],
+ "party_members": [2],
+ "bosses": [7],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [14],
+ "rules": ["Oblitzerator"]
+ },
+ {
+ "name": "Luca 2nd visit",
+ "id": 14,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [30],
+ "recruits": [1, 4, 12, 15, 16, 21, 23, 24],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Mi'ihen Highroad 1st visit: Pre-Chocobo Eater",
+ "id": 15,
+ "treasures": [311, 309, 310, 38, 44, 313, 312, 317, 318, 45, 316, 314, 315, 46, 423, 97],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [8],
+ "recruits": [14],
+ "leads_to": [16],
+ "rules": ["Mi'ihen Highroad"]
+ },
+ {
+ "name": "Mi'ihen Highroad 1st visit: Post-Chocobo Eater",
+ "id": 16,
+ "treasures": [42, 43, 40, 41, 39, 269],
+ "party_members": [],
+ "bosses": [8],
+ "overdrives": [],
+ "other": [9],
+ "recruits": [],
+ "leads_to": [17],
+ "rules": ["Chocobo Eater"]
+ },
+ {
+ "name": "Mi'ihen Highroad 2nd visit",
+ "id": 17,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [31],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Mushroom Rock Road 1st visit: Pre-Sinspawn Gui",
+ "id": 18,
+ "treasures": [321, 319, 323, 322, 320, 324, 48, 325, 50, 49, 327, 57, 326, 328, 52, 51],
+ "party_members": [7],
+ "bosses": [],
+ "overdrives": [],
+ "other": [10],
+ "recruits": [],
+ "leads_to": [19],
+ "rules": ["Mushroom Rock Road"]
+ },
+ {
+ "name": "Mushroom Rock Road 1st visit: Post-Sinspawn Gui",
+ "id": 19,
+ "treasures": [98],
+ "party_members": [],
+ "bosses": [9, 10],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [20, 21],
+ "rules": ["Sinspawn Gui"]
+ },
+ {
+ "name": "Mushroom Rock Road 2nd visit",
+ "id": 20,
+ "treasures": [99 ,47],
+ "party_members": [],
+ "bosses": [45, 46, 47],
+ "overdrives": [],
+ "other": [32],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Djose 1st visit",
+ "id": 21,
+ "treasures": [54, 298, 300, 299, 301, 297, 55, 58, 59, 61, 62, 63, 484, 60, 302, 303, 304],
+ "party_members": [10],
+ "bosses": [],
+ "overdrives": [],
+ "other": [11],
+ "recruits": [6],
+ "leads_to": [22],
+ "rules": ["Djose"]
+ },
+ {
+ "name": "Djose 2nd visit",
+ "id": 22,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Moonflow 1st visit: Pre-Extractor",
+ "id": 23,
+ "treasures": [376, 197, 377, 378, 198, 199],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [24],
+ "rules": ["Moonflow"]
+ },
+ {
+ "name": "Moonflow 1st visit: Post-Extractor",
+ "id": 24,
+ "treasures": [200, 201, 202],
+ "party_members": [6],
+ "bosses": [12],
+ "overdrives": [],
+ "other": [12],
+ "recruits": [10],
+ "leads_to": [25],
+ "rules": ["Extractor"]
+ },
+ {
+ "name": "Moonflow 2nd visit",
+ "id": 25,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [33],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Guadosalam 1st visit",
+ "id": 26,
+ "treasures": [65, 64, 66, 67, 218, 272],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [13, 37],
+ "recruits": [22],
+ "leads_to": [27],
+ "rules": ["Guadosalam"]
+ },
+ {
+ "name": "Guadosalam 2nd visit",
+ "id": 27,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Thunder Plains 1st visit",
+ "id": 28,
+ "treasures": [178, 179, 180, 181, 345, 182, 183, 185, 184, 188, 189, 190, 191, 192, 193, 194, 278],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [14],
+ "recruits": [9],
+ "leads_to": [29],
+ "rules": ["Thunder Plains"]
+ },
+ {
+ "name": "Thunder Plains 2nd visit",
+ "id": 29,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [13],
+ "overdrives": [],
+ "other": [34],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Macalania Woods 1st visit: Pre-Spherimorph",
+ "id": 30,
+ "treasures": [69, 68, 70, 277, 73],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [15],
+ "recruits": [],
+ "leads_to": [31],
+ "rules": ["Macalania"]
+ },
+ {
+ "name": "Macalania Woods 1st visit: Post-Spherimorph",
+ "id": 31,
+ "treasures": [177],
+ "party_members": [],
+ "bosses": [14],
+ "overdrives": [],
+ "other": [27],
+ "recruits": [],
+ "leads_to": [32],
+ "rules": ["Spherimorph"]
+ },
+ {
+ "name": "Lake Macalania 1st visit: Pre-Crawler",
+ "id": 32,
+ "treasures": [76],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [16],
+ "recruits": [],
+ "leads_to": [33],
+ "rules": []
+ },
+ {
+ "name": "Lake Macalania 1st visit: Post-Crawler",
+ "id": 33,
+ "treasures": [78, 305, 85, 83, 84, 87, 308, 307, 306, 86],
+ "party_members": [],
+ "bosses": [15],
+ "overdrives": [],
+ "other": [],
+ "recruits": [7],
+ "leads_to": [34],
+ "rules": ["Crawler"]
+ },
+ {
+ "name": "Lake Macalania 1st visit: Post-Seymour/Anima",
+ "id": 34,
+ "treasures": [485, 77],
+ "party_members": [11],
+ "bosses": [16],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [35],
+ "rules": ["Seymour/Anima"]
+ },
+ {
+ "name": "Lake Macalania 1st visit: Post-Wendigo",
+ "id": 35,
+ "treasures": [80, 79],
+ "party_members": [],
+ "bosses": [17],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [36],
+ "rules": ["Wendigo"]
+ },
+ {
+ "name": "Lake Macalania 2nd visit",
+ "id": 36,
+ "treasures": [111],
+ "party_members": [],
+ "bosses": [18],
+ "overdrives": [],
+ "other": [38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Bikanel 1st visit",
+ "id": 37,
+ "treasures": [346, 210, 347, 211, 348, 351, 350, 349, 212, 352, 354, 353, 358, 359, 355, 273, 356, 357],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [19, 18, 17],
+ "recruits": [],
+ "leads_to": [38],
+ "rules": ["Bikanel"]
+ },
+ {
+ "name": "Home 1st visit",
+ "id": 38,
+ "treasures": [367, 360, 396, 395, 361, 397, 362, 363, 364],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [21, 20],
+ "recruits": [],
+ "leads_to": [39],
+ "rules": []
+ },
+ {
+ "name": "Bikanel 2nd visit",
+ "id": 39,
+ "treasures": [279],
+ "party_members": [],
+ "bosses": [19],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Airship 1st visit: Pre-Evrae",
+ "id": 40,
+ "treasures": [375, 405],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [2, 13, 20],
+ "leads_to": [42],
+ "rules": ["Airship"]
+ },
+ {
+ "name": "Airship 1st visit: Post-Evrae",
+ "id": 42,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [20],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [43],
+ "rules": ["Evrae"]
+ },
+ {
+ "name": "Airship 2nd visit: Sin Fins + Genais",
+ "id": 43,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [21, 22, 23],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [44],
+ "rules": ["Airship Sin"]
+ },
+ {
+ "name": "Airship 2nd visit: Overdrive Sin",
+ "id": 44,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [24],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [45],
+ "rules": ["Overdrive Sin"]
+ },
+ {
+ "name": "Airship 3rd visit",
+ "id": 45,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [25],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Bevelle 1st visit: Pre-Isaaru",
+ "id": 46,
+ "treasures": [217, 102, 110, 105, 104, 109, 106, 107, 108],
+ "party_members": [12],
+ "bosses": [],
+ "overdrives": [],
+ "other": [22],
+ "recruits": [],
+ "leads_to": [47],
+ "rules": ["Bevelle"]
+ },
+ {
+ "name": "Bevelle 1st visit: Post-Isaaru",
+ "id": 47,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [26],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [48],
+ "rules": ["Isaaru"]
+ },
+ {
+ "name": "Bevelle 1st visit: Post-Evrae Altana",
+ "id": 48,
+ "treasures": [101, 100],
+ "party_members": [],
+ "bosses": [27],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [49],
+ "rules": ["Evrae Altana"]
+ },
+ {
+ "name": "Bevelle 1st visit: Post-Seymour Natus",
+ "id": 49,
+ "treasures": [75],
+ "party_members": [],
+ "bosses": [28],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [50],
+ "rules": ["Seymour Natus"]
+ },
+ {
+ "name": "Bevelle 2nd visit",
+ "id": 50,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Calm Lands 1st visit: Pre-Defender X",
+ "id": 51,
+ "treasures": [116, 115, 117],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [23],
+ "recruits": [11, 17],
+ "leads_to": [52, 53, 57],
+ "rules": ["Calm Lands"]
+ },
+ {
+ "name": "Remiem Temple",
+ "id": 52,
+ "treasures": [176, 379, 381, 383, 385, 334, 388, 390, 392, 275],
+ "party_members": [15],
+ "bosses": [],
+ "overdrives": [],
+ "other": [24],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+ {
+ "name": "Calm Lands 1st visit: Post-Defender X",
+ "id": 53,
+ "treasures": [118, 337, 338, 339, 340, 114, 274],
+ "party_members": [],
+ "bosses": [29],
+ "overdrives": [],
+ "other": [],
+ "recruits": [3],
+ "leads_to": [54, 55],
+ "rules": ["Defender X"]
+ },
+ {
+ "name": "Calm Lands 2nd visit",
+ "id": 54,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Cavern of the Stolen Fayth 1st visit",
+ "id": 55,
+ "treasures": [120, 121, 122, 123, 124, 125, 126],
+ "party_members": [14],
+ "bosses": [],
+ "overdrives": [],
+ "other": [25],
+ "recruits": [],
+ "leads_to": [56],
+ "rules": ["Cavern of the Stolen Fayth"]
+ },
+
+ {
+ "name": "Cavern of the Stolen Fayth 2nd visit",
+ "id": 56,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [31],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+
+ {
+ "name": "Monster Arena",
+ "id": 57,
+ "treasures": [113],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+
+ {
+ "name": "Mt. Gagazet 1st visit: Pre-Biran and Yenke",
+ "id": 58,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [32],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [59],
+ "rules": ["Mt. Gagazet"]
+ },
+ {
+ "name": "Mt. Gagazet 1st visit: Post-Biran and Yenke",
+ "id": 59,
+ "treasures": [128, 129, 130, 131, 132],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [35],
+ "recruits": [],
+ "leads_to": [60],
+ "rules": ["Biran and Yenke"]
+ },
+ {
+ "name": "Mt. Gagazet 1st visit: Post-Seymour Flux",
+ "id": 60,
+ "treasures": [270, 136, 137, 138, 139, 135],
+ "party_members": [],
+ "bosses": [33],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [61],
+ "rules": ["Seymour Flux"]
+ },
+ {
+ "name": "Mt. Gagazet 2nd visit",
+ "id": 61,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [34, 35],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": ["Sanctuary Keeper"]
+ },
+
+ {
+ "name": "Zanarkand Ruins 1st visit: Pre-Spectral Keeper",
+ "id": 62,
+ "treasures": [145, 146, 147, 148, 149, 150, 209],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [63],
+ "rules": ["Zanarkand Ruins"]
+ },
+ {
+ "name": "Zanarkand Ruins 1st visit: Post-Spectral Keeper",
+ "id": 63,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [36],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [64],
+ "rules": ["Spectral Keeper"]
+ },
+ {
+ "name": "Zanarkand Ruins 1st visit: Post-Yunalesca",
+ "id": 64,
+ "treasures": [267],
+ "party_members": [],
+ "bosses": [37],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [65],
+ "rules": ["Yunalesca"]
+ },
+ {
+ "name": "Zanarkand Ruins 2nd visit",
+ "id": 65,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [38],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": []
+ },
+
+ {
+ "name": "Sin: Pre-Seymour Omnis",
+ "id": 66,
+ "treasures": [166, 167, 163, 164, 165],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [67],
+ "rules": ["Sin"]
+ },
+ {
+ "name": "Sin: Post-Seymour Omnis",
+ "id": 67,
+ "treasures": [168, 169, 170, 171, 174, 172, 173, 175],
+ "party_members": [],
+ "bosses": [39],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [68],
+ "rules": ["Seymour Omnis"]
+ },
+ {
+ "name": "Sin: Braska's Final Aeon",
+ "id": 68,
+ "treasures": [],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": ["Braska's Final Aeon"]
+ },
+
+ {
+ "name": "Omega Ruins: Pre-Ultima Weapon",
+ "id": 69,
+ "treasures": [330],
+ "party_members": [],
+ "bosses": [],
+ "overdrives": [],
+ "other": [26],
+ "recruits": [],
+ "leads_to": [70],
+ "rules": ["Omega Ruins"]
+ },
+ {
+ "name": "Omega Ruins: Post-Ultima Weapon",
+ "id": 70,
+ "treasures": [331],
+ "party_members": [],
+ "bosses": [43],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [71],
+ "rules": ["Ultima Weapon"]
+ },
+ {
+ "name": "Omega Ruins: Post-Omega Weapon",
+ "id": 71,
+ "treasures": [332],
+ "party_members": [],
+ "bosses": [44],
+ "overdrives": [],
+ "other": [],
+ "recruits": [],
+ "leads_to": [],
+ "rules": ["Omega Weapon"]
+ }
+]
\ No newline at end of file
diff --git a/worlds/ffx/docs/en_Final Fantasy X.md b/worlds/ffx/docs/en_Final Fantasy X.md
new file mode 100644
index 000000000000..d57a4d167ef4
--- /dev/null
+++ b/worlds/ffx/docs/en_Final Fantasy X.md
@@ -0,0 +1,26 @@
+# Final Fantasy X
+
+## Setup:
+
+
+## Things to know:
+
+## What is randomized?:
+The world is split into regions which can be played in any order.
+You can receive equipment, consumables, party members, Aeons and region unlocks from other players depending on settings.
+
+## What are the locations?
+Treasures (chests, rewards from NPCs) and Bosses.
+
+## What is the goal?
+Defeat Yu Yevon. Depending on options the final battle may be locked until you find all party members, or complete all
+(pilgrimage) temple regions.
+
+## Known issues:
+
+## Credits:
+Peppy and Evelyn - Developers of Fahrenheit (modding framework for FFX). They've also provided a lot of support.\
+Omnises - Modified CSR for use with AP and provided some help with the APWorld.\
+Andrewki - Created the Poptracker pack and contributed to the client, APWorld, and the modified CSR.\
+Pepen - Helped a bunch by organizing all the testing.\
+Aqua - Created the initial FFX thread in the AP Discord.
\ No newline at end of file
diff --git a/worlds/ffx/docs/setup_en.md b/worlds/ffx/docs/setup_en.md
new file mode 100644
index 000000000000..01e0fcd8f850
--- /dev/null
+++ b/worlds/ffx/docs/setup_en.md
@@ -0,0 +1 @@
+# Final Fantasy X Setup Guide
diff --git a/worlds/ffx/generate.py b/worlds/ffx/generate.py
new file mode 100644
index 000000000000..37fbc8b00a3e
--- /dev/null
+++ b/worlds/ffx/generate.py
@@ -0,0 +1,66 @@
+import os
+import pkgutil
+import struct
+import zipfile
+import json
+import typing
+
+from settings import get_settings
+from worlds.AutoWorld import World
+from worlds.Files import APProcedurePatch, APTokenMixin, APTokenTypes, APPatch
+from .locations import location_types, get_location_type
+
+if typing.TYPE_CHECKING:
+ from .__init__ import FFXWorld
+else:
+ FFXWorld = object
+
+
+class APFFXFile(APPatch):
+ game = "Final Fantasy X"
+ def get_manifest(self):
+ manifest = super().get_manifest()
+ manifest["patch_file_ending"] = ".apffx"
+ return manifest
+
+
+def generate_output(world: FFXWorld, player: int, output_directory: str) -> None:
+ miscellaneous_data = {
+ "SeedId": world.multiworld.get_out_file_name_base(world.player),
+ "GoalRequirement": world.options.goal_requirement.value,
+ "RequiredPartyMembers": world.options.required_party_members.value,
+ "APMultiplier": world.options.ap_multiplier.value,
+ }
+
+ locations: dict[str, list[dict[str, int | str] | int] | str] = {x: list() for x in location_types.values()}
+
+ for location in world.multiworld.get_filled_locations(player):
+ if location.is_event:
+ continue
+ if location.item.player != player:
+ item_id = 0
+ else:
+ item_id = location.item.code
+ locations[get_location_type(location.address)].append({"location_name": location.name,
+ "location_id": location.address & 0x0FFF,
+ "item_id": item_id,
+ "item_name": location.item.name,
+ "player_name": world.multiworld.get_player_name(location.item.player)})
+
+ starting_items: list[int] = list()
+
+ for item in world.multiworld.precollected_items[player]:
+ starting_items.append(item.code)
+ locations["StartingItems"] = starting_items
+
+
+ file_path = os.path.join(output_directory, f"{world.multiworld.get_out_file_name_base(world.player)}.json")
+ with open(file_path, "w", encoding="utf-8") as outfile:
+ outfile.write(json.dumps(miscellaneous_data | locations, indent=4))
+
+ #file_path = os.path.join(output_directory, f"{world.multiworld.get_out_file_name_base(world.player)}.apffx")
+ #APFFX = APFFXFile(file_path, player=world.player, player_name=world.multiworld.player_name[world.player])
+ #with zipfile.ZipFile(file_path, mode="w", compression=zipfile.ZIP_DEFLATED,
+ # compresslevel=9) as zf:
+ # zf.writestr("locations.json", json.dumps(locations))
+ # APFFX.write_contents(zf)
diff --git a/worlds/ffx/items.py b/worlds/ffx/items.py
new file mode 100644
index 000000000000..ab480221acab
--- /dev/null
+++ b/worlds/ffx/items.py
@@ -0,0 +1,713 @@
+from typing import NamedTuple
+from itertools import chain
+import re
+
+from BaseClasses import Item, ItemClassification
+
+
+class ItemData(NamedTuple):
+ itemName: str
+ progression: ItemClassification
+ itemID: int = 0x00
+ player: int = 0
+
+
+class FFXItem(Item):
+ itemName: str
+ progression: ItemClassification
+ itemID: int = 0x00
+ player: int = 0
+ game: str = "Final Fantasy X"
+
+
+normalItemOffset = 0x2000
+keyItemOffset = 0xA000
+equipItemOffset = 0x5000
+partyMemberItemOffset = 0xF000
+regionItemOffset = 0xE000
+abilityItemOffset = 0xD000
+gilItemOffset = 0x1000
+trapItemOffset = 0x9000
+
+
+
+
+normal_items: list[ItemData] = [ItemData(f"{x[0]} x {i}", x[1], x[2] | normalItemOffset | (i << 16)) for i in range(10, 100, 10) for x in [
+ ("Potion", ItemClassification.filler, 0x0000),
+ ("Hi-Potion", ItemClassification.filler, 0x0001),
+ ("X-Potion", ItemClassification.filler, 0x0002),
+ ("Mega-Potion", ItemClassification.filler, 0x0003),
+ ("Ether", ItemClassification.filler, 0x0004),
+ ("Turbo Ether", ItemClassification.filler, 0x0005),
+ ("Phoenix Down", ItemClassification.filler, 0x0006),
+ ("Mega Phoenix", ItemClassification.filler, 0x0007),
+ ("Elixir", ItemClassification.filler, 0x0008),
+ ("Megalixir", ItemClassification.filler, 0x0009),
+ ("Antidote", ItemClassification.filler, 0x000A),
+ ("Soft", ItemClassification.filler, 0x000B),
+ ("Eye Drops", ItemClassification.filler, 0x000C),
+ ("Echo Screen", ItemClassification.filler, 0x000D),
+ ("Holy Water", ItemClassification.filler, 0x000E),
+ ("Remedy", ItemClassification.filler, 0x000F),
+ ("Power Distiller", ItemClassification.useful, 0x0010),
+ ("Mana Distiller", ItemClassification.useful, 0x0011),
+ ("Speed Distiller", ItemClassification.useful, 0x0012),
+ ("Ability Distiller", ItemClassification.useful, 0x0013),
+ ("Al Bhed Potion", ItemClassification.useful, 0x0014),
+ ("Healing Water", ItemClassification.useful, 0x0015),
+ ("Tetra Elemental", ItemClassification.useful, 0x0016),
+ ("Antarctic Wind", ItemClassification.useful, 0x0017),
+ ("Arctic Wind", ItemClassification.useful, 0x0018),
+ ("Ice Gem", ItemClassification.useful, 0x0019),
+ ("Bomb Fragment", ItemClassification.useful, 0x001A),
+ ("Bomb Core", ItemClassification.useful, 0x001B),
+ ("Fire Gem", ItemClassification.useful, 0x001C),
+ ("Electro Marble", ItemClassification.useful, 0x001D),
+ ("Lightning Marble", ItemClassification.useful, 0x001E),
+ ("Lightning Gem", ItemClassification.useful, 0x001F),
+ ("Fish Scale", ItemClassification.useful, 0x0020),
+ ("Dragon Scale", ItemClassification.useful, 0x0021),
+ ("Water Gem", ItemClassification.useful, 0x0022),
+ ("Grenade", ItemClassification.useful, 0x0023),
+ ("Frag Grenade", ItemClassification.useful, 0x0024),
+ ("Sleeping Powder", ItemClassification.useful, 0x0025),
+ ("Dream Powder", ItemClassification.useful, 0x0026),
+ ("Silence Grenade", ItemClassification.useful, 0x0027),
+ ("Smoke Bomb", ItemClassification.useful, 0x0028),
+ ("Shadow Gem", ItemClassification.useful, 0x0029),
+ ("Shining Gem", ItemClassification.useful, 0x002A),
+ ("Blessed Gem", ItemClassification.useful, 0x002B),
+ ("Supreme Gem", ItemClassification.useful, 0x002C),
+ ("Poison Fang", ItemClassification.useful, 0x002D),
+ ("Silver Hourglass", ItemClassification.useful, 0x002E),
+ ("Gold Hourglass", ItemClassification.useful, 0x002F),
+ ("Candle of Life", ItemClassification.useful, 0x0030),
+ ("Petrify Grenade", ItemClassification.useful, 0x0031),
+ ("Farplane Shadow", ItemClassification.useful, 0x0032),
+ ("Farplane Wind", ItemClassification.useful, 0x0033),
+ ("[Designer Wallet]", ItemClassification.useful, 0x0034),
+ ("Dark Matter", ItemClassification.useful, 0x0035),
+ ("Chocobo Feather", ItemClassification.useful, 0x0036),
+ ("Chocobo Wing", ItemClassification.useful, 0x0037),
+ ("Lunar Curtain", ItemClassification.useful, 0x0038),
+ ("Light Curtain", ItemClassification.useful, 0x0039),
+ ("Star Curtain", ItemClassification.useful, 0x003A),
+ ("Healing Spring", ItemClassification.useful, 0x003B),
+ ("Mana Spring", ItemClassification.useful, 0x003C),
+ ("Stamina Spring", ItemClassification.useful, 0x003D),
+ ("Soul Spring", ItemClassification.useful, 0x003E),
+ ("Purifying Salt", ItemClassification.useful, 0x003F),
+ ("Stamina Tablet", ItemClassification.useful, 0x0040),
+ ("Mana Tablet", ItemClassification.useful, 0x0041),
+ ("Twin Stars", ItemClassification.useful, 0x0042),
+ ("Stamina Tonic", ItemClassification.useful, 0x0043),
+ ("Mana Tonic", ItemClassification.useful, 0x0044),
+ ("Three Stars", ItemClassification.useful, 0x0045),
+ #("[Power Sphere]", ItemClassification.useful, 0x0046), # Infinite supply
+ #("[Mana Sphere]", ItemClassification.useful, 0x0047), # Infinite supply
+ #("[Speed Sphere]", ItemClassification.useful, 0x0048), # Infinite supply
+ #("[Ability Sphere]", ItemClassification.useful, 0x0049), # Infinite supply
+ ("[Fortune Sphere]", ItemClassification.useful, 0x004A),
+ ("[Attribute Sphere]", ItemClassification.useful, 0x004B),
+ ("[Special Sphere]", ItemClassification.useful, 0x004C),
+ ("[Skill Sphere]", ItemClassification.useful, 0x004D),
+ ("[Wht Magic Sphere]", ItemClassification.useful, 0x004E),
+ ("[Blk Magic Sphere]", ItemClassification.useful, 0x004F),
+ ("[Master Sphere]", ItemClassification.useful, 0x0050),
+ ("[Lv. 1 Key Sphere]", ItemClassification.useful, 0x0051),
+ ("[Lv. 2 Key Sphere]", ItemClassification.useful, 0x0052),
+ ("[Lv. 3 Key Sphere]", ItemClassification.useful, 0x0053),
+ ("[Lv. 4 Key Sphere]", ItemClassification.useful, 0x0054),
+ ("[HP Sphere]", ItemClassification.useful, 0x0055),
+ ("[MP Sphere]", ItemClassification.useful, 0x0056),
+ ("[Strength Sphere]", ItemClassification.useful, 0x0057),
+ ("[Defense Sphere]", ItemClassification.useful, 0x0058),
+ ("[Magic Sphere]", ItemClassification.useful, 0x0059),
+ ("[Magic Def Sphere]", ItemClassification.useful, 0x005A),
+ ("[Agility Sphere]", ItemClassification.useful, 0x005B),
+ ("[Evasion Sphere]", ItemClassification.useful, 0x005C),
+ ("[Accuracy Sphere]", ItemClassification.useful, 0x005D),
+ ("[Luck Sphere]", ItemClassification.useful, 0x005E),
+ ("[Clear Sphere]", ItemClassification.useful, 0x005F),
+ ("[Return Sphere]", ItemClassification.useful, 0x0060),
+ ("[Friend Sphere]", ItemClassification.useful, 0x0061),
+ ("[Teleport Sphere]", ItemClassification.useful, 0x0062),
+ ("[Warp Sphere]", ItemClassification.useful, 0x0063),
+ ("[Map]", ItemClassification.useful, 0x0064),
+ ("[Rename Card]", ItemClassification.useful, 0x0065),
+ ("[Musk]", ItemClassification.useful, 0x0066),
+ ("[Hypello Potion]", ItemClassification.useful, 0x0067),
+ ("[Shining Thorn]", ItemClassification.useful, 0x0068),
+ ("[Pendulum]", ItemClassification.useful, 0x0069),
+ ("[Amulet]", ItemClassification.useful, 0x006A),
+ ("[Door to Tomorrow]", ItemClassification.useful, 0x006B),
+ ("[Wings to Discovery]", ItemClassification.useful, 0x006C),
+ ("[Gambler's Spirit]", ItemClassification.useful, 0x006D),
+ ("[Underdog's Secret]", ItemClassification.useful, 0x006E),
+ ("[Winning Formula]", ItemClassification.useful, 0x006F),
+]]
+
+key_items: list[ItemData] = [ItemData(x[0], x[1], x[2] | keyItemOffset) for x in [
+ ("Withered Bouquet", ItemClassification.progression, 0x0000),
+ ("Flint", ItemClassification.progression, 0x0001),
+ ("Cloudy Mirror", ItemClassification.progression, 0x0002),
+ ("Celestial Mirror", ItemClassification.progression, 0x0003),
+ ("Al Bhed Primer I", ItemClassification.progression, 0x0004),
+ ("Al Bhed Primer II", ItemClassification.progression, 0x0005),
+ ("Al Bhed Primer III", ItemClassification.progression, 0x0006),
+ ("Al Bhed Primer IV", ItemClassification.progression, 0x0007),
+ ("Al Bhed Primer V", ItemClassification.progression, 0x0008),
+ ("Al Bhed Primer VI", ItemClassification.progression, 0x0009),
+ ("Al Bhed Primer VII", ItemClassification.progression, 0x000A),
+ ("Al Bhed Primer VIII", ItemClassification.progression, 0x000B),
+ ("Al Bhed Primer IX", ItemClassification.progression, 0x000C),
+ ("Al Bhed Primer X", ItemClassification.progression, 0x000D),
+ ("Al Bhed Primer XI", ItemClassification.progression, 0x000E),
+ ("Al Bhed Primer XII", ItemClassification.progression, 0x000F),
+ ("Al Bhed Primer XIII", ItemClassification.progression, 0x0010),
+ ("Al Bhed Primer XIV", ItemClassification.progression, 0x0011),
+ ("Al Bhed Primer XV", ItemClassification.progression, 0x0012),
+ ("Al Bhed Primer XVI", ItemClassification.progression, 0x0013),
+ ("Al Bhed Primer XVII", ItemClassification.progression, 0x0014),
+ ("Al Bhed Primer XVIII", ItemClassification.progression, 0x0015),
+ ("Al Bhed Primer XIX", ItemClassification.progression, 0x0016),
+ ("Al Bhed Primer XX", ItemClassification.progression, 0x0017),
+ ("Al Bhed Primer XXI", ItemClassification.progression, 0x0018),
+ ("Al Bhed Primer XXII", ItemClassification.progression, 0x0019),
+ ("Al Bhed Primer XXIII", ItemClassification.progression, 0x001A),
+ ("Al Bhed Primer XXIV", ItemClassification.progression, 0x001B),
+ ("Al Bhed Primer XXV", ItemClassification.progression, 0x001C),
+ ("Al Bhed Primer XXVI", ItemClassification.progression, 0x001D),
+ ("Summoner's Soul", ItemClassification.progression, 0x001E),
+ ("Aeon's Soul", ItemClassification.progression, 0x001F),
+ ("Jecht's Sphere", ItemClassification.progression, 0x0020),
+ ("Rusty Sword", ItemClassification.progression, 0x0021),
+ # ("", ItemClassification.progression, 0x0022),
+ ("Sun Crest", ItemClassification.progression, 0x0023),
+ ("Sun Sigil", ItemClassification.progression, 0x0024),
+ ("Moon Crest", ItemClassification.progression, 0x0025),
+ ("Moon Sigil", ItemClassification.progression, 0x0026),
+ ("Mars Crest", ItemClassification.progression, 0x0027),
+ ("Mars Sigil", ItemClassification.progression, 0x0028),
+ ("Mark of Conquest", ItemClassification.progression, 0x0029),
+ ("Saturn Crest", ItemClassification.progression, 0x002A),
+ ("Saturn Sigil", ItemClassification.progression, 0x002B),
+ ("Jupiter Crest", ItemClassification.progression, 0x002C),
+ ("Jupiter Sigil", ItemClassification.progression, 0x002D),
+ ("Venus Crest", ItemClassification.progression, 0x002E),
+ ("Venus Sigil", ItemClassification.progression, 0x002F),
+ ("Mercury Crest", ItemClassification.progression, 0x0030),
+ ("Mercury Sigil", ItemClassification.progression, 0x0031),
+ ("Blossom Crown", ItemClassification.progression, 0x0032),
+ ("Flower Scepter", ItemClassification.progression, 0x0033),
+ # ("", ItemClassification.progression, 0x0034),
+ # ("", ItemClassification.progression, 0x0035),
+ # ("", ItemClassification.progression, 0x0036),
+ # ("", ItemClassification.progression, 0x0037),
+ # ("", ItemClassification.progression, 0x0038),
+ # ("", ItemClassification.progression, 0x0039),
+ # ("", ItemClassification.progression, 0x003A),
+ # ("", ItemClassification.progression, 0x003B),
+ # ("", ItemClassification.progression, 0x003C),
+ # ("", ItemClassification.progression, 0x003D),
+ # ("", ItemClassification.progression, 0x003E),
+ # ("", ItemClassification.progression, 0x003F),
+]]
+
+equip_items: list[ItemData] = [ItemData(x[0], x[1], x[2] | equipItemOffset) for x in [
+ ("Weapon (Tidus): Crystal Sword", ItemClassification.useful , 0x0000), # Offset=0014 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Firestrike [801Eh], Icestrike [8022h], Lightningstrike [8026h], Waterstrike [802Ah]} }
+ ("Weapon (Tidus): Brotherhood", ItemClassification.progression, 0x0001), # Offset=0024 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Strength +5% [8063h], Strength +10% [8064h], Waterstrike [802Ah], Sensor [8000h]}, Brotherhood }
+ ("Weapon (Yuna): Astral Rod", ItemClassification.useful , 0x0002), # Offset=0034 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {One MP Cost [800Dh], Empty, Empty, Empty} }
+ ("Weapon (Lulu): Onion Knight", ItemClassification.progression, 0x0003), # Offset=0044 Weapon [00h], Formula=Celestial MP-based [12h], Power=16, Crit=3%, Slots=4 {No AP [8014h], Empty, Empty, Empty}, Celestial }
+ ("Weapon (Tidus): Flametongue", ItemClassification.useful , 0x0004), # Offset=0054 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Firestrike [801Eh]} }
+ ("Weapon (Yuna): Rod of Wisdom", ItemClassification.useful , 0x0005), # Offset=0064 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Magic +5% [8067h], !Magic +3% [8066h], !Sensor [8000h]} }
+ ("Armor (Kimahri): Red Armlet", ItemClassification.useful , 0x0006), # Offset=0074 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=3 {Fire Ward [801Fh], Ice Ward [8023h], Lightning Ward [8027h]} }
+ ("Armor (Lulu): Serene Bangle", ItemClassification.useful , 0x0007), # Offset=0084 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Berserk Ward [8051h]} }
+ ("Weapon (Wakka): Scout I", ItemClassification.useful , 0x0008), # Offset=0094 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Icestrike [8022h], Sensor [8000h]} }
+ ("Armor (Tidus): NulBlaze Shield", ItemClassification.useful , 0x0009), # Offset=00A4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {SOS NulBlaze [8061h]} }
+ ("Weapon (Kimahri): Tidal Spear", ItemClassification.useful , 0x000a), # Offset=00B4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Piercing [800Bh], Waterstrike [802Ah]} }
+ ("Weapon (Tidus): Ice Brand", ItemClassification.useful , 0x000b), # Offset=00C4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Icestrike [8022h]} }
+ ("Weapon (Auron): Thunder Blade", ItemClassification.useful , 0x000c), # Offset=00D4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Piercing [800Bh], Lightningstrike [8026h]} }
+ ("Weapon (Wakka): Scout II", ItemClassification.useful , 0x000d), # Offset=00E4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Lightningstrike [8026h], Sensor [8000h]} }
+ ("Weapon (Kimahri): Heat Lance", ItemClassification.useful , 0x000e), # Offset=00F4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Piercing [800Bh], Firestrike [801Eh]} }
+ ("Armor (Auron): Serene Bracer", ItemClassification.useful , 0x000f), # Offset=0104 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {HP +5% [8072h], Berserk Ward [8051h]} }
+ ("Armor (Lulu): Bright Bangle", ItemClassification.useful , 0x0010), # Offset=0114 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Dark Ward [8049h], Empty} }
+ ("Armor (Yuna): Serum Ring", ItemClassification.useful , 0x0011), # Offset=0124 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Lightning Ward [8027h], Poison Ward [803Dh]} }
+ ("Armor (Kimahri): Serene Armlet", ItemClassification.useful , 0x0012), # Offset=0134 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Dark Ward [8049h], Berserk Ward [8051h]} }
+ ("Weapon (Wakka): All-Rounder", ItemClassification.useful , 0x0013), # Offset=0144 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Strength +3% [8062h], Strength +5% [8063h]} }
+ ("Weapon (Lulu): Sleepy Cait Sith", ItemClassification.useful , 0x0014), # Offset=0154 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Sleeptouch [803Fh]} }
+ ("Armor (Yuna): Lucid Ring I", ItemClassification.useful , 0x0015), # Offset=0164 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Silence Ward [8045h], Confuse Ward [804Fh]} }
+ ("Weapon (Tidus): Avenger I", ItemClassification.useful , 0x0016), # Offset=0174 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Counterattack [8003h]} }
+ ("Weapon (Lulu): Quiet Cait Sith", ItemClassification.useful , 0x0017), # Offset=0184 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Silencetouch [8043h], Magic +5% [8067h]} }
+ ("Armor (Rikku): Shell Targe", ItemClassification.useful , 0x0018), # Offset=0194 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {SOS Shell [8059h]} }
+ ("Armor (Kimahri): Lucid Armlet", ItemClassification.useful , 0x0019), # Offset=01A4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Poison Ward [803Dh], Confuse Ward [804Fh], Silence Ward [8045h], Empty} }
+ ("Weapon (Wakka): World Champion", ItemClassification.progression, 0x001a), # Offset=01B4 Weapon [00h], Formula=Celestial HP-based [11h], Power=16, Crit=3%, Slots=4 {No AP [8014h], Empty, Empty, Empty}, Celestial }
+ ("Weapon (Wakka): Scout III", ItemClassification.useful , 0x001b), # Offset=01C4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Sensor [8000h]} }
+ ("Weapon (Kimahri): Ice Lance", ItemClassification.useful , 0x001c), # Offset=01D4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Piercing [800Bh], Icestrike [8022h]} }
+ ("Armor (Yuna): Moon Ring", ItemClassification.useful , 0x001d), # Offset=01E4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {SOS Shell [8059h], SOS Protect [805Ah]} }
+ ("Weapon (Auron): Masamune", ItemClassification.progression, 0x001e), # Offset=01F4 Weapon [00h], Formula=Celestial Auron [13h], Power=16, Crit=3%, Slots=4 {No AP [8014h], Empty, Empty, Empty}, Celestial }
+ ("Weapon (Tidus): Avenger II", ItemClassification.useful , 0x001f), # Offset=0204 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Counterattack [8003h]} }
+ ("Weapon (Wakka): Rematch", ItemClassification.useful , 0x0020), # Offset=0214 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Evade & Counter [8004h]} }
+ ("Weapon (Kimahri): Knight Lance I", ItemClassification.useful , 0x0021), # Offset=0224 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=3 {Strength +3% [8062h], Strength +5% [8063h], Strength +10% [8064h]} }
+ ("Armor (Yuna): Lucid Ring II", ItemClassification.useful , 0x0022), # Offset=0234 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=3 {Silence Ward [8045h], Confuse Ward [804Fh], Poison Ward [803Dh]} }
+ ("Weapon (Wakka): Bright Armguard", ItemClassification.useful , 0x0023), # Offset=0244 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Fire Ward [801Fh], Dark Ward [8049h]} }
+ ("Weapon (Yuna): Nirvana", ItemClassification.progression, 0x0024), # Offset=0254 Weapon [00h], Formula=Celestial MP-based [12h], Power=16, Crit=3%, Slots=4 {No AP [8014h], Empty, Empty, Empty}, Celestial }
+ ("Weapon (Tidus): Caladbolg", ItemClassification.progression, 0x0025), # Offset=0264 Weapon [00h], Formula=Celestial HP-based [11h], Power=16, Crit=3%, Slots=4 {No AP [8014h], Empty, Empty, Empty}, Celestial }
+ ("Armor (Kimahri): Tetra Armlet", ItemClassification.useful , 0x0026), # Offset=0274 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {HP +10% [8073h], Empty, Empty, Empty} }
+ ("Weapon (Rikku): Flexible Arm", ItemClassification.useful , 0x0027), # Offset=0284 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Empty, Empty, Empty, Empty} }
+ ("Armor (Auron): Defending Bracer I", ItemClassification.useful , 0x0028), # Offset=0294 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Stoneproof [8038h], Poisonproof [803Ch]} }
+ ("Weapon (Wakka): Pep Talk", ItemClassification.useful , 0x0029), # Offset=02A4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=3 {SOS NulFrost [805Fh], SOS NulShock [8060h], SOS NulBlaze [8061h]} }
+ ("Armor (Yuna): Recovery Ring", ItemClassification.useful , 0x002a), # Offset=02B4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {HP Stroll [801Bh]} }
+ ("Armor (Rikku): Spiritual Targe", ItemClassification.useful , 0x002b), # Offset=02C4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {MP Stroll [801Ch]} }
+ ("Armor (Auron): Defending Bracer II", ItemClassification.useful , 0x002c), # Offset=02D4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Silenceproof [8044h], Darkproof [8048h]} }
+ ("Weapon (Wakka): Turnover", ItemClassification.useful , 0x002d), # Offset=02E4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Magic Counter [8005h], Counterattack [8003h]} }
+ ("Armor (Kimahri): Defending Armlet", ItemClassification.useful , 0x002e), # Offset=02F4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Stoneproof [8038h], Poisonproof [803Ch], Empty, Empty} }
+ ("Armor (Yuna): Phantom Ring I", ItemClassification.useful , 0x002f), # Offset=0304 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Ice Eater [8025h], Fire Eater [8021h], Lightning Eater [8029h], Empty} }
+ ("Weapon (Lulu): Cactuar Wizard", ItemClassification.useful , 0x0030), # Offset=0314 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Half MP Cost [800Ch]} }
+ ("Weapon (Rikku): Warmonger", ItemClassification.useful , 0x0031), # Offset=0324 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Double AP [8012h], !Double Overdrive [800Eh]} }
+ ("Weapon (Kimahri): Wizard Lance", ItemClassification.useful , 0x0032), # Offset=0334 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Magic +3% [8066h], Magic +5% [8067h], Magic +10% [8068h], Empty} }
+ ("Armor (Yuna): Phantom Ring II", ItemClassification.useful , 0x0033), # Offset=0344 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Water Eater [802Dh], Fire Eater [8021h], Lightning Eater [8029h], Empty} }
+ ("Weapon (Wakka): Four-on-One", ItemClassification.useful , 0x0034), # Offset=0354 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Waterstrike [802Ah], Firestrike [801Eh], Lightningstrike [8026h], Icestrike [8022h]} }
+ ("Armor (Auron): Defending Bracer III", ItemClassification.useful , 0x0035), # Offset=0364 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Darkproof [8048h], Deathproof [8030h], Empty, Empty} }
+ ("Weapon (Yuna): Laevatein", ItemClassification.useful , 0x0036), # Offset=0374 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {SOS Overdrive [8010h]} }
+ ("Weapon (Wakka): Water Ball", ItemClassification.useful , 0x0037), # Offset=0384 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Waterstrike [802Ah], Empty} }
+ ("Weapon (Kimahri): Spirit Lance", ItemClassification.progression, 0x0038), # Offset=0394 Weapon [00h], Formula=Celestial HP-based [11h], Power=16, Crit=3%, Slots=4 {No AP [8014h], Empty, Empty, Empty}, Celestial }
+ ("Armor (Yuna): Soft Ring I", ItemClassification.useful , 0x0039), # Offset=03A4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Stone Ward [8039h], HP +5% [8072h]} }
+ ("Weapon (Tidus): Variable Steel I", ItemClassification.useful , 0x003a), # Offset=03B4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Strength +3% [8062h], Strength +5% [8063h]} }
+ ("Weapon (Lulu): Magician Mog", ItemClassification.useful , 0x003b), # Offset=03C4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Magic +3% [8066h], Magic +5% [8067h], Magic +20% [8069h], Empty} }
+ ("Weapon (Yuna): Magistral Rod", ItemClassification.useful , 0x003c), # Offset=03D4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=3 {Half MP Cost [800Ch], Empty, Empty} }
+ ("Weapon (Rikku): Godhand", ItemClassification.progression, 0x003d), # Offset=03E4 Weapon [00h], Formula=Celestial HP-based [11h], Power=16, Crit=3%, Slots=4 {No AP [8014h], Empty, Empty, Empty}, Celestial }
+ ("Armor (Yuna): Tetra Ring I", ItemClassification.useful , 0x003e), # Offset=03F4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {HP +10% [8073h]} }
+ ("Weapon (Tidus): Variable Steel II", ItemClassification.useful , 0x003f), # Offset=0404 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Strength +3% [8062h], Empty, Empty, Empty} }
+ ("Armor (Yuna): Soft Ring II", ItemClassification.useful , 0x0040), # Offset=0414 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Stoneproof [8038h], Empty} }
+ ("Weapon (Kimahri): Shapeshifter", ItemClassification.useful , 0x0041), # Offset=0424 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Magic +20% [8069h], Empty} }
+ ("Weapon (Kimahri): Hunter's Spear", ItemClassification.useful , 0x0042), # Offset=0434 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=3 {Piercing [800Bh], Sensor [8000h], Strength +10% [8064h]} }
+ ("Armor (Yuna): Red Ring", ItemClassification.useful , 0x0043), # Offset=0444 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {HP +10% [8073h], Fire Ward [801Fh]} }
+ ("Armor (Lulu): Tetra Bangle", ItemClassification.useful , 0x0044), # Offset=0454 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {HP +20% [8074h], Empty} }
+ ("Armor (Tidus): Yellow Shield", ItemClassification.useful , 0x0045), # Offset=0464 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Lightningproof [8028h], Empty} }
+ ("Weapon (Wakka): Ace Wizard", ItemClassification.useful , 0x0046), # Offset=0474 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Magic +20% [8069h], Magic +10% [8068h], Magic +5% [8067h], Magic +3% [8066h]} }
+ ("Armor (Yuna): Tetra Ring II", ItemClassification.useful , 0x0047), # Offset=0484 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {HP +10% [8073h], Empty} }
+ ("Armor (Rikku): Victorious", ItemClassification.useful , 0x0048), # Offset=0494 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Lightningproof [8028h], Fireproof [8020h], Iceproof [8024h], Empty} }
+ ("Weapon (Auron): Murasame", ItemClassification.useful , 0x0049), # Offset=04A4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Piercing [800Bh], One MP Cost [800Dh], Empty, Empty} }
+ ("Armor (Yuna): Echo Ring", ItemClassification.useful , 0x004a), # Offset=04B4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {HP +10% [8073h], Silence Ward [8045h]} }
+ ("Weapon (Kimahri): Dragoon Lance", ItemClassification.useful , 0x004b), # Offset=04C4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Magic Counter [8005h], Evade & Counter [8004h], Empty, Empty} }
+ ("Weapon (Rikku): Sonar", ItemClassification.useful , 0x004c), # Offset=04D4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=3 {Initiative [8002h], Poisonstrike [803Ah], Empty} }
+ ("Armor (Lulu): Phantom Bangle", ItemClassification.useful , 0x004d), # Offset=04E4 Armor [01h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Ice Eater [8025h], Fire Eater [8021h], !Water Eater [802Dh]} }
+ ("Weapon (Tidus): Ascalon", ItemClassification.useful , 0x004e), # Offset=04F4 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {Double AP [8012h]} }
+ ("Weapon (Wakka): Prism Ball", ItemClassification.useful , 0x004f), # Offset=0504 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {Magic Counter [8005h], Empty} }
+ ("Weapon (Auron): Stillblade", ItemClassification.useful , 0x0050), # Offset=0514 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=3 {Silencestrike [8042h], Stonestrike [8036h], Empty} }
+ ("Weapon (Yuna): Mage's Staff", ItemClassification.useful , 0x0051), # Offset=0524 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Magic +10% [8068h], Magic +5% [8067h], Magic +3% [8066h], Empty} }
+ ("Weapon (Kimahri): Knight Lance II", ItemClassification.useful , 0x0052), # Offset=0534 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Strength +10% [8064h], Strength +5% [8063h], Strength +3% [8062h], Empty} }
+ ("Weapon (Rikku): Infinity", ItemClassification.useful , 0x0053), # Offset=0544 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=2 {One MP Cost [800Dh], Sensor [8000h]} }
+ ("Weapon (Lulu): Wicked Cait Sith", ItemClassification.useful , 0x0054), # Offset=0554 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=4 {Deathstrike [802Eh], Empty, Empty, Empty} }
+ ("Weapon (Tidus): Hrunting", ItemClassification.useful , 0x0055), # Offset=0564 Weapon [00h], Formula=STR vs DEF [01h], Power=16, Crit=3%, Slots=1 {SOS Overdrive [8010h]} }
+]]
+
+party_member_items: list[ItemData] = [ItemData(x[0], x[1], x[2] | partyMemberItemOffset) for x in [
+ ("Party Member: Tidus", ItemClassification.progression, 0x0000),
+ ("Party Member: Yuna", ItemClassification.progression, 0x0001),
+ ("Party Member: Auron", ItemClassification.progression, 0x0002),
+ ("Party Member: Kimahri", ItemClassification.progression, 0x0003),
+ ("Party Member: Wakka", ItemClassification.progression, 0x0004),
+ ("Party Member: Lulu", ItemClassification.progression, 0x0005),
+ ("Party Member: Rikku", ItemClassification.progression, 0x0006),
+ ("Party Member: Seymour", ItemClassification.progression, 0x0007),
+ ("Party Member: Valefor", ItemClassification.progression, 0x0008),
+ ("Party Member: Ifrit", ItemClassification.progression, 0x0009),
+ ("Party Member: Ixion", ItemClassification.progression, 0x000A),
+ ("Party Member: Shiva", ItemClassification.progression, 0x000B),
+ ("Party Member: Bahamut", ItemClassification.progression, 0x000C),
+ ("Party Member: Anima", ItemClassification.progression, 0x000D),
+ ("Party Member: Yojimbo", ItemClassification.progression, 0x000E),
+ ("Party Member: Magus Sisters", ItemClassification.progression, 0x000F), # Sisters are 0x0f, 0x10, 0x11
+]]
+
+region_unlock_items: list[ItemData] = [ItemData(x[0], x[1], x[2] | regionItemOffset) for x in [
+ #("Region: None", ItemClassification.progression, 0),
+ #("Region: Dream Zanarkand", ItemClassification.progression, 1),
+ ("Region: Baaj Temple", ItemClassification.progression, 2),
+ ("Region: Besaid", ItemClassification.progression, 3),
+ ("Region: Kilika", ItemClassification.progression, 4),
+ ("Region: Luca", ItemClassification.progression, 5),
+ ("Region: Mi'ihen Highroad", ItemClassification.progression, 6),
+ ("Region: Mushroom Rock Road", ItemClassification.progression, 7),
+ ("Region: Djose", ItemClassification.progression, 8),
+ ("Region: Moonflow", ItemClassification.progression, 9),
+ ("Region: Guadosalam", ItemClassification.progression, 10),
+ ("Region: Thunder Plains", ItemClassification.progression, 11),
+ ("Region: Macalania", ItemClassification.progression, 12),
+ ("Region: Bikanel", ItemClassification.progression, 13),
+ ("Region: Airship", ItemClassification.progression, 14),
+ ("Region: Bevelle", ItemClassification.progression, 15),
+ ("Region: Calm Lands", ItemClassification.progression, 16),
+ ("Region: Cavern of the Stolen Fayth", ItemClassification.progression, 17),
+ ("Region: Mt. Gagazet", ItemClassification.progression, 18),
+ ("Region: Zanarkand Ruins", ItemClassification.progression, 19),
+ ("Region: Sin", ItemClassification.progression, 20),
+ ("Region: Omega Ruins", ItemClassification.progression, 21),
+]]
+
+gil_items: list[ItemData] = [ItemData(f"{i*1000} Gil", ItemClassification.filler, (i << 16) | gilItemOffset) for i in range(1, 10)]
+#gil_items: list[ItemData] = [ItemData(x[0], x[1], x[2] | gilItemOffset) for x in [
+# ("1000 Gil", ItemClassification.filler, 0x0000),
+#]]
+
+character_names = [
+ "Tidus",
+ "Yuna",
+ "Auron",
+ "Kimahri",
+ "Wakka",
+ "Lulu",
+ "Rikku",
+ #"Seymour",
+]
+
+aeon_names = [
+ "Valefor",
+ "Ifrit",
+ "Ixion",
+ "Shiva",
+ "Bahamut",
+ "Anima",
+ "Yojimbo",
+ "Magus Sisters",
+]
+
+abilities_per_character: list[ItemData] = [ ItemData(f"{character_names[character]} {ability[0]}", ItemClassification.progression, ability[1] | abilityItemOffset | character << 8) for character in range(7) for ability in [
+ # Lvl 3 lock
+ # Empty node
+ ("Ability: Strength +1", 0x0002),
+ ("Ability: Strength +2", 0x0003),
+ ("Ability: Strength +3", 0x0004),
+ ("Ability: Strength +4", 0x0005),
+
+ ("Ability: Defense +1", 0x0006),
+ ("Ability: Defense +2", 0x0007),
+ ("Ability: Defense +3", 0x0008),
+ ("Ability: Defense +4", 0x0009),
+
+ ("Ability: Magic +1", 0x000A),
+ ("Ability: Magic +2", 0x000B),
+ ("Ability: Magic +3", 0x000C),
+ ("Ability: Magic +4", 0x000D),
+
+ ("Ability: Magic Defense +1", 0x000E),
+ ("Ability: Magic Defense +2", 0x000F),
+ ("Ability: Magic Defense +3", 0x0010),
+ ("Ability: Magic Defense +4", 0x0011),
+
+ ("Ability: Agility +1", 0x0012),
+ ("Ability: Agility +2", 0x0013),
+ ("Ability: Agility +3", 0x0014),
+ ("Ability: Agility +4", 0x0015),
+
+ ("Ability: Luck +1", 0x0016),
+ ("Ability: Luck +2", 0x0017),
+ ("Ability: Luck +3", 0x0018),
+ ("Ability: Luck +4", 0x0019),
+
+ ("Ability: Evasion +1", 0x001A),
+ ("Ability: Evasion +2", 0x001B),
+ ("Ability: Evasion +3", 0x001C),
+ ("Ability: Evasion +4", 0x001D),
+
+ ("Ability: Accuracy +1", 0x001E),
+ ("Ability: Accuracy +2", 0x001F),
+ ("Ability: Accuracy +3", 0x0020),
+ ("Ability: Accuracy +4", 0x0021),
+
+ ("Ability: HP +200", 0x0022),
+ ("Ability: HP +300", 0x0023),
+
+ ("Ability: MP +40", 0x0024),
+ ("Ability: MP +20", 0x0025),
+ ("Ability: MP +10", 0x0026),
+
+ # Lvl 1 lock
+ # Lvl 2 lock
+ # Lvl 4 lock
+
+ ("Ability: Delay Attack", 0x002A),
+ ("Ability: Delay Buster", 0x002B),
+ ("Ability: Sleep Attack", 0x002C),
+ ("Ability: Silence Attack", 0x002D),
+ ("Ability: Dark Attack", 0x002E),
+ ("Ability: Zombie Attack", 0x002F),
+ ("Ability: Sleep Buster", 0x0030),
+ ("Ability: Silence Buster", 0x0031),
+ ("Ability: Dark Buster", 0x0032),
+ ("Ability: Triple Foul", 0x0033),
+ ("Ability: Power Break", 0x0034),
+ ("Ability: Magic Break", 0x0035),
+ ("Ability: Armor Break", 0x0036),
+ ("Ability: Mental Break", 0x0037),
+ ("Ability: Mug", 0x0038),
+ ("Ability: Quick Hit", 0x0039),
+
+ ("Ability: Steal", 0x003A),
+ ("Ability: Use", 0x003B),
+ ("Ability: Flee", 0x003C),
+ ("Ability: Pray", 0x003D),
+ ("Ability: Cheer", 0x003E),
+ ("Ability: Focus", 0x003F),
+ ("Ability: Reflex", 0x0040),
+ ("Ability: Aim", 0x0041),
+ ("Ability: Luck", 0x0042),
+ ("Ability: Jinx", 0x0043),
+ ("Ability: Lancet", 0x0044),
+ ("Ability: Guard", 0x0045),
+ ("Ability: Sentinel", 0x0046),
+ ("Ability: Spare Change", 0x0047),
+ ("Ability: Threaten", 0x0048),
+ ("Ability: Provoke", 0x0049),
+ ("Ability: Entrust", 0x004A),
+ ("Ability: Copycat", 0x004B),
+ ("Ability: Doublecast", 0x004C),
+ ("Ability: Bribe", 0x004D),
+
+ ("Ability: Cure", 0x004E),
+ ("Ability: Cura", 0x004F),
+ ("Ability: Curaga", 0x0050),
+ ("Ability: Nul Frost", 0x0051),
+ ("Ability: Nul Blaze", 0x0052),
+ ("Ability: Nul Shock", 0x0053),
+ ("Ability: Nul Tide", 0x0054),
+ ("Ability: Scan", 0x0055),
+ ("Ability: Esuna", 0x0056),
+ ("Ability: Life", 0x0057),
+ ("Ability: Full Life", 0x0058),
+ ("Ability: Haste", 0x0059),
+ ("Ability: Hastega", 0x005A),
+ ("Ability: Slow", 0x005B),
+ ("Ability: Slowga", 0x005C),
+ ("Ability: Shell", 0x005D),
+ ("Ability: Protect", 0x005E),
+ ("Ability: Reflect", 0x005F),
+ ("Ability: Dispel", 0x0060),
+ ("Ability: Regen", 0x0061),
+ ("Ability: Holy", 0x0062),
+ ("Ability: Auto Life", 0x0063),
+
+ ("Ability: Blizzard", 0x0064),
+ ("Ability: Fire", 0x0065),
+ ("Ability: Thunder", 0x0066),
+ ("Ability: Water", 0x0067),
+ ("Ability: Fira", 0x0068),
+ ("Ability: Blizzara", 0x0069),
+ ("Ability: Thundara", 0x006A),
+ ("Ability: Watera", 0x006B),
+ ("Ability: Firaga", 0x006C),
+ ("Ability: Blizzaga", 0x006D),
+ ("Ability: Thundaga", 0x006E),
+ ("Ability: Waterga", 0x006F),
+ ("Ability: Bio", 0x0070),
+ ("Ability: Demi", 0x0071),
+ ("Ability: Death", 0x0072),
+ ("Ability: Drain", 0x0073),
+ ("Ability: Osmose", 0x0074),
+ ("Ability: Flare", 0x0075),
+ ("Ability: Ultima", 0x0076),
+
+ ("Ability: Pilfer Gil", 0x0077),
+ ("Ability: Full Break", 0x0078),
+ ("Ability: Extract Power", 0x0079),
+ ("Ability: Extract Mana", 0x007A),
+ ("Ability: Extract Speed", 0x007B),
+ ("Ability: Extract Ability", 0x007C),
+
+ ("Ability: Nab Gil", 0x007D),
+ ("Ability: Quick Pockets", 0x007E),
+]]
+
+abilities: list[ItemData] = [
+ # Lvl 3 lock
+ # Empty node
+ ItemData("Ability: Strength +1", ItemClassification.progression, 0xD002),
+ ItemData("Ability: Strength +2", ItemClassification.progression, 0xD003),
+ ItemData("Ability: Strength +3", ItemClassification.progression, 0xD004),
+ ItemData("Ability: Strength +4", ItemClassification.progression, 0xD005),
+
+ ItemData("Ability: Defense +1", ItemClassification.progression, 0xD006),
+ ItemData("Ability: Defense +2", ItemClassification.progression, 0xD007),
+ ItemData("Ability: Defense +3", ItemClassification.progression, 0xD008),
+ ItemData("Ability: Defense +4", ItemClassification.progression, 0xD009),
+
+ ItemData("Ability: Magic +1", ItemClassification.progression, 0xD00A),
+ ItemData("Ability: Magic +2", ItemClassification.progression, 0xD00B),
+ ItemData("Ability: Magic +3", ItemClassification.progression, 0xD00C),
+ ItemData("Ability: Magic +4", ItemClassification.progression, 0xD00D),
+
+ ItemData("Ability: Magic Defense +1", ItemClassification.progression, 0xD00E),
+ ItemData("Ability: Magic Defense +2", ItemClassification.progression, 0xD00F),
+ ItemData("Ability: Magic Defense +3", ItemClassification.progression, 0xD010),
+ ItemData("Ability: Magic Defense +4", ItemClassification.progression, 0xD011),
+
+ ItemData("Ability: Agility +1", ItemClassification.progression, 0xD012),
+ ItemData("Ability: Agility +2", ItemClassification.progression, 0xD013),
+ ItemData("Ability: Agility +3", ItemClassification.progression, 0xD014),
+ ItemData("Ability: Agility +4", ItemClassification.progression, 0xD015),
+
+ ItemData("Ability: Luck +1", ItemClassification.progression, 0xD016),
+ ItemData("Ability: Luck +2", ItemClassification.progression, 0xD017),
+ ItemData("Ability: Luck +3", ItemClassification.progression, 0xD018),
+ ItemData("Ability: Luck +4", ItemClassification.progression, 0xD019),
+
+ ItemData("Ability: Evasion +1", ItemClassification.progression, 0xD01A),
+ ItemData("Ability: Evasion +2", ItemClassification.progression, 0xD01B),
+ ItemData("Ability: Evasion +3", ItemClassification.progression, 0xD01C),
+ ItemData("Ability: Evasion +4", ItemClassification.progression, 0xD01D),
+
+ ItemData("Ability: Accuracy +1", ItemClassification.progression, 0xD01E),
+ ItemData("Ability: Accuracy +2", ItemClassification.progression, 0xD01F),
+ ItemData("Ability: Accuracy +3", ItemClassification.progression, 0xD020),
+ ItemData("Ability: Accuracy +4", ItemClassification.progression, 0xD021),
+
+ ItemData("Ability: HP +200", ItemClassification.progression, 0xD022),
+ ItemData("Ability: HP +300", ItemClassification.progression, 0xD023),
+
+ ItemData("Ability: MP +40", ItemClassification.progression, 0xD024),
+ ItemData("Ability: MP +20", ItemClassification.progression, 0xD025),
+ ItemData("Ability: MP +10", ItemClassification.progression, 0xD026),
+
+ # Lvl 1 lock
+ # Lvl 2 lock
+ # Lvl 4 lock
+
+ ItemData("Delay Attack", ItemClassification.progression, 0xD02A),
+ ItemData("Delay Buster", ItemClassification.progression, 0xD02B),
+ ItemData("Sleep Attack", ItemClassification.progression, 0xD02C),
+ ItemData("Silence Attack", ItemClassification.progression, 0xD02D),
+ ItemData("Dark Attack", ItemClassification.progression, 0xD02E),
+ ItemData("Zombie Attack", ItemClassification.progression, 0xD02F),
+ ItemData("Sleep Buster", ItemClassification.progression, 0xD030),
+ ItemData("Silence Buster", ItemClassification.progression, 0xD031),
+ ItemData("Dark Buster", ItemClassification.progression, 0xD032),
+ ItemData("Triple Foul", ItemClassification.progression, 0xD033),
+ ItemData("Power Break", ItemClassification.progression, 0xD034),
+ ItemData("Magic Break", ItemClassification.progression, 0xD035),
+ ItemData("Armor Break", ItemClassification.progression, 0xD036),
+ ItemData("Mental Break", ItemClassification.progression, 0xD037),
+ ItemData("Mug", ItemClassification.progression, 0xD038),
+ ItemData("Quick Hit", ItemClassification.progression, 0xD039),
+
+ ItemData("Steal", ItemClassification.progression, 0xD03A),
+ ItemData("Use", ItemClassification.progression, 0xD03B),
+ ItemData("Flee", ItemClassification.progression, 0xD03C),
+ ItemData("Pray", ItemClassification.progression, 0xD03D),
+ ItemData("Cheer", ItemClassification.progression, 0xD03E),
+ ItemData("Focus", ItemClassification.progression, 0xD03F),
+ ItemData("Reflex", ItemClassification.progression, 0xD040),
+ ItemData("Aim", ItemClassification.progression, 0xD041),
+ ItemData("Luck", ItemClassification.progression, 0xD042),
+ ItemData("Jinx", ItemClassification.progression, 0xD043),
+ ItemData("Lancet", ItemClassification.progression, 0xD044),
+ ItemData("Guard", ItemClassification.progression, 0xD045),
+ ItemData("Sentinel", ItemClassification.progression, 0xD046),
+ ItemData("Spare Change", ItemClassification.progression, 0xD047),
+ ItemData("Threaten", ItemClassification.progression, 0xD048),
+ ItemData("Provoke", ItemClassification.progression, 0xD049),
+ ItemData("Entrust", ItemClassification.progression, 0xD04A),
+ ItemData("Copycat", ItemClassification.progression, 0xD04B),
+ ItemData("Doublecast", ItemClassification.progression, 0xD04C),
+ ItemData("Bribe", ItemClassification.progression, 0xD04D),
+
+ ItemData("Cure", ItemClassification.progression, 0xD04E),
+ ItemData("Cura", ItemClassification.progression, 0xD04F),
+ ItemData("Curaga", ItemClassification.progression, 0xD050),
+ ItemData("Nul Frost", ItemClassification.progression, 0xD051),
+ ItemData("Nul Blaze", ItemClassification.progression, 0xD052),
+ ItemData("Nul Shock", ItemClassification.progression, 0xD053),
+ ItemData("Nul Tide", ItemClassification.progression, 0xD054),
+ ItemData("Scan", ItemClassification.progression, 0xD055),
+ ItemData("Esuna", ItemClassification.progression, 0xD056),
+ ItemData("Life", ItemClassification.progression, 0xD057),
+ ItemData("Full Life", ItemClassification.progression, 0xD058),
+ ItemData("Haste", ItemClassification.progression, 0xD059),
+ ItemData("Hastega", ItemClassification.progression, 0xD05A),
+ ItemData("Slow", ItemClassification.progression, 0xD05B),
+ ItemData("Slowga", ItemClassification.progression, 0xD05C),
+ ItemData("Shell", ItemClassification.progression, 0xD05D),
+ ItemData("Protect", ItemClassification.progression, 0xD05E),
+ ItemData("Reflect", ItemClassification.progression, 0xD05F),
+ ItemData("Dispel", ItemClassification.progression, 0xD060),
+ ItemData("Regen", ItemClassification.progression, 0xD061),
+ ItemData("Holy", ItemClassification.progression, 0xD062),
+ ItemData("Auto Life", ItemClassification.progression, 0xD063),
+
+ ItemData("Blizzard", ItemClassification.progression, 0xD064),
+ ItemData("Fire", ItemClassification.progression, 0xD065),
+ ItemData("Thunder", ItemClassification.progression, 0xD066),
+ ItemData("Water", ItemClassification.progression, 0xD067),
+ ItemData("Fira", ItemClassification.progression, 0xD068),
+ ItemData("Blizzara", ItemClassification.progression, 0xD069),
+ ItemData("Thundara", ItemClassification.progression, 0xD06A),
+ ItemData("Watera", ItemClassification.progression, 0xD06B),
+ ItemData("Firaga", ItemClassification.progression, 0xD06C),
+ ItemData("Blizzaga", ItemClassification.progression, 0xD06D),
+ ItemData("Thundaga", ItemClassification.progression, 0xD06E),
+ ItemData("Waterga", ItemClassification.progression, 0xD06F),
+ ItemData("Bio", ItemClassification.progression, 0xD070),
+ ItemData("Demi", ItemClassification.progression, 0xD071),
+ ItemData("Death", ItemClassification.progression, 0xD072),
+ ItemData("Drain", ItemClassification.progression, 0xD073),
+ ItemData("Osmose", ItemClassification.progression, 0xD074),
+ ItemData("Flare", ItemClassification.progression, 0xD075),
+ ItemData("Ultima", ItemClassification.progression, 0xD076),
+
+ ItemData("Pilfer Gil", ItemClassification.progression, 0xD077),
+ ItemData("Full Break", ItemClassification.progression, 0xD078),
+ ItemData("Extract Power", ItemClassification.progression, 0xD079),
+ ItemData("Extract Mana", ItemClassification.progression, 0xD07A),
+ ItemData("Extract Speed", ItemClassification.progression, 0xD07B),
+ ItemData("Extract Ability", ItemClassification.progression, 0xD07C),
+
+ ItemData("Nab Gil", ItemClassification.progression, 0xD07D),
+ ItemData("Quick Pockets", ItemClassification.progression, 0xD07E),
+]
+
+trap_items: list[ItemData] = [ItemData(x[0], x[1], x[2] | trapItemOffset) for x in [
+ ("Stay away from the summoner", ItemClassification.trap, 0x0000),
+]]
+
+stat_abilities = [ability for ability in abilities_per_character if ability.itemID & 0xFF <= 0x26]
+
+skill_abilities = [ability for ability in abilities_per_character if ability not in stat_abilities]
+
+#item_to_stat_value = {item.itemName: int(re.search(r"\+([0-9])+", item.itemName).group(0)) for item in stat_abilities}
+item_to_stat_value: dict[str, tuple[str, int]] = dict()
+for item in stat_abilities:
+ character = re.match(r"([a-zA-Z]+)", item.itemName).group(0)
+ value = int(re.search(r"\+([0-9])+", item.itemName).group(0))
+ if "HP" in item.itemName:
+ value = value / 10
+ elif "MP" in item.itemName:
+ value = value / 5
+
+ item_to_stat_value[item.itemName] = (character, value)
+
+
+
+AllItems = list(chain(normal_items,
+ key_items,
+ equip_items,
+ party_member_items,
+ region_unlock_items,
+ trap_items))
+
+filler_items: list[ItemData] = [item for item in AllItems if item.progression == ItemClassification.filler]
+
+item_table: dict[str, ItemData] = {item.itemName: item for item in AllItems}
+items_by_id: dict[int, ItemData] = {item.itemID: item for item in AllItems}
+
+
+def create_item_label_to_code_map() -> dict[str, int]:
+ """
+ Creates a map from item labels to their AP item id (code)
+ """
+ offset = 0
+ label_to_code_map: dict[str, int] = {}
+ for item in AllItems:
+ label_to_code_map[item.itemName] = item.itemID + offset
+
+ return label_to_code_map
diff --git a/worlds/ffx/locations.py b/worlds/ffx/locations.py
new file mode 100644
index 000000000000..725b87ce1643
--- /dev/null
+++ b/worlds/ffx/locations.py
@@ -0,0 +1,833 @@
+from typing import Dict, Optional, List, Tuple, NamedTuple
+from itertools import chain
+
+from BaseClasses import Location, Region
+
+
+class FFXLocation(Location):
+ game: str = "Final Fantasy X"
+
+ def __init__(self, player: int, name: str = '', address: Optional[int] = None, parent: Optional[Region] = None):
+ super().__init__(player, name, address, parent)
+
+
+class FFXLocationData(NamedTuple):
+ rom_address: int
+ name: str
+ location_id: int
+ missable: bool
+
+
+
+TreasureOffset: int = 0x1000
+BossOffset: int = 0x2000
+PartyMemberOffset: int = 0x3000
+OverdriveOffset: int = 0x4000
+OverdriveModeOffset: int = 0x5000
+OtherOffset: int = 0x6000
+RecruitOffset: int = 0x7000
+SphereGridOffset: int = 0x8000
+
+location_types: Dict[int, str] = {
+ TreasureOffset: "Treasure",
+ BossOffset: "Boss",
+ PartyMemberOffset: "PartyMember",
+ OverdriveOffset: "Overdrive",
+ OverdriveModeOffset: "OverdriveMode",
+ OtherOffset: "Other",
+ RecruitOffset: "Recruit",
+ SphereGridOffset: "SphereGrid"
+}
+
+def get_location_type(location_id: int):
+ return location_types[location_id & 0xF000]
+
+encounter_to_id = {
+ "Baaj Temple: Klikk Defeated" : ["bjyt04_01"],
+ "Al Bhed Ship: Tros Defeated" : ["cdsp07_00"],
+ "Besaid: Dark Valefor" : ["bsil07_70"],
+ "S.S. Liki: Sin Fin" : ["slik02_00"],
+ "S.S. Liki: Sinspawn Echuilles" : ["slik02_01"],
+ "Kilika: Lord Ochu" : ["klyt00_00"],
+ "Kilika: Sinspawn Geneaux" : ["klyt01_00"],
+ "Luca: Oblitzerator defeated" : ["cdsp02_00"],
+ "Mi'ihen Highroad: Chocobo Eater" : ["mihn02_00"],
+ "Mushroom Rock Road: Sinspawn Gui" : ["kino02_00"],
+ "Mushroom Rock Road: Sinspawn Gui 2" : ["kino03_10"],
+ "Moonflow: Extractor" : ["genk09_00"],
+ "Thunder Plains: Dark Ixion" : ["kami03_71"],
+ "Macalania Woods: Spherimorph" : ["mcfr03_00"],
+ "Lake Macalania: Crawler" : ["maca02_00"],
+ "Lake Macalania: Seymour/Anima" : ["mcyt06_00"],
+ "Lake Macalania: Wendigo" : ["maca02_01"],
+ "Lake Macalania: Dark Shiva" : ["mcyt00_70"],
+ "Bikanel: Dark Ifrit" : ["bika03_70"],
+ "Airship: Evrae" : ["hiku15_00"],
+ "Airship: Sin Left Fin" : ["ssbt00_00"],
+ "Airship: Sin Right Fin" : ["ssbt01_00"],
+ "Airship: Sinspawn Genais and Core" : ["ssbt02_00"],
+ "Airship: Overdrive Sin" : ["ssbt03_00"],
+ "Airship: Penance" : ["hiku15_70"],
+ "Bevelle: Isaaru" : ["bvyt09_12"], # Probably?
+ "Bevelle: Evrae Altana" : ["stbv00_10"],
+ "Bevelle: Seymour Natus" : ["stbv01_10"],
+ "Calm Lands: Defender X" : ["nagi01_00"],
+ "Monster Arena: Nemesis" : ["zzzz02_76"],
+ "Cavern of the Stolen Fayth: Dark Yojimbo" : ["nagi05_74"],
+ "Gagazet (Outside): Biran and Yenke" : ["mtgz01_10"],
+ "Gagazet (Outside): Seymour Flux" : ["mtgz02_00"],
+ "Gagazet (Outside): Dark Anima" : ["mtgz01_70"],
+ "Gagazet: Sanctuary Keeper" : ["mtgz08_00"],
+ "Zanarkand: Spectral Keeper" : ["dome02_00"],
+ "Zanarkand: Yunalesca" : ["dome06_00"],
+ "Zanarkand: Dark Bahamut" : ["dome06_70"],
+ "Sin: Seymour Omnis" : ["sins03_00"],
+ "Sin: Braska's Final Aeon" : ["sins06_00"],
+ "Sin: Contest of Aeons" : ["sins07_0x"],
+ "Sin: Yu Yevon" : ["sins07_10"],
+ "Omega Ruins: Ultima Weapon" : ["omeg00_10"],
+ "Omega Ruins: Omega Weapon" : ["omeg01_10"],
+ "Mushroom Rock Road: Dark Mindy" : ["kino00_70", "kino01_70", "kino01_72", "kino05_71"],
+ "Mushroom Rock Road: Dark Sandy" : ["kino00_70", "kino01_70", "kino01_72", "kino05_70"],
+ "Mushroom Rock Road: Dark Cindy" : ["kino00_70", "kino01_70", "kino01_71"],
+ "Baaj Temple: Geosgaeno" : ["bjyt02_02"],
+}
+
+
+FFXBossLocations: List[FFXLocationData] = [ FFXLocationData(location[1]+BossOffset, *location) for location in [
+ ("Baaj Temple: Klikk", 0, False),
+ ("Al Bhed Ship: Tros", 1, False),
+ ("Besaid: Dark Valefor", 2, False),
+ ("S.S. Liki: Sin Fin", 3, False),
+ ("S.S. Liki: Sinspawn Echuilles", 4, False),
+ ("Kilika: Lord Ochu", 5, False),
+ ("Kilika: Sinspawn Geneaux", 6, False),
+ ("Luca: Oblitzerator", 7, False),
+ ("Mi'ihen Highroad: Chocobo Eater", 8, False),
+ ("Mushroom Rock Road: Sinspawn Gui", 9, False),
+ ("Mushroom Rock Road: Sinspawn Gui 2", 10, False),
+ #("Mushroom Rock Road: Dark Magus Sisters", 11, False),
+ ("Moonflow: Extractor", 12, False),
+ ("Thunder Plains: Dark Ixion", 13, False),
+ ("Macalania Woods: Spherimorph", 14, False),
+ ("Lake Macalania: Crawler", 15, False),
+ ("Lake Macalania: Seymour/Anima", 16, False),
+ ("Lake Macalania: Wendigo", 17, False),
+ ("Lake Macalania: Dark Shiva", 18, False),
+ ("Bikanel: Dark Ifrit", 19, False),
+ ("Airship: Evrae", 20, False),
+ ("Airship: Sin Left Fin", 21, False),
+ ("Airship: Sin Right Fin", 22, False),
+ ("Airship: Sinspawn Genais and Core", 23, False),
+ ("Airship: Overdrive Sin", 24, False),
+ ("Airship: Penance", 25, False),
+ ("Bevelle: Isaaru", 26, False),
+ ("Bevelle: Evrae Altana", 27, False),
+ ("Bevelle: Seymour Natus", 28, False),
+ ("Calm Lands: Defender X", 29, False),
+ ("Monster Arena: Nemesis", 30, False),
+ ("Cavern of the Stolen Fayth: Dark Yojimbo", 31, False),
+ ("Gagazet (Outside): Biran and Yenke", 32, False),
+ ("Gagazet (Outside): Seymour Flux", 33, False),
+ ("Gagazet (Outside): Dark Anima", 34, False),
+ ("Gagazet: Sanctuary Keeper", 35, False),
+ ("Zanarkand: Spectral Keeper", 36, False),
+ ("Zanarkand: Yunalesca", 37, False),
+ ("Zanarkand: Dark Bahamut", 38, False),
+ ("Sin: Seymour Omnis", 39, False),
+ #("Sin: Braska's Final Aeon", 40, False),
+ #("Sin: Contest of Aeons", 41, False),
+ #("Sin: Yu Yevon", 42, False),
+ ("Omega Ruins: Ultima Weapon", 43, False),
+ ("Omega Ruins: Omega Weapon", 44, False),
+ ("Mushroom Rock Road: Dark Mindy", 45, False),
+ ("Mushroom Rock Road: Dark Sandy", 46, False),
+ ("Mushroom Rock Road: Dark Cindy", 47, False),
+ ("Baaj Temple: Geosgaeno", 48, False),
+]]
+
+FFXOverdriveLocations: List[FFXLocationData] = [ FFXLocationData(location[1]+OverdriveOffset, *location) for location in [
+ ("Slice and Dice", 1, False),
+ ("Energy Rain", 2, False),
+ ("Blitz Ace", 3, False),
+ ("Shooting Star", 4, False),
+ ("Banishing Blade", 5, False),
+ ("Tornado", 6, False),
+ ("Attack Reels", 7, False),
+ ("Status Reels", 8, False),
+ ("Auroch Reels", 9, False),
+ ("Seed Cannon", 10, False),
+ ("Stone Breath", 11, False),
+ ("Self Destruct", 12, False),
+ ("Fire Breath", 13, False),
+ ("Aqua Breath", 14, False),
+ ("Bad Breath", 15, False),
+ ("Doom", 16, False),
+ ("Thrust Kick", 17, False),
+ ("White Wind", 18, False),
+ ("Mighty Guard", 19, False),
+ ("Nova", 20, False),
+ ("Energy Blast", 21, False),
+]]
+
+FFXOverdriveModeLocations: List[FFXLocationData] = [ FFXLocationData(location[1]+OverdriveModeOffset, *location) for location in [
+ ("Stoic", 0, False),
+ ("Warrior", 1, False),
+ ("Comrade", 2, False),
+ ("Healer", 3, False),
+ ("Tactician", 4, False),
+ ("Victim", 5, False),
+ ("Dancer", 6, False),
+ ("Avenger", 7, False),
+ ("Slayer", 8, False),
+ ("Hero", 9, False),
+ ("Rook", 10, False),
+ ("Victor", 11, False),
+ ("Coward", 12, False),
+ ("Ally", 13, False),
+ ("Sufferer", 14, False),
+ ("Daredevil", 15, False),
+ ("Loner", 16, False),
+]]
+
+# Brotherhood, Al Bhed Primers, Jecht Spheres
+FFXOtherLocations: List[FFXLocationData] = [ FFXLocationData(location[1]+OtherOffset, *location) for location in [
+ ("Brotherhood", 0, False),
+
+ ("Al Bhed Primer I", 1, False),
+ ("Al Bhed Primer II", 2, False),
+ ("Al Bhed Primer III", 3, False),
+ ("Al Bhed Primer IV", 4, False),
+ ("Al Bhed Primer V", 5, False),
+ ("Al Bhed Primer VI", 6, False),
+ ("Al Bhed Primer VII", 7, False),
+ ("Al Bhed Primer VIII", 8, False),
+ ("Al Bhed Primer IX", 9, False),
+ ("Al Bhed Primer X", 10, False),
+ ("Al Bhed Primer XI", 11, False),
+ ("Al Bhed Primer XII", 12, False),
+ ("Al Bhed Primer XIII", 13, False),
+ ("Al Bhed Primer XIV", 14, False),
+ ("Al Bhed Primer XV", 15, False),
+ ("Al Bhed Primer XVI", 16, False),
+ ("Al Bhed Primer XVII", 17, False),
+ ("Al Bhed Primer XVIII", 18, False),
+ ("Al Bhed Primer XIX", 19, False),
+ ("Al Bhed Primer XX", 20, False),
+ ("Al Bhed Primer XXI", 21, False),
+ ("Al Bhed Primer XXII", 22, False),
+ ("Al Bhed Primer XXIII", 23, False),
+ ("Al Bhed Primer XXIV", 24, False),
+ ("Al Bhed Primer XXV", 25, False),
+ ("Al Bhed Primer XXVI", 26, False),
+
+ #("Jecht Sphere - Macalania Woods", 27, False),
+ #("Jecht Sphere - Besaid", 28, False),
+ #("Jecht Sphere - S.S. Liki", 29, False),
+ #("Jecht Sphere - Luca", 30, False),
+ #("Jecht Sphere - Mi'ihen Oldroad", 31, False),
+ #("Auron's Sphere - Mushroom Rock", 32, False),
+ #("Jecht Sphere - Moonflow", 33, False),
+ #("Jecht Sphere - Thunder Plains", 34, False),
+ #("Braska's Sphere - Mt. Gagazet", 35, False),
+
+ #("S.S. Winno: Jecht Shot", 36, False),
+ ("Brotherhood Upgrade", 37, False),
+
+ ("Caladbolg Crest Upgrade", 38, False),
+ ("Caladbolg Sigil Upgrade", 39, False),
+ ("Nirvana Crest Upgrade", 40, False),
+ ("Nirvana Sigil Upgrade", 41, False),
+ ("Masamune Crest Upgrade", 42, False),
+ ("Masamune Sigil Upgrade", 43, False),
+ ("Spirit Lance Crest Upgrade", 44, False),
+ ("Spirit Lance Sigil Upgrade", 45, False),
+ ("World Champion Crest Upgrade", 46, False),
+ ("World Champion Sigil Upgrade", 47, False),
+ ("Onion Knight Crest Upgrade", 48, False),
+ ("Onion Knight Sigil Upgrade", 49, False),
+ ("Godhand Crest Upgrade", 50, False),
+ ("Godhand Sigil Upgrade", 51, False),
+]]
+
+FFXPartyMemberLocations: List[FFXLocationData] = [ FFXLocationData(location[1]+PartyMemberOffset, *location) for location in [
+ # ("Party Member: Tidus", 0, False),
+ ("Party Member: Yuna", 1, False),
+ ("Party Member: Auron", 2, False),
+ ("Party Member: Kimahri", 3, False),
+ ("Party Member: Wakka", 4, False),
+ ("Party Member: Lulu", 5, False),
+ ("Party Member: Rikku", 6, False),
+ ("Party Member: Seymour", 7, False),
+ ("Party Member: Valefor", 8, False),
+ ("Party Member: Ifrit", 9, False),
+ ("Party Member: Ixion", 10, False),
+ ("Party Member: Shiva", 11, False),
+ ("Party Member: Bahamut", 12, False),
+ ("Party Member: Anima", 13, False),
+ ("Party Member: Yojimbo", 14, False),
+ ("Party Member: Magus Sisters", 15, False),
+]]
+
+FFXRecruitLocations: List[FFXLocationData] = [ FFXLocationData(location[1]+RecruitOffset, *location) for location in [
+ ("Recruit: Biggs", 1, False),
+ ("Recruit: Brother", 2, False),
+ ("Recruit: Durren", 3, False),
+ ("Recruit: Jumal", 4, False),
+ ("Recruit: Kiyuri", 5, False),
+ ("Recruit: Kyou", 6, False),
+ ("Recruit: Linna", 7, False),
+ ("Recruit: Mep", 8, False),
+ ("Recruit: Mifurey", 9, False),
+ ("Recruit: Miyu", 10, False),
+ ("Recruit: Naida", 11, False),
+ ("Recruit: Nedus", 12, False),
+ ("Recruit: Rin", 13, False),
+ ("Recruit: Ropp", 14, False),
+ ("Recruit: Shaami", 15, False),
+ ("Recruit: Shuu", 16, False),
+ ("Recruit: Svanda", 17, False),
+ ("Recruit: Tatts", 18, False),
+ ("Recruit: Vilucha", 19, False),
+ ("Recruit: Wakka", 20, False),
+ ("Recruit: Wedge", 21, False),
+ ("Recruit: Yuma Guado", 22, False),
+ ("Recruit: Zalits", 23, False),
+ ("Recruit: Zev Ronso", 24, False)
+]]
+
+FFXTreasureLocations: List[FFXLocationData] = [ FFXLocationData(location[1]+TreasureOffset, *location) for location in [
+ ("Baaj Temple: Chest 1", 0, False), # Gil: 200 [02h]
+ ("Baaj Temple: Chest 2", 1, False), # Item: 2x Potion [2000h]
+ ("Withered Bouquet", 2, False), # Key Item: Withered Bouquet [A000h]
+ ("Flint", 3, False), # Key Item: Flint [A001h]
+ ("Treasure 4 (Potentially Trashed Chest)", 4, False), # Gear: buki_get #2 [02h] { Yuna [01h], Weapon {One MP Cost [800Dh], Empty, Empty, Empty} }
+ ("Onion Knight", 5, False), # Gear: buki_get #3 [03h] { Lulu [05h], Weapon Formula=Celestial MP-based [12h] {No AP [8014h], Empty, Empty, Empty} }
+ ("Baaj Temple: Ether (Chest)", 6, False), # Item: 1x Ether [2004h]
+ ("Baaj Temple: Hi-Potion (Chest)", 7, False), # Item: 1x Hi-Potion [2001h]
+ ("Treasure 8 (Potentially Trashed Chest)", 8, False), # Item: 1x Phoenix Down [2006h]
+ ("Besaid: Antidote x2", 9, False), # Item: 2x Antidote [200Ah]
+ # ("Treasure 10 (Potentially Trashed Chest)", 10, False), # Gil: 200 [02h]
+ # ("Treasure 11 (Potentially Trashed Chest)", 11, False), # Gear: buki_get #4 [04h] { Tidus [00h], Weapon {Firestrike [801Eh]} }
+ # ("Treasure 12 (Potentially Trashed Chest)", 12, False), # Item: 1x Potion [2000h]
+ ("Besaid: Phoenix Down x1 (Chest, Besaid Village)", 13, False), # Item: 1x Phoenix Down [2006h]
+ ("Besaid: Hi-Potion x1 (Chest)", 14, False), # Item: 1x Hi-Potion [2001h]
+ ("Besaid: Destruction Sphere", 15, False), # Gear: buki_get #5 [05h] { Yuna [01h], Weapon {Magic +5% [8067h], !Magic +3% [8066h], !Sensor [8000h]} }
+ ("S.S. Liki: Remedy x1 (Chest)", 16, False), # Item: 1x Remedy [200Fh]
+ ("Kilika: Potion x3 (Chest)", 17, False), # Item: 3x Potion [2000h]
+ ("Kilika: Ether x1 (Chest)", 18, False), # Item: 1x Ether [2004h]
+ ("Kilika: Destruction Sphere", 19, False), # Gear: buki_get #6 [06h] { Kimahri [03h], Armor {Fire Ward [801Fh], Ice Ward [8023h], Lightning Ward [8027h]} }
+ # ("Treasure 20 (Potentially Trashed Chest)", 20, False), # Gear: buki_get #7 [07h] { Lulu [05h], Armor {Berserk Ward [8051h]} }
+ # ("Treasure 21 (Potentially Trashed Chest)", 21, False), # Item: 1x Potion [2000h] #Likely 21-26 are Potions from Yuna's Luggage as entries are near by S.S. Liki's treasure ID's
+ # ("Treasure 22 (Potentially Trashed Chest)", 22, False), # Item: 1x Potion [2000h]
+ # ("Treasure 23 (Potentially Trashed Chest)", 23, False), # Item: 1x Potion [2000h]
+ # ("Treasure 24 (Potentially Trashed Chest)", 24, False), # Item: 1x Potion [2000h]
+ # ("Treasure 25 (Potentially Trashed Chest)", 25, False), # Item: 1x Potion [2000h]
+ # ("Treasure 26 (Potentially Trashed Chest)", 26, False), # Item: 1x Potion [2000h]
+ ("Kilika: Mana Sphere x2 (Chest)", 27, False), # Item: 2x Mana Sphere [2047h]
+ ("Kilika: Scout (Chest)", 28, False), # Gear: buki_get #8 [08h] { Wakka [04h], Weapon {Icestrike [8022h], Sensor [8000h]} }
+ ("Kilika: Luck Sphere x1 (Chest)", 29, False), # Item: 1x Luck Sphere [205Eh]
+ ("Kilika: NulBlaze Shield (Woman NPC after defeating Lord Ochu)", 30, False), # Gear: buki_get #9 [09h] { Tidus [00h], Armor {SOS NulBlaze [8061h]} }
+ ("S.S. Winno: Hi-Potion x1 (Chest)", 31, False), # Item: 1x Hi-Potion [2001h]
+ ("Luca: Phoenix Down x2 (Chest)", 32, False), # Item: 2x Phoenix Down [2006h]
+ ("Luca: 600 Gil (Chest)", 33, False), # Gil: 600 [06h]
+ ("Luca: Tidal Spear (Chest)", 34, False), # Gear: buki_get #10 [0Ah] { Kimahri [03h], Weapon {Piercing [800Bh], Waterstrike [802Ah]} }
+ ("Luca: HP Sphere x1 (Chest)", 35, False), # Item: 1x HP Sphere [2055h]
+ ("Luca: Hi-Potion x2 (Chest)", 36, False), # Item: 2x Hi-Potion [2001h]
+ ("Luca: 1000 Gil (Chest)", 37, False), # Gil: 1000 [0Ah]
+ ("Mi'ihen Highroad: Ice Brand (Chest)", 38, False), # Gear: buki_get #11 [0Bh] { Tidus [00h], Weapon {Icestrike [8022h]} }
+ ("Mi'ihen Highroad: Fortune Sphere (Chest)", 39, False), # Item: 1x Fortune Sphere [204Ah]
+ ("Mi'ihen Highroad: Thunder Blade (Chest)", 40, False), # Gear: buki_get #12 [0Ch] { Auron [02h], Weapon {Piercing [800Bh], Lightningstrike [8026h]} }
+ ("Mi'ihen Highroad: Scout (Chest)", 41, False), # Gear: buki_get #13 [0Dh] { Wakka [04h], Weapon {Lightningstrike [8026h], Sensor [8000h]} }
+ ("Mi'ihen Highroad: Heat Lance (Chest)", 42, False), # Gear: buki_get #14 [0Eh] { Kimahri [03h], Weapon {Piercing [800Bh], Firestrike [801Eh]} }
+ ("Mi'ihen Highroad: Hi-Potion x2 (Chest)", 43, False), # Item: 2x Hi-Potion [2001h]
+ ("Mi'ihen Highroad: Remedy x1 (Chest)", 44, False), # Item: 1x Remedy [200Fh]
+ ("Mi'ihen Highroad: 2000 gil (Chest)", 45, False), # Gil: 2000 [14h]
+ ("Mi'ihen Highroad: Eye Drops x3 (Chest)", 46, False), # Item: 3x Eye Drops [200Ch]
+ ("Mushroom Rock Road: Soft x4 (Chest)", 47, False), # Item: 4x Soft [200Bh]
+ ("Mushroom Rock Road: 1000 gil (Chest)", 48, False), # Gil: 1000 [0Ah]
+ ("Mushroom Rock Road: Hi-Potion x1 (Chest)", 49, False), # Item: 1x Hi-Potion [2001h]
+ ("Mushroom Rock Road: Remedy x1 (Chest)", 50, False), # Item: 1x Remedy [200Fh]
+ ("Mushroom Rock Road: Serene Bracer (Chest)", 51, False), # Gear: buki_get #15 [0Fh] { Auron [02h], Armor {HP +5% [8072h], Berserk Ward [8051h]} }
+ ("Mushroom Rock Road: Mega-Potion x1 (Chest)", 52, False), # Item: 1x Mega-Potion [2003h]
+ # ("Treasure 53 (Potentially Trashed Treasure)", 53, False), # Item: 1x Potion [2000h]
+ ("Djose: Phoenix Down x2 (Chest)", 54, False), # Item: 2x Phoenix Down [2006h]
+ ("Djose: Bright Bangle (Chest)", 55, False), # Gear: Bright Bangle
+ # ("Treasure 56 (Potentially Trashed Treasure)", 56, False), # Gear: buki_get #17 [11h] { Yuna [01h], Armor {Lightning Ward [8027h], Poison Ward [803Dh]} }
+ ("Mushroom Rock Road: Serene Armlet (Chest)", 57, False), # Gear: buki_get #18 [12h] { Kimahri [03h], Armor {Dark Ward [8049h], Berserk Ward [8051h]} }
+ ("Djose: Ability Sphere x4 (Chest)", 58, False), # Item: 4x Ability Sphere [2049h]
+ ("Djose: 4000 gil (Chest)", 59, False), # Gil: 4000 [28h]
+ ("Djose: Switch Hitter (Chest)", 60, False), # Gear: buki_get #19 [13h] { Wakka [04h], Weapon {Strength +3% [8062h], Strength +5% [8063h]} }
+ ("Djose: Ether x1 (Chest)", 61, False), # Item: 1x Ether [2004h]
+ ("Djose: Remedy x1 (Chest)", 62, False), # Item: 1x Remedy [200Fh]
+ ("Djose: Mega Phoenix x1 (Chest)", 63, False), # Item: 1x Mega Phoenix [2007h]
+ ("Guadosalam: 3000 gil (Chest)", 64, False), # Gil: 3000 [1Eh]
+ ("Guadosalam: Mega-Potion x1 (Chest)", 65, False), # Item: 1x Mega-Potion [2003h]
+ ("Guadosalam: Elixir x1 gil (Chest)", 66, False), # Item: 1x Elixir [2008h]
+ ("Guadosalam: Hi-Potion x2 (Chest)", 67, False), # Item: 2x Hi-Potion [2001h]
+ ("Macalania Woods: 2000 gil (Chest)", 68, False), # Gil: 2000 [14h]
+ ("Macalania Woods: Sleepy Cait Sith (Chest)", 69, False), # Gear: buki_get #20 [14h] { Lulu [05h], Weapon {Sleeptouch [803Fh]} }
+ ("Macalania Woods: Phoenix Down x3 (Chest)", 70, False), # Item: 3x Phoenix Down [2006h]
+ ("Macalania Woods: MP Sphere x1 (Butterfly Minigame Reward before Spherimorph)", 71, False), # Item: 1x MP Sphere [2056h]
+ ("Macalania Woods: Ether x1 (Butterfly Minigame Reward before Spherimorph)", 72, False), # Item: 1x Ether [2004h]
+ ("Macalania Woods: Remedy x1 (Chest)", 73, False), # Item: 1x Remedy [200Fh]
+ # ("Treasure 74 (Trashed)", 74, False), # Item: 1x Potion [2000h]
+ ("Macalania Woods: Lucid Ring (Chest)", 75, False), # Gear: buki_get #21 [15h] { Yuna [01h], Armor {Silence Ward [8045h], Confuse Ward [804Fh]} }
+ ("Lake Macalania: 4000 gil (Chest)", 76, False), # Gil: 4000 [28h]
+ ("Lake Macalania: Lv. 1 Key Sphere x1 (Chest)", 77, False), # Item: 1x Lv. 1 Key Sphere [2051h]
+ ("Lake Macalania: Mega-Potion x1 (Chest)", 78, False), # Item: 1x Mega-Potion [2003h]
+ ("Lake Macalania Bottom: Avenger (Chest)", 79, False), # Gear: buki_get #22 [16h] { Tidus [00h], Weapon {Counterattack [8003h]} }
+ ("Lake Macalania Bottom: Lv. 2 Key Sphere (Chest)", 80, False), # Item: 1x Lv. 2 Key Sphere [2052h]
+ # ("Treasure 81 (Trashed)", 81, False), # Gear: buki_get #23 [17h] { Lulu [05h], Weapon {Silencetouch [8043h], Magic +5% [8067h]} }
+ # ("Treasure 82 (Trashed)", 82, False), # Item: 1x Mega-Potion [2003h]
+ ("Lake Macalania: 5000 gil (Chest)", 83, False), # Gil: 5000 [32h]
+ ("Lake Macalania: X-Potion x2 (Chest)", 84, False), # Item: 2x X-Potion [2002h]
+ ("Lake Macalania: Shell Targe (Tromell)", 85, False), # Gear: buki_get #24 [18h] { Rikku [06h], Armor {SOS Shell [8059h]} }
+ ("Lake Macalania: Phoenix Down x3 (Chest)", 86, False), # Item: 3x Phoenix Down [2006h]
+ ("Lake Macalania: Remedy x2 (Chest)", 87, False), # Item: 2x Remedy [200Fh]
+ # ("Treasure 88 (Trashed)", 88, False), # Gear: buki_get #25 [19h] { Kimahri [03h], Armor {Poison Ward [803Dh], Confuse Ward [804Fh], Silence Ward [8045h], Empty} }
+ # ("Treasure 89 (Trashed)", 89, False), # Item: 1x Potion [2000h]
+ ("Besaid: Phoenix Down x1 (Chest, Besaid Valley)", 90, False), # Item: 1x Phoenix Down [2006h]
+ ("Besaid: Hi-Potion x1(Chest)", 91, False), # Item: 1x Hi-Potion [2001h]
+ ("Besaid: Antidote x2 (Chest)", 92, False), # Item: 2x Antidote [200Ah]
+ ("World Champion", 93, False), # Gear: buki_get #26 [1Ah] { Wakka [04h], Weapon Formula=Celestial HP-based [11h] {No AP [8014h], Empty, Empty, Empty} }
+ ("Mi'ihen Highroad: Scout (Donate 100 gil to Operation)", 94, False), # Gear: buki_get #27 [1Bh] { Wakka [04h], Weapon {Sensor [8000h]} }
+ ("Mi'ihen Highroad: Ice Lance (Donate 1000 gil to Operation)", 95, False), # Gear: buki_get #28 [1Ch] { Kimahri [03h], Weapon {Piercing [800Bh], Icestrike [8022h]} }
+ ("Mi'ihen Highroad: Moon Ring (Donate 10000 gil to Operation)", 96, False), # Gear: buki_get #29 [1Dh] { Yuna [01h], Armor {SOS Shell [8059h], SOS Protect [805Ah]} }
+ ("Mi'ihen Highroad: Mega-Potion (NPC)", 97, False), # Item: 2x Mega-Potion [2003h]
+ ("Mushroom Rock Road: Hi-Potion x1 (Chest) (Aftermath)", 98, False), # Item: 1x Hi-Potion [2001h]
+ ("Masamune", 99, False), # Gear: buki_get #30 [1Eh] { Auron [02h], Weapon Formula=Celestial Auron [13h] {No AP [8014h], Empty, Empty, Empty} }
+ ("Bevelle: Avenger (Chest)", 100, False), # Gear: buki_get #31 [1Fh] { Tidus [00h], Weapon {Counterattack [8003h]} }
+ ("Bevelle: Rematch (Chest)", 101, False), # Gear: buki_get #32 [20h] { Wakka [04h], Weapon {Evade & Counter [8004h]} }
+ ("Bevelle: Knight Lance (Chest)", 102, False), # Gear: buki_get #33 [21h] { Kimahri [03h], Weapon {Strength +3% [8062h], Strength +5% [8063h], Strength +10% [8064h]} }
+ # ("Treasure 103 (Trashed)", 103, False), # Item: 1x Potion [2000h]
+ ("Bevelle: Elixir x1 (Chest)", 104, False), # Item: 1x Elixir [2008h]
+ ("Bevelle: Wht Magic Sphere x1 (Chest)", 105, False), # Item: 1x Wht Magic Sphere [204Eh]
+ ("Bevelle: Skill Sphere x1 (Chest)", 106, False), # Item: 1x Skill Sphere [204Dh]
+ ("Bevelle: 10000 gil (Chest)", 107, False), # Gil: 10000 [64h]
+ ("Bevelle: Lucid Ring (Chest)", 108, False), # Gear: buki_get #34 [22h] { Yuna [01h], Armor {Silence Ward [8045h], Confuse Ward [804Fh], Poison Ward [803Dh]} }
+ ("Bevelle: Blk Magic Sphere (Chest)", 109, False), # Item: 1x Blk Magic Sphere [204Fh]
+ ("Bevelle: Mega-Potion x1 (Chest)", 110, False), # Item: 1x Mega-Potion [2003h]
+ ("Celestial Mirror", 111, False), # Key Item: Celestial Mirror [A003h]
+ # ("Treasure 112 (Trashed)", 112, False), # Item: 1x Potion [2000h]
+ ("Nirvana", 113, False), # Gear: buki_get #36 [24h] { Yuna [01h], Weapon Formula=Celestial MP-based [12h] {No AP [8014h], Empty, Empty, Empty} }
+ ("Caladbolg", 114, False), # Gear: buki_get #37 [25h] { Tidus [00h], Weapon Formula=Celestial HP-based [11h] {No AP [8014h], Empty, Empty, Empty} }
+ ("Calm Lands: 10000 gil (Chest)", 115, False), # Gil: 10000 [64h]
+ ("Calm Lands: 5000 gil (Chest)", 116, False), # Gil: 5000 [32h]
+ ("Calm Lands: Lv. 2 Key Sphere x1 (Chest)", 117, False), # Item: 1x Lv. 2 Key Sphere [2052h]
+ ("Rusty Sword", 118, False), # Key Item: Rusty Sword [A021h]
+ # ("Treasure 119 (Trashed)", 119, False), # Gear: buki_get #38 [26h] { Kimahri [03h], Armor {HP +10% [8073h], Empty, Empty, Empty} }
+ ("Cavern of the Stolen Fayth: Megalixir x1 (Chest)", 120, False), # Item: 1x Megalixir [2009h]
+ ("Cavern of the Stolen Fayth: Lv. 2 Key Sphere x1 (Chest)", 121, False), # Item: 1x Lv. 2 Key Sphere [2052h]
+ ("Cavern of the Stolen Fayth: Fortune Sphere x1 (Chest)", 122, False), # Item: 1x Fortune Sphere [204Ah]
+ ("Cavern of the Stolen Fayth: Mega-Potion x1 (Chest)", 123, False), # Item: 2x Mega-Potion [2003h]
+ ("Cavern of the Stolen Fayth: Flexible Arm (Chest)", 124, False), # Gear: buki_get #39 [27h] { Rikku [06h], Weapon {Empty, Empty, Empty, Empty} }
+ ("Cavern of the Stolen Fayth: MP Sphere x1 (Chest)", 125, False), # Item: 1x MP Sphere [2056h]
+ ("Cavern of the Stolen Fayth: X-Potion x2 (Chest)", 126, False), # Item: 2x X-Potion [2002h]
+ # ("Treasure 127 (Trashed)", 127, False), # Item: 1x Potion [2000h]
+ ("Mt. Gagazet: 20000 gil (Chest)", 128, False), # Gil: 20000 [C8h]
+ ("Mt. Gagazet: Mega-Potion x2 (Chest)", 129, False), # Item: 2x Mega-Potion [2003h]
+ ("Mt. Gagazet: Defending Bracer (Chest)", 130, False), # Gear: buki_get #40 [28h] { Auron [02h], Armor {Stoneproof [8038h], Poisonproof [803Ch]} }
+ ("Mt. Gagazet: Lv. 4 Key Sphere x1 (Chest)", 131, False), # Item: 1x Lv. 4 Key Sphere [2054h]
+ ("Mt. Gagazet: HP Sphere x1 (Chest)", 132, False), # Item: 1x HP Sphere [2055h]
+ # ("Treasure 133 (Trashed)", 133, False), # Item: 1x Potion [2000h]
+ # ("Treasure 134 (Trashed)", 134, False), # Item: 1x Potion [2000h]
+ ("Mt. Gagazet (Cave): Pep Talk (Chest)", 135, False), # Gear: buki_get #41 [29h] { Wakka [04h], Armor {SOS NulFrost [805Fh], SOS NulShock [8060h], SOS NulBlaze [8061h]} }
+ ("Mt. Gagazet (Cave): Lv. 1 Key Sphere x1 (Chest)", 136, False), # Item: 1x Lv. 1 Key Sphere [2051h]
+ ("Mt. Gagazet (Cave): Fortune Sphere x1 (Chest)", 137, False), # Item: 1x Fortune Sphere [204Ah]
+ ("Mt. Gagazet (Cave): Return Sphere x1 (Chest)", 138, False), # Item: 1x Return Sphere [2060h]
+ ("Mt. Gagazet (Cave): Recovery Ring (Chest)", 139, False), # Gear: buki_get #42 [2Ah] { Yuna [01h], Armor {HP Stroll [801Bh]} }
+ # ("Treasure 140 (Trashed)", 140, False), # Item: 1x Potion [2000h]
+ # ("Treasure 141 (Trashed)", 141, False), # Item: 1x Potion [2000h]
+ # ("Treasure 142 (Trashed)", 142, False), # Item: 1x Potion [2000h]
+ # ("Treasure 143 (Trashed)", 143, False), # Item: 1x Potion [2000h]
+ # ("Treasure 144 (Trashed)", 144, False), # Item: 1x Potion [2000h]
+ ("Zanarkand: Fortune Sphere x1 (Chest)", 145, False), # Item: 1x Fortune Sphere [204Ah]
+ ("Zanarkand: Spiritual Targe (Chest)", 146, False), # Gear: buki_get #43 [2Bh] { Rikku [06h], Armor {MP Stroll [801Ch]} }
+ ("Zanarkand: 10000 gil (Chest)", 147, False), # Gil: 10000 [64h]
+ ("Zanarkand: Friend Sphere x1 (Chest)", 148, False), # Item: 1x Friend Sphere [2061h]
+ ("Zanarkand: Lv. 3 Key Sphere x1 (Chest)", 149, False), # Item: 1x Lv. 3 Key Sphere [2053h]
+ ("Zanarkand: Luck Sphere x1", 150, False), # Item: 1x Luck Sphere [205Eh]
+ # ("Treasure 151 (Trashed)", 151, False), # Item: 1x Potion [2000h]
+ ("Omega Ruins: Fortune Sphere x1 (Chest)", 152, False), # Item: 1x Lv. 4 Key Sphere [2054h]
+ ("Omega Ruins: Defending Bracer (Chest)", 153, False), # Gear: buki_get #44 [2Ch] { Auron [02h], Armor {Silenceproof [8044h], Darkproof [8048h]} }
+ ("Omega Ruins: Turnover (Chest)", 154, False), # Gear: buki_get #45 [2Dh] { Wakka [04h], Weapon {Magic Counter [8005h], Counterattack [8003h]} }
+ ("Omega Ruins: Lv. 3 Key Sphere x2 (Chest)", 155, False), # Item: 2x Lv. 3 Key Sphere [2053h]
+ ("Omega Ruins: Defending Armlet (Chest)", 156, False), # Gear: buki_get #46 [2Eh] { Kimahri [03h], Armor {Stoneproof [8038h], Poisonproof [803Ch], Empty, Empty} }
+ ("Omega Ruins: Friend Sphere x2 (Chest)", 157, False), # Item: 2x Friend Sphere [2061h]
+ ("Omega Ruins: Lv. 4 Key Sphere x1 (Chest)", 158, False), # Item: 1x Lv. 4 Key Sphere [2054h]
+ ("Omega Ruins: Phantom Ring (Chest)", 159, False), # Gear: buki_get #47 [2Fh] { Yuna [01h], Armor {Ice Eater [8025h], Fire Eater [8021h], Lightning Eater [8029h], Empty} }
+ ("Omega Ruins: Cactuar Wizard (Chest)", 160, False), # Gear: buki_get #48 [30h] { Lulu [05h], Weapon {Half MP Cost [800Ch]} }
+ ("Omega Ruins: Warmonger (Chest)", 161, False), # Gear: buki_get #49 [31h] { Rikku [06h], Weapon {Double AP [8012h], !Double Overdrive [800Eh]} }
+ ("Yojimbo 3x Reward/Omega Ruins: Teleport Sphere x2 (Chest)", 162, False), # Item: 2x Teleport Sphere [2062h]
+ ("Inside Sin: Elixir x1 (Chest)", 163, False), # Item: 1x Elixir [2008h]
+ ("Inside Sin: Wizard Lance (Chest)", 164, False), # Gear: buki_get #50 [32h] { Kimahri [03h], Weapon {Magic +3% [8066h], Magic +5% [8067h], Magic +10% [8068h], Empty} }
+ ("Inside Sin: Lv. 3 Key Sphere x1 (Chest)", 165, False), # Item: 1x Lv. 3 Key Sphere [2053h]
+ ("Inside Sin: Phantom Ring (Chest)", 166, False), # Gear: buki_get #51 [33h] { Yuna [01h], Armor {Water Eater [802Dh], Fire Eater [8021h], Lightning Eater [8029h], Empty} }
+ ("Inside Sin: Special Sphere x1 (Chest)", 167, False), # Item: 1x Special Sphere [204Ch]
+ ("Inside Sin: Lv. 4 Key Sphere x1 (Chest)", 168, False), # Item: 1x Lv. 4 Key Sphere [2054h]
+ ("Inside Sin: Four-on-One (Chest)", 169, False), # Gear: buki_get #52 [34h] { Wakka [04h], Weapon {Waterstrike [802Ah], Firestrike [801Eh], Lightningstrike [8026h], Icestrike [8022h]} }
+ ("Inside Sin: Defending Bracer (Chest)", 170, False), # Gear: buki_get #53 [35h] { Auron [02h], Armor {Darkproof [8048h], Deathproof [8030h], Empty, Empty} }
+ ("Inside Sin: 20000 gil (Chest)", 171, False), # Gil: 20000 [C8h]
+ ("Inside Sin: HP Sphere x1 (Chest)", 172, False), # Item: 1x HP Sphere [2055h]
+ ("Inside Sin: Defense Sphere x1 (Chest)", 173, False), # Item: 1x Defense Sphere [2058h]
+ ("Inside Sin: Megalixir x1 (Chest)", 174, False), # Item: 1x Megalixir [2009h]
+ ("Inside Sin: Laevatein (Chest)", 175, False), # Gear: buki_get #54 [36h] { Yuna [01h], Weapon {SOS Overdrive [8010h]} }
+ ("Cloudy Mirror", 176, False), # Key Item: Cloudy Mirror [A002h]
+ ("Jecht Sphere", 177, False), # Key Item: Jecht's Sphere [A020h]
+ ("Thunder Plains: Phoenix Down x2 (Chest)", 178, False), # Item: 2x Phoenix Down [2006h]
+ ("Thunder Plains: Hi-Potion x2 (Chest)", 179, False), # Item: 2x Hi-Potion [2001h]
+ ("Thunder Plains: 5000 gil (Chest)", 180, False), # Gil: 5000 [32h]
+ ("Thunder Plains: Water Ball (Chest)", 181, False), # Gear: buki_get #55 [37h] { Wakka [04h], Weapon {Waterstrike [802Ah], Empty} }
+ ("Thunder Plains: X-Potion x1 (Chest)", 182, False), # Item: 1x X-Potion [2002h]
+ ("Thunder Plains: Ether x1 (Chest)", 183, False), # Item: 1x Ether [2004h]
+ ("Thunder Plains: Remedy x1 (Chest)", 184, False), # Item: 1x Remedy [200Fh]
+ ("Thunder Plains: 2000 gil (Chest)", 185, False), # Gil: 2000 [14h]
+ ("Mi'ihen Highroad Echo Ring (Win Aeon Fight)", 186, False), # Gear: buki_get #74 [4Ah] { Yuna [01h], Armor {HP +10% [8073h], Silence Ward [8045h]} }
+ ("Calm Lands: Power Spheres x30 (NPC)", 187, False), # Item: 30x Power Sphere [2046h]
+ ("Spirit Lance", 188, False), # Gear: buki_get #56 [38h] { Kimahri [03h], Weapon Formula=Celestial HP-based [11h] {No AP [8014h], Empty, Empty, Empty} }
+ ("Thunder Plains: X-Potion x2 (Dodging Minigame Reward)", 189, False), # Item: 2x X-Potion [2002h]
+ ("Thunder Plains: Mega-Potion x2 (Dodging Minigame Reward)", 190, False), # Item: 2x Mega-Potion [2003h]
+ ("Thunder Plains: MP Sphere x2 (Dodging Minigame Reward)", 191, False), # Item: 2x MP Sphere [2056h]
+ ("Thunder Plains: Strength Sphere x3 (Dodging Minigame Reward)", 192, False), # Item: 3x Strength Sphere [2057h]
+ ("Thunder Plains: HP Sphere x3 (Dodging Minigame Reward)", 193, False), # Item: 3x HP Sphere [2055h]
+ ("Thunder Plains: Megalixir x4 (Dodging Minigame Reward)", 194, False), # Item: 4x Megalixir [2009h]
+ # ("Treasure 195 (Trashed)", 195, False), # Item: 1x Ether [2004h]
+ # ("Treasure 196 (Trashed)", 196, False), # Item: 1x Elixir [2008h]
+ ("Moonflow: X-Potion x1 (Chest)", 197, False), # Item: 1x X-Potion [2002h]
+ ("Moonflow: Phoenix Down x2 (Chest)", 198, False), # Item: 2x Phoenix Down [2006h]
+ ("Moonflow: 5000 gil (Chest)", 199, False), # Gil: 5000 [32h]
+ ("Moonflow: Ether x1 (Chest)", 200, False), # Item: 1x Ether [2004h]
+ ("Moonflow: Antidote x4 (Chest)", 201, False), # Item: 4x Antidote [200Ah]
+ ("Moonflow: Mega-Potion x1 (Chest)", 202, False), # Item: 1x Mega-Potion [2003h]
+ # ("Baaj Temple: Grenades from Rikku", 203, False), # Item: 2x Grenade [2023h]
+ ("Baaj Temple: Megalixir (Temple Area Chest)", 204, False), # Item: 1x Megalixir [2009h]
+ ("Baaj Temple: Mega Phoenix x4 (Temple Area Chest)", 205, False), # Item: 4x Mega Phoenix [2007h]
+ ("Luca: Magic Sphere x1 (Chest)", 206, False), # Item: 1x Magic Sphere [2059h]
+ ("Brotherhood", 207, False), # Gear: buki_get #1 [01h] { Tidus [00h], Weapon {Strength +5% [8063h], Strength +10% [8064h], Waterstrike [802Ah], Sensor [8000h]} }
+ ("Brotherhood?", 208, False), # Gear: buki_get #1 [01h] { Tidus [00h], Weapon {Strength +5% [8063h], Strength +10% [8064h], Waterstrike [802Ah], Sensor [8000h]} }
+ ("Zanarkand Ruins: Destruction Sphere", 209, False), # Gear: buki_get #60 [3Ch] { Yuna [01h], Weapon {Half MP Cost [800Ch], Empty, Empty} }
+ ("Bikanel: Al Bhed Potion x8 (Chest, 1)", 210, False), # Item: 8x Al Bhed Potion [2014h]
+ ("Bikanel: Al Bhed Potion x8 (Chest, 2)", 211, False), # Item: 8x Al Bhed Potion [2014h]
+ ("Bikanel: Al Bhed Potion x8 (Chest, 3)", 212, False), # Item: 8x Al Bhed Potion [2014h]
+ ("Baaj Temple: X-Potion x1 (Chest)", 213, False), # Item: 1x X-Potion [2002h]
+ ("Godhand", 214, False), # Gear: buki_get #61 [3Dh] { Rikku [06h], Weapon Formula=Celestial HP-based [11h] {No AP [8014h], Empty, Empty, Empty} }
+ ("Besaid Village: 400 gil (Chest)", 215, False), # Gil: 400 [04h]
+ ("Besaid Village: Potion x2 (Chest)", 216, False), # Item: 2x Potion [2000h]
+ ("Bevelle: HP Sphere x1 (Chest)", 217, False), # Item: 1x HP Sphere [2055h]
+ ("Guadosalam: Lightning Marble x8 (Chest)", 218, False), # Item: 8x Lightning Marble [201Eh]
+ ("Baaj Temple: Hi Potion x1 (Chest)", 219, False), # Item: 1x Hi-Potion [2001h]
+ ("Blitzball Reward 1", 220, False), # Item: 1x Hi-Potion [2001h]
+ ("Blitzball Reward 2", 221, False), # Item: 1x Dark Matter [2035h]
+ ("Blitzball Reward 3", 222, False), # Item: 1x Teleport Sphere [2062h]
+ ("Blitzball Reward 4", 223, False), # Item: 1x Three Stars [2045h]
+ ("Blitzball Reward 5", 224, False), # Item: 1x Luck Sphere [205Eh]
+ ("Blitzball Reward 6", 225, False), # Item: 1x Underdog's Secret [206Eh]
+ ("Blitzball Reward 7", 226, False), # Item: 1x Megalixir [2009h]
+ ("Blitzball Reward 8", 227, False), # Item: 1x Return Sphere [2060h]
+ ("Blitzball Reward 9", 228, False), # Item: 1x Lv. 1 Key Sphere [2051h]
+ ("Blitzball Reward 10", 229, False), # Item: 1x Mega Phoenix [2007h]
+ ("Blitzball Reward 11", 230, False), # Item: 1x Elixir [2008h]
+ ("Blitzball Reward 12", 231, False), # Item: 1x Mega-Potion [2003h]
+ ("Blitzball Reward 13", 232, False), # Item: 1x X-Potion [2002h]
+ ("Blitzball Reward 14", 233, False), # Item: 1x Ether [2004h]
+ ("Blitzball Reward 15", 234, False), # Item: 2x Remedy [200Fh]
+ ("Blitzball Reward 16", 235, False), # Item: 2x Phoenix Down [2006h]
+ ("Blitzball Reward 17", 236, False), # Item: 2x Hi-Potion [2001h]
+ ("Blitzball Reward 18", 237, False), # Item: 5x Power Sphere [2046h]
+ ("Blitzball Reward 19", 238, False), # Item: 5x Mana Sphere [2047h]
+ ("Blitzball Reward 20", 239, False), # Item: 5x Speed Sphere [2048h]
+ ("Blitzball Reward 21", 240, False), # Item: 5x Ability Sphere [2049h]
+ ("Blitzball Reward 22", 241, False), # Item: 1x Echo Screen [200Dh]
+ ("Blitzball Reward 23", 242, False), # Item: 1x Eye Drops [200Ch]
+ ("Blitzball Reward 24", 243, False), # Item: 1x Antidote [200Ah]
+ ("Jupiter Sigil", 244, False), # Key Item: Jupiter Sigil [A02Dh]
+ ("Blitzball Reward 25", 245, False), # Item: 1x Elixir [2008h]
+ ("Blitzball Reward 26", 246, False), # Item: 1x X-Potion [2002h]
+ ("Blitzball Reward 27", 247, False), # Item: 1x Remedy [200Fh]
+ ("Blitzball Reward 28", 248, False), # Item: 1x Hi-Potion [2001h]
+ ("Blitzball Reward 29", 249, False), # Item: 4x Echo Screen [200Dh]
+ ("Blitzball Reward 30", 250, False), # Item: 4x Eye Drops [200Ch]
+ ("Blitzball Reward 31", 251, False), # Item: 4x Antidote [200Ah]
+ ("Blitzball Reward 32", 252, False), # Item: 4x Soft [200Bh]
+ ("Blitzball Reward 33", 253, False), # Item: 2x Potion [2000h]
+ ("Blitzball Reward 34", 254, False), # Item: 2x Phoenix Down [2006h]
+ ("Blitzball Reward 35", 255, False), # Item: 1x Potion [2000h]
+ ("Blitzball Reward 36", 256, False), # Item: 1x Phoenix Down [2006h]
+ ("Blitzball Reward 37", 257, False), # Item: 2x Hi-Potion [2001h]
+ ("Blitzball Reward 38", 258, False), # Item: 1x Hi-Potion [2001h]
+ ("Blitzball Reward 39", 259, False), # Item: 1x Potion [2000h]
+ ("Blitzball Reward 40", 260, False), # Item: 1x Phoenix Down [2006h]
+ ("Blitzball Reward 41", 261, False), # Item: 1x Return Sphere [2060h]
+ ("Blitzball Reward 42", 262, False), # Item: 1x Rename Card [2065h]
+ ("Blitzball Reward 43", 263, False), # Item: 1x Ether [2004h]
+ ("Blitzball Reward 44", 264, False), # Item: 1x X-Potion [2002h]
+ ("Blitzball Reward 45", 265, False), # Item: 1x Mega-Potion [2003h]
+ ("Blitzball Reward 46", 266, False), # Item: 2x Remedy [200Fh]
+ ("Sun Crest", 267, False), # Key Item: Sun Crest [A023h]
+ ("Moon Crest", 268, False), # Key Item: Moon Crest [A025h]
+ ("Mars Crest", 269, False), # Key Item: Mars Crest [A027h]
+ ("Saturn Crest", 270, False), # Key Item: Saturn Crest [A02Ah]
+ ("Jupiter Crest", 271, False), # Key Item: Jupiter Crest [A02Ch]
+ ("Venus Crest", 272, False), # Key Item: Venus Crest [A02Eh]
+ ("Mercury Crest", 273, False), # Key Item: Mercury Crest [A030h]
+ ("Sun Sigil", 274, False), # Key Item: Sun Sigil [A024h]
+ ("Moon Sigil", 275, False), # Key Item: Moon Sigil [A026h]
+ ("Mars Sigil", 276, False), # Key Item: Mars Sigil [A028h]
+ ("Saturn Sigil", 277, False), # Key Item: Saturn Sigil [A02Bh]
+ ("Venus Sigil", 278, False), # Key Item: Venus Sigil [A02Fh]
+ ("Mercury Sigil", 279, False), # Key Item: Mercury Sigil [A031h]
+ ("Lake Macalania: Megalixir x2 (Butterfly Game after defeating Spherimorph)", 280, False), # Item: 2x Megalixir [2009h]
+ ("Lake Macalania: Elixir x2 (Butterfly Game after defeating Spherimorph)", 281, False), # Item: 2x Elixir [2008h]
+ ("Besaid: Hi-Potion x1 (Datto NPC)", 282, False), # Item: 1x Hi-Potion [2001h]
+ ("Besaid: Potion x3 (Jassu NPC)", 283, False), # Item: 3x Potion [2000h]
+ ("Besaid: Potion x2 (Botta NPC)", 284, False), # Item: 2x Potion [2000h]
+ ("Besaid: 200 gil (Keepa NPC)", 285, False), # Gil: 200 [02h]
+ ("Besaid: Remedy x1 (Kid on Dock Bridge NPC)", 286, False), # Item: 1x Remedy [200Fh]
+ ("Besaid: Seeker's Ring (Priest NPC)", 287, False), # Gear: buki_get #62 [3Eh] { Yuna [01h], Armor {HP +10% [8073h]} }
+ ("Besaid: Phoenix Down x3 (Woman NPC)", 288, False), # Item: 3x Phoenix Down [2006h]
+ ("Besaid: 400 gil (Shirtless Man NPC)", 289, False), # Gil: 400 [04h]
+ ("Besaid: Ether (Green Shirt NPC)", 290, False), # Item: 1x Ether [2004h]
+ ("Kilika: Antidote x4 (Luzzu NPC before Ochu)", 291, False), # Item: 4x Antidote [200Ah]
+ ("Kilika: Elixir x1 (Luzzu NPC after Ochu)", 292, False), # Item: 1x Elixir [2008h]
+ ("Kilika: Remedy x1 (Leader NPC)", 293, False), # Item: 1x Remedy [200Fh]
+ # ("Kilika: Phoenix Down x3 (Guard NPC, fight Ochu from west and run?)", 294, False), # Item: 3x Phoenix Down [2006h]
+ ("Kilika: Remedy x1 (Guard NPC)", 295, False), # Item: 1x Hi-Potion [2001h]
+ ("Al Bhed Ship: Potion x 3 (NPC)", 296, False), # Item: 3x Potion [2000h]
+ ("Djose: Variable Steel (NPC)", 297, False), # Gear: buki_get #63 [3Fh] { Tidus [00h], Weapon {Strength +3% [8062h], Empty, Empty, Empty} }
+ ("Djose: Soft Ring (NPC)", 298, False), # Gear: buki_get #64 [40h] { Yuna [01h], Armor {Stoneproof [8038h], Empty} }
+ ("Djose: Hi-Potion x1 (NPC)", 299, False), # Item: 1x Hi-Potion [2001h]
+ ("Djose: Ether x1 (NPC)", 300, False), # Item: 1x Ether [2004h]
+ ("Djose: Mega-Potion x1 (NPC)", 301, False), # Item: 1x Mega-Potion [2003h]
+ ("Djose: Halberd (NPC)", 302, False), # Gear: buki_get #65 [41h] { Kimahri [03h], Weapon {Magic +20% [8069h], Empty} }
+ ("Djose: Potion x10 (NPC)", 303, False), # Item: 10x Potion [2000h]
+ ("Djose: Hi-Potion x2 (NPC)", 304, False), # Item: 2x Hi-Potion [2001h]
+ ("Lake Macalania: 400 gil (Al Bhed Soldier NPC)", 305, False), # Gil: 400 [04h]
+ ("Lake Macalania: Elixir x1 (Man Sitting NPC)", 306, False), # Item: 1x Elixir [2008h]
+ ("Lake Macalania: Ether x1 (Man Sitting NPC)", 307, False), # Item: 1x Ether [2004h]
+ ("Lake Macalania: Hi-Potion x2 (NPC)", 308, False), # Item: 2x Hi-Potion [2001h]
+ ("Mi'ihen Highroad: Hunters Spear (Blue Shirt NPC)", 309, False), # Gear: buki_get #66 [42h] { Kimahri [03h], Weapon {Piercing [800Bh], Sensor [8000h], Strength +10% [8064h]} }
+ ("Mi'ihen Highroad: Antidote x2 (Red Skirt NPC)", 310, False), # Item: 2x Antidote [200Ah]
+ ("Mi'ihen Highroad: Hi-Potion (Yellow Shirt NPC)", 311, False), # Item: 1x Hi-Potion [2001h]
+ ("Mi'ihen Highroad: Soft x3 (Boy NPC)", 312, False), # Item: 3x Soft [200Bh]
+ ("Mi'ihen Highroad: Red Ring (Crusader NPC)", 313, False), # Gear: buki_get #67 [43h] { Yuna [01h], Armor {HP +10% [8073h], Fire Ward [801Fh]} }
+ ("Mi'ihen Highroad: Ether x1 (NPC)", 314, False), # Item: 1x Ether [2004h]
+ ("Mi'ihen Highroad: Hi-Potion x1 (NPC)", 315, False), # Item: 1x Hi-Potion [2001h]
+ ("Mi'ihen Highroad: 600 gil (Yellow Crusader NPC)", 316, False), # Gil: 600 [06h]
+ ("Mi'ihen Highroad: Lv. 1 Key Sphere x1 (Purple Crusader NPC)", 317, False), # Item: 1x Lv. 1 Key Sphere [2051h]
+ ("Mi'ihen Highroad: Antidote x4 (Woman in Yellow NPC)", 318, False), # Item: 4x Antidote [200Ah]
+ ("Mushroom Rock Road: Tough Bangle (Gray Helmet NPC)", 319, False), # Gear: buki_get #68 [44h] { Lulu [05h], Armor {HP +20% [8074h], Empty} }
+ ("Mushroom Rock Road: Phoenix Down x2 (Blue Shirt Crusader NPC)", 320, False), # Item: 2x Phoenix Down [2006h]
+ ("Mushroom Rock Road: Remedy x1 (Near Grey Helmet NPC)", 321, False), # Item: 1x Remedy [200Fh]
+ ("Mushroom Rock Road: Hi-Potion x1 (Woman in Blue NPC)", 322, False), # Item: 1x Hi-Potion [2001h]
+ ("Mushroom Rock Road: Ether x1 (Purple Helmet NPC)", 323, False), # Item: 1x Ether [2004h]
+ ("Mushroom Rock Road: Hi-Potion x1 (Woman NPC near Save Point)", 324, False), # Item: 1x Hi-Potion [2001h]
+ ("Mushroom Rock Road: 1000 gil (NPC near chest)", 325, False), # Item: 10x Potion [2000h]
+ ("Mushroom Rock Road: 400 gil (NPC near elevator)", 326, False), # Gil: 400 [04h]
+ ("Mushroom Rock Road: X-Potion x1 (NPC near lift)", 327, False), # Item: 1x X-Potion [2002h]
+ ("Mushroom Rock Road: Mega-Potion x1 (NPC)", 328, False), # Item: 1x Mega-Potion [2003h]
+ ("Omega Ruins: Warp Sphere x99 (Chest)", 329, False), # Item: 99x Warp Sphere [2063h]
+ ("Omega Ruins: Teleport Sphere x1 (Chest)", 330, False), # Item: 1x Teleport Sphere [2062h]
+ ("Omega Ruins Friend Sphere x1 (Chest)", 331, False), # Item: 1x Friend Sphere [2061h]
+ ("Omega Ruins: Magic Sphere x1 (Chest)", 332, False), # Item: 1x Magic Sphere [2059h]
+ ("Treasure 333 (Old Entry?)", 333, False), # Key Item: Blossom Crown [A032h]
+ ("Flower Scepter", 334, False), # Key Item: Flower Scepter [A033h]
+ # ("Treasure 335 (Trashed)", 335, False), # Item: 1x Potion [2000h]
+ ("S.S. Liki: Friend Sphere x1 (Clasko NPC)", 336, False), # Item: 1x Friend Sphere [2061h] # Talk to Clasko before Crawler and make sure to have him become a Chocobo Breeder
+ ("Calm Lands: Elixir x1 (Wobbly Chocobo Minigame Reward)", 337, False), # Item: 1x Elixir [2008h]
+ ("Calm Lands: Lv. 1 Key Sphere x1 (Dodger Chocobo Minigame Reward)", 338, False), # Item: 1x Lv. 1 Key Sphere [2051h]
+ ("Calm Lands: Lv. 2 Key Sphere x1 x1 (Hyper Dodger Chocobo Minigame Reward)", 339, False), # Item: 1x Lv. 2 Key Sphere [2052h]
+ ("Calm Lands: Lv. 3 Key Sphere x1 x1 (Catcher Chocobo Minigame Reward)", 340, False), # Item: 1x Lv. 3 Key Sphere [2053h]
+ # ("Treasure 341 (Trashed)", 341, False), # Item: 1x X-Potion [2002h]
+ # ("Treasure 342 (Trashed)", 342, False), # Item: 1x Mega-Potion [2003h]
+ # ("Treasure 343 (Trashed)", 343, False), # Item: 1x Ether [2004h]
+ # ("Treasure 344 (Trashed)", 344, False), # Item: 1x Turbo Ether [2005h]
+ ("Thunder Plains: Yellow Shield (Ground Item)", 345, False), # Gear: buki_get #69 [45h] { Tidus [00h], Armor {Lightningproof [8028h], Empty} }
+ ("Bikanel: Remedy x4 (Chest)", 346, False), # Item: 4x Remedy [200Fh]
+ ("Bikanel: Ether x2 (Chest)", 347, False), # Item: 2x Ether [2004h]
+ ("Bikanel: Hi-Potion x4 (Chest, 1)", 348, False), # Item: 4x Hi-Potion [2001h]
+ ("Bikanel: Mega-Potion x2 (Chest)", 349, False), # Item: 2x Mega-Potion [2003h]
+ ("Bikanel: X-Potion x2 (Chest, 1)", 350, False), # Item: 2x X-Potion [2002h]
+ ("Bikanel: Hi-Potion x4 (Chest, 2)", 351, False), # Item: 4x Hi-Potion [2001h]
+ ("Bikanel: Elixir x1 (Chest)", 352, False), # Item: 1x Elixir [2008h]
+ ("Bikanel: 10000 gil (Chest)", 353, False), # Gil: 10000 [64h]
+ ("Bikanel: Lv. 2 Key Sphere x1 (Chest)", 354, False), # Item: 1x Lv. 2 Key Sphere [2052h]
+ ("Bikanel: Hi-Potion x8 (Chest)", 355, False), # Item: 8x Hi-Potion [2001h]
+ ("Bikanel: Mega-Potion x3 (Chest)", 356, False), # Item: 3x Mega-Potion [2003h]
+ ("Bikanel: X-Potion x2 (Chest, 2)", 357, False), # Item: 2x X-Potion [2002h]
+ ("Bikanel: Megalixir x3 (Chest)", 358, False), # Item: 3x Megalixir [2009h]
+ ("Bikanel: Teleport Sphere x2 (Chest)", 359, False), # Item: 2x Teleport Sphere [2062h]
+ ("Home: Al Bhed Potion x6 (Chest)", 360, False), # Item: 6x Al Bhed Potion [2014h]
+ ("Home: Al Bhed Potion x4 (Chest)", 361, False), # Item: 4x Al Bhed Potion [2014h]
+ ("Home: Lv. 2 Key Sphere x1 (Chest)", 362, False), # Item: 1x Lv. 2 Key Sphere [2052h]
+ ("Home: Lv. 4 Key Sphere x4 (Chest)", 363, False), # Item: 1x Lv. 4 Key Sphere [2054h]
+ ("Home: 10000 gil (Chest)", 364, False), # Gil: 10000 [64h]
+ ("S.S. Winno: Ace Wizard", 365, False), # Gear: buki_get #70 [46h] { Wakka [04h], Weapon {Magic +20% [8069h], Magic +10% [8068h], Magic +5% [8067h], Magic +3% [8066h]} }
+ ("Mi'ihen Highroad: Seeker's Ring (Lose Aeon Fight)", 366, False), # Gear: buki_get #71 [47h] { Yuna [01h], Armor {HP +10% [8073h], Empty} }
+ ("Home: Hi-Potion x2 (NPC on Ground)", 367, False), # Item: 2x Hi-Potion [2001h]
+ ("Mushroom Rock Road: Victorious", 368, False), # Gear: buki_get #72 [48h] { Rikku [06h], Armor {Lightningproof [8028h], Fireproof [8020h], Iceproof [8024h], Empty} }
+ ("Besaid Ruins: Murasame", 369, False), # Gear: buki_get #73 [49h] { Auron [02h], Weapon {Piercing [800Bh], One MP Cost [800Dh], Empty, Empty} }
+ ("Calm Lands: Speed Sphere x30 (Lose Aeon Fight)", 370, False), # Item: 30x Speed Sphere [2048h]
+ ("Aeon's Soul", 371, False), # Key Item: Aeon's Soul [A01Fh]
+ ("Moonflow: Dragon Scale x2 (Win Aeon Fight)", 372, False), # Item: 2x Dragon Scale [2021h]
+ ("Moonflow: Smoke Bomb x6 (Lose Aeon Fight)", 373, False), # Item: 6x Smoke Bomb [2028h]
+ ("Summoner's Soul", 374, False), # Key Item: Summoner's Soul [A01Eh]
+ ("Airship: Al Bhed Potion (NPC)", 375, False), # Item: 4x Al Bhed Potion [2014h]
+ ("Moonflow: Lv. 1 Key Sphere x3 (Shelinda Chest)", 376, False), # Item: 3x Lv. 1 Key Sphere [2051h]
+ ("Moonflow: Lv. 1 Key Sphere x3 (Benke and Biran Chest)", 377, False), # Item: 3x Lv. 1 Key Sphere [2051h]
+ ("Moonflow: Magic Def Sphere x1 (Chest)", 378, False), # Item: 1x Magic Def Sphere [205Ah]
+ ("Calm Lands: Valefor Fight First Reward (Remiem Tower)", 379, False), # Item: 4x Lightning Gem [201Fh]
+ ("Calm Lands: Valefor Post First Fight Reward (Remiem Tower)", 380, False), # Item: 4x Power Sphere [2046h]
+ ("Calm Lands: Ifrit Fight (Remiem Tower)", 381, False), # Item: 30x X-Potion [2002h]
+ ("Calm Lands: Ifrit Post First Fight Reward (Remiem Tower)", 382, False), # Item: 5x Mana Sphere [2047h]
+ ("Calm Lands: Ixion Fight (Remiem Tower)", 383, False), # Item: 10x Chocobo Feather [2036h]
+ ("Calm Lands: Ixion Post First Fight Reward (Remiem Tower)", 384, False), # Item: 8x Power Sphere [2046h]
+ ("Calm Lands: Shiva Fight (Remiem Tower)", 385, False), # Item: 60x Mega-Potion [2003h]
+ ("Calm Lands: Shiva Post First Fight Reward (Remiem Tower)", 386, False), # Item: 6x Star Curtain [203Ah]
+ ("Calm Lands: Bahamut Post First Fight Reward (Remiem Tower)", 387, False), # Item: 8x Mana Sphere [2047h]
+ ("Calm Lands: Yojimbo Fight (Remiem Tower)", 388, False), # Item: 8x Shadow Gem [2029h]
+ ("Calm Lands: Yojimbo Post First Fight Reward (Remiem Tower)", 389, False), # Item: 10x Power Sphere [2046h]
+ ("Calm Lands: Anima Fight (Remiem Tower)", 390, False), # Item: 60x Stamina Spring [203Dh]
+ ("Calm Lands: Anima Post First Fight Reward (Remiem Tower)", 391, False), # Item: 10x Mana Sphere [2047h]
+ ("Calm Lands: Magus Sisters Fight (Remiem Tower)", 392, False), # Item: 40x Shining Gem [202Ah]
+ ("Calm Lands: Magus Sisters Post First Fight Reward (Remiem Tower)", 393, False), # Item: 12x Power Sphere [2046h]
+ ("Lake Macalania: Teleport Sphere x1 (Butterfly Game after Airship)", 394, False), # Item: 1x Teleport Sphere [2062h]
+ ("Home: Skill Sphere x1 (Al Bhed Quiz Chest)", 395, False), # Item: 1x Skill Sphere [204Dh]
+ ("Home: Skill Sphere x1 (Al Bhed Password Chest)", 396, False), # Item: 1x Special Sphere [204Ch]
+ ("Home: Skill Sphere x1 (Al Bhed Vocabulary Chest)", 397, False), # Item: 1x Friend Sphere [2061h]
+ ("Home: Elixir x1 (Al Bhed Vocabulary Chest)", 398, False), # Item: 1x Elixir [2008h]
+ ("Treasure 399 (Trashed)", 399, False), # Item: 1x Hi-Potion [2001h]
+ ("Treasure 400 (Trashed)", 400, False), # Item: 1x Mega-Potion [2003h]
+ ("Treasure 401 (Trashed)", 401, False), # Item: 1x Soft [200Bh]
+ ("Treasure 402 (Trashed)", 402, False), # Item: 1x Potion [2000h]
+ ("Treasure 403 (Trashed)", 403, False), # Item: 1x Remedy [200Fh]
+ ("Treasure 404 (Trashed)", 404, False), # Item: 2x Potion [2000h]
+ ("Complete Al Bhed Primers", 405, False), # Item: 99x Underdog's Secret [206Eh]
+ ("Besaid: Wht Magic Sphere x1 (Aeon Room)", 406, False), # Item: 1x Wht Magic Sphere [204Eh]
+ ("Besaid: Elixir x1 (Aeon Room)", 407, False), # Item: 1x Elixir [2008h]
+ ("Besaid: Hi-Potion x1 (Aeon Room)", 408, False), # Item: 1x Hi-Potion [2001h]
+ ("Besaid: Potion x2 (Aeon Room)", 409, False), # Item: 2x Potion [2000h]
+ # ("S.S Liki: Potion (Yuna's suitcase)", 410, False), # Item: 1x Potion [2000h] # Definitely Yuna's Suitcase
+ # ("Treasure 411 (Trashed)", 411, False), # Item: 1x Potion [2000h]
+ # ("Treasure 412 (Trashed)", 412, False), # Item: 1x Potion [2000h]
+ # ("Treasure 413 (Trashed)", 413, False), # Item: 1x Potion [2000h]
+ # ("Treasure 414 (Trashed)", 414, False), # Item: 1x Potion [2000h]
+ # ("Treasure 415 (Trashed)", 415, False), # Item: 1x Potion [2000h]
+ # ("Treasure 416 (Trashed)", 416, False), # Item: 1x Potion [2000h]
+ ("Calm Lands: Elixir x1 (Chocobo Race Reward)", 417, False), # Item: 1x Elixir [2008h]
+ ("Calm Lands: Megalixir x1 (Chocobo Race Reward)", 418, False), # Item: 1x Megalixir [2009h]
+ ("Calm Lands: Three Stars x60 (Chocobo Race Reward)", 419, False), # Item: 60x Three Stars [2045h]
+ ("Calm Lands: Pendulum x30 (Chocobo Race Reward)", 420, False), # Item: 30x Pendulum [2069h]
+ ("Calm Lands: Wings to Discovery x30 (Chocobo Race Reward)", 421, False), # Item: 30x Wings to Discovery [206Ch]
+ # ("Treasure 422", 422, False), # Item: 1x Potion [2000h]
+ ("Mi'ihen Highroad: Lv. 1 Key Sphere x1 (NPC)", 423, False), # Item: 1x Lv. 1 Key Sphere [2051h]
+ ("Monster Arena: Stamina Tonic x99 (NPC)", 424, False), # Item: 99x Stamina Tonic [2043h]
+ ("Monster Arena: Poison Fang x99 (NPC)", 425, False), # Item: 99x Poison Fang [202Dh]
+ ("Monster Arena: Soul Spring x99 (NPC)", 426, False), # Item: 99x Soul Spring [203Eh]
+ ("Monster Arena: Candle of Life x99 (NPC)", 427, False), # Item: 99x Candle of Life [2030h]
+ ("Monster Arena: Petrify Grenade x99 (NPC)", 428, False), # Item: 99x Petrify Grenade [2031h]
+ ("Monster Arena: Chocobo Wing x99 (NPC)", 429, False), # Item: 99x Chocobo Wing [2037h]
+ ("Monster Arena: Shining Gem x60 (NPC)", 430, False), # Item: 60x Shining Gem [202Ah]
+ ("Monster Arena: Shadow Gem x99 (NPC)", 431, False), # Item: 99x Shadow Gem [2029h]
+ ("Monster Arena: Farplane Wind x60 (NPC)", 432, False), # Item: 60x Farplane Wind [2033h]
+ ("Monster Arena: Silver Hourglass x40 (NPC)", 433, False), # Item: 40x Silver Hourglass [202Eh]
+ ("Blossom Crown", 434, False), # Key Item: Blossom Crown [A032h]
+ ("Monster Arena: Lunar Curtain x99 (NPC)", 435, False), # Item: 99x Lunar Curtain [2038h]
+ ("Monster Arena: Designer Wallet x60 (NPC)", 436, False), # Item: 60x Designer Wallet [2034h]
+ ("Monster Arena: Chocobo Feather x99 (NPC)", 437, False), # Item: 99x Chocobo Feather [2036h]
+ ("Monster Arena: Stamina Spring x99 (NPC)", 438, False), # Item: 99x Stamina Spring [203Dh]
+ ("Monster Arena: Mega Phoenix x99 (NPC)", 439, False), # Item: 99x Mega Phoenix [2007h]
+ ("Monster Arena: Mana Tonic x60 (NPC)", 440, False), # Item: 60x Mana Tonic [2044h]
+ ("Monster Arena: Mana Spring x99 (NPC)", 441, False), # Item: 99x Mana Spring [203Ch]
+ ("Monster Arena: Stamina Tablet x60 (NPC)", 442, False), # Item: 60x Stamina Tablet [2040h]
+ ("Monster Arena: Twin Stars x60 (NPC)", 443, False), # Item: 60x Twin Stars [2042h]
+ ("Monster Arena: Star Curtain x99 (NPC)", 444, False), # Item: 99x Star Curtain [203Ah]
+ ("Monster Arena: Gold Hourglass x99 (NPC)", 445, False), # Item: 99x Gold Hourglass [202Fh]
+ ("Monster Arena: Purifying Salt x99 (NPC)", 446, False), # Item: 99x Purifying Salt [203Fh]
+ ("Monster Arena: Healing Spring x99 (NPC)", 447, False), # Item: 99x Healing Spring [203Bh]
+ ("Monster Arena: Turbo Ether x60 (NPC)", 448, False), # Item: 60x Turbo Ether [2005h]
+ ("Monster Arena: Light Curtain x99 (NPC)", 449, False), # Item: 99x Light Curtain [2039h]
+ ("Monster Arena: Mana Tablet x60 (NPC)", 450, False), # Item: 60x Mana Tablet [2041h]
+ ("Monster Arena: Three Stars x60 (NPC)", 451, False), # Item: 60x Three Stars [2045h]
+ ("Monster Arena: Supreme Gem x60 (NPC)", 452, False), # Item: 60x Supreme Gem [202Ch]
+ ("Monster Arena: Door to Tomorrow x99 (NPC)", 453, False), # Item: 99x Door to Tomorrow [206Bh]
+ ("Monster Arena: Gambler's Spirit x99 (NPC)", 454, False), # Item: 99x Gambler's Spirit [206Dh]
+ ("Monster Arena: Winning Formula x99 (NPC)", 455, False), # Item: 99x Winning Formula [206Fh]
+ ("Monster Arena: Dark Matter x99 (NPC)", 456, False), # Item: 99x Dark Matter [2035h]
+ ("Monster Arena: Megalixir x30 (NPC)", 457, False), # Item: 30x Megalixir [2009h]
+ ("Monster Arena: Master Sphere x10 (NPC)", 458, False), # Item: 10x Master Sphere [2050h]
+ ("Besaid: Map", 459, False), # Item: 1x Map [2064h]
+ ("Lake Macalania: Magic Def Sphere x1 (Aeon Room)", 460, False), # Item: 1x Magic Def Sphere [205Ah]
+ ("Lake Macalania: Accuracy Sphere x1 (Aeon Room)", 461, False), # Item: 1x Accuracy Sphere [205Dh]
+ ("Lake Macalania: Magic Sphere x1 (Aeon Room)", 462, False), # Item: 1x Magic Sphere [2059h]
+ ("Djose: Agility Sphere x1 (Aeon Room)", 463, False), # Item: 1x Agility Sphere [205Bh]
+ ("Djose: Magic Def Sphere x1 (Aeon Room)", 464, False), # Item: 1x Magic Def Sphere [205Ah]
+ ("Djose: Luck Sphere x1 (Aeon Room)", 465, False), # Item: 1x Luck Sphere [205Eh]
+ ("Calm Lands: Defense Sphere x1 (Remiem Temple Aeon Room)", 466, False), # Item: 1x Defense Sphere [2058h]
+ ("Besaid: Evasion Sphere x1 (Aeon Room)", 467, False), # Item: 1x Evasion Sphere [205Ch]
+ ("Calm Lands: Strength Sphere x1 (Yojimbo Aeon Room)", 468, False), # Item: 1x Strength Sphere [2057h]
+ ("Bikanel: Shadow Gem x2 (Robeya Minigame Chest)", 469, False), # Item: 2x Shadow Gem [2029h]
+ ("Bikanel: Shining Gem x1 (Robeya Minigame Chest)", 470, False), # Item: 1x Shining Gem [202Ah]
+ ("Bikanel: Blessed Gem x1 (Robeya Minigame Chest)", 471, False), # Item: 1x Blessed Gem [202Bh]
+ ("Bikanel: Potion x1 (Cactuar Sidequest Prize)", 472, False), # Item: 1x Potion [2000h]
+ ("Bikanel: Elixir x1 (Cactuar Sidequest Prize)", 473, False), # Item: 1x Elixir [2008h]
+ ("Bikanel: Megalixir x1 (Cactuar Sidequest Prize)", 474, False), # Item: 1x Megalixir [2009h]
+ ("Bikanel: Friend Sphere x1 (Cactuar Sidequest Prize)", 475, False), # Item: 1x Friend Sphere [2061h]
+ ("Kilika: Agility Sphere x1 (Aeon Room)", 476, False), # Item: 1x Agility Sphere [205Bh]
+ ("Kilika: Defense Sphere x1 (Aeon Room)", 477, False), # Item: 1x Defense Sphere [2058h]
+ ("Kilika: Luck Sphere x1 (Aeon Room)", 478, False), # Item: 1x Luck Sphere [205Eh]
+ ("Kilika: Accuracy Sphere x1 (Aeon Room)", 479, False), # Item: 1x Accuracy Sphere [205Dh]
+ ("Besaid: Dragoon Lance", 480, False), # Gear: buki_get #75 [4Bh] { Kimahri [03h], Weapon {Magic Counter [8005h], Evade & Counter [8004h], Empty, Empty} }
+ ("Mi'ihen Ruins: Sonar", 481, False), # Gear: buki_get #76 [4Ch] { Rikku [06h], Weapon {Initiative [8002h], Poisonstrike [803Ah], Empty} }
+ ("Battle Site: Phantom Bangle", 482, False), # Gear: buki_get #77 [4Dh] { Lulu [05h], Armor {Ice Eater [8025h], Fire Eater [8021h], !Water Eater [802Dh]} }
+ ("Sanubia Sands: Ascalon", 483, False), # Gear: buki_get #78 [4Eh] { Tidus [00h], Weapon {Double AP [8012h]} }
+ ("Djose: Destruction Sphere", 484, False), # Item: 1x Magic Sphere [2059h]
+ ("Lake Macalania: Destruction Sphere", 485, False), # Item: 1x Luck Sphere [205Eh]
+ ("Inside Sin: Prism Ball (Point of No Return)", 486, False), # Gear: buki_get #79 [4Fh] { Wakka [04h], Weapon {Magic Counter [8005h], Empty} }
+ ("Inside Sin: Stillblade (Point of No Return)", 487, False), # Gear: buki_get #80 [50h] { Auron [02h], Weapon {Silencestrike [8042h], Stonestrike [8036h], Empty} }
+ ("Inside Sin: Skill Sphere x1 (Point of No Return)", 488, False), # Item: 1x Skill Sphere [204Dh]
+ ("Inside Sin: Mage's Staff (Point of No Return)", 489, False), # Gear: buki_get #81 [51h] { Yuna [01h], Weapon {Magic +10% [8068h], Magic +5% [8067h], Magic +3% [8066h], Empty} }
+ ("Inside Sin: Knight Lance (Point of No Return)", 490, False), # Gear: buki_get #82 [52h] { Kimahri [03h], Weapon {Strength +10% [8064h], Strength +5% [8063h], Strength +3% [8062h], Empty} }
+ ("Inside Sin: Wht Magic Sphere x1 (Point of No Return)", 491, False), # Item: 1x Wht Magic Sphere [204Eh]
+ ("Inside Sin: Infinity (Point of No Return)", 492, False), # Gear: buki_get #83 [53h] { Rikku [06h], Weapon {One MP Cost [800Dh], Sensor [8000h]} }
+ ("Inside Sin: Wicked Cait Sith (Point of No Return)", 493, False), # Gear: buki_get #84 [54h] { Lulu [05h], Weapon {Deathstrike [802Eh], Empty, Empty, Empty} }
+ ("Inside Sin: Attribute Sphere x1 (Point of No Return)", 494, False), # Item: 1x Attribute Sphere [204Bh]
+ ("Inside Sin: Hrunting (Point of No Return)", 495, False), # Gear: buki_get #85 [55h] { Tidus [00h], Weapon {SOS Overdrive [8010h]} }
+ ("Mark of Conquest", 496, False), # Key Item: Mark of Conquest [A029h]
+ ("Story Win vs. Luca Goers Reward", 497, False), # Item: 1x Strength Sphere [2057h]
+]]
+
+character_names = [
+ "Tidus",
+ "Yuna",
+ "Auron",
+ "Kimahri",
+ "Wakka",
+ "Lulu",
+ "Rikku"
+]
+
+FFXSphereGridLocations: List[List[FFXLocationData]] = [
+ [FFXLocationData(location[1]+SphereGridOffset, *location) for location in [(f"{name}: Sphere Grid Node {i}", i + character*100, False) for i in range(100)]]
+ for character, name in enumerate(character_names)
+]
+
+
+allLocations = list(chain(FFXTreasureLocations,
+ FFXBossLocations,
+ FFXPartyMemberLocations,
+ FFXOverdriveLocations,
+ FFXOverdriveModeLocations,
+ FFXOtherLocations,
+ FFXRecruitLocations,
+ *FFXSphereGridLocations))
+
+def create_location_label_to_id_map() -> Dict[str, int]:
+ """
+ Creates a map from location labels to their AP location id (address)
+ """
+ label_to_id_map: Dict[str, int] = {}
+ for location in allLocations:
+ label_to_id_map[location.name] = location.rom_address
+
+ return label_to_id_map
diff --git a/worlds/ffx/options.py b/worlds/ffx/options.py
new file mode 100644
index 000000000000..11becdbd5624
--- /dev/null
+++ b/worlds/ffx/options.py
@@ -0,0 +1,118 @@
+"""
+Option definitions for Final Fantasy ¨X
+"""
+from dataclasses import dataclass
+from Options import Choice, DefaultOnToggle, Option, Range, Toggle, PerGameCommonOptions
+
+class GoalRequirement(Choice):
+ """
+ Sets the requirement to start the final battles. Defeating Yu Yevon is always the goal.
+ None: No requirements.
+ Party Members: Requires unlocking a number of party members (not counting Aeons).
+ Party Members and Aeons: Requires unlocking a number of party members (including Aeons).
+ Pilgrimage: Complete all required temples, and defeat the boss in Zanarkand Ruins.
+ """
+ display_name = "Goal Requirement"
+ default = 0
+ option_none = 0
+ option_party_members = 1
+ option_pilgrimage = 2
+ option_party_members_and_aeons = 3
+
+
+class RequiredPartyMembers(Range):
+ """
+ Sets how many party members are required if goal_requirement is set to party_members or party_members_and_aeons.
+ Default is 7. Max is 8 for party_members, 16 for party_members_and_aeons.
+ """
+ display_name = "Required Party Members"
+ default = 7
+ range_start = 1
+ range_end = 16
+
+
+class APMultiplier(Range):
+ """
+ Sets the AP multiplier.
+ Default is 2.
+ """
+ display_name = "AP Multiplier"
+ default = 2
+ range_start = 1
+ range_end = 10
+
+
+class SphereGridRandomization(Choice):
+ """
+ Sets whether the Sphere Grid is randomized.
+ Default is off.
+ """
+ display_name = "Sphere Grid Randomization"
+ default = 0
+ option_off = 0
+
+
+class SuperBosses(Toggle):
+ """
+ Sets whether super boss locations are included or not. If off they will only have filler items.
+ Default is off.
+ """
+ display_name = "Super Bosses"
+ default = 0
+ option_off = 0
+ option_on = 1
+
+
+class MiniGames(Toggle):
+ """
+ Sets whether minigames (blitzball, lightning dodging, etc.) are included or not. If off they will only have filler items.
+ Default is off.
+ """
+ display_name = "Minigames"
+ default = 0
+ option_off = 0
+ option_on = 1
+
+class RecruitSanity(Toggle):
+ """
+ Sets whether Blitzball Free Agents are included or not. If off they will only have filler items.
+ Default is off.
+ """
+ display_name = "Recruit Sanity"
+ default = 0
+ option_off = 0
+ option_on = 1
+
+
+class TrapPercentage(Range):
+ """
+ Sets the percentage of non-progression items that will be traps.
+ Default is 0.
+ """
+ display_name = "Traps Enabled"
+ default = 0
+ range_start = 0
+ range_end = 100
+
+
+class LogicDifficulty(Range):
+ """
+ Sets how strict the logic is for region access. Higher is harder / less restrictive.
+ Default is 3.
+ """
+ display_name = "Logic Difficulty"
+ default = 3
+ range_start = 1
+ range_end = 10
+
+@dataclass
+class FFXOptions(PerGameCommonOptions):
+ goal_requirement: GoalRequirement
+ required_party_members: RequiredPartyMembers
+ ap_multiplier: APMultiplier
+ sphere_grid_randomization: SphereGridRandomization
+ super_bosses: SuperBosses
+ mini_games: MiniGames
+ recruit_sanity: RecruitSanity
+ trap_percentage: TrapPercentage
+ logic_difficulty: LogicDifficulty
diff --git a/worlds/ffx/regions.py b/worlds/ffx/regions.py
new file mode 100644
index 000000000000..10481c0be3b7
--- /dev/null
+++ b/worlds/ffx/regions.py
@@ -0,0 +1,319 @@
+from BaseClasses import Entrance, ItemClassification, Region, Location, LocationProgressType, CollectionState
+import json
+import pkgutil
+import typing
+from typing import NamedTuple
+
+from .locations import FFXLocation, FFXTreasureLocations, FFXPartyMemberLocations, FFXBossLocations, \
+ FFXOverdriveLocations, FFXOtherLocations, FFXRecruitLocations, FFXSphereGridLocations, FFXLocationData, TreasureOffset, BossOffset, PartyMemberOffset, RecruitOffset
+from .rules import ruleDict
+from .items import party_member_items, FFXItem
+from worlds.generic.Rules import add_rule
+from ..AutoWorld import World
+
+if typing.TYPE_CHECKING:
+ from .__init__ import FFXWorld
+else:
+ FFXWorld = object
+
+class RegionData(dict):
+ @property
+ def name(self) -> str:
+ return self["name"]
+ @property
+ def id(self) -> int:
+ return self["id"]
+ @property
+ def treasures(self) -> list[int]:
+ return self["treasures"]
+ @property
+ def party_members(self) -> list[int]:
+ return self["party_members"]
+ @property
+ def bosses(self) -> list[int]:
+ return self["bosses"]
+ @property
+ def overdrives(self) -> list[int]:
+ return self["overdrives"]
+ @property
+ def other(self) -> list[int]:
+ return self["other"]
+ @property
+ def recruits(self) -> list[int]:
+ return self["recruits"]
+ @property
+ def leads_to(self) -> list[int]:
+ return self["leads_to"]
+ @property
+ def rules(self) -> list[str]:
+ return self["rules"]
+
+
+def create_regions(world: FFXWorld, player) -> None:
+ def create_region_locations(region_name, treasures):
+ region = Region(region_name, player, world.multiworld)
+ for treasure_id in treasures:
+ location = [x for x in FFXTreasureLocations if x.location_id == treasure_id][0]
+ new_location = FFXLocation(player, location.name, location.rom_address, region)
+ if location.missable:
+ new_location.progress_type = LocationProgressType.EXCLUDED
+ region.locations.append(new_location)
+ return region
+
+ def add_locations_by_ids(region: Region, location_ids: list[int], location_data: list[FFXLocationData], location_type: str = ""):
+ for id in location_ids:
+ locations = [x for x in location_data if x.location_id == id]
+ if len(locations) != 1:
+ #print(f"Ambiguous or invalid location id {id} ({location_type}) in Region {region.name}. Found {locations}")
+ continue
+ location = locations[0]
+ new_location = FFXLocation(player, location.name, location.rom_address, region)
+ if location.missable:
+ new_location.progress_type = LocationProgressType.EXCLUDED
+ region.locations.append(new_location)
+ all_locations.append(new_location)
+
+
+
+ menu_region = Region("Menu", player, world.multiworld)
+ world.multiworld.regions.append(menu_region)
+
+ region_file = pkgutil.get_data(__name__, "data/regions.json")
+ #region_data_list = [RegionData(x) for x in json.loads(region_file)]
+ region_data_list = json.loads(region_file)
+ region_data_list = [RegionData(x) for x in region_data_list]
+
+ region_dict: dict[int, Region] = dict()
+ region_rules: dict[int, list[str]] = dict()
+
+ all_locations = []
+
+ for region_data in region_data_list:
+ new_region = Region(region_data.name, player, world.multiworld)
+ region_dict[region_data.id] = new_region
+ world.multiworld.regions.append(new_region)
+ if len(region_data.rules) > 0:
+ region_rules[region_data.id] = region_data.rules
+
+ add_locations_by_ids(new_region, region_data.treasures, FFXTreasureLocations, "Treasure")
+ # for id in region_data.treasures:
+ # print(region_data.name, id)
+ # location = [x for x in FFXTreasureLocations if x.location_id == id][0]
+ # new_location = FFXLocation(player, location.name, location.rom_address, new_region)
+ # if location.missable:
+ # new_location.progress_type = LocationProgressType.EXCLUDED
+ # new_region.locations.append(new_location)
+ # all_locations.append(new_location)
+
+ # TODO: Implement in client
+ add_locations_by_ids(new_region, region_data.party_members, FFXPartyMemberLocations, "Party Member")
+ # for id in region_data.party_members:
+ # print(region_data.name, id)
+ # location = [x for x in FFXPartyMemberLocations if x.location_id == id][0]
+ # new_location = FFXLocation(player, location.name, location.rom_address, new_region)
+ # if location.missable:
+ # new_location.progress_type = LocationProgressType.EXCLUDED
+ # new_region.locations.append(new_location)
+ # all_locations.append(new_location)
+
+ add_locations_by_ids(new_region, region_data.bosses, FFXBossLocations, "Boss")
+ # for id in region_data.bosses:
+ # print(region_data.name, id)
+ # location = [x for x in FFXBossLocations if x.location_id == id][0]
+ # new_location = FFXLocation(player, location.name, location.rom_address, new_region)
+ # if location.missable:
+ # new_location.progress_type = LocationProgressType.EXCLUDED
+ # new_region.locations.append(new_location)
+ # all_locations.append(new_location)
+
+ # TODO: Implement in client
+ # add_locations_by_ids(new_region, region_data.overdrives, FFXOverdriveLocations, "Overdrive")
+ # for id in region_data.overdrives:
+ # print(region_data.name, id)
+ # location = [x for x in FFXOverdriveLocations if x.location_id == id][0]
+ # new_location = FFXLocation(player, location.name, location.rom_address, new_region)
+ # if location.missable:
+ # new_location.progress_type = LocationProgressType.EXCLUDED
+ # new_region.locations.append(new_location)
+ # all_locations.append(new_location)
+
+ # TODO: Implement in client
+ add_locations_by_ids(new_region, region_data.other, FFXOtherLocations, "Other")
+ # for id in region_data.other:
+ # print(region_data.name, id)
+ # location = [x for x in FFXOtherLocations if x.location_id == id][0]
+ # new_location = FFXLocation(player, location.name, location.rom_address, new_region)
+ # if location.missable:
+ # new_location.progress_type = LocationProgressType.EXCLUDED
+ # new_region.locations.append(new_location)
+ # all_locations.append(new_location)
+
+ add_locations_by_ids(new_region, region_data.recruits, FFXRecruitLocations, "Recruit")
+
+ for region_data in region_data_list:
+ curr_region = region_dict[region_data.id]
+ for region_id in region_data.leads_to:
+ other_region = region_dict[region_id]
+ rules = region_rules.get(region_id)
+ if rules is not None:
+ rule_lambdas = [ruleDict[x](world) for x in rules]
+ new_rule = lambda state, rule_list=rule_lambdas: all([rule(state) for rule in rule_list])
+ else:
+ new_rule = None
+ curr_region.connect(other_region, rule=new_rule)
+
+ top_level_regions: list[tuple[Region, Entrance]] = []
+ for region_id, other_region in region_dict.items():
+ if len(other_region.entrances) == 0:
+ rules = region_rules.get(region_id)
+ if rules is not None:
+ rule_lambdas = [ruleDict[x](world) for x in rules]
+ new_rule = lambda state, rule_list=rule_lambdas: all([rule(state) for rule in rule_list])
+ else:
+ new_rule = None
+ menu_entrance: Entrance = menu_region.connect(other_region, rule=new_rule)
+ top_level_regions.append((other_region, menu_entrance))
+
+ #for this_region, _ in top_level_regions:
+ # for other_region, menu_entrance in top_level_regions:
+ # if this_region == other_region:
+ # continue
+ # world.multiworld.register_indirect_condition(this_region, menu_entrance)
+
+ if not world.options.super_bosses.value:
+ super_boss_location_ids = [
+ 2, # "Besaid: Dark Valefor"
+ 19, # "Bikanel: Dark Ifrit"
+ 13, # "Thunder Plains: Dark Ixion"
+ 18, # "Lake Macalania: Dark Shiva"
+ 38, # "Zanarkand: Dark Bahamut"
+ 31, # "Cavern of the Stolen Fayth: Dark Yojimbo"
+ 45, # "Mushroom Rock Road: Dark Mindy"
+ 46, # "Mushroom Rock Road: Dark Sandy"
+ 47, # "Mushroom Rock Road: Dark Cindy"
+ 34, # "Gagazet (Outside): Dark Anima"
+ 25, # "Airship: Penance"
+ 44, # "Omega Ruins: Omega Weapon"
+ #30, # "Monster Arena: Nemesis"
+ ]
+ for id in super_boss_location_ids:
+ location_name = world.location_id_to_name[id | BossOffset]
+ #world.get_location(location_name).progress_type = LocationProgressType.EXCLUDED
+ world.options.exclude_locations.value.add(location_name)
+ location_name = world.location_id_to_name[332 | TreasureOffset]
+ #world.get_location(location_name).progress_type = LocationProgressType.EXCLUDED
+ world.options.exclude_locations.value.add(location_name)
+
+ if not world.options.mini_games.value:
+ mini_game_location_ids = [
+ 338, # "Calm Lands: Lv. 1 Key Sphere x1 (Dodger Chocobo Minigame Reward)"
+ 339, # "Calm Lands: Lv. 2 Key Sphere x1 x1 (Hyper Dodger Chocobo Minigame Reward)"
+ 340, # "Calm Lands: Lv. 3 Key Sphere x1 x1 (Catcher Chocobo Minigame Reward)"
+ 417, # "Calm Lands: Elixir x1 (Chocobo Race Reward)"
+ 418, # "Calm Lands: Megalixir x1 (Chocobo Race Reward)"
+ 419, # "Calm Lands: Three Stars x60 (Chocobo Race Reward)"
+ 420, # "Calm Lands: Pendulum x30 (Chocobo Race Reward)"
+ 421, # "Calm Lands: Wings to Discovery x30 (Chocobo Race Reward)"
+ 189, # "Thunder Plains: Megalixir x4 (Dodging Minigame Reward)",
+ 190, # "Thunder Plains: HP Sphere x3 (Dodging Minigame Reward)",
+ 191, # "Thunder Plains: Strength Sphere x3 (Dodging Minigame Reward)",
+ 192, # "Thunder Plains: MP Sphere x2 (Dodging Minigame Reward)",
+ 193, # "Thunder Plains: Mega-Potion x2 (Dodging Minigame Reward)",
+ 194, # "Thunder Plains: X-Potion x2 (Dodging Minigame Reward)",
+ 497, # "Story Win vs. Luca Goers Reward",
+ 274, # "Sun Sigil",
+ 278, # "Venus Sigil",
+ 277, # "Saturn Sigil",
+ 279, # "Mercury Sigil",
+ 244, # "Jupiter Sigil",
+ 114, # "Caladbolg"
+ ]
+ for id in mini_game_location_ids:
+ location_name = world.location_id_to_name[id | TreasureOffset]
+ #world.get_location(location_name).progress_type = LocationProgressType.EXCLUDED
+ world.options.exclude_locations.value.add(location_name)
+
+ if not world.options.recruit_sanity.value:
+ recruit_location_ids = []
+ for location in FFXRecruitLocations:
+ recruit_location_ids.append(location.location_id)
+ for id in recruit_location_ids:
+ location_name = world.location_id_to_name[id | RecruitOffset]
+ world.options.exclude_locations.value.add(location_name)
+
+ final_region = world.get_region("Sin: Braska's Final Aeon")
+ final_region.add_event("Sin: Braska's Final Aeon", "Victory", location_type=FFXLocation, item_type=FFXItem)
+ final_aeon = world.get_location("Sin: Braska's Final Aeon")
+
+ #final_aeon.place_locked_item(victory_event)
+
+ world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.player)
+ match world.options.goal_requirement.value:
+ case world.options.goal_requirement.option_none:
+ pass
+ case world.options.goal_requirement.option_party_members:
+ final_aeon.access_rule = lambda state: state.has_from_list_unique(
+ [character.itemName for character in party_member_items[:8]], world.player, min(world.options.required_party_members.value, 8))
+ case world.options.goal_requirement.option_party_members_and_aeons:
+ final_aeon.access_rule = lambda state: state.has_from_list_unique(
+ [character.itemName for character in party_member_items], world.player, world.options.required_party_members.value)
+ case world.options.goal_requirement.option_pilgrimage:
+ final_aeon.access_rule = lambda state: (
+ state.can_reach_location(world.location_id_to_name[ 8 | PartyMemberOffset], world.player) and # Valefor
+ state.can_reach_location(world.location_id_to_name[ 9 | PartyMemberOffset], world.player) and # Ifrit
+ state.can_reach_location(world.location_id_to_name[10 | PartyMemberOffset], world.player) and # Ixion
+ state.can_reach_location(world.location_id_to_name[11 | PartyMemberOffset], world.player) and # Shiva
+ state.can_reach_location(world.location_id_to_name[12 | PartyMemberOffset], world.player) and # Bahamut
+ state.can_reach_location(world.location_id_to_name[37 | BossOffset ], world.player) # Yunalesca
+ )
+
+
+ #world.get_location("Monster Arena: Nemesis" ).progress_type = LocationProgressType.EXCLUDED
+
+ # character_names = [
+ # "Tidus",
+ # "Yuna",
+ # "Auron",
+ # "Kimahri",
+ # "Wakka",
+ # "Lulu",
+ # "Rikku"
+ # ]
+ #
+ # for character, region in enumerate(FFXSphereGridLocations):
+ # new_region = Region(f"Sphere Grid: {character_names[character]}", player, world.multiworld)
+ # for location in region:
+ # new_location = FFXLocation(player, location.name, location.rom_address, new_region)
+ # if location.missable:
+ # new_location.progress_type = LocationProgressType.EXCLUDED
+ # new_region.locations.append(new_location)
+ # all_locations.append(new_location)
+ # menu_region.connect(new_region, rule=lambda state, i=character: state.has(party_members[i].itemName, world.player))
+
+
+ #test_region = Region("Test", player, world.multiworld)
+ #menu_region.connect(test_region)
+ #
+ #for location in FFXTreasureLocations:
+ # new_location = FFXLocation(player, location.name, location.rom_address, test_region)
+ # new_location.progress_type = LocationProgressType.EXCLUDED
+ # test_region.locations.append(new_location)
+
+ #baaj_1_region = Region("Baaj Temple 1st visit", player, world.multiworld)
+ #baaj_1_region = create_region_locations("Baaj Temple 1st visit", [0, 1, 2, 3, 6, 7, 219, 213]) # + Klikk
+
+ #al_bhed_ship_region = Region("Al Bhed Ship", player, world.multiworld)
+ #al_bhed_ship_region = create_region_locations("Al Bhed Ship", [296]) # + Tros, Al Bhed Primer I
+
+ #baaj_2_region = Region("Baaj Temple 2nd visit", player, world.multiworld)
+ #baaj_2_region = create_region_locations("Baaj Temple 2nd visit", [204, 205, 5]) # + Anima
+
+
+
+
+ #besaid_1_region = Region("Besaid Island 1st visit", player, world.multiworld)
+ #besaid_1_region = create_region_locations("Besaid Island 1st visit", [268, 9, 283, 285, 284, 282, 90, 91, 92, 13, 14, 215, 216, 15, 459]) # + Al Bhed Primer II, Yuna, Lulu, Wakka, Valefor, Brotherhood
+
+
+
+ #besaid_2_region = Region("Besaid Island 2nd visit", player, world.multiworld)
diff --git a/worlds/ffx/rules.py b/worlds/ffx/rules.py
new file mode 100644
index 000000000000..d1af7a13f0dd
--- /dev/null
+++ b/worlds/ffx/rules.py
@@ -0,0 +1,297 @@
+import typing
+from collections import Counter
+from typing import Callable
+
+from BaseClasses import CollectionState
+from worlds.generic.Rules import add_rule, CollectionRule
+from . import key_items
+from .items import character_names, stat_abilities, item_to_stat_value, aeon_names, region_unlock_items, equipItemOffset
+from .locations import TreasureOffset, OtherOffset, BossOffset, PartyMemberOffset
+
+if typing.TYPE_CHECKING:
+ from .__init__ import FFXWorld
+else:
+ FFXWorld = object
+
+world_battle_levels: dict[str, int] = {
+"Baaj Temple": 1,
+"Besaid": 2,
+"Kilika": 3,
+"Luca": 4,
+"Mi'ihen Highroad": 5,
+"Mushroom Rock Road": 6,
+"Djose": 7,
+"Moonflow": 8,
+"Guadosalam": 1,
+"Thunder Plains": 9,
+"Macalania": 10,
+"Bikanel": 11,
+"Bevelle": 12,
+"Calm Lands": 13,
+"Cavern of the Stolen Fayth": 13,
+"Mt. Gagazet": 14,
+"Zanarkand Ruins": 15,
+"Sin": 16,
+"Airship": 12,
+"Omega Ruins": 17,
+}
+
+region_to_first_visit: dict[str, str] = {
+"Baaj Temple": "Baaj Temple 1st visit",
+"Besaid": "Besaid Island 1st visit",
+"Kilika": "Kilika 1st visit: Pre-Geneaux",
+"Luca": "Luca 1st visit: Pre-Oblitzerator",
+"Mi'ihen Highroad": "Mi'ihen Highroad 1st visit: Pre-Chocobo Eater",
+"Mushroom Rock Road": "Mushroom Rock Road 1st visit: Pre-Sinspawn Gui",
+"Djose": "Djose 1st visit",
+"Moonflow": "Moonflow 1st visit: Pre-Extractor",
+"Guadosalam": "Guadosalam 1st visit",
+"Thunder Plains": "Thunder Plains 1st visit",
+"Macalania": "Macalania Woods 1st visit: Pre-Spherimorph",
+"Bikanel": "Bikanel 1st visit",
+"Bevelle": "Bevelle 1st visit: Pre-Isaaru",
+"Calm Lands": "Calm Lands 1st visit: Pre-Defender X",
+"Cavern of the Stolen Fayth": "Cavern of the Stolen Fayth 1st visit",
+"Mt. Gagazet": "Mt. Gagazet 1st visit: Pre-Biran and Yenke",
+"Zanarkand Ruins": "Zanarkand Ruins 1st visit: Pre-Spectral Keeper",
+"Sin": "Sin: Pre-Seymour Omnis",
+"Airship": "Airship 1st visit: Pre-Evrae",
+"Omega Ruins": "Omega Ruins: Pre-Ultima Weapon",
+}
+
+
+
+
+def create_region_access_rule(world: FFXWorld, region_name: str):
+ region_level = world_battle_levels[region_name]
+ if region_level < 5:
+ return lambda state: state.has(f"Region: {region_name}", world.player)
+ else:
+ appropriate_level_regions = [other_region for other_region, other_level in world_battle_levels.items() if
+ other_region != region_name and region_level > other_level >= region_level - world.options.logic_difficulty.value]
+
+ return lambda state: state.has(f"Region: {region_name}", world.player) and any([state.can_reach_region(region_to_first_visit[other_region], world.player) for other_region in appropriate_level_regions])
+
+def create_level_rule(world: FFXWorld, level: int):
+ appropriate_level_regions = [other_region for other_region, other_level in world_battle_levels.items() if
+ level > other_level >= level - world.options.logic_difficulty.value]
+
+ return lambda state: any([state.can_reach_region(region_to_first_visit[other_region], world.player) for other_region in appropriate_level_regions])
+
+def create_ability_rule(world: FFXWorld, ability_name: str):
+ #return lambda state: state.has_any([f"{name} Ability: {ability_name}" for name in character_names], world.player)
+ #return lambda state: True; " Ability: Fire"
+ #temp = [(f"{name} Ability: {ability_name}", f"Party Member: {name}") for name in character_names]
+ #for ability, character in temp:
+ # print(world.item_name_to_id[ability])
+ # print(world.item_name_to_id[character])
+ #print(temp)
+ if world.options.sphere_grid_randomization.value == world.options.sphere_grid_randomization.option_on:
+ return lambda state: any([state.has_all([f"{name} Ability: {ability_name}", f"Party Member: {name}"], world.player) for name in character_names])
+ else:
+ return lambda state: True
+
+
+#def create_stat_rule(world: World, stat_total: int):
+# return lambda state: state.has
+def create_stat_total_rule(world: FFXWorld, num_party_members: int, stat_total: int) -> CollectionRule:
+ def has_stat_total(state: CollectionState) -> bool:
+ player_prog_items = state.prog_items[world.player]
+ totals = Counter()
+ for item, count in player_prog_items.items():
+ if item in stat_abilities:
+ character, value = item_to_stat_value[item]
+ totals[character] += value*count
+
+ return len([total for total in totals.values() if total > stat_total]) >= num_party_members
+ #for total in totals.values():
+ # if total > stat_total:
+ # return True
+ #return False
+ return has_stat_total
+
+def create_min_party_rule(world: FFXWorld, num_characters: int) -> CollectionRule:
+ return lambda state: state.has_from_list_unique([f"Party Member: {name}" for name in character_names], world.player, num_characters)
+
+def create_min_swimmers_rule(world: FFXWorld, num_characters: int) -> CollectionRule:
+ return lambda state: state.has_from_list_unique([f"Party Member: {name}" for name in ["Tidus", "Wakka", "Rikku"]], world.player, num_characters)
+
+def create_min_summon_rule(world: FFXWorld, num_aeons: int) -> CollectionRule:
+ return lambda state: state.has(f"Party Member: Yuna", world.player) and state.has_from_list_unique([f"Party Member: {name}" for name in aeon_names], world.player, num_aeons)
+
+ruleDict: dict[str, Callable[[FFXWorld], CollectionRule]] = {
+ "Sin Fin": lambda world: lambda state: create_level_rule(world, 2)(state) and create_min_party_rule (world, 3)(state) and state.has("Party Member: Wakka", world.player),
+ "Sinspawn Echuilles": lambda world: lambda state: create_level_rule(world, 2)(state) and create_min_swimmers_rule(world, 2)(state),
+ "Sinspawn Geneaux": lambda world: lambda state: create_level_rule(world, 3)(state) and create_min_party_rule (world, 3)(state),
+ "Oblitzerator": lambda world: lambda state: create_level_rule(world, 4)(state) and create_min_party_rule (world, 3)(state),
+ "Chocobo Eater": lambda world: lambda state: create_level_rule(world, 5)(state) and create_min_party_rule (world, 3)(state),
+ "Sinspawn Gui": lambda world: lambda state: create_level_rule(world, 6)(state) and create_min_party_rule (world, 3)(state),
+ "Extractor": lambda world: lambda state: create_level_rule(world, 8)(state) and create_min_swimmers_rule(world, 2)(state), # At least 2 swimmers
+ "Spherimorph": lambda world: lambda state: create_level_rule(world, 10)(state) and create_min_party_rule (world, 3)(state),
+ "Crawler": lambda world: lambda state: create_level_rule(world, 10)(state) and create_min_party_rule (world, 3)(state),
+ "Seymour/Anima": lambda world: lambda state: create_level_rule(world, 10)(state) and create_min_party_rule (world, 3)(state),
+ "Wendigo": lambda world: lambda state: create_level_rule(world, 10)(state) and create_min_party_rule (world, 3)(state),
+ "Evrae": lambda world: lambda state: create_level_rule(world, 12)(state) and create_min_party_rule (world, 3)(state),
+ "Airship Sin": lambda world: lambda state: create_level_rule(world, 16)(state) and create_min_party_rule (world, 3)(state),
+ "Overdrive Sin": lambda world: lambda state: create_level_rule(world, 16)(state) and create_min_party_rule (world, 3)(state),
+ "Penance": lambda world: lambda state: create_level_rule(world, 17)(state) and create_min_party_rule (world, 3)(state),
+ "Isaaru": lambda world: lambda state: create_level_rule(world, 12)(state) and create_min_summon_rule (world, 2)(state), # Yuna + 2 summons
+ "Evrae Altana": lambda world: lambda state: create_level_rule(world, 12)(state) and create_min_swimmers_rule(world, 3)(state), # All swimmers
+ "Seymour Natus": lambda world: lambda state: create_level_rule(world, 12)(state) and create_min_party_rule (world, 3)(state),
+ "Defender X": lambda world: lambda state: create_level_rule(world, 13)(state) and create_min_party_rule (world, 3)(state),
+ "Biran and Yenke": lambda world: lambda state: create_level_rule(world, 14)(state) and create_min_party_rule (world, 3)(state),
+ "Seymour Flux": lambda world: lambda state: create_level_rule(world, 14)(state) and create_min_party_rule (world, 3)(state),
+ "Sanctuary Keeper": lambda world: lambda state: create_level_rule(world, 14)(state) and create_min_party_rule (world, 3)(state),
+ "Spectral Keeper": lambda world: lambda state: create_level_rule(world, 15)(state) and create_min_party_rule (world, 3)(state),
+ "Yunalesca": lambda world: lambda state: create_level_rule(world, 15)(state) and create_min_party_rule (world, 3)(state),
+ "Seymour Omnis": lambda world: lambda state: create_level_rule(world, 16)(state) and create_min_party_rule (world, 3)(state),
+ "Braska's Final Aeon": lambda world: lambda state: create_level_rule(world, 16)(state) and create_min_party_rule (world, 3)(state),
+ "Ultima Weapon": lambda world: lambda state: create_level_rule(world, 17)(state) and create_min_party_rule (world, 3)(state),
+ "Omega Weapon": lambda world: lambda state: create_level_rule(world, 18)(state) and create_min_party_rule (world, 3)(state),
+ "Geosgaeno": lambda world: lambda state: create_level_rule(world, 15)(state) and create_min_swimmers_rule(world, 3)(state),
+
+ "Dark Valefor": lambda world: lambda state: create_level_rule(world, 18)(state) and create_min_party_rule (world, 3)(state) and state.has("Party Member: Yuna", world.player),
+ "Dark Ifrit": lambda world: lambda state: create_level_rule(world, 18)(state) and create_min_party_rule (world, 3)(state),
+ "Dark Ixion": lambda world: lambda state: create_level_rule(world, 18)(state) and create_min_party_rule (world, 3)(state),
+ "Dark Shiva": lambda world: lambda state: create_level_rule(world, 18)(state) and create_min_party_rule (world, 3)(state),
+ "Dark Bahamut": lambda world: lambda state: create_level_rule(world, 18)(state) and create_min_party_rule (world, 3)(state),
+ "Dark Anima": lambda world: lambda state: create_level_rule(world, 18)(state) and create_min_party_rule (world, 3)(state),
+ "Dark Yojimbo": lambda world: lambda state: create_level_rule(world, 18)(state) and create_min_party_rule (world, 3)(state),
+ "Dark Magus Sisters": lambda world: lambda state: create_level_rule(world, 18)(state) and create_min_party_rule (world, 3)(state),
+
+ "Baaj Temple": lambda world: create_region_access_rule(world, "Baaj Temple"), # lambda state: state.has("Region: Baaj Temple", world.player),
+ "Besaid": lambda world: create_region_access_rule(world, "Besaid"),
+ "Kilika": lambda world: create_region_access_rule(world, "Kilika"),
+ "Luca": lambda world: create_region_access_rule(world, "Luca"),
+ "Mi'ihen Highroad": lambda world: create_region_access_rule(world, "Mi'ihen Highroad"),
+ "Mushroom Rock Road": lambda world: create_region_access_rule(world, "Mushroom Rock Road"),
+ "Djose": lambda world: create_region_access_rule(world, "Djose"),
+ "Moonflow": lambda world: create_region_access_rule(world, "Moonflow"),
+ "Guadosalam": lambda world: create_region_access_rule(world, "Guadosalam"),
+ "Thunder Plains": lambda world: create_region_access_rule(world, "Thunder Plains"),
+ "Macalania": lambda world: create_region_access_rule(world, "Macalania"),
+ "Bikanel": lambda world: create_region_access_rule(world, "Bikanel"),
+ "Bevelle": lambda world: create_region_access_rule(world, "Bevelle"),
+ "Calm Lands": lambda world: create_region_access_rule(world, "Calm Lands"),
+ "Cavern of the Stolen Fayth": lambda world: create_region_access_rule(world, "Cavern of the Stolen Fayth"),
+ "Mt. Gagazet": lambda world: create_region_access_rule(world, "Mt. Gagazet"),
+ "Zanarkand Ruins": lambda world: create_region_access_rule(world, "Zanarkand Ruins"),
+ "Sin": lambda world: create_region_access_rule(world, "Sin"),
+ "Airship": lambda world: create_region_access_rule(world, "Airship"),
+ "Omega Ruins": lambda world: create_region_access_rule(world, "Omega Ruins"),
+}
+
+
+def set_rules(world: FFXWorld) -> None:
+ ## Remiem
+ # Valefor fight
+ add_rule(world.get_location(world.location_id_to_name[379 | TreasureOffset]), create_min_summon_rule(world, 2))
+ # Ifrit fight
+ add_rule(world.get_location(world.location_id_to_name[381 | TreasureOffset]), create_min_summon_rule(world, 2))
+ # Ixion fight
+ add_rule(world.get_location(world.location_id_to_name[383 | TreasureOffset]), create_min_summon_rule(world, 2))
+ # Shiva fight
+ add_rule(world.get_location(world.location_id_to_name[385 | TreasureOffset]), create_min_summon_rule(world, 2))
+ # Bahamut fight
+ add_rule(world.get_location(world.location_id_to_name[334 | TreasureOffset]), create_min_summon_rule(world, 2))
+ # Yojimbo fight
+ add_rule(world.get_location(world.location_id_to_name[388 | TreasureOffset]), lambda state: create_min_summon_rule(world, 2)(state) and state.has(f"Party Member: Yojimbo", world.player))
+ # Anima fight
+ add_rule(world.get_location(world.location_id_to_name[390 | TreasureOffset]), lambda state: state.has(f"Party Member: Yuna", world.player) and state.has_all([f"Party Member: {name}" for name in ["Yojimbo", "Anima"]], world.player))
+ # Magus Sisters fight
+ add_rule(world.get_location(world.location_id_to_name[392 | TreasureOffset]), lambda state: state.has(f"Party Member: Yuna", world.player) and state.has_all([f"Party Member: {name}" for name in ["Yojimbo", "Anima", "Magus Sisters"]], world.player))
+ # Send Belgemine? (Moon sigil)
+ add_rule(world.get_location(world.location_id_to_name[275 | TreasureOffset]), lambda state: state.has(f"Party Member: Yuna", world.player) and state.has_all([f"Party Member: {name}" for name in ["Yojimbo", "Anima", "Magus Sisters"]], world.player))
+
+
+ ## Dark Aeons
+ dark_aeons = [
+ ( 2, "Dark Valefor" ), # "Besaid: Dark Valefor
+ (13, "Dark Ifrit" ), # "Thunder Plains: Dark Ixion
+ (18, "Dark Ixion" ), # "Lake Macalania: Dark Shiva
+ (19, "Dark Shiva" ), # "Bikanel: Dark Ifrit
+ (31, "Dark Bahamut" ), # "Cavern of the Stolen Fayth: Dark Yojimbo
+ (34, "Dark Anima" ), # "Gagazet (Outside): Dark Anima
+ (38, "Dark Yojimbo" ), # "Zanarkand: Dark Bahamut
+ (45, "Dark Magus Sisters"), # "Mushroom Rock Road: Dark Mindy
+ (46, "Dark Magus Sisters"), # "Mushroom Rock Road: Dark Sandy
+ (47, "Dark Magus Sisters"), # "Mushroom Rock Road: Dark Cindy
+ ]
+ for location_id, aeon in dark_aeons:
+ add_rule(world.get_location(world.location_id_to_name[location_id | BossOffset]),
+ ruleDict[aeon](world))
+
+ ## Aeons
+ # Anima
+ add_rule(world.get_location(world.location_id_to_name[13 | PartyMemberOffset]), lambda state: (
+ state.can_reach_location(world.location_id_to_name[ 15 | TreasureOffset], world.player), # Besaid
+ state.can_reach_location(world.location_id_to_name[ 19 | TreasureOffset], world.player), # Kilika
+ state.can_reach_location(world.location_id_to_name[484 | TreasureOffset], world.player), # Djose
+ state.can_reach_location(world.location_id_to_name[485 | TreasureOffset], world.player), # Macalania
+ state.can_reach_location(world.location_id_to_name[217 | TreasureOffset], world.player), # Bevelle
+ state.can_reach_location(world.location_id_to_name[209 | TreasureOffset], world.player), # Zanarkand
+ ))
+ # Magus Sisters
+ add_rule(world.get_location(world.location_id_to_name[15 | PartyMemberOffset]), lambda state: state.has_all(["Flower Scepter", "Blossom Crown"], world.player))
+
+
+
+
+
+ celestial_weapon_locations = [
+ 5,
+ 93,
+ #99, # Requires Rusty Sword
+ 113,
+ 114,
+ 188,
+ #214, # Airship password location
+ ]
+ for location_id in celestial_weapon_locations:
+ add_rule(world.get_location(world.location_id_to_name[location_id | TreasureOffset]),
+ lambda state: state.has("Celestial Mirror", world.player))
+
+ # Masamune
+ add_rule(world.get_location(world.location_id_to_name[99 | TreasureOffset]),
+ lambda state: state.has_all(["Celestial Mirror", "Rusty Sword"], world.player))
+
+ # Celestial Mirror
+ add_rule(world.get_location(world.location_id_to_name[111 | TreasureOffset]),
+ lambda state: state.has("Cloudy Mirror", world.player))
+
+ # Mercury Sigil
+ add_rule(world.get_location(world.location_id_to_name[279 | TreasureOffset]),
+ lambda state: state.can_reach_region("Airship 1st visit: Post-Evrae", world.player))
+
+ celestial_upgrades = [
+ (38, 0x25, "Sun"),
+ (40, 0x24, "Moon"),
+ (42, 0x1e, "Mars"),
+ (44, 0x38, "Saturn"),
+ (46, 0x1a, "Jupiter"),
+ (48, 0x03, "Venus"),
+ (50, 0x3d, "Mercury"),
+ ]
+ for crest_id, weapon_id, celestial in celestial_upgrades:
+ add_rule(world.get_location(world.location_id_to_name[crest_id | OtherOffset]),
+ lambda state, weapon_id=weapon_id, celestial=celestial: state.has_all(["Celestial Mirror",
+ world.item_id_to_name[weapon_id | equipItemOffset],
+ f"{celestial} Crest",
+ ], world.player))
+ add_rule(world.get_location(world.location_id_to_name[crest_id+1 | OtherOffset]),
+ lambda state, weapon_id=weapon_id, celestial=celestial: state.has_all(["Celestial Mirror",
+ world.item_id_to_name[weapon_id | equipItemOffset],
+ f"{celestial} Crest",
+ f"{celestial} Sigil",
+ ], world.player))
+
+
+ # Complete Al Bhed Primers
+ al_bhed_primers = [item.itemName for item in key_items[0x4:0x1D+1]]
+ add_rule(world.get_location(world.location_id_to_name[405 | TreasureOffset]),
+ lambda state: state.has_all(al_bhed_primers, world.player))
+
+ # TODO: Disabled for now due to multiple bugs related to this location (Ship softlocks + possible Macalania softlock)
+ # Clasko S.S. Liki second visit (Talk to Clasko before Crawler and make sure to have him become a Chocobo Breeder)
+ #add_rule(world.get_location(world.location_id_to_name[336 | TreasureOffset]),
+ # lambda state: state.can_reach_region("Lake Macalania 1st visit: Pre-Crawler", world.player))
\ No newline at end of file
diff --git a/worlds/ffx/ut.py b/worlds/ffx/ut.py
new file mode 100644
index 000000000000..ad191617173b
--- /dev/null
+++ b/worlds/ffx/ut.py
@@ -0,0 +1,1681 @@
+import typing
+
+if typing.TYPE_CHECKING:
+ from .__init__ import FFXWorld
+else:
+ FFXWorld = object
+
+def setup_options_from_slot_data(world: FFXWorld) -> None:
+ if hasattr(world.multiworld, "re_gen_passthrough"):
+ if "Final Fantasy X" in world.multiworld.re_gen_passthrough:
+ world.using_ut = True
+ world.passthrough = world.multiworld.re_gen_passthrough["Final Fantasy X"]
+
+ world.options.goal_requirement.value = world.passthrough["goal_requirement"]
+ world.options.required_party_members.value = world.passthrough["required_party_members"]
+ world.options.sphere_grid_randomization.value = world.passthrough["sphere_grid_randomization"]
+ world.options.super_bosses.value = world.passthrough["super_bosses"]
+ world.options.mini_games.value = world.passthrough["mini_games"]
+ world.options.logic_difficulty.value = world.passthrough["logic_difficulty"]
+ world.options.recruit_sanity.value = world.passthrough["recruit_sanity"]
+
+ else:
+ world.using_ut = False
+ else:
+ world.using_ut = False
+
+
+poptracker_name_mapping = {
+ "Outside/200 gil (Chest)": 4096,
+ "Outside/Potion x2 (Chest)": 4097,
+ "Inside/Withered Bouquet": 4098,
+ "Inside/Flint": 4099,
+ "Trashed/Treasure 4 (Potentially Trashed Chest)": 4100,
+ "Lulu/Onion Knight (Chest)": 4101,
+ "Inside/Ether (Chest)": 4102,
+ "Inside/Hi-Potion x1 (Chest)": 4103,
+ "Trashed/Treasure 8 (Potentially Trashed Chest)": 4104,
+ "Beach/Antidote x2 (Chest)": 4105,
+ "Village/Phoenix Down x1 (Chest)": 4109,
+ "Behind Shop/Hi-Potion x1 (Chest)": 4110,
+ "Destruction Sphere/Rod of Wisdom": 4111,
+ "S.S. Liki/Remedy x1 (Chest)": 4112,
+ "Village/Potion x3 (Chest)": 4113,
+ "Village/Ether x1 (Chest)": 4114,
+ "Destruction Sphere/Red Armlet": 4115,
+ "Woods/Mana Sphere x2 (Chest)": 4123,
+ "Woods/Scout (Chest)": 4124,
+ "Woods/Luck Sphere x1 (Chest)": 4125,
+ "Woods/NulBlaze Shield (Woman NPC after defeating Lord Ochu)": 4126,
+ "S.S. Winno/Hi-Potion x1 (Chest)": 4127,
+ "Dock/Phoenix Down x2 (Chest)": 4128,
+ "Dock/600 Gil (Chest)": 4129,
+ "Dock/Tidal Spear (Chest)": 4130,
+ "Dock/HP Sphere x1 (Chest)": 4131,
+ "Stadium/Hi-Potion x2 (Chest)": 4132,
+ "City/1000 Gil (Chest)": 4133,
+ "South/Ice Brand (Chest)": 4134,
+ "North/Fortune Sphere (Chest)": 4135,
+ "North/Thunder Blade (Chest)": 4136,
+ "North/Scout (Chest)": 4137,
+ "North/Heat Lance (Chest)": 4138,
+ "North/Hi-Potion x2 (Chest)": 4139,
+ "South/Remedy x1 (Chest)": 4140,
+ "Central/2000 gil (Chest)": 4141,
+ "Central/Eye Drops x3 (Chest)": 4142,
+ "Aftermath/Soft x4 (Chest)": 4143,
+ "Valley/1000 gil (Chest)": 4144,
+ "Valley/Hi-Potion x1 (Chest)": 4187,
+ "Valley/Remedy x1 (Chest)": 4146,
+ "Ridge/Serene Bracer (Chest)": 4147,
+ "Ridge/Mega-Potion x1 (Chest)": 4148,
+ "Highroad/Phoenix Down x2 (Chest)": 4150,
+ "Highroad/Bright Bangle (Chest)": 4151,
+ "Precipice/Serene Armlet (Chest)": 4153,
+ "Temple/Ability Sphere x4 (Chest)": 4154,
+ "Temple/4000 gil (Chest)": 4155,
+ "Temple/Switch Hitter (Chest)": 4156,
+ "Temple/Ether x1 (Chest)": 4157,
+ "Temple/Remedy x1 (Chest)": 4158,
+ "Temple/Mega Phoenix x1 (Chest)": 4159,
+ "Guadosalam/3000 gil (Chest)": 4160,
+ "Guadosalam/Mega-Potion x1 (Chest)": 4161,
+ "Guadosalam/Elixir x1 (Chest)": 4162,
+ "Guadosalam/Hi-Potion x2 (Chest)": 4163,
+ "Woods/2000 gil (Chest)": 4164,
+ "Woods/Sleepy Cait Sith (Chest)": 4165,
+ "Woods/Phoenix Down x3 (Chest)": 4166,
+ "Woods/MP Sphere x1 (Butterfly Minigame Reward before Spherimorph)": 4167,
+ "Woods/Ether x1 (Butterfly Minigame Reward before Spherimorph)": 4168,
+ "Woods/Remedy x1 (Chest)": 4169,
+ "Woods/Lucid Ring (Chest)": 4171,
+ "Agency/4000 gil (Chest)": 4172,
+ "Road/Lv. 1 Key Sphere x1 (Chest)": 4173,
+ "Road/Mega-Potion x1 (Chest)": 4174,
+ "Bottom/Avenger (Chest)": 4175,
+ "Bottom/Lv. 2 Key Sphere (Chest)": 4176,
+ "Temple/5000 gil (Chest)": 4179,
+ "Temple/X-Potion x2 (Chest)": 4180,
+ "Temple/Shell Targe (Tromell)": 4181,
+ "Temple/Phoenix Down x3 (Chest)": 4182,
+ "Temple/Remedy x2 (Chest)": 4183,
+ "Valley/Phoenix Down x1 (Chest)": 4186,
+ "Valley/Antidote x2 (Chest)": 4188,
+ "Wakka/World Champion": 4189,
+ "Miihen Highroad/Scout (Donate 100 gil to Operation)": 4190,
+ "Miihen Highroad/Ice Lance (Donate 1000 gil to Operation)": 4191,
+ "Miihen Highroad/Moon Ring (Donate 10000 gil to Operation)": 4192,
+ "Agency/Mega-Potion (NPC)": 4193,
+ "Aftermath/Hi-Potion x1 (Chest)": 4194,
+ "Auron/Masamune": 4195,
+ "Underwater/Avenger (Chest)": 4196,
+ "Underwater/Rematch (Chest)": 4197,
+ "Cloister/Knight Lance (Chest)": 4198,
+ "Via Purifico/Elixir x1 (Chest)": 4200,
+ "Via Purifico/Wht Magic Sphere x1 (Chest)": 4201,
+ "Via Purifico/Skill Sphere x1 (Chest)": 4202,
+ "Via Purifico/10000 gil (Chest)": 4203,
+ "Via Purifico/Lucid Ring (Chest)": 4204,
+ "Via Purifico/Blk Magic Sphere (Chest)": 4205,
+ "Via Purifico/Mega-Potion x1 (Chest)": 4206,
+ "Celestial/Celestial Mirror": 4207,
+ "Yuna/Nirvana": 4209,
+ "Tidus/Caladbolg": 4210,
+ "Plains/10000 gil (Chest)": 4211,
+ "Plains/5000 gil (Chest)": 4212,
+ "Plains/Lv. 2 Key Sphere x1 (Chest)": 4213,
+ "Celestial/Rusty Sword": 4214,
+ "Cavern of the Stolen Fayth/Megalixir x1 (Chest)": 4216,
+ "Cavern of the Stolen Fayth/Lv. 2 Key Sphere x1 (Chest)": 4217,
+ "Cavern of the Stolen Fayth/Fortune Sphere x1 (Chest)": 4218,
+ "Cavern of the Stolen Fayth/Mega-Potion x1 (Chest)": 4219,
+ "Cavern of the Stolen Fayth/Flexible Arm (Chest)": 4220,
+ "Cavern of the Stolen Fayth/MP Sphere x1 (Chest)": 4221,
+ "Cavern of the Stolen Fayth/X-Potion x2 (Chest)": 4222,
+ "Outside/20000 gil (Chest)": 4224,
+ "Outside/Mega-Potion x2 (Chest)": 4225,
+ "Outside/Defending Bracer (Chest)": 4226,
+ "Outside/Lv. 4 Key Sphere x1 (Chest)": 4227,
+ "Outside/HP Sphere x1 (Chest)": 4228,
+ "Inside/Pep Talk (Chest)": 4231,
+ "Inside/Lv. 1 Key Sphere x1 (Chest)": 4232,
+ "Inside/Fortune Sphere x1 (Chest)": 4233,
+ "Inside/Return Sphere x1 (Chest)": 4234,
+ "Inside/Recovery Ring (Chest)": 4235,
+ "Overpass/Fortune Sphere x1 (Chest)": 4241,
+ "Overpass/Spiritual Targe (Chest)": 4242,
+ "Dome/10000 gil (Chest)": 4243,
+ "Dome/Friend Sphere x1 (Chest)": 4244,
+ "Dome/Lv. 3 Key Sphere x1 (Chest)": 4245,
+ "Dome/Luck Sphere x1 (Chest)": 4246,
+ "Omega Ruins/Fortune Sphere x1 (Chest)": 4248,
+ "Omega Ruins/Defending Bracer (Chest)": 4249,
+ "Omega Ruins/Turnover (Chest)": 4250,
+ "Omega Ruins/Lv. 3 Key Sphere x2 (Chest)": 4251,
+ "Omega Ruins/Defending Armlet (Chest)": 4252,
+ "Omega Ruins/Friend Sphere x2 (Chest)": 4253,
+ "Omega Ruins/Lv. 4 Key Sphere x1 (Chest)": 4254,
+ "Omega Ruins/Phantom Ring (Chest)": 4255,
+ "Omega Ruins/Cactuar Wizard (Chest)": 4256,
+ "Omega Ruins/Warmonger (Chest)": 4257,
+ "Omega Ruins/Teleport Sphere x2 (Chest)": 4258,
+ "Sea of Sorrow/Elixir x1 (Chest)": 4259,
+ "Sea of Sorrow/Wizard Lance (Chest)": 4260,
+ "Sea of Sorrow/Lv. 3 Key Sphere x1 (Chest)": 4261,
+ "Sea of Sorrow/Phantom Ring (Chest)": 4262,
+ "Sea of Sorrow/Special Sphere x1 (Chest)": 4263,
+ "City of Dying Dreams/Lv. 4 Key Sphere x1 (Chest)": 4264,
+ "City of Dying Dreams/Four-on-One (Chest)": 4265,
+ "City of Dying Dreams/Defending Bracer (Chest)": 4266,
+ "City of Dying Dreams/20000 gil (Chest)": 4267,
+ "City of Dying Dreams/HP Sphere x1 (Chest)": 4268,
+ "City of Dying Dreams/Defense Sphere x1 (Chest)": 4269,
+ "City of Dying Dreams/Megalixir x1 (Chest)": 4270,
+ "City of Dying Dreams/Laevatein (Chest)": 4271,
+ "Celestial/Cloudy Mirror (Chocobo Race)": 4272,
+ "South/Phoenix Down x2 (Chest)": 4294,
+ "South/Hi-Potion x2 (Chest)": 4275,
+ "South/5000 gil (Chest)": 4295,
+ "South/Water Ball (Chest)": 4277,
+ "North/X-Potion x1 (Chest)": 4278,
+ "North/Ether x1 (Chest)": 4296,
+ "North/Remedy x1 (Chest)": 4280,
+ "North/2000 gil (Chest)": 4281,
+ "South/Echo Ring (Win Aeon Fight)": 4282,
+ "Remiem Temple/Power Spheres x30 (NPC)": 4283,
+ "Kimahri/Spirit Lance": 4284,
+ "Lightning Dodging/X-Potion x2 (5x Dodges)": 4285,
+ "Lightning Dodging/Mega-Potion x2 (10x Dodges)": 4286,
+ "Lightning Dodging/MP Sphere x2 (20x Dodges)": 4287,
+ "Lightning Dodging/Strength Sphere x3 (50x Dodges)": 4288,
+ "Lightning Dodging/HP Sphere x3 (100x Dodges)": 4289,
+ "Lightning Dodging/Megalixir x4 (150x Dodges)": 4290,
+ "South/X-Potion x1 (Chest)": 4293,
+ "North/Antidote x4 (Chest)": 4297,
+ "North/Mega-Potion x1 (Chest)": 4298,
+ "Temple/Megalixir (Chest)": 4300,
+ "Temple/Mega Phoenix x4 (Chest)": 4301,
+ "Dock/Magic Sphere x1 (Chest)": 4302,
+ "Exit the Village (Event)/Brotherhood": 24576,
+ "Tidus/Brotherhood": 4304,
+ "Destruction Sphere/Magistral Rod": 4305,
+ "Al Bhed Potion x8/Chest 1": 4306,
+ "Al Bhed Potion x8/Chest 2": 4307,
+ "Central/Al Bhed Potion x8": 4308,
+ "Inside/X-Potion x1 (Chest)": 4309,
+ "Rikku/Godhand": 4310,
+ "Behind Shop/400 gil (Chest)": 4311,
+ "Behind Shop/Potion x2 (Chest)": 4312,
+ "Destruction Sphere/HP Sphere": 4313,
+ "Farplane/Lightning Marble x8 (Chest)": 4314,
+ "Outside/Hi-Potion x1 (Chest)": 4315,
+ "Blitzball/Blitzball Reward 1": 4316,
+ "Blitzball/Blitzball Reward 2": 4317,
+ "Blitzball/Blitzball Reward 3": 4318,
+ "Blitzball/Blitzball Reward 4": 4319,
+ "Blitzball/Blitzball Reward 5": 4320,
+ "Blitzball/Blitzball Reward 6": 4321,
+ "Blitzball/Blitzball Reward 7": 4322,
+ "Blitzball/Blitzball Reward 8": 4323,
+ "Blitzball/Blitzball Reward 9": 4324,
+ "Blitzball/Blitzball Reward 10": 4325,
+ "Blitzball/Blitzball Reward 11": 4326,
+ "Blitzball/Blitzball Reward 12": 4327,
+ "Blitzball/Blitzball Reward 13": 4328,
+ "Blitzball/Blitzball Reward 14": 4329,
+ "Blitzball/Blitzball Reward 15": 4330,
+ "Blitzball/Blitzball Reward 16": 4331,
+ "Blitzball/Blitzball Reward 17": 4332,
+ "Blitzball/Blitzball Reward 18": 4333,
+ "Blitzball/Blitzball Reward 19": 4334,
+ "Blitzball/Blitzball Reward 20": 4335,
+ "Blitzball/Blitzball Reward 21": 4336,
+ "Blitzball/Blitzball Reward 22": 4337,
+ "Blitzball/Blitzball Reward 23": 4338,
+ "Blitzball/Blitzball Reward 24": 4339,
+ "Wakka/Jupiter Sigil (Blitzball League)": 4340,
+ "Blitzball/Blitzball Reward 25": 4341,
+ "Blitzball/Blitzball Reward 26": 4342,
+ "Blitzball/Blitzball Reward 27": 4343,
+ "Blitzball/Blitzball Reward 28": 4344,
+ "Blitzball/Blitzball Reward 29": 4345,
+ "Blitzball/Blitzball Reward 30": 4346,
+ "Blitzball/Blitzball Reward 31": 4347,
+ "Blitzball/Blitzball Reward 32": 4348,
+ "Blitzball/Blitzball Reward 33": 4349,
+ "Blitzball/Blitzball Reward 34": 4350,
+ "Blitzball/Blitzball Reward 35": 4351,
+ "Blitzball/Blitzball Reward 36": 4352,
+ "Blitzball/Blitzball Reward 37": 4353,
+ "Blitzball/Blitzball Reward 38": 4354,
+ "Blitzball/Blitzball Reward 39": 4355,
+ "Blitzball/Blitzball Reward 40": 4356,
+ "Blitzball/Blitzball Reward 41": 4357,
+ "Blitzball/Blitzball Reward 42": 4358,
+ "Blitzball/Blitzball Reward 43": 4359,
+ "Blitzball/Blitzball Reward 44": 4360,
+ "Blitzball/Blitzball Reward 45": 4361,
+ "Blitzball/Blitzball Reward 46": 4362,
+ "Tidus/Sun Crest (Chest)": 4363,
+ "Yuna/Moon Crest (Chest)": 4364,
+ "Auron/Mars Crest (Chest)": 4365,
+ "Kimahri/Saturn Crest (Chest)": 4366,
+ "Wakka/Jupiter Crest (Locker)": 4367,
+ "Lulu/Venus Crest (Chest)": 4368,
+ "Rikku/Mercury Crest (Chest)": 4369,
+ "Tidus/Sun Sigil (Chocobo Catcher 0:00)": 4370,
+ "Yuna/Moon Sigil (Send Belgemine)": 4371,
+ "Auron/Mars Sigil (10x Creations)": 4372,
+ "Kimahri/Saturn Sigil (Butterfly Hunt)": 4373,
+ "Lulu/Venus Sigil (Lightning Dodging)": 4374,
+ "Rikku/Mercury Sigil (Cactuar Village)": 4375,
+ "Lake/Megalixir x2 (Butterfly Game after defeating Spherimorph)": 4376,
+ "Lake/Elixir x2 (Butterfly Game after defeating Spherimorph)": 4377,
+ "Aurochs/Hi-Potion x1 (Datto NPC)": 4378,
+ "Aurochs/Potion x3 (Jassu NPC)": 4379,
+ "Aurochs/Potion x2 (Botta NPC)": 4380,
+ "Aurochs/200 gil (Keepa NPC)": 4381,
+ "Dock/Remedy x1 (Kid on Dock Bridge NPC)": 4382,
+ "Dock/Seekers Ring (Priest NPC)": 4383,
+ "Dock/Phoenix Down x3 (Woman NPC)": 4384,
+ "Dock/400 gil (Shirtless Man NPC)": 4385,
+ "Dock/Ether (Green Shirt NPC)": 4386,
+ "Luzu (NPC)/Antidote x4 (Before Ochu)": 4387,
+ "Luzu (NPC)/Elixir x1 (After Ochu)": 4388,
+ "Woods/Remedy x1 (Leader NPC)": 4389,
+ "Woods/High Potion x1 (Guard NPC)": 4391,
+ "Al Bhed Ship/Potion x 3 (NPC)": 4392,
+ "Highroad/Variable Steel (NPC)": 4393,
+ "Highroad/Soft Ring (NPC)": 4394,
+ "Highroad/Hi-Potion x1 (NPC)": 4395,
+ "Highroad/Ether x1 (NPC)": 4396,
+ "Highroad/Mega-Potion x1 (NPC)": 4397,
+ "Pilgrimage Road/Halberd (NPC)": 4398,
+ "Pilgrimage Road/Potion x10 (NPC)": 4399,
+ "Pilgrimage Road/Hi-Potion x2 (NPC)": 4400,
+ "Road/400 gil (Al Bhed Soldier NPC)": 4401,
+ "Temple/Elixir x1 (Man Sitting NPC)": 4402,
+ "Temple/Ether x1 (Man Sitting NPC)": 4403,
+ "Temple/Hi-Potion x2 (NPC)": 4404,
+ "South/Hunters Spear (Blue Shirt NPC)": 4405,
+ "South/Antidote x2 (Red Skirt NPC)": 4406,
+ "South/Hi-Potion (Yellow Shirt NPC)": 4407,
+ "South/Soft x3 (Boy NPC)": 4408,
+ "South/Red Ring (Crusader NPC)": 4409,
+ "Central/Ether x1 (NPC)": 4410,
+ "Central/Hi-Potion x1 (NPC)": 4411,
+ "Central/600 gil (Yellow Crusader NPC)": 4412,
+ "Central/Lv. 1 Key Sphere x1 (Purple Crusader NPC)": 4413,
+ "Central/Antidote x4 (Woman in Yellow NPC)": 4414,
+ "Entrance/Tough Bangle (Gray Helmet NPC)": 4415,
+ "Entrance/Phoenix Down x2 (Blue Shirt Crusader NPC)": 4416,
+ "Entrance/Remedy x1 (Near Grey Helmet NPC)": 4417,
+ "Entrance/Hi-Potion x1 (Woman in Blue NPC)": 4418,
+ "Entrance/Ether x1 (Purple Helmet NPC)": 4419,
+ "Valley/Hi-Potion x1 (Woman NPC near Save Point)": 4420,
+ "Valley/Potion x10 (NPC near chest)": 4421,
+ "Precipice/400 gil (NPC near elevator)": 4422,
+ "Precipice/X-Potion x1 (NPC near lift)": 4423,
+ "Precipice/Mega-Potion x1 (NPC)": 4424,
+ "Omega Ruins/Warp Sphere x99 (Chest)": 4425,
+ "Omega Ruins/Teleport Sphere x1 (Chest)": 4426,
+ "Omega Ruins/Friend Sphere x1 (Chest)": 4427,
+ "Omega Ruins/Magic Sphere x1 (Chest)": 4428,
+ "Trashed/Treasure 333 (Old Entry)": 4429,
+ "Belgemine/Bahamut Fight (Flower Scepter)": 4430,
+ "S.S. Liki/Friend Sphere x1 (Clasko NPC)": 4432,
+ "Chocobo/Wobbly Chocobo Minigame Reward": 4433,
+ "Chocobo/Dodger Chocobo Minigame Reward": 4434,
+ "Chocobo/Hyper Dodger Chocobo Minigame Reward": 4435,
+ "Chocobo/Catcher Chocobo Minigame Reward": 4436,
+ "Agency/Yellow Shield (Ground Item)": 4441,
+ "East/Remedy x4 (Chest)": 4442,
+ "East/Ether x2 (Chest)": 4443,
+ "East/Hi-Potion x4 (Chest)": 4444,
+ "Central/Mega-Potion x2 (Chest)": 4445,
+ "Central/X-Potion x2 (Chest)": 4446,
+ "Central/Hi-Potion x4 (Chest)": 4447,
+ "Central/Elixir x1 (Chest)": 4448,
+ "Central/10000 gil (Chest)": 4449,
+ "Central/Lv. 2 Key Sphere x1 (Chest)": 4450,
+ "West/Hi-Potion x8 (Chest)": 4451,
+ "West/Mega-Potion x3 (Chest)": 4452,
+ "West/X-Potion x2 (Chest)": 4453,
+ "West/Megalixir x3 (Chest)": 4454,
+ "West/Teleport Sphere x2 (Chest)": 4455,
+ "Home/Al Bhed Potion x6 (Chest)": 4456,
+ "Home/Al Bhed Potion x4 (Chest)": 4457,
+ "Area Completion/Lv. 2 Key Sphere x1": 4458,
+ "Area Completion/Lv. 4 Key Sphere x4": 4459,
+ "Area Completion/10000 gil": 4460,
+ "S.S. Winno/Ace Wizard (Seagulls)": 4461,
+ "South/Seekers Ring (Lose Aeon Fight)": 4462,
+ "Home/Hi-Potion x2 (NPC on Ground)": 4463,
+ "Mushroom Rock Road/Victorious": 4464,
+ "Besaid Ruins/Murasame": 4465,
+ "Remiem Temple/Speed Sphere x30 (Lose Aeon Fight)": 4466,
+ "Remiem Temple/Aeons Soul": 4467,
+ "Moonflow/Dragon Scale x2 (Win Aeon Fight)": 4468,
+ "Moonflow/Smoke Bomb x6 (Lose Aeon Fight)": 4469,
+ "Airship/Al Bhed Potion x4 (NPC)": 4471,
+ "South/Lv. 1 Key Sphere x3 (Shelinda Chest)": 4472,
+ "South/Lv. 1 Key Sphere x3 (Benke and Biran Chest)": 4473,
+ "South/Magic Def Sphere x1 (Chest)": 4474,
+ "Belgemine/Valefor Fight": 4475,
+ "Belgemine/Valefor Post First Fight Reward (Remiem Tower)": 4476,
+ "Belgemine/Ifrit Fight": 4477,
+ "Belgemine/Ifrit Post First Fight Reward (Remiem Tower)": 4478,
+ "Belgemine/Ixion Fight": 4479,
+ "Belgemine/Ixion Post First Fight Reward (Remiem Tower)": 4480,
+ "Belgemine/Shiva Fight": 4481,
+ "Belgemine/Shiva Post First Fight Reward (Remiem Tower)": 4482,
+ "Belgemine/Bahamut Post First Fight Reward (Remiem Tower)": 4483,
+ "Belgemine/Yojimbo Fight": 4484,
+ "Belgemine/Yojimbo Post First Fight Reward (Remiem Tower)": 4485,
+ "Belgemine/Anima Fight": 4486,
+ "Belgemine/Anima Post First Fight Reward (Remiem Tower)": 4487,
+ "Belgemine/Magus Sisters Fight": 4488,
+ "Belgemine/Magus Sisters Post First Fight Reward (Remiem Tower)": 4489,
+ "Lake/Teleport Sphere x1 (Butterfly Game after Airship)": 4490,
+ "Home/Skill Sphere x1 (Al Bhed Quiz Chest)": 4491,
+ "Home/Special Sphere x1 (Al Bhed Password Chest)": 4492,
+ "Home/Friend Sphere x1 (Al Bhed Vocabulary Chest)": 4493,
+ "Home/Elixir x1 (Al Bhed Vocabulary Chest)": 4494,
+ "Trashed/Treasure 399 (Trashed)": 4495,
+ "Trashed/Treasure 400 (Trashed)": 4496,
+ "Trashed/Treasure 401 (Trashed)": 4497,
+ "Trashed/Treasure 402 (Trashed)": 4498,
+ "Trashed/Treasure 403 (Trashed)": 4499,
+ "Trashed/Treasure 404 (Trashed)": 4500,
+ "Al Bhed Primers/Complete Al Bhed Primers": 4501,
+ "Besaid/Wht Magic Sphere x1 (Aeon Room)": 4502,
+ "Besaid/Elixir x1 (Aeon Room)": 4503,
+ "Besaid/Hi-Potion x1 (Aeon Room)": 4504,
+ "Besaid/Potion x2 (Aeon Room)": 4505,
+ "Chocobo/Elixir x1 (1 Chest)": 4513,
+ "Chocobo/Megalixir x1 (2 Chests)": 4514,
+ "Chocobo/Three Stars x60 (3 Chests)": 4515,
+ "Chocobo/Pendulum x30 (4 Chests)": 4516,
+ "Chocobo/Wings to Discovery x30 (5 Chests)": 4517,
+ "Agency/Lv. 1 Key Sphere x1 (NPC)": 4519,
+ "Monster Arena/Stamina Tonic x99 (NPC)": 4520,
+ "Monster Arena/Poison Fang x99 (NPC)": 4521,
+ "Monster Arena/Soul Spring x99 (NPC)": 4522,
+ "Monster Arena/Candle of Life x99 (NPC)": 4523,
+ "Monster Arena/Petrify Grenade x99 (NPC)": 4524,
+ "Monster Arena/Chocobo Wing x99 (NPC)": 4525,
+ "Monster Arena/Shining Gem x60 (NPC)": 4526,
+ "Monster Arena/Shadow Gem x99 (NPC)": 4527,
+ "Monster Arena/Farplane Wind x60 (NPC)": 4528,
+ "Monster Arena/Silver Hourglass x40 (NPC)": 4529,
+ "Area Conquest/Blossom Crown": 4530,
+ "Monster Arena/Lunar Curtain x99 (NPC)": 4531,
+ "Monster Arena/Designer Wallet x60 (NPC)": 4532,
+ "Monster Arena/Chocobo Feather x99 (NPC)": 4533,
+ "Monster Arena/Stamina Spring x99 (NPC)": 4534,
+ "Monster Arena/Mega Phoenix x99 (NPC)": 4535,
+ "Monster Arena/Mana Tonic x60 (NPC)": 4536,
+ "Monster Arena/Mana Spring x99 (NPC)": 4537,
+ "Monster Arena/Stamina Tablet x60 (NPC)": 4538,
+ "Monster Arena/Twin Stars x60 (NPC)": 4539,
+ "Monster Arena/Star Curtain x99 (NPC)": 4540,
+ "Monster Arena/Gold Hourglass x99 (NPC)": 4541,
+ "Monster Arena/Purifying Salt x99 (NPC)": 4542,
+ "Monster Arena/Healing Spring x99 (NPC)": 4543,
+ "Monster Arena/Turbo Ether x60 (NPC)": 4544,
+ "Monster Arena/Light Curtain x99 (NPC)": 4545,
+ "Monster Arena/Mana Tablet x60 (NPC)": 4546,
+ "Monster Arena/Three Stars x60 (NPC)": 4547,
+ "Monster Arena/Supreme Gem x60 (NPC)": 4548,
+ "Monster Arena/Door to Tomorrow x99 (NPC)": 4549,
+ "Monster Arena/Gamblers Spirit x99 (NPC)": 4550,
+ "Monster Arena/Winning Formula x99 (NPC)": 4551,
+ "Monster Arena/Dark Matter x99 (NPC)": 4552,
+ "Monster Arena/Megalixir x30 (NPC)": 4553,
+ "Monster Arena/Master Sphere x10 (NPC)": 4554,
+ "Exit the Village (Event)/Map": 4555,
+ "Lake/Magic Def Sphere x1 (Aeon Room)": 4556,
+ "Lake/Accuracy Sphere x1 (Aeon Room)": 4557,
+ "Lake/Magic Sphere x1 (Aeon Room)": 4558,
+ "Temple/Agility Sphere x1 (Aeon Room)": 4559,
+ "Djose/Magic Def Sphere x1 (Aeon Room)": 4560,
+ "Djose/Luck Sphere x1 (Aeon Room)": 4561,
+ "Calm Lands/Defense Sphere x1 (Remiem Temple Aeon Room)": 4562,
+ "Besaid/Evasion Sphere x1 (Aeon Room)": 4563,
+ "Calm Lands/Strength Sphere x1 (Yojimbo Aeon Room)": 4564,
+ "Bikanel/Shadow Gem x2 (Robeya Minigame Chest)": 4565,
+ "Bikanel/Shining Gem x1 (Robeya Minigame Chest)": 4566,
+ "Bikanel/Blessed Gem x1 (Robeya Minigame Chest)": 4567,
+ "Bikanel/Potion x1 (Cactuar Sidequest Prize)": 4568,
+ "Bikanel/Elixir x1 (Cactuar Sidequest Prize)": 4569,
+ "Bikanel/Megalixir x1 (Cactuar Sidequest Prize)": 4570,
+ "Bikanel/Friend Sphere x1 (Cactuar Sidequest Prize)": 4571,
+ "Kilika/Agility Sphere x1 (Aeon Room)": 4572,
+ "Kilika/Defense Sphere x1 (Aeon Room)": 4573,
+ "Kilika/Luck Sphere x1 (Aeon Room)": 4574,
+ "Kilika/Accuracy Sphere x1 (Aeon Room)": 4575,
+ "Besaid/Dragoon Lance": 4576,
+ "Miihen Ruins/Sonar (X35": 4577,
+ "Battle Site/Phantom Bangle": 4578,
+ "Sanubia Sands/Ascalon": 4579,
+ "Destruction Sphere/Magic Sphere": 4580,
+ "Destruction Sphere/Luck Sphere": 4581,
+ "Inside Sin/Prism Ball (Point of No Return)": 4582,
+ "Inside Sin/Stillblade (Point of No Return)": 4583,
+ "Inside Sin/Skill Sphere x1 (Point of No Return)": 4584,
+ "Inside Sin/Mages Staff (Point of No Return)": 4585,
+ "Inside Sin/Knight Lance (Point of No Return)": 4586,
+ "Inside Sin/Wht Magic Sphere x1 (Point of No Return)": 4587,
+ "Inside Sin/Infinity (Point of No Return)": 4588,
+ "Inside Sin/Wicked Cait Sith (Point of No Return)": 4589,
+ "Inside Sin/Attribute Sphere x1 (Point of No Return)": 4590,
+ "Inside Sin/Hrunting (Point of No Return)": 4591,
+ "Monster Arena/Mark of Conquest": 4592,
+ "Stadium/Story Win vs. Luca Goers Reward": 4593,
+ "Inside/Klikk": 8192,
+ "Al Bhed Ship/Tros": 8193,
+ "Super Bosses/Dark Valefor": 8194,
+ "S.S. Liki/Sin Fin": 8195,
+ "S.S. Liki/Sinspawn Echuilles": 8196,
+ "Woods/Lord Ochu": 8197,
+ "Woods/Sinspawn Geneaux": 8198,
+ "Dock/Oblitzerator": 8199,
+ "Agency/Chocobo Eater": 8200,
+ "Sinspawn Gui/First Fight": 8201,
+ "Sinspawn Gui/Second Fight": 8202,
+ "South/Extractor": 8204,
+ "Super Bosses/Dark Ixion": 8205,
+ "Woods/Spherimorph": 8206,
+ "Agency/Crawler": 8207,
+ "Temple/Seymour": 8208,
+ "Agency/Wendigo": 8209,
+ "Super Bosses/Dark Shiva": 8210,
+ "Super Bosses/Dark Ifrit": 8211,
+ "Airship/Evrae": 8212,
+ "Airship/Sin Left Fin": 8213,
+ "Airship/Sin Right Fin": 8214,
+ "Airship/Sinspawn Genais and Core": 8215,
+ "Airship/Overdrive Sin": 8216,
+ "Super Bosses/Penance": 8217,
+ "Via Purifico/Isaaru": 8218,
+ "Underwater/Evrae Altana": 8219,
+ "Highbridge/Seymour Natus": 8220,
+ "Plains/Defender X": 8221,
+ "Monster Arena/Nemesis": 8222,
+ "Super Bosses/Dark Yojimbo": 8223,
+ "Mt. Gagazet/Biran and Yenke": 8224,
+ "Outside/Seymour Flux": 8225,
+ "Super Bosses/Dark Anima": 8226,
+ "Inside/Sanctuary Keeper": 8227,
+ "Cloister/Spectral Keeper": 8228,
+ "Cloister/Yunalesca": 8229,
+ "Super Bosses/Dark Bahamut": 8230,
+ "Sea of Sorrow/Seymour Omnis": 8231,
+ "Omega Ruins/Ultima Weapon": 8235,
+ "Omega Ruins/Omega Weapon": 8236,
+ "Dark Magus Sisters/Dark Mindy": 8237,
+ "Dark Magus Sisters/Dark Sandy": 8238,
+ "Dark Magus Sisters/Dark Cindy": 8239,
+ "Temple/Geosgaeno": 8240,
+ "Tidus/Unlock": 12288,
+ "Yuna/Unlock": 12289,
+ "Auron/Unlock": 12290,
+ "Kimahri/Unlock": 12291,
+ "Wakka/Unlock": 12292,
+ "Lulu/Unlock": 12293,
+ "Rikku/Unlock": 12294,
+ "Seymour/Unlock": 12295,
+ "Aeons/Valefor": 12296,
+ "Aeons/Ifrit": 12297,
+ "Aeons/Ixion": 12298,
+ "Aeons/Shiva": 12299,
+ "Aeons/Bahamut": 12300,
+ "Aeons/Anima": 12301,
+ "Aeons/Yojimbo": 12302,
+ "Aeons/Magus Sisters": 12303,
+ "Overdrives/Slice and Dice": 16385,
+ "Overdrives/Energy Rain": 16386,
+ "Overdrives/Blitz Ace": 16387,
+ "Overdrives/Shooting Star": 16388,
+ "Overdrives/Banishing Blade": 16389,
+ "Overdrives/Tornado": 16390,
+ "Wakka/Attack Reels": 16391,
+ "Wakka/Status Reels": 16392,
+ "Wakka/Auroch Reels": 16393,
+ "Overdrives/Seed Cannon": 16394,
+ "Overdrives/Stone Breath": 16395,
+ "Overdrives/Self Destruct": 16396,
+ "Overdrives/Fire Breath": 16397,
+ "Overdrives/Aqua Breath": 16398,
+ "Overdrives/Bad Breath": 16399,
+ "Overdrives/Doom": 16400,
+ "Overdrives/Thrust Kick": 16401,
+ "Overdrives/White Wind": 16402,
+ "Overdrives/Mighty Guard": 16403,
+ "Overdrives/Nova": 16404,
+ "Overdrives/Energy Blast": 16405,
+ "Overdrive Modes/Stoic": 20480,
+ "Overdrive Modes/Warrior": 20481,
+ "Overdrive Modes/Comrade": 20482,
+ "Overdrive Modes/Healer": 20483,
+ "Overdrive Modes/Tactician": 20484,
+ "Overdrive Modes/Victim": 20485,
+ "Overdrive Modes/Dancer": 20486,
+ "Overdrive Modes/Avenger": 20487,
+ "Overdrive Modes/Slayer": 20488,
+ "Overdrive Modes/Hero": 20489,
+ "Overdrive Modes/Rook": 20490,
+ "Overdrive Modes/Victor": 20491,
+ "Overdrive Modes/Coward": 20492,
+ "Overdrive Modes/Ally": 20493,
+ "Overdrive Modes/Sufferer": 20494,
+ "Overdrive Modes/Daredevil": 20495,
+ "Overdrive Modes/Loner": 20496,
+ "Al Bhed Primer I/Al Bhed Ship": 24577,
+ "Al Bhed Primer I/Bikanel": 24577,
+ "Al Bhed Primers/Al Bhed Primer II": 24578,
+ "Al Bhed Primer III/S.S. Liki": 24579,
+ "Al Bhed Primer III/Bikanel": 24579,
+ "Al Bhed Primers/Al Bhed Primer IV": 24580,
+ "Al Bhed Primer V/S.S. Winno": 24581,
+ "Al Bhed Primer V/Bikanel": 24581,
+ "Al Bhed Primers/Al Bhed Primer VI": 24582,
+ "Al Bhed Primers/Al Bhed Primer VII": 24583,
+ "Al Bhed Primers/Al Bhed Primer VIII": 24584,
+ "Al Bhed Primers/Al Bhed Primer IX": 24585,
+ "Al Bhed Primers/Al Bhed Primer X": 24586,
+ "Al Bhed Primers/Al Bhed Primer XI": 24587,
+ "Al Bhed Primers/Al Bhed Primer XII": 24588,
+ "Al Bhed Primers/Al Bhed Primer XIII": 24589,
+ "Al Bhed Primer XIV/Thunder Plains": 24590,
+ "Al Bhed Primer XIV/Bikanel": 24590,
+ "Al Bhed Primers/Al Bhed Primer XV": 24591,
+ "Al Bhed Primers/Al Bhed Primer XVI": 24592,
+ "Al Bhed Primers/Al Bhed Primer XVII": 24593,
+ "Al Bhed Primers/Al Bhed Primer XVIII": 24594,
+ "Al Bhed Primers/Al Bhed Primer XIX": 24595,
+ "Al Bhed Primers/Al Bhed Primer XX": 24596,
+ "Al Bhed Primers/Al Bhed Primer XXI": 24597,
+ "Al Bhed Primers/Al Bhed Primer XXII": 24598,
+ "Al Bhed Primers/Al Bhed Primer XXIII": 24599,
+ "Al Bhed Primers/Al Bhed Primer XXIV": 24600,
+ "Al Bhed Primers/Al Bhed Primer XXV": 24601,
+ "Al Bhed Primers/Al Bhed Primer XXVI": 24602,
+ "Tidus/Brotherhood Upgrade": 24613,
+ "Caladbolg Upgrade/Crest": 24614,
+ "Caladbolg Upgrade/Sigil": 24615,
+ "Nirvana Upgrade/Crest": 24616,
+ "Nirvana Upgrade/Sigil": 24617,
+ "Masamune Upgrade/Crest": 24618,
+ "Masamune Upgrade/Sigil": 24619,
+ "Spirit Lance Upgrade/Crest": 24620,
+ "Spirit Lance Upgrade/Sigil": 24621,
+ "World Champion Upgrade/Crest": 24622,
+ "World Champion Upgrade/Sigil": 24623,
+ "Onion Knight Upgrade/Crest": 24624,
+ "Onion Knight Upgrade/Sigil": 24625,
+ "Godhand Upgrade/Crest": 24626,
+ "Godhand Upgrade/Sigil": 24627,
+ "Tidus/Sphere Grid Node 0": 28672,
+ "Tidus/Sphere Grid Node 1": 28673,
+ "Tidus/Sphere Grid Node 2": 28674,
+ "Tidus/Sphere Grid Node 3": 28675,
+ "Tidus/Sphere Grid Node 4": 28676,
+ "Tidus/Sphere Grid Node 5": 28677,
+ "Tidus/Sphere Grid Node 6": 28678,
+ "Tidus/Sphere Grid Node 7": 28679,
+ "Tidus/Sphere Grid Node 8": 28680,
+ "Tidus/Sphere Grid Node 9": 28681,
+ "Tidus/Sphere Grid Node 10": 28682,
+ "Tidus/Sphere Grid Node 11": 28683,
+ "Tidus/Sphere Grid Node 12": 28684,
+ "Tidus/Sphere Grid Node 13": 28685,
+ "Tidus/Sphere Grid Node 14": 28686,
+ "Tidus/Sphere Grid Node 15": 28687,
+ "Tidus/Sphere Grid Node 16": 28688,
+ "Tidus/Sphere Grid Node 17": 28689,
+ "Tidus/Sphere Grid Node 18": 28690,
+ "Tidus/Sphere Grid Node 19": 28691,
+ "Tidus/Sphere Grid Node 20": 28692,
+ "Tidus/Sphere Grid Node 21": 28693,
+ "Tidus/Sphere Grid Node 22": 28694,
+ "Tidus/Sphere Grid Node 23": 28695,
+ "Tidus/Sphere Grid Node 24": 28696,
+ "Tidus/Sphere Grid Node 25": 28697,
+ "Tidus/Sphere Grid Node 26": 28698,
+ "Tidus/Sphere Grid Node 27": 28699,
+ "Tidus/Sphere Grid Node 28": 28700,
+ "Tidus/Sphere Grid Node 29": 28701,
+ "Tidus/Sphere Grid Node 30": 28702,
+ "Tidus/Sphere Grid Node 31": 28703,
+ "Tidus/Sphere Grid Node 32": 28704,
+ "Tidus/Sphere Grid Node 33": 28705,
+ "Tidus/Sphere Grid Node 34": 28706,
+ "Tidus/Sphere Grid Node 35": 28707,
+ "Tidus/Sphere Grid Node 36": 28708,
+ "Tidus/Sphere Grid Node 37": 28709,
+ "Tidus/Sphere Grid Node 38": 28710,
+ "Tidus/Sphere Grid Node 39": 28711,
+ "Tidus/Sphere Grid Node 40": 28712,
+ "Tidus/Sphere Grid Node 41": 28713,
+ "Tidus/Sphere Grid Node 42": 28714,
+ "Tidus/Sphere Grid Node 43": 28715,
+ "Tidus/Sphere Grid Node 44": 28716,
+ "Tidus/Sphere Grid Node 45": 28717,
+ "Tidus/Sphere Grid Node 46": 28718,
+ "Tidus/Sphere Grid Node 47": 28719,
+ "Tidus/Sphere Grid Node 48": 28720,
+ "Tidus/Sphere Grid Node 49": 28721,
+ "Tidus/Sphere Grid Node 50": 28722,
+ "Tidus/Sphere Grid Node 51": 28723,
+ "Tidus/Sphere Grid Node 52": 28724,
+ "Tidus/Sphere Grid Node 53": 28725,
+ "Tidus/Sphere Grid Node 54": 28726,
+ "Tidus/Sphere Grid Node 55": 28727,
+ "Tidus/Sphere Grid Node 56": 28728,
+ "Tidus/Sphere Grid Node 57": 28729,
+ "Tidus/Sphere Grid Node 58": 28730,
+ "Tidus/Sphere Grid Node 59": 28731,
+ "Tidus/Sphere Grid Node 60": 28732,
+ "Tidus/Sphere Grid Node 61": 28733,
+ "Tidus/Sphere Grid Node 62": 28734,
+ "Tidus/Sphere Grid Node 63": 28735,
+ "Tidus/Sphere Grid Node 64": 28736,
+ "Tidus/Sphere Grid Node 65": 28737,
+ "Tidus/Sphere Grid Node 66": 28738,
+ "Tidus/Sphere Grid Node 67": 28739,
+ "Tidus/Sphere Grid Node 68": 28740,
+ "Tidus/Sphere Grid Node 69": 28741,
+ "Tidus/Sphere Grid Node 70": 28742,
+ "Tidus/Sphere Grid Node 71": 28743,
+ "Tidus/Sphere Grid Node 72": 28744,
+ "Tidus/Sphere Grid Node 73": 28745,
+ "Tidus/Sphere Grid Node 74": 28746,
+ "Tidus/Sphere Grid Node 75": 28747,
+ "Tidus/Sphere Grid Node 76": 28748,
+ "Tidus/Sphere Grid Node 77": 28749,
+ "Tidus/Sphere Grid Node 78": 28750,
+ "Tidus/Sphere Grid Node 79": 28751,
+ "Tidus/Sphere Grid Node 80": 28752,
+ "Tidus/Sphere Grid Node 81": 28753,
+ "Tidus/Sphere Grid Node 82": 28754,
+ "Tidus/Sphere Grid Node 83": 28755,
+ "Tidus/Sphere Grid Node 84": 28756,
+ "Tidus/Sphere Grid Node 85": 28757,
+ "Tidus/Sphere Grid Node 86": 28758,
+ "Tidus/Sphere Grid Node 87": 28759,
+ "Tidus/Sphere Grid Node 88": 28760,
+ "Tidus/Sphere Grid Node 89": 28761,
+ "Tidus/Sphere Grid Node 90": 28762,
+ "Tidus/Sphere Grid Node 91": 28763,
+ "Tidus/Sphere Grid Node 92": 28764,
+ "Tidus/Sphere Grid Node 93": 28765,
+ "Tidus/Sphere Grid Node 94": 28766,
+ "Tidus/Sphere Grid Node 95": 28767,
+ "Tidus/Sphere Grid Node 96": 28768,
+ "Tidus/Sphere Grid Node 97": 28769,
+ "Tidus/Sphere Grid Node 98": 28770,
+ "Tidus/Sphere Grid Node 99": 28771,
+ "Yuna/Sphere Grid Node 0": 28772,
+ "Yuna/Sphere Grid Node 1": 28773,
+ "Yuna/Sphere Grid Node 2": 28774,
+ "Yuna/Sphere Grid Node 3": 28775,
+ "Yuna/Sphere Grid Node 4": 28776,
+ "Yuna/Sphere Grid Node 5": 28777,
+ "Yuna/Sphere Grid Node 6": 28778,
+ "Yuna/Sphere Grid Node 7": 28779,
+ "Yuna/Sphere Grid Node 8": 28780,
+ "Yuna/Sphere Grid Node 9": 28781,
+ "Yuna/Sphere Grid Node 10": 28782,
+ "Yuna/Sphere Grid Node 11": 28783,
+ "Yuna/Sphere Grid Node 12": 28784,
+ "Yuna/Sphere Grid Node 13": 28785,
+ "Yuna/Sphere Grid Node 14": 28786,
+ "Yuna/Sphere Grid Node 15": 28787,
+ "Yuna/Sphere Grid Node 16": 28788,
+ "Yuna/Sphere Grid Node 17": 28789,
+ "Yuna/Sphere Grid Node 18": 28790,
+ "Yuna/Sphere Grid Node 19": 28791,
+ "Yuna/Sphere Grid Node 20": 28792,
+ "Yuna/Sphere Grid Node 21": 28793,
+ "Yuna/Sphere Grid Node 22": 28794,
+ "Yuna/Sphere Grid Node 23": 28795,
+ "Yuna/Sphere Grid Node 24": 28796,
+ "Yuna/Sphere Grid Node 25": 28797,
+ "Yuna/Sphere Grid Node 26": 28798,
+ "Yuna/Sphere Grid Node 27": 28799,
+ "Yuna/Sphere Grid Node 28": 28800,
+ "Yuna/Sphere Grid Node 29": 28801,
+ "Yuna/Sphere Grid Node 30": 28802,
+ "Yuna/Sphere Grid Node 31": 28803,
+ "Yuna/Sphere Grid Node 32": 28804,
+ "Yuna/Sphere Grid Node 33": 28805,
+ "Yuna/Sphere Grid Node 34": 28806,
+ "Yuna/Sphere Grid Node 35": 28807,
+ "Yuna/Sphere Grid Node 36": 28808,
+ "Yuna/Sphere Grid Node 37": 28809,
+ "Yuna/Sphere Grid Node 38": 28810,
+ "Yuna/Sphere Grid Node 39": 28811,
+ "Yuna/Sphere Grid Node 40": 28812,
+ "Yuna/Sphere Grid Node 41": 28813,
+ "Yuna/Sphere Grid Node 42": 28814,
+ "Yuna/Sphere Grid Node 43": 28815,
+ "Yuna/Sphere Grid Node 44": 28816,
+ "Yuna/Sphere Grid Node 45": 28817,
+ "Yuna/Sphere Grid Node 46": 28818,
+ "Yuna/Sphere Grid Node 47": 28819,
+ "Yuna/Sphere Grid Node 48": 28820,
+ "Yuna/Sphere Grid Node 49": 28821,
+ "Yuna/Sphere Grid Node 50": 28822,
+ "Yuna/Sphere Grid Node 51": 28823,
+ "Yuna/Sphere Grid Node 52": 28824,
+ "Yuna/Sphere Grid Node 53": 28825,
+ "Yuna/Sphere Grid Node 54": 28826,
+ "Yuna/Sphere Grid Node 55": 28827,
+ "Yuna/Sphere Grid Node 56": 28828,
+ "Yuna/Sphere Grid Node 57": 28829,
+ "Yuna/Sphere Grid Node 58": 28830,
+ "Yuna/Sphere Grid Node 59": 28831,
+ "Yuna/Sphere Grid Node 60": 28832,
+ "Yuna/Sphere Grid Node 61": 28833,
+ "Yuna/Sphere Grid Node 62": 28834,
+ "Yuna/Sphere Grid Node 63": 28835,
+ "Yuna/Sphere Grid Node 64": 28836,
+ "Yuna/Sphere Grid Node 65": 28837,
+ "Yuna/Sphere Grid Node 66": 28838,
+ "Yuna/Sphere Grid Node 67": 28839,
+ "Yuna/Sphere Grid Node 68": 28840,
+ "Yuna/Sphere Grid Node 69": 28841,
+ "Yuna/Sphere Grid Node 70": 28842,
+ "Yuna/Sphere Grid Node 71": 28843,
+ "Yuna/Sphere Grid Node 72": 28844,
+ "Yuna/Sphere Grid Node 73": 28845,
+ "Yuna/Sphere Grid Node 74": 28846,
+ "Yuna/Sphere Grid Node 75": 28847,
+ "Yuna/Sphere Grid Node 76": 28848,
+ "Yuna/Sphere Grid Node 77": 28849,
+ "Yuna/Sphere Grid Node 78": 28850,
+ "Yuna/Sphere Grid Node 79": 28851,
+ "Yuna/Sphere Grid Node 80": 28852,
+ "Yuna/Sphere Grid Node 81": 28853,
+ "Yuna/Sphere Grid Node 82": 28854,
+ "Yuna/Sphere Grid Node 83": 28855,
+ "Yuna/Sphere Grid Node 84": 28856,
+ "Yuna/Sphere Grid Node 85": 28857,
+ "Yuna/Sphere Grid Node 86": 28858,
+ "Yuna/Sphere Grid Node 87": 28859,
+ "Yuna/Sphere Grid Node 88": 28860,
+ "Yuna/Sphere Grid Node 89": 28861,
+ "Yuna/Sphere Grid Node 90": 28862,
+ "Yuna/Sphere Grid Node 91": 28863,
+ "Yuna/Sphere Grid Node 92": 28864,
+ "Yuna/Sphere Grid Node 93": 28865,
+ "Yuna/Sphere Grid Node 94": 28866,
+ "Yuna/Sphere Grid Node 95": 28867,
+ "Yuna/Sphere Grid Node 96": 28868,
+ "Yuna/Sphere Grid Node 97": 28869,
+ "Yuna/Sphere Grid Node 98": 28870,
+ "Yuna/Sphere Grid Node 99": 28871,
+ "Auron/Sphere Grid Node 0": 28872,
+ "Auron/Sphere Grid Node 1": 28873,
+ "Auron/Sphere Grid Node 2": 28874,
+ "Auron/Sphere Grid Node 3": 28875,
+ "Auron/Sphere Grid Node 4": 28876,
+ "Auron/Sphere Grid Node 5": 28877,
+ "Auron/Sphere Grid Node 6": 28878,
+ "Auron/Sphere Grid Node 7": 28879,
+ "Auron/Sphere Grid Node 8": 28880,
+ "Auron/Sphere Grid Node 9": 28881,
+ "Auron/Sphere Grid Node 10": 28882,
+ "Auron/Sphere Grid Node 11": 28883,
+ "Auron/Sphere Grid Node 12": 28884,
+ "Auron/Sphere Grid Node 13": 28885,
+ "Auron/Sphere Grid Node 14": 28886,
+ "Auron/Sphere Grid Node 15": 28887,
+ "Auron/Sphere Grid Node 16": 28888,
+ "Auron/Sphere Grid Node 17": 28889,
+ "Auron/Sphere Grid Node 18": 28890,
+ "Auron/Sphere Grid Node 19": 28891,
+ "Auron/Sphere Grid Node 20": 28892,
+ "Auron/Sphere Grid Node 21": 28893,
+ "Auron/Sphere Grid Node 22": 28894,
+ "Auron/Sphere Grid Node 23": 28895,
+ "Auron/Sphere Grid Node 24": 28896,
+ "Auron/Sphere Grid Node 25": 28897,
+ "Auron/Sphere Grid Node 26": 28898,
+ "Auron/Sphere Grid Node 27": 28899,
+ "Auron/Sphere Grid Node 28": 28900,
+ "Auron/Sphere Grid Node 29": 28901,
+ "Auron/Sphere Grid Node 30": 28902,
+ "Auron/Sphere Grid Node 31": 28903,
+ "Auron/Sphere Grid Node 32": 28904,
+ "Auron/Sphere Grid Node 33": 28905,
+ "Auron/Sphere Grid Node 34": 28906,
+ "Auron/Sphere Grid Node 35": 28907,
+ "Auron/Sphere Grid Node 36": 28908,
+ "Auron/Sphere Grid Node 37": 28909,
+ "Auron/Sphere Grid Node 38": 28910,
+ "Auron/Sphere Grid Node 39": 28911,
+ "Auron/Sphere Grid Node 40": 28912,
+ "Auron/Sphere Grid Node 41": 28913,
+ "Auron/Sphere Grid Node 42": 28914,
+ "Auron/Sphere Grid Node 43": 28915,
+ "Auron/Sphere Grid Node 44": 28916,
+ "Auron/Sphere Grid Node 45": 28917,
+ "Auron/Sphere Grid Node 46": 28918,
+ "Auron/Sphere Grid Node 47": 28919,
+ "Auron/Sphere Grid Node 48": 28920,
+ "Auron/Sphere Grid Node 49": 28921,
+ "Auron/Sphere Grid Node 50": 28922,
+ "Auron/Sphere Grid Node 51": 28923,
+ "Auron/Sphere Grid Node 52": 28924,
+ "Auron/Sphere Grid Node 53": 28925,
+ "Auron/Sphere Grid Node 54": 28926,
+ "Auron/Sphere Grid Node 55": 28927,
+ "Auron/Sphere Grid Node 56": 28928,
+ "Auron/Sphere Grid Node 57": 28929,
+ "Auron/Sphere Grid Node 58": 28930,
+ "Auron/Sphere Grid Node 59": 28931,
+ "Auron/Sphere Grid Node 60": 28932,
+ "Auron/Sphere Grid Node 61": 28933,
+ "Auron/Sphere Grid Node 62": 28934,
+ "Auron/Sphere Grid Node 63": 28935,
+ "Auron/Sphere Grid Node 64": 28936,
+ "Auron/Sphere Grid Node 65": 28937,
+ "Auron/Sphere Grid Node 66": 28938,
+ "Auron/Sphere Grid Node 67": 28939,
+ "Auron/Sphere Grid Node 68": 28940,
+ "Auron/Sphere Grid Node 69": 28941,
+ "Auron/Sphere Grid Node 70": 28942,
+ "Auron/Sphere Grid Node 71": 28943,
+ "Auron/Sphere Grid Node 72": 28944,
+ "Auron/Sphere Grid Node 73": 28945,
+ "Auron/Sphere Grid Node 74": 28946,
+ "Auron/Sphere Grid Node 75": 28947,
+ "Auron/Sphere Grid Node 76": 28948,
+ "Auron/Sphere Grid Node 77": 28949,
+ "Auron/Sphere Grid Node 78": 28950,
+ "Auron/Sphere Grid Node 79": 28951,
+ "Auron/Sphere Grid Node 80": 28952,
+ "Auron/Sphere Grid Node 81": 28953,
+ "Auron/Sphere Grid Node 82": 28954,
+ "Auron/Sphere Grid Node 83": 28955,
+ "Auron/Sphere Grid Node 84": 28956,
+ "Auron/Sphere Grid Node 85": 28957,
+ "Auron/Sphere Grid Node 86": 28958,
+ "Auron/Sphere Grid Node 87": 28959,
+ "Auron/Sphere Grid Node 88": 28960,
+ "Auron/Sphere Grid Node 89": 28961,
+ "Auron/Sphere Grid Node 90": 28962,
+ "Auron/Sphere Grid Node 91": 28963,
+ "Auron/Sphere Grid Node 92": 28964,
+ "Auron/Sphere Grid Node 93": 28965,
+ "Auron/Sphere Grid Node 94": 28966,
+ "Auron/Sphere Grid Node 95": 28967,
+ "Auron/Sphere Grid Node 96": 28968,
+ "Auron/Sphere Grid Node 97": 28969,
+ "Auron/Sphere Grid Node 98": 28970,
+ "Auron/Sphere Grid Node 99": 28971,
+ "Kimahri/Sphere Grid Node 0": 28972,
+ "Kimahri/Sphere Grid Node 1": 28973,
+ "Kimahri/Sphere Grid Node 2": 28974,
+ "Kimahri/Sphere Grid Node 3": 28975,
+ "Kimahri/Sphere Grid Node 4": 28976,
+ "Kimahri/Sphere Grid Node 5": 28977,
+ "Kimahri/Sphere Grid Node 6": 28978,
+ "Kimahri/Sphere Grid Node 7": 28979,
+ "Kimahri/Sphere Grid Node 8": 28980,
+ "Kimahri/Sphere Grid Node 9": 28981,
+ "Kimahri/Sphere Grid Node 10": 28982,
+ "Kimahri/Sphere Grid Node 11": 28983,
+ "Kimahri/Sphere Grid Node 12": 28984,
+ "Kimahri/Sphere Grid Node 13": 28985,
+ "Kimahri/Sphere Grid Node 14": 28986,
+ "Kimahri/Sphere Grid Node 15": 28987,
+ "Kimahri/Sphere Grid Node 16": 28988,
+ "Kimahri/Sphere Grid Node 17": 28989,
+ "Kimahri/Sphere Grid Node 18": 28990,
+ "Kimahri/Sphere Grid Node 19": 28991,
+ "Kimahri/Sphere Grid Node 20": 28992,
+ "Kimahri/Sphere Grid Node 21": 28993,
+ "Kimahri/Sphere Grid Node 22": 28994,
+ "Kimahri/Sphere Grid Node 23": 28995,
+ "Kimahri/Sphere Grid Node 24": 28996,
+ "Kimahri/Sphere Grid Node 25": 28997,
+ "Kimahri/Sphere Grid Node 26": 28998,
+ "Kimahri/Sphere Grid Node 27": 28999,
+ "Kimahri/Sphere Grid Node 28": 29000,
+ "Kimahri/Sphere Grid Node 29": 29001,
+ "Kimahri/Sphere Grid Node 30": 29002,
+ "Kimahri/Sphere Grid Node 31": 29003,
+ "Kimahri/Sphere Grid Node 32": 29004,
+ "Kimahri/Sphere Grid Node 33": 29005,
+ "Kimahri/Sphere Grid Node 34": 29006,
+ "Kimahri/Sphere Grid Node 35": 29007,
+ "Kimahri/Sphere Grid Node 36": 29008,
+ "Kimahri/Sphere Grid Node 37": 29009,
+ "Kimahri/Sphere Grid Node 38": 29010,
+ "Kimahri/Sphere Grid Node 39": 29011,
+ "Kimahri/Sphere Grid Node 40": 29012,
+ "Kimahri/Sphere Grid Node 41": 29013,
+ "Kimahri/Sphere Grid Node 42": 29014,
+ "Kimahri/Sphere Grid Node 43": 29015,
+ "Kimahri/Sphere Grid Node 44": 29016,
+ "Kimahri/Sphere Grid Node 45": 29017,
+ "Kimahri/Sphere Grid Node 46": 29018,
+ "Kimahri/Sphere Grid Node 47": 29019,
+ "Kimahri/Sphere Grid Node 48": 29020,
+ "Kimahri/Sphere Grid Node 49": 29021,
+ "Kimahri/Sphere Grid Node 50": 29022,
+ "Kimahri/Sphere Grid Node 51": 29023,
+ "Kimahri/Sphere Grid Node 52": 29024,
+ "Kimahri/Sphere Grid Node 53": 29025,
+ "Kimahri/Sphere Grid Node 54": 29026,
+ "Kimahri/Sphere Grid Node 55": 29027,
+ "Kimahri/Sphere Grid Node 56": 29028,
+ "Kimahri/Sphere Grid Node 57": 29029,
+ "Kimahri/Sphere Grid Node 58": 29030,
+ "Kimahri/Sphere Grid Node 59": 29031,
+ "Kimahri/Sphere Grid Node 60": 29032,
+ "Kimahri/Sphere Grid Node 61": 29033,
+ "Kimahri/Sphere Grid Node 62": 29034,
+ "Kimahri/Sphere Grid Node 63": 29035,
+ "Kimahri/Sphere Grid Node 64": 29036,
+ "Kimahri/Sphere Grid Node 65": 29037,
+ "Kimahri/Sphere Grid Node 66": 29038,
+ "Kimahri/Sphere Grid Node 67": 29039,
+ "Kimahri/Sphere Grid Node 68": 29040,
+ "Kimahri/Sphere Grid Node 69": 29041,
+ "Kimahri/Sphere Grid Node 70": 29042,
+ "Kimahri/Sphere Grid Node 71": 29043,
+ "Kimahri/Sphere Grid Node 72": 29044,
+ "Kimahri/Sphere Grid Node 73": 29045,
+ "Kimahri/Sphere Grid Node 74": 29046,
+ "Kimahri/Sphere Grid Node 75": 29047,
+ "Kimahri/Sphere Grid Node 76": 29048,
+ "Kimahri/Sphere Grid Node 77": 29049,
+ "Kimahri/Sphere Grid Node 78": 29050,
+ "Kimahri/Sphere Grid Node 79": 29051,
+ "Kimahri/Sphere Grid Node 80": 29052,
+ "Kimahri/Sphere Grid Node 81": 29053,
+ "Kimahri/Sphere Grid Node 82": 29054,
+ "Kimahri/Sphere Grid Node 83": 29055,
+ "Kimahri/Sphere Grid Node 84": 29056,
+ "Kimahri/Sphere Grid Node 85": 29057,
+ "Kimahri/Sphere Grid Node 86": 29058,
+ "Kimahri/Sphere Grid Node 87": 29059,
+ "Kimahri/Sphere Grid Node 88": 29060,
+ "Kimahri/Sphere Grid Node 89": 29061,
+ "Kimahri/Sphere Grid Node 90": 29062,
+ "Kimahri/Sphere Grid Node 91": 29063,
+ "Kimahri/Sphere Grid Node 92": 29064,
+ "Kimahri/Sphere Grid Node 93": 29065,
+ "Kimahri/Sphere Grid Node 94": 29066,
+ "Kimahri/Sphere Grid Node 95": 29067,
+ "Kimahri/Sphere Grid Node 96": 29068,
+ "Kimahri/Sphere Grid Node 97": 29069,
+ "Kimahri/Sphere Grid Node 98": 29070,
+ "Kimahri/Sphere Grid Node 99": 29071,
+ "Wakka/Sphere Grid Node 0": 29072,
+ "Wakka/Sphere Grid Node 1": 29073,
+ "Wakka/Sphere Grid Node 2": 29074,
+ "Wakka/Sphere Grid Node 3": 29075,
+ "Wakka/Sphere Grid Node 4": 29076,
+ "Wakka/Sphere Grid Node 5": 29077,
+ "Wakka/Sphere Grid Node 6": 29078,
+ "Wakka/Sphere Grid Node 7": 29079,
+ "Wakka/Sphere Grid Node 8": 29080,
+ "Wakka/Sphere Grid Node 9": 29081,
+ "Wakka/Sphere Grid Node 10": 29082,
+ "Wakka/Sphere Grid Node 11": 29083,
+ "Wakka/Sphere Grid Node 12": 29084,
+ "Wakka/Sphere Grid Node 13": 29085,
+ "Wakka/Sphere Grid Node 14": 29086,
+ "Wakka/Sphere Grid Node 15": 29087,
+ "Wakka/Sphere Grid Node 16": 29088,
+ "Wakka/Sphere Grid Node 17": 29089,
+ "Wakka/Sphere Grid Node 18": 29090,
+ "Wakka/Sphere Grid Node 19": 29091,
+ "Wakka/Sphere Grid Node 20": 29092,
+ "Wakka/Sphere Grid Node 21": 29093,
+ "Wakka/Sphere Grid Node 22": 29094,
+ "Wakka/Sphere Grid Node 23": 29095,
+ "Wakka/Sphere Grid Node 24": 29096,
+ "Wakka/Sphere Grid Node 25": 29097,
+ "Wakka/Sphere Grid Node 26": 29098,
+ "Wakka/Sphere Grid Node 27": 29099,
+ "Wakka/Sphere Grid Node 28": 29100,
+ "Wakka/Sphere Grid Node 29": 29101,
+ "Wakka/Sphere Grid Node 30": 29102,
+ "Wakka/Sphere Grid Node 31": 29103,
+ "Wakka/Sphere Grid Node 32": 29104,
+ "Wakka/Sphere Grid Node 33": 29105,
+ "Wakka/Sphere Grid Node 34": 29106,
+ "Wakka/Sphere Grid Node 35": 29107,
+ "Wakka/Sphere Grid Node 36": 29108,
+ "Wakka/Sphere Grid Node 37": 29109,
+ "Wakka/Sphere Grid Node 38": 29110,
+ "Wakka/Sphere Grid Node 39": 29111,
+ "Wakka/Sphere Grid Node 40": 29112,
+ "Wakka/Sphere Grid Node 41": 29113,
+ "Wakka/Sphere Grid Node 42": 29114,
+ "Wakka/Sphere Grid Node 43": 29115,
+ "Wakka/Sphere Grid Node 44": 29116,
+ "Wakka/Sphere Grid Node 45": 29117,
+ "Wakka/Sphere Grid Node 46": 29118,
+ "Wakka/Sphere Grid Node 47": 29119,
+ "Wakka/Sphere Grid Node 48": 29120,
+ "Wakka/Sphere Grid Node 49": 29121,
+ "Wakka/Sphere Grid Node 50": 29122,
+ "Wakka/Sphere Grid Node 51": 29123,
+ "Wakka/Sphere Grid Node 52": 29124,
+ "Wakka/Sphere Grid Node 53": 29125,
+ "Wakka/Sphere Grid Node 54": 29126,
+ "Wakka/Sphere Grid Node 55": 29127,
+ "Wakka/Sphere Grid Node 56": 29128,
+ "Wakka/Sphere Grid Node 57": 29129,
+ "Wakka/Sphere Grid Node 58": 29130,
+ "Wakka/Sphere Grid Node 59": 29131,
+ "Wakka/Sphere Grid Node 60": 29132,
+ "Wakka/Sphere Grid Node 61": 29133,
+ "Wakka/Sphere Grid Node 62": 29134,
+ "Wakka/Sphere Grid Node 63": 29135,
+ "Wakka/Sphere Grid Node 64": 29136,
+ "Wakka/Sphere Grid Node 65": 29137,
+ "Wakka/Sphere Grid Node 66": 29138,
+ "Wakka/Sphere Grid Node 67": 29139,
+ "Wakka/Sphere Grid Node 68": 29140,
+ "Wakka/Sphere Grid Node 69": 29141,
+ "Wakka/Sphere Grid Node 70": 29142,
+ "Wakka/Sphere Grid Node 71": 29143,
+ "Wakka/Sphere Grid Node 72": 29144,
+ "Wakka/Sphere Grid Node 73": 29145,
+ "Wakka/Sphere Grid Node 74": 29146,
+ "Wakka/Sphere Grid Node 75": 29147,
+ "Wakka/Sphere Grid Node 76": 29148,
+ "Wakka/Sphere Grid Node 77": 29149,
+ "Wakka/Sphere Grid Node 78": 29150,
+ "Wakka/Sphere Grid Node 79": 29151,
+ "Wakka/Sphere Grid Node 80": 29152,
+ "Wakka/Sphere Grid Node 81": 29153,
+ "Wakka/Sphere Grid Node 82": 29154,
+ "Wakka/Sphere Grid Node 83": 29155,
+ "Wakka/Sphere Grid Node 84": 29156,
+ "Wakka/Sphere Grid Node 85": 29157,
+ "Wakka/Sphere Grid Node 86": 29158,
+ "Wakka/Sphere Grid Node 87": 29159,
+ "Wakka/Sphere Grid Node 88": 29160,
+ "Wakka/Sphere Grid Node 89": 29161,
+ "Wakka/Sphere Grid Node 90": 29162,
+ "Wakka/Sphere Grid Node 91": 29163,
+ "Wakka/Sphere Grid Node 92": 29164,
+ "Wakka/Sphere Grid Node 93": 29165,
+ "Wakka/Sphere Grid Node 94": 29166,
+ "Wakka/Sphere Grid Node 95": 29167,
+ "Wakka/Sphere Grid Node 96": 29168,
+ "Wakka/Sphere Grid Node 97": 29169,
+ "Wakka/Sphere Grid Node 98": 29170,
+ "Wakka/Sphere Grid Node 99": 29171,
+ "Lulu/Sphere Grid Node 0": 29172,
+ "Lulu/Sphere Grid Node 1": 29173,
+ "Lulu/Sphere Grid Node 2": 29174,
+ "Lulu/Sphere Grid Node 3": 29175,
+ "Lulu/Sphere Grid Node 4": 29176,
+ "Lulu/Sphere Grid Node 5": 29177,
+ "Lulu/Sphere Grid Node 6": 29178,
+ "Lulu/Sphere Grid Node 7": 29179,
+ "Lulu/Sphere Grid Node 8": 29180,
+ "Lulu/Sphere Grid Node 9": 29181,
+ "Lulu/Sphere Grid Node 10": 29182,
+ "Lulu/Sphere Grid Node 11": 29183,
+ "Lulu/Sphere Grid Node 12": 29184,
+ "Lulu/Sphere Grid Node 13": 29185,
+ "Lulu/Sphere Grid Node 14": 29186,
+ "Lulu/Sphere Grid Node 15": 29187,
+ "Lulu/Sphere Grid Node 16": 29188,
+ "Lulu/Sphere Grid Node 17": 29189,
+ "Lulu/Sphere Grid Node 18": 29190,
+ "Lulu/Sphere Grid Node 19": 29191,
+ "Lulu/Sphere Grid Node 20": 29192,
+ "Lulu/Sphere Grid Node 21": 29193,
+ "Lulu/Sphere Grid Node 22": 29194,
+ "Lulu/Sphere Grid Node 23": 29195,
+ "Lulu/Sphere Grid Node 24": 29196,
+ "Lulu/Sphere Grid Node 25": 29197,
+ "Lulu/Sphere Grid Node 26": 29198,
+ "Lulu/Sphere Grid Node 27": 29199,
+ "Lulu/Sphere Grid Node 28": 29200,
+ "Lulu/Sphere Grid Node 29": 29201,
+ "Lulu/Sphere Grid Node 30": 29202,
+ "Lulu/Sphere Grid Node 31": 29203,
+ "Lulu/Sphere Grid Node 32": 29204,
+ "Lulu/Sphere Grid Node 33": 29205,
+ "Lulu/Sphere Grid Node 34": 29206,
+ "Lulu/Sphere Grid Node 35": 29207,
+ "Lulu/Sphere Grid Node 36": 29208,
+ "Lulu/Sphere Grid Node 37": 29209,
+ "Lulu/Sphere Grid Node 38": 29210,
+ "Lulu/Sphere Grid Node 39": 29211,
+ "Lulu/Sphere Grid Node 40": 29212,
+ "Lulu/Sphere Grid Node 41": 29213,
+ "Lulu/Sphere Grid Node 42": 29214,
+ "Lulu/Sphere Grid Node 43": 29215,
+ "Lulu/Sphere Grid Node 44": 29216,
+ "Lulu/Sphere Grid Node 45": 29217,
+ "Lulu/Sphere Grid Node 46": 29218,
+ "Lulu/Sphere Grid Node 47": 29219,
+ "Lulu/Sphere Grid Node 48": 29220,
+ "Lulu/Sphere Grid Node 49": 29221,
+ "Lulu/Sphere Grid Node 50": 29222,
+ "Lulu/Sphere Grid Node 51": 29223,
+ "Lulu/Sphere Grid Node 52": 29224,
+ "Lulu/Sphere Grid Node 53": 29225,
+ "Lulu/Sphere Grid Node 54": 29226,
+ "Lulu/Sphere Grid Node 55": 29227,
+ "Lulu/Sphere Grid Node 56": 29228,
+ "Lulu/Sphere Grid Node 57": 29229,
+ "Lulu/Sphere Grid Node 58": 29230,
+ "Lulu/Sphere Grid Node 59": 29231,
+ "Lulu/Sphere Grid Node 60": 29232,
+ "Lulu/Sphere Grid Node 61": 29233,
+ "Lulu/Sphere Grid Node 62": 29234,
+ "Lulu/Sphere Grid Node 63": 29235,
+ "Lulu/Sphere Grid Node 64": 29236,
+ "Lulu/Sphere Grid Node 65": 29237,
+ "Lulu/Sphere Grid Node 66": 29238,
+ "Lulu/Sphere Grid Node 67": 29239,
+ "Lulu/Sphere Grid Node 68": 29240,
+ "Lulu/Sphere Grid Node 69": 29241,
+ "Lulu/Sphere Grid Node 70": 29242,
+ "Lulu/Sphere Grid Node 71": 29243,
+ "Lulu/Sphere Grid Node 72": 29244,
+ "Lulu/Sphere Grid Node 73": 29245,
+ "Lulu/Sphere Grid Node 74": 29246,
+ "Lulu/Sphere Grid Node 75": 29247,
+ "Lulu/Sphere Grid Node 76": 29248,
+ "Lulu/Sphere Grid Node 77": 29249,
+ "Lulu/Sphere Grid Node 78": 29250,
+ "Lulu/Sphere Grid Node 79": 29251,
+ "Lulu/Sphere Grid Node 80": 29252,
+ "Lulu/Sphere Grid Node 81": 29253,
+ "Lulu/Sphere Grid Node 82": 29254,
+ "Lulu/Sphere Grid Node 83": 29255,
+ "Lulu/Sphere Grid Node 84": 29256,
+ "Lulu/Sphere Grid Node 85": 29257,
+ "Lulu/Sphere Grid Node 86": 29258,
+ "Lulu/Sphere Grid Node 87": 29259,
+ "Lulu/Sphere Grid Node 88": 29260,
+ "Lulu/Sphere Grid Node 89": 29261,
+ "Lulu/Sphere Grid Node 90": 29262,
+ "Lulu/Sphere Grid Node 91": 29263,
+ "Lulu/Sphere Grid Node 92": 29264,
+ "Lulu/Sphere Grid Node 93": 29265,
+ "Lulu/Sphere Grid Node 94": 29266,
+ "Lulu/Sphere Grid Node 95": 29267,
+ "Lulu/Sphere Grid Node 96": 29268,
+ "Lulu/Sphere Grid Node 97": 29269,
+ "Lulu/Sphere Grid Node 98": 29270,
+ "Lulu/Sphere Grid Node 99": 29271,
+ "Rikku/Sphere Grid Node 0": 29272,
+ "Rikku/Sphere Grid Node 1": 29273,
+ "Rikku/Sphere Grid Node 2": 29274,
+ "Rikku/Sphere Grid Node 3": 29275,
+ "Rikku/Sphere Grid Node 4": 29276,
+ "Rikku/Sphere Grid Node 5": 29277,
+ "Rikku/Sphere Grid Node 6": 29278,
+ "Rikku/Sphere Grid Node 7": 29279,
+ "Rikku/Sphere Grid Node 8": 29280,
+ "Rikku/Sphere Grid Node 9": 29281,
+ "Rikku/Sphere Grid Node 10": 29282,
+ "Rikku/Sphere Grid Node 11": 29283,
+ "Rikku/Sphere Grid Node 12": 29284,
+ "Rikku/Sphere Grid Node 13": 29285,
+ "Rikku/Sphere Grid Node 14": 29286,
+ "Rikku/Sphere Grid Node 15": 29287,
+ "Rikku/Sphere Grid Node 16": 29288,
+ "Rikku/Sphere Grid Node 17": 29289,
+ "Rikku/Sphere Grid Node 18": 29290,
+ "Rikku/Sphere Grid Node 19": 29291,
+ "Rikku/Sphere Grid Node 20": 29292,
+ "Rikku/Sphere Grid Node 21": 29293,
+ "Rikku/Sphere Grid Node 22": 29294,
+ "Rikku/Sphere Grid Node 23": 29295,
+ "Rikku/Sphere Grid Node 24": 29296,
+ "Rikku/Sphere Grid Node 25": 29297,
+ "Rikku/Sphere Grid Node 26": 29298,
+ "Rikku/Sphere Grid Node 27": 29299,
+ "Rikku/Sphere Grid Node 28": 29300,
+ "Rikku/Sphere Grid Node 29": 29301,
+ "Rikku/Sphere Grid Node 30": 29302,
+ "Rikku/Sphere Grid Node 31": 29303,
+ "Rikku/Sphere Grid Node 32": 29304,
+ "Rikku/Sphere Grid Node 33": 29305,
+ "Rikku/Sphere Grid Node 34": 29306,
+ "Rikku/Sphere Grid Node 35": 29307,
+ "Rikku/Sphere Grid Node 36": 29308,
+ "Rikku/Sphere Grid Node 37": 29309,
+ "Rikku/Sphere Grid Node 38": 29310,
+ "Rikku/Sphere Grid Node 39": 29311,
+ "Rikku/Sphere Grid Node 40": 29312,
+ "Rikku/Sphere Grid Node 41": 29313,
+ "Rikku/Sphere Grid Node 42": 29314,
+ "Rikku/Sphere Grid Node 43": 29315,
+ "Rikku/Sphere Grid Node 44": 29316,
+ "Rikku/Sphere Grid Node 45": 29317,
+ "Rikku/Sphere Grid Node 46": 29318,
+ "Rikku/Sphere Grid Node 47": 29319,
+ "Rikku/Sphere Grid Node 48": 29320,
+ "Rikku/Sphere Grid Node 49": 29321,
+ "Rikku/Sphere Grid Node 50": 29322,
+ "Rikku/Sphere Grid Node 51": 29323,
+ "Rikku/Sphere Grid Node 52": 29324,
+ "Rikku/Sphere Grid Node 53": 29325,
+ "Rikku/Sphere Grid Node 54": 29326,
+ "Rikku/Sphere Grid Node 55": 29327,
+ "Rikku/Sphere Grid Node 56": 29328,
+ "Rikku/Sphere Grid Node 57": 29329,
+ "Rikku/Sphere Grid Node 58": 29330,
+ "Rikku/Sphere Grid Node 59": 29331,
+ "Rikku/Sphere Grid Node 60": 29332,
+ "Rikku/Sphere Grid Node 61": 29333,
+ "Rikku/Sphere Grid Node 62": 29334,
+ "Rikku/Sphere Grid Node 63": 29335,
+ "Rikku/Sphere Grid Node 64": 29336,
+ "Rikku/Sphere Grid Node 65": 29337,
+ "Rikku/Sphere Grid Node 66": 29338,
+ "Rikku/Sphere Grid Node 67": 29339,
+ "Rikku/Sphere Grid Node 68": 29340,
+ "Rikku/Sphere Grid Node 69": 29341,
+ "Rikku/Sphere Grid Node 70": 29342,
+ "Rikku/Sphere Grid Node 71": 29343,
+ "Rikku/Sphere Grid Node 72": 29344,
+ "Rikku/Sphere Grid Node 73": 29345,
+ "Rikku/Sphere Grid Node 74": 29346,
+ "Rikku/Sphere Grid Node 75": 29347,
+ "Rikku/Sphere Grid Node 76": 29348,
+ "Rikku/Sphere Grid Node 77": 29349,
+ "Rikku/Sphere Grid Node 78": 29350,
+ "Rikku/Sphere Grid Node 79": 29351,
+ "Rikku/Sphere Grid Node 80": 29352,
+ "Rikku/Sphere Grid Node 81": 29353,
+ "Rikku/Sphere Grid Node 82": 29354,
+ "Rikku/Sphere Grid Node 83": 29355,
+ "Rikku/Sphere Grid Node 84": 29356,
+ "Rikku/Sphere Grid Node 85": 29357,
+ "Rikku/Sphere Grid Node 86": 29358,
+ "Rikku/Sphere Grid Node 87": 29359,
+ "Rikku/Sphere Grid Node 88": 29360,
+ "Rikku/Sphere Grid Node 89": 29361,
+ "Rikku/Sphere Grid Node 90": 29362,
+ "Rikku/Sphere Grid Node 91": 29363,
+ "Rikku/Sphere Grid Node 92": 29364,
+ "Rikku/Sphere Grid Node 93": 29365,
+ "Rikku/Sphere Grid Node 94": 29366,
+ "Rikku/Sphere Grid Node 95": 29367,
+ "Rikku/Sphere Grid Node 96": 29368,
+ "Rikku/Sphere Grid Node 97": 29369,
+ "Rikku/Sphere Grid Node 98": 29370,
+ "Rikku/Sphere Grid Node 99": 29371,
+ "Al Bhed Ship/Al Bhed Primer I": 24577,
+ "Baaj Temple/Outside - 200 gil (Chest)": 4096,
+ "Baaj Temple/Outside - Potion x2 (Chest)": 4097,
+ "Baaj Temple/Outside - Hi-Potion x1 (Chest)": 4315,
+ "Baaj Temple/Inside - Flint": 4099,
+ "Baaj Temple/Inside - X-Potion x1 (Chest)": 4309,
+ "Baaj Temple/Inside - Ether (Chest)": 4102,
+ "Baaj Temple/Inside - Withered Bouquet": 4098,
+ "Baaj Temple/Inside - Hi-Potion x1 (Chest)": 4103,
+ "Baaj Temple/Inside - Klikk": 8192,
+ "Baaj Temple/Temple - Geosgaeno": 8240,
+ "Baaj Temple/Temple - Megalixir (Chest)": 4300,
+ "Baaj Temple/Temple - Mega Phoenix x4 (Chest)": 4301,
+ "Baaj Temple/Temple - Onion Knight (Chest)": 4101,
+ "Besaid/Beach - Antidote x2": 4105,
+ "Besaid/Beach - Moon Crest (Chest)": 4364,
+ "Besaid/Valley - Phoenix Down x1 (Chest)": 4186,
+ "Besaid/Valley - Hi-Potion x1 (Chest)": 4187,
+ "Besaid/Valley - Antidote x2 (Chest)": 4188,
+ "Besaid/Village - Phoenix Down x1 (Chest)": 4109,
+ "Besaid/Village - Al Bhed Primer II": 24578,
+ "Besaid/Cloister - Rod of Wisdom": 4111,
+ "Besaid/Village - Map": 4555,
+ "Besaid/Village - Brotherhood": 4304,
+ "Besaid/Super Bosses - Dark Valefor": 8194,
+ "S.S. Liki/Al Bhed Primer III": 24579,
+ "Kilika/Village - Remedy x1 (Chest)": 4113,
+ "Kilika/Village - Ether x1 (Chest)": 4114,
+ "Kilika/Village - Al Bhed Primer IV": 24580,
+ "Kilika/Woods - Mana Sphere x2 (Chest)": 4123,
+ "Kilika/Woods - Scout (Chest)": 4124,
+ "Kilika/Woods - Remedy x1 (Leader NPC)": 4389,
+ "Kilika/Woods - High Potion x1 (Guard NPC)": 4391,
+ "Kilika/Woods - Luck Sphere x1 (Chest)": 4125,
+ "Kilika/Woods - Luzu (NPC) - Antidote x4 (Before Ochu)": 4387,
+ "Kilika/Woods - Luzu (NPC) - Elixir x1 (After Ochu)": 4388,
+ "Kilika/Woods - Lord Ochu": 8197,
+ "Kilika/Woods - Sinspawn Geneaux": 8198,
+ "Kilika/Cloister - Red Armlet": 4115,
+ "S.S. Winno/Al Bhed Primer V": 24581,
+ "Luca/Stadium - Hi-Potion x2 (Chest)": 4132,
+ "Luca/Stadium - Al Bhed Primer VI": 24582,
+ "Luca/Stadium - Story Win vs. Luca Goers Reward": 4593,
+ "Luca/Stadium - Jupiter Crest (Locker)": 4367,
+ "Luca/Dock - 600 Gil (Chest)": 4129,
+ "Luca/Dock - Tidal Spear (Chest)": 4130,
+ "Luca/Dock - Phoenix Down x2 (Chest)": 4128,
+ "Luca/Dock - HP Sphere x1 (Chest)": 4131,
+ "Luca/Dock - Magic Sphere x1 (Chest)": 4302,
+ "Luca/Dock - Oblitzerator": 8199,
+ "Luca/City - 1000 Gil (Chest)": 4133,
+ "Luca/City - Al Bhed Primer VII": 24583,
+ "Luca/City - World Champion": 4189,
+ "Luca/Blitzball - Jupiter Sigil (Blitzball League)": 4340,
+ "Miihen Highroad/South - Hi-Potion (Yellow Shirt NPC)": 4407,
+ "Miihen Highroad/South - Hunters Spear (Blue Shirt NPC)": 4405,
+ "Miihen Highroad/South - Antidote x2 (Red Skirt NPC)": 4406,
+ "Miihen Highroad/South - Ice Brand (Chest)": 4134,
+ "Miihen Highroad/South - Remedy x1 (Chest)": 4140,
+ "Miihen Highroad/Central - Lv. 1 Key Sphere x1 (Purple Crusader NPC)": 4413,
+ "Miihen Highroad/Central - Antidote x4 (Woman in Yellow NPC)": 4414,
+ "Miihen Highroad/Central - 2000 gil (Chest)": 4141,
+ "Miihen Highroad/Central - 600 gil (Yellow Crusader NPC)": 4412,
+ "Miihen Highroad/Central - Ether x1 (NPC)": 4410,
+ "Miihen Highroad/Central - Hi-Potion x1 (NPC)": 4411,
+ "Miihen Highroad/Central - Eye Drops x3 (Chest)": 4142,
+ "Miihen Highroad/South - Red Ring (Crusader NPC)": 4409,
+ "Miihen Highroad/South - Soft x3 (Boy NPC)": 4408,
+ "Miihen Highroad/Agency - Lv. 1 Key Sphere x1 (NPC)": 4519,
+ "Miihen Highroad/Agency - Mega-Potion (NPC)": 4193,
+ "Miihen Highroad/Agency - Al Bhed Primer VIII": 24584,
+ "Miihen Highroad/Agency - Chocobo Eater": 8200,
+ "Miihen Highroad/North - Heat Lance (Chest)": 4138,
+ "Miihen Highroad/North - Al Bhed Primer IX": 24585,
+ "Miihen Highroad/North - Hi-Potion x2 (Chest)": 4139,
+ "Miihen Highroad/North - Thunder Blade (Chest)": 4136,
+ "Miihen Highroad/North - Scout (Chest)": 4137,
+ "Miihen Highroad/North - Fortune Sphere (Chest)": 4135,
+ "Miihen Highroad/North - Mars Crest (Chest)": 4365,
+ "Mushroom Rock Road/Entrance - Remedy x1 (Near Grey Helmet NPC)": 4417,
+ "Mushroom Rock Road/Entrance - Tough Bangle (Gray Helmet NPC)": 4415,
+ "Mushroom Rock Road/Entrance - Ether x1 (Purple Helmet NPC)": 4419,
+ "Mushroom Rock Road/Entrance - Hi-Potion x1 (Woman in Blue NPC)": 4418,
+ "Mushroom Rock Road/Entrance - Phoenix Down x2 (Blue Shirt Crusader NPC)": 4416,
+ "Mushroom Rock Road/Valley - Hi-Potion x1 (Woman NPC near Save Point)": 4420,
+ "Mushroom Rock Road/Valley - 1000 gil (Chest)": 4144,
+ "Mushroom Rock Road/Valley - Potion x10 (NPC near chest)": 4421,
+ "Mushroom Rock Road/Valley - Remedy x1 (Chest)": 4146,
+ "Mushroom Rock Road/Valley - Hi-Potion x1 (Chest)": 4187,
+ "Mushroom Rock Road/Precipice - X-Potion x1 (NPC near lift)": 4423,
+ "Mushroom Rock Road/Precipice - Serene Armlet (Chest)": 4153,
+ "Mushroom Rock Road/Precipice - Al Bhed Primer X": 24586,
+ "Mushroom Rock Road/Precipice - 400 gil (NPC near elevator)": 4422,
+ "Mushroom Rock Road/Precipice - Mega-Potion x1 (NPC)": 4424,
+ "Mushroom Rock Road/Ridge - Mega-Potion x1 (Chest)": 4148,
+ "Mushroom Rock Road/Ridge - Serene Bracer (Chest)": 4147,
+ "Mushroom Rock Road/Aftermath - Hi-Potion x1 (Chest)": 4194,
+ "Mushroom Rock Road/Aftermath - Soft x4 (Chest)": 4143,
+ "Mushroom Rock Road/Aftermath - Masamune": 4195,
+ "Mushroom Rock Road/Super Bosses - Dark Magus Sisters (Mindy)": 8237,
+ "Mushroom Rock Road/Super Bosses - Dark Magus Sisters (Sandy)": 8238,
+ "Mushroom Rock Road/Super Bosses - Dark Magus Sisters (Cindy)": 8239,
+ "Djose/Highroad - Phoenix Down x2 (Chest)": 4150,
+ "Djose/Highroad - Al Bhed Primer XI": 24587,
+ "Djose/Highroad - Soft Ring (NPC)": 4394,
+ "Djose/Highroad - Ether x1 (NPC)": 4396,
+ "Djose/Highroad - Hi-Potion x1 (NPC)": 4395,
+ "Djose/Highroad - Mega-Potion x1 (NPC)": 4397,
+ "Djose/Highroad - Variable Steel (NPC)": 4393,
+ "Djose/Highroad - Bright Bangle (Chest)": 4151,
+ "Djose/Temple - Ability Sphere x4 (Chest)": 4154,
+ "Djose/Temple - 4000 gil (Chest)": 4155,
+ "Djose/Temple - Ether x1 (Chest)": 4157,
+ "Djose/Temple - Remedy x1 (Chest)": 4158,
+ "Djose/Temple - Mega Phoenix x1 (Chest)": 4159,
+ "Djose/Cloister - Magic Sphere": 4580,
+ "Djose/Temple - Switch Hitter (Chest)": 4156,
+ "Djose/Pilgrimage Road - Halberd (NPC)": 4398,
+ "Djose/Pilgrimage Road - Potion x10 (NPC)": 4399,
+ "Djose/Pilgrimage Road - Hi-Potion x2 (NPC)": 4400,
+ "Moonflow/South - Lv. 1 Key Sphere x3 (Shelinda Chest)": 4472,
+ "Moonflow/South - X-Potion x1 (Chest)": 4293,
+ "Moonflow/South - Lv. 1 Key Sphere x3 (Benke and Biran Chest)": 4473,
+ "Moonflow/South - Magic Def Sphere x1 (Chest)": 4474,
+ "Moonflow/South - Phoenix Down x2 (Chest)": 4294,
+ "Moonflow/South - 5000 gil (Chest)": 4295,
+ "Moonflow/South - Extractor": 8204,
+ "Moonflow/North - Al Bhed Primer XII": 24588,
+ "Moonflow/North - Ether x1 (Chest)": 4296,
+ "Moonflow/North - Antidote x4 (Chest)": 4297,
+ "Moonflow/North - Mega-Potion x1 (Chest)": 4298,
+ "Guadosalam/Guadosalam - Mega-Potion x1 (Chest)": 4161,
+ "Guadosalam/Guadosalam - Al Bhed Primer XIII": 24589,
+ "Guadosalam/Guadosalam - 3000 gil (Chest)": 4160,
+ "Guadosalam/Guadosalam - Elixir x1 (Chest)": 4162,
+ "Guadosalam/Guadosalam - Hi-Potion x2 (Chest)": 4163,
+ "Guadosalam/Farplane - Lightning Marble x8 (Chest)": 4314,
+ "Guadosalam/Farplane - Brotherhood Upgrade": 24613,
+ "Guadosalam/Farplane - Venus Crest (Chest)": 4368,
+ "Thunder Plains/South - Phoenix Down x2 (Chest)": 4294,
+ "Thunder Plains/South - Hi-Potion x2 (Chest)": 4275,
+ "Thunder Plains/South - 5000 gil (Chest)": 4295,
+ "Thunder Plains/South - Water Ball (Chest)": 4277,
+ "Thunder Plains/Agency - Al Bhed Primer XIV": 24590,
+ "Thunder Plains/Agency - Yellow Shield (Ground Item)": 4441,
+ "Thunder Plains/North - X-Potion x1 (Chest)": 4278,
+ "Thunder Plains/North - Ether x1 (Chest)": 4296,
+ "Thunder Plains/North - 2000 gil (Chest)": 4281,
+ "Thunder Plains/North - Remedy x1 (Chest)": 4280,
+ "Thunder Plains/Super Bosses - Dark Ixion": 8205,
+ "Macalania/Woods - Sleepy Cait Sith (Chest)": 4165,
+ "Macalania/Woods - 2000 gil (Chest)": 4164,
+ "Macalania/Woods - Phoenix Down x3 (Chest)": 4166,
+ "Macalania/Woods - Remedy x1 (Chest)": 4169,
+ "Macalania/Woods - Al Bhed Primer XV": 24591,
+ "Macalania/Woods - Spherimorph": 8206,
+ "Macalania/Woods - Lucid Ring (Chest)": 4171,
+ "Macalania/Lake Agency - Al Bhed Primer XVI": 24592,
+ "Macalania/Lake Agency - Crawler": 8207,
+ "Macalania/Lake Agency - 4000 gil (Chest)": 4172,
+ "Macalania/Lake Road - Mega-Potion x1 (Chest)": 4174,
+ "Macalania/Lake Road - 400 gil (Al Bhed Soldier NPC)": 4401,
+ "Macalania/Lake Road - Lv. 1 Key Sphere x1 (Chest)": 4173,
+ "Macalania/Temple - Shell Targe (Tromell)": 4181,
+ "Macalania/Temple - 5000 gil (Chest)": 4179,
+ "Macalania/Temple - X-Potion x2 (Chest)": 4180,
+ "Macalania/Temple - Remedy x2 (Chest)": 4183,
+ "Macalania/Temple - Hi-Potion x2 (NPC)": 4404,
+ "Macalania/Temple - Ether x1 (Man Sitting NPC)": 4403,
+ "Macalania/Temple - Elixir x1 (Man Sitting NPC)": 4402,
+ "Macalania/Temple - Phoenix Down x3 (Chest)": 4182,
+ "Macalania/Temple - Seymour & Anima": 8208,
+ "Macalania/Cloister - Luck Sphere": 4581,
+ "Macalania/Lake Agency - Wendigo": 8209,
+ "Macalania/Lake Bottom - Lv. 2 Key Sphere (Chest)": 4176,
+ "Macalania/Lake Bottom - Avenger (Chest)": 4175,
+ "Macalania/Super Bosses - Dark Shiva": 8210,
+ "Celestial Flower/Celestial Mirror": 4207,
+ "Celestial Flower/Auron - Masamune Crest Updgrade": 24618,
+ "Celestial Flower/Auron - Masamune Sigil Updgrade": 24619,
+ "Celestial Flower/Kimahri - Spirit Lance Crest Updgrade": 24620,
+ "Celestial Flower/Kimahri - Spirit Lance Sigil Updgrade": 24621,
+ "Celestial Flower/Lulu - Onion Knight Crest Updgrade": 24624,
+ "Celestial Flower/Lulu - Onion Knight Sigil Updgrade": 24625,
+ "Celestial Flower/Rikku - Godhand Crest Updgrade": 24626,
+ "Celestial Flower/Rikku - Godhand Sigil Updgrade": 24627,
+ "Celestial Flower/Tidus - Caladbolg Crest Updgrade": 24614,
+ "Celestial Flower/Tidus - Caladbolg Sigil Updgrade": 24615,
+ "Celestial Flower/Wakka - World Champion Crest Updgrade": 24622,
+ "Celestial Flower/Wakka - World Champion Sigil Updgrade": 24623,
+ "Celestial Flower/Yuna - Nirvana Crest Updgrade": 24616,
+ "Celestial Flower/Yuna - Nirvana Sigil Updgrade": 24617,
+ "Bikanel/Bikanel East - Remedy x4 (Chest)": 4442,
+ "Bikanel/Bikanel East - Al Bhed Primer I": 24577,
+ "Bikanel/Bikanel East - Al Bhed Primer III": 24579,
+ "Bikanel/Bikanel East - Al Bhed Primer V": 24581,
+ "Bikanel/Bikanel East - Ether x2 (Chest)": 4443,
+ "Bikanel/Bikanel East - Al Bhed Primer XIV": 24590,
+ "Bikanel/Bikanel East - Hi-Potion x4 (Chest)": 4444,
+ "Bikanel/Bikanel Central - X-Potion x2 (Chest)": 4446,
+ "Bikanel/Bikanel Central - Mega-Potion x2 (Chest)": 4445,
+ "Bikanel/Bikanel Central - Al Bhed Potion x8": 4308,
+ "Bikanel/Bikanel Central - Al Bhed Primer XVIII": 24594,
+ "Bikanel/Bikanel Central - Elixir x1 (Chest)": 4448,
+ "Bikanel/Bikanel Central - Lv. 2 Key Sphere x1 (Chest)": 4450,
+ "Bikanel/Bikanel Central - 10000 gil (Chest)": 4449,
+ "Bikanel/Bikanel Central - Al Bhed Primer XVII": 24593,
+ "Bikanel/Bikanel West - Megalixir x3 (Chest)": 4454,
+ "Bikanel/Bikanel West - Teleport Sphere x2 (Chest)": 4455,
+ "Bikanel/Bikanel West - Hi-Potion x8 (Chest)": 4451,
+ "Bikanel/Bikanel West - Mercury Crest (Chest)": 4369,
+ "Bikanel/Bikanel West - Mega-Potion x3 (Chest)": 4452,
+ "Bikanel/Bikanel West - X-Potion x2 (Chest)": 4453,
+ "Bikanel/Bikanel West - Mercury Sigil (Cactuar Village)": 4375,
+ "Bikanel/Super Bosses - Dark Ifrit": 8211,
+ "Home/Home - Hi-Potion x2 (NPC on Ground)": 4463,
+ "Home/Home - Al Bhed Primer XIX": 24595,
+ "Home/Home - Al Bhed Potion x6 (Chest)": 4456,
+ "Home/Home - Al Bhed Primer XXI": 24597,
+ "Home/Home - Special Sphere x1 (Al Bhed Password Chest)": 4492,
+ "Home/Home - Skill Sphere x1 (Al Bhed Quiz Chest)": 4491,
+ "Home/Home - Al Bhed Potion x4 (Chest)": 4457,
+ "Home/Home - Al Bhed Primer XX": 24596,
+ "Home/Home - Friend Sphere x1 (Al Bhed Vocabulary Chest)": 4493,
+ "Airship/Airship - Al Bhed Potion x4 (NPC)": 4471,
+ "Airship/Airship - Complete Al Bhed Primers": 4501,
+ "Airship/Airship - Evrae": 8212,
+ "Airship/Airship - Sin Left Fin": 8213,
+ "Airship/Airship - Sin Right Fin": 8214,
+ "Airship/Airship - Sinspawn Genais and Core": 8215,
+ "Airship/Airship - Overdrive Sin": 8216,
+ "Airship/Super Bosses - Penance": 8217,
+ "Bevelle/Cloister - Al Bhed Primer XXII": 24598,
+ "Bevelle/Cloister - HP Sphere": 4313,
+ "Bevelle/Cloister - Knight Lance (Chest)": 4198,
+ "Bevelle/Via Purifico - Mega-Potion x1 (Chest)": 4206,
+ "Bevelle/Via Purifico - Wht Magic Sphere x1 (Chest)": 4201,
+ "Bevelle/Via Purifico - Elixir x1 (Chest)": 4200,
+ "Bevelle/Via Purifico - Blk Magic Sphere (Chest)": 4205,
+ "Bevelle/Via Purifico - Skill Sphere x1 (Chest)": 4202,
+ "Bevelle/Via Purifico - 10000 gil (Chest)": 4203,
+ "Bevelle/Via Purifico - Lucid Ring (Chest)": 4204,
+ "Bevelle/Via Purifico - Isaaru": 8218,
+ "Bevelle/Underwater - Evrae Altana": 8219,
+ "Bevelle/Underwater - Rematch (Chest)": 4197,
+ "Bevelle/Underwater - Avenger (Chest)": 4196,
+ "Bevelle/Highbridge - Seymour Natus": 8220,
+ "Calm Lands/Plains - 5000 gil (Chest)": 4212,
+ "Calm Lands/Plains - 10000 gil (Chest)": 4211,
+ "Calm Lands/Plains - Lv. 2 Key Sphere x1 (Chest)": 4213,
+ "Calm Lands/Plains - Al Bhed Primer XXIII": 24599,
+ "Calm Lands/Plains - Defender X": 8221,
+ "Calm Lands/Plains - Rusty Sword": 4214,
+ "Calm Lands/Chocobo - Wobbly Chocobo Minigame Reward": 4433,
+ "Calm Lands/Chocobo - Dodger Chocobo Minigame Reward": 4434,
+ "Calm Lands/Chocobo - Hyper Dodger Chocobo Minigame Reward": 4435,
+ "Calm Lands/Chocobo - Catcher Chocobo Minigame Reward": 4436,
+ "Calm Lands/Chocobo - Sun Sigil (Chocobo Catcher 0:00)": 4370,
+ "Calm Lands/Plains - Caladbolg": 4210,
+ "Remiem Temple/Al Bhed Primer XXIV": 24600,
+ "Remiem Temple/Cloudy Mirror (Chocobo Race)": 4272,
+ "Remiem Temple/Belgemine - Valefor Fight": 4475,
+ "Remiem Temple/Belgemine - Ifrit Fight": 4477,
+ "Remiem Temple/Belgemine - Ixion Fight": 4479,
+ "Remiem Temple/Belgemine - Shiva Fight": 4481,
+ "Remiem Temple/Belgemine - Bahamut Fight (Flower Scepter)": 4430,
+ "Remiem Temple/Belgemine - Yojimbo Fight": 4484,
+ "Remiem Temple/Belgemine - Anima Fight": 4486,
+ "Remiem Temple/Belgemine - Magus Sisters Fight": 4488,
+ "Remiem Temple/Belgemine - Moon Sigil (Send Belgemine)": 4371,
+ "Monster Arena/Area Conquest (Calm Lands) - Nirvana": 4209,
+ "Monster Arena/Area Conquest (Mt. Gagazet) - Blossom Crown": 4530,
+ "Monster Arena/Monster Creations x10 - Mars Sigil": 4372,
+ "Cavern of the Stolen Fayth/Al Bhed Primer XXV": 24601,
+ "Cavern of the Stolen Fayth/Super Bosses - Dark Yojimbo": 8223,
+ "Mt. Gagazet/Outside - Biran and Yenke": 8224,
+ "Mt. Gagazet/Outside - 20000 gil (Chest)": 4224,
+ "Mt. Gagazet/Outside - Mega-Potion x2 (Chest)": 4225,
+ "Mt. Gagazet/Outside - Defending Bracer (Chest)": 4226,
+ "Mt. Gagazet/Outside - Lv. 4 Key Sphere x1 (Chest)": 4227,
+ "Mt. Gagazet/Outside - HP Sphere x1 (Chest)": 4228,
+ "Mt. Gagazet/Outside - Seymour Flux": 8225,
+ "Mt. Gagazet/Outside - Saturn Crest (Chest)": 4366,
+ "Mt. Gagazet/Inside - Lv. 1 Key Sphere x1 (Chest)": 4232,
+ "Mt. Gagazet/Inside - Fortune Sphere x1 (Chest)": 4233,
+ "Mt. Gagazet/Inside - Return Sphere x1 (Chest)": 4234,
+ "Mt. Gagazet/Inside - Recovery Ring (Chest)": 4235,
+ "Mt. Gagazet/Inside - Pep Talk (Chest)": 4231,
+ "Mt. Gagazet/Inside - Sanctuary Keeper": 8227,
+ "Mt. Gagazet/Super Bosses - Dark Anima": 8226,
+ "Zanarkand Ruins/Overpass - Fortune Sphere x1 (Chest)": 4241,
+ "Zanarkand Ruins/Overpass - Spiritual Targe (Chest)": 4242,
+ "Zanarkand Ruins/Dome - 10000 gil (Chest)": 4243,
+ "Zanarkand Ruins/Dome - Friend Sphere x1 (Chest)": 4244,
+ "Zanarkand Ruins/Dome - Lv. 3 Key Sphere x1 (Chest)": 4245,
+ "Zanarkand Ruins/Dome - Luck Sphere x1 (Chest)": 4246,
+ "Zanarkand Ruins/Cloister -Magistral Rod ": 4305,
+ "Zanarkand Ruins/Cloister - Spectral Keeper": 8228,
+ "Zanarkand Ruins/Cloister - Yunalesca": 8229,
+ "Zanarkand Ruins/Cloister - Sun Crest (Chest)": 4363,
+ "Zanarkand Ruins/Super Bosses - Dark Bahamut": 8230,
+ "Inside Sin/Sea of Sorrow - Phantom Ring (Chest)": 4262,
+ "Inside Sin/Sea of Sorrow - Special Sphere x1 (Chest)": 4263,
+ "Inside Sin/Sea of Sorrow - Elixir x1 (Chest)": 4259,
+ "Inside Sin/Sea of Sorrow - Wizard Lance (Chest)": 4260,
+ "Inside Sin/Sea of Sorrow - Lv. 3 Key Sphere x1 (Chest)": 4261,
+ "Inside Sin/Sea of Sorrow - Seymour Omnis": 8231,
+ "Inside Sin/City of Dying Dreams - Lv. 4 Key Sphere x1 (Chest)": 4264,
+ "Inside Sin/City of Dying Dreams - Four-on-One (Chest)": 4265,
+ "Inside Sin/City of Dying Dreams - Defending Bracer (Chest)": 4266,
+ "Inside Sin/City of Dying Dreams - 20000 gil (Chest)": 4267,
+ "Inside Sin/City of Dying Dreams - Megalixir x1 (Chest)": 4270,
+ "Inside Sin/City of Dying Dreams - HP Sphere x1 (Chest)": 4268,
+ "Inside Sin/City of Dying Dreams - Defense Sphere x1 (Chest)": 4269,
+ "Inside Sin/City of Dying Dreams - Laevatein (Chest)": 4271,
+ "Omega Ruins/Al Bhed Primer XXVI": 24602
+}
+
+tracker_world = {
+ "map_page_maps": ["maps/maps.json"],
+ "map_page_locations": [
+ "locations/Aeons.json",
+ "locations/Airship.json",
+ "locations/Al Bhed Primers.json",
+ "locations/Al Bhed Ship.json",
+ "locations/Baaj Temple.json",
+ "locations/Besaid.json",
+ "locations/Bevelle.json",
+ "locations/Bikanel.json",
+ "locations/Calm Lands.json",
+ "locations/Cavern of the Stolen Fayth.json",
+ "locations/Celestial.json",
+ "locations/Djose.json",
+ "locations/Guadosalam.json",
+ "locations/Inside Sin.json",
+ "locations/Kilika.json",
+ "locations/Luca.json",
+ "locations/Macalania.json",
+ "locations/Miihen Highroad.json",
+ "locations/Monster Arena.json",
+ "locations/Moonflow.json",
+ "locations/Mt. Gagazet.json",
+ "locations/Mushroom Rock Road.json",
+ "locations/Omega Ruins.json",
+ "locations/Overdrives.json",
+ "locations/Remiem Temple.json",
+ "locations/S.S. Liki.json",
+ "locations/S.S. Winno.json",
+ "locations/Spira.json",
+ "locations/Super Bosses.json",
+ "locations/Thunder Plains.json",
+ "locations/Zanarkand.json",
+ ],
+ "map_page_layouts": ["layouts/maps.json"],
+ #"map_page_groups": ["Spira"],
+ #"map_page_setting_key": "Slot:{player}:Current Map",
+ "map_page_index": lambda data: 55, # Temporarily hardcoded to Spira
+ "external_pack_key": "ut_poptracker_path",
+ "poptracker_name_mapping": poptracker_name_mapping
+ }
+
+
+
diff --git a/worlds/generic/__init__.py b/worlds/generic/__init__.py
index fa53f31f7c1b..c4aef4f67bb0 100644
--- a/worlds/generic/__init__.py
+++ b/worlds/generic/__init__.py
@@ -1,4 +1,5 @@
from typing import NamedTuple, Union
+from typing_extensions import deprecated
import logging
from BaseClasses import Item, Tutorial, ItemClassification
@@ -16,7 +17,7 @@ class GenericWeb(WebWorld):
'A guide detailing the commands available to the user when participating in an Archipelago session.',
'English', 'commands_en.md', 'commands/en', ['jat2980', 'Ijwu'])
mac = Tutorial('Archipelago Setup Guide for Mac', 'A guide detailing how to run Archipelago clients on macOS.',
- 'English', 'mac_en.md','mac/en', ['Bicoloursnake'])
+ 'English', 'mac_en.md','mac/en', ['Bicoloursnake', 'silasary'])
plando = Tutorial('Archipelago Plando Guide', 'A guide to understanding and using plando for your game.',
'English', 'plando_en.md', 'plando/en', ['alwaysintreble', 'Alchav'])
setup = Tutorial('Getting Started',
@@ -49,7 +50,8 @@ def create_item(self, name: str) -> Item:
return Item(name, ItemClassification.filler, -1, self.player)
raise InvalidItemError(name)
-
+@deprecated("worlds.generic.PlandoItem is deprecated and will be removed in the next version. "
+ "Use Options.PlandoItem(s) instead.")
class PlandoItem(NamedTuple):
item: str
location: str
diff --git a/worlds/generic/docs/mac_en.md b/worlds/generic/docs/mac_en.md
index b06593f5011d..4db48d2abd3a 100644
--- a/worlds/generic/docs/mac_en.md
+++ b/worlds/generic/docs/mac_en.md
@@ -19,14 +19,17 @@ It is generally recommended that you use a virtual environment to run python bas
2. Run the command `python3 -m venv venv` to create a virtual environment. Running this command will create a new directory at the specified path, so make sure that path is clear for a new directory to be created.
3. Run the command `source venv/bin/activate` to activate the virtual environment.
4. If you want to exit the virtual environment, run the command `deactivate`.
+## Building the App
+1. Run the command `python3 setup.py bdist_mac` to build an App Bundle. This step may take a while.
+2. Back in Finder, go to the new `build` subdirectory, and copy the Archipelago app you just made to your Applications directory.
## Steps to Run the Clients
-1. Run the command `python3 Launcher.py`.
+1. Launch the Archipelago Launcher
2. If your game doesn't have a patch file, just click the desired client in the right side column.
3. If your game does have a patch file, click the 'Open Patch' button and navigate to your patch file (the filename extension will look something like apsm, aplttp, apsmz3, etc.).
4. If the patching process needs a rom, but cannot find it, it will ask you to navigate to your legally obtained rom.
5. Your client should now be running and rom created (where applicable).
## Additional Steps for SNES Games
-1. If using RetroArch, the instructions to set up your emulator [here in the Link to the Past setup guide](https://archipelago.gg/tutorial/A%20Link%20to%20the%20Past/multiworld/en) also work on the macOS version of RetroArch.
+1. If using RetroArch, the instructions to set up your emulator [here in the Link to the Past setup guide](/tutorial/A%20Link%20to%20the%20Past/multiworld/en) also work on the macOS version of RetroArch.
2. Double click on the SNI tar.gz download to extract the files to an SNI directory. If it isn't already, rename this directory to SNI to make some steps easier.
3. Move the SNI directory out of the downloads directory, preferably into the Archipelago directory created earlier.
4. If the SNI directory is correctly named and moved into the Archipelago directory, it should auto run with the SNI client. If it doesn't automatically run, open up the SNI directory and run the SNI executable file manually.
diff --git a/worlds/generic/docs/retroarch-network-commands-en.png b/worlds/generic/docs/retroarch-network-commands-en.png
new file mode 100644
index 000000000000..66103ff61c65
Binary files /dev/null and b/worlds/generic/docs/retroarch-network-commands-en.png differ
diff --git a/worlds/generic/docs/retroarch-network-commands-fr.png b/worlds/generic/docs/retroarch-network-commands-fr.png
new file mode 100644
index 000000000000..8944694f49ab
Binary files /dev/null and b/worlds/generic/docs/retroarch-network-commands-fr.png differ
diff --git a/worlds/hk/docs/setup_en.md b/worlds/hk/docs/setup_en.md
index d49a4002e4b7..6728e38d7394 100644
--- a/worlds/hk/docs/setup_en.md
+++ b/worlds/hk/docs/setup_en.md
@@ -5,6 +5,8 @@
* A legal copy of Hollow Knight.
* Steam, Gog, and Xbox Game Pass versions of the game are supported.
* Windows, Mac, and Linux (including Steam Deck) are supported.
+
+**Do NOT** install BepInEx, it is not required and is incompatible with most mods. Archipelago, along with the majority of mods use custom tooling pre-dating BepInEx, and they are only available through Lumafly and similar installers rather than sites like Nexus Mods.
## Installing the Archipelago Mod using Lumafly
1. Launch Lumafly and ensure it locates your Hollow Knight installation directory.
diff --git a/worlds/hk/docs/setup_es.md b/worlds/hk/docs/setup_es.md
index 13628c401905..5a7667665bf1 100644
--- a/worlds/hk/docs/setup_es.md
+++ b/worlds/hk/docs/setup_es.md
@@ -5,6 +5,10 @@
* Tener una copia legal de Hollow Knight.
* Las versiones de Steam, GOG y Xbox Game Pass son compatibles
* Las versiones de Windows, Mac y Linux (Incluyendo Steam Deck) son compatibles
+
+**NO** instales BepInEx, **no** es necesario y es incompatible con varios mods. Archipelago (y la mayoría de los mods)
+usan herramientas más antiguas que BepInEx, que solo están disponibles por medio de instaladores de mods como Lumafly y
+similares, en vez de sitios web como Nexus Mods.
## Instalación del mod de Archipelago con Lumafly
1. Ejecuta Lumafly y asegurate de localizar la carpeta de instalación de Hollow Knight
@@ -61,4 +65,4 @@ de Archipelago para generar un YAML usando una interfaz gráfica.
## Consejos y otros comandos
Mientras juegas en un multiworld, puedes interactuar con el servidor usando varios comandos listados en la
[guía de comandos](/tutorial/Archipelago/commands/en). Puedes usar el Cliente de Texto Archipelago para hacer esto,
-que está incluido en la última versión del [software de Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest).
\ No newline at end of file
+que está incluido en la última versión del [software de Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest).
diff --git a/worlds/hk/docs/setup_pt_br.md b/worlds/hk/docs/setup_pt_br.md
index 14e54db1315c..6509d27a2c06 100644
--- a/worlds/hk/docs/setup_pt_br.md
+++ b/worlds/hk/docs/setup_pt_br.md
@@ -5,6 +5,10 @@
* Uma cópia legal de Hollow Knight.
* Versões Steam, Gog, e Xbox Game Pass do jogo são suportadas.
* Windows, Mac, e Linux (incluindo Steam Deck) são suportados.
+
+**NÃO** instale o BepInEx, ele **não** é necessário e é incompatível com vários mods. O Archipelago (e a maioria dos mods)
+usam ferramentas mais antigas do que o BepInEx, disponíveis apenas a partir de gerenciadores como o Lumafly e semelhantes,
+ao invés de sites como o Nexus Mods.
## Instalando o mod Archipelago Mod usando Lumafly
1. Abra o Lumafly e confirme que ele localizou sua pasta de instalação do Hollow Knight.
diff --git a/worlds/jakanddaxter/client.py b/worlds/jakanddaxter/client.py
index fa2aea5495ef..7cb8cb8ea95f 100644
--- a/worlds/jakanddaxter/client.py
+++ b/worlds/jakanddaxter/client.py
@@ -601,7 +601,7 @@ async def run_game(ctx: JakAndDaxterContext):
f"Please check your host.yaml file.\n"
f"If the value of 'jakanddaxter_options > auto_detect_root_directory' is true, verify that OpenGOAL "
f"is installed properly.\n"
- f"If it is false, check the value of 'jakanddaxter_options > root_directory'."
+ f"If it is false, check the value of 'jakanddaxter_options > root_directory'. "
f"Verify it is a valid existing path, and all backslashes have been replaced with forward slashes.")
ctx.on_log_error(logger, msg)
return
diff --git a/worlds/jakanddaxter/docs/setup_en.md b/worlds/jakanddaxter/docs/setup_en.md
index 9cd892a9b2c7..379b4eecf214 100644
--- a/worlds/jakanddaxter/docs/setup_en.md
+++ b/worlds/jakanddaxter/docs/setup_en.md
@@ -9,7 +9,7 @@ At this time, this method of setup works on Windows only, but Linux support is a
## Installation via OpenGOAL Launcher
-**You must set up a vanilla installation of Jak and Daxter before you can install mods for it.**
+**You must set up a vanilla installation of Jak and Daxter before you can install mods for it.** These instructions will work on both NTSC and PAL versions of the game.
- Follow the installation process for the official OpenGOAL Launcher. See [here](https://opengoal.dev/docs/usage/installation).
- Follow the setup process for adding mods to the OpenGOAL Launcher. See [here](https://jakmods.dev/).
@@ -18,45 +18,7 @@ At this time, this method of setup works on Windows only, but Linux support is a
- Click `Features` in the bottom right corner, then click `Mods`.
- Under `Available Mods`, click `ArchipelaGOAL`. The mod should begin installing. When it is done, click `Continue` in the bottom right corner.
- **DO NOT PLAY AN ARCHIPELAGO GAME THROUGH THE OPENGOAL LAUNCHER.** The Archipelago Client should handle everything for you.
-
-### For NTSC versions of the game, follow these steps.
-
-- Run the OpenGOAL Launcher (if you had it open before, close it and reopen it).
-- Click the Jak and Daxter logo on the left sidebar.
-- Click `Features` in the bottom right corner, then click `Mods`, then under `Installed Mods`, click `ArchipelaGOAL`.
-- In the bottom right corner, click `Advanced`, then click `Compile`.
-
-### For PAL versions of the game, follow these steps.
-
-PAL versions of the game seem to require additional troubleshooting/setup in order to work properly.
-Below are some instructions that may help.
-If you see `-- Compilation Error! --` after pressing `Compile` or Launching the ArchipelaGOAL mod, try these steps.
-
-- Remove these folders if you have them:
- - `/iso_data`
- - `/iso_data`
- - `/data/iso_data`
-- Place your Jak1 ISO in `` and rename it to `JakAndDaxter.iso`
-- Type `cmd` in Windows search, right click `Command Prompt`, and pick `Run as Administrator`
-- Run `cd `
-- Then run `.\extractor.exe --extract --extract-path .\data\iso_data "JakAndDaxter.iso"`
- - This command should end by saying `Uses Decompiler Config Version - ntsc_v1` or `... - pal`. Take note of this message.
-- If you saw `ntsc_v1`:
- - In cmd, run `.\decompiler.exe data\decompiler\config\jak1\jak1_config.jsonc --version "ntsc_v1" data\iso_data data\decompiler_out`
-- If you saw `pal`:
- - Rename `\data\iso_data\jak1` to `jak1_pal`
- - Back in cmd, run `.\decompiler.exe data\decompiler\config\jak1\jak1_config.jsonc --version "pal" data\iso_data data\decompiler_out`
- - Rename `\data\iso_data\jak1_pal` back to `jak1`
- - Rename `\data\decompiler_out\jak1_pal` back to `jak1`
-- Open a **brand new** console window and launch the compiler:
- - `cd `
- - `.\goalc.exe --user-auto --game jak1`
- - From the compiler (in the same window): `(mi)`. This should compile the game. **Note that the parentheses are important.**
- - **Don't close this first terminal, you will need it at the end.**
-- Then, open **another brand new** console window and execute the game:
- - `cd `
- - `.\gk.exe -v --game jak1 -- -boot -fakeiso -debug`
-- Finally, **from the first console still in the GOALC compiler**, connect to the game: `(lt)`.
+- Once the mod is installed, in the bottom right corner, click `Advanced`, then click `Compile`.
## Updates and New Releases via OpenGOAL Launcher
diff --git a/worlds/jakanddaxter/test/test_trades.py b/worlds/jakanddaxter/test/test_trades.py
index 92552f9d0eb6..97b7c2a220cf 100644
--- a/worlds/jakanddaxter/test/test_trades.py
+++ b/worlds/jakanddaxter/test/test_trades.py
@@ -7,7 +7,9 @@ class TradesCostNothingTest(JakAndDaxterTestBase):
"global_orbsanity_bundle_size": 10,
"citizen_orb_trade_amount": 0,
"oracle_orb_trade_amount": 0,
- "start_inventory": {"Power Cell": 100},
+ "fire_canyon_cell_count": 0,
+ "mountain_pass_cell_count": 0,
+ "lava_tube_cell_count": 0,
}
def test_orb_items_are_filler(self):
@@ -26,7 +28,9 @@ class TradesCostEverythingTest(JakAndDaxterTestBase):
"global_orbsanity_bundle_size": 10,
"citizen_orb_trade_amount": 120,
"oracle_orb_trade_amount": 150,
- "start_inventory": {"Power Cell": 100},
+ "fire_canyon_cell_count": 0,
+ "mountain_pass_cell_count": 0,
+ "lava_tube_cell_count": 0,
}
def test_orb_items_are_progression(self):
diff --git a/worlds/kdl3/presets.py b/worlds/kdl3/presets.py
index 491ad9dca993..f07973e6b0d1 100644
--- a/worlds/kdl3/presets.py
+++ b/worlds/kdl3/presets.py
@@ -7,7 +7,7 @@
"game_language": "random",
"goal": "random",
"goal_speed": "random",
- "total_heart_stars": "random",
+ "max_heart_stars": "random",
"heart_stars_required": "random",
"filler_percentage": "random",
"trap_percentage": "random",
@@ -34,7 +34,7 @@
beginner = {
"goal": "zero",
"goal_speed": "normal",
- "total_heart_stars": 50,
+ "max_heart_stars": 50,
"heart_stars_required": 30,
"filler_percentage": 25,
"trap_percentage": 0,
diff --git a/worlds/kh1/Client.py b/worlds/kh1/Client.py
index 9d3889d6ceb0..1f6dfc086ccf 100644
--- a/worlds/kh1/Client.py
+++ b/worlds/kh1/Client.py
@@ -134,13 +134,13 @@ def on_package(self, cmd: str, args: dict):
os.makedirs(self.game_communication_path)
for ss in self.checked_locations:
filename = f"send{ss}"
- with open(os.path.join(self.game_communication_path, filename), 'w') as f:
+ with open(os.path.join(self.game_communication_path, filename), 'w', encoding='utf-8') as f:
f.close()
# Handle Slot Data
self.slot_data = args['slot_data']
for key in list(args['slot_data'].keys()):
- with open(os.path.join(self.game_communication_path, key + ".cfg"), 'w') as f:
+ with open(os.path.join(self.game_communication_path, key + ".cfg"), 'w', encoding='utf-8') as f:
f.write(str(args['slot_data'][key]))
f.close()
if key == "remote_location_ids":
@@ -161,7 +161,7 @@ def on_package(self, cmd: str, args: dict):
found = True
if not found:
if (NetworkItem(*item).player == self.slot and (NetworkItem(*item).location in self.remote_location_ids) or (NetworkItem(*item).location < 0)) or NetworkItem(*item).player != self.slot:
- with open(os.path.join(self.game_communication_path, item_filename), 'w') as f:
+ with open(os.path.join(self.game_communication_path, item_filename), 'w', encoding='utf-8') as f:
f.write(str(NetworkItem(*item).item) + "\n" + str(NetworkItem(*item).location) + "\n" + str(NetworkItem(*item).player))
f.close()
self.item_num += 1
@@ -170,7 +170,7 @@ def on_package(self, cmd: str, args: dict):
if "checked_locations" in args:
for ss in self.checked_locations:
filename = f"send{ss}"
- with open(os.path.join(self.game_communication_path, filename), 'w') as f:
+ with open(os.path.join(self.game_communication_path, filename), 'w', encoding='utf-8') as f:
f.close()
if cmd in {"PrintJSON"} and "type" in args:
@@ -195,7 +195,7 @@ def on_package(self, cmd: str, args: dict):
filename = "msg"
if message != "":
if not os.path.exists(self.game_communication_path + "/" + filename):
- with open(os.path.join(self.game_communication_path, filename), 'w') as f:
+ with open(os.path.join(self.game_communication_path, filename), 'w', encoding='utf-8') as f:
f.write(message)
f.close()
if args["type"] == "ItemCheat":
@@ -207,7 +207,7 @@ def on_package(self, cmd: str, args: dict):
filename = "msg"
message = "Received " + itemName + "\nfrom server"
if not os.path.exists(self.game_communication_path + "/" + filename):
- with open(os.path.join(self.game_communication_path, filename), 'w') as f:
+ with open(os.path.join(self.game_communication_path, filename), 'w', encoding='utf-8') as f:
f.write(message)
f.close()
@@ -218,7 +218,7 @@ def on_deathlink(self, data: dict[str, object]):
logger.info(f"DeathLink: {text}")
else:
logger.info(f"DeathLink: Received from {data['source']}")
- with open(os.path.join(self.game_communication_path, 'dlreceive'), 'w') as f:
+ with open(os.path.join(self.game_communication_path, 'dlreceive'), 'w', encoding='utf-8') as f:
f.write(str(int(data["time"])))
f.close()
diff --git a/worlds/kh1/Options.py b/worlds/kh1/Options.py
index 879ea4c331f3..221a1818c67d 100644
--- a/worlds/kh1/Options.py
+++ b/worlds/kh1/Options.py
@@ -589,7 +589,7 @@ class OrichalcumInPool(Range):
You need 17 to synth every recipe that requires it.
"""
- display_name = "Mythril In Pool"
+ display_name = "Orichalcum In Pool"
default = 20
range_start = 17
range_end = 30
diff --git a/worlds/kh1/__init__.py b/worlds/kh1/__init__.py
index fbdc99206a2f..593a612bbdbb 100644
--- a/worlds/kh1/__init__.py
+++ b/worlds/kh1/__init__.py
@@ -30,7 +30,7 @@ class KH1Web(WebWorld):
theme = "ocean"
tutorials = [Tutorial(
"Multiworld Setup Guide",
- "A guide to setting up the Kingdom Hearts Randomizer software on your computer."
+ "A guide to setting up the Kingdom Hearts Randomizer software on your computer. "
"This guide covers single-player, multiworld, and related software.",
"English",
"kh1_en.md",
diff --git a/worlds/kh1/docs/kh1_en.md b/worlds/kh1/docs/kh1_en.md
index f6b6a640673a..ad534b6f6dae 100644
--- a/worlds/kh1/docs/kh1_en.md
+++ b/worlds/kh1/docs/kh1_en.md
@@ -1,6 +1,6 @@
# Kingdom Hearts Archipelago Randomizer Setup Guide
-
Required software
+## Required software
- KINGDOM HEARTS -HD 1.5+2.5 ReMIX- from the [Epic Games Store](https://store.epicgames.com/en-US/discover/kingdom-hearts) or [Steam](https://store.steampowered.com/app/2552430/KINGDOM_HEARTS_HD_1525_ReMIX/)
@@ -10,9 +10,9 @@
- The latest release of [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) for the ArchipelagoKH1Client.exe
-
Setting up the required software
+## Setting up the required software
-
OpenKH
+### OpenKH
- Extract the OpenKH files to a directory of your choosing.
- When prompted for game edition, choose PC Release, select which platform you're using (EGS or Steam), navigate to your `Kingdom Hearts I.5 + II.5` installation folder in the path box and click `Next`.
@@ -21,11 +21,11 @@
- Extract the data for KH1.
- Click `Finish`
-
Kingdom Hearts 1FM Randomizer Software
+### Kingdom Hearts 1FM Randomizer Software
- Extract the Kingdom Hearts 1FM Randomizer Software files in a directory of your choosing.
-
Obtaining and using the seed zip
+## Obtaining and using the seed zip
- When you generate a game you will see a download link for a KH1 .zip seed on the room page.
- After downloading this zip, open `mod_generator.exe` in your Kingdom Hearts 1FM Randomizer Software folder.
@@ -38,7 +38,7 @@
- Ensure this mod is checked, then, if you want to play right away, click `Mod Loader` at the top.
- Click `Build and Run`. Your modded game should now open.
-
Connecting to your multiworld via the KH1 Client
+## Connecting to your multiworld via the KH1 Client
- Once your game is being hosted, open `ArchipelagoLauncher.exe`.
- Find `KH1 Client` and open it.
@@ -48,52 +48,68 @@
- After typing your slot name, press enter.
- If all is well, you are now connected.
-
FAQ
+## FAQ
-
The client did not confirm connection to the game, is that normal?
+### The client did not confirm connection to the game, is that normal?
Yes, the game and client communicate via a game communication path set up in your in your `%AppData%` folder, and therefore don't need to establish a socket connection.
-
I am not sending or receiving items.
+### I am not sending or receiving items.
Check out this [troubleshooting guide](https://docs.google.com/document/d/1oAXxJWrNeqSL-tkB_01bLR0eT0urxz2FBo4URpq3VbM/edit?usp=sharing)
-
Why aren't the evidence boxes spawning in Wonderland?
+### Why aren't the evidence boxes spawning in Wonderland?
You'll need to find `Footprints` in your multiworld.
-
Why won't Phil let me start the Prelims?
+### Why won't Phil let me start the Prelims?
You'll need to find `Entry Pass` in the multiworld.
-
Why aren't the slides spawning in Deep Jungle?
+### Why aren't the slides spawning in Deep Jungle?
You'll need to find `Slides` in the multiworld.
-
Why can't I make progress in Atlantica?
+### Why can't I make progress in Atlantica?
You'll need to find `Crystal Trident` in the multiworld.
-
Why won't the doctor let me progress in Halloween Town?
+### Why won't the doctor let me progress in Halloween Town?
You'll need to find either `Forget-Me-Not` or `Jack-in-the-Box` in the multiworld.
-
Why is there a book missing in the Hollow Bastion library?
+### Why is there a book missing in the Hollow Bastion library?
You'll need to find `Theon Vol. 6` in the multiworld.
-
How do I unlock End of the World?
+### How do I unlock End of the World?
Depending on your settings, your options are either finding a specified amount of `Lucky Emblems` or finding the item `End of the World`.
-
How do I enter Destiny Islands?
+### How do I enter Destiny Islands?
After obtaining the item `Destiny Islands`, you can land there as an additional option in Traverse Town.
-
How do I progress to Destiny Islands Day 2 and 3?
+### How do I progress to Destiny Islands Day 2 and 3?
In order to access Day 2 and 3, you need to collect an amount of `Raft Materials` specified in your settings. When you start Day 3, you'll be immediately warped to Homecoming.
-
Why can't I use the summon I obtained?
+### Why can't I use the summon I obtained?
You need at least one magic spell before you can use summons.
+
+## Troubleshooting
+
+### Why am I not sending or receiving any items despite being connected to the server?
+
+Make sure you are using the KH1 Client and not the Text Client. You will need to open the client manually via the Archipelago Launcher.
+
+### Why don't I have any worlds on the world map?
+
+If you have any of these symptoms: you find that the title screen does not have the Archipelago logo, that you had to do the entirety of Dive to the Heart, that you do not warp to the world map after choosing your Dream Weapons, or that when you get to the world map there are no worlds there;
+
+This is likely due to the mod not being applied properly. First, reinstall both Panacea and Lua Backend via the Setup Wizard under Settings. Second, make sure the seed mod is enabled [x]. Finally, ensure the game builds with no errors after selecting Build and Run under Mod Loader.
+
+### Why did the game send checks that I had not collected?
+
+The client caches your inventory and does not clear the cache when switching slots. The game also does something similar, even if backed out to the title screen. Therefore, it's highly encouraged, whenever switching slots or connecting to a different server, to always fully close both the game and the client first.
diff --git a/worlds/kh2/__init__.py b/worlds/kh2/__init__.py
index 3068e7bb56de..f13dec726523 100644
--- a/worlds/kh2/__init__.py
+++ b/worlds/kh2/__init__.py
@@ -390,9 +390,9 @@ def goofy_pre_fill(self):
# take one of the 2 out
# randomize the list with only
for location in goofy_weapon_location_list:
- random_ability = self.random.choice(self.goofy_weapon_abilities)
+ i = self.random.randrange(len(self.goofy_weapon_abilities))
+ random_ability = self.goofy_weapon_abilities.pop(i)
location.place_locked_item(random_ability)
- self.goofy_weapon_abilities.remove(random_ability)
if not self.options.DonaldGoofyStatsanity:
# plando goofy get bonuses
@@ -401,10 +401,9 @@ def goofy_pre_fill(self):
if len(goofy_get_bonus_location_pool) > len(self.goofy_get_bonus_abilities):
raise Exception(f"Too little abilities to fill goofy get bonus locations for player {self.player_name}.")
for location in goofy_get_bonus_location_pool:
- self.random.choice(self.goofy_get_bonus_abilities)
- random_ability = self.random.choice(self.goofy_get_bonus_abilities)
+ i = self.random.randrange(len(self.goofy_get_bonus_abilities))
+ random_ability = self.goofy_get_bonus_abilities.pop(i)
location.place_locked_item(random_ability)
- self.goofy_get_bonus_abilities.remove(random_ability)
def donald_pre_fill(self):
donald_weapon_location_list = [self.multiworld.get_location(location, self.player) for location in
@@ -413,9 +412,9 @@ def donald_pre_fill(self):
# take one of the 2 out
# randomize the list with only
for location in donald_weapon_location_list:
- random_ability = self.random.choice(self.donald_weapon_abilities)
+ i = self.random.randrange(len(self.donald_weapon_abilities))
+ random_ability = self.donald_weapon_abilities.pop(i)
location.place_locked_item(random_ability)
- self.donald_weapon_abilities.remove(random_ability)
# if option is turned off
if not self.options.DonaldGoofyStatsanity:
donald_get_bonus_location_pool = [self.multiworld.get_location(location, self.player) for location in
@@ -423,9 +422,9 @@ def donald_pre_fill(self):
if len(donald_get_bonus_location_pool) > len(self.donald_get_bonus_abilities):
raise Exception(f"Too little abilities to fill donald get bonus locations for player {self.player_name}.")
for location in donald_get_bonus_location_pool:
- random_ability = self.random.choice(self.donald_get_bonus_abilities)
+ i = self.random.randrange(len(self.donald_get_bonus_abilities))
+ random_ability = self.donald_get_bonus_abilities.pop(i)
location.place_locked_item(random_ability)
- self.donald_get_bonus_abilities.remove(random_ability)
def keyblade_pre_fill(self):
"""
diff --git a/worlds/kh2/archipelago.json b/worlds/kh2/archipelago.json
new file mode 100644
index 000000000000..180801e687de
--- /dev/null
+++ b/worlds/kh2/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Kingdom Hearts 2",
+ "authors": [ "JaredWeakStrike" ],
+ "minimum_ap_version": "0.6.3",
+ "world_version": "2.0.0"
+}
diff --git a/worlds/kh2/docs/setup_en.md b/worlds/kh2/docs/setup_en.md
index db0f6c86b920..22489c598e45 100644
--- a/worlds/kh2/docs/setup_en.md
+++ b/worlds/kh2/docs/setup_en.md
@@ -84,7 +84,7 @@ Enter The room's port number into the top box where the x's are and pres
- To fix this look over the guide at [KH2Rando.com](https://tommadness.github.io/KH2Randomizer/setup/Panacea-ModLoader/). Specifically the Panacea and Lua Backend Steps.
- Using a seed from the standalone KH2 Randomizer Seed Generator.
- - The Archipelago version of the KH2 Randomizer does not use this Seed Generator; refer to the [Archipelago Setup](https://archipelago.gg/tutorial/Archipelago/setup/en) to learn how to generate and play a seed through Archipelago.
+ - The Archipelago version of the KH2 Randomizer does not use this Seed Generator; refer to the [Archipelago Setup](/tutorial/Archipelago/setup/en) to learn how to generate and play a seed through Archipelago.
## Best Practices
diff --git a/worlds/ladx/LADXR/generator.py b/worlds/ladx/LADXR/generator.py
index f4023469d72b..58efda6c048f 100644
--- a/worlds/ladx/LADXR/generator.py
+++ b/worlds/ladx/LADXR/generator.py
@@ -1,11 +1,9 @@
import binascii
import importlib.util
import importlib.machinery
-import os
import random
import pickle
import Utils
-import settings
from collections import defaultdict
from typing import Dict
@@ -57,6 +55,7 @@
from .patches import multiworld as _
from .patches import tradeSequence as _
from . import hints
+from . import utils
from .patches import bank34
from .roomEditor import RoomEditor, Object
@@ -64,8 +63,27 @@
from .. import Options
+class VersionError(Exception):
+ pass
+
# Function to generate a final rom, this patches the rom with all required patches
def generateRom(base_rom: bytes, args, patch_data: Dict):
+ from .. import LinksAwakeningWorld
+ patcher_version = LinksAwakeningWorld.world_version
+ generated_version = Utils.tuplize_version(patch_data.get("generated_world_version", "2.0.0"))
+ if generated_version.major != patcher_version.major or generated_version.minor != patcher_version.minor:
+ Utils.messagebox(
+ "Error",
+ "The apworld version that this patch was generated on is incompatible with your installed world.\n\n"
+ f"Generated on {generated_version.as_simple_string()}\n"
+ f"Installed version {patcher_version.as_simple_string()}",
+ True
+ )
+ raise VersionError(
+ f"The installed world ({patcher_version.as_simple_string()}) is incompatible with the world this patch "
+ f"was generated on ({generated_version.as_simple_string()})"
+ )
+
random.seed(patch_data["seed"] + patch_data["player"])
multi_key = binascii.unhexlify(patch_data["multi_key"].encode())
item_list = pickle.loads(binascii.unhexlify(patch_data["item_list"].encode()))
@@ -84,9 +102,8 @@ def generateRom(base_rom: bytes, args, patch_data: Dict):
pymod.prePatch(rom)
if options["gfxmod"]:
- user_settings = settings.get_settings()
try:
- gfx_mod_file = user_settings["ladx_options"]["gfx_mod_file"]
+ gfx_mod_file = LinksAwakeningWorld.settings.gfx_mod_file
patches.aesthetics.gfxMod(rom, gfx_mod_file)
except FileNotFoundError:
pass # if user just doesnt provide gfxmod file, let patching continue
@@ -231,10 +248,10 @@ def generateRom(base_rom: bytes, args, patch_data: Dict):
rom.patch(0, 0x0003, "00", "01")
# Patch the sword check on the shopkeeper turning around.
- #if ladxr_settings["steal"] == 'never':
- # rom.patch(4, 0x36F9, "FA4EDB", "3E0000")
- #elif ladxr_settings["steal"] == 'always':
- # rom.patch(4, 0x36F9, "FA4EDB", "3E0100")
+ if options["stealing"] == Options.Stealing.option_disabled:
+ rom.patch(4, 0x36F9, "FA4EDB", "3E0000")
+ rom.texts[0x2E] = utils.formatText("Hey! Welcome! Did you know that I have eyes on the back of my head?")
+ rom.texts[0x2F] = utils.formatText("Nothing escapes my gaze! Your thieving ways shall never prosper!")
#if ladxr_settings["hpmode"] == 'inverted':
# patches.health.setStartHealth(rom, 9)
@@ -325,7 +342,7 @@ def generateRom(base_rom: bytes, args, patch_data: Dict):
0x1A9, 0x1AA, 0x1AB, 0x1AC, 0x1AD,
# Prices
- 0x02C, 0x02D, 0x030, 0x031, 0x032, 0x033, # Shop items
+ 0x02C, 0x02D, 0x02E, 0x02F, 0x030, 0x031, 0x032, 0x033, # Shop items
0x03B, # Trendy Game
0x045, # Fisherman
0x018, 0x019, # Crazy Tracy
diff --git a/worlds/ladx/LADXR/logic/overworld.py b/worlds/ladx/LADXR/logic/overworld.py
index b63aad2b340d..30236ab4b0dc 100644
--- a/worlds/ladx/LADXR/logic/overworld.py
+++ b/worlds/ladx/LADXR/logic/overworld.py
@@ -43,8 +43,12 @@ def __init__(self, options, world_setup, r):
self._addEntrance("start_house", mabe_village, start_house, None)
shop = Location("Shop")
- Location().add(ShopItem(0)).connect(shop, OR(AND(r.can_farm, COUNT("RUPEES", 500)), SWORD))
- Location().add(ShopItem(1)).connect(shop, OR(AND(r.can_farm, COUNT("RUPEES", 1480)), SWORD))
+ if options.steal == "inlogic":
+ Location().add(ShopItem(0)).connect(shop, OR(SWORD, AND(r.can_farm, COUNT("RUPEES", 500))))
+ Location().add(ShopItem(1)).connect(shop, OR(SWORD, AND(r.can_farm, COUNT("RUPEES", 1480))))
+ else:
+ Location().add(ShopItem(0)).connect(shop, AND(r.can_farm, COUNT("RUPEES", 500)))
+ Location().add(ShopItem(1)).connect(shop, AND(r.can_farm, COUNT("RUPEES", 1480)))
self._addEntrance("shop", mabe_village, shop, None)
dream_hut = Location("Dream Hut")
diff --git a/worlds/ladx/LADXR/settings.py b/worlds/ladx/LADXR/settings.py
index 3b8407c147d1..c0949f267879 100644
--- a/worlds/ladx/LADXR/settings.py
+++ b/worlds/ladx/LADXR/settings.py
@@ -162,8 +162,8 @@ def __init__(self, ap_options):
[Hero] Switch version hero mode, double damage, no heart/fairy drops.
[One hit KO] You die on a single hit, always."""),
Setting('steal', 'Gameplay', 't', 'Stealing from the shop',
- options=[('always', 'a', 'Always'), ('never', 'n', 'Never'), ('default', '', 'Normal')], default='default',
- description="""Effects when you can steal from the shop. Stealing is bad and never in logic.
+ options=[('inlogic', 'a', 'In logic'), ('disabled', 'n', 'Disabled'), ('outoflogic', '', 'Out of logic')], default='outoflogic',
+ description="""Effects when you can steal from the shop and if it is in logic.
[Normal] requires the sword before you can steal.
[Always] you can always steal from the shop
[Never] you can never steal from the shop."""),
@@ -286,7 +286,7 @@ def dis(setting: str, value: str, new_value: str, message: str) -> None:
if self.goal in ("bingo", "bingo-full"):
req("overworld", "normal", "Bingo goal does not work with dungeondive")
req("accessibility", "all", "Bingo goal needs 'all' accessibility")
- dis("steal", "never", "default", "With bingo goal, stealing should be allowed")
+ dis("steal", "disabled", "default", "With bingo goal, stealing should be allowed")
dis("boss", "random", "shuffle", "With bingo goal, bosses need to be on normal or shuffle")
dis("miniboss", "random", "shuffle", "With bingo goal, minibosses need to be on normal or shuffle")
if self.overworld == "dungeondive":
diff --git a/LinksAwakeningClient.py b/worlds/ladx/LinksAwakeningClient.py
similarity index 89%
rename from LinksAwakeningClient.py
rename to worlds/ladx/LinksAwakeningClient.py
index 293e782f59ea..b192b264e7ec 100644
--- a/LinksAwakeningClient.py
+++ b/worlds/ladx/LinksAwakeningClient.py
@@ -3,9 +3,6 @@
import Utils
-if __name__ == "__main__":
- Utils.init_logging("LinksAwakeningContext", exception_logger="Client")
-
import asyncio
import base64
import binascii
@@ -26,16 +23,14 @@
from CommonClient import (CommonContext, get_base_parser, gui_enabled, logger,
server_loop)
from NetUtils import ClientStatus
-from worlds.ladx import LinksAwakeningWorld
-from worlds.ladx.Common import BASE_ID as LABaseID
-from worlds.ladx.GpsTracker import GpsTracker
-from worlds.ladx.TrackerConsts import storage_key
-from worlds.ladx.ItemTracker import ItemTracker
-from worlds.ladx.LADXR.checkMetadata import checkMetadataTable
-from worlds.ladx.Locations import get_locations_to_id, meta_to_name
-from worlds.ladx.Tracker import LocationTracker, MagpieBridge, Check
-
-
+from . import LinksAwakeningWorld
+from .Common import BASE_ID as LABaseID
+from .GpsTracker import GpsTracker
+from .TrackerConsts import storage_key
+from .ItemTracker import ItemTracker
+from .LADXR.checkMetadata import checkMetadataTable
+from .Locations import get_locations_to_id, meta_to_name
+from .Tracker import LocationTracker, MagpieBridge, Check
class GameboyException(Exception):
pass
@@ -52,6 +47,10 @@ class BadRetroArchResponse(GameboyException):
pass
+class VersionError(Exception):
+ pass
+
+
class LAClientConstants:
# Connector version
VERSION = 0x01
@@ -141,7 +140,7 @@ def recv(self):
return response
async def async_recv(self, timeout=1.0):
- response = await asyncio.wait_for(asyncio.get_event_loop().sock_recv(self.socket, 4096), timeout)
+ response = await asyncio.wait_for(asyncio.get_running_loop().sock_recv(self.socket, 4096), timeout)
return response
async def check_safe_gameplay(self, throw=True):
@@ -523,7 +522,7 @@ class LADXManager(GameManager):
("Client", "Archipelago"),
("Tracker", "Tracker"),
]
- base_title = "Archipelago Links Awakening DX Client"
+ base_title = f"Links Awakening DX Client {LinksAwakeningWorld.world_version.as_simple_string()} | Archipelago"
def build(self):
b = super().build()
@@ -619,11 +618,20 @@ def on_package(self, cmd: str, args: dict):
if cmd == "Connected":
self.game = self.slot_info[self.slot].game
self.slot_data = args.get("slot_data", {})
+ generated_version = Utils.tuplize_version(self.slot_data.get("world_version", "2.0.0"))
+ client_version = LinksAwakeningWorld.world_version
+ if generated_version.major != client_version.major:
+ self.disconnected_intentionally = True
+ raise VersionError(
+ f"The installed world ({client_version.as_simple_string()}) is incompatible with "
+ f"the world this game was generated on ({generated_version.as_simple_string()})"
+ )
# This is sent to magpie over local websocket to make its own connection
self.slot_data.update({
"server_address": self.server_address,
"slot_name": self.player_names[self.slot],
"password": self.password,
+ "client_version": client_version.as_simple_string(),
})
# We can process linked items on already-checked checks now that we have slot_data
@@ -760,42 +768,44 @@ def run_game(romfile: str) -> None:
except FileNotFoundError:
logger.error(f"Couldn't launch ROM, {args[0]} is missing")
-async def main():
- parser = get_base_parser(description="Link's Awakening Client.")
- parser.add_argument("--url", help="Archipelago connection url")
- parser.add_argument("--no-magpie", dest='magpie', default=True, action='store_false', help="Disable magpie bridge")
- parser.add_argument('diff_file', default="", type=str, nargs="?",
- help='Path to a .apladx Archipelago Binary Patch file')
+def launch(*launch_args):
+ async def main():
+ parser = get_base_parser(description="Link's Awakening Client.")
+ parser.add_argument("--url", help="Archipelago connection url")
+ parser.add_argument("--no-magpie", dest='magpie', default=True, action='store_false', help="Disable magpie bridge")
+ parser.add_argument('diff_file', default="", type=str, nargs="?",
+ help='Path to a .apladx Archipelago Binary Patch file')
- args = parser.parse_args()
+ args = parser.parse_args(launch_args)
- if args.diff_file:
- import Patch
- logger.info("patch file was supplied - creating rom...")
- meta, rom_file = Patch.create_rom_file(args.diff_file)
- if "server" in meta and not args.connect:
- args.connect = meta["server"]
- logger.info(f"wrote rom file to {rom_file}")
+ if args.diff_file:
+ import Patch
+ logger.info("patch file was supplied - creating rom...")
+ meta, rom_file = Patch.create_rom_file(args.diff_file)
+ if "server" in meta and not args.connect:
+ args.connect = meta["server"]
+ logger.info(f"wrote rom file to {rom_file}")
- ctx = LinksAwakeningContext(args.connect, args.password, args.magpie)
+ ctx = LinksAwakeningContext(args.connect, args.password, args.magpie)
- ctx.server_task = asyncio.create_task(server_loop(ctx), name="server loop")
+ ctx.server_task = asyncio.create_task(server_loop(ctx), name="server loop")
- # TODO: nothing about the lambda about has to be in a lambda
- ctx.la_task = create_task_log_exception(ctx.run_game_loop())
- if gui_enabled:
- ctx.run_gui()
- ctx.run_cli()
+ # TODO: nothing about the lambda about has to be in a lambda
+ ctx.la_task = create_task_log_exception(ctx.run_game_loop())
+ if gui_enabled:
+ ctx.run_gui()
+ ctx.run_cli()
- # Down below run_gui so that we get errors out of the process
- if args.diff_file:
- run_game(rom_file)
+ # Down below run_gui so that we get errors out of the process
+ if args.diff_file:
+ run_game(rom_file)
- await ctx.exit_event.wait()
- await ctx.shutdown()
+ await ctx.exit_event.wait()
+ await ctx.shutdown()
+
+ Utils.init_logging("LinksAwakeningContext", exception_logger="Client")
-if __name__ == '__main__':
colorama.just_fix_windows_console()
asyncio.run(main())
colorama.deinit()
diff --git a/worlds/ladx/Options.py b/worlds/ladx/Options.py
index 2352e0fb91bb..47328036dd50 100644
--- a/worlds/ladx/Options.py
+++ b/worlds/ladx/Options.py
@@ -3,7 +3,7 @@
import os.path
import typing
import logging
-from Options import Choice, Toggle, DefaultOnToggle, Range, FreeText, PerGameCommonOptions, OptionGroup, Removed
+from Options import Choice, Toggle, DefaultOnToggle, Range, FreeText, PerGameCommonOptions, OptionGroup, Removed, StartInventoryPool
from collections import defaultdict
import Utils
@@ -325,6 +325,18 @@ class HardMode(Choice, LADXROption):
default = option_none
+class Stealing(Choice, LADXROption):
+ """
+ Puts stealing from the shop in logic if the player has a sword.
+ """
+ display_name = "Stealing"
+ ladxr_name = "steal"
+ option_in_logic = 1
+ option_out_of_logic = 2
+ option_disabled = 3
+ default = option_out_of_logic
+
+
class Overworld(Choice, LADXROption):
"""
**Open Mabe:** Replaces rock on the east side of Mabe Village with bushes,
@@ -656,6 +668,7 @@ class LinksAwakeningOptions(PerGameCommonOptions):
nag_messages: NagMessages
ap_title_screen: APTitleScreen
boots_controls: BootsControls
+ stealing: Stealing
quickswap: Quickswap
hard_mode: HardMode
low_hp_beep: LowHpBeep
@@ -665,6 +678,7 @@ class LinksAwakeningOptions(PerGameCommonOptions):
tarins_gift: TarinsGift
overworld: Overworld
stabilize_item_pool: StabilizeItemPool
+ start_inventory_from_pool: StartInventoryPool
warp_improvements: Removed
additional_warp_points: Removed
diff --git a/worlds/ladx/Rom.py b/worlds/ladx/Rom.py
index 969215a5e486..e0ee4b98ea3a 100644
--- a/worlds/ladx/Rom.py
+++ b/worlds/ladx/Rom.py
@@ -1,4 +1,3 @@
-import settings
import worlds.Files
import hashlib
import Utils
@@ -59,6 +58,7 @@ def get_source_data(cls) -> bytes:
def write_patch_data(world: "LinksAwakeningWorld", patch: LADXProcedurePatch):
item_list = pickle.dumps([item for item in world.ladxr_logic.iteminfo_list if not isinstance(item, KeyLocation)])
data_dict = {
+ "generated_world_version": world.world_version.as_simple_string(),
"out_base": world.multiworld.get_out_file_name_base(patch.player),
"is_race": world.multiworld.is_race,
"seed": world.multiworld.seed,
@@ -97,7 +97,7 @@ def write_patch_data(world: "LinksAwakeningWorld", patch: LADXProcedurePatch):
"nag_messages",
"ap_title_screen",
"boots_controls",
- # "stealing",
+ "stealing",
"quickswap",
"hard_mode",
"low_hp_beep",
@@ -125,9 +125,9 @@ def get_base_rom_bytes(file_name: str = "") -> bytes:
def get_base_rom_path(file_name: str = "") -> str:
- options = settings.get_settings()
+ from . import LinksAwakeningWorld
if not file_name:
- file_name = options["ladx_options"]["rom_file"]
+ file_name = LinksAwakeningWorld.settings.rom_file
if not os.path.exists(file_name):
file_name = Utils.user_path(file_name)
return file_name
diff --git a/worlds/ladx/Tracker.py b/worlds/ladx/Tracker.py
index 93b746328c33..adc3f1a19789 100644
--- a/worlds/ladx/Tracker.py
+++ b/worlds/ladx/Tracker.py
@@ -195,21 +195,24 @@ def use_entrance_tracker(self):
async def handler(self, websocket):
self.ws = websocket
while True:
- message = json.loads(await websocket.recv())
- if message["type"] == "handshake":
- logger.info(
- f"Connected, supported features: {message['features']}")
- self.features = message["features"]
-
- await self.send_handshAck()
-
- if message["type"] == "sendFull":
- if "items" in self.features:
- await self.send_all_inventory()
- if "checks" in self.features:
- await self.send_all_checks()
- if self.use_entrance_tracker():
- await self.send_gps(diff=False)
+ try:
+ message = json.loads(await websocket.recv())
+ if message["type"] == "handshake":
+ logger.info(
+ f"Connected, supported features: {message['features']}")
+ self.features = message["features"]
+
+ await self.send_handshAck()
+
+ if message["type"] == "sendFull":
+ if "items" in self.features:
+ await self.send_all_inventory()
+ if "checks" in self.features:
+ await self.send_all_checks()
+ if self.use_entrance_tracker():
+ await self.send_gps(diff=False)
+ except websockets.exceptions.ConnectionClosedOK:
+ pass
# Translate renamed IDs back to LADXR IDs
@staticmethod
diff --git a/worlds/ladx/__init__.py b/worlds/ladx/__init__.py
index 58d6c1681f25..c9f907046a81 100644
--- a/worlds/ladx/__init__.py
+++ b/worlds/ladx/__init__.py
@@ -4,11 +4,14 @@
import typing
import logging
import re
+import struct
import settings
+import Utils
from BaseClasses import CollectionState, Entrance, Item, ItemClassification, Location, Tutorial
from Fill import fill_restrictive
from worlds.AutoWorld import WebWorld, World
+from worlds.LauncherComponents import Component, components, SuffixIdentifier, Type, launch, icon_paths
from .Common import *
from . import ItemIconGuessing
from .Items import (DungeonItemData, DungeonItemType, ItemName, LinksAwakeningItem, TradeItemData,
@@ -29,6 +32,19 @@
DEVELOPER_MODE = False
+def launch_client(*args):
+ from .LinksAwakeningClient import launch as ladx_launch
+ launch(ladx_launch, name=f"{LINKS_AWAKENING} Client", args=args)
+
+components.append(Component(f"{LINKS_AWAKENING} Client",
+ func=launch_client,
+ component_type=Type.CLIENT,
+ icon=LINKS_AWAKENING,
+ file_identifier=SuffixIdentifier('.apladx')))
+
+icon_paths[LINKS_AWAKENING] = "ap:worlds.ladx/assets/MarinV-3_small.png"
+
+
class LinksAwakeningSettings(settings.Group):
class RomFile(settings.UserFilePath):
"""File name of the Link's Awakening DX rom"""
@@ -36,6 +52,17 @@ class RomFile(settings.UserFilePath):
description = "LADX ROM File"
md5s = [LADXProcedurePatch.hash]
+ @classmethod
+ def validate(cls, path: str) -> None:
+ try:
+ super().validate(path)
+ except ValueError:
+ Utils.messagebox(
+ "Error",
+ "Provided rom does not match hash for English 1.0/revision-0 of Link's Awakening DX",
+ True)
+ raise
+
class RomStart(str):
"""
Set this to false to never autostart a rom (such as after patching)
@@ -57,6 +84,24 @@ class GfxModFile(settings.FilePath):
Only .bin or .bdiff files
The same directory will be checked for a matching text modification file
"""
+ def browse(self, filetypes=None, **kwargs):
+ filetypes = [("Binary / Patch files", [".bin", ".bdiff"])]
+ return super().browse(filetypes=filetypes, **kwargs)
+
+ @classmethod
+ def validate(cls, path: str) -> None:
+ with open(path, "rb", buffering=0) as f:
+ header, size = struct.unpack(" None:
itempool = []
- exclude = [item.name for item in self.multiworld.precollected_items[self.player]]
-
self.prefill_original_dungeon = [ [], [], [], [], [], [], [], [], [] ]
self.prefill_own_dungeons = []
self.pre_fill_items = []
@@ -229,50 +272,46 @@ def create_items(self) -> None:
continue
item_name = ladxr_item_to_la_item_name[ladx_item_name]
for _ in range(count):
- if item_name in exclude:
- exclude.remove(item_name) # this is destructive. create unique list above
- self.multiworld.itempool.append(self.create_item(self.get_filler_item_name()))
- else:
- item = self.create_item(item_name)
-
- if not self.options.tradequest and isinstance(item.item_data, TradeItemData):
- location = self.multiworld.get_location(item.item_data.vanilla_location, self.player)
- location.place_locked_item(item)
- location.show_in_spoiler = False
- continue
-
- if isinstance(item.item_data, DungeonItemData):
- item_type = item.item_data.ladxr_id[:-1]
- shuffle_type = self.dungeon_item_types[item_type]
-
- if item.item_data.dungeon_item_type == DungeonItemType.INSTRUMENT and shuffle_type == ShuffleInstruments.option_vanilla:
- # Find instrument, lock
- # TODO: we should be able to pinpoint the region we want, save a lookup table please
- found = False
- for r in self.multiworld.get_regions(self.player):
- if r.dungeon_index != item.item_data.dungeon_index:
+ item = self.create_item(item_name)
+
+ if not self.options.tradequest and isinstance(item.item_data, TradeItemData):
+ location = self.multiworld.get_location(item.item_data.vanilla_location, self.player)
+ location.place_locked_item(item)
+ location.show_in_spoiler = False
+ continue
+
+ if isinstance(item.item_data, DungeonItemData):
+ item_type = item.item_data.ladxr_id[:-1]
+ shuffle_type = self.dungeon_item_types[item_type]
+
+ if item.item_data.dungeon_item_type == DungeonItemType.INSTRUMENT and shuffle_type == ShuffleInstruments.option_vanilla:
+ # Find instrument, lock
+ # TODO: we should be able to pinpoint the region we want, save a lookup table please
+ found = False
+ for r in self.multiworld.get_regions(self.player):
+ if r.dungeon_index != item.item_data.dungeon_index:
+ continue
+ for loc in r.locations:
+ if not isinstance(loc, LinksAwakeningLocation):
continue
- for loc in r.locations:
- if not isinstance(loc, LinksAwakeningLocation):
- continue
- if not isinstance(loc.ladxr_item, Instrument):
- continue
- loc.place_locked_item(item)
- found = True
- break
- if found:
- break
- else:
- if shuffle_type == DungeonItemShuffle.option_original_dungeon:
- self.prefill_original_dungeon[item.item_data.dungeon_index - 1].append(item)
- self.pre_fill_items.append(item)
- elif shuffle_type == DungeonItemShuffle.option_own_dungeons:
- self.prefill_own_dungeons.append(item)
- self.pre_fill_items.append(item)
- else:
- itempool.append(item)
+ if not isinstance(loc.ladxr_item, Instrument):
+ continue
+ loc.place_locked_item(item)
+ found = True
+ break
+ if found:
+ break
else:
- itempool.append(item)
+ if shuffle_type == DungeonItemShuffle.option_original_dungeon:
+ self.prefill_original_dungeon[item.item_data.dungeon_index - 1].append(item)
+ self.pre_fill_items.append(item)
+ elif shuffle_type == DungeonItemShuffle.option_own_dungeons:
+ self.prefill_own_dungeons.append(item)
+ self.pre_fill_items.append(item)
+ else:
+ itempool.append(item)
+ else:
+ itempool.append(item)
self.multi_key = self.generate_multi_key()
@@ -485,7 +524,7 @@ def generate_output(self, output_directory: str):
loc.ladxr_item.item = 'PIECE_OF_POWER'
else:
loc.ladxr_item.item = 'GUARDIAN_ACORN'
- loc.ladxr_item.custom_item_name = loc.item.name
+ loc.ladxr_item.setCustomItemName(loc.item.name)
if loc.item:
loc.ladxr_item.item_owner = loc.item.player
@@ -531,7 +570,9 @@ def get_filler_item_name(self) -> str:
return self.random.choices(self.filler_choices, self.filler_weights)[0]
def fill_slot_data(self):
- slot_data = {}
+ slot_data = {
+ "world_version": self.world_version.as_simple_string()
+ }
if not self.multiworld.is_race:
# all of these option are NOT used by the LADX- or Text-Client.
@@ -561,7 +602,7 @@ def fill_slot_data(self):
]
# use the default behaviour to grab options
- slot_data = self.options.as_dict(*slot_options)
+ slot_data.update(self.options.as_dict(*slot_options))
# for options which should not get the internal int value but the display name use the extra handling
slot_data.update({
diff --git a/worlds/ladx/archipelago.json b/worlds/ladx/archipelago.json
new file mode 100644
index 000000000000..a9b417483488
--- /dev/null
+++ b/worlds/ladx/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Links Awakening DX",
+ "authors": [ "zig", "threeandthree" ],
+ "minimum_ap_version": "0.6.4",
+ "world_version": "2.0.1"
+}
diff --git a/worlds/ladx/assets/MarinV-3.png b/worlds/ladx/assets/MarinV-3.png
new file mode 100644
index 000000000000..9174a632660a
Binary files /dev/null and b/worlds/ladx/assets/MarinV-3.png differ
diff --git a/worlds/ladx/assets/MarinV-3_small.png b/worlds/ladx/assets/MarinV-3_small.png
new file mode 100644
index 000000000000..3c87601fd187
Binary files /dev/null and b/worlds/ladx/assets/MarinV-3_small.png differ
diff --git a/worlds/ladx/docs/setup_en.md b/worlds/ladx/docs/setup_en.md
index d12f9b8b3b84..a7f9f87ef9ad 100644
--- a/worlds/ladx/docs/setup_en.md
+++ b/worlds/ladx/docs/setup_en.md
@@ -73,9 +73,8 @@ You only have to do these steps once. Note, RetroArch 1.9.x will not work as it
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
-
+ Network Command Port at 55355. \
+ 
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - Gameboy / Color (SameBoy)".
#### BizHawk 2.8 or newer (older versions untested)
diff --git a/worlds/landstalker/archipelago.json b/worlds/landstalker/archipelago.json
new file mode 100644
index 000000000000..8b8d5ea527b4
--- /dev/null
+++ b/worlds/landstalker/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Landstalker - The Treasures of King Nole",
+ "authors": ["Dinopony"],
+ "minimum_ap_version": "0.6.4",
+ "world_version": "1.8.7"
+}
\ No newline at end of file
diff --git a/worlds/landstalker/docs/landstalker_setup_en.md b/worlds/landstalker/docs/landstalker_setup_en.md
index 05cf35f8b071..9cd96eb960ce 100644
--- a/worlds/landstalker/docs/landstalker_setup_en.md
+++ b/worlds/landstalker/docs/landstalker_setup_en.md
@@ -48,7 +48,7 @@ A window will open with a few settings to enter:
- **Slot name**: Put the player name you specified in your YAML config file in this field.
- **Password**: If the server has a password, put it there.
-
+
Once all those fields were filled appropriately, click on the `Connect to Archipelago` button below to try connecting to
the Archipelago server.
@@ -67,7 +67,7 @@ You should see a window with settings to fill:
- **Output ROM directory**: This is where the randomized ROMs will be put. No need to change this unless you want them
to be created in a very specific folder.
-
+
There also a few cosmetic options you can fill before clicking the `Build ROM` button which should create your
randomized seed if everything went right.
@@ -83,7 +83,7 @@ the items you have received from other players.
You should see the following window:
-
+
As written, you have to open the newly generated ROM inside either Retroarch or Bizhawk using the Genesis Plus GX core.
Be careful to select that core, because any other core (e.g. BlastEm) won't work.
@@ -116,6 +116,6 @@ The client is packaged with both an **automatic item tracker** and an **automati
If you don't know all checks in the game, don't be afraid: you can click the `Where is it?` button that will show
you a screenshot of where the location actually is.
-
+
-Have fun!
\ No newline at end of file
+Have fun!
diff --git a/worlds/landstalker/docs/ls_guide_ap.png b/worlds/landstalker/docs/ls_guide_ap.png
index 674938ce6707..b65d1578140b 100644
Binary files a/worlds/landstalker/docs/ls_guide_ap.png and b/worlds/landstalker/docs/ls_guide_ap.png differ
diff --git a/worlds/landstalker/docs/ls_guide_client.png b/worlds/landstalker/docs/ls_guide_client.png
index a4e0f1ccf3d8..d73997968e53 100644
Binary files a/worlds/landstalker/docs/ls_guide_client.png and b/worlds/landstalker/docs/ls_guide_client.png differ
diff --git a/worlds/landstalker/docs/ls_guide_emu.png b/worlds/landstalker/docs/ls_guide_emu.png
index ff9218de12ba..e1027bd4e0dc 100644
Binary files a/worlds/landstalker/docs/ls_guide_emu.png and b/worlds/landstalker/docs/ls_guide_emu.png differ
diff --git a/worlds/landstalker/docs/ls_guide_rom.png b/worlds/landstalker/docs/ls_guide_rom.png
index c57554ab43d8..80d547240a96 100644
Binary files a/worlds/landstalker/docs/ls_guide_rom.png and b/worlds/landstalker/docs/ls_guide_rom.png differ
diff --git a/worlds/lufia2ac/docs/setup_en.md b/worlds/lufia2ac/docs/setup_en.md
index d82853d4fddf..adb6e4ff616d 100644
--- a/worlds/lufia2ac/docs/setup_en.md
+++ b/worlds/lufia2ac/docs/setup_en.md
@@ -106,9 +106,8 @@ You only have to do these steps once. Note, RetroArch 1.9.x will not work as it
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
-
+ Network Command Port at 55355. \
+ 
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
diff --git a/worlds/marioland2/docs/setup_en.md b/worlds/marioland2/docs/setup_en.md
index 581d36e7864f..7900fb91203c 100644
--- a/worlds/marioland2/docs/setup_en.md
+++ b/worlds/marioland2/docs/setup_en.md
@@ -44,7 +44,7 @@ You can generate a yaml or download a template by visiting the [Super Mario Land
### Generating and Patching a Game
1. Create your options file (YAML).
-2. Follow the general Archipelago instructions for [generating a game](../../Archipelago/setup/en#generating-a-game).
+2. Follow the general Archipelago instructions for [generating a game](/tutorial/Archipelago/setup_en#generating-a-game).
This will generate an output file for you. Your patch file will have a `.apsml2` file extension.
3. Open `ArchipelagoLauncher.exe`
4. Select "Open Patch" on the left side and select your patch file.
diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py
index 5405e8287739..492ba80a12e4 100644
--- a/worlds/marioland2/logic.py
+++ b/worlds/marioland2/logic.py
@@ -106,26 +106,38 @@ def tree_zone_4_midway_bell(state, player):
def tree_zone_4_coins(state, player, coins):
auto_scroll = is_auto_scroll(state, player, "Tree Zone 4")
- reachable_coins = 0
+ entryway = 14
+ hall = 4
+ first_trip_downstairs = 31
+ second_trip_downstairs = 15
+ downstairs_with_auto_scroll = 12
+ final_room = 10
+
+ reachable_coins_from_start = 0
+ reachable_coins_from_bell = 0
+
if has_pipe_up(state, player):
- reachable_coins += 14
+ reachable_coins_from_start += entryway
if has_pipe_right(state, player):
- reachable_coins += 4
+ reachable_coins_from_start += hall
if has_pipe_down(state, player):
- reachable_coins += 10
- if not auto_scroll:
- reachable_coins += 46
- elif state.has("Tree Zone 4 Midway Bell", player):
- if not auto_scroll:
- if has_pipe_left(state, player):
- reachable_coins += 18
- if has_pipe_down(state, player):
- reachable_coins += 10
+ if auto_scroll:
+ reachable_coins_from_start += downstairs_with_auto_scroll
+ else:
+ reachable_coins_from_start += final_room + first_trip_downstairs + second_trip_downstairs
+ if state.has("Tree Zone 4 Midway Bell", player):
+ if has_pipe_down(state, player) and (auto_scroll or not has_pipe_left(state, player)):
+ reachable_coins_from_bell += final_room
+ elif has_pipe_left(state, player) and not auto_scroll:
+ if has_pipe_down(state, player):
+ reachable_coins_from_bell += first_trip_downstairs
+ if has_pipe_right(state, player):
+ reachable_coins_from_bell += entryway + hall
if has_pipe_up(state, player):
- reachable_coins += 46
- elif has_pipe_down(state, player):
- reachable_coins += 10
- return coins <= reachable_coins
+ reachable_coins_from_bell += second_trip_downstairs + final_room
+ else:
+ reachable_coins_from_bell += entryway + hall
+ return coins <= max(reachable_coins_from_start, reachable_coins_from_bell)
def tree_zone_5_boss(state, player):
@@ -239,12 +251,9 @@ def pumpkin_zone_4_coins(state, player, coins):
def mario_zone_1_normal_exit(state, player):
- if has_pipe_right(state, player):
- if state.has_any(["Mushroom", "Fire Flower", "Carrot", "Mario Zone 1 Midway Bell"], player):
- return True
- if is_auto_scroll(state, player, "Mario Zone 1"):
- return True
- return False
+ return has_pipe_right(state, player) and (not is_auto_scroll(state, player, "Mario Zone 1")
+ or state.has_any(["Mushroom", "Fire Flower", "Carrot",
+ "Mario Zone 1 Midway Bell"], player))
def mario_zone_1_midway_bell(state, player):
diff --git a/worlds/mlss/Client.py b/worlds/mlss/Client.py
index 17d87197ca84..7944442b2a36 100644
--- a/worlds/mlss/Client.py
+++ b/worlds/mlss/Client.py
@@ -88,7 +88,7 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
if seed not in ctx.seed_name:
logger.info(
"ERROR: The ROM you loaded is for a different game of AP. "
- "Please make sure the host has sent you the correct patch file,"
+ "Please make sure the host has sent you the correct patch file, "
"and that you have opened the correct ROM."
)
raise bizhawk.ConnectorError("Loaded ROM is for Incorrect lobby.")
diff --git a/worlds/mm2/archipelago.json b/worlds/mm2/archipelago.json
index 75c098fdf964..9144dc292492 100644
--- a/worlds/mm2/archipelago.json
+++ b/worlds/mm2/archipelago.json
@@ -1,5 +1,5 @@
{
"game": "Mega Man 2",
- "world_version": "0.3.2",
+ "world_version": "0.3.3",
"minimum_ap_version": "0.6.4"
}
diff --git a/worlds/mm2/data/mm2_basepatch.bsdiff4 b/worlds/mm2/data/mm2_basepatch.bsdiff4
index 8f3c17c3c7af..d593ef49102c 100644
Binary files a/worlds/mm2/data/mm2_basepatch.bsdiff4 and b/worlds/mm2/data/mm2_basepatch.bsdiff4 differ
diff --git a/worlds/mm2/docs/setup_en.md b/worlds/mm2/docs/setup_en.md
index 3b8f833b9967..7462e3f73577 100644
--- a/worlds/mm2/docs/setup_en.md
+++ b/worlds/mm2/docs/setup_en.md
@@ -23,7 +23,7 @@ clear it.
1. Create your options file (YAML). You can make one on the
[Mega Man 2 options page](../../../games/Mega%20Man%202/player-options).
-2. Follow the general Archipelago instructions for [generating a game](../../Archipelago/setup/en#generating-a-game).
+2. Follow the general Archipelago instructions for [generating a game](/tutorial/Archipelago/setup_en#generating-a-game).
This will generate an output file for you. Your patch file will have the `.apmm2` file extension.
3. Open `ArchipelagoLauncher.exe`
4. Select "Open Patch" on the left side and select your patch file.
diff --git a/worlds/mm2/options.py b/worlds/mm2/options.py
index f33334898223..c1b291a91150 100644
--- a/worlds/mm2/options.py
+++ b/worlds/mm2/options.py
@@ -1,6 +1,7 @@
from dataclasses import dataclass
-from Options import Choice, Toggle, DeathLink, DefaultOnToggle, TextChoice, Range, OptionDict, PerGameCommonOptions
+from Options import (Choice, Toggle, DeathLink, DefaultOnToggle, TextChoice, Range, OptionDict, PerGameCommonOptions,
+ Visibility)
from schema import Schema, And, Use, Optional
bosses = {
@@ -178,6 +179,7 @@ class WeaknessPlando(OptionDict):
And(str, Use(str.title), lambda s: s in weapons_to_id): And(int, lambda i: i in range(-1, 15))
}
})
+ visibility = Visibility.template | Visibility.complex_ui | Visibility.spoiler
default = {}
diff --git a/worlds/mm2/rom.py b/worlds/mm2/rom.py
index e37c5bc2a148..3c31067ffa69 100644
--- a/worlds/mm2/rom.py
+++ b/worlds/mm2/rom.py
@@ -16,7 +16,7 @@
from . import MM2World
MM2LCHASH = "37f2c36ce7592f1e16b3434b3985c497"
-PROTEUSHASH = "9ff045a3ca30018b6e874c749abb3ec4"
+PROTEUSHASH = "b69fff40212b80c94f19e786d1efbf61"
MM2NESHASH = "0527a0ee512f69e08b8db6dc97964632"
MM2VCHASH = "0c78dfe8e90fb8f3eed022ff01126ad3"
@@ -327,8 +327,6 @@ def patch_rom(world: "MM2World", patch: MM2ProcedurePatch) -> None:
patch.write_byte(0x36089, pool[18]) # Intro
patch.write_byte(0x361F1, pool[19]) # Title
-
-
from Utils import __version__
patch.name = bytearray(f'MM2{__version__.replace(".", "")[0:3]}_{world.player}_{world.multiworld.seed:11}\0',
'utf8')[:21]
@@ -406,7 +404,7 @@ def get_base_rom_path(file_name: str = "") -> str:
return file_name
-PRG_OFFSET = 0x8ED70
+PRG_OFFSET = 0x8F170
PRG_SIZE = 0x40000
diff --git a/worlds/mm2/src/mm2_basepatch.asm b/worlds/mm2/src/mm2_basepatch.asm
index 00c8500f03df..a43f12bb4d86 100644
--- a/worlds/mm2/src/mm2_basepatch.asm
+++ b/worlds/mm2/src/mm2_basepatch.asm
@@ -58,6 +58,10 @@ FlashFixTarget1:
%org($808D, $0B)
FlashFixTarget2:
+%org($A65C, $0B)
+HeatFix:
+ CMP #$FF
+
%org($8015, $0D)
ClearRefreshHook:
; if we're already doing a fresh load of the stage select
diff --git a/worlds/musedash/MuseDashData.py b/worlds/musedash/MuseDashData.py
index f3a6becb6a26..4950c53e9667 100644
--- a/worlds/musedash/MuseDashData.py
+++ b/worlds/musedash/MuseDashData.py
@@ -671,4 +671,10 @@
"Temptation": SongData(2900795, "89-3", "Legendary Voyage, Mystic Treasure", False, 5, 8, 10),
"PwP": SongData(2900796, "89-4", "Legendary Voyage, Mystic Treasure", True, 3, 6, 9),
"I Can Show You": SongData(2900797, "89-5", "Legendary Voyage, Mystic Treasure", False, 5, 7, 9),
+ "An Artificial Flower Has No Right to Wilt": SongData(2900798, "90-0", "MEDIUM5 Echoes", True, 3, 5, 7),
+ "Time Stopped": SongData(2900799, "90-1", "MEDIUM5 Echoes", True, 5, 7, 9),
+ "Stranded Siren": SongData(2900800, "90-2", "MEDIUM5 Echoes", True, 6, 8, 10),
+ "City Lights": SongData(2900801, "90-3", "MEDIUM5 Echoes", True, 4, 6, 9),
+ "Polaris Wandering Night": SongData(2900802, "90-4", "MEDIUM5 Echoes", True, 5, 8, 10),
+ "Chasing the Moonlight": SongData(2900803, "90-5", "MEDIUM5 Echoes", True, 4, 6, 8),
}
diff --git a/worlds/musedash/archipelago.json b/worlds/musedash/archipelago.json
index d10e8369d6dd..9b22a9960523 100644
--- a/worlds/musedash/archipelago.json
+++ b/worlds/musedash/archipelago.json
@@ -1,6 +1,6 @@
{
"game": "Muse Dash",
"authors": ["DeamonHunter"],
- "world_version": "1.5.25",
+ "world_version": "1.5.26",
"minimum_ap_version": "0.6.3"
}
\ No newline at end of file
diff --git a/worlds/oot/archipelago.json b/worlds/oot/archipelago.json
new file mode 100644
index 000000000000..d651bfeb4715
--- /dev/null
+++ b/worlds/oot/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Ocarina of Time",
+ "authors": ["espeon65536"],
+ "world_version": "7.0.0",
+ "minimum_ap_version": "0.6.4"
+}
diff --git a/worlds/oot/docs/setup_de.md b/worlds/oot/docs/setup_de.md
index c35dd2769f1a..f257ddc015f4 100644
--- a/worlds/oot/docs/setup_de.md
+++ b/worlds/oot/docs/setup_de.md
@@ -84,7 +84,7 @@ Wenn du einer Multiworld beitrittst, wirst du gefordert eine YAML-Datei bei dem
erhälst du (in der Regel) einen Link vom Host der Multiworld. Dieser führt dich zu einem Raum, in dem alle
teilnehmenden Spieler (bzw. Welten) aufgelistet sind. Du solltest dich dann auf **deine** Welt konzentrieren
und klicke dann auf `Download APZ5 File...`.
-
+
Führe die `.apz5`-Datei mit einem Doppelklick aus, um deinen Ocarina Of Time-Client zu starten, sowie das patchen
deiner ROM. Ist dieser Prozess fertig (kann etwas dauern), startet sich der Client und der Emulator automatisch
diff --git a/worlds/paint/archipelago.json b/worlds/paint/archipelago.json
new file mode 100644
index 000000000000..f87089741fad
--- /dev/null
+++ b/worlds/paint/archipelago.json
@@ -0,0 +1,5 @@
+{
+ "game": "Paint",
+ "authors": ["MarioManTAW"],
+ "world_version": "0.5.2"
+}
diff --git a/worlds/pokemon_emerald/docs/setup_en.md b/worlds/pokemon_emerald/docs/setup_en.md
index 2ae54d5e0c14..fefedd2b1862 100644
--- a/worlds/pokemon_emerald/docs/setup_en.md
+++ b/worlds/pokemon_emerald/docs/setup_en.md
@@ -28,7 +28,7 @@ clear it.
1. Create your options file (YAML). You can make one on the
[Pokémon Emerald options page](../../../games/Pokemon%20Emerald/player-options).
-2. Follow the general Archipelago instructions for [generating a game](../../Archipelago/setup/en#generating-a-game).
+2. Follow the general Archipelago instructions for [generating a game](/tutorial/Archipelago/setup_en#generating-a-game).
This will generate an output file for you. Your patch file will have the `.apemerald` file extension.
3. Open `ArchipelagoLauncher.exe`
4. Select "Open Patch" on the left side and select your patch file.
diff --git a/worlds/pokemon_emerald/docs/setup_es.md b/worlds/pokemon_emerald/docs/setup_es.md
index 1d3721862a4f..f32bf0a28ead 100644
--- a/worlds/pokemon_emerald/docs/setup_es.md
+++ b/worlds/pokemon_emerald/docs/setup_es.md
@@ -29,7 +29,7 @@ con [PopTracker](https://github.com/black-sliver/PopTracker/releases)
1. Crea tu archivo de configuración (YAML). Puedes hacerlo en
[Página de Opciones de Pokémon Emerald](../../../games/Pokemon%20Emerald/player-options).
2. Sigue las instrucciones generales de Archipelago para
-[Generar un juego](../../Archipelago/setup/en#generating-a-game). Esto generará un archivo de salida (output file) para
+[Generar un juego](/tutorial/Archipelago/setup_en#generating-a-game). Esto generará un archivo de salida (output file) para
ti. Tu archivo de parche tendrá la extensión de archivo `.apemerald`.
3. Abre `ArchipelagoLauncher.exe`
4. Selecciona "Open Patch" en el lado derecho y elige tu archivo de parcheo.
diff --git a/worlds/pokemon_emerald/docs/setup_sv.md b/worlds/pokemon_emerald/docs/setup_sv.md
index 88b1d384096b..d3d4d8a69f22 100644
--- a/worlds/pokemon_emerald/docs/setup_sv.md
+++ b/worlds/pokemon_emerald/docs/setup_sv.md
@@ -30,7 +30,7 @@ används tillsammans med
1. Skapa din konfigurationsfil (YAML). Du kan göra en via att använda
[Pokémon Emerald options hemsida](../../../games/Pokemon%20Emerald/player-options).
2. Följ de allmänna Archipelago instruktionerna för att
-[Generera ett spel](../../Archipelago/setup/en#generating-a-game).
+[Generera ett spel](/tutorial/Archipelago/setup_en#generating-a-game).
Detta kommer generera en fil för dig. Din patchfil kommer ha `.apemerald` som sitt filnamnstillägg.
3. Öppna `ArchipelagoLauncher.exe`
4. Välj "Open Patch" på vänstra sidan, och välj din patchfil.
diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py
index 3f5a24cc173a..0810ec5dc748 100644
--- a/worlds/pokemon_rb/__init__.py
+++ b/worlds/pokemon_rb/__init__.py
@@ -656,11 +656,20 @@ def extend_hint_information(self, hint_data):
if self.options.dexsanity:
mon_locations = {mon: set() for mon in poke_data.pokemon_data.keys()}
for loc in location_data:
- if loc.type in ["Wild Encounter", "Static Pokemon", "Legendary Pokemon", "Missable Pokemon"]:
+ if loc.type in ["Wild Encounter", "Static Pokemon", "Legendary Pokemon"]:
mon = self.multiworld.get_location(loc.name, self.player).item.name
- if mon.startswith("Static ") or mon.startswith("Missable "):
+ if mon.startswith("Static "):
mon = " ".join(mon.split(" ")[1:])
- mon_locations[mon].add(loc.name.split(" -")[0])
+ if "Wild" in loc.name or "Fishing" in loc.name or "Prize" in loc.name:
+ encounter_label = loc.name.split(" -")[0]
+ elif "Surf" in loc.name:
+ encounter_label = f"{loc.name.split(' -')[0]} (Surf)"
+ elif "Fake" in loc.name:
+ encounter_label = f"{loc.name.split(' -')[0]} (Fake Pokeball)"
+ else:
+ split_loc_name = loc.name.split(" - ")
+ encounter_label = f"{split_loc_name[0]} ({split_loc_name[1]})"
+ mon_locations[mon].add(encounter_label)
for i, mon in enumerate(mon_locations):
if self.dexsanity_table[i] and mon_locations[mon]:
hint_data[self.player][self.multiworld.get_location(f"Pokedex - {mon}", self.player).address] =\
diff --git a/worlds/pokemon_rb/docs/setup_en.md b/worlds/pokemon_rb/docs/setup_en.md
index 7e05c8c782d0..7bb173071012 100644
--- a/worlds/pokemon_rb/docs/setup_en.md
+++ b/worlds/pokemon_rb/docs/setup_en.md
@@ -73,7 +73,7 @@ And the following special characters (these each count as one character):
### Generating and Patching a Game
1. Create your options file (YAML).
-2. Follow the general Archipelago instructions for [generating a game](../../Archipelago/setup/en#generating-a-game).
+2. Follow the general Archipelago instructions for [generating a game](/tutorial/Archipelago/setup_en#generating-a-game).
This will generate an output file for you. Your patch file will have a `.apred` or `.apblue` file extension.
3. Open `ArchipelagoLauncher.exe`
4. Select "Open Patch" on the left side and select your patch file.
diff --git a/worlds/pokemon_rb/docs/setup_es.md b/worlds/pokemon_rb/docs/setup_es.md
index 5c735bfe992a..8c79329f82bd 100644
--- a/worlds/pokemon_rb/docs/setup_es.md
+++ b/worlds/pokemon_rb/docs/setup_es.md
@@ -77,7 +77,7 @@ Y los siguientes caracteres especiales (cada uno ocupa un carácter):
### Generar y parchar un juego
1. Crea tu archivo de opciones (YAML).
-2. Sigue las instrucciones generales de Archipelago para [generar un juego](../../Archipelago/setup/en#generating-a-game).
+2. Sigue las instrucciones generales de Archipelago para [generar un juego](/tutorial/Archipelago/setup_en#generating-a-game).
Haciendo esto se generará un archivo de salida. Tu parche tendrá la extensión de archivo `.apred` o `.apblue`.
3. Abre `ArchipelagoLauncher.exe`
4. Selecciona "Open Patch" en el lado izquierdo y selecciona tu parche.
diff --git a/worlds/pokemon_rb/regions.py b/worlds/pokemon_rb/regions.py
index 5aa6243514b3..182f1fc79a92 100644
--- a/worlds/pokemon_rb/regions.py
+++ b/worlds/pokemon_rb/regions.py
@@ -1237,7 +1237,7 @@
entrance_only = [
"Route 4-W to Mt Moon 1F", "Saffron City-G to Saffron Gym-S", "Saffron City-Copycat to Saffron Copycat's House 1F",
- "Saffron City-Pidgey to Saffron Pidgey House", "Celadon Game Corner-Hidden Stairs to Rocket Hideout B1F"
+ "Saffron City-Pidgey to Saffron Pidgey House", "Celadon Game Corner-Hidden Stairs to Rocket Hideout B1F",
"Cinnabar Island-M to Pokemon Mansion 1F", "Mt Moon B2F to Mt Moon B1F-W", "Silph Co 7F-NW to Silph Co 11F-W",
"Viridian City-G", "Cerulean City-Cave to Cerulean Cave 1F-SE", "Cerulean City-T to Cerulean Trashed House",
"Route 10-P to Power Plant", "S.S. Anne 2F to S.S. Anne Captain's Room", "Pewter City-M to Pewter Museum 1F-E",
diff --git a/worlds/satisfactory/CriticalPathCalculator.py b/worlds/satisfactory/CriticalPathCalculator.py
new file mode 100644
index 000000000000..fc055b4e044a
--- /dev/null
+++ b/worlds/satisfactory/CriticalPathCalculator.py
@@ -0,0 +1,273 @@
+from random import Random
+from typing import Optional
+from collections.abc import Iterable
+from .GameLogic import GameLogic, Recipe
+from .Options import SatisfactoryOptions
+
+
+class CriticalPathCalculator:
+ logic: GameLogic
+ random: Random
+ final_elevator_phase: int
+ randomize_starter_recipes: bool
+
+ required_parts: set[str]
+ required_buildings: set[str]
+ required_item_names: set[str]
+ required_power_level: int
+
+ __potential_required_belt_speed: int
+
+ parts_to_exclude: set[str]
+ recipes_to_exclude: set[str]
+ buildings_to_exclude: set[str]
+
+ implicitly_unlocked: set[str]
+ handcraftable_parts: dict[str, list[Recipe]]
+ tier_0_recipes: set[str]
+
+ def __init__(self, logic: GameLogic, seed: float, options: SatisfactoryOptions):
+ self.logic = logic
+ self.random = Random(seed)
+ self.final_elevator_phase = options.final_elevator_phase.value
+ self.randomize_starter_recipes = bool(options.randomize_starter_recipes.value)
+
+ def calculate(self) -> None:
+ self.required_parts = set[str]()
+ self.required_buildings = set[str]()
+ self.required_power_level: int = 1
+
+ self.__potential_required_belt_speed = 1
+
+ self.configure_implicitly_unlocked_and_handcraftable_parts()
+
+ self.select_minimal_required_parts_for(
+ self.logic.space_elevator_phases[self.final_elevator_phase-1].keys())
+
+ for tree in self.logic.man_trees.values():
+ self.select_minimal_required_parts_for(tree.access_items)
+
+ for node in tree.nodes:
+ if node.minimal_phase > self.final_elevator_phase:
+ continue
+
+ self.select_minimal_required_parts_for(node.unlock_cost)
+
+ self.select_minimal_required_parts_for_building("MAM")
+ self.select_minimal_required_parts_for_building("AWESOME Sink")
+ self.select_minimal_required_parts_for_building("AWESOME Shop")
+ self.select_minimal_required_parts_for_building("Space Elevator")
+ self.select_minimal_required_parts_for_building("Conveyor Splitter")
+ self.select_minimal_required_parts_for_building("Conveyor Merger")
+ self.select_minimal_required_parts_for_building("Equipment Workshop")
+ self.select_minimal_required_parts_for_building("Foundation")
+ self.select_minimal_required_parts_for_building("Walls Orange")
+ self.select_minimal_required_parts_for_building("Power Storage")
+ self.select_minimal_required_parts_for_building("Miner Mk.2")
+ self.select_minimal_required_parts_for_building("Pipes Mk.1")
+ self.select_minimal_required_parts_for_building("Pipes Mk.2")
+ self.select_minimal_required_parts_for_building("Pipeline Pump Mk.1")
+ self.select_minimal_required_parts_for_building("Pipeline Pump Mk.2")
+
+ if self.logic.recipes["Uranium"][0].minimal_phase <= self.final_elevator_phase:
+ self.select_minimal_required_parts_for(("Hazmat Suit", "Iodine-Infused Filter"))
+
+ for i in range(1, self.__potential_required_belt_speed + 1):
+ self.select_minimal_required_parts_for_building(f"Conveyor Mk.{i}")
+
+ for i in range(1, self.required_power_level + 1):
+ power_recipe = self.random.choice(self.logic.requirement_per_powerlevel[i])
+ self.select_minimal_required_parts_for(power_recipe.inputs)
+ self.select_minimal_required_parts_for_building(power_recipe.building)
+
+ self.required_item_names = {
+ recipe.name
+ for part in self.required_parts
+ for recipe in self.logic.recipes[part]
+ if recipe.minimal_phase <= self.final_elevator_phase
+ }
+ self.required_item_names.update({"Building: " + building for building in self.required_buildings})
+
+ self.calculate_excluded_things()
+ self.select_starter_recipes()
+
+ def select_minimal_required_parts_for_building(self, building: str) -> None:
+ self.select_minimal_required_parts_for(self.logic.buildings[building].inputs)
+ self.required_buildings.add(building)
+
+ def select_minimal_required_parts_for(self, parts: Optional[Iterable[str]]) -> None:
+ if parts is None:
+ return
+
+ for part in parts:
+ if part in self.required_parts:
+ continue
+
+ self.required_parts.add(part)
+
+ for recipe in self.logic.recipes[part]:
+ if recipe.minimal_phase > self.final_elevator_phase:
+ continue
+
+ self.__potential_required_belt_speed = \
+ max(self.__potential_required_belt_speed, recipe.minimal_belt_speed)
+
+ self.select_minimal_required_parts_for(recipe.inputs)
+
+ if recipe.building:
+ self.select_minimal_required_parts_for(self.logic.buildings[recipe.building].inputs)
+ self.required_buildings.add(recipe.building)
+
+ if self.logic.buildings[recipe.building].power_requirement:
+ self.required_power_level = \
+ max(self.required_power_level,
+ self.logic.buildings[recipe.building].power_requirement)
+
+ def calculate_excluded_things(self) -> None:
+ self.parts_to_exclude = set[str]()
+ self.buildings_to_exclude = set[str]()
+ self.recipes_to_exclude = {
+ recipe.name
+ for part in self.logic.recipes
+ for recipe in self.logic.recipes[part]
+ if recipe.minimal_phase > self.final_elevator_phase
+ }
+
+ excluded_count = len(self.recipes_to_exclude)
+ while True:
+ for part in self.logic.recipes:
+ if part in self.parts_to_exclude:
+ continue
+
+ for recipe in self.logic.recipes[part]:
+ if recipe.name in self.recipes_to_exclude:
+ continue
+
+ if recipe.inputs and any(input in self.parts_to_exclude for input in recipe.inputs):
+ self.recipes_to_exclude.add(recipe.name)
+
+ if all(r.name in self.recipes_to_exclude for r in self.logic.recipes[part]):
+ self.parts_to_exclude.add(part)
+
+ new_buildings_to_exclude = {
+ building_name
+ for building_name, building in self.logic.buildings.items()
+ if building_name not in self.buildings_to_exclude
+ and building.inputs and any(input in self.parts_to_exclude for input in building.inputs)
+ }
+
+ self.recipes_to_exclude.update({
+ recipe_per_part.name
+ for recipes_per_part in self.logic.recipes.values()
+ for recipe_per_part in recipes_per_part
+ if recipe_per_part.building in new_buildings_to_exclude
+ })
+
+ self.buildings_to_exclude.update(new_buildings_to_exclude)
+
+ new_length = len(self.recipes_to_exclude)
+ if new_length == excluded_count:
+ break
+ excluded_count = new_length
+
+ def configure_implicitly_unlocked_and_handcraftable_parts(self) -> None:
+ self.implicitly_unlocked: set[str] = {
+ recipe.name
+ for recipes_per_part in self.logic.recipes.values()
+ for recipe in recipes_per_part if recipe.implicitly_unlocked
+ }
+ self.implicitly_unlocked.update({
+ building.name
+ for building in self.logic.buildings.values() if building.implicitly_unlocked
+ })
+
+ self.handcraftable_parts: dict[str, list[Recipe]] = {}
+ for part, recipes_per_part in self.logic.recipes.items():
+ for recipe in recipes_per_part:
+ if recipe.handcraftable:
+ self.handcraftable_parts.setdefault(part, []).append(recipe)
+
+ def select_starter_recipes(self) -> None:
+ # cable is left unaffected as all its alternative recipes require refinery
+ if not self.randomize_starter_recipes:
+ self.tier_0_recipes = {
+ "Recipe: Iron Ingot",
+ "Recipe: Iron Plate",
+ "Recipe: Iron Rod",
+ "Recipe: Copper Ingot",
+ "Recipe: Wire",
+ "Recipe: Concrete",
+ "Recipe: Screw",
+ "Recipe: Reinforced Iron Plate"
+ }
+ else:
+ # we only allow basic parts to be made without the need of refineries
+ # could be made more based of GameLogic rather than hardcoded but this is likely faster
+ # would likely need to be based of GameLogic when we add mod support
+ self.tier_0_recipes = set()
+
+ self.tier_0_recipes.add(self.random.choice(
+ ("Recipe: Iron Ingot", "Recipe: Basic Iron Ingot", "Recipe: Iron Alloy Ingot")))
+
+ selected_recipe = self.random.choice(
+ ("Recipe: Iron Plate", "Recipe: Iron Plate", "Recipe: Iron Plate", "Recipe: Steel Cast Plate"))
+ self.tier_0_recipes.add(selected_recipe)
+ if selected_recipe == "Recipe: Steel Cast Plate":
+ self.add_steel_ingot_to_starter_recipes()
+
+ selected_recipe = self.random.choice(
+ ("Recipe: Iron Rod", "Recipe: Iron Rod", "Recipe: Iron Rod", "Recipe: Steel Rod"))
+ self.tier_0_recipes.add(selected_recipe)
+ if selected_recipe == "Recipe: Steel Rod":
+ self.add_steel_ingot_to_starter_recipes()
+
+ self.tier_0_recipes.add(self.random.choice(("Recipe: Copper Ingot", "Recipe: Copper Alloy Ingot")))
+
+ selected_recipe = self.random.choice(
+ ("Recipe: Wire", "Recipe: Caterium Wire", "Recipe: Fused Wire", "Recipe: Iron Wire"))
+ self.tier_0_recipes.add(selected_recipe)
+ if selected_recipe in {"Recipe: Caterium Wire", "Recipe: Fused Wire"}:
+ # add Caterium Ingot
+ self.tier_0_recipes.add("Recipe: Caterium Ingot")
+
+ selected_recipe = self.random.choice(("Recipe: Concrete", "Recipe: Fine Concrete"))
+ self.tier_0_recipes.add(selected_recipe)
+ if selected_recipe == "Recipe: Fine Concrete":
+ # add Silica
+ self.tier_0_recipes.add(self.random.choice(("Recipe: Silica", "Recipe: Cheap Silica")))
+
+ selected_recipe = self.random.choice(
+ ("Recipe: Screw", "Recipe: Screw", "Recipe: Cast Screw", "Recipe: Cast Screw", "Recipe: Steel Screw"))
+
+ self.tier_0_recipes.add(selected_recipe)
+ if selected_recipe == "Recipe: Steel Screw":
+ # add Steel Beam and steel Ingot
+ self.add_steel_ingot_to_starter_recipes()
+ self.tier_0_recipes.add(self.random.choice(("Recipe: Steel Beam", "Recipe: Molded Beam")))
+
+ self.tier_0_recipes.add(self.random.choice(
+ ("Recipe: Reinforced Iron Plate", "Recipe: Bolted Iron Plate", "Recipe: Stitched Iron Plate")))
+
+ for part, recipes in self.logic.recipes.items():
+ for recipe in recipes:
+ if recipe.name in self.tier_0_recipes:
+ if part in self.handcraftable_parts:
+ self.handcraftable_parts[part].append(recipe)
+ else:
+ self.handcraftable_parts[part] = [recipe]
+ self.tier_0_recipes.add(self.logic.buildings[recipe.building].name)
+
+ self.implicitly_unlocked.update(self.tier_0_recipes)
+
+ def add_steel_ingot_to_starter_recipes(self) -> None:
+ if "Recipe: Steel Ingot" not in self.tier_0_recipes \
+ and "Recipe: Compacted Steel Ingot" not in self.tier_0_recipes \
+ and "Recipe: Solid Steel Ingot" not in self.tier_0_recipes:
+
+ selected_recipe = self.random.choice(
+ ("Recipe: Steel Ingot", "Recipe: Compacted Steel Ingot", "Recipe: Solid Steel Ingot"))
+
+ self.tier_0_recipes.add(selected_recipe)
+
+ if selected_recipe == "Recipe: Compacted Steel Ingot":
+ self.tier_0_recipes.add("Recipe: Compacted Coal")
diff --git a/worlds/satisfactory/GameLogic.py b/worlds/satisfactory/GameLogic.py
new file mode 100644
index 000000000000..a1039a7efe4c
--- /dev/null
+++ b/worlds/satisfactory/GameLogic.py
@@ -0,0 +1,992 @@
+from typing import Optional
+from dataclasses import dataclass
+from enum import IntEnum
+
+
+class PowerInfrastructureLevel(IntEnum):
+ Basic = 1
+ Automated = 2
+ Advanced = 3
+ Complex = 4
+
+ def to_name(self) -> str:
+ return "Power level: " + self.name
+
+
+liquids: set[str] = {
+ "Water",
+ "Liquid Biofuel",
+ "Crude Oil",
+ "Fuel",
+ "Heavy Oil Residue",
+ "Turbofuel",
+ "Alumina Solution",
+ "Sulfuric Acid",
+ "Nitrogen Gas",
+ "Nitric Acid",
+ "Dissolved Silica",
+ "Rocket Fuel",
+ "Ionized Fuel",
+ "Excited Photonic Matter",
+ "Dark Matter Residue"
+}
+
+radio_actives: set[str] = {
+ "Uranium",
+ "Encased Uranium Cell",
+ "Uranium Fuel Rod"
+ "Uranium Waste",
+ "Non-fissile Uranium",
+ "Plutonium Pellet",
+ "Encased Plutonium Cell",
+ "Plutonium Fuel Rod",
+ "Plutonium Waste",
+ "Ficsonium",
+ "Ficsonium Fuel Rod"
+}
+
+
+class Recipe:
+ """
+ Relationship between components and what is required to produce them (input ingredients, production building, etc.)
+ Not all recipes are Satisfactory FGRecipes - for example, Water has a Recipe, but it's not an FGRecipe
+ """
+ name: str
+ building: Optional[str]
+ inputs: Optional[tuple[str, ...]]
+ minimal_belt_speed: int
+ handcraftable: bool
+ implicitly_unlocked: bool
+ """No explicit location/item is needed to unlock this recipe, you have access as soon as dependencies are met
+ (ex. Water, Leaves, tutorial starting items)"""
+ additional_outputs: Optional[tuple[str, ...]]
+ minimal_phase: int
+
+ needs_pipes: bool
+ is_radio_active: bool
+
+ def __init__(self, name: str, building: Optional[str] = None, inputs: Optional[tuple[str, ...]] = None,
+ minimal_belt_speed: int = 1, handcraftable: bool = False, implicitly_unlocked: bool = False,
+ additional_outputs: Optional[tuple[str, ...]] = None, minimal_phase: Optional[int] = 1):
+ self.name = "Recipe: " + name
+ self.building = building
+ self.inputs = inputs
+ self.minimal_belt_speed = minimal_belt_speed
+ self.handcraftable = handcraftable
+ self.implicitly_unlocked = implicitly_unlocked
+ self.additional_outputs = additional_outputs
+ self.minimal_phase = minimal_phase
+
+ all_parts: list[str] = [name]
+ if inputs:
+ all_parts += inputs
+ if additional_outputs:
+ all_parts += additional_outputs
+
+ self.needs_pipes = not liquids.isdisjoint(all_parts)
+ self.is_radio_active = not radio_actives.isdisjoint(all_parts)
+
+
+class Building(Recipe):
+ power_requirement: Optional[PowerInfrastructureLevel]
+ can_produce: bool
+
+ def __init__(self, name: str, inputs: Optional[tuple[str, ...]] = None,
+ power_requirement: Optional[PowerInfrastructureLevel] = None, can_produce: bool = True,
+ implicitly_unlocked: bool = False):
+ super().__init__(name, None, inputs, handcraftable=True, implicitly_unlocked=implicitly_unlocked)
+ self.name = "Building: " + name
+ self.power_requirement = power_requirement
+ self.can_produce = can_produce
+ self.implicitly_unlocked = implicitly_unlocked
+
+
+class MamNode:
+ name: str
+ unlock_cost: dict[str, int]
+ """All game items must be submitted to purchase this MamNode"""
+ depends_on: tuple[str, ...]
+ """At least one of these prerequisite MamNodes must be unlocked to purchase this MamNode"""
+ minimal_phase: Optional[int]
+
+ def __init__(self, name: str, unlock_cost: dict[str, int], depends_on: tuple[str, ...],
+ minimal_phase: Optional[int] = 1):
+ self.name = name
+ self.unlock_cost = unlock_cost
+ self.depends_on = depends_on
+ self.minimal_phase = minimal_phase
+
+
+class MamTree:
+ access_items: tuple[str, ...]
+ """At least one of these game items must enter the player inventory for this MamTree to be available"""
+ nodes: tuple[MamNode, ...]
+
+ def __init__(self, access_items: tuple[str, ...], nodes: tuple[MamNode, ...]):
+ self.access_items = access_items
+ self.nodes = nodes
+
+
+@dataclass
+class DropPodData:
+ x: int
+ y: int
+ z: int
+ item: Optional[str]
+ power: int
+ gassed: Optional[bool] = None
+ radioactive: Optional[bool] = None
+
+
+class GameLogic:
+ indirect_recipes: dict[str, str] = {
+ "Recipe: Quartz Purification": "Recipe: Distilled Silica"
+ }
+
+ recipes: dict[str, tuple[Recipe, ...]] = {
+ # This Dict should only contain items that are used somewhere in a logic chain
+
+ # Exploration Items
+ "Leaves": (
+ Recipe("Leaves", handcraftable=True, implicitly_unlocked=True), ),
+ "Wood": (
+ Recipe("Wood", handcraftable=True, implicitly_unlocked=True), ),
+ "Hatcher Remains": (
+ Recipe("Hatcher Remains", handcraftable=True, implicitly_unlocked=True), ),
+ "Hog Remains": (
+ Recipe("Hog Remains", handcraftable=True, implicitly_unlocked=True), ),
+ "Plasma Spitter Remains": (
+ Recipe("Plasma Spitter Remains", handcraftable=True, implicitly_unlocked=True), ),
+ "Stinger Remains": (
+ Recipe("Stinger Remains", handcraftable=True, implicitly_unlocked=True), ),
+ "Mycelia": (
+ Recipe("Mycelia", handcraftable=True, implicitly_unlocked=True), ),
+ "Beryl Nut": (
+ Recipe("Beryl Nut", handcraftable=True, implicitly_unlocked=True), ),
+ "Paleberry": (
+ Recipe("Paleberry", handcraftable=True, implicitly_unlocked=True), ),
+ "Bacon Agaric": (
+ Recipe("Bacon Agaric", handcraftable=True, implicitly_unlocked=True), ),
+ "Blue Power Slug": (
+ Recipe("Blue Power Slug", handcraftable=True, implicitly_unlocked=True), ),
+ "Yellow Power Slug": (
+ Recipe("Yellow Power Slug", handcraftable=True, implicitly_unlocked=True), ),
+ "Purple Power Slug": (
+ Recipe("Purple Power Slug", handcraftable=True, implicitly_unlocked=True), ),
+ "Hard Drive": (
+ Recipe("Hard Drive", handcraftable=True, implicitly_unlocked=True), ),
+ "Mercer Sphere": (
+ Recipe("Mercer Sphere", handcraftable=True, implicitly_unlocked=True), ),
+ "Somersloop": (
+ Recipe("Somersloop", handcraftable=True, implicitly_unlocked=True), ),
+
+ # Raw Resources
+ "Water": (
+ Recipe("Water", "Water Extractor", implicitly_unlocked=True),
+ Recipe("Water (Resource Well)", "Resource Well Pressurizer", implicitly_unlocked=True, minimal_phase=2)),
+ "Limestone": (
+ Recipe("Limestone", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
+ "Raw Quartz": (
+ Recipe("Raw Quartz", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
+ "Iron Ore": (
+ Recipe("Iron Ore", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
+ "Copper Ore": (
+ Recipe("Copper Ore", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
+ "Coal": (
+ Recipe("Coal", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
+ "Sulfur": (
+ Recipe("Sulfur", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
+ "Caterium Ore": (
+ Recipe("Caterium Ore", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
+ "Crude Oil": (
+ Recipe("Crude Oil", "Oil Extractor", implicitly_unlocked=True),
+ Recipe("Crude Oil (Resource Well)", "Resource Well Pressurizer", implicitly_unlocked=True, minimal_phase=2)),
+ "Bauxite": (
+ Recipe("Bauxite", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True, minimal_phase=2), ),
+ "Nitrogen Gas": (
+ Recipe("Nitrogen Gas", "Resource Well Pressurizer", implicitly_unlocked=True, minimal_phase=2), ),
+ "Uranium": (
+ Recipe("Uranium", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True, minimal_phase=2), ),
+
+ # Special Items
+ "Uranium Waste": (
+ Recipe("Uranium Waste", "Nuclear Power Plant", ("Uranium Fuel Rod", "Water"), implicitly_unlocked=True, minimal_phase=2), ),
+ # "Plutonium Waste": (
+ # Recipe("Plutonium Waste", "Nuclear Power Plant", ("Plutonium Fuel Rod", "Water"), implicitly_unlocked=True), ),
+
+ # Recipes
+ "Reinforced Iron Plate": (
+ Recipe("Reinforced Iron Plate", "Assembler", ("Iron Plate", "Screw")),
+ Recipe("Adhered Iron Plate", "Assembler", ("Iron Plate", "Rubber")),
+ Recipe("Bolted Iron Plate", "Assembler", ("Iron Plate", "Screw"), minimal_belt_speed=3),
+ Recipe("Stitched Iron Plate", "Assembler", ("Iron Plate", "Wire"))),
+ "Rotor": (
+ Recipe("Rotor", "Assembler", ("Iron Rod", "Screw"), minimal_belt_speed=2, handcraftable=True),
+ Recipe("Copper Rotor", "Assembler", ("Copper Sheet", "Screw"), minimal_belt_speed=3),
+ Recipe("Steel Rotor", "Assembler", ("Steel Pipe", "Wire"))),
+ "Stator": (
+ Recipe("Stator", "Assembler", ("Steel Pipe", "Wire"), handcraftable=True),
+ Recipe("Quickwire Stator", "Assembler", ("Steel Pipe", "Quickwire"))),
+ "Plastic": (
+ Recipe("Plastic", "Refinery", ("Crude Oil", ), additional_outputs=("Heavy Oil Residue", )),
+ Recipe("Residual Plastic", "Refinery", ("Polymer Resin", "Water")),
+ Recipe("Recycled Plastic", "Refinery", ("Rubber", "Fuel"))),
+ "Rubber": (
+ Recipe("Rubber", "Refinery", ("Crude Oil", ), additional_outputs=("Heavy Oil Residue", )),
+ Recipe("Residual Rubber", "Refinery", ("Polymer Resin", "Water")),
+ Recipe("Recycled Rubber", "Refinery", ("Plastic", "Fuel"))),
+ "Iron Plate": (
+ Recipe("Iron Plate", "Constructor", ("Iron Ingot", )),
+ Recipe("Coated Iron Plate", "Assembler", ("Iron Ingot", "Plastic"), minimal_belt_speed=2),
+ Recipe("Steel Cast Plate", "Foundry", ("Iron Ingot", "Steel Ingot"))),
+ "Iron Rod": (
+ Recipe("Iron Rod", "Constructor", ("Iron Ingot", )),
+ Recipe("Steel Rod", "Constructor", ("Steel Ingot", )),
+ Recipe("Aluminum Rod", "Constructor", ("Aluminum Ingot", ))),
+ "Screw": (
+ Recipe("Screw", "Constructor", ("Iron Rod", )),
+ Recipe("Cast Screw", "Constructor", ("Iron Ingot", )),
+ Recipe("Steel Screw", "Constructor", ("Steel Beam", ), minimal_belt_speed=3)),
+ "Wire": (
+ Recipe("Wire", "Constructor", ("Copper Ingot", )),
+ Recipe("Fused Wire", "Assembler", ("Copper Ingot", "Caterium Ingot"), minimal_belt_speed=2),
+ Recipe("Iron Wire", "Constructor", ("Iron Ingot", )),
+ Recipe("Caterium Wire", "Constructor", ("Caterium Ingot", ), minimal_belt_speed=2)),
+ "Cable": (
+ Recipe("Cable", "Constructor", ("Wire", ), handcraftable=True, implicitly_unlocked=True),
+ Recipe("Coated Cable", "Refinery", ("Wire", "Heavy Oil Residue"), minimal_belt_speed=2),
+ Recipe("Insulated Cable", "Assembler", ("Wire", "Rubber"), minimal_belt_speed=2),
+ Recipe("Quickwire Cable", "Assembler", ("Quickwire", "Rubber"))),
+ "Quickwire": (
+ Recipe("Quickwire", "Constructor", ("Caterium Ingot", ), handcraftable=True),
+ Recipe("Fused Quickwire", "Assembler", ("Caterium Ingot", "Copper Ingot"), minimal_belt_speed=2)),
+ "Copper Sheet": (
+ Recipe("Copper Sheet", "Constructor", ("Copper Ingot", ), handcraftable=True),
+ Recipe("Steamed Copper Sheet", "Refinery", ("Copper Ingot", "Water"))),
+ "Steel Pipe": (
+ Recipe("Steel Pipe", "Constructor", ("Steel Ingot", ), handcraftable=True),
+ Recipe("Iron Pipe", "Constructor", ("Iron Ingot", ), minimal_belt_speed=2),
+ Recipe("Molded Steel Pipe", "Foundry", ("Steel Ingot", "Concrete"))),
+ "Steel Beam": (
+ Recipe("Steel Beam", "Constructor", ("Steel Ingot", ), handcraftable=True),
+ Recipe("Aluminum Beam", "Constructor", ("Aluminum Ingot", ), minimal_phase=2),
+ Recipe("Molded Beam", "Foundry", ("Steel Ingot", "Concrete"), minimal_belt_speed=2)),
+ "Heavy Oil Residue": (
+ Recipe("Heavy Oil Residue", "Refinery", ("Crude Oil", ), additional_outputs=("Polymer Resin", )),
+ Recipe("Plastic", "Refinery", ("Crude Oil", ), additional_outputs=("Plastic", )),
+ Recipe("Rubber", "Refinery", ("Crude Oil", ), additional_outputs=("Rubber", )),
+ Recipe("Polymer Resin", "Refinery", ("Crude Oil", ), additional_outputs=("Polymer Resin", ), minimal_belt_speed=3)),
+ "Polymer Resin": (
+ Recipe("Polymer Resin", "Refinery", ("Crude Oil", ), additional_outputs=("Heavy Oil Residue", ), minimal_belt_speed=2),
+ Recipe("Fuel", "Refinery", ("Crude Oil", ), additional_outputs=("Fuel", )),
+ Recipe("Heavy Oil Residue", "Refinery", ("Crude Oil", ), additional_outputs=("Heavy Oil Residue", ), minimal_belt_speed=3)),
+ "Fuel": (
+ Recipe("Fuel", "Refinery", ("Crude Oil", ), additional_outputs=("Polymer Resin", )),
+ Recipe("Diluted Fuel", "Blender", ("Heavy Oil Residue", "Water"), minimal_phase=2),
+ Recipe("Residual Fuel", "Refinery", ("Heavy Oil Residue", ))),
+ "Concrete": (
+ Recipe("Concrete", "Constructor", ("Limestone", )),
+ Recipe("Fine Concrete", "Assembler", ("Limestone", "Silica")),
+ Recipe("Rubber Concrete", "Assembler", ("Limestone", "Rubber")),
+ Recipe("Wet Concrete", "Refinery", ("Limestone", "Water"), minimal_belt_speed=2)),
+ "Silica": (
+ Recipe("Silica", "Constructor", ("Raw Quartz", ), handcraftable=True),
+ Recipe("Alumina Solution", "Refinery", ("Bauxite", "Water"), additional_outputs=("Alumina Solution", ), minimal_belt_speed=2, minimal_phase=2),
+ Recipe("Cheap Silica", "Assembler", ("Raw Quartz", "Limestone")),
+ Recipe("Distilled Silica", "Blender", ("Dissolved Silica", "Limestone", "Water"), additional_outputs=("Water", ), minimal_phase=2)),
+ "Dissolved Silica": (
+ Recipe("Quartz Purification", "Refinery", ("Raw Quartz", "Nitric Acid"), additional_outputs=("Quartz Crystal", ), minimal_belt_speed=2, minimal_phase=2), ),
+ "Quartz Crystal": (
+ Recipe("Quartz Crystal", "Constructor", ("Raw Quartz", ), handcraftable=True),
+ Recipe("Pure Quartz Crystal", "Refinery", ("Raw Quartz", "Water"), minimal_belt_speed=2),
+ Recipe("Fused Quartz Crystal", "Foundry", ("Raw Quartz", "Coal"), minimal_belt_speed=2),
+ Recipe("Quartz Purification", "Refinery", ("Raw Quartz", "Nitric Acid"), additional_outputs=("Dissolved Silica", ), minimal_belt_speed=2, minimal_phase=2)),
+ "Iron Ingot": (
+ Recipe("Iron Ingot", "Smelter", ("Iron Ore", )),
+ Recipe("Pure Iron Ingot", "Refinery", ("Iron Ore", "Water"), minimal_belt_speed=2),
+ Recipe("Iron Alloy Ingot", "Foundry", ("Iron Ore", "Copper Ore")),
+ Recipe("Basic Iron Ingot", "Foundry", ("Iron Ore", "Limestone")),
+ Recipe("Leached Iron ingot", "Refinery", ("Iron Ore", "Sulfuric Acid"), minimal_belt_speed=2)),
+ "Steel Ingot": (
+ Recipe("Steel Ingot", "Foundry", ("Iron Ore", "Coal"), handcraftable=True),
+ Recipe("Coke Steel Ingot", "Foundry", ("Iron Ore", "Petroleum Coke"), minimal_belt_speed=2),
+ Recipe("Compacted Steel Ingot", "Foundry", ("Iron Ore", "Compacted Coal")),
+ Recipe("Solid Steel Ingot", "Foundry", ("Iron Ingot", "Coal"))),
+ "Copper Ingot": (
+ Recipe("Copper Ingot", "Smelter", ("Copper Ore", )),
+ Recipe("Copper Alloy Ingot", "Foundry", ("Copper Ore", "Iron Ore"), minimal_belt_speed=2),
+ Recipe("Pure Copper Ingot", "Refinery", ("Copper Ore", "Water")),
+ Recipe("Leached Copper Ingot", "Refinery", ("Copper Ore", "Sulfuric Acid"), minimal_belt_speed=2),
+ Recipe("Tempered Copper Ingot", "Foundry", ("Copper Ore", "Petroleum Coke"))),
+ "Caterium Ingot": (
+ Recipe("Caterium Ingot", "Smelter", ("Caterium Ore", ), handcraftable=True),
+ Recipe("Pure Caterium Ingot", "Refinery", ("Caterium Ore", "Water")),
+ Recipe("Leached Caterium Ingot", "Refinery", ("Caterium Ore", "Sulfuric Acid")),
+ Recipe("Tempered Caterium Ingot", "Foundry", ("Caterium Ore", "Petroleum Coke"))),
+ "Petroleum Coke": (
+ Recipe("Petroleum Coke", "Refinery", ("Heavy Oil Residue", ), minimal_belt_speed=2), ),
+ "Compacted Coal": (
+ Recipe("Compacted Coal", "Assembler", ("Coal", "Sulfur")), ),
+ "Motor": (
+ Recipe("Motor", "Assembler", ("Rotor", "Stator"), handcraftable=True),
+ Recipe("Rigor Motor", "Manufacturer", ("Rotor", "Stator", "Crystal Oscillator")),
+ Recipe("Electric Motor", "Assembler", ("Electromagnetic Control Rod", "Rotor"))),
+ "Modular Frame": (
+ Recipe("Modular Frame", "Assembler", ("Reinforced Iron Plate", "Iron Rod"), handcraftable=True),
+ Recipe("Bolted Frame", "Assembler", ("Reinforced Iron Plate", "Screw"), minimal_belt_speed=3),
+ Recipe("Steeled Frame", "Assembler", ("Reinforced Iron Plate", "Steel Pipe"))),
+ "Heavy Modular Frame": (
+ Recipe("Heavy Modular Frame", "Manufacturer", ("Modular Frame", "Steel Pipe", "Encased Industrial Beam", "Screw"), minimal_belt_speed=3, handcraftable=True),
+ Recipe("Heavy Flexible Frame", "Manufacturer", ("Modular Frame", "Encased Industrial Beam", "Rubber", "Screw"), minimal_belt_speed=4),
+ Recipe("Heavy Encased Frame", "Manufacturer", ("Modular Frame", "Encased Industrial Beam", "Steel Pipe", "Concrete"))),
+ "Encased Industrial Beam": (
+ Recipe("Encased Industrial Beam", "Assembler", ("Steel Beam", "Concrete"), handcraftable=True),
+ Recipe("Encased Industrial Pipe", "Assembler", ("Steel Pipe", "Concrete"))),
+ "Computer": (
+ Recipe("Computer", "Manufacturer", ("Circuit Board", "Cable", "Plastic"), minimal_belt_speed=3, handcraftable=True),
+ Recipe("Crystal Computer", "Assembler", ("Circuit Board", "Crystal Oscillator")),
+ Recipe("Caterium Computer", "Manufacturer", ("Circuit Board", "Quickwire", "Rubber"), minimal_belt_speed=2)),
+ "Circuit Board": (
+ Recipe("Circuit Board", "Assembler", ("Copper Sheet", "Plastic"), handcraftable=True),
+ Recipe("Electrode Circuit Board", "Assembler", ("Rubber", "Petroleum Coke")),
+ Recipe("Silicon Circuit Board", "Assembler", ("Copper Sheet", "Silica")),
+ Recipe("Caterium Circuit Board", "Assembler", ("Plastic", "Quickwire"))),
+ "Crystal Oscillator": (
+ Recipe("Crystal Oscillator", "Manufacturer", ("Quartz Crystal", "Cable", "Reinforced Iron Plate"), handcraftable=True),
+ Recipe("Insulated Crystal Oscillator", "Manufacturer", ("Quartz Crystal", "Rubber", "AI Limiter"))),
+ "AI Limiter": (
+ Recipe("AI Limiter", "Assembler", ("Copper Sheet", "Quickwire"), minimal_belt_speed=2, handcraftable=True),
+ Recipe("Plastic AI Limiter", "Assembler", ("Quickwire", "Plastic"), minimal_belt_speed=2)),
+ "Electromagnetic Control Rod": (
+ Recipe("Electromagnetic Control Rod", "Assembler", ("Stator", "AI Limiter"), handcraftable=True),
+ Recipe("Electromagnetic Connection Rod", "Assembler", ("Stator", "High-Speed Connector"))),
+ "High-Speed Connector": (
+ Recipe("High-Speed Connector", "Manufacturer", ("Quickwire", "Cable", "Circuit Board"), minimal_belt_speed=3, handcraftable=True),
+ Recipe("Silicon High-Speed Connector", "Manufacturer", ("Quickwire", "Silica", "Circuit Board"), minimal_belt_speed=2)),
+ "Smart Plating": (
+ Recipe("Smart Plating", "Assembler", ("Reinforced Iron Plate", "Rotor")),
+ Recipe("Plastic Smart Plating", "Manufacturer", ("Reinforced Iron Plate", "Rotor", "Plastic"))),
+ "Versatile Framework": (
+ Recipe("Versatile Framework", "Assembler", ("Modular Frame", "Steel Beam"), minimal_phase=2),
+ Recipe("Flexible Framework", "Manufacturer", ("Modular Frame", "Steel Beam", "Rubber"), minimal_phase=2)),
+ "Automated Wiring": (
+ Recipe("Automated Wiring", "Assembler", ("Stator", "Cable"), minimal_phase=2),
+ Recipe("Automated Speed Wiring", "Manufacturer", ("Stator", "Wire", "High-Speed Connector"), minimal_belt_speed=2, minimal_phase=2)),
+ "Modular Engine": (
+ Recipe("Modular Engine", "Manufacturer", ("Motor", "Rubber", "Smart Plating"), minimal_phase=3), ),
+ "Adaptive Control Unit": (
+ Recipe("Adaptive Control Unit", "Manufacturer", ("Automated Wiring", "Circuit Board", "Heavy Modular Frame", "Computer"), minimal_phase=3), ),
+ "Portable Miner": (
+ Recipe("Portable Miner", "Equipment Workshop", ("Iron Rod", "Iron Plate"), handcraftable=True, minimal_belt_speed=0, implicitly_unlocked=True),
+ Recipe("Automated Miner", "Assembler", ("Steel Pipe", "Iron Plate")), ),
+ "Alumina Solution": (
+ Recipe("Alumina Solution", "Refinery", ("Bauxite", "Water"), additional_outputs=("Silica", ), minimal_belt_speed=2, minimal_phase=2),
+ Recipe("Sloppy Alumina", "Refinery", ("Bauxite", "Water"), minimal_belt_speed=3, minimal_phase=2)),
+ "Aluminum Scrap": (
+ Recipe("Aluminum Scrap", "Refinery", ("Alumina Solution", "Coal"), additional_outputs=("Water", ), minimal_belt_speed=4, minimal_phase=2),
+ Recipe("Electrode Aluminum Scrap", "Refinery", ("Alumina Solution", "Petroleum Coke"), additional_outputs=("Water", ), minimal_belt_speed=4, minimal_phase=2),
+ Recipe("Instant Scrap", "Blender", ("Bauxite", "Coal", "Sulfuric Acid", "Water"), additional_outputs=("Water", ), minimal_belt_speed=3, minimal_phase=2)),
+ "Aluminum Ingot": (
+ Recipe("Aluminum Ingot", "Foundry", ("Aluminum Scrap", "Silica"), minimal_belt_speed=2, handcraftable=True, minimal_phase=2),
+ Recipe("Pure Aluminum Ingot", "Smelter", ("Aluminum Scrap", ), minimal_phase=2)),
+ "Alclad Aluminum Sheet": (
+ Recipe("Alclad Aluminum Sheet", "Assembler", ("Aluminum Ingot", "Copper Ingot"), handcraftable=True, minimal_phase=2), ),
+ "Aluminum Casing": (
+ Recipe("Aluminum Casing", "Constructor", ("Alclad Aluminum Sheet", ), handcraftable=True, minimal_phase=2),
+ Recipe("Alclad Casing", "Assembler", ("Aluminum Ingot", "Copper Ingot"), minimal_phase=2)),
+ "Heat Sink": (
+ Recipe("Heat Sink", "Assembler", ("Alclad Aluminum Sheet", "Silica"), minimal_belt_speed=2, handcraftable=True, minimal_phase=2),
+ Recipe("Heat Exchanger", "Assembler", ("Aluminum Casing", "Rubber"), minimal_belt_speed=3, minimal_phase=2)),
+ "Nitric Acid": (
+ Recipe("Nitric Acid", "Blender", ("Nitrogen Gas", "Water", "Iron Plate"), minimal_phase=2), ),
+ "Fused Modular Frame": (
+ Recipe("Fused Modular Frame", "Blender", ("Heavy Modular Frame", "Aluminum Casing", "Nitrogen Gas"), minimal_belt_speed=2, minimal_phase=2),
+ Recipe("Heat-Fused Frame", "Blender", ("Heavy Modular Frame", "Aluminum Ingot", "Nitric Acid", "Fuel"), minimal_belt_speed=3, minimal_phase=2)),
+ "Radio Control Unit": (
+ Recipe("Radio Control Unit", "Manufacturer", ("Aluminum Casing", "Crystal Oscillator", "Computer"), handcraftable=True, minimal_phase=2),
+ Recipe("Radio Connection Unit", "Manufacturer", ("Heat Sink", "High-Speed Connector", "Quartz Crystal"), minimal_phase=2),
+ Recipe("Radio Control System", "Manufacturer", ("Crystal Oscillator", "Circuit Board", "Aluminum Casing", "Rubber"), minimal_belt_speed=2, minimal_phase=2)),
+ "Pressure Conversion Cube": (
+ Recipe("Pressure Conversion Cube", "Assembler", ("Fused Modular Frame", "Radio Control Unit"), handcraftable=True, minimal_phase=2), ),
+ "Cooling System": (
+ Recipe("Cooling System", "Blender", ("Heat Sink", "Rubber", "Water", "Nitrogen Gas"), minimal_phase=2),
+ Recipe("Cooling Device", "Blender", ("Heat Sink", "Motor", "Nitrogen Gas"), minimal_phase=2)),
+ "Turbo Motor": (
+ Recipe("Turbo Motor", "Manufacturer", ("Cooling System", "Radio Control Unit", "Motor", "Rubber"), handcraftable=True, minimal_phase=2),
+ Recipe("Turbo Electric Motor", "Manufacturer", ("Motor", "Radio Control Unit", "Electromagnetic Control Rod", "Rotor"), minimal_phase=2),
+ Recipe("Turbo Pressure Motor", "Manufacturer", ("Motor", "Pressure Conversion Cube", "Packaged Nitrogen Gas", "Stator"), minimal_phase=2)),
+ "Battery": (
+ Recipe("Battery", "Blender", ("Sulfuric Acid", "Alumina Solution", "Aluminum Casing"), additional_outputs=("Water", ), minimal_phase=2),
+ Recipe("Classic Battery", "Manufacturer", ("Sulfur", "Alclad Aluminum Sheet", "Plastic", "Wire"), minimal_belt_speed=2, minimal_phase=2)),
+ "Supercomputer": (
+ Recipe("Supercomputer", "Manufacturer", ("Computer", "AI Limiter", "High-Speed Connector", "Plastic"), handcraftable=True, minimal_phase=2),
+ Recipe("OC Supercomputer", "Assembler", ("Radio Control Unit", "Cooling System"), minimal_phase=2),
+ Recipe("Super-State Computer", "Manufacturer", ("Computer", "Electromagnetic Control Rod", "Battery", "Wire"), minimal_phase=2)),
+ "Sulfuric Acid": (
+ Recipe("Sulfuric Acid", "Refinery", ("Sulfur", "Water")), ),
+ "Encased Uranium Cell": (
+ Recipe("Encased Uranium Cell", "Blender", ("Uranium", "Concrete", "Sulfuric Acid"), additional_outputs=("Sulfuric Acid", )),
+ Recipe("Infused Uranium Cell", "Manufacturer", ("Uranium", "Silica", "Sulfur", "Quickwire"), minimal_belt_speed=2)),
+ "Uranium Fuel Rod": (
+ Recipe("Uranium Fuel Rod", "Manufacturer", ("Encased Uranium Cell", "Encased Industrial Beam", "Electromagnetic Control Rod")),
+ Recipe("Uranium Fuel Unit", "Manufacturer", ("Encased Uranium Cell", "Electromagnetic Control Rod", "Crystal Oscillator", "Rotor"))),
+ "Non-fissile Uranium": (
+ Recipe("Non-fissile Uranium", "Blender", ("Uranium Waste", "Silica", "Nitric Acid", "Sulfuric Acid"), additional_outputs=("Water", )),
+ Recipe("Fertile Uranium", "Blender", ("Uranium", "Uranium Waste", "Nitric Acid", "Sulfuric Acid"), additional_outputs=("Water", ), minimal_belt_speed=2)),
+ "Plutonium Pellet": (
+ Recipe("Plutonium Pellet", "Particle Accelerator", ("Non-fissile Uranium", "Uranium Waste"), minimal_belt_speed=2), ),
+ "Encased Plutonium Cell": (
+ Recipe("Encased Plutonium Cell", "Assembler", ("Plutonium Pellet", "Concrete")),
+ Recipe("Instant Plutonium Cell", "Particle Accelerator", ("Non-fissile Uranium", "Aluminum Casing"), minimal_belt_speed=2)),
+ "Plutonium Fuel Rod": (
+ Recipe("Plutonium Fuel Rod", "Manufacturer", ("Encased Plutonium Cell", "Steel Beam", "Electromagnetic Control Rod", "Heat Sink")),
+ Recipe("Plutonium Fuel Unit", "Assembler", ("Encased Plutonium Cell", "Pressure Conversion Cube"))),
+ "Gas Filter": (
+ Recipe("Gas Filter", "Manufacturer", ("Coal", "Rubber", "Fabric"), handcraftable=True), ),
+ "Iodine-Infused Filter": (
+ Recipe("Iodine-Infused Filter", "Manufacturer", ("Gas Filter", "Quickwire", "Aluminum Casing"), handcraftable=True, minimal_phase=2), ),
+ "Hazmat Suit": (
+ Recipe("Hazmat Suit", "Equipment Workshop", ("Rubber", "Plastic", "Fabric", "Alclad Aluminum Sheet"), handcraftable=True, minimal_phase=2), ),
+ "Assembly Director System": (
+ Recipe("Assembly Director System", "Assembler", ("Adaptive Control Unit", "Supercomputer"), minimal_phase=4), ),
+ "Magnetic Field Generator": (
+ Recipe("Magnetic Field Generator", "Assembler", ("Versatile Framework", "Electromagnetic Control Rod"), minimal_phase=4), ),
+ "Copper Powder": (
+ Recipe("Copper Powder", "Constructor", ("Copper Ingot", ), handcraftable=True), ),
+ "Nuclear Pasta": (
+ Recipe("Nuclear Pasta", "Particle Accelerator", ("Copper Powder", "Pressure Conversion Cube"), minimal_phase=2), ),
+ "Thermal Propulsion Rocket": (
+ Recipe("Thermal Propulsion Rocket", "Manufacturer", ("Modular Engine", "Turbo Motor", "Cooling System", "Fused Modular Frame"), minimal_phase=4), ),
+ "Alien Protein": (
+ Recipe("Hatcher Protein", "Constructor", ("Hatcher Remains", ), handcraftable=True),
+ Recipe("Hog Protein", "Constructor", ("Hog Remains", ), handcraftable=True),
+ Recipe("Spitter Protein", "Constructor", ("Plasma Spitter Remains", ), handcraftable=True),
+ Recipe("Stinger Protein", "Constructor", ("Stinger Remains", ), handcraftable=True)),
+ "Biomass": (
+ Recipe("Biomass (Leaves)", "Constructor", ("Leaves", ), minimal_belt_speed=2, handcraftable=True, implicitly_unlocked=True),
+ Recipe("Biomass (Wood)", "Constructor", ("Wood", ), minimal_belt_speed=4, handcraftable=True, implicitly_unlocked=True),
+ Recipe("Biomass (Mycelia)", "Constructor", ("Mycelia", ), minimal_belt_speed=3, handcraftable=True),
+ Recipe("Biomass (Alien Protein)", "Constructor", ("Alien Protein", ), minimal_belt_speed=4, handcraftable=True)),
+ "Fabric": (
+ Recipe("Fabric", "Assembler", ("Biomass", "Mycelia"), handcraftable=True, minimal_belt_speed=2),
+ Recipe("Polyester Fabric", "Refinery", ("Polymer Resin", "Water"))),
+ "Solid Biofuel": (
+ Recipe("Solid Biofuel", "Constructor", ("Biomass", ), minimal_belt_speed=2, handcraftable=True), ),
+ "Liquid Biofuel": (
+ Recipe("Liquid Biofuel", "Refinery", ("Solid Biofuel", "Water"), minimal_belt_speed=2), ),
+ "Empty Canister": (
+ Recipe("Empty Canister", "Constructor", ("Plastic", ), handcraftable=True),
+ Recipe("Coated Iron Canister", "Assembler", ("Iron Plate", "Copper Sheet")),
+ Recipe("Steel Canister", "Constructor", ("Steel Ingot", ))),
+ "Empty Fluid Tank": (
+ Recipe("Empty Fluid Tank", "Constructor", ("Aluminum Ingot", ), handcraftable=True, minimal_phase=2), ),
+ "Packaged Alumina Solution": (
+ Recipe("Packaged Alumina Solution", "Packager", ("Alumina Solution", "Empty Canister"), minimal_belt_speed=2), ),
+ "Packaged Fuel": (
+ Recipe("Packaged Fuel", "Packager", ("Fuel", "Empty Canister")),
+ Recipe("Diluted Packaged Fuel", "Refinery", ("Heavy Oil Residue", "Packaged Water"))),
+ "Packaged Heavy Oil Residue": (
+ Recipe("Packaged Heavy Oil Residue", "Packager", ("Heavy Oil Residue", "Empty Canister")), ),
+ "Packaged Liquid Biofuel": (
+ Recipe("Packaged Liquid Biofuel", "Packager", ("Liquid Biofuel", "Empty Canister")), ),
+ "Packaged Nitric Acid": (
+ Recipe("Packaged Nitric Acid", "Packager", ("Nitric Acid", "Empty Fluid Tank")), ),
+ "Packaged Nitrogen Gas": (
+ Recipe("Packaged Nitrogen Gas", "Packager", ("Nitrogen Gas", "Empty Fluid Tank")), ),
+ "Packaged Oil": (
+ Recipe("Packaged Oil", "Packager", ("Crude Oil", "Empty Canister")), ),
+ "Packaged Sulfuric Acid": (
+ Recipe("Packaged Sulfuric Acid", "Packager", ("Sulfuric Acid", "Empty Canister")), ),
+ "Packaged Turbofuel": (
+ Recipe("Packaged Turbofuel", "Packager", ("Turbofuel", "Empty Canister")), ),
+ "Packaged Water": (
+ Recipe("Packaged Water", "Packager", ("Water", "Empty Canister")), ),
+ "Turbofuel": (
+ Recipe("Turbofuel", "Refinery", ("Fuel", "Compacted Coal")),
+ Recipe("Turbo Heavy Fuel", "Refinery", ("Heavy Oil Residue", "Compacted Coal")),
+ Recipe("Turbo Blend Fuel", "Blender", ("Fuel", "Heavy Oil Residue", "Sulfur", "Petroleum Coke"), minimal_phase=2)),
+ "Gas Mask": (
+ Recipe("Gas Mask", "Equipment Workshop", ("Rubber", "Plastic", "Fabric"), handcraftable=True, minimal_belt_speed=0), ),
+ "Alien DNA Capsule": (
+ Recipe("Alien DNA Capsule", "Constructor", ("Alien Protein", ), handcraftable=True), ),
+ "Black Powder": (
+ Recipe("Black Powder", "Equipment Workshop", ("Coal", "Sulfur"), handcraftable=True),
+ Recipe("Fine Black Powder", "Assembler", ("Sulfur", "Compacted Coal"))),
+ "Smokeless Powder": (
+ Recipe("Smokeless Powder", "Refinery", ("Black Powder", "Heavy Oil Residue")), ),
+ "Rifle Ammo": (
+ Recipe("Rifle Ammo", "Assembler", ("Copper Sheet", "Smokeless Powder"), handcraftable=True, minimal_belt_speed=2), ),
+ "Iron Rebar": (
+ Recipe("Iron Rebar", "Constructor", ("Iron Rod", ), handcraftable=True), ),
+ "Nobelisk": (
+ Recipe("Nobelisk", "Assembler", ("Black Powder", "Steel Pipe"), handcraftable=True), ),
+ "Power Shard": (
+ Recipe("Power Shard (1)", "Constructor", ("Blue Power Slug", ), handcraftable=True),
+ Recipe("Power Shard (2)", "Constructor", ("Yellow Power Slug", ), handcraftable=True),
+ Recipe("Power Shard (5)", "Constructor", ("Purple Power Slug", ), handcraftable=True),
+ Recipe("Synthetic Power Shard", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Time Crystal", "Dark Matter Crystal", "Quartz Crystal"), minimal_phase=4)), # 1.0
+ "Object Scanner": (
+ Recipe("Object Scanner", "Equipment Workshop", ("Reinforced Iron Plate", "Wire", "Screw"), handcraftable=True), ),
+ "Xeno-Zapper": (
+ Recipe("Xeno-Zapper", "Equipment Workshop", ("Iron Rod", "Reinforced Iron Plate", "Cable", "Wire"), handcraftable=True, implicitly_unlocked=True), ),
+
+# 1.0
+ "Rocket Fuel": (
+ Recipe("Rocket Fuel", "Blender", ("Turbofuel", "Nitric Acid"), additional_outputs=("Compacted Coal", ), minimal_phase=2),
+ Recipe("Nitro Rocket Fuel", "Blender", ("Fuel", "Nitrogen Gas", "Sulfur", "Coal"), minimal_belt_speed=2, additional_outputs=("Compacted Coal", ), minimal_phase=2)),
+ "Ionized Fuel": (
+ Recipe("Ionized Fuel", "Refinery", ("Rocket Fuel", "Power Shard"), additional_outputs=("Compacted Coal", )),
+ Recipe("Dark-Ion Fuel", "Blender", ("Packaged Rocket Fuel", "Dark Matter Crystal"), minimal_belt_speed=3, additional_outputs=("Compacted Coal", ), minimal_phase=4)),
+ "Packaged Rocket Fuel": (
+ Recipe("Packaged Rocket Fuel", "Packager", ("Rocket Fuel", "Empty Fluid Tank")), ),
+ "Packaged Ionized Fuel": (
+ Recipe("Packaged Ionized Fuel", "Packager", ("Ionized Fuel", "Empty Fluid Tank")), ),
+ "Diamonds": (
+ Recipe("Diamonds", "Particle Accelerator", ("Coal", ), minimal_belt_speed=5),
+ Recipe("Cloudy Diamonds", "Particle Accelerator", ("Coal", "Limestone"), minimal_belt_speed=4),
+ Recipe("Oil-Based Diamonds", "Particle Accelerator", ("Crude Oil", )),
+ Recipe("Petroleum Diamonds", "Particle Accelerator", ("Petroleum Coke", ), minimal_belt_speed=5),
+ Recipe("Pink Diamonds", "Converter", ("Coal", "Quartz Crystal"), minimal_belt_speed=2),
+ Recipe("Turbo Diamonds", "Particle Accelerator", ("Coal", "Packaged Turbofuel"), minimal_belt_speed=5)),
+ "Time Crystal": (
+ Recipe("Time Crystal", "Converter", ("Diamonds", )), ),
+ "Ficsite Ingot": (
+ Recipe("Ficsite Ingot (Aluminum)", "Converter", ("Reanimated SAM", "Aluminum Ingot"), minimal_belt_speed=2),
+ Recipe("Ficsite Ingot (Caterium)", "Converter", ("Reanimated SAM", "Caterium Ingot")),
+ Recipe("Ficsite Ingot (Iron)", "Converter", ("Reanimated SAM", "Iron Ingot"), minimal_belt_speed=3)),
+ "Ficsite Trigon": (
+ Recipe("Ficsite Trigon", "Constructor", ("Ficsite Ingot", ), handcraftable=True), ),
+ "SAM": (
+ Recipe("SAM", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
+ "Reanimated SAM": (
+ Recipe("Reanimated SAM", "Constructor", ("SAM", ), handcraftable=True, minimal_belt_speed=2), ),
+ "SAM Fluctuator": (
+ Recipe("SAM Fluctuator", "Manufacturer", ("Reanimated SAM", "Steel Pipe", "Wire"), handcraftable=True), ),
+ "Excited Photonic Matter": (
+ Recipe("Excited Photonic Matter", "Converter", implicitly_unlocked=True), ),
+ "Dark Matter Crystal": (
+ Recipe("Dark Matter Crystal", "Particle Accelerator", ("Diamonds", ), additional_outputs=("Dark Matter Residue", )),
+ Recipe("Dark Matter Crystallization", "Particle Accelerator", additional_outputs=("Dark Matter Residue", )),
+ Recipe("Dark Matter Trap", "Particle Accelerator", ("Time Crystal", ), additional_outputs=("Dark Matter Residue", ))),
+ "Singularity Cell": (
+ Recipe("Singularity Cell", "Manufacturer", ("Nuclear Pasta", "Dark Matter Crystal", "Iron Plate", "Concrete"), minimal_belt_speed=3), ),
+ "Biochemical Sculptor": (
+ Recipe("Biochemical Sculptor", "Blender", ("Assembly Director System", "Ficsite Trigon", "Water"), minimal_phase=5), ),
+ "Ballistic Warp Drive": (
+ Recipe("Ballistic Warp Drive", "Manufacturer", ("Thermal Propulsion Rocket", "Singularity Cell", "Superposition Oscillator", "Dark Matter Crystal"), minimal_phase=5), ),
+
+ # All Quantum Encoder recipes have `Dark Matter Residue` set as an input, this hack makes the logic make sure you can get rid of it
+ "Dark Matter Residue": (
+ # Recipe("Ficsonium", "Particle Accelerator", ("Plutonium Waste", "Singularity Cell"), additional_outputs=("Ficsonium", )),
+ Recipe("Dark Matter Crystal", "Particle Accelerator", ("Diamonds", ), additional_outputs=("Dark Matter Crystal", )),
+ Recipe("Dark Matter Crystallization", "Particle Accelerator", additional_outputs=("Dark Matter Crystal", )),
+ Recipe("Dark Matter Trap", "Particle Accelerator", ("Time Crystal", ), additional_outputs=("Dark Matter Crystal", )),
+ Recipe("Dark Matter Residue", "Converter", ("Reanimated SAM", ))),
+ "Superposition Oscillator": (
+ Recipe("Superposition Oscillator", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Dark Matter Crystal", "Crystal Oscillator", "Alclad Aluminum Sheet")), ),
+ "Neural-Quantum Processor": (
+ Recipe("Neural-Quantum Processor", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Time Crystal", "Supercomputer", "Ficsite Trigon")), ),
+ "AI Expansion Server": (
+ Recipe("AI Expansion Server", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Magnetic Field Generator", "Neural-Quantum Processor", "Superposition Oscillator"), minimal_phase=5), ),
+ ###
+# 1.0
+ # For exclusion logic
+ "Hoverpack": (
+ Recipe("Hoverpack", "Equipment Workshop", ("Motor", "Heavy Modular Frame", "Computer", "Alclad Aluminum Sheet")), ),
+ "Turbo Rifle Ammo": (
+ Recipe("Turbo Rifle Ammo", "Blender", ("Rifle Ammo", "Aluminum Casing", "Turbofuel"), minimal_belt_speed=3),
+ Recipe("Turbo Rifle Ammo (Packaged)", "Manufacturer", ("Rifle Ammo", "Aluminum Casing", "Packaged Turbofuel"), minimal_belt_speed=2, minimal_phase=2)),
+ "Homing Rifle Ammo": (
+ Recipe("Homing Rifle Ammo", "Assembler", ("Rifle Ammo", "High-Speed Connector")), ),
+ ###
+ }
+
+ buildings: dict[str, Building] = {
+ "Constructor": Building("Constructor", ("Reinforced Iron Plate", "Cable"), PowerInfrastructureLevel.Basic),
+ "Assembler": Building("Assembler", ("Reinforced Iron Plate", "Iron Rod", "Cable"), PowerInfrastructureLevel.Basic), # Simplified , used ("Reinforced Iron Plate", "Rotor", "Cable")
+ "Manufacturer": Building("Manufacturer", ("Motor", "Heavy Modular Frame", "Cable", "Plastic"), PowerInfrastructureLevel.Advanced),
+ "Packager": Building("Packager", ("Steel Beam", "Rubber", "Plastic"), PowerInfrastructureLevel.Basic),
+ "Refinery": Building("Refinery", ("Motor", "Encased Industrial Beam", "Steel Pipe", "Copper Sheet"), PowerInfrastructureLevel.Automated),
+ "Blender": Building("Blender", ("Motor", "Heavy Modular Frame", "Aluminum Casing", "Radio Control Unit"), PowerInfrastructureLevel.Advanced),
+ "Particle Accelerator": Building("Particle Accelerator", ("Radio Control Unit", "Electromagnetic Control Rod", "Supercomputer", "Cooling System", "Fused Modular Frame", "Turbo Motor"), PowerInfrastructureLevel.Complex),
+ "Biomass Burner": Building("Biomass Burner", ("Iron Plate", "Iron Rod", "Wire"), implicitly_unlocked=True),
+ "Coal Generator": Building("Coal Generator", ("Reinforced Iron Plate", "Rotor", "Cable")),
+ "Fuel Generator": Building("Fuel Generator", ("Computer", "Heavy Modular Frame", "Motor", "Rubber", "Quickwire")),
+ "Geothermal Generator": Building("Geothermal Generator", ("Motor", "Modular Frame", "High-Speed Connector", "Copper Sheet", "Wire")),
+ "Nuclear Power Plant": Building("Nuclear Power Plant", ("Concrete", "Heavy Modular Frame", "Supercomputer", "Cable", "Alclad Aluminum Sheet")),
+ "Miner Mk.1": Building("Miner Mk.1", ("Iron Plate", "Concrete"), PowerInfrastructureLevel.Basic, implicitly_unlocked=True),
+ "Miner Mk.2": Building("Miner Mk.2", ("Encased Industrial Beam", "Steel Pipe", "Modular Frame"), PowerInfrastructureLevel.Automated, can_produce=False),
+ "Miner Mk.3": Building("Miner Mk.3", ("Steel Pipe", "Supercomputer", "Fused Modular Frame", "Turbo Motor"), PowerInfrastructureLevel.Advanced, can_produce=False),
+ "Oil Extractor": Building("Oil Extractor", ("Motor", "Encased Industrial Beam", "Cable")),
+ "Water Extractor": Building("Water Extractor", ("Copper Sheet", "Reinforced Iron Plate", "Rotor")),
+ "Smelter": Building("Smelter", ("Iron Rod", "Wire"), PowerInfrastructureLevel.Basic),
+ "Foundry": Building("Foundry", ("Reinforced Iron Plate", "Iron Rod", "Concrete"), PowerInfrastructureLevel.Basic), # Simplified, used ("Modular Frame", "Rotor", "Concrete")
+ "Resource Well Pressurizer": Building("Resource Well Pressurizer", ("Steel Pipe", "Heavy Modular Frame", "Motor", "Reinforced Iron Plate", "Copper Sheet", "Steel Beam"), PowerInfrastructureLevel.Advanced), # Simplified, used ("Radio Control Unit", "Heavy Modular Frame", "Motor", "Alclad Aluminum Sheet", "Rubber", "Steel Beam", "Aluminum Casing")
+ "Equipment Workshop": Building("Equipment Workshop", ("Iron Plate", "Iron Rod"), implicitly_unlocked=True),
+ "AWESOME Sink": Building("AWESOME Sink", ("Reinforced Iron Plate", "Cable", "Concrete"), can_produce=False),
+ "AWESOME Shop": Building("AWESOME Shop", ("Screw", "Iron Plate", "Cable"), can_produce=False),
+ "MAM": Building("MAM", ("Reinforced Iron Plate", "Wire", "Cable"), can_produce=False),
+ "Pipes Mk.1": Building("Pipes Mk.1", ("Copper Sheet", "Iron Plate", "Concrete"), can_produce=False),
+ "Pipes Mk.2": Building("Pipes Mk.2", ("Copper Sheet", "Plastic", "Iron Plate", "Concrete"), can_produce=False),
+ "Pipeline Pump Mk.1": Building("Pipeline Pump Mk.1", ("Copper Sheet", "Rotor"), can_produce=False),
+ "Pipeline Pump Mk.2": Building("Pipeline Pump Mk.2", ("Motor", "Encased Industrial Beam", "Plastic"), can_produce=False),
+ "Conveyor Merger": Building("Conveyor Merger", ("Iron Plate", "Iron Rod"), can_produce=False),
+ "Conveyor Splitter": Building("Conveyor Splitter", ("Iron Plate", "Cable"), can_produce=False),
+ "Conveyor Mk.1": Building("Conveyor Mk.1", ("Iron Plate", "Iron Rod", "Concrete"), can_produce=False, implicitly_unlocked=True),
+ "Conveyor Mk.2": Building("Conveyor Mk.2", ("Reinforced Iron Plate", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
+ "Conveyor Mk.3": Building("Conveyor Mk.3", ("Steel Beam", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
+ "Conveyor Mk.4": Building("Conveyor Mk.4", ("Encased Industrial Beam", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
+ "Conveyor Mk.5": Building("Conveyor Mk.5", ("Alclad Aluminum Sheet", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
+ "Conveyor Mk.6": Building("Conveyor Mk.6", ("Ficsite Trigon", "Time Crystal", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
+ "Power Pole Mk.1": Building("Power Pole Mk.1", ("Iron Plate", "Iron Rod", "Concrete"), can_produce=False, implicitly_unlocked=True),
+ # higher level power poles arent in logic (yet)
+ # "Power Pole Mk.2": Building("Power Pole Mk.2", ("Quickwire", "Iron Rod", "Concrete"), False),
+ # "Power Pole Mk.3": Building("Power Pole Mk.3", ("High-Speed Connector", "Steel Pipe", "Rubber"), False),
+ "Power Storage": Building("Power Storage", ("Wire", "Modular Frame", "Stator"), can_produce=False),
+ "Foundation": Building("Foundation", ("Iron Plate", "Concrete"), can_produce=False),
+ "Walls Orange": Building("Walls Orange", ("Iron Plate", "Concrete"), can_produce=False),
+ "Space Elevator": Building("Space Elevator", ("Concrete", "Iron Plate", "Iron Rod", "Wire"), can_produce=False),
+
+# 1.0
+ "Converter": Building("Converter", ("Fused Modular Frame", "Cooling System", "Radio Control Unit", "SAM Fluctuator"), PowerInfrastructureLevel.Complex),
+ "Quantum Encoder": Building("Quantum Encoder", ("Turbo Motor", "Supercomputer", "Cooling System", "Time Crystal", "Ficsite Trigon"), PowerInfrastructureLevel.Complex),
+ "Alien Power Augmenter": Building("Alien Power Augmenter", ("SAM Fluctuator", "Cable", "Encased Industrial Beam", "Motor", "Computer")),
+# 1.0
+
+ # For exclusion logic
+ "Portal": Building("Portal", ("Turbo Motor", "Radio Control Unit", "Superposition Oscillator", "SAM Fluctuator", "Ficsite Trigon", "Singularity Cell"), PowerInfrastructureLevel.Advanced),
+ ###
+ }
+
+ requirement_per_powerlevel: dict[PowerInfrastructureLevel, tuple[Recipe, ...]] = {
+ # no need to polute the logic by including higher level recipes based on previus recipes
+ PowerInfrastructureLevel.Basic: (
+ Recipe("Biomass Power (Biomass)", "Biomass Burner", ("Biomass", ), implicitly_unlocked=True),
+ ),
+ PowerInfrastructureLevel.Automated: (
+ Recipe("Biomass Power (Solid Biofuel)", "Biomass Burner", ("Solid Biofuel", ), implicitly_unlocked=True),
+ # Recipe("Coal Generator Power (Petroleum Coke)", "Coal Generator", ("Petroleum Coke", "Water"), implicitly_unlocked=True),
+ Recipe("Coal Generator Power (Coal)", "Coal Generator", ("Coal", "Water"), implicitly_unlocked=True),
+ ),
+ PowerInfrastructureLevel.Advanced: (
+ Recipe("Coal Generator Power (Compacted Coal)", "Coal Generator", ("Compacted Coal", "Water"), implicitly_unlocked=True),
+ Recipe("Geothermal Generator Power", "Geothermal Generator", implicitly_unlocked=True),
+ Recipe("Fuel Generator Power (Liquid Biofuel)", "Fuel Generator", ("Liquid Biofuel", ), implicitly_unlocked=True),
+ Recipe("Fuel Generator Power (Fuel)", "Fuel Generator", ("Fuel", ), implicitly_unlocked=True),
+ Recipe("Alien Power Augmenter Power", "Alien Power Augmenter", implicitly_unlocked=True),
+ ),
+ PowerInfrastructureLevel.Complex: (
+ Recipe("Fuel Generator Power (Turbofuel)", "Fuel Generator", ("Turbofuel", ), implicitly_unlocked=True),
+ # Recipe("Fuel Generator Power (Rocket Fuel)", "Fuel Generator", ("Rocket Fuel", ), implicitly_unlocked=True),
+ # Recipe("Fuel Generator Power (Ionized Fuel)", "Fuel Generator", ("Ionized Fuel", ), implicitly_unlocked=True),
+ Recipe("Nuclear Power Plant Power (Uranium)", "Nuclear Power Plant", ("Uranium Fuel Rod", "Water"), implicitly_unlocked=True),
+ # Recipe("Nuclear Power Plant Power (Plutonium)", "Nuclear Power Plant", ("Plutonium Fuel Rod", "Water"), implicitly_unlocked=True),
+ # Recipe("Nuclear Power Plant Power (Ficsonium)", "Nuclear Power Plant", ("Ficsonium Fuel Rod", "Water"), implicitly_unlocked=True),
+ # Recipe("Alien Power Augmenter Power (Alien Power Matrix)", "Alien Power Augmenter", ("Alien Power Matrix"), implicitly_unlocked=True),
+ )
+ }
+
+ slots_per_milestone: int = 8
+
+ hub_layout: tuple[tuple[dict[str, int], ...], ...] = (
+ # Regenerate via /Script/Engine.Blueprint'/Archipelago/Debug/CC_BuildHubData.CC_BuildHubData'
+ ( # Tier 1
+ {"Concrete": 200, "Iron Plate": 100, "Iron Rod": 100, }, # Schematic: Base Building (Schematic_1-1_C)
+ {"Iron Plate": 150, "Iron Rod": 150, "Wire": 300, }, # Schematic: Logistics (Schematic_1-2_C)
+ {"Wire": 300, "Screw": 300, "Iron Plate": 100, }, # Schematic: Field Research (Schematic_1-3_C)
+ {"Wire": 100, "Screw": 200, "Concrete": 200, }, # Schematic: Archipelago Additional Tier1 (Schem_ApExtraTier1_C)
+ ),
+ ( # Tier 2
+ {"Cable": 200, "Iron Rod": 200, "Screw": 500, "Iron Plate": 300, }, # Schematic: Part Assembly (Schematic_2-1_C)
+ {"Screw": 500, "Cable": 100, "Concrete": 100, }, # Schematic: Obstacle Clearing (Schematic_2-2_C)
+ {"Rotor": 50, "Iron Plate": 300, "Cable": 150, }, # Schematic: Jump Pads (Schematic_2-3_C)
+ {"Concrete": 400, "Wire": 500, "Iron Rod": 200, "Iron Plate": 200, }, # Schematic: Resource Sink Bonus Program (Schematic_2-5_C)
+ {"Reinforced Iron Plate": 50, "Concrete": 200, "Iron Rod": 300, "Iron Plate": 300, }, # Schematic: Logistics Mk.2 (Schematic_3-2_C)
+ ),
+ ( # Tier 3
+ {"Reinforced Iron Plate": 150, "Rotor": 50, "Cable": 500, }, # Schematic: Coal Power (Schematic_3-1_C)
+ {"Modular Frame": 25, "Rotor": 100, "Cable": 100, "Iron Plate": 400, }, # Schematic: Vehicular Transport (Schematic_3-3_C)
+ {"Modular Frame": 50, "Rotor": 150, "Concrete": 500, "Wire": 1000, }, # Schematic: Basic Steel Production (Schematic_3-4_C)
+ {"Reinforced Iron Plate": 100, "Iron Rod": 600, "Wire": 1500, }, # Schematic: Improved Melee Combat (Schematic_4-2_C)
+ ),
+ ( # Tier 4
+ {"Modular Frame": 100, "Steel Beam": 200, "Cable": 500, "Concrete": 1000, }, # Schematic: FICSIT Blueprints (Schematic_4-5_C)
+ {"Steel Beam": 200, "Steel Pipe": 200, "Reinforced Iron Plate": 400, }, # Schematic: Logistics Mk.3 (Schematic_5-3_C)
+ {"Steel Pipe": 100, "Modular Frame": 100, "Rotor": 200, "Concrete": 500, }, # Schematic: Advanced Steel Production (Schematic_4-1_C)
+ {"Encased Industrial Beam": 50, "Steel Beam": 100, "Modular Frame": 200, "Wire": 2000, }, # Schematic: Expanded Power Infrastructure (Schematic_4-3_C)
+ {"Copper Sheet": 500, "Steel Pipe": 300, "Encased Industrial Beam": 50, }, # Schematic: Hypertubes (Schematic_4-4_C)
+ ),
+ ( # Tier 5
+ {"Motor": 50, "Cable": 100, "Iron Plate": 500, }, # Something jetpack
+ {"Motor": 50, "Encased Industrial Beam": 100, "Steel Pipe": 500, "Copper Sheet": 500, }, # Schematic: Oil Processing (Schematic_5-1_C)
+ {"Rubber": 200, "Encased Industrial Beam": 300, "Modular Frame": 400, },
+ {"Plastic": 200, "Steel Beam": 400, "Copper Sheet": 1000, }, # Schematic: Alternative Fluid Transport (Schematic_5-4_C)
+ {"Motor": 100, "Encased Industrial Beam": 100, "Plastic": 200, "Rubber": 200, }, # Schematic: Industrial Manufacturing (Schematic_5-2_C)
+ ),
+ ( # Tier 6
+ {"Motor": 200, "Modular Frame": 200, "Plastic": 400, "Cable": 1000, }, # Schematic: Industrial Manufacturing (Schematic_5-2_C)
+ {"Motor": 250, "Encased Industrial Beam": 500, "Steel Pipe": 1000, "Steel Beam": 1000, }, # Schematic: Monorail Train Technology (Schematic_6-3_C)
+ {"Computer": 50, "Steel Pipe": 4000, "Copper Sheet": 1000, },
+ {"Heavy Modular Frame": 50, "Plastic": 1000, "Rubber": 1000, }, # Schematic: Pipeline Engineering Mk.2 (Schematic_6-5_C)
+ {"Heavy Modular Frame": 50, "Computer": 100, "Rubber": 400, "Concrete": 1000, },
+ ),
+ ( # Tier 7
+ {"Computer": 100, "Heavy Modular Frame": 100, "Motor": 250, "Rubber": 500, }, # Schematic: Bauxite Refinement (Schematic_7-1_C)
+ {"Alclad Aluminum Sheet": 100, "Heavy Modular Frame": 100, "Computer": 100, "Motor": 250, }, # Schematic: Hover Pack (Schematic_8-3_C)
+ {"Alclad Aluminum Sheet": 200, "Encased Industrial Beam": 400, "Reinforced Iron Plate": 600, }, # Schematic: Logistics Mk.5 (Schematic_7-2_C)
+ {"Gas Filter": 50, "Aluminum Casing": 100, "Quickwire": 500, }, # Schematic: Hazmat Suit (Schematic_7-3_C)
+ {"Alclad Aluminum Sheet": 200, "Aluminum Casing": 400, "Computer": 200, "Plastic": 1000, }, # Schematic: Aeronautical Engineering (Schematic_7-4_C)
+ ),
+ ( # Tier 8
+ {"Radio Control Unit": 50, "Alclad Aluminum Sheet": 100, "Aluminum Casing": 200, "Motor": 300, }, # Schematic: Aeronautical Engineering (Schematic_7-4_C)
+ {"Supercomputer": 50, "Heavy Modular Frame": 200, "Cable": 1000, "Concrete": 2000, }, # Schematic: Nuclear Power (Schematic_8-1_C)
+ {"Radio Control Unit": 50, "Aluminum Casing": 200, "Alclad Aluminum Sheet": 400, "Wire": 3000, }, # Schematic: Advanced Aluminum Production (Schematic_8-2_C)
+ {"Fused Modular Frame": 50, "Supercomputer": 100, "Steel Pipe": 1000, }, # Schematic: Leading-edge Production (Schematic_8-4_C)
+ {"Turbo Motor": 50, "Fused Modular Frame": 100, "Cooling System": 200, "Quickwire": 2500, }, # Schematic: Particle Enrichment (Schematic_8-5_C)
+ ),
+ ( # Tier 9
+ {"Fused Modular Frame": 100, "Radio Control Unit": 250, "Cooling System": 500, },
+ {"Time Crystal": 50, "Ficsite Trigon": 100, "Turbo Motor": 200, "Supercomputer": 400, },
+ {"Neural-Quantum Processor": 100, "Time Crystal": 250, "Ficsite Trigon": 500, "Fused Modular Frame": 500, },
+ {"Superposition Oscillator": 100, "Turbo Motor": 250, "Radio Control Unit": 500, "SAM Fluctuator": 1000, },
+ {"Time Crystal": 250, "Ficsite Trigon": 250, "Alclad Aluminum Sheet": 500, "Iron Plate": 10000, },
+ ),
+ )
+
+ # Values from /Game/FactoryGame/Schematics/Progression/BP_GamePhaseManager.BP_GamePhaseManager
+ space_elevator_phases: tuple[dict[str, int], ...] = (
+ {"Smart Plating": 50},
+ {"Smart Plating": 500, "Versatile Framework": 500, "Automated Wiring": 100},
+ {"Versatile Framework": 2500, "Modular Engine": 500, "Adaptive Control Unit": 100},
+ {"Assembly Director System": 4000, "Magnetic Field Generator": 4000, "Nuclear Pasta": 1000, "Thermal Propulsion Rocket": 1000},
+ {"Nuclear Pasta": 1000, "Biochemical Sculptor": 1000, "AI Expansion Server": 256, "Ballistic Warp Drive": 200}
+ )
+
+ # Do not regenerate as format got changed
+ # Regenerate via /Script/Engine.Blueprint'/Archipelago/Debug/CC_BuildMamData.CC_BuildMamData'
+ man_trees: dict[str, MamTree] = {
+ "Alien Organisms": MamTree(("Hog Remains", "Plasma Spitter Remains", "Stinger Remains", "Hatcher Remains"), ( # Alien Organisms (BPD_ResearchTree_AlienOrganisms_C)
+ MamNode("Inflated Pocket Dimension", {"Alien Protein": 3, "Cable": 1000, }, depends_on=("Bio-Organic Properties", )), # (Research_AOrgans_3_C)
+ MamNode("Hostile Organism Detection", {"Alien DNA Capsule": 10, "Crystal Oscillator": 5, "High-Speed Connector": 5, }, depends_on=("Bio-Organic Properties", )), # (Research_AOrganisms_2_C)
+ MamNode("Expanded Toolbelt", {"Alien DNA Capsule": 5, "Steel Beam": 500, }, depends_on=("Inflated Pocket Dimension", )), # (Research_ACarapace_3_C)
+ MamNode("Bio-Organic Properties", {"Alien Protein": 5, }, depends_on=("Spitter Research", "Hog Research", "Hatcher Research", "Stinger Research")), # (Research_AO_DNACapsule_C)
+ MamNode("Stinger Research", {"Stinger Remains": 1, }, depends_on=tuple()), # (Research_AO_Stinger_C)
+ MamNode("Hatcher Research", {"Hatcher Remains": 1, }, depends_on=tuple()), # (Research_AO_Hatcher_C)
+ MamNode("Hog Research", {"Hog Remains": 1, }, depends_on=tuple()), # (Research_ACarapace_0_C)
+ MamNode("Spitter Research", {"Plasma Spitter Remains": 1, }, depends_on=tuple()), # (Research_AOrgans_0_C)
+ MamNode("Structural Analysis", {"Alien DNA Capsule": 5, "Iron Rod": 100, }, depends_on=("Bio-Organic Properties", )), # (Research_AO_Pre_Rebar_C)
+ MamNode("Protein Inhaler", {"Alien Protein": 2, "Beryl Nut": 20, "Rotor": 50, }, depends_on=("Bio-Organic Properties", )), # (Research_AOrgans_2_C)
+ MamNode("The Rebar Gun", {"Rotor": 25, "Reinforced Iron Plate": 50, "Screw": 500, }, depends_on=("Structural Analysis", )), # (Research_ACarapace_2_C)
+ )),
+ # 1.0
+ "Alien Technology": MamTree(("SAM", "Mercer Sphere", "Somersloop"), (
+ MamNode("SAM Analysis", {"SAM": 10, }, depends_on=tuple()),
+ MamNode("SAM Reanimation", {"SAM": 20, }, depends_on=("SAM Analysis",)),
+ MamNode("SAM Fluctuator", {"Reanimated SAM": 10, "Steel Pipe": 100, "Wire": 200, }, depends_on=("SAM Reanimation",)),
+ MamNode("Mercer Sphere Analysis", {"Mercer Sphere": 1, }, depends_on=tuple()),
+ MamNode("Dimensional Depot", {"Mercer Sphere": 1, "SAM Fluctuator": 11, }, depends_on=("Mercer Sphere Analysis", "SAM Fluctuator")),
+ MamNode("Manual Depot Uploader", {"Mercer Sphere": 3, "Computer": 17, "SAM Fluctuator": 19, }, depends_on=("Dimensional Depot",)),
+ MamNode("Depot Expansion (200%)", {"Mercer Sphere": 3, "SAM Fluctuator": 47, }, depends_on=("Dimensional Depot",)),
+ MamNode("Depot Expansion (300%)", {"Mercer Sphere": 7, "SAM Fluctuator": 103, }, depends_on=("Depot Expansion (200%)",)),
+ MamNode("Depot Expansion (400%)", {"Mercer Sphere": 13, "SAM Fluctuator": 151, }, depends_on=("Depot Expansion (300%)",)),
+ MamNode("Depot Expansion (500%)", {"Mercer Sphere": 23, "SAM Fluctuator": 199, }, depends_on=("Depot Expansion (400%)",)),
+ MamNode("Upload Upgrade: 30/min", {"Mercer Sphere": 3, "SAM Fluctuator": 47, }, depends_on=("Dimensional Depot",)),
+ MamNode("Upload Upgrade: 60/min", {"Mercer Sphere": 7, "SAM Fluctuator": 103, }, depends_on=("Upload Upgrade: 30/min",)),
+ MamNode("Upload Upgrade: 120/min", {"Mercer Sphere": 13, "SAM Fluctuator": 151, }, depends_on=("Upload Upgrade: 60/min",)),
+ MamNode("Upload Upgrade: 240/min", {"Mercer Sphere": 23, "SAM Fluctuator": 199, }, depends_on=("Upload Upgrade: 120/min",)),
+ MamNode("Somersloop Analysis", {"Somersloop": 1, }, depends_on=tuple()),
+ MamNode("Alien Energy Harvesting", {"SAM Fluctuator": 10, }, depends_on=("Somersloop Analysis", "SAM Fluctuator")),
+ MamNode("Production Amplifier", {"Somersloop": 1, "SAM Fluctuator": 100, "Circuit Board": 50, }, depends_on=("Alien Energy Harvesting",)),
+ MamNode("Power Augmenter", {"Somersloop": 1, "SAM Fluctuator": 100, "Computer": 50, }, depends_on=("Alien Energy Harvesting",)),
+ MamNode("Alien Power Matrix", {"Singularity Cell": 50, "Power Shard": 100, "SAM Fluctuator": 500, }, depends_on=("Power Augmenter",), minimal_phase=4),
+ )),
+ # 1.0
+ "Caterium": MamTree(("Caterium Ore", ), ( # Caterium (BPD_ResearchTree_Caterium_C)
+ MamNode("Caterium Electronics", {"Quickwire": 100, }, depends_on=("Quickwire", )), # (Research_Caterium_3_C)
+ MamNode("Bullet Guidance System", {"High-Speed Connector": 10, "Rifle Ammo": 500, }, depends_on=("High-Speed Connector", )), # (Research_Caterium_6_3_C)
+ MamNode("High-Speed Connector", {"Quickwire": 500, "Plastic": 50, }, depends_on=("Caterium Electronics", )), # (Research_Caterium_5_C)
+ MamNode("Caterium", {"Caterium Ore": 10, }, depends_on=tuple()), # (Research_Caterium_0_C)
+ MamNode("Caterium Ingots", {"Caterium Ore": 50, }, depends_on=("Caterium", )), # (Research_Caterium_1_C)
+ MamNode("Quickwire", {"Caterium Ingot": 50, }, depends_on=("Caterium Ingots", )), # (Research_Caterium_2_C)
+ MamNode("Power Switch", {"Steel Beam": 100, "AI Limiter": 50, }, depends_on=("AI Limiter", )), # (Research_Caterium_4_1_2_C)
+ MamNode("Priority Power Switch", {"High-Speed Connector": 25, "Quickwire": 500, }, depends_on=("High-Speed Connector", )), # 1.0
+ MamNode("Power Poles Mk.2", {"Quickwire": 300, }, depends_on=("Caterium Electronics", )), # (Research_Caterium_4_2_C)
+ MamNode("AI Limiter", {"Quickwire": 200, "Copper Sheet": 50, }, depends_on=("Caterium Electronics", )), # (Research_Caterium_4_1_C)
+ MamNode("Smart Splitter", {"AI Limiter": 10, "Reinforced Iron Plate": 50, }, depends_on=("AI Limiter", )), # (Research_Caterium_4_1_1_C)
+ MamNode("Programmable Splitter", {"AI Limiter": 100, "Computer": 50, "Heavy Modular Frame": 50, }, depends_on=("AI Limiter", "High-Speed Connector")), # (Research_Caterium_7_1_C) # 1.0
+ MamNode("Zipline", {"Quickwire": 100, "Cable": 50, }, depends_on=("Quickwire", )), # (Research_Caterium_2_1_C)
+ MamNode("Geothermal Generator", {"High-Speed Connector": 100, "Quickwire": 1000, "Motor": 50, }, depends_on=("AI Limiter", "High-Speed Connector")), # (Research_Caterium_7_2_C) # 1.0
+ MamNode("Stun Rebar", {"Quickwire": 50, "Iron Rebar": 10, }, depends_on=("Quickwire", )), # (Research_Caterium_3_2_C)
+ MamNode("Power Poles Mk.3", {"High-Speed Connector": 50, "Steel Pipe": 200, }, depends_on=("Power Poles Mk.2", )), # (Research_Caterium_6_2_C) # 1.0
+ )),
+ "Mycelia": MamTree(("Mycelia", ), ( # Mycelia (BPD_ResearchTree_Mycelia_C)
+ MamNode("Therapeutic Inhaler", {"Mycelia": 15, "Bacon Agaric": 1, "Alien Protein": 1, }, depends_on=("Medical Properties", )), # (Research_Mycelia_6_C)
+ MamNode("Expanded Toolbelt", {"Fabric": 50, "Rotor": 100, }, depends_on=("Fabric", )), # (Research_Mycelia_7_C)
+ MamNode("Mycelia", {"Mycelia": 5, }, depends_on=tuple()), # (Research_Mycelia_1_C)
+ MamNode("Fabric", {"Mycelia": 25, "Biomass": 100, }, depends_on=("Mycelia", )), # (Research_Mycelia_2_C)
+ MamNode("Medical Properties", {"Mycelia": 25, "Stator": 10, }, depends_on=("Mycelia", )), # (Research_Mycelia_4_C)
+ MamNode("Toxic Cellular Modification", {"Nobelisk": 10, "Mycelia": 100, "Biomass": 200, }, depends_on=("Mycelia", )), # (Research_Mycelia_8_C)
+ MamNode("Vitamin Inhaler", {"Mycelia": 10, "Paleberry": 5, }, depends_on=("Medical Properties", )), # (Research_Mycelia_5_C)
+ MamNode("Parachute", {"Fabric": 10, "Cable": 50, }, depends_on=("Fabric", )), # (Research_Mycelia_3_C)
+ MamNode("Synthethic Polyester Fabric", {"Fabric": 25, "Polymer Resin": 100, }, depends_on=("Fabric", )), # (Research_Mycelia_2_1_C)
+ MamNode("Gas Mask", {"Coal": 10, "Fabric": 50, "Steel Pipe": 50, }, depends_on=("Fabric", )), # 1.0
+ )),
+ "Nutrients": MamTree(("Paleberry", "Beryl Nut", "Bacon Agaric"), ( # Nutrients (BPD_ResearchTree_Nutrients_C)
+ MamNode("Bacon Agaric", {"Bacon Agaric": 1, }, depends_on=tuple()), # (Research_Nutrients_2_C)
+ MamNode("Beryl Nut", {"Beryl Nut": 5, }, depends_on=tuple()), # (Research_Nutrients_1_C)
+ MamNode("Paleberry", {"Paleberry": 2, }, depends_on=tuple()), # (Research_Nutrients_0_C)
+ MamNode("Nutritional Processor", {"Modular Frame": 25, "Steel Pipe": 50, "Wire": 500, }, depends_on=("Beryl Nut", "Bacon Agaric", "Paleberry")), # (Research_Nutrients_3_C)
+ MamNode("Nutritional Inhaler", {"Bacon Agaric": 2, "Paleberry": 4, "Beryl Nut": 10, }, depends_on=("Nutritional Processor", )), # (Research_Nutrients_4_C)
+ )),
+ "Power Slugs": MamTree(("Blue Power Slug", ), ( # Power Slugs (BPD_ResearchTree_PowerSlugs_C)
+ MamNode("Slug Scanning", {"Iron Rod": 50, "Wire": 100, "Screw": 200, }, depends_on=("Blue Power Slugs", )), # (Research_PowerSlugs_3_C)
+ MamNode("Blue Power Slugs", {"Blue Power Slug": 1, }, depends_on=tuple()), # (Research_PowerSlugs_1_C)
+ MamNode("Yellow Power Shards", {"Yellow Power Slug": 1, "Rotor": 25, "Cable": 100, }, depends_on=("Blue Power Slugs", )), # (Research_PowerSlugs_4_C)
+ MamNode("Purple Power Shards", {"Purple Power Slug": 1, "Modular Frame": 25, "Copper Sheet": 100, }, depends_on=("Yellow Power Shards", )), # (Research_PowerSlugs_5_C)
+ MamNode("Overclock Production", {"Power Shard": 1, "Iron Plate": 50, "Wire": 50, }, depends_on=("Blue Power Slugs", )), # (Research_PowerSlugs_2_C)
+ MamNode("Synthetic Power Shards", {"Power Shard": 10, "Time Crystal": 100, "Quartz Crystal": 200, }, depends_on=("Purple Power Shards", ), minimal_phase=4), # 1.0
+ )),
+ "Quartz": MamTree(("Raw Quartz", ), ( # Quartz (BPD_ResearchTree_Quartz_C)
+ MamNode("Crystal Oscillator", {"Quartz Crystal": 100, "Reinforced Iron Plate": 50, }, depends_on=("Quartz Crystals", )), # (Research_Quartz_2_C)
+ MamNode("Quartz Crystals", {"Raw Quartz": 20, }, depends_on=("Quartz", )), # (Research_Quartz_1_1_C)
+ MamNode("Quartz", {"Raw Quartz": 10, }, depends_on=tuple()), # (Research_Quartz_0_C)
+ MamNode("Shatter Rebar", {"Quartz Crystal": 30, "Iron Rebar": 150, }, depends_on=("Quartz Crystals", )), # (Research_Quartz_2_1_C)
+ MamNode("Silica", {"Raw Quartz": 20, }, depends_on=("Quartz", )), # (Research_Quartz_1_2_C)
+ MamNode("Explosive Resonance Application", {"Crystal Oscillator": 5, "Nobelisk": 100, }, depends_on=("Crystal Oscillator", )), # (Research_Quartz_3_4_C)
+ MamNode("Blade Runners", {"Silica": 50, "Modular Frame": 10, }, depends_on=("Silica", )), # (Research_Caterium_4_3_C)
+ MamNode("The Explorer", {"Crystal Oscillator": 10, "Modular Frame": 100, }, depends_on=("Crystal Oscillator", )), # (Research_Quartz_3_1_C)
+ MamNode("Material Resonance Screening", {"Crystal Oscillator": 15, "Reinforced Iron Plate": 100, }, depends_on=("Crystal Oscillator", )), # (Research_Quartz_PriorityMerger_C)
+ MamNode("Radio Signal Scanning", {"Crystal Oscillator": 100, "Motor": 100, "Object Scanner": 1, }, depends_on=("Crystal Oscillator", )), # (Research_Quartz_4_1_C)
+ MamNode("Inflated Pocket Dimension", {"Silica": 200, }, depends_on=("Silica", )), # (Research_Caterium_3_1_C)
+ MamNode("Radar Technology", {"Crystal Oscillator": 50, "Heavy Modular Frame": 50, "Computer": 50, }, depends_on=("Crystal Oscillator", )), # (Research_Quartz_4_C) # 1.0
+ )),
+ "Sulfur": MamTree(("Sulfur", ), ( # Sulfur (BPD_ResearchTree_Sulfur_C)
+ MamNode("The Nobelisk Detonator", {"Black Powder": 50, "Steel Pipe": 100, "Cable": 200, }, depends_on=("Black Powder", )), # (Research_Sulfur_3_1_C)
+ MamNode("Smokeless Powder", {"Black Powder": 100, "Plastic": 50, }, depends_on=("Black Powder", )), # (Research_Sulfur_3_C)
+ MamNode("Sulfur", {"Sulfur": 10, }, depends_on=tuple()), # (Research_Sulfur_0_C)
+ MamNode("Inflated Pocket Dimension", {"Smokeless Powder": 50, "Computer": 50, }, depends_on=("Nuclear Deterrent Development", "Turbo Rifle Ammo", "Cluster Nobelisk", "The Rifle")), # (Research_Sulfur_6_C)
+ MamNode("The Rifle", {"Smokeless Powder": 50, "Motor": 100, "Rubber": 200, }, depends_on=("Smokeless Powder", )), # (Research_Sulfur_4_1_C)
+ MamNode("Compacted Coal", {"Hard Drive": 1, "Sulfur": 25, "Coal": 25, }, depends_on=("Experimental Power Generation", )), # (Research_Sulfur_CompactedCoal_C)
+ MamNode("Black Powder", {"Sulfur": 50, "Coal": 25, }, depends_on=("Sulfur", )), # (Research_Sulfur_1_C)
+ MamNode("Explosive Rebar", {"Smokeless Powder": 200, "Iron Rebar": 200, "Steel Beam": 200, }, depends_on=("Smokeless Powder", )), # (Research_Sulfur_4_2_C)
+ MamNode("Cluster Nobelisk", {"Smokeless Powder": 100, "Nobelisk": 200, }, depends_on=("Smokeless Powder", )), # (Research_Sulfur_4_C)
+ MamNode("Experimental Power Generation", {"Sulfur": 25, "Modular Frame": 50, "Rotor": 100, }, depends_on=("Sulfur", )), # (Research_Sulfur_ExperimentalPower_C)
+ MamNode("Turbo Rifle Ammo", {"Rifle Ammo": 1000, "Packaged Turbofuel": 50, "Aluminum Casing": 100, }, depends_on=("The Rifle", ), minimal_phase=2), # (Research_Sulfur_5_2_C) # 1.0
+ MamNode("Turbo Fuel", {"Hard Drive": 1, "Compacted Coal": 15, "Packaged Fuel": 50, }, depends_on=("Experimental Power Generation", )), # (Research_Sulfur_TurboFuel_C)
+ MamNode("Expanded Toolbelt", {"Black Powder": 100, "Encased Industrial Beam": 50, }, depends_on=("Black Powder", )), # (Research_Sulfur_5_C)
+ MamNode("Nuclear Deterrent Development", {"Nobelisk": 500, "Encased Uranium Cell": 10, "AI Limiter": 100, }, depends_on=("Cluster Nobelisk", ), minimal_phase=2), # (Research_Sulfur_5_1_C) # 1.0
+ MamNode("Rocket Fuel", {"Hard Drive": 1, "Empty Fluid Tank": 10, "Packaged Turbofuel": 100, }, depends_on=("Turbo Fuel", ), minimal_phase=3), # 1.0
+ MamNode("Ionized Fuel", {"Hard Drive": 1, "Power Shard": 100, "Packaged Rocket Fuel": 200, }, depends_on=("Turbo Fuel", ), minimal_phase=4), # 1.0
+ ))
+ }
+
+ drop_pods: list[DropPodData] = [
+ # Regenerate via /Script/Engine.Blueprint'/Archipelago/Debug/CC_BuildDropPodLocations.CC_BuildDropPodLocations'
+ DropPodData(-29068, -22640, 17384, "Encased Industrial Beam", 0), # Unlocks with: 4 x Desc_SteelPlateReinforced_C
+ DropPodData(-33340, 5176, 23519, "Crystal Oscillator", 0), # Unlocks with: 5 x Desc_CrystalOscillator_C
+ DropPodData(8680, -41777, 13053, "Steel Pipe", 0), # Unlocks with: 7 x Desc_SteelPipe_C
+ DropPodData(35082, 16211, 22759, "Supercomputer", 0), # Unlocks with: 7 x Desc_ComputerSuper_C
+ # DropPodData(-3511, 62314, 22109, "Quantum Computer", 0), # Unlocks with: 1 x Desc_ComputerQuantum_C
+ DropPodData(66652, -13642, 13420, "Encased Industrial Beam", 50), # Unlocks with: 3 x Desc_SteelPlateReinforced_C
+ DropPodData(55247, -51316, 14363, None, 25), # Unlocks with: (No Item)
+ DropPodData(-4706, -76301, 13618, "Black Powder", 0), # Unlocks with: 10 x Desc_Gunpowder_C
+ DropPodData(-40194, 62956, 26261, "Superposition Oscillator", 138), # Unlocks with: 2 x Desc_QuantumOscillator_C
+ DropPodData(80980, -44100, 8303, "Rotor", 0), # Unlocks with: 3 x Desc_Rotor_C
+ DropPodData(-56144, -72864, 27668, "Quartz Crystal", 0), # Unlocks with: 2 x Desc_QuartzCrystal_C
+ DropPodData(-95228, 6970, 25142, "High-Speed Connector", 112), # Unlocks with: 11 x Desc_HighSpeedConnector_C
+ DropPodData(-89284, -50630, 16019, None, 50), # Unlocks with: (No Item)
+ DropPodData(-94708, 40337, 19832, "Heat Sink", 138), # Unlocks with: 2 x Desc_AluminumPlateReinforced_C
+ DropPodData(94267, 47237, 9435, "Motor", 0), # Unlocks with: 1 x Desc_Motor_C
+ DropPodData(87739, -62975, 13444, None, 30), # Unlocks with: (No Item)
+ DropPodData(12249, 114177, 26721, "AI Limiter", 267), # Unlocks with: 9 x Desc_CircuitBoardHighSpeed_C
+ DropPodData(115978, 21424, 15519, None, 0), # Unlocks with: (No Item)
+ DropPodData(-78236, 90857, 20305, "Radio Control Unit", 0), # Unlocks with: 6 x Desc_ModularFrameLightweight_C
+ DropPodData(-35359, 116594, 21827, "Turbo Motor", 0), # Unlocks with: 6 x Desc_MotorLightweight_C
+ DropPodData(111479, -54515, 17081, "Stator", 20), # Unlocks with: 1 x Desc_Stator_C
+ DropPodData(121061, 45324, 17373, None, 0), # Unlocks with: (No Item)
+ DropPodData(125497, -34949, 8220, None, 50), # Unlocks with: (No Item)
+ DropPodData(-26327, -129047, 7780, "Modular Frame", 0), # Unlocks with: 1 x Desc_ModularFrame_C
+ DropPodData(21373, 132336, 2510, "Motor", 20), # Unlocks with: 2 x Desc_Motor_C
+ DropPodData(17807, -136922, 13621, "Rotor", 0), # Unlocks with: 1 x Desc_Rotor_C
+ DropPodData(-118480, 74929, 16995, None, 420), # Unlocks with: (No Item)
+ DropPodData(94940, 105482, 9860, "Heavy Modular Frame", 0), # Unlocks with: 1 x Desc_ModularFrameHeavy_C
+ DropPodData(-129115, 60165, 4800, None, 53), # Unlocks with: (No Item)
+ DropPodData(-142000, 23970, 32660, None, 0), # Unlocks with: (No Item)
+ DropPodData(46048, 141933, 13064, None, 40), # Unlocks with: (No Item)
+ DropPodData(144456, 36294, 17301, "Circuit Board", 48), # Unlocks with: 20 x Desc_CircuitBoard_C
+ DropPodData(-43144, 145820, 7472, "Modular Frame", 0), # Unlocks with: 5 x Desc_ModularFrame_C
+ DropPodData(-108774, 107811, 10154, "Crystal Oscillator", 0), # Unlocks with: 1 x Desc_CrystalOscillator_C
+ DropPodData(-56987, -144603, 2072, "Rotor", 10), # Unlocks with: 1 x Desc_Rotor_C
+ DropPodData(-152676, 33864, 19283, None, 256), # Unlocks with: (No Item)
+ DropPodData(90313, 129583, 9112, "Crystal Oscillator", 20), # Unlocks with: 2 x Desc_CrystalOscillator_C
+ DropPodData(111212, -113040, 12036, "Screw", 10), # Unlocks with: 15 x Desc_IronScrew_C
+ DropPodData(-157077, -6312, 25128, "Turbo Motor", 0), # Unlocks with: 8 x Desc_MotorLightweight_C
+ DropPodData(157249, -40206, 13694, "High-Speed Connector", 0), # Unlocks with: 2 x Desc_HighSpeedConnector_C
+ DropPodData(-151842, 72468, 9945, "Encased Industrial Beam", 0), # Unlocks with: 3 x Desc_SteelPlateReinforced_C
+ DropPodData(64696, 156038, 14067, "Modular Frame", 0), # Unlocks with: 6 x Desc_ModularFrame_C
+ DropPodData(-157080, -67028, 11766, "Rotor", 0), # Unlocks with: 4 x Desc_Rotor_C
+ DropPodData(170057, -10579, 18823, None, 50), # Unlocks with: (No Item)
+ DropPodData(143671, 92573, 24990, "Crystal Oscillator", 20), # Unlocks with: 2 x Desc_CrystalOscillator_C
+ DropPodData(127215, -116866, -1397, "Rubber", 0), # Unlocks with: 10 x Desc_Rubber_C
+ DropPodData(163999, 61333, 21481, "AI Limiter", 0), # Unlocks with: 3 x Desc_CircuitBoardHighSpeed_C
+ DropPodData(98306, -149781, 2552, None, 40), # Unlocks with: (No Item)
+ DropPodData(5302, -187090, -1608, None, 0), # Unlocks with: (No Item)
+ DropPodData(188304, 17059, 12949, None, 0), # Unlocks with: (No Item)
+ DropPodData(84256, -171122, -290, None, 0), # Unlocks with: (No Item)
+ DropPodData(191366, 37694, 5676, "Computer", 0), # Unlocks with: 4 x Desc_Computer_C
+ DropPodData(28695, 193441, 17459, "Quickwire", 0), # Unlocks with: 9 x Desc_HighSpeedWire_C
+ DropPodData(-146044, -137047, 2357, "Modular Frame", 0), # Unlocks with: 9 x Desc_ModularFrame_C
+ DropPodData(-200203, -17766, 12193, "Solid Biofuel", 0), # Unlocks with: 10 x Desc_Biofuel_C
+ DropPodData(47834, 195703, 2943, "Black Powder", 0), # Unlocks with: 4 x Desc_Gunpowder_C
+ DropPodData(198418, -41186, 13786, None, 0), # Unlocks with: (No Item)
+ DropPodData(-195756, -59210, -84, None, 30), # Unlocks with: (No Item)
+ DropPodData(-121994, 166916, -49, "Steel Beam", 20), # Unlocks with: 4 x Desc_SteelPlate_C
+ DropPodData(88323, 188913, 1420, None, 30), # Unlocks with: (No Item)
+ DropPodData(-123677, -167107, 29710, "Motor", 0), # Unlocks with: 4 x Desc_Motor_C
+ DropPodData(150633, 146698, 7727, "Crystal Oscillator", 20), # Unlocks with: 2 x Desc_CrystalOscillator_C
+ DropPodData(-55111, -204857, 7844, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
+ DropPodData(216096, -268, -1592, "Heat Sink", 0), # Unlocks with: 7 x Desc_AluminumPlateReinforced_C
+ DropPodData(159088, -145116, 23164, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
+ DropPodData(207683, -68352, 3927, "Encased Industrial Beam", 20), # Unlocks with: 27 x Desc_SteelPlateReinforced_C
+ DropPodData(-189258, 116331, -1764, None, 0), # Unlocks with: (No Item)
+ DropPodData(46951, 221859, 5917, None, 20), # Unlocks with: (No Item)
+ DropPodData(-9988, 227625, -1017, None, 40), # Unlocks with: (No Item)
+ DropPodData(232515, -20519, 8979, "Crystal Oscillator", 15), # Unlocks with: 2 x Desc_CrystalOscillator_C
+ DropPodData(232138, 27191, -1629, "Supercomputer", 0), # Unlocks with: 5 x Desc_ComputerSuper_C
+ DropPodData(-135, -237257, -1760, None, 0), # Unlocks with: (No Item)
+ DropPodData(-232498, -51432, -386, "Rotor", 0), # Unlocks with: 21 x Desc_Rotor_C
+ DropPodData(-238333, 17321, 19741, "Heat Sink", 0), # Unlocks with: 3 x Desc_AluminumPlateReinforced_C
+ DropPodData(200510, 131912, 6341, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
+ DropPodData(-108812, 214051, 3200, "Quickwire", 0), # Unlocks with: 1 x Desc_HighSpeedWire_C
+ DropPodData(232255, 79925, -1275, "Turbo Motor", 67), # Unlocks with: 2 x Desc_MotorLightweight_C
+ DropPodData(226418, 98109, 7339, None, 200), # Unlocks with: (No Item)
+ DropPodData(156569, 191767, -9312, "Rubber", 0), # Unlocks with: 4 x Desc_Rubber_C
+ DropPodData(44579, -244343, -874, None, 0), # Unlocks with: (No Item)
+ DropPodData(118349, 221905, -7063, "Encased Industrial Beam", 0), # Unlocks with: 6 x Desc_SteelPlateReinforced_C
+ # DropPodData(249919, 59534, 2430, "Quantum Computer", 0), # Unlocks with: 1 x Desc_ComputerQuantum_C
+ DropPodData(188233, 177201, 9608, "Quickwire", 0), # Unlocks with: 12 x Desc_HighSpeedWire_C
+ DropPodData(-174494, -197134, -1538, None, 30), # Unlocks with: (No Item)
+ DropPodData(-50655, -259272, -1667, None, 0), # Unlocks with: (No Item)
+ DropPodData(30383, 266975, -987, "Screw", 0), # Unlocks with: 12 x Desc_IronScrew_C
+ DropPodData(272715, 28087, -1586, "Supercomputer", 0), # Unlocks with: 2 x Desc_ComputerSuper_C
+ DropPodData(-152279, 229520, 1052, "Modular Frame", 0), # Unlocks with: 5 x Desc_ModularFrame_C
+ DropPodData(241532, 131343, 17157, None, 0), # Unlocks with: (No Item)
+ DropPodData(-259577, 105048, -1548, None, 0), # Unlocks with: (No Item)
+ DropPodData(275070, -52585, 5980, None, 0), # Unlocks with: (No Item)
+ DropPodData(-247303, -142348, 4524, "Rotor", 0), # Unlocks with: 4 x Desc_Rotor_C
+ DropPodData(261797, 124616, -2597, "AI Limiter", 73), # Unlocks with: 3 x Desc_CircuitBoardHighSpeed_C
+ DropPodData(187056, 223656, -3215, None, 42), # Unlocks with: (No Item)
+ DropPodData(293299, 51, 522, "Crystal Oscillator", 42), # Unlocks with: 8 x Desc_CrystalOscillator_C
+ DropPodData(219146, -199880, 6503, "Rotor", 0), # Unlocks with: 10 x Desc_Rotor_C
+ DropPodData(176423, 243273, -9780, "Motor", 19), # Unlocks with: 3 x Desc_Motor_C
+ DropPodData(291821, 74782, -1574, "Superposition Oscillator", 0), # Unlocks with: 5 x Desc_QuantumOscillator_C
+ DropPodData(-78884, 292640, -4763, "Modular Frame", 0), # Unlocks with: 5 x Desc_ModularFrame_C
+ DropPodData(174948, -276436, 21151, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
+ DropPodData(295166, -173139, 8083, None, 0), # Unlocks with: (No Item)
+ DropPodData(349295, -38831, -1485, "Motor", 0), # Unlocks with: 10 x Desc_Motor_C
+ DropPodData(360114, -106614, 11815, "Motor", 0), # Unlocks with: 35 x Desc_Motor_C
+ DropPodData(303169, -246169, 5487, None, 50), # Unlocks with: (No Item)
+ DropPodData(236508, -312236, 9971, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
+ DropPodData(360285, -217558, 3900, None, 70), # Unlocks with: (No Item)
+ DropPodData(366637, -303548, -7288, None, 0), # Unlocks with: (No Item)
+ ]
diff --git a/worlds/satisfactory/ItemData.py b/worlds/satisfactory/ItemData.py
new file mode 100644
index 000000000000..a56a14732309
--- /dev/null
+++ b/worlds/satisfactory/ItemData.py
@@ -0,0 +1,46 @@
+from enum import IntFlag
+from dataclasses import dataclass
+from BaseClasses import ItemClassification
+
+
+class ItemGroups(IntFlag):
+ Parts = 1 << 1
+ Equipment = 1 << 2
+ Ammo = 1 << 3
+ Recipe = 1 << 4
+ Building = 1 << 5
+ Trap = 1 << 6
+ Lights = 1 << 7
+ Foundations = 1 << 8
+ Transport = 1 << 9
+ Trains = 1 << 10
+ ConveyorMk1 = 1 << 11
+ ConveyorMk2 = 1 << 12
+ ConveyorMk3 = 1 << 13
+ ConveyorMk4 = 1 << 14
+ ConveyorMk5 = 1 << 15
+ ConveyorSupports = 1 << 16
+ PipesMk1 = 1 << 17
+ PipesMk2 = 1 << 18
+ PipelineSupports = 1 << 19
+ HyperTubes = 1 << 20
+ Signs = 1 << 21
+ Pilars = 1 << 22
+ Beams = 1 << 23
+ Walls = 1 << 24
+ Upgrades = 1 << 25
+ Vehicles = 1 << 26
+ Customizer = 1 << 27
+ ConveyorMk6 = 1 << 28
+ NeverExclude = 1 << 29
+
+
+@dataclass
+class ItemData:
+ """Represents an item in the pool, it could be a resource bundle, production recipe, trap, etc."""
+ category: ItemGroups
+ code: int
+ type: ItemClassification = ItemClassification.filler
+ count: int = 1
+ """How many of this item exists in the pool. 0 means none, but still defines the item so it can be added in the
+ starting inventory for example"""
diff --git a/worlds/satisfactory/Items.py b/worlds/satisfactory/Items.py
new file mode 100644
index 000000000000..d92a10cd2fd0
--- /dev/null
+++ b/worlds/satisfactory/Items.py
@@ -0,0 +1,991 @@
+from random import Random
+from typing import ClassVar, Optional
+from collections.abc import Sequence
+from BaseClasses import Item, ItemClassification as C
+from .GameLogic import GameLogic
+from .Options import SatisfactoryOptions
+from .ItemData import ItemData, ItemGroups as G
+from .CriticalPathCalculator import CriticalPathCalculator
+
+
+class Items:
+ item_data: ClassVar[dict[str, ItemData]] = {
+ # Resource Bundles
+ "Bundle: Adaptive Control Unit": ItemData(G.Parts, 1338000, count=0),
+ "Bundle: AI Limiter": ItemData(G.Parts, 1338001),
+ "Bundle: Alclad Aluminum Sheet": ItemData(G.Parts, 1338002),
+ "Bundle: Blue Power Slug": ItemData(G.Parts, 1338003),
+ "Bundle: Yellow Power Slug": ItemData(G.Parts, 1338004),
+ "Bundle: Alien Protein": ItemData(G.Parts, 1338005),
+ "Bundle: Purple Power Slug": ItemData(G.Parts, 1338006),
+ "Bundle: Aluminum Casing": ItemData(G.Parts, 1338007),
+ "Bundle: Aluminum Ingot": ItemData(G.Parts, 1338008),
+ "Bundle: Aluminum Scrap": ItemData(G.Parts, 1338009),
+ "Bundle: Assembly Director System": ItemData(G.Parts, 1338010, count=0),
+ "Bundle: Automated Wiring": ItemData(G.Parts, 1338011, count=0),
+ "Bundle: Battery": ItemData(G.Parts, 1338012),
+ "Bundle: Bauxite": ItemData(G.Parts, 1338013),
+ "Bundle: Neural-Quantum Processor": ItemData(G.Parts, 1338014), # 1.0
+ "Bundle: Biomass": ItemData(G.Parts, 1338015),
+ "Bundle: Black Powder": ItemData(G.Parts, 1338016),
+ "Bundle: Cable": ItemData(G.Parts, 1338017),
+ "Bundle: Caterium Ingot": ItemData(G.Parts, 1338018),
+ "Bundle: Caterium Ore": ItemData(G.Parts, 1338019),
+ "Bundle: Circuit Board": ItemData(G.Parts, 1338020),
+ "Bundle: Coal": ItemData(G.Parts, 1338021),
+ "Bundle: Singularity Cell": ItemData(G.Parts, 1338022), # 1.0
+ "Bundle: Compacted Coal": ItemData(G.Parts, 1338023),
+ "Bundle: Computer": ItemData(G.Parts, 1338024),
+ "Bundle: Concrete": ItemData(G.Parts, 1338025),
+ "Bundle: Cooling System": ItemData(G.Parts, 1338026),
+ "Bundle: Copper Ingot": ItemData(G.Parts, 1338027),
+ "Bundle: Copper Ore": ItemData(G.Parts, 1338028),
+ "Bundle: Copper Powder": ItemData(G.Parts, 1338029),
+ "Bundle: Copper Sheet": ItemData(G.Parts, 1338030),
+ "Bundle: Adequate Pioneering Statue": ItemData(G.Parts, 1338031),
+ "Bundle: Crystal Oscillator": ItemData(G.Parts, 1338032),
+ "Bundle: Electromagnetic Control Rod": ItemData(G.Parts, 1338033),
+ "Bundle: Empty Canister": ItemData(G.Parts, 1338034),
+ "Bundle: Empty Fluid Tank": ItemData(G.Parts, 1338035),
+ "Bundle: Encased Industrial Beam": ItemData(G.Parts, 1338036),
+ "Bundle: Encased Plutonium Cell": ItemData(G.Trap, 1338037, C.trap),
+ "Bundle: Encased Uranium Cell": ItemData(G.Trap, 1338038, C.trap),
+ "Bundle: Fabric": ItemData(G.Parts, 1338039),
+ "Bundle: FICSIT Coupon": ItemData(G.Parts, 1338040, count=0),
+ "Bundle: AI Expansion Server": ItemData(G.Parts, 1338041, count=0), # 1.0
+ "Bundle: Fused Modular Frame": ItemData(G.Parts, 1338042),
+ "Bundle: Hard Drive": ItemData(G.Parts, 1338043, count=0),
+ "Bundle: Heat Sink": ItemData(G.Parts, 1338044),
+ "Bundle: Heavy Modular Frame": ItemData(G.Parts, 1338045),
+ "Bundle: High-Speed Connector": ItemData(G.Parts, 1338046),
+ "Bundle: Satisfactory Pioneering Statue": ItemData(G.Parts, 1338047),
+ "Bundle: Pretty Good Pioneering Statue": ItemData(G.Parts, 1338048),
+ "Bundle: Iron Ingot": ItemData(G.Parts, 1338049),
+ "Bundle: Iron Ore": ItemData(G.Parts, 1338050),
+ "Bundle: Iron Plate": ItemData(G.Parts, 1338051),
+ "Bundle: Iron Rod": ItemData(G.Parts, 1338052),
+ "Bundle: Golden Nut Statue": ItemData(G.Parts, 1338053),
+ "Bundle: Leaves": ItemData(G.Parts, 1338054),
+ "Bundle: Limestone": ItemData(G.Parts, 1338055),
+ "Bundle: Magnetic Field Generator": ItemData(G.Parts, 1338056, count=0),
+ "Bundle: Mercer Sphere": ItemData(G.Parts, 1338057, count=0),
+ "Bundle: Modular Engine": ItemData(G.Parts, 1338058, count=0),
+ "Bundle: Modular Frame": ItemData(G.Parts, 1338059),
+ "Bundle: Motor": ItemData(G.Parts, 1338060),
+ "Bundle: Mycelia": ItemData(G.Parts, 1338061),
+ "Bundle: Non-fissile Uranium": ItemData(G.Trap, 1338062, C.trap),
+ "Bundle: Nuclear Pasta": ItemData(G.Parts, 1338063, count=0),
+ "Bundle: Lizard Doggo Statue": ItemData(G.Parts, 1338064),
+ "Bundle: Organic Data Capsule": ItemData(G.Parts, 1338065),
+ "Bundle: Packaged Alumina Solution": ItemData(G.Parts, 1338066),
+ "Bundle: Packaged Fuel": ItemData(G.Parts, 1338067),
+ "Bundle: Packaged Heavy Oil Residue": ItemData(G.Parts, 1338068),
+ "Bundle: Packaged Liquid Biofuel": ItemData(G.Parts, 1338069),
+ "Bundle: Packaged Nitric Acid": ItemData(G.Parts, 1338070),
+ "Bundle: Packaged Nitrogen Gas": ItemData(G.Parts, 1338071),
+ "Bundle: Packaged Oil": ItemData(G.Parts, 1338072),
+ "Bundle: Packaged Sulfuric Acid": ItemData(G.Parts, 1338073),
+ "Bundle: Packaged Turbofuel": ItemData(G.Parts, 1338074),
+ "Bundle: Packaged Water": ItemData(G.Parts, 1338075),
+ "Bundle: Petroleum Coke": ItemData(G.Parts, 1338076),
+ "Bundle: Plastic": ItemData(G.Parts, 1338077),
+ "Bundle: Plutonium Fuel Rod": ItemData(G.Trap, 1338078, C.trap),
+ "Bundle: Plutonium Pellet": ItemData(G.Trap, 1338079, C.trap),
+ "Bundle: Plutonium Waste": ItemData(G.Trap, 1338080, C.trap),
+ "Bundle: Polymer Resin": ItemData(G.Parts, 1338081),
+ "Bundle: Power Shard": ItemData(G.Parts, 1338082, count=0),
+ "Bundle: Confusing Creature Statue": ItemData(G.Parts, 1338083),
+ "Bundle: Pressure Conversion Cube": ItemData(G.Parts, 1338084),
+ "Bundle: Alien Power Matrix": ItemData(G.Parts, 1338085), # 1.0
+ "Bundle: Quartz Crystal": ItemData(G.Parts, 1338086),
+ "Bundle: Quickwire": ItemData(G.Parts, 1338087),
+ "Bundle: Radio Control Unit": ItemData(G.Parts, 1338088),
+ "Bundle: Raw Quartz": ItemData(G.Parts, 1338089),
+ "Bundle: Reinforced Iron Plate": ItemData(G.Parts, 1338090),
+ "Bundle: Rotor": ItemData(G.Parts, 1338091),
+ "Bundle: Rubber": ItemData(G.Parts, 1338092),
+ "Bundle: SAM": ItemData(G.Parts, 1338093), # 1.0
+ "Bundle: Screw": ItemData(G.Parts, 1338094),
+ "Bundle: Silica": ItemData(G.Parts, 1338095),
+ "Bundle: Smart Plating": ItemData(G.Parts, 1338096, count=0),
+ "Bundle: Smokeless Powder": ItemData(G.Parts, 1338097),
+ "Bundle: Solid Biofuel": ItemData(G.Parts, 1338098, C.useful),
+ "Bundle: Somersloop": ItemData(G.Parts, 1338099, count=0),
+ "Bundle: Stator": ItemData(G.Parts, 1338100),
+ "Bundle: Silver Hog Statue": ItemData(G.Parts, 1338101),
+ "Bundle: Steel Beam": ItemData(G.Parts, 1338102),
+ "Bundle: Steel Ingot": ItemData(G.Parts, 1338103),
+ "Bundle: Steel Pipe": ItemData(G.Parts, 1338104),
+ "Bundle: Sulfur": ItemData(G.Parts, 1338105),
+ "Bundle: Supercomputer": ItemData(G.Parts, 1338106),
+ "Bundle: Superposition Oscillator": ItemData(G.Parts, 1338107),
+ "Bundle: Thermal Propulsion Rocket": ItemData(G.Parts, 1338108, count=0),
+ "Bundle: Turbo Motor": ItemData(G.Parts, 1338109),
+ "Bundle: Hog Remains": ItemData(G.Parts, 1338110),
+ "Bundle: Uranium": ItemData(G.Trap, 1338111, C.trap),
+ "Bundle: Uranium Fuel Rod": ItemData(G.Trap, 1338112, C.trap),
+ "Bundle: Uranium Waste": ItemData(G.Trap, 1338113, C.trap),
+ "Bundle: Versatile Framework": ItemData(G.Parts, 1338114, count=0),
+ "Bundle: Wire": ItemData(G.Parts, 1338115),
+ "Bundle: Wood": ItemData(G.Parts, 1338116),
+ "Bundle: Plasma Spitter Remains": ItemData(G.Parts, 1338117),
+ "Bundle: Stinger Remains": ItemData(G.Parts, 1338118),
+ "Bundle: Hatcher Remains": ItemData(G.Parts, 1338119),
+ "Bundle: Alien DNA Capsule": ItemData(G.Parts, 1338120),
+ "Bundle: Diamonds": ItemData(G.Parts, 1338121),
+ "Bundle: Time Crystal": ItemData(G.Parts, 1338122),
+ "Bundle: Ficsite Ingot": ItemData(G.Parts, 1338123),
+ "Bundle: Ficsite Trigon": ItemData(G.Parts, 1338124),
+ "Bundle: Reanimated SAM": ItemData(G.Parts, 1338125),
+ "Bundle: SAM Fluctuator": ItemData(G.Parts, 1338126),
+ "Bundle: Biochemical Sculptor": ItemData(G.Parts, 1338127, count=0),
+ "Bundle: Ballistic Warp Drive": ItemData(G.Parts, 1338128, count=0),
+ "Bundle: Ficsonium": ItemData(G.Trap, 1338129, C.trap),
+ "Bundle: Ficsonium Fuel Rod": ItemData(G.Trap, 1338130, C.trap),
+ "Bundle: Packaged Rocket Fuel": ItemData(G.Parts, 1338131),
+ "Bundle: Packaged Ionized Fuel": ItemData(G.Parts, 1338132),
+ "Bundle: Dark Matter Crystal": ItemData(G.Parts, 1338133),
+ # 1338134 - 1338149 Reserved for future parts
+ # 1338150 - 1338199 Equipment / Ammo
+ "Bundle: Bacon Agaric": ItemData(G.Ammo, 1338150, count=0),
+ "Bundle: Beryl Nut": ItemData(G.Ammo, 1338151, count=0),
+ "Bundle: Blade Runners": ItemData(G.Equipment, 1338152, count=0),
+ "Bundle: Boom Box": ItemData(G.Equipment, 1338153, count=0),
+ "Bundle: Chainsaw": ItemData(G.Equipment, 1338154, count=0),
+ "Bundle: Cluster Nobelisk": ItemData(G.Ammo, 1338155),
+ "Bundle: Iodine-Infused Filter": ItemData(G.Equipment, 1338156, count=3), # 1.1
+ "Bundle: Cup": ItemData(G.Equipment, 1338157, count=0),
+ "Bundle: Cup (gold)": ItemData(G.Equipment, 1338158, count=0),
+ "Bundle: Explosive Rebar": ItemData(G.Ammo, 1338159),
+ "Bundle: Factory Cart": ItemData(G.Equipment, 1338160, count=0),
+ "Bundle: Factory Cart (golden)": ItemData(G.Equipment, 1338161, count=0),
+ "Bundle: Gas Mask": ItemData(G.Equipment, 1338162, count=0),
+ "Bundle: Gas Nobelisk": ItemData(G.Ammo, 1338163),
+ "Bundle: Hazmat Suit": ItemData(G.Equipment, 1338164, count=0),
+ "Bundle: Homing Rifle Ammo": ItemData(G.Ammo, 1338165),
+ "Bundle: Hoverpack": ItemData(G.Equipment, 1338166, count=0),
+ "Bundle: Iron Rebar": ItemData(G.Ammo, 1338167),
+ "Bundle: Jetpack": ItemData(G.Equipment, 1338168, count=0),
+ "Bundle: Medicinal Inhaler": ItemData(G.Ammo, 1338169),
+ "Bundle: Nobelisk": ItemData(G.Ammo, 1338170),
+ "Bundle: Nobelisk Detonator": ItemData(G.Equipment, 1338171, count=0),
+ "Bundle: Nuke Nobelisk": ItemData(G.Ammo, 1338172),
+ "Bundle: Object Scanner": ItemData(G.Equipment, 1338173, count=0),
+ "Bundle: Paleberry": ItemData(G.Ammo, 1338174, count=0),
+ "Bundle: Parachute": ItemData(G.Equipment, 1338175, count=0),
+ "Bundle: Pulse Nobelisk": ItemData(G.Ammo, 1338176),
+ "Bundle: Rebar Gun": ItemData(G.Equipment, 1338177, count=0),
+ "Bundle: Rifle": ItemData(G.Equipment, 1338178, count=0),
+ "Bundle: Rifle Ammo": ItemData(G.Ammo, 1338179),
+ "Bundle: Shatter Rebar": ItemData(G.Ammo, 1338180),
+ "Bundle: Stun Rebar": ItemData(G.Ammo, 1338181),
+ "Bundle: Turbo Rifle Ammo": ItemData(G.Ammo, 1338182),
+ "Bundle: Xeno-Basher": ItemData(G.Equipment, 1338183, count=0),
+ "Bundle: Xeno-Zapper": ItemData(G.Equipment, 1338184, count=0),
+ "Bundle: Zipline": ItemData(G.Equipment, 1338185, count=0),
+ "Bundle: Portable Miner": ItemData(G.Equipment, 1338186, count=0),
+ "Bundle: Gas Filter": ItemData(G.Equipment, 1338187, count=3),
+ # Special cases
+ "Small Inflated Pocket Dimension": ItemData(G.Upgrades, 1338188, C.useful, 11),
+ "Inflated Pocket Dimension": ItemData(G.Upgrades, 1338189, C.useful, 5),
+ "Expanded Toolbelt": ItemData(G.Upgrades, 1338190, C.useful, 5),
+ "Dimensional Depot upload from inventory": ItemData(G.Upgrades, 1338191, C.useful),
+# added in 1.1
+ "Bundle of Three: Power Shards": ItemData(G.Parts, 1338192),
+ "Bundle of Three: Mercer Spheres": ItemData(G.Parts, 1338193),
+ "Bundle of Four: Somersloops": ItemData(G.Parts, 1338194),
+ "Bundle of Three: Hard Drives": ItemData(G.Parts, 1338195),
+#
+ # 1338196 - 1338199 Reserved for future equipment/ammo
+
+ # 1338200+ Recipes / buildings / schematics
+ "Recipe: Reinforced Iron Plate": ItemData(G.Recipe, 1338200, C.progression),
+ "Recipe: Adhered Iron Plate": ItemData(G.Recipe, 1338201, C.progression),
+ "Recipe: Bolted Iron Plate": ItemData(G.Recipe, 1338202, C.progression),
+ "Recipe: Stitched Iron Plate": ItemData(G.Recipe, 1338203, C.progression),
+ "Recipe: Rotor": ItemData(G.Recipe, 1338204, C.progression),
+ "Recipe: Copper Rotor": ItemData(G.Recipe, 1338205, C.progression),
+ "Recipe: Steel Rotor": ItemData(G.Recipe, 1338206, C.progression),
+ "Recipe: Stator": ItemData(G.Recipe, 1338207, C.progression),
+ "Recipe: Quickwire Stator": ItemData(G.Recipe, 1338208, C.progression),
+ "Recipe: Plastic": ItemData(G.Recipe, 1338209, C.progression),
+ "Recipe: Residual Plastic": ItemData(G.Recipe, 1338210, C.progression),
+ "Recipe: Recycled Plastic": ItemData(G.Recipe, 1338211, C.progression),
+ "Recipe: Rubber": ItemData(G.Recipe, 1338212, C.progression),
+ "Recipe: Residual Rubber": ItemData(G.Recipe, 1338213, C.progression),
+ "Recipe: Recycled Rubber": ItemData(G.Recipe, 1338214, C.progression),
+ "Recipe: Iron Plate": ItemData(G.Recipe, 1338215, C.progression),
+ "Recipe: Coated Iron Plate": ItemData(G.Recipe, 1338216, C.progression),
+ "Recipe: Steel Cast Plate": ItemData(G.Recipe, 1338217, C.progression), # 1.0
+ "Recipe: Iron Rod": ItemData(G.Recipe, 1338218, C.progression),
+ "Recipe: Steel Rod": ItemData(G.Recipe, 1338219, C.progression),
+ "Recipe: Screw": ItemData(G.Recipe, 1338220, C.progression),
+ "Recipe: Cast Screw": ItemData(G.Recipe, 1338221, C.progression),
+ "Recipe: Steel Screw": ItemData(G.Recipe, 1338222, C.progression),
+ "Recipe: Wire": ItemData(G.Recipe, 1338223, C.progression),
+ "Recipe: Fused Wire": ItemData(G.Recipe, 1338224, C.progression),
+ "Recipe: Iron Wire": ItemData(G.Recipe, 1338225, C.progression),
+ "Recipe: Caterium Wire": ItemData(G.Recipe, 1338226, C.progression),
+ "Recipe: Cable": ItemData(G.Recipe, 1338227, C.progression),
+ "Recipe: Coated Cable": ItemData(G.Recipe, 1338228, C.progression),
+ "Recipe: Insulated Cable": ItemData(G.Recipe, 1338229, C.progression),
+ "Recipe: Quickwire Cable": ItemData(G.Recipe, 1338230, C.progression),
+ "Recipe: Quickwire": ItemData(G.Recipe, 1338231, C.progression),
+ "Recipe: Fused Quickwire": ItemData(G.Recipe, 1338232, C.progression),
+ "Recipe: Copper Sheet": ItemData(G.Recipe, 1338233, C.progression),
+ "Recipe: Steamed Copper Sheet": ItemData(G.Recipe, 1338234, C.progression),
+ "Recipe: Steel Pipe": ItemData(G.Recipe, 1338235, C.progression),
+ "Recipe: Steel Beam": ItemData(G.Recipe, 1338236, C.progression),
+ "Recipe: Neural-Quantum Processor": ItemData(G.Recipe, 1338237, C.progression), # 1.0
+ "Recipe: Heavy Oil Residue": ItemData(G.Recipe, 1338238, C.progression),
+ "Recipe: Polymer Resin": ItemData(G.Recipe, 1338239, C.progression),
+ "Recipe: Fuel": ItemData(G.Recipe, 1338240, C.progression),
+ "Recipe: Residual Fuel": ItemData(G.Recipe, 1338241, C.progression),
+ "Recipe: Diluted Packaged Fuel": ItemData(G.Recipe, 1338242, C.progression),
+ "Recipe: AI Expansion Server": ItemData(G.Recipe, 1338243, C.progression), # 1.0
+ "Recipe: Concrete": ItemData(G.Recipe, 1338244, C.progression),
+ "Recipe: Rubber Concrete": ItemData(G.Recipe, 1338245, C.progression),
+ "Recipe: Wet Concrete": ItemData(G.Recipe, 1338246, C.progression),
+ "Recipe: Fine Concrete": ItemData(G.Recipe, 1338247, C.progression),
+ "Recipe: Silica": ItemData(G.Recipe, 1338248, C.progression),
+ "Recipe: Cheap Silica": ItemData(G.Recipe, 1338249, C.progression),
+ "Recipe: Quartz Crystal": ItemData(G.Recipe, 1338250, C.progression),
+ "Recipe: Pure Quartz Crystal": ItemData(G.Recipe, 1338251, C.progression),
+ "Recipe: Iron Ingot": ItemData(G.Recipe, 1338252, C.progression),
+ "Recipe: Pure Iron Ingot": ItemData(G.Recipe, 1338253, C.progression),
+ "Recipe: Iron Alloy Ingot": ItemData(G.Recipe, 1338254, C.progression),
+ "Recipe: Steel Ingot": ItemData(G.Recipe, 1338255, C.progression),
+ "Recipe: Coke Steel Ingot": ItemData(G.Recipe, 1338256, C.progression),
+ "Recipe: Compacted Steel Ingot": ItemData(G.Recipe, 1338257, C.progression),
+ "Recipe: Solid Steel Ingot": ItemData(G.Recipe, 1338258, C.progression),
+ "Recipe: Copper Ingot": ItemData(G.Recipe, 1338259, C.progression),
+ "Recipe: Copper Alloy Ingot": ItemData(G.Recipe, 1338260, C.progression),
+ "Recipe: Pure Copper Ingot": ItemData(G.Recipe, 1338261, C.progression),
+ "Recipe: Caterium Ingot": ItemData(G.Recipe, 1338262, C.progression),
+ "Recipe: Pure Caterium Ingot": ItemData(G.Recipe, 1338263, C.progression),
+ "Recipe: Alien Power Matrix": ItemData(G.Recipe, 1338264), # 1.0
+ "Recipe: Ficsite Ingot (Aluminum)": ItemData(G.Recipe, 1338265, C.progression), # 1.0
+ "Recipe: Ficsite Ingot (Caterium)": ItemData(G.Recipe, 1338266, C.progression), # 1.0
+ "Recipe: Ficsite Ingot (Iron)": ItemData(G.Recipe, 1338267, C.progression), # 1.0
+ "Recipe: Ficsite Trigon": ItemData(G.Recipe, 1338268, C.progression), # 1.0
+ "Recipe: Reanimated SAM": ItemData(G.Recipe, 1338269, C.progression), # 1.0
+ "Recipe: SAM Fluctuator": ItemData(G.Recipe, 1338270, C.progression), # 1.0
+ "Recipe: Petroleum Coke": ItemData(G.Recipe, 1338271, C.progression),
+ "Recipe: Compacted Coal": ItemData(G.Recipe, 1338272, C.progression),
+ "Recipe: Motor": ItemData(G.Recipe, 1338273, C.progression),
+ "Recipe: Rigor Motor": ItemData(G.Recipe, 1338274, C.progression),
+ "Recipe: Electric Motor": ItemData(G.Recipe, 1338275, C.progression),
+ "Recipe: Modular Frame": ItemData(G.Recipe, 1338276, C.progression),
+ "Recipe: Bolted Frame": ItemData(G.Recipe, 1338277, C.progression),
+ "Recipe: Steeled Frame": ItemData(G.Recipe, 1338278, C.progression),
+ "Recipe: Heavy Modular Frame": ItemData(G.Recipe, 1338279, C.progression),
+ "Recipe: Heavy Flexible Frame": ItemData(G.Recipe, 1338280, C.progression),
+ "Recipe: Heavy Encased Frame": ItemData(G.Recipe, 1338281, C.progression),
+ "Recipe: Encased Industrial Beam": ItemData(G.Recipe, 1338282, C.progression),
+ "Recipe: Encased Industrial Pipe": ItemData(G.Recipe, 1338283, C.progression),
+ "Recipe: Computer": ItemData(G.Recipe, 1338284, C.progression),
+ "Recipe: Crystal Computer": ItemData(G.Recipe, 1338285, C.progression),
+ "Recipe: Caterium Computer": ItemData(G.Recipe, 1338286, C.progression),
+ "Recipe: Circuit Board": ItemData(G.Recipe, 1338287, C.progression),
+ "Recipe: Electrode Circuit Board": ItemData(G.Recipe, 1338288, C.progression),
+ "Recipe: Silicon Circuit Board": ItemData(G.Recipe, 1338289, C.progression),
+ "Recipe: Caterium Circuit Board": ItemData(G.Recipe, 1338290, C.progression),
+ "Recipe: Crystal Oscillator": ItemData(G.Recipe, 1338291, C.progression),
+ "Recipe: Insulated Crystal Oscillator": ItemData(G.Recipe, 1338292, C.progression),
+ "Recipe: AI Limiter": ItemData(G.Recipe, 1338293, C.progression),
+ "Recipe: Electromagnetic Control Rod": ItemData(G.Recipe, 1338294, C.progression),
+ "Recipe: Electromagnetic Connection Rod": ItemData(G.Recipe, 1338295, C.progression),
+ "Recipe: High-Speed Connector": ItemData(G.Recipe, 1338296, C.progression),
+ "Recipe: Silicon High-Speed Connector": ItemData(G.Recipe, 1338297, C.progression),
+ "Recipe: Smart Plating": ItemData(G.Recipe, 1338298, C.progression),
+ "Recipe: Plastic Smart Plating": ItemData(G.Recipe, 1338299, C.progression),
+ "Recipe: Versatile Framework": ItemData(G.Recipe, 1338300, C.progression),
+ "Recipe: Flexible Framework": ItemData(G.Recipe, 1338301, C.progression),
+ "Recipe: Automated Wiring": ItemData(G.Recipe, 1338302, C.progression),
+ "Recipe: Automated Speed Wiring": ItemData(G.Recipe, 1338303, C.progression),
+ "Recipe: Modular Engine": ItemData(G.Recipe, 1338304, C.progression),
+ "Recipe: Adaptive Control Unit": ItemData(G.Recipe, 1338305, C.progression),
+ "Recipe: Diluted Fuel": ItemData(G.Recipe, 1338306, C.progression),
+ "Recipe: Alumina Solution": ItemData(G.Recipe, 1338307, C.progression),
+ "Recipe: Automated Miner": ItemData(G.Recipe, 1338308, C.progression),
+ "Recipe: Singularity Cell": ItemData(G.Recipe, 1338309, C.progression), # 1.0
+ "Recipe: Aluminum Scrap": ItemData(G.Recipe, 1338310, C.progression),
+ "Recipe: Electrode Aluminum Scrap": ItemData(G.Recipe, 1338311, C.progression),
+ "Recipe: Instant Scrap": ItemData(G.Recipe, 1338312, C.progression),
+ "Recipe: Aluminum Ingot": ItemData(G.Recipe, 1338313, C.progression),
+ "Recipe: Pure Aluminum Ingot": ItemData(G.Recipe, 1338314, C.progression),
+ "Recipe: Alclad Aluminum Sheet": ItemData(G.Recipe, 1338315, C.progression),
+ "Recipe: Aluminum Casing": ItemData(G.Recipe, 1338316, C.progression),
+ "Recipe: Alclad Casing": ItemData(G.Recipe, 1338317, C.progression),
+ "Recipe: Heat Sink": ItemData(G.Recipe, 1338318, C.progression),
+ "Recipe: Heat Exchanger": ItemData(G.Recipe, 1338319, C.progression),
+ "Recipe: Synthetic Power Shard": ItemData(G.Recipe, 1338320, C.progression),
+ "Recipe: Nitric Acid": ItemData(G.Recipe, 1338321, C.progression),
+ "Recipe: Fused Modular Frame": ItemData(G.Recipe, 1338322, C.progression),
+ "Recipe: Heat-Fused Frame": ItemData(G.Recipe, 1338323, C.progression),
+ "Recipe: Radio Control Unit": ItemData(G.Recipe, 1338324, C.progression),
+ "Recipe: Radio Connection Unit": ItemData(G.Recipe, 1338325, C.progression),
+ "Recipe: Radio Control System": ItemData(G.Recipe, 1338326, C.progression),
+ "Recipe: Pressure Conversion Cube": ItemData(G.Recipe, 1338327, C.progression),
+ "Recipe: Cooling System": ItemData(G.Recipe, 1338328, C.progression),
+ "Recipe: Cooling Device": ItemData(G.Recipe, 1338329, C.progression),
+ "Recipe: Turbo Motor": ItemData(G.Recipe, 1338330, C.progression),
+ "Recipe: Turbo Electric Motor": ItemData(G.Recipe, 1338331, C.progression),
+ "Recipe: Turbo Pressure Motor": ItemData(G.Recipe, 1338332, C.progression),
+ "Recipe: Battery": ItemData(G.Recipe, 1338333, C.progression),
+ "Recipe: Classic Battery": ItemData(G.Recipe, 1338334, C.progression),
+ "Recipe: Supercomputer": ItemData(G.Recipe, 1338335, C.progression),
+ "Recipe: OC Supercomputer": ItemData(G.Recipe, 1338336, C.progression),
+ "Recipe: Super-State Computer": ItemData(G.Recipe, 1338337, C.progression),
+ "Recipe: Biochemical Sculptor": ItemData(G.Recipe, 1338338, C.progression), # 1.0
+ "Recipe: Sulfuric Acid": ItemData(G.Recipe, 1338339, C.progression),
+ "Recipe: Ballistic Warp Drive": ItemData(G.Recipe, 1338340, C.progression), # 1.0
+ "Recipe: Encased Uranium Cell": ItemData(G.Recipe, 1338341, C.progression),
+ "Recipe: Infused Uranium Cell": ItemData(G.Recipe, 1338342, C.progression),
+ "Recipe: Uranium Fuel Rod": ItemData(G.Recipe, 1338343, C.progression),
+ "Recipe: Uranium Fuel Unit": ItemData(G.Recipe, 1338344, C.progression),
+ "Recipe: Aluminum Beam": ItemData(G.Recipe, 1338345, C.progression), # 1.0
+ "Recipe: Aluminum Rod": ItemData(G.Recipe, 1338346, C.progression), # 1.0
+ "Recipe: Basic Iron Ingot": ItemData(G.Recipe, 1338347, C.progression), # 1.0
+ "Recipe: Non-fissile Uranium": ItemData(G.Recipe, 1338348, C.progression),
+ "Recipe: Fertile Uranium": ItemData(G.Recipe, 1338349, C.progression),
+ "Recipe: Plutonium Pellet": ItemData(G.Recipe, 1338350),
+ "Recipe: Encased Plutonium Cell": ItemData(G.Recipe, 1338351),
+ "Recipe: Instant Plutonium Cell": ItemData(G.Recipe, 1338352),
+ "Recipe: Plutonium Fuel Rod": ItemData(G.Recipe, 1338353),
+ "Recipe: Plutonium Fuel Unit": ItemData(G.Recipe, 1338354),
+ "Recipe: Gas Filter": ItemData(G.Recipe, 1338355, C.progression),
+ "Recipe: Iodine-Infused Filter": ItemData(G.Recipe, 1338356, C.progression),
+ "Recipe: Assembly Director System": ItemData(G.Recipe, 1338357, C.progression),
+ "Recipe: Magnetic Field Generator": ItemData(G.Recipe, 1338358, C.progression),
+ "Recipe: Copper Powder": ItemData(G.Recipe, 1338359, C.progression),
+ "Recipe: Nuclear Pasta": ItemData(G.Recipe, 1338360, C.progression),
+ "Recipe: Thermal Propulsion Rocket": ItemData(G.Recipe, 1338361, C.progression),
+ "Recipe: Ficsonium": ItemData(G.Recipe, 1338362), # 1.0
+ "Recipe: Ficsonium Fuel Rod": ItemData(G.Recipe, 1338363), # 1.0
+ "Recipe: Dark Matter Crystal": ItemData(G.Recipe, 1338364, C.progression), # 1.0
+ "Recipe: Dark Matter Crystallization": ItemData(G.Recipe, 1338365, C.progression), # 1.0
+ "Recipe: Dark Matter Trap": ItemData(G.Recipe, 1338366, C.progression), # 1.0
+ "Recipe: Pulse Nobelisk": ItemData(G.Recipe, 1338367, C.useful),
+ "Recipe: Hatcher Protein": ItemData(G.Recipe, 1338368, C.progression),
+ "Recipe: Hog Protein": ItemData(G.Recipe, 1338369, C.progression),
+ "Recipe: Spitter Protein": ItemData(G.Recipe, 1338370, C.progression),
+ "Recipe: Stinger Protein": ItemData(G.Recipe, 1338371, C.progression),
+ "Recipe: Biomass (Leaves)": ItemData(G.Recipe, 1338372, C.progression),
+ "Recipe: Biomass (Wood)": ItemData(G.Recipe, 1338373, C.progression),
+ "Recipe: Biomass (Mycelia)": ItemData(G.Recipe, 1338374, C.progression),
+ "Recipe: Biomass (Alien Protein)": ItemData(G.Recipe, 1338375, C.progression),
+ "Recipe: Turbo Rifle Ammo (Packaged)": ItemData(G.Recipe, 1338376, C.useful),
+ "Recipe: Fabric": ItemData(G.Recipe, 1338377, C.progression),
+ "Recipe: Polyester Fabric": ItemData(G.Recipe, 1338378, C.progression),
+ "Recipe: Solid Biofuel": ItemData(G.Recipe, 1338379, C.progression),
+ "Recipe: Liquid Biofuel": ItemData(G.Recipe, 1338380, C.progression),
+ "Recipe: Empty Canister": ItemData(G.Recipe, 1338381, C.progression),
+ "Recipe: Coated Iron Canister": ItemData(G.Recipe, 1338382, C.progression),
+ "Recipe: Steel Canister": ItemData(G.Recipe, 1338383, C.progression),
+ "Recipe: Empty Fluid Tank": ItemData(G.Recipe, 1338384, C.progression),
+ "Recipe: Packaged Alumina Solution": ItemData(G.Recipe, 1338385, C.progression),
+ "Recipe: Packaged Fuel": ItemData(G.Recipe, 1338386, C.progression),
+ # "Recipe: Diluted Packaged Fuel": ItemData(G.Recipe, 1338387, C.progression), # Duplicated
+ "Recipe: Packaged Heavy Oil Residue": ItemData(G.Recipe, 1338388, C.progression),
+ "Recipe: Packaged Liquid Biofuel": ItemData(G.Recipe, 1338389, C.progression),
+ "Recipe: Packaged Nitric Acid": ItemData(G.Recipe, 1338390, C.progression),
+ "Recipe: Packaged Nitrogen Gas": ItemData(G.Recipe, 1338391, C.progression),
+ "Recipe: Packaged Oil": ItemData(G.Recipe, 1338392, C.progression),
+ "Recipe: Packaged Sulfuric Acid": ItemData(G.Recipe, 1338393, C.progression),
+ "Recipe: Packaged Turbofuel": ItemData(G.Recipe, 1338394, C.progression),
+ "Recipe: Packaged Water": ItemData(G.Recipe, 1338395, C.progression),
+ "Recipe: Turbofuel": ItemData(G.Recipe, 1338396, C.progression),
+ "Recipe: Turbo Heavy Fuel": ItemData(G.Recipe, 1338397, C.progression),
+ "Recipe: Turbo Blend Fuel": ItemData(G.Recipe, 1338398, C.progression),
+ "Recipe: Hazmat Suit": ItemData(G.Recipe, 1338399, C.progression),
+ "Recipe: Gas Mask": ItemData(G.Recipe, 1338400, C.progression),
+ "Recipe: Black Powder": ItemData(G.Recipe, 1338401, C.progression),
+ "Recipe: Blade Runners": ItemData(G.Recipe, 1338402, C.useful),
+ "Recipe: Chainsaw": ItemData(G.Recipe, 1338403, C.useful),
+ "Recipe: Cluster Nobelisk": ItemData(G.Recipe, 1338404, C.useful),
+ "Recipe: Explosive Rebar": ItemData(G.Recipe, 1338405, C.useful),
+ "Recipe: Factory Cart": ItemData(G.Recipe, 1338406, C.useful),
+ "Recipe: Gas Nobelisk": ItemData(G.Recipe, 1338407, C.useful),
+ "Recipe: Golden Factory Cart": ItemData(G.Recipe, 1338408),
+ "Recipe: Homing Rifle Ammo": ItemData(G.Recipe, 1338409, C.useful),
+ "Recipe: Iron Rebar": ItemData(G.Recipe, 1338410, C.progression),
+ "Recipe: Nobelisk": ItemData(G.Recipe, 1338411, C.progression),
+ "Recipe: Nuke Nobelisk": ItemData(G.Recipe, 1338412, C.useful),
+ "Recipe: Nutritional Inhaler": ItemData(G.Recipe, 1338413, C.useful),
+ "Recipe: Object Scanner": ItemData(G.Recipe, 1338414, C.progression),
+ "Recipe: Parachute": ItemData(G.Recipe, 1338415, C.useful),
+ "Recipe: Protein Inhaler": ItemData(G.Recipe, 1338416, C.useful),
+ "Recipe: Rebar Gun": ItemData(G.Recipe, 1338417, C.useful),
+ "Recipe: Rifle": ItemData(G.Recipe, 1338418, C.useful),
+ "Recipe: Rifle Ammo": ItemData(G.Recipe, 1338419, C.progression),
+ "Recipe: Shatter Rebar": ItemData(G.Recipe, 1338420, C.useful),
+ "Recipe: Stun Rebar": ItemData(G.Recipe, 1338421, C.useful),
+ "Recipe: Therapeutic Inhaler": ItemData(G.Recipe, 1338422, C.useful),
+ "Recipe: Turbo Rifle Ammo": ItemData(G.Recipe, 1338423, C.useful),
+ "Recipe: Vitamin Inhaler": ItemData(G.Recipe, 1338424, C.useful),
+ "Recipe: Xeno-Basher": ItemData(G.Recipe, 1338425, C.useful),
+ "Recipe: Xeno-Zapper": ItemData(G.Recipe, 1338426, C.useful),
+ "Recipe: Zipline": ItemData(G.Recipe, 1338427, C.useful),
+ "Recipe: Fine Black Powder": ItemData(G.Recipe, 1338428, C.progression),
+ "Recipe: Smokeless Powder": ItemData(G.Recipe, 1338429, C.progression),
+ "Recipe: Alien DNA Capsule": ItemData(G.Recipe, 1338430, C.progression),
+ "Recipe: Power Shard (1)": ItemData(G.Recipe, 1338431, C.progression),
+ "Recipe: Power Shard (2)": ItemData(G.Recipe, 1338432, C.useful),
+ "Recipe: Power Shard (5)": ItemData(G.Recipe, 1338433, C.useful),
+
+# 1.0
+ "Recipe: Diamonds": ItemData(G.Recipe, 1338434, C.progression),
+ "Recipe: Cloudy Diamonds": ItemData(G.Recipe, 1338435, C.progression),
+ "Recipe: Oil-Based Diamonds": ItemData(G.Recipe, 1338436, C.progression),
+ "Recipe: Petroleum Diamonds": ItemData(G.Recipe, 1338437, C.progression),
+ "Recipe: Pink Diamonds": ItemData(G.Recipe, 1338438, C.progression),
+ "Recipe: Turbo Diamonds": ItemData(G.Recipe, 1338439, C.progression),
+ "Recipe: Time Crystal": ItemData(G.Recipe, 1338440, C.progression),
+ "Recipe: Superposition Oscillator": ItemData(G.Recipe, 1338441, C.progression),
+ # "Recipe: Excited Photonic Matter": ItemData(G.Recipe, 1338442, C.progression), unlocked with converter
+ "Recipe: Rocket Fuel": ItemData(G.Recipe, 1338443, C.progression),
+ "Recipe: Nitro Rocket Fuel": ItemData(G.Recipe, 1338444, C.progression),
+ "Recipe: Ionized Fuel": ItemData(G.Recipe, 1338445, C.useful),
+ "Recipe: Packaged Rocket Fuel": ItemData(G.Recipe, 1338446, C.progression),
+ "Recipe: Packaged Ionized Fuel": ItemData(G.Recipe, 1338447, C.useful),
+ "Recipe: Dark-Ion Fuel": ItemData(G.Recipe, 1338448, C.useful),
+ "Recipe: Quartz Purification": ItemData(G.Recipe, 1338449, C.progression),
+ "Recipe: Fused Quartz Crystal": ItemData(G.Recipe, 1338450, C.progression),
+ "Recipe: Leached Caterium Ingot": ItemData(G.Recipe, 1338451, C.progression),
+ "Recipe: Leached Copper Ingot": ItemData(G.Recipe, 1338452, C.progression),
+ "Recipe: Leached Iron ingot": ItemData(G.Recipe, 1338453, C.progression),
+ "Recipe: Molded Beam": ItemData(G.Recipe, 1338454, C.progression),
+ "Recipe: Molded Steel Pipe": ItemData(G.Recipe, 1338455, C.progression),
+ "Recipe: Plastic AI Limiter": ItemData(G.Recipe, 1338456, C.progression),
+ "Recipe: Tempered Caterium Ingot": ItemData(G.Recipe, 1338457, C.progression),
+ "Recipe: Tempered Copper Ingot": ItemData(G.Recipe, 1338458, C.progression),
+# 1.0
+
+# added in 1.1 or missed
+ "Recipe: Iron Pipe": ItemData(G.Recipe, 1338459, C.progression),
+ "Recipe: Biocoal": ItemData(G.Recipe, 1338460, C.useful),
+ "Recipe: Charcoal": ItemData(G.Recipe, 1338461, C.useful),
+ "Recipe: Sloppy Alumina": ItemData(G.Recipe, 1338462, C.progression),
+ "Recipe: Hoverpack": ItemData(G.Recipe, 1338463, C.useful),
+ "Recipe: Jetpack": ItemData(G.Recipe | G.NeverExclude, 1338464, C.useful),
+ "Recipe: Nobelisk Detonator": ItemData(G.Recipe, 1338465, C.progression),
+ "Recipe: Portable Miner": ItemData(G.Recipe, 1338466, C.progression),
+#
+ "Recipe: Dark Matter Residue": ItemData(G.Recipe, 1338467, C.progression),
+
+ # 1338468 - 1338599 Reserved for future recipes
+ # 1338400 - 1338899 buildings / others
+ "Building: Constructor": ItemData(G.Building, 1338600, C.progression), # unlocked by default
+ "Building: Assembler": ItemData(G.Building, 1338601, C.progression),
+ "Building: Manufacturer": ItemData(G.Building, 1338602, C.progression),
+ "Building: Packager": ItemData(G.Building, 1338603, C.progression),
+ "Building: Refinery": ItemData(G.Building, 1338604, C.progression),
+ "Building: Blender": ItemData(G.Building, 1338605, C.progression),
+ "Building: Particle Accelerator": ItemData(G.Building, 1338606, C.progression),
+ "Building: Biomass Burner": ItemData(G.Building, 1338607, C.progression), # unlocked by default
+ "Building: Coal Generator": ItemData(G.Building, 1338608, C.progression),
+ "Building: Geothermal Generator": ItemData(G.Building, 1338609, C.progression),
+ "Building: Nuclear Power Plant": ItemData(G.Building, 1338610, C.progression),
+ "Building: Miner Mk.1": ItemData(G.Building, 1338611, C.progression), # unlocked by default
+ "Building: Miner Mk.2": ItemData(G.Building, 1338612, C.progression),
+ "Building: Miner Mk.3": ItemData(G.Building, 1338613, C.progression),
+ "Building: Oil Extractor": ItemData(G.Building, 1338614, C.progression),
+ "Building: Water Extractor": ItemData(G.Building, 1338615, C.progression),
+ "Building: Smelter": ItemData(G.Building, 1338616, C.progression), # unlocked by default
+ "Building: Foundry": ItemData(G.Building, 1338617, C.progression),
+ "Building: Fuel Generator": ItemData(G.Building, 1338618, C.progression),
+ "Building: Resource Well Pressurizer": ItemData(G.Building, 1338619, C.progression),
+ "Building: Equipment Workshop": ItemData(G.Building, 1338620, C.progression),
+ "Building: AWESOME Sink": ItemData(G.Building | G.NeverExclude, 1338621, C.progression),
+ "Building: AWESOME Shop": ItemData(G.Building | G.NeverExclude, 1338622, C.progression),
+ "Building: Structural Beam Pack": ItemData(G.Beams, 1338623, C.filler),
+ "Building: Blueprint Designer": ItemData(G.Building, 1338624, C.filler, 0), # unlocked by default
+ "Building: Fluid Buffer": ItemData(G.Building | G.NeverExclude, 1338625, C.useful),
+ "Building: Industrial Fluid Buffer": ItemData(G.Building | G.NeverExclude, 1338626, C.useful),
+ "Building: Jump Pad": ItemData(G.Building, 1338627, C.filler),
+ "Building: Ladder": ItemData(G.Building, 1338628, C.filler),
+ "Building: MAM": ItemData(G.Building | G.NeverExclude, 1338629, C.progression),
+ "Building: Personal Storage Box": ItemData(G.Building, 1338630, C.filler),
+ "Building: Power Storage": ItemData(G.Building | G.NeverExclude, 1338631, C.progression),
+ "Building: U-Jelly Landing Pad": ItemData(G.Building, 1338632, C.useful),
+ "Building: Power Switch": ItemData(G.Building | G.NeverExclude, 1338633, C.useful),
+ "Building: Priority Power Switch": ItemData(G.Building | G.NeverExclude, 1338634, C.useful),
+ "Building: Storage Container": ItemData(G.Building, 1338635, C.useful, 0),
+ "Building: Lookout Tower": ItemData(G.Building, 1338636, C.filler),
+ # "Building: Power Pole Mk.1": ItemData(G.Building, 1338637, C.progression), # unlocked by default
+ "Building: Power Pole Mk.2": ItemData(G.Building | G.NeverExclude, 1338638, C.useful),
+ "Building: Power Pole Mk.3": ItemData(G.Building | G.NeverExclude, 1338639, C.useful),
+ "Building: Industrial Storage Container": ItemData(G.Building | G.NeverExclude, 1338640, C.useful),
+ "Building: Conveyor Merger": ItemData(G.Building | G.NeverExclude, 1338641, C.progression),
+ "Building: Conveyor Splitter": ItemData(G.Building | G.NeverExclude, 1338642, C.progression),
+ "Building: Conveyor Mk.1": ItemData(G.Building | G.ConveyorMk1, 1338643, C.progression), # unlocked by default
+ "Building: Conveyor Mk.2": ItemData(G.Building | G.ConveyorMk2, 1338644, C.progression),
+ "Building: Conveyor Mk.3": ItemData(G.Building | G.ConveyorMk3, 1338645, C.progression),
+ "Building: Conveyor Mk.4": ItemData(G.Building | G.ConveyorMk4, 1338646, C.progression),
+ "Building: Conveyor Mk.5": ItemData(G.Building | G.ConveyorMk5, 1338647, C.progression),
+ "Building: Conveyor Lift Mk.1": ItemData(G.Building | G.ConveyorMk1, 1338648, C.useful),
+ "Building: Conveyor Lift Mk.2": ItemData(G.Building | G.ConveyorMk2, 1338649, C.useful),
+ "Building: Conveyor Lift Mk.3": ItemData(G.Building | G.ConveyorMk3, 1338650, C.useful),
+ "Building: Conveyor Lift Mk.4": ItemData(G.Building | G.ConveyorMk4, 1338651, C.useful),
+ "Building: Conveyor Lift Mk.5": ItemData(G.Building | G.ConveyorMk5, 1338652, C.useful),
+ "Building: Cable Beam Pack": ItemData(G.Beams, 1338653, C.filler, 0),
+ "Building: Stackable Conveyor Pole": ItemData(G.Building | G.ConveyorSupports, 1338654, C.useful),
+ "Building: Conveyor Wall Mount": ItemData(G.Building | G.ConveyorSupports, 1338655, C.useful, 0),
+ "Building: Conveyor Lift Floor Hole": ItemData(G.Building | G.ConveyorSupports, 1338656, C.useful, 0),
+ "Building: Conveyor Ceiling Mount": ItemData(G.Building | G.ConveyorSupports, 1338657, C.useful, 0),
+ "Building: Pipes Mk.1": ItemData(G.Building | G.PipesMk1, 1338658, C.progression),
+ "Building: Pipes Mk.2": ItemData(G.Building | G.PipesMk2, 1338659, C.progression),
+ "Building: Pipeline Pump Mk.1": ItemData(G.Building | G.PipesMk1, 1338660, C.progression),
+ "Building: Pipeline Pump Mk.2": ItemData(G.Building | G.PipesMk2, 1338661, C.progression),
+ "Building: Pipeline Junction Cross": ItemData(G.Building | G.PipesMk1 | G.PipesMk2, 1338662, C.progression),
+ "Building: Valve": ItemData(G.Building | G.PipesMk1 | G.PipesMk2, 1338663, C.useful),
+ "Building: Stackable Pipeline Support": ItemData(G.Building | G.PipelineSupports, 1338664, C.useful, 0),
+ "Building: Wall Pipeline Support": ItemData(G.Building | G.PipelineSupports, 1338665, C.useful, 0),
+ "Building: Pipeline Wall Hole": ItemData(G.Building | G.PipelineSupports, 1338666, C.useful, 0),
+ "Building: Pipeline Floor Hole": ItemData(G.Building | G.PipelineSupports, 1338667, C.useful, 0),
+ "Building: Lights Control Panel": ItemData(G.Building | G.Lights, 1338668, C.filler, 0),
+ "Building: Wall Mounted Flood Light": ItemData(G.Building | G.Lights, 1338669, C.filler, 0),
+ "Building: Street Light": ItemData(G.Building | G.Lights, 1338670, C.filler, 0),
+ "Building: Flood Light Tower": ItemData(G.Building | G.Lights, 1338671, C.filler, 0),
+ "Building: Ceiling Light": ItemData(G.Building | G.Lights, 1338672, C.filler, 0),
+ "Building: Power Tower": ItemData(G.Building | G.NeverExclude, 1338673, C.useful),
+ "Building: Walls Orange": ItemData(G.Building | G.Walls, 1338674, C.progression),
+ "Building: Radar Tower": ItemData(G.Building, 1338675, C.useful),
+ "Building: Smart Splitter": ItemData(G.Building | G.NeverExclude, 1338676, C.useful),
+ "Building: Programmable Splitter": ItemData(G.Building | G.NeverExclude, 1338677, C.useful),
+ "Building: Label Sign Bundle": ItemData(G.Building | G.Signs, 1338678, C.filler, 0),
+ "Building: Display Sign Bundle": ItemData(G.Building | G.Signs, 1338679, C.filler, 0),
+ "Building: Billboard Set": ItemData(G.Building | G.Signs, 1338680, C.filler, 0),
+ # 1338681 Moved to cosmetics - 1.1
+ "Building: Metal Pillar": ItemData(G.Pilars, 1338682, C.filler, 0),
+ "Building: Concrete Pillar": ItemData(G.Pilars, 1338683, C.filler, 0),
+ "Building: Frame Pillar": ItemData(G.Pilars, 1338684, C.filler, 0),
+ # 1338685 - 1338691 Moved to cosmetics - 1.1
+ "Building: Foundation": ItemData(G.Building | G.Foundations | G.NeverExclude, 1338692, C.progression),
+ "Building: Half Foundation": ItemData(G.Foundations, 1338693, C.filler, 0),
+ "Building: Corner Ramp Pack": ItemData(G.Foundations, 1338694, C.filler, 0),
+ "Building: Inverted Ramp Pack": ItemData(G.Foundations, 1338695, C.filler, 0),
+ "Building: Inverted Corner Ramp Pack": ItemData(G.Foundations, 1338696, C.filler, 0),
+ "Building: Quarter Pipes Pack": ItemData(G.Foundations, 1338697, C.filler, 0),
+ "Building: Quarter Pipe Extensions Pack": ItemData(G.Foundations, 1338698, C.filler, 0),
+ "Building: Frame Foundation": ItemData(G.Foundations, 1338699, C.filler, 0),
+ "Building: Wall Outlet Mk.1": ItemData(G.Building, 1338700, C.useful),
+ "Building: Wall Outlet Mk.2": ItemData(G.Building, 1338701, C.useful),
+ "Building: Wall Outlet Mk.3": ItemData(G.Building, 1338702, C.useful),
+ "Building: Modern Catwalks": ItemData(G.Building, 1338703, C.filler, 0),
+ "Building: Industrial Walkways": ItemData(G.Building, 1338704, C.filler, 0),
+ "Building: Stairs": ItemData(G.Building, 1338705, C.filler, 0),
+ "Building: Clean Pipeline Mk.1": ItemData(G.Building, 1338706, C.filler, 0),
+ "Building: Clean Pipeline Mk.2": ItemData(G.Building, 1338707, C.filler, 0),
+ "Building: Road Barrier": ItemData(G.Building, 1338708, C.filler, 0),
+ "Building: Modern Railing": ItemData(G.Building, 1338709, C.filler, 0),
+ "Building: Industrial Railing": ItemData(G.Building, 1338710, C.filler, 0),
+ "Building: Double Ramp Pack": ItemData(G.Foundations, 1338711, C.filler, 0),
+ "Building: Conveyor Walls": ItemData(G.Walls, 1338712, C.filler, 0),
+ "Building: Inverted Ramp Wall Bundle": ItemData(G.Walls, 1338713, C.filler, 0),
+ "Building: Ramp Wall Bundle": ItemData(G.Walls, 1338714, C.filler, 0),
+ "Building: Door Walls": ItemData(G.Walls, 1338715, C.filler, 0),
+ "Building: Tilted Walls": ItemData(G.Walls, 1338716, C.filler, 0),
+ "Building: Windowed Walls": ItemData(G.Walls, 1338717, C.filler, 0),
+ "Building: Steel-framed Windows": ItemData(G.Walls, 1338718, C.filler, 0),
+ "Building: Gates": ItemData(G.Walls, 1338719, C.filler, 0),
+ "Building: Roofs": ItemData(G.Walls, 1338720, C.filler, 0),
+ "Building: Roof Corners": ItemData(G.Walls, 1338721, C.filler, 0),
+ "Building: Converter": ItemData(G.Building, 1338722, C.progression),
+ "Building: Quantum Encoder": ItemData(G.Building, 1338723, C.progression),
+ "Building: Portal": ItemData(G.Building, 1338724, C.useful),
+ "Building: Conveyor Mk.6": ItemData(G.Building | G.ConveyorMk6, 1338725, C.progression),
+ "Building: Conveyor Lift Mk.6": ItemData(G.Building | G.ConveyorMk6, 1338726, C.useful),
+ "Building: Alien Power Augmenter": ItemData(G.Building, 1338727, C.progression),
+ "Building: Dimensional Depot Uploader": ItemData(G.Building, 1338728, C.useful),
+# Added in 1.1
+ "Building: Priority Merger": ItemData(G.Building | G.NeverExclude, 1338729, C.useful),
+ "Building: Conveyor Wall Hole": ItemData(G.Building, 1338730, C.useful, 0),
+ "Building: Conveyor Throughput Monitor": ItemData(G.Building, 1338731, C.useful, 0),
+ "Building: Basic Shelf Unit": ItemData(G.Building, 1338732, C.useful, 0),
+ "Building: Beam Expansion Pack": ItemData(G.Beams, 1338733, C.filler, 0),
+ "Building: Ventilation Bundle": ItemData(G.Building, 1338734, C.filler, 0),
+###
+ # 1338729 - 1338749 Reserved for buildings
+ "Customizer: Asphalt Foundation Material": ItemData(G.Customizer | G.Foundations, 1338750, C.filler, 0),
+ "Customizer: Concrete Foundation Material": ItemData(G.Customizer | G.Foundations, 1338751, C.filler, 0),
+ "Customizer: Concrete Wall Material": ItemData(G.Customizer | G.Walls, 1338752, C.filler, 0),
+ "Customizer: Glass Roof Material": ItemData(G.Customizer | G.Walls, 1338753, C.filler, 0),
+ "Customizer: Grip Metal Foundation Material": ItemData(G.Customizer | G.Foundations, 1338754, C.filler, 0),
+ "Customizer: Coated Concrete Foundation Material": ItemData(G.Customizer | G.Foundations, 1338755, C.filler, 0),
+ "Customizer: Metal Roof Material": ItemData(G.Customizer | G.Walls, 1338756, C.filler, 0),
+ "Customizer: Steel Wall Material": ItemData(G.Customizer | G.Walls, 1338757, C.filler, 0),
+ "Customizer: Tar Roof Material": ItemData(G.Customizer | G.Walls, 1338758, C.filler, 0),
+ "Customizer: Arrow Patterns": ItemData(G.Customizer | G.Foundations, 1338759, C.filler, 0),
+ "Customizer: Dotted Line Patterns": ItemData(G.Customizer | G.Foundations, 1338760, C.filler, 0),
+ "Customizer: Solid Line Patterns": ItemData(G.Customizer | G.Foundations, 1338761, C.filler, 0),
+ "Customizer: Factory Icon Patterns": ItemData(G.Customizer | G.Foundations, 1338762, C.filler, 0),
+ "Customizer: Transportation Icon Patterns": ItemData(G.Customizer | G.Foundations, 1338763, C.filler, 0),
+ "Customizer: Number Patterns": ItemData(G.Customizer | G.Foundations, 1338764, C.filler, 0),
+ "Customizer: Pathway Patterns": ItemData(G.Customizer | G.Foundations, 1338765, C.filler, 0),
+ "Customizer: Factory Zone Patterns": ItemData(G.Customizer | G.Foundations, 1338766, C.filler, 0),
+ "Customizer: Steel-Framed Windows": ItemData(G.Customizer | G.Walls, 1338767, C.filler, 0),
+ "Customizer: Construction Fences": ItemData(G.Customizer, 1338768, C.filler, 0),
+ "Customizer: Unpainted Finish": ItemData(G.Customizer, 1338769, C.filler, 0),
+ "Customizer: Copper Paint Finish": ItemData(G.Customizer, 1338770, C.filler, 0),
+ "Customizer: Chrome Paint Finish": ItemData(G.Customizer, 1338771, C.filler, 0),
+ "Customizer: Carbon Steel Finish": ItemData(G.Customizer, 1338772, C.filler, 0),
+ "Customizer: Caterium Paint Finish": ItemData(G.Customizer, 1338773, C.filler, 0),
+
+ # 1338776 - 1338799 Reserved for Cosmetics
+
+ # Transports 1338800 - 1338899
+ # Drones (including Drone)
+ "Transport: Drones": ItemData(G.Transport, 1338800, C.useful),
+ # Trains (including Empty Platform, rails, station, locomotive, train stop) # 1.1
+ "Transport: Trains": ItemData(G.Transport | G.Trains, 1338801, C.useful),
+ "Transport: Fluid Trains": ItemData(G.Transport | G.Trains, 1338802, C.useful),
+ # Tracker / Truck (including truck station)
+ "Transport: Tractor": ItemData(G.Transport | G.Vehicles, 1338803, C.useful),
+ "Transport: Truck": ItemData(G.Transport | G.Vehicles, 1338804, C.useful),
+ "Transport: Explorer": ItemData(G.Transport | G.Vehicles, 1338805, C.useful),
+ "Transport: Factory Cart": ItemData(G.Transport | G.Vehicles, 1338806, C.useful),
+ "Transport: Factory Cart (golden)": ItemData(G.Transport | G.Vehicles, 1338807, C.filler),
+ "Transport: Cyber Wagon": ItemData(G.Transport | G.Vehicles | G.Trap, 1338808, C.filler),
+ # Hypertubes (including supports / pipes / entrance / holes)
+ "Transport: Hypertube": ItemData(G.Transport | G.HyperTubes, 1338809, C.useful),
+ "Transport: Hypertube Floor Hole": ItemData(G.Transport | G.HyperTubes, 1338810, C.filler),
+ "Transport: Hypertube Wall Support": ItemData(G.Transport | G.HyperTubes, 1338811, C.filler),
+ "Transport: Hypertube Wall Hole": ItemData(G.Transport | G.HyperTubes, 1338812, C.filler),
+ # Personal Elevator (including additional floors)
+ "Transport: Personal Elevator": ItemData(G.Transport, 1338813, C.useful), # 1.1
+
+ # 1338900 - 1338998 Handled by trap system (includes a few non-trap things)
+ # Regenerate via /Script/Blutility.EditorUtilityWidgetBlueprint'/Archipelago/Debug/EU_GenerateTrapIds.EU_GenerateTrapIds'
+ "Trap: Hog": ItemData(G.Trap, 1338900, C.trap),
+ "Trap: Alpha Hog": ItemData(G.Trap, 1338901, C.trap),
+ "Trap: Johnny": ItemData(G.Trap, 1338902, C.trap),
+ "Trap: Cliff Hog": ItemData(G.Trap, 1338903, C.trap),
+ "Trap: Nuclear Hog": ItemData(G.Trap, 1338904, C.trap),
+ "Trap: Not the Bees": ItemData(G.Trap, 1338905, C.trap),
+ "Trap: Hatcher": ItemData(G.Trap, 1338906, C.trap),
+ "Trap: Doggo with Pulse Nobelisk": ItemData(G.Trap, 1338907, C.trap),
+ "Trap: Doggo with Nuke Nobelisk": ItemData(G.Trap, 1338908, C.trap),
+ "Doggo with Power Slug": ItemData(G.Parts, 1338909, C.filler),
+ "Trap: Doggo with Gas Nobelisk": ItemData(G.Trap, 1338910, C.trap),
+ "Trap: Spore Flower": ItemData(G.Trap, 1338911, C.trap),
+ "Trap: Stinger": ItemData(G.Trap, 1338912, C.trap),
+ "Trap: Gas Stinger": ItemData(G.Trap, 1338913, C.trap),
+ "Trap: Small Stinger": ItemData(G.Trap, 1338914, C.trap),
+ "Trap: Spitter": ItemData(G.Trap, 1338915, C.trap),
+ "Trap: Alpha Spitter": ItemData(G.Trap, 1338916, C.trap),
+ "Trap: Nuclear Waste Drop": ItemData(G.Trap, 1338917, C.trap),
+ "Trap: Plutonium Waste Drop": ItemData(G.Trap, 1338918, C.trap),
+ "Trap: Elite Hatcher": ItemData(G.Trap, 1338919, C.trap),
+ "Trap: Can of Beans": ItemData(G.Trap, 1338920, C.trap),
+ "Trap: Fart Cloud": ItemData(G.Trap, 1338921, C.trap),
+
+ "Building: Space Elevator": ItemData(G.Building, 1338999, C.progression),
+
+ # Resource singles
+ "Single: Adaptive Control Unit": ItemData(G.Parts, 1339000, count=0),
+ "Single: AI Limiter": ItemData(G.Parts, 1339001, count=0),
+ "Single: Alclad Aluminum Sheet": ItemData(G.Parts, 1339002, count=0),
+ "Single: Blue Power Slug": ItemData(G.Parts, 1339003, count=0),
+ "Single: Yellow Power Slug": ItemData(G.Parts, 1339004, count=0),
+ "Single: Alien Protein": ItemData(G.Parts, 1339005, count=0),
+ "Single: Purple Power Slug": ItemData(G.Parts, 1339006, count=0),
+ "Single: Aluminum Casing": ItemData(G.Parts, 1339007, count=0),
+ "Single: Aluminum Ingot": ItemData(G.Parts, 1339008, count=0),
+ "Single: Aluminum Scrap": ItemData(G.Parts, 1339009, count=0),
+ "Single: Assembly Director System": ItemData(G.Parts, 1339010, count=0),
+ "Single: Automated Wiring": ItemData(G.Parts, 1339011, count=0),
+ "Single: Battery": ItemData(G.Parts, 1339012, count=0),
+ "Single: Bauxite": ItemData(G.Parts, 1339013, count=0),
+ "Single: Neural-Quantum Processor": ItemData(G.Parts, 1339014, count=0),
+ "Single: Biomass": ItemData(G.Parts, 1339015, count=0),
+ "Single: Black Powder": ItemData(G.Parts, 1339016, count=0),
+ "Single: Cable": ItemData(G.Parts, 1339017, count=0),
+ "Single: Caterium Ingot": ItemData(G.Parts, 1339018, count=0),
+ "Single: Caterium Ore": ItemData(G.Parts, 1339019, count=0),
+ "Single: Circuit Board": ItemData(G.Parts, 1339020, count=0),
+ "Single: Coal": ItemData(G.Parts, 1339021, count=0),
+ "Single: Singularity Cell": ItemData(G.Parts, 1339022, count=0),
+ "Single: Compacted Coal": ItemData(G.Parts, 1339023, count=0),
+ "Single: Computer": ItemData(G.Parts, 1339024, count=0),
+ "Single: Concrete": ItemData(G.Parts, 1339025, count=0),
+ "Single: Cooling System": ItemData(G.Parts, 1339026, count=0),
+ "Single: Copper Ingot": ItemData(G.Parts, 1339027, count=0),
+ "Single: Copper Ore": ItemData(G.Parts, 1339028, count=0),
+ "Single: Copper Powder": ItemData(G.Parts, 1339029, count=0),
+ "Single: Copper Sheet": ItemData(G.Parts, 1339030, count=0),
+ "Single: Adequate Pioneering Statue": ItemData(G.Parts, 1339031, count=0),
+ "Single: Crystal Oscillator": ItemData(G.Parts, 1339032, count=0),
+ "Single: Electromagnetic Control Rod": ItemData(G.Parts, 1339033, count=0),
+ "Single: Empty Canister": ItemData(G.Parts, 1339034, count=0),
+ "Single: Empty Fluid Tank": ItemData(G.Parts, 1339035, count=0),
+ "Single: Encased Industrial Beam": ItemData(G.Parts, 1339036, count=0),
+ "Single: Encased Plutonium Cell": ItemData(G.Trap, 1339037, C.trap, count=0),
+ "Single: Encased Uranium Cell": ItemData(G.Trap, 1339038, C.trap, count=0),
+ "Single: Fabric": ItemData(G.Parts, 1339039, count=0),
+ "Single: FICSIT Coupon": ItemData(G.Parts, 1339040, count=0),
+ "Single: AI Expansion Server": ItemData(G.Parts, 1339041, count=0),
+ "Single: Fused Modular Frame": ItemData(G.Parts, 1339042, count=0),
+ "Single: Hard Drive": ItemData(G.Parts, 1339043, count=0),
+ "Single: Heat Sink": ItemData(G.Parts, 1339044, count=0),
+ "Single: Heavy Modular Frame": ItemData(G.Parts, 1339045, count=0),
+ "Single: High-Speed Connector": ItemData(G.Parts, 1339046, count=0),
+ "Single: Satisfactory Pioneering Statue": ItemData(G.Parts, 1339047, count=0),
+ "Single: Pretty Good Pioneering Statue": ItemData(G.Parts, 1339048, count=0),
+ "Single: Iron Ingot": ItemData(G.Parts, 1339049, count=0),
+ "Single: Iron Ore": ItemData(G.Parts, 1339050, count=0),
+ "Single: Iron Plate": ItemData(G.Parts, 1339051, count=0),
+ "Single: Iron Rod": ItemData(G.Parts, 1339052, count=0),
+ "Single: Golden Nut Statue": ItemData(G.Parts, 1339053, count=0),
+ "Single: Leaves": ItemData(G.Parts, 1339054, count=0),
+ "Single: Limestone": ItemData(G.Parts, 1339055, count=0),
+ "Single: Magnetic Field Generator": ItemData(G.Parts, 1339056, count=0),
+ "Single: Mercer Sphere": ItemData(G.Parts, 1339057, count=0),
+ "Single: Modular Engine": ItemData(G.Parts, 1339058, count=0),
+ "Single: Modular Frame": ItemData(G.Parts, 1339059, count=0),
+ "Single: Motor": ItemData(G.Parts, 1339060, count=0),
+ "Single: Mycelia": ItemData(G.Parts, 1339061, count=0),
+ "Single: Non-fissile Uranium": ItemData(G.Trap, 1339062, C.trap, count=0),
+ "Single: Nuclear Pasta": ItemData(G.Parts, 1339063, count=0),
+ "Single: Lizard Doggo Statue": ItemData(G.Parts, 1339064, count=0),
+ "Single: Organic Data Capsule": ItemData(G.Parts, 1339065, count=0),
+ "Single: Packaged Alumina Solution": ItemData(G.Parts, 1339066, count=0),
+ "Single: Packaged Fuel": ItemData(G.Parts, 1339067, count=0),
+ "Single: Packaged Heavy Oil Residue": ItemData(G.Parts, 1339068, count=0),
+ "Single: Packaged Liquid Biofuel": ItemData(G.Parts, 1339069, count=0),
+ "Single: Packaged Nitric Acid": ItemData(G.Parts, 1339070, count=0),
+ "Single: Packaged Nitrogen Gas": ItemData(G.Parts, 1339071, count=0),
+ "Single: Packaged Oil": ItemData(G.Parts, 1339072, count=0),
+ "Single: Packaged Sulfuric Acid": ItemData(G.Parts, 1339073, count=0),
+ "Single: Packaged Turbofuel": ItemData(G.Parts, 1339074, count=0),
+ "Single: Packaged Water": ItemData(G.Parts, 1339075, count=0),
+ "Single: Petroleum Coke": ItemData(G.Parts, 1339076, count=0),
+ "Single: Plastic": ItemData(G.Parts, 1339077, count=0),
+ "Single: Plutonium Fuel Rod": ItemData(G.Trap, 1339078, C.trap, count=0),
+ "Single: Plutonium Pellet": ItemData(G.Trap, 1339079, C.trap, count=0),
+ "Single: Plutonium Waste": ItemData(G.Trap, 1339080, C.trap, count=0),
+ "Single: Polymer Resin": ItemData(G.Parts, 1339081, count=0),
+ "Single: Power Shard": ItemData(G.Parts, 1339082, count=0),
+ "Single: Confusing Creature Statue": ItemData(G.Parts, 1339083, count=0),
+ "Single: Pressure Conversion Cube": ItemData(G.Parts, 1339084, count=0),
+ "Single: Alien Power Matrix": ItemData(G.Parts, 1339085, count=0),
+ "Single: Quartz Crystal": ItemData(G.Parts, 1339086, count=0),
+ "Single: Quickwire": ItemData(G.Parts, 1339087, count=0),
+ "Single: Radio Control Unit": ItemData(G.Parts, 1339088, count=0),
+ "Single: Raw Quartz": ItemData(G.Parts, 1339089, count=0),
+ "Single: Reinforced Iron Plate": ItemData(G.Parts, 1339090, count=0),
+ "Single: Rotor": ItemData(G.Parts, 1339091, count=0),
+ "Single: Rubber": ItemData(G.Parts, 1339092, count=0),
+ "Single: SAM": ItemData(G.Parts, 1339093, count=0),
+ "Single: Screw": ItemData(G.Parts, 1339094, count=0),
+ "Single: Silica": ItemData(G.Parts, 1339095, count=0),
+ "Single: Smart Plating": ItemData(G.Parts, 1339096, count=0),
+ "Single: Smokeless Powder": ItemData(G.Parts, 1339097, count=0),
+ "Single: Solid Biofuel": ItemData(G.Parts, 1339098, count=0),
+ "Single: Somersloop": ItemData(G.Parts, 1339099, count=0),
+ "Single: Stator": ItemData(G.Parts, 1339100, count=0),
+ "Single: Silver Hog Statue": ItemData(G.Parts, 1339101, count=0),
+ "Single: Steel Beam": ItemData(G.Parts, 1339102, count=0),
+ "Single: Steel Ingot": ItemData(G.Parts, 1339103, count=0),
+ "Single: Steel Pipe": ItemData(G.Parts, 1339104, count=0),
+ "Single: Sulfur": ItemData(G.Parts, 1339105, count=0),
+ "Single: Supercomputer": ItemData(G.Parts, 1339106, count=0),
+ "Single: Superposition Oscillator": ItemData(G.Parts, 1339107, count=0),
+ "Single: Thermal Propulsion Rocket": ItemData(G.Parts, 1339108, count=0),
+ "Single: Turbo Motor": ItemData(G.Parts, 1339109, count=0),
+ "Single: Hog Remains": ItemData(G.Parts, 1339110, count=0),
+ "Single: Uranium": ItemData(G.Trap, 1339111, C.trap, count=0),
+ "Single: Uranium Fuel Rod": ItemData(G.Trap, 1339112, C.trap, count=0),
+ "Single: Uranium Waste": ItemData(G.Trap, 1339113, C.trap, count=0),
+ "Single: Versatile Framework": ItemData(G.Parts, 1339114, count=0),
+ "Single: Wire": ItemData(G.Parts, 1339115, count=0),
+ "Single: Wood": ItemData(G.Parts, 1339116, count=0),
+ "Single: Plasma Spitter Remains": ItemData(G.Parts, 1339117, count=0),
+ "Single: Stinger Remains": ItemData(G.Parts, 1339118, count=0),
+ "Single: Hatcher Remains": ItemData(G.Parts, 1339119, count=0),
+ "Single: Alien DNA Capsule": ItemData(G.Parts, 1339120, count=0),
+ "Single: Diamonds": ItemData(G.Parts, 1339121, count=0),
+ "Single: Time Crystal": ItemData(G.Parts, 1339122, count=0),
+ "Single: Ficsite Ingot": ItemData(G.Parts, 1339123, count=0),
+ "Single: Ficsite Trigon": ItemData(G.Parts, 1339124, count=0),
+ "Single: Reanimated SAM": ItemData(G.Parts, 1339125, count=0),
+ "Single: SAM Fluctuator": ItemData(G.Parts, 1339126, count=0),
+ "Single: Biochemical Sculptor": ItemData(G.Parts, 1339127, count=0),
+ "Single: Ballistic Warp Drive": ItemData(G.Parts, 1339128, count=0),
+ "Single: Ficsonium": ItemData(G.Trap, 1339129, C.trap, count=0),
+ "Single: Ficsonium Fuel Rod": ItemData(G.Trap, 1339130, C.trap, count=0),
+ "Single: Packaged Rocket Fuel": ItemData(G.Parts, 1339131, count=0),
+ "Single: Packaged Ionized Fuel": ItemData(G.Parts, 1339132, count=0),
+ "Single: Dark Matter Crystal": ItemData(G.Parts, 1339133, count=0),
+ # 1339134 - 1339149 Reserved for future parts
+ # 1339150 - 1339199 Equipment / Ammo
+ "Single: Bacon Agaric": ItemData(G.Ammo, 1339150, count=0),
+ "Single: Beryl Nut": ItemData(G.Ammo, 1339151, count=0),
+ "Single: Blade Runners": ItemData(G.Equipment, 1339152),
+ "Single: Boom Box": ItemData(G.Equipment, 1339153),
+ "Single: Chainsaw": ItemData(G.Equipment, 1339154, C.useful),
+ "Single: Cluster Nobelisk": ItemData(G.Ammo, 1339155, count=0),
+ "Single: Iodine-Infused Filter": ItemData(G.Equipment, 1339156, count=0), # 1.1
+ "Single: Cup": ItemData(G.Equipment, 1339157),
+ "Single: Cup (gold)": ItemData(G.Equipment, 1339158, count=0),
+ "Single: Explosive Rebar": ItemData(G.Ammo, 1339159, count=0),
+ "Single: Factory Cart": ItemData(G.Equipment, 1339160, C.useful),
+ "Single: Factory Cart (golden)": ItemData(G.Equipment, 1339161, count=0),
+ "Single: Gas Mask": ItemData(G.Equipment, 1339162, C.useful),
+ "Single: Gas Nobelisk": ItemData(G.Ammo, 1339163, count=0),
+ "Single: Hazmat Suit": ItemData(G.Equipment, 1339164, C.useful),
+ "Single: Homing Rifle Ammo": ItemData(G.Ammo, 1339165, count=0),
+ "Single: Hoverpack": ItemData(G.Equipment, 1339166, C.useful),
+ "Single: Iron Rebar": ItemData(G.Ammo, 1339167, count=0),
+ "Single: Jetpack": ItemData(G.Equipment, 1339168, C.useful),
+ "Single: Medicinal Inhaler": ItemData(G.Ammo, 1339169, count=0),
+ "Single: Nobelisk": ItemData(G.Ammo, 1339170, count=0),
+ "Single: Nobelisk Detonator": ItemData(G.Equipment, 1339171, C.useful),
+ "Single: Nuke Nobelisk": ItemData(G.Ammo, 1339172, count=0),
+ "Single: Object Scanner": ItemData(G.Equipment, 1339173),
+ "Single: Paleberry": ItemData(G.Ammo, 1339174, count=0),
+ "Single: Parachute": ItemData(G.Equipment, 1339175, C.useful),
+ "Single: Pulse Nobelisk": ItemData(G.Ammo, 1339176, count=0),
+ "Single: Rebar Gun": ItemData(G.Equipment, 1339177, C.useful),
+ "Single: Rifle": ItemData(G.Equipment, 1339178, C.useful),
+ "Single: Rifle Ammo": ItemData(G.Ammo, 1339179, count=0),
+ "Single: Shatter Rebar": ItemData(G.Ammo, 1339180, count=0),
+ "Single: Stun Rebar": ItemData(G.Ammo, 1339181, count=0),
+ "Single: Turbo Rifle Ammo": ItemData(G.Ammo, 1339182, count=0),
+ "Single: Xeno-Basher": ItemData(G.Equipment, 1339183, C.useful),
+ "Single: Xeno-Zapper": ItemData(G.Equipment, 1339184, C.useful),
+ "Single: Zipline": ItemData(G.Equipment, 1339185, C.useful),
+ "Single: Portable Miner": ItemData(G.Equipment, 1339186),
+ "Single: Gas Filter": ItemData(G.Equipment, 1339187, count=0)
+ }
+
+ item_names_and_ids: ClassVar[dict[str, int]] = {name: data.code for name, data in item_data.items()}
+ filler_items: ClassVar[tuple[str, ...]] = tuple(item for item, details in item_data.items()
+ if details.count > 0 and details.category & (G.Parts | G.Ammo))
+
+ @classmethod
+ def get_item_names_per_category(cls, game_logic: GameLogic) -> dict[str, set[str]]:
+ groups: dict[str, set[str]] = {}
+
+ # To allow hinting for first part recipe in logic
+ for part, recipes in game_logic.recipes.items():
+ recipes_for_part: set[str] = {recipe.name for recipe in recipes if not recipe.implicitly_unlocked}
+ if recipes_for_part:
+
+ for original, indirect in game_logic.indirect_recipes.items():
+ if indirect in recipes_for_part:
+ recipes_for_part.remove(indirect)
+ recipes_for_part.add(original)
+
+ groups[part] = recipes_for_part
+
+ for name, data in cls.item_data.items():
+ for category in data.category:
+ if category != G.NeverExclude:
+ groups.setdefault(category.name, set()).add(name)
+
+ return groups
+
+ player: int
+ logic: GameLogic
+ random: Random
+ critical_path: CriticalPathCalculator
+
+ trap_chance: int
+ enabled_traps: tuple[str, ...]
+
+ def __init__(self, player: Optional[int], logic: GameLogic, random: Random,
+ options: SatisfactoryOptions, critical_path: CriticalPathCalculator):
+ self.player = player
+ self.logic = logic
+ self.random = random
+ self.critical_path = critical_path
+ self.options = options
+
+ self.trap_chance = self.options.trap_chance.value
+ self.enabled_traps = tuple(sorted(self.options.trap_selection_override.value))
+
+ @classmethod
+ def create_item_uninitialized(cls, name: str, player: int) -> Item:
+ data: ItemData = cls.item_data[name]
+ return Item(name, data.type, data.code, player)
+
+ def create_item(self, name: str, player: int) -> Item:
+ data: ItemData = self.item_data[name]
+ item_type = data.type
+
+ if item_type == C.progression \
+ and (data.category & (G.Recipe | G.Building)) and not (data.category & G.NeverExclude) \
+ and self.critical_path.required_item_names and name not in self.critical_path.required_item_names:
+ item_type = C.useful
+
+ return Item(name, item_type, data.code, player)
+
+ @classmethod
+ def get_filler_item_name_uninitialized(cls, random: Random) -> str:
+ return random.choice(cls.filler_items)
+
+ def get_filler_item_name(self, random: Random, filler_items: Sequence[str] | None) -> str:
+ if self.enabled_traps and random.random() < (self.trap_chance / 100):
+ return random.choice(self.enabled_traps)
+ else:
+ if filler_items:
+ return random.choice(filler_items)
+ else:
+ return Items.get_filler_item_name_uninitialized(random)
+
+ def get_excluded_items(self, precollected_items: list[Item]) -> set[str]:
+ excluded_items: set[str] = {
+ item.name
+ for item in precollected_items
+ if item.name in self.item_data and item.name not in self.options.start_inventory_from_pool.value
+ }
+
+ excluded_items.update({"Building: " + building for building in self.critical_path.buildings_to_exclude})
+ excluded_items.update({"Bundle: " + part for part in self.critical_path.parts_to_exclude})
+ excluded_items.update({"Single: " + part for part in self.critical_path.parts_to_exclude})
+
+ excluded_items.update({recipe for recipe in self.critical_path.recipes_to_exclude})
+ excluded_items.update(self.critical_path.implicitly_unlocked)
+
+ # since we dont have part logic setup for Transports
+ if self.options.final_elevator_phase == 1:
+ excluded_items.add("Transport: Drones")
+
+ # Remove excluded items that arent unique
+ excluded_items = excluded_items - {
+ item_name
+ for item_name, data in self.item_data.items()
+ if data.category & (G.Parts | G.Equipment | G.Ammo | G.Trap | G.Upgrades)
+ }
+
+ return excluded_items
+
+ def build_item_pool(self, random: Random, precollected_items: list[Item], number_of_locations: int) -> list[Item]:
+ excluded_from_pool: set[str] = self.get_excluded_items(precollected_items)
+
+ pool_items: list[Item] = [
+ self.create_item(name, self.player)
+ for name, data in self.item_data.items()
+ for _ in range(data.count)
+ if data.category & (G.Recipe | G.Building | G.Equipment | G.Ammo | G.Transport | G.Upgrades)
+ and name not in excluded_from_pool
+ ]
+ pool: list[Item] = [
+ item
+ for item in pool_items
+ if item.classification != C.filler or self.item_data[item.name].category & (G.Equipment | G.Ammo)
+ ]
+
+ free_space: int = number_of_locations - len(pool)
+ if free_space < 0:
+ raise Exception(f"Location pool starved, trying to add {len(pool)} items to {number_of_locations} locations")
+
+ non_excluded_filler_items: list[str] = [item for item in self.filler_items if item not in excluded_from_pool]
+ pool += [
+ self.create_item(self.get_filler_item_name(random, non_excluded_filler_items), self.player)
+ for _ in range(free_space)
+ ]
+
+ return pool
diff --git a/worlds/satisfactory/Locations.py b/worlds/satisfactory/Locations.py
new file mode 100644
index 000000000000..0174b178f867
--- /dev/null
+++ b/worlds/satisfactory/Locations.py
@@ -0,0 +1,426 @@
+from typing import ClassVar, Optional
+from collections.abc import Iterable, Callable
+from math import ceil, floor
+from BaseClasses import CollectionState
+from .GameLogic import GameLogic, Recipe, Building, PowerInfrastructureLevel, DropPodData
+from .StateLogic import StateLogic, EventId, part_event_prefix, building_event_prefix
+from .Items import Items
+from .Options import SatisfactoryOptions
+from .CriticalPathCalculator import CriticalPathCalculator
+
+
+class LocationData:
+ __slots__ = ("region", "name", "event_name", "code", "non_progression", "rule")
+ region: str
+ name: str
+ event_name: str
+ code: Optional[int]
+ non_progression: Optional[bool]
+ rule: Optional[Callable[[CollectionState], bool]]
+
+ def __init__(self, region: str, name: str, code: Optional[int], event_name: Optional[str] = None,
+ non_progression: Optional[bool] = False, rule: Optional[Callable[[CollectionState], bool]] = None):
+ self.region = region
+ self.name = name
+ self.code = code
+ self.rule = rule
+ self.non_progression = non_progression
+ self.event_name = event_name or name
+
+
+class Part(LocationData):
+ @staticmethod
+ def get_parts(state_logic: StateLogic, recipes: tuple[Recipe, ...], name: str,
+ final_elevator_phase: int) -> list[LocationData]:
+ recipes_per_region: dict[str, list[Recipe]] = {}
+
+ for recipe in recipes:
+ if recipe.minimal_phase > final_elevator_phase:
+ continue
+
+ recipes_per_region.setdefault(recipe.building or "Overworld", []).append(recipe)
+
+ return [Part(state_logic, region, recipes_for_region, name)
+ for region, recipes_for_region in recipes_per_region.items()]
+
+ def __init__(self, state_logic: StateLogic, region: str, recipes: Iterable[Recipe], name: str):
+ super().__init__(region, part_event_prefix + name + " in " + region, EventId, part_event_prefix + name,
+ rule=Part.can_produce_any_recipe_for_part(state_logic, recipes))
+
+ @staticmethod
+ def can_produce_any_recipe_for_part(state_logic: StateLogic, recipes: Iterable[Recipe]) \
+ -> Callable[[CollectionState], bool]:
+
+ recipe_rules = tuple(state_logic.get_can_produce_specific_recipe_for_part_rule(recipe) for recipe in recipes)
+
+ def can_build_by_any_recipe(state: CollectionState) -> bool:
+ return any(rule(state) for rule in recipe_rules)
+
+ return can_build_by_any_recipe
+
+
+class EventBuilding(LocationData):
+ def __init__(self, state_logic: StateLogic, building_name: str, building: Building):
+ super().__init__("Overworld", building_event_prefix + building_name, EventId,
+ rule=EventBuilding.get_can_create_building_rule(state_logic, building))
+
+ @staticmethod
+ def get_can_create_building_rule(state_logic: StateLogic, building: Building) \
+ -> Callable[[CollectionState], bool]:
+ handcrafting_rule = state_logic.get_can_produce_all_allowing_handcrafting_rule(building.inputs)
+
+ def can_build(state: CollectionState) -> bool:
+ return state_logic.has_recipe(state, building) \
+ and state_logic.can_power(state, building.power_requirement) \
+ and handcrafting_rule(state)
+
+ return can_build
+
+
+class PowerInfrastructure(LocationData):
+ def __init__(self, state_logic: StateLogic,
+ power_level: PowerInfrastructureLevel, recipes: Iterable[Recipe]):
+ super().__init__("Overworld", building_event_prefix + power_level.to_name(), EventId,
+ rule=PowerInfrastructure.get_can_create_power_infrastructure_rule(state_logic, power_level, recipes))
+
+ @staticmethod
+ def get_can_create_power_infrastructure_rule(state_logic: StateLogic,
+ power_level: PowerInfrastructureLevel, recipes: Iterable[Recipe])\
+ -> Callable[[CollectionState], bool]:
+
+ higher_levels = tuple(level for level in PowerInfrastructureLevel if level > power_level)
+
+ def can_power(state: CollectionState) -> bool:
+ return any(state_logic.can_power(state, higher_level) for higher_level in higher_levels) \
+ or any(state_logic.can_build(state, recipe.building) for recipe in recipes)
+
+ return can_power
+
+
+class ElevatorPhase(LocationData):
+ def __init__(self, phase_index: int, state_logic: StateLogic, game_logic: GameLogic):
+ super().__init__("Overworld", f"Elevator Phase {phase_index + 1}", EventId,
+ rule=lambda state: state_logic.can_build(state, "Space Elevator") and
+ state_logic.can_produce_all(state, game_logic.space_elevator_phases[phase_index].keys()))
+
+
+class HubSlot(LocationData):
+ def __init__(self, tier: int, milestone: int, slot: int, location_id: int):
+ super().__init__(f"Hub {tier}-{milestone}", f"Hub {tier}-{milestone}, item {slot}", location_id)
+
+
+class MamSlot(LocationData):
+ def __init__(self, tree: str, node_name: str, location_id: int):
+ super().__init__(f"{tree}: {node_name}", f"{tree}: {node_name}", location_id)
+
+
+class ShopSlot(LocationData):
+ def __init__(self, state_logic: Optional[StateLogic], slot: int, cost: int, location_id: int):
+ super().__init__("AWESOME Shop", f"AWESOME Shop purchase {slot}", location_id,
+ rule=ShopSlot.can_purchase_from_shop(state_logic, cost))
+
+ @staticmethod
+ def can_purchase_from_shop(state_logic: Optional[StateLogic], cost: int) -> Callable[[CollectionState], bool]:
+ def can_purchase(state: CollectionState) -> bool:
+ if not state_logic or cost < 20:
+ return True
+ elif 20 <= cost < 50:
+ return state_logic.is_elevator_phase(state, 1)
+ elif 50 <= cost < 100:
+ return state_logic.is_elevator_phase(state, 2)
+ else:
+ return state_logic.is_elevator_phase(state, 3)
+
+ return can_purchase
+
+
+class HardDrive(LocationData):
+ def __init__(self, data: DropPodData, state_logic: Optional[StateLogic],
+ location_id: int, tier: int, can_hold_progression: bool):
+
+ # drop pod locations are unlocked by hard drives, there is currently no direct mapping between location and hard drive
+ # we currently do not know how many hdd require gas or radioactive protection
+ # coordinates are for us to reference them, there is no real link between coordinate and check
+ def get_region(gassed: Optional[bool], radioactive: Optional[bool]) -> str:
+ return f"Hub Tier {tier}"
+
+ def get_rule(unlocked_by: Optional[str], power_needed: int) -> Callable[[CollectionState], bool]:
+ # Power is kept out of logic. with energy link its simple,
+ # without you just going to have to figure it your yourself
+
+ def logic_rule(state: CollectionState) -> bool:
+ return state_logic.can_build(state, "MAM") and (
+ (not unlocked_by) or (state_logic and state_logic.can_produce(state, unlocked_by)))
+
+ return logic_rule
+
+ super().__init__(
+ get_region(data.gassed, data.radioactive),
+ f"Hard drive random check {(location_id - 1338600) + 1}", location_id,
+ non_progression=not can_hold_progression, rule=get_rule(data.item, data.power))
+
+
+class Locations:
+ game_logic: Optional[GameLogic]
+ options: Optional[SatisfactoryOptions]
+ state_logic: Optional[StateLogic]
+ items: Optional[Items]
+ critical_path: Optional[CriticalPathCalculator]
+
+ hub_location_start: ClassVar[int] = 1338000
+ max_tiers: ClassVar[int] = 10
+ max_milestones: ClassVar[int] = 5
+ max_slots: ClassVar[int] = 10
+ drop_pod_location_id_start: ClassVar[int] = 1338600
+ drop_pod_location_id_end: ClassVar[int] = 1338699
+
+ def __init__(self, game_logic: Optional[GameLogic] = None, options: Optional[SatisfactoryOptions] = None,
+ state_logic: Optional[StateLogic] = None, items: Optional[Items] = None,
+ critical_path: Optional[CriticalPathCalculator] = None):
+ self.game_logic = game_logic
+ self.options = options
+ self.state_logic = state_logic
+ self.items = items
+ self.critical_path = critical_path
+
+ def get_base_location_table(self, max_tier: int) -> list[LocationData]:
+ all_locations = [
+ MamSlot("Alien Organisms", "Inflated Pocket Dimension", 1338500),
+ MamSlot("Alien Organisms", "Hostile Organism Detection", 1338501),
+ MamSlot("Alien Organisms", "Expanded Toolbelt", 1338502),
+ MamSlot("Alien Organisms", "Bio-Organic Properties", 1338503),
+ MamSlot("Alien Organisms", "Stinger Research", 1338504),
+ MamSlot("Alien Organisms", "Hatcher Research", 1338505),
+ MamSlot("Alien Organisms", "Hog Research", 1338506),
+ MamSlot("Alien Organisms", "Spitter Research", 1338507),
+ MamSlot("Alien Organisms", "Structural Analysis", 1338508),
+ MamSlot("Alien Organisms", "Protein Inhaler", 1338509),
+ MamSlot("Alien Organisms", "The Rebar Gun", 1338510),
+ MamSlot("Caterium", "Caterium Electronics", 1338511),
+ MamSlot("Caterium", "Bullet Guidance System", 1338512),
+ MamSlot("Caterium", "High-Speed Connector", 1338513),
+ MamSlot("Caterium", "Caterium", 1338514),
+ MamSlot("Caterium", "Caterium Ingots", 1338515),
+ MamSlot("Caterium", "Quickwire", 1338516),
+ MamSlot("Caterium", "Power Switch", 1338517),
+ MamSlot("Caterium", "Power Poles Mk.2", 1338518),
+ MamSlot("Caterium", "AI Limiter", 1338519),
+ MamSlot("Caterium", "Smart Splitter", 1338520),
+ MamSlot("Caterium", "Programmable Splitter", 1338521),
+ MamSlot("Mycelia", "Gas Mask", 1338522), # 1.0
+ MamSlot("Caterium", "Zipline", 1338523),
+ MamSlot("Caterium", "Geothermal Generator", 1338524),
+ MamSlot("Caterium", "Priority Power Switch", 1338525),
+ MamSlot("Caterium", "Stun Rebar", 1338526),
+ MamSlot("Caterium", "Power Poles Mk.3", 1338527),
+ MamSlot("Mycelia", "Therapeutic Inhaler", 1338528),
+ MamSlot("Mycelia", "Expanded Toolbelt", 1338529),
+ MamSlot("Mycelia", "Mycelia", 1338530),
+ MamSlot("Mycelia", "Fabric", 1338531),
+ MamSlot("Mycelia", "Medical Properties", 1338532),
+ MamSlot("Mycelia", "Toxic Cellular Modification", 1338533),
+ MamSlot("Mycelia", "Vitamin Inhaler", 1338534),
+ MamSlot("Mycelia", "Parachute", 1338535),
+ MamSlot("Mycelia", "Synthethic Polyester Fabric", 1338536),
+ MamSlot("Nutrients", "Bacon Agaric", 1338537),
+ MamSlot("Nutrients", "Beryl Nut", 1338538),
+ MamSlot("Nutrients", "Paleberry", 1338539),
+ MamSlot("Nutrients", "Nutritional Processor", 1338540),
+ MamSlot("Nutrients", "Nutritional Inhaler", 1338541),
+ MamSlot("Power Slugs", "Slug Scanning", 1338542),
+ MamSlot("Power Slugs", "Blue Power Slugs", 1338543),
+ MamSlot("Power Slugs", "Yellow Power Shards", 1338544),
+ MamSlot("Power Slugs", "Purple Power Shards", 1338545),
+ MamSlot("Power Slugs", "Overclock Production", 1338546),
+ MamSlot("Quartz", "Crystal Oscillator", 1338547),
+ MamSlot("Quartz", "Quartz Crystals", 1338548),
+ MamSlot("Quartz", "Quartz", 1338549),
+ MamSlot("Quartz", "Shatter Rebar", 1338550),
+ MamSlot("Quartz", "Silica", 1338551),
+ MamSlot("Quartz", "Explosive Resonance Application", 1338552),
+ MamSlot("Quartz", "Blade Runners", 1338553),
+ MamSlot("Quartz", "The Explorer", 1338554),
+ MamSlot("Quartz", "Radio Signal Scanning", 1338555),
+ MamSlot("Quartz", "Inflated Pocket Dimension", 1338556),
+ MamSlot("Quartz", "Radar Technology", 1338557),
+ MamSlot("Sulfur", "The Nobelisk Detonator", 1338558),
+ MamSlot("Sulfur", "Smokeless Powder", 1338559),
+ MamSlot("Sulfur", "Sulfur", 1338560),
+ MamSlot("Sulfur", "Inflated Pocket Dimension", 1338561),
+ MamSlot("Sulfur", "The Rifle", 1338562),
+ MamSlot("Sulfur", "Compacted Coal", 1338563),
+ MamSlot("Sulfur", "Black Powder", 1338564),
+ MamSlot("Sulfur", "Explosive Rebar", 1338565),
+ MamSlot("Sulfur", "Cluster Nobelisk", 1338566),
+ MamSlot("Sulfur", "Experimental Power Generation", 1338567),
+ # 1338568 Turbo Rifle Ammo
+ MamSlot("Sulfur", "Turbo Fuel", 1338569),
+ MamSlot("Sulfur", "Expanded Toolbelt", 1338570),
+ # 1338571 Nuclear Deterrent Development
+ # 1338572 Synthetic Power Shards
+ # 1338573 Rocket Fuel
+ # 1338574 Ionized Fuel
+ MamSlot("Alien Technology", "SAM Analysis", 1338575),
+ MamSlot("Alien Technology", "SAM Reanimation", 1338576),
+ MamSlot("Alien Technology", "SAM Fluctuator", 1338577),
+ MamSlot("Alien Technology", "Mercer Sphere Analysis", 1338578),
+ MamSlot("Alien Technology", "Dimensional Depot", 1338579),
+ MamSlot("Alien Technology", "Manual Depot Uploader", 1338580),
+ MamSlot("Alien Technology", "Depot Expansion (200%)", 1338581),
+ MamSlot("Alien Technology", "Depot Expansion (300%)", 1338582),
+ MamSlot("Alien Technology", "Depot Expansion (400%)", 1338583),
+ MamSlot("Alien Technology", "Depot Expansion (500%)", 1338584),
+ MamSlot("Alien Technology", "Upload Upgrade: 30/min", 1338585),
+ MamSlot("Alien Technology", "Upload Upgrade: 60/min", 1338586),
+ MamSlot("Alien Technology", "Upload Upgrade: 120/min", 1338587),
+ MamSlot("Alien Technology", "Upload Upgrade: 240/min", 1338588),
+ MamSlot("Alien Technology", "Somersloop Analysis", 1338589),
+ MamSlot("Alien Technology", "Alien Energy Harvesting", 1338590),
+ MamSlot("Alien Technology", "Production Amplifier", 1338591),
+ MamSlot("Alien Technology", "Power Augmenter", 1338592),
+ # 1338593 Alien Power Matrix
+ MamSlot("Quartz", "Material Resonance Screening", 1338594), # 1.1
+ # 1338600 - 1338699 - Harddrives - Harddrives
+ ShopSlot(self.state_logic, 1, 3, 1338700),
+ ShopSlot(self.state_logic, 2, 3, 1338701),
+ ShopSlot(self.state_logic, 3, 5, 1338702),
+ ShopSlot(self.state_logic, 4, 5, 1338703),
+ ShopSlot(self.state_logic, 5, 10, 1338704),
+ ShopSlot(self.state_logic, 6, 10, 1338705),
+ ShopSlot(self.state_logic, 7, 20, 1338706),
+ ShopSlot(self.state_logic, 8, 20, 1338707),
+ ShopSlot(self.state_logic, 9, 50, 1338708),
+ ShopSlot(self.state_logic, 10, 50, 1338709)
+ ]
+
+ if max_tier > 8:
+ all_locations.append(MamSlot("Power Slugs", "Synthetic Power Shards", 1338572))
+ if max_tier > 8:
+ all_locations.append(MamSlot("Alien Technology", "Alien Power Matrix", 1338593))
+ if max_tier > 2:
+ all_locations.append(MamSlot("Sulfur", "Turbo Rifle Ammo", 1338568))
+ if max_tier > 2:
+ all_locations.append(MamSlot("Sulfur", "Nuclear Deterrent Development", 1338571))
+ if max_tier > 4:
+ all_locations.append(MamSlot("Sulfur", "Rocket Fuel", 1338573))
+ if max_tier > 6:
+ all_locations.append(MamSlot("Sulfur", "Ionized Fuel", 1338574))
+
+ return all_locations
+
+ def get_locations_for_data_package(self) -> dict[str, int]:
+ """Must include all possible location names and their id's"""
+
+ # 1338000 - 1338499 - Milestones
+ # 1338500 - 1338599 - Mam
+ # 1338600 - 1338699 - Harddrives
+ # 1338700 - 1338709 - Shop
+ # 1338999 - Upper bound
+
+ location_table = self.get_base_location_table(self.max_tiers)
+ location_table.extend(self.get_hub_locations(True, self.max_tiers))
+ location_table.extend(self.get_hard_drive_locations(True, self.max_tiers, set()))
+ location_table.append(LocationData("Overworld", "UpperBound", 1338999))
+
+ return {location.name: location.code for location in location_table}
+
+ def get_locations(self) -> list[LocationData]:
+ """Only return location used in this game based on settings"""
+
+ if not self.game_logic or not self.options or not self.state_logic or not self.items:
+ raise Exception("Locations need to be initialized with logic, options and items before using this method")
+
+ max_tier_for_game = min(self.options.final_elevator_phase * 2, len(self.game_logic.hub_layout))
+
+ location_table = self.get_base_location_table(max_tier_for_game)
+ location_table.extend(self.get_hub_locations(False, max_tier_for_game))
+ location_table.extend(self.get_hard_drive_locations(False, max_tier_for_game, self.critical_path.required_parts))
+ location_table.extend(self.get_logical_event_locations(self.options.final_elevator_phase.value))
+
+ return location_table
+
+ def get_hub_locations(self, for_data_package: bool, max_tier: int) -> list[LocationData]:
+ location_table: list[LocationData] = []
+
+ number_of_slots_per_milestone_for_game: int
+ if for_data_package:
+ number_of_slots_per_milestone_for_game = self.max_slots
+ else:
+ if self.options.final_elevator_phase <= 2:
+ number_of_slots_per_milestone_for_game = self.max_slots
+ else:
+ number_of_slots_per_milestone_for_game = self.game_logic.slots_per_milestone
+
+ hub_location_id = self.hub_location_start
+ for tier in range(1, max_tier + 1):
+ for milestone in range(1, self.max_milestones + 1):
+ for slot in range(1, self.max_slots + 1):
+ if for_data_package:
+ location_table.append(HubSlot(tier, milestone, slot, hub_location_id))
+ else:
+ if tier <= max_tier \
+ and milestone <= len(self.game_logic.hub_layout[tier - 1]) \
+ and slot <= number_of_slots_per_milestone_for_game:
+
+ location_table.append(HubSlot(tier, milestone, slot, hub_location_id))
+
+ hub_location_id += 1
+
+ return location_table
+
+ def get_logical_event_locations(self, final_elevator_phase: int) -> list[LocationData]:
+ location_table: list[LocationData] = []
+
+ # for performance plan is to upfront calculated everything we need
+ # and than create one massive state.has_all for each logical gate (hub tiers, elevator phases)
+
+ location_table.extend(
+ ElevatorPhase(phaseIndex, self.state_logic, self.game_logic)
+ for phaseIndex, _ in enumerate(self.game_logic.space_elevator_phases)
+ if phaseIndex < final_elevator_phase)
+ location_table.extend(
+ part
+ for part_name, recipes in self.game_logic.recipes.items()
+ if part_name in self.critical_path.required_parts
+ for part in Part.get_parts(self.state_logic, recipes, part_name, final_elevator_phase))
+ location_table.extend(
+ EventBuilding(self.state_logic, name, building)
+ for name, building in self.game_logic.buildings.items()
+ if name in self.critical_path.required_buildings)
+ location_table.extend(
+ PowerInfrastructure(self.state_logic, power_level, recipes)
+ for power_level, recipes in self.game_logic.requirement_per_powerlevel.items()
+ if power_level <= self.critical_path.required_power_level)
+
+ return location_table
+
+ def get_hard_drive_locations(self, for_data_package: bool, max_tier: int, available_parts: set[str]) \
+ -> list[LocationData]:
+ hard_drive_locations: list[LocationData] = []
+
+ bucket_size: int
+ drop_pod_data: list[DropPodData]
+ if for_data_package:
+ bucket_size = 0
+ drop_pod_data = []
+ else:
+ bucket_size = floor((self.drop_pod_location_id_end - self.drop_pod_location_id_start) / max_tier)
+ drop_pod_data = self.game_logic.drop_pods
+ # sort, easily obtainable first, should be deterministic
+ drop_pod_data.sort(key=lambda dp: ("!" if dp.item is None else dp.item) + str(dp.x - dp.z))
+
+ for location_id in range(self.drop_pod_location_id_start, self.drop_pod_location_id_end + 1):
+ if for_data_package:
+ hard_drive_locations.append(HardDrive(DropPodData(0, 0, 0, None, 0), None, location_id, 1, False))
+ else:
+ location_id_normalized: int = location_id - self.drop_pod_location_id_start
+
+ data: DropPodData = drop_pod_data[location_id_normalized]
+ can_hold_progression: bool = location_id_normalized < self.options.hard_drive_progression_limit.value
+ tier = min(ceil((location_id_normalized + 1) / bucket_size), max_tier)
+
+ if not data.item or data.item in available_parts:
+ hard_drive_locations.append(
+ HardDrive(data, self.state_logic, location_id, tier, can_hold_progression))
+
+ return hard_drive_locations
diff --git a/worlds/satisfactory/Options.py b/worlds/satisfactory/Options.py
new file mode 100644
index 000000000000..6a4865d7451b
--- /dev/null
+++ b/worlds/satisfactory/Options.py
@@ -0,0 +1,614 @@
+from dataclasses import dataclass
+from typing import ClassVar, Any, cast
+from enum import IntEnum
+from schema import Schema, And
+from Options import PerGameCommonOptions, DeathLinkMixin, AssembleOptions, OptionGroup
+from Options import Range, NamedRange, Toggle, DefaultOnToggle, OptionSet, StartInventoryPool, Choice
+
+
+class Placement(IntEnum):
+ starting_inventory = 0
+ early = 1
+ somewhere = 2
+
+
+class PlacementLogicMeta(AssembleOptions):
+ def __new__(mcs, name: str, bases: tuple[type], attrs: dict[Any, Any]) -> "PlacementLogicMeta":
+ if "default" in attrs and isinstance(attrs["default"], Placement):
+ attrs["default"] = int(attrs["default"])
+
+ cls = super(PlacementLogicMeta, mcs).__new__(mcs, name, bases, attrs)
+ return cast(PlacementLogicMeta, cls)
+
+
+class PlacementLogic(Choice, metaclass=PlacementLogicMeta):
+ option_unlocked_from_start = Placement.starting_inventory.value
+ option_early_game = Placement.early.value
+ option_somewhere = Placement.somewhere.value
+
+
+class ChoiceMapMeta(AssembleOptions):
+ def __new__(mcs, name: str, bases: tuple[type], attrs: dict[Any, Any]) -> "ChoiceMapMeta":
+ if "choices" in attrs:
+ for index, choice in enumerate(attrs["choices"]):
+ option_name = "option_" + choice.replace(' ', '_')
+ attrs[option_name] = index
+
+ if "default" in attrs and attrs["default"] == choice:
+ attrs["default"] = index
+
+ cls = super(ChoiceMapMeta, mcs).__new__(mcs, name, bases, attrs)
+ return cast(ChoiceMapMeta, cls)
+
+
+class ChoiceMap(Choice, metaclass=ChoiceMapMeta):
+ choices: ClassVar[dict[str, list[str]]]
+ default: str
+
+ def get_selected_list(self) -> list[str]:
+ for index, choice in enumerate(self.choices):
+ if index == self.value:
+ return self.choices[choice]
+
+ raise Exception(f"ChoiceMap: selected choice {self.value} is not valid, valid choices are: {self.choices.keys()}")
+
+
+class ElevatorPhase(NamedRange):
+ """
+ Put the milestones accessible BEFORE this Space Elevator Phase in logic.
+ Milestones after the selected Phase are empty and contain nothing.
+ If your goal selection contains *Space Elevator Phase* then submitting this Phase's elevator package completes that goal.
+ If the goal is not enabled, this setting simply limits the HUB's content.
+
+ Estimated in-game completion times:
+
+ - **Phase 1 (Tiers 1-2)**: ~3 Hours
+ - **Phase 2 (Tiers 1-4)**: ~8 Hours
+ - **Phase 3 (Tiers 1-6)**: ~50 Hours
+ - **Phase 4 (Tiers 1-8)**: ~100 Hours
+ - **Phase 5 (Tiers 1-9)**: ~150 Hours
+ """
+ display_name = "Final Space Elevator Phase in logic"
+ default = 2
+ range_start = 1
+ range_end = 5
+ special_range_names = {
+ "phase 1 (tiers 1-2)": 1,
+ "phase 2 (tiers 1-4)": 2,
+ "phase 3 (tiers 1-6)": 3,
+ "phase 4 (tiers 1-8)": 4,
+ "phase 5 (tiers 1-9)": 5,
+ }
+
+
+class ResourceSinkPointsTotal(NamedRange):
+ """
+ Does nothing if *AWESOME Sink Points (total)* goal is not enabled.
+
+ Sink an amount of items totalling this amount of points to finish.
+ This setting is a *point count*, not a *coupon* count!
+
+ In the base game, it takes 347 coupons to unlock every non-repeatable purchase, or 1895 coupons to purchase every non-producible item.
+
+ Use the **TFIT - Ficsit Information Tool** mod or the Satisfactory wiki to find out how many points items are worth.
+
+ If you have *Free Samples* enabled, consider setting this higher so that you can't reach the goal just by sinking your Free Samples.
+ """
+ # Coupon data for above comment from https://satisfactory.wiki.gg/wiki/AWESOME_Shop
+ display_name = "AWESOME Sink points total"
+ default = 2166000
+ range_start = 2166000
+ range_end = 18436379500
+ special_range_names = {
+ "50 coupons (~2m points)": 2166000,
+ "100 coupons (~18m points)": 17804500,
+ "150 coupons (~61m points)": 60787500,
+ "200 coupons (~145m points)": 145053500,
+ "250 coupons (~284m points)": 284442000,
+ "300 coupons (~493m points)": 492825000,
+ "350 coupons (~784m points)": 784191000,
+ "400 coupons (~1,2b points)": 1172329500,
+ "450 coupons (~1,7b points)": 1671112500,
+ "500 coupons (~2b points)": 2294578500,
+ "550 coupons (~3b points)": 3056467000,
+ "600 coupons (~4b points)": 3970650000,
+ "650 coupons (~5b points)": 5051216000,
+ "700 coupons (~6b points)": 6311854500,
+ "750 coupons (~8b points)": 7766437500,
+ "800 coupons (~9b points)": 9429103500,
+ "850 coupons (~11b points)": 11313492000,
+ "900 coupons (~13b points)": 13433475000,
+ "950 coupons (~16b points)": 15803241000,
+ "1000 coupons (~18b points)": 18436379500
+ }
+
+
+class ResourceSinkPointsPerMinute(NamedRange):
+ """
+ Does nothing if *AWESOME Sink Points (per minute)* goal is not enabled.
+
+ Sink items to maintain a sink points per minute of the chosen amount for 10 minutes to finish.
+ This setting is in *points per minute* on the orange track, so DNA Capsules don't count.
+ This option's presets are example production thresholds - you don't have to sink exactly those specific items.
+
+ Use the **TFIT - Ficsit Information Tool** mod or the Satisfactory wiki to find out how many points items are worth.
+ """
+ # Coupon data for above comment from https://satisfactory.wiki.gg/wiki/AWESOME_Shop
+ display_name = "AWESOME Sink points per minute"
+ default = 50000
+ range_start = 1000
+ range_end = 10000000
+ special_range_names = {
+ "~500 screw/min": 1000,
+ "~100 reinforced iron plate/min": 12000,
+ "~100 stator/min": 24000,
+ "~100 modular frame/min": 40000,
+ "~100 smart plating/min": 50000,
+ "~20 crystal oscillator/min": 60000,
+ "~50 motor/min": 76000,
+ "~10 heavy modular frame/min": 100000,
+ "~10 radio control unit/min": 300000,
+ "~10 fused modular frame/min": 625000,
+ "~10 supercomputer/min": 1000000,
+ "~10 pressure conversion cube/min": 2500000,
+ "~10 nuclear pasta/min": 5000000,
+ "~4 ballistic warp drive/min": 10000000,
+ }
+
+
+class HardDriveProgressionLimit(Range):
+ """
+ How many Hard Drives can contain progression items.
+ Hard Drives above this count cannot contain progression, but can still be Useful.
+
+ There are 118 total hard drives in the world and the current implementation supports up to 100 progression hard drives.
+ """
+ display_name = "Hard Drive Progression Items"
+ default = 20
+ range_start = 0
+ range_end = 100
+
+
+class FreeSampleEquipment(Range):
+ """
+ How many free sample Equipment items are given when they are unlocked.
+
+ (ex. Jetpack, Rifle)
+ """
+ display_name = "Free Samples: Equipment"
+ default = 1
+ range_start = 0
+ range_end = 10
+
+
+class FreeSampleBuildings(Range):
+ """
+ How many copies of a Building's construction cost are given as a free sample when they are unlocked.
+ Space Elevator is always excluded.
+
+ (ex. Packager, Constructor, Smelter)
+ """
+ display_name = "Free Samples: Buildings"
+ default = 5
+ range_start = 0
+ range_end = 10
+
+
+class FreeSampleParts(NamedRange):
+ """
+ How many general crafting component free samples are given when their recipe is unlocked.
+ Space Elevator Project Parts are always excluded.
+
+ Negative numbers mean that fraction of a full stack.
+
+ (ex. Iron Plate, Packaged Turbofuel, Reinforced Modular Frame)
+ """
+ display_name = "Free Samples: Parts"
+ default = -2
+ range_start = -5
+ range_end = 500
+ special_range_names = {
+ "disabled": 0,
+ "half_stack": -2,
+ "one_stack": -1,
+ "1": 1,
+ "50": 50,
+ "100": 100,
+ "200": 200,
+ "500": 500,
+ }
+
+
+class FreeSampleRadioactive(Toggle):
+ """
+ Allow free samples to include radioactive parts.
+ Remember, they are delivered directly to your player inventory.
+ """
+ display_name = "Free Samples: Radioactive"
+
+
+class TrapChance(Range):
+ """
+ Chance of traps in the item pool.
+ Traps will only replace filler items such as parts and resources.
+
+ - **0:** No traps will be present
+ - **100:** Every filler item will be a trap.
+ """
+ display_name = "Trap Chance"
+ range_start = 0
+ range_end = 100
+ default = 10
+
+
+_trap_types = {
+ "Trap: Doggo with Pulse Nobelisk",
+ "Trap: Doggo with Nuke Nobelisk",
+ "Trap: Doggo with Gas Nobelisk",
+ "Trap: Hog",
+ "Trap: Alpha Hog",
+ "Trap: Cliff Hog",
+ "Trap: Nuclear Hog",
+ "Trap: Johnny",
+ "Trap: Hatcher",
+ "Trap: Elite Hatcher",
+ "Trap: Small Stinger",
+ "Trap: Stinger",
+ "Trap: Gas Stinger",
+ "Trap: Spore Flower",
+ "Trap: Spitter",
+ "Trap: Alpha Spitter",
+ "Trap: Not the Bees",
+ "Trap: Nuclear Waste Drop",
+ "Trap: Plutonium Waste Drop",
+ "Trap: Can of Beans",
+ "Trap: Fart Cloud",
+
+ # Radioactive parts delivered via portal
+ "Bundle: Uranium",
+ "Bundle: Uranium Fuel Rod",
+ "Bundle: Uranium Waste",
+ "Bundle: Plutonium Fuel Rod",
+ "Bundle: Plutonium Pellet",
+ "Bundle: Plutonium Waste",
+ "Bundle: Non-fissile Uranium",
+ "Bundle: Ficsonium",
+ "Bundle: Ficsonium Fuel Rod"
+ }
+
+
+class TrapSelectionPreset(ChoiceMap):
+ """
+ Themed presets of trap types to enable.
+
+ If you want more control, use *Trap Override* or visit the Weighted Options page.
+ """
+ display_name = "Trap Presets"
+ choices = {
+ "Gentle": ["Trap: Doggo with Pulse Nobelisk", "Trap: Hog", "Trap: Spitter", "Trap: Can of Beans"],
+ "Normal": ["Trap: Doggo with Pulse Nobelisk", "Trap: Doggo with Gas Nobelisk", "Trap: Hog", "Trap: Alpha Hog", "Trap: Hatcher", "Trap: Elite Hatcher", "Trap: Small Stinger", "Trap: Stinger", "Trap: Spitter", "Trap: Alpha Spitter", "Trap: Not the Bees", "Trap: Nuclear Waste Drop", "Bundle: Uranium", "Bundle: Non-fissile Uranium", "Trap: Can of Beans", "Trap: Fart Cloud"],
+ "Harder": ["Trap: Doggo with Pulse Nobelisk", "Trap: Doggo with Nuke Nobelisk", "Trap: Doggo with Gas Nobelisk", "Trap: Alpha Hog", "Trap: Cliff Hog", "Trap: Spore Flower", "Trap: Hatcher", "Trap: Elite Hatcher", "Trap: Stinger", "Trap: Alpha Spitter", "Trap: Not the Bees", "Trap: Fart Cloud", "Trap: Nuclear Waste Drop", "Trap: Plutonium Waste Drop", "Bundle: Uranium", "Bundle: Uranium Fuel Rod", "Bundle: Uranium Waste", "Bundle: Plutonium Fuel Rod", "Bundle: Plutonium Pellet", "Bundle: Plutonium Waste", "Bundle: Non-fissile Uranium"],
+ "All": list(_trap_types),
+ "Ruthless": ["Trap: Doggo with Nuke Nobelisk", "Trap: Nuclear Hog", "Trap: Cliff Hog", "Trap: Elite Hatcher", "Trap: Spore Flower", "Trap: Gas Stinger", "Trap: Nuclear Waste Drop", "Trap: Plutonium Waste Drop", "Bundle: Uranium Fuel Rod", "Bundle: Uranium Waste", "Bundle: Plutonium Fuel Rod", "Bundle: Plutonium Pellet", "Bundle: Plutonium Waste", "Bundle: Non-fissile Uranium", "Bundle: Ficsonium", "Bundle: Ficsonium Fuel Rod"],
+ "All Arachnids All the Time": ["Trap: Small Stinger", "Trap: Stinger", "Trap: Gas Stinger"],
+ "Whole Hog": ["Trap: Hog", "Trap: Alpha Hog", "Trap: Cliff Hog", "Trap: Nuclear Hog", "Trap: Johnny"],
+ "Nicholas Cage": ["Trap: Hatcher", "Trap: Elite Hatcher", "Trap: Not the Bees"],
+ "Fallout": ["Trap: Doggo with Nuke Nobelisk", "Trap: Nuclear Hog", "Trap: Nuclear Waste Drop", "Trap: Plutonium Waste Drop", "Bundle: Uranium", "Bundle: Uranium Fuel Rod", "Bundle: Uranium Waste", "Bundle: Plutonium Fuel Rod", "Bundle: Plutonium Waste", "Bundle: Ficsonium", "Bundle: Ficsonium Fuel Rod"],
+ }
+ default = "Normal"
+
+
+class TrapSelectionOverride(OptionSet):
+ """
+ Precise list of traps that may be in the item pool to find.
+ If you select anything with this option it will be used instead of the *Trap Presets* setting.
+ """
+ display_name = "Trap Override"
+ valid_keys = _trap_types
+
+
+class EnergyLink(DefaultOnToggle):
+ """
+ Allow transferring energy to and from other worlds using the Power Storage building.
+ No energy is lost in the transfer on Satisfactory's side, but other worlds may have other settings.
+ """
+ display_name = "EnergyLink"
+
+
+class MamLogic(PlacementLogic):
+ """
+ Where to place the MAM building in logic.
+ Earlier means it will be more likely that you will need to interact with it for progression purposes.
+ """
+ display_name = "MAM Placement"
+ default = Placement.early.value
+
+
+class AwesomeLogic(PlacementLogic):
+ """
+ Where to place the AWESOME Shop and Sink buildings in logic.
+ Earlier means it will be more likely that you will need to interact with it for progression purposes.
+ """
+ display_name = "AWESOME Stuff Placement"
+ default = Placement.early.value
+
+
+class EnergyLinkLogic(PlacementLogic):
+ """
+ Where to place the EnergyLink building (or Power Storage if EnergyLink is disabled) in logic.
+ Earlier means it will be more likely that you will need to interact with it for progression purposes.
+ """
+ display_name = "EnergyLink Placement"
+ default = Placement.early.value
+
+
+class SplitterLogic(PlacementLogic):
+ """
+ Where to place the Conveyor Splitter and Merger buildings in logic.
+ Earlier means it will be more likely that you will need to interact with it for progression purposes.
+ """
+ display_name = "Splitter and Merger Placement"
+ default = Placement.starting_inventory.value
+
+
+_skip_tutorial_starting_items = [
+ # https://satisfactory.wiki.gg/wiki/Onboarding
+ "Single: Portable Miner",
+ "Single: Portable Miner",
+ "Single: Portable Miner",
+ "Single: Portable Miner",
+ "Bundle: Iron Plate",
+ "Bundle: Concrete",
+ "Bundle: Iron Rod",
+ "Bundle: Wire",
+ "Single: Reinforced Iron Plate",
+ "Single: Reinforced Iron Plate",
+ "Single: Reinforced Iron Plate",
+ "Single: Reinforced Iron Plate",
+ "Single: Reinforced Iron Plate",
+ "Single: Reinforced Iron Plate",
+ "Single: Reinforced Iron Plate",
+ "Single: Reinforced Iron Plate",
+ "Single: Reinforced Iron Plate",
+ "Single: Reinforced Iron Plate",
+ "Bundle: Cable",
+ "Bundle: Iron Ore"
+]
+
+_default_starting_items = _skip_tutorial_starting_items + [
+ "Bundle: Iron Ingot",
+ "Bundle: Copper Ingot",
+ "Bundle: Concrete",
+ "Bundle: Solid Biofuel", # user's choice if they want to hold onto it for chainsaw or burn it right away
+ "Building: Blueprint Designer",
+ "Expanded Toolbelt",
+ "Inflated Pocket Dimension",
+ "Building: Personal Storage Box"
+]
+
+_default_plus_foundations_starting_items = _default_starting_items + [
+ "Building: Foundation",
+ "Building: Half Foundation"
+]
+
+_explorer_starting_items = _default_plus_foundations_starting_items + [
+ "Single: Parachute",
+ "Single: Blade Runners",
+ "Single: Object Scanner",
+ "Single: Boom Box",
+ "Expanded Toolbelt",
+ "Inflated Pocket Dimension"
+]
+
+_foundation_lover_starting_items = _default_plus_foundations_starting_items + [
+ "Bundle: Iron Plate", "Bundle: Iron Plate", "Bundle: Iron Plate",
+ "Bundle: Concrete", "Bundle: Concrete", "Bundle: Concrete"
+]
+
+
+class StartingInventoryPreset(ChoiceMap):
+ """
+ What resources (and buildings) the player should start with in their inventory.
+ If you want more control, visit the Weighted Options page or edit the YAML directly.
+
+ - **Barebones**: Nothing but the default xeno zapper and buildings.
+ - **Skip Tutorial Inspired**: Inspired by the items you would have if you skipped the base game's tutorial.
+ - **Archipelago**: The starting items we think will lead to a fun experience.
+ - **Foundations**: 'Archipelago' option, but also guaranteeing that you have foundations unlocked at the start.
+ - **Foundation Lover**: You really like foundations.
+ - **Explorer**: 'Foundations' option plus one set of early exploration equipment (Parachute, Blade Runners, Object Scanner, Boom Box).
+ """
+ display_name = "Starting Goodies Presets"
+ choices = {
+ "Barebones": [], # Nothing but the xeno zapper
+ "Skip Tutorial Inspired": _skip_tutorial_starting_items,
+ "Archipelago": _default_starting_items,
+ "Foundations": _default_plus_foundations_starting_items,
+ "Foundation Lover": _foundation_lover_starting_items,
+ "Explorer": _explorer_starting_items
+ }
+ default = "Archipelago"
+
+
+class ExplorationCollectableCount(Range):
+ """
+ Does nothing if *Exploration Collectables* goal is not enabled.
+
+ Collect this amount of Mercer Spheres, Somersloops, Hard Drives, Paleberries, Beryl Nuts, and Bacon Agarics each to finish.
+
+ - The amount of **Mercer Spheres** is **2x** the selected amount
+ - The amount of **Somersloops** is **the** selected amount
+ - The amount of **Hard Drives** is **1/5th** the selected amount
+ - The amount of **Paleberries** is **10x** the selected amount
+ - The amount of **Beryl Nuts** is **20x** the selected amount
+ - The amount of **Bacon Agarics** is **the** selected amount
+ """
+ display_name = "Exploration Collectables"
+ default = 20
+ range_start = 5
+ range_end = 100
+
+
+class MilestoneCostMultiplier(Range):
+ """
+ Multiplies the amount of resources needed to unlock a Milestone by this factor.
+
+ The value is a percentage:
+
+ - **50** = half cost
+ - **100** = normal milestone cost
+ - **200** = double the cost
+ """
+ display_name = "Milestone cost multiplier %"
+ default = 100
+ range_start = 1
+ range_end = 500
+
+
+class GoalSelection(OptionSet):
+ """
+ What will be your goal(s)?
+ Configure them further with other options.
+
+ Possible values are:
+ - **Space Elevator Tier**
+ - **AWESOME Sink Points (total)**
+ - **AWESOME Sink Points (per minute)**
+ - **Exploration Collectables**
+ """
+ display_name = "Select your Goals"
+ valid_keys = {
+ "Space Elevator Phase",
+ "AWESOME Sink Points (total)",
+ "AWESOME Sink Points (per minute)",
+ "Exploration Collectables",
+ # "Erect a FICSMAS Tree",
+ }
+ default = {"Space Elevator Phase"}
+ schema = Schema(And(set, len),
+ error="yaml does not specify a goal, the Satisfactory option `goal_selection` is empty")
+
+
+class GoalRequirement(Choice):
+ """
+ Of the goals selected in *Select your Goals*, how many must be reached to complete your slot?
+ """
+ display_name = "Goal Requirements"
+ option_require_any_one_goal = 0
+ option_require_all_goals = 1
+ default = 0
+
+
+class RandomizeTier0(DefaultOnToggle):
+ """
+ Randomizes what recipes you use to craft the default unlocked parts:
+ Iron Ingot, Iron Plate, Iron Rod, Copper Ingot, Wire, Concrete, Screw, Reinforced Iron Plate
+
+ * Could require usage of Foundries or Assemblers (which get unlocked by default if needed, at reduced build costs)
+ * Could require other ores to be mixed in via alt recipes (which will become hand-craftable if needed)
+ """
+ display_name = "Randomize Default Part Recipes"
+
+
+@dataclass
+class SatisfactoryOptions(PerGameCommonOptions, DeathLinkMixin):
+ goal_selection: GoalSelection
+ goal_requirement: GoalRequirement
+ final_elevator_phase: ElevatorPhase
+ goal_awesome_sink_points_total: ResourceSinkPointsTotal
+ goal_awesome_sink_points_per_minute: ResourceSinkPointsPerMinute
+ goal_exploration_collectables_amount: ExplorationCollectableCount
+ hard_drive_progression_limit: HardDriveProgressionLimit
+ free_sample_equipment: FreeSampleEquipment
+ free_sample_buildings: FreeSampleBuildings
+ free_sample_parts: FreeSampleParts
+ free_sample_radioactive: FreeSampleRadioactive
+ starting_inventory_preset: StartingInventoryPreset
+ mam_logic_placement: MamLogic
+ awesome_logic_placement: AwesomeLogic
+ energy_link_logic_placement: EnergyLinkLogic
+ splitter_placement: SplitterLogic
+ milestone_cost_multiplier: MilestoneCostMultiplier
+ trap_chance: TrapChance
+ trap_selection_preset: TrapSelectionPreset
+ trap_selection_override: TrapSelectionOverride
+ energy_link: EnergyLink
+ start_inventory_from_pool: StartInventoryPool
+ randomize_starter_recipes: RandomizeTier0
+
+
+option_groups = [
+ OptionGroup("Game Scope", [
+ ElevatorPhase,
+ HardDriveProgressionLimit
+ ]),
+ OptionGroup("Goal Selection", [
+ GoalSelection,
+ GoalRequirement,
+ ResourceSinkPointsTotal,
+ ResourceSinkPointsPerMinute,
+ ExplorationCollectableCount
+ ]),
+ OptionGroup("Placement logic", [
+ StartingInventoryPreset,
+ RandomizeTier0,
+ MamLogic,
+ AwesomeLogic,
+ SplitterLogic,
+ EnergyLinkLogic
+ ], start_collapsed=True),
+ OptionGroup("Free Samples", [
+ FreeSampleEquipment,
+ FreeSampleBuildings,
+ FreeSampleParts,
+ FreeSampleRadioactive
+ ], start_collapsed=True),
+ OptionGroup("Traps", [
+ TrapChance,
+ TrapSelectionPreset,
+ TrapSelectionOverride
+ ], start_collapsed=True)
+]
+
+option_presets: dict[str, dict[str, Any]] = {
+ "Short": {
+ "final_elevator_phase": 1,
+ "goal_selection": {"Space Elevator Phase", "AWESOME Sink Points (total)"},
+ "goal_requirement": GoalRequirement.option_require_any_one_goal,
+ "goal_awesome_sink_points_total": 17804500, # 100 coupons
+ "hard_drive_progression_limit": 20,
+ "starting_inventory_preset": 3, # "Foundations"
+ "randomize_starter_recipes": False,
+ "mam_logic_placement": Placement.starting_inventory.value,
+ "awesome_logic_placement": Placement.starting_inventory.value,
+ "energy_link_logic_placement": Placement.starting_inventory.value,
+ "splitter_placement": Placement.starting_inventory.value,
+ "milestone_cost_multiplier": 50,
+ "trap_selection_preset": 1 # Gentle
+ },
+ "Long": {
+ "final_elevator_phase": 3,
+ "goal_selection": {"Space Elevator Phase", "AWESOME Sink Points (per minute)"},
+ "goal_requirement": GoalRequirement.option_require_all_goals,
+ "goal_awesome_sink_points_per_minute": 100000, # ~10 heavy modular frame/min
+ "hard_drive_progression_limit": 60,
+ "mam_logic_placement": Placement.somewhere.value,
+ "awesome_logic_placement": Placement.somewhere.value,
+ "energy_link_logic_placement": Placement.somewhere.value,
+ "splitter_placement": Placement.somewhere.value,
+ "trap_selection_preset": 3 # Harder
+ },
+ "Extra Long": {
+ "final_elevator_phase": 5,
+ "goal_selection": {"Space Elevator Phase", "AWESOME Sink Points (per minute)"},
+ "goal_requirement": GoalRequirement.option_require_all_goals,
+ "goal_awesome_sink_points_per_minute": 625000, # ~10 fused modular frame/min
+ "hard_drive_progression_limit": 100,
+ "mam_logic_placement": Placement.somewhere.value,
+ "awesome_logic_placement": Placement.somewhere.value,
+ "energy_link_logic_placement": Placement.somewhere.value,
+ "splitter_placement": Placement.somewhere.value,
+ "milestone_cost_multiplier": 300,
+ "trap_selection_preset": 4 # All
+ }
+}
diff --git a/worlds/satisfactory/Regions.py b/worlds/satisfactory/Regions.py
new file mode 100644
index 000000000000..4ab4ea327d37
--- /dev/null
+++ b/worlds/satisfactory/Regions.py
@@ -0,0 +1,199 @@
+from typing import Optional
+from collections.abc import Callable
+from BaseClasses import MultiWorld, Region, Location, Item, CollectionState
+from .Locations import LocationData
+from .GameLogic import GameLogic, PowerInfrastructureLevel
+from .StateLogic import StateLogic
+from .Options import SatisfactoryOptions, Placement
+from .CriticalPathCalculator import CriticalPathCalculator
+
+
+class SatisfactoryLocation(Location):
+ game: str = "Satisfactory"
+ event_name: Optional[str]
+
+ def __init__(self, player: int, data: LocationData, region: Region):
+ super().__init__(player, data.name, data.code, region)
+
+ self.event_name = data.event_name
+
+ if data.code is None:
+ self.event = True
+ self.locked = True
+
+ if data.rule:
+ self.access_rule = data.rule
+
+ if data.non_progression:
+ self.item_rule = self.non_progression_only
+
+ @staticmethod
+ def non_progression_only(item: Item) -> bool:
+ return not item.advancement
+
+
+def create_regions_and_return_locations(multiworld: MultiWorld, options: SatisfactoryOptions, player: int,
+ game_logic: GameLogic, state_logic: StateLogic,
+ critical_path: CriticalPathCalculator, locations: list[LocationData]) -> None:
+
+ region_names: list[str] = [
+ "Overworld",
+ "Mam",
+ "AWESOME Shop"
+ ]
+
+ for hub_tier, milestones_per_hub_tier in enumerate(game_logic.hub_layout, 1):
+ if hub_tier > (options.final_elevator_phase * 2):
+ break
+
+ region_names.append(f"Hub Tier {hub_tier}")
+
+ for milestone, _ in enumerate(milestones_per_hub_tier, 1):
+ region_names.append(f"Hub {hub_tier}-{milestone}")
+
+ region_names += [
+ building_name
+ for building_name, building in game_logic.buildings.items()
+ if building.can_produce and building_name in critical_path.required_buildings
+ ]
+
+ for tree_name, tree in game_logic.man_trees.items():
+ region_names.append(tree_name)
+
+ for node in tree.nodes:
+ if node.minimal_phase <= options.final_elevator_phase:
+ region_names.append(f"{tree_name}: {node.name}")
+
+ locations_per_region: dict[str, list[LocationData]] = get_locations_per_region(locations)
+ regions: dict[str, Region] = create_regions(multiworld, player, locations_per_region, region_names)
+
+ if __debug__:
+ throw_if_any_location_is_not_assigned_to_a__region(regions, locations_per_region)
+
+ multiworld.regions += regions.values()
+
+ super_early_game_buildings: list[str] = [
+ "Foundation",
+ "Walls Orange"
+ ]
+
+ early_game_buildings: list[str] = [
+ PowerInfrastructureLevel.Automated.to_name()
+ ]
+
+ if options.mam_logic_placement.value == Placement.early:
+ early_game_buildings.append("MAM")
+ if options.awesome_logic_placement.value == Placement.early:
+ early_game_buildings.append("AWESOME Sink")
+ early_game_buildings.append("AWESOME Shop")
+ if options.energy_link_logic_placement.value == Placement.early:
+ early_game_buildings.append("Power Storage")
+ if options.splitter_placement == Placement.early:
+ super_early_game_buildings.append("Conveyor Splitter")
+ super_early_game_buildings.append("Conveyor Merger")
+
+ if options.final_elevator_phase == 1:
+ super_early_game_buildings.extend(early_game_buildings)
+
+ # Hub Tier 1 and 2 are always accessible, so universal tracker should display them out the gates
+ is_universal_tracker = getattr(multiworld, "generation_is_fake", False)
+
+ connect(regions, "Overworld", "Hub Tier 1")
+ connect(regions, "Hub Tier 1", "Hub Tier 2",
+ lambda state: is_universal_tracker or state_logic.can_build_all(state, super_early_game_buildings))
+
+ if options.final_elevator_phase >= 2:
+ connect(regions, "Hub Tier 2", "Hub Tier 3", lambda state: state.has("Elevator Phase 1", player)
+ and (is_universal_tracker or state_logic.can_build_all(state, early_game_buildings)))
+ connect(regions, "Hub Tier 3", "Hub Tier 4")
+ if options.final_elevator_phase >= 3:
+ connect(regions, "Hub Tier 4", "Hub Tier 5", lambda state: state.has("Elevator Phase 2", player))
+ connect(regions, "Hub Tier 5", "Hub Tier 6")
+ if options.final_elevator_phase >= 4:
+ connect(regions, "Hub Tier 6", "Hub Tier 7", lambda state: state.has("Elevator Phase 3", player))
+ connect(regions, "Hub Tier 7", "Hub Tier 8")
+ if options.final_elevator_phase >= 5:
+ connect(regions, "Hub Tier 8", "Hub Tier 9", lambda state: state.has("Elevator Phase 4", player))
+
+ connect(regions, "Overworld", "Mam", lambda state: state_logic.can_build(state, "MAM"))
+ connect(regions, "Overworld", "AWESOME Shop",
+ lambda state: state_logic.can_build_all(state, ("AWESOME Shop", "AWESOME Sink")))
+
+ for hub_tier, milestones_per_hub_tier in enumerate(game_logic.hub_layout, 1):
+ if hub_tier > (options.final_elevator_phase * 2):
+ break
+
+ for milestone, parts_per_milestone in enumerate(milestones_per_hub_tier, 1):
+ connect(regions, f"Hub Tier {hub_tier}", f"Hub {hub_tier}-{milestone}",
+ state_logic.get_can_produce_all_allowing_handcrafting_rule(parts_per_milestone))
+
+ for building_name, building in game_logic.buildings.items():
+ if building.can_produce and building_name in critical_path.required_buildings:
+ connect(regions, "Overworld", building_name,
+ lambda state, name=building_name: state_logic.can_build(state, name))
+
+ for tree_name, tree in game_logic.man_trees.items():
+ connect(regions, "Mam", tree_name)
+
+ for node in tree.nodes:
+ if node.minimal_phase > options.final_elevator_phase:
+ continue
+
+ if not node.depends_on:
+ connect(regions, tree_name, f"{tree_name}: {node.name}",
+ lambda state, parts=node.unlock_cost: state_logic.can_produce_all(state, parts))
+ else:
+ for parent in node.depends_on:
+ if f"{tree_name}: {parent}" in region_names:
+ connect(regions, f"{tree_name}: {parent}", f"{tree_name}: {node.name}",
+ lambda state, parts=node.unlock_cost: state_logic.can_produce_all(state, parts))
+
+
+def throw_if_any_location_is_not_assigned_to_a__region(regions: dict[str, Region],
+ region_names: dict[str, list[LocationData]]) -> None:
+ existing_regions = set(regions)
+ existing_region_names = set(region_names)
+
+ if existing_region_names - existing_regions:
+ raise Exception(f"Satisfactory: the following regions are used in locations: "
+ f"{existing_region_names - existing_regions}, but no such region exists")
+
+
+def create_region(multiworld: MultiWorld, player: int,
+ locations_per_region: dict[str, list[LocationData]], name: str) -> Region:
+
+ region = Region(name, player, multiworld)
+
+ if name in locations_per_region:
+ region.locations += [
+ SatisfactoryLocation(player, location_data, region)
+ for location_data in locations_per_region[name]
+ ]
+
+ return region
+
+
+def create_regions(multiworld: MultiWorld, player: int, locations_per_region: dict[str, list[LocationData]],
+ region_names: list[str]) -> dict[str, Region]:
+ return {
+ name: create_region(multiworld, player, locations_per_region, name)
+ for name in region_names
+ }
+
+
+def connect(regions: dict[str, Region], source: str, target: str,
+ rule: Optional[Callable[[CollectionState], bool]] = None) -> None:
+
+ source_region = regions[source]
+ target_region = regions[target]
+
+ source_region.connect(target_region, rule=rule)
+
+
+def get_locations_per_region(locations: list[LocationData]) -> dict[str, list[LocationData]]:
+ per_region: dict[str, list[LocationData]] = {}
+
+ for location in locations:
+ per_region.setdefault(location.region, []).append(location)
+
+ return per_region
diff --git a/worlds/satisfactory/StateLogic.py b/worlds/satisfactory/StateLogic.py
new file mode 100644
index 000000000000..58406d4eece3
--- /dev/null
+++ b/worlds/satisfactory/StateLogic.py
@@ -0,0 +1,171 @@
+from typing import Optional, Callable, ClassVar, Tuple
+from collections.abc import Iterable
+from BaseClasses import CollectionState
+from .GameLogic import Recipe, PowerInfrastructureLevel
+from .Options import SatisfactoryOptions
+from .CriticalPathCalculator import CriticalPathCalculator
+
+EventId: Optional[int] = None
+
+part_event_prefix = "Can Produce: "
+building_event_prefix = "Can Build: "
+
+
+def true_rule(_: CollectionState) -> bool:
+ return True
+
+
+def to_part_event(part: str) -> str:
+ return part_event_prefix + part
+
+
+def to_building_event(part: str) -> str:
+ return building_event_prefix + part
+
+
+def to_belt_name(power_level: int) -> str:
+ return "Conveyor Mk." + str(power_level)
+
+
+class StateLogic:
+ player: int
+ options: SatisfactoryOptions
+ critical_path: CriticalPathCalculator
+ initial_unlocked_items: set[str]
+
+ pipe_events: ClassVar[tuple[str, str]] = \
+ tuple(to_building_event(building) for building in ("Pipes Mk.1", "Pipes Mk.2"))
+ pump_events: ClassVar[tuple[str, str]] = \
+ tuple(to_building_event(building) for building in ("Pipeline Pump Mk.1", "Pipeline Pump Mk.2"))
+ hazmat_events: ClassVar[tuple[str, str]] = \
+ tuple(to_part_event(part) for part in ("Hazmat Suit", "Iodine-Infused Filter"))
+ belt_events: ClassVar[tuple[tuple[str, ...], ...]] = tuple(
+ tuple(map(to_building_event, map(to_belt_name, range(speed, 6))))
+ for speed in range(1, 6)
+ )
+
+ pipes_rule: Callable[[CollectionState], bool]
+ radio_active_rule: Callable[[CollectionState], bool]
+ belt_rules: Tuple[Callable[[CollectionState], bool], ...]
+
+ def __init__(self, player: int, options: SatisfactoryOptions, critical_path: CriticalPathCalculator):
+ self.player = player
+ self.options = options
+ self.critical_path = critical_path
+
+ self.pipes_rule = self.get_requires_pipes_rule()
+ self.radio_active_rule = self.get_requires_hazmat_rule()
+ self.belt_rule = tuple(self.get_belt_speed_rule(speed) for speed in range(1, 6))
+
+ def has_recipe(self, state: CollectionState, recipe: Recipe) -> bool:
+ return state.has(recipe.name, self.player) or recipe.name in self.critical_path.implicitly_unlocked
+
+ def can_build(self, state: CollectionState, building_name: Optional[str]) -> bool:
+ return building_name is None or state.has(building_event_prefix + building_name, self.player)
+
+ def can_build_any(self, state: CollectionState, building_names: Optional[Iterable[str]]) -> bool:
+ return building_names is None or \
+ state.has_any(map(to_building_event, building_names), self.player)
+
+ def can_build_all(self, state: CollectionState, building_names: Optional[Iterable[str]]) -> bool:
+ return building_names is None or \
+ state.has_all(map(to_building_event, building_names), self.player)
+
+ def can_produce(self, state: CollectionState, part_name: Optional[str]) -> bool:
+ return part_name is None or state.has(part_event_prefix + part_name, self.player)
+
+ def can_power(self, state: CollectionState, power_level: Optional[PowerInfrastructureLevel]) -> bool:
+ return power_level is None or state.has(building_event_prefix + power_level.to_name(), self.player)
+
+ def can_produce_all(self, state: CollectionState, parts: Optional[Iterable[str]]) -> bool:
+ return parts is None or \
+ state.has_all(map(to_part_event, parts), self.player)
+
+ def can_handcraft_single_part(self, state: CollectionState, part: str) -> bool:
+ if self.can_produce(state, part):
+ return True
+
+ if part not in self.critical_path.handcraftable_parts:
+ return False
+
+ recipes: list[Recipe] = self.critical_path.handcraftable_parts[part]
+ return any(
+ self.has_recipe(state, recipe)
+ and (not recipe.inputs or all(
+ self.can_handcraft_single_part(state, recipe_part)
+ for recipe_part in recipe.inputs))
+ for recipe in recipes)
+
+ def get_can_produce_all_allowing_handcrafting_rule(self, parts: Optional[Iterable[str]]) \
+ -> Callable[[CollectionState], bool]:
+ if not parts:
+ return true_rule
+
+ return lambda state: all(self.can_handcraft_single_part(state, part) for part in parts)
+
+ def get_requires_pipes_rule(self) -> Callable[[CollectionState], bool]:
+ return lambda state: \
+ state.has_any(self.pipe_events, self.player) and state.has_any(self.pump_events, self.player)
+
+ def get_requires_hazmat_rule(self) -> Callable[[CollectionState], bool]:
+ return lambda state: state.has_all(self.hazmat_events, self.player)
+
+ def get_belt_speed_rule(self, belt_speed: int) -> Callable[[CollectionState], bool]:
+ return lambda state: state.has_any(self.belt_events[belt_speed], self.player)
+
+ def is_recipe_producible(self, state: CollectionState, recipe: Recipe) -> bool:
+ return self.has_recipe(state, recipe) \
+ and self.can_build(state, recipe.building) \
+ and self.can_produce_all(state, recipe.inputs)
+
+ def get_can_produce_specific_recipe_for_part_rule(self, recipe: Recipe) -> Callable[[CollectionState], bool]:
+ if recipe.needs_pipes:
+ if recipe.is_radio_active:
+ if recipe.minimal_belt_speed:
+ return lambda state: \
+ self.is_recipe_producible(state, recipe) \
+ and self.pipes_rule(state) \
+ and self.radio_active_rule(state) \
+ and self.belt_rule[recipe.minimal_belt_speed - 1]
+ else:
+ return lambda state: \
+ self.is_recipe_producible(state, recipe) \
+ and self.pipes_rule(state) \
+ and self.radio_active_rule(state)
+ else:
+ if recipe.minimal_belt_speed:
+ return lambda state: \
+ self.is_recipe_producible(state, recipe) \
+ and self.pipes_rule(state) \
+ and self.belt_rule[recipe.minimal_belt_speed - 1]
+ else:
+ return lambda state: \
+ self.is_recipe_producible(state, recipe) \
+ and self.pipes_rule(state)
+ else:
+ if recipe.is_radio_active:
+ if recipe.minimal_belt_speed:
+ return lambda state: \
+ self.is_recipe_producible(state, recipe) \
+ and self.radio_active_rule(state) \
+ and self.belt_rule[recipe.minimal_belt_speed - 1]
+ else:
+ return lambda state: \
+ self.is_recipe_producible(state, recipe) \
+ and self.radio_active_rule(state)
+ else:
+ if recipe.minimal_belt_speed:
+ return lambda state: \
+ self.is_recipe_producible(state, recipe) \
+ and self.belt_rule[recipe.minimal_belt_speed - 1]
+ else:
+ return lambda state: \
+ self.is_recipe_producible(state, recipe)
+
+ def is_elevator_phase(self, state: CollectionState, phase: int) -> bool:
+ limited_phase = min(self.options.final_elevator_phase - 1, phase)
+
+ if limited_phase != 0:
+ return state.has(f"Elevator Phase {limited_phase}", self.player)
+ else:
+ return True
diff --git a/worlds/satisfactory/Web.py b/worlds/satisfactory/Web.py
new file mode 100644
index 000000000000..1504b9f29908
--- /dev/null
+++ b/worlds/satisfactory/Web.py
@@ -0,0 +1,20 @@
+from BaseClasses import Tutorial
+from .Options import option_groups, option_presets
+from ..AutoWorld import WebWorld
+
+
+class SatisfactoryWebWorld(WebWorld):
+ theme = "dirt"
+ setup = Tutorial(
+ "Multiworld Setup Guide",
+ "A guide to setting up the Satisfactory Archipelago mod and connecting it to an Archipelago Multiworld",
+ "English",
+ "setup_en.md",
+ "setup/en",
+ ["Robb", "Jarno"]
+ )
+ tutorials = [setup]
+ rich_text_options_doc = True
+
+ option_groups = option_groups
+ options_presets = option_presets
diff --git a/worlds/satisfactory/__init__.py b/worlds/satisfactory/__init__.py
new file mode 100644
index 000000000000..43494ec8e014
--- /dev/null
+++ b/worlds/satisfactory/__init__.py
@@ -0,0 +1,264 @@
+from typing import TextIO, ClassVar, Any
+from collections.abc import Iterable
+from BaseClasses import Item, ItemClassification, CollectionState
+from NetUtils import Hint
+from .GameLogic import GameLogic
+from .Items import Items
+from .Locations import Locations, LocationData
+from .StateLogic import EventId, StateLogic
+from .Options import SatisfactoryOptions, Placement
+from .Regions import SatisfactoryLocation, create_regions_and_return_locations
+from .CriticalPathCalculator import CriticalPathCalculator
+from .Web import SatisfactoryWebWorld
+from ..AutoWorld import World
+
+
+class SatisfactoryWorld(World):
+ """
+ Satisfactory is a first-person open-world factory building game with a dash of exploration and combat.
+ Explore an alien planet, create multi-story factories, and enter conveyor belt heaven!
+ """
+
+ game = "Satisfactory"
+ options_dataclass = SatisfactoryOptions
+ options: SatisfactoryOptions
+ topology_present = False
+ web = SatisfactoryWebWorld()
+ origin_region_name = "Overworld"
+ required_client_version = (0, 6, 0)
+ ut_can_gen_without_yaml = True
+
+ game_logic: ClassVar[GameLogic] = GameLogic()
+
+ # These are set in generate_early and thus aren't always available
+ state_logic: StateLogic | None = None
+ items: Items | None = None
+ critical_path: CriticalPathCalculator | None = None
+ critical_path_seed: float | None = None
+ #
+
+ item_name_to_id = Items.item_names_and_ids
+ location_name_to_id = Locations().get_locations_for_data_package()
+ item_name_groups = Items.get_item_names_per_category(game_logic)
+
+ def generate_early(self) -> None:
+ self.process_universal_tracker_slot_data_if_available()
+
+ if not self.critical_path_seed:
+ self.critical_path_seed = self.random.random()
+
+ if self.options.mam_logic_placement.value == Placement.starting_inventory:
+ self.push_precollected_by_name("Building: MAM")
+ if self.options.awesome_logic_placement.value == Placement.starting_inventory:
+ self.push_precollected_by_name("Building: AWESOME Sink")
+ self.push_precollected_by_name("Building: AWESOME Shop")
+ if self.options.energy_link_logic_placement.value == Placement.starting_inventory:
+ self.push_precollected_by_name("Building: Power Storage")
+ if self.options.splitter_placement == Placement.starting_inventory:
+ self.push_precollected_by_name("Building: Conveyor Splitter")
+ self.push_precollected_by_name("Building: Conveyor Merger")
+
+ if not self.options.trap_selection_override.value:
+ self.options.trap_selection_override.value = set(self.options.trap_selection_preset.get_selected_list())
+
+ self.critical_path = CriticalPathCalculator(self.game_logic, self.critical_path_seed, self.options)
+ self.critical_path.calculate()
+
+ self.state_logic = StateLogic(self.player, self.options, self.critical_path)
+ self.items = Items(self.player, self.game_logic, self.random, self.options, self.critical_path)
+
+ starting_inventory: list[str] = self.options.starting_inventory_preset.get_selected_list()
+ for item_name in starting_inventory:
+ self.push_precollected_by_name(item_name)
+
+ def create_regions(self) -> None:
+ locations: list[LocationData] = \
+ Locations(self.game_logic, self.options, self.state_logic, self.items, self.critical_path).get_locations()
+ create_regions_and_return_locations(
+ self.multiworld, self.options, self.player, self.game_logic, self.state_logic, self.critical_path,
+ locations)
+
+ def create_items(self) -> None:
+ self.setup_events()
+
+ number_of_locations: int = len(self.multiworld.get_unfilled_locations(self.player))
+ precollected_items: list[Item] = self.multiworld.precollected_items[self.player]
+
+ self.multiworld.itempool += \
+ self.items.build_item_pool(self.random, precollected_items, number_of_locations)
+
+ def set_rules(self) -> None:
+ resource_sink_goal: bool = "AWESOME Sink Points (total)" in self.options.goal_selection \
+ or "AWESOME Sink Points (per minute)" in self.options.goal_selection
+
+ required_parts = set(self.game_logic.space_elevator_phases[self.options.final_elevator_phase.value - 1].keys())
+
+ if resource_sink_goal:
+ required_parts.union(self.game_logic.buildings["AWESOME Sink"].inputs)
+
+ self.multiworld.completion_condition[self.player] = \
+ lambda state: self.state_logic.can_produce_all(state, required_parts)
+
+ def collect(self, state: CollectionState, item: Item) -> bool:
+ change = super().collect(state, item)
+ if change and item.name in self.game_logic.indirect_recipes:
+ state.prog_items[self.player][self.game_logic.indirect_recipes[item.name]] += 1
+ return change
+
+ def remove(self, state: CollectionState, item: Item) -> bool:
+ change = super().remove(state, item)
+ if change and item.name in self.game_logic.indirect_recipes:
+ state.prog_items[self.player][self.game_logic.indirect_recipes[item.name]] -= 1
+ return change
+
+ def fill_slot_data(self) -> dict[str, object]:
+ slot_hub_layout: list[list[dict[str, int]]] = []
+
+ for tier, milestones in enumerate(self.game_logic.hub_layout, 1):
+ slot_hub_layout.append([])
+ for milestone, parts in enumerate(milestones, 1):
+ slot_hub_layout[tier - 1].append({})
+ for part, amount in parts.items():
+ multiplied_amount = int(max(amount * (self.options.milestone_cost_multiplier / 100), 1))
+ slot_hub_layout[tier - 1][milestone - 1][self.item_id_str(part)] = multiplied_amount
+
+ starting_recipes: tuple[int, ...] = tuple(
+ self.item_name_to_id[recipe_name]
+ for recipe_name in self.critical_path.tier_0_recipes
+ )
+
+ return {
+ "Data": {
+ "HubLayout": slot_hub_layout,
+ "ExplorationCosts": {
+ self.item_id_str("Mercer Sphere"): int(self.options.goal_exploration_collectables_amount.value * 2),
+ self.item_id_str("Somersloop"): self.options.goal_exploration_collectables_amount.value,
+ self.item_id_str("Hard Drive"): int(self.options.goal_exploration_collectables_amount.value / 5),
+ self.item_id_str("Paleberry"): self.options.goal_exploration_collectables_amount.value * 10,
+ self.item_id_str("Beryl Nut"): self.options.goal_exploration_collectables_amount.value * 20,
+ self.item_id_str("Bacon Agaric"): self.options.goal_exploration_collectables_amount.value,
+ },
+ "Options": {
+ "GoalSelection": self.options.goal_selection.value,
+ "GoalRequirement": self.options.goal_requirement.value,
+ "FinalElevatorPhase": self.options.final_elevator_phase.value,
+ "FinalResourceSinkPointsTotal": self.options.goal_awesome_sink_points_total.value,
+ "FinalResourceSinkPointsPerMinute": self.options.goal_awesome_sink_points_per_minute.value,
+ "FreeSampleEquipment": self.options.free_sample_equipment.value,
+ "FreeSampleBuildings": self.options.free_sample_buildings.value,
+ "FreeSampleParts": self.options.free_sample_parts.value,
+ "FreeSampleRadioactive": bool(self.options.free_sample_radioactive),
+ "EnergyLink": bool(self.options.energy_link),
+ "StartingRecipies": starting_recipes
+ },
+ "SlotDataVersion": 1,
+ "UT": {
+ "Seed": self.critical_path_seed,
+ "RandomizeTier0": bool(self.options.randomize_starter_recipes)
+ }
+ },
+ "DeathLink": bool(self.options.death_link)
+ }
+
+ @staticmethod
+ def interpret_slot_data(slot_data: dict[str, Any] | None) -> dict[str, Any] | None:
+ """Used by Universal Tracker, return value is passed to self.multiworld.re_gen_passthrough["Satisfactory"]"""
+ return slot_data
+
+ def process_universal_tracker_slot_data_if_available(self) -> None:
+ """Used by Universal Tracker to correctly rebuild state"""
+
+ slot_data: dict[str, Any] | None = None
+ if (hasattr(self.multiworld, "re_gen_passthrough")
+ and isinstance(self.multiworld.re_gen_passthrough, dict)
+ and "Satisfactory" in self.multiworld.re_gen_passthrough):
+ slot_data = self.multiworld.re_gen_passthrough["Satisfactory"]
+
+ if not slot_data:
+ return
+
+ if slot_data["Data"]["SlotDataVersion"] != 1:
+ raise Exception("The slot_data version mismatch, the UT's Satisfactory .apworld is different from the one "
+ "used during generation")
+
+ self.options.goal_selection.value = slot_data["Data"]["Options"]["GoalSelection"]
+ self.options.goal_requirement.value = slot_data["Data"]["Options"]["GoalRequirement"]
+ # TODO rename slot data FinalElevatorTier to FinalElevatorPhase in the mod, then here
+ self.options.final_elevator_phase.value = slot_data["Data"]["Options"]["FinalElevatorTier"]
+ self.options.goal_awesome_sink_points_total.value = slot_data["Data"]["Options"]["FinalResourceSinkPointsTotal"]
+ self.options.goal_awesome_sink_points_per_minute.value = \
+ slot_data["Data"]["Options"]["FinalResourceSinkPointsPerMinute"]
+ self.options.free_sample_equipment.value = slot_data["Data"]["Options"]["FreeSampleEquipment"]
+ self.options.free_sample_buildings.value = slot_data["Data"]["Options"]["FreeSampleBuildings"]
+ self.options.free_sample_parts.value = slot_data["Data"]["Options"]["FreeSampleParts"]
+ self.options.free_sample_radioactive.value = int(slot_data["Data"]["Options"]["FreeSampleRadioactive"])
+ self.options.energy_link.value = int(slot_data["Data"]["Options"]["EnergyLink"])
+
+ self.options.milestone_cost_multiplier.value = 100 * \
+ (slot_data["Data"]["HubLayout"][0][0][self.item_id_str("Concrete")]
+ / self.game_logic.hub_layout[0][0]["Concrete"])
+ self.options.goal_exploration_collectables_amount.value = \
+ slot_data["Data"]["ExplorationCosts"][self.item_id_str("Somersloop")]
+
+ self.critical_path_seed = slot_data["Data"]["UT"]["Seed"]
+ self.options.randomize_starter_recipes.value = slot_data["Data"]["UT"]["RandomizeTier0"]
+
+ def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
+ if self.options.randomize_starter_recipes:
+ spoiler_handle.write(f'Starter Recipes: {sorted(self.critical_path.tier_0_recipes)}\n')
+
+ def setup_events(self) -> None:
+ location: SatisfactoryLocation
+ for location in self.get_locations():
+ if location.address == EventId:
+ item_name = location.event_name
+
+ item = Item(item_name, ItemClassification.progression, EventId, self.player)
+
+ location.place_locked_item(item)
+ location.show_in_spoiler = False
+
+ def get_filler_item_name(self) -> str:
+ if self.items:
+ return self.items.get_filler_item_name(self.random, None)
+ else:
+ return Items.get_filler_item_name_uninitialized(self.random)
+
+ def create_item(self, name: str) -> Item:
+ if self.items:
+ return self.items.create_item(name, self.player)
+ else:
+ return Items.create_item_uninitialized(name, self.player)
+
+ def extend_hint_information(self, _: dict[int, dict[int, str]]):
+ """
+ Normally used for adding entrance information,
+ but in this case we want to create hints for locations that hold usefull items.
+ Since we only know item placements after generation is completed it was either this
+ or fill_slot_data or modify_multidata, and this method seemed the best fit
+ """
+
+ locations_visible_from_start: set[int] = set(range(1338000, 1338099)) # ids of Hub 1-1,1 to 2-5,10
+
+ if "Building: AWESOME Shop" in self.options.start_inventory \
+ or "Building: AWESOME Shop" in self.options.start_inventory_from_pool \
+ or self.options.awesome_logic_placement.value == Placement.starting_inventory:
+ locations_visible_from_start.update(range(1338700, 1338709)) # ids of shop locations 1 to 10
+
+ location_names_with_useful_items: Iterable[str] = [
+ location.name
+ for location in self.get_locations()
+ if location.address in locations_visible_from_start and location.item \
+ and location.item.flags & (ItemClassification.progression | ItemClassification.useful) > 0
+ ]
+
+ self.options.start_location_hints.value.update(location_names_with_useful_items)
+
+ def push_precollected_by_name(self, item_name: str) -> None:
+ item = self.create_item(item_name)
+ self.push_precollected(item)
+
+ def item_id_str(self, item_name: str) -> str:
+ # ItemIDs of bundles are shared with their component item
+ bundled_name = f"Bundle: {item_name}"
+ return str(self.item_name_to_id[bundled_name])
diff --git a/worlds/satisfactory/archipelago.json b/worlds/satisfactory/archipelago.json
new file mode 100644
index 000000000000..8788f365833d
--- /dev/null
+++ b/worlds/satisfactory/archipelago.json
@@ -0,0 +1,9 @@
+{
+ "game": "Satisfactory",
+ "authors": [
+ "Jarno",
+ "Robb"
+ ],
+ "minimum_ap_version": "0.6.3",
+ "world_version": "2.2.0"
+}
diff --git a/worlds/satisfactory/docs/en_Satisfactory.md b/worlds/satisfactory/docs/en_Satisfactory.md
new file mode 100644
index 000000000000..299bdacd58d0
--- /dev/null
+++ b/worlds/satisfactory/docs/en_Satisfactory.md
@@ -0,0 +1,208 @@
+# Satisfactory
+
+
+
+## Where is the settings page?
+
+The [player settings page for this game](../player-options)
+contains all the options you need to configure and export a config file.
+
+## What does randomization do to this game?
+
+In Satisfactory, the HUB Milestones and MAM Research Nodes are shuffled,
+causing technologies to be obtained in a non-standard order.
+Hard Drive scanning results also contain Archipelago items,
+meaning alternate recipes could now become part of your required progression path.
+There are also a few new purchases in the AWESOME Shop.
+The materials required for constructing Assemblers and Foundries is altered to increase early game recipe variety.
+
+## What is the goal of Satisfactory?
+
+The player can choose from a number of goals using their YAML settings:
+
+- Complete the selected number of **[Space Elevator](https://satisfactory.wiki.gg/wiki/Space_Elevator) Phases**.
+ - The goal completes upon submitting your selected Space Elevator Phase. Any other progression you may have access to (HUB, MAM, AWESOME Shop) is not required for goal completion.
+ - Selecting Phase 5 is equivalent to beating the vanilla game by launching Project Assembly.
+ - Expect Phase 1 to take ~3 hours to finish, Phase 2 to take ~8 hours, Phase 3 to take ~2 days, Phase 4 to take ~1 week, and Phase 5 to take ~1.5 weeks on default settings.
+- Supply items to the [AWESOME Sink](https://satisfactory.wiki.gg/wiki/AWESOME_Sink) **totalling a configurable amount of points** to finish.
+ - The goal is tracked in the background and completes once the points total is reached.
+ - Your selected point total can be reviewed in the AWESOME Sink graph.
+ - Time to finish this goal varies significantly depending on your goal level and Free Sample settings, and can technically be reached by AFKing at any point after you unlock the Sink.
+- Supply items to the [AWESOME Sink](https://satisfactory.wiki.gg/wiki/AWESOME_Sink) **maintaining a configurable level of points per minute** to finish.
+ - The goal is tracked in the background and completes once you have maintained the selected sink points rate for 10 minutes.
+ - This goal requires establishing a more robust factory since it can't be AFKed like the points total or elevator goals.
+ - Your selected points rate can be reviewed in the AWESOME Sink graph.
+ - Time to finish this goal varies significantly depending on your Space Elevator packages in logic and the resource sink point improvement ratios of the recipes you have access to.
+- **Explore the world to gather exotic items** and submit them in the HUB.
+ - The goal completes upon submitting the HUB milestone.
+ - There is no partial progress system for this goal - combining it with another goal is recommended.
+ - Time to finish this goal varies significantly depending on your map knowledge, equipment, and movement skills.
+
+You can also configure whether completing your slot requires *any one* goal or *all* goals to be met.
+
+## What Satisfactory items can appear in other players' worlds?
+
+Satisfactory's technologies are removed from the HUB, MAM, and Hard Drives and placed into other players' worlds.
+When those technologies are found, they are sent back to Satisfactory
+along with, optionally, free samples of those technologies.
+
+Other players' worlds may have Resource Bundles of building materials, equipment, ammunition, or FICSIT Coupons.
+They may also contain Traps.
+
+## What is a Free Sample?
+
+A free sample is a package of items in Satisfactory granted in addition to a technology received from another world.
+For equipment and component crafting recipes, this is the output product.
+For buildings, this is the ingredients for the building.
+For example, receiving the [Nobelisk Detonator MAM Node](https://satisfactory.wiki.gg/wiki/Nobelisk_Detonator#Unlocking)
+would give you one Nobelisk Detonator and 50 Nobelisk,
+receiving the [Jump Pads Milestone](https://satisfactory.wiki.gg/wiki/Milestones#Tier_2)
+would give you the ingredients to construct 5 Jump Pads and 5 U-Jelly Landing Pads, etc.
+In Satisfactory multiplayer, each Satisfactory player gets a copy of the sample.
+Certain recipes and items, like Somersloops, are always excluded from samples.
+
+You can separately configure how many samples to receive for buildings, equipment, and crafting components
+in your player settings.
+
+## What is a Resource Bundle?
+
+A Resource Bundle is a package of items received as a check from another world.
+All resource bundle type items are named either `Single: ` or `Bundle: ` to distinguish them from component recipes.
+They must be collected by constructing an Archipelago Portal.
+For example, `Single: Jetpack` would contain a single jetpack, and `Bundle: Biomass` would contain one stack of biomass.
+
+Any Resource Bundle type items added to your starting inventory will be delivered to your player inventory when you initally spawn,
+unless they can't fit, in which case they can be collected by building an Archipelago Portal.
+
+## What is a Trap?
+
+Traps are items intended to disrupt the player that replace non-progression filler items.
+Satisfactory's traps currently include spawning disruptive creatures or sending inconvenient items to your Archipelago Portal.
+The player settings page gives full control over which traps are enabled,
+how many traps replace filler items,
+as well as some pre-selected groups of themed traps.
+
+A complete list of traps and their effects is intentionally omitted to keep some surprise and mystery.
+In the current implementation, the most severe traps could temporarily lock you out of a small area until you have gas/radiation protection.
+
+## What does another world's item look like in Satisfactory?
+
+In Satisfactory, items which need to be sent to other worlds appear in the HUB and MAM as info cards
+in a similar manner to the base game's building and recipe unlocks.
+Info cards have the Archipelago icon
+and are color coded to indicate what Archipelago progression type they are.
+
+Hover over them to read a description, since many Satisfactory UIs (such as the MAM) cut this information off.
+
+
+
+Upon successful unlock of the technology, the item will be sent to its home world.
+
+## When the pioneer receives an item, what happens?
+
+When the player receives a technology, it is instantly unlocked and able to be crafted or constructed.
+A message will appear in the chat to notify the player,
+and if free samples are enabled the player may also receive some items delivered directly to their inventory.
+Bundles will instantly be added to the Archipelago Portal network and can be collected at any Archipelago Portal.
+
+## What is EnergyLink?
+
+EnergyLink is an energy storage supported by certain games that is shared across all worlds in a multiworld.
+In Satisfactory, if enabled in the player settings, all base-game Power Storage buildings will act as Energy Link interfaces.
+They will deposit surplus produced energy and draw energy from the shared storage when needed.
+
+Just like the base game, there is no limit to the discharge/draw rate of one building,
+and each Power Storage provides 100 MW of charging throughput.
+The shared storage has unlimited capacity, and only a small amount of energy is lost during depositing.
+The amount of energy currently in the shared storage is displayed in the Archipelago client
+and appears in the Power Storage building UI.
+
+You can find a list of Energy Link compatible games on the
+[Archipelago Discord](https://discord.com/channels/731205301247803413/1010929117748809758/1174728119568048130).
+
+## What is the Archipelago Portal?
+
+The Archipelago Portal is a building that serves multiple purposes:
+
+- Collecting received "Resource Bundle"-type items.
+- Transfering items within your Satisfactory world to other Portals
+- Transfering items between multiple Satisfactory worlds
+- Gifting items to other games that support the **Archipelago Gifting** system.
+
+The building requires power to operate.
+You can build multiple portals or use faster belts to increase their bandwith.
+However, they currently have no filtering capabilities,
+so you must deal with this problem when handling their output items.
+
+You can find a list of Gifting compatible games on the
+[Archipelago Discord](https://discord.com/channels/731205301247803413/1134306496042258482/1247617772993908891).
+
+## How do Hard Drives work?
+
+All base game Hard Drive contents (alternate recipes) have been moved into the normal Archipelago pool.
+Instead, Hard Drives can contain Archipelago items from a dedicated "Hard Drive" pool.
+Scanning a drive presents a choice between 2 items from the pool,
+and the scan time has been reduced from 10 minutes to 3 seconds.
+
+Unlike the base game, Archipelago hard drive results have no hard progression requirements,
+other than access to the MAM itself.
+The random contents selection system prefers to pick items earlier in progression,
+but keeping unselected Hard Drives in the Hard Drive Library will force later progression items to be presented.
+
+The "Hard Drive Progression Items" option controls how many Hard Drives contain progression items,
+the rest are filler or useful.
+
+## Where do I run Archipelago commands?
+
+You can use the game's built-in chat menu.
+Check the game's keybinding options to see how to open it.
+Run the `/help` command to list all available commands.
+Note that Archipelago commands are *not* prefixed with `!` inside of Satisfactory.
+
+Note that multiple base-game bugs affect the chat menu's functionality
+and Archipelago can put a lot of info into the chat.
+You may wish to launch the Archipelago Text Client and use it to run commands instead of the game's chat.
+
+### Hints
+
+Archipelago's hint system is available within Satisfactory via the `/hint` command.
+Most multiworld item names have a prefix to distinguish recipes from bundles.
+For example, to hint for the Assembler, run `/hint Building: Assembler`.
+
+Satisfactory's hint system has special behavior for Satisfactory crafting items.
+If you hint the unprefixed name of an item with multiple recipes, the system will hint the recipe you are expected to find first in randomizer logic.
+For example, hinting `Smart Plating` will return the logically first Smart Plating recipe,
+but hinting `Recipe: Smart Plating` or `Recipe: Plastic Smart Plating` will hint that specific recipe for Smart Plating,
+which may or may not be in logic.
+
+Exact Archipelago Item names (for hints/starting inventory/etc.) can be found
+[on the mod's GitHub](https://github.com/Jarno458/Archipelago/blob/Satisfactory/worlds/satisfactory/Items.py).
+
+## Multiplayer and Dedicated Servers
+
+It is possible to host a Satisfactory Archipelago Slot using the game's built in multiplayer,
+allowing other Satisfactory players to join in constructing your factory.
+This experience is wonderful - but there are few things not yet properly working for multiplayer:
+
+- Death-links do not kill clients
+- Starting inventory for clients is missing
+
+Remember that client players must have the same mods installed as the host player to join,
+however, they do not need to configure Archipelago connection settings.
+
+Dedicated server support is only working for Windows at the moment.
+
+## Additional Mods
+
+It is possible to use other Satisfactory mods in tandem with the Archipelago Satisfactory mod.
+However, no guarantee is made that they will work correctly,
+especially if they affect game progression, recipes, or add unlocks to base-game technologies.
+
+Content added by unaffiliated mods may end up inaccessible based on your chosen slot settings,
+for example, its milestones could be in a tier that is after your goal.
+You may be able to write patches using [ContentLib](https://ficsit.app/mod/ContentLib)
+to adjust other mods to work with your slot settings,
+but doing so is out of the scope of this guide.
+
+[The Satisfactory Archipelago mod GitHub](https://github.com/Jarno458/SatisfactoryArchipelagoMod/blob/main/Docs/AdditionalMods.md)
+maintains a list of additional mods that have been tested with Archipelago to some extent.
diff --git a/worlds/satisfactory/docs/setup_en.md b/worlds/satisfactory/docs/setup_en.md
new file mode 100644
index 000000000000..4486f1bb490d
--- /dev/null
+++ b/worlds/satisfactory/docs/setup_en.md
@@ -0,0 +1,230 @@
+# Satisfactory Setup Guide
+
+
+
+## Required Software
+
+- Satisfactory, either
+ - Steam [Satisfactory (Steam)](https://store.steampowered.com/app/526870/Satisfactory/)
+ - Epic [Satisfactory (Epic)](https://www.epicgames.com/store/en-US/product/satisfactory/home)
+- Satisfactory Mod Manager, either
+ - Automatically via [smm.ficsit.app](https://smm.ficsit.app/) or
+ - Manually via [latest stable release on GitHub](https://github.com/satisfactorymodding/SatisfactoryModManager/releases/latest/)
+
+## Overview
+
+This guide walks you through installing the Satisfactory Archipelago mod via the Satisfactory Mod Manager,
+configuring an Archipelago slot for Satisfactory,
+and playing the game with a Satisfactory client.
+
+### Defining Some Terms
+
+In Archipelago, multiple Satisfactory worlds may be played simultaneously.
+Each of these worlds must be hosted by a Satisfactory Host which is connected to the Archipelago Server via the Archipelago mod.
+
+This guide uses the following terms to refer to the software:
+
+- **Archipelago Server** - The central Archipelago server, which connects all games to each other.
+- **Archipelago Client** - The desktop application used by many Archipelago games as middleware. Satisfactory does NOT require this software, unless you would like to generate a world locally.
+- **Archipelago (Satisfactory) mod** - The Satisfactory mod which implements Archipelago in-game functionality and connectivity.
+ All Satisfactory hosts and clients must have this mod installed.
+- **Satisfactory Host** - The Satisfactory instance which will be used to host the game.
+ This could be a Satisfactory Client using Singleplayer or host-and-play multiplayer, or it could be a Satisfactory dedicated server.
+ It must be supplied with the Archipelago Server connection details.
+ *Any number of Satisfactory Clients may connect to this server.*
+- **Satisfactory Client** - The Satisfactory instance (game client) with which additional players can use to connect to the same Satisfactory world.
+
+### What a Playable State Looks Like
+
+- An Archipelago Server
+- One running modded Satisfactory Host (game client or dedicated server) per Satisfactory world
+- Optionally, additional modded Satisfactory Clients for additional players
+
+### Additional Resources
+
+- Satisfactory Wiki: [Satisfactory Official Wiki](https://satisfactory.wiki.gg/wiki/)
+- Satisfactory Modding 'Frequently Asked Questions' page: [Satisfactory Modding Documentation FAQ](https://docs.ficsit.app/satisfactory-modding/latest/faq.html)
+- Satisfactory Archipelago Item names (for hints/starting inventory/etc.) can be found [on the mod's github](https://github.com/Jarno458/Archipelago/blob/Satisfactory/worlds/satisfactory/Items.py)
+
+## Preparing to Play Satisfactory Archipelago
+
+### Installing Satisfactory
+
+Purchase and install Satisfactory via one the sources linked [above](#required-software).
+Launch the game at least once to ensure that the Mod Manager can detect the game's install location.
+
+Make sure that you are running the correct branch of the game (Release or Experimental) that Archipelago supports.
+Learn how to switch branches here:
+[Satisfactory Modding Documentation FAQ: Switching Branches](https://docs.ficsit.app/satisfactory-modding/latest/faq.html#_how_do_i_get_the_experimental_or_early_access_branch_of_the_game)
+
+### Installing Satisfactory Mod Manager
+
+The Mod Manager is used to install and manage mods for Satisfactory.
+It automatically detects your game install location and automatically handles mod dependencies for you.
+
+Download the Mod Manager here:
+[Satisfactory Mod Manager automatic download via ficsit.app](https://smm.ficsit.app/)
+
+Directions for setting and using up the Mod Manager can be found here:
+[Satisfactory Modding Documentation FAQ: Installing the Mod Manager](https://docs.ficsit.app/satisfactory-modding/latest/ForUsers/SatisfactoryModManager.html)
+
+### Installing the Archipelago Mod
+
+Once the Mod Manager is installed you can install mods directly in the manager or via the Satisfactory Mod Repository website.
+
+Inside the Mod Manager, search for and install the "Archipelago Randomizer".
+Alternatively, visit the mod page: [Archipelago Randomizer mod on ficsit.app](https://ficsit.app/mod/Archipelago).
+Once on the mod page, click the "Install" link in the Latest Versions card.
+
+The Mod Manager will install all required dependency mods for you with no additional action required.
+
+As soon as you have the relevant mods installed,
+you do not need to launch the game through the Mod Manager -
+desktop shortcuts, Steam, Epic. etc. will all launch the game with mods still loaded.
+
+### Installing Additional Mods
+
+You may also wish to install some of the suggested mods mentioned on the
+[Archipelago Info page for Satisfactory](/games/Satisfactory/info/en#additional-mods).
+If you are playing multiplayer in the same Satisfactory world, all Satisfactory Clients should have the same mods installed.
+The Mod Manager's profile import/export feature makes coordinating this easy.
+
+## Connecting to Someone Else's Satisfactory Game
+
+If you are joining someone else's existing Satisfactory game,
+your setup process is almost complete.
+If your host has sent you a Mod Manager profile containing additional mods,
+be sure to install it.
+See [Satisfactory Modding Documentation: Profiles](https://docs.ficsit.app/satisfactory-modding/latest/ForUsers/SatisfactoryModManager.html#_profiles) for more information.
+
+To get started playing, connect to the Satisfactory Host using the connection details provided by your host.
+([Satisfactory Wiki: Joining a Session](https://satisfactory.wiki.gg/wiki/Multiplayer#Joining_a_session))
+
+See the [Troubleshooting section below](#troubleshooting) if you encounter any issues.
+
+## Hosting Your Own Satisfactory Game
+
+If you're hosting your own Satisfactory game,
+you will need to configure an Archipelago world and set up the Satisfactory Host you will be playing on.
+
+### Create a Config (.yaml) File
+
+#### What is a config file and why do I need one?
+
+Your config file contains a set of configuration options
+which provide the generator with information about how it should generate your game.
+Each player of a multiworld will provide their own config file.
+This setup allows each player to enjoy an experience customized for their taste,
+and different players in the same multiworld can all have different options.
+
+#### Where do I get a config file?
+
+The Player Settings page on the website
+allows you to configure your personal settings and export a config file from them.
+Satisfactory player settings page: [Satisfactory Settings Page](/games/Satisfactory/player-settings)
+
+#### Verifying Your Config File
+
+If you would like to validate your config file to make sure it works,
+you may do so on the YAML Validator page.
+YAML Validator page: [Yaml Validation Page](/mysterycheck)
+
+#### Starting Inventory
+
+The Player Settings page provides a few options for controlling what materials you start with
+and when certain key technologies are unlocked.
+Any Resource Bundle type items added to your starting inventory will be delivered to your player inventory when you initally spawn,
+unless they can't fit, in which case they can be collected by building an Archipelago Portal.
+
+Advanced users can use Plando, Weighted Options, and manual yaml editing to further configure the starting inventory.
+If you don't wish to use these techniques, consider using Satisfactory's
+[Advanced Game Settings (Satisfactory Wiki)](https://satisfactory.wiki.gg/wiki/Advanced_Game_Settings)
+to spawn the items you desire.
+
+#### Advanced Configuration
+
+Advanced users can utilize the
+[Weighted Options Page](/weighted-options)
+and [Plando](/tutorial/Archipelago/plando)
+to futher customize their experience.
+
+### Generating and Hosting the Multiworld
+
+Generating a game and hosting an Archipelago server is explained in the [Archipelago Setup Guide](/tutorial/Archipelago/setup/en).
+
+### Creating the Satisfactory World
+
+After you have installed the mods, launch the game via the Mod Manager or via your preferred method.
+Once the game has launched, start creating a new game.
+
+Select your starting location and Skip Intro if you wish to skip the tutorial sequence,
+then click the "Mod Savegame Settings" button in the bottom right corner of the screen.
+Next, enter the connection details in the relevant fields.
+
+- **Server URI**: Archipelago Server URI and port, for example, `archipelago.gg:49236`
+- **User Name**: The name you entered as your Player Name when you created your config file. It's also listed in the Name column of your room page.
+- **Password**: The password for your Archipelago room, blank if you did not assign or receive one.
+
+Note that the Satisfactory Host/Client does *not* need a copy of your Archipelago config file.
+The mod communicates with the Archipelago Server, which already has your config file,
+to generate the required content at runtime.
+
+Consider setting the following options in the "Options" > "Gameplay" section, especially because they are per-user and persist across your game saves:
+
+- **Creature Hostility**: `Default` (the game's default). Some of the mod's Traps involve creatures, and having them Passive or Retaliate cheapens the experience.
+- **Keep Inventory**: `Keep Everything` or `Keep Equipment` (the game's default). Although dying and dropping items will never lock you out of progression, Free Samples and Bundles means you can easily gain items you can't easily replace.
+
+### Verifying Connection Success
+
+After you have created your new world,
+you should see in-game chat messages confirming that you have connected to the Archipelago Server.
+
+You can issue the `/help` command in the game's chat to list available commands, such as `/hint`.
+For more information about the commands you can use, see the [Commands Guide](/tutorial/Archipelago/commands/en).
+Note that Archipelago commands are not prefixed with `!` inside of Satisfactory.
+You may wish to use the Text Client to run commands since Satisfactory's in game chat is not very user friendly.
+
+Check out the HUB to get started!
+
+See the [Troubleshooting section below](#troubleshooting) if you encounter any issues.
+
+### Allowing Other People to Join Your Game
+
+Additional players can join your game using the game's built-in multiplayer functionality.
+For more information, see [Satisfactory Wiki: Multiplayer](https://satisfactory.wiki.gg/wiki/Multiplayer).
+
+Have anyone you want to join follow the [Preparing to Play Satisfactory Archipelago](#preparing-to-play-satisfactory-archipelago) section above.
+If you're using any additional mods, be sure to export a profile using the Mod Manager for players to import.
+[Satisfactory Modding Documentation: Sharing Mod Manager Profiles](https://docs.ficsit.app/satisfactory-modding/latest/ForUsers/SatisfactoryModManager.html#_sharing_profiles)
+
+As mentioned above, it is possible to use a Satisfactory dedicated Server as your Satisfactory Host.
+The process for setting up and configuring a dedicated server is out of scope of this guide,
+but you can find more information here: [Satisfactory Modding Documentation: Installing Mods on Dedicated Servers](https://docs.ficsit.app/satisfactory-modding/latest/ForUsers/DedicatedServerSetup.html).
+
+It is important to note that the Satisfactory Archipelago mod
+is not yet compatible with Linux dedicated servers - only Windows dedicated servers are supported.
+
+### Port Changes
+
+If you are using a public Archipelago Server to host your game,
+rooms are automatically put to sleep after a period of inactivity.
+The room can be awoken by visiting the room page on the Archipelago website.
+This may cause the room's assigned port to change,
+requiring you to update your "Mod Savegame Settings" with the new Server URI.
+To do this, open your save, go to the pause menu's "Mod Savegame Settings" section,
+enter the updated Server URI, then save and reload the game.
+
+## Troubleshooting
+
+- If you are having trouble connecting to the Archipelago Server,
+ make sure you have entered the correct server address and port.
+ The server port may have changed if the room went to sleep.
+ See the [Port Changes section](#port-changes) above for more information.
+- If you are having trouble using the Satisfactory Mod Manager, join the [Satisfactory Modding Discord](https://discord.ficsit.app) for support.
+- If you encounter a game crash, please report it to us via the [Satisfactory Modding Discord](https://discord.ficsit.app).
+ Please include the following information:
+ - What you were doing when the crash occurred.
+ - If you were a Satisfactory multiplayer host or client, and if you were playing on a dedicated server.
+ - Use the Mod Manager to generate a debug zip and attach that file.
+ [Satisfactory Modding Documentation FAQ: Generating a debug zip](https://docs.ficsit.app/satisfactory-modding/latest/faq.html#_where_can_i_find_the_games_log_files)
+ - Attach your Archipelago config file and spoiler to your report.
diff --git a/worlds/saving_princess/docs/setup_en.md b/worlds/saving_princess/docs/setup_en.md
index 5f7cfb49f560..8dde0b30fe63 100644
--- a/worlds/saving_princess/docs/setup_en.md
+++ b/worlds/saving_princess/docs/setup_en.md
@@ -135,7 +135,7 @@ Additionally, if you get an item while already having the max for that item (for
It is likely that you do not have release or collect permissions, or that there is nothing to release or collect.
Another option is that your connection was interrupted.
-If you would still like to use release or collect, refer to [this section of the server commands page](https://archipelago.gg/tutorial/Archipelago/commands/en#collect/release).
+If you would still like to use release or collect, refer to [this section of the server commands page](/tutorial/Archipelago/commands/en#collectrelease).
You may use the in-game console to execute the commands, if your slot has permissions to do so.
diff --git a/worlds/sc2/__init__.py b/worlds/sc2/__init__.py
index b5c7b5f0cf78..0df315c62920 100644
--- a/worlds/sc2/__init__.py
+++ b/worlds/sc2/__init__.py
@@ -148,7 +148,6 @@ def create_items(self) -> None:
flag_start_inventory(self, item_list)
flag_unused_upgrade_types(self, item_list)
flag_unreleased_items(item_list)
- flag_user_excluded_item_sets(self, item_list)
flag_war_council_items(self, item_list)
flag_and_add_resource_locations(self, item_list)
flag_mission_order_required_items(self, item_list)
@@ -375,45 +374,67 @@ def create_and_flag_explicit_item_locks_and_excludes(world: SC2World) -> List[Fi
Handles `excluded_items`, `locked_items`, and `start_inventory`
Returns a list of all possible non-filler items that can be added, with an accompanying flags bitfield.
"""
- excluded_items = world.options.excluded_items
- unexcluded_items = world.options.unexcluded_items
- locked_items = world.options.locked_items
- start_inventory = world.options.start_inventory
+ excluded_items: dict[str, int] = world.options.excluded_items.value
+ unexcluded_items: dict[str, int] = world.options.unexcluded_items.value
+ locked_items: dict[str, int] = world.options.locked_items.value
+ start_inventory: dict[str, int] = world.options.start_inventory.value
key_items = world.custom_mission_order.get_items_to_lock()
- def resolve_count(count: Optional[int], max_count: int) -> int:
- if count == 0:
+ def resolve_exclude(count: int, max_count: int) -> int:
+ if count < 0:
return max_count
- if count is None:
- return 0
- if max_count == 0:
- return count
- return min(count, max_count)
+ return count
+
+ def resolve_count(count: int, max_count: int, negative_value: int | None = None) -> int:
+ """
+ Handles `count` being out of range.
+ * If `count > max_count`, returns `max_count`.
+ * If `count < 0`, returns `negative_value` (returns `max_count` if `negative_value` is unspecified)
+ """
+ if count < 0:
+ if negative_value is None:
+ return max_count
+ return negative_value
+ if max_count and count > max_count:
+ return max_count
+ return count
- auto_excludes = {item_name: 1 for item_name in item_groups.legacy_items}
+ auto_excludes = Counter({item_name: 1 for item_name in item_groups.legacy_items})
if world.options.exclude_overpowered_items.value == ExcludeOverpoweredItems.option_true:
for item_name in item_groups.overpowered_items:
auto_excludes[item_name] = 1
+ if world.options.vanilla_items_only.value == VanillaItemsOnly.option_true:
+ for item_name, item_data in item_tables.item_table.items():
+ if item_name in item_groups.terran_original_progressive_upgrades:
+ auto_excludes[item_name] = max(item_data.quantity - 1, auto_excludes.get(item_name, 0))
+ elif item_name in item_groups.vanilla_items:
+ continue
+ elif item_name in item_groups.nova_equipment:
+ continue
+ else:
+ auto_excludes[item_name] = item_data.quantity
+
result: List[FilterItem] = []
for item_name, item_data in item_tables.item_table.items():
max_count = item_data.quantity
- auto_excluded_count = auto_excludes.get(item_name)
+ auto_excluded_count = auto_excludes.get(item_name, 0)
excluded_count = excluded_items.get(item_name, auto_excluded_count)
- unexcluded_count = unexcluded_items.get(item_name)
- locked_count = locked_items.get(item_name)
- start_count: Optional[int] = start_inventory.get(item_name)
+ unexcluded_count = unexcluded_items.get(item_name, 0)
+ locked_count = locked_items.get(item_name, 0)
+ start_count = start_inventory.get(item_name, 0)
key_count = key_items.get(item_name, 0)
- # specifying 0 in the yaml means exclude / lock all
- # start_inventory doesn't allow specifying 0
- # not specifying means don't exclude/lock/start
- excluded_count = resolve_count(excluded_count, max_count)
- unexcluded_count = resolve_count(unexcluded_count, max_count)
+ # Specifying a negative number in the yaml means exclude / lock / start all.
+ # In the case of excluded/unexcluded, resolve negatives to max_count before subtracting them,
+ # and after subtraction resolve negatives to just 0 (when unexcluded > excluded).
+ excluded_count = resolve_count(
+ resolve_exclude(excluded_count, max_count) - resolve_exclude(unexcluded_count, max_count),
+ max_count,
+ negative_value=0
+ )
locked_count = resolve_count(locked_count, max_count)
start_count = resolve_count(start_count, max_count)
- excluded_count = max(0, excluded_count - unexcluded_count)
-
# Priority: start_inventory >> locked_items >> excluded_items >> unspecified
if max_count == 0:
if excluded_count:
@@ -476,8 +497,9 @@ def flag_excludes_by_faction_presence(world: SC2World, item_list: List[FilterIte
item.flags |= ItemFilterFlags.FilterExcluded
continue
if not zerg_missions and item.data.race == SC2Race.ZERG:
- if item.data.type != item_tables.ZergItemType.Ability \
- and item.data.type != ZergItemType.Level:
+ if (item.data.type != item_tables.ZergItemType.Ability
+ and item.data.type != ZergItemType.Level
+ ):
item.flags |= ItemFilterFlags.FilterExcluded
continue
if not protoss_missions and item.data.race == SC2Race.PROTOSS:
@@ -631,7 +653,7 @@ def flag_mission_based_item_excludes(world: SC2World, item_list: List[FilterItem
item.flags |= ItemFilterFlags.FilterExcluded
# Remove Spear of Adun passives
- if item.name in item_tables.spear_of_adun_castable_passives and not soa_passive_presence:
+ if item.name in item_groups.spear_of_adun_passives and not soa_passive_presence:
item.flags |= ItemFilterFlags.FilterExcluded
# Remove matchup-specific items if you don't play that matchup
@@ -836,26 +858,6 @@ def flag_unreleased_items(item_list: List[FilterItem]) -> None:
item.flags |= ItemFilterFlags.Removed
-def flag_user_excluded_item_sets(world: SC2World, item_list: List[FilterItem]) -> None:
- """Excludes items based on item set options (`only_vanilla_items`)"""
- vanilla_nonprogressive_count = {
- item_name: 0 for item_name in item_groups.terran_original_progressive_upgrades
- }
- if world.options.vanilla_items_only.value == VanillaItemsOnly.option_true:
- vanilla_items = item_groups.vanilla_items + item_groups.nova_equipment
- for item in item_list:
- if ItemFilterFlags.UserExcluded in item.flags:
- continue
- if item.name not in vanilla_items:
- item.flags |= ItemFilterFlags.UserExcluded
- if item.name in item_groups.terran_original_progressive_upgrades:
- if vanilla_nonprogressive_count[item.name]:
- item.flags |= ItemFilterFlags.UserExcluded
- vanilla_nonprogressive_count[item.name] += 1
-
- excluded_count: Dict[str, int] = dict()
-
-
def flag_war_council_items(world: SC2World, item_list: List[FilterItem]) -> None:
"""Excludes / start-inventories items based on `nerf_unit_baselines` option.
Will skip items that are excluded by other sources."""
diff --git a/worlds/sc2/archipelago.json b/worlds/sc2/archipelago.json
new file mode 100644
index 000000000000..01817550f1d9
--- /dev/null
+++ b/worlds/sc2/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Starcraft 2",
+ "authors": ["The Archipelago SC2 Team"],
+ "world_version": "4.0.0",
+ "minimum_ap_version": "0.6.4"
+}
diff --git a/worlds/sc2/client.py b/worlds/sc2/client.py
index e9f46f93b7a9..708b00cd274b 100644
--- a/worlds/sc2/client.py
+++ b/worlds/sc2/client.py
@@ -21,7 +21,6 @@
import concurrent.futures
import time
import uuid
-import argparse
from pathlib import Path
# CommonClient import first to trigger ModuleUpdater
@@ -41,6 +40,7 @@
SpearOfAdunPassivesPresentInNoBuild, EnableVoidTrade, VoidTradeAgeLimit, void_trade_age_limits_ms, VoidTradeWorkers,
DifficultyDamageModifier, MissionOrderScouting, GenericUpgradeResearchSpeedup, MercenaryHighlanders, WarCouncilNerfs,
is_mission_in_soa_presence,
+ upgrade_included_names,
)
from .mission_order.slot_data import CampaignSlotData, LayoutSlotData, MissionSlotData, MissionOrderObjectSlotData
from .mission_order.entry_rules import SubRuleRuleData, CountMissionsRuleData, MissionEntryRules
@@ -72,17 +72,19 @@
)
import colorama
-from .options import Option, upgrade_included_names
from NetUtils import ClientStatus, NetworkItem, JSONtoTextParser, JSONMessagePart, add_json_item, add_json_location, add_json_text, JSONTypes
from MultiServer import mark_raw
+if typing.TYPE_CHECKING:
+ from Options import Option
+
pool = concurrent.futures.ThreadPoolExecutor(1)
loop = asyncio.get_event_loop_policy().new_event_loop()
nest_asyncio.apply(loop)
MAX_BONUS: int = 28
# GitHub repo where the Map/mod data is hosted for /download_data command
-DATA_REPO_OWNER = "Ziktofel"
+DATA_REPO_OWNER = "archipelago-sc2"
DATA_REPO_NAME = "Archipelago-SC2-data"
DATA_API_VERSION = "API4"
diff --git a/worlds/sc2/client_gui.py b/worlds/sc2/client_gui.py
index 6b2abcd9e96a..33969a06994b 100644
--- a/worlds/sc2/client_gui.py
+++ b/worlds/sc2/client_gui.py
@@ -1,3 +1,4 @@
+import inspect
from typing import *
import asyncio
import logging
@@ -362,13 +363,25 @@ def mission_text(
text = mission_obj.mission_name
tooltip: str = ""
- remaining_locations, plando_locations, remaining_count = self.sort_unfinished_locations(mission_id)
+ mission_remaining_locations, plando_locations, remaining_count = self.sort_unfinished_locations(mission_id)
+ remaining_goal_missions_locations_by_mission = [
+ mission_locations for mission_locations, _, _ in [
+ self.sort_unfinished_locations(final_mission_id)
+ for final_mission_id in self.ctx.final_mission_ids
+ ]
+ ]
+ remaining_goal_victory_locations = [
+ location for mission_locations
+ in remaining_goal_missions_locations_by_mission
+ for location in mission_locations
+ if location[0] == LocationType.VICTORY
+ ]
campaign_locked = campaign_id not in available_campaigns
layout_locked = layout_id not in available_layouts[campaign_id]
# Map has uncollected locations
if mission_id in unfinished_missions:
- if self.any_valuable_locations(remaining_locations):
+ if self.any_valuable_locations(mission_remaining_locations):
text = f"[color={COLOR_MISSION_IMPORTANT}]{text}[/color]"
else:
text = f"[color={COLOR_MISSION_UNIMPORTANT}]{text}[/color]"
@@ -428,10 +441,21 @@ def mission_text(
tooltip += "\n\n"
elif exit_for:
tooltip += "\n"
- if any(location_type == LocationType.VICTORY for (location_type, _, _) in remaining_locations):
+ if any(location_type == LocationType.VICTORY for (location_type, _, _) in mission_remaining_locations):
tooltip += f"[color={COLOR_FINAL_MISSION_REMINDER}]Required to beat the world[/color]"
else:
- tooltip += "This goal mission is already beaten.\nBeat the remaining goal missions to beat the world."
+ if len(remaining_goal_victory_locations) > 0:
+ tooltip += inspect.cleandoc("""
+ This goal mission is already beaten.
+ Beat the remaining goal missions to beat the world.
+ """)
+ elif not self.ctx.finished_game:
+ # All goal locations may have been collected. Beat any goal mission to beat the world then.
+ tooltip += inspect.cleandoc("""
+ This is a goal mission.
+ Beat any goal mission to beat the world.
+ """
+ )
# Populate remaining location list
if remaining_count > 0:
@@ -444,18 +468,18 @@ def mission_text(
if self.ctx.mission_order_scouting != MissionOrderScouting.option_none:
mission_available = mission_id in available_missions
- scoutable = self.is_scoutable(remaining_locations, mission_available, layout_locked, campaign_locked)
+ scoutable = self.is_scoutable(mission_remaining_locations, mission_available, layout_locked, campaign_locked)
else:
scoutable = False
- for location_type, location_name, _ in remaining_locations:
+ for location_type, location_name, _ in mission_remaining_locations:
if location_type in (LocationType.VICTORY, LocationType.VICTORY_CACHE) and victory_printed:
continue
if location_type != last_location_type:
tooltip += f"\n[color={COLOR_MISSION_IMPORTANT}]{self.get_location_type_title(location_type)}:[/color]"
last_location_type = location_type
if location_type == LocationType.VICTORY:
- victory_count = len([loc for loc in remaining_locations if loc[0] in (LocationType.VICTORY, LocationType.VICTORY_CACHE)])
+ victory_count = len([loc for loc in mission_remaining_locations if loc[0] in (LocationType.VICTORY, LocationType.VICTORY_CACHE)])
victory_loc = location_name.replace(":", f":[color={COLOR_VICTORY_LOCATION}]")
if victory_count > 1:
victory_loc += f' ({victory_count})'
diff --git a/worlds/sc2/docs/contributors.md b/worlds/sc2/docs/contributors.md
index b1e7e65511cf..ff0e51697960 100644
--- a/worlds/sc2/docs/contributors.md
+++ b/worlds/sc2/docs/contributors.md
@@ -34,6 +34,7 @@ code contributors also reported bugs and participated in beta testing.
* 7thAce (@7thAce) - Pulsar
* Panicmoon (@panicmoon.bsky.social) - Skylord
* JayborinoPlays (@Jayborino) - Oppressor
+* MindHawk (@MindHawk) - Caladrius
## Maintenance of 2024 release
* Ziktofel (@Ziktofel)
diff --git a/worlds/sc2/docs/custom_mission_orders_en.md b/worlds/sc2/docs/custom_mission_orders_en.md
index 6aba753b699e..c8d7c25c7c5b 100644
--- a/worlds/sc2/docs/custom_mission_orders_en.md
+++ b/worlds/sc2/docs/custom_mission_orders_en.md
@@ -60,7 +60,7 @@
This is usage documentation for the `custom_mission_order` YAML option for Starcraft 2. You can enable Custom Mission Orders by setting `mission_order: custom` in your YAML.
-You will need to know how to write a YAML before engaging with this feature, and should read the [Archipelago YAML documentation](https://archipelago.gg/tutorial/Archipelago/advanced_settings/en) before continuing here.
+You will need to know how to write a YAML before engaging with this feature, and should read the [Archipelago YAML documentation](/tutorial/Archipelago/advanced_settings/en) before continuing here.
Every example in this document should be valid to generate.
diff --git a/worlds/sc2/docs/setup_en.md b/worlds/sc2/docs/setup_en.md
index a8d03d5a3df2..d6f9f6cda518 100644
--- a/worlds/sc2/docs/setup_en.md
+++ b/worlds/sc2/docs/setup_en.md
@@ -145,7 +145,7 @@ That's the default value in the template, which should let you know to use this
#### How do I know the exact names of items and locations?
You can look up a complete list of the item names in the
-[Icon Repository](https://matthewmarinets.github.io/ap_sc2_icons/) page.
+[APSC2 Item Docs](https://archipelago-sc2.github.io/content-docs/) page.
This page also contains supplementary information of each item.
Locations are of the format `: `. Names are most easily looked up by hovering
diff --git a/worlds/sc2/docs/setup_fr.md b/worlds/sc2/docs/setup_fr.md
index 4e7a9663aa91..f5c3b93c162f 100644
--- a/worlds/sc2/docs/setup_fr.md
+++ b/worlds/sc2/docs/setup_fr.md
@@ -117,7 +117,7 @@ La page [*datapackage*](/datapackage) d'Archipelago liste l'ensemble des *items*
que le site web prend en charge actuellement, dont ceux de *StarCraft 2*.
Vous trouverez aussi la liste complète des *items* de *StarCraft 2 Archipelago* à la page
-[*Icon Repository*](https://matthewmarinets.github.io/ap_sc2_icons/).
+[*APSC2 Item Docs*](https://archipelago-sc2.github.io/content-docs/).
Notez que cette page contient diverses informations supplémentaires sur chacun des *items*.
Cependant, l'information présente dans cette dernière peut différer de celle du *datapackage* d'Archipelago
puisqu'elle est générée, habituellement, à partir de la version en développement de *StarCraft 2 Archipelago* qui
diff --git a/worlds/sc2/item/item_descriptions.py b/worlds/sc2/item/item_descriptions.py
index f1520df1424c..45ab2b384d78 100644
--- a/worlds/sc2/item/item_descriptions.py
+++ b/worlds/sc2/item/item_descriptions.py
@@ -775,7 +775,7 @@ def _ability_desc(unit_name_plural: str, ability_name: str, ability_description:
item_names.BULLFROG_BROODLINGS: "Bullfrogs spawn two broodlings on impact, in addition to unloading their cargo.",
item_names.BULLFROG_HARD_IMPACT: "Bullfrogs deal more damage and stun longer on impact.",
item_names.INFESTED_BANSHEE_BRACED_EXOSKELETON: "Infested Banshees gain +100 life.",
- item_names.INFESTED_BANSHEE_RAPID_HIBERNATION: "Infested Banshees regenerate 20 life and energy per second while burrowed.",
+ item_names.INFESTED_BANSHEE_RAPID_HIBERNATION: "Allows Infested Banshees to Burrow. Infested Banshees regenerate 20 life and energy per second while burrowed.",
item_names.INFESTED_BANSHEE_FLESHFUSED_TARGETING_OPTICS: "Infested Banshees gain +2 range while cloaked.",
item_names.INFESTED_LIBERATOR_CLOUD_DISPERSAL: "Infested Liberators instantly transform into a cloud of microscopic organisms while attacking, reducing the damage they take by 85%.",
item_names.INFESTED_LIBERATOR_VIRAL_CONTAMINATION: "Increases the damage Infested Liberators deal to their primary target by 100%.",
@@ -951,14 +951,14 @@ def _ability_desc(unit_name_plural: str, ability_name: str, ability_description:
item_names.TEMPEST_GRAVITY_SLING: "Tempests gain +8 range against air targets and +8 cast range.",
item_names.TEMPEST_INTERPLANETARY_RANGE: "Tempests gain +8 weapon range against all targets.",
item_names.PHOENIX_CLASS_IONIC_WAVELENGTH_FLUX: "Increases Phoenix, Mirage, and Skirmisher weapon damage by +2.",
- item_names.PHOENIX_CLASS_ANION_PULSE_CRYSTALS: "Increases Phoenix, Mirage, and Skirmiser range by +2.",
+ item_names.PHOENIX_CLASS_ANION_PULSE_CRYSTALS: "Increases Phoenix, Mirage, and Skirmisher range by +2.",
item_names.CORSAIR_STEALTH_DRIVE: "Corsairs become permanently cloaked.",
item_names.CORSAIR_ARGUS_JEWEL: "Corsairs can store 2 charges of disruption web.",
item_names.CORSAIR_SUSTAINING_DISRUPTION: "Corsair disruption webs last longer.",
item_names.CORSAIR_NEUTRON_SHIELDS: "Increases corsair maximum shields by +20.",
item_names.ORACLE_STEALTH_DRIVE: "Oracles become permanently cloaked.",
item_names.ORACLE_SKYWARD_CHRONOANOMALY: "The Oracle's Stasis Ward can affect air units.",
- item_names.ORACLE_TEMPORAL_ACCELERATION_BEAM: "Oracles no longer need to to spend energy to attack.",
+ item_names.ORACLE_TEMPORAL_ACCELERATION_BEAM: "Oracles no longer need to spend energy to attack.",
item_names.ORACLE_BOSONIC_CORE: "Increases starting energy by 150 and maximum energy by 50.",
item_names.ARBITER_CHRONOSTATIC_REINFORCEMENT: "Arbiters gain +50 maximum life and +1 armor.",
item_names.ARBITER_KHAYDARIN_CORE: _get_start_and_max_energy_desc("Arbiters"),
diff --git a/worlds/sc2/item/item_groups.py b/worlds/sc2/item/item_groups.py
index ea65dc3e4aba..7a41b9865985 100644
--- a/worlds/sc2/item/item_groups.py
+++ b/worlds/sc2/item/item_groups.py
@@ -111,6 +111,9 @@ class ItemGroupNames:
TERRAN_ORIGINAL_PROGRESSIVE_UPGRADES = "Terran Original Progressive Upgrades"
"""Progressive items where level 1 appeared in WoL"""
MENGSK_UNITS = "Mengsk Units"
+ TERRAN_SC1_UNITS = "Terran SC1 Units"
+ TERRAN_SC1_BUILDINGS = "Terran SC1 Buildings"
+ TERRAN_LADDER_UNITS = "Terran Ladder Units"
TERRAN_VETERANCY_UNITS = "Terran Veterancy Units"
ORBITAL_COMMAND_ABILITIES = "Orbital Command Abilities"
WOL_ORBITAL_COMMAND_ABILITIES = "WoL Command Center Abilities"
@@ -154,6 +157,8 @@ class ItemGroupNames:
"""All items from Stukov co-op subfaction"""
INF_TERRAN_UNITS = "Infested Terran Units"
INF_TERRAN_UPGRADES = "Infested Terran Upgrades"
+ ZERG_SC1_UNITS = "Zerg SC1 Units"
+ ZERG_LADDER_UNITS = "Zerg Ladder Units"
PROTOSS_ITEMS = "Protoss Items"
PROTOSS_UNITS = "Protoss Units"
@@ -167,6 +172,7 @@ class ItemGroupNames:
LOTV_UNITS = "LotV Units"
LOTV_ITEMS = "LotV Items"
LOTV_GLOBAL_UPGRADES = "LotV Global Upgrades"
+ SOA_PASSIVES = "SOA Passive Abilities"
SOA_ITEMS = "SOA"
PROTOSS_GLOBAL_UPGRADES = "Protoss Global Upgrades"
PROTOSS_BUILDINGS = "Protoss Buildings"
@@ -175,6 +181,9 @@ class ItemGroupNames:
NERAZIM_UNITS = "Nerazim"
TAL_DARIM_UNITS = "Tal'Darim"
PURIFIER_UNITS = "Purifier"
+ PROTOSS_SC1_UNITS = "Protoss SC1 Units"
+ PROTOSS_SC1_BUILDINGS = "Protoss SC1 Buildings"
+ PROTOSS_LADDER_UNITS = "Protoss Ladder Units"
VANILLA_ITEMS = "Vanilla Items"
OVERPOWERED_ITEMS = "Overpowered Items"
@@ -287,8 +296,14 @@ def get_all_group_names(cls) -> typing.Set[str]:
item_names.HIVE_MIND_EMULATOR, item_names.PSI_DISRUPTER,
]
item_name_groups[ItemGroupNames.TERRAN_BUILDINGS] = terran_buildings = [
- item_name for item_name, item_data in item_tables.item_table.items()
- if item_data.type == item_tables.TerranItemType.Building or item_name in wol_buildings
+ *[
+ item_name for item_name, item_data in item_tables.item_table.items()
+ if item_data.type == item_tables.TerranItemType.Building or item_name in wol_buildings
+ ],
+ item_names.PSI_SCREEN,
+ item_names.SONIC_DISRUPTER,
+ item_names.PSI_INDOCTRINATOR,
+ item_names.ARGUS_AMPLIFIER,
]
item_name_groups[ItemGroupNames.MENGSK_UNITS] = [
item_names.AEGIS_GUARD, item_names.EMPERORS_SHADOW,
@@ -316,6 +331,41 @@ def get_all_group_names(cls) -> typing.Set[str]:
item_names.SIEGE_TANK_SPIDER_MINES,
item_names.RAVEN_SPIDER_MINES,
]
+item_name_groups[ItemGroupNames.TERRAN_SC1_UNITS] = [
+ item_names.MARINE,
+ item_names.FIREBAT,
+ item_names.GHOST,
+ item_names.MEDIC,
+ item_names.VULTURE,
+ item_names.SIEGE_TANK,
+ item_names.GOLIATH,
+ item_names.WRAITH,
+ # No dropship
+ item_names.SCIENCE_VESSEL,
+ item_names.BATTLECRUISER,
+ item_names.VALKYRIE,
+]
+item_name_groups[ItemGroupNames.TERRAN_SC1_BUILDINGS] = [
+ item_names.BUNKER,
+ item_names.MISSILE_TURRET,
+]
+item_name_groups[ItemGroupNames.TERRAN_LADDER_UNITS] = [
+ item_names.MARINE,
+ item_names.MARAUDER,
+ item_names.REAPER,
+ item_names.GHOST,
+ item_names.HELLION,
+ item_names.WIDOW_MINE,
+ item_names.SIEGE_TANK,
+ item_names.THOR,
+ item_names.CYCLONE,
+ item_names.VIKING,
+ item_names.MEDIVAC,
+ item_names.LIBERATOR,
+ item_names.RAVEN,
+ item_names.BANSHEE,
+ item_names.BATTLECRUISER,
+]
# Terran Upgrades
item_name_groups[ItemGroupNames.WOL_UPGRADES] = wol_upgrades = [
@@ -596,6 +646,38 @@ def get_all_group_names(cls) -> typing.Set[str]:
item_names.OVERLORD_IMPROVED_OVERLORDS,
item_names.OVERLORD_OVERSEER_ASPECT,
]
+item_name_groups[ItemGroupNames.ZERG_SC1_UNITS] = [
+ item_names.ZERGLING,
+ item_names.HYDRALISK,
+ item_names.MUTALISK,
+ item_names.SCOURGE,
+ item_names.BROOD_QUEEN,
+ item_names.DEFILER,
+ item_names.ULTRALISK,
+ item_names.HYDRALISK_LURKER_ASPECT,
+ item_names.MUTALISK_CORRUPTOR_DEVOURER_ASPECT,
+ item_names.MUTALISK_CORRUPTOR_GUARDIAN_ASPECT,
+ item_names.DEVOURING_ONES,
+ item_names.HUNTER_KILLERS,
+ item_names.TORRASQUE_MERC,
+]
+item_name_groups[ItemGroupNames.ZERG_LADDER_UNITS] = [
+ item_names.ZERGLING,
+ item_names.SWARM_QUEEN, # Replace: Hive Queen
+ item_names.ZERGLING_BANELING_ASPECT,
+ item_names.ROACH,
+ item_names.ROACH_RAVAGER_ASPECT,
+ item_names.OVERLORD_OVERSEER_ASPECT,
+ item_names.HYDRALISK,
+ item_names.HYDRALISK_LURKER_ASPECT,
+ item_names.MUTALISK,
+ item_names.CORRUPTOR,
+ item_names.MUTALISK_CORRUPTOR_VIPER_ASPECT,
+ item_names.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT,
+ item_names.INFESTOR,
+ item_names.SWARM_HOST,
+ item_names.ULTRALISK,
+]
# Zerg Upgrades
item_name_groups[ItemGroupNames.HOTS_STRAINS] = hots_strains = [
@@ -777,11 +859,21 @@ def get_all_group_names(cls) -> typing.Set[str]:
item_names.MIRAGE, item_names.DAWNBRINGER, item_names.TRIREME, item_names.TEMPEST,
item_names.CALADRIUS,
]
-item_name_groups[ItemGroupNames.SOA_ITEMS] = soa_items = [
+item_name_groups[ItemGroupNames.SOA_PASSIVES] = spear_of_adun_passives = [
+ item_names.RECONSTRUCTION_BEAM,
+ item_names.OVERWATCH,
+ item_names.GUARDIAN_SHELL,
+]
+spear_of_adun_actives = [
*[item_name for item_name, item_data in item_tables.item_table.items() if item_data.type == item_tables.ProtossItemType.Spear_Of_Adun],
item_names.SOA_PROGRESSIVE_PROXY_PYLON,
]
-lotv_soa_items = [item_name for item_name in soa_items if item_name != item_names.SOA_PYLON_OVERCHARGE]
+item_name_groups[ItemGroupNames.SOA_ITEMS] = soa_items = spear_of_adun_actives + spear_of_adun_passives
+lotv_soa_items = [
+ item_name
+ for item_name in soa_items
+ if item_name not in (item_names.SOA_PYLON_OVERCHARGE, item_names.OVERWATCH)
+]
item_name_groups[ItemGroupNames.PROTOSS_GLOBAL_UPGRADES] = [
item_name for item_name, item_data in item_tables.item_table.items() if item_data.type == item_tables.ProtossItemType.Solarite_Core
]
@@ -815,6 +907,45 @@ def get_all_group_names(cls) -> typing.Set[str]:
+ protoss_generic_upgrades
+ lotv_war_council_upgrades
)
+item_name_groups[ItemGroupNames.PROTOSS_SC1_UNITS] = [
+ item_names.ZEALOT,
+ item_names.DRAGOON,
+ item_names.HIGH_TEMPLAR,
+ item_names.DARK_TEMPLAR,
+ item_names.DARK_ARCHON,
+ item_names.DARK_TEMPLAR_DARK_ARCHON_MELD,
+ # No shuttle
+ item_names.REAVER,
+ item_names.OBSERVER,
+ item_names.SCOUT,
+ item_names.CARRIER,
+ item_names.ARBITER,
+ item_names.CORSAIR,
+]
+item_name_groups[ItemGroupNames.PROTOSS_SC1_BUILDINGS] = [
+ item_names.PHOTON_CANNON,
+ item_names.SHIELD_BATTERY,
+]
+item_name_groups[ItemGroupNames.PROTOSS_LADDER_UNITS] = [
+ item_names.ZEALOT,
+ item_names.STALKER,
+ item_names.SENTRY,
+ item_names.ADEPT,
+ item_names.HIGH_TEMPLAR,
+ item_names.DARK_TEMPLAR,
+ item_names.DARK_TEMPLAR_ARCHON_MERGE,
+ item_names.OBSERVER,
+ item_names.WARP_PRISM,
+ item_names.IMMORTAL,
+ item_names.COLOSSUS,
+ item_names.DISRUPTOR,
+ item_names.PHOENIX,
+ item_names.VOID_RAY,
+ item_names.ORACLE,
+ item_names.CARRIER,
+ item_names.TEMPEST,
+ item_names.MOTHERSHIP, # Replace: Aiur Mothership
+]
item_name_groups[ItemGroupNames.VANILLA_ITEMS] = vanilla_items = (
vanilla_wol_items + vanilla_hots_items + vanilla_lotv_items
diff --git a/worlds/sc2/item/item_tables.py b/worlds/sc2/item/item_tables.py
index d63b00489f01..46d1ceacbacc 100644
--- a/worlds/sc2/item/item_tables.py
+++ b/worlds/sc2/item/item_tables.py
@@ -2151,127 +2151,6 @@ def get_item_table():
item_names.TEMPEST,
}
-
-# Defense rating table
-# Commented defense ratings are handled in LogicMixin
-tvx_defense_ratings = {
- item_names.SIEGE_TANK: 5,
- # "Graduating Range": 1,
- item_names.PLANETARY_FORTRESS: 3,
- # Bunker w/ Marine/Marauder: 3,
- item_names.PERDITION_TURRET: 2,
- item_names.DEVASTATOR_TURRET: 2,
- item_names.VULTURE: 1,
- item_names.BANSHEE: 1,
- item_names.BATTLECRUISER: 1,
- item_names.LIBERATOR: 4,
- item_names.WIDOW_MINE: 1,
- # "Concealment (Widow Mine)": 1
-}
-tvz_defense_ratings = {
- item_names.PERDITION_TURRET: 2,
- # Bunker w/ Firebat: 2,
- item_names.LIBERATOR: -2,
- item_names.HIVE_MIND_EMULATOR: 3,
- item_names.PSI_DISRUPTER: 3,
-}
-tvx_air_defense_ratings = {
- item_names.MISSILE_TURRET: 2,
-}
-zvx_defense_ratings = {
- # Note that this doesn't include Kerrigan because this is just for race swaps, which doesn't involve her (for now)
- item_names.SPINE_CRAWLER: 3,
- # w/ Twin Drones: 1
- item_names.SWARM_QUEEN: 1,
- item_names.SWARM_HOST: 1,
- # impaler: 3
- # "Hardened Tentacle Spines (Impaler)": 2
- # lurker: 1
- # "Seismic Spines (Lurker)": 2
- # "Adapted Spines (Lurker)": 1
- # brood lord : 2
- # corpser roach: 1
- # creep tumors (swarm queen or overseer): 1
- # w/ malignant creep: 1
- # tanks with ammo: 5
- item_names.INFESTED_BUNKER: 3,
- item_names.BILE_LAUNCHER: 2,
-}
-# zvz_defense_ratings = {
- # corpser roach: 1
- # primal igniter: 2
- # lurker: 1
- # w/ adapted spines: -1
- # impaler: -1
-# }
-zvx_air_defense_ratings = {
- item_names.SPORE_CRAWLER: 2,
- # w/ Twin Drones: 1
- item_names.INFESTED_MISSILE_TURRET: 2,
-}
-pvx_defense_ratings = {
- item_names.PHOTON_CANNON: 2,
- item_names.KHAYDARIN_MONOLITH: 3,
- item_names.SHIELD_BATTERY: 1,
- item_names.NEXUS_OVERCHARGE: 2,
- item_names.SKYLORD: 1,
- item_names.MATRIX_OVERLOAD: 1,
- item_names.COLOSSUS: 1,
- item_names.VANGUARD: 1,
- item_names.REAVER: 1,
-}
-pvz_defense_ratings = {
- item_names.KHAYDARIN_MONOLITH: -2,
- item_names.COLOSSUS: 1,
-}
-
-terran_passive_ratings = {
- item_names.AUTOMATED_REFINERY: 4,
- item_names.COMMAND_CENTER_MULE: 4,
- item_names.ORBITAL_DEPOTS: 2,
- item_names.COMMAND_CENTER_COMMAND_CENTER_REACTOR: 2,
- item_names.COMMAND_CENTER_EXTRA_SUPPLIES: 2,
- item_names.MICRO_FILTERING: 2,
- item_names.TECH_REACTOR: 2
-}
-
-zerg_passive_ratings = {
- item_names.TWIN_DRONES: 7,
- item_names.AUTOMATED_EXTRACTORS: 4,
- item_names.VESPENE_EFFICIENCY: 3,
- item_names.OVERLORD_IMPROVED_OVERLORDS: 4,
- item_names.MALIGNANT_CREEP: 2
-}
-
-protoss_passive_ratings = {
- item_names.QUATRO: 4,
- item_names.ORBITAL_ASSIMILATORS: 4,
- item_names.AMPLIFIED_ASSIMILATORS: 3,
- item_names.PROBE_WARPIN: 2,
- item_names.ELDER_PROBES: 2,
- item_names.MATRIX_OVERLOAD: 2
-}
-
-soa_energy_ratings = {
- item_names.SOA_SOLAR_LANCE: 8,
- item_names.SOA_DEPLOY_FENIX: 7,
- item_names.SOA_TEMPORAL_FIELD: 6,
- item_names.SOA_PROGRESSIVE_PROXY_PYLON: 5, # Requires Lvl 2 (Warp in Reinforcements)
- item_names.SOA_SHIELD_OVERCHARGE: 5,
- item_names.SOA_ORBITAL_STRIKE: 4
-}
-
-soa_passive_ratings = {
- item_names.GUARDIAN_SHELL: 4,
- item_names.OVERWATCH: 2
-}
-
-soa_ultimate_ratings = {
- item_names.SOA_TIME_STOP: 4,
- item_names.SOA_PURIFIER_BEAM: 3,
- item_names.SOA_SOLAR_BOMBARDMENT: 3
-}
-
kerrigan_levels = [
item_name for item_name, item_data in item_table.items()
if item_data.type == ZergItemType.Level and item_data.race == SC2Race.ZERG
@@ -2293,12 +2172,6 @@ def get_item_table():
item_names.SOA_SOLAR_BOMBARDMENT
}
-spear_of_adun_castable_passives = {
- item_names.RECONSTRUCTION_BEAM,
- item_names.OVERWATCH,
- item_names.GUARDIAN_SHELL,
-}
-
nova_equipment = {
*[item_name for item_name, item_data in get_full_item_list().items()
if item_data.type == TerranItemType.Nova_Gear],
diff --git a/worlds/sc2/locations.py b/worlds/sc2/locations.py
index 34245d863751..318d52f85611 100644
--- a/worlds/sc2/locations.py
+++ b/worlds/sc2/locations.py
@@ -3950,8 +3950,11 @@ def get_locations(world: Optional["SC2World"]) -> Tuple[LocationData, ...]:
"Victory",
SC2LOTV_LOC_ID_OFFSET + 500,
LocationType.VICTORY,
- lambda state: logic.protoss_common_unit(state)
- and (adv_tactics or logic.protoss_moderate_anti_air(state)),
+ lambda state: (
+ logic.protoss_common_unit(state)
+ and logic.protoss_moderate_anti_air(state)
+ ),
+ hard_rule=logic.protoss_any_anti_air_unit_or_soa,
),
make_location_data(
SC2Mission.THE_GROWING_SHADOW.mission_name,
@@ -3986,8 +3989,11 @@ def get_locations(world: Optional["SC2World"]) -> Tuple[LocationData, ...]:
"Templar Base",
SC2LOTV_LOC_ID_OFFSET + 505,
LocationType.EXTRA,
- lambda state: logic.protoss_common_unit(state)
- and (adv_tactics or logic.protoss_moderate_anti_air(state)),
+ lambda state: (
+ logic.protoss_common_unit(state)
+ and logic.protoss_moderate_anti_air(state)
+ ),
+ hard_rule=logic.protoss_any_anti_air_unit_or_soa,
),
make_location_data(
SC2Mission.THE_SPEAR_OF_ADUN.mission_name,
@@ -12170,8 +12176,9 @@ def get_locations(world: Optional["SC2World"]) -> Tuple[LocationData, ...]:
LocationType.VICTORY,
lambda state: (
logic.terran_common_unit(state)
- and (adv_tactics or logic.terran_moderate_anti_air(state))
+ and logic.terran_moderate_anti_air(state)
),
+ hard_rule=logic.terran_any_anti_air,
),
make_location_data(
SC2Mission.THE_GROWING_SHADOW_T.mission_name,
@@ -12218,8 +12225,9 @@ def get_locations(world: Optional["SC2World"]) -> Tuple[LocationData, ...]:
LocationType.EXTRA,
lambda state: (
logic.terran_common_unit(state)
- and (adv_tactics or logic.terran_moderate_anti_air(state))
+ and logic.terran_moderate_anti_air(state)
),
+ hard_rule=logic.terran_any_anti_air,
),
make_location_data(
SC2Mission.THE_GROWING_SHADOW_Z.mission_name,
diff --git a/worlds/sc2/mission_order/mission_pools.py b/worlds/sc2/mission_order/mission_pools.py
index a3ab99f461c4..1c95f4896a0f 100644
--- a/worlds/sc2/mission_order/mission_pools.py
+++ b/worlds/sc2/mission_order/mission_pools.py
@@ -56,18 +56,21 @@ class SC2MOGenMissionPools:
"""
master_list: Set[int]
difficulty_pools: Dict[Difficulty, Set[int]]
+ exclude_mission_variants_on_pull: bool
_used_flags: Dict[MissionFlag, int]
_used_missions: List[SC2Mission]
_updated_difficulties: Dict[int, Difficulty]
_flag_ratios: Dict[MissionFlag, float]
_flag_weights: Dict[MissionFlag, int]
+ _unexcluded_missions: Iterable[SC2Mission]
- def __init__(self) -> None:
+ def __init__(self, exclude_mission_variants_on_pull: bool) -> None:
self.master_list = {mission.id for mission in SC2Mission}
self.difficulty_pools = {
diff: {mission.id for mission in SC2Mission if mission.pool + 1 == diff}
for diff in Difficulty if diff != Difficulty.RELATIVE
}
+ self.exclude_mission_variants_on_pull = exclude_mission_variants_on_pull
self._used_flags = {}
self._used_missions = []
self._updated_difficulties = {}
@@ -78,10 +81,24 @@ def set_exclusions(self, excluded: Iterable[SC2Mission], unexcluded: Iterable[SC
"""Prevents all the missions that appear in the `excluded` list, but not in the `unexcluded` list,
from appearing in the mission order."""
total_exclusions = [mission.id for mission in excluded if mission not in unexcluded]
+ self._unexcluded_missions = unexcluded
self.master_list.difference_update(total_exclusions)
+ def exclude_mission(self, mission: SC2Mission) -> None:
+ """Excludes a single mission unless it is unexcluded."""
+ if not mission in self._unexcluded_missions:
+ self.master_list.remove(mission.id)
+ self.difficulty_pools[self.get_modified_mission_difficulty(mission)].remove(mission.id)
+
def get_allowed_mission_count(self) -> int:
- return len(self.master_list)
+ if self.exclude_mission_variants_on_pull:
+ used_files = set(
+ lookup_id_to_mission[mission].map_file
+ for mission in self.master_list
+ )
+ return len(used_files)
+ else:
+ return len(self.master_list)
def count_allowed_missions(self, campaign: SC2Campaign) -> int:
allowed_missions = [
@@ -192,6 +209,17 @@ def _add_mission_stats(self, mission: SC2Mission) -> None:
if flag & mission.flags == flag:
self._used_flags.setdefault(flag, 0)
self._used_flags[flag] += 1
+
+ # Exclude race swap variants
+ if self.exclude_mission_variants_on_pull and mission.flags & (MissionFlag.HasRaceSwap|MissionFlag.RaceSwap):
+ variants = [
+ other_mission
+ for other_mission in self.master_list
+ if lookup_id_to_mission[other_mission].map_file == mission.map_file and other_mission != mission.id
+ ]
+ for variant in variants:
+ self.exclude_mission(lookup_id_to_mission[variant])
+
self._used_missions.append(mission)
def pull_random_mission(self, world: World, slot: 'SC2MOGenMission', *, prefer_close_difficulty: bool = False) -> SC2Mission:
diff --git a/worlds/sc2/mission_order/options.py b/worlds/sc2/mission_order/options.py
index 84630ba13a95..aa1c6727c711 100644
--- a/worlds/sc2/mission_order/options.py
+++ b/worlds/sc2/mission_order/options.py
@@ -176,8 +176,7 @@ def __init__(self, yaml_value: Dict[str, Dict[str, Any]]) -> None:
# This function constructs self.value by parts,
# so the parent constructor isn't called
self.value: Dict[str, Dict[str, Any]] = {}
- if yaml_value == self.default: # If this option is default, it shouldn't mess with its own values
- yaml_value = copy.deepcopy(self.default)
+ yaml_value = copy.deepcopy(yaml_value) # Ensure that all the mutations are local to the world
for campaign in yaml_value:
self.value[campaign] = {}
diff --git a/worlds/sc2/mission_order/presets.py b/worlds/sc2/mission_order/presets.py
index 2910ca2433b9..2a3977eb1b5e 100644
--- a/worlds/sc2/mission_order/presets.py
+++ b/worlds/sc2/mission_order/presets.py
@@ -209,7 +209,7 @@
OPTION_NAME[ProgressionBalancing]: ProgressionBalancing.default,
OPTION_NAME[GameDifficulty]: GameDifficulty.option_normal,
OPTION_NAME[SelectedRaces]: SelectedRaces.valid_keys,
- OPTION_NAME[MissionOrder]: MissionOrder.option_blitz,
+ OPTION_NAME[MissionOrder]: MissionOrder.option_golden_path,
OPTION_NAME[RequiredTactics]: RequiredTactics.option_standard,
OPTION_NAME[EnabledCampaigns]: EnabledCampaigns.valid_keys,
OPTION_NAME[EnableRaceSwapVariants]: EnableRaceSwapVariants.option_pick_one,
@@ -331,12 +331,13 @@
OPTION_NAME[GameDifficulty]: GameDifficulty.option_brutal,
OPTION_NAME[SelectedRaces]: SelectedRaces.valid_keys,
OPTION_NAME[MissionOrder]: MissionOrder.option_grid,
- OPTION_NAME[RequiredTactics]: RequiredTactics.option_standard,
+ OPTION_NAME[RequiredTactics]: RequiredTactics.option_any_units,
OPTION_NAME[EnabledCampaigns]: EnabledCampaigns.valid_keys,
OPTION_NAME[EnableRaceSwapVariants]: EnableRaceSwapVariants.option_pick_one,
OPTION_NAME[EnableMissionRaceBalancing]: EnableMissionRaceBalancing.option_semi_balanced,
OPTION_NAME[KeyMode]: KeyMode.option_progressive_questlines,
OPTION_NAME[MaximumCampaignSize]: 35,
+ OPTION_NAME[TwoStartPositions]: TwoStartPositions.option_true,
OPTION_NAME[StarterUnit]: StarterUnit.option_off,
OPTION_NAME[EnableMorphling]: EnableMorphling.option_true,
OPTION_NAME[TakeOverAIAllies]: TakeOverAIAllies.option_false,
diff --git a/worlds/sc2/mission_tables.py b/worlds/sc2/mission_tables.py
index 3dfe50ff2b1a..6857d178f518 100644
--- a/worlds/sc2/mission_tables.py
+++ b/worlds/sc2/mission_tables.py
@@ -118,23 +118,23 @@ def get_short_name(self):
# Wings of Liberty
LIBERATION_DAY = 1, "Liberation Day", SC2Campaign.WOL, "Mar Sara", SC2Race.ANY, MissionPools.STARTER, "ap_liberation_day", MissionFlag.Terran|MissionFlag.NoBuild|MissionFlag.VsTerran
THE_OUTLAWS = 2, "The Outlaws (Terran)", SC2Campaign.WOL, "Mar Sara", SC2Race.TERRAN, MissionPools.EASY, "ap_the_outlaws", MissionFlag.Terran|MissionFlag.VsTerran|MissionFlag.HasRaceSwap
- ZERO_HOUR = 3, "Zero Hour (Terran)", SC2Campaign.WOL, "Mar Sara", SC2Race.TERRAN, MissionPools.EASY, "ap_zero_hour", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
- EVACUATION = 4, "Evacuation (Terran)", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.EASY, "ap_evacuation", MissionFlag.Terran|MissionFlag.AutoScroller|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
+ ZERO_HOUR = 3, "Zero Hour (Terran)", SC2Campaign.WOL, "Mar Sara", SC2Race.TERRAN, MissionPools.STARTER, "ap_zero_hour", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
+ EVACUATION = 4, "Evacuation (Terran)", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.STARTER, "ap_evacuation", MissionFlag.Terran|MissionFlag.AutoScroller|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
OUTBREAK = 5, "Outbreak (Terran)", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.EASY, "ap_outbreak", MissionFlag.Terran|MissionFlag.Defense|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
SAFE_HAVEN = 6, "Safe Haven (Terran)", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_safe_haven", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap
HAVENS_FALL = 7, "Haven's Fall (Terran)", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_havens_fall", MissionFlag.Terran|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
- SMASH_AND_GRAB = 8, "Smash and Grab (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.EASY, "ap_smash_and_grab", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.HasRaceSwap
+ SMASH_AND_GRAB = 8, "Smash and Grab (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.STARTER, "ap_smash_and_grab", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.HasRaceSwap
THE_DIG = 9, "The Dig (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_dig", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap
THE_MOEBIUS_FACTOR = 10, "The Moebius Factor (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_moebius_factor", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
SUPERNOVA = 11, "Supernova (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.HARD, "ap_supernova", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap
MAW_OF_THE_VOID = 12, "Maw of the Void (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.HARD, "ap_maw_of_the_void", MissionFlag.Terran|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap
- DEVILS_PLAYGROUND = 13, "Devil's Playground (Terran)", SC2Campaign.WOL, "Covert", SC2Race.TERRAN, MissionPools.EASY, "ap_devils_playground", MissionFlag.Terran|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
+ DEVILS_PLAYGROUND = 13, "Devil's Playground (Terran)", SC2Campaign.WOL, "Covert", SC2Race.TERRAN, MissionPools.STARTER, "ap_devils_playground", MissionFlag.Terran|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
WELCOME_TO_THE_JUNGLE = 14, "Welcome to the Jungle (Terran)", SC2Campaign.WOL, "Covert", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_welcome_to_the_jungle", MissionFlag.Terran|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap
BREAKOUT = 15, "Breakout", SC2Campaign.WOL, "Covert", SC2Race.ANY, MissionPools.STARTER, "ap_breakout", MissionFlag.Terran|MissionFlag.NoBuild|MissionFlag.VsTerran
GHOST_OF_A_CHANCE = 16, "Ghost of a Chance", SC2Campaign.WOL, "Covert", SC2Race.ANY, MissionPools.STARTER, "ap_ghost_of_a_chance", MissionFlag.Terran|MissionFlag.NoBuild|MissionFlag.VsTerran|MissionFlag.WoLNova
THE_GREAT_TRAIN_ROBBERY = 17, "The Great Train Robbery (Terran)", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.EASY, "ap_the_great_train_robbery", MissionFlag.Terran|MissionFlag.AutoScroller|MissionFlag.VsTerran|MissionFlag.HasRaceSwap
- CUTTHROAT = 18, "Cutthroat (Terran)", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_cutthroat", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsTerran|MissionFlag.HasRaceSwap
- ENGINE_OF_DESTRUCTION = 19, "Engine of Destruction (Terran)", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.HARD, "ap_engine_of_destruction", MissionFlag.Terran|MissionFlag.AutoScroller|MissionFlag.VsTerran|MissionFlag.HasRaceSwap
+ CUTTHROAT = 18, "Cutthroat (Terran)", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.EASY, "ap_cutthroat", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsTerran|MissionFlag.HasRaceSwap
+ ENGINE_OF_DESTRUCTION = 19, "Engine of Destruction (Terran)", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_engine_of_destruction", MissionFlag.Terran|MissionFlag.AutoScroller|MissionFlag.VsTerran|MissionFlag.HasRaceSwap
MEDIA_BLITZ = 20, "Media Blitz (Terran)", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_media_blitz", MissionFlag.Terran|MissionFlag.VsTerran|MissionFlag.HasRaceSwap
PIERCING_OF_THE_SHROUD = 21, "Piercing the Shroud", SC2Campaign.WOL, "Rebellion", SC2Race.TERRAN, MissionPools.STARTER, "ap_piercing_the_shroud", MissionFlag.Terran|MissionFlag.NoBuild|MissionFlag.VsAll
GATES_OF_HELL = 26, "Gates of Hell (Terran)", SC2Campaign.WOL, "Char", SC2Race.TERRAN, MissionPools.HARD, "ap_gates_of_hell", MissionFlag.Terran|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
@@ -145,7 +145,7 @@ def get_short_name(self):
# Prophecy
WHISPERS_OF_DOOM = 22, "Whispers of Doom", SC2Campaign.PROPHECY, "_1", SC2Race.ANY, MissionPools.STARTER, "ap_whispers_of_doom", MissionFlag.Protoss|MissionFlag.NoBuild|MissionFlag.VsZerg
A_SINISTER_TURN = 23, "A Sinister Turn (Protoss)", SC2Campaign.PROPHECY, "_2", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_a_sinister_turn", MissionFlag.Protoss|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap
- ECHOES_OF_THE_FUTURE = 24, "Echoes of the Future (Protoss)", SC2Campaign.PROPHECY, "_3", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_echoes_of_the_future", MissionFlag.Protoss|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
+ ECHOES_OF_THE_FUTURE = 24, "Echoes of the Future (Protoss)", SC2Campaign.PROPHECY, "_3", SC2Race.PROTOSS, MissionPools.EASY, "ap_echoes_of_the_future", MissionFlag.Protoss|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
IN_UTTER_DARKNESS = 25, "In Utter Darkness (Protoss)", SC2Campaign.PROPHECY, "_4", SC2Race.PROTOSS, MissionPools.HARD, "ap_in_utter_darkness", MissionFlag.Protoss|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.HasRaceSwap
# Heart of the Swarm
@@ -177,7 +177,7 @@ def get_short_name(self):
# LotV
FOR_AIUR = 53, "For Aiur!", SC2Campaign.LOTV, "Aiur", SC2Race.ANY, MissionPools.STARTER, "ap_for_aiur", MissionFlag.Protoss|MissionFlag.NoBuild|MissionFlag.VsZerg
- THE_GROWING_SHADOW = 54, "The Growing Shadow (Protoss)", SC2Campaign.LOTV, "Aiur", SC2Race.PROTOSS, MissionPools.EASY, "ap_the_growing_shadow", MissionFlag.Protoss|MissionFlag.VsPZ|MissionFlag.HasRaceSwap
+ THE_GROWING_SHADOW = 54, "The Growing Shadow (Protoss)", SC2Campaign.LOTV, "Aiur", SC2Race.PROTOSS, MissionPools.STARTER, "ap_the_growing_shadow", MissionFlag.Protoss|MissionFlag.VsPZ|MissionFlag.HasRaceSwap
THE_SPEAR_OF_ADUN = 55, "The Spear of Adun (Protoss)", SC2Campaign.LOTV, "Aiur", SC2Race.PROTOSS, MissionPools.EASY, "ap_the_spear_of_adun", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.VsPZ|MissionFlag.HasRaceSwap
SKY_SHIELD = 56, "Sky Shield (Protoss)", SC2Campaign.LOTV, "Korhal", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_sky_shield", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.Countdown|MissionFlag.VsTerran|MissionFlag.AiTerranAlly|MissionFlag.HasRaceSwap
BROTHERS_IN_ARMS = 57, "Brothers in Arms (Protoss)", SC2Campaign.LOTV, "Korhal", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_brothers_in_arms", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.VsTerran|MissionFlag.AiTerranAlly|MissionFlag.HasRaceSwap
@@ -202,13 +202,13 @@ def get_short_name(self):
AMON_S_FALL = 74, "Amon's Fall", SC2Campaign.EPILOGUE, "_3", SC2Race.ZERG, MissionPools.VERY_HARD, "ap_amon_s_fall", MissionFlag.Zerg|MissionFlag.AutoScroller|MissionFlag.VsAll|MissionFlag.AiTerranAlly|MissionFlag.AiProtossAlly
# Nova Covert Ops
- THE_ESCAPE = 75, "The Escape", SC2Campaign.NCO, "_1", SC2Race.ANY, MissionPools.MEDIUM, "ap_the_escape", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.NoBuild|MissionFlag.VsTerran
+ THE_ESCAPE = 75, "The Escape", SC2Campaign.NCO, "_1", SC2Race.ANY, MissionPools.EASY, "ap_the_escape", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.NoBuild|MissionFlag.VsTerran
SUDDEN_STRIKE = 76, "Sudden Strike", SC2Campaign.NCO, "_1", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_sudden_strike", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.TimedDefense|MissionFlag.VsZerg
ENEMY_INTELLIGENCE = 77, "Enemy Intelligence", SC2Campaign.NCO, "_1", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_enemy_intelligence", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.Defense|MissionFlag.VsZerg
TROUBLE_IN_PARADISE = 78, "Trouble In Paradise", SC2Campaign.NCO, "_2", SC2Race.TERRAN, MissionPools.HARD, "ap_trouble_in_paradise", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.Countdown|MissionFlag.VsPZ
NIGHT_TERRORS = 79, "Night Terrors", SC2Campaign.NCO, "_2", SC2Race.TERRAN, MissionPools.HARD, "ap_night_terrors", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.VsPZ
FLASHPOINT = 80, "Flashpoint", SC2Campaign.NCO, "_2", SC2Race.TERRAN, MissionPools.HARD, "ap_flashpoint", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.VsZerg
- IN_THE_ENEMY_S_SHADOW = 81, "In the Enemy's Shadow", SC2Campaign.NCO, "_3", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_in_the_enemy_s_shadow", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.NoBuild|MissionFlag.VsTerran
+ IN_THE_ENEMY_S_SHADOW = 81, "In the Enemy's Shadow", SC2Campaign.NCO, "_3", SC2Race.TERRAN, MissionPools.EASY, "ap_in_the_enemy_s_shadow", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.NoBuild|MissionFlag.VsTerran
DARK_SKIES = 82, "Dark Skies", SC2Campaign.NCO, "_3", SC2Race.TERRAN, MissionPools.HARD, "ap_dark_skies", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.TimedDefense|MissionFlag.VsProtoss
END_GAME = 83, "End Game", SC2Campaign.NCO, "_3", SC2Race.TERRAN, MissionPools.VERY_HARD, "ap_end_game", MissionFlag.Terran|MissionFlag.Nova|MissionFlag.Defense|MissionFlag.VsTerran
@@ -218,10 +218,10 @@ def get_short_name(self):
THE_OUTLAWS_P = 87, "The Outlaws (Protoss)", SC2Campaign.WOL, "Mar Sara", SC2Race.PROTOSS, MissionPools.EASY, "ap_the_outlaws", MissionFlag.Protoss|MissionFlag.VsTerran|MissionFlag.RaceSwap
ZERO_HOUR_Z = 88, "Zero Hour (Zerg)", SC2Campaign.WOL, "Mar Sara", SC2Race.ZERG, MissionPools.MEDIUM, "ap_zero_hour", MissionFlag.Zerg|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.RaceSwap
ZERO_HOUR_P = 89, "Zero Hour (Protoss)", SC2Campaign.WOL, "Mar Sara", SC2Race.PROTOSS, MissionPools.EASY, "ap_zero_hour", MissionFlag.Protoss|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.RaceSwap
- EVACUATION_Z = 90, "Evacuation (Zerg)", SC2Campaign.WOL, "Colonist", SC2Race.ZERG, MissionPools.EASY, "ap_evacuation", MissionFlag.Zerg|MissionFlag.AutoScroller|MissionFlag.VsZerg|MissionFlag.RaceSwap
- EVACUATION_P = 91, "Evacuation (Protoss)", SC2Campaign.WOL, "Colonist", SC2Race.PROTOSS, MissionPools.EASY, "ap_evacuation", MissionFlag.Protoss|MissionFlag.AutoScroller|MissionFlag.VsZerg|MissionFlag.RaceSwap
+ EVACUATION_Z = 90, "Evacuation (Zerg)", SC2Campaign.WOL, "Colonist", SC2Race.ZERG, MissionPools.STARTER, "ap_evacuation", MissionFlag.Zerg|MissionFlag.AutoScroller|MissionFlag.VsZerg|MissionFlag.RaceSwap
+ EVACUATION_P = 91, "Evacuation (Protoss)", SC2Campaign.WOL, "Colonist", SC2Race.PROTOSS, MissionPools.STARTER, "ap_evacuation", MissionFlag.Protoss|MissionFlag.AutoScroller|MissionFlag.VsZerg|MissionFlag.RaceSwap
OUTBREAK_Z = 92, "Outbreak (Zerg)", SC2Campaign.WOL, "Colonist", SC2Race.ZERG, MissionPools.MEDIUM, "ap_outbreak", MissionFlag.Zerg|MissionFlag.Defense|MissionFlag.VsZerg|MissionFlag.RaceSwap
- OUTBREAK_P = 93, "Outbreak (Protoss)", SC2Campaign.WOL, "Colonist", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_outbreak", MissionFlag.Protoss|MissionFlag.Defense|MissionFlag.VsZerg|MissionFlag.RaceSwap
+ OUTBREAK_P = 93, "Outbreak (Protoss)", SC2Campaign.WOL, "Colonist", SC2Race.PROTOSS, MissionPools.EASY, "ap_outbreak", MissionFlag.Protoss|MissionFlag.Defense|MissionFlag.VsZerg|MissionFlag.RaceSwap
SAFE_HAVEN_Z = 94, "Safe Haven (Zerg)", SC2Campaign.WOL, "Colonist", SC2Race.ZERG, MissionPools.MEDIUM, "ap_safe_haven", MissionFlag.Zerg|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.RaceSwap
SAFE_HAVEN_P = 95, "Safe Haven (Protoss)", SC2Campaign.WOL, "Colonist", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_safe_haven", MissionFlag.Protoss|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.RaceSwap
HAVENS_FALL_Z = 96, "Haven's Fall (Zerg)", SC2Campaign.WOL, "Colonist", SC2Race.ZERG, MissionPools.MEDIUM, "ap_havens_fall", MissionFlag.Zerg|MissionFlag.VsZerg|MissionFlag.RaceSwap
@@ -236,16 +236,16 @@ def get_short_name(self):
SUPERNOVA_P = 105, "Supernova (Protoss)", SC2Campaign.WOL, "Artifact", SC2Race.PROTOSS, MissionPools.HARD, "ap_supernova", MissionFlag.Protoss|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.RaceSwap
MAW_OF_THE_VOID_Z = 106, "Maw of the Void (Zerg)", SC2Campaign.WOL, "Artifact", SC2Race.ZERG, MissionPools.HARD, "ap_maw_of_the_void", MissionFlag.Zerg|MissionFlag.VsProtoss|MissionFlag.RaceSwap
MAW_OF_THE_VOID_P = 107, "Maw of the Void (Protoss)", SC2Campaign.WOL, "Artifact", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_maw_of_the_void", MissionFlag.Protoss|MissionFlag.VsProtoss|MissionFlag.RaceSwap
- DEVILS_PLAYGROUND_Z = 108, "Devil's Playground (Zerg)", SC2Campaign.WOL, "Covert", SC2Race.ZERG, MissionPools.EASY, "ap_devils_playground", MissionFlag.Zerg|MissionFlag.VsZerg|MissionFlag.RaceSwap
- DEVILS_PLAYGROUND_P = 109, "Devil's Playground (Protoss)", SC2Campaign.WOL, "Covert", SC2Race.PROTOSS, MissionPools.EASY, "ap_devils_playground", MissionFlag.Protoss|MissionFlag.VsZerg|MissionFlag.RaceSwap
+ DEVILS_PLAYGROUND_Z = 108, "Devil's Playground (Zerg)", SC2Campaign.WOL, "Covert", SC2Race.ZERG, MissionPools.STARTER, "ap_devils_playground", MissionFlag.Zerg|MissionFlag.VsZerg|MissionFlag.RaceSwap
+ DEVILS_PLAYGROUND_P = 109, "Devil's Playground (Protoss)", SC2Campaign.WOL, "Covert", SC2Race.PROTOSS, MissionPools.STARTER, "ap_devils_playground", MissionFlag.Protoss|MissionFlag.VsZerg|MissionFlag.RaceSwap
WELCOME_TO_THE_JUNGLE_Z = 110, "Welcome to the Jungle (Zerg)", SC2Campaign.WOL, "Covert", SC2Race.ZERG, MissionPools.HARD, "ap_welcome_to_the_jungle", MissionFlag.Zerg|MissionFlag.VsProtoss|MissionFlag.RaceSwap
WELCOME_TO_THE_JUNGLE_P = 111, "Welcome to the Jungle (Protoss)", SC2Campaign.WOL, "Covert", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_welcome_to_the_jungle", MissionFlag.Protoss|MissionFlag.VsProtoss|MissionFlag.RaceSwap
# 112/113 - Breakout
# 114/115 - Ghost of a Chance
THE_GREAT_TRAIN_ROBBERY_Z = 116, "The Great Train Robbery (Zerg)", SC2Campaign.WOL, "Rebellion", SC2Race.ZERG, MissionPools.EASY, "ap_the_great_train_robbery", MissionFlag.Zerg|MissionFlag.AutoScroller|MissionFlag.VsTerran|MissionFlag.RaceSwap
THE_GREAT_TRAIN_ROBBERY_P = 117, "The Great Train Robbery (Protoss)", SC2Campaign.WOL, "Rebellion", SC2Race.PROTOSS, MissionPools.EASY, "ap_the_great_train_robbery", MissionFlag.Protoss|MissionFlag.AutoScroller|MissionFlag.VsTerran|MissionFlag.RaceSwap
- CUTTHROAT_Z = 118, "Cutthroat (Zerg)", SC2Campaign.WOL, "Rebellion", SC2Race.ZERG, MissionPools.MEDIUM, "ap_cutthroat", MissionFlag.Zerg|MissionFlag.Countdown|MissionFlag.VsTerran|MissionFlag.RaceSwap
- CUTTHROAT_P = 119, "Cutthroat (Protoss)", SC2Campaign.WOL, "Rebellion", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_cutthroat", MissionFlag.Protoss|MissionFlag.Countdown|MissionFlag.VsTerran|MissionFlag.RaceSwap
+ CUTTHROAT_Z = 118, "Cutthroat (Zerg)", SC2Campaign.WOL, "Rebellion", SC2Race.ZERG, MissionPools.EASY, "ap_cutthroat", MissionFlag.Zerg|MissionFlag.Countdown|MissionFlag.VsTerran|MissionFlag.RaceSwap
+ CUTTHROAT_P = 119, "Cutthroat (Protoss)", SC2Campaign.WOL, "Rebellion", SC2Race.PROTOSS, MissionPools.EASY, "ap_cutthroat", MissionFlag.Protoss|MissionFlag.Countdown|MissionFlag.VsTerran|MissionFlag.RaceSwap
ENGINE_OF_DESTRUCTION_Z = 120, "Engine of Destruction (Zerg)", SC2Campaign.WOL, "Rebellion", SC2Race.ZERG, MissionPools.HARD, "ap_engine_of_destruction", MissionFlag.Zerg|MissionFlag.AutoScroller|MissionFlag.VsTerran|MissionFlag.RaceSwap
ENGINE_OF_DESTRUCTION_P = 121, "Engine of Destruction (Protoss)", SC2Campaign.WOL, "Rebellion", SC2Race.PROTOSS, MissionPools.HARD, "ap_engine_of_destruction", MissionFlag.Protoss|MissionFlag.AutoScroller|MissionFlag.VsTerran|MissionFlag.RaceSwap
MEDIA_BLITZ_Z = 122, "Media Blitz (Zerg)", SC2Campaign.WOL, "Rebellion", SC2Race.ZERG, MissionPools.HARD, "ap_media_blitz", MissionFlag.Zerg|MissionFlag.VsTerran|MissionFlag.RaceSwap
@@ -254,8 +254,8 @@ def get_short_name(self):
# 126/127 - Whispers of Doom
A_SINISTER_TURN_T = 128, "A Sinister Turn (Terran)", SC2Campaign.PROPHECY, "_2", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_a_sinister_turn", MissionFlag.Terran|MissionFlag.VsProtoss|MissionFlag.RaceSwap
A_SINISTER_TURN_Z = 129, "A Sinister Turn (Zerg)", SC2Campaign.PROPHECY, "_2", SC2Race.ZERG, MissionPools.MEDIUM, "ap_a_sinister_turn", MissionFlag.Zerg|MissionFlag.VsProtoss|MissionFlag.RaceSwap
- ECHOES_OF_THE_FUTURE_T = 130, "Echoes of the Future (Terran)", SC2Campaign.PROPHECY, "_3", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_echoes_of_the_future", MissionFlag.Terran|MissionFlag.VsZerg|MissionFlag.RaceSwap
- ECHOES_OF_THE_FUTURE_Z = 131, "Echoes of the Future (Zerg)", SC2Campaign.PROPHECY, "_3", SC2Race.ZERG, MissionPools.MEDIUM, "ap_echoes_of_the_future", MissionFlag.Zerg|MissionFlag.VsZerg|MissionFlag.RaceSwap
+ ECHOES_OF_THE_FUTURE_T = 130, "Echoes of the Future (Terran)", SC2Campaign.PROPHECY, "_3", SC2Race.TERRAN, MissionPools.EASY, "ap_echoes_of_the_future", MissionFlag.Terran|MissionFlag.VsZerg|MissionFlag.RaceSwap
+ ECHOES_OF_THE_FUTURE_Z = 131, "Echoes of the Future (Zerg)", SC2Campaign.PROPHECY, "_3", SC2Race.ZERG, MissionPools.EASY, "ap_echoes_of_the_future", MissionFlag.Zerg|MissionFlag.VsZerg|MissionFlag.RaceSwap
IN_UTTER_DARKNESS_T = 132, "In Utter Darkness (Terran)", SC2Campaign.PROPHECY, "_4", SC2Race.TERRAN, MissionPools.HARD, "ap_in_utter_darkness", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.RaceSwap
IN_UTTER_DARKNESS_Z = 133, "In Utter Darkness (Zerg)", SC2Campaign.PROPHECY, "_4", SC2Race.ZERG, MissionPools.HARD, "ap_in_utter_darkness", MissionFlag.Zerg|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.RaceSwap
GATES_OF_HELL_Z = 134, "Gates of Hell (Zerg)", SC2Campaign.WOL, "Char", SC2Race.ZERG, MissionPools.HARD, "ap_gates_of_hell", MissionFlag.Zerg|MissionFlag.VsZerg|MissionFlag.RaceSwap
diff --git a/worlds/sc2/options.py b/worlds/sc2/options.py
index 1df082fea4b7..63f9af94f556 100644
--- a/worlds/sc2/options.py
+++ b/worlds/sc2/options.py
@@ -5,14 +5,13 @@
from Options import (
Choice, Toggle, DefaultOnToggle, OptionSet, Range,
- PerGameCommonOptions, Option, VerifyKeys, StartInventory,
+ PerGameCommonOptions, VerifyKeys, StartInventory,
is_iterable_except_str, OptionGroup, Visibility, ItemDict,
- Accessibility, ProgressionBalancing
+ OptionCounter,
)
from Utils import get_fuzzy_results
from BaseClasses import PlandoOptions
-from .item import item_names, item_tables
-from .item.item_groups import kerrigan_active_abilities, kerrigan_passives, nova_weapons, nova_gadgets
+from .item import item_names, item_tables, item_groups
from .mission_tables import (
SC2Campaign, SC2Mission, lookup_name_to_mission, MissionPools, get_missions_with_any_flags_in_list,
campaign_mission_table, SC2Race, MissionFlag
@@ -700,7 +699,7 @@ class KerriganMaxActiveAbilities(Range):
"""
display_name = "Kerrigan Maximum Active Abilities"
range_start = 0
- range_end = len(kerrigan_active_abilities)
+ range_end = len(item_groups.kerrigan_active_abilities)
default = range_end
@@ -711,7 +710,7 @@ class KerriganMaxPassiveAbilities(Range):
"""
display_name = "Kerrigan Maximum Passive Abilities"
range_start = 0
- range_end = len(kerrigan_passives)
+ range_end = len(item_groups.kerrigan_passives)
default = range_end
@@ -829,7 +828,7 @@ class SpearOfAdunMaxAutocastAbilities(Range):
"""
display_name = "Spear of Adun Maximum Passive Abilities"
range_start = 0
- range_end = sum(item.quantity for item_name, item in item_tables.get_full_item_list().items() if item_name in item_tables.spear_of_adun_castable_passives)
+ range_end = sum(item_tables.item_table[item_name].quantity for item_name in item_groups.spear_of_adun_passives)
default = range_end
@@ -883,7 +882,7 @@ class NovaMaxWeapons(Range):
"""
display_name = "Nova Maximum Weapons"
range_start = 0
- range_end = len(nova_weapons)
+ range_end = len(item_groups.nova_weapons)
default = range_end
@@ -897,7 +896,7 @@ class NovaMaxGadgets(Range):
"""
display_name = "Nova Maximum Gadgets"
range_start = 0
- range_end = len(nova_gadgets)
+ range_end = len(item_groups.nova_gadgets)
default = range_end
@@ -932,33 +931,48 @@ class TakeOverAIAllies(Toggle):
display_name = "Take Over AI Allies"
-class Sc2ItemDict(Option[Dict[str, int]], VerifyKeys, Mapping[str, int]):
- """A branch of ItemDict that supports item counts of 0"""
+class Sc2ItemDict(OptionCounter, VerifyKeys, Mapping[str, int]):
+ """A branch of ItemDict that supports negative item counts"""
default = {}
supports_weighting = False
verify_item_name = True
# convert_name_groups = True
display_name = 'Unnamed dictionary'
- minimum_value: int = 0
+ # Note(phaneros): Limiting minimum to -1 means that if two triggers add -1 to the same item,
+ # the validation fails. So give trigger people space to stack a bunch of triggers.
+ min: int = -1000
+ max: int = 1000
+ valid_keys = set(item_tables.item_table) | set(item_groups.item_name_groups)
- def __init__(self, value: Dict[str, int]):
+ def __init__(self, value: dict[str, int]):
self.value = {key: val for key, val in value.items()}
@classmethod
- def from_any(cls, data: Union[List[str], Dict[str, int]]) -> 'Sc2ItemDict':
+ def from_any(cls, data: list[str] | dict[str, int]) -> 'Sc2ItemDict':
if isinstance(data, list):
- # This is a little default that gets us backwards compatibility with lists.
- # It doesn't play nice with trigger merging dicts and lists together, though, so best not to advertise it overmuch.
- data = {item: 0 for item in data}
+ raise ValueError(
+ f"{cls.display_name}: Cannot convert from list. "
+ f"Use dict syntax (no dashes, 'value: number' synax)."
+ )
if isinstance(data, dict):
for key, value in data.items():
if not isinstance(value, int):
- raise ValueError(f"Invalid type in '{cls.display_name}': element '{key}' maps to '{value}', expected an integer")
- if value < cls.minimum_value:
- raise ValueError(f"Invalid value for '{cls.display_name}': element '{key}' maps to {value}, which is less than the minimum ({cls.minimum_value})")
+ raise ValueError(
+ f"Invalid type in '{cls.display_name}': "
+ f"element '{key}' maps to '{value}', expected an integer"
+ )
+ if value < cls.min:
+ raise ValueError(
+ f"Invalid value for '{cls.display_name}': "
+ f"element '{key}' maps to {value}, which is less than the minimum ({cls.min})"
+ )
+ if value > cls.max:
+ raise ValueError(f"Invalid value for '{cls.display_name}': "
+ f"element '{key}' maps to {value}, which is greater than the maximum ({cls.max})"
+ )
return cls(data)
else:
- raise NotImplementedError(f"Cannot Convert from non-dictionary, got {type(data)}")
+ raise NotImplementedError(f"{cls.display_name}: Cannot convert from non-dictionary, got {type(data)}")
def verify(self, world: Type['World'], player_name: str, plando_options: PlandoOptions) -> None:
"""Overridden version of function from Options.VerifyKeys for a better error message"""
@@ -974,15 +988,16 @@ def verify(self, world: Type['World'], player_name: str, plando_options: PlandoO
self.value = new_value
for item_name in self.value:
if item_name not in world.item_names:
- from .item import item_groups
picks = get_fuzzy_results(
item_name,
list(world.item_names) + list(item_groups.ItemGroupNames.get_all_group_names()),
limit=1,
)
- raise Exception(f"Item {item_name} from option {self} "
- f"is not a valid item name from {world.game}. "
- f"Did you mean '{picks[0][0]}' ({picks[0][1]}% sure)")
+ raise Exception(
+ f"Item {item_name} from option {self} "
+ f"is not a valid item name from {world.game}. "
+ f"Did you mean '{picks[0][0]}' ({picks[0][1]}% sure)"
+ )
def get_option_name(self, value):
return ", ".join(f"{key}: {v}" for key, v in value.items())
@@ -998,25 +1013,25 @@ def __len__(self) -> int:
class Sc2StartInventory(Sc2ItemDict):
- """Start with these items."""
+ """Start with these items. Use an amount of -1 to start with all copies of an item."""
display_name = StartInventory.display_name
class LockedItems(Sc2ItemDict):
"""Guarantees that these items will be unlockable, in the amount specified.
- Specify an amount of 0 to lock all copies of an item."""
+ Specify an amount of -1 to lock all copies of an item."""
display_name = "Locked Items"
class ExcludedItems(Sc2ItemDict):
"""Guarantees that these items will not be unlockable, in the amount specified.
- Specify an amount of 0 to exclude all copies of an item."""
+ Specify an amount of -1 to exclude all copies of an item."""
display_name = "Excluded Items"
class UnexcludedItems(Sc2ItemDict):
"""Undoes an item exclusion; useful for whitelisting or fine-tuning a category.
- Specify an amount of 0 to unexclude all copies of an item."""
+ Specify an amount of -1 to unexclude all copies of an item."""
display_name = "Unexcluded Items"
@@ -1294,7 +1309,7 @@ class MaximumSupplyReductionPerItem(Range):
class LowestMaximumSupply(Range):
"""Controls how far max supply reduction traps can reduce maximum supply."""
display_name = "Lowest Maximum Supply"
- range_start = 100
+ range_start = 50
range_end = 200
default = 180
@@ -1640,21 +1655,8 @@ def get_excluded_missions(world: 'SC2World') -> Set[SC2Mission]:
# Omitting missions not in enabled campaigns
for campaign in disabled_campaigns:
excluded_missions = excluded_missions.union(campaign_mission_table[campaign])
- # Omitting unwanted mission variants
- if world.options.enable_race_swap.value in [EnableRaceSwapVariants.option_pick_one, EnableRaceSwapVariants.option_pick_one_non_vanilla]:
- swaps = [
- mission for mission in SC2Mission
- if mission not in excluded_missions
- and mission.flags & (MissionFlag.HasRaceSwap|MissionFlag.RaceSwap)
- ]
- while len(swaps) > 0:
- curr = swaps[0]
- variants = [mission for mission in swaps if mission.map_file == curr.map_file]
- variants.sort(key=lambda mission: mission.id)
- swaps = [mission for mission in swaps if mission not in variants]
- if len(variants) > 1:
- variants.pop(world.random.randint(0, len(variants)-1))
- excluded_missions = excluded_missions.union(variants)
+
+ # Exclusions for race_swap: pick_one are handled during mission order generation
return excluded_missions
diff --git a/worlds/sc2/pool_filter.py b/worlds/sc2/pool_filter.py
index 31e47934ee9d..0ffa08e010c2 100644
--- a/worlds/sc2/pool_filter.py
+++ b/worlds/sc2/pool_filter.py
@@ -3,8 +3,7 @@
from BaseClasses import Location, ItemClassification
from .item import StarcraftItem, ItemFilterFlags, item_names, item_parents, item_groups
-from .item.item_tables import item_table, TerranItemType, ZergItemType, spear_of_adun_calldowns, \
- spear_of_adun_castable_passives
+from .item.item_tables import item_table, TerranItemType, ZergItemType, spear_of_adun_calldowns
from .options import RequiredTactics
if TYPE_CHECKING:
@@ -272,7 +271,7 @@ def request_minimum_items(group: List[StarcraftItem], requested_minimum) -> None
self.world.random.shuffle(spear_of_adun_actives)
cull_items_over_maximum(spear_of_adun_actives, self.world.options.spear_of_adun_max_active_abilities.value)
- spear_of_adun_autocasts = [item for item in inventory if item.name in spear_of_adun_castable_passives]
+ spear_of_adun_autocasts = [item for item in inventory if item.name in item_groups.spear_of_adun_passives]
self.world.random.shuffle(spear_of_adun_autocasts)
cull_items_over_maximum(spear_of_adun_autocasts, self.world.options.spear_of_adun_max_passive_abilities.value)
diff --git a/worlds/sc2/regions.py b/worlds/sc2/regions.py
index 26d127a1c5e9..4b02d294d1ba 100644
--- a/worlds/sc2/regions.py
+++ b/worlds/sc2/regions.py
@@ -36,7 +36,8 @@ def create_mission_order(
# whenever the event location becomes accessible
# Set up mission pools
- mission_pools = SC2MOGenMissionPools()
+ race_swap_pick_one = world.options.enable_race_swap.value in [EnableRaceSwapVariants.option_pick_one, EnableRaceSwapVariants.option_pick_one_non_vanilla]
+ mission_pools = SC2MOGenMissionPools(race_swap_pick_one)
mission_pools.set_exclusions(get_excluded_missions(world), []) # TODO set unexcluded
adjust_mission_pools(world, mission_pools)
setup_mission_pool_balancing(world, mission_pools)
@@ -73,89 +74,78 @@ def adjust_mission_pools(world: 'SC2World', pools: SC2MOGenMissionPools):
# Mission pool changes
mission_order_type = world.options.mission_order.value
enabled_campaigns = get_enabled_campaigns(world)
- adv_tactics = world.options.required_tactics.value != RequiredTactics.option_standard
- shuffle_no_build = world.options.shuffle_no_build.value
- extra_locations = world.options.extra_locations.value
grant_story_tech = world.options.grant_story_tech.value
grant_story_levels = world.options.grant_story_levels.value
war_council_nerfs = world.options.war_council_nerfs.value == WarCouncilNerfs.option_true
+ kerriganless = (
+ world.options.kerrigan_presence.value not in kerrigan_unit_available
+ or SC2Campaign.HOTS not in enabled_campaigns
+ )
+
+ # General changes for standard tactics
+ if world.options.required_tactics.value == RequiredTactics.option_standard:
+ pools.move_mission(SC2Mission.SMASH_AND_GRAB, Difficulty.STARTER, Difficulty.EASY)
- # WoL
- if shuffle_no_build == ShuffleNoBuild.option_false or adv_tactics:
- # Replacing No Build missions with Easy missions
- # WoL
- pools.move_mission(SC2Mission.ZERO_HOUR, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.EVACUATION, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.EVACUATION_Z, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.EVACUATION_P, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.DEVILS_PLAYGROUND, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.DEVILS_PLAYGROUND_Z, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.DEVILS_PLAYGROUND_P, Difficulty.EASY, Difficulty.STARTER)
- if world.options.required_tactics != RequiredTactics.option_any_units:
- # Per playtester feedback: doing this mission with only one unit is flaky
- # but there are enough viable comps that >= 2 random units is probably workable
- pools.move_mission(SC2Mission.THE_GREAT_TRAIN_ROBBERY, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.THE_GREAT_TRAIN_ROBBERY_Z, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.THE_GREAT_TRAIN_ROBBERY_P, Difficulty.EASY, Difficulty.STARTER)
- # LotV
- pools.move_mission(SC2Mission.THE_GROWING_SHADOW, Difficulty.EASY, Difficulty.STARTER)
- if shuffle_no_build == ShuffleNoBuild.option_false:
- # Pushing Outbreak to Normal, as it cannot be placed as the second mission on Build-Only
- pools.move_mission(SC2Mission.OUTBREAK, Difficulty.EASY, Difficulty.MEDIUM)
- # Pushing extra Normal missions to Easy
- pools.move_mission(SC2Mission.ECHOES_OF_THE_FUTURE, Difficulty.MEDIUM, Difficulty.EASY)
- pools.move_mission(SC2Mission.CUTTHROAT, Difficulty.MEDIUM, Difficulty.EASY)
- # Additional changes on Advanced Tactics
- if adv_tactics:
+ if world.options.shuffle_no_build.value == ShuffleNoBuild.option_false:
# WoL
- pools.move_mission(SC2Mission.SMASH_AND_GRAB, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.THE_MOEBIUS_FACTOR, Difficulty.MEDIUM, Difficulty.EASY)
- pools.move_mission(SC2Mission.THE_MOEBIUS_FACTOR_Z, Difficulty.MEDIUM, Difficulty.EASY)
- pools.move_mission(SC2Mission.THE_MOEBIUS_FACTOR_P, Difficulty.MEDIUM, Difficulty.EASY)
- pools.move_mission(SC2Mission.WELCOME_TO_THE_JUNGLE, Difficulty.MEDIUM, Difficulty.EASY)
- pools.move_mission(SC2Mission.ENGINE_OF_DESTRUCTION, Difficulty.HARD, Difficulty.MEDIUM)
- # Prophecy needs to be adjusted if by itself
+ pools.move_mission(SC2Mission.ZERO_HOUR, Difficulty.STARTER, Difficulty.EASY)
+ pools.move_mission(SC2Mission.EVACUATION, Difficulty.STARTER, Difficulty.EASY)
+ pools.move_mission(SC2Mission.EVACUATION_Z, Difficulty.STARTER, Difficulty.EASY)
+ pools.move_mission(SC2Mission.EVACUATION_P, Difficulty.STARTER, Difficulty.EASY)
+ pools.move_mission(SC2Mission.DEVILS_PLAYGROUND, Difficulty.STARTER, Difficulty.EASY)
+ pools.move_mission(SC2Mission.DEVILS_PLAYGROUND_Z, Difficulty.STARTER, Difficulty.EASY)
+ pools.move_mission(SC2Mission.DEVILS_PLAYGROUND_P, Difficulty.STARTER, Difficulty.EASY)
+
+ # LotV
+ pools.move_mission(SC2Mission.THE_GROWING_SHADOW, Difficulty.STARTER, Difficulty.EASY)
+
+ # Mission-specific adjustments for particular options
+ # Cement Prophecy's mission order if it is the only campaign
if enabled_campaigns == {SC2Campaign.PROPHECY}:
pools.move_mission(SC2Mission.A_SINISTER_TURN, Difficulty.MEDIUM, Difficulty.EASY)
- # Prologue's only valid starter is the goal mission
- if enabled_campaigns == {SC2Campaign.PROLOGUE} \
- or mission_order_type in static_mission_orders \
- and world.options.shuffle_campaigns.value == ShuffleCampaigns.option_false:
+
+ # Don't start on Ghost of a Chance if it will require Nova items
+ if (grant_story_tech != GrantStoryTech.option_grant
+ and (
+ world.options.nova_ghost_of_a_chance_variant == NovaGhostOfAChanceVariant.option_nco
+ or (
+ SC2Campaign.NCO in enabled_campaigns
+ and world.options.nova_ghost_of_a_chance_variant.value == NovaGhostOfAChanceVariant.option_auto
+ )
+ )
+ ):
+ # Using NCO tech for this mission that must be acquired
+ pools.move_mission(SC2Mission.GHOST_OF_A_CHANCE, Difficulty.STARTER, Difficulty.MEDIUM)
+
+ # Cement Prophecy's mission order if it is the only campaign
+ if (enabled_campaigns == {SC2Campaign.PROLOGUE}
+ or (mission_order_type in static_mission_orders
+ and world.options.shuffle_campaigns.value == ShuffleCampaigns.option_false
+ )
+ ):
pools.move_mission(SC2Mission.DARK_WHISPERS, Difficulty.EASY, Difficulty.STARTER)
- # HotS
- kerriganless = world.options.kerrigan_presence.value not in kerrigan_unit_available \
- or SC2Campaign.HOTS not in enabled_campaigns
+
+ # Grant Story Tech
if grant_story_tech == GrantStoryTech.option_grant:
# Additional starter mission if player is granted story tech
pools.move_mission(SC2Mission.ENEMY_WITHIN, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.TEMPLAR_S_RETURN, Difficulty.MEDIUM, Difficulty.STARTER)
pools.move_mission(SC2Mission.THE_ESCAPE, Difficulty.MEDIUM, Difficulty.STARTER)
pools.move_mission(SC2Mission.IN_THE_ENEMY_S_SHADOW, Difficulty.MEDIUM, Difficulty.STARTER)
- if not war_council_nerfs:
+ if not war_council_nerfs or grant_story_tech == GrantStoryTech.option_grant:
pools.move_mission(SC2Mission.TEMPLAR_S_RETURN, Difficulty.MEDIUM, Difficulty.STARTER)
if (grant_story_tech == GrantStoryTech.option_grant and grant_story_levels) or kerriganless:
# The player has, all the stuff he needs, provided under these settings
pools.move_mission(SC2Mission.SUPREME, Difficulty.MEDIUM, Difficulty.STARTER)
pools.move_mission(SC2Mission.THE_INFINITE_CYCLE, Difficulty.HARD, Difficulty.STARTER)
pools.move_mission(SC2Mission.CONVICTION, Difficulty.MEDIUM, Difficulty.STARTER)
- if (grant_story_tech != GrantStoryTech.option_grant
- and (
- world.options.nova_ghost_of_a_chance_variant == NovaGhostOfAChanceVariant.option_nco
- or (
- SC2Campaign.NCO in enabled_campaigns
- and world.options.nova_ghost_of_a_chance_variant.value == NovaGhostOfAChanceVariant.option_auto
- )
- )
- ):
- # Using NCO tech for this mission that must be acquired
- pools.move_mission(SC2Mission.GHOST_OF_A_CHANCE, Difficulty.STARTER, Difficulty.MEDIUM)
+
+ # Take over AI allies
if world.options.take_over_ai_allies.value == TakeOverAIAllies.option_true:
pools.move_mission(SC2Mission.HARBINGER_OF_OBLIVION, Difficulty.MEDIUM, Difficulty.STARTER)
- if pools.get_pool_size(Difficulty.STARTER) < 2 and not kerriganless or adv_tactics:
- # Conditionally moving Easy missions to Starter
- pools.move_mission(SC2Mission.HARVEST_OF_SCREAMS, Difficulty.EASY, Difficulty.STARTER)
- pools.move_mission(SC2Mission.DOMINATION, Difficulty.EASY, Difficulty.STARTER)
+
+ # Final pool size adjustments
if pools.get_pool_size(Difficulty.STARTER) < 2:
+ pools.move_mission(SC2Mission.HARVEST_OF_SCREAMS, Difficulty.EASY, Difficulty.STARTER)
pools.move_mission(SC2Mission.DOMINATION, Difficulty.EASY, Difficulty.STARTER)
pools.move_mission(SC2Mission.DOMINATION_T, Difficulty.EASY, Difficulty.STARTER)
pools.move_mission(SC2Mission.DOMINATION_P, Difficulty.EASY, Difficulty.STARTER)
diff --git a/worlds/sc2/rules.py b/worlds/sc2/rules.py
index 2298c2cea669..52511f152317 100644
--- a/worlds/sc2/rules.py
+++ b/worlds/sc2/rules.py
@@ -19,26 +19,13 @@
get_enabled_races,
)
from .item.item_tables import (
- tvx_defense_ratings,
- tvz_defense_ratings,
- tvx_air_defense_ratings,
kerrigan_levels,
get_full_item_list,
- zvx_air_defense_ratings,
- zvx_defense_ratings,
- pvx_defense_ratings,
- pvz_defense_ratings,
no_logic_basic_units,
advanced_basic_units,
basic_units,
upgrade_bundle_inverted_lookup,
WEAPON_ARMOR_UPGRADE_MAX_LEVEL,
- soa_ultimate_ratings,
- soa_energy_ratings,
- terran_passive_ratings,
- soa_passive_ratings,
- zerg_passive_ratings,
- protoss_passive_ratings,
)
from .mission_tables import SC2Race, SC2Campaign
from .item import item_groups, item_names
@@ -116,7 +103,7 @@ def is_item_placement(self, state: CollectionState) -> bool:
# has_group with count = 0 is always true for item placement and always false for SC2 item filtering
return state.has_group("Missions", self.player, 0)
- def get_very_hard_required_upgrade_level(self) -> bool:
+ def get_very_hard_required_upgrade_level(self) -> int:
return 2 if self.advanced_tactics else 3
def weapon_armor_upgrade_count(self, upgrade_item: str, state: CollectionState) -> int:
@@ -140,7 +127,7 @@ def weapon_armor_upgrade_count(self, upgrade_item: str, state: CollectionState)
count += 1
return count
- def soa_power_rating(self, state: CollectionState) -> bool:
+ def soa_power_rating(self, state: CollectionState) -> int:
power_rating = 0
# Spear of Adun Ultimates (Strongest)
for item, rating in soa_ultimate_ratings.items():
@@ -256,26 +243,23 @@ def terran_air_anti_air(self, state: CollectionState) -> bool:
)
def terran_any_air_unit(self, state: CollectionState) -> bool:
- return state.has_any(
- {
- item_names.VIKING,
- item_names.MEDIVAC,
- item_names.RAVEN,
- item_names.BANSHEE,
- item_names.SCIENCE_VESSEL,
- item_names.BATTLECRUISER,
- item_names.WRAITH,
- item_names.HERCULES,
- item_names.LIBERATOR,
- item_names.VALKYRIE,
- item_names.SKY_FURY,
- item_names.NIGHT_HAWK,
- item_names.EMPERORS_GUARDIAN,
- item_names.NIGHT_WOLF,
- item_names.PRIDE_OF_AUGUSTRGRAD,
- },
- self.player,
- )
+ return state.has_any((
+ item_names.VIKING,
+ item_names.MEDIVAC,
+ item_names.RAVEN,
+ item_names.BANSHEE,
+ item_names.SCIENCE_VESSEL,
+ item_names.BATTLECRUISER,
+ item_names.WRAITH,
+ item_names.HERCULES,
+ item_names.LIBERATOR,
+ item_names.VALKYRIE,
+ item_names.SKY_FURY,
+ item_names.NIGHT_HAWK,
+ item_names.EMPERORS_GUARDIAN,
+ item_names.NIGHT_WOLF,
+ item_names.PRIDE_OF_AUGUSTRGRAD,
+ ), self.player)
def terran_competent_ground_to_air(self, state: CollectionState) -> bool:
"""
@@ -288,10 +272,12 @@ def terran_competent_ground_to_air(self, state: CollectionState) -> bool:
and self.terran_bio_heal(state)
and self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_TERRAN_INFANTRY_WEAPON, state) >= 2
)
- or self.advanced_tactics
- and (
- state.has(item_names.CYCLONE, self.player)
- or state.has_all((item_names.THOR, item_names.THOR_PROGRESSIVE_HIGH_IMPACT_PAYLOAD), self.player)
+ or (
+ self.advanced_tactics
+ and (
+ state.has(item_names.CYCLONE, self.player)
+ or state.has_all((item_names.THOR, item_names.THOR_PROGRESSIVE_HIGH_IMPACT_PAYLOAD), self.player)
+ )
)
)
@@ -381,33 +367,28 @@ def terran_basic_anti_air(self, state: CollectionState) -> bool:
Basic AA to deal with few air units
"""
return (
- state.has_any(
- (
- item_names.MISSILE_TURRET,
- item_names.WAR_PIGS,
- item_names.SPARTAN_COMPANY,
- item_names.HELS_ANGELS,
- item_names.WINGED_NIGHTMARES,
- item_names.BRYNHILDS,
- item_names.SKY_FURY,
- item_names.SON_OF_KORHAL,
- item_names.BULWARK_COMPANY,
- ),
- self.player,
- )
+ state.has_any((
+ item_names.MISSILE_TURRET,
+ item_names.WAR_PIGS,
+ item_names.SPARTAN_COMPANY,
+ item_names.HELS_ANGELS,
+ item_names.WINGED_NIGHTMARES,
+ item_names.BRYNHILDS,
+ item_names.SKY_FURY,
+ item_names.SON_OF_KORHAL,
+ item_names.BULWARK_COMPANY,
+ ), self.player)
or self.terran_moderate_anti_air(state)
- or self.advanced_tactics
- and (
- state.has_any(
- (
+ or (self.advanced_tactics
+ and (
+ state.has_any((
item_names.WIDOW_MINE,
item_names.PRIDE_OF_AUGUSTRGRAD,
item_names.BLACKHAMMER,
item_names.EMPERORS_SHADOW,
item_names.EMPERORS_GUARDIAN,
item_names.NIGHT_HAWK,
- ),
- self.player,
+ ), self.player)
)
)
)
@@ -422,30 +403,37 @@ def terran_defense_rating(self, state: CollectionState, zerg_enemy: bool, air_en
"""
defense_score = sum((tvx_defense_ratings[item] for item in tvx_defense_ratings if state.has(item, self.player)))
# Manned Bunker
- if state.has_any({item_names.MARINE, item_names.DOMINION_TROOPER, item_names.MARAUDER}, self.player) and state.has(
- item_names.BUNKER, self.player
+ if (state.has_any((item_names.MARINE, item_names.DOMINION_TROOPER, item_names.MARAUDER), self.player)
+ and state.has(item_names.BUNKER, self.player)
):
defense_score += 3
elif zerg_enemy and state.has(item_names.FIREBAT, self.player) and state.has(item_names.BUNKER, self.player):
defense_score += 2
# Siege Tank upgrades
- if state.has_all({item_names.SIEGE_TANK, item_names.SIEGE_TANK_MAELSTROM_ROUNDS}, self.player):
+ if state.has_all((item_names.SIEGE_TANK, item_names.SIEGE_TANK_MAELSTROM_ROUNDS), self.player):
defense_score += 2
- if state.has_all({item_names.SIEGE_TANK, item_names.SIEGE_TANK_GRADUATING_RANGE}, self.player):
+ if state.has_all((item_names.SIEGE_TANK, item_names.SIEGE_TANK_GRADUATING_RANGE), self.player):
defense_score += 1
# Widow Mine upgrade
- if state.has_all({item_names.WIDOW_MINE, item_names.WIDOW_MINE_CONCEALMENT}, self.player):
+ if state.has_all((item_names.WIDOW_MINE, item_names.WIDOW_MINE_CONCEALMENT), self.player):
defense_score += 1
# Viking with splash
- if state.has_all({item_names.VIKING, item_names.VIKING_SHREDDER_ROUNDS}, self.player):
+ if state.has_all((item_names.VIKING, item_names.VIKING_SHREDDER_ROUNDS), self.player):
defense_score += 2
# General enemy-based rules
if zerg_enemy:
- defense_score += sum((tvz_defense_ratings[item] for item in tvz_defense_ratings if state.has(item, self.player)))
+ defense_score += sum((
+ tvz_defense_ratings[item]
+ for item in tvz_defense_ratings
+ if state.has(item, self.player)
+ ))
if air_enemy:
# Capped at 2
- defense_score += min(sum((tvx_air_defense_ratings[item] for item in tvx_air_defense_ratings if state.has(item, self.player))), 2)
+ defense_score += min2(
+ 2,
+ sum((tvx_air_defense_ratings[item] for item in tvx_air_defense_ratings if state.has(item, self.player))),
+ )
if air_enemy and zerg_enemy and state.has(item_names.VALKYRIE, self.player):
# Valkyries shred mass Mutas, the most common air enemy that's massed in these cases
defense_score += 2
@@ -461,19 +449,24 @@ def terran_competent_comp(self, state: CollectionState, upgrade_level: int = 1)
# Infantry with Healing
infantry_weapons = self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_TERRAN_INFANTRY_WEAPON, state)
infantry_armor = self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_TERRAN_INFANTRY_ARMOR, state)
- infantry = state.has_any({item_names.MARINE, item_names.DOMINION_TROOPER, item_names.MARAUDER}, self.player)
- if infantry_weapons >= upgrade_level + 1 and infantry_armor >= upgrade_level and infantry and self.terran_bio_heal(state):
+ infantry = state.has_any((item_names.MARINE, item_names.DOMINION_TROOPER, item_names.MARAUDER), self.player)
+ if (infantry_weapons >= upgrade_level + 1
+ and infantry_armor >= upgrade_level
+ and infantry
+ and self.terran_bio_heal(state)
+ ):
return True
# Mass Air-To-Ground
ship_weapons = self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_TERRAN_SHIP_WEAPON, state)
ship_armor = self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_TERRAN_SHIP_ARMOR, state)
if ship_weapons >= upgrade_level and ship_armor >= upgrade_level:
air = (
- state.has_any({item_names.BANSHEE, item_names.BATTLECRUISER}, self.player)
- or state.has_all({item_names.LIBERATOR, item_names.LIBERATOR_RAID_ARTILLERY}, self.player)
- or state.has_all({item_names.WRAITH, item_names.WRAITH_ADVANCED_LASER_TECHNOLOGY}, self.player)
- or state.has_all({item_names.VALKYRIE, item_names.VALKYRIE_FLECHETTE_MISSILES}, self.player)
- and ship_weapons >= 2
+ state.has_any((item_names.BANSHEE, item_names.BATTLECRUISER), self.player)
+ or state.has_all((item_names.LIBERATOR, item_names.LIBERATOR_RAID_ARTILLERY), self.player)
+ or state.has_all((item_names.WRAITH, item_names.WRAITH_ADVANCED_LASER_TECHNOLOGY), self.player)
+ or (state.has_all((item_names.VALKYRIE, item_names.VALKYRIE_FLECHETTE_MISSILES), self.player)
+ and ship_weapons >= 2
+ )
)
if air and self.terran_mineral_dump(state):
return True
@@ -499,9 +492,11 @@ def terran_mineral_dump(self, state: CollectionState) -> bool:
Can build something using only minerals
"""
return (
- state.has_any({item_names.MARINE, item_names.VULTURE, item_names.HELLION, item_names.SON_OF_KORHAL}, self.player)
- or state.has_all({item_names.REAPER, item_names.REAPER_RESOURCE_EFFICIENCY}, self.player)
- or (self.advanced_tactics and state.has_any({item_names.PERDITION_TURRET, item_names.DEVASTATOR_TURRET}, self.player))
+ state.has_any((item_names.MARINE, item_names.VULTURE, item_names.HELLION, item_names.SON_OF_KORHAL), self.player)
+ or state.has_all((item_names.REAPER, item_names.REAPER_RESOURCE_EFFICIENCY), self.player)
+ or (self.advanced_tactics
+ and state.has_any((item_names.PERDITION_TURRET, item_names.DEVASTATOR_TURRET), self.player)
+ )
)
def terran_beats_protoss_deathball(self, state: CollectionState) -> bool:
@@ -510,12 +505,15 @@ def terran_beats_protoss_deathball(self, state: CollectionState) -> bool:
"""
return (
(
- state.has_any({item_names.BANSHEE, item_names.BATTLECRUISER}, self.player)
- or state.has_all({item_names.LIBERATOR, item_names.LIBERATOR_RAID_ARTILLERY}, self.player)
+ (
+ state.has_any((item_names.BANSHEE, item_names.BATTLECRUISER), self.player)
+ or state.has_all((item_names.LIBERATOR, item_names.LIBERATOR_RAID_ARTILLERY), self.player)
+ )
+ and self.terran_competent_anti_air(state)
+ )
+ or (self.terran_competent_comp(state)
+ and self.terran_air_anti_air(state)
)
- and self.terran_competent_anti_air(state)
- or self.terran_competent_comp(state)
- and self.terran_air_anti_air(state)
) and self.terran_army_weapon_armor_upgrade_min_level(state) >= 2
def marine_medic_upgrade(self, state: CollectionState) -> bool:
@@ -523,10 +521,17 @@ def marine_medic_upgrade(self, state: CollectionState) -> bool:
Infantry upgrade to infantry-only no-build segments
"""
return (
- state.has_any({item_names.MARINE_COMBAT_SHIELD, item_names.MARINE_MAGRAIL_MUNITIONS, item_names.MEDIC_STABILIZER_MEDPACKS}, self.player)
- or (state.count(item_names.MARINE_PROGRESSIVE_STIMPACK, self.player) >= 2 and state.has_group("Missions", self.player, 1))
- or self.advanced_tactics
- and state.has(item_names.MARINE_LASER_TARGETING_SYSTEM, self.player)
+ state.has_any((
+ item_names.MARINE_COMBAT_SHIELD,
+ item_names.MARINE_MAGRAIL_MUNITIONS,
+ item_names.MEDIC_STABILIZER_MEDPACKS,
+ ), self.player)
+ or (state.count(item_names.MARINE_PROGRESSIVE_STIMPACK, self.player) >= 2
+ and state.has_group("Missions", self.player, 1)
+ )
+ or (self.advanced_tactics
+ and state.has(item_names.MARINE_LASER_TARGETING_SYSTEM, self.player)
+ )
)
def marine_medic_firebat_upgrade(self, state: CollectionState) -> bool:
@@ -977,15 +982,22 @@ def zerg_competent_comp(self, state: CollectionState) -> bool:
if self.zerg_army_weapon_armor_upgrade_min_level(state) < 2:
return False
advanced = self.advanced_tactics
- core_unit = state.has_any(
- {item_names.ROACH, item_names.ABERRATION, item_names.ZERGLING, item_names.INFESTED_DIAMONDBACK}, self.player
- ) or self.morph_igniter(state)
+ core_unit = (
+ state.has_any((
+ item_names.ROACH,
+ item_names.ABERRATION,
+ item_names.ZERGLING,
+ item_names.INFESTED_DIAMONDBACK,
+ ), self.player)
+ or self.morph_igniter(state)
+ )
support_unit = (
state.has_any({item_names.SWARM_QUEEN, item_names.HYDRALISK, item_names.INFESTED_BANSHEE}, self.player)
or self.morph_brood_lord(state)
or state.has_all((item_names.MUTALISK, item_names.MUTALISK_SEVERING_GLAIVE, item_names.MUTALISK_VICIOUS_GLAIVE), self.player)
- or advanced
- and (state.has_any({item_names.INFESTOR, item_names.DEFILER}, self.player) or self.morph_viper(state))
+ or (advanced
+ and (state.has_any((item_names.INFESTOR, item_names.DEFILER), self.player) or self.morph_viper(state))
+ )
)
if core_unit and support_unit:
return True
@@ -997,8 +1009,9 @@ def zerg_competent_comp(self, state: CollectionState) -> bool:
(item_names.GUARDIAN_SORONAN_ACID, item_names.GUARDIAN_EXPLOSIVE_SPORES, item_names.GUARDIAN_PRIMORDIAL_FURY), self.player
)
)
- or advanced
- and self.morph_viper(state)
+ or (advanced
+ and self.morph_viper(state)
+ )
)
return vespene_unit and state.has_any({item_names.ZERGLING, item_names.SWARM_QUEEN}, self.player)
@@ -1057,22 +1070,22 @@ def zerg_base_buster(self, state: CollectionState) -> bool:
self.zerg_ranged_weapon_armor_upgrade_min_level(state) >= self.get_very_hard_required_upgrade_level()
and (
self.morph_impaler(state)
- or self.morph_lurker(state)
- and state.has_all((item_names.LURKER_SEISMIC_SPINES, item_names.LURKER_ADAPTED_SPINES), self.player)
- or state.has_all(
- (
- item_names.ROACH,
- item_names.ROACH_CORPSER_STRAIN,
- item_names.ROACH_ADAPTIVE_PLATING,
- item_names.ROACH_GLIAL_RECONSTITUTION,
- ),
- self.player,
+ or (self.morph_lurker(state)
+ and state.has_all((item_names.LURKER_SEISMIC_SPINES, item_names.LURKER_ADAPTED_SPINES), self.player)
+ )
+ or state.has_all((
+ item_names.ROACH,
+ item_names.ROACH_CORPSER_STRAIN,
+ item_names.ROACH_ADAPTIVE_PLATING,
+ item_names.ROACH_GLIAL_RECONSTITUTION,
+ ), self.player)
+ or (self.morph_igniter(state)
+ and state.has(item_names.PRIMAL_IGNITER_PRIMAL_TENACITY, self.player)
)
- or self.morph_igniter(state)
- and state.has(item_names.PRIMAL_IGNITER_PRIMAL_TENACITY, self.player)
or state.has_all((item_names.INFESTOR, item_names.INFESTOR_INFESTED_TERRAN), self.player)
- or self.spread_creep(state, False)
- and state.has(item_names.INFESTED_BUNKER, self.player)
+ or (self.spread_creep(state, False)
+ and state.has(item_names.INFESTED_BUNKER, self.player)
+ )
or self.zerg_infested_tank_with_ammo(state)
# Highly-upgraded swarm hosts may also work, but that would require promoting many upgrades to progression
)
@@ -1081,8 +1094,9 @@ def zerg_base_buster(self, state: CollectionState) -> bool:
self.zerg_flyer_weapon_armor_upgrade_min_level(state) >= self.get_very_hard_required_upgrade_level()
and (
self.morph_brood_lord(state)
- or self.morph_guardian(state)
- and state.has_all((item_names.GUARDIAN_PROPELLANT_SACS, item_names.GUARDIAN_SORONAN_ACID), self.player)
+ or (self.morph_guardian(state)
+ and state.has_all((item_names.GUARDIAN_PROPELLANT_SACS, item_names.GUARDIAN_SORONAN_ACID), self.player)
+ )
or state.has_all((item_names.INFESTED_BANSHEE, item_names.INFESTED_BANSHEE_FLESHFUSED_TARGETING_OPTICS), self.player)
# Highly-upgraded anti-ground devourers would also be good
)
@@ -1154,11 +1168,7 @@ def basic_kerrigan(self, state: CollectionState, story_tech_available=True) -> b
def two_kerrigan_actives(self, state: CollectionState, story_tech_available=True) -> bool:
if story_tech_available and self.grant_story_tech == GrantStoryTech.option_grant:
return True
- count = 0
- for i in range(7):
- if state.has_any(kerrigan_logic_active_abilities, self.player):
- count += 1
- return count >= 2
+ return state.count_from_list(item_groups.kerrigan_logic_active_abilities, self.player) >= 2
# Global Protoss
def protoss_power_rating(self, state: CollectionState) -> int:
@@ -1346,7 +1356,6 @@ def protoss_basic_anti_air(self, state: CollectionState) -> bool:
item_names.MISTWING,
item_names.CALADRIUS,
item_names.OPPRESSOR,
- item_names.PULSAR,
item_names.DRAGOON,
},
self.player,
@@ -1354,8 +1363,14 @@ def protoss_basic_anti_air(self, state: CollectionState) -> bool:
or state.has_all({item_names.TRIREME, item_names.TRIREME_SOLAR_BEAM}, self.player)
or state.has_all({item_names.WRATHWALKER, item_names.WRATHWALKER_AERIAL_TRACKING}, self.player)
or state.has_all({item_names.WARP_PRISM, item_names.WARP_PRISM_PHASE_BLASTER}, self.player)
- or self.advanced_tactics
- and state.has_any({item_names.HIGH_TEMPLAR, item_names.SIGNIFIER, item_names.SENTRY, item_names.ENERGIZER}, self.player)
+ or (self.advanced_tactics
+ and state.has_any((
+ item_names.HIGH_TEMPLAR,
+ item_names.SIGNIFIER,
+ item_names.SENTRY,
+ item_names.ENERGIZER,
+ ), self.player)
+ )
or self.protoss_can_merge_archon(state)
or self.protoss_can_merge_dark_archon(state)
)
@@ -1629,8 +1644,9 @@ def protoss_mineral_dump(self, state: CollectionState) -> bool:
return (
state.has_any((item_names.ZEALOT, item_names.SENTINEL, item_names.PHOTON_CANNON), self.player)
or state.has_all((item_names.CENTURION, item_names.CENTURION_RESOURCE_EFFICIENCY), self.player)
- or self.advanced_tactics
- and state.has_any((item_names.SUPPLICANT, item_names.SHIELD_BATTERY), self.player)
+ or (self.advanced_tactics
+ and state.has_any((item_names.SUPPLICANT, item_names.SHIELD_BATTERY), self.player)
+ )
)
def zealot_sentry_slayer_start(self, state: CollectionState) -> bool:
@@ -1770,8 +1786,12 @@ def zerg_respond_to_colony_infestations(self, state: CollectionState) -> bool:
return self.zerg_havens_fall_requirement(state) and (
self.morph_devourer(state)
or state.has_any({item_names.MUTALISK, item_names.CORRUPTOR}, self.player)
- or self.advanced_tactics
- and (self.morph_viper(state) or state.has_any({item_names.BROOD_QUEEN, item_names.SCOURGE}, self.player))
+ or (self.advanced_tactics
+ and (
+ self.morph_viper(state)
+ or state.has_any((item_names.BROOD_QUEEN, item_names.SCOURGE), self.player)
+ )
+ )
)
def protoss_havens_fall_requirement(self, state: CollectionState) -> bool:
@@ -1780,12 +1800,12 @@ def protoss_havens_fall_requirement(self, state: CollectionState) -> bool:
and self.protoss_competent_anti_air(state)
and (
self.protoss_competent_comp(state)
+ or state.has_any((item_names.TEMPEST, item_names.SKYLORD, item_names.DESTROYER), self.player)
or (
- state.has_any((item_names.TEMPEST, item_names.SKYLORD, item_names.DESTROYER), self.player)
- or (
- self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_PROTOSS_AIR_WEAPON, state) >= 2
- and state.has(item_names.CARRIER, self.player)
- or state.has_all((item_names.SKIRMISHER, item_names.SKIRMISHER_PEER_CONTEMPT), self.player)
+ self.weapon_armor_upgrade_count(item_names.PROGRESSIVE_PROTOSS_AIR_WEAPON, state) >= 2
+ and (
+ state.has_all((item_names.SKIRMISHER, item_names.SKIRMISHER_PEER_CONTEMPT), self.player)
+ or (state.has(item_names.CARRIER, self.player))
)
)
)
@@ -1795,30 +1815,33 @@ def protoss_respond_to_colony_infestations(self, state: CollectionState) -> bool
"""
Can deal quickly with Brood Lords and Mutas in Haven's Fall and being able to progress the mission
"""
- return self.protoss_havens_fall_requirement(state) and (
- state.has_any({item_names.CARRIER, item_names.SKYLORD, item_names.DESTROYER, item_names.TEMPEST}, self.player)
- # handle mutas
- or (
- state.has_any(
- {
- item_names.PHOENIX,
- item_names.MIRAGE,
- item_names.CORSAIR,
- },
- self.player,
- )
- or state.has_all((item_names.SKIRMISHER, item_names.SKIRMISHER_PEER_CONTEMPT), self.player)
- )
- # handle brood lords and virophages
+ return (
+ self.protoss_havens_fall_requirement(state)
and (
- state.has_any(
- {
- item_names.VOID_RAY,
- },
- self.player,
+ # One-unit solutions
+ state.has_any((
+ item_names.CARRIER,
+ item_names.SKYLORD,
+ item_names.DESTROYER,
+ item_names.TEMPEST,
+ item_names.VOID_RAY,
+ item_names.SCOUT,
+ ), self.player)
+ or (
+ (
+ # handle mutas
+ state.has_any((
+ item_names.PHOENIX,
+ item_names.MIRAGE,
+ item_names.CORSAIR,
+ ), self.player)
+ or state.has_all((item_names.SKIRMISHER, item_names.SKIRMISHER_PEER_CONTEMPT), self.player)
+ )
+ and (
+ # handle brood lords and virophages
+ state.has(item_names.MISTWING, self.player)
+ )
)
- or self.advanced_tactics
- and state.has_all({item_names.SCOUT, item_names.MISTWING}, self.player)
)
)
@@ -1876,22 +1899,19 @@ def terran_can_grab_ghosts_in_the_fog_east_rock_formation(self, state: Collectio
Able to shoot by a long range or from air to claim the rock formation separated by a chasm
"""
return (
- state.has_any(
- {
- item_names.MEDIVAC,
- item_names.HERCULES,
- item_names.VIKING,
- item_names.BANSHEE,
- item_names.WRAITH,
- item_names.SIEGE_TANK,
- item_names.BATTLECRUISER,
- item_names.NIGHT_HAWK,
- item_names.NIGHT_WOLF,
- item_names.SHOCK_DIVISION,
- item_names.SKY_FURY,
- },
- self.player,
- )
+ state.has_any((
+ item_names.MEDIVAC,
+ item_names.HERCULES,
+ item_names.VIKING,
+ item_names.BANSHEE,
+ item_names.WRAITH,
+ item_names.SIEGE_TANK,
+ item_names.BATTLECRUISER,
+ item_names.NIGHT_HAWK,
+ item_names.NIGHT_WOLF,
+ item_names.SHOCK_DIVISION,
+ item_names.SKY_FURY,
+ ), self.player)
or state.has_all({item_names.VALKYRIE, item_names.VALKYRIE_FLECHETTE_MISSILES}, self.player)
or state.has_all({item_names.RAVEN, item_names.RAVEN_HUNTER_SEEKER_WEAPON}, self.player)
or (
@@ -1901,19 +1921,18 @@ def terran_can_grab_ghosts_in_the_fog_east_rock_formation(self, state: Collectio
or (
self.advanced_tactics
and (
- state.has_any(
- {
- item_names.HELS_ANGELS,
- item_names.DUSK_WINGS,
- item_names.WINGED_NIGHTMARES,
- item_names.SIEGE_BREAKERS,
- item_names.BRYNHILDS,
- item_names.JACKSONS_REVENGE,
- },
- self.player,
- )
+ state.has_any((
+ item_names.HELS_ANGELS,
+ item_names.DUSK_WINGS,
+ item_names.WINGED_NIGHTMARES,
+ item_names.SIEGE_BREAKERS,
+ item_names.BRYNHILDS,
+ item_names.JACKSONS_REVENGE,
+ ), self.player)
+ or state.has_all((
+ item_names.MIDNIGHT_RIDERS, item_names.LIBERATOR_RAID_ARTILLERY,
+ ), self.player)
)
- or state.has_all({item_names.MIDNIGHT_RIDERS, item_names.LIBERATOR_RAID_ARTILLERY}, self.player)
)
)
@@ -1947,15 +1966,9 @@ def zerg_great_train_robbery_train_stopper(self, state: CollectionState) -> bool
)
or state.has_all({item_names.MUTALISK, item_names.MUTALISK_SUNDERING_GLAIVE}, self.player)
or state.has_all((item_names.HYDRALISK, item_names.HYDRALISK_MUSCULAR_AUGMENTS), self.player)
- or (
- state.has(item_names.ZERGLING, self.player)
- and (
- state.has_any(
- (item_names.ZERGLING_SHREDDING_CLAWS, item_names.ZERGLING_SHREDDING_CLAWS, item_names.ZERGLING_RAPTOR_STRAIN), self.player
- )
- )
- and (self.advanced_tactics or state.has_any((item_names.ZERGLING_METABOLIC_BOOST, item_names.ZERGLING_RAPTOR_STRAIN), self.player))
- )
+ # Note: Zerglings were tested by Snarky, and it was found they'd need >= 3 upgrades to be viable,
+ # so they are not included in this logic.
+ # Raptor + 2 of (Shredding, Adrenal, +2 attack upgrade)
or self.zerg_infested_tank_with_ammo(state)
or (self.advanced_tactics and (self.morph_tyrannozor(state)))
)
@@ -1965,13 +1978,18 @@ def protoss_great_train_robbery_train_stopper(self, state: CollectionState) -> b
Ability to deal with trains (moving target with a lot of HP)
"""
return (
- state.has_any(
- (item_names.ANNIHILATOR, item_names.IMMORTAL, item_names.STALKER, item_names.WRATHWALKER, item_names.VOID_RAY, item_names.DESTROYER),
- self.player,
- )
- or state.has_all({item_names.SLAYER, item_names.SLAYER_PHASE_BLINK}, self.player)
+ state.has_any((
+ item_names.ANNIHILATOR,
+ item_names.IMMORTAL,
+ item_names.STALKER,
+ item_names.ADEPT, # Tested by Snarky, "An easy 1-item solve"
+ item_names.WRATHWALKER,
+ item_names.VOID_RAY,
+ item_names.DESTROYER,
+ ), self.player)
+ or state.has_all((item_names.SLAYER, item_names.SLAYER_PHASE_BLINK), self.player)
or state.has_all((item_names.REAVER, item_names.REAVER_KHALAI_REPLICATORS), self.player)
- or state.has_all({item_names.VANGUARD, item_names.VANGUARD_FUSION_MORTARS}, self.player)
+ or state.has_all((item_names.VANGUARD, item_names.VANGUARD_FUSION_MORTARS), self.player)
or (
state.has(item_names.INSTIGATOR, self.player)
and state.has_any((item_names.INSTIGATOR_BLINK_OVERDRIVE, item_names.INSTIGATOR_MODERNIZED_SERVOS), self.player)
@@ -1982,8 +2000,7 @@ def protoss_great_train_robbery_train_stopper(self, state: CollectionState) -> b
self.advanced_tactics
and (
state.has(item_names.TEMPEST, self.player)
- or state.has_all((item_names.ADEPT, item_names.ADEPT_RESONATING_GLAIVES), self.player)
- or state.has_all({item_names.VANGUARD, item_names.VANGUARD_RAPIDFIRE_CANNON}, self.player)
+ or state.has_all((item_names.VANGUARD, item_names.VANGUARD_RAPIDFIRE_CANNON), self.player)
or state.has_all((item_names.OPPRESSOR, item_names.SCOUT_GRAVITIC_THRUSTERS, item_names.OPPRESSOR_VULCAN_BLASTER), self.player)
or state.has_all((item_names.ASCENDANT, item_names.ASCENDANT_POWER_OVERWHELMING, item_names.SUPPLICANT), self.player)
or state.has_all(
@@ -2005,7 +2022,12 @@ def terran_can_rescue(self, state) -> bool:
"""
Rescuing in The Moebius Factor
"""
- return state.has_any({item_names.MEDIVAC, item_names.HERCULES, item_names.RAVEN, item_names.VIKING}, self.player) or self.advanced_tactics
+ return (
+ state.has_any((
+ item_names.MEDIVAC, item_names.HERCULES, item_names.RAVEN, item_names.VIKING
+ ), self.player)
+ or self.advanced_tactics
+ )
def terran_supernova_requirement(self, state) -> bool:
return self.terran_beats_protoss_deathball(state) and self.terran_power_rating(state) >= 6
@@ -2024,7 +2046,10 @@ def protoss_supernova_requirement(self, state: CollectionState) -> bool:
or (self.advanced_tactics and state.has(item_names.PROGRESSIVE_WARP_RELOCATE, self.player))
)
and self.protoss_competent_anti_air(state)
- and (self.protoss_fleet(state) or (self.protoss_competent_comp(state) and self.protoss_power_rating(state) >= 6))
+ and (
+ self.protoss_fleet(state)
+ or (self.protoss_competent_comp(state) and self.protoss_power_rating(state) >= 6)
+ )
)
def terran_maw_requirement(self, state: CollectionState) -> bool:
@@ -2079,18 +2104,25 @@ def zerg_maw_requirement(self, state: CollectionState) -> bool:
return True
usable_muta = (
state.has_all((item_names.MUTALISK, item_names.MUTALISK_RAPID_REGENERATION), self.player)
- and state.has_any((item_names.MUTALISK_SEVERING_GLAIVE, item_names.MUTALISK_VICIOUS_GLAIVE), self.player)
- and (
- state.has(item_names.MUTALISK_SUNDERING_GLAIVE, self.player)
- or state.has_all((item_names.MUTALISK_SEVERING_GLAIVE, item_names.MUTALISK_VICIOUS_GLAIVE), self.player)
- )
+ and state.count_from_list_unique((
+ item_names.MUTALISK_SEVERING_GLAIVE,
+ item_names.MUTALISK_SUNDERING_GLAIVE,
+ item_names.MUTALISK_VICIOUS_GLAIVE,
+ ), self.player) >= 2
)
return (
# Heal
(
state.has(item_names.SWARM_QUEEN, self.player)
- or self.advanced_tactics
- and ((self.morph_tyrannozor(state) and state.has(item_names.TYRANNOZOR_HEALING_ADAPTATION, self.player)) or (usable_muta))
+ or (self.advanced_tactics
+ and (
+ (
+ self.morph_tyrannozor(state)
+ and state.has(item_names.TYRANNOZOR_HEALING_ADAPTATION, self.player)
+ )
+ or usable_muta
+ )
+ )
)
# Cross the gap
and (
@@ -2147,8 +2179,9 @@ def terran_engine_of_destruction_requirement(self, state: CollectionState) -> bo
else:
return (
state.has_any((item_names.WRAITH, item_names.BATTLECRUISER), self.player)
- or self.terran_air_anti_air(state)
- and state.has_any((item_names.BANSHEE, item_names.LIBERATOR), self.player)
+ or (self.terran_air_anti_air(state)
+ and state.has_any((item_names.BANSHEE, item_names.LIBERATOR), self.player)
+ )
)
def zerg_engine_of_destruction_requirement(self, state: CollectionState) -> bool:
@@ -2488,50 +2521,47 @@ def templars_return_phase_2_requirement(self, state: CollectionState) -> bool:
self.grant_story_tech == GrantStoryTech.option_grant
or self.advanced_tactics
or (
- state.has_any(
- (
- item_names.IMMORTAL,
- item_names.ANNIHILATOR,
- item_names.VANGUARD,
- item_names.COLOSSUS,
- item_names.WRATHWALKER,
- item_names.REAVER,
- item_names.DARK_TEMPLAR,
- item_names.HIGH_TEMPLAR,
- item_names.ENERGIZER,
- item_names.SENTRY,
- ),
- self.player,
- )
+ state.has_any((
+ item_names.IMMORTAL,
+ item_names.ANNIHILATOR,
+ item_names.VANGUARD,
+ item_names.COLOSSUS,
+ item_names.WRATHWALKER,
+ item_names.REAVER,
+ item_names.DARK_TEMPLAR,
+ item_names.HIGH_TEMPLAR,
+ item_names.ENERGIZER,
+ item_names.SENTRY,
+ ), self.player)
)
)
def templars_return_phase_3_reach_colossus_requirement(self, state: CollectionState) -> bool:
return self.templars_return_phase_2_requirement(state) and (
self.grant_story_tech == GrantStoryTech.option_grant
- or self.advanced_tactics
- and state.has_any({item_names.ZEALOT_WHIRLWIND, item_names.VANGUARD_RAPIDFIRE_CANNON}, self.player)
- or state.has_all((
- item_names.ZEALOT_WHIRLWIND, item_names.VANGUARD_RAPIDFIRE_CANNON
+ or (self.advanced_tactics
+ and state.has_any((
+ item_names.ZEALOT_WHIRLWIND, item_names.VANGUARD_RAPIDFIRE_CANNON,
), self.player)
+ )
+ or state.has_all((
+ item_names.ZEALOT_WHIRLWIND, item_names.VANGUARD_RAPIDFIRE_CANNON
+ ), self.player)
)
def templars_return_phase_3_reach_dts_requirement(self, state: CollectionState) -> bool:
return self.templars_return_phase_3_reach_colossus_requirement(state) and (
self.grant_story_tech == GrantStoryTech.option_grant
+ or state.has_all((
+ item_names.COLOSSUS_PACIFICATION_PROTOCOL,
+ item_names.ENERGIZER_MOBILE_CHRONO_BEAM,
+ ), self.player)
or (
- (self.advanced_tactics or state.has(item_names.ENERGIZER_MOBILE_CHRONO_BEAM, self.player))
- and (state.has(item_names.COLOSSUS_FIRE_LANCE, self.player)
- or (
- state.has_all(
- {
- item_names.COLOSSUS_PACIFICATION_PROTOCOL,
- item_names.ENERGIZER_MOBILE_CHRONO_BEAM,
- },
- self.player,
- )
+ state.has(item_names.COLOSSUS_FIRE_LANCE, self.player)
+ and (self.advanced_tactics
+ or state.has(item_names.ENERGIZER_MOBILE_CHRONO_BEAM, self.player)
)
- ))
+ )
)
def terran_spear_of_adun_requirement(self, state: CollectionState) -> bool:
@@ -2627,8 +2657,9 @@ def zerg_last_stand_requirement(self, state: CollectionState) -> bool:
self.morph_lurker(state)
or state.has_all({item_names.MUTALISK, item_names.MUTALISK_SEVERING_GLAIVE, item_names.MUTALISK_VICIOUS_GLAIVE}, self.player)
or self.zerg_infested_tank_with_ammo(state)
- or self.advanced_tactics
- and state.has_all({item_names.ULTRALISK, item_names.ULTRALISK_CHITINOUS_PLATING, item_names.ULTRALISK_MONARCH_BLADES}, self.player)
+ or (self.advanced_tactics
+ and state.has_all((item_names.ULTRALISK, item_names.ULTRALISK_CHITINOUS_PLATING, item_names.ULTRALISK_MONARCH_BLADES), self.player)
+ )
)
and (
self.morph_impaler(state)
@@ -2639,8 +2670,9 @@ def zerg_last_stand_requirement(self, state: CollectionState) -> bool:
and (
self.morph_devourer(state)
or state.has_all({item_names.MUTALISK, item_names.MUTALISK_SUNDERING_GLAIVE}, self.player)
- or self.advanced_tactics
- and state.has(item_names.BROOD_QUEEN, self.player)
+ or (self.advanced_tactics
+ and state.has(item_names.BROOD_QUEEN, self.player)
+ )
)
and self.zerg_mineral_dump(state)
and self.zerg_army_weapon_armor_upgrade_min_level(state) >= 2
@@ -2674,9 +2706,10 @@ def protoss_harbinger_of_oblivion_requirement(self, state: CollectionState) -> b
return (
self.protoss_anti_armor_anti_air(state)
and (
- self.take_over_ai_allies
- and (self.protoss_common_unit(state) or self.zerg_common_unit(state))
- or (self.protoss_competent_comp(state) and self.protoss_hybrid_counter(state))
+ (self.protoss_competent_comp(state) and self.protoss_hybrid_counter(state))
+ or (self.take_over_ai_allies
+ and (self.protoss_common_unit(state) or self.zerg_common_unit(state))
+ )
)
and self.protoss_power_rating(state) >= 6
)
@@ -2685,11 +2718,18 @@ def terran_harbinger_of_oblivion_requirement(self, state: CollectionState) -> bo
return (
self.terran_competent_anti_air(state)
and (
- self.take_over_ai_allies
- and (self.terran_common_unit(state) or self.zerg_common_unit(state))
- or (
+ (
self.terran_beats_protoss_deathball(state)
- and state.has_any({item_names.BATTLECRUISER, item_names.LIBERATOR, item_names.SIEGE_TANK, item_names.THOR}, self.player)
+ and state.has_any((
+ item_names.BATTLECRUISER,
+ item_names.LIBERATOR,
+ item_names.SIEGE_TANK,
+ item_names.THOR,
+ ), self.player)
+ )
+ or (
+ self.take_over_ai_allies
+ and (self.terran_common_unit(state) or self.zerg_common_unit(state))
)
)
and self.terran_power_rating(state) >= 6
@@ -2727,10 +2767,12 @@ def terran_unsealing_the_past_requirement(self, state: CollectionState) -> bool:
state.has_all({item_names.LIBERATOR, item_names.LIBERATOR_SMART_SERVOS}, self.player)
and (
(
- state.has_all({item_names.HELLION, item_names.HELLION_HELLBAT}, self.player)
- or state.has(item_names.FIREBAT, self.player)
+ (
+ state.has_all((item_names.HELLION, item_names.HELLION_HELLBAT), self.player)
+ or state.has(item_names.FIREBAT, self.player)
+ )
+ and self.terran_bio_heal(state)
)
- and self.terran_bio_heal(state)
or state.has_all({item_names.VIKING, item_names.VIKING_SHREDDER_ROUNDS}, self.player)
or state.has(item_names.BANSHEE, self.player)
)
@@ -2921,12 +2963,20 @@ def zerg_templars_charge_requirement(self, state: CollectionState) -> bool:
def protoss_the_host_requirement(self, state: CollectionState) -> bool:
return (
- self.protoss_fleet(state) and self.protoss_static_defense(state) and self.protoss_army_weapon_armor_upgrade_min_level(state) >= 2
- ) or (
- self.protoss_deathball(state)
- and state.has(item_names.SOA_TIME_STOP, self.player)
- or self.advanced_tactics
- and (state.has_any((item_names.SOA_SHIELD_OVERCHARGE, item_names.SOA_SOLAR_BOMBARDMENT), self.player))
+ (
+ self.protoss_fleet(state)
+ and self.protoss_static_defense(state)
+ and self.protoss_army_weapon_armor_upgrade_min_level(state) >= 2
+ )
+ or (
+ self.protoss_deathball(state)
+ and (
+ state.has(item_names.SOA_TIME_STOP, self.player)
+ or (self.advanced_tactics
+ and (state.has_any((item_names.SOA_SHIELD_OVERCHARGE, item_names.SOA_SOLAR_BOMBARDMENT), self.player))
+ )
+ )
+ )
)
def terran_the_host_requirement(self, state: CollectionState) -> bool:
@@ -2947,10 +2997,21 @@ def terran_the_host_requirement(self, state: CollectionState) -> bool:
)
)
or (
- self.spear_of_adun_presence == SpearOfAdunPresence.option_everywhere
- and state.has(item_names.SOA_TIME_STOP, self.player)
- or self.advanced_tactics
- and (state.has_any((item_names.SOA_SHIELD_OVERCHARGE, item_names.SOA_SOLAR_BOMBARDMENT), self.player))
+ (
+ self.spear_of_adun_presence == SpearOfAdunPresence.option_everywhere
+ or self.spear_of_adun_presence == SpearOfAdunPresence.option_any_race_lotv
+ )
+ and (
+ state.has(item_names.SOA_TIME_STOP, self.player)
+ or (self.advanced_tactics
+ and (
+ state.has_any((
+ item_names.SOA_SHIELD_OVERCHARGE,
+ item_names.SOA_SOLAR_BOMBARDMENT,
+ ), self.player)
+ )
+ )
+ )
)
)
)
@@ -2963,30 +3024,51 @@ def zerg_the_host_requirement(self, state: CollectionState) -> bool:
and self.zerg_base_buster(state)
and self.zerg_big_monsters(state)
and (
- (self.morph_brood_lord(state) or self.morph_guardian(state))
- and (
- (self.morph_devourer(state) and state.has(item_names.MUTALISK, self.player))
- or state.has_all((item_names.INFESTED_LIBERATOR, item_names.INFESTED_LIBERATOR_CLOUD_DISPERSAL), self.player)
+ (
+ (
+ (self.morph_brood_lord(state) or self.morph_guardian(state))
+ and (
+ self.morph_devourer(state) and state.has(item_names.MUTALISK, self.player)
+ or state.has_all(
+ (
+ item_names.INFESTED_LIBERATOR,
+ item_names.INFESTED_LIBERATOR_CLOUD_DISPERSAL
+ ),
+ self.player
+ )
+ )
+ )
+ or (
+ state.has_all(
+ (
+ item_names.MUTALISK,
+ item_names.MUTALISK_SEVERING_GLAIVE,
+ item_names.MUTALISK_VICIOUS_GLAIVE,
+ item_names.MUTALISK_SUNDERING_GLAIVE,
+ item_names.MUTALISK_RAPID_REGENERATION,
+ ),
+ self.player,
+ )
+ )
)
or (
- state.has_all(
- (
- item_names.MUTALISK,
- item_names.MUTALISK_SEVERING_GLAIVE,
- item_names.MUTALISK_VICIOUS_GLAIVE,
- item_names.MUTALISK_SUNDERING_GLAIVE,
- item_names.MUTALISK_RAPID_REGENERATION,
- ),
- self.player,
+ (
+ self.spear_of_adun_presence == SpearOfAdunPresence.option_everywhere
+ or self.spear_of_adun_presence == SpearOfAdunPresence.option_any_race_lotv
+ )
+ and (
+ state.has(item_names.SOA_TIME_STOP, self.player)
+ or (self.advanced_tactics
+ and (
+ state.has_any((
+ item_names.SOA_SHIELD_OVERCHARGE,
+ item_names.SOA_SOLAR_BOMBARDMENT
+ ), self.player)
+ )
+ )
)
)
)
- or (
- self.spear_of_adun_presence == SpearOfAdunPresence.option_everywhere
- and state.has(item_names.SOA_TIME_STOP, self.player)
- or self.advanced_tactics
- and (state.has_any((item_names.SOA_SHIELD_OVERCHARGE, item_names.SOA_SOLAR_BOMBARDMENT), self.player))
- )
)
def protoss_salvation_requirement(self, state: CollectionState) -> bool:
@@ -3249,19 +3331,22 @@ def night_terrors_requirement(self, state: CollectionState) -> bool:
(
# Regular infesteds
(
- state.has_any((item_names.FIREBAT, item_names.REAPER), self.player)
- or state.has_all({item_names.HELLION, item_names.HELLION_HELLBAT}, self.player)
+ self.terran_bio_heal(state)
+ and (
+ state.has_any((item_names.FIREBAT, item_names.REAPER), self.player)
+ or state.has_all((item_names.HELLION, item_names.HELLION_HELLBAT), self.player)
+ )
)
- and self.terran_bio_heal(state)
- or (self.advanced_tactics and state.has_any({item_names.PERDITION_TURRET, item_names.PLANETARY_FORTRESS}, self.player))
+ or (self.advanced_tactics and state.has_any((item_names.PERDITION_TURRET, item_names.PLANETARY_FORTRESS), self.player))
)
and (
# Volatile infesteds
state.has(item_names.LIBERATOR, self.player)
or (
self.advanced_tactics
- and state.has(item_names.VULTURE, self.player)
- or (state.has(item_names.HERC, self.player) and self.terran_bio_heal(state))
+ and (state.has(item_names.VULTURE, self.player)
+ or (state.has(item_names.HERC, self.player) and self.terran_bio_heal(state))
+ )
)
)
)
@@ -3303,14 +3388,25 @@ def enemy_shadow_domination(self, state: CollectionState) -> bool:
def enemy_shadow_first_stage(self, state: CollectionState) -> bool:
return self.enemy_shadow_domination(state) and (
self.grant_story_tech == GrantStoryTech.option_grant
- or ((self.nova_full_stealth(state) and self.enemy_shadow_tripwires_tool(state)) or (self.nova_heal(state) and self.nova_splash(state)))
+ or (self.nova_full_stealth(state)
+ and self.enemy_shadow_tripwires_tool(state)
+ )
+ or (self.nova_heal(state)
+ and self.nova_splash(state)
+ )
)
def enemy_shadow_second_stage(self, state: CollectionState) -> bool:
return self.enemy_shadow_first_stage(state) and (
self.grant_story_tech == GrantStoryTech.option_grant
- or (self.nova_splash(state) or self.nova_heal(state) or self.nova_escape_assist(state))
- and (self.advanced_tactics or state.has(item_names.NOVA_GHOST_VISOR, self.player))
+ or (
+ (self.advanced_tactics or state.has(item_names.NOVA_GHOST_VISOR, self.player))
+ and (
+ self.nova_splash(state)
+ or self.nova_heal(state)
+ or self.nova_escape_assist(state)
+ )
+ )
)
def enemy_shadow_door_controls(self, state: CollectionState) -> bool:
@@ -3495,21 +3591,21 @@ def _has_zerg_units(state: CollectionState) -> bool:
and state.has(item_names.DEVOURER_PRESCIENT_SPORES, self.player)
)
or (
- state.has_any((
- # Mercs with <= 300s first drop time
- item_names.DEVOURING_ONES,
- item_names.HUNTER_KILLERS,
- item_names.CAUSTIC_HORRORS,
- item_names.HUNTERLING,
- ), self.player)
- # + 2 upgrades that allow getting faster/earlier mercs
- and state.count_from_list((
- item_names.UNRESTRICTED_MUTATION,
- item_names.EVOLUTIONARY_LEAP,
- item_names.CELL_DIVISION,
- item_names.SELF_SUFFICIENT,
- ), self.player) >= 2
- )
+ state.has_any((
+ # Mercs with <= 300s first drop time
+ item_names.DEVOURING_ONES,
+ item_names.HUNTER_KILLERS,
+ item_names.CAUSTIC_HORRORS,
+ item_names.HUNTERLING,
+ ), self.player)
+ # + 2 upgrades that allow getting faster/earlier mercs
+ and state.count_from_list((
+ item_names.UNRESTRICTED_MUTATION,
+ item_names.EVOLUTIONARY_LEAP,
+ item_names.CELL_DIVISION,
+ item_names.SELF_SUFFICIENT,
+ ), self.player) >= 2
+ )
)
)
@@ -3603,3 +3699,124 @@ def get_basic_units(logic_level: int, race: SC2Race) -> Set[str]:
return advanced_basic_units[race]
else:
return basic_units[race]
+
+
+# Defense rating table
+# Commented defense ratings are handled in the defense_rating function
+tvx_defense_ratings = {
+ item_names.SIEGE_TANK: 5,
+ # "Graduating Range": 1,
+ item_names.PLANETARY_FORTRESS: 3,
+ # Bunker w/ Marine/Marauder: 3,
+ item_names.PERDITION_TURRET: 2,
+ item_names.DEVASTATOR_TURRET: 2,
+ item_names.VULTURE: 1,
+ item_names.BANSHEE: 1,
+ item_names.BATTLECRUISER: 1,
+ item_names.LIBERATOR: 4,
+ item_names.WIDOW_MINE: 1,
+ # "Concealment (Widow Mine)": 1
+}
+tvz_defense_ratings = {
+ item_names.PERDITION_TURRET: 2,
+ # Bunker w/ Firebat: 2,
+ item_names.LIBERATOR: -2,
+ item_names.HIVE_MIND_EMULATOR: 3,
+ item_names.PSI_DISRUPTER: 3,
+}
+tvx_air_defense_ratings = {
+ item_names.MISSILE_TURRET: 2,
+}
+zvx_defense_ratings = {
+ # Note that this doesn't include Kerrigan because this is just for race swaps, which doesn't involve her (for now)
+ item_names.SPINE_CRAWLER: 3,
+ # w/ Twin Drones: 1
+ item_names.SWARM_QUEEN: 1,
+ item_names.SWARM_HOST: 1,
+ # impaler: 3
+ # "Hardened Tentacle Spines (Impaler)": 2
+ # lurker: 1
+ # "Seismic Spines (Lurker)": 2
+ # "Adapted Spines (Lurker)": 1
+ # brood lord : 2
+ # corpser roach: 1
+ # creep tumors (swarm queen or overseer): 1
+ # w/ malignant creep: 1
+ # tanks with ammo: 5
+ item_names.INFESTED_BUNKER: 3,
+ item_names.BILE_LAUNCHER: 2,
+}
+# zvz_defense_ratings = {
+ # corpser roach: 1
+ # primal igniter: 2
+ # lurker: 1
+ # w/ adapted spines: -1
+ # impaler: -1
+# }
+zvx_air_defense_ratings = {
+ item_names.SPORE_CRAWLER: 2,
+ # w/ Twin Drones: 1
+ item_names.INFESTED_MISSILE_TURRET: 2,
+}
+pvx_defense_ratings = {
+ item_names.PHOTON_CANNON: 2,
+ item_names.KHAYDARIN_MONOLITH: 3,
+ item_names.SHIELD_BATTERY: 1,
+ item_names.NEXUS_OVERCHARGE: 2,
+ item_names.SKYLORD: 1,
+ item_names.MATRIX_OVERLOAD: 1,
+ item_names.COLOSSUS: 1,
+ item_names.VANGUARD: 1,
+ item_names.REAVER: 1,
+}
+pvz_defense_ratings = {
+ item_names.KHAYDARIN_MONOLITH: -2,
+ item_names.COLOSSUS: 1,
+}
+
+terran_passive_ratings = {
+ item_names.AUTOMATED_REFINERY: 4,
+ item_names.COMMAND_CENTER_MULE: 4,
+ item_names.ORBITAL_DEPOTS: 2,
+ item_names.COMMAND_CENTER_COMMAND_CENTER_REACTOR: 2,
+ item_names.COMMAND_CENTER_EXTRA_SUPPLIES: 2,
+ item_names.MICRO_FILTERING: 2,
+ item_names.TECH_REACTOR: 2
+}
+
+zerg_passive_ratings = {
+ item_names.TWIN_DRONES: 7,
+ item_names.AUTOMATED_EXTRACTORS: 4,
+ item_names.VESPENE_EFFICIENCY: 3,
+ item_names.OVERLORD_IMPROVED_OVERLORDS: 4,
+ item_names.MALIGNANT_CREEP: 2
+}
+
+protoss_passive_ratings = {
+ item_names.QUATRO: 4,
+ item_names.ORBITAL_ASSIMILATORS: 4,
+ item_names.AMPLIFIED_ASSIMILATORS: 3,
+ item_names.PROBE_WARPIN: 2,
+ item_names.ELDER_PROBES: 2,
+ item_names.MATRIX_OVERLOAD: 2
+}
+
+soa_energy_ratings = {
+ item_names.SOA_SOLAR_LANCE: 8,
+ item_names.SOA_DEPLOY_FENIX: 7,
+ item_names.SOA_TEMPORAL_FIELD: 6,
+ item_names.SOA_PROGRESSIVE_PROXY_PYLON: 5, # Requires Lvl 2 (Warp in Reinforcements)
+ item_names.SOA_SHIELD_OVERCHARGE: 5,
+ item_names.SOA_ORBITAL_STRIKE: 4
+}
+
+soa_passive_ratings = {
+ item_names.GUARDIAN_SHELL: 4,
+ item_names.OVERWATCH: 2
+}
+
+soa_ultimate_ratings = {
+ item_names.SOA_TIME_STOP: 4,
+ item_names.SOA_PURIFIER_BEAM: 3,
+ item_names.SOA_SOLAR_BOMBARDMENT: 3
+}
diff --git a/worlds/sc2/test/test_generation.py b/worlds/sc2/test/test_generation.py
index 61de392c0c6f..329cd593e1a8 100644
--- a/worlds/sc2/test/test_generation.py
+++ b/worlds/sc2/test/test_generation.py
@@ -18,19 +18,19 @@ def test_explicit_locks_excludes_interact_and_set_flags(self):
world_options = {
**self.ALL_CAMPAIGNS,
'locked_items': {
- item_names.MARINE: 0,
- item_names.MARAUDER: 0,
+ item_names.MARINE: -1,
+ item_names.MARAUDER: -1,
item_names.MEDIVAC: 1,
item_names.FIREBAT: 1,
- item_names.ZEALOT: 0,
+ item_names.ZEALOT: -1,
item_names.PROGRESSIVE_REGENERATIVE_BIO_STEEL: 2,
},
'excluded_items': {
- item_names.MARINE: 0,
- item_names.MARAUDER: 0,
- item_names.MEDIVAC: 0,
+ item_names.MARINE: -1,
+ item_names.MARAUDER: -1,
+ item_names.MEDIVAC: -1,
item_names.FIREBAT: 1,
- item_names.ZERGLING: 0,
+ item_names.ZERGLING: -1,
item_names.PROGRESSIVE_REGENERATIVE_BIO_STEEL: 2,
}
}
@@ -50,38 +50,38 @@ def test_unexcludes_cancel_out_excludes(self):
world_options = {
'grant_story_tech': options.GrantStoryTech.option_grant,
'excluded_items': {
- item_groups.ItemGroupNames.NOVA_EQUIPMENT: 15,
+ item_groups.ItemGroupNames.NOVA_EQUIPMENT: -1,
item_names.MARINE_PROGRESSIVE_STIMPACK: 1,
item_names.MARAUDER_PROGRESSIVE_STIMPACK: 2,
- item_names.MARINE: 0,
- item_names.MARAUDER: 0,
+ item_names.MARINE: -1,
+ item_names.MARAUDER: -1,
item_names.REAPER: 1,
- item_names.DIAMONDBACK: 0,
+ item_names.DIAMONDBACK: -1,
item_names.HELLION: 1,
# Additional excludes to increase the likelihood that unexcluded items actually appear
- item_groups.ItemGroupNames.STARPORT_UNITS: 0,
- item_names.WARHOUND: 0,
- item_names.VULTURE: 0,
- item_names.WIDOW_MINE: 0,
- item_names.THOR: 0,
- item_names.GHOST: 0,
- item_names.SPECTRE: 0,
- item_groups.ItemGroupNames.MENGSK_UNITS: 0,
- item_groups.ItemGroupNames.TERRAN_VETERANCY_UNITS: 0,
+ item_groups.ItemGroupNames.STARPORT_UNITS: -1,
+ item_names.WARHOUND: -1,
+ item_names.VULTURE: -1,
+ item_names.WIDOW_MINE: -1,
+ item_names.THOR: -1,
+ item_names.GHOST: -1,
+ item_names.SPECTRE: -1,
+ item_groups.ItemGroupNames.MENGSK_UNITS: -1,
+ item_groups.ItemGroupNames.TERRAN_VETERANCY_UNITS: -1,
},
'unexcluded_items': {
- item_names.NOVA_PLASMA_RIFLE: 1, # Necessary to pass logic
- item_names.NOVA_PULSE_GRENADES: 0, # Necessary to pass logic
- item_names.NOVA_JUMP_SUIT_MODULE: 0, # Necessary to pass logic
- item_groups.ItemGroupNames.BARRACKS_UNITS: 0,
+ item_names.NOVA_PLASMA_RIFLE: 1, # Necessary to pass logic
+ item_names.NOVA_PULSE_GRENADES: -1, # Necessary to pass logic
+ item_names.NOVA_JUMP_SUIT_MODULE: -1, # Necessary to pass logic
+ item_groups.ItemGroupNames.BARRACKS_UNITS: -1,
item_names.NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE: 1,
item_names.HELLION: 1,
item_names.MARINE_PROGRESSIVE_STIMPACK: 1,
- item_names.MARAUDER_PROGRESSIVE_STIMPACK: 0,
+ item_names.MARAUDER_PROGRESSIVE_STIMPACK: -1,
# Additional unexcludes for logic
- item_names.MEDIVAC: 0,
- item_names.BATTLECRUISER: 0,
- item_names.SCIENCE_VESSEL: 0,
+ item_names.MEDIVAC: -1,
+ item_names.BATTLECRUISER: -1,
+ item_names.SCIENCE_VESSEL: -1,
},
# Terran-only
'enabled_campaigns': {
@@ -103,11 +103,29 @@ def test_unexcludes_cancel_out_excludes(self):
self.assertNotIn(item_names.NOVA_BLAZEFIRE_GUNBLADE, itempool)
self.assertNotIn(item_names.NOVA_ENERGY_SUIT_MODULE, itempool)
+ def test_exclude_2_beats_unexclude_1(self) -> None:
+ world_options = {
+ options.OPTION_NAME[options.ExcludedItems]: {
+ item_names.MARINE: 2,
+ },
+ options.OPTION_NAME[options.UnexcludedItems]: {
+ item_names.MARINE: 1,
+ },
+ # Ensure enough locations that marine doesn't get culled
+ options.OPTION_NAME[options.SelectedRaces]: {
+ SC2Race.TERRAN.get_title(),
+ },
+ options.OPTION_NAME[options.VictoryCache]: 9,
+ }
+ self.generate_world(world_options)
+ itempool = [item.name for item in self.multiworld.itempool]
+ self.assertNotIn(item_names.MARINE, itempool)
+
def test_excluding_groups_excludes_all_items_in_group(self):
world_options = {
- 'excluded_items': [
- item_groups.ItemGroupNames.BARRACKS_UNITS.lower(),
- ]
+ 'excluded_items': {
+ item_groups.ItemGroupNames.BARRACKS_UNITS.lower(): -1,
+ },
}
self.generate_world(world_options)
itempool = [item.name for item in self.multiworld.itempool]
@@ -325,6 +343,53 @@ def test_vanilla_items_only_includes_only_nova_equipment_and_vanilla_and_filler_
if item_data.quantity == 0:
continue
self.assertIn(item_name, item_groups.vanilla_items + item_groups.nova_equipment)
+
+ def test_vanilla_items_only_can_unexclude_items(self) -> None:
+ world_options = {
+ # Ensuring an excess of locations so expected items don't get culled
+ **self.ALL_CAMPAIGNS,
+ 'mission_order': options.MissionOrder.option_grid,
+ 'maximum_campaign_size': options.MaximumCampaignSize.range_end,
+ 'enable_race_swap': options.EnableRaceSwapVariants.option_shuffle_all,
+ 'selected_races': {SC2Race.TERRAN.get_title()},
+ # Options under test
+ 'vanilla_items_only': True,
+ 'unexcluded_items': {
+ item_names.PROGRESSIVE_FIRE_SUPPRESSION_SYSTEM: -1,
+ item_names.WARHOUND: 1,
+ item_groups.ItemGroupNames.TERRAN_STIMPACKS: -1,
+ },
+ # Avoid options that lock non-vanilla items for logic
+ 'required_tactics': options.RequiredTactics.option_any_units,
+ 'mastery_locations': options.MasteryLocations.option_disabled,
+ # Move the unit nerf items from the start inventory to the pool,
+ # else this option could push non-vanilla items past this test
+ 'war_council_nerfs': True,
+ }
+ self.generate_world(world_options)
+ world_items = [item.name for item in self.multiworld.itempool]
+ self.assertTrue(world_items)
+ self.assertNotIn(item_names.MARAUDER_MAGRAIL_MUNITIONS, world_items)
+ self.assertEqual(world_items.count(item_names.PROGRESSIVE_FIRE_SUPPRESSION_SYSTEM), 2)
+ self.assertIn(item_names.WARHOUND, world_items)
+ self.assertIn(item_names.MARAUDER_PROGRESSIVE_STIMPACK, world_items)
+ self.assertIn(item_names.REAPER_PROGRESSIVE_STIMPACK, world_items)
+
+ def test_vanilla_items_only_and_exclude_op_items_together_allow_one_level_of_regen_biosteel(self) -> None:
+ world_options = {
+ # Ensuring an excess of locations so expected items don't get culled
+ **self.ALL_CAMPAIGNS,
+ 'mission_order': options.MissionOrder.option_grid,
+ 'maximum_campaign_size': options.MaximumCampaignSize.range_end,
+ 'enable_race_swap': options.EnableRaceSwapVariants.option_shuffle_all,
+ 'selected_races': {SC2Race.TERRAN.get_title()},
+ # Options under test
+ 'vanilla_items_only': True,
+ 'exclude_overpowered_items': True,
+ }
+ self.generate_world(world_options)
+ world_items = [item.name for item in self.multiworld.itempool]
+ self.assertEqual(world_items.count(item_names.PROGRESSIVE_REGENERATIVE_BIO_STEEL), 1)
def test_evil_awoken_with_vanilla_items_only_generates(self) -> None:
world_options = {
@@ -416,12 +481,12 @@ def test_excluding_zerg_units_with_morphling_enabled_doesnt_exclude_aspects(self
},
'required_tactics': options.RequiredTactics.option_no_logic,
'enable_morphling': options.EnableMorphling.option_true,
- 'excluded_items': [
- item_groups.ItemGroupNames.ZERG_UNITS.lower()
- ],
- 'unexcluded_items': [
- item_groups.ItemGroupNames.ZERG_MORPHS.lower()
- ]
+ 'excluded_items': {
+ item_groups.ItemGroupNames.ZERG_UNITS.lower(): -1,
+ },
+ 'unexcluded_items': {
+ item_groups.ItemGroupNames.ZERG_MORPHS.lower(): -1,
+ },
}
self.generate_world(world_options)
itempool = [item.name for item in self.multiworld.itempool]
@@ -439,12 +504,12 @@ def test_excluding_zerg_units_with_morphling_disabled_should_exclude_aspects(sel
},
'required_tactics': options.RequiredTactics.option_no_logic,
'enable_morphling': options.EnableMorphling.option_false,
- 'excluded_items': [
- item_groups.ItemGroupNames.ZERG_UNITS.lower()
- ],
- 'unexcluded_items': [
- item_groups.ItemGroupNames.ZERG_MORPHS.lower()
- ]
+ 'excluded_items': {
+ item_groups.ItemGroupNames.ZERG_UNITS.lower(): -1,
+ },
+ 'unexcluded_items': {
+ item_groups.ItemGroupNames.ZERG_MORPHS.lower(): -1,
+ },
}
self.generate_world(world_options)
itempool = [item.name for item in self.multiworld.itempool]
@@ -473,14 +538,14 @@ def test_deprecated_orbital_command_not_present(self) -> None:
def test_planetary_orbital_module_not_present_without_cc_spells(self) -> None:
world_options = {
- "excluded_items": [
- item_names.COMMAND_CENTER_MULE,
- item_names.COMMAND_CENTER_SCANNER_SWEEP,
- item_names.COMMAND_CENTER_EXTRA_SUPPLIES
- ],
- "locked_items": [
- item_names.PLANETARY_FORTRESS
- ]
+ "excluded_items": {
+ item_names.COMMAND_CENTER_MULE: -1,
+ item_names.COMMAND_CENTER_SCANNER_SWEEP: -1,
+ item_names.COMMAND_CENTER_EXTRA_SUPPLIES: -1,
+ },
+ "locked_items": {
+ item_names.PLANETARY_FORTRESS: -1,
+ }
}
self.generate_world(world_options)
@@ -884,10 +949,10 @@ def test_locking_required_items(self):
}
},
'grant_story_levels': options.GrantStoryLevels.option_additive,
- 'excluded_items': [
- item_names.KERRIGAN_LEAPING_STRIKE,
- item_names.KERRIGAN_MEND,
- ]
+ 'excluded_items': {
+ item_names.KERRIGAN_LEAPING_STRIKE: -1,
+ item_names.KERRIGAN_MEND: -1,
+ }
}
self.generate_world(world_options)
itempool = [item.name for item in self.multiworld.itempool]
@@ -1161,7 +1226,7 @@ def test_exclude_locked_overpowered_items(self) -> None:
'mission_order': MissionOrder.option_grid,
'maximum_campaign_size': MaximumCampaignSize.range_end,
'exclude_overpowered_items': ExcludeOverpoweredItems.option_true,
- 'locked_items': [locked_item],
+ 'locked_items': {locked_item: -1},
'enable_race_swap': options.EnableRaceSwapVariants.option_shuffle_all,
'selected_races': [SC2Race.TERRAN.get_title()],
}
@@ -1202,7 +1267,7 @@ def test_unreleased_item_quantity_locked(self) -> None:
'maximum_campaign_size': MaximumCampaignSize.range_end,
'exclude_overpowered_items': ExcludeOverpoweredItems.option_false,
'enable_race_swap': options.EnableRaceSwapVariants.option_shuffle_all,
- 'locked_items': {item_name: 0 for item_name in unreleased_items},
+ 'locked_items': {item_name: -1 for item_name in unreleased_items},
}
self.generate_world(world_options)
@@ -1217,7 +1282,7 @@ def test_merc_excluded_excludes_merc_upgrades(self) -> None:
**self.ALL_CAMPAIGNS,
'mission_order': MissionOrder.option_grid,
'maximum_campaign_size': MaximumCampaignSize.range_end,
- 'excluded_items': [item_name for item_name in item_groups.terran_mercenaries],
+ 'excluded_items': {item_name: -1 for item_name in item_groups.terran_mercenaries},
'enable_race_swap': options.EnableRaceSwapVariants.option_shuffle_all,
'selected_races': [SC2Race.TERRAN.get_title()],
}
@@ -1233,7 +1298,7 @@ def test_unexcluded_items_applies_over_op_items(self) -> None:
'mission_order': MissionOrder.option_grid,
'maximum_campaign_size': MaximumCampaignSize.range_end,
'exclude_overpowered_items': ExcludeOverpoweredItems.option_true,
- 'unexcluded_items': [item_names.SOA_TIME_STOP],
+ 'unexcluded_items': {item_names.SOA_TIME_STOP: -1},
'enable_race_swap': options.EnableRaceSwapVariants.option_shuffle_all,
}
@@ -1275,7 +1340,7 @@ def test_terran_nobuild_sections_get_marine_medic_upgrades_with_units_excluded(s
'enabled_campaigns': {
SC2Campaign.WOL.campaign_name
},
- 'excluded_items': [item_names.MARINE, item_names.MEDIC],
+ 'excluded_items': {item_names.MARINE: -1, item_names.MEDIC: -1},
'shuffle_no_build': False,
'required_tactics': RequiredTactics.option_standard
}
diff --git a/worlds/sc2/test/test_item_filtering.py b/worlds/sc2/test/test_item_filtering.py
index 7f8251c52a59..bebd29cf6a3d 100644
--- a/worlds/sc2/test/test_item_filtering.py
+++ b/worlds/sc2/test/test_item_filtering.py
@@ -11,7 +11,7 @@ class ItemFilterTests(Sc2SetupTestBase):
def test_excluding_all_barracks_units_excludes_infantry_upgrades(self) -> None:
world_options = {
'excluded_items': {
- item_groups.ItemGroupNames.BARRACKS_UNITS: 0
+ item_groups.ItemGroupNames.BARRACKS_UNITS: -1,
},
'required_tactics': 'standard',
'min_number_of_upgrades': 1,
diff --git a/worlds/sc2/test/test_usecases.py b/worlds/sc2/test/test_usecases.py
index bf79dbea010d..08d0cb4cc82a 100644
--- a/worlds/sc2/test/test_usecases.py
+++ b/worlds/sc2/test/test_usecases.py
@@ -35,10 +35,10 @@ def test_terran_with_nco_units_only_generates(self):
SC2Campaign.NCO.campaign_name
},
'excluded_items': {
- item_groups.ItemGroupNames.TERRAN_UNITS: 0,
+ item_groups.ItemGroupNames.TERRAN_UNITS: -1,
},
'unexcluded_items': {
- item_groups.ItemGroupNames.NCO_UNITS: 0,
+ item_groups.ItemGroupNames.NCO_UNITS: -1,
},
'max_number_of_upgrades': 2,
}
@@ -81,10 +81,10 @@ def test_terran_with_nco_upgrades_units_only_generates(self):
},
'mission_order': options.MissionOrder.option_vanilla_shuffled,
'excluded_items': {
- item_groups.ItemGroupNames.TERRAN_ITEMS: 0,
+ item_groups.ItemGroupNames.TERRAN_ITEMS: -1,
},
'unexcluded_items': {
- item_groups.ItemGroupNames.NCO_MAX_PROGRESSIVE_ITEMS: 0,
+ item_groups.ItemGroupNames.NCO_MAX_PROGRESSIVE_ITEMS: -1,
item_groups.ItemGroupNames.NCO_MIN_PROGRESSIVE_ITEMS: 1,
},
'excluded_missions': [
@@ -398,7 +398,7 @@ def test_spear_of_adun_max_active_abilities(self):
self.generate_world(world_options)
world_item_names = [item.name for item in self.multiworld.itempool]
- spear_of_adun_actives = [item_name for item_name in world_item_names if item_name in item_tables.spear_of_adun_calldowns]
+ spear_of_adun_actives = [item_name for item_name in world_item_names if item_name in item_groups.spear_of_adun_actives]
self.assertLessEqual(len(spear_of_adun_actives), target_number)
@@ -418,7 +418,9 @@ def test_spear_of_adun_max_autocasts(self):
self.generate_world(world_options)
world_item_names = [item.name for item in self.multiworld.itempool]
- spear_of_adun_autocasts = [item_name for item_name in world_item_names if item_name in item_tables.spear_of_adun_castable_passives]
+ spear_of_adun_autocasts = [
+ item_name for item_name in world_item_names if item_name in item_groups.spear_of_adun_passives
+ ]
self.assertLessEqual(len(spear_of_adun_autocasts), target_number)
@@ -471,12 +473,12 @@ def test_mercs_only(self) -> None:
],
'required_tactics': options.RequiredTactics.option_any_units,
'excluded_items': {
- item_groups.ItemGroupNames.TERRAN_UNITS: 0,
- item_groups.ItemGroupNames.ZERG_UNITS: 0,
+ item_groups.ItemGroupNames.TERRAN_UNITS: -1,
+ item_groups.ItemGroupNames.ZERG_UNITS: -1,
},
'unexcluded_items': {
- item_groups.ItemGroupNames.TERRAN_MERCENARIES: 0,
- item_groups.ItemGroupNames.ZERG_MERCENARIES: 0,
+ item_groups.ItemGroupNames.TERRAN_MERCENARIES: -1,
+ item_groups.ItemGroupNames.ZERG_MERCENARIES: -1,
},
'start_inventory': {
item_names.PROGRESSIVE_FAST_DELIVERY: 1,
diff --git a/worlds/shapez/__init__.py b/worlds/shapez/__init__.py
index 5557e2a96a6e..67d6c22cf378 100644
--- a/worlds/shapez/__init__.py
+++ b/worlds/shapez/__init__.py
@@ -306,8 +306,8 @@ def create_regions(self) -> None:
self.location_count = len(self.included_locations)
# Create regions and entrances based on included locations and player options
- self.multiworld.regions.extend(create_shapez_regions(self.player, self.multiworld,
- bool(self.options.allow_floating_layers.value),
+ has_floating = self.options.allow_floating_layers.value or not (self.options.randomize_level_requirements and self.options.randomize_upgrade_requirements)
+ self.multiworld.regions.extend(create_shapez_regions(self.player, self.multiworld, has_floating,
self.included_locations, self.location_name_to_id,
self.level_logic, self.upgrade_logic,
self.options.early_balancer_tunnel_and_trash.current_key,
diff --git a/worlds/shapez/regions.py b/worlds/shapez/regions.py
index b5835461d875..e4d755a5386c 100644
--- a/worlds/shapez/regions.py
+++ b/worlds/shapez/regions.py
@@ -101,7 +101,7 @@ def has_x_belt_multiplier(state: CollectionState, player: int, needed: float) ->
def has_logic_list_building(state: CollectionState, player: int, buildings: list[str], index: int,
- includeuseful: bool) -> bool:
+ includeuseful: bool, floating: bool) -> bool:
# Includes balancer, tunnel, and trash in logic in order to make them appear in earlier spheres
if includeuseful and not (state.has(ITEMS.trash, player) and has_balancer(state, player) and
@@ -109,7 +109,7 @@ def has_logic_list_building(state: CollectionState, player: int, buildings: list
return False
if buildings[index] == ITEMS.cutter:
- if buildings.index(ITEMS.stacker) < index:
+ if buildings.index(ITEMS.stacker) < index and not floating:
return state.has_any((ITEMS.cutter, ITEMS.cutter_quad), player)
else:
return can_cut_half(state, player)
@@ -195,38 +195,38 @@ def create_shapez_regions(player: int, multiworld: MultiWorld, floating: bool,
# Progressively connect level and upgrade regions
regions[REGIONS.main].connect(
regions[REGIONS.levels_1], "Using first level building",
- lambda state: has_logic_list_building(state, player, level_logic_buildings, 0, False))
+ lambda state: has_logic_list_building(state, player, level_logic_buildings, 0, False, floating))
regions[REGIONS.levels_1].connect(
regions[REGIONS.levels_2], "Using second level building",
- lambda state: has_logic_list_building(state, player, level_logic_buildings, 1, False))
+ lambda state: has_logic_list_building(state, player, level_logic_buildings, 1, False, floating))
regions[REGIONS.levels_2].connect(
regions[REGIONS.levels_3], "Using third level building",
lambda state: has_logic_list_building(state, player, level_logic_buildings, 2,
- early_useful == OPTIONS.buildings_3))
+ early_useful == OPTIONS.buildings_3, floating))
regions[REGIONS.levels_3].connect(
regions[REGIONS.levels_4], "Using fourth level building",
- lambda state: has_logic_list_building(state, player, level_logic_buildings, 3, False))
+ lambda state: has_logic_list_building(state, player, level_logic_buildings, 3, False, floating))
regions[REGIONS.levels_4].connect(
regions[REGIONS.levels_5], "Using fifth level building",
lambda state: has_logic_list_building(state, player, level_logic_buildings, 4,
- early_useful == OPTIONS.buildings_5))
+ early_useful == OPTIONS.buildings_5, floating))
regions[REGIONS.main].connect(
regions[REGIONS.upgrades_1], "Using first upgrade building",
- lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 0, False))
+ lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 0, False, floating))
regions[REGIONS.upgrades_1].connect(
regions[REGIONS.upgrades_2], "Using second upgrade building",
- lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 1, False))
+ lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 1, False, floating))
regions[REGIONS.upgrades_2].connect(
regions[REGIONS.upgrades_3], "Using third upgrade building",
lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 2,
- early_useful == OPTIONS.buildings_3))
+ early_useful == OPTIONS.buildings_3, floating))
regions[REGIONS.upgrades_3].connect(
regions[REGIONS.upgrades_4], "Using fourth upgrade building",
- lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 3, False))
+ lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 3, False, floating))
regions[REGIONS.upgrades_4].connect(
regions[REGIONS.upgrades_5], "Using fifth upgrade building",
lambda state: has_logic_list_building(state, player, upgrade_logic_buildings, 4,
- early_useful == OPTIONS.buildings_5))
+ early_useful == OPTIONS.buildings_5, floating))
# Connect Uncolored shapesanity regions to Main
regions[REGIONS.main].connect(
diff --git a/worlds/sm/docs/multiworld_en.md b/worlds/sm/docs/multiworld_en.md
index 8f30630bc96d..2fa8207acc4d 100644
--- a/worlds/sm/docs/multiworld_en.md
+++ b/worlds/sm/docs/multiworld_en.md
@@ -124,9 +124,8 @@ You only have to do these steps once. Note, RetroArch 1.9.x will not work as it
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
-
+ Network Command Port at 55355. \
+ 
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
diff --git a/worlds/smw/docs/setup_en.md b/worlds/smw/docs/setup_en.md
index 825f0954c8f1..a18dddd20aa6 100644
--- a/worlds/smw/docs/setup_en.md
+++ b/worlds/smw/docs/setup_en.md
@@ -100,9 +100,8 @@ You only have to do these steps once. Note, RetroArch 1.9.x will not work as it
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
-
+ Network Command Port at 55355. \
+ 
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
diff --git a/worlds/smz3/docs/multiworld_en.md b/worlds/smz3/docs/multiworld_en.md
index 38c410faee02..e9b8495f80ec 100644
--- a/worlds/smz3/docs/multiworld_en.md
+++ b/worlds/smz3/docs/multiworld_en.md
@@ -120,9 +120,8 @@ You only have to do these steps once. Note, RetroArch 1.9.x will not work as it
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
-
+ Network Command Port at 55355. \
+ 
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
diff --git a/worlds/soe/__init__.py b/worlds/soe/__init__.py
index 161c749fd6bd..8de83da7eace 100644
--- a/worlds/soe/__init__.py
+++ b/worlds/soe/__init__.py
@@ -20,7 +20,8 @@
from BaseClasses import MultiWorld, CollectionState
__all__ = ["pyevermizer", "SoEWorld"]
-
+__version__ = "0.50.1"
+__author__ = "black-sliver"
"""
In evermizer:
@@ -461,7 +462,7 @@ def generate_output(self, output_directory: str) -> None:
except FileNotFoundError:
pass
- def modify_multidata(self, multidata: typing.Dict[str, typing.Any]) -> None:
+ def modify_multidata(self, multidata: typing.Mapping[str, typing.Any]) -> None:
# wait for self.connect_name to be available.
self.connect_name_available_event.wait()
# we skip in case of error, so that the original error in the output thread is the one that gets raised
diff --git a/worlds/soe/archipelago.json b/worlds/soe/archipelago.json
new file mode 100644
index 000000000000..5a3ca8353e8a
--- /dev/null
+++ b/worlds/soe/archipelago.json
@@ -0,0 +1,8 @@
+{
+ "game": "Secret of Evermore",
+ "authors": [
+ "black-sliver"
+ ],
+ "world_version": "0.50.1",
+ "minimum_ap_version": "0.4.2"
+}
\ No newline at end of file
diff --git a/worlds/soe/docs/multiworld_en.md b/worlds/soe/docs/multiworld_en.md
index 9378626df4f6..5908b2e78a1e 100644
--- a/worlds/soe/docs/multiworld_en.md
+++ b/worlds/soe/docs/multiworld_en.md
@@ -108,8 +108,8 @@ You only have to do these steps once.
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
+ Network Command Port at 55355. \
+ 
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
diff --git a/worlds/soe/logic.py b/worlds/soe/logic.py
index 92ffb14b3f95..30633d2fcccd 100644
--- a/worlds/soe/logic.py
+++ b/worlds/soe/logic.py
@@ -16,9 +16,14 @@
# Logic.items are all items and extra items excluding non-progression items and duplicates
# NOTE: we are skipping sniff items here because none of them is supposed to provide progression
item_names: Set[str] = set()
-items = [item for item in filter(lambda item: item.progression, # type: ignore[arg-type]
- chain(pyevermizer.get_items(), pyevermizer.get_extra_items()))
- if item.name not in item_names and not item_names.add(item.name)] # type: ignore[func-returns-value]
+items = [
+ item
+ for item in filter(
+ lambda item: item.progression,
+ chain(pyevermizer.get_items(), pyevermizer.get_extra_items()),
+ )
+ if item.name not in item_names and not item_names.add(item.name) # type: ignore[func-returns-value]
+]
class SoEPlayerLogic:
diff --git a/worlds/soe/test/test_manifest.py b/worlds/soe/test/test_manifest.py
new file mode 100644
index 000000000000..64dfbdf8c823
--- /dev/null
+++ b/worlds/soe/test/test_manifest.py
@@ -0,0 +1,14 @@
+import json
+from pathlib import Path
+from unittest import TestCase, skipUnless
+
+
+@skipUnless((Path(__file__).parent.parent / "tools" / "make_manifest.py").exists(), "Packaged without tools")
+class ManifestTest(TestCase):
+ def test_manifest_is_up_to_date(self) -> None:
+ from ..tools.make_manifest import make_manifest
+
+ expected_manifest = make_manifest()
+ with (Path(__file__).parent.parent / "archipelago.json").open("r", encoding="utf-8") as f:
+ actual_manifest = json.load(f)
+ self.assertEqual(actual_manifest, expected_manifest, "Manifest is not up to date")
diff --git a/worlds/soe/tools/__init__.py b/worlds/soe/tools/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/worlds/soe/tools/make_manifest.py b/worlds/soe/tools/make_manifest.py
new file mode 100644
index 000000000000..34e9c42b0593
--- /dev/null
+++ b/worlds/soe/tools/make_manifest.py
@@ -0,0 +1,42 @@
+"""Create archipelago.json manifest file. Run as `python -m worlds.soe.tools.make_manifest`."""
+
+import json
+from importlib.metadata import metadata
+from pathlib import Path
+from typing import Any
+
+from .. import SoEWorld, __version__ as world_version, __author__ as world_author
+
+
+__all__ = ["make_manifest"]
+
+
+def make_manifest() -> dict[str, Any]:
+ """
+ Generate and return manifest dict for the Secret of Evermore APWorld.
+
+ The world version is supposed to be equal to the pyevermizer version, but we may have to break that in the future
+ if we ever do a breaking change to the world after we are >= 1.0.0, or we release multiple versions of the APWorld
+ for a single pyevermizer version.
+ """
+ meta = metadata("pyevermizer")
+ version = meta["Version"]
+ authors = list(dict.fromkeys(map(str.rstrip, (world_author + "," + meta["Author"]).split(","))))
+ assert world_version == version, f"Expected world version ({world_version}) == pyevermizer version ({version})."
+
+ return {
+ "game": SoEWorld.game,
+ "authors": authors,
+ "world_version": world_version,
+ "minimum_ap_version": "0.4.2", # introduction of settings API
+ }
+
+
+if __name__ == "__main__":
+ assert SoEWorld.__file__, "Could not determine world source."
+ module_dir = Path(SoEWorld.__file__).parent
+ assert module_dir.is_dir(), f"{module_dir} is not a directory"
+ manifest_path = module_dir / "archipelago.json"
+
+ with manifest_path.open("w", encoding="utf-8") as f:
+ json.dump(make_manifest(), f, indent=4)
diff --git a/worlds/stardew_valley/archipelago.json b/worlds/stardew_valley/archipelago.json
new file mode 100644
index 000000000000..e47d48324870
--- /dev/null
+++ b/worlds/stardew_valley/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Stardew Valley",
+ "authors": ["KaitoKid", "Jouramie", "Witchybun (Mod Support)", "Exempt-Medic (Proofreading)"],
+ "minimum_ap_version": "0.6.4",
+ "world_version": "6.0.0"
+}
diff --git a/worlds/terraria/docs/setup_en.md b/worlds/terraria/docs/setup_en.md
index b41595533743..69f531901957 100644
--- a/worlds/terraria/docs/setup_en.md
+++ b/worlds/terraria/docs/setup_en.md
@@ -50,7 +50,6 @@ on the Archipelago website to generate a YAML using a graphical interface.
significantly more difficult with this mod, so it is recommended to choose a lower difficulty than you normally would
play on.
4. Open the world in single player or multiplayer.
-5. When you're ready, open chat, and enter `/apstart` to start the game.
## Commands
diff --git a/worlds/timespinner/archipelago.json b/worlds/timespinner/archipelago.json
new file mode 100644
index 000000000000..97b7cd32b6ee
--- /dev/null
+++ b/worlds/timespinner/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Timespinner",
+ "authors": ["Jarno"],
+ "minimum_ap_version": "0.6.3",
+ "world_version": "1.38.0"
+}
diff --git a/worlds/timespinner/docs/setup_de.md b/worlds/timespinner/docs/setup_de.md
index e86474744676..62c634516534 100644
--- a/worlds/timespinner/docs/setup_de.md
+++ b/worlds/timespinner/docs/setup_de.md
@@ -42,7 +42,7 @@ Weitere Informationen zum Randomizer findest du hier: [ReadMe](https://github.co
## Woher bekomme ich eine Konfigurationsdatei?
-Die [Player Options](https://archipelago.gg/games/Timespinner/player-options) Seite auf der Website erlaubt dir,
+Die [Player Options](/games/Timespinner/player-options) Seite auf der Website erlaubt dir,
persönliche Einstellungen zu definieren und diese in eine Konfigurationsdatei zu exportieren
* Die Timespinner Randomizer Option "StinkyMaw" ist in Archipelago Seeds aktuell immer an
diff --git a/worlds/tloz/archipelago.json b/worlds/tloz/archipelago.json
new file mode 100644
index 000000000000..3b7e07bf0715
--- /dev/null
+++ b/worlds/tloz/archipelago.json
@@ -0,0 +1,5 @@
+{
+ "game": "The Legend of Zelda",
+ "world_version": "1.0.0",
+ "authors": ["Rosalie"]
+}
diff --git a/worlds/tunic/archipelago.json b/worlds/tunic/archipelago.json
index 4c4d8dd93109..7a1929a00d35 100644
--- a/worlds/tunic/archipelago.json
+++ b/worlds/tunic/archipelago.json
@@ -1,6 +1,6 @@
{
"game": "TUNIC",
- "authors": ["SilentSR", "ScipioWright"],
+ "authors": ["silent-destroyer", "ScipioWright"],
"minimum_ap_version": "0.6.4",
- "world_version": "4.1.0"
+ "world_version": "4.2.7"
}
diff --git a/worlds/tunic/er_rules.py b/worlds/tunic/er_rules.py
index ad26e10c9e9e..ecee659272a8 100644
--- a/worlds/tunic/er_rules.py
+++ b/worlds/tunic/er_rules.py
@@ -1047,7 +1047,7 @@ def get_paired_portal(portal_sd: str) -> tuple[str, str]:
connecting_region=regions["Rooted Ziggurat Portal Room"])
regions["Rooted Ziggurat Portal Room"].connect(
connecting_region=regions["Rooted Ziggurat Portal"],
- rule=lambda state: has_fuses("Activate Ziggurat Fuse", state, world) and has_ability(prayer, state, world))
+ rule=lambda state: has_ability(prayer, state, world))
regions["Rooted Ziggurat Portal Room"].connect(
connecting_region=regions["Rooted Ziggurat Portal Room Exit"],
diff --git a/worlds/tunic/options.py b/worlds/tunic/options.py
index f705979a4436..e873e230572b 100644
--- a/worlds/tunic/options.py
+++ b/worlds/tunic/options.py
@@ -50,8 +50,8 @@ class AbilityShuffling(DefaultOnToggle):
class Lanternless(Toggle):
"""
- Choose whether you require the Lantern for dark areas.
- When enabled, the Lantern is marked as Useful instead of Progression.
+ When enabled, you may be required to navigate dark areas without the Lantern.
+ The Lantern is marked as Useful instead of Progression.
"""
internal_name = "lanternless"
display_name = "Lanternless"
@@ -59,8 +59,8 @@ class Lanternless(Toggle):
class Maskless(Toggle):
"""
- Choose whether you require the Scavenger's Mask for Lower Quarry.
- When enabled, the Scavenger's Mask is marked as Useful instead of Progression.
+ When enabled, you may be required to traverse Lower Quarry without the Scavenger's Mask.
+ The Scavenger's Mask is marked as Useful instead of Progression.
"""
internal_name = "maskless"
display_name = "Maskless"
@@ -200,8 +200,8 @@ class ShuffleLadders(Toggle):
class ShuffleFuses(Toggle):
"""
- Praying at a fuse will reward a check instead of turning on the power. The power from each fuse gets turned into an
- item that must be found in order to restore power for that part of the path.
+ Praying at a fuse will reward a check instead of turning on the power.
+ The power from each fuse gets turned into an item that must be found in order to restore power for that part of the path.
"""
internal_name = "shuffle_fuses"
display_name = "Shuffle Fuses"
diff --git a/worlds/tunic/ut_stuff.py b/worlds/tunic/ut_stuff.py
index 9096f037d8e5..45caf4da9a77 100644
--- a/worlds/tunic/ut_stuff.py
+++ b/worlds/tunic/ut_stuff.py
@@ -23,6 +23,7 @@ def setup_options_from_slot_data(world: "TunicWorld") -> None:
world.options.maskless.value = world.passthrough["maskless"]
world.options.hexagon_quest.value = world.passthrough["hexagon_quest"]
world.options.hexagon_quest_ability_type.value = world.passthrough.get("hexagon_quest_ability_type", 0)
+ world.options.hexagon_goal.value = world.passthrough["Hexagon Quest Goal"]
world.options.entrance_rando.value = world.passthrough["entrance_rando"]
world.options.shuffle_ladders.value = world.passthrough["shuffle_ladders"]
world.options.shuffle_fuses.value = world.passthrough.get("shuffle_fuses", 0)
diff --git a/worlds/tww/TWWClient.py b/worlds/tww/TWWClient.py
index 84173659f5a2..cb4c25685d92 100644
--- a/worlds/tww/TWWClient.py
+++ b/worlds/tww/TWWClient.py
@@ -146,8 +146,8 @@ def __init__(self, server_address: Optional[str], password: Optional[str]) -> No
self.curr_stage_switches_bitfield: int
self.curr_stage_pickups_bitfield: int
- # Keep track of whether the player has yet received their first progressive magic meter.
- self.received_magic: bool = False
+ # Keep track of when the player received their first progressive magic meter.
+ self.received_magic_idx: int = -1
# A dictionary that maps salvage locations to their sunken treasure bit.
self.salvage_locations_map: dict[str, int] = {}
@@ -349,10 +349,10 @@ def _give_item(ctx: TWWContext, item_name: str) -> bool:
if slot == 0xFF:
# Special case: Use a different item ID for the second progressive magic meter.
if item_name == "Progressive Magic Meter":
- if ctx.received_magic:
+ if ctx.received_magic_idx == -1:
+ ctx.received_magic_idx = idx
+ elif idx > ctx.received_magic_idx:
item_id = 0xB2
- else:
- ctx.received_magic = True
dolphin_memory_engine.write_byte(GIVE_ITEM_ARRAY_ADDR + idx, item_id)
return True
diff --git a/worlds/wargroove/Client.py b/worlds/wargroove/Client.py
index 0627c7e9f2df..ac80e85005e1 100644
--- a/worlds/wargroove/Client.py
+++ b/worlds/wargroove/Client.py
@@ -178,7 +178,7 @@ def __init__(self, server_address, password):
self.remove_communication_files()
atexit.register(self.remove_communication_files)
if not os.path.isdir(appdata_wargroove):
- print_error_and_close("WargrooveClient couldn't find Wargoove in appdata!"
+ print_error_and_close("WargrooveClient couldn't find Wargoove in appdata! "
"Boot Wargroove and then close it to attempt to fix this error")
mods_directory = os.path.join(appdata_wargroove, "mods", "ArchipelagoMod")
save_directory = os.path.join(appdata_wargroove, "save")
diff --git a/worlds/wargroove/archipelago.json b/worlds/wargroove/archipelago.json
new file mode 100644
index 000000000000..d6e158219245
--- /dev/null
+++ b/worlds/wargroove/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Wargroove",
+ "world_version": "1.0.0",
+ "minimum_ap_version": "0.6.3",
+ "authors": ["Fly Hyping", "Magnemania"]
+}
diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py
index 6aaf2fdc0a17..3d80fd245875 100644
--- a/worlds/witness/__init__.py
+++ b/worlds/witness/__init__.py
@@ -81,7 +81,7 @@ class WitnessWorld(World):
item_name_groups = static_witness_items.ITEM_GROUPS
location_name_groups = static_witness_locations.AREA_LOCATION_GROUPS
- required_client_version = (0, 6, 0)
+ required_client_version = (0, 6, 4)
player_logic: WitnessPlayerLogic
player_locations: WitnessPlayerLocations
diff --git a/worlds/witness/archipelago.json b/worlds/witness/archipelago.json
new file mode 100644
index 000000000000..c623452d243a
--- /dev/null
+++ b/worlds/witness/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "The Witness",
+ "minimum_ap_version": "0.6.4",
+ "world_version": "8.0.0",
+ "authors": ["NewSoupVi", "blastron"]
+}
diff --git a/worlds/witness/hints.py b/worlds/witness/hints.py
index ac5572257fc3..f04c1f6d3738 100644
--- a/worlds/witness/hints.py
+++ b/worlds/witness/hints.py
@@ -705,6 +705,8 @@ def get_compact_hint_args(hint: WitnessWordedHint, local_player_number: int) ->
Area Hint: 1st Arg is the amount of area progression and hunt panels. 2nd Arg is the name of the area.
Location Hint: 1st Arg is the location's address, second arg is the player number the location belongs to.
+ In case of a local vague hint, the second arg is a string with the containing location group.
+ In case of a nonlocal vague hint, the second arg is the *negative* player number.
Junk Hint: 1st Arg is -1, second arg is this slot's player number.
"""
@@ -724,10 +726,13 @@ def get_compact_hint_args(hint: WitnessWordedHint, local_player_number: int) ->
# Is location hint
if location and location.address is not None:
- if hint.vague_location_hint and location.player == local_player_number:
- assert hint.area is not None # A local vague location hint should have an area argument
- return location.address, "containing_area:" + hint.area
- return location.address, location.player # Scouting does not matter for other players (currently)
+ if hint.vague_location_hint:
+ if location.player == local_player_number:
+ assert hint.area is not None # A local vague location hint should have an area argument
+ return location.address, "containing_area:" + hint.area
+ return location.address, - location.player # Encode non-local vague hint as negative player number
+
+ return location.address, location.player
# Is junk / undefined hint
return -1, local_player_number
diff --git a/worlds/yachtdice/archipelago.json b/worlds/yachtdice/archipelago.json
new file mode 100644
index 000000000000..a651c6b6c363
--- /dev/null
+++ b/worlds/yachtdice/archipelago.json
@@ -0,0 +1,6 @@
+{
+ "game": "Yacht Dice",
+ "authors": ["Spineraks"],
+ "minimum_ap_version": "0.6.1",
+ "world_version": "2.1.4"
+}
diff --git a/worlds/yoshisisland/Rules.py b/worlds/yoshisisland/Rules.py
index 68d4f29a7381..57805fd7dfed 100644
--- a/worlds/yoshisisland/Rules.py
+++ b/worlds/yoshisisland/Rules.py
@@ -34,7 +34,7 @@ def set_easy_rules(world: "YoshisIslandWorld") -> None:
set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "Beanstalk"}, player))
set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Red Coins", player), lambda state: state.has_all({"Flashing Eggs", "Spring Ball", "Chomp Rock", "Beanstalk"}, player))
- set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Stars", player), lambda state: logic.has_midring(state) or (logic.cansee_clouds and state.has_all({"Spring Ball", "Chomp Rock", "Beanstalk"}, player)))
+ set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Stars", player), lambda state: logic.has_midring(state) or (logic.cansee_clouds(state) and state.has_all({"Spring Ball", "Chomp Rock", "Beanstalk"}, player)))
set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Red Coins", player), lambda state: state.has("Platform Ghost", player))
set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Flowers", player), lambda state: state.has("Platform Ghost", player))
@@ -127,7 +127,7 @@ def set_easy_rules(world: "YoshisIslandWorld") -> None:
set_rule(world.multiworld.get_location("Marching Milde's Fort: Flowers", player), lambda state: state.has_all({"Dashed Stairs", "Vanishing Arrow Wheel", "Arrow Wheel", "Bucket"}, player) and (state.has("Egg Capacity Upgrade", player, 1) or logic.combat_item(state)))
set_rule(world.multiworld.get_location("Marching Milde's Fort: Stars", player), lambda state: state.has("Dashed Stairs", player) and (logic.has_midring(state) or state.has("Vanishing Arrow Wheel", player) or logic.cansee_clouds(state)))
- set_rule(world.multiworld.get_location("Chomp Rock Zone: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "Chomp Rock"}, player))
+ set_rule(world.multiworld.get_location("Chomp Rock Zone: Red Coins", player), lambda state: state.has_all({"Large Spring Ball", "Chomp Rock", "Dashed Platform", "! Switch"}, player))
set_rule(world.multiworld.get_location("Chomp Rock Zone: Flowers", player), lambda state: state.has_all({"Chomp Rock", "! Switch", "Spring Ball", "Dashed Platform"}, player))
set_rule(world.multiworld.get_location("Chomp Rock Zone: Stars", player), lambda state: state.has_all({"Chomp Rock", "! Switch", "Spring Ball", "Dashed Platform"}, player))
@@ -262,7 +262,7 @@ def set_normal_rules(world: "YoshisIslandWorld") -> None:
set_rule(world.multiworld.get_location("Shy-Guys On Stilts: Level Clear", player), lambda state: state.has_all({"Large Spring Ball", "Beanstalk"}, player))
set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Red Coins", player), lambda state: state.has_all({"Flashing Eggs", "Spring Ball", "Chomp Rock", "Beanstalk"}, player))
- set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Stars", player), lambda state: logic.has_midring(state) or (logic.cansee_clouds and state.has_all({"Spring Ball", "Chomp Rock", "Beanstalk"}, player)))
+ set_rule(world.multiworld.get_location("Touch Fuzzy Get Dizzy: Stars", player), lambda state: logic.has_midring(state) or (logic.cansee_clouds(state) and state.has_all({"Spring Ball", "Chomp Rock", "Beanstalk"}, player)))
set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Red Coins", player), lambda state: state.has("Platform Ghost", player))
set_rule(world.multiworld.get_location("Salvo The Slime's Castle: Flowers", player), lambda state: state.has("Platform Ghost", player))
@@ -610,3 +610,5 @@ def set_hard_extra_rules(world: "YoshisIslandWorld") -> None:
set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Flowers", player), lambda state: state.has(("Large Spring Ball"), player))
set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Stars", player), lambda state: True)
set_rule(world.multiworld.get_location("Castles - Masterpiece Set: Level Clear", player), lambda state: state.has(("Large Spring Ball"), player))
+
+
diff --git a/worlds/yoshisisland/archipelago.json b/worlds/yoshisisland/archipelago.json
new file mode 100644
index 000000000000..8b5a465e36a5
--- /dev/null
+++ b/worlds/yoshisisland/archipelago.json
@@ -0,0 +1,5 @@
+{"game": "Yoshi's Island",
+"authors": ["Pink Switch"],
+"minimum_ap_version": "0.6.3",
+
+"world_version": "1.0.0"}
diff --git a/worlds/yugioh06/boosterpacks.py b/worlds/yugioh06/boosterpacks.py
index 645977d28def..7fbb56a83895 100644
--- a/worlds/yugioh06/boosterpacks.py
+++ b/worlds/yugioh06/boosterpacks.py
@@ -423,7 +423,7 @@
"Kaiser Glider",
"Horus the Black Flame Dragon LV6",
"Luster Dragon",
- "Luster Dragon #2"
+ "Luster Dragon #2",
"Spear Dragon",
"Armed Dragon LV3",
"Armed Dragon LV5",
@@ -634,7 +634,7 @@
"Mystic Swordsman LV6",
"Horus the Black Flame Dragon LV6",
"Horus the Black Flame Dragon LV4",
- "Armed Dragon LV3"
+ "Armed Dragon LV3",
"Armed Dragon LV5",
"Silent Swordsman Lv3",
"Silent Swordsman Lv5",
@@ -750,7 +750,7 @@
"Formation Union",
"Princess Pikeru",
"Skull Zoma",
- "Metal Reflect Slime"
+ "Metal Reflect Slime",
"Level Up!",
"Howling Insect",
"Tribute Doll",
diff --git a/worlds/yugioh06/rom.py b/worlds/yugioh06/rom.py
index 3ac10f9ea496..a61c3a6f4242 100644
--- a/worlds/yugioh06/rom.py
+++ b/worlds/yugioh06/rom.py
@@ -145,7 +145,7 @@ def get_base_rom_bytes(file_name: str = "") -> bytes:
md5hash = basemd5.hexdigest()
if MD5Europe != md5hash and MD5America != md5hash:
raise Exception(
- "Supplied Base Rom does not match known MD5 for"
+ "Supplied Base Rom does not match known MD5 for "
"Yu-Gi-Oh! World Championship 2006 America or Europe "
"Get the correct game and version, then dump it"
)
diff --git a/worlds/yugioh06/rules.py b/worlds/yugioh06/rules.py
index 0b46e0b5d0b0..ce61fa18d2aa 100644
--- a/worlds/yugioh06/rules.py
+++ b/worlds/yugioh06/rules.py
@@ -668,7 +668,7 @@ def only_dragon(state, player):
], player) and (state.count_from_list_unique([
"Luster Dragon",
"Spear Dragon",
- "Cave Dragon"
+ "Cave Dragon",
"Armed Dragon LV3",
"Masked Dragon",
"Twin-Headed Behemoth",
diff --git a/worlds/zillion/docs/setup_en.md b/worlds/zillion/docs/setup_en.md
index c8e29fc36cde..dba7f069a12d 100644
--- a/worlds/zillion/docs/setup_en.md
+++ b/worlds/zillion/docs/setup_en.md
@@ -20,9 +20,8 @@ RetroArch 1.9.x will not work, as it is older than 1.10.3.
- "Sega - MS/GG/MD/CD (Genesis Plus GX)
3. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
4. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
- Network Command Port at 55355.
-
-
+ Network Command Port at 55355. \
+ 
### Linux Setup