diff --git a/BaseClasses.py b/BaseClasses.py index a186404727..6816617279 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -126,7 +126,6 @@ class MultiWorld(): set_player_attr('beemizer_total_chance', 0) set_player_attr('beemizer_trap_chance', 0) set_player_attr('escape_assist', []) - set_player_attr('open_pyramid', False) set_player_attr('treasure_hunt_icon', 'Triforce Piece') set_player_attr('treasure_hunt_count', 0) set_player_attr('clock_mode', False) @@ -390,20 +389,14 @@ class MultiWorld(): self.state.collect(item, True) def push_item(self, location: Location, item: Item, collect: bool = True): - if not isinstance(location, Location): - raise RuntimeError( - 'Cannot assign item %s to invalid location %s (player %d).' % (item, location, item.player)) + assert location.can_fill(self.state, item, False), f"Cannot place {item} into {location}." + location.item = item + item.location = location + item.world = self # try to not have this here anymore and create it with item? + if collect: + self.state.collect(item, location.event, location) - if location.can_fill(self.state, item, False): - location.item = item - item.location = location - item.world = self # try to not have this here anymore - if collect: - self.state.collect(item, location.event, location) - - logging.debug('Placed %s at %s', item, location) - else: - raise RuntimeError('Cannot assign item %s to location %s.' % (item, location)) + logging.debug('Placed %s at %s', item, location) def get_entrances(self) -> List[Entrance]: if self._cached_entrances is None: @@ -1431,8 +1424,6 @@ class Spoiler(): outfile.write('Entrance Shuffle: %s\n' % self.world.shuffle[player]) if self.world.shuffle[player] != "vanilla": outfile.write('Entrance Shuffle Seed %s\n' % self.world.worlds[player].er_seed) - outfile.write('Pyramid hole pre-opened: %s\n' % ( - 'Yes' if self.world.open_pyramid[player] else 'No')) outfile.write('Shop inventory shuffle: %s\n' % bool_to_text("i" in self.world.shop_shuffle[player])) outfile.write('Shop price shuffle: %s\n' % diff --git a/Generate.py b/Generate.py index b46c730c9a..125fab4163 100644 --- a/Generate.py +++ b/Generate.py @@ -583,9 +583,6 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options): ret.goal = goals[goal] - # TODO consider moving open_pyramid to an automatic variable in the core roller, set to True when - # fast ganon + ganon at hole - ret.open_pyramid = get_choice_legacy('open_pyramid', weights, 'goal') extra_pieces = get_choice_legacy('triforce_pieces_mode', weights, 'available') diff --git a/Main.py b/Main.py index acbb4ad5cf..3912e65c99 100644 --- a/Main.py +++ b/Main.py @@ -47,7 +47,6 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No world.item_functionality = args.item_functionality.copy() world.timer = args.timer.copy() world.goal = args.goal.copy() - world.open_pyramid = args.open_pyramid.copy() world.boss_shuffle = args.shufflebosses.copy() world.enemy_health = args.enemy_health.copy() world.enemy_damage = args.enemy_damage.copy() @@ -364,7 +363,8 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No for location in world.get_filled_locations(): if type(location.address) == int: assert location.item.code is not None, "item code None should be event, " \ - "location.address should then also be None" + "location.address should then also be None. Location: " \ + f" {location}" locations_data[location.player][location.address] = \ location.item.code, location.item.player, location.item.flags if location.name in world.start_location_hints[location.player]: diff --git a/MultiServer.py b/MultiServer.py index 06f9a9f9cd..e8e1cc8d4c 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -720,16 +720,16 @@ def get_players_string(ctx: Context): return f'{len(auth_clients)} players of {total} connected ' + text[:-1] -def get_status_string(ctx: Context, team: int): - text = "Player Status on your team:" +def get_status_string(ctx: Context, team: int, tag: str): + text = f"Player Status on team {team}:" for slot in ctx.locations: connected = len(ctx.clients[team][slot]) - death_link = len([client for client in ctx.clients[team][slot] if "DeathLink" in client.tags]) + tagged = len([client for client in ctx.clients[team][slot] if tag in client.tags]) completion_text = f"({len(ctx.location_checks[team, slot])}/{len(ctx.locations[slot])})" - death_text = f" {death_link} of which are death link" if connected else "" + tag_text = f" {tagged} of which are tagged {tag}" if connected and tag else "" goal_text = " and has finished." if ctx.client_game_state[team, slot] == ClientStatus.CLIENT_GOAL else "." text += f"\n{ctx.get_aliased_name(team, slot)} has {connected} connection{'' if connected == 1 else 's'}" \ - f"{death_text}{goal_text} {completion_text}" + f"{tag_text}{goal_text} {completion_text}" return text @@ -1113,9 +1113,11 @@ class ClientMessageProcessor(CommonCommandProcessor): self.output(get_players_string(self.ctx)) return True - def _cmd_status(self) -> bool: - """Get status information about your team.""" - self.output(get_status_string(self.ctx, self.client.team)) + def _cmd_status(self, tag:str="") -> bool: + """Get status information about your team. + Optionally mention a Tag name and get information on who has that Tag. + For example: DeathLink or EnergyLink.""" + self.output(get_status_string(self.ctx, self.client.team, tag)) return True def _cmd_release(self) -> bool: @@ -1306,6 +1308,8 @@ class ClientMessageProcessor(CommonCommandProcessor): can_pay = 1000 self.ctx.random.shuffle(not_found_hints) + # By popular vote, make hints prefer non-local placements + not_found_hints.sort(key=lambda hint: int(hint.receiving_player != hint.finding_player)) hints = found_hints while can_pay > 0: @@ -1657,6 +1661,14 @@ class ServerCommandProcessor(CommonCommandProcessor): self.output(get_players_string(self.ctx)) return True + def _cmd_status(self, tag: str = "") -> bool: + """Get status information about teams. + Optionally mention a Tag name and get information on who has that Tag. + For example: DeathLink or EnergyLink.""" + for team in self.ctx.clients: + self.output(get_status_string(self.ctx, team, tag)) + return True + def _cmd_exit(self) -> bool: """Shutdown the server""" asyncio.create_task(self.ctx.server.ws_server._close()) diff --git a/SNIClient.py b/SNIClient.py index e313feff00..151a68da11 100644 --- a/SNIClient.py +++ b/SNIClient.py @@ -62,7 +62,7 @@ class SNIClientCommandProcessor(ClientCommandProcessor): def _cmd_snes(self, snes_options: str = "") -> bool: """Connect to a snes. Optionally include network address of a snes to connect to, otherwise show available devices; and a SNES device number if more than one SNES is detected. - Examples: "/snes", "/snes 1", "/snes localhost:8080 1" """ + Examples: "/snes", "/snes 1", "/snes localhost:23074 1" """ snes_address = self.ctx.snes_address snes_device_number = -1 @@ -1296,7 +1296,7 @@ async def main(): parser = get_base_parser() parser.add_argument('diff_file', default="", type=str, nargs="?", help='Path to a Archipelago Binary Patch file') - parser.add_argument('--snes', default='localhost:8080', help='Address of the SNI server.') + parser.add_argument('--snes', default='localhost:23074', help='Address of the SNI server.') parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical']) args = parser.parse_args() diff --git a/Starcraft2Client.py b/Starcraft2Client.py index f9b6b43fe3..e9e06335ac 100644 --- a/Starcraft2Client.py +++ b/Starcraft2Client.py @@ -19,7 +19,13 @@ from worlds.sc2wol.Items import lookup_id_to_name, item_table from worlds.sc2wol.Locations import SC2WOL_LOC_ID_OFFSET from worlds.sc2wol import SC2WoLWorld -from Utils import init_logging +from pathlib import Path +import re +from MultiServer import mark_raw +import ctypes +import sys + +from Utils import init_logging, is_windows if __name__ == "__main__": init_logging("SC2Client", exception_logger="Client") @@ -73,6 +79,17 @@ class StarcraftClientProcessor(ClientCommandProcessor): request_unfinished_missions(self.ctx.checked_locations, self.ctx.mission_req_table, self.ctx.ui, self.ctx) return True + @mark_raw + def _cmd_set_path(self, path: str = '') -> bool: + """Manually set the SC2 install directory (if the automatic detection fails).""" + if path: + os.environ["SC2PATH"] = path + check_mod_install() + return True + else: + sc2_logger.warning("When using set_path, you must type the path to your SC2 install directory.") + return False + class SC2Context(CommonContext): command_processor = StarcraftClientProcessor @@ -111,6 +128,11 @@ class SC2Context(CommonContext): for mission in slot_req_table: self.mission_req_table[mission] = MissionInfo(**slot_req_table[mission]) + # Look for and set SC2PATH. + # check_game_install_path() returns True if and only if it finds + sets SC2PATH. + if "SC2PATH" not in os.environ and check_game_install_path(): + check_mod_install() + if cmd in {"PrintJSON"}: if "receiving" in args: if self.slot_concerns_self(args["receiving"]): @@ -415,8 +437,9 @@ async def starcraft_launch(ctx: SC2Context, mission_id): sc2_logger.info(f"Launching {lookup_id_to_mission[mission_id]}. If game does not launch check log file for errors.") - run_game(sc2.maps.get(maps_table[mission_id - 1]), [Bot(Race.Terran, ArchipelagoBot(ctx, mission_id), - name="Archipelago", fullscreen=True)], realtime=True) + with DllDirectory(None): + run_game(sc2.maps.get(maps_table[mission_id - 1]), [Bot(Race.Terran, ArchipelagoBot(ctx, mission_id), + name="Archipelago", fullscreen=True)], realtime=True) class ArchipelagoBot(sc2.bot_ai.BotAI): @@ -796,6 +819,101 @@ def initialize_blank_mission_dict(location_table): return unlocks +def check_game_install_path() -> bool: + # First thing: go to the default location for ExecuteInfo. + # An exception for Windows is included because it's very difficult to find ~\Documents if the user moved it. + if is_windows: + # The next five lines of utterly inscrutable code are brought to you by copy-paste from Stack Overflow. + # https://stackoverflow.com/questions/6227590/finding-the-users-my-documents-path/30924555# + import ctypes.wintypes + CSIDL_PERSONAL = 5 # My Documents + SHGFP_TYPE_CURRENT = 0 # Get current, not default value + + buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) + ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf) + documentspath = buf.value + einfo = str(documentspath / Path("StarCraft II\\ExecuteInfo.txt")) + else: + einfo = str(sc2.paths.get_home() / Path(sc2.paths.USERPATH[sc2.paths.PF])) + + # Check if the file exists. + if os.path.isfile(einfo): + + # Open the file and read it, picking out the latest executable's path. + with open(einfo) as f: + content = f.read() + if content: + base = re.search(r" = (.*)Versions", content).group(1) + if os.path.exists(base): + executable = sc2.paths.latest_executeble(Path(base).expanduser() / "Versions") + + # Finally, check the path for an actual executable. + # If we find one, great. Set up the SC2PATH. + if os.path.isfile(executable): + sc2_logger.info(f"Found an SC2 install at {base}!") + sc2_logger.debug(f"Latest executable at {executable}.") + os.environ["SC2PATH"] = base + sc2_logger.debug(f"SC2PATH set to {base}.") + return True + else: + sc2_logger.warning(f"We may have found an SC2 install at {base}, but couldn't find {executable}.") + else: + sc2_logger.warning(f"{einfo} pointed to {base}, but we could not find an SC2 install there.") + else: + sc2_logger.warning(f"Couldn't find {einfo}. Please run /set_path with your SC2 install directory.") + return False + + +def check_mod_install() -> bool: + # Pull up the SC2PATH if set. If not, encourage the user to manually run /set_path. + try: + # Check inside the Mods folder for Archipelago.SC2Mod. If found, tell user. If not, tell user. + if os.path.isfile(modfile := (os.environ["SC2PATH"] / Path("Mods") / Path("Archipelago.SC2Mod"))): + sc2_logger.info(f"Archipelago mod found at {modfile}.") + return True + else: + sc2_logger.warning(f"Archipelago mod could not be found at {modfile}. Please install the mod file there.") + except KeyError: + sc2_logger.warning(f"SC2PATH isn't set. Please run /set_path with the path to your SC2 install.") + return False + + +class DllDirectory: + # Credit to Black Sliver for this code. + # More info: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw + _old: typing.Optional[str] = None + _new: typing.Optional[str] = None + + def __init__(self, new: typing.Optional[str]): + self._new = new + + def __enter__(self): + old = self.get() + if self.set(self._new): + self._old = old + + def __exit__(self, *args): + if self._old is not None: + self.set(self._old) + + @staticmethod + def get() -> str: + if sys.platform == "win32": + n = ctypes.windll.kernel32.GetDllDirectoryW(0, None) + buf = ctypes.create_unicode_buffer(n) + ctypes.windll.kernel32.GetDllDirectoryW(n, buf) + return buf.value + # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific + return None + + @staticmethod + def set(s: typing.Optional[str]) -> bool: + if sys.platform == "win32": + return ctypes.windll.kernel32.SetDllDirectoryW(s) != 0 + # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific + return False + + if __name__ == '__main__': colorama.init() asyncio.run(main()) diff --git a/WebHostLib/autolauncher.py b/WebHostLib/autolauncher.py index 9d7b7f4959..6f978211fb 100644 --- a/WebHostLib/autolauncher.py +++ b/WebHostLib/autolauncher.py @@ -154,8 +154,10 @@ def autogen(config: dict): while 1: time.sleep(0.1) with db_session: + # for update locks the database row(s) during transaction, preventing writes from elsewhere to_start = select( - generation for generation in Generation if generation.state == STATE_QUEUED) + generation for generation in Generation + if generation.state == STATE_QUEUED).for_update() for generation in to_start: launch_generator(generator_pool, generation) except AlreadyRunningException: diff --git a/WebHostLib/generate.py b/WebHostLib/generate.py index c33d2648a7..15067e131b 100644 --- a/WebHostLib/generate.py +++ b/WebHostLib/generate.py @@ -4,7 +4,7 @@ import random import json import zipfile from collections import Counter -from typing import Dict, Optional as TypeOptional +from typing import Dict, Optional, Any from Utils import __version__ from flask import request, flash, redirect, url_for, session, render_template @@ -15,7 +15,7 @@ from BaseClasses import seeddigits, get_seed from Generate import handle_name, PlandoSettings import pickle -from .models import * +from .models import Generation, STATE_ERROR, STATE_QUEUED, commit, db_session, Seed, UUID from WebHostLib import app from .check import get_yaml_data, roll_options from .upload import upload_zip_to_db @@ -30,16 +30,15 @@ def get_meta(options_source: dict) -> dict: } plando_options -= {""} - meta = { + server_options = { "hint_cost": int(options_source.get("hint_cost", 10)), "forfeit_mode": options_source.get("forfeit_mode", "goal"), "remaining_mode": options_source.get("remaining_mode", "disabled"), "collect_mode": options_source.get("collect_mode", "disabled"), "item_cheat": bool(int(options_source.get("item_cheat", 1))), "server_password": options_source.get("server_password", None), - "plando_options": list(plando_options) } - return meta + return {"server_options": server_options, "plando_options": list(plando_options)} @app.route('/generate', methods=['GET', 'POST']) @@ -60,13 +59,13 @@ def generate(race=False): results, gen_options = roll_options(options, meta["plando_options"]) if race: - meta["item_cheat"] = False - meta["remaining_mode"] = "disabled" + meta["server_options"]["item_cheat"] = False + meta["server_options"]["remaining_mode"] = "disabled" if any(type(result) == str for result in results.values()): return render_template("checkResult.html", results=results) elif len(gen_options) > app.config["MAX_ROLL"]: - flash(f"Sorry, generating of multiworlds is limited to {app.config['MAX_ROLL']} players for now. " + flash(f"Sorry, generating of multiworlds is limited to {app.config['MAX_ROLL']} players. " f"If you have a larger group, please generate it yourself and upload it.") elif len(gen_options) >= app.config["JOB_THRESHOLD"]: gen = Generation( @@ -92,23 +91,22 @@ def generate(race=False): return render_template("generate.html", race=race, version=__version__) -def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=None, sid=None): +def gen_game(gen_options, meta: Optional[Dict[str, Any]] = None, owner=None, sid=None): if not meta: - meta: Dict[str, object] = {} + meta: Dict[str, Any] = {} + + meta.setdefault("server_options", {}).setdefault("hint_cost", 10) + race = meta.setdefault("race", False) - meta.setdefault("hint_cost", 10) - race = meta.get("race", False) - del (meta["race"]) - plando_options = meta.get("plando", {"bosses", "items", "connections", "texts"}) - del (meta["plando_options"]) try: target = tempfile.TemporaryDirectory() playercount = len(gen_options) seed = get_seed() - random.seed(seed) if race: - random.seed() # reset to time-based random source + random.seed() # use time-based random source + else: + random.seed(seed) seedname = "W" + (f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits)) @@ -120,7 +118,8 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No erargs.outputname = seedname erargs.outputpath = target.name erargs.teams = 1 - erargs.plando_options = PlandoSettings.from_set(plando_options) + erargs.plando_options = PlandoSettings.from_set(meta.setdefault("plando_options", + {"bosses", "items", "connections", "texts"})) name_counter = Counter() for player, (playerfile, settings) in enumerate(gen_options.items(), 1): @@ -136,7 +135,7 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No erargs.name[player] = handle_name(erargs.name[player], player, name_counter) if len(set(erargs.name.values())) != len(erargs.name): raise Exception(f"Names have to be unique. Names: {Counter(erargs.name.values())}") - ERmain(erargs, seed, baked_server_options=meta) + ERmain(erargs, seed, baked_server_options=meta["server_options"]) return upload_to_db(target.name, sid, owner, race) except BaseException as e: @@ -148,7 +147,6 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No meta = json.loads(gen.meta) meta["error"] = (e.__class__.__name__ + ": " + str(e)) gen.meta = json.dumps(meta) - commit() raise diff --git a/WebHostLib/models.py b/WebHostLib/models.py index 3d6de6812c..70f0318f85 100644 --- a/WebHostLib/models.py +++ b/WebHostLib/models.py @@ -27,7 +27,7 @@ class Room(db.Entity): seed = Required('Seed', index=True) multisave = Optional(buffer, lazy=True) show_spoiler = Required(int, default=0) # 0 -> never, 1 -> after completion, -> 2 always - timeout = Required(int, default=lambda: 6 * 60 * 60) # seconds since last activity to shutdown + timeout = Required(int, default=lambda: 2 * 60 * 60) # seconds since last activity to shutdown tracker = Optional(UUID, index=True) last_port = Optional(int, default=lambda: 0) diff --git a/WebHostLib/options.py b/WebHostLib/options.py index 203f223561..2cab7728da 100644 --- a/WebHostLib/options.py +++ b/WebHostLib/options.py @@ -60,7 +60,7 @@ def create(): for game_name, world in AutoWorldRegister.world_types.items(): - all_options = {**world.options, **Options.per_game_common_options} + all_options = {**Options.per_game_common_options, **world.options} res = Template(open(os.path.join("WebHostLib", "templates", "options.yaml")).read()).render( options=all_options, __version__=__version__, game=game_name, yaml_dump=yaml.dump, diff --git a/WebHostLib/templates/hostRoom.html b/WebHostLib/templates/hostRoom.html index b5ec01f256..15429e7f8d 100644 --- a/WebHostLib/templates/hostRoom.html +++ b/WebHostLib/templates/hostRoom.html @@ -16,9 +16,9 @@ This room has a Multiworld Tracker enabled.
{% endif %} - This room will be closed after {{ room.timeout//60//60 }} hours of inactivity. Should you wish to continue - later, - you can simply refresh this page and the server will be started again.
+ The server for this room will be paused after {{ room.timeout//60//60 }} hours of inactivity. + Should you wish to continue later, + anyone can simply refresh this page and the server will resume.
{% if room.last_port %} You can connect to this room by using diff --git a/docs/style.md b/docs/style.md new file mode 100644 index 0000000000..a9f55caa7c --- /dev/null +++ b/docs/style.md @@ -0,0 +1,49 @@ +# Style Guide + +## Generic + +* This guide can be ignored for data files that are not to be viewed in an editor. +* 120 character per line for all source files. +* Avoid white space errors like trailing spaces. + + +## Python Code + +* We mostly follow [PEP8](https://peps.python.org/pep-0008/). Read below to see the differences. +* 120 characters per line. PyCharm does this automatically, other editors can be configured for it. +* Strings in core code will be `"strings"`. In other words: double quote your strings. +* Strings in worlds should use double quotes as well, but imported code may differ. +* Prefer [format string literals](https://peps.python.org/pep-0498/) over string concatenation, + use single quotes inside them: `f"Like {dct['key']}"` +* Use type annotation where possible. + + +## Markdown + +* We almost follow [Google's styleguide](https://google.github.io/styleguide/docguide/style.html). + Read below for differences. +* For existing documents, try to follow its style or ask to completely reformat it. +* 120 characters per line. +* One space between bullet/number and text. +* No lazy numbering. + + +## HTML + +* Indent with 2 spaces for new code. +* kebab-case for ids and classes. + + +## CSS + +* Indent with 2 spaces for new code. +* `{` on the same line as the selector. +* No space between selector and `{`. + + +## JS + +* Indent with 2 spaces. +* Indent `case` inside `switch ` with 2 spaces. +* Use single quotes. +* Semicolons are required after every statement. diff --git a/docs/world api.md b/docs/world api.md index a1138c9e16..4fa81f4aab 100644 --- a/docs/world api.md +++ b/docs/world api.md @@ -236,7 +236,7 @@ class MyGameLocation(Location): game: str = "My Game" # override constructor to automatically mark event locations as such - def __init__(self, player: int, name = '', code = None, parent = None): + def __init__(self, player: int, name = "", code = None, parent = None): super(MyGameLocation, self).__init__(player, name, code, parent) self.event = code is None ``` @@ -487,14 +487,14 @@ def create_items(self) -> None: for item in map(self.create_item, mygame_items): if item in exclude: exclude.remove(item) # this is destructive. create unique list above - self.world.itempool.append(self.create_item('nothing')) + self.world.itempool.append(self.create_item("nothing")) else: self.world.itempool.append(item) # itempool and number of locations should match up. # If this is not the case we want to fill the itempool with junk. junk = 0 # calculate this based on player settings - self.world.itempool += [self.create_item('nothing') for _ in range(junk)] + self.world.itempool += [self.create_item("nothing") for _ in range(junk)] ``` #### create_regions @@ -628,7 +628,7 @@ class MyGameLogic(LogicMixin): def _mygame_has_key(self, world: MultiWorld, player: int): # Arguments above are free to choose # it may make sense to use World as argument instead of MultiWorld - return self.has('key', player) # or whatever + return self.has("key", player) # or whatever ``` ```python # __init__.py diff --git a/inno_setup.iss b/inno_setup.iss index 1dee01af18..1005cadad0 100644 --- a/inno_setup.iss +++ b/inno_setup.iss @@ -129,6 +129,7 @@ Type: dirifempty; Name: "{app}" [InstallDelete] Type: files; Name: "{app}\ArchipelagoLttPClient.exe" +Type: filesandordirs; Name: "{app}\lib\worlds\rogue-legacy*" [Registry] diff --git a/playerSettings.yaml b/playerSettings.yaml index ff3596a77a..4ebae9e6d7 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -175,12 +175,15 @@ A Link to the Past: retro_caves: on: 0 # Zelda-1 like mode. There are randomly placed take-any caves that contain one Sword and choices of Heart Container/Blue Potion. off: 50 - hints: # Vendors: King Zora and Bottle Merchant say what they're selling. - # On/Full: Put item and entrance placement hints on telepathic tiles and some NPCs, Full removes joke hints. + hints: # On/Full: Put item and entrance placement hints on telepathic tiles and some NPCs, Full removes joke hints. 'on': 50 - vendors: 0 'off': 0 full: 0 + scams: # If on, these Merchants will no longer tell you what they're selling. + 'off': 50 + 'king_zora': 0 + 'bottle_merchant': 0 + 'all': 0 swordless: on: 0 # Your swords are replaced by rupees. Gameplay changes have been made to accommodate this change off: 1 @@ -273,6 +276,7 @@ A Link to the Past: p: 0 # Randomize the prices of the items in shop inventories u: 0 # Shuffle capacity upgrades into the item pool (and allow them to traverse the multiworld) w: 0 # Consider witch's hut like any other shop and shuffle/randomize it too + P: 0 # Prices of the items in shop inventories cost hearts, arrow, or bombs instead of rupees ip: 0 # Shuffle inventories and randomize prices fpu: 0 # Generate new inventories, randomize prices and shuffle capacity upgrades into item pool uip: 0 # Shuffle inventories, randomize prices and shuffle capacity upgrades into the item pool diff --git a/worlds/alttp/Options.py b/worlds/alttp/Options.py index d7f9becbfd..b42a5eb377 100644 --- a/worlds/alttp/Options.py +++ b/worlds/alttp/Options.py @@ -1,5 +1,6 @@ import typing +from BaseClasses import MultiWorld from Options import Choice, Range, Option, Toggle, DefaultOnToggle, DeathLink @@ -27,6 +28,35 @@ class Goal(Choice): option_hand_in = 2 +class OpenPyramid(Choice): + """Determines whether the hole at the top of pyramid is open. + Goal will open the pyramid if the goal requires you to kill Ganon, without needing to kill Agahnim 2. + Auto is the same as goal except if Ganon's dropdown is in another location, the hole will be closed.""" + display_name = "Open Pyramid Hole" + option_closed = 0 + option_open = 1 + option_goal = 2 + option_auto = 3 + default = option_goal + + alias_true = option_open + alias_false = option_closed + alias_yes = option_open + alias_no = option_closed + + def to_bool(self, world: MultiWorld, player: int) -> bool: + if self.value == self.option_goal: + return world.goal[player] in {'crystals', 'ganontriforcehunt', 'localganontriforcehunt', 'ganonpedestal'} + elif self.value == self.option_auto: + return world.goal[player] in {'crystals', 'ganontriforcehunt', 'localganontriforcehunt', 'ganonpedestal'} \ + and (world.shuffle[player] in {'vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed'} or not + world.shuffle_ganon) + elif self.value == self.option_open: + return True + else: + return False + + class DungeonItem(Choice): value: int option_original_dungeon = 0 @@ -331,6 +361,7 @@ class AllowCollect(Toggle): alttp_options: typing.Dict[str, type(Option)] = { "crystals_needed_for_gt": CrystalsTower, "crystals_needed_for_ganon": CrystalsGanon, + "open_pyramid": OpenPyramid, "bigkey_shuffle": bigkey_shuffle, "smallkey_shuffle": smallkey_shuffle, "compass_shuffle": compass_shuffle, diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index 72cd1ceac5..c16bbf5322 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -1247,7 +1247,7 @@ def patch_rom(world, rom, player, enemized): rom.write_bytes(0x50563, [0x3F, 0x14]) # disable below ganon chest rom.write_byte(0x50599, 0x00) # disable below ganon chest rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest - rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player] else 0x00) # pre-open Pyramid Hole + rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player].to_bool(world, player) else 0x00) # pre-open Pyramid Hole rom.write_byte(0x18008C, 0x01 if world.crystals_needed_for_gt[ player] == 0 else 0x00) # GT pre-opened if crystal requirement is 0 rom.write_byte(0xF5D73, 0xF0) # bees are catchable diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index b33499d6d1..1ba5e9f941 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -402,17 +402,6 @@ class ALTTPWorld(World): def create_regions(self): player = self.player world = self.world - if world.open_pyramid[player] == 'goal': - world.open_pyramid[player] = world.goal[player] in {'crystals', 'ganontriforcehunt', - 'localganontriforcehunt', 'ganonpedestal'} - elif world.open_pyramid[player] == 'auto': - world.open_pyramid[player] = world.goal[player] in {'crystals', 'ganontriforcehunt', - 'localganontriforcehunt', 'ganonpedestal'} and \ - (world.shuffle[player] in {'vanilla', 'dungeonssimple', 'dungeonsfull', - 'dungeonscrossed'} or not world.shuffle_ganon) - else: - world.open_pyramid[player] = {'on': True, 'off': False, 'yes': True, 'no': False}.get( - world.open_pyramid[player], 'auto') world.triforce_pieces_available[player] = max(world.triforce_pieces_available[player], world.triforce_pieces_required[player]) diff --git a/worlds/factorio/requirements.txt b/worlds/factorio/requirements.txt index ce5a83049a..00d9d20af1 100644 --- a/worlds/factorio/requirements.txt +++ b/worlds/factorio/requirements.txt @@ -1 +1 @@ -factorio-rcon-py>=1.2.1 +factorio-rcon-py==1.2.1 diff --git a/worlds/ff1/__init__.py b/worlds/ff1/__init__.py index 761d9fbbe4..9818bed974 100644 --- a/worlds/ff1/__init__.py +++ b/worlds/ff1/__init__.py @@ -1,5 +1,5 @@ from typing import Dict -from BaseClasses import Item, Location, MultiWorld, Tutorial +from BaseClasses import Item, Location, MultiWorld, Tutorial, ItemClassification from .Items import ItemData, FF1Items, FF1_STARTER_ITEMS, FF1_PROGRESSION_LIST, FF1_BRIDGE from .Locations import EventId, FF1Locations, generate_rule, CHAOS_TERMINATED_EVENT from .Options import ff1_options @@ -55,7 +55,7 @@ class FF1World(World): rules = get_options(self.world, 'rules', self.player) menu_region = self.ff1_locations.create_menu_region(self.player, locations, rules) terminated_event = Location(self.player, CHAOS_TERMINATED_EVENT, EventId, menu_region) - terminated_item = Item(CHAOS_TERMINATED_EVENT, True, EventId, self.player) + terminated_item = Item(CHAOS_TERMINATED_EVENT, ItemClassification.progression, EventId, self.player) terminated_event.place_locked_item(terminated_item) items = get_options(self.world, 'items', self.player) @@ -114,5 +114,6 @@ class FF1World(World): def get_filler_item_name(self) -> str: return self.world.random.choice(["Heal", "Pure", "Soft", "Tent", "Cabin", "House"]) + def get_options(world: MultiWorld, name: str, player: int): return getattr(world, name, None)[player].value diff --git a/worlds/oot/ItemPool.py b/worlds/oot/ItemPool.py index 24dda8e24f..301c502a7e 100644 --- a/worlds/oot/ItemPool.py +++ b/worlds/oot/ItemPool.py @@ -1088,10 +1088,10 @@ def get_pool_core(world): placed_items['Hideout Jail Guard (4 Torches)'] = 'Recovery Heart' skip_in_spoiler_locations.extend(['Hideout Jail Guard (2 Torches)', 'Hideout Jail Guard (3 Torches)', 'Hideout Jail Guard (4 Torches)']) else: - placed_items['Hideout Jail Guard (1 Torch)'] = 'Small Key (Gerudo Fortress)' - placed_items['Hideout Jail Guard (2 Torches)'] = 'Small Key (Gerudo Fortress)' - placed_items['Hideout Jail Guard (3 Torches)'] = 'Small Key (Gerudo Fortress)' - placed_items['Hideout Jail Guard (4 Torches)'] = 'Small Key (Gerudo Fortress)' + placed_items['Hideout Jail Guard (1 Torch)'] = 'Small Key (Thieves Hideout)' + placed_items['Hideout Jail Guard (2 Torches)'] = 'Small Key (Thieves Hideout)' + placed_items['Hideout Jail Guard (3 Torches)'] = 'Small Key (Thieves Hideout)' + placed_items['Hideout Jail Guard (4 Torches)'] = 'Small Key (Thieves Hideout)' if world.shuffle_gerudo_card and world.gerudo_fortress != 'open': pool.append('Gerudo Membership Card') diff --git a/worlds/ror2/Options.py b/worlds/ror2/Options.py index d747f3801c..727d01ffaa 100644 --- a/worlds/ror2/Options.py +++ b/worlds/ror2/Options.py @@ -6,7 +6,7 @@ class TotalLocations(Range): """Number of location checks which are added to the Risk of Rain playthrough.""" display_name = "Total Locations" range_start = 10 - range_end = 500 + range_end = 250 default = 20 @@ -36,10 +36,14 @@ class AllowLunarItems(DefaultOnToggle): class StartWithRevive(DefaultOnToggle): """Start the game with a `Dio's Best Friend` item.""" display_name = "Start with a Revive" + +class FinalStageDeath(DefaultOnToggle): + """Death on the final boss stage counts as a win.""" + display_name = "Final Stage Death is Win" class GreenScrap(Range): - """Weight of Green Scraps in the item pool.""" + """Weight of Green Scraps in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "Green Scraps" range_start = 0 range_end = 100 @@ -47,7 +51,7 @@ class GreenScrap(Range): class RedScrap(Range): - """Weight of Red Scraps in the item pool.""" + """Weight of Red Scraps in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "Red Scraps" range_start = 0 range_end = 100 @@ -55,7 +59,7 @@ class RedScrap(Range): class YellowScrap(Range): - """Weight of yellow scraps in the item pool.""" + """Weight of yellow scraps in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "Yellow Scraps" range_start = 0 range_end = 100 @@ -63,7 +67,7 @@ class YellowScrap(Range): class WhiteScrap(Range): - """Weight of white scraps in the item pool.""" + """Weight of white scraps in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "White Scraps" range_start = 0 range_end = 100 @@ -71,7 +75,7 @@ class WhiteScrap(Range): class CommonItem(Range): - """Weight of common items in the item pool.""" + """Weight of common items in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "Common Items" range_start = 0 range_end = 100 @@ -79,7 +83,7 @@ class CommonItem(Range): class UncommonItem(Range): - """Weight of uncommon items in the item pool.""" + """Weight of uncommon items in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "Uncommon Items" range_start = 0 range_end = 100 @@ -87,7 +91,7 @@ class UncommonItem(Range): class LegendaryItem(Range): - """Weight of legendary items in the item pool.""" + """Weight of legendary items in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "Legendary Items" range_start = 0 range_end = 100 @@ -95,7 +99,7 @@ class LegendaryItem(Range): class BossItem(Range): - """Weight of boss items in the item pool.""" + """Weight of boss items in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "Boss Items" range_start = 0 range_end = 100 @@ -103,7 +107,7 @@ class BossItem(Range): class LunarItem(Range): - """Weight of lunar items in the item pool.""" + """Weight of lunar items in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "Lunar Items" range_start = 0 range_end = 100 @@ -111,7 +115,7 @@ class LunarItem(Range): class Equipment(Range): - """Weight of equipment items in the item pool.""" + """Weight of equipment items in the item pool. (Ignored unless Item Weight Presets is 'No')""" display_name = "Equipment" range_start = 0 range_end = 100 @@ -122,15 +126,16 @@ class ItemPoolPresetToggle(DefaultOnToggle): """Will use the item weight presets when set to true, otherwise will use the custom set item pool weights.""" display_name = "Item Weight Presets" + class ItemWeights(Choice): - """Preset choices for determining the weights of the item pool.
- New is a test for a potential adjustment to the default weights.
- Uncommon puts a large number of uncommon items in the pool.
- Legendary puts a large number of legendary items in the pool.
- Lunartic makes everything a lunar item.
- Chaos generates the pool completely at random with rarer items having a slight cap to prevent this option being too easy.
- No Scraps removes all scrap items from the item pool.
- Even generates the item pool with every item having an even weight.
+ """Preset choices for determining the weights of the item pool. + New is a test for a potential adjustment to the default weights. + Uncommon puts a large number of uncommon items in the pool. + Legendary puts a large number of legendary items in the pool. + Lunartic makes everything a lunar item. + Chaos generates the pool completely at random with rarer items having a slight cap to prevent this option being too easy. + No Scraps removes all scrap items from the item pool. + Even generates the item pool with every item having an even weight. Scraps Only will be only scrap items in the item pool.""" display_name = "Item Weights" option_default = 0 @@ -143,7 +148,8 @@ class ItemWeights(Choice): option_even = 7 option_scraps_only = 8 -#define a dictionary for the weights of the generated item pool. + +# define a dictionary for the weights of the generated item pool. ror2_weights: typing.Dict[str, type(Option)] = { "green_scrap": GreenScrap, "red_scrap": RedScrap, @@ -161,6 +167,7 @@ ror2_options: typing.Dict[str, type(Option)] = { "total_locations": TotalLocations, "total_revivals": TotalRevivals, "start_with_revive": StartWithRevive, + "final_stage_death": FinalStageDeath, "item_pickup_step": ItemPickupStep, "enable_lunar": AllowLunarItems, "item_weights": ItemWeights, diff --git a/worlds/ror2/__init__.py b/worlds/ror2/__init__.py index 06df244b97..1a7060786f 100644 --- a/worlds/ror2/__init__.py +++ b/worlds/ror2/__init__.py @@ -110,15 +110,16 @@ class RiskOfRainWorld(World): "seed": "".join(self.world.slot_seeds[self.player].choice(string.digits) for i in range(16)), "totalLocations": self.world.total_locations[self.player].value, "totalRevivals": self.world.total_revivals[self.player].value, - "startWithDio": self.world.start_with_revive[self.player].value + "startWithDio": self.world.start_with_revive[self.player].value, + "FinalStageDeath": self.world.final_stage_death[self.player].value } def create_item(self, name: str) -> Item: item_id = item_table[name] item = RiskOfRainItem(name, ItemClassification.filler, item_id, self.player) - if name == 'Dio\'s Best Friend': + if name == "Dio's Best Friend": item.classification = ItemClassification.progression - elif name == 'Equipment': + elif name in {"Equipment", "Legendary Item"}: item.classification = ItemClassification.useful return item diff --git a/worlds/ror2/docs/en_Risk of Rain 2.md b/worlds/ror2/docs/en_Risk of Rain 2.md index 92232116cc..a58269a35b 100644 --- a/worlds/ror2/docs/en_Risk of Rain 2.md +++ b/worlds/ror2/docs/en_Risk of Rain 2.md @@ -12,6 +12,32 @@ functionality in which certain chests (made clear via a location check progress multiworld. The items that _would have been_ in those chests will be returned to the Risk of Rain player via grants by other players in other worlds. +## What is the goal of Risk of Rain 2 in Archipelago? + +Just like in the original game, any way to "beat the game or obliterate" counts as a win. By default, if you die while +on a final boss stage, that also counts as a win. (You can turn this off in your player settings.) **You do not need to +complete all the location checks** to win; any item you don't collect is automatically sent out to the multiworld when +you meet your goal. + +If you die before you accomplish your goal, you can start a new run. You will start the run with any items that you +received from other players. Any items that you picked up the "normal" way will be lost. + +Note, you can play Simulacrum mode as part of an Archipelago, but you can't achieve any of the victory conditions in +Simulacrum. So you could, for example, collect most of your items through a Simulacrum run, then finish a normal mode +run while keeping the items you received via the multiworld. + +## Can you play multiplayer? + +Yes! You can have a single multiplayer instance as one world in the multiworld. All the players involved need to have +the Archipelago mod, but only the host needs to configure the Archipelago settings. When someone finds an item for your +world, all the connected players will receive a copy of the item, and the location check bar will increase whenever any +player finds an item in Risk of Rain. + +You cannot have players with different player slots in the same co-op game instance. Only the host's Archipelago +settings apply, so each Risk of Rain 2 player slot in the multiworld needs to be a separate game instance. You could, +for example, have two players trade off hosting and making progress on each other's player slot, but a single co-op +instance can't make progress towards multiple player slots in the multiworld. + ## What Risk of Rain items can appear in other players' worlds? The Risk of Rain items are: @@ -31,13 +57,34 @@ in-game item of that tier will appear in the Risk of Rain player's inventory. If the player already has an equipment item equipped then the _item that was equipped_ will be dropped on the ground and _ the new equipment_ will take it's place. (If you want the old one back, pick it up.) +### How many items are there? + +Since a Risk of Rain 2 run can go on indefinitely, you have to configure how many collectible items (also known as +"checks") the game has for purposes of Archipelago when you set up a multiworld. You can configure anywhere from **10 +to 250** items. The number of items will be randomized between all players, so you may want to adjust the number and +item pickup step based on how many items the other players in the multiworld have. (Around 100 seems to be a good +ballpark if you want to have a similar number of items to most other games.) + +After you have completed the specified number of checks, you won't send anything else to the multiworld. You can +receive up to the specified number of randomized items from the multiworld as the players find them. In either case, +you can continue to collect items as normal in Risk of Rain 2 if you've already found all your location checks. + ## What does another world's item look like in Risk of Rain? When the Risk of Rain player fills up their location check bar then the next spawned item will become an item grant for -another player's world. The item in Risk of Rain will disappear in a poof of smoke and the grant will automatically go -out to the multiworld. +another player's world (or possibly get sent back to yourself). The item in Risk of Rain will disappear in a poof of +smoke and the grant will automatically go out to the multiworld. ## What is the item pickup step? The item pickup step is a YAML setting which allows you to set how many items you need to spawn before the _next_ item that is spawned disappears (in a poof of smoke) and goes out to the multiworld. + +## Is Archipelago compatible with other Risk of Rain 2 mods? + +Mostly, yes. Not every mod will work; in particular, anything that causes items to go directly into your inventory +rather than spawning onto the map will interfere with the way the Archipelago mod works. However, many common mods work +just fine with Archipelago. + +For competitive play, of course, you should only use mods that are agreed-upon by the competitors so that you don't +have an unfair advantage. diff --git a/worlds/sm/__init__.py b/worlds/sm/__init__.py index 29d428abc2..fe1323caec 100644 --- a/worlds/sm/__init__.py +++ b/worlds/sm/__init__.py @@ -7,6 +7,8 @@ import threading import base64 from typing import Set, List, TextIO +from worlds.sm.variaRandomizer.graph.graph_utils import GraphUtils + logger = logging.getLogger("Super Metroid") from .Locations import lookup_name_to_id as locations_lookup_name_to_id @@ -654,11 +656,10 @@ class SMLocation(Location): def can_comeback(self, state: CollectionState, item: Item): randoExec = state.world.worlds[self.player].variaRando.randoExec for key in locationsDict[self.name].AccessFrom.keys(): - if (randoExec.areaGraph.canAccess( state.smbm[self.player], - key, - randoExec.graphSettings.startAP, - state.smbm[self.player].maxDiff, - None)): + if (randoExec.areaGraph.canAccessList( state.smbm[self.player], + key, + [randoExec.graphSettings.startAP, 'Landing Site'] if not GraphUtils.isStandardStart(randoExec.graphSettings.startAP) else ['Landing Site'], + state.smbm[self.player].maxDiff)): return True return False diff --git a/worlds/sm/docs/en_Super Metroid.md b/worlds/sm/docs/en_Super Metroid.md index 44a292f582..941cbf48cf 100644 --- a/worlds/sm/docs/en_Super Metroid.md +++ b/worlds/sm/docs/en_Super Metroid.md @@ -23,7 +23,8 @@ certain items to your own world. ## What does another world's item look like in Super Metroid? -A unique item sprite has been added to the game to represent items belonging to another world. +Two unique item sprites have been added to the game to represent items belonging to another world. Progression items have +a small up arrow on the sprite and non-progression don't. ## When the player receives an item, what happens? diff --git a/worlds/sm/variaRandomizer/graph/graph.py b/worlds/sm/variaRandomizer/graph/graph.py index bcbf138123..6ca7465a7e 100644 --- a/worlds/sm/variaRandomizer/graph/graph.py +++ b/worlds/sm/variaRandomizer/graph/graph.py @@ -367,6 +367,22 @@ class AccessGraph(object): #print("canAccess: {}".format(can)) return can + # test access from an access point to a list of others, given an optional item + def canAccessList(self, smbm, srcAccessPointName, destAccessPointNameList, maxDiff, item=None): + if item is not None: + smbm.addItem(item) + #print("canAccess: item: {}, src: {}, dest: {}".format(item, srcAccessPointName, destAccessPointName)) + destAccessPointList = [self.accessPoints[destAccessPointName] for destAccessPointName in destAccessPointNameList] + srcAccessPoint = self.accessPoints[srcAccessPointName] + availAccessPoints = self.getAvailableAccessPoints(srcAccessPoint, smbm, maxDiff, item) + can = any(ap in availAccessPoints for ap in destAccessPointList) + # if not can: + # self.log.debug("canAccess KO: avail = {}".format([ap.Name for ap in availAccessPoints.keys()])) + if item is not None: + smbm.removeItem(item) + #print("canAccess: {}".format(can)) + return can + # returns a list of AccessPoint instances from srcAccessPointName to destAccessPointName # (not including source ap) # or None if no possible path diff --git a/worlds/sm/variaRandomizer/randomizer.py b/worlds/sm/variaRandomizer/randomizer.py index 0da2b2d042..ebb87c520b 100644 --- a/worlds/sm/variaRandomizer/randomizer.py +++ b/worlds/sm/variaRandomizer/randomizer.py @@ -341,6 +341,8 @@ class VariaRandomizer: if preset == 'custom': PresetLoader.factory(world.custom_preset[player].value).load(self.player) elif preset == 'varia_custom': + if len(world.varia_custom_preset[player].value) == 0: + raise Exception("varia_custom was chosen but varia_custom_preset is missing.") url = 'https://randommetroidsolver.pythonanywhere.com/presetWebService' preset_name = next(iter(world.varia_custom_preset[player].value)) payload = '{{"preset": "{}"}}'.format(preset_name) diff --git a/worlds/sm64ex/Options.py b/worlds/sm64ex/Options.py index 99b3b3ee0b..bddfc3fb31 100644 --- a/worlds/sm64ex/Options.py +++ b/worlds/sm64ex/Options.py @@ -13,9 +13,28 @@ class StrictCannonRequirements(DefaultOnToggle): """If disabled, Stars that expect cannons may have to be acquired without them. Only makes a difference if Buddy Checks are enabled""" display_name = "Strict Cannon Requirements" +class FirstBowserStarDoorCost(Range): + """How many stars are required at the Star Door to Bowser in the Dark World""" + range_start = 0 + range_end = 20 + default = 8 + +class BasementStarDoorCost(Range): + """How many stars are required at the Star Door in the Basement""" + range_start = 0 + range_end = 50 + default = 30 + +class SecondFloorStarDoorCost(Range): + """How many stars are required to access the third floor""" + range_start = 0 + range_end = 50 + default = 50 + class StarsToFinish(Range): """How many stars are required at the infinite stairs""" - range_start = 50 + display_name = "Endless Stairs Stars" + range_start = 0 range_end = 100 default = 70 @@ -43,6 +62,9 @@ sm64_options: typing.Dict[str,type(Option)] = { "EnableCoinStars": EnableCoinStars, "StrictCapRequirements": StrictCapRequirements, "StrictCannonRequirements": StrictCannonRequirements, + "FirstBowserStarDoorCost": FirstBowserStarDoorCost, + "BasementStarDoorCost": BasementStarDoorCost, + "SecondFloorStarDoorCost": SecondFloorStarDoorCost, "StarsToFinish": StarsToFinish, "ExtraStars": ExtraStars, "death_link": DeathLink, diff --git a/worlds/sm64ex/Regions.py b/worlds/sm64ex/Regions.py index 85c08933f5..d9a314dfff 100644 --- a/worlds/sm64ex/Regions.py +++ b/worlds/sm64ex/Regions.py @@ -11,6 +11,8 @@ sm64courses = ["Bob-omb Battlefield", "Whomp's Fortress", "Jolly Roger Bay", "Co "Wet-Dry World", "Tall, Tall Mountain", "Tiny-Huge Island", "Tick Tock Clock", "Rainbow Ride"] +# sm64paintings is list of strings for quick reference for Painting IDs (NOT warp node IDs!) +sm64paintings = ["BOB", "WF", "JRB", "CCM", "BBH", "HMC", "LLL", "SSL", "DDD", "SL", "WDW", "TTM", "THI Tiny", "THI Huge", "TTC", "RR"] def create_regions(world: MultiWorld, player: int): regSS = Region("Menu", RegionType.Generic, "Castle Area", player, world) diff --git a/worlds/sm64ex/Rules.py b/worlds/sm64ex/Rules.py index fcd5619323..6dc6e84964 100644 --- a/worlds/sm64ex/Rules.py +++ b/worlds/sm64ex/Rules.py @@ -1,40 +1,50 @@ from ..generic.Rules import add_rule -from .Regions import connect_regions, sm64courses +from .Regions import connect_regions, sm64courses, sm64paintings def set_rules(world, player: int, area_connections): - courseshuffle = list(range(len(sm64courses))) + entrance_ids = list(range(len(sm64paintings))) + destination_courses = list(range(13)) + [12,13,14] # Two instances of Destination Course THI if world.AreaRandomizer[player]: - world.random.shuffle(courseshuffle) - area_connections.update({index: value for index, value in enumerate(courseshuffle)}) + world.random.shuffle(entrance_ids) + temp_assign = dict(zip(entrance_ids,destination_courses)) # Used for Rules only - connect_regions(world, player, "Menu", sm64courses[area_connections[0]]) - connect_regions(world, player, "Menu", sm64courses[area_connections[1]], lambda state: state.has("Power Star", player, 1)) - connect_regions(world, player, "Menu", sm64courses[area_connections[2]], lambda state: state.has("Power Star", player, 3)) - connect_regions(world, player, "Menu", sm64courses[area_connections[3]], lambda state: state.has("Power Star", player, 3)) - connect_regions(world, player, "Menu", "Bowser in the Dark World", lambda state: state.has("Power Star", player, 8)) - connect_regions(world, player, "Menu", sm64courses[area_connections[4]], lambda state: state.has("Power Star", player, 12)) + # Destination Format: LVL | AREA with LVL = Course ID, 0-indexed, AREA = Area as used in sm64 code + area_connections.update({entrance: (destination_course*10 + 1) for entrance, destination_course in temp_assign.items()}) + for i in range(len(area_connections)): + if (int(area_connections[i]/10) == 12): + # Change first occurence of course 12 (THI) to Area 2 (THI Tiny) + area_connections[i] = 12*10 + 2 + break + + connect_regions(world, player, "Menu", sm64courses[temp_assign[0]]) + connect_regions(world, player, "Menu", sm64courses[temp_assign[1]], lambda state: state.has("Power Star", player, 1)) + connect_regions(world, player, "Menu", sm64courses[temp_assign[2]], lambda state: state.has("Power Star", player, 3)) + connect_regions(world, player, "Menu", sm64courses[temp_assign[3]], lambda state: state.has("Power Star", player, 3)) + connect_regions(world, player, "Menu", "Bowser in the Dark World", lambda state: state.has("Power Star", player, world.FirstBowserStarDoorCost[player].value)) + connect_regions(world, player, "Menu", sm64courses[temp_assign[4]], lambda state: state.has("Power Star", player, 12)) connect_regions(world, player, "Menu", "Basement", lambda state: state.has("Basement Key", player) or state.has("Progressive Key", player, 1)) - connect_regions(world, player, "Basement", sm64courses[area_connections[5]]) - connect_regions(world, player, "Basement", sm64courses[area_connections[6]]) - connect_regions(world, player, "Basement", sm64courses[area_connections[7]]) - connect_regions(world, player, "Basement", sm64courses[area_connections[8]], lambda state: state.has("Power Star", player, 30)) - connect_regions(world, player, "Basement", "Bowser in the Fire Sea", lambda state: state.has("Power Star", player, 30) and + connect_regions(world, player, "Basement", sm64courses[temp_assign[5]]) + connect_regions(world, player, "Basement", sm64courses[temp_assign[6]]) + connect_regions(world, player, "Basement", sm64courses[temp_assign[7]]) + connect_regions(world, player, "Basement", sm64courses[temp_assign[8]], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value)) + connect_regions(world, player, "Basement", "Bowser in the Fire Sea", lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value) and state.can_reach("DDD: Board Bowser's Sub", 'Location', player)) connect_regions(world, player, "Menu", "Second Floor", lambda state: state.has("Second Floor Key", player) or state.has("Progressive Key", player, 2)) - connect_regions(world, player, "Second Floor", sm64courses[area_connections[9]]) - connect_regions(world, player, "Second Floor", sm64courses[area_connections[10]]) - connect_regions(world, player, "Second Floor", sm64courses[area_connections[11]]) - connect_regions(world, player, "Second Floor", sm64courses[area_connections[12]]) + connect_regions(world, player, "Second Floor", sm64courses[temp_assign[9]]) + connect_regions(world, player, "Second Floor", sm64courses[temp_assign[10]]) + connect_regions(world, player, "Second Floor", sm64courses[temp_assign[11]]) + connect_regions(world, player, "Second Floor", sm64courses[temp_assign[12]]) # THI Tiny + connect_regions(world, player, "Second Floor", sm64courses[temp_assign[13]]) # THI Huge - connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, 50)) + connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, world.SecondFloorStarDoorCost[player].value)) - connect_regions(world, player, "Third Floor", sm64courses[area_connections[13]]) - connect_regions(world, player, "Third Floor", sm64courses[area_connections[14]]) + connect_regions(world, player, "Third Floor", sm64courses[temp_assign[14]]) + connect_regions(world, player, "Third Floor", sm64courses[temp_assign[15]]) #Special Rules for some Locations add_rule(world.get_location("Tower of the Wing Cap Switch", player), lambda state: state.has("Power Star", player, 10)) diff --git a/worlds/sm64ex/__init__.py b/worlds/sm64ex/__init__.py index f19f8ee79e..bcf1bf2a8a 100644 --- a/worlds/sm64ex/__init__.py +++ b/worlds/sm64ex/__init__.py @@ -5,13 +5,10 @@ from .Items import item_table, cannon_item_table, SM64Item from .Locations import location_table, SM64Location from .Options import sm64_options from .Rules import set_rules -from .Regions import create_regions, sm64courses +from .Regions import create_regions, sm64courses, sm64paintings from BaseClasses import Item, Tutorial, ItemClassification from ..AutoWorld import World, WebWorld -client_version = 1 - - class SM64Web(WebWorld): tutorials = [Tutorial( "Multiworld Setup Guide", @@ -38,6 +35,8 @@ class SM64World(World): location_name_to_id = location_table data_version = 6 + required_client_version = (0,3,0) + forced_auto_forfeit = False area_connections: typing.Dict[int, int] @@ -55,10 +54,10 @@ class SM64World(World): set_rules(self.world, self.player, self.area_connections) if self.topology_present: # Write area_connections to spoiler log - for painting_id, course_id in self.area_connections.items(): + for painting_id, destination in self.area_connections.items(): self.world.spoiler.set_entrance( - sm64courses[painting_id] + " Painting", - sm64courses[course_id], + sm64paintings[painting_id] + " Painting", + sm64courses[destination // 10], 'entrance', self.player) def create_item(self, name: str) -> Item: @@ -115,6 +114,9 @@ class SM64World(World): def fill_slot_data(self): return { "AreaRando": self.area_connections, + "FirstBowserDoorCost": self.world.FirstBowserStarDoorCost[self.player].value, + "BasementDoorCost": self.world.BasementStarDoorCost[self.player].value, + "SecondFloorCost": self.world.SecondFloorStarDoorCost[self.player].value, "StarsToFinish": self.world.StarsToFinish[self.player].value, "DeathLink": self.world.death_link[self.player].value, } @@ -143,8 +145,8 @@ class SM64World(World): def modify_multidata(self, multidata): if self.topology_present: er_hint_data = {} - for painting_id, course_id in self.area_connections.items(): - region = self.world.get_region(sm64courses[course_id], self.player) + for painting_id, destination in self.area_connections.items(): + region = self.world.get_region(sm64courses[destination // 10], self.player) for location in region.locations: - er_hint_data[location.address] = sm64courses[painting_id] + er_hint_data[location.address] = sm64paintings[painting_id] multidata['er_hint_data'][self.player] = er_hint_data diff --git a/worlds/smz3/TotalSMZ3/Patch.py b/worlds/smz3/TotalSMZ3/Patch.py index 54395714ba..d029e58473 100644 --- a/worlds/smz3/TotalSMZ3/Patch.py +++ b/worlds/smz3/TotalSMZ3/Patch.py @@ -619,6 +619,7 @@ class Patch: if (self.myWorld.Config.Keysanity): self.patches.append((Snes(0x40003B), [ 1 ])) #// MapMode #$00 = Always On (default) - #$01 = Require Map Item self.patches.append((Snes(0x400045), [ 0x0f ])) #// display ----dcba a: Small Keys, b: Big Key, c: Map, d: Compass + self.patches.append((Snes(0x40016A), [ 0x01 ])) #// enable local item dialog boxes for dungeon and keycard items def WriteSMKeyCardDoors(self): if (not self.myWorld.Config.Keysanity): diff --git a/worlds/smz3/TotalSMZ3/Text/Scripts/General.yaml b/worlds/smz3/TotalSMZ3/Text/Scripts/General.yaml index c0eae5bbbe..12b5271eab 100644 --- a/worlds/smz3/TotalSMZ3/Text/Scripts/General.yaml +++ b/worlds/smz3/TotalSMZ3/Text/Scripts/General.yaml @@ -380,6 +380,10 @@ Items: Keycard: |- A key from the future? + + Something: |- + A small victory! + default: |- Don't waste your time! diff --git a/worlds/smz3/__init__.py b/worlds/smz3/__init__.py index 7f05e0dfd5..e440eab2c9 100644 --- a/worlds/smz3/__init__.py +++ b/worlds/smz3/__init__.py @@ -27,14 +27,18 @@ class SMZ3CollectionState(metaclass=AutoLogicRegister): # for unit tests where MultiWorld is instantiated before worlds if hasattr(parent, "state"): self.smz3state = {player: TotalSMZ3Item.Progression([]) for player in parent.get_game_players("SMZ3")} + for player, group in parent.groups.items(): + if (group["game"] == "SMZ3"): + self.smz3state[player] = TotalSMZ3Item.Progression([]) + if player not in parent.state.smz3state: + parent.state.smz3state[player] = TotalSMZ3Item.Progression([]) else: self.smz3state = {} def copy_mixin(self, ret) -> CollectionState: - ret.smz3state = {player: copy.deepcopy(self.smz3state[player]) for player in self.world.get_game_players("SMZ3")} + ret.smz3state = {player: copy.deepcopy(self.smz3state[player]) for player in self.smz3state} return ret - class SMZ3Web(WebWorld): tutorials = [Tutorial( "Multiworld Setup Guide", @@ -106,6 +110,7 @@ class SMZ3World(World): 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): progressionItems = self.progression + self.dungeon + self.keyCardsItems @@ -256,11 +261,11 @@ class SMZ3World(World): base_combined_rom = basepatch.apply(base_combined_rom) patcher = TotalSMZ3Patch(self.smz3World, - [world.smz3World for key, world in self.world.worlds.items() if isinstance(world, SMZ3World)], + [world.smz3World for key, world in self.world.worlds.items() if isinstance(world, SMZ3World) and hasattr(world, "smz3World")], self.world.seed_name, self.world.seed, self.local_random, - self.world.world_name_lookup, + {v: k for k, v in self.world.player_name.items()}, next(iter(loc.player for loc in self.world.get_locations() if (loc.item.name == "SilverArrows" and loc.item.player == self.player)))) patches = patcher.Create(self.smz3World.Config) patches.update(self.apply_sm_custom_sprite()) @@ -312,7 +317,7 @@ class SMZ3World(World): return slot_data def collect(self, state: CollectionState, item: Item) -> bool: - state.smz3state[item.player].Add([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World)]) + state.smz3state[self.player].Add([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World if hasattr(self, "smz3World") else None)]) if item.advancement: state.prog_items[item.name, item.player] += 1 return True # indicate that a logical state change has occured @@ -321,7 +326,7 @@ class SMZ3World(World): def remove(self, state: CollectionState, item: Item) -> bool: name = self.collect_item(state, item, True) if name: - state.smz3state[item.player].Remove([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World)]) + state.smz3state[item.player].Remove([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World if hasattr(self, "smz3World") else None)]) state.prog_items[name, item.player] -= 1 if state.prog_items[name, item.player] < 1: del (state.prog_items[name, item.player]) @@ -330,7 +335,9 @@ class SMZ3World(World): def create_item(self, name: str) -> Item: return SMZ3Item(name, ItemClassification.progression, - TotalSMZ3Item.ItemType[name], self.item_name_to_id[name], player = self.player) + TotalSMZ3Item.ItemType[name], self.item_name_to_id[name], + self.player, + TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[name], self)) def pre_fill(self): from Fill import fill_restrictive @@ -364,6 +371,9 @@ class SMZ3World(World): else: return [] + def get_filler_item_name(self) -> str: + return self.world.random.choice(self.junkItemsNames) + def write_spoiler(self, spoiler_handle: TextIO): self.world.spoiler.unreachables.update(self.unreachable) diff --git a/worlds/smz3/data/zsm.ips b/worlds/smz3/data/zsm.ips index faf7443a57..6faeeaa2fb 100644 Binary files a/worlds/smz3/data/zsm.ips and b/worlds/smz3/data/zsm.ips differ diff --git a/worlds/smz3/docs/en_SMZ3.md b/worlds/smz3/docs/en_SMZ3.md index 91dace7b4d..f0302d12f3 100644 --- a/worlds/smz3/docs/en_SMZ3.md +++ b/worlds/smz3/docs/en_SMZ3.md @@ -23,7 +23,8 @@ certain items to your own world. ## What does another world's item look like in Super Metroid? -A unique item sprite has been added to the game to represent items belonging to another world. +Two unique item sprites have been added to the game to represent items belonging to another world. Progression items have +a small up arrow on the sprite and non-progression don't. ## What does another world's item look like in LttP? diff --git a/worlds/soe/Logic.py b/worlds/soe/Logic.py index f25f2ada1b..d08e6a3e96 100644 --- a/worlds/soe/Logic.py +++ b/worlds/soe/Logic.py @@ -1,5 +1,6 @@ from BaseClasses import MultiWorld from ..AutoWorld import LogicMixin +from .Options import EnergyCore from typing import Set # TODO: Options may preset certain progress steps (i.e. P_ROCK_SKIP), set in generate_early? @@ -8,9 +9,9 @@ from . import pyevermizer # TODO: resolve/flatten/expand rules to get rid of recursion below where possible # Logic.rules are all rules including locations, excluding those with no progress (i.e. locations that only drop items) rules = [rule for rule in pyevermizer.get_logic() if len(rule.provides) > 0] -# Logic.items are all items excluding non-progression items and duplicates +# Logic.items are all items and extra items excluding non-progression items and duplicates item_names: Set[str] = set() -items = [item for item in filter(lambda item: item.progression, pyevermizer.get_items()) +items = [item for item in filter(lambda item: item.progression, pyevermizer.get_items() + pyevermizer.get_extra_items()) if item.name not in item_names and not item_names.add(item.name)] @@ -47,4 +48,9 @@ class SecretOfEvermoreLogic(LogicMixin): """ Returns True if count of one of evermizer's progress steps is reached based on collected items. i.e. 2 * P_DE """ + if progress == pyevermizer.P_ENERGY_CORE: # logic is shared between worlds, so we override in the call + w = world.worlds[player] + if w.energy_core == EnergyCore.option_fragments: + progress = pyevermizer.P_CORE_FRAGMENT + count = w.required_fragments return self._soe_count(progress, world, player, count) >= count diff --git a/worlds/soe/Options.py b/worlds/soe/Options.py index 0c24399ddd..4ec0ce2bcc 100644 --- a/worlds/soe/Options.py +++ b/worlds/soe/Options.py @@ -37,6 +37,32 @@ class Difficulty(EvermizerFlags, Choice): flags = ['e', 'n', 'h', 'x'] +class EnergyCore(EvermizerFlags, Choice): + """How to obtain the Energy Core""" + display_name = "Energy Core" + option_vanilla = 0 + option_shuffle = 1 + option_fragments = 2 + default = 1 + flags = ['z', '', 'Z'] + + +class RequiredFragments(Range): + """Required fragment count for Energy Core = Fragments""" + display_name = "Required Fragments" + range_start = 1 + range_end = 99 + default = 10 + + +class AvailableFragments(Range): + """Placed fragment count for Energy Core = Fragments""" + display_name = "Available Fragments" + range_start = 1 + range_end = 99 + default = 11 + + class MoneyModifier(Range): """Money multiplier in %""" display_name = "Money Modifier" @@ -186,10 +212,15 @@ class TrapChanceOHKO(TrapChance): class SoEProgressionBalancing(ProgressionBalancing): default = 30 + __doc__ = ProgressionBalancing.__doc__.replace(f"default {ProgressionBalancing.default}", f"default {default}") + special_range_names = {**ProgressionBalancing.special_range_names, "normal": default} soe_options: typing.Dict[str, type(Option)] = { "difficulty": Difficulty, + "energy_core": EnergyCore, + "required_fragments": RequiredFragments, + "available_fragments": AvailableFragments, "money_modifier": MoneyModifier, "exp_modifier": ExpModifier, "fix_sequence": FixSequence, diff --git a/worlds/soe/__init__.py b/worlds/soe/__init__.py index f00ebbe143..d708d6d7d3 100644 --- a/worlds/soe/__init__.py +++ b/worlds/soe/__init__.py @@ -16,7 +16,7 @@ except ImportError: from . import pyevermizer # as part of the source tree from . import Logic # load logic mixin -from .Options import soe_options +from .Options import soe_options, EnergyCore, RequiredFragments, AvailableFragments from .Patch import SoEDeltaPatch, get_base_rom_path """ @@ -52,7 +52,6 @@ Item grouping currently supports * Ingredients - Matches all ingredient drops * Alchemy - Matches all alchemy formulas * Weapons - Matches all weapons but Bazooka, Bone Crusher, Neutron Blade -* Bazooka - Matches all bazookas (currently only one) * Traps - Matches all traps """ @@ -63,12 +62,14 @@ _id_offset: typing.Dict[int, int] = { pyevermizer.CHECK_GOURD: _id_base + 100, # gourds 64100..64399 pyevermizer.CHECK_NPC: _id_base + 400, # npc 64400..64499 # TODO: sniff 64500..64799 - pyevermizer.CHECK_TRAP: _id_base + 900, # npc 64900..64999 + pyevermizer.CHECK_EXTRA: _id_base + 800, # extra items 64800..64899 + pyevermizer.CHECK_TRAP: _id_base + 900, # trap 64900..64999 } # cache native evermizer items and locations _items = pyevermizer.get_items() _traps = pyevermizer.get_traps() +_extras = pyevermizer.get_extra_items() # items that are not placed by default _locations = pyevermizer.get_locations() # fix up texts for AP for _loc in _locations: @@ -104,7 +105,7 @@ def _get_location_mapping() -> typing.Tuple[typing.Dict[str, int], typing.Dict[i def _get_item_mapping() -> typing.Tuple[typing.Dict[str, int], typing.Dict[int, pyevermizer.Item]]: name_to_id = {} id_to_raw = {} - for item in itertools.chain(_items, _traps): + for item in itertools.chain(_items, _extras, _traps): if item.name in name_to_id: continue ap_id = _id_offset[item.type] + item.index @@ -127,7 +128,6 @@ def _get_item_grouping() -> typing.Dict[str, typing.Set[str]]: groups['Alchemy'] = set(item.name for item in _items if item.type == pyevermizer.CHECK_ALCHEMY) groups['Weapons'] = {'Spider Claw', 'Horn Spear', 'Gladiator Sword', 'Bronze Axe', 'Bronze Spear', 'Crusader Sword', 'Lance (Weapon)', 'Knight Basher', 'Atom Smasher', 'Laser Lance'} - groups['Bazooka'] = {'Bazooka+Shells / Shining Armor / 5k Gold'} groups['Traps'] = {trap.name for trap in _traps} return groups @@ -136,7 +136,8 @@ class SoEWebWorld(WebWorld): theme = 'jungle' tutorials = [Tutorial( "Multiworld Setup Guide", - "A guide to playing Secret of Evermore randomizer. This guide covers single-player, multiworld and related software.", + "A guide to playing Secret of Evermore randomizer. This guide covers single-player, multiworld and related" + " software.", "English", "multiworld_en.md", "multiworld/en", @@ -153,9 +154,9 @@ class SoEWorld(World): options = soe_options topology_present = False remote_items = False - data_version = 2 + data_version = 3 web = SoEWebWorld() - required_client_version = (0, 2, 6) + required_client_version = (0, 3, 3) item_name_to_id, item_id_to_raw = _get_item_mapping() location_name_to_id, location_id_to_raw = _get_location_mapping() @@ -165,6 +166,9 @@ class SoEWorld(World): evermizer_seed: int connect_name: str + energy_core: int + available_fragments: int + required_fragments: int _halls_ne_chest_names: typing.List[str] = [loc.name for loc in _locations if 'Halls NE' in loc.name] @@ -172,6 +176,14 @@ class SoEWorld(World): self.connect_name_available_event = threading.Event() super(SoEWorld, self).__init__(*args, **kwargs) + def generate_early(self) -> None: + # store option values that change logic + self.energy_core = self.world.energy_core[self.player].value + self.required_fragments = self.world.required_fragments[self.player].value + if self.required_fragments > self.world.available_fragments[self.player].value: + self.world.available_fragments[self.player].value = self.required_fragments + self.available_fragments = self.world.available_fragments[self.player].value + def create_event(self, event: str) -> Item: return SoEItem(event, ItemClassification.progression, None, self.player) @@ -182,6 +194,8 @@ class SoEWorld(World): classification = ItemClassification.trap elif item.progression: classification = ItemClassification.progression + elif item.useful: + classification = ItemClassification.useful else: classification = ItemClassification.filler @@ -208,9 +222,33 @@ class SoEWorld(World): self.world.get_entrance('New Game', self.player).connect(self.world.get_region('Ingame', self.player)) def create_items(self): - # add items to the pool - items = list(map(lambda item: self.create_item(item), _items)) + # add regular items to the pool + exclusions: typing.List[str] = [] + if self.energy_core != EnergyCore.option_shuffle: + exclusions.append("Energy Core") # will be placed in generate_basic or replaced by a fragment below + items = list(map(lambda item: self.create_item(item), (item for item in _items if item.name not in exclusions))) + # remove one pair of wings that will be placed in generate_basic + items.remove(self.create_item("Wings")) + + def is_ingredient(item): + for ingredient in _ingredients: + if _match_item_name(item, ingredient): + return True + return False + + # add energy core fragments to the pool + ingredients = [n for n, item in enumerate(items) if is_ingredient(item)] + if self.energy_core == EnergyCore.option_fragments: + items.append(self.create_item("Energy Core Fragment")) # replaces the vanilla energy core + for _ in range(self.available_fragments - 1): + if len(ingredients) < 1: + break # out of ingredients to replace + r = self.world.random.choice(ingredients) + ingredients.remove(r) + items[r] = self.create_item("Energy Core Fragment") + + # add traps to the pool trap_count = self.world.trap_count[self.player].value trap_chances = {} trap_names = {} @@ -232,13 +270,12 @@ class SoEWorld(World): return self.create_item(trap_names[t]) v -= c - while trap_count > 0: - r = self.world.random.randrange(len(items)) - for ingredient in _ingredients: - if _match_item_name(items[r], ingredient): - items[r] = create_trap() - trap_count -= 1 - break + for _ in range(trap_count): + if len(ingredients) < 1: + break # out of ingredients to replace + r = self.world.random.choice(ingredients) + ingredients.remove(r) + items[r] = create_trap() self.world.itempool += items @@ -271,7 +308,10 @@ class SoEWorld(World): wings_location = self.world.random.choice(self._halls_ne_chest_names) wings_item = self.create_item('Wings') self.world.get_location(wings_location, self.player).place_locked_item(wings_item) - self.world.itempool.remove(wings_item) + # place energy core at vanilla location for vanilla mode + if self.energy_core == EnergyCore.option_vanilla: + energy_core = self.create_item('Energy Core') + self.world.get_location('Energy Core #285', self.player).place_locked_item(energy_core) # generate stuff for later self.evermizer_seed = self.world.random.randint(0, 2 ** 16 - 1) # TODO: make this an option for "full" plando? @@ -286,9 +326,12 @@ class SoEWorld(World): try: money = self.world.money_modifier[self.player].value exp = self.world.exp_modifier[self.player].value - switches = [] + switches: typing.List[str] = [] if self.world.death_link[self.player].value: switches.append("--death-link") + if self.energy_core == EnergyCore.option_fragments: + switches.extend(('--available-fragments', str(self.available_fragments), + '--required-fragments', str(self.required_fragments))) rom_file = get_base_rom_path() out_base = output_path(output_directory, f'AP_{self.world.seed_name}_P{self.player}_' f'{self.world.get_file_safe_player_name(self.player)}') diff --git a/worlds/soe/requirements.txt b/worlds/soe/requirements.txt index 8dfdc0de9f..7f6a11e490 100644 --- a/worlds/soe/requirements.txt +++ b/worlds/soe/requirements.txt @@ -1,14 +1,14 @@ -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp38-cp38-win_amd64.whl#egg=pyevermizer; sys_platform == 'win32' and platform_machine == 'AMD64' and python_version == '3.8' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp39-cp39-win_amd64.whl#egg=pyevermizer; sys_platform == 'win32' and platform_machine == 'AMD64' and python_version == '3.9' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp310-cp310-win_amd64.whl#egg=pyevermizer; sys_platform == 'win32' and platform_machine == 'AMD64' and python_version == '3.10' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.8' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.9' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.10' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.8' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.9' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.10' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp38-cp38-macosx_10_9_x86_64.whl#egg=pyevermizer; sys_platform == 'darwin' and python_version == '3.8' -#https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp39-cp39-macosx_10_9_x86_64.whl#egg=pyevermizer; sys_platform == 'darwin' and python_version == '3.9' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp39-cp39-macosx_10_9_universal2.whl#egg=pyevermizer; sys_platform == 'darwin' and python_version == '3.9' -https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2-cp310-cp310-macosx_10_9_universal2.whl#egg=pyevermizer; sys_platform == 'darwin' and python_version == '3.10' -#https://github.com/black-sliver/pyevermizer/releases/download/v0.41.2/pyevermizer-0.41.2.tar.gz#egg=pyevermizer; python_version == '3.11' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp38-cp38-win_amd64.whl#egg=pyevermizer; sys_platform == 'win32' and platform_machine == 'AMD64' and python_version == '3.8' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp39-cp39-win_amd64.whl#egg=pyevermizer; sys_platform == 'win32' and platform_machine == 'AMD64' and python_version == '3.9' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp310-cp310-win_amd64.whl#egg=pyevermizer; sys_platform == 'win32' and platform_machine == 'AMD64' and python_version == '3.10' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.8' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.9' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.10' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.8' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.9' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl#egg=pyevermizer; sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.10' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp38-cp38-macosx_10_9_x86_64.whl#egg=pyevermizer; sys_platform == 'darwin' and python_version == '3.8' +#https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp39-cp39-macosx_10_9_x86_64.whl#egg=pyevermizer; sys_platform == 'darwin' and python_version == '3.9' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp39-cp39-macosx_10_9_universal2.whl#egg=pyevermizer; sys_platform == 'darwin' and python_version == '3.9' +https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3-cp310-cp310-macosx_10_9_universal2.whl#egg=pyevermizer; sys_platform == 'darwin' and python_version == '3.10' +#https://github.com/black-sliver/pyevermizer/releases/download/v0.41.3/pyevermizer-0.41.3.tar.gz#egg=pyevermizer; python_version == '3.11' diff --git a/worlds/subnautica/Creatures.py b/worlds/subnautica/Creatures.py new file mode 100644 index 0000000000..56e2a7efa1 --- /dev/null +++ b/worlds/subnautica/Creatures.py @@ -0,0 +1,82 @@ +from typing import Dict, Set, List + +# EN Locale Creature Name to rough depth in meters found at +all_creatures: Dict[str, int] = { + "Gasopod": 0, + "Bladderfish": 0, + "Ancient Floater": 0, + "Skyray": 0, + "Garryfish": 0, + "Peeper": 0, + "Shuttlebug": 0, + "Rabbit Ray": 0, + "Stalker": 0, + "Floater": 0, + "Holefish": 0, + "Cave Crawler": 0, + "Hoopfish": 0, + "Crashfish": 0, + "Hoverfish": 0, + "Spadefish": 0, + "Reefback Leviathan": 0, + "Reaper Leviathan": 0, + "Warper": 0, + "Boomerang": 0, + "Biter": 200, + "Sand Shark": 200, + "Bleeder": 200, + "Crabsnake": 300, + "Jellyray": 300, + "Oculus": 300, + "Mesmer": 300, + "Eyeye": 300, + "Reginald": 400, + "Sea Treader Leviathan": 400, + "Crabsquid": 400, + "Ampeel": 400, + "Boneshark": 400, + "Rockgrub": 400, + "Ghost Leviathan": 500, + "Ghost Leviathan Juvenile": 500, + "Spinefish": 600, + "Blighter": 600, + "Blood Crawler": 600, + "Ghostray": 1000, + "Amoeboid": 1000, + "River Prowler": 1000, + "Red Eyeye": 1300, + "Magmarang": 1300, + "Crimson Ray": 1300, + "Lava Larva": 1300, + "Lava Lizard": 1300, + "Sea Dragon Leviathan": 1300, + "Sea Emperor Leviathan": 1700, + "Sea Emperor Juvenile": 1700, + + # "Cuddlefish": 300, # maybe at some point, needs hatching in containment chamber (20 real-life minutes) +} + +# be nice and make these require Stasis Rifle +aggressive: Set[str] = { + "Cave Crawler", # is very easy without Stasis Rifle, but included for consistency + "Crashfish", + "Bleeder", + "Mesmer", + "Reaper Leviathan", + "Crabsquid", + "Warper", + "Crabsnake", + "Ampeel", + "Boneshark", + "Lava Lizard", + "Sea Dragon Leviathan", + "River Prowler", +} + +suffix: str = " Scan" + +creature_locations: Dict[str, int] = { + creature+suffix: creature_id for creature_id, creature in enumerate(all_creatures, start=34000) +} + +all_creatures_presorted: List[str] = sorted(all_creatures) diff --git a/worlds/subnautica/Items.py b/worlds/subnautica/Items.py index b9377b7ae6..f3a6ded5aa 100644 --- a/worlds/subnautica/Items.py +++ b/worlds/subnautica/Items.py @@ -1,23 +1,353 @@ -import json -import os +from BaseClasses import ItemClassification +from typing import TypedDict, Dict, Set -with open(os.path.join(os.path.dirname(__file__), 'items.json'), 'r') as file: - item_table = json.loads(file.read()) -lookup_id_to_name = {} -lookup_name_to_item = {} -advancement_item_names = set() -non_advancement_item_names = set() +class ItemDict(TypedDict): + classification: ItemClassification + count: int + name: str + tech_type: str -for item in item_table: - item_name = item["name"] - lookup_id_to_name[item["id"]] = item_name - lookup_name_to_item[item_name] = item - if item["progression"]: + +item_table: Dict[int, ItemDict] = { + 35000: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Compass', + 'tech_type': 'Compass'}, + 35001: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Lightweight High Capacity Tank', + 'tech_type': 'PlasteelTank'}, + 35002: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Vehicle Upgrade Console', + 'tech_type': 'BaseUpgradeConsole'}, + 35003: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Ultra Glide Fins', + 'tech_type': 'UltraGlideFins'}, + 35004: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Cyclops Sonar Upgrade', + 'tech_type': 'CyclopsSonarModule'}, + 35005: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Reinforced Dive Suit', + 'tech_type': 'ReinforcedDiveSuit'}, + 35006: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Cyclops Thermal Reactor Module', + 'tech_type': 'CyclopsThermalReactorModule'}, + 35007: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Stillsuit', + 'tech_type': 'Stillsuit'}, + 35008: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Alien Containment Fragment', + 'tech_type': 'BaseWaterParkFragment'}, + 35009: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Creature Decoy', + 'tech_type': 'CyclopsDecoy'}, + 35010: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Cyclops Fire Suppression System', + 'tech_type': 'CyclopsFireSuppressionModule'}, + 35011: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Swim Charge Fins', + 'tech_type': 'SwimChargeFins'}, + 35012: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Repulsion Cannon', + 'tech_type': 'RepulsionCannon'}, + 35013: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Cyclops Decoy Tube Upgrade', + 'tech_type': 'CyclopsDecoyModule'}, + 35014: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Cyclops Shield Generator', + 'tech_type': 'CyclopsShieldModule'}, + 35015: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Cyclops Depth Module MK1', + 'tech_type': 'CyclopsHullModule1'}, + 35016: {'classification': ItemClassification.useful, + 'count': 1, + 'name': 'Cyclops Docking Bay Repair Module', + 'tech_type': 'CyclopsSeamothRepairModule'}, + 35017: {'classification': ItemClassification.useful, + 'count': 2, + 'name': 'Battery Charger fragment', + 'tech_type': 'BatteryChargerFragment'}, + 35018: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Beacon Fragment', + 'tech_type': 'BeaconFragment'}, + 35019: {'classification': ItemClassification.useful, + 'count': 2, + 'name': 'Bioreactor Fragment', + 'tech_type': 'BaseBioReactorFragment'}, + 35020: {'classification': ItemClassification.progression, + 'count': 3, + 'name': 'Cyclops Bridge Fragment', + 'tech_type': 'CyclopsBridgeFragment'}, + 35021: {'classification': ItemClassification.progression, + 'count': 3, + 'name': 'Cyclops Engine Fragment', + 'tech_type': 'CyclopsEngineFragment'}, + 35022: {'classification': ItemClassification.progression, + 'count': 3, + 'name': 'Cyclops Hull Fragment', + 'tech_type': 'CyclopsHullFragment'}, + 35023: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Grav Trap Fragment', + 'tech_type': 'GravSphereFragment'}, + 35024: {'classification': ItemClassification.progression, + 'count': 3, + 'name': 'Laser Cutter Fragment', + 'tech_type': 'LaserCutterFragment'}, + 35025: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Light Stick Fragment', + 'tech_type': 'TechlightFragment'}, + 35026: {'classification': ItemClassification.progression, + 'count': 3, + 'name': 'Mobile Vehicle Bay Fragment', + 'tech_type': 'ConstructorFragment'}, + 35027: {'classification': ItemClassification.progression, + 'count': 3, + 'name': 'Modification Station Fragment', + 'tech_type': 'WorkbenchFragment'}, + 35028: {'classification': ItemClassification.progression, + 'count': 2, + 'name': 'Moonpool Fragment', + 'tech_type': 'MoonpoolFragment'}, + 35029: {'classification': ItemClassification.useful, + 'count': 3, + 'name': 'Nuclear Reactor Fragment', + 'tech_type': 'BaseNuclearReactorFragment'}, + 35030: {'classification': ItemClassification.useful, + 'count': 2, + 'name': 'Power Cell Charger Fragment', + 'tech_type': 'PowerCellChargerFragment'}, + 35031: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Power Transmitter Fragment', + 'tech_type': 'PowerTransmitterFragment'}, + 35032: {'classification': ItemClassification.progression, + 'count': 4, + 'name': 'Prawn Suit Fragment', + 'tech_type': 'ExosuitFragment'}, + 35033: {'classification': ItemClassification.useful, + 'count': 2, + 'name': 'Prawn Suit Drill Arm Fragment', + 'tech_type': 'ExosuitDrillArmFragment'}, + 35034: {'classification': ItemClassification.useful, + 'count': 2, + 'name': 'Prawn Suit Grappling Arm Fragment', + 'tech_type': 'ExosuitGrapplingArmFragment'}, + 35035: {'classification': ItemClassification.useful, + 'count': 2, + 'name': 'Prawn Suit Propulsion Cannon Fragment', + 'tech_type': 'ExosuitPropulsionArmFragment'}, + 35036: {'classification': ItemClassification.useful, + 'count': 2, + 'name': 'Prawn Suit Torpedo Arm Fragment', + 'tech_type': 'ExosuitTorpedoArmFragment'}, + 35037: {'classification': ItemClassification.useful, + 'count': 3, + 'name': 'Scanner Room Fragment', + 'tech_type': 'BaseMapRoomFragment'}, + 35038: {'classification': ItemClassification.progression, + 'count': 5, + 'name': 'Seamoth Fragment', + 'tech_type': 'SeamothFragment'}, + 35039: {'classification': ItemClassification.progression, + 'count': 2, + 'name': 'Stasis Rifle Fragment', + 'tech_type': 'StasisRifleFragment'}, + 35040: {'classification': ItemClassification.useful, + 'count': 2, + 'name': 'Thermal Plant Fragment', + 'tech_type': 'ThermalPlantFragment'}, + 35041: {'classification': ItemClassification.progression, + 'count': 2, + 'name': 'Seaglide Fragment', + 'tech_type': 'SeaglideFragment'}, + 35042: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Radiation Suit', + 'tech_type': 'RadiationSuit'}, + 35043: {'classification': ItemClassification.progression, + 'count': 2, + 'name': 'Propulsion Cannon Fragment', + 'tech_type': 'PropulsionCannonFragment'}, + 35044: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Neptune Launch Platform', + 'tech_type': 'RocketBase'}, + 35045: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Ion Power Cell', + 'tech_type': 'PrecursorIonPowerCell'}, + 35046: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Exterior Growbed Fragment', + 'tech_type': 'FarmingTrayFragment'}, + 35047: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Picture Frame', + 'tech_type': 'PictureFrameFragment'}, + 35048: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Bench Fragment', + 'tech_type': 'BenchFragment'}, + 35049: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Basic Plant Pot', + 'tech_type': 'PlanterPotFragment'}, + 35050: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Interior Growbed', + 'tech_type': 'PlanterBoxFragment'}, + 35051: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Plant Shelf', + 'tech_type': 'PlanterShelfFragment'}, + 35052: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Observatory Fragment', + 'tech_type': 'BaseObservatoryFragment'}, + 35053: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Multipurpose Room Fragment', + 'tech_type': 'BaseRoomFragment'}, + 35054: {'classification': ItemClassification.useful, + 'count': 2, + 'name': 'Bulkhead Fragment', + 'tech_type': 'BaseBulkheadFragment'}, + 35055: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Spotlight', + 'tech_type': 'Spotlight'}, + 35056: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Desk', + 'tech_type': 'StarshipDesk'}, + 35057: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Swivel Chair', + 'tech_type': 'StarshipChair'}, + 35058: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Office Chair', + 'tech_type': 'StarshipChair2'}, + 35059: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Command Chair', + 'tech_type': 'StarshipChair3'}, + 35060: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Counter', + 'tech_type': 'LabCounter'}, + 35061: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Single Bed', + 'tech_type': 'NarrowBed'}, + 35062: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Basic Double Bed', + 'tech_type': 'Bed1'}, + 35063: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Quilted Double Bed', + 'tech_type': 'Bed2'}, + 35064: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Coffee Vending Machine', + 'tech_type': 'CoffeeVendingMachine'}, + 35065: {'classification': ItemClassification.filler, + 'count': 2, + 'name': 'Trash Can', + 'tech_type': 'Trashcans'}, + 35066: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Floodlight', + 'tech_type': 'Techlight'}, + 35067: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Bar Table', + 'tech_type': 'BarTable'}, + 35068: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Vending Machine', + 'tech_type': 'VendingMachine'}, + 35069: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Single Wall Shelf', + 'tech_type': 'SingleWallShelf'}, + 35070: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Wall Shelves', + 'tech_type': 'WallShelves'}, + 35071: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Round Plant Pot', + 'tech_type': 'PlanterPot2'}, + 35072: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Chic Plant Pot', + 'tech_type': 'PlanterPot3'}, + 35073: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Nuclear Waste Disposal', + 'tech_type': 'LabTrashcan'}, + 35074: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Wall Planter', + 'tech_type': 'BasePlanter'}, + 35075: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Ion Battery', + 'tech_type': 'PrecursorIonBattery'}, + 35076: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Neptune Gantry', + 'tech_type': 'RocketBaseLadder'}, + 35077: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Neptune Boosters', + 'tech_type': 'RocketStage1'}, + 35078: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Neptune Fuel Reserve', + 'tech_type': 'RocketStage2'}, + 35079: {'classification': ItemClassification.progression, + 'count': 1, + 'name': 'Neptune Cockpit', + 'tech_type': 'RocketStage3'}, + 35080: {'classification': ItemClassification.filler, + 'count': 1, + 'name': 'Water Filtration Machine', + 'tech_type': 'BaseFiltrationMachine'}} + +advancement_item_names: Set[str] = set() +non_advancement_item_names: Set[str] = set() + +for item_id, item_data in item_table.items(): + item_name = item_data["name"] + if ItemClassification.progression in item_data["classification"]: advancement_item_names.add(item_name) else: non_advancement_item_names.add(item_name) -lookup_id_to_name[None] = "Victory" +if False: # turn to True to export for Subnautica mod + payload = {item_id: item_data["tech_type"] for item_id, item_data in item_table.items()} + import json -lookup_name_to_id = {name: id for id, name in lookup_id_to_name.items()} \ No newline at end of file + with open("items.json", "w") as f: + json.dump(payload, f) diff --git a/worlds/subnautica/Locations.py b/worlds/subnautica/Locations.py index 361a712ba8..c437fbc9bf 100644 --- a/worlds/subnautica/Locations.py +++ b/worlds/subnautica/Locations.py @@ -1,12 +1,570 @@ -import json -import os +from typing import Dict, TypedDict, List -with open(os.path.join(os.path.dirname(__file__), 'locations.json'), 'r') as file: - location_table = json.loads(file.read()) -lookup_id_to_name = {} -for item in location_table: - lookup_id_to_name[item["id"]] = item["name"] +class Vector(TypedDict): + x: float + y: float + z: float -lookup_id_to_name[None] = "Neptune Launch" -lookup_name_to_id = {name: id for id, name in lookup_id_to_name.items()} + +class LocationDict(TypedDict, total=False): + name: str + can_slip_through: bool + need_laser_cutter: bool + position: Vector + need_propulsion_cannon: bool + + +events: List[str] = ["Neptune Launch", "Disable Quarantine", "Full Infection", "Repair Aurora Drive"] + +location_table: Dict[int, LocationDict] = { + 33000: {'can_slip_through': False, + 'name': 'Blood Kelp Trench Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': -1234.3, 'y': -349.7, 'z': -396.0}}, + 33001: {'can_slip_through': False, + 'name': 'Blood Kelp Trench Wreck - Inside Databox', + 'need_laser_cutter': False, + 'position': {'x': -1208.0, 'y': -349.6, 'z': -383.0}}, + 33002: {'can_slip_through': False, + 'name': 'Blood Kelp Trench Wreck - PDA', + 'need_laser_cutter': False, + 'position': {'x': -1210.6, 'y': -340.7, 'z': -393.4}}, + 33003: {'can_slip_through': False, + 'name': 'Bulb Zone West Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': 903.8, 'y': -220.3, 'z': 590.9}}, + 33004: {'can_slip_through': False, + 'name': 'Bulb Zone West Wreck - Under Databox', + 'need_laser_cutter': False, + 'position': {'x': 910.9, 'y': -201.8, 'z': 623.5}}, + 33005: {'can_slip_through': False, + 'name': 'Bulb Zone West Wreck - Inside Databox', + 'need_laser_cutter': True, + 'position': {'x': 914.9, 'y': -202.1, 'z': 611.8}}, + 33006: {'can_slip_through': False, + 'name': 'Bulb Zone West Wreck - PDA', + 'need_laser_cutter': True, + 'position': {'x': 912.6, 'y': -202.0, 'z': 609.5}}, + 33007: {'can_slip_through': False, + 'name': 'Bulb Zone East Wreck - Databox', + 'need_laser_cutter': False, + 'position': {'x': 1327.1, 'y': -234.9, 'z': 575.8}}, + 33008: {'can_slip_through': False, + 'name': 'Dunes North Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': -1407.7, 'y': -344.2, 'z': 721.5}}, + 33009: {'can_slip_through': False, + 'name': 'Dunes North Wreck - Office Databox', + 'need_laser_cutter': False, + 'position': {'x': -1393.9, 'y': -329.7, 'z': 733.5}}, + 33010: {'can_slip_through': False, + 'name': 'Dunes North Wreck - PDA', + 'need_laser_cutter': False, + 'position': {'x': -1396.3, 'y': -330.8, 'z': 730.0}}, + 33011: {'can_slip_through': False, + 'name': 'Dunes North Wreck - Cargo Databox', + 'need_laser_cutter': True, + 'position': {'x': -1409.8, 'y': -332.4, 'z': 706.9}}, + 33012: {'can_slip_through': False, + 'name': 'Dunes West Wreck - Databox', + 'need_laser_cutter': False, + 'position': {'x': -1626.2, 'y': -357.5, 'z': 99.5}}, + 33013: {'can_slip_through': False, + 'name': 'Dunes East Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': -1196.3, 'y': -223.0, 'z': 12.5}}, + 33014: {'can_slip_through': False, + 'name': 'Dunes East Wreck - Inside Databox', + 'need_laser_cutter': False, + 'position': {'x': -1206.4, 'y': -225.6, 'z': 4.0}}, + 33015: {'can_slip_through': False, + 'name': 'Grand Reef North Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': -269.7, 'y': -262.8, 'z': -764.3}}, + 33016: {'can_slip_through': False, + 'name': 'Grand Reef North Wreck - Elevator Databox', + 'need_laser_cutter': True, + 'position': {'x': -285.8, 'y': -240.2, 'z': -786.5}}, + 33017: {'can_slip_through': False, + 'name': 'Grand Reef North Wreck - Bottom Databox', + 'need_laser_cutter': False, + 'position': {'x': -285.2, 'y': -262.4, 'z': -788.4}}, + 33018: {'can_slip_through': False, + 'name': 'Grand Reef North Wreck - Hangar PDA', + 'need_laser_cutter': False, + 'position': {'x': -272.5, 'y': -254.7, 'z': -788.5}}, + 33019: {'can_slip_through': False, + 'name': 'Grand Reef South Wreck - Trench Databox', + 'need_laser_cutter': False, + 'position': {'x': -850.9, 'y': -473.2, 'z': -1414.6}}, + 33020: {'can_slip_through': False, + 'name': 'Grand Reef South Wreck - Comms Databox', + 'need_laser_cutter': True, + 'position': {'x': -889.4, 'y': -433.8, 'z': -1424.8}}, + 33021: {'can_slip_through': False, + 'name': 'Grand Reef South Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': -862.4, 'y': -437.5, 'z': -1444.1}}, + 33022: {'can_slip_through': False, + 'name': 'Grand Reef South Wreck - PDA', + 'need_laser_cutter': False, + 'position': {'x': -887.9, 'y': -446.0, 'z': -1422.7}}, + 33023: {'can_slip_through': False, + 'name': 'Grassy Plateaus South Wreck - Databox', + 'need_laser_cutter': False, + 'position': {'x': -23.3, 'y': -105.8, 'z': -604.2}}, + 33024: {'can_slip_through': False, + 'name': 'Grassy Plateaus South Wreck - PDA', + 'need_laser_cutter': False, + 'position': {'x': -27.3, 'y': -106.8, 'z': -607.2}}, + 33025: {'can_slip_through': True, + 'name': 'Grassy Plateaus East Wreck - Breach Databox', + 'need_laser_cutter': True, + 'position': {'x': 313.9, 'y': -91.8, 'z': 432.6}}, + 33026: {'can_slip_through': True, + 'name': 'Grassy Plateaus East Wreck - Hangar Databox', + 'need_laser_cutter': True, + 'position': {'x': 319.4, 'y': -104.3, 'z': 441.5}}, + 33027: {'can_slip_through': False, + 'name': 'Grassy Plateaus West Wreck - Locker PDA', + 'need_laser_cutter': False, + 'position': {'x': -632.3, 'y': -75.0, 'z': -8.9}}, + 33028: {'can_slip_through': False, + 'name': 'Grassy Plateaus West Wreck - Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -664.4, 'y': -97.8, 'z': -8.0}}, + 33029: {'can_slip_through': False, + 'name': 'Grassy Plateaus West Wreck - Databox', + 'need_laser_cutter': True, + 'position': {'x': -421.4, 'y': -107.8, 'z': -266.5}}, + 33030: {'can_slip_through': False, + 'name': 'Safe Shallows Wreck - PDA', + 'need_laser_cutter': False, + 'position': {'x': -44.0, 'y': -29.1, 'z': -403.6}}, + 33031: {'can_slip_through': False, + 'name': 'Kelp Forest Wreck - Databox', + 'need_laser_cutter': False, + 'position': {'x': -317.6, 'y': -78.8, 'z': 247.4}}, + 33032: {'can_slip_through': False, + 'name': 'Kelp Forest Wreck - PDA', + 'need_laser_cutter': False, + 'position': {'x': 63.2, 'y': -38.5, 'z': 382.9}}, + 33033: {'can_slip_through': False, + 'name': 'Mountains West Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': 740.3, 'y': -389.2, 'z': 1179.8}}, + 33034: {'can_slip_through': False, + 'name': 'Mountains West Wreck - Data Terminal', + 'need_laser_cutter': True, + 'position': {'x': 703.7, 'y': -365.9, 'z': 1199.3}}, + 33035: {'can_slip_through': False, + 'name': 'Mountains West Wreck - Hangar Databox', + 'need_laser_cutter': True, + 'position': {'x': 698.2, 'y': -350.8, 'z': 1186.9}}, + 33036: {'can_slip_through': False, + 'name': 'Mountains West Wreck - Office Databox', + 'need_laser_cutter': False, + 'position': {'x': 676.3, 'y': -343.6, 'z': 1204.6}}, + 33037: {'can_slip_through': False, + 'name': 'Mountains East Wreck - Comms Databox', + 'need_laser_cutter': False, + 'position': {'x': 1068.5, 'y': -283.4, 'z': 1345.3}}, + 33038: {'can_slip_through': False, + 'name': 'Mountains East Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': 1075.7, 'y': -288.9, 'z': 1321.8}}, + 33039: {'can_slip_through': False, + 'name': 'Northwestern Mushroom Forest Wreck - Cargo Databox', + 'need_laser_cutter': True, + 'position': {'x': -655.1, 'y': -109.6, 'z': 791.0}}, + 33040: {'can_slip_through': False, + 'name': 'Northwestern Mushroom Forest Wreck - Office Databox', + 'need_laser_cutter': False, + 'position': {'x': -663.4, 'y': -111.9, 'z': 777.9}}, + 33041: {'can_slip_through': False, + 'name': 'Northwestern Mushroom Forest Wreck - PDA', + 'need_laser_cutter': False, + 'position': {'x': -662.2, 'y': -113.4, 'z': 777.7}}, + 33042: {'can_slip_through': False, + 'name': "Sea Treader's Path Wreck - Outside Databox", + 'need_laser_cutter': False, + 'position': {'x': -1161.1, 'y': -191.7, 'z': -758.3}}, + 33043: {'can_slip_through': False, + 'name': "Sea Treader's Path Wreck - Hangar Databox", + 'need_laser_cutter': True, + 'position': {'x': -1129.5, 'y': -155.2, 'z': -729.3}}, + 33044: {'can_slip_through': False, + 'name': "Sea Treader's Path Wreck - Lobby Databox", + 'need_laser_cutter': False, + 'position': {'x': -1115.9, 'y': -175.3, 'z': -724.5}}, + 33045: {'can_slip_through': False, + 'name': "Sea Treader's Path Wreck - PDA", + 'need_laser_cutter': False, + 'position': {'x': -1136.8, 'y': -157.0, 'z': -734.6}}, + 33046: {'can_slip_through': False, + 'name': 'Sparse Reef Wreck - Locker Databox', + 'need_laser_cutter': True, + 'position': {'x': -789.8, 'y': -216.1, 'z': -711.0}}, + 33047: {'can_slip_through': False, + 'name': 'Sparse Reef Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': -810.7, 'y': -209.3, 'z': -685.5}}, + 33048: {'can_slip_through': False, + 'name': 'Sparse Reef Wreck - Lab Databox', + 'need_laser_cutter': True, + 'position': {'x': -795.5, 'y': -204.1, 'z': -774.7}}, + 33049: {'can_slip_through': False, + 'name': 'Underwater Islands Wreck - Outside Databox', + 'need_laser_cutter': False, + 'position': {'x': -170.8, 'y': -187.6, 'z': 880.7}}, + 33050: {'can_slip_through': False, + 'name': 'Underwater Islands Wreck - Hangar Databox', + 'need_laser_cutter': True, + 'position': {'x': -138.4, 'y': -193.6, 'z': 888.7}}, + 33051: {'can_slip_through': False, + 'name': 'Underwater Islands Wreck - Data Terminal', + 'need_laser_cutter': True, + 'position': {'x': -130.7, 'y': -193.2, 'z': 883.3}}, + 33052: {'can_slip_through': False, + 'name': 'Underwater Islands Wreck - Cable Databox', + 'need_laser_cutter': False, + 'position': {'x': -137.8, 'y': -193.4, 'z': 879.4}}, + 33053: {'can_slip_through': False, + 'name': 'Underwater Islands Wreck - Pipes Databox 1', + 'need_laser_cutter': False, + 'need_propulsion_cannon': True, + 'position': {'x': -124.4, 'y': -200.7, 'z': 853.0}}, + 33054: {'can_slip_through': False, + 'name': 'Underwater Islands Wreck - Pipes Databox 2', + 'need_laser_cutter': False, + 'need_propulsion_cannon': True, + 'position': {'x': -126.8, 'y': -201.1, 'z': 852.1}}, + 33055: {'can_slip_through': False, + 'name': 'Degasi Seabase - Deep Grand Reef - Bedroom Databox', + 'need_laser_cutter': False, + 'position': {'x': -643.8, 'y': -509.9, 'z': -941.9}}, + 33056: {'can_slip_through': False, + 'name': 'Degasi Seabase - Deep Grand Reef - Observatory Databox', + 'need_laser_cutter': False, + 'position': {'x': -635.1, 'y': -502.7, 'z': -951.4}}, + 33057: {'can_slip_through': False, + 'name': 'Degasi Seabase - Deep Grand Reef - Bedroom PDA', + 'need_laser_cutter': False, + 'position': {'x': -645.8, 'y': -508.7, 'z': -943.0}}, + 33058: {'can_slip_through': False, + 'name': 'Degasi Seabase - Deep Grand Reef - Outside PDA', + 'need_laser_cutter': False, + 'position': {'x': -630.5, 'y': -511.1, 'z': -936.1}}, + 33059: {'can_slip_through': False, + 'name': 'Degasi Seabase - Deep Grand Reef - Observatory PDA', + 'need_laser_cutter': False, + 'position': {'x': -647.7, 'y': -502.6, 'z': -935.8}}, + 33060: {'can_slip_through': False, + 'name': 'Degasi Seabase - Deep Grand Reef - Lab PDA', + 'need_laser_cutter': False, + 'position': {'x': -639.6, 'y': -505.9, 'z': -946.6}}, + 33061: {'can_slip_through': False, + 'name': 'Floating Island - Lake PDA', + 'need_laser_cutter': False, + 'position': {'x': -707.2, 'y': 0.5, 'z': -1096.7}}, + 33062: {'can_slip_through': False, + 'name': 'Degasi Seabase - Floating Island - Databox', + 'need_laser_cutter': False, + 'position': {'x': -765.7, 'y': 17.6, 'z': -1116.4}}, + 33063: {'can_slip_through': False, + 'name': 'Degasi Seabase - Floating Island - Room PDA', + 'need_laser_cutter': False, + 'position': {'x': -754.9, 'y': 14.6, 'z': -1108.9}}, + 33064: {'can_slip_through': False, + 'name': 'Degasi Seabase - Floating Island - Green Wall PDA', + 'need_laser_cutter': False, + 'position': {'x': -765.3, 'y': 14.1, 'z': -1115.0}}, + 33065: {'can_slip_through': False, + 'name': 'Degasi Seabase - Floating Island - Corridor PDA', + 'need_laser_cutter': False, + 'position': {'x': -758.6, 'y': 14.1, 'z': -1111.3}}, + 33066: {'can_slip_through': False, + 'name': 'Degasi Seabase - Floating Island - North Observatory PDA', + 'need_laser_cutter': False, + 'position': {'x': -805.4, 'y': 76.9, 'z': -1055.7}}, + 33067: {'can_slip_through': False, + 'name': 'Degasi Seabase - Floating Island - South Observatory PDA', + 'need_laser_cutter': False, + 'position': {'x': -715.9, 'y': 75.4, 'z': -1168.8}}, + 33068: {'can_slip_through': False, + 'name': 'Jellyshroom Cave - PDA', + 'need_laser_cutter': False, + 'position': {'x': -540.5, 'y': -250.8, 'z': -83.4}}, + 33069: {'can_slip_through': False, + 'name': 'Degasi Seabase - Jellyshroom Cave - Bedroom Databox', + 'need_laser_cutter': False, + 'position': {'x': 110.6, 'y': -264.9, 'z': -369.0}}, + 33070: {'can_slip_through': False, + 'name': 'Degasi Seabase - Jellyshroom Cave - Detached PDA', + 'need_laser_cutter': False, + 'position': {'x': 80.6, 'y': -268.6, 'z': -358.3}}, + 33071: {'can_slip_through': False, + 'name': 'Degasi Seabase - Jellyshroom Cave - Office PDA', + 'need_laser_cutter': False, + 'position': {'x': 78.2, 'y': -265.0, 'z': -373.4}}, + 33072: {'can_slip_through': False, + 'name': 'Degasi Seabase - Jellyshroom Cave - Locker PDA', + 'need_laser_cutter': False, + 'position': {'x': 85.1, 'y': -264.1, 'z': -372.8}}, + 33073: {'can_slip_through': False, + 'name': 'Degasi Seabase - Jellyshroom Cave - Bedroom PDA', + 'need_laser_cutter': False, + 'position': {'x': 112.3, 'y': -264.9, 'z': -369.3}}, + 33074: {'can_slip_through': False, + 'name': 'Degasi Seabase - Jellyshroom Cave - Observatory PDA', + 'need_laser_cutter': False, + 'position': {'x': 95.5, 'y': -258.9, 'z': -366.5}}, + 33075: {'can_slip_through': False, + 'name': 'Lifepod 2 - Databox', + 'need_laser_cutter': False, + 'position': {'x': -483.6, 'y': -504.7, 'z': 1326.6}}, + 33076: {'can_slip_through': False, + 'name': 'Lifepod 2 - PDA', + 'need_laser_cutter': False, + 'position': {'x': -481.4, 'y': -503.6, 'z': 1324.1}}, + 33077: {'can_slip_through': False, + 'name': 'Lifepod 3 - Databox', + 'need_laser_cutter': False, + 'position': {'x': -34.2, 'y': -22.4, 'z': 410.5}}, + 33078: {'can_slip_through': False, + 'name': 'Lifepod 3 - PDA', + 'need_laser_cutter': False, + 'position': {'x': -33.8, 'y': -22.5, 'z': 408.8}}, + 33079: {'can_slip_through': False, + 'name': 'Lifepod 4 - Databox', + 'need_laser_cutter': False, + 'position': {'x': 712.4, 'y': -3.4, 'z': 160.8}}, + 33080: {'can_slip_through': False, + 'name': 'Lifepod 4 - PDA', + 'need_laser_cutter': False, + 'position': {'x': 712.0, 'y': -3.5, 'z': 161.5}}, + 33081: {'can_slip_through': False, + 'name': 'Lifepod 6 - Databox', + 'need_laser_cutter': False, + 'position': {'x': 358.7, 'y': -117.1, 'z': 306.8}}, + 33082: {'can_slip_through': False, + 'name': 'Lifepod 6 - Inside PDA', + 'need_laser_cutter': False, + 'position': {'x': 361.8, 'y': -116.2, 'z': 309.5}}, + 33083: {'can_slip_through': False, + 'name': 'Lifepod 6 - Outside PDA', + 'need_laser_cutter': False, + 'position': {'x': 359.9, 'y': -117.0, 'z': 312.1}}, + 33084: {'can_slip_through': False, + 'name': 'Lifepod 7 - PDA', + 'need_laser_cutter': False, + 'position': {'x': -56.0, 'y': -182.0, 'z': -1039.0}}, + 33085: {'can_slip_through': False, + 'name': 'Lifepod 12 - Databox', + 'need_laser_cutter': False, + 'position': {'x': 1119.5, 'y': -271.7, 'z': 561.7}}, + 33086: {'can_slip_through': False, + 'name': 'Lifepod 12 - PDA', + 'need_laser_cutter': False, + 'position': {'x': 1116.1, 'y': -271.3, 'z': 566.9}}, + 33087: {'can_slip_through': False, + 'name': 'Lifepod 13 - Databox', + 'need_laser_cutter': False, + 'position': {'x': -926.4, 'y': -185.2, 'z': 501.8}}, + 33088: {'can_slip_through': False, + 'name': 'Lifepod 13 - PDA', + 'need_laser_cutter': False, + 'position': {'x': -926.8, 'y': -184.4, 'z': 506.6}}, + 33089: {'can_slip_through': False, + 'name': 'Lifepod 17 - PDA', + 'need_laser_cutter': False, + 'position': {'x': -514.5, 'y': -98.1, 'z': -56.5}}, + 33090: {'can_slip_through': False, + 'name': 'Lifepod 19 - Databox', + 'need_laser_cutter': False, + 'position': {'x': -809.8, 'y': -302.2, 'z': -876.9}}, + 33091: {'can_slip_through': False, + 'name': 'Lifepod 19 - Outside PDA', + 'need_laser_cutter': False, + 'position': {'x': -806.1, 'y': -294.1, 'z': -866.0}}, + 33092: {'can_slip_through': False, + 'name': 'Lifepod 19 - Inside PDA', + 'need_laser_cutter': False, + 'position': {'x': -810.5, 'y': -299.4, 'z': -873.1}}, + 33093: {'can_slip_through': False, + 'name': 'Aurora Seamoth Bay - Upgrade Console', + 'need_laser_cutter': False, + 'need_propulsion_cannon': True, + 'position': {'x': 903.5, 'y': -0.2, 'z': 16.1}}, + 33094: {'can_slip_through': False, + 'name': 'Aurora Drive Room - Upgrade Console', + 'need_laser_cutter': False, + 'need_propulsion_cannon': True, + 'position': {'x': 872.5, 'y': 2.7, 'z': -0.7}}, + 33095: {'can_slip_through': False, + 'name': 'Aurora Prawn Suit Bay - Upgrade Console', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 991.6, 'y': 3.2, 'z': -31.0}}, + 33096: {'can_slip_through': False, + 'name': 'Aurora - Office PDA', + 'need_laser_cutter': False, + 'position': {'x': 952.1, 'y': 41.2, 'z': 113.9}}, + 33097: {'can_slip_through': False, + 'name': 'Aurora - Corridor PDA', + 'need_laser_cutter': False, + 'position': {'x': 977.2, 'y': 39.1, 'z': 83.0}}, + 33098: {'can_slip_through': False, + 'name': 'Aurora - Cargo Bay PDA', + 'need_laser_cutter': False, + 'need_propulsion_cannon': True, + 'position': {'x': 954.9, 'y': 11.2, 'z': 3.4}}, + 33099: {'can_slip_through': False, + 'name': 'Aurora - Seamoth Bay PDA', + 'need_laser_cutter': False, + 'need_propulsion_cannon': True, + 'position': {'x': 907.1, 'y': -1.5, 'z': 15.3}}, + 33100: {'can_slip_through': False, + 'name': 'Aurora - Medkit Locker PDA', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 951.8, 'y': -2.3, 'z': -34.7}}, + 33101: {'can_slip_through': False, + 'name': 'Aurora - Locker PDA', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 952.0, 'y': -3.7, 'z': -23.4}}, + 33102: {'can_slip_through': False, + 'name': 'Aurora - Canteen PDA', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 986.5, 'y': 9.6, 'z': -48.6}}, + 33103: {'can_slip_through': False, + 'name': 'Aurora - Cabin 4 PDA', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 951.3, 'y': 11.2, 'z': -51.0}}, + 33104: {'can_slip_through': False, + 'name': 'Aurora - Cabin 7 PDA', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 967.1, 'y': 10.4, 'z': -47.4}}, + 33105: {'can_slip_through': False, + 'name': 'Aurora - Cabin 1 PDA', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 964.1, 'y': 11.1, 'z': -61.9}}, + 33106: {'can_slip_through': False, + 'name': 'Aurora - Captain PDA', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 971.2, 'y': 10.8, 'z': -70.4}}, + 33107: {'can_slip_through': False, + 'name': 'Aurora - Ring PDA', + 'need_laser_cutter': False, + 'need_propulsion_cannon': True, + 'position': {'x': 1033.6, 'y': -8.5, 'z': 16.2}}, + 33108: {'can_slip_through': False, + 'name': 'Aurora - Lab PDA', + 'need_laser_cutter': False, + 'need_propulsion_cannon': True, + 'position': {'x': 1032.5, 'y': -7.8, 'z': 32.4}}, + 33109: {'can_slip_through': False, + 'name': 'Aurora - Office Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': 945.8, 'y': 40.8, 'z': 115.1}}, + 33110: {'can_slip_through': False, + 'name': 'Aurora - Captain Data Terminal', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 974.8, 'y': 10.0, 'z': -77.0}}, + 33111: {'can_slip_through': False, + 'name': 'Aurora - Battery Room Data Terminal', + 'need_laser_cutter': True, + 'need_propulsion_cannon': True, + 'position': {'x': 1040.8, 'y': -11.4, 'z': -3.4}}, + 33112: {'can_slip_through': False, + 'name': 'Aurora - Lab Data Terminal', + 'need_laser_cutter': False, + 'need_propulsion_cannon': True, + 'position': {'x': 1029.5, 'y': -8.7, 'z': 35.9}}, + 33113: {'can_slip_through': False, + 'name': "Quarantine Enforcement Platform's - Upper Alien Data " + 'Terminal', + 'need_laser_cutter': False, + 'position': {'x': 432.2, 'y': 3.0, 'z': 1193.2}}, + 33114: {'can_slip_through': False, + 'name': "Quarantine Enforcement Platform's - Mid Alien Data Terminal", + 'need_laser_cutter': False, + 'position': {'x': 474.4, 'y': -4.5, 'z': 1224.4}}, + 33115: {'can_slip_through': False, + 'name': 'Dunes Sanctuary - Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -1224.2, 'y': -400.4, 'z': 1057.9}}, + 33116: {'can_slip_through': False, + 'name': 'Deep Sparse Reef Sanctuary - Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -895.5, 'y': -311.6, 'z': -838.1}}, + 33117: {'can_slip_through': False, + 'name': 'Northern Blood Kelp Zone Sanctuary - Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -642.9, 'y': -563.5, 'z': 1485.5}}, + 33118: {'can_slip_through': False, + 'name': 'Lost River Laboratory Cache - Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -1112.3, 'y': -687.3, 'z': -695.5}}, + 33119: {'can_slip_through': False, + 'name': 'Disease Research Facility - Upper Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -280.2, 'y': -804.3, 'z': 305.1}}, + 33120: {'can_slip_through': False, + 'name': 'Disease Research Facility - Mid Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -267.9, 'y': -806.6, 'z': 250.0}}, + 33121: {'can_slip_through': False, + 'name': 'Disease Research Facility - Lower Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -286.2, 'y': -815.6, 'z': 297.8}}, + 33122: {'can_slip_through': False, + 'name': 'Alien Thermal Plant - Entrance Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -71.3, 'y': -1227.2, 'z': 104.8}}, + 33123: {'can_slip_through': False, + 'name': 'Alien Thermal Plant - Green Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -38.7, 'y': -1226.6, 'z': 111.8}}, + 33124: {'can_slip_through': False, + 'name': 'Alien Thermal Plant - Yellow Alien Data Terminal', + 'need_laser_cutter': False, + 'position': {'x': -30.4, 'y': -1220.3, 'z': 111.8}}, + 33125: {'can_slip_through': False, + 'name': "Primary Containment Facility's Antechamber - Alien Data " + 'Terminal', + 'need_laser_cutter': False, + 'position': {'x': 245.8, 'y': -1430.6, 'z': -311.5}}, + 33126: {'can_slip_through': False, + 'name': "Primary Containment Facility's Egg Laboratory - Alien Data " + 'Terminal', + 'need_laser_cutter': False, + 'position': {'x': 165.5, 'y': -1442.4, 'z': -385.8}}, + 33127: {'can_slip_through': False, + 'name': "Primary Containment Facility's Pipe Room - Alien Data " + 'Terminal', + 'need_laser_cutter': False, + 'position': {'x': 348.7, 'y': -1443.5, 'z': -291.9}}, + 33128: {'can_slip_through': False, + 'name': 'Grassy Plateaus West Wreck - Beam PDA', + 'need_laser_cutter': True, + 'position': {'x': -641.8, 'y': -111.3, 'z': -19.7}}, + 33129: {'can_slip_through': False, + 'name': 'Floating Island - Cave Entrance PDA', + 'need_laser_cutter': False, + 'position': {'x': -748.9, 'y': 14.4, 'z': -1179.5}}} + +if False: # turn to True to export for Subnautica mod + payload = {location_id: location_data["position"] for location_id, location_data in location_table.items()} + import json + + with open("locations.json", "w") as f: + json.dump(payload, f) diff --git a/worlds/subnautica/Options.py b/worlds/subnautica/Options.py index 4189aecb19..b5dc2241fb 100644 --- a/worlds/subnautica/Options.py +++ b/worlds/subnautica/Options.py @@ -1,14 +1,46 @@ -from Options import Choice +from Options import Choice, Range +from .Creatures import all_creatures class ItemPool(Choice): - """Valuable item pool moves all not progression relevant items to starting inventory and - creates random duplicates of important items in their place.""" + """Valuable item pool leaves all filler items in their vanilla locations and + creates random duplicates of important items into freed spots.""" display_name = "Item Pool" option_standard = 0 option_valuable = 1 +class Goal(Choice): + """Goal to complete. + Launch: Leave the planet. + Free: Disable quarantine. + Infected: Reach maximum infection level. + Drive: Repair the Aurora's Drive Core""" + auto_display_name = True + display_name = "Goal" + option_launch = 0 + option_free = 1 + option_infected = 2 + option_drive = 3 + + def get_event_name(self) -> str: + return { + self.option_launch: "Neptune Launch", + self.option_infected: "Full Infection", + self.option_free: "Disable Quarantine", + self.option_drive: "Repair Aurora Drive" + }[self.value] + + +class CreatureScans(Range): + """Place items on specific creature scans. + Warning: Includes aggressive Leviathans.""" + display_name = "Creature Scans" + range_end = len(all_creatures) + + options = { - "item_pool": ItemPool + "item_pool": ItemPool, + "goal": Goal, + "creature_scans": CreatureScans } diff --git a/worlds/subnautica/Regions.py b/worlds/subnautica/Regions.py deleted file mode 100644 index 1eb0e12f61..0000000000 --- a/worlds/subnautica/Regions.py +++ /dev/null @@ -1,8 +0,0 @@ -def create_regions(world, player: int): - from . import create_region - from .Locations import lookup_name_to_id as location_lookup_name_to_id - - world.regions += [ - create_region(world, player, 'Menu', None, ['Lifepod 5']), - create_region(world, player, 'Planet 4546B', [location for location in location_lookup_name_to_id]) - ] diff --git a/worlds/subnautica/Rules.py b/worlds/subnautica/Rules.py index 8d5bcae457..b8f8f1a7b4 100644 --- a/worlds/subnautica/Rules.py +++ b/worlds/subnautica/Rules.py @@ -1,113 +1,122 @@ -from ..generic.Rules import set_rule -from .Locations import location_table -import logging +from typing import TYPE_CHECKING + +from worlds.generic.Rules import set_rule +from .Locations import location_table, LocationDict +from .Creatures import all_creatures, aggressive, suffix import math +if TYPE_CHECKING: + from . import SubnauticaWorld -def has_seaglide(state, player): + +def has_seaglide(state, player: int): return state.has("Seaglide Fragment", player, 2) -def has_modification_station(state, player): +def has_modification_station(state, player: int): return state.has("Modification Station Fragment", player, 3) -def has_mobile_vehicle_bay(state, player): +def has_mobile_vehicle_bay(state, player: int): return state.has("Mobile Vehicle Bay Fragment", player, 3) -def has_moonpool(state, player): +def has_moonpool(state, player: int): return state.has("Moonpool Fragment", player, 2) -def has_vehicle_upgrade_console(state, player): +def has_vehicle_upgrade_console(state, player: int): return state.has("Vehicle Upgrade Console", player) and \ has_moonpool(state, player) -def has_seamoth(state, player): +def has_seamoth(state, player: int): return state.has("Seamoth Fragment", player, 3) and \ has_mobile_vehicle_bay(state, player) -def has_seamoth_depth_module_mk1(state, player): +def has_seamoth_depth_module_mk1(state, player: int): return has_vehicle_upgrade_console(state, player) -def has_seamoth_depth_module_mk2(state, player): +def has_seamoth_depth_module_mk2(state, player: int): return has_seamoth_depth_module_mk1(state, player) and \ has_modification_station(state, player) -def has_seamoth_depth_module_mk3(state, player): +def has_seamoth_depth_module_mk3(state, player: int): return has_seamoth_depth_module_mk2(state, player) and \ has_modification_station(state, player) -def has_cyclops_bridge(state, player): +def has_cyclops_bridge(state, player: int): return state.has("Cyclops Bridge Fragment", player, 3) -def has_cyclops_engine(state, player): +def has_cyclops_engine(state, player: int): return state.has("Cyclops Engine Fragment", player, 3) -def has_cyclops_hull(state, player): +def has_cyclops_hull(state, player: int): return state.has("Cyclops Hull Fragment", player, 3) -def has_cyclops(state, player): +def has_cyclops(state, player: int): return has_cyclops_bridge(state, player) and \ has_cyclops_engine(state, player) and \ has_cyclops_hull(state, player) and \ has_mobile_vehicle_bay(state, player) -def has_cyclops_depth_module_mk1(state, player): +def has_cyclops_depth_module_mk1(state, player: int): return state.has("Cyclops Depth Module MK1", player) and \ has_modification_station(state, player) -def has_cyclops_depth_module_mk2(state, player): +def has_cyclops_depth_module_mk2(state, player: int): return has_cyclops_depth_module_mk1(state, player) and \ has_modification_station(state, player) -def has_cyclops_depth_module_mk3(state, player): +def has_cyclops_depth_module_mk3(state, player: int): return has_cyclops_depth_module_mk2(state, player) and \ has_modification_station(state, player) -def has_prawn(state, player): +def has_prawn(state, player: int): return state.has("Prawn Suit Fragment", player, 4) and \ has_mobile_vehicle_bay(state, player) -def has_praw_propulsion_arm(state, player): +def has_praw_propulsion_arm(state, player: int): return state.has("Prawn Suit Propulsion Cannon Fragment", player, 2) and \ has_vehicle_upgrade_console(state, player) -def has_prawn_depth_module_mk1(state, player): +def has_prawn_depth_module_mk1(state, player: int): return has_vehicle_upgrade_console(state, player) -def has_prawn_depth_module_mk2(state, player): +def has_prawn_depth_module_mk2(state, player: int): return has_prawn_depth_module_mk1(state, player) and \ has_modification_station(state, player) -def has_laser_cutter(state, player): +def has_laser_cutter(state, player: int): return state.has("Laser Cutter Fragment", player, 3) +def has_stasis_rile(state, player: int): + return state.has("Stasis Rifle Fragment", player, 2) + + # Either we have propulsion cannon, or prawn + propulsion cannon arm -def has_propulsion_cannon(state, player): +def has_propulsion_cannon(state, player: int): return state.has("Propulsion Cannon Fragment", player, 2) or \ (has_prawn(state, player) and has_praw_propulsion_arm(state, player)) -def has_cyclops_shield(state, player): +def has_cyclops_shield(state, player: int): return has_cyclops(state, player) and \ state.has("Cyclops Shield Generator", player) @@ -120,7 +129,7 @@ def has_cyclops_shield(state, player): # negligeable with from high capacity tank. 430m -> 460m # Fins are not used when using seaglide # -def get_max_swim_depth(state, player): +def get_max_swim_depth(state, player: int): # TODO, Make this a difficulty setting. # Only go up to 200m without any submarines for now. return 200 @@ -131,7 +140,7 @@ def get_max_swim_depth(state, player): # has_ultra_glide_fins = state.has("Ultra Glide Fins", player) # max_depth = 400 # More like 430m. Give some room - # if has_seaglide(state, player): + # if has_seaglide(state, player: int): # if has_ultra_high_capacity_tank: # max_depth = 750 # It's about 50m more. Give some room # else: @@ -147,7 +156,7 @@ def get_max_swim_depth(state, player): # return max_depth -def get_seamoth_max_depth(state, player): +def get_seamoth_max_depth(state, player: int): if has_seamoth(state, player): if has_seamoth_depth_module_mk3(state, player): return 900 @@ -187,7 +196,7 @@ def get_prawn_max_depth(state, player): return 0 -def get_max_depth(state, player): +def get_max_depth(state, player: int): # TODO, Difficulty option, we can add vehicle depth + swim depth # But at this point, we have to consider traver distance in caves, not # just depth @@ -197,54 +206,82 @@ def get_max_depth(state, player): get_prawn_max_depth(state, player)) -def can_access_location(state, player, loc): - pos_x = loc.get("position").get("x") - pos_y = loc.get("position").get("y") - pos_z = loc.get("position").get("z") - depth = -pos_y # y-up - map_center_dist = math.sqrt(pos_x ** 2 + pos_z ** 2) - aurora_dist = math.sqrt((pos_x - 1038.0) ** 2 + (pos_y - -3.4) ** 2 + (pos_z - -163.1) ** 2) - - need_radiation_suit = aurora_dist < 950 +def can_access_location(state, player: int, loc: LocationDict) -> bool: need_laser_cutter = loc.get("need_laser_cutter", False) - need_propulsion_cannon = loc.get("need_propulsion_cannon", False) - if need_laser_cutter and not has_laser_cutter(state, player): return False - if need_radiation_suit and not state.has("Radiation Suit", player): + need_propulsion_cannon = loc.get("need_propulsion_cannon", False) + if need_propulsion_cannon and not has_propulsion_cannon(state, player): return False - if need_propulsion_cannon and not has_propulsion_cannon(state, player): + pos = loc["position"] + pos_x = pos["x"] + pos_y = pos["y"] + pos_z = pos["z"] + + aurora_dist = math.sqrt((pos_x - 1038.0) ** 2 + (pos_y - -3.4) ** 2 + (pos_z - -163.1) ** 2) + need_radiation_suit = aurora_dist < 950 + if need_radiation_suit and not state.has("Radiation Suit", player): return False # Seaglide doesn't unlock anything specific, but just allows for faster movement. # Otherwise the game is painfully slow. + map_center_dist = math.sqrt(pos_x ** 2 + pos_z ** 2) if (map_center_dist > 800 or pos_y < -200) and not has_seaglide(state, player): return False + depth = -pos_y # y-up return get_max_depth(state, player) >= depth -def set_location_rule(world, player, loc): +def set_location_rule(world, player: int, loc: LocationDict): set_rule(world.get_location(loc["name"], player), lambda state: can_access_location(state, player, loc)) -def set_rules(world, player): - for loc in location_table: +def can_scan_creature(state, player: int, creature: str) -> bool: + if not has_seaglide(state, player): + return False + if creature in aggressive and not has_stasis_rile(state, player): + return False + return get_max_depth(state, player) >= all_creatures[creature] + + +def set_creature_rule(world, player, creature_name: str): + set_rule(world.get_location(creature_name + suffix, player), + lambda state: can_scan_creature(state, player, creature_name)) + + +def set_rules(subnautica_world: "SubnauticaWorld"): + player = subnautica_world.player + world = subnautica_world.world + + for loc in location_table.values(): set_location_rule(world, player, loc) - # Victory location - set_rule(world.get_location("Neptune Launch", player), lambda state: \ - get_max_depth(state, player) >= 1444 and \ - has_mobile_vehicle_bay(state, player) and \ - state.has('Neptune Launch Platform', player) and \ - state.has('Neptune Gantry', player) and \ - state.has('Neptune Boosters', player) and \ - state.has('Neptune Fuel Reserve', player) and \ - state.has('Neptune Cockpit', player) and \ - state.has('Ion Power Cell', player) and \ - state.has('Ion Battery', player) and \ + for creature_name in subnautica_world.creatures_to_scan: + set_creature_rule(world, player, creature_name) + + # Victory locations + set_rule(world.get_location("Neptune Launch", player), lambda state: + get_max_depth(state, player) >= 1444 and + has_mobile_vehicle_bay(state, player) and + state.has("Neptune Launch Platform", player) and + state.has("Neptune Gantry", player) and + state.has("Neptune Boosters", player) and + state.has("Neptune Fuel Reserve", player) and + state.has("Neptune Cockpit", player) and + state.has("Ion Power Cell", player) and + state.has("Ion Battery", player) and has_cyclops_shield(state, player)) - world.completion_condition[player] = lambda state: state.has('Victory', player) + set_rule(world.get_location("Disable Quarantine", player), lambda state: + get_max_depth(state, player) >= 1444) + + set_rule(world.get_location("Full Infection", player), lambda state: + get_max_depth(state, player) >= 900) + + room = world.get_location("Aurora Drive Room - Upgrade Console", player) + set_rule(world.get_location("Repair Aurora Drive", player), lambda state: room.can_reach(state)) + + world.completion_condition[player] = lambda state: state.has("Victory", player) diff --git a/worlds/subnautica/__init__.py b/worlds/subnautica/__init__.py index ae92331809..9ad4feb1a4 100644 --- a/worlds/subnautica/__init__.py +++ b/worlds/subnautica/__init__.py @@ -1,18 +1,17 @@ import logging +from typing import List, Dict, Any + +from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, RegionType +from worlds.AutoWorld import World, WebWorld +from . import Items +from . import Locations +from . import Creatures +from . import Options +from .Items import item_table +from .Rules import set_rules logger = logging.getLogger("Subnautica") -from .Locations import lookup_name_to_id as locations_lookup_name_to_id -from .Items import item_table, lookup_name_to_item, advancement_item_names -from .Items import lookup_name_to_id as items_lookup_name_to_id - -from .Regions import create_regions -from .Rules import set_rules -from .Options import options - -from BaseClasses import Region, Entrance, Location, MultiWorld, Item, Tutorial, ItemClassification, RegionType -from ..AutoWorld import World, WebWorld - class SubnaticaWeb(WebWorld): tutorials = [Tutorial( @@ -25,6 +24,10 @@ class SubnaticaWeb(WebWorld): )] +all_locations = {data["name"]: loc_id for loc_id, data in Locations.location_table.items()} +all_locations.update(Creatures.creature_locations) + + class SubnauticaWorld(World): """ Subnautica is an undersea exploration game. Stranded on an alien world, you become infected by @@ -34,34 +37,56 @@ class SubnauticaWorld(World): game: str = "Subnautica" web = SubnaticaWeb() - item_name_to_id = items_lookup_name_to_id - location_name_to_id = locations_lookup_name_to_id - options = options + item_name_to_id = {data["name"]: item_id for item_id, data in Items.item_table.items()} + location_name_to_id = all_locations + options = Options.options - data_version = 2 - required_client_version = (0, 1, 9) + data_version = 3 + required_client_version = (0, 3, 3) + + prefill_items: List[Item] + creatures_to_scan: List[str] + + def generate_early(self) -> None: + self.prefill_items = [ + self.create_item("Seaglide Fragment"), + self.create_item("Seaglide Fragment") + ] + self.creatures_to_scan = self.world.random.sample(Creatures.all_creatures_presorted, + self.world.creature_scans[self.player].value) + + def create_regions(self): + self.world.regions += [ + self.create_region("Menu", None, ["Lifepod 5"]), + self.create_region("Planet 4546B", + Locations.events + + [location["name"] for location in Locations.location_table.values()] + + [creature+Creatures.suffix for creature in self.creatures_to_scan]) + ] + + # refer to Rules.py + set_rules = set_rules def generate_basic(self): # Link regions - self.world.get_entrance('Lifepod 5', self.player).connect(self.world.get_region('Planet 4546B', self.player)) + self.world.get_entrance("Lifepod 5", self.player).connect(self.world.get_region("Planet 4546B", self.player)) # Generate item pool pool = [] neptune_launch_platform = None - extras = 0 - valuable = self.world.item_pool[self.player] == "valuable" - for item in item_table: + extras = self.world.creature_scans[self.player].value + valuable = self.world.item_pool[self.player] == Options.ItemPool.option_valuable + for item in item_table.values(): for i in range(item["count"]): subnautica_item = self.create_item(item["name"]) if item["name"] == "Neptune Launch Platform": neptune_launch_platform = subnautica_item - elif valuable and not item["progression"]: - self.world.push_precollected(subnautica_item) + elif valuable and ItemClassification.filler == item["classification"]: extras += 1 else: pool.append(subnautica_item) - for item_name in self.world.random.choices(sorted(advancement_item_names - {"Neptune Launch Platform"}), + for item_name in self.world.random.choices(sorted(Items.advancement_item_names - {"Neptune Launch Platform"}), k=extras): item = self.create_item(item_name) item.classification = ItemClassification.filler # as it's an extra, just fast-fill it somewhere @@ -72,39 +97,58 @@ class SubnauticaWorld(World): # Victory item self.world.get_location("Aurora - Captain Data Terminal", self.player).place_locked_item( neptune_launch_platform) - self.world.get_location("Neptune Launch", self.player).place_locked_item( - SubnauticaItem("Victory", ItemClassification.progression, None, player=self.player)) + for event in Locations.events: + self.world.get_location(event, self.player).place_locked_item( + SubnauticaItem(event, ItemClassification.progression, None, player=self.player)) + # make the goal event the victory "item" + self.world.get_location(self.world.goal[self.player].get_event_name(), self.player).item.name = "Victory" - def set_rules(self): - set_rules(self.world, self.player) + def fill_slot_data(self) -> Dict[str, Any]: + goal: Options.Goal = self.world.goal[self.player] + item_pool: Options.ItemPool = self.world.item_pool[self.player] + vanilla_tech: List[str] = [] + if item_pool == Options.ItemPool.option_valuable: + for item in Items.item_table.values(): + if item["classification"] == ItemClassification.filler: + vanilla_tech.append(item["tech_type"]) - def create_regions(self): - create_regions(self.world, self.player) + slot_data: Dict[str, Any] = { + "goal": goal.current_key, + "vanilla_tech": vanilla_tech, + "creatures_to_scan": self.creatures_to_scan + } - def fill_slot_data(self): - slot_data = {} return slot_data def create_item(self, name: str) -> Item: - item = lookup_name_to_item[name] + item_id: int = self.item_name_to_id[name] + return SubnauticaItem(name, - ItemClassification.progression if item["progression"] else ItemClassification.filler, - item["id"], player=self.player) + item_table[item_id]["classification"], + item_id, player=self.player) + def create_region(self, name: str, locations=None, exits=None): + ret = Region(name, RegionType.Generic, name, self.player) + ret.world = self.world + if locations: + for location in locations: + loc_id = self.location_name_to_id.get(location, None) + location = SubnauticaLocation(self.player, location, loc_id, ret) + ret.locations.append(location) + if exits: + for region_exit in exits: + ret.exits.append(Entrance(self.player, region_exit, ret)) + return ret -def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): - ret = Region(name, RegionType.Generic, name, player) - ret.world = world - if locations: - for location in locations: - loc_id = locations_lookup_name_to_id.get(location, 0) - location = SubnauticaLocation(player, location, loc_id, ret) - ret.locations.append(location) - if exits: - for exit in exits: - ret.exits.append(Entrance(player, exit, ret)) + def get_pre_fill_items(self) -> List[Item]: + return self.prefill_items - return ret + def pre_fill(self) -> None: + reachable = self.world.get_reachable_locations(player=self.player) + self.world.random.shuffle(reachable) + items = self.prefill_items.copy() + for item in items: + reachable.pop().place_locked_item(item) class SubnauticaLocation(Location): @@ -112,4 +156,4 @@ class SubnauticaLocation(Location): class SubnauticaItem(Item): - game = "Subnautica" + game: str = "Subnautica" diff --git a/worlds/subnautica/items.json b/worlds/subnautica/items.json deleted file mode 100644 index 2dc4b22575..0000000000 --- a/worlds/subnautica/items.json +++ /dev/null @@ -1,83 +0,0 @@ -[ - { "id": 35000, "count": 1, "progression": false, "tech_type": "Compass", "name": "Compass" }, - { "id": 35001, "count": 1, "progression": true, "tech_type": "PlasteelTank", "name": "Lightweight High Capacity Tank" }, - { "id": 35002, "count": 1, "progression": true, "tech_type": "BaseUpgradeConsole", "name": "Vehicle Upgrade Console" }, - { "id": 35003, "count": 1, "progression": true, "tech_type": "UltraGlideFins", "name": "Ultra Glide Fins" }, - { "id": 35004, "count": 1, "progression": false, "tech_type": "CyclopsSonarModule", "name": "Cyclops Sonar Upgrade" }, - { "id": 35005, "count": 1, "progression": false, "tech_type": "ReinforcedDiveSuit", "name": "Reinforced Dive Suit" }, - { "id": 35006, "count": 1, "progression": false, "tech_type": "CyclopsThermalReactorModule", "name": "Cyclops Thermal Reactor Module" }, - { "id": 35007, "count": 1, "progression": false, "tech_type": "Stillsuit", "name": "Stillsuit" }, - { "id": 35008, "count": 2, "progression": false, "tech_type": "BaseWaterParkFragment", "name": "Alien Containment Fragment" }, - { "id": 35009, "count": 1, "progression": false, "tech_type": "CyclopsDecoy", "name": "Creature Decoy" }, - { "id": 35010, "count": 1, "progression": false, "tech_type": "CyclopsFireSuppressionModule", "name": "Cyclops Fire Suppression System" }, - { "id": 35011, "count": 1, "progression": false, "tech_type": "SwimChargeFins", "name": "Swim Charge Fins" }, - { "id": 35012, "count": 1, "progression": false, "tech_type": "RepulsionCannon", "name": "Repulsion Cannon" }, - { "id": 35013, "count": 1, "progression": false, "tech_type": "CyclopsDecoyModule", "name": "Cyclops Decoy Tube Upgrade" }, - { "id": 35014, "count": 1, "progression": true, "tech_type": "CyclopsShieldModule", "name": "Cyclops Shield Generator" }, - { "id": 35015, "count": 1, "progression": true, "tech_type": "CyclopsHullModule1", "name": "Cyclops Depth Module MK1" }, - { "id": 35016, "count": 1, "progression": false, "tech_type": "CyclopsSeamothRepairModule", "name": "Cyclops Docking Bay Repair Module" }, - { "id": 35017, "count": 2, "progression": false, "tech_type": "BatteryChargerFragment", "name": "Battery Charger fragment" }, - { "id": 35018, "count": 2, "progression": false, "tech_type": "BeaconFragment", "name": "Beacon Fragment" }, - { "id": 35019, "count": 2, "progression": false, "tech_type": "BaseBioReactorFragment", "name": "Bioreactor Fragment" }, - { "id": 35020, "count": 3, "progression": true, "tech_type": "CyclopsBridgeFragment", "name": "Cyclops Bridge Fragment" }, - { "id": 35021, "count": 3, "progression": true, "tech_type": "CyclopsEngineFragment", "name": "Cyclops Engine Fragment" }, - { "id": 35022, "count": 3, "progression": true, "tech_type": "CyclopsHullFragment", "name": "Cyclops Hull Fragment" }, - { "id": 35023, "count": 2, "progression": false, "tech_type": "GravSphereFragment", "name": "Grav Trap Fragment" }, - { "id": 35024, "count": 3, "progression": true, "tech_type": "LaserCutterFragment", "name": "Laser Cutter Fragment" }, - { "id": 35025, "count": 1, "progression": false, "tech_type": "TechlightFragment", "name": "Light Stick Fragment" }, - { "id": 35026, "count": 3, "progression": true, "tech_type": "ConstructorFragment", "name": "Mobile Vehicle Bay Fragment" }, - { "id": 35027, "count": 3, "progression": true, "tech_type": "WorkbenchFragment", "name": "Modification Station Fragment" }, - { "id": 35028, "count": 2, "progression": true, "tech_type": "MoonpoolFragment", "name": "Moonpool Fragment" }, - { "id": 35029, "count": 3, "progression": false, "tech_type": "BaseNuclearReactorFragment", "name": "Nuclear Reactor Fragment" }, - { "id": 35030, "count": 2, "progression": false, "tech_type": "PowerCellChargerFragment", "name": "Power Cell Charger Fragment" }, - { "id": 35031, "count": 1, "progression": false, "tech_type": "PowerTransmitterFragment", "name": "Power Transmitter Fragment" }, - { "id": 35032, "count": 4, "progression": true, "tech_type": "ExosuitFragment", "name": "Prawn Suit Fragment" }, - { "id": 35033, "count": 2, "progression": false, "tech_type": "ExosuitDrillArmFragment", "name": "Prawn Suit Drill Arm Fragment" }, - { "id": 35034, "count": 2, "progression": false, "tech_type": "ExosuitGrapplingArmFragment", "name": "Prawn Suit Grappling Arm Fragment" }, - { "id": 35035, "count": 2, "progression": false, "tech_type": "ExosuitPropulsionArmFragment", "name": "Prawn Suit Propulsion Cannon Fragment" }, - { "id": 35036, "count": 2, "progression": false, "tech_type": "ExosuitTorpedoArmFragment", "name": "Prawn Suit Torpedo Arm Fragment" }, - { "id": 35037, "count": 3, "progression": false, "tech_type": "BaseMapRoomFragment", "name": "Scanner Room Fragment" }, - { "id": 35038, "count": 5, "progression": true, "tech_type": "SeamothFragment", "name": "Seamoth Fragment" }, - { "id": 35039, "count": 2, "progression": false, "tech_type": "StasisRifleFragment", "name": "Stasis Rifle Fragment" }, - { "id": 35040, "count": 2, "progression": false, "tech_type": "ThermalPlantFragment", "name": "Thermal Plant Fragment" }, - { "id": 35041, "count": 4, "progression": true, "tech_type": "SeaglideFragment", "name": "Seaglide Fragment" }, - { "id": 35042, "count": 1, "progression": true, "tech_type": "RadiationSuit", "name": "Radiation Suit" }, - { "id": 35043, "count": 2, "progression": true, "tech_type": "PropulsionCannonFragment", "name": "Propulsion Cannon Fragment" }, - { "id": 35044, "count": 1, "progression": true, "tech_type": "RocketBase", "name": "Neptune Launch Platform" }, - { "id": 35045, "count": 1, "progression": true, "tech_type": "PrecursorIonPowerCell", "name": "Ion Power Cell" }, - { "id": 35046, "count": 2, "progression": false, "tech_type": "FarmingTrayFragment", "name": "Exterior Growbed Fragment" }, - { "id": 35047, "count": 1, "progression": false, "tech_type": "PictureFrameFragment", "name": "Picture Frame" }, - { "id": 35048, "count": 2, "progression": false, "tech_type": "BenchFragment", "name": "Bench Fragment" }, - { "id": 35049, "count": 1, "progression": false, "tech_type": "PlanterPotFragment", "name": "Basic Plant Pot" }, - { "id": 35050, "count": 1, "progression": false, "tech_type": "PlanterBoxFragment", "name": "Interior Growbed" }, - { "id": 35051, "count": 1, "progression": false, "tech_type": "PlanterShelfFragment", "name": "Plant Shelf" }, - { "id": 35052, "count": 2, "progression": false, "tech_type": "BaseObservatoryFragment", "name": "Observatory Fragment" }, - { "id": 35053, "count": 2, "progression": false, "tech_type": "BaseRoomFragment", "name": "Multipurpose Room Fragment" }, - { "id": 35054, "count": 2, "progression": false, "tech_type": "BaseBulkheadFragment", "name": "Bulkhead Fragment" }, - { "id": 35055, "count": 1, "progression": false, "tech_type": "Spotlight", "name": "Spotlight" }, - { "id": 35056, "count": 2, "progression": false, "tech_type": "StarshipDesk", "name": "Desk" }, - { "id": 35057, "count": 1, "progression": false, "tech_type": "StarshipChair", "name": "Swivel Chair" }, - { "id": 35058, "count": 1, "progression": false, "tech_type": "StarshipChair2", "name": "Office Chair" }, - { "id": 35059, "count": 1, "progression": false, "tech_type": "StarshipChair3", "name": "Command Chair" }, - { "id": 35060, "count": 2, "progression": false, "tech_type": "LabCounter", "name": "Counter" }, - { "id": 35061, "count": 1, "progression": false, "tech_type": "NarrowBed", "name": "Single Bed" }, - { "id": 35062, "count": 1, "progression": false, "tech_type": "Bed1", "name": "Basic Double Bed" }, - { "id": 35063, "count": 1, "progression": false, "tech_type": "Bed2", "name": "Quilted Double Bed" }, - { "id": 35064, "count": 2, "progression": false, "tech_type": "CoffeeVendingMachine", "name": "Coffee Vending Machine" }, - { "id": 35065, "count": 2, "progression": false, "tech_type": "Trashcans", "name": "Trash Can" }, - { "id": 35066, "count": 1, "progression": false, "tech_type": "Techlight", "name": "Floodlight" }, - { "id": 35067, "count": 1, "progression": false, "tech_type": "BarTable", "name": "Bar Table" }, - { "id": 35068, "count": 1, "progression": false, "tech_type": "VendingMachine", "name": "Vending Machine" }, - { "id": 35069, "count": 1, "progression": false, "tech_type": "SingleWallShelf", "name": "Single Wall Shelf" }, - { "id": 35070, "count": 1, "progression": false, "tech_type": "WallShelves", "name": "Wall Shelves" }, - { "id": 35071, "count": 1, "progression": false, "tech_type": "PlanterPot2", "name": "Round Plant Pot" }, - { "id": 35072, "count": 1, "progression": false, "tech_type": "PlanterPot3", "name": "Chic Plant Pot" }, - { "id": 35073, "count": 1, "progression": false, "tech_type": "LabTrashcan", "name": "Nuclear Waste Disposal" }, - { "id": 35074, "count": 1, "progression": false, "tech_type": "BasePlanter", "name": "Wall Planter" }, - { "id": 35075, "count": 1, "progression": true, "tech_type": "PrecursorIonBattery", "name": "Ion Battery" }, - { "id": 35076, "count": 1, "progression": true, "tech_type": "RocketBaseLadder", "name": "Neptune Gantry" }, - { "id": 35077, "count": 1, "progression": true, "tech_type": "RocketStage1", "name": "Neptune Boosters" }, - { "id": 35078, "count": 1, "progression": true, "tech_type": "RocketStage2", "name": "Neptune Fuel Reserve" }, - { "id": 35079, "count": 1, "progression": true, "tech_type": "RocketStage3", "name": "Neptune Cockpit" }, - { "id": 35080, "count": 1, "progression": false, "tech_type": "BaseFiltrationMachine", "name": "Water Filtration Machine" } -] diff --git a/worlds/subnautica/locations.json b/worlds/subnautica/locations.json deleted file mode 100644 index f39e453e67..0000000000 --- a/worlds/subnautica/locations.json +++ /dev/null @@ -1,521 +0,0 @@ -[ - { "id": 33000, "position": { "x": -1234.3, "y": -349.7, "z": -396.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Blood Kelp Trench Wreck - Outside Databox" }, - - { "id": 33001, "position": { "x": -1208.0, "y": -349.6, "z": -383.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Blood Kelp Trench Wreck - Inside Databox" }, - - { "id": 33002, "position": { "x": -1210.6, "y": -340.7, "z": -393.4}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Blood Kelp Trench Wreck - PDA" }, - - { "id": 33003, "position": { "x": 903.8, "y": -220.3, "z": 590.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Bulb Zone West Wreck - Outside Databox" }, - - { "id": 33004, "position": { "x": 910.9, "y": -201.8, "z": 623.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Bulb Zone West Wreck - Under Databox" }, - - { "id": 33005, "position": { "x": 914.9, "y": -202.1, "z": 611.8}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Bulb Zone West Wreck - Inside Databox" }, - - { "id": 33006, "position": { "x": 912.6, "y": -202.0, "z": 609.5}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Bulb Zone West Wreck - PDA" }, - - { "id": 33007, "position": { "x": 1327.1, "y": -234.9, "z": 575.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Bulb Zone East Wreck - Databox" }, - - { "id": 33008, "position": { "x": -1407.7, "y": -344.2, "z": 721.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Dunes North Wreck - Outside Databox" }, - - { "id": 33009, "position": { "x": -1393.9, "y": -329.7, "z": 733.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Dunes North Wreck - Office Databox" }, - - { "id": 33010, "position": { "x": -1396.3, "y": -330.8, "z": 730.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Dunes North Wreck - PDA" }, - - { "id": 33011, "position": { "x": -1409.8, "y": -332.4, "z": 706.9}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Dunes North Wreck - Cargo Databox" }, - - { "id": 33012, "position": { "x": -1626.2, "y": -357.5, "z": 99.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Dunes West Wreck - Databox" }, - - { "id": 33013, "position": { "x": -1196.3, "y": -223.0, "z": 12.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Dunes East Wreck - Outside Databox" }, - - { "id": 33014, "position": { "x": -1206.4, "y": -225.6, "z": 4.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Dunes East Wreck - Inside Databox" }, - - { "id": 33015, "position": { "x": -269.7, "y": -262.8, "z": -764.3}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grand Reef North Wreck - Outside Databox" }, - - { "id": 33016, "position": { "x": -285.8, "y": -240.2, "z": -786.5}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Grand Reef North Wreck - Elevator Databox" }, - - { "id": 33017, "position": { "x": -285.2, "y": -262.4, "z": -788.4}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grand Reef North Wreck - Bottom Databox" }, - - { "id": 33018, "position": { "x": -272.5, "y": -254.7, "z": -788.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grand Reef North Wreck - Hangar PDA" }, - - { "id": 33019, "position": { "x": -850.9, "y": -473.2, "z": -1414.6}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grand Reef South Wreck - Trench Databox" }, - - { "id": 33020, "position": { "x": -889.4, "y": -433.8, "z": -1424.8}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Grand Reef South Wreck - Comms Databox" }, - - { "id": 33021, "position": { "x": -862.4, "y": -437.5, "z": -1444.1}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grand Reef South Wreck - Outside Databox" }, - - { "id": 33022, "position": { "x": -887.9, "y": -446.0, "z": -1422.7}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grand Reef South Wreck - PDA" }, - - { "id": 33023, "position": { "x": -23.3, "y": -105.8, "z": -604.2}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grassy Plateaus South Wreck - Databox" }, - - { "id": 33024, "position": { "x": -27.3, "y": -106.8, "z": -607.2}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grassy Plateaus South Wreck - PDA" }, - - { "id": 33025, "position": { "x": 313.9, "y": -91.8, "z": 432.6}, - "need_laser_cutter": true, "can_slip_through": true, - "name": "Grassy Plateaus East Wreck - Breach Databox" }, - - { "id": 33026, "position": { "x": 319.4, "y": -104.3, "z": 441.5}, - "need_laser_cutter": true, "can_slip_through": true, - "name": "Grassy Plateaus East Wreck - Hangar Databox" }, - - { "id": 33027, "position": { "x": -632.3, "y": -75.0, "z": -8.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grassy Plateaus West Wreck - Locker PDA" }, - - { "id": 33028, "position": { "x": -664.4, "y": -97.8, "z": -8.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Grassy Plateaus West Wreck - Data Terminal" }, - - { "id": 33029, "position": { "x": -421.4, "y": -107.8, "z": -266.5}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Grassy Plateaus West Wreck - Databox" }, - - { "id": 33030, "position": { "x": -44.0, "y": -29.1, "z": -403.6}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Safe Shallows Wreck - PDA" }, - - { "id": 33031, "position": { "x": -317.1, "y": -79.0, "z": 248.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Kelp Forest Wreck - Databox" }, - - { "id": 33032, "position": { "x": 63.2, "y": -38.5, "z": 382.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Kelp Forest Wreck - PDA" }, - - { "id": 33033, "position": { "x": 740.3, "y": -389.2, "z": 1179.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Mountains West Wreck - Outside Databox" }, - - { "id": 33034, "position": { "x": 703.7, "y": -365.9, "z": 1199.3}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Mountains West Wreck - Data Terminal" }, - - { "id": 33035, "position": { "x": 698.2, "y": -350.8, "z": 1186.9}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Mountains West Wreck - Hangar Databox" }, - - { "id": 33036, "position": { "x": 676.3, "y": -343.6, "z": 1204.6}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Mountains West Wreck - Office Databox" }, - - { "id": 33037, "position": { "x": 1068.5, "y": -283.4, "z": 1345.3}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Mountains East Wreck - Comms Databox" }, - - { "id": 33038, "position": { "x": 1075.7, "y": -288.9, "z": 1321.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Mountains East Wreck - Outside Databox" }, - - { "id": 33039, "position": { "x": -655.1, "y": -109.6, "z": 791.0}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Northwestern Mushroom Forest Wreck - Cargo Databox" }, - - { "id": 33040, "position": { "x": -663.4, "y": -111.9, "z": 777.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Northwestern Mushroom Forest Wreck - Office Databox" }, - - { "id": 33041, "position": { "x": -662.2, "y": -113.4, "z": 777.7}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Northwestern Mushroom Forest Wreck - PDA" }, - - { "id": 33042, "position": { "x": -1161.1, "y": -191.7, "z": -758.3}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Sea Treader's Path Wreck - Outside Databox" }, - - { "id": 33043, "position": { "x": -1129.5, "y": -155.2, "z": -729.3}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Sea Treader's Path Wreck - Hangar Databox" }, - - { "id": 33044, "position": { "x": -1115.9, "y": -175.3, "z": -724.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Sea Treader's Path Wreck - Lobby Databox" }, - - { "id": 33045, "position": { "x": -1136.8, "y": -157.0, "z": -734.6}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Sea Treader's Path Wreck - PDA" }, - - { "id": 33046, "position": { "x": -789.8, "y": -216.1, "z": -711.0}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Sparse Reef Wreck - Locker Databox" }, - - { "id": 33047, "position": { "x": -810.7, "y": -209.3, "z": -685.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Sparse Reef Wreck - Outside Databox" }, - - { "id": 33048, "position": { "x": -795.5, "y": -204.1, "z": -774.7}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Sparse Reef Wreck - Lab Databox" }, - - { "id": 33049, "position": { "x": -170.8, "y": -187.6, "z": 880.7}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Underwater Islands Wreck - Outside Databox" }, - - { "id": 33050, "position": { "x": -138.4, "y": -193.6, "z": 888.7}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Underwater Islands Wreck - Hangar Databox" }, - - { "id": 33051, "position": { "x": -130.7, "y": -193.2, "z": 883.3}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Underwater Islands Wreck - Data Terminal" }, - - { "id": 33052, "position": { "x": -137.8, "y": -193.4, "z": 879.4}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Underwater Islands Wreck - Cable Databox" }, - - { "id": 33053, "position": { "x": -124.4, "y": -200.7, "z": 853.0}, - "need_laser_cutter": false, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Underwater Islands Wreck - Pipes Databox 1" }, - - { "id": 33054, "position": { "x": -126.8, "y": -201.1, "z": 852.1}, - "need_laser_cutter": false, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Underwater Islands Wreck - Pipes Databox 2" }, - - { "id": 33055, "position": { "x": -643.8, "y": -509.9, "z": -941.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Deep Grand Reef - Bedroom Databox" }, - - { "id": 33056, "position": { "x": -635.1, "y": -502.7, "z": -951.4}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Deep Grand Reef - Observatory Databox" }, - - { "id": 33057, "position": { "x": -645.8, "y": -508.7, "z": -943.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Deep Grand Reef - Bedroom PDA" }, - - { "id": 33058, "position": { "x": -630.5, "y": -511.1, "z": -936.1}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Deep Grand Reef - Outside PDA" }, - - { "id": 33059, "position": { "x": -647.7, "y": -502.6, "z": -935.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Deep Grand Reef - Observatory PDA" }, - - { "id": 33060, "position": { "x": -639.6, "y": -505.9, "z": -946.6}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Deep Grand Reef - Lab PDA" }, - - { "id": 33061, "position": { "x": -707.2, "y": 0.5, "z": -1096.7}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Floating Island - Lake PDA" }, - - { "id": 33062, "position": { "x": -765.7, "y": 17.6, "z": -1116.4}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Floating Island - Databox" }, - - { "id": 33063, "position": { "x": -754.9, "y": 14.6, "z": -1108.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Floating Island - Room PDA" }, - - { "id": 33064, "position": { "x": -765.3, "y": 14.1, "z": -1115.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Floating Island - Green Wall PDA" }, - - { "id": 33065, "position": { "x": -758.6, "y": 14.1, "z": -1111.3}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Floating Island - Corridor PDA" }, - - { "id": 33066, "position": { "x": -805.4, "y": 76.9, "z": -1055.7}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Floating Island - North Observatory PDA" }, - - { "id": 33067, "position": { "x": -715.9, "y": 75.4, "z": -1168.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Floating Island - South Observatory PDA" }, - - { "id": 33068, "position": { "x": -540.5, "y": -250.8, "z": -83.4}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Jellyshroom Cave - PDA" }, - - { "id": 33069, "position": { "x": 110.6, "y": -264.9, "z": -369.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Jellyshroom Cave - Bedroom Databox" }, - - { "id": 33070, "position": { "x": 80.6, "y": -268.6, "z": -358.3}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Jellyshroom Cave - Detached PDA" }, - - { "id": 33071, "position": { "x": 78.2, "y": -265.0, "z": -373.4}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Jellyshroom Cave - Office PDA" }, - - { "id": 33072, "position": { "x": 85.1, "y": -264.1, "z": -372.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Jellyshroom Cave - Locker PDA" }, - - { "id": 33073, "position": { "x": 112.3, "y": -264.9, "z": -369.3}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Jellyshroom Cave - Bedroom PDA" }, - - { "id": 33074, "position": { "x": 95.5, "y": -258.9, "z": -366.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Degasi Seabase - Jellyshroom Cave - Observatory PDA" }, - - { "id": 33075, "position": { "x": -483.6, "y": -504.7, "z": 1326.6}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 2 - Databox" }, - - { "id": 33076, "position": { "x": -481.4, "y": -503.6, "z": 1324.1}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 2 - PDA" }, - - { "id": 33077, "position": { "x": -34.2, "y": -22.4, "z": 410.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 3 - Databox" }, - - { "id": 33078, "position": { "x": -33.8, "y": -22.5, "z": 408.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 3 - PDA" }, - - { "id": 33079, "position": { "x": 712.4, "y": -3.4, "z": 160.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 4 - Databox" }, - - { "id": 33080, "position": { "x": 712.0, "y": -3.5, "z": 161.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 4 - PDA" }, - - { "id": 33081, "position": { "x": 358.7, "y": -117.1, "z": 306.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 6 - Databox" }, - - { "id": 33082, "position": { "x": 361.8, "y": -116.2, "z": 309.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 6 - Inside PDA" }, - - { "id": 33083, "position": { "x": 359.9, "y": -117.0, "z": 312.1}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 6 - Outside PDA" }, - - { "id": 33084, "position": { "x": -56.0, "y": -182.0, "z": -1039.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 7 - PDA" }, - - { "id": 33085, "position": { "x": 1119.5, "y": -271.7, "z": 561.7}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 12 - Databox" }, - - { "id": 33086, "position": { "x": 1116.1, "y": -271.3, "z": 566.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 12 - PDA" }, - - { "id": 33087, "position": { "x": -926.4, "y": -185.2, "z": 501.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 13 - Databox" }, - - { "id": 33088, "position": { "x": -926.8, "y": -184.4, "z": 506.6}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 13 - PDA" }, - - { "id": 33089, "position": { "x": -514.5, "y": -98.1, "z": -56.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 17 - PDA" }, - - { "id": 33090, "position": { "x": -809.8, "y": -302.2, "z": -876.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 19 - Databox" }, - - { "id": 33091, "position": { "x": -806.1, "y": -294.1, "z": -866.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 19 - Outside PDA" }, - - { "id": 33092, "position": { "x": -810.5, "y": -299.4, "z": -873.1}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lifepod 19 - Inside PDA" }, - - { "id": 33093, "position": { "x": 903.5, "y": -0.2, "z": 16.1}, - "need_laser_cutter": false, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora Seamoth Bay - Upgrade Console" }, - - { "id": 33094, "position": { "x": 872.5, "y": 2.7, "z": -0.7}, - "need_laser_cutter": false, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora Drive Room - Upgrade Console" }, - - { "id": 33095, "position": { "x": 991.6, "y": 3.2, "z": -31.0}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora Prawn Suit Bay - Upgrade Console" }, - - { "id": 33096, "position": { "x": 952.1, "y": 41.2, "z": 113.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Aurora - Office PDA" }, - - { "id": 33097, "position": { "x": 977.2, "y": 39.1, "z": 83.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Aurora - Corridor PDA" }, - - { "id": 33098, "position": { "x": 954.9, "y": 11.2, "z": 3.4}, - "need_laser_cutter": false, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Cargo Bay PDA" }, - - { "id": 33099, "position": { "x": 907.1, "y": -1.5, "z": 15.3}, - "need_laser_cutter": false, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Seamoth Bay PDA" }, - - { "id": 33100, "position": { "x": 951.8, "y": -2.3, "z": -34.7}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Medkit Locker PDA" }, - - { "id": 33101, "position": { "x": 952.0, "y": -3.7, "z": -23.4}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Locker PDA" }, - - { "id": 33102, "position": { "x": 986.5, "y": 9.6, "z": -48.6}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Canteen PDA" }, - - { "id": 33103, "position": { "x": 951.3, "y": 11.2, "z": -51.0}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Cabin 4 PDA" }, - - { "id": 33104, "position": { "x": 967.1, "y": 10.4, "z": -47.4}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Cabin 7 PDA" }, - - { "id": 33105, "position": { "x": 964.1, "y": 11.1, "z": -61.9}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Cabin 1 PDA" }, - - { "id": 33106, "position": { "x": 971.2, "y": 10.8, "z": -70.4}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Captain PDA" }, - - { "id": 33107, "position": { "x": 1033.6, "y": -8.5, "z": 16.2}, - "need_laser_cutter": false, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Ring PDA" }, - - { "id": 33108, "position": { "x": 1032.5, "y": -7.8, "z": 32.4}, - "need_laser_cutter": false, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Lab PDA" }, - - { "id": 33109, "position": { "x": 945.8, "y": 40.8, "z": 115.1}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Aurora - Office Data Terminal" }, - - { "id": 33110, "position": { "x": 974.8, "y": 10.0, "z": -77.0}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Captain Data Terminal" }, - - { "id": 33111, "position": { "x": 1040.8, "y": -11.4, "z": -3.4}, - "need_laser_cutter": true, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Battery Room Data Terminal" }, - - { "id": 33112, "position": { "x": 1029.5, "y": -8.7, "z": 35.9}, - "need_laser_cutter": false, "can_slip_through": false, "need_propulsion_cannon": true, - "name": "Aurora - Lab Data Terminal" }, - - { "id": 33113, "position": { "x": 432.2, "y": 3.0, "z": 1193.2}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Quarantine Enforcement Platform's - Upper Alien Data Terminal" }, - - { "id": 33114, "position": { "x": 474.4, "y": -4.5, "z": 1224.4}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Quarantine Enforcement Platform's - Mid Alien Data Terminal" }, - - { "id": 33115, "position": { "x": -1224.2, "y": -400.4, "z": 1057.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Dunes Sanctuary - Alien Data Terminal" }, - - { "id": 33116, "position": { "x": -895.5, "y": -311.6, "z": -838.1}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Deep Sparse Reef Sanctuary - Alien Data Terminal" }, - - { "id": 33117, "position": { "x": -642.9, "y": -563.5, "z": 1485.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Northern Blood Kelp Zone Sanctuary - Alien Data Terminal" }, - - { "id": 33118, "position": { "x": -1112.3, "y": -687.3, "z": -695.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Lost River Laboratory Cache - Alien Data Terminal" }, - - { "id": 33119, "position": { "x": -280.2, "y": -804.3, "z": 305.1}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Disease Research Facility - Upper Alien Data Terminal" }, - - { "id": 33120, "position": { "x": -267.9, "y": -806.6, "z": 250.0}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Disease Research Facility - Mid Alien Data Terminal" }, - - { "id": 33121, "position": { "x": -286.2, "y": -815.6, "z": 297.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Disease Research Facility - Lower Alien Data Terminal" }, - - { "id": 33122, "position": { "x": -71.3, "y": -1227.2, "z": 104.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Alien Thermal Plant - Entrance Alien Data Terminal" }, - - { "id": 33123, "position": { "x": -38.7, "y": -1226.6, "z": 111.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Alien Thermal Plant - Green Alien Data Terminal" }, - - { "id": 33124, "position": { "x": -30.4, "y": -1220.3, "z": 111.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Alien Thermal Plant - Yellow Alien Data Terminal" }, - - { "id": 33125, "position": { "x": 245.8, "y": -1430.6, "z": -311.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Primary Containment Facility's Antechamber - Alien Data Terminal" }, - - { "id": 33126, "position": { "x": 165.5, "y": -1442.4, "z": -385.8}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Primary Containment Facility's Egg Laboratory - Alien Data Terminal" }, - - { "id": 33127, "position": { "x": 348.7, "y": -1443.5, "z": -291.9}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Primary Containment Facility's Pipe Room - Alien Data Terminal" }, - - { "id": 33128, "position": { "x": -641.8, "y": -111.3, "z": -19.7}, - "need_laser_cutter": true, "can_slip_through": false, - "name": "Grassy Plateaus West Wreck - Beam PDA" }, - - { "id": 33129, "position": { "x": -748.9, "y": 14.4, "z": -1179.5}, - "need_laser_cutter": false, "can_slip_through": false, - "name": "Floating Island - Cave Entrance PDA" } -] diff --git a/worlds/witness/Door_Shuffle.txt b/worlds/witness/Door_Shuffle.txt deleted file mode 100644 index 7d48064cc8..0000000000 --- a/worlds/witness/Door_Shuffle.txt +++ /dev/null @@ -1,30 +0,0 @@ -100 - 0x01A54 - None - Glass Factory Entry Door -105 - 0x000B0 - 0x0343A - Door to Symmetry Island Lower -107 - 0x1C349 - 0x00076 - Door to Symmetry Island Upper -110 - 0x0C339 - 0x09F94 - Door to Desert Flood Light Room -111 - 0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B - None - Desert Flood Room Flood Controls -120 - 0x03678 - None - Quarry Mill Ramp Control -122 - 0x03679 - 0x014E8 - Quarry Mill Elevator Control -125 - 0x03852 - 0x034D4,0x021D5 - Quarry Boathouse Ramp Height Control -127 - 0x03858 - 0x021AE - Quarry Boathouse Ramp Horizontal Control -131 - 0x334DB,0x334DC - None - Shadows Door Timer -150 - 0x00B10 - None - Monastery Entry Door Left -151 - 0x00C92 - None - Monastery Entry Door Right -162 - 0x28998 - None - Town Door to RGB House -163 - 0x28A0D - 0x28998 - Town Door to Church -166 - 0x28A79 - None - Town Maze Panel (Drop-Down Staircase) -169 - 0x17F5F - None - Windmill Door -200 - 0x0288C - None - Treehouse First & Second Door -202 - 0x0A182 - None - Treehouse Third Door -205 - 0x2700B - None - Treehouse Laser House Door Timer -208 - 0x17CBC - None - Treehouse Shortcut Drop-Down Bridge -175 - 0x17CAB - 0x002C7 - Jungle Popup Wall -180 - 0x17C2E - None - Bunker Entry Door -183 - 0x0A099 - 0x09DAF - Inside Bunker Door to Bunker Proper -186 - 0x0A079 - None - Bunker Elevator Control -190 - 0x0056E - None - Swamp Entry Door -192 - 0x00609,0x18488 - 0x181A9 - Swamp Sliding Bridge -195 - 0x181F5 - None - Swamp Rotating Bridge -197 - 0x17C0A - None - Swamp Maze Control -300 - 0x0042D - None - Mountaintop River Shape Panel (Shortcut to Secret Area) -310 - 0x17CDF,0x17CC8,0x17CA6,0x09DB8,0x17C95,0x0A054 - None - Boat diff --git a/worlds/witness/Early_UTM.txt b/worlds/witness/Early_UTM.txt deleted file mode 100644 index 57da491e35..0000000000 --- a/worlds/witness/Early_UTM.txt +++ /dev/null @@ -1,5 +0,0 @@ -Event Items: -Shortcut to Secret Area Opens - 0x0042D - -Region Changes: -Inside Mountain Secret Area (Inside Mountain Secret Area) - Inside Mountain Path to Secret Area - 0x00FF8 - Main Island - 0x021D7 | 0x0042D - Main Island - 0x17CF2 \ No newline at end of file diff --git a/worlds/witness/Options.py b/worlds/witness/Options.py index e8b7e576da..2bfe3f41e1 100644 --- a/worlds/witness/Options.py +++ b/worlds/witness/Options.py @@ -7,37 +7,48 @@ from Options import Toggle, DefaultOnToggle, Option, Range, Choice # "Play the randomizer in hardmode" # display_name = "Hard Mode" -# class UnlockSymbols(DefaultOnToggle): -# "All Puzzle symbols of a specific panel need to be unlocked before the panel can be used" -# display_name = "Unlock Symbols" - class DisableNonRandomizedPuzzles(DefaultOnToggle): - """Disable puzzles that cannot be randomized. - Non randomized puzzles are Shadows, Monastery, and Greenhouse. + """Disables puzzles that cannot be randomized. + This includes many puzzles that heavily involve the environment, such as Shadows, Monastery or Orchard. The lasers for those areas will be activated as you solve optional puzzles throughout the island.""" display_name = "Disable non randomized puzzles" class EarlySecretArea(Toggle): - """The Mountainside shortcut to the Mountain Secret Area is open from the start. + """Opens the Mountainside shortcut to the Mountain Secret Area from the start. (Otherwise known as "UTM", "Caves" or the "Challenge Area")""" display_name = "Early Secret Area" class ShuffleSymbols(DefaultOnToggle): - """You will need to unlock puzzle symbols as items to be able to solve the panels that contain those symbols.""" + """You will need to unlock puzzle symbols as items to be able to solve the panels that contain those symbols. + If you turn this off, there will be no progression items in the game unless you turn on door shuffle.""" display_name = "Shuffle Symbols" -class ShuffleDoors(Toggle): - """Many doors around the island will have their panels turned off initially. - You will need to find the items that power the panels to open those doors.""" +class ShuffleLasers(Toggle): + """If on, the 11 lasers are turned into items and will activate on their own upon receiving them. + Note: There is a visual bug that can occur with the Desert Laser. It does not affect gameplay - The Laser can still + be redirected as normal, for both applications of redirection.""" + display_name = "Shuffle Lasers" + + +class ShuffleDoors(Choice): + """If on, opening doors will require their respective "keys". + If set to "panels", those keys will unlock the panels on doors. + In "doors_simple" and "doors_complex", the doors will magically open by themselves upon receiving the key.""" display_name = "Shuffle Doors" + option_none = 0 + option_panels = 1 + option_doors_simple = 2 + option_doors_complex = 3 + option_max = 4 class ShuffleDiscardedPanels(Toggle): - """Discarded Panels will have items on them. - Solving certain Discarded Panels may still be necessary even if off!""" + """Add Discarded Panels into the location pool. + Solving certain Discarded Panels may still be necessary to beat the game, even if this is off.""" + display_name = "Shuffle Discarded Panels" @@ -52,9 +63,10 @@ class ShuffleUncommonLocations(Toggle): display_name = "Shuffle Uncommon Locations" -class ShuffleHardLocations(Toggle): - """Adds some harder locations into the game, e.g. Mountain Secret Area panels""" - display_name = "Shuffle Hard Locations" +class ShufflePostgame(Toggle): + """Adds locations into the pool that are guaranteed to be locked behind your goal. Use this if you don't play with + forfeit on victory.""" + display_name = "Shuffle Postgame" class VictoryCondition(Choice): @@ -103,16 +115,17 @@ class PuzzleSkipAmount(Range): the_witness_options: Dict[str, type] = { # "hard_mode": HardMode, + "shuffle_symbols": ShuffleSymbols, + "shuffle_doors": ShuffleDoors, + "shuffle_lasers": ShuffleLasers, "disable_non_randomized_puzzles": DisableNonRandomizedPuzzles, "shuffle_discarded_panels": ShuffleDiscardedPanels, "shuffle_vault_boxes": ShuffleVaultBoxes, "shuffle_uncommon": ShuffleUncommonLocations, - "shuffle_hard": ShuffleHardLocations, + "shuffle_postgame": ShufflePostgame, "victory_condition": VictoryCondition, "trap_percentage": TrapPercentage, "early_secret_area": EarlySecretArea, - # "shuffle_symbols": ShuffleSymbols, - # "shuffle_doors": ShuffleDoors, "mountain_lasers": MountainLasers, "challenge_lasers": ChallengeLasers, "puzzle_skip_amount": PuzzleSkipAmount, diff --git a/worlds/witness/WitnessItems.txt b/worlds/witness/WitnessItems.txt index 9d8831bb10..5631ab2f41 100644 --- a/worlds/witness/WitnessItems.txt +++ b/worlds/witness/WitnessItems.txt @@ -1,6 +1,8 @@ Progression: 0 - Dots 1 - Colored Dots +2 - Full Dots +3 - Invisible Dots 5 - Sound Dots 10 - Symmetry 20 - Triangles @@ -12,6 +14,7 @@ Progression: 61 - Stars + Same Colored Symbol 71 - Black/White Squares 72 - Colored Squares +80 - Arrows Usefuls: 101 - Functioning Brain - False @@ -23,3 +26,173 @@ Boosts: Traps: 600 - Slowness 610 - Power Surge + +Doors: +1100 - Glass Factory Entry Door (Panel) - 0x01A54 +1105 - Door to Symmetry Island Lower (Panel) - 0x000B0 +1107 - Door to Symmetry Island Upper (Panel) - 0x1C349 +1110 - Door to Desert Flood Light Room (Panel) - 0x0C339 +1111 - Desert Flood Room Flood Controls (Panel) - 0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B +1119 - Quarry Door to Mill (Panel) - 0x01E5A,0x01E59 +1120 - Quarry Mill Ramp Controls (Panel) - 0x03678,0x03676 +1122 - Quarry Mill Elevator Controls (Panel) - 0x03679,0x03675 +1125 - Quarry Boathouse Ramp Height Control (Panel) - 0x03852 +1127 - Quarry Boathouse Ramp Horizontal Control (Panel) - 0x03858 +1131 - Shadows Door Timer (Panel) - 0x334DB,0x334DC +1150 - Monastery Entry Door Left (Panel) - 0x00B10 +1151 - Monastery Entry Door Right (Panel) - 0x00C92 +1162 - Town Door to RGB House (Panel) - 0x28998 +1163 - Town Door to Church (Panel) - 0x28A0D +1166 - Town Maze Panel (Drop-Down Staircase) (Panel) - 0x28A79 +1169 - Windmill Door (Panel) - 0x17F5F +1200 - Treehouse First & Second Doors (Panel) - 0x0288C,0x02886 +1202 - Treehouse Third Door (Panel) - 0x0A182 +1205 - Treehouse Laser House Door Timer (Panel) - 0x2700B,0x334DC +1208 - Treehouse Shortcut Drop-Down Bridge (Panel) - 0x17CBC +1175 - Jungle Popup Wall (Panel) - 0x17CAB +1180 - Bunker Entry Door (Panel) - 0x17C2E +1183 - Inside Bunker Door to Bunker Proper (Panel) - 0x0A099 +1186 - Bunker Elevator Control (Panel) - 0x0A079 +1190 - Swamp Entry Door (Panel) - 0x0056E +1192 - Swamp Sliding Bridge (Panel) - 0x00609,0x18488 +1195 - Swamp Rotating Bridge (Panel) - 0x181F5 +1197 - Swamp Maze Control (Panel) - 0x17C0A +1310 - Boat - 0x17CDF,0x17CC8,0x17CA6,0x09DB8,0x17C95,0x0A054 + +1400 - Caves Mountain Shortcut - 0x2D73F + +1500 - Symmetry Laser - 0x00509 +1501 - Desert Laser - 0x012FB,0x01317 +1502 - Quarry Laser - 0x01539 +1503 - Shadows Laser - 0x181B3 +1504 - Keep Laser - 0x014BB +1505 - Monastery Laser - 0x17C65 +1506 - Town Laser - 0x032F9 +1507 - Jungle Laser - 0x00274 +1508 - Bunker Laser - 0x0C2B2 +1509 - Swamp Laser - 0x00BF6 +1510 - Treehouse Laser - 0x028A4 + +1600 - Outside Tutorial Optional Door - 0x03BA2 +1603 - Outside Tutorial Outpost Entry Door - 0x0A170 +1606 - Outside Tutorial Outpost Exit Door - 0x04CA3 +1609 - Glass Factory Entry Door - 0x01A29 +1612 - Glass Factory Back Wall - 0x0D7ED +1615 - Symmetry Island Lower Door - 0x17F3E +1618 - Symmetry Island Upper Door - 0x18269 +1619 - Orchard Middle Gate - 0x03307 +1620 - Orchard Final Gate - 0x03313 +1621 - Desert Door to Flood Light Room - 0x09FEE +1624 - Desert Door to Pond Room - 0x0C2C3 +1627 - Desert Door to Water Levels Room - 0x0A24B +1630 - Desert Door to Elevator Room - 0x0C316 +1633 - Quarry Main Entry 1 - 0x09D6F +1636 - Quarry Main Entry 2 - 0x17C07 +1639 - Quarry Door to Mill - 0x02010 +1642 - Quarry Mill Side Door - 0x275FF +1645 - Quarry Mill Rooftop Shortcut - 0x17CE8 +1648 - Quarry Mill Stairs - 0x0368A +1651 - Quarry Boathouse Boat Staircase - 0x2769B,0x27163 +1653 - Quarry Boathouse First Barrier - 0x17C50 +1654 - Quarry Boathouse Shortcut - 0x3865F +1656 - Shadows Timed Door - 0x19B24 +1657 - Shadows Laser Room Right Door - 0x194B2 +1660 - Shadows Laser Room Left Door - 0x19665 +1663 - Shadows Barrier to Quarry - 0x19865,0x0A2DF +1666 - Shadows Barrier to Ledge - 0x1855B,0x19ADE +1669 - Keep Hedge Maze 1 Exit Door - 0x01954 +1672 - Keep Pressure Plates 1 Exit Door - 0x01BEC +1675 - Keep Hedge Maze 2 Shortcut - 0x018CE +1678 - Keep Hedge Maze 2 Exit Door - 0x019D8 +1681 - Keep Hedge Maze 3 Shortcut - 0x019B5 +1684 - Keep Hedge Maze 3 Exit Door - 0x019E6 +1687 - Keep Hedge Maze 4 Shortcut - 0x0199A +1690 - Keep Hedge Maze 4 Exit Door - 0x01A0E +1693 - Keep Pressure Plates 2 Exit Door - 0x01BEA +1696 - Keep Pressure Plates 3 Exit Door - 0x01CD5 +1699 - Keep Pressure Plates 4 Exit Door - 0x01D40 +1702 - Keep Shortcut to Shadows - 0x09E3D +1705 - Keep Tower Shortcut - 0x04F8F +1708 - Monastery Shortcut - 0x0364E +1711 - Monastery Inner Door - 0x0C128 +1714 - Monastery Outer Door - 0x0C153 +1717 - Monastery Door to Garden - 0x03750 +1718 - Town Cargo Box Door - 0x0A0C9 +1720 - Town Wooden Roof Staircase - 0x034F5 +1723 - Town Tinted Door to RGB House - 0x28A61 +1726 - Town Door to Church - 0x03BB0 +1729 - Town Maze Staircase - 0x28AA2 +1732 - Town Windmill Door - 0x1845B +1735 - Town RGB House Staircase - 0x2897B +1738 - Town Tower Blue Panels Door - 0x27798 +1741 - Town Tower Lattice Door - 0x27799 +1744 - Town Tower Environmental Set Door - 0x2779A +1747 - Town Tower Wooden Roof Set Door - 0x2779C +1750 - Theater Entry Door - 0x17F88 +1753 - Theater Exit Door Left - 0x0A16D +1756 - Theater Exit Door Right - 0x3CCDF +1759 - Jungle Bamboo Shortcut to River - 0x3873B +1760 - Jungle Popup Wall - 0x1475B +1762 - River Shortcut to Monastery Garden - 0x0CF2A +1765 - Bunker Bunker Entry Door - 0x0C2A4 +1768 - Bunker Tinted Glass Door - 0x17C79 +1771 - Bunker Door to Ultraviolet Room - 0x0C2A3 +1774 - Bunker Door to Elevator - 0x0A08D +1777 - Swamp Entry Door - 0x00C1C +1780 - Swamp Door to Broken Shapers - 0x184B7 +1783 - Swamp Platform Shortcut Door - 0x38AE6 +1786 - Swamp Cyan Water Pump - 0x04B7F +1789 - Swamp Door to Rotated Shapers - 0x18507 +1792 - Swamp Red Water Pump - 0x183F2 +1795 - Swamp Red Underwater Exit - 0x305D5 +1798 - Swamp Blue Water Pump - 0x18482 +1801 - Swamp Purple Water Pump - 0x0A1D6 +1804 - Swamp Near Laser Shortcut - 0x2D880 +1807 - Treehouse First Door - 0x0C309 +1810 - Treehouse Second Door - 0x0C310 +1813 - Treehouse Beyond Yellow Bridge Door - 0x0A181 +1816 - Treehouse Drawbridge - 0x0C32D +1819 - Treehouse Timed Door to Laser House - 0x0C323 +1822 - Inside Mountain First Layer Exit Door - 0x09E54 +1825 - Inside Mountain Second Layer Staircase Near - 0x09FFB +1828 - Inside Mountain Second Layer Exit Door - 0x09EDD +1831 - Inside Mountain Second Layer Staircase Far - 0x09E07 +1834 - Inside Mountain Giant Puzzle Exit Door - 0x09F89 +1840 - Inside Mountain Door to Final Room - 0x0C141 +1843 - Inside Mountain Bottom Layer Rock - 0x17F33 +1846 - Inside Mountain Door to Secret Area - 0x2D77D +1849 - Caves Pillar Door - 0x019A5 +1855 - Caves Swamp Shortcut - 0x2D859 +1858 - Challenge Entry Door - 0x0A19A +1861 - Challenge Door to Theater Walkway - 0x0348A +1864 - Theater Walkway Door to Windmill Interior - 0x27739 +1867 - Theater Walkway Door to Desert Elevator Room - 0x27263 +1870 - Theater Walkway Door to Town - 0x09E87 + +1903 - Outside Tutorial Outpost Doors - 0x03BA2,0x0A170,0x04CA3 +1906 - Symmetry Island Doors - 0x17F3E,0x18269 +1909 - Orchard Gates - 0x03313,0x03307 +1912 - Desert Doors - 0x09FEE,0x0C2C3,0x0A24B,0x0C316 +1915 - Quarry Main Entry - 0x09D6F +1918 - Quarry Mill Shortcuts - 0x17C07,0x17CE8,0x0368A +1921 - Quarry Boathouse Barriers - 0x17C50,0x3865F +1924 - Shadows Laser Room Door - 0x194B2,0x19665 +1927 - Shadows Barriers - 0x19865,0x0A2DF,0x1855B,0x19ADE +1930 - Keep Hedge Maze Doors - 0x01954,0x018CE,0x019D8,0x019B5,0x019E6,0x0199A,0x01A0E +1933 - Keep Pressure Plates Doors - 0x01BEC,0x01BEA,0x01CD5,0x01D40 +1936 - Keep Shortcuts - 0x09E3D,0x04F8F +1939 - Monastery Entry Door - 0x0C128,0x0C153 +1942 - Monastery Shortcuts - 0x0364E,0x03750 +1945 - Town Doors - 0x0A0C9,0x034F5,0x28A61,0x03BB0,0x28AA2,0x1845B,0x2897B +1948 - Town Tower Doors - 0x27798,0x27799,0x2779A,0x2779C +1951 - Theater Exit Door - 0x0A16D,0x3CCDF +1954 - Jungle & River Shortcuts - 0x3873B,0x0CF2A +1957 - Bunker Doors - 0x0C2A4,0x17C79,0x0C2A3,0x0A08D +1960 - Swamp Doors - 0x00C1C,0x184B7,0x38AE6,0x18507 +1963 - Swamp Water Pumps - 0x04B7F,0x183F2,0x305D5,0x18482,0x0A1D6 +1966 - Treehouse Entry Doors - 0x0C309,0x0C310,0x0A181 +1975 - Inside Mountain Second Layer Stairs & Doors - 0x09FFB,0x09EDD,0x09E07 +1978 - Inside Mountain Bottom Layer Doors to Caves - 0x17F33,0x2D77D +1981 - Caves Doors to Challenge - 0x019A5,0x0A19A +1984 - Caves Exits to Main Island - 0x2D859,0x2D73F +1987 - Theater Walkway Doors - 0x27739,0x27263,0x09E87 \ No newline at end of file diff --git a/worlds/witness/WitnessLogic.txt b/worlds/witness/WitnessLogic.txt index e4e63dc434..350f72b680 100644 --- a/worlds/witness/WitnessLogic.txt +++ b/worlds/witness/WitnessLogic.txt @@ -1,712 +1,924 @@ -First Hallway (First Hallway) - Entry - True: -0x00064 (Straight) - True - True -0x00182 (Bend) - 0x00064 - True +First Hallway (First Hallway) - Entry - True - Tutorial - 0x00182: +158000 - 0x00064 (Straight) - True - True +158001 - 0x00182 (Bend) - 0x00064 - True -Tutorial (Tutorial) - First Hallway - 0x00182: -0x00293 (Front Center) - True - True -0x00295 (Center Left) - 0x00293 - True -0x002C2 (Front Left) - 0x00295 - True -0x0A3B5 (Back Left) - True - True -0x0A3B2 (Back Right) - True - True -0x03629 (Gate Open) - 0x002C2 & 0x0A3B5 & 0x0A3B2 - True -0x03505 (Gate Close) - 0x2FAF6 - True -0x0C335 (Pillar) - True - Triangles - True -0x0C373 (Patio Floor) - 0x0C335 - Dots +Tutorial (Tutorial) - Outside Tutorial - 0x03629: +158002 - 0x00293 (Front Center) - True - True +158003 - 0x00295 (Center Left) - 0x00293 - True +158004 - 0x002C2 (Front Left) - 0x00295 - True +158005 - 0x0A3B5 (Back Left) - True - True +158006 - 0x0A3B2 (Back Right) - True - True +158007 - 0x03629 (Gate Open) - 0x002C2 & 0x0A3B5 & 0x0A3B2 - True +158008 - 0x03505 (Gate Close) - 0x2FAF6 - True +158009 - 0x0C335 (Pillar) - True - Triangles - True +158010 - 0x0C373 (Patio Floor) - 0x0C335 - Dots -Outside Tutorial (Outside Tutorial) - Tutorial - 0x03629: -0x033D4 (Vault) - True - Dots & Squares & Black/White Squares -0x03481 (Vault Box) - 0x033D4 - True -0x0A171 (Optional Door 1) - 0x0A3B5 - Dots -0x17CFB (Discard) - 0x0A171 - Triangles -0x04CA4 (Optional Door 2) - 0x0A171 - Dots & Squares & Black/White Squares -0x0005D (Dots Introduction 1) - True - Dots -0x0005E (Dots Introduction 2) - 0x0005D - Dots -0x0005F (Dots Introduction 3) - 0x0005E - Dots -0x00060 (Dots Introduction 4) - 0x0005F - Dots -0x00061 (Dots Introduction 5) - 0x00060 - Dots -0x018AF (Squares Introduction 1) - True - Squares & Black/White Squares -0x0001B (Squares Introduction 2) - 0x018AF - Squares & Black/White Squares -0x012C9 (Squares Introduction 3) - 0x0001B - Squares & Black/White Squares -0x0001C (Squares Introduction 4) - 0x012C9 - Squares & Black/White Squares -0x0001D (Squares Introduction 5) - 0x0001C - Squares & Black/White Squares -0x0001E (Squares Introduction 6) - 0x0001D - Squares & Black/White Squares -0x0001F (Squares Introduction 7) - 0x0001E - Squares & Black/White Squares -0x00020 (Squares Introduction 8) - 0x0001F - Squares & Black/White Squares -0x00021 (Squares Introduction 9) - 0x00020 - Squares & Black/White Squares +Outside Tutorial (Outside Tutorial) - Outside Tutorial Path To Outpost - 0x03BA2: +158650 - 0x033D4 (Vault) - True - Dots & Squares & Black/White Squares +158651 - 0x03481 (Vault Box) - 0x033D4 - True +158013 - 0x0005D (Dots Introduction 1) - True - Dots +158014 - 0x0005E (Dots Introduction 2) - 0x0005D - Dots +158015 - 0x0005F (Dots Introduction 3) - 0x0005E - Dots +158016 - 0x00060 (Dots Introduction 4) - 0x0005F - Dots +158017 - 0x00061 (Dots Introduction 5) - 0x00060 - Dots +158018 - 0x018AF (Squares Introduction 1) - True - Squares & Black/White Squares +158019 - 0x0001B (Squares Introduction 2) - 0x018AF - Squares & Black/White Squares +158020 - 0x012C9 (Squares Introduction 3) - 0x0001B - Squares & Black/White Squares +158021 - 0x0001C (Squares Introduction 4) - 0x012C9 - Squares & Black/White Squares +158022 - 0x0001D (Squares Introduction 5) - 0x0001C - Squares & Black/White Squares +158023 - 0x0001E (Squares Introduction 6) - 0x0001D - Squares & Black/White Squares +158024 - 0x0001F (Squares Introduction 7) - 0x0001E - Squares & Black/White Squares +158025 - 0x00020 (Squares Introduction 8) - 0x0001F - Squares & Black/White Squares +158026 - 0x00021 (Squares Introduction 9) - 0x00020 - Squares & Black/White Squares +Door - 0x03BA2 (Optional Door 1) - 0x0A3B5 + +Outside Tutorial Path To Outpost (Outside Tutorial) - Outside Tutorial Outpost - 0x0A170: +158011 - 0x0A171 (Door to Outpost Panel) - True - Dots +Door - 0x0A170 (Door to Outpost) - 0x0A171 + +Outside Tutorial Outpost (Outside Tutorial) - Outside Tutorial - 0x04CA3: +158012 - 0x04CA4 (Exit Door from Outpost Panel) - True - Dots & Squares & Black/White Squares +Door - 0x04CA3 (Exit Door from Outpost) - 0x04CA4 +158600 - 0x17CFB (Discard) - True - Triangles Main Island () - Outside Tutorial - True: -Outside Glass Factory (Glass Factory) - Main Island - True: -0x01A54 (Entry Door) - True - Symmetry -0x3C12B (Discard) - True - Triangles +Outside Glass Factory (Glass Factory) - Main Island - True - Inside Glass Factory - 0x01A29: +158027 - 0x01A54 (Entry Door Panel) - True - Symmetry +Door - 0x01A29 (Entry Door) - 0x01A54 +158601 - 0x3C12B (Discard) - True - Triangles -Inside Glass Factory (Glass Factory) - Outside Glass Factory - 0x01A54: -0x00086 (Vertical Symmetry 1) - True - Symmetry -0x00087 (Vertical Symmetry 2) - 0x00086 - Symmetry -0x00059 (Vertical Symmetry 3) - 0x00087 - Symmetry -0x00062 (Vertical Symmetry 4) - 0x00059 - Symmetry -0x0005C (Vertical Symmetry 5) - 0x00062 - Symmetry -0x0008D (Rotational Symmetry 1) - 0x0005C - Symmetry -0x00081 (Rotational Symmetry 2) - 0x0008D - Symmetry -0x00083 (Rotational Symmetry 3) - 0x00081 - Symmetry -0x00084 (Melting 1) - 0x00083 - Symmetry -0x00082 (Melting 2) - 0x00084 - Symmetry -0x0343A (Melting 3) - 0x00082 - Symmetry -0x17CC8 (Boat Spawn) - 0x0005C - Boat +Inside Glass Factory (Glass Factory) - Inside Glass Factory Behind Back Wall - 0x0D7ED: +158028 - 0x00086 (Vertical Symmetry 1) - True - Symmetry +158029 - 0x00087 (Vertical Symmetry 2) - 0x00086 - Symmetry +158030 - 0x00059 (Vertical Symmetry 3) - 0x00087 - Symmetry +158031 - 0x00062 (Vertical Symmetry 4) - 0x00059 - Symmetry +158032 - 0x0005C (Vertical Symmetry 5) - 0x00062 - Symmetry +158033 - 0x0008D (Rotational Symmetry 1) - 0x0005C - Symmetry +158034 - 0x00081 (Rotational Symmetry 2) - 0x0008D - Symmetry +158035 - 0x00083 (Rotational Symmetry 3) - 0x00081 - Symmetry +158036 - 0x00084 (Melting 1) - 0x00083 - Symmetry +158037 - 0x00082 (Melting 2) - 0x00084 - Symmetry +158038 - 0x0343A (Melting 3) - 0x00082 - Symmetry +Door - 0x0D7ED (Back Wall) - 0x0005C -Outside Symmetry Island (Symmetry Island) - Main Island - True: -0x000B0 (Door to Symmetry Island Lower) - 0x0343A - Dots +Inside Glass Factory Behind Back Wall (Glass Factory) - Boat - 0x17CC8: +158039 - 0x17CC8 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat -Symmetry Island Lower (Symmetry Island) - Outside Symmetry Island - 0x000B0: -0x00022 (Black Dots 1) - True - Symmetry & Dots -0x00023 (Black Dots 2) - 0x00022 - Symmetry & Dots -0x00024 (Black Dots 3) - 0x00023 - Symmetry & Dots -0x00025 (Black Dots 4) - 0x00024 - Symmetry & Dots -0x00026 (Black Dots 5) - 0x00025 - Symmetry & Dots -0x0007C (Colored Dots 1) - 0x00026 - Symmetry & Colored Dots -0x0007E (Colored Dots 2) - 0x0007C - Symmetry & Colored Dots -0x00075 (Colored Dots 3) - 0x0007E - Symmetry & Colored Dots -0x00073 (Colored Dots 4) - 0x00075 - Symmetry & Colored Dots -0x00077 (Colored Dots 5) - 0x00073 - Symmetry & Colored Dots -0x00079 (Colored Dots 6) - 0x00077 - Symmetry & Colored Dots -0x00065 (Fading Lines 1) - 0x00079 - Symmetry & Colored Dots -0x0006D (Fading Lines 2) - 0x00065 - Symmetry & Colored Dots -0x00072 (Fading Lines 3) - 0x0006D - Symmetry & Colored Dots -0x0006F (Fading Lines 4) - 0x00072 - Symmetry & Colored Dots -0x00070 (Fading Lines 5) - 0x0006F - Symmetry & Colored Dots -0x00071 (Fading Lines 6) - 0x00070 - Symmetry & Colored Dots -0x00076 (Fading Lines 7) - 0x00071 - Symmetry & Colored Dots -0x009B8 (Scenery Outlines 1) - True - Symmetry & Environment -0x003E8 (Scenery Outlines 2) - 0x009B8 - Symmetry & Environment -0x00A15 (Scenery Outlines 3) - 0x003E8 - Symmetry & Environment -0x00B53 (Scenery Outlines 4) - 0x00A15 - Symmetry & Environment -0x00B8D (Scenery Outlines 5) - 0x00B53 - Symmetry & Environment -0x1C349 (Door to Symmetry Island Upper) - 0x00076 - Symmetry & Dots +Outside Symmetry Island (Symmetry Island) - Main Island - True - Symmetry Island Lower - 0x17F3E: +158040 - 0x000B0 (Door to Symmetry Island Lower Panel) - 0x0343A - Dots +Door - 0x17F3E (Door to Symmetry Island Lower) - 0x000B0 -Symmetry Island Upper (Symmetry Island) - Symmetry Island Lower - 0x1C349: -0x00A52 (Yellow 1) - True - Symmetry & Colored Dots -0x00A57 (Yellow 2) - 0x00A52 - Symmetry & Colored Dots -0x00A5B (Yellow 3) - 0x00A57 - Symmetry & Colored Dots -0x00A61 (Blue 1) - 0x00A52 - Symmetry & Colored Dots -0x00A64 (Blue 2) - 0x00A61 & 0x00A52 - Symmetry & Colored Dots -0x00A68 (Blue 3) - 0x00A64 & 0x00A57 - Symmetry & Colored Dots -0x0360D (Laser) - 0x00A68 - True +Symmetry Island Lower (Symmetry Island) - Symmetry Island Upper - 0x18269: +158041 - 0x00022 (Black Dots 1) - True - Symmetry & Dots +158042 - 0x00023 (Black Dots 2) - 0x00022 - Symmetry & Dots +158043 - 0x00024 (Black Dots 3) - 0x00023 - Symmetry & Dots +158044 - 0x00025 (Black Dots 4) - 0x00024 - Symmetry & Dots +158045 - 0x00026 (Black Dots 5) - 0x00025 - Symmetry & Dots +158046 - 0x0007C (Colored Dots 1) - 0x00026 - Symmetry & Colored Dots +158047 - 0x0007E (Colored Dots 2) - 0x0007C - Symmetry & Colored Dots +158048 - 0x00075 (Colored Dots 3) - 0x0007E - Symmetry & Colored Dots +158049 - 0x00073 (Colored Dots 4) - 0x00075 - Symmetry & Colored Dots +158050 - 0x00077 (Colored Dots 5) - 0x00073 - Symmetry & Colored Dots +158051 - 0x00079 (Colored Dots 6) - 0x00077 - Symmetry & Colored Dots +158052 - 0x00065 (Fading Lines 1) - 0x00079 - Symmetry & Colored Dots +158053 - 0x0006D (Fading Lines 2) - 0x00065 - Symmetry & Colored Dots +158054 - 0x00072 (Fading Lines 3) - 0x0006D - Symmetry & Colored Dots +158055 - 0x0006F (Fading Lines 4) - 0x00072 - Symmetry & Colored Dots +158056 - 0x00070 (Fading Lines 5) - 0x0006F - Symmetry & Colored Dots +158057 - 0x00071 (Fading Lines 6) - 0x00070 - Symmetry & Colored Dots +158058 - 0x00076 (Fading Lines 7) - 0x00071 - Symmetry & Colored Dots +158059 - 0x009B8 (Scenery Outlines 1) - True - Symmetry & Environment +158060 - 0x003E8 (Scenery Outlines 2) - 0x009B8 - Symmetry & Environment +158061 - 0x00A15 (Scenery Outlines 3) - 0x003E8 - Symmetry & Environment +158062 - 0x00B53 (Scenery Outlines 4) - 0x00A15 - Symmetry & Environment +158063 - 0x00B8D (Scenery Outlines 5) - 0x00B53 - Symmetry & Environment +158064 - 0x1C349 (Door to Symmetry Island Upper Panel) - 0x00076 - Symmetry & Dots +Door - 0x18269 (Door to Symmetry Island Upper) - 0x1C349 -Orchard (Orchard) - Main Island - True: -0x00143 (Apple Tree 1) - True - Environment -0x0003B (Apple Tree 2) - 0x00143 - Environment -0x00055 (Apple Tree 3) - 0x0003B - Environment -0x032F7 (Apple Tree 4) - 0x00055 - Environment -0x032FF (Apple Tree 5) - 0x032F7 - Environment +Symmetry Island Upper (Symmetry Island): +158065 - 0x00A52 (Yellow 1) - True - Symmetry & Colored Dots +158066 - 0x00A57 (Yellow 2) - 0x00A52 - Symmetry & Colored Dots +158067 - 0x00A5B (Yellow 3) - 0x00A57 - Symmetry & Colored Dots +158068 - 0x00A61 (Blue 1) - 0x00A52 - Symmetry & Colored Dots +158069 - 0x00A64 (Blue 2) - 0x00A61 & 0x00A52 - Symmetry & Colored Dots +158070 - 0x00A68 (Blue 3) - 0x00A64 & 0x00A57 - Symmetry & Colored Dots +158700 - 0x0360D (Laser Panel) - 0x00A68 - True +Laser - 0x00509 (Laser) - 0x0360D - True -Desert Outside (Desert) - Main Island - True: -0x0CC7B (Vault) - True - Dots & Shapers & Rotated Shapers & Negative Shapers -0x0339E (Vault Box) - 0x0CC7B - True -0x17CE7 (Discard) - True - Triangles -0x00698 (Sun Reflection 1) - True - Reflection -0x0048F (Sun Reflection 2) - 0x00698 - Reflection -0x09F92 (Sun Reflection 3) - 0x0048F & 0x09FA0 - Reflection -0x09FA0 (Reflection 3 Control) - 0x0048F - True -0x0A036 (Sun Reflection 4) - 0x09F92 - Reflection -0x09DA6 (Sun Reflection 5) - 0x09F92 - Reflection -0x0A049 (Sun Reflection 6) - 0x09F92 - Reflection -0x0A053 (Sun Reflection 7) - 0x0A036 & 0x09DA6 & 0x0A049 - Reflection -0x09F94 (Sun Reflection 8) - 0x0A053 & 0x09F86 - Reflection -0x09F86 (Reflection 8 Control) - 0x0A053 - True -0x0C339 (Door to Desert Flood Light Room) - 0x09F94 - True +Orchard (Orchard) - Main Island - True - Orchard Beyond First Gate - 0x03307: +158071 - 0x00143 (Apple Tree 1) - True - Environment +158072 - 0x0003B (Apple Tree 2) - 0x00143 - Environment +158073 - 0x00055 (Apple Tree 3) - 0x0003B - Environment +Door - 0x03307 (Mid Gate) - 0x00055 -Desert Floodlight Room (Desert) - Desert Outside - 0x0C339: -0x09FAA (Light Control) - True - True -0x00422 (Artificial Light Reflection 1) - 0x09FAA - Reflection -0x006E3 (Artificial Light Reflection 2) - 0x09FAA - Reflection -0x0A02D (Artificial Light Reflection 3) - 0x09FAA & 0x00422 & 0x006E3 - Reflection +Orchard Beyond First Gate (Orchard) - Orchard End - 0x03313: +158074 - 0x032F7 (Apple Tree 4) - 0x00055 - Environment +158075 - 0x032FF (Apple Tree 5) - 0x032F7 - Environment +Door - 0x03313 (Final Gate) - 0x032FF -Desert Pond Room (Desert) - Desert Floodlight Room - 0x0A02D: -0x00C72 (Pond Reflection 1) - True - Reflection -0x0129D (Pond Reflection 2) - 0x00C72 - Reflection -0x008BB (Pond Reflection 3) - 0x0129D - Reflection -0x0078D (Pond Reflection 4) - 0x008BB - Reflection -0x18313 (Pond Reflection 5) - 0x0078D - Reflection -0x0A249 (Door to Desert Water Levels Room) - 0x18313 - Reflection +Orchard End (Orchard): -Desert Water Levels Room (Desert) - Desert Pond Room - 0x0A249: -0x1C2DF (Reduce Water Level Far Left) - True - True -0x1831E (Reduce Water Level Far Right) - True - True -0x1C260 (Reduce Water Level Near Left) - True - True -0x1831C (Reduce Water Level Near Right) - True - True -0x1C2F3 (Raise Water Level Far Left) - True - True -0x1831D (Raise Water Level Far Right) - True - True -0x1C2B1 (Raise Water Level Near Left) - True - True -0x1831B (Raise Water Level Near Right) - True - True -0x04D18 (Flood Reflection 1) - 0x1C260 & 0x1831C - Reflection -0x01205 (Flood Reflection 2) - 0x04D18 & 0x1C260 & 0x1831C - Reflection -0x181AB (Flood Reflection 3) - 0x01205 & 0x1C260 & 0x1831C - Reflection -0x0117A (Flood Reflection 4) - 0x181AB & 0x1C260 & 0x1831C - Reflection -0x17ECA (Flood Reflection 5) - 0x0117A & 0x1C260 & 0x1831C - Reflection -0x18076 (Flood Reflection 6) - 0x17ECA & 0x1C260 & 0x1831C - Reflection +Desert Outside (Desert) - Main Island - True - Desert Floodlight Room - 0x09FEE: +158652 - 0x0CC7B (Vault) - True - Dots & Shapers & Rotated Shapers & Negative Shapers +158653 - 0x0339E (Vault Box) - 0x0CC7B - True +158602 - 0x17CE7 (Discard) - True - Triangles +158076 - 0x00698 (Sun Reflection 1) - True - Reflection +158077 - 0x0048F (Sun Reflection 2) - 0x00698 - Reflection +158078 - 0x09F92 (Sun Reflection 3) - 0x0048F & 0x09FA0 - Reflection +158079 - 0x09FA0 (Reflection 3 Control) - 0x0048F - True +158080 - 0x0A036 (Sun Reflection 4) - 0x09F92 - Reflection +158081 - 0x09DA6 (Sun Reflection 5) - 0x09F92 - Reflection +158082 - 0x0A049 (Sun Reflection 6) - 0x09F92 - Reflection +158083 - 0x0A053 (Sun Reflection 7) - 0x0A036 & 0x09DA6 & 0x0A049 - Reflection +158084 - 0x09F94 (Sun Reflection 8) - 0x0A053 & 0x09F86 - Reflection +158085 - 0x09F86 (Reflection 8 Control) - 0x0A053 - True +158086 - 0x0C339 (Door to Desert Flood Light Room Panel) - 0x09F94 - True +Door - 0x09FEE (Door to Desert Flood Light Room) - 0x0C339 - True -Desert Elevator Room (Desert) - Desert Water Levels Room - 0x18076: -0x17C31 (Final Transparent Reflection) - True - Reflection -0x012D7 (Final Reflection) - 0x17C31 & 0x0A015 - Reflection -0x0A015 (Final Reflection Control) - 0x17C31 - True -0x0A15C (Final Bent Reflection 1) - True - Reflection -0x09FFF (Final Bent Reflection 2) - 0x0A15C - Reflection -0x0A15F (Final Bent Reflection 3) - 0x09FFF - Reflection -0x03608 (Laser) - 0x012D7 & 0x0A15F - True +Desert Floodlight Room (Desert) - Desert Pond Room - 0x0C2C3: +158087 - 0x09FAA (Light Control) - True - True +158088 - 0x00422 (Artificial Light Reflection 1) - 0x09FAA - Reflection +158089 - 0x006E3 (Artificial Light Reflection 2) - 0x09FAA - Reflection +158090 - 0x0A02D (Artificial Light Reflection 3) - 0x09FAA & 0x00422 & 0x006E3 - Reflection +Door - 0x0C2C3 (Door to Pond Room) - 0x0A02D -Outside Quarry (Quarry) - Main Island - True: -0x09E57 (Door to Quarry 1) - True - Squares & Black/White Squares -0x17C09 (Door to Quarry 2) - 0x09E57 - Shapers -0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser +Desert Pond Room (Desert) - Desert Water Levels Room - 0x0A24B: +158091 - 0x00C72 (Pond Reflection 1) - True - Reflection +158092 - 0x0129D (Pond Reflection 2) - 0x00C72 - Reflection +158093 - 0x008BB (Pond Reflection 3) - 0x0129D - Reflection +158094 - 0x0078D (Pond Reflection 4) - 0x008BB - Reflection +158095 - 0x18313 (Pond Reflection 5) - 0x0078D - Reflection +158096 - 0x0A249 (Door to Water Levels Room Panel) - 0x18313 - Reflection +Door - 0x0A24B (Door to Water Levels Room) - 0x0A249 -Quarry (Quarry) - Outside Quarry - 0x17C09 - Quarry Mill - 0x275ED - Quarry Mill - 0x17CAC - Shadows Ledge - 0x198BF: -0x01E5A (Door to Mill Left) - True - Squares & Black/White Squares -0x01E59 (Door to Mill Right) - True - Dots -0x17CF0 (Discard) - True - Triangles -0x03612 (Laser) - 0x0A3D0 & 0x0367C - Eraser & Shapers +Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316: +158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True +158098 - 0x1831E (Reduce Water Level Far Right) - True - True +158099 - 0x1C260 (Reduce Water Level Near Left) - True - True +158100 - 0x1831C (Reduce Water Level Near Right) - True - True +158101 - 0x1C2F3 (Raise Water Level Far Left) - True - True +158102 - 0x1831D (Raise Water Level Far Right) - True - True +158103 - 0x1C2B1 (Raise Water Level Near Left) - True - True +158104 - 0x1831B (Raise Water Level Near Right) - True - True +158105 - 0x04D18 (Flood Reflection 1) - 0x1C260 & 0x1831C - Reflection +158106 - 0x01205 (Flood Reflection 2) - 0x04D18 & 0x1C260 & 0x1831C - Reflection +158107 - 0x181AB (Flood Reflection 3) - 0x01205 & 0x1C260 & 0x1831C - Reflection +158108 - 0x0117A (Flood Reflection 4) - 0x181AB & 0x1C260 & 0x1831C - Reflection +158109 - 0x17ECA (Flood Reflection 5) - 0x0117A & 0x1C260 & 0x1831C - Reflection +158110 - 0x18076 (Flood Reflection 6) - 0x17ECA & 0x1C260 & 0x1831C - Reflection +Door - 0x0C316 (Door to Elevator Room) - 0x18076 -Quarry Mill (Quarry Mill) - Quarry - 0x01E59 & 0x01E5A: -0x275ED (Ground Floor Shortcut Door) - True - True -0x03678 (Lower Ramp Control) - True - Dots & Eraser -0x00E0C (Eraser and Dots 1) - 0x03678 - Dots & Eraser -0x01489 (Eraser and Dots 2) - 0x00E0C - Dots & Eraser -0x0148A (Eraser and Dots 3) - 0x01489 - Dots & Eraser -0x014D9 (Eraser and Dots 4) - 0x0148A - Dots & Eraser -0x014E7 (Eraser and Dots 5) - 0x014D9 - Dots & Eraser -0x014E8 (Eraser and Dots 6) - 0x014E7 - Dots & Eraser -0x03679 (Lower Lift Control) - 0x014E8 - Dots & Eraser -0x03675 (Upper Ramp Control) - 0x03679 - Dots & Eraser -0x03676 (Upper Lift Control) - 0x03679 - Dots & Eraser -0x00557 (Eraser and Squares 1) - 0x03679 - Squares & Colored Squares & Eraser -0x005F1 (Eraser and Squares 2) - 0x00557 - Squares & Colored Squares & Eraser -0x00620 (Eraser and Squares 3) - 0x005F1 - Squares & Colored Squares & Eraser -0x009F5 (Eraser and Squares 4) - 0x00620 - Squares & Colored Squares & Eraser -0x0146C (Eraser and Squares 5) - 0x009F5 - Squares & Colored Squares & Eraser -0x3C12D (Eraser and Squares 6) - 0x0146C - Squares & Colored Squares & Eraser -0x03686 (Eraser and Squares 7) - 0x3C12D - Squares & Colored Squares & Eraser -0x014E9 (Eraser and Squares 8) - 0x03686 - Squares & Colored Squares & Eraser -0x03677 (Stair Control) - 0x014E8 - Squares & Colored Squares & Eraser -0x3C125 (Big Squares & Dots & Eraser) - 0x0367C - Squares & Black/White Squares & Dots & Eraser -0x0367C (Small Squares & Dots & Eraser) - 0x014E9 - Squares & Colored Squares & Dots & Eraser -0x17CAC (Door to Outside Quarry Stairs) - True - True +Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x012FB: +158111 - 0x17C31 (Final Transparent Reflection) - True - Reflection +158113 - 0x012D7 (Final Reflection) - 0x17C31 & 0x0A015 - Reflection +158114 - 0x0A015 (Final Reflection Control) - 0x17C31 - True +158115 - 0x0A15C (Final Bent Reflection 1) - True - Reflection +158116 - 0x09FFF (Final Bent Reflection 2) - 0x0A15C - Reflection +158117 - 0x0A15F (Final Bent Reflection 3) - 0x09FFF - Reflection +158701 - 0x03608 (Laser Panel) - 0x012D7 & 0x0A15F - True +Laser - 0x012FB (Laser) - 0x03608 -Quarry Boathouse (Quarry Boathouse) - Quarry - True: -0x034D4 (Intro Stars) - True - Stars -0x021D5 (Intro Shapers) - True - Shapers & Rotated Shapers -0x03852 (Ramp Height Control) - 0x034D4 & 0x021D5 - Rotated Shapers -0x021B3 (Eraser and Shapers 1) - 0x03852 - Shapers & Eraser -0x021B4 (Eraser and Shapers 2) - 0x021B3 - Shapers & Eraser -0x021B0 (Eraser and Shapers 3) - 0x021B4 - Shapers & Eraser -0x021AF (Eraser and Shapers 4) - 0x021B0 - Shapers & Eraser -0x021AE (Eraser and Shapers 5) - 0x021AF - Shapers & Eraser & Broken Shapers -0x03858 (Ramp Horizontal Control) - 0x021AE - Shapers & Eraser -0x38663 (Shortcut Door) - 0x03858 - True -0x021B5 (Stars and Colored Eraser 1) - 0x03858 - Stars & Stars + Same Colored Symbol & Eraser -0x021B6 (Stars and Colored Eraser 2) - 0x021B5 - Stars & Stars + Same Colored Symbol & Eraser -0x021B7 (Stars and Colored Eraser 3) - 0x021B6 - Stars & Stars + Same Colored Symbol & Eraser -0x021BB (Stars and Colored Eraser 4) - 0x021B7 - Stars & Stars + Same Colored Symbol & Eraser -0x09DB5 (Stars and Colored Eraser 5) - 0x021BB - Stars & Stars + Same Colored Symbol & Eraser -0x09DB1 (Stars and Colored Eraser 6) - 0x09DB5 - Stars & Stars + Same Colored Symbol & Eraser -0x3C124 (Stars and Colored Eraser 7) - 0x09DB1 - Stars & Stars + Same Colored Symbol & Eraser -0x09DB3 (Stars & Eraser & Shapers 1) - 0x3C124 - Stars & Eraser & Shapers -0x09DB4 (Stars & Eraser & Shapers 2) - 0x09DB3 - Stars & Eraser & Shapers -0x275FA (Hook Control) - 0x03858 - Shapers & Eraser -0x17CA6 (Boat Spawn) - True - Boat -0x0A3CB (Stars & Eraser & Shapers 3) - 0x09DB4 - Stars & Eraser & Shapers -0x0A3CC (Stars & Eraser & Shapers 4) - 0x0A3CB - Stars & Eraser & Shapers -0x0A3D0 (Stars & Eraser & Shapers 5) - 0x0A3CC - Stars & Eraser & Shapers +Desert Lowest Level Inbetween Shortcuts (Desert): -Shadows (Shadows) - Main Island - True - Keep Glass Plates - 0x09E49: -0x334DB (Door Timer Outside) - True - True -0x0AC74 (Lower Avoid 6) - 0x0A8DC - Shadows Avoid -0x0AC7A (Lower Avoid 7) - 0x0AC74 - Shadows Avoid -0x0A8E0 (Lower Avoid 8) - 0x0AC7A - Shadows Avoid -0x386FA (Environmental Avoid 1) - 0x0A8E0 - Shadows Avoid & Environment -0x1C33F (Environmental Avoid 2) - 0x386FA - Shadows Avoid & Environment -0x196E2 (Environmental Avoid 3) - 0x1C33F - Shadows Avoid & Environment -0x1972A (Environmental Avoid 4) - 0x196E2 - Shadows Avoid & Environment -0x19809 (Environmental Avoid 5) - 0x1972A - Shadows Avoid & Environment -0x19806 (Environmental Avoid 6) - 0x19809 - Shadows Avoid & Environment -0x196F8 (Environmental Avoid 7) - 0x19806 - Shadows Avoid & Environment -0x1972F (Environmental Avoid 8) - 0x196F8 - Shadows Avoid & Environment -0x19797 (Follow 1) - 0x0A8E0 - Shadows Follow -0x1979A (Follow 2) - 0x19797 - Shadows Follow -0x197E0 (Follow 3) - 0x1979A - Shadows Follow -0x197E8 (Follow 4) - 0x197E0 - Shadows Follow -0x197E5 (Follow 5) - 0x197E8 - Shadows Follow -0x19650 (Laser) - 0x197E5 & 0x196F8 - Shadows Avoid & Shadows Follow +Outside Quarry (Quarry) - Main Island - True - Quarry Between Entry Doors - 0x09D6F: +158118 - 0x09E57 (Door to Quarry 1 Panel) - True - Squares & Black/White Squares +158120 - 0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser +158603 - 0x17CF0 (Discard) - True - Triangles +158702 - 0x03612 (Laser Panel) - 0x0A3D0 & 0x0367C - Eraser & Shapers +Laser - 0x01539 (Laser) - 0x03612 +Door - 0x09D6F (Door to Quarry 1) - 0x09E57 -Shadows Ledge (Shadows) - Shadows - 0x334DB | 0x334DC | 0x0A8DC: -0x334DC (Door Timer Inside) - True - True -0x198B5 (Lower Avoid 1) - True - Shadows Avoid -0x198BD (Lower Avoid 2) - 0x198B5 - Shadows Avoid -0x198BF (Lower Avoid 3) - 0x198BD & 0x334DC - Shadows Avoid -0x19771 (Lower Avoid 4) - 0x198BF - Shadows Avoid -0x0A8DC (Lower Avoid 5) - 0x19771 - Shadows Avoid +Quarry Between Entry Doors (Quarry) - Quarry - 0x17C07: +158119 - 0x17C09 (Door to Quarry 2 Panel) - True - Shapers +Door - 0x17C07 (Door to Quarry 2) - 0x17C09 -Keep (Keep) - Main Island - True: +Quarry (Quarry) - Quarry Mill Ground Floor - 0x02010: +158121 - 0x01E5A (Door to Mill Left) - True - Squares & Black/White Squares +158122 - 0x01E59 (Door to Mill Right) - True - Dots +Door - 0x02010 (Door to Mill) - 0x01E59 & 0x01E5A -Keep Hedges (Keep) - Keep - True: -0x00139 (Hedge Maze 1) - True - Environment -0x019DC (Hedge Maze 2) - 0x00139 - Environment -0x019E7 (Hedge Maze 3) - 0x019DC - Environment & Sound -0x01A0F (Hedge Maze 4) - 0x019E7 - Environment +Quarry Mill Ground Floor (Quarry Mill) - Quarry - 0x275FF - Quarry Mill Middle Floor - 0x03678 - Outside Quarry - 0x17CE8: +158123 - 0x275ED (Ground Floor Shortcut Door Panel) - True - True +Door - 0x275FF (Ground Floor Shortcut Door) - 0x275ED +158124 - 0x03678 (Lower Ramp Control) - True - Dots & Eraser +158145 - 0x17CAC (Door to Outside Quarry Stairs Panel) - True - True +Door - 0x17CE8 (Door to Outside Quarry Stairs) - 0x17CAC -Keep Glass Plates (Keep) - Keep - True - Keep Tower - 0x0361B: -0x0A3A8 (Reset Pressure Plates 1) - True - True -0x033EA (Pressure Plates 1) - 0x0A3A8 - Pressure Plates & Dots -0x0A3B9 (Reset Pressure Plates 2) - 0x033EA - True -0x01BE9 (Pressure Plates 2) - 0x033EA & 0x0A3B9 - Pressure Plates & Stars & Stars + Same Colored Symbol & Squares & Black/White Squares -0x0A3BB (Reset Pressure Plates 3) - 0x0A3A8 - True -0x01CD3 (Pressure Plates 3) - 0x0A3A8 & 0x0A3BB - Pressure Plates & Shapers & Squares & Black/White Squares & Colored Squares -0x0A3AD (Reset Pressure Plates 4) - 0x01CD3 - True -0x01D3F (Pressure Plates 4) - 0x01CD3 & 0x0A3AD - Pressure Plates & Shapers & Dots & Symmetry -0x17D27 (Discard) - 0x01CD3 - Triangles -0x09E49 (Shortcut to Shadows) - 0x01CD3 - True +Quarry Mill Middle Floor (Quarry Mill) - Quarry Mill Ground Floor - 0x03675 - Quarry Mill Upper Floor - 0x03679: +158125 - 0x00E0C (Eraser and Dots 1) - True - Dots & Eraser +158126 - 0x01489 (Eraser and Dots 2) - 0x00E0C - Dots & Eraser +158127 - 0x0148A (Eraser and Dots 3) - 0x01489 - Dots & Eraser +158128 - 0x014D9 (Eraser and Dots 4) - 0x0148A - Dots & Eraser +158129 - 0x014E7 (Eraser and Dots 5) - 0x014D9 - Dots & Eraser +158130 - 0x014E8 (Eraser and Dots 6) - 0x014E7 - Dots & Eraser +158131 - 0x03679 (Lower Lift Control) - 0x014E8 - Dots & Eraser -Shipwreck (Shipwreck) - Keep Glass Plates - 0x033EA: -0x00AFB (Vault) - True - Symmetry & Sound & Sound Dots & Colored Dots -0x03535 (Vault Box) - 0x00AFB - True -0x17D28 (Discard) - True - Triangles +Quarry Mill Upper Floor (Quarry Mill) - Quarry Mill Middle Floor - 0x03676 & 0x03679 - Quarry Mill Ground Floor - 0x0368A: +158132 - 0x03676 (Upper Ramp Control) - True - Dots & Eraser +158133 - 0x03675 (Upper Lift Control) - True - Dots & Eraser +158134 - 0x00557 (Eraser and Squares 1) - True - Squares & Colored Squares & Eraser +158135 - 0x005F1 (Eraser and Squares 2) - 0x00557 - Squares & Colored Squares & Eraser +158136 - 0x00620 (Eraser and Squares 3) - 0x005F1 - Squares & Colored Squares & Eraser +158137 - 0x009F5 (Eraser and Squares 4) - 0x00620 - Squares & Colored Squares & Eraser +158138 - 0x0146C (Eraser and Squares 5) - 0x009F5 - Squares & Colored Squares & Eraser +158139 - 0x3C12D (Eraser and Squares 6) - 0x0146C - Squares & Colored Squares & Eraser +158140 - 0x03686 (Eraser and Squares 7) - 0x3C12D - Squares & Colored Squares & Eraser +158141 - 0x014E9 (Eraser and Squares 8) - 0x03686 - Squares & Colored Squares & Eraser +158142 - 0x03677 (Stair Control) - True - Squares & Colored Squares & Eraser +Door - 0x0368A (Stairs) - 0x03677 +158143 - 0x3C125 (Big Squares & Dots & Eraser) - 0x0367C - Squares & Black/White Squares & Dots & Eraser +158144 - 0x0367C (Small Squares & Dots & Eraser) - 0x014E9 - Squares & Colored Squares & Dots & Eraser -Keep Tower (Keep) - Keep Hedges - 0x01A0F - Keep Glass Plates - 0x01D3F: -0x0361B (Shortcut to Keep Glass Plates) - True - True -0x0360E (Laser Hedges) - 0x01A0F - Environment & Sound -0x03317 (Laser Pressure Plates) - 0x01D3F - Shapers & Squares & Black/White Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Dots +Quarry Boathouse (Quarry Boathouse) - Quarry - True - Quarry Boathouse Upper Front - 0x03852 - Quarry Boathouse Behind Staircase - 0x2769B: +158146 - 0x034D4 (Intro Stars) - True - Stars +158147 - 0x021D5 (Intro Shapers) - True - Shapers & Rotated Shapers +158148 - 0x03852 (Ramp Height Control) - 0x034D4 & 0x021D5 - Rotated Shapers +158166 - 0x17CA6 (Boat Spawn) - True - Boat +Door - 0x2769B (Boat Staircase) - 0x17CA6 +Door - 0x27163 (Boat Staircase Invis Barrier) - 0x17CA6 -Outside Monastery (Monastery) - Main Island - True: -0x03713 (Shortcut) - True - True -0x00B10 (Door Open Left) - True - True -0x00C92 (Door Open Right) - True - True -0x00290 (Rhombic Avoid 1) - 0x09D9B - Environment -0x00038 (Rhombic Avoid 2) - 0x09D9B & 0x00290 - Environment -0x00037 (Rhombic Avoid 3) - 0x09D9B & 0x00038 - Environment -0x17CA4 (Laser) - 0x193A6 - True +Quarry Boathouse Behind Staircase (Quarry Boathouse) - Boat - 0x17CA6: -Inside Monastery (Monastery) - Outside Monastery - 0x00B10 & 0x00C92: -0x09D9B (Overhead Door Control) - True - Dots -0x193A7 (Branch Avoid 1) - 0x00037 - Environment -0x193AA (Branch Avoid 2) - 0x193A7 - Environment -0x193AB (Branch Follow 1) - 0x193AA - Environment -0x193A6 (Branch Follow 2) - 0x193AB - Environment +Quarry Boathouse Upper Front (Quarry Boathouse) - Quarry Boathouse Upper Middle - 0x17C50: +158149 - 0x021B3 (Eraser and Shapers 1) - True - Shapers & Eraser +158150 - 0x021B4 (Eraser and Shapers 2) - 0x021B3 - Shapers & Eraser +158151 - 0x021B0 (Eraser and Shapers 3) - 0x021B4 - Shapers & Eraser +158152 - 0x021AF (Eraser and Shapers 4) - 0x021B0 - Shapers & Eraser +158153 - 0x021AE (Eraser and Shapers 5) - 0x021AF - Shapers & Eraser & Broken Shapers +Door - 0x17C50 (Boathouse Barrier 1) - 0x021AE -Monastery Garden (Monastery) - Outside Monastery - 0x00037 - Outside Jungle River - 0x17CAA: +Quarry Boathouse Upper Middle (Quarry Boathouse) - Quarry Boathouse Upper Back - 0x03858: +158154 - 0x03858 (Ramp Horizontal Control) - True - Shapers & Eraser -Town (Town) - Main Island - True - Theater - 0x0A168 | 0x33AB2: -0x0A054 (Boat Summon) - True - Boat -0x0A0C8 (Cargo Box) - True - Squares & Black/White Squares & Shapers -0x17D01 (Cargo Box Discard) - 0x0A0C8 - Triangles -0x09F98 (Desert Laser Redirect) - True - True -0x18590 (Tree Outlines) - True - Symmetry & Environment -0x28AE3 (Vines Shadows Follow) - 0x18590 - Shadows Follow & Environment -0x28938 (Four-way Apple Tree) - 0x28AE3 - Environment -0x079DF (Triple Environmental Puzzle) - 0x28938 - Shadows Avoid & Environment & Reflection -0x28B39 (Hexagonal Reflection) - 0x079DF & 0x2896A - Reflection -0x28998 (Tinted Door to RGB House) - True - Stars & Rotated Shapers -0x28A0D (Door to Church) - 0x28998 - Stars & RGB & Environment -0x28A69 (Square Avoid) - 0x28A0D - Environment -0x28A79 (Maze Stair Control) - True - Environment -0x2896A (Maze Rooftop Bridge Control) - 0x28A79 - Shapers -0x17C71 (Rooftop Discard) - 0x2896A - Triangles -0x28AC7 (Symmetry Squares 1) - 0x2896A - Symmetry & Squares & Black/White Squares -0x28AC8 (Symmetry Squares 2) - 0x28AC7 - Symmetry & Squares & Black/White Squares -0x28ACA (Symmetry Squares 3 + Dots) - 0x28AC8 - Symmetry & Squares & Black/White Squares & Dots -0x28ACB (Symmetry Squares 4 + Dots) - 0x28ACA - Symmetry & Squares & Black/White Squares & Dots -0x28ACC (Symmetry Squares 5 + Dots) - 0x28ACB - Symmetry & Squares & Black/White Squares & Dots -0x2899C (Full Dot Grid Shapers 1) - True - Rotated Shapers & Dots -0x28A33 (Full Dot Grid Shapers 2) - 0x2899C - Shapers & Dots -0x28ABF (Full Dot Grid Shapers 3) - 0x28A33 - Shapers & Rotated Shapers & Dots -0x28AC0 (Full Dot Grid Shapers 4) - 0x28ABF - Rotated Shapers & Dots -0x28AC1 (Full Dot Grid Shapers 5) - 0x28AC0 - Rotated Shapers & Dots -0x28AD9 (Shapers & Dots & Eraser) - 0x28AC1 - Rotated Shapers & Dots & Eraser -0x17F5F (Windmill Door) - True - Dots +Quarry Boathouse Upper Back (Quarry Boathouse) - Quarry Boathouse Upper Middle - 0x3865F: +158155 - 0x38663 (Shortcut Door Panel) - True - True +Door - 0x3865F (Shortcut Door) - 0x38663 +158156 - 0x021B5 (Stars and Colored Eraser 1) - True - Stars & Stars + Same Colored Symbol & Eraser +158157 - 0x021B6 (Stars and Colored Eraser 2) - 0x021B5 - Stars & Stars + Same Colored Symbol & Eraser +158158 - 0x021B7 (Stars and Colored Eraser 3) - 0x021B6 - Stars & Stars + Same Colored Symbol & Eraser +158159 - 0x021BB (Stars and Colored Eraser 4) - 0x021B7 - Stars & Stars + Same Colored Symbol & Eraser +158160 - 0x09DB5 (Stars and Colored Eraser 5) - 0x021BB - Stars & Stars + Same Colored Symbol & Eraser +158161 - 0x09DB1 (Stars and Colored Eraser 6) - 0x09DB5 - Stars & Stars + Same Colored Symbol & Eraser +158162 - 0x3C124 (Stars and Colored Eraser 7) - 0x09DB1 - Stars & Stars + Same Colored Symbol & Eraser +158163 - 0x09DB3 (Stars & Eraser & Shapers 1) - 0x3C124 - Stars & Eraser & Shapers +158164 - 0x09DB4 (Stars & Eraser & Shapers 2) - 0x09DB3 - Stars & Eraser & Shapers +158165 - 0x275FA (Hook Control) - True - Shapers & Eraser +158167 - 0x0A3CB (Stars & Eraser & Shapers 3) - 0x09DB4 - Stars & Eraser & Shapers +158168 - 0x0A3CC (Stars & Eraser & Shapers 4) - 0x0A3CB - Stars & Eraser & Shapers +158169 - 0x0A3D0 (Stars & Eraser & Shapers 5) - 0x0A3CC - Stars & Eraser & Shapers -RGB House (Town) - Town - 0x28998: -0x034E4 (Sound Room Left) - True - Sound & Sound Waves -0x034E3 (Sound Room Right) - True - Sound & Sound Dots -0x334D8 (RGB Control) - 0x034E4 & 0x034E3 - Rotated Shapers & RGB & Squares & Colored Squares -0x03C0C (RGB Squares) - 0x334D8 - RGB & Squares & Colored Squares & Black/White Squares -0x03C08 (RGB Stars) - 0x334D8 - RGB & Stars +Shadows (Shadows) - Main Island - True - Shadows Ledge - 0x19B24 - Shadows Laser Room - 0x194B2 & 0x19665: +158170 - 0x334DB (Door Timer Outside) - True - True +Door - 0x19B24 (Timed Door) - 0x334DB +158171 - 0x0AC74 (Lower Avoid 6) - 0x0A8DC - Shadows Avoid +158172 - 0x0AC7A (Lower Avoid 7) - 0x0AC74 - Shadows Avoid +158173 - 0x0A8E0 (Lower Avoid 8) - 0x0AC7A - Shadows Avoid +158174 - 0x386FA (Environmental Avoid 1) - 0x0A8E0 - Shadows Avoid & Environment +158175 - 0x1C33F (Environmental Avoid 2) - 0x386FA - Shadows Avoid & Environment +158176 - 0x196E2 (Environmental Avoid 3) - 0x1C33F - Shadows Avoid & Environment +158177 - 0x1972A (Environmental Avoid 4) - 0x196E2 - Shadows Avoid & Environment +158178 - 0x19809 (Environmental Avoid 5) - 0x1972A - Shadows Avoid & Environment +158179 - 0x19806 (Environmental Avoid 6) - 0x19809 - Shadows Avoid & Environment +158180 - 0x196F8 (Environmental Avoid 7) - 0x19806 - Shadows Avoid & Environment +158181 - 0x1972F (Environmental Avoid 8) - 0x196F8 - Shadows Avoid & Environment +Door - 0x194B2 (Laser Room Right Door) - 0x1972F +158182 - 0x19797 (Follow 1) - 0x0A8E0 - Shadows Follow +158183 - 0x1979A (Follow 2) - 0x19797 - Shadows Follow +158184 - 0x197E0 (Follow 3) - 0x1979A - Shadows Follow +158185 - 0x197E8 (Follow 4) - 0x197E0 - Shadows Follow +158186 - 0x197E5 (Follow 5) - 0x197E8 - Shadows Follow +Door - 0x19665 (Laser Room Left Door) - 0x197E5 -Town Tower Top (Town) - Town - 0x28A69 & 0x28B39 & 0x28ACC & 0x28AD9: -0x032F5 (Laser) - True - True +Shadows Ledge (Shadows) - Shadows - 0x1855B - Quarry - 0x19865 & 0x0A2DF: +158187 - 0x334DC (Door Timer Inside) - True - True +158188 - 0x198B5 (Lower Avoid 1) - True - Shadows Avoid +158189 - 0x198BD (Lower Avoid 2) - 0x198B5 - Shadows Avoid +158190 - 0x198BF (Lower Avoid 3) - 0x198BD & 0x334DC & 0x19B24 - Shadows Avoid +Door - 0x19865 (Barrier to Quarry) - 0x198BF +Door - 0x0A2DF (Barrier to Quarry 2) - 0x198BF +158191 - 0x19771 (Lower Avoid 4) - 0x198BF - Shadows Avoid +158192 - 0x0A8DC (Lower Avoid 5) - 0x19771 - Shadows Avoid +Door - 0x1855B (Barrier to Shadows) - 0x0A8DC +Door - 0x19ADE (Barrier to Shadows 2) - 0x0A8DC -Windmill Interior (Windmill) - Town - 0x17F5F: -0x17D02 (Turn Control) - True - Dots -0x17F89 (Door to Front of Theater) - True - Squares & Black/White Squares +Shadows Laser Room (Shadows): +158703 - 0x19650 (Laser Panel) - True - Shadows Avoid & Shadows Follow +Laser - 0x181B3 (Laser) - 0x19650 -Theater (Theater) - Windmill Interior - 0x17F89: -0x00815 (Video Input) - True - True -0x03553 (Tutorial Video) - 0x00815 & 0x03481 - True -0x03552 (Desert Video) - 0x00815 & 0x0339E - True -0x0354E (Jungle Video) - 0x00815 & 0x03702 - True -0x03549 (Challenge Video) - 0x00815 & 0x2FAF6 - True -0x0354F (Shipwreck Video) - 0x00815 & 0x03535 - True -0x03545 (Mountain Video) - 0x00815 & 0x03542 - True -0x0A168 (Door to Cargo Box Left) - True - Squares & Black/White Squares & Eraser -0x33AB2 (Door to Cargo Box Right) - True - Squares & Black/White Squares & Shapers -0x17CF7 (Discard) - True - Triangles +Keep (Keep) - Main Island - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC: +158193 - 0x00139 (Hedge Maze 1) - True - Environment +158197 - 0x0A3A8 (Reset Pressure Plates 1) - True - True +158198 - 0x033EA (Pressure Plates 1) - 0x0A3A8 - Pressure Plates & Dots +Door - 0x01954 (Hedge Maze 1 Exit Door) - 0x00139 +Door - 0x01BEC (Pressure Plates 1 Exit Door) - 0x033EA -Jungle (Jungle) - Main Island - True: -0x17CDF (Shore Boat Spawn) - True - Boat -0x17F9B (Discard) - True - Triangles -0x002C4 (Waves 1) - True - Sound & Sound Waves -0x00767 (Waves 2) - 0x002C4 - Sound & Sound Waves -0x002C6 (Waves 3) - 0x00767 - Sound & Sound Waves -0x0070E (Waves 4) - 0x002C6 - Sound & Sound Waves -0x0070F (Waves 5) - 0x0070E - Sound & Sound Waves -0x0087D (Waves 6) - 0x0070F - Sound & Sound Waves -0x002C7 (Waves 7) - 0x0087D - Sound & Sound Waves -0x17CAB (Popup Wall Control) - 0x002C7 - True -0x0026D (Popup Wall 1) - 0x17CAB - Sound & Sound Dots -0x0026E (Popup Wall 2) - 0x0026D - Sound & Sound Dots -0x0026F (Popup Wall 3) - 0x0026E - Sound & Sound Dots -0x00C3F (Popup Wall 4) - 0x0026F - Sound & Sound Dots -0x00C41 (Popup Wall 5) - 0x00C3F - Sound & Sound Dots -0x014B2 (Popup Wall 6) - 0x00C41 - Sound & Sound Dots -0x03616 (Laser) - 0x014B2 - True -0x337FA (Shortcut to River) - True - True +Keep 2nd Maze (Keep) - Keep - 0x018CE - Keep 3rd Maze - 0x019D8: +Door - 0x018CE (Hedge Maze 2 Shortcut) - 0x00139 +158194 - 0x019DC (Hedge Maze 2) - True - Environment +Door - 0x019D8 (Hedge Maze 2 Exit Door) - 0x019DC -Outside Jungle River (River) - Main Island - True - Jungle - 0x337FA: -0x17CAA (Rhombic Avoid to Monastery Garden) - True - Environment -0x15ADD (Vault) - True - Environment & Black/White Squares & Dots -0x03702 (Vault Box) - 0x15ADD - True +Keep 3rd Maze (Keep) - Keep - 0x019B5 - Keep 4th Maze - 0x019E6: +Door - 0x019B5 (Hedge Maze 3 Shortcut) - 0x019DC +158195 - 0x019E7 (Hedge Maze 3) - True - Environment & Sound +Door - 0x019E6 (Hedge Maze 3 Exit Door) - 0x019E7 -Outside Bunker (Bunker) - Main Island - True - Inside Bunker - 0x0A079: -0x17C2E (Door to Bunker) - True - Squares & Black/White Squares -0x09DE0 (Laser) - 0x0A079 - True +Keep 4th Maze (Keep) - Keep - 0x0199A - Keep Tower - 0x01A0E: +Door - 0x0199A (Hedge Maze 4 Shortcut) - 0x019E7 +158196 - 0x01A0F (Hedge Maze 4) - True - Environment +Door - 0x01A0E (Hedge Maze 4 Exit Door) - 0x01A0F -Inside Bunker (Bunker) - Outside Bunker - 0x17C2E: -0x09F7D (Drawn Squares 1) - True - Squares & Colored Squares -0x09FDC (Drawn Squares 2) - 0x09F7D - Squares & Colored Squares & Black/White Squares -0x09FF7 (Drawn Squares 3) - 0x09FDC - Squares & Colored Squares & Black/White Squares -0x09F82 (Drawn Squares 4) - 0x09FF7 - Squares & Colored Squares & Black/White Squares -0x09FF8 (Drawn Squares 5) - 0x09F82 - Squares & Colored Squares & Black/White Squares -0x09D9F (Drawn Squares 6) - 0x09FF8 - Squares & Colored Squares & Black/White Squares -0x09DA1 (Drawn Squares 7) - 0x09D9F - Squares & Colored Squares -0x09DA2 (Drawn Squares 8) - 0x09DA1 - Squares & Colored Squares -0x09DAF (Drawn Squares 9) - 0x09DA2 - Squares & Colored Squares -0x0A099 (Door to Bunker Proper) - 0x09DAF - True -0x0A010 (Drawn Squares through Tinted Glass 1) - 0x0A099 - Squares & Colored Squares & RGB & Environment -0x0A01B (Drawn Squares through Tinted Glass 2) - 0x0A010 - Squares & Colored Squares & Black/White Squares & RGB & Environment -0x0A01F (Drawn Squares through Tinted Glass 3) - 0x0A01B - Squares & Colored Squares & Black/White Squares & RGB & Environment -0x34BC5 (Drop-Down Door Open) - 0x0A01F - True -0x34BC6 (Drop-Down Door Close) - 0x34BC5 - True -0x17E63 (Drop-Down Door Squares 1) - 0x0A01F & 0x34BC5 - Squares & Colored Squares & RGB & Environment -0x17E67 (Drop-Down Door Squares 2) - 0x17E63 & 0x34BC6 - Squares & Colored Squares & Black/White Squares & RGB -0x0A079 (Elevator Control) - 0x17E67 - Squares & Colored Squares & Black/White Squares & RGB +Keep 2nd Pressure Plate (Keep) - Keep 3rd Pressure Plate - 0x01BEA: +158199 - 0x0A3B9 (Reset Pressure Plates 2) - True - True +158200 - 0x01BE9 (Pressure Plates 2) - 0x0A3B9 - Pressure Plates & Stars & Stars + Same Colored Symbol & Squares & Black/White Squares +Door - 0x01BEA (Pressure Plates 2 Exit Door) - 0x01BE9 -Outside Swamp (Swamp) - Main Island - True: -0x0056E (Entry Door) - True - Shapers +Keep 3rd Pressure Plate (Keep) - Keep 4th Pressure Plate - 0x01CD5: +158201 - 0x0A3BB (Reset Pressure Plates 3) - True - True +158202 - 0x01CD3 (Pressure Plates 3) - 0x0A3BB - Pressure Plates & Shapers & Squares & Black/White Squares & Colored Squares +Door - 0x01CD5 (Pressure Plates 3 Exit Door) - 0x01CD3 -Swamp Entry Area (Swamp) - Outside Swamp - 0x0056E: -0x00469 (Seperatable Shapers 1) - True - Shapers -0x00472 (Seperatable Shapers 2) - 0x00469 - Shapers -0x00262 (Seperatable Shapers 3) - 0x00472 - Shapers -0x00474 (Seperatable Shapers 4) - 0x00262 - Shapers -0x00553 (Seperatable Shapers 5) - 0x00474 - Shapers -0x0056F (Seperatable Shapers 6) - 0x00553 - Shapers -0x00390 (Combinable Shapers 1) - 0x0056F - Shapers -0x010CA (Combinable Shapers 2) - 0x00390 - Shapers -0x00983 (Combinable Shapers 3) - 0x010CA - Shapers -0x00984 (Combinable Shapers 4) - 0x00983 - Shapers -0x00986 (Combinable Shapers 5) - 0x00984 - Shapers -0x00985 (Combinable Shapers 6) - 0x00986 - Shapers -0x00987 (Combinable Shapers 7) - 0x00985 - Shapers -0x181A9 (Combinable Shapers 8) - 0x00987 - Shapers -0x00609 (Sliding Bridge) - 0x181A9 - Shapers +Keep 4th Pressure Plate (Keep) - Keep - 0x09E3D - Keep Tower - 0x01D40: +158203 - 0x0A3AD (Reset Pressure Plates 4) - True - True +158204 - 0x01D3F (Pressure Plates 4) - 0x0A3AD - Pressure Plates & Shapers & Dots & Symmetry +Door - 0x01D40 (Pressure Plates 4 Exit Door) - 0x01D3F +158604 - 0x17D27 (Discard) - True - Triangles +158205 - 0x09E49 (Shortcut to Shadows Panel) - True - True +Door - 0x09E3D (Shortcut to Shadows) - 0x09E49 -Swamp Near Platform (Swamp) - Swamp Entry Area - 0x00609 | 0x18488: -0x00999 (Broken Shapers 1) - 0x00990 - Broken Shapers -0x0099D (Broken Shapers 2) - 0x00999 - Broken Shapers -0x009A0 (Broken Shapers 3) - 0x0099D - Broken Shapers -0x009A1 (Broken Shapers 4) - 0x009A0 - Broken Shapers -0x00002 (Cyan Underwater Negative Shapers 1) - 0x00006 - Shapers & Negative Shapers -0x00004 (Cyan Underwater Negative Shapers 2) - 0x00002 - Shapers & Negative Shapers -0x00005 (Cyan Underwater Negative Shapers 3) - 0x00004 - Shapers & Negative Shapers -0x013E6 (Cyan Underwater Negative Shapers 4) - 0x00005 - Shapers & Negative Shapers -0x00596 (Cyan Underwater Negative Shapers 5) - 0x013E6 - Shapers & Negative Shapers -0x18488 (Cyan Underwater Sliding Bridge Control) - 0x00006 - Shapers +Shipwreck (Shipwreck) - Keep 3rd Pressure Plate - True: +158654 - 0x00AFB (Vault) - True - Symmetry & Sound & Sound Dots & Colored Dots +158655 - 0x03535 (Vault Box) - 0x00AFB - True +158605 - 0x17D28 (Discard) - True - Triangles -Swamp Platform (Swamp) - Swamp Near Platform - True: -0x00982 (Platform Shapers 1) - True - Shapers -0x0097F (Platform Shapers 2) - 0x00982 - Shapers -0x0098F (Platform Shapers 3) - 0x0097F - Shapers -0x00990 (Platform Shapers 4) - 0x0098F - Shapers -0x17C0D (Platform Shortcut Door Left) - True - Shapers -0x17C0E (Platform Shortcut Door Right) - True - Shapers +Keep Tower (Keep) - Keep - 0x04F8F: +158206 - 0x0361B (Tower Shortcut to Keep Panel) - True - True +Door - 0x04F8F (Tower Shortcut to Keep) - 0x0361B +158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F - Environment & Sound +158705 - 0x03317 (Laser Panel Pressure Plates) - 0x01D3F - Shapers & Squares & Black/White Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Dots +Laser - 0x014BB (Laser) - 0x0360E | 0x03317 -Swamp Rotating Bridge Near Side (Swamp) - Swamp Near Platform - 0x009A1: -0x00007 (Rotated Shapers 1) - 0x009A1 - Rotated Shapers -0x00008 (Rotated Shapers 2) - 0x00007 - Rotated Shapers & Shapers -0x00009 (Rotated Shapers 3) - 0x00008 - Rotated Shapers -0x0000A (Rotated Shapers 4) - 0x00009 - Rotated Shapers -0x00001 (Red Underwater Negative Shapers 1) - 0x00596 - Shapers & Negative Shapers -0x014D2 (Red Underwater Negative Shapers 2) - 0x00596 - Shapers & Negative Shapers -0x014D4 (Red Underwater Negative Shapers 3) - 0x00596 - Shapers & Negative Shapers -0x014D1 (Red Underwater Negative Shapers 4) - 0x00596 - Shapers & Negative Shapers +Outside Monastery (Monastery) - Main Island - True - Main Island - 0x0364E - Inside Monastery - 0x0C128 & 0x0C153 - Monastery Garden - 0x03750: +158207 - 0x03713 (Shortcut Door Panel) - True - True +Door - 0x0364E (Shortcut) - 0x03713 +158208 - 0x00B10 (Door Open Left) - True - True +158209 - 0x00C92 (Door Open Right) - True - True +Door - 0x0C128 (Left Door) - 0x00B10 +Door - 0x0C153 (Right Door) - 0x00C92 +158210 - 0x00290 (Rhombic Avoid 1) - 0x09D9B - Environment +158211 - 0x00038 (Rhombic Avoid 2) - 0x09D9B & 0x00290 - Environment +158212 - 0x00037 (Rhombic Avoid 3) - 0x09D9B & 0x00038 - Environment +Door - 0x03750 (Door to Garden) - 0x00037 +158706 - 0x17CA4 (Laser Panel) - 0x193A6 - True +Laser - 0x17C65 (Laser) - 0x17CA4 -Swamp Near Boat (Swamp) - Swamp Rotating Bridge Near Side - 0x0000A - Swamp Platform - 0x17C0D & 0x17C0E: -0x181F5 (Rotating Bridge) - True - Rotated Shapers & Shapers -0x09DB8 (Boat Spawn) - True - Boat -0x003B2 (More Rotated Shapers 1) - 0x0000A - Rotated Shapers -0x00A1E (More Rotated Shapers 2) - 0x003B2 - Rotated Shapers -0x00C2E (More Rotated Shapers 3) - 0x00A1E - Rotated Shapers -0x00E3A (More Rotated Shapers 4) - 0x00C2E - Rotated Shapers -0x009A6 (Underwater Back Optional) - 0x00E3A & 0x181F5 - Shapers -0x009AB (Blue Underwater Negative Shapers 1) - 0x00E3A - Shapers & Negative Shapers -0x009AD (Blue Underwater Negative Shapers 2) - 0x009AB - Shapers & Negative Shapers -0x009AE (Blue Underwater Negetive Shapers 3) - 0x009AD - Shapers & Negative Shapers -0x009AF (Blue Underwater Negative Shapers 4) - 0x009AE - Shapers & Negative Shapers -0x00006 (Blue Underwater Negative Shapers 5) - 0x009AF - Shapers & Negative Shapers & Broken Negative Shapers -0x17E2B (Long Bridge Control) - True - Rotated Shapers & Shapers +Inside Monastery (Monastery): +158213 - 0x09D9B (Overhead Door Control) - True - Dots +158214 - 0x193A7 (Branch Avoid 1) - 0x00037 - Environment +158215 - 0x193AA (Branch Avoid 2) - 0x193A7 - Environment +158216 - 0x193AB (Branch Follow 1) - 0x193AA - Environment +158217 - 0x193A6 (Branch Follow 2) - 0x193AB - Environment -Swamp Maze (Swamp) - Swamp Rotating Bridge Near Side - 0x00001 & 0x014D2 & 0x014D4 & 0x014D1 - Outside Swamp - 0x17C05 & 0x17C02: -0x17C0A (Maze Control) - True - Shapers & Negative Shapers & Rotated Shapers & Environment -0x17E07 (Maze Control Other Side) - True - Shapers & Negative Shapers & Rotated Shapers & Environment -0x03615 (Laser) - 0x17C0A & 0x17E07 - True -0x17C05 (Near Laser Shortcut Door Left) - True - Rotated Shapers -0x17C02 (Near Laser Shortcut Door Right) - 0x17C05 - Shapers & Negative Shapers & Rotated Shapers +Monastery Garden (Monastery): -Treehouse Entry Area (Treehouse): -0x17C95 (Boat Spawn) - True - Boat -0x0288C (First Door) - True - Stars -0x02886 (Second Door) - 0x0288C - Stars -0x17D72 (Yellow Bridge 1) - 0x02886 - Stars -0x17D8F (Yellow Bridge 2) - 0x17D72 - Stars -0x17D74 (Yellow Bridge 3) - 0x17D8F - Stars -0x17DAC (Yellow Bridge 4) - 0x17D74 - Stars -0x17D9E (Yellow Bridge 5) - 0x17DAC - Stars -0x17DB9 (Yellow Bridge 6) - 0x17D9E - Stars -0x17D9C (Yellow Bridge 7) - 0x17DB9 - Stars -0x17DC2 (Yellow Bridge 8) - 0x17D9C - Stars -0x17DC4 (Yellow Bridge 9) - 0x17DC2 - Stars -0x0A182 (Beyond Yellow Bridge Door) - 0x17DC4 - Stars +Town (Town) - Main Island - True - Boat - 0x0A054 - Town Maze Rooftop - 0x28AA2 - Town Church - True - Town Wooden Rooftop - 0x034F5 - RGB House - 0x28A61 - Windmill Interior - 0x1845B - Town Inside Cargo Box - 0x0A0C9: +158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat +158219 - 0x0A0C8 (Cargo Box Panel) - True - Squares & Black/White Squares & Shapers +Door - 0x0A0C9 (Cargo Box Door) - 0x0A0C8 +158707 - 0x09F98 (Desert Laser Redirect) - True - True +158220 - 0x18590 (Tree Outlines) - True - Symmetry & Environment +158221 - 0x28AE3 (Vines Shadows Follow) - 0x18590 - Shadows Follow & Environment +158222 - 0x28938 (Four-way Apple Tree) - 0x28AE3 - Environment +158223 - 0x079DF (Triple Environmental Puzzle) - 0x28938 - Shadows Avoid & Environment & Reflection +158235 - 0x2899C (Full Dot Grid Shapers 1) - True - Rotated Shapers & Dots +158236 - 0x28A33 (Full Dot Grid Shapers 2) - 0x2899C - Shapers & Dots +158237 - 0x28ABF (Full Dot Grid Shapers 3) - 0x28A33 - Shapers & Rotated Shapers & Dots +158238 - 0x28AC0 (Full Dot Grid Shapers 4) - 0x28ABF - Rotated Shapers & Dots +158239 - 0x28AC1 (Full Dot Grid Shapers 5) - 0x28AC0 - Rotated Shapers & Dots +Door - 0x034F5 (Wooden Roof Staircase) - 0x28AC1 +158225 - 0x28998 (Tinted Door Panel) - True - Stars & Rotated Shapers +Door - 0x28A61 (Tinted Door to RGB House) - 0x28998 +158226 - 0x28A0D (Door to Church Stars Panel) - 0x28998 - Stars & RGB & Environment +Door - 0x03BB0 (Door to Church) - 0x28A0D +158228 - 0x28A79 (Maze Stair Control) - True - Environment +Door - 0x28AA2 (Maze Staircase) - 0x28A79 +158241 - 0x17F5F (Windmill Door Panel) - True - Dots +Door - 0x1845B (Windmill Door) - 0x17F5F -Treehouse Beyond Yellow Bridge (Treehouse) - Treehouse Entry Area - 0x0A182: -0x2700B (Laser House Door Timer Outside Control) - True - True -0x17DC8 (First Purple Bridge 1) - True - Stars & Dots -0x17DC7 (First Purple Bridge 2) - 0x17DC8 - Stars & Dots -0x17CE4 (First Purple Bridge 3) - 0x17DC7 - Stars & Dots -0x17D2D (First Purple Bridge 4) - 0x17CE4 - Stars & Dots -0x17D6C (First Purple Bridge 5) - 0x17D2D - Stars & Dots -0x17D9B (Second Purple Bridge 1) - 0x17D6C - Stars & Squares & Black/White Squares -0x17D99 (Second Purple Bridge 2) - 0x17D9B - Stars & Squares & Black/White Squares -0x17DAA (Second Purple Bridge 3) - 0x17D99 - Stars & Squares & Black/White Squares -0x17D97 (Second Purple Bridge 4) - 0x17DAA - Stars & Squares & Black/White Squares & Colored Squares -0x17BDF (Second Purple Bridge 5) - 0x17D97 - Stars & Squares & Colored Squares -0x17D91 (Second Purple Bridge 6) - 0x17BDF - Stars & Squares & Colored Squares -0x17DC6 (Second Purple Bridge 7) - 0x17D91 - Stars & Squares & Colored Squares -0x17E3C (Green Bridge 1) - True - Stars & Shapers -0x17E4D (Green Bridge 2) - 0x17E3C - Stars & Shapers -0x17E4F (Green Bridge 3) - 0x17E4D - Stars & Shapers & Rotated Shapers -0x17E52 (Green Bridge 4 & Directional) - 0x17E4F - Stars & Rotated Shapers & Environment -0x17E5B (Green Bridge 5) - 0x17E52 - Stars & Shapers & Colored Shapers & Stars + Same Colored Symbol -0x17E5F (Green Bridge 6) - 0x17E5B - Stars & Shapers & Colored Shapers & Negative Shapers & Colored Negative Shapers & Stars + Same Colored Symbol -0x17E61 (Green Bridge 7) - 0x17E5F - Stars & Shapers & Rotated Shapers -0x17FA9 (Green Bridge Discard) - 0x17E61 - Triangles -0x17DB3 (Left Orange Bridge 1) - 0x17DC6 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x17DB5 (Left Orange Bridge 2) - 0x17DB3 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x17DB6 (Left Orange Bridge 3) - 0x17DB5 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x17DC0 (Left Orange Bridge 4) - 0x17DB6 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x17DD7 (Left Orange Bridge 5) - 0x17DC0 - Stars & Squares & Black/White Squares & Colored Squares & Stars + Same Colored Symbol -0x17DD9 (Left Orange Bridge 6) - 0x17DD7 - Stars & Squares & Black/White Squares & Colored Squares & Stars + Same Colored Symbol -0x17DB8 (Left Orange Bridge 7) - 0x17DD9 - Stars & Squares & Black/White Squares & Colored Squares & Stars + Same Colored Symbol -0x17DDC (Left Orange Bridge 8) - 0x17DB8 - Stars & Squares & Colored Squares & Stars + Same Colored Symbol -0x17DD1 (Left Orange Bridge 9 & Directional) - 0x17DDC - Stars & Squares & Colored Squares & Stars + Same Colored Symbol & Environment -0x17DDE (Left Orange Bridge 10) - 0x17DD1 - Stars & Squares & Colored Squares & Stars + Same Colored Symbol -0x17DE3 (Left Orange Bridge 11) - 0x17DDE - Stars & Squares & Colored Squares & Stars + Same Colored Symbol -0x17DEC (Left Orange Bridge 12) - 0x17DE3 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x17DAE (Left Orange Bridge 13) - 0x17DEC - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x17DB0 (Left Orange Bridge 14) - 0x17DAE - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x17DDB (Left Orange Bridge 15) - 0x17DB0 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x17FA0 (Burned House Discard) - 0x17DDB - Triangles -0x17D88 (Right Orange Bridge 1) - True - Stars -0x17DB4 (Right Orange Bridge 2) - 0x17D88 - Stars -0x17D8C (Right Orange Bridge 3) - 0x17DB4 - Stars -0x17CE3 (Right Orange Bridge 4 & Directional) - 0x17D8C - Stars & Environment -0x17DCD (Right Orange Bridge 5) - 0x17CE3 - Stars -0x17DB2 (Right Orange Bridge 6) - 0x17DCD - Stars -0x17DCC (Right Orange Bridge 7) - 0x17DB2 - Stars -0x17DCA (Right Orange Bridge 8) - 0x17DCC - Stars -0x17D8E (Right Orange Bridge 9) - 0x17DCA - Stars -0x17DB7 (Right Orange Bridge 10 & Directional) - 0x17D8E - Stars -0x17DB1 (Right Orange Bridge 11) - 0x17DB7 - Stars -0x17DA2 (Right Orange Bridge 12) - 0x17DB1 - Stars +Town Inside Cargo Box (Town): +158606 - 0x17D01 (Cargo Box Discard) - True - Triangles -Treehouse Laser Room (Treehouse) - Treehouse Beyond Yellow Bridge - 0x2700B & 0x17DA2 & 0x17DDB: -0x03613 (Laser) - True - True -0x17CBC (Laser House Door Timer Inside Control) - True - True +Town Maze Rooftop (Town) - Town Red Rooftop - 0x2896A: +158229 - 0x2896A (Maze Rooftop Bridge Control) - True - Shapers -Treehouse Bridge Platform (Treehouse) - Treehouse Beyond Yellow Bridge - 0x17DA2 - Main Island - 0x037FF: -0x037FF (Bridge Control) - True - Stars +Town Red Rooftop (Town): +158607 - 0x17C71 (Rooftop Discard) - True - Triangles +158230 - 0x28AC7 (Symmetry Squares 1) - True - Symmetry & Squares & Black/White Squares +158231 - 0x28AC8 (Symmetry Squares 2) - 0x28AC7 - Symmetry & Squares & Black/White Squares +158232 - 0x28ACA (Symmetry Squares 3 + Dots) - 0x28AC8 - Symmetry & Squares & Black/White Squares & Dots +158233 - 0x28ACB (Symmetry Squares 4 + Dots) - 0x28ACA - Symmetry & Squares & Black/White Squares & Dots +158234 - 0x28ACC (Symmetry Squares 5 + Dots) - 0x28ACB - Symmetry & Squares & Black/White Squares & Dots +158224 - 0x28B39 (Hexagonal Reflection) - 0x079DF - Reflection -Mountaintop (Mountaintop) - Main Island - True: -0x0042D (River Shape) - True - True -0x09F7F (Box Short) - 7 Lasers - True -0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True -0x17C34 (Trap Door Triple Exit) - 0x09F7F - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x17C42 (Discard) - True - Triangles -0x002A6 (Vault) - True - Symmetry & Colored Dots & Squares & Black/White Squares & Dots -0x03542 (Vault Box) - 0x002A6 - True +Town Wooden Rooftop (Town): +158240 - 0x28AD9 (Shapers & Dots & Eraser) - 0x28AC1 - Rotated Shapers & Dots & Eraser -Inside Mountain Top Layer (Inside Mountain) - Mountaintop - 0x17C34: -0x09E39 (Light Bridge Controller) - True - Squares & Black/White Squares & Colored Squares & Eraser & Colored Eraser +Town Church (Town): +158227 - 0x28A69 (Church Lattice) - 0x03BB0 - Environment -Inside Mountain Top Layer Bridge (Inside Mountain) - Inside Mountain Top Layer - 0x09E39: -0x09E7A (Obscured Vision 1) - True - Obscured & Squares & Black/White Squares & Dots -0x09E71 (Obscured Vision 2) - 0x09E7A - Obscured & Squares & Black/White Squares & Dots -0x09E72 (Obscured Vision 3) - 0x09E71 - Obscured & Squares & Black/White Squares & Shapers & Dots -0x09E69 (Obscured Vision 4) - 0x09E72 - Obscured & Squares & Black/White Squares & Dots -0x09E7B (Obscured Vision 5) - 0x09E69 - Obscured & Squares & Black/White Squares & Dots -0x09E73 (Moving Background 1) - True - Moving & Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x09E75 (Moving Background 2) - 0x09E73 - Moving & Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x09E78 (Moving Background 3) - 0x09E75 - Moving & Shapers -0x09E79 (Moving Background 4) - 0x09E78 - Moving & Shapers & Rotated Shapers -0x09E6C (Moving Background 5) - 0x09E79 - Moving & Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x09E6F (Moving Background 6) - 0x09E6C - Moving & Stars & Rotated Shapers & Shapers -0x09E6B (Moving Background 7) - 0x09E6F - Moving & Stars & Dots -0x33AF5 (Physically Obstructed 1) - True - Squares & Black/White Squares & Environment & Symmetry -0x33AF7 (Physically Obstructed 2) - 0x33AF5 - Squares & Black/White Squares & Stars & Environment -0x09F6E (Physically Obstructed 3) - 0x33AF7 - Symmetry & Dots & Environment -0x09EAD (Angled Inside Trash 1) - True - Squares & Black/White Squares & Shapers & Angled -0x09EAF (Angled Inside Trash 2) - 0x09EAD - Squares & Black/White Squares & Shapers & Angled +RGB House (Town) - RGB Room - 0x2897B: +158242 - 0x034E4 (Sound Room Left) - True - Sound & Sound Waves +158243 - 0x034E3 (Sound Room Right) - True - Sound & Sound Dots +Door - 0x2897B (RGB House Staircase) - 0x034E4 & 0x034E3 -Inside Mountain Second Layer (Inside Mountain) - Inside Mountain Top Layer Bridge - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B: -0x09FD3 (Color Cycle 1) - True - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol -0x09FD4 (Color Cycle 2) - 0x09FD3 - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol -0x09FD6 (Color Cycle 3) - 0x09FD4 - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol -0x09FD7 (Color Cycle 4) - 0x09FD6 - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol & Shapers & Colored Shapers -0x09FD8 (Color Cycle 5) - 0x09FD7 - Color Cycle & RGB & Squares & Colored Squares & Symmetry & Colored Dots -0x09E86 (Light Bridge Controller 2) - 0x09FD8 - Stars & Stars + Same Colored Symbol & Colored Rotated Shapers & Rotated Shapers & Eraser & Two Lines +RGB Room (Town): +158244 - 0x334D8 (RGB Control) - True - Rotated Shapers & RGB & Squares & Colored Squares +158245 - 0x03C0C (RGB Squares) - 0x334D8 - RGB & Squares & Colored Squares & Black/White Squares +158246 - 0x03C08 (RGB Stars) - 0x334D8 - RGB & Stars -Inside Mountain Second Layer Beyond Bridge (Inside Mountain) - Inside Mountain Second Layer - 0x09E86: -0x09FCC (Same Solution 1) - True - Dots & Same Solution -0x09FCE (Same Solution 2) - 0x09FCC - Squares & Black/White Squares & Same Solution -0x09FCF (Same Solution 3) - 0x09FCE - Stars & Same Solution -0x09FD0 (Same Solution 4) - 0x09FCF - Rotated Shapers & Same Solution -0x09FD1 (Same Solution 5) - 0x09FD0 - Stars & Squares & Colored Squares & Stars + Same Colored Symbol & Same Solution -0x09FD2 (Same Solution 6) - 0x09FD1 - Shapers & Same Solution -0x09ED8 (Light Bridge Controller 3) - 0x09FD2 - Stars & Stars + Same Colored Symbol & Colored Rotated Shapers & Rotated Shapers & Eraser & Two Lines +Town Tower (Town Tower) - Town - True - Town Tower Top - 0x27798 & 0x27799 & 0x2779A & 0x2779C: +Door - 0x27798 (Blue Panels Door) - 0x28ACC +Door - 0x27799 (Church Lattice Door) - 0x28A69 +Door - 0x2779A (Environmental Set Door) - 0x28B39 +Door - 0x2779C (Eraser Set Door) - 0x28AD9 -Inside Mountain Second Layer Elevator (Inside Mountain) - Inside Mountain Second Layer - 0x09ED8 & 0x09E86: -0x09EEB (Elevator Control Panel) - True - Dots -0x17F93 (Elevator Discard) - True - Triangles +Town Tower Top (Town): +158708 - 0x032F5 (Laser Panel) - True - True +Laser - 0x032F9 (Laser) - 0x032F5 -Inside Mountain Third Layer (Inside Mountain) - Inside Mountain Second Layer Elevator - 0x09EEB: -0x09FC1 (Giant Puzzle Bottom Left) - True - Shapers & Eraser -0x09F8E (Giant Puzzle Bottom Right) - True - Shapers & Eraser -0x09F01 (Giant Puzzle Top Right) - True - Rotated Shapers -0x09EFF (Giant Puzzle Top Left) - True - Shapers & Eraser -0x09FDA (Giant Puzzle) - 0x09FC1 & 0x09F8E & 0x09F01 & 0x09EFF - Shapers & Symmetry +Windmill Interior (Windmill) - Theater - 0x17F88: +158247 - 0x17D02 (Turn Control) - True - Dots +158248 - 0x17F89 (Door to Front of Theater Panel) - True - Squares & Black/White Squares +Door - 0x17F88 (Door to Front of Theater) - 0x17F89 -Inside Mountain Bottom Layer (Inside Mountain) - Inside Mountain Third Layer - 0x09FDA - Inside Mountain Path to Secret Area - 0x334E1: -0x17FA2 (Bottom Layer Discard) - 0xFFF00 - Triangles & Environment -0x01983 (Door to Final Room Left) - True - Shapers & Stars -0x01987 (Door to Final Room Right) - True - Squares & Colored Squares & Dots +Theater (Theater) - Town - 0x0A16D | 0x3CCDF: +158656 - 0x00815 (Video Input) - True - True +158657 - 0x03553 (Tutorial Video) - 0x00815 & 0x03481 - True +158658 - 0x03552 (Desert Video) - 0x00815 & 0x0339E - True +158659 - 0x0354E (Jungle Video) - 0x00815 & 0x03702 - True +158660 - 0x03549 (Challenge Video) - 0x00815 & 0x0356B - True +158661 - 0x0354F (Shipwreck Video) - 0x00815 & 0x03535 - True +158662 - 0x03545 (Mountain Video) - 0x00815 & 0x03542 - True +158249 - 0x0A168 (Door to Cargo Box Left Panel) - True - Squares & Black/White Squares & Eraser +158250 - 0x33AB2 (Door to Cargo Box Right Panel) - True - Squares & Black/White Squares & Shapers +Door - 0x0A16D (Door to Cargo Box Left) - 0x0A168 +Door - 0x3CCDF (Door to Cargo Box Right) - 0x33AB2 +158608 - 0x17CF7 (Discard) - True - Triangles -Inside Mountain Path to Secret Area (Inside Mountain) - Inside Mountain Bottom Layer - 0x17FA2: -0x00FF8 (Door to Secret Area) - True - Triangles & Black/White Squares & Squares -0x334E1 (Rock Control) - True - True +Jungle (Jungle) - Main Island - True - Outside Jungle River - 0x3873B - Boat - 0x17CDF: +158251 - 0x17CDF (Shore Boat Spawn) - True - Boat +158609 - 0x17F9B (Discard) - True - Triangles +158252 - 0x002C4 (Waves 1) - True - Sound & Sound Waves +158253 - 0x00767 (Waves 2) - 0x002C4 - Sound & Sound Waves +158254 - 0x002C6 (Waves 3) - 0x00767 - Sound & Sound Waves +158255 - 0x0070E (Waves 4) - 0x002C6 - Sound & Sound Waves +158256 - 0x0070F (Waves 5) - 0x0070E - Sound & Sound Waves +158257 - 0x0087D (Waves 6) - 0x0070F - Sound & Sound Waves +158258 - 0x002C7 (Waves 7) - 0x0087D - Sound & Sound Waves +158259 - 0x17CAB (Popup Wall Control) - 0x002C7 - True +Door - 0x1475B (Popup Wall) - 0x17CAB +158260 - 0x0026D (Popup Wall 1) - 0x1475B - Sound & Sound Dots +158261 - 0x0026E (Popup Wall 2) - 0x0026D - Sound & Sound Dots +158262 - 0x0026F (Popup Wall 3) - 0x0026E - Sound & Sound Dots +158263 - 0x00C3F (Popup Wall 4) - 0x0026F - Sound & Sound Dots +158264 - 0x00C41 (Popup Wall 5) - 0x00C3F - Sound & Sound Dots +158265 - 0x014B2 (Popup Wall 6) - 0x00C41 - Sound & Sound Dots +158709 - 0x03616 (Laser Panel) - 0x014B2 - True +Laser - 0x00274 (Laser) - 0x03616 +158266 - 0x337FA (Shortcut to River Panel) - True - True +Door - 0x3873B (Shortcut to River) - 0x337FA -Inside Mountain Secret Area (Inside Mountain Secret Area) - Inside Mountain Path to Secret Area - 0x00FF8 - Main Island - 0x021D7 - Main Island - 0x17CF2: -0x021D7 (Shortcut to Mountain) - True - Triangles & Stars & Stars + Same Colored Symbol & Colored Triangles -0x17CF2 (Shortcut to Swamp) - True - Triangles -0x335AB (Elevator Inside Control) - True - Dots & Squares & Black/White Squares -0x335AC (Elevator Upper Outside Control) - 0x335AB - Squares & Black/White Squares -0x3369D (Elevator Lower Outside Control) - 0x335AB - Squares & Black/White Squares & Dots -0x00190 (Dot Grid Triangles 1) - True - Dots & Triangles -0x00558 (Dot Grid Triangles 2) - 0x00190 - Dots & Triangles -0x00567 (Dot Grid Triangles 3) - 0x00558 - Dots & Triangles -0x006FE (Dot Grid Triangles 4) - 0x00567 - Dots & Triangles -0x01A0D (Symmetry Triangles) - True - Symmetry & Triangles -0x008B8 (Squares and Triangles) - True - Squares & Black/White Squares & Triangles -0x00973 (Stars and Triangles) - 0x008B8 - Stars & Triangles -0x0097B (Stars and Triangles of same color) - 0x00973 - Stars & Triangles & Stars and Triangles of same color & Stars + Same Colored Symbol -0x0097D (Stars & Squares and Triangles) - 0x0097B - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol & Triangles -0x0097E (Stars & Squares and Triangles 2) - 0x0097D - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol & Stars and Triangles of same color -0x00994 (Rotated Shapers and Triangles 1) - True - Rotated Shapers & Triangles -0x334D5 (Rotated Shapers and Triangles 2) - 0x00994 - Rotated Shapers & Triangles -0x00995 (Rotated Shapers and Triangles 3) - 0x334D5 - Rotated Shapers & Triangles -0x00996 (Shapers and Triangles 1) - 0x00995 - Shapers & Triangles -0x00998 (Shapers and Triangles 2) - 0x00996 - Shapers & Triangles -0x009A4 (Broken Shapers) - True - Shapers & Broken Shapers -0x018A0 (Symmetry Shapers) - True - Shapers & Symmetry -0x00A72 (Broken and Negative Shapers) - True - Shapers & Broken Shapers & Negative Shapers -0x32962 (Rotated Broken Shapers) - True - Rotated Shapers & Broken Rotated Shapers -0x32966 (Stars and Squares) - True - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol -0x01A31 (Rainbow Squares) - True - Color Cycle & RGB & Squares & Colored Squares -0x00B71 (Squares & Stars and Colored Eraser) - True - Colored Eraser & Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Eraser -0x09DD5 (Lone Pillar) - True - Pillar & Triangles -0x0A16E (Door to Challenge) - 0x09DD5 - Stars & Shapers & Colored Shapers & Stars + Same Colored Symbol -0x288EA (Wooden Beam Shapers) - True - Environment & Shapers -0x288FC (Wooden Beam Squares and Shapers) - True - Environment & Squares & Black/White Squares & Shapers & Rotated Shapers -0x289E7 (Wooden Beam Stars and Squares) - True - Environment & Stars & Squares & Black/White Squares -0x288AA (Wooden Beam Shapers and Stars) - True - Environment & Stars & Shapers -0x17FB9 (Upstairs Dot Grid Negative Shapers) - True - Shapers & Dots & Negative Shapers -0x0A16B (Upstairs Dot Grid Gap Dots) - True - Dots -0x0A2CE (Upstairs Dot Grid Stars) - 0x0A16B - Stars & Dots -0x0A2D7 (Upstairs Dot Grid Stars & Squares) - 0x0A2CE - Dots & Black/White Squares & Stars + Same Colored Symbol & Stars -0x0A2DD (Upstairs Dot Grid Shapers) - 0x0A2D7 - Shapers & Dots -0x0A2EA (Upstairs Dot Grid Rotated Shapers) - 0x0A2DD - Rotated Shapers & Dots -0x0008F (Upstairs Invisible Dots 1) - True - Dots & Invisible Dots -0x0006B (Upstairs Invisible Dots 2) - 0x0008F - Dots & Invisible Dots -0x0008B (Upstairs Invisible Dots 3) - 0x0006B - Dots & Invisible Dots -0x0008C (Upstairs Invisible Dots 4) - 0x0008B - Dots & Invisible Dots -0x0008A (Upstairs Invisible Dots 5) - 0x0008C - Dots & Invisible Dots -0x00089 (Upstairs Invisible Dots 6) - 0x0008A - Dots & Invisible Dots -0x0006A (Upstairs Invisible Dots 7) - 0x00089 - Dots & Invisible Dots -0x0006C (Upstairs Invisible Dots 8) - 0x0006A - Dots & Invisible Dots -0x00027 (Upstairs Invisible Dot Symmetry 1) - True - Dots & Invisible Dots & Symmetry -0x00028 (Upstairs Invisible Dot Symmetry 2) - 0x00027 - Dots & Invisible Dots & Symmetry -0x00029 (Upstairs Invisible Dot Symmetry 3) - 0x00028 - Dots & Invisible Dots & Symmetry +Outside Jungle River (River) - Main Island - True - Monastery Garden - 0x0CF2A: +158267 - 0x17CAA (Rhombic Avoid to Monastery Garden) - True - Environment +Door - 0x0CF2A (Shortcut to Monastery Garden) - 0x17CAA +158663 - 0x15ADD (Vault) - True - Environment & Black/White Squares & Dots +158664 - 0x03702 (Vault Box) - 0x15ADD - True -Challenge (Challenge) - Inside Mountain Secret Area - 0x0A16E: -0x0A332 (Start Timer) - 11 Lasers - True -0x0088E (Small Basic) - 0x0A332 - True -0x00BAF (Big Basic) - 0x0088E - True -0x00BF3 (Square) - 0x00BAF - Squares & Black/White Squares -0x00C09 (Maze Map) - 0x00BF3 - Dots -0x00CDB (Stars and Dots) - 0x00C09 - Stars & Dots -0x0051F (Symmetry) - 0x00CDB - Symmetry & Colored Dots & Dots -0x00524 (Stars and Shapers) - 0x0051F - Stars & Shapers -0x00CD4 (Big Basic 2) - 0x00524 - True -0x00CB9 (Choice Squares Right) - 0x00CD4 - Squares & Black/White Squares -0x00CA1 (Choice Squares Middle) - 0x00CD4 - Squares & Black/White Squares -0x00C80 (Choice Squares Left) - 0x00CD4 - Squares & Black/White Squares -0x00C68 (Choice Squares 2 Right) - 0x00CB9 | 0x00CA1 | 0x00C80 - Squares & Black/White Squares & Colored Squares -0x00C59 (Choice Squares 2 Middle) - 0x00CB9 | 0x00CA1 | 0x00C80 - Squares & Black/White Squares & Colored Squares -0x00C22 (Choice Squares 2 Left) - 0x00CB9 | 0x00CA1 | 0x00C80 - Squares & Black/White Squares & Colored Squares -0x034F4 (Maze Hidden 1) - 0x00C68 | 0x00C59 | 0x00C22 - Triangles -0x034EC (Maze Hidden 2) - 0x00C68 | 0x00C59 | 0x00C22 - Triangles -0x1C31A (Dots Pillar) - 0x034F4 & 0x034EC - Dots & Symmetry & Pillar -0x1C319 (Squares Pillar) - 0x034F4 & 0x034EC - Squares & Black/White Squares & Symmetry & Pillar -0x0356B (Vault Box) - 0x1C31A & 0x1C319 - True -0x039B4 (Door to Theater Walkway) - True - Triangles +Outside Bunker (Bunker) - Main Island - True - Inside Bunker - 0x0C2A4: +158268 - 0x17C2E (Bunker Entry Panel) - True - Squares & Black/White Squares & Colored Squares +Door - 0x0C2A4 (Bunker Entry Door) - 0x17C2E -Theater Walkway (Theater Walkway) - Challenge - 0x039B4 - Theater - 0x27732 - Desert Elevator Room - 0x2773D & 0x03608 - Town - 0x09E85: -0x2FAF6 (Vault Box) - True - True -0x27732 (Door to Back of Theater) - True - True -0x2773D (Door to Desert Elevator Room) - True - True -0x09E85 (Door to Town) - True - Triangles +Inside Bunker (Bunker) - Inside Bunker Glass Room - 0x17C79: +158269 - 0x09F7D (Drawn Squares 1) - True - Squares & Colored Squares +158270 - 0x09FDC (Drawn Squares 2) - 0x09F7D - Squares & Colored Squares & Black/White Squares +158271 - 0x09FF7 (Drawn Squares 3) - 0x09FDC - Squares & Colored Squares & Black/White Squares +158272 - 0x09F82 (Drawn Squares 4) - 0x09FF7 - Squares & Colored Squares & Black/White Squares +158273 - 0x09FF8 (Drawn Squares 5) - 0x09F82 - Squares & Colored Squares & Black/White Squares +158274 - 0x09D9F (Drawn Squares 6) - 0x09FF8 - Squares & Colored Squares & Black/White Squares +158275 - 0x09DA1 (Drawn Squares 7) - 0x09D9F - Squares & Colored Squares +158276 - 0x09DA2 (Drawn Squares 8) - 0x09DA1 - Squares & Colored Squares +158277 - 0x09DAF (Drawn Squares 9) - 0x09DA2 - Squares & Colored Squares +158278 - 0x0A099 (Door to Bunker Proper Panel) - 0x09DAF - True +Door - 0x17C79 (Door to Bunker Proper) - 0x0A099 -Final Room (Inside Mountain Final Room) - Inside Mountain Bottom Layer - 0x01983 & 0x01987: -0x0383A (Stars Pillar) - True - Stars & Pillar -0x09E56 (Stars and Dots Pillar) - 0x0383A - Stars & Dots & Pillar -0x09E5A (Dot Grid Pillar) - 0x09E56 - Dots & Pillar -0x33961 (Sparse Dots Pillar) - 0x09E5A - Dots & Symmetry & Pillar -0x0383D (Dot Maze Pillar) - True - Dots & Pillar -0x0383F (Squares Pillar) - 0x0383D - Squares & Black/White Squares & Pillar -0x03859 (Shapers Pillar) - 0x0383F - Shapers & Pillar -0x339BB (Squares and Stars) - 0x03859 - Squares & Black/White Squares & Stars & Symmetry & Pillar +Inside Bunker Glass Room (Bunker) - Inside Bunker Ultraviolet Room - 0x0C2A3: +158279 - 0x0A010 (Drawn Squares through Tinted Glass 1) - True - Squares & Colored Squares & RGB & Environment +158280 - 0x0A01B (Drawn Squares through Tinted Glass 2) - 0x0A010 - Squares & Colored Squares & Black/White Squares & RGB & Environment +158281 - 0x0A01F (Drawn Squares through Tinted Glass 3) - 0x0A01B - Squares & Colored Squares & Black/White Squares & RGB & Environment +Door - 0x0C2A3 (Door to Ultraviolet Room) - 0x0A01F -Elevator (Inside Mountain Final Room) - Final Room - 0x339BB & 0x33961: -0x3D9A6 (Elevator Door Closer Left) - True - True -0x3D9A7 (Elevator Door Close Right) - True - True -0x3C113 (Elevator Door Open Left) - 0x3D9A6 | 0x3D9A7 - True -0x3C114 (Elevator Door Open Right) - 0x3D9A6 | 0x3D9A7 - True -0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True -0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True -0x3D9A9 (Elevator Start) - 0x3D9AA | 0x3D9A8 - True +Inside Bunker Ultraviolet Room (Bunker) - Inside Bunker Elevator Section - 0x0A08D: +158282 - 0x34BC5 (Drop-Down Door Open) - True - True +158283 - 0x34BC6 (Drop-Down Door Close) - 0x34BC5 - True +158284 - 0x17E63 (Drop-Down Door Squares 1) - 0x34BC5 - Squares & Colored Squares & RGB & Environment +158285 - 0x17E67 (Drop-Down Door Squares 2) - 0x17E63 & 0x34BC6 - Squares & Colored Squares & Black/White Squares & RGB +Door - 0x0A08D (Door to Elevator) - 0x17E67 -Boat (Boat) - Main Island - 0x17CDF | 0x17CC8 & 0x0005C | 0x17CA6 | 0x09DB8 | 0x17C95 | 0x0A054 - Inside Glass Factory - 0x17CDF & 0x0005C | 0x17CC8 & 0x0005C | 0x17CA6 & 0x0005C | 0x09DB8 & 0x0005C | 0x17C95 & 0x0005C | 0x0A054 & 0x0005C - Quarry Boathouse - 0x17CA6 - Swamp Near Boat - 0x17CDF | 0x17CC8 & 0x0005C | 0x17CA6 | 0x09DB8 | 0x17C95 | 0x0A054 - Treehouse Entry Area - 0x17CDF | 0x17CC8 & 0x0005C | 0x17CA6 | 0x09DB8 | 0x17C95 | 0x0A054: +Inside Bunker Elevator Section (Bunker) - Bunker Laser Platform - 0x0A079: +158286 - 0x0A079 (Elevator Control) - True - Squares & Colored Squares & Black/White Squares & RGB + +Bunker Laser Platform (Bunker): +158710 - 0x09DE0 (Laser Panel) - True - True +Laser - 0x0C2B2 (Laser) - 0x09DE0 + +Outside Swamp (Swamp) - Swamp Entry Area - 0x00C1C - Main Island - True: +158287 - 0x0056E (Entry Panel) - True - Shapers +Door - 0x00C1C (Entry Door) - 0x0056E + +Swamp Entry Area (Swamp) - Swamp Sliding Bridge - TrueOneWay: +158288 - 0x00469 (Seperatable Shapers 1) - True - Shapers +158289 - 0x00472 (Seperatable Shapers 2) - 0x00469 - Shapers +158290 - 0x00262 (Seperatable Shapers 3) - 0x00472 - Shapers +158291 - 0x00474 (Seperatable Shapers 4) - 0x00262 - Shapers +158292 - 0x00553 (Seperatable Shapers 5) - 0x00474 - Shapers +158293 - 0x0056F (Seperatable Shapers 6) - 0x00553 - Shapers +158294 - 0x00390 (Combinable Shapers 1) - 0x0056F - Shapers +158295 - 0x010CA (Combinable Shapers 2) - 0x00390 - Shapers +158296 - 0x00983 (Combinable Shapers 3) - 0x010CA - Shapers +158297 - 0x00984 (Combinable Shapers 4) - 0x00983 - Shapers +158298 - 0x00986 (Combinable Shapers 5) - 0x00984 - Shapers +158299 - 0x00985 (Combinable Shapers 6) - 0x00986 - Shapers +158300 - 0x00987 (Combinable Shapers 7) - 0x00985 - Shapers +158301 - 0x181A9 (Combinable Shapers 8) - 0x00987 - Shapers + +Swamp Sliding Bridge (Swamp) - Swamp Entry Area - 0x00609 - Swamp Near Platform - 0x00609: +158302 - 0x00609 (Sliding Bridge) - True - Shapers + +Swamp Near Platform (Swamp) - Swamp Cyan Underwater - 0x04B7F - Swamp Near Boat - 0x38AE6 - Swamp Broken Shapers - 0x184B7 - Swamp Sliding Bridge - TrueOneWay: +158313 - 0x00982 (Platform Shapers 1) - True - Shapers +158314 - 0x0097F (Platform Shapers 2) - 0x00982 - Shapers +158315 - 0x0098F (Platform Shapers 3) - 0x0097F - Shapers +158316 - 0x00990 (Platform Shapers 4) - 0x0098F - Shapers +Door - 0x184B7 (Door to Broken Shapers) - 0x00990 +158317 - 0x17C0D (Platform Shortcut Left Panel) - True - Shapers +158318 - 0x17C0E (Platform Shortcut Right Panel) - True - Shapers +Door - 0x38AE6 (Platform Shortcut Door) - 0x17C0E +Door - 0x04B7F (Cyan Water Pump) - 0x00006 + +Swamp Cyan Underwater (Swamp): +158307 - 0x00002 (Cyan Underwater Negative Shapers 1) - True - Shapers & Negative Shapers +158308 - 0x00004 (Cyan Underwater Negative Shapers 2) - 0x00002 - Shapers & Negative Shapers +158309 - 0x00005 (Cyan Underwater Negative Shapers 3) - 0x00004 - Shapers & Negative Shapers +158310 - 0x013E6 (Cyan Underwater Negative Shapers 4) - 0x00005 - Shapers & Negative Shapers +158311 - 0x00596 (Cyan Underwater Negative Shapers 5) - 0x013E6 - Shapers & Negative Shapers +158312 - 0x18488 (Cyan Underwater Sliding Bridge Control) - True - Shapers + +Swamp Broken Shapers (Swamp) - Swamp Rotated Shapers - 0x18507: +158303 - 0x00999 (Broken Shapers 1) - 0x00990 - Shapers & Broken Shapers +158304 - 0x0099D (Broken Shapers 2) - 0x00999 - Shapers & Broken Shapers +158305 - 0x009A0 (Broken Shapers 3) - 0x0099D - Shapers & Broken Shapers +158306 - 0x009A1 (Broken Shapers 4) - 0x009A0 - Shapers & Broken Shapers +Door - 0x18507 (Door to Rotated Shapers) - 0x009A1 + +Swamp Rotated Shapers (Swamp) - Swamp Red Underwater - 0x183F2 - Swamp Rotating Bridge - TrueOneWay: +158319 - 0x00007 (Rotated Shapers 1) - 0x009A1 - Rotated Shapers +158320 - 0x00008 (Rotated Shapers 2) - 0x00007 - Rotated Shapers & Shapers +158321 - 0x00009 (Rotated Shapers 3) - 0x00008 - Rotated Shapers +158322 - 0x0000A (Rotated Shapers 4) - 0x00009 - Rotated Shapers +Door - 0x183F2 (Red Water Pump) - 0x00596 + +Swamp Red Underwater (Swamp) - Swamp Maze - 0x014D1: +158323 - 0x00001 (Red Underwater Negative Shapers 1) - True - Shapers & Negative Shapers +158324 - 0x014D2 (Red Underwater Negative Shapers 2) - True - Shapers & Negative Shapers +158325 - 0x014D4 (Red Underwater Negative Shapers 3) - True - Shapers & Negative Shapers +158326 - 0x014D1 (Red Underwater Negative Shapers 4) - True - Shapers & Negative Shapers +Door - 0x305D5 (Red Underwater Exit) - 0x014D1 + +Swamp Rotating Bridge (Swamp) - Swamp Rotated Shapers - 0x181F5 - Swamp Near Boat - 0x181F5 - Swamp Purple Area - 0x181F5: +158327 - 0x181F5 (Rotating Bridge) - True - Rotated Shapers & Shapers + +Swamp Near Boat (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Blue Underwater - 0x18482: +158328 - 0x09DB8 (Boat Spawn) - True - Boat +158329 - 0x003B2 (More Rotated Shapers 1) - 0x0000A - Rotated Shapers +158330 - 0x00A1E (More Rotated Shapers 2) - 0x003B2 - Rotated Shapers +158331 - 0x00C2E (More Rotated Shapers 3) - 0x00A1E - Rotated Shapers +158332 - 0x00E3A (More Rotated Shapers 4) - 0x00C2E - Rotated Shapers +158339 - 0x17E2B (Long Bridge Control) - True - Rotated Shapers & Shapers +Door - 0x18482 (Blue Water Pump) - 0x00E3A + +Swamp Purple Area (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Purple Underwater - 0x0A1D6: +Door - 0x0A1D6 (Purple Water Pump) - 0x00E3A + +Swamp Purple Underwater (Swamp): +158333 - 0x009A6 (Underwater Back Optional) - True - Shapers + +Swamp Blue Underwater (Swamp): +158334 - 0x009AB (Blue Underwater Negative Shapers 1) - True - Shapers & Negative Shapers +158335 - 0x009AD (Blue Underwater Negative Shapers 2) - 0x009AB - Shapers & Negative Shapers +158336 - 0x009AE (Blue Underwater Negetive Shapers 3) - 0x009AD - Shapers & Negative Shapers +158337 - 0x009AF (Blue Underwater Negative Shapers 4) - 0x009AE - Shapers & Negative Shapers +158338 - 0x00006 (Blue Underwater Negative Shapers 5) - 0x009AF - Shapers & Negative Shapers & Broken Negative Shapers + +Swamp Maze (Swamp) - Swamp Laser Area - 0x17C0A & 0x17E07: +158340 - 0x17C0A (Maze Control) - True - Shapers & Negative Shapers & Rotated Shapers & Environment +158112 - 0x17E07 (Maze Control Other Side) - True - Shapers & Negative Shapers & Rotated Shapers & Environment + +Swamp Laser Area (Swamp) - Outside Swamp - 0x2D880: +158711 - 0x03615 (Laser Panel) - True - True +Laser - 0x00BF6 (Laser) - 0x03615 +158341 - 0x17C05 (Near Laser Shortcut Left Panel) - True - Rotated Shapers +158342 - 0x17C02 (Near Laser Shortcut Right Panel) - 0x17C05 - Shapers & Negative Shapers & Rotated Shapers +Door - 0x2D880 (Near Laser Shortcut) - 0x17C02 + +Treehouse Entry Area (Treehouse) - Treehouse Between Doors - 0x0C309: +158343 - 0x17C95 (Boat Spawn) - True - Boat +158344 - 0x0288C (First Door Panel) - True - Stars +Door - 0x0C309 (First Door) - 0x0288C + +Treehouse Between Doors (Treehouse) - Treehouse Yellow Bridge - 0x0C310: +158345 - 0x02886 (Second Door Panel) - True - Stars +Door - 0x0C310 (Second Door) - 0x02886 + +Treehouse Yellow Bridge (Treehouse) - Treehouse After Yellow Bridge - 0x17DC4: +158346 - 0x17D72 (Yellow Bridge 1) - True - Stars +158347 - 0x17D8F (Yellow Bridge 2) - 0x17D72 - Stars +158348 - 0x17D74 (Yellow Bridge 3) - 0x17D8F - Stars +158349 - 0x17DAC (Yellow Bridge 4) - 0x17D74 - Stars +158350 - 0x17D9E (Yellow Bridge 5) - 0x17DAC - Stars +158351 - 0x17DB9 (Yellow Bridge 6) - 0x17D9E - Stars +158352 - 0x17D9C (Yellow Bridge 7) - 0x17DB9 - Stars +158353 - 0x17DC2 (Yellow Bridge 8) - 0x17D9C - Stars +158354 - 0x17DC4 (Yellow Bridge 9) - 0x17DC2 - Stars + +Treehouse After Yellow Bridge (Treehouse) - Treehouse Junction - 0x0A181: +158355 - 0x0A182 (Beyond Yellow Bridge Door Panel) - True - Stars +Door - 0x0A181 (Beyond Yellow Bridge Door) - 0x0A182 + +Treehouse Junction (Treehouse) - Treehouse Right Orange Bridge - True - Treehouse First Purple Bridge - True - Treehouse Green Bridge - True: +158356 - 0x2700B (Laser House Door Timer Outside Control) - True - True + +Treehouse First Purple Bridge (Treehouse) - Treehouse Second Purple Bridge - 0x17D6C: +158357 - 0x17DC8 (First Purple Bridge 1) - True - Stars & Dots +158358 - 0x17DC7 (First Purple Bridge 2) - 0x17DC8 - Stars & Dots +158359 - 0x17CE4 (First Purple Bridge 3) - 0x17DC7 - Stars & Dots +158360 - 0x17D2D (First Purple Bridge 4) - 0x17CE4 - Stars & Dots +158361 - 0x17D6C (First Purple Bridge 5) - 0x17D2D - Stars & Dots + +Treehouse Right Orange Bridge (Treehouse) - Treehouse Bridge Platform - 0x17DA2: +158391 - 0x17D88 (Right Orange Bridge 1) - True - Stars +158392 - 0x17DB4 (Right Orange Bridge 2) - 0x17D88 - Stars +158393 - 0x17D8C (Right Orange Bridge 3) - 0x17DB4 - Stars +158394 - 0x17CE3 (Right Orange Bridge 4 & Directional) - 0x17D8C - Stars & Environment +158395 - 0x17DCD (Right Orange Bridge 5) - 0x17CE3 - Stars +158396 - 0x17DB2 (Right Orange Bridge 6) - 0x17DCD - Stars +158397 - 0x17DCC (Right Orange Bridge 7) - 0x17DB2 - Stars +158398 - 0x17DCA (Right Orange Bridge 8) - 0x17DCC - Stars +158399 - 0x17D8E (Right Orange Bridge 9) - 0x17DCA - Stars +158400 - 0x17DB7 (Right Orange Bridge 10 & Directional) - 0x17D8E - Stars +158401 - 0x17DB1 (Right Orange Bridge 11) - 0x17DB7 - Stars +158402 - 0x17DA2 (Right Orange Bridge 12) - 0x17DB1 - Stars + +Treehouse Bridge Platform (Treehouse) - Main Island - 0x0C32D: +158404 - 0x037FF (Bridge Control) - True - Stars +Door - 0x0C32D (Drawbridge) - 0x037FF + +Treehouse Second Purple Bridge (Treehouse) - Treehouse Left Orange Bridge - 0x17DC6: +158362 - 0x17D9B (Second Purple Bridge 1) - True - Stars & Squares & Black/White Squares +158363 - 0x17D99 (Second Purple Bridge 2) - 0x17D9B - Stars & Squares & Black/White Squares +158364 - 0x17DAA (Second Purple Bridge 3) - 0x17D99 - Stars & Squares & Black/White Squares +158365 - 0x17D97 (Second Purple Bridge 4) - 0x17DAA - Stars & Squares & Black/White Squares & Colored Squares +158366 - 0x17BDF (Second Purple Bridge 5) - 0x17D97 - Stars & Squares & Colored Squares +158367 - 0x17D91 (Second Purple Bridge 6) - 0x17BDF - Stars & Squares & Colored Squares +158368 - 0x17DC6 (Second Purple Bridge 7) - 0x17D91 - Stars & Squares & Colored Squares + +Treehouse Left Orange Bridge (Treehouse) - Treehouse Laser Room - 0x0C323: +158376 - 0x17DB3 (Left Orange Bridge 1) - True - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158377 - 0x17DB5 (Left Orange Bridge 2) - 0x17DB3 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158378 - 0x17DB6 (Left Orange Bridge 3) - 0x17DB5 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158379 - 0x17DC0 (Left Orange Bridge 4) - 0x17DB6 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158380 - 0x17DD7 (Left Orange Bridge 5) - 0x17DC0 - Stars & Squares & Black/White Squares & Colored Squares & Stars + Same Colored Symbol +158381 - 0x17DD9 (Left Orange Bridge 6) - 0x17DD7 - Stars & Squares & Black/White Squares & Colored Squares & Stars + Same Colored Symbol +158382 - 0x17DB8 (Left Orange Bridge 7) - 0x17DD9 - Stars & Squares & Black/White Squares & Colored Squares & Stars + Same Colored Symbol +158383 - 0x17DDC (Left Orange Bridge 8) - 0x17DB8 - Stars & Squares & Colored Squares & Stars + Same Colored Symbol +158384 - 0x17DD1 (Left Orange Bridge 9 & Directional) - 0x17DDC - Stars & Squares & Colored Squares & Stars + Same Colored Symbol & Environment +158385 - 0x17DDE (Left Orange Bridge 10) - 0x17DD1 - Stars & Squares & Colored Squares & Stars + Same Colored Symbol +158386 - 0x17DE3 (Left Orange Bridge 11) - 0x17DDE - Stars & Squares & Colored Squares & Stars + Same Colored Symbol +158387 - 0x17DEC (Left Orange Bridge 12) - 0x17DE3 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158388 - 0x17DAE (Left Orange Bridge 13) - 0x17DEC - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158389 - 0x17DB0 (Left Orange Bridge 14) - 0x17DAE - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158390 - 0x17DDB (Left Orange Bridge 15) - 0x17DB0 - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158611 - 0x17FA0 (Burnt House Discard) - 0x17DDB - Triangles +Door - 0x0C323 (Door to Laser House) - 0x17DDB & 0x17DA2 & 0x2700B + +Treehouse Green Bridge (Treehouse): +158369 - 0x17E3C (Green Bridge 1) - True - Stars & Shapers +158370 - 0x17E4D (Green Bridge 2) - 0x17E3C - Stars & Shapers +158371 - 0x17E4F (Green Bridge 3) - 0x17E4D - Stars & Shapers & Rotated Shapers +158372 - 0x17E52 (Green Bridge 4 & Directional) - 0x17E4F - Stars & Rotated Shapers & Environment +158373 - 0x17E5B (Green Bridge 5) - 0x17E52 - Stars & Shapers & Colored Shapers & Stars + Same Colored Symbol +158374 - 0x17E5F (Green Bridge 6) - 0x17E5B - Stars & Shapers & Colored Shapers & Negative Shapers & Colored Negative Shapers & Stars + Same Colored Symbol +158375 - 0x17E61 (Green Bridge 7) - 0x17E5F - Stars & Shapers & Rotated Shapers +158610 - 0x17FA9 (Green Bridge Discard) - 0x17E61 - Triangles + +Treehouse Laser Room (Treehouse): +158712 - 0x03613 (Laser Panel) - True - True +158403 - 0x17CBC (Laser House Door Timer Inside Control) - True - True +Laser - 0x028A4 (Laser) - 0x03613 + +Mountaintop (Mountaintop) - Main Island - True - Inside Mountain Top Layer - 0x17C34: +158405 - 0x0042D (River Shape) - True - True +158406 - 0x09F7F (Box Short) - 7 Lasers - True +158407 - 0x17C34 (Trap Door Triple Exit) - 0x09F7F - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158612 - 0x17C42 (Discard) - True - Triangles +158665 - 0x002A6 (Vault) - True - Symmetry & Colored Dots & Squares & Black/White Squares & Dots +158666 - 0x03542 (Vault Box) - 0x002A6 - True +158800 - 0xFFF00 (Box Long) - 7 Lasers & 11 Lasers & 0x17C34 - True + +Inside Mountain Top Layer (Inside Mountain) - Inside Mountain Top Layer Bridge - 0x09E39: +158408 - 0x09E39 (Light Bridge Controller) - True - Squares & Black/White Squares & Colored Squares & Eraser & Colored Eraser + +Inside Mountain Top Layer Bridge (Inside Mountain) - Inside Mountain Second Layer - 0x09E54: +158409 - 0x09E7A (Obscured Vision 1) - True - Obscured & Squares & Black/White Squares & Dots +158410 - 0x09E71 (Obscured Vision 2) - 0x09E7A - Obscured & Squares & Black/White Squares & Dots +158411 - 0x09E72 (Obscured Vision 3) - 0x09E71 - Obscured & Squares & Black/White Squares & Shapers & Dots +158412 - 0x09E69 (Obscured Vision 4) - 0x09E72 - Obscured & Squares & Black/White Squares & Dots +158413 - 0x09E7B (Obscured Vision 5) - 0x09E69 - Obscured & Squares & Black/White Squares & Dots +158414 - 0x09E73 (Moving Background 1) - True - Moving & Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158415 - 0x09E75 (Moving Background 2) - 0x09E73 - Moving & Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158416 - 0x09E78 (Moving Background 3) - 0x09E75 - Moving & Shapers +158417 - 0x09E79 (Moving Background 4) - 0x09E78 - Moving & Shapers & Rotated Shapers +158418 - 0x09E6C (Moving Background 5) - 0x09E79 - Moving & Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158419 - 0x09E6F (Moving Background 6) - 0x09E6C - Moving & Stars & Rotated Shapers & Shapers +158420 - 0x09E6B (Moving Background 7) - 0x09E6F - Moving & Stars & Dots +158421 - 0x33AF5 (Physically Obstructed 1) - True - Squares & Black/White Squares & Environment & Symmetry +158422 - 0x33AF7 (Physically Obstructed 2) - 0x33AF5 - Squares & Black/White Squares & Stars & Environment +158423 - 0x09F6E (Physically Obstructed 3) - 0x33AF7 - Symmetry & Dots & Environment +158424 - 0x09EAD (Angled Inside Trash 1) - True - Squares & Black/White Squares & Shapers & Angled +158425 - 0x09EAF (Angled Inside Trash 2) - 0x09EAD - Squares & Black/White Squares & Shapers & Angled +Door - 0x09E54 (Door to Second Layer) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B + +Inside Mountain Second Layer (Inside Mountain) - Inside Mountain Second Layer Light Bridge Room Near - 0x09FFB - Inside Mountain Second Layer Blue Bridge - 0x09E86: +158426 - 0x09FD3 (Color Cycle 1) - True - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol +158427 - 0x09FD4 (Color Cycle 2) - 0x09FD3 - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol +158428 - 0x09FD6 (Color Cycle 3) - 0x09FD4 - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol +158429 - 0x09FD7 (Color Cycle 4) - 0x09FD6 - Color Cycle & RGB & Stars & Squares & Colored Squares & Stars + Same Colored Symbol & Shapers & Colored Shapers +158430 - 0x09FD8 (Color Cycle 5) - 0x09FD7 - Color Cycle & RGB & Squares & Colored Squares & Symmetry & Colored Dots +Door - 0x09FFB (Staircase Near) - 0x09FD8 + +Inside Mountain Second Layer Blue Bridge (Inside Mountain) - Inside Mountain Second Layer Beyond Bridge - TrueOneWay - Inside Mountain Second Layer Elevator Room - 0x09EDD: +Door - 0x09EDD (Door to Elevator) - 0x09ED8 & 0x09E86 + +Inside Mountain Second Layer Light Bridge Room Near (Inside Mountain): +158431 - 0x09E86 (Light Bridge Controller 2) - True - Stars & Stars + Same Colored Symbol & Colored Rotated Shapers & Rotated Shapers & Eraser & Two Lines + +Inside Mountain Second Layer Beyond Bridge (Inside Mountain) - Inside Mountain Second Layer Light Bridge Room Far - 0x09E07: +158432 - 0x09FCC (Same Solution 1) - True - Dots & Same Solution +158433 - 0x09FCE (Same Solution 2) - 0x09FCC - Squares & Black/White Squares & Same Solution +158434 - 0x09FCF (Same Solution 3) - 0x09FCE - Stars & Same Solution +158435 - 0x09FD0 (Same Solution 4) - 0x09FCF - Rotated Shapers & Same Solution +158436 - 0x09FD1 (Same Solution 5) - 0x09FD0 - Stars & Squares & Colored Squares & Stars + Same Colored Symbol & Same Solution +158437 - 0x09FD2 (Same Solution 6) - 0x09FD1 - Shapers & Same Solution +Door - 0x09E07 (Staircase Far) - 0x09FD2 + +Inside Mountain Second Layer Light Bridge Room Far (Inside Mountain): +158438 - 0x09ED8 (Light Bridge Controller 3) - True - Stars & Stars + Same Colored Symbol & Colored Rotated Shapers & Rotated Shapers & Eraser & Two Lines + +Inside Mountain Second Layer Elevator Room (Inside Mountain) - Inside Mountain Second Layer Elevator - TrueOneWay: +158613 - 0x17F93 (Elevator Discard) - True - Triangles + +Inside Mountain Second Layer Elevator (Inside Mountain) - Inside Mountain Second Layer Elevator Room - 0x09EEB - Inside Mountain Third Layer - 0x09EEB: +158439 - 0x09EEB (Elevator Control Panel) - True - Dots + +Inside Mountain Third Layer (Inside Mountain) - Inside Mountain Second Layer Elevator - TrueOneWay - Inside Mountain Bottom Layer - 0x09F89: +158440 - 0x09FC1 (Giant Puzzle Bottom Left) - True - Shapers & Eraser +158441 - 0x09F8E (Giant Puzzle Bottom Right) - True - Shapers & Eraser +158442 - 0x09F01 (Giant Puzzle Top Right) - True - Rotated Shapers +158443 - 0x09EFF (Giant Puzzle Top Left) - True - Shapers & Eraser +158444 - 0x09FDA (Giant Puzzle) - 0x09FC1 & 0x09F8E & 0x09F01 & 0x09EFF - Shapers & Symmetry +Door - 0x09F89 (Glass Door) - 0x09FDA + +Inside Mountain Bottom Layer (Inside Mountain) - Inside Mountain Bottom Layer Rock - 0x17FA2 - Final Room - 0x0C141: +158614 - 0x17FA2 (Bottom Layer Discard) - 0xFFF00 - Triangles & Environment +158445 - 0x01983 (Door to Final Room Left) - True - Shapers & Stars +158446 - 0x01987 (Door to Final Room Right) - True - Squares & Colored Squares & Dots +Door - 0x0C141 (Door to Final Room) - 0x01983 & 0x01987 + +Inside Mountain Bottom Layer Rock (Inside Mountain) - Inside Mountain Bottom Layer - 0x17F33 - Inside Mountain Path to Secret Area - 0x17F33: +Door - 0x17F33 (Bottom Layer Rock Open) - True + +Inside Mountain Path to Secret Area (Inside Mountain) - Inside Mountain Bottom Layer Rock - 0x334E1 - Inside Mountain Caves - 0x2D77D: +158447 - 0x00FF8 (Secret Area Entry Panel) - True - Triangles & Black/White Squares & Squares +Door - 0x2D77D (Door to Secret Area) - 0x00FF8 +158448 - 0x334E1 (Rock Control) - True - True + +Inside Mountain Caves (Inside Mountain Caves) - Main Island - 0x2D73F - Main Island - 0x2D859 - Path to Challenge - 0x019A5: +158451 - 0x335AB (Elevator Inside Control) - True - Dots & Squares & Black/White Squares +158452 - 0x335AC (Elevator Upper Outside Control) - 0x335AB - Squares & Black/White Squares +158453 - 0x3369D (Elevator Lower Outside Control) - 0x335AB - Squares & Black/White Squares & Dots +158454 - 0x00190 (Dot Grid Triangles 1) - True - Dots & Triangles +158455 - 0x00558 (Dot Grid Triangles 2) - 0x00190 - Dots & Triangles +158456 - 0x00567 (Dot Grid Triangles 3) - 0x00558 - Dots & Triangles +158457 - 0x006FE (Dot Grid Triangles 4) - 0x00567 - Dots & Triangles +158458 - 0x01A0D (Symmetry Triangles) - True - Symmetry & Triangles +158459 - 0x008B8 (Squares and Triangles) - True - Squares & Black/White Squares & Triangles +158460 - 0x00973 (Stars and Triangles) - 0x008B8 - Stars & Triangles +158461 - 0x0097B (Stars and Triangles of same color) - 0x00973 - Stars & Triangles & Stars and Triangles of same color & Stars + Same Colored Symbol +158462 - 0x0097D (Stars & Squares and Triangles) - 0x0097B - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol & Triangles +158463 - 0x0097E (Stars & Squares and Triangles 2) - 0x0097D - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol & Stars and Triangles of same color +158464 - 0x00994 (Rotated Shapers and Triangles 1) - True - Rotated Shapers & Triangles +158465 - 0x334D5 (Rotated Shapers and Triangles 2) - 0x00994 - Rotated Shapers & Triangles +158466 - 0x00995 (Rotated Shapers and Triangles 3) - 0x334D5 - Rotated Shapers & Triangles +158467 - 0x00996 (Shapers and Triangles 1) - 0x00995 - Shapers & Triangles +158468 - 0x00998 (Shapers and Triangles 2) - 0x00996 - Shapers & Triangles +158469 - 0x009A4 (Broken Shapers) - True - Shapers & Broken Shapers +158470 - 0x018A0 (Symmetry Shapers) - True - Shapers & Symmetry +158471 - 0x00A72 (Broken and Negative Shapers) - True - Shapers & Broken Shapers & Negative Shapers +158472 - 0x32962 (Rotated Broken Shapers) - True - Rotated Shapers & Broken Rotated Shapers +158473 - 0x32966 (Stars and Squares) - True - Stars & Squares & Black/White Squares & Stars + Same Colored Symbol +158474 - 0x01A31 (Rainbow Squares) - True - Color Cycle & RGB & Squares & Colored Squares +158475 - 0x00B71 (Squares & Stars and Colored Eraser) - True - Colored Eraser & Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Eraser +158478 - 0x288EA (Wooden Beam Shapers) - True - Environment & Shapers +158479 - 0x288FC (Wooden Beam Squares and Shapers) - True - Environment & Squares & Black/White Squares & Shapers & Rotated Shapers +158480 - 0x289E7 (Wooden Beam Stars and Squares) - True - Environment & Stars & Squares & Black/White Squares +158481 - 0x288AA (Wooden Beam Shapers and Stars) - True - Environment & Stars & Shapers +158482 - 0x17FB9 (Upstairs Dot Grid Negative Shapers) - True - Shapers & Dots & Negative Shapers +158483 - 0x0A16B (Upstairs Dot Grid Gap Dots) - True - Dots +158484 - 0x0A2CE (Upstairs Dot Grid Stars) - 0x0A16B - Stars & Dots +158485 - 0x0A2D7 (Upstairs Dot Grid Stars & Squares) - 0x0A2CE - Dots & Black/White Squares & Stars + Same Colored Symbol & Stars +158486 - 0x0A2DD (Upstairs Dot Grid Shapers) - 0x0A2D7 - Shapers & Dots +158487 - 0x0A2EA (Upstairs Dot Grid Rotated Shapers) - 0x0A2DD - Rotated Shapers & Dots +158488 - 0x0008F (Upstairs Invisible Dots 1) - True - Dots & Invisible Dots +158489 - 0x0006B (Upstairs Invisible Dots 2) - 0x0008F - Dots & Invisible Dots +158490 - 0x0008B (Upstairs Invisible Dots 3) - 0x0006B - Dots & Invisible Dots +158491 - 0x0008C (Upstairs Invisible Dots 4) - 0x0008B - Dots & Invisible Dots +158492 - 0x0008A (Upstairs Invisible Dots 5) - 0x0008C - Dots & Invisible Dots +158493 - 0x00089 (Upstairs Invisible Dots 6) - 0x0008A - Dots & Invisible Dots +158494 - 0x0006A (Upstairs Invisible Dots 7) - 0x00089 - Dots & Invisible Dots +158495 - 0x0006C (Upstairs Invisible Dots 8) - 0x0006A - Dots & Invisible Dots +158496 - 0x00027 (Upstairs Invisible Dot Symmetry 1) - True - Dots & Invisible Dots & Symmetry +158497 - 0x00028 (Upstairs Invisible Dot Symmetry 2) - 0x00027 - Dots & Invisible Dots & Symmetry +158498 - 0x00029 (Upstairs Invisible Dot Symmetry 3) - 0x00028 - Dots & Invisible Dots & Symmetry +158476 - 0x09DD5 (Lone Pillar) - True - Pillar & Triangles +Door - 0x019A5 (Secret Black Door to Challenge) - 0x09DD5 +158449 - 0x021D7 (Shortcut to Mountain Panel) - True - Triangles & Stars & Stars + Same Colored Symbol & Colored Triangles +Door - 0x2D73F (Shortcut to Mountain Door) - 0x021D7 +158450 - 0x17CF2 (Shortcut to Swamp Panel) - True - Triangles +Door - 0x2D859 (Shortcut to Swamp Door) - 0x17CF2 + +Path to Challenge (Inside Mountain Caves) - Challenge - 0x0A19A: +158477 - 0x0A16E (Challenge Entry Panel) - True - Stars & Shapers & Colored Shapers & Stars + Same Colored Symbol +Door - 0x0A19A (Challenge Entry Door) - 0x0A16E + +Challenge (Challenge) - Theater Walkway - 0x0348A: +158499 - 0x0A332 (Start Timer) - 11 Lasers - True +158500 - 0x0088E (Small Basic) - 0x0A332 - True +158501 - 0x00BAF (Big Basic) - 0x0088E - True +158502 - 0x00BF3 (Square) - 0x00BAF - Squares & Black/White Squares +158503 - 0x00C09 (Maze Map) - 0x00BF3 - Dots +158504 - 0x00CDB (Stars and Dots) - 0x00C09 - Stars & Dots +158505 - 0x0051F (Symmetry) - 0x00CDB - Symmetry & Colored Dots & Dots +158506 - 0x00524 (Stars and Shapers) - 0x0051F - Stars & Shapers +158507 - 0x00CD4 (Big Basic 2) - 0x00524 - True +158508 - 0x00CB9 (Choice Squares Right) - 0x00CD4 - Squares & Black/White Squares +158509 - 0x00CA1 (Choice Squares Middle) - 0x00CD4 - Squares & Black/White Squares +158510 - 0x00C80 (Choice Squares Left) - 0x00CD4 - Squares & Black/White Squares +158511 - 0x00C68 (Choice Squares 2 Right) - 0x00CB9 | 0x00CA1 | 0x00C80 - Squares & Black/White Squares & Colored Squares +158512 - 0x00C59 (Choice Squares 2 Middle) - 0x00CB9 | 0x00CA1 | 0x00C80 - Squares & Black/White Squares & Colored Squares +158513 - 0x00C22 (Choice Squares 2 Left) - 0x00CB9 | 0x00CA1 | 0x00C80 - Squares & Black/White Squares & Colored Squares +158514 - 0x034F4 (Maze Hidden 1) - 0x00C68 | 0x00C59 | 0x00C22 - Triangles +158515 - 0x034EC (Maze Hidden 2) - 0x00C68 | 0x00C59 | 0x00C22 - Triangles +158516 - 0x1C31A (Dots Pillar) - 0x034F4 & 0x034EC - Dots & Symmetry & Pillar +158517 - 0x1C319 (Squares Pillar) - 0x034F4 & 0x034EC - Squares & Black/White Squares & Symmetry & Pillar +158667 - 0x0356B (Vault Box) - 0x1C31A & 0x1C319 - True +158518 - 0x039B4 (Door to Theater Walkway Panel) - True - Triangles +Door - 0x0348A (Door to Theater Walkway) - 0x039B4 + +Theater Walkway (Theater Walkway) - Windmill Interior - 0x27739 - Desert Lowest Level Inbetween Shortcuts - 0x27263 - Town - 0x09E87: +158668 - 0x2FAF6 (Vault Box) - True - True +158519 - 0x27732 (Theater Shortcut Panel) - True - True +Door - 0x27739 (Door to Windmill Interior) - 0x27732 +158520 - 0x2773D (Desert Shortcut Panel) - True - True +Door - 0x27263 (Door to Desert Elevator Room) - 0x2773D +158521 - 0x09E85 (Town Shortcut Panel) - True - Triangles +Door - 0x09E87 (Door to Town) - 0x09E85 + +Final Room (Inside Mountain Final Room) - Elevator - 0x339BB & 0x33961: +158522 - 0x0383A (Right Pillar 1) - True - Stars & Pillar +158523 - 0x09E56 (Right Pillar 2) - 0x0383A - Stars & Dots & Pillar +158524 - 0x09E5A (Right Pillar 3) - 0x09E56 - Dots & Pillar +158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Dots & Symmetry & Pillar +158526 - 0x0383D (Left Pillar 1) - True - Dots & Pillar +158527 - 0x0383F (Left Pillar 2) - 0x0383D - Squares & Black/White Squares & Pillar +158528 - 0x03859 (Left Pillar 3) - 0x0383F - Shapers & Pillar +158529 - 0x339BB (Left Pillar 4) - 0x03859 - Squares & Black/White Squares & Stars & Symmetry & Pillar + +Elevator (Inside Mountain Final Room): +158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True +158531 - 0x3D9A7 (Elevator Door Close Right) - True - True +158532 - 0x3C113 (Elevator Door Open Left) - 0x3D9A6 | 0x3D9A7 - True +158533 - 0x3C114 (Elevator Door Open Right) - 0x3D9A6 | 0x3D9A7 - True +158534 - 0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True +158535 - 0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True +158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA | 0x3D9A8 - True + +Boat (Boat) - Main Island - TrueOneWay - Swamp Near Boat - TrueOneWay - Treehouse Entry Area - TrueOneWay - Quarry Boathouse Behind Staircase - TrueOneWay - Inside Glass Factory Behind Back Wall - TrueOneWay: diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index 0857ef6b42..01669cffba 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -35,7 +35,7 @@ class WitnessWorld(World): """ game = "The Witness" topology_present = False - data_version = 2 + data_version = 5 static_logic = StaticWitnessLogic() static_locat = StaticWitnessLocations() @@ -53,11 +53,18 @@ class WitnessWorld(World): 'seed': self.world.random.randint(0, 1000000), 'victory_location': int(self.player_logic.VICTORY_LOCATION, 16), 'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID, - 'doorhex_to_id': self.player_logic.DOOR_DICT_FOR_CLIENT, - 'door_connections_to_sever': self.player_logic.DOOR_CONNECTIONS_TO_SEVER + 'item_id_to_door_hexes': self.items.ITEM_ID_TO_DOOR_HEX, + 'door_hexes': self.items.DOORS, + 'symbols_not_in_the_game': self.items.SYMBOLS_NOT_IN_THE_GAME } def generate_early(self): + if not (is_option_enabled(self.world, self.player, "shuffle_symbols") + or get_option_value(self.world, self.player, "shuffle_doors") + or is_option_enabled(self.world, self.player, "shuffle_lasers")): + raise Exception("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle, Door" + " Shuffle or Laser Shuffle") + self.player_logic = WitnessPlayerLogic(self.world, self.player) self.locat = WitnessPlayerLocations(self.world, self.player, self.player_logic) self.items = WitnessPlayerItems(self.locat, self.world, self.player, self.player_logic) @@ -78,11 +85,11 @@ class WitnessWorld(World): less_junk = 0 # Put good item on first check if symbol shuffle is on - # symbols = is_option_enabled(self.world, self.player, "shuffle_symbols") - symbols = True + symbols = is_option_enabled(self.world, self.player, "shuffle_symbols") if symbols: random_good_item = self.world.random.choice(self.items.GOOD_ITEMS) + first_check = self.world.get_location( "Tutorial Gate Open", self.player ) @@ -91,6 +98,10 @@ class WitnessWorld(World): less_junk = 1 + for item in self.player_logic.STARTING_INVENTORY: + self.world.push_precollected(items_by_name[item]) + pool.remove(items_by_name[item]) + for item in self.items.EXTRA_AMOUNTS: witness_item = self.create_item(item) for i in range(0, self.items.EXTRA_AMOUNTS[item]): diff --git a/worlds/witness/items.py b/worlds/witness/items.py index 0d7530988e..65a8326984 100644 --- a/worlds/witness/items.py +++ b/worlds/witness/items.py @@ -51,12 +51,15 @@ class StaticWitnessItems: def __init__(self): item_tab = dict() - for item in StaticWitnessLogic.ALL_SYMBOL_ITEMS.union(StaticWitnessLogic.ALL_DOOR_ITEMS): + for item in StaticWitnessLogic.ALL_SYMBOL_ITEMS: if item[0] == "11 Lasers" or item == "7 Lasers": continue item_tab[item[0]] = ItemData(158000 + item[1], True, False) + for item in StaticWitnessLogic.ALL_DOOR_ITEMS: + item_tab[item[0]] = ItemData(158000 + item[1], True, False) + for item in StaticWitnessLogic.ALL_TRAPS: item_tab[item[0]] = ItemData( 158000 + item[1], False, False, True @@ -89,23 +92,39 @@ class WitnessPlayerItems: self.ITEM_TABLE = copy.copy(StaticWitnessItems.ALL_ITEM_TABLE) self.PROGRESSION_TABLE = dict() + self.ITEM_ID_TO_DOOR_HEX = dict() + self.DOORS = set() + + self.SYMBOLS_NOT_IN_THE_GAME = set() + self.EXTRA_AMOUNTS = { "Functioning Brain": 1, "Puzzle Skip": get_option_value(world, player, "puzzle_skip_amount") } for item in StaticWitnessLogic.ALL_SYMBOL_ITEMS.union(StaticWitnessLogic.ALL_DOOR_ITEMS): - if item not in player_logic.PROG_ITEMS_ACTUALLY_IN_THE_GAME: + if item[0] not in player_logic.PROG_ITEMS_ACTUALLY_IN_THE_GAME: del self.ITEM_TABLE[item[0]] + if item in StaticWitnessLogic.ALL_SYMBOL_ITEMS: + self.SYMBOLS_NOT_IN_THE_GAME.add(StaticWitnessItems.ALL_ITEM_TABLE[item[0]].code) else: self.PROGRESSION_TABLE[item[0]] = self.ITEM_TABLE[item[0]] + for entity_hex, items in player_logic.DOOR_ITEMS_BY_ID.items(): + entity_hex_int = int(entity_hex, 16) + + self.DOORS.add(entity_hex_int) + + for item in items: + item_id = StaticWitnessItems.ALL_ITEM_TABLE[item].code + self.ITEM_ID_TO_DOOR_HEX.setdefault(item_id, set()).add(entity_hex_int) + symbols = is_option_enabled(world, player, "shuffle_symbols") if "shuffle_symbols" not in the_witness_options.keys(): symbols = True - doors = is_option_enabled(world, player, "shuffle_doors") + doors = get_option_value(world, player, "shuffle_doors") if doors and symbols: self.GOOD_ITEMS = [ @@ -117,10 +136,10 @@ class WitnessPlayerItems: "Shapers", "Symmetry" ] - if is_option_enabled(world, player, "shuffle_discarded_panels"): - self.GOOD_ITEMS.append("Triangles") - if not is_option_enabled(world, player, "disable_non_randomized_puzzles"): - self.GOOD_ITEMS.append("Colored Squares") + if is_option_enabled(world, player, "shuffle_discarded_panels"): + self.GOOD_ITEMS.append("Triangles") + if not is_option_enabled(world, player, "disable_non_randomized_puzzles"): + self.GOOD_ITEMS.append("Colored Squares") for event_location in locat.EVENT_LOCATION_TABLE: location = player_logic.EVENT_ITEM_PAIRS[event_location] diff --git a/worlds/witness/locations.py b/worlds/witness/locations.py index 380c64c069..f6fcad70ce 100644 --- a/worlds/witness/locations.py +++ b/worlds/witness/locations.py @@ -2,7 +2,7 @@ Defines constants for different types of locations in the game """ -from .Options import is_option_enabled +from .Options import is_option_enabled, get_option_value from .player_logic import StaticWitnessLogic, WitnessPlayerLogic @@ -42,7 +42,7 @@ class StaticWitnessLocations: "Symmetry Island Colored Dots 6", "Symmetry Island Fading Lines 7", "Symmetry Island Scenery Outlines 5", - "Symmetry Island Laser", + "Symmetry Island Laser Panel", "Orchard Apple Tree 5", @@ -52,7 +52,7 @@ class StaticWitnessLocations: "Desert Artificial Light Reflection 3", "Desert Pond Reflection 5", "Desert Flood Reflection 6", - "Desert Laser", + "Desert Laser Panel", "Quarry Mill Eraser and Dots 6", "Quarry Mill Eraser and Squares 8", @@ -63,34 +63,34 @@ class StaticWitnessLocations: "Quarry Boathouse Stars & Eraser & Shapers 2", "Quarry Boathouse Stars & Eraser & Shapers 5", "Quarry Discard", - "Quarry Laser", + "Quarry Laser Panel", "Shadows Lower Avoid 8", "Shadows Environmental Avoid 8", "Shadows Follow 5", - "Shadows Laser", + "Shadows Laser Panel", "Keep Hedge Maze 4", "Keep Pressure Plates 4", "Keep Discard", - "Keep Laser Hedges", - "Keep Laser Pressure Plates", + "Keep Laser Panel Hedges", + "Keep Laser Panel Pressure Plates", "Shipwreck Vault Box", "Shipwreck Discard", "Monastery Rhombic Avoid 3", "Monastery Branch Follow 2", - "Monastery Laser", + "Monastery Laser Panel", "Town Cargo Box Discard", "Town Hexagonal Reflection", - "Town Square Avoid", + "Town Church Lattice", "Town Rooftop Discard", "Town Symmetry Squares 5 + Dots", "Town Full Dot Grid Shapers 5", "Town Shapers & Dots & Eraser", - "Town Laser", + "Town Laser Panel", "Theater Discard", @@ -98,7 +98,7 @@ class StaticWitnessLocations: "Jungle Waves 3", "Jungle Waves 7", "Jungle Popup Wall 6", - "Jungle Laser", + "Jungle Laser Panel", "River Vault Box", @@ -106,7 +106,7 @@ class StaticWitnessLocations: "Bunker Drawn Squares 9", "Bunker Drawn Squares through Tinted Glass 3", "Bunker Drop-Down Door Squares 2", - "Bunker Laser", + "Bunker Laser Panel", "Swamp Seperatable Shapers 6", "Swamp Combinable Shapers 8", @@ -117,7 +117,7 @@ class StaticWitnessLocations: "Swamp Red Underwater Negative Shapers 4", "Swamp More Rotated Shapers 4", "Swamp Blue Underwater Negative Shapers 5", - "Swamp Laser", + "Swamp Laser Panel", "Treehouse Yellow Bridge 9", "Treehouse First Purple Bridge 5", @@ -125,21 +125,12 @@ class StaticWitnessLocations: "Treehouse Green Bridge 7", "Treehouse Green Bridge Discard", "Treehouse Left Orange Bridge 15", - "Treehouse Burned House Discard", + "Treehouse Burnt House Discard", "Treehouse Right Orange Bridge 12", - "Treehouse Laser", + "Treehouse Laser Panel", "Mountaintop Discard", "Mountaintop Vault Box", - - "Inside Mountain Obscured Vision 5", - "Inside Mountain Moving Background 7", - "Inside Mountain Physically Obstructed 3", - "Inside Mountain Angled Inside Trash 2", - "Inside Mountain Color Cycle 5", - "Inside Mountain Same Solution 6", - "Inside Mountain Elevator Discard", - "Inside Mountain Giant Puzzle", } UNCOMMON_LOCATIONS = { @@ -156,35 +147,53 @@ class StaticWitnessLocations: "Swamp Underwater Back Optional", } - HARD_LOCATIONS = { - "Inside Mountain Secret Area Dot Grid Triangles 4", - "Inside Mountain Secret Area Symmetry Triangles", - "Inside Mountain Secret Area Stars & Squares and Triangles 2", - "Inside Mountain Secret Area Shapers and Triangles 2", - "Inside Mountain Secret Area Symmetry Shapers", - "Inside Mountain Secret Area Broken and Negative Shapers", - "Inside Mountain Secret Area Broken Shapers", + CAVES_LOCATIONS = { + "Inside Mountain Caves Dot Grid Triangles 4", + "Inside Mountain Caves Symmetry Triangles", + "Inside Mountain Caves Stars & Squares and Triangles 2", + "Inside Mountain Caves Shapers and Triangles 2", + "Inside Mountain Caves Symmetry Shapers", + "Inside Mountain Caves Broken and Negative Shapers", + "Inside Mountain Caves Broken Shapers", - "Inside Mountain Secret Area Rainbow Squares", - "Inside Mountain Secret Area Squares & Stars and Colored Eraser", - "Inside Mountain Secret Area Rotated Broken Shapers", - "Inside Mountain Secret Area Stars and Squares", - "Inside Mountain Secret Area Lone Pillar", - "Inside Mountain Secret Area Wooden Beam Shapers", - "Inside Mountain Secret Area Wooden Beam Squares and Shapers", - "Inside Mountain Secret Area Wooden Beam Stars and Squares", - "Inside Mountain Secret Area Wooden Beam Shapers and Stars", - "Inside Mountain Secret Area Upstairs Invisible Dots 8", - "Inside Mountain Secret Area Upstairs Invisible Dot Symmetry 3", - "Inside Mountain Secret Area Upstairs Dot Grid Negative Shapers", - "Inside Mountain Secret Area Upstairs Dot Grid Rotated Shapers", + "Inside Mountain Caves Rainbow Squares", + "Inside Mountain Caves Squares & Stars and Colored Eraser", + "Inside Mountain Caves Rotated Broken Shapers", + "Inside Mountain Caves Stars and Squares", + "Inside Mountain Caves Lone Pillar", + "Inside Mountain Caves Wooden Beam Shapers", + "Inside Mountain Caves Wooden Beam Squares and Shapers", + "Inside Mountain Caves Wooden Beam Stars and Squares", + "Inside Mountain Caves Wooden Beam Shapers and Stars", + "Inside Mountain Caves Upstairs Invisible Dots 8", + "Inside Mountain Caves Upstairs Invisible Dot Symmetry 3", + "Inside Mountain Caves Upstairs Dot Grid Negative Shapers", + "Inside Mountain Caves Upstairs Dot Grid Rotated Shapers", - "Challenge Vault Box", "Theater Walkway Vault Box", "Inside Mountain Bottom Layer Discard", "Theater Challenge Video", } + MOUNTAIN_UNREACHABLE_FROM_BEHIND = { + "Mountaintop Trap Door Triple Exit", + + "Inside Mountain Obscured Vision 5", + "Inside Mountain Moving Background 7", + "Inside Mountain Physically Obstructed 3", + "Inside Mountain Angled Inside Trash 2", + "Inside Mountain Color Cycle 5", + "Inside Mountain Same Solution 6", + } + + MOUNTAIN_REACHABLE_FROM_BEHIND = { + "Inside Mountain Elevator Discard", + "Inside Mountain Giant Puzzle", + + "Inside Mountain Final Room Left Pillar 4", + "Inside Mountain Final Room Right Pillar 4", + } + ALL_LOCATIONS_TO_ID = dict() @staticmethod @@ -193,12 +202,7 @@ class StaticWitnessLocations: Calculates the location ID for any given location """ - panel_offset = StaticWitnessLogic.CHECKS_BY_HEX[chex]["idOffset"] - type_offset = StaticWitnessLocations.TYPE_OFFSETS[ - StaticWitnessLogic.CHECKS_BY_HEX[chex]["panelType"] - ] - - return StaticWitnessLocations.ID_START + panel_offset + type_offset + return StaticWitnessLogic.CHECKS_BY_HEX[chex]["id"] @staticmethod def get_event_name(panel_hex): @@ -213,6 +217,7 @@ class StaticWitnessLocations: all_loc_to_id = { panel_obj["checkName"]: self.get_id(chex) for chex, panel_obj in StaticWitnessLogic.CHECKS_BY_HEX.items() + if panel_obj["id"] } all_loc_to_id = dict( @@ -229,12 +234,34 @@ class WitnessPlayerLocations: """ def __init__(self, world, player, player_logic: WitnessPlayerLogic): + """Defines locations AFTER logic changes due to options""" + self.PANEL_TYPES_TO_SHUFFLE = {"General", "Laser"} self.CHECK_LOCATIONS = ( StaticWitnessLocations.GENERAL_LOCATIONS ) - """Defines locations AFTER logic changes due to options""" + doors = get_option_value(world, player, "shuffle_doors") + earlyutm = is_option_enabled(world, player, "early_secret_area") + victory = get_option_value(world, player, "victory_condition") + lasers = get_option_value(world, player, "challenge_lasers") + laser_shuffle = get_option_value(world, player, "shuffle_lasers") + + postgame = set() + postgame = postgame | StaticWitnessLocations.CAVES_LOCATIONS + postgame = postgame | StaticWitnessLocations.MOUNTAIN_REACHABLE_FROM_BEHIND + postgame = postgame | StaticWitnessLocations.MOUNTAIN_UNREACHABLE_FROM_BEHIND + + self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | postgame + + if earlyutm or doors >= 2 or (victory == 1 and (lasers <= 11 or laser_shuffle)): + postgame -= StaticWitnessLocations.CAVES_LOCATIONS + + if doors >= 2: + postgame -= StaticWitnessLocations.MOUNTAIN_REACHABLE_FROM_BEHIND + + if victory != 2: + postgame -= StaticWitnessLocations.MOUNTAIN_UNREACHABLE_FROM_BEHIND if is_option_enabled(world, player, "shuffle_discarded_panels"): self.PANEL_TYPES_TO_SHUFFLE.add("Discard") @@ -245,18 +272,11 @@ class WitnessPlayerLocations: if is_option_enabled(world, player, "shuffle_uncommon"): self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | StaticWitnessLocations.UNCOMMON_LOCATIONS - if is_option_enabled(world, player, "shuffle_hard"): - self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | StaticWitnessLocations.HARD_LOCATIONS - - if is_option_enabled(world, player, "shuffle_symbols") and is_option_enabled(world, player, "shuffle_doors"): - if is_option_enabled(world, player, "disable_non_randomized_puzzles"): - # This particular combination of logic settings leads to logic so restrictive that generation can fail - # Hence, we add some extra sphere 0 locations - - self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | StaticWitnessLocations.EXTRA_LOCATIONS - self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | player_logic.ADDED_CHECKS + if not is_option_enabled(world, player, "shuffle_postgame"): + self.CHECK_LOCATIONS -= postgame + self.CHECK_LOCATIONS = self.CHECK_LOCATIONS - { StaticWitnessLogic.CHECKS_BY_HEX[check_hex]["checkName"] for check_hex in player_logic.COMPLETELY_DISABLED_CHECKS @@ -272,7 +292,7 @@ class WitnessPlayerLocations: ) event_locations = { - p for p in player_logic.NECESSARY_EVENT_PANELS + p for p in player_logic.EVENT_PANELS } self.EVENT_LOCATION_TABLE = { diff --git a/worlds/witness/player_logic.py b/worlds/witness/player_logic.py index 689403dc22..eb57f2c6a0 100644 --- a/worlds/witness/player_logic.py +++ b/worlds/witness/player_logic.py @@ -18,22 +18,15 @@ When the world has parsed its options, a second function is called to finalize t import copy from BaseClasses import MultiWorld from .static_logic import StaticWitnessLogic -from .utils import define_new_region, get_disable_unrandomized_list, parse_lambda, get_early_utm_list +from .utils import define_new_region, get_disable_unrandomized_list, parse_lambda, get_early_utm_list, \ + get_symbol_shuffle_list, get_door_panel_shuffle_list, get_doors_complex_list, get_doors_max_list, \ + get_doors_simple_list, get_laser_shuffle from .Options import is_option_enabled, get_option_value, the_witness_options class WitnessPlayerLogic: """WITNESS LOGIC CLASS""" - def update_door_dict(self, panel_hex): - item_id = StaticWitnessLogic.ALL_DOOR_ITEM_IDS_BY_HEX.get(panel_hex) - - if item_id is None: - return - - self.DOOR_DICT_FOR_CLIENT[panel_hex] = item_id - self.DOOR_CONNECTIONS_TO_SEVER.update(StaticWitnessLogic.CONNECTIONS_TO_SEVER_BY_DOOR_HEX[panel_hex]) - def reduce_req_within_region(self, panel_hex): """ Panels in this game often only turn on when other panels are solved. @@ -43,35 +36,42 @@ class WitnessPlayerLogic: Panels outside of the same region will still be checked manually. """ - these_items = self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["items"] + check_obj = StaticWitnessLogic.CHECKS_BY_HEX[panel_hex] - real_items = {item[0] for item in self.PROG_ITEMS_ACTUALLY_IN_THE_GAME} + these_items = frozenset({frozenset()}) + + if check_obj["id"]: + these_items = self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["items"] these_items = frozenset({ - subset.intersection(real_items) + subset.intersection(self.PROG_ITEMS_ACTUALLY_IN_THE_GAME) for subset in these_items }) + if panel_hex in self.DOOR_ITEMS_BY_ID: + door_items = frozenset({frozenset([item]) for item in self.DOOR_ITEMS_BY_ID[panel_hex]}) + + all_options = set() + + for items_option in these_items: + for dependentItem in door_items: + all_options.add(items_option.union(dependentItem)) + + return frozenset(all_options) + these_panels = self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["panels"] - if StaticWitnessLogic.DOOR_NAMES_BY_HEX.get(panel_hex) in real_items: - self.update_door_dict(panel_hex) - - these_panels = frozenset({frozenset()}) - if these_panels == frozenset({frozenset()}): return these_items all_options = set() - check_obj = StaticWitnessLogic.CHECKS_BY_HEX[panel_hex] - for option in these_panels: dependent_items_for_option = frozenset({frozenset()}) for option_panel in option: - new_items = set() dep_obj = StaticWitnessLogic.CHECKS_BY_HEX.get(option_panel) + if option_panel in {"7 Lasers", "11 Lasers"}: new_items = frozenset({frozenset([option_panel])}) # If a panel turns on when a panel in a different region turns on, @@ -101,8 +101,34 @@ class WitnessPlayerLogic: return frozenset(all_options) def make_single_adjustment(self, adj_type, line): + from . import StaticWitnessItems """Makes a single logic adjustment based on additional logic file""" + if adj_type == "Items": + if line not in StaticWitnessItems.ALL_ITEM_TABLE: + raise RuntimeError("Item \"" + line + "\" does not exit.") + + self.PROG_ITEMS_ACTUALLY_IN_THE_GAME.add(line) + + if line in StaticWitnessLogic.ALL_DOOR_ITEMS_AS_DICT: + panel_hexes = StaticWitnessLogic.ALL_DOOR_ITEMS_AS_DICT[line][2] + for panel_hex in panel_hexes: + self.DOOR_ITEMS_BY_ID.setdefault(panel_hex, set()).add(line) + + return + + if adj_type == "Remove Items": + self.PROG_ITEMS_ACTUALLY_IN_THE_GAME.discard(line) + + if line in StaticWitnessLogic.ALL_DOOR_ITEMS_AS_DICT: + panel_hexes = StaticWitnessLogic.ALL_DOOR_ITEMS_AS_DICT[line][2] + for panel_hex in panel_hexes: + if panel_hex in self.DOOR_ITEMS_BY_ID: + self.DOOR_ITEMS_BY_ID[panel_hex].discard(line) + + if adj_type == "Starting Inventory": + self.STARTING_INVENTORY.add(line) + if adj_type == "Event Items": line_split = line.split(" - ") hex_set = line_split[1].split(",") @@ -130,18 +156,20 @@ class WitnessPlayerLogic: if adj_type == "Requirement Changes": line_split = line.split(" - ") - required_items = parse_lambda(line_split[2]) - items_actually_in_the_game = {item[0] for item in StaticWitnessLogic.ALL_SYMBOL_ITEMS} - required_items = frozenset( - subset.intersection(items_actually_in_the_game) - for subset in required_items - ) - requirement = { "panels": parse_lambda(line_split[1]), - "items": required_items } + if len(line_split) > 2: + required_items = parse_lambda(line_split[2]) + items_actually_in_the_game = {item[0] for item in StaticWitnessLogic.ALL_SYMBOL_ITEMS} + required_items = frozenset( + subset.intersection(items_actually_in_the_game) + for subset in required_items + ) + + requirement["items"] = required_items + self.DEPENDENT_REQUIREMENTS_BY_HEX[line_split[0]] = requirement return @@ -151,11 +179,6 @@ class WitnessPlayerLogic: self.COMPLETELY_DISABLED_CHECKS.add(panel_hex) - self.PROG_ITEMS_ACTUALLY_IN_THE_GAME = { - item for item in self.PROG_ITEMS_ACTUALLY_IN_THE_GAME - if item[0] != StaticWitnessLogic.DOOR_NAMES_BY_HEX.get(panel_hex) - } - return if adj_type == "Region Changes": @@ -189,18 +212,25 @@ class WitnessPlayerLogic: adjustment_linesets_in_order.append(get_disable_unrandomized_list()) if is_option_enabled(world, player, "shuffle_symbols") or "shuffle_symbols" not in the_witness_options.keys(): - self.PROG_ITEMS_ACTUALLY_IN_THE_GAME.update(StaticWitnessLogic.ALL_SYMBOL_ITEMS) + adjustment_linesets_in_order.append(get_symbol_shuffle_list()) - if is_option_enabled(world, player, "shuffle_doors"): - self.PROG_ITEMS_ACTUALLY_IN_THE_GAME.update(StaticWitnessLogic.ALL_DOOR_ITEMS) + if get_option_value(world, player, "shuffle_doors") == 1: + adjustment_linesets_in_order.append(get_door_panel_shuffle_list()) + + if get_option_value(world, player, "shuffle_doors") == 2: + adjustment_linesets_in_order.append(get_doors_simple_list()) + + if get_option_value(world, player, "shuffle_doors") == 3: + adjustment_linesets_in_order.append(get_doors_complex_list()) + + if get_option_value(world, player, "shuffle_doors") == 4: + adjustment_linesets_in_order.append(get_doors_max_list()) if is_option_enabled(world, player, "early_secret_area"): adjustment_linesets_in_order.append(get_early_utm_list()) - else: - self.PROG_ITEMS_ACTUALLY_IN_THE_GAME = { - item for item in self.PROG_ITEMS_ACTUALLY_IN_THE_GAME - if item[0] != "Mountaintop River Shape Power On" - } + + if is_option_enabled(world, player, "shuffle_lasers"): + adjustment_linesets_in_order.append(get_laser_shuffle()) for adjustment_lineset in adjustment_linesets_in_order: current_adjustment_type = None @@ -233,62 +263,32 @@ class WitnessPlayerLogic: pair = (name, self.EVENT_ITEM_NAMES[panel]) return pair - def _regions_are_adjacent(self, region1, region2): - for connection in self.CONNECTIONS_BY_REGION_NAME[region1]: - if connection[0] == region2: - return True - - for connection in self.CONNECTIONS_BY_REGION_NAME[region2]: - if connection[0] == region1: - return True - - return False - def make_event_panel_lists(self): """ Special event panel data structures """ - for region_conn in self.CONNECTIONS_BY_REGION_NAME.values(): - for region_and_option in region_conn: - for panelset in region_and_option[1]: - for panel in panelset: - self.EVENT_PANELS_FROM_REGIONS.add(panel) - self.ALWAYS_EVENT_NAMES_BY_HEX[self.VICTORY_LOCATION] = "Victory" - self.ORIGINAL_EVENT_PANELS.update(self.EVENT_PANELS_FROM_PANELS) - self.ORIGINAL_EVENT_PANELS.update(self.EVENT_PANELS_FROM_REGIONS) + for region_name, connections in self.CONNECTIONS_BY_REGION_NAME.items(): + for connection in connections: + for panel_req in connection[1]: + for panel in panel_req: + if panel == "TrueOneWay": + continue - for panel in self.EVENT_PANELS_FROM_REGIONS: - for region_name, region in StaticWitnessLogic.ALL_REGIONS_BY_NAME.items(): - for connection in self.CONNECTIONS_BY_REGION_NAME[region_name]: - connected_r = connection[0] - if connected_r not in StaticWitnessLogic.ALL_REGIONS_BY_NAME: - continue - if region_name == "Boat" or connected_r == "Boat": - continue - connected_r = StaticWitnessLogic.ALL_REGIONS_BY_NAME[connected_r] - if not any([panel in option for option in connection[1]]): - continue - if panel not in region["panels"] | connected_r["panels"]: - self.NECESSARY_EVENT_PANELS.add(panel) + if StaticWitnessLogic.CHECKS_BY_HEX[panel]["region"]["name"] != region_name: + self.EVENT_PANELS_FROM_REGIONS.add(panel) - for event_panel in self.EVENT_PANELS_FROM_PANELS: - for panel, panel_req in self.REQUIREMENTS_BY_HEX.items(): - if any([event_panel in item_set for item_set in panel_req]): - region1 = StaticWitnessLogic.CHECKS_BY_HEX[panel]["region"]["name"] - region2 = StaticWitnessLogic.CHECKS_BY_HEX[event_panel]["region"]["name"] - - if not self._regions_are_adjacent(region1, region2): - self.NECESSARY_EVENT_PANELS.add(event_panel) + self.EVENT_PANELS.update(self.EVENT_PANELS_FROM_PANELS) + self.EVENT_PANELS.update(self.EVENT_PANELS_FROM_REGIONS) for always_hex, always_item in self.ALWAYS_EVENT_NAMES_BY_HEX.items(): self.ALWAYS_EVENT_HEX_CODES.add(always_hex) - self.NECESSARY_EVENT_PANELS.add(always_hex) + self.EVENT_PANELS.add(always_hex) self.EVENT_ITEM_NAMES[always_hex] = always_item - for panel in self.NECESSARY_EVENT_PANELS: + for panel in self.EVENT_PANELS: pair = self.make_event_item_pair(panel) self.EVENT_ITEM_PAIRS[pair[0]] = pair[1] @@ -297,8 +297,8 @@ class WitnessPlayerLogic: self.EVENT_PANELS_FROM_REGIONS = set() self.PROG_ITEMS_ACTUALLY_IN_THE_GAME = set() - self.DOOR_DICT_FOR_CLIENT = dict() - self.DOOR_CONNECTIONS_TO_SEVER = set() + self.DOOR_ITEMS_BY_ID = dict() + self.STARTING_INVENTORY = set() self.CONNECTIONS_BY_REGION_NAME = copy.copy(StaticWitnessLogic.STATIC_CONNECTIONS_BY_REGION_NAME) self.DEPENDENT_REQUIREMENTS_BY_HEX = copy.copy(StaticWitnessLogic.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX) @@ -306,8 +306,7 @@ class WitnessPlayerLogic: # Determining which panels need to be events is a difficult process. # At the end, we will have EVENT_ITEM_PAIRS for all the necessary ones. - self.ORIGINAL_EVENT_PANELS = set() - self.NECESSARY_EVENT_PANELS = set() + self.EVENT_PANELS = set() self.EVENT_ITEM_PAIRS = dict() self.ALWAYS_EVENT_HEX_CODES = set() self.COMPLETELY_DISABLED_CHECKS = set() @@ -320,42 +319,63 @@ class WitnessPlayerLogic: "0x00037": "Monastery Branch Panels Activate", "0x0A079": "Access to Bunker Laser", "0x0A3B5": "Door to Tutorial Discard Opens", + "0x00139": "Keep Hedges 2 Turns On", + "0x019DC": "Keep Hedges 3 Turns On", + "0x019E7": "Keep Hedges 4 Turns On", "0x01D3F": "Keep Laser Panel (Pressure Plates) Activates", "0x09F7F": "Mountain Access", "0x0367C": "Quarry Laser Mill Requirement Met", - "0x009A1": "Swamp Rotating Bridge Near Side", + "0x009A1": "Swamp Rotated Shapers 1 Activates", "0x00006": "Swamp Cyan Water Drains", "0x00990": "Swamp Broken Shapers 1 Activates", "0x0A8DC": "Lower Avoid 6 Activates", "0x0000A": "Swamp More Rotated Shapers 1 Access", - "0x09ED8": "Inside Mountain Second Layer Both Light Bridges Solved", + "0x09E86": "Inside Mountain Second Layer Blue Bridge Access", + "0x09ED8": "Inside Mountain Second Layer Yellow Bridge Access", "0x0A3D0": "Quarry Laser Boathouse Requirement Met", "0x00596": "Swamp Red Water Drains", - "0x28B39": "Town Tower 4th Door Opens", + "0x00E3A": "Swamp Purple Water Drains", "0x0343A": "Door to Symmetry Island Powers On", - "0xFFF00": "Inside Mountain Bottom Layer Discard Turns On" + "0xFFF00": "Inside Mountain Bottom Layer Discard Turns On", + "0x17CA6": "All Boat Panels Turn On", + "0x17CDF": "All Boat Panels Turn On", + "0x09DB8": "All Boat Panels Turn On", + "0x17C95": "All Boat Panels Turn On", + "0x03BB0": "Town Church Lattice Vision From Outside", + "0x28AC1": "Town Shapers & Dots & Eraser Turns On", + "0x28A69": "Town Tower 1st Door Opens", + "0x28ACC": "Town Tower 2nd Door Opens", + "0x28AD9": "Town Tower 3rd Door Opens", + "0x28B39": "Town Tower 4th Door Opens", + "0x03675": "Quarry Mill Ramp Activation From Above", + "0x03679": "Quarry Mill Lift Lowering While Standing On It", + "0x2FAF6": "Tutorial Gate Secret Solution Knowledge", + "0x079DF": "Town Hexagonal Reflection Turns On", + "0x17DA2": "Right Orange Bridge Fully Extended", + "0x19B24": "Shadows Lower Avoid Patterns Visible", + "0x2700B": "Open Door to Treehouse Laser House", + "0x00055": "Orchard Apple Trees 4 Turns On", } self.ALWAYS_EVENT_NAMES_BY_HEX = { - "0x0360D": "Symmetry Laser Activation", - "0x03608": "Desert Laser Activation", + "0x00509": "Symmetry Laser Activation", + "0x012FB": "Desert Laser Activation", "0x09F98": "Desert Laser Redirection", - "0x03612": "Quarry Laser Activation", - "0x19650": "Shadows Laser Activation", - "0x0360E": "Keep Laser Activation", - "0x03317": "Keep Laser Activation", - "0x17CA4": "Monastery Laser Activation", - "0x032F5": "Town Laser Activation", - "0x03616": "Jungle Laser Activation", - "0x09DE0": "Bunker Laser Activation", - "0x03615": "Swamp Laser Activation", - "0x03613": "Treehouse Laser Activation", + "0x01539": "Quarry Laser Activation", + "0x181B3": "Shadows Laser Activation", + "0x014BB": "Keep Laser Activation", + "0x17C65": "Monastery Laser Activation", + "0x032F9": "Town Laser Activation", + "0x00274": "Jungle Laser Activation", + "0x0C2B2": "Bunker Laser Activation", + "0x00BF6": "Swamp Laser Activation", + "0x028A4": "Treehouse Laser Activation", "0x03535": "Shipwreck Video Pattern Knowledge", "0x03542": "Mountain Video Pattern Knowledge", "0x0339E": "Desert Video Pattern Knowledge", "0x03481": "Tutorial Video Pattern Knowledge", "0x03702": "Jungle Video Pattern Knowledge", - "0x2FAF6": "Theater Walkway Video Pattern Knowledge", + "0x0356B": "Challenge Video Pattern Knowledge", "0x09F7F": "Mountaintop Trap Door Turns On", "0x17C34": "Mountain Access", } diff --git a/worlds/witness/regions.py b/worlds/witness/regions.py index a7d549e704..b5ee31b8ca 100644 --- a/worlds/witness/regions.py +++ b/worlds/witness/regions.py @@ -33,6 +33,10 @@ class WitnessRegions: source_region = world.get_region(source, player) target_region = world.get_region(target, player) + #print(source_region) + #print(target_region) + #print("---") + connection = Entrance( player, source + " to " + target + " via " + str(panel_hex_to_solve_set), @@ -76,10 +80,17 @@ class WitnessRegions: for connection in player_logic.CONNECTIONS_BY_REGION_NAME[region_name]: if connection[0] == "Entry": continue - self.connect(world, player, region_name, - connection[0], player_logic, connection[1]) - self.connect(world, player, connection[0], - region_name, player_logic, connection[1]) + + if connection[1] == frozenset({frozenset(["TrueOneWay"])}): + self.connect(world, player, region_name, connection[0], player_logic, frozenset({frozenset()})) + continue + + for subset in connection[1]: + if all({panel in player_logic.DOOR_ITEMS_BY_ID for panel in subset}): + if all({StaticWitnessLogic.CHECKS_BY_HEX[panel]["id"] is None for panel in subset}): + self.connect(world, player, connection[0], region_name, player_logic, frozenset({subset})) + + self.connect(world, player, region_name, connection[0], player_logic, connection[1]) world.get_entrance("The Splashscreen?", player).connect( world.get_region('First Hallway', player) diff --git a/worlds/witness/rules.py b/worlds/witness/rules.py index 1f13074a88..cd1fae1235 100644 --- a/worlds/witness/rules.py +++ b/worlds/witness/rules.py @@ -22,6 +22,21 @@ class WitnessLogic(LogicMixin): def _witness_has_lasers(self, world, player: int, amount: int) -> bool: lasers = 0 + if is_option_enabled(world, player, "shuffle_lasers"): + lasers += int(self.has("Symmetry Laser", player)) + lasers += int(self.has("Desert Laser", player) + and self.has("Desert Laser Redirection", player)) + lasers += int(self.has("Town Laser", player)) + lasers += int(self.has("Monastery Laser", player)) + lasers += int(self.has("Keep Laser", player)) + lasers += int(self.has("Quarry Laser", player)) + lasers += int(self.has("Treehouse Laser", player)) + lasers += int(self.has("Jungle Laser", player)) + lasers += int(self.has("Bunker Laser", player)) + lasers += int(self.has("Swamp Laser", player)) + lasers += int(self.has("Shadows Laser", player)) + return lasers >= amount + lasers += int(self.has("Symmetry Laser Activation", player)) lasers += int(self.has("Desert Laser Activation", player) and self.has("Desert Laser Redirection", player)) @@ -48,11 +63,8 @@ class WitnessLogic(LogicMixin): if (check_name + " Solved" in locat.EVENT_LOCATION_TABLE and not self.has(player_logic.EVENT_ITEM_PAIRS[check_name + " Solved"], player)): return False - if panel not in player_logic.ORIGINAL_EVENT_PANELS and not self.can_reach(check_name, "Location", player): - return False - if (panel in player_logic.ORIGINAL_EVENT_PANELS - and check_name + " Solved" not in locat.EVENT_LOCATION_TABLE - and not self._witness_safe_manual_panel_check(panel, world, player, player_logic, locat)): + if (check_name + " Solved" not in locat.EVENT_LOCATION_TABLE + and not self._witness_meets_item_requirements(panel, world, player, player_logic, locat)): return False return True @@ -79,8 +91,10 @@ class WitnessLogic(LogicMixin): if not self._witness_has_lasers(world, player, get_option_value(world, player, "challenge_lasers")): valid_option = False break - elif item in player_logic.ORIGINAL_EVENT_PANELS: - valid_option = self._witness_can_solve_panel(item, world, player, player_logic, locat) + elif item in player_logic.EVENT_PANELS: + if not self._witness_can_solve_panel(item, world, player, player_logic, locat): + valid_option = False + break elif not self.has(item, player): valid_option = False break @@ -90,24 +104,6 @@ class WitnessLogic(LogicMixin): return False - def _witness_safe_manual_panel_check(self, panel, world, player, player_logic: WitnessPlayerLogic, locat): - """ - nested can_reach can cause problems, but only if the region being - checked is neither of the two original regions from the first - can_reach. - A nested can_reach is okay here because the only panels this - function is called on are panels that exist on either side of all - connections they are required for. - The spoiler log looks so much nicer this way, - it gets rid of a bunch of event items, only leaving a couple. :) - """ - region = StaticWitnessLogic.CHECKS_BY_HEX[panel]["region"]["name"] - - return ( - self._witness_meets_item_requirements(panel, world, player, player_logic, locat) - and self.can_reach(region, "Region", player) - ) - def _witness_can_solve_panels(self, panel_hex_to_solve_set, world, player, player_logic: WitnessPlayerLogic, locat): """ Checks whether a set of panels can be solved. @@ -120,7 +116,12 @@ class WitnessLogic(LogicMixin): valid_option = True for panel in option: - if not self._witness_can_solve_panel(panel, world, player, player_logic, locat): + if panel in player_logic.DOOR_ITEMS_BY_ID: + if all({not self.has(item, player) for item in player_logic.DOOR_ITEMS_BY_ID[panel]}): + valid_option = False + break + + elif not self._witness_can_solve_panel(panel, world, player, player_logic, locat): valid_option = False break diff --git a/worlds/witness/Disable_Unrandomized.txt b/worlds/witness/settings/Disable_Unrandomized.txt similarity index 94% rename from worlds/witness/Disable_Unrandomized.txt rename to worlds/witness/settings/Disable_Unrandomized.txt index cad3804f34..6f957ada5c 100644 --- a/worlds/witness/Disable_Unrandomized.txt +++ b/worlds/witness/settings/Disable_Unrandomized.txt @@ -1,18 +1,16 @@ Event Items: -Shadows Laser Activation - 0x00021,0x17D28,0x17C71 -Keep Laser Activation - 0x03317 -Bunker Laser Activation - 0x00061,0x17D01,0x17C42 -Monastery Laser Activation - 0x00A5B,0x17CE7,0x17FA9,0x17CA4 Town Tower 4th Door Opens - 0x17CFB,0x3C12B,0x00B8D,0x17CF7 +Monastery Laser Activation - 0x00A5B,0x17CE7,0x17FA9,0x17CA4 +Bunker Laser Activation - 0x00061,0x17D01,0x17C42 +Shadows Laser Activation - 0x00021,0x17D28,0x17C71 Requirement Changes: -0x17CA4 - True - True -0x28B39 - 0x2896A - Reflection +0x17C65 - 0x00A5B | 0x17CE7 | 0x17FA9 | 0x17CA4 +0x0C2B2 - 0x00061 | 0x17D01 | 0x17C42 +0x181B3 - 0x00021 | 0x17D28 | 0x17C71 +0x28B39 - True - Reflection 0x17CAB - True - True -Region Changes: -Quarry (Quarry) - Outside Quarry - 0x17C09 - Quarry Mill - 0x275ED - Quarry Mill - 0x17CAC - Disabled Locations: 0x03505 (Tutorial Gate Close) 0x0C335 (Tutorial Pillar) diff --git a/worlds/witness/settings/Door_Panel_Shuffle.txt b/worlds/witness/settings/Door_Panel_Shuffle.txt new file mode 100644 index 0000000000..d6982f52e3 --- /dev/null +++ b/worlds/witness/settings/Door_Panel_Shuffle.txt @@ -0,0 +1,31 @@ +Items: +Glass Factory Entry Door (Panel) +Door to Symmetry Island Lower (Panel) +Door to Symmetry Island Upper (Panel) +Door to Desert Flood Light Room (Panel) +Desert Flood Room Flood Controls (Panel) +Quarry Door to Mill (Panel) +Quarry Mill Ramp Controls (Panel) +Quarry Mill Elevator Controls (Panel) +Quarry Boathouse Ramp Height Control (Panel) +Quarry Boathouse Ramp Horizontal Control (Panel) +Shadows Door Timer (Panel) +Monastery Entry Door Left (Panel) +Monastery Entry Door Right (Panel) +Town Door to RGB House (Panel) +Town Door to Church (Panel) +Town Maze Panel (Drop-Down Staircase) (Panel) +Windmill Door (Panel) +Treehouse First & Second Doors (Panel) +Treehouse Third Door (Panel) +Treehouse Laser House Door Timer (Panel) +Treehouse Shortcut Drop-Down Bridge (Panel) +Jungle Popup Wall (Panel) +Bunker Entry Door (Panel) +Inside Bunker Door to Bunker Proper (Panel) +Bunker Elevator Control (Panel) +Swamp Entry Door (Panel) +Swamp Sliding Bridge (Panel) +Swamp Rotating Bridge (Panel) +Swamp Maze Control (Panel) +Boat diff --git a/worlds/witness/settings/Doors_Complex.txt b/worlds/witness/settings/Doors_Complex.txt new file mode 100644 index 0000000000..c62562e32a --- /dev/null +++ b/worlds/witness/settings/Doors_Complex.txt @@ -0,0 +1,201 @@ +Items: +Outside Tutorial Optional Door +Outside Tutorial Outpost Entry Door +Outside Tutorial Outpost Exit Door +Glass Factory Entry Door +Glass Factory Back Wall +Symmetry Island Lower Door +Symmetry Island Upper Door +Orchard Middle Gate +Orchard Final Gate +Desert Door to Flood Light Room +Desert Door to Pond Room +Desert Door to Water Levels Room +Desert Door to Elevator Room +Quarry Main Entry 1 +Quarry Main Entry 2 +Quarry Door to Mill +Quarry Mill Side Door +Quarry Mill Rooftop Shortcut +Quarry Mill Stairs +Quarry Boathouse Boat Staircase +Quarry Boathouse First Barrier +Quarry Boathouse Shortcut +Shadows Timed Door +Shadows Laser Room Right Door +Shadows Laser Room Left Door +Shadows Barrier to Quarry +Shadows Barrier to Ledge +Keep Hedge Maze 1 Exit Door +Keep Pressure Plates 1 Exit Door +Keep Hedge Maze 2 Shortcut +Keep Hedge Maze 2 Exit Door +Keep Hedge Maze 3 Shortcut +Keep Hedge Maze 3 Exit Door +Keep Hedge Maze 4 Shortcut +Keep Hedge Maze 4 Exit Door +Keep Pressure Plates 2 Exit Door +Keep Pressure Plates 3 Exit Door +Keep Pressure Plates 4 Exit Door +Keep Shortcut to Shadows +Keep Tower Shortcut +Monastery Shortcut +Monastery Inner Door +Monastery Outer Door +Monastery Door to Garden +Town Cargo Box Door +Town Wooden Roof Staircase +Town Tinted Door to RGB House +Town Door to Church +Town Maze Staircase +Town Windmill Door +Town RGB House Staircase +Town Tower Blue Panels Door +Town Tower Lattice Door +Town Tower Environmental Set Door +Town Tower Wooden Roof Set Door +Theater Entry Door +Theater Exit Door Left +Theater Exit Door Right +Jungle Bamboo Shortcut to River +Jungle Popup Wall +River Shortcut to Monastery Garden +Bunker Bunker Entry Door +Bunker Tinted Glass Door +Bunker Door to Ultraviolet Room +Bunker Door to Elevator +Swamp Entry Door +Swamp Door to Broken Shapers +Swamp Platform Shortcut Door +Swamp Cyan Water Pump +Swamp Door to Rotated Shapers +Swamp Red Water Pump +Swamp Red Underwater Exit +Swamp Blue Water Pump +Swamp Purple Water Pump +Swamp Near Laser Shortcut +Treehouse First Door +Treehouse Second Door +Treehouse Beyond Yellow Bridge Door +Treehouse Drawbridge +Treehouse Timed Door to Laser House +Inside Mountain First Layer Exit Door +Inside Mountain Second Layer Staircase Near +Inside Mountain Second Layer Exit Door +Inside Mountain Second Layer Staircase Far +Inside Mountain Giant Puzzle Exit Door +Inside Mountain Door to Final Room +Inside Mountain Bottom Layer Rock +Inside Mountain Door to Secret Area +Caves Pillar Door +Caves Mountain Shortcut +Caves Swamp Shortcut +Challenge Entry Door +Challenge Door to Theater Walkway +Theater Walkway Door to Windmill Interior +Theater Walkway Door to Desert Elevator Room +Theater Walkway Door to Town + +Added Locations: +Outside Tutorial Door to Outpost Panel +Outside Tutorial Exit Door from Outpost Panel +Glass Factory Entry Door Panel +Glass Factory Vertical Symmetry 5 +Symmetry Island Door to Symmetry Island Lower Panel +Symmetry Island Door to Symmetry Island Upper Panel +Orchard Apple Tree 3 +Orchard Apple Tree 5 +Desert Door to Desert Flood Light Room Panel +Desert Artificial Light Reflection 3 +Desert Door to Water Levels Room Panel +Desert Flood Reflection 6 +Quarry Door to Quarry 1 Panel +Quarry Door to Quarry 2 Panel +Quarry Door to Mill Right +Quarry Door to Mill Left +Quarry Mill Ground Floor Shortcut Door Panel +Quarry Mill Door to Outside Quarry Stairs Panel +Quarry Mill Stair Control +Quarry Boathouse Shortcut Door Panel +Shadows Door Timer Inside +Shadows Door Timer Outside +Shadows Environmental Avoid 8 +Shadows Follow 5 +Shadows Lower Avoid 3 +Shadows Lower Avoid 5 +Keep Hedge Maze 1 +Keep Pressure Plates 1 +Keep Hedge Maze 2 +Keep Hedge Maze 3 +Keep Hedge Maze 4 +Keep Pressure Plates 2 +Keep Pressure Plates 3 +Keep Pressure Plates 4 +Keep Shortcut to Shadows Panel +Keep Tower Shortcut to Keep Panel +Monastery Shortcut Door Panel +Monastery Door Open Left +Monastery Door Open Right +Monastery Rhombic Avoid 3 +Town Cargo Box Panel +Town Full Dot Grid Shapers 5 +Town Tinted Door Panel +Town Door to Church Stars Panel +Town Maze Stair Control +Town Windmill Door Panel +Town Sound Room Left +Town Sound Room Right +Town Symmetry Squares 5 + Dots +Town Church Lattice +Town Hexagonal Reflection +Town Shapers & Dots & Eraser +Windmill Door to Front of Theater Panel +Theater Door to Cargo Box Left Panel +Theater Door to Cargo Box Right Panel +Jungle Shortcut to River Panel +Jungle Popup Wall Control +River Rhombic Avoid to Monastery Garden +Bunker Bunker Entry Panel +Bunker Door to Bunker Proper Panel +Bunker Drawn Squares through Tinted Glass 3 +Bunker Drop-Down Door Squares 2 +Swamp Entry Panel +Swamp Platform Shapers 4 +Swamp Platform Shortcut Right Panel +Swamp Blue Underwater Negative Shapers 5 +Swamp Broken Shapers 4 +Swamp Cyan Underwater Negative Shapers 5 +Swamp Red Underwater Negative Shapers 4 +Swamp More Rotated Shapers 4 +Swamp More Rotated Shapers 4 +Swamp Near Laser Shortcut Right Panel +Treehouse First Door Panel +Treehouse Second Door Panel +Treehouse Beyond Yellow Bridge Door Panel +Treehouse Bridge Control +Treehouse Left Orange Bridge 15 +Treehouse Right Orange Bridge 12 +Treehouse Laser House Door Timer Outside Control +Treehouse Laser House Door Timer Inside Control +Inside Mountain Moving Background 7 +Inside Mountain Obscured Vision 5 +Inside Mountain Physically Obstructed 3 +Inside Mountain Angled Inside Trash 2 +Inside Mountain Color Cycle 5 +Inside Mountain Light Bridge Controller 2 +Inside Mountain Light Bridge Controller 3 +Inside Mountain Same Solution 6 +Inside Mountain Giant Puzzle +Inside Mountain Door to Final Room Left +Inside Mountain Door to Final Room Right +Inside Mountain Bottom Layer Discard +Inside Mountain Rock Control +Inside Mountain Secret Area Entry Panel +Inside Mountain Caves Lone Pillar +Inside Mountain Caves Shortcut to Mountain Panel +Inside Mountain Caves Shortcut to Swamp Panel +Inside Mountain Caves Challenge Entry Panel +Challenge Door to Theater Walkway Panel +Theater Walkway Theater Shortcut Panel +Theater Walkway Desert Shortcut Panel +Theater Walkway Town Shortcut Panel \ No newline at end of file diff --git a/worlds/witness/settings/Doors_Max.txt b/worlds/witness/settings/Doors_Max.txt new file mode 100644 index 0000000000..ec0a56a597 --- /dev/null +++ b/worlds/witness/settings/Doors_Max.txt @@ -0,0 +1,212 @@ +Items: +Outside Tutorial Optional Door +Outside Tutorial Outpost Entry Door +Outside Tutorial Outpost Exit Door +Glass Factory Entry Door +Glass Factory Back Wall +Symmetry Island Lower Door +Symmetry Island Upper Door +Orchard Middle Gate +Orchard Final Gate +Desert Door to Flood Light Room +Desert Door to Pond Room +Desert Door to Water Levels Room +Desert Door to Elevator Room +Quarry Main Entry 1 +Quarry Main Entry 2 +Quarry Door to Mill +Quarry Mill Side Door +Quarry Mill Rooftop Shortcut +Quarry Mill Stairs +Quarry Boathouse Boat Staircase +Quarry Boathouse First Barrier +Quarry Boathouse Shortcut +Shadows Timed Door +Shadows Laser Room Right Door +Shadows Laser Room Left Door +Shadows Barrier to Quarry +Shadows Barrier to Ledge +Keep Hedge Maze 1 Exit Door +Keep Pressure Plates 1 Exit Door +Keep Hedge Maze 2 Shortcut +Keep Hedge Maze 2 Exit Door +Keep Hedge Maze 3 Shortcut +Keep Hedge Maze 3 Exit Door +Keep Hedge Maze 4 Shortcut +Keep Hedge Maze 4 Exit Door +Keep Pressure Plates 2 Exit Door +Keep Pressure Plates 3 Exit Door +Keep Pressure Plates 4 Exit Door +Keep Shortcut to Shadows +Keep Tower Shortcut +Monastery Shortcut +Monastery Inner Door +Monastery Outer Door +Monastery Door to Garden +Town Cargo Box Door +Town Wooden Roof Staircase +Town Tinted Door to RGB House +Town Door to Church +Town Maze Staircase +Town Windmill Door +Town RGB House Staircase +Town Tower Blue Panels Door +Town Tower Lattice Door +Town Tower Environmental Set Door +Town Tower Wooden Roof Set Door +Theater Entry Door +Theater Exit Door Left +Theater Exit Door Right +Jungle Bamboo Shortcut to River +Jungle Popup Wall +River Shortcut to Monastery Garden +Bunker Bunker Entry Door +Bunker Tinted Glass Door +Bunker Door to Ultraviolet Room +Bunker Door to Elevator +Swamp Entry Door +Swamp Door to Broken Shapers +Swamp Platform Shortcut Door +Swamp Cyan Water Pump +Swamp Door to Rotated Shapers +Swamp Red Water Pump +Swamp Red Underwater Exit +Swamp Blue Water Pump +Swamp Purple Water Pump +Swamp Near Laser Shortcut +Treehouse First Door +Treehouse Second Door +Treehouse Beyond Yellow Bridge Door +Treehouse Drawbridge +Treehouse Timed Door to Laser House +Inside Mountain First Layer Exit Door +Inside Mountain Second Layer Staircase Near +Inside Mountain Second Layer Exit Door +Inside Mountain Second Layer Staircase Far +Inside Mountain Giant Puzzle Exit Door +Inside Mountain Door to Final Room +Inside Mountain Bottom Layer Rock +Inside Mountain Door to Secret Area +Caves Pillar Door +Caves Mountain Shortcut +Caves Swamp Shortcut +Challenge Entry Door +Challenge Door to Theater Walkway +Theater Walkway Door to Windmill Interior +Theater Walkway Door to Desert Elevator Room +Theater Walkway Door to Town + +Desert Flood Room Flood Controls (Panel) +Quarry Mill Ramp Controls (Panel) +Quarry Mill Elevator Controls (Panel) +Quarry Boathouse Ramp Height Control (Panel) +Quarry Boathouse Ramp Horizontal Control (Panel) +Bunker Elevator Control (Panel) +Swamp Sliding Bridge (Panel) +Swamp Rotating Bridge (Panel) +Swamp Maze Control (Panel) +Boat + +Added Locations: +Outside Tutorial Door to Outpost Panel +Outside Tutorial Exit Door from Outpost Panel +Glass Factory Entry Door Panel +Glass Factory Vertical Symmetry 5 +Symmetry Island Door to Symmetry Island Lower Panel +Symmetry Island Door to Symmetry Island Upper Panel +Orchard Apple Tree 3 +Orchard Apple Tree 5 +Desert Door to Desert Flood Light Room Panel +Desert Artificial Light Reflection 3 +Desert Door to Water Levels Room Panel +Desert Flood Reflection 6 +Quarry Door to Quarry 1 Panel +Quarry Door to Quarry 2 Panel +Quarry Door to Mill Right +Quarry Door to Mill Left +Quarry Mill Ground Floor Shortcut Door Panel +Quarry Mill Door to Outside Quarry Stairs Panel +Quarry Mill Stair Control +Quarry Boathouse Shortcut Door Panel +Shadows Door Timer Inside +Shadows Door Timer Outside +Shadows Environmental Avoid 8 +Shadows Follow 5 +Shadows Lower Avoid 3 +Shadows Lower Avoid 5 +Keep Hedge Maze 1 +Keep Pressure Plates 1 +Keep Hedge Maze 2 +Keep Hedge Maze 3 +Keep Hedge Maze 4 +Keep Pressure Plates 2 +Keep Pressure Plates 3 +Keep Pressure Plates 4 +Keep Shortcut to Shadows Panel +Keep Tower Shortcut to Keep Panel +Monastery Shortcut Door Panel +Monastery Door Open Left +Monastery Door Open Right +Monastery Rhombic Avoid 3 +Town Cargo Box Panel +Town Full Dot Grid Shapers 5 +Town Tinted Door Panel +Town Door to Church Stars Panel +Town Maze Stair Control +Town Windmill Door Panel +Town Sound Room Left +Town Sound Room Right +Town Symmetry Squares 5 + Dots +Town Church Lattice +Town Hexagonal Reflection +Town Shapers & Dots & Eraser +Windmill Door to Front of Theater Panel +Theater Door to Cargo Box Left Panel +Theater Door to Cargo Box Right Panel +Jungle Shortcut to River Panel +Jungle Popup Wall Control +River Rhombic Avoid to Monastery Garden +Bunker Bunker Entry Panel +Bunker Door to Bunker Proper Panel +Bunker Drawn Squares through Tinted Glass 3 +Bunker Drop-Down Door Squares 2 +Swamp Entry Panel +Swamp Platform Shapers 4 +Swamp Platform Shortcut Right Panel +Swamp Blue Underwater Negative Shapers 5 +Swamp Broken Shapers 4 +Swamp Cyan Underwater Negative Shapers 5 +Swamp Red Underwater Negative Shapers 4 +Swamp More Rotated Shapers 4 +Swamp More Rotated Shapers 4 +Swamp Near Laser Shortcut Right Panel +Treehouse First Door Panel +Treehouse Second Door Panel +Treehouse Beyond Yellow Bridge Door Panel +Treehouse Bridge Control +Treehouse Left Orange Bridge 15 +Treehouse Right Orange Bridge 12 +Treehouse Laser House Door Timer Outside Control +Treehouse Laser House Door Timer Inside Control +Inside Mountain Moving Background 7 +Inside Mountain Obscured Vision 5 +Inside Mountain Physically Obstructed 3 +Inside Mountain Angled Inside Trash 2 +Inside Mountain Color Cycle 5 +Inside Mountain Light Bridge Controller 2 +Inside Mountain Light Bridge Controller 3 +Inside Mountain Same Solution 6 +Inside Mountain Giant Puzzle +Inside Mountain Door to Final Room Left +Inside Mountain Door to Final Room Right +Inside Mountain Bottom Layer Discard +Inside Mountain Rock Control +Inside Mountain Secret Area Entry Panel +Inside Mountain Caves Lone Pillar +Inside Mountain Caves Shortcut to Mountain Panel +Inside Mountain Caves Shortcut to Swamp Panel +Inside Mountain Caves Challenge Entry Panel +Challenge Door to Theater Walkway Panel +Theater Walkway Theater Shortcut Panel +Theater Walkway Desert Shortcut Panel +Theater Walkway Town Shortcut Panel \ No newline at end of file diff --git a/worlds/witness/settings/Doors_Simple.txt b/worlds/witness/settings/Doors_Simple.txt new file mode 100644 index 0000000000..1335456d95 --- /dev/null +++ b/worlds/witness/settings/Doors_Simple.txt @@ -0,0 +1,146 @@ +Items: +Glass Factory Back Wall +Quarry Boathouse Boat Staircase +Outside Tutorial Outpost Doors +Glass Factory Entry Door +Symmetry Island Doors +Orchard Gates +Desert Doors +Quarry Main Entry +Quarry Door to Mill +Quarry Mill Shortcuts +Quarry Boathouse Barriers +Shadows Timed Door +Shadows Laser Room Door +Shadows Barriers +Keep Hedge Maze Doors +Keep Pressure Plates Doors +Keep Shortcuts +Monastery Entry Door +Monastery Shortcuts +Town Doors +Town Tower Doors +Theater Entry Door +Theater Exit Door +Jungle & River Shortcuts +Jungle Popup Wall +Bunker Doors +Swamp Doors +Swamp Near Laser Shortcut +Swamp Water Pumps +Treehouse Entry Doors +Treehouse Drawbridge +Treehouse Timed Door to Laser House +Inside Mountain First Layer Exit Door +Inside Mountain Second Layer Stairs & Doors +Inside Mountain Giant Puzzle Exit Door +Inside Mountain Door to Final Room +Inside Mountain Bottom Layer Doors to Caves +Caves Doors to Challenge +Caves Exits to Main Island +Challenge Door to Theater Walkway +Theater Walkway Doors + +Added Locations: +Outside Tutorial Door to Outpost Panel +Outside Tutorial Exit Door from Outpost Panel +Glass Factory Entry Door Panel +Glass Factory Vertical Symmetry 5 +Symmetry Island Door to Symmetry Island Lower Panel +Symmetry Island Door to Symmetry Island Upper Panel +Orchard Apple Tree 3 +Orchard Apple Tree 5 +Desert Door to Desert Flood Light Room Panel +Desert Artificial Light Reflection 3 +Desert Door to Water Levels Room Panel +Desert Flood Reflection 6 +Quarry Door to Quarry 1 Panel +Quarry Door to Quarry 2 Panel +Quarry Door to Mill Right +Quarry Door to Mill Left +Quarry Mill Ground Floor Shortcut Door Panel +Quarry Mill Door to Outside Quarry Stairs Panel +Quarry Mill Stair Control +Quarry Boathouse Shortcut Door Panel +Shadows Door Timer Inside +Shadows Door Timer Outside +Shadows Environmental Avoid 8 +Shadows Follow 5 +Shadows Lower Avoid 3 +Shadows Lower Avoid 5 +Keep Hedge Maze 1 +Keep Pressure Plates 1 +Keep Hedge Maze 2 +Keep Hedge Maze 3 +Keep Hedge Maze 4 +Keep Pressure Plates 2 +Keep Pressure Plates 3 +Keep Pressure Plates 4 +Keep Shortcut to Shadows Panel +Keep Tower Shortcut to Keep Panel +Monastery Shortcut Door Panel +Monastery Door Open Left +Monastery Door Open Right +Monastery Rhombic Avoid 3 +Town Cargo Box Panel +Town Full Dot Grid Shapers 5 +Town Tinted Door Panel +Town Door to Church Stars Panel +Town Maze Stair Control +Town Windmill Door Panel +Town Sound Room Left +Town Sound Room Right +Town Symmetry Squares 5 + Dots +Town Church Lattice +Town Hexagonal Reflection +Town Shapers & Dots & Eraser +Windmill Door to Front of Theater Panel +Theater Door to Cargo Box Left Panel +Theater Door to Cargo Box Right Panel +Jungle Shortcut to River Panel +Jungle Popup Wall Control +River Rhombic Avoid to Monastery Garden +Bunker Bunker Entry Panel +Bunker Door to Bunker Proper Panel +Bunker Drawn Squares through Tinted Glass 3 +Bunker Drop-Down Door Squares 2 +Swamp Entry Panel +Swamp Platform Shapers 4 +Swamp Platform Shortcut Right Panel +Swamp Blue Underwater Negative Shapers 5 +Swamp Broken Shapers 4 +Swamp Cyan Underwater Negative Shapers 5 +Swamp Red Underwater Negative Shapers 4 +Swamp More Rotated Shapers 4 +Swamp More Rotated Shapers 4 +Swamp Near Laser Shortcut Right Panel +Treehouse First Door Panel +Treehouse Second Door Panel +Treehouse Beyond Yellow Bridge Door Panel +Treehouse Bridge Control +Treehouse Left Orange Bridge 15 +Treehouse Right Orange Bridge 12 +Treehouse Laser House Door Timer Outside Control +Treehouse Laser House Door Timer Inside Control +Inside Mountain Moving Background 7 +Inside Mountain Obscured Vision 5 +Inside Mountain Physically Obstructed 3 +Inside Mountain Angled Inside Trash 2 +Inside Mountain Color Cycle 5 +Inside Mountain Light Bridge Controller 2 +Inside Mountain Light Bridge Controller 3 +Inside Mountain Same Solution 6 +Inside Mountain Giant Puzzle +Inside Mountain Door to Final Room Left +Inside Mountain Door to Final Room Right +Inside Mountain Bottom Layer Discard +Inside Mountain Rock Control +Inside Mountain Secret Area Entry Panel +Inside Mountain Caves Lone Pillar +Inside Mountain Caves Shortcut to Mountain Panel +Inside Mountain Caves Shortcut to Swamp Panel +Inside Mountain Caves Challenge Entry Panel +Challenge Door to Theater Walkway Panel +Theater Walkway Theater Shortcut Panel +Theater Walkway Desert Shortcut Panel +Theater Walkway Town Shortcut Panel \ No newline at end of file diff --git a/worlds/witness/settings/Early_UTM.txt b/worlds/witness/settings/Early_UTM.txt new file mode 100644 index 0000000000..893f29d8bb --- /dev/null +++ b/worlds/witness/settings/Early_UTM.txt @@ -0,0 +1,9 @@ +Items: +Caves Exits to Main Island + +Starting Inventory: +Caves Exits to Main Island + +Remove Items: +Caves Mountain Shortcut +Caves Swamp Shortcut \ No newline at end of file diff --git a/worlds/witness/settings/Laser_Shuffle.txt b/worlds/witness/settings/Laser_Shuffle.txt new file mode 100644 index 0000000000..668a13f94a --- /dev/null +++ b/worlds/witness/settings/Laser_Shuffle.txt @@ -0,0 +1,12 @@ +Items: +Symmetry Laser +Desert Laser +Keep Laser +Shadows Laser +Quarry Laser +Town Laser +Swamp Laser +Jungle Laser +Bunker Laser +Monastery Laser +Treehouse Laser \ No newline at end of file diff --git a/worlds/witness/settings/Symbol_Shuffle.txt b/worlds/witness/settings/Symbol_Shuffle.txt new file mode 100644 index 0000000000..d03391f5c5 --- /dev/null +++ b/worlds/witness/settings/Symbol_Shuffle.txt @@ -0,0 +1,14 @@ +Items: +Dots +Colored Dots +Sound Dots +Symmetry +Triangles +Eraser +Shapers +Rotated Shapers +Negative Shapers +Stars +Stars + Same Colored Symbol +Black/White Squares +Colored Squares \ No newline at end of file diff --git a/worlds/witness/static_logic.py b/worlds/witness/static_logic.py index 5f1d77d314..646957c462 100644 --- a/worlds/witness/static_logic.py +++ b/worlds/witness/static_logic.py @@ -5,12 +5,11 @@ from .utils import define_new_region, parse_lambda class StaticWitnessLogic: ALL_SYMBOL_ITEMS = set() + ALL_DOOR_ITEMS = set() + ALL_DOOR_ITEMS_AS_DICT = dict() ALL_USEFULS = set() ALL_TRAPS = set() ALL_BOOSTS = set() - ALL_DOOR_ITEM_IDS_BY_HEX = dict() - DOOR_NAMES_BY_HEX = dict() - ALL_DOOR_ITEMS = set() CONNECTIONS_TO_SEVER_BY_DOOR_HEX = dict() EVENT_PANELS_FROM_REGIONS = set() @@ -47,35 +46,23 @@ class StaticWitnessLogic: if line == "Usefuls:": current_set = self.ALL_USEFULS continue + if line == "Doors:": + current_set = self.ALL_DOOR_ITEMS + continue if line == "": continue line_split = line.split(" - ") - if current_set is not self.ALL_USEFULS: - current_set.add((line_split[1], int(line_split[0]))) - else: + if current_set is self.ALL_USEFULS: current_set.add((line_split[1], int(line_split[0]), line_split[2] == "True")) + elif current_set is self.ALL_DOOR_ITEMS: + new_door = (line_split[1], int(line_split[0]), frozenset(line_split[2].split(","))) + current_set.add(new_door) + self.ALL_DOOR_ITEMS_AS_DICT[line_split[1]] = new_door + else: + current_set.add((line_split[1], int(line_split[0]))) - path = os.path.join(os.path.dirname(__file__), "Door_Shuffle.txt") - with open(path, "r", encoding="utf-8") as file: - for line in file.readlines(): - line = line.strip() - - line_split = line.split(" - ") - - hex_set_split = line_split[1].split(",") - - sever_list = line_split[2].split(",") - sever_set = {sever_panel for sever_panel in sever_list if sever_panel != "None"} - - for door_hex in hex_set_split: - self.ALL_DOOR_ITEM_IDS_BY_HEX[door_hex] = int(line_split[0]) - self.CONNECTIONS_TO_SEVER_BY_DOOR_HEX[door_hex] = sever_set - - if len(line_split) > 3: - self.DOOR_NAMES_BY_HEX[door_hex] = line_split[3] - def read_logic_file(self): """ Reads the logic file and does the initial population of data structures @@ -84,10 +71,7 @@ class StaticWitnessLogic: with open(path, "r", encoding="utf-8") as file: current_region = dict() - discard_ids = 0 - normal_panel_ids = 0 - vault_ids = 0 - laser_ids = 0 + counter = 0 for line in file.readlines(): line = line.strip() @@ -95,7 +79,7 @@ class StaticWitnessLogic: if line == "": continue - if line[0] != "0": + if line[-1] == ":": new_region_and_connections = define_new_region(line) current_region = new_region_and_connections[0] region_name = current_region["name"] @@ -105,12 +89,33 @@ class StaticWitnessLogic: line_split = line.split(" - ") + location_id = line_split.pop(0) + check_name_full = line_split.pop(0) check_hex = check_name_full[0:7] check_name = check_name_full[9:-1] required_panel_lambda = line_split.pop(0) + + if location_id == "Door" or location_id == "Laser": + self.CHECKS_BY_HEX[check_hex] = { + "checkName": current_region["shortName"] + " " + check_name, + "checkHex": check_hex, + "region": current_region, + "id": None, + "panelType": location_id + } + + self.CHECKS_BY_NAME[self.CHECKS_BY_HEX[check_hex]["checkName"]] = self.CHECKS_BY_HEX[check_hex] + + self.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX[check_hex] = { + "panels": parse_lambda(required_panel_lambda) + } + + current_region["panels"].add(check_hex) + continue + required_item_lambda = line_split.pop(0) laser_names = { @@ -123,53 +128,14 @@ class StaticWitnessLogic: if "Discard" in check_name: location_type = "Discard" - location_id = discard_ids - discard_ids += 1 elif is_vault_or_video or check_name == "Tutorial Gate Close": location_type = "Vault" - location_id = vault_ids - vault_ids += 1 elif check_name in laser_names: location_type = "Laser" - location_id = laser_ids - laser_ids += 1 else: location_type = "General" - if check_hex == "0x012D7": # Compatibility - normal_panel_ids += 1 - - if check_hex == "0x17E07": # Compatibility - location_id = 112 - - elif check_hex == "0xFFF00": - location_id = 800 - - else: - location_id = normal_panel_ids - normal_panel_ids += 1 - required_items = parse_lambda(required_item_lambda) - items_actually_in_the_game = {item[0] for item in self.ALL_SYMBOL_ITEMS} - required_items = set( - subset.intersection(items_actually_in_the_game) - for subset in required_items - ) - - doors_in_the_game = self.ALL_DOOR_ITEM_IDS_BY_HEX.keys() - if check_hex in doors_in_the_game: - door_name = current_region["shortName"] + " " + check_name + " Power On" - if check_hex in self.DOOR_NAMES_BY_HEX.keys(): - door_name = self.DOOR_NAMES_BY_HEX[check_hex] - - required_items = set( - subset.union(frozenset({door_name})) - for subset in required_items - ) - - self.ALL_DOOR_ITEMS.add( - (door_name, self.ALL_DOOR_ITEM_IDS_BY_HEX[check_hex]) - ) required_items = frozenset(required_items) @@ -182,7 +148,7 @@ class StaticWitnessLogic: "checkName": current_region["shortName"] + " " + check_name, "checkHex": check_hex, "region": current_region, - "idOffset": location_id, + "id": int(location_id), "panelType": location_type } diff --git a/worlds/witness/utils.py b/worlds/witness/utils.py index df4b43717e..809b2b1c3d 100644 --- a/worlds/witness/utils.py +++ b/worlds/witness/utils.py @@ -98,9 +98,39 @@ def get_adjustment_file(adjustment_file): @cache_argsless def get_disable_unrandomized_list(): - return get_adjustment_file("Disable_Unrandomized.txt") + return get_adjustment_file("settings/Disable_Unrandomized.txt") @cache_argsless def get_early_utm_list(): - return get_adjustment_file("Early_UTM.txt") \ No newline at end of file + return get_adjustment_file("settings/Early_UTM.txt") + + +@cache_argsless +def get_symbol_shuffle_list(): + return get_adjustment_file("settings/Symbol_Shuffle.txt") + + +@cache_argsless +def get_door_panel_shuffle_list(): + return get_adjustment_file("settings/Door_Panel_Shuffle.txt") + + +@cache_argsless +def get_doors_simple_list(): + return get_adjustment_file("settings/Doors_Simple.txt") + + +@cache_argsless +def get_doors_complex_list(): + return get_adjustment_file("settings/Doors_Complex.txt") + + +@cache_argsless +def get_doors_max_list(): + return get_adjustment_file("settings/Doors_Max.txt") + + +@cache_argsless +def get_laser_shuffle(): + return get_adjustment_file("settings/Laser_Shuffle.txt")