diff --git a/.gitignore b/.gitignore index 5686f43de38..791f7b1bb7f 100644 --- a/.gitignore +++ b/.gitignore @@ -150,7 +150,7 @@ venv/ ENV/ env.bak/ venv.bak/ -.code-workspace +*.code-workspace shell.nix # Spyder project settings diff --git a/BaseClasses.py b/BaseClasses.py index 88857f80321..1c7dad7f3b9 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -680,13 +680,13 @@ def can_reach_entrance(self, spot: str, player: int) -> bool: def can_reach_region(self, spot: str, player: int) -> bool: return self.multiworld.get_region(spot, player).can_reach(self) - def sweep_for_events(self, key_only: bool = False, locations: Optional[Iterable[Location]] = None) -> None: + def sweep_for_events(self, locations: Optional[Iterable[Location]] = None) -> None: if locations is None: locations = self.multiworld.get_filled_locations() reachable_events = True # since the loop has a good chance to run more than once, only filter the events once - locations = {location for location in locations if location.advancement and location not in self.events and - not key_only or getattr(location.item, "locked_dungeon_item", False)} + locations = {location for location in locations if location.advancement and location not in self.events} + while reachable_events: reachable_events = {location for location in locations if location.can_reach(self)} locations -= reachable_events @@ -1291,8 +1291,6 @@ def create_playthrough(self, create_paths: bool = True) -> None: state = CollectionState(multiworld) collection_spheres = [] while required_locations: - state.sweep_for_events(key_only=True) - sphere = set(filter(state.can_reach, required_locations)) for location in sphere: diff --git a/CommonClient.py b/CommonClient.py index f8d1fcb7a22..09937e4b9ab 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -61,6 +61,7 @@ def _cmd_connect(self, address: str = "") -> bool: if address: self.ctx.server_address = None self.ctx.username = None + self.ctx.password = None elif not self.ctx.server_address: self.output("Please specify an address.") return False @@ -514,6 +515,7 @@ def update_permissions(self, permissions: typing.Dict[str, int]): async def shutdown(self): self.server_address = "" self.username = None + self.password = None self.cancel_autoreconnect() if self.server and not self.server.socket.closed: await self.server.socket.close() diff --git a/Fill.py b/Fill.py index 4967ff07360..5185bbb60ee 100644 --- a/Fill.py +++ b/Fill.py @@ -646,7 +646,6 @@ def balance_multiworld_progression(multiworld: MultiWorld) -> None: def get_sphere_locations(sphere_state: CollectionState, locations: typing.Set[Location]) -> typing.Set[Location]: - sphere_state.sweep_for_events(key_only=True, locations=locations) return {loc for loc in locations if sphere_state.can_reach(loc)} def item_percentage(player: int, num: int) -> float: diff --git a/Main.py b/Main.py index de6b467f93d..56b3a6545db 100644 --- a/Main.py +++ b/Main.py @@ -124,14 +124,19 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No for player in multiworld.player_ids: exclusion_rules(multiworld, player, multiworld.worlds[player].options.exclude_locations.value) multiworld.worlds[player].options.priority_locations.value -= multiworld.worlds[player].options.exclude_locations.value + world_excluded_locations = set() for location_name in multiworld.worlds[player].options.priority_locations.value: try: location = multiworld.get_location(location_name, player) - except KeyError as e: # failed to find the given location. Check if it's a legitimate location - if location_name not in multiworld.worlds[player].location_name_to_id: - raise Exception(f"Unable to prioritize location {location_name} in player {player}'s world.") from e - else: + except KeyError: + continue + + if location.progress_type != LocationProgressType.EXCLUDED: location.progress_type = LocationProgressType.PRIORITY + else: + logger.warning(f"Unable to prioritize location \"{location_name}\" in player {player}'s world because the world excluded it.") + world_excluded_locations.add(location_name) + multiworld.worlds[player].options.priority_locations.value -= world_excluded_locations # Set local and non-local item rules. if multiworld.players > 1: diff --git a/WebHostLib/options.py b/WebHostLib/options.py index 33339daa198..15b7bd61cee 100644 --- a/WebHostLib/options.py +++ b/WebHostLib/options.py @@ -231,6 +231,13 @@ def generate_yaml(game: str): del options[key] + # Detect keys which end with -range, indicating a NamedRange with a possible custom value + elif key_parts[-1].endswith("-range"): + if options[key_parts[-1][:-6]] == "custom": + options[key_parts[-1][:-6]] = val + + del options[key] + # Detect random-* keys and set their options accordingly for key, val in options.copy().items(): if key.startswith("random-"): diff --git a/WebHostLib/templates/playerOptions/macros.html b/WebHostLib/templates/playerOptions/macros.html index 415739b861a..30a4fc78dff 100644 --- a/WebHostLib/templates/playerOptions/macros.html +++ b/WebHostLib/templates/playerOptions/macros.html @@ -54,7 +54,7 @@ {% macro NamedRange(option_name, option) %} {{ OptionTitle(option_name, option) }}
- {% for key, val in option.special_range_names.items() %} {% if option.default == val %} @@ -64,17 +64,17 @@ {% endfor %} -
+
- + {{ option.default | default(option.range_start) if option.default != "random" else option.range_start }} {{ RandomizeButton(option_name, option) }} diff --git a/WebHostLib/templates/playerOptions/playerOptions.html b/WebHostLib/templates/playerOptions/playerOptions.html index aeb6e864a54..73de5d56eb2 100644 --- a/WebHostLib/templates/playerOptions/playerOptions.html +++ b/WebHostLib/templates/playerOptions/playerOptions.html @@ -11,7 +11,7 @@ diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index 8e567afc35f..75b5fb0202d 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -79,7 +79,7 @@ def __init__(self, room: Room): # Normal lookup tables as well. self.item_name_to_id[game] = game_package["item_name_to_id"] - self.location_name_to_id[game] = game_package["item_name_to_id"] + self.location_name_to_id[game] = game_package["location_name_to_id"] def get_seed_name(self) -> str: """Retrieves the seed name.""" diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index 3b40d7e77a7..ab841e65ee4 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -1,8 +1,8 @@ # Archipelago World Code Owners / Maintainers Document # -# This file is used to notate the current "owners" or "maintainers" of any currently merged world folder. For any pull -# requests that modify these worlds, a code owner must approve the PR in addition to a core maintainer. This is not to -# be used for files/folders outside the /worlds folder, those will always need sign off from a core maintainer. +# This file is used to notate the current "owners" or "maintainers" of any currently merged world folder as well as +# certain documentation. For any pull requests that modify these worlds/docs, a code owner must approve the PR in +# addition to a core maintainer. All other files and folders are owned and maintained by core maintainers directly. # # All usernames must be GitHub usernames (and are case sensitive). @@ -226,3 +226,11 @@ # Ori and the Blind Forest # /worlds_disabled/oribf/ + +################### +## Documentation ## +################### + +# Apworld Dev Faq +/docs/apworld_dev_faq.md @qwint @ScipioWright + diff --git a/docs/apworld_dev_faq.md b/docs/apworld_dev_faq.md new file mode 100644 index 00000000000..8d9429afa32 --- /dev/null +++ b/docs/apworld_dev_faq.md @@ -0,0 +1,45 @@ +# APWorld Dev FAQ + +This document is meant as a reference tool to show solutions to common problems when developing an apworld. +It is not intended to answer every question about Archipelago and it assumes you have read the other docs, +including [Contributing](contributing.md), [Adding Games](), and [World API](). + +--- + +### My game has a restrictive start that leads to fill errors + +Hint to the Generator that an item needs to be in sphere one with local_early_items. Here, `1` represents the number of "Sword" items to attempt to place in sphere one. +```py +early_item_name = "Sword" +self.multiworld.local_early_items[self.player][early_item_name] = 1 +``` + +Some alternative ways to try to fix this problem are: +* Add more locations to sphere one of your world, potentially only when there would be a restrictive start +* Pre-place items yourself, such as during `create_items` +* Put items into the player's starting inventory using `push_precollected` +* Raise an exception, such as an `OptionError` during `generate_early`, to disallow options that would lead to a restrictive start + +--- + +### I have multiple settings that change the item/location pool counts and need to balance them out + +In an ideal situation your system for producing locations and items wouldn't leave any opportunity for them to be unbalanced. But in real, complex situations, that might be unfeasible. + +If that's the case, you can create extra filler based on the difference between your unfilled locations and your itempool by comparing [get_unfilled_locations](https://github.com/ArchipelagoMW/Archipelago/blob/main/BaseClasses.py#:~:text=get_unfilled_locations) to your list of items to submit + +Note: to use self.create_filler(), self.get_filler_item_name() should be defined to only return valid filler item names +```py +total_locations = len(self.multiworld.get_unfilled_locations(self.player)) +item_pool = self.create_non_filler_items() + +for _ in range(total_locations - len(item_pool)): + item_pool.append(self.create_filler()) + +self.multiworld.itempool += item_pool +``` + +A faster alternative to the `for` loop would be to use a [list comprehension](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions): +```py +item_pool += [self.create_filler() for _ in range(total_locations - len(item_pool))] +``` diff --git a/kvui.py b/kvui.py index 500203a8818..f83590a819d 100644 --- a/kvui.py +++ b/kvui.py @@ -595,8 +595,9 @@ def command_button_action(self, button): "!help for server commands.") def connect_button_action(self, button): + self.ctx.username = None + self.ctx.password = None if self.ctx.server: - self.ctx.username = None async_start(self.ctx.disconnect()) else: async_start(self.ctx.connect(self.server_connect_bar.text.replace("/connect ", ""))) @@ -836,6 +837,10 @@ def _handle_color(self, node: JSONMessagePart): return self._handle_text(node) def _handle_text(self, node: JSONMessagePart): + # All other text goes through _handle_color, and we don't want to escape markup twice, + # or mess up text that already has intentional markup applied to it + if node.get("type", "text") == "text": + node["text"] = escape_markup(node["text"]) for ref in node.get("refs", []): node["text"] = f"[ref={self.ref_count}|{ref}]{node['text']}[/ref]" self.ref_count += 1 diff --git a/settings.py b/settings.py index 7ab618c344d..79277052145 100644 --- a/settings.py +++ b/settings.py @@ -3,6 +3,7 @@ This is different from player options. """ +import os import os.path import shutil import sys @@ -11,7 +12,6 @@ from enum import IntEnum from threading import Lock from typing import cast, Any, BinaryIO, ClassVar, Dict, Iterator, List, Optional, TextIO, Tuple, Union, TypeVar -import os __all__ = [ "get_settings", "fmt_doc", "no_gui", @@ -798,6 +798,7 @@ def autosave() -> None: atexit.register(autosave) def save(self, location: Optional[str] = None) -> None: # as above + from Utils import parse_yaml location = location or self._filename assert location, "No file specified" temp_location = location + ".tmp" # not using tempfile to test expected file access @@ -807,10 +808,18 @@ def save(self, location: Optional[str] = None) -> None: # as above # can't use utf-8-sig because it breaks backward compat: pyyaml on Windows with bytes does not strip the BOM with open(temp_location, "w", encoding="utf-8") as f: self.dump(f) - # replace old with new - if os.path.exists(location): + f.flush() + if hasattr(os, "fsync"): + os.fsync(f.fileno()) + # validate new file is valid yaml + with open(temp_location, encoding="utf-8") as f: + parse_yaml(f.read()) + # replace old with new, try atomic operation first + try: + os.rename(temp_location, location) + except (OSError, FileExistsError): os.unlink(location) - os.rename(temp_location, location) + os.rename(temp_location, location) self._filename = location def dump(self, f: TextIO, level: int = 0) -> None: @@ -832,7 +841,6 @@ def get_settings() -> Settings: with _lock: # make sure we only have one instance res = getattr(get_settings, "_cache", None) if not res: - import os from Utils import user_path, local_path filenames = ("options.yaml", "host.yaml") locations: List[str] = [] diff --git a/test/general/test_host_yaml.py b/test/general/test_host_yaml.py index 7174befca42..3edbd34a51c 100644 --- a/test/general/test_host_yaml.py +++ b/test/general/test_host_yaml.py @@ -1,11 +1,12 @@ import os +import os.path import unittest from io import StringIO -from tempfile import TemporaryFile +from tempfile import TemporaryDirectory, TemporaryFile from typing import Any, Dict, List, cast import Utils -from settings import Settings, Group +from settings import Group, Settings, ServerOptions class TestIDs(unittest.TestCase): @@ -80,3 +81,27 @@ class AGroup(Group): self.assertEqual(value_spaces[2], value_spaces[0]) # start of sub-list self.assertGreater(value_spaces[3], value_spaces[0], f"{value_lines[3]} should have more indentation than {value_lines[0]} in {lines}") + + +class TestSettingsSave(unittest.TestCase): + def test_save(self) -> None: + """Test that saving and updating works""" + with TemporaryDirectory() as d: + filename = os.path.join(d, "host.yaml") + new_release_mode = ServerOptions.ReleaseMode("enabled") + # create default host.yaml + settings = Settings(None) + settings.save(filename) + self.assertTrue(os.path.exists(filename), + "Default settings could not be saved") + self.assertNotEqual(settings.server_options.release_mode, new_release_mode, + "Unexpected default release mode") + # update host.yaml + settings.server_options.release_mode = new_release_mode + settings.save(filename) + self.assertFalse(os.path.exists(filename + ".tmp"), + "Temp file was not removed during save") + # read back host.yaml + settings = Settings(filename) + self.assertEqual(settings.server_options.release_mode, new_release_mode, + "Settings were not overwritten") diff --git a/worlds/ahit/Items.py b/worlds/ahit/Items.py index 3ef83fe81e6..54c6e6b5d39 100644 --- a/worlds/ahit/Items.py +++ b/worlds/ahit/Items.py @@ -39,7 +39,7 @@ def create_itempool(world: "HatInTimeWorld") -> List[Item]: continue else: if name == "Scooter Badge": - if world.options.CTRLogic is CTRLogic.option_scooter or get_difficulty(world) >= Difficulty.MODERATE: + if world.options.CTRLogic == CTRLogic.option_scooter or get_difficulty(world) >= Difficulty.MODERATE: item_type = ItemClassification.progression elif name == "No Bonk Badge" and world.is_dw(): item_type = ItemClassification.progression diff --git a/worlds/ahit/Regions.py b/worlds/ahit/Regions.py index c6aeaa35779..8cb3782bdec 100644 --- a/worlds/ahit/Regions.py +++ b/worlds/ahit/Regions.py @@ -659,6 +659,10 @@ def is_valid_act_combo(world: "HatInTimeWorld", entrance_act: Region, if exit_act.name not in chapter_finales: return False + exit_chapter: str = act_chapters.get(exit_act.name) + # make sure that certain time rift combinations never happen + always_block: bool = exit_chapter != "Mafia Town" and exit_chapter != "Subcon Forest" + if not ignore_certain_rules or always_block: if entrance_act.name in rift_access_regions and exit_act.name in rift_access_regions[entrance_act.name]: return False @@ -684,9 +688,12 @@ def is_valid_first_act(world: "HatInTimeWorld", act: Region) -> bool: if act.name not in guaranteed_first_acts: return False + if world.options.ActRandomizer == ActRandomizer.option_light and "Time Rift" in act.name: + return False + # If there's only a single level in the starting chapter, only allow Mafia Town or Subcon Forest levels start_chapter = world.options.StartingChapter - if start_chapter is ChapterIndex.ALPINE or start_chapter is ChapterIndex.SUBCON: + if start_chapter == ChapterIndex.ALPINE or start_chapter == ChapterIndex.SUBCON: if "Time Rift" in act.name: return False @@ -723,7 +730,8 @@ def is_valid_first_act(world: "HatInTimeWorld", act: Region) -> bool: elif act.name == "Contractual Obligations" and world.options.ShuffleSubconPaintings: return False - if world.options.ShuffleSubconPaintings and act_chapters.get(act.name, "") == "Subcon Forest": + if world.options.ShuffleSubconPaintings and "Time Rift" not in act.name \ + and act_chapters.get(act.name, "") == "Subcon Forest": # Only allow Subcon levels if painting skips are allowed if diff < Difficulty.MODERATE or world.options.NoPaintingSkips: return False diff --git a/worlds/ahit/Rules.py b/worlds/ahit/Rules.py index b0513c43328..b716b793a79 100644 --- a/worlds/ahit/Rules.py +++ b/worlds/ahit/Rules.py @@ -1,7 +1,6 @@ from worlds.AutoWorld import CollectionState from worlds.generic.Rules import add_rule, set_rule -from .Locations import location_table, zipline_unlocks, is_location_valid, contract_locations, \ - shop_locations, event_locs +from .Locations import location_table, zipline_unlocks, is_location_valid, shop_locations, event_locs from .Types import HatType, ChapterIndex, hat_type_to_item, Difficulty, HitType from BaseClasses import Location, Entrance, Region from typing import TYPE_CHECKING, List, Callable, Union, Dict @@ -148,14 +147,14 @@ def set_rules(world: "HatInTimeWorld"): if world.is_dlc1(): chapter_list.append(ChapterIndex.CRUISE) - if world.is_dlc2() and final_chapter is not ChapterIndex.METRO: + if world.is_dlc2() and final_chapter != ChapterIndex.METRO: chapter_list.append(ChapterIndex.METRO) chapter_list.remove(starting_chapter) world.random.shuffle(chapter_list) # Make sure Alpine is unlocked before any DLC chapters are, as the Alpine door needs to be open to access them - if starting_chapter is not ChapterIndex.ALPINE and (world.is_dlc1() or world.is_dlc2()): + if starting_chapter != ChapterIndex.ALPINE and (world.is_dlc1() or world.is_dlc2()): index1 = 69 index2 = 69 pos: int @@ -165,7 +164,7 @@ def set_rules(world: "HatInTimeWorld"): if world.is_dlc1(): index1 = chapter_list.index(ChapterIndex.CRUISE) - if world.is_dlc2() and final_chapter is not ChapterIndex.METRO: + if world.is_dlc2() and final_chapter != ChapterIndex.METRO: index2 = chapter_list.index(ChapterIndex.METRO) lowest_index = min(index1, index2) @@ -242,9 +241,6 @@ def set_rules(world: "HatInTimeWorld"): if not is_location_valid(world, key): continue - if key in contract_locations.keys(): - continue - loc = world.multiworld.get_location(key, world.player) for hat in data.required_hats: @@ -256,7 +252,7 @@ def set_rules(world: "HatInTimeWorld"): if data.paintings > 0 and world.options.ShuffleSubconPaintings: add_rule(loc, lambda state, paintings=data.paintings: has_paintings(state, world, paintings)) - if data.hit_type is not HitType.none and world.options.UmbrellaLogic: + if data.hit_type != HitType.none and world.options.UmbrellaLogic: if data.hit_type == HitType.umbrella: add_rule(loc, lambda state: state.has("Umbrella", world.player)) @@ -518,7 +514,7 @@ def set_hard_rules(world: "HatInTimeWorld"): lambda state: can_use_hat(state, world, HatType.ICE)) # Hard: clear Rush Hour with Brewing Hat only - if world.options.NoTicketSkips is not NoTicketSkips.option_true: + if world.options.NoTicketSkips != NoTicketSkips.option_true: set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player), lambda state: can_use_hat(state, world, HatType.BREWING)) else: diff --git a/worlds/ahit/__init__.py b/worlds/ahit/__init__.py index 15140379b96..dd5e88abbc6 100644 --- a/worlds/ahit/__init__.py +++ b/worlds/ahit/__init__.py @@ -1,15 +1,16 @@ from BaseClasses import Item, ItemClassification, Tutorial, Location, MultiWorld from .Items import item_table, create_item, relic_groups, act_contracts, create_itempool, get_shop_trap_name, \ - calculate_yarn_costs + calculate_yarn_costs, alps_hooks from .Regions import create_regions, randomize_act_entrances, chapter_act_info, create_events, get_shuffled_region from .Locations import location_table, contract_locations, is_location_valid, get_location_names, TASKSANITY_START_ID, \ get_total_locations -from .Rules import set_rules +from .Rules import set_rules, has_paintings from .Options import AHITOptions, slot_data_options, adjust_options, RandomizeHatOrder, EndGoal, create_option_groups -from .Types import HatType, ChapterIndex, HatInTimeItem, hat_type_to_item +from .Types import HatType, ChapterIndex, HatInTimeItem, hat_type_to_item, Difficulty from .DeathWishLocations import create_dw_regions, dw_classes, death_wishes from .DeathWishRules import set_dw_rules, create_enemy_events, hit_list, bosses from worlds.AutoWorld import World, WebWorld, CollectionState +from worlds.generic.Rules import add_rule from typing import List, Dict, TextIO from worlds.LauncherComponents import Component, components, icon_paths, launch_subprocess, Type from Utils import local_path @@ -86,19 +87,27 @@ def generate_early(self): if self.is_dw_only(): return - # If our starting chapter is 4 and act rando isn't on, force hookshot into inventory - # If starting chapter is 3 and painting shuffle is enabled, and act rando isn't, give one free painting unlock - start_chapter: ChapterIndex = ChapterIndex(self.options.StartingChapter) - - if start_chapter == ChapterIndex.ALPINE or start_chapter == ChapterIndex.SUBCON: - if not self.options.ActRandomizer: - if start_chapter == ChapterIndex.ALPINE: - self.multiworld.push_precollected(self.create_item("Hookshot Badge")) - if self.options.UmbrellaLogic: - self.multiworld.push_precollected(self.create_item("Umbrella")) - - if start_chapter == ChapterIndex.SUBCON and self.options.ShuffleSubconPaintings: + # Take care of some extremely restrictive starts in other chapters with act shuffle off + if not self.options.ActRandomizer: + start_chapter = self.options.StartingChapter + if start_chapter == ChapterIndex.ALPINE: + self.multiworld.push_precollected(self.create_item("Hookshot Badge")) + if self.options.UmbrellaLogic: + self.multiworld.push_precollected(self.create_item("Umbrella")) + + if self.options.ShuffleAlpineZiplines: + ziplines = list(alps_hooks.keys()) + ziplines.remove("Zipline Unlock - The Twilight Bell Path") # not enough checks from this one + self.multiworld.push_precollected(self.create_item(self.random.choice(ziplines))) + elif start_chapter == ChapterIndex.SUBCON: + if self.options.ShuffleSubconPaintings: self.multiworld.push_precollected(self.create_item("Progressive Painting Unlock")) + elif start_chapter == ChapterIndex.BIRDS: + if self.options.UmbrellaLogic: + if self.options.LogicDifficulty < Difficulty.EXPERT: + self.multiworld.push_precollected(self.create_item("Umbrella")) + elif self.options.LogicDifficulty < Difficulty.MODERATE: + self.multiworld.push_precollected(self.create_item("Umbrella")) def create_regions(self): # noinspection PyClassVar @@ -119,7 +128,10 @@ def create_regions(self): # place vanilla contract locations if contract shuffle is off if not self.options.ShuffleActContracts: for name in contract_locations.keys(): - self.multiworld.get_location(name, self.player).place_locked_item(create_item(self, name)) + loc = self.get_location(name) + loc.place_locked_item(create_item(self, name)) + if self.options.ShuffleSubconPaintings and loc.name != "Snatcher's Contract - The Subcon Well": + add_rule(loc, lambda state: has_paintings(state, self, 1)) def create_items(self): if self.has_yarn(): @@ -317,7 +329,7 @@ def collect(self, state: "CollectionState", item: "Item") -> bool: def remove(self, state: "CollectionState", item: "Item") -> bool: old_count: int = state.count(item.name, self.player) - change = super().collect(state, item) + change = super().remove(state, item) if change and old_count == 1: if "Stamp" in item.name: if "2 Stamp" in item.name: diff --git a/worlds/ahit/docs/setup_en.md b/worlds/ahit/docs/setup_en.md index 509869fc256..23b34907071 100644 --- a/worlds/ahit/docs/setup_en.md +++ b/worlds/ahit/docs/setup_en.md @@ -12,41 +12,29 @@ ## Instructions -1. Have Steam running. Open the Steam console with this link: [steam://open/console](steam://open/console) -This may not work for some browsers. If that's the case, and you're on Windows, open the Run dialog using Win+R, -paste the link into the box, and hit Enter. +1. **BACK UP YOUR SAVE FILES IN YOUR MAIN INSTALL IF YOU CARE ABOUT THEM!!!** + Go to `steamapps/common/HatinTime/HatinTimeGame/SaveData/` and copy everything inside that folder over to a safe place. + **This is important! Changing the game version CAN and WILL break your existing save files!!!** -2. In the Steam console, enter the following command: -`download_depot 253230 253232 7770543545116491859`. ***Wait for the console to say the download is finished!*** -This can take a while to finish (30+ minutes) depending on your connection speed, so please be patient. Additionally, -**try to prevent your connection from being interrupted or slowed while Steam is downloading the depot,** -or else the download may potentially become corrupted (see first FAQ issue below). +2. In your Steam library, right-click on **A Hat in Time** in the list of games and click on **Properties**. -3. Once the download finishes, go to `steamapps/content/app_253230` in Steam's program folder. +3. Click the **Betas** tab. In the **Beta Participation** dropdown, select `tcplink`. + While it downloads, you can subscribe to the [Archipelago workshop mod.]((https://steamcommunity.com/sharedfiles/filedetails/?id=3026842601)) -4. There should be a folder named `depot_253232`. Rename it to HatinTime_AP and move it to your `steamapps/common` folder. +4. Once the game finishes downloading, start it up. + In Game Settings, make sure **Enable Developer Console** is checked. -5. In the HatinTime_AP folder, navigate to `Binaries/Win64` and create a new file: `steam_appid.txt`. -In this new text file, input the number **253230** on the first line. - - -6. Create a shortcut of `HatinTimeGame.exe` from that folder and move it to wherever you'd like. -You will use this shortcut to open the Archipelago-compatible version of A Hat in Time. - - -7. Start up the game using your new shortcut. To confirm if you are on the correct version, -go to Settings -> Game Settings. If you don't see an option labelled ***Live Game Events*** you should be running -the correct version of the game. In Game Settings, make sure ***Enable Developer Console*** is checked. +5. You should now be good to go. See below for more details on how to use the mod and connect to an Archipelago game. ## Connecting to the Archipelago server -To connect to the multiworld server, simply run the **ArchipelagoAHITClient** -(or run it from the Launcher if you have the apworld installed) and connect it to the Archipelago server. +To connect to the multiworld server, simply run the **Archipelago AHIT Client** from the Launcher +and connect it to the Archipelago server. The game will connect to the client automatically when you create a new save file. @@ -61,33 +49,8 @@ make sure ***Enable Developer Console*** is checked in Game Settings and press t ## FAQ/Common Issues -### I followed the setup, but I receive an odd error message upon starting the game or creating a save file! -If you receive an error message such as -**"Failed to find default engine .ini to retrieve My Documents subdirectory to use. Force quitting."** or -**"Failed to load map "hub_spaceship"** after booting up the game or creating a save file respectively, then the depot -download was likely corrupted. The only way to fix this is to start the entire download all over again. -Unfortunately, this appears to be an underlying issue with Steam's depot downloader. The only way to really prevent this -from happening is to ensure that your connection is not interrupted or slowed while downloading. - -### The game keeps crashing on startup after the splash screen! -This issue is unfortunately very hard to fix, and the underlying cause is not known. If it does happen however, -try the following: - -- Close Steam **entirely**. -- Open the downpatched version of the game (with Steam closed) and allow it to load to the titlescreen. -- Close the game, and then open Steam again. -- After launching the game, the issue should hopefully disappear. If not, repeat the above steps until it does. - -### I followed the setup, but "Live Game Events" still shows up in the options menu! -The most common cause of this is the `steam_appid.txt` file. If you're on Windows 10, file extensions are hidden by -default (thanks Microsoft). You likely made the mistake of still naming the file `steam_appid.txt`, which, since file -extensions are hidden, would result in the file being named `steam_appid.txt.txt`, which is incorrect. -To show file extensions in Windows 10, open any folder, click the View tab at the top, and check -"File name extensions". Then you can correct the name of the file. If the name of the file is correct, -and you're still running into the issue, re-read the setup guide again in case you missed a step. -If you still can't get it to work, ask for help in the Discord thread. - -### The game is running on the older version, but it's not connecting when starting a new save! + +### The game is not connecting when starting a new save! For unknown reasons, the mod will randomly disable itself in the mod menu. To fix this, go to the Mods menu (rocket icon) in-game, and re-enable the mod. diff --git a/worlds/alttp/SubClasses.py b/worlds/alttp/SubClasses.py index 769dcc19985..328e28da934 100644 --- a/worlds/alttp/SubClasses.py +++ b/worlds/alttp/SubClasses.py @@ -76,10 +76,6 @@ def dungeon_item(self) -> Optional[str]: if self.type in {"SmallKey", "BigKey", "Map", "Compass"}: return self.type - @property - def locked_dungeon_item(self): - return self.location.locked and self.dungeon_item - class LTTPRegionType(IntEnum): LightWorld = 1 diff --git a/worlds/bomb_rush_cyberfunk/Locations.py b/worlds/bomb_rush_cyberfunk/Locations.py index 863e2ad020c..7ea95901906 100644 --- a/worlds/bomb_rush_cyberfunk/Locations.py +++ b/worlds/bomb_rush_cyberfunk/Locations.py @@ -762,7 +762,7 @@ class EventDict(TypedDict): 'game_id': "graf385"}, {'name': "Tagged 389 Graffiti Spots", 'stage': Stages.Misc, - 'game_id': "graf379"}, + 'game_id': "graf389"}, ] diff --git a/worlds/dlcquest/__init__.py b/worlds/dlcquest/__init__.py index a9dfcc5044b..b8f2aad6ff9 100644 --- a/worlds/dlcquest/__init__.py +++ b/worlds/dlcquest/__init__.py @@ -8,11 +8,15 @@ from .Options import DLCQuestOptions from .Regions import create_regions from .Rules import set_rules +from .presets import dlcq_options_presets +from .option_groups import dlcq_option_groups client_version = 0 class DLCqwebworld(WebWorld): + options_presets = dlcq_options_presets + option_groups = dlcq_option_groups setup_en = Tutorial( "Multiworld Setup Guide", "A guide to setting up the Archipelago DLCQuest game on your computer.", diff --git a/worlds/dlcquest/option_groups.py b/worlds/dlcquest/option_groups.py new file mode 100644 index 00000000000..9510c061e18 --- /dev/null +++ b/worlds/dlcquest/option_groups.py @@ -0,0 +1,27 @@ +from typing import List + +from Options import ProgressionBalancing, Accessibility, OptionGroup +from .Options import (Campaign, ItemShuffle, TimeIsMoney, EndingChoice, PermanentCoins, DoubleJumpGlitch, CoinSanity, + CoinSanityRange, DeathLink) + +dlcq_option_groups: List[OptionGroup] = [ + OptionGroup("General", [ + Campaign, + ItemShuffle, + CoinSanity, + ]), + OptionGroup("Customization", [ + EndingChoice, + PermanentCoins, + CoinSanityRange, + ]), + OptionGroup("Tedious and Grind", [ + TimeIsMoney, + DoubleJumpGlitch, + ]), + OptionGroup("Advanced Options", [ + DeathLink, + ProgressionBalancing, + Accessibility, + ]), +] diff --git a/worlds/dlcquest/presets.py b/worlds/dlcquest/presets.py new file mode 100644 index 00000000000..ccfd7939952 --- /dev/null +++ b/worlds/dlcquest/presets.py @@ -0,0 +1,68 @@ +from typing import Any, Dict + +from .Options import DoubleJumpGlitch, CoinSanity, CoinSanityRange, PermanentCoins, TimeIsMoney, EndingChoice, Campaign, ItemShuffle + +all_random_settings = { + DoubleJumpGlitch.internal_name: "random", + CoinSanity.internal_name: "random", + CoinSanityRange.internal_name: "random", + PermanentCoins.internal_name: "random", + TimeIsMoney.internal_name: "random", + EndingChoice.internal_name: "random", + Campaign.internal_name: "random", + ItemShuffle.internal_name: "random", + "death_link": "random", +} + +main_campaign_settings = { + DoubleJumpGlitch.internal_name: DoubleJumpGlitch.option_none, + CoinSanity.internal_name: CoinSanity.option_coin, + CoinSanityRange.internal_name: 30, + PermanentCoins.internal_name: PermanentCoins.option_false, + TimeIsMoney.internal_name: TimeIsMoney.option_required, + EndingChoice.internal_name: EndingChoice.option_true, + Campaign.internal_name: Campaign.option_basic, + ItemShuffle.internal_name: ItemShuffle.option_shuffled, +} + +lfod_campaign_settings = { + DoubleJumpGlitch.internal_name: DoubleJumpGlitch.option_none, + CoinSanity.internal_name: CoinSanity.option_coin, + CoinSanityRange.internal_name: 30, + PermanentCoins.internal_name: PermanentCoins.option_false, + TimeIsMoney.internal_name: TimeIsMoney.option_required, + EndingChoice.internal_name: EndingChoice.option_true, + Campaign.internal_name: Campaign.option_live_freemium_or_die, + ItemShuffle.internal_name: ItemShuffle.option_shuffled, +} + +easy_settings = { + DoubleJumpGlitch.internal_name: DoubleJumpGlitch.option_none, + CoinSanity.internal_name: CoinSanity.option_none, + CoinSanityRange.internal_name: 40, + PermanentCoins.internal_name: PermanentCoins.option_true, + TimeIsMoney.internal_name: TimeIsMoney.option_required, + EndingChoice.internal_name: EndingChoice.option_true, + Campaign.internal_name: Campaign.option_both, + ItemShuffle.internal_name: ItemShuffle.option_shuffled, +} + +hard_settings = { + DoubleJumpGlitch.internal_name: DoubleJumpGlitch.option_simple, + CoinSanity.internal_name: CoinSanity.option_coin, + CoinSanityRange.internal_name: 30, + PermanentCoins.internal_name: PermanentCoins.option_false, + TimeIsMoney.internal_name: TimeIsMoney.option_optional, + EndingChoice.internal_name: EndingChoice.option_true, + Campaign.internal_name: Campaign.option_both, + ItemShuffle.internal_name: ItemShuffle.option_shuffled, +} + + +dlcq_options_presets: Dict[str, Dict[str, Any]] = { + "All random": all_random_settings, + "Main campaign": main_campaign_settings, + "LFOD campaign": lfod_campaign_settings, + "Both easy": easy_settings, + "Both hard": hard_settings, +} diff --git a/worlds/factorio/data/mod_template/control.lua b/worlds/factorio/data/mod_template/control.lua index 8ce0b45a5f6..ace231e12b4 100644 --- a/worlds/factorio/data/mod_template/control.lua +++ b/worlds/factorio/data/mod_template/control.lua @@ -660,11 +660,18 @@ commands.add_command("ap-get-technology", "Grant a technology, used by the Archi end local tech local force = game.forces["player"] + if call.parameter == nil then + game.print("ap-get-technology is only to be used by the Archipelago Factorio Client") + return + end chunks = split(call.parameter, "\t") local item_name = chunks[1] local index = chunks[2] local source = chunks[3] or "Archipelago" - if index == -1 then -- for coop sync and restoring from an older savegame + if index == nil then + game.print("ap-get-technology is only to be used by the Archipelago Factorio Client") + return + elseif index == -1 then -- for coop sync and restoring from an older savegame tech = force.technologies[item_name] if tech.researched ~= true then game.print({"", "Received [technology=" .. tech.name .. "] as it is already checked."}) diff --git a/worlds/ffmq/Client.py b/worlds/ffmq/Client.py index 7de486314c6..93688a6116f 100644 --- a/worlds/ffmq/Client.py +++ b/worlds/ffmq/Client.py @@ -71,7 +71,7 @@ async def game_watcher(self, ctx): received = await snes_read(ctx, RECEIVED_DATA[0], RECEIVED_DATA[1]) data = await snes_read(ctx, READ_DATA_START, READ_DATA_END - READ_DATA_START) check_2 = await snes_read(ctx, 0xF53749, 1) - if check_1 in (b'\x00', b'\x55') or check_2 in (b'\x00', b'\x55'): + if check_1 != b'\x01' or check_2 != b'\x01': return def get_range(data_range): diff --git a/worlds/ffmq/Items.py b/worlds/ffmq/Items.py index d0898d7e81c..f1c102d34ef 100644 --- a/worlds/ffmq/Items.py +++ b/worlds/ffmq/Items.py @@ -222,10 +222,10 @@ def yaml_item(text): def create_items(self) -> None: items = [] - starting_weapon = self.multiworld.starting_weapon[self.player].current_key.title().replace("_", " ") + starting_weapon = self.options.starting_weapon.current_key.title().replace("_", " ") self.multiworld.push_precollected(self.create_item(starting_weapon)) self.multiworld.push_precollected(self.create_item("Steel Armor")) - if self.multiworld.sky_coin_mode[self.player] == "start_with": + if self.options.sky_coin_mode == "start_with": self.multiworld.push_precollected(self.create_item("Sky Coin")) precollected_item_names = {item.name for item in self.multiworld.precollected_items[self.player]} @@ -233,28 +233,28 @@ def create_items(self) -> None: def add_item(item_name): if item_name in ["Steel Armor", "Sky Fragment"] or "Progressive" in item_name: return - if item_name.lower().replace(" ", "_") == self.multiworld.starting_weapon[self.player].current_key: + if item_name.lower().replace(" ", "_") == self.options.starting_weapon.current_key: return - if self.multiworld.progressive_gear[self.player]: + if self.options.progressive_gear: for item_group in prog_map: if item_name in self.item_name_groups[item_group]: item_name = prog_map[item_group] break if item_name == "Sky Coin": - if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin": + if self.options.sky_coin_mode == "shattered_sky_coin": for _ in range(40): items.append(self.create_item("Sky Fragment")) return - elif self.multiworld.sky_coin_mode[self.player] == "save_the_crystals": + elif self.options.sky_coin_mode == "save_the_crystals": items.append(self.create_filler()) return if item_name in precollected_item_names: items.append(self.create_filler()) return i = self.create_item(item_name) - if self.multiworld.logic[self.player] != "friendly" and item_name in ("Magic Mirror", "Mask"): + if self.options.logic != "friendly" and item_name in ("Magic Mirror", "Mask"): i.classification = ItemClassification.useful - if (self.multiworld.logic[self.player] == "expert" and self.multiworld.map_shuffle[self.player] == "none" and + if (self.options.logic == "expert" and self.options.map_shuffle == "none" and item_name == "Exit Book"): i.classification = ItemClassification.progression items.append(i) @@ -263,11 +263,11 @@ def add_item(item_name): for item in self.item_name_groups[item_group]: add_item(item) - if self.multiworld.brown_boxes[self.player] == "include": + if self.options.brown_boxes == "include": filler_items = [] for item, count in fillers.items(): filler_items += [self.create_item(item) for _ in range(count)] - if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin": + if self.options.sky_coin_mode == "shattered_sky_coin": self.multiworld.random.shuffle(filler_items) filler_items = filler_items[39:] items += filler_items diff --git a/worlds/ffmq/Options.py b/worlds/ffmq/Options.py index af3625f28a9..41c397315f8 100644 --- a/worlds/ffmq/Options.py +++ b/worlds/ffmq/Options.py @@ -1,4 +1,5 @@ -from Options import Choice, FreeText, Toggle, Range +from Options import Choice, FreeText, Toggle, Range, PerGameCommonOptions +from dataclasses import dataclass class Logic(Choice): @@ -321,36 +322,36 @@ class KaelisMomFightsMinotaur(Toggle): default = 0 -option_definitions = { - "logic": Logic, - "brown_boxes": BrownBoxes, - "sky_coin_mode": SkyCoinMode, - "shattered_sky_coin_quantity": ShatteredSkyCoinQuantity, - "starting_weapon": StartingWeapon, - "progressive_gear": ProgressiveGear, - "leveling_curve": LevelingCurve, - "starting_companion": StartingCompanion, - "available_companions": AvailableCompanions, - "companions_locations": CompanionsLocations, - "kaelis_mom_fight_minotaur": KaelisMomFightsMinotaur, - "companion_leveling_type": CompanionLevelingType, - "companion_spellbook_type": CompanionSpellbookType, - "enemies_density": EnemiesDensity, - "enemies_scaling_lower": EnemiesScalingLower, - "enemies_scaling_upper": EnemiesScalingUpper, - "bosses_scaling_lower": BossesScalingLower, - "bosses_scaling_upper": BossesScalingUpper, - "enemizer_attacks": EnemizerAttacks, - "enemizer_groups": EnemizerGroups, - "shuffle_res_weak_types": ShuffleResWeakType, - "shuffle_enemies_position": ShuffleEnemiesPositions, - "progressive_formations": ProgressiveFormations, - "doom_castle_mode": DoomCastle, - "doom_castle_shortcut": DoomCastleShortcut, - "tweak_frustrating_dungeons": TweakFrustratingDungeons, - "map_shuffle": MapShuffle, - "crest_shuffle": CrestShuffle, - "shuffle_battlefield_rewards": ShuffleBattlefieldRewards, - "map_shuffle_seed": MapShuffleSeed, - "battlefields_battles_quantities": BattlefieldsBattlesQuantities, -} +@dataclass +class FFMQOptions(PerGameCommonOptions): + logic: Logic + brown_boxes: BrownBoxes + sky_coin_mode: SkyCoinMode + shattered_sky_coin_quantity: ShatteredSkyCoinQuantity + starting_weapon: StartingWeapon + progressive_gear: ProgressiveGear + leveling_curve: LevelingCurve + starting_companion: StartingCompanion + available_companions: AvailableCompanions + companions_locations: CompanionsLocations + kaelis_mom_fight_minotaur: KaelisMomFightsMinotaur + companion_leveling_type: CompanionLevelingType + companion_spellbook_type: CompanionSpellbookType + enemies_density: EnemiesDensity + enemies_scaling_lower: EnemiesScalingLower + enemies_scaling_upper: EnemiesScalingUpper + bosses_scaling_lower: BossesScalingLower + bosses_scaling_upper: BossesScalingUpper + enemizer_attacks: EnemizerAttacks + enemizer_groups: EnemizerGroups + shuffle_res_weak_types: ShuffleResWeakType + shuffle_enemies_position: ShuffleEnemiesPositions + progressive_formations: ProgressiveFormations + doom_castle_mode: DoomCastle + doom_castle_shortcut: DoomCastleShortcut + tweak_frustrating_dungeons: TweakFrustratingDungeons + map_shuffle: MapShuffle + crest_shuffle: CrestShuffle + shuffle_battlefield_rewards: ShuffleBattlefieldRewards + map_shuffle_seed: MapShuffleSeed + battlefields_battles_quantities: BattlefieldsBattlesQuantities diff --git a/worlds/ffmq/Output.py b/worlds/ffmq/Output.py index 1b17aaa98f2..1e436a90c5f 100644 --- a/worlds/ffmq/Output.py +++ b/worlds/ffmq/Output.py @@ -1,13 +1,13 @@ import yaml import os import zipfile +import Utils from copy import deepcopy from .Regions import object_id_table -from Utils import __version__ from worlds.Files import APPatch import pkgutil -settings_template = yaml.load(pkgutil.get_data(__name__, "data/settings.yaml"), yaml.Loader) +settings_template = Utils.parse_yaml(pkgutil.get_data(__name__, "data/settings.yaml")) def generate_output(self, output_directory): @@ -21,7 +21,7 @@ def output_item_name(item): item_name = "".join(item_name.split(" ")) else: if item.advancement or item.useful or (item.trap and - self.multiworld.per_slot_randoms[self.player].randint(0, 1)): + self.random.randint(0, 1)): item_name = "APItem" else: item_name = "APItemFiller" @@ -46,60 +46,60 @@ def tf(option): options = deepcopy(settings_template) options["name"] = self.multiworld.player_name[self.player] option_writes = { - "enemies_density": cc(self.multiworld.enemies_density[self.player]), + "enemies_density": cc(self.options.enemies_density), "chests_shuffle": "Include", - "shuffle_boxes_content": self.multiworld.brown_boxes[self.player] == "shuffle", + "shuffle_boxes_content": self.options.brown_boxes == "shuffle", "npcs_shuffle": "Include", "battlefields_shuffle": "Include", - "logic_options": cc(self.multiworld.logic[self.player]), - "shuffle_enemies_position": tf(self.multiworld.shuffle_enemies_position[self.player]), - "enemies_scaling_lower": cc(self.multiworld.enemies_scaling_lower[self.player]), - "enemies_scaling_upper": cc(self.multiworld.enemies_scaling_upper[self.player]), - "bosses_scaling_lower": cc(self.multiworld.bosses_scaling_lower[self.player]), - "bosses_scaling_upper": cc(self.multiworld.bosses_scaling_upper[self.player]), - "enemizer_attacks": cc(self.multiworld.enemizer_attacks[self.player]), - "leveling_curve": cc(self.multiworld.leveling_curve[self.player]), - "battles_quantity": cc(self.multiworld.battlefields_battles_quantities[self.player]) if - self.multiworld.battlefields_battles_quantities[self.player].value < 5 else + "logic_options": cc(self.options.logic), + "shuffle_enemies_position": tf(self.options.shuffle_enemies_position), + "enemies_scaling_lower": cc(self.options.enemies_scaling_lower), + "enemies_scaling_upper": cc(self.options.enemies_scaling_upper), + "bosses_scaling_lower": cc(self.options.bosses_scaling_lower), + "bosses_scaling_upper": cc(self.options.bosses_scaling_upper), + "enemizer_attacks": cc(self.options.enemizer_attacks), + "leveling_curve": cc(self.options.leveling_curve), + "battles_quantity": cc(self.options.battlefields_battles_quantities) if + self.options.battlefields_battles_quantities.value < 5 else "RandomLow" if - self.multiworld.battlefields_battles_quantities[self.player].value == 5 else + self.options.battlefields_battles_quantities.value == 5 else "RandomHigh", - "shuffle_battlefield_rewards": tf(self.multiworld.shuffle_battlefield_rewards[self.player]), + "shuffle_battlefield_rewards": tf(self.options.shuffle_battlefield_rewards), "random_starting_weapon": True, - "progressive_gear": tf(self.multiworld.progressive_gear[self.player]), - "tweaked_dungeons": tf(self.multiworld.tweak_frustrating_dungeons[self.player]), - "doom_castle_mode": cc(self.multiworld.doom_castle_mode[self.player]), - "doom_castle_shortcut": tf(self.multiworld.doom_castle_shortcut[self.player]), - "sky_coin_mode": cc(self.multiworld.sky_coin_mode[self.player]), - "sky_coin_fragments_qty": cc(self.multiworld.shattered_sky_coin_quantity[self.player]), + "progressive_gear": tf(self.options.progressive_gear), + "tweaked_dungeons": tf(self.options.tweak_frustrating_dungeons), + "doom_castle_mode": cc(self.options.doom_castle_mode), + "doom_castle_shortcut": tf(self.options.doom_castle_shortcut), + "sky_coin_mode": cc(self.options.sky_coin_mode), + "sky_coin_fragments_qty": cc(self.options.shattered_sky_coin_quantity), "enable_spoilers": False, - "progressive_formations": cc(self.multiworld.progressive_formations[self.player]), - "map_shuffling": cc(self.multiworld.map_shuffle[self.player]), - "crest_shuffle": tf(self.multiworld.crest_shuffle[self.player]), - "enemizer_groups": cc(self.multiworld.enemizer_groups[self.player]), - "shuffle_res_weak_type": tf(self.multiworld.shuffle_res_weak_types[self.player]), - "companion_leveling_type": cc(self.multiworld.companion_leveling_type[self.player]), - "companion_spellbook_type": cc(self.multiworld.companion_spellbook_type[self.player]), - "starting_companion": cc(self.multiworld.starting_companion[self.player]), + "progressive_formations": cc(self.options.progressive_formations), + "map_shuffling": cc(self.options.map_shuffle), + "crest_shuffle": tf(self.options.crest_shuffle), + "enemizer_groups": cc(self.options.enemizer_groups), + "shuffle_res_weak_type": tf(self.options.shuffle_res_weak_types), + "companion_leveling_type": cc(self.options.companion_leveling_type), + "companion_spellbook_type": cc(self.options.companion_spellbook_type), + "starting_companion": cc(self.options.starting_companion), "available_companions": ["Zero", "One", "Two", - "Three", "Four"][self.multiworld.available_companions[self.player].value], - "companions_locations": cc(self.multiworld.companions_locations[self.player]), - "kaelis_mom_fight_minotaur": tf(self.multiworld.kaelis_mom_fight_minotaur[self.player]), + "Three", "Four"][self.options.available_companions.value], + "companions_locations": cc(self.options.companions_locations), + "kaelis_mom_fight_minotaur": tf(self.options.kaelis_mom_fight_minotaur), } for option, data in option_writes.items(): options["Final Fantasy Mystic Quest"][option][data] = 1 - rom_name = f'MQ{__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed_name:11}'[:21] + rom_name = f'MQ{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed_name:11}'[:21] self.rom_name = bytearray(rom_name, 'utf8') self.rom_name_available_event.set() setup = {"version": "1.5", "name": self.multiworld.player_name[self.player], "romname": rom_name, "seed": - hex(self.multiworld.per_slot_randoms[self.player].randint(0, 0xFFFFFFFF)).split("0x")[1].upper()} + hex(self.random.randint(0, 0xFFFFFFFF)).split("0x")[1].upper()} starting_items = [output_item_name(item) for item in self.multiworld.precollected_items[self.player]] - if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin": + if self.options.sky_coin_mode == "shattered_sky_coin": starting_items.append("SkyCoin") file_path = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.apmq") diff --git a/worlds/ffmq/Regions.py b/worlds/ffmq/Regions.py index 8b83c88e72c..f7b9b9eed4d 100644 --- a/worlds/ffmq/Regions.py +++ b/worlds/ffmq/Regions.py @@ -1,11 +1,9 @@ from BaseClasses import Region, MultiWorld, Entrance, Location, LocationProgressType, ItemClassification from worlds.generic.Rules import add_rule +from .data.rooms import rooms, entrances from .Items import item_groups, yaml_item -import pkgutil -import yaml -rooms = yaml.load(pkgutil.get_data(__name__, "data/rooms.yaml"), yaml.Loader) -entrance_names = {entrance["id"]: entrance["name"] for entrance in yaml.load(pkgutil.get_data(__name__, "data/entrances.yaml"), yaml.Loader)} +entrance_names = {entrance["id"]: entrance["name"] for entrance in entrances} object_id_table = {} object_type_table = {} @@ -69,7 +67,7 @@ def create_regions(self): location_table else None, object["type"], object["access"], self.create_item(yaml_item(object["on_trigger"][0])) if object["type"] == "Trigger" else None) for object in room["game_objects"] if "Hero Chest" not in object["name"] and object["type"] not in ("BattlefieldGp", - "BattlefieldXp") and (object["type"] != "Box" or self.multiworld.brown_boxes[self.player] == "include") and + "BattlefieldXp") and (object["type"] != "Box" or self.options.brown_boxes == "include") and not (object["name"] == "Kaeli Companion" and not object["on_trigger"])], room["links"])) dark_king_room = self.multiworld.get_region("Doom Castle Dark King Room", self.player) @@ -91,15 +89,13 @@ def create_regions(self): if "entrance" in link and link["entrance"] != -1: spoiler = False if link["entrance"] in crest_warps: - if self.multiworld.crest_shuffle[self.player]: + if self.options.crest_shuffle: spoiler = True - elif self.multiworld.map_shuffle[self.player] == "everything": + elif self.options.map_shuffle == "everything": spoiler = True - elif "Subregion" in region.name and self.multiworld.map_shuffle[self.player] not in ("dungeons", - "none"): + elif "Subregion" in region.name and self.options.map_shuffle not in ("dungeons", "none"): spoiler = True - elif "Subregion" not in region.name and self.multiworld.map_shuffle[self.player] not in ("none", - "overworld"): + elif "Subregion" not in region.name and self.options.map_shuffle not in ("none", "overworld"): spoiler = True if spoiler: @@ -111,6 +107,7 @@ def create_regions(self): connection.connect(connect_room) break + non_dead_end_crest_rooms = [ 'Libra Temple', 'Aquaria Gemini Room', "GrenadeMan's Mobius Room", 'Fireburg Gemini Room', 'Sealed Temple', 'Alive Forest', 'Kaidge Temple Upper Ledge', @@ -140,7 +137,7 @@ def hard_boss_logic(state): add_rule(self.multiworld.get_location("Gidrah", self.player), hard_boss_logic) add_rule(self.multiworld.get_location("Dullahan", self.player), hard_boss_logic) - if self.multiworld.map_shuffle[self.player]: + if self.options.map_shuffle: for boss in ("Freezer Crab", "Ice Golem", "Jinn", "Medusa", "Dualhead Hydra"): loc = self.multiworld.get_location(boss, self.player) checked_regions = {loc.parent_region} @@ -158,12 +155,12 @@ def check_foresta(region): return True check_foresta(loc.parent_region) - if self.multiworld.logic[self.player] == "friendly": + if self.options.logic == "friendly": process_rules(self.multiworld.get_entrance("Overworld - Ice Pyramid", self.player), ["MagicMirror"]) process_rules(self.multiworld.get_entrance("Overworld - Volcano", self.player), ["Mask"]) - if self.multiworld.map_shuffle[self.player] in ("none", "overworld"): + if self.options.map_shuffle in ("none", "overworld"): process_rules(self.multiworld.get_entrance("Overworld - Bone Dungeon", self.player), ["Bomb"]) process_rules(self.multiworld.get_entrance("Overworld - Wintry Cave", self.player), @@ -185,8 +182,8 @@ def check_foresta(region): process_rules(self.multiworld.get_entrance("Overworld - Mac Ship Doom", self.player), ["DragonClaw", "CaptainCap"]) - if self.multiworld.logic[self.player] == "expert": - if self.multiworld.map_shuffle[self.player] == "none" and not self.multiworld.crest_shuffle[self.player]: + if self.options.logic == "expert": + if self.options.map_shuffle == "none" and not self.options.crest_shuffle: inner_room = self.multiworld.get_region("Wintry Temple Inner Room", self.player) connection = Entrance(self.player, "Sealed Temple Exit Trick", inner_room) connection.connect(self.multiworld.get_region("Wintry Temple Outer Room", self.player)) @@ -198,14 +195,14 @@ def check_foresta(region): if entrance.connected_region.name in non_dead_end_crest_rooms: entrance.access_rule = lambda state: False - if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin": - logic_coins = [16, 24, 32, 32, 38][self.multiworld.shattered_sky_coin_quantity[self.player].value] + if self.options.sky_coin_mode == "shattered_sky_coin": + logic_coins = [16, 24, 32, 32, 38][self.options.shattered_sky_coin_quantity.value] self.multiworld.get_entrance("Focus Tower 1F - Sky Door", self.player).access_rule = \ lambda state: state.has("Sky Fragment", self.player, logic_coins) - elif self.multiworld.sky_coin_mode[self.player] == "save_the_crystals": + elif self.options.sky_coin_mode == "save_the_crystals": self.multiworld.get_entrance("Focus Tower 1F - Sky Door", self.player).access_rule = \ lambda state: state.has_all(["Flamerus Rex", "Dualhead Hydra", "Ice Golem", "Pazuzu"], self.player) - elif self.multiworld.sky_coin_mode[self.player] in ("standard", "start_with"): + elif self.options.sky_coin_mode in ("standard", "start_with"): self.multiworld.get_entrance("Focus Tower 1F - Sky Door", self.player).access_rule = \ lambda state: state.has("Sky Coin", self.player) @@ -213,26 +210,24 @@ def check_foresta(region): def stage_set_rules(multiworld): # If there's no enemies, there's no repeatable income sources no_enemies_players = [player for player in multiworld.get_game_players("Final Fantasy Mystic Quest") - if multiworld.enemies_density[player] == "none"] + if multiworld.worlds[player].options.enemies_density == "none"] if (len([item for item in multiworld.itempool if item.classification in (ItemClassification.filler, ItemClassification.trap)]) > len([player for player in no_enemies_players if - multiworld.accessibility[player] == "minimal"]) * 3): + multiworld.worlds[player].options.accessibility == "minimal"]) * 3): for player in no_enemies_players: for location in vendor_locations: - if multiworld.accessibility[player] == "locations": + if multiworld.worlds[player].options.accessibility == "locations": multiworld.get_location(location, player).progress_type = LocationProgressType.EXCLUDED else: multiworld.get_location(location, player).access_rule = lambda state: False else: # There are not enough junk items to fill non-minimal players' vendors. Just set an item rule not allowing - # advancement items so that useful items can be placed + # advancement items so that useful items can be placed. for player in no_enemies_players: for location in vendor_locations: multiworld.get_location(location, player).item_rule = lambda item: not item.advancement - - class FFMQLocation(Location): game = "Final Fantasy Mystic Quest" diff --git a/worlds/ffmq/__init__.py b/worlds/ffmq/__init__.py index ac3e9137093..c464203dc6a 100644 --- a/worlds/ffmq/__init__.py +++ b/worlds/ffmq/__init__.py @@ -10,7 +10,7 @@ non_dead_end_crest_warps from .Items import item_table, item_groups, create_items, FFMQItem, fillers from .Output import generate_output -from .Options import option_definitions +from .Options import FFMQOptions from .Client import FFMQClient @@ -45,7 +45,8 @@ class FFMQWorld(World): item_name_to_id = {name: data.id for name, data in item_table.items() if data.id is not None} location_name_to_id = location_table - option_definitions = option_definitions + options_dataclass = FFMQOptions + options: FFMQOptions topology_present = True @@ -67,20 +68,14 @@ def __init__(self, world, player: int): super().__init__(world, player) def generate_early(self): - if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin": - self.multiworld.brown_boxes[self.player].value = 1 - if self.multiworld.enemies_scaling_lower[self.player].value > \ - self.multiworld.enemies_scaling_upper[self.player].value: - (self.multiworld.enemies_scaling_lower[self.player].value, - self.multiworld.enemies_scaling_upper[self.player].value) =\ - (self.multiworld.enemies_scaling_upper[self.player].value, - self.multiworld.enemies_scaling_lower[self.player].value) - if self.multiworld.bosses_scaling_lower[self.player].value > \ - self.multiworld.bosses_scaling_upper[self.player].value: - (self.multiworld.bosses_scaling_lower[self.player].value, - self.multiworld.bosses_scaling_upper[self.player].value) =\ - (self.multiworld.bosses_scaling_upper[self.player].value, - self.multiworld.bosses_scaling_lower[self.player].value) + if self.options.sky_coin_mode == "shattered_sky_coin": + self.options.brown_boxes.value = 1 + if self.options.enemies_scaling_lower.value > self.options.enemies_scaling_upper.value: + self.options.enemies_scaling_lower.value, self.options.enemies_scaling_upper.value = \ + self.options.enemies_scaling_upper.value, self.options.enemies_scaling_lower.value + if self.options.bosses_scaling_lower.value > self.options.bosses_scaling_upper.value: + self.options.bosses_scaling_lower.value, self.options.bosses_scaling_upper.value = \ + self.options.bosses_scaling_upper.value, self.options.bosses_scaling_lower.value @classmethod def stage_generate_early(cls, multiworld): @@ -94,20 +89,20 @@ def stage_generate_early(cls, multiworld): rooms_data = {} for world in multiworld.get_game_worlds("Final Fantasy Mystic Quest"): - if (world.multiworld.map_shuffle[world.player] or world.multiworld.crest_shuffle[world.player] or - world.multiworld.crest_shuffle[world.player]): - if world.multiworld.map_shuffle_seed[world.player].value.isdigit(): - multiworld.random.seed(int(world.multiworld.map_shuffle_seed[world.player].value)) - elif world.multiworld.map_shuffle_seed[world.player].value != "random": - multiworld.random.seed(int(hash(world.multiworld.map_shuffle_seed[world.player].value)) - + int(world.multiworld.seed)) + if (world.options.map_shuffle or world.options.crest_shuffle or world.options.shuffle_battlefield_rewards + or world.options.companions_locations): + if world.options.map_shuffle_seed.value.isdigit(): + multiworld.random.seed(int(world.options.map_shuffle_seed.value)) + elif world.options.map_shuffle_seed.value != "random": + multiworld.random.seed(int(hash(world.options.map_shuffle_seed.value)) + + int(world.multiworld.seed)) seed = hex(multiworld.random.randint(0, 0xFFFFFFFF)).split("0x")[1].upper() - map_shuffle = multiworld.map_shuffle[world.player].value - crest_shuffle = multiworld.crest_shuffle[world.player].current_key - battlefield_shuffle = multiworld.shuffle_battlefield_rewards[world.player].current_key - companion_shuffle = multiworld.companions_locations[world.player].value - kaeli_mom = multiworld.kaelis_mom_fight_minotaur[world.player].current_key + map_shuffle = world.options.map_shuffle.value + crest_shuffle = world.options.crest_shuffle.current_key + battlefield_shuffle = world.options.shuffle_battlefield_rewards.current_key + companion_shuffle = world.options.companions_locations.value + kaeli_mom = world.options.kaelis_mom_fight_minotaur.current_key query = f"s={seed}&m={map_shuffle}&c={crest_shuffle}&b={battlefield_shuffle}&cs={companion_shuffle}&km={kaeli_mom}" @@ -175,14 +170,14 @@ def get_filler_item_name(self): def extend_hint_information(self, hint_data): hint_data[self.player] = {} - if self.multiworld.map_shuffle[self.player]: + if self.options.map_shuffle: single_location_regions = ["Subregion Volcano Battlefield", "Subregion Mac's Ship", "Subregion Doom Castle"] for subregion in ["Subregion Foresta", "Subregion Aquaria", "Subregion Frozen Fields", "Subregion Fireburg", "Subregion Volcano Battlefield", "Subregion Windia", "Subregion Mac's Ship", "Subregion Doom Castle"]: region = self.multiworld.get_region(subregion, self.player) for location in region.locations: - if location.address and self.multiworld.map_shuffle[self.player] != "dungeons": + if location.address and self.options.map_shuffle != "dungeons": hint_data[self.player][location.address] = (subregion.split("Subregion ")[-1] + (" Region" if subregion not in single_location_regions else "")) @@ -202,14 +197,13 @@ def extend_hint_information(self, hint_data): for location in exit_check.connected_region.locations: if location.address: hint = [] - if self.multiworld.map_shuffle[self.player] != "dungeons": + if self.options.map_shuffle != "dungeons": hint.append((subregion.split("Subregion ")[-1] + (" Region" if subregion not in single_location_regions else ""))) - if self.multiworld.map_shuffle[self.player] != "overworld" and subregion not in \ - ("Subregion Mac's Ship", "Subregion Doom Castle"): + if self.options.map_shuffle != "overworld": hint.append(overworld_spot.name.split("Overworld - ")[-1].replace("Pazuzu", "Pazuzu's")) - hint = " - ".join(hint) + hint = " - ".join(hint).replace(" - Mac Ship", "") if location.address in hint_data[self.player]: hint_data[self.player][location.address] += f"/{hint}" else: diff --git a/worlds/ffmq/data/entrances.yaml b/worlds/ffmq/data/entrances.yaml deleted file mode 100644 index 1dfef2655c3..00000000000 --- a/worlds/ffmq/data/entrances.yaml +++ /dev/null @@ -1,2450 +0,0 @@ -- name: Doom Castle - Sand Floor - To Sky Door - Sand Floor - id: 0 - area: 7 - coordinates: [24, 19] - teleporter: [0, 0] -- name: Doom Castle - Sand Floor - Main Entrance - Sand Floor - id: 1 - area: 7 - coordinates: [19, 43] - teleporter: [1, 6] -- name: Doom Castle - Aero Room - Aero Room Entrance - id: 2 - area: 7 - coordinates: [27, 39] - teleporter: [1, 0] -- name: Focus Tower B1 - Main Loop - South Entrance - id: 3 - area: 8 - coordinates: [43, 60] - teleporter: [2, 6] -- name: Focus Tower B1 - Main Loop - To Focus Tower 1F - Main Hall - id: 4 - area: 8 - coordinates: [37, 41] - teleporter: [4, 0] -- name: Focus Tower B1 - Aero Corridor - To Focus Tower 1F - Sun Coin Room - id: 5 - area: 8 - coordinates: [59, 35] - teleporter: [5, 0] -- name: Focus Tower B1 - Aero Corridor - To Sand Floor - Aero Chest - id: 6 - area: 8 - coordinates: [57, 59] - teleporter: [8, 0] -- name: Focus Tower B1 - Inner Loop - To Focus Tower 1F - Sky Door - id: 7 - area: 8 - coordinates: [51, 49] - teleporter: [6, 0] -- name: Focus Tower B1 - Inner Loop - To Doom Castle Sand Floor - id: 8 - area: 8 - coordinates: [51, 45] - teleporter: [7, 0] -- name: Focus Tower 1F - Focus Tower West Entrance - id: 9 - area: 9 - coordinates: [25, 29] - teleporter: [3, 6] -- name: Focus Tower 1F - To Focus Tower 2F - From SandCoin - id: 10 - area: 9 - coordinates: [16, 4] - teleporter: [10, 0] -- name: Focus Tower 1F - To Focus Tower B1 - Main Hall - id: 11 - area: 9 - coordinates: [4, 23] - teleporter: [11, 0] -- name: Focus Tower 1F - To Focus Tower B1 - To Aero Chest - id: 12 - area: 9 - coordinates: [26, 17] - teleporter: [12, 0] -- name: Focus Tower 1F - Sky Door - id: 13 - area: 9 - coordinates: [16, 24] - teleporter: [13, 0] -- name: Focus Tower 1F - To Focus Tower 2F - From RiverCoin - id: 14 - area: 9 - coordinates: [16, 10] - teleporter: [14, 0] -- name: Focus Tower 1F - To Focus Tower B1 - From Sky Door - id: 15 - area: 9 - coordinates: [16, 29] - teleporter: [15, 0] -- name: Focus Tower 2F - Sand Coin Passage - North Entrance - id: 16 - area: 10 - coordinates: [49, 30] - teleporter: [4, 6] -- name: Focus Tower 2F - Sand Coin Passage - To Focus Tower 1F - To SandCoin - id: 17 - area: 10 - coordinates: [47, 33] - teleporter: [17, 0] -- name: Focus Tower 2F - River Coin Passage - To Focus Tower 1F - To RiverCoin - id: 18 - area: 10 - coordinates: [47, 41] - teleporter: [18, 0] -- name: Focus Tower 2F - River Coin Passage - To Focus Tower 3F - Lower Floor - id: 19 - area: 10 - coordinates: [38, 40] - teleporter: [20, 0] -- name: Focus Tower 2F - Venus Chest Room - To Focus Tower 3F - Upper Floor - id: 20 - area: 10 - coordinates: [56, 40] - teleporter: [19, 0] -- name: Focus Tower 2F - Venus Chest Room - Pillar Script - id: 21 - area: 10 - coordinates: [48, 53] - teleporter: [13, 8] -- name: Focus Tower 3F - Lower Floor - To Fireburg Entrance - id: 22 - area: 11 - coordinates: [11, 39] - teleporter: [6, 6] -- name: Focus Tower 3F - Lower Floor - To Focus Tower 2F - Jump on Pillar - id: 23 - area: 11 - coordinates: [6, 47] - teleporter: [24, 0] -- name: Focus Tower 3F - Upper Floor - To Aquaria Entrance - id: 24 - area: 11 - coordinates: [21, 38] - teleporter: [5, 6] -- name: Focus Tower 3F - Upper Floor - To Focus Tower 2F - Venus Chest Room - id: 25 - area: 11 - coordinates: [24, 47] - teleporter: [23, 0] -- name: Level Forest - Boulder Script - id: 26 - area: 14 - coordinates: [52, 15] - teleporter: [0, 8] -- name: Level Forest - Rotten Tree Script - id: 27 - area: 14 - coordinates: [47, 6] - teleporter: [2, 8] -- name: Level Forest - Exit Level Forest 1 - id: 28 - area: 14 - coordinates: [46, 25] - teleporter: [25, 0] -- name: Level Forest - Exit Level Forest 2 - id: 29 - area: 14 - coordinates: [46, 26] - teleporter: [25, 0] -- name: Level Forest - Exit Level Forest 3 - id: 30 - area: 14 - coordinates: [47, 25] - teleporter: [25, 0] -- name: Level Forest - Exit Level Forest 4 - id: 31 - area: 14 - coordinates: [47, 26] - teleporter: [25, 0] -- name: Level Forest - Exit Level Forest 5 - id: 32 - area: 14 - coordinates: [60, 14] - teleporter: [25, 0] -- name: Level Forest - Exit Level Forest 6 - id: 33 - area: 14 - coordinates: [61, 14] - teleporter: [25, 0] -- name: Level Forest - Exit Level Forest 7 - id: 34 - area: 14 - coordinates: [46, 4] - teleporter: [25, 0] -- name: Level Forest - Exit Level Forest 8 - id: 35 - area: 14 - coordinates: [46, 3] - teleporter: [25, 0] -- name: Level Forest - Exit Level Forest 9 - id: 36 - area: 14 - coordinates: [47, 4] - teleporter: [25, 0] -- name: Level Forest - Exit Level Forest A - id: 37 - area: 14 - coordinates: [47, 3] - teleporter: [25, 0] -- name: Foresta - Exit Foresta 1 - id: 38 - area: 15 - coordinates: [10, 25] - teleporter: [31, 0] -- name: Foresta - Exit Foresta 2 - id: 39 - area: 15 - coordinates: [10, 26] - teleporter: [31, 0] -- name: Foresta - Exit Foresta 3 - id: 40 - area: 15 - coordinates: [11, 25] - teleporter: [31, 0] -- name: Foresta - Exit Foresta 4 - id: 41 - area: 15 - coordinates: [11, 26] - teleporter: [31, 0] -- name: Foresta - Old Man House - Front Door - id: 42 - area: 15 - coordinates: [25, 17] - teleporter: [32, 4] -- name: Foresta - Old Man House - Back Door - id: 43 - area: 15 - coordinates: [25, 14] - teleporter: [33, 0] -- name: Foresta - Kaeli's House - id: 44 - area: 15 - coordinates: [7, 21] - teleporter: [0, 5] -- name: Foresta - Rest House - id: 45 - area: 15 - coordinates: [23, 23] - teleporter: [1, 5] -- name: Kaeli's House - Kaeli's House Entrance - id: 46 - area: 16 - coordinates: [11, 20] - teleporter: [86, 3] -- name: Foresta Houses - Old Man's House - Old Man Front Exit - id: 47 - area: 17 - coordinates: [35, 44] - teleporter: [34, 0] -- name: Foresta Houses - Old Man's House - Old Man Back Exit - id: 48 - area: 17 - coordinates: [35, 27] - teleporter: [35, 0] -- name: Foresta - Old Man House - Barrel Tile Script # New, use the focus tower column's script - id: 483 - area: 17 - coordinates: [0x23, 0x1E] - teleporter: [0x0D, 8] -- name: Foresta Houses - Rest House - Bed Script - id: 49 - area: 17 - coordinates: [30, 6] - teleporter: [1, 8] -- name: Foresta Houses - Rest House - Rest House Exit - id: 50 - area: 17 - coordinates: [35, 20] - teleporter: [87, 3] -- name: Foresta Houses - Libra House - Libra House Script - id: 51 - area: 17 - coordinates: [8, 49] - teleporter: [67, 8] -- name: Foresta Houses - Gemini House - Gemini House Script - id: 52 - area: 17 - coordinates: [26, 55] - teleporter: [68, 8] -- name: Foresta Houses - Mobius House - Mobius House Script - id: 53 - area: 17 - coordinates: [14, 33] - teleporter: [69, 8] -- name: Sand Temple - Sand Temple Entrance - id: 54 - area: 18 - coordinates: [56, 27] - teleporter: [36, 0] -- name: Bone Dungeon 1F - Bone Dungeon Entrance - id: 55 - area: 19 - coordinates: [13, 60] - teleporter: [37, 0] -- name: Bone Dungeon 1F - To Bone Dungeon B1 - id: 56 - area: 19 - coordinates: [13, 39] - teleporter: [2, 2] -- name: Bone Dungeon B1 - Waterway - Exit Waterway - id: 57 - area: 20 - coordinates: [27, 39] - teleporter: [3, 2] -- name: Bone Dungeon B1 - Waterway - Tristam's Script - id: 58 - area: 20 - coordinates: [27, 45] - teleporter: [3, 8] -- name: Bone Dungeon B1 - Waterway - To Bone Dungeon 1F - id: 59 - area: 20 - coordinates: [54, 61] - teleporter: [88, 3] -- name: Bone Dungeon B1 - Checker Room - Exit Checker Room - id: 60 - area: 20 - coordinates: [23, 40] - teleporter: [4, 2] -- name: Bone Dungeon B1 - Checker Room - To Waterway - id: 61 - area: 20 - coordinates: [39, 49] - teleporter: [89, 3] -- name: Bone Dungeon B1 - Hidden Room - To B2 - Exploding Skull Room - id: 62 - area: 20 - coordinates: [5, 33] - teleporter: [91, 3] -- name: Bonne Dungeon B2 - Exploding Skull Room - To Hidden Passage - id: 63 - area: 21 - coordinates: [19, 13] - teleporter: [5, 2] -- name: Bonne Dungeon B2 - Exploding Skull Room - To Two Skulls Room - id: 64 - area: 21 - coordinates: [29, 15] - teleporter: [6, 2] -- name: Bonne Dungeon B2 - Exploding Skull Room - To Checker Room - id: 65 - area: 21 - coordinates: [8, 25] - teleporter: [90, 3] -- name: Bonne Dungeon B2 - Box Room - To B2 - Two Skulls Room - id: 66 - area: 21 - coordinates: [59, 12] - teleporter: [93, 3] -- name: Bonne Dungeon B2 - Quake Room - To B2 - Two Skulls Room - id: 67 - area: 21 - coordinates: [59, 28] - teleporter: [94, 3] -- name: Bonne Dungeon B2 - Two Skulls Room - To Box Room - id: 68 - area: 21 - coordinates: [53, 7] - teleporter: [7, 2] -- name: Bonne Dungeon B2 - Two Skulls Room - To Quake Room - id: 69 - area: 21 - coordinates: [41, 3] - teleporter: [8, 2] -- name: Bonne Dungeon B2 - Two Skulls Room - To Boss Room - id: 70 - area: 21 - coordinates: [47, 57] - teleporter: [9, 2] -- name: Bonne Dungeon B2 - Two Skulls Room - To B2 - Exploding Skull Room - id: 71 - area: 21 - coordinates: [54, 23] - teleporter: [92, 3] -- name: Bone Dungeon B2 - Boss Room - Flamerus Rex Script - id: 72 - area: 22 - coordinates: [29, 19] - teleporter: [4, 8] -- name: Bone Dungeon B2 - Boss Room - Tristam Leave Script - id: 73 - area: 22 - coordinates: [29, 23] - teleporter: [75, 8] -- name: Bone Dungeon B2 - Boss Room - To B2 - Two Skulls Room - id: 74 - area: 22 - coordinates: [30, 27] - teleporter: [95, 3] -- name: Libra Temple - Entrance - id: 75 - area: 23 - coordinates: [10, 15] - teleporter: [13, 6] -- name: Libra Temple - Libra Tile Script - id: 76 - area: 23 - coordinates: [9, 8] - teleporter: [59, 8] -- name: Aquaria Winter - Winter Entrance 1 - id: 77 - area: 24 - coordinates: [25, 25] - teleporter: [8, 6] -- name: Aquaria Winter - Winter Entrance 2 - id: 78 - area: 24 - coordinates: [25, 26] - teleporter: [8, 6] -- name: Aquaria Winter - Winter Entrance 3 - id: 79 - area: 24 - coordinates: [26, 25] - teleporter: [8, 6] -- name: Aquaria Winter - Winter Entrance 4 - id: 80 - area: 24 - coordinates: [26, 26] - teleporter: [8, 6] -- name: Aquaria Winter - Winter Phoebe's House Entrance Script #Modified to not be a script - id: 81 - area: 24 - coordinates: [8, 19] - teleporter: [10, 5] # original value [5, 8] -- name: Aquaria Winter - Winter Vendor House Entrance - id: 82 - area: 24 - coordinates: [8, 5] - teleporter: [44, 4] -- name: Aquaria Winter - Winter INN Entrance - id: 83 - area: 24 - coordinates: [26, 17] - teleporter: [11, 5] -- name: Aquaria Summer - Summer Entrance 1 - id: 84 - area: 25 - coordinates: [57, 25] - teleporter: [8, 6] -- name: Aquaria Summer - Summer Entrance 2 - id: 85 - area: 25 - coordinates: [57, 26] - teleporter: [8, 6] -- name: Aquaria Summer - Summer Entrance 3 - id: 86 - area: 25 - coordinates: [58, 25] - teleporter: [8, 6] -- name: Aquaria Summer - Summer Entrance 4 - id: 87 - area: 25 - coordinates: [58, 26] - teleporter: [8, 6] -- name: Aquaria Summer - Summer Phoebe's House Entrance - id: 88 - area: 25 - coordinates: [40, 19] - teleporter: [10, 5] -- name: Aquaria Summer - Spencer's Place Entrance Top - id: 89 - area: 25 - coordinates: [40, 16] - teleporter: [42, 0] -- name: Aquaria Summer - Spencer's Place Entrance Side - id: 90 - area: 25 - coordinates: [41, 18] - teleporter: [43, 0] -- name: Aquaria Summer - Summer Vendor House Entrance - id: 91 - area: 25 - coordinates: [40, 5] - teleporter: [44, 4] -- name: Aquaria Summer - Summer INN Entrance - id: 92 - area: 25 - coordinates: [58, 17] - teleporter: [11, 5] -- name: Phoebe's House - Entrance # Change to a script, same as vendor house - id: 93 - area: 26 - coordinates: [29, 14] - teleporter: [5, 8] # Original Value [11,3] -- name: Aquaria Vendor House - Vendor House Entrance's Script - id: 94 - area: 27 - coordinates: [7, 10] - teleporter: [40, 8] -- name: Aquaria Vendor House - Vendor House Stairs - id: 95 - area: 27 - coordinates: [1, 4] - teleporter: [47, 0] -- name: Aquaria Gemini Room - Gemini Script - id: 96 - area: 27 - coordinates: [2, 40] - teleporter: [72, 8] -- name: Aquaria Gemini Room - Gemini Room Stairs - id: 97 - area: 27 - coordinates: [4, 39] - teleporter: [48, 0] -- name: Aquaria INN - Aquaria INN entrance # Change to a script, same as vendor house - id: 98 - area: 27 - coordinates: [51, 46] - teleporter: [75, 8] # Original value [48,3] -- name: Wintry Cave 1F - Main Entrance - id: 99 - area: 28 - coordinates: [50, 58] - teleporter: [49, 0] -- name: Wintry Cave 1F - To 3F Top - id: 100 - area: 28 - coordinates: [40, 25] - teleporter: [14, 2] -- name: Wintry Cave 1F - To 2F - id: 101 - area: 28 - coordinates: [10, 43] - teleporter: [15, 2] -- name: Wintry Cave 1F - Phoebe's Script - id: 102 - area: 28 - coordinates: [44, 37] - teleporter: [6, 8] -- name: Wintry Cave 2F - To 3F Bottom - id: 103 - area: 29 - coordinates: [58, 5] - teleporter: [50, 0] -- name: Wintry Cave 2F - To 1F - id: 104 - area: 29 - coordinates: [38, 18] - teleporter: [97, 3] -- name: Wintry Cave 3F Top - Exit from 3F Top - id: 105 - area: 30 - coordinates: [24, 6] - teleporter: [96, 3] -- name: Wintry Cave 3F Bottom - Exit to 2F - id: 106 - area: 31 - coordinates: [4, 29] - teleporter: [51, 0] -- name: Life Temple - Entrance - id: 107 - area: 32 - coordinates: [9, 60] - teleporter: [14, 6] -- name: Life Temple - Libra Tile Script - id: 108 - area: 32 - coordinates: [3, 55] - teleporter: [60, 8] -- name: Life Temple - Mysterious Man Script - id: 109 - area: 32 - coordinates: [9, 44] - teleporter: [78, 8] -- name: Fall Basin - Back Exit Script - id: 110 - area: 33 - coordinates: [17, 5] - teleporter: [9, 0] # Remove script [42, 8] for overworld teleport (but not main exit) -- name: Fall Basin - Main Exit - id: 111 - area: 33 - coordinates: [15, 26] - teleporter: [53, 0] -- name: Fall Basin - Phoebe's Script - id: 112 - area: 33 - coordinates: [17, 6] - teleporter: [9, 8] -- name: Ice Pyramid B1 Taunt Room - To Climbing Wall Room - id: 113 - area: 34 - coordinates: [43, 6] - teleporter: [55, 0] -- name: Ice Pyramid 1F Maze - Main Entrance 1 - id: 114 - area: 35 - coordinates: [18, 36] - teleporter: [56, 0] -- name: Ice Pyramid 1F Maze - Main Entrance 2 - id: 115 - area: 35 - coordinates: [19, 36] - teleporter: [56, 0] -- name: Ice Pyramid 1F Maze - West Stairs To 2F South Tiled Room - id: 116 - area: 35 - coordinates: [3, 27] - teleporter: [57, 0] -- name: Ice Pyramid 1F Maze - West Center Stairs to 2F West Room - id: 117 - area: 35 - coordinates: [11, 15] - teleporter: [58, 0] -- name: Ice Pyramid 1F Maze - East Center Stairs to 2F Center Room - id: 118 - area: 35 - coordinates: [25, 16] - teleporter: [59, 0] -- name: Ice Pyramid 1F Maze - Upper Stairs to 2F Small North Room - id: 119 - area: 35 - coordinates: [31, 1] - teleporter: [60, 0] -- name: Ice Pyramid 1F Maze - East Stairs to 2F North Corridor - id: 120 - area: 35 - coordinates: [34, 9] - teleporter: [61, 0] -- name: Ice Pyramid 1F Maze - Statue's Script - id: 121 - area: 35 - coordinates: [21, 32] - teleporter: [77, 8] -- name: Ice Pyramid 2F South Tiled Room - To 1F - id: 122 - area: 36 - coordinates: [4, 26] - teleporter: [62, 0] -- name: Ice Pyramid 2F South Tiled Room - To 3F Two Boxes Room - id: 123 - area: 36 - coordinates: [22, 17] - teleporter: [67, 0] -- name: Ice Pyramid 2F West Room - To 1F - id: 124 - area: 36 - coordinates: [9, 10] - teleporter: [63, 0] -- name: Ice Pyramid 2F Center Room - To 1F - id: 125 - area: 36 - coordinates: [22, 14] - teleporter: [64, 0] -- name: Ice Pyramid 2F Small North Room - To 1F - id: 126 - area: 36 - coordinates: [26, 4] - teleporter: [65, 0] -- name: Ice Pyramid 2F North Corridor - To 1F - id: 127 - area: 36 - coordinates: [32, 8] - teleporter: [66, 0] -- name: Ice Pyramid 2F North Corridor - To 3F Main Loop - id: 128 - area: 36 - coordinates: [12, 7] - teleporter: [68, 0] -- name: Ice Pyramid 3F Two Boxes Room - To 2F South Tiled Room - id: 129 - area: 37 - coordinates: [24, 54] - teleporter: [69, 0] -- name: Ice Pyramid 3F Main Loop - To 2F Corridor - id: 130 - area: 37 - coordinates: [16, 45] - teleporter: [70, 0] -- name: Ice Pyramid 3F Main Loop - To 4F - id: 131 - area: 37 - coordinates: [19, 43] - teleporter: [71, 0] -- name: Ice Pyramid 4F Treasure Room - To 3F Main Loop - id: 132 - area: 38 - coordinates: [52, 5] - teleporter: [72, 0] -- name: Ice Pyramid 4F Treasure Room - To 5F Leap of Faith Room - id: 133 - area: 38 - coordinates: [62, 19] - teleporter: [73, 0] -- name: Ice Pyramid 5F Leap of Faith Room - To 4F Treasure Room - id: 134 - area: 39 - coordinates: [54, 63] - teleporter: [74, 0] -- name: Ice Pyramid 5F Leap of Faith Room - Bombed Ice Plate - id: 135 - area: 39 - coordinates: [47, 54] - teleporter: [77, 8] -- name: Ice Pyramid 5F Stairs to Ice Golem - To Ice Golem Room - id: 136 - area: 39 - coordinates: [39, 43] - teleporter: [75, 0] -- name: Ice Pyramid 5F Stairs to Ice Golem - To Climbing Wall Room - id: 137 - area: 39 - coordinates: [39, 60] - teleporter: [76, 0] -- name: Ice Pyramid - Duplicate Ice Golem Room # not used? - id: 138 - area: 40 - coordinates: [44, 43] - teleporter: [77, 0] -- name: Ice Pyramid Climbing Wall Room - To Taunt Room - id: 139 - area: 41 - coordinates: [4, 59] - teleporter: [78, 0] -- name: Ice Pyramid Climbing Wall Room - To 5F Stairs - id: 140 - area: 41 - coordinates: [4, 45] - teleporter: [79, 0] -- name: Ice Pyramid Ice Golem Room - To 5F Stairs - id: 141 - area: 42 - coordinates: [44, 43] - teleporter: [80, 0] -- name: Ice Pyramid Ice Golem Room - Ice Golem Script - id: 142 - area: 42 - coordinates: [53, 32] - teleporter: [10, 8] -- name: Spencer Waterfall - To Spencer Cave - id: 143 - area: 43 - coordinates: [48, 57] - teleporter: [81, 0] -- name: Spencer Waterfall - Upper Exit to Aquaria 1 - id: 144 - area: 43 - coordinates: [40, 5] - teleporter: [82, 0] -- name: Spencer Waterfall - Upper Exit to Aquaria 2 - id: 145 - area: 43 - coordinates: [40, 6] - teleporter: [82, 0] -- name: Spencer Waterfall - Upper Exit to Aquaria 3 - id: 146 - area: 43 - coordinates: [41, 5] - teleporter: [82, 0] -- name: Spencer Waterfall - Upper Exit to Aquaria 4 - id: 147 - area: 43 - coordinates: [41, 6] - teleporter: [82, 0] -- name: Spencer Waterfall - Right Exit to Aquaria 1 - id: 148 - area: 43 - coordinates: [46, 8] - teleporter: [83, 0] -- name: Spencer Waterfall - Right Exit to Aquaria 2 - id: 149 - area: 43 - coordinates: [47, 8] - teleporter: [83, 0] -- name: Spencer Cave Normal Main - To Waterfall - id: 150 - area: 44 - coordinates: [14, 39] - teleporter: [85, 0] -- name: Spencer Cave Normal From Overworld - Exit to Overworld - id: 151 - area: 44 - coordinates: [15, 57] - teleporter: [7, 6] -- name: Spencer Cave Unplug - Exit to Overworld - id: 152 - area: 45 - coordinates: [40, 29] - teleporter: [7, 6] -- name: Spencer Cave Unplug - Libra Teleporter Start Script - id: 153 - area: 45 - coordinates: [28, 21] - teleporter: [33, 8] -- name: Spencer Cave Unplug - Libra Teleporter End Script - id: 154 - area: 45 - coordinates: [46, 4] - teleporter: [34, 8] -- name: Spencer Cave Unplug - Mobius Teleporter Chest Script - id: 155 - area: 45 - coordinates: [21, 9] - teleporter: [35, 8] -- name: Spencer Cave Unplug - Mobius Teleporter Start Script - id: 156 - area: 45 - coordinates: [29, 28] - teleporter: [36, 8] -- name: Wintry Temple Outer Room - Main Entrance - id: 157 - area: 46 - coordinates: [8, 31] - teleporter: [15, 6] -- name: Wintry Temple Inner Room - Gemini Tile to Sealed temple - id: 158 - area: 46 - coordinates: [9, 24] - teleporter: [62, 8] -- name: Fireburg - To Overworld - id: 159 - area: 47 - coordinates: [4, 13] - teleporter: [9, 6] -- name: Fireburg - To Overworld - id: 160 - area: 47 - coordinates: [5, 13] - teleporter: [9, 6] -- name: Fireburg - To Overworld - id: 161 - area: 47 - coordinates: [28, 15] - teleporter: [9, 6] -- name: Fireburg - To Overworld - id: 162 - area: 47 - coordinates: [27, 15] - teleporter: [9, 6] -- name: Fireburg - Vendor House - id: 163 - area: 47 - coordinates: [10, 24] - teleporter: [91, 0] -- name: Fireburg - Reuben House - id: 164 - area: 47 - coordinates: [14, 6] - teleporter: [98, 8] # Script for reuben, original value [16, 2] -- name: Fireburg - Hotel - id: 165 - area: 47 - coordinates: [20, 8] - teleporter: [96, 8] # It's a script now for tristam, original value [17, 2] -- name: Fireburg - GrenadeMan House Script - id: 166 - area: 47 - coordinates: [12, 18] - teleporter: [11, 8] -- name: Reuben House - Main Entrance - id: 167 - area: 48 - coordinates: [33, 46] - teleporter: [98, 3] -- name: GrenadeMan House - Entrance Script - id: 168 - area: 49 - coordinates: [55, 60] - teleporter: [9, 8] -- name: GrenadeMan House - To Mobius Crest Room - id: 169 - area: 49 - coordinates: [57, 52] - teleporter: [93, 0] -- name: GrenadeMan Mobius Room - Stairs to House - id: 170 - area: 49 - coordinates: [39, 26] - teleporter: [94, 0] -- name: GrenadeMan Mobius Room - Mobius Teleporter Script - id: 171 - area: 49 - coordinates: [39, 23] - teleporter: [54, 8] -- name: Fireburg Vendor House - Entrance Script # No use to be a script - id: 172 - area: 49 - coordinates: [7, 10] - teleporter: [95, 0] # Original value [39, 8] -- name: Fireburg Vendor House - Stairs to Gemini Room - id: 173 - area: 49 - coordinates: [1, 4] - teleporter: [96, 0] -- name: Fireburg Gemini Room - Stairs to Vendor House - id: 174 - area: 49 - coordinates: [4, 39] - teleporter: [97, 0] -- name: Fireburg Gemini Room - Gemini Teleporter Script - id: 175 - area: 49 - coordinates: [2, 40] - teleporter: [45, 8] -- name: Fireburg Hotel Lobby - Stairs to beds - id: 176 - area: 49 - coordinates: [4, 50] - teleporter: [213, 0] -- name: Fireburg Hotel Lobby - Entrance - id: 177 - area: 49 - coordinates: [17, 56] - teleporter: [99, 3] -- name: Fireburg Hotel Beds - Stairs to Hotel Lobby - id: 178 - area: 49 - coordinates: [45, 59] - teleporter: [214, 0] -- name: Mine Exterior - Main Entrance - id: 179 - area: 50 - coordinates: [5, 28] - teleporter: [98, 0] -- name: Mine Exterior - To Cliff - id: 180 - area: 50 - coordinates: [58, 29] - teleporter: [99, 0] -- name: Mine Exterior - To Parallel Room - id: 181 - area: 50 - coordinates: [8, 7] - teleporter: [20, 2] -- name: Mine Exterior - To Crescent Room - id: 182 - area: 50 - coordinates: [26, 15] - teleporter: [21, 2] -- name: Mine Exterior - To Climbing Room - id: 183 - area: 50 - coordinates: [21, 35] - teleporter: [22, 2] -- name: Mine Exterior - Jinn Fight Script - id: 184 - area: 50 - coordinates: [58, 31] - teleporter: [74, 8] -- name: Mine Parallel Room - To Mine Exterior - id: 185 - area: 51 - coordinates: [7, 60] - teleporter: [100, 3] -- name: Mine Crescent Room - To Mine Exterior - id: 186 - area: 51 - coordinates: [22, 61] - teleporter: [101, 3] -- name: Mine Climbing Room - To Mine Exterior - id: 187 - area: 51 - coordinates: [56, 21] - teleporter: [102, 3] -- name: Mine Cliff - Entrance - id: 188 - area: 52 - coordinates: [9, 5] - teleporter: [100, 0] -- name: Mine Cliff - Reuben Grenade Script - id: 189 - area: 52 - coordinates: [15, 7] - teleporter: [12, 8] -- name: Sealed Temple - To Overworld - id: 190 - area: 53 - coordinates: [58, 43] - teleporter: [16, 6] -- name: Sealed Temple - Gemini Tile Script - id: 191 - area: 53 - coordinates: [56, 38] - teleporter: [63, 8] -- name: Volcano Base - Main Entrance 1 - id: 192 - area: 54 - coordinates: [23, 25] - teleporter: [103, 0] -- name: Volcano Base - Main Entrance 2 - id: 193 - area: 54 - coordinates: [23, 26] - teleporter: [103, 0] -- name: Volcano Base - Main Entrance 3 - id: 194 - area: 54 - coordinates: [24, 25] - teleporter: [103, 0] -- name: Volcano Base - Main Entrance 4 - id: 195 - area: 54 - coordinates: [24, 26] - teleporter: [103, 0] -- name: Volcano Base - Left Stairs Script - id: 196 - area: 54 - coordinates: [20, 5] - teleporter: [31, 8] -- name: Volcano Base - Right Stairs Script - id: 197 - area: 54 - coordinates: [32, 5] - teleporter: [30, 8] -- name: Volcano Top Right - Top Exit - id: 198 - area: 55 - coordinates: [44, 8] - teleporter: [9, 0] # Original value [103, 0] changed to volcano escape so floor shuffling doesn't pick it up -- name: Volcano Top Left - To Right-Left Path Script - id: 199 - area: 55 - coordinates: [40, 24] - teleporter: [26, 8] -- name: Volcano Top Right - To Left-Right Path Script - id: 200 - area: 55 - coordinates: [52, 24] - teleporter: [79, 8] # Original Value [26, 8] -- name: Volcano Right Path - To Volcano Base Script - id: 201 - area: 56 - coordinates: [48, 42] - teleporter: [15, 8] # Original Value [27, 8] -- name: Volcano Left Path - To Volcano Cross Left-Right - id: 202 - area: 56 - coordinates: [40, 31] - teleporter: [25, 2] -- name: Volcano Left Path - To Volcano Cross Right-Left - id: 203 - area: 56 - coordinates: [52, 29] - teleporter: [26, 2] -- name: Volcano Left Path - To Volcano Base Script - id: 204 - area: 56 - coordinates: [36, 42] - teleporter: [27, 8] -- name: Volcano Cross Left-Right - To Volcano Left Path - id: 205 - area: 56 - coordinates: [10, 42] - teleporter: [103, 3] -- name: Volcano Cross Left-Right - To Volcano Top Right Script - id: 206 - area: 56 - coordinates: [16, 24] - teleporter: [29, 8] -- name: Volcano Cross Right-Left - To Volcano Top Left Script - id: 207 - area: 56 - coordinates: [8, 22] - teleporter: [28, 8] -- name: Volcano Cross Right-Left - To Volcano Left Path - id: 208 - area: 56 - coordinates: [16, 42] - teleporter: [104, 3] -- name: Lava Dome Inner Ring Main Loop - Main Entrance 1 - id: 209 - area: 57 - coordinates: [32, 5] - teleporter: [104, 0] -- name: Lava Dome Inner Ring Main Loop - Main Entrance 2 - id: 210 - area: 57 - coordinates: [33, 5] - teleporter: [104, 0] -- name: Lava Dome Inner Ring Main Loop - To Three Steps Room - id: 211 - area: 57 - coordinates: [14, 5] - teleporter: [105, 0] -- name: Lava Dome Inner Ring Main Loop - To Life Chest Room Lower - id: 212 - area: 57 - coordinates: [40, 17] - teleporter: [106, 0] -- name: Lava Dome Inner Ring Main Loop - To Big Jump Room Left - id: 213 - area: 57 - coordinates: [8, 11] - teleporter: [108, 0] -- name: Lava Dome Inner Ring Main Loop - To Split Corridor Room - id: 214 - area: 57 - coordinates: [11, 19] - teleporter: [111, 0] -- name: Lava Dome Inner Ring Center Ledge - To Life Chest Room Higher - id: 215 - area: 57 - coordinates: [32, 11] - teleporter: [107, 0] -- name: Lava Dome Inner Ring Plate Ledge - To Plate Corridor - id: 216 - area: 57 - coordinates: [12, 23] - teleporter: [109, 0] -- name: Lava Dome Inner Ring Plate Ledge - Plate Script - id: 217 - area: 57 - coordinates: [5, 23] - teleporter: [47, 8] -- name: Lava Dome Inner Ring Upper Ledges - To Pointless Room - id: 218 - area: 57 - coordinates: [0, 9] - teleporter: [110, 0] -- name: Lava Dome Inner Ring Upper Ledges - To Lower Moon Helm Room - id: 219 - area: 57 - coordinates: [0, 15] - teleporter: [112, 0] -- name: Lava Dome Inner Ring Upper Ledges - To Up-Down Corridor - id: 220 - area: 57 - coordinates: [54, 5] - teleporter: [113, 0] -- name: Lava Dome Inner Ring Big Door Ledge - To Jumping Maze II - id: 221 - area: 57 - coordinates: [54, 21] - teleporter: [114, 0] -- name: Lava Dome Inner Ring Big Door Ledge - Hydra Gate 1 - id: 222 - area: 57 - coordinates: [62, 20] - teleporter: [29, 2] -- name: Lava Dome Inner Ring Big Door Ledge - Hydra Gate 2 - id: 223 - area: 57 - coordinates: [63, 20] - teleporter: [29, 2] -- name: Lava Dome Inner Ring Big Door Ledge - Hydra Gate 3 - id: 224 - area: 57 - coordinates: [62, 21] - teleporter: [29, 2] -- name: Lava Dome Inner Ring Big Door Ledge - Hydra Gate 4 - id: 225 - area: 57 - coordinates: [63, 21] - teleporter: [29, 2] -- name: Lava Dome Inner Ring Tiny Bottom Ledge - To Four Boxes Corridor - id: 226 - area: 57 - coordinates: [50, 25] - teleporter: [115, 0] -- name: Lava Dome Jump Maze II - Lower Right Entrance - id: 227 - area: 58 - coordinates: [55, 28] - teleporter: [116, 0] -- name: Lava Dome Jump Maze II - Upper Entrance - id: 228 - area: 58 - coordinates: [35, 3] - teleporter: [119, 0] -- name: Lava Dome Jump Maze II - Lower Left Entrance - id: 229 - area: 58 - coordinates: [34, 27] - teleporter: [120, 0] -- name: Lava Dome Up-Down Corridor - Upper Entrance - id: 230 - area: 58 - coordinates: [29, 8] - teleporter: [117, 0] -- name: Lava Dome Up-Down Corridor - Lower Entrance - id: 231 - area: 58 - coordinates: [28, 25] - teleporter: [118, 0] -- name: Lava Dome Jump Maze I - South Entrance - id: 232 - area: 59 - coordinates: [20, 27] - teleporter: [121, 0] -- name: Lava Dome Jump Maze I - North Entrance - id: 233 - area: 59 - coordinates: [7, 3] - teleporter: [122, 0] -- name: Lava Dome Pointless Room - Entrance - id: 234 - area: 60 - coordinates: [2, 7] - teleporter: [123, 0] -- name: Lava Dome Pointless Room - Visit Quest Script 1 - id: 490 - area: 60 - coordinates: [4, 4] - teleporter: [99, 8] -- name: Lava Dome Pointless Room - Visit Quest Script 2 - id: 491 - area: 60 - coordinates: [4, 5] - teleporter: [99, 8] -- name: Lava Dome Lower Moon Helm Room - Left Entrance - id: 235 - area: 60 - coordinates: [2, 19] - teleporter: [124, 0] -- name: Lava Dome Lower Moon Helm Room - Right Entrance - id: 236 - area: 60 - coordinates: [11, 21] - teleporter: [125, 0] -- name: Lava Dome Moon Helm Room - Entrance - id: 237 - area: 60 - coordinates: [15, 23] - teleporter: [126, 0] -- name: Lava Dome Three Jumps Room - To Main Loop - id: 238 - area: 61 - coordinates: [58, 15] - teleporter: [127, 0] -- name: Lava Dome Life Chest Room - Lower South Entrance - id: 239 - area: 61 - coordinates: [38, 27] - teleporter: [128, 0] -- name: Lava Dome Life Chest Room - Upper South Entrance - id: 240 - area: 61 - coordinates: [28, 23] - teleporter: [129, 0] -- name: Lava Dome Big Jump Room - Left Entrance - id: 241 - area: 62 - coordinates: [42, 51] - teleporter: [133, 0] -- name: Lava Dome Big Jump Room - North Entrance - id: 242 - area: 62 - coordinates: [30, 29] - teleporter: [131, 0] -- name: Lava Dome Big Jump Room - Lower Right Stairs - id: 243 - area: 62 - coordinates: [61, 59] - teleporter: [132, 0] -- name: Lava Dome Split Corridor - Upper Stairs - id: 244 - area: 62 - coordinates: [30, 43] - teleporter: [130, 0] -- name: Lava Dome Split Corridor - Lower Stairs - id: 245 - area: 62 - coordinates: [36, 61] - teleporter: [134, 0] -- name: Lava Dome Plate Corridor - Right Entrance - id: 246 - area: 63 - coordinates: [19, 29] - teleporter: [135, 0] -- name: Lava Dome Plate Corridor - Left Entrance - id: 247 - area: 63 - coordinates: [60, 21] - teleporter: [137, 0] -- name: Lava Dome Four Boxes Stairs - Upper Entrance - id: 248 - area: 63 - coordinates: [22, 3] - teleporter: [136, 0] -- name: Lava Dome Four Boxes Stairs - Lower Entrance - id: 249 - area: 63 - coordinates: [22, 17] - teleporter: [16, 0] -- name: Lava Dome Hydra Room - South Entrance - id: 250 - area: 64 - coordinates: [14, 59] - teleporter: [105, 3] -- name: Lava Dome Hydra Room - North Exit - id: 251 - area: 64 - coordinates: [25, 31] - teleporter: [138, 0] -- name: Lava Dome Hydra Room - Hydra Script - id: 252 - area: 64 - coordinates: [14, 36] - teleporter: [14, 8] -- name: Lava Dome Escape Corridor - South Entrance - id: 253 - area: 65 - coordinates: [22, 17] - teleporter: [139, 0] -- name: Lava Dome Escape Corridor - North Entrance - id: 254 - area: 65 - coordinates: [22, 3] - teleporter: [9, 0] -- name: Rope Bridge - West Entrance 1 - id: 255 - area: 66 - coordinates: [3, 10] - teleporter: [140, 0] -- name: Rope Bridge - West Entrance 2 - id: 256 - area: 66 - coordinates: [3, 11] - teleporter: [140, 0] -- name: Rope Bridge - West Entrance 3 - id: 257 - area: 66 - coordinates: [3, 12] - teleporter: [140, 0] -- name: Rope Bridge - West Entrance 4 - id: 258 - area: 66 - coordinates: [3, 13] - teleporter: [140, 0] -- name: Rope Bridge - West Entrance 5 - id: 259 - area: 66 - coordinates: [4, 10] - teleporter: [140, 0] -- name: Rope Bridge - West Entrance 6 - id: 260 - area: 66 - coordinates: [4, 11] - teleporter: [140, 0] -- name: Rope Bridge - West Entrance 7 - id: 261 - area: 66 - coordinates: [4, 12] - teleporter: [140, 0] -- name: Rope Bridge - West Entrance 8 - id: 262 - area: 66 - coordinates: [4, 13] - teleporter: [140, 0] -- name: Rope Bridge - East Entrance 1 - id: 263 - area: 66 - coordinates: [59, 10] - teleporter: [140, 0] -- name: Rope Bridge - East Entrance 2 - id: 264 - area: 66 - coordinates: [59, 11] - teleporter: [140, 0] -- name: Rope Bridge - East Entrance 3 - id: 265 - area: 66 - coordinates: [59, 12] - teleporter: [140, 0] -- name: Rope Bridge - East Entrance 4 - id: 266 - area: 66 - coordinates: [59, 13] - teleporter: [140, 0] -- name: Rope Bridge - East Entrance 5 - id: 267 - area: 66 - coordinates: [60, 10] - teleporter: [140, 0] -- name: Rope Bridge - East Entrance 6 - id: 268 - area: 66 - coordinates: [60, 11] - teleporter: [140, 0] -- name: Rope Bridge - East Entrance 7 - id: 269 - area: 66 - coordinates: [60, 12] - teleporter: [140, 0] -- name: Rope Bridge - East Entrance 8 - id: 270 - area: 66 - coordinates: [60, 13] - teleporter: [140, 0] -- name: Rope Bridge - Reuben Fall Script - id: 271 - area: 66 - coordinates: [13, 12] - teleporter: [15, 8] -- name: Alive Forest - West Entrance 1 - id: 272 - area: 67 - coordinates: [8, 13] - teleporter: [142, 0] -- name: Alive Forest - West Entrance 2 - id: 273 - area: 67 - coordinates: [9, 13] - teleporter: [142, 0] -- name: Alive Forest - Giant Tree Entrance - id: 274 - area: 67 - coordinates: [42, 42] - teleporter: [143, 0] -- name: Alive Forest - Libra Teleporter Script - id: 275 - area: 67 - coordinates: [8, 52] - teleporter: [64, 8] -- name: Alive Forest - Gemini Teleporter Script - id: 276 - area: 67 - coordinates: [57, 49] - teleporter: [65, 8] -- name: Alive Forest - Mobius Teleporter Script - id: 277 - area: 67 - coordinates: [24, 10] - teleporter: [66, 8] -- name: Giant Tree 1F - Entrance Script 1 - id: 278 - area: 68 - coordinates: [18, 31] - teleporter: [56, 1] # The script is restored if no map shuffling [49, 8] -- name: Giant Tree 1F - Entrance Script 2 - id: 279 - area: 68 - coordinates: [19, 31] - teleporter: [56, 1] # Same [49, 8] -- name: Giant Tree 1F - North Entrance To 2F - id: 280 - area: 68 - coordinates: [16, 1] - teleporter: [144, 0] -- name: Giant Tree 2F Main Lobby - North Entrance to 1F - id: 281 - area: 69 - coordinates: [44, 33] - teleporter: [145, 0] -- name: Giant Tree 2F Main Lobby - Central Entrance to 3F - id: 282 - area: 69 - coordinates: [42, 47] - teleporter: [146, 0] -- name: Giant Tree 2F Main Lobby - West Entrance to Mushroom Room - id: 283 - area: 69 - coordinates: [58, 49] - teleporter: [149, 0] -- name: Giant Tree 2F West Ledge - To 3F Northwest Ledge - id: 284 - area: 69 - coordinates: [34, 37] - teleporter: [147, 0] -- name: Giant Tree 2F Fall From Vine Script - id: 482 - area: 69 - coordinates: [0x2E, 0x33] - teleporter: [76, 8] -- name: Giant Tree Meteor Chest Room - To 2F Mushroom Room - id: 285 - area: 69 - coordinates: [58, 44] - teleporter: [148, 0] -- name: Giant Tree 2F Mushroom Room - Entrance - id: 286 - area: 70 - coordinates: [55, 18] - teleporter: [150, 0] -- name: Giant Tree 2F Mushroom Room - North Face to Meteor - id: 287 - area: 70 - coordinates: [56, 7] - teleporter: [151, 0] -- name: Giant Tree 3F Central Room - Central Entrance to 2F - id: 288 - area: 71 - coordinates: [46, 53] - teleporter: [152, 0] -- name: Giant Tree 3F Central Room - East Entrance to Worm Room - id: 289 - area: 71 - coordinates: [58, 39] - teleporter: [153, 0] -- name: Giant Tree 3F Lower Corridor - Entrance from Worm Room - id: 290 - area: 71 - coordinates: [45, 39] - teleporter: [154, 0] -- name: Giant Tree 3F West Platform - Lower Entrance - id: 291 - area: 71 - coordinates: [33, 43] - teleporter: [155, 0] -- name: Giant Tree 3F West Platform - Top Entrance - id: 292 - area: 71 - coordinates: [52, 25] - teleporter: [156, 0] -- name: Giant Tree Worm Room - East Entrance - id: 293 - area: 72 - coordinates: [20, 58] - teleporter: [157, 0] -- name: Giant Tree Worm Room - West Entrance - id: 294 - area: 72 - coordinates: [6, 56] - teleporter: [158, 0] -- name: Giant Tree 4F Lower Floor - Entrance - id: 295 - area: 73 - coordinates: [20, 7] - teleporter: [159, 0] -- name: Giant Tree 4F Lower Floor - Lower West Mouth - id: 296 - area: 73 - coordinates: [8, 23] - teleporter: [160, 0] -- name: Giant Tree 4F Lower Floor - Lower Central Mouth - id: 297 - area: 73 - coordinates: [14, 25] - teleporter: [161, 0] -- name: Giant Tree 4F Lower Floor - Lower East Mouth - id: 298 - area: 73 - coordinates: [20, 25] - teleporter: [162, 0] -- name: Giant Tree 4F Upper Floor - Upper West Mouth - id: 299 - area: 73 - coordinates: [8, 19] - teleporter: [163, 0] -- name: Giant Tree 4F Upper Floor - Upper Central Mouth - id: 300 - area: 73 - coordinates: [12, 17] - teleporter: [164, 0] -- name: Giant Tree 4F Slime Room - Exit - id: 301 - area: 74 - coordinates: [47, 10] - teleporter: [165, 0] -- name: Giant Tree 4F Slime Room - West Entrance - id: 302 - area: 74 - coordinates: [45, 24] - teleporter: [166, 0] -- name: Giant Tree 4F Slime Room - Central Entrance - id: 303 - area: 74 - coordinates: [50, 24] - teleporter: [167, 0] -- name: Giant Tree 4F Slime Room - East Entrance - id: 304 - area: 74 - coordinates: [57, 28] - teleporter: [168, 0] -- name: Giant Tree 5F - Entrance - id: 305 - area: 75 - coordinates: [14, 51] - teleporter: [169, 0] -- name: Giant Tree 5F - Giant Tree Face # Unused - id: 306 - area: 75 - coordinates: [14, 37] - teleporter: [170, 0] -- name: Kaidge Temple - Entrance - id: 307 - area: 77 - coordinates: [44, 63] - teleporter: [18, 6] -- name: Kaidge Temple - Mobius Teleporter Script - id: 308 - area: 77 - coordinates: [35, 57] - teleporter: [71, 8] -- name: Windhole Temple - Entrance - id: 309 - area: 78 - coordinates: [10, 29] - teleporter: [173, 0] -- name: Mount Gale - Entrance 1 - id: 310 - area: 79 - coordinates: [1, 45] - teleporter: [174, 0] -- name: Mount Gale - Entrance 2 - id: 311 - area: 79 - coordinates: [2, 45] - teleporter: [174, 0] -- name: Mount Gale - Visit Quest - id: 494 - area: 79 - coordinates: [44, 7] - teleporter: [101, 8] -- name: Windia - Main Entrance 1 - id: 312 - area: 80 - coordinates: [12, 40] - teleporter: [10, 6] -- name: Windia - Main Entrance 2 - id: 313 - area: 80 - coordinates: [13, 40] - teleporter: [10, 6] -- name: Windia - Main Entrance 3 - id: 314 - area: 80 - coordinates: [14, 40] - teleporter: [10, 6] -- name: Windia - Main Entrance 4 - id: 315 - area: 80 - coordinates: [15, 40] - teleporter: [10, 6] -- name: Windia - Main Entrance 5 - id: 316 - area: 80 - coordinates: [12, 41] - teleporter: [10, 6] -- name: Windia - Main Entrance 6 - id: 317 - area: 80 - coordinates: [13, 41] - teleporter: [10, 6] -- name: Windia - Main Entrance 7 - id: 318 - area: 80 - coordinates: [14, 41] - teleporter: [10, 6] -- name: Windia - Main Entrance 8 - id: 319 - area: 80 - coordinates: [15, 41] - teleporter: [10, 6] -- name: Windia - Otto's House - id: 320 - area: 80 - coordinates: [21, 39] - teleporter: [30, 5] -- name: Windia - INN's Script # Change to teleporter / Change back to script! - id: 321 - area: 80 - coordinates: [18, 34] - teleporter: [97, 8] # Original value [79, 8] > [31, 2] -- name: Windia - Vendor House - id: 322 - area: 80 - coordinates: [8, 36] - teleporter: [32, 5] -- name: Windia - Kid House - id: 323 - area: 80 - coordinates: [7, 23] - teleporter: [176, 4] -- name: Windia - Old People House - id: 324 - area: 80 - coordinates: [19, 21] - teleporter: [177, 4] -- name: Windia - Rainbow Bridge Script - id: 325 - area: 80 - coordinates: [21, 9] - teleporter: [10, 6] # Change to entrance, usually a script [41, 8] -- name: Otto's House - Attic Stairs - id: 326 - area: 81 - coordinates: [2, 19] - teleporter: [33, 2] -- name: Otto's House - Entrance - id: 327 - area: 81 - coordinates: [9, 30] - teleporter: [106, 3] -- name: Otto's Attic - Stairs - id: 328 - area: 81 - coordinates: [26, 23] - teleporter: [107, 3] -- name: Windia Kid House - Entrance Script # Change to teleporter - id: 329 - area: 82 - coordinates: [7, 10] - teleporter: [178, 0] # Original value [38, 8] -- name: Windia Kid House - Basement Stairs - id: 330 - area: 82 - coordinates: [1, 4] - teleporter: [180, 0] -- name: Windia Old People House - Entrance - id: 331 - area: 82 - coordinates: [55, 12] - teleporter: [179, 0] -- name: Windia Old People House - Basement Stairs - id: 332 - area: 82 - coordinates: [60, 5] - teleporter: [181, 0] -- name: Windia Kid House Basement - Stairs - id: 333 - area: 82 - coordinates: [43, 8] - teleporter: [182, 0] -- name: Windia Kid House Basement - Mobius Teleporter - id: 334 - area: 82 - coordinates: [41, 9] - teleporter: [44, 8] -- name: Windia Old People House Basement - Stairs - id: 335 - area: 82 - coordinates: [39, 26] - teleporter: [183, 0] -- name: Windia Old People House Basement - Mobius Teleporter Script - id: 336 - area: 82 - coordinates: [39, 23] - teleporter: [43, 8] -- name: Windia Inn Lobby - Stairs to Beds - id: 337 - area: 82 - coordinates: [45, 24] - teleporter: [102, 8] # Changed to script, original value [215, 0] -- name: Windia Inn Lobby - Exit - id: 338 - area: 82 - coordinates: [53, 30] - teleporter: [135, 3] -- name: Windia Inn Beds - Stairs to Lobby - id: 339 - area: 82 - coordinates: [33, 59] - teleporter: [216, 0] -- name: Windia Vendor House - Entrance - id: 340 - area: 82 - coordinates: [29, 14] - teleporter: [108, 3] -- name: Pazuzu Tower 1F Main Lobby - Main Entrance 1 - id: 341 - area: 83 - coordinates: [47, 29] - teleporter: [184, 0] -- name: Pazuzu Tower 1F Main Lobby - Main Entrance 2 - id: 342 - area: 83 - coordinates: [47, 30] - teleporter: [184, 0] -- name: Pazuzu Tower 1F Main Lobby - Main Entrance 3 - id: 343 - area: 83 - coordinates: [48, 29] - teleporter: [184, 0] -- name: Pazuzu Tower 1F Main Lobby - Main Entrance 4 - id: 344 - area: 83 - coordinates: [48, 30] - teleporter: [184, 0] -- name: Pazuzu Tower 1F Main Lobby - East Entrance - id: 345 - area: 83 - coordinates: [55, 12] - teleporter: [185, 0] -- name: Pazuzu Tower 1F Main Lobby - South Stairs - id: 346 - area: 83 - coordinates: [51, 25] - teleporter: [186, 0] -- name: Pazuzu Tower 1F Main Lobby - Pazuzu Script 1 - id: 347 - area: 83 - coordinates: [47, 8] - teleporter: [16, 8] -- name: Pazuzu Tower 1F Main Lobby - Pazuzu Script 2 - id: 348 - area: 83 - coordinates: [48, 8] - teleporter: [16, 8] -- name: Pazuzu Tower 1F Boxes Room - West Stairs - id: 349 - area: 83 - coordinates: [38, 17] - teleporter: [187, 0] -- name: Pazuzu 2F - West Upper Stairs - id: 350 - area: 84 - coordinates: [7, 11] - teleporter: [188, 0] -- name: Pazuzu 2F - South Stairs - id: 351 - area: 84 - coordinates: [20, 24] - teleporter: [189, 0] -- name: Pazuzu 2F - West Lower Stairs - id: 352 - area: 84 - coordinates: [6, 17] - teleporter: [190, 0] -- name: Pazuzu 2F - Central Stairs - id: 353 - area: 84 - coordinates: [15, 15] - teleporter: [191, 0] -- name: Pazuzu 2F - Pazuzu 1 - id: 354 - area: 84 - coordinates: [15, 8] - teleporter: [17, 8] -- name: Pazuzu 2F - Pazuzu 2 - id: 355 - area: 84 - coordinates: [16, 8] - teleporter: [17, 8] -- name: Pazuzu 3F Main Room - North Stairs - id: 356 - area: 85 - coordinates: [23, 11] - teleporter: [192, 0] -- name: Pazuzu 3F Main Room - West Stairs - id: 357 - area: 85 - coordinates: [7, 15] - teleporter: [193, 0] -- name: Pazuzu 3F Main Room - Pazuzu Script 1 - id: 358 - area: 85 - coordinates: [15, 8] - teleporter: [18, 8] -- name: Pazuzu 3F Main Room - Pazuzu Script 2 - id: 359 - area: 85 - coordinates: [16, 8] - teleporter: [18, 8] -- name: Pazuzu 3F Central Island - Central Stairs - id: 360 - area: 85 - coordinates: [15, 14] - teleporter: [194, 0] -- name: Pazuzu 3F Central Island - South Stairs - id: 361 - area: 85 - coordinates: [17, 25] - teleporter: [195, 0] -- name: Pazuzu 4F - Northwest Stairs - id: 362 - area: 86 - coordinates: [39, 12] - teleporter: [196, 0] -- name: Pazuzu 4F - Southwest Stairs - id: 363 - area: 86 - coordinates: [39, 19] - teleporter: [197, 0] -- name: Pazuzu 4F - South Stairs - id: 364 - area: 86 - coordinates: [47, 24] - teleporter: [198, 0] -- name: Pazuzu 4F - Northeast Stairs - id: 365 - area: 86 - coordinates: [54, 9] - teleporter: [199, 0] -- name: Pazuzu 4F - Pazuzu Script 1 - id: 366 - area: 86 - coordinates: [47, 8] - teleporter: [19, 8] -- name: Pazuzu 4F - Pazuzu Script 2 - id: 367 - area: 86 - coordinates: [48, 8] - teleporter: [19, 8] -- name: Pazuzu 5F Pazuzu Loop - West Stairs - id: 368 - area: 87 - coordinates: [9, 49] - teleporter: [200, 0] -- name: Pazuzu 5F Pazuzu Loop - South Stairs - id: 369 - area: 87 - coordinates: [16, 55] - teleporter: [201, 0] -- name: Pazuzu 5F Upper Loop - Northeast Stairs - id: 370 - area: 87 - coordinates: [22, 40] - teleporter: [202, 0] -- name: Pazuzu 5F Upper Loop - Northwest Stairs - id: 371 - area: 87 - coordinates: [9, 40] - teleporter: [203, 0] -- name: Pazuzu 5F Upper Loop - Pazuzu Script 1 - id: 372 - area: 87 - coordinates: [15, 40] - teleporter: [20, 8] -- name: Pazuzu 5F Upper Loop - Pazuzu Script 2 - id: 373 - area: 87 - coordinates: [16, 40] - teleporter: [20, 8] -- name: Pazuzu 6F - West Stairs - id: 374 - area: 88 - coordinates: [41, 47] - teleporter: [204, 0] -- name: Pazuzu 6F - Northwest Stairs - id: 375 - area: 88 - coordinates: [41, 40] - teleporter: [205, 0] -- name: Pazuzu 6F - Northeast Stairs - id: 376 - area: 88 - coordinates: [54, 40] - teleporter: [206, 0] -- name: Pazuzu 6F - South Stairs - id: 377 - area: 88 - coordinates: [52, 56] - teleporter: [207, 0] -- name: Pazuzu 6F - Pazuzu Script 1 - id: 378 - area: 88 - coordinates: [47, 40] - teleporter: [21, 8] -- name: Pazuzu 6F - Pazuzu Script 2 - id: 379 - area: 88 - coordinates: [48, 40] - teleporter: [21, 8] -- name: Pazuzu 7F Main Room - Southwest Stairs - id: 380 - area: 89 - coordinates: [15, 54] - teleporter: [26, 0] -- name: Pazuzu 7F Main Room - Northeast Stairs - id: 381 - area: 89 - coordinates: [21, 40] - teleporter: [27, 0] -- name: Pazuzu 7F Main Room - Southeast Stairs - id: 382 - area: 89 - coordinates: [21, 56] - teleporter: [28, 0] -- name: Pazuzu 7F Main Room - Pazuzu Script 1 - id: 383 - area: 89 - coordinates: [15, 44] - teleporter: [22, 8] -- name: Pazuzu 7F Main Room - Pazuzu Script 2 - id: 384 - area: 89 - coordinates: [16, 44] - teleporter: [22, 8] -- name: Pazuzu 7F Main Room - Crystal Script # Added for floor shuffle - id: 480 - area: 89 - coordinates: [15, 40] - teleporter: [38, 8] -- name: Pazuzu 1F to 3F - South Stairs - id: 385 - area: 90 - coordinates: [43, 60] - teleporter: [29, 0] -- name: Pazuzu 1F to 3F - North Stairs - id: 386 - area: 90 - coordinates: [43, 36] - teleporter: [30, 0] -- name: Pazuzu 3F to 5F - South Stairs - id: 387 - area: 91 - coordinates: [43, 60] - teleporter: [40, 0] -- name: Pazuzu 3F to 5F - North Stairs - id: 388 - area: 91 - coordinates: [43, 36] - teleporter: [41, 0] -- name: Pazuzu 5F to 7F - South Stairs - id: 389 - area: 92 - coordinates: [43, 60] - teleporter: [38, 0] -- name: Pazuzu 5F to 7F - North Stairs - id: 390 - area: 92 - coordinates: [43, 36] - teleporter: [39, 0] -- name: Pazuzu 2F to 4F - South Stairs - id: 391 - area: 93 - coordinates: [43, 60] - teleporter: [21, 0] -- name: Pazuzu 2F to 4F - North Stairs - id: 392 - area: 93 - coordinates: [43, 36] - teleporter: [22, 0] -- name: Pazuzu 4F to 6F - South Stairs - id: 393 - area: 94 - coordinates: [43, 60] - teleporter: [2, 0] -- name: Pazuzu 4F to 6F - North Stairs - id: 394 - area: 94 - coordinates: [43, 36] - teleporter: [3, 0] -- name: Light Temple - Entrance - id: 395 - area: 95 - coordinates: [28, 57] - teleporter: [19, 6] -- name: Light Temple - Mobius Teleporter Script - id: 396 - area: 95 - coordinates: [29, 37] - teleporter: [70, 8] -- name: Light Temple - Visit Quest Script 1 - id: 492 - area: 95 - coordinates: [34, 39] - teleporter: [100, 8] -- name: Light Temple - Visit Quest Script 2 - id: 493 - area: 95 - coordinates: [35, 39] - teleporter: [100, 8] -- name: Ship Dock - Mobius Teleporter Script - id: 397 - area: 96 - coordinates: [15, 18] - teleporter: [61, 8] -- name: Ship Dock - From Overworld - id: 398 - area: 96 - coordinates: [15, 11] - teleporter: [73, 0] -- name: Ship Dock - Entrance - id: 399 - area: 96 - coordinates: [15, 23] - teleporter: [17, 6] -- name: Mac Ship Deck - East Entrance Script - id: 400 - area: 97 - coordinates: [26, 40] - teleporter: [37, 8] -- name: Mac Ship Deck - Central Stairs Script - id: 401 - area: 97 - coordinates: [16, 47] - teleporter: [50, 8] -- name: Mac Ship Deck - West Stairs Script - id: 402 - area: 97 - coordinates: [8, 34] - teleporter: [51, 8] -- name: Mac Ship Deck - East Stairs Script - id: 403 - area: 97 - coordinates: [24, 36] - teleporter: [52, 8] -- name: Mac Ship Deck - North Stairs Script - id: 404 - area: 97 - coordinates: [12, 9] - teleporter: [53, 8] -- name: Mac Ship B1 Outer Ring - South Stairs - id: 405 - area: 98 - coordinates: [16, 45] - teleporter: [208, 0] -- name: Mac Ship B1 Outer Ring - West Stairs - id: 406 - area: 98 - coordinates: [8, 35] - teleporter: [175, 0] -- name: Mac Ship B1 Outer Ring - East Stairs - id: 407 - area: 98 - coordinates: [25, 37] - teleporter: [172, 0] -- name: Mac Ship B1 Outer Ring - Northwest Stairs - id: 408 - area: 98 - coordinates: [10, 23] - teleporter: [88, 0] -- name: Mac Ship B1 Square Room - North Stairs - id: 409 - area: 98 - coordinates: [14, 9] - teleporter: [141, 0] -- name: Mac Ship B1 Square Room - South Stairs - id: 410 - area: 98 - coordinates: [16, 12] - teleporter: [87, 0] -- name: Mac Ship B1 Mac Room - Stairs # Unused? - id: 411 - area: 98 - coordinates: [16, 51] - teleporter: [101, 0] -- name: Mac Ship B1 Central Corridor - South Stairs - id: 412 - area: 98 - coordinates: [16, 38] - teleporter: [102, 0] -- name: Mac Ship B1 Central Corridor - North Stairs - id: 413 - area: 98 - coordinates: [16, 26] - teleporter: [86, 0] -- name: Mac Ship B2 South Corridor - South Stairs - id: 414 - area: 99 - coordinates: [48, 51] - teleporter: [57, 1] -- name: Mac Ship B2 South Corridor - North Stairs Script - id: 415 - area: 99 - coordinates: [48, 38] - teleporter: [55, 8] -- name: Mac Ship B2 North Corridor - South Stairs Script - id: 416 - area: 99 - coordinates: [48, 27] - teleporter: [56, 8] -- name: Mac Ship B2 North Corridor - North Stairs Script - id: 417 - area: 99 - coordinates: [48, 12] - teleporter: [57, 8] -- name: Mac Ship B2 Outer Ring - Northwest Stairs Script - id: 418 - area: 99 - coordinates: [55, 11] - teleporter: [58, 8] -- name: Mac Ship B1 Outer Ring Cleared - South Stairs - id: 419 - area: 100 - coordinates: [16, 45] - teleporter: [208, 0] -- name: Mac Ship B1 Outer Ring Cleared - West Stairs - id: 420 - area: 100 - coordinates: [8, 35] - teleporter: [175, 0] -- name: Mac Ship B1 Outer Ring Cleared - East Stairs - id: 421 - area: 100 - coordinates: [25, 37] - teleporter: [172, 0] -- name: Mac Ship B1 Square Room Cleared - North Stairs - id: 422 - area: 100 - coordinates: [14, 9] - teleporter: [141, 0] -- name: Mac Ship B1 Square Room Cleared - South Stairs - id: 423 - area: 100 - coordinates: [16, 12] - teleporter: [87, 0] -- name: Mac Ship B1 Mac Room Cleared - Main Stairs - id: 424 - area: 100 - coordinates: [16, 51] - teleporter: [101, 0] -- name: Mac Ship B1 Central Corridor Cleared - South Stairs - id: 425 - area: 100 - coordinates: [16, 38] - teleporter: [102, 0] -- name: Mac Ship B1 Central Corridor Cleared - North Stairs - id: 426 - area: 100 - coordinates: [16, 26] - teleporter: [86, 0] -- name: Mac Ship B1 Central Corridor Cleared - Northwest Stairs - id: 427 - area: 100 - coordinates: [23, 10] - teleporter: [88, 0] -- name: Doom Castle Corridor of Destiny - South Entrance - id: 428 - area: 101 - coordinates: [59, 29] - teleporter: [84, 0] -- name: Doom Castle Corridor of Destiny - Ice Floor Entrance - id: 429 - area: 101 - coordinates: [59, 21] - teleporter: [35, 2] -- name: Doom Castle Corridor of Destiny - Lava Floor Entrance - id: 430 - area: 101 - coordinates: [59, 13] - teleporter: [209, 0] -- name: Doom Castle Corridor of Destiny - Sky Floor Entrance - id: 431 - area: 101 - coordinates: [59, 5] - teleporter: [211, 0] -- name: Doom Castle Corridor of Destiny - Hero Room Entrance - id: 432 - area: 101 - coordinates: [59, 61] - teleporter: [13, 2] -- name: Doom Castle Ice Floor - Entrance - id: 433 - area: 102 - coordinates: [23, 42] - teleporter: [109, 3] -- name: Doom Castle Lava Floor - Entrance - id: 434 - area: 103 - coordinates: [23, 40] - teleporter: [210, 0] -- name: Doom Castle Sky Floor - Entrance - id: 435 - area: 104 - coordinates: [24, 41] - teleporter: [212, 0] -- name: Doom Castle Hero Room - Dark King Entrance 1 - id: 436 - area: 106 - coordinates: [15, 5] - teleporter: [54, 0] -- name: Doom Castle Hero Room - Dark King Entrance 2 - id: 437 - area: 106 - coordinates: [16, 5] - teleporter: [54, 0] -- name: Doom Castle Hero Room - Dark King Entrance 3 - id: 438 - area: 106 - coordinates: [15, 4] - teleporter: [54, 0] -- name: Doom Castle Hero Room - Dark King Entrance 4 - id: 439 - area: 106 - coordinates: [16, 4] - teleporter: [54, 0] -- name: Doom Castle Hero Room - Hero Statue Script - id: 440 - area: 106 - coordinates: [15, 17] - teleporter: [24, 8] -- name: Doom Castle Hero Room - Entrance - id: 441 - area: 106 - coordinates: [15, 24] - teleporter: [110, 3] -- name: Doom Castle Dark King Room - Entrance - id: 442 - area: 107 - coordinates: [14, 26] - teleporter: [52, 0] -- name: Doom Castle Dark King Room - Dark King Script - id: 443 - area: 107 - coordinates: [14, 15] - teleporter: [25, 8] -- name: Doom Castle Dark King Room - Unknown - id: 444 - area: 107 - coordinates: [47, 54] - teleporter: [77, 0] -- name: Overworld - Level Forest - id: 445 - area: 0 - type: "Overworld" - teleporter: [0x2E, 8] -- name: Overworld - Foresta - id: 446 - area: 0 - type: "Overworld" - teleporter: [0x02, 1] -- name: Overworld - Sand Temple - id: 447 - area: 0 - type: "Overworld" - teleporter: [0x03, 1] -- name: Overworld - Bone Dungeon - id: 448 - area: 0 - type: "Overworld" - teleporter: [0x04, 1] -- name: Overworld - Focus Tower Foresta - id: 449 - area: 0 - type: "Overworld" - teleporter: [0x05, 1] -- name: Overworld - Focus Tower Aquaria - id: 450 - area: 0 - type: "Overworld" - teleporter: [0x13, 1] -- name: Overworld - Libra Temple - id: 451 - area: 0 - type: "Overworld" - teleporter: [0x07, 1] -- name: Overworld - Aquaria - id: 452 - area: 0 - type: "Overworld" - teleporter: [0x08, 8] -- name: Overworld - Wintry Cave - id: 453 - area: 0 - type: "Overworld" - teleporter: [0x0A, 1] -- name: Overworld - Life Temple - id: 454 - area: 0 - type: "Overworld" - teleporter: [0x0B, 1] -- name: Overworld - Falls Basin - id: 455 - area: 0 - type: "Overworld" - teleporter: [0x0C, 1] -- name: Overworld - Ice Pyramid - id: 456 - area: 0 - type: "Overworld" - teleporter: [0x0D, 1] # Will be switched to a script -- name: Overworld - Spencer's Place - id: 457 - area: 0 - type: "Overworld" - teleporter: [0x30, 8] -- name: Overworld - Wintry Temple - id: 458 - area: 0 - type: "Overworld" - teleporter: [0x10, 1] -- name: Overworld - Focus Tower Frozen Strip - id: 459 - area: 0 - type: "Overworld" - teleporter: [0x11, 1] -- name: Overworld - Focus Tower Fireburg - id: 460 - area: 0 - type: "Overworld" - teleporter: [0x12, 1] -- name: Overworld - Fireburg - id: 461 - area: 0 - type: "Overworld" - teleporter: [0x14, 1] -- name: Overworld - Mine - id: 462 - area: 0 - type: "Overworld" - teleporter: [0x15, 1] -- name: Overworld - Sealed Temple - id: 463 - area: 0 - type: "Overworld" - teleporter: [0x16, 1] -- name: Overworld - Volcano - id: 464 - area: 0 - type: "Overworld" - teleporter: [0x17, 1] -- name: Overworld - Lava Dome - id: 465 - area: 0 - type: "Overworld" - teleporter: [0x18, 1] -- name: Overworld - Focus Tower Windia - id: 466 - area: 0 - type: "Overworld" - teleporter: [0x06, 1] -- name: Overworld - Rope Bridge - id: 467 - area: 0 - type: "Overworld" - teleporter: [0x19, 1] -- name: Overworld - Alive Forest - id: 468 - area: 0 - type: "Overworld" - teleporter: [0x1A, 1] -- name: Overworld - Giant Tree - id: 469 - area: 0 - type: "Overworld" - teleporter: [0x1B, 1] -- name: Overworld - Kaidge Temple - id: 470 - area: 0 - type: "Overworld" - teleporter: [0x1C, 1] -- name: Overworld - Windia - id: 471 - area: 0 - type: "Overworld" - teleporter: [0x1D, 1] -- name: Overworld - Windhole Temple - id: 472 - area: 0 - type: "Overworld" - teleporter: [0x1E, 1] -- name: Overworld - Mount Gale - id: 473 - area: 0 - type: "Overworld" - teleporter: [0x1F, 1] -- name: Overworld - Pazuzu Tower - id: 474 - area: 0 - type: "Overworld" - teleporter: [0x20, 1] -- name: Overworld - Ship Dock - id: 475 - area: 0 - type: "Overworld" - teleporter: [0x3E, 1] -- name: Overworld - Doom Castle - id: 476 - area: 0 - type: "Overworld" - teleporter: [0x21, 1] -- name: Overworld - Light Temple - id: 477 - area: 0 - type: "Overworld" - teleporter: [0x22, 1] -- name: Overworld - Mac Ship - id: 478 - area: 0 - type: "Overworld" - teleporter: [0x24, 1] -- name: Overworld - Mac Ship Doom - id: 479 - area: 0 - type: "Overworld" - teleporter: [0x24, 1] -- name: Dummy House - Bed Script - id: 480 - area: 17 - coordinates: [0x28, 0x38] - teleporter: [1, 8] -- name: Dummy House - Entrance - id: 481 - area: 17 - coordinates: [0x29, 0x3B] - teleporter: [0, 10] #None diff --git a/worlds/ffmq/data/rooms.py b/worlds/ffmq/data/rooms.py new file mode 100644 index 00000000000..38634f10767 --- /dev/null +++ b/worlds/ffmq/data/rooms.py @@ -0,0 +1,2 @@ +rooms = [{'name': 'Overworld', 'id': 0, 'type': 'Overworld', 'game_objects': [], 'links': [{'target_room': 220, 'access': []}]}, {'name': 'Subregion Foresta', 'id': 220, 'type': 'Subregion', 'region': 'Foresta', 'game_objects': [{'name': 'Foresta South Battlefield', 'object_id': 1, 'location': 'ForestaSouthBattlefield', 'location_slot': 'ForestaSouthBattlefield', 'type': 'BattlefieldXp', 'access': []}, {'name': 'Foresta West Battlefield', 'object_id': 2, 'location': 'ForestaWestBattlefield', 'location_slot': 'ForestaWestBattlefield', 'type': 'BattlefieldItem', 'access': []}, {'name': 'Foresta East Battlefield', 'object_id': 3, 'location': 'ForestaEastBattlefield', 'location_slot': 'ForestaEastBattlefield', 'type': 'BattlefieldGp', 'access': []}], 'links': [{'target_room': 15, 'location': 'LevelForest', 'location_slot': 'LevelForest', 'entrance': 445, 'teleporter': [46, 8], 'access': []}, {'target_room': 16, 'location': 'Foresta', 'location_slot': 'Foresta', 'entrance': 446, 'teleporter': [2, 1], 'access': []}, {'target_room': 24, 'location': 'SandTemple', 'location_slot': 'SandTemple', 'entrance': 447, 'teleporter': [3, 1], 'access': []}, {'target_room': 25, 'location': 'BoneDungeon', 'location_slot': 'BoneDungeon', 'entrance': 448, 'teleporter': [4, 1], 'access': []}, {'target_room': 3, 'location': 'FocusTowerForesta', 'location_slot': 'FocusTowerForesta', 'entrance': 449, 'teleporter': [5, 1], 'access': []}, {'target_room': 221, 'access': ['SandCoin']}, {'target_room': 224, 'access': ['RiverCoin']}, {'target_room': 226, 'access': ['SunCoin']}]}, {'name': 'Subregion Aquaria', 'id': 221, 'type': 'Subregion', 'region': 'Aquaria', 'game_objects': [{'name': 'South of Libra Temple Battlefield', 'object_id': 4, 'location': 'AquariaBattlefield01', 'location_slot': 'AquariaBattlefield01', 'type': 'BattlefieldXp', 'access': []}, {'name': 'East of Libra Temple Battlefield', 'object_id': 5, 'location': 'AquariaBattlefield02', 'location_slot': 'AquariaBattlefield02', 'type': 'BattlefieldGp', 'access': []}, {'name': 'South of Aquaria Battlefield', 'object_id': 6, 'location': 'AquariaBattlefield03', 'location_slot': 'AquariaBattlefield03', 'type': 'BattlefieldItem', 'access': []}, {'name': 'South of Wintry Cave Battlefield', 'object_id': 7, 'location': 'WintryBattlefield01', 'location_slot': 'WintryBattlefield01', 'type': 'BattlefieldXp', 'access': []}, {'name': 'West of Wintry Cave Battlefield', 'object_id': 8, 'location': 'WintryBattlefield02', 'location_slot': 'WintryBattlefield02', 'type': 'BattlefieldGp', 'access': []}, {'name': 'Ice Pyramid Battlefield', 'object_id': 9, 'location': 'PyramidBattlefield01', 'location_slot': 'PyramidBattlefield01', 'type': 'BattlefieldXp', 'access': []}], 'links': [{'target_room': 10, 'location': 'FocusTowerAquaria', 'location_slot': 'FocusTowerAquaria', 'entrance': 450, 'teleporter': [19, 1], 'access': []}, {'target_room': 39, 'location': 'LibraTemple', 'location_slot': 'LibraTemple', 'entrance': 451, 'teleporter': [7, 1], 'access': []}, {'target_room': 40, 'location': 'Aquaria', 'location_slot': 'Aquaria', 'entrance': 452, 'teleporter': [8, 8], 'access': []}, {'target_room': 45, 'location': 'WintryCave', 'location_slot': 'WintryCave', 'entrance': 453, 'teleporter': [10, 1], 'access': []}, {'target_room': 52, 'location': 'FallsBasin', 'location_slot': 'FallsBasin', 'entrance': 455, 'teleporter': [12, 1], 'access': []}, {'target_room': 54, 'location': 'IcePyramid', 'location_slot': 'IcePyramid', 'entrance': 456, 'teleporter': [13, 1], 'access': []}, {'target_room': 220, 'access': ['SandCoin']}, {'target_room': 224, 'access': ['SandCoin', 'RiverCoin']}, {'target_room': 226, 'access': ['SandCoin', 'SunCoin']}, {'target_room': 223, 'access': ['SummerAquaria']}]}, {'name': 'Subregion Life Temple', 'id': 222, 'type': 'Subregion', 'region': 'LifeTemple', 'game_objects': [], 'links': [{'target_room': 51, 'location': 'LifeTemple', 'location_slot': 'LifeTemple', 'entrance': 454, 'teleporter': [11, 1], 'access': []}]}, {'name': 'Subregion Frozen Fields', 'id': 223, 'type': 'Subregion', 'region': 'AquariaFrozenField', 'game_objects': [{'name': 'North of Libra Temple Battlefield', 'object_id': 10, 'location': 'LibraBattlefield01', 'location_slot': 'LibraBattlefield01', 'type': 'BattlefieldItem', 'access': []}, {'name': 'Aquaria Frozen Field Battlefield', 'object_id': 11, 'location': 'LibraBattlefield02', 'location_slot': 'LibraBattlefield02', 'type': 'BattlefieldXp', 'access': []}], 'links': [{'target_room': 74, 'location': 'WintryTemple', 'location_slot': 'WintryTemple', 'entrance': 458, 'teleporter': [16, 1], 'access': []}, {'target_room': 14, 'location': 'FocusTowerFrozen', 'location_slot': 'FocusTowerFrozen', 'entrance': 459, 'teleporter': [17, 1], 'access': []}, {'target_room': 221, 'access': []}, {'target_room': 225, 'access': ['SummerAquaria', 'DualheadHydra']}]}, {'name': 'Subregion Fireburg', 'id': 224, 'type': 'Subregion', 'region': 'Fireburg', 'game_objects': [{'name': 'Path to Fireburg Southern Battlefield', 'object_id': 12, 'location': 'FireburgBattlefield01', 'location_slot': 'FireburgBattlefield01', 'type': 'BattlefieldGp', 'access': []}, {'name': 'Path to Fireburg Central Battlefield', 'object_id': 13, 'location': 'FireburgBattlefield02', 'location_slot': 'FireburgBattlefield02', 'type': 'BattlefieldItem', 'access': []}, {'name': 'Path to Fireburg Northern Battlefield', 'object_id': 14, 'location': 'FireburgBattlefield03', 'location_slot': 'FireburgBattlefield03', 'type': 'BattlefieldXp', 'access': []}, {'name': 'Sealed Temple Battlefield', 'object_id': 15, 'location': 'MineBattlefield01', 'location_slot': 'MineBattlefield01', 'type': 'BattlefieldGp', 'access': []}, {'name': 'Mine Battlefield', 'object_id': 16, 'location': 'MineBattlefield02', 'location_slot': 'MineBattlefield02', 'type': 'BattlefieldItem', 'access': []}, {'name': 'Boulder Battlefield', 'object_id': 17, 'location': 'MineBattlefield03', 'location_slot': 'MineBattlefield03', 'type': 'BattlefieldXp', 'access': []}], 'links': [{'target_room': 13, 'location': 'FocusTowerFireburg', 'location_slot': 'FocusTowerFireburg', 'entrance': 460, 'teleporter': [18, 1], 'access': []}, {'target_room': 76, 'location': 'Fireburg', 'location_slot': 'Fireburg', 'entrance': 461, 'teleporter': [20, 1], 'access': []}, {'target_room': 84, 'location': 'Mine', 'location_slot': 'Mine', 'entrance': 462, 'teleporter': [21, 1], 'access': []}, {'target_room': 92, 'location': 'SealedTemple', 'location_slot': 'SealedTemple', 'entrance': 463, 'teleporter': [22, 1], 'access': []}, {'target_room': 93, 'location': 'Volcano', 'location_slot': 'Volcano', 'entrance': 464, 'teleporter': [23, 1], 'access': []}, {'target_room': 100, 'location': 'LavaDome', 'location_slot': 'LavaDome', 'entrance': 465, 'teleporter': [24, 1], 'access': []}, {'target_room': 220, 'access': ['RiverCoin']}, {'target_room': 221, 'access': ['SandCoin', 'RiverCoin']}, {'target_room': 226, 'access': ['RiverCoin', 'SunCoin']}, {'target_room': 225, 'access': ['DualheadHydra']}]}, {'name': 'Subregion Volcano Battlefield', 'id': 225, 'type': 'Subregion', 'region': 'VolcanoBattlefield', 'game_objects': [{'name': 'Volcano Battlefield', 'object_id': 18, 'location': 'VolcanoBattlefield01', 'location_slot': 'VolcanoBattlefield01', 'type': 'BattlefieldXp', 'access': []}], 'links': [{'target_room': 224, 'access': ['DualheadHydra']}, {'target_room': 223, 'access': ['SummerAquaria']}]}, {'name': 'Subregion Windia', 'id': 226, 'type': 'Subregion', 'region': 'Windia', 'game_objects': [{'name': 'Kaidge Temple Battlefield', 'object_id': 19, 'location': 'WindiaBattlefield01', 'location_slot': 'WindiaBattlefield01', 'type': 'BattlefieldXp', 'access': ['SandCoin', 'RiverCoin']}, {'name': 'South of Windia Battlefield', 'object_id': 20, 'location': 'WindiaBattlefield02', 'location_slot': 'WindiaBattlefield02', 'type': 'BattlefieldXp', 'access': ['SandCoin', 'RiverCoin']}], 'links': [{'target_room': 9, 'location': 'FocusTowerWindia', 'location_slot': 'FocusTowerWindia', 'entrance': 466, 'teleporter': [6, 1], 'access': []}, {'target_room': 123, 'location': 'RopeBridge', 'location_slot': 'RopeBridge', 'entrance': 467, 'teleporter': [25, 1], 'access': []}, {'target_room': 124, 'location': 'AliveForest', 'location_slot': 'AliveForest', 'entrance': 468, 'teleporter': [26, 1], 'access': []}, {'target_room': 125, 'location': 'GiantTree', 'location_slot': 'GiantTree', 'entrance': 469, 'teleporter': [27, 1], 'access': ['Barred']}, {'target_room': 152, 'location': 'KaidgeTemple', 'location_slot': 'KaidgeTemple', 'entrance': 470, 'teleporter': [28, 1], 'access': []}, {'target_room': 156, 'location': 'Windia', 'location_slot': 'Windia', 'entrance': 471, 'teleporter': [29, 1], 'access': []}, {'target_room': 154, 'location': 'WindholeTemple', 'location_slot': 'WindholeTemple', 'entrance': 472, 'teleporter': [30, 1], 'access': []}, {'target_room': 155, 'location': 'MountGale', 'location_slot': 'MountGale', 'entrance': 473, 'teleporter': [31, 1], 'access': []}, {'target_room': 166, 'location': 'PazuzusTower', 'location_slot': 'PazuzusTower', 'entrance': 474, 'teleporter': [32, 1], 'access': []}, {'target_room': 220, 'access': ['SunCoin']}, {'target_room': 221, 'access': ['SandCoin', 'SunCoin']}, {'target_room': 224, 'access': ['RiverCoin', 'SunCoin']}, {'target_room': 227, 'access': ['RainbowBridge']}]}, {'name': "Subregion Spencer's Cave", 'id': 227, 'type': 'Subregion', 'region': 'SpencerCave', 'game_objects': [], 'links': [{'target_room': 73, 'location': 'SpencersPlace', 'location_slot': 'SpencersPlace', 'entrance': 457, 'teleporter': [48, 8], 'access': []}, {'target_room': 226, 'access': ['RainbowBridge']}]}, {'name': 'Subregion Ship Dock', 'id': 228, 'type': 'Subregion', 'region': 'ShipDock', 'game_objects': [], 'links': [{'target_room': 186, 'location': 'ShipDock', 'location_slot': 'ShipDock', 'entrance': 475, 'teleporter': [62, 1], 'access': []}, {'target_room': 229, 'access': ['ShipLiberated', 'ShipDockAccess']}]}, {'name': "Subregion Mac's Ship", 'id': 229, 'type': 'Subregion', 'region': 'MacShip', 'game_objects': [], 'links': [{'target_room': 187, 'location': 'MacsShip', 'location_slot': 'MacsShip', 'entrance': 478, 'teleporter': [36, 1], 'access': []}, {'target_room': 228, 'access': ['ShipLiberated', 'ShipDockAccess']}, {'target_room': 231, 'access': ['ShipLoaned', 'ShipDockAccess', 'ShipSteeringWheel']}]}, {'name': 'Subregion Light Temple', 'id': 230, 'type': 'Subregion', 'region': 'LightTemple', 'game_objects': [], 'links': [{'target_room': 185, 'location': 'LightTemple', 'location_slot': 'LightTemple', 'entrance': 477, 'teleporter': [35, 1], 'access': []}]}, {'name': 'Subregion Doom Castle', 'id': 231, 'type': 'Subregion', 'region': 'DoomCastle', 'game_objects': [], 'links': [{'target_room': 1, 'location': 'DoomCastle', 'location_slot': 'DoomCastle', 'entrance': 476, 'teleporter': [33, 1], 'access': []}, {'target_room': 187, 'location': 'MacsShipDoom', 'location_slot': 'MacsShipDoom', 'entrance': 479, 'teleporter': [36, 1], 'access': ['Barred']}, {'target_room': 229, 'access': ['ShipLoaned', 'ShipDockAccess', 'ShipSteeringWheel']}]}, {'name': 'Doom Castle - Sand Floor', 'id': 1, 'game_objects': [{'name': 'Doom Castle B2 - Southeast Chest', 'object_id': 1, 'type': 'Chest', 'access': ['Bomb']}, {'name': 'Doom Castle B2 - Bone Ledge Box', 'object_id': 30, 'type': 'Box', 'access': []}, {'name': 'Doom Castle B2 - Hook Platform Box', 'object_id': 31, 'type': 'Box', 'access': ['DragonClaw']}], 'links': [{'target_room': 231, 'entrance': 1, 'teleporter': [1, 6], 'access': []}, {'target_room': 5, 'entrance': 0, 'teleporter': [0, 0], 'access': ['DragonClaw', 'MegaGrenade']}]}, {'name': 'Doom Castle - Aero Room', 'id': 2, 'game_objects': [{'name': 'Doom Castle B2 - Sun Door Chest', 'object_id': 0, 'type': 'Chest', 'access': []}], 'links': [{'target_room': 4, 'entrance': 2, 'teleporter': [1, 0], 'access': []}]}, {'name': 'Focus Tower B1 - Main Loop', 'id': 3, 'game_objects': [], 'links': [{'target_room': 220, 'entrance': 3, 'teleporter': [2, 6], 'access': []}, {'target_room': 6, 'entrance': 4, 'teleporter': [4, 0], 'access': []}]}, {'name': 'Focus Tower B1 - Aero Corridor', 'id': 4, 'game_objects': [], 'links': [{'target_room': 9, 'entrance': 5, 'teleporter': [5, 0], 'access': []}, {'target_room': 2, 'entrance': 6, 'teleporter': [8, 0], 'access': []}]}, {'name': 'Focus Tower B1 - Inner Loop', 'id': 5, 'game_objects': [], 'links': [{'target_room': 1, 'entrance': 8, 'teleporter': [7, 0], 'access': []}, {'target_room': 201, 'entrance': 7, 'teleporter': [6, 0], 'access': []}]}, {'name': 'Focus Tower 1F Main Lobby', 'id': 6, 'game_objects': [{'name': 'Focus Tower 1F - Main Lobby Box', 'object_id': 33, 'type': 'Box', 'access': []}], 'links': [{'target_room': 3, 'entrance': 11, 'teleporter': [11, 0], 'access': []}, {'target_room': 7, 'access': ['SandCoin']}, {'target_room': 8, 'access': ['RiverCoin']}, {'target_room': 9, 'access': ['SunCoin']}]}, {'name': 'Focus Tower 1F SandCoin Room', 'id': 7, 'game_objects': [], 'links': [{'target_room': 6, 'access': ['SandCoin']}, {'target_room': 10, 'entrance': 10, 'teleporter': [10, 0], 'access': []}]}, {'name': 'Focus Tower 1F RiverCoin Room', 'id': 8, 'game_objects': [], 'links': [{'target_room': 6, 'access': ['RiverCoin']}, {'target_room': 11, 'entrance': 14, 'teleporter': [14, 0], 'access': []}]}, {'name': 'Focus Tower 1F SunCoin Room', 'id': 9, 'game_objects': [], 'links': [{'target_room': 6, 'access': ['SunCoin']}, {'target_room': 4, 'entrance': 12, 'teleporter': [12, 0], 'access': []}, {'target_room': 226, 'entrance': 9, 'teleporter': [3, 6], 'access': []}]}, {'name': 'Focus Tower 1F SkyCoin Room', 'id': 201, 'game_objects': [], 'links': [{'target_room': 195, 'entrance': 13, 'teleporter': [13, 0], 'access': ['SkyCoin', 'FlamerusRex', 'IceGolem', 'DualheadHydra', 'Pazuzu']}, {'target_room': 5, 'entrance': 15, 'teleporter': [15, 0], 'access': []}]}, {'name': 'Focus Tower 2F - Sand Coin Passage', 'id': 10, 'game_objects': [{'name': 'Focus Tower 2F - Sand Door Chest', 'object_id': 3, 'type': 'Chest', 'access': []}], 'links': [{'target_room': 221, 'entrance': 16, 'teleporter': [4, 6], 'access': []}, {'target_room': 7, 'entrance': 17, 'teleporter': [17, 0], 'access': []}]}, {'name': 'Focus Tower 2F - River Coin Passage', 'id': 11, 'game_objects': [], 'links': [{'target_room': 8, 'entrance': 18, 'teleporter': [18, 0], 'access': []}, {'target_room': 13, 'entrance': 19, 'teleporter': [20, 0], 'access': []}]}, {'name': 'Focus Tower 2F - Venus Chest Room', 'id': 12, 'game_objects': [{'name': 'Focus Tower 2F - Back Door Chest', 'object_id': 2, 'type': 'Chest', 'access': []}, {'name': 'Focus Tower 2F - Venus Chest', 'object_id': 9, 'type': 'NPC', 'access': ['Bomb', 'VenusKey']}], 'links': [{'target_room': 14, 'entrance': 20, 'teleporter': [19, 0], 'access': []}]}, {'name': 'Focus Tower 3F - Lower Floor', 'id': 13, 'game_objects': [{'name': 'Focus Tower 3F - River Door Box', 'object_id': 34, 'type': 'Box', 'access': []}], 'links': [{'target_room': 224, 'entrance': 22, 'teleporter': [6, 6], 'access': []}, {'target_room': 11, 'entrance': 23, 'teleporter': [24, 0], 'access': []}]}, {'name': 'Focus Tower 3F - Upper Floor', 'id': 14, 'game_objects': [], 'links': [{'target_room': 223, 'entrance': 24, 'teleporter': [5, 6], 'access': []}, {'target_room': 12, 'entrance': 25, 'teleporter': [23, 0], 'access': []}]}, {'name': 'Level Forest', 'id': 15, 'game_objects': [{'name': 'Level Forest - Northwest Box', 'object_id': 40, 'type': 'Box', 'access': ['Axe']}, {'name': 'Level Forest - Northeast Box', 'object_id': 41, 'type': 'Box', 'access': ['Axe']}, {'name': 'Level Forest - Middle Box', 'object_id': 42, 'type': 'Box', 'access': []}, {'name': 'Level Forest - Southwest Box', 'object_id': 43, 'type': 'Box', 'access': ['Axe']}, {'name': 'Level Forest - Southeast Box', 'object_id': 44, 'type': 'Box', 'access': ['Axe']}, {'name': 'Minotaur', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Minotaur'], 'access': ['Kaeli1']}, {'name': 'Level Forest - Old Man', 'object_id': 0, 'type': 'NPC', 'access': []}, {'name': 'Level Forest - Kaeli', 'object_id': 1, 'type': 'NPC', 'access': ['Kaeli1', 'Minotaur']}], 'links': [{'target_room': 220, 'entrance': 28, 'teleporter': [25, 0], 'access': []}]}, {'name': 'Foresta', 'id': 16, 'game_objects': [{'name': 'Foresta - Outside Box', 'object_id': 45, 'type': 'Box', 'access': ['Axe']}], 'links': [{'target_room': 220, 'entrance': 38, 'teleporter': [31, 0], 'access': []}, {'target_room': 17, 'entrance': 44, 'teleporter': [0, 5], 'access': []}, {'target_room': 18, 'entrance': 42, 'teleporter': [32, 4], 'access': []}, {'target_room': 19, 'entrance': 43, 'teleporter': [33, 0], 'access': []}, {'target_room': 20, 'entrance': 45, 'teleporter': [1, 5], 'access': []}]}, {'name': "Kaeli's House", 'id': 17, 'game_objects': [{'name': "Foresta - Kaeli's House Box", 'object_id': 46, 'type': 'Box', 'access': []}, {'name': 'Kaeli Companion', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Kaeli1'], 'access': ['TreeWither']}, {'name': 'Kaeli 2', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Kaeli2'], 'access': ['Kaeli1', 'Minotaur', 'Elixir']}], 'links': [{'target_room': 16, 'entrance': 46, 'teleporter': [86, 3], 'access': []}]}, {'name': "Foresta Houses - Old Man's House Main", 'id': 18, 'game_objects': [], 'links': [{'target_room': 19, 'access': ['BarrelPushed']}, {'target_room': 16, 'entrance': 47, 'teleporter': [34, 0], 'access': []}]}, {'name': "Foresta Houses - Old Man's House Back", 'id': 19, 'game_objects': [{'name': 'Foresta - Old Man House Chest', 'object_id': 5, 'type': 'Chest', 'access': []}, {'name': 'Old Man Barrel', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['BarrelPushed'], 'access': []}], 'links': [{'target_room': 18, 'access': ['BarrelPushed']}, {'target_room': 16, 'entrance': 48, 'teleporter': [35, 0], 'access': []}]}, {'name': 'Foresta Houses - Rest House', 'id': 20, 'game_objects': [{'name': 'Foresta - Rest House Box', 'object_id': 47, 'type': 'Box', 'access': []}], 'links': [{'target_room': 16, 'entrance': 50, 'teleporter': [87, 3], 'access': []}]}, {'name': 'Libra Treehouse', 'id': 21, 'game_objects': [{'name': 'Alive Forest - Libra Treehouse Box', 'object_id': 50, 'type': 'Box', 'access': []}], 'links': [{'target_room': 124, 'entrance': 51, 'teleporter': [67, 8], 'access': ['LibraCrest']}]}, {'name': 'Gemini Treehouse', 'id': 22, 'game_objects': [{'name': 'Alive Forest - Gemini Treehouse Box', 'object_id': 51, 'type': 'Box', 'access': []}], 'links': [{'target_room': 124, 'entrance': 52, 'teleporter': [68, 8], 'access': ['GeminiCrest']}]}, {'name': 'Mobius Treehouse', 'id': 23, 'game_objects': [{'name': 'Alive Forest - Mobius Treehouse West Box', 'object_id': 48, 'type': 'Box', 'access': []}, {'name': 'Alive Forest - Mobius Treehouse East Box', 'object_id': 49, 'type': 'Box', 'access': []}], 'links': [{'target_room': 124, 'entrance': 53, 'teleporter': [69, 8], 'access': ['MobiusCrest']}]}, {'name': 'Sand Temple', 'id': 24, 'game_objects': [{'name': 'Tristam Companion', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Tristam'], 'access': []}], 'links': [{'target_room': 220, 'entrance': 54, 'teleporter': [36, 0], 'access': []}]}, {'name': 'Bone Dungeon 1F', 'id': 25, 'game_objects': [{'name': 'Bone Dungeon 1F - Entrance Room West Box', 'object_id': 53, 'type': 'Box', 'access': []}, {'name': 'Bone Dungeon 1F - Entrance Room Middle Box', 'object_id': 54, 'type': 'Box', 'access': []}, {'name': 'Bone Dungeon 1F - Entrance Room East Box', 'object_id': 55, 'type': 'Box', 'access': []}], 'links': [{'target_room': 220, 'entrance': 55, 'teleporter': [37, 0], 'access': []}, {'target_room': 26, 'entrance': 56, 'teleporter': [2, 2], 'access': []}]}, {'name': 'Bone Dungeon B1 - Waterway', 'id': 26, 'game_objects': [{'name': 'Bone Dungeon B1 - Skull Chest', 'object_id': 6, 'type': 'Chest', 'access': ['Bomb']}, {'name': 'Bone Dungeon B1 - Tristam', 'object_id': 2, 'type': 'NPC', 'access': ['Tristam']}, {'name': 'Tristam Bone Dungeon Item Given', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['TristamBoneItemGiven'], 'access': ['Tristam']}], 'links': [{'target_room': 25, 'entrance': 59, 'teleporter': [88, 3], 'access': []}, {'target_room': 28, 'entrance': 57, 'teleporter': [3, 2], 'access': ['Bomb']}]}, {'name': 'Bone Dungeon B1 - Checker Room', 'id': 28, 'game_objects': [{'name': 'Bone Dungeon B1 - Checker Room Box', 'object_id': 56, 'type': 'Box', 'access': ['Bomb']}], 'links': [{'target_room': 26, 'entrance': 61, 'teleporter': [89, 3], 'access': []}, {'target_room': 30, 'entrance': 60, 'teleporter': [4, 2], 'access': []}]}, {'name': 'Bone Dungeon B1 - Hidden Room', 'id': 29, 'game_objects': [{'name': 'Bone Dungeon B1 - Ribcage Waterway Box', 'object_id': 57, 'type': 'Box', 'access': []}], 'links': [{'target_room': 31, 'entrance': 62, 'teleporter': [91, 3], 'access': []}]}, {'name': 'Bone Dungeon B2 - Exploding Skull Room - First Room', 'id': 30, 'game_objects': [{'name': 'Bone Dungeon B2 - Spines Room Alcove Box', 'object_id': 59, 'type': 'Box', 'access': []}, {'name': 'Long Spine', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['LongSpineBombed'], 'access': ['Bomb']}], 'links': [{'target_room': 28, 'entrance': 65, 'teleporter': [90, 3], 'access': []}, {'target_room': 31, 'access': ['LongSpineBombed']}]}, {'name': 'Bone Dungeon B2 - Exploding Skull Room - Second Room', 'id': 31, 'game_objects': [{'name': 'Bone Dungeon B2 - Spines Room Looped Hallway Box', 'object_id': 58, 'type': 'Box', 'access': []}, {'name': 'Short Spine', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['ShortSpineBombed'], 'access': ['Bomb']}], 'links': [{'target_room': 29, 'entrance': 63, 'teleporter': [5, 2], 'access': ['LongSpineBombed']}, {'target_room': 32, 'access': ['ShortSpineBombed']}, {'target_room': 30, 'access': ['LongSpineBombed']}]}, {'name': 'Bone Dungeon B2 - Exploding Skull Room - Third Room', 'id': 32, 'game_objects': [], 'links': [{'target_room': 35, 'entrance': 64, 'teleporter': [6, 2], 'access': []}, {'target_room': 31, 'access': ['ShortSpineBombed']}]}, {'name': 'Bone Dungeon B2 - Box Room', 'id': 33, 'game_objects': [{'name': 'Bone Dungeon B2 - Lone Room Box', 'object_id': 61, 'type': 'Box', 'access': []}], 'links': [{'target_room': 36, 'entrance': 66, 'teleporter': [93, 3], 'access': []}]}, {'name': 'Bone Dungeon B2 - Quake Room', 'id': 34, 'game_objects': [{'name': 'Bone Dungeon B2 - Penultimate Room Chest', 'object_id': 7, 'type': 'Chest', 'access': []}], 'links': [{'target_room': 37, 'entrance': 67, 'teleporter': [94, 3], 'access': []}]}, {'name': 'Bone Dungeon B2 - Two Skulls Room - First Room', 'id': 35, 'game_objects': [{'name': 'Bone Dungeon B2 - Two Skulls Room Box', 'object_id': 60, 'type': 'Box', 'access': []}, {'name': 'Skull 1', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Skull1Bombed'], 'access': ['Bomb']}], 'links': [{'target_room': 32, 'entrance': 71, 'teleporter': [92, 3], 'access': []}, {'target_room': 36, 'access': ['Skull1Bombed']}]}, {'name': 'Bone Dungeon B2 - Two Skulls Room - Second Room', 'id': 36, 'game_objects': [{'name': 'Skull 2', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Skull2Bombed'], 'access': ['Bomb']}], 'links': [{'target_room': 33, 'entrance': 68, 'teleporter': [7, 2], 'access': []}, {'target_room': 37, 'access': ['Skull2Bombed']}, {'target_room': 35, 'access': ['Skull1Bombed']}]}, {'name': 'Bone Dungeon B2 - Two Skulls Room - Third Room', 'id': 37, 'game_objects': [], 'links': [{'target_room': 34, 'entrance': 69, 'teleporter': [8, 2], 'access': []}, {'target_room': 38, 'entrance': 70, 'teleporter': [9, 2], 'access': ['Bomb']}, {'target_room': 36, 'access': ['Skull2Bombed']}]}, {'name': 'Bone Dungeon B2 - Boss Room', 'id': 38, 'game_objects': [{'name': 'Bone Dungeon B2 - North Box', 'object_id': 62, 'type': 'Box', 'access': []}, {'name': 'Bone Dungeon B2 - South Box', 'object_id': 63, 'type': 'Box', 'access': []}, {'name': 'Bone Dungeon B2 - Flamerus Rex Chest', 'object_id': 8, 'type': 'Chest', 'access': []}, {'name': "Bone Dungeon B2 - Tristam's Treasure Chest", 'object_id': 4, 'type': 'Chest', 'access': []}, {'name': 'Flamerus Rex', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['FlamerusRex'], 'access': []}], 'links': [{'target_room': 37, 'entrance': 74, 'teleporter': [95, 3], 'access': []}]}, {'name': 'Libra Temple', 'id': 39, 'game_objects': [{'name': 'Libra Temple - Box', 'object_id': 64, 'type': 'Box', 'access': []}, {'name': 'Phoebe Companion', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Phoebe1'], 'access': []}], 'links': [{'target_room': 221, 'entrance': 75, 'teleporter': [13, 6], 'access': []}, {'target_room': 51, 'entrance': 76, 'teleporter': [59, 8], 'access': ['LibraCrest']}]}, {'name': 'Aquaria', 'id': 40, 'game_objects': [{'name': 'Summer Aquaria', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['SummerAquaria'], 'access': ['WakeWater']}], 'links': [{'target_room': 221, 'entrance': 77, 'teleporter': [8, 6], 'access': []}, {'target_room': 41, 'entrance': 81, 'teleporter': [10, 5], 'access': []}, {'target_room': 42, 'entrance': 82, 'teleporter': [44, 4], 'access': []}, {'target_room': 44, 'entrance': 83, 'teleporter': [11, 5], 'access': []}, {'target_room': 71, 'entrance': 89, 'teleporter': [42, 0], 'access': ['SummerAquaria']}, {'target_room': 71, 'entrance': 90, 'teleporter': [43, 0], 'access': ['SummerAquaria']}]}, {'name': "Phoebe's House", 'id': 41, 'game_objects': [{'name': "Aquaria - Phoebe's House Chest", 'object_id': 65, 'type': 'Box', 'access': []}], 'links': [{'target_room': 40, 'entrance': 93, 'teleporter': [5, 8], 'access': []}]}, {'name': 'Aquaria Vendor House', 'id': 42, 'game_objects': [{'name': 'Aquaria - Vendor', 'object_id': 4, 'type': 'NPC', 'access': []}, {'name': 'Aquaria - Vendor House Box', 'object_id': 66, 'type': 'Box', 'access': []}], 'links': [{'target_room': 40, 'entrance': 94, 'teleporter': [40, 8], 'access': []}, {'target_room': 43, 'entrance': 95, 'teleporter': [47, 0], 'access': []}]}, {'name': 'Aquaria Gemini Room', 'id': 43, 'game_objects': [], 'links': [{'target_room': 42, 'entrance': 97, 'teleporter': [48, 0], 'access': []}, {'target_room': 81, 'entrance': 96, 'teleporter': [72, 8], 'access': ['GeminiCrest']}]}, {'name': 'Aquaria INN', 'id': 44, 'game_objects': [], 'links': [{'target_room': 40, 'entrance': 98, 'teleporter': [75, 8], 'access': []}]}, {'name': 'Wintry Cave 1F - East Ledge', 'id': 45, 'game_objects': [{'name': 'Wintry Cave 1F - North Box', 'object_id': 67, 'type': 'Box', 'access': []}, {'name': 'Wintry Cave 1F - Entrance Box', 'object_id': 70, 'type': 'Box', 'access': []}, {'name': 'Wintry Cave 1F - Slippery Cliff Box', 'object_id': 68, 'type': 'Box', 'access': ['Claw']}, {'name': 'Wintry Cave 1F - Phoebe', 'object_id': 5, 'type': 'NPC', 'access': ['Phoebe1']}], 'links': [{'target_room': 221, 'entrance': 99, 'teleporter': [49, 0], 'access': []}, {'target_room': 49, 'entrance': 100, 'teleporter': [14, 2], 'access': ['Bomb']}, {'target_room': 46, 'access': ['Claw']}]}, {'name': 'Wintry Cave 1F - Central Space', 'id': 46, 'game_objects': [{'name': 'Wintry Cave 1F - Scenic Overlook Box', 'object_id': 69, 'type': 'Box', 'access': ['Claw']}], 'links': [{'target_room': 45, 'access': ['Claw']}, {'target_room': 47, 'access': ['Claw']}]}, {'name': 'Wintry Cave 1F - West Ledge', 'id': 47, 'game_objects': [], 'links': [{'target_room': 48, 'entrance': 101, 'teleporter': [15, 2], 'access': ['Bomb']}, {'target_room': 46, 'access': ['Claw']}]}, {'name': 'Wintry Cave 2F', 'id': 48, 'game_objects': [{'name': 'Wintry Cave 2F - West Left Box', 'object_id': 71, 'type': 'Box', 'access': []}, {'name': 'Wintry Cave 2F - West Right Box', 'object_id': 72, 'type': 'Box', 'access': []}, {'name': 'Wintry Cave 2F - East Left Box', 'object_id': 73, 'type': 'Box', 'access': []}, {'name': 'Wintry Cave 2F - East Right Box', 'object_id': 74, 'type': 'Box', 'access': []}], 'links': [{'target_room': 47, 'entrance': 104, 'teleporter': [97, 3], 'access': []}, {'target_room': 50, 'entrance': 103, 'teleporter': [50, 0], 'access': []}]}, {'name': 'Wintry Cave 3F Top', 'id': 49, 'game_objects': [{'name': 'Wintry Cave 3F - West Box', 'object_id': 75, 'type': 'Box', 'access': []}, {'name': 'Wintry Cave 3F - East Box', 'object_id': 76, 'type': 'Box', 'access': []}], 'links': [{'target_room': 45, 'entrance': 105, 'teleporter': [96, 3], 'access': []}]}, {'name': 'Wintry Cave 3F Bottom', 'id': 50, 'game_objects': [{'name': 'Wintry Cave 3F - Squidite Chest', 'object_id': 9, 'type': 'Chest', 'access': ['Phanquid']}, {'name': 'Phanquid', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Phanquid'], 'access': []}, {'name': 'Wintry Cave 3F - Before Boss Box', 'object_id': 77, 'type': 'Box', 'access': []}], 'links': [{'target_room': 48, 'entrance': 106, 'teleporter': [51, 0], 'access': []}]}, {'name': 'Life Temple', 'id': 51, 'game_objects': [{'name': 'Life Temple - Box', 'object_id': 78, 'type': 'Box', 'access': []}, {'name': 'Life Temple - Mysterious Man', 'object_id': 6, 'type': 'NPC', 'access': []}], 'links': [{'target_room': 222, 'entrance': 107, 'teleporter': [14, 6], 'access': []}, {'target_room': 39, 'entrance': 108, 'teleporter': [60, 8], 'access': ['LibraCrest']}]}, {'name': 'Fall Basin', 'id': 52, 'game_objects': [{'name': 'Falls Basin - Snow Crab Chest', 'object_id': 10, 'type': 'Chest', 'access': ['FreezerCrab']}, {'name': 'Freezer Crab', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['FreezerCrab'], 'access': []}, {'name': 'Falls Basin - Box', 'object_id': 79, 'type': 'Box', 'access': []}], 'links': [{'target_room': 221, 'entrance': 111, 'teleporter': [53, 0], 'access': []}]}, {'name': 'Ice Pyramid B1 Taunt Room', 'id': 53, 'game_objects': [{'name': 'Ice Pyramid B1 - Chest', 'object_id': 11, 'type': 'Chest', 'access': []}, {'name': 'Ice Pyramid B1 - West Box', 'object_id': 80, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid B1 - North Box', 'object_id': 81, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid B1 - East Box', 'object_id': 82, 'type': 'Box', 'access': []}], 'links': [{'target_room': 68, 'entrance': 113, 'teleporter': [55, 0], 'access': []}]}, {'name': 'Ice Pyramid 1F Maze Lobby', 'id': 54, 'game_objects': [{'name': 'Ice Pyramid 1F Statue', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['IcePyramid1FStatue'], 'access': ['Sword']}], 'links': [{'target_room': 221, 'entrance': 114, 'teleporter': [56, 0], 'access': []}, {'target_room': 55, 'access': ['IcePyramid1FStatue']}]}, {'name': 'Ice Pyramid 1F Maze', 'id': 55, 'game_objects': [{'name': 'Ice Pyramid 1F - East Alcove Chest', 'object_id': 13, 'type': 'Chest', 'access': []}, {'name': 'Ice Pyramid 1F - Sandwiched Alcove Box', 'object_id': 83, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 1F - Southwest Left Box', 'object_id': 84, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 1F - Southwest Right Box', 'object_id': 85, 'type': 'Box', 'access': []}], 'links': [{'target_room': 56, 'entrance': 116, 'teleporter': [57, 0], 'access': []}, {'target_room': 57, 'entrance': 117, 'teleporter': [58, 0], 'access': []}, {'target_room': 58, 'entrance': 118, 'teleporter': [59, 0], 'access': []}, {'target_room': 59, 'entrance': 119, 'teleporter': [60, 0], 'access': []}, {'target_room': 60, 'entrance': 120, 'teleporter': [61, 0], 'access': []}, {'target_room': 54, 'access': ['IcePyramid1FStatue']}]}, {'name': 'Ice Pyramid 2F South Tiled Room', 'id': 56, 'game_objects': [{'name': 'Ice Pyramid 2F - South Side Glass Door Box', 'object_id': 87, 'type': 'Box', 'access': ['Sword']}, {'name': 'Ice Pyramid 2F - South Side East Box', 'object_id': 91, 'type': 'Box', 'access': []}], 'links': [{'target_room': 55, 'entrance': 122, 'teleporter': [62, 0], 'access': []}, {'target_room': 61, 'entrance': 123, 'teleporter': [67, 0], 'access': []}]}, {'name': 'Ice Pyramid 2F West Room', 'id': 57, 'game_objects': [{'name': 'Ice Pyramid 2F - Northwest Room Box', 'object_id': 90, 'type': 'Box', 'access': []}], 'links': [{'target_room': 55, 'entrance': 124, 'teleporter': [63, 0], 'access': []}]}, {'name': 'Ice Pyramid 2F Center Room', 'id': 58, 'game_objects': [{'name': 'Ice Pyramid 2F - Center Room Box', 'object_id': 86, 'type': 'Box', 'access': []}], 'links': [{'target_room': 55, 'entrance': 125, 'teleporter': [64, 0], 'access': []}]}, {'name': 'Ice Pyramid 2F Small North Room', 'id': 59, 'game_objects': [{'name': 'Ice Pyramid 2F - North Room Glass Door Box', 'object_id': 88, 'type': 'Box', 'access': ['Sword']}], 'links': [{'target_room': 55, 'entrance': 126, 'teleporter': [65, 0], 'access': []}]}, {'name': 'Ice Pyramid 2F North Corridor', 'id': 60, 'game_objects': [{'name': 'Ice Pyramid 2F - North Corridor Glass Door Box', 'object_id': 89, 'type': 'Box', 'access': ['Sword']}], 'links': [{'target_room': 55, 'entrance': 127, 'teleporter': [66, 0], 'access': []}, {'target_room': 62, 'entrance': 128, 'teleporter': [68, 0], 'access': []}]}, {'name': 'Ice Pyramid 3F Two Boxes Room', 'id': 61, 'game_objects': [{'name': 'Ice Pyramid 3F - Staircase Dead End Left Box', 'object_id': 94, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 3F - Staircase Dead End Right Box', 'object_id': 95, 'type': 'Box', 'access': []}], 'links': [{'target_room': 56, 'entrance': 129, 'teleporter': [69, 0], 'access': []}]}, {'name': 'Ice Pyramid 3F Main Loop', 'id': 62, 'game_objects': [{'name': 'Ice Pyramid 3F - Inner Room North Box', 'object_id': 92, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 3F - Inner Room South Box', 'object_id': 93, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 3F - East Alcove Box', 'object_id': 96, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 3F - Leapfrog Box', 'object_id': 97, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 3F Statue', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['IcePyramid3FStatue'], 'access': ['Sword']}], 'links': [{'target_room': 60, 'entrance': 130, 'teleporter': [70, 0], 'access': []}, {'target_room': 63, 'access': ['IcePyramid3FStatue']}]}, {'name': 'Ice Pyramid 3F Blocked Room', 'id': 63, 'game_objects': [], 'links': [{'target_room': 64, 'entrance': 131, 'teleporter': [71, 0], 'access': []}, {'target_room': 62, 'access': ['IcePyramid3FStatue']}]}, {'name': 'Ice Pyramid 4F Main Loop', 'id': 64, 'game_objects': [], 'links': [{'target_room': 66, 'entrance': 133, 'teleporter': [73, 0], 'access': []}, {'target_room': 63, 'entrance': 132, 'teleporter': [72, 0], 'access': []}, {'target_room': 65, 'access': ['IcePyramid4FStatue']}]}, {'name': 'Ice Pyramid 4F Treasure Room', 'id': 65, 'game_objects': [{'name': 'Ice Pyramid 4F - Chest', 'object_id': 12, 'type': 'Chest', 'access': []}, {'name': 'Ice Pyramid 4F - Northwest Box', 'object_id': 98, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 4F - West Left Box', 'object_id': 99, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 4F - West Right Box', 'object_id': 100, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 4F - South Left Box', 'object_id': 101, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 4F - South Right Box', 'object_id': 102, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 4F - East Left Box', 'object_id': 103, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 4F - East Right Box', 'object_id': 104, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 4F Statue', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['IcePyramid4FStatue'], 'access': ['Sword']}], 'links': [{'target_room': 64, 'access': ['IcePyramid4FStatue']}]}, {'name': 'Ice Pyramid 5F Leap of Faith Room', 'id': 66, 'game_objects': [{'name': 'Ice Pyramid 5F - Glass Door Left Box', 'object_id': 105, 'type': 'Box', 'access': ['IcePyramid5FStatue']}, {'name': 'Ice Pyramid 5F - West Ledge Box', 'object_id': 106, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 5F - South Shelf Box', 'object_id': 107, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 5F - South Leapfrog Box', 'object_id': 108, 'type': 'Box', 'access': []}, {'name': 'Ice Pyramid 5F - Glass Door Right Box', 'object_id': 109, 'type': 'Box', 'access': ['IcePyramid5FStatue']}, {'name': 'Ice Pyramid 5F - North Box', 'object_id': 110, 'type': 'Box', 'access': []}], 'links': [{'target_room': 64, 'entrance': 134, 'teleporter': [74, 0], 'access': []}, {'target_room': 65, 'access': []}, {'target_room': 53, 'access': ['Bomb', 'Claw', 'Sword']}]}, {'name': 'Ice Pyramid 5F Stairs to Ice Golem', 'id': 67, 'game_objects': [{'name': 'Ice Pyramid 5F Statue', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['IcePyramid5FStatue'], 'access': ['Sword']}], 'links': [{'target_room': 69, 'entrance': 137, 'teleporter': [76, 0], 'access': []}, {'target_room': 65, 'access': []}, {'target_room': 70, 'entrance': 136, 'teleporter': [75, 0], 'access': []}]}, {'name': 'Ice Pyramid Climbing Wall Room Lower Space', 'id': 68, 'game_objects': [], 'links': [{'target_room': 53, 'entrance': 139, 'teleporter': [78, 0], 'access': []}, {'target_room': 69, 'access': ['Claw']}]}, {'name': 'Ice Pyramid Climbing Wall Room Upper Space', 'id': 69, 'game_objects': [], 'links': [{'target_room': 67, 'entrance': 140, 'teleporter': [79, 0], 'access': []}, {'target_room': 68, 'access': ['Claw']}]}, {'name': 'Ice Pyramid Ice Golem Room', 'id': 70, 'game_objects': [{'name': 'Ice Pyramid 6F - Ice Golem Chest', 'object_id': 14, 'type': 'Chest', 'access': ['IceGolem']}, {'name': 'Ice Golem', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['IceGolem'], 'access': []}], 'links': [{'target_room': 67, 'entrance': 141, 'teleporter': [80, 0], 'access': []}, {'target_room': 66, 'access': []}]}, {'name': 'Spencer Waterfall', 'id': 71, 'game_objects': [], 'links': [{'target_room': 72, 'entrance': 143, 'teleporter': [81, 0], 'access': []}, {'target_room': 40, 'entrance': 145, 'teleporter': [82, 0], 'access': []}, {'target_room': 40, 'entrance': 148, 'teleporter': [83, 0], 'access': []}]}, {'name': 'Spencer Cave Normal Main', 'id': 72, 'game_objects': [{'name': "Spencer's Cave - Box", 'object_id': 111, 'type': 'Box', 'access': ['Claw']}, {'name': "Spencer's Cave - Spencer", 'object_id': 8, 'type': 'NPC', 'access': []}, {'name': "Spencer's Cave - Locked Chest", 'object_id': 13, 'type': 'NPC', 'access': ['VenusKey']}], 'links': [{'target_room': 71, 'entrance': 150, 'teleporter': [85, 0], 'access': []}]}, {'name': 'Spencer Cave Normal South Ledge', 'id': 73, 'game_objects': [{'name': "Collapse Spencer's Cave", 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['ShipLiberated'], 'access': ['MegaGrenade']}], 'links': [{'target_room': 227, 'entrance': 151, 'teleporter': [7, 6], 'access': []}, {'target_room': 203, 'access': ['MegaGrenade']}]}, {'name': 'Spencer Cave Caved In Main Loop', 'id': 203, 'game_objects': [], 'links': [{'target_room': 73, 'access': []}, {'target_room': 207, 'entrance': 156, 'teleporter': [36, 8], 'access': ['MobiusCrest']}, {'target_room': 204, 'access': ['Claw']}, {'target_room': 205, 'access': ['Bomb']}]}, {'name': 'Spencer Cave Caved In Waters', 'id': 204, 'game_objects': [{'name': 'Bomb Libra Block', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['SpencerCaveLibraBlockBombed'], 'access': ['MegaGrenade', 'Claw']}], 'links': [{'target_room': 203, 'access': ['Claw']}]}, {'name': 'Spencer Cave Caved In Libra Nook', 'id': 205, 'game_objects': [], 'links': [{'target_room': 206, 'entrance': 153, 'teleporter': [33, 8], 'access': ['LibraCrest']}]}, {'name': 'Spencer Cave Caved In Libra Corridor', 'id': 206, 'game_objects': [], 'links': [{'target_room': 205, 'entrance': 154, 'teleporter': [34, 8], 'access': ['LibraCrest']}, {'target_room': 207, 'access': ['SpencerCaveLibraBlockBombed']}]}, {'name': 'Spencer Cave Caved In Mobius Chest', 'id': 207, 'game_objects': [{'name': "Spencer's Cave - Mobius Chest", 'object_id': 15, 'type': 'Chest', 'access': []}], 'links': [{'target_room': 203, 'entrance': 155, 'teleporter': [35, 8], 'access': ['MobiusCrest']}, {'target_room': 206, 'access': ['Bomb']}]}, {'name': 'Wintry Temple Outer Room', 'id': 74, 'game_objects': [], 'links': [{'target_room': 223, 'entrance': 157, 'teleporter': [15, 6], 'access': []}]}, {'name': 'Wintry Temple Inner Room', 'id': 75, 'game_objects': [{'name': 'Wintry Temple - West Box', 'object_id': 112, 'type': 'Box', 'access': []}, {'name': 'Wintry Temple - North Box', 'object_id': 113, 'type': 'Box', 'access': []}], 'links': [{'target_room': 92, 'entrance': 158, 'teleporter': [62, 8], 'access': ['GeminiCrest']}]}, {'name': 'Fireburg Upper Plaza', 'id': 76, 'game_objects': [], 'links': [{'target_room': 224, 'entrance': 159, 'teleporter': [9, 6], 'access': []}, {'target_room': 80, 'entrance': 163, 'teleporter': [91, 0], 'access': []}, {'target_room': 77, 'entrance': 164, 'teleporter': [98, 8], 'access': []}, {'target_room': 82, 'entrance': 165, 'teleporter': [96, 8], 'access': []}, {'target_room': 208, 'access': ['Claw']}]}, {'name': 'Fireburg Lower Plaza', 'id': 208, 'game_objects': [{'name': 'Fireburg - Hidden Tunnel Box', 'object_id': 116, 'type': 'Box', 'access': []}], 'links': [{'target_room': 76, 'access': ['Claw']}, {'target_room': 78, 'entrance': 166, 'teleporter': [11, 8], 'access': ['MultiKey']}]}, {'name': "Reuben's House", 'id': 77, 'game_objects': [{'name': "Fireburg - Reuben's House Arion", 'object_id': 14, 'type': 'NPC', 'access': ['ReubenDadSaved']}, {'name': 'Reuben Companion', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Reuben1'], 'access': []}, {'name': "Fireburg - Reuben's House Box", 'object_id': 117, 'type': 'Box', 'access': []}], 'links': [{'target_room': 76, 'entrance': 167, 'teleporter': [98, 3], 'access': []}]}, {'name': "GrenadeMan's House", 'id': 78, 'game_objects': [{'name': 'Fireburg - Locked House Man', 'object_id': 12, 'type': 'NPC', 'access': []}], 'links': [{'target_room': 208, 'entrance': 168, 'teleporter': [9, 8], 'access': ['MultiKey']}, {'target_room': 79, 'entrance': 169, 'teleporter': [93, 0], 'access': []}]}, {'name': "GrenadeMan's Mobius Room", 'id': 79, 'game_objects': [], 'links': [{'target_room': 78, 'entrance': 170, 'teleporter': [94, 0], 'access': []}, {'target_room': 161, 'entrance': 171, 'teleporter': [54, 8], 'access': ['MobiusCrest']}]}, {'name': 'Fireburg Vendor House', 'id': 80, 'game_objects': [{'name': 'Fireburg - Vendor', 'object_id': 11, 'type': 'NPC', 'access': []}], 'links': [{'target_room': 76, 'entrance': 172, 'teleporter': [95, 0], 'access': []}, {'target_room': 81, 'entrance': 173, 'teleporter': [96, 0], 'access': []}]}, {'name': 'Fireburg Gemini Room', 'id': 81, 'game_objects': [], 'links': [{'target_room': 80, 'entrance': 174, 'teleporter': [97, 0], 'access': []}, {'target_room': 43, 'entrance': 175, 'teleporter': [45, 8], 'access': ['GeminiCrest']}]}, {'name': 'Fireburg Hotel Lobby', 'id': 82, 'game_objects': [{'name': 'Fireburg - Tristam', 'object_id': 10, 'type': 'NPC', 'access': ['Tristam', 'TristamBoneItemGiven']}], 'links': [{'target_room': 76, 'entrance': 177, 'teleporter': [99, 3], 'access': []}, {'target_room': 83, 'entrance': 176, 'teleporter': [213, 0], 'access': []}]}, {'name': 'Fireburg Hotel Beds', 'id': 83, 'game_objects': [], 'links': [{'target_room': 82, 'entrance': 178, 'teleporter': [214, 0], 'access': []}]}, {'name': 'Mine Exterior North West Platforms', 'id': 84, 'game_objects': [], 'links': [{'target_room': 224, 'entrance': 179, 'teleporter': [98, 0], 'access': []}, {'target_room': 88, 'entrance': 181, 'teleporter': [20, 2], 'access': ['Bomb']}, {'target_room': 85, 'access': ['Claw']}, {'target_room': 86, 'access': ['Claw']}, {'target_room': 87, 'access': ['Claw']}]}, {'name': 'Mine Exterior Central Ledge', 'id': 85, 'game_objects': [], 'links': [{'target_room': 90, 'entrance': 183, 'teleporter': [22, 2], 'access': ['Bomb']}, {'target_room': 84, 'access': ['Claw']}]}, {'name': 'Mine Exterior North Ledge', 'id': 86, 'game_objects': [], 'links': [{'target_room': 89, 'entrance': 182, 'teleporter': [21, 2], 'access': ['Bomb']}, {'target_room': 85, 'access': ['Claw']}]}, {'name': 'Mine Exterior South East Platforms', 'id': 87, 'game_objects': [{'name': 'Jinn', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Jinn'], 'access': []}], 'links': [{'target_room': 91, 'entrance': 180, 'teleporter': [99, 0], 'access': ['Jinn']}, {'target_room': 86, 'access': []}, {'target_room': 85, 'access': ['Claw']}]}, {'name': 'Mine Parallel Room', 'id': 88, 'game_objects': [{'name': 'Mine - Parallel Room West Box', 'object_id': 119, 'type': 'Box', 'access': ['Claw']}, {'name': 'Mine - Parallel Room East Box', 'object_id': 120, 'type': 'Box', 'access': ['Claw']}], 'links': [{'target_room': 84, 'entrance': 185, 'teleporter': [100, 3], 'access': []}]}, {'name': 'Mine Crescent Room', 'id': 89, 'game_objects': [{'name': 'Mine - Crescent Room Chest', 'object_id': 16, 'type': 'Chest', 'access': []}], 'links': [{'target_room': 86, 'entrance': 186, 'teleporter': [101, 3], 'access': []}]}, {'name': 'Mine Climbing Room', 'id': 90, 'game_objects': [{'name': 'Mine - Glitchy Collision Cave Box', 'object_id': 118, 'type': 'Box', 'access': ['Claw']}], 'links': [{'target_room': 85, 'entrance': 187, 'teleporter': [102, 3], 'access': []}]}, {'name': 'Mine Cliff', 'id': 91, 'game_objects': [{'name': 'Mine - Cliff Southwest Box', 'object_id': 121, 'type': 'Box', 'access': []}, {'name': 'Mine - Cliff Northwest Box', 'object_id': 122, 'type': 'Box', 'access': []}, {'name': 'Mine - Cliff Northeast Box', 'object_id': 123, 'type': 'Box', 'access': []}, {'name': 'Mine - Cliff Southeast Box', 'object_id': 124, 'type': 'Box', 'access': []}, {'name': 'Mine - Reuben', 'object_id': 7, 'type': 'NPC', 'access': ['Reuben1']}, {'name': "Reuben's dad Saved", 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['ReubenDadSaved'], 'access': ['MegaGrenade']}], 'links': [{'target_room': 87, 'entrance': 188, 'teleporter': [100, 0], 'access': []}]}, {'name': 'Sealed Temple', 'id': 92, 'game_objects': [{'name': 'Sealed Temple - West Box', 'object_id': 125, 'type': 'Box', 'access': []}, {'name': 'Sealed Temple - East Box', 'object_id': 126, 'type': 'Box', 'access': []}], 'links': [{'target_room': 224, 'entrance': 190, 'teleporter': [16, 6], 'access': []}, {'target_room': 75, 'entrance': 191, 'teleporter': [63, 8], 'access': ['GeminiCrest']}]}, {'name': 'Volcano Base', 'id': 93, 'game_objects': [{'name': 'Volcano - Base Chest', 'object_id': 17, 'type': 'Chest', 'access': []}, {'name': 'Volcano - Base West Box', 'object_id': 127, 'type': 'Box', 'access': []}, {'name': 'Volcano - Base East Left Box', 'object_id': 128, 'type': 'Box', 'access': []}, {'name': 'Volcano - Base East Right Box', 'object_id': 129, 'type': 'Box', 'access': []}], 'links': [{'target_room': 224, 'entrance': 192, 'teleporter': [103, 0], 'access': []}, {'target_room': 98, 'entrance': 196, 'teleporter': [31, 8], 'access': []}, {'target_room': 96, 'entrance': 197, 'teleporter': [30, 8], 'access': []}]}, {'name': 'Volcano Top Left', 'id': 94, 'game_objects': [{'name': 'Volcano - Medusa Chest', 'object_id': 18, 'type': 'Chest', 'access': ['Medusa']}, {'name': 'Medusa', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Medusa'], 'access': []}, {'name': 'Volcano - Behind Medusa Box', 'object_id': 130, 'type': 'Box', 'access': []}], 'links': [{'target_room': 209, 'entrance': 199, 'teleporter': [26, 8], 'access': []}]}, {'name': 'Volcano Top Right', 'id': 95, 'game_objects': [{'name': 'Volcano - Top of the Volcano Left Box', 'object_id': 131, 'type': 'Box', 'access': []}, {'name': 'Volcano - Top of the Volcano Right Box', 'object_id': 132, 'type': 'Box', 'access': []}], 'links': [{'target_room': 99, 'entrance': 200, 'teleporter': [79, 8], 'access': []}]}, {'name': 'Volcano Right Path', 'id': 96, 'game_objects': [{'name': 'Volcano - Right Path Box', 'object_id': 135, 'type': 'Box', 'access': []}], 'links': [{'target_room': 93, 'entrance': 201, 'teleporter': [15, 8], 'access': []}]}, {'name': 'Volcano Left Path', 'id': 98, 'game_objects': [{'name': 'Volcano - Left Path Box', 'object_id': 134, 'type': 'Box', 'access': []}], 'links': [{'target_room': 93, 'entrance': 204, 'teleporter': [27, 8], 'access': []}, {'target_room': 99, 'entrance': 202, 'teleporter': [25, 2], 'access': []}, {'target_room': 209, 'entrance': 203, 'teleporter': [26, 2], 'access': []}]}, {'name': 'Volcano Cross Left-Right', 'id': 99, 'game_objects': [], 'links': [{'target_room': 95, 'entrance': 206, 'teleporter': [29, 8], 'access': []}, {'target_room': 98, 'entrance': 205, 'teleporter': [103, 3], 'access': []}]}, {'name': 'Volcano Cross Right-Left', 'id': 209, 'game_objects': [{'name': 'Volcano - Crossover Section Box', 'object_id': 133, 'type': 'Box', 'access': []}], 'links': [{'target_room': 98, 'entrance': 208, 'teleporter': [104, 3], 'access': []}, {'target_room': 94, 'entrance': 207, 'teleporter': [28, 8], 'access': []}]}, {'name': 'Lava Dome Inner Ring Main Loop', 'id': 100, 'game_objects': [{'name': 'Lava Dome - Exterior Caldera Near Switch Cliff Box', 'object_id': 136, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Exterior South Cliff Box', 'object_id': 137, 'type': 'Box', 'access': []}], 'links': [{'target_room': 224, 'entrance': 209, 'teleporter': [104, 0], 'access': []}, {'target_room': 113, 'entrance': 211, 'teleporter': [105, 0], 'access': []}, {'target_room': 114, 'entrance': 212, 'teleporter': [106, 0], 'access': []}, {'target_room': 116, 'entrance': 213, 'teleporter': [108, 0], 'access': []}, {'target_room': 118, 'entrance': 214, 'teleporter': [111, 0], 'access': []}]}, {'name': 'Lava Dome Inner Ring Center Ledge', 'id': 101, 'game_objects': [{'name': 'Lava Dome - Exterior Center Dropoff Ledge Box', 'object_id': 138, 'type': 'Box', 'access': []}], 'links': [{'target_room': 115, 'entrance': 215, 'teleporter': [107, 0], 'access': []}, {'target_room': 100, 'access': ['Claw']}]}, {'name': 'Lava Dome Inner Ring Plate Ledge', 'id': 102, 'game_objects': [{'name': 'Lava Dome Plate', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['LavaDomePlate'], 'access': []}], 'links': [{'target_room': 119, 'entrance': 216, 'teleporter': [109, 0], 'access': []}]}, {'name': 'Lava Dome Inner Ring Upper Ledge West', 'id': 103, 'game_objects': [], 'links': [{'target_room': 111, 'entrance': 219, 'teleporter': [112, 0], 'access': []}, {'target_room': 108, 'entrance': 220, 'teleporter': [113, 0], 'access': []}, {'target_room': 104, 'access': ['Claw']}, {'target_room': 100, 'access': ['Claw']}]}, {'name': 'Lava Dome Inner Ring Upper Ledge East', 'id': 104, 'game_objects': [], 'links': [{'target_room': 110, 'entrance': 218, 'teleporter': [110, 0], 'access': []}, {'target_room': 103, 'access': ['Claw']}]}, {'name': 'Lava Dome Inner Ring Big Door Ledge', 'id': 105, 'game_objects': [], 'links': [{'target_room': 107, 'entrance': 221, 'teleporter': [114, 0], 'access': []}, {'target_room': 121, 'entrance': 222, 'teleporter': [29, 2], 'access': ['LavaDomePlate']}]}, {'name': 'Lava Dome Inner Ring Tiny Bottom Ledge', 'id': 106, 'game_objects': [{'name': 'Lava Dome - Exterior Dead End Caldera Box', 'object_id': 139, 'type': 'Box', 'access': []}], 'links': [{'target_room': 120, 'entrance': 226, 'teleporter': [115, 0], 'access': []}]}, {'name': 'Lava Dome Jump Maze II', 'id': 107, 'game_objects': [{'name': 'Lava Dome - Gold Maze Northwest Box', 'object_id': 140, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Gold Maze Southwest Box', 'object_id': 246, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Gold Maze Northeast Box', 'object_id': 247, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Gold Maze North Box', 'object_id': 248, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Gold Maze Center Box', 'object_id': 249, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Gold Maze Southeast Box', 'object_id': 250, 'type': 'Box', 'access': []}], 'links': [{'target_room': 105, 'entrance': 227, 'teleporter': [116, 0], 'access': []}, {'target_room': 108, 'entrance': 228, 'teleporter': [119, 0], 'access': []}, {'target_room': 120, 'entrance': 229, 'teleporter': [120, 0], 'access': []}]}, {'name': 'Lava Dome Up-Down Corridor', 'id': 108, 'game_objects': [], 'links': [{'target_room': 107, 'entrance': 231, 'teleporter': [118, 0], 'access': []}, {'target_room': 103, 'entrance': 230, 'teleporter': [117, 0], 'access': []}]}, {'name': 'Lava Dome Jump Maze I', 'id': 109, 'game_objects': [{'name': 'Lava Dome - Bare Maze Leapfrog Alcove North Box', 'object_id': 141, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Bare Maze Leapfrog Alcove South Box', 'object_id': 142, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Bare Maze Center Box', 'object_id': 143, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Bare Maze Southwest Box', 'object_id': 144, 'type': 'Box', 'access': []}], 'links': [{'target_room': 118, 'entrance': 232, 'teleporter': [121, 0], 'access': []}, {'target_room': 111, 'entrance': 233, 'teleporter': [122, 0], 'access': []}]}, {'name': 'Lava Dome Pointless Room', 'id': 110, 'game_objects': [], 'links': [{'target_room': 104, 'entrance': 234, 'teleporter': [123, 0], 'access': []}]}, {'name': 'Lava Dome Lower Moon Helm Room', 'id': 111, 'game_objects': [{'name': 'Lava Dome - U-Bend Room North Box', 'object_id': 146, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - U-Bend Room South Box', 'object_id': 147, 'type': 'Box', 'access': []}], 'links': [{'target_room': 103, 'entrance': 235, 'teleporter': [124, 0], 'access': []}, {'target_room': 109, 'entrance': 236, 'teleporter': [125, 0], 'access': []}]}, {'name': 'Lava Dome Moon Helm Room', 'id': 112, 'game_objects': [{'name': 'Lava Dome - Beyond River Room Chest', 'object_id': 19, 'type': 'Chest', 'access': []}, {'name': 'Lava Dome - Beyond River Room Box', 'object_id': 145, 'type': 'Box', 'access': []}], 'links': [{'target_room': 117, 'entrance': 237, 'teleporter': [126, 0], 'access': []}]}, {'name': 'Lava Dome Three Jumps Room', 'id': 113, 'game_objects': [{'name': 'Lava Dome - Three Jumps Room Box', 'object_id': 150, 'type': 'Box', 'access': []}], 'links': [{'target_room': 100, 'entrance': 238, 'teleporter': [127, 0], 'access': []}]}, {'name': 'Lava Dome Life Chest Room Lower Ledge', 'id': 114, 'game_objects': [{'name': 'Lava Dome - Gold Bar Room Boulder Chest', 'object_id': 28, 'type': 'Chest', 'access': ['MegaGrenade']}], 'links': [{'target_room': 100, 'entrance': 239, 'teleporter': [128, 0], 'access': []}, {'target_room': 115, 'access': ['Claw']}]}, {'name': 'Lava Dome Life Chest Room Upper Ledge', 'id': 115, 'game_objects': [{'name': 'Lava Dome - Gold Bar Room Leapfrog Alcove Box West', 'object_id': 148, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Gold Bar Room Leapfrog Alcove Box East', 'object_id': 149, 'type': 'Box', 'access': []}], 'links': [{'target_room': 101, 'entrance': 240, 'teleporter': [129, 0], 'access': []}, {'target_room': 114, 'access': ['Claw']}]}, {'name': 'Lava Dome Big Jump Room Main Area', 'id': 116, 'game_objects': [{'name': 'Lava Dome - Lava River Room North Box', 'object_id': 152, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Lava River Room East Box', 'object_id': 153, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Lava River Room South Box', 'object_id': 154, 'type': 'Box', 'access': []}], 'links': [{'target_room': 100, 'entrance': 241, 'teleporter': [133, 0], 'access': []}, {'target_room': 119, 'entrance': 243, 'teleporter': [132, 0], 'access': []}, {'target_room': 117, 'access': ['MegaGrenade']}]}, {'name': 'Lava Dome Big Jump Room MegaGrenade Area', 'id': 117, 'game_objects': [], 'links': [{'target_room': 112, 'entrance': 242, 'teleporter': [131, 0], 'access': []}, {'target_room': 116, 'access': ['Bomb']}]}, {'name': 'Lava Dome Split Corridor', 'id': 118, 'game_objects': [{'name': 'Lava Dome - Split Corridor Box', 'object_id': 151, 'type': 'Box', 'access': []}], 'links': [{'target_room': 109, 'entrance': 244, 'teleporter': [130, 0], 'access': []}, {'target_room': 100, 'entrance': 245, 'teleporter': [134, 0], 'access': []}]}, {'name': 'Lava Dome Plate Corridor', 'id': 119, 'game_objects': [], 'links': [{'target_room': 102, 'entrance': 246, 'teleporter': [135, 0], 'access': []}, {'target_room': 116, 'entrance': 247, 'teleporter': [137, 0], 'access': []}]}, {'name': 'Lava Dome Four Boxes Stairs', 'id': 120, 'game_objects': [{'name': 'Lava Dome - Caldera Stairway West Left Box', 'object_id': 155, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Caldera Stairway West Right Box', 'object_id': 156, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Caldera Stairway East Left Box', 'object_id': 157, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Caldera Stairway East Right Box', 'object_id': 158, 'type': 'Box', 'access': []}], 'links': [{'target_room': 107, 'entrance': 248, 'teleporter': [136, 0], 'access': []}, {'target_room': 106, 'entrance': 249, 'teleporter': [16, 0], 'access': []}]}, {'name': 'Lava Dome Hydra Room', 'id': 121, 'game_objects': [{'name': 'Lava Dome - Dualhead Hydra Chest', 'object_id': 20, 'type': 'Chest', 'access': ['DualheadHydra']}, {'name': 'Dualhead Hydra', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['DualheadHydra'], 'access': []}, {'name': 'Lava Dome - Hydra Room Northwest Box', 'object_id': 159, 'type': 'Box', 'access': []}, {'name': 'Lava Dome - Hydra Room Southweast Box', 'object_id': 160, 'type': 'Box', 'access': []}], 'links': [{'target_room': 105, 'entrance': 250, 'teleporter': [105, 3], 'access': []}, {'target_room': 122, 'entrance': 251, 'teleporter': [138, 0], 'access': ['DualheadHydra']}]}, {'name': 'Lava Dome Escape Corridor', 'id': 122, 'game_objects': [], 'links': [{'target_room': 121, 'entrance': 253, 'teleporter': [139, 0], 'access': []}]}, {'name': 'Rope Bridge', 'id': 123, 'game_objects': [{'name': 'Rope Bridge - West Box', 'object_id': 163, 'type': 'Box', 'access': []}, {'name': 'Rope Bridge - East Box', 'object_id': 164, 'type': 'Box', 'access': []}], 'links': [{'target_room': 226, 'entrance': 255, 'teleporter': [140, 0], 'access': []}]}, {'name': 'Alive Forest', 'id': 124, 'game_objects': [{'name': 'Alive Forest - Tree Stump Chest', 'object_id': 21, 'type': 'Chest', 'access': ['Axe']}, {'name': 'Alive Forest - Near Entrance Box', 'object_id': 165, 'type': 'Box', 'access': ['Axe']}, {'name': 'Alive Forest - After Bridge Box', 'object_id': 166, 'type': 'Box', 'access': ['Axe']}, {'name': 'Alive Forest - Gemini Stump Box', 'object_id': 167, 'type': 'Box', 'access': ['Axe']}], 'links': [{'target_room': 226, 'entrance': 272, 'teleporter': [142, 0], 'access': ['Axe']}, {'target_room': 21, 'entrance': 275, 'teleporter': [64, 8], 'access': ['LibraCrest', 'Axe']}, {'target_room': 22, 'entrance': 276, 'teleporter': [65, 8], 'access': ['GeminiCrest', 'Axe']}, {'target_room': 23, 'entrance': 277, 'teleporter': [66, 8], 'access': ['MobiusCrest', 'Axe']}, {'target_room': 125, 'entrance': 274, 'teleporter': [143, 0], 'access': ['Axe']}]}, {'name': 'Giant Tree 1F Main Area', 'id': 125, 'game_objects': [{'name': 'Giant Tree 1F - Northwest Box', 'object_id': 168, 'type': 'Box', 'access': []}, {'name': 'Giant Tree 1F - Southwest Box', 'object_id': 169, 'type': 'Box', 'access': []}, {'name': 'Giant Tree 1F - Center Box', 'object_id': 170, 'type': 'Box', 'access': []}, {'name': 'Giant Tree 1F - East Box', 'object_id': 171, 'type': 'Box', 'access': []}], 'links': [{'target_room': 124, 'entrance': 278, 'teleporter': [56, 1], 'access': []}, {'target_room': 202, 'access': ['DragonClaw']}]}, {'name': 'Giant Tree 1F North Island', 'id': 202, 'game_objects': [], 'links': [{'target_room': 127, 'entrance': 280, 'teleporter': [144, 0], 'access': []}, {'target_room': 125, 'access': ['DragonClaw']}]}, {'name': 'Giant Tree 1F Central Island', 'id': 126, 'game_objects': [], 'links': [{'target_room': 202, 'access': ['DragonClaw']}]}, {'name': 'Giant Tree 2F Main Lobby', 'id': 127, 'game_objects': [{'name': 'Giant Tree 2F - North Box', 'object_id': 172, 'type': 'Box', 'access': []}], 'links': [{'target_room': 126, 'access': ['DragonClaw']}, {'target_room': 125, 'entrance': 281, 'teleporter': [145, 0], 'access': []}, {'target_room': 133, 'entrance': 283, 'teleporter': [149, 0], 'access': []}, {'target_room': 129, 'access': ['DragonClaw']}]}, {'name': 'Giant Tree 2F West Ledge', 'id': 128, 'game_objects': [{'name': 'Giant Tree 2F - Dropdown Ledge Box', 'object_id': 174, 'type': 'Box', 'access': []}], 'links': [{'target_room': 140, 'entrance': 284, 'teleporter': [147, 0], 'access': ['Sword']}, {'target_room': 130, 'access': ['DragonClaw']}]}, {'name': 'Giant Tree 2F Lower Area', 'id': 129, 'game_objects': [{'name': 'Giant Tree 2F - South Box', 'object_id': 173, 'type': 'Box', 'access': []}], 'links': [{'target_room': 130, 'access': ['Claw']}, {'target_room': 131, 'access': ['Claw']}]}, {'name': 'Giant Tree 2F Central Island', 'id': 130, 'game_objects': [], 'links': [{'target_room': 129, 'access': ['Claw']}, {'target_room': 135, 'entrance': 282, 'teleporter': [146, 0], 'access': ['Sword']}]}, {'name': 'Giant Tree 2F East Ledge', 'id': 131, 'game_objects': [], 'links': [{'target_room': 129, 'access': ['Claw']}, {'target_room': 130, 'access': ['DragonClaw']}]}, {'name': 'Giant Tree 2F Meteor Chest Room', 'id': 132, 'game_objects': [{'name': 'Giant Tree 2F - Gidrah Chest', 'object_id': 22, 'type': 'Chest', 'access': []}], 'links': [{'target_room': 133, 'entrance': 285, 'teleporter': [148, 0], 'access': []}]}, {'name': 'Giant Tree 2F Mushroom Room', 'id': 133, 'game_objects': [{'name': 'Giant Tree 2F - Mushroom Tunnel West Box', 'object_id': 175, 'type': 'Box', 'access': ['Axe']}, {'name': 'Giant Tree 2F - Mushroom Tunnel East Box', 'object_id': 176, 'type': 'Box', 'access': ['Axe']}], 'links': [{'target_room': 127, 'entrance': 286, 'teleporter': [150, 0], 'access': ['Axe']}, {'target_room': 132, 'entrance': 287, 'teleporter': [151, 0], 'access': ['Axe', 'Gidrah']}]}, {'name': 'Giant Tree 3F Central Island', 'id': 135, 'game_objects': [{'name': 'Giant Tree 3F - Central Island Box', 'object_id': 179, 'type': 'Box', 'access': []}], 'links': [{'target_room': 130, 'entrance': 288, 'teleporter': [152, 0], 'access': []}, {'target_room': 136, 'access': ['Claw']}, {'target_room': 137, 'access': ['DragonClaw']}]}, {'name': 'Giant Tree 3F Central Area', 'id': 136, 'game_objects': [{'name': 'Giant Tree 3F - Center North Box', 'object_id': 177, 'type': 'Box', 'access': []}, {'name': 'Giant Tree 3F - Center West Box', 'object_id': 178, 'type': 'Box', 'access': []}], 'links': [{'target_room': 135, 'access': ['Claw']}, {'target_room': 127, 'access': []}, {'target_room': 131, 'access': []}]}, {'name': 'Giant Tree 3F Lower Ledge', 'id': 137, 'game_objects': [], 'links': [{'target_room': 135, 'access': ['DragonClaw']}, {'target_room': 142, 'entrance': 289, 'teleporter': [153, 0], 'access': ['Sword']}]}, {'name': 'Giant Tree 3F West Area', 'id': 138, 'game_objects': [{'name': 'Giant Tree 3F - West Side Box', 'object_id': 180, 'type': 'Box', 'access': []}], 'links': [{'target_room': 128, 'access': []}, {'target_room': 210, 'entrance': 290, 'teleporter': [154, 0], 'access': []}]}, {'name': 'Giant Tree 3F Middle Up Island', 'id': 139, 'game_objects': [], 'links': [{'target_room': 136, 'access': ['Claw']}]}, {'name': 'Giant Tree 3F West Platform', 'id': 140, 'game_objects': [], 'links': [{'target_room': 139, 'access': ['Claw']}, {'target_room': 141, 'access': ['Claw']}, {'target_room': 128, 'entrance': 291, 'teleporter': [155, 0], 'access': []}]}, {'name': 'Giant Tree 3F North Ledge', 'id': 141, 'game_objects': [], 'links': [{'target_room': 143, 'entrance': 292, 'teleporter': [156, 0], 'access': ['Sword']}, {'target_room': 139, 'access': ['Claw']}, {'target_room': 136, 'access': ['Claw']}]}, {'name': 'Giant Tree Worm Room Upper Ledge', 'id': 142, 'game_objects': [{'name': 'Giant Tree 3F - Worm Room North Box', 'object_id': 181, 'type': 'Box', 'access': ['Axe']}, {'name': 'Giant Tree 3F - Worm Room South Box', 'object_id': 182, 'type': 'Box', 'access': ['Axe']}], 'links': [{'target_room': 137, 'entrance': 293, 'teleporter': [157, 0], 'access': ['Axe']}, {'target_room': 210, 'access': ['Axe', 'Claw']}]}, {'name': 'Giant Tree Worm Room Lower Ledge', 'id': 210, 'game_objects': [], 'links': [{'target_room': 138, 'entrance': 294, 'teleporter': [158, 0], 'access': []}]}, {'name': 'Giant Tree 4F Lower Floor', 'id': 143, 'game_objects': [], 'links': [{'target_room': 141, 'entrance': 295, 'teleporter': [159, 0], 'access': []}, {'target_room': 148, 'entrance': 296, 'teleporter': [160, 0], 'access': []}, {'target_room': 148, 'entrance': 297, 'teleporter': [161, 0], 'access': []}, {'target_room': 147, 'entrance': 298, 'teleporter': [162, 0], 'access': ['Sword']}]}, {'name': 'Giant Tree 4F Middle Floor', 'id': 144, 'game_objects': [{'name': 'Giant Tree 4F - Highest Platform North Box', 'object_id': 183, 'type': 'Box', 'access': []}, {'name': 'Giant Tree 4F - Highest Platform South Box', 'object_id': 184, 'type': 'Box', 'access': []}], 'links': [{'target_room': 149, 'entrance': 299, 'teleporter': [163, 0], 'access': []}, {'target_room': 145, 'access': ['Claw']}, {'target_room': 146, 'access': ['DragonClaw']}]}, {'name': 'Giant Tree 4F Upper Floor', 'id': 145, 'game_objects': [], 'links': [{'target_room': 150, 'entrance': 300, 'teleporter': [164, 0], 'access': ['Sword']}, {'target_room': 144, 'access': ['Claw']}]}, {'name': 'Giant Tree 4F South Ledge', 'id': 146, 'game_objects': [{'name': 'Giant Tree 4F - Hook Ledge Northeast Box', 'object_id': 185, 'type': 'Box', 'access': []}, {'name': 'Giant Tree 4F - Hook Ledge Southwest Box', 'object_id': 186, 'type': 'Box', 'access': []}], 'links': [{'target_room': 144, 'access': ['DragonClaw']}]}, {'name': 'Giant Tree 4F Slime Room East Area', 'id': 147, 'game_objects': [{'name': 'Giant Tree 4F - East Slime Room Box', 'object_id': 188, 'type': 'Box', 'access': ['Axe']}], 'links': [{'target_room': 143, 'entrance': 304, 'teleporter': [168, 0], 'access': []}]}, {'name': 'Giant Tree 4F Slime Room West Area', 'id': 148, 'game_objects': [], 'links': [{'target_room': 143, 'entrance': 303, 'teleporter': [167, 0], 'access': ['Axe']}, {'target_room': 143, 'entrance': 302, 'teleporter': [166, 0], 'access': ['Axe']}, {'target_room': 149, 'access': ['Axe', 'Claw']}]}, {'name': 'Giant Tree 4F Slime Room Platform', 'id': 149, 'game_objects': [{'name': 'Giant Tree 4F - West Slime Room Box', 'object_id': 187, 'type': 'Box', 'access': []}], 'links': [{'target_room': 144, 'entrance': 301, 'teleporter': [165, 0], 'access': []}, {'target_room': 148, 'access': ['Claw']}]}, {'name': 'Giant Tree 5F Lower Area', 'id': 150, 'game_objects': [{'name': 'Giant Tree 5F - Northwest Left Box', 'object_id': 189, 'type': 'Box', 'access': []}, {'name': 'Giant Tree 5F - Northwest Right Box', 'object_id': 190, 'type': 'Box', 'access': []}, {'name': 'Giant Tree 5F - South Left Box', 'object_id': 191, 'type': 'Box', 'access': []}, {'name': 'Giant Tree 5F - South Right Box', 'object_id': 192, 'type': 'Box', 'access': []}], 'links': [{'target_room': 145, 'entrance': 305, 'teleporter': [169, 0], 'access': []}, {'target_room': 151, 'access': ['Claw']}, {'target_room': 143, 'access': []}]}, {'name': 'Giant Tree 5F Gidrah Platform', 'id': 151, 'game_objects': [{'name': 'Gidrah', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Gidrah'], 'access': []}], 'links': [{'target_room': 150, 'access': ['Claw']}]}, {'name': 'Kaidge Temple Lower Ledge', 'id': 152, 'game_objects': [], 'links': [{'target_room': 226, 'entrance': 307, 'teleporter': [18, 6], 'access': []}, {'target_room': 153, 'access': ['Claw']}]}, {'name': 'Kaidge Temple Upper Ledge', 'id': 153, 'game_objects': [{'name': 'Kaidge Temple - Box', 'object_id': 193, 'type': 'Box', 'access': []}], 'links': [{'target_room': 185, 'entrance': 308, 'teleporter': [71, 8], 'access': ['MobiusCrest']}, {'target_room': 152, 'access': ['Claw']}]}, {'name': 'Windhole Temple', 'id': 154, 'game_objects': [{'name': 'Windhole Temple - Box', 'object_id': 194, 'type': 'Box', 'access': []}], 'links': [{'target_room': 226, 'entrance': 309, 'teleporter': [173, 0], 'access': []}]}, {'name': 'Mount Gale', 'id': 155, 'game_objects': [{'name': 'Mount Gale - Dullahan Chest', 'object_id': 23, 'type': 'Chest', 'access': ['DragonClaw', 'Dullahan']}, {'name': 'Dullahan', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Dullahan'], 'access': ['DragonClaw']}, {'name': 'Mount Gale - East Box', 'object_id': 195, 'type': 'Box', 'access': ['DragonClaw']}, {'name': 'Mount Gale - West Box', 'object_id': 196, 'type': 'Box', 'access': []}], 'links': [{'target_room': 226, 'entrance': 310, 'teleporter': [174, 0], 'access': []}]}, {'name': 'Windia', 'id': 156, 'game_objects': [], 'links': [{'target_room': 226, 'entrance': 312, 'teleporter': [10, 6], 'access': []}, {'target_room': 157, 'entrance': 320, 'teleporter': [30, 5], 'access': []}, {'target_room': 163, 'entrance': 321, 'teleporter': [97, 8], 'access': []}, {'target_room': 165, 'entrance': 322, 'teleporter': [32, 5], 'access': []}, {'target_room': 159, 'entrance': 323, 'teleporter': [176, 4], 'access': []}, {'target_room': 160, 'entrance': 324, 'teleporter': [177, 4], 'access': []}]}, {'name': "Otto's House", 'id': 157, 'game_objects': [{'name': 'Otto', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['RainbowBridge'], 'access': ['ThunderRock']}], 'links': [{'target_room': 156, 'entrance': 327, 'teleporter': [106, 3], 'access': []}, {'target_room': 158, 'entrance': 326, 'teleporter': [33, 2], 'access': []}]}, {'name': "Otto's Attic", 'id': 158, 'game_objects': [{'name': "Windia - Otto's Attic Box", 'object_id': 197, 'type': 'Box', 'access': []}], 'links': [{'target_room': 157, 'entrance': 328, 'teleporter': [107, 3], 'access': []}]}, {'name': 'Windia Kid House', 'id': 159, 'game_objects': [], 'links': [{'target_room': 156, 'entrance': 329, 'teleporter': [178, 0], 'access': []}, {'target_room': 161, 'entrance': 330, 'teleporter': [180, 0], 'access': []}]}, {'name': 'Windia Old People House', 'id': 160, 'game_objects': [], 'links': [{'target_room': 156, 'entrance': 331, 'teleporter': [179, 0], 'access': []}, {'target_room': 162, 'entrance': 332, 'teleporter': [181, 0], 'access': []}]}, {'name': 'Windia Kid House Basement', 'id': 161, 'game_objects': [], 'links': [{'target_room': 159, 'entrance': 333, 'teleporter': [182, 0], 'access': []}, {'target_room': 79, 'entrance': 334, 'teleporter': [44, 8], 'access': ['MobiusCrest']}]}, {'name': 'Windia Old People House Basement', 'id': 162, 'game_objects': [{'name': 'Windia - Mobius Basement West Box', 'object_id': 200, 'type': 'Box', 'access': []}, {'name': 'Windia - Mobius Basement East Box', 'object_id': 201, 'type': 'Box', 'access': []}], 'links': [{'target_room': 160, 'entrance': 335, 'teleporter': [183, 0], 'access': []}, {'target_room': 186, 'entrance': 336, 'teleporter': [43, 8], 'access': ['MobiusCrest']}]}, {'name': 'Windia Inn Lobby', 'id': 163, 'game_objects': [], 'links': [{'target_room': 156, 'entrance': 338, 'teleporter': [135, 3], 'access': []}, {'target_room': 164, 'entrance': 337, 'teleporter': [102, 8], 'access': []}]}, {'name': 'Windia Inn Beds', 'id': 164, 'game_objects': [{'name': 'Windia - Inn Bedroom North Box', 'object_id': 198, 'type': 'Box', 'access': []}, {'name': 'Windia - Inn Bedroom South Box', 'object_id': 199, 'type': 'Box', 'access': []}, {'name': 'Windia - Kaeli', 'object_id': 15, 'type': 'NPC', 'access': ['Kaeli2']}], 'links': [{'target_room': 163, 'entrance': 339, 'teleporter': [216, 0], 'access': []}]}, {'name': 'Windia Vendor House', 'id': 165, 'game_objects': [{'name': 'Windia - Vendor', 'object_id': 16, 'type': 'NPC', 'access': []}], 'links': [{'target_room': 156, 'entrance': 340, 'teleporter': [108, 3], 'access': []}]}, {'name': 'Pazuzu Tower 1F Main Lobby', 'id': 166, 'game_objects': [{'name': 'Pazuzu 1F', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu1F'], 'access': []}], 'links': [{'target_room': 226, 'entrance': 341, 'teleporter': [184, 0], 'access': []}, {'target_room': 180, 'entrance': 345, 'teleporter': [185, 0], 'access': []}]}, {'name': 'Pazuzu Tower 1F Boxes Room', 'id': 167, 'game_objects': [{'name': "Pazuzu's Tower 1F - Descent Bomb Wall West Box", 'object_id': 202, 'type': 'Box', 'access': ['Bomb']}, {'name': "Pazuzu's Tower 1F - Descent Bomb Wall Center Box", 'object_id': 203, 'type': 'Box', 'access': ['Bomb']}, {'name': "Pazuzu's Tower 1F - Descent Bomb Wall East Box", 'object_id': 204, 'type': 'Box', 'access': ['Bomb']}, {'name': "Pazuzu's Tower 1F - Descent Box", 'object_id': 205, 'type': 'Box', 'access': []}], 'links': [{'target_room': 169, 'entrance': 349, 'teleporter': [187, 0], 'access': []}]}, {'name': 'Pazuzu Tower 1F Southern Platform', 'id': 168, 'game_objects': [], 'links': [{'target_room': 169, 'entrance': 346, 'teleporter': [186, 0], 'access': []}, {'target_room': 166, 'access': ['DragonClaw']}]}, {'name': 'Pazuzu 2F', 'id': 169, 'game_objects': [{'name': "Pazuzu's Tower 2F - East Room West Box", 'object_id': 206, 'type': 'Box', 'access': []}, {'name': "Pazuzu's Tower 2F - East Room East Box", 'object_id': 207, 'type': 'Box', 'access': []}, {'name': 'Pazuzu 2F Lock', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu2FLock'], 'access': ['Axe']}, {'name': 'Pazuzu 2F', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu2F'], 'access': ['Bomb']}], 'links': [{'target_room': 183, 'entrance': 350, 'teleporter': [188, 0], 'access': []}, {'target_room': 168, 'entrance': 351, 'teleporter': [189, 0], 'access': []}, {'target_room': 167, 'entrance': 352, 'teleporter': [190, 0], 'access': []}, {'target_room': 171, 'entrance': 353, 'teleporter': [191, 0], 'access': []}]}, {'name': 'Pazuzu 3F Main Room', 'id': 170, 'game_objects': [{'name': "Pazuzu's Tower 3F - Guest Room West Box", 'object_id': 208, 'type': 'Box', 'access': []}, {'name': "Pazuzu's Tower 3F - Guest Room East Box", 'object_id': 209, 'type': 'Box', 'access': []}, {'name': 'Pazuzu 3F', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu3F'], 'access': []}], 'links': [{'target_room': 180, 'entrance': 356, 'teleporter': [192, 0], 'access': []}, {'target_room': 181, 'entrance': 357, 'teleporter': [193, 0], 'access': []}]}, {'name': 'Pazuzu 3F Central Island', 'id': 171, 'game_objects': [], 'links': [{'target_room': 169, 'entrance': 360, 'teleporter': [194, 0], 'access': []}, {'target_room': 170, 'access': ['DragonClaw']}, {'target_room': 172, 'access': ['DragonClaw']}]}, {'name': 'Pazuzu 3F Southern Island', 'id': 172, 'game_objects': [{'name': "Pazuzu's Tower 3F - South Ledge Box", 'object_id': 210, 'type': 'Box', 'access': []}], 'links': [{'target_room': 173, 'entrance': 361, 'teleporter': [195, 0], 'access': []}, {'target_room': 171, 'access': ['DragonClaw']}]}, {'name': 'Pazuzu 4F', 'id': 173, 'game_objects': [{'name': "Pazuzu's Tower 4F - Elevator West Box", 'object_id': 211, 'type': 'Box', 'access': ['Bomb']}, {'name': "Pazuzu's Tower 4F - Elevator East Box", 'object_id': 212, 'type': 'Box', 'access': ['Bomb']}, {'name': "Pazuzu's Tower 4F - East Storage Room Chest", 'object_id': 24, 'type': 'Chest', 'access': []}, {'name': 'Pazuzu 4F Lock', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu4FLock'], 'access': ['Axe']}, {'name': 'Pazuzu 4F', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu4F'], 'access': ['Bomb']}], 'links': [{'target_room': 183, 'entrance': 362, 'teleporter': [196, 0], 'access': []}, {'target_room': 184, 'entrance': 363, 'teleporter': [197, 0], 'access': []}, {'target_room': 172, 'entrance': 364, 'teleporter': [198, 0], 'access': []}, {'target_room': 175, 'entrance': 365, 'teleporter': [199, 0], 'access': []}]}, {'name': 'Pazuzu 5F Pazuzu Loop', 'id': 174, 'game_objects': [{'name': 'Pazuzu 5F', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu5F'], 'access': []}], 'links': [{'target_room': 181, 'entrance': 368, 'teleporter': [200, 0], 'access': []}, {'target_room': 182, 'entrance': 369, 'teleporter': [201, 0], 'access': []}]}, {'name': 'Pazuzu 5F Upper Loop', 'id': 175, 'game_objects': [{'name': "Pazuzu's Tower 5F - North Box", 'object_id': 213, 'type': 'Box', 'access': []}, {'name': "Pazuzu's Tower 5F - South Box", 'object_id': 214, 'type': 'Box', 'access': []}], 'links': [{'target_room': 173, 'entrance': 370, 'teleporter': [202, 0], 'access': []}, {'target_room': 176, 'entrance': 371, 'teleporter': [203, 0], 'access': []}]}, {'name': 'Pazuzu 6F', 'id': 176, 'game_objects': [{'name': "Pazuzu's Tower 6F - Box", 'object_id': 215, 'type': 'Box', 'access': []}, {'name': "Pazuzu's Tower 6F - Chest", 'object_id': 25, 'type': 'Chest', 'access': []}, {'name': 'Pazuzu 6F Lock', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu6FLock'], 'access': ['Bomb', 'Axe']}, {'name': 'Pazuzu 6F', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu6F'], 'access': ['Bomb']}], 'links': [{'target_room': 184, 'entrance': 374, 'teleporter': [204, 0], 'access': []}, {'target_room': 175, 'entrance': 375, 'teleporter': [205, 0], 'access': []}, {'target_room': 178, 'entrance': 376, 'teleporter': [206, 0], 'access': []}, {'target_room': 178, 'entrance': 377, 'teleporter': [207, 0], 'access': []}]}, {'name': 'Pazuzu 7F Southwest Area', 'id': 177, 'game_objects': [], 'links': [{'target_room': 182, 'entrance': 380, 'teleporter': [26, 0], 'access': []}, {'target_room': 178, 'access': ['DragonClaw']}]}, {'name': 'Pazuzu 7F Rest of the Area', 'id': 178, 'game_objects': [], 'links': [{'target_room': 177, 'access': ['DragonClaw']}, {'target_room': 176, 'entrance': 381, 'teleporter': [27, 0], 'access': []}, {'target_room': 176, 'entrance': 382, 'teleporter': [28, 0], 'access': []}, {'target_room': 179, 'access': ['DragonClaw', 'Pazuzu2FLock', 'Pazuzu4FLock', 'Pazuzu6FLock', 'Pazuzu1F', 'Pazuzu2F', 'Pazuzu3F', 'Pazuzu4F', 'Pazuzu5F', 'Pazuzu6F']}]}, {'name': 'Pazuzu 7F Sky Room', 'id': 179, 'game_objects': [{'name': "Pazuzu's Tower 7F - Pazuzu Chest", 'object_id': 26, 'type': 'Chest', 'access': []}, {'name': 'Pazuzu', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Pazuzu'], 'access': ['Pazuzu2FLock', 'Pazuzu4FLock', 'Pazuzu6FLock', 'Pazuzu1F', 'Pazuzu2F', 'Pazuzu3F', 'Pazuzu4F', 'Pazuzu5F', 'Pazuzu6F']}], 'links': [{'target_room': 178, 'access': ['DragonClaw']}]}, {'name': 'Pazuzu 1F to 3F', 'id': 180, 'game_objects': [], 'links': [{'target_room': 166, 'entrance': 385, 'teleporter': [29, 0], 'access': []}, {'target_room': 170, 'entrance': 386, 'teleporter': [30, 0], 'access': []}]}, {'name': 'Pazuzu 3F to 5F', 'id': 181, 'game_objects': [], 'links': [{'target_room': 170, 'entrance': 387, 'teleporter': [40, 0], 'access': []}, {'target_room': 174, 'entrance': 388, 'teleporter': [41, 0], 'access': []}]}, {'name': 'Pazuzu 5F to 7F', 'id': 182, 'game_objects': [], 'links': [{'target_room': 174, 'entrance': 389, 'teleporter': [38, 0], 'access': []}, {'target_room': 177, 'entrance': 390, 'teleporter': [39, 0], 'access': []}]}, {'name': 'Pazuzu 2F to 4F', 'id': 183, 'game_objects': [], 'links': [{'target_room': 169, 'entrance': 391, 'teleporter': [21, 0], 'access': []}, {'target_room': 173, 'entrance': 392, 'teleporter': [22, 0], 'access': []}]}, {'name': 'Pazuzu 4F to 6F', 'id': 184, 'game_objects': [], 'links': [{'target_room': 173, 'entrance': 393, 'teleporter': [2, 0], 'access': []}, {'target_room': 176, 'entrance': 394, 'teleporter': [3, 0], 'access': []}]}, {'name': 'Light Temple', 'id': 185, 'game_objects': [{'name': 'Light Temple - Box', 'object_id': 216, 'type': 'Box', 'access': []}], 'links': [{'target_room': 230, 'entrance': 395, 'teleporter': [19, 6], 'access': []}, {'target_room': 153, 'entrance': 396, 'teleporter': [70, 8], 'access': ['MobiusCrest']}]}, {'name': 'Ship Dock', 'id': 186, 'game_objects': [{'name': 'Ship Dock Access', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['ShipDockAccess'], 'access': []}], 'links': [{'target_room': 228, 'entrance': 399, 'teleporter': [17, 6], 'access': []}, {'target_room': 162, 'entrance': 397, 'teleporter': [61, 8], 'access': ['MobiusCrest']}]}, {'name': 'Mac Ship Deck', 'id': 187, 'game_objects': [{'name': 'Mac Ship Steering Wheel', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['ShipSteeringWheel'], 'access': []}, {'name': "Mac's Ship Deck - North Box", 'object_id': 217, 'type': 'Box', 'access': []}, {'name': "Mac's Ship Deck - Center Box", 'object_id': 218, 'type': 'Box', 'access': []}, {'name': "Mac's Ship Deck - South Box", 'object_id': 219, 'type': 'Box', 'access': []}], 'links': [{'target_room': 229, 'entrance': 400, 'teleporter': [37, 8], 'access': []}, {'target_room': 188, 'entrance': 401, 'teleporter': [50, 8], 'access': []}, {'target_room': 188, 'entrance': 402, 'teleporter': [51, 8], 'access': []}, {'target_room': 188, 'entrance': 403, 'teleporter': [52, 8], 'access': []}, {'target_room': 189, 'entrance': 404, 'teleporter': [53, 8], 'access': []}]}, {'name': 'Mac Ship B1 Outer Ring', 'id': 188, 'game_objects': [{'name': "Mac's Ship B1 - Northwest Hook Platform Box", 'object_id': 228, 'type': 'Box', 'access': ['DragonClaw']}, {'name': "Mac's Ship B1 - Center Hook Platform Box", 'object_id': 229, 'type': 'Box', 'access': ['DragonClaw']}], 'links': [{'target_room': 187, 'entrance': 405, 'teleporter': [208, 0], 'access': []}, {'target_room': 187, 'entrance': 406, 'teleporter': [175, 0], 'access': []}, {'target_room': 187, 'entrance': 407, 'teleporter': [172, 0], 'access': []}, {'target_room': 193, 'entrance': 408, 'teleporter': [88, 0], 'access': []}, {'target_room': 193, 'access': []}]}, {'name': 'Mac Ship B1 Square Room', 'id': 189, 'game_objects': [], 'links': [{'target_room': 187, 'entrance': 409, 'teleporter': [141, 0], 'access': []}, {'target_room': 192, 'entrance': 410, 'teleporter': [87, 0], 'access': []}]}, {'name': 'Mac Ship B1 Central Corridor', 'id': 190, 'game_objects': [{'name': "Mac's Ship B1 - Central Corridor Box", 'object_id': 230, 'type': 'Box', 'access': []}], 'links': [{'target_room': 192, 'entrance': 413, 'teleporter': [86, 0], 'access': []}, {'target_room': 191, 'entrance': 412, 'teleporter': [102, 0], 'access': []}, {'target_room': 193, 'access': []}]}, {'name': 'Mac Ship B2 South Corridor', 'id': 191, 'game_objects': [], 'links': [{'target_room': 190, 'entrance': 415, 'teleporter': [55, 8], 'access': []}, {'target_room': 194, 'entrance': 414, 'teleporter': [57, 1], 'access': []}]}, {'name': 'Mac Ship B2 North Corridor', 'id': 192, 'game_objects': [], 'links': [{'target_room': 190, 'entrance': 416, 'teleporter': [56, 8], 'access': []}, {'target_room': 189, 'entrance': 417, 'teleporter': [57, 8], 'access': []}]}, {'name': 'Mac Ship B2 Outer Ring', 'id': 193, 'game_objects': [{'name': "Mac's Ship B2 - Barrel Room South Box", 'object_id': 223, 'type': 'Box', 'access': []}, {'name': "Mac's Ship B2 - Barrel Room North Box", 'object_id': 224, 'type': 'Box', 'access': []}, {'name': "Mac's Ship B2 - Southwest Room Box", 'object_id': 225, 'type': 'Box', 'access': []}, {'name': "Mac's Ship B2 - Southeast Room Box", 'object_id': 226, 'type': 'Box', 'access': []}], 'links': [{'target_room': 188, 'entrance': 418, 'teleporter': [58, 8], 'access': []}]}, {'name': 'Mac Ship B1 Mac Room', 'id': 194, 'game_objects': [{'name': "Mac's Ship B1 - Mac Room Chest", 'object_id': 27, 'type': 'Chest', 'access': []}, {'name': 'Captain Mac', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['ShipLoaned'], 'access': ['CaptainCap']}], 'links': [{'target_room': 191, 'entrance': 424, 'teleporter': [101, 0], 'access': []}]}, {'name': 'Doom Castle Corridor of Destiny', 'id': 195, 'game_objects': [], 'links': [{'target_room': 201, 'entrance': 428, 'teleporter': [84, 0], 'access': []}, {'target_room': 196, 'entrance': 429, 'teleporter': [35, 2], 'access': []}, {'target_room': 197, 'entrance': 430, 'teleporter': [209, 0], 'access': ['StoneGolem']}, {'target_room': 198, 'entrance': 431, 'teleporter': [211, 0], 'access': ['StoneGolem', 'TwinheadWyvern']}, {'target_room': 199, 'entrance': 432, 'teleporter': [13, 2], 'access': ['StoneGolem', 'TwinheadWyvern', 'Zuh']}]}, {'name': 'Doom Castle Ice Floor', 'id': 196, 'game_objects': [{'name': 'Doom Castle 4F - Northwest Room Box', 'object_id': 231, 'type': 'Box', 'access': ['Sword', 'DragonClaw']}, {'name': 'Doom Castle 4F - Southwest Room Box', 'object_id': 232, 'type': 'Box', 'access': ['Sword', 'DragonClaw']}, {'name': 'Doom Castle 4F - Northeast Room Box', 'object_id': 233, 'type': 'Box', 'access': ['Sword']}, {'name': 'Doom Castle 4F - Southeast Room Box', 'object_id': 234, 'type': 'Box', 'access': ['Sword', 'DragonClaw']}, {'name': 'Stone Golem', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['StoneGolem'], 'access': ['Sword', 'DragonClaw']}], 'links': [{'target_room': 195, 'entrance': 433, 'teleporter': [109, 3], 'access': []}]}, {'name': 'Doom Castle Lava Floor', 'id': 197, 'game_objects': [{'name': 'Doom Castle 5F - North Left Box', 'object_id': 235, 'type': 'Box', 'access': ['DragonClaw']}, {'name': 'Doom Castle 5F - North Right Box', 'object_id': 236, 'type': 'Box', 'access': ['DragonClaw']}, {'name': 'Doom Castle 5F - South Left Box', 'object_id': 237, 'type': 'Box', 'access': ['DragonClaw']}, {'name': 'Doom Castle 5F - South Right Box', 'object_id': 238, 'type': 'Box', 'access': ['DragonClaw']}, {'name': 'Twinhead Wyvern', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['TwinheadWyvern'], 'access': ['DragonClaw']}], 'links': [{'target_room': 195, 'entrance': 434, 'teleporter': [210, 0], 'access': []}]}, {'name': 'Doom Castle Sky Floor', 'id': 198, 'game_objects': [{'name': 'Doom Castle 6F - West Box', 'object_id': 239, 'type': 'Box', 'access': []}, {'name': 'Doom Castle 6F - East Box', 'object_id': 240, 'type': 'Box', 'access': []}, {'name': 'Zuh', 'object_id': 0, 'type': 'Trigger', 'on_trigger': ['Zuh'], 'access': ['DragonClaw']}], 'links': [{'target_room': 195, 'entrance': 435, 'teleporter': [212, 0], 'access': []}, {'target_room': 197, 'access': []}]}, {'name': 'Doom Castle Hero Room', 'id': 199, 'game_objects': [{'name': 'Doom Castle Hero Chest 01', 'object_id': 242, 'type': 'Chest', 'access': []}, {'name': 'Doom Castle Hero Chest 02', 'object_id': 243, 'type': 'Chest', 'access': []}, {'name': 'Doom Castle Hero Chest 03', 'object_id': 244, 'type': 'Chest', 'access': []}, {'name': 'Doom Castle Hero Chest 04', 'object_id': 245, 'type': 'Chest', 'access': []}], 'links': [{'target_room': 200, 'entrance': 436, 'teleporter': [54, 0], 'access': []}, {'target_room': 195, 'entrance': 441, 'teleporter': [110, 3], 'access': []}]}, {'name': 'Doom Castle Dark King Room', 'id': 200, 'game_objects': [], 'links': [{'target_room': 199, 'entrance': 442, 'teleporter': [52, 0], 'access': []}]}] +entrances = [{'name': 'Doom Castle - Sand Floor - To Sky Door - Sand Floor', 'id': 0, 'area': 7, 'coordinates': [24, 19], 'teleporter': [0, 0]}, {'name': 'Doom Castle - Sand Floor - Main Entrance - Sand Floor', 'id': 1, 'area': 7, 'coordinates': [19, 43], 'teleporter': [1, 6]}, {'name': 'Doom Castle - Aero Room - Aero Room Entrance', 'id': 2, 'area': 7, 'coordinates': [27, 39], 'teleporter': [1, 0]}, {'name': 'Focus Tower B1 - Main Loop - South Entrance', 'id': 3, 'area': 8, 'coordinates': [43, 60], 'teleporter': [2, 6]}, {'name': 'Focus Tower B1 - Main Loop - To Focus Tower 1F - Main Hall', 'id': 4, 'area': 8, 'coordinates': [37, 41], 'teleporter': [4, 0]}, {'name': 'Focus Tower B1 - Aero Corridor - To Focus Tower 1F - Sun Coin Room', 'id': 5, 'area': 8, 'coordinates': [59, 35], 'teleporter': [5, 0]}, {'name': 'Focus Tower B1 - Aero Corridor - To Sand Floor - Aero Chest', 'id': 6, 'area': 8, 'coordinates': [57, 59], 'teleporter': [8, 0]}, {'name': 'Focus Tower B1 - Inner Loop - To Focus Tower 1F - Sky Door', 'id': 7, 'area': 8, 'coordinates': [51, 49], 'teleporter': [6, 0]}, {'name': 'Focus Tower B1 - Inner Loop - To Doom Castle Sand Floor', 'id': 8, 'area': 8, 'coordinates': [51, 45], 'teleporter': [7, 0]}, {'name': 'Focus Tower 1F - Focus Tower West Entrance', 'id': 9, 'area': 9, 'coordinates': [25, 29], 'teleporter': [3, 6]}, {'name': 'Focus Tower 1F - To Focus Tower 2F - From SandCoin', 'id': 10, 'area': 9, 'coordinates': [16, 4], 'teleporter': [10, 0]}, {'name': 'Focus Tower 1F - To Focus Tower B1 - Main Hall', 'id': 11, 'area': 9, 'coordinates': [4, 23], 'teleporter': [11, 0]}, {'name': 'Focus Tower 1F - To Focus Tower B1 - To Aero Chest', 'id': 12, 'area': 9, 'coordinates': [26, 17], 'teleporter': [12, 0]}, {'name': 'Focus Tower 1F - Sky Door', 'id': 13, 'area': 9, 'coordinates': [16, 24], 'teleporter': [13, 0]}, {'name': 'Focus Tower 1F - To Focus Tower 2F - From RiverCoin', 'id': 14, 'area': 9, 'coordinates': [16, 10], 'teleporter': [14, 0]}, {'name': 'Focus Tower 1F - To Focus Tower B1 - From Sky Door', 'id': 15, 'area': 9, 'coordinates': [16, 29], 'teleporter': [15, 0]}, {'name': 'Focus Tower 2F - Sand Coin Passage - North Entrance', 'id': 16, 'area': 10, 'coordinates': [49, 30], 'teleporter': [4, 6]}, {'name': 'Focus Tower 2F - Sand Coin Passage - To Focus Tower 1F - To SandCoin', 'id': 17, 'area': 10, 'coordinates': [47, 33], 'teleporter': [17, 0]}, {'name': 'Focus Tower 2F - River Coin Passage - To Focus Tower 1F - To RiverCoin', 'id': 18, 'area': 10, 'coordinates': [47, 41], 'teleporter': [18, 0]}, {'name': 'Focus Tower 2F - River Coin Passage - To Focus Tower 3F - Lower Floor', 'id': 19, 'area': 10, 'coordinates': [38, 40], 'teleporter': [20, 0]}, {'name': 'Focus Tower 2F - Venus Chest Room - To Focus Tower 3F - Upper Floor', 'id': 20, 'area': 10, 'coordinates': [56, 40], 'teleporter': [19, 0]}, {'name': 'Focus Tower 2F - Venus Chest Room - Pillar Script', 'id': 21, 'area': 10, 'coordinates': [48, 53], 'teleporter': [13, 8]}, {'name': 'Focus Tower 3F - Lower Floor - To Fireburg Entrance', 'id': 22, 'area': 11, 'coordinates': [11, 39], 'teleporter': [6, 6]}, {'name': 'Focus Tower 3F - Lower Floor - To Focus Tower 2F - Jump on Pillar', 'id': 23, 'area': 11, 'coordinates': [6, 47], 'teleporter': [24, 0]}, {'name': 'Focus Tower 3F - Upper Floor - To Aquaria Entrance', 'id': 24, 'area': 11, 'coordinates': [21, 38], 'teleporter': [5, 6]}, {'name': 'Focus Tower 3F - Upper Floor - To Focus Tower 2F - Venus Chest Room', 'id': 25, 'area': 11, 'coordinates': [24, 47], 'teleporter': [23, 0]}, {'name': 'Level Forest - Boulder Script', 'id': 26, 'area': 14, 'coordinates': [52, 15], 'teleporter': [0, 8]}, {'name': 'Level Forest - Rotten Tree Script', 'id': 27, 'area': 14, 'coordinates': [47, 6], 'teleporter': [2, 8]}, {'name': 'Level Forest - Exit Level Forest 1', 'id': 28, 'area': 14, 'coordinates': [46, 25], 'teleporter': [25, 0]}, {'name': 'Level Forest - Exit Level Forest 2', 'id': 29, 'area': 14, 'coordinates': [46, 26], 'teleporter': [25, 0]}, {'name': 'Level Forest - Exit Level Forest 3', 'id': 30, 'area': 14, 'coordinates': [47, 25], 'teleporter': [25, 0]}, {'name': 'Level Forest - Exit Level Forest 4', 'id': 31, 'area': 14, 'coordinates': [47, 26], 'teleporter': [25, 0]}, {'name': 'Level Forest - Exit Level Forest 5', 'id': 32, 'area': 14, 'coordinates': [60, 14], 'teleporter': [25, 0]}, {'name': 'Level Forest - Exit Level Forest 6', 'id': 33, 'area': 14, 'coordinates': [61, 14], 'teleporter': [25, 0]}, {'name': 'Level Forest - Exit Level Forest 7', 'id': 34, 'area': 14, 'coordinates': [46, 4], 'teleporter': [25, 0]}, {'name': 'Level Forest - Exit Level Forest 8', 'id': 35, 'area': 14, 'coordinates': [46, 3], 'teleporter': [25, 0]}, {'name': 'Level Forest - Exit Level Forest 9', 'id': 36, 'area': 14, 'coordinates': [47, 4], 'teleporter': [25, 0]}, {'name': 'Level Forest - Exit Level Forest A', 'id': 37, 'area': 14, 'coordinates': [47, 3], 'teleporter': [25, 0]}, {'name': 'Foresta - Exit Foresta 1', 'id': 38, 'area': 15, 'coordinates': [10, 25], 'teleporter': [31, 0]}, {'name': 'Foresta - Exit Foresta 2', 'id': 39, 'area': 15, 'coordinates': [10, 26], 'teleporter': [31, 0]}, {'name': 'Foresta - Exit Foresta 3', 'id': 40, 'area': 15, 'coordinates': [11, 25], 'teleporter': [31, 0]}, {'name': 'Foresta - Exit Foresta 4', 'id': 41, 'area': 15, 'coordinates': [11, 26], 'teleporter': [31, 0]}, {'name': 'Foresta - Old Man House - Front Door', 'id': 42, 'area': 15, 'coordinates': [25, 17], 'teleporter': [32, 4]}, {'name': 'Foresta - Old Man House - Back Door', 'id': 43, 'area': 15, 'coordinates': [25, 14], 'teleporter': [33, 0]}, {'name': "Foresta - Kaeli's House", 'id': 44, 'area': 15, 'coordinates': [7, 21], 'teleporter': [0, 5]}, {'name': 'Foresta - Rest House', 'id': 45, 'area': 15, 'coordinates': [23, 23], 'teleporter': [1, 5]}, {'name': "Kaeli's House - Kaeli's House Entrance", 'id': 46, 'area': 16, 'coordinates': [11, 20], 'teleporter': [86, 3]}, {'name': "Foresta Houses - Old Man's House - Old Man Front Exit", 'id': 47, 'area': 17, 'coordinates': [35, 44], 'teleporter': [34, 0]}, {'name': "Foresta Houses - Old Man's House - Old Man Back Exit", 'id': 48, 'area': 17, 'coordinates': [35, 27], 'teleporter': [35, 0]}, {'name': 'Foresta - Old Man House - Barrel Tile Script', 'id': 483, 'area': 17, 'coordinates': [35, 30], 'teleporter': [13, 8]}, {'name': 'Foresta Houses - Rest House - Bed Script', 'id': 49, 'area': 17, 'coordinates': [30, 6], 'teleporter': [1, 8]}, {'name': 'Foresta Houses - Rest House - Rest House Exit', 'id': 50, 'area': 17, 'coordinates': [35, 20], 'teleporter': [87, 3]}, {'name': 'Foresta Houses - Libra House - Libra House Script', 'id': 51, 'area': 17, 'coordinates': [8, 49], 'teleporter': [67, 8]}, {'name': 'Foresta Houses - Gemini House - Gemini House Script', 'id': 52, 'area': 17, 'coordinates': [26, 55], 'teleporter': [68, 8]}, {'name': 'Foresta Houses - Mobius House - Mobius House Script', 'id': 53, 'area': 17, 'coordinates': [14, 33], 'teleporter': [69, 8]}, {'name': 'Sand Temple - Sand Temple Entrance', 'id': 54, 'area': 18, 'coordinates': [56, 27], 'teleporter': [36, 0]}, {'name': 'Bone Dungeon 1F - Bone Dungeon Entrance', 'id': 55, 'area': 19, 'coordinates': [13, 60], 'teleporter': [37, 0]}, {'name': 'Bone Dungeon 1F - To Bone Dungeon B1', 'id': 56, 'area': 19, 'coordinates': [13, 39], 'teleporter': [2, 2]}, {'name': 'Bone Dungeon B1 - Waterway - Exit Waterway', 'id': 57, 'area': 20, 'coordinates': [27, 39], 'teleporter': [3, 2]}, {'name': "Bone Dungeon B1 - Waterway - Tristam's Script", 'id': 58, 'area': 20, 'coordinates': [27, 45], 'teleporter': [3, 8]}, {'name': 'Bone Dungeon B1 - Waterway - To Bone Dungeon 1F', 'id': 59, 'area': 20, 'coordinates': [54, 61], 'teleporter': [88, 3]}, {'name': 'Bone Dungeon B1 - Checker Room - Exit Checker Room', 'id': 60, 'area': 20, 'coordinates': [23, 40], 'teleporter': [4, 2]}, {'name': 'Bone Dungeon B1 - Checker Room - To Waterway', 'id': 61, 'area': 20, 'coordinates': [39, 49], 'teleporter': [89, 3]}, {'name': 'Bone Dungeon B1 - Hidden Room - To B2 - Exploding Skull Room', 'id': 62, 'area': 20, 'coordinates': [5, 33], 'teleporter': [91, 3]}, {'name': 'Bonne Dungeon B2 - Exploding Skull Room - To Hidden Passage', 'id': 63, 'area': 21, 'coordinates': [19, 13], 'teleporter': [5, 2]}, {'name': 'Bonne Dungeon B2 - Exploding Skull Room - To Two Skulls Room', 'id': 64, 'area': 21, 'coordinates': [29, 15], 'teleporter': [6, 2]}, {'name': 'Bonne Dungeon B2 - Exploding Skull Room - To Checker Room', 'id': 65, 'area': 21, 'coordinates': [8, 25], 'teleporter': [90, 3]}, {'name': 'Bonne Dungeon B2 - Box Room - To B2 - Two Skulls Room', 'id': 66, 'area': 21, 'coordinates': [59, 12], 'teleporter': [93, 3]}, {'name': 'Bonne Dungeon B2 - Quake Room - To B2 - Two Skulls Room', 'id': 67, 'area': 21, 'coordinates': [59, 28], 'teleporter': [94, 3]}, {'name': 'Bonne Dungeon B2 - Two Skulls Room - To Box Room', 'id': 68, 'area': 21, 'coordinates': [53, 7], 'teleporter': [7, 2]}, {'name': 'Bonne Dungeon B2 - Two Skulls Room - To Quake Room', 'id': 69, 'area': 21, 'coordinates': [41, 3], 'teleporter': [8, 2]}, {'name': 'Bonne Dungeon B2 - Two Skulls Room - To Boss Room', 'id': 70, 'area': 21, 'coordinates': [47, 57], 'teleporter': [9, 2]}, {'name': 'Bonne Dungeon B2 - Two Skulls Room - To B2 - Exploding Skull Room', 'id': 71, 'area': 21, 'coordinates': [54, 23], 'teleporter': [92, 3]}, {'name': 'Bone Dungeon B2 - Boss Room - Flamerus Rex Script', 'id': 72, 'area': 22, 'coordinates': [29, 19], 'teleporter': [4, 8]}, {'name': 'Bone Dungeon B2 - Boss Room - Tristam Leave Script', 'id': 73, 'area': 22, 'coordinates': [29, 23], 'teleporter': [75, 8]}, {'name': 'Bone Dungeon B2 - Boss Room - To B2 - Two Skulls Room', 'id': 74, 'area': 22, 'coordinates': [30, 27], 'teleporter': [95, 3]}, {'name': 'Libra Temple - Entrance', 'id': 75, 'area': 23, 'coordinates': [10, 15], 'teleporter': [13, 6]}, {'name': 'Libra Temple - Libra Tile Script', 'id': 76, 'area': 23, 'coordinates': [9, 8], 'teleporter': [59, 8]}, {'name': 'Aquaria Winter - Winter Entrance 1', 'id': 77, 'area': 24, 'coordinates': [25, 25], 'teleporter': [8, 6]}, {'name': 'Aquaria Winter - Winter Entrance 2', 'id': 78, 'area': 24, 'coordinates': [25, 26], 'teleporter': [8, 6]}, {'name': 'Aquaria Winter - Winter Entrance 3', 'id': 79, 'area': 24, 'coordinates': [26, 25], 'teleporter': [8, 6]}, {'name': 'Aquaria Winter - Winter Entrance 4', 'id': 80, 'area': 24, 'coordinates': [26, 26], 'teleporter': [8, 6]}, {'name': "Aquaria Winter - Winter Phoebe's House Entrance Script", 'id': 81, 'area': 24, 'coordinates': [8, 19], 'teleporter': [10, 5]}, {'name': 'Aquaria Winter - Winter Vendor House Entrance', 'id': 82, 'area': 24, 'coordinates': [8, 5], 'teleporter': [44, 4]}, {'name': 'Aquaria Winter - Winter INN Entrance', 'id': 83, 'area': 24, 'coordinates': [26, 17], 'teleporter': [11, 5]}, {'name': 'Aquaria Summer - Summer Entrance 1', 'id': 84, 'area': 25, 'coordinates': [57, 25], 'teleporter': [8, 6]}, {'name': 'Aquaria Summer - Summer Entrance 2', 'id': 85, 'area': 25, 'coordinates': [57, 26], 'teleporter': [8, 6]}, {'name': 'Aquaria Summer - Summer Entrance 3', 'id': 86, 'area': 25, 'coordinates': [58, 25], 'teleporter': [8, 6]}, {'name': 'Aquaria Summer - Summer Entrance 4', 'id': 87, 'area': 25, 'coordinates': [58, 26], 'teleporter': [8, 6]}, {'name': "Aquaria Summer - Summer Phoebe's House Entrance", 'id': 88, 'area': 25, 'coordinates': [40, 19], 'teleporter': [10, 5]}, {'name': "Aquaria Summer - Spencer's Place Entrance Top", 'id': 89, 'area': 25, 'coordinates': [40, 16], 'teleporter': [42, 0]}, {'name': "Aquaria Summer - Spencer's Place Entrance Side", 'id': 90, 'area': 25, 'coordinates': [41, 18], 'teleporter': [43, 0]}, {'name': 'Aquaria Summer - Summer Vendor House Entrance', 'id': 91, 'area': 25, 'coordinates': [40, 5], 'teleporter': [44, 4]}, {'name': 'Aquaria Summer - Summer INN Entrance', 'id': 92, 'area': 25, 'coordinates': [58, 17], 'teleporter': [11, 5]}, {'name': "Phoebe's House - Entrance", 'id': 93, 'area': 26, 'coordinates': [29, 14], 'teleporter': [5, 8]}, {'name': "Aquaria Vendor House - Vendor House Entrance's Script", 'id': 94, 'area': 27, 'coordinates': [7, 10], 'teleporter': [40, 8]}, {'name': 'Aquaria Vendor House - Vendor House Stairs', 'id': 95, 'area': 27, 'coordinates': [1, 4], 'teleporter': [47, 0]}, {'name': 'Aquaria Gemini Room - Gemini Script', 'id': 96, 'area': 27, 'coordinates': [2, 40], 'teleporter': [72, 8]}, {'name': 'Aquaria Gemini Room - Gemini Room Stairs', 'id': 97, 'area': 27, 'coordinates': [4, 39], 'teleporter': [48, 0]}, {'name': 'Aquaria INN - Aquaria INN entrance', 'id': 98, 'area': 27, 'coordinates': [51, 46], 'teleporter': [75, 8]}, {'name': 'Wintry Cave 1F - Main Entrance', 'id': 99, 'area': 28, 'coordinates': [50, 58], 'teleporter': [49, 0]}, {'name': 'Wintry Cave 1F - To 3F Top', 'id': 100, 'area': 28, 'coordinates': [40, 25], 'teleporter': [14, 2]}, {'name': 'Wintry Cave 1F - To 2F', 'id': 101, 'area': 28, 'coordinates': [10, 43], 'teleporter': [15, 2]}, {'name': "Wintry Cave 1F - Phoebe's Script", 'id': 102, 'area': 28, 'coordinates': [44, 37], 'teleporter': [6, 8]}, {'name': 'Wintry Cave 2F - To 3F Bottom', 'id': 103, 'area': 29, 'coordinates': [58, 5], 'teleporter': [50, 0]}, {'name': 'Wintry Cave 2F - To 1F', 'id': 104, 'area': 29, 'coordinates': [38, 18], 'teleporter': [97, 3]}, {'name': 'Wintry Cave 3F Top - Exit from 3F Top', 'id': 105, 'area': 30, 'coordinates': [24, 6], 'teleporter': [96, 3]}, {'name': 'Wintry Cave 3F Bottom - Exit to 2F', 'id': 106, 'area': 31, 'coordinates': [4, 29], 'teleporter': [51, 0]}, {'name': 'Life Temple - Entrance', 'id': 107, 'area': 32, 'coordinates': [9, 60], 'teleporter': [14, 6]}, {'name': 'Life Temple - Libra Tile Script', 'id': 108, 'area': 32, 'coordinates': [3, 55], 'teleporter': [60, 8]}, {'name': 'Life Temple - Mysterious Man Script', 'id': 109, 'area': 32, 'coordinates': [9, 44], 'teleporter': [78, 8]}, {'name': 'Fall Basin - Back Exit Script', 'id': 110, 'area': 33, 'coordinates': [17, 5], 'teleporter': [9, 0]}, {'name': 'Fall Basin - Main Exit', 'id': 111, 'area': 33, 'coordinates': [15, 26], 'teleporter': [53, 0]}, {'name': "Fall Basin - Phoebe's Script", 'id': 112, 'area': 33, 'coordinates': [17, 6], 'teleporter': [9, 8]}, {'name': 'Ice Pyramid B1 Taunt Room - To Climbing Wall Room', 'id': 113, 'area': 34, 'coordinates': [43, 6], 'teleporter': [55, 0]}, {'name': 'Ice Pyramid 1F Maze - Main Entrance 1', 'id': 114, 'area': 35, 'coordinates': [18, 36], 'teleporter': [56, 0]}, {'name': 'Ice Pyramid 1F Maze - Main Entrance 2', 'id': 115, 'area': 35, 'coordinates': [19, 36], 'teleporter': [56, 0]}, {'name': 'Ice Pyramid 1F Maze - West Stairs To 2F South Tiled Room', 'id': 116, 'area': 35, 'coordinates': [3, 27], 'teleporter': [57, 0]}, {'name': 'Ice Pyramid 1F Maze - West Center Stairs to 2F West Room', 'id': 117, 'area': 35, 'coordinates': [11, 15], 'teleporter': [58, 0]}, {'name': 'Ice Pyramid 1F Maze - East Center Stairs to 2F Center Room', 'id': 118, 'area': 35, 'coordinates': [25, 16], 'teleporter': [59, 0]}, {'name': 'Ice Pyramid 1F Maze - Upper Stairs to 2F Small North Room', 'id': 119, 'area': 35, 'coordinates': [31, 1], 'teleporter': [60, 0]}, {'name': 'Ice Pyramid 1F Maze - East Stairs to 2F North Corridor', 'id': 120, 'area': 35, 'coordinates': [34, 9], 'teleporter': [61, 0]}, {'name': "Ice Pyramid 1F Maze - Statue's Script", 'id': 121, 'area': 35, 'coordinates': [21, 32], 'teleporter': [77, 8]}, {'name': 'Ice Pyramid 2F South Tiled Room - To 1F', 'id': 122, 'area': 36, 'coordinates': [4, 26], 'teleporter': [62, 0]}, {'name': 'Ice Pyramid 2F South Tiled Room - To 3F Two Boxes Room', 'id': 123, 'area': 36, 'coordinates': [22, 17], 'teleporter': [67, 0]}, {'name': 'Ice Pyramid 2F West Room - To 1F', 'id': 124, 'area': 36, 'coordinates': [9, 10], 'teleporter': [63, 0]}, {'name': 'Ice Pyramid 2F Center Room - To 1F', 'id': 125, 'area': 36, 'coordinates': [22, 14], 'teleporter': [64, 0]}, {'name': 'Ice Pyramid 2F Small North Room - To 1F', 'id': 126, 'area': 36, 'coordinates': [26, 4], 'teleporter': [65, 0]}, {'name': 'Ice Pyramid 2F North Corridor - To 1F', 'id': 127, 'area': 36, 'coordinates': [32, 8], 'teleporter': [66, 0]}, {'name': 'Ice Pyramid 2F North Corridor - To 3F Main Loop', 'id': 128, 'area': 36, 'coordinates': [12, 7], 'teleporter': [68, 0]}, {'name': 'Ice Pyramid 3F Two Boxes Room - To 2F South Tiled Room', 'id': 129, 'area': 37, 'coordinates': [24, 54], 'teleporter': [69, 0]}, {'name': 'Ice Pyramid 3F Main Loop - To 2F Corridor', 'id': 130, 'area': 37, 'coordinates': [16, 45], 'teleporter': [70, 0]}, {'name': 'Ice Pyramid 3F Main Loop - To 4F', 'id': 131, 'area': 37, 'coordinates': [19, 43], 'teleporter': [71, 0]}, {'name': 'Ice Pyramid 4F Treasure Room - To 3F Main Loop', 'id': 132, 'area': 38, 'coordinates': [52, 5], 'teleporter': [72, 0]}, {'name': 'Ice Pyramid 4F Treasure Room - To 5F Leap of Faith Room', 'id': 133, 'area': 38, 'coordinates': [62, 19], 'teleporter': [73, 0]}, {'name': 'Ice Pyramid 5F Leap of Faith Room - To 4F Treasure Room', 'id': 134, 'area': 39, 'coordinates': [54, 63], 'teleporter': [74, 0]}, {'name': 'Ice Pyramid 5F Leap of Faith Room - Bombed Ice Plate', 'id': 135, 'area': 39, 'coordinates': [47, 54], 'teleporter': [77, 8]}, {'name': 'Ice Pyramid 5F Stairs to Ice Golem - To Ice Golem Room', 'id': 136, 'area': 39, 'coordinates': [39, 43], 'teleporter': [75, 0]}, {'name': 'Ice Pyramid 5F Stairs to Ice Golem - To Climbing Wall Room', 'id': 137, 'area': 39, 'coordinates': [39, 60], 'teleporter': [76, 0]}, {'name': 'Ice Pyramid - Duplicate Ice Golem Room', 'id': 138, 'area': 40, 'coordinates': [44, 43], 'teleporter': [77, 0]}, {'name': 'Ice Pyramid Climbing Wall Room - To Taunt Room', 'id': 139, 'area': 41, 'coordinates': [4, 59], 'teleporter': [78, 0]}, {'name': 'Ice Pyramid Climbing Wall Room - To 5F Stairs', 'id': 140, 'area': 41, 'coordinates': [4, 45], 'teleporter': [79, 0]}, {'name': 'Ice Pyramid Ice Golem Room - To 5F Stairs', 'id': 141, 'area': 42, 'coordinates': [44, 43], 'teleporter': [80, 0]}, {'name': 'Ice Pyramid Ice Golem Room - Ice Golem Script', 'id': 142, 'area': 42, 'coordinates': [53, 32], 'teleporter': [10, 8]}, {'name': 'Spencer Waterfall - To Spencer Cave', 'id': 143, 'area': 43, 'coordinates': [48, 57], 'teleporter': [81, 0]}, {'name': 'Spencer Waterfall - Upper Exit to Aquaria 1', 'id': 144, 'area': 43, 'coordinates': [40, 5], 'teleporter': [82, 0]}, {'name': 'Spencer Waterfall - Upper Exit to Aquaria 2', 'id': 145, 'area': 43, 'coordinates': [40, 6], 'teleporter': [82, 0]}, {'name': 'Spencer Waterfall - Upper Exit to Aquaria 3', 'id': 146, 'area': 43, 'coordinates': [41, 5], 'teleporter': [82, 0]}, {'name': 'Spencer Waterfall - Upper Exit to Aquaria 4', 'id': 147, 'area': 43, 'coordinates': [41, 6], 'teleporter': [82, 0]}, {'name': 'Spencer Waterfall - Right Exit to Aquaria 1', 'id': 148, 'area': 43, 'coordinates': [46, 8], 'teleporter': [83, 0]}, {'name': 'Spencer Waterfall - Right Exit to Aquaria 2', 'id': 149, 'area': 43, 'coordinates': [47, 8], 'teleporter': [83, 0]}, {'name': 'Spencer Cave Normal Main - To Waterfall', 'id': 150, 'area': 44, 'coordinates': [14, 39], 'teleporter': [85, 0]}, {'name': 'Spencer Cave Normal From Overworld - Exit to Overworld', 'id': 151, 'area': 44, 'coordinates': [15, 57], 'teleporter': [7, 6]}, {'name': 'Spencer Cave Unplug - Exit to Overworld', 'id': 152, 'area': 45, 'coordinates': [40, 29], 'teleporter': [7, 6]}, {'name': 'Spencer Cave Unplug - Libra Teleporter Start Script', 'id': 153, 'area': 45, 'coordinates': [28, 21], 'teleporter': [33, 8]}, {'name': 'Spencer Cave Unplug - Libra Teleporter End Script', 'id': 154, 'area': 45, 'coordinates': [46, 4], 'teleporter': [34, 8]}, {'name': 'Spencer Cave Unplug - Mobius Teleporter Chest Script', 'id': 155, 'area': 45, 'coordinates': [21, 9], 'teleporter': [35, 8]}, {'name': 'Spencer Cave Unplug - Mobius Teleporter Start Script', 'id': 156, 'area': 45, 'coordinates': [29, 28], 'teleporter': [36, 8]}, {'name': 'Wintry Temple Outer Room - Main Entrance', 'id': 157, 'area': 46, 'coordinates': [8, 31], 'teleporter': [15, 6]}, {'name': 'Wintry Temple Inner Room - Gemini Tile to Sealed temple', 'id': 158, 'area': 46, 'coordinates': [9, 24], 'teleporter': [62, 8]}, {'name': 'Fireburg - To Overworld', 'id': 159, 'area': 47, 'coordinates': [4, 13], 'teleporter': [9, 6]}, {'name': 'Fireburg - To Overworld', 'id': 160, 'area': 47, 'coordinates': [5, 13], 'teleporter': [9, 6]}, {'name': 'Fireburg - To Overworld', 'id': 161, 'area': 47, 'coordinates': [28, 15], 'teleporter': [9, 6]}, {'name': 'Fireburg - To Overworld', 'id': 162, 'area': 47, 'coordinates': [27, 15], 'teleporter': [9, 6]}, {'name': 'Fireburg - Vendor House', 'id': 163, 'area': 47, 'coordinates': [10, 24], 'teleporter': [91, 0]}, {'name': 'Fireburg - Reuben House', 'id': 164, 'area': 47, 'coordinates': [14, 6], 'teleporter': [98, 8]}, {'name': 'Fireburg - Hotel', 'id': 165, 'area': 47, 'coordinates': [20, 8], 'teleporter': [96, 8]}, {'name': 'Fireburg - GrenadeMan House Script', 'id': 166, 'area': 47, 'coordinates': [12, 18], 'teleporter': [11, 8]}, {'name': 'Reuben House - Main Entrance', 'id': 167, 'area': 48, 'coordinates': [33, 46], 'teleporter': [98, 3]}, {'name': 'GrenadeMan House - Entrance Script', 'id': 168, 'area': 49, 'coordinates': [55, 60], 'teleporter': [9, 8]}, {'name': 'GrenadeMan House - To Mobius Crest Room', 'id': 169, 'area': 49, 'coordinates': [57, 52], 'teleporter': [93, 0]}, {'name': 'GrenadeMan Mobius Room - Stairs to House', 'id': 170, 'area': 49, 'coordinates': [39, 26], 'teleporter': [94, 0]}, {'name': 'GrenadeMan Mobius Room - Mobius Teleporter Script', 'id': 171, 'area': 49, 'coordinates': [39, 23], 'teleporter': [54, 8]}, {'name': 'Fireburg Vendor House - Entrance Script', 'id': 172, 'area': 49, 'coordinates': [7, 10], 'teleporter': [95, 0]}, {'name': 'Fireburg Vendor House - Stairs to Gemini Room', 'id': 173, 'area': 49, 'coordinates': [1, 4], 'teleporter': [96, 0]}, {'name': 'Fireburg Gemini Room - Stairs to Vendor House', 'id': 174, 'area': 49, 'coordinates': [4, 39], 'teleporter': [97, 0]}, {'name': 'Fireburg Gemini Room - Gemini Teleporter Script', 'id': 175, 'area': 49, 'coordinates': [2, 40], 'teleporter': [45, 8]}, {'name': 'Fireburg Hotel Lobby - Stairs to beds', 'id': 176, 'area': 49, 'coordinates': [4, 50], 'teleporter': [213, 0]}, {'name': 'Fireburg Hotel Lobby - Entrance', 'id': 177, 'area': 49, 'coordinates': [17, 56], 'teleporter': [99, 3]}, {'name': 'Fireburg Hotel Beds - Stairs to Hotel Lobby', 'id': 178, 'area': 49, 'coordinates': [45, 59], 'teleporter': [214, 0]}, {'name': 'Mine Exterior - Main Entrance', 'id': 179, 'area': 50, 'coordinates': [5, 28], 'teleporter': [98, 0]}, {'name': 'Mine Exterior - To Cliff', 'id': 180, 'area': 50, 'coordinates': [58, 29], 'teleporter': [99, 0]}, {'name': 'Mine Exterior - To Parallel Room', 'id': 181, 'area': 50, 'coordinates': [8, 7], 'teleporter': [20, 2]}, {'name': 'Mine Exterior - To Crescent Room', 'id': 182, 'area': 50, 'coordinates': [26, 15], 'teleporter': [21, 2]}, {'name': 'Mine Exterior - To Climbing Room', 'id': 183, 'area': 50, 'coordinates': [21, 35], 'teleporter': [22, 2]}, {'name': 'Mine Exterior - Jinn Fight Script', 'id': 184, 'area': 50, 'coordinates': [58, 31], 'teleporter': [74, 8]}, {'name': 'Mine Parallel Room - To Mine Exterior', 'id': 185, 'area': 51, 'coordinates': [7, 60], 'teleporter': [100, 3]}, {'name': 'Mine Crescent Room - To Mine Exterior', 'id': 186, 'area': 51, 'coordinates': [22, 61], 'teleporter': [101, 3]}, {'name': 'Mine Climbing Room - To Mine Exterior', 'id': 187, 'area': 51, 'coordinates': [56, 21], 'teleporter': [102, 3]}, {'name': 'Mine Cliff - Entrance', 'id': 188, 'area': 52, 'coordinates': [9, 5], 'teleporter': [100, 0]}, {'name': 'Mine Cliff - Reuben Grenade Script', 'id': 189, 'area': 52, 'coordinates': [15, 7], 'teleporter': [12, 8]}, {'name': 'Sealed Temple - To Overworld', 'id': 190, 'area': 53, 'coordinates': [58, 43], 'teleporter': [16, 6]}, {'name': 'Sealed Temple - Gemini Tile Script', 'id': 191, 'area': 53, 'coordinates': [56, 38], 'teleporter': [63, 8]}, {'name': 'Volcano Base - Main Entrance 1', 'id': 192, 'area': 54, 'coordinates': [23, 25], 'teleporter': [103, 0]}, {'name': 'Volcano Base - Main Entrance 2', 'id': 193, 'area': 54, 'coordinates': [23, 26], 'teleporter': [103, 0]}, {'name': 'Volcano Base - Main Entrance 3', 'id': 194, 'area': 54, 'coordinates': [24, 25], 'teleporter': [103, 0]}, {'name': 'Volcano Base - Main Entrance 4', 'id': 195, 'area': 54, 'coordinates': [24, 26], 'teleporter': [103, 0]}, {'name': 'Volcano Base - Left Stairs Script', 'id': 196, 'area': 54, 'coordinates': [20, 5], 'teleporter': [31, 8]}, {'name': 'Volcano Base - Right Stairs Script', 'id': 197, 'area': 54, 'coordinates': [32, 5], 'teleporter': [30, 8]}, {'name': 'Volcano Top Right - Top Exit', 'id': 198, 'area': 55, 'coordinates': [44, 8], 'teleporter': [9, 0]}, {'name': 'Volcano Top Left - To Right-Left Path Script', 'id': 199, 'area': 55, 'coordinates': [40, 24], 'teleporter': [26, 8]}, {'name': 'Volcano Top Right - To Left-Right Path Script', 'id': 200, 'area': 55, 'coordinates': [52, 24], 'teleporter': [79, 8]}, {'name': 'Volcano Right Path - To Volcano Base Script', 'id': 201, 'area': 56, 'coordinates': [48, 42], 'teleporter': [15, 8]}, {'name': 'Volcano Left Path - To Volcano Cross Left-Right', 'id': 202, 'area': 56, 'coordinates': [40, 31], 'teleporter': [25, 2]}, {'name': 'Volcano Left Path - To Volcano Cross Right-Left', 'id': 203, 'area': 56, 'coordinates': [52, 29], 'teleporter': [26, 2]}, {'name': 'Volcano Left Path - To Volcano Base Script', 'id': 204, 'area': 56, 'coordinates': [36, 42], 'teleporter': [27, 8]}, {'name': 'Volcano Cross Left-Right - To Volcano Left Path', 'id': 205, 'area': 56, 'coordinates': [10, 42], 'teleporter': [103, 3]}, {'name': 'Volcano Cross Left-Right - To Volcano Top Right Script', 'id': 206, 'area': 56, 'coordinates': [16, 24], 'teleporter': [29, 8]}, {'name': 'Volcano Cross Right-Left - To Volcano Top Left Script', 'id': 207, 'area': 56, 'coordinates': [8, 22], 'teleporter': [28, 8]}, {'name': 'Volcano Cross Right-Left - To Volcano Left Path', 'id': 208, 'area': 56, 'coordinates': [16, 42], 'teleporter': [104, 3]}, {'name': 'Lava Dome Inner Ring Main Loop - Main Entrance 1', 'id': 209, 'area': 57, 'coordinates': [32, 5], 'teleporter': [104, 0]}, {'name': 'Lava Dome Inner Ring Main Loop - Main Entrance 2', 'id': 210, 'area': 57, 'coordinates': [33, 5], 'teleporter': [104, 0]}, {'name': 'Lava Dome Inner Ring Main Loop - To Three Steps Room', 'id': 211, 'area': 57, 'coordinates': [14, 5], 'teleporter': [105, 0]}, {'name': 'Lava Dome Inner Ring Main Loop - To Life Chest Room Lower', 'id': 212, 'area': 57, 'coordinates': [40, 17], 'teleporter': [106, 0]}, {'name': 'Lava Dome Inner Ring Main Loop - To Big Jump Room Left', 'id': 213, 'area': 57, 'coordinates': [8, 11], 'teleporter': [108, 0]}, {'name': 'Lava Dome Inner Ring Main Loop - To Split Corridor Room', 'id': 214, 'area': 57, 'coordinates': [11, 19], 'teleporter': [111, 0]}, {'name': 'Lava Dome Inner Ring Center Ledge - To Life Chest Room Higher', 'id': 215, 'area': 57, 'coordinates': [32, 11], 'teleporter': [107, 0]}, {'name': 'Lava Dome Inner Ring Plate Ledge - To Plate Corridor', 'id': 216, 'area': 57, 'coordinates': [12, 23], 'teleporter': [109, 0]}, {'name': 'Lava Dome Inner Ring Plate Ledge - Plate Script', 'id': 217, 'area': 57, 'coordinates': [5, 23], 'teleporter': [47, 8]}, {'name': 'Lava Dome Inner Ring Upper Ledges - To Pointless Room', 'id': 218, 'area': 57, 'coordinates': [0, 9], 'teleporter': [110, 0]}, {'name': 'Lava Dome Inner Ring Upper Ledges - To Lower Moon Helm Room', 'id': 219, 'area': 57, 'coordinates': [0, 15], 'teleporter': [112, 0]}, {'name': 'Lava Dome Inner Ring Upper Ledges - To Up-Down Corridor', 'id': 220, 'area': 57, 'coordinates': [54, 5], 'teleporter': [113, 0]}, {'name': 'Lava Dome Inner Ring Big Door Ledge - To Jumping Maze II', 'id': 221, 'area': 57, 'coordinates': [54, 21], 'teleporter': [114, 0]}, {'name': 'Lava Dome Inner Ring Big Door Ledge - Hydra Gate 1', 'id': 222, 'area': 57, 'coordinates': [62, 20], 'teleporter': [29, 2]}, {'name': 'Lava Dome Inner Ring Big Door Ledge - Hydra Gate 2', 'id': 223, 'area': 57, 'coordinates': [63, 20], 'teleporter': [29, 2]}, {'name': 'Lava Dome Inner Ring Big Door Ledge - Hydra Gate 3', 'id': 224, 'area': 57, 'coordinates': [62, 21], 'teleporter': [29, 2]}, {'name': 'Lava Dome Inner Ring Big Door Ledge - Hydra Gate 4', 'id': 225, 'area': 57, 'coordinates': [63, 21], 'teleporter': [29, 2]}, {'name': 'Lava Dome Inner Ring Tiny Bottom Ledge - To Four Boxes Corridor', 'id': 226, 'area': 57, 'coordinates': [50, 25], 'teleporter': [115, 0]}, {'name': 'Lava Dome Jump Maze II - Lower Right Entrance', 'id': 227, 'area': 58, 'coordinates': [55, 28], 'teleporter': [116, 0]}, {'name': 'Lava Dome Jump Maze II - Upper Entrance', 'id': 228, 'area': 58, 'coordinates': [35, 3], 'teleporter': [119, 0]}, {'name': 'Lava Dome Jump Maze II - Lower Left Entrance', 'id': 229, 'area': 58, 'coordinates': [34, 27], 'teleporter': [120, 0]}, {'name': 'Lava Dome Up-Down Corridor - Upper Entrance', 'id': 230, 'area': 58, 'coordinates': [29, 8], 'teleporter': [117, 0]}, {'name': 'Lava Dome Up-Down Corridor - Lower Entrance', 'id': 231, 'area': 58, 'coordinates': [28, 25], 'teleporter': [118, 0]}, {'name': 'Lava Dome Jump Maze I - South Entrance', 'id': 232, 'area': 59, 'coordinates': [20, 27], 'teleporter': [121, 0]}, {'name': 'Lava Dome Jump Maze I - North Entrance', 'id': 233, 'area': 59, 'coordinates': [7, 3], 'teleporter': [122, 0]}, {'name': 'Lava Dome Pointless Room - Entrance', 'id': 234, 'area': 60, 'coordinates': [2, 7], 'teleporter': [123, 0]}, {'name': 'Lava Dome Pointless Room - Visit Quest Script 1', 'id': 490, 'area': 60, 'coordinates': [4, 4], 'teleporter': [99, 8]}, {'name': 'Lava Dome Pointless Room - Visit Quest Script 2', 'id': 491, 'area': 60, 'coordinates': [4, 5], 'teleporter': [99, 8]}, {'name': 'Lava Dome Lower Moon Helm Room - Left Entrance', 'id': 235, 'area': 60, 'coordinates': [2, 19], 'teleporter': [124, 0]}, {'name': 'Lava Dome Lower Moon Helm Room - Right Entrance', 'id': 236, 'area': 60, 'coordinates': [11, 21], 'teleporter': [125, 0]}, {'name': 'Lava Dome Moon Helm Room - Entrance', 'id': 237, 'area': 60, 'coordinates': [15, 23], 'teleporter': [126, 0]}, {'name': 'Lava Dome Three Jumps Room - To Main Loop', 'id': 238, 'area': 61, 'coordinates': [58, 15], 'teleporter': [127, 0]}, {'name': 'Lava Dome Life Chest Room - Lower South Entrance', 'id': 239, 'area': 61, 'coordinates': [38, 27], 'teleporter': [128, 0]}, {'name': 'Lava Dome Life Chest Room - Upper South Entrance', 'id': 240, 'area': 61, 'coordinates': [28, 23], 'teleporter': [129, 0]}, {'name': 'Lava Dome Big Jump Room - Left Entrance', 'id': 241, 'area': 62, 'coordinates': [42, 51], 'teleporter': [133, 0]}, {'name': 'Lava Dome Big Jump Room - North Entrance', 'id': 242, 'area': 62, 'coordinates': [30, 29], 'teleporter': [131, 0]}, {'name': 'Lava Dome Big Jump Room - Lower Right Stairs', 'id': 243, 'area': 62, 'coordinates': [61, 59], 'teleporter': [132, 0]}, {'name': 'Lava Dome Split Corridor - Upper Stairs', 'id': 244, 'area': 62, 'coordinates': [30, 43], 'teleporter': [130, 0]}, {'name': 'Lava Dome Split Corridor - Lower Stairs', 'id': 245, 'area': 62, 'coordinates': [36, 61], 'teleporter': [134, 0]}, {'name': 'Lava Dome Plate Corridor - Right Entrance', 'id': 246, 'area': 63, 'coordinates': [19, 29], 'teleporter': [135, 0]}, {'name': 'Lava Dome Plate Corridor - Left Entrance', 'id': 247, 'area': 63, 'coordinates': [60, 21], 'teleporter': [137, 0]}, {'name': 'Lava Dome Four Boxes Stairs - Upper Entrance', 'id': 248, 'area': 63, 'coordinates': [22, 3], 'teleporter': [136, 0]}, {'name': 'Lava Dome Four Boxes Stairs - Lower Entrance', 'id': 249, 'area': 63, 'coordinates': [22, 17], 'teleporter': [16, 0]}, {'name': 'Lava Dome Hydra Room - South Entrance', 'id': 250, 'area': 64, 'coordinates': [14, 59], 'teleporter': [105, 3]}, {'name': 'Lava Dome Hydra Room - North Exit', 'id': 251, 'area': 64, 'coordinates': [25, 31], 'teleporter': [138, 0]}, {'name': 'Lava Dome Hydra Room - Hydra Script', 'id': 252, 'area': 64, 'coordinates': [14, 36], 'teleporter': [14, 8]}, {'name': 'Lava Dome Escape Corridor - South Entrance', 'id': 253, 'area': 65, 'coordinates': [22, 17], 'teleporter': [139, 0]}, {'name': 'Lava Dome Escape Corridor - North Entrance', 'id': 254, 'area': 65, 'coordinates': [22, 3], 'teleporter': [9, 0]}, {'name': 'Rope Bridge - West Entrance 1', 'id': 255, 'area': 66, 'coordinates': [3, 10], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - West Entrance 2', 'id': 256, 'area': 66, 'coordinates': [3, 11], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - West Entrance 3', 'id': 257, 'area': 66, 'coordinates': [3, 12], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - West Entrance 4', 'id': 258, 'area': 66, 'coordinates': [3, 13], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - West Entrance 5', 'id': 259, 'area': 66, 'coordinates': [4, 10], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - West Entrance 6', 'id': 260, 'area': 66, 'coordinates': [4, 11], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - West Entrance 7', 'id': 261, 'area': 66, 'coordinates': [4, 12], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - West Entrance 8', 'id': 262, 'area': 66, 'coordinates': [4, 13], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - East Entrance 1', 'id': 263, 'area': 66, 'coordinates': [59, 10], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - East Entrance 2', 'id': 264, 'area': 66, 'coordinates': [59, 11], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - East Entrance 3', 'id': 265, 'area': 66, 'coordinates': [59, 12], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - East Entrance 4', 'id': 266, 'area': 66, 'coordinates': [59, 13], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - East Entrance 5', 'id': 267, 'area': 66, 'coordinates': [60, 10], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - East Entrance 6', 'id': 268, 'area': 66, 'coordinates': [60, 11], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - East Entrance 7', 'id': 269, 'area': 66, 'coordinates': [60, 12], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - East Entrance 8', 'id': 270, 'area': 66, 'coordinates': [60, 13], 'teleporter': [140, 0]}, {'name': 'Rope Bridge - Reuben Fall Script', 'id': 271, 'area': 66, 'coordinates': [13, 12], 'teleporter': [15, 8]}, {'name': 'Alive Forest - West Entrance 1', 'id': 272, 'area': 67, 'coordinates': [8, 13], 'teleporter': [142, 0]}, {'name': 'Alive Forest - West Entrance 2', 'id': 273, 'area': 67, 'coordinates': [9, 13], 'teleporter': [142, 0]}, {'name': 'Alive Forest - Giant Tree Entrance', 'id': 274, 'area': 67, 'coordinates': [42, 42], 'teleporter': [143, 0]}, {'name': 'Alive Forest - Libra Teleporter Script', 'id': 275, 'area': 67, 'coordinates': [8, 52], 'teleporter': [64, 8]}, {'name': 'Alive Forest - Gemini Teleporter Script', 'id': 276, 'area': 67, 'coordinates': [57, 49], 'teleporter': [65, 8]}, {'name': 'Alive Forest - Mobius Teleporter Script', 'id': 277, 'area': 67, 'coordinates': [24, 10], 'teleporter': [66, 8]}, {'name': 'Giant Tree 1F - Entrance Script 1', 'id': 278, 'area': 68, 'coordinates': [18, 31], 'teleporter': [56, 1]}, {'name': 'Giant Tree 1F - Entrance Script 2', 'id': 279, 'area': 68, 'coordinates': [19, 31], 'teleporter': [56, 1]}, {'name': 'Giant Tree 1F - North Entrance To 2F', 'id': 280, 'area': 68, 'coordinates': [16, 1], 'teleporter': [144, 0]}, {'name': 'Giant Tree 2F Main Lobby - North Entrance to 1F', 'id': 281, 'area': 69, 'coordinates': [44, 33], 'teleporter': [145, 0]}, {'name': 'Giant Tree 2F Main Lobby - Central Entrance to 3F', 'id': 282, 'area': 69, 'coordinates': [42, 47], 'teleporter': [146, 0]}, {'name': 'Giant Tree 2F Main Lobby - West Entrance to Mushroom Room', 'id': 283, 'area': 69, 'coordinates': [58, 49], 'teleporter': [149, 0]}, {'name': 'Giant Tree 2F West Ledge - To 3F Northwest Ledge', 'id': 284, 'area': 69, 'coordinates': [34, 37], 'teleporter': [147, 0]}, {'name': 'Giant Tree 2F Fall From Vine Script', 'id': 482, 'area': 69, 'coordinates': [46, 51], 'teleporter': [76, 8]}, {'name': 'Giant Tree Meteor Chest Room - To 2F Mushroom Room', 'id': 285, 'area': 69, 'coordinates': [58, 44], 'teleporter': [148, 0]}, {'name': 'Giant Tree 2F Mushroom Room - Entrance', 'id': 286, 'area': 70, 'coordinates': [55, 18], 'teleporter': [150, 0]}, {'name': 'Giant Tree 2F Mushroom Room - North Face to Meteor', 'id': 287, 'area': 70, 'coordinates': [56, 7], 'teleporter': [151, 0]}, {'name': 'Giant Tree 3F Central Room - Central Entrance to 2F', 'id': 288, 'area': 71, 'coordinates': [46, 53], 'teleporter': [152, 0]}, {'name': 'Giant Tree 3F Central Room - East Entrance to Worm Room', 'id': 289, 'area': 71, 'coordinates': [58, 39], 'teleporter': [153, 0]}, {'name': 'Giant Tree 3F Lower Corridor - Entrance from Worm Room', 'id': 290, 'area': 71, 'coordinates': [45, 39], 'teleporter': [154, 0]}, {'name': 'Giant Tree 3F West Platform - Lower Entrance', 'id': 291, 'area': 71, 'coordinates': [33, 43], 'teleporter': [155, 0]}, {'name': 'Giant Tree 3F West Platform - Top Entrance', 'id': 292, 'area': 71, 'coordinates': [52, 25], 'teleporter': [156, 0]}, {'name': 'Giant Tree Worm Room - East Entrance', 'id': 293, 'area': 72, 'coordinates': [20, 58], 'teleporter': [157, 0]}, {'name': 'Giant Tree Worm Room - West Entrance', 'id': 294, 'area': 72, 'coordinates': [6, 56], 'teleporter': [158, 0]}, {'name': 'Giant Tree 4F Lower Floor - Entrance', 'id': 295, 'area': 73, 'coordinates': [20, 7], 'teleporter': [159, 0]}, {'name': 'Giant Tree 4F Lower Floor - Lower West Mouth', 'id': 296, 'area': 73, 'coordinates': [8, 23], 'teleporter': [160, 0]}, {'name': 'Giant Tree 4F Lower Floor - Lower Central Mouth', 'id': 297, 'area': 73, 'coordinates': [14, 25], 'teleporter': [161, 0]}, {'name': 'Giant Tree 4F Lower Floor - Lower East Mouth', 'id': 298, 'area': 73, 'coordinates': [20, 25], 'teleporter': [162, 0]}, {'name': 'Giant Tree 4F Upper Floor - Upper West Mouth', 'id': 299, 'area': 73, 'coordinates': [8, 19], 'teleporter': [163, 0]}, {'name': 'Giant Tree 4F Upper Floor - Upper Central Mouth', 'id': 300, 'area': 73, 'coordinates': [12, 17], 'teleporter': [164, 0]}, {'name': 'Giant Tree 4F Slime Room - Exit', 'id': 301, 'area': 74, 'coordinates': [47, 10], 'teleporter': [165, 0]}, {'name': 'Giant Tree 4F Slime Room - West Entrance', 'id': 302, 'area': 74, 'coordinates': [45, 24], 'teleporter': [166, 0]}, {'name': 'Giant Tree 4F Slime Room - Central Entrance', 'id': 303, 'area': 74, 'coordinates': [50, 24], 'teleporter': [167, 0]}, {'name': 'Giant Tree 4F Slime Room - East Entrance', 'id': 304, 'area': 74, 'coordinates': [57, 28], 'teleporter': [168, 0]}, {'name': 'Giant Tree 5F - Entrance', 'id': 305, 'area': 75, 'coordinates': [14, 51], 'teleporter': [169, 0]}, {'name': 'Giant Tree 5F - Giant Tree Face', 'id': 306, 'area': 75, 'coordinates': [14, 37], 'teleporter': [170, 0]}, {'name': 'Kaidge Temple - Entrance', 'id': 307, 'area': 77, 'coordinates': [44, 63], 'teleporter': [18, 6]}, {'name': 'Kaidge Temple - Mobius Teleporter Script', 'id': 308, 'area': 77, 'coordinates': [35, 57], 'teleporter': [71, 8]}, {'name': 'Windhole Temple - Entrance', 'id': 309, 'area': 78, 'coordinates': [10, 29], 'teleporter': [173, 0]}, {'name': 'Mount Gale - Entrance 1', 'id': 310, 'area': 79, 'coordinates': [1, 45], 'teleporter': [174, 0]}, {'name': 'Mount Gale - Entrance 2', 'id': 311, 'area': 79, 'coordinates': [2, 45], 'teleporter': [174, 0]}, {'name': 'Mount Gale - Visit Quest', 'id': 494, 'area': 79, 'coordinates': [44, 7], 'teleporter': [101, 8]}, {'name': 'Windia - Main Entrance 1', 'id': 312, 'area': 80, 'coordinates': [12, 40], 'teleporter': [10, 6]}, {'name': 'Windia - Main Entrance 2', 'id': 313, 'area': 80, 'coordinates': [13, 40], 'teleporter': [10, 6]}, {'name': 'Windia - Main Entrance 3', 'id': 314, 'area': 80, 'coordinates': [14, 40], 'teleporter': [10, 6]}, {'name': 'Windia - Main Entrance 4', 'id': 315, 'area': 80, 'coordinates': [15, 40], 'teleporter': [10, 6]}, {'name': 'Windia - Main Entrance 5', 'id': 316, 'area': 80, 'coordinates': [12, 41], 'teleporter': [10, 6]}, {'name': 'Windia - Main Entrance 6', 'id': 317, 'area': 80, 'coordinates': [13, 41], 'teleporter': [10, 6]}, {'name': 'Windia - Main Entrance 7', 'id': 318, 'area': 80, 'coordinates': [14, 41], 'teleporter': [10, 6]}, {'name': 'Windia - Main Entrance 8', 'id': 319, 'area': 80, 'coordinates': [15, 41], 'teleporter': [10, 6]}, {'name': "Windia - Otto's House", 'id': 320, 'area': 80, 'coordinates': [21, 39], 'teleporter': [30, 5]}, {'name': "Windia - INN's Script", 'id': 321, 'area': 80, 'coordinates': [18, 34], 'teleporter': [97, 8]}, {'name': 'Windia - Vendor House', 'id': 322, 'area': 80, 'coordinates': [8, 36], 'teleporter': [32, 5]}, {'name': 'Windia - Kid House', 'id': 323, 'area': 80, 'coordinates': [7, 23], 'teleporter': [176, 4]}, {'name': 'Windia - Old People House', 'id': 324, 'area': 80, 'coordinates': [19, 21], 'teleporter': [177, 4]}, {'name': 'Windia - Rainbow Bridge Script', 'id': 325, 'area': 80, 'coordinates': [21, 9], 'teleporter': [10, 6]}, {'name': "Otto's House - Attic Stairs", 'id': 326, 'area': 81, 'coordinates': [2, 19], 'teleporter': [33, 2]}, {'name': "Otto's House - Entrance", 'id': 327, 'area': 81, 'coordinates': [9, 30], 'teleporter': [106, 3]}, {'name': "Otto's Attic - Stairs", 'id': 328, 'area': 81, 'coordinates': [26, 23], 'teleporter': [107, 3]}, {'name': 'Windia Kid House - Entrance Script', 'id': 329, 'area': 82, 'coordinates': [7, 10], 'teleporter': [178, 0]}, {'name': 'Windia Kid House - Basement Stairs', 'id': 330, 'area': 82, 'coordinates': [1, 4], 'teleporter': [180, 0]}, {'name': 'Windia Old People House - Entrance', 'id': 331, 'area': 82, 'coordinates': [55, 12], 'teleporter': [179, 0]}, {'name': 'Windia Old People House - Basement Stairs', 'id': 332, 'area': 82, 'coordinates': [60, 5], 'teleporter': [181, 0]}, {'name': 'Windia Kid House Basement - Stairs', 'id': 333, 'area': 82, 'coordinates': [43, 8], 'teleporter': [182, 0]}, {'name': 'Windia Kid House Basement - Mobius Teleporter', 'id': 334, 'area': 82, 'coordinates': [41, 9], 'teleporter': [44, 8]}, {'name': 'Windia Old People House Basement - Stairs', 'id': 335, 'area': 82, 'coordinates': [39, 26], 'teleporter': [183, 0]}, {'name': 'Windia Old People House Basement - Mobius Teleporter Script', 'id': 336, 'area': 82, 'coordinates': [39, 23], 'teleporter': [43, 8]}, {'name': 'Windia Inn Lobby - Stairs to Beds', 'id': 337, 'area': 82, 'coordinates': [45, 24], 'teleporter': [102, 8]}, {'name': 'Windia Inn Lobby - Exit', 'id': 338, 'area': 82, 'coordinates': [53, 30], 'teleporter': [135, 3]}, {'name': 'Windia Inn Beds - Stairs to Lobby', 'id': 339, 'area': 82, 'coordinates': [33, 59], 'teleporter': [216, 0]}, {'name': 'Windia Vendor House - Entrance', 'id': 340, 'area': 82, 'coordinates': [29, 14], 'teleporter': [108, 3]}, {'name': 'Pazuzu Tower 1F Main Lobby - Main Entrance 1', 'id': 341, 'area': 83, 'coordinates': [47, 29], 'teleporter': [184, 0]}, {'name': 'Pazuzu Tower 1F Main Lobby - Main Entrance 2', 'id': 342, 'area': 83, 'coordinates': [47, 30], 'teleporter': [184, 0]}, {'name': 'Pazuzu Tower 1F Main Lobby - Main Entrance 3', 'id': 343, 'area': 83, 'coordinates': [48, 29], 'teleporter': [184, 0]}, {'name': 'Pazuzu Tower 1F Main Lobby - Main Entrance 4', 'id': 344, 'area': 83, 'coordinates': [48, 30], 'teleporter': [184, 0]}, {'name': 'Pazuzu Tower 1F Main Lobby - East Entrance', 'id': 345, 'area': 83, 'coordinates': [55, 12], 'teleporter': [185, 0]}, {'name': 'Pazuzu Tower 1F Main Lobby - South Stairs', 'id': 346, 'area': 83, 'coordinates': [51, 25], 'teleporter': [186, 0]}, {'name': 'Pazuzu Tower 1F Main Lobby - Pazuzu Script 1', 'id': 347, 'area': 83, 'coordinates': [47, 8], 'teleporter': [16, 8]}, {'name': 'Pazuzu Tower 1F Main Lobby - Pazuzu Script 2', 'id': 348, 'area': 83, 'coordinates': [48, 8], 'teleporter': [16, 8]}, {'name': 'Pazuzu Tower 1F Boxes Room - West Stairs', 'id': 349, 'area': 83, 'coordinates': [38, 17], 'teleporter': [187, 0]}, {'name': 'Pazuzu 2F - West Upper Stairs', 'id': 350, 'area': 84, 'coordinates': [7, 11], 'teleporter': [188, 0]}, {'name': 'Pazuzu 2F - South Stairs', 'id': 351, 'area': 84, 'coordinates': [20, 24], 'teleporter': [189, 0]}, {'name': 'Pazuzu 2F - West Lower Stairs', 'id': 352, 'area': 84, 'coordinates': [6, 17], 'teleporter': [190, 0]}, {'name': 'Pazuzu 2F - Central Stairs', 'id': 353, 'area': 84, 'coordinates': [15, 15], 'teleporter': [191, 0]}, {'name': 'Pazuzu 2F - Pazuzu 1', 'id': 354, 'area': 84, 'coordinates': [15, 8], 'teleporter': [17, 8]}, {'name': 'Pazuzu 2F - Pazuzu 2', 'id': 355, 'area': 84, 'coordinates': [16, 8], 'teleporter': [17, 8]}, {'name': 'Pazuzu 3F Main Room - North Stairs', 'id': 356, 'area': 85, 'coordinates': [23, 11], 'teleporter': [192, 0]}, {'name': 'Pazuzu 3F Main Room - West Stairs', 'id': 357, 'area': 85, 'coordinates': [7, 15], 'teleporter': [193, 0]}, {'name': 'Pazuzu 3F Main Room - Pazuzu Script 1', 'id': 358, 'area': 85, 'coordinates': [15, 8], 'teleporter': [18, 8]}, {'name': 'Pazuzu 3F Main Room - Pazuzu Script 2', 'id': 359, 'area': 85, 'coordinates': [16, 8], 'teleporter': [18, 8]}, {'name': 'Pazuzu 3F Central Island - Central Stairs', 'id': 360, 'area': 85, 'coordinates': [15, 14], 'teleporter': [194, 0]}, {'name': 'Pazuzu 3F Central Island - South Stairs', 'id': 361, 'area': 85, 'coordinates': [17, 25], 'teleporter': [195, 0]}, {'name': 'Pazuzu 4F - Northwest Stairs', 'id': 362, 'area': 86, 'coordinates': [39, 12], 'teleporter': [196, 0]}, {'name': 'Pazuzu 4F - Southwest Stairs', 'id': 363, 'area': 86, 'coordinates': [39, 19], 'teleporter': [197, 0]}, {'name': 'Pazuzu 4F - South Stairs', 'id': 364, 'area': 86, 'coordinates': [47, 24], 'teleporter': [198, 0]}, {'name': 'Pazuzu 4F - Northeast Stairs', 'id': 365, 'area': 86, 'coordinates': [54, 9], 'teleporter': [199, 0]}, {'name': 'Pazuzu 4F - Pazuzu Script 1', 'id': 366, 'area': 86, 'coordinates': [47, 8], 'teleporter': [19, 8]}, {'name': 'Pazuzu 4F - Pazuzu Script 2', 'id': 367, 'area': 86, 'coordinates': [48, 8], 'teleporter': [19, 8]}, {'name': 'Pazuzu 5F Pazuzu Loop - West Stairs', 'id': 368, 'area': 87, 'coordinates': [9, 49], 'teleporter': [200, 0]}, {'name': 'Pazuzu 5F Pazuzu Loop - South Stairs', 'id': 369, 'area': 87, 'coordinates': [16, 55], 'teleporter': [201, 0]}, {'name': 'Pazuzu 5F Upper Loop - Northeast Stairs', 'id': 370, 'area': 87, 'coordinates': [22, 40], 'teleporter': [202, 0]}, {'name': 'Pazuzu 5F Upper Loop - Northwest Stairs', 'id': 371, 'area': 87, 'coordinates': [9, 40], 'teleporter': [203, 0]}, {'name': 'Pazuzu 5F Upper Loop - Pazuzu Script 1', 'id': 372, 'area': 87, 'coordinates': [15, 40], 'teleporter': [20, 8]}, {'name': 'Pazuzu 5F Upper Loop - Pazuzu Script 2', 'id': 373, 'area': 87, 'coordinates': [16, 40], 'teleporter': [20, 8]}, {'name': 'Pazuzu 6F - West Stairs', 'id': 374, 'area': 88, 'coordinates': [41, 47], 'teleporter': [204, 0]}, {'name': 'Pazuzu 6F - Northwest Stairs', 'id': 375, 'area': 88, 'coordinates': [41, 40], 'teleporter': [205, 0]}, {'name': 'Pazuzu 6F - Northeast Stairs', 'id': 376, 'area': 88, 'coordinates': [54, 40], 'teleporter': [206, 0]}, {'name': 'Pazuzu 6F - South Stairs', 'id': 377, 'area': 88, 'coordinates': [52, 56], 'teleporter': [207, 0]}, {'name': 'Pazuzu 6F - Pazuzu Script 1', 'id': 378, 'area': 88, 'coordinates': [47, 40], 'teleporter': [21, 8]}, {'name': 'Pazuzu 6F - Pazuzu Script 2', 'id': 379, 'area': 88, 'coordinates': [48, 40], 'teleporter': [21, 8]}, {'name': 'Pazuzu 7F Main Room - Southwest Stairs', 'id': 380, 'area': 89, 'coordinates': [15, 54], 'teleporter': [26, 0]}, {'name': 'Pazuzu 7F Main Room - Northeast Stairs', 'id': 381, 'area': 89, 'coordinates': [21, 40], 'teleporter': [27, 0]}, {'name': 'Pazuzu 7F Main Room - Southeast Stairs', 'id': 382, 'area': 89, 'coordinates': [21, 56], 'teleporter': [28, 0]}, {'name': 'Pazuzu 7F Main Room - Pazuzu Script 1', 'id': 383, 'area': 89, 'coordinates': [15, 44], 'teleporter': [22, 8]}, {'name': 'Pazuzu 7F Main Room - Pazuzu Script 2', 'id': 384, 'area': 89, 'coordinates': [16, 44], 'teleporter': [22, 8]}, {'name': 'Pazuzu 7F Main Room - Crystal Script', 'id': 480, 'area': 89, 'coordinates': [15, 40], 'teleporter': [38, 8]}, {'name': 'Pazuzu 1F to 3F - South Stairs', 'id': 385, 'area': 90, 'coordinates': [43, 60], 'teleporter': [29, 0]}, {'name': 'Pazuzu 1F to 3F - North Stairs', 'id': 386, 'area': 90, 'coordinates': [43, 36], 'teleporter': [30, 0]}, {'name': 'Pazuzu 3F to 5F - South Stairs', 'id': 387, 'area': 91, 'coordinates': [43, 60], 'teleporter': [40, 0]}, {'name': 'Pazuzu 3F to 5F - North Stairs', 'id': 388, 'area': 91, 'coordinates': [43, 36], 'teleporter': [41, 0]}, {'name': 'Pazuzu 5F to 7F - South Stairs', 'id': 389, 'area': 92, 'coordinates': [43, 60], 'teleporter': [38, 0]}, {'name': 'Pazuzu 5F to 7F - North Stairs', 'id': 390, 'area': 92, 'coordinates': [43, 36], 'teleporter': [39, 0]}, {'name': 'Pazuzu 2F to 4F - South Stairs', 'id': 391, 'area': 93, 'coordinates': [43, 60], 'teleporter': [21, 0]}, {'name': 'Pazuzu 2F to 4F - North Stairs', 'id': 392, 'area': 93, 'coordinates': [43, 36], 'teleporter': [22, 0]}, {'name': 'Pazuzu 4F to 6F - South Stairs', 'id': 393, 'area': 94, 'coordinates': [43, 60], 'teleporter': [2, 0]}, {'name': 'Pazuzu 4F to 6F - North Stairs', 'id': 394, 'area': 94, 'coordinates': [43, 36], 'teleporter': [3, 0]}, {'name': 'Light Temple - Entrance', 'id': 395, 'area': 95, 'coordinates': [28, 57], 'teleporter': [19, 6]}, {'name': 'Light Temple - Mobius Teleporter Script', 'id': 396, 'area': 95, 'coordinates': [29, 37], 'teleporter': [70, 8]}, {'name': 'Light Temple - Visit Quest Script 1', 'id': 492, 'area': 95, 'coordinates': [34, 39], 'teleporter': [100, 8]}, {'name': 'Light Temple - Visit Quest Script 2', 'id': 493, 'area': 95, 'coordinates': [35, 39], 'teleporter': [100, 8]}, {'name': 'Ship Dock - Mobius Teleporter Script', 'id': 397, 'area': 96, 'coordinates': [15, 18], 'teleporter': [61, 8]}, {'name': 'Ship Dock - From Overworld', 'id': 398, 'area': 96, 'coordinates': [15, 11], 'teleporter': [73, 0]}, {'name': 'Ship Dock - Entrance', 'id': 399, 'area': 96, 'coordinates': [15, 23], 'teleporter': [17, 6]}, {'name': 'Mac Ship Deck - East Entrance Script', 'id': 400, 'area': 97, 'coordinates': [26, 40], 'teleporter': [37, 8]}, {'name': 'Mac Ship Deck - Central Stairs Script', 'id': 401, 'area': 97, 'coordinates': [16, 47], 'teleporter': [50, 8]}, {'name': 'Mac Ship Deck - West Stairs Script', 'id': 402, 'area': 97, 'coordinates': [8, 34], 'teleporter': [51, 8]}, {'name': 'Mac Ship Deck - East Stairs Script', 'id': 403, 'area': 97, 'coordinates': [24, 36], 'teleporter': [52, 8]}, {'name': 'Mac Ship Deck - North Stairs Script', 'id': 404, 'area': 97, 'coordinates': [12, 9], 'teleporter': [53, 8]}, {'name': 'Mac Ship B1 Outer Ring - South Stairs', 'id': 405, 'area': 98, 'coordinates': [16, 45], 'teleporter': [208, 0]}, {'name': 'Mac Ship B1 Outer Ring - West Stairs', 'id': 406, 'area': 98, 'coordinates': [8, 35], 'teleporter': [175, 0]}, {'name': 'Mac Ship B1 Outer Ring - East Stairs', 'id': 407, 'area': 98, 'coordinates': [25, 37], 'teleporter': [172, 0]}, {'name': 'Mac Ship B1 Outer Ring - Northwest Stairs', 'id': 408, 'area': 98, 'coordinates': [10, 23], 'teleporter': [88, 0]}, {'name': 'Mac Ship B1 Square Room - North Stairs', 'id': 409, 'area': 98, 'coordinates': [14, 9], 'teleporter': [141, 0]}, {'name': 'Mac Ship B1 Square Room - South Stairs', 'id': 410, 'area': 98, 'coordinates': [16, 12], 'teleporter': [87, 0]}, {'name': 'Mac Ship B1 Mac Room - Stairs', 'id': 411, 'area': 98, 'coordinates': [16, 51], 'teleporter': [101, 0]}, {'name': 'Mac Ship B1 Central Corridor - South Stairs', 'id': 412, 'area': 98, 'coordinates': [16, 38], 'teleporter': [102, 0]}, {'name': 'Mac Ship B1 Central Corridor - North Stairs', 'id': 413, 'area': 98, 'coordinates': [16, 26], 'teleporter': [86, 0]}, {'name': 'Mac Ship B2 South Corridor - South Stairs', 'id': 414, 'area': 99, 'coordinates': [48, 51], 'teleporter': [57, 1]}, {'name': 'Mac Ship B2 South Corridor - North Stairs Script', 'id': 415, 'area': 99, 'coordinates': [48, 38], 'teleporter': [55, 8]}, {'name': 'Mac Ship B2 North Corridor - South Stairs Script', 'id': 416, 'area': 99, 'coordinates': [48, 27], 'teleporter': [56, 8]}, {'name': 'Mac Ship B2 North Corridor - North Stairs Script', 'id': 417, 'area': 99, 'coordinates': [48, 12], 'teleporter': [57, 8]}, {'name': 'Mac Ship B2 Outer Ring - Northwest Stairs Script', 'id': 418, 'area': 99, 'coordinates': [55, 11], 'teleporter': [58, 8]}, {'name': 'Mac Ship B1 Outer Ring Cleared - South Stairs', 'id': 419, 'area': 100, 'coordinates': [16, 45], 'teleporter': [208, 0]}, {'name': 'Mac Ship B1 Outer Ring Cleared - West Stairs', 'id': 420, 'area': 100, 'coordinates': [8, 35], 'teleporter': [175, 0]}, {'name': 'Mac Ship B1 Outer Ring Cleared - East Stairs', 'id': 421, 'area': 100, 'coordinates': [25, 37], 'teleporter': [172, 0]}, {'name': 'Mac Ship B1 Square Room Cleared - North Stairs', 'id': 422, 'area': 100, 'coordinates': [14, 9], 'teleporter': [141, 0]}, {'name': 'Mac Ship B1 Square Room Cleared - South Stairs', 'id': 423, 'area': 100, 'coordinates': [16, 12], 'teleporter': [87, 0]}, {'name': 'Mac Ship B1 Mac Room Cleared - Main Stairs', 'id': 424, 'area': 100, 'coordinates': [16, 51], 'teleporter': [101, 0]}, {'name': 'Mac Ship B1 Central Corridor Cleared - South Stairs', 'id': 425, 'area': 100, 'coordinates': [16, 38], 'teleporter': [102, 0]}, {'name': 'Mac Ship B1 Central Corridor Cleared - North Stairs', 'id': 426, 'area': 100, 'coordinates': [16, 26], 'teleporter': [86, 0]}, {'name': 'Mac Ship B1 Central Corridor Cleared - Northwest Stairs', 'id': 427, 'area': 100, 'coordinates': [23, 10], 'teleporter': [88, 0]}, {'name': 'Doom Castle Corridor of Destiny - South Entrance', 'id': 428, 'area': 101, 'coordinates': [59, 29], 'teleporter': [84, 0]}, {'name': 'Doom Castle Corridor of Destiny - Ice Floor Entrance', 'id': 429, 'area': 101, 'coordinates': [59, 21], 'teleporter': [35, 2]}, {'name': 'Doom Castle Corridor of Destiny - Lava Floor Entrance', 'id': 430, 'area': 101, 'coordinates': [59, 13], 'teleporter': [209, 0]}, {'name': 'Doom Castle Corridor of Destiny - Sky Floor Entrance', 'id': 431, 'area': 101, 'coordinates': [59, 5], 'teleporter': [211, 0]}, {'name': 'Doom Castle Corridor of Destiny - Hero Room Entrance', 'id': 432, 'area': 101, 'coordinates': [59, 61], 'teleporter': [13, 2]}, {'name': 'Doom Castle Ice Floor - Entrance', 'id': 433, 'area': 102, 'coordinates': [23, 42], 'teleporter': [109, 3]}, {'name': 'Doom Castle Lava Floor - Entrance', 'id': 434, 'area': 103, 'coordinates': [23, 40], 'teleporter': [210, 0]}, {'name': 'Doom Castle Sky Floor - Entrance', 'id': 435, 'area': 104, 'coordinates': [24, 41], 'teleporter': [212, 0]}, {'name': 'Doom Castle Hero Room - Dark King Entrance 1', 'id': 436, 'area': 106, 'coordinates': [15, 5], 'teleporter': [54, 0]}, {'name': 'Doom Castle Hero Room - Dark King Entrance 2', 'id': 437, 'area': 106, 'coordinates': [16, 5], 'teleporter': [54, 0]}, {'name': 'Doom Castle Hero Room - Dark King Entrance 3', 'id': 438, 'area': 106, 'coordinates': [15, 4], 'teleporter': [54, 0]}, {'name': 'Doom Castle Hero Room - Dark King Entrance 4', 'id': 439, 'area': 106, 'coordinates': [16, 4], 'teleporter': [54, 0]}, {'name': 'Doom Castle Hero Room - Hero Statue Script', 'id': 440, 'area': 106, 'coordinates': [15, 17], 'teleporter': [24, 8]}, {'name': 'Doom Castle Hero Room - Entrance', 'id': 441, 'area': 106, 'coordinates': [15, 24], 'teleporter': [110, 3]}, {'name': 'Doom Castle Dark King Room - Entrance', 'id': 442, 'area': 107, 'coordinates': [14, 26], 'teleporter': [52, 0]}, {'name': 'Doom Castle Dark King Room - Dark King Script', 'id': 443, 'area': 107, 'coordinates': [14, 15], 'teleporter': [25, 8]}, {'name': 'Doom Castle Dark King Room - Unknown', 'id': 444, 'area': 107, 'coordinates': [47, 54], 'teleporter': [77, 0]}, {'name': 'Overworld - Level Forest', 'id': 445, 'area': 0, 'type': 'Overworld', 'teleporter': [46, 8]}, {'name': 'Overworld - Foresta', 'id': 446, 'area': 0, 'type': 'Overworld', 'teleporter': [2, 1]}, {'name': 'Overworld - Sand Temple', 'id': 447, 'area': 0, 'type': 'Overworld', 'teleporter': [3, 1]}, {'name': 'Overworld - Bone Dungeon', 'id': 448, 'area': 0, 'type': 'Overworld', 'teleporter': [4, 1]}, {'name': 'Overworld - Focus Tower Foresta', 'id': 449, 'area': 0, 'type': 'Overworld', 'teleporter': [5, 1]}, {'name': 'Overworld - Focus Tower Aquaria', 'id': 450, 'area': 0, 'type': 'Overworld', 'teleporter': [19, 1]}, {'name': 'Overworld - Libra Temple', 'id': 451, 'area': 0, 'type': 'Overworld', 'teleporter': [7, 1]}, {'name': 'Overworld - Aquaria', 'id': 452, 'area': 0, 'type': 'Overworld', 'teleporter': [8, 8]}, {'name': 'Overworld - Wintry Cave', 'id': 453, 'area': 0, 'type': 'Overworld', 'teleporter': [10, 1]}, {'name': 'Overworld - Life Temple', 'id': 454, 'area': 0, 'type': 'Overworld', 'teleporter': [11, 1]}, {'name': 'Overworld - Falls Basin', 'id': 455, 'area': 0, 'type': 'Overworld', 'teleporter': [12, 1]}, {'name': 'Overworld - Ice Pyramid', 'id': 456, 'area': 0, 'type': 'Overworld', 'teleporter': [13, 1]}, {'name': "Overworld - Spencer's Place", 'id': 457, 'area': 0, 'type': 'Overworld', 'teleporter': [48, 8]}, {'name': 'Overworld - Wintry Temple', 'id': 458, 'area': 0, 'type': 'Overworld', 'teleporter': [16, 1]}, {'name': 'Overworld - Focus Tower Frozen Strip', 'id': 459, 'area': 0, 'type': 'Overworld', 'teleporter': [17, 1]}, {'name': 'Overworld - Focus Tower Fireburg', 'id': 460, 'area': 0, 'type': 'Overworld', 'teleporter': [18, 1]}, {'name': 'Overworld - Fireburg', 'id': 461, 'area': 0, 'type': 'Overworld', 'teleporter': [20, 1]}, {'name': 'Overworld - Mine', 'id': 462, 'area': 0, 'type': 'Overworld', 'teleporter': [21, 1]}, {'name': 'Overworld - Sealed Temple', 'id': 463, 'area': 0, 'type': 'Overworld', 'teleporter': [22, 1]}, {'name': 'Overworld - Volcano', 'id': 464, 'area': 0, 'type': 'Overworld', 'teleporter': [23, 1]}, {'name': 'Overworld - Lava Dome', 'id': 465, 'area': 0, 'type': 'Overworld', 'teleporter': [24, 1]}, {'name': 'Overworld - Focus Tower Windia', 'id': 466, 'area': 0, 'type': 'Overworld', 'teleporter': [6, 1]}, {'name': 'Overworld - Rope Bridge', 'id': 467, 'area': 0, 'type': 'Overworld', 'teleporter': [25, 1]}, {'name': 'Overworld - Alive Forest', 'id': 468, 'area': 0, 'type': 'Overworld', 'teleporter': [26, 1]}, {'name': 'Overworld - Giant Tree', 'id': 469, 'area': 0, 'type': 'Overworld', 'teleporter': [27, 1]}, {'name': 'Overworld - Kaidge Temple', 'id': 470, 'area': 0, 'type': 'Overworld', 'teleporter': [28, 1]}, {'name': 'Overworld - Windia', 'id': 471, 'area': 0, 'type': 'Overworld', 'teleporter': [29, 1]}, {'name': 'Overworld - Windhole Temple', 'id': 472, 'area': 0, 'type': 'Overworld', 'teleporter': [30, 1]}, {'name': 'Overworld - Mount Gale', 'id': 473, 'area': 0, 'type': 'Overworld', 'teleporter': [31, 1]}, {'name': 'Overworld - Pazuzu Tower', 'id': 474, 'area': 0, 'type': 'Overworld', 'teleporter': [32, 1]}, {'name': 'Overworld - Ship Dock', 'id': 475, 'area': 0, 'type': 'Overworld', 'teleporter': [62, 1]}, {'name': 'Overworld - Doom Castle', 'id': 476, 'area': 0, 'type': 'Overworld', 'teleporter': [33, 1]}, {'name': 'Overworld - Light Temple', 'id': 477, 'area': 0, 'type': 'Overworld', 'teleporter': [34, 1]}, {'name': 'Overworld - Mac Ship', 'id': 478, 'area': 0, 'type': 'Overworld', 'teleporter': [36, 1]}, {'name': 'Overworld - Mac Ship Doom', 'id': 479, 'area': 0, 'type': 'Overworld', 'teleporter': [36, 1]}, {'name': 'Dummy House - Bed Script', 'id': 480, 'area': 17, 'coordinates': [40, 56], 'teleporter': [1, 8]}, {'name': 'Dummy House - Entrance', 'id': 481, 'area': 17, 'coordinates': [41, 59], 'teleporter': [0, 10]}] \ No newline at end of file diff --git a/worlds/ffmq/data/rooms.yaml b/worlds/ffmq/data/rooms.yaml deleted file mode 100644 index e0c2e8d7f9f..00000000000 --- a/worlds/ffmq/data/rooms.yaml +++ /dev/null @@ -1,4026 +0,0 @@ -- name: Overworld - id: 0 - type: "Overworld" - game_objects: [] - links: - - target_room: 220 # To Forest Subregion - access: [] -- name: Subregion Foresta - id: 220 - type: "Subregion" - region: "Foresta" - game_objects: - - name: "Foresta South Battlefield" - object_id: 0x01 - location: "ForestaSouthBattlefield" - location_slot: "ForestaSouthBattlefield" - type: "BattlefieldXp" - access: [] - - name: "Foresta West Battlefield" - object_id: 0x02 - location: "ForestaWestBattlefield" - location_slot: "ForestaWestBattlefield" - type: "BattlefieldItem" - access: [] - - name: "Foresta East Battlefield" - object_id: 0x03 - location: "ForestaEastBattlefield" - location_slot: "ForestaEastBattlefield" - type: "BattlefieldGp" - access: [] - links: - - target_room: 15 # Level Forest - location: "LevelForest" - location_slot: "LevelForest" - entrance: 445 - teleporter: [0x2E, 8] - access: [] - - target_room: 16 # Foresta - location: "Foresta" - location_slot: "Foresta" - entrance: 446 - teleporter: [0x02, 1] - access: [] - - target_room: 24 # Sand Temple - location: "SandTemple" - location_slot: "SandTemple" - entrance: 447 - teleporter: [0x03, 1] - access: [] - - target_room: 25 # Bone Dungeon - location: "BoneDungeon" - location_slot: "BoneDungeon" - entrance: 448 - teleporter: [0x04, 1] - access: [] - - target_room: 3 # Focus Tower Foresta - location: "FocusTowerForesta" - location_slot: "FocusTowerForesta" - entrance: 449 - teleporter: [0x05, 1] - access: [] - - target_room: 221 - access: ["SandCoin"] - - target_room: 224 - access: ["RiverCoin"] - - target_room: 226 - access: ["SunCoin"] -- name: Subregion Aquaria - id: 221 - type: "Subregion" - region: "Aquaria" - game_objects: - - name: "South of Libra Temple Battlefield" - object_id: 0x04 - location: "AquariaBattlefield01" - location_slot: "AquariaBattlefield01" - type: "BattlefieldXp" - access: [] - - name: "East of Libra Temple Battlefield" - object_id: 0x05 - location: "AquariaBattlefield02" - location_slot: "AquariaBattlefield02" - type: "BattlefieldGp" - access: [] - - name: "South of Aquaria Battlefield" - object_id: 0x06 - location: "AquariaBattlefield03" - location_slot: "AquariaBattlefield03" - type: "BattlefieldItem" - access: [] - - name: "South of Wintry Cave Battlefield" - object_id: 0x07 - location: "WintryBattlefield01" - location_slot: "WintryBattlefield01" - type: "BattlefieldXp" - access: [] - - name: "West of Wintry Cave Battlefield" - object_id: 0x08 - location: "WintryBattlefield02" - location_slot: "WintryBattlefield02" - type: "BattlefieldGp" - access: [] - - name: "Ice Pyramid Battlefield" - object_id: 0x09 - location: "PyramidBattlefield01" - location_slot: "PyramidBattlefield01" - type: "BattlefieldXp" - access: [] - links: - - target_room: 10 # Focus Tower Aquaria - location: "FocusTowerAquaria" - location_slot: "FocusTowerAquaria" - entrance: 450 - teleporter: [0x13, 1] - access: [] - - target_room: 39 # Libra Temple - location: "LibraTemple" - location_slot: "LibraTemple" - entrance: 451 - teleporter: [0x07, 1] - access: [] - - target_room: 40 # Aquaria - location: "Aquaria" - location_slot: "Aquaria" - entrance: 452 - teleporter: [0x08, 8] - access: [] - - target_room: 45 # Wintry Cave - location: "WintryCave" - location_slot: "WintryCave" - entrance: 453 - teleporter: [0x0A, 1] - access: [] - - target_room: 52 # Falls Basin - location: "FallsBasin" - location_slot: "FallsBasin" - entrance: 455 - teleporter: [0x0C, 1] - access: [] - - target_room: 54 # Ice Pyramid - location: "IcePyramid" - location_slot: "IcePyramid" - entrance: 456 - teleporter: [0x0D, 1] # Will be switched to a script - access: [] - - target_room: 220 - access: ["SandCoin"] - - target_room: 224 - access: ["SandCoin", "RiverCoin"] - - target_room: 226 - access: ["SandCoin", "SunCoin"] - - target_room: 223 - access: ["SummerAquaria"] -- name: Subregion Life Temple - id: 222 - type: "Subregion" - region: "LifeTemple" - game_objects: [] - links: - - target_room: 51 # Life Temple - location: "LifeTemple" - location_slot: "LifeTemple" - entrance: 454 - teleporter: [0x0B, 1] - access: [] -- name: Subregion Frozen Fields - id: 223 - type: "Subregion" - region: "AquariaFrozenField" - game_objects: - - name: "North of Libra Temple Battlefield" - object_id: 0x0A - location: "LibraBattlefield01" - location_slot: "LibraBattlefield01" - type: "BattlefieldItem" - access: [] - - name: "Aquaria Frozen Field Battlefield" - object_id: 0x0B - location: "LibraBattlefield02" - location_slot: "LibraBattlefield02" - type: "BattlefieldXp" - access: [] - links: - - target_room: 74 # Wintry Temple - location: "WintryTemple" - location_slot: "WintryTemple" - entrance: 458 - teleporter: [0x10, 1] - access: [] - - target_room: 14 # Focus Tower Frozen Strip - location: "FocusTowerFrozen" - location_slot: "FocusTowerFrozen" - entrance: 459 - teleporter: [0x11, 1] - access: [] - - target_room: 221 - access: [] - - target_room: 225 - access: ["SummerAquaria", "DualheadHydra"] -- name: Subregion Fireburg - id: 224 - type: "Subregion" - region: "Fireburg" - game_objects: - - name: "Path to Fireburg Southern Battlefield" - object_id: 0x0C - location: "FireburgBattlefield01" - location_slot: "FireburgBattlefield01" - type: "BattlefieldGp" - access: [] - - name: "Path to Fireburg Central Battlefield" - object_id: 0x0D - location: "FireburgBattlefield02" - location_slot: "FireburgBattlefield02" - type: "BattlefieldItem" - access: [] - - name: "Path to Fireburg Northern Battlefield" - object_id: 0x0E - location: "FireburgBattlefield03" - location_slot: "FireburgBattlefield03" - type: "BattlefieldXp" - access: [] - - name: "Sealed Temple Battlefield" - object_id: 0x0F - location: "MineBattlefield01" - location_slot: "MineBattlefield01" - type: "BattlefieldGp" - access: [] - - name: "Mine Battlefield" - object_id: 0x10 - location: "MineBattlefield02" - location_slot: "MineBattlefield02" - type: "BattlefieldItem" - access: [] - - name: "Boulder Battlefield" - object_id: 0x11 - location: "MineBattlefield03" - location_slot: "MineBattlefield03" - type: "BattlefieldXp" - access: [] - links: - - target_room: 13 # Focus Tower Fireburg - location: "FocusTowerFireburg" - location_slot: "FocusTowerFireburg" - entrance: 460 - teleporter: [0x12, 1] - access: [] - - target_room: 76 # Fireburg - location: "Fireburg" - location_slot: "Fireburg" - entrance: 461 - teleporter: [0x14, 1] - access: [] - - target_room: 84 # Mine - location: "Mine" - location_slot: "Mine" - entrance: 462 - teleporter: [0x15, 1] - access: [] - - target_room: 92 # Sealed Temple - location: "SealedTemple" - location_slot: "SealedTemple" - entrance: 463 - teleporter: [0x16, 1] - access: [] - - target_room: 93 # Volcano - location: "Volcano" - location_slot: "Volcano" - entrance: 464 - teleporter: [0x17, 1] # Also this one / 0x0F, 8 - access: [] - - target_room: 100 # Lava Dome - location: "LavaDome" - location_slot: "LavaDome" - entrance: 465 - teleporter: [0x18, 1] - access: [] - - target_room: 220 - access: ["RiverCoin"] - - target_room: 221 - access: ["SandCoin", "RiverCoin"] - - target_room: 226 - access: ["RiverCoin", "SunCoin"] - - target_room: 225 - access: ["DualheadHydra"] -- name: Subregion Volcano Battlefield - id: 225 - type: "Subregion" - region: "VolcanoBattlefield" - game_objects: - - name: "Volcano Battlefield" - object_id: 0x12 - location: "VolcanoBattlefield01" - location_slot: "VolcanoBattlefield01" - type: "BattlefieldXp" - access: [] - links: - - target_room: 224 - access: ["DualheadHydra"] - - target_room: 223 - access: ["SummerAquaria"] -- name: Subregion Windia - id: 226 - type: "Subregion" - region: "Windia" - game_objects: - - name: "Kaidge Temple Battlefield" - object_id: 0x13 - location: "WindiaBattlefield01" - location_slot: "WindiaBattlefield01" - type: "BattlefieldXp" - access: ["SandCoin", "RiverCoin"] - - name: "South of Windia Battlefield" - object_id: 0x14 - location: "WindiaBattlefield02" - location_slot: "WindiaBattlefield02" - type: "BattlefieldXp" - access: ["SandCoin", "RiverCoin"] - links: - - target_room: 9 # Focus Tower Windia - location: "FocusTowerWindia" - location_slot: "FocusTowerWindia" - entrance: 466 - teleporter: [0x06, 1] - access: [] - - target_room: 123 # Rope Bridge - location: "RopeBridge" - location_slot: "RopeBridge" - entrance: 467 - teleporter: [0x19, 1] - access: [] - - target_room: 124 # Alive Forest - location: "AliveForest" - location_slot: "AliveForest" - entrance: 468 - teleporter: [0x1A, 1] - access: [] - - target_room: 125 # Giant Tree - location: "GiantTree" - location_slot: "GiantTree" - entrance: 469 - teleporter: [0x1B, 1] - access: ["Barred"] - - target_room: 152 # Kaidge Temple - location: "KaidgeTemple" - location_slot: "KaidgeTemple" - entrance: 470 - teleporter: [0x1C, 1] - access: [] - - target_room: 156 # Windia - location: "Windia" - location_slot: "Windia" - entrance: 471 - teleporter: [0x1D, 1] - access: [] - - target_room: 154 # Windhole Temple - location: "WindholeTemple" - location_slot: "WindholeTemple" - entrance: 472 - teleporter: [0x1E, 1] - access: [] - - target_room: 155 # Mount Gale - location: "MountGale" - location_slot: "MountGale" - entrance: 473 - teleporter: [0x1F, 1] - access: [] - - target_room: 166 # Pazuzu Tower - location: "PazuzusTower" - location_slot: "PazuzusTower" - entrance: 474 - teleporter: [0x20, 1] - access: [] - - target_room: 220 - access: ["SunCoin"] - - target_room: 221 - access: ["SandCoin", "SunCoin"] - - target_room: 224 - access: ["RiverCoin", "SunCoin"] - - target_room: 227 - access: ["RainbowBridge"] -- name: Subregion Spencer's Cave - id: 227 - type: "Subregion" - region: "SpencerCave" - game_objects: [] - links: - - target_room: 73 # Spencer's Place - location: "SpencersPlace" - location_slot: "SpencersPlace" - entrance: 457 - teleporter: [0x30, 8] - access: [] - - target_room: 226 - access: ["RainbowBridge"] -- name: Subregion Ship Dock - id: 228 - type: "Subregion" - region: "ShipDock" - game_objects: [] - links: - - target_room: 186 # Ship Dock - location: "ShipDock" - location_slot: "ShipDock" - entrance: 475 - teleporter: [0x3E, 1] - access: [] - - target_room: 229 - access: ["ShipLiberated", "ShipDockAccess"] -- name: Subregion Mac's Ship - id: 229 - type: "Subregion" - region: "MacShip" - game_objects: [] - links: - - target_room: 187 # Mac Ship - location: "MacsShip" - location_slot: "MacsShip" - entrance: 478 - teleporter: [0x24, 1] - access: [] - - target_room: 228 - access: ["ShipLiberated", "ShipDockAccess"] - - target_room: 231 - access: ["ShipLoaned", "ShipDockAccess", "ShipSteeringWheel"] -- name: Subregion Light Temple - id: 230 - type: "Subregion" - region: "LightTemple" - game_objects: [] - links: - - target_room: 185 # Light Temple - location: "LightTemple" - location_slot: "LightTemple" - entrance: 477 - teleporter: [0x23, 1] - access: [] -- name: Subregion Doom Castle - id: 231 - type: "Subregion" - region: "DoomCastle" - game_objects: [] - links: - - target_room: 1 # Doom Castle - location: "DoomCastle" - location_slot: "DoomCastle" - entrance: 476 - teleporter: [0x21, 1] - access: [] - - target_room: 187 # Mac Ship Doom - location: "MacsShipDoom" - location_slot: "MacsShipDoom" - entrance: 479 - teleporter: [0x24, 1] - access: ["Barred"] - - target_room: 229 - access: ["ShipLoaned", "ShipDockAccess", "ShipSteeringWheel"] -- name: Doom Castle - Sand Floor - id: 1 - game_objects: - - name: "Doom Castle B2 - Southeast Chest" - object_id: 0x01 - type: "Chest" - access: ["Bomb"] - - name: "Doom Castle B2 - Bone Ledge Box" - object_id: 0x1E - type: "Box" - access: [] - - name: "Doom Castle B2 - Hook Platform Box" - object_id: 0x1F - type: "Box" - access: ["DragonClaw"] - links: - - target_room: 231 - entrance: 1 - teleporter: [1, 6] - access: [] - - target_room: 5 - entrance: 0 - teleporter: [0, 0] - access: ["DragonClaw", "MegaGrenade"] -- name: Doom Castle - Aero Room - id: 2 - game_objects: - - name: "Doom Castle B2 - Sun Door Chest" - object_id: 0x00 - type: "Chest" - access: [] - links: - - target_room: 4 - entrance: 2 - teleporter: [1, 0] - access: [] -- name: Focus Tower B1 - Main Loop - id: 3 - game_objects: [] - links: - - target_room: 220 - entrance: 3 - teleporter: [2, 6] - access: [] - - target_room: 6 - entrance: 4 - teleporter: [4, 0] - access: [] -- name: Focus Tower B1 - Aero Corridor - id: 4 - game_objects: [] - links: - - target_room: 9 - entrance: 5 - teleporter: [5, 0] - access: [] - - target_room: 2 - entrance: 6 - teleporter: [8, 0] - access: [] -- name: Focus Tower B1 - Inner Loop - id: 5 - game_objects: [] - links: - - target_room: 1 - entrance: 8 - teleporter: [7, 0] - access: [] - - target_room: 201 - entrance: 7 - teleporter: [6, 0] - access: [] -- name: Focus Tower 1F Main Lobby - id: 6 - game_objects: - - name: "Focus Tower 1F - Main Lobby Box" - object_id: 0x21 - type: "Box" - access: [] - links: - - target_room: 3 - entrance: 11 - teleporter: [11, 0] - access: [] - - target_room: 7 - access: ["SandCoin"] - - target_room: 8 - access: ["RiverCoin"] - - target_room: 9 - access: ["SunCoin"] -- name: Focus Tower 1F SandCoin Room - id: 7 - game_objects: [] - links: - - target_room: 6 - access: ["SandCoin"] - - target_room: 10 - entrance: 10 - teleporter: [10, 0] - access: [] -- name: Focus Tower 1F RiverCoin Room - id: 8 - game_objects: [] - links: - - target_room: 6 - access: ["RiverCoin"] - - target_room: 11 - entrance: 14 - teleporter: [14, 0] - access: [] -- name: Focus Tower 1F SunCoin Room - id: 9 - game_objects: [] - links: - - target_room: 6 - access: ["SunCoin"] - - target_room: 4 - entrance: 12 - teleporter: [12, 0] - access: [] - - target_room: 226 - entrance: 9 - teleporter: [3, 6] - access: [] -- name: Focus Tower 1F SkyCoin Room - id: 201 - game_objects: [] - links: - - target_room: 195 - entrance: 13 - teleporter: [13, 0] - access: ["SkyCoin", "FlamerusRex", "IceGolem", "DualheadHydra", "Pazuzu"] - - target_room: 5 - entrance: 15 - teleporter: [15, 0] - access: [] -- name: Focus Tower 2F - Sand Coin Passage - id: 10 - game_objects: - - name: "Focus Tower 2F - Sand Door Chest" - object_id: 0x03 - type: "Chest" - access: [] - links: - - target_room: 221 - entrance: 16 - teleporter: [4, 6] - access: [] - - target_room: 7 - entrance: 17 - teleporter: [17, 0] - access: [] -- name: Focus Tower 2F - River Coin Passage - id: 11 - game_objects: [] - links: - - target_room: 8 - entrance: 18 - teleporter: [18, 0] - access: [] - - target_room: 13 - entrance: 19 - teleporter: [20, 0] - access: [] -- name: Focus Tower 2F - Venus Chest Room - id: 12 - game_objects: - - name: "Focus Tower 2F - Back Door Chest" - object_id: 0x02 - type: "Chest" - access: [] - - name: "Focus Tower 2F - Venus Chest" - object_id: 9 - type: "NPC" - access: ["Bomb", "VenusKey"] - links: - - target_room: 14 - entrance: 20 - teleporter: [19, 0] - access: [] -- name: Focus Tower 3F - Lower Floor - id: 13 - game_objects: - - name: "Focus Tower 3F - River Door Box" - object_id: 0x22 - type: "Box" - access: [] - links: - - target_room: 224 - entrance: 22 - teleporter: [6, 6] - access: [] - - target_room: 11 - entrance: 23 - teleporter: [24, 0] - access: [] -- name: Focus Tower 3F - Upper Floor - id: 14 - game_objects: [] - links: - - target_room: 223 - entrance: 24 - teleporter: [5, 6] - access: [] - - target_room: 12 - entrance: 25 - teleporter: [23, 0] - access: [] -- name: Level Forest - id: 15 - game_objects: - - name: "Level Forest - Northwest Box" - object_id: 0x28 - type: "Box" - access: ["Axe"] - - name: "Level Forest - Northeast Box" - object_id: 0x29 - type: "Box" - access: ["Axe"] - - name: "Level Forest - Middle Box" - object_id: 0x2A - type: "Box" - access: [] - - name: "Level Forest - Southwest Box" - object_id: 0x2B - type: "Box" - access: ["Axe"] - - name: "Level Forest - Southeast Box" - object_id: 0x2C - type: "Box" - access: ["Axe"] - - name: "Minotaur" - object_id: 0 - type: "Trigger" - on_trigger: ["Minotaur"] - access: ["Kaeli1"] - - name: "Level Forest - Old Man" - object_id: 0 - type: "NPC" - access: [] - - name: "Level Forest - Kaeli" - object_id: 1 - type: "NPC" - access: ["Kaeli1", "Minotaur"] - links: - - target_room: 220 - entrance: 28 - teleporter: [25, 0] - access: [] -- name: Foresta - id: 16 - game_objects: - - name: "Foresta - Outside Box" - object_id: 0x2D - type: "Box" - access: ["Axe"] - links: - - target_room: 220 - entrance: 38 - teleporter: [31, 0] - access: [] - - target_room: 17 - entrance: 44 - teleporter: [0, 5] - access: [] - - target_room: 18 - entrance: 42 - teleporter: [32, 4] - access: [] - - target_room: 19 - entrance: 43 - teleporter: [33, 0] - access: [] - - target_room: 20 - entrance: 45 - teleporter: [1, 5] - access: [] -- name: Kaeli's House - id: 17 - game_objects: - - name: "Foresta - Kaeli's House Box" - object_id: 0x2E - type: "Box" - access: [] - - name: "Kaeli Companion" - object_id: 0 - type: "Trigger" - on_trigger: ["Kaeli1"] - access: ["TreeWither"] - - name: "Kaeli 2" - object_id: 0 - type: "Trigger" - on_trigger: ["Kaeli2"] - access: ["Kaeli1", "Minotaur", "Elixir"] - links: - - target_room: 16 - entrance: 46 - teleporter: [86, 3] - access: [] -- name: Foresta Houses - Old Man's House Main - id: 18 - game_objects: [] - links: - - target_room: 19 - access: ["BarrelPushed"] - - target_room: 16 - entrance: 47 - teleporter: [34, 0] - access: [] -- name: Foresta Houses - Old Man's House Back - id: 19 - game_objects: - - name: "Foresta - Old Man House Chest" - object_id: 0x05 - type: "Chest" - access: [] - - name: "Old Man Barrel" - object_id: 0 - type: "Trigger" - on_trigger: ["BarrelPushed"] - access: [] - links: - - target_room: 18 - access: ["BarrelPushed"] - - target_room: 16 - entrance: 48 - teleporter: [35, 0] - access: [] -- name: Foresta Houses - Rest House - id: 20 - game_objects: - - name: "Foresta - Rest House Box" - object_id: 0x2F - type: "Box" - access: [] - links: - - target_room: 16 - entrance: 50 - teleporter: [87, 3] - access: [] -- name: Libra Treehouse - id: 21 - game_objects: - - name: "Alive Forest - Libra Treehouse Box" - object_id: 0x32 - type: "Box" - access: [] - links: - - target_room: 124 - entrance: 51 - teleporter: [67, 8] - access: ["LibraCrest"] -- name: Gemini Treehouse - id: 22 - game_objects: - - name: "Alive Forest - Gemini Treehouse Box" - object_id: 0x33 - type: "Box" - access: [] - links: - - target_room: 124 - entrance: 52 - teleporter: [68, 8] - access: ["GeminiCrest"] -- name: Mobius Treehouse - id: 23 - game_objects: - - name: "Alive Forest - Mobius Treehouse West Box" - object_id: 0x30 - type: "Box" - access: [] - - name: "Alive Forest - Mobius Treehouse East Box" - object_id: 0x31 - type: "Box" - access: [] - links: - - target_room: 124 - entrance: 53 - teleporter: [69, 8] - access: ["MobiusCrest"] -- name: Sand Temple - id: 24 - game_objects: - - name: "Tristam Companion" - object_id: 0 - type: "Trigger" - on_trigger: ["Tristam"] - access: [] - links: - - target_room: 220 - entrance: 54 - teleporter: [36, 0] - access: [] -- name: Bone Dungeon 1F - id: 25 - game_objects: - - name: "Bone Dungeon 1F - Entrance Room West Box" - object_id: 0x35 - type: "Box" - access: [] - - name: "Bone Dungeon 1F - Entrance Room Middle Box" - object_id: 0x36 - type: "Box" - access: [] - - name: "Bone Dungeon 1F - Entrance Room East Box" - object_id: 0x37 - type: "Box" - access: [] - links: - - target_room: 220 - entrance: 55 - teleporter: [37, 0] - access: [] - - target_room: 26 - entrance: 56 - teleporter: [2, 2] - access: [] -- name: Bone Dungeon B1 - Waterway - id: 26 - game_objects: - - name: "Bone Dungeon B1 - Skull Chest" - object_id: 0x06 - type: "Chest" - access: ["Bomb"] - - name: "Bone Dungeon B1 - Tristam" - object_id: 2 - type: "NPC" - access: ["Tristam"] - - name: "Tristam Bone Dungeon Item Given" - object_id: 0 - type: "Trigger" - on_trigger: ["TristamBoneItemGiven"] - access: ["Tristam"] - links: - - target_room: 25 - entrance: 59 - teleporter: [88, 3] - access: [] - - target_room: 28 - entrance: 57 - teleporter: [3, 2] - access: ["Bomb"] -- name: Bone Dungeon B1 - Checker Room - id: 28 - game_objects: - - name: "Bone Dungeon B1 - Checker Room Box" - object_id: 0x38 - type: "Box" - access: ["Bomb"] - links: - - target_room: 26 - entrance: 61 - teleporter: [89, 3] - access: [] - - target_room: 30 - entrance: 60 - teleporter: [4, 2] - access: [] -- name: Bone Dungeon B1 - Hidden Room - id: 29 - game_objects: - - name: "Bone Dungeon B1 - Ribcage Waterway Box" - object_id: 0x39 - type: "Box" - access: [] - links: - - target_room: 31 - entrance: 62 - teleporter: [91, 3] - access: [] -- name: Bone Dungeon B2 - Exploding Skull Room - First Room - id: 30 - game_objects: - - name: "Bone Dungeon B2 - Spines Room Alcove Box" - object_id: 0x3B - type: "Box" - access: [] - - name: "Long Spine" - object_id: 0 - type: "Trigger" - on_trigger: ["LongSpineBombed"] - access: ["Bomb"] - links: - - target_room: 28 - entrance: 65 - teleporter: [90, 3] - access: [] - - target_room: 31 - access: ["LongSpineBombed"] -- name: Bone Dungeon B2 - Exploding Skull Room - Second Room - id: 31 - game_objects: - - name: "Bone Dungeon B2 - Spines Room Looped Hallway Box" - object_id: 0x3A - type: "Box" - access: [] - - name: "Short Spine" - object_id: 0 - type: "Trigger" - on_trigger: ["ShortSpineBombed"] - access: ["Bomb"] - links: - - target_room: 29 - entrance: 63 - teleporter: [5, 2] - access: ["LongSpineBombed"] - - target_room: 32 - access: ["ShortSpineBombed"] - - target_room: 30 - access: ["LongSpineBombed"] -- name: Bone Dungeon B2 - Exploding Skull Room - Third Room - id: 32 - game_objects: [] - links: - - target_room: 35 - entrance: 64 - teleporter: [6, 2] - access: [] - - target_room: 31 - access: ["ShortSpineBombed"] -- name: Bone Dungeon B2 - Box Room - id: 33 - game_objects: - - name: "Bone Dungeon B2 - Lone Room Box" - object_id: 0x3D - type: "Box" - access: [] - links: - - target_room: 36 - entrance: 66 - teleporter: [93, 3] - access: [] -- name: Bone Dungeon B2 - Quake Room - id: 34 - game_objects: - - name: "Bone Dungeon B2 - Penultimate Room Chest" - object_id: 0x07 - type: "Chest" - access: [] - links: - - target_room: 37 - entrance: 67 - teleporter: [94, 3] - access: [] -- name: Bone Dungeon B2 - Two Skulls Room - First Room - id: 35 - game_objects: - - name: "Bone Dungeon B2 - Two Skulls Room Box" - object_id: 0x3C - type: "Box" - access: [] - - name: "Skull 1" - object_id: 0 - type: "Trigger" - on_trigger: ["Skull1Bombed"] - access: ["Bomb"] - links: - - target_room: 32 - entrance: 71 - teleporter: [92, 3] - access: [] - - target_room: 36 - access: ["Skull1Bombed"] -- name: Bone Dungeon B2 - Two Skulls Room - Second Room - id: 36 - game_objects: - - name: "Skull 2" - object_id: 0 - type: "Trigger" - on_trigger: ["Skull2Bombed"] - access: ["Bomb"] - links: - - target_room: 33 - entrance: 68 - teleporter: [7, 2] - access: [] - - target_room: 37 - access: ["Skull2Bombed"] - - target_room: 35 - access: ["Skull1Bombed"] -- name: Bone Dungeon B2 - Two Skulls Room - Third Room - id: 37 - game_objects: [] - links: - - target_room: 34 - entrance: 69 - teleporter: [8, 2] - access: [] - - target_room: 38 - entrance: 70 - teleporter: [9, 2] - access: ["Bomb"] - - target_room: 36 - access: ["Skull2Bombed"] -- name: Bone Dungeon B2 - Boss Room - id: 38 - game_objects: - - name: "Bone Dungeon B2 - North Box" - object_id: 0x3E - type: "Box" - access: [] - - name: "Bone Dungeon B2 - South Box" - object_id: 0x3F - type: "Box" - access: [] - - name: "Bone Dungeon B2 - Flamerus Rex Chest" - object_id: 0x08 - type: "Chest" - access: [] - - name: "Bone Dungeon B2 - Tristam's Treasure Chest" - object_id: 0x04 - type: "Chest" - access: [] - - name: "Flamerus Rex" - object_id: 0 - type: "Trigger" - on_trigger: ["FlamerusRex"] - access: [] - links: - - target_room: 37 - entrance: 74 - teleporter: [95, 3] - access: [] -- name: Libra Temple - id: 39 - game_objects: - - name: "Libra Temple - Box" - object_id: 0x40 - type: "Box" - access: [] - - name: "Phoebe Companion" - object_id: 0 - type: "Trigger" - on_trigger: ["Phoebe1"] - access: [] - links: - - target_room: 221 - entrance: 75 - teleporter: [13, 6] - access: [] - - target_room: 51 - entrance: 76 - teleporter: [59, 8] - access: ["LibraCrest"] -- name: Aquaria - id: 40 - game_objects: - - name: "Summer Aquaria" - object_id: 0 - type: "Trigger" - on_trigger: ["SummerAquaria"] - access: ["WakeWater"] - links: - - target_room: 221 - entrance: 77 - teleporter: [8, 6] - access: [] - - target_room: 41 - entrance: 81 - teleporter: [10, 5] - access: [] - - target_room: 42 - entrance: 82 - teleporter: [44, 4] - access: [] - - target_room: 44 - entrance: 83 - teleporter: [11, 5] - access: [] - - target_room: 71 - entrance: 89 - teleporter: [42, 0] - access: ["SummerAquaria"] - - target_room: 71 - entrance: 90 - teleporter: [43, 0] - access: ["SummerAquaria"] -- name: Phoebe's House - id: 41 - game_objects: - - name: "Aquaria - Phoebe's House Chest" - object_id: 0x41 - type: "Box" - access: [] - links: - - target_room: 40 - entrance: 93 - teleporter: [5, 8] - access: [] -- name: Aquaria Vendor House - id: 42 - game_objects: - - name: "Aquaria - Vendor" - object_id: 4 - type: "NPC" - access: [] - - name: "Aquaria - Vendor House Box" - object_id: 0x42 - type: "Box" - access: [] - links: - - target_room: 40 - entrance: 94 - teleporter: [40, 8] - access: [] - - target_room: 43 - entrance: 95 - teleporter: [47, 0] - access: [] -- name: Aquaria Gemini Room - id: 43 - game_objects: [] - links: - - target_room: 42 - entrance: 97 - teleporter: [48, 0] - access: [] - - target_room: 81 - entrance: 96 - teleporter: [72, 8] - access: ["GeminiCrest"] -- name: Aquaria INN - id: 44 - game_objects: [] - links: - - target_room: 40 - entrance: 98 - teleporter: [75, 8] - access: [] -- name: Wintry Cave 1F - East Ledge - id: 45 - game_objects: - - name: "Wintry Cave 1F - North Box" - object_id: 0x43 - type: "Box" - access: [] - - name: "Wintry Cave 1F - Entrance Box" - object_id: 0x46 - type: "Box" - access: [] - - name: "Wintry Cave 1F - Slippery Cliff Box" - object_id: 0x44 - type: "Box" - access: ["Claw"] - - name: "Wintry Cave 1F - Phoebe" - object_id: 5 - type: "NPC" - access: ["Phoebe1"] - links: - - target_room: 221 - entrance: 99 - teleporter: [49, 0] - access: [] - - target_room: 49 - entrance: 100 - teleporter: [14, 2] - access: ["Bomb"] - - target_room: 46 - access: ["Claw"] -- name: Wintry Cave 1F - Central Space - id: 46 - game_objects: - - name: "Wintry Cave 1F - Scenic Overlook Box" - object_id: 0x45 - type: "Box" - access: ["Claw"] - links: - - target_room: 45 - access: ["Claw"] - - target_room: 47 - access: ["Claw"] -- name: Wintry Cave 1F - West Ledge - id: 47 - game_objects: [] - links: - - target_room: 48 - entrance: 101 - teleporter: [15, 2] - access: ["Bomb"] - - target_room: 46 - access: ["Claw"] -- name: Wintry Cave 2F - id: 48 - game_objects: - - name: "Wintry Cave 2F - West Left Box" - object_id: 0x47 - type: "Box" - access: [] - - name: "Wintry Cave 2F - West Right Box" - object_id: 0x48 - type: "Box" - access: [] - - name: "Wintry Cave 2F - East Left Box" - object_id: 0x49 - type: "Box" - access: [] - - name: "Wintry Cave 2F - East Right Box" - object_id: 0x4A - type: "Box" - access: [] - links: - - target_room: 47 - entrance: 104 - teleporter: [97, 3] - access: [] - - target_room: 50 - entrance: 103 - teleporter: [50, 0] - access: [] -- name: Wintry Cave 3F Top - id: 49 - game_objects: - - name: "Wintry Cave 3F - West Box" - object_id: 0x4B - type: "Box" - access: [] - - name: "Wintry Cave 3F - East Box" - object_id: 0x4C - type: "Box" - access: [] - links: - - target_room: 45 - entrance: 105 - teleporter: [96, 3] - access: [] -- name: Wintry Cave 3F Bottom - id: 50 - game_objects: - - name: "Wintry Cave 3F - Squidite Chest" - object_id: 0x09 - type: "Chest" - access: ["Phanquid"] - - name: "Phanquid" - object_id: 0 - type: "Trigger" - on_trigger: ["Phanquid"] - access: [] - - name: "Wintry Cave 3F - Before Boss Box" - object_id: 0x4D - type: "Box" - access: [] - links: - - target_room: 48 - entrance: 106 - teleporter: [51, 0] - access: [] -- name: Life Temple - id: 51 - game_objects: - - name: "Life Temple - Box" - object_id: 0x4E - type: "Box" - access: [] - - name: "Life Temple - Mysterious Man" - object_id: 6 - type: "NPC" - access: [] - links: - - target_room: 222 - entrance: 107 - teleporter: [14, 6] - access: [] - - target_room: 39 - entrance: 108 - teleporter: [60, 8] - access: ["LibraCrest"] -- name: Fall Basin - id: 52 - game_objects: - - name: "Falls Basin - Snow Crab Chest" - object_id: 0x0A - type: "Chest" - access: ["FreezerCrab"] - - name: "Freezer Crab" - object_id: 0 - type: "Trigger" - on_trigger: ["FreezerCrab"] - access: [] - - name: "Falls Basin - Box" - object_id: 0x4F - type: "Box" - access: [] - links: - - target_room: 221 - entrance: 111 - teleporter: [53, 0] - access: [] -- name: Ice Pyramid B1 Taunt Room - id: 53 - game_objects: - - name: "Ice Pyramid B1 - Chest" - object_id: 0x0B - type: "Chest" - access: [] - - name: "Ice Pyramid B1 - West Box" - object_id: 0x50 - type: "Box" - access: [] - - name: "Ice Pyramid B1 - North Box" - object_id: 0x51 - type: "Box" - access: [] - - name: "Ice Pyramid B1 - East Box" - object_id: 0x52 - type: "Box" - access: [] - links: - - target_room: 68 - entrance: 113 - teleporter: [55, 0] - access: [] -- name: Ice Pyramid 1F Maze Lobby - id: 54 - game_objects: - - name: "Ice Pyramid 1F Statue" - object_id: 0 - type: "Trigger" - on_trigger: ["IcePyramid1FStatue"] - access: ["Sword"] - links: - - target_room: 221 - entrance: 114 - teleporter: [56, 0] - access: [] - - target_room: 55 - access: ["IcePyramid1FStatue"] -- name: Ice Pyramid 1F Maze - id: 55 - game_objects: - - name: "Ice Pyramid 1F - East Alcove Chest" - object_id: 0x0D - type: "Chest" - access: [] - - name: "Ice Pyramid 1F - Sandwiched Alcove Box" - object_id: 0x53 - type: "Box" - access: [] - - name: "Ice Pyramid 1F - Southwest Left Box" - object_id: 0x54 - type: "Box" - access: [] - - name: "Ice Pyramid 1F - Southwest Right Box" - object_id: 0x55 - type: "Box" - access: [] - links: - - target_room: 56 - entrance: 116 - teleporter: [57, 0] - access: [] - - target_room: 57 - entrance: 117 - teleporter: [58, 0] - access: [] - - target_room: 58 - entrance: 118 - teleporter: [59, 0] - access: [] - - target_room: 59 - entrance: 119 - teleporter: [60, 0] - access: [] - - target_room: 60 - entrance: 120 - teleporter: [61, 0] - access: [] - - target_room: 54 - access: ["IcePyramid1FStatue"] -- name: Ice Pyramid 2F South Tiled Room - id: 56 - game_objects: - - name: "Ice Pyramid 2F - South Side Glass Door Box" - object_id: 0x57 - type: "Box" - access: ["Sword"] - - name: "Ice Pyramid 2F - South Side East Box" - object_id: 0x5B - type: "Box" - access: [] - links: - - target_room: 55 - entrance: 122 - teleporter: [62, 0] - access: [] - - target_room: 61 - entrance: 123 - teleporter: [67, 0] - access: [] -- name: Ice Pyramid 2F West Room - id: 57 - game_objects: - - name: "Ice Pyramid 2F - Northwest Room Box" - object_id: 0x5A - type: "Box" - access: [] - links: - - target_room: 55 - entrance: 124 - teleporter: [63, 0] - access: [] -- name: Ice Pyramid 2F Center Room - id: 58 - game_objects: - - name: "Ice Pyramid 2F - Center Room Box" - object_id: 0x56 - type: "Box" - access: [] - links: - - target_room: 55 - entrance: 125 - teleporter: [64, 0] - access: [] -- name: Ice Pyramid 2F Small North Room - id: 59 - game_objects: - - name: "Ice Pyramid 2F - North Room Glass Door Box" - object_id: 0x58 - type: "Box" - access: ["Sword"] - links: - - target_room: 55 - entrance: 126 - teleporter: [65, 0] - access: [] -- name: Ice Pyramid 2F North Corridor - id: 60 - game_objects: - - name: "Ice Pyramid 2F - North Corridor Glass Door Box" - object_id: 0x59 - type: "Box" - access: ["Sword"] - links: - - target_room: 55 - entrance: 127 - teleporter: [66, 0] - access: [] - - target_room: 62 - entrance: 128 - teleporter: [68, 0] - access: [] -- name: Ice Pyramid 3F Two Boxes Room - id: 61 - game_objects: - - name: "Ice Pyramid 3F - Staircase Dead End Left Box" - object_id: 0x5E - type: "Box" - access: [] - - name: "Ice Pyramid 3F - Staircase Dead End Right Box" - object_id: 0x5F - type: "Box" - access: [] - links: - - target_room: 56 - entrance: 129 - teleporter: [69, 0] - access: [] -- name: Ice Pyramid 3F Main Loop - id: 62 - game_objects: - - name: "Ice Pyramid 3F - Inner Room North Box" - object_id: 0x5C - type: "Box" - access: [] - - name: "Ice Pyramid 3F - Inner Room South Box" - object_id: 0x5D - type: "Box" - access: [] - - name: "Ice Pyramid 3F - East Alcove Box" - object_id: 0x60 - type: "Box" - access: [] - - name: "Ice Pyramid 3F - Leapfrog Box" - object_id: 0x61 - type: "Box" - access: [] - - name: "Ice Pyramid 3F Statue" - object_id: 0 - type: "Trigger" - on_trigger: ["IcePyramid3FStatue"] - access: ["Sword"] - links: - - target_room: 60 - entrance: 130 - teleporter: [70, 0] - access: [] - - target_room: 63 - access: ["IcePyramid3FStatue"] -- name: Ice Pyramid 3F Blocked Room - id: 63 - game_objects: [] - links: - - target_room: 64 - entrance: 131 - teleporter: [71, 0] - access: [] - - target_room: 62 - access: ["IcePyramid3FStatue"] -- name: Ice Pyramid 4F Main Loop - id: 64 - game_objects: [] - links: - - target_room: 66 - entrance: 133 - teleporter: [73, 0] - access: [] - - target_room: 63 - entrance: 132 - teleporter: [72, 0] - access: [] - - target_room: 65 - access: ["IcePyramid4FStatue"] -- name: Ice Pyramid 4F Treasure Room - id: 65 - game_objects: - - name: "Ice Pyramid 4F - Chest" - object_id: 0x0C - type: "Chest" - access: [] - - name: "Ice Pyramid 4F - Northwest Box" - object_id: 0x62 - type: "Box" - access: [] - - name: "Ice Pyramid 4F - West Left Box" - object_id: 0x63 - type: "Box" - access: [] - - name: "Ice Pyramid 4F - West Right Box" - object_id: 0x64 - type: "Box" - access: [] - - name: "Ice Pyramid 4F - South Left Box" - object_id: 0x65 - type: "Box" - access: [] - - name: "Ice Pyramid 4F - South Right Box" - object_id: 0x66 - type: "Box" - access: [] - - name: "Ice Pyramid 4F - East Left Box" - object_id: 0x67 - type: "Box" - access: [] - - name: "Ice Pyramid 4F - East Right Box" - object_id: 0x68 - type: "Box" - access: [] - - name: "Ice Pyramid 4F Statue" - object_id: 0 - type: "Trigger" - on_trigger: ["IcePyramid4FStatue"] - access: ["Sword"] - links: - - target_room: 64 - access: ["IcePyramid4FStatue"] -- name: Ice Pyramid 5F Leap of Faith Room - id: 66 - game_objects: - - name: "Ice Pyramid 5F - Glass Door Left Box" - object_id: 0x69 - type: "Box" - access: ["IcePyramid5FStatue"] - - name: "Ice Pyramid 5F - West Ledge Box" - object_id: 0x6A - type: "Box" - access: [] - - name: "Ice Pyramid 5F - South Shelf Box" - object_id: 0x6B - type: "Box" - access: [] - - name: "Ice Pyramid 5F - South Leapfrog Box" - object_id: 0x6C - type: "Box" - access: [] - - name: "Ice Pyramid 5F - Glass Door Right Box" - object_id: 0x6D - type: "Box" - access: ["IcePyramid5FStatue"] - - name: "Ice Pyramid 5F - North Box" - object_id: 0x6E - type: "Box" - access: [] - links: - - target_room: 64 - entrance: 134 - teleporter: [74, 0] - access: [] - - target_room: 65 - access: [] - - target_room: 53 - access: ["Bomb", "Claw", "Sword"] -- name: Ice Pyramid 5F Stairs to Ice Golem - id: 67 - game_objects: - - name: "Ice Pyramid 5F Statue" - object_id: 0 - type: "Trigger" - on_trigger: ["IcePyramid5FStatue"] - access: ["Sword"] - links: - - target_room: 69 - entrance: 137 - teleporter: [76, 0] - access: [] - - target_room: 65 - access: [] - - target_room: 70 - entrance: 136 - teleporter: [75, 0] - access: [] -- name: Ice Pyramid Climbing Wall Room Lower Space - id: 68 - game_objects: [] - links: - - target_room: 53 - entrance: 139 - teleporter: [78, 0] - access: [] - - target_room: 69 - access: ["Claw"] -- name: Ice Pyramid Climbing Wall Room Upper Space - id: 69 - game_objects: [] - links: - - target_room: 67 - entrance: 140 - teleporter: [79, 0] - access: [] - - target_room: 68 - access: ["Claw"] -- name: Ice Pyramid Ice Golem Room - id: 70 - game_objects: - - name: "Ice Pyramid 6F - Ice Golem Chest" - object_id: 0x0E - type: "Chest" - access: ["IceGolem"] - - name: "Ice Golem" - object_id: 0 - type: "Trigger" - on_trigger: ["IceGolem"] - access: [] - links: - - target_room: 67 - entrance: 141 - teleporter: [80, 0] - access: [] - - target_room: 66 - access: [] -- name: Spencer Waterfall - id: 71 - game_objects: [] - links: - - target_room: 72 - entrance: 143 - teleporter: [81, 0] - access: [] - - target_room: 40 - entrance: 145 - teleporter: [82, 0] - access: [] - - target_room: 40 - entrance: 148 - teleporter: [83, 0] - access: [] -- name: Spencer Cave Normal Main - id: 72 - game_objects: - - name: "Spencer's Cave - Box" - object_id: 0x6F - type: "Box" - access: ["Claw"] - - name: "Spencer's Cave - Spencer" - object_id: 8 - type: "NPC" - access: [] - - name: "Spencer's Cave - Locked Chest" - object_id: 13 - type: "NPC" - access: ["VenusKey"] - links: - - target_room: 71 - entrance: 150 - teleporter: [85, 0] - access: [] -- name: Spencer Cave Normal South Ledge - id: 73 - game_objects: - - name: "Collapse Spencer's Cave" - object_id: 0 - type: "Trigger" - on_trigger: ["ShipLiberated"] - access: ["MegaGrenade"] - links: - - target_room: 227 - entrance: 151 - teleporter: [7, 6] - access: [] - - target_room: 203 - access: ["MegaGrenade"] -# - target_room: 72 # access to spencer? -# access: ["MegaGrenade"] -- name: Spencer Cave Caved In Main Loop - id: 203 - game_objects: [] - links: - - target_room: 73 - access: [] - - target_room: 207 - entrance: 156 - teleporter: [36, 8] - access: ["MobiusCrest"] - - target_room: 204 - access: ["Claw"] - - target_room: 205 - access: ["Bomb"] -- name: Spencer Cave Caved In Waters - id: 204 - game_objects: - - name: "Bomb Libra Block" - object_id: 0 - type: "Trigger" - on_trigger: ["SpencerCaveLibraBlockBombed"] - access: ["MegaGrenade", "Claw"] - links: - - target_room: 203 - access: ["Claw"] -- name: Spencer Cave Caved In Libra Nook - id: 205 - game_objects: [] - links: - - target_room: 206 - entrance: 153 - teleporter: [33, 8] - access: ["LibraCrest"] -- name: Spencer Cave Caved In Libra Corridor - id: 206 - game_objects: [] - links: - - target_room: 205 - entrance: 154 - teleporter: [34, 8] - access: ["LibraCrest"] - - target_room: 207 - access: ["SpencerCaveLibraBlockBombed"] -- name: Spencer Cave Caved In Mobius Chest - id: 207 - game_objects: - - name: "Spencer's Cave - Mobius Chest" - object_id: 0x0F - type: "Chest" - access: [] - links: - - target_room: 203 - entrance: 155 - teleporter: [35, 8] - access: ["MobiusCrest"] - - target_room: 206 - access: ["Bomb"] -- name: Wintry Temple Outer Room - id: 74 - game_objects: [] - links: - - target_room: 223 - entrance: 157 - teleporter: [15, 6] - access: [] -- name: Wintry Temple Inner Room - id: 75 - game_objects: - - name: "Wintry Temple - West Box" - object_id: 0x70 - type: "Box" - access: [] - - name: "Wintry Temple - North Box" - object_id: 0x71 - type: "Box" - access: [] - links: - - target_room: 92 - entrance: 158 - teleporter: [62, 8] - access: ["GeminiCrest"] -- name: Fireburg Upper Plaza - id: 76 - game_objects: [] - links: - - target_room: 224 - entrance: 159 - teleporter: [9, 6] - access: [] - - target_room: 80 - entrance: 163 - teleporter: [91, 0] - access: [] - - target_room: 77 - entrance: 164 - teleporter: [98, 8] # original value [16, 2] - access: [] - - target_room: 82 - entrance: 165 - teleporter: [96, 8] # original value [17, 2] - access: [] - - target_room: 208 - access: ["Claw"] -- name: Fireburg Lower Plaza - id: 208 - game_objects: - - name: "Fireburg - Hidden Tunnel Box" - object_id: 0x74 - type: "Box" - access: [] - links: - - target_room: 76 - access: ["Claw"] - - target_room: 78 - entrance: 166 - teleporter: [11, 8] - access: ["MultiKey"] -- name: Reuben's House - id: 77 - game_objects: - - name: "Fireburg - Reuben's House Arion" - object_id: 14 - type: "NPC" - access: ["ReubenDadSaved"] - - name: "Reuben Companion" - object_id: 0 - type: "Trigger" - on_trigger: ["Reuben1"] - access: [] - - name: "Fireburg - Reuben's House Box" - object_id: 0x75 - type: "Box" - access: [] - links: - - target_room: 76 - entrance: 167 - teleporter: [98, 3] - access: [] -- name: GrenadeMan's House - id: 78 - game_objects: - - name: "Fireburg - Locked House Man" - object_id: 12 - type: "NPC" - access: [] - links: - - target_room: 208 - entrance: 168 - teleporter: [9, 8] - access: ["MultiKey"] - - target_room: 79 - entrance: 169 - teleporter: [93, 0] - access: [] -- name: GrenadeMan's Mobius Room - id: 79 - game_objects: [] - links: - - target_room: 78 - entrance: 170 - teleporter: [94, 0] - access: [] - - target_room: 161 - entrance: 171 - teleporter: [54, 8] - access: ["MobiusCrest"] -- name: Fireburg Vendor House - id: 80 - game_objects: - - name: "Fireburg - Vendor" - object_id: 11 - type: "NPC" - access: [] - links: - - target_room: 76 - entrance: 172 - teleporter: [95, 0] - access: [] - - target_room: 81 - entrance: 173 - teleporter: [96, 0] - access: [] -- name: Fireburg Gemini Room - id: 81 - game_objects: [] - links: - - target_room: 80 - entrance: 174 - teleporter: [97, 0] - access: [] - - target_room: 43 - entrance: 175 - teleporter: [45, 8] - access: ["GeminiCrest"] -- name: Fireburg Hotel Lobby - id: 82 - game_objects: - - name: "Fireburg - Tristam" - object_id: 10 - type: "NPC" - access: ["Tristam", "TristamBoneItemGiven"] - links: - - target_room: 76 - entrance: 177 - teleporter: [99, 3] - access: [] - - target_room: 83 - entrance: 176 - teleporter: [213, 0] - access: [] -- name: Fireburg Hotel Beds - id: 83 - game_objects: [] - links: - - target_room: 82 - entrance: 178 - teleporter: [214, 0] - access: [] -- name: Mine Exterior North West Platforms - id: 84 - game_objects: [] - links: - - target_room: 224 - entrance: 179 - teleporter: [98, 0] - access: [] - - target_room: 88 - entrance: 181 - teleporter: [20, 2] - access: ["Bomb"] - - target_room: 85 - access: ["Claw"] - - target_room: 86 - access: ["Claw"] - - target_room: 87 - access: ["Claw"] -- name: Mine Exterior Central Ledge - id: 85 - game_objects: [] - links: - - target_room: 90 - entrance: 183 - teleporter: [22, 2] - access: ["Bomb"] - - target_room: 84 - access: ["Claw"] -- name: Mine Exterior North Ledge - id: 86 - game_objects: [] - links: - - target_room: 89 - entrance: 182 - teleporter: [21, 2] - access: ["Bomb"] - - target_room: 85 - access: ["Claw"] -- name: Mine Exterior South East Platforms - id: 87 - game_objects: - - name: "Jinn" - object_id: 0 - type: "Trigger" - on_trigger: ["Jinn"] - access: [] - links: - - target_room: 91 - entrance: 180 - teleporter: [99, 0] - access: ["Jinn"] - - target_room: 86 - access: [] - - target_room: 85 - access: ["Claw"] -- name: Mine Parallel Room - id: 88 - game_objects: - - name: "Mine - Parallel Room West Box" - object_id: 0x77 - type: "Box" - access: ["Claw"] - - name: "Mine - Parallel Room East Box" - object_id: 0x78 - type: "Box" - access: ["Claw"] - links: - - target_room: 84 - entrance: 185 - teleporter: [100, 3] - access: [] -- name: Mine Crescent Room - id: 89 - game_objects: - - name: "Mine - Crescent Room Chest" - object_id: 0x10 - type: "Chest" - access: [] - links: - - target_room: 86 - entrance: 186 - teleporter: [101, 3] - access: [] -- name: Mine Climbing Room - id: 90 - game_objects: - - name: "Mine - Glitchy Collision Cave Box" - object_id: 0x76 - type: "Box" - access: ["Claw"] - links: - - target_room: 85 - entrance: 187 - teleporter: [102, 3] - access: [] -- name: Mine Cliff - id: 91 - game_objects: - - name: "Mine - Cliff Southwest Box" - object_id: 0x79 - type: "Box" - access: [] - - name: "Mine - Cliff Northwest Box" - object_id: 0x7A - type: "Box" - access: [] - - name: "Mine - Cliff Northeast Box" - object_id: 0x7B - type: "Box" - access: [] - - name: "Mine - Cliff Southeast Box" - object_id: 0x7C - type: "Box" - access: [] - - name: "Mine - Reuben" - object_id: 7 - type: "NPC" - access: ["Reuben1"] - - name: "Reuben's dad Saved" - object_id: 0 - type: "Trigger" - on_trigger: ["ReubenDadSaved"] - access: ["MegaGrenade"] - links: - - target_room: 87 - entrance: 188 - teleporter: [100, 0] - access: [] -- name: Sealed Temple - id: 92 - game_objects: - - name: "Sealed Temple - West Box" - object_id: 0x7D - type: "Box" - access: [] - - name: "Sealed Temple - East Box" - object_id: 0x7E - type: "Box" - access: [] - links: - - target_room: 224 - entrance: 190 - teleporter: [16, 6] - access: [] - - target_room: 75 - entrance: 191 - teleporter: [63, 8] - access: ["GeminiCrest"] -- name: Volcano Base - id: 93 - game_objects: - - name: "Volcano - Base Chest" - object_id: 0x11 - type: "Chest" - access: [] - - name: "Volcano - Base West Box" - object_id: 0x7F - type: "Box" - access: [] - - name: "Volcano - Base East Left Box" - object_id: 0x80 - type: "Box" - access: [] - - name: "Volcano - Base East Right Box" - object_id: 0x81 - type: "Box" - access: [] - links: - - target_room: 224 - entrance: 192 - teleporter: [103, 0] - access: [] - - target_room: 98 - entrance: 196 - teleporter: [31, 8] - access: [] - - target_room: 96 - entrance: 197 - teleporter: [30, 8] - access: [] -- name: Volcano Top Left - id: 94 - game_objects: - - name: "Volcano - Medusa Chest" - object_id: 0x12 - type: "Chest" - access: ["Medusa"] - - name: "Medusa" - object_id: 0 - type: "Trigger" - on_trigger: ["Medusa"] - access: [] - - name: "Volcano - Behind Medusa Box" - object_id: 0x82 - type: "Box" - access: [] - links: - - target_room: 209 - entrance: 199 - teleporter: [26, 8] - access: [] -- name: Volcano Top Right - id: 95 - game_objects: - - name: "Volcano - Top of the Volcano Left Box" - object_id: 0x83 - type: "Box" - access: [] - - name: "Volcano - Top of the Volcano Right Box" - object_id: 0x84 - type: "Box" - access: [] - links: - - target_room: 99 - entrance: 200 - teleporter: [79, 8] - access: [] -- name: Volcano Right Path - id: 96 - game_objects: - - name: "Volcano - Right Path Box" - object_id: 0x87 - type: "Box" - access: [] - links: - - target_room: 93 - entrance: 201 - teleporter: [15, 8] - access: [] -- name: Volcano Left Path - id: 98 - game_objects: - - name: "Volcano - Left Path Box" - object_id: 0x86 - type: "Box" - access: [] - links: - - target_room: 93 - entrance: 204 - teleporter: [27, 8] - access: [] - - target_room: 99 - entrance: 202 - teleporter: [25, 2] - access: [] - - target_room: 209 - entrance: 203 - teleporter: [26, 2] - access: [] -- name: Volcano Cross Left-Right - id: 99 - game_objects: [] - links: - - target_room: 95 - entrance: 206 - teleporter: [29, 8] - access: [] - - target_room: 98 - entrance: 205 - teleporter: [103, 3] - access: [] -- name: Volcano Cross Right-Left - id: 209 - game_objects: - - name: "Volcano - Crossover Section Box" - object_id: 0x85 - type: "Box" - access: [] - links: - - target_room: 98 - entrance: 208 - teleporter: [104, 3] - access: [] - - target_room: 94 - entrance: 207 - teleporter: [28, 8] - access: [] -- name: Lava Dome Inner Ring Main Loop - id: 100 - game_objects: - - name: "Lava Dome - Exterior Caldera Near Switch Cliff Box" - object_id: 0x88 - type: "Box" - access: [] - - name: "Lava Dome - Exterior South Cliff Box" - object_id: 0x89 - type: "Box" - access: [] - links: - - target_room: 224 - entrance: 209 - teleporter: [104, 0] - access: [] - - target_room: 113 - entrance: 211 - teleporter: [105, 0] - access: [] - - target_room: 114 - entrance: 212 - teleporter: [106, 0] - access: [] - - target_room: 116 - entrance: 213 - teleporter: [108, 0] - access: [] - - target_room: 118 - entrance: 214 - teleporter: [111, 0] - access: [] -- name: Lava Dome Inner Ring Center Ledge - id: 101 - game_objects: - - name: "Lava Dome - Exterior Center Dropoff Ledge Box" - object_id: 0x8A - type: "Box" - access: [] - links: - - target_room: 115 - entrance: 215 - teleporter: [107, 0] - access: [] - - target_room: 100 - access: ["Claw"] -- name: Lava Dome Inner Ring Plate Ledge - id: 102 - game_objects: - - name: "Lava Dome Plate" - object_id: 0 - type: "Trigger" - on_trigger: ["LavaDomePlate"] - access: [] - links: - - target_room: 119 - entrance: 216 - teleporter: [109, 0] - access: [] -- name: Lava Dome Inner Ring Upper Ledge West - id: 103 - game_objects: [] - links: - - target_room: 111 - entrance: 219 - teleporter: [112, 0] - access: [] - - target_room: 108 - entrance: 220 - teleporter: [113, 0] - access: [] - - target_room: 104 - access: ["Claw"] - - target_room: 100 - access: ["Claw"] -- name: Lava Dome Inner Ring Upper Ledge East - id: 104 - game_objects: [] - links: - - target_room: 110 - entrance: 218 - teleporter: [110, 0] - access: [] - - target_room: 103 - access: ["Claw"] -- name: Lava Dome Inner Ring Big Door Ledge - id: 105 - game_objects: [] - links: - - target_room: 107 - entrance: 221 - teleporter: [114, 0] - access: [] - - target_room: 121 - entrance: 222 - teleporter: [29, 2] - access: ["LavaDomePlate"] -- name: Lava Dome Inner Ring Tiny Bottom Ledge - id: 106 - game_objects: - - name: "Lava Dome - Exterior Dead End Caldera Box" - object_id: 0x8B - type: "Box" - access: [] - links: - - target_room: 120 - entrance: 226 - teleporter: [115, 0] - access: [] -- name: Lava Dome Jump Maze II - id: 107 - game_objects: - - name: "Lava Dome - Gold Maze Northwest Box" - object_id: 0x8C - type: "Box" - access: [] - - name: "Lava Dome - Gold Maze Southwest Box" - object_id: 0xF6 - type: "Box" - access: [] - - name: "Lava Dome - Gold Maze Northeast Box" - object_id: 0xF7 - type: "Box" - access: [] - - name: "Lava Dome - Gold Maze North Box" - object_id: 0xF8 - type: "Box" - access: [] - - name: "Lava Dome - Gold Maze Center Box" - object_id: 0xF9 - type: "Box" - access: [] - - name: "Lava Dome - Gold Maze Southeast Box" - object_id: 0xFA - type: "Box" - access: [] - links: - - target_room: 105 - entrance: 227 - teleporter: [116, 0] - access: [] - - target_room: 108 - entrance: 228 - teleporter: [119, 0] - access: [] - - target_room: 120 - entrance: 229 - teleporter: [120, 0] - access: [] -- name: Lava Dome Up-Down Corridor - id: 108 - game_objects: [] - links: - - target_room: 107 - entrance: 231 - teleporter: [118, 0] - access: [] - - target_room: 103 - entrance: 230 - teleporter: [117, 0] - access: [] -- name: Lava Dome Jump Maze I - id: 109 - game_objects: - - name: "Lava Dome - Bare Maze Leapfrog Alcove North Box" - object_id: 0x8D - type: "Box" - access: [] - - name: "Lava Dome - Bare Maze Leapfrog Alcove South Box" - object_id: 0x8E - type: "Box" - access: [] - - name: "Lava Dome - Bare Maze Center Box" - object_id: 0x8F - type: "Box" - access: [] - - name: "Lava Dome - Bare Maze Southwest Box" - object_id: 0x90 - type: "Box" - access: [] - links: - - target_room: 118 - entrance: 232 - teleporter: [121, 0] - access: [] - - target_room: 111 - entrance: 233 - teleporter: [122, 0] - access: [] -- name: Lava Dome Pointless Room - id: 110 - game_objects: [] - links: - - target_room: 104 - entrance: 234 - teleporter: [123, 0] - access: [] -- name: Lava Dome Lower Moon Helm Room - id: 111 - game_objects: - - name: "Lava Dome - U-Bend Room North Box" - object_id: 0x92 - type: "Box" - access: [] - - name: "Lava Dome - U-Bend Room South Box" - object_id: 0x93 - type: "Box" - access: [] - links: - - target_room: 103 - entrance: 235 - teleporter: [124, 0] - access: [] - - target_room: 109 - entrance: 236 - teleporter: [125, 0] - access: [] -- name: Lava Dome Moon Helm Room - id: 112 - game_objects: - - name: "Lava Dome - Beyond River Room Chest" - object_id: 0x13 - type: "Chest" - access: [] - - name: "Lava Dome - Beyond River Room Box" - object_id: 0x91 - type: "Box" - access: [] - links: - - target_room: 117 - entrance: 237 - teleporter: [126, 0] - access: [] -- name: Lava Dome Three Jumps Room - id: 113 - game_objects: - - name: "Lava Dome - Three Jumps Room Box" - object_id: 0x96 - type: "Box" - access: [] - links: - - target_room: 100 - entrance: 238 - teleporter: [127, 0] - access: [] -- name: Lava Dome Life Chest Room Lower Ledge - id: 114 - game_objects: - - name: "Lava Dome - Gold Bar Room Boulder Chest" - object_id: 0x1C - type: "Chest" - access: ["MegaGrenade"] - links: - - target_room: 100 - entrance: 239 - teleporter: [128, 0] - access: [] - - target_room: 115 - access: ["Claw"] -- name: Lava Dome Life Chest Room Upper Ledge - id: 115 - game_objects: - - name: "Lava Dome - Gold Bar Room Leapfrog Alcove Box West" - object_id: 0x94 - type: "Box" - access: [] - - name: "Lava Dome - Gold Bar Room Leapfrog Alcove Box East" - object_id: 0x95 - type: "Box" - access: [] - links: - - target_room: 101 - entrance: 240 - teleporter: [129, 0] - access: [] - - target_room: 114 - access: ["Claw"] -- name: Lava Dome Big Jump Room Main Area - id: 116 - game_objects: - - name: "Lava Dome - Lava River Room North Box" - object_id: 0x98 - type: "Box" - access: [] - - name: "Lava Dome - Lava River Room East Box" - object_id: 0x99 - type: "Box" - access: [] - - name: "Lava Dome - Lava River Room South Box" - object_id: 0x9A - type: "Box" - access: [] - links: - - target_room: 100 - entrance: 241 - teleporter: [133, 0] - access: [] - - target_room: 119 - entrance: 243 - teleporter: [132, 0] - access: [] - - target_room: 117 - access: ["MegaGrenade"] -- name: Lava Dome Big Jump Room MegaGrenade Area - id: 117 - game_objects: [] - links: - - target_room: 112 - entrance: 242 - teleporter: [131, 0] - access: [] - - target_room: 116 - access: ["Bomb"] -- name: Lava Dome Split Corridor - id: 118 - game_objects: - - name: "Lava Dome - Split Corridor Box" - object_id: 0x97 - type: "Box" - access: [] - links: - - target_room: 109 - entrance: 244 - teleporter: [130, 0] - access: [] - - target_room: 100 - entrance: 245 - teleporter: [134, 0] - access: [] -- name: Lava Dome Plate Corridor - id: 119 - game_objects: [] - links: - - target_room: 102 - entrance: 246 - teleporter: [135, 0] - access: [] - - target_room: 116 - entrance: 247 - teleporter: [137, 0] - access: [] -- name: Lava Dome Four Boxes Stairs - id: 120 - game_objects: - - name: "Lava Dome - Caldera Stairway West Left Box" - object_id: 0x9B - type: "Box" - access: [] - - name: "Lava Dome - Caldera Stairway West Right Box" - object_id: 0x9C - type: "Box" - access: [] - - name: "Lava Dome - Caldera Stairway East Left Box" - object_id: 0x9D - type: "Box" - access: [] - - name: "Lava Dome - Caldera Stairway East Right Box" - object_id: 0x9E - type: "Box" - access: [] - links: - - target_room: 107 - entrance: 248 - teleporter: [136, 0] - access: [] - - target_room: 106 - entrance: 249 - teleporter: [16, 0] - access: [] -- name: Lava Dome Hydra Room - id: 121 - game_objects: - - name: "Lava Dome - Dualhead Hydra Chest" - object_id: 0x14 - type: "Chest" - access: ["DualheadHydra"] - - name: "Dualhead Hydra" - object_id: 0 - type: "Trigger" - on_trigger: ["DualheadHydra"] - access: [] - - name: "Lava Dome - Hydra Room Northwest Box" - object_id: 0x9F - type: "Box" - access: [] - - name: "Lava Dome - Hydra Room Southweast Box" - object_id: 0xA0 - type: "Box" - access: [] - links: - - target_room: 105 - entrance: 250 - teleporter: [105, 3] - access: [] - - target_room: 122 - entrance: 251 - teleporter: [138, 0] - access: ["DualheadHydra"] -- name: Lava Dome Escape Corridor - id: 122 - game_objects: [] - links: - - target_room: 121 - entrance: 253 - teleporter: [139, 0] - access: [] -- name: Rope Bridge - id: 123 - game_objects: - - name: "Rope Bridge - West Box" - object_id: 0xA3 - type: "Box" - access: [] - - name: "Rope Bridge - East Box" - object_id: 0xA4 - type: "Box" - access: [] - links: - - target_room: 226 - entrance: 255 - teleporter: [140, 0] - access: [] -- name: Alive Forest - id: 124 - game_objects: - - name: "Alive Forest - Tree Stump Chest" - object_id: 0x15 - type: "Chest" - access: ["Axe"] - - name: "Alive Forest - Near Entrance Box" - object_id: 0xA5 - type: "Box" - access: ["Axe"] - - name: "Alive Forest - After Bridge Box" - object_id: 0xA6 - type: "Box" - access: ["Axe"] - - name: "Alive Forest - Gemini Stump Box" - object_id: 0xA7 - type: "Box" - access: ["Axe"] - links: - - target_room: 226 - entrance: 272 - teleporter: [142, 0] - access: ["Axe"] - - target_room: 21 - entrance: 275 - teleporter: [64, 8] - access: ["LibraCrest", "Axe"] - - target_room: 22 - entrance: 276 - teleporter: [65, 8] - access: ["GeminiCrest", "Axe"] - - target_room: 23 - entrance: 277 - teleporter: [66, 8] - access: ["MobiusCrest", "Axe"] - - target_room: 125 - entrance: 274 - teleporter: [143, 0] - access: ["Axe"] -- name: Giant Tree 1F Main Area - id: 125 - game_objects: - - name: "Giant Tree 1F - Northwest Box" - object_id: 0xA8 - type: "Box" - access: [] - - name: "Giant Tree 1F - Southwest Box" - object_id: 0xA9 - type: "Box" - access: [] - - name: "Giant Tree 1F - Center Box" - object_id: 0xAA - type: "Box" - access: [] - - name: "Giant Tree 1F - East Box" - object_id: 0xAB - type: "Box" - access: [] - links: - - target_room: 124 - entrance: 278 - teleporter: [56, 1] # [49, 8] script restored if no map shuffling - access: [] - - target_room: 202 - access: ["DragonClaw"] -- name: Giant Tree 1F North Island - id: 202 - game_objects: [] - links: - - target_room: 127 - entrance: 280 - teleporter: [144, 0] - access: [] - - target_room: 125 - access: ["DragonClaw"] -- name: Giant Tree 1F Central Island - id: 126 - game_objects: [] - links: - - target_room: 202 - access: ["DragonClaw"] -- name: Giant Tree 2F Main Lobby - id: 127 - game_objects: - - name: "Giant Tree 2F - North Box" - object_id: 0xAC - type: "Box" - access: [] - links: - - target_room: 126 - access: ["DragonClaw"] - - target_room: 125 - entrance: 281 - teleporter: [145, 0] - access: [] - - target_room: 133 - entrance: 283 - teleporter: [149, 0] - access: [] - - target_room: 129 - access: ["DragonClaw"] -- name: Giant Tree 2F West Ledge - id: 128 - game_objects: - - name: "Giant Tree 2F - Dropdown Ledge Box" - object_id: 0xAE - type: "Box" - access: [] - links: - - target_room: 140 - entrance: 284 - teleporter: [147, 0] - access: ["Sword"] - - target_room: 130 - access: ["DragonClaw"] -- name: Giant Tree 2F Lower Area - id: 129 - game_objects: - - name: "Giant Tree 2F - South Box" - object_id: 0xAD - type: "Box" - access: [] - links: - - target_room: 130 - access: ["Claw"] - - target_room: 131 - access: ["Claw"] -- name: Giant Tree 2F Central Island - id: 130 - game_objects: [] - links: - - target_room: 129 - access: ["Claw"] - - target_room: 135 - entrance: 282 - teleporter: [146, 0] - access: ["Sword"] -- name: Giant Tree 2F East Ledge - id: 131 - game_objects: [] - links: - - target_room: 129 - access: ["Claw"] - - target_room: 130 - access: ["DragonClaw"] -- name: Giant Tree 2F Meteor Chest Room - id: 132 - game_objects: - - name: "Giant Tree 2F - Gidrah Chest" - object_id: 0x16 - type: "Chest" - access: [] - links: - - target_room: 133 - entrance: 285 - teleporter: [148, 0] - access: [] -- name: Giant Tree 2F Mushroom Room - id: 133 - game_objects: - - name: "Giant Tree 2F - Mushroom Tunnel West Box" - object_id: 0xAF - type: "Box" - access: ["Axe"] - - name: "Giant Tree 2F - Mushroom Tunnel East Box" - object_id: 0xB0 - type: "Box" - access: ["Axe"] - links: - - target_room: 127 - entrance: 286 - teleporter: [150, 0] - access: ["Axe"] - - target_room: 132 - entrance: 287 - teleporter: [151, 0] - access: ["Axe", "Gidrah"] -- name: Giant Tree 3F Central Island - id: 135 - game_objects: - - name: "Giant Tree 3F - Central Island Box" - object_id: 0xB3 - type: "Box" - access: [] - links: - - target_room: 130 - entrance: 288 - teleporter: [152, 0] - access: [] - - target_room: 136 - access: ["Claw"] - - target_room: 137 - access: ["DragonClaw"] -- name: Giant Tree 3F Central Area - id: 136 - game_objects: - - name: "Giant Tree 3F - Center North Box" - object_id: 0xB1 - type: "Box" - access: [] - - name: "Giant Tree 3F - Center West Box" - object_id: 0xB2 - type: "Box" - access: [] - links: - - target_room: 135 - access: ["Claw"] - - target_room: 127 - access: [] - - target_room: 131 - access: [] -- name: Giant Tree 3F Lower Ledge - id: 137 - game_objects: [] - links: - - target_room: 135 - access: ["DragonClaw"] - - target_room: 142 - entrance: 289 - teleporter: [153, 0] - access: ["Sword"] -- name: Giant Tree 3F West Area - id: 138 - game_objects: - - name: "Giant Tree 3F - West Side Box" - object_id: 0xB4 - type: "Box" - access: [] - links: - - target_room: 128 - access: [] - - target_room: 210 - entrance: 290 - teleporter: [154, 0] - access: [] -- name: Giant Tree 3F Middle Up Island - id: 139 - game_objects: [] - links: - - target_room: 136 - access: ["Claw"] -- name: Giant Tree 3F West Platform - id: 140 - game_objects: [] - links: - - target_room: 139 - access: ["Claw"] - - target_room: 141 - access: ["Claw"] - - target_room: 128 - entrance: 291 - teleporter: [155, 0] - access: [] -- name: Giant Tree 3F North Ledge - id: 141 - game_objects: [] - links: - - target_room: 143 - entrance: 292 - teleporter: [156, 0] - access: ["Sword"] - - target_room: 139 - access: ["Claw"] - - target_room: 136 - access: ["Claw"] -- name: Giant Tree Worm Room Upper Ledge - id: 142 - game_objects: - - name: "Giant Tree 3F - Worm Room North Box" - object_id: 0xB5 - type: "Box" - access: ["Axe"] - - name: "Giant Tree 3F - Worm Room South Box" - object_id: 0xB6 - type: "Box" - access: ["Axe"] - links: - - target_room: 137 - entrance: 293 - teleporter: [157, 0] - access: ["Axe"] - - target_room: 210 - access: ["Axe", "Claw"] -- name: Giant Tree Worm Room Lower Ledge - id: 210 - game_objects: [] - links: - - target_room: 138 - entrance: 294 - teleporter: [158, 0] - access: [] -- name: Giant Tree 4F Lower Floor - id: 143 - game_objects: [] - links: - - target_room: 141 - entrance: 295 - teleporter: [159, 0] - access: [] - - target_room: 148 - entrance: 296 - teleporter: [160, 0] - access: [] - - target_room: 148 - entrance: 297 - teleporter: [161, 0] - access: [] - - target_room: 147 - entrance: 298 - teleporter: [162, 0] - access: ["Sword"] -- name: Giant Tree 4F Middle Floor - id: 144 - game_objects: - - name: "Giant Tree 4F - Highest Platform North Box" - object_id: 0xB7 - type: "Box" - access: [] - - name: "Giant Tree 4F - Highest Platform South Box" - object_id: 0xB8 - type: "Box" - access: [] - links: - - target_room: 149 - entrance: 299 - teleporter: [163, 0] - access: [] - - target_room: 145 - access: ["Claw"] - - target_room: 146 - access: ["DragonClaw"] -- name: Giant Tree 4F Upper Floor - id: 145 - game_objects: [] - links: - - target_room: 150 - entrance: 300 - teleporter: [164, 0] - access: ["Sword"] - - target_room: 144 - access: ["Claw"] -- name: Giant Tree 4F South Ledge - id: 146 - game_objects: - - name: "Giant Tree 4F - Hook Ledge Northeast Box" - object_id: 0xB9 - type: "Box" - access: [] - - name: "Giant Tree 4F - Hook Ledge Southwest Box" - object_id: 0xBA - type: "Box" - access: [] - links: - - target_room: 144 - access: ["DragonClaw"] -- name: Giant Tree 4F Slime Room East Area - id: 147 - game_objects: - - name: "Giant Tree 4F - East Slime Room Box" - object_id: 0xBC - type: "Box" - access: ["Axe"] - links: - - target_room: 143 - entrance: 304 - teleporter: [168, 0] - access: [] -- name: Giant Tree 4F Slime Room West Area - id: 148 - game_objects: [] - links: - - target_room: 143 - entrance: 303 - teleporter: [167, 0] - access: ["Axe"] - - target_room: 143 - entrance: 302 - teleporter: [166, 0] - access: ["Axe"] - - target_room: 149 - access: ["Axe", "Claw"] -- name: Giant Tree 4F Slime Room Platform - id: 149 - game_objects: - - name: "Giant Tree 4F - West Slime Room Box" - object_id: 0xBB - type: "Box" - access: [] - links: - - target_room: 144 - entrance: 301 - teleporter: [165, 0] - access: [] - - target_room: 148 - access: ["Claw"] -- name: Giant Tree 5F Lower Area - id: 150 - game_objects: - - name: "Giant Tree 5F - Northwest Left Box" - object_id: 0xBD - type: "Box" - access: [] - - name: "Giant Tree 5F - Northwest Right Box" - object_id: 0xBE - type: "Box" - access: [] - - name: "Giant Tree 5F - South Left Box" - object_id: 0xBF - type: "Box" - access: [] - - name: "Giant Tree 5F - South Right Box" - object_id: 0xC0 - type: "Box" - access: [] - links: - - target_room: 145 - entrance: 305 - teleporter: [169, 0] - access: [] - - target_room: 151 - access: ["Claw"] - - target_room: 143 - access: [] -- name: Giant Tree 5F Gidrah Platform - id: 151 - game_objects: - - name: "Gidrah" - object_id: 0 - type: "Trigger" - on_trigger: ["Gidrah"] - access: [] - links: - - target_room: 150 - access: ["Claw"] -- name: Kaidge Temple Lower Ledge - id: 152 - game_objects: [] - links: - - target_room: 226 - entrance: 307 - teleporter: [18, 6] - access: [] - - target_room: 153 - access: ["Claw"] -- name: Kaidge Temple Upper Ledge - id: 153 - game_objects: - - name: "Kaidge Temple - Box" - object_id: 0xC1 - type: "Box" - access: [] - links: - - target_room: 185 - entrance: 308 - teleporter: [71, 8] - access: ["MobiusCrest"] - - target_room: 152 - access: ["Claw"] -- name: Windhole Temple - id: 154 - game_objects: - - name: "Windhole Temple - Box" - object_id: 0xC2 - type: "Box" - access: [] - links: - - target_room: 226 - entrance: 309 - teleporter: [173, 0] - access: [] -- name: Mount Gale - id: 155 - game_objects: - - name: "Mount Gale - Dullahan Chest" - object_id: 0x17 - type: "Chest" - access: ["DragonClaw", "Dullahan"] - - name: "Dullahan" - object_id: 0 - type: "Trigger" - on_trigger: ["Dullahan"] - access: ["DragonClaw"] - - name: "Mount Gale - East Box" - object_id: 0xC3 - type: "Box" - access: ["DragonClaw"] - - name: "Mount Gale - West Box" - object_id: 0xC4 - type: "Box" - access: [] - links: - - target_room: 226 - entrance: 310 - teleporter: [174, 0] - access: [] -- name: Windia - id: 156 - game_objects: [] - links: - - target_room: 226 - entrance: 312 - teleporter: [10, 6] - access: [] - - target_room: 157 - entrance: 320 - teleporter: [30, 5] - access: [] - - target_room: 163 - entrance: 321 - teleporter: [97, 8] - access: [] - - target_room: 165 - entrance: 322 - teleporter: [32, 5] - access: [] - - target_room: 159 - entrance: 323 - teleporter: [176, 4] - access: [] - - target_room: 160 - entrance: 324 - teleporter: [177, 4] - access: [] -- name: Otto's House - id: 157 - game_objects: - - name: "Otto" - object_id: 0 - type: "Trigger" - on_trigger: ["RainbowBridge"] - access: ["ThunderRock"] - links: - - target_room: 156 - entrance: 327 - teleporter: [106, 3] - access: [] - - target_room: 158 - entrance: 326 - teleporter: [33, 2] - access: [] -- name: Otto's Attic - id: 158 - game_objects: - - name: "Windia - Otto's Attic Box" - object_id: 0xC5 - type: "Box" - access: [] - links: - - target_room: 157 - entrance: 328 - teleporter: [107, 3] - access: [] -- name: Windia Kid House - id: 159 - game_objects: [] - links: - - target_room: 156 - entrance: 329 - teleporter: [178, 0] - access: [] - - target_room: 161 - entrance: 330 - teleporter: [180, 0] - access: [] -- name: Windia Old People House - id: 160 - game_objects: [] - links: - - target_room: 156 - entrance: 331 - teleporter: [179, 0] - access: [] - - target_room: 162 - entrance: 332 - teleporter: [181, 0] - access: [] -- name: Windia Kid House Basement - id: 161 - game_objects: [] - links: - - target_room: 159 - entrance: 333 - teleporter: [182, 0] - access: [] - - target_room: 79 - entrance: 334 - teleporter: [44, 8] - access: ["MobiusCrest"] -- name: Windia Old People House Basement - id: 162 - game_objects: - - name: "Windia - Mobius Basement West Box" - object_id: 0xC8 - type: "Box" - access: [] - - name: "Windia - Mobius Basement East Box" - object_id: 0xC9 - type: "Box" - access: [] - links: - - target_room: 160 - entrance: 335 - teleporter: [183, 0] - access: [] - - target_room: 186 - entrance: 336 - teleporter: [43, 8] - access: ["MobiusCrest"] -- name: Windia Inn Lobby - id: 163 - game_objects: [] - links: - - target_room: 156 - entrance: 338 - teleporter: [135, 3] - access: [] - - target_room: 164 - entrance: 337 - teleporter: [102, 8] - access: [] -- name: Windia Inn Beds - id: 164 - game_objects: - - name: "Windia - Inn Bedroom North Box" - object_id: 0xC6 - type: "Box" - access: [] - - name: "Windia - Inn Bedroom South Box" - object_id: 0xC7 - type: "Box" - access: [] - - name: "Windia - Kaeli" - object_id: 15 - type: "NPC" - access: ["Kaeli2"] - links: - - target_room: 163 - entrance: 339 - teleporter: [216, 0] - access: [] -- name: Windia Vendor House - id: 165 - game_objects: - - name: "Windia - Vendor" - object_id: 16 - type: "NPC" - access: [] - links: - - target_room: 156 - entrance: 340 - teleporter: [108, 3] - access: [] -- name: Pazuzu Tower 1F Main Lobby - id: 166 - game_objects: - - name: "Pazuzu 1F" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu1F"] - access: [] - links: - - target_room: 226 - entrance: 341 - teleporter: [184, 0] - access: [] - - target_room: 180 - entrance: 345 - teleporter: [185, 0] - access: [] -- name: Pazuzu Tower 1F Boxes Room - id: 167 - game_objects: - - name: "Pazuzu's Tower 1F - Descent Bomb Wall West Box" - object_id: 0xCA - type: "Box" - access: ["Bomb"] - - name: "Pazuzu's Tower 1F - Descent Bomb Wall Center Box" - object_id: 0xCB - type: "Box" - access: ["Bomb"] - - name: "Pazuzu's Tower 1F - Descent Bomb Wall East Box" - object_id: 0xCC - type: "Box" - access: ["Bomb"] - - name: "Pazuzu's Tower 1F - Descent Box" - object_id: 0xCD - type: "Box" - access: [] - links: - - target_room: 169 - entrance: 349 - teleporter: [187, 0] - access: [] -- name: Pazuzu Tower 1F Southern Platform - id: 168 - game_objects: [] - links: - - target_room: 169 - entrance: 346 - teleporter: [186, 0] - access: [] - - target_room: 166 - access: ["DragonClaw"] -- name: Pazuzu 2F - id: 169 - game_objects: - - name: "Pazuzu's Tower 2F - East Room West Box" - object_id: 0xCE - type: "Box" - access: [] - - name: "Pazuzu's Tower 2F - East Room East Box" - object_id: 0xCF - type: "Box" - access: [] - - name: "Pazuzu 2F Lock" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu2FLock"] - access: ["Axe"] - - name: "Pazuzu 2F" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu2F"] - access: ["Bomb"] - links: - - target_room: 183 - entrance: 350 - teleporter: [188, 0] - access: [] - - target_room: 168 - entrance: 351 - teleporter: [189, 0] - access: [] - - target_room: 167 - entrance: 352 - teleporter: [190, 0] - access: [] - - target_room: 171 - entrance: 353 - teleporter: [191, 0] - access: [] -- name: Pazuzu 3F Main Room - id: 170 - game_objects: - - name: "Pazuzu's Tower 3F - Guest Room West Box" - object_id: 0xD0 - type: "Box" - access: [] - - name: "Pazuzu's Tower 3F - Guest Room East Box" - object_id: 0xD1 - type: "Box" - access: [] - - name: "Pazuzu 3F" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu3F"] - access: [] - links: - - target_room: 180 - entrance: 356 - teleporter: [192, 0] - access: [] - - target_room: 181 - entrance: 357 - teleporter: [193, 0] - access: [] -- name: Pazuzu 3F Central Island - id: 171 - game_objects: [] - links: - - target_room: 169 - entrance: 360 - teleporter: [194, 0] - access: [] - - target_room: 170 - access: ["DragonClaw"] - - target_room: 172 - access: ["DragonClaw"] -- name: Pazuzu 3F Southern Island - id: 172 - game_objects: - - name: "Pazuzu's Tower 3F - South Ledge Box" - object_id: 0xD2 - type: "Box" - access: [] - links: - - target_room: 173 - entrance: 361 - teleporter: [195, 0] - access: [] - - target_room: 171 - access: ["DragonClaw"] -- name: Pazuzu 4F - id: 173 - game_objects: - - name: "Pazuzu's Tower 4F - Elevator West Box" - object_id: 0xD3 - type: "Box" - access: ["Bomb"] - - name: "Pazuzu's Tower 4F - Elevator East Box" - object_id: 0xD4 - type: "Box" - access: ["Bomb"] - - name: "Pazuzu's Tower 4F - East Storage Room Chest" - object_id: 0x18 - type: "Chest" - access: [] - - name: "Pazuzu 4F Lock" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu4FLock"] - access: ["Axe"] - - name: "Pazuzu 4F" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu4F"] - access: ["Bomb"] - links: - - target_room: 183 - entrance: 362 - teleporter: [196, 0] - access: [] - - target_room: 184 - entrance: 363 - teleporter: [197, 0] - access: [] - - target_room: 172 - entrance: 364 - teleporter: [198, 0] - access: [] - - target_room: 175 - entrance: 365 - teleporter: [199, 0] - access: [] -- name: Pazuzu 5F Pazuzu Loop - id: 174 - game_objects: - - name: "Pazuzu 5F" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu5F"] - access: [] - links: - - target_room: 181 - entrance: 368 - teleporter: [200, 0] - access: [] - - target_room: 182 - entrance: 369 - teleporter: [201, 0] - access: [] -- name: Pazuzu 5F Upper Loop - id: 175 - game_objects: - - name: "Pazuzu's Tower 5F - North Box" - object_id: 0xD5 - type: "Box" - access: [] - - name: "Pazuzu's Tower 5F - South Box" - object_id: 0xD6 - type: "Box" - access: [] - links: - - target_room: 173 - entrance: 370 - teleporter: [202, 0] - access: [] - - target_room: 176 - entrance: 371 - teleporter: [203, 0] - access: [] -- name: Pazuzu 6F - id: 176 - game_objects: - - name: "Pazuzu's Tower 6F - Box" - object_id: 0xD7 - type: "Box" - access: [] - - name: "Pazuzu's Tower 6F - Chest" - object_id: 0x19 - type: "Chest" - access: [] - - name: "Pazuzu 6F Lock" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu6FLock"] - access: ["Bomb", "Axe"] - - name: "Pazuzu 6F" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu6F"] - access: ["Bomb"] - links: - - target_room: 184 - entrance: 374 - teleporter: [204, 0] - access: [] - - target_room: 175 - entrance: 375 - teleporter: [205, 0] - access: [] - - target_room: 178 - entrance: 376 - teleporter: [206, 0] - access: [] - - target_room: 178 - entrance: 377 - teleporter: [207, 0] - access: [] -- name: Pazuzu 7F Southwest Area - id: 177 - game_objects: [] - links: - - target_room: 182 - entrance: 380 - teleporter: [26, 0] - access: [] - - target_room: 178 - access: ["DragonClaw"] -- name: Pazuzu 7F Rest of the Area - id: 178 - game_objects: [] - links: - - target_room: 177 - access: ["DragonClaw"] - - target_room: 176 - entrance: 381 - teleporter: [27, 0] - access: [] - - target_room: 176 - entrance: 382 - teleporter: [28, 0] - access: [] - - target_room: 179 - access: ["DragonClaw", "Pazuzu2FLock", "Pazuzu4FLock", "Pazuzu6FLock", "Pazuzu1F", "Pazuzu2F", "Pazuzu3F", "Pazuzu4F", "Pazuzu5F", "Pazuzu6F"] -- name: Pazuzu 7F Sky Room - id: 179 - game_objects: - - name: "Pazuzu's Tower 7F - Pazuzu Chest" - object_id: 0x1A - type: "Chest" - access: [] - - name: "Pazuzu" - object_id: 0 - type: "Trigger" - on_trigger: ["Pazuzu"] - access: ["Pazuzu2FLock", "Pazuzu4FLock", "Pazuzu6FLock", "Pazuzu1F", "Pazuzu2F", "Pazuzu3F", "Pazuzu4F", "Pazuzu5F", "Pazuzu6F"] - links: - - target_room: 178 - access: ["DragonClaw"] -- name: Pazuzu 1F to 3F - id: 180 - game_objects: [] - links: - - target_room: 166 - entrance: 385 - teleporter: [29, 0] - access: [] - - target_room: 170 - entrance: 386 - teleporter: [30, 0] - access: [] -- name: Pazuzu 3F to 5F - id: 181 - game_objects: [] - links: - - target_room: 170 - entrance: 387 - teleporter: [40, 0] - access: [] - - target_room: 174 - entrance: 388 - teleporter: [41, 0] - access: [] -- name: Pazuzu 5F to 7F - id: 182 - game_objects: [] - links: - - target_room: 174 - entrance: 389 - teleporter: [38, 0] - access: [] - - target_room: 177 - entrance: 390 - teleporter: [39, 0] - access: [] -- name: Pazuzu 2F to 4F - id: 183 - game_objects: [] - links: - - target_room: 169 - entrance: 391 - teleporter: [21, 0] - access: [] - - target_room: 173 - entrance: 392 - teleporter: [22, 0] - access: [] -- name: Pazuzu 4F to 6F - id: 184 - game_objects: [] - links: - - target_room: 173 - entrance: 393 - teleporter: [2, 0] - access: [] - - target_room: 176 - entrance: 394 - teleporter: [3, 0] - access: [] -- name: Light Temple - id: 185 - game_objects: - - name: "Light Temple - Box" - object_id: 0xD8 - type: "Box" - access: [] - links: - - target_room: 230 - entrance: 395 - teleporter: [19, 6] - access: [] - - target_room: 153 - entrance: 396 - teleporter: [70, 8] - access: ["MobiusCrest"] -- name: Ship Dock - id: 186 - game_objects: - - name: "Ship Dock Access" - object_id: 0 - type: "Trigger" - on_trigger: ["ShipDockAccess"] - access: [] - links: - - target_room: 228 - entrance: 399 - teleporter: [17, 6] - access: [] - - target_room: 162 - entrance: 397 - teleporter: [61, 8] - access: ["MobiusCrest"] -- name: Mac Ship Deck - id: 187 - game_objects: - - name: "Mac Ship Steering Wheel" - object_id: 00 - type: "Trigger" - on_trigger: ["ShipSteeringWheel"] - access: [] - - name: "Mac's Ship Deck - North Box" - object_id: 0xD9 - type: "Box" - access: [] - - name: "Mac's Ship Deck - Center Box" - object_id: 0xDA - type: "Box" - access: [] - - name: "Mac's Ship Deck - South Box" - object_id: 0xDB - type: "Box" - access: [] - links: - - target_room: 229 - entrance: 400 - teleporter: [37, 8] - access: [] - - target_room: 188 - entrance: 401 - teleporter: [50, 8] - access: [] - - target_room: 188 - entrance: 402 - teleporter: [51, 8] - access: [] - - target_room: 188 - entrance: 403 - teleporter: [52, 8] - access: [] - - target_room: 189 - entrance: 404 - teleporter: [53, 8] - access: [] -- name: Mac Ship B1 Outer Ring - id: 188 - game_objects: - - name: "Mac's Ship B1 - Northwest Hook Platform Box" - object_id: 0xE4 - type: "Box" - access: ["DragonClaw"] - - name: "Mac's Ship B1 - Center Hook Platform Box" - object_id: 0xE5 - type: "Box" - access: ["DragonClaw"] - links: - - target_room: 187 - entrance: 405 - teleporter: [208, 0] - access: [] - - target_room: 187 - entrance: 406 - teleporter: [175, 0] - access: [] - - target_room: 187 - entrance: 407 - teleporter: [172, 0] - access: [] - - target_room: 193 - entrance: 408 - teleporter: [88, 0] - access: [] - - target_room: 193 - access: [] -- name: Mac Ship B1 Square Room - id: 189 - game_objects: [] - links: - - target_room: 187 - entrance: 409 - teleporter: [141, 0] - access: [] - - target_room: 192 - entrance: 410 - teleporter: [87, 0] - access: [] -- name: Mac Ship B1 Central Corridor - id: 190 - game_objects: - - name: "Mac's Ship B1 - Central Corridor Box" - object_id: 0xE6 - type: "Box" - access: [] - links: - - target_room: 192 - entrance: 413 - teleporter: [86, 0] - access: [] - - target_room: 191 - entrance: 412 - teleporter: [102, 0] - access: [] - - target_room: 193 - access: [] -- name: Mac Ship B2 South Corridor - id: 191 - game_objects: [] - links: - - target_room: 190 - entrance: 415 - teleporter: [55, 8] - access: [] - - target_room: 194 - entrance: 414 - teleporter: [57, 1] - access: [] -- name: Mac Ship B2 North Corridor - id: 192 - game_objects: [] - links: - - target_room: 190 - entrance: 416 - teleporter: [56, 8] - access: [] - - target_room: 189 - entrance: 417 - teleporter: [57, 8] - access: [] -- name: Mac Ship B2 Outer Ring - id: 193 - game_objects: - - name: "Mac's Ship B2 - Barrel Room South Box" - object_id: 0xDF - type: "Box" - access: [] - - name: "Mac's Ship B2 - Barrel Room North Box" - object_id: 0xE0 - type: "Box" - access: [] - - name: "Mac's Ship B2 - Southwest Room Box" - object_id: 0xE1 - type: "Box" - access: [] - - name: "Mac's Ship B2 - Southeast Room Box" - object_id: 0xE2 - type: "Box" - access: [] - links: - - target_room: 188 - entrance: 418 - teleporter: [58, 8] - access: [] -- name: Mac Ship B1 Mac Room - id: 194 - game_objects: - - name: "Mac's Ship B1 - Mac Room Chest" - object_id: 0x1B - type: "Chest" - access: [] - - name: "Captain Mac" - object_id: 0 - type: "Trigger" - on_trigger: ["ShipLoaned"] - access: ["CaptainCap"] - links: - - target_room: 191 - entrance: 424 - teleporter: [101, 0] - access: [] -- name: Doom Castle Corridor of Destiny - id: 195 - game_objects: [] - links: - - target_room: 201 - entrance: 428 - teleporter: [84, 0] - access: [] - - target_room: 196 - entrance: 429 - teleporter: [35, 2] - access: [] - - target_room: 197 - entrance: 430 - teleporter: [209, 0] - access: ["StoneGolem"] - - target_room: 198 - entrance: 431 - teleporter: [211, 0] - access: ["StoneGolem", "TwinheadWyvern"] - - target_room: 199 - entrance: 432 - teleporter: [13, 2] - access: ["StoneGolem", "TwinheadWyvern", "Zuh"] -- name: Doom Castle Ice Floor - id: 196 - game_objects: - - name: "Doom Castle 4F - Northwest Room Box" - object_id: 0xE7 - type: "Box" - access: ["Sword", "DragonClaw"] - - name: "Doom Castle 4F - Southwest Room Box" - object_id: 0xE8 - type: "Box" - access: ["Sword", "DragonClaw"] - - name: "Doom Castle 4F - Northeast Room Box" - object_id: 0xE9 - type: "Box" - access: ["Sword"] - - name: "Doom Castle 4F - Southeast Room Box" - object_id: 0xEA - type: "Box" - access: ["Sword", "DragonClaw"] - - name: "Stone Golem" - object_id: 0 - type: "Trigger" - on_trigger: ["StoneGolem"] - access: ["Sword", "DragonClaw"] - links: - - target_room: 195 - entrance: 433 - teleporter: [109, 3] - access: [] -- name: Doom Castle Lava Floor - id: 197 - game_objects: - - name: "Doom Castle 5F - North Left Box" - object_id: 0xEB - type: "Box" - access: ["DragonClaw"] - - name: "Doom Castle 5F - North Right Box" - object_id: 0xEC - type: "Box" - access: ["DragonClaw"] - - name: "Doom Castle 5F - South Left Box" - object_id: 0xED - type: "Box" - access: ["DragonClaw"] - - name: "Doom Castle 5F - South Right Box" - object_id: 0xEE - type: "Box" - access: ["DragonClaw"] - - name: "Twinhead Wyvern" - object_id: 0 - type: "Trigger" - on_trigger: ["TwinheadWyvern"] - access: ["DragonClaw"] - links: - - target_room: 195 - entrance: 434 - teleporter: [210, 0] - access: [] -- name: Doom Castle Sky Floor - id: 198 - game_objects: - - name: "Doom Castle 6F - West Box" - object_id: 0xEF - type: "Box" - access: [] - - name: "Doom Castle 6F - East Box" - object_id: 0xF0 - type: "Box" - access: [] - - name: "Zuh" - object_id: 0 - type: "Trigger" - on_trigger: ["Zuh"] - access: ["DragonClaw"] - links: - - target_room: 195 - entrance: 435 - teleporter: [212, 0] - access: [] - - target_room: 197 - access: [] -- name: Doom Castle Hero Room - id: 199 - game_objects: - - name: "Doom Castle Hero Chest 01" - object_id: 0xF2 - type: "Chest" - access: [] - - name: "Doom Castle Hero Chest 02" - object_id: 0xF3 - type: "Chest" - access: [] - - name: "Doom Castle Hero Chest 03" - object_id: 0xF4 - type: "Chest" - access: [] - - name: "Doom Castle Hero Chest 04" - object_id: 0xF5 - type: "Chest" - access: [] - links: - - target_room: 200 - entrance: 436 - teleporter: [54, 0] - access: [] - - target_room: 195 - entrance: 441 - teleporter: [110, 3] - access: [] -- name: Doom Castle Dark King Room - id: 200 - game_objects: [] - links: - - target_room: 199 - entrance: 442 - teleporter: [52, 0] - access: [] diff --git a/worlds/hk/Options.py b/worlds/hk/Options.py index 38be2cd794a..e2602036a24 100644 --- a/worlds/hk/Options.py +++ b/worlds/hk/Options.py @@ -1,10 +1,12 @@ import typing import re +from dataclasses import dataclass, make_dataclass + from .ExtractedData import logic_options, starts, pool_options from .Rules import cost_terms from schema import And, Schema, Optional -from Options import Option, DefaultOnToggle, Toggle, Choice, Range, OptionDict, NamedRange, DeathLink +from Options import Option, DefaultOnToggle, Toggle, Choice, Range, OptionDict, NamedRange, DeathLink, PerGameCommonOptions from .Charms import vanilla_costs, names as charm_names if typing.TYPE_CHECKING: @@ -538,3 +540,5 @@ class CostSanityHybridChance(Range): }, **cost_sanity_weights } + +HKOptions = make_dataclass("HKOptions", [(name, option) for name, option in hollow_knight_options.items()], bases=(PerGameCommonOptions,)) diff --git a/worlds/hk/Rules.py b/worlds/hk/Rules.py index a3c7e13cf02..e162e1dfa81 100644 --- a/worlds/hk/Rules.py +++ b/worlds/hk/Rules.py @@ -49,3 +49,42 @@ def set_rules(hk_world: World): if term == "GEO": # No geo logic! continue add_rule(location, lambda state, term=term, amount=amount: state.count(term, player) >= amount) + + +def _hk_nail_combat(state, player) -> bool: + return state.has_any({'LEFTSLASH', 'RIGHTSLASH', 'UPSLASH'}, player) + + +def _hk_can_beat_thk(state, player) -> bool: + return ( + state.has('Opened_Black_Egg_Temple', player) + and (state.count('FIREBALL', player) + state.count('SCREAM', player) + state.count('QUAKE', player)) > 1 + and _hk_nail_combat(state, player) + and ( + state.has_any({'LEFTDASH', 'RIGHTDASH'}, player) + or state._hk_option(player, 'ProficientCombat') + ) + and state.has('FOCUS', player) + ) + + +def _hk_siblings_ending(state, player) -> bool: + return _hk_can_beat_thk(state, player) and state.has('WHITEFRAGMENT', player, 3) + + +def _hk_can_beat_radiance(state, player) -> bool: + return ( + state.has('Opened_Black_Egg_Temple', player) + and _hk_nail_combat(state, player) + and state.has('WHITEFRAGMENT', player, 3) + and state.has('DREAMNAIL', player) + and ( + (state.has('LEFTCLAW', player) and state.has('RIGHTCLAW', player)) + or state.has('WINGS', player) + ) + and (state.count('FIREBALL', player) + state.count('SCREAM', player) + state.count('QUAKE', player)) > 1 + and ( + (state.has('LEFTDASH', player, 2) and state.has('RIGHTDASH', player, 2)) # Both Shade Cloaks + or (state._hk_option(player, 'ProficientCombat') and state.has('QUAKE', player)) # or Dive + ) + ) diff --git a/worlds/hk/__init__.py b/worlds/hk/__init__.py index 78287305df5..e5065876ddf 100644 --- a/worlds/hk/__init__.py +++ b/worlds/hk/__init__.py @@ -10,9 +10,9 @@ from .Items import item_table, lookup_type_to_names, item_name_groups from .Regions import create_regions -from .Rules import set_rules, cost_terms +from .Rules import set_rules, cost_terms, _hk_can_beat_thk, _hk_siblings_ending, _hk_can_beat_radiance from .Options import hollow_knight_options, hollow_knight_randomize_options, Goal, WhitePalace, CostSanity, \ - shop_to_option + shop_to_option, HKOptions from .ExtractedData import locations, starts, multi_locations, location_to_region_lookup, \ event_names, item_effects, connectors, one_ways, vanilla_shop_costs, vanilla_location_costs from .Charms import names as charm_names @@ -142,7 +142,8 @@ class HKWorld(World): As the enigmatic Knight, you’ll traverse the depths, unravel its mysteries and conquer its evils. """ # from https://www.hollowknight.com game: str = "Hollow Knight" - option_definitions = hollow_knight_options + options_dataclass = HKOptions + options: HKOptions web = HKWeb() @@ -155,8 +156,8 @@ class HKWorld(World): charm_costs: typing.List[int] cached_filler_items = {} - def __init__(self, world, player): - super(HKWorld, self).__init__(world, player) + def __init__(self, multiworld, player): + super(HKWorld, self).__init__(multiworld, player) self.created_multi_locations: typing.Dict[str, typing.List[HKLocation]] = { location: list() for location in multi_locations } @@ -165,29 +166,29 @@ def __init__(self, world, player): self.vanilla_shop_costs = deepcopy(vanilla_shop_costs) def generate_early(self): - world = self.multiworld - charm_costs = world.RandomCharmCosts[self.player].get_costs(world.random) - self.charm_costs = world.PlandoCharmCosts[self.player].get_costs(charm_costs) - # world.exclude_locations[self.player].value.update(white_palace_locations) + options = self.options + charm_costs = options.RandomCharmCosts.get_costs(self.random) + self.charm_costs = options.PlandoCharmCosts.get_costs(charm_costs) + # options.exclude_locations.value.update(white_palace_locations) for term, data in cost_terms.items(): - mini = getattr(world, f"Minimum{data.option}Price")[self.player] - maxi = getattr(world, f"Maximum{data.option}Price")[self.player] + mini = getattr(options, f"Minimum{data.option}Price") + maxi = getattr(options, f"Maximum{data.option}Price") # if minimum > maximum, set minimum to maximum mini.value = min(mini.value, maxi.value) self.ranges[term] = mini.value, maxi.value - world.push_precollected(HKItem(starts[world.StartLocation[self.player].current_key], + self.multiworld.push_precollected(HKItem(starts[options.StartLocation.current_key], True, None, "Event", self.player)) def white_palace_exclusions(self): exclusions = set() - wp = self.multiworld.WhitePalace[self.player] + wp = self.options.WhitePalace if wp <= WhitePalace.option_nopathofpain: exclusions.update(path_of_pain_locations) if wp <= WhitePalace.option_kingfragment: exclusions.update(white_palace_checks) if wp == WhitePalace.option_exclude: exclusions.add("King_Fragment") - if self.multiworld.RandomizeCharms[self.player]: + if self.options.RandomizeCharms: # If charms are randomized, this will be junk-filled -- so transitions and events are not progression exclusions.update(white_palace_transitions) exclusions.update(white_palace_events) @@ -200,7 +201,7 @@ def create_regions(self): # check for any goal that godhome events are relevant to all_event_names = event_names.copy() - if self.multiworld.Goal[self.player] in [Goal.option_godhome, Goal.option_godhome_flower]: + if self.options.Goal in [Goal.option_godhome, Goal.option_godhome_flower]: from .GodhomeData import godhome_event_names all_event_names.update(set(godhome_event_names)) @@ -230,12 +231,12 @@ def create_items(self): pool: typing.List[HKItem] = [] wp_exclusions = self.white_palace_exclusions() junk_replace: typing.Set[str] = set() - if self.multiworld.RemoveSpellUpgrades[self.player]: + if self.options.RemoveSpellUpgrades: junk_replace.update(("Abyss_Shriek", "Shade_Soul", "Descending_Dark")) randomized_starting_items = set() for attr, items in randomizable_starting_items.items(): - if getattr(self.multiworld, attr)[self.player]: + if getattr(self.options, attr): randomized_starting_items.update(items) # noinspection PyShadowingNames @@ -257,7 +258,7 @@ def _add(item_name: str, location_name: str, randomized: bool): if item_name in junk_replace: item_name = self.get_filler_item_name() - item = self.create_item(item_name) if not vanilla or location_name == "Start" or self.multiworld.AddUnshuffledLocations[self.player] else self.create_event(item_name) + item = self.create_item(item_name) if not vanilla or location_name == "Start" or self.options.AddUnshuffledLocations else self.create_event(item_name) if location_name == "Start": if item_name in randomized_starting_items: @@ -281,55 +282,55 @@ def _add(item_name: str, location_name: str, randomized: bool): location.progress_type = LocationProgressType.EXCLUDED for option_key, option in hollow_knight_randomize_options.items(): - randomized = getattr(self.multiworld, option_key)[self.player] - if all([not randomized, option_key in logicless_options, not self.multiworld.AddUnshuffledLocations[self.player]]): + randomized = getattr(self.options, option_key) + if all([not randomized, option_key in logicless_options, not self.options.AddUnshuffledLocations]): continue for item_name, location_name in zip(option.items, option.locations): if item_name in junk_replace: item_name = self.get_filler_item_name() - if (item_name == "Crystal_Heart" and self.multiworld.SplitCrystalHeart[self.player]) or \ - (item_name == "Mothwing_Cloak" and self.multiworld.SplitMothwingCloak[self.player]): + if (item_name == "Crystal_Heart" and self.options.SplitCrystalHeart) or \ + (item_name == "Mothwing_Cloak" and self.options.SplitMothwingCloak): _add("Left_" + item_name, location_name, randomized) _add("Right_" + item_name, "Split_" + location_name, randomized) continue - if item_name == "Mantis_Claw" and self.multiworld.SplitMantisClaw[self.player]: + if item_name == "Mantis_Claw" and self.options.SplitMantisClaw: _add("Left_" + item_name, "Left_" + location_name, randomized) _add("Right_" + item_name, "Right_" + location_name, randomized) continue - if item_name == "Shade_Cloak" and self.multiworld.SplitMothwingCloak[self.player]: - if self.multiworld.random.randint(0, 1): + if item_name == "Shade_Cloak" and self.options.SplitMothwingCloak: + if self.random.randint(0, 1): item_name = "Left_Mothwing_Cloak" else: item_name = "Right_Mothwing_Cloak" - if item_name == "Grimmchild2" and self.multiworld.RandomizeGrimmkinFlames[self.player] and self.multiworld.RandomizeCharms[self.player]: + if item_name == "Grimmchild2" and self.options.RandomizeGrimmkinFlames and self.options.RandomizeCharms: _add("Grimmchild1", location_name, randomized) continue _add(item_name, location_name, randomized) - if self.multiworld.RandomizeElevatorPass[self.player]: + if self.options.RandomizeElevatorPass: randomized = True _add("Elevator_Pass", "Elevator_Pass", randomized) for shop, locations in self.created_multi_locations.items(): - for _ in range(len(locations), getattr(self.multiworld, shop_to_option[shop])[self.player].value): + for _ in range(len(locations), getattr(self.options, shop_to_option[shop]).value): loc = self.create_location(shop) unfilled_locations += 1 # Balance the pool item_count = len(pool) - additional_shop_items = max(item_count - unfilled_locations, self.multiworld.ExtraShopSlots[self.player].value) + additional_shop_items = max(item_count - unfilled_locations, self.options.ExtraShopSlots.value) # Add additional shop items, as needed. if additional_shop_items > 0: shops = list(shop for shop, locations in self.created_multi_locations.items() if len(locations) < 16) - if not self.multiworld.EggShopSlots[self.player].value: # No eggshop, so don't place items there + if not self.options.EggShopSlots: # No eggshop, so don't place items there shops.remove('Egg_Shop') if shops: for _ in range(additional_shop_items): - shop = self.multiworld.random.choice(shops) + shop = self.random.choice(shops) loc = self.create_location(shop) unfilled_locations += 1 if len(self.created_multi_locations[shop]) >= 16: @@ -355,7 +356,7 @@ def sort_shops_by_cost(self): loc.costs = costs def apply_costsanity(self): - setting = self.multiworld.CostSanity[self.player].value + setting = self.options.CostSanity.value if not setting: return # noop @@ -369,10 +370,10 @@ def _compute_weights(weights: dict, desc: str) -> typing.Dict[str, int]: return {k: v for k, v in weights.items() if v} - random = self.multiworld.random - hybrid_chance = getattr(self.multiworld, f"CostSanityHybridChance")[self.player].value + random = self.random + hybrid_chance = getattr(self.options, f"CostSanityHybridChance").value weights = { - data.term: getattr(self.multiworld, f"CostSanity{data.option}Weight")[self.player].value + data.term: getattr(self.options, f"CostSanity{data.option}Weight").value for data in cost_terms.values() } weights_geoless = dict(weights) @@ -427,22 +428,22 @@ def _compute_weights(weights: dict, desc: str) -> typing.Dict[str, int]: location.sort_costs() def set_rules(self): - world = self.multiworld + multiworld = self.multiworld player = self.player - goal = world.Goal[player] + goal = self.options.Goal if goal == Goal.option_hollowknight: - world.completion_condition[player] = lambda state: state._hk_can_beat_thk(player) + multiworld.completion_condition[player] = lambda state: _hk_can_beat_thk(state, player) elif goal == Goal.option_siblings: - world.completion_condition[player] = lambda state: state._hk_siblings_ending(player) + multiworld.completion_condition[player] = lambda state: _hk_siblings_ending(state, player) elif goal == Goal.option_radiance: - world.completion_condition[player] = lambda state: state._hk_can_beat_radiance(player) + multiworld.completion_condition[player] = lambda state: _hk_can_beat_radiance(state, player) elif goal == Goal.option_godhome: - world.completion_condition[player] = lambda state: state.count("Defeated_Pantheon_5", player) + multiworld.completion_condition[player] = lambda state: state.count("Defeated_Pantheon_5", player) elif goal == Goal.option_godhome_flower: - world.completion_condition[player] = lambda state: state.count("Godhome_Flower_Quest", player) + multiworld.completion_condition[player] = lambda state: state.count("Godhome_Flower_Quest", player) else: # Any goal - world.completion_condition[player] = lambda state: state._hk_can_beat_thk(player) or state._hk_can_beat_radiance(player) + multiworld.completion_condition[player] = lambda state: _hk_can_beat_thk(state, player) or _hk_can_beat_radiance(state, player) set_rules(self) @@ -450,8 +451,8 @@ def fill_slot_data(self): slot_data = {} options = slot_data["options"] = {} - for option_name in self.option_definitions: - option = getattr(self.multiworld, option_name)[self.player] + for option_name in hollow_knight_options: + option = getattr(self.options, option_name) try: optionvalue = int(option.value) except TypeError: @@ -460,10 +461,10 @@ def fill_slot_data(self): options[option_name] = optionvalue # 32 bit int - slot_data["seed"] = self.multiworld.per_slot_randoms[self.player].randint(-2147483647, 2147483646) + slot_data["seed"] = self.random.randint(-2147483647, 2147483646) # Backwards compatibility for shop cost data (HKAP < 0.1.0) - if not self.multiworld.CostSanity[self.player]: + if not self.options.CostSanity: for shop, terms in shop_cost_types.items(): unit = cost_terms[next(iter(terms))].option if unit == "Geo": @@ -498,7 +499,7 @@ def create_location(self, name: str, vanilla=False) -> HKLocation: basename = name if name in shop_cost_types: costs = { - term: self.multiworld.random.randint(*self.ranges[term]) + term: self.random.randint(*self.ranges[term]) for term in shop_cost_types[name] } elif name in vanilla_location_costs: @@ -512,7 +513,7 @@ def create_location(self, name: str, vanilla=False) -> HKLocation: region = self.multiworld.get_region("Menu", self.player) - if vanilla and not self.multiworld.AddUnshuffledLocations[self.player]: + if vanilla and not self.options.AddUnshuffledLocations: loc = HKLocation(self.player, name, None, region, costs=costs, vanilla=vanilla, basename=basename) @@ -554,31 +555,32 @@ def remove(self, state, item: HKItem) -> bool: for effect_name, effect_value in item_effects.get(item.name, {}).items(): if state.prog_items[item.player][effect_name] == effect_value: del state.prog_items[item.player][effect_name] - state.prog_items[item.player][effect_name] -= effect_value + else: + state.prog_items[item.player][effect_name] -= effect_value return change @classmethod - def stage_write_spoiler(cls, world: MultiWorld, spoiler_handle): - hk_players = world.get_game_players(cls.game) + def stage_write_spoiler(cls, multiworld: MultiWorld, spoiler_handle): + hk_players = multiworld.get_game_players(cls.game) spoiler_handle.write('\n\nCharm Notches:') for player in hk_players: - name = world.get_player_name(player) + name = multiworld.get_player_name(player) spoiler_handle.write(f'\n{name}\n') - hk_world: HKWorld = world.worlds[player] + hk_world: HKWorld = multiworld.worlds[player] for charm_number, cost in enumerate(hk_world.charm_costs): spoiler_handle.write(f"\n{charm_names[charm_number]}: {cost}") spoiler_handle.write('\n\nShop Prices:') for player in hk_players: - name = world.get_player_name(player) + name = multiworld.get_player_name(player) spoiler_handle.write(f'\n{name}\n') - hk_world: HKWorld = world.worlds[player] + hk_world: HKWorld = multiworld.worlds[player] - if world.CostSanity[player].value: + if hk_world.options.CostSanity: for loc in sorted( ( - loc for loc in itertools.chain(*(region.locations for region in world.get_regions(player))) + loc for loc in itertools.chain(*(region.locations for region in multiworld.get_regions(player))) if loc.costs ), key=operator.attrgetter('name') ): @@ -602,15 +604,15 @@ def get_filler_item_name(self) -> str: 'RandomizeGeoRocks', 'RandomizeSoulTotems', 'RandomizeLoreTablets', 'RandomizeJunkPitChests', 'RandomizeRancidEggs' ): - if getattr(self.multiworld, group): + if getattr(self.options, group): fillers.extend(item for item in hollow_knight_randomize_options[group].items if item not in exclusions) self.cached_filler_items[self.player] = fillers - return self.multiworld.random.choice(self.cached_filler_items[self.player]) + return self.random.choice(self.cached_filler_items[self.player]) -def create_region(world: MultiWorld, player: int, name: str, location_names=None) -> Region: - ret = Region(name, player, world) +def create_region(multiworld: MultiWorld, player: int, name: str, location_names=None) -> Region: + ret = Region(name, player, multiworld) if location_names: for location in location_names: loc_id = HKWorld.location_name_to_id.get(location, None) @@ -683,42 +685,7 @@ def _hk_notches(self, player: int, *notches: int) -> int: return sum(self.multiworld.worlds[player].charm_costs[notch] for notch in notches) def _hk_option(self, player: int, option_name: str) -> int: - return getattr(self.multiworld, option_name)[player].value + return getattr(self.multiworld.worlds[player].options, option_name).value def _hk_start(self, player, start_location: str) -> bool: - return self.multiworld.StartLocation[player] == start_location - - def _hk_nail_combat(self, player: int) -> bool: - return self.has_any({'LEFTSLASH', 'RIGHTSLASH', 'UPSLASH'}, player) - - def _hk_can_beat_thk(self, player: int) -> bool: - return ( - self.has('Opened_Black_Egg_Temple', player) - and (self.count('FIREBALL', player) + self.count('SCREAM', player) + self.count('QUAKE', player)) > 1 - and self._hk_nail_combat(player) - and ( - self.has_any({'LEFTDASH', 'RIGHTDASH'}, player) - or self._hk_option(player, 'ProficientCombat') - ) - and self.has('FOCUS', player) - ) - - def _hk_siblings_ending(self, player: int) -> bool: - return self._hk_can_beat_thk(player) and self.has('WHITEFRAGMENT', player, 3) - - def _hk_can_beat_radiance(self, player: int) -> bool: - return ( - self.has('Opened_Black_Egg_Temple', player) - and self._hk_nail_combat(player) - and self.has('WHITEFRAGMENT', player, 3) - and self.has('DREAMNAIL', player) - and ( - (self.has('LEFTCLAW', player) and self.has('RIGHTCLAW', player)) - or self.has('WINGS', player) - ) - and (self.count('FIREBALL', player) + self.count('SCREAM', player) + self.count('QUAKE', player)) > 1 - and ( - (self.has('LEFTDASH', player, 2) and self.has('RIGHTDASH', player, 2)) # Both Shade Cloaks - or (self._hk_option(player, 'ProficientCombat') and self.has('QUAKE', player)) # or Dive - ) - ) + return self.multiworld.worlds[player].options.StartLocation == start_location diff --git a/worlds/kh2/Client.py b/worlds/kh2/Client.py index 513d85257b9..e2d2338b765 100644 --- a/worlds/kh2/Client.py +++ b/worlds/kh2/Client.py @@ -116,12 +116,19 @@ def __init__(self, server_address, password): # self.inBattle = 0x2A0EAC4 + 0x40 # self.onDeath = 0xAB9078 # PC Address anchors - self.Now = 0x0714DB8 - self.Save = 0x09A70B0 + # self.Now = 0x0714DB8 old address + # epic addresses + self.Now = 0x0716DF8 + self.Save = 0x09A92F0 + self.Journal = 0x743260 + self.Shop = 0x743350 + self.Slot1 = 0x2A22FD8 # self.Sys3 = 0x2A59DF0 # self.Bt10 = 0x2A74880 # self.BtlEnd = 0x2A0D3E0 - self.Slot1 = 0x2A20C98 + # self.Slot1 = 0x2A20C98 old address + + self.kh2_game_version = None # can be egs or steam self.chest_set = set(exclusion_table["Chests"]) self.keyblade_set = set(CheckDupingItems["Weapons"]["Keyblades"]) @@ -228,6 +235,9 @@ def kh2_read_int(self, address): def kh2_write_int(self, address, value): self.kh2.write_int(self.kh2.base_address + address, value) + def kh2_read_string(self, address, length): + return self.kh2.read_string(self.kh2.base_address + address, length) + def on_package(self, cmd: str, args: dict): if cmd in {"RoomInfo"}: self.kh2seedname = args['seed_name'] @@ -367,10 +377,26 @@ def on_package(self, cmd: str, args: dict): for weapon_location in all_weapon_slot: all_weapon_location_id.append(self.kh2_loc_name_to_id[weapon_location]) self.all_weapon_location_id = set(all_weapon_location_id) + try: self.kh2 = pymem.Pymem(process_name="KINGDOM HEARTS II FINAL MIX") - logger.info("You are now auto-tracking") - self.kh2connected = True + if self.kh2_game_version is None: + if self.kh2_read_string(0x09A9830, 4) == "KH2J": + self.kh2_game_version = "STEAM" + self.Now = 0x0717008 + self.Save = 0x09A9830 + self.Slot1 = 0x2A23518 + self.Journal = 0x7434E0 + self.Shop = 0x7435D0 + + elif self.kh2_read_string(0x09A92F0, 4) == "KH2J": + self.kh2_game_version = "EGS" + else: + self.kh2_game_version = None + logger.info("Your game version is out of date. Please update your game via The Epic Games Store or Steam.") + if self.kh2_game_version is not None: + logger.info(f"You are now auto-tracking. {self.kh2_game_version}") + self.kh2connected = True except Exception as e: if self.kh2connected: @@ -589,8 +615,8 @@ async def IsInShop(self, sellable): # if journal=-1 and shop = 5 then in shop # if journal !=-1 and shop = 10 then journal - journal = self.kh2_read_short(0x741230) - shop = self.kh2_read_short(0x741320) + journal = self.kh2_read_short(self.Journal) + shop = self.kh2_read_short(self.Shop) if (journal == -1 and shop == 5) or (journal != -1 and shop == 10): # print("your in the shop") sellable_dict = {} @@ -599,8 +625,8 @@ async def IsInShop(self, sellable): amount = self.kh2_read_byte(self.Save + itemdata.memaddr) sellable_dict[itemName] = amount while (journal == -1 and shop == 5) or (journal != -1 and shop == 10): - journal = self.kh2_read_short(0x741230) - shop = self.kh2_read_short(0x741320) + journal = self.kh2_read_short(self.Journal) + shop = self.kh2_read_short(self.Shop) await asyncio.sleep(0.5) for item, amount in sellable_dict.items(): itemdata = self.item_name_to_data[item] @@ -750,7 +776,7 @@ async def verifyItems(self): item_data = self.item_name_to_data[item_name] amount_of_items = 0 amount_of_items += self.kh2_seed_save_cache["AmountInvo"]["Magic"][item_name] - if self.kh2_read_byte(self.Save + item_data.memaddr) != amount_of_items and self.kh2_read_byte(0x741320) in {10, 8}: + if self.kh2_read_byte(self.Save + item_data.memaddr) != amount_of_items and self.kh2_read_byte(self.Shop) in {10, 8}: self.kh2_write_byte(self.Save + item_data.memaddr, amount_of_items) for item_name in master_stat: @@ -802,7 +828,7 @@ async def verifyItems(self): self.kh2_write_byte(self.Save + 0x2502, current_item_slots + 1) elif self.base_item_slots + amount_of_items < 8: self.kh2_write_byte(self.Save + 0x2502, self.base_item_slots + amount_of_items) - + # if self.kh2_read_byte(self.Save + item_data.memaddr) != amount_of_items \ # and self.kh2_read_byte(self.Slot1 + 0x1B2) >= 5 and \ # self.kh2_read_byte(self.Save + 0x23DF) & 0x1 << 3 > 0 and self.kh2_read_byte(0x741320) in {10, 8}: @@ -905,8 +931,23 @@ async def kh2_watcher(ctx: KH2Context): await asyncio.sleep(15) ctx.kh2 = pymem.Pymem(process_name="KINGDOM HEARTS II FINAL MIX") if ctx.kh2 is not None: - logger.info("You are now auto-tracking") - ctx.kh2connected = True + if ctx.kh2_game_version is None: + if ctx.kh2_read_string(0x09A9830, 4) == "KH2J": + ctx.kh2_game_version = "STEAM" + ctx.Now = 0x0717008 + ctx.Save = 0x09A9830 + ctx.Slot1 = 0x2A23518 + ctx.Journal = 0x7434E0 + ctx.Shop = 0x7435D0 + + elif ctx.kh2_read_string(0x09A92F0, 4) == "KH2J": + ctx.kh2_game_version = "EGS" + else: + ctx.kh2_game_version = None + logger.info("Your game version is out of date. Please update your game via The Epic Games Store or Steam.") + if ctx.kh2_game_version is not None: + logger.info(f"You are now auto-tracking {ctx.kh2_game_version}") + ctx.kh2connected = True except Exception as e: if ctx.kh2connected: ctx.kh2connected = False diff --git a/worlds/ladx/__init__.py b/worlds/ladx/__init__.py index 21876ed671e..c958ef212fe 100644 --- a/worlds/ladx/__init__.py +++ b/worlds/ladx/__init__.py @@ -98,9 +98,12 @@ class LinksAwakeningWorld(World): # Items can be grouped using their names to allow easy checking if any item # from that group has been collected. Group names can also be used for !hint - #item_name_groups = { - # "weapons": {"sword", "lance"} - #} + item_name_groups = { + "Instruments": { + "Full Moon Cello", "Conch Horn", "Sea Lily's Bell", "Surf Harp", + "Wind Marimba", "Coral Triangle", "Organ of Evening Calm", "Thunder Drum" + }, + } prefill_dungeon_items = None diff --git a/worlds/lingo/__init__.py b/worlds/lingo/__init__.py index 8d6a7fc4ebe..9853be73fa9 100644 --- a/worlds/lingo/__init__.py +++ b/worlds/lingo/__init__.py @@ -3,13 +3,13 @@ """ from logging import warning -from BaseClasses import Item, ItemClassification, Tutorial +from BaseClasses import CollectionState, Item, ItemClassification, Tutorial from Options import OptionError from worlds.AutoWorld import WebWorld, World from .datatypes import Room, RoomEntrance from .items import ALL_ITEM_TABLE, ITEMS_BY_GROUP, TRAP_ITEMS, LingoItem from .locations import ALL_LOCATION_TABLE, LOCATIONS_BY_GROUP -from .options import LingoOptions, lingo_option_groups +from .options import LingoOptions, lingo_option_groups, SunwarpAccess, VictoryCondition from .player_logic import LingoPlayerLogic from .regions import create_regions @@ -54,20 +54,54 @@ class LingoWorld(World): player_logic: LingoPlayerLogic def generate_early(self): - if not (self.options.shuffle_doors or self.options.shuffle_colors or self.options.shuffle_sunwarps): + if not (self.options.shuffle_doors or self.options.shuffle_colors or + (self.options.sunwarp_access >= SunwarpAccess.option_unlock and + self.options.victory_condition == VictoryCondition.option_pilgrimage)): if self.multiworld.players == 1: - warning(f"{self.multiworld.get_player_name(self.player)}'s Lingo world doesn't have any progression" - f" items. Please turn on Door Shuffle, Color Shuffle, or Sunwarp Shuffle if that doesn't seem" - f" right.") + warning(f"{self.player_name}'s Lingo world doesn't have any progression items. Please turn on Door" + f" Shuffle or Color Shuffle, or use item-blocked sunwarps with the Pilgrimage victory condition" + f" if that doesn't seem right.") else: - raise OptionError(f"{self.multiworld.get_player_name(self.player)}'s Lingo world doesn't have any" - f" progression items. Please turn on Door Shuffle, Color Shuffle or Sunwarp Shuffle.") + raise OptionError(f"{self.player_name}'s Lingo world doesn't have any progression items. Please turn on" + f" Door Shuffle or Color Shuffle, or use item-blocked sunwarps with the Pilgrimage" + f" victory condition.") self.player_logic = LingoPlayerLogic(self) def create_regions(self): create_regions(self) + if not self.options.shuffle_postgame: + state = CollectionState(self.multiworld) + state.collect(LingoItem("Prevent Victory", ItemClassification.progression, None, self.player), True) + + # Note: relies on the assumption that real_items is a definitive list of real progression items in this + # world, and is not modified after being created. + for item in self.player_logic.real_items: + state.collect(self.create_item(item), True) + + # Exception to the above: a forced good item is not considered a "real item", but needs to be here anyway. + if self.player_logic.forced_good_item != "": + state.collect(self.create_item(self.player_logic.forced_good_item), True) + + all_locations = self.multiworld.get_locations(self.player) + state.sweep_for_events(locations=all_locations) + + unreachable_locations = [location for location in all_locations + if not state.can_reach_location(location.name, self.player)] + + for location in unreachable_locations: + if location.name in self.player_logic.event_loc_to_item.keys(): + continue + + self.player_logic.real_locations.remove(location.name) + location.parent_region.locations.remove(location) + + if len(self.player_logic.real_items) > len(self.player_logic.real_locations): + raise OptionError(f"{self.player_name}'s Lingo world does not have enough locations to fit the number" + f" of required items without shuffling the postgame. Either enable postgame" + f" shuffling, or choose different options.") + def create_items(self): pool = [self.create_item(name) for name in self.player_logic.real_items] @@ -136,7 +170,8 @@ def fill_slot_data(self): slot_options = [ "death_link", "victory_condition", "shuffle_colors", "shuffle_doors", "shuffle_paintings", "shuffle_panels", "enable_pilgrimage", "sunwarp_access", "mastery_achievements", "level_2_requirement", "location_checks", - "early_color_hallways", "pilgrimage_allows_roof_access", "pilgrimage_allows_paintings", "shuffle_sunwarps" + "early_color_hallways", "pilgrimage_allows_roof_access", "pilgrimage_allows_paintings", "shuffle_sunwarps", + "group_doors" ] slot_data = { diff --git a/worlds/lingo/data/LL1.yaml b/worlds/lingo/data/LL1.yaml index 4d6771a7350..950fd326743 100644 --- a/worlds/lingo/data/LL1.yaml +++ b/worlds/lingo/data/LL1.yaml @@ -1,6 +1,13 @@ --- # This file is an associative array where the keys are region names. Rooms - # have four properties: entrances, panels, doors, and paintings. + # have a number of properties: + # - entrances + # - panels + # - doors + # - panel_doors + # - paintings + # - progression + # - sunwarps # # entrances is an array of regions from which this room can be accessed. The # key of each entry is the room that can access this one. The value is a list @@ -13,7 +20,7 @@ # room that the door is in. The room name may be omitted if the door is # located in the current room. # - # panels is an array of panels in the room. The key of the array is an + # panels is a named array of panels in the room. The key of the array is an # arbitrary name for the panel. Panels can have the following fields: # - id: The internal ID of the panel in the LINGO map # - required_room: In addition to having access to this room, the player must @@ -45,7 +52,7 @@ # - hunt: If True, the tracker will show this panel even when it is # not a check. Used for hunts like the Number Hunt. # - # doors is an array of doors associated with this room. When door + # doors is a named array of doors associated with this room. When door # randomization is enabled, each of these is an item. The key is a name that # will be displayed as part of the item's name. Doors can have the following # fields: @@ -78,6 +85,18 @@ # - event: Denotes that the door is event only. This is similar to # setting both skip_location and skip_item. # + # panel_doors is a named array of "panel doors" associated with this room. + # When panel door shuffle is enabled, each of these becomes an item, and those + # items block access to the listed panels. The key is a name for internal + # reference only. Panel doors can have the following fields: + # - panels: Required. This is the set of panels that are blocked by this + # panel door. + # - item_name: Overrides the name of the item generated for this panel + # door. If not specified, the item name will be generated from + # the room name and the name(s) of the panel(s). + # - panel_group: When region grouping is enabled, all panel doors with the + # same group will be covered by a single item. + # # paintings is an array of paintings in the room. This is used for painting # shuffling. # - id: The internal painting ID from the LINGO map. @@ -105,6 +124,14 @@ # fine in door shuffle mode. # - move: Denotes that the painting is able to move. # + # progression is a named array of items that define an ordered set of items. + # progression items do not have any true connection to the rooms that they + # are defined in, but it is best to place them in a thematically appropriate + # room. The key for a progression entry is the name of the item that will be + # created. A progression entry is a dictionary with one or both of a "doors" + # key and a "panel_doors" key. These fields should be lists of doors or + # panel doors that will be contained in this progressive item. + # # sunwarps is an array of sunwarps in the room. This is used for sunwarp # shuffling. # - dots: The number of dots on this sunwarp. @@ -140,6 +167,15 @@ painting: True The Colorful: painting: True + Welcome Back Area: + room: Welcome Back Area + door: Shortcut to Starting Room + Second Room: + door: Main Door + Hidden Room: + door: Back Right Door + Rhyme Room (Looped Square): + door: Rhyme Room Entrance panels: HI: id: Entry Room/Panel_hi_hi @@ -184,6 +220,10 @@ panel: RACECAR (Black) - room: The Tenacious panel: SOLOS (Black) + panel_doors: + HIDDEN: + panels: + - HIDDEN paintings: - id: arrows_painting exit_only: True @@ -294,6 +334,10 @@ panel: SOLOS (Black) - room: Hub Room panel: RAT + panel_doors: + OPEN: + panels: + - OPEN paintings: - id: owl_painting orientation: north @@ -308,7 +352,13 @@ panels: Achievement: id: Countdown Panels/Panel_seeker_seeker - required_room: Hidden Room + # The Seeker uniquely has the property that 1) it can be entered (through the Pilgrim Room) without opening the + # front door in panels mode door shuffle, and 2) the front door panel is part of the CDP. This necessitates this + # required_panel clause, because the entrance panel needs to be solvable for the achievement even if an + # alternate entrance to the room is used. + required_panel: + room: Hidden Room + panel: OPEN tag: forbid check: True achievement: The Seeker @@ -528,6 +578,23 @@ item_group: Achievement Room Entrances panels: - OPEN + panel_doors: + ORDER: + panels: + - ORDER + SLAUGHTER: + panel_group: Tenacious Entrance Panels + panels: + - SLAUGHTER + TRACE: + panels: + - TRACE + RAT: + panels: + - RAT + OPEN: + panels: + - OPEN paintings: - id: maze_painting orientation: west @@ -599,12 +666,13 @@ item_name: "6 Sunwarp" progression: Progressive Pilgrimage: - - 1 Sunwarp - - 2 Sunwarp - - 3 Sunwarp - - 4 Sunwarp - - 5 Sunwarp - - 6 Sunwarp + doors: + - 1 Sunwarp + - 2 Sunwarp + - 3 Sunwarp + - 4 Sunwarp + - 5 Sunwarp + - 6 Sunwarp Pilgrim Antechamber: # The entrances to this room are special. When pilgrimage is enabled, we use a special access rule to determine # whether a pilgrimage can succeed. When pilgrimage is disabled, the sun painting will be added to the pool. @@ -870,6 +938,26 @@ panel: DRAWL + RUNS - room: Owl Hallway panel: READS + RUST + - room: Ending Area + panel: THE END + panel_doors: + DECAY: + panel_group: Tenacious Entrance Panels + panels: + - DECAY + NOPE: + panels: + - NOPE + WE ROT: + panels: + - WE ROT + WORDS SWORD: + panels: + - WORDS + - SWORD + BEND HI: + panels: + - BEND HI paintings: - id: eye_painting disable: True @@ -884,6 +972,14 @@ direction: exit entrance_indicator_pos: [ -17, 2.5, -41.01 ] orientation: north + progression: + Progressive Suits Area: + panel_doors: + - WORDS SWORD + - room: Lost Area + panel_door: LOST + - room: Amen Name Area + panel_door: AMEN NAME Lost Area: entrances: Outside The Agreeable: @@ -909,6 +1005,11 @@ panels: - LOST (1) - LOST (2) + panel_doors: + LOST: + panels: + - LOST (1) + - LOST (2) Amen Name Area: entrances: Crossroads: @@ -942,6 +1043,11 @@ panels: - AMEN - NAME + panel_doors: + AMEN NAME: + panels: + - AMEN + - NAME Suits Area: entrances: Amen Name Area: @@ -1045,6 +1151,13 @@ - LEVEL (White) - RACECAR (White) - SOLOS (White) + panel_doors: + Black Palindromes: + item_name: The Tenacious - Black Palindromes (Panels) + panels: + - LEVEL (Black) + - RACECAR (Black) + - SOLOS (Black) Near Far Area: entrances: Hub Room: True @@ -1070,6 +1183,21 @@ panels: - NEAR - FAR + panel_doors: + NEAR FAR: + item_name: Symmetry Room - NEAR, FAR (Panels) + panel_group: Symmetry Room Panels + panels: + - NEAR + - FAR + progression: + Progressive Symmetry Room: + panel_doors: + - NEAR FAR + - room: Warts Straw Area + panel_door: WARTS STRAW + - room: Leaf Feel Area + panel_door: LEAF FEEL Warts Straw Area: entrances: Near Far Area: @@ -1097,6 +1225,13 @@ panels: - WARTS - STRAW + panel_doors: + WARTS STRAW: + item_name: Symmetry Room - WARTS, STRAW (Panels) + panel_group: Symmetry Room Panels + panels: + - WARTS + - STRAW Leaf Feel Area: entrances: Warts Straw Area: @@ -1124,6 +1259,13 @@ panels: - LEAF - FEEL + panel_doors: + LEAF FEEL: + item_name: Symmetry Room - LEAF, FEEL (Panels) + panel_group: Symmetry Room Panels + panels: + - LEAF + - FEEL Outside The Agreeable: entrances: Crossroads: @@ -1232,6 +1374,20 @@ panels: - room: Color Hunt panel: PURPLE + panel_doors: + MASSACRED: + panel_group: Tenacious Entrance Panels + panels: + - MASSACRED + BLACK: + panels: + - BLACK + CLOSE: + panels: + - CLOSE + RIGHT: + panels: + - RIGHT paintings: - id: eyes_yellow_painting orientation: east @@ -1283,6 +1439,14 @@ - WINTER - DIAMONDS - FIRE + panel_doors: + Lookout: + item_name: Compass Room Panels + panels: + - NORTH + - WINTER + - DIAMONDS + - FIRE paintings: - id: pencil_painting7 orientation: north @@ -1392,6 +1556,8 @@ room: Owl Hallway door: Shortcut to Hedge Maze Roof: True + The Incomparable: + door: Observant Entrance panels: DOWN: id: Maze Room/Panel_down_up @@ -1499,6 +1665,10 @@ - HIDE (3) - room: Outside The Agreeable panel: HIDE + panel_doors: + DOWN: + panels: + - DOWN The Perceptive: entrances: Starting Room: @@ -1520,6 +1690,10 @@ check: True exclude_reduce: True tag: botwhite + panel_doors: + GAZE: + panels: + - GAZE paintings: - id: garden_painting_tower orientation: north @@ -1561,9 +1735,10 @@ - EAT progression: Progressive Fearless: - - Second Floor - - room: The Fearless (Second Floor) - door: Third Floor + doors: + - Second Floor + - room: The Fearless (Second Floor) + door: Third Floor The Fearless (Second Floor): entrances: The Fearless (First Floor): @@ -1658,6 +1833,10 @@ tag: forbid required_door: door: Stairs + required_panel: + - panel: FOUR (1) + - panel: FOUR (2) + - panel: SIX achievement: The Observant FOUR (1): id: Look Room/Panel_four_back @@ -1771,6 +1950,16 @@ door_group: Observant Doors panels: - SIX + panel_doors: + BACKSIDE: + item_name: The Observant - Backside Entrance Panels + panel_group: Backside Entrance Panels + panels: + - FOUR (1) + - FOUR (2) + STAIRS: + panels: + - SIX The Incomparable: entrances: The Observant: @@ -1780,6 +1969,9 @@ door: Eight Door Orange Tower Sixth Floor: painting: True + Hedge Maze: + room: Hedge Maze + door: Observant Entrance panels: Achievement: id: Countdown Panels/Panel_incomparable_incomparable @@ -1787,9 +1979,12 @@ check: True tag: forbid required_room: - - Elements Area - - Courtyard - Eight Room + required_panel: + - room: Courtyard + panel: I + - room: Elements Area + panel: A achievement: The Incomparable A (One): id: Strand Room/Panel_blank_a @@ -1854,6 +2049,15 @@ panel: I - room: Elements Area panel: A + panel_doors: + Giant Sevens: + item_name: Giant Seven Panels + panels: + - I (Seven) + - room: Courtyard + panel: I + - room: Elements Area + panel: A paintings: - id: crown_painting orientation: east @@ -1961,14 +2165,31 @@ panel: DRAWL + RUNS - room: Owl Hallway panel: READS + RUST + panel_doors: + Access: + item_name: Orange Tower Panels + panels: + - room: Orange Tower First Floor + panel: DADS + ALE + - room: Outside The Undeterred + panel: ART + ART + - room: Orange Tower Third Floor + panel: DEER + WREN + - room: Orange Tower Fourth Floor + panel: LEARNS + UNSEW + - room: Orange Tower Fifth Floor + panel: DRAWL + RUNS + - room: Owl Hallway + panel: READS + RUST progression: Progressive Orange Tower: - - Second Floor - - Third Floor - - Fourth Floor - - Fifth Floor - - Sixth Floor - - Seventh Floor + doors: + - Second Floor + - Third Floor + - Fourth Floor + - Fifth Floor + - Sixth Floor + - Seventh Floor Orange Tower First Floor: entrances: Hub Room: @@ -2011,6 +2232,10 @@ - SALT - room: Directional Gallery panel: PEPPER + panel_doors: + SECRET: + panels: + - SECRET sunwarps: - dots: 4 direction: enter @@ -2163,6 +2388,10 @@ id: Shuffle Room Area Doors/Door_hotcrust_shortcuts panels: - HOT CRUSTS + panel_doors: + HOT CRUSTS: + panels: + - HOT CRUSTS sunwarps: - dots: 5 direction: enter @@ -2277,6 +2506,12 @@ panels: - SIZE (Small) - SIZE (Big) + panel_doors: + SIZE: + item_name: Orange Tower Fifth Floor - SIZE Panels + panels: + - SIZE (Small) + - SIZE (Big) paintings: - id: hi_solved_painting3 orientation: south @@ -2313,7 +2548,7 @@ orientation: east - id: hi_solved_painting orientation: west - Orange Tower Seventh Floor: + Ending Area: entrances: Orange Tower Sixth Floor: room: Orange Tower @@ -2325,6 +2560,18 @@ check: True tag: forbid non_counting: True + location_name: Orange Tower Seventh Floor - THE END + doors: + End: + event: True + panels: + - THE END + Orange Tower Seventh Floor: + entrances: + Ending Area: + room: Ending Area + door: End + panels: THE MASTER: # We will set up special rules for this in code. id: Countdown Panels/Panel_master_master @@ -2608,6 +2855,15 @@ - SECOND - THIRD - FOURTH + panel_doors: + FIRST SECOND THIRD FOURTH: + item_name: Courtyard - Ordinal Panels + panel_group: Backside Entrance Panels + panels: + - FIRST + - SECOND + - THIRD + - FOURTH The Colorful (White): entrances: Courtyard: True @@ -2625,6 +2881,12 @@ location_name: The Colorful - White panels: - BEGIN + panel_doors: + BEGIN: + item_name: The Colorful - BEGIN (Panel) + panel_group: Colorful Panels + panels: + - BEGIN The Colorful (Black): entrances: The Colorful (White): @@ -2645,6 +2907,12 @@ door_group: Colorful Doors panels: - FOUND + panel_doors: + FOUND: + item_name: The Colorful - FOUND (Panel) + panel_group: Colorful Panels + panels: + - FOUND The Colorful (Red): entrances: The Colorful (Black): @@ -2665,6 +2933,12 @@ door_group: Colorful Doors panels: - LOAF + panel_doors: + LOAF: + item_name: The Colorful - LOAF (Panel) + panel_group: Colorful Panels + panels: + - LOAF The Colorful (Yellow): entrances: The Colorful (Red): @@ -2685,6 +2959,12 @@ door_group: Colorful Doors panels: - CREAM + panel_doors: + CREAM: + item_name: The Colorful - CREAM (Panel) + panel_group: Colorful Panels + panels: + - CREAM The Colorful (Blue): entrances: The Colorful (Yellow): @@ -2705,6 +2985,12 @@ door_group: Colorful Doors panels: - SUN + panel_doors: + SUN: + item_name: The Colorful - SUN (Panel) + panel_group: Colorful Panels + panels: + - SUN The Colorful (Purple): entrances: The Colorful (Blue): @@ -2725,6 +3011,12 @@ door_group: Colorful Doors panels: - SPOON + panel_doors: + SPOON: + item_name: The Colorful - SPOON (Panel) + panel_group: Colorful Panels + panels: + - SPOON The Colorful (Orange): entrances: The Colorful (Purple): @@ -2745,6 +3037,12 @@ door_group: Colorful Doors panels: - LETTERS + panel_doors: + LETTERS: + item_name: The Colorful - LETTERS (Panel) + panel_group: Colorful Panels + panels: + - LETTERS The Colorful (Green): entrances: The Colorful (Orange): @@ -2765,6 +3063,12 @@ door_group: Colorful Doors panels: - WALLS + panel_doors: + WALLS: + item_name: The Colorful - WALLS (Panel) + panel_group: Colorful Panels + panels: + - WALLS The Colorful (Brown): entrances: The Colorful (Green): @@ -2785,6 +3089,12 @@ door_group: Colorful Doors panels: - IRON + panel_doors: + IRON: + item_name: The Colorful - IRON (Panel) + panel_group: Colorful Panels + panels: + - IRON The Colorful (Gray): entrances: The Colorful (Brown): @@ -2805,6 +3115,12 @@ door_group: Colorful Doors panels: - OBSTACLE + panel_doors: + OBSTACLE: + item_name: The Colorful - OBSTACLE (Panel) + panel_group: Colorful Panels + panels: + - OBSTACLE The Colorful: entrances: The Colorful (Gray): @@ -2843,26 +3159,48 @@ orientation: north progression: Progressive Colorful: - - room: The Colorful (White) - door: Progress Door - - room: The Colorful (Black) - door: Progress Door - - room: The Colorful (Red) - door: Progress Door - - room: The Colorful (Yellow) - door: Progress Door - - room: The Colorful (Blue) - door: Progress Door - - room: The Colorful (Purple) - door: Progress Door - - room: The Colorful (Orange) - door: Progress Door - - room: The Colorful (Green) - door: Progress Door - - room: The Colorful (Brown) - door: Progress Door - - room: The Colorful (Gray) - door: Progress Door + doors: + - room: The Colorful (White) + door: Progress Door + - room: The Colorful (Black) + door: Progress Door + - room: The Colorful (Red) + door: Progress Door + - room: The Colorful (Yellow) + door: Progress Door + - room: The Colorful (Blue) + door: Progress Door + - room: The Colorful (Purple) + door: Progress Door + - room: The Colorful (Orange) + door: Progress Door + - room: The Colorful (Green) + door: Progress Door + - room: The Colorful (Brown) + door: Progress Door + - room: The Colorful (Gray) + door: Progress Door + panel_doors: + - room: The Colorful (White) + panel_door: BEGIN + - room: The Colorful (Black) + panel_door: FOUND + - room: The Colorful (Red) + panel_door: LOAF + - room: The Colorful (Yellow) + panel_door: CREAM + - room: The Colorful (Blue) + panel_door: SUN + - room: The Colorful (Purple) + panel_door: SPOON + - room: The Colorful (Orange) + panel_door: LETTERS + - room: The Colorful (Green) + panel_door: WALLS + - room: The Colorful (Brown) + panel_door: IRON + - room: The Colorful (Gray) + panel_door: OBSTACLE Welcome Back Area: entrances: Starting Room: @@ -2935,6 +3273,10 @@ door_group: Hedge Maze Doors panels: - STRAYS + panel_doors: + STRAYS: + panels: + - STRAYS paintings: - id: arrows_painting_8 orientation: south @@ -3132,6 +3474,13 @@ panel: I - room: Elements Area panel: A + panel_doors: + UNCOVER: + panels: + - UNCOVER + OXEN: + panels: + - OXEN paintings: - id: clock_painting_5 orientation: east @@ -3265,7 +3614,6 @@ door: Traveled Entrance Color Hallways: door: Color Hallways Entrance - warp: True panels: Achievement: id: Countdown Panels/Panel_traveled_traveled @@ -3502,6 +3850,13 @@ - RISE (Sunrise) - ZEN - SON + panel_doors: + UNOPEN: + panels: + - UNOPEN + BEGIN: + panels: + - BEGIN paintings: - id: pencil_painting2 orientation: west @@ -3797,6 +4152,34 @@ item_group: Achievement Room Entrances panels: - ZERO + panel_doors: + ZERO: + panels: + - ZERO + PEN: + panels: + - PEN + TWO: + item_name: Two Panels + panels: + - TWO (1) + - TWO (2) + THREE: + item_name: Three Panels + panels: + - THREE (1) + - THREE (2) + - THREE (3) + FOUR: + item_name: Four Panels + panels: + - FOUR + - room: Hub Room + panel: FOUR + - room: Dead End Area + panel: FOUR + - room: The Traveled + panel: FOUR paintings: - id: maze_painting_3 enter_only: True @@ -3972,6 +4355,10 @@ panel: FIVE (1) - room: Directional Gallery panel: FIVE (2) + First Six: + event: True + panels: + - SIX Sevens: id: - Count Up Room Area Doors/Door_seven_hider @@ -4080,12 +4467,109 @@ panel: NINE - room: Elements Area panel: NINE + panel_doors: + FIVE: + item_name: Five Panels + panels: + - FIVE + - room: Outside The Agreeable + panel: FIVE (1) + - room: Outside The Agreeable + panel: FIVE (2) + - room: Directional Gallery + panel: FIVE (1) + - room: Directional Gallery + panel: FIVE (2) + SIX: + item_name: Six Panels + panels: + - SIX + - room: Outside The Bold + panel: SIX + - room: Directional Gallery + panel: SIX (1) + - room: Directional Gallery + panel: SIX (2) + - room: The Bearer (East) + panel: SIX + - room: The Bearer (South) + panel: SIX + SEVEN: + item_name: Seven Panels + panels: + - SEVEN + - room: Directional Gallery + panel: SEVEN + - room: Knight Night Exit + panel: SEVEN (1) + - room: Knight Night Exit + panel: SEVEN (2) + - room: Knight Night Exit + panel: SEVEN (3) + - room: Outside The Initiated + panel: SEVEN (1) + - room: Outside The Initiated + panel: SEVEN (2) + EIGHT: + item_name: Eight Panels + panels: + - EIGHT + - room: Directional Gallery + panel: EIGHT + - room: The Eyes They See + panel: EIGHT + - room: Dead End Area + panel: EIGHT + - room: Crossroads + panel: EIGHT + - room: Hot Crusts Area + panel: EIGHT + - room: Art Gallery + panel: EIGHT + - room: Outside The Initiated + panel: EIGHT + NINE: + item_name: Nine Panels + panels: + - NINE + - room: Directional Gallery + panel: NINE + - room: Amen Name Area + panel: NINE + - room: Yellow Backside Area + panel: NINE + - room: Outside The Initiated + panel: NINE + - room: Outside The Bold + panel: NINE + - room: Rhyme Room (Cross) + panel: NINE + - room: Orange Tower Fifth Floor + panel: NINE + - room: Elements Area + panel: NINE paintings: - id: smile_painting_5 enter_only: True orientation: east required_door: door: Eights + progression: + Progressive Number Hunt: + panel_doors: + - room: Outside The Undeterred + panel_door: TWO + - room: Outside The Undeterred + panel_door: THREE + - room: Outside The Undeterred + panel_door: FOUR + - FIVE + - SIX + - SEVEN + - EIGHT + - NINE + - room: Outside The Undeterred + panel_door: ZERO Directional Gallery: entrances: Outside The Agreeable: @@ -4173,7 +4657,7 @@ tag: midorange required_door: room: Number Hunt - door: Sixes + door: First Six PARANOID: id: Backside Room/Panel_paranoid_paranoid tag: midwhite @@ -4181,7 +4665,7 @@ exclude_reduce: True required_door: room: Number Hunt - door: Sixes + door: First Six YELLOW: id: Color Arrow Room/Panel_yellow_afar tag: midwhite @@ -4244,6 +4728,11 @@ panels: - room: Color Hunt panel: YELLOW + panel_doors: + TURN LEARN: + panels: + - TURN + - LEARN paintings: - id: smile_painting_7 orientation: south @@ -4255,7 +4744,7 @@ move: True required_door: room: Number Hunt - door: Sixes + door: First Six - id: boxes_painting orientation: south - id: cherry_painting @@ -4322,6 +4811,34 @@ id: Rock Room Doors/Door_hint panels: - EXIT + panel_doors: + EXIT: + panels: + - EXIT + RED: + panel_group: Color Hunt Panels + panels: + - RED + BLUE: + panel_group: Color Hunt Panels + panels: + - BLUE + YELLOW: + panel_group: Color Hunt Panels + panels: + - YELLOW + ORANGE: + panel_group: Color Hunt Panels + panels: + - ORANGE + PURPLE: + panel_group: Color Hunt Panels + panels: + - PURPLE + GREEN: + panel_group: Color Hunt Panels + panels: + - GREEN paintings: - id: arrows_painting_7 orientation: east @@ -4459,6 +4976,14 @@ event: True panels: - HEART + panel_doors: + FARTHER: + panel_group: Backside Entrance Panels + panels: + - FARTHER + MIDDLE: + panels: + - MIDDLE The Bearer (East): entrances: Cross Tower (East): True @@ -5311,6 +5836,11 @@ item_name: Knight Night Room - Exit panels: - TRUSTED + panel_doors: + TRUSTED: + item_name: Knight Night Room - TRUSTED (Panel) + panels: + - TRUSTED Knight Night Exit: entrances: Knight Night (Outer Ring): @@ -5995,6 +6525,10 @@ item_group: Achievement Room Entrances panels: - SHRINK + panel_doors: + SHRINK: + panels: + - SHRINK The Wondrous (Doorknob): entrances: Outside The Wondrous: @@ -6206,18 +6740,36 @@ - KEEP - BAILEY - TOWER + panel_doors: + CASTLE: + item_name: Hallway Room - First Room Panels + panel_group: Hallway Room Panels + panels: + - WALL + - KEEP + - BAILEY + - TOWER paintings: - id: panda_painting orientation: south progression: Progressive Hallway Room: - - Exit - - room: Hallway Room (2) - door: Exit - - room: Hallway Room (3) - door: Exit - - room: Hallway Room (4) - door: Exit + doors: + - Exit + - room: Hallway Room (2) + door: Exit + - room: Hallway Room (3) + door: Exit + - room: Hallway Room (4) + door: Exit + panel_doors: + - CASTLE + - room: Hallway Room (2) + panel_door: COUNTERCLOCKWISE + - room: Hallway Room (3) + panel_door: TRANSFORMATION + - room: Hallway Room (4) + panel_door: WHEELBARROW Hallway Room (2): entrances: Hallway Room (1): @@ -6256,6 +6808,15 @@ - CLOCK - ER - COUNT + panel_doors: + COUNTERCLOCKWISE: + item_name: Hallway Room - Second Room Panels + panel_group: Hallway Room Panels + panels: + - WISE + - CLOCK + - ER + - COUNT Hallway Room (3): entrances: Hallway Room (2): @@ -6294,6 +6855,15 @@ - FORM - A - SHUN + panel_doors: + TRANSFORMATION: + item_name: Hallway Room - Third Room Panels + panel_group: Hallway Room Panels + panels: + - TRANCE + - FORM + - A + - SHUN Hallway Room (4): entrances: Hallway Room (3): @@ -6316,6 +6886,12 @@ panels: - WHEEL include_reduce: True + panel_doors: + WHEELBARROW: + item_name: Hallway Room - WHEEL + panel_group: Hallway Room Panels + panels: + - WHEEL Elements Area: entrances: Roof: True @@ -6390,6 +6966,10 @@ panels: - room: The Wanderer panel: Achievement + panel_doors: + WANDERLUST: + panels: + - WANDERLUST The Wanderer: entrances: Outside The Wanderer: @@ -6531,6 +7111,10 @@ item_group: Achievement Room Entrances panels: - ORDER + panel_doors: + ORDER: + panels: + - ORDER paintings: - id: smile_painting_3 orientation: west @@ -6544,10 +7128,11 @@ orientation: south progression: Progressive Art Gallery: - - Second Floor - - Third Floor - - Fourth Floor - - Fifth Floor + doors: + - Second Floor + - Third Floor + - Fourth Floor + - Fifth Floor Art Gallery (Second Floor): entrances: Art Gallery: @@ -7069,6 +7654,8 @@ LEAP: id: Double Room/Panel_leap_leap tag: midwhite + required_door: + door: Door to Cross doors: Door to Cross: id: Double Room Area Doors/Door_room_4a @@ -7259,8 +7846,8 @@ id: Panel Room/Panel_broomed_bedroom colors: yellow tag: midyellow - required_door: - door: Excavation + required_panel: + panel: WALL (1) LAYS: id: Panel Room/Panel_lays_maze colors: purple @@ -7287,13 +7874,24 @@ Excavation: event: True panels: - - WALL (1) + - STAIRS Cellar Exit: id: - Tower Room Area Doors/Door_panel_basement - Tower Room Area Doors/Door_panel_basement2 panels: - BASE + panel_doors: + STAIRS: + panel_group: Room Room Panels + panels: + - STAIRS + Colors: + panel_group: Room Room Panels + panels: + - BROOMED + - LAYS + - BASE Cellar: entrances: Room Room: @@ -7332,6 +7930,11 @@ panels: - KITTEN - CAT + panel_doors: + KITTEN CAT: + panels: + - KITTEN + - CAT paintings: - id: arrows_painting_2 orientation: east @@ -7586,6 +8189,10 @@ item_group: Achievement Room Entrances panels: - OPEN + panel_doors: + OPEN: + panels: + - OPEN The Scientific: entrances: Outside The Scientific: diff --git a/worlds/lingo/data/generated.dat b/worlds/lingo/data/generated.dat index 6c8c925138a..9a49d3d9d4b 100644 Binary files a/worlds/lingo/data/generated.dat and b/worlds/lingo/data/generated.dat differ diff --git a/worlds/lingo/data/ids.yaml b/worlds/lingo/data/ids.yaml index 1fa06d24254..b46f1d36ec1 100644 --- a/worlds/lingo/data/ids.yaml +++ b/worlds/lingo/data/ids.yaml @@ -272,8 +272,9 @@ panels: PAINTING (4): 445081 PAINTING (5): 445082 ROOM: 445083 - Orange Tower Seventh Floor: + Ending Area: THE END: 444620 + Orange Tower Seventh Floor: THE MASTER: 444621 MASTERY: 444622 Behind A Smile: @@ -1477,3 +1478,145 @@ progression: Progressive Art Gallery: 444563 Progressive Colorful: 444580 Progressive Pilgrimage: 444583 + Progressive Suits Area: 444602 + Progressive Symmetry Room: 444608 + Progressive Number Hunt: 444654 +panel_doors: + Starting Room: + HIDDEN: 444589 + Hidden Room: + OPEN: 444590 + Hub Room: + ORDER: 444591 + SLAUGHTER: 444592 + TRACE: 444594 + RAT: 444595 + OPEN: 444596 + Crossroads: + DECAY: 444597 + NOPE: 444598 + WE ROT: 444599 + WORDS SWORD: 444600 + BEND HI: 444601 + Lost Area: + LOST: 444603 + Amen Name Area: + AMEN NAME: 444604 + The Tenacious: + Black Palindromes: 444605 + Near Far Area: + NEAR FAR: 444606 + Warts Straw Area: + WARTS STRAW: 444609 + Leaf Feel Area: + LEAF FEEL: 444610 + Outside The Agreeable: + MASSACRED: 444611 + BLACK: 444612 + CLOSE: 444613 + RIGHT: 444614 + Compass Room: + Lookout: 444615 + Hedge Maze: + DOWN: 444617 + The Perceptive: + GAZE: 444618 + The Observant: + BACKSIDE: 444619 + STAIRS: 444621 + The Incomparable: + Giant Sevens: 444622 + Orange Tower: + Access: 444623 + Orange Tower First Floor: + SECRET: 444624 + Orange Tower Fourth Floor: + HOT CRUSTS: 444625 + Orange Tower Fifth Floor: + SIZE: 444626 + First Second Third Fourth: + FIRST SECOND THIRD FOURTH: 444627 + The Colorful (White): + BEGIN: 444628 + The Colorful (Black): + FOUND: 444630 + The Colorful (Red): + LOAF: 444631 + The Colorful (Yellow): + CREAM: 444632 + The Colorful (Blue): + SUN: 444633 + The Colorful (Purple): + SPOON: 444634 + The Colorful (Orange): + LETTERS: 444635 + The Colorful (Green): + WALLS: 444636 + The Colorful (Brown): + IRON: 444637 + The Colorful (Gray): + OBSTACLE: 444638 + Owl Hallway: + STRAYS: 444639 + Outside The Initiated: + UNCOVER: 444640 + OXEN: 444641 + Outside The Bold: + UNOPEN: 444642 + BEGIN: 444643 + Outside The Undeterred: + ZERO: 444644 + PEN: 444645 + TWO: 444646 + THREE: 444647 + FOUR: 444648 + Number Hunt: + FIVE: 444649 + SIX: 444650 + SEVEN: 444651 + EIGHT: 444652 + NINE: 444653 + Color Hunt: + EXIT: 444655 + RED: 444656 + BLUE: 444658 + YELLOW: 444659 + ORANGE: 444660 + PURPLE: 444661 + GREEN: 444662 + The Bearer: + FARTHER: 444663 + MIDDLE: 444664 + Knight Night (Final): + TRUSTED: 444665 + Outside The Wondrous: + SHRINK: 444666 + Hallway Room (1): + CASTLE: 444667 + Hallway Room (2): + COUNTERCLOCKWISE: 444669 + Hallway Room (3): + TRANSFORMATION: 444670 + Hallway Room (4): + WHEELBARROW: 444671 + Outside The Wanderer: + WANDERLUST: 444672 + Art Gallery: + ORDER: 444673 + Room Room: + STAIRS: 444674 + Colors: 444676 + Outside The Wise: + KITTEN CAT: 444677 + Outside The Scientific: + OPEN: 444678 + Directional Gallery: + TURN LEARN: 444679 +panel_groups: + Tenacious Entrance Panels: 444593 + Symmetry Room Panels: 444607 + Backside Entrance Panels: 444620 + Colorful Panels: 444629 + Color Hunt Panels: 444657 + Hallway Room Panels: 444668 + Room Room Panels: 444675 diff --git a/worlds/lingo/datatypes.py b/worlds/lingo/datatypes.py index 36141daa410..9521422ab15 100644 --- a/worlds/lingo/datatypes.py +++ b/worlds/lingo/datatypes.py @@ -12,6 +12,11 @@ class RoomAndPanel(NamedTuple): panel: str +class RoomAndPanelDoor(NamedTuple): + room: Optional[str] + panel_door: str + + class EntranceType(Flag): NORMAL = auto() PAINTING = auto() @@ -63,9 +68,15 @@ class Panel(NamedTuple): exclude_reduce: bool achievement: bool non_counting: bool + panel_door: Optional[RoomAndPanelDoor] # This will always be fully specified. location_name: Optional[str] +class PanelDoor(NamedTuple): + item_name: str + panel_group: Optional[str] + + class Painting(NamedTuple): id: str room: str diff --git a/worlds/lingo/items.py b/worlds/lingo/items.py index 67eaceab10f..78b288e7c2d 100644 --- a/worlds/lingo/items.py +++ b/worlds/lingo/items.py @@ -3,7 +3,7 @@ from BaseClasses import Item, ItemClassification from .static_logic import DOORS_BY_ROOM, PROGRESSIVE_ITEMS, get_door_group_item_id, get_door_item_id, \ - get_progressive_item_id, get_special_item_id + get_progressive_item_id, get_special_item_id, PANEL_DOORS_BY_ROOM, get_panel_door_item_id, get_panel_group_item_id class ItemType(Enum): @@ -65,6 +65,21 @@ def load_item_data(): ItemClassification.progression, ItemType.NORMAL, True, []) ITEMS_BY_GROUP.setdefault("Doors", []).append(group) + panel_groups: Set[str] = set() + for room_name, panel_doors in PANEL_DOORS_BY_ROOM.items(): + for panel_door_name, panel_door in panel_doors.items(): + if panel_door.panel_group is not None: + panel_groups.add(panel_door.panel_group) + + ALL_ITEM_TABLE[panel_door.item_name] = ItemData(get_panel_door_item_id(room_name, panel_door_name), + ItemClassification.progression, ItemType.NORMAL, False, []) + ITEMS_BY_GROUP.setdefault("Panels", []).append(panel_door.item_name) + + for group in panel_groups: + ALL_ITEM_TABLE[group] = ItemData(get_panel_group_item_id(group), ItemClassification.progression, + ItemType.NORMAL, False, []) + ITEMS_BY_GROUP.setdefault("Panels", []).append(group) + special_items: Dict[str, ItemClassification] = { ":)": ItemClassification.filler, "The Feeling of Being Lost": ItemClassification.filler, diff --git a/worlds/lingo/options.py b/worlds/lingo/options.py index 333b3e1ef08..2fd57ff5ede 100644 --- a/worlds/lingo/options.py +++ b/worlds/lingo/options.py @@ -8,21 +8,31 @@ class ShuffleDoors(Choice): - """If on, opening doors will require their respective "keys". + """This option specifies how doors open. - - **Simple:** Doors are sorted into logical groups, which are all opened by - receiving an item. - - **Complex:** The items are much more granular, and will usually only open - a single door each. + - **None:** Doors in the game will open the way they do in vanilla. + - **Panels:** Doors still open as in vanilla, but the panels that open the + doors will be locked, and an item will be required to unlock the panels. + - **Doors:** the doors themselves are locked behind items, and will open + automatically without needing to solve a panel once the key is obtained. """ display_name = "Shuffle Doors" option_none = 0 - option_simple = 1 - option_complex = 2 + option_panels = 1 + option_doors = 2 + alias_simple = 2 + alias_complex = 2 + + +class GroupDoors(Toggle): + """By default, door shuffle in either panels or doors mode will create individual keys for every panel or door to be locked. + + When group doors is on, some panels and doors are sorted into logical groups, which are opened together by receiving an item.""" + display_name = "Group Doors" class ProgressiveOrangeTower(DefaultOnToggle): - """When "Shuffle Doors" is on, this setting governs the manner in which the Orange Tower floors open up. + """When "Shuffle Doors" is on doors mode, this setting governs the manner in which the Orange Tower floors open up. - **Off:** There is an item for each floor of the tower, and each floor's item is the only one needed to access that floor. @@ -33,7 +43,7 @@ class ProgressiveOrangeTower(DefaultOnToggle): class ProgressiveColorful(DefaultOnToggle): - """When "Shuffle Doors" is on "complex", this setting governs the manner in which The Colorful opens up. + """When "Shuffle Doors" is on either panels or doors mode and "Group Doors" is off, this setting governs the manner in which The Colorful opens up. - **Off:** There is an item for each room of The Colorful, meaning that random rooms in the middle of the sequence can open up without giving you @@ -194,6 +204,11 @@ class EarlyColorHallways(Toggle): display_name = "Early Color Hallways" +class ShufflePostgame(Toggle): + """When off, locations that could not be reached without also reaching your victory condition are removed.""" + display_name = "Shuffle Postgame" + + class TrapPercentage(Range): """Replaces junk items with traps, at the specified rate.""" display_name = "Trap Percentage" @@ -248,6 +263,7 @@ class DeathLink(Toggle): @dataclass class LingoOptions(PerGameCommonOptions): shuffle_doors: ShuffleDoors + group_doors: GroupDoors progressive_orange_tower: ProgressiveOrangeTower progressive_colorful: ProgressiveColorful location_checks: LocationChecks @@ -263,6 +279,7 @@ class LingoOptions(PerGameCommonOptions): mastery_achievements: MasteryAchievements level_2_requirement: Level2Requirement early_color_hallways: EarlyColorHallways + shuffle_postgame: ShufflePostgame trap_percentage: TrapPercentage trap_weights: TrapWeights puzzle_skip_percentage: PuzzleSkipPercentage diff --git a/worlds/lingo/player_logic.py b/worlds/lingo/player_logic.py index 1621620e1e1..b21735c1f53 100644 --- a/worlds/lingo/player_logic.py +++ b/worlds/lingo/player_logic.py @@ -7,8 +7,8 @@ from .locations import ALL_LOCATION_TABLE, LocationClassification from .options import LocationChecks, ShuffleDoors, SunwarpAccess, VictoryCondition from .static_logic import DOORS_BY_ROOM, PAINTINGS, PAINTING_ENTRANCES, PAINTING_EXITS, \ - PANELS_BY_ROOM, PROGRESSION_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS, \ - SUNWARP_ENTRANCES, SUNWARP_EXITS + PANELS_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS, PROGRESSIVE_DOORS_BY_ROOM, \ + PANEL_DOORS_BY_ROOM, PROGRESSIVE_PANELS_BY_ROOM, SUNWARP_ENTRANCES, SUNWARP_EXITS if TYPE_CHECKING: from . import LingoWorld @@ -18,23 +18,35 @@ class AccessRequirements: rooms: Set[str] doors: Set[RoomAndDoor] colors: Set[str] + items: Set[str] + progression: Dict[str, int] the_master: bool + postgame: bool def __init__(self): self.rooms = set() self.doors = set() self.colors = set() + self.items = set() + self.progression = dict() self.the_master = False + self.postgame = False def merge(self, other: "AccessRequirements"): self.rooms |= other.rooms self.doors |= other.doors self.colors |= other.colors + self.items |= other.items self.the_master |= other.the_master + self.postgame |= other.postgame + + for progression, index in other.progression.items(): + if progression not in self.progression or index > self.progression[progression]: + self.progression[progression] = index def __str__(self): - return f"AccessRequirements(rooms={self.rooms}, doors={self.doors}, colors={self.colors})," \ - f" the_master={self.the_master}" + return f"AccessRequirements(rooms={self.rooms}, doors={self.doors}, colors={self.colors}, items={self.items}," \ + f" progression={self.progression}), the_master={self.the_master}, postgame={self.postgame}" class PlayerLocation(NamedTuple): @@ -114,15 +126,15 @@ def set_door_item(self, room: str, door: str, item: str): self.item_by_door.setdefault(room, {})[door] = item def handle_non_grouped_door(self, room_name: str, door_data: Door, world: "LingoWorld"): - if room_name in PROGRESSION_BY_ROOM and door_data.name in PROGRESSION_BY_ROOM[room_name]: - progression_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name + if room_name in PROGRESSIVE_DOORS_BY_ROOM and door_data.name in PROGRESSIVE_DOORS_BY_ROOM[room_name]: + progression_name = PROGRESSIVE_DOORS_BY_ROOM[room_name][door_data.name].item_name progression_handling = should_split_progression(progression_name, world) if progression_handling == ProgressiveItemBehavior.SPLIT: self.set_door_item(room_name, door_data.name, door_data.item_name) self.real_items.append(door_data.item_name) elif progression_handling == ProgressiveItemBehavior.PROGRESSIVE: - progressive_item_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name + progressive_item_name = PROGRESSIVE_DOORS_BY_ROOM[room_name][door_data.name].item_name self.set_door_item(room_name, door_data.name, progressive_item_name) self.real_items.append(progressive_item_name) else: @@ -153,17 +165,31 @@ def __init__(self, world: "LingoWorld"): victory_condition = world.options.victory_condition early_color_hallways = world.options.early_color_hallways - if location_checks == LocationChecks.option_reduced and door_shuffle != ShuffleDoors.option_none: - raise OptionError("You cannot have reduced location checks when door shuffle is on, because there would not" - " be enough locations for all of the door items.") + if location_checks == LocationChecks.option_reduced: + if door_shuffle == ShuffleDoors.option_doors: + raise OptionError(f"Slot \"{world.player_name}\" cannot have reduced location checks when door shuffle" + f" is on, because there would not be enough locations for all of the door items.") + if door_shuffle == ShuffleDoors.option_panels: + if not world.options.group_doors: + raise OptionError(f"Slot \"{world.player_name}\" cannot have reduced location checks when ungrouped" + f" panels mode door shuffle is on, because there would not be enough locations for" + f" all of the panel items.") + if color_shuffle: + raise OptionError(f"Slot \"{world.player_name}\" cannot have reduced location checks with both" + f" panels mode door shuffle and color shuffle because there would not be enough" + f" locations for all of the items.") + if world.options.sunwarp_access >= SunwarpAccess.option_individual: + raise OptionError(f"Slot \"{world.player_name}\" cannot have reduced location checks with both" + f" panels mode door shuffle and individual or progressive sunwarp access because" + f" there would not be enough locations for all of the items.") # Create door items, where needed. door_groups: Set[str] = set() for room_name, room_data in DOORS_BY_ROOM.items(): for door_name, door_data in room_data.items(): if door_data.skip_item is False and door_data.event is False: - if door_data.type == DoorType.NORMAL and door_shuffle != ShuffleDoors.option_none: - if door_data.door_group is not None and door_shuffle == ShuffleDoors.option_simple: + if door_data.type == DoorType.NORMAL and door_shuffle == ShuffleDoors.option_doors: + if door_data.door_group is not None and world.options.group_doors: # Grouped doors are handled differently if shuffle doors is on simple. self.set_door_item(room_name, door_name, door_data.door_group) door_groups.add(door_data.door_group) @@ -185,21 +211,33 @@ def __init__(self, world: "LingoWorld"): self.real_items.append(door_data.item_name) self.real_items += door_groups - + + # Create panel items, where needed. + if world.options.shuffle_doors == ShuffleDoors.option_panels: + panel_groups: Set[str] = set() + + for room_name, room_data in PANEL_DOORS_BY_ROOM.items(): + for panel_door_name, panel_door_data in room_data.items(): + if panel_door_data.panel_group is not None and world.options.group_doors: + panel_groups.add(panel_door_data.panel_group) + elif room_name in PROGRESSIVE_PANELS_BY_ROOM \ + and panel_door_name in PROGRESSIVE_PANELS_BY_ROOM[room_name]: + progression_obj = PROGRESSIVE_PANELS_BY_ROOM[room_name][panel_door_name] + progression_handling = should_split_progression(progression_obj.item_name, world) + + if progression_handling == ProgressiveItemBehavior.SPLIT: + self.real_items.append(panel_door_data.item_name) + elif progression_handling == ProgressiveItemBehavior.PROGRESSIVE: + self.real_items.append(progression_obj.item_name) + else: + self.real_items.append(panel_door_data.item_name) + + self.real_items += panel_groups + # Create color items, if needed. if color_shuffle: self.real_items += [name for name, item in ALL_ITEM_TABLE.items() if item.type == ItemType.COLOR] - # Create events for each achievement panel, so that we can determine when THE MASTER is accessible. - for room_name, room_data in PANELS_BY_ROOM.items(): - for panel_name, panel_data in room_data.items(): - if panel_data.achievement: - access_req = AccessRequirements() - access_req.merge(self.calculate_panel_requirements(room_name, panel_name, world)) - access_req.rooms.add(room_name) - - self.mastery_reqs.append(access_req) - # Handle the victory condition. Victory conditions other than the chosen one become regular checks, so we need # to prevent the actual victory condition from becoming a check. self.mastery_location = "Orange Tower Seventh Floor - THE MASTER" @@ -207,7 +245,7 @@ def __init__(self, world: "LingoWorld"): if victory_condition == VictoryCondition.option_the_end: self.victory_condition = "Orange Tower Seventh Floor - THE END" - self.add_location("Orange Tower Seventh Floor", "The End (Solved)", None, [], world) + self.add_location("Ending Area", "The End (Solved)", None, [], world) self.event_loc_to_item["The End (Solved)"] = "Victory" elif victory_condition == VictoryCondition.option_the_master: self.victory_condition = "Orange Tower Seventh Floor - THE MASTER" @@ -231,6 +269,16 @@ def __init__(self, world: "LingoWorld"): [RoomAndPanel("Pilgrim Antechamber", "PILGRIM")], world) self.event_loc_to_item["PILGRIM (Solved)"] = "Victory" + # Create events for each achievement panel, so that we can determine when THE MASTER is accessible. + for room_name, room_data in PANELS_BY_ROOM.items(): + for panel_name, panel_data in room_data.items(): + if panel_data.achievement: + access_req = AccessRequirements() + access_req.merge(self.calculate_panel_requirements(room_name, panel_name, world)) + access_req.rooms.add(room_name) + + self.mastery_reqs.append(access_req) + # Create groups of counting panel access requirements for the LEVEL 2 check. self.create_panel_hunt_events(world) @@ -241,7 +289,7 @@ def __init__(self, world: "LingoWorld"): elif location_checks == LocationChecks.option_insanity: location_classification = LocationClassification.insanity - if door_shuffle != ShuffleDoors.option_none and not early_color_hallways: + if door_shuffle == ShuffleDoors.option_doors and not early_color_hallways: location_classification |= LocationClassification.small_sphere_one for location_name, location_data in ALL_LOCATION_TABLE.items(): @@ -283,7 +331,7 @@ def __init__(self, world: "LingoWorld"): "iterations. This is very unlikely to happen on its own, and probably indicates some " "kind of logic error.") - if door_shuffle != ShuffleDoors.option_none and location_checks != LocationChecks.option_insanity \ + if door_shuffle == ShuffleDoors.option_doors and location_checks != LocationChecks.option_insanity \ and not early_color_hallways and world.multiworld.players > 1: # Under the combination of door shuffle, normal location checks, and no early color hallways, sphere 1 is # only three checks. In a multiplayer situation, this can be frustrating for the player because they are @@ -298,19 +346,19 @@ def __init__(self, world: "LingoWorld"): # Starting Room - Exit Door gives access to OPEN and TRACE. good_item_options: List[str] = ["Starting Room - Back Right Door", "Second Room - Exit Door"] - if not color_shuffle and not world.options.enable_pilgrimage: - # HOT CRUST and THIS. - good_item_options.append("Pilgrim Room - Sun Painting") - if not color_shuffle: - if door_shuffle == ShuffleDoors.option_simple: + if not world.options.enable_pilgrimage: + # HOT CRUST and THIS. + good_item_options.append("Pilgrim Room - Sun Painting") + + if world.options.group_doors: # WELCOME BACK, CLOCKWISE, and DRAWL + RUNS. good_item_options.append("Welcome Back Doors") else: # WELCOME BACK and CLOCKWISE. good_item_options.append("Welcome Back Area - Shortcut to Starting Room") - if door_shuffle == ShuffleDoors.option_simple: + if world.options.group_doors: # Color hallways access (NOTE: reconsider when sunwarp shuffling exists). good_item_options.append("Rhyme Room Doors") @@ -356,13 +404,11 @@ def __init__(self, world: "LingoWorld"): def randomize_paintings(self, world: "LingoWorld") -> bool: self.painting_mapping.clear() - door_shuffle = world.options.shuffle_doors - # First, assign mappings to the required-exit paintings. We ensure that req-blocked paintings do not lead to # required paintings. req_exits = [] required_painting_rooms = REQUIRED_PAINTING_ROOMS - if door_shuffle == ShuffleDoors.option_none: + if world.options.shuffle_doors != ShuffleDoors.option_doors: required_painting_rooms += REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS req_exits = [painting_id for painting_id, painting in PAINTINGS.items() if painting.required_when_no_doors] @@ -429,7 +475,7 @@ def is_req_enterable(painting_id: str, painting: Painting) -> bool: for painting_id, painting in PAINTINGS.items(): if painting_id not in self.painting_mapping.values() \ and (painting.required or (painting.required_when_no_doors and - door_shuffle == ShuffleDoors.option_none)): + world.options.shuffle_doors != ShuffleDoors.option_doors)): return False return True @@ -444,12 +490,31 @@ def calculate_panel_requirements(self, room: str, panel: str, world: "LingoWorld access_reqs = AccessRequirements() panel_object = PANELS_BY_ROOM[room][panel] + if world.options.shuffle_doors == ShuffleDoors.option_panels and panel_object.panel_door is not None: + panel_door_room = panel_object.panel_door.room + panel_door_name = panel_object.panel_door.panel_door + panel_door = PANEL_DOORS_BY_ROOM[panel_door_room][panel_door_name] + + if panel_door.panel_group is not None and world.options.group_doors: + access_reqs.items.add(panel_door.panel_group) + elif panel_door_room in PROGRESSIVE_PANELS_BY_ROOM\ + and panel_door_name in PROGRESSIVE_PANELS_BY_ROOM[panel_door_room]: + progression_obj = PROGRESSIVE_PANELS_BY_ROOM[panel_door_room][panel_door_name] + progression_handling = should_split_progression(progression_obj.item_name, world) + + if progression_handling == ProgressiveItemBehavior.SPLIT: + access_reqs.items.add(panel_door.item_name) + elif progression_handling == ProgressiveItemBehavior.PROGRESSIVE: + access_reqs.progression[progression_obj.item_name] = progression_obj.index + else: + access_reqs.items.add(panel_door.item_name) + for req_room in panel_object.required_rooms: access_reqs.rooms.add(req_room) for req_door in panel_object.required_doors: door_object = DOORS_BY_ROOM[room if req_door.room is None else req_door.room][req_door.door] - if door_object.event or world.options.shuffle_doors == ShuffleDoors.option_none: + if door_object.event or world.options.shuffle_doors != ShuffleDoors.option_doors: sub_access_reqs = self.calculate_door_requirements( room if req_door.room is None else req_door.room, req_door.door, world) access_reqs.merge(sub_access_reqs) @@ -470,6 +535,11 @@ def calculate_panel_requirements(self, room: str, panel: str, world: "LingoWorld if panel == "THE MASTER": access_reqs.the_master = True + # Evil python magic (so sayeth NewSoupVi): this checks victory_condition against the panel's location name + # override if it exists, or the auto-generated location name if it's None. + if self.victory_condition == (panel_object.location_name or f"{room} - {panel}"): + access_reqs.postgame = True + self.panel_reqs[room][panel] = access_reqs return self.panel_reqs[room][panel] @@ -514,11 +584,14 @@ def create_panel_hunt_events(self, world: "LingoWorld"): continue # We won't coalesce any panels that have requirements beyond colors. To simplify things for now, we will - # only coalesce single-color panels. Chains/stacks/combo puzzles will be separate. THE MASTER has - # special access rules and is handled separately. + # only coalesce single-color panels. Chains/stacks/combo puzzles will be separate. Panel door locked + # puzzles will be separate if panels mode is on. THE MASTER has special access rules and is handled + # separately. if len(panel_data.required_panels) > 0 or len(panel_data.required_doors) > 0\ or len(panel_data.required_rooms) > 0\ or (world.options.shuffle_colors and len(panel_data.colors) > 1)\ + or (world.options.shuffle_doors == ShuffleDoors.option_panels + and panel_data.panel_door is not None)\ or panel_name == "THE MASTER": self.counting_panel_reqs.setdefault(room_name, []).append( (self.calculate_panel_requirements(room_name, panel_name, world), 1)) diff --git a/worlds/lingo/regions.py b/worlds/lingo/regions.py index 9834f04f9de..9773f22d919 100644 --- a/worlds/lingo/regions.py +++ b/worlds/lingo/regions.py @@ -159,7 +159,7 @@ def create_regions(world: "LingoWorld") -> None: RoomAndDoor("Pilgrim Antechamber", "Sun Painting"), EntranceType.PAINTING, False, world) if early_color_hallways: - connect_entrance(regions, regions["Starting Room"], regions["Outside The Undeterred"], "Early Color Hallways", + connect_entrance(regions, regions["Starting Room"], regions["Color Hallways"], "Early Color Hallways", None, EntranceType.PAINTING, False, world) if painting_shuffle: diff --git a/worlds/lingo/rules.py b/worlds/lingo/rules.py index d91c53f05b4..e0bb08fa1f7 100644 --- a/worlds/lingo/rules.py +++ b/worlds/lingo/rules.py @@ -3,7 +3,7 @@ from BaseClasses import CollectionState from .datatypes import RoomAndDoor from .player_logic import AccessRequirements, PlayerLocation -from .static_logic import PROGRESSION_BY_ROOM, PROGRESSIVE_ITEMS +from .static_logic import PROGRESSIVE_DOORS_BY_ROOM, PROGRESSIVE_ITEMS if TYPE_CHECKING: from . import LingoWorld @@ -59,9 +59,18 @@ def _lingo_can_satisfy_requirements(state: CollectionState, access: AccessRequir if not state.has(color.capitalize(), world.player): return False + if not all(state.has(item, world.player) for item in access.items): + return False + + if not all(state.has(item, world.player, index) for item, index in access.progression.items()): + return False + if access.the_master and not lingo_can_use_mastery_location(state, world): return False + if access.postgame and state.has("Prevent Victory", world.player): + return False + return True @@ -74,7 +83,7 @@ def _lingo_can_open_door(state: CollectionState, room: str, door: str, world: "L item_name = world.player_logic.item_by_door[room][door] if item_name in PROGRESSIVE_ITEMS: - progression = PROGRESSION_BY_ROOM[room][door] + progression = PROGRESSIVE_DOORS_BY_ROOM[room][door] return state.has(item_name, world.player, progression.index) return state.has(item_name, world.player) diff --git a/worlds/lingo/static_logic.py b/worlds/lingo/static_logic.py index ff820dd0cb1..74eea449f22 100644 --- a/worlds/lingo/static_logic.py +++ b/worlds/lingo/static_logic.py @@ -4,15 +4,17 @@ from io import BytesIO from typing import Dict, List, Set -from .datatypes import Door, Painting, Panel, Progression, Room +from .datatypes import Door, Painting, Panel, PanelDoor, Progression, Room ALL_ROOMS: List[Room] = [] DOORS_BY_ROOM: Dict[str, Dict[str, Door]] = {} PANELS_BY_ROOM: Dict[str, Dict[str, Panel]] = {} +PANEL_DOORS_BY_ROOM: Dict[str, Dict[str, PanelDoor]] = {} PAINTINGS: Dict[str, Painting] = {} -PROGRESSIVE_ITEMS: List[str] = [] -PROGRESSION_BY_ROOM: Dict[str, Dict[str, Progression]] = {} +PROGRESSIVE_ITEMS: Set[str] = set() +PROGRESSIVE_DOORS_BY_ROOM: Dict[str, Dict[str, Progression]] = {} +PROGRESSIVE_PANELS_BY_ROOM: Dict[str, Dict[str, Progression]] = {} PAINTING_ENTRANCES: int = 0 PAINTING_EXIT_ROOMS: Set[str] = set() @@ -28,6 +30,8 @@ DOOR_LOCATION_IDS: Dict[str, Dict[str, int]] = {} DOOR_ITEM_IDS: Dict[str, Dict[str, int]] = {} DOOR_GROUP_ITEM_IDS: Dict[str, int] = {} +PANEL_DOOR_ITEM_IDS: Dict[str, Dict[str, int]] = {} +PANEL_GROUP_ITEM_IDS: Dict[str, int] = {} PROGRESSIVE_ITEM_IDS: Dict[str, int] = {} HASHES: Dict[str, str] = {} @@ -68,6 +72,20 @@ def get_door_group_item_id(name: str): return DOOR_GROUP_ITEM_IDS[name] +def get_panel_door_item_id(room: str, name: str): + if room not in PANEL_DOOR_ITEM_IDS or name not in PANEL_DOOR_ITEM_IDS[room]: + raise Exception(f"Item ID for panel door {room} - {name} not found in ids.yaml.") + + return PANEL_DOOR_ITEM_IDS[room][name] + + +def get_panel_group_item_id(name: str): + if name not in PANEL_GROUP_ITEM_IDS: + raise Exception(f"Item ID for panel group {name} not found in ids.yaml.") + + return PANEL_GROUP_ITEM_IDS[name] + + def get_progressive_item_id(name: str): if name not in PROGRESSIVE_ITEM_IDS: raise Exception(f"Item ID for progressive item {name} not found in ids.yaml.") @@ -97,8 +115,10 @@ def find_class(self, module, name): ALL_ROOMS.extend(pickdata["ALL_ROOMS"]) DOORS_BY_ROOM.update(pickdata["DOORS_BY_ROOM"]) PANELS_BY_ROOM.update(pickdata["PANELS_BY_ROOM"]) - PROGRESSIVE_ITEMS.extend(pickdata["PROGRESSIVE_ITEMS"]) - PROGRESSION_BY_ROOM.update(pickdata["PROGRESSION_BY_ROOM"]) + PANEL_DOORS_BY_ROOM.update(pickdata["PANEL_DOORS_BY_ROOM"]) + PROGRESSIVE_ITEMS.update(pickdata["PROGRESSIVE_ITEMS"]) + PROGRESSIVE_DOORS_BY_ROOM.update(pickdata["PROGRESSIVE_DOORS_BY_ROOM"]) + PROGRESSIVE_PANELS_BY_ROOM.update(pickdata["PROGRESSIVE_PANELS_BY_ROOM"]) PAINTING_ENTRANCES = pickdata["PAINTING_ENTRANCES"] PAINTING_EXIT_ROOMS.update(pickdata["PAINTING_EXIT_ROOMS"]) PAINTING_EXITS = pickdata["PAINTING_EXITS"] @@ -111,6 +131,8 @@ def find_class(self, module, name): DOOR_LOCATION_IDS.update(pickdata["DOOR_LOCATION_IDS"]) DOOR_ITEM_IDS.update(pickdata["DOOR_ITEM_IDS"]) DOOR_GROUP_ITEM_IDS.update(pickdata["DOOR_GROUP_ITEM_IDS"]) + PANEL_DOOR_ITEM_IDS.update(pickdata["PANEL_DOOR_ITEM_IDS"]) + PANEL_GROUP_ITEM_IDS.update(pickdata["PANEL_GROUP_ITEM_IDS"]) PROGRESSIVE_ITEM_IDS.update(pickdata["PROGRESSIVE_ITEM_IDS"]) diff --git a/worlds/lingo/test/TestDoors.py b/worlds/lingo/test/TestDoors.py index f496c5f5785..cfbd7f30278 100644 --- a/worlds/lingo/test/TestDoors.py +++ b/worlds/lingo/test/TestDoors.py @@ -3,7 +3,7 @@ class TestRequiredRoomLogic(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", "shuffle_colors": "false", } @@ -50,7 +50,7 @@ def test_hidden_first(self) -> None: class TestRequiredDoorLogic(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", "shuffle_colors": "false", } @@ -78,7 +78,8 @@ def test_through_hidden(self) -> None: class TestSimpleDoors(LingoTestBase): options = { - "shuffle_doors": "simple", + "shuffle_doors": "doors", + "group_doors": "true", "shuffle_colors": "false", } @@ -90,3 +91,52 @@ def test_requirement(self): self.assertTrue(self.multiworld.state.can_reach("Outside The Wanderer", "Region", self.player)) self.assertTrue(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player)) + +class TestPanels(LingoTestBase): + options = { + "shuffle_doors": "panels" + } + + def test_requirement(self): + self.assertFalse(self.can_reach_location("Starting Room - HIDDEN")) + self.assertFalse(self.can_reach_location("Hidden Room - OPEN")) + self.assertFalse(self.can_reach_location("The Seeker - Achievement")) + + self.collect_by_name("Starting Room - HIDDEN (Panel)") + self.assertTrue(self.can_reach_location("Starting Room - HIDDEN")) + self.assertFalse(self.can_reach_location("Hidden Room - OPEN")) + self.assertFalse(self.can_reach_location("The Seeker - Achievement")) + + self.collect_by_name("Hidden Room - OPEN (Panel)") + self.assertTrue(self.can_reach_location("Starting Room - HIDDEN")) + self.assertTrue(self.can_reach_location("Hidden Room - OPEN")) + self.assertTrue(self.can_reach_location("The Seeker - Achievement")) + + +class TestGroupedPanels(LingoTestBase): + options = { + "shuffle_doors": "panels", + "group_doors": "true", + "shuffle_colors": "false", + } + + def test_requirement(self): + self.assertFalse(self.can_reach_location("Hub Room - SLAUGHTER")) + self.assertFalse(self.can_reach_location("Dread Hallway - DREAD")) + self.assertFalse(self.can_reach_location("The Tenacious - Achievement")) + + self.collect_by_name("Tenacious Entrance Panels") + self.assertTrue(self.can_reach_location("Hub Room - SLAUGHTER")) + self.assertFalse(self.can_reach_location("Dread Hallway - DREAD")) + self.assertFalse(self.can_reach_location("The Tenacious - Achievement")) + + self.collect_by_name("Outside The Agreeable - BLACK (Panel)") + self.assertTrue(self.can_reach_location("Hub Room - SLAUGHTER")) + self.assertTrue(self.can_reach_location("Dread Hallway - DREAD")) + self.assertFalse(self.can_reach_location("The Tenacious - Achievement")) + + self.collect_by_name("The Tenacious - Black Palindromes (Panels)") + self.assertTrue(self.can_reach_location("Hub Room - SLAUGHTER")) + self.assertTrue(self.can_reach_location("Dread Hallway - DREAD")) + self.assertTrue(self.can_reach_location("The Tenacious - Achievement")) + diff --git a/worlds/lingo/test/TestMastery.py b/worlds/lingo/test/TestMastery.py index 3ebe40aa22d..c9c79a9d065 100644 --- a/worlds/lingo/test/TestMastery.py +++ b/worlds/lingo/test/TestMastery.py @@ -5,7 +5,8 @@ class TestMasteryWhenVictoryIsTheEnd(LingoTestBase): options = { "mastery_achievements": "22", "victory_condition": "the_end", - "shuffle_colors": "true" + "shuffle_colors": "true", + "shuffle_postgame": "true", } def test_requirement(self): @@ -43,7 +44,8 @@ class TestMasteryBlocksDependents(LingoTestBase): options = { "mastery_achievements": "24", "shuffle_colors": "true", - "location_checks": "insanity" + "location_checks": "insanity", + "victory_condition": "level_2", } def test_requirement(self): diff --git a/worlds/lingo/test/TestOptions.py b/worlds/lingo/test/TestOptions.py index fce07431163..bd8ed81d7a1 100644 --- a/worlds/lingo/test/TestOptions.py +++ b/worlds/lingo/test/TestOptions.py @@ -3,7 +3,7 @@ class TestMultiShuffleOptions(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", "progressive_orange_tower": "true", "shuffle_colors": "true", "shuffle_paintings": "true", @@ -13,7 +13,7 @@ class TestMultiShuffleOptions(LingoTestBase): class TestPanelsanity(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", "progressive_orange_tower": "true", "location_checks": "insanity", "shuffle_colors": "true" @@ -22,7 +22,18 @@ class TestPanelsanity(LingoTestBase): class TestAllPanelHunt(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", + "progressive_orange_tower": "true", + "shuffle_colors": "true", + "victory_condition": "level_2", + "level_2_requirement": "800", + "early_color_hallways": "true" + } + + +class TestAllPanelHuntPanelsMode(LingoTestBase): + options = { + "shuffle_doors": "panels", "progressive_orange_tower": "true", "shuffle_colors": "true", "victory_condition": "level_2", diff --git a/worlds/lingo/test/TestOrangeTower.py b/worlds/lingo/test/TestOrangeTower.py index 7b0c3bb5251..444264a5896 100644 --- a/worlds/lingo/test/TestOrangeTower.py +++ b/worlds/lingo/test/TestOrangeTower.py @@ -3,7 +3,7 @@ class TestProgressiveOrangeTower(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", "progressive_orange_tower": "true" } diff --git a/worlds/lingo/test/TestPanelsanity.py b/worlds/lingo/test/TestPanelsanity.py index 34c1b3815a4..f8330ae7820 100644 --- a/worlds/lingo/test/TestPanelsanity.py +++ b/worlds/lingo/test/TestPanelsanity.py @@ -3,7 +3,7 @@ class TestPanelHunt(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", "location_checks": "insanity", "victory_condition": "level_2", "level_2_requirement": "15" diff --git a/worlds/lingo/test/TestPilgrimage.py b/worlds/lingo/test/TestPilgrimage.py index 3cc91940017..328156da2d1 100644 --- a/worlds/lingo/test/TestPilgrimage.py +++ b/worlds/lingo/test/TestPilgrimage.py @@ -18,7 +18,7 @@ class TestPilgrimageWithRoofAndPaintings(LingoTestBase): options = { "enable_pilgrimage": "true", "shuffle_colors": "false", - "shuffle_doors": "complex", + "shuffle_doors": "doors", "pilgrimage_allows_roof_access": "true", "pilgrimage_allows_paintings": "true", "early_color_hallways": "false" @@ -29,7 +29,6 @@ def test_access(self): "Outside The Undeterred - Green Painting"] for door in doors: - print(door) self.assertFalse(self.can_reach_location("Pilgrim Antechamber - PILGRIM")) self.collect_by_name(door) @@ -40,7 +39,7 @@ class TestPilgrimageNoRoofYesPaintings(LingoTestBase): options = { "enable_pilgrimage": "true", "shuffle_colors": "false", - "shuffle_doors": "complex", + "shuffle_doors": "doors", "pilgrimage_allows_roof_access": "false", "pilgrimage_allows_paintings": "true", "early_color_hallways": "false" @@ -53,7 +52,6 @@ def test_access(self): "Starting Room - Street Painting"] for door in doors: - print(door) self.assertFalse(self.can_reach_location("Pilgrim Antechamber - PILGRIM")) self.collect_by_name(door) @@ -64,7 +62,7 @@ class TestPilgrimageNoRoofNoPaintings(LingoTestBase): options = { "enable_pilgrimage": "true", "shuffle_colors": "false", - "shuffle_doors": "complex", + "shuffle_doors": "doors", "pilgrimage_allows_roof_access": "false", "pilgrimage_allows_paintings": "false", "early_color_hallways": "false" @@ -81,18 +79,45 @@ def test_access(self): "Orange Tower Fourth Floor - Hot Crusts Door"] for door in doors: - print(door) self.assertFalse(self.can_reach_location("Pilgrim Antechamber - PILGRIM")) self.collect_by_name(door) self.assertTrue(self.can_reach_location("Pilgrim Antechamber - PILGRIM")) -class TestPilgrimageYesRoofNoPaintings(LingoTestBase): +class TestPilgrimageRequireStartingRoom(LingoTestBase): options = { "enable_pilgrimage": "true", "shuffle_colors": "false", "shuffle_doors": "complex", + "pilgrimage_allows_roof_access": "false", + "pilgrimage_allows_paintings": "false", + "early_color_hallways": "false" + } + + def test_access(self): + doors = ["Second Room - Exit Door", "Crossroads - Roof Access", "Hub Room - Crossroads Entrance", + "Outside The Undeterred - Green Painting", "Outside The Undeterred - Number Hunt", + "Starting Room - Street Painting", "Outside The Initiated - Shortcut to Hub Room", + "Directional Gallery - Shortcut to The Undeterred", "Orange Tower First Floor - Salt Pepper Door", + "Color Hunt - Shortcut to The Steady", "The Bearer - Entrance", + "Orange Tower Fifth Floor - Quadruple Intersection", "The Tenacious - Shortcut to Hub Room", + "Outside The Agreeable - Tenacious Entrance", "Crossroads - Tower Entrance", + "Orange Tower Fourth Floor - Hot Crusts Door", "Challenge Room - Welcome Door", + "Number Hunt - Challenge Entrance", "Welcome Back Area - Shortcut to Starting Room"] + + for door in doors: + self.assertFalse(self.can_reach_location("Pilgrim Antechamber - PILGRIM")) + self.collect_by_name(door) + + self.assertTrue(self.can_reach_location("Pilgrim Antechamber - PILGRIM")) + + +class TestPilgrimageYesRoofNoPaintings(LingoTestBase): + options = { + "enable_pilgrimage": "true", + "shuffle_colors": "false", + "shuffle_doors": "doors", "pilgrimage_allows_roof_access": "true", "pilgrimage_allows_paintings": "false", "early_color_hallways": "false" @@ -107,7 +132,6 @@ def test_access(self): "Orange Tower Fifth Floor - Quadruple Intersection"] for door in doors: - print(door) self.assertFalse(self.can_reach_location("Pilgrim Antechamber - PILGRIM")) self.collect_by_name(door) diff --git a/worlds/lingo/test/TestPostgame.py b/worlds/lingo/test/TestPostgame.py new file mode 100644 index 00000000000..d2e2232ff76 --- /dev/null +++ b/worlds/lingo/test/TestPostgame.py @@ -0,0 +1,62 @@ +from . import LingoTestBase + + +class TestPostgameVanillaTheEnd(LingoTestBase): + options = { + "shuffle_doors": "none", + "victory_condition": "the_end", + "shuffle_postgame": "false", + } + + def test_requirement(self): + location_names = [location.name for location in self.multiworld.get_locations(self.player)] + + self.assertTrue("The End (Solved)" in location_names) + self.assertTrue("Champion's Rest - YOU" in location_names) + self.assertFalse("Orange Tower Seventh Floor - THE MASTER" in location_names) + self.assertFalse("The Red - Achievement" in location_names) + + +class TestPostgameComplexDoorsTheEnd(LingoTestBase): + options = { + "shuffle_doors": "complex", + "victory_condition": "the_end", + "shuffle_postgame": "false", + } + + def test_requirement(self): + location_names = [location.name for location in self.multiworld.get_locations(self.player)] + + self.assertTrue("The End (Solved)" in location_names) + self.assertFalse("Orange Tower Seventh Floor - THE MASTER" in location_names) + self.assertTrue("The Red - Achievement" in location_names) + + +class TestPostgameLateColorHunt(LingoTestBase): + options = { + "shuffle_doors": "none", + "victory_condition": "the_end", + "sunwarp_access": "disabled", + "shuffle_postgame": "false", + } + + def test_requirement(self): + location_names = [location.name for location in self.multiworld.get_locations(self.player)] + + self.assertFalse("Champion's Rest - YOU" in location_names) + + +class TestPostgameVanillaTheMaster(LingoTestBase): + options = { + "shuffle_doors": "none", + "victory_condition": "the_master", + "shuffle_postgame": "false", + } + + def test_requirement(self): + location_names = [location.name for location in self.multiworld.get_locations(self.player)] + + self.assertTrue("Orange Tower Seventh Floor - THE END" in location_names) + self.assertTrue("Orange Tower Seventh Floor - Mastery Achievements" in location_names) + self.assertTrue("The Red - Achievement" in location_names) + self.assertFalse("Mastery Panels" in location_names) diff --git a/worlds/lingo/test/TestProgressive.py b/worlds/lingo/test/TestProgressive.py index e79fd6bc908..2c837f53f34 100644 --- a/worlds/lingo/test/TestProgressive.py +++ b/worlds/lingo/test/TestProgressive.py @@ -3,7 +3,7 @@ class TestComplexProgressiveHallwayRoom(LingoTestBase): options = { - "shuffle_doors": "complex" + "shuffle_doors": "doors" } def test_item(self): @@ -54,7 +54,8 @@ def test_item(self): class TestSimpleHallwayRoom(LingoTestBase): options = { - "shuffle_doors": "simple" + "shuffle_doors": "doors", + "group_doors": "true", } def test_item(self): @@ -81,7 +82,7 @@ def test_item(self): class TestProgressiveArtGallery(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", "shuffle_colors": "false", } diff --git a/worlds/lingo/test/TestSunwarps.py b/worlds/lingo/test/TestSunwarps.py index e8e913c4f49..66ba3afd6e9 100644 --- a/worlds/lingo/test/TestSunwarps.py +++ b/worlds/lingo/test/TestSunwarps.py @@ -19,7 +19,8 @@ def test_access(self): class TestSimpleDoorsNormalSunwarps(LingoTestBase): options = { - "shuffle_doors": "simple", + "shuffle_doors": "doors", + "group_doors": "true", "sunwarp_access": "normal" } @@ -37,7 +38,8 @@ def test_access(self): class TestSimpleDoorsDisabledSunwarps(LingoTestBase): options = { - "shuffle_doors": "simple", + "shuffle_doors": "doors", + "group_doors": "true", "sunwarp_access": "disabled" } @@ -56,7 +58,8 @@ def test_access(self): class TestSimpleDoorsUnlockSunwarps(LingoTestBase): options = { - "shuffle_doors": "simple", + "shuffle_doors": "doors", + "group_doors": "true", "sunwarp_access": "unlock" } @@ -78,7 +81,8 @@ def test_access(self): class TestComplexDoorsNormalSunwarps(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", + "group_doors": "false", "sunwarp_access": "normal" } @@ -96,7 +100,8 @@ def test_access(self): class TestComplexDoorsDisabledSunwarps(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", + "group_doors": "false", "sunwarp_access": "disabled" } @@ -115,7 +120,8 @@ def test_access(self): class TestComplexDoorsIndividualSunwarps(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", + "group_doors": "false", "sunwarp_access": "individual" } @@ -142,7 +148,8 @@ def test_access(self): class TestComplexDoorsProgressiveSunwarps(LingoTestBase): options = { - "shuffle_doors": "complex", + "shuffle_doors": "doors", + "group_doors": "false", "sunwarp_access": "progressive" } diff --git a/worlds/lingo/utils/assign_ids.rb b/worlds/lingo/utils/assign_ids.rb index 9e1ce67bd2d..f7de3d03f58 100644 --- a/worlds/lingo/utils/assign_ids.rb +++ b/worlds/lingo/utils/assign_ids.rb @@ -73,6 +73,22 @@ end end end +if old_generated.include? "panel_doors" then + old_generated["panel_doors"].each do |room, panel_doors| + panel_doors.each do |name, id| + if id >= next_item_id then + next_item_id = id + 1 + end + end + end +end +if old_generated.include? "panel_groups" then + old_generated["panel_groups"].each do |name, id| + if id >= next_item_id then + next_item_id = id + 1 + end + end +end if old_generated.include? "progression" then old_generated["progression"].each do |name, id| if id >= next_item_id then @@ -82,6 +98,7 @@ end door_groups = Set[] +panel_groups = Set[] config = YAML.load_file(configpath) config.each do |room_name, room_data| @@ -163,6 +180,29 @@ end end + if room_data.include? "panel_doors" + room_data["panel_doors"].each do |panel_door_name, panel_door| + unless old_generated.include? "panel_doors" and old_generated["panel_doors"].include? room_name and old_generated["panel_doors"][room_name].include? panel_door_name then + old_generated["panel_doors"] ||= {} + old_generated["panel_doors"][room_name] ||= {} + old_generated["panel_doors"][room_name][panel_door_name] = next_item_id + + next_item_id += 1 + end + + if panel_door.include? "panel_group" and not panel_groups.include? panel_door["panel_group"] then + panel_groups.add(panel_door["panel_group"]) + + unless old_generated.include? "panel_groups" and old_generated["panel_groups"].include? panel_door["panel_group"] then + old_generated["panel_groups"] ||= {} + old_generated["panel_groups"][panel_door["panel_group"]] = next_item_id + + next_item_id += 1 + end + end + end + end + if room_data.include? "progression" room_data["progression"].each do |progression_name, pdata| unless old_generated.include? "progression" and old_generated["progression"].include? progression_name then diff --git a/worlds/lingo/utils/pickle_static_data.py b/worlds/lingo/utils/pickle_static_data.py index e40c21ce3e6..92bcb7a859e 100644 --- a/worlds/lingo/utils/pickle_static_data.py +++ b/worlds/lingo/utils/pickle_static_data.py @@ -6,8 +6,8 @@ sys.path.append(os.path.join("worlds", "lingo")) sys.path.append(".") sys.path.append("..") -from datatypes import Door, DoorType, EntranceType, Painting, Panel, Progression, Room, RoomAndDoor, RoomAndPanel,\ - RoomEntrance +from datatypes import Door, DoorType, EntranceType, Painting, Panel, PanelDoor, Progression, Room, RoomAndDoor,\ + RoomAndPanel, RoomAndPanelDoor, RoomEntrance import hashlib import pickle @@ -18,10 +18,12 @@ ALL_ROOMS: List[Room] = [] DOORS_BY_ROOM: Dict[str, Dict[str, Door]] = {} PANELS_BY_ROOM: Dict[str, Dict[str, Panel]] = {} +PANEL_DOORS_BY_ROOM: Dict[str, Dict[str, PanelDoor]] = {} PAINTINGS: Dict[str, Painting] = {} -PROGRESSIVE_ITEMS: List[str] = [] -PROGRESSION_BY_ROOM: Dict[str, Dict[str, Progression]] = {} +PROGRESSIVE_ITEMS: Set[str] = set() +PROGRESSIVE_DOORS_BY_ROOM: Dict[str, Dict[str, Progression]] = {} +PROGRESSIVE_PANELS_BY_ROOM: Dict[str, Dict[str, Progression]] = {} PAINTING_ENTRANCES: int = 0 PAINTING_EXIT_ROOMS: Set[str] = set() @@ -37,8 +39,13 @@ DOOR_LOCATION_IDS: Dict[str, Dict[str, int]] = {} DOOR_ITEM_IDS: Dict[str, Dict[str, int]] = {} DOOR_GROUP_ITEM_IDS: Dict[str, int] = {} +PANEL_DOOR_ITEM_IDS: Dict[str, Dict[str, int]] = {} +PANEL_GROUP_ITEM_IDS: Dict[str, int] = {} PROGRESSIVE_ITEM_IDS: Dict[str, int] = {} +# This doesn't need to be stored in the datafile. +PANEL_DOOR_BY_PANEL_BY_ROOM: Dict[str, Dict[str, str]] = {} + def hash_file(path): md5 = hashlib.md5() @@ -53,7 +60,7 @@ def hash_file(path): def load_static_data(ll1_path, ids_path): global PAINTING_EXITS, SPECIAL_ITEM_IDS, PANEL_LOCATION_IDS, DOOR_LOCATION_IDS, DOOR_ITEM_IDS, \ - DOOR_GROUP_ITEM_IDS, PROGRESSIVE_ITEM_IDS + DOOR_GROUP_ITEM_IDS, PROGRESSIVE_ITEM_IDS, PANEL_DOOR_ITEM_IDS, PANEL_GROUP_ITEM_IDS # Load in all item and location IDs. These are broken up into groups based on the type of item/location. with open(ids_path, "r") as file: @@ -86,6 +93,17 @@ def load_static_data(ll1_path, ids_path): for item_name, item_id in config["door_groups"].items(): DOOR_GROUP_ITEM_IDS[item_name] = item_id + if "panel_doors" in config: + for room_name, panel_doors in config["panel_doors"].items(): + PANEL_DOOR_ITEM_IDS[room_name] = {} + + for panel_door, item_id in panel_doors.items(): + PANEL_DOOR_ITEM_IDS[room_name][panel_door] = item_id + + if "panel_groups" in config: + for item_name, item_id in config["panel_groups"].items(): + PANEL_GROUP_ITEM_IDS[item_name] = item_id + if "progression" in config: for item_name, item_id in config["progression"].items(): PROGRESSIVE_ITEM_IDS[item_name] = item_id @@ -147,6 +165,46 @@ def process_entrance(source_room, doors, room_obj): room_obj.entrances.append(RoomEntrance(source_room, door, entrance_type)) +def process_panel_door(room_name, panel_door_name, panel_door_data): + global PANEL_DOORS_BY_ROOM, PANEL_DOOR_BY_PANEL_BY_ROOM + + panels: List[RoomAndPanel] = list() + for panel in panel_door_data["panels"]: + if isinstance(panel, dict): + panels.append(RoomAndPanel(panel["room"], panel["panel"])) + else: + panels.append(RoomAndPanel(room_name, panel)) + + for panel in panels: + PANEL_DOOR_BY_PANEL_BY_ROOM.setdefault(panel.room, {})[panel.panel] = RoomAndPanelDoor(room_name, + panel_door_name) + + if "item_name" in panel_door_data: + item_name = panel_door_data["item_name"] + else: + panel_per_room = dict() + for panel in panels: + panel_room_name = room_name if panel.room is None else panel.room + panel_per_room.setdefault(panel_room_name, []).append(panel.panel) + + room_strs = list() + for door_room_str, door_panels_str in panel_per_room.items(): + room_strs.append(door_room_str + " - " + ", ".join(door_panels_str)) + + if len(panels) == 1: + item_name = f"{room_strs[0]} (Panel)" + else: + item_name = " and ".join(room_strs) + " (Panels)" + + if "panel_group" in panel_door_data: + panel_group = panel_door_data["panel_group"] + else: + panel_group = None + + panel_door_obj = PanelDoor(item_name, panel_group) + PANEL_DOORS_BY_ROOM[room_name][panel_door_name] = panel_door_obj + + def process_panel(room_name, panel_name, panel_data): global PANELS_BY_ROOM @@ -227,13 +285,18 @@ def process_panel(room_name, panel_name, panel_data): else: non_counting = False + if room_name in PANEL_DOOR_BY_PANEL_BY_ROOM and panel_name in PANEL_DOOR_BY_PANEL_BY_ROOM[room_name]: + panel_door = PANEL_DOOR_BY_PANEL_BY_ROOM[room_name][panel_name] + else: + panel_door = None + if "location_name" in panel_data: location_name = panel_data["location_name"] else: location_name = None panel_obj = Panel(required_rooms, required_doors, required_panels, colors, check, event, exclude_reduce, - achievement, non_counting, location_name) + achievement, non_counting, panel_door, location_name) PANELS_BY_ROOM[room_name][panel_name] = panel_obj @@ -325,7 +388,7 @@ def process_door(room_name, door_name, door_data): painting_ids = [] door_type = DoorType.NORMAL - if door_name.endswith(" Sunwarp"): + if room_name == "Sunwarps": door_type = DoorType.SUNWARP elif room_name == "Pilgrim Antechamber" and door_name == "Sun Painting": door_type = DoorType.SUN_PAINTING @@ -404,11 +467,11 @@ def process_sunwarp(room_name, sunwarp_data): SUNWARP_EXITS[sunwarp_data["dots"] - 1] = room_name -def process_progression(room_name, progression_name, progression_doors): - global PROGRESSIVE_ITEMS, PROGRESSION_BY_ROOM +def process_progressive_door(room_name, progression_name, progression_doors): + global PROGRESSIVE_ITEMS, PROGRESSIVE_DOORS_BY_ROOM # Progressive items are configured as a list of doors. - PROGRESSIVE_ITEMS.append(progression_name) + PROGRESSIVE_ITEMS.add(progression_name) progression_index = 1 for door in progression_doors: @@ -419,11 +482,31 @@ def process_progression(room_name, progression_name, progression_doors): door_room = room_name door_door = door - room_progressions = PROGRESSION_BY_ROOM.setdefault(door_room, {}) + room_progressions = PROGRESSIVE_DOORS_BY_ROOM.setdefault(door_room, {}) room_progressions[door_door] = Progression(progression_name, progression_index) progression_index += 1 +def process_progressive_panel(room_name, progression_name, progression_panel_doors): + global PROGRESSIVE_ITEMS, PROGRESSIVE_PANELS_BY_ROOM + + # Progressive items are configured as a list of panel doors. + PROGRESSIVE_ITEMS.add(progression_name) + + progression_index = 1 + for panel_door in progression_panel_doors: + if isinstance(panel_door, Dict): + panel_door_room = panel_door["room"] + panel_door_door = panel_door["panel_door"] + else: + panel_door_room = room_name + panel_door_door = panel_door + + room_progressions = PROGRESSIVE_PANELS_BY_ROOM.setdefault(panel_door_room, {}) + room_progressions[panel_door_door] = Progression(progression_name, progression_index) + progression_index += 1 + + def process_room(room_name, room_data): global ALL_ROOMS @@ -433,6 +516,12 @@ def process_room(room_name, room_data): for source_room, doors in room_data["entrances"].items(): process_entrance(source_room, doors, room_obj) + if "panel_doors" in room_data: + PANEL_DOORS_BY_ROOM[room_name] = dict() + + for panel_door_name, panel_door_data in room_data["panel_doors"].items(): + process_panel_door(room_name, panel_door_name, panel_door_data) + if "panels" in room_data: PANELS_BY_ROOM[room_name] = dict() @@ -454,8 +543,11 @@ def process_room(room_name, room_data): process_sunwarp(room_name, sunwarp_data) if "progression" in room_data: - for progression_name, progression_doors in room_data["progression"].items(): - process_progression(room_name, progression_name, progression_doors) + for progression_name, pdata in room_data["progression"].items(): + if "doors" in pdata: + process_progressive_door(room_name, progression_name, pdata["doors"]) + if "panel_doors" in pdata: + process_progressive_panel(room_name, progression_name, pdata["panel_doors"]) ALL_ROOMS.append(room_obj) @@ -492,8 +584,10 @@ def process_room(room_name, room_data): "ALL_ROOMS": ALL_ROOMS, "DOORS_BY_ROOM": DOORS_BY_ROOM, "PANELS_BY_ROOM": PANELS_BY_ROOM, + "PANEL_DOORS_BY_ROOM": PANEL_DOORS_BY_ROOM, "PROGRESSIVE_ITEMS": PROGRESSIVE_ITEMS, - "PROGRESSION_BY_ROOM": PROGRESSION_BY_ROOM, + "PROGRESSIVE_DOORS_BY_ROOM": PROGRESSIVE_DOORS_BY_ROOM, + "PROGRESSIVE_PANELS_BY_ROOM": PROGRESSIVE_PANELS_BY_ROOM, "PAINTING_ENTRANCES": PAINTING_ENTRANCES, "PAINTING_EXIT_ROOMS": PAINTING_EXIT_ROOMS, "PAINTING_EXITS": PAINTING_EXITS, @@ -506,6 +600,8 @@ def process_room(room_name, room_data): "DOOR_LOCATION_IDS": DOOR_LOCATION_IDS, "DOOR_ITEM_IDS": DOOR_ITEM_IDS, "DOOR_GROUP_ITEM_IDS": DOOR_GROUP_ITEM_IDS, + "PANEL_DOOR_ITEM_IDS": PANEL_DOOR_ITEM_IDS, + "PANEL_GROUP_ITEM_IDS": PANEL_GROUP_ITEM_IDS, "PROGRESSIVE_ITEM_IDS": PROGRESSIVE_ITEM_IDS, } diff --git a/worlds/lingo/utils/validate_config.rb b/worlds/lingo/utils/validate_config.rb index 498980bb719..70f7fc2cf65 100644 --- a/worlds/lingo/utils/validate_config.rb +++ b/worlds/lingo/utils/validate_config.rb @@ -33,19 +33,23 @@ configured_rooms = Set["Menu"] configured_doors = Set[] configured_panels = Set[] +configured_panel_doors = Set[] mentioned_rooms = Set[] mentioned_doors = Set[] mentioned_panels = Set[] +mentioned_panel_doors = Set[] mentioned_sunwarp_entrances = Set[] mentioned_sunwarp_exits = Set[] mentioned_paintings = Set[] door_groups = {} +panel_groups = {} -directives = Set["entrances", "panels", "doors", "paintings", "sunwarps", "progression"] +directives = Set["entrances", "panels", "doors", "panel_doors", "paintings", "sunwarps", "progression"] panel_directives = Set["id", "required_room", "required_door", "required_panel", "colors", "check", "exclude_reduce", "tag", "link", "subtag", "achievement", "copy_to_sign", "non_counting", "hunt", "location_name"] door_directives = Set["id", "painting_id", "panels", "item_name", "item_group", "location_name", "skip_location", "skip_item", "door_group", "include_reduce", "event", "warp_id"] +panel_door_directives = Set["panels", "item_name", "panel_group"] painting_directives = Set["id", "enter_only", "exit_only", "orientation", "required_door", "required", "required_when_no_doors", "move", "req_blocked", "req_blocked_when_no_doors"] non_counting = 0 @@ -253,6 +257,43 @@ end end + (room["panel_doors"] || {}).each do |panel_door_name, panel_door| + configured_panel_doors.add("#{room_name} - #{panel_door_name}") + + if panel_door.include?("panels") + panel_door["panels"].each do |panel| + if panel.kind_of? Hash then + other_room = panel.include?("room") ? panel["room"] : room_name + mentioned_panels.add("#{other_room} - #{panel["panel"]}") + else + other_room = panel.include?("room") ? panel["room"] : room_name + mentioned_panels.add("#{room_name} - #{panel}") + end + end + else + puts "#{room_name} - #{panel_door_name} :::: Missing panels field" + end + + if panel_door.include?("panel_group") + panel_groups[panel_door["panel_group"]] ||= 0 + panel_groups[panel_door["panel_group"]] += 1 + end + + bad_subdirectives = [] + panel_door.keys.each do |key| + unless panel_door_directives.include?(key) then + bad_subdirectives << key + end + end + unless bad_subdirectives.empty? then + puts "#{room_name} - #{panel_door_name} :::: Panel door has the following invalid subdirectives: #{bad_subdirectives.join(", ")}" + end + + unless ids.include?("panel_doors") and ids["panel_doors"].include?(room_name) and ids["panel_doors"][room_name].include?(panel_door_name) + puts "#{room_name} - #{panel_door_name} :::: Panel door is missing an item ID" + end + end + (room["paintings"] || []).each do |painting| if painting.include?("id") and painting["id"].kind_of? String then unless paintings.include? painting["id"] then @@ -327,12 +368,24 @@ end end - (room["progression"] || {}).each do |progression_name, door_list| - door_list.each do |door| - if door.kind_of? Hash then - mentioned_doors.add("#{door["room"]} - #{door["door"]}") - else - mentioned_doors.add("#{room_name} - #{door}") + (room["progression"] || {}).each do |progression_name, pdata| + if pdata.include? "doors" then + pdata["doors"].each do |door| + if door.kind_of? Hash then + mentioned_doors.add("#{door["room"]} - #{door["door"]}") + else + mentioned_doors.add("#{room_name} - #{door}") + end + end + end + + if pdata.include? "panel_doors" then + pdata["panel_doors"].each do |panel_door| + if panel_door.kind_of? Hash then + mentioned_panel_doors.add("#{panel_door["room"]} - #{panel_door["panel_door"]}") + else + mentioned_panel_doors.add("#{room_name} - #{panel_door}") + end end end @@ -344,17 +397,22 @@ errored_rooms = mentioned_rooms - configured_rooms unless errored_rooms.empty? then - puts "The folloring rooms are mentioned but do not exist: " + errored_rooms.to_s + puts "The following rooms are mentioned but do not exist: " + errored_rooms.to_s end errored_panels = mentioned_panels - configured_panels unless errored_panels.empty? then - puts "The folloring panels are mentioned but do not exist: " + errored_panels.to_s + puts "The following panels are mentioned but do not exist: " + errored_panels.to_s end errored_doors = mentioned_doors - configured_doors unless errored_doors.empty? then - puts "The folloring doors are mentioned but do not exist: " + errored_doors.to_s + puts "The following doors are mentioned but do not exist: " + errored_doors.to_s +end + +errored_panel_doors = mentioned_panel_doors - configured_panel_doors +unless errored_panel_doors.empty? then + puts "The following panel doors are mentioned but do not exist: " + errored_panel_doors.to_s end door_groups.each do |group,num| @@ -367,6 +425,16 @@ end end +panel_groups.each do |group,num| + if num == 1 then + puts "Panel group \"#{group}\" only has one panel in it" + end + + unless ids.include?("panel_groups") and ids["panel_groups"].include?(group) + puts "#{group} :::: Panel group is missing an item ID" + end +end + slashed_rooms = configured_rooms.select do |room| room.include? "/" end diff --git a/worlds/pokemon_emerald/__init__.py b/worlds/pokemon_emerald/__init__.py index aa4f6ccf751..abdee26f572 100644 --- a/worlds/pokemon_emerald/__init__.py +++ b/worlds/pokemon_emerald/__init__.py @@ -52,8 +52,17 @@ class PokemonEmeraldWebWorld(WebWorld): "setup/es", ["nachocua"] ) + + setup_sv = Tutorial( + "Multivärld Installations Guide", + "En guide för att kunna spela Pokémon Emerald med Archipelago.", + "Svenska", + "setup_sv.md", + "setup/sv", + ["Tsukino"] + ) - tutorials = [setup_en, setup_es] + tutorials = [setup_en, setup_es, setup_sv] class PokemonEmeraldSettings(settings.Group): diff --git a/worlds/pokemon_emerald/docs/setup_sv.md b/worlds/pokemon_emerald/docs/setup_sv.md new file mode 100644 index 00000000000..88b1d384096 --- /dev/null +++ b/worlds/pokemon_emerald/docs/setup_sv.md @@ -0,0 +1,78 @@ +# Pokémon Emerald Installationsguide + +## Programvara som behövs + +- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) +- Ett engelskt Pokémon Emerald ROM, Archipelago kan inte hjälpa dig med detta. +- [BizHawk](https://tasvideos.org/BizHawk/ReleaseHistory) 2.7 eller senare + +### Konfigurera BizHawk + +När du har installerat BizHawk, öppna `EmuHawk.exe` och ändra följande inställningar: + +- Om du använder BizHawk 2.7 eller 2.8, gå till `Config > Customize`. På "Advanced Tab", byt Lua core från +`NLua+KopiLua` till `Lua+LuaInterface`, starta om EmuHawk efteråt. (Använder du BizHawk 2.9, kan du skippa detta steg.) +- Gå till `Config > Customize`. Markera "Run in background" inställningen för att förhindra bortkoppling från +klienten om du alt-tabbar bort från EmuHawk. +- Öppna en `.gba` fil i EmuHawk och gå till `Config > Controllers…` för att konfigurera dina inputs. +Om du inte hittar `Controllers…`, starta ett valfritt `.gba` ROM först. +- Överväg att rensa keybinds i `Config > Hotkeys…` som du inte tänkt använda. Välj en keybind och tryck på ESC +för att rensa bort den. + +## Extra programvara + +- [Pokémon Emerald AP Tracker](https://github.com/seto10987/Archipelago-Emerald-AP-Tracker/releases/latest), +används tillsammans med +[PopTracker](https://github.com/black-sliver/PopTracker/releases) + +## Generera och patcha ett spel + +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). +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. +5. Om detta är första gången du patchar, så kommer du behöva välja var ditt ursprungliga ROM är. +6. En patchad `.gba` fil kommer skapas på samma plats som patchfilen. +7. Första gången du öppnar en patch med BizHawk-klienten, kommer du också behöva bekräfta var `EmuHawk.exe` filen är +installerad i din BizHawk-mapp. + +Om du bara tänkt spela själv och du inte bryr dig om automatisk spårning eller ledtrådar, så kan du stanna här, stänga +av klienten, och starta ditt patchade ROM med valfri emulator. Dock, för multvärldsfunktionen eller andra +Archipelago-funktioner, fortsätt nedanför med BizHawk. + +## Anslut till en server + +Om du vanligtsvis öppnar en patchad fil så görs steg 1-5 automatiskt åt dig. Även om det är så, kom ihåg dessa steg +ifall du till exempel behöver stänga ner och starta om något medans du spelar. + +1. Pokemon Emerald använder Archipelagos BizHawk-klient. Om klienten inte startat efter att du patchat ditt spel, +så kan du bara öppna den igen från launchern. +2. Dubbelkolla att EmuHawk faktiskt startat med den patchade ROM-filen. +3. I EmuHawk, gå till `Tools > Lua Console`. Luakonsolen måste vara igång medans du spelar. +4. I Luakonsolen, Tryck på `Script > Open Script…`. +5. Leta reda på din Archipelago-mapp och i den öppna `data/lua/connector_bizhawk_generic.lua`. +6. Emulatorn och klienten kommer så småningom ansluta till varandra. I BizHawk-klienten kommer du kunna see om allt är +anslutet och att Pokemon Emerald är igenkänt. +7. För att ansluta klienten till en server, skriv in din lobbyadress och port i textfältet t.ex. +`archipelago.gg:38281` +längst upp i din klient och tryck sen på "Connect". + +Du borde nu kunna ta emot och skicka föremål. Du behöver göra dom här stegen varje gång du vill ansluta igen. Det är +helt okej att göra saker offline utan att behöva oroa sig; allt kommer att synkronisera när du ansluter till servern +igen. + +## Automatisk Spårning + +Pokémon Emerald har en fullt fungerande spårare med stöd för automatisk spårning. + +1. Ladda ner [Pokémon Emerald AP Tracker](https://github.com/seto10987/Archipelago-Emerald-AP-Tracker/releases/latest) +och +[PopTracker](https://github.com/black-sliver/PopTracker/releases). +2. Placera tracker pack zip-filen i packs/ där du har PopTracker installerat. +3. Öppna PopTracker, och välj Pokemon Emerald. +4. För att automatiskt spåra, tryck på "AP" symbolen längst upp. +5. Skriv in Archipelago-serverns uppgifter (Samma som du använde för att ansluta med klienten), "Slot"-namn samt +lösenord. diff --git a/worlds/pokemon_rb/locations.py b/worlds/pokemon_rb/locations.py index 251beb59cc1..6aee25df263 100644 --- a/worlds/pokemon_rb/locations.py +++ b/worlds/pokemon_rb/locations.py @@ -427,7 +427,7 @@ def __init__(self, flag): LocationData("Seafoam Islands B3F", "Hidden Item Rock", "Max Elixir", rom_addresses['Hidden_Item_Seafoam_Islands_B3F'], Hidden(50), inclusion=hidden_items), LocationData("Vermilion City", "Hidden Item In Water Near Fan Club", "Max Ether", rom_addresses['Hidden_Item_Vermilion_City'], Hidden(51), inclusion=hidden_items), LocationData("Cerulean City-Badge House Backyard", "Hidden Item Gym Badge Guy's Backyard", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_City'], Hidden(52), inclusion=hidden_items), - LocationData("Route 4-E", "Hidden Item Plateau East Of Mt Moon", "Great Ball", rom_addresses['Hidden_Item_Route_4'], Hidden(53), inclusion=hidden_items), + LocationData("Route 4-C", "Hidden Item Plateau East Of Mt Moon", "Great Ball", rom_addresses['Hidden_Item_Route_4'], Hidden(53), inclusion=hidden_items), LocationData("Oak's Lab", "Oak's Parcel Reward", "Pokedex", rom_addresses["Event_Pokedex"], EventFlag(0x38)), diff --git a/worlds/sc2/requirements.txt b/worlds/sc2/requirements.txt index 9b84863c459..5bc808b639d 100644 --- a/worlds/sc2/requirements.txt +++ b/worlds/sc2/requirements.txt @@ -1,2 +1 @@ nest-asyncio >= 1.5.5 -six >= 1.16.0 \ No newline at end of file diff --git a/worlds/shorthike/Locations.py b/worlds/shorthike/Locations.py index 319ad8f20e1..657035a0301 100644 --- a/worlds/shorthike/Locations.py +++ b/worlds/shorthike/Locations.py @@ -328,7 +328,7 @@ class LocationInfo(TypedDict): {"name": "Boat Rental", "id": base_id + 55, "inGameId": "DadDeer[0]", - "needsShovel": False, "purchase": True, + "needsShovel": False, "purchase": 100, "minGoldenFeathers": 0, "minGoldenFeathersEasy": 0, "minGoldenFeathersBucket": 0}, {"name": "Boat Challenge Reward", "id": base_id + 56, diff --git a/worlds/smz3/__init__.py b/worlds/smz3/__init__.py index 6056a171d37..d78c9f7d822 100644 --- a/worlds/smz3/__init__.py +++ b/worlds/smz3/__init__.py @@ -215,7 +215,6 @@ def create_items(self): niceItems = TotalSMZ3Item.Item.CreateNicePool(self.smz3World) junkItems = TotalSMZ3Item.Item.CreateJunkPool(self.smz3World) - allJunkItems = niceItems + junkItems self.junkItemsNames = [item.Type.name for item in junkItems] if (self.smz3World.Config.Keysanity): @@ -228,7 +227,8 @@ def create_items(self): self.multiworld.push_precollected(SMZ3Item(item.Type.name, ItemClassification.filler, item.Type, self.item_name_to_id[item.Type.name], self.player, item)) itemPool = [SMZ3Item(item.Type.name, ItemClassification.progression, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in progressionItems] + \ - [SMZ3Item(item.Type.name, ItemClassification.filler, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in allJunkItems] + [SMZ3Item(item.Type.name, ItemClassification.useful, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in niceItems] + \ + [SMZ3Item(item.Type.name, ItemClassification.filler, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in junkItems] self.smz3DungeonItems = [SMZ3Item(item.Type.name, ItemClassification.progression, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in self.dungeon] self.multiworld.itempool += itemPool diff --git a/worlds/stardew_valley/__init__.py b/worlds/stardew_valley/__init__.py index 07235ad2983..f9df8c292e3 100644 --- a/worlds/stardew_valley/__init__.py +++ b/worlds/stardew_valley/__init__.py @@ -1,4 +1,5 @@ import logging +from random import Random from typing import Dict, Any, Iterable, Optional, Union, List, TextIO from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState @@ -27,15 +28,20 @@ from .strings.metal_names import Ore from .strings.region_names import Region as RegionName, LogicRegion +logger = logging.getLogger(__name__) + +STARDEW_VALLEY = "Stardew Valley" +UNIVERSAL_TRACKER_SEED_PROPERTY = "ut_seed" + client_version = 0 class StardewLocation(Location): - game: str = "Stardew Valley" + game: str = STARDEW_VALLEY class StardewItem(Item): - game: str = "Stardew Valley" + game: str = STARDEW_VALLEY class StardewWebWorld(WebWorld): @@ -60,7 +66,7 @@ class StardewValleyWorld(World): Stardew Valley is an open-ended country-life RPG. You can farm, fish, mine, fight, complete quests, befriend villagers, and uncover dark secrets. """ - game = "Stardew Valley" + game = STARDEW_VALLEY topology_present = False item_name_to_id = {name: data.code for name, data in item_table.items()} @@ -95,6 +101,17 @@ def __init__(self, multiworld: MultiWorld, player: int): self.total_progression_items = 0 # self.all_progression_items = dict() + # Taking the seed specified in slot data for UT, otherwise just generating the seed. + self.seed = getattr(multiworld, "re_gen_passthrough", {}).get(STARDEW_VALLEY, self.random.getrandbits(64)) + self.random = Random(self.seed) + + def interpret_slot_data(self, slot_data: Dict[str, Any]) -> Optional[int]: + # If the seed is not specified in the slot data, this mean the world was generated before Universal Tracker support. + seed = slot_data.get(UNIVERSAL_TRACKER_SEED_PROPERTY) + if seed is None: + logger.warning(f"World was generated before Universal Tracker support. Tracker might not be accurate.") + return seed + def generate_early(self): self.force_change_options_if_incompatible() self.content = create_content(self.options) @@ -108,12 +125,12 @@ def force_change_options_if_incompatible(self): self.options.exclude_ginger_island.value = ExcludeGingerIsland.option_false goal_name = self.options.goal.current_key player_name = self.multiworld.player_name[self.player] - logging.warning( + logger.warning( f"Goal '{goal_name}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})") if exclude_ginger_island and self.options.walnutsanity != Walnutsanity.preset_none: self.options.walnutsanity.value = Walnutsanity.preset_none player_name = self.multiworld.player_name[self.player] - logging.warning( + logger.warning( f"Walnutsanity requires Ginger Island. Ginger Island was excluded from {self.player} ({player_name})'s world, so walnutsanity was force disabled") def create_regions(self): @@ -255,7 +272,7 @@ def setup_victory(self): Event.victory) elif self.options.goal == Goal.option_greatest_walnut_hunter: self.create_event_location(location_table[GoalName.greatest_walnut_hunter], - self.logic.has_walnut(130), + self.logic.walnut.has_walnut(130), Event.victory) elif self.options.goal == Goal.option_protector_of_the_valley: self.create_event_location(location_table[GoalName.protector_of_the_valley], @@ -413,6 +430,7 @@ def fill_slot_data(self) -> Dict[str, Any]: included_option_names: List[str] = [option_name for option_name in self.options_dataclass.type_hints if option_name not in excluded_option_names] slot_data = self.options.as_dict(*included_option_names) slot_data.update({ + UNIVERSAL_TRACKER_SEED_PROPERTY: self.seed, "seed": self.random.randrange(1000000000), # Seed should be max 9 digits "randomized_entrances": self.randomized_entrances, "modified_bundles": bundles, diff --git a/worlds/stardew_valley/content/mods/alecto.py b/worlds/stardew_valley/content/mods/alecto.py new file mode 100644 index 00000000000..c05c936de3c --- /dev/null +++ b/worlds/stardew_valley/content/mods/alecto.py @@ -0,0 +1,33 @@ +from ..game_content import ContentPack, StardewContent +from ..mod_registry import register_mod_content_pack +from ...data import villagers_data +from ...data.harvest import ForagingSource +from ...data.requirement import QuestRequirement +from ...mods.mod_data import ModNames +from ...strings.quest_names import ModQuest +from ...strings.region_names import Region +from ...strings.seed_names import DistantLandsSeed + + +class AlectoContentPack(ContentPack): + + def harvest_source_hook(self, content: StardewContent): + if ModNames.distant_lands in content.registered_packs: + content.game_items.pop(DistantLandsSeed.void_mint) + content.game_items.pop(DistantLandsSeed.vile_ancient_fruit) + content.source_item(DistantLandsSeed.void_mint, + ForagingSource(regions=(Region.witch_swamp,), other_requirements=(QuestRequirement(ModQuest.WitchOrder),)),), + content.source_item(DistantLandsSeed.vile_ancient_fruit, + ForagingSource(regions=(Region.witch_swamp,), other_requirements=(QuestRequirement(ModQuest.WitchOrder),)), ), + + +register_mod_content_pack(ContentPack( + ModNames.alecto, + weak_dependencies=( + ModNames.distant_lands, # For Witch's order + ), + villagers=( + villagers_data.alecto, + ) + +)) diff --git a/worlds/stardew_valley/content/mods/archeology.py b/worlds/stardew_valley/content/mods/archeology.py index 97d38085d3b..5eb8af4cfc3 100644 --- a/worlds/stardew_valley/content/mods/archeology.py +++ b/worlds/stardew_valley/content/mods/archeology.py @@ -1,20 +1,34 @@ -from ..game_content import ContentPack +from ..game_content import ContentPack, StardewContent from ..mod_registry import register_mod_content_pack -from ...data.game_item import ItemTag, Tag -from ...data.shop import ShopSource +from ...data.artisan import MachineSource from ...data.skill import Skill from ...mods.mod_data import ModNames -from ...strings.book_names import ModBook -from ...strings.region_names import LogicRegion +from ...strings.craftable_names import ModMachine +from ...strings.fish_names import ModTrash +from ...strings.metal_names import all_artifacts, all_fossils from ...strings.skill_names import ModSkill -register_mod_content_pack(ContentPack( + +class ArchaeologyContentPack(ContentPack): + def artisan_good_hook(self, content: StardewContent): + # Done as honestly there are too many display items to put into the initial registration traditionally. + display_items = all_artifacts + all_fossils + for item in display_items: + self.source_display_items(item, content) + content.source_item(ModTrash.rusty_scrap, *(MachineSource(item=artifact, machine=ModMachine.grinder) for artifact in all_artifacts)) + + def source_display_items(self, item: str, content: StardewContent): + wood_display = f"Wooden Display: {item}" + hardwood_display = f"Hardwood Display: {item}" + if item == "Trilobite": + wood_display = f"Wooden Display: Trilobite Fossil" + hardwood_display = f"Hardwood Display: Trilobite Fossil" + content.source_item(wood_display, MachineSource(item=str(item), machine=ModMachine.preservation_chamber)) + content.source_item(hardwood_display, MachineSource(item=str(item), machine=ModMachine.hardwood_preservation_chamber)) + + +register_mod_content_pack(ArchaeologyContentPack( ModNames.archaeology, - shop_sources={ - ModBook.digging_like_worms: ( - Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL), - ShopSource(money_price=500, shop_region=LogicRegion.bookseller_1),), - }, skills=(Skill(name=ModSkill.archaeology, has_mastery=False),), )) diff --git a/worlds/stardew_valley/content/mods/distant_lands.py b/worlds/stardew_valley/content/mods/distant_lands.py index 19380d4ff56..c5614d13025 100644 --- a/worlds/stardew_valley/content/mods/distant_lands.py +++ b/worlds/stardew_valley/content/mods/distant_lands.py @@ -1,9 +1,26 @@ -from ..game_content import ContentPack +from ..game_content import ContentPack, StardewContent from ..mod_registry import register_mod_content_pack from ...data import villagers_data, fish_data +from ...data.game_item import ItemTag, Tag +from ...data.harvest import ForagingSource, HarvestCropSource +from ...data.requirement import QuestRequirement from ...mods.mod_data import ModNames +from ...strings.crop_names import DistantLandsCrop +from ...strings.forageable_names import DistantLandsForageable +from ...strings.quest_names import ModQuest +from ...strings.region_names import Region +from ...strings.season_names import Season +from ...strings.seed_names import DistantLandsSeed -register_mod_content_pack(ContentPack( + +class DistantLandsContentPack(ContentPack): + + def harvest_source_hook(self, content: StardewContent): + content.untag_item(DistantLandsSeed.void_mint, tag=ItemTag.CROPSANITY_SEED) + content.untag_item(DistantLandsSeed.vile_ancient_fruit, tag=ItemTag.CROPSANITY_SEED) + + +register_mod_content_pack(DistantLandsContentPack( ModNames.distant_lands, fishes=( fish_data.void_minnow, @@ -13,5 +30,13 @@ ), villagers=( villagers_data.zic, - ) + ), + harvest_sources={ + DistantLandsForageable.swamp_herb: (ForagingSource(regions=(Region.witch_swamp,)),), + DistantLandsForageable.brown_amanita: (ForagingSource(regions=(Region.witch_swamp,)),), + DistantLandsSeed.void_mint: (ForagingSource(regions=(Region.witch_swamp,), other_requirements=(QuestRequirement(ModQuest.CorruptedCropsTask),)),), + DistantLandsCrop.void_mint: (Tag(ItemTag.VEGETABLE), HarvestCropSource(seed=DistantLandsSeed.void_mint, seasons=(Season.spring, Season.summer, Season.fall)),), + DistantLandsSeed.vile_ancient_fruit: (ForagingSource(regions=(Region.witch_swamp,), other_requirements=(QuestRequirement(ModQuest.CorruptedCropsTask),)),), + DistantLandsCrop.vile_ancient_fruit: (Tag(ItemTag.FRUIT), HarvestCropSource(seed=DistantLandsSeed.vile_ancient_fruit, seasons=(Season.spring, Season.summer, Season.fall)),) + } )) diff --git a/worlds/stardew_valley/content/mods/npc_mods.py b/worlds/stardew_valley/content/mods/npc_mods.py index 3172a55dbf3..52d97d5c52b 100644 --- a/worlds/stardew_valley/content/mods/npc_mods.py +++ b/worlds/stardew_valley/content/mods/npc_mods.py @@ -73,13 +73,6 @@ ) )) -register_mod_content_pack(ContentPack( - ModNames.alecto, - villagers=( - villagers_data.alecto, - ) -)) - register_mod_content_pack(ContentPack( ModNames.lacey, villagers=( diff --git a/worlds/stardew_valley/content/mods/sve.py b/worlds/stardew_valley/content/mods/sve.py index f74b80948c9..a68d4ae9c09 100644 --- a/worlds/stardew_valley/content/mods/sve.py +++ b/worlds/stardew_valley/content/mods/sve.py @@ -3,15 +3,27 @@ from ..override import override from ..vanilla.ginger_island import ginger_island_content_pack as ginger_island_content_pack from ...data import villagers_data, fish_data -from ...data.harvest import ForagingSource -from ...data.requirement import YearRequirement +from ...data.game_item import ItemTag, Tag +from ...data.harvest import ForagingSource, HarvestCropSource +from ...data.requirement import YearRequirement, CombatRequirement, RelationshipRequirement, ToolRequirement, SkillRequirement, FishingRequirement +from ...data.shop import ShopSource from ...mods.mod_data import ModNames -from ...strings.crop_names import Fruit -from ...strings.fish_names import WaterItem +from ...strings.craftable_names import ModEdible +from ...strings.crop_names import Fruit, SVEVegetable, SVEFruit +from ...strings.fish_names import WaterItem, SVEFish, SVEWaterItem from ...strings.flower_names import Flower -from ...strings.forageable_names import Mushroom, Forageable -from ...strings.region_names import Region, SVERegion +from ...strings.food_names import SVEMeal, SVEBeverage +from ...strings.forageable_names import Mushroom, Forageable, SVEForage +from ...strings.gift_names import SVEGift +from ...strings.metal_names import Ore +from ...strings.monster_drop_names import ModLoot, Loot +from ...strings.performance_names import Performance +from ...strings.region_names import Region, SVERegion, LogicRegion from ...strings.season_names import Season +from ...strings.seed_names import SVESeed +from ...strings.skill_names import Skill +from ...strings.tool_names import Tool, ToolMaterial +from ...strings.villager_names import ModNPC class SVEContentPack(ContentPack): @@ -38,6 +50,24 @@ def villager_hook(self, content: StardewContent): # Remove Lance if Ginger Island is not in content since he is first encountered in Volcano Forge content.villagers.pop(villagers_data.lance.name) + def harvest_source_hook(self, content: StardewContent): + content.untag_item(SVESeed.shrub, tag=ItemTag.CROPSANITY_SEED) + content.untag_item(SVESeed.fungus, tag=ItemTag.CROPSANITY_SEED) + content.untag_item(SVESeed.slime, tag=ItemTag.CROPSANITY_SEED) + content.untag_item(SVESeed.stalk, tag=ItemTag.CROPSANITY_SEED) + content.untag_item(SVESeed.void, tag=ItemTag.CROPSANITY_SEED) + content.untag_item(SVESeed.ancient_fern, tag=ItemTag.CROPSANITY_SEED) + if ginger_island_content_pack.name not in content.registered_packs: + # Remove Highlands seeds as these are behind Lance existing. + content.game_items.pop(SVESeed.void) + content.game_items.pop(SVEVegetable.void_root) + content.game_items.pop(SVESeed.stalk) + content.game_items.pop(SVEFruit.monster_fruit) + content.game_items.pop(SVESeed.fungus) + content.game_items.pop(SVEVegetable.monster_mushroom) + content.game_items.pop(SVESeed.slime) + content.game_items.pop(SVEFruit.slime_berry) + register_mod_content_pack(SVEContentPack( ModNames.sve, @@ -45,12 +75,24 @@ def villager_hook(self, content: StardewContent): ginger_island_content_pack.name, ModNames.jasper, # To override Marlon and Gunther ), + shop_sources={ + SVEGift.aged_blue_moon_wine: (ShopSource(money_price=28000, shop_region=SVERegion.blue_moon_vineyard),), + SVEGift.blue_moon_wine: (ShopSource(money_price=3000, shop_region=SVERegion.blue_moon_vineyard),), + ModEdible.lightning_elixir: (ShopSource(money_price=12000, shop_region=SVERegion.galmoran_outpost),), + ModEdible.barbarian_elixir: (ShopSource(money_price=22000, shop_region=SVERegion.galmoran_outpost),), + ModEdible.gravity_elixir: (ShopSource(money_price=4000, shop_region=SVERegion.galmoran_outpost),), + SVEMeal.grampleton_orange_chicken: (ShopSource(money_price=650, shop_region=Region.saloon, other_requirements=(RelationshipRequirement(ModNPC.sophia, 6),)),), + ModEdible.hero_elixir: (ShopSource(money_price=8000, shop_region=SVERegion.isaac_shop),), + ModEdible.aegis_elixir: (ShopSource(money_price=28000, shop_region=SVERegion.galmoran_outpost),), + SVEBeverage.sports_drink: (ShopSource(money_price=750, shop_region=Region.hospital),), + SVEMeal.stamina_capsule: (ShopSource(money_price=4000, shop_region=Region.hospital),), + }, harvest_sources={ Mushroom.red: ( ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.summer, Season.fall)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), ) ), Mushroom.purple: ( - ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.fall,)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), ) + ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.fall,)), ForagingSource(regions=(SVERegion.sprite_spring_cave, SVERegion.junimo_woods), ) ), Mushroom.morel: ( ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.fall,)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), ) @@ -64,17 +106,59 @@ def villager_hook(self, content: StardewContent): Flower.sunflower: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.summer,)),), Flower.fairy_rose: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.fall,)),), Fruit.ancient_fruit: ( - ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring, Season.summer, Season.fall), other_requirements=(YearRequirement(3),)), + ForagingSource(regions=(SVERegion.sprite_spring,), seasons=Season.not_winter, other_requirements=(YearRequirement(3),)), ForagingSource(regions=(SVERegion.sprite_spring_cave,)), ), Fruit.sweet_gem_berry: ( - ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring, Season.summer, Season.fall), other_requirements=(YearRequirement(3),)), + ForagingSource(regions=(SVERegion.sprite_spring,), seasons=Season.not_winter, other_requirements=(YearRequirement(3),)), ), + # New items + + ModLoot.green_mushroom: (ForagingSource(regions=(SVERegion.highlands_pond,), seasons=Season.not_winter),), + ModLoot.ornate_treasure_chest: (ForagingSource(regions=(SVERegion.highlands_outside,), + other_requirements=(CombatRequirement(Performance.galaxy), ToolRequirement(Tool.axe, ToolMaterial.iron))),), + ModLoot.swirl_stone: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.galaxy),)),), + ModLoot.void_soul: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.good),)),), + SVEForage.winter_star_rose: (ForagingSource(regions=(SVERegion.summit,), seasons=(Season.winter,)),), + SVEForage.bearberry: (ForagingSource(regions=(Region.secret_woods,), seasons=(Season.winter,)),), + SVEForage.poison_mushroom: (ForagingSource(regions=(Region.secret_woods,), seasons=(Season.summer, Season.fall)),), + SVEForage.red_baneberry: (ForagingSource(regions=(Region.secret_woods,), seasons=(Season.summer, Season.summer)),), + SVEForage.ferngill_primrose: (ForagingSource(regions=(SVERegion.summit,), seasons=(Season.spring,)),), + SVEForage.goldenrod: (ForagingSource(regions=(SVERegion.summit,), seasons=(Season.summer, Season.fall)),), + SVEForage.conch: (ForagingSource(regions=(Region.beach, SVERegion.fable_reef,)),), + SVEForage.dewdrop_berry: (ForagingSource(regions=(SVERegion.enchanted_grove,)),), + SVEForage.sand_dollar: (ForagingSource(regions=(Region.beach, SVERegion.fable_reef,), seasons=(Season.spring, Season.summer)),), + SVEForage.golden_ocean_flower: (ForagingSource(regions=(SVERegion.fable_reef,)),), + SVEForage.four_leaf_clover: (ForagingSource(regions=(Region.secret_woods, SVERegion.forest_west,), seasons=(Season.summer, Season.fall)),), + SVEForage.mushroom_colony: (ForagingSource(regions=(Region.secret_woods, SVERegion.junimo_woods, SVERegion.forest_west,), seasons=(Season.fall,)),), + SVEForage.rusty_blade: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.great),)),), + SVEForage.rafflesia: (ForagingSource(regions=(Region.secret_woods,), seasons=Season.not_winter),), + SVEForage.thistle: (ForagingSource(regions=(SVERegion.summit,)),), + ModLoot.void_pebble: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.great),)),), + ModLoot.void_shard: (ForagingSource(regions=(SVERegion.crimson_badlands,), + other_requirements=(CombatRequirement(Performance.galaxy), SkillRequirement(Skill.combat, 10), YearRequirement(3),)),), + SVEWaterItem.dulse_seaweed: (ForagingSource(regions=(Region.beach,), other_requirements=(FishingRequirement(Region.beach),)),), + # Fable Reef WaterItem.coral: (ForagingSource(regions=(SVERegion.fable_reef,)),), Forageable.rainbow_shell: (ForagingSource(regions=(SVERegion.fable_reef,)),), WaterItem.sea_urchin: (ForagingSource(regions=(SVERegion.fable_reef,)),), + + # Crops + SVESeed.shrub: (ForagingSource(regions=(Region.secret_woods,), other_requirements=(CombatRequirement(Performance.good),)),), + SVEFruit.salal_berry: (Tag(ItemTag.FRUIT), HarvestCropSource(seed=SVESeed.shrub, seasons=(Season.spring,)),), + SVESeed.slime: (ForagingSource(regions=(SVERegion.highlands_outside,), other_requirements=(CombatRequirement(Performance.good),)),), + SVEFruit.slime_berry: (Tag(ItemTag.FRUIT), HarvestCropSource(seed=SVESeed.slime, seasons=(Season.spring,)),), + SVESeed.ancient_fern: (ForagingSource(regions=(Region.secret_woods,)),), + SVEVegetable.ancient_fiber: (Tag(ItemTag.VEGETABLE), HarvestCropSource(seed=SVESeed.ancient_fern, seasons=(Season.summer,)),), + SVESeed.stalk: (ForagingSource(regions=(SVERegion.highlands_outside,), other_requirements=(CombatRequirement(Performance.good),)),), + SVEFruit.monster_fruit: (Tag(ItemTag.FRUIT), HarvestCropSource(seed=SVESeed.stalk, seasons=(Season.summer,)),), + SVESeed.fungus: (ForagingSource(regions=(SVERegion.highlands_pond,), other_requirements=(CombatRequirement(Performance.good),)),), + SVEVegetable.monster_mushroom: (Tag(ItemTag.VEGETABLE), HarvestCropSource(seed=SVESeed.fungus, seasons=(Season.fall,)),), + SVESeed.void: (ForagingSource(regions=(SVERegion.highlands_cavern,), other_requirements=(CombatRequirement(Performance.good),)),), + SVEVegetable.void_root: (Tag(ItemTag.VEGETABLE), HarvestCropSource(seed=SVESeed.void, seasons=(Season.winter,)),), + }, fishes=( fish_data.baby_lunaloo, # Removed when no ginger island diff --git a/worlds/stardew_valley/content/vanilla/ginger_island.py b/worlds/stardew_valley/content/vanilla/ginger_island.py index d824deff390..2fbcb032799 100644 --- a/worlds/stardew_valley/content/vanilla/ginger_island.py +++ b/worlds/stardew_valley/content/vanilla/ginger_island.py @@ -3,6 +3,7 @@ from ...data import villagers_data, fish_data from ...data.game_item import ItemTag, Tag from ...data.harvest import ForagingSource, HarvestFruitTreeSource, HarvestCropSource +from ...data.requirement import WalnutRequirement from ...data.shop import ShopSource from ...strings.book_names import Book from ...strings.crop_names import Fruit, Vegetable @@ -10,7 +11,7 @@ from ...strings.forageable_names import Forageable, Mushroom from ...strings.fruit_tree_names import Sapling from ...strings.metal_names import Fossil, Mineral -from ...strings.region_names import Region +from ...strings.region_names import Region, LogicRegion from ...strings.season_names import Season from ...strings.seed_names import Seed @@ -62,6 +63,9 @@ def harvest_source_hook(self, content: StardewContent): Tag(ItemTag.BOOK, ItemTag.BOOK_POWER), ShopSource(items_price=((10, Mineral.diamond),), shop_region=Region.volcano_dwarf_shop), ), + Book.queen_of_sauce_cookbook: ( + Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL), + ShopSource(money_price=50000, shop_region=LogicRegion.bookseller_2, other_requirements=(WalnutRequirement(100),)),), # Worst book ever }, fishes=( diff --git a/worlds/stardew_valley/content/vanilla/pelican_town.py b/worlds/stardew_valley/content/vanilla/pelican_town.py index 2c687eacbdd..220b46eae2a 100644 --- a/worlds/stardew_valley/content/vanilla/pelican_town.py +++ b/worlds/stardew_valley/content/vanilla/pelican_town.py @@ -229,7 +229,7 @@ ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),), Book.mapping_cave_systems: ( Tag(ItemTag.BOOK, ItemTag.BOOK_POWER), - GenericSource(regions=Region.adventurer_guild_bedroom), + GenericSource(regions=(Region.adventurer_guild_bedroom,)), ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),), Book.monster_compendium: ( Tag(ItemTag.BOOK, ItemTag.BOOK_POWER), @@ -243,12 +243,12 @@ ShopSource(money_price=3000, shop_region=LogicRegion.bookseller_2),), Book.the_alleyway_buffet: ( Tag(ItemTag.BOOK, ItemTag.BOOK_POWER), - GenericSource(regions=Region.town, + GenericSource(regions=(Region.town,), other_requirements=(ToolRequirement(Tool.axe, ToolMaterial.iron), ToolRequirement(Tool.pickaxe, ToolMaterial.iron))), ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),), Book.the_art_o_crabbing: ( Tag(ItemTag.BOOK, ItemTag.BOOK_POWER), - GenericSource(regions=Region.beach, + GenericSource(regions=(Region.beach,), other_requirements=(ToolRequirement(Tool.fishing_rod, ToolMaterial.iridium), SkillRequirement(Skill.fishing, 6), SeasonRequirement(Season.winter))), @@ -290,9 +290,6 @@ Book.woodcutters_weekly: ( Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL), ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),), - Book.queen_of_sauce_cookbook: ( - Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL), - ShopSource(money_price=50000, shop_region=LogicRegion.bookseller_2),), # Worst book ever }, fishes=( fish_data.albacore, diff --git a/worlds/stardew_valley/data/fish_data.py b/worlds/stardew_valley/data/fish_data.py index c6f0c30d41f..26b1a0d58a8 100644 --- a/worlds/stardew_valley/data/fish_data.py +++ b/worlds/stardew_valley/data/fish_data.py @@ -46,7 +46,8 @@ def __repr__(self): crimson_badlands = (SVERegion.crimson_badlands,) shearwater = (SVERegion.shearwater,) -highlands = (SVERegion.highlands_outside,) +highlands_pond = (SVERegion.highlands_pond,) +highlands_cave = (SVERegion.highlands_cavern,) sprite_spring = (SVERegion.sprite_spring,) fable_reef = (SVERegion.fable_reef,) vineyard = (SVERegion.blue_moon_vineyard,) @@ -133,9 +134,9 @@ def create_fish(name: str, locations: Tuple[str, ...], seasons: Union[str, Tuple bull_trout = create_fish(SVEFish.bull_trout, forest_river, season.not_spring, 45, mod_name=ModNames.sve) butterfish = create_fish(SVEFish.butterfish, shearwater, season.not_winter, 75, mod_name=ModNames.sve) clownfish = create_fish(SVEFish.clownfish, ginger_island_ocean, season.all_seasons, 45, mod_name=ModNames.sve) -daggerfish = create_fish(SVEFish.daggerfish, highlands, season.all_seasons, 50, mod_name=ModNames.sve) +daggerfish = create_fish(SVEFish.daggerfish, highlands_pond, season.all_seasons, 50, mod_name=ModNames.sve) frog = create_fish(SVEFish.frog, mountain_lake, (season.spring, season.summer), 70, mod_name=ModNames.sve) -gemfish = create_fish(SVEFish.gemfish, highlands, season.all_seasons, 100, mod_name=ModNames.sve) +gemfish = create_fish(SVEFish.gemfish, highlands_cave, season.all_seasons, 100, mod_name=ModNames.sve) goldenfish = create_fish(SVEFish.goldenfish, sprite_spring, season.all_seasons, 60, mod_name=ModNames.sve) grass_carp = create_fish(SVEFish.grass_carp, secret_woods, (season.spring, season.summer), 85, mod_name=ModNames.sve) king_salmon = create_fish(SVEFish.king_salmon, forest_river, (season.spring, season.summer), 80, mod_name=ModNames.sve) diff --git a/worlds/stardew_valley/data/items.csv b/worlds/stardew_valley/data/items.csv index 2604ad2c46b..e026090f865 100644 --- a/worlds/stardew_valley/data/items.csv +++ b/worlds/stardew_valley/data/items.csv @@ -307,7 +307,7 @@ id,name,classification,groups,mod_name 322,Phoenix Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE", 323,Immunity Band,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE", 324,Glowstone Ring,useful,"RING,RESOURCE_PACK,MAXIMUM_ONE", -325,Fairy Dust Recipe,progression,, +325,Fairy Dust Recipe,progression,"GINGER_ISLAND", 326,Heavy Tapper Recipe,progression,"QI_CRAFTING_RECIPE,GINGER_ISLAND", 327,Hyper Speed-Gro Recipe,progression,"QI_CRAFTING_RECIPE,GINGER_ISLAND", 328,Deluxe Fertilizer Recipe,progression,QI_CRAFTING_RECIPE, diff --git a/worlds/stardew_valley/data/locations.csv b/worlds/stardew_valley/data/locations.csv index bb2ed2e2ce1..608b6a5f576 100644 --- a/worlds/stardew_valley/data/locations.csv +++ b/worlds/stardew_valley/data/locations.csv @@ -2088,7 +2088,7 @@ id,region,name,tags,mod_name 3472,Farm,Craft Life Elixir,CRAFTSANITY, 3473,Farm,Craft Oil of Garlic,CRAFTSANITY, 3474,Farm,Craft Monster Musk,CRAFTSANITY, -3475,Farm,Craft Fairy Dust,CRAFTSANITY, +3475,Farm,Craft Fairy Dust,"CRAFTSANITY,GINGER_ISLAND", 3476,Farm,Craft Warp Totem: Beach,CRAFTSANITY, 3477,Farm,Craft Warp Totem: Mountains,CRAFTSANITY, 3478,Farm,Craft Warp Totem: Farm,CRAFTSANITY, @@ -2212,7 +2212,7 @@ id,region,name,tags,mod_name 3808,Shipping,Shipsanity: Mystery Box,"SHIPSANITY", 3809,Shipping,Shipsanity: Golden Tag,"SHIPSANITY", 3810,Shipping,Shipsanity: Deluxe Bait,"SHIPSANITY", -3811,Shipping,Shipsanity: Moss,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT", +3811,Shipping,Shipsanity: Moss,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT", 3812,Shipping,Shipsanity: Mossy Seed,"SHIPSANITY", 3813,Shipping,Shipsanity: Sonar Bobber,"SHIPSANITY", 3814,Shipping,Shipsanity: Tent Kit,"SHIPSANITY", @@ -2221,7 +2221,7 @@ id,region,name,tags,mod_name 3817,Shipping,Shipsanity: Raisins,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT", 3818,Shipping,Shipsanity: Dried Fruit,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT", 3819,Shipping,Shipsanity: Dried Mushrooms,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT", -3820,Shipping,Shipsanity: Stardrop Tea,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT", +3820,Shipping,Shipsanity: Stardrop Tea,"SHIPSANITY", 3821,Shipping,Shipsanity: Prize Ticket,"SHIPSANITY", 3822,Shipping,Shipsanity: Treasure Totem,"SHIPSANITY,REQUIRES_MASTERIES", 3823,Shipping,Shipsanity: Challenge Bait,"SHIPSANITY,REQUIRES_MASTERIES", @@ -2252,7 +2252,7 @@ id,region,name,tags,mod_name 3848,Shipping,Shipsanity: Way Of The Wind pt. 1,"SHIPSANITY", 3849,Shipping,Shipsanity: Mapping Cave Systems,"SHIPSANITY", 3850,Shipping,Shipsanity: Price Catalogue,"SHIPSANITY", -3851,Shipping,Shipsanity: Queen Of Sauce Cookbook,"SHIPSANITY", +3851,Shipping,Shipsanity: Queen Of Sauce Cookbook,"SHIPSANITY,GINGER_ISLAND", 3852,Shipping,Shipsanity: The Diamond Hunter,"SHIPSANITY,GINGER_ISLAND", 3853,Shipping,Shipsanity: Book of Mysteries,"SHIPSANITY", 3854,Shipping,Shipsanity: Animal Catalogue,"SHIPSANITY", @@ -2292,7 +2292,7 @@ id,region,name,tags,mod_name 4032,Farm,Read Book Of Stars,"BOOKSANITY,BOOKSANITY_SKILL", 4033,Farm,Read Combat Quarterly,"BOOKSANITY,BOOKSANITY_SKILL", 4034,Farm,Read Mining Monthly,"BOOKSANITY,BOOKSANITY_SKILL", -4035,Farm,Read Queen Of Sauce Cookbook,"BOOKSANITY,BOOKSANITY_SKILL", +4035,Farm,Read Queen Of Sauce Cookbook,"BOOKSANITY,BOOKSANITY_SKILL,GINGER_ISLAND", 4036,Farm,Read Stardew Valley Almanac,"BOOKSANITY,BOOKSANITY_SKILL", 4037,Farm,Read Woodcutter's Weekly,"BOOKSANITY,BOOKSANITY_SKILL", 4051,Museum,Read Tips on Farming,"BOOKSANITY,BOOKSANITY_LOST", @@ -2900,7 +2900,6 @@ id,region,name,tags,mod_name 7055,Abandoned Mines - 3,Abandoned Treasure - Floor 3,MANDATORY,Boarding House and Bus Stop Extension 7056,Abandoned Mines - 4,Abandoned Treasure - Floor 4,MANDATORY,Boarding House and Bus Stop Extension 7057,Abandoned Mines - 5,Abandoned Treasure - Floor 5,MANDATORY,Boarding House and Bus Stop Extension -7351,Farm,Read Digging Like Worms,"BOOKSANITY,BOOKSANITY_SKILL",Archaeology 7401,Farm,Cook Magic Elixir,COOKSANITY,Magic 7402,Farm,Craft Travel Core,CRAFTSANITY,Magic 7403,Farm,Craft Haste Elixir,CRAFTSANITY,Stardew Valley Expanded @@ -3280,10 +3279,10 @@ id,region,name,tags,mod_name 8237,Shipping,Shipsanity: Pterodactyl R Wing Bone,SHIPSANITY,Boarding House and Bus Stop Extension 8238,Shipping,Shipsanity: Scrap Rust,SHIPSANITY,Archaeology 8239,Shipping,Shipsanity: Rusty Path,SHIPSANITY,Archaeology -8240,Shipping,Shipsanity: Digging Like Worms,SHIPSANITY,Archaeology 8241,Shipping,Shipsanity: Digger's Delight,SHIPSANITY,Archaeology 8242,Shipping,Shipsanity: Rocky Root Coffee,SHIPSANITY,Archaeology 8243,Shipping,Shipsanity: Ancient Jello,SHIPSANITY,Archaeology 8244,Shipping,Shipsanity: Bone Fence,SHIPSANITY,Archaeology 8245,Shipping,Shipsanity: Grilled Cheese,SHIPSANITY,Binning Skill 8246,Shipping,Shipsanity: Fish Casserole,SHIPSANITY,Binning Skill +8247,Shipping,Shipsanity: Snatcher Worm,SHIPSANITY,Stardew Valley Expanded diff --git a/worlds/stardew_valley/data/recipe_data.py b/worlds/stardew_valley/data/recipe_data.py index b4824687627..3123bb92430 100644 --- a/worlds/stardew_valley/data/recipe_data.py +++ b/worlds/stardew_valley/data/recipe_data.py @@ -5,7 +5,7 @@ from ..strings.artisan_good_names import ArtisanGood from ..strings.craftable_names import ModEdible, Edible from ..strings.crop_names import Fruit, Vegetable, SVEFruit, DistantLandsCrop -from ..strings.fish_names import Fish, SVEFish, WaterItem, DistantLandsFish +from ..strings.fish_names import Fish, SVEFish, WaterItem, DistantLandsFish, SVEWaterItem from ..strings.flower_names import Flower from ..strings.forageable_names import Forageable, SVEForage, DistantLandsForageable, Mushroom from ..strings.ingredient_names import Ingredient @@ -195,7 +195,7 @@ def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource, ModNames.sve) mushroom_berry_rice = friendship_and_shop_recipe(SVEMeal.mushroom_berry_rice, ModNPC.marlon, 6, Region.adventurer_guild, 1500, {SVEForage.poison_mushroom: 3, SVEForage.red_baneberry: 10, Ingredient.rice: 1, Ingredient.sugar: 2}, ModNames.sve) -seaweed_salad = shop_recipe(SVEMeal.seaweed_salad, Region.fish_shop, 1250, {SVEFish.dulse_seaweed: 2, WaterItem.seaweed: 2, Ingredient.oil: 1}, ModNames.sve) +seaweed_salad = shop_recipe(SVEMeal.seaweed_salad, Region.fish_shop, 1250, {SVEWaterItem.dulse_seaweed: 2, WaterItem.seaweed: 2, Ingredient.oil: 1}, ModNames.sve) void_delight = friendship_and_shop_recipe(SVEMeal.void_delight, NPC.krobus, 10, Region.sewer, 5000, {SVEFish.void_eel: 1, Loot.void_essence: 50, Loot.solar_essence: 20}, ModNames.sve) void_salmon_sushi = friendship_and_shop_recipe(SVEMeal.void_salmon_sushi, NPC.krobus, 10, Region.sewer, 5000, diff --git a/worlds/stardew_valley/data/requirement.py b/worlds/stardew_valley/data/requirement.py index 7e9466630fc..b2416d8d0b7 100644 --- a/worlds/stardew_valley/data/requirement.py +++ b/worlds/stardew_valley/data/requirement.py @@ -29,3 +29,29 @@ class SeasonRequirement(Requirement): @dataclass(frozen=True) class YearRequirement(Requirement): year: int + + +@dataclass(frozen=True) +class CombatRequirement(Requirement): + level: str + + +@dataclass(frozen=True) +class QuestRequirement(Requirement): + quest: str + + +@dataclass(frozen=True) +class RelationshipRequirement(Requirement): + npc: str + hearts: int + + +@dataclass(frozen=True) +class FishingRequirement(Requirement): + region: str + + +@dataclass(frozen=True) +class WalnutRequirement(Requirement): + amount: int diff --git a/worlds/stardew_valley/data/shop.py b/worlds/stardew_valley/data/shop.py index ca54d35e14f..f14dbac8213 100644 --- a/worlds/stardew_valley/data/shop.py +++ b/worlds/stardew_valley/data/shop.py @@ -16,8 +16,8 @@ class ShopSource(ItemSource): other_requirements: Tuple[Requirement, ...] = () def __post_init__(self): - assert self.money_price or self.items_price, "At least money price or items price need to be defined." - assert self.items_price is None or all(type(p) == tuple for p in self.items_price), "Items price should be a tuple." + assert self.money_price is not None or self.items_price is not None, "At least money price or items price need to be defined." + assert self.items_price is None or all(isinstance(p, tuple) for p in self.items_price), "Items price should be a tuple." @dataclass(frozen=True, **kw_only) diff --git a/worlds/stardew_valley/items.py b/worlds/stardew_valley/items.py index cb610201694..31c7da5e3ad 100644 --- a/worlds/stardew_valley/items.py +++ b/worlds/stardew_valley/items.py @@ -409,8 +409,9 @@ def create_special_quest_rewards(item_factory: StardewItemFactory, options: Star else: items.append(item_factory(Wallet.bears_knowledge, ItemClassification.useful)) # Not necessary outside of SVE items.append(item_factory(Wallet.iridium_snake_milk)) - items.append(item_factory("Fairy Dust Recipe")) items.append(item_factory("Dark Talisman")) + if options.exclude_ginger_island == ExcludeGingerIsland.option_false: + items.append(item_factory("Fairy Dust Recipe")) def create_help_wanted_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): diff --git a/worlds/stardew_valley/logic/bundle_logic.py b/worlds/stardew_valley/logic/bundle_logic.py index 4ca5fd81fc7..98fda1c73c7 100644 --- a/worlds/stardew_valley/logic/bundle_logic.py +++ b/worlds/stardew_valley/logic/bundle_logic.py @@ -27,8 +27,8 @@ def __init__(self, *args, **kwargs): self.bundle = BundleLogic(*args, **kwargs) -class BundleLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin, TimeLogicMixin, RegionLogicMixin, MoneyLogicMixin, QualityLogicMixin, FishingLogicMixin, SkillLogicMixin, -QuestLogicMixin]]): +class BundleLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin, TimeLogicMixin, RegionLogicMixin, MoneyLogicMixin, QualityLogicMixin, FishingLogicMixin, +SkillLogicMixin, QuestLogicMixin]]): # Should be cached def can_complete_bundle(self, bundle: Bundle) -> StardewRule: item_rules = [] @@ -45,7 +45,7 @@ def can_complete_bundle(self, bundle: Bundle) -> StardewRule: qualities.append(bundle_item.quality) quality_rules = self.get_quality_rules(qualities) item_rules = self.logic.has_n(*item_rules, count=bundle.number_required) - time_rule = True_() if time_to_grind <= 0 else self.logic.time.has_lived_months(time_to_grind) + time_rule = self.logic.time.has_lived_months(time_to_grind) return can_speak_junimo & item_rules & quality_rules & time_rule def get_quality_rules(self, qualities: List[str]) -> StardewRule: diff --git a/worlds/stardew_valley/logic/logic.py b/worlds/stardew_valley/logic/logic.py index 74cdaf2374e..fb0d938fbb1 100644 --- a/worlds/stardew_valley/logic/logic.py +++ b/worlds/stardew_valley/logic/logic.py @@ -1,7 +1,6 @@ from __future__ import annotations import logging -from functools import cached_property from typing import Collection, Callable from .ability_logic import AbilityLogicMixin @@ -43,6 +42,7 @@ from .tool_logic import ToolLogicMixin from .traveling_merchant_logic import TravelingMerchantLogicMixin from .wallet_logic import WalletLogicMixin +from .walnut_logic import WalnutLogicMixin from ..content.game_content import StardewContent from ..data.craftable_data import all_crafting_recipes from ..data.museum_data import all_museum_items @@ -50,16 +50,14 @@ from ..mods.logic.magic_logic import MagicLogicMixin from ..mods.logic.mod_logic import ModLogicMixin from ..mods.mod_data import ModNames -from ..options import SpecialOrderLocations, ExcludeGingerIsland, FestivalLocations, StardewValleyOptions, Walnutsanity +from ..options import ExcludeGingerIsland, FestivalLocations, StardewValleyOptions from ..stardew_rule import False_, True_, StardewRule from ..strings.animal_names import Animal from ..strings.animal_product_names import AnimalProduct -from ..strings.ap_names.ap_option_names import OptionName from ..strings.ap_names.community_upgrade_names import CommunityUpgrade -from ..strings.ap_names.event_names import Event from ..strings.artisan_good_names import ArtisanGood from ..strings.building_names import Building -from ..strings.craftable_names import Consumable, Furniture, Ring, Fishing, Lighting, WildSeeds +from ..strings.craftable_names import Consumable, Ring, Fishing, Lighting, WildSeeds from ..strings.crop_names import Fruit, Vegetable from ..strings.currency_names import Currency from ..strings.decoration_names import Decoration @@ -96,7 +94,7 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin CombatLogicMixin, MagicLogicMixin, MonsterLogicMixin, ToolLogicMixin, PetLogicMixin, QualityLogicMixin, SkillLogicMixin, FarmingLogicMixin, BundleLogicMixin, FishingLogicMixin, MineLogicMixin, CookingLogicMixin, AbilityLogicMixin, SpecialOrderLogicMixin, QuestLogicMixin, CraftingLogicMixin, ModLogicMixin, HarvestingLogicMixin, SourceLogicMixin, - RequirementLogicMixin, BookLogicMixin, GrindLogicMixin): + RequirementLogicMixin, BookLogicMixin, GrindLogicMixin, WalnutLogicMixin): player: int options: StardewValleyOptions content: StardewContent @@ -461,32 +459,6 @@ def setup_events(self, register_event: Callable[[str, str, StardewRule], None]) def can_smelt(self, item: str) -> StardewRule: return self.has(Machine.furnace) & self.has(item) - @cached_property - def can_start_field_office(self) -> StardewRule: - field_office = self.region.can_reach(Region.field_office) - professor_snail = self.received("Open Professor Snail Cave") - return field_office & professor_snail - - def can_complete_large_animal_collection(self) -> StardewRule: - fossils = self.has_all(Fossil.fossilized_leg, Fossil.fossilized_ribs, Fossil.fossilized_skull, Fossil.fossilized_spine, Fossil.fossilized_tail) - return self.can_start_field_office & fossils - - def can_complete_snake_collection(self) -> StardewRule: - fossils = self.has_all(Fossil.snake_skull, Fossil.snake_vertebrae) - return self.can_start_field_office & fossils - - def can_complete_frog_collection(self) -> StardewRule: - fossils = self.has_all(Fossil.mummified_frog) - return self.can_start_field_office & fossils - - def can_complete_bat_collection(self) -> StardewRule: - fossils = self.has_all(Fossil.mummified_bat) - return self.can_start_field_office & fossils - - def can_complete_field_office(self) -> StardewRule: - return self.can_complete_large_animal_collection() & self.can_complete_snake_collection() & \ - self.can_complete_frog_collection() & self.can_complete_bat_collection() - def can_finish_grandpa_evaluation(self) -> StardewRule: # https://stardewvalleywiki.com/Grandpa rules_worth_a_point = [ @@ -566,86 +538,6 @@ def has_island_trader(self) -> StardewRule: return False_() return self.region.can_reach(Region.island_trader) - def has_walnut(self, number: int) -> StardewRule: - if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true: - return False_() - if number <= 0: - return True_() - - if self.options.walnutsanity == Walnutsanity.preset_none: - return self.can_get_walnuts(number) - if self.options.walnutsanity == Walnutsanity.preset_all: - return self.has_received_walnuts(number) - puzzle_walnuts = 61 - bush_walnuts = 25 - dig_walnuts = 18 - repeatable_walnuts = 33 - total_walnuts = puzzle_walnuts + bush_walnuts + dig_walnuts + repeatable_walnuts - walnuts_to_receive = 0 - walnuts_to_collect = number - if OptionName.walnutsanity_puzzles in self.options.walnutsanity: - puzzle_walnut_rate = puzzle_walnuts / total_walnuts - puzzle_walnuts_required = round(puzzle_walnut_rate * number) - walnuts_to_receive += puzzle_walnuts_required - walnuts_to_collect -= puzzle_walnuts_required - if OptionName.walnutsanity_bushes in self.options.walnutsanity: - bush_walnuts_rate = bush_walnuts / total_walnuts - bush_walnuts_required = round(bush_walnuts_rate * number) - walnuts_to_receive += bush_walnuts_required - walnuts_to_collect -= bush_walnuts_required - if OptionName.walnutsanity_dig_spots in self.options.walnutsanity: - dig_walnuts_rate = dig_walnuts / total_walnuts - dig_walnuts_required = round(dig_walnuts_rate * number) - walnuts_to_receive += dig_walnuts_required - walnuts_to_collect -= dig_walnuts_required - if OptionName.walnutsanity_repeatables in self.options.walnutsanity: - repeatable_walnuts_rate = repeatable_walnuts / total_walnuts - repeatable_walnuts_required = round(repeatable_walnuts_rate * number) - walnuts_to_receive += repeatable_walnuts_required - walnuts_to_collect -= repeatable_walnuts_required - return self.has_received_walnuts(walnuts_to_receive) & self.can_get_walnuts(walnuts_to_collect) - - def has_received_walnuts(self, number: int) -> StardewRule: - return self.received(Event.received_walnuts, number) - - def can_get_walnuts(self, number: int) -> StardewRule: - # https://stardewcommunitywiki.com/Golden_Walnut#Walnut_Locations - reach_south = self.region.can_reach(Region.island_south) - reach_north = self.region.can_reach(Region.island_north) - reach_west = self.region.can_reach(Region.island_west) - reach_hut = self.region.can_reach(Region.leo_hut) - reach_southeast = self.region.can_reach(Region.island_south_east) - reach_field_office = self.region.can_reach(Region.field_office) - reach_pirate_cove = self.region.can_reach(Region.pirate_cove) - reach_outside_areas = self.logic.and_(reach_south, reach_north, reach_west, reach_hut) - reach_volcano_regions = [self.region.can_reach(Region.volcano), - self.region.can_reach(Region.volcano_secret_beach), - self.region.can_reach(Region.volcano_floor_5), - self.region.can_reach(Region.volcano_floor_10)] - reach_volcano = self.logic.or_(*reach_volcano_regions) - reach_all_volcano = self.logic.and_(*reach_volcano_regions) - reach_walnut_regions = [reach_south, reach_north, reach_west, reach_volcano, reach_field_office] - reach_caves = self.logic.and_(self.region.can_reach(Region.qi_walnut_room), self.region.can_reach(Region.dig_site), - self.region.can_reach(Region.gourmand_frog_cave), - self.region.can_reach(Region.colored_crystals_cave), - self.region.can_reach(Region.shipwreck), self.combat.has_slingshot) - reach_entire_island = self.logic.and_(reach_outside_areas, reach_all_volcano, - reach_caves, reach_southeast, reach_field_office, reach_pirate_cove) - if number <= 5: - return self.logic.or_(reach_south, reach_north, reach_west, reach_volcano) - if number <= 10: - return self.count(2, *reach_walnut_regions) - if number <= 15: - return self.count(3, *reach_walnut_regions) - if number <= 20: - return self.logic.and_(*reach_walnut_regions) - if number <= 50: - return reach_entire_island - gems = (Mineral.amethyst, Mineral.aquamarine, Mineral.emerald, Mineral.ruby, Mineral.topaz) - return reach_entire_island & self.has(Fruit.banana) & self.has_all(*gems) & self.ability.can_mine_perfectly() & \ - self.ability.can_fish_perfectly() & self.has(Furniture.flute_block) & self.has(Seed.melon) & self.has(Seed.wheat) & self.has(Seed.garlic) & \ - self.can_complete_field_office() - def has_all_stardrops(self) -> StardewRule: other_rules = [] number_of_stardrops_to_receive = 0 diff --git a/worlds/stardew_valley/logic/museum_logic.py b/worlds/stardew_valley/logic/museum_logic.py index 4ba5364f552..36ba62b31fc 100644 --- a/worlds/stardew_valley/logic/museum_logic.py +++ b/worlds/stardew_valley/logic/museum_logic.py @@ -41,7 +41,7 @@ def can_find_museum_item(self, item: MuseumItem) -> StardewRule: else: geodes_rule = False_() # monster_rule = self.can_farm_monster(item.monsters) - time_needed_to_grind = (20 - item.difficulty) / 2 + time_needed_to_grind = int((20 - item.difficulty) // 2) time_rule = self.logic.time.has_lived_months(time_needed_to_grind) pan_rule = False_() if item.item_name == Mineral.earth_crystal or item.item_name == Mineral.fire_quartz or item.item_name == Mineral.frozen_tear: diff --git a/worlds/stardew_valley/logic/requirement_logic.py b/worlds/stardew_valley/logic/requirement_logic.py index 87d9ee02152..6a5adf4890c 100644 --- a/worlds/stardew_valley/logic/requirement_logic.py +++ b/worlds/stardew_valley/logic/requirement_logic.py @@ -3,14 +3,20 @@ from .base_logic import BaseLogicMixin, BaseLogic from .book_logic import BookLogicMixin +from .combat_logic import CombatLogicMixin +from .fishing_logic import FishingLogicMixin from .has_logic import HasLogicMixin +from .quest_logic import QuestLogicMixin from .received_logic import ReceivedLogicMixin +from .relationship_logic import RelationshipLogicMixin from .season_logic import SeasonLogicMixin from .skill_logic import SkillLogicMixin from .time_logic import TimeLogicMixin from .tool_logic import ToolLogicMixin +from .walnut_logic import WalnutLogicMixin from ..data.game_item import Requirement -from ..data.requirement import ToolRequirement, BookRequirement, SkillRequirement, SeasonRequirement, YearRequirement +from ..data.requirement import ToolRequirement, BookRequirement, SkillRequirement, SeasonRequirement, YearRequirement, CombatRequirement, QuestRequirement, \ + RelationshipRequirement, FishingRequirement, WalnutRequirement class RequirementLogicMixin(BaseLogicMixin): @@ -20,7 +26,7 @@ def __init__(self, *args, **kwargs): class RequirementLogic(BaseLogic[Union[RequirementLogicMixin, HasLogicMixin, ReceivedLogicMixin, ToolLogicMixin, SkillLogicMixin, BookLogicMixin, -SeasonLogicMixin, TimeLogicMixin]]): +SeasonLogicMixin, TimeLogicMixin, CombatLogicMixin, QuestLogicMixin, RelationshipLogicMixin, FishingLogicMixin, WalnutLogicMixin]]): def meet_all_requirements(self, requirements: Iterable[Requirement]): if not requirements: @@ -50,3 +56,25 @@ def _(self, requirement: SeasonRequirement): @meet_requirement.register def _(self, requirement: YearRequirement): return self.logic.time.has_year(requirement.year) + + @meet_requirement.register + def _(self, requirement: WalnutRequirement): + return self.logic.walnut.has_walnut(requirement.amount) + + @meet_requirement.register + def _(self, requirement: CombatRequirement): + return self.logic.combat.can_fight_at_level(requirement.level) + + @meet_requirement.register + def _(self, requirement: QuestRequirement): + return self.logic.quest.can_complete_quest(requirement.quest) + + @meet_requirement.register + def _(self, requirement: RelationshipRequirement): + return self.logic.relationship.has_hearts(requirement.npc, requirement.hearts) + + @meet_requirement.register + def _(self, requirement: FishingRequirement): + return self.logic.fishing.can_fish_at(requirement.region) + + diff --git a/worlds/stardew_valley/logic/time_logic.py b/worlds/stardew_valley/logic/time_logic.py index 94e0e277c86..2ba76579ff4 100644 --- a/worlds/stardew_valley/logic/time_logic.py +++ b/worlds/stardew_valley/logic/time_logic.py @@ -26,8 +26,10 @@ class TimeLogic(BaseLogic[Union[TimeLogicMixin, HasLogicMixin]]): @cache_self1 def has_lived_months(self, number: int) -> StardewRule: + assert isinstance(number, int), "Can't have lived a fraction of a month. Use // instead of / when dividing." if number <= 0: return self.logic.true_ + number = min(number, MAX_MONTHS) return HasProgressionPercent(self.player, number * MONTH_COEFFICIENT) diff --git a/worlds/stardew_valley/logic/walnut_logic.py b/worlds/stardew_valley/logic/walnut_logic.py new file mode 100644 index 00000000000..14fe1c33909 --- /dev/null +++ b/worlds/stardew_valley/logic/walnut_logic.py @@ -0,0 +1,135 @@ +from functools import cached_property +from typing import Union + +from .ability_logic import AbilityLogicMixin +from .base_logic import BaseLogic, BaseLogicMixin +from .combat_logic import CombatLogicMixin +from .has_logic import HasLogicMixin +from .received_logic import ReceivedLogicMixin +from .region_logic import RegionLogicMixin +from ..strings.ap_names.event_names import Event +from ..options import ExcludeGingerIsland, Walnutsanity +from ..stardew_rule import StardewRule, False_, True_ +from ..strings.ap_names.ap_option_names import OptionName +from ..strings.craftable_names import Furniture +from ..strings.crop_names import Fruit +from ..strings.metal_names import Mineral, Fossil +from ..strings.region_names import Region +from ..strings.seed_names import Seed + + +class WalnutLogicMixin(BaseLogicMixin): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.walnut = WalnutLogic(*args, **kwargs) + + +class WalnutLogic(BaseLogic[Union[WalnutLogicMixin, ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, CombatLogicMixin, + AbilityLogicMixin]]): + + def has_walnut(self, number: int) -> StardewRule: + if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true: + return False_() + if number <= 0: + return True_() + + if self.options.walnutsanity == Walnutsanity.preset_none: + return self.can_get_walnuts(number) + if self.options.walnutsanity == Walnutsanity.preset_all: + return self.has_received_walnuts(number) + puzzle_walnuts = 61 + bush_walnuts = 25 + dig_walnuts = 18 + repeatable_walnuts = 33 + total_walnuts = puzzle_walnuts + bush_walnuts + dig_walnuts + repeatable_walnuts + walnuts_to_receive = 0 + walnuts_to_collect = number + if OptionName.walnutsanity_puzzles in self.options.walnutsanity: + puzzle_walnut_rate = puzzle_walnuts / total_walnuts + puzzle_walnuts_required = round(puzzle_walnut_rate * number) + walnuts_to_receive += puzzle_walnuts_required + walnuts_to_collect -= puzzle_walnuts_required + if OptionName.walnutsanity_bushes in self.options.walnutsanity: + bush_walnuts_rate = bush_walnuts / total_walnuts + bush_walnuts_required = round(bush_walnuts_rate * number) + walnuts_to_receive += bush_walnuts_required + walnuts_to_collect -= bush_walnuts_required + if OptionName.walnutsanity_dig_spots in self.options.walnutsanity: + dig_walnuts_rate = dig_walnuts / total_walnuts + dig_walnuts_required = round(dig_walnuts_rate * number) + walnuts_to_receive += dig_walnuts_required + walnuts_to_collect -= dig_walnuts_required + if OptionName.walnutsanity_repeatables in self.options.walnutsanity: + repeatable_walnuts_rate = repeatable_walnuts / total_walnuts + repeatable_walnuts_required = round(repeatable_walnuts_rate * number) + walnuts_to_receive += repeatable_walnuts_required + walnuts_to_collect -= repeatable_walnuts_required + return self.has_received_walnuts(walnuts_to_receive) & self.can_get_walnuts(walnuts_to_collect) + + def has_received_walnuts(self, number: int) -> StardewRule: + return self.logic.received(Event.received_walnuts, number) + + def can_get_walnuts(self, number: int) -> StardewRule: + # https://stardewcommunitywiki.com/Golden_Walnut#Walnut_Locations + reach_south = self.logic.region.can_reach(Region.island_south) + reach_north = self.logic.region.can_reach(Region.island_north) + reach_west = self.logic.region.can_reach(Region.island_west) + reach_hut = self.logic.region.can_reach(Region.leo_hut) + reach_southeast = self.logic.region.can_reach(Region.island_south_east) + reach_field_office = self.logic.region.can_reach(Region.field_office) + reach_pirate_cove = self.logic.region.can_reach(Region.pirate_cove) + reach_outside_areas = self.logic.and_(reach_south, reach_north, reach_west, reach_hut) + reach_volcano_regions = [self.logic.region.can_reach(Region.volcano), + self.logic.region.can_reach(Region.volcano_secret_beach), + self.logic.region.can_reach(Region.volcano_floor_5), + self.logic.region.can_reach(Region.volcano_floor_10)] + reach_volcano = self.logic.or_(*reach_volcano_regions) + reach_all_volcano = self.logic.and_(*reach_volcano_regions) + reach_walnut_regions = [reach_south, reach_north, reach_west, reach_volcano, reach_field_office] + reach_caves = self.logic.and_(self.logic.region.can_reach(Region.qi_walnut_room), self.logic.region.can_reach(Region.dig_site), + self.logic.region.can_reach(Region.gourmand_frog_cave), + self.logic.region.can_reach(Region.colored_crystals_cave), + self.logic.region.can_reach(Region.shipwreck), self.logic.combat.has_slingshot) + reach_entire_island = self.logic.and_(reach_outside_areas, reach_all_volcano, + reach_caves, reach_southeast, reach_field_office, reach_pirate_cove) + if number <= 5: + return self.logic.or_(reach_south, reach_north, reach_west, reach_volcano) + if number <= 10: + return self.logic.count(2, *reach_walnut_regions) + if number <= 15: + return self.logic.count(3, *reach_walnut_regions) + if number <= 20: + return self.logic.and_(*reach_walnut_regions) + if number <= 50: + return reach_entire_island + gems = (Mineral.amethyst, Mineral.aquamarine, Mineral.emerald, Mineral.ruby, Mineral.topaz) + return reach_entire_island & self.logic.has(Fruit.banana) & self.logic.has_all(*gems) & \ + self.logic.ability.can_mine_perfectly() & self.logic.ability.can_fish_perfectly() & \ + self.logic.has(Furniture.flute_block) & self.logic.has(Seed.melon) & self.logic.has(Seed.wheat) & \ + self.logic.has(Seed.garlic) & self.can_complete_field_office() + + @cached_property + def can_start_field_office(self) -> StardewRule: + field_office = self.logic.region.can_reach(Region.field_office) + professor_snail = self.logic.received("Open Professor Snail Cave") + return field_office & professor_snail + + def can_complete_large_animal_collection(self) -> StardewRule: + fossils = self.logic.has_all(Fossil.fossilized_leg, Fossil.fossilized_ribs, Fossil.fossilized_skull, Fossil.fossilized_spine, Fossil.fossilized_tail) + return self.can_start_field_office & fossils + + def can_complete_snake_collection(self) -> StardewRule: + fossils = self.logic.has_all(Fossil.snake_skull, Fossil.snake_vertebrae) + return self.can_start_field_office & fossils + + def can_complete_frog_collection(self) -> StardewRule: + fossils = self.logic.has_all(Fossil.mummified_frog) + return self.can_start_field_office & fossils + + def can_complete_bat_collection(self) -> StardewRule: + fossils = self.logic.has_all(Fossil.mummified_bat) + return self.can_start_field_office & fossils + + def can_complete_field_office(self) -> StardewRule: + return self.can_complete_large_animal_collection() & self.can_complete_snake_collection() & \ + self.can_complete_frog_collection() & self.can_complete_bat_collection() diff --git a/worlds/stardew_valley/mods/logic/item_logic.py b/worlds/stardew_valley/mods/logic/item_logic.py index cfafc88e83f..ef5eab0134d 100644 --- a/worlds/stardew_valley/mods/logic/item_logic.py +++ b/worlds/stardew_valley/mods/logic/item_logic.py @@ -23,24 +23,15 @@ from ...options import Cropsanity from ...stardew_rule import StardewRule, True_ from ...strings.artisan_good_names import ModArtisanGood -from ...strings.craftable_names import ModCraftable, ModEdible, ModMachine -from ...strings.crop_names import SVEVegetable, SVEFruit, DistantLandsCrop -from ...strings.fish_names import ModTrash, SVEFish -from ...strings.food_names import SVEMeal, SVEBeverage -from ...strings.forageable_names import SVEForage, DistantLandsForageable -from ...strings.gift_names import SVEGift +from ...strings.craftable_names import ModCraftable, ModMachine +from ...strings.fish_names import ModTrash from ...strings.ingredient_names import Ingredient from ...strings.material_names import Material from ...strings.metal_names import all_fossils, all_artifacts, Ore, ModFossil -from ...strings.monster_drop_names import ModLoot, Loot +from ...strings.monster_drop_names import Loot from ...strings.performance_names import Performance -from ...strings.quest_names import ModQuest -from ...strings.region_names import Region, SVERegion, DeepWoodsRegion, BoardingHouseRegion -from ...strings.season_names import Season -from ...strings.seed_names import SVESeed, DistantLandsSeed -from ...strings.skill_names import Skill +from ...strings.region_names import SVERegion, DeepWoodsRegion, BoardingHouseRegion from ...strings.tool_names import Tool, ToolMaterial -from ...strings.villager_names import ModNPC display_types = [ModCraftable.wooden_display, ModCraftable.hardwood_display] display_items = all_artifacts + all_fossils @@ -58,12 +49,6 @@ class ModItemLogic(BaseLogic[Union[CombatLogicMixin, ReceivedLogicMixin, Cooking def get_modded_item_rules(self) -> Dict[str, StardewRule]: items = dict() - if ModNames.sve in self.options.mods: - items.update(self.get_sve_item_rules()) - if ModNames.archaeology in self.options.mods: - items.update(self.get_archaeology_item_rules()) - if ModNames.distant_lands in self.options.mods: - items.update(self.get_distant_lands_item_rules()) if ModNames.boarding_house in self.options.mods: items.update(self.get_boarding_house_item_rules()) return items @@ -75,61 +60,6 @@ def modify_vanilla_item_rules_with_mod_additions(self, item_rule: Dict[str, Star item_rule.update(self.get_modified_item_rules_for_deep_woods(item_rule)) return item_rule - def get_sve_item_rules(self): - return {SVEGift.aged_blue_moon_wine: self.logic.money.can_spend_at(SVERegion.sophias_house, 28000), - SVEGift.blue_moon_wine: self.logic.money.can_spend_at(SVERegion.sophias_house, 3000), - SVESeed.fungus: self.logic.region.can_reach(SVERegion.highlands_cavern) & self.logic.combat.has_good_weapon, - ModLoot.green_mushroom: self.logic.region.can_reach(SVERegion.highlands_outside) & - self.logic.tool.has_tool(Tool.axe, ToolMaterial.iron) & self.logic.season.has_any_not_winter(), - SVEFruit.monster_fruit: self.logic.season.has(Season.summer) & self.logic.has(SVESeed.stalk), - SVEVegetable.monster_mushroom: self.logic.season.has(Season.fall) & self.logic.has(SVESeed.fungus), - ModLoot.ornate_treasure_chest: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_galaxy_weapon & - self.logic.tool.has_tool(Tool.axe, ToolMaterial.iron), - SVEFruit.slime_berry: self.logic.season.has(Season.spring) & self.logic.has(SVESeed.slime), - SVESeed.slime: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_good_weapon, - SVESeed.stalk: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_good_weapon, - ModLoot.swirl_stone: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon, - SVEVegetable.void_root: self.logic.season.has(Season.winter) & self.logic.has(SVESeed.void), - SVESeed.void: self.logic.region.can_reach(SVERegion.highlands_cavern) & self.logic.combat.has_good_weapon, - ModLoot.void_soul: self.logic.region.can_reach( - SVERegion.crimson_badlands) & self.logic.combat.has_good_weapon & self.logic.cooking.can_cook(), - SVEForage.winter_star_rose: self.logic.region.can_reach(SVERegion.summit) & self.logic.season.has(Season.winter), - SVEForage.bearberry: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has(Season.winter), - SVEForage.poison_mushroom: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has_any([Season.summer, Season.fall]), - SVEForage.red_baneberry: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has(Season.summer), - SVEForage.ferngill_primrose: self.logic.region.can_reach(SVERegion.summit) & self.logic.season.has(Season.spring), - SVEForage.goldenrod: self.logic.region.can_reach(SVERegion.summit) & ( - self.logic.season.has(Season.summer) | self.logic.season.has(Season.fall)), - SVESeed.shrub: self.logic.region.can_reach(Region.secret_woods) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic), - SVEFruit.salal_berry: self.logic.farming.can_plant_and_grow_item((Season.spring, Season.summer)) & self.logic.has(SVESeed.shrub), - ModEdible.aegis_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 28000), - ModEdible.lightning_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 12000), - ModEdible.barbarian_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 22000), - ModEdible.gravity_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 4000), - SVESeed.ancient_fern: self.logic.region.can_reach(Region.secret_woods) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic), - SVEVegetable.ancient_fiber: self.logic.farming.can_plant_and_grow_item(Season.summer) & self.logic.has(SVESeed.ancient_fern), - SVEForage.conch: self.logic.region.can_reach_any((Region.beach, SVERegion.fable_reef)), - SVEForage.dewdrop_berry: self.logic.region.can_reach(SVERegion.enchanted_grove), - SVEForage.sand_dollar: self.logic.region.can_reach(SVERegion.fable_reef) | (self.logic.region.can_reach(Region.beach) & - self.logic.season.has_any([Season.summer, Season.fall])), - SVEForage.golden_ocean_flower: self.logic.region.can_reach(SVERegion.fable_reef), - SVEMeal.grampleton_orange_chicken: self.logic.money.can_spend_at(Region.saloon, 650) & self.logic.relationship.has_hearts(ModNPC.sophia, 6), - ModEdible.hero_elixir: self.logic.money.can_spend_at(SVERegion.isaac_shop, 8000), - SVEForage.four_leaf_clover: self.logic.region.can_reach_any((Region.secret_woods, SVERegion.forest_west)) & - self.logic.season.has_any([Season.spring, Season.summer]), - SVEForage.mushroom_colony: self.logic.region.can_reach_any((Region.secret_woods, SVERegion.junimo_woods, SVERegion.forest_west)) & - self.logic.season.has(Season.fall), - SVEForage.rusty_blade: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon, - SVEForage.rafflesia: self.logic.region.can_reach(Region.secret_woods), - SVEBeverage.sports_drink: self.logic.money.can_spend_at(Region.hospital, 750), - "Stamina Capsule": self.logic.money.can_spend_at(Region.hospital, 4000), - SVEForage.thistle: self.logic.region.can_reach(SVERegion.summit), - ModLoot.void_pebble: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon, - ModLoot.void_shard: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_galaxy_weapon & - self.logic.skill.has_level(Skill.combat, 10) & self.logic.region.can_reach(Region.saloon) & self.logic.time.has_year_three - } - # @formatter:on - def get_modified_item_rules_for_sve(self, items: Dict[str, StardewRule]): return { Loot.void_essence: items[Loot.void_essence] | self.logic.region.can_reach(SVERegion.highlands_cavern) | self.logic.region.can_reach( @@ -141,7 +71,7 @@ def get_modified_item_rules_for_sve(self, items: Dict[str, StardewRule]): self.logic.combat.can_fight_at_level(Performance.great)), Ore.iridium: items[Ore.iridium] | (self.logic.tool.can_use_tool_at(Tool.pickaxe, ToolMaterial.basic, SVERegion.crimson_badlands) & self.logic.combat.can_fight_at_level(Performance.maximum)), - SVEFish.dulse_seaweed: self.logic.fishing.can_fish_at(Region.beach) & self.logic.season.has_any([Season.spring, Season.summer, Season.winter]) + } def get_modified_item_rules_for_deep_woods(self, items: Dict[str, StardewRule]): @@ -160,36 +90,6 @@ def get_modified_item_rules_for_deep_woods(self, items: Dict[str, StardewRule]): return options_to_update - def get_archaeology_item_rules(self): - archaeology_item_rules = {} - preservation_chamber_rule = self.logic.has(ModMachine.preservation_chamber) - hardwood_preservation_chamber_rule = self.logic.has(ModMachine.hardwood_preservation_chamber) - for item in display_items: - for display_type in display_types: - if item == "Trilobite": - location_name = f"{display_type}: Trilobite Fossil" - else: - location_name = f"{display_type}: {item}" - display_item_rule = self.logic.crafting.can_craft(all_crafting_recipes_by_name[display_type]) & self.logic.has(item) - if "Wooden" in display_type: - archaeology_item_rules[location_name] = display_item_rule & preservation_chamber_rule - else: - archaeology_item_rules[location_name] = display_item_rule & hardwood_preservation_chamber_rule - archaeology_item_rules[ModTrash.rusty_scrap] = self.logic.has(ModMachine.grinder) & self.logic.has_any(*all_artifacts) - return archaeology_item_rules - - def get_distant_lands_item_rules(self): - return { - DistantLandsForageable.swamp_herb: self.logic.region.can_reach(Region.witch_swamp), - DistantLandsForageable.brown_amanita: self.logic.region.can_reach(Region.witch_swamp), - DistantLandsSeed.vile_ancient_fruit: self.logic.quest.can_complete_quest(ModQuest.WitchOrder) | self.logic.quest.can_complete_quest( - ModQuest.CorruptedCropsTask), - DistantLandsSeed.void_mint: self.logic.quest.can_complete_quest(ModQuest.WitchOrder) | self.logic.quest.can_complete_quest( - ModQuest.CorruptedCropsTask), - DistantLandsCrop.void_mint: self.logic.season.has_any_not_winter() & self.logic.has(DistantLandsSeed.void_mint), - DistantLandsCrop.vile_ancient_fruit: self.logic.season.has_any_not_winter() & self.logic.has(DistantLandsSeed.vile_ancient_fruit), - } - def get_boarding_house_item_rules(self): return { # Mob Drops from lost valley enemies @@ -251,8 +151,3 @@ def get_boarding_house_item_rules(self): BoardingHouseRegion.lost_valley_house_2,)) & self.logic.combat.can_fight_at_level( Performance.great), } - - def has_seed_unlocked(self, seed_name: str): - if self.options.cropsanity == Cropsanity.option_disabled: - return True_() - return self.logic.received(seed_name) diff --git a/worlds/stardew_valley/mods/mod_regions.py b/worlds/stardew_valley/mods/mod_regions.py index c075bd4d106..a402ba60686 100644 --- a/worlds/stardew_valley/mods/mod_regions.py +++ b/worlds/stardew_valley/mods/mod_regions.py @@ -183,7 +183,8 @@ RegionData(SVERegion.first_slash_guild, [SVEEntrance.first_slash_guild_to_hallway], is_ginger_island=True), RegionData(SVERegion.first_slash_hallway, [SVEEntrance.first_slash_hallway_to_room], is_ginger_island=True), RegionData(SVERegion.first_slash_spare_room, is_ginger_island=True), - RegionData(SVERegion.highlands_outside, [SVEEntrance.highlands_to_lance, SVEEntrance.highlands_to_cave], is_ginger_island=True), + RegionData(SVERegion.highlands_outside, [SVEEntrance.highlands_to_lance, SVEEntrance.highlands_to_cave, SVEEntrance.highlands_to_pond], is_ginger_island=True), + RegionData(SVERegion.highlands_pond, is_ginger_island=True), RegionData(SVERegion.highlands_cavern, [SVEEntrance.to_dwarf_prison], is_ginger_island=True), RegionData(SVERegion.dwarf_prison, is_ginger_island=True), RegionData(SVERegion.lances_house, [SVEEntrance.lance_to_ladder], is_ginger_island=True), @@ -276,6 +277,7 @@ ConnectionData(SVEEntrance.sprite_spring_to_cave, SVERegion.sprite_spring_cave, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.fish_shop_to_willy_bedroom, SVERegion.willy_bedroom, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.museum_to_gunther_bedroom, SVERegion.gunther_bedroom, flag=RandomizationFlag.BUILDINGS), + ConnectionData(SVEEntrance.highlands_to_pond, SVERegion.highlands_pond), ] alecto_regions = [ diff --git a/worlds/stardew_valley/regions.py b/worlds/stardew_valley/regions.py index 2aca2d3f4d3..b0fc7fa0ea5 100644 --- a/worlds/stardew_valley/regions.py +++ b/worlds/stardew_valley/regions.py @@ -137,7 +137,8 @@ def __call__(self, name: str, regions: Iterable[str]) -> Region: [Entrance.island_west_to_islandfarmhouse, Entrance.island_west_to_gourmand_cave, Entrance.island_west_to_crystals_cave, Entrance.island_west_to_shipwreck, Entrance.island_west_to_qi_walnut_room, Entrance.use_farm_obelisk, Entrance.parrot_express_jungle_to_docks, Entrance.parrot_express_jungle_to_dig_site, Entrance.parrot_express_jungle_to_volcano, LogicEntrance.grow_spring_crops_on_island, - LogicEntrance.grow_summer_crops_on_island, LogicEntrance.grow_fall_crops_on_island, LogicEntrance.grow_winter_crops_on_island, LogicEntrance.grow_indoor_crops_on_island], + LogicEntrance.grow_summer_crops_on_island, LogicEntrance.grow_fall_crops_on_island, LogicEntrance.grow_winter_crops_on_island, + LogicEntrance.grow_indoor_crops_on_island], is_ginger_island=True), RegionData(Region.island_east, [Entrance.island_east_to_leo_hut, Entrance.island_east_to_island_shrine], is_ginger_island=True), RegionData(Region.island_shrine, is_ginger_island=True), @@ -536,7 +537,7 @@ def create_final_regions(world_options) -> List[RegionData]: def create_final_connections_and_regions(world_options) -> Tuple[Dict[str, ConnectionData], Dict[str, RegionData]]: regions_data: Dict[str, RegionData] = {region.name: region for region in create_final_regions(world_options)} connections = {connection.name: connection for connection in vanilla_connections} - connections = modify_connections_for_mods(connections, world_options.mods) + connections = modify_connections_for_mods(connections, sorted(world_options.mods.value)) include_island = world_options.exclude_ginger_island == ExcludeGingerIsland.option_false return remove_ginger_island_regions_and_connections(regions_data, connections, include_island) @@ -563,10 +564,8 @@ def remove_ginger_island_regions_and_connections(regions_by_name: Dict[str, Regi return connections, regions_by_name -def modify_connections_for_mods(connections: Dict[str, ConnectionData], mods) -> Dict[str, ConnectionData]: - if mods is None: - return connections - for mod in mods.value: +def modify_connections_for_mods(connections: Dict[str, ConnectionData], mods: Iterable) -> Dict[str, ConnectionData]: + for mod in mods: if mod not in ModDataList: continue if mod in vanilla_connections_to_remove_by_mod: diff --git a/worlds/stardew_valley/rules.py b/worlds/stardew_valley/rules.py index c30d04c8a6f..89b1cf87c3c 100644 --- a/worlds/stardew_valley/rules.py +++ b/worlds/stardew_valley/rules.py @@ -375,7 +375,7 @@ def set_ginger_island_rules(logic: StardewLogic, multiworld, player, world_optio MultiWorldRules.add_rule(multiworld.get_location("Open Professor Snail Cave", player), logic.has(Bomb.cherry_bomb)) MultiWorldRules.add_rule(multiworld.get_location("Complete Island Field Office", player), - logic.can_complete_field_office()) + logic.walnut.can_complete_field_office()) set_walnut_rules(logic, multiworld, player, world_options) @@ -432,10 +432,10 @@ def set_island_entrances_rules(logic: StardewLogic, multiworld, player, world_op def set_island_parrot_rules(logic: StardewLogic, multiworld, player): # Logic rules require more walnuts than in reality, to allow the player to spend them "wrong" - has_walnut = logic.has_walnut(5) - has_5_walnut = logic.has_walnut(15) - has_10_walnut = logic.has_walnut(40) - has_20_walnut = logic.has_walnut(60) + has_walnut = logic.walnut.has_walnut(5) + has_5_walnut = logic.walnut.has_walnut(15) + has_10_walnut = logic.walnut.has_walnut(40) + has_20_walnut = logic.walnut.has_walnut(60) MultiWorldRules.add_rule(multiworld.get_location("Leo's Parrot", player), has_walnut) MultiWorldRules.add_rule(multiworld.get_location("Island West Turtle", player), @@ -471,7 +471,7 @@ def set_walnut_rules(logic: StardewLogic, multiworld, player, world_options: Sta set_walnut_repeatable_rules(logic, multiworld, player, world_options) -def set_walnut_puzzle_rules(logic, multiworld, player, world_options): +def set_walnut_puzzle_rules(logic: StardewLogic, multiworld, player, world_options): if OptionName.walnutsanity_puzzles not in world_options.walnutsanity: return @@ -482,15 +482,17 @@ def set_walnut_puzzle_rules(logic, multiworld, player, world_options): logic.has(Mineral.emerald) & logic.has(Mineral.ruby) & logic.has(Mineral.topaz) & logic.region.can_reach_all((Region.island_north, Region.island_west, Region.island_east, Region.island_south))) MultiWorldRules.add_rule(multiworld.get_location("Gourmand Frog Melon", player), logic.has(Fruit.melon) & logic.region.can_reach(Region.island_west)) - MultiWorldRules.add_rule(multiworld.get_location("Gourmand Frog Wheat", player), logic.has(Vegetable.wheat) & logic.region.can_reach(Region.island_west)) - MultiWorldRules.add_rule(multiworld.get_location("Gourmand Frog Garlic", player), logic.has(Vegetable.garlic) & logic.region.can_reach(Region.island_west)) + MultiWorldRules.add_rule(multiworld.get_location("Gourmand Frog Wheat", player), logic.has(Vegetable.wheat) & + logic.region.can_reach(Region.island_west) & logic.region.can_reach_location("Gourmand Frog Melon")) + MultiWorldRules.add_rule(multiworld.get_location("Gourmand Frog Garlic", player), logic.has(Vegetable.garlic) & + logic.region.can_reach(Region.island_west) & logic.region.can_reach_location("Gourmand Frog Wheat")) MultiWorldRules.add_rule(multiworld.get_location("Whack A Mole", player), logic.tool.has_tool(Tool.watering_can, ToolMaterial.iridium)) - MultiWorldRules.add_rule(multiworld.get_location("Complete Large Animal Collection", player), logic.can_complete_large_animal_collection()) - MultiWorldRules.add_rule(multiworld.get_location("Complete Snake Collection", player), logic.can_complete_snake_collection()) - MultiWorldRules.add_rule(multiworld.get_location("Complete Mummified Frog Collection", player), logic.can_complete_frog_collection()) - MultiWorldRules.add_rule(multiworld.get_location("Complete Mummified Bat Collection", player), logic.can_complete_bat_collection()) - MultiWorldRules.add_rule(multiworld.get_location("Purple Flowers Island Survey", player), logic.can_start_field_office) - MultiWorldRules.add_rule(multiworld.get_location("Purple Starfish Island Survey", player), logic.can_start_field_office) + MultiWorldRules.add_rule(multiworld.get_location("Complete Large Animal Collection", player), logic.walnut.can_complete_large_animal_collection()) + MultiWorldRules.add_rule(multiworld.get_location("Complete Snake Collection", player), logic.walnut.can_complete_snake_collection()) + MultiWorldRules.add_rule(multiworld.get_location("Complete Mummified Frog Collection", player), logic.walnut.can_complete_frog_collection()) + MultiWorldRules.add_rule(multiworld.get_location("Complete Mummified Bat Collection", player), logic.walnut.can_complete_bat_collection()) + MultiWorldRules.add_rule(multiworld.get_location("Purple Flowers Island Survey", player), logic.walnut.can_start_field_office) + MultiWorldRules.add_rule(multiworld.get_location("Purple Starfish Island Survey", player), logic.walnut.can_start_field_office) MultiWorldRules.add_rule(multiworld.get_location("Protruding Tree Walnut", player), logic.combat.has_slingshot) MultiWorldRules.add_rule(multiworld.get_location("Starfish Tide Pool", player), logic.tool.has_fishing_rod(1)) MultiWorldRules.add_rule(multiworld.get_location("Mermaid Song", player), logic.has(Furniture.flute_block)) @@ -1029,6 +1031,7 @@ def set_sve_ginger_island_rules(logic: StardewLogic, multiworld: MultiWorld, pla set_entrance_rule(multiworld, player, SVEEntrance.wizard_to_fable_reef, logic.received(SVEQuestItem.fable_reef_portal)) set_entrance_rule(multiworld, player, SVEEntrance.highlands_to_cave, logic.tool.has_tool(Tool.pickaxe, ToolMaterial.iron) & logic.tool.has_tool(Tool.axe, ToolMaterial.iron)) + set_entrance_rule(multiworld, player, SVEEntrance.highlands_to_pond, logic.tool.has_tool(Tool.axe, ToolMaterial.iron)) def set_boarding_house_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions): diff --git a/worlds/stardew_valley/stardew_rule/base.py b/worlds/stardew_valley/stardew_rule/base.py index 576cd36851f..3e6eb327ea9 100644 --- a/worlds/stardew_valley/stardew_rule/base.py +++ b/worlds/stardew_valley/stardew_rule/base.py @@ -431,7 +431,7 @@ def rules_count(self): return len(self.rules) def __repr__(self): - return f"Received {self.count} {repr(self.rules)}" + return f"Received {self.count} [{', '.join(f'{value}x {repr(rule)}' for rule, value in self.counter.items())}]" @dataclass(frozen=True) diff --git a/worlds/stardew_valley/stardew_rule/rule_explain.py b/worlds/stardew_valley/stardew_rule/rule_explain.py index 61a88ceb699..a9767c7b72d 100644 --- a/worlds/stardew_valley/stardew_rule/rule_explain.py +++ b/worlds/stardew_valley/stardew_rule/rule_explain.py @@ -34,7 +34,7 @@ def __str__(self, depth=0): if not self.sub_rules: return self.summary(depth) - return self.summary(depth) + "\n" + "\n".join(RuleExplanation.__str__(i, depth + 1) + return self.summary(depth) + "\n" + "\n".join(i.__str__(depth + 1) if i.result is not self.expected else i.summary(depth + 1) for i in sorted(self.explained_sub_rules, key=lambda x: x.result)) @@ -42,7 +42,7 @@ def __repr__(self, depth=0): if not self.sub_rules: return self.summary(depth) - return self.summary(depth) + "\n" + "\n".join(RuleExplanation.__repr__(i, depth + 1) + return self.summary(depth) + "\n" + "\n".join(i.__repr__(depth + 1) for i in sorted(self.explained_sub_rules, key=lambda x: x.result)) @cached_property @@ -61,6 +61,33 @@ def explained_sub_rules(self) -> List[RuleExplanation]: return [_explain(i, self.state, self.expected, self.explored_rules_key) for i in self.sub_rules] +@dataclass +class CountSubRuleExplanation(RuleExplanation): + count: int = 1 + + @staticmethod + def from_explanation(expl: RuleExplanation, count: int) -> CountSubRuleExplanation: + return CountSubRuleExplanation(expl.rule, expl.state, expl.expected, expl.sub_rules, expl.explored_rules_key, expl.current_rule_explored, count) + + def summary(self, depth=0) -> str: + summary = " " * depth + f"{self.count}x {str(self.rule)} -> {self.result}" + if self.current_rule_explored: + summary += " [Already explained]" + return summary + + +@dataclass +class CountExplanation(RuleExplanation): + rule: Count + + @cached_property + def explained_sub_rules(self) -> List[RuleExplanation]: + return [ + CountSubRuleExplanation.from_explanation(_explain(rule, self.state, self.expected, self.explored_rules_key), count) + for rule, count in self.rule.counter.items() + ] + + def explain(rule: CollectionRule, state: CollectionState, expected: bool = True) -> RuleExplanation: if isinstance(rule, StardewRule): return _explain(rule, state, expected, explored_spots=set()) @@ -80,7 +107,7 @@ def _(rule: AggregatingStardewRule, state: CollectionState, expected: bool, expl @_explain.register def _(rule: Count, state: CollectionState, expected: bool, explored_spots: Set[Tuple[str, str]]) -> RuleExplanation: - return RuleExplanation(rule, state, expected, rule.rules, explored_rules_key=explored_spots) + return CountExplanation(rule, state, expected, rule.rules, explored_rules_key=explored_spots) @_explain.register diff --git a/worlds/stardew_valley/stardew_rule/state.py b/worlds/stardew_valley/stardew_rule/state.py index cf0996a63bb..5f5e61b3d4e 100644 --- a/worlds/stardew_valley/stardew_rule/state.py +++ b/worlds/stardew_valley/stardew_rule/state.py @@ -122,4 +122,4 @@ def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRul return self, self(state) def __repr__(self): - return f"Received {self.percent}% progression items." + return f"Received {self.percent}% progression items" diff --git a/worlds/stardew_valley/strings/book_names.py b/worlds/stardew_valley/strings/book_names.py index 3c32cd81b32..6c271f42ae9 100644 --- a/worlds/stardew_valley/strings/book_names.py +++ b/worlds/stardew_valley/strings/book_names.py @@ -27,10 +27,6 @@ class Book: the_diamond_hunter = "The Diamond Hunter" -class ModBook: - digging_like_worms = "Digging Like Worms" - - ordered_lost_books = [] all_lost_books = set() diff --git a/worlds/stardew_valley/strings/entrance_names.py b/worlds/stardew_valley/strings/entrance_names.py index 9b651f42760..58a919f2a8a 100644 --- a/worlds/stardew_valley/strings/entrance_names.py +++ b/worlds/stardew_valley/strings/entrance_names.py @@ -358,6 +358,7 @@ class SVEEntrance: sprite_spring_to_cave = "Sprite Spring to Sprite Spring Cave" fish_shop_to_willy_bedroom = "Willy's Fish Shop to Willy's Bedroom" museum_to_gunther_bedroom = "Museum to Gunther's Bedroom" + highlands_to_pond = "Highlands to Highlands Pond" class AlectoEntrance: diff --git a/worlds/stardew_valley/strings/fish_names.py b/worlds/stardew_valley/strings/fish_names.py index d94f9e2fd40..d4ee81430eb 100644 --- a/worlds/stardew_valley/strings/fish_names.py +++ b/worlds/stardew_valley/strings/fish_names.py @@ -137,7 +137,6 @@ class SVEFish: void_eel = "Void Eel" water_grub = "Water Grub" sea_sponge = "Sea Sponge" - dulse_seaweed = "Dulse Seaweed" class DistantLandsFish: @@ -147,6 +146,10 @@ class DistantLandsFish: giant_horsehoe_crab = "Giant Horsehoe Crab" +class SVEWaterItem: + dulse_seaweed = "Dulse Seaweed" + + class ModTrash: rusty_scrap = "Scrap Rust" diff --git a/worlds/stardew_valley/strings/food_names.py b/worlds/stardew_valley/strings/food_names.py index 5555316f831..03784336d19 100644 --- a/worlds/stardew_valley/strings/food_names.py +++ b/worlds/stardew_valley/strings/food_names.py @@ -102,6 +102,7 @@ class SVEMeal: void_delight = "Void Delight" void_salmon_sushi = "Void Salmon Sushi" grampleton_orange_chicken = "Grampleton Orange Chicken" + stamina_capsule = "Stamina Capsule" class TrashyMeal: diff --git a/worlds/stardew_valley/strings/region_names.py b/worlds/stardew_valley/strings/region_names.py index 9cedb6b8ef3..58763b6fcb8 100644 --- a/worlds/stardew_valley/strings/region_names.py +++ b/worlds/stardew_valley/strings/region_names.py @@ -296,6 +296,7 @@ class SVERegion: sprite_spring_cave = "Sprite Spring Cave" willy_bedroom = "Willy's Bedroom" gunther_bedroom = "Gunther's Bedroom" + highlands_pond = "Highlands Pond" class AlectoRegion: diff --git a/worlds/stardew_valley/test/__init__.py b/worlds/stardew_valley/test/__init__.py index bee02f3c3d6..d077432e24a 100644 --- a/worlds/stardew_valley/test/__init__.py +++ b/worlds/stardew_valley/test/__init__.py @@ -441,6 +441,16 @@ def setup_multiworld(test_options: Iterable[Dict[str, int]] = None, seed=None) - for i in range(1, len(test_options) + 1): multiworld.game[i] = StardewValleyWorld.game multiworld.player_name.update({i: f"Tester{i}"}) + args = create_args(test_options) + multiworld.set_options(args) + + for step in gen_steps: + call_all(multiworld, step) + + return multiworld + + +def create_args(test_options): args = Namespace() for name, option in StardewValleyWorld.options_dataclass.type_hints.items(): options = {} @@ -449,9 +459,4 @@ def setup_multiworld(test_options: Iterable[Dict[str, int]] = None, seed=None) - value = option(player_options[name]) if name in player_options else option.from_any(option.default) options.update({i: value}) setattr(args, name, options) - multiworld.set_options(args) - - for step in gen_steps: - call_all(multiworld, step) - - return multiworld + return args diff --git a/worlds/stardew_valley/test/mods/TestMods.py b/worlds/stardew_valley/test/mods/TestMods.py index 5e7e9d4143b..97184b1338b 100644 --- a/worlds/stardew_valley/test/mods/TestMods.py +++ b/worlds/stardew_valley/test/mods/TestMods.py @@ -14,7 +14,8 @@ class TestGenerateModsOptions(WorldAssertMixin, ModAssertMixin, SVTestCase): def test_given_single_mods_when_generate_then_basic_checks(self): for mod in options.Mods.valid_keys: - with self.solo_world_sub_test(f"Mod: {mod}", {options.Mods: mod}) as (multi_world, _): + world_options = {options.Mods: mod, options.ExcludeGingerIsland: options.ExcludeGingerIsland.option_false} + with self.solo_world_sub_test(f"Mod: {mod}", world_options) as (multi_world, _): self.assert_basic_checks(multi_world) self.assert_stray_mod_items(mod, multi_world) @@ -22,8 +23,9 @@ def test_given_mod_names_when_generate_paired_with_entrance_randomizer_then_basi for option in options.EntranceRandomization.options: for mod in options.Mods.valid_keys: world_options = { - options.EntranceRandomization.internal_name: options.EntranceRandomization.options[option], - options.Mods: mod + options.EntranceRandomization: options.EntranceRandomization.options[option], + options.Mods: mod, + options.ExcludeGingerIsland: options.ExcludeGingerIsland.option_false } with self.solo_world_sub_test(f"entrance_randomization: {option}, Mod: {mod}", world_options) as (multi_world, _): self.assert_basic_checks(multi_world) diff --git a/worlds/stardew_valley/test/rules/TestBundles.py b/worlds/stardew_valley/test/rules/TestBundles.py index 25d4c70b2ab..ab376c90d4e 100644 --- a/worlds/stardew_valley/test/rules/TestBundles.py +++ b/worlds/stardew_valley/test/rules/TestBundles.py @@ -37,7 +37,7 @@ class TestRaccoonBundlesLogic(SVTestBase): options.BundlePrice: options.BundlePrice.option_normal, options.Craftsanity: options.Craftsanity.option_all, } - seed = 1234 # Magic seed that does what I want. Might need to get changed if we change the randomness behavior of raccoon bundles + seed = 2 # Magic seed that does what I want. Might need to get changed if we change the randomness behavior of raccoon bundles def test_raccoon_bundles_rely_on_previous_ones(self): # The first raccoon bundle is a fishing one diff --git a/worlds/stardew_valley/test/stability/StabilityOutputScript.py b/worlds/stardew_valley/test/stability/StabilityOutputScript.py index 4b31011d9f4..c8918d6cf2e 100644 --- a/worlds/stardew_valley/test/stability/StabilityOutputScript.py +++ b/worlds/stardew_valley/test/stability/StabilityOutputScript.py @@ -1,6 +1,7 @@ import argparse import json +from ...options import FarmType, EntranceRandomization from ...test import setup_solo_multiworld, allsanity_mods_6_x_x if __name__ == "__main__": @@ -10,21 +11,23 @@ args = parser.parse_args() seed = args.seed - multi_world = setup_solo_multiworld( - allsanity_mods_6_x_x(), - seed=seed - ) + options = allsanity_mods_6_x_x() + options[FarmType.internal_name] = FarmType.option_standard + options[EntranceRandomization.internal_name] = EntranceRandomization.option_buildings + multi_world = setup_solo_multiworld(options, seed=seed) + world = multi_world.worlds[1] output = { "bundles": { bundle_room.name: { bundle.name: str(bundle.items) for bundle in bundle_room.bundles } - for bundle_room in multi_world.worlds[1].modified_bundles + for bundle_room in world.modified_bundles }, "items": [item.name for item in multi_world.get_items()], - "location_rules": {location.name: repr(location.access_rule) for location in multi_world.get_locations(1)} + "location_rules": {location.name: repr(location.access_rule) for location in multi_world.get_locations(1)}, + "slot_data": world.fill_slot_data() } print(json.dumps(output)) diff --git a/worlds/stardew_valley/test/stability/TestStability.py b/worlds/stardew_valley/test/stability/TestStability.py index aaa8b331846..8bb904a56ea 100644 --- a/worlds/stardew_valley/test/stability/TestStability.py +++ b/worlds/stardew_valley/test/stability/TestStability.py @@ -24,8 +24,7 @@ def test_all_locations_and_items_are_the_same_between_two_generations(self): if self.skip_long_tests: raise unittest.SkipTest("Long tests disabled") - # seed = get_seed(33778671150797368040) # troubleshooting seed - seed = get_seed(74716545478307145559) + seed = get_seed() output_a = subprocess.check_output([sys.executable, '-m', 'worlds.stardew_valley.test.stability.StabilityOutputScript', '--seed', str(seed)]) output_b = subprocess.check_output([sys.executable, '-m', 'worlds.stardew_valley.test.stability.StabilityOutputScript', '--seed', str(seed)]) @@ -54,3 +53,6 @@ def test_all_locations_and_items_are_the_same_between_two_generations(self): # We check that the actual rule has the same order to make sure it is evaluated in the same order, # so performance tests are repeatable as much as possible. self.assertEqual(rule_a, rule_b, f"Location rule of {location_a} at index {i} is different between both executions. Seed={seed}") + + for key, value in result_a["slot_data"].items(): + self.assertEqual(value, result_b["slot_data"][key], f"Slot data {key} is different between both executions. Seed={seed}") diff --git a/worlds/stardew_valley/test/stability/TestUniversalTracker.py b/worlds/stardew_valley/test/stability/TestUniversalTracker.py new file mode 100644 index 00000000000..3e334098341 --- /dev/null +++ b/worlds/stardew_valley/test/stability/TestUniversalTracker.py @@ -0,0 +1,52 @@ +import unittest +from unittest.mock import Mock + +from .. import SVTestBase, create_args, allsanity_mods_6_x_x +from ... import STARDEW_VALLEY, FarmType, BundleRandomization, EntranceRandomization + + +class TestUniversalTrackerGenerationIsStable(SVTestBase): + options = allsanity_mods_6_x_x() + options.update({ + EntranceRandomization.internal_name: EntranceRandomization.option_buildings, + BundleRandomization.internal_name: BundleRandomization.option_shuffled, + FarmType.internal_name: FarmType.option_standard, # Need to choose one otherwise it's random + }) + + def test_all_locations_and_items_are_the_same_between_two_generations(self): + # This might open a kivy window temporarily, but it's the only way to test this... + if self.skip_long_tests: + raise unittest.SkipTest("Long tests disabled") + + try: + # This test only run if UT is present, so no risk of running in the CI. + from worlds.tracker.TrackerClient import TrackerGameContext # noqa + except ImportError: + raise unittest.SkipTest("UT not loaded, skipping test") + + slot_data = self.world.fill_slot_data() + ut_data = self.world.interpret_slot_data(slot_data) + + fake_context = Mock() + fake_context.re_gen_passthrough = {STARDEW_VALLEY: ut_data} + args = create_args({0: self.options}) + args.outputpath = None + args.outputname = None + args.multi = 1 + args.race = None + args.plando_options = self.multiworld.plando_options + args.plando_items = self.multiworld.plando_items + args.plando_texts = self.multiworld.plando_texts + args.plando_connections = self.multiworld.plando_connections + args.game = self.multiworld.game + args.name = self.multiworld.player_name + args.sprite = {} + args.sprite_pool = {} + args.skip_output = True + + generated_multi_world = TrackerGameContext.TMain(fake_context, args, self.multiworld.seed) + generated_slot_data = generated_multi_world.worlds[1].fill_slot_data() + + # Just checking slot data should prove that UT generates the same result as AP generation. + self.maxDiff = None + self.assertEqual(slot_data, generated_slot_data) diff --git a/worlds/subnautica/rules.py b/worlds/subnautica/rules.py index 3b6c5cd4dd6..ea9ec6a8058 100644 --- a/worlds/subnautica/rules.py +++ b/worlds/subnautica/rules.py @@ -150,7 +150,7 @@ def has_ultra_glide_fins(state: "CollectionState", player: int) -> bool: def get_max_swim_depth(state: "CollectionState", player: int) -> int: - swim_rule: SwimRule = state.multiworld.swim_rule[player] + swim_rule: SwimRule = state.multiworld.worlds[player].options.swim_rule depth: int = swim_rule.base_depth if swim_rule.consider_items: if has_seaglide(state, player): @@ -296,7 +296,7 @@ def set_rules(subnautica_world: "SubnauticaWorld"): set_location_rule(multiworld, player, loc) if subnautica_world.creatures_to_scan: - option = multiworld.creature_scan_logic[player] + option = multiworld.worlds[player].options.creature_scan_logic for creature_name in subnautica_world.creatures_to_scan: location = set_creature_rule(multiworld, player, creature_name) diff --git a/worlds/tloz/ItemPool.py b/worlds/tloz/ItemPool.py index 5b90e99722d..4acda4ef41f 100644 --- a/worlds/tloz/ItemPool.py +++ b/worlds/tloz/ItemPool.py @@ -80,7 +80,7 @@ def generate_itempool(tlozworld): location.item.classification = ItemClassification.progression def get_pool_core(world): - random = world.multiworld.random + random = world.random pool = [] placed_items = {} @@ -132,14 +132,6 @@ def get_pool_core(world): else: pool.append(fragment) - # Level 9 junk fill - if world.options.ExpandedPool > 0: - spots = random.sample(level_locations[8], len(level_locations[8]) // 2) - for spot in spots: - junk = random.choice(list(minor_items.keys())) - placed_items[spot] = junk - minor_items[junk] -= 1 - # Finish Pool final_pool = basic_pool if world.options.ExpandedPool: diff --git a/worlds/tloz/Locations.py b/worlds/tloz/Locations.py index 5b30357c940..9715cc68429 100644 --- a/worlds/tloz/Locations.py +++ b/worlds/tloz/Locations.py @@ -99,6 +99,14 @@ "Potion Shop Item Left", "Potion Shop Item Middle", "Potion Shop Item Right" ] +take_any_locations = [ + "Take Any Item Left", "Take Any Item Middle", "Take Any Item Right" +] + +sword_cave_locations = [ + "Starting Sword Cave", "White Sword Pond", "Magical Sword Grave" +] + food_locations = [ "Level 7 Map", "Level 7 Boss", "Level 7 Triforce", "Level 7 Key Drop (Goriyas)", "Level 7 Bomb Drop (Moldorms North)", "Level 7 Bomb Drop (Goriyas North)", diff --git a/worlds/tloz/__init__.py b/worlds/tloz/__init__.py index a1f9081418e..8ea5f3e18ca 100644 --- a/worlds/tloz/__init__.py +++ b/worlds/tloz/__init__.py @@ -12,7 +12,8 @@ from .ItemPool import generate_itempool, starting_weapons, dangerous_weapon_locations from .Items import item_table, item_prices, item_game_ids from .Locations import location_table, level_locations, major_locations, shop_locations, all_level_locations, \ - standard_level_locations, shop_price_location_ids, secret_money_ids, location_ids, food_locations + standard_level_locations, shop_price_location_ids, secret_money_ids, location_ids, food_locations, \ + take_any_locations, sword_cave_locations from .Options import TlozOptions from .Rom import TLoZDeltaPatch, get_base_rom_path, first_quest_dungeon_items_early, first_quest_dungeon_items_late from .Rules import set_rules @@ -87,6 +88,21 @@ class TLoZWorld(World): } } + location_name_groups = { + "Shops": set(shop_locations), + "Take Any": set(take_any_locations), + "Sword Caves": set(sword_cave_locations), + "Level 1": set(level_locations[0]), + "Level 2": set(level_locations[1]), + "Level 3": set(level_locations[2]), + "Level 4": set(level_locations[3]), + "Level 5": set(level_locations[4]), + "Level 6": set(level_locations[5]), + "Level 7": set(level_locations[6]), + "Level 8": set(level_locations[7]), + "Level 9": set(level_locations[8]) + } + for k, v in item_name_to_id.items(): item_name_to_id[k] = v + base_id @@ -307,7 +323,7 @@ def modify_multidata(self, multidata: dict): def get_filler_item_name(self) -> str: if self.filler_items is None: self.filler_items = [item for item in item_table if item_table[item].classification == ItemClassification.filler] - return self.multiworld.random.choice(self.filler_items) + return self.random.choice(self.filler_items) def fill_slot_data(self) -> Dict[str, Any]: if self.options.ExpandedPool: diff --git a/worlds/tunic/__init__.py b/worlds/tunic/__init__.py index f63193e6aee..5253e995143 100644 --- a/worlds/tunic/__init__.py +++ b/worlds/tunic/__init__.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Any, Tuple, TypedDict +from typing import Dict, List, Any, Tuple, TypedDict, ClassVar, Union from logging import warning from BaseClasses import Region, Location, Item, Tutorial, ItemClassification, MultiWorld from .items import item_name_to_id, item_table, item_name_groups, fool_tiers, filler_items, slot_data_item_names @@ -12,6 +12,14 @@ from worlds.AutoWorld import WebWorld, World from Options import PlandoConnection from decimal import Decimal, ROUND_HALF_UP +from settings import Group, Bool + + +class TunicSettings(Group): + class DisableLocalSpoiler(Bool): + """Disallows the TUNIC client from creating a local spoiler log.""" + + disable_local_spoiler: Union[DisableLocalSpoiler, bool] = False class TunicWeb(WebWorld): @@ -57,6 +65,7 @@ class TunicWorld(World): options: TunicOptions options_dataclass = TunicOptions + settings: ClassVar[TunicSettings] item_name_groups = item_name_groups location_name_groups = location_name_groups @@ -112,7 +121,7 @@ def stage_generate_early(cls, multiworld: MultiWorld) -> None: cls.seed_groups[group] = SeedGroup(logic_rules=tunic.options.logic_rules.value, laurels_at_10_fairies=tunic.options.laurels_location == 3, fixed_shop=bool(tunic.options.fixed_shop), - plando=multiworld.plando_connections[tunic.player]) + plando=tunic.options.plando_connections) continue # lower value is more restrictive @@ -125,9 +134,9 @@ def stage_generate_early(cls, multiworld: MultiWorld) -> None: if tunic.options.fixed_shop: cls.seed_groups[group]["fixed_shop"] = True - if multiworld.plando_connections[tunic.player]: + if tunic.options.plando_connections: # loop through the connections in the player's yaml - for cxn in multiworld.plando_connections[tunic.player]: + for cxn in tunic.options.plando_connections: new_cxn = True for group_cxn in cls.seed_groups[group]["plando"]: # if neither entrance nor exit match anything in the group, add to group @@ -151,9 +160,9 @@ def stage_generate_early(cls, multiworld: MultiWorld) -> None: if new_cxn: cls.seed_groups[group]["plando"].value.append(cxn) - def create_item(self, name: str) -> TunicItem: + def create_item(self, name: str, classification: ItemClassification = None) -> TunicItem: item_data = item_table[name] - return TunicItem(name, item_data.classification, self.item_name_to_id[name], self.player) + return TunicItem(name, classification or item_data.classification, self.item_name_to_id[name], self.player) def create_items(self) -> None: @@ -183,14 +192,12 @@ def create_items(self) -> None: self.multiworld.get_location("Coins in the Well - 10 Coins", self.player).place_locked_item(laurels) elif self.options.laurels_location == "10_fairies": self.multiworld.get_location("Secret Gathering Place - 10 Fairy Reward", self.player).place_locked_item(laurels) - self.slot_data_items.append(laurels) items_to_create["Hero's Laurels"] = 0 if self.options.keys_behind_bosses: for rgb_hexagon, location in hexagon_locations.items(): hex_item = self.create_item(gold_hexagon if self.options.hexagon_quest else rgb_hexagon) self.multiworld.get_location(location, self.player).place_locked_item(hex_item) - self.slot_data_items.append(hex_item) items_to_create[rgb_hexagon] = 0 items_to_create[gold_hexagon] -= 3 @@ -236,33 +243,30 @@ def remove_filler(amount: int) -> None: remove_filler(items_to_create[gold_hexagon]) for hero_relic in item_name_groups["Hero Relics"]: - relic_item = TunicItem(hero_relic, ItemClassification.useful, self.item_name_to_id[hero_relic], self.player) - tunic_items.append(relic_item) + tunic_items.append(self.create_item(hero_relic, ItemClassification.useful)) items_to_create[hero_relic] = 0 if not self.options.ability_shuffling: for page in item_name_groups["Abilities"]: if items_to_create[page] > 0: - page_item = TunicItem(page, ItemClassification.useful, self.item_name_to_id[page], self.player) - tunic_items.append(page_item) + tunic_items.append(self.create_item(page, ItemClassification.useful)) items_to_create[page] = 0 if self.options.maskless: - mask_item = TunicItem("Scavenger Mask", ItemClassification.useful, self.item_name_to_id["Scavenger Mask"], self.player) - tunic_items.append(mask_item) + tunic_items.append(self.create_item("Scavenger Mask", ItemClassification.useful)) items_to_create["Scavenger Mask"] = 0 if self.options.lanternless: - lantern_item = TunicItem("Lantern", ItemClassification.useful, self.item_name_to_id["Lantern"], self.player) - tunic_items.append(lantern_item) + tunic_items.append(self.create_item("Lantern", ItemClassification.useful)) items_to_create["Lantern"] = 0 for item, quantity in items_to_create.items(): for _ in range(quantity): - tunic_item: TunicItem = self.create_item(item) - if item in slot_data_item_names: - self.slot_data_items.append(tunic_item) - tunic_items.append(tunic_item) + tunic_items.append(self.create_item(item)) + + for tunic_item in tunic_items: + if tunic_item.name in slot_data_item_names: + self.slot_data_items.append(tunic_item) self.multiworld.itempool += tunic_items @@ -373,7 +377,8 @@ def fill_slot_data(self) -> Dict[str, Any]: "Hexagon Quest Holy Cross": self.ability_unlocks["Pages 42-43 (Holy Cross)"], "Hexagon Quest Icebolt": self.ability_unlocks["Pages 52-53 (Icebolt)"], "Hexagon Quest Goal": self.options.hexagon_goal.value, - "Entrance Rando": self.tunic_portal_pairs + "Entrance Rando": self.tunic_portal_pairs, + "disable_local_spoiler": int(self.settings.disable_local_spoiler or self.multiworld.is_race), } for tunic_item in filter(lambda item: item.location is not None and item.code is not None, self.slot_data_items): diff --git a/worlds/tunic/locations.py b/worlds/tunic/locations.py index 2d87140fe50..09916228163 100644 --- a/worlds/tunic/locations.py +++ b/worlds/tunic/locations.py @@ -208,15 +208,15 @@ class TunicLocationData(NamedTuple): "Monastery - Monastery Chest": TunicLocationData("Quarry", "Monastery Back"), "Quarry - [Back Entrance] Bushes Holy Cross": TunicLocationData("Quarry Back", "Quarry Back", location_group="Holy Cross"), "Quarry - [Back Entrance] Chest": TunicLocationData("Quarry Back", "Quarry Back"), - "Quarry - [Central] Near Shortcut Ladder": TunicLocationData("Quarry", "Quarry"), + "Quarry - [Central] Near Shortcut Ladder": TunicLocationData("Quarry Back", "Quarry Back"), "Quarry - [East] Near Telescope": TunicLocationData("Quarry", "Quarry"), "Quarry - [East] Upper Floor": TunicLocationData("Quarry", "Quarry"), - "Quarry - [Central] Below Entry Walkway": TunicLocationData("Quarry", "Quarry"), + "Quarry - [Central] Below Entry Walkway": TunicLocationData("Quarry Back", "Quarry Back"), "Quarry - [East] Obscured Near Winding Staircase": TunicLocationData("Quarry", "Quarry"), "Quarry - [East] Obscured Beneath Scaffolding": TunicLocationData("Quarry", "Quarry"), "Quarry - [East] Obscured Near Telescope": TunicLocationData("Quarry", "Quarry"), "Quarry - [Back Entrance] Obscured Behind Wall": TunicLocationData("Quarry Back", "Quarry Back"), - "Quarry - [Central] Obscured Below Entry Walkway": TunicLocationData("Quarry", "Quarry"), + "Quarry - [Central] Obscured Below Entry Walkway": TunicLocationData("Quarry Back", "Quarry Back"), "Quarry - [Central] Top Floor Overhang": TunicLocationData("Quarry", "Quarry"), "Quarry - [East] Near Bridge": TunicLocationData("Quarry", "Quarry"), "Quarry - [Central] Above Ladder": TunicLocationData("Quarry", "Quarry Monastery Entry"), diff --git a/worlds/witness/data/WitnessLogic.txt b/worlds/witness/data/WitnessLogic.txt index 272ed176e34..b7814626ada 100644 --- a/worlds/witness/data/WitnessLogic.txt +++ b/worlds/witness/data/WitnessLogic.txt @@ -805,7 +805,7 @@ Swamp Rotating Bridge (Swamp) - Swamp Between Bridges Far - 0x181F5 - Swamp Near 159334 - 0x036CE (Rotating Bridge CW EP) - 0x181F5 - True Swamp Near Boat (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Blue Underwater - 0x18482 - Swamp Long Bridge - 0xFFD00 & 0xFFD02 - The Ocean - 0x09DB8: -158903 - 0xFFD02 (Beyond Rotating Bridge Reached Independently) - True - True +159803 - 0xFFD02 (Beyond Rotating Bridge Reached Independently) - True - True 158328 - 0x09DB8 (Boat Spawn) - True - Boat 158329 - 0x003B2 (Beyond Rotating Bridge 1) - 0x0000A - Rotated Shapers 158330 - 0x00A1E (Beyond Rotating Bridge 2) - 0x003B2 - Rotated Shapers @@ -1088,7 +1088,7 @@ Mountain Bottom Floor Pillars Room (Mountain Bottom Floor) - Elevator - 0x339BB 158529 - 0x339BB (Left Pillar 4) - 0x03859 - Black/White Squares & Stars & Symmetry Elevator (Mountain Bottom Floor): -158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True +158530 - 0x3D9A6 (Elevator Door Close Left) - True - True 158531 - 0x3D9A7 (Elevator Door Close Right) - True - True 158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True 158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True diff --git a/worlds/witness/data/WitnessLogicExpert.txt b/worlds/witness/data/WitnessLogicExpert.txt index 63e7e36c243..1d1d010fde8 100644 --- a/worlds/witness/data/WitnessLogicExpert.txt +++ b/worlds/witness/data/WitnessLogicExpert.txt @@ -805,7 +805,7 @@ Swamp Rotating Bridge (Swamp) - Swamp Between Bridges Far - 0x181F5 - Swamp Near 159334 - 0x036CE (Rotating Bridge CW EP) - 0x181F5 - True Swamp Near Boat (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Blue Underwater - 0x18482 - Swamp Long Bridge - 0xFFD00 & 0xFFD02 - The Ocean - 0x09DB8: -158903 - 0xFFD02 (Beyond Rotating Bridge Reached Independently) - True - True +159803 - 0xFFD02 (Beyond Rotating Bridge Reached Independently) - True - True 158328 - 0x09DB8 (Boat Spawn) - True - Boat 158329 - 0x003B2 (Beyond Rotating Bridge 1) - 0x0000A - Shapers & Dots & Full Dots 158330 - 0x00A1E (Beyond Rotating Bridge 2) - 0x003B2 - Rotated Shapers & Shapers & Dots & Full Dots @@ -1088,7 +1088,7 @@ Mountain Bottom Floor Pillars Room (Mountain Bottom Floor) - Elevator - 0x339BB 158529 - 0x339BB (Left Pillar 4) - 0x03859 - Symmetry & Black/White Squares & Stars & Stars + Same Colored Symbol & Triangles & Colored Dots Elevator (Mountain Bottom Floor): -158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True +158530 - 0x3D9A6 (Elevator Door Close Left) - True - True 158531 - 0x3D9A7 (Elevator Door Close Right) - True - True 158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True 158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True diff --git a/worlds/witness/data/WitnessLogicVanilla.txt b/worlds/witness/data/WitnessLogicVanilla.txt index 1aa9655361f..851031ab72f 100644 --- a/worlds/witness/data/WitnessLogicVanilla.txt +++ b/worlds/witness/data/WitnessLogicVanilla.txt @@ -805,7 +805,7 @@ Swamp Rotating Bridge (Swamp) - Swamp Between Bridges Far - 0x181F5 - Swamp Near 159334 - 0x036CE (Rotating Bridge CW EP) - 0x181F5 - True Swamp Near Boat (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Blue Underwater - 0x18482 - Swamp Long Bridge - 0xFFD00 & 0xFFD02 - The Ocean - 0x09DB8: -158903 - 0xFFD02 (Beyond Rotating Bridge Reached Independently) - True - True +159803 - 0xFFD02 (Beyond Rotating Bridge Reached Independently) - True - True 158328 - 0x09DB8 (Boat Spawn) - True - Boat 158329 - 0x003B2 (Beyond Rotating Bridge 1) - 0x0000A - Rotated Shapers 158330 - 0x00A1E (Beyond Rotating Bridge 2) - 0x003B2 - Rotated Shapers @@ -1088,7 +1088,7 @@ Mountain Bottom Floor Pillars Room (Mountain Bottom Floor) - Elevator - 0x339BB 158529 - 0x339BB (Left Pillar 4) - 0x03859 - Black/White Squares & Stars & Symmetry Elevator (Mountain Bottom Floor): -158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True +158530 - 0x3D9A6 (Elevator Door Close Left) - True - True 158531 - 0x3D9A7 (Elevator Door Close Right) - True - True 158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True 158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True diff --git a/worlds/yugioh06/boosterpacks.py b/worlds/yugioh06/boosterpacks.py index f6f4ec7732c..645977d28de 100644 --- a/worlds/yugioh06/boosterpacks.py +++ b/worlds/yugioh06/boosterpacks.py @@ -1,13 +1,13 @@ -from typing import Dict, Set +from typing import Dict, List -booster_contents: Dict[str, Set[str]] = { - "LEGEND OF B.E.W.D.": { +booster_contents: Dict[str, List[str]] = { + "LEGEND OF B.E.W.D.": [ "Exodia", "Dark Magician", "Polymerization", "Skull Servant" - }, - "METAL RAIDERS": { + ], + "METAL RAIDERS": [ "Petit Moth", "Cocoon of Evolution", "Time Wizard", @@ -30,8 +30,8 @@ "Solemn Judgment", "Dream Clown", "Heavy Storm" - }, - "PHARAOH'S SERVANT": { + ], + "PHARAOH'S SERVANT": [ "Beast of Talwar", "Jinzo", "Gearfried the Iron Knight", @@ -43,8 +43,8 @@ "The Shallow Grave", "Nobleman of Crossout", "Magic Drain" - }, - "PHARAONIC GUARDIAN": { + ], + "PHARAONIC GUARDIAN": [ "Don Zaloog", "Reasoning", "Dark Snake Syndrome", @@ -71,8 +71,8 @@ "Book of Taiyou", "Dust Tornado", "Raigeki Break" - }, - "SPELL RULER": { + ], + "SPELL RULER": [ "Ritual", "Messenger of Peace", "Megamorph", @@ -94,8 +94,8 @@ "Senju of the Thousand Hands", "Sonic Bird", "Mystical Space Typhoon" - }, - "LABYRINTH OF NIGHTMARE": { + ], + "LABYRINTH OF NIGHTMARE": [ "Destiny Board", "Spirit Message 'I'", "Spirit Message 'N'", @@ -119,8 +119,8 @@ "United We Stand", "Earthbound Spirit", "The Masked Beast" - }, - "LEGACY OF DARKNESS": { + ], + "LEGACY OF DARKNESS": [ "Last Turn", "Yata-Garasu", "Opticlops", @@ -143,8 +143,8 @@ "Maharaghi", "Susa Soldier", "Emergency Provisions", - }, - "MAGICIAN'S FORCE": { + ], + "MAGICIAN'S FORCE": [ "Huge Revolution", "Oppressed People", "United Resistance", @@ -185,8 +185,8 @@ "Royal Magical Library", "Spell Shield Type-8", "Tribute Doll", - }, - "DARK CRISIS": { + ], + "DARK CRISIS": [ "Final Countdown", "Ojama Green", "Dark Scorpion Combination", @@ -213,8 +213,8 @@ "Spell Reproduction", "Contract with the Abyss", "Dark Master - Zorc" - }, - "INVASION OF CHAOS": { + ], + "INVASION OF CHAOS": [ "Ojama Delta Hurricane", "Ojama Yellow", "Ojama Black", @@ -241,8 +241,8 @@ "Cursed Seal of the Forbidden Spell", "Stray Lambs", "Manju of the Ten Thousand Hands" - }, - "ANCIENT SANCTUARY": { + ], + "ANCIENT SANCTUARY": [ "Monster Gate", "Wall of Revealing Light", "Mystik Wok", @@ -255,8 +255,8 @@ "King of the Swamp", "Enemy Controller", "Enchanting Fitting Room" - }, - "SOUL OF THE DUELIST": { + ], + "SOUL OF THE DUELIST": [ "Ninja Grandmaster Sasuke", "Mystic Swordsman LV2", "Mystic Swordsman LV4", @@ -272,8 +272,8 @@ "Level Up!", "Howling Insect", "Mobius the Frost Monarch" - }, - "RISE OF DESTINY": { + ], + "RISE OF DESTINY": [ "Homunculus the Alchemic Being", "Thestalos the Firestorm Monarch", "Roc from the Valley of Haze", @@ -283,8 +283,8 @@ "Ultimate Insect Lv3", "Divine Wrath", "Serial Spell" - }, - "FLAMING ETERNITY": { + ], + "FLAMING ETERNITY": [ "Insect Knight", "Chiron the Mage", "Granmarg the Rock Monarch", @@ -297,8 +297,8 @@ "Golem Sentry", "Rescue Cat", "Blade Rabbit" - }, - "THE LOST MILLENIUM": { + ], + "THE LOST MILLENIUM": [ "Ritual", "Megarock Dragon", "D.D. Survivor", @@ -311,8 +311,8 @@ "Elemental Hero Thunder Giant", "Aussa the Earth Charmer", "Brain Control" - }, - "CYBERNETIC REVOLUTION": { + ], + "CYBERNETIC REVOLUTION": [ "Power Bond", "Cyber Dragon", "Cyber Twin Dragon", @@ -322,8 +322,8 @@ "Miracle Fusion", "Elemental Hero Bubbleman", "Jerry Beans Man" - }, - "ELEMENTAL ENERGY": { + ], + "ELEMENTAL ENERGY": [ "V-Tiger Jet", "W-Wing Catapult", "VW-Tiger Catapult", @@ -344,8 +344,8 @@ "Elemental Hero Bladedge", "Pot of Avarice", "B.E.S. Tetran" - }, - "SHADOW OF INFINITY": { + ], + "SHADOW OF INFINITY": [ "Hamon, Lord of Striking Thunder", "Raviel, Lord of Phantasms", "Uria, Lord of Searing Flames", @@ -357,8 +357,8 @@ "Gokipon", "Demise, King of Armageddon", "Anteatereatingant" - }, - "GAME GIFT COLLECTION": { + ], + "GAME GIFT COLLECTION": [ "Ritual", "Valkyrion the Magna Warrior", "Alpha the Magnet Warrior", @@ -383,8 +383,8 @@ "Card Destruction", "Dark Magic Ritual", "Calamity of the Wicked" - }, - "Special Gift Collection": { + ], + "Special Gift Collection": [ "Gate Guardian", "Scapegoat", "Gil Garth", @@ -398,8 +398,8 @@ "Curse of Vampire", "Elemental Hero Flame Wingman", "Magician of Black Chaos" - }, - "Fairy Collection": { + ], + "Fairy Collection": [ "Silpheed", "Dunames Dark Witch", "Hysteric Fairy", @@ -416,8 +416,8 @@ "Asura Priest", "Manju of the Ten Thousand Hands", "Senju of the Thousand Hands" - }, - "Dragon Collection": { + ], + "Dragon Collection": [ "Victory D.", "Chaos Emperor Dragon - Envoy of the End", "Kaiser Glider", @@ -434,16 +434,16 @@ "Troop Dragon", "Horus the Black Flame Dragon LV4", "Pitch-Dark Dragon" - }, - "Warrior Collection A": { + ], + "Warrior Collection A": [ "Gate Guardian", "Gearfried the Iron Knight", "Dimensional Warrior", "Command Knight", "The Last Warrior from Another Planet", "Dream Clown" - }, - "Warrior Collection B": { + ], + "Warrior Collection B": [ "Don Zaloog", "Dark Scorpion - Chick the Yellow", "Dark Scorpion - Meanae the Thorn", @@ -467,8 +467,8 @@ "Blade Knight", "Marauding Captain", "Toon Goblin Attack Force" - }, - "Fiend Collection A": { + ], + "Fiend Collection A": [ "Sangan", "Castle of Dark Illusions", "Barox", @@ -480,8 +480,8 @@ "Spear Cretin", "Versago the Destroyer", "Toon Summoned Skull" - }, - "Fiend Collection B": { + ], + "Fiend Collection B": [ "Raviel, Lord of Phantasms", "Yata-Garasu", "Helpoemer", @@ -505,15 +505,15 @@ "Jowls of Dark Demise", "D. D. Trainer", "Earthbound Spirit" - }, - "Machine Collection A": { + ], + "Machine Collection A": [ "Cyber-Stein", "Mechanicalchaser", "Jinzo", "UFO Turtle", "Cyber-Tech Alligator" - }, - "Machine Collection B": { + ], + "Machine Collection B": [ "X-Head Cannon", "Y-Dragon Head", "Z-Metal Tank", @@ -531,8 +531,8 @@ "Red Gadget", "Yellow Gadget", "B.E.S. Tetran" - }, - "Spellcaster Collection A": { + ], + "Spellcaster Collection A": [ "Exodia", "Dark Sage", "Dark Magician", @@ -544,8 +544,8 @@ "Injection Fairy Lily", "Cosmo Queen", "Magician of Black Chaos" - }, - "Spellcaster Collection B": { + ], + "Spellcaster Collection B": [ "Jowgen the Spiritualist", "Tsukuyomi", "Manticore of Darkness", @@ -574,8 +574,8 @@ "Royal Magical Library", "Aussa the Earth Charmer", - }, - "Zombie Collection": { + ], + "Zombie Collection": [ "Skull Servant", "Regenerating Mummy", "Ryu Kokki", @@ -590,8 +590,8 @@ "Des Lacooda", "Wandering Mummy", "Royal Keeper" - }, - "Special Monsters A": { + ], + "Special Monsters A": [ "X-Head Cannon", "Y-Dragon Head", "Z-Metal Tank", @@ -626,8 +626,8 @@ "Fushi No Tori", "Maharaghi", "Susa Soldier" - }, - "Special Monsters B": { + ], + "Special Monsters B": [ "Polymerization", "Mystic Swordsman LV2", "Mystic Swordsman LV4", @@ -656,8 +656,8 @@ "Level Up!", "Ultimate Insect Lv3", "Ultimate Insect Lv5" - }, - "Reverse Collection": { + ], + "Reverse Collection": [ "Magical Merchant", "Castle of Dark Illusions", "Magician of Faith", @@ -675,8 +675,8 @@ "Spear Cretin", "Nobleman of Crossout", "Aussa the Earth Charmer" - }, - "LP Recovery Collection": { + ], + "LP Recovery Collection": [ "Mystik Wok", "Poison of the Old Man", "Hysteric Fairy", @@ -691,8 +691,8 @@ "Elemental Hero Steam Healer", "Fushi No Tori", "Emergency Provisions" - }, - "Special Summon Collection A": { + ], + "Special Summon Collection A": [ "Perfectly Ultimate Great Moth", "Dark Sage", "Polymerization", @@ -726,8 +726,8 @@ "Morphing Jar #2", "Spear Cretin", "Dark Magic Curtain" - }, - "Special Summon Collection B": { + ], + "Special Summon Collection B": [ "Monster Gate", "Chaos Emperor Dragon - Envoy of the End", "Ojama Trio", @@ -756,8 +756,8 @@ "Tribute Doll", "Enchanting Fitting Room", "Stray Lambs" - }, - "Special Summon Collection C": { + ], + "Special Summon Collection C": [ "Hamon, Lord of Striking Thunder", "Raviel, Lord of Phantasms", "Uria, Lord of Searing Flames", @@ -782,13 +782,13 @@ "Ultimate Insect Lv5", "Rescue Cat", "Anteatereatingant" - }, - "Equipment Collection": { + ], + "Equipment Collection": [ "Megamorph", "Cestus of Dagla", "United We Stand" - }, - "Continuous Spell/Trap A": { + ], + "Continuous Spell/Trap A": [ "Destiny Board", "Spirit Message 'I'", "Spirit Message 'N'", @@ -801,8 +801,8 @@ "Solemn Wishes", "Embodiment of Apophis", "Toon World" - }, - "Continuous Spell/Trap B": { + ], + "Continuous Spell/Trap B": [ "Hamon, Lord of Striking Thunder", "Uria, Lord of Searing Flames", "Wave-Motion Cannon", @@ -815,8 +815,8 @@ "Skull Zoma", "Pitch-Black Power Stone", "Metal Reflect Slime" - }, - "Quick/Counter Collection": { + ], + "Quick/Counter Collection": [ "Mystik Wok", "Poison of the Old Man", "Scapegoat", @@ -841,8 +841,8 @@ "Book of Moon", "Serial Spell", "Mystical Space Typhoon" - }, - "Direct Damage Collection": { + ], + "Direct Damage Collection": [ "Hamon, Lord of Striking Thunder", "Chaos Emperor Dragon - Envoy of the End", "Dark Snake Syndrome", @@ -868,8 +868,8 @@ "Jowls of Dark Demise", "Stealth Bird", "Elemental Hero Bladedge", - }, - "Direct Attack Collection": { + ], + "Direct Attack Collection": [ "Victory D.", "Dark Scorpion Combination", "Spirit Reaper", @@ -880,8 +880,8 @@ "Toon Mermaid", "Toon Summoned Skull", "Toon Dark Magician Girl" - }, - "Monster Destroy Collection": { + ], + "Monster Destroy Collection": [ "Hamon, Lord of Striking Thunder", "Inferno", "Ninja Grandmaster Sasuke", @@ -912,12 +912,12 @@ "Offerings to the Doomed", "Divine Wrath", "Dream Clown" - }, + ], } def get_booster_locations(booster: str) -> Dict[str, str]: return { f"{booster} {i}": content - for i, content in enumerate(booster_contents[booster]) + for i, content in enumerate(booster_contents[booster], 1) } diff --git a/worlds/yugioh06/structure_deck.py b/worlds/yugioh06/structure_deck.py index d58223f2e21..3559e7c5153 100644 --- a/worlds/yugioh06/structure_deck.py +++ b/worlds/yugioh06/structure_deck.py @@ -1,7 +1,7 @@ -from typing import Dict, Set +from typing import Dict, List -structure_contents: Dict[str, Set] = { - "dragons_roar": { +structure_contents: Dict[str, List[str]] = { + "dragons_roar": [ "Luster Dragon", "Armed Dragon LV3", "Armed Dragon LV5", @@ -14,9 +14,9 @@ "Stamping Destruction", "Heavy Storm", "Dust Tornado", - "Mystical Space Typhoon", - }, - "zombie_madness": { + "Mystical Space Typhoon" + ], + "zombie_madness": [ "Pyramid Turtle", "Regenerating Mummy", "Ryu Kokki", @@ -26,9 +26,9 @@ "Reload", "Heavy Storm", "Dust Tornado", - "Mystical Space Typhoon", - }, - "blazing_destruction": { + "Mystical Space Typhoon" + ], + "blazing_destruction": [ "Inferno", "Solar Flare Dragon", "UFO Turtle", @@ -38,9 +38,9 @@ "Level Limit - Area B", "Heavy Storm", "Dust Tornado", - "Mystical Space Typhoon", - }, - "fury_from_the_deep": { + "Mystical Space Typhoon" + ], + "fury_from_the_deep": [ "Mother Grizzly", "Water Beaters", "Gravity Bind", @@ -48,9 +48,9 @@ "Mobius the Frost Monarch", "Heavy Storm", "Dust Tornado", - "Mystical Space Typhoon", - }, - "warriors_triumph": { + "Mystical Space Typhoon" + ], + "warriors_triumph": [ "Gearfried the Iron Knight", "D.D. Warrior Lady", "Marauding Captain", @@ -60,9 +60,9 @@ "Reload", "Heavy Storm", "Dust Tornado", - "Mystical Space Typhoon", - }, - "spellcasters_judgement": { + "Mystical Space Typhoon" + ], + "spellcasters_judgement": [ "Dark Magician", "Apprentice Magician", "Breaker the Magical Warrior", @@ -70,14 +70,18 @@ "Skilled Dark Magician", "Tsukuyomi", "Magical Dimension", - "Mage PowerSpell-Counter Cards", + "Mage Power", + "Spell-Counter Cards", "Heavy Storm", "Dust Tornado", - "Mystical Space Typhoon", - }, - "none": {}, + "Mystical Space Typhoon" + ], + "none": [], } def get_deck_content_locations(deck: str) -> Dict[str, str]: - return {f"{deck} {i}": content for i, content in enumerate(structure_contents[deck])} + return { + f"{deck} {i}": content + for i, content in enumerate(structure_contents[deck], 1) + }