From 0be0732a2b04de56ffb9ad43545e35d9c75d1e01 Mon Sep 17 00:00:00 2001 From: The T Date: Sat, 28 Jan 2023 21:41:20 -0500 Subject: [PATCH 01/47] WebHost: FAQ: change "seeds" to "a world" where world is the right term. Berserker has frequently corrected, that each player's game is a single world, inside a larger seed; not that each player's game is a seed. --- WebHostLib/static/assets/faq/faq_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebHostLib/static/assets/faq/faq_en.md b/WebHostLib/static/assets/faq/faq_en.md index 339a2a15ea..74f423df1f 100644 --- a/WebHostLib/static/assets/faq/faq_en.md +++ b/WebHostLib/static/assets/faq/faq_en.md @@ -20,7 +20,7 @@ comfortable exploiting certain glitches in the game. ## What is a multi-world? While a randomizer shuffles a game, a multi-world randomizer shuffles that game for multiple players. For example, in a -two player multi-world, players A and B each get their own randomized version of a game, called seeds. In each player's +two player multi-world, players A and B each get their own randomized version of a game, called a world. In each player's game, they may find items which belong to the other player. If player A finds an item which belongs to player B, the item will be sent to player B's world over the internet. From 6c1023a88c0042ab368a7ae15ca522c5ea7a95b1 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 29 Jan 2023 22:12:39 +0100 Subject: [PATCH 02/47] Subnautica: fix swim_rule considers items property use (#1419) --- worlds/subnautica/Rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/subnautica/Rules.py b/worlds/subnautica/Rules.py index 673c844247..6283026433 100644 --- a/worlds/subnautica/Rules.py +++ b/worlds/subnautica/Rules.py @@ -152,7 +152,7 @@ def has_ultra_glide_fins(state: "CollectionState", player: int) -> bool: def get_max_swim_depth(state: "CollectionState", player: int) -> int: swim_rule: SwimRule = state.multiworld.swim_rule[player] depth: int = swim_rule.base_depth - if swim_rule == swim_rule.consider_items: + if swim_rule.consider_items: if has_seaglide(state, player): if has_ultra_high_capacity_tank(state, player): depth += 350 # It's about 800m. Give some room From 11873e059a45a4f526f94d9bb6a1478665bb1c91 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 29 Jan 2023 22:14:20 +0100 Subject: [PATCH 03/47] Setup: don't use dependency before it's installed --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 238732790a..a9c7da5c5c 100644 --- a/setup.py +++ b/setup.py @@ -11,8 +11,6 @@ from collections.abc import Iterable from hashlib import sha3_512 from pathlib import Path -import setuptools -import setuptools.command.build if __name__ == "__main__": import ModuleUpdate @@ -41,12 +39,15 @@ except pkg_resources.ResolutionError: subprocess.call([sys.executable, '-m', 'pip', 'install', requirement, '--upgrade']) import cx_Freeze +# .build only exists if cx-Freeze is the right version, so we have to update/install that first before this line +import setuptools.command.build if os.path.exists("X:/pw.txt"): print("Using signtool") with open("X:/pw.txt", encoding="utf-8-sig") as f: pw = f.read() - signtool = r'signtool sign /f X:/_SITS_Zertifikat_.pfx /p "' + pw + r'" /fd sha256 /tr http://timestamp.digicert.com/ ' + signtool = r'signtool sign /f X:/_SITS_Zertifikat_.pfx /p "' + pw + \ + r'" /fd sha256 /tr http://timestamp.digicert.com/ ' else: signtool = None From ea2175cb8a22da1a4e98c89503aa9ecf47ad7bac Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 30 Jan 2023 00:54:57 +0100 Subject: [PATCH 04/47] MultiServer: load old forfeit_mode if release_mode not present --- MultiServer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MultiServer.py b/MultiServer.py index 1df0936259..98d0905bbc 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -574,7 +574,7 @@ class Context: self.location_check_points = savedata["game_options"]["location_check_points"] self.server_password = savedata["game_options"]["server_password"] self.password = savedata["game_options"]["password"] - self.release_mode = savedata["game_options"]["release_mode"] + self.release_mode = savedata["game_options"].get("release_mode", savedata["game_options"].get("forfeit_mode", "goal")) self.remaining_mode = savedata["game_options"]["remaining_mode"] self.collect_mode = savedata["game_options"]["collect_mode"] self.item_cheat = savedata["game_options"]["item_cheat"] From 428344b6bccf7f69193e5b10d9db3d42414d01f1 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 30 Jan 2023 01:47:28 +0100 Subject: [PATCH 05/47] setup: honor build automation ... ... and reorder imports to PEP it up --- setup.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/setup.py b/setup.py index a9c7da5c5c..35a6acc8e8 100644 --- a/setup.py +++ b/setup.py @@ -10,27 +10,12 @@ import zipfile from collections.abc import Iterable from hashlib import sha3_512 from pathlib import Path - - -if __name__ == "__main__": - import ModuleUpdate - ModuleUpdate.update() - -from Launcher import components, icon_paths -from Utils import version_tuple, is_windows, is_linux - -# On Python < 3.10 LogicMixin is not currently supported. -apworlds: set = { - "Subnautica", - "Factorio", - "Rogue Legacy", -} - -# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it import subprocess import pkg_resources -requirement = 'cx-Freeze>=6.14.1' + +# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it try: + requirement = 'cx-Freeze>=6.14.1' pkg_resources.require(requirement) import cx_Freeze except pkg_resources.ResolutionError: @@ -42,6 +27,24 @@ except pkg_resources.ResolutionError: # .build only exists if cx-Freeze is the right version, so we have to update/install that first before this line import setuptools.command.build +if __name__ == "__main__": + # need to run this early to import from Utils and Launcher + # TODO: move stuff to not require this + import ModuleUpdate + ModuleUpdate.update(yes="--yes" in sys.argv or "-y" in sys.argv) + ModuleUpdate.update_ran = False # restore for later + +from Launcher import components, icon_paths +from Utils import version_tuple, is_windows, is_linux + + +# On Python < 3.10 LogicMixin is not currently supported. +apworlds: set = { + "Subnautica", + "Factorio", + "Rogue Legacy", +} + if os.path.exists("X:/pw.txt"): print("Using signtool") with open("X:/pw.txt", encoding="utf-8-sig") as f: From dc2aa5f41e208168fe36848ba69dd1919f46901c Mon Sep 17 00:00:00 2001 From: PoryGone <98504756+PoryGone@users.noreply.github.com> Date: Sun, 29 Jan 2023 23:53:56 -0500 Subject: [PATCH 06/47] SMW: v1.1 Content Update (#1344) * Make Bowser unkillable on Egg Hunt * Increment Data Package version Changed a location name. * Baseline for Bowser Rooms shuffling * Add boss shuffle * Remove extra space * Overworld Palette Shuffle * Fix Literature Trap typo * Handle Queuing traps and new Timer Trap * Fix trap name and actually create them * Early Climb and Overworld Speed * Add correct tooltip for Early Climb * Tooltip text edit * Address unconnected regions * Add option to fully exclude Special Zone levels from the seed * Fix Chocolate Island 4 Dragon Coins logic * Update worlds/smw/Client.py to use `getattr` --- worlds/smw/Aesthetics.py | 15 ++++ worlds/smw/Client.py | 82 ++++++++++++++++- worlds/smw/Items.py | 1 + worlds/smw/Levels.py | 99 +++++++++++++++++++-- worlds/smw/Locations.py | 22 +++++ worlds/smw/Names/ItemName.py | 1 + worlds/smw/Names/LiteratureTrap.py | 2 +- worlds/smw/Options.py | 84 +++++++++++++++++- worlds/smw/Regions.py | 20 +---- worlds/smw/Rom.py | 137 ++++++++++++++++++++++++----- worlds/smw/__init__.py | 20 ++++- 11 files changed, 425 insertions(+), 58 deletions(-) diff --git a/worlds/smw/Aesthetics.py b/worlds/smw/Aesthetics.py index 624440c55f..fb53295de1 100644 --- a/worlds/smw/Aesthetics.py +++ b/worlds/smw/Aesthetics.py @@ -136,6 +136,15 @@ valid_background_colors = { 0xFFF45A: [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07], # Castle } +valid_ow_palettes = { + 0x2D1E: [0x00, 0x01, 0x03], # Main OW + 0x2D1F: [0x00, 0x03, 0x04], # Yoshi's Island + 0x2D20: [0x00, 0x01, 0x03, 0x04], # Vanilla Dome + 0x2D21: [0x00, 0x02, 0x03, 0x04], # Forest of Illusion + 0x2D22: [0x00, 0x01, 0x03, 0x04], # Valley of Bowser + 0x2D24: [0x00, 0x02, 0x03], # Star Road +} + def generate_shuffled_level_music(world, player): shuffled_level_music = level_music_value_data.copy() @@ -158,6 +167,12 @@ def generate_shuffled_ow_music(world, player): return shuffled_ow_music +def generate_shuffled_ow_palettes(rom, world, player): + if world.overworld_palette_shuffle[player]: + for address, valid_palettes in valid_ow_palettes.items(): + chosen_palette = world.random.choice(valid_palettes) + rom.write_byte(address, chosen_palette) + def generate_shuffled_header_data(rom, world, player): if world.music_shuffle[player] != "full" and not world.foreground_palette_shuffle[player] and not world.background_palette_shuffle[player]: return diff --git a/worlds/smw/Client.py b/worlds/smw/Client.py index c2981eff8e..92aeac4d4a 100644 --- a/worlds/smw/Client.py +++ b/worlds/smw/Client.py @@ -169,7 +169,74 @@ class SMWSNIClient(SNIClient): await snes_flush_writes(ctx) - return + + def add_trap_to_queue(self, trap_item, trap_msg): + self.trap_queue = getattr(self, "trap_queue", []) + + self.trap_queue.append((trap_item, trap_msg)) + + + async def handle_trap_queue(self, ctx): + from SNIClient import snes_buffered_write, snes_flush_writes, snes_read + + if not hasattr(self, "trap_queue") or len(self.trap_queue) == 0: + return + + game_state = await snes_read(ctx, SMW_GAME_STATE_ADDR, 0x1) + if game_state[0] != 0x14: + return + + mario_state = await snes_read(ctx, SMW_MARIO_STATE_ADDR, 0x1) + if mario_state[0] != 0x00: + return + + pause_state = await snes_read(ctx, SMW_PAUSE_ADDR, 0x1) + if pause_state[0] != 0x00: + return + + next_trap, message = self.trap_queue.pop(0) + + from worlds.smw.Rom import trap_rom_data + if next_trap.item in trap_rom_data: + trap_active = await snes_read(ctx, WRAM_START + trap_rom_data[next_trap.item][0], 0x3) + + if next_trap.item == 0xBC0016: + # Timer Trap + if trap_active[0] == 0 or (trap_active[0] == 1 and trap_active[1] == 0 and trap_active[2] == 0): + # Trap already active + self.add_trap_to_queue(next_trap, message) + return + else: + snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0], bytes([0x01])) + snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0] + 1, bytes([0x00])) + snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0] + 2, bytes([0x00])) + else: + if trap_active[0] > 0: + # Trap already active + self.add_trap_to_queue(next_trap, message) + return + else: + verify_game_state = await snes_read(ctx, SMW_GAME_STATE_ADDR, 0x1) + if verify_game_state[0] == 0x14 and len(trap_rom_data[next_trap.item]) > 2: + snes_buffered_write(ctx, SMW_SFX_ADDR, bytes([trap_rom_data[next_trap.item][2]])) + + new_item_count = trap_rom_data[next_trap.item][1] + snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0], bytes([new_item_count])) + + current_level = await snes_read(ctx, SMW_CURRENT_LEVEL_ADDR, 0x1) + if current_level[0] in SMW_BAD_TEXT_BOX_LEVELS: + return + + boss_state = await snes_read(ctx, SMW_BOSS_STATE_ADDR, 0x1) + if boss_state[0] in SMW_BOSS_STATES: + return + + active_boss = await snes_read(ctx, SMW_ACTIVE_BOSS_ADDR, 0x1) + if active_boss[0] != 0x00: + return + + if ctx.receive_option == 1 or (ctx.receive_option == 2 and ((next_trap.flags & 1) != 0)): + self.add_message_to_queue(message) async def game_watcher(self, ctx): @@ -229,13 +296,14 @@ class SMWSNIClient(SNIClient): await snes_flush_writes(ctx) await self.handle_message_queue(ctx) + await self.handle_trap_queue(ctx) new_checks = [] event_data = await snes_read(ctx, SMW_EVENT_ROM_DATA, 0x60) progress_data = bytearray(await snes_read(ctx, SMW_PROGRESS_DATA, 0x0F)) dragon_coins_data = bytearray(await snes_read(ctx, SMW_DRAGON_COINS_DATA, 0x0C)) dragon_coins_active = await snes_read(ctx, SMW_DRAGON_COINS_ACTIVE_ADDR, 0x1) - from worlds.smw.Rom import item_rom_data, ability_rom_data + from worlds.smw.Rom import item_rom_data, ability_rom_data, trap_rom_data from worlds.smw.Levels import location_id_to_level_id, level_info_dict from worlds import AutoWorldRegister for loc_name, level_data in location_id_to_level_id.items(): @@ -307,7 +375,7 @@ class SMWSNIClient(SNIClient): ctx.location_names[item.location], recv_index, len(ctx.items_received))) if ctx.receive_option == 1 or (ctx.receive_option == 2 and ((item.flags & 1) != 0)): - if item.item != 0xBC0012: + if item.item != 0xBC0012 and item.item not in trap_rom_data: # Don't send messages for Boss Tokens item_name = ctx.item_names[item.item] player_name = ctx.player_names[item.player] @@ -316,7 +384,13 @@ class SMWSNIClient(SNIClient): self.add_message_to_queue(receive_message) snes_buffered_write(ctx, SMW_RECV_PROGRESS_ADDR, bytes([recv_index])) - if item.item in item_rom_data: + if item.item in trap_rom_data: + item_name = ctx.item_names[item.item] + player_name = ctx.player_names[item.player] + + receive_message = generate_received_text(item_name, player_name) + self.add_trap_to_queue(item, receive_message) + elif item.item in item_rom_data: item_count = await snes_read(ctx, WRAM_START + item_rom_data[item.item][0], 0x1) increment = item_rom_data[item.item][1] diff --git a/worlds/smw/Items.py b/worlds/smw/Items.py index e650aef4a5..5b6cce5a7f 100644 --- a/worlds/smw/Items.py +++ b/worlds/smw/Items.py @@ -49,6 +49,7 @@ trap_table = { ItemName.ice_trap: ItemData(0xBC0013, False, True), ItemName.stun_trap: ItemData(0xBC0014, False, True), ItemName.literature_trap: ItemData(0xBC0015, False, True), + ItemName.timer_trap: ItemData(0xBC0016, False, True), } event_table = { diff --git a/worlds/smw/Levels.py b/worlds/smw/Levels.py index e61a312b8d..3940a08c7c 100644 --- a/worlds/smw/Levels.py +++ b/worlds/smw/Levels.py @@ -1,6 +1,80 @@ from .Names import LocationName + +class BowserRoom(): + name: str + exitAddress: int + roomID: int + + def __init__(self, name: str, exitAddress: int, roomID: int): + self.name = name + self.exitAddress = exitAddress + self.roomID = roomID + +full_bowser_rooms = [ + BowserRoom("Hallway 1 - Door 1", 0x3A680, 0x0D), + BowserRoom("Hallway 1 - Door 2", 0x3A684, 0x0D), + BowserRoom("Hallway 1 - Door 3", 0x3A688, 0x0D), + BowserRoom("Hallway 1 - Door 4", 0x3A68C, 0x0D), + BowserRoom("Hallway 2 - Door 1", 0x3A8CB, 0xD0), + BowserRoom("Hallway 2 - Door 2", 0x3A8CF, 0xD0), + BowserRoom("Hallway 2 - Door 3", 0x3A8D3, 0xD0), + BowserRoom("Hallway 2 - Door 4", 0x3A8D7, 0xD0), + + BowserRoom("Room 1", 0x3A705, 0xD4), + BowserRoom("Room 2", 0x3A763, 0xD3), + BowserRoom("Room 3", 0x3A800, 0xD2), + BowserRoom("Room 4", 0x3A83D, 0xD1), + BowserRoom("Room 5", 0x3A932, 0xCF), + BowserRoom("Room 6", 0x3A9E1, 0xCE), + BowserRoom("Room 7", 0x3AA75, 0xCD), + BowserRoom("Room 8", 0x3AAC7, 0xCC), +] + +standard_bowser_rooms = [ + BowserRoom("Room 1", 0x3A705, 0xD4), + BowserRoom("Room 2", 0x3A763, 0xD3), + BowserRoom("Room 3", 0x3A800, 0xD2), + BowserRoom("Room 4", 0x3A83D, 0xD1), + BowserRoom("Room 5", 0x3A932, 0xCF), + BowserRoom("Room 6", 0x3A9E1, 0xCE), + BowserRoom("Room 7", 0x3AA75, 0xCD), + BowserRoom("Room 8", 0x3AAC7, 0xCC), +] + + +class BossRoom(): + name: str + exitAddress: int + exitAddressAlt: int + roomID: int + + def __init__(self, name: str, exitAddress: int, roomID: int, exitAddressAlt=None): + self.name = name + self.exitAddress = exitAddress + self.roomID = roomID + self.exitAddressAlt = exitAddressAlt + + +submap_boss_rooms = [ + BossRoom("#1 Lemmy Koopa", 0x311E3, 0xF6), # Submap 0x1F6 + BossRoom("#3 Lemmy Koopa", 0x33749, 0xF2), # Submap 0x1F2 + BossRoom("Valley Reznor", 0x3A132, 0xDE), # Submap 0x1DE + BossRoom("#7 Larry Koopa", 0x3A026, 0xEB), # Submap 0x1EB +] + +ow_boss_rooms = [ + BossRoom("#2 Morton Koopa Jr.", 0x3209B, 0xE5), # OW 0x0E5 + BossRoom("Vanilla Reznor", 0x33EAB, 0xDF), # OW 0x0DF + BossRoom("#4 Ludwig von Koopa", 0x346EA, 0xD9), # OW 0x0D9 + BossRoom("Forest Reznor", 0x3643E, 0xD5, 0x36442), # OW 0x0D5 + BossRoom("#5 Roy Koopa", 0x35ABC, 0xCC), # OW 0x0CC + BossRoom("Chocolate Reznor", 0x3705B, 0xE2), # OW 0x0E2 + BossRoom("#6 Wendy O. Koopa", 0x38BB5, 0xD3), # OW 0x0D3 +] + + class SMWPath(): thisEndDirection: int otherLevelID: int @@ -203,6 +277,9 @@ hard_single_levels = [ 0x3B, 0x3A, 0x37, +] + +special_zone_levels = [ 0x4E, 0x4F, 0x50, @@ -443,6 +520,7 @@ def generate_level_list(world, player): world.random.shuffle(easy_single_levels_copy) hard_single_levels_copy = hard_single_levels.copy() world.random.shuffle(hard_single_levels_copy) + special_zone_levels_copy = special_zone_levels.copy() easy_double_levels_copy = easy_double_levels.copy() world.random.shuffle(easy_double_levels_copy) hard_double_levels_copy = hard_double_levels.copy() @@ -474,6 +552,8 @@ def generate_level_list(world, player): shuffled_level_list.append(0x16) single_levels_copy = (easy_single_levels_copy.copy() + hard_single_levels_copy.copy()) + if not world.exclude_special_zone[player]: + single_levels_copy.extend(special_zone_levels_copy) world.random.shuffle(single_levels_copy) castle_fortress_levels_copy = (easy_castle_fortress_levels_copy.copy() + hard_castle_fortress_levels_copy.copy()) @@ -566,14 +646,17 @@ def generate_level_list(world, player): # Special Zone shuffled_level_list.append(0x4D) - shuffled_level_list.append(single_levels_copy.pop(0)) - shuffled_level_list.append(single_levels_copy.pop(0)) - shuffled_level_list.append(single_levels_copy.pop(0)) - shuffled_level_list.append(single_levels_copy.pop(0)) - shuffled_level_list.append(single_levels_copy.pop(0)) - shuffled_level_list.append(single_levels_copy.pop(0)) - shuffled_level_list.append(single_levels_copy.pop(0)) - shuffled_level_list.append(single_levels_copy.pop(0)) + if not world.exclude_special_zone[player]: + shuffled_level_list.append(single_levels_copy.pop(0)) + shuffled_level_list.append(single_levels_copy.pop(0)) + shuffled_level_list.append(single_levels_copy.pop(0)) + shuffled_level_list.append(single_levels_copy.pop(0)) + shuffled_level_list.append(single_levels_copy.pop(0)) + shuffled_level_list.append(single_levels_copy.pop(0)) + shuffled_level_list.append(single_levels_copy.pop(0)) + shuffled_level_list.append(single_levels_copy.pop(0)) + else: + shuffled_level_list.extend(special_zone_levels_copy) shuffled_level_list.append(0x48) return shuffled_level_list diff --git a/worlds/smw/Locations.py b/worlds/smw/Locations.py index a997f92f65..a8b7f7a4ec 100644 --- a/worlds/smw/Locations.py +++ b/worlds/smw/Locations.py @@ -212,6 +212,28 @@ all_locations = { **yoshi_house_location_table, } +special_zone_level_names = [ + LocationName.special_zone_1_exit_1, + LocationName.special_zone_2_exit_1, + LocationName.special_zone_3_exit_1, + LocationName.special_zone_4_exit_1, + LocationName.special_zone_5_exit_1, + LocationName.special_zone_6_exit_1, + LocationName.special_zone_7_exit_1, + LocationName.special_zone_8_exit_1, +] + +special_zone_dragon_coin_names = [ + LocationName.special_zone_1_dragon, + LocationName.special_zone_2_dragon, + LocationName.special_zone_3_dragon, + LocationName.special_zone_4_dragon, + LocationName.special_zone_5_dragon, + LocationName.special_zone_6_dragon, + LocationName.special_zone_7_dragon, + LocationName.special_zone_8_dragon, +] + location_table = {} diff --git a/worlds/smw/Names/ItemName.py b/worlds/smw/Names/ItemName.py index 72c984b016..fecb18685e 100644 --- a/worlds/smw/Names/ItemName.py +++ b/worlds/smw/Names/ItemName.py @@ -26,6 +26,7 @@ blue_switch_palace = "Blue Switch Palace" ice_trap = "Ice Trap" stun_trap = "Stun Trap" literature_trap = "Literature Trap" +timer_trap = "Timer Trap" # Other Definitions victory = "The Princess" diff --git a/worlds/smw/Names/LiteratureTrap.py b/worlds/smw/Names/LiteratureTrap.py index 94c038228d..19cd402b1c 100644 --- a/worlds/smw/Names/LiteratureTrap.py +++ b/worlds/smw/Names/LiteratureTrap.py @@ -25,7 +25,7 @@ lit_trap_text_list = [ [[0x18, 0x4e, 0x54, 0x1f, 0x47, 0x40, 0x55, 0x44, 0x1f, 0x41, 0x51, 0x40, 0x48, 0x4d, 0x52, 0x1f, 0x48, 0xcd, 0x58, 0x4e, 0x54, 0x51, 0x1f, 0x47, 0x44, 0x40, 0x43, 0x1b, 0x1f, 0x18, 0x4e, 0x54, 0x9f, 0x47, 0x40, 0x55, 0x44, 0x1f, 0x45, 0x44, 0x44, 0x53, 0x1f, 0x48, 0x4d, 0x1f, 0x58, 0x4e, 0x54, 0x51, 0x9f, 0x52, 0x47, 0x4e, 0x44, 0x52, 0x1b, 0x1f, 0x18, 0x4e, 0x54, 0x1f, 0x42, 0x40, 0x4d, 0x9f, 0x52, 0x53, 0x44, 0x44, 0x51, 0x1f, 0x58, 0x4e, 0x54, 0x51, 0x52, 0x44, 0x4b, 0x45, 0x1f, 0x40, 0x4d, 0xd8, 0x43, 0x48, 0x51, 0x44, 0x42, 0x53, 0x48, 0x4e, 0x4d, 0x1f, 0x58, 0x4e, 0x54, 0x9f, 0x42, 0x47, 0x4e, 0x4e, 0x52, 0x44, 0x1b, 0x1f, 0x18, 0x4e, 0x54, 0x5d, 0x51, 0x44, 0x1f, 0x4e, 0x4d, 0x9f, 0x58, 0x4e, 0x54, 0x51, 0x1f, 0x4e, 0x56, 0x4d, 0x1b, 0x1f, 0x0, 0x4d, 0x43, 0x1f, 0x58, 0x4e, 0x54, 0x9f, ], [0x4a, 0x4d, 0x4e, 0x56, 0x1f, 0x56, 0x47, 0x40, 0x53, 0x1f, 0x58, 0x4e, 0x54, 0x9f, 0x4a, 0x4d, 0x4e, 0x56, 0x1b, 0x1f, 0x0, 0x4d, 0x43, 0x1f, 0x18, 0xe, 0x14, 0x1f, 0x40, 0x51, 0x44, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x4e, 0x4d, 0x44, 0x1f, 0x56, 0x47, 0x4e, 0x5d, 0x4b, 0x4b, 0x9f, 0x43, 0x44, 0x42, 0x48, 0x43, 0x44, 0x1f, 0x56, 0x47, 0x44, 0x51, 0x44, 0x1f, 0x53, 0x4e, 0x9f, 0x46, 0x4e, 0x1b, 0x1b, 0x1b, 0x1f, 0x1c, 0x12, 0x54, 0x44, 0x52, 0x52, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, ]], [[0x16, 0x47, 0x44, 0x4d, 0x1f, 0x58, 0x4e, 0x54, 0x1f, 0x47, 0x40, 0x55, 0x44, 0x9f, 0x44, 0x4b, 0x48, 0x4c, 0x48, 0x4d, 0x40, 0x53, 0x44, 0x43, 0x1f, 0x40, 0x4b, 0x4b, 0x9f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x48, 0x52, 0x9f, 0x48, 0x4c, 0x4f, 0x4e, 0x52, 0x52, 0x48, 0x41, 0x4b, 0x44, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x4d, 0x9f, 0x56, 0x47, 0x40, 0x53, 0x44, 0x55, 0x44, 0x51, 0x1f, 0x51, 0x44, 0x4c, 0x40, 0x48, 0x4d, 0x52, 0x1d, 0x9f, 0x47, 0x4e, 0x56, 0x44, 0x55, 0x44, 0x51, 0x9f, 0x48, 0x4c, 0x4f, 0x51, 0x4e, 0x41, 0x40, 0x41, 0x4b, 0x44, 0x1d, 0x1f, 0x4c, 0x54, 0x52, 0x53, 0x9f, 0x41, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x53, 0x51, 0x54, 0x53, 0x47, 0x1b, 0x9f, ], [0x1c, 0x3, 0x4e, 0x58, 0x4b, 0x44, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, ]], [[0x5d, 0xc, 0x58, 0x1f, 0x4c, 0x48, 0x4d, 0x43, 0x1d, 0x5d, 0x1f, 0x47, 0x44, 0x9f, 0x52, 0x40, 0x48, 0x43, 0x1d, 0x1f, 0x5d, 0x51, 0x44, 0x41, 0x44, 0x4b, 0x52, 0x1f, 0x40, 0x53, 0x9f, 0x52, 0x53, 0x40, 0x46, 0x4d, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1b, 0x1f, 0x6, 0x48, 0x55, 0x44, 0x9f, 0x4c, 0x44, 0x1f, 0x4f, 0x51, 0x4e, 0x41, 0x4b, 0x44, 0x4c, 0x52, 0x1d, 0x1f, 0x46, 0x48, 0x55, 0x44, 0x9f, 0x4c, 0x44, 0x1f, 0x56, 0x4e, 0x51, 0x4a, 0x1d, 0x1f, 0x46, 0x48, 0x55, 0x44, 0x1f, 0x4c, 0x44, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x4c, 0x4e, 0x52, 0x53, 0x1f, 0x40, 0x41, 0x52, 0x53, 0x51, 0x54, 0x52, 0x44, 0x9f, 0x42, 0x51, 0x58, 0x4f, 0x53, 0x4e, 0x46, 0x51, 0x40, 0x4c, 0x1f, 0x4e, 0x51, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4c, 0x4e, 0x52, 0x53, 0x1f, 0x48, 0x4d, 0x53, 0x51, 0x48, 0x42, 0x40, 0x53, 0x44, 0x9f, ], [0x40, 0x4d, 0x40, 0x4b, 0x58, 0x52, 0x48, 0x52, 0x1d, 0x1f, 0x40, 0x4d, 0x43, 0x1f, 0x8, 0x1f, 0x40, 0xcc, 0x48, 0x4d, 0x1f, 0x4c, 0x58, 0x1f, 0x4e, 0x56, 0x4d, 0x1f, 0x4f, 0x51, 0x4e, 0x4f, 0x44, 0x51, 0x9f, 0x40, 0x53, 0x4c, 0x4e, 0x52, 0x4f, 0x47, 0x44, 0x51, 0x44, 0x1b, 0x1f, 0x8, 0x1f, 0x42, 0x40, 0x4d, 0x9f, 0x43, 0x48, 0x52, 0x4f, 0x44, 0x4d, 0x52, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x4d, 0x1f, 0x56, 0x48, 0x53, 0xc7, 0x40, 0x51, 0x53, 0x48, 0x45, 0x48, 0x42, 0x48, 0x40, 0x4b, 0x9f, 0x52, 0x53, 0x48, 0x4c, 0x54, 0x4b, 0x40, 0x4d, 0x53, 0x52, 0x1b, 0x1f, 0x1, 0x54, 0x53, 0x1f, 0x8, 0x9f, 0x40, 0x41, 0x47, 0x4e, 0x51, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x43, 0x54, 0x4b, 0x4b, 0x9f, 0x51, 0x4e, 0x54, 0x53, 0x48, 0x4d, 0x44, 0x1f, 0x4e, 0x45, 0x9f, ], [0x44, 0x57, 0x48, 0x52, 0x53, 0x44, 0x4d, 0x42, 0x44, 0x1b, 0x1f, 0x8, 0x1f, 0x42, 0x51, 0x40, 0x55, 0xc4, 0x45, 0x4e, 0x51, 0x1f, 0x4c, 0x44, 0x4d, 0x53, 0x40, 0x4b, 0x9f, 0x44, 0x57, 0x40, 0x4b, 0x53, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1b, 0x1f, 0x13, 0x47, 0x40, 0x53, 0x9f, 0x48, 0x52, 0x1f, 0x56, 0x47, 0x58, 0x1f, 0x8, 0x1f, 0x47, 0x40, 0x55, 0x44, 0x9f, 0x42, 0x47, 0x4e, 0x52, 0x44, 0x4d, 0x1f, 0x4c, 0x58, 0x1f, 0x4e, 0x56, 0x4d, 0x9f, 0x4f, 0x40, 0x51, 0x53, 0x48, 0x42, 0x54, 0x4b, 0x40, 0x51, 0x9f, 0x4f, 0x51, 0x4e, 0x45, 0x44, 0x52, 0x52, 0x48, 0x4e, 0x4d, 0x1d, 0x1f, 0x4e, 0x51, 0x9f, 0x51, 0x40, 0x53, 0x47, 0x44, 0x51, 0x1f, 0x42, 0x51, 0x44, 0x40, 0x53, 0x44, 0x43, 0x1f, 0x48, 0x53, 0x9d, ], [0x45, 0x4e, 0x51, 0x1f, 0x8, 0x1f, 0x40, 0x4c, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x4e, 0x4d, 0x4b, 0x58, 0x9f, 0x4e, 0x4d, 0x44, 0x1f, 0x48, 0x4d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x56, 0x4e, 0x51, 0x4b, 0x43, 0x1b, 0xdd, 0x1c, 0x3, 0x4e, 0x58, 0x4b, 0x44, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, ]], -[[0xb, 0x48, 0x45, 0x44, 0x1f, 0x48, 0x52, 0x1f, 0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x4b, 0xd8, 0x52, 0x53, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x40, 0x4d, 0x9f, 0x40, 0x4d, 0x58, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x1f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x53, 0x47, 0xc4, 0x4c, 0x48, 0x4d, 0x43, 0x1f, 0x4e, 0x45, 0x1f, 0x4c, 0x40, 0x4d, 0x1f, 0x42, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x48, 0x4d, 0x55, 0x44, 0x4d, 0x53, 0x1b, 0x1f, 0x16, 0x44, 0x1f, 0x56, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x4d, 0x4e, 0x53, 0x1f, 0x43, 0x40, 0x51, 0x44, 0x1f, 0x53, 0x4e, 0x9f, 0x42, 0x4e, 0x4d, 0x42, 0x44, 0x48, 0x55, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x52, 0x1f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x40, 0x51, 0x44, 0x9f, ], [0x51, 0x44, 0x40, 0x4b, 0x4b, 0x58, 0x1f, 0x4c, 0x44, 0x51, 0x44, 0x9f, 0x42, 0x4e, 0x4c, 0x4c, 0x4e, 0x4d, 0x4f, 0x4b, 0x40, 0x42, 0x44, 0x52, 0x1f, 0x4e, 0x45, 0x9f, 0x44, 0x57, 0x48, 0x52, 0x53, 0x44, 0x4d, 0x42, 0x44, 0x1b, 0x1f, 0x8, 0x45, 0x1f, 0x56, 0x44, 0x9f, 0x42, 0x4e, 0x54, 0x4b, 0x43, 0x1f, 0x45, 0x4b, 0x58, 0x1f, 0x4e, 0x54, 0x53, 0x1f, 0x4e, 0x45, 0x9f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x56, 0x48, 0x4d, 0x43, 0x4e, 0x56, 0x1f, 0x47, 0x40, 0x4d, 0x43, 0x9f, 0x48, 0x4d, 0x1f, 0x47, 0x40, 0x4d, 0x43, 0x1d, 0x1f, 0x47, 0x4e, 0x55, 0x44, 0x51, 0x9f, 0x4e, 0x55, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x48, 0x52, 0x1f, 0x46, 0x51, 0x44, 0x40, 0x53, 0x9f, 0x42, 0x48, 0x53, 0x58, 0x1d, 0x1f, 0x46, 0x44, 0x4d, 0x53, 0x4b, 0x58, 0x9f, ], [0x51, 0x44, 0x4c, 0x4e, 0x55, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x51, 0x4e, 0x4e, 0x45, 0x52, 0x1d, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x40, 0x4d, 0x43, 0x1f, 0x4f, 0x44, 0x44, 0x4f, 0x1f, 0x48, 0x4d, 0x1f, 0x40, 0xd3, 0x53, 0x47, 0x44, 0x1f, 0x50, 0x54, 0x44, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x52, 0x9f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x40, 0x51, 0x44, 0x1f, 0x46, 0x4e, 0x48, 0x4d, 0x46, 0x9f, 0x4e, 0x4d, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x52, 0x53, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x9f, 0x42, 0x4e, 0x48, 0x4d, 0x42, 0x48, 0x43, 0x44, 0x4d, 0x42, 0x44, 0x52, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4f, 0x4b, 0x40, 0x4d, 0x4d, 0x48, 0x4d, 0x46, 0x52, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x42, 0x51, 0x4e, 0x52, 0x52, 0x1c, 0x4f, 0x54, 0x51, 0x4f, 0x4e, 0x52, 0x44, 0x52, 0x1d, 0x9f, ], [0x53, 0x47, 0x44, 0x1f, 0x56, 0x4e, 0x4d, 0x43, 0x44, 0x51, 0x45, 0x54, 0x4b, 0x9f, 0x42, 0x47, 0x40, 0x48, 0x4d, 0x52, 0x1f, 0x4e, 0x45, 0x1f, 0x44, 0x55, 0x44, 0x4d, 0x53, 0x52, 0x1d, 0x9f, 0x56, 0x4e, 0x51, 0x4a, 0x48, 0x4d, 0x46, 0x1f, 0x53, 0x47, 0x51, 0x4e, 0x54, 0x46, 0x47, 0x9f, 0x46, 0x44, 0x4d, 0x44, 0x51, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x52, 0x1d, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x4b, 0x44, 0x40, 0x43, 0x48, 0x4d, 0x46, 0x1f, 0x53, 0x4e, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4c, 0x4e, 0x52, 0x53, 0x1f, 0x4e, 0x54, 0x53, 0x51, 0x44, 0x9f, 0x51, 0x44, 0x52, 0x54, 0x4b, 0x53, 0x52, 0x1d, 0x1f, 0x48, 0x53, 0x1f, 0x56, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x4c, 0x40, 0x4a, 0x44, 0x1f, 0x40, 0x4b, 0x4b, 0x1f, 0x45, 0x48, 0x42, 0x53, 0x48, 0x4e, 0x4d, 0x9f, ], [0x56, 0x48, 0x53, 0x47, 0x1f, 0x48, 0x53, 0x52, 0x9f, 0x42, 0x4e, 0x4d, 0x55, 0x44, 0x4d, 0x53, 0x48, 0x4e, 0x4d, 0x40, 0x4b, 0x48, 0x53, 0x48, 0x44, 0x52, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x45, 0x4e, 0x51, 0x44, 0x52, 0x44, 0x44, 0x4d, 0x9f, 0x42, 0x4e, 0x4d, 0x42, 0x4b, 0x54, 0x52, 0x48, 0x4e, 0x4d, 0x52, 0x1f, 0x4c, 0x4e, 0x52, 0x53, 0x9f, 0x52, 0x53, 0x40, 0x4b, 0x44, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x54, 0x4d, 0x4f, 0x51, 0x4e, 0x45, 0x48, 0x53, 0x40, 0x41, 0x4b, 0x44, 0x1b, 0x9f, 0x1c, 0x3, 0x4e, 0x58, 0x4b, 0x44, 0x1f, 0x9f, 0x9f, ]], +[[0xb, 0x48, 0x45, 0x44, 0x1f, 0x48, 0x52, 0x1f, 0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x4b, 0xd8, 0x52, 0x53, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x40, 0x4d, 0x9f, 0x40, 0x4d, 0x58, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x1f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x53, 0x47, 0xc4, 0x4c, 0x48, 0x4d, 0x43, 0x1f, 0x4e, 0x45, 0x1f, 0x4c, 0x40, 0x4d, 0x1f, 0x42, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x48, 0x4d, 0x55, 0x44, 0x4d, 0x53, 0x1b, 0x1f, 0x16, 0x44, 0x1f, 0x56, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x4d, 0x4e, 0x53, 0x1f, 0x43, 0x40, 0x51, 0x44, 0x1f, 0x53, 0x4e, 0x9f, 0x42, 0x4e, 0x4d, 0x42, 0x44, 0x48, 0x55, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x52, 0x1f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x40, 0x51, 0x44, 0x9f, ], [0x51, 0x44, 0x40, 0x4b, 0x4b, 0x58, 0x1f, 0x4c, 0x44, 0x51, 0x44, 0x9f, 0x42, 0x4e, 0x4c, 0x4c, 0x4e, 0x4d, 0x4f, 0x4b, 0x40, 0x42, 0x44, 0x52, 0x1f, 0x4e, 0x45, 0x9f, 0x44, 0x57, 0x48, 0x52, 0x53, 0x44, 0x4d, 0x42, 0x44, 0x1b, 0x1f, 0x8, 0x45, 0x1f, 0x56, 0x44, 0x9f, 0x42, 0x4e, 0x54, 0x4b, 0x43, 0x1f, 0x45, 0x4b, 0x58, 0x1f, 0x4e, 0x54, 0x53, 0x1f, 0x4e, 0x45, 0x9f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x56, 0x48, 0x4d, 0x43, 0x4e, 0x56, 0x1f, 0x47, 0x40, 0x4d, 0x43, 0x9f, 0x48, 0x4d, 0x1f, 0x47, 0x40, 0x4d, 0x43, 0x1d, 0x1f, 0x47, 0x4e, 0x55, 0x44, 0x51, 0x9f, 0x4e, 0x55, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x48, 0x52, 0x1f, 0x46, 0x51, 0x44, 0x40, 0x53, 0x9f, 0x42, 0x48, 0x53, 0x58, 0x1d, 0x1f, 0x46, 0x44, 0x4d, 0x53, 0x4b, 0x58, 0x9f, ], [0x51, 0x44, 0x4c, 0x4e, 0x55, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x51, 0x4e, 0x4e, 0x45, 0x52, 0x1d, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x4f, 0x44, 0x44, 0x4f, 0x1f, 0x48, 0x4d, 0x1f, 0x40, 0xd3, 0x53, 0x47, 0x44, 0x1f, 0x50, 0x54, 0x44, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x52, 0x9f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x40, 0x51, 0x44, 0x1f, 0x46, 0x4e, 0x48, 0x4d, 0x46, 0x9f, 0x4e, 0x4d, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x52, 0x53, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x9f, 0x42, 0x4e, 0x48, 0x4d, 0x42, 0x48, 0x43, 0x44, 0x4d, 0x42, 0x44, 0x52, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4f, 0x4b, 0x40, 0x4d, 0x4d, 0x48, 0x4d, 0x46, 0x52, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x42, 0x51, 0x4e, 0x52, 0x52, 0x1c, 0x4f, 0x54, 0x51, 0x4f, 0x4e, 0x52, 0x44, 0x52, 0x1d, 0x9f, ], [0x53, 0x47, 0x44, 0x1f, 0x56, 0x4e, 0x4d, 0x43, 0x44, 0x51, 0x45, 0x54, 0x4b, 0x9f, 0x42, 0x47, 0x40, 0x48, 0x4d, 0x52, 0x1f, 0x4e, 0x45, 0x1f, 0x44, 0x55, 0x44, 0x4d, 0x53, 0x52, 0x1d, 0x9f, 0x56, 0x4e, 0x51, 0x4a, 0x48, 0x4d, 0x46, 0x1f, 0x53, 0x47, 0x51, 0x4e, 0x54, 0x46, 0x47, 0x9f, 0x46, 0x44, 0x4d, 0x44, 0x51, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x52, 0x1d, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x4b, 0x44, 0x40, 0x43, 0x48, 0x4d, 0x46, 0x1f, 0x53, 0x4e, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4c, 0x4e, 0x52, 0x53, 0x1f, 0x4e, 0x54, 0x53, 0x51, 0x44, 0x9f, 0x51, 0x44, 0x52, 0x54, 0x4b, 0x53, 0x52, 0x1d, 0x1f, 0x48, 0x53, 0x1f, 0x56, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x4c, 0x40, 0x4a, 0x44, 0x1f, 0x40, 0x4b, 0x4b, 0x1f, 0x45, 0x48, 0x42, 0x53, 0x48, 0x4e, 0x4d, 0x9f, ], [0x56, 0x48, 0x53, 0x47, 0x1f, 0x48, 0x53, 0x52, 0x9f, 0x42, 0x4e, 0x4d, 0x55, 0x44, 0x4d, 0x53, 0x48, 0x4e, 0x4d, 0x40, 0x4b, 0x48, 0x53, 0x48, 0x44, 0x52, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x45, 0x4e, 0x51, 0x44, 0x52, 0x44, 0x44, 0x4d, 0x9f, 0x42, 0x4e, 0x4d, 0x42, 0x4b, 0x54, 0x52, 0x48, 0x4e, 0x4d, 0x52, 0x1f, 0x4c, 0x4e, 0x52, 0x53, 0x9f, 0x52, 0x53, 0x40, 0x4b, 0x44, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x54, 0x4d, 0x4f, 0x51, 0x4e, 0x45, 0x48, 0x53, 0x40, 0x41, 0x4b, 0x44, 0x1b, 0x9f, 0x1c, 0x3, 0x4e, 0x58, 0x4b, 0x44, 0x1f, 0x9f, 0x9f, ]], [[0x13, 0x47, 0x44, 0x1f, 0x52, 0x53, 0x4e, 0x51, 0x58, 0x1f, 0x52, 0x4e, 0x1f, 0x45, 0x40, 0x51, 0x1b, 0x9f, 0x8, 0x4d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x41, 0x44, 0x46, 0x48, 0x4d, 0x4d, 0x48, 0x4d, 0x46, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x14, 0x4d, 0x48, 0x55, 0x44, 0x51, 0x52, 0x44, 0x1f, 0x56, 0x40, 0x52, 0x9f, 0x42, 0x51, 0x44, 0x40, 0x53, 0x44, 0x43, 0x1b, 0x1f, 0x13, 0x47, 0x48, 0x52, 0x1f, 0x47, 0x40, 0x52, 0x9f, 0x4c, 0x40, 0x43, 0x44, 0x1f, 0x40, 0x1f, 0x4b, 0x4e, 0x53, 0x1f, 0x4e, 0x45, 0x9f, 0x4f, 0x44, 0x4e, 0x4f, 0x4b, 0x44, 0x1f, 0x55, 0x44, 0x51, 0x58, 0x1f, 0x40, 0x4d, 0x46, 0x51, 0x58, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x41, 0x44, 0x44, 0x4d, 0x1f, 0x56, 0x48, 0x43, 0x44, 0x4b, 0x58, 0x9f, 0x51, 0x44, 0x46, 0x40, 0x51, 0x43, 0x44, 0x43, 0x1f, 0x40, 0x52, 0x1f, 0x40, 0x1f, 0x41, 0x40, 0x43, 0x9f, ], [0x4c, 0x4e, 0x55, 0x44, 0x1b, 0x1f, 0x1c, 0x0, 0x43, 0x40, 0x4c, 0x52, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, ]], [[0x5, 0x4e, 0x51, 0x1f, 0x48, 0x4d, 0x52, 0x53, 0x40, 0x4d, 0x42, 0x44, 0x1d, 0x1f, 0x4e, 0x4d, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x4f, 0x4b, 0x40, 0x4d, 0x44, 0x53, 0x1f, 0x4, 0x40, 0x51, 0x53, 0x47, 0x1d, 0x9f, 0x4c, 0x40, 0x4d, 0x1f, 0x47, 0x40, 0x43, 0x1f, 0x40, 0x4b, 0x56, 0x40, 0x58, 0x52, 0x9f, 0x40, 0x52, 0x52, 0x54, 0x4c, 0x44, 0x43, 0x1f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x47, 0x44, 0x9f, 0x56, 0x40, 0x52, 0x1f, 0x4c, 0x4e, 0x51, 0x44, 0x9f, 0x48, 0x4d, 0x53, 0x44, 0x4b, 0x4b, 0x48, 0x46, 0x44, 0x4d, 0x53, 0x1f, 0x53, 0x47, 0x40, 0x4d, 0x9f, 0x43, 0x4e, 0x4b, 0x4f, 0x47, 0x48, 0x4d, 0x52, 0x1f, 0x41, 0x44, 0x42, 0x40, 0x54, 0x52, 0x44, 0x9f, 0x47, 0x44, 0x1f, 0x47, 0x40, 0x43, 0x1f, 0x40, 0x42, 0x47, 0x48, 0x44, 0x55, 0x44, 0x43, 0x1f, 0x52, 0xce, ], [0x4c, 0x54, 0x42, 0x47, 0x1c, 0x53, 0x47, 0x44, 0x1f, 0x56, 0x47, 0x44, 0x44, 0x4b, 0x1d, 0x9f, 0xd, 0x44, 0x56, 0x1f, 0x18, 0x4e, 0x51, 0x4a, 0x1d, 0x1f, 0x56, 0x40, 0x51, 0x52, 0x1f, 0x40, 0x4d, 0xc3, 0x52, 0x4e, 0x1f, 0x4e, 0x4d, 0x1c, 0x56, 0x47, 0x48, 0x4b, 0x52, 0x53, 0x1f, 0x40, 0x4b, 0x4b, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x43, 0x4e, 0x4b, 0x4f, 0x47, 0x48, 0x4d, 0x52, 0x1f, 0x47, 0x40, 0x43, 0x9f, 0x44, 0x55, 0x44, 0x51, 0x1f, 0x43, 0x4e, 0x4d, 0x44, 0x1f, 0x56, 0x40, 0x52, 0x1f, 0x4c, 0x54, 0x42, 0xca, 0x40, 0x41, 0x4e, 0x54, 0x53, 0x1f, 0x48, 0x4d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x56, 0x40, 0x53, 0x44, 0xd1, 0x47, 0x40, 0x55, 0x48, 0x4d, 0x46, 0x1f, 0x40, 0x1f, 0x46, 0x4e, 0x4e, 0x43, 0x9f, 0x53, 0x48, 0x4c, 0x44, 0x1b, 0x1f, 0x1, 0x54, 0x53, 0x9f, ], [0x42, 0x4e, 0x4d, 0x55, 0x44, 0x51, 0x52, 0x44, 0x4b, 0x58, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x43, 0x4e, 0x4b, 0x4f, 0x47, 0x48, 0x4d, 0x52, 0x1f, 0x47, 0x40, 0x43, 0x9f, 0x40, 0x4b, 0x56, 0x40, 0x58, 0x52, 0x1f, 0x41, 0x44, 0x4b, 0x48, 0x44, 0x55, 0x44, 0x43, 0x9f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x53, 0x47, 0x44, 0x58, 0x1f, 0x56, 0x44, 0x51, 0x44, 0x1f, 0x45, 0x40, 0xd1, 0x4c, 0x4e, 0x51, 0x44, 0x1f, 0x48, 0x4d, 0x53, 0x44, 0x4b, 0x4b, 0x48, 0x46, 0x44, 0x4d, 0x53, 0x9f, 0x53, 0x47, 0x40, 0x4d, 0x1f, 0x4c, 0x40, 0x4d, 0x1b, 0x45, 0x4e, 0x51, 0x9f, 0x4f, 0x51, 0x44, 0x42, 0x48, 0x52, 0x44, 0x4b, 0x58, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x52, 0x40, 0x4c, 0xc4, 0x51, 0x44, 0x40, 0x52, 0x4e, 0x4d, 0x52, 0x1b, 0x1f, 0x1c, 0x0, 0x43, 0x40, 0x4c, 0x52, 0x1f, 0x9f, ]], [[0x8, 0x53, 0x1f, 0x48, 0x52, 0x1f, 0x4a, 0x4d, 0x4e, 0x56, 0x4d, 0x1f, 0x53, 0x47, 0x40, 0x53, 0x9f, 0x53, 0x47, 0x44, 0x51, 0x44, 0x1f, 0x40, 0x51, 0x44, 0x1f, 0x40, 0x4d, 0x9f, 0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x1f, 0x4d, 0x54, 0x4c, 0x41, 0x44, 0x51, 0x1f, 0x4e, 0xc5, 0x56, 0x4e, 0x51, 0x4b, 0x43, 0x52, 0x1d, 0x1f, 0x52, 0x48, 0x4c, 0x4f, 0x4b, 0x58, 0x9f, 0x41, 0x44, 0x42, 0x40, 0x54, 0x52, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x51, 0x44, 0x1f, 0x48, 0x52, 0x9f, 0x40, 0x4d, 0x1f, 0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x1f, 0x40, 0x4c, 0x4e, 0x54, 0x4d, 0xd3, 0x4e, 0x45, 0x1f, 0x52, 0x4f, 0x40, 0x42, 0x44, 0x1f, 0x45, 0x4e, 0x51, 0x1f, 0x53, 0x47, 0x44, 0x4c, 0x9f, 0x53, 0x4e, 0x1f, 0x41, 0x44, 0x1f, 0x48, 0x4d, 0x1b, 0x1f, 0x7, 0x4e, 0x56, 0x44, 0x55, 0x44, 0x51, 0x9d, ], [0x4d, 0x4e, 0x53, 0x1f, 0x44, 0x55, 0x44, 0x51, 0x58, 0x1f, 0x4e, 0x4d, 0x44, 0x1f, 0x4e, 0x45, 0x9f, 0x53, 0x47, 0x44, 0x4c, 0x1f, 0x48, 0x52, 0x1f, 0x48, 0x4d, 0x47, 0x40, 0x41, 0x48, 0x53, 0x44, 0x43, 0x9b, 0x13, 0x47, 0x44, 0x51, 0x44, 0x45, 0x4e, 0x51, 0x44, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x51, 0x44, 0x9f, 0x4c, 0x54, 0x52, 0x53, 0x1f, 0x41, 0x44, 0x1f, 0x40, 0x1f, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x9f, 0x4d, 0x54, 0x4c, 0x41, 0x44, 0x51, 0x1f, 0x4e, 0x45, 0x9f, 0x48, 0x4d, 0x47, 0x40, 0x41, 0x48, 0x53, 0x44, 0x43, 0x1f, 0x56, 0x4e, 0x51, 0x4b, 0x43, 0x52, 0x1b, 0x9f, 0x0, 0x4d, 0x58, 0x1f, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x1f, 0x4d, 0x54, 0x4c, 0x41, 0x44, 0x51, 0x9f, 0x43, 0x48, 0x55, 0x48, 0x43, 0x44, 0x43, 0x1f, 0x41, 0x58, 0x9f, ], [0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x58, 0x1f, 0x48, 0x52, 0x1f, 0x40, 0x52, 0x9f, 0x4d, 0x44, 0x40, 0x51, 0x1f, 0x53, 0x4e, 0x1f, 0x4d, 0x4e, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x1f, 0x40, 0xd2, 0x4c, 0x40, 0x4a, 0x44, 0x52, 0x1f, 0x4d, 0x4e, 0x1f, 0x4e, 0x43, 0x43, 0x52, 0x1d, 0x1f, 0x52, 0x4e, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x40, 0x55, 0x44, 0x51, 0x40, 0x46, 0x44, 0x9f, 0x4f, 0x4e, 0x4f, 0x54, 0x4b, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1f, 0x4e, 0x45, 0x1f, 0x40, 0x4b, 0x4b, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x4f, 0x4b, 0x40, 0x4d, 0x44, 0x53, 0x52, 0x1f, 0x48, 0x4d, 0x1f, 0x53, 0x47, 0xc4, 0x14, 0x4d, 0x48, 0x55, 0x44, 0x51, 0x52, 0x44, 0x1f, 0x42, 0x40, 0x4d, 0x1f, 0x41, 0x44, 0x9f, 0x52, 0x40, 0x48, 0x43, 0x1f, 0x53, 0x4e, 0x1f, 0x41, 0x44, 0x1f, 0x59, 0x44, 0x51, 0x4e, 0x1b, 0x9f, ], [0x5, 0x51, 0x4e, 0x4c, 0x1f, 0x53, 0x47, 0x48, 0x52, 0x1f, 0x48, 0x53, 0x9f, 0x45, 0x4e, 0x4b, 0x4b, 0x4e, 0x56, 0x52, 0x1f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4f, 0x4e, 0x4f, 0x54, 0x4b, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1f, 0x4e, 0x45, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x56, 0x47, 0x4e, 0x4b, 0x44, 0x1f, 0x14, 0x4d, 0x48, 0x55, 0x44, 0x51, 0x52, 0x44, 0x1f, 0x48, 0x52, 0x9f, 0x40, 0x4b, 0x52, 0x4e, 0x1f, 0x59, 0x44, 0x51, 0x4e, 0x1d, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x40, 0x4d, 0x58, 0x1f, 0x4f, 0x44, 0x4e, 0x4f, 0x4b, 0x44, 0x9f, 0x58, 0x4e, 0x54, 0x1f, 0x4c, 0x40, 0x58, 0x1f, 0x4c, 0x44, 0x44, 0x53, 0x1f, 0x45, 0x51, 0x4e, 0x4c, 0x9f, 0x53, 0x48, 0x4c, 0x44, 0x1f, 0x53, 0x4e, 0x1f, 0x53, 0x48, 0x4c, 0x44, 0x1f, 0x40, 0x51, 0x44, 0x9f, ], [0x4c, 0x44, 0x51, 0x44, 0x4b, 0x58, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4f, 0x51, 0x4e, 0x43, 0x54, 0x42, 0x53, 0x52, 0x1f, 0x4e, 0x45, 0x1f, 0x40, 0x9f, 0x43, 0x44, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x43, 0x9f, 0x48, 0x4c, 0x40, 0x46, 0x48, 0x4d, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1b, 0x9f, 0x1c, 0x0, 0x43, 0x40, 0x4c, 0x52, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, ]], diff --git a/worlds/smw/Options.py b/worlds/smw/Options.py index 65ecbbd771..a9416b633d 100644 --- a/worlds/smw/Options.py +++ b/worlds/smw/Options.py @@ -58,7 +58,7 @@ class BowserCastleDoors(Choice): Vanilla: Front and Back Doors behave as vanilla Fast: Both doors behave as the Back Door Slow: Both doors behave as the Front Door - "Front Door" requires beating all 8 Rooms + "Front Door" rooms depend on the `bowser_castle_rooms` option "Back Door" only requires going through the dark hallway to Bowser """ display_name = "Bowser Castle Doors" @@ -68,6 +68,40 @@ class BowserCastleDoors(Choice): default = 0 +class BowserCastleRooms(Choice): + """ + How the rooms of Bowser's Castle Front Door behave + Vanilla: You can choose which rooms to enter, as in vanilla + Random Two Room: Two random rooms are chosen + Random Five Room: Five random rooms are chosen + Gauntlet: All eight rooms must be cleared + Labyrinth: Which room leads to Bowser? + """ + display_name = "Bowser Castle Rooms" + option_vanilla = 0 + option_random_two_room = 1 + option_random_five_room = 2 + option_gauntlet = 3 + option_labyrinth = 4 + default = 1 + + +class BossShuffle(Choice): + """ + How the rooms of Bowser's Castle Front Door behave + None: Bosses are not shuffled + Simple: Four Reznors and the seven Koopalings are shuffled around + Full: Each boss location gets a fully random boss + Singularity: One or two bosses are chosen and placed at every boss location + """ + display_name = "Boss Shuffle" + option_none = 0 + option_simple = 1 + option_full = 2 + option_singularity = 3 + default = 0 + + class LevelShuffle(Toggle): """ Whether levels are shuffled @@ -75,6 +109,14 @@ class LevelShuffle(Toggle): display_name = "Level Shuffle" +class ExcludeSpecialZone(Toggle): + """ + If active, this option will prevent any progression items from being placed in Special Zone levels. + Additionally, if Level Shuffle is active, Special Zone levels will not be shuffled away from their vanilla tiles. + """ + display_name = "Exclude Special Zone" + + class SwapDonutGhostHouseExits(Toggle): """ If enabled, this option will swap which overworld direction the two exits of the level at the Donut Ghost House @@ -148,6 +190,13 @@ class LiteratureTrapWeight(BaseTrapWeight): display_name = "Literature Trap Weight" +class TimerTrapWeight(BaseTrapWeight): + """ + Likelihood of a receiving a trap which causes the timer to run low + """ + display_name = "Timer Trap Weight" + + class Autosave(DefaultOnToggle): """ Whether a save prompt will appear after every level @@ -155,6 +204,25 @@ class Autosave(DefaultOnToggle): display_name = "Autosave" +class EarlyClimb(Toggle): + """ + Force Climb to appear early in the seed as a local item. + This is particularly useful to prevent BK when Level Shuffle is disabled + """ + display_name = "Early Climb" + + +class OverworldSpeed(Choice): + """ + How fast Mario moves on the overworld + """ + display_name = "Overworld Speed" + option_slow = 0 + option_vanilla = 1 + option_fast = 2 + default = 1 + + class MusicShuffle(Choice): """ Music shuffle type @@ -201,6 +269,13 @@ class BackgroundPaletteShuffle(Toggle): display_name = "Background Palette Shuffle" +class OverworldPaletteShuffle(Toggle): + """ + Whether to shuffle overworld palettes + """ + display_name = "Overworld Palette Shuffle" + + class StartingLifeCount(Range): """ How many extra lives to start the game with @@ -220,7 +295,10 @@ smw_options: typing.Dict[str, type(Option)] = { "percentage_of_yoshi_eggs": PercentageOfYoshiEggs, "dragon_coin_checks": DragonCoinChecks, "bowser_castle_doors": BowserCastleDoors, + "bowser_castle_rooms": BowserCastleRooms, "level_shuffle": LevelShuffle, + "exclude_special_zone": ExcludeSpecialZone, + "boss_shuffle": BossShuffle, "swap_donut_gh_exits": SwapDonutGhostHouseExits, #"display_sent_item_popups": DisplaySentItemPopups, "display_received_item_popups": DisplayReceivedItemPopups, @@ -228,10 +306,14 @@ smw_options: typing.Dict[str, type(Option)] = { "ice_trap_weight": IceTrapWeight, "stun_trap_weight": StunTrapWeight, "literature_trap_weight": LiteratureTrapWeight, + "timer_trap_weight": TimerTrapWeight, "autosave": Autosave, + "early_climb": EarlyClimb, + "overworld_speed": OverworldSpeed, "music_shuffle": MusicShuffle, "mario_palette": MarioPalette, "foreground_palette_shuffle": ForegroundPaletteShuffle, "background_palette_shuffle": BackgroundPaletteShuffle, + "overworld_palette_shuffle": OverworldPaletteShuffle, "starting_life_count": StartingLifeCount, } diff --git a/worlds/smw/Regions.py b/worlds/smw/Regions.py index 052f0f0137..fd94e0b168 100644 --- a/worlds/smw/Regions.py +++ b/worlds/smw/Regions.py @@ -11,14 +11,6 @@ def create_regions(world, player: int, active_locations): menu_region = create_region(world, player, active_locations, 'Menu', None) yoshis_island_region = create_region(world, player, active_locations, LocationName.yoshis_island_region, None) - donut_plains_region = create_region(world, player, active_locations, LocationName.donut_plains_region, None) - vanilla_dome_region = create_region(world, player, active_locations, LocationName.vanilla_dome_region, None) - twin_bridges_region = create_region(world, player, active_locations, LocationName.twin_bridges_region, None) - forest_of_illusion_region = create_region(world, player, active_locations, LocationName.forest_of_illusion_region, None) - chocolate_island_region = create_region(world, player, active_locations, LocationName.chocolate_island_region, None) - valley_of_bowser_region = create_region(world, player, active_locations, LocationName.valley_of_bowser_region, None) - star_road_region = create_region(world, player, active_locations, LocationName.star_road_region, None) - special_zone_region = create_region(world, player, active_locations, LocationName.special_zone_region, None) yoshis_house_tile = create_region(world, player, active_locations, LocationName.yoshis_house_tile, None) @@ -472,14 +464,6 @@ def create_regions(world, player: int, active_locations): world.regions += [ menu_region, yoshis_island_region, - donut_plains_region, - vanilla_dome_region, - twin_bridges_region, - forest_of_illusion_region, - chocolate_island_region, - valley_of_bowser_region, - star_road_region, - special_zone_region, yoshis_house_tile, yoshis_house_region, yoshis_island_1_tile, @@ -824,7 +808,7 @@ def create_regions(world, player: int, active_locations): (state.has(ItemName.yellow_switch_palace, player) or state.has(ItemName.red_switch_palace, player))))) add_location_to_region(world, player, active_locations, LocationName.chocolate_island_3_region, LocationName.chocolate_island_3_dragon) add_location_to_region(world, player, active_locations, LocationName.chocolate_island_4_region, LocationName.chocolate_island_4_dragon, - lambda state: (state.has(ItemName.mario_run, player) and + lambda state: (state.has(ItemName.p_switch, player) and state.has(ItemName.progressive_powerup, player, 3))) add_location_to_region(world, player, active_locations, LocationName.chocolate_island_5_region, LocationName.chocolate_island_5_dragon, lambda state: (state.has(ItemName.mario_swim, player) or @@ -866,6 +850,8 @@ def connect_regions(world, player, level_to_tile_dict): names: typing.Dict[str, int] = {} connect(world, player, names, "Menu", LocationName.yoshis_island_region) + connect(world, player, names, LocationName.yoshis_island_region, LocationName.yoshis_house_tile) + connect(world, player, names, LocationName.yoshis_house_tile, LocationName.donut_plains_top_secret) connect(world, player, names, LocationName.yoshis_island_region, LocationName.yoshis_island_1_tile) connect(world, player, names, LocationName.yoshis_island_region, LocationName.yoshis_island_2_tile) diff --git a/worlds/smw/Rom.py b/worlds/smw/Rom.py index 4641141c38..d39fcc46b9 100644 --- a/worlds/smw/Rom.py +++ b/worlds/smw/Rom.py @@ -1,7 +1,7 @@ import Utils from worlds.Files import APDeltaPatch -from .Aesthetics import generate_shuffled_header_data -from .Levels import level_info_dict +from .Aesthetics import generate_shuffled_header_data, generate_shuffled_ow_palettes +from .Levels import level_info_dict, full_bowser_rooms, standard_bowser_rooms, submap_boss_rooms, ow_boss_rooms from .Names.TextBox import generate_goal_text, title_text_mapping, generate_text_box USHASH = 'cdd3c8c37322978ca8669b34bc89c804' @@ -36,18 +36,13 @@ item_rom_data = { 0xBC000F: [0x1F27, 0x1, 0x1C], # Green Switch Palace 0xBC0010: [0x1F2A, 0x1, 0x1C], # Red Switch Palace 0xBC0011: [0x1F29, 0x1, 0x1C], # Blue Switch Palace - - 0xBC0013: [0x0086, 0x1, 0x0E], # Ice Trap - 0xBC0014: [0x18BD, 0x7F, 0x18], # Stun Trap } -music_rom_data = [ - -] - -level_music_ids = [ - -] +trap_rom_data = { + 0xBC0013: [0x0086, 0x1, 0x0E], # Ice Trap + 0xBC0014: [0x18BD, 0x7F, 0x18], # Stun Trap + 0xBC0016: [0x0F31, 0x1], # Timer Trap +} class SMWDeltaPatch(APDeltaPatch): @@ -728,6 +723,103 @@ def handle_swap_donut_gh_exits(rom): rom.write_bytes(0x26371, bytes([0x32])) +def handle_bowser_rooms(rom, world, player): + if world.bowser_castle_rooms[player] == "random_two_room": + chosen_rooms = world.random.sample(standard_bowser_rooms, 2) + + rom.write_byte(0x3A680, chosen_rooms[0].roomID) + rom.write_byte(0x3A684, chosen_rooms[0].roomID) + rom.write_byte(0x3A688, chosen_rooms[0].roomID) + rom.write_byte(0x3A68C, chosen_rooms[0].roomID) + + for i in range(1, len(chosen_rooms)): + rom.write_byte(chosen_rooms[i-1].exitAddress, chosen_rooms[i].roomID) + + rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD) + + elif world.bowser_castle_rooms[player] == "random_five_room": + chosen_rooms = world.random.sample(standard_bowser_rooms, 5) + + rom.write_byte(0x3A680, chosen_rooms[0].roomID) + rom.write_byte(0x3A684, chosen_rooms[0].roomID) + rom.write_byte(0x3A688, chosen_rooms[0].roomID) + rom.write_byte(0x3A68C, chosen_rooms[0].roomID) + + for i in range(1, len(chosen_rooms)): + rom.write_byte(chosen_rooms[i-1].exitAddress, chosen_rooms[i].roomID) + + rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD) + + elif world.bowser_castle_rooms[player] == "gauntlet": + chosen_rooms = standard_bowser_rooms.copy() + world.random.shuffle(chosen_rooms) + + rom.write_byte(0x3A680, chosen_rooms[0].roomID) + rom.write_byte(0x3A684, chosen_rooms[0].roomID) + rom.write_byte(0x3A688, chosen_rooms[0].roomID) + rom.write_byte(0x3A68C, chosen_rooms[0].roomID) + + for i in range(1, len(chosen_rooms)): + rom.write_byte(chosen_rooms[i-1].exitAddress, chosen_rooms[i].roomID) + + rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD) + elif world.bowser_castle_rooms[player] == "labyrinth": + bowser_rooms_copy = full_bowser_rooms.copy() + + entrance_point = bowser_rooms_copy.pop(0) + + world.random.shuffle(bowser_rooms_copy) + + rom.write_byte(entrance_point.exitAddress, bowser_rooms_copy[0].roomID) + for i in range(0, len(bowser_rooms_copy) - 1): + rom.write_byte(bowser_rooms_copy[i].exitAddress, bowser_rooms_copy[i+1].roomID) + + rom.write_byte(bowser_rooms_copy[len(bowser_rooms_copy)-1].exitAddress, 0xBD) + + +def handle_boss_shuffle(rom, world, player): + if world.boss_shuffle[player] == "simple": + submap_boss_rooms_copy = submap_boss_rooms.copy() + ow_boss_rooms_copy = ow_boss_rooms.copy() + + world.random.shuffle(submap_boss_rooms_copy) + world.random.shuffle(ow_boss_rooms_copy) + + for i in range(len(submap_boss_rooms_copy)): + rom.write_byte(submap_boss_rooms[i].exitAddress, submap_boss_rooms_copy[i].roomID) + + for i in range(len(ow_boss_rooms_copy)): + rom.write_byte(ow_boss_rooms[i].exitAddress, ow_boss_rooms_copy[i].roomID) + + if ow_boss_rooms[i].exitAddressAlt is not None: + rom.write_byte(ow_boss_rooms[i].exitAddressAlt, ow_boss_rooms_copy[i].roomID) + + elif world.boss_shuffle[player] == "full": + for i in range(len(submap_boss_rooms)): + chosen_boss = world.random.choice(submap_boss_rooms) + rom.write_byte(submap_boss_rooms[i].exitAddress, chosen_boss.roomID) + + for i in range(len(ow_boss_rooms)): + chosen_boss = world.random.choice(ow_boss_rooms) + rom.write_byte(ow_boss_rooms[i].exitAddress, chosen_boss.roomID) + + if ow_boss_rooms[i].exitAddressAlt is not None: + rom.write_byte(ow_boss_rooms[i].exitAddressAlt, chosen_boss.roomID) + + elif world.boss_shuffle[player] == "singularity": + chosen_submap_boss = world.random.choice(submap_boss_rooms) + chosen_ow_boss = world.random.choice(ow_boss_rooms) + + for i in range(len(submap_boss_rooms)): + rom.write_byte(submap_boss_rooms[i].exitAddress, chosen_submap_boss.roomID) + + for i in range(len(ow_boss_rooms)): + rom.write_byte(ow_boss_rooms[i].exitAddress, chosen_ow_boss.roomID) + + if ow_boss_rooms[i].exitAddressAlt is not None: + rom.write_byte(ow_boss_rooms[i].exitAddressAlt, chosen_ow_boss.roomID) + + def patch_rom(world, rom, player, active_level_dict): local_random = world.slot_seeds[player] @@ -739,18 +831,8 @@ def patch_rom(world, rom, player, active_level_dict): intro_text = generate_text_box("Bowser has stolen all of Mario's abilities. Can you help Mario travel across Dinosaur land to get them back and save the Princess from him?") rom.write_bytes(0x2A5D9, intro_text) - # Force all 8 Bowser's Castle Rooms - rom.write_byte(0x3A680, 0xD4) - rom.write_byte(0x3A684, 0xD4) - rom.write_byte(0x3A688, 0xD4) - rom.write_byte(0x3A68C, 0xD4) - rom.write_byte(0x3A705, 0xD3) - rom.write_byte(0x3A763, 0xD2) - rom.write_byte(0x3A800, 0xD1) - rom.write_byte(0x3A83D, 0xCF) - rom.write_byte(0x3A932, 0xCE) - rom.write_byte(0x3A9E1, 0xCD) - rom.write_byte(0x3AA75, 0xCC) + handle_bowser_rooms(rom, world, player) + handle_boss_shuffle(rom, world, player) # Prevent Title Screen Deaths rom.write_byte(0x1C6A, 0x80) @@ -805,6 +887,11 @@ def patch_rom(world, rom, player, active_level_dict): if world.autosave[player]: rom.write_bytes(0x20F93, bytearray([0x00])) + if world.overworld_speed[player] == "fast": + rom.write_bytes(0x21414, bytearray([0x20, 0x10])) + elif world.overworld_speed[player] == "slow": + rom.write_bytes(0x21414, bytearray([0x05, 0x05])) + # Starting Life Count rom.write_bytes(0x1E25, bytearray([world.starting_life_count[player].value - 1])) @@ -835,6 +922,8 @@ def patch_rom(world, rom, player, active_level_dict): if world.music_shuffle[player] != "none": handle_music_shuffle(rom, world, player) + generate_shuffled_ow_palettes(rom, world, player) + generate_shuffled_header_data(rom, world, player) if world.swap_donut_gh_exits[player]: diff --git a/worlds/smw/__init__.py b/worlds/smw/__init__.py index 6accda9c6c..73d6d58d46 100644 --- a/worlds/smw/__init__.py +++ b/worlds/smw/__init__.py @@ -5,12 +5,12 @@ import threading from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification from .Items import SMWItem, ItemData, item_table -from .Locations import SMWLocation, all_locations, setup_locations +from .Locations import SMWLocation, all_locations, setup_locations, special_zone_level_names, special_zone_dragon_coin_names from .Options import smw_options from .Regions import create_regions, connect_regions from .Levels import full_level_list, generate_level_list, location_id_to_level_id from .Rules import set_rules -from ..generic.Rules import add_rule +from ..generic.Rules import add_rule, exclusion_rules from .Names import ItemName, LocationName from .Client import SMWSNIClient from ..AutoWorld import WebWorld, World @@ -41,7 +41,7 @@ class SMWWorld(World): game: str = "Super Mario World" option_definitions = smw_options topology_present = False - data_version = 2 + data_version = 3 required_client_version = (0, 3, 5) item_name_to_id = {name: data.code for name, data in item_table.items()} @@ -74,6 +74,10 @@ class SMWWorld(World): return slot_data + def generate_early(self): + if self.multiworld.early_climb[self.player]: + self.multiworld.local_early_items[self.player][ItemName.mario_climb] = 1 + def generate_basic(self): itempool: typing.List[SMWItem] = [] @@ -89,6 +93,15 @@ class SMWWorld(World): add_rule(self.multiworld.get_region(LocationName.chocolate_island_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 5)) add_rule(self.multiworld.get_region(LocationName.valley_of_bowser_1_tile, self.player).entrances[0], lambda state: state.has(ItemName.koopaling, self.player, 6)) + if self.multiworld.exclude_special_zone[self.player]: + exclusion_pool = set() + if self.multiworld.dragon_coin_checks[self.player]: + exclusion_pool.update(special_zone_level_names) + exclusion_pool.update(special_zone_dragon_coin_names) + elif self.multiworld.number_of_yoshi_eggs[self.player].value <= 72: + exclusion_pool.update(special_zone_level_names) + exclusion_rules(self.multiworld, self.player, exclusion_pool) + total_required_locations = 96 if self.multiworld.dragon_coin_checks[self.player]: total_required_locations += 49 @@ -120,6 +133,7 @@ class SMWWorld(World): trap_weights += ([ItemName.ice_trap] * self.multiworld.ice_trap_weight[self.player].value) trap_weights += ([ItemName.stun_trap] * self.multiworld.stun_trap_weight[self.player].value) trap_weights += ([ItemName.literature_trap] * self.multiworld.literature_trap_weight[self.player].value) + trap_weights += ([ItemName.timer_trap] * self.multiworld.timer_trap_weight[self.player].value) trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.multiworld.trap_fill_percentage[self.player].value / 100.0)) junk_count -= trap_count From ffff9ece5513f25fe6e65badc2a1e482a4ee3166 Mon Sep 17 00:00:00 2001 From: el-u <109771707+el-u@users.noreply.github.com> Date: Tue, 31 Jan 2023 21:26:09 +0100 Subject: [PATCH 07/47] core: properly declare from_any as an abstract classmethod --- Options.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Options.py b/Options.py index 53bf357630..10add11be6 100644 --- a/Options.py +++ b/Options.py @@ -79,9 +79,6 @@ class AssembleOptions(abc.ABCMeta): return super(AssembleOptions, mcs).__new__(mcs, name, bases, attrs) - @abc.abstractclassmethod - def from_any(cls, value: typing.Any) -> "Option[typing.Any]": ... - T = typing.TypeVar('T') @@ -129,8 +126,9 @@ class Option(typing.Generic[T], metaclass=AssembleOptions): return bool(self.value) @classmethod + @abc.abstractmethod def from_any(cls, data: typing.Any) -> Option[T]: - raise NotImplementedError + ... if typing.TYPE_CHECKING: from Generate import PlandoOptions @@ -168,7 +166,7 @@ class FreeText(Option): return value -class NumericOption(Option[int], numbers.Integral): +class NumericOption(Option[int], numbers.Integral, abc.ABC): default = 0 # note: some of the `typing.Any`` here is a result of unresolved issue in python standards # `int` is not a `numbers.Integral` according to the official typestubs From 3cef39a387b97963d57df6515442165c08b37735 Mon Sep 17 00:00:00 2001 From: PoryGone <98504756+PoryGone@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:15:01 -0500 Subject: [PATCH 08/47] SMW & DKC3: Ship as `.apworld` (#1426) --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 35a6acc8e8..d34798d3db 100644 --- a/setup.py +++ b/setup.py @@ -43,6 +43,8 @@ apworlds: set = { "Subnautica", "Factorio", "Rogue Legacy", + "Donkey Kong Country 3", + "Super Mario World", } if os.path.exists("X:/pw.txt"): From 4de7ebd8b046ca70e7c7375311a26d4e8539ea63 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Wed, 1 Feb 2023 21:18:07 +0100 Subject: [PATCH 09/47] The Witness: v4 Content Update (#1338) ## New Features: - EP Shuffle (Individual or Obelisk Sides, with varying difficulty levels) - Ability to play without Puzzle Randomization (I.e. vanilla + AP layer) - Pet the Dog to get a Puzzle Skip :) (No, really.) ## Changes: - Starting inventory behavior improved (Consider starting items like doors and lasers logically even if they aren't part of the mode) - Audio Log hint system improved (On low hint counts, you will no longer get the same locations hinted every time, i.e. always hints are shuffled) ## Fixes: - Many fixes to symbol requirements - Fixes to "shuffle_postgame" (What checks are evaluated as "postgame" in specific modes) - Logically irrelevant doors are now "useful" instead of "progression" --- worlds/witness/Options.py | 28 + worlds/witness/WitnessLogic.txt | 229 +++- worlds/witness/WitnessLogicExpert.txt | 457 +++++-- worlds/witness/WitnessLogicVanilla.txt | 1123 +++++++++++++++++ worlds/witness/__init__.py | 91 +- worlds/witness/docs/en_The Witness.md | 13 +- worlds/witness/hints.py | 92 +- worlds/witness/items.py | 84 +- worlds/witness/locations.py | 255 +++- worlds/witness/player_logic.py | 106 +- worlds/witness/regions.py | 15 +- worlds/witness/rules.py | 67 +- .../witness/settings/Disable_Unrandomized.txt | 14 +- worlds/witness/settings/EP_Shuffle/EP_All.txt | 136 ++ .../witness/settings/EP_Shuffle/EP_Easy.txt | 11 + .../settings/EP_Shuffle/EP_NoCavesEPs.txt | 5 + .../settings/EP_Shuffle/EP_NoEclipse.txt | 2 + .../settings/EP_Shuffle/EP_NoMountainEPs.txt | 4 + .../witness/settings/EP_Shuffle/EP_Sides.txt | 34 + .../witness/settings/EP_Shuffle/EP_Videos.txt | 6 + worlds/witness/static_logic.py | 47 +- worlds/witness/utils.py | 35 + 22 files changed, 2557 insertions(+), 297 deletions(-) create mode 100644 worlds/witness/WitnessLogicVanilla.txt create mode 100644 worlds/witness/settings/EP_Shuffle/EP_All.txt create mode 100644 worlds/witness/settings/EP_Shuffle/EP_Easy.txt create mode 100644 worlds/witness/settings/EP_Shuffle/EP_NoCavesEPs.txt create mode 100644 worlds/witness/settings/EP_Shuffle/EP_NoEclipse.txt create mode 100644 worlds/witness/settings/EP_Shuffle/EP_NoMountainEPs.txt create mode 100644 worlds/witness/settings/EP_Shuffle/EP_Sides.txt create mode 100644 worlds/witness/settings/EP_Shuffle/EP_Videos.txt diff --git a/worlds/witness/Options.py b/worlds/witness/Options.py index 2975696acf..1b539f6f0d 100644 --- a/worlds/witness/Options.py +++ b/worlds/witness/Options.py @@ -58,6 +58,31 @@ class ShuffleVaultBoxes(Toggle): display_name = "Shuffle Vault Boxes" +class ShuffleEnvironmentalPuzzles(Choice): + """ + Add Environmental/Obelisk Puzzles into the location pool. + In "individual", every Environmental Puzzle sends an item. + In "obelisk_sides", completing every puzzle on one side of an Obelisk sends an item. + """ + display_name = "Shuffle Environmental Puzzles" + option_off = 0 + option_individual = 1 + option_obelisk_sides = 2 + + +class ShuffleDog(Toggle): + """Add petting the Town dog into the location pool.""" + + display_name = "Pet the Dog" + + +class EnvironmentalPuzzlesDifficulty(Choice): + display_name = "Environmental Puzzles Difficulty" + option_normal = 0 + option_tedious = 1 + option_eclipse = 2 + + class ShufflePostgame(Toggle): """Adds locations into the pool that are guaranteed to become accessible after or at the same time as your goal. Use this if you don't play with release on victory. IMPORTANT NOTE: The possibility of your second @@ -81,6 +106,7 @@ class PuzzleRandomization(Choice): display_name = "Puzzle Randomization" option_sigma_normal = 0 option_sigma_expert = 1 + option_none = 2 class MountainLasers(Range): @@ -133,6 +159,8 @@ the_witness_options: Dict[str, type] = { "disable_non_randomized_puzzles": DisableNonRandomizedPuzzles, "shuffle_discarded_panels": ShuffleDiscardedPanels, "shuffle_vault_boxes": ShuffleVaultBoxes, + "shuffle_EPs": ShuffleEnvironmentalPuzzles, + "EP_difficulty": EnvironmentalPuzzlesDifficulty, "shuffle_postgame": ShufflePostgame, "victory_condition": VictoryCondition, "mountain_lasers": MountainLasers, diff --git a/worlds/witness/WitnessLogic.txt b/worlds/witness/WitnessLogic.txt index 2bb0b5ca24..85bf7d40cb 100644 --- a/worlds/witness/WitnessLogic.txt +++ b/worlds/witness/WitnessLogic.txt @@ -1,6 +1,11 @@ -First Hallway (First Hallway) - Entry - True - Tutorial - 0x00182: +Entry (Entry): + +First Hallway (First Hallway) - Entry - True - First Hallway Room - 0x00064: 158000 - 0x00064 (Straight) - True - True -158001 - 0x00182 (Bend) - 0x00064 - True +159510 - 0x01848 (EP) - 0x00064 - True + +First Hallway Room (First Hallway) - Tutorial - 0x00182: +158001 - 0x00182 (Bend) - True - True Tutorial (Tutorial) - Outside Tutorial - 0x03629: 158002 - 0x00293 (Front Center) - True - True @@ -10,8 +15,11 @@ Tutorial (Tutorial) - Outside Tutorial - 0x03629: 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 +158009 - 0x0C335 (Pillar) - True - Triangles 158010 - 0x0C373 (Patio Floor) - 0x0C335 - Dots +159512 - 0x33530 (Cloud EP) - True - True +159513 - 0x33600 (Patio Flowers EP) - 0x0C373 - True +159517 - 0x3352F (Gate EP) - 0x03505 - True Outside Tutorial (Outside Tutorial) - Outside Tutorial Path To Outpost - 0x03BA2: 158650 - 0x033D4 (Vault) - True - Dots & Black/White Squares @@ -31,6 +39,10 @@ Outside Tutorial (Outside Tutorial) - Outside Tutorial Path To Outpost - 0x03BA2 158025 - 0x00020 (Tree Row 8) - 0x0001F - Black/White Squares 158026 - 0x00021 (Tree Row 9) - 0x00020 - Black/White Squares Door - 0x03BA2 (Outpost Path) - 0x0A3B5 +159511 - 0x03D06 (Garden EP) - True - True +159514 - 0x28A2F (Town Sewer EP) - True - True +159516 - 0x334A3 (Path EP) - True - True +159500 - 0x035C7 (Tractor EP) - True - True Outside Tutorial Path To Outpost (Outside Tutorial) - Outside Tutorial Outpost - 0x0A170: 158011 - 0x0A171 (Outpost Entry Panel) - True - Dots & Full Dots @@ -41,12 +53,14 @@ Outside Tutorial Outpost (Outside Tutorial) - Outside Tutorial - 0x04CA3: Door - 0x04CA3 (Outpost Exit) - 0x04CA4 158600 - 0x17CFB (Discard) - True - Triangles -Main Island () - Outside Tutorial - True: +Main Island (Main Island) - Outside Tutorial - True: +159550 - 0x28B91 (Thundercloud EP) - 0x09F98 & 0x012FB - True Outside Glass Factory (Glass Factory) - Main Island - True - Inside Glass Factory - 0x01A29: 158027 - 0x01A54 (Entry Panel) - True - Symmetry Door - 0x01A29 (Entry) - 0x01A54 158601 - 0x3C12B (Discard) - True - Triangles +159002 - 0x28B8A (Vase EP) - 0x01A54 - True Inside Glass Factory (Glass Factory) - Inside Glass Factory Behind Back Wall - 0x0D7ED: 158028 - 0x00086 (Back Wall 1) - True - Symmetry @@ -95,6 +109,7 @@ Symmetry Island Lower (Symmetry Island) - Symmetry Island Upper - 0x18269: 158063 - 0x00B8D (Scenery Outlines 5) - 0x00B53 - Symmetry 158064 - 0x1C349 (Upper Panel) - 0x00076 - Symmetry & Dots Door - 0x18269 (Upper) - 0x1C349 +159000 - 0x0332B (Glass Factory Black Line Reflection EP) - True - True Symmetry Island Upper (Symmetry Island): 158065 - 0x00A52 (Yellow 1) - True - Symmetry & Colored Dots @@ -104,7 +119,8 @@ Symmetry Island Upper (Symmetry Island): 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 +Laser - 0x00509 (Laser) - 0x0360D +159001 - 0x03367 (Glass Factory Black Line EP) - True - True Orchard (Orchard) - Main Island - True - Orchard Beyond First Gate - 0x03307: 158071 - 0x00143 (Apple Tree 1) - True - True @@ -134,9 +150,18 @@ Desert Outside (Desert) - Main Island - True - Desert Floodlight Room - 0x09FEE: 158084 - 0x09F94 (Surface 8) - 0x0A053 & 0x09F86 - True 158085 - 0x09F86 (Surface 8 Control) - 0x0A053 - True 158086 - 0x0C339 (Light Room Entry Panel) - 0x09F94 - True -Door - 0x09FEE (Light Room Entry) - 0x0C339 - True +Door - 0x09FEE (Light Room Entry) - 0x0C339 158701 - 0x03608 (Laser Panel) - 0x012D7 & 0x0A15F - True Laser - 0x012FB (Laser) - 0x03608 +159020 - 0x3351D (Sand Snake EP) - True - True +159030 - 0x0053C (Facade Right EP) - True - True +159031 - 0x00771 (Facade Left EP) - True - True +159032 - 0x335C8 (Stairs Left EP) - True - True +159033 - 0x335C9 (Stairs Right EP) - True - True +159036 - 0x220E4 (Broken Wall Straight EP) - True - True +159037 - 0x220E5 (Broken Wall Bend EP) - True - True +159040 - 0x334B9 (Shore EP) - True - True +159041 - 0x334BC (Island EP) - True - True Desert Floodlight Room (Desert) - Desert Pond Room - 0x0C2C3: 158087 - 0x09FAA (Light Control) - True - True @@ -153,6 +178,8 @@ Desert Pond Room (Desert) - Desert Water Levels Room - 0x0A24B: 158095 - 0x18313 (Pond Room 5) - 0x0078D - True 158096 - 0x0A249 (Flood Room Entry Panel) - 0x18313 - True Door - 0x0A24B (Flood Room Entry) - 0x0A249 +159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True +159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316: 158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True @@ -170,6 +197,7 @@ Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316: 158109 - 0x17ECA (Flood Room 5) - 0x0117A & 0x1C260 & 0x1831C - True 158110 - 0x18076 (Flood Room 6) - 0x17ECA & 0x1C260 & 0x1831C - True Door - 0x0C316 (Elevator Room Entry) - 0x18076 +159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x012FB: 158111 - 0x17C31 (Final Transparent) - True - True @@ -178,22 +206,31 @@ Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x012F 158115 - 0x0A15C (Final Bent 1) - True - True 158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True 158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True +159035 - 0x037BB (Elevator EP) - 0x012FB - True Desert Lowest Level Inbetween Shortcuts (Desert): -Outside Quarry (Quarry) - Main Island - True - Quarry Between Entrys - 0x09D6F: +Outside Quarry (Quarry) - Main Island - True - Quarry Between Entrys - 0x09D6F - Quarry Elevator - TrueOneWay: 158118 - 0x09E57 (Entry 1 Panel) - True - 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 (Entry 1) - 0x09E57 +159404 - 0x28A4A (Shore EP) - True - True +159410 - 0x334B6 (Entrance Pipe EP) - True - True +159412 - 0x28A4C (Sand Pile EP) - True - True +159420 - 0x289CF (Rock Line EP) - True - True +159421 - 0x289D1 (Rock Line Reflection EP) - True - True + +Quarry Elevator (Quarry): +158120 - 0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser +159403 - 0x17CB9 (Railroad EP) - 0x17CC4 - True Quarry Between Entrys (Quarry) - Quarry - 0x17C07: 158119 - 0x17C09 (Entry 2 Panel) - True - Shapers Door - 0x17C07 (Entry 2) - 0x17C09 -Quarry (Quarry) - Quarry Mill Ground Floor - 0x02010: +Quarry (Quarry) - Quarry Mill Ground Floor - 0x02010 - Quarry Elevator - 0x17CC4: 158121 - 0x01E5A (Mill Entry Left Panel) - True - Black/White Squares 158122 - 0x01E59 (Mill Entry Right Panel) - True - Dots Door - 0x02010 (Mill Entry) - 0x01E59 & 0x01E5A @@ -229,6 +266,8 @@ Quarry Mill Upper Floor (Quarry Mill) - Quarry Mill Middle Floor - 0x03676 & 0x0 Door - 0x0368A (Stairs) - 0x03677 158143 - 0x3C125 (Control Room Left) - 0x0367C - Black/White Squares & Dots & Eraser 158144 - 0x0367C (Control Room Right) - 0x014E9 - Colored Squares & Dots & Eraser +159411 - 0x0069D (Ramp EP) - 0x03676 & 0x275FF - True +159413 - 0x00614 (Lift EP) - 0x275FF & 0x03675 - True Quarry Boathouse (Quarry Boathouse) - Quarry - True - Quarry Boathouse Upper Front - 0x03852 - Quarry Boathouse Behind Staircase - 0x2769B: 158146 - 0x034D4 (Intro Left) - True - Stars @@ -250,6 +289,7 @@ Door - 0x17C50 (First Barrier) - 0x021AE Quarry Boathouse Upper Middle (Quarry Boathouse) - Quarry Boathouse Upper Back - 0x03858: 158154 - 0x03858 (Ramp Horizontal Control) - True - Shapers & Eraser +159402 - 0x00859 (Moving Ramp EP) - 0x03858 & 0x03852 & 0x3865F - True Quarry Boathouse Upper Back (Quarry Boathouse) - Quarry Boathouse Upper Middle - 0x3865F: 158155 - 0x38663 (Second Barrier Panel) - True - True @@ -267,6 +307,7 @@ Door - 0x3865F (Second Barrier) - 0x38663 158167 - 0x0A3CB (Back Second Row 1) - 0x09DB4 - Stars & Eraser & Shapers 158168 - 0x0A3CC (Back Second Row 2) - 0x0A3CB - Stars & Eraser & Shapers 158169 - 0x0A3D0 (Back Second Row 3) - 0x0A3CC - Stars & Eraser & Shapers +159401 - 0x005F6 (Hook EP) - 0x275FA & 0x03852 & 0x3865F - True Shadows (Shadows) - Main Island - True - Shadows Ledge - 0x19B24 - Shadows Laser Room - 0x194B2 & 0x19665: 158170 - 0x334DB (Door Timer Outside) - True - True @@ -289,6 +330,7 @@ Door - 0x194B2 (Laser Entry Right) - 0x1972F 158185 - 0x197E8 (Near 4) - 0x197E0 - True 158186 - 0x197E5 (Near 5) - 0x197E8 - True Door - 0x19665 (Laser Entry Left) - 0x197E5 +159400 - 0x28A7B (Quarry Mill Rooftop Vent EP) - True - True Shadows Ledge (Shadows) - Shadows - 0x1855B - Quarry - 0x19865 & 0x0A2DF: 158187 - 0x334DC (Door Timer Inside) - True - True @@ -306,12 +348,19 @@ Shadows Laser Room (Shadows): 158703 - 0x19650 (Laser Panel) - True - True Laser - 0x181B3 (Laser) - 0x19650 +Treehouse Beach (Treehouse Beach) - Main Island - True: +159200 - 0x0053D (Rock Shadow EP) - True - True +159201 - 0x0053E (Sand Shadow EP) - True - True +159212 - 0x220BD (Both Orange Bridges EP) - 0x17DA2 & 0x17DDB - True + Keep (Keep) - Main Island - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC: 158193 - 0x00139 (Hedge Maze 1) - True - True 158197 - 0x0A3A8 (Reset Pressure Plates 1) - True - True 158198 - 0x033EA (Pressure Plates 1) - 0x0A3A8 - Dots Door - 0x01954 (Hedge Maze 1 Exit) - 0x00139 Door - 0x01BEC (Pressure Plates 1 Exit) - 0x033EA +159430 - 0x03E77 (Red Flowers EP) - True - True +159431 - 0x03E7C (Purple Flowers EP) - True - True Keep 2nd Maze (Keep) - Keep - 0x018CE - Keep 3rd Maze - 0x019D8: Door - 0x018CE (Hedge Maze 2 Shortcut) - 0x00139 @@ -350,6 +399,13 @@ Shipwreck (Shipwreck) - Keep 3rd Pressure Plate - True: 158654 - 0x00AFB (Vault) - True - Symmetry & Sound Dots & Colored Dots 158655 - 0x03535 (Vault Box) - 0x00AFB - True 158605 - 0x17D28 (Discard) - True - Triangles +159220 - 0x03B22 (Circle Far EP) - True - True +159221 - 0x03B23 (Circle Left EP) - True - True +159222 - 0x03B24 (Circle Near EP) - True - True +159224 - 0x03A79 (Stern EP) - True - True +159225 - 0x28ABD (Rope Inner EP) - True - True +159226 - 0x28ABE (Rope Outer EP) - True - True +159230 - 0x3388F (Couch EP) - 0x17CDF | 0x0A054 - True Keep Tower (Keep) - Keep - 0x04F8F: 158206 - 0x0361B (Tower Shortcut Panel) - True - True @@ -357,6 +413,13 @@ Door - 0x04F8F (Tower Shortcut) - 0x0361B 158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True 158705 - 0x03317 (Laser Panel Pressure Plates) - 0x01D3F - Shapers & Black/White Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Dots Laser - 0x014BB (Laser) - 0x0360E | 0x03317 +159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True +159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True +159242 - 0x033DD (Pressure Plates 3 EP) - 0x01CD3 & 0x01CD5 - True +159243 - 0x033E5 (Pressure Plates 4 Left Exit EP) - 0x01D3F - True +159244 - 0x018B6 (Pressure Plates 4 Right Exit EP) - 0x01D3F - True +159250 - 0x28AE9 (Path EP) - True - True +159251 - 0x3348F (Hedges EP) - True - True Outside Monastery (Monastery) - Main Island - True - Inside Monastery - 0x0C128 & 0x0C153 - Monastery Garden - 0x03750: 158207 - 0x03713 (Shortcut Panel) - True - True @@ -371,6 +434,13 @@ Door - 0x0C153 (Entry Outer) - 0x00C92 Door - 0x03750 (Garden Entry) - 0x00037 158706 - 0x17CA4 (Laser Panel) - 0x193A6 - True Laser - 0x17C65 (Laser) - 0x17CA4 +159130 - 0x006E5 (Facade Left Near EP) - True - True +159131 - 0x006E6 (Facade Left Far Short EP) - True - True +159132 - 0x006E7 (Facade Left Far Long EP) - True - True +159136 - 0x03DAB (Facade Right Near EP) - True - True +159137 - 0x03DAC (Facade Left Stairs EP) - True - True +159138 - 0x03DAD (Facade Right Stairs EP) - True - True +159140 - 0x03E01 (Grass Stairs EP) - True - True Inside Monastery (Monastery): 158213 - 0x09D9B (Shutters Control) - True - Dots @@ -378,6 +448,9 @@ Inside Monastery (Monastery): 158215 - 0x193AA (Inside 2) - 0x193A7 - True 158216 - 0x193AB (Inside 3) - 0x193AA - True 158217 - 0x193A6 (Inside 4) - 0x193AB - True +159133 - 0x034A7 (Left Shutter EP) - 0x09D9B - True +159134 - 0x034AD (Middle Shutter EP) - 0x09D9B - True +159135 - 0x034AF (Right Shutter EP) - 0x09D9B - True Monastery Garden (Monastery): @@ -404,12 +477,24 @@ Door - 0x03BB0 (Church Entry) - 0x28A0D Door - 0x28AA2 (Maze Stairs) - 0x28A79 158241 - 0x17F5F (Windmill Entry Panel) - True - Dots Door - 0x1845B (Windmill Entry) - 0x17F5F +159010 - 0x037B6 (Windmill First Blade EP) - 0x17D02 - True +159011 - 0x037B2 (Windmill Second Blade EP) - 0x17D02 - True +159012 - 0x000F7 (Windmill Third Blade EP) - 0x17D02 - True +159540 - 0x03335 (Tower Underside First EP) - True - True +159541 - 0x03412 (Tower Underside Second EP) - True - True +159542 - 0x038A6 (Tower Underside Third EP) - True - True +159543 - 0x038AA (Tower Underside Fourth EP) - True - True +159545 - 0x03E40 (RGB House Green EP) - 0x334D8 - True +159546 - 0x28B8E (Maze Bridge Underside EP) - 0x2896A - True +159552 - 0x03BCF (Black Line Redirect EP) - True - True +159800 - 0xFFF80 (Pet the Dog) - True - True Town Inside Cargo Box (Town): 158606 - 0x17D01 (Cargo Box Discard) - True - Triangles Town Maze Rooftop (Town) - Town Red Rooftop - 0x2896A: 158229 - 0x2896A (Maze Rooftop Bridge Control) - True - Shapers +159544 - 0x03E3F (RGB House Red EP) - 0x334D8 - True Town Red Rooftop (Town): 158607 - 0x17C71 (Rooftop Discard) - True - Triangles @@ -425,6 +510,7 @@ Town Wooden Rooftop (Town): Town Church (Town): 158227 - 0x28A69 (Church Lattice) - 0x03BB0 - True +159553 - 0x03BD1 (Black Line Church EP) - True - True RGB House (Town) - RGB Room - 0x2897B: 158242 - 0x034E4 (Sound Room Left) - True - True @@ -445,6 +531,8 @@ Door - 0x2779A (Fourth Door) - 0x28B39 Town Tower Top (Town): 158708 - 0x032F5 (Laser Panel) - True - True Laser - 0x032F9 (Laser) - 0x032F5 +159422 - 0x33692 (Brown Bridge EP) - True - True +159551 - 0x03BCE (Black Line Tower EP) - True - True Windmill Interior (Windmill) - Theater - 0x17F88: 158247 - 0x17D02 (Turn Control) - True - Dots @@ -464,6 +552,10 @@ Theater (Theater) - Town - 0x0A16D | 0x3CCDF: Door - 0x0A16D (Exit Left) - 0x0A168 Door - 0x3CCDF (Exit Right) - 0x33AB2 158608 - 0x17CF7 (Discard) - True - Triangles +159554 - 0x339B6 (Eclipse EP) - 0x03549 & 0x0A16D & 0x3CCDF - True +159555 - 0x33A29 (Window EP) - 0x03553 - True +159556 - 0x33A2A (Door EP) - 0x03553 - True +159558 - 0x33B06 (Church EP) - 0x0354E - True Jungle (Jungle) - Main Island - True - Outside Jungle River - 0x3873B - Boat - 0x17CDF: 158251 - 0x17CDF (Shore Boat Spawn) - True - Boat @@ -487,12 +579,24 @@ Door - 0x1475B (Popup Wall) - 0x17CAB Laser - 0x00274 (Laser) - 0x03616 158266 - 0x337FA (Laser Shortcut Panel) - True - True Door - 0x3873B (Laser Shortcut) - 0x337FA +159100 - 0x03ABC (Long Arch Moss EP) - True - True +159101 - 0x03ABE (Straight Left Moss EP) - True - True +159102 - 0x03AC0 (Pop-up Wall Moss EP) - True - True +159103 - 0x03AC4 (Short Arch Moss EP) - True - True +159150 - 0x289F4 (Entrance EP) - True - True +159151 - 0x289F5 (Tree Halo EP) - True - True +159350 - 0x035CB (Bamboo CCW EP) - True - True +159351 - 0x035CF (Bamboo CW EP) - True - True Outside Jungle River (River) - Main Island - True - Monastery Garden - 0x0CF2A: 158267 - 0x17CAA (Monastery Shortcut Panel) - True - True Door - 0x0CF2A (Monastery Shortcut) - 0x17CAA 158663 - 0x15ADD (Vault) - True - Black/White Squares & Dots 158664 - 0x03702 (Vault Box) - 0x15ADD - True +159110 - 0x03AC5 (Green Leaf Moss EP) - True - True +159120 - 0x03BE2 (Monastery Garden Left EP) - 0x03750 - True +159121 - 0x03BE3 (Monastery Garden Right EP) - True - True +159122 - 0x0A409 (Monastery Wall EP) - True - True Outside Bunker (Bunker) - Main Island - True - Bunker - 0x0C2A4: 158268 - 0x17C2E (Entry Panel) - True - Black/White Squares & Colored Squares @@ -524,16 +628,25 @@ Bunker Ultraviolet Room (Bunker) - Bunker Elevator Section - 0x0A08D: 158285 - 0x17E67 (UV Room 2) - 0x17E63 & 0x34BC6 - Colored Squares & Black/White Squares Door - 0x0A08D (Elevator Room Entry) - 0x17E67 -Bunker Elevator Section (Bunker) - Bunker Laser Platform - 0x0A079: +Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay: +159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True + +Bunker Elevator (Bunker) - Bunker Laser Platform - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079: 158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares -Bunker Laser Platform (Bunker): +Bunker Green Room (Bunker) - Bunker Elevator - TrueOneWay: +159310 - 0x000D3 (Green Room Flowers EP) - True - True + +Bunker Laser Platform (Bunker) - Bunker Elevator - TrueOneWay: 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) - 0x0056E +159321 - 0x03603 (Purple Sand Middle EP) - 0x17E2B - True +159322 - 0x03601 (Purple Sand Top EP) - 0x17E2B - True +159327 - 0x035DE (Purple Sand Bottom EP) - True - True Swamp Entry Area (Swamp) - Swamp Sliding Bridge - TrueOneWay: 158288 - 0x00469 (Intro Front 1) - True - Shapers @@ -553,6 +666,8 @@ Swamp Entry Area (Swamp) - Swamp Sliding Bridge - TrueOneWay: Swamp Sliding Bridge (Swamp) - Swamp Entry Area - 0x00609 - Swamp Near Platform - 0x00609: 158302 - 0x00609 (Sliding Bridge) - True - Shapers +159342 - 0x0105D (Sliding Bridge Left EP) - 0x00609 - True +159343 - 0x0A304 (Sliding Bridge Right EP) - 0x00609 - True Swamp Near Platform (Swamp) - Swamp Cyan Underwater - 0x04B7F - Swamp Near Boat - 0x38AE6 - Swamp Between Bridges Near - 0x184B7 - Swamp Sliding Bridge - TrueOneWay: 158313 - 0x00982 (Platform Row 1) - True - Shapers @@ -572,6 +687,7 @@ Swamp Cyan Underwater (Swamp): 158310 - 0x013E6 (Cyan Underwater 4) - 0x00005 - Shapers & Negative Shapers 158311 - 0x00596 (Cyan Underwater 5) - 0x013E6 - Shapers & Negative Shapers 158312 - 0x18488 (Cyan Underwater Sliding Bridge Control) - True - Shapers +159340 - 0x03AA6 (Cyan Underwater Sliding Bridge EP) - 0x18488 - True Swamp Between Bridges Near (Swamp) - Swamp Between Bridges Far - 0x18507: 158303 - 0x00999 (Between Bridges Near Row 1) - 0x00990 - Shapers @@ -596,6 +712,8 @@ Door - 0x305D5 (Red Underwater Exit) - 0x014D1 Swamp Rotating Bridge (Swamp) - Swamp Between Bridges Far - 0x181F5 - Swamp Near Boat - 0x181F5 - Swamp Purple Area - 0x181F5: 158327 - 0x181F5 (Rotating Bridge) - True - Rotated Shapers & Shapers +159331 - 0x016B2 (Rotating Bridge CCW EP) - 0x181F5 - True +159334 - 0x036CE (Rotating Bridge CW EP) - 0x181F5 - True Swamp Near Boat (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Blue Underwater - 0x18482: 158328 - 0x09DB8 (Boat Spawn) - True - Boat @@ -605,12 +723,16 @@ Swamp Near Boat (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Blue Underw 158332 - 0x00E3A (Beyond Rotating Bridge 4) - 0x00C2E - Rotated Shapers 158339 - 0x17E2B (Long Bridge Control) - True - Rotated Shapers & Shapers Door - 0x18482 (Blue Water Pump) - 0x00E3A +159332 - 0x3365F (Boat EP) - 0x09DB8 - True +159333 - 0x03731 (Long Bridge Side EP) - 0x17E2B - True Swamp Purple Area (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Purple Underwater - 0x0A1D6: Door - 0x0A1D6 (Purple Water Pump) - 0x00E3A Swamp Purple Underwater (Swamp): 158333 - 0x009A6 (Purple Underwater) - True - Shapers +159330 - 0x03A9E (Purple Underwater Right EP) - True - True +159336 - 0x03A93 (Purple Underwater Left EP) - True - True Swamp Blue Underwater (Swamp): 158334 - 0x009AB (Blue Underwater 1) - True - Shapers & Negative Shapers @@ -634,6 +756,7 @@ 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 +159210 - 0x33721 (Buoy EP) - 0x17C95 - True Treehouse Between Doors (Treehouse) - Treehouse Yellow Bridge - 0x0C310: 158345 - 0x02886 (Second Door Panel) - True - Stars @@ -691,7 +814,7 @@ Treehouse Second Purple Bridge (Treehouse) - Treehouse Left Orange Bridge - 0x17 158367 - 0x17D91 (Second Purple Bridge 6) - 0x17BDF - Stars & Colored Squares 158368 - 0x17DC6 (Second Purple Bridge 7) - 0x17D91 - Stars & Colored Squares -Treehouse Left Orange Bridge (Treehouse) - Treehouse Laser Room Front Platform - 0x17DDB - Treehouse Laser Room Back Platform - 0x17DDB: +Treehouse Left Orange Bridge (Treehouse) - Treehouse Laser Room Front Platform - 0x17DDB - Treehouse Laser Room Back Platform - 0x17DDB - Treehouse Burned House - 0x17DDB: 158376 - 0x17DB3 (Left Orange Bridge 1) - True - Stars & Black/White Squares & Stars + Same Colored Symbol 158377 - 0x17DB5 (Left Orange Bridge 2) - 0x17DB3 - Stars & Black/White Squares & Stars + Same Colored Symbol 158378 - 0x17DB6 (Left Orange Bridge 3) - 0x17DB5 - Stars & Black/White Squares & Stars + Same Colored Symbol @@ -708,7 +831,7 @@ Treehouse Left Orange Bridge (Treehouse) - Treehouse Laser Room Front Platform - 158389 - 0x17DB0 (Left Orange Bridge 14) - 0x17DAE - Stars & Black/White Squares & Stars + Same Colored Symbol 158390 - 0x17DDB (Left Orange Bridge 15) - 0x17DB0 - Stars & Black/White Squares & Stars + Same Colored Symbol -Treehouse Green Bridge (Treehouse): +Treehouse Green Bridge (Treehouse) - Treehouse Green Bridge Front House - 0x17E61 - Treehouse Green Bridge Left House - 0x17E61: 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 @@ -716,7 +839,12 @@ Treehouse Green Bridge (Treehouse): 158373 - 0x17E5B (Green Bridge 5) - 0x17E52 - Stars & Shapers & Stars + Same Colored Symbol 158374 - 0x17E5F (Green Bridge 6) - 0x17E5B - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol 158375 - 0x17E61 (Green Bridge 7) - 0x17E5F - Stars & Shapers & Rotated Shapers -158610 - 0x17FA9 (Green Bridge Discard) - 0x17E61 - Triangles + +Treehouse Green Bridge Front House (Treehouse): +158610 - 0x17FA9 (Green Bridge Discard) - True - Triangles + +Treehouse Green Bridge Left House (Treehouse): +159211 - 0x220A7 (Right Orange Bridge EP) - 0x17DA2 - True Treehouse Laser Room Front Platform (Treehouse) - Treehouse Laser Room - 0x0C323: Door - 0x0C323 (Laser House Entry) - 0x17DA2 & 0x2700B & 0x17DDB @@ -724,6 +852,9 @@ Door - 0x0C323 (Laser House Entry) - 0x17DA2 & 0x2700B & 0x17DDB Treehouse Laser Room Back Platform (Treehouse): 158611 - 0x17FA0 (Laser Discard) - True - Triangles +Treehouse Burned House (Treehouse): +159202 - 0x00769 (Burned House Beach EP) - True - True + Treehouse Laser Room (Treehouse): 158712 - 0x03613 (Laser Panel) - True - True 158403 - 0x17CBC (Laser House Door Timer Inside) - True - True @@ -733,12 +864,19 @@ Mountainside (Mountainside) - Main Island - True - Mountaintop - True: 158612 - 0x17C42 (Discard) - True - Triangles 158665 - 0x002A6 (Vault) - True - Symmetry & Colored Dots & Black/White Squares & Dots 158666 - 0x03542 (Vault Box) - 0x002A6 - True +159301 - 0x335AE (Cloud Cycle EP) - True - True +159325 - 0x33505 (Bush EP) - True - True +159335 - 0x03C07 (Apparent River EP) - True - True Mountaintop (Mountaintop) - 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 & Black/White Squares & Stars + Same Colored Symbol 158800 - 0xFFF00 (Box Long) - 7 Lasers & 11 Lasers & 0x17C34 - True +159300 - 0x001A3 (River Shape EP) - True - True +159320 - 0x3370E (Arch Black EP) - True - True +159324 - 0x336C8 (Arch White Right EP) - True - True +159326 - 0x3369A (Arch White Left EP) - True - True Mountain Top Layer (Mountain Floor 1) - Mountain Top Layer Bridge - 0x09E39: 158408 - 0x09E39 (Light Bridge Controller) - True - Black/White Squares & Colored Squares & Eraser @@ -763,7 +901,7 @@ Mountain Top Layer Bridge (Mountain Floor 1) - Mountain Floor 2 - 0x09E54: 158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Black/White Squares & Shapers Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B -Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Blue Bridge - 0x09E86: +Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Blue Bridge - 0x09E86 - Mountain Pink Bridge EP - TrueOneWay: 158426 - 0x09FD3 (Near Row 1) - True - Stars & Colored Squares & Stars + Same Colored Symbol 158427 - 0x09FD4 (Near Row 2) - 0x09FD3 - Stars & Colored Squares & Stars + Same Colored Symbol 158428 - 0x09FD6 (Near Row 3) - 0x09FD4 - Stars & Colored Squares & Stars + Same Colored Symbol @@ -779,7 +917,7 @@ Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86 Mountain Floor 2 Light Bridge Room Near (Mountain Floor 2): 158431 - 0x09E86 (Light Bridge Controller Near) - True - Stars & Stars + Same Colored Symbol & Rotated Shapers & Eraser -Mountain Floor 2 Beyond Bridge (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Far - 0x09E07: +Mountain Floor 2 Beyond Bridge (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Far - 0x09E07 - Mountain Pink Bridge EP - TrueOneWay: 158432 - 0x09FCC (Far Row 1) - True - Dots 158433 - 0x09FCE (Far Row 2) - 0x09FCC - Black/White Squares 158434 - 0x09FCF (Far Row 3) - 0x09FCE - Stars @@ -805,14 +943,19 @@ Mountain Third Layer (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueO 158444 - 0x09FDA (Giant Puzzle) - 0x09FC1 & 0x09F8E & 0x09F01 & 0x09EFF - Shapers & Symmetry Door - 0x09F89 (Exit) - 0x09FDA -Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Bottom Floor Rock - 0x17FA2 - Final Room - 0x0C141: +Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Bottom Floor Rock - 0x17FA2 - Final Room - 0x0C141 - Mountain Pink Bridge EP - TrueOneWay: 158614 - 0x17FA2 (Discard) - 0xFFF00 - Triangles 158445 - 0x01983 (Final Room Entry Left) - True - Shapers & Stars 158446 - 0x01987 (Final Room Entry Right) - True - Colored Squares & Dots Door - 0x0C141 (Final Room Entry) - 0x01983 & 0x01987 +159313 - 0x09D5D (Yellow Bridge EP) - 0x09E86 & 0x09ED8 - True +159314 - 0x09D5E (Blue Bridge EP) - 0x09E86 & 0x09ED8 - True + +Mountain Pink Bridge EP (Mountain Floor 2): +159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True Mountain Bottom Floor Rock (Mountain Bottom Floor) - Mountain Bottom Floor - 0x17F33 - Mountain Path to Caves - 0x17F33: -Door - 0x17F33 (Rock Open) - True +Door - 0x17F33 (Rock Open) - True - True Mountain Path to Caves (Mountain Bottom Floor) - Mountain Bottom Floor Rock - 0x334E1 - Caves - 0x2D77D: 158447 - 0x00FF8 (Caves Entry Panel) - True - Triangles & Black/White Squares @@ -872,6 +1015,7 @@ Door - 0x019A5 (Pillar Door) - 0x09DD5 Door - 0x2D73F (Mountain Shortcut Door) - 0x021D7 158450 - 0x17CF2 (Swamp Shortcut Panel) - True - Triangles Door - 0x2D859 (Swamp Shortcut Door) - 0x17CF2 +159341 - 0x3397C (Skylight EP) - True - True Path to Challenge (Caves) - Challenge - 0x0A19A: 158477 - 0x0A16E (Challenge Entry Panel) - True - Stars & Shapers & Stars + Same Colored Symbol @@ -900,6 +1044,7 @@ Challenge (Challenge) - Tunnels - 0x0348A: 158667 - 0x0356B (Vault Box) - 0x1C31A & 0x1C319 - True 158518 - 0x039B4 (Tunnels Entry Panel) - True - Triangles Door - 0x0348A (Tunnels Entry) - 0x039B4 +159530 - 0x28B30 (Water EP) - True - True Tunnels (Tunnels) - Windmill Interior - 0x27739 - Desert Lowest Level Inbetween Shortcuts - 0x27263 - Town - 0x09E87: 158668 - 0x2FAF6 (Vault Box) - True - True @@ -909,6 +1054,7 @@ Door - 0x27739 (Theater Shortcut) - 0x27732 Door - 0x27263 (Desert Shortcut) - 0x2773D 158521 - 0x09E85 (Town Shortcut Panel) - True - Triangles Door - 0x09E87 (Town Shortcut) - 0x09E85 +159557 - 0x33A20 (Theater Flowers EP) - 0x03553 & Theater to Tunnels - True Final Room (Mountain Final Room) - Elevator - 0x339BB & 0x33961: 158522 - 0x0383A (Right Pillar 1) - True - Stars @@ -930,3 +1076,50 @@ Elevator (Mountain Final Room): 158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA & 7 Lasers | 0x3D9A8 & 7 Lasers - 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: +159042 - 0x22106 (Desert EP) - True - True +159223 - 0x03B25 (Shipwreck CCW Underside EP) - True - True +159231 - 0x28B29 (Shipwreck Green EP) - True - True +159232 - 0x28B2A (Shipwreck CW Underside EP) - True - True +159323 - 0x03D0D (Bunker Yellow Line EP) - True - True +159515 - 0x28A37 (Town Long Sewer EP) - True - True +159520 - 0x33857 (Tutorial EP) - True - True +159521 - 0x33879 (Tutorial Reflection EP) - True - True +159522 - 0x03C19 (Tutorial Moss EP) - True - True +159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True + +Obelisks (EPs) - Entry - True: +159700 - 0xFFE00 (Desert Obelisk Side 1) - 0x0332B & 0x03367 & 0x28B8A - True +159701 - 0xFFE01 (Desert Obelisk Side 2) - 0x037B6 & 0x037B2 & 0x000F7 - True +159702 - 0xFFE02 (Desert Obelisk Side 3) - 0x3351D - True +159703 - 0xFFE03 (Desert Obelisk Side 4) - 0x0053C & 0x00771 & 0x335C8 & 0x335C9 & 0x337F8 & 0x037BB & 0x220E4 & 0x220E5 - True +159704 - 0xFFE04 (Desert Obelisk Side 5) - 0x334B9 & 0x334BC & 0x22106 & 0x0A14C & 0x0A14D - True +159710 - 0xFFE10 (Monastery Obelisk Side 1) - 0x03ABC & 0x03ABE & 0x03AC0 & 0x03AC4 - True +159711 - 0xFFE11 (Monastery Obelisk Side 2) - 0x03AC5 - True +159712 - 0xFFE12 (Monastery Obelisk Side 3) - 0x03BE2 & 0x03BE3 & 0x0A409 - True +159713 - 0xFFE13 (Monastery Obelisk Side 4) - 0x006E5 & 0x006E6 & 0x006E7 & 0x034A7 & 0x034AD & 0x034AF & 0x03DAB & 0x03DAC & 0x03DAD - True +159714 - 0xFFE14 (Monastery Obelisk Side 5) - 0x03E01 - True +159715 - 0xFFE15 (Monastery Obelisk Side 6) - 0x289F4 & 0x289F5 - True +159720 - 0xFFE20 (Treehouse Obelisk Side 1) - 0x0053D & 0x0053E & 0x00769 - True +159721 - 0xFFE21 (Treehouse Obelisk Side 2) - 0x33721 & 0x220A7 & 0x220BD - True +159722 - 0xFFE22 (Treehouse Obelisk Side 3) - 0x03B22 & 0x03B23 & 0x03B24 & 0x03B25 & 0x03A79 & 0x28ABD & 0x28ABE - True +159723 - 0xFFE23 (Treehouse Obelisk Side 4) - 0x3388F & 0x28B29 & 0x28B2A - True +159724 - 0xFFE24 (Treehouse Obelisk Side 5) - 0x018B6 & 0x033BE & 0x033BF & 0x033DD & 0x033E5 - True +159725 - 0xFFE25 (Treehouse Obelisk Side 6) - 0x28AE9 & 0x3348F - True +159730 - 0xFFE30 (River Obelisk Side 1) - 0x001A3 & 0x335AE - True +159731 - 0xFFE31 (River Obelisk Side 2) - 0x000D3 & 0x035F5 & 0x09D5D & 0x09D5E & 0x09D63 - True +159732 - 0xFFE32 (River Obelisk Side 3) - 0x3370E & 0x035DE & 0x03601 & 0x03603 & 0x03D0D & 0x3369A & 0x336C8 & 0x33505 - True +159733 - 0xFFE33 (River Obelisk Side 4) - 0x03A9E & 0x016B2 & 0x3365F & 0x03731 & 0x036CE & 0x03C07 & 0x03A93 - True +159734 - 0xFFE34 (River Obelisk Side 5) - 0x03AA6 & 0x3397C & 0x0105D & 0x0A304 - True +159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True +159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True +159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True +159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 & 0x33692 - True +159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x03E77 & 0x03E7C - True +159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True +159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True +159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True +159753 - 0xFFE53 (Town Obelisk Side 4) - 0x28B30 & 0x035C9 - True +159754 - 0xFFE54 (Town Obelisk Side 5) - 0x03335 & 0x03412 & 0x038A6 & 0x038AA & 0x03E3F & 0x03E40 & 0x28B8E - True +159755 - 0xFFE55 (Town Obelisk Side 6) - 0x28B91 & 0x03BCE & 0x03BCF & 0x03BD1 & 0x339B6 & 0x33A20 & 0x33A29 & 0x33A2A & 0x33B06 - True + +Lasers (Lasers) - Entry - True: diff --git a/worlds/witness/WitnessLogicExpert.txt b/worlds/witness/WitnessLogicExpert.txt index 89ac7390cc..aecf5f04ce 100644 --- a/worlds/witness/WitnessLogicExpert.txt +++ b/worlds/witness/WitnessLogicExpert.txt @@ -1,6 +1,11 @@ -First Hallway (First Hallway) - Entry - True - Tutorial - 0x00182: +Entry (Entry): + +First Hallway (First Hallway) - Entry - True - First Hallway Room - 0x00064: 158000 - 0x00064 (Straight) - True - True -158001 - 0x00182 (Bend) - 0x00064 - True +159510 - 0x01848 (EP) - 0x00064 - True + +First Hallway Room (First Hallway) - Tutorial - 0x00182: +158001 - 0x00182 (Bend) - True - True Tutorial (Tutorial) - Outside Tutorial - True: 158002 - 0x00293 (Front Center) - True - Dots @@ -10,8 +15,11 @@ Tutorial (Tutorial) - Outside Tutorial - True: 158006 - 0x0A3B2 (Back Right) - True - Dots & Full Dots 158007 - 0x03629 (Gate Open) - 0x002C2 - Symmetry & Dots 158008 - 0x03505 (Gate Close) - 0x2FAF6 - False -158009 - 0x0C335 (Pillar) - True - Triangles - True +158009 - 0x0C335 (Pillar) - True - Triangles 158010 - 0x0C373 (Patio Floor) - 0x0C335 - Dots +159512 - 0x33530 (Cloud EP) - True - True +159513 - 0x33600 (Patio Flowers EP) - 0x0C373 - True +159517 - 0x3352F (Gate EP) - 0x03505 - True Outside Tutorial (Outside Tutorial) - Outside Tutorial Path To Outpost - 0x03BA2: 158650 - 0x033D4 (Vault) - True - Dots & Full Dots & Squares & Black/White Squares @@ -31,6 +39,10 @@ Outside Tutorial (Outside Tutorial) - Outside Tutorial Path To Outpost - 0x03BA2 158025 - 0x00020 (Tree Row 8) - 0x0001F - Squares & Black/White Squares & Dots & Full Dots 158026 - 0x00021 (Tree Row 9) - 0x00020 - Squares & Black/White Squares & Dots & Full Dots Door - 0x03BA2 (Outpost Path) - 0x0A3B5 +159511 - 0x03D06 (Garden EP) - True - True +159514 - 0x28A2F (Town Sewer EP) - True - True +159516 - 0x334A3 (Path EP) - True - True +159500 - 0x035C7 (Tractor EP) - True - True Outside Tutorial Path To Outpost (Outside Tutorial) - Outside Tutorial Outpost - 0x0A170: 158011 - 0x0A171 (Outpost Entry Panel) - True - Dots & Full Dots & Triangles @@ -41,12 +53,14 @@ Outside Tutorial Outpost (Outside Tutorial) - Outside Tutorial - 0x04CA3: Door - 0x04CA3 (Outpost Exit) - 0x04CA4 158600 - 0x17CFB (Discard) - True - Arrows -Main Island () - Outside Tutorial - True: +Main Island (Main Island) - Outside Tutorial - True: +159550 - 0x28B91 (Thundercloud EP) - 0x09F98 & 0x012FB - True Outside Glass Factory (Glass Factory) - Main Island - True - Inside Glass Factory - 0x01A29: 158027 - 0x01A54 (Entry Panel) - True - Symmetry Door - 0x01A29 (Entry) - 0x01A54 158601 - 0x3C12B (Discard) - True - Arrows +159002 - 0x28B8A (Vase EP) - 0x01A54 - True Inside Glass Factory (Glass Factory) - Inside Glass Factory Behind Back Wall - 0x0D7ED: 158028 - 0x00086 (Back Wall 1) - True - Symmetry & Dots @@ -88,13 +102,14 @@ Symmetry Island Lower (Symmetry Island) - Symmetry Island Upper - 0x18269: 158056 - 0x00070 (Left 5) - 0x0006F - Symmetry & Colored Dots & Triangles 158057 - 0x00071 (Left 6) - 0x00070 - Symmetry & Triangles 158058 - 0x00076 (Left 7) - 0x00071 - Symmetry & Triangles -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 +158059 - 0x009B8 (Scenery Outlines 1) - True - Symmetry +158060 - 0x003E8 (Scenery Outlines 2) - 0x009B8 - Symmetry +158061 - 0x00A15 (Scenery Outlines 3) - 0x003E8 - Symmetry +158062 - 0x00B53 (Scenery Outlines 4) - 0x00A15 - Symmetry +158063 - 0x00B8D (Scenery Outlines 5) - 0x00B53 - Symmetry 158064 - 0x1C349 (Upper Panel) - 0x00076 - Symmetry & Triangles Door - 0x18269 (Upper) - 0x1C349 +159000 - 0x0332B (Glass Factory Black Line Reflection EP) - True - True Symmetry Island Upper (Symmetry Island): 158065 - 0x00A52 (Yellow 1) - True - Symmetry & Colored Dots @@ -104,17 +119,18 @@ Symmetry Island Upper (Symmetry Island): 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 +Laser - 0x00509 (Laser) - 0x0360D +159001 - 0x03367 (Glass Factory Black Line EP) - True - 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 +158071 - 0x00143 (Apple Tree 1) - True - True +158072 - 0x0003B (Apple Tree 2) - 0x00143 - True +158073 - 0x00055 (Apple Tree 3) - 0x0003B - True Door - 0x03307 (First Gate) - 0x00055 Orchard Beyond First Gate (Orchard) - Orchard End - 0x03313: -158074 - 0x032F7 (Apple Tree 4) - 0x00055 - Environment -158075 - 0x032FF (Apple Tree 5) - 0x032F7 - Environment +158074 - 0x032F7 (Apple Tree 4) - 0x00055 - True +158075 - 0x032FF (Apple Tree 5) - 0x032F7 - True Door - 0x03313 (Second Gate) - 0x032FF Orchard End (Orchard): @@ -123,36 +139,47 @@ Desert Outside (Desert) - Main Island - True - Desert Floodlight Room - 0x09FEE: 158652 - 0x0CC7B (Vault) - True - Dots & Full Dots & Stars & Stars + Same Colored Symbol & Eraser & Triangles & Shapers & Negative Shapers & Colored Squares 158653 - 0x0339E (Vault Box) - 0x0CC7B - True 158602 - 0x17CE7 (Discard) - True - Arrows -158076 - 0x00698 (Surface 1) - True - Reflection -158077 - 0x0048F (Surface 2) - 0x00698 - Reflection -158078 - 0x09F92 (Surface 3) - 0x0048F & 0x09FA0 - Reflection +158076 - 0x00698 (Surface 1) - True - True +158077 - 0x0048F (Surface 2) - 0x00698 - True +158078 - 0x09F92 (Surface 3) - 0x0048F & 0x09FA0 - True 158079 - 0x09FA0 (Surface 3 Control) - 0x0048F - True -158080 - 0x0A036 (Surface 4) - 0x09F92 - Reflection -158081 - 0x09DA6 (Surface 5) - 0x09F92 - Reflection -158082 - 0x0A049 (Surface 6) - 0x09F92 - Reflection -158083 - 0x0A053 (Surface 7) - 0x0A036 & 0x09DA6 & 0x0A049 - Reflection -158084 - 0x09F94 (Surface 8) - 0x0A053 & 0x09F86 - Reflection +158080 - 0x0A036 (Surface 4) - 0x09F92 - True +158081 - 0x09DA6 (Surface 5) - 0x09F92 - True +158082 - 0x0A049 (Surface 6) - 0x09F92 - True +158083 - 0x0A053 (Surface 7) - 0x0A036 & 0x09DA6 & 0x0A049 - True +158084 - 0x09F94 (Surface 8) - 0x0A053 & 0x09F86 - True 158085 - 0x09F86 (Surface 8 Control) - 0x0A053 - True 158086 - 0x0C339 (Light Room Entry Panel) - 0x09F94 - True -Door - 0x09FEE (Light Room Entry) - 0x0C339 - True +Door - 0x09FEE (Light Room Entry) - 0x0C339 158701 - 0x03608 (Laser Panel) - 0x012D7 & 0x0A15F - True Laser - 0x012FB (Laser) - 0x03608 +159020 - 0x3351D (Sand Snake EP) - True - True +159030 - 0x0053C (Facade Right EP) - True - True +159031 - 0x00771 (Facade Left EP) - True - True +159032 - 0x335C8 (Stairs Left EP) - True - True +159033 - 0x335C9 (Stairs Right EP) - True - True +159036 - 0x220E4 (Broken Wall Straight EP) - True - True +159037 - 0x220E5 (Broken Wall Bend EP) - True - True +159040 - 0x334B9 (Shore EP) - True - True +159041 - 0x334BC (Island EP) - True - True Desert Floodlight Room (Desert) - Desert Pond Room - 0x0C2C3: 158087 - 0x09FAA (Light Control) - True - True -158088 - 0x00422 (Light Room 1) - 0x09FAA - Reflection -158089 - 0x006E3 (Light Room 2) - 0x09FAA - Reflection -158090 - 0x0A02D (Light Room 3) - 0x09FAA & 0x00422 & 0x006E3 - Reflection +158088 - 0x00422 (Light Room 1) - 0x09FAA - True +158089 - 0x006E3 (Light Room 2) - 0x09FAA - True +158090 - 0x0A02D (Light Room 3) - 0x09FAA & 0x00422 & 0x006E3 - True Door - 0x0C2C3 (Pond Room Entry) - 0x0A02D Desert Pond Room (Desert) - Desert Water Levels Room - 0x0A24B: -158091 - 0x00C72 (Pond Room 1) - True - Reflection -158092 - 0x0129D (Pond Room 2) - 0x00C72 - Reflection -158093 - 0x008BB (Pond Room 3) - 0x0129D - Reflection -158094 - 0x0078D (Pond Room 4) - 0x008BB - Reflection -158095 - 0x18313 (Pond Room 5) - 0x0078D - Reflection -158096 - 0x0A249 (Flood Room Entry Panel) - 0x18313 - Reflection +158091 - 0x00C72 (Pond Room 1) - True - True +158092 - 0x0129D (Pond Room 2) - 0x00C72 - True +158093 - 0x008BB (Pond Room 3) - 0x0129D - True +158094 - 0x0078D (Pond Room 4) - 0x008BB - True +158095 - 0x18313 (Pond Room 5) - 0x0078D - True +158096 - 0x0A249 (Flood Room Entry Panel) - 0x18313 - True Door - 0x0A24B (Flood Room Entry) - 0x0A249 +159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True +159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316: 158097 - 0x1C2DF (Reduce Water Level Far Left) - True - True @@ -163,37 +190,47 @@ Desert Water Levels Room (Desert) - Desert Elevator Room - 0x0C316: 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 Room 1) - 0x1C260 & 0x1831C - Reflection -158106 - 0x01205 (Flood Room 2) - 0x04D18 & 0x1C260 & 0x1831C - Reflection -158107 - 0x181AB (Flood Room 3) - 0x01205 & 0x1C260 & 0x1831C - Reflection -158108 - 0x0117A (Flood Room 4) - 0x181AB & 0x1C260 & 0x1831C - Reflection -158109 - 0x17ECA (Flood Room 5) - 0x0117A & 0x1C260 & 0x1831C - Reflection -158110 - 0x18076 (Flood Room 6) - 0x17ECA & 0x1C260 & 0x1831C - Reflection +158105 - 0x04D18 (Flood Room 1) - 0x1C260 & 0x1831C - True +158106 - 0x01205 (Flood Room 2) - 0x04D18 & 0x1C260 & 0x1831C - True +158107 - 0x181AB (Flood Room 3) - 0x01205 & 0x1C260 & 0x1831C - True +158108 - 0x0117A (Flood Room 4) - 0x181AB & 0x1C260 & 0x1831C - True +158109 - 0x17ECA (Flood Room 5) - 0x0117A & 0x1C260 & 0x1831C - True +158110 - 0x18076 (Flood Room 6) - 0x17ECA & 0x1C260 & 0x1831C - True Door - 0x0C316 (Elevator Room Entry) - 0x18076 +159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x012FB: -158111 - 0x17C31 (Final Transparent) - True - Reflection -158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - Reflection +158111 - 0x17C31 (Final Transparent) - True - True +158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True 158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True -158115 - 0x0A15C (Final Bent 1) - True - Reflection -158116 - 0x09FFF (Final Bent 2) - 0x0A15C - Reflection -158117 - 0x0A15F (Final Bent 3) - 0x09FFF - Reflection +158115 - 0x0A15C (Final Bent 1) - True - True +158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True +158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True +159035 - 0x037BB (Elevator EP) - 0x012FB - True Desert Lowest Level Inbetween Shortcuts (Desert): -Outside Quarry (Quarry) - Main Island - True - Quarry Between Entrys - 0x09D6F: +Outside Quarry (Quarry) - Main Island - True - Quarry Between Entrys - 0x09D6F - Quarry Elevator - TrueOneWay: 158118 - 0x09E57 (Entry 1 Panel) - True - Squares & Black/White Squares & Triangles -158120 - 0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser 158603 - 0x17CF0 (Discard) - True - Arrows 158702 - 0x03612 (Laser Panel) - 0x0A3D0 & 0x0367C - Eraser & Triangles & Stars & Stars + Same Colored Symbol Laser - 0x01539 (Laser) - 0x03612 Door - 0x09D6F (Entry 1) - 0x09E57 +159404 - 0x28A4A (Shore EP) - True - True +159410 - 0x334B6 (Entrance Pipe EP) - True - True +159412 - 0x28A4C (Sand Pile EP) - True - True +159420 - 0x289CF (Rock Line EP) - True - True +159421 - 0x289D1 (Rock Line Reflection EP) - True - True + +Quarry Elevator (Quarry): +158120 - 0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser +159403 - 0x17CB9 (Railroad EP) - 0x17CC4 - True Quarry Between Entrys (Quarry) - Quarry - 0x17C07: 158119 - 0x17C09 (Entry 2 Panel) - True - Shapers & Triangles Door - 0x17C07 (Entry 2) - 0x17C09 -Quarry (Quarry) - Quarry Mill Ground Floor - 0x02010: +Quarry (Quarry) - Quarry Mill Ground Floor - 0x02010 - Quarry Elevator - 0x17CC4: 158121 - 0x01E5A (Mill Entry Left Panel) - True - Squares & Black/White Squares & Stars & Stars + Same Colored Symbol 158122 - 0x01E59 (Mill Entry Right Panel) - True - Triangles Door - 0x02010 (Mill Entry) - 0x01E59 & 0x01E5A @@ -229,6 +266,8 @@ Quarry Mill Upper Floor (Quarry Mill) - Quarry Mill Middle Floor - 0x03676 & 0x0 Door - 0x0368A (Stairs) - 0x03677 158143 - 0x3C125 (Control Room Left) - 0x0367C - Squares & Black/White Squares & Dots & Full Dots & Eraser 158144 - 0x0367C (Control Room Right) - 0x014E9 - Squares & Colored Squares & Triangles & Eraser & Stars & Stars + Same Colored Symbol +159411 - 0x0069D (Ramp EP) - 0x03676 & 0x275FF - True +159413 - 0x00614 (Lift EP) - 0x275FF & 0x03675 - True Quarry Boathouse (Quarry Boathouse) - Quarry - True - Quarry Boathouse Upper Front - 0x03852 - Quarry Boathouse Behind Staircase - 0x2769B: 158146 - 0x034D4 (Intro Left) - True - Stars & Eraser @@ -250,6 +289,7 @@ Door - 0x17C50 (First Barrier) - 0x021AE Quarry Boathouse Upper Middle (Quarry Boathouse) - Quarry Boathouse Upper Back - 0x03858: 158154 - 0x03858 (Ramp Horizontal Control) - True - Shapers & Eraser +159402 - 0x00859 (Moving Ramp EP) - 0x03858 & 0x03852 & 0x3865F - True Quarry Boathouse Upper Back (Quarry Boathouse) - Quarry Boathouse Upper Middle - 0x3865F: 158155 - 0x38663 (Second Barrier Panel) - True - True @@ -267,96 +307,119 @@ Door - 0x3865F (Second Barrier) - 0x38663 158167 - 0x0A3CB (Back Second Row 1) - 0x09DB4 - Stars & Eraser & Shapers & Negative Shapers & Stars + Same Colored Symbol 158168 - 0x0A3CC (Back Second Row 2) - 0x0A3CB - Stars & Eraser & Shapers & Negative Shapers & Stars + Same Colored Symbol 158169 - 0x0A3D0 (Back Second Row 3) - 0x0A3CC - Stars & Eraser & Shapers & Negative Shapers & Stars + Same Colored Symbol +159401 - 0x005F6 (Hook EP) - 0x275FA & 0x03852 & 0x3865F - True 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 (Intro 6) - 0x0A8DC - Shadows Avoid -158172 - 0x0AC7A (Intro 7) - 0x0AC74 - Shadows Avoid -158173 - 0x0A8E0 (Intro 8) - 0x0AC7A - Shadows Avoid -158174 - 0x386FA (Far 1) - 0x0A8E0 - Shadows Avoid & Environment -158175 - 0x1C33F (Far 2) - 0x386FA - Shadows Avoid & Environment -158176 - 0x196E2 (Far 3) - 0x1C33F - Shadows Avoid & Environment -158177 - 0x1972A (Far 4) - 0x196E2 - Shadows Avoid & Environment -158178 - 0x19809 (Far 5) - 0x1972A - Shadows Avoid & Environment -158179 - 0x19806 (Far 6) - 0x19809 - Shadows Avoid & Environment -158180 - 0x196F8 (Far 7) - 0x19806 - Shadows Avoid & Environment -158181 - 0x1972F (Far 8) - 0x196F8 - Shadows Avoid & Environment +158171 - 0x0AC74 (Intro 6) - 0x0A8DC - True +158172 - 0x0AC7A (Intro 7) - 0x0AC74 - True +158173 - 0x0A8E0 (Intro 8) - 0x0AC7A - True +158174 - 0x386FA (Far 1) - 0x0A8E0 - True +158175 - 0x1C33F (Far 2) - 0x386FA - True +158176 - 0x196E2 (Far 3) - 0x1C33F - True +158177 - 0x1972A (Far 4) - 0x196E2 - True +158178 - 0x19809 (Far 5) - 0x1972A - True +158179 - 0x19806 (Far 6) - 0x19809 - True +158180 - 0x196F8 (Far 7) - 0x19806 - True +158181 - 0x1972F (Far 8) - 0x196F8 - True Door - 0x194B2 (Laser Entry Right) - 0x1972F -158182 - 0x19797 (Near 1) - 0x0A8E0 - Shadows Follow -158183 - 0x1979A (Near 2) - 0x19797 - Shadows Follow -158184 - 0x197E0 (Near 3) - 0x1979A - Shadows Follow -158185 - 0x197E8 (Near 4) - 0x197E0 - Shadows Follow -158186 - 0x197E5 (Near 5) - 0x197E8 - Shadows Follow +158182 - 0x19797 (Near 1) - 0x0A8E0 - True +158183 - 0x1979A (Near 2) - 0x19797 - True +158184 - 0x197E0 (Near 3) - 0x1979A - True +158185 - 0x197E8 (Near 4) - 0x197E0 - True +158186 - 0x197E5 (Near 5) - 0x197E8 - True Door - 0x19665 (Laser Entry Left) - 0x197E5 +159400 - 0x28A7B (Quarry Mill Rooftop Vent EP) - True - True Shadows Ledge (Shadows) - Shadows - 0x1855B - Quarry - 0x19865 & 0x0A2DF: 158187 - 0x334DC (Door Timer Inside) - True - True -158188 - 0x198B5 (Intro 1) - True - Shadows Avoid -158189 - 0x198BD (Intro 2) - 0x198B5 - Shadows Avoid -158190 - 0x198BF (Intro 3) - 0x198BD & 0x334DC & 0x19B24 - Shadows Avoid +158188 - 0x198B5 (Intro 1) - True - True +158189 - 0x198BD (Intro 2) - 0x198B5 - True +158190 - 0x198BF (Intro 3) - 0x198BD & 0x334DC & 0x19B24 - True Door - 0x19865 (Quarry Barrier) - 0x198BF Door - 0x0A2DF (Quarry Barrier 2) - 0x198BF -158191 - 0x19771 (Intro 4) - 0x198BF - Shadows Avoid -158192 - 0x0A8DC (Intro 5) - 0x19771 - Shadows Avoid +158191 - 0x19771 (Intro 4) - 0x198BF - True +158192 - 0x0A8DC (Intro 5) - 0x19771 - True Door - 0x1855B (Ledge Barrier) - 0x0A8DC Door - 0x19ADE (Ledge Barrier 2) - 0x0A8DC Shadows Laser Room (Shadows): -158703 - 0x19650 (Laser Panel) - True - Shadows Avoid & Shadows Follow +158703 - 0x19650 (Laser Panel) - True - True Laser - 0x181B3 (Laser) - 0x19650 +Treehouse Beach (Treehouse Beach) - Main Island - True: +159200 - 0x0053D (Rock Shadow EP) - True - True +159201 - 0x0053E (Sand Shadow EP) - True - True +159212 - 0x220BD (Both Orange Bridges EP) - 0x17DA2 & 0x17DDB - True + Keep (Keep) - Main Island - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC: -158193 - 0x00139 (Hedge Maze 1) - True - Environment +158193 - 0x00139 (Hedge Maze 1) - True - True 158197 - 0x0A3A8 (Reset Pressure Plates 1) - True - True -158198 - 0x033EA (Pressure Plates 1) - 0x0A3A8 - Pressure Plates & Colored Squares & Triangles & Stars & Stars + Same Colored Symbol +158198 - 0x033EA (Pressure Plates 1) - 0x0A3A8 - Colored Squares & Triangles & Stars & Stars + Same Colored Symbol Door - 0x01954 (Hedge Maze 1 Exit) - 0x00139 Door - 0x01BEC (Pressure Plates 1 Exit) - 0x033EA +159430 - 0x03E77 (Red Flowers EP) - True - True +159431 - 0x03E7C (Purple Flowers EP) - 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 +158194 - 0x019DC (Hedge Maze 2) - True - True Door - 0x019D8 (Hedge Maze 2 Exit) - 0x019DC 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 +158195 - 0x019E7 (Hedge Maze 3) - True - True Door - 0x019E6 (Hedge Maze 3 Exit) - 0x019E7 Keep 4th Maze (Keep) - Keep - 0x0199A - Keep Tower - 0x01A0E: Door - 0x0199A (Hedge Maze 4 Shortcut) - 0x019E7 -158196 - 0x01A0F (Hedge Maze 4) - True - Environment +158196 - 0x01A0F (Hedge Maze 4) - True - True Door - 0x01A0E (Hedge Maze 4 Exit) - 0x01A0F Keep 2nd Pressure Plate (Keep) - Keep 3rd Pressure Plate - True: 158199 - 0x0A3B9 (Reset Pressure Plates 2) - True - True -158200 - 0x01BE9 (Pressure Plates 2) - PP2 Weirdness - Pressure Plates & Stars & Stars + Same Colored Symbol & Squares & Black/White Squares & Shapers & Rotated Shapers +158200 - 0x01BE9 (Pressure Plates 2) - PP2 Weirdness - Stars & Stars + Same Colored Symbol & Squares & Black/White Squares & Shapers & Rotated Shapers Door - 0x01BEA (Pressure Plates 2 Exit) - 0x01BE9 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 & Black/White Squares & Triangles & Shapers & Rotated Shapers +158202 - 0x01CD3 (Pressure Plates 3) - 0x0A3BB - Black/White Squares & Triangles & Shapers & Rotated Shapers Door - 0x01CD5 (Pressure Plates 3 Exit) - 0x01CD3 Keep 4th Pressure Plate (Keep) - Shadows - 0x09E3D - Keep Tower - 0x01D40: 158203 - 0x0A3AD (Reset Pressure Plates 4) - True - True -158204 - 0x01D3F (Pressure Plates 4) - 0x0A3AD - Pressure Plates & Shapers & Triangles & Stars & Stars + Same Colored Symbol +158204 - 0x01D3F (Pressure Plates 4) - 0x0A3AD - Shapers & Triangles & Stars & Stars + Same Colored Symbol Door - 0x01D40 (Pressure Plates 4 Exit) - 0x01D3F 158604 - 0x17D27 (Discard) - True - Arrows 158205 - 0x09E49 (Shadows Shortcut Panel) - True - True Door - 0x09E3D (Shadows Shortcut) - 0x09E49 Shipwreck (Shipwreck) - Keep 3rd Pressure Plate - True: -158654 - 0x00AFB (Vault) - True - Symmetry & Sound & Sound Dots & Colored Dots +158654 - 0x00AFB (Vault) - True - Symmetry & Sound Dots & Colored Dots 158655 - 0x03535 (Vault Box) - 0x00AFB - True 158605 - 0x17D28 (Discard) - True - Arrows +159220 - 0x03B22 (Circle Far EP) - True - True +159221 - 0x03B23 (Circle Left EP) - True - True +159222 - 0x03B24 (Circle Near EP) - True - True +159224 - 0x03A79 (Stern EP) - True - True +159225 - 0x28ABD (Rope Inner EP) - True - True +159226 - 0x28ABE (Rope Outer EP) - True - True +159230 - 0x3388F (Couch EP) - 0x17CDF | 0x0A054 - True Keep Tower (Keep) - Keep - 0x04F8F: 158206 - 0x0361B (Tower Shortcut Panel) - True - True Door - 0x04F8F (Tower Shortcut) - 0x0361B -158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - Environment & Sound +158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True 158705 - 0x03317 (Laser Panel Pressure Plates) - 0x01BE9 - Shapers & Rotated Shapers & Triangles & Stars & Stars + Same Colored Symbol & Colored Squares & Black/White Squares Laser - 0x014BB (Laser) - 0x0360E | 0x03317 +159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True +159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True +159242 - 0x033DD (Pressure Plates 3 EP) - 0x01CD3 & 0x01CD5 - True +159243 - 0x033E5 (Pressure Plates 4 Left Exit EP) - 0x01D3F - True +159244 - 0x018B6 (Pressure Plates 4 Right Exit EP) - 0x01D3F - True +159250 - 0x28AE9 (Path EP) - True - True +159251 - 0x3348F (Hedges EP) - True - True Outside Monastery (Monastery) - Main Island - True - Inside Monastery - 0x0C128 & 0x0C153 - Monastery Garden - 0x03750: 158207 - 0x03713 (Shortcut Panel) - True - True @@ -365,19 +428,29 @@ Door - 0x0364E (Shortcut) - 0x03713 158209 - 0x00C92 (Entry Right) - True - True Door - 0x0C128 (Entry Inner) - 0x00B10 Door - 0x0C153 (Entry Outer) - 0x00C92 -158210 - 0x00290 (Outside 1) - 0x09D9B - Environment -158211 - 0x00038 (Outside 2) - 0x09D9B & 0x00290 - Environment -158212 - 0x00037 (Outside 3) - 0x09D9B & 0x00038 - Environment +158210 - 0x00290 (Outside 1) - 0x09D9B - True +158211 - 0x00038 (Outside 2) - 0x09D9B & 0x00290 - True +158212 - 0x00037 (Outside 3) - 0x09D9B & 0x00038 - True Door - 0x03750 (Garden Entry) - 0x00037 158706 - 0x17CA4 (Laser Panel) - 0x193A6 - True Laser - 0x17C65 (Laser) - 0x17CA4 +159130 - 0x006E5 (Facade Left Near EP) - True - True +159131 - 0x006E6 (Facade Left Far Short EP) - True - True +159132 - 0x006E7 (Facade Left Far Long EP) - True - True +159136 - 0x03DAB (Facade Right Near EP) - True - True +159137 - 0x03DAC (Facade Left Stairs EP) - True - True +159138 - 0x03DAD (Facade Right Stairs EP) - True - True +159140 - 0x03E01 (Grass Stairs EP) - True - True Inside Monastery (Monastery): 158213 - 0x09D9B (Shutters Control) - True - Dots -158214 - 0x193A7 (Inside 1) - 0x00037 - Environment -158215 - 0x193AA (Inside 2) - 0x193A7 - Environment -158216 - 0x193AB (Inside 3) - 0x193AA - Environment -158217 - 0x193A6 (Inside 4) - 0x193AB - Environment +158214 - 0x193A7 (Inside 1) - 0x00037 - True +158215 - 0x193AA (Inside 2) - 0x193A7 - True +158216 - 0x193AB (Inside 3) - 0x193AA - True +158217 - 0x193A6 (Inside 4) - 0x193AB - True +159133 - 0x034A7 (Left Shutter EP) - 0x09D9B - True +159134 - 0x034AD (Middle Shutter EP) - 0x09D9B - True +159135 - 0x034AF (Right Shutter EP) - 0x09D9B - True Monastery Garden (Monastery): @@ -386,10 +459,10 @@ Town (Town) - Main Island - True - Boat - 0x0A054 - Town Maze Rooftop - 0x28AA2 158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Squares & Black/White Squares & Shapers & Triangles Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8 158707 - 0x09F98 (Desert Laser Redirect) - True - True -158220 - 0x18590 (Transparent) - True - Symmetry & Environment -158221 - 0x28AE3 (Vines) - 0x18590 - Shadows Follow & Environment -158222 - 0x28938 (Apple Tree) - 0x28AE3 - Environment -158223 - 0x079DF (Triple Exit) - 0x28938 - Shadows Avoid & Environment & Reflection +158220 - 0x18590 (Transparent) - True - Symmetry +158221 - 0x28AE3 (Vines) - 0x18590 - True +158222 - 0x28938 (Apple Tree) - 0x28AE3 - True +158223 - 0x079DF (Triple Exit) - 0x28938 - True 158235 - 0x2899C (Wooden Roof Lower Row 1) - True - Triangles & Dots & Full Dots 158236 - 0x28A33 (Wooden Roof Lower Row 2) - 0x2899C - Triangles & Dots & Full Dots 158237 - 0x28ABF (Wooden Roof Lower Row 3) - 0x28A33 - Triangles & Dots & Full Dots @@ -398,18 +471,30 @@ Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8 Door - 0x034F5 (Wooden Roof Stairs) - 0x28AC1 158225 - 0x28998 (Tinted Glass Door Panel) - True - Stars & Rotated Shapers & Stars + Same Colored Symbol Door - 0x28A61 (Tinted Glass Door) - 0x28A0D -158226 - 0x28A0D (Church Entry Panel) - 0x28998 - Stars & RGB & Environment +158226 - 0x28A0D (Church Entry Panel) - 0x28998 - Stars Door - 0x03BB0 (Church Entry) - 0x03C08 -158228 - 0x28A79 (Maze Stair Control) - True - Environment +158228 - 0x28A79 (Maze Stair Control) - True - True Door - 0x28AA2 (Maze Stairs) - 0x28A79 158241 - 0x17F5F (Windmill Entry Panel) - True - Dots Door - 0x1845B (Windmill Entry) - 0x17F5F +159010 - 0x037B6 (Windmill First Blade EP) - 0x17D02 - True +159011 - 0x037B2 (Windmill Second Blade EP) - 0x17D02 - True +159012 - 0x000F7 (Windmill Third Blade EP) - 0x17D02 - True +159540 - 0x03335 (Tower Underside First EP) - True - True +159541 - 0x03412 (Tower Underside Second EP) - True - True +159542 - 0x038A6 (Tower Underside Third EP) - True - True +159543 - 0x038AA (Tower Underside Fourth EP) - True - True +159545 - 0x03E40 (RGB House Green EP) - 0x334D8 & 0x03C0C & 0x03C08 - True +159546 - 0x28B8E (Maze Bridge Underside EP) - 0x2896A - True +159552 - 0x03BCF (Black Line Redirect EP) - True - True +159800 - 0xFFF80 (Pet the Dog) - True - True Town Inside Cargo Box (Town): 158606 - 0x17D01 (Cargo Box Discard) - True - Arrows Town Maze Rooftop (Town) - Town Red Rooftop - 0x2896A: 158229 - 0x2896A (Maze Rooftop Bridge Control) - True - Shapers +159544 - 0x03E3F (RGB House Red EP) - 0x334D8 & 0x03C0C & 0x03C08 - True Town Red Rooftop (Town): 158607 - 0x17C71 (Rooftop Discard) - True - Arrows @@ -418,23 +503,24 @@ Town Red Rooftop (Town): 158232 - 0x28ACA (Red Rooftop 3) - 0x28AC8 - Symmetry & Shapers 158233 - 0x28ACB (Red Rooftop 4) - 0x28ACA - Symmetry & Shapers 158234 - 0x28ACC (Red Rooftop 5) - 0x28ACB - Symmetry & Shapers -158224 - 0x28B39 (Tall Hexagonal) - 0x079DF - Reflection +158224 - 0x28B39 (Tall Hexagonal) - 0x079DF - True Town Wooden Rooftop (Town): 158240 - 0x28AD9 (Wooden Rooftop) - 0x28AC1 - Triangles & Dots & Full Dots & Eraser Town Church (Town): -158227 - 0x28A69 (Church Lattice) - 0x03BB0 - Environment +158227 - 0x28A69 (Church Lattice) - 0x03BB0 - True +159553 - 0x03BD1 (Black Line Church EP) - True - True RGB House (Town) - RGB Room - 0x2897B: -158242 - 0x034E4 (Sound Room Left) - True - Sound -158243 - 0x034E3 (Sound Room Right) - True - Sound & Sound Dots +158242 - 0x034E4 (Sound Room Left) - True - True +158243 - 0x034E3 (Sound Room Right) - True - Sound Dots Door - 0x2897B (RGB House Stairs) - 0x034E4 & 0x034E3 RGB Room (Town): -158244 - 0x334D8 (RGB Control) - True - Rotated Shapers & RGB & Squares & Colored Squares & Triangles -158245 - 0x03C0C (RGB Room Left) - 0x334D8 - RGB & Squares & Colored Squares & Black/White Squares & Eraser -158246 - 0x03C08 (RGB Room Right) - 0x334D8 & 0x03C0C - RGB & Symmetry & Dots & Colored Dots & Triangles +158244 - 0x334D8 (RGB Control) - True - Rotated Shapers & Squares & Colored Squares & Triangles +158245 - 0x03C0C (RGB Room Left) - 0x334D8 - Squares & Colored Squares & Black/White Squares & Eraser +158246 - 0x03C08 (RGB Room Right) - 0x334D8 & 0x03C0C - Symmetry & Dots & Colored Dots & Triangles Town Tower (Town Tower) - Town - True - Town Tower Top - 0x27798 & 0x27799 & 0x2779A & 0x2779C: Door - 0x27798 (First Door) - 0x28ACC @@ -445,6 +531,8 @@ Door - 0x2779A (Fourth Door) - 0x28B39 Town Tower Top (Town): 158708 - 0x032F5 (Laser Panel) - True - True Laser - 0x032F9 (Laser) - 0x032F5 +159422 - 0x33692 (Brown Bridge EP) - True - True +159551 - 0x03BCE (Black Line Tower EP) - True - True Windmill Interior (Windmill) - Theater - 0x17F88: 158247 - 0x17D02 (Turn Control) - True - Dots @@ -464,35 +552,51 @@ Theater (Theater) - Town - 0x0A16D | 0x3CCDF: Door - 0x0A16D (Exit Left) - 0x0A168 Door - 0x3CCDF (Exit Right) - 0x33AB2 158608 - 0x17CF7 (Discard) - True - Arrows +159554 - 0x339B6 (Eclipse EP) - 0x03549 & 0x0A16D & 0x3CCDF - True +159555 - 0x33A29 (Window EP) - 0x03553 - True +159556 - 0x33A2A (Door EP) - 0x03553 - True +159558 - 0x33B06 (Church EP) - 0x0354E - True Jungle (Jungle) - Main Island - True - Outside Jungle River - 0x3873B - Boat - 0x17CDF: 158251 - 0x17CDF (Shore Boat Spawn) - True - Boat 158609 - 0x17F9B (Discard) - True - Arrows -158252 - 0x002C4 (First Row 1) - True - Sound -158253 - 0x00767 (First Row 2) - 0x002C4 - Sound -158254 - 0x002C6 (First Row 3) - 0x00767 - Sound -158255 - 0x0070E (Second Row 1) - 0x002C6 - Sound -158256 - 0x0070F (Second Row 2) - 0x0070E - Sound -158257 - 0x0087D (Second Row 3) - 0x0070F - Sound -158258 - 0x002C7 (Second Row 4) - 0x0087D - Sound +158252 - 0x002C4 (First Row 1) - True - True +158253 - 0x00767 (First Row 2) - 0x002C4 - True +158254 - 0x002C6 (First Row 3) - 0x00767 - True +158255 - 0x0070E (Second Row 1) - 0x002C6 - True +158256 - 0x0070F (Second Row 2) - 0x0070E - True +158257 - 0x0087D (Second Row 3) - 0x0070F - True +158258 - 0x002C7 (Second Row 4) - 0x0087D - True 158259 - 0x17CAB (Popup Wall Control) - 0x002C7 - True Door - 0x1475B (Popup Wall) - 0x17CAB -158260 - 0x0026D (Popup Wall 1) - 0x1475B - Sound & Sound Dots & Symmetry -158261 - 0x0026E (Popup Wall 2) - 0x0026D - Sound & Sound Dots & Symmetry -158262 - 0x0026F (Popup Wall 3) - 0x0026E - Sound & Sound Dots & Symmetry -158263 - 0x00C3F (Popup Wall 4) - 0x0026F - Sound & Sound Dots & Symmetry -158264 - 0x00C41 (Popup Wall 5) - 0x00C3F - Sound & Sound Dots & Symmetry -158265 - 0x014B2 (Popup Wall 6) - 0x00C41 - Sound & Sound Dots & Symmetry +158260 - 0x0026D (Popup Wall 1) - 0x1475B - Sound Dots & Symmetry +158261 - 0x0026E (Popup Wall 2) - 0x0026D - Sound Dots & Symmetry +158262 - 0x0026F (Popup Wall 3) - 0x0026E - Sound Dots & Symmetry +158263 - 0x00C3F (Popup Wall 4) - 0x0026F - Sound Dots & Symmetry +158264 - 0x00C41 (Popup Wall 5) - 0x00C3F - Sound Dots & Symmetry +158265 - 0x014B2 (Popup Wall 6) - 0x00C41 - Sound Dots & Symmetry 158709 - 0x03616 (Laser Panel) - 0x014B2 - True Laser - 0x00274 (Laser) - 0x03616 158266 - 0x337FA (Laser Shortcut Panel) - True - True Door - 0x3873B (Laser Shortcut) - 0x337FA +159100 - 0x03ABC (Long Arch Moss EP) - True - True +159101 - 0x03ABE (Straight Left Moss EP) - True - True +159102 - 0x03AC0 (Pop-up Wall Moss EP) - True - True +159103 - 0x03AC4 (Short Arch Moss EP) - True - True +159150 - 0x289F4 (Entrance EP) - True - True +159151 - 0x289F5 (Tree Halo EP) - True - True +159350 - 0x035CB (Bamboo CCW EP) - True - True +159351 - 0x035CF (Bamboo CW EP) - True - True Outside Jungle River (River) - Main Island - True - Monastery Garden - 0x0CF2A: -158267 - 0x17CAA (Monastery Shortcut Panel) - True - Environment +158267 - 0x17CAA (Monastery Shortcut Panel) - True - True Door - 0x0CF2A (Monastery Shortcut) - 0x17CAA -158663 - 0x15ADD (Vault) - True - Environment & Black/White Squares & Dots +158663 - 0x15ADD (Vault) - True - Black/White Squares & Dots 158664 - 0x03702 (Vault Box) - 0x15ADD - True +159110 - 0x03AC5 (Green Leaf Moss EP) - True - True +159120 - 0x03BE2 (Monastery Garden Left EP) - 0x03750 - True +159121 - 0x03BE3 (Monastery Garden Right EP) - True - True +159122 - 0x0A409 (Monastery Wall EP) - True - True Outside Bunker (Bunker) - Main Island - True - Bunker - 0x0C2A4: 158268 - 0x17C2E (Entry Panel) - True - Squares & Black/White Squares & Colored Squares @@ -512,28 +616,37 @@ Bunker (Bunker) - Bunker Glass Room - 0x17C79: Door - 0x17C79 (Tinted Glass Door) - 0x0A099 Bunker Glass Room (Bunker) - Bunker Ultraviolet Room - 0x0C2A3: -158279 - 0x0A010 (Glass Room 1) - True - Squares & Colored Squares & RGB & Environment -158280 - 0x0A01B (Glass Room 2) - 0x0A010 - Squares & Colored Squares & Black/White Squares & RGB & Environment -158281 - 0x0A01F (Glass Room 3) - 0x0A01B - Squares & Colored Squares & Black/White Squares & RGB & Environment +158279 - 0x0A010 (Glass Room 1) - True - Squares & Colored Squares +158280 - 0x0A01B (Glass Room 2) - 0x0A010 - Squares & Colored Squares & Black/White Squares +158281 - 0x0A01F (Glass Room 3) - 0x0A01B - Squares & Colored Squares & Black/White Squares Door - 0x0C2A3 (UV Room Entry) - 0x0A01F Bunker Ultraviolet Room (Bunker) - Bunker Elevator Section - 0x0A08D: 158282 - 0x34BC5 (Drop-Down Door Open) - True - True 158283 - 0x34BC6 (Drop-Down Door Close) - 0x34BC5 - True -158284 - 0x17E63 (UV Room 1) - 0x34BC5 - Squares & Colored Squares & RGB & Environment -158285 - 0x17E67 (UV Room 2) - 0x17E63 & 0x34BC6 - Squares & Colored Squares & Black/White Squares & RGB +158284 - 0x17E63 (UV Room 1) - 0x34BC5 - Squares & Colored Squares +158285 - 0x17E67 (UV Room 2) - 0x17E63 & 0x34BC6 - Squares & Colored Squares & Black/White Squares Door - 0x0A08D (Elevator Room Entry) - 0x17E67 -Bunker Elevator Section (Bunker) - Bunker Laser Platform - 0x0A079: -158286 - 0x0A079 (Elevator Control) - True - Squares & Colored Squares & Black/White Squares & RGB +Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay: +159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True -Bunker Laser Platform (Bunker): +Bunker Elevator (Bunker) - Bunker Laser Platform - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079: +158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares + +Bunker Green Room (Bunker) - Bunker Elevator - TrueOneWay: +159310 - 0x000D3 (Green Room Flowers EP) - True - True + +Bunker Laser Platform (Bunker) - Bunker Elevator - TrueOneWay: 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 - Rotated Shapers & Black/White Squares & Triangles Door - 0x00C1C (Entry) - 0x0056E +159321 - 0x03603 (Purple Sand Middle EP) - 0x17E2B - True +159322 - 0x03601 (Purple Sand Top EP) - 0x17E2B - True +159327 - 0x035DE (Purple Sand Bottom EP) - True - True Swamp Entry Area (Swamp) - Swamp Sliding Bridge - TrueOneWay: 158288 - 0x00469 (Intro Front 1) - True - Black/White Squares & Shapers @@ -553,6 +666,8 @@ Swamp Entry Area (Swamp) - Swamp Sliding Bridge - TrueOneWay: Swamp Sliding Bridge (Swamp) - Swamp Entry Area - 0x00609 - Swamp Near Platform - 0x00609: 158302 - 0x00609 (Sliding Bridge) - True - Shapers & Black/White Squares +159342 - 0x0105D (Sliding Bridge Left EP) - 0x00609 - True +159343 - 0x0A304 (Sliding Bridge Right EP) - 0x00609 - True Swamp Near Platform (Swamp) - Swamp Cyan Underwater - 0x04B7F - Swamp Near Boat - 0x38AE6 - Swamp Between Bridges Near - 0x184B7 - Swamp Sliding Bridge - TrueOneWay: 158313 - 0x00982 (Platform Row 1) - True - Rotated Shapers @@ -572,6 +687,7 @@ Swamp Cyan Underwater (Swamp): 158310 - 0x013E6 (Cyan Underwater 4) - 0x00005 - Shapers & Negative Shapers & Triangles & Black/White Squares 158311 - 0x00596 (Cyan Underwater 5) - 0x013E6 - Shapers & Negative Shapers & Triangles & Black/White Squares 158312 - 0x18488 (Cyan Underwater Sliding Bridge Control) - True - Shapers +159340 - 0x03AA6 (Cyan Underwater Sliding Bridge EP) - 0x18488 - True Swamp Between Bridges Near (Swamp) - Swamp Between Bridges Far - 0x18507: 158303 - 0x00999 (Between Bridges Near Row 1) - 0x00990 - Rotated Shapers @@ -596,6 +712,8 @@ Door - 0x305D5 (Red Underwater Exit) - 0x014D1 Swamp Rotating Bridge (Swamp) - Swamp Between Bridges Far - 0x181F5 - Swamp Near Boat - 0x181F5 - Swamp Purple Area - 0x181F5: 158327 - 0x181F5 (Rotating Bridge) - True - Rotated Shapers & Shapers & Stars & Colored Squares & Triangles & Stars + Same Colored Symbol +159331 - 0x016B2 (Rotating Bridge CCW EP) - 0x181F5 - True +159334 - 0x036CE (Rotating Bridge CW EP) - 0x181F5 - True Swamp Near Boat (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Blue Underwater - 0x18482: 158328 - 0x09DB8 (Boat Spawn) - True - Boat @@ -605,12 +723,16 @@ Swamp Near Boat (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Blue Underw 158332 - 0x00E3A (Beyond Rotating Bridge 4) - 0x00C2E - Shapers & Dots & Full Dots 158339 - 0x17E2B (Long Bridge Control) - True - Rotated Shapers & Shapers Door - 0x18482 (Blue Water Pump) - 0x00E3A +159332 - 0x3365F (Boat EP) - 0x09DB8 - True +159333 - 0x03731 (Long Bridge Side EP) - 0x17E2B - True Swamp Purple Area (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Purple Underwater - 0x0A1D6: Door - 0x0A1D6 (Purple Water Pump) - 0x00E3A Swamp Purple Underwater (Swamp): 158333 - 0x009A6 (Purple Underwater) - True - Shapers & Triangles & Black/White Squares & Rotated Shapers & Dots & Full Dots +159330 - 0x03A9E (Purple Underwater Right EP) - True - True +159336 - 0x03A93 (Purple Underwater Left EP) - True - True Swamp Blue Underwater (Swamp): 158334 - 0x009AB (Blue Underwater 1) - True - Shapers & Negative Shapers @@ -620,8 +742,8 @@ Swamp Blue Underwater (Swamp): 158338 - 0x00006 (Blue Underwater 5) - 0x009AF - Shapers & 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 +158340 - 0x17C0A (Maze Control) - True - Shapers & Negative Shapers & Rotated Shapers +158112 - 0x17E07 (Maze Control Other Side) - True - Shapers & Negative Shapers & Rotated Shapers Swamp Laser Area (Swamp) - Outside Swamp - 0x2D880: 158711 - 0x03615 (Laser Panel) - True - True @@ -634,6 +756,7 @@ Treehouse Entry Area (Treehouse) - Treehouse Between Doors - 0x0C309: 158343 - 0x17C95 (Boat Spawn) - True - Boat 158344 - 0x0288C (First Door Panel) - True - Stars & Stars + Same Colored Symbol & Triangles Door - 0x0C309 (First Door) - 0x0288C +159210 - 0x33721 (Buoy EP) - 0x17C95 - True Treehouse Between Doors (Treehouse) - Treehouse Yellow Bridge - 0x0C310: 158345 - 0x02886 (Second Door Panel) - True - Stars & Stars + Same Colored Symbol & Triangles @@ -691,7 +814,7 @@ Treehouse Second Purple Bridge (Treehouse) - Treehouse Left Orange Bridge - 0x17 158367 - 0x17D91 (Second Purple Bridge 6) - 0x17BDF - Stars & Colored Squares & Triangles & Stars + Same Colored Symbol 158368 - 0x17DC6 (Second Purple Bridge 7) - 0x17D91 - Stars & Colored Squares & Triangles & Stars + Same Colored Symbol -Treehouse Left Orange Bridge (Treehouse) - Treehouse Laser Room Front Platform - 0x17DDE - Treehouse Laser Room Back Platform - 0x17DDB: +Treehouse Left Orange Bridge (Treehouse) - Treehouse Laser Room Front Platform - 0x17DDE - Treehouse Laser Room Back Platform - 0x17DDB - Treehouse Burned House - 0x17DDB: 158376 - 0x17DB3 (Left Orange Bridge 1) - True - Stars & Black/White Squares & Stars + Same Colored Symbol & Shapers & Rotated Shapers 158377 - 0x17DB5 (Left Orange Bridge 2) - 0x17DB3 - Stars & Black/White Squares & Stars + Same Colored Symbol & Shapers & Rotated Shapers 158378 - 0x17DB6 (Left Orange Bridge 3) - 0x17DB5 - Stars & Black/White Squares & Stars + Same Colored Symbol & Shapers & Rotated Shapers @@ -708,7 +831,7 @@ Treehouse Left Orange Bridge (Treehouse) - Treehouse Laser Room Front Platform - 158389 - 0x17DB0 (Left Orange Bridge 14) - 0x17DAE - Stars & Black/White Squares & Stars + Same Colored Symbol 158390 - 0x17DDB (Left Orange Bridge 15) - 0x17DB0 - Stars & Black/White Squares & Stars + Same Colored Symbol -Treehouse Green Bridge (Treehouse): +Treehouse Green Bridge (Treehouse) - Treehouse Green Bridge Front House - 0x17E61 - Treehouse Green Bridge Left House - 0x17E61: 158369 - 0x17E3C (Green Bridge 1) - True - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol 158370 - 0x17E4D (Green Bridge 2) - 0x17E3C - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol 158371 - 0x17E4F (Green Bridge 3) - 0x17E4D - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol @@ -716,7 +839,12 @@ Treehouse Green Bridge (Treehouse): 158373 - 0x17E5B (Green Bridge 5) - 0x17E52 - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol 158374 - 0x17E5F (Green Bridge 6) - 0x17E5B - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol & Triangles 158375 - 0x17E61 (Green Bridge 7) - 0x17E5F - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol & Triangles -158610 - 0x17FA9 (Green Bridge Discard) - 0x17E61 - Arrows + +Treehouse Green Bridge Front House (Treehouse): +158610 - 0x17FA9 (Green Bridge Discard) - True - Arrows + +Treehouse Green Bridge Left House (Treehouse): +159211 - 0x220A7 (Right Orange Bridge EP) - 0x17DA2 - True Treehouse Laser Room Front Platform (Treehouse) - Treehouse Laser Room - 0x0C323: Door - 0x0C323 (Laser House Entry) - 0x17DA2 & 0x2700B & 0x17DEC @@ -724,6 +852,9 @@ Door - 0x0C323 (Laser House Entry) - 0x17DA2 & 0x2700B & 0x17DEC Treehouse Laser Room Back Platform (Treehouse): 158611 - 0x17FA0 (Laser Discard) - True - Arrows +Treehouse Burned House (Treehouse): +159202 - 0x00769 (Burned House Beach EP) - True - True + Treehouse Laser Room (Treehouse): 158712 - 0x03613 (Laser Panel) - True - True 158403 - 0x17CBC (Laser House Door Timer Inside) - True - True @@ -733,12 +864,19 @@ Mountainside (Mountainside) - Main Island - True - Mountaintop - True: 158612 - 0x17C42 (Discard) - True - Arrows 158665 - 0x002A6 (Vault) - True - Symmetry & Colored Squares & Triangles & Stars & Stars + Same Colored Symbol 158666 - 0x03542 (Vault Box) - 0x002A6 - True +159301 - 0x335AE (Cloud Cycle EP) - True - True +159325 - 0x33505 (Bush EP) - True - True +159335 - 0x03C07 (Apparent River EP) - True - True Mountaintop (Mountaintop) - 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 & Black/White Squares & Stars + Same Colored Symbol & Triangles 158800 - 0xFFF00 (Box Long) - 7 Lasers & 11 Lasers & 0x17C34 - True +159300 - 0x001A3 (River Shape EP) - True - True +159320 - 0x3370E (Arch Black EP) - True - True +159324 - 0x336C8 (Arch White Right EP) - True - True +159326 - 0x3369A (Arch White Left EP) - True - True Mountain Top Layer (Mountain Floor 1) - Mountain Top Layer Bridge - 0x09E39: 158408 - 0x09E39 (Light Bridge Controller) - True - Eraser & Triangles @@ -763,7 +901,7 @@ Mountain Top Layer Bridge (Mountain Floor 1) - Mountain Floor 2 - 0x09E54: 158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Rotated Shapers & Triangles Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B -Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Blue Bridge - 0x09E86: +Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Blue Bridge - 0x09E86 - Mountain Pink Bridge EP - TrueOneWay: 158426 - 0x09FD3 (Near Row 1) - True - Stars & Colored Squares & Stars + Same Colored Symbol 158427 - 0x09FD4 (Near Row 2) - 0x09FD3 - Stars & Triangles & Stars + Same Colored Symbol 158428 - 0x09FD6 (Near Row 3) - 0x09FD4 - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol @@ -779,7 +917,7 @@ Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86 Mountain Floor 2 Light Bridge Room Near (Mountain Floor 2): 158431 - 0x09E86 (Light Bridge Controller Near) - True - Shapers & Dots -Mountain Floor 2 Beyond Bridge (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Far - 0x09E07: +Mountain Floor 2 Beyond Bridge (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Far - 0x09E07 - Mountain Pink Bridge EP - TrueOneWay: 158432 - 0x09FCC (Far Row 1) - True - Triangles 158433 - 0x09FCE (Far Row 2) - 0x09FCC - Black/White Squares & Stars & Stars + Same Colored Symbol 158434 - 0x09FCF (Far Row 3) - 0x09FCE - Stars & Triangles & Stars + Same Colored Symbol @@ -805,11 +943,16 @@ Mountain Third Layer (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueO 158444 - 0x09FDA (Giant Puzzle) - 0x09FC1 & 0x09F8E & 0x09F01 & 0x09EFF - Shapers & Symmetry Door - 0x09F89 (Exit) - 0x09FDA -Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Bottom Floor Rock - 0x17FA2 - Final Room - 0x0C141: +Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Bottom Floor Rock - 0x17FA2 - Final Room - 0x0C141 - Mountain Pink Bridge EP - TrueOneWay: 158614 - 0x17FA2 (Discard) - 0xFFF00 - Arrows 158445 - 0x01983 (Final Room Entry Left) - True - Shapers & Stars 158446 - 0x01987 (Final Room Entry Right) - True - Squares & Colored Squares & Dots Door - 0x0C141 (Final Room Entry) - 0x01983 & 0x01987 +159313 - 0x09D5D (Yellow Bridge EP) - 0x09E86 & 0x09ED8 - True +159314 - 0x09D5E (Blue Bridge EP) - 0x09E86 & 0x09ED8 - True + +Mountain Pink Bridge EP (Mountain Floor 2): +159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True Mountain Bottom Floor Rock (Mountain Bottom Floor) - Mountain Bottom Floor - 0x17F33 - Mountain Path to Caves - 0x17F33: Door - 0x17F33 (Rock Open) - True @@ -872,6 +1015,7 @@ Door - 0x019A5 (Pillar Door) - 0x09DD5 Door - 0x2D73F (Mountain Shortcut Door) - 0x021D7 158450 - 0x17CF2 (Swamp Shortcut Panel) - True - Arrows Door - 0x2D859 (Swamp Shortcut Door) - 0x17CF2 +159341 - 0x3397C (Skylight EP) - True - True Path to Challenge (Caves) - Challenge - 0x0A19A: 158477 - 0x0A16E (Challenge Entry Panel) - True - Stars & Arrows & Stars + Same Colored Symbol @@ -895,11 +1039,12 @@ Challenge (Challenge) - Tunnels - 0x0348A: 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 +158516 - 0x1C31A (Dots Pillar) - 0x034F4 & 0x034EC - Dots & Symmetry +158517 - 0x1C319 (Squares Pillar) - 0x034F4 & 0x034EC - Squares & Black/White Squares & Symmetry 158667 - 0x0356B (Vault Box) - 0x1C31A & 0x1C319 - True 158518 - 0x039B4 (Tunnels Entry Panel) - True - Arrows Door - 0x0348A (Tunnels Entry) - 0x039B4 +159530 - 0x28B30 (Water EP) - True - True Tunnels (Tunnels) - Windmill Interior - 0x27739 - Desert Lowest Level Inbetween Shortcuts - 0x27263 - Town - 0x09E87: 158668 - 0x2FAF6 (Vault Box) - True - True @@ -909,12 +1054,13 @@ Door - 0x27739 (Theater Shortcut) - 0x27732 Door - 0x27263 (Desert Shortcut) - 0x2773D 158521 - 0x09E85 (Town Shortcut Panel) - True - Arrows Door - 0x09E87 (Town Shortcut) - 0x09E85 +159557 - 0x33A20 (Theater Flowers EP) - 0x03553 & Theater to Tunnels - True Final Room (Mountain Final Room) - Elevator - 0x339BB & 0x33961: 158522 - 0x0383A (Right Pillar 1) - True - Stars & Eraser & Triangles & Stars + Same Colored Symbol 158523 - 0x09E56 (Right Pillar 2) - 0x0383A - Dots & Full Dots & Triangles & Symmetry 158524 - 0x09E5A (Right Pillar 3) - 0x09E56 - Dots & Shapers & Stars & Negative Shapers & Stars + Same Colored Symbol & Symmetry -158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Eraser & Symmetry & Stars & Stars + Same Colored Symbols & Negative Shapers & Shapers +158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Eraser & Symmetry & Stars & Stars + Same Colored Symbol & Negative Shapers & Shapers 158526 - 0x0383D (Left Pillar 1) - True - Stars & Black/White Squares & Stars + Same Colored Symbol 158527 - 0x0383F (Left Pillar 2) - 0x0383D - Triangles & Symmetry 158528 - 0x03859 (Left Pillar 3) - 0x0383F - Symmetry & Shapers & Black/White Squares @@ -930,3 +1076,48 @@ Elevator (Mountain Final Room): 158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA & 7 Lasers | 0x3D9A8 & 7 Lasers - 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: +159042 - 0x22106 (Desert EP) - True - True +159223 - 0x03B25 (Shipwreck CCW Underside EP) - True - True +159231 - 0x28B29 (Shipwreck Green EP) - True - True +159232 - 0x28B2A (Shipwreck CW Underside EP) - True - True +159323 - 0x03D0D (Bunker Yellow Line EP) - True - True +159515 - 0x28A37 (Town Long Sewer EP) - True - True +159520 - 0x33857 (Tutorial EP) - True - True +159521 - 0x33879 (Tutorial Reflection EP) - True - True +159522 - 0x03C19 (Tutorial Moss EP) - True - True +159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True + +Obelisks (EPs) - Entry - True: +159700 - 0xFFE00 (Desert Obelisk Side 1) - 0x0332B & 0x03367 & 0x28B8A - True +159701 - 0xFFE01 (Desert Obelisk Side 2) - 0x037B6 & 0x037B2 & 0x000F7 - True +159702 - 0xFFE02 (Desert Obelisk Side 3) - 0x3351D - True +159703 - 0xFFE03 (Desert Obelisk Side 4) - 0x0053C & 0x00771 & 0x335C8 & 0x335C9 & 0x337F8 & 0x037BB & 0x220E4 & 0x220E5 - True +159704 - 0xFFE04 (Desert Obelisk Side 5) - 0x334B9 & 0x334BC & 0x22106 & 0x0A14C & 0x0A14D - True +159710 - 0xFFE10 (Monastery Obelisk Side 1) - 0x03ABC & 0x03ABE & 0x03AC0 & 0x03AC4 - True +159711 - 0xFFE11 (Monastery Obelisk Side 2) - 0x03AC5 - True +159712 - 0xFFE12 (Monastery Obelisk Side 3) - 0x03BE2 & 0x03BE3 & 0x0A409 - True +159713 - 0xFFE13 (Monastery Obelisk Side 4) - 0x006E5 & 0x006E6 & 0x006E7 & 0x034A7 & 0x034AD & 0x034AF & 0x03DAB & 0x03DAC & 0x03DAD - True +159714 - 0xFFE14 (Monastery Obelisk Side 5) - 0x03E01 - True +159715 - 0xFFE15 (Monastery Obelisk Side 6) - 0x289F4 & 0x289F5 - True +159720 - 0xFFE20 (Treehouse Obelisk Side 1) - 0x0053D & 0x0053E & 0x00769 - True +159721 - 0xFFE21 (Treehouse Obelisk Side 2) - 0x33721 & 0x220A7 & 0x220BD - True +159722 - 0xFFE22 (Treehouse Obelisk Side 3) - 0x03B22 & 0x03B23 & 0x03B24 & 0x03B25 & 0x03A79 & 0x28ABD & 0x28ABE - True +159723 - 0xFFE23 (Treehouse Obelisk Side 4) - 0x3388F & 0x28B29 & 0x28B2A - True +159724 - 0xFFE24 (Treehouse Obelisk Side 5) - 0x018B6 & 0x033BE & 0x033BF & 0x033DD & 0x033E5 - True +159725 - 0xFFE25 (Treehouse Obelisk Side 6) - 0x28AE9 & 0x3348F - True +159730 - 0xFFE30 (River Obelisk Side 1) - 0x001A3 & 0x335AE - True +159731 - 0xFFE31 (River Obelisk Side 2) - 0x000D3 & 0x035F5 & 0x09D5D & 0x09D5E & 0x09D63 - True +159732 - 0xFFE32 (River Obelisk Side 3) - 0x3370E & 0x035DE & 0x03601 & 0x03603 & 0x03D0D & 0x3369A & 0x336C8 & 0x33505 - True +159733 - 0xFFE33 (River Obelisk Side 4) - 0x03A9E & 0x016B2 & 0x3365F & 0x03731 & 0x036CE & 0x03C07 & 0x03A93 - True +159734 - 0xFFE34 (River Obelisk Side 5) - 0x03AA6 & 0x3397C & 0x0105D & 0x0A304 - True +159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True +159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True +159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True +159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 & 0x33692 - True +159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x03E77 & 0x03E7C - True +159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True +159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True +159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True +159753 - 0xFFE53 (Town Obelisk Side 4) - 0x28B30 & 0x035C9 - True +159754 - 0xFFE54 (Town Obelisk Side 5) - 0x03335 & 0x03412 & 0x038A6 & 0x038AA & 0x03E3F & 0x03E40 & 0x28B8E - True +159755 - 0xFFE55 (Town Obelisk Side 6) - 0x28B91 & 0x03BCE & 0x03BCF & 0x03BD1 & 0x339B6 & 0x33A20 & 0x33A29 & 0x33A2A & 0x33B06 - True \ No newline at end of file diff --git a/worlds/witness/WitnessLogicVanilla.txt b/worlds/witness/WitnessLogicVanilla.txt new file mode 100644 index 0000000000..7b9fa884d6 --- /dev/null +++ b/worlds/witness/WitnessLogicVanilla.txt @@ -0,0 +1,1123 @@ +Entry (Entry): + +First Hallway (First Hallway) - Entry - True - First Hallway Room - 0x00064: +158000 - 0x00064 (Straight) - True - True +159510 - 0x01848 (EP) - 0x00064 - True + +First Hallway Room (First Hallway) - Tutorial - 0x00182: +158001 - 0x00182 (Bend) - True - True + +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 +159512 - 0x33530 (Cloud EP) - True - True +159513 - 0x33600 (Patio Flowers EP) - 0x0C373 - True +159517 - 0x3352F (Gate EP) - 0x03505 - True + +Outside Tutorial (Outside Tutorial) - Outside Tutorial Path To Outpost - 0x03BA2: +158650 - 0x033D4 (Vault) - True - Dots & Black/White Squares +158651 - 0x03481 (Vault Box) - 0x033D4 - True +158013 - 0x0005D (Shed Row 1) - True - Dots +158014 - 0x0005E (Shed Row 2) - 0x0005D - Dots +158015 - 0x0005F (Shed Row 3) - 0x0005E - Dots +158016 - 0x00060 (Shed Row 4) - 0x0005F - Dots +158017 - 0x00061 (Shed Row 5) - 0x00060 - Dots +158018 - 0x018AF (Tree Row 1) - True - Black/White Squares +158019 - 0x0001B (Tree Row 2) - 0x018AF - Black/White Squares +158020 - 0x012C9 (Tree Row 3) - 0x0001B - Black/White Squares +158021 - 0x0001C (Tree Row 4) - 0x012C9 - Black/White Squares +158022 - 0x0001D (Tree Row 5) - 0x0001C - Black/White Squares +158023 - 0x0001E (Tree Row 6) - 0x0001D - Black/White Squares +158024 - 0x0001F (Tree Row 7) - 0x0001E - Black/White Squares +158025 - 0x00020 (Tree Row 8) - 0x0001F - Black/White Squares +158026 - 0x00021 (Tree Row 9) - 0x00020 - Black/White Squares +Door - 0x03BA2 (Outpost Path) - 0x0A3B5 +159511 - 0x03D06 (Garden EP) - True - True +159514 - 0x28A2F (Town Sewer EP) - True - True +159516 - 0x334A3 (Path EP) - True - True +159500 - 0x035C7 (Tractor EP) - True - True + +Outside Tutorial Path To Outpost (Outside Tutorial) - Outside Tutorial Outpost - 0x0A170: +158011 - 0x0A171 (Outpost Entry Panel) - True - Dots & Full Dots +Door - 0x0A170 (Outpost Entry) - 0x0A171 + +Outside Tutorial Outpost (Outside Tutorial) - Outside Tutorial - 0x04CA3: +158012 - 0x04CA4 (Outpost Exit Panel) - True - Dots & Full Dots +Door - 0x04CA3 (Outpost Exit) - 0x04CA4 +158600 - 0x17CFB (Discard) - True - Triangles + +Main Island (Main Island) - Outside Tutorial - True: +159550 - 0x28B91 (Thundercloud EP) - 0x09F98 & 0x012FB - True + +Outside Glass Factory (Glass Factory) - Main Island - True - Inside Glass Factory - 0x01A29: +158027 - 0x01A54 (Entry Panel) - True - Symmetry +Door - 0x01A29 (Entry) - 0x01A54 +158601 - 0x3C12B (Discard) - True - Triangles +159002 - 0x28B8A (Vase EP) - 0x01A54 - True + +Inside Glass Factory (Glass Factory) - Inside Glass Factory Behind Back Wall - 0x0D7ED: +158028 - 0x00086 (Back Wall 1) - True - Symmetry +158029 - 0x00087 (Back Wall 2) - 0x00086 - Symmetry +158030 - 0x00059 (Back Wall 3) - 0x00087 - Symmetry +158031 - 0x00062 (Back Wall 4) - 0x00059 - Symmetry +158032 - 0x0005C (Back Wall 5) - 0x00062 - Symmetry +158033 - 0x0008D (Front 1) - 0x0005C - Symmetry +158034 - 0x00081 (Front 2) - 0x0008D - Symmetry +158035 - 0x00083 (Front 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 + +Inside Glass Factory Behind Back Wall (Glass Factory) - Boat - 0x17CC8: +158039 - 0x17CC8 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat + +Outside Symmetry Island (Symmetry Island) - Main Island - True - Symmetry Island Lower - 0x17F3E: +158040 - 0x000B0 (Lower Panel) - 0x0343A - Dots +Door - 0x17F3E (Lower) - 0x000B0 + +Symmetry Island Lower (Symmetry Island) - Symmetry Island Upper - 0x18269: +158041 - 0x00022 (Right 1) - True - Symmetry & Dots +158042 - 0x00023 (Right 2) - 0x00022 - Symmetry & Dots +158043 - 0x00024 (Right 3) - 0x00023 - Symmetry & Dots +158044 - 0x00025 (Right 4) - 0x00024 - Symmetry & Dots +158045 - 0x00026 (Right 5) - 0x00025 - Symmetry & Dots +158046 - 0x0007C (Back 1) - 0x00026 - Symmetry & Colored Dots +158047 - 0x0007E (Back 2) - 0x0007C - Symmetry & Colored Dots +158048 - 0x00075 (Back 3) - 0x0007E - Symmetry & Colored Dots +158049 - 0x00073 (Back 4) - 0x00075 - Symmetry & Dots & Colored Dots +158050 - 0x00077 (Back 5) - 0x00073 - Symmetry & Dots & Colored Dots +158051 - 0x00079 (Back 6) - 0x00077 - Symmetry & Dots & Colored Dots +158052 - 0x00065 (Left 1) - 0x00079 - Symmetry & Colored Dots +158053 - 0x0006D (Left 2) - 0x00065 - Symmetry & Colored Dots +158054 - 0x00072 (Left 3) - 0x0006D - Symmetry & Colored Dots +158055 - 0x0006F (Left 4) - 0x00072 - Symmetry & Colored Dots +158056 - 0x00070 (Left 5) - 0x0006F - Symmetry & Colored Dots +158057 - 0x00071 (Left 6) - 0x00070 - Symmetry & Colored Dots +158058 - 0x00076 (Left 7) - 0x00071 - Symmetry & Colored Dots +158059 - 0x009B8 (Scenery Outlines 1) - True - Symmetry +158060 - 0x003E8 (Scenery Outlines 2) - 0x009B8 - Symmetry +158061 - 0x00A15 (Scenery Outlines 3) - 0x003E8 - Symmetry +158062 - 0x00B53 (Scenery Outlines 4) - 0x00A15 - Symmetry +158063 - 0x00B8D (Scenery Outlines 5) - 0x00B53 - Symmetry +158064 - 0x1C349 (Upper Panel) - 0x00076 - Symmetry & Dots +Door - 0x18269 (Upper) - 0x1C349 +159000 - 0x0332B (Glass Factory Black Line Reflection EP) - True - True + +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 +159001 - 0x03367 (Glass Factory Black Line EP) - True - True + +Orchard (Orchard) - Main Island - True - Orchard Beyond First Gate - 0x03307: +158071 - 0x00143 (Apple Tree 1) - True - True +158072 - 0x0003B (Apple Tree 2) - 0x00143 - True +158073 - 0x00055 (Apple Tree 3) - 0x0003B - True +Door - 0x03307 (First Gate) - 0x00055 + +Orchard Beyond First Gate (Orchard) - Orchard End - 0x03313: +158074 - 0x032F7 (Apple Tree 4) - 0x00055 - True +158075 - 0x032FF (Apple Tree 5) - 0x032F7 - True +Door - 0x03313 (Second Gate) - 0x032FF + +Orchard End (Orchard): + +Desert Outside (Desert) - Main Island - True - Desert Floodlight Room - 0x09FEE: +158652 - 0x0CC7B (Vault) - True - Dots & Shapers & Rotated Shapers & Negative Shapers & Full Dots +158653 - 0x0339E (Vault Box) - 0x0CC7B - True +158602 - 0x17CE7 (Discard) - True - Triangles +158076 - 0x00698 (Surface 1) - True - True +158077 - 0x0048F (Surface 2) - 0x00698 - True +158078 - 0x09F92 (Surface 3) - 0x0048F & 0x09FA0 - True +158079 - 0x09FA0 (Surface 3 Control) - 0x0048F - True +158080 - 0x0A036 (Surface 4) - 0x09F92 - True +158081 - 0x09DA6 (Surface 5) - 0x09F92 - True +158082 - 0x0A049 (Surface 6) - 0x09F92 - True +158083 - 0x0A053 (Surface 7) - 0x0A036 & 0x09DA6 & 0x0A049 - True +158084 - 0x09F94 (Surface 8) - 0x0A053 & 0x09F86 - True +158085 - 0x09F86 (Surface 8 Control) - 0x0A053 - True +158086 - 0x0C339 (Light Room Entry Panel) - 0x09F94 - True +Door - 0x09FEE (Light Room Entry) - 0x0C339 +158701 - 0x03608 (Laser Panel) - 0x012D7 & 0x0A15F - True +Laser - 0x012FB (Laser) - 0x03608 +159020 - 0x3351D (Sand Snake EP) - True - True +159030 - 0x0053C (Facade Right EP) - True - True +159031 - 0x00771 (Facade Left EP) - True - True +159032 - 0x335C8 (Stairs Left EP) - True - True +159033 - 0x335C9 (Stairs Right EP) - True - True +159036 - 0x220E4 (Broken Wall Straight EP) - True - True +159037 - 0x220E5 (Broken Wall Bend EP) - True - True +159040 - 0x334B9 (Shore EP) - True - True +159041 - 0x334BC (Island EP) - True - True + +Desert Floodlight Room (Desert) - Desert Pond Room - 0x0C2C3: +158087 - 0x09FAA (Light Control) - True - True +158088 - 0x00422 (Light Room 1) - 0x09FAA - True +158089 - 0x006E3 (Light Room 2) - 0x09FAA - True +158090 - 0x0A02D (Light Room 3) - 0x09FAA & 0x00422 & 0x006E3 - True +Door - 0x0C2C3 (Pond Room Entry) - 0x0A02D + +Desert Pond Room (Desert) - Desert Water Levels Room - 0x0A24B: +158091 - 0x00C72 (Pond Room 1) - True - True +158092 - 0x0129D (Pond Room 2) - 0x00C72 - True +158093 - 0x008BB (Pond Room 3) - 0x0129D - True +158094 - 0x0078D (Pond Room 4) - 0x008BB - True +158095 - 0x18313 (Pond Room 5) - 0x0078D - True +158096 - 0x0A249 (Flood Room Entry Panel) - 0x18313 - True +Door - 0x0A24B (Flood Room Entry) - 0x0A249 +159043 - 0x0A14C (Pond Room Near Reflection EP) - True - True +159044 - 0x0A14D (Pond Room Far Reflection EP) - True - True + +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 Room 1) - 0x1C260 & 0x1831C - True +158106 - 0x01205 (Flood Room 2) - 0x04D18 & 0x1C260 & 0x1831C - True +158107 - 0x181AB (Flood Room 3) - 0x01205 & 0x1C260 & 0x1831C - True +158108 - 0x0117A (Flood Room 4) - 0x181AB & 0x1C260 & 0x1831C - True +158109 - 0x17ECA (Flood Room 5) - 0x0117A & 0x1C260 & 0x1831C - True +158110 - 0x18076 (Flood Room 6) - 0x17ECA & 0x1C260 & 0x1831C - True +Door - 0x0C316 (Elevator Room Entry) - 0x18076 +159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True + +Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x012FB: +158111 - 0x17C31 (Final Transparent) - True - True +158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True +158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True +158115 - 0x0A15C (Final Bent 1) - True - True +158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True +158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True +159035 - 0x037BB (Elevator EP) - 0x012FB - True + +Desert Lowest Level Inbetween Shortcuts (Desert): + +Outside Quarry (Quarry) - Main Island - True - Quarry Between Entrys - 0x09D6F - Quarry Elevator - TrueOneWay: +158118 - 0x09E57 (Entry 1 Panel) - True - Black/White Squares +158603 - 0x17CF0 (Discard) - True - Triangles +158702 - 0x03612 (Laser Panel) - 0x0A3D0 & 0x0367C - Eraser & Shapers +Laser - 0x01539 (Laser) - 0x03612 +Door - 0x09D6F (Entry 1) - 0x09E57 +159404 - 0x28A4A (Shore EP) - True - True +159410 - 0x334B6 (Entrance Pipe EP) - True - True +159412 - 0x28A4C (Sand Pile EP) - True - True +159420 - 0x289CF (Rock Line EP) - True - True +159421 - 0x289D1 (Rock Line Reflection EP) - True - True + +Quarry Elevator (Quarry): +158120 - 0x17CC4 (Elevator Control) - 0x0367C - Dots & Eraser +159403 - 0x17CB9 (Railroad EP) - 0x17CC4 - True + +Quarry Between Entrys (Quarry) - Quarry - 0x17C07: +158119 - 0x17C09 (Entry 2 Panel) - True - Shapers +Door - 0x17C07 (Entry 2) - 0x17C09 + +Quarry (Quarry) - Quarry Mill Ground Floor - 0x02010 - Quarry Elevator - 0x17CC4: +158121 - 0x01E5A (Mill Entry Left Panel) - True - Black/White Squares +158122 - 0x01E59 (Mill Entry Right Panel) - True - Dots +Door - 0x02010 (Mill Entry) - 0x01E59 & 0x01E5A + +Quarry Mill Ground Floor (Quarry Mill) - Quarry - 0x275FF - Quarry Mill Middle Floor - 0x03678 - Outside Quarry - 0x17CE8: +158123 - 0x275ED (Side Exit Panel) - True - True +Door - 0x275FF (Side Exit) - 0x275ED +158124 - 0x03678 (Lower Ramp Control) - True - Dots & Eraser +158145 - 0x17CAC (Roof Exit Panel) - True - True +Door - 0x17CE8 (Roof Exit) - 0x17CAC + +Quarry Mill Middle Floor (Quarry Mill) - Quarry Mill Ground Floor - 0x03675 - Quarry Mill Upper Floor - 0x03679: +158125 - 0x00E0C (Lower Row 1) - True - Dots & Eraser +158126 - 0x01489 (Lower Row 2) - 0x00E0C - Dots & Eraser +158127 - 0x0148A (Lower Row 3) - 0x01489 - Dots & Eraser +158128 - 0x014D9 (Lower Row 4) - 0x0148A - Dots & Eraser +158129 - 0x014E7 (Lower Row 5) - 0x014D9 - Dots +158130 - 0x014E8 (Lower Row 6) - 0x014E7 - Dots & Eraser +158131 - 0x03679 (Lower Lift Control) - 0x014E8 - Dots & Eraser + +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 (Upper Row 1) - True - Colored Squares & Eraser +158135 - 0x005F1 (Upper Row 2) - 0x00557 - Colored Squares & Eraser +158136 - 0x00620 (Upper Row 3) - 0x005F1 - Colored Squares & Eraser +158137 - 0x009F5 (Upper Row 4) - 0x00620 - Colored Squares & Eraser +158138 - 0x0146C (Upper Row 5) - 0x009F5 - Colored Squares & Eraser +158139 - 0x3C12D (Upper Row 6) - 0x0146C - Colored Squares & Eraser +158140 - 0x03686 (Upper Row 7) - 0x3C12D - Colored Squares & Eraser +158141 - 0x014E9 (Upper Row 8) - 0x03686 - Colored Squares & Eraser +158142 - 0x03677 (Stair Control) - True - Colored Squares & Eraser +Door - 0x0368A (Stairs) - 0x03677 +158143 - 0x3C125 (Control Room Left) - 0x0367C - Black/White Squares & Dots & Eraser +158144 - 0x0367C (Control Room Right) - 0x014E9 - Colored Squares & Dots & Eraser +159411 - 0x0069D (Ramp EP) - 0x03676 & 0x275FF - True +159413 - 0x00614 (Lift EP) - 0x275FF & 0x03675 - True + +Quarry Boathouse (Quarry Boathouse) - Quarry - True - Quarry Boathouse Upper Front - 0x03852 - Quarry Boathouse Behind Staircase - 0x2769B: +158146 - 0x034D4 (Intro Left) - True - Stars +158147 - 0x021D5 (Intro Right) - True - Shapers & Rotated Shapers +158148 - 0x03852 (Ramp Height Control) - 0x034D4 & 0x021D5 - Rotated Shapers +158166 - 0x17CA6 (Boat Spawn) - True - Boat +Door - 0x2769B (Dock) - 0x17CA6 +Door - 0x27163 (Dock Invis Barrier) - 0x17CA6 + +Quarry Boathouse Behind Staircase (Quarry Boathouse) - Boat - 0x17CA6: + +Quarry Boathouse Upper Front (Quarry Boathouse) - Quarry Boathouse Upper Middle - 0x17C50: +158149 - 0x021B3 (Front Row 1) - True - Shapers & Eraser +158150 - 0x021B4 (Front Row 2) - 0x021B3 - Shapers & Eraser +158151 - 0x021B0 (Front Row 3) - 0x021B4 - Shapers & Eraser +158152 - 0x021AF (Front Row 4) - 0x021B0 - Shapers & Eraser +158153 - 0x021AE (Front Row 5) - 0x021AF - Shapers & Eraser +Door - 0x17C50 (First Barrier) - 0x021AE + +Quarry Boathouse Upper Middle (Quarry Boathouse) - Quarry Boathouse Upper Back - 0x03858: +158154 - 0x03858 (Ramp Horizontal Control) - True - Shapers & Eraser +159402 - 0x00859 (Moving Ramp EP) - 0x03858 & 0x03852 & 0x3865F - True + +Quarry Boathouse Upper Back (Quarry Boathouse) - Quarry Boathouse Upper Middle - 0x3865F: +158155 - 0x38663 (Second Barrier Panel) - True - True +Door - 0x3865F (Second Barrier) - 0x38663 +158156 - 0x021B5 (Back First Row 1) - True - Stars & Stars + Same Colored Symbol & Eraser +158157 - 0x021B6 (Back First Row 2) - 0x021B5 - Stars & Stars + Same Colored Symbol & Eraser +158158 - 0x021B7 (Back First Row 3) - 0x021B6 - Stars & Stars + Same Colored Symbol & Eraser +158159 - 0x021BB (Back First Row 4) - 0x021B7 - Stars & Stars + Same Colored Symbol & Eraser +158160 - 0x09DB5 (Back First Row 5) - 0x021BB - Stars & Stars + Same Colored Symbol & Eraser +158161 - 0x09DB1 (Back First Row 6) - 0x09DB5 - Stars & Stars + Same Colored Symbol & Eraser +158162 - 0x3C124 (Back First Row 7) - 0x09DB1 - Stars & Stars + Same Colored Symbol & Eraser +158163 - 0x09DB3 (Back First Row 8) - 0x3C124 - Stars & Eraser & Shapers +158164 - 0x09DB4 (Back First Row 9) - 0x09DB3 - Stars & Eraser & Shapers +158165 - 0x275FA (Hook Control) - True - Shapers & Eraser +158167 - 0x0A3CB (Back Second Row 1) - 0x09DB4 - Stars & Eraser & Shapers +158168 - 0x0A3CC (Back Second Row 2) - 0x0A3CB - Stars & Eraser & Shapers +158169 - 0x0A3D0 (Back Second Row 3) - 0x0A3CC - Stars & Eraser & Shapers +159401 - 0x005F6 (Hook EP) - 0x275FA & 0x03852 & 0x3865F - True + +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 (Intro 6) - 0x0A8DC - True +158172 - 0x0AC7A (Intro 7) - 0x0AC74 - True +158173 - 0x0A8E0 (Intro 8) - 0x0AC7A - True +158174 - 0x386FA (Far 1) - 0x0A8E0 - True +158175 - 0x1C33F (Far 2) - 0x386FA - True +158176 - 0x196E2 (Far 3) - 0x1C33F - True +158177 - 0x1972A (Far 4) - 0x196E2 - True +158178 - 0x19809 (Far 5) - 0x1972A - True +158179 - 0x19806 (Far 6) - 0x19809 - True +158180 - 0x196F8 (Far 7) - 0x19806 - True +158181 - 0x1972F (Far 8) - 0x196F8 - True +Door - 0x194B2 (Laser Entry Right) - 0x1972F +158182 - 0x19797 (Near 1) - 0x0A8E0 - True +158183 - 0x1979A (Near 2) - 0x19797 - True +158184 - 0x197E0 (Near 3) - 0x1979A - True +158185 - 0x197E8 (Near 4) - 0x197E0 - True +158186 - 0x197E5 (Near 5) - 0x197E8 - True +Door - 0x19665 (Laser Entry Left) - 0x197E5 +159400 - 0x28A7B (Quarry Mill Rooftop Vent EP) - True - True + +Shadows Ledge (Shadows) - Shadows - 0x1855B - Quarry - 0x19865 & 0x0A2DF: +158187 - 0x334DC (Door Timer Inside) - True - True +158188 - 0x198B5 (Intro 1) - True - True +158189 - 0x198BD (Intro 2) - 0x198B5 - True +158190 - 0x198BF (Intro 3) - 0x198BD & 0x334DC & 0x19B24 - True +Door - 0x19865 (Quarry Barrier) - 0x198BF +Door - 0x0A2DF (Quarry Barrier 2) - 0x198BF +158191 - 0x19771 (Intro 4) - 0x198BF - True +158192 - 0x0A8DC (Intro 5) - 0x19771 - True +Door - 0x1855B (Ledge Barrier) - 0x0A8DC +Door - 0x19ADE (Ledge Barrier 2) - 0x0A8DC + +Shadows Laser Room (Shadows): +158703 - 0x19650 (Laser Panel) - True - True +Laser - 0x181B3 (Laser) - 0x19650 + +Treehouse Beach (Treehouse Beach) - Main Island - True: +159200 - 0x0053D (Rock Shadow EP) - True - True +159201 - 0x0053E (Sand Shadow EP) - True - True +159212 - 0x220BD (Both Orange Bridges EP) - 0x17DA2 & 0x17DDB - True + +Keep (Keep) - Main Island - True - Keep 2nd Maze - 0x01954 - Keep 2nd Pressure Plate - 0x01BEC: +158193 - 0x00139 (Hedge Maze 1) - True - True +158197 - 0x0A3A8 (Reset Pressure Plates 1) - True - True +158198 - 0x033EA (Pressure Plates 1) - 0x0A3A8 - Dots +Door - 0x01954 (Hedge Maze 1 Exit) - 0x00139 +Door - 0x01BEC (Pressure Plates 1 Exit) - 0x033EA +159430 - 0x03E77 (Red Flowers EP) - True - True +159431 - 0x03E7C (Purple Flowers EP) - 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 - True +Door - 0x019D8 (Hedge Maze 2 Exit) - 0x019DC + +Keep 3rd Maze (Keep) - Keep - 0x019B5 - Keep 4th Maze - 0x019E6: +Door - 0x019B5 (Hedge Maze 3 Shortcut) - 0x019DC +158195 - 0x019E7 (Hedge Maze 3) - True - True +Door - 0x019E6 (Hedge Maze 3 Exit) - 0x019E7 + +Keep 4th Maze (Keep) - Keep - 0x0199A - Keep Tower - 0x01A0E: +Door - 0x0199A (Hedge Maze 4 Shortcut) - 0x019E7 +158196 - 0x01A0F (Hedge Maze 4) - True - True +Door - 0x01A0E (Hedge Maze 4 Exit) - 0x01A0F + +Keep 2nd Pressure Plate (Keep) - Keep 3rd Pressure Plate - 0x01BEA: +158199 - 0x0A3B9 (Reset Pressure Plates 2) - True - True +158200 - 0x01BE9 (Pressure Plates 2) - 0x0A3B9 - Black/White Squares +Door - 0x01BEA (Pressure Plates 2 Exit) - 0x01BE9 + +Keep 3rd Pressure Plate (Keep) - Keep 4th Pressure Plate - 0x01CD5: +158201 - 0x0A3BB (Reset Pressure Plates 3) - True - True +158202 - 0x01CD3 (Pressure Plates 3) - 0x0A3BB - Shapers +Door - 0x01CD5 (Pressure Plates 3 Exit) - 0x01CD3 + +Keep 4th Pressure Plate (Keep) - Shadows - 0x09E3D - Keep Tower - 0x01D40: +158203 - 0x0A3AD (Reset Pressure Plates 4) - True - True +158204 - 0x01D3F (Pressure Plates 4) - 0x0A3AD - Rotated Shapers & Symmetry +Door - 0x01D40 (Pressure Plates 4 Exit) - 0x01D3F +158604 - 0x17D27 (Discard) - True - Triangles +158205 - 0x09E49 (Shadows Shortcut Panel) - True - True +Door - 0x09E3D (Shadows Shortcut) - 0x09E49 + +Shipwreck (Shipwreck) - Keep 3rd Pressure Plate - True: +158654 - 0x00AFB (Vault) - True - Symmetry & Sound Dots & Colored Dots +158655 - 0x03535 (Vault Box) - 0x00AFB - True +158605 - 0x17D28 (Discard) - True - Triangles +159220 - 0x03B22 (Circle Far EP) - True - True +159221 - 0x03B23 (Circle Left EP) - True - True +159222 - 0x03B24 (Circle Near EP) - True - True +159224 - 0x03A79 (Stern EP) - True - True +159225 - 0x28ABD (Rope Inner EP) - True - True +159226 - 0x28ABE (Rope Outer EP) - True - True +159230 - 0x3388F (Couch EP) - 0x17CDF | 0x0A054 - True + +Keep Tower (Keep) - Keep - 0x04F8F: +158206 - 0x0361B (Tower Shortcut Panel) - True - True +Door - 0x04F8F (Tower Shortcut) - 0x0361B +158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - True +158705 - 0x03317 (Laser Panel Pressure Plates) - 0x01D3F - Shapers & Black/White Squares & Rotated Shapers +Laser - 0x014BB (Laser) - 0x0360E | 0x03317 +159240 - 0x033BE (Pressure Plates 1 EP) - 0x033EA - True +159241 - 0x033BF (Pressure Plates 2 EP) - 0x01BE9 - True +159242 - 0x033DD (Pressure Plates 3 EP) - 0x01CD3 & 0x01CD5 - True +159243 - 0x033E5 (Pressure Plates 4 Left Exit EP) - 0x01D3F - True +159244 - 0x018B6 (Pressure Plates 4 Right Exit EP) - 0x01D3F - True +159250 - 0x28AE9 (Path EP) - True - True +159251 - 0x3348F (Hedges EP) - True - True + +Outside Monastery (Monastery) - Main Island - True - Inside Monastery - 0x0C128 & 0x0C153 - Monastery Garden - 0x03750: +158207 - 0x03713 (Shortcut Panel) - True - True +Door - 0x0364E (Shortcut) - 0x03713 +158208 - 0x00B10 (Entry Left) - True - True +158209 - 0x00C92 (Entry Right) - True - True +Door - 0x0C128 (Entry Inner) - 0x00B10 +Door - 0x0C153 (Entry Outer) - 0x00C92 +158210 - 0x00290 (Outside 1) - 0x09D9B - True +158211 - 0x00038 (Outside 2) - 0x09D9B & 0x00290 - True +158212 - 0x00037 (Outside 3) - 0x09D9B & 0x00038 - True +Door - 0x03750 (Garden Entry) - 0x00037 +158706 - 0x17CA4 (Laser Panel) - 0x193A6 - True +Laser - 0x17C65 (Laser) - 0x17CA4 +159130 - 0x006E5 (Facade Left Near EP) - True - True +159131 - 0x006E6 (Facade Left Far Short EP) - True - True +159132 - 0x006E7 (Facade Left Far Long EP) - True - True +159136 - 0x03DAB (Facade Right Near EP) - True - True +159137 - 0x03DAC (Facade Left Stairs EP) - True - True +159138 - 0x03DAD (Facade Right Stairs EP) - True - True +159140 - 0x03E01 (Grass Stairs EP) - True - True + +Inside Monastery (Monastery): +158213 - 0x09D9B (Shutters Control) - True - Dots +158214 - 0x193A7 (Inside 1) - 0x00037 - True +158215 - 0x193AA (Inside 2) - 0x193A7 - True +158216 - 0x193AB (Inside 3) - 0x193AA - True +158217 - 0x193A6 (Inside 4) - 0x193AB - True +159133 - 0x034A7 (Left Shutter EP) - 0x09D9B - True +159134 - 0x034AD (Middle Shutter EP) - 0x09D9B - True +159135 - 0x034AF (Right Shutter EP) - 0x09D9B - True + +Monastery Garden (Monastery): + +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 Entry Panel) - True - Black/White Squares & Shapers +Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8 +158707 - 0x09F98 (Desert Laser Redirect) - True - True +158220 - 0x18590 (Transparent) - True - Symmetry +158221 - 0x28AE3 (Vines) - 0x18590 - True +158222 - 0x28938 (Apple Tree) - 0x28AE3 - True +158223 - 0x079DF (Triple Exit) - 0x28938 - True +158235 - 0x2899C (Wooden Roof Lower Row 1) - True - Rotated Shapers & Dots & Full Dots +158236 - 0x28A33 (Wooden Roof Lower Row 2) - 0x2899C - Shapers & Rotated Shapers & Dots & Full Dots +158237 - 0x28ABF (Wooden Roof Lower Row 3) - 0x28A33 - Shapers & Rotated Shapers & Dots & Full Dots +158238 - 0x28AC0 (Wooden Roof Lower Row 4) - 0x28ABF - Rotated Shapers & Dots & Full Dots +158239 - 0x28AC1 (Wooden Roof Lower Row 5) - 0x28AC0 - Rotated Shapers & Dots & Full Dots +Door - 0x034F5 (Wooden Roof Stairs) - 0x28AC1 +158225 - 0x28998 (Tinted Glass Door Panel) - True - Stars & Rotated Shapers +Door - 0x28A61 (Tinted Glass Door) - 0x28998 +158226 - 0x28A0D (Church Entry Panel) - 0x28A61 - Stars +Door - 0x03BB0 (Church Entry) - 0x28A0D +158228 - 0x28A79 (Maze Stair Control) - True - True +Door - 0x28AA2 (Maze Stairs) - 0x28A79 +158241 - 0x17F5F (Windmill Entry Panel) - True - Dots +Door - 0x1845B (Windmill Entry) - 0x17F5F +159010 - 0x037B6 (Windmill First Blade EP) - 0x17D02 - True +159011 - 0x037B2 (Windmill Second Blade EP) - 0x17D02 - True +159012 - 0x000F7 (Windmill Third Blade EP) - 0x17D02 - True +159540 - 0x03335 (Tower Underside First EP) - True - True +159541 - 0x03412 (Tower Underside Second EP) - True - True +159542 - 0x038A6 (Tower Underside Third EP) - True - True +159543 - 0x038AA (Tower Underside Fourth EP) - True - True +159545 - 0x03E40 (RGB House Green EP) - 0x334D8 - True +159546 - 0x28B8E (Maze Bridge Underside EP) - 0x2896A - True +159552 - 0x03BCF (Black Line Redirect EP) - True - True +159800 - 0xFFF80 (Pet the Dog) - True - True + +Town Inside Cargo Box (Town): +158606 - 0x17D01 (Cargo Box Discard) - True - Triangles + +Town Maze Rooftop (Town) - Town Red Rooftop - 0x2896A: +158229 - 0x2896A (Maze Rooftop Bridge Control) - True - Shapers +159544 - 0x03E3F (RGB House Red EP) - 0x334D8 - True + +Town Red Rooftop (Town): +158607 - 0x17C71 (Rooftop Discard) - True - Triangles +158230 - 0x28AC7 (Red Rooftop 1) - True - Symmetry & Black/White Squares +158231 - 0x28AC8 (Red Rooftop 2) - 0x28AC7 - Symmetry & Black/White Squares +158232 - 0x28ACA (Red Rooftop 3) - 0x28AC8 - Symmetry & Black/White Squares & Dots +158233 - 0x28ACB (Red Rooftop 4) - 0x28ACA - Symmetry & Black/White Squares & Dots +158234 - 0x28ACC (Red Rooftop 5) - 0x28ACB - Symmetry & Black/White Squares & Dots +158224 - 0x28B39 (Tall Hexagonal) - 0x079DF - True + +Town Wooden Rooftop (Town): +158240 - 0x28AD9 (Wooden Rooftop) - 0x28AC1 - Rotated Shapers & Dots & Eraser & Full Dots + +Town Church (Town): +158227 - 0x28A69 (Church Lattice) - 0x03BB0 - True +159553 - 0x03BD1 (Black Line Church EP) - True - True + +RGB House (Town) - RGB Room - 0x2897B: +158242 - 0x034E4 (Sound Room Left) - True - True +158243 - 0x034E3 (Sound Room Right) - True - Sound Dots +Door - 0x2897B (RGB House Stairs) - 0x034E4 & 0x034E3 + +RGB Room (Town): +158244 - 0x334D8 (RGB Control) - True - Rotated Shapers & Colored Squares +158245 - 0x03C0C (RGB Room Left) - 0x334D8 - Colored Squares & Black/White Squares +158246 - 0x03C08 (RGB Room Right) - 0x334D8 - Stars + +Town Tower (Town Tower) - Town - True - Town Tower Top - 0x27798 & 0x27799 & 0x2779A & 0x2779C: +Door - 0x27798 (First Door) - 0x28ACC +Door - 0x2779C (Second Door) - 0x28AD9 +Door - 0x27799 (Third Door) - 0x28A69 +Door - 0x2779A (Fourth Door) - 0x28B39 + +Town Tower Top (Town): +158708 - 0x032F5 (Laser Panel) - True - True +Laser - 0x032F9 (Laser) - 0x032F5 +159422 - 0x33692 (Brown Bridge EP) - True - True +159551 - 0x03BCE (Black Line Tower EP) - True - True + +Windmill Interior (Windmill) - Theater - 0x17F88: +158247 - 0x17D02 (Turn Control) - True - Dots +158248 - 0x17F89 (Theater Entry Panel) - True - Black/White Squares +Door - 0x17F88 (Theater Entry) - 0x17F89 + +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 (Exit Left Panel) - True - Black/White Squares & Eraser +158250 - 0x33AB2 (Exit Right Panel) - True - Black/White Squares & Shapers +Door - 0x0A16D (Exit Left) - 0x0A168 +Door - 0x3CCDF (Exit Right) - 0x33AB2 +158608 - 0x17CF7 (Discard) - True - Triangles +159554 - 0x339B6 (Eclipse EP) - 0x03549 & 0x0A16D & 0x3CCDF - True +159555 - 0x33A29 (Window EP) - 0x03553 - True +159556 - 0x33A2A (Door EP) - 0x03553 - True +159558 - 0x33B06 (Church EP) - 0x0354E - 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 (First Row 1) - True - True +158253 - 0x00767 (First Row 2) - 0x002C4 - True +158254 - 0x002C6 (First Row 3) - 0x00767 - True +158255 - 0x0070E (Second Row 1) - 0x002C6 - True +158256 - 0x0070F (Second Row 2) - 0x0070E - True +158257 - 0x0087D (Second Row 3) - 0x0070F - True +158258 - 0x002C7 (Second Row 4) - 0x0087D - True +158259 - 0x17CAB (Popup Wall Control) - 0x002C7 - True +Door - 0x1475B (Popup Wall) - 0x17CAB +158260 - 0x0026D (Popup Wall 1) - 0x1475B - Sound Dots +158261 - 0x0026E (Popup Wall 2) - 0x0026D - Sound Dots +158262 - 0x0026F (Popup Wall 3) - 0x0026E - Sound Dots +158263 - 0x00C3F (Popup Wall 4) - 0x0026F - Sound Dots +158264 - 0x00C41 (Popup Wall 5) - 0x00C3F - Sound Dots +158265 - 0x014B2 (Popup Wall 6) - 0x00C41 - Sound Dots +158709 - 0x03616 (Laser Panel) - 0x014B2 - True +Laser - 0x00274 (Laser) - 0x03616 +158266 - 0x337FA (Laser Shortcut Panel) - True - True +Door - 0x3873B (Laser Shortcut) - 0x337FA +159100 - 0x03ABC (Long Arch Moss EP) - True - True +159101 - 0x03ABE (Straight Left Moss EP) - True - True +159102 - 0x03AC0 (Pop-up Wall Moss EP) - True - True +159103 - 0x03AC4 (Short Arch Moss EP) - True - True +159150 - 0x289F4 (Entrance EP) - True - True +159151 - 0x289F5 (Tree Halo EP) - True - True +159350 - 0x035CB (Bamboo CCW EP) - True - True +159351 - 0x035CF (Bamboo CW EP) - True - True + +Outside Jungle River (River) - Main Island - True - Monastery Garden - 0x0CF2A: +158267 - 0x17CAA (Monastery Shortcut Panel) - True - True +Door - 0x0CF2A (Monastery Shortcut) - 0x17CAA +158663 - 0x15ADD (Vault) - True - Black/White Squares & Dots +158664 - 0x03702 (Vault Box) - 0x15ADD - True +159110 - 0x03AC5 (Green Leaf Moss EP) - True - True +159120 - 0x03BE2 (Monastery Garden Left EP) - 0x03750 - True +159121 - 0x03BE3 (Monastery Garden Right EP) - True - True +159122 - 0x0A409 (Monastery Wall EP) - True - True + +Outside Bunker (Bunker) - Main Island - True - Bunker - 0x0C2A4: +158268 - 0x17C2E (Entry Panel) - True - Black/White Squares & Colored Squares +Door - 0x0C2A4 (Entry) - 0x17C2E + +Bunker (Bunker) - Bunker Glass Room - 0x17C79: +158269 - 0x09F7D (Intro Left 1) - True - Colored Squares +158270 - 0x09FDC (Intro Left 2) - 0x09F7D - Colored Squares & Black/White Squares +158271 - 0x09FF7 (Intro Left 3) - 0x09FDC - Colored Squares & Black/White Squares +158272 - 0x09F82 (Intro Left 4) - 0x09FF7 - Colored Squares & Black/White Squares +158273 - 0x09FF8 (Intro Left 5) - 0x09F82 - Colored Squares & Black/White Squares +158274 - 0x09D9F (Intro Back 1) - 0x09FF8 - Colored Squares & Black/White Squares +158275 - 0x09DA1 (Intro Back 2) - 0x09D9F - Colored Squares +158276 - 0x09DA2 (Intro Back 3) - 0x09DA1 - Colored Squares +158277 - 0x09DAF (Intro Back 4) - 0x09DA2 - Colored Squares +158278 - 0x0A099 (Tinted Glass Door Panel) - 0x09DAF - True +Door - 0x17C79 (Tinted Glass Door) - 0x0A099 + +Bunker Glass Room (Bunker) - Bunker Ultraviolet Room - 0x0C2A3: +158279 - 0x0A010 (Glass Room 1) - True - Colored Squares +158280 - 0x0A01B (Glass Room 2) - 0x0A010 - Colored Squares & Black/White Squares +158281 - 0x0A01F (Glass Room 3) - 0x0A01B - Colored Squares & Black/White Squares +Door - 0x0C2A3 (UV Room Entry) - 0x0A01F + +Bunker Ultraviolet Room (Bunker) - Bunker Elevator Section - 0x0A08D: +158282 - 0x34BC5 (Drop-Down Door Open) - True - True +158283 - 0x34BC6 (Drop-Down Door Close) - 0x34BC5 - True +158284 - 0x17E63 (UV Room 1) - 0x34BC5 - Colored Squares +158285 - 0x17E67 (UV Room 2) - 0x17E63 & 0x34BC6 - Colored Squares & Black/White Squares +Door - 0x0A08D (Elevator Room Entry) - 0x17E67 + +Bunker Elevator Section (Bunker) - Bunker Elevator - TrueOneWay: +159311 - 0x035F5 (Tinted Door EP) - 0x17C79 - True + +Bunker Elevator (Bunker) - Bunker Laser Platform - 0x0A079 - Bunker Green Room - 0x0A079 - Bunker Laser Platform - 0x0A079 - Outside Bunker - 0x0A079: +158286 - 0x0A079 (Elevator Control) - True - Colored Squares & Black/White Squares + +Bunker Green Room (Bunker) - Bunker Elevator - TrueOneWay: +159310 - 0x000D3 (Green Room Flowers EP) - True - True + +Bunker Laser Platform (Bunker) - Bunker Elevator - TrueOneWay: +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) - 0x0056E +159321 - 0x03603 (Purple Sand Middle EP) - 0x17E2B - True +159322 - 0x03601 (Purple Sand Top EP) - 0x17E2B - True +159327 - 0x035DE (Purple Sand Bottom EP) - True - True + +Swamp Entry Area (Swamp) - Swamp Sliding Bridge - TrueOneWay: +158288 - 0x00469 (Intro Front 1) - True - Shapers +158289 - 0x00472 (Intro Front 2) - 0x00469 - Shapers +158290 - 0x00262 (Intro Front 3) - 0x00472 - Shapers +158291 - 0x00474 (Intro Front 4) - 0x00262 - Shapers +158292 - 0x00553 (Intro Front 5) - 0x00474 - Shapers +158293 - 0x0056F (Intro Front 6) - 0x00553 - Shapers +158294 - 0x00390 (Intro Back 1) - 0x0056F - Shapers +158295 - 0x010CA (Intro Back 2) - 0x00390 - Shapers +158296 - 0x00983 (Intro Back 3) - 0x010CA - Shapers +158297 - 0x00984 (Intro Back 4) - 0x00983 - Shapers +158298 - 0x00986 (Intro Back 5) - 0x00984 - Shapers +158299 - 0x00985 (Intro Back 6) - 0x00986 - Shapers +158300 - 0x00987 (Intro Back 7) - 0x00985 - Shapers +158301 - 0x181A9 (Intro Back 8) - 0x00987 - Shapers + +Swamp Sliding Bridge (Swamp) - Swamp Entry Area - 0x00609 - Swamp Near Platform - 0x00609: +158302 - 0x00609 (Sliding Bridge) - True - Shapers +159342 - 0x0105D (Sliding Bridge Left EP) - 0x00609 - True +159343 - 0x0A304 (Sliding Bridge Right EP) - 0x00609 - True + +Swamp Near Platform (Swamp) - Swamp Cyan Underwater - 0x04B7F - Swamp Near Boat - 0x38AE6 - Swamp Between Bridges Near - 0x184B7 - Swamp Sliding Bridge - TrueOneWay: +158313 - 0x00982 (Platform Row 1) - True - Shapers +158314 - 0x0097F (Platform Row 2) - 0x00982 - Shapers +158315 - 0x0098F (Platform Row 3) - 0x0097F - Shapers +158316 - 0x00990 (Platform Row 4) - 0x0098F - Shapers +Door - 0x184B7 (Between Bridges First Door) - 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 1) - True - Shapers & Negative Shapers +158308 - 0x00004 (Cyan Underwater 2) - 0x00002 - Shapers & Negative Shapers +158309 - 0x00005 (Cyan Underwater 3) - 0x00004 - Shapers & Negative Shapers +158310 - 0x013E6 (Cyan Underwater 4) - 0x00005 - Shapers & Negative Shapers +158311 - 0x00596 (Cyan Underwater 5) - 0x013E6 - Shapers & Negative Shapers +158312 - 0x18488 (Cyan Underwater Sliding Bridge Control) - True - Shapers +159340 - 0x03AA6 (Cyan Underwater Sliding Bridge EP) - 0x18488 - True + +Swamp Between Bridges Near (Swamp) - Swamp Between Bridges Far - 0x18507: +158303 - 0x00999 (Between Bridges Near Row 1) - 0x00990 - Shapers +158304 - 0x0099D (Between Bridges Near Row 2) - 0x00999 - Shapers +158305 - 0x009A0 (Between Bridges Near Row 3) - 0x0099D - Shapers +158306 - 0x009A1 (Between Bridges Near Row 4) - 0x009A0 - Shapers +Door - 0x18507 (Between Bridges Second Door) - 0x009A1 + +Swamp Between Bridges Far (Swamp) - Swamp Red Underwater - 0x183F2 - Swamp Rotating Bridge - TrueOneWay: +158319 - 0x00007 (Between Bridges Far Row 1) - 0x009A1 - Rotated Shapers +158320 - 0x00008 (Between Bridges Far Row 2) - 0x00007 - Rotated Shapers +158321 - 0x00009 (Between Bridges Far Row 3) - 0x00008 - Rotated Shapers +158322 - 0x0000A (Between Bridges Far Row 4) - 0x00009 - Rotated Shapers +Door - 0x183F2 (Red Water Pump) - 0x00596 + +Swamp Red Underwater (Swamp) - Swamp Maze - 0x305D5: +158323 - 0x00001 (Red Underwater 1) - True - Shapers & Negative Shapers +158324 - 0x014D2 (Red Underwater 2) - True - Shapers & Negative Shapers +158325 - 0x014D4 (Red Underwater 3) - True - Shapers & Negative Shapers +158326 - 0x014D1 (Red Underwater 4) - True - Shapers & Negative Shapers +Door - 0x305D5 (Red Underwater Exit) - 0x014D1 + +Swamp Rotating Bridge (Swamp) - Swamp Between Bridges Far - 0x181F5 - Swamp Near Boat - 0x181F5 - Swamp Purple Area - 0x181F5: +158327 - 0x181F5 (Rotating Bridge) - True - Rotated Shapers & Shapers +159331 - 0x016B2 (Rotating Bridge CCW EP) - 0x181F5 - True +159334 - 0x036CE (Rotating Bridge CW EP) - 0x181F5 - True + +Swamp Near Boat (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Blue Underwater - 0x18482: +158328 - 0x09DB8 (Boat Spawn) - True - Boat +158329 - 0x003B2 (Beyond Rotating Bridge 1) - 0x0000A - Rotated Shapers +158330 - 0x00A1E (Beyond Rotating Bridge 2) - 0x003B2 - Rotated Shapers +158331 - 0x00C2E (Beyond Rotating Bridge 3) - 0x00A1E - Rotated Shapers & Shapers +158332 - 0x00E3A (Beyond Rotating Bridge 4) - 0x00C2E - Rotated Shapers +158339 - 0x17E2B (Long Bridge Control) - True - Rotated Shapers & Shapers +Door - 0x18482 (Blue Water Pump) - 0x00E3A +159332 - 0x3365F (Boat EP) - 0x09DB8 - True +159333 - 0x03731 (Long Bridge Side EP) - 0x17E2B - True + +Swamp Purple Area (Swamp) - Swamp Rotating Bridge - TrueOneWay - Swamp Purple Underwater - 0x0A1D6: +Door - 0x0A1D6 (Purple Water Pump) - 0x00E3A + +Swamp Purple Underwater (Swamp): +158333 - 0x009A6 (Purple Underwater) - True - Shapers +159330 - 0x03A9E (Purple Underwater Right EP) - True - True +159336 - 0x03A93 (Purple Underwater Left EP) - True - True + +Swamp Blue Underwater (Swamp): +158334 - 0x009AB (Blue Underwater 1) - True - Shapers & Negative Shapers +158335 - 0x009AD (Blue Underwater 2) - 0x009AB - Shapers & Negative Shapers +158336 - 0x009AE (Blue Underwater 3) - 0x009AD - Shapers & Negative Shapers +158337 - 0x009AF (Blue Underwater 4) - 0x009AE - Shapers & Negative Shapers +158338 - 0x00006 (Blue Underwater 5) - 0x009AF - Shapers & Negative Shapers + +Swamp Maze (Swamp) - Swamp Laser Area - 0x17C0A & 0x17E07: +158340 - 0x17C0A (Maze Control) - True - Shapers & Negative Shapers & Rotated Shapers +158112 - 0x17E07 (Maze Control Other Side) - True - Shapers & Negative Shapers & Rotated Shapers + +Swamp Laser Area (Swamp) - Outside Swamp - 0x2D880: +158711 - 0x03615 (Laser Panel) - True - True +Laser - 0x00BF6 (Laser) - 0x03615 +158341 - 0x17C05 (Laser Shortcut Left Panel) - True - Rotated Shapers +158342 - 0x17C02 (Laser Shortcut Right Panel) - 0x17C05 - Shapers & Negative Shapers & Rotated Shapers +Door - 0x2D880 (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 +159210 - 0x33721 (Buoy EP) - 0x17C95 - True + +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 (Third Door Panel) - True - Stars +Door - 0x0A181 (Third 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 +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 & Black/White Squares +158363 - 0x17D99 (Second Purple Bridge 2) - 0x17D9B - Stars & Black/White Squares +158364 - 0x17DAA (Second Purple Bridge 3) - 0x17D99 - Stars & Black/White Squares +158365 - 0x17D97 (Second Purple Bridge 4) - 0x17DAA - Stars & Black/White Squares & Colored Squares +158366 - 0x17BDF (Second Purple Bridge 5) - 0x17D97 - Stars & Colored Squares +158367 - 0x17D91 (Second Purple Bridge 6) - 0x17BDF - Stars & Colored Squares +158368 - 0x17DC6 (Second Purple Bridge 7) - 0x17D91 - Stars & Colored Squares + +Treehouse Left Orange Bridge (Treehouse) - Treehouse Laser Room Front Platform - 0x17DDB - Treehouse Laser Room Back Platform - 0x17DDB - Treehouse Burned House - 0x17DDB: +158376 - 0x17DB3 (Left Orange Bridge 1) - True - Stars & Black/White Squares & Stars + Same Colored Symbol +158377 - 0x17DB5 (Left Orange Bridge 2) - 0x17DB3 - Stars & Black/White Squares +158378 - 0x17DB6 (Left Orange Bridge 3) - 0x17DB5 - Stars & Black/White Squares & Stars + Same Colored Symbol +158379 - 0x17DC0 (Left Orange Bridge 4) - 0x17DB6 - Stars & Black/White Squares & Stars + Same Colored Symbol +158380 - 0x17DD7 (Left Orange Bridge 5) - 0x17DC0 - Stars & Black/White Squares & Stars + Same Colored Symbol +158381 - 0x17DD9 (Left Orange Bridge 6) - 0x17DD7 - Stars & Black/White Squares & Colored Squares & Stars + Same Colored Symbol +158382 - 0x17DB8 (Left Orange Bridge 7) - 0x17DD9 - Stars & Black/White Squares & Colored Squares & Stars + Same Colored Symbol +158383 - 0x17DDC (Left Orange Bridge 8) - 0x17DB8 - Stars & Colored Squares & Stars + Same Colored Symbol +158384 - 0x17DD1 (Left Orange Bridge 9 & Directional) - 0x17DDC - Stars & Colored Squares & Stars + Same Colored Symbol +158385 - 0x17DDE (Left Orange Bridge 10) - 0x17DD1 - Stars & Colored Squares & Stars + Same Colored Symbol +158386 - 0x17DE3 (Left Orange Bridge 11) - 0x17DDE - Stars & Colored Squares & Stars + Same Colored Symbol +158387 - 0x17DEC (Left Orange Bridge 12) - 0x17DE3 - Stars & Black/White Squares & Stars + Same Colored Symbol +158388 - 0x17DAE (Left Orange Bridge 13) - 0x17DEC - Stars & Black/White Squares & Stars + Same Colored Symbol +158389 - 0x17DB0 (Left Orange Bridge 14) - 0x17DAE - Stars & Black/White Squares & Stars + Same Colored Symbol +158390 - 0x17DDB (Left Orange Bridge 15) - 0x17DB0 - Stars & Black/White Squares & Stars + Same Colored Symbol + +Treehouse Green Bridge (Treehouse) - Treehouse Green Bridge Front House - 0x17E61 - Treehouse Green Bridge Left House - 0x17E61: +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 +158373 - 0x17E5B (Green Bridge 5) - 0x17E52 - Stars & Shapers & Stars + Same Colored Symbol +158374 - 0x17E5F (Green Bridge 6) - 0x17E5B - Stars & Shapers & Negative Shapers & Stars + Same Colored Symbol & Rotated Shapers +158375 - 0x17E61 (Green Bridge 7) - 0x17E5F - Stars & Shapers & Rotated Shapers + +Treehouse Green Bridge Front House (Treehouse): +158610 - 0x17FA9 (Green Bridge Discard) - True - Triangles + +Treehouse Green Bridge Left House (Treehouse): +159211 - 0x220A7 (Right Orange Bridge EP) - 0x17DA2 - True + +Treehouse Laser Room Front Platform (Treehouse) - Treehouse Laser Room - 0x0C323: +Door - 0x0C323 (Laser House Entry) - 0x17DA2 & 0x2700B & 0x17DDB + +Treehouse Laser Room Back Platform (Treehouse): +158611 - 0x17FA0 (Laser Discard) - True - Triangles + +Treehouse Burned House (Treehouse): +159202 - 0x00769 (Burned House Beach EP) - True - True + +Treehouse Laser Room (Treehouse): +158712 - 0x03613 (Laser Panel) - True - True +158403 - 0x17CBC (Laser House Door Timer Inside) - True - True +Laser - 0x028A4 (Laser) - 0x03613 + +Mountainside (Mountainside) - Main Island - True - Mountaintop - True: +158612 - 0x17C42 (Discard) - True - Triangles +158665 - 0x002A6 (Vault) - True - Symmetry & Colored Dots & Black/White Squares +158666 - 0x03542 (Vault Box) - 0x002A6 - True +159301 - 0x335AE (Cloud Cycle EP) - True - True +159325 - 0x33505 (Bush EP) - True - True +159335 - 0x03C07 (Apparent River EP) - True - True + +Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34: +158405 - 0x0042D (River Shape) - True - True +158406 - 0x09F7F (Box Short) - 7 Lasers - True +158407 - 0x17C34 (Trap Door Triple Exit) - 0x09F7F - Black/White Squares +158800 - 0xFFF00 (Box Long) - 7 Lasers & 11 Lasers & 0x17C34 - True +159300 - 0x001A3 (River Shape EP) - True - True +159320 - 0x3370E (Arch Black EP) - True - True +159324 - 0x336C8 (Arch White Right EP) - True - True +159326 - 0x3369A (Arch White Left EP) - True - True + +Mountain Top Layer (Mountain Floor 1) - Mountain Top Layer Bridge - 0x09E39: +158408 - 0x09E39 (Light Bridge Controller) - True - Black/White Squares & Rotated Shapers + +Mountain Top Layer Bridge (Mountain Floor 1) - Mountain Floor 2 - 0x09E54: +158409 - 0x09E7A (Right Row 1) - True - Black/White Squares & Dots +158410 - 0x09E71 (Right Row 2) - 0x09E7A - Black/White Squares & Dots +158411 - 0x09E72 (Right Row 3) - 0x09E71 - Black/White Squares & Shapers +158412 - 0x09E69 (Right Row 4) - 0x09E72 - Black/White Squares & Dots +158413 - 0x09E7B (Right Row 5) - 0x09E69 - Black/White Squares & Dots +158414 - 0x09E73 (Left Row 1) - True - Black/White Squares & Dots +158415 - 0x09E75 (Left Row 2) - 0x09E73 - Black/White Squares & Dots +158416 - 0x09E78 (Left Row 3) - 0x09E75 - Dots & Shapers +158417 - 0x09E79 (Left Row 4) - 0x09E78 - Shapers & Rotated Shapers +158418 - 0x09E6C (Left Row 5) - 0x09E79 - Stars & Black/White Squares +158419 - 0x09E6F (Left Row 6) - 0x09E6C - Shapers +158420 - 0x09E6B (Left Row 7) - 0x09E6F - Dots +158421 - 0x33AF5 (Back Row 1) - True - Black/White Squares & Symmetry +158422 - 0x33AF7 (Back Row 2) - 0x33AF5 - Black/White Squares & Stars +158423 - 0x09F6E (Back Row 3) - 0x33AF7 - Symmetry & Dots +158424 - 0x09EAD (Trash Pillar 1) - True - Black/White Squares & Shapers +158425 - 0x09EAF (Trash Pillar 2) - 0x09EAD - Black/White Squares & Shapers +Door - 0x09E54 (Exit) - 0x09EAF & 0x09F6E & 0x09E6B & 0x09E7B + +Mountain Floor 2 (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Near - 0x09FFB - Mountain Floor 2 Blue Bridge - 0x09E86 - Mountain Pink Bridge EP - TrueOneWay: +158426 - 0x09FD3 (Near Row 1) - True - Colored Squares +158427 - 0x09FD4 (Near Row 2) - 0x09FD3 - Colored Squares & Dots +158428 - 0x09FD6 (Near Row 3) - 0x09FD4 - Stars & Colored Squares & Stars + Same Colored Symbol +158429 - 0x09FD7 (Near Row 4) - 0x09FD6 - Stars & Colored Squares & Stars + Same Colored Symbol & Shapers +158430 - 0x09FD8 (Near Row 5) - 0x09FD7 - Colored Squares & Symmetry +Door - 0x09FFB (Staircase Near) - 0x09FD8 + +Mountain Floor 2 Blue Bridge (Mountain Floor 2) - Mountain Floor 2 Beyond Bridge - TrueOneWay - Mountain Floor 2 At Door - TrueOneWay: + +Mountain Floor 2 At Door (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EDD: +Door - 0x09EDD (Elevator Room Entry) - 0x09ED8 & 0x09E86 + +Mountain Floor 2 Light Bridge Room Near (Mountain Floor 2): +158431 - 0x09E86 (Light Bridge Controller Near) - True - Stars & Black/White Squares + +Mountain Floor 2 Beyond Bridge (Mountain Floor 2) - Mountain Floor 2 Light Bridge Room Far - 0x09E07 - Mountain Pink Bridge EP - TrueOneWay: +158432 - 0x09FCC (Far Row 1) - True - Dots +158433 - 0x09FCE (Far Row 2) - 0x09FCC - Black/White Squares +158434 - 0x09FCF (Far Row 3) - 0x09FCE - Shapers +158435 - 0x09FD0 (Far Row 4) - 0x09FCF - Stars +158436 - 0x09FD1 (Far Row 5) - 0x09FD0 - Black/White Squares +158437 - 0x09FD2 (Far Row 6) - 0x09FD1 - Shapers +Door - 0x09E07 (Staircase Far) - 0x09FD2 + +Mountain Floor 2 Light Bridge Room Far (Mountain Floor 2): +158438 - 0x09ED8 (Light Bridge Controller Far) - True - Stars & Black/White Squares + +Mountain Floor 2 Elevator Room (Mountain Floor 2) - Mountain Floor 2 Elevator - TrueOneWay: +158613 - 0x17F93 (Elevator Discard) - True - Triangles + +Mountain Floor 2 Elevator (Mountain Floor 2) - Mountain Floor 2 Elevator Room - 0x09EEB - Mountain Third Layer - 0x09EEB: +158439 - 0x09EEB (Elevator Control Panel) - True - Dots + +Mountain Third Layer (Mountain Bottom Floor) - Mountain Floor 2 Elevator - TrueOneWay - Mountain Bottom Floor - 0x09F89: +158440 - 0x09FC1 (Giant Puzzle Bottom Left) - True - Shapers & Eraser +158441 - 0x09F8E (Giant Puzzle Bottom Right) - True - Rotated Shapers & Eraser +158442 - 0x09F01 (Giant Puzzle Top Right) - True - Shapers & Eraser +158443 - 0x09EFF (Giant Puzzle Top Left) - True - Shapers & Eraser +158444 - 0x09FDA (Giant Puzzle) - 0x09FC1 & 0x09F8E & 0x09F01 & 0x09EFF - Shapers & Symmetry +Door - 0x09F89 (Exit) - 0x09FDA + +Mountain Bottom Floor (Mountain Bottom Floor) - Mountain Bottom Floor Rock - 0x17FA2 - Final Room - 0x0C141 - Mountain Pink Bridge EP - TrueOneWay: +158614 - 0x17FA2 (Discard) - 0xFFF00 - Triangles +158445 - 0x01983 (Final Room Entry Left) - True - Shapers & Stars +158446 - 0x01987 (Final Room Entry Right) - True - Colored Squares & Dots +Door - 0x0C141 (Final Room Entry) - 0x01983 & 0x01987 +159313 - 0x09D5D (Yellow Bridge EP) - 0x09E86 & 0x09ED8 - True +159314 - 0x09D5E (Blue Bridge EP) - 0x09E86 & 0x09ED8 - True + +Mountain Pink Bridge EP (Mountain Floor 2): +159312 - 0x09D63 (Pink Bridge EP) - 0x09E39 - True + +Mountain Bottom Floor Rock (Mountain Bottom Floor) - Mountain Bottom Floor - 0x17F33 - Mountain Path to Caves - 0x17F33: +Door - 0x17F33 (Rock Open) - True - True + +Mountain Path to Caves (Mountain Bottom Floor) - Mountain Bottom Floor Rock - 0x334E1 - Caves - 0x2D77D: +158447 - 0x00FF8 (Caves Entry Panel) - True - Black/White Squares +Door - 0x2D77D (Caves Entry) - 0x00FF8 +158448 - 0x334E1 (Rock Control) - True - True + +Caves (Caves) - Main Island - 0x2D73F | 0x2D859 - Path to Challenge - 0x019A5: +158451 - 0x335AB (Elevator Inside Control) - True - Dots & Black/White Squares +158452 - 0x335AC (Elevator Upper Outside Control) - 0x335AB - Black/White Squares +158453 - 0x3369D (Elevator Lower Outside Control) - 0x335AB - Black/White Squares & Dots +158454 - 0x00190 (Blue Tunnel Right First 1) - True - Shapers +158455 - 0x00558 (Blue Tunnel Right First 2) - 0x00190 - Shapers +158456 - 0x00567 (Blue Tunnel Right First 3) - 0x00558 - Shapers +158457 - 0x006FE (Blue Tunnel Right First 4) - 0x00567 - Shapers +158458 - 0x01A0D (Blue Tunnel Left First 1) - True - Symmetry & Shapers +158459 - 0x008B8 (Blue Tunnel Left Second 1) - True - Shapers +158460 - 0x00973 (Blue Tunnel Left Second 2) - 0x008B8 - Shapers +158461 - 0x0097B (Blue Tunnel Left Second 3) - 0x00973 - Shapers +158462 - 0x0097D (Blue Tunnel Left Second 4) - 0x0097B - Shapers +158463 - 0x0097E (Blue Tunnel Left Second 5) - 0x0097D - Shapers +158464 - 0x00994 (Blue Tunnel Right Second 1) - True - Shapers +158465 - 0x334D5 (Blue Tunnel Right Second 2) - 0x00994 - Shapers +158466 - 0x00995 (Blue Tunnel Right Second 3) - 0x334D5 - Shapers +158467 - 0x00996 (Blue Tunnel Right Second 4) - 0x00995 - Shapers +158468 - 0x00998 (Blue Tunnel Right Second 5) - 0x00996 - Shapers +158469 - 0x009A4 (Blue Tunnel Left Third 1) - True - Shapers +158470 - 0x018A0 (Blue Tunnel Right Third 1) - True - Shapers & Symmetry +158471 - 0x00A72 (Blue Tunnel Left Fourth 1) - True - Shapers & Negative Shapers +158472 - 0x32962 (First Floor Left) - True - Rotated Shapers +158473 - 0x32966 (First Floor Grounded) - True - Stars & Black/White Squares & Stars + Same Colored Symbol +158474 - 0x01A31 (First Floor Middle) - True - Colored Squares +158475 - 0x00B71 (First Floor Right) - True - Colored Squares & Stars & Stars + Same Colored Symbol & Eraser +158478 - 0x288EA (First Wooden Beam) - True - Shapers +158479 - 0x288FC (Second Wooden Beam) - True - Black/White Squares & Shapers & Rotated Shapers +158480 - 0x289E7 (Third Wooden Beam) - True - Black/White Squares +158481 - 0x288AA (Fourth Wooden Beam) - True - Black/White Squares & Shapers +158482 - 0x17FB9 (Left Upstairs Single) - True - Dots +158483 - 0x0A16B (Left Upstairs Left Row 1) - True - Dots & Full Dots +158484 - 0x0A2CE (Left Upstairs Left Row 2) - 0x0A16B - Dots & Full Dots +158485 - 0x0A2D7 (Left Upstairs Left Row 3) - 0x0A2CE - Dots & Full Dots +158486 - 0x0A2DD (Left Upstairs Left Row 4) - 0x0A2D7 - Dots & Full Dots +158487 - 0x0A2EA (Left Upstairs Left Row 5) - 0x0A2DD - Dots & Full Dots +158488 - 0x0008F (Right Upstairs Left Row 1) - True - Dots & Invisible Dots +158489 - 0x0006B (Right Upstairs Left Row 2) - 0x0008F - Dots & Invisible Dots +158490 - 0x0008B (Right Upstairs Left Row 3) - 0x0006B - Dots & Invisible Dots +158491 - 0x0008C (Right Upstairs Left Row 4) - 0x0008B - Dots & Invisible Dots +158492 - 0x0008A (Right Upstairs Left Row 5) - 0x0008C - Dots & Invisible Dots +158493 - 0x00089 (Right Upstairs Left Row 6) - 0x0008A - Dots & Invisible Dots +158494 - 0x0006A (Right Upstairs Left Row 7) - 0x00089 - Dots & Invisible Dots +158495 - 0x0006C (Right Upstairs Left Row 8) - 0x0006A - Dots & Invisible Dots +158496 - 0x00027 (Right Upstairs Right Row 1) - True - Dots & Invisible Dots & Symmetry +158497 - 0x00028 (Right Upstairs Right Row 2) - 0x00027 - Dots & Invisible Dots & Symmetry +158498 - 0x00029 (Right Upstairs Right Row 3) - 0x00028 - Dots & Invisible Dots & Symmetry +158476 - 0x09DD5 (Lone Pillar) - True - Triangles +Door - 0x019A5 (Pillar Door) - 0x09DD5 +158449 - 0x021D7 (Mountain Shortcut Panel) - True - Stars +Door - 0x2D73F (Mountain Shortcut Door) - 0x021D7 +158450 - 0x17CF2 (Swamp Shortcut Panel) - True - Triangles +Door - 0x2D859 (Swamp Shortcut Door) - 0x17CF2 +159341 - 0x3397C (Skylight EP) - True - True + +Path to Challenge (Caves) - Challenge - 0x0A19A: +158477 - 0x0A16E (Challenge Entry Panel) - True - Stars & Shapers & Stars + Same Colored Symbol +Door - 0x0A19A (Challenge Entry) - 0x0A16E + +Challenge (Challenge) - Tunnels - 0x0348A: +158499 - 0x0A332 (Start Timer) - 11 Lasers - True +158500 - 0x0088E (Small Basic) - 0x0A332 - True +158501 - 0x00BAF (Big Basic) - 0x0088E - True +158502 - 0x00BF3 (Square) - 0x00BAF - 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 - Black/White Squares +158509 - 0x00CA1 (Choice Squares Middle) - 0x00CD4 - Black/White Squares +158510 - 0x00C80 (Choice Squares Left) - 0x00CD4 - Black/White Squares +158511 - 0x00C68 (Choice Squares 2 Right) - 0x00CB9 | 0x00CA1 | 0x00C80 - Black/White Squares & Colored Squares +158512 - 0x00C59 (Choice Squares 2 Middle) - 0x00CB9 | 0x00CA1 | 0x00C80 - Black/White Squares & Colored Squares +158513 - 0x00C22 (Choice Squares 2 Left) - 0x00CB9 | 0x00CA1 | 0x00C80 - 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 +158517 - 0x1C319 (Squares Pillar) - 0x034F4 & 0x034EC - Black/White Squares & Symmetry +158667 - 0x0356B (Vault Box) - 0x1C31A & 0x1C319 - True +158518 - 0x039B4 (Tunnels Entry Panel) - True - Triangles +Door - 0x0348A (Tunnels Entry) - 0x039B4 +159530 - 0x28B30 (Water EP) - True - True + +Tunnels (Tunnels) - 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 (Theater Shortcut) - 0x27732 +158520 - 0x2773D (Desert Shortcut Panel) - True - True +Door - 0x27263 (Desert Shortcut) - 0x2773D +158521 - 0x09E85 (Town Shortcut Panel) - True - Triangles +Door - 0x09E87 (Town Shortcut) - 0x09E85 +159557 - 0x33A20 (Theater Flowers EP) - 0x03553 & Theater to Tunnels - True + +Final Room (Mountain Final Room) - Elevator - 0x339BB & 0x33961: +158522 - 0x0383A (Right Pillar 1) - True - Stars +158523 - 0x09E56 (Right Pillar 2) - 0x0383A - Stars & Dots +158524 - 0x09E5A (Right Pillar 3) - 0x09E56 - Dots & Full Dots +158525 - 0x33961 (Right Pillar 4) - 0x09E5A - Dots & Symmetry +158526 - 0x0383D (Left Pillar 1) - True - Dots & Full Dots +158527 - 0x0383F (Left Pillar 2) - 0x0383D - Black/White Squares +158528 - 0x03859 (Left Pillar 3) - 0x0383F - Shapers +158529 - 0x339BB (Left Pillar 4) - 0x03859 - Black/White Squares & Stars & Symmetry + +Elevator (Mountain Final Room): +158530 - 0x3D9A6 (Elevator Door Closer Left) - True - True +158531 - 0x3D9A7 (Elevator Door Close Right) - True - True +158532 - 0x3C113 (Elevator Entry Left) - 0x3D9A6 | 0x3D9A7 - True +158533 - 0x3C114 (Elevator Entry Right) - 0x3D9A6 | 0x3D9A7 - True +158534 - 0x3D9AA (Back Wall Left) - 0x3D9A6 | 0x3D9A7 - True +158535 - 0x3D9A8 (Back Wall Right) - 0x3D9A6 | 0x3D9A7 - True +158536 - 0x3D9A9 (Elevator Start) - 0x3D9AA & 7 Lasers | 0x3D9A8 & 7 Lasers - 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: +159042 - 0x22106 (Desert EP) - True - True +159223 - 0x03B25 (Shipwreck CCW Underside EP) - True - True +159231 - 0x28B29 (Shipwreck Green EP) - True - True +159232 - 0x28B2A (Shipwreck CW Underside EP) - True - True +159323 - 0x03D0D (Bunker Yellow Line EP) - True - True +159515 - 0x28A37 (Town Long Sewer EP) - True - True +159520 - 0x33857 (Tutorial EP) - True - True +159521 - 0x33879 (Tutorial Reflection EP) - True - True +159522 - 0x03C19 (Tutorial Moss EP) - True - True +159531 - 0x035C9 (Cargo Box EP) - 0x0A0C9 - True + +Obelisks (EPs) - Entry - True: +159700 - 0xFFE00 (Desert Obelisk Side 1) - 0x0332B & 0x03367 & 0x28B8A - True +159701 - 0xFFE01 (Desert Obelisk Side 2) - 0x037B6 & 0x037B2 & 0x000F7 - True +159702 - 0xFFE02 (Desert Obelisk Side 3) - 0x3351D - True +159703 - 0xFFE03 (Desert Obelisk Side 4) - 0x0053C & 0x00771 & 0x335C8 & 0x335C9 & 0x337F8 & 0x037BB & 0x220E4 & 0x220E5 - True +159704 - 0xFFE04 (Desert Obelisk Side 5) - 0x334B9 & 0x334BC & 0x22106 & 0x0A14C & 0x0A14D - True +159710 - 0xFFE10 (Monastery Obelisk Side 1) - 0x03ABC & 0x03ABE & 0x03AC0 & 0x03AC4 - True +159711 - 0xFFE11 (Monastery Obelisk Side 2) - 0x03AC5 - True +159712 - 0xFFE12 (Monastery Obelisk Side 3) - 0x03BE2 & 0x03BE3 & 0x0A409 - True +159713 - 0xFFE13 (Monastery Obelisk Side 4) - 0x006E5 & 0x006E6 & 0x006E7 & 0x034A7 & 0x034AD & 0x034AF & 0x03DAB & 0x03DAC & 0x03DAD - True +159714 - 0xFFE14 (Monastery Obelisk Side 5) - 0x03E01 - True +159715 - 0xFFE15 (Monastery Obelisk Side 6) - 0x289F4 & 0x289F5 - True +159720 - 0xFFE20 (Treehouse Obelisk Side 1) - 0x0053D & 0x0053E & 0x00769 - True +159721 - 0xFFE21 (Treehouse Obelisk Side 2) - 0x33721 & 0x220A7 & 0x220BD - True +159722 - 0xFFE22 (Treehouse Obelisk Side 3) - 0x03B22 & 0x03B23 & 0x03B24 & 0x03B25 & 0x03A79 & 0x28ABD & 0x28ABE - True +159723 - 0xFFE23 (Treehouse Obelisk Side 4) - 0x3388F & 0x28B29 & 0x28B2A - True +159724 - 0xFFE24 (Treehouse Obelisk Side 5) - 0x018B6 & 0x033BE & 0x033BF & 0x033DD & 0x033E5 - True +159725 - 0xFFE25 (Treehouse Obelisk Side 6) - 0x28AE9 & 0x3348F - True +159730 - 0xFFE30 (River Obelisk Side 1) - 0x001A3 & 0x335AE - True +159731 - 0xFFE31 (River Obelisk Side 2) - 0x000D3 & 0x035F5 & 0x09D5D & 0x09D5E & 0x09D63 - True +159732 - 0xFFE32 (River Obelisk Side 3) - 0x3370E & 0x035DE & 0x03601 & 0x03603 & 0x03D0D & 0x3369A & 0x336C8 & 0x33505 - True +159733 - 0xFFE33 (River Obelisk Side 4) - 0x03A9E & 0x016B2 & 0x3365F & 0x03731 & 0x036CE & 0x03C07 & 0x03A93 - True +159734 - 0xFFE34 (River Obelisk Side 5) - 0x03AA6 & 0x3397C & 0x0105D & 0x0A304 - True +159735 - 0xFFE35 (River Obelisk Side 6) - 0x035CB & 0x035CF - True +159740 - 0xFFE40 (Quarry Obelisk Side 1) - 0x28A7B & 0x005F6 & 0x00859 & 0x17CB9 & 0x28A4A - True +159741 - 0xFFE41 (Quarry Obelisk Side 2) - 0x334B6 & 0x00614 & 0x0069D & 0x28A4C - True +159742 - 0xFFE42 (Quarry Obelisk Side 3) - 0x289CF & 0x289D1 & 0x33692 - True +159743 - 0xFFE43 (Quarry Obelisk Side 4) - 0x03E77 & 0x03E7C - True +159750 - 0xFFE50 (Town Obelisk Side 1) - 0x035C7 - True +159751 - 0xFFE51 (Town Obelisk Side 2) - 0x01848 & 0x03D06 & 0x33530 & 0x33600 & 0x28A2F & 0x28A37 & 0x334A3 & 0x3352F - True +159752 - 0xFFE52 (Town Obelisk Side 3) - 0x33857 & 0x33879 & 0x03C19 - True +159753 - 0xFFE53 (Town Obelisk Side 4) - 0x28B30 & 0x035C9 - True +159754 - 0xFFE54 (Town Obelisk Side 5) - 0x03335 & 0x03412 & 0x038A6 & 0x038AA & 0x03E3F & 0x03E40 & 0x28B8E - True +159755 - 0xFFE55 (Town Obelisk Side 6) - 0x28B91 & 0x03BCE & 0x03BCF & 0x03BD1 & 0x339B6 & 0x33A20 & 0x33A29 & 0x33A2A & 0x33B06 - True \ No newline at end of file diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index 845ca3ddef..da0854446e 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -38,7 +38,7 @@ class WitnessWorld(World): """ game = "The Witness" topology_present = False - data_version = 8 + data_version = 11 static_logic = StaticWitnessLogic() static_locat = StaticWitnessLocations() @@ -52,22 +52,27 @@ class WitnessWorld(World): location_name_to_id = StaticWitnessLocations.ALL_LOCATIONS_TO_ID item_name_groups = StaticWitnessItems.ITEM_NAME_GROUPS - required_client_version = (0, 3, 6) + required_client_version = (0, 3, 8) def _get_slot_data(self): return { 'seed': self.multiworld.random.randint(0, 1000000), 'victory_location': int(self.player_logic.VICTORY_LOCATION, 16), 'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID, - 'item_id_to_door_hexes': self.items.ITEM_ID_TO_DOOR_HEX, - 'door_hexes': self.items.DOORS, + 'item_id_to_door_hexes': self.static_items.ITEM_ID_TO_DOOR_HEX_ALL, + 'door_hexes_in_the_pool': self.items.DOORS, 'symbols_not_in_the_game': self.items.SYMBOLS_NOT_IN_THE_GAME, 'disabled_panels': self.player_logic.COMPLETELY_DISABLED_CHECKS, 'log_ids_to_hints': self.log_ids_to_hints, - 'progressive_item_lists': self.items.MULTI_LISTS_BY_CODE + 'progressive_item_lists': self.items.MULTI_LISTS_BY_CODE, + 'obelisk_side_id_to_EPs': self.static_logic.OBELISK_SIDE_ID_TO_EP_HEXES, + 'precompleted_puzzles': {int(h, 16) for h in self.player_logic.PRECOMPLETED_LOCATIONS}, + 'ep_to_name': self.static_logic.EP_ID_TO_NAME, } def generate_early(self): + self.items_by_name = dict() + if not (is_option_enabled(self.multiworld, self.player, "shuffle_symbols") or get_option_value(self.multiworld, self.player, "shuffle_doors") or is_option_enabled(self.multiworld, self.player, "shuffle_lasers")): @@ -78,45 +83,52 @@ class WitnessWorld(World): 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.multiworld, self.player) + disabled_locations = self.multiworld.exclude_locations[self.player].value + + self.player_logic = WitnessPlayerLogic( + self.multiworld, self.player, disabled_locations, self.multiworld.start_inventory[self.player].value + ) + self.locat = WitnessPlayerLocations(self.multiworld, self.player, self.player_logic) self.items = WitnessPlayerItems(self.locat, self.multiworld, self.player, self.player_logic) self.regio = WitnessRegions(self.locat) - self.log_ids_to_hints = dict() - - self.junk_items_created = {key: 0 for key in self.items.JUNK_WEIGHTS.keys()} + def create_regions(self): + self.regio.create_regions(self.multiworld, self.player, self.player_logic) def generate_basic(self): + self.log_ids_to_hints = dict() + self.junk_items_created = {key: 0 for key in self.items.JUNK_WEIGHTS.keys()} + # Generate item pool pool = [] - items_by_name = dict() for item in self.items.ITEM_TABLE: for i in range(0, self.items.PROG_ITEM_AMOUNTS[item]): if item in self.items.PROGRESSION_TABLE: witness_item = self.create_item(item) pool.append(witness_item) - items_by_name[item] = witness_item + self.items_by_name[item] = witness_item less_junk = 0 - # Put good item on first check if symbol shuffle is on - symbols = is_option_enabled(self.multiworld, self.player, "shuffle_symbols") + dog_check = self.multiworld.get_location( + "Town Pet the Dog", self.player + ) - if symbols and get_option_value(self.multiworld, self.player, "puzzle_randomization") != 1: - random_good_item = self.multiworld.random.choice(self.items.GOOD_ITEMS) + dog_check.place_locked_item(self.create_item("Puzzle Skip")) - first_check = self.multiworld.get_location( - "Tutorial Gate Open", self.player - ) - first_check.place_locked_item(items_by_name[random_good_item]) - pool.remove(items_by_name[random_good_item]) + less_junk += 1 - less_junk = 1 + for precol_item in self.multiworld.precollected_items[self.player]: + if precol_item.name in self.items_by_name: # if item is in the pool, remove 1 instance. + item_obj = self.items_by_name[precol_item.name] + + if item_obj in pool: + pool.remove(item_obj) # remove one instance of this pre-collected item if it exists for item in self.player_logic.STARTING_INVENTORY: - self.multiworld.push_precollected(items_by_name[item]) - pool.remove(items_by_name[item]) + self.multiworld.push_precollected(self.items_by_name[item]) + pool.remove(self.items_by_name[item]) for item in self.items.EXTRA_AMOUNTS: for i in range(0, self.items.EXTRA_AMOUNTS[item]): @@ -140,8 +152,25 @@ class WitnessWorld(World): self.multiworld.itempool += pool - def create_regions(self): - self.regio.create_regions(self.multiworld, self.player, self.player_logic) + def pre_fill(self): + # Put good item on first check if there are any of the designated "good items" in the pool + good_items_in_the_game = [] + + for symbol in self.items.GOOD_ITEMS: + item = self.items_by_name[symbol] + if item in self.multiworld.itempool: # Only do this if the item is still in item pool (e.g. after plando) + good_items_in_the_game.append(symbol) + + if good_items_in_the_game: + random_good_item = self.multiworld.random.choice(good_items_in_the_game) + + first_check = self.multiworld.get_location( + "Tutorial Gate Open", self.player + ) + item = self.items_by_name[random_good_item] + + first_check.place_locked_item(item) + self.multiworld.itempool.remove(item) def set_rules(self): set_rules(self.multiworld, self.player, self.player_logic, self.locat) @@ -150,7 +179,9 @@ class WitnessWorld(World): hint_amount = get_option_value(self.multiworld, self.player, "hint_amount") credits_hint = ( - "This Randomizer", "is brought to you by", "NewSoupVi, Jarno, jbzdarkid, sigma144, IHNN, blastron", -1 + "This Randomizer is brought to you by", + "NewSoupVi, Jarno, blastron,", + "jbzdarkid, sigma144, IHNN, oddGarrett.", -1 ) audio_logs = get_audio_logs().copy() @@ -158,12 +189,12 @@ class WitnessWorld(World): if hint_amount != 0: generated_hints = make_hints(self.multiworld, self.player, hint_amount) - self.multiworld.random.shuffle(audio_logs) + self.multiworld.slot_seeds[self.player].shuffle(audio_logs) duplicates = len(audio_logs) // hint_amount for _ in range(0, hint_amount): - hint = generated_hints.pop() + hint = generated_hints.pop(0) for _ in range(0, duplicates): audio_log = audio_logs.pop() @@ -173,7 +204,7 @@ class WitnessWorld(World): audio_log = audio_logs.pop() self.log_ids_to_hints[int(audio_log, 16)] = credits_hint - joke_hints = generate_joke_hints(self.multiworld, len(audio_logs)) + joke_hints = generate_joke_hints(self.multiworld, self.player, len(audio_logs)) while audio_logs: audio_log = audio_logs.pop() @@ -192,7 +223,7 @@ class WitnessWorld(World): def create_item(self, name: str) -> Item: # this conditional is purely for unit tests, which need to be able to create an item before generate_early - if hasattr(self, 'items'): + if hasattr(self, 'items') and name in self.items.ITEM_TABLE: item = self.items.ITEM_TABLE[name] else: item = StaticWitnessItems.ALL_ITEM_TABLE[name] diff --git a/worlds/witness/docs/en_The Witness.md b/worlds/witness/docs/en_The Witness.md index 86ee8e9311..2ae478bed0 100644 --- a/worlds/witness/docs/en_The Witness.md +++ b/worlds/witness/docs/en_The Witness.md @@ -35,4 +35,15 @@ Example: "Shipwreck Vault contains Triangles". ## The Jungle, Orchard, Forest and Color House aren't randomized. What gives? There are limitations to what can currently be randomized in The Witness. -There is an option to turn these non-randomized panels off, called "disable_non_randomized" in your yaml file. This will also slightly change the activation requirement of certain panels, detailed [here](https://github.com/sigma144/witness-randomizer/wiki/Activation-Triggers). \ No newline at end of file +There is an option to turn these non-randomized panels off, called "disable_non_randomized" in your yaml file. This will also slightly change the activation requirement of certain panels, detailed [here](https://github.com/sigma144/witness-randomizer/wiki/Activation-Triggers). + +## A note on starting inventory and excluded locations + +In this randomizer, items added to start_inventory will be removed from the item pool (as many copies as specified). + +It is also possible to add items to the starting inventory that are not part of the mode you are playing. +In this case, the generator will make its best attempt to adjust logic accordingly. +One of the use cases of this could be to pre-open a specific door or pre-activate a single laser. + +In "shuffle_EPs: obelisk_sides", any Environmental Puzzles in exclude_locations will be pre-completed and not considered for their Obelisk Side. +If every Environmental Puzzle on an Obelisk Side is pre-completed, that side disappears from the location pool entirely. \ No newline at end of file diff --git a/worlds/witness/hints.py b/worlds/witness/hints.py index b815ba4d12..898bd4dc55 100644 --- a/worlds/witness/hints.py +++ b/worlds/witness/hints.py @@ -17,7 +17,7 @@ joke_hints = [ ("Have you tried ChecksFinder?", "If you like puzzles,", "you might enjoy it!"), ("Have you tried Dark Souls III?", "A tough game like this", "feels better when friends are helping you!"), ("Have you tried Donkey Kong Country 3?", "A legendary game", "from a golden age of platformers!"), - ("Have you tried Factorio?", "Alone in an unknown world.", "Sound familiar?"), + ("Have you tried Factorio?", "Alone in an unknown multiworld.", "Sound familiar?"), ("Have you tried Final Fantasy?", "Experience a classic game", "improved to fit modern standards!"), ("Have you tried Hollow Knight?", "Another independent hit", "revolutionising a genre!"), ("Have you tried A Link to the Past?", "The Archipelago game", "that started it all!"), @@ -95,17 +95,18 @@ joke_hints = [ ] -def get_always_hint_items(world: MultiWorld, player: int): +def get_always_hint_items(multiworld: MultiWorld, player: int): priority = [ "Boat", "Mountain Bottom Floor Final Room Entry (Door)", "Caves Mountain Shortcut (Door)", "Caves Swamp Shortcut (Door)", "Caves Exits to Main Island", + "Progressive Dots", ] - difficulty = get_option_value(world, player, "puzzle_randomization") - discards = is_option_enabled(world, player, "shuffle_discards") + difficulty = get_option_value(multiworld, player, "puzzle_randomization") + discards = is_option_enabled(multiworld, player, "shuffle_discarded_panels") if discards: if difficulty == 1: @@ -116,16 +117,19 @@ def get_always_hint_items(world: MultiWorld, player: int): return priority -def get_always_hint_locations(world: MultiWorld, player: int): +def get_always_hint_locations(multiworld: MultiWorld, player: int): return { "Swamp Purple Underwater", "Shipwreck Vault Box", "Challenge Vault Box", "Mountain Bottom Floor Discard", + "Theater Eclipse EP", + "Shipwreck Couch EP", + "Mountainside Cloud Cycle EP", } -def get_priority_hint_items(world: MultiWorld, player: int): +def get_priority_hint_items(multiworld: MultiWorld, player: int): priority = { "Negative Shapers", "Sound Dots", @@ -135,7 +139,7 @@ def get_priority_hint_items(world: MultiWorld, player: int): "Swamp Laser Shortcut (Door)", } - if is_option_enabled(world, player, "shuffle_lasers"): + if is_option_enabled(multiworld, player, "shuffle_lasers"): lasers = { "Symmetry Laser", "Desert Laser", @@ -150,18 +154,18 @@ def get_priority_hint_items(world: MultiWorld, player: int): "Shadows Laser", } - if get_option_value(world, player, "doors") >= 2: + if get_option_value(multiworld, player, "doors") >= 2: priority.add("Desert Laser") lasers.remove("Desert Laser") - priority.update(world.random.sample(lasers, 2)) + priority.update(multiworld.slot_seeds[player].sample(lasers, 2)) else: - priority.update(world.random.sample(lasers, 3)) + priority.update(multiworld.slot_seeds[player].sample(lasers, 3)) return priority -def get_priority_hint_locations(world: MultiWorld, player: int): +def get_priority_hint_locations(multiworld: MultiWorld, player: int): return { "Town RGB Room Left", "Town RGB Room Right", @@ -171,75 +175,85 @@ def get_priority_hint_locations(world: MultiWorld, player: int): "Desert Vault Box", "Mountainside Vault Box", "Mountainside Discard", + "Tunnels Theater Flowers EP", + "Boat Shipwreck Green EP", } -def make_hint_from_item(world: MultiWorld, player: int, item: str): - location_obj = world.find_item(item, player).item.location +def make_hint_from_item(multiworld: MultiWorld, player: int, item: str): + location_obj = multiworld.find_item(item, player).item.location location_name = location_obj.name if location_obj.player != player: - location_name += " (" + world.get_player_name(location_obj.player) + ")" + location_name += " (" + multiworld.get_player_name(location_obj.player) + ")" return location_name, item, location_obj.address if(location_obj.player == player) else -1 -def make_hint_from_location(world: MultiWorld, player: int, location: str): - location_obj = world.get_location(location, player) - item_obj = world.get_location(location, player).item +def make_hint_from_location(multiworld: MultiWorld, player: int, location: str): + location_obj = multiworld.get_location(location, player) + item_obj = multiworld.get_location(location, player).item item_name = item_obj.name if item_obj.player != player: - item_name += " (" + world.get_player_name(item_obj.player) + ")" + item_name += " (" + multiworld.get_player_name(item_obj.player) + ")" return location, item_name, location_obj.address if(location_obj.player == player) else -1 -def make_hints(world: MultiWorld, player: int, hint_amount: int): +def make_hints(multiworld: MultiWorld, player: int, hint_amount: int): hints = list() prog_items_in_this_world = { - item.name for item in world.get_items() + item.name for item in multiworld.get_items() if item.player == player and item.code and item.advancement } loc_in_this_world = { - location.name for location in world.get_locations() - if location.player == player and not location.event + location.name for location in multiworld.get_locations() + if location.player == player and location.address } always_locations = [ - location for location in get_always_hint_locations(world, player) + location for location in get_always_hint_locations(multiworld, player) if location in loc_in_this_world ] always_items = [ - item for item in get_always_hint_items(world, player) + item for item in get_always_hint_items(multiworld, player) if item in prog_items_in_this_world ] priority_locations = [ - location for location in get_priority_hint_locations(world, player) + location for location in get_priority_hint_locations(multiworld, player) if location in loc_in_this_world ] priority_items = [ - item for item in get_priority_hint_items(world, player) + item for item in get_priority_hint_items(multiworld, player) if item in prog_items_in_this_world ] always_hint_pairs = dict() for item in always_items: - hint_pair = make_hint_from_item(world, player, item) + hint_pair = make_hint_from_item(multiworld, player, item) + + if hint_pair[2] == 158007: # Tutorial Gate Open + continue + always_hint_pairs[hint_pair[0]] = (hint_pair[1], True, hint_pair[2]) for location in always_locations: - hint_pair = make_hint_from_location(world, player, location) + hint_pair = make_hint_from_location(multiworld, player, location) always_hint_pairs[hint_pair[0]] = (hint_pair[1], False, hint_pair[2]) priority_hint_pairs = dict() for item in priority_items: - hint_pair = make_hint_from_item(world, player, item) + hint_pair = make_hint_from_item(multiworld, player, item) + + if hint_pair[2] == 158007: # Tutorial Gate Open + continue + priority_hint_pairs[hint_pair[0]] = (hint_pair[1], True, hint_pair[2]) for location in priority_locations: - hint_pair = make_hint_from_location(world, player, location) + hint_pair = make_hint_from_location(multiworld, player, location) priority_hint_pairs[hint_pair[0]] = (hint_pair[1], False, hint_pair[2]) for loc, item in always_hint_pairs.items(): @@ -248,17 +262,19 @@ def make_hints(world: MultiWorld, player: int, hint_amount: int): else: hints.append((loc, "contains", item[0], item[2])) - next_random_hint_is_item = world.random.randint(0, 2) + multiworld.slot_seeds[player].shuffle(hints) # shuffle always hint order in case of low hint amount + + next_random_hint_is_item = multiworld.slot_seeds[player].randint(0, 2) prog_items_in_this_world = sorted(list(prog_items_in_this_world)) locations_in_this_world = sorted(list(loc_in_this_world)) - world.random.shuffle(prog_items_in_this_world) - world.random.shuffle(locations_in_this_world) + multiworld.slot_seeds[player].shuffle(prog_items_in_this_world) + multiworld.slot_seeds[player].shuffle(locations_in_this_world) while len(hints) < hint_amount: if priority_hint_pairs: - loc = world.random.choice(list(priority_hint_pairs.keys())) + loc = multiworld.slot_seeds[player].choice(list(priority_hint_pairs.keys())) item = priority_hint_pairs[loc] del priority_hint_pairs[loc] @@ -273,10 +289,10 @@ def make_hints(world: MultiWorld, player: int, hint_amount: int): next_random_hint_is_item = not next_random_hint_is_item continue - hint = make_hint_from_item(world, player, prog_items_in_this_world.pop()) + hint = make_hint_from_item(multiworld, player, prog_items_in_this_world.pop()) hints.append((hint[1], "can be found at", hint[0], hint[2])) else: - hint = make_hint_from_location(world, player, locations_in_this_world.pop()) + hint = make_hint_from_location(multiworld, player, locations_in_this_world.pop()) hints.append((hint[0], "contains", hint[1], hint[2])) next_random_hint_is_item = not next_random_hint_is_item @@ -284,5 +300,5 @@ def make_hints(world: MultiWorld, player: int, hint_amount: int): return hints -def generate_joke_hints(world: MultiWorld, amount: int): - return [(x, y, z, -1) for (x, y, z) in world.random.sample(joke_hints, amount)] +def generate_joke_hints(multiworld: MultiWorld, player: int, amount: int): + return [(x, y, z, -1) for (x, y, z) in multiworld.slot_seeds[player].sample(joke_hints, amount)] diff --git a/worlds/witness/items.py b/worlds/witness/items.py index cbb1554096..914c1af2c0 100644 --- a/worlds/witness/items.py +++ b/worlds/witness/items.py @@ -51,6 +51,8 @@ class StaticWitnessItems: ALL_JUNK_ITEMS = set(BONUS_WEIGHTS.keys()) | set(TRAP_WEIGHTS.keys()) + ITEM_ID_TO_DOOR_HEX_ALL = dict() + def __init__(self): item_tab = dict() @@ -62,6 +64,13 @@ class StaticWitnessItems: self.ITEM_NAME_GROUPS.setdefault("Symbols", set()).add(item[0]) + for progressive, item_list in StaticWitnessLogic.PROGRESSIVE_TO_ITEMS.items(): + if not item_list: + continue + + if item_list[0] in self.ITEM_NAME_GROUPS.setdefault("Symbols", set()): + self.ITEM_NAME_GROUPS.setdefault("Symbols", set()).add(progressive) + for item in StaticWitnessLogic.ALL_DOOR_ITEMS: item_tab[item[0]] = ItemData(158000 + item[1], True, False) @@ -91,6 +100,9 @@ class StaticWitnessItems: for key, item in item_tab.items(): self.ALL_ITEM_TABLE[key] = item + for door in StaticWitnessLogic.ALL_DOOR_ITEMS: + self.ITEM_ID_TO_DOOR_HEX_ALL[door[1] + 158000] = {int(door_hex, 16) for door_hex in door[2]} + class WitnessPlayerItems: """ @@ -101,10 +113,33 @@ class WitnessPlayerItems: def code(item_name: str): return StaticWitnessItems.ALL_ITEM_TABLE[item_name].code - def __init__(self, locat: WitnessPlayerLocations, world: MultiWorld, player: int, player_logic: WitnessPlayerLogic): + @staticmethod + def is_progression(item_name: str, multiworld: MultiWorld, player: int): + useless_doors = { + "River Monastery Shortcut (Door)", + "Jungle & River Shortcuts", + "Monastery Shortcut (Door)", + "Orchard Second Gate (Door)", + } + + if item_name in useless_doors: + return False + + ep_doors = { + "Monastery Garden Entry (Door)", + "Monastery Shortcuts", + } + + if item_name in ep_doors: + return get_option_value(multiworld, player, "shuffle_EPs") != 0 + + return True + + def __init__(self, locat: WitnessPlayerLocations, multiworld: MultiWorld, player: int, logic: WitnessPlayerLogic): """Adds event items after logic changes due to options""" self.EVENT_ITEM_TABLE = dict() self.ITEM_TABLE = copy.copy(StaticWitnessItems.ALL_ITEM_TABLE) + self.PROGRESSION_TABLE = dict() self.ITEM_ID_TO_DOOR_HEX = dict() @@ -116,27 +151,31 @@ class WitnessPlayerItems: self.EXTRA_AMOUNTS = { "Functioning Brain": 1, - "Puzzle Skip": get_option_value(world, player, "puzzle_skip_amount") + "Puzzle Skip": get_option_value(multiworld, player, "puzzle_skip_amount") } + for k, v in self.ITEM_TABLE.items(): + if v.progression and not self.is_progression(k, multiworld, player): + self.ITEM_TABLE[k] = ItemData(v.code, False, False, never_exclude=True) + for item in StaticWitnessLogic.ALL_SYMBOL_ITEMS.union(StaticWitnessLogic.ALL_DOOR_ITEMS): - if item[0] not in player_logic.PROG_ITEMS_ACTUALLY_IN_THE_GAME: + if item[0] not in 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: if item[0] in StaticWitnessLogic.PROGRESSIVE_TO_ITEMS: - self.PROG_ITEM_AMOUNTS[item[0]] = len(player_logic.MULTI_LISTS[item[0]]) + self.PROG_ITEM_AMOUNTS[item[0]] = len(logic.MULTI_LISTS[item[0]]) self.PROGRESSION_TABLE[item[0]] = self.ITEM_TABLE[item[0]] self.MULTI_LISTS_BY_CODE = dict() for item in self.PROG_ITEM_AMOUNTS: - multi_list = player_logic.MULTI_LISTS[item] + multi_list = logic.MULTI_LISTS[item] self.MULTI_LISTS_BY_CODE[self.code(item)] = [self.code(single_item) for single_item in multi_list] - for entity_hex, items in player_logic.DOOR_ITEMS_BY_ID.items(): + for entity_hex, items in logic.DOOR_ITEMS_BY_ID.items(): entity_hex_int = int(entity_hex, 16) self.DOORS.add(entity_hex_int) @@ -145,26 +184,33 @@ class WitnessPlayerItems: 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") + symbols = is_option_enabled(multiworld, player, "shuffle_symbols") if "shuffle_symbols" not in the_witness_options.keys(): symbols = True - doors = get_option_value(world, player, "shuffle_doors") + doors = get_option_value(multiworld, player, "shuffle_doors") - if doors and symbols: + self.GOOD_ITEMS = [] + + if symbols: self.GOOD_ITEMS = [ - "Progressive Dots", "Black/White Squares", "Symmetry" - ] - elif symbols: - self.GOOD_ITEMS = [ - "Progressive Dots", "Black/White Squares", "Progressive Stars", + "Dots", "Black/White Squares", "Stars", "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"): + if doors: + self.GOOD_ITEMS = [ + "Dots", "Black/White Squares", "Symmetry" + ] + + if is_option_enabled(multiworld, player, "shuffle_discarded_panels"): + if is_option_enabled(multiworld, player, "shuffle_discarded_panels"): + if get_option_value(multiworld, player, "puzzle_randomization") == 1: + self.GOOD_ITEMS.append("Arrows") + else: + self.GOOD_ITEMS.append("Triangles") + if not is_option_enabled(multiworld, player, "disable_non_randomized_puzzles"): self.GOOD_ITEMS.append("Colored Squares") self.GOOD_ITEMS = [ @@ -172,11 +218,11 @@ class WitnessPlayerItems: ] for event_location in locat.EVENT_LOCATION_TABLE: - location = player_logic.EVENT_ITEM_PAIRS[event_location] + location = logic.EVENT_ITEM_PAIRS[event_location] self.EVENT_ITEM_TABLE[location] = ItemData(None, True, True) self.ITEM_TABLE[location] = ItemData(None, True, True) - trap_percentage = get_option_value(world, player, "trap_percentage") + trap_percentage = get_option_value(multiworld, player, "trap_percentage") self.JUNK_WEIGHTS = dict() diff --git a/worlds/witness/locations.py b/worlds/witness/locations.py index cd27f3a943..14291f4ee2 100644 --- a/worlds/witness/locations.py +++ b/worlds/witness/locations.py @@ -13,13 +13,6 @@ class StaticWitnessLocations: """ ID_START = 158000 - TYPE_OFFSETS = { - "General": 0, - "Discard": 600, - "Vault": 650, - "Laser": 700, - } - EXTRA_LOCATIONS = { "Tutorial Front Left", "Tutorial Back Left", @@ -143,7 +136,214 @@ class StaticWitnessLocations: "Theater Mountain Video", "Town RGB Room Left", "Town RGB Room Right", + "Town Sound Room Right", "Swamp Purple Underwater", + + "First Hallway EP", + "Tutorial Cloud EP", + "Tutorial Patio Flowers EP", + "Tutorial Gate EP", + "Outside Tutorial Garden EP", + "Outside Tutorial Town Sewer EP", + "Outside Tutorial Path EP", + "Outside Tutorial Tractor EP", + "Main Island Thundercloud EP", + "Glass Factory Vase EP", + "Symmetry Island Glass Factory Black Line Reflection EP", + "Symmetry Island Glass Factory Black Line EP", + "Desert Sand Snake EP", + "Desert Facade Right EP", + "Desert Facade Left EP", + "Desert Stairs Left EP", + "Desert Stairs Right EP", + "Desert Broken Wall Straight EP", + "Desert Broken Wall Bend EP", + "Desert Shore EP", + "Desert Island EP", + "Desert Pond Room Near Reflection EP", + "Desert Pond Room Far Reflection EP", + "Desert Flood Room EP", + "Desert Elevator EP", + "Quarry Shore EP", + "Quarry Entrance Pipe EP", + "Quarry Sand Pile EP", + "Quarry Rock Line EP", + "Quarry Rock Line Reflection EP", + "Quarry Railroad EP", + "Quarry Mill Ramp EP", + "Quarry Mill Lift EP", + "Quarry Boathouse Moving Ramp EP", + "Quarry Boathouse Hook EP", + "Shadows Quarry Mill Rooftop Vent EP", + "Treehouse Beach Rock Shadow EP", + "Treehouse Beach Sand Shadow EP", + "Treehouse Beach Both Orange Bridges EP", + "Keep Red Flowers EP", + "Keep Purple Flowers EP", + "Shipwreck Circle Near EP", + "Shipwreck Circle Left EP", + "Shipwreck Circle Far EP", + "Shipwreck Stern EP", + "Shipwreck Rope Inner EP", + "Shipwreck Rope Outer EP", + "Shipwreck Couch EP", + "Keep Pressure Plates 1 EP", + "Keep Pressure Plates 2 EP", + "Keep Pressure Plates 3 EP", + "Keep Pressure Plates 4 Left Exit EP", + "Keep Pressure Plates 4 Right Exit EP", + "Keep Path EP", + "Keep Hedges EP", + "Monastery Facade Left Near EP", + "Monastery Facade Left Far Short EP", + "Monastery Facade Left Far Long EP", + "Monastery Facade Right Near EP", + "Monastery Facade Left Stairs EP", + "Monastery Facade Right Stairs EP", + "Monastery Grass Stairs EP", + "Monastery Left Shutter EP", + "Monastery Middle Shutter EP", + "Monastery Right Shutter EP", + "Town Windmill First Blade EP", + "Town Windmill Second Blade EP", + "Town Windmill Third Blade EP", + "Town Tower Underside First EP", + "Town Tower Underside Second EP", + "Town Tower Underside Third EP", + "Town Tower Underside Fourth EP", + "Town RGB House Red EP", + "Town RGB House Green EP", + "Town Maze Bridge Underside EP", + "Town Black Line Redirect EP", + "Town Black Line Church EP", + "Town Brown Bridge EP", + "Town Black Line Tower EP", + "Theater Eclipse EP", + "Theater Window EP", + "Theater Door EP", + "Theater Church EP", + "Jungle Long Arch Moss EP", + "Jungle Straight Left Moss EP", + "Jungle Pop-up Wall Moss EP", + "Jungle Short Arch Moss EP", + "Jungle Entrance EP", + "Jungle Tree Halo EP", + "Jungle Bamboo CCW EP", + "Jungle Bamboo CW EP", + "River Green Leaf Moss EP", + "River Monastery Garden Left EP", + "River Monastery Garden Right EP", + "River Monastery Wall EP", + "Bunker Tinted Door EP", + "Bunker Green Room Flowers EP", + "Swamp Purple Sand Middle EP", + "Swamp Purple Sand Top EP", + "Swamp Purple Sand Bottom EP", + "Swamp Sliding Bridge Left EP", + "Swamp Sliding Bridge Right EP", + "Swamp Cyan Underwater Sliding Bridge EP", + "Swamp Rotating Bridge CCW EP", + "Swamp Rotating Bridge CW EP", + "Swamp Boat EP", + "Swamp Long Bridge Side EP", + "Swamp Purple Underwater Right EP", + "Swamp Purple Underwater Left EP", + "Treehouse Buoy EP", + "Treehouse Right Orange Bridge EP", + "Treehouse Burned House Beach EP", + "Mountainside Cloud Cycle EP", + "Mountainside Bush EP", + "Mountainside Apparent River EP", + "Mountaintop River Shape EP", + "Mountaintop Arch Black EP", + "Mountaintop Arch White Right EP", + "Mountaintop Arch White Left EP", + "Mountain Bottom Floor Yellow Bridge EP", + "Mountain Bottom Floor Blue Bridge EP", + "Mountain Floor 2 Pink Bridge EP", + "Caves Skylight EP", + "Challenge Water EP", + "Tunnels Theater Flowers EP", + "Boat Desert EP", + "Boat Shipwreck CCW Underside EP", + "Boat Shipwreck Green EP", + "Boat Shipwreck CW Underside EP", + "Boat Bunker Yellow Line EP", + "Boat Town Long Sewer EP", + "Boat Tutorial EP", + "Boat Tutorial Reflection EP", + "Boat Tutorial Moss EP", + "Boat Cargo Box EP", + + "Desert Obelisk Side 1", + "Desert Obelisk Side 2", + "Desert Obelisk Side 3", + "Desert Obelisk Side 4", + "Desert Obelisk Side 5", + "Monastery Obelisk Side 1", + "Monastery Obelisk Side 2", + "Monastery Obelisk Side 3", + "Monastery Obelisk Side 4", + "Monastery Obelisk Side 5", + "Monastery Obelisk Side 6", + "Treehouse Obelisk Side 1", + "Treehouse Obelisk Side 2", + "Treehouse Obelisk Side 3", + "Treehouse Obelisk Side 4", + "Treehouse Obelisk Side 5", + "Treehouse Obelisk Side 6", + "River Obelisk Side 1", + "River Obelisk Side 2", + "River Obelisk Side 3", + "River Obelisk Side 4", + "River Obelisk Side 5", + "River Obelisk Side 6", + "Quarry Obelisk Side 1", + "Quarry Obelisk Side 2", + "Quarry Obelisk Side 3", + "Quarry Obelisk Side 4", + "Town Obelisk Side 1", + "Town Obelisk Side 2", + "Town Obelisk Side 3", + "Town Obelisk Side 4", + "Town Obelisk Side 5", + "Town Obelisk Side 6", + } + + OBELISK_SIDES = { + "Desert Obelisk Side 1", + "Desert Obelisk Side 2", + "Desert Obelisk Side 3", + "Desert Obelisk Side 4", + "Desert Obelisk Side 5", + "Monastery Obelisk Side 1", + "Monastery Obelisk Side 2", + "Monastery Obelisk Side 3", + "Monastery Obelisk Side 4", + "Monastery Obelisk Side 5", + "Monastery Obelisk Side 6", + "Treehouse Obelisk Side 1", + "Treehouse Obelisk Side 2", + "Treehouse Obelisk Side 3", + "Treehouse Obelisk Side 4", + "Treehouse Obelisk Side 5", + "Treehouse Obelisk Side 6", + "River Obelisk Side 1", + "River Obelisk Side 2", + "River Obelisk Side 3", + "River Obelisk Side 4", + "River Obelisk Side 5", + "River Obelisk Side 6", + "Quarry Obelisk Side 1", + "Quarry Obelisk Side 2", + "Quarry Obelisk Side 3", + "Quarry Obelisk Side 4", + "Town Obelisk Side 1", + "Town Obelisk Side 2", + "Town Obelisk Side 3", + "Town Obelisk Side 4", + "Town Obelisk Side 5", + "Town Obelisk Side 6", } CAVES_LOCATIONS = { @@ -170,8 +370,12 @@ class StaticWitnessLocations: "Caves Left Upstairs Left Row 5", "Tunnels Vault Box", - "Mountain Bottom Floor Discard", "Theater Challenge Video", + + "Caves Skylight EP", + "Challenge Water EP", + "Tunnels Theater Flowers EP", + "Tutorial Gate EP", } MOUNTAIN_UNREACHABLE_FROM_BEHIND = { @@ -183,6 +387,10 @@ class StaticWitnessLocations: "Mountain Floor 1 Trash Pillar 2", "Mountain Floor 2 Near Row 5", "Mountain Floor 2 Far Row 6", + + "Mountain Bottom Floor Yellow Bridge EP", + "Mountain Bottom Floor Blue Bridge EP", + "Mountain Floor 2 Pink Bridge EP", } MOUNTAIN_REACHABLE_FROM_BEHIND = { @@ -213,10 +421,11 @@ class StaticWitnessLocations: def get_event_name(panel_hex): """ Returns the event name of any given panel. - Currently this is always "Panelname Solved" """ - return StaticWitnessLogic.CHECKS_BY_HEX[panel_hex]["checkName"] + " Solved" + action = " Opened" if StaticWitnessLogic.CHECKS_BY_HEX[panel_hex]["panelType"] == "Door" else " Solved" + + return StaticWitnessLogic.CHECKS_BY_HEX[panel_hex]["checkName"] + action def __init__(self): all_loc_to_id = { @@ -286,11 +495,26 @@ class WitnessPlayerLocations: if is_option_enabled(world, player, "shuffle_vault_boxes"): self.PANEL_TYPES_TO_SHUFFLE.add("Vault") + if get_option_value(world, player, "shuffle_EPs") == 1: + self.PANEL_TYPES_TO_SHUFFLE.add("EP") + elif get_option_value(world, player, "shuffle_EPs") == 2: + self.PANEL_TYPES_TO_SHUFFLE.add("Obelisk Side") + + for obelisk_loc in StaticWitnessLocations.OBELISK_SIDES: + obelisk_loc_hex = StaticWitnessLogic.CHECKS_BY_NAME[obelisk_loc]["checkHex"] + if player_logic.REQUIREMENTS_BY_HEX[obelisk_loc_hex] == frozenset({frozenset()}): + self.CHECK_LOCATIONS.discard(obelisk_loc) + 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 -= { + StaticWitnessLogic.CHECKS_BY_HEX[panel]["checkName"] + for panel in player_logic.PRECOMPLETED_LOCATIONS + } + self.CHECK_LOCATIONS.discard(StaticWitnessLogic.CHECKS_BY_HEX[player_logic.VICTORY_LOCATION]["checkName"]) self.CHECK_LOCATIONS = self.CHECK_LOCATIONS - { @@ -301,8 +525,13 @@ class WitnessPlayerLocations: self.CHECK_PANELHEX_TO_ID = { StaticWitnessLogic.CHECKS_BY_NAME[ch]["checkHex"]: StaticWitnessLocations.ALL_LOCATIONS_TO_ID[ch] for ch in self.CHECK_LOCATIONS + if StaticWitnessLogic.CHECKS_BY_NAME[ch]["panelType"] in self.PANEL_TYPES_TO_SHUFFLE } + dog_hex = StaticWitnessLogic.CHECKS_BY_NAME["Town Pet the Dog"]["checkHex"] + dog_id = StaticWitnessLocations.ALL_LOCATIONS_TO_ID["Town Pet the Dog"] + self.CHECK_PANELHEX_TO_ID[dog_hex] = dog_id + self.CHECK_PANELHEX_TO_ID = dict( sorted(self.CHECK_PANELHEX_TO_ID.items(), key=lambda item: item[1]) ) @@ -317,9 +546,9 @@ class WitnessPlayerLocations: } check_dict = { - location: StaticWitnessLocations.get_id(StaticWitnessLogic.CHECKS_BY_NAME[location]["checkHex"]) - for location in self.CHECK_LOCATIONS - if StaticWitnessLogic.CHECKS_BY_NAME[location]["panelType"] in self.PANEL_TYPES_TO_SHUFFLE + StaticWitnessLogic.CHECKS_BY_HEX[location]["checkName"]: + StaticWitnessLocations.get_id(StaticWitnessLogic.CHECKS_BY_HEX[location]["checkHex"]) + for location in self.CHECK_PANELHEX_TO_ID } self.CHECK_LOCATION_TABLE = {**self.EVENT_LOCATION_TABLE, **check_dict} diff --git a/worlds/witness/player_logic.py b/worlds/witness/player_logic.py index a58ad8ef7d..948029ec43 100644 --- a/worlds/witness/player_logic.py +++ b/worlds/witness/player_logic.py @@ -16,11 +16,15 @@ When the world has parsed its options, a second function is called to finalize t """ import copy +from typing import Set, Dict +from logging import warning + 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, \ get_symbol_shuffle_list, get_door_panel_shuffle_list, get_doors_complex_list, get_doors_max_list, \ - get_doors_simple_list, get_laser_shuffle + get_doors_simple_list, get_laser_shuffle, get_ep_all_individual, get_ep_obelisks, get_ep_easy, get_ep_no_eclipse, \ + get_ep_no_caves, get_ep_no_mountain, get_ep_no_videos from .Options import is_option_enabled, get_option_value, the_witness_options @@ -71,6 +75,8 @@ class WitnessPlayerLogic: these_panels = self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["panels"] + these_panels = frozenset({panels - self.PRECOMPLETED_LOCATIONS for panels in these_panels}) + if these_panels == frozenset({frozenset()}): return these_items @@ -84,7 +90,7 @@ class WitnessPlayerLogic: if option_panel in self.COMPLETELY_DISABLED_CHECKS: new_items = frozenset() - elif option_panel in {"7 Lasers", "11 Lasers", "PP2 Weirdness"}: + elif option_panel in {"7 Lasers", "11 Lasers", "PP2 Weirdness", "Theater to Tunnels"}: new_items = frozenset({frozenset([option_panel])}) # If a panel turns on when a panel in a different region turns on, # the latter panel will be an "event panel", unless it ends up being @@ -207,8 +213,13 @@ class WitnessPlayerLogic: return if adj_type == "Added Locations": + if "0x" in line: + line = StaticWitnessLogic.CHECKS_BY_HEX[line]["checkName"] self.ADDED_CHECKS.add(line) + if adj_type == "Precompleted Locations": + self.PRECOMPLETED_LOCATIONS.add(line) + def make_options_adjustments(self, world, player): """Makes logic adjustments based on options""" adjustment_linesets_in_order = [] @@ -234,6 +245,29 @@ class WitnessPlayerLogic: if is_option_enabled(world, player, "shuffle_symbols") or "shuffle_symbols" not in the_witness_options.keys(): adjustment_linesets_in_order.append(get_symbol_shuffle_list()) + if get_option_value(world, player, "EP_difficulty") == 0: + adjustment_linesets_in_order.append(get_ep_easy()) + elif get_option_value(world, player, "EP_difficulty") == 1: + adjustment_linesets_in_order.append(get_ep_no_eclipse()) + + if not is_option_enabled(world, player, "shuffle_vault_boxes"): + adjustment_linesets_in_order.append(get_ep_no_videos()) + + doors = get_option_value(world, player, "shuffle_doors") >= 2 + earlyutm = is_option_enabled(world, player, "early_secret_area") + victory = get_option_value(world, player, "victory_condition") + mount_lasers = get_option_value(world, player, "mountain_lasers") + chal_lasers = get_option_value(world, player, "challenge_lasers") + + excluse_postgame = not is_option_enabled(world, player, "shuffle_postgame") + + if excluse_postgame and not (earlyutm or doors): + adjustment_linesets_in_order.append(get_ep_no_caves()) + + mountain_enterable_from_top = victory == 0 or victory == 1 or (victory == 3 and chal_lasers > mount_lasers) + if excluse_postgame and not mountain_enterable_from_top: + adjustment_linesets_in_order.append(get_ep_no_mountain()) + if get_option_value(world, player, "shuffle_doors") == 1: adjustment_linesets_in_order.append(get_door_panel_shuffle_list()) @@ -249,9 +283,35 @@ class WitnessPlayerLogic: if is_option_enabled(world, player, "early_secret_area"): adjustment_linesets_in_order.append(get_early_utm_list()) + for item in self.YAML_ADDED_ITEMS: + adjustment_linesets_in_order.append(["Items:", item]) + if is_option_enabled(world, player, "shuffle_lasers"): adjustment_linesets_in_order.append(get_laser_shuffle()) + if get_option_value(world, player, "shuffle_EPs") == 0: # No EP Shuffle + adjustment_linesets_in_order.append(["Disabled Locations:"] + get_ep_all_individual()[1:]) + adjustment_linesets_in_order.append(["Disabled Locations:"] + get_ep_obelisks()[1:]) + + elif get_option_value(world, player, "shuffle_EPs") == 1: # Individual EPs + adjustment_linesets_in_order.append(["Disabled Locations:"] + get_ep_obelisks()[1:]) + + else: # Obelisk Sides + yaml_disabled_eps = [] + + for yaml_disabled_location in self.YAML_DISABLED_LOCATIONS: + if yaml_disabled_location not in StaticWitnessLogic.CHECKS_BY_NAME: + continue + + loc_obj = StaticWitnessLogic.CHECKS_BY_NAME[yaml_disabled_location] + + if loc_obj["panelType"] != "EP": + continue + + yaml_disabled_eps.append(loc_obj["checkHex"]) + + adjustment_linesets_in_order.append(["Precompleted Locations:"] + yaml_disabled_eps) + for adjustment_lineset in adjustment_linesets_in_order: current_adjustment_type = None @@ -290,7 +350,18 @@ class WitnessPlayerLogic: """ Makes a pair of an event panel and its event item """ - name = self.REFERENCE_LOGIC.CHECKS_BY_HEX[panel]["checkName"] + " Solved" + action = " Opened" if StaticWitnessLogic.CHECKS_BY_HEX[panel]["panelType"] == "Door" else " Solved" + + name = StaticWitnessLogic.CHECKS_BY_HEX[panel]["checkName"] + action + if panel not in self.EVENT_ITEM_NAMES: + if StaticWitnessLogic.CHECKS_BY_HEX[panel]["panelType"] == "EP": + obelisk = StaticWitnessLogic.CHECKS_BY_HEX[StaticWitnessLogic.EP_TO_OBELISK_SIDE[panel]]["checkName"] + + self.EVENT_ITEM_NAMES[panel] = obelisk + " - " + StaticWitnessLogic.CHECKS_BY_HEX[panel]["checkName"] + + else: + warning("Panel \"" + name + "\" does not have an associated event name.") + self.EVENT_ITEM_NAMES[panel] = name + " Event" pair = (name, self.EVENT_ITEM_NAMES[panel]) return pair @@ -323,7 +394,10 @@ class WitnessPlayerLogic: pair = self.make_event_item_pair(panel) self.EVENT_ITEM_PAIRS[pair[0]] = pair[1] - def __init__(self, world: MultiWorld, player: int): + def __init__(self, world: MultiWorld, player: int, disabled_locations: Set[str], start_inv: Dict[str, int]): + self.YAML_DISABLED_LOCATIONS = disabled_locations + self.YAML_ADDED_ITEMS = start_inv + self.EVENT_PANELS_FROM_PANELS = set() self.EVENT_PANELS_FROM_REGIONS = set() @@ -342,6 +416,8 @@ class WitnessPlayerLogic: self.REFERENCE_LOGIC = StaticWitnessLogic.sigma_normal elif self.DIFFICULTY == 1: self.REFERENCE_LOGIC = StaticWitnessLogic.sigma_expert + elif self.DIFFICULTY == 2: + self.REFERENCE_LOGIC = StaticWitnessLogic.vanilla self.CONNECTIONS_BY_REGION_NAME = copy.copy(self.REFERENCE_LOGIC.STATIC_CONNECTIONS_BY_REGION_NAME) self.DEPENDENT_REQUIREMENTS_BY_HEX = copy.copy(self.REFERENCE_LOGIC.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX) @@ -353,6 +429,7 @@ class WitnessPlayerLogic: self.EVENT_ITEM_PAIRS = dict() self.ALWAYS_EVENT_HEX_CODES = set() self.COMPLETELY_DISABLED_CHECKS = set() + self.PRECOMPLETED_LOCATIONS = set() self.ADDED_CHECKS = set() self.VICTORY_LOCATION = "0x0356B" self.EVENT_ITEM_NAMES = { @@ -366,7 +443,7 @@ class WitnessPlayerLogic: "0x019DC": "Keep Hedges 2 Knowledge", "0x019E7": "Keep Hedges 3 Knowledge", "0x01D3F": "Keep Laser Panel (Pressure Plates) Activates", - "0x01BE9": "Keep Laser Panel (Pressure Plates) Activates", + "0x01BE9": "Keep Laser Panel (Pressure Plates) Activates - Expert", "0x09F7F": "Mountain Access", "0x0367C": "Quarry Laser Mill Requirement Met", "0x009A1": "Swamp Between Bridges Far 1 Activates", @@ -385,6 +462,7 @@ class WitnessPlayerLogic: "0x17CDF": "All Boat Panels Turn On", "0x09DB8": "All Boat Panels Turn On", "0x17C95": "All Boat Panels Turn On", + "0x0A054": "Couch EP solvable", "0x03BB0": "Town Church Lattice Vision From Outside", "0x28AC1": "Town Wooden Rooftop Turns On", "0x28A69": "Town Tower 1st Door Opens", @@ -411,6 +489,24 @@ class WitnessPlayerLogic: "0x03613": "Treehouse Orange Bridge 13 Turns On", "0x17DEC": "Treehouse Laser House Access Requirement", "0x03C08": "Town Church Entry Opens", + "0x17D02": "Windmill Blades Spinning", + "0x0A0C9": "Cargo Box EP completable", + "0x09E39": "Pink Light Bridge Extended", + "0x01CD3": "Pressure Plates 3 EP available", + "0x17CC4": "Rails EP available", + "0x2896A": "Bridge Underside EP available", + "0x00064": "First Tunnel EP visible", + "0x033EA": "Pressure Plates 1 EP available", + "0x03553": "Tutorial Video EPs availble", + "0x17C79": "Bunker Door EP available", + "0x275FF": "Mill Light EPs available", + "0x17E2B": "Remaining Purple Sand EPs available", + "0x03852": "Ramp EPs requirement", + "0x334D8": "RGB panels & EPs solvable", + "0x03750": "Left Garden EP available", + "0x03C0C": "RGB Flowers EP requirement", + "0x01CD5": "Pressure Plates 3 EP requirement", + "0x3865F": "Ramp EPs access requirement", } self.ALWAYS_EVENT_NAMES_BY_HEX = { diff --git a/worlds/witness/regions.py b/worlds/witness/regions.py index e17acf7343..69b0317d85 100644 --- a/worlds/witness/regions.py +++ b/worlds/witness/regions.py @@ -6,7 +6,7 @@ and connects them with the proper requirements from BaseClasses import MultiWorld, Entrance from .static_logic import StaticWitnessLogic from .Options import get_option_value -from .locations import WitnessPlayerLocations +from .locations import WitnessPlayerLocations, StaticWitnessLocations from .player_logic import WitnessPlayerLogic @@ -27,7 +27,7 @@ class WitnessRegions: ) def connect(self, world: MultiWorld, player: int, source: str, target: str, player_logic: WitnessPlayerLogic, - panel_hex_to_solve_set=None): + panel_hex_to_solve_set=frozenset({frozenset()})): """ connect two regions and set the corresponding requirement """ @@ -63,8 +63,10 @@ class WitnessRegions: if difficulty == 1: reference_logic = StaticWitnessLogic.sigma_expert - else: + elif difficulty == 0: reference_logic = StaticWitnessLogic.sigma_normal + else: + reference_logic = StaticWitnessLogic.vanilla all_locations = set() @@ -74,8 +76,8 @@ class WitnessRegions: if reference_logic.CHECKS_BY_HEX[panel]["checkName"] in self.locat.CHECK_LOCATION_TABLE ] locations_for_this_region += [ - reference_logic.CHECKS_BY_HEX[panel]["checkName"] + " Solved" for panel in region["panels"] - if reference_logic.CHECKS_BY_HEX[panel]["checkName"] + " Solved" in self.locat.EVENT_LOCATION_TABLE + StaticWitnessLocations.get_event_name(panel) for panel in region["panels"] + if StaticWitnessLocations.get_event_name(panel) in self.locat.EVENT_LOCATION_TABLE ] all_locations = all_locations | set(locations_for_this_region) @@ -86,9 +88,6 @@ class WitnessRegions: for region_name, region in reference_logic.ALL_REGIONS_BY_NAME.items(): for connection in player_logic.CONNECTIONS_BY_REGION_NAME[region_name]: - if connection[0] == "Entry": - continue - if connection[1] == frozenset({frozenset(["TrueOneWay"])}): self.connect(world, player, region_name, connection[0], player_logic, frozenset({frozenset()})) continue diff --git a/worlds/witness/rules.py b/worlds/witness/rules.py index f7de20a258..ba9a1fb7a3 100644 --- a/worlds/witness/rules.py +++ b/worlds/witness/rules.py @@ -20,35 +20,24 @@ class WitnessLogic(LogicMixin): """ def _witness_has_lasers(self, world, player: int, amount: int) -> bool: + regular_lasers = not is_option_enabled(world, player, "shuffle_lasers") + 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 + place_names = [ + "Symmetry", "Desert", "Town", "Monastery", "Keep", + "Quarry", "Treehouse", "Jungle", "Bunker", "Swamp", "Shadows" + ] - lasers += int(self.has("Symmetry Laser Activation", player)) - lasers += int(self.has("Desert Laser Activation", player) - and self.has("Desert Laser Redirection", player)) - lasers += int(self.has("Town Laser Activation", player)) - lasers += int(self.has("Monastery Laser Activation", player)) - lasers += int(self.has("Keep Laser Activation", player)) - lasers += int(self.has("Quarry Laser Activation", player)) - lasers += int(self.has("Treehouse Laser Activation", player)) - lasers += int(self.has("Jungle Laser Activation", player)) - lasers += int(self.has("Bunker Laser Activation", player)) - lasers += int(self.has("Swamp Laser Activation", player)) - lasers += int(self.has("Shadows Laser Activation", player)) + for place in place_names: + has_laser = self.has(place + " Laser", player) + + has_laser = has_laser or (regular_lasers and self.has(place + " Laser Activation", player)) + + if place == "Desert": + has_laser = has_laser and self.has("Desert Laser Redirection", player) + + lasers += int(has_laser) return lasers >= amount @@ -84,11 +73,15 @@ class WitnessLogic(LogicMixin): for item in option: if item == "7 Lasers": - if not self._witness_has_lasers(world, player, get_option_value(world, player, "mountain_lasers")): + laser_req = get_option_value(world, player, "mountain_lasers") + + if not self._witness_has_lasers(world, player, laser_req): valid_option = False break elif item == "11 Lasers": - if not self._witness_has_lasers(world, player, get_option_value(world, player, "challenge_lasers")): + laser_req = get_option_value(world, player, "challenge_lasers") + + if not self._witness_has_lasers(world, player, laser_req): valid_option = False break elif item == "PP2 Weirdness": @@ -142,6 +135,24 @@ class WitnessLogic(LogicMixin): if not (front_access and backwards_access): valid_option = False break + elif item == "Theater to Tunnels": + direct_access = ( + self.can_reach("Tunnels to Windmill Interior", "Entrance", player) + and self.can_reach("Windmill Interior to Theater", "Entrance", player) + ) + + exit_to_town = self.can_reach("Theater to Town", "Entrance", player) + entrance_to_town = ( + self.can_reach("Town to Windmill Interior", "Entrance", player) + and self.can_reach("Windmill Interior to Theater", "Entrance", player) + ) + tunnels_to_town = self.can_reach("Tunnels to Town", "Entrance", player) + + if not (direct_access or (exit_to_town or entrance_to_town) and tunnels_to_town): + valid_option = False + break + + elif item in player_logic.EVENT_PANELS: if not self._witness_can_solve_panel(item, world, player, player_logic, locat): valid_option = False diff --git a/worlds/witness/settings/Disable_Unrandomized.txt b/worlds/witness/settings/Disable_Unrandomized.txt index 43c69409fc..8f6034ccb9 100644 --- a/worlds/witness/settings/Disable_Unrandomized.txt +++ b/worlds/witness/settings/Disable_Unrandomized.txt @@ -111,6 +111,18 @@ Disabled Locations: 0x17E67 (Bunker UV Room 2) 0x09DE0 (Bunker Laser) 0x0A079 (Bunker Elevator Control) -0x0042D (Mountaintop River Shape) 0x17CAA (River Garden Entry Panel) + +0x034A7 (Left Shutter EP) +0x034AD (Middle Shutter EP) +0x034AF (Right Shutter EP) +0x339B6 (Eclipse EP) - 0x03549 - True +0x33A29 (Window EP) - 0x03553 - True +0x33A2A (Door EP) - 0x03553 - True +0x33B06 (Church EP) - 0x0354E - True +0x3352F (Gate EP) +0x33600 (Patio Flowers EP) +0x035F5 (Tinted Door EP) +0x000D3 (Green Room Flowers EP) +0x33A20 (Theater Flowers EP) \ No newline at end of file diff --git a/worlds/witness/settings/EP_Shuffle/EP_All.txt b/worlds/witness/settings/EP_Shuffle/EP_All.txt new file mode 100644 index 0000000000..51af5e3850 --- /dev/null +++ b/worlds/witness/settings/EP_Shuffle/EP_All.txt @@ -0,0 +1,136 @@ +Added Locations: +0x0332B +0x03367 +0x28B8A +0x037B6 +0x037B2 +0x000F7 +0x3351D +0x0053C +0x00771 +0x335C8 +0x335C9 +0x337F8 +0x037BB +0x220E4 +0x220E5 +0x334B9 +0x334BC +0x22106 +0x0A14C +0x0A14D +0x03ABC +0x03ABE +0x03AC0 +0x03AC4 +0x03AC5 +0x03BE2 +0x03BE3 +0x0A409 +0x006E5 +0x006E6 +0x006E7 +0x034A7 +0x034AD +0x034AF +0x03DAB +0x03DAC +0x03DAD +0x03E01 +0x289F4 +0x289F5 +0x0053D +0x0053E +0x00769 +0x33721 +0x220A7 +0x220BD +0x03B22 +0x03B23 +0x03B24 +0x03B25 +0x03A79 +0x28ABD +0x28ABE +0x3388F +0x28B29 +0x28B2A +0x018B6 +0x033BE +0x033BF +0x033DD +0x033E5 +0x28AE9 +0x3348F +0x001A3 +0x335AE +0x000D3 +0x035F5 +0x09D5D +0x09D5E +0x09D63 +0x3370E +0x035DE +0x03601 +0x03603 +0x03D0D +0x3369A +0x336C8 +0x33505 +0x03A9E +0x016B2 +0x3365F +0x03731 +0x036CE +0x03C07 +0x03A93 +0x03AA6 +0x3397C +0x0105D +0x0A304 +0x035CB +0x035CF +0x28A7B +0x005F6 +0x00859 +0x17CB9 +0x28A4A +0x334B6 +0x0069D +0x00614 +0x28A4C +0x289CF +0x289D1 +0x33692 +0x03E77 +0x03E7C +0x035C7 +0x01848 +0x03D06 +0x33530 +0x33600 +0x28A2F +0x28A37 +0x334A3 +0x3352F +0x33857 +0x33879 +0x03C19 +0x28B30 +0x035C9 +0x03335 +0x03412 +0x038A6 +0x038AA +0x03E3F +0x03E40 +0x28B8E +0x28B91 +0x03BCE +0x03BCF +0x03BD1 +0x339B6 +0x33A20 +0x33A29 +0x33A2A +0x33B06 \ No newline at end of file diff --git a/worlds/witness/settings/EP_Shuffle/EP_Easy.txt b/worlds/witness/settings/EP_Shuffle/EP_Easy.txt new file mode 100644 index 0000000000..10127438ac --- /dev/null +++ b/worlds/witness/settings/EP_Shuffle/EP_Easy.txt @@ -0,0 +1,11 @@ +Precompleted Locations: +0x335AE +0x3388F +0x33A20 +0x037B2 +0x000F7 +0x28B29 +0x33857 +0x33879 +0x016B2 +0x036CE \ No newline at end of file diff --git a/worlds/witness/settings/EP_Shuffle/EP_NoCavesEPs.txt b/worlds/witness/settings/EP_Shuffle/EP_NoCavesEPs.txt new file mode 100644 index 0000000000..3bb318a69e --- /dev/null +++ b/worlds/witness/settings/EP_Shuffle/EP_NoCavesEPs.txt @@ -0,0 +1,5 @@ +Precompleted Locations: +0x3397C +0x33A20 +0x3352F +0x28B30 \ No newline at end of file diff --git a/worlds/witness/settings/EP_Shuffle/EP_NoEclipse.txt b/worlds/witness/settings/EP_Shuffle/EP_NoEclipse.txt new file mode 100644 index 0000000000..d41badfa18 --- /dev/null +++ b/worlds/witness/settings/EP_Shuffle/EP_NoEclipse.txt @@ -0,0 +1,2 @@ +Precompleted Locations: +0x339B6 \ No newline at end of file diff --git a/worlds/witness/settings/EP_Shuffle/EP_NoMountainEPs.txt b/worlds/witness/settings/EP_Shuffle/EP_NoMountainEPs.txt new file mode 100644 index 0000000000..3558d77ad8 --- /dev/null +++ b/worlds/witness/settings/EP_Shuffle/EP_NoMountainEPs.txt @@ -0,0 +1,4 @@ +Precompleted Locations: +0x09D63 +0x09D5D +0x09D5E \ No newline at end of file diff --git a/worlds/witness/settings/EP_Shuffle/EP_Sides.txt b/worlds/witness/settings/EP_Shuffle/EP_Sides.txt new file mode 100644 index 0000000000..1da52ffb89 --- /dev/null +++ b/worlds/witness/settings/EP_Shuffle/EP_Sides.txt @@ -0,0 +1,34 @@ +Added Locations: +0xFFE00 +0xFFE01 +0xFFE02 +0xFFE03 +0xFFE04 +0xFFE10 +0xFFE11 +0xFFE12 +0xFFE13 +0xFFE14 +0xFFE15 +0xFFE20 +0xFFE21 +0xFFE22 +0xFFE23 +0xFFE24 +0xFFE25 +0xFFE30 +0xFFE31 +0xFFE32 +0xFFE33 +0xFFE34 +0xFFE35 +0xFFE40 +0xFFE41 +0xFFE42 +0xFFE43 +0xFFE50 +0xFFE51 +0xFFE52 +0xFFE53 +0xFFE54 +0xFFE55 \ No newline at end of file diff --git a/worlds/witness/settings/EP_Shuffle/EP_Videos.txt b/worlds/witness/settings/EP_Shuffle/EP_Videos.txt new file mode 100644 index 0000000000..c4aaca13a6 --- /dev/null +++ b/worlds/witness/settings/EP_Shuffle/EP_Videos.txt @@ -0,0 +1,6 @@ +Precompleted Locations: +0x339B6 +0x33A29 +0x33A2A +0x33B06 +0x33A20 \ No newline at end of file diff --git a/worlds/witness/static_logic.py b/worlds/witness/static_logic.py index 18a0e19cff..4311a84fa1 100644 --- a/worlds/witness/static_logic.py +++ b/worlds/witness/static_logic.py @@ -40,9 +40,11 @@ class StaticWitnessLogicObj: required_panel_lambda = line_split.pop(0) + full_check_name = current_region["shortName"] + " " + check_name + if location_id == "Door" or location_id == "Laser": self.CHECKS_BY_HEX[check_hex] = { - "checkName": current_region["shortName"] + " " + check_name, + "checkName": full_check_name, "checkHex": check_hex, "region": current_region, "id": None, @@ -74,20 +76,38 @@ class StaticWitnessLogicObj: location_type = "Vault" elif check_name in laser_names: location_type = "Laser" + elif "Obelisk Side" in check_name: + location_type = "Obelisk Side" + full_check_name = check_name + elif "EP" in check_name: + location_type = "EP" + + self.EP_ID_TO_NAME[check_hex] = full_check_name else: location_type = "General" required_items = parse_lambda(required_item_lambda) + required_panels = parse_lambda(required_panel_lambda) required_items = frozenset(required_items) requirement = { - "panels": parse_lambda(required_panel_lambda), + "panels": required_panels, "items": required_items } + if location_type == "Obelisk Side": + eps = set(list(required_panels)[0]) + eps -= {"Theater to Tunnels"} + + eps_ints = {int(h, 16) for h in eps} + + self.OBELISK_SIDE_ID_TO_EP_HEXES[int(check_hex, 16)] = eps_ints + for ep_hex in eps: + self.EP_TO_OBELISK_SIDE[ep_hex] = check_hex + self.CHECKS_BY_HEX[check_hex] = { - "checkName": current_region["shortName"] + " " + check_name, + "checkName": full_check_name, "checkHex": check_hex, "region": current_region, "id": int(location_id), @@ -108,6 +128,12 @@ class StaticWitnessLogicObj: self.CHECKS_BY_NAME = dict() self.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX = dict() + self.OBELISK_SIDE_ID_TO_EP_HEXES = dict() + + self.EP_TO_OBELISK_SIDE = dict() + + self.EP_ID_TO_NAME = dict() + self.read_logic_file(file_path) @@ -125,10 +151,16 @@ class StaticWitnessLogic: ALL_REGIONS_BY_NAME = dict() STATIC_CONNECTIONS_BY_REGION_NAME = dict() + OBELISK_SIDE_ID_TO_EP_HEXES = dict() + CHECKS_BY_HEX = dict() CHECKS_BY_NAME = dict() STATIC_DEPENDENT_REQUIREMENTS_BY_HEX = dict() + EP_TO_OBELISK_SIDE = dict() + + EP_ID_TO_NAME = dict() + def parse_items(self): """ Parses currently defined items from WitnessItems.txt @@ -185,6 +217,10 @@ class StaticWitnessLogic: def sigma_normal(self) -> StaticWitnessLogicObj: return StaticWitnessLogicObj("WitnessLogic.txt") + @lazy + def vanilla(self) -> StaticWitnessLogicObj: + return StaticWitnessLogicObj("WitnessLogicVanilla.txt") + def __init__(self): self.parse_items() @@ -195,3 +231,8 @@ class StaticWitnessLogic: self.CHECKS_BY_NAME.update(self.sigma_normal.CHECKS_BY_NAME) self.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX.update(self.sigma_normal.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX) + self.OBELISK_SIDE_ID_TO_EP_HEXES.update(self.sigma_normal.OBELISK_SIDE_ID_TO_EP_HEXES) + + self.EP_TO_OBELISK_SIDE.update(self.sigma_normal.EP_TO_OBELISK_SIDE) + + self.EP_ID_TO_NAME.update(self.sigma_normal.EP_ID_TO_NAME) \ No newline at end of file diff --git a/worlds/witness/utils.py b/worlds/witness/utils.py index a69dabbb7c..dcb335edd9 100644 --- a/worlds/witness/utils.py +++ b/worlds/witness/utils.py @@ -155,3 +155,38 @@ def get_laser_shuffle(): @cache_argsless def get_audio_logs(): return get_adjustment_file("settings/Audio_Logs.txt") + + +@cache_argsless +def get_ep_all_individual(): + return get_adjustment_file("settings/EP_Shuffle/EP_All.txt") + + +@cache_argsless +def get_ep_obelisks(): + return get_adjustment_file("settings/EP_Shuffle/EP_Sides.txt") + + +@cache_argsless +def get_ep_easy(): + return get_adjustment_file("settings/EP_Shuffle/EP_Easy.txt") + + +@cache_argsless +def get_ep_no_eclipse(): + return get_adjustment_file("settings/EP_Shuffle/EP_NoEclipse.txt") + + +@cache_argsless +def get_ep_no_caves(): + return get_adjustment_file("settings/EP_Shuffle/EP_NoCavesEPs.txt") + + +@cache_argsless +def get_ep_no_mountain(): + return get_adjustment_file("settings/EP_Shuffle/EP_NoMountainEPs.txt") + + +@cache_argsless +def get_ep_no_videos(): + return get_adjustment_file("settings/EP_Shuffle/EP_Videos.txt") \ No newline at end of file From 995c9786284ddc6e5c4eec743bac82fa930dd3c9 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Thu, 2 Feb 2023 01:14:23 +0100 Subject: [PATCH 10/47] Core: replace global random state with descriptive error (#1424) * Core: replace global random state with descriptive error * Core: make random a proxy object and rename slot_seeds --- BaseClasses.py | 28 +++++++++++++++++++++++----- LttPAdjuster.py | 2 +- Main.py | 4 ++++ OoTAdjuster.py | 2 +- worlds/alttp/Rom.py | 16 ++++++++-------- worlds/checksfinder/__init__.py | 2 +- worlds/dkc3/Rom.py | 2 +- worlds/factorio/Mod.py | 2 +- worlds/factorio/__init__.py | 2 +- worlds/hk/__init__.py | 2 +- worlds/minecraft/__init__.py | 2 +- worlds/oot/Cosmetics.py | 2 +- worlds/oot/Patches.py | 2 +- worlds/oot/__init__.py | 4 ++-- worlds/pokemon_rb/rom.py | 2 +- worlds/ror2/__init__.py | 2 +- worlds/smw/Rom.py | 2 +- worlds/spire/__init__.py | 2 +- 18 files changed, 51 insertions(+), 29 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index bf89a0e6ec..219ff5eede 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -29,6 +29,20 @@ class Group(TypedDict, total=False): link_replacement: bool +class ThreadBarrierProxy(): + """Passes through getattr while passthrough is True""" + def __init__(self, obj: Any): + self.passthrough = True + self.obj = obj + + def __getattr__(self, item): + if self.passthrough: + return getattr(self.obj, item) + else: + raise RuntimeError("You are in a threaded context and global random state was removed for your safety. " + "Please use multiworld.per_slot_randoms[player] or randomize ahead of output.") + + class MultiWorld(): debug_types = False player_name: Dict[int, str] @@ -61,6 +75,9 @@ class MultiWorld(): game: Dict[int, str] + random: random.Random + per_slot_randoms: Dict[int, random.Random] + class AttributeProxy(): def __init__(self, rule): self.rule = rule @@ -69,7 +86,8 @@ class MultiWorld(): return self.rule(player) def __init__(self, players: int): - self.random = random.Random() # world-local random state is saved for multiple generations running concurrently + # world-local random state is saved for multiple generations running concurrently + self.random = ThreadBarrierProxy(random.Random()) self.players = players self.player_types = {player: NetUtils.SlotType.player for player in self.player_ids} self.glitch_triforce = False @@ -160,7 +178,7 @@ class MultiWorld(): set_player_attr('completion_condition', lambda state: True) self.custom_data = {} self.worlds = {} - self.slot_seeds = {} + self.per_slot_randoms = {} self.plando_options = PlandoOptions.none def get_all_ids(self) -> Tuple[int, ...]: @@ -206,8 +224,8 @@ class MultiWorld(): else: self.random.seed(self.seed) self.seed_name = name if name else str(self.seed) - self.slot_seeds = {player: random.Random(self.random.getrandbits(64)) for player in - range(1, self.players + 1)} + self.per_slot_randoms = {player: random.Random(self.random.getrandbits(64)) for player in + range(1, self.players + 1)} def set_options(self, args: Namespace) -> None: for option_key in Options.common_options: @@ -291,7 +309,7 @@ class MultiWorld(): self.state = CollectionState(self) def secure(self): - self.random = secrets.SystemRandom() + self.random = ThreadBarrierProxy(secrets.SystemRandom()) self.is_race = True @functools.cached_property diff --git a/LttPAdjuster.py b/LttPAdjuster.py index a2cc2eeba5..205a76813a 100644 --- a/LttPAdjuster.py +++ b/LttPAdjuster.py @@ -35,7 +35,7 @@ class AdjusterWorld(object): def __init__(self, sprite_pool): import random self.sprite_pool = {1: sprite_pool} - self.slot_seeds = {1: random} + self.per_slot_randoms = {1: random} class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): diff --git a/Main.py b/Main.py index 983874f593..879ca720f7 100644 --- a/Main.py +++ b/Main.py @@ -251,6 +251,10 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No balance_multiworld_progression(world) logger.info(f'Beginning output...') + + # we're about to output using multithreading, so we're removing the global random state to prevent accidental use + world.random.passthrough = False + outfilebase = 'AP_' + world.seed_name output = tempfile.TemporaryDirectory() diff --git a/OoTAdjuster.py b/OoTAdjuster.py index c2df9b074e..f449113d22 100644 --- a/OoTAdjuster.py +++ b/OoTAdjuster.py @@ -197,7 +197,7 @@ def set_icon(window): def adjust(args): # Create a fake world and OOTWorld to use as a base world = MultiWorld(1) - world.slot_seeds = {1: random} + world.per_slot_randoms = {1: random} ootworld = OOTWorld(world, 1) # Set options in the fake OOTWorld for name, option in chain(cosmetic_options.items(), sfx_options.items()): diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index a374658e8c..a55946ba1c 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -92,7 +92,7 @@ class LocalRom(object): # cause crash to provide traceback import xxtea - local_random = world.slot_seeds[player] + local_random = world.per_slot_randoms[player] key = bytes(local_random.getrandbits(8 * 16).to_bytes(16, 'big')) self.write_bytes(0x1800B0, bytearray(key)) self.write_int16(0x180087, 1) @@ -384,7 +384,7 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, output_direct max_enemizer_tries = 5 for i in range(max_enemizer_tries): - enemizer_seed = str(world.slot_seeds[player].randint(0, 999999999)) + enemizer_seed = str(world.per_slot_randoms[player].randint(0, 999999999)) enemizer_command = [os.path.abspath(enemizercli), '--rom', randopatch_path, '--seed', enemizer_seed, @@ -414,7 +414,7 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, output_direct continue for j in range(i + 1, max_enemizer_tries): - world.slot_seeds[player].randint(0, 999999999) + world.per_slot_randoms[player].randint(0, 999999999) # Sacrifice all remaining random numbers that would have been used for unused enemizer tries. # This allows for future enemizer bug fixes to NOT affect the rest of the seed's randomness break @@ -766,7 +766,7 @@ def get_nonnative_item_sprite(item: str) -> int: def patch_rom(world, rom, player, enemized): - local_random = world.slot_seeds[player] + local_random = world.per_slot_randoms[player] # patch items @@ -1646,7 +1646,7 @@ def patch_rom(world, rom, player, enemized): rom.write_byte(0xFEE41, 0x2A) # preopen bombable exit if world.tile_shuffle[player]: - tile_set = TileSet.get_random_tile_set(world.slot_seeds[player]) + tile_set = TileSet.get_random_tile_set(world.per_slot_randoms[player]) rom.write_byte(0x4BA21, tile_set.get_speed()) rom.write_byte(0x4BA1D, tile_set.get_len()) rom.write_bytes(0x4BA2A, tile_set.get_bytes()) @@ -1779,7 +1779,7 @@ def hud_format_text(text): def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, sprite: str, palettes_options, world=None, player=1, allow_random_on_event=False, reduceflashing=False, triforcehud: str = None, deathlink: bool = False, allowcollect: bool = False): - local_random = random if not world else world.slot_seeds[player] + local_random = random if not world else world.per_slot_randoms[player] disable_music: bool = not music # enable instant item menu if menuspeed == 'instant': @@ -2105,7 +2105,7 @@ def write_string_to_rom(rom, target, string): def write_strings(rom, world, player): from . import ALTTPWorld - local_random = world.slot_seeds[player] + local_random = world.per_slot_randoms[player] w: ALTTPWorld = world.worlds[player] tt = TextTable() @@ -2330,7 +2330,7 @@ def write_strings(rom, world, player): if world.worlds[player].has_progressive_bows and (world.difficulty_requirements[player].progressive_bow_limit >= 2 or ( world.swordless[player] or world.logic[player] == 'noglitches')): prog_bow_locs = world.find_item_locations('Progressive Bow', player, True) - world.slot_seeds[player].shuffle(prog_bow_locs) + world.per_slot_randoms[player].shuffle(prog_bow_locs) found_bow = False found_bow_alt = False while prog_bow_locs and not (found_bow and found_bow_alt): diff --git a/worlds/checksfinder/__init__.py b/worlds/checksfinder/__init__.py index 5dce1b8e11..9be2350fcf 100644 --- a/worlds/checksfinder/__init__.py +++ b/worlds/checksfinder/__init__.py @@ -38,7 +38,7 @@ class ChecksFinderWorld(World): def _get_checksfinder_data(self): return { - 'world_seed': self.multiworld.slot_seeds[self.player].getrandbits(32), + 'world_seed': self.multiworld.per_slot_randoms[self.player].getrandbits(32), 'seed_name': self.multiworld.seed_name, 'player_name': self.multiworld.get_player_name(self.player), 'player_id': self.player, diff --git a/worlds/dkc3/Rom.py b/worlds/dkc3/Rom.py index 6308e95860..37b8ecf04c 100644 --- a/worlds/dkc3/Rom.py +++ b/worlds/dkc3/Rom.py @@ -476,7 +476,7 @@ class LocalRom(object): def patch_rom(world, rom, player, active_level_list): - local_random = world.slot_seeds[player] + local_random = world.per_slot_randoms[player] # Boomer Costs bonus_coin_cost = world.krematoa_bonus_coin_cost[player] diff --git a/worlds/factorio/Mod.py b/worlds/factorio/Mod.py index 73f4c4b77c..cda1ca1f66 100644 --- a/worlds/factorio/Mod.py +++ b/worlds/factorio/Mod.py @@ -99,7 +99,7 @@ def generate_mod(world: "Factorio", output_directory: str): for location in world.locations] mod_name = f"AP-{multiworld.seed_name}-P{player}-{multiworld.get_file_safe_player_name(player)}" - random = multiworld.slot_seeds[player] + random = multiworld.per_slot_randoms[player] def flop_random(low, high, base=None): """Guarantees 50% below base and 50% above base, uniform distribution in each direction.""" diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py index 4b6d6b9e6e..6b4e5c8603 100644 --- a/worlds/factorio/__init__.py +++ b/worlds/factorio/__init__.py @@ -222,7 +222,7 @@ class Factorio(World): map_basic_settings = self.multiworld.world_gen[player].value["basic"] if map_basic_settings.get("seed", None) is None: # allow seed 0 - map_basic_settings["seed"] = self.multiworld.slot_seeds[player].randint(0, 2 ** 32 - 1) # 32 bit uint + map_basic_settings["seed"] = self.multiworld.per_slot_randoms[player].randint(0, 2 ** 32 - 1) # 32 bit uint start_location_hints: typing.Set[str] = self.multiworld.start_location_hints[self.player].value diff --git a/worlds/hk/__init__.py b/worlds/hk/__init__.py index a635bb5c60..5993d1b342 100644 --- a/worlds/hk/__init__.py +++ b/worlds/hk/__init__.py @@ -446,7 +446,7 @@ class HKWorld(World): options[option_name] = optionvalue # 32 bit int - slot_data["seed"] = self.multiworld.slot_seeds[self.player].randint(-2147483647, 2147483646) + slot_data["seed"] = self.multiworld.per_slot_randoms[self.player].randint(-2147483647, 2147483646) # Backwards compatibility for shop cost data (HKAP < 0.1.0) if not self.multiworld.CostSanity[self.player]: diff --git a/worlds/minecraft/__init__.py b/worlds/minecraft/__init__.py index f94a1159ed..c0a034f213 100644 --- a/worlds/minecraft/__init__.py +++ b/worlds/minecraft/__init__.py @@ -70,7 +70,7 @@ class MinecraftWorld(World): def _get_mc_data(self): exits = [connection[0] for connection in default_connections] return { - 'world_seed': self.multiworld.slot_seeds[self.player].getrandbits(32), + 'world_seed': self.multiworld.per_slot_randoms[self.player].getrandbits(32), 'seed_name': self.multiworld.seed_name, 'player_name': self.multiworld.get_player_name(self.player), 'player_id': self.player, diff --git a/worlds/oot/Cosmetics.py b/worlds/oot/Cosmetics.py index e132c2ceb1..7b8008fbe6 100644 --- a/worlds/oot/Cosmetics.py +++ b/worlds/oot/Cosmetics.py @@ -769,7 +769,7 @@ patch_sets[0x1F073FD9] = { def patch_cosmetics(ootworld, rom): # Use the world's slot seed for cosmetics - random.seed(ootworld.multiworld.slot_seeds[ootworld.player]) + random.seed(ootworld.multiworld.per_slot_randoms[ootworld.player]) # try to detect the cosmetic patch data format versioned_patch_set = None diff --git a/worlds/oot/Patches.py b/worlds/oot/Patches.py index 1569d12fbe..c02512a788 100644 --- a/worlds/oot/Patches.py +++ b/worlds/oot/Patches.py @@ -2346,7 +2346,7 @@ def patch_rom(world, rom): # Write numeric seed truncated to 32 bits for rng seeding # Overwritten with new seed every time a new rng value is generated - rng_seed = world.multiworld.slot_seeds[world.player].getrandbits(32) + rng_seed = world.multiworld.per_slot_randoms[world.player].getrandbits(32) rom.write_int32(rom.sym('RNG_SEED_INT'), rng_seed) # Static initial seed value for one-time random actions like the Hylian Shield discount rom.write_int32(rom.sym('RANDOMIZER_RNG_SEED'), rng_seed) diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py index 3dd5baf763..7207cada84 100644 --- a/worlds/oot/__init__.py +++ b/worlds/oot/__init__.py @@ -962,10 +962,10 @@ class OOTWorld(World): trap_location_ids = [loc.address for loc in self.get_locations() if loc.item.trap] self.trap_appearances = {} for loc_id in trap_location_ids: - self.trap_appearances[loc_id] = self.create_item(self.multiworld.slot_seeds[self.player].choice(self.fake_items).name) + self.trap_appearances[loc_id] = self.create_item(self.multiworld.per_slot_randoms[self.player].choice(self.fake_items).name) # Seed hint RNG, used for ganon text lines also - self.hint_rng = self.multiworld.slot_seeds[self.player] + self.hint_rng = self.multiworld.per_slot_randoms[self.player] outfile_name = self.multiworld.get_out_file_name_base(self.player) rom = Rom(file=get_options()['oot_options']['rom_file']) diff --git a/worlds/pokemon_rb/rom.py b/worlds/pokemon_rb/rom.py index 6f3b89087c..9dbc3a8b83 100644 --- a/worlds/pokemon_rb/rom.py +++ b/worlds/pokemon_rb/rom.py @@ -359,7 +359,7 @@ def process_pokemon_data(self): def generate_output(self, output_directory: str): - random = self.multiworld.slot_seeds[self.player] + random = self.multiworld.per_slot_randoms[self.player] game_version = self.multiworld.game_version[self.player].current_key data = bytes(get_base_rom_bytes(game_version)) diff --git a/worlds/ror2/__init__.py b/worlds/ror2/__init__.py index c1b775cfbc..127e614fb2 100644 --- a/worlds/ror2/__init__.py +++ b/worlds/ror2/__init__.py @@ -110,7 +110,7 @@ class RiskOfRainWorld(World): def fill_slot_data(self): return { "itemPickupStep": self.multiworld.item_pickup_step[self.player].value, - "seed": "".join(self.multiworld.slot_seeds[self.player].choice(string.digits) for _ in range(16)), + "seed": "".join(self.multiworld.per_slot_randoms[self.player].choice(string.digits) for _ in range(16)), "totalLocations": self.multiworld.total_locations[self.player].value, "totalRevivals": self.multiworld.total_revivals[self.player].value, "startWithDio": self.multiworld.start_with_revive[self.player].value, diff --git a/worlds/smw/Rom.py b/worlds/smw/Rom.py index d39fcc46b9..2d03e19d0b 100644 --- a/worlds/smw/Rom.py +++ b/worlds/smw/Rom.py @@ -821,7 +821,7 @@ def handle_boss_shuffle(rom, world, player): def patch_rom(world, rom, player, active_level_dict): - local_random = world.slot_seeds[player] + local_random = world.per_slot_randoms[player] goal_text = generate_goal_text(world, player) diff --git a/worlds/spire/__init__.py b/worlds/spire/__init__.py index 237e677500..a1d0222248 100644 --- a/worlds/spire/__init__.py +++ b/worlds/spire/__init__.py @@ -38,7 +38,7 @@ class SpireWorld(World): def _get_slot_data(self): return { - 'seed': "".join(self.multiworld.slot_seeds[self.player].choice(string.ascii_letters) for i in range(16)), + 'seed': "".join(self.multiworld.per_slot_randoms[self.player].choice(string.ascii_letters) for i in range(16)), 'character': self.multiworld.character[self.player], 'ascension': self.multiworld.ascension[self.player], 'heart_run': self.multiworld.heart_run[self.player] From 0817305d5bd5151bd264c9586370a5506967502a Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Fri, 3 Feb 2023 19:38:54 +0100 Subject: [PATCH 11/47] Witness: Added an option tooltip for "Environmental Puzzles Difficulty" option (+ another bugfix) (#1431) * Added an option tooltip * Fixed eclipse being on in EP difficulty normal --- worlds/witness/Options.py | 7 +++++++ worlds/witness/settings/EP_Shuffle/EP_Easy.txt | 1 + 2 files changed, 8 insertions(+) diff --git a/worlds/witness/Options.py b/worlds/witness/Options.py index 1b539f6f0d..e4e7e33faa 100644 --- a/worlds/witness/Options.py +++ b/worlds/witness/Options.py @@ -63,6 +63,7 @@ class ShuffleEnvironmentalPuzzles(Choice): Add Environmental/Obelisk Puzzles into the location pool. In "individual", every Environmental Puzzle sends an item. In "obelisk_sides", completing every puzzle on one side of an Obelisk sends an item. + Note: In Obelisk Sides, any EPs excluded through another setting will be counted as pre-completed on their Obelisk. """ display_name = "Shuffle Environmental Puzzles" option_off = 0 @@ -77,6 +78,12 @@ class ShuffleDog(Toggle): class EnvironmentalPuzzlesDifficulty(Choice): + """ + When "Shuffle Environmental Puzzles" is on, this setting governs which EPs are eligible for the location pool. + On "eclipse", every EP in the game is eligible, including the 1-hour-long "Theater Eclipse EP". + On "tedious", Theater Eclipse EP is excluded from the location pool. + On "normal", several other difficult or long EPs are excluded as well. + """ display_name = "Environmental Puzzles Difficulty" option_normal = 0 option_tedious = 1 diff --git a/worlds/witness/settings/EP_Shuffle/EP_Easy.txt b/worlds/witness/settings/EP_Shuffle/EP_Easy.txt index 10127438ac..3e168b5891 100644 --- a/worlds/witness/settings/EP_Shuffle/EP_Easy.txt +++ b/worlds/witness/settings/EP_Shuffle/EP_Easy.txt @@ -1,4 +1,5 @@ Precompleted Locations: +0x339B6 0x335AE 0x3388F 0x33A20 From 555a0da46db95e9c15bc4fa023e1132624740c9d Mon Sep 17 00:00:00 2001 From: CaitSith2 Date: Fri, 3 Feb 2023 10:39:18 -0800 Subject: [PATCH 12/47] Core: Rename the missed slot_seeds. (#1432) * Rename the missed slot_seeds. * Fixed a threaded context random error. --- worlds/AutoWorld.py | 2 +- worlds/witness/__init__.py | 4 ++-- worlds/witness/hints.py | 16 ++++++++-------- worlds/zillion/__init__.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index 8d1619b8b5..d7547a3a51 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -229,7 +229,7 @@ class World(metaclass=AutoWorldRegister): def generate_output(self, output_directory: str) -> None: """This method gets called from a threadpool, do not use world.random here. - If you need any last-second randomization, use MultiWorld.slot_seeds[slot] instead.""" + If you need any last-second randomization, use MultiWorld.per_slot_randoms[slot] instead.""" pass def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index da0854446e..5a288e0c8a 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -56,7 +56,7 @@ class WitnessWorld(World): def _get_slot_data(self): return { - 'seed': self.multiworld.random.randint(0, 1000000), + 'seed': self.multiworld.per_slot_randoms[self.player].randint(0, 1000000), 'victory_location': int(self.player_logic.VICTORY_LOCATION, 16), 'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID, 'item_id_to_door_hexes': self.static_items.ITEM_ID_TO_DOOR_HEX_ALL, @@ -189,7 +189,7 @@ class WitnessWorld(World): if hint_amount != 0: generated_hints = make_hints(self.multiworld, self.player, hint_amount) - self.multiworld.slot_seeds[self.player].shuffle(audio_logs) + self.multiworld.per_slot_randoms[self.player].shuffle(audio_logs) duplicates = len(audio_logs) // hint_amount diff --git a/worlds/witness/hints.py b/worlds/witness/hints.py index 898bd4dc55..baa6dd45dd 100644 --- a/worlds/witness/hints.py +++ b/worlds/witness/hints.py @@ -157,10 +157,10 @@ def get_priority_hint_items(multiworld: MultiWorld, player: int): if get_option_value(multiworld, player, "doors") >= 2: priority.add("Desert Laser") lasers.remove("Desert Laser") - priority.update(multiworld.slot_seeds[player].sample(lasers, 2)) + priority.update(multiworld.per_slot_randoms[player].sample(lasers, 2)) else: - priority.update(multiworld.slot_seeds[player].sample(lasers, 3)) + priority.update(multiworld.per_slot_randoms[player].sample(lasers, 3)) return priority @@ -262,19 +262,19 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int): else: hints.append((loc, "contains", item[0], item[2])) - multiworld.slot_seeds[player].shuffle(hints) # shuffle always hint order in case of low hint amount + multiworld.per_slot_randoms[player].shuffle(hints) # shuffle always hint order in case of low hint amount - next_random_hint_is_item = multiworld.slot_seeds[player].randint(0, 2) + next_random_hint_is_item = multiworld.per_slot_randoms[player].randint(0, 2) prog_items_in_this_world = sorted(list(prog_items_in_this_world)) locations_in_this_world = sorted(list(loc_in_this_world)) - multiworld.slot_seeds[player].shuffle(prog_items_in_this_world) - multiworld.slot_seeds[player].shuffle(locations_in_this_world) + multiworld.per_slot_randoms[player].shuffle(prog_items_in_this_world) + multiworld.per_slot_randoms[player].shuffle(locations_in_this_world) while len(hints) < hint_amount: if priority_hint_pairs: - loc = multiworld.slot_seeds[player].choice(list(priority_hint_pairs.keys())) + loc = multiworld.per_slot_randoms[player].choice(list(priority_hint_pairs.keys())) item = priority_hint_pairs[loc] del priority_hint_pairs[loc] @@ -301,4 +301,4 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int): def generate_joke_hints(multiworld: MultiWorld, player: int, amount: int): - return [(x, y, z, -1) for (x, y, z) in multiworld.slot_seeds[player].sample(joke_hints, amount)] + return [(x, y, z, -1) for (x, y, z) in multiworld.per_slot_randoms[player].sample(joke_hints, amount)] diff --git a/worlds/zillion/__init__.py b/worlds/zillion/__init__.py index bc82c48002..63823e8cc4 100644 --- a/worlds/zillion/__init__.py +++ b/worlds/zillion/__init__.py @@ -343,7 +343,7 @@ class ZillionWorld(World): def generate_output(self, output_directory: str) -> None: """This method gets called from a threadpool, do not use world.random here. - If you need any last-second randomization, use MultiWorld.slot_seeds[slot] instead.""" + If you need any last-second randomization, use MultiWorld.per_slot_randoms[slot] instead.""" self.finalize_item_locations() assert self.zz_system.patcher, "didn't get patcher from generate_early" From fb1a9e9c5a33c743cbf553dfb21a5d1933164f38 Mon Sep 17 00:00:00 2001 From: vgZerst <36623732+vgZerst@users.noreply.github.com> Date: Fri, 3 Feb 2023 23:04:00 -0600 Subject: [PATCH 13/47] WebHost: add checks percent done column to tracker (#1376) * WebHost: add checks percent done column to tracker * WebHost: add checks percent done column to tracker --- WebHostLib/templates/tracker.html | 2 ++ WebHostLib/tracker.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/WebHostLib/templates/tracker.html b/WebHostLib/templates/tracker.html index 0f6fe12ffb..96148e3454 100644 --- a/WebHostLib/templates/tracker.html +++ b/WebHostLib/templates/tracker.html @@ -98,6 +98,7 @@ {{ area }} {%- endif -%} {%- endfor -%} + % Last
Activity @@ -140,6 +141,7 @@ {% if inventory[team][player][big_key_ids[area]] %}✔️{% endif %} {%- endif -%} {%- endfor -%} + {{ percent_total_checks_done[team][player] }} {%- if activity_timers[(team, player)] -%} {{ activity_timers[(team, player)].total_seconds() }} {%- else -%} diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index 68a40a323a..c0b83e4daf 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -1232,6 +1232,11 @@ def getTracker(tracker: UUID): for playernumber in range(1, len(team) + 1) if playernumber not in groups} for teamnumber, team in enumerate(names)} + percent_total_checks_done = {teamnumber: {playernumber: 0 + for playernumber in range(1, len(team) + 1) if playernumber not in groups} + for teamnumber, team in enumerate(names)} + + hints = {team: set() for team in range(len(names))} if room.multisave: multisave = restricted_loads(room.multisave) @@ -1259,6 +1264,7 @@ def getTracker(tracker: UUID): attribute_item(inventory, team, recipient, item) checks_done[team][player][player_location_to_area[player][location]] += 1 checks_done[team][player]["Total"] += 1 + percent_total_checks_done[team][player] = int(checks_done[team][player]["Total"] / seed_checks_in_area[player]["Total"] * 100) if seed_checks_in_area[player]["Total"] else 100 for (team, player), game_state in multisave.get("client_game_state", {}).items(): if player in groups: @@ -1303,8 +1309,8 @@ def getTracker(tracker: UUID): return render_template("tracker.html", inventory=inventory, get_item_name_from_id=lookup_any_item_id_to_name, lookup_id_to_name=Items.lookup_id_to_name, player_names=player_names, tracking_names=tracking_names, tracking_ids=tracking_ids, room=room, icons=alttp_icons, - multi_items=multi_items, checks_done=checks_done, ordered_areas=ordered_areas, - checks_in_area=seed_checks_in_area, activity_timers=activity_timers, + multi_items=multi_items, checks_done=checks_done, percent_total_checks_done=percent_total_checks_done, + ordered_areas=ordered_areas, checks_in_area=seed_checks_in_area, activity_timers=activity_timers, key_locations=group_key_locations, small_key_ids=small_key_ids, big_key_ids=big_key_ids, video=video, big_key_locations=group_big_key_locations, hints=hints, long_player_names=long_player_names) From cae1e683e2934e63806bdaa480c84c4f8f8eb5e8 Mon Sep 17 00:00:00 2001 From: kindasneaki Date: Sun, 5 Feb 2023 13:51:03 -0700 Subject: [PATCH 14/47] RoR2: 1.20 content update (#1396) ## Adding in Explore Mode: Features include: * Added in `environments` to be items. * `Location checks` are now `environment based` instead of being able to get them from anywhere. * Added in support for the `DLC Survivors of the void` which include `Void Items` and `3 new maps` that come with it. (option added to use DLC) --------- Co-authored-by: Dogpetkid --- worlds/ror2/Items.py | 47 +++++- worlds/ror2/Locations.py | 116 ++++++++++++++- worlds/ror2/Options.py | 206 +++++++++++++++++++++----- worlds/ror2/Regions.py | 126 ++++++++++++++++ worlds/ror2/RoR2Environments.py | 118 +++++++++++++++ worlds/ror2/Rules.py | 173 +++++++++++++++++---- worlds/ror2/__init__.py | 181 +++++++++++++++++----- worlds/ror2/docs/en_Risk of Rain 2.md | 57 ++++++- 8 files changed, 908 insertions(+), 116 deletions(-) create mode 100644 worlds/ror2/Regions.py create mode 100644 worlds/ror2/RoR2Environments.py diff --git a/worlds/ror2/Items.py b/worlds/ror2/Items.py index 9efbc713d8..874b2a7cef 100644 --- a/worlds/ror2/Items.py +++ b/worlds/ror2/Items.py @@ -1,13 +1,13 @@ -from typing import Dict from BaseClasses import Item from .Options import ItemWeights +from .RoR2Environments import * class RiskOfRainItem(Item): game: str = "Risk of Rain 2" -# 37000 - 38000 +# 37000 - 37699, 38000 item_table: Dict[str, int] = { "Dio's Best Friend": 37001, "Common Item": 37002, @@ -19,9 +19,24 @@ item_table: Dict[str, int] = { "Item Scrap, White": 37008, "Item Scrap, Green": 37009, "Item Scrap, Red": 37010, - "Item Scrap, Yellow": 37011 + "Item Scrap, Yellow": 37011, + "Void Item": 37012 } +# 37700 - 37699 +################################################## +# environments + +environment_offest = 37700 + +# add ALL environments into the item table +environment_offset_table = shift_by_offset(environment_ALL_table, environment_offest) +item_table.update(shift_by_offset(environment_ALL_table, environment_offest)) +# use the sotv dlc in the item table so that all names can be looked up regardless of use + +# end of environments +################################################## + default_weights: Dict[str, int] = { "Item Scrap, Green": 16, "Item Scrap, Red": 4, @@ -32,6 +47,7 @@ default_weights: Dict[str, int] = { "Legendary Item": 8, "Boss Item": 4, "Lunar Item": 16, + "Void Item": 16, "Equipment": 32 } @@ -45,6 +61,7 @@ new_weights: Dict[str, int] = { "Legendary Item": 10, "Boss Item": 5, "Lunar Item": 10, + "Void Item": 16, "Equipment": 20 } @@ -58,6 +75,7 @@ uncommon_weights: Dict[str, int] = { "Legendary Item": 10, "Boss Item": 5, "Lunar Item": 15, + "Void Item": 16, "Equipment": 20 } @@ -71,6 +89,7 @@ legendary_weights: Dict[str, int] = { "Legendary Item": 100, "Boss Item": 5, "Lunar Item": 15, + "Void Item": 16, "Equipment": 20 } @@ -84,6 +103,7 @@ lunartic_weights: Dict[str, int] = { "Legendary Item": 0, "Boss Item": 0, "Lunar Item": 100, + "Void Item": 0, "Equipment": 0 } @@ -97,6 +117,7 @@ chaos_weights: Dict[str, int] = { "Legendary Item": 30, "Boss Item": 20, "Lunar Item": 60, + "Void Item": 60, "Equipment": 40 } @@ -110,6 +131,7 @@ no_scraps_weights: Dict[str, int] = { "Legendary Item": 15, "Boss Item": 5, "Lunar Item": 10, + "Void Item": 16, "Equipment": 25 } @@ -123,6 +145,7 @@ even_weights: Dict[str, int] = { "Legendary Item": 1, "Boss Item": 1, "Lunar Item": 1, + "Void Item": 1, "Equipment": 1 } @@ -136,6 +159,21 @@ scraps_only: Dict[str, int] = { "Legendary Item": 0, "Boss Item": 0, "Lunar Item": 0, + "Void Item": 0, + "Equipment": 0 +} + +void_weights: Dict[str, int] = { + "Item Scrap, Green": 0, + "Item Scrap, Red": 0, + "Item Scrap, Yellow": 0, + "Item Scrap, White": 0, + "Common Item": 0, + "Uncommon Item": 0, + "Legendary Item": 0, + "Boss Item": 0, + "Lunar Item": 0, + "Void Item": 100, "Equipment": 0 } @@ -148,7 +186,8 @@ item_pool_weights: Dict[int, Dict[str, int]] = { ItemWeights.option_chaos: chaos_weights, ItemWeights.option_no_scraps: no_scraps_weights, ItemWeights.option_even: even_weights, - ItemWeights.option_scraps_only: scraps_only + ItemWeights.option_scraps_only: scraps_only, + ItemWeights.option_void: void_weights, } lookup_id_to_name: Dict[int, str] = {id: name for name, id in item_table.items()} diff --git a/worlds/ror2/Locations.py b/worlds/ror2/Locations.py index e4ebe8ddfa..7db3ceca73 100644 --- a/worlds/ror2/Locations.py +++ b/worlds/ror2/Locations.py @@ -1,13 +1,119 @@ -from typing import Dict +from typing import Tuple from BaseClasses import Location from .Options import TotalLocations +from .Options import ChestsPerEnvironment +from .Options import ShrinesPerEnvironment +from .Options import ScavengersPerEnvironment +from .Options import ScannersPerEnvironment +from .Options import AltarsPerEnvironment +from .RoR2Environments import * class RiskOfRainLocation(Location): game: str = "Risk of Rain 2" -# 37006 - 37506 -item_pickups: Dict[str, int] = { - f"ItemPickup{i+1}": 37000+i for i in range(TotalLocations.range_end) -} +ror2_locations_start_id = 38000 + + +def get_classic_item_pickups(n: int) -> Dict[str, int]: + """Get n ItemPickups, capped at the max value for TotalLocations""" + n = max(n, 0) + n = min(n, TotalLocations.range_end) + return { f"ItemPickup{i+1}": ror2_locations_start_id+i for i in range(n) } + + +item_pickups = get_classic_item_pickups(TotalLocations.range_end) +location_table = item_pickups + + +def environment_abreviation(long_name:str) -> str: + """convert long environment names to initials""" + abrev = "" + # go through every word finding a letter (or number) for an initial + for word in long_name.split(): + initial = word[0] + for letter in word: + if letter.isalnum(): + initial = letter + break + abrev+= initial + return abrev + +# highest numbered orderedstages (this is so we can treat the easily caculate the check ids based on the environment and location "offset") +highest_orderedstage: int= max(compress_dict_list_horizontal(environment_orderedstages_table).values()) + +ror2_locations_start_orderedstage = ror2_locations_start_id + TotalLocations.range_end + +class orderedstage_location: + """A class to behave like a struct for storing the offsets of location types in the allocated space per orderedstage environments.""" + # TODO is there a better, more generic way to do this? + offset_ChestsPerEnvironment = 0 + offset_ShrinesPerEnvironment = offset_ChestsPerEnvironment + ChestsPerEnvironment.range_end + offset_ScavengersPerEnvironment = offset_ShrinesPerEnvironment + ShrinesPerEnvironment.range_end + offset_ScannersPerEnvironment = offset_ScavengersPerEnvironment + ScavengersPerEnvironment.range_end + offset_AltarsPerEnvironment = offset_ScannersPerEnvironment + ScannersPerEnvironment.range_end + + # total space allocated to the locations in a single orderedstage environment + allocation = offset_AltarsPerEnvironment + AltarsPerEnvironment.range_end + + def get_environment_locations(chests:int, shrines:int, scavengers:int, scanners:int, altars:int, environment: Tuple[str, int]) -> Dict[str, int]: + """Get the locations within a specific environment""" + environment_name = environment[0] + environment_index = environment[1] + locations = {} + + # due to this mapping, since environment ids are not consecutive, there are lots of "wasted" id numbers + # TODO perhaps a hashing algorithm could be used to compress this range and save "wasted" ids + environment_start_id = environment_index * orderedstage_location.allocation + ror2_locations_start_orderedstage + for n in range(chests): + locations.update({f"{environment_name}: Chest {n+1}": n + orderedstage_location.offset_ChestsPerEnvironment + environment_start_id}) + for n in range(shrines): + locations.update({f"{environment_name}: Shrine {n+1}": n + orderedstage_location.offset_ShrinesPerEnvironment + environment_start_id}) + for n in range(scavengers): + locations.update({f"{environment_name}: Scavenger {n+1}": n + orderedstage_location.offset_ScavengersPerEnvironment + environment_start_id}) + for n in range(scanners): + locations.update({f"{environment_name}: Radio Scanner {n+1}": n + orderedstage_location.offset_ScannersPerEnvironment + environment_start_id}) + for n in range(altars): + locations.update({f"{environment_name}: Newt Altar {n+1}": n + orderedstage_location.offset_AltarsPerEnvironment + environment_start_id}) + return locations + + def get_locations(chests:int, shrines:int, scavengers:int, scanners:int, altars:int, dlc_sotv:bool) -> Dict[str, int]: + """Get a dictionary of locations for the ordedstage environments with the locations from the parameters.""" + locations = {} + orderedstages = compress_dict_list_horizontal(environment_vanilla_orderedstages_table) + if(dlc_sotv): orderedstages.update(compress_dict_list_horizontal(environment_sotv_orderedstages_table)) + # for every environment, generate the respective locations + for environment_name, environment_index in orderedstages.items(): + # locations = locations | orderedstage_location.get_environment_locations( + locations.update(orderedstage_location.get_environment_locations( + chests=chests, + shrines=shrines, + scavengers=scavengers, + scanners=scanners, + altars=altars, + environment=(environment_name, environment_index) + )) + return locations + + def getall_locations(dlc_sotv:bool=True) -> Dict[str, int]: + """ + Get all locations in ordered stages. + Set dlc_sotv to true for the SOTV DLC to be included. + """ + # to get all locations, attempt using as many locations as possible + return orderedstage_location.get_locations( + chests=ChestsPerEnvironment.range_end, + shrines=ShrinesPerEnvironment.range_end, + scavengers=ScavengersPerEnvironment.range_end, + scanners=ScannersPerEnvironment.range_end, + altars=AltarsPerEnvironment.range_end, + dlc_sotv=dlc_sotv + ) + + +ror2_location_post_orderedstage = ror2_locations_start_orderedstage + highest_orderedstage*orderedstage_location.allocation +location_table.update(orderedstage_location.getall_locations()) +# use the sotv dlc in the lookup table so that all ids can be looked up regardless of use + +lookup_id_to_name: Dict[int, str] = {id: name for name, id in location_table.items()} diff --git a/worlds/ror2/Options.py b/worlds/ror2/Options.py index a95cbf597a..8876a4e423 100644 --- a/worlds/ror2/Options.py +++ b/worlds/ror2/Options.py @@ -1,31 +1,99 @@ from typing import Dict -from Options import Option, DefaultOnToggle, Range, Choice +from Options import Option, Toggle, DefaultOnToggle, DeathLink, Range, Choice + + +# NOTE be aware that since the range of item ids that RoR2 uses is based off of the maximums of checks +# Be careful when changing the range_end values not to go into another game's IDs +# NOTE that these changes to range_end must also be reflected in the RoR2 client so it understands the same ids. + +class Goal(Choice): + """ + Classic Mode: Every Item pickup increases fills a progress bar which gives location checks. + + Explore Mode: Each environment will have location checks within each environment. + environments will be locked in the item pool until received. + """ + display_name = "Game Mode" + option_classic = 0 + option_explore = 1 + default = 0 class TotalLocations(Range): - """Number of location checks which are added to the Risk of Rain playthrough.""" + """Classic Mode: Number of location checks which are added to the Risk of Rain playthrough.""" display_name = "Total Locations" - range_start = 10 + range_start = 40 range_end = 250 - default = 20 + default = 40 +class ChestsPerEnvironment(Range): + """Explore Mode: The number of chest locations per environment.""" + display_name = "Chests per Environment" + range_start = 2 + range_end = 20 + default = 10 + + +class ShrinesPerEnvironment(Range): + """Explore Mode: The number of shrine locations per environment.""" + display_name = "Shrines per Environment" + range_start = 2 + range_end = 20 + default = 5 + + +class ScavengersPerEnvironment(Range): + """Explore Mode: The number of scavenger locations per environment.""" + display_name = "Scavenger per Environment" + range_start = 0 + range_end = 1 + default = 1 + +class ScannersPerEnvironment(Range): + """Explore Mode: The number of scanners locations per environment.""" + display_name = "Radio Scanners per Environment" + range_start = 0 + range_end = 1 + default = 1 + +class AltarsPerEnvironment(Range): + """Explore Mode: The number of altars locations per environment.""" + display_name = "Newts Per Environment" + range_start = 0 + range_end = 2 + default = 1 + class TotalRevivals(Range): """Total Percentage of `Dio's Best Friend` item put in the item pool.""" - display_name = "Total Percentage Revivals Available" + display_name = "Total Revives" range_start = 0 range_end = 10 default = 4 class ItemPickupStep(Range): - """Number of items to pick up before an AP Check is completed. + """ + Number of items to pick up before an AP Check is completed. Setting to 1 means every other pickup. - Setting to 2 means every third pickup. So on...""" + Setting to 2 means every third pickup. So on... + """ display_name = "Item Pickup Step" range_start = 0 range_end = 5 - default = 2 + default = 1 + +class ShrineUseStep(Range): + """ + Explore Mode: + Number of shrines to use up before an AP Check is completed. + Setting to 1 means every other pickup. + Setting to 2 means every third pickup. So on... + """ + display_name = "Shrine use Step" + range_start = 0 + range_end = 3 + default = 0 class AllowLunarItems(DefaultOnToggle): @@ -38,13 +106,33 @@ class StartWithRevive(DefaultOnToggle): display_name = "Start with a Revive" -class FinalStageDeath(DefaultOnToggle): +class FinalStageDeath(Toggle): """Death on the final boss stage counts as a win.""" display_name = "Final Stage Death is Win" +class BeginWithLoop(Toggle): + """ + Enable to precollect a full loop of environments. + Only has an effect with Explore Mode. + """ + display_name = "Begin With Loop" + + +class DLC_SOTV(Toggle): + """ + Enable if you are using SOTV DLC. + Affects environment availability for Explore Mode. + Adds Void Items into the item pool + """ + display_name = "Enable DLC - SOTV" + + + class GreenScrap(Range): - """Weight of Green Scraps in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """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 @@ -52,7 +140,9 @@ class GreenScrap(Range): class RedScrap(Range): - """Weight of Red Scraps in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """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 @@ -60,7 +150,9 @@ class RedScrap(Range): class YellowScrap(Range): - """Weight of yellow scraps in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """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 @@ -68,7 +160,9 @@ class YellowScrap(Range): class WhiteScrap(Range): - """Weight of white scraps in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """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 @@ -76,7 +170,9 @@ class WhiteScrap(Range): class CommonItem(Range): - """Weight of common items in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """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 @@ -84,7 +180,9 @@ class CommonItem(Range): class UncommonItem(Range): - """Weight of uncommon items in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """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 @@ -92,7 +190,9 @@ class UncommonItem(Range): class LegendaryItem(Range): - """Weight of legendary items in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """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 @@ -100,7 +200,9 @@ class LegendaryItem(Range): class BossItem(Range): - """Weight of boss items in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """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 @@ -108,36 +210,54 @@ class BossItem(Range): class LunarItem(Range): - """Weight of lunar items in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """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 default = 16 +class VoidItem(Range): + """Weight of void items in the item pool. + + (Ignored unless Item Weight Presets is 'No') + + (Ignored if Enable DLC - SOTV is 'No') """ + display_name = "Void Items" + range_start = 0 + range_end = 100 + default = 16 + + class Equipment(Range): - """Weight of equipment items in the item pool. (Ignored unless Item Weight Presets is 'No')""" + """Weight of equipment items in the item pool. + + (Ignored unless Item Weight Presets is 'No')""" display_name = "Equipment" range_start = 0 range_end = 100 default = 32 -class ItemPoolPresetToggle(DefaultOnToggle): +class ItemPoolPresetToggle(Toggle): """Will use the item weight presets when set to true, otherwise will use the custom set item pool weights.""" display_name = "Use 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. - Scraps Only will be only scrap items in the item pool.""" + """Set item_pool_presets to true if you want to use one of these presets. + 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. + - Void makes everything a void item.""" display_name = "Item Weights" option_default = 0 option_new = 1 @@ -148,6 +268,7 @@ class ItemWeights(Choice): option_no_scraps = 6 option_even = 7 option_scraps_only = 8 + option_void = 9 # define a dictionary for the weights of the generated item pool. @@ -161,17 +282,28 @@ ror2_weights: Dict[str, type(Option)] = { "legendary_item": LegendaryItem, "boss_item": BossItem, "lunar_item": LunarItem, + "void_item": VoidItem, "equipment": Equipment } ror2_options: 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, - "item_pool_presets": ItemPoolPresetToggle, + "goal": Goal, + "total_locations": TotalLocations, + "chests_per_stage": ChestsPerEnvironment, + "shrines_per_stage": ShrinesPerEnvironment, + "scavengers_per_stage": ScavengersPerEnvironment, + "scanner_per_stage": ScannersPerEnvironment, + "altars_per_stage": AltarsPerEnvironment, + "total_revivals": TotalRevivals, + "start_with_revive": StartWithRevive, + "final_stage_death": FinalStageDeath, + "begin_with_loop": BeginWithLoop, + "dlc_sotv": DLC_SOTV, + "death_link": DeathLink, + "item_pickup_step": ItemPickupStep, + "shrine_use_step": ShrineUseStep, + "enable_lunar": AllowLunarItems, + "item_weights": ItemWeights, + "item_pool_presets": ItemPoolPresetToggle, **ror2_weights } diff --git a/worlds/ror2/Regions.py b/worlds/ror2/Regions.py new file mode 100644 index 0000000000..878d91ec38 --- /dev/null +++ b/worlds/ror2/Regions.py @@ -0,0 +1,126 @@ +from typing import Dict, List, NamedTuple, Optional + +from BaseClasses import MultiWorld, Region, RegionType, Entrance +from .Locations import location_table, RiskOfRainLocation + + +class RoRRegionData(NamedTuple): + locations: Optional[List[str]] + region_exits: Optional[List[str]] + + +def create_regions(multiworld: MultiWorld, player: int): + # Default Locations + non_dlc_regions: Dict[str, RoRRegionData] = { + "Menu": RoRRegionData(None, ["Distant Roost", "Distant Roost (2)", "Titanic Plains", "Titanic Plains (2)"]), + "Distant Roost": RoRRegionData([], ["OrderedStage_1"]), + "Distant Roost (2)": RoRRegionData([], ["OrderedStage_1"]), + "Titanic Plains": RoRRegionData([], ["OrderedStage_1"]), + "Titanic Plains (2)": RoRRegionData([], ["OrderedStage_1"]), + "Abandoned Aqueduct": RoRRegionData([], ["OrderedStage_2"]), + "Wetland Aspect": RoRRegionData([], ["OrderedStage_2"]), + "Rallypoint Delta": RoRRegionData([], ["OrderedStage_3"]), + "Scorched Acres": RoRRegionData([], ["OrderedStage_3"]), + "Abyssal Depths": RoRRegionData([], ["OrderedStage_4"]), + "Siren's Call": RoRRegionData([], ["OrderedStage_4"]), + "Sundered Grove": RoRRegionData([], ["OrderedStage_4"]), + "Sky Meadow": RoRRegionData([], ["Hidden Realm: Bulwark's Ambry", "OrderedStage_5"]), + } + # SOTV Regions + dlc_regions: Dict[str, RoRRegionData] = { + "Siphoned Forest": RoRRegionData([], ["OrderedStage_1"]), + "Aphelian Sanctuary": RoRRegionData([], ["OrderedStage_2"]), + "Sulfur Pools": RoRRegionData([], ["OrderedStage_3"]) + } + other_regions: Dict[str, RoRRegionData] = { + "Commencement": RoRRegionData(None, ["Victory"]), + "OrderedStage_5": RoRRegionData(None, ["Hidden Realm: A Moment, Fractured", "Commencement"]), + "OrderedStage_1": RoRRegionData(None, ["Hidden Realm: Bazaar Between Time", + "Hidden Realm: Gilded Coast", "Abandoned Aqueduct", "Wetland Aspect"]), + "OrderedStage_2": RoRRegionData(None, ["Rallypoint Delta", "Scorched Acres"]), + "OrderedStage_3": RoRRegionData(None, ["Abyssal Depths", "Siren's Call", "Sundered Grove"]), + "OrderedStage_4": RoRRegionData(None, ["Sky Meadow"]), + "Hidden Realm: A Moment, Fractured": RoRRegionData(None, ["Hidden Realm: A Moment, Whole"]), + "Hidden Realm: A Moment, Whole": RoRRegionData(None, ["Victory"]), + "Void Fields": RoRRegionData(None, []), + "Victory": RoRRegionData(None, None), + "Petrichor V": RoRRegionData(None, ["Victory"]), + "Hidden Realm: Bulwark's Ambry": RoRRegionData(None, None), + "Hidden Realm: Bazaar Between Time": RoRRegionData(None, ["Void Fields"]), + "Hidden Realm: Gilded Coast": RoRRegionData(None, None) + } + dlc_other_regions: Dict[str, RoRRegionData] = { + "The Planetarium": RoRRegionData(None, ["Victory"]), + "Void Locus": RoRRegionData(None, ["The Planetarium"]) + } + # Totals of each item + chests = int(multiworld.chests_per_stage[player]) + shrines = int(multiworld.shrines_per_stage[player]) + scavengers = int(multiworld.scavengers_per_stage[player]) + scanners = int(multiworld.scanner_per_stage[player]) + newt = int(multiworld.altars_per_stage[player]) + all_location_regions = {**non_dlc_regions} + if multiworld.dlc_sotv[player]: + all_location_regions = {**non_dlc_regions, **dlc_regions} + + # Locations + for key in all_location_regions: + if key == "Menu": + continue + # Chests + for i in range(0, chests): + all_location_regions[key].locations.append(f"{key}: Chest {i + 1}") + # Shrines + for i in range(0, shrines): + all_location_regions[key].locations.append(f"{key}: Shrine {i + 1}") + # Scavengers + if scavengers > 0: + for i in range(0, scavengers): + all_location_regions[key].locations.append(f"{key}: Scavenger {i + 1}") + # Radio Scanners + if scanners > 0: + for i in range(0, scanners): + all_location_regions[key].locations.append(f"{key}: Radio Scanner {i + 1}") + # Newt Altars + if newt > 0: + for i in range(0, newt): + all_location_regions[key].locations.append(f"{key}: Newt Altar {i + 1}") + regions_pool: Dict = {**all_location_regions, **other_regions} + + # DLC Locations + if multiworld.dlc_sotv[player]: + non_dlc_regions["Menu"].region_exits.append("Siphoned Forest") + other_regions["OrderedStage_2"].region_exits.append("Aphelian Sanctuary") + other_regions["OrderedStage_3"].region_exits.append("Sulfur Pools") + other_regions["Commencement"].region_exits.append("The Planetarium") + other_regions["Void Fields"].region_exits.append("Void Locus") + regions_pool: Dict = {**all_location_regions, **other_regions, **dlc_other_regions} + + # Create all the regions + for name, data in regions_pool.items(): + multiworld.regions.append(create_region(multiworld, player, name, data)) + + # Connect all the regions to their exits + for name, data in regions_pool.items(): + create_connections_in_regions(multiworld, player, name, data) + + +def create_region(multiworld: MultiWorld, player: int, name: str, data: RoRRegionData): + region = Region(name, RegionType.Generic, name, player, multiworld) + if data.locations: + for location_name in data.locations: + location_data = location_table.get(location_name) + location = RiskOfRainLocation(player, location_name, location_data, region) + region.locations.append(location) + + return region + + +def create_connections_in_regions(multiworld: MultiWorld, player: int, name: str, data: RoRRegionData): + region = multiworld.get_region(name, player) + if data.region_exits: + for region_exit in data.region_exits: + r_exit_stage = Entrance(player, region_exit, region) + exit_region = multiworld.get_region(region_exit, player) + r_exit_stage.connect(exit_region) + region.exits.append(r_exit_stage) diff --git a/worlds/ror2/RoR2Environments.py b/worlds/ror2/RoR2Environments.py new file mode 100644 index 0000000000..43d2fe52af --- /dev/null +++ b/worlds/ror2/RoR2Environments.py @@ -0,0 +1,118 @@ +from typing import Dict, List, TypeVar + +# TODO probably move to Locations + +environment_vanilla_orderedstage_1_table: Dict[str, int] = { + "Distant Roost": 7, # blackbeach + "Distant Roost (2)": 8, # blackbeach2 + "Titanic Plains": 15, # golemplains + "Titanic Plains (2)": 16, # golemplains2 +} +environment_vanilla_orderedstage_2_table: Dict[str, int] = { + "Abandoned Aqueduct": 17, # goolake + "Wetland Aspect": 12, # foggyswamp +} +environment_vanilla_orderedstage_3_table: Dict[str, int] = { + "Rallypoint Delta": 13, # frozenwall + "Scorched Acres": 47, # wispgraveyard +} +environment_vanilla_orderedstage_4_table: Dict[str, int] = { + "Abyssal Depths": 10, # dampcavesimple + "Siren's Call": 37, # shipgraveyard + "Sundered Grove": 35, # rootjungle +} +environment_vanilla_orderedstage_5_table: Dict[str, int] = { + "Sky Meadow": 38, # skymeadow +} + +environment_vanilla_hidden_realm_table: Dict[str, int] = { + "Hidden Realm: Bulwark's Ambry": 5, # artifactworld + "Hidden Realm: Bazaar Between Time": 6, # bazaar + "Hidden Realm: Gilded Coast": 14, # goldshores + "Hidden Realm: A Moment, Whole": 27, # limbo + "Hidden Realm: A Moment, Fractured": 33, # mysteryspace +} + +environment_vanilla_special_table: Dict[str, int] = { + "Void Fields": 4, # arena + "Commencement": 32, # moon2 +} + +environment_sotv_orderedstage_1_table: Dict[str, int] = { + "Siphoned Forest": 39, # snowyforest +} +environment_sotv_orderedstage_2_table: Dict[str, int] = { + "Aphelian Sanctuary": 3, # ancientloft +} +environment_sotv_orderedstage_3_table: Dict[str, int] = { + "Sulfur Pools": 41, # sulfurpools +} +environment_sotv_orderedstage_4_table: Dict[str, int] = { } +environment_sotv_orderedstage_5_table: Dict[str, int] = { } + +# TODO idk much and idc much about simulacrum, is there a forced order or something? +environment_sotv_simulacrum_table: Dict[str, int] = { + "The Simulacrum (Aphelian Sanctuary)": 20, # itancientloft + "The Simulacrum (Abyssal Depths)": 21, # itdampcave + "The Simulacrum (Rallypoint Delta)": 22, # itfrozenwall + "The Simulacrum (Titanic Plains)": 23, # itgolemplains + "The Simulacrum (Abandoned Aqueduct)": 24, # itgoolake + "The Simulacrum (Commencement)": 25, # itmoon + "The Simulacrum (Sky Meadow)": 26, # itskymeadow +} + +environment_sotv_special_table: Dict[str, int] = { + "Void Locus": 45, # voidstage + "The Planetarium": 46, # voidraid +} + +X = TypeVar("X") +Y = TypeVar("Y") + + +def compress_dict_list_horizontal(list_of_dict: List[Dict[X, Y]]) -> Dict[X, Y]: + """Combine all dictionaries in a list together into one dictionary.""" + compressed: Dict[X,Y] = {} + for individual in list_of_dict: compressed.update(individual) + return compressed + +def collapse_dict_list_vertical(list_of_dict1: List[Dict[X, Y]], *args: List[Dict[X, Y]]) -> List[Dict[X, Y]]: + """Combine all parallel dictionaries in lists together to make a new list of dictionaries of the same length.""" + # find the length of the longest list + length = len(list_of_dict1) + for list_of_dictN in args: + length = max(length, len(list_of_dictN)) + + # create a combined list with a length the same as the longest list + collapsed = [{}] * (length) + # The reason the list_of_dict1 is not directly used to make collapsed is + # side effects can occur if all the dictionaries are not manually unioned. + + # merge contents from list_of_dict1 + for i in range(len(list_of_dict1)): + collapsed[i] = {**collapsed[i], **list_of_dict1[i]} + + # merge contents of remaining lists_of_dicts + for list_of_dictN in args: + for i in range(len(list_of_dictN)): + collapsed[i] = {**collapsed[i], **list_of_dictN[i]} + + return collapsed + +# TODO potentially these should only be created when they are directly referenced (unsure of the space/time cost of creating these initially) + +environment_vanilla_orderedstages_table = [ environment_vanilla_orderedstage_1_table, environment_vanilla_orderedstage_2_table, environment_vanilla_orderedstage_3_table, environment_vanilla_orderedstage_4_table, environment_vanilla_orderedstage_5_table ] +environment_vanilla_table = {**compress_dict_list_horizontal(environment_vanilla_orderedstages_table), **environment_vanilla_hidden_realm_table, **environment_vanilla_special_table} + +environment_sotv_orderedstages_table = [ environment_sotv_orderedstage_1_table, environment_sotv_orderedstage_2_table, environment_sotv_orderedstage_3_table, environment_sotv_orderedstage_4_table, environment_sotv_orderedstage_5_table ] +environment_sotv_non_simulacrum_table = {**compress_dict_list_horizontal(environment_sotv_orderedstages_table), **environment_sotv_special_table} +environment_sotv_table = {**environment_sotv_non_simulacrum_table} + +environment_non_orderedstages_table = {**environment_vanilla_hidden_realm_table, **environment_vanilla_special_table, **environment_sotv_simulacrum_table, **environment_sotv_special_table} +environment_orderedstages_table = collapse_dict_list_vertical(environment_vanilla_orderedstages_table, environment_sotv_orderedstages_table) +environment_ALL_table = {**environment_vanilla_table, **environment_sotv_table} + + +def shift_by_offset(dictionary: Dict[str, int], offset:int) -> Dict[str, int]: + """Shift all indexes in a dictionary by an offset""" + return {name:index+offset for name, index in dictionary.items()} diff --git a/worlds/ror2/Rules.py b/worlds/ror2/Rules.py index bf00f617d8..66a2e7409a 100644 --- a/worlds/ror2/Rules.py +++ b/worlds/ror2/Rules.py @@ -1,33 +1,154 @@ -from BaseClasses import MultiWorld +from BaseClasses import MultiWorld, CollectionState from worlds.generic.Rules import set_rule, add_rule +from .Locations import orderedstage_location +from .RoR2Environments import environment_vanilla_orderedstages_table, environment_sotv_orderedstages_table, \ + environment_orderedstages_table -def set_rules(world: MultiWorld, player: int) -> None: - total_locations = world.total_locations[player].value # total locations for current player +# Rule to see if it has access to the previous stage +def has_entrance_access_rule(multiworld: MultiWorld, stage: str, entrance: str, player: int): + multiworld.get_entrance(entrance, player).access_rule = \ + lambda state: state.has(entrance, player) and state.has(stage, player) + + +# Checks to see if chest/shrine are accessible +def has_location_access_rule(multiworld: MultiWorld, environment: str, player: int, item_number: int, item_type: str): + if item_number == 1: + multiworld.get_location(f"{environment}: {item_type} {item_number}", player).access_rule = \ + lambda state: state.has(environment, player) + if item_type == "Scavenger": + multiworld.get_location(f"{environment}: {item_type} {item_number}", player).access_rule = \ + lambda state: state.has(environment, player) and state.has("Stage_4", player) + else: + multiworld.get_location(f"{environment}: {item_type} {item_number}", player).access_rule = \ + lambda state: check_location(state, environment, player, item_number, item_type) + + +def check_location(state, environment: str, player: int, item_number: int, item_name: str): + return state.can_reach(f"{environment}: {item_name} {item_number - 1}", "Location", player) + + +# unlock event to next set of stages +def get_stage_event(multiworld: MultiWorld, player: int, stage_number: int): + if not multiworld.dlc_sotv[player]: + environment_name = multiworld.random.choices(list(environment_vanilla_orderedstages_table[stage_number].keys()), k=1) + else: + environment_name = multiworld.random.choices(list(environment_orderedstages_table[stage_number].keys()), k=1) + multiworld.get_location(f"Stage_{stage_number+1}", player).access_rule = \ + lambda state: get_one_of_the_stages(state, environment_name[0], player) + + +def get_one_of_the_stages(state: CollectionState, stage: str, player: int): + return state.has(stage, player) + + +def set_rules(multiworld: MultiWorld, player: int) -> None: + + if multiworld.goal[player] == "classic": + # classic mode + total_locations = multiworld.total_locations[player].value # total locations for current player + else: + # explore mode + total_locations = len( + orderedstage_location.get_locations( + chests=multiworld.chests_per_stage[player].value, + shrines=multiworld.shrines_per_stage[player].value, + scavengers=multiworld.scavengers_per_stage[player].value, + scanners=multiworld.scanner_per_stage[player].value, + altars=multiworld.altars_per_stage[player].value, + dlc_sotv=multiworld.dlc_sotv[player].value + ) + ) + event_location_step = 25 # set an event location at these locations for "spheres" divisions = total_locations // event_location_step - total_revivals = world.worlds[player].total_revivals # pulling this info we calculated in generate_basic + total_revivals = multiworld.worlds[player].total_revivals # pulling this info we calculated in generate_basic - if divisions: - for i in range(1, divisions): # since divisions is the floor of total_locations / 25 - event_loc = world.get_location(f"Pickup{i * event_location_step}", player) - set_rule(event_loc, - lambda state, i=i: state.can_reach(f"ItemPickup{i * event_location_step - 1}", "Location", player)) - for n in range(i * event_location_step, (i + 1) * event_location_step): # we want to create a rule for each of the 25 locations per division - if n == i * event_location_step: - set_rule(world.get_location(f"ItemPickup{n}", player), - lambda state, event_item=event_loc.item.name: state.has(event_item, player)) - else: - set_rule(world.get_location(f"ItemPickup{n}", player), - lambda state, n=n: state.can_reach(f"ItemPickup{n - 1}", "Location", player)) - for i in range(divisions * event_location_step, total_locations+1): - set_rule(world.get_location(f"ItemPickup{i}", player), - lambda state, i=i: state.can_reach(f"ItemPickup{i - 1}", "Location", player)) - set_rule(world.get_location("Victory", player), - lambda state: state.can_reach(f"ItemPickup{total_locations}", "Location", player)) - if total_revivals or world.start_with_revive[player].value: - add_rule(world.get_location("Victory", player), - lambda state: state.has("Dio's Best Friend", player, - total_revivals + world.start_with_revive[player])) + if multiworld.goal[player] == "classic": + # classic mode + if divisions: + for i in range(1, divisions): # since divisions is the floor of total_locations / 25 + event_loc = multiworld.get_location(f"Pickup{i * event_location_step}", player) + set_rule(event_loc, + lambda state, i=i: state.can_reach(f"ItemPickup{i * event_location_step - 1}", "Location", player)) + for n in range(i * event_location_step, (i + 1) * event_location_step): # we want to create a rule for each of the 25 locations per division + if n == i * event_location_step: + set_rule(multiworld.get_location(f"ItemPickup{n}", player), + lambda state, event_item=event_loc.item.name: state.has(event_item, player)) + else: + set_rule(multiworld.get_location(f"ItemPickup{n}", player), + lambda state, n=n: state.can_reach(f"ItemPickup{n - 1}", "Location", player)) + for i in range(divisions * event_location_step, total_locations+1): + set_rule(multiworld.get_location(f"ItemPickup{i}", player), + lambda state, i=i: state.can_reach(f"ItemPickup{i - 1}", "Location", player)) + set_rule(multiworld.get_location("Victory", player), + lambda state: state.can_reach(f"ItemPickup{total_locations}", "Location", player)) + if total_revivals or multiworld.start_with_revive[player].value: + add_rule(multiworld.get_location("Victory", player), + lambda state: state.has("Dio's Best Friend", player, + total_revivals + multiworld.start_with_revive[player])) - world.completion_condition[player] = lambda state: state.has("Victory", player) + elif multiworld.goal[player] == "explore": + # When explore_mode is used, + # scavengers need to be locked till after a full loop since that is when they are capable of spawning. + # (While technically the requirement is just beating 5 stages, this will ensure that the player will have + # a long enough run to have enough director credits for scavengers and + # help prevent being stuck in the same stages until that point.) + + for location in multiworld.get_locations(): + if location.player != player: continue # ignore all checks that don't belong to this player + if "Scavenger" in location.name: + add_rule(location, lambda state: state.has("Stage_5", player)) + # Regions + chests = multiworld.chests_per_stage[player] + shrines = multiworld.shrines_per_stage[player] + newts = multiworld.altars_per_stage[player] + scavengers = multiworld.scavengers_per_stage[player] + scanners = multiworld.scanner_per_stage[player] + for i in range(len(environment_vanilla_orderedstages_table)): + for environment_name, _ in environment_vanilla_orderedstages_table[i].items(): + # Make sure to go through each location + if scavengers == 1: + has_location_access_rule(multiworld, environment_name, player, scavengers, "Scavenger") + if scanners == 1: + has_location_access_rule(multiworld, environment_name, player, scanners, "Radio Scanner") + for chest in range(1, chests + 1): + has_location_access_rule(multiworld, environment_name, player, chest, "Chest") + for shrine in range(1, shrines + 1): + has_location_access_rule(multiworld, environment_name, player, shrine, "Shrine") + if newts > 0: + for newt in range(1, newts + 1): + has_location_access_rule(multiworld, environment_name, player, newt, "Newt Altar") + if i > 0: + has_entrance_access_rule(multiworld, f"Stage_{i}", environment_name, player) + get_stage_event(multiworld, player, i) + + if multiworld.dlc_sotv[player]: + for i in range(len(environment_sotv_orderedstages_table)): + for environment_name, _ in environment_sotv_orderedstages_table[i].items(): + # Make sure to go through each location + if scavengers == 1: + has_location_access_rule(multiworld, environment_name, player, scavengers, "Scavenger") + if scanners == 1: + has_location_access_rule(multiworld, environment_name, player, scanners, "Radio Scanner") + for chest in range(1, chests + 1): + has_location_access_rule(multiworld, environment_name, player, chest, "Chest") + for shrine in range(1, shrines + 1): + has_location_access_rule(multiworld, environment_name, player, shrine, "Shrine") + if newts > 0: + for newt in range(1, newts + 1): + has_location_access_rule(multiworld, environment_name, player, newt, "Newt Altar") + if i > 0: + has_entrance_access_rule(multiworld, f"Stage_{i}", environment_name, player) + has_entrance_access_rule(multiworld, f"Sky Meadow", "Hidden Realm: Bulwark's Ambry", player) + has_entrance_access_rule(multiworld, f"Hidden Realm: A Moment, Fractured", "Hidden Realm: A Moment, Whole", player) + has_entrance_access_rule(multiworld, f"Stage_1", "Hidden Realm: Gilded Coast", player) + has_entrance_access_rule(multiworld, f"Stage_1", "Hidden Realm: Bazaar Between Time", player) + has_entrance_access_rule(multiworld, f"Hidden Realm: Bazaar Between Time", "Void Fields", player) + has_entrance_access_rule(multiworld, f"Stage_5", "Commencement", player) + has_entrance_access_rule(multiworld, f"Stage_5", "Hidden Realm: A Moment, Fractured", player) + if multiworld.dlc_sotv[player]: + has_entrance_access_rule(multiworld, f"Stage_5", "Void Locus", player) + has_entrance_access_rule(multiworld, f"Void Locus", "The Planetarium", player) + # Win Condition + multiworld.completion_condition[player] = lambda state: state.has("Victory", player) diff --git a/worlds/ror2/__init__.py b/worlds/ror2/__init__.py index 127e614fb2..59c9801c38 100644 --- a/worlds/ror2/__init__.py +++ b/worlds/ror2/__init__.py @@ -1,14 +1,14 @@ import string -from typing import Dict, List -from BaseClasses import Entrance, Item, ItemClassification, MultiWorld, Region, RegionType, Tutorial -from worlds.AutoWorld import WebWorld, World -from .Items import RiskOfRainItem, item_pool_weights, item_table -from .Locations import RiskOfRainLocation, item_pickups -from .Options import ItemWeights, ror2_options +from .Items import RiskOfRainItem, item_table, item_pool_weights, environment_offest +from .Locations import RiskOfRainLocation, get_classic_item_pickups, item_pickups, orderedstage_location from .Rules import set_rules +from .RoR2Environments import * -client_version = 1 +from BaseClasses import Region, RegionType, Entrance, Item, ItemClassification, MultiWorld, Tutorial +from .Options import ror2_options, ItemWeights +from worlds.AutoWorld import World, WebWorld +from .Regions import create_regions class RiskOfWeb(WebWorld): @@ -35,20 +35,58 @@ class RiskOfRainWorld(World): item_name_to_id = item_table location_name_to_id = item_pickups - data_version = 4 + data_version = 6 + required_client_version = (0, 3, 7) web = RiskOfWeb() total_revivals: int def generate_early(self) -> None: # figure out how many revivals should exist in the pool + if self.multiworld.goal[self.player] == "classic": + total_locations = self.multiworld.total_locations[self.player].value + else: + total_locations = len( + orderedstage_location.get_locations( + chests=self.multiworld.chests_per_stage[self.player].value, + shrines=self.multiworld.shrines_per_stage[self.player].value, + scavengers=self.multiworld.scavengers_per_stage[self.player].value, + scanners=self.multiworld.scanner_per_stage[self.player].value, + altars=self.multiworld.altars_per_stage[self.player].value, + dlc_sotv=self.multiworld.dlc_sotv[self.player].value + ) + ) self.total_revivals = int(self.multiworld.total_revivals[self.player].value / 100 * - self.multiworld.total_locations[self.player].value) - - def generate_basic(self) -> None: - # shortcut for starting_inventory... The start_with_revive option lets you start with a Dio's Best Friend + total_locations) + # self.total_revivals = self.multiworld.total_revivals[self.player].value if self.multiworld.start_with_revive[self.player].value: + self.total_revivals -= 1 + + def create_items(self) -> None: + # shortcut for starting_inventory... The start_with_revive option lets you start with a Dio's Best Friend + if self.multiworld.start_with_revive[self.player]: self.multiworld.push_precollected(self.multiworld.create_item("Dio's Best Friend", self.player)) + environments_pool = {} + # only mess with the environments if they are set as items + if self.multiworld.goal[self.player] == "explore": + + # figure out all available ordered stages for each tier + environment_available_orderedstages_table = environment_vanilla_orderedstages_table + if self.multiworld.dlc_sotv[self.player]: + environment_available_orderedstages_table = collapse_dict_list_vertical(environment_available_orderedstages_table, environment_sotv_orderedstages_table) + + environments_pool = shift_by_offset(environment_vanilla_table, environment_offest) + + if self.multiworld.dlc_sotv[self.player]: + environment_offset_table = shift_by_offset(environment_sotv_table, environment_offest) + environments_pool = {**environments_pool, **environment_offset_table} + environments_to_precollect = 5 if self.multiworld.begin_with_loop[self.player].value else 1 + # percollect environments for each stage (or just stage 1) + for i in range(environments_to_precollect): + unlock = self.multiworld.random.choices(list(environment_available_orderedstages_table[i].keys()), k=1) + self.multiworld.push_precollected(self.create_item(unlock[0])) + environments_pool.pop(unlock[0]) + # if presets are enabled generate junk_pool from the selected preset pool_option = self.multiworld.item_weights[self.player].value junk_pool: Dict[str, int] = {} @@ -70,61 +108,120 @@ class RiskOfRainWorld(World): "Legendary Item": self.multiworld.legendary_item[self.player].value, "Boss Item": self.multiworld.boss_item[self.player].value, "Lunar Item": self.multiworld.lunar_item[self.player].value, + "Void Item": self.multiworld.void_item[self.player].value, "Equipment": self.multiworld.equipment[self.player].value } # remove lunar items from the pool if they're disabled in the yaml unless lunartic is rolled - if not (self.multiworld.enable_lunar[self.player] or pool_option == ItemWeights.option_lunartic): + if not self.multiworld.enable_lunar[self.player] or pool_option == ItemWeights.option_lunartic: junk_pool.pop("Lunar Item") + # remove void items from the pool + if not self.multiworld.dlc_sotv[self.player] or pool_option == ItemWeights.option_void: + junk_pool.pop("Void Item") # Generate item pool itempool: List = [] # Add revive items for the player itempool += ["Dio's Best Friend"] * self.total_revivals + for env_name, _ in environments_pool.items(): + itempool += [env_name] + + # precollected environments are popped from the pool so counting like this is valid + nonjunk_item_count = self.total_revivals + len(environments_pool) + if self.multiworld.goal[self.player] == "classic": + # classic mode + total_locations = self.multiworld.total_locations[self.player].value + else: + # explore mode + total_locations = len( + orderedstage_location.get_locations( + chests=self.multiworld.chests_per_stage[self.player].value, + shrines=self.multiworld.shrines_per_stage[self.player].value, + scavengers=self.multiworld.scavengers_per_stage[self.player].value, + scanners=self.multiworld.scanner_per_stage[self.player].value, + altars=self.multiworld.altars_per_stage[self.player].value, + dlc_sotv=self.multiworld.dlc_sotv[self.player].value + ) + ) + junk_item_count = total_locations - nonjunk_item_count # Fill remaining items with randomly generated junk itempool += self.multiworld.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()), - k=self.multiworld.total_locations[self.player].value - self.total_revivals) + k=junk_item_count) # Convert itempool into real items itempool = list(map(lambda name: self.create_item(name), itempool)) - self.multiworld.itempool += itempool def set_rules(self) -> None: set_rules(self.multiworld, self.player) def create_regions(self) -> None: - menu = create_region(self.multiworld, self.player, "Menu") - petrichor = create_region(self.multiworld, self.player, "Petrichor V", - [f"ItemPickup{i + 1}" for i in range(self.multiworld.total_locations[self.player].value)]) - connection = Entrance(self.player, "Lobby", menu) - menu.exits.append(connection) - connection.connect(petrichor) + if self.multiworld.goal[self.player] == "classic": + # classic mode + menu = create_region(self.multiworld, self.player, "Menu") + self.multiworld.regions.append(menu) + # By using a victory region, we can define it as being connected to by several regions + # which can then determine the availability of the victory. + victory_region = create_region(self.multiworld, self.player, "Victory") + self.multiworld.regions.append(victory_region) + petrichor = create_region(self.multiworld, self.player, "Petrichor V", + get_classic_item_pickups(self.multiworld.total_locations[self.player].value)) + self.multiworld.regions.append(petrichor) - self.multiworld.regions += [menu, petrichor] + # classic mode can get to victory from the beginning of the game + to_victory = Entrance(self.player, "beating game", petrichor) + petrichor.exits.append(to_victory) + to_victory.connect(victory_region) + + connection = Entrance(self.player, "Lobby", menu) + menu.exits.append(connection) + connection.connect(petrichor) + else: + # explore mode + create_regions(self.multiworld, self.player) create_events(self.multiworld, self.player) def fill_slot_data(self): return { "itemPickupStep": self.multiworld.item_pickup_step[self.player].value, + "shrineUseStep": self.multiworld.shrine_use_step[self.player].value, + "goal": self.multiworld.goal[self.player].value, "seed": "".join(self.multiworld.per_slot_randoms[self.player].choice(string.digits) for _ in range(16)), "totalLocations": self.multiworld.total_locations[self.player].value, + "chestsPerStage": self.multiworld.chests_per_stage[self.player].value, + "shrinesPerStage": self.multiworld.shrines_per_stage[self.player].value, + "scavengersPerStage": self.multiworld.scavengers_per_stage[self.player].value, + "scannerPerStage": self.multiworld.scanner_per_stage[self.player].value, + "altarsPerStage": self.multiworld.altars_per_stage[self.player].value, "totalRevivals": self.multiworld.total_revivals[self.player].value, "startWithDio": self.multiworld.start_with_revive[self.player].value, - "FinalStageDeath": self.multiworld.final_stage_death[self.player].value + "finalStageDeath": self.multiworld.final_stage_death[self.player].value, + "deathLink": self.multiworld.death_link[self.player].value, } def create_item(self, name: str) -> Item: item_id = item_table[name] + classification = ItemClassification.filler if name == "Dio's Best Friend": classification = ItemClassification.progression - elif name in {"Equipment", "Legendary Item"}: + elif name in {"Legendary Item", "Boss Item"}: classification = ItemClassification.useful - else: - classification = ItemClassification.filler + elif name == "Lunar Item": + classification = ItemClassification.trap + + # Only check for an item to be a environment unlock if those are known to be in the pool. + # This should shave down comparions. + + elif name in environment_ALL_table.keys(): + if name in {"Void Fields", "Hidden Realm: Bazaar Between Time", "Hidden Realm: Bulwark's Ambry", + "Hidden Realm: Gilded Coast,"}: + classification = ItemClassification.useful + else: + classification = ItemClassification.progression + item = RiskOfRainItem(name, classification, item_id, self.player) return item @@ -135,23 +232,31 @@ def create_events(world: MultiWorld, player: int) -> None: if total_locations / 25 == num_of_events: num_of_events -= 1 world_region = world.get_region("Petrichor V", player) + if world.goal[player] == "classic": + # only setup Pickups when using classic_mode + for i in range(num_of_events): + event_loc = RiskOfRainLocation(player, f"Pickup{(i + 1) * 25}", None, world_region) + event_loc.place_locked_item(RiskOfRainItem(f"Pickup{(i + 1) * 25}", ItemClassification.progression, None, player)) + event_loc.access_rule = \ + lambda state, i=i: state.can_reach(f"ItemPickup{((i + 1) * 25) - 1}", "Location", player) + world_region.locations.append(event_loc) + elif world.goal[player] == "explore": + for n in range(1, 6): - for i in range(num_of_events): - event_loc = RiskOfRainLocation(player, f"Pickup{(i + 1) * 25}", None, world_region) - event_loc.place_locked_item(RiskOfRainItem(f"Pickup{(i + 1) * 25}", ItemClassification.progression, None, player)) - event_loc.access_rule(lambda state, i=i: state.can_reach(f"ItemPickup{((i + 1) * 25) - 1}", player)) - world_region.locations.append(event_loc) + event_region = world.get_region(f"OrderedStage_{n}", player) + event_loc = RiskOfRainLocation(player, f"Stage_{n}", None, event_region) + event_loc.place_locked_item(RiskOfRainItem(f"Stage_{n}", ItemClassification.progression, None, player)) + event_loc.show_in_spoiler = False + event_region.locations.append(event_loc) - victory_event = RiskOfRainLocation(player, "Victory", None, world_region) + victory_region = world.get_region("Victory", player) + victory_event = RiskOfRainLocation(player, "Victory", None, victory_region) victory_event.place_locked_item(RiskOfRainItem("Victory", ItemClassification.progression, None, player)) world_region.locations.append(victory_event) -def create_region(world: MultiWorld, player: int, name: str, locations: List[str] = None) -> Region: +def create_region(world: MultiWorld, player: int, name: str, locations: Dict[str, int] = {}) -> Region: ret = Region(name, RegionType.Generic, name, player, world) - if locations: - for location in locations: - loc_id = item_pickups[location] - location = RiskOfRainLocation(player, location, loc_id, ret) - ret.locations.append(location) + for location_name, location_id in locations.items(): + ret.locations.append(RiskOfRainLocation(player, location_name, location_id, ret)) return ret diff --git a/worlds/ror2/docs/en_Risk of Rain 2.md b/worlds/ror2/docs/en_Risk of Rain 2.md index a58269a35b..f2f15fac53 100644 --- a/worlds/ror2/docs/en_Risk of Rain 2.md +++ b/worlds/ror2/docs/en_Risk of Rain 2.md @@ -12,19 +12,34 @@ 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. +There are two modes in risk of rain. Classic Mode and Explore Mode + +Classic Mode: + + - Classic mode implements pure multiworld +functionality in which certain chests (made clear via a location check progress bar) will send an item out to the +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. + +Explore Mode: + + - Just like in Classic mode chests will send out an item to the multiworld. The difference is that each environment + will have a set amount that can be sent out and shrines along with other things that will need to be checked. + Also, each environment is an item and, you'll need it to be able to access it. + ## 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. +Just like in the original game, any way to "beat the game or obliterate" counts as a win. There is a setting that +if you die while on a final boss stage, that also counts as a win.(You can turn this on in your player settings.) +**You do not need to complete all the location checks** to win; any item you don't collect may be released if the +server options allow. 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. +Simulacrum. So you could, for example, collect most of your items through a Simulacrum run(only works in classic mode), +then finish a normal mode run while keeping the items you received via the multiworld. ## Can you play multiplayer? @@ -38,6 +53,8 @@ settings apply, so each Risk of Rain 2 player slot in the multiworld needs to be 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. +Explore mode is untested in multiplayer and will likely not work until a later release. + ## What Risk of Rain items can appear in other players' worlds? The Risk of Rain items are: @@ -49,6 +66,7 @@ The Risk of Rain items are: * `Lunar Item` (Blue items) * `Equipment` (Orange items) * `Dio's Best Friend` (Used if you set the YAML setting `total_revives_available` above `0`) +* `Void Item` (Purple items) (needs dlc_sotv: enabled) Each item grants you a random in-game item from the category it belongs to. @@ -57,6 +75,29 @@ 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.) +Explore Mode items are: + +* `Titanic Plains (1)`, `Titanic Plains (2)`, `Distant Roost (1)`, `Distant Roost (2)` +* `Abandoned Aqueduct`, `Wetland Aspect` +* `Rallypoint Delta`, `Scorched Acres` +* `Abyssal Depths`, `Siren's Call`, `Sundered Grove` +* `Sky Meadow` +* `Commencement` +* `All the Hidden Realms` + +Dlc_Sotv items +* `Siphoned Forest` +* `Aphelian Sanctuary` +* `Sulfur Pools` +* `Void Locus` + +When a explore item is granted it will unlock that environment and will now be accessible to progress to victory! The +game will still pick randomly which environment is next but it will first check to see if they are available. If you have +them unlocked it will weight the game to have a ***higher chance*** to go to one you have checks versus one you have +already completed. You will still not be able to goto a stage 3 environment from a stage 1 environment. + + + ### 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 @@ -65,6 +106,10 @@ to 250** items. The number of items will be randomized between all players, so y 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.) +In explore mode the amount of checks base on how many **chests, shrines, scavengers, radio scanners and, newt altars** +are in the pool. With just the base game the numbers are **52 to 516** and with the dlc its **60 to 660** with +everything on default being **216** + 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. From ff175008a17071c88721b08d717048fda4294ad1 Mon Sep 17 00:00:00 2001 From: Jarno Date: Sun, 5 Feb 2023 22:06:38 +0100 Subject: [PATCH 15/47] Core: Phase out Print packets (#1364) --- MultiServer.py | 75 +++++++++++++++------------------------- docs/network protocol.md | 15 ++------ 2 files changed, 30 insertions(+), 60 deletions(-) diff --git a/MultiServer.py b/MultiServer.py index 98d0905bbc..e715191925 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -41,7 +41,6 @@ from NetUtils import Endpoint, ClientStatus, NetworkItem, decode, encode, Networ SlotType min_client_version = Version(0, 1, 6) -print_command_compatability_threshold = Version(0, 3, 5) # Remove backwards compatibility around 0.3.7 colorama.init() @@ -309,6 +308,10 @@ class Context: endpoints = (endpoint for endpoint in self.endpoints if endpoint.auth) async_start(self.broadcast_send_encoded_msgs(endpoints, msgs)) + def broadcast_text_all(self, text: str, additional_arguments: dict = {}): + logging.info("Notice (all): %s" % text) + self.broadcast_all([{**{"cmd": "PrintJSON", "data": [{ "text": text }]}, **additional_arguments}]) + def broadcast_team(self, team: int, msgs: typing.List[dict]): msgs = self.dumper(msgs) endpoints = (endpoint for endpoint in itertools.chain.from_iterable(self.clients[team].values())) @@ -325,29 +328,18 @@ class Context: self.clients[endpoint.team][endpoint.slot].remove(endpoint) await on_client_disconnected(self, endpoint) - # text - - def notify_all(self, text: str): - logging.info("Notice (all): %s" % text) - broadcast_text_all(self, text) def notify_client(self, client: Client, text: str): if not client.auth: return logging.info("Notice (Player %s in team %d): %s" % (client.name, client.team + 1, text)) - if client.version >= print_command_compatability_threshold: - async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }]}])) - else: - async_start(self.send_msgs(client, [{"cmd": "Print", "text": text}])) + async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }]}])) + def notify_client_multiple(self, client: Client, texts: typing.List[str]): if not client.auth: return - if client.version >= print_command_compatability_threshold: - async_start(self.send_msgs(client, - [{"cmd": "PrintJSON", "data": [{ "text": text }]} for text in texts])) - else: - async_start(self.send_msgs(client, [{"cmd": "Print", "text": text} for text in texts])) + async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }]} for text in texts])) # loading @@ -685,7 +677,7 @@ class Context: def on_goal_achieved(self, client: Client): finished_msg = f'{self.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1})' \ f' has completed their goal.' - self.notify_all(finished_msg) + self.broadcast_text_all(finished_msg) if "auto" in self.collect_mode: collect_player(self, client.team, client.slot) if "auto" in self.release_mode: @@ -778,7 +770,7 @@ async def on_client_joined(ctx: Context, client: Client): update_client_status(ctx, client, ClientStatus.CLIENT_CONNECTED) version_str = '.'.join(str(x) for x in client.version) verb = "tracking" if "Tracker" in client.tags else "playing" - ctx.notify_all( + ctx.broadcast_text_all( f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) " f"{verb} {ctx.games[client.slot]} has joined. " f"Client({version_str}), {client.tags}).") @@ -791,42 +783,26 @@ async def on_client_joined(ctx: Context, client: Client): async def on_client_left(ctx: Context, client: Client): update_client_status(ctx, client, ClientStatus.CLIENT_UNKNOWN) - ctx.notify_all( + ctx.broadcast_text_all( "%s (Team #%d) has left the game" % (ctx.get_aliased_name(client.team, client.slot), client.team + 1)) ctx.client_connection_timers[client.team, client.slot] = datetime.datetime.now(datetime.timezone.utc) async def countdown(ctx: Context, timer: int): - broadcast_countdown(ctx, timer, f"[Server]: Starting countdown of {timer}s") + ctx.broadcast_text_all(f"[Server]: Starting countdown of {timer}s", {"type": "Countdown", "countdown": timer}) if ctx.countdown_timer: ctx.countdown_timer = timer # timer is already running, set it to a different time else: ctx.countdown_timer = timer while ctx.countdown_timer > 0: - broadcast_countdown(ctx, ctx.countdown_timer, f"[Server]: {ctx.countdown_timer}") + ctx.broadcast_text_all(f"[Server]: {ctx.countdown_timer}", + {"type": "Countdown", "countdown": ctx.countdown_timer}) ctx.countdown_timer -= 1 await asyncio.sleep(1) - broadcast_countdown(ctx, 0, f"[Server]: GO") + ctx.broadcast_text_all(f"[Server]: GO", {"type": "Countdown", "countdown": 0}) ctx.countdown_timer = 0 -def broadcast_text_all(ctx: Context, text: str, additional_arguments: dict = {}): - old_clients, new_clients = [], [] - - for teams in ctx.clients.values(): - for clients in teams.values(): - for client in clients: - new_clients.append(client) if client.version >= print_command_compatability_threshold \ - else old_clients.append(client) - - ctx.broadcast(old_clients, [{"cmd": "Print", "text": text }]) - ctx.broadcast(new_clients, [{**{"cmd": "PrintJSON", "data": [{ "text": text }]}, **additional_arguments}]) - - -def broadcast_countdown(ctx: Context, timer: int, message: str): - broadcast_text_all(ctx, message, {"type": "Countdown", "countdown": timer}) - - def get_players_string(ctx: Context): auth_clients = {(c.team, c.slot) for c in ctx.endpoints if c.auth} @@ -894,7 +870,9 @@ def update_checked_locations(ctx: Context, team: int, slot: int): def release_player(ctx: Context, team: int, slot: int): """register any locations that are in the multidata""" all_locations = set(ctx.locations[slot]) - ctx.notify_all("%s (Team #%d) has released all remaining items from their world." % (ctx.player_names[(team, slot)], team + 1)) + ctx.broadcast_text_all( + "%s (Team #%d) has released all remaining items from their world." % + (ctx.player_names[(team, slot)], team + 1)) register_location_checks(ctx, team, slot, all_locations) update_checked_locations(ctx, team, slot) @@ -907,7 +885,8 @@ def collect_player(ctx: Context, team: int, slot: int, is_group: bool = False): if values[1] == slot: all_locations[source_slot].add(location_id) - ctx.notify_all("%s (Team #%d) has collected their items from other worlds." % (ctx.player_names[(team, slot)], team + 1)) + ctx.broadcast_text_all( + "%s (Team #%d) has collected their items from other worlds." % (ctx.player_names[(team, slot)], team + 1)) for source_player, location_ids in all_locations.items(): register_location_checks(ctx, team, source_player, location_ids, count_activity=False) update_checked_locations(ctx, team, source_player) @@ -1177,7 +1156,7 @@ class ClientMessageProcessor(CommonCommandProcessor): def __call__(self, raw: str) -> typing.Optional[bool]: if not raw.startswith("!admin"): - self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + raw) + self.ctx.broadcast_text_all(self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + raw) return super(ClientMessageProcessor, self).__call__(raw) def output(self, text): @@ -1205,8 +1184,8 @@ class ClientMessageProcessor(CommonCommandProcessor): "!admin /option server_password"): output = f"!admin /option server_password {('*' * random.randint(4, 16))}" # Otherwise notify the others what is happening. - self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team, - self.client.slot) + ': ' + output) + self.ctx.broadcast_text_all( + self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + output) if not self.ctx.server_password: self.output("Sorry, Remote administration is disabled") @@ -1243,7 +1222,7 @@ class ClientMessageProcessor(CommonCommandProcessor): def _cmd_players(self) -> bool: """Get information about connected and missing players.""" if len(self.ctx.player_names) < 10: - self.ctx.notify_all(get_players_string(self.ctx)) + self.ctx.broadcast_text_all(get_players_string(self.ctx)) else: self.output(get_players_string(self.ctx)) return True @@ -1381,7 +1360,7 @@ class ClientMessageProcessor(CommonCommandProcessor): new_item = NetworkItem(names[item_name], -1, self.client.slot) get_received_items(self.ctx, self.client.team, self.client.slot, False).append(new_item) get_received_items(self.ctx, self.client.team, self.client.slot, True).append(new_item) - self.ctx.notify_all( + self.ctx.broadcast_text_all( 'Cheat console: sending "' + item_name + '" to ' + self.ctx.get_aliased_name(self.client.team, self.client.slot)) send_new_items(self.ctx) @@ -1682,7 +1661,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): client.tags = args["tags"] if set(old_tags) != set(client.tags): client.no_locations = 'TextOnly' in client.tags or 'Tracker' in client.tags - ctx.notify_all( + ctx.broadcast_text_all( f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) has changed tags " f"from {old_tags} to {client.tags}.") @@ -1806,7 +1785,7 @@ class ServerCommandProcessor(CommonCommandProcessor): super(ServerCommandProcessor, self).output(text) def default(self, raw: str): - self.ctx.notify_all('[Server]: ' + raw) + self.ctx.broadcast_text_all('[Server]: ' + raw) def _cmd_save(self) -> bool: """Save current state to multidata""" @@ -1947,7 +1926,7 @@ class ServerCommandProcessor(CommonCommandProcessor): send_items_to(self.ctx, team, slot, *new_items) send_new_items(self.ctx) - self.ctx.notify_all( + self.ctx.broadcast_text_all( 'Cheat console: sending ' + ('' if amount == 1 else f'{amount} of ') + f'"{item_name}" to {self.ctx.get_aliased_name(team, slot)}') return True diff --git a/docs/network protocol.md b/docs/network protocol.md index ff50351507..f743e474fb 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -9,7 +9,7 @@ These steps should be followed in order to establish a gameplay connection with 5. Client sends [Connect](#Connect) packet in order to authenticate with the server. 6. Server validates the client's packet and responds with [Connected](#Connected) or [ConnectionRefused](#ConnectionRefused). 7. Server may send [ReceivedItems](#ReceivedItems) to the client, in the case that the client is missing items that are queued up for it. -8. Server sends [Print](#Print) to all players to notify them of the new client connection. +8. Server sends [PrintJSON](#PrintJSON) to all players to notify them of updates. In the case that the client does not authenticate properly and receives a [ConnectionRefused](#ConnectionRefused) then the server will maintain the connection and allow for follow-up [Connect](#Connect) packet. @@ -54,7 +54,6 @@ These packets are are sent from the multiworld server to the client. They are no * [ReceivedItems](#ReceivedItems) * [LocationInfo](#LocationInfo) * [RoomUpdate](#RoomUpdate) -* [Print](#Print) * [PrintJSON](#PrintJSON) * [DataPackage](#DataPackage) * [Bounced](#Bounced) @@ -160,16 +159,8 @@ The arguments for RoomUpdate are identical to [RoomInfo](#RoomInfo) barring: All arguments for this packet are optional, only changes are sent. -### Print -Sent to clients purely to display a message to the player. -* *Deprecation warning: clients that connect with version 0.3.5 or higher will nolonger recieve Print packets, instead all messsages are send as [PrintJSON](#PrintJSON)* -#### Arguments -| Name | Type | Notes | -| ---- | ---- | ----- | -| text | str | Message to display to player. | - ### PrintJSON -Sent to clients purely to display a message to the player. This packet differs from [Print](#Print) in that the data being sent with this packet allows for more configurable or specific messaging. +Sent to clients purely to display a message to the player. The data being sent with this packet allows for configurability or specific messaging. #### Arguments | Name | Type | Notes | | ---- | ---- | ----- | @@ -181,7 +172,7 @@ Sent to clients purely to display a message to the player. This packet differs f | countdown | int | Is present if type is `Countdown`, denotes the amount of seconds remaining on the countdown. | ##### PrintJsonType -PrintJsonType indicates the type of [PrintJson](#PrintJson) packet, different types can be handled differently by the client and can also contain additional arguments. When receiving an unknown type the data's list\[[JSONMessagePart](#JSONMessagePart)\] should still be printed as normal. +PrintJsonType indicates the type of [PrintJson](#PrintJson) packet, different types can be handled differently by the client and can also contain additional arguments. When receiving an unknown or missing type the data's list\[[JSONMessagePart](#JSONMessagePart)\] should still be displayed to the player as normal text. Currently defined types are: | Type | Notes | From 6148213e4372726fae85404428785ff4363c1a66 Mon Sep 17 00:00:00 2001 From: beauxq Date: Sun, 5 Feb 2023 22:37:18 -0800 Subject: [PATCH 16/47] Zillion: fix name data overflow --- worlds/zillion/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/zillion/requirements.txt b/worlds/zillion/requirements.txt index 964016fd3f..e92bea1945 100644 --- a/worlds/zillion/requirements.txt +++ b/worlds/zillion/requirements.txt @@ -1 +1 @@ -git+https://github.com/beauxq/zilliandomizer@23d938daa5aaeaa3ec2255c50f2729b33a9e7f87#egg=zilliandomizer==0.5.1 +git+https://github.com/beauxq/zilliandomizer@cd6a940ad7b585c75a560b91468d6b9eee030559#egg=zilliandomizer==0.5.2 From c360b9266ceec03002fdf577dae7879e975238dc Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 7 Feb 2023 03:12:47 +0100 Subject: [PATCH 17/47] Witness: Renaming: Mill -> Stoneworks, correcting order for 'First, Second, ...', removed all instances of 'Door (Door)' (#1435) --- worlds/witness/WitnessItems.txt | 38 ++++++++-------- worlds/witness/WitnessLogic.txt | 30 ++++++------- worlds/witness/WitnessLogicExpert.txt | 30 ++++++------- worlds/witness/WitnessLogicVanilla.txt | 30 ++++++------- worlds/witness/__init__.py | 2 +- worlds/witness/locations.py | 18 ++++---- worlds/witness/player_logic.py | 8 ++-- .../witness/settings/Door_Panel_Shuffle.txt | 6 +-- worlds/witness/settings/Doors_Complex.txt | 40 ++++++++--------- worlds/witness/settings/Doors_Max.txt | 44 +++++++++---------- worlds/witness/settings/Doors_Simple.txt | 16 +++---- 11 files changed, 131 insertions(+), 131 deletions(-) diff --git a/worlds/witness/WitnessItems.txt b/worlds/witness/WitnessItems.txt index e85e7c50db..8e5d9383b4 100644 --- a/worlds/witness/WitnessItems.txt +++ b/worlds/witness/WitnessItems.txt @@ -35,9 +35,9 @@ Doors: 1107 - Symmetry Island Upper (Panel) - 0x1C349 1110 - Desert Light Room Entry (Panel) - 0x0C339 1111 - Desert Flood Controls (Panel) - 0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B -1119 - Quarry Mill Entry (Panel) - 0x01E5A,0x01E59 -1120 - Quarry Mill Ramp Controls (Panel) - 0x03678,0x03676 -1122 - Quarry Mill Lift Controls (Panel) - 0x03679,0x03675 +1119 - Quarry Stoneworks Entry (Panel) - 0x01E5A,0x01E59 +1120 - Quarry Stoneworks Ramp Controls (Panel) - 0x03678,0x03676 +1122 - Quarry Stoneworks Lift 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 @@ -90,14 +90,14 @@ Doors: 1630 - Desert Elevator Room Entry (Door) - 0x0C316 1633 - Quarry Entry 1 (Door) - 0x09D6F 1636 - Quarry Entry 2 (Door) - 0x17C07 -1639 - Quarry Mill Entry (Door) - 0x02010 -1642 - Quarry Mill Side Exit (Door) - 0x275FF -1645 - Quarry Mill Roof Exit (Door) - 0x17CE8 -1648 - Quarry Mill Stairs (Door) - 0x0368A +1639 - Quarry Stoneworks Entry (Door) - 0x02010 +1642 - Quarry Stoneworks Side Exit (Door) - 0x275FF +1645 - Quarry Stoneworks Roof Exit (Door) - 0x17CE8 +1648 - Quarry Stoneworks Stairs (Door) - 0x0368A 1651 - Quarry Boathouse Dock (Door) - 0x2769B,0x27163 1653 - Quarry Boathouse First Barrier (Door) - 0x17C50 1654 - Quarry Boathouse Second Barrier (Door) - 0x3865F -1656 - Shadows Timed Door (Door) - 0x19B24 +1656 - Shadows Timed Door - 0x19B24 1657 - Shadows Laser Entry Right (Door) - 0x194B2 1660 - Shadows Laser Entry Left (Door) - 0x19665 1663 - Shadows Quarry Barrier (Door) - 0x19865,0x0A2DF @@ -121,15 +121,15 @@ Doors: 1717 - Monastery Garden Entry (Door) - 0x03750 1718 - Town Cargo Box Entry (Door) - 0x0A0C9 1720 - Town Wooden Roof Stairs (Door) - 0x034F5 -1723 - Town Tinted Glass Door (Door) - 0x28A61 +1723 - Town Tinted Glass Door - 0x28A61 1726 - Town Church Entry (Door) - 0x03BB0 1729 - Town Maze Stairs (Door) - 0x28AA2 1732 - Town Windmill Entry (Door) - 0x1845B 1735 - Town RGB House Stairs (Door) - 0x2897B -1738 - Town Tower First Door (Door) - 0x27798 -1741 - Town Tower Third Door (Door) - 0x27799 -1744 - Town Tower Fourth Door (Door) - 0x2779A -1747 - Town Tower Second Door (Door) - 0x2779C +1738 - Town Tower Second (Door) - 0x27798 +1741 - Town Tower First (Door) - 0x27799 +1744 - Town Tower Fourth (Door) - 0x2779A +1747 - Town Tower Third (Door) - 0x2779C 1750 - Theater Entry (Door) - 0x17F88 1753 - Theater Exit Left (Door) - 0x0A16D 1756 - Theater Exit Right (Door) - 0x3CCDF @@ -137,7 +137,7 @@ Doors: 1760 - Jungle Popup Wall (Door) - 0x1475B 1762 - River Monastery Shortcut (Door) - 0x0CF2A 1765 - Bunker Entry (Door) - 0x0C2A4 -1768 - Bunker Tinted Glass Door (Door) - 0x17C79 +1768 - Bunker Tinted Glass Door - 0x17C79 1771 - Bunker UV Room Entry (Door) - 0x0C2A3 1774 - Bunker Elevator Room Entry (Door) - 0x0A08D 1777 - Swamp Entry (Door) - 0x00C1C @@ -150,9 +150,9 @@ Doors: 1798 - Swamp Blue Water Pump (Door) - 0x18482 1801 - Swamp Purple Water Pump (Door) - 0x0A1D6 1804 - Swamp Laser Shortcut (Door) - 0x2D880 -1807 - Treehouse First Door (Door) - 0x0C309 -1810 - Treehouse Second Door (Door) - 0x0C310 -1813 - Treehouse Third Door (Door) - 0x0A181 +1807 - Treehouse First (Door) - 0x0C309 +1810 - Treehouse Second (Door) - 0x0C310 +1813 - Treehouse Third (Door) - 0x0A181 1816 - Treehouse Drawbridge (Door) - 0x0C32D 1819 - Treehouse Laser House Entry (Door) - 0x0C323 1822 - Mountain Floor 1 Exit (Door) - 0x09E54 @@ -163,7 +163,7 @@ Doors: 1840 - Mountain Bottom Floor Final Room Entry (Door) - 0x0C141 1843 - Mountain Bottom Floor Rock (Door) - 0x17F33 1846 - Caves Entry (Door) - 0x2D77D -1849 - Caves Pillar Door (Door) - 0x019A5 +1849 - Caves Pillar Door - 0x019A5 1855 - Caves Swamp Shortcut (Door) - 0x2D859 1858 - Challenge Entry (Door) - 0x0A19A 1861 - Challenge Tunnels Entry (Door) - 0x0348A @@ -176,7 +176,7 @@ Doors: 1909 - Orchard Gates - 0x03313,0x03307 1912 - Desert Doors - 0x09FEE,0x0C2C3,0x0A24B,0x0C316 1915 - Quarry Main Entry - 0x09D6F,0x17C07 -1918 - Quarry Mill Shortcuts - 0x17CE8,0x0368A,0x275FF +1918 - Quarry Stoneworks Shortcuts - 0x17CE8,0x0368A,0x275FF 1921 - Quarry Boathouse Barriers - 0x17C50,0x3865F 1924 - Shadows Laser Room Door - 0x194B2,0x19665 1927 - Shadows Barriers - 0x19865,0x0A2DF,0x1855B,0x19ADE diff --git a/worlds/witness/WitnessLogic.txt b/worlds/witness/WitnessLogic.txt index 85bf7d40cb..308cba385f 100644 --- a/worlds/witness/WitnessLogic.txt +++ b/worlds/witness/WitnessLogic.txt @@ -230,19 +230,19 @@ Quarry Between Entrys (Quarry) - Quarry - 0x17C07: 158119 - 0x17C09 (Entry 2 Panel) - True - Shapers Door - 0x17C07 (Entry 2) - 0x17C09 -Quarry (Quarry) - Quarry Mill Ground Floor - 0x02010 - Quarry Elevator - 0x17CC4: -158121 - 0x01E5A (Mill Entry Left Panel) - True - Black/White Squares -158122 - 0x01E59 (Mill Entry Right Panel) - True - Dots -Door - 0x02010 (Mill Entry) - 0x01E59 & 0x01E5A +Quarry (Quarry) - Quarry Stoneworks Ground Floor - 0x02010 - Quarry Elevator - 0x17CC4: +158121 - 0x01E5A (Stoneworks Entry Left Panel) - True - Black/White Squares +158122 - 0x01E59 (Stoneworks Entry Right Panel) - True - Dots +Door - 0x02010 (Stoneworks Entry) - 0x01E59 & 0x01E5A -Quarry Mill Ground Floor (Quarry Mill) - Quarry - 0x275FF - Quarry Mill Middle Floor - 0x03678 - Outside Quarry - 0x17CE8: +Quarry Stoneworks Ground Floor (Quarry Stoneworks) - Quarry - 0x275FF - Quarry Stoneworks Middle Floor - 0x03678 - Outside Quarry - 0x17CE8: 158123 - 0x275ED (Side Exit Panel) - True - True Door - 0x275FF (Side Exit) - 0x275ED 158124 - 0x03678 (Lower Ramp Control) - True - Dots & Eraser 158145 - 0x17CAC (Roof Exit Panel) - True - True Door - 0x17CE8 (Roof Exit) - 0x17CAC -Quarry Mill Middle Floor (Quarry Mill) - Quarry Mill Ground Floor - 0x03675 - Quarry Mill Upper Floor - 0x03679: +Quarry Stoneworks Middle Floor (Quarry Stoneworks) - Quarry Stoneworks Ground Floor - 0x03675 - Quarry Stoneworks Upper Floor - 0x03679: 158125 - 0x00E0C (Lower Row 1) - True - Dots & Eraser 158126 - 0x01489 (Lower Row 2) - 0x00E0C - Dots & Eraser 158127 - 0x0148A (Lower Row 3) - 0x01489 - Dots & Eraser @@ -251,7 +251,7 @@ Quarry Mill Middle Floor (Quarry Mill) - Quarry Mill Ground Floor - 0x03675 - Qu 158130 - 0x014E8 (Lower Row 6) - 0x014E7 - Dots & Eraser 158131 - 0x03679 (Lower Lift Control) - 0x014E8 - Dots & Eraser -Quarry Mill Upper Floor (Quarry Mill) - Quarry Mill Middle Floor - 0x03676 & 0x03679 - Quarry Mill Ground Floor - 0x0368A: +Quarry Stoneworks Upper Floor (Quarry Stoneworks) - Quarry Stoneworks Middle Floor - 0x03676 & 0x03679 - Quarry Stoneworks Ground Floor - 0x0368A: 158132 - 0x03676 (Upper Ramp Control) - True - Dots & Eraser 158133 - 0x03675 (Upper Lift Control) - True - Dots & Eraser 158134 - 0x00557 (Upper Row 1) - True - Colored Squares & Eraser @@ -330,7 +330,7 @@ Door - 0x194B2 (Laser Entry Right) - 0x1972F 158185 - 0x197E8 (Near 4) - 0x197E0 - True 158186 - 0x197E5 (Near 5) - 0x197E8 - True Door - 0x19665 (Laser Entry Left) - 0x197E5 -159400 - 0x28A7B (Quarry Mill Rooftop Vent EP) - True - True +159400 - 0x28A7B (Quarry Stoneworks Rooftop Vent EP) - True - True Shadows Ledge (Shadows) - Shadows - 0x1855B - Quarry - 0x19865 & 0x0A2DF: 158187 - 0x334DC (Door Timer Inside) - True - True @@ -480,10 +480,10 @@ Door - 0x1845B (Windmill Entry) - 0x17F5F 159010 - 0x037B6 (Windmill First Blade EP) - 0x17D02 - True 159011 - 0x037B2 (Windmill Second Blade EP) - 0x17D02 - True 159012 - 0x000F7 (Windmill Third Blade EP) - 0x17D02 - True -159540 - 0x03335 (Tower Underside First EP) - True - True -159541 - 0x03412 (Tower Underside Second EP) - True - True -159542 - 0x038A6 (Tower Underside Third EP) - True - True -159543 - 0x038AA (Tower Underside Fourth EP) - True - True +159540 - 0x03335 (Tower Underside Third EP) - True - True +159541 - 0x03412 (Tower Underside Fourth EP) - True - True +159542 - 0x038A6 (Tower Underside First EP) - True - True +159543 - 0x038AA (Tower Underside Second EP) - True - True 159545 - 0x03E40 (RGB House Green EP) - 0x334D8 - True 159546 - 0x28B8E (Maze Bridge Underside EP) - 0x2896A - True 159552 - 0x03BCF (Black Line Redirect EP) - True - True @@ -523,9 +523,9 @@ RGB Room (Town): 158246 - 0x03C08 (RGB Room Right) - 0x334D8 - Stars Town Tower (Town Tower) - Town - True - Town Tower Top - 0x27798 & 0x27799 & 0x2779A & 0x2779C: -Door - 0x27798 (First Door) - 0x28ACC -Door - 0x2779C (Second Door) - 0x28AD9 -Door - 0x27799 (Third Door) - 0x28A69 +Door - 0x27798 (Second Door) - 0x28ACC +Door - 0x2779C (Third Door) - 0x28AD9 +Door - 0x27799 (First Door) - 0x28A69 Door - 0x2779A (Fourth Door) - 0x28B39 Town Tower Top (Town): diff --git a/worlds/witness/WitnessLogicExpert.txt b/worlds/witness/WitnessLogicExpert.txt index aecf5f04ce..f100a3095b 100644 --- a/worlds/witness/WitnessLogicExpert.txt +++ b/worlds/witness/WitnessLogicExpert.txt @@ -230,19 +230,19 @@ Quarry Between Entrys (Quarry) - Quarry - 0x17C07: 158119 - 0x17C09 (Entry 2 Panel) - True - Shapers & Triangles Door - 0x17C07 (Entry 2) - 0x17C09 -Quarry (Quarry) - Quarry Mill Ground Floor - 0x02010 - Quarry Elevator - 0x17CC4: -158121 - 0x01E5A (Mill Entry Left Panel) - True - Squares & Black/White Squares & Stars & Stars + Same Colored Symbol -158122 - 0x01E59 (Mill Entry Right Panel) - True - Triangles -Door - 0x02010 (Mill Entry) - 0x01E59 & 0x01E5A +Quarry (Quarry) - Quarry Stoneworks Ground Floor - 0x02010 - Quarry Elevator - 0x17CC4: +158121 - 0x01E5A (Stoneworks Entry Left Panel) - True - Squares & Black/White Squares & Stars & Stars + Same Colored Symbol +158122 - 0x01E59 (Stoneworks Entry Right Panel) - True - Triangles +Door - 0x02010 (Stoneworks Entry) - 0x01E59 & 0x01E5A -Quarry Mill Ground Floor (Quarry Mill) - Quarry - 0x275FF - Quarry Mill Middle Floor - 0x03678 - Outside Quarry - 0x17CE8: +Quarry Stoneworks Ground Floor (Quarry Stoneworks) - Quarry - 0x275FF - Quarry Stoneworks Middle Floor - 0x03678 - Outside Quarry - 0x17CE8: 158123 - 0x275ED (Side Exit Panel) - True - True Door - 0x275FF (Side Exit) - 0x275ED 158124 - 0x03678 (Lower Ramp Control) - True - Dots & Eraser 158145 - 0x17CAC (Roof Exit Panel) - True - True Door - 0x17CE8 (Roof Exit) - 0x17CAC -Quarry Mill Middle Floor (Quarry Mill) - Quarry Mill Ground Floor - 0x03675 - Quarry Mill Upper Floor - 0x03679: +Quarry Stoneworks Middle Floor (Quarry Stoneworks) - Quarry Stoneworks Ground Floor - 0x03675 - Quarry Stoneworks Upper Floor - 0x03679: 158125 - 0x00E0C (Lower Row 1) - True - Dots & Eraser 158126 - 0x01489 (Lower Row 2) - 0x00E0C - Triangles & Eraser 158127 - 0x0148A (Lower Row 3) - 0x01489 - Triangles & Eraser @@ -251,7 +251,7 @@ Quarry Mill Middle Floor (Quarry Mill) - Quarry Mill Ground Floor - 0x03675 - Qu 158130 - 0x014E8 (Lower Row 6) - 0x014E7 - Triangles & Eraser 158131 - 0x03679 (Lower Lift Control) - 0x014E8 - Dots & Eraser -Quarry Mill Upper Floor (Quarry Mill) - Quarry Mill Middle Floor - 0x03676 & 0x03679 - Quarry Mill Ground Floor - 0x0368A: +Quarry Stoneworks Upper Floor (Quarry Stoneworks) - Quarry Stoneworks Middle Floor - 0x03676 & 0x03679 - Quarry Stoneworks Ground Floor - 0x0368A: 158132 - 0x03676 (Upper Ramp Control) - True - Dots & Eraser 158133 - 0x03675 (Upper Lift Control) - True - Dots & Eraser 158134 - 0x00557 (Upper Row 1) - True - Squares & Colored Squares & Eraser & Stars & Stars + Same Colored Symbol @@ -330,7 +330,7 @@ Door - 0x194B2 (Laser Entry Right) - 0x1972F 158185 - 0x197E8 (Near 4) - 0x197E0 - True 158186 - 0x197E5 (Near 5) - 0x197E8 - True Door - 0x19665 (Laser Entry Left) - 0x197E5 -159400 - 0x28A7B (Quarry Mill Rooftop Vent EP) - True - True +159400 - 0x28A7B (Quarry Stoneworks Rooftop Vent EP) - True - True Shadows Ledge (Shadows) - Shadows - 0x1855B - Quarry - 0x19865 & 0x0A2DF: 158187 - 0x334DC (Door Timer Inside) - True - True @@ -480,10 +480,10 @@ Door - 0x1845B (Windmill Entry) - 0x17F5F 159010 - 0x037B6 (Windmill First Blade EP) - 0x17D02 - True 159011 - 0x037B2 (Windmill Second Blade EP) - 0x17D02 - True 159012 - 0x000F7 (Windmill Third Blade EP) - 0x17D02 - True -159540 - 0x03335 (Tower Underside First EP) - True - True -159541 - 0x03412 (Tower Underside Second EP) - True - True -159542 - 0x038A6 (Tower Underside Third EP) - True - True -159543 - 0x038AA (Tower Underside Fourth EP) - True - True +159540 - 0x03335 (Tower Underside Third EP) - True - True +159541 - 0x03412 (Tower Underside Fourth EP) - True - True +159542 - 0x038A6 (Tower Underside First EP) - True - True +159543 - 0x038AA (Tower Underside Second EP) - True - True 159545 - 0x03E40 (RGB House Green EP) - 0x334D8 & 0x03C0C & 0x03C08 - True 159546 - 0x28B8E (Maze Bridge Underside EP) - 0x2896A - True 159552 - 0x03BCF (Black Line Redirect EP) - True - True @@ -523,9 +523,9 @@ RGB Room (Town): 158246 - 0x03C08 (RGB Room Right) - 0x334D8 & 0x03C0C - Symmetry & Dots & Colored Dots & Triangles Town Tower (Town Tower) - Town - True - Town Tower Top - 0x27798 & 0x27799 & 0x2779A & 0x2779C: -Door - 0x27798 (First Door) - 0x28ACC -Door - 0x2779C (Second Door) - 0x28AD9 -Door - 0x27799 (Third Door) - 0x28A69 +Door - 0x27798 (Second Door) - 0x28ACC +Door - 0x2779C (Third Door) - 0x28AD9 +Door - 0x27799 (First Door) - 0x28A69 Door - 0x2779A (Fourth Door) - 0x28B39 Town Tower Top (Town): diff --git a/worlds/witness/WitnessLogicVanilla.txt b/worlds/witness/WitnessLogicVanilla.txt index 7b9fa884d6..294950c305 100644 --- a/worlds/witness/WitnessLogicVanilla.txt +++ b/worlds/witness/WitnessLogicVanilla.txt @@ -230,19 +230,19 @@ Quarry Between Entrys (Quarry) - Quarry - 0x17C07: 158119 - 0x17C09 (Entry 2 Panel) - True - Shapers Door - 0x17C07 (Entry 2) - 0x17C09 -Quarry (Quarry) - Quarry Mill Ground Floor - 0x02010 - Quarry Elevator - 0x17CC4: -158121 - 0x01E5A (Mill Entry Left Panel) - True - Black/White Squares -158122 - 0x01E59 (Mill Entry Right Panel) - True - Dots -Door - 0x02010 (Mill Entry) - 0x01E59 & 0x01E5A +Quarry (Quarry) - Quarry Stoneworks Ground Floor - 0x02010 - Quarry Elevator - 0x17CC4: +158121 - 0x01E5A (Stoneworks Entry Left Panel) - True - Black/White Squares +158122 - 0x01E59 (Stoneworks Entry Right Panel) - True - Dots +Door - 0x02010 (Stoneworks Entry) - 0x01E59 & 0x01E5A -Quarry Mill Ground Floor (Quarry Mill) - Quarry - 0x275FF - Quarry Mill Middle Floor - 0x03678 - Outside Quarry - 0x17CE8: +Quarry Stoneworks Ground Floor (Quarry Stoneworks) - Quarry - 0x275FF - Quarry Stoneworks Middle Floor - 0x03678 - Outside Quarry - 0x17CE8: 158123 - 0x275ED (Side Exit Panel) - True - True Door - 0x275FF (Side Exit) - 0x275ED 158124 - 0x03678 (Lower Ramp Control) - True - Dots & Eraser 158145 - 0x17CAC (Roof Exit Panel) - True - True Door - 0x17CE8 (Roof Exit) - 0x17CAC -Quarry Mill Middle Floor (Quarry Mill) - Quarry Mill Ground Floor - 0x03675 - Quarry Mill Upper Floor - 0x03679: +Quarry Stoneworks Middle Floor (Quarry Stoneworks) - Quarry Stoneworks Ground Floor - 0x03675 - Quarry Stoneworks Upper Floor - 0x03679: 158125 - 0x00E0C (Lower Row 1) - True - Dots & Eraser 158126 - 0x01489 (Lower Row 2) - 0x00E0C - Dots & Eraser 158127 - 0x0148A (Lower Row 3) - 0x01489 - Dots & Eraser @@ -251,7 +251,7 @@ Quarry Mill Middle Floor (Quarry Mill) - Quarry Mill Ground Floor - 0x03675 - Qu 158130 - 0x014E8 (Lower Row 6) - 0x014E7 - Dots & Eraser 158131 - 0x03679 (Lower Lift Control) - 0x014E8 - Dots & Eraser -Quarry Mill Upper Floor (Quarry Mill) - Quarry Mill Middle Floor - 0x03676 & 0x03679 - Quarry Mill Ground Floor - 0x0368A: +Quarry Stoneworks Upper Floor (Quarry Stoneworks) - Quarry Stoneworks Middle Floor - 0x03676 & 0x03679 - Quarry Stoneworks Ground Floor - 0x0368A: 158132 - 0x03676 (Upper Ramp Control) - True - Dots & Eraser 158133 - 0x03675 (Upper Lift Control) - True - Dots & Eraser 158134 - 0x00557 (Upper Row 1) - True - Colored Squares & Eraser @@ -330,7 +330,7 @@ Door - 0x194B2 (Laser Entry Right) - 0x1972F 158185 - 0x197E8 (Near 4) - 0x197E0 - True 158186 - 0x197E5 (Near 5) - 0x197E8 - True Door - 0x19665 (Laser Entry Left) - 0x197E5 -159400 - 0x28A7B (Quarry Mill Rooftop Vent EP) - True - True +159400 - 0x28A7B (Quarry Stoneworks Rooftop Vent EP) - True - True Shadows Ledge (Shadows) - Shadows - 0x1855B - Quarry - 0x19865 & 0x0A2DF: 158187 - 0x334DC (Door Timer Inside) - True - True @@ -480,10 +480,10 @@ Door - 0x1845B (Windmill Entry) - 0x17F5F 159010 - 0x037B6 (Windmill First Blade EP) - 0x17D02 - True 159011 - 0x037B2 (Windmill Second Blade EP) - 0x17D02 - True 159012 - 0x000F7 (Windmill Third Blade EP) - 0x17D02 - True -159540 - 0x03335 (Tower Underside First EP) - True - True -159541 - 0x03412 (Tower Underside Second EP) - True - True -159542 - 0x038A6 (Tower Underside Third EP) - True - True -159543 - 0x038AA (Tower Underside Fourth EP) - True - True +159540 - 0x03335 (Tower Underside Third EP) - True - True +159541 - 0x03412 (Tower Underside Fourth EP) - True - True +159542 - 0x038A6 (Tower Underside First EP) - True - True +159543 - 0x038AA (Tower Underside Second EP) - True - True 159545 - 0x03E40 (RGB House Green EP) - 0x334D8 - True 159546 - 0x28B8E (Maze Bridge Underside EP) - 0x2896A - True 159552 - 0x03BCF (Black Line Redirect EP) - True - True @@ -523,9 +523,9 @@ RGB Room (Town): 158246 - 0x03C08 (RGB Room Right) - 0x334D8 - Stars Town Tower (Town Tower) - Town - True - Town Tower Top - 0x27798 & 0x27799 & 0x2779A & 0x2779C: -Door - 0x27798 (First Door) - 0x28ACC -Door - 0x2779C (Second Door) - 0x28AD9 -Door - 0x27799 (Third Door) - 0x28A69 +Door - 0x27798 (Second Door) - 0x28ACC +Door - 0x2779C (Third Door) - 0x28AD9 +Door - 0x27799 (First Door) - 0x28A69 Door - 0x2779A (Fourth Door) - 0x28B39 Town Tower Top (Town): diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index 5a288e0c8a..a5765bf714 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -38,7 +38,7 @@ class WitnessWorld(World): """ game = "The Witness" topology_present = False - data_version = 11 + data_version = 12 static_logic = StaticWitnessLogic() static_locat = StaticWitnessLocations() diff --git a/worlds/witness/locations.py b/worlds/witness/locations.py index 14291f4ee2..37f61646d1 100644 --- a/worlds/witness/locations.py +++ b/worlds/witness/locations.py @@ -48,9 +48,9 @@ class StaticWitnessLocations: "Desert Flood Room 6", "Desert Laser Panel", - "Quarry Mill Lower Row 6", - "Quarry Mill Upper Row 8", - "Quarry Mill Control Room Right", + "Quarry Stoneworks Lower Row 6", + "Quarry Stoneworks Upper Row 8", + "Quarry Stoneworks Control Room Right", "Quarry Boathouse Intro Right", "Quarry Boathouse Intro Left", "Quarry Boathouse Front Row 5", @@ -128,7 +128,7 @@ class StaticWitnessLocations: "Mountaintop River Shape", "Tutorial Patio Floor", - "Quarry Mill Control Room Left", + "Quarry Stoneworks Control Room Left", "Theater Tutorial Video", "Theater Desert Video", "Theater Jungle Video", @@ -170,11 +170,11 @@ class StaticWitnessLocations: "Quarry Rock Line EP", "Quarry Rock Line Reflection EP", "Quarry Railroad EP", - "Quarry Mill Ramp EP", - "Quarry Mill Lift EP", + "Quarry Stoneworks Ramp EP", + "Quarry Stoneworks Lift EP", "Quarry Boathouse Moving Ramp EP", "Quarry Boathouse Hook EP", - "Shadows Quarry Mill Rooftop Vent EP", + "Shadows Quarry Stoneworks Rooftop Vent EP", "Treehouse Beach Rock Shadow EP", "Treehouse Beach Sand Shadow EP", "Treehouse Beach Both Orange Bridges EP", @@ -207,10 +207,10 @@ class StaticWitnessLocations: "Town Windmill First Blade EP", "Town Windmill Second Blade EP", "Town Windmill Third Blade EP", - "Town Tower Underside First EP", - "Town Tower Underside Second EP", "Town Tower Underside Third EP", "Town Tower Underside Fourth EP", + "Town Tower Underside First EP", + "Town Tower Underside Second EP", "Town RGB House Red EP", "Town RGB House Green EP", "Town Maze Bridge Underside EP", diff --git a/worlds/witness/player_logic.py b/worlds/witness/player_logic.py index 948029ec43..34b53b62ff 100644 --- a/worlds/witness/player_logic.py +++ b/worlds/witness/player_logic.py @@ -445,7 +445,7 @@ class WitnessPlayerLogic: "0x01D3F": "Keep Laser Panel (Pressure Plates) Activates", "0x01BE9": "Keep Laser Panel (Pressure Plates) Activates - Expert", "0x09F7F": "Mountain Access", - "0x0367C": "Quarry Laser Mill Requirement Met", + "0x0367C": "Quarry Laser Stoneworks Requirement Met", "0x009A1": "Swamp Between Bridges Far 1 Activates", "0x00006": "Swamp Cyan Water Drains", "0x00990": "Swamp Between Bridges Near Row 1 Activates", @@ -469,8 +469,8 @@ class WitnessPlayerLogic: "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", + "0x03675": "Quarry Stoneworks Ramp Activation From Above", + "0x03679": "Quarry Stoneworks Lift Lowering While Standing On It", "0x2FAF6": "Tutorial Gate Secret Solution Knowledge", "0x079DF": "Town Tall Hexagonal Turns On", "0x17DA2": "Right Orange Bridge Fully Extended", @@ -499,7 +499,7 @@ class WitnessPlayerLogic: "0x033EA": "Pressure Plates 1 EP available", "0x03553": "Tutorial Video EPs availble", "0x17C79": "Bunker Door EP available", - "0x275FF": "Mill Light EPs available", + "0x275FF": "Stoneworks Light EPs available", "0x17E2B": "Remaining Purple Sand EPs available", "0x03852": "Ramp EPs requirement", "0x334D8": "RGB panels & EPs solvable", diff --git a/worlds/witness/settings/Door_Panel_Shuffle.txt b/worlds/witness/settings/Door_Panel_Shuffle.txt index 745c0b4a45..80195cfb99 100644 --- a/worlds/witness/settings/Door_Panel_Shuffle.txt +++ b/worlds/witness/settings/Door_Panel_Shuffle.txt @@ -4,9 +4,9 @@ Symmetry Island Lower (Panel) Symmetry Island Upper (Panel) Desert Light Room Entry (Panel) Desert Flood Controls (Panel) -Quarry Mill Entry (Panel) -Quarry Mill Ramp Controls (Panel) -Quarry Mill Lift Controls (Panel) +Quarry Stoneworks Entry (Panel) +Quarry Stoneworks Ramp Controls (Panel) +Quarry Stoneworks Lift Controls (Panel) Quarry Boathouse Ramp Height Control (Panel) Quarry Boathouse Ramp Horizontal Control (Panel) Shadows Door Timer (Panel) diff --git a/worlds/witness/settings/Doors_Complex.txt b/worlds/witness/settings/Doors_Complex.txt index 9d883cad2f..d8da6783b0 100644 --- a/worlds/witness/settings/Doors_Complex.txt +++ b/worlds/witness/settings/Doors_Complex.txt @@ -14,14 +14,14 @@ Desert Flood Room Entry (Door) Desert Elevator Room Entry (Door) Quarry Entry 1 (Door) Quarry Entry 2 (Door) -Quarry Mill Entry (Door) -Quarry Mill Side Exit (Door) -Quarry Mill Roof Exit (Door) -Quarry Mill Stairs (Door) +Quarry Stoneworks Entry (Door) +Quarry Stoneworks Side Exit (Door) +Quarry Stoneworks Roof Exit (Door) +Quarry Stoneworks Stairs (Door) Quarry Boathouse Dock (Door) Quarry Boathouse First Barrier (Door) Quarry Boathouse Second Barrier (Door) -Shadows Timed Door (Door) +Shadows Timed Door Shadows Laser Entry Right (Door) Shadows Laser Entry Left (Door) Shadows Quarry Barrier (Door) @@ -45,15 +45,15 @@ Monastery Entry Outer (Door) Monastery Garden Entry (Door) Town Cargo Box Entry (Door) Town Wooden Roof Stairs (Door) -Town Tinted Glass Door (Door) +Town Tinted Glass Door Town Church Entry (Door) Town Maze Stairs (Door) Town Windmill Entry (Door) Town RGB House Stairs (Door) -Town Tower First Door (Door) -Town Tower Third Door (Door) -Town Tower Fourth Door (Door) -Town Tower Second Door (Door) +Town Tower Second (Door) +Town Tower First (Door) +Town Tower Fourth (Door) +Town Tower Third (Door) Theater Entry (Door) Theater Exit Left (Door) Theater Exit Right (Door) @@ -61,7 +61,7 @@ Jungle Bamboo Laser Shortcut (Door) Jungle Popup Wall (Door) River Monastery Shortcut (Door) Bunker Entry (Door) -Bunker Tinted Glass Door (Door) +Bunker Tinted Glass Door Bunker UV Room Entry (Door) Bunker Elevator Room Entry (Door) Swamp Entry (Door) @@ -74,9 +74,9 @@ Swamp Red Underwater Exit (Door) Swamp Blue Water Pump (Door) Swamp Purple Water Pump (Door) Swamp Laser Shortcut (Door) -Treehouse First Door (Door) -Treehouse Second Door (Door) -Treehouse Third Door (Door) +Treehouse First (Door) +Treehouse Second (Door) +Treehouse Third (Door) Treehouse Drawbridge (Door) Treehouse Laser House Entry (Door) Mountain Floor 1 Exit (Door) @@ -87,7 +87,7 @@ Mountain Bottom Floor Giant Puzzle Exit (Door) Mountain Bottom Floor Final Room Entry (Door) Mountain Bottom Floor Rock (Door) Caves Entry (Door) -Caves Pillar Door (Door) +Caves Pillar Door Caves Mountain Shortcut (Door) Caves Swamp Shortcut (Door) Challenge Entry (Door) @@ -111,11 +111,11 @@ Desert Flood Room Entry Panel Desert Flood Room 6 Quarry Entry 1 Panel Quarry Entry 2 Panel -Quarry Mill Entry Right Panel -Quarry Mill Entry Left Panel -Quarry Mill Side Exit Panel -Quarry Mill Roof Exit Panel -Quarry Mill Stair Control +Quarry Stoneworks Entry Right Panel +Quarry Stoneworks Entry Left Panel +Quarry Stoneworks Side Exit Panel +Quarry Stoneworks Roof Exit Panel +Quarry Stoneworks Stair Control Quarry Boathouse Second Barrier Panel Shadows Door Timer Inside Shadows Door Timer Outside diff --git a/worlds/witness/settings/Doors_Max.txt b/worlds/witness/settings/Doors_Max.txt index 739000426c..e722b61ca0 100644 --- a/worlds/witness/settings/Doors_Max.txt +++ b/worlds/witness/settings/Doors_Max.txt @@ -14,14 +14,14 @@ Desert Flood Room Entry (Door) Desert Elevator Room Entry (Door) Quarry Entry 1 (Door) Quarry Entry 2 (Door) -Quarry Mill Entry (Door) -Quarry Mill Side Exit (Door) -Quarry Mill Roof Exit (Door) -Quarry Mill Stairs (Door) +Quarry Stoneworks Entry (Door) +Quarry Stoneworks Side Exit (Door) +Quarry Stoneworks Roof Exit (Door) +Quarry Stoneworks Stairs (Door) Quarry Boathouse Dock (Door) Quarry Boathouse First Barrier (Door) Quarry Boathouse Second Barrier (Door) -Shadows Timed Door (Door) +Shadows Timed Door Shadows Laser Entry Right (Door) Shadows Laser Entry Left (Door) Shadows Quarry Barrier (Door) @@ -45,15 +45,15 @@ Monastery Entry Outer (Door) Monastery Garden Entry (Door) Town Cargo Box Entry (Door) Town Wooden Roof Stairs (Door) -Town Tinted Glass Door (Door) +Town Tinted Glass Door Town Church Entry (Door) Town Maze Stairs (Door) Town Windmill Entry (Door) Town RGB House Stairs (Door) -Town Tower First Door (Door) -Town Tower Third Door (Door) -Town Tower Fourth Door (Door) -Town Tower Second Door (Door) +Town Tower Second (Door) +Town Tower First (Door) +Town Tower Fourth (Door) +Town Tower Third (Door) Theater Entry (Door) Theater Exit Left (Door) Theater Exit Right (Door) @@ -61,7 +61,7 @@ Jungle Bamboo Laser Shortcut (Door) Jungle Popup Wall (Door) River Monastery Shortcut (Door) Bunker Entry (Door) -Bunker Tinted Glass Door (Door) +Bunker Tinted Glass Door Bunker UV Room Entry (Door) Bunker Elevator Room Entry (Door) Swamp Entry (Door) @@ -74,9 +74,9 @@ Swamp Red Underwater Exit (Door) Swamp Blue Water Pump (Door) Swamp Purple Water Pump (Door) Swamp Laser Shortcut (Door) -Treehouse First Door (Door) -Treehouse Second Door (Door) -Treehouse Third Door (Door) +Treehouse First (Door) +Treehouse Second (Door) +Treehouse Third (Door) Treehouse Drawbridge (Door) Treehouse Laser House Entry (Door) Mountain Floor 1 Exit (Door) @@ -87,7 +87,7 @@ Mountain Bottom Floor Giant Puzzle Exit (Door) Mountain Bottom Floor Final Room Entry (Door) Mountain Bottom Floor Rock (Door) Caves Entry (Door) -Caves Pillar Door (Door) +Caves Pillar Door Caves Mountain Shortcut (Door) Caves Swamp Shortcut (Door) Challenge Entry (Door) @@ -97,8 +97,8 @@ Tunnels Desert Shortcut (Door) Tunnels Town Shortcut (Door) Desert Flood Controls (Panel) -Quarry Mill Ramp Controls (Panel) -Quarry Mill Lift Controls (Panel) +Quarry Stoneworks Ramp Controls (Panel) +Quarry Stoneworks Lift Controls (Panel) Quarry Boathouse Ramp Height Control (Panel) Quarry Boathouse Ramp Horizontal Control (Panel) Bunker Elevator Control (Panel) @@ -122,11 +122,11 @@ Desert Flood Room Entry Panel Desert Flood Room 6 Quarry Entry 1 Panel Quarry Entry 2 Panel -Quarry Mill Entry Right Panel -Quarry Mill Entry Left Panel -Quarry Mill Side Exit Panel -Quarry Mill Roof Exit Panel -Quarry Mill Stair Control +Quarry Stoneworks Entry Right Panel +Quarry Stoneworks Entry Left Panel +Quarry Stoneworks Side Exit Panel +Quarry Stoneworks Roof Exit Panel +Quarry Stoneworks Stair Control Quarry Boathouse Second Barrier Panel Shadows Door Timer Inside Shadows Door Timer Outside diff --git a/worlds/witness/settings/Doors_Simple.txt b/worlds/witness/settings/Doors_Simple.txt index 8cda2ee64b..309874b131 100644 --- a/worlds/witness/settings/Doors_Simple.txt +++ b/worlds/witness/settings/Doors_Simple.txt @@ -7,10 +7,10 @@ Symmetry Island Doors Orchard Gates Desert Doors Quarry Main Entry -Quarry Mill Entry (Door) -Quarry Mill Shortcuts +Quarry Stoneworks Entry (Door) +Quarry Stoneworks Shortcuts Quarry Boathouse Barriers -Shadows Timed Door (Door) +Shadows Timed Door Shadows Laser Room Door Shadows Barriers Keep Hedge Maze Doors @@ -56,11 +56,11 @@ Desert Flood Room Entry Panel Desert Flood Room 6 Quarry Entry 1 Panel Quarry Entry 2 Panel -Quarry Mill Entry Right Panel -Quarry Mill Entry Left Panel -Quarry Mill Side Exit Panel -Quarry Mill Roof Exit Panel -Quarry Mill Stair Control +Quarry Stoneworks Entry Right Panel +Quarry Stoneworks Entry Left Panel +Quarry Stoneworks Side Exit Panel +Quarry Stoneworks Roof Exit Panel +Quarry Stoneworks Stair Control Quarry Boathouse Second Barrier Panel Shadows Door Timer Inside Shadows Door Timer Outside From cc0540d3fb06c51652248363748e30a8f12fddf6 Mon Sep 17 00:00:00 2001 From: lordlou <87331798+lordlou@users.noreply.github.com> Date: Mon, 6 Feb 2023 21:14:03 -0500 Subject: [PATCH 18/47] SMZ3: keysanity accessibility fix (#1428) --- worlds/smz3/__init__.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/worlds/smz3/__init__.py b/worlds/smz3/__init__.py index 5e91b533b2..35e7ea6635 100644 --- a/worlds/smz3/__init__.py +++ b/worlds/smz3/__init__.py @@ -244,12 +244,12 @@ class SMZ3World(World): if self.multiworld.accessibility[self.player] != 'locations': l.always_allow = lambda state, item, loc=loc: \ item.game == "SMZ3" and \ - loc.alwaysAllow(TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World), state.smz3state[self.player]) + loc.alwaysAllow(item.item, state.smz3state[self.player]) old_rule = l.item_rule l.item_rule = lambda item, loc=loc, region=region: (\ item.game != "SMZ3" or \ - loc.allow(TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World), None) and \ - region.CanFill(TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World))) and old_rule(item) + loc.allow(item.item, None) and \ + region.CanFill(item.item)) and old_rule(item) set_rule(l, lambda state, loc=loc: loc.Available(state.smz3state[self.player])) def create_regions(self): @@ -502,17 +502,6 @@ class SMZ3World(World): all_dungeonItems = self.smz3DungeonItems[:] fill_restrictive(self.multiworld, all_state, locations, all_dungeonItems, True, True) - # some small or big keys (those always_allow) can be unreachable in-game - # while logic still collects some of them (probably to simulate the player collecting pot keys in the logic), some others don't - # so we need to remove those exceptions as progression items - if self.multiworld.accessibility[self.player] != 'locations': - exception_item = [TotalSMZ3Item.ItemType.BigKeySW, TotalSMZ3Item.ItemType.BigKeySP, TotalSMZ3Item.ItemType.KeyTH] - for item in self.smz3DungeonItems: - if item.item.Type in exception_item and item.location.always_allow(all_state, item) and not all_state.can_reach(item.location): - item.classification = ItemClassification.filler - item.item.Progression = False - item.location.event = False - self.unreachable.append(item.location) self.JunkFillGT(0.5) def get_pre_fill_items(self): @@ -520,6 +509,22 @@ class SMZ3World(World): return self.smz3DungeonItems else: return [] + + def post_fill(self): + # some small or big keys (those always_allow) can be unreachable in-game + # while logic still collects some of them (probably to simulate the player collecting pot keys in the logic), some others don't + # so we need to remove those exceptions as progression items + if self.multiworld.accessibility[self.player] == 'items': + state = CollectionState(self.multiworld) + locs = [self.multiworld.get_location("Swamp Palace - Big Chest", self.player), + self.multiworld.get_location("Skull Woods - Big Chest", self.player), + self.multiworld.get_location("Tower of Hera - Big Key Chest", self.player)] + for loc in locs: + if (loc.item.player == self.player and loc.always_allow(state, loc.item)): + loc.item.classification = ItemClassification.filler + loc.item.item.Progression = False + loc.item.location.event = False + self.unreachable.append(loc) def get_filler_item_name(self) -> str: return self.multiworld.random.choice(self.junkItemsNames) From e24a85ca5c9d6d3f16d1c30f6b51dab4a5f7cb93 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Tue, 7 Feb 2023 08:53:21 +0100 Subject: [PATCH 19/47] CI: update SNI to v0.0.88 --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 60dbd15b9b..36a690a523 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ name: Build on: workflow_dispatch env: - SNI_VERSION: v0.0.84 + SNI_VERSION: v0.0.88 ENEMIZER_VERSION: 7.1 APPIMAGETOOL_VERSION: 13 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 23f018caf2..fa8e80df4e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ on: - '*.*.*' env: - SNI_VERSION: v0.0.84 + SNI_VERSION: v0.0.88 ENEMIZER_VERSION: 7.1 APPIMAGETOOL_VERSION: 13 From 60584b76175837ddec7e38d6be6187d07ad5275d Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Tue, 7 Feb 2023 09:09:43 +0100 Subject: [PATCH 20/47] CI: more pip to fix the build --- .github/workflows/build.yml | 3 ++- .github/workflows/release.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 36a690a523..c51f155049 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,9 +78,10 @@ jobs: - name: Build run: | # pygobject is an optional dependency for kivy that's not in requirements - "${{ env.PYTHON }}" -m pip install --upgrade pip virtualenv PyGObject setuptools + # charset-normalizer was somehow incomplete in the github runner "${{ env.PYTHON }}" -m venv venv source venv/bin/activate + "${{ env.PYTHON }}" -m pip install --upgrade pip PyGObject setuptools charset-normalizer pip install -r requirements.txt python setup.py build_exe --yes bdist_appimage --yes echo -e "setup.py build output:\n `ls build`" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fa8e80df4e..e9559f7856 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,9 +65,10 @@ jobs: - name: Build run: | # pygobject is an optional dependency for kivy that's not in requirements - "${{ env.PYTHON }}" -m pip install --upgrade pip virtualenv PyGObject setuptools + # charset-normalizer was somehow incomplete in the github runner "${{ env.PYTHON }}" -m venv venv source venv/bin/activate + "${{ env.PYTHON }}" -m pip install --upgrade pip PyGObject setuptools charset-normalizer pip install -r requirements.txt python setup.py build --yes bdist_appimage --yes echo -e "setup.py build output:\n `ls build`" From 873a374a69c64ee35b37ae603d9367b898eaed67 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Tue, 7 Feb 2023 10:16:39 +0100 Subject: [PATCH 21/47] SNIclient: connect fixes (#1436) --- SNIClient.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/SNIClient.py b/SNIClient.py index 623bc17554..8d402b1d5f 100644 --- a/SNIClient.py +++ b/SNIClient.py @@ -56,7 +56,9 @@ class SNIClientCommandProcessor(ClientCommandProcessor): """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:23074 1" """ - + if self.ctx.snes_state in {SNESState.SNES_ATTACHED, SNESState.SNES_CONNECTED, SNESState.SNES_CONNECTING}: + self.output("Already connected to SNES. Disconnecting first.") + self._cmd_snes_close() return self.connect_to_snes(snes_options) def connect_to_snes(self, snes_options: str = "") -> bool: @@ -84,7 +86,7 @@ class SNIClientCommandProcessor(ClientCommandProcessor): """Close connection to a currently connected snes""" self.ctx.snes_reconnect_address = None self.ctx.cancel_snes_autoreconnect() - if self.ctx.snes_socket is not None and not self.ctx.snes_socket.closed: + if self.ctx.snes_socket and not self.ctx.snes_socket.closed: async_start(self.ctx.snes_socket.close()) return True else: @@ -442,7 +444,8 @@ async def snes_connect(ctx: SNIContext, address: str, deviceIndex: int = -1) -> recv_task = asyncio.create_task(snes_recv_loop(ctx)) except Exception as e: - if recv_task is not None: + ctx.snes_state = SNESState.SNES_DISCONNECTED + if task_alive(recv_task): if not ctx.snes_socket.closed: await ctx.snes_socket.close() else: @@ -450,15 +453,9 @@ async def snes_connect(ctx: SNIContext, address: str, deviceIndex: int = -1) -> if not ctx.snes_socket.closed: await ctx.snes_socket.close() ctx.snes_socket = None - ctx.snes_state = SNESState.SNES_DISCONNECTED - if not ctx.snes_reconnect_address: - snes_logger.error("Error connecting to snes (%s)" % e) - else: - snes_logger.error(f"Error connecting to snes, retrying in {_global_snes_reconnect_delay} seconds") - assert ctx.snes_autoreconnect_task is None - ctx.snes_autoreconnect_task = asyncio.create_task(snes_autoreconnect(ctx), name="snes auto-reconnect") + snes_logger.error(f"Error connecting to snes ({e}), retrying in {_global_snes_reconnect_delay} seconds") + ctx.snes_autoreconnect_task = asyncio.create_task(snes_autoreconnect(ctx), name="snes auto-reconnect") _global_snes_reconnect_delay *= 2 - else: _global_snes_reconnect_delay = ctx.starting_reconnect_delay snes_logger.info(f"Attached to {device}") @@ -471,10 +468,17 @@ async def snes_disconnect(ctx: SNIContext) -> None: ctx.snes_socket = None +def task_alive(task: typing.Optional[asyncio.Task]) -> bool: + if task: + return not task.done() + return False + + async def snes_autoreconnect(ctx: SNIContext) -> None: await asyncio.sleep(_global_snes_reconnect_delay) - if ctx.snes_reconnect_address and not ctx.snes_socket and not ctx.snes_connect_task: - ctx.snes_connect_task = asyncio.create_task(snes_connect(ctx, ctx.snes_reconnect_address), name="SNES Connect") + if not ctx.snes_socket and not task_alive(ctx.snes_connect_task): + address = ctx.snes_reconnect_address if ctx.snes_reconnect_address else ctx.snes_address + ctx.snes_connect_task = asyncio.create_task(snes_connect(ctx, address), name="SNES Connect") async def snes_recv_loop(ctx: SNIContext) -> None: From 0ff3c693d592cd7a838a5c0a7991f88fce4df90b Mon Sep 17 00:00:00 2001 From: toasterparty Date: Fri, 10 Feb 2023 12:42:28 -0800 Subject: [PATCH 22/47] [OC2] Relax Horde Logic for Horde H-8 and Winter H-4 (#1439) --- worlds/overcooked2/Logic.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/worlds/overcooked2/Logic.py b/worlds/overcooked2/Logic.py index 0b844fc189..eb58f0e737 100644 --- a/worlds/overcooked2/Logic.py +++ b/worlds/overcooked2/Logic.py @@ -3055,7 +3055,8 @@ level_logic = { "Horde H-8": ( ( # 1-star { # Exclusive - + "Coin Purse", + "Calmer Unbread" }, horde_logic, ), @@ -3467,7 +3468,8 @@ level_logic = { "Winter H-4": ( ( # 1-star { # Exclusive - + "Coin Purse", + "Calmer Unbread" }, horde_logic, ), From a40f6058b5b881e0d59c47efe6ad8d7c0c609aff Mon Sep 17 00:00:00 2001 From: el-u <109771707+el-u@users.noreply.github.com> Date: Sun, 12 Feb 2023 21:22:28 +0100 Subject: [PATCH 23/47] lufia2ac: fix mismatched exits/parent region --- worlds/lufia2ac/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/lufia2ac/__init__.py b/worlds/lufia2ac/__init__.py index 3ff7c50b5c..ae82c4c572 100644 --- a/worlds/lufia2ac/__init__.py +++ b/worlds/lufia2ac/__init__.py @@ -133,7 +133,7 @@ class L2ACWorld(World): self.multiworld.regions.append(menu) ancient_dungeon = Region("AncientDungeon", RegionType.Generic, "Ancient Dungeon", self.player, self.multiworld) - ancient_dungeon.exits.append(Entrance(self.player, "FinalFloorEntrance", menu)) + ancient_dungeon.exits.append(Entrance(self.player, "FinalFloorEntrance", ancient_dungeon)) item_count: int = self.blue_chest_count if self.shuffle_capsule_monsters: item_count += len(self.item_name_groups["Capsule monsters"]) From 803d7105a1521c600bcaacd25a5678b9bffcc616 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 13 Feb 2023 01:55:43 +0100 Subject: [PATCH 24/47] kivy: allow user-defined text colors in data/client.kv (#1429) --- .gitignore | 1 + data/client.kv | 19 ++++++++++++++++++- kvui.py | 20 +++++++++++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c009036324..e269202db9 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ Output Logs/ /Archipelago.zip /setup.ini /installdelete.iss +/data/user.kv # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/data/client.kv b/data/client.kv index 5948a3ebf6..117539b700 100644 --- a/data/client.kv +++ b/data/client.kv @@ -1,4 +1,21 @@ - +: + # Hex-format RGB colors used in clients. Resets after an update/install. + # To avoid, you can copy the TextColors section into a new "user.kv" next to this file + # and it will read from there instead. + black: "000000" + red: "EE0000" + green: "00FF7F" # typically a location + yellow: "FAFAD2" # typically other slots/players + blue: "6495ED" # typically extra info (such as entrance) + magenta: "EE00EE" # typically your slot/player + cyan: "00EEEE" # typically regular item + slateblue: "6D8BE8" # typically useful item + plum: "AF99EF" # typically progression item + salmon: "FA8072" # typically trap item + white: "FFFFFF" # not used, if you want to change the generic text color change color in Label +