diff --git a/README.md b/README.md index b51fe00f9ac2..3508dd16095c 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Currently, the following games are supported: * DOOM II * Shivers * Heretic +* Landstalker: The Treasures of King Nole For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/). Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index c589b1333c9b..0764fa927464 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -67,6 +67,9 @@ # Kingdom Hearts 2 /worlds/kh2/ @JaredWeakStrike +# Landstalker: The Treasures of King Nole +/worlds/landstalker/ @Dinopony + # Lingo /worlds/lingo/ @hatkirby diff --git a/worlds/landstalker/Hints.py b/worlds/landstalker/Hints.py new file mode 100644 index 000000000000..93274f1d68bb --- /dev/null +++ b/worlds/landstalker/Hints.py @@ -0,0 +1,140 @@ +from typing import TYPE_CHECKING + +from BaseClasses import Location +from .data.hint_source import HINT_SOURCES_JSON + +if TYPE_CHECKING: + from random import Random + from . import LandstalkerWorld + + +def generate_blurry_location_hint(location: Location, random: "Random"): + cleaned_location_name = location.hint_text.lower().translate({ord(c): None for c in "(),:"}) + cleaned_location_name.replace("-", " ") + cleaned_location_name.replace("/", " ") + cleaned_location_name.replace(".", " ") + location_name_words = [w for w in cleaned_location_name.split(" ") if len(w) > 3] + + random_word_1 = "mysterious" + random_word_2 = "place" + if location_name_words: + random_word_1 = random.choice(location_name_words) + location_name_words.remove(random_word_1) + if location_name_words: + random_word_2 = random.choice(location_name_words) + return [random_word_1, random_word_2] + + +def generate_lithograph_hint(world: "LandstalkerWorld"): + hint_text = "It's barely readable:\n" + jewel_items = world.jewel_items + + for item in jewel_items: + # Jewel hints are composed of 4 'words' shuffled randomly: + # - the name of the player whose world contains said jewel (if not ours) + # - the color of the jewel (if relevant) + # - two random words from the location name + words = generate_blurry_location_hint(item.location, world.random) + words[0] = words[0].upper() + words[1] = words[1].upper() + if len(jewel_items) < 6: + # Add jewel color if we are not using generic jewels because jewel count is 6 or more + words.append(item.name.split(" ")[0].upper()) + if item.location.player != world.player: + # Add player name if it's not in our own world + player_name = world.multiworld.get_player_name(world.player) + words.append(player_name.upper()) + world.random.shuffle(words) + hint_text += " ".join(words) + "\n" + return hint_text.rstrip("\n") + + +def generate_random_hints(world: "LandstalkerWorld"): + hints = {} + hint_texts = [] + random = world.random + multiworld = world.multiworld + this_player = world.player + + # Exclude Life Stock from the hints as some of them are considered as progression for Fahl, but isn't really + # exciting when hinted + excluded_items = ["Life Stock", "EkeEke"] + + progression_items = [item for item in multiworld.itempool if item.advancement and + item.name not in excluded_items] + + local_own_progression_items = [item for item in progression_items if item.player == this_player + and item.location.player == this_player] + remote_own_progression_items = [item for item in progression_items if item.player == this_player + and item.location.player != this_player] + local_unowned_progression_items = [item for item in progression_items if item.player != this_player + and item.location.player == this_player] + remote_unowned_progression_items = [item for item in progression_items if item.player != this_player + and item.location.player != this_player] + + # Hint-type #1: Own progression item in own world + for item in local_own_progression_items: + region_hint = item.location.parent_region.hint_text + hint_texts.append(f"I can sense {item.name} {region_hint}.") + + # Hint-type #2: Remote progression item in own world + for item in local_unowned_progression_items: + other_player = multiworld.get_player_name(item.player) + own_local_region = item.location.parent_region.hint_text + hint_texts.append(f"You might find something useful for {other_player} {own_local_region}. " + f"It is a {item.name}, to be precise.") + + # Hint-type #3: Own progression item in remote location + for item in remote_own_progression_items: + other_player = multiworld.get_player_name(item.location.player) + if item.location.game == "Landstalker - The Treasures of King Nole": + region_hint_name = item.location.parent_region.hint_text + hint_texts.append(f"If you need {item.name}, tell {other_player} to look {region_hint_name}.") + else: + [word_1, word_2] = generate_blurry_location_hint(item.location, random) + if word_1 == "mysterious" and word_2 == "place": + continue + hint_texts.append(f"Looking for {item.name}? I read something about {other_player}'s world... " + f"Does \"{word_1} {word_2}\" remind you anything?") + + # Hint-type #4: Remote progression item in remote location + for item in remote_unowned_progression_items: + owner_name = multiworld.get_player_name(item.player) + if item.location.player == item.player: + world_name = "their own world" + else: + world_name = f"{multiworld.get_player_name(item.location.player)}'s world" + [word_1, word_2] = generate_blurry_location_hint(item.location, random) + if word_1 == "mysterious" and word_2 == "place": + continue + hint_texts.append(f"I once found {owner_name}'s {item.name} in {world_name}. " + f"I remember \"{word_1} {word_2}\"... Does that make any sense?") + + # Hint-type #5: Jokes + other_player_names = [multiworld.get_player_name(player) for player in multiworld.player_ids if + player != this_player] + if other_player_names: + random_player_name = random.choice(other_player_names) + hint_texts.append(f"{random_player_name}'s world is objectively better than yours.") + + hint_texts.append(f"Have you found all of the {len(multiworld.itempool)} items in this universe?") + + local_progression_item_count = len(local_own_progression_items) + len(local_unowned_progression_items) + remote_progression_item_count = len(remote_own_progression_items) + len(remote_unowned_progression_items) + percent = (local_progression_item_count / (local_progression_item_count + remote_progression_item_count)) * 100 + hint_texts.append(f"Did you know that your world contains {int(percent)} percent of all progression items?") + + # Shuffle hint texts and hint source names, and pair the two of those together + hint_texts = list(set(hint_texts)) + random.shuffle(hint_texts) + + hint_count = world.options.hint_count.value + del hint_texts[hint_count:] + + hint_source_names = [source["description"] for source in HINT_SOURCES_JSON if + source["description"].startswith("Foxy")] + random.shuffle(hint_source_names) + + for i in range(hint_count): + hints[hint_source_names[i]] = hint_texts[i] + return hints diff --git a/worlds/landstalker/Items.py b/worlds/landstalker/Items.py new file mode 100644 index 000000000000..ad7efa1cb27a --- /dev/null +++ b/worlds/landstalker/Items.py @@ -0,0 +1,105 @@ +from typing import Dict, List, NamedTuple + +from BaseClasses import Item, ItemClassification + +BASE_ITEM_ID = 4000 + + +class LandstalkerItem(Item): + game: str = "Landstalker - The Treasures of King Nole" + price_in_shops: int + + +class LandstalkerItemData(NamedTuple): + id: int + classification: ItemClassification + price_in_shops: int + quantity: int = 1 + + +item_table: Dict[str, LandstalkerItemData] = { + "EkeEke": LandstalkerItemData(0, ItemClassification.filler, 20, 0), # Variable amount + "Magic Sword": LandstalkerItemData(1, ItemClassification.useful, 300), + "Sword of Ice": LandstalkerItemData(2, ItemClassification.useful, 300), + "Thunder Sword": LandstalkerItemData(3, ItemClassification.useful, 500), + "Sword of Gaia": LandstalkerItemData(4, ItemClassification.progression, 300), + "Fireproof": LandstalkerItemData(5, ItemClassification.progression, 150), + "Iron Boots": LandstalkerItemData(6, ItemClassification.progression, 150), + "Healing Boots": LandstalkerItemData(7, ItemClassification.useful, 300), + "Snow Spikes": LandstalkerItemData(8, ItemClassification.progression, 400), + "Steel Breast": LandstalkerItemData(9, ItemClassification.useful, 200), + "Chrome Breast": LandstalkerItemData(10, ItemClassification.useful, 350), + "Shell Breast": LandstalkerItemData(11, ItemClassification.useful, 500), + "Hyper Breast": LandstalkerItemData(12, ItemClassification.useful, 700), + "Mars Stone": LandstalkerItemData(13, ItemClassification.useful, 150), + "Moon Stone": LandstalkerItemData(14, ItemClassification.useful, 150), + "Saturn Stone": LandstalkerItemData(15, ItemClassification.useful, 200), + "Venus Stone": LandstalkerItemData(16, ItemClassification.useful, 300), + # Awakening Book: 17 + "Detox Grass": LandstalkerItemData(18, ItemClassification.filler, 25, 9), + "Statue of Gaia": LandstalkerItemData(19, ItemClassification.filler, 75, 12), + "Golden Statue": LandstalkerItemData(20, ItemClassification.filler, 150, 10), + "Mind Repair": LandstalkerItemData(21, ItemClassification.filler, 25, 7), + "Casino Ticket": LandstalkerItemData(22, ItemClassification.progression, 50), + "Axe Magic": LandstalkerItemData(23, ItemClassification.progression, 400), + "Blue Ribbon": LandstalkerItemData(24, ItemClassification.filler, 50), + "Buyer Card": LandstalkerItemData(25, ItemClassification.progression, 150), + "Lantern": LandstalkerItemData(26, ItemClassification.progression, 200), + "Garlic": LandstalkerItemData(27, ItemClassification.progression, 150, 2), + "Anti Paralyze": LandstalkerItemData(28, ItemClassification.filler, 20, 7), + "Statue of Jypta": LandstalkerItemData(29, ItemClassification.useful, 250), + "Sun Stone": LandstalkerItemData(30, ItemClassification.progression, 300), + "Armlet": LandstalkerItemData(31, ItemClassification.progression, 300), + "Einstein Whistle": LandstalkerItemData(32, ItemClassification.progression, 200), + "Blue Jewel": LandstalkerItemData(33, ItemClassification.progression, 500, 0), # Detox Book in base game + "Yellow Jewel": LandstalkerItemData(34, ItemClassification.progression, 500, 0), # AntiCurse Book in base game + # Record Book: 35 + # Spell Book: 36 + # Hotel Register: 37 + # Island Map: 38 + "Lithograph": LandstalkerItemData(39, ItemClassification.progression, 250), + "Red Jewel": LandstalkerItemData(40, ItemClassification.progression, 500, 0), + "Pawn Ticket": LandstalkerItemData(41, ItemClassification.useful, 200, 4), + "Purple Jewel": LandstalkerItemData(42, ItemClassification.progression, 500, 0), + "Gola's Eye": LandstalkerItemData(43, ItemClassification.progression, 400), + "Death Statue": LandstalkerItemData(44, ItemClassification.filler, 150), + "Dahl": LandstalkerItemData(45, ItemClassification.filler, 100, 18), + "Restoration": LandstalkerItemData(46, ItemClassification.filler, 40, 9), + "Logs": LandstalkerItemData(47, ItemClassification.progression, 100, 2), + "Oracle Stone": LandstalkerItemData(48, ItemClassification.progression, 250), + "Idol Stone": LandstalkerItemData(49, ItemClassification.progression, 200), + "Key": LandstalkerItemData(50, ItemClassification.progression, 150), + "Safety Pass": LandstalkerItemData(51, ItemClassification.progression, 250), + "Green Jewel": LandstalkerItemData(52, ItemClassification.progression, 500, 0), # No52 in base game + "Bell": LandstalkerItemData(53, ItemClassification.useful, 200), + "Short Cake": LandstalkerItemData(54, ItemClassification.useful, 250), + "Gola's Nail": LandstalkerItemData(55, ItemClassification.progression, 800), + "Gola's Horn": LandstalkerItemData(56, ItemClassification.progression, 800), + "Gola's Fang": LandstalkerItemData(57, ItemClassification.progression, 800), + # Broad Sword: 58 + # Leather Breast: 59 + # Leather Boots: 60 + # No Ring: 61 + "Life Stock": LandstalkerItemData(62, ItemClassification.filler, 250, 0), # Variable amount + "No Item": LandstalkerItemData(63, ItemClassification.filler, 0, 0), + "1 Gold": LandstalkerItemData(64, ItemClassification.filler, 1), + "20 Golds": LandstalkerItemData(65, ItemClassification.filler, 20, 15), + "50 Golds": LandstalkerItemData(66, ItemClassification.filler, 50, 7), + "100 Golds": LandstalkerItemData(67, ItemClassification.filler, 100, 5), + "200 Golds": LandstalkerItemData(68, ItemClassification.useful, 200, 2), + + "Progressive Armor": LandstalkerItemData(69, ItemClassification.useful, 250, 0), + "Kazalt Jewel": LandstalkerItemData(70, ItemClassification.progression, 500, 0) +} + + +def get_weighted_filler_item_names(): + weighted_item_names: List[str] = [] + for name, data in item_table.items(): + if data.classification == ItemClassification.filler: + weighted_item_names += [name for _ in range(data.quantity)] + return weighted_item_names + + +def build_item_name_to_id_table(): + return {name: data.id + BASE_ITEM_ID for name, data in item_table.items()} diff --git a/worlds/landstalker/Locations.py b/worlds/landstalker/Locations.py new file mode 100644 index 000000000000..5e42fbecda72 --- /dev/null +++ b/worlds/landstalker/Locations.py @@ -0,0 +1,53 @@ +from typing import Dict, Optional + +from BaseClasses import Location +from .Regions import LandstalkerRegion +from .data.item_source import ITEM_SOURCES_JSON + +BASE_LOCATION_ID = 4000 +BASE_GROUND_LOCATION_ID = BASE_LOCATION_ID + 256 +BASE_SHOP_LOCATION_ID = BASE_GROUND_LOCATION_ID + 30 +BASE_REWARD_LOCATION_ID = BASE_SHOP_LOCATION_ID + 50 + + +class LandstalkerLocation(Location): + game: str = "Landstalker - The Treasures of King Nole" + type_string: str + price: int = 0 + + def __init__(self, player: int, name: str, location_id: Optional[int], region: LandstalkerRegion, type_string: str): + super().__init__(player, name, location_id, region) + self.type_string = type_string + + +def create_locations(player: int, regions_table: Dict[str, LandstalkerRegion], name_to_id_table: Dict[str, int]): + # Create real locations from the data inside the corresponding JSON file + for data in ITEM_SOURCES_JSON: + region_id = data["nodeId"] + region = regions_table[region_id] + new_location = LandstalkerLocation(player, data["name"], name_to_id_table[data["name"]], region, data["type"]) + region.locations.append(new_location) + + # Create a specific end location that will contain a fake win-condition item + end_location = LandstalkerLocation(player, "End", None, regions_table["end"], "reward") + regions_table["end"].locations.append(end_location) + + +def build_location_name_to_id_table(): + location_name_to_id_table = {} + + for data in ITEM_SOURCES_JSON: + if data["type"] == "chest": + location_id = BASE_LOCATION_ID + int(data["chestId"]) + elif data["type"] == "ground": + location_id = BASE_GROUND_LOCATION_ID + int(data["groundItemId"]) + elif data["type"] == "shop": + location_id = BASE_SHOP_LOCATION_ID + int(data["shopItemId"]) + else: # if data["type"] == "reward": + location_id = BASE_REWARD_LOCATION_ID + int(data["rewardId"]) + location_name_to_id_table[data["name"]] = location_id + + # Win condition location ID + location_name_to_id_table["Gola"] = BASE_REWARD_LOCATION_ID + 10 + + return location_name_to_id_table diff --git a/worlds/landstalker/Options.py b/worlds/landstalker/Options.py new file mode 100644 index 000000000000..65ffd2c1f31e --- /dev/null +++ b/worlds/landstalker/Options.py @@ -0,0 +1,228 @@ +from dataclasses import dataclass + +from Options import Choice, DeathLink, DefaultOnToggle, PerGameCommonOptions, Range, Toggle + + +class LandstalkerGoal(Choice): + """ + The goal to accomplish in order to complete the seed. + - Beat Gola: beat the usual final boss (same as vanilla) + - Reach Kazalt: find the jewels and take the teleporter to Kazalt + - Beat Dark Nole: the door to King Nole's fight brings you into a final dungeon with an absurdly hard boss you have + to beat to win the game + """ + display_name = "Goal" + + option_beat_gola = 0 + option_reach_kazalt = 1 + option_beat_dark_nole = 2 + + default = 0 + + +class JewelCount(Range): + """ + Determines the number of jewels to find to be able to reach Kazalt. + """ + display_name = "Jewel Count" + range_start = 0 + range_end = 9 + default = 5 + + +class ProgressiveArmors(DefaultOnToggle): + """ + When obtaining an armor, you get the next armor tier instead of getting the specific armor tier that was + placed here by randomization. Enabling this provides a smoother progression. + """ + display_name = "Progressive Armors" + + +class UseRecordBook(DefaultOnToggle): + """ + Gives a Record Book item in starting inventory, allowing to save the game anywhere. + This makes the game significantly less frustrating and enables interesting save-scumming strategies in some places. + """ + display_name = "Use Record Book" + + +class UseSpellBook(DefaultOnToggle): + """ + Gives a Spell Book item in starting inventory, allowing to warp back to the starting location at any time. + This prevents any kind of softlock and makes the world easier to explore. + """ + display_name = "Use Spell Book" + + +class EnsureEkeEkeInShops(DefaultOnToggle): + """ + Ensures an EkeEke will always be for sale in one shop per region in the game. + Disabling this can lead to frustrating situations where you cannot refill your health items and might get locked. + """ + display_name = "Ensure EkeEke in Shops" + + +class RemoveGumiBoulder(Toggle): + """ + Removes the boulder between Gumi and Ryuma, which is usually a one-way path. + This makes the vanilla early game (Massan, Gumi...) more easily accessible when starting outside it. + """ + display_name = "Remove Boulder After Gumi" + + +class EnemyJumpingInLogic(Toggle): + """ + Adds jumping on enemies' heads as a logical rule. + This gives access to Mountainous Area from Lake Shrine sector and to the cliff chest behind a magic tree near Mir Tower. + These tricks not being easy, you should leave this disabled until practiced. + """ + display_name = "Enemy Jumping in Logic" + + +class TreeCuttingGlitchInLogic(Toggle): + """ + Adds tree-cutting glitch as a logical rule, enabling access to both chests behind magic trees in Mir Tower Sector + without having Axe Magic. + """ + display_name = "Tree-cutting Glitch in Logic" + + +class DamageBoostingInLogic(Toggle): + """ + Adds damage boosting as a logical rule, removing any requirements involving Iron Boots or Fireproof Boots. + Who doesn't like walking on spikes and lava? + """ + display_name = "Damage Boosting in Logic" + + +class WhistleUsageBehindTrees(DefaultOnToggle): + """ + In Greenmaze, Einstein Whistle can only be used to call Cutter from the intended side by default. + Enabling this allows using Einstein Whistle from both sides of the magic trees. + This is only useful in seeds starting in the "waterfall" spawn region or where teleportation trees are made open from the start. + """ + display_name = "Allow Using Einstein Whistle Behind Trees" + + +class SpawnRegion(Choice): + """ + List of spawn locations that can be picked by the randomizer. + It is advised to keep Massan as your spawn location for your first few seeds. + Picking a late-game location can make the seed significantly harder, both for logic and combat. + """ + display_name = "Starting Region" + + option_massan = 0 + option_gumi = 1 + option_kado = 2 + option_waterfall = 3 + option_ryuma = 4 + option_mercator = 5 + option_verla = 6 + option_greenmaze = 7 + option_destel = 8 + + default = 0 + + +class TeleportTreeRequirements(Choice): + """ + Determines the requirements to be able to use a teleport tree pair. + - None: All teleport trees are available right from the start + - Clear Tibor: Tibor needs to be cleared before unlocking any tree + - Visit Trees: Both trees from a tree pair need to be visited to teleport between them + Vanilla behavior is "Clear Tibor And Visit Trees" + """ + display_name = "Teleportation Trees Requirements" + + option_none = 0 + option_clear_tibor = 1 + option_visit_trees = 2 + option_clear_tibor_and_visit_trees = 3 + + default = 3 + + +class ShuffleTrees(Toggle): + """ + If enabled, all teleportation trees will be shuffled into new pairs. + """ + display_name = "Shuffle Teleportation Trees" + + +class ReviveUsingEkeeke(DefaultOnToggle): + """ + In the vanilla game, when you die, you are automatically revived by Friday using an EkeEke. + This setting allows disabling this feature, making the game extremely harder. + USE WITH CAUTION! + """ + display_name = "Revive Using EkeEke" + + +class ShopPricesFactor(Range): + """ + Applies a percentage factor on all prices in shops. Having higher prices can lead to a bit of gold farming, which + can make seeds longer but also sometimes more frustrating. + """ + display_name = "Shop Prices Factor (%)" + range_start = 50 + range_end = 200 + default = 100 + + +class CombatDifficulty(Choice): + """ + Determines the overall combat difficulty in the game by modifying both monsters HP & damage. + - Peaceful: 50% HP & damage + - Easy: 75% HP & damage + - Normal: 100% HP & damage + - Hard: 140% HP & damage + - Insane: 200% HP & damage + """ + display_name = "Combat Difficulty" + + option_peaceful = 0 + option_easy = 1 + option_normal = 2 + option_hard = 3 + option_insane = 4 + + default = 2 + + +class HintCount(Range): + """ + Determines the number of Foxy NPCs that will be scattered across the world, giving various types of hints + """ + display_name = "Hint Count" + range_start = 0 + range_end = 25 + default = 12 + + +@dataclass +class LandstalkerOptions(PerGameCommonOptions): + goal: LandstalkerGoal + spawn_region: SpawnRegion + jewel_count: JewelCount + progressive_armors: ProgressiveArmors + use_record_book: UseRecordBook + use_spell_book: UseSpellBook + + shop_prices_factor: ShopPricesFactor + combat_difficulty: CombatDifficulty + + teleport_tree_requirements: TeleportTreeRequirements + shuffle_trees: ShuffleTrees + + ensure_ekeeke_in_shops: EnsureEkeEkeInShops + remove_gumi_boulder: RemoveGumiBoulder + allow_whistle_usage_behind_trees: WhistleUsageBehindTrees + handle_damage_boosting_in_logic: DamageBoostingInLogic + handle_enemy_jumping_in_logic: EnemyJumpingInLogic + handle_tree_cutting_glitch_in_logic: TreeCuttingGlitchInLogic + + hint_count: HintCount + + revive_using_ekeeke: ReviveUsingEkeeke + death_link: DeathLink diff --git a/worlds/landstalker/Regions.py b/worlds/landstalker/Regions.py new file mode 100644 index 000000000000..21704194f157 --- /dev/null +++ b/worlds/landstalker/Regions.py @@ -0,0 +1,118 @@ +from typing import Dict, List, NamedTuple, Optional, TYPE_CHECKING + +from BaseClasses import MultiWorld, Region +from .data.world_node import WORLD_NODES_JSON +from .data.world_path import WORLD_PATHS_JSON +from .data.world_region import WORLD_REGIONS_JSON +from .data.world_teleport_tree import WORLD_TELEPORT_TREES_JSON + +if TYPE_CHECKING: + from . import LandstalkerWorld + + +class LandstalkerRegion(Region): + code: str + + def __init__(self, code: str, name: str, player: int, multiworld: MultiWorld, hint: Optional[str] = None): + super().__init__(name, player, multiworld, hint) + self.code = code + + +class LandstalkerRegionData(NamedTuple): + locations: Optional[List[str]] + region_exits: Optional[List[str]] + + +def create_regions(world: "LandstalkerWorld"): + regions_table: Dict[str, LandstalkerRegion] = {} + multiworld = world.multiworld + player = world.player + + # Create the hardcoded starting "Menu" region + menu_region = LandstalkerRegion("menu", "Menu", player, multiworld) + regions_table["menu"] = menu_region + multiworld.regions.append(menu_region) + + # Create regions from world_nodes + for code, region_data in WORLD_NODES_JSON.items(): + random_hint_name = None + if "hints" in region_data: + random_hint_name = multiworld.random.choice(region_data["hints"]) + region = LandstalkerRegion(code, region_data["name"], player, multiworld, random_hint_name) + regions_table[code] = region + multiworld.regions.append(region) + + # Create exits/entrances from world_paths + for data in WORLD_PATHS_JSON: + two_way = data["twoWay"] if "twoWay" in data else False + create_entrance(data["fromId"], data["toId"], two_way, regions_table) + + # Create a path between the fake Menu location and the starting location + starting_region = get_starting_region(world, regions_table) + menu_region.connect(starting_region, f"menu -> {starting_region.code}") + + add_specific_paths(world, regions_table) + + return regions_table + + +def add_specific_paths(world: "LandstalkerWorld", regions_table: Dict[str, LandstalkerRegion]): + # If Gumi boulder is removed, add a path from "route_gumi_ryuma" to "gumi" + if world.options.remove_gumi_boulder == 1: + create_entrance("route_gumi_ryuma", "gumi", False, regions_table) + + # If enemy jumping is in logic, Mountainous Area can be reached from route to Lake Shrine by doing a "ghost jump" + # at crossroads map + if world.options.handle_enemy_jumping_in_logic == 1: + create_entrance("route_lake_shrine", "route_lake_shrine_cliff", False, regions_table) + + # If using Einstein Whistle behind trees is allowed, add a new logic path there to reflect that change + if world.options.allow_whistle_usage_behind_trees == 1: + create_entrance("greenmaze_post_whistle", "greenmaze_pre_whistle", False, regions_table) + + +def create_entrance(from_id: str, to_id: str, two_way: bool, regions_table: Dict[str, LandstalkerRegion]): + created_entrances = [] + + name = from_id + " -> " + to_id + from_region = regions_table[from_id] + to_region = regions_table[to_id] + + created_entrances.append(from_region.connect(to_region, name)) + + if two_way: + reverse_name = to_id + " -> " + from_id + created_entrances.append(to_region.connect(from_region, reverse_name)) + + return created_entrances + + +def get_starting_region(world: "LandstalkerWorld", regions_table: Dict[str, LandstalkerRegion]): + # Most spawn locations have the same name as the region they are bound to, but a few vary. + spawn_id = world.options.spawn_region.current_key + if spawn_id == "waterfall": + return regions_table["greenmaze_post_whistle"] + elif spawn_id == "kado": + return regions_table["route_gumi_ryuma"] + elif spawn_id == "greenmaze": + return regions_table["greenmaze_pre_whistle"] + return regions_table[spawn_id] + + +def get_darkenable_regions(): + return {data["name"]: data["nodeIds"] for data in WORLD_REGIONS_JSON if "darkMapIds" in data} + + +def load_teleport_trees(): + pairs = [] + for pair in WORLD_TELEPORT_TREES_JSON: + first_tree = { + 'name': pair[0]["name"], + 'region': pair[0]["nodeId"] + } + second_tree = { + 'name': pair[1]["name"], + 'region': pair[1]["nodeId"] + } + pairs.append([first_tree, second_tree]) + return pairs diff --git a/worlds/landstalker/Rules.py b/worlds/landstalker/Rules.py new file mode 100644 index 000000000000..51357c9480b0 --- /dev/null +++ b/worlds/landstalker/Rules.py @@ -0,0 +1,134 @@ +from typing import List, TYPE_CHECKING + +from BaseClasses import CollectionState +from .data.world_path import WORLD_PATHS_JSON +from .Locations import LandstalkerLocation +from .Regions import LandstalkerRegion + +if TYPE_CHECKING: + from . import LandstalkerWorld + + +def _landstalker_has_visited_regions(state: CollectionState, player: int, regions): + return all([state.can_reach(region, None, player) for region in regions]) + + +def _landstalker_has_health(state: CollectionState, player: int, health): + return state.has("Life Stock", player, health) + + +# multiworld: MultiWorld, player: int, regions_table: Dict[str, Region], dark_region_ids: List[str] +def create_rules(world: "LandstalkerWorld"): + # Item & exploration requirements to take paths + add_path_requirements(world) + add_specific_path_requirements(world) + + # Location rules to forbid some item types depending on location types + add_location_rules(world) + + # Win condition + world.multiworld.completion_condition[world.player] = lambda state: state.has("King Nole's Treasure", world.player) + + +# multiworld: MultiWorld, player: int, regions_table: Dict[str, Region], +# dark_region_ids: List[str] +def add_path_requirements(world: "LandstalkerWorld"): + for data in WORLD_PATHS_JSON: + name = data["fromId"] + " -> " + data["toId"] + + # Determine required items to reach this region + required_items = data["requiredItems"] if "requiredItems" in data else [] + if "itemsPlacedWhenCrossing" in data: + required_items += data["itemsPlacedWhenCrossing"] + + if data["toId"] in world.dark_region_ids: + # Make Lantern required to reach the randomly selected dark regions + required_items.append("Lantern") + if world.options.handle_damage_boosting_in_logic: + # If damage boosting is handled in logic, remove all iron boots & fireproof requirements + required_items = [item for item in required_items if item != "Iron Boots" and item != "Fireproof"] + + # Determine required other visited regions to reach this region + required_region_ids = data["requiredNodes"] if "requiredNodes" in data else [] + required_regions = [world.regions_table[region_id] for region_id in required_region_ids] + + if not (required_items or required_regions): + continue + + # Create the rule lambda using those requirements + access_rule = make_path_requirement_lambda(world.player, required_items, required_regions) + world.multiworld.get_entrance(name, world.player).access_rule = access_rule + + # If two-way, also apply the rule to the opposite path + if "twoWay" in data and data["twoWay"] is True: + reverse_name = data["toId"] + " -> " + data["fromId"] + world.multiworld.get_entrance(reverse_name, world.player).access_rule = access_rule + + +def add_specific_path_requirements(world: "LandstalkerWorld"): + multiworld = world.multiworld + player = world.player + + # Make the jewels required to reach Kazalt + jewel_count = world.options.jewel_count.value + path_to_kazalt = multiworld.get_entrance("king_nole_cave -> kazalt", player) + if jewel_count < 6: + # 5- jewels => the player needs to find as many uniquely named jewel items + required_jewels = ["Red Jewel", "Purple Jewel", "Green Jewel", "Blue Jewel", "Yellow Jewel"] + del required_jewels[jewel_count:] + path_to_kazalt.access_rule = make_path_requirement_lambda(player, required_jewels, []) + else: + # 6+ jewels => the player needs to find as many "Kazalt Jewel" items + path_to_kazalt.access_rule = lambda state: state.has("Kazalt Jewel", player, jewel_count) + + # If enemy jumping is enabled, Mir Tower sector first tree can be bypassed to reach the elevated ledge + if world.options.handle_enemy_jumping_in_logic == 1: + remove_requirements_for(world, "mir_tower_sector -> mir_tower_sector_tree_ledge") + + # Both trees in Mir Tower sector can be abused using tree cutting glitch + if world.options.handle_tree_cutting_glitch_in_logic == 1: + remove_requirements_for(world, "mir_tower_sector -> mir_tower_sector_tree_ledge") + remove_requirements_for(world, "mir_tower_sector -> mir_tower_sector_tree_coast") + + # If Whistle can be used from behind the trees, it adds a new path that requires the whistle as well + if world.options.allow_whistle_usage_behind_trees == 1: + entrance = multiworld.get_entrance("greenmaze_post_whistle -> greenmaze_pre_whistle", player) + entrance.access_rule = make_path_requirement_lambda(player, ["Einstein Whistle"], []) + + +def make_path_requirement_lambda(player: int, required_items: List[str], required_regions: List[LandstalkerRegion]): + """ + Lambdas are created in a for loop, so values need to be captured + """ + return lambda state: \ + state.has_all(set(required_items), player) and _landstalker_has_visited_regions(state, player, required_regions) + + +def make_shop_location_requirement_lambda(player: int, location: LandstalkerLocation): + """ + Lambdas are created in a for loop, so values need to be captured + """ + # Prevent local golds in shops, as well as duplicates + other_locations_in_shop = [loc for loc in location.parent_region.locations if loc != location] + return lambda item: \ + item.player != player \ + or (" Gold" not in item.name + and item.name not in [loc.item.name for loc in other_locations_in_shop if loc.item is not None]) + + +def remove_requirements_for(world: "LandstalkerWorld", entrance_name: str): + entrance = world.multiworld.get_entrance(entrance_name, world.player) + entrance.access_rule = lambda state: True + + +def add_location_rules(world: "LandstalkerWorld"): + location: LandstalkerLocation + for location in world.multiworld.get_locations(world.player): + if location.type_string == "ground": + location.item_rule = lambda item: not (item.player == world.player and " Gold" in item.name) + elif location.type_string == "shop": + location.item_rule = make_shop_location_requirement_lambda(world.player, location) + + # Add a special rule for Fahl + fahl_location = world.multiworld.get_location("Mercator: Fahl's dojo challenge reward", world.player) + fahl_location.access_rule = lambda state: _landstalker_has_health(state, world.player, 15) diff --git a/worlds/landstalker/__init__.py b/worlds/landstalker/__init__.py new file mode 100644 index 000000000000..baa1deb620a4 --- /dev/null +++ b/worlds/landstalker/__init__.py @@ -0,0 +1,262 @@ +from typing import ClassVar, Set + +from BaseClasses import LocationProgressType, Tutorial +from worlds.AutoWorld import WebWorld, World +from .Hints import * +from .Items import * +from .Locations import * +from .Options import JewelCount, LandstalkerGoal, LandstalkerOptions, ProgressiveArmors, TeleportTreeRequirements +from .Regions import * +from .Rules import * + + +class LandstalkerWeb(WebWorld): + theme = "grass" + tutorials = [Tutorial( + "Multiworld Setup Guide", + "A guide to setting up the Landstalker Randomizer software on your computer.", + "English", + "landstalker_setup_en.md", + "landstalker_setup/en", + ["Dinopony"] + )] + + +class LandstalkerWorld(World): + """ + Landstalker: The Treasures of King Nole is a classic Action-RPG with an isometric view (also known as "2.5D"). + You play Nigel, a treasure hunter exploring the island of Mercator trying to find the legendary treasure. + Roam freely on the island, get stronger to beat dungeons and gather the required key items in order to reach the + hidden palace and claim the treasure. + """ + game = "Landstalker - The Treasures of King Nole" + options_dataclass = LandstalkerOptions + options: LandstalkerOptions + required_client_version = (0, 4, 4) + web = LandstalkerWeb() + + item_name_to_id = build_item_name_to_id_table() + location_name_to_id = build_location_name_to_id_table() + + cached_spheres: ClassVar[List[Set[Location]]] + + def __init__(self, multiworld, player): + super().__init__(multiworld, player) + self.regions_table: Dict[str, LandstalkerRegion] = {} + self.dark_dungeon_id = "None" + self.dark_region_ids = [] + self.teleport_tree_pairs = [] + self.jewel_items = [] + + def fill_slot_data(self) -> dict: + # Generate hints. + self.adjust_shop_prices() + hints = Hints.generate_random_hints(self) + hints["Lithograph"] = Hints.generate_lithograph_hint(self) + hints["Oracle Stone"] = f"It shows {self.dark_dungeon_id}\nenshrouded in darkness." + + # Put options, locations' contents and some additional data inside slot data + options = [ + "goal", "jewel_count", "progressive_armors", "use_record_book", "use_spell_book", "shop_prices_factor", + "combat_difficulty", "teleport_tree_requirements", "shuffle_trees", "ensure_ekeeke_in_shops", + "remove_gumi_boulder", "allow_whistle_usage_behind_trees", "handle_damage_boosting_in_logic", + "handle_enemy_jumping_in_logic", "handle_tree_cutting_glitch_in_logic", "hint_count", "death_link", + "revive_using_ekeeke", + ] + + slot_data = self.options.as_dict(*options) + slot_data["spawn_region"] = self.options.spawn_region.current_key + slot_data["seed"] = self.random.randint(0, 2 ** 32 - 1) + slot_data["dark_region"] = self.dark_dungeon_id + slot_data["hints"] = hints + slot_data["teleport_tree_pairs"] = [[pair[0]["name"], pair[1]["name"]] for pair in self.teleport_tree_pairs] + + # Type hinting for location. + location: LandstalkerLocation + slot_data["location_prices"] = { + location.name: location.price for location in self.multiworld.get_locations(self.player) if location.price} + + return slot_data + + def generate_early(self): + # Randomly pick a set of dark regions where Lantern is needed + darkenable_regions = get_darkenable_regions() + self.dark_dungeon_id = self.random.choice(list(darkenable_regions)) + self.dark_region_ids = darkenable_regions[self.dark_dungeon_id] + + def create_regions(self): + self.regions_table = Regions.create_regions(self) + Locations.create_locations(self.player, self.regions_table, self.location_name_to_id) + self.create_teleportation_trees() + + def create_item(self, name: str, classification_override: Optional[ItemClassification] = None) -> LandstalkerItem: + data = item_table[name] + classification = classification_override or data.classification + item = LandstalkerItem(name, classification, BASE_ITEM_ID + data.id, self.player) + item.price_in_shops = data.price_in_shops + return item + + def create_event(self, name: str) -> LandstalkerItem: + return LandstalkerItem(name, ItemClassification.progression, None, self.player) + + def get_filler_item_name(self) -> str: + return "EkeEke" + + def create_items(self): + item_pool: List[LandstalkerItem] = [] + for name, data in item_table.items(): + # If item is an armor and progressive armors are enabled, transform it into a progressive armor item + if self.options.progressive_armors and "Breast" in name: + name = "Progressive Armor" + item_pool += [self.create_item(name) for _ in range(data.quantity)] + + # If the appropriate setting is on, place one EkeEke in one shop in every town in the game + if self.options.ensure_ekeeke_in_shops: + shops_to_fill = [ + "Massan: Shop item #1", + "Gumi: Inn item #1", + "Ryuma: Inn item", + "Mercator: Shop item #1", + "Verla: Shop item #1", + "Destel: Inn item", + "Route to Lake Shrine: Greedly's shop item #1", + "Kazalt: Shop item #1" + ] + for location_name in shops_to_fill: + self.multiworld.get_location(location_name, self.player).place_locked_item(self.create_item("EkeEke")) + + # Add a fixed amount of progression Life Stock for a specific requirement (Fahl) + fahl_lifestock_req = 15 + item_pool += [self.create_item("Life Stock", ItemClassification.progression) for _ in range(fahl_lifestock_req)] + # Add a unique progression EkeEke for a specific requirement (Cutter) + item_pool.append(self.create_item("EkeEke", ItemClassification.progression)) + + # Add a variable amount of "useful" Life Stock to the pool, depending on the amount of starting Life Stock + # (i.e. on the starting location) + starting_lifestocks = self.get_starting_health() - 4 + lifestock_count = 80 - starting_lifestocks - fahl_lifestock_req + item_pool += [self.create_item("Life Stock") for _ in range(lifestock_count)] + + # Add jewels to the item pool depending on the number of jewels set in generation settings + self.jewel_items = [self.create_item(name) for name in self.get_jewel_names(self.options.jewel_count)] + item_pool += self.jewel_items + + # Add a pre-placed fake win condition item + self.multiworld.get_location("End", self.player).place_locked_item(self.create_event("King Nole's Treasure")) + + # Fill the rest of the item pool with EkeEke + remaining_items = len(self.multiworld.get_unfilled_locations(self.player)) - len(item_pool) + item_pool += [self.create_item(self.get_filler_item_name()) for _ in range(remaining_items)] + + self.multiworld.itempool += item_pool + + def create_teleportation_trees(self): + self.teleport_tree_pairs = load_teleport_trees() + + def pairwise(iterable): + """Yields pairs of elements from the given list -> [0,1], [2,3]...""" + a = iter(iterable) + return zip(a, a) + + # Shuffle teleport tree pairs if the matching setting is on + if self.options.shuffle_trees: + all_trees = [item for pair in self.teleport_tree_pairs for item in pair] + self.random.shuffle(all_trees) + self.teleport_tree_pairs = [[x, y] for x, y in pairwise(all_trees)] + + # If a specific setting is set, teleport trees are potentially active without visiting both sides. + # This means we need to add those as explorable paths for the generation algorithm. + teleport_trees_mode = self.options.teleport_tree_requirements.value + created_entrances = [] + if teleport_trees_mode in [TeleportTreeRequirements.option_none, TeleportTreeRequirements.option_clear_tibor]: + for pair in self.teleport_tree_pairs: + entrances = create_entrance(pair[0]["region"], pair[1]["region"], True, self.regions_table) + created_entrances += entrances + + # Teleport trees are open but require access to Tibor to work + if teleport_trees_mode == TeleportTreeRequirements.option_clear_tibor: + for entrance in created_entrances: + entrance.access_rule = make_path_requirement_lambda(self.player, [], [self.regions_table["tibor"]]) + + def set_rules(self): + Rules.create_rules(self) + + # In "Reach Kazalt" goal, player doesn't have access to Kazalt, King Nole's Labyrinth & King Nole's Palace. + # As a consequence, all locations inside those regions must be excluded, and the teleporter from + # King Nole's Cave to Kazalt must go to the end region instead. + if self.options.goal == LandstalkerGoal.option_reach_kazalt: + kazalt_tp = self.multiworld.get_entrance("king_nole_cave -> kazalt", self.player) + kazalt_tp.connected_region = self.regions_table["end"] + + excluded_regions = [ + "kazalt", + "king_nole_labyrinth_pre_door", + "king_nole_labyrinth_post_door", + "king_nole_labyrinth_exterior", + "king_nole_labyrinth_fall_from_exterior", + "king_nole_labyrinth_raft_entrance", + "king_nole_labyrinth_raft", + "king_nole_labyrinth_sacred_tree", + "king_nole_labyrinth_path_to_palace", + "king_nole_palace" + ] + + for location in self.multiworld.get_locations(self.player): + if location.parent_region.name in excluded_regions: + location.progress_type = LocationProgressType.EXCLUDED + + def get_starting_health(self): + spawn_id = self.options.spawn_region.current_key + if spawn_id == "destel": + return 20 + elif spawn_id == "verla": + return 16 + elif spawn_id in ["waterfall", "mercator", "greenmaze"]: + return 10 + else: + return 4 + + @classmethod + def stage_post_fill(cls, multiworld): + # Cache spheres for hint calculation after fill completes. + cls.cached_spheres = list(multiworld.get_spheres()) + + @classmethod + def stage_modify_multidata(cls, *_): + # Clean up all references in cached spheres after generation completes. + del cls.cached_spheres + + def adjust_shop_prices(self): + # Calculate prices for items in shops once all items have their final position + unknown_items_price = 250 + earlygame_price_factor = 1.0 + endgame_price_factor = 2.0 + factor_diff = endgame_price_factor - earlygame_price_factor + + global_price_factor = self.options.shop_prices_factor / 100.0 + + spheres = self.cached_spheres + sphere_count = len(spheres) + for sphere_id, sphere in enumerate(spheres): + location: LandstalkerLocation # after conditional, we guarantee it's this kind of location. + for location in sphere: + if location.player != self.player or location.type_string != "shop": + continue + + current_playthrough_progression = sphere_id / sphere_count + progression_price_factor = earlygame_price_factor + (current_playthrough_progression * factor_diff) + + price = location.item.price_in_shops \ + if location.item.game == "Landstalker - The Treasures of King Nole" else unknown_items_price + price *= progression_price_factor + price *= global_price_factor + price -= price % 5 + price = max(price, 5) + location.price = int(price) + + @staticmethod + def get_jewel_names(count: JewelCount): + if count < 6: + return ["Red Jewel", "Purple Jewel", "Green Jewel", "Blue Jewel", "Yellow Jewel"][:count] + + return ["Kazalt Jewel"] * count diff --git a/worlds/landstalker/data/hint_source.py b/worlds/landstalker/data/hint_source.py new file mode 100644 index 000000000000..4f22cac4bdd6 --- /dev/null +++ b/worlds/landstalker/data/hint_source.py @@ -0,0 +1,1989 @@ +HINT_SOURCES_JSON = [ + { + "description": "Lithograph", + "smallTextbox": True + }, + { + "description": "Oracle Stone", + "smallTextbox": True + }, + { + "description": "Mercator fortune teller", + "textIds": [ + 654 + ] + }, + { + "description": "King Nole's Cave sign", + "textIds": [ + 253 + ] + }, + { + "description": "Foxy (next to Ryuma's mayor house)", + "entity": { + "mapId": 611, + "position": { + "x": 47, + "y": 25, + "z": 3 + }, + "orientation": "sw" + }, + "nodeId": "ryuma" + }, + { + "description": "Foxy (behind trees in Gumi)", + "entity": { + "mapId": [602, 603], + "position": { + "x": 24, + "y": 35, + "z": 6 + }, + "orientation": "sw" + }, + "nodeId": "gumi" + }, + { + "description": "Foxy (next to Mercator gates)", + "entity": { + "mapId": 454, + "position": { + "x": 18, + "y": 46, + "z": 0 + }, + "orientation": "se" + }, + "nodeId": "route_gumi_ryuma" + }, + { + "description": "Foxy (near basin behind Mercator)", + "entity": { + "mapId": 636, + "position": { + "x": 18, + "y": 27, + "z": 1 + }, + "orientation": "nw" + }, + "nodeId": "mercator" + }, + { + "description": "Foxy (near cabin on Verla Shore)", + "entity": { + "mapId": 468, + "position": { + "x": 24, + "y": 45, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "verla_shore" + }, + { + "description": "Foxy (outside Verla Mines entrance)", + "entity": { + "mapId": 470, + "position": { + "x": 24, + "y": 29, + "z": 5 + }, + "orientation": "sw" + }, + "nodeId": "verla_shore" + }, + { + "description": "Foxy (room below Thieves Hideout summit)", + "entity": { + "mapId": 221, + "position": { + "x": 29, + "y": 19, + "z": 2 + }, + "orientation": "nw" + }, + "nodeId": "thieves_hideout_post_key" + }, + { + "description": "Foxy (near waterfall in Mountainous Area)", + "entity": { + "mapId": 485, + "position": { + "x": 42, + "y": 62, + "z": 2 + }, + "orientation": "nw", + "highPalette": True + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (in Mercator Castle left court)", + "entity": { + "mapId": 32, + "position": { + "x": 36, + "y": 38, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "mercator" + }, + { + "description": "Foxy (on Mercator inn balcony)", + "entity": { + "mapId": 632, + "position": { + "x": 19, + "y": 35, + "z": 4 + }, + "orientation": "se" + }, + "nodeId": "mercator" + }, + { + "description": "Foxy (on a beach between Ryuma and Mercator)", + "entity": { + "mapId": 450, + "position": { + "x": 18, + "y": 28, + "z": 0 + }, + "orientation": "nw" + }, + "nodeId": "route_gumi_ryuma" + }, + { + "description": "Foxy (atop Ryuma's lighthouse)", + "entity": { + "mapId": [628, 629], + "position": { + "x": 26, + "y": 21, + "z": 1 + }, + "orientation": "ne" + }, + "nodeId": "ryuma" + }, + { + "description": "Foxy (looking at dead man in Thieves Hideout)", + "entity": { + "mapId": 210, + "position": { + "x": 25, + "y": 20, + "z": 2 + }, + "orientation": "se" + }, + "nodeId": "thieves_hideout_pre_key" + }, + { + "description": "Foxy (contemplating water near goddess statue in Thieves Hideout)", + "entity": { + "mapId": [219, 220], + "position": { + "x": 36, + "y": 31, + "z": 2 + }, + "orientation": "se" + }, + "nodeId": "thieves_hideout_pre_key" + }, + { + "description": "Foxy (after timed trial in Thieves Hideout)", + "entity": { + "mapId": 196, + "position": { + "x": 49, + "y": 24, + "z": 10 + }, + "orientation": "sw" + }, + "nodeId": "thieves_hideout_post_key" + }, + { + "description": "Foxy (inside Mercator Castle armory tower)", + "entity": { + "mapId": 106, + "position": { + "x": 31, + "y": 30, + "z": 4 + }, + "orientation": "nw" + }, + "nodeId": "mercator" + }, + { + "description": "Foxy (near Mercator Castle kitchen)", + "entity": { + "mapId": 71, + "position": { + "x": 15, + "y": 19, + "z": 1 + }, + "orientation": "nw" + }, + "nodeId": "mercator" + }, + { + "description": "Foxy (in Mercator Castle library)", + "entity": { + "mapId": 73, + "position": { + "x": 18, + "y": 29, + "z": 0 + }, + "orientation": "nw" + }, + "nodeId": "mercator" + }, + { + "description": "Foxy (in Mercator Dungeon main room)", + "entity": { + "mapId": 38, + "position": { + "x": 24, + "y": 35, + "z": 3 + }, + "orientation": "se" + }, + "nodeId": "mercator_dungeon" + }, + { + "description": "Foxy (in hallway before tower in Mercator Dungeon)", + "entity": { + "mapId": 46, + "position": { + "x": 24, + "y": 13, + "z": 0 + }, + "orientation": "sw" + }, + "nodeId": "mercator_dungeon" + }, + { + "description": "Foxy (atop Mercator Dungeon tower)", + "entity": { + "mapId": 35, + "position": { + "x": 31, + "y": 31, + "z": 12 + }, + "orientation": "nw" + }, + "nodeId": "mercator_dungeon" + }, + { + "description": "Foxy (inside Mercator Crypt)", + "entity": { + "mapId": 647, + "position": { + "x": 30, + "y": 21, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "crypt" + }, + { + "description": "Foxy (on Verla beach)", + "entity": { + "mapId": 474, + "position": { + "x": 43, + "y": 30, + "z": 0 + }, + "orientation": "sw" + }, + "nodeId": "verla_shore" + }, + { + "description": "Foxy (spying on house in Verla)", + "entity": { + "mapId": [711, 712], + "position": { + "x": 48, + "y": 29, + "z": 5 + }, + "orientation": "nw" + }, + "nodeId": "verla" + }, + { + "description": "Foxy (on upper Verla shore, reachable from Dex exit)", + "entity": { + "mapId": 530, + "position": { + "x": 18, + "y": 29, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "verla_mines" + }, + { + "description": "Foxy (in Verla Mines jar staircase room)", + "entity": { + "mapId": 235, + "position": { + "x": 42, + "y": 22, + "z": 6 + }, + "orientation": "sw" + }, + "nodeId": "verla_mines" + }, + { + "description": "Foxy (in Verla Mines lizards and crates room)", + "entity": { + "mapId": 239, + "position": { + "x": 32, + "y": 31, + "z": 3, + "halfX": True, + "halfY": True + }, + "orientation": "ne" + }, + "nodeId": "verla_mines" + }, + { + "description": "Foxy (in Verla Mines lava room in Slasher sector)", + "entity": { + "mapId": 252, + "position": { + "x": 16, + "y": 13, + "z": 1, + "halfX": True, + "halfY": True + }, + "orientation": "sw" + }, + "nodeId": "verla_mines" + }, + { + "description": "Foxy (in Verla Mines room behind lava)", + "entity": { + "mapId": 265, + "position": { + "x": 13, + "y": 16, + "z": 0 + }, + "orientation": "se" + }, + "nodeId": "verla_mines" + }, + { + "description": "Foxy (in Verla Mines lava room in Marley sector)", + "entity": { + "mapId": 264, + "position": { + "x": 18, + "y": 19, + "z": 6, + "halfX": True, + "halfY": True + }, + "orientation": "sw" + }, + "nodeId": "verla_mines" + }, + { + "description": "Foxy (on small rocky ledge in elevator map near Kelketo shop)", + "entity": { + "mapId": 473, + "position": { + "x": 35, + "y": 25, + "z": 8 + }, + "orientation": "se" + }, + "nodeId": "route_verla_destel" + }, + { + "description": "Foxy (contemplating fast currents below Kelketo shop)", + "entity": { + "mapId": 481, + "position": { + "x": 40, + "y": 48, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "route_verla_destel" + }, + { + "description": "Foxy (in Destel)", + "entity": { + "mapId": 726, + "position": { + "x": 48, + "y": 55, + "z": 5 + }, + "orientation": "sw" + }, + "nodeId": "destel" + }, + { + "description": "Foxy (contemplating water near boatmaker house in route after Destel)", + "entity": { + "mapId": 489, + "position": { + "x": 23, + "y": 20, + "z": 1 + }, + "orientation": "ne" + }, + "nodeId": "route_after_destel" + }, + { + "description": "Foxy (looking at Lake Shrine from elevated viewpoint)", + "entity": { + "mapId": 525, + "position": { + "x": 53, + "y": 45, + "z": 5 + }, + "orientation": "ne" + }, + "nodeId": "route_after_destel" + }, + { + "description": "Foxy (on small floating block in Destel Well)", + "entity": { + "mapId": 275, + "position": { + "x": 27, + "y": 36, + "z": 5 + }, + "orientation": "nw" + }, + "nodeId": "destel_well" + }, + { + "description": "Foxy (in Destel Well watery hub room)", + "entity": { + "mapId": 283, + "position": { + "x": 34, + "y": 41, + "z": 2 + }, + "orientation": "nw" + }, + "nodeId": "destel_well" + }, + { + "description": "Foxy (in Destel Well watery room before boss)", + "entity": { + "mapId": 287, + "position": { + "x": 50, + "y": 46, + "z": 8, + "halfX": True, + "halfY": True + }, + "orientation": "nw" + }, + "nodeId": "destel_well" + }, + { + "description": "Foxy (at Destel Well exit on Lake Shrine side)", + "entity": { + "mapId": 545, + "position": { + "x": 58, + "y": 18, + "z": 0 + }, + "orientation": "sw" + }, + "nodeId": "route_lake_shrine" + }, + { + "description": "Foxy (at crossroads on route to Lake Shrine)", + "entity": { + "mapId": 515, + "position": { + "x": 30, + "y": 20, + "z": 4 + }, + "orientation": "nw" + }, + "nodeId": "route_lake_shrine" + }, + { + "description": "Foxy (on mountainous path to Lake Shrine)", + "entity": { + "mapId": 514, + "position": { + "x": 57, + "y": 24, + "z": 1 + }, + "orientation": "sw" + }, + "nodeId": "route_lake_shrine" + }, + { + "description": "Foxy (in volcano to Lake Shrine)", + "entity": { + "mapId": 522, + "position": { + "x": 50, + "y": 39, + "z": 6, + "halfX": True, + "halfY": True + }, + "orientation": "nw" + }, + "nodeId": "route_lake_shrine" + }, + { + "description": "Foxy (next to Lake Shrine door)", + "entity": { + "mapId": 524, + "position": { + "x": 24, + "y": 51, + "z": 2, + "halfX": True + }, + "orientation": "nw" + }, + "nodeId": "lake_shrine" + }, + { + "description": "Foxy (above Greedly's shop)", + "entity": { + "mapId": 503, + "position": { + "x": 23, + "y": 35, + "z": 8 + }, + "orientation": "se" + }, + "nodeId": "route_lake_shrine" + }, + { + "description": "Foxy (contemplating water near Greedly's teleport tree)", + "entity": { + "mapId": 501, + "position": { + "x": 30, + "y": 26, + "z": 5 + }, + "orientation": "sw" + }, + "nodeId": "route_lake_shrine" + }, + { + "description": "Foxy (in room after golem hops riddle in Lake Shrine)", + "entity": { + "mapId": 298, + "position": { + "x": 21, + "y": 19, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "lake_shrine" + }, + { + "description": "Foxy (in room next to green golem roundabout in Lake Shrine)", + "entity": { + "mapId": 293, + "position": { + "x": 19, + "y": 18, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "lake_shrine" + }, + { + "description": "Foxy (in Lake Shrine 'throne room')", + "entity": { + "mapId": 327, + "position": { + "x": 31, + "y": 31, + "z": 2 + }, + "orientation": "ne" + }, + "nodeId": "lake_shrine" + }, + { + "description": "Foxy (in room next to golden golems roundabout in Lake Shrine)", + "entity": { + "mapId": 353, + "position": { + "x": 31, + "y": 20, + "z": 4 + }, + "orientation": "sw" + }, + "nodeId": "lake_shrine" + }, + { + "description": "Foxy (in room near white golems roundabout in Lake Shrine)", + "entity": { + "mapId": 329, + "position": { + "x": 25, + "y": 25, + "z": 2, + "halfY": True + }, + "orientation": "nw" + }, + "nodeId": "lake_shrine" + }, + { + "description": "Foxy (next to Mir Tower)", + "entity": { + "mapId": 475, + "position": { + "x": 34, + "y": 17, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "mir_tower_sector" + }, + { + "description": "Foxy (on the way to Mir Tower)", + "entity": { + "mapId": 464, + "position": { + "x": 22, + "y": 40, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "mir_tower_sector" + }, + { + "description": "Foxy (near Twinkle Village)", + "entity": { + "mapId": 461, + "position": { + "x": 20, + "y": 21, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "mir_tower_sector" + }, + { + "description": "Foxy (inside Tibor)", + "entity": { + "mapId": 813, + "position": { + "x": 19, + "y": 32, + "z": 2 + }, + "orientation": "se" + }, + "nodeId": "tibor" + }, + { + "description": "Foxy (inside Tibor spikeballs room)", + "entity": { + "mapId": 810, + "position": { + "x": 21, + "y": 33, + "z": 2, + "halfX": True, + "halfY": True + }, + "orientation": "ne" + }, + "nodeId": "tibor" + }, + { + "description": "Foxy (near Kado's house)", + "entity": { + "mapId": 430, + "position": { + "x": 24, + "y": 27, + "z": 11 + }, + "orientation": "se" + }, + "nodeId": "route_gumi_ryuma" + }, + { + "description": "Foxy (in Gumi boulder map)", + "entity": { + "mapId": 449, + "position": { + "x": 48, + "y": 20, + "z": 1, + "halfX": True + }, + "orientation": "sw" + }, + "nodeId": "route_gumi_ryuma" + }, + { + "description": "Foxy (at Waterfall Shrine crossroads)", + "entity": { + "mapId": 425, + "position": { + "x": 22, + "y": 56, + "z": 0, + "halfX": True + }, + "orientation": "sw" + }, + "nodeId": "route_massan_gumi" + }, + { + "description": "Foxy (in upstairs room inside Waterfall Shrine)", + "entity": { + "mapId": 182, + "position": { + "x": 29, + "y": 19, + "z": 4 + }, + "orientation": "nw" + }, + "nodeId": "waterfall_shrine" + }, + { + "description": "Foxy (inside Waterfall Shrine pit)", + "entity": { + "mapId": 174, + "position": { + "x": 32, + "y": 29, + "z": 1 + }, + "orientation": "sw" + }, + "nodeId": "waterfall_shrine" + }, + { + "description": "Foxy (in Massan)", + "entity": { + "mapId": 592, + "position": { + "x": 24, + "y": 46, + "z": 0, + "halfY": True + }, + "orientation": "se" + }, + "nodeId": "massan" + }, + { + "description": "Foxy (in room at the bottom of ladders in Massan Cave)", + "entity": { + "mapId": 805, + "position": { + "x": 34, + "y": 30, + "z": 2, + "halfY": True + }, + "orientation": "se" + }, + "nodeId": "massan_cave" + }, + { + "description": "Foxy (in treasure room of Massan Cave)", + "entity": { + "mapId": 807, + "position": { + "x": 28, + "y": 22, + "z": 1 + }, + "orientation": "sw" + }, + "nodeId": "massan_cave" + }, + { + "description": "Foxy (bathing in the swamp next to Swamp Shrine entrance)", + "entity": { + "mapId": 433, + "position": { + "x": 39, + "y": 20, + "z": 0 + }, + "orientation": "sw" + }, + "nodeId": "massan_cave" + }, + { + "description": "Foxy (in side room of Swamp Shrine accessible without Idol Stone)", + "entity": { + "mapId": 10, + "position": { + "x": 25, + "y": 27, + "z": 2, + "halfX": True + }, + "orientation": "ne" + }, + "nodeId": "route_massan_gumi" + }, + { + "description": "Foxy (in wooden room with falling EkeEke chest in Swamp Shrine)", + "entity": { + "mapId": 7, + "position": { + "x": 29, + "y": 25, + "z": 1, + "halfY": True + }, + "orientation": "nw" + }, + "nodeId": "swamp_shrine" + }, + { + "description": "Foxy (in Swamp Shrine carpet room)", + "entity": { + "mapId": 2, + "position": { + "x": 19, + "y": 33, + "z": 4 + }, + "orientation": "se" + }, + "nodeId": "swamp_shrine" + }, + { + "description": "Foxy (in Swamp Shrine spikeball storage room)", + "entity": { + "mapId": 16, + "position": { + "x": 25, + "y": 24, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "swamp_shrine" + }, + { + "description": "Foxy (in Swamp Shrine spiked floor room)", + "entity": { + "mapId": 21, + "position": { + "x": 27, + "y": 17, + "z": 4 + }, + "orientation": "sw" + }, + "nodeId": "swamp_shrine" + }, + { + "description": "Foxy (in Mercator Castle backdoor court)", + "entity": { + "mapId": 639, + "position": { + "x": 23, + "y": 15, + "z": 0 + }, + "orientation": "sw" + }, + "nodeId": "mercator" + }, + { + "description": "Foxy (on Greenmaze / Mountainous Area crossroad)", + "entity": { + "mapId": 460, + "position": { + "x": 16, + "y": 27, + "z": 4 + }, + "orientation": "se" + }, + "nodeId": "greenmaze_pre_whistle" + }, + { + "description": "Foxy (below Mountainous Area bridge)", + "entity": { + "mapId": 486, + "position": { + "x": 52, + "y": 45, + "z": 5 + }, + "orientation": "se" + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (in Mountainous Area isolated cave)", + "entity": { + "mapId": 553, + "position": { + "x": 23, + "y": 21, + "z": 3 + }, + "orientation": "ne" + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (in access to Zak arena inside Mountainous Area)", + "entity": { + "mapId": 487, + "position": { + "x": 44, + "y": 51, + "z": 3 + }, + "orientation": "se" + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (in Zak arena inside Mountainous Area)", + "entity": { + "mapId": 492, + "position": { + "x": 27, + "y": 55, + "z": 9 + }, + "orientation": "se" + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (in empty secret room inside Mountainous Area cave)", + "entity": { + "mapId": 552, + "position": { + "x": 24, + "y": 27, + "z": 0, + "halfX": True + }, + "orientation": "sw" + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (in empty visible room inside Mountainous Area cave)", + "entity": { + "mapId": 547, + "position": { + "x": 23, + "y": 23, + "z": 0, + "halfY": True + }, + "orientation": "se" + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (in waterfall entrance of Mountainous Area cave)", + "entity": { + "mapId": 549, + "position": { + "x": 27, + "y": 40, + "z": 0 + }, + "orientation": "se" + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (on Mir Tower sector crossroads)", + "entity": { + "mapId": 458, + "position": { + "x": 21, + "y": 21, + "z": 1 + }, + "orientation": "se", + "highPalette": True + }, + "nodeId": "mir_tower_sector" + }, + { + "description": "Foxy (near Mountainous Area teleport tree)", + "entity": { + "mapId": 484, + "position": { + "x": 38, + "y": 57, + "z": 0 + }, + "orientation": "sw", + "highPalette": True + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (on route to Mountainous Area, in rocky arch map)", + "entity": { + "mapId": 500, + "position": { + "x": 19, + "y": 19, + "z": 7 + }, + "orientation": "sw", + "highPalette": True + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (on route to Mountainous Area, in L-shaped turn map)", + "entity": { + "mapId": 540, + "position": { + "x": 16, + "y": 23, + "z": 3 + }, + "orientation": "se", + "halfY": True, + "highPalette": True + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (in map next to Mountainous Area goddess statue)", + "entity": { + "mapId": 518, + "position": { + "x": 38, + "y": 33, + "z": 12 + }, + "orientation": "sw", + "highPalette": True + }, + "nodeId": "mountainous_area" + }, + { + "description": "Foxy (in King Nole's Cave isolated chest room)", + "entity": { + "mapId": 156, + "position": { + "x": 21, + "y": 27, + "z": 0, + "halfX": True + }, + "orientation": "ne" + }, + "nodeId": "king_nole_cave" + }, + { + "description": "Foxy (in King Nole's Cave crate stairway room)", + "entity": { + "mapId": 158, + "position": { + "x": 29, + "y": 26, + "z": 6 + }, + "orientation": "sw", + "highPalette": True + }, + "nodeId": "king_nole_cave" + }, + { + "description": "Foxy (in room before boulder hallway inside King Nole's Cave)", + "entity": { + "mapId": 147, + "position": { + "x": 26, + "y": 23, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "king_nole_cave" + }, + { + "description": "Foxy (in empty isolated room inside King Nole's Cave)", + "entity": { + "mapId": 162, + "position": { + "x": 26, + "y": 17, + "z": 0, + "halfX": True + }, + "orientation": "sw" + }, + "nodeId": "king_nole_cave" + }, + { + "description": "Foxy (looking at the waterfall in King Nole's Cave)", + "entity": { + "mapId": 164, + "position": { + "x": 22, + "y": 48, + "z": 1 + }, + "orientation": "sw", + "highPalette": True + }, + "nodeId": "king_nole_cave" + }, + { + "description": "Foxy (in King Nole's Cave teleporter to Kazalt)", + "entity": { + "mapId": 170, + "position": { + "x": 22, + "y": 27, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "king_nole_cave" + }, + { + "description": "Foxy (in access to Kazalt)", + "entity": { + "mapId": 739, + "position": { + "x": 17, + "y": 28, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "kazalt" + }, + { + "description": "Foxy (on Kazalt bridge)", + "entity": { + "mapId": 737, + "position": { + "x": 46, + "y": 34, + "z": 7 + }, + "orientation": "se" + }, + "nodeId": "kazalt" + }, + { + "description": "Foxy (in Mir Tower 0F isolated chest room)", + "entity": { + "mapId": 757, + "position": { + "x": 19, + "y": 24, + "z": 0 + }, + "orientation": "se" + }, + "nodeId": "mir_tower_pre_garlic" + }, + { + "description": "Foxy (in Mir Tower activatable bridge room)", + "entity": { + "mapId": [752, 753], + "position": { + "x": 29, + "y": 34, + "z": 3, + "halfX": True + }, + "orientation": "sw" + }, + "nodeId": "mir_tower_pre_garlic" + }, + { + "description": "Foxy (in Garlic trial room inside Mir Tower)", + "entity": { + "mapId": 750, + "position": { + "x": 22, + "y": 21, + "z": 4 + }, + "orientation": "sw" + }, + "nodeId": "mir_tower_pre_garlic" + }, + { + "description": "Foxy (in Mir Tower library)", + "entity": { + "mapId": 759, + "position": { + "x": 38, + "y": 29, + "z": 4 + }, + "orientation": "ne" + }, + "nodeId": "mir_tower_post_garlic" + }, + { + "description": "Foxy (in Mir Tower priest room)", + "entity": { + "mapId": 775, + "position": { + "x": 23, + "y": 22, + "z": 1, + "halfX": True + }, + "orientation": "sw" + }, + "nodeId": "mir_tower_post_garlic" + }, + { + "description": "Foxy (right after making Miro flee with Garlic in Mir Tower)", + "entity": { + "mapId": 758, + "position": { + "x": 14, + "y": 34, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "mir_tower_post_garlic" + }, + { + "description": "Foxy (in falling spikeballs room inside Mir Tower)", + "entity": { + "mapId": 761, + "position": { + "x": 14, + "y": 24, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "mir_tower_post_garlic" + }, + { + "description": "Foxy (in first room of Mir Tower teleporter maze)", + "entity": { + "mapId": 767, + "position": { + "x": 18, + "y": 18, + "z": 2 + }, + "orientation": "se" + }, + "nodeId": "mir_tower_post_garlic" + }, + { + "description": "Foxy (in small spikeballs room of Mir Tower teleporter maze)", + "entity": { + "mapId": 771, + "position": { + "x": 18, + "y": 18, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "mir_tower_post_garlic" + }, + { + "description": "Foxy (in wooden elevators room after Mir Tower teleporter maze)", + "entity": { + "mapId": 779, + "position": { + "x": 32, + "y": 20, + "z": 7, + "halfY": True + }, + "orientation": "nw" + }, + "nodeId": "mir_tower_post_garlic" + }, + { + "description": "Foxy (in room before Mir Tower boss room)", + "entity": { + "mapId": 783, + "position": { + "x": 32, + "y": 19, + "z": 2 + }, + "orientation": "se" + }, + "nodeId": "mir_tower_post_garlic" + }, + { + "description": "Foxy (in Mir Tower treasure room)", + "entity": { + "mapId": 781, + "position": { + "x": 53, + "y": 26, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "mir_tower_post_garlic" + }, + { + "description": "Foxy (next to Waterfall Shrine entrance)", + "entity": { + "mapId": 426, + "position": { + "x": 46, + "y": 31, + "z": 0, + "halfX": True, + "halfY": True + }, + "orientation": "sw" + }, + "nodeId": "route_massan_gumi" + }, + { + "description": "Foxy (looking at river next to Massan teleport tree)", + "entity": { + "mapId": 424, + "position": { + "x": 44, + "y": 35, + "z": 0 + }, + "orientation": "nw" + }, + "nodeId": "route_massan_gumi" + }, + { + "description": "Foxy (looking at bush at Swamp Shrine crossroads)", + "entity": { + "mapId": 440, + "position": { + "x": 25, + "y": 42, + "z": 4 + }, + "orientation": "nw", + "highPalette": True + }, + "nodeId": "route_massan_gumi" + }, + { + "description": "Foxy (at Helga's Hut crossroads)", + "entity": { + "mapId": 447, + "position": { + "x": 24, + "y": 17, + "z": 1 + }, + "orientation": "se", + "highPalette": True + }, + "nodeId": "route_gumi_ryuma" + }, + { + "description": "Foxy (near Helga's Hut)", + "entity": { + "mapId": 444, + "position": { + "x": 25, + "y": 26, + "z": 7 + }, + "orientation": "sw" + }, + "nodeId": "route_gumi_ryuma" + }, + { + "description": "Foxy (in reapers room at Greenmaze entrance)", + "entity": { + "mapId": 571, + "position": { + "x": 31, + "y": 20, + "z": 6 + }, + "orientation": "nw", + "highPalette": True + }, + "nodeId": "greenmaze_pre_whistle" + }, + { + "description": "Foxy (near Greenmaze swamp)", + "entity": { + "mapId": 566, + "position": { + "x": 53, + "y": 51, + "z": 1 + }, + "orientation": "ne" + }, + "nodeId": "greenmaze_pre_whistle" + }, + { + "description": "Foxy (spying on Cutter in Greenmaze)", + "entity": { + "mapId": 560, + "position": { + "x": 31, + "y": 52, + "z": 9 + }, + "orientation": "nw" + }, + "nodeId": "greenmaze_pre_whistle" + }, + { + "description": "Foxy (in sector with red orcs making an elevator appear in Greenmaze)", + "entity": { + "mapId": 565, + "position": { + "x": 50, + "y": 30, + "z": 1 + }, + "orientation": "se" + }, + "nodeId": "greenmaze_pre_whistle" + }, + { + "description": "Foxy (in center of Greenmaze)", + "entity": { + "mapId": 576, + "position": { + "x": 32, + "y": 38, + "z": 5, + "halfY": True + }, + "orientation": "se" + }, + "nodeId": "greenmaze_pre_whistle" + }, + { + "description": "Foxy (in waterfall sector of Greenmaze)", + "entity": { + "mapId": 568, + "position": { + "x": 29, + "y": 41, + "z": 7, + "halfX": True + }, + "orientation": "ne", + "highPalette": True + }, + "nodeId": "greenmaze_pre_whistle" + }, + { + "description": "Foxy (in ropes sector of Greenmaze)", + "entity": { + "mapId": 567, + "position": { + "x": 38, + "y": 28, + "z": 0 + }, + "orientation": "se" + }, + "nodeId": "greenmaze_pre_whistle" + }, + { + "description": "Foxy (in Sun Stone sector of Greenmaze)", + "entity": { + "mapId": 564, + "position": { + "x": 30, + "y": 35, + "z": 1 + }, + "orientation": "sw" + }, + "nodeId": "greenmaze_pre_whistle" + }, + { + "description": "Foxy (in first chest map of Greenmaze after cutting trees)", + "entity": { + "mapId": 570, + "position": { + "x": 26, + "y": 15, + "z": 1 + }, + "orientation": "sw" + }, + "nodeId": "greenmaze_post_whistle" + }, + { + "description": "Foxy (near shortcut cavern entrance in Greenmaze after cutting trees)", + "entity": { + "mapId": 569, + "position": { + "x": 20, + "y": 24, + "z": 6, + "halfY": True + }, + "orientation": "se" + }, + "nodeId": "greenmaze_post_whistle" + }, + { + "description": "Foxy (in room next to spiked floor and keydoor room in King Nole's Labyrinth)", + "entity": { + "mapId": 380, + "position": { + "x": 17, + "y": 18, + "z": 0 + }, + "orientation": "se" + }, + "nodeId": "king_nole_labyrinth_pre_door" + }, + { + "description": "Foxy (in ice shortcut room in King Nole's Labyrinth)", + "entity": { + "mapId": 390, + "position": { + "x": 19, + "y": 41, + "z": 2 + }, + "orientation": "se" + }, + "nodeId": "king_nole_labyrinth_pre_door" + }, + { + "description": "Foxy (in exterior room of King Nole's Labyrinth)", + "entity": { + "mapId": 362, + "position": { + "x": 35, + "y": 21, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "king_nole_labyrinth_pre_door" + }, + { + "description": "Foxy (in room above Iron Boots in King Nole's Labyrinth)", + "entity": { + "mapId": 373, + "position": { + "x": 26, + "y": 30, + "z": 2 + }, + "orientation": "se" + }, + "nodeId": "king_nole_labyrinth_post_door" + }, + { + "description": "Foxy (next to raft starting point in King Nole's Labyrinth)", + "entity": { + "mapId": 406, + "position": { + "x": 46, + "y": 40, + "z": 7 + }, + "orientation": "nw", + "highPalette": True + }, + "nodeId": "king_nole_labyrinth_raft_entrance" + }, + { + "description": "Foxy (in fast boulder room in King Nole's Labyrinth)", + "entity": { + "mapId": 382, + "position": { + "x": 30, + "y": 30, + "z": 7, + "halfX": True + }, + "orientation": "ne" + }, + "nodeId": "king_nole_labyrinth_post_door" + }, + { + "description": "Foxy (in first maze room inside King Nole's Labyrinth)", + "entity": { + "mapId": 367, + "position": { + "x": 43, + "y": 38, + "z": 1 + }, + "orientation": "sw" + }, + "nodeId": "king_nole_labyrinth_post_door" + }, + { + "description": "Foxy (in lava sector of King Nole's Labyrinth)", + "entity": { + "mapId": 399, + "position": { + "x": 23, + "y": 19, + "z": 2, + "halfY": True + }, + "orientation": "se" + }, + "nodeId": "king_nole_labyrinth_post_door" + }, + { + "description": "Foxy (in hands room inside King Nole's Labyrinth)", + "entity": { + "mapId": 418, + "position": { + "x": 41, + "y": 31, + "z": 7 + }, + "orientation": "sw" + }, + "nodeId": "king_nole_labyrinth_post_door" + }, + { + "description": "Foxy (next to King Nole's Palace entrance)", + "entity": { + "mapId": 422, + "position": { + "x": 27, + "y": 25, + "z": 2 + }, + "orientation": "ne" + }, + "nodeId": "king_nole_labyrinth_path_to_palace" + }, + { + "description": "Foxy (in King Nole's Palace entrance room)", + "entity": { + "mapId": 122, + "position": { + "x": 30, + "y": 35, + "z": 8 + }, + "orientation": "ne" + }, + "nodeId": "king_nole_palace" + }, + { + "description": "Foxy (in King Nole's Palace jar and moving platforms room)", + "entity": { + "mapId": 126, + "position": { + "x": 27, + "y": 37, + "z": 6 + }, + "orientation": "se" + }, + "nodeId": "king_nole_palace" + }, + { + "description": "Foxy (in King Nole's Palace last chest room)", + "entity": { + "mapId": 125, + "position": { + "x": 25, + "y": 39, + "z": 2 + }, + "orientation": "ne" + }, + "nodeId": "king_nole_palace" + }, + { + "description": "Foxy (in Mercator casino)", + "entity": { + "mapId": 663, + "position": { + "x": 16, + "y": 58, + "z": 0, + "halfX": True, + "halfY": True + }, + "orientation": "ne" + }, + "nodeId": "mercator_casino" + }, + { + "description": "Foxy (in Helga's hut basement)", + "entity": { + "mapId": 479, + "position": { + "x": 20, + "y": 33, + "z": 0, + "halfX": True + }, + "orientation": "sw" + }, + "nodeId": "helga_hut" + }, + { + "description": "Foxy (in Helga's hut dungeon deepest room)", + "entity": { + "mapId": 802, + "position": { + "x": 28, + "y": 19, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "helga_hut" + }, + { + "description": "Foxy (in Helga's hut dungeon topmost room)", + "entity": { + "mapId": 786, + "position": { + "x": 25, + "y": 23, + "z": 2, + "halfY": True + }, + "orientation": "se" + }, + "nodeId": "helga_hut" + }, + { + "description": "Foxy (in Swamp Shrine right aisle room)", + "entity": { + "mapId": 1, + "position": { + "x": 34, + "y": 20, + "z": 2 + }, + "orientation": "se", + "highPalette": True + }, + "nodeId": "swamp_shrine" + }, + { + "description": "Foxy (upstairs in Swamp Shrine main hall)", + "entity": { + "mapId": [5, 15], + "position": { + "x": 45, + "y": 24, + "z": 8, + "halfY": True + }, + "orientation": "nw" + }, + "nodeId": "swamp_shrine" + }, + { + "description": "Foxy (in room before boss inside Swamp Shrine)", + "entity": { + "mapId": 30, + "position": { + "x": 19, + "y": 25, + "z": 2, + "halfY": True + }, + "orientation": "se" + }, + "nodeId": "swamp_shrine" + }, + { + "description": "Foxy (in Thieves Hideout entrance room)", + "entity": { + "mapId": [185, 186], + "position": { + "x": 40, + "y": 35, + "z": 2 + }, + "orientation": "se", + "highPalette": True + }, + "nodeId": "thieves_hideout_pre_key" + }, + { + "description": "Foxy (in Thieves Hideout room with hidden door behind waterfall)", + "entity": { + "mapId": [192, 193], + "position": { + "x": 30, + "y": 34, + "z": 1 + }, + "orientation": "nw" + }, + "nodeId": "thieves_hideout_pre_key" + }, + { + "description": "Foxy (in Thieves Hideout double chest room before goddess statue)", + "entity": { + "mapId": 215, + "position": { + "x": 17, + "y": 17, + "z": 0 + }, + "orientation": "sw" + }, + "nodeId": "thieves_hideout_pre_key" + }, + { + "description": "Foxy (in hub room after Thieves Hideout keydoor)", + "entity": { + "mapId": 199, + "position": { + "x": 24, + "y": 52, + "z": 2 + }, + "orientation": "sw" + }, + "nodeId": "thieves_hideout_post_key" + }, + { + "description": "Foxy (in reward room after Thieves Hideout moving balls riddle)", + "entity": { + "mapId": 205, + "position": { + "x": 32, + "y": 24, + "z": 0 + }, + "orientation": "sw" + }, + "nodeId": "thieves_hideout_post_key" + }, + { + "description": "Foxy (in Lake Shrine main hallway)", + "entity": { + "mapId": 302, + "position": { + "x": 20, + "y": 19, + "z": 0 + }, + "orientation": "sw" + }, + "nodeId": "lake_shrine" + }, + { + "description": "Foxy (in triple chest room in Slasher sector of Verla Mines)", + "entity": { + "mapId": 256, + "position": { + "x": 23, + "y": 23, + "z": 0 + }, + "orientation": "sw" + }, + "nodeId": "verla_mines" + }, + { + "description": "Foxy (near teleport tree after Destel)", + "entity": { + "mapId": 488, + "position": { + "x": 28, + "y": 53, + "z": 0 + }, + "orientation": "se" + }, + "nodeId": "route_after_destel" + }, + { + "description": "Foxy (in lower half of mimics room in King Nole's Labyrinth)", + "entity": { + "mapId": 383, + "position": { + "x": 26, + "y": 26, + "z": 2 + }, + "orientation": "nw" + }, + "nodeId": "king_nole_labyrinth_pre_door" + } +] diff --git a/worlds/landstalker/data/item_source.py b/worlds/landstalker/data/item_source.py new file mode 100644 index 000000000000..e0a2d701f4bf --- /dev/null +++ b/worlds/landstalker/data/item_source.py @@ -0,0 +1,2017 @@ +ITEM_SOURCES_JSON = [ + { + "name": "Swamp Shrine (0F): chest in room to the right", + "type": "chest", + "nodeId": "swamp_shrine", + "chestId": 0 + }, + { + "name": "Swamp Shrine (0F): chest in carpet room", + "type": "chest", + "nodeId": "swamp_shrine", + "chestId": 1 + }, + { + "name": "Swamp Shrine (0F): chest in left hallway (accessed by falling from upstairs)", + "type": "chest", + "nodeId": "swamp_shrine", + "chestId": 2 + }, + { + "name": "Swamp Shrine (0F): falling chest after beating orc", + "type": "chest", + "nodeId": "swamp_shrine", + "chestId": 3 + }, + { + "name": "Swamp Shrine (0F): chest in room visible from second entrance", + "type": "chest", + "nodeId": "swamp_shrine", + "chestId": 4 + }, + { + "name": "Swamp Shrine (1F): lower chest in wooden bridges room", + "type": "chest", + "nodeId": "swamp_shrine", + "chestId": 5 + }, + { + "name": "Swamp Shrine (2F): upper chest in wooden bridges room", + "type": "chest", + "nodeId": "swamp_shrine", + "chestId": 6 + }, + { + "name": "Swamp Shrine (2F): chest on spiked floor room balcony", + "type": "chest", + "nodeId": "swamp_shrine", + "chestId": 7 + }, + { + "name": "Swamp Shrine (3F): chest in boss arena", + "type": "chest", + "nodeId": "swamp_shrine", + "chestId": 8 + }, + { + "name": "Mercator Dungeon (-1F): chest on elevated path near entrance", + "type": "chest", + "nodeId": "mercator_dungeon", + "hints": [ + "hidden in the depths of Mercator" + ], + "chestId": 9 + }, + { + "name": "Mercator Dungeon (-1F): chest in Moralis's cell", + "type": "chest", + "nodeId": "mercator_dungeon", + "hints": [ + "hidden in the depths of Mercator" + ], + "chestId": 10 + }, + { + "name": "Mercator Dungeon (-1F): left chest in undeground double chest room", + "type": "chest", + "nodeId": "mercator_dungeon", + "hints": [ + "hidden in the depths of Mercator" + ], + "chestId": 11 + }, + { + "name": "Mercator Dungeon (-1F): right chest in undeground double chest room", + "type": "chest", + "nodeId": "mercator_dungeon", + "hints": [ + "hidden in the depths of Mercator" + ], + "chestId": 12 + }, + { + "name": "Mercator: castle kitchen chest", + "type": "chest", + "nodeId": "mercator", + "chestId": 13 + }, + { + "name": "Mercator: chest in special shop backroom", + "type": "chest", + "nodeId": "mercator", + "chestId": 14 + }, + { + "name": "Mercator Dungeon (1F): left chest in tower double chest room", + "type": "chest", + "nodeId": "mercator_dungeon", + "hints": [ + "inside a tower" + ], + "chestId": 15 + }, + { + "name": "Mercator Dungeon (1F): right chest in tower double chest room", + "type": "chest", + "nodeId": "mercator_dungeon", + "hints": [ + "inside a tower" + ], + "chestId": 16 + }, + { + "name": "Mercator: chest in castle tower (ladder revealed by slashing armor)", + "type": "chest", + "nodeId": "mercator", + "hints": [ + "inside a tower" + ], + "chestId": 17 + }, + { + "name": "Mercator Dungeon (4F): chest in topmost tower room", + "type": "chest", + "nodeId": "mercator_dungeon", + "hints": [ + "inside a tower" + ], + "chestId": 18 + }, + { + "name": "King Nole's Palace: chest at entrance", + "type": "chest", + "nodeId": "king_nole_palace", + "chestId": 19 + }, + { + "name": "King Nole's Palace: chest along central pit", + "type": "chest", + "nodeId": "king_nole_palace", + "chestId": 20 + }, + { + "name": "King Nole's Palace: chest in floating button room", + "type": "chest", + "nodeId": "king_nole_palace", + "chestId": 21 + }, + { + "name": "King Nole's Cave: chest in second room", + "type": "chest", + "nodeId": "king_nole_cave", + "chestId": 22 + }, + { + "name": "King Nole's Cave: first chest in third room", + "type": "chest", + "nodeId": "king_nole_cave", + "chestId": 24 + }, + { + "name": "King Nole's Cave: second chest in third room", + "type": "chest", + "nodeId": "king_nole_cave", + "chestId": 25 + }, + { + "name": "King Nole's Cave: chest in isolated room", + "type": "chest", + "nodeId": "king_nole_cave", + "chestId": 28 + }, + { + "name": "King Nole's Cave: chest in crate room", + "type": "chest", + "nodeId": "king_nole_cave", + "chestId": 29 + }, + { + "name": "King Nole's Cave: boulder chase hallway chest", + "type": "chest", + "nodeId": "king_nole_cave", + "chestId": 31 + }, + { + "name": "Waterfall Shrine: chest under entrance hallway", + "type": "chest", + "nodeId": "waterfall_shrine", + "chestId": 33 + }, + { + "name": "Waterfall Shrine: chest near Prospero", + "type": "chest", + "nodeId": "waterfall_shrine", + "chestId": 34 + }, + { + "name": "Waterfall Shrine: chest on right branch of biggest room", + "type": "chest", + "nodeId": "waterfall_shrine", + "chestId": 35 + }, + { + "name": "Waterfall Shrine: upstairs chest", + "type": "chest", + "nodeId": "waterfall_shrine", + "chestId": 36 + }, + { + "name": "Thieves Hideout: chest under water in entrance room", + "type": "chest", + "nodeId": "thieves_hideout_pre_key", + "chestId": 38 + }, + { + "name": "Thieves Hideout (back): right chest after teal knight mini-boss", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 41 + }, + { + "name": "Thieves Hideout (back): left chest after teal knight mini-boss", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 42 + }, + { + "name": "Thieves Hideout: left chest in Pockets cell", + "type": "chest", + "nodeId": "thieves_hideout_pre_key", + "chestId": 43 + }, + { + "name": "Thieves Hideout: right chest in Pockets cell", + "type": "chest", + "nodeId": "thieves_hideout_pre_key", + "chestId": 44 + }, + { + "name": "Thieves Hideout (back): second chest in hallway after quick climb trial", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 45 + }, + { + "name": "Thieves Hideout (back): first chest in hallway after quick climb trial", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 46 + }, + { + "name": "Thieves Hideout (back): chest in moving platforms room", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 47 + }, + { + "name": "Thieves Hideout (back): chest in falling platforms room", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 48 + }, + { + "name": "Thieves Hideout (back): reward chest after moving balls room", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 49 + }, + { + "name": "Thieves Hideout: rolling boulder chest near entrance", + "type": "chest", + "nodeId": "thieves_hideout_pre_key", + "chestId": 50 + }, + { + "name": "Thieves Hideout: left chest in room on the way to goddess statue", + "type": "chest", + "nodeId": "thieves_hideout_pre_key", + "chestId": 52 + }, + { + "name": "Thieves Hideout: right chest in room on the way to goddess statue", + "type": "chest", + "nodeId": "thieves_hideout_pre_key", + "chestId": 53 + }, + { + "name": "Thieves Hideout (back): left chest in room before boss", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 54 + }, + { + "name": "Thieves Hideout (back): right chest in room before boss", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 55 + }, + { + "name": "Thieves Hideout (back): chest #1 in boss reward room", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 56 + }, + { + "name": "Thieves Hideout (back): chest #2 in boss reward room", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 57 + }, + { + "name": "Thieves Hideout (back): chest #3 in boss reward room", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 58 + }, + { + "name": "Thieves Hideout (back): chest #4 in boss reward room", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 59 + }, + { + "name": "Thieves Hideout (back): chest #5 in boss reward room", + "type": "chest", + "nodeId": "thieves_hideout_post_key", + "chestId": 60 + }, + { + "name": "Verla Mines: right chest in double chest room near entrance", + "type": "chest", + "nodeId": "verla_mines", + "chestId": 66 + }, + { + "name": "Verla Mines: left chest in double chest room near entrance", + "type": "chest", + "nodeId": "verla_mines", + "chestId": 67 + }, + { + "name": "Verla Mines: chest on jar staircase room balcony", + "type": "chest", + "nodeId": "verla_mines", + "chestId": 68 + }, + { + "name": "Verla Mines: Dex reward chest", + "type": "chest", + "nodeId": "verla_mines", + "chestId": 69 + }, + { + "name": "Verla Mines: Slasher reward chest", + "type": "chest", + "nodeId": "verla_mines", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 70 + }, + { + "name": "Verla Mines: left chest in 3-chests room near Slasher", + "type": "chest", + "nodeId": "verla_mines", + "chestId": 71 + }, + { + "name": "Verla Mines: middle chest in 3-chests room near Slasher", + "type": "chest", + "nodeId": "verla_mines", + "chestId": 72 + }, + { + "name": "Verla Mines: right chest in 3-chests room near Slasher", + "type": "chest", + "nodeId": "verla_mines", + "chestId": 73 + }, + { + "name": "Verla Mines: right chest in button room near elevator shaft leading to Marley", + "type": "chest", + "nodeId": "verla_mines", + "chestId": 74 + }, + { + "name": "Verla Mines: left chest in button room near elevator shaft leading to Marley", + "type": "chest", + "nodeId": "verla_mines", + "chestId": 75 + }, + { + "name": "Verla Mines: chest in hidden room accessed by walking on lava", + "type": "chest", + "nodeId": "verla_mines_behind_lava", + "hints": [ + "in a very hot place" + ], + "chestId": 76 + }, + { + "name": "Destel Well (0F): 4 crates puzzle room chest", + "type": "chest", + "nodeId": "destel_well", + "chestId": 77 + }, + { + "name": "Destel Well (1F): chest on small stairs", + "type": "chest", + "nodeId": "destel_well", + "chestId": 78 + }, + { + "name": "Destel Well (1F): chest on narrow floating ground", + "type": "chest", + "nodeId": "destel_well", + "chestId": 79 + }, + { + "name": "Destel Well (1F): chest in spiky hallway", + "type": "chest", + "nodeId": "destel_well", + "chestId": 80 + }, + { + "name": "Destel Well (2F): chest in ghosts room", + "type": "chest", + "nodeId": "destel_well", + "chestId": 81 + }, + { + "name": "Destel Well (2F): chest in falling platforms room", + "type": "chest", + "nodeId": "destel_well", + "chestId": 82 + }, + { + "name": "Destel Well (2F): right chest in Pockets room", + "type": "chest", + "nodeId": "destel_well", + "chestId": 83 + }, + { + "name": "Destel Well (2F): left chest in Pockets room", + "type": "chest", + "nodeId": "destel_well", + "chestId": 84 + }, + { + "name": "Destel Well (3F): chest in first trapped arena", + "type": "chest", + "nodeId": "destel_well", + "chestId": 85 + }, + { + "name": "Destel Well (3F): chest in trapped giants room", + "type": "chest", + "nodeId": "destel_well", + "chestId": 86 + }, + { + "name": "Destel Well (3F): chest in second trapped arena", + "type": "chest", + "nodeId": "destel_well", + "chestId": 87 + }, + { + "name": "Destel Well (4F): top chest in room before boss", + "type": "chest", + "nodeId": "destel_well", + "chestId": 88 + }, + { + "name": "Destel Well (4F): left chest in room before boss", + "type": "chest", + "nodeId": "destel_well", + "chestId": 89 + }, + { + "name": "Destel Well (4F): bottom chest in room before boss", + "type": "chest", + "nodeId": "destel_well", + "chestId": 90 + }, + { + "name": "Destel Well (4F): right chest in room before boss", + "type": "chest", + "nodeId": "destel_well", + "chestId": 91 + }, + { + "name": "Lake Shrine (-1F): chest in crate room near green golem spinner", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 92 + }, + { + "name": "Lake Shrine (-1F): chest in hallway with hole leading downstairs", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 93 + }, + { + "name": "Lake Shrine (-1F): chest in spikeballs hallway near green golem spinner", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 94 + }, + { + "name": "Lake Shrine (-1F): reward chest for golem hopping puzzle", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 95 + }, + { + "name": "Lake Shrine (-2F): chest on room corner accessed by falling from above", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 96 + }, + { + "name": "Lake Shrine (-2F): lower chest in throne room", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 97 + }, + { + "name": "Lake Shrine (-2F): upper chest in throne room", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 98 + }, + { + "name": "Lake Shrine (-3F): chest on floating platform in white golems room", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 99 + }, + { + "name": "Lake Shrine (-3F): chest near Sword of Ice", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 100 + }, + { + "name": "Lake Shrine (-3F): chest in snake trapping puzzle room", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 101 + }, + { + "name": "Lake Shrine (-3F): chest on cube accessed by falling from upstairs", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 102 + }, + { + "name": "Lake Shrine (-3F): chest in watery archway room", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 103 + }, + { + "name": "Lake Shrine (-3F): left reward chest in boss room", + "type": "chest", + "nodeId": "lake_shrine", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 104 + }, + { + "name": "Lake Shrine (-3F): middle reward chest in boss room", + "type": "chest", + "nodeId": "lake_shrine", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 105 + }, + { + "name": "Lake Shrine (-3F): right reward chest in boss room", + "type": "chest", + "nodeId": "lake_shrine", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 106 + }, + { + "name": "Lake Shrine (-3F): chest near golden golems spinner", + "type": "chest", + "nodeId": "lake_shrine", + "chestId": 107 + }, + { + "name": "King Nole's Labyrinth (0F): chest in exterior room", + "type": "chest", + "nodeId": "king_nole_labyrinth_exterior", + "chestId": 108 + }, + { + "name": "King Nole's Labyrinth (0F): left chest in room after key door", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 109 + }, + { + "name": "King Nole's Labyrinth (0F): right chest in room after key door", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 110 + }, + { + "name": "King Nole's Labyrinth (-1F): chest in maze room with healing tile", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 111 + }, + { + "name": "King Nole's Labyrinth (0F): chest in spike balls room", + "type": "chest", + "nodeId": "king_nole_labyrinth_pre_door", + "chestId": 112 + }, + { + "name": "King Nole's Labyrinth (-1F): right chest in 3-chest dark room (left side)", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 113 + }, + { + "name": "King Nole's Labyrinth (-1F): chest in 3-chest dark room (right side)", + "type": "chest", + "nodeId": "king_nole_labyrinth_pre_door", + "chestId": 114 + }, + { + "name": "King Nole's Labyrinth (-1F): left chest in 3-chest dark room (left side)", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 115 + }, + { + "name": "King Nole's Labyrinth (-1F): chest in maze room with two buttons", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 116 + }, + { + "name": "King Nole's Labyrinth (-1F): upper chest in lantern room", + "type": "chest", + "nodeId": "king_nole_labyrinth_pre_door", + "chestId": 117 + }, + { + "name": "King Nole's Labyrinth (-1F): lower chest in lantern room", + "type": "chest", + "nodeId": "king_nole_labyrinth_pre_door", + "chestId": 118 + }, + { + "name": "King Nole's Labyrinth (-1F): chest in ice shortcut room", + "type": "chest", + "nodeId": "king_nole_labyrinth_pre_door", + "chestId": 119 + }, + { + "name": "King Nole's Labyrinth (-2F): chest in save room", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 120 + }, + { + "name": "King Nole's Labyrinth (-1F): chest in room with button and crates stairway", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 121 + }, + { + "name": "King Nole's Labyrinth (-3F): first chest before Firedemon", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "hints": [ + "in a very hot place" + ], + "chestId": 122 + }, + { + "name": "King Nole's Labyrinth (-3F): second chest before Firedemon", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "hints": [ + "in a very hot place" + ], + "chestId": 123 + }, + { + "name": "King Nole's Labyrinth (-3F): reward chest for beating Firedemon", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "hints": [ + "kept by a threatening guardian", + "in a very hot place" + ], + "chestId": 124 + }, + { + "name": "King Nole's Labyrinth (-2F): chest in four buttons room", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 125 + }, + { + "name": "King Nole's Labyrinth (-3F): first chest after falling from raft", + "type": "chest", + "nodeId": "king_nole_labyrinth_raft", + "chestId": 126 + }, + { + "name": "King Nole's Labyrinth (-3F): left chest in room before Spinner", + "type": "chest", + "nodeId": "king_nole_labyrinth_raft", + "chestId": 127 + }, + { + "name": "King Nole's Labyrinth (-3F): right chest in room before Spinner", + "type": "chest", + "nodeId": "king_nole_labyrinth_raft", + "chestId": 128 + }, + { + "name": "King Nole's Labyrinth (-3F): reward chest for beating Spinner", + "type": "chest", + "nodeId": "king_nole_labyrinth_raft", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 129 + }, + { + "name": "King Nole's Labyrinth (-3F): chest in room after Spinner", + "type": "chest", + "nodeId": "king_nole_labyrinth_raft", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 130 + }, + { + "name": "King Nole's Labyrinth (-3F): chest in room before Miro", + "type": "chest", + "nodeId": "king_nole_labyrinth_path_to_palace", + "hints": [ + "close to a waterfall" + ], + "chestId": 131 + }, + { + "name": "King Nole's Labyrinth (-3F): reward chest for beating Miro", + "type": "chest", + "nodeId": "king_nole_labyrinth_path_to_palace", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 132 + }, + { + "name": "King Nole's Labyrinth (-3F): chest in hands room", + "type": "chest", + "nodeId": "king_nole_labyrinth_post_door", + "chestId": 133 + }, + { + "name": "Route between Gumi and Ryuma: chest on the way to Swordsman Kado", + "type": "chest", + "nodeId": "route_gumi_ryuma", + "chestId": 134 + }, + { + "name": "Route between Massan and Gumi: chest on cliff", + "type": "chest", + "nodeId": "route_massan_gumi", + "hints": [ + "near a swamp" + ], + "chestId": 135 + }, + { + "name": "Route between Mercator and Verla: chest on cliff next to tree", + "type": "chest", + "nodeId": "mir_tower_sector", + "chestId": 136 + }, + { + "name": "Route between Mercator and Verla: chest on cliff next to blocked cave", + "type": "chest", + "nodeId": "mir_tower_sector", + "chestId": 137 + }, + { + "name": "Route between Mercator and Verla: chest near Twinkle village", + "type": "chest", + "nodeId": "mir_tower_sector", + "chestId": 138 + }, + { + "name": "Verla Shore: chest on corner cliff after Verla tunnel", + "type": "chest", + "nodeId": "verla_shore", + "chestId": 139 + }, + { + "name": "Verla Shore: chest on highest cliff after Verla tunnel (accessible through Verla mines)", + "type": "chest", + "nodeId": "verla_shore_cliff", + "chestId": 140 + }, + { + "name": "Route to Mir Tower: chest on cliff accessed by pressing hidden switch", + "type": "chest", + "nodeId": "mir_tower_sector", + "chestId": 141 + }, + { + "name": "Route to Mir Tower: chest behind first sacred tree", + "type": "chest", + "nodeId": "mir_tower_sector_tree_ledge", + "chestId": 142 + }, + { + "name": "Verla Shore: chest behind cabin", + "type": "chest", + "nodeId": "verla_shore", + "hints": [ + "in a well-hidden chest" + ], + "chestId": 143 + }, + { + "name": "Route to Destel: chest in map right after Verla mines exit", + "type": "chest", + "nodeId": "route_verla_destel", + "chestId": 144 + }, + { + "name": "Route to Destel: chest in small platform elevator map", + "type": "chest", + "nodeId": "route_verla_destel", + "chestId": 145 + }, + { + "name": "Route to Mir Tower: chest behind second sacred tree", + "type": "chest", + "nodeId": "mir_tower_sector_tree_coast", + "chestId": 146 + }, + { + "name": "Route to Destel: hidden chest in map right before Destel", + "type": "chest", + "nodeId": "route_verla_destel", + "hints": [ + "in a well-hidden chest" + ], + "chestId": 147 + }, + { + "name": "Mountainous Area: chest near teleport tree", + "type": "chest", + "nodeId": "mountainous_area", + "chestId": 148 + }, + { + "name": "Mountainous Area: chest on right side of map before the bridge", + "type": "chest", + "nodeId": "mountainous_area", + "chestId": 149 + }, + { + "name": "Mountainous Area: hidden chest in L-shaped path", + "type": "chest", + "nodeId": "mountainous_area", + "hints": [ + "in a well-hidden chest" + ], + "chestId": 150 + }, + { + "name": "Mountainous Area: hidden chest in uppermost path", + "type": "chest", + "nodeId": "mountainous_area", + "hints": [ + "in a well-hidden chest" + ], + "chestId": 151 + }, + { + "name": "Mountainous Area: isolated chest on cliff in bridge map", + "type": "chest", + "nodeId": "mountainous_area", + "chestId": 152 + }, + { + "name": "Mountainous Area: left chest on wall in bridge map", + "type": "chest", + "nodeId": "mountainous_area", + "chestId": 153 + }, + { + "name": "Mountainous Area: right chest on wall in bridge map", + "type": "chest", + "nodeId": "mountainous_area", + "chestId": 154 + }, + { + "name": "Mountainous Area: right chest in map before Zak arena", + "type": "chest", + "nodeId": "mountainous_area", + "chestId": 155 + }, + { + "name": "Mountainous Area: left chest in map before Zak arena", + "type": "chest", + "nodeId": "mountainous_area", + "chestId": 156 + }, + { + "name": "Route after Destel: chest on tiny cliff", + "type": "chest", + "nodeId": "route_after_destel", + "chestId": 157 + }, + { + "name": "Route after Destel: hidden chest in map after seeing Duke raft", + "type": "chest", + "nodeId": "route_after_destel", + "hints": [ + "in a well-hidden chest" + ], + "chestId": 158 + }, + { + "name": "Route after Destel: visible chest in map after seeing Duke raft", + "type": "chest", + "nodeId": "route_after_destel", + "chestId": 159 + }, + { + "name": "Mountainous Area: chest hidden under rocky arch", + "type": "chest", + "nodeId": "mountainous_area", + "hints": [ + "in a well-hidden chest" + ], + "chestId": 160 + }, + { + "name": "Route to Lake Shrine: chest on long cliff in crossroads map", + "type": "chest", + "nodeId": "route_lake_shrine", + "chestId": 161 + }, + { + "name": "Route to Lake Shrine: chest on middle cliff in crossroads map (reached from Mountainous Area)", + "type": "chest", + "nodeId": "route_lake_shrine_cliff", + "chestId": 162 + }, + { + "name": "Mountainous Area: chest in map in front of bridge statue", + "type": "chest", + "nodeId": "mountainous_area", + "chestId": 163 + }, + { + "name": "Route to Lake Shrine: right chest in volcano", + "type": "chest", + "nodeId": "route_lake_shrine", + "chestId": 164 + }, + { + "name": "Route to Lake Shrine: left chest in volcano", + "type": "chest", + "nodeId": "route_lake_shrine", + "chestId": 165 + }, + { + "name": "Mountainous Area Cave: chest in small hidden room", + "type": "chest", + "nodeId": "mountainous_area", + "hints": [ + "in a small cave", + "in a well-hidden chest", + "in a cave in the mountains" + ], + "chestId": 166 + }, + { + "name": "Mountainous Area Cave: chest in small visible room", + "type": "chest", + "nodeId": "mountainous_area", + "hints": [ + "in a small cave", + "in a cave in the mountains" + ], + "chestId": 167 + }, + { + "name": "Greenmaze: chest on path to Cutter", + "type": "chest", + "nodeId": "greenmaze_cutter", + "chestId": 168 + }, + { + "name": "Greenmaze: chest on cliff near the swamp", + "type": "chest", + "nodeId": "greenmaze_pre_whistle", + "chestId": 169 + }, + { + "name": "Greenmaze: chest between Sunstone and Massan shortcut", + "type": "chest", + "nodeId": "greenmaze_post_whistle", + "chestId": 170 + }, + { + "name": "Greenmaze: chest in mages room", + "type": "chest", + "nodeId": "greenmaze_pre_whistle", + "chestId": 171 + }, + { + "name": "Greenmaze: left chest in elbow cave", + "type": "chest", + "nodeId": "greenmaze_pre_whistle", + "chestId": 172 + }, + { + "name": "Greenmaze: right chest in elbow cave", + "type": "chest", + "nodeId": "greenmaze_pre_whistle", + "chestId": 173 + }, + { + "name": "Greenmaze: chest in waterfall cave", + "type": "chest", + "nodeId": "greenmaze_pre_whistle", + "hints": [ + "close to a waterfall" + ], + "chestId": 174 + }, + { + "name": "Greenmaze: left chest in hidden room behind waterfall", + "type": "chest", + "nodeId": "greenmaze_pre_whistle", + "hints": [ + "close to a waterfall" + ], + "chestId": 175 + }, + { + "name": "Greenmaze: right chest in hidden room behind waterfall", + "type": "chest", + "nodeId": "greenmaze_pre_whistle", + "hints": [ + "close to a waterfall" + ], + "chestId": 176 + }, + { + "name": "Massan: chest triggered by dog statue", + "type": "chest", + "nodeId": "massan", + "hints": [ + "in a well-hidden chest" + ], + "chestId": 177 + }, + { + "name": "Massan: chest in house nearest to elder house", + "type": "chest", + "nodeId": "massan", + "chestId": 178 + }, + { + "name": "Massan: chest in middle house", + "type": "chest", + "nodeId": "massan", + "chestId": 179 + }, + { + "name": "Massan: chest in house farthest from elder house", + "type": "chest", + "nodeId": "massan", + "chestId": 180 + }, + { + "name": "Gumi: chest on top of bed in house", + "type": "chest", + "nodeId": "gumi", + "chestId": 181 + }, + { + "name": "Gumi: chest in elder house after saving Fara", + "type": "chest", + "nodeId": "gumi_after_swamp_shrine", + "chestId": 182 + }, + { + "name": "Ryuma: chest in mayor's house", + "type": "chest", + "nodeId": "ryuma", + "chestId": 183 + }, + { + "name": "Ryuma: chest in repaired lighthouse", + "type": "chest", + "nodeId": "ryuma_lighthouse_repaired", + "chestId": 184 + }, + { + "name": "Crypt: chest in main room", + "type": "chest", + "nodeId": "crypt", + "chestId": 185 + }, + { + "name": "Crypt: reward chest", + "type": "chest", + "nodeId": "crypt", + "chestId": 186 + }, + { + "name": "Mercator: hidden casino chest", + "type": "chest", + "nodeId": "mercator_casino", + "hints": [ + "hidden in the depths of Mercator", + "in a well-hidden chest" + ], + "chestId": 191 + }, + { + "name": "Mercator: chest in Greenpea's house", + "type": "chest", + "nodeId": "mercator", + "chestId": 192 + }, + { + "name": "Mercator: chest in grandma's house (pot shelving trial)", + "type": "chest", + "nodeId": "mercator", + "chestId": 193 + }, + { + "name": "Verla: chest in well after beating Marley", + "type": "chest", + "nodeId": "verla_after_mines", + "hints": [ + "in a well-hidden chest" + ], + "chestId": 194 + }, + { + "name": "Destel: chest in inn next to innkeeper", + "type": "chest", + "nodeId": "destel", + "chestId": 196 + }, + { + "name": "Mir Tower: timed jump trial chest", + "type": "chest", + "nodeId": "mir_tower_pre_garlic", + "chestId": 197 + }, + { + "name": "Mir Tower: chest after mimic room", + "type": "chest", + "nodeId": "mir_tower_pre_garlic", + "chestId": 198 + }, + { + "name": "Mir Tower: mimic room chest #1", + "type": "chest", + "nodeId": "mir_tower_pre_garlic", + "chestId": 199 + }, + { + "name": "Mir Tower: mimic room chest #2", + "type": "chest", + "nodeId": "mir_tower_pre_garlic", + "chestId": 200 + }, + { + "name": "Mir Tower: mimic room chest #3", + "type": "chest", + "nodeId": "mir_tower_pre_garlic", + "chestId": 201 + }, + { + "name": "Mir Tower: mimic room chest #4", + "type": "chest", + "nodeId": "mir_tower_pre_garlic", + "chestId": 202 + }, + { + "name": "Mir Tower: chest in mushroom pit room", + "type": "chest", + "nodeId": "mir_tower_pre_garlic", + "chestId": 203 + }, + { + "name": "Mir Tower: chest in room next to mummy switch room", + "type": "chest", + "nodeId": "mir_tower_pre_garlic", + "chestId": 204 + }, + { + "name": "Mir Tower: chest in library accessible from teleporter maze", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "chestId": 205 + }, + { + "name": "Mir Tower: hidden chest in room before library", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "hints": [ + "in a well-hidden chest" + ], + "chestId": 206 + }, + { + "name": "Mir Tower: chest in falling spikeballs room", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "chestId": 207 + }, + { + "name": "Mir Tower: chest in timed challenge room", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "chestId": 208 + }, + { + "name": "Mir Tower: chest in room where Miro closes the door", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "chestId": 209 + }, + { + "name": "Mir Tower: chest after room where Miro closes the door", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "chestId": 210 + }, + { + "name": "Mir Tower: reward chest", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 211 + }, + { + "name": "Mir Tower: right chest in reward room", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 212 + }, + { + "name": "Mir Tower: left chest in reward room", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 213 + }, + { + "name": "Mir Tower: chest behind wall accessible after beating Mir", + "type": "chest", + "nodeId": "mir_tower_post_garlic", + "hints": [ + "kept by a threatening guardian" + ], + "chestId": 214 + }, + { + "name": "Witch Helga's Hut: end chest", + "type": "chest", + "nodeId": "helga_hut", + "chestId": 215 + }, + { + "name": "Massan Cave: right chest", + "type": "chest", + "nodeId": "massan_cave", + "chestId": 216 + }, + { + "name": "Massan Cave: left chest", + "type": "chest", + "nodeId": "massan_cave", + "chestId": 217 + }, + { + "name": "Tibor: reward chest after boss", + "type": "chest", + "nodeId": "tibor", + "chestId": 218 + }, + { + "name": "Tibor: chest in spike balls room", + "type": "chest", + "nodeId": "tibor", + "chestId": 219 + }, + { + "name": "Tibor: left chest on 2 chest group", + "type": "chest", + "nodeId": "tibor", + "chestId": 220 + }, + { + "name": "Tibor: right chest on 2 chest group", + "type": "chest", + "nodeId": "tibor", + "chestId": 221 + }, + { + "name": "Gumi: item on furniture in elder's house", + "type": "ground", + "nodeId": "gumi", + "entity": {"mapId": 605, "entityId": 2}, + "groundItemId": 1 + }, + { + "name": "Greenmaze: item behind trees requiring Cutter", + "type": "ground", + "nodeId": "greenmaze_post_whistle", + "entity": {"mapId": 564, "entityId": 0}, + "groundItemId": 2 + }, + { + "name": "Verla Mines: item in the corner of lava filled room", + "type": "ground", + "nodeId": "verla_mines", + "hints": [ + "in a very hot place" + ], + "entity": {"mapId": 263, "entityId": 7}, + "groundItemId": 3 + }, + { + "name": "Lake Shrine (-3F): item on ground at the SE exit of the golden golems roundabout", + "type": "ground", + "nodeId": "lake_shrine", + "entity": {"mapId": 333, "entityId": 0}, + "groundItemId": 4 + }, + { + "name": "King Nole's Labyrinth (-3F): item on ground behind waterfall after beating Spinner", + "type": "ground", + "nodeId": "king_nole_labyrinth_raft", + "hints": [ + "kept by a threatening guardian" + ], + "entity": {"mapId": 411, "entityId": 0}, + "groundItemId": 5 + }, + { + "name": "Destel Well: item on platform revealed after beating Quake", + "type": "ground", + "nodeId": "destel_well", + "hints": [ + "kept by a threatening guardian" + ], + "entity": {"mapId": 288, "entityId": 0}, + "groundItemId": 6 + }, + { + "name": "King Nole's Labyrinth (-1F): item on ground in ninjas room", + "type": "ground", + "nodeId": "king_nole_labyrinth_post_door", + "entity": {"mapId": 374, "entityId": 3}, + "groundItemId": 7 + }, + { + "name": "Massan Cave: item on ground in treasure room", + "type": "ground", + "nodeId": "massan_cave", + "entity": {"mapId": 807, "entityId": 4}, + "groundItemId": 8 + }, + { + "name": "King Nole's Labyrinth (-3F): item on floating hands", + "type": "ground", + "nodeId": "king_nole_labyrinth_post_door", + "entity": {"mapId": 418, "entityId": 0}, + "groundItemId": 9 + }, + { + "name": "Lake Shrine (-3F): isolated item on ground requiring raised platform to reach", + "type": "ground", + "nodeId": "lake_shrine", + "entities": [ + {"mapId": 344, "entityId": 0}, + {"mapId": 345, "entityId": 0} + ], + "groundItemId": 10 + }, + { + "name": "King Nole's Labyrinth (-2F): item on ground after falling from exterior room", + "type": "ground", + "nodeId": "king_nole_labyrinth_fall_from_exterior", + "entity": {"mapId": 363, "entityId": 0}, + "groundItemId": 11 + }, + { + "name": "Route after Destel: item on ground on the cliff", + "type": "ground", + "nodeId": "route_after_destel", + "entity": {"mapId": 483, "entityId": 0}, + "groundItemId": 12 + }, + { + "name": "Mountainous Area cave: item on ground behind hidden path", + "type": "ground", + "nodeId": "mountainous_area", + "hints": [ + "in a small cave", + "in a cave in the mountains" + ], + "entity": {"mapId": 553, "entityId": 0}, + "groundItemId": 13 + }, + { + "name": "Witch Helga's Hut: item on furniture", + "type": "ground", + "nodeId": "helga_hut", + "entity": {"mapId": 479, "entityId": 1}, + "groundItemId": 14 + }, + { + "name": "King Nole's Labyrinth (-3F): item on ground climbing back from Firedemon", + "type": "ground", + "nodeId": "king_nole_labyrinth_post_door", + "hints": [ + "in a very hot place" + ], + "entity": {"mapId": 399, "entityId": 0}, + "groundItemId": 15 + }, + { + "name": "Mercator: falling item in castle court", + "type": "ground", + "nodeId": "mercator", + "entity": {"mapId": 32, "entityId": 2}, + "groundItemId": 16 + }, + { + "name": "Lake Shrine (-2F): north item on ground in quadruple items room", + "type": "ground", + "nodeId": "lake_shrine", + "entity": {"mapId": 318, "entityId": 0}, + "groundItemId": 17 + }, + { + "name": "Lake Shrine (-2F): south item on ground in quadruple items room", + "type": "ground", + "nodeId": "lake_shrine", + "entity": {"mapId": 318, "entityId": 1}, + "groundItemId": 18 + }, + { + "name": "Lake Shrine (-2F): west item on ground in quadruple items room", + "type": "ground", + "nodeId": "lake_shrine", + "entity": {"mapId": 318, "entityId": 2}, + "groundItemId": 19 + }, + { + "name": "Lake Shrine (-2F): east item on ground in quadruple items room", + "type": "ground", + "nodeId": "lake_shrine", + "entity": {"mapId": 318, "entityId": 3}, + "groundItemId": 20 + }, + { + "name": "Twinkle Village: first item on ground", + "type": "ground", + "nodeId": "twinkle_village", + "entity": {"mapId": 462, "entityId": 5}, + "groundItemId": 21 + }, + { + "name": "Twinkle Village: second item on ground", + "type": "ground", + "nodeId": "twinkle_village", + "entity": {"mapId": 462, "entityId": 4}, + "groundItemId": 22 + }, + { + "name": "Twinkle Village: third item on ground", + "type": "ground", + "nodeId": "twinkle_village", + "entity": {"mapId": 462, "entityId": 3}, + "groundItemId": 23 + }, + { + "name": "Mir Tower: Priest room item #1", + "type": "ground", + "nodeId": "mir_tower_post_garlic", + "entity": {"mapId": 775, "entityId": 7}, + "groundItemId": 24 + }, + { + "name": "Mir Tower: Priest room item #2", + "type": "ground", + "nodeId": "mir_tower_post_garlic", + "entity": {"mapId": 775, "entityId": 6}, + "groundItemId": 25 + }, + { + "name": "Mir Tower: Priest room item #3", + "type": "ground", + "nodeId": "mir_tower_post_garlic", + "entity": {"mapId": 775, "entityId": 1}, + "groundItemId": 26 + }, + { + "name": "King Nole's Labyrinth (-2F): Left item dropped by sacred tree", + "type": "ground", + "nodeId": "king_nole_labyrinth_sacred_tree", + "entity": {"mapId": 415, "entityId": 2}, + "groundItemId": 27 + }, + { + "name": "King Nole's Labyrinth (-2F): Right item dropped by sacred tree", + "type": "ground", + "nodeId": "king_nole_labyrinth_sacred_tree", + "entity": {"mapId": 415, "entityId": 1}, + "groundItemId": 28 + }, + { + "name": "King Nole's Labyrinth (-3F): First item on ground before Firedemon", + "type": "ground", + "nodeId": "king_nole_labyrinth_post_door", + "hints": [ + "in a very hot place" + ], + "entity": {"mapId": 400, "entityId": 0}, + "groundItemId": 29 + }, + { + "name": "Massan: Shop item #1", + "type": "shop", + "nodeId": "massan", + "entity": {"mapId": 596, "entityId": 1}, + "shopItemId": 1 + }, + { + "name": "Massan: Shop item #2", + "type": "shop", + "nodeId": "massan", + "entity": {"mapId": 596, "entityId": 2}, + "shopItemId": 2 + }, + { + "name": "Massan: Shop item #3", + "type": "shop", + "nodeId": "massan", + "entity": {"mapId": 596, "entityId": 3}, + "shopItemId": 3 + }, + { + "name": "Gumi: Inn item #1", + "type": "shop", + "nodeId": "gumi", + "entity": {"mapId": 608, "entityId": 4}, + "shopItemId": 4 + }, + { + "name": "Gumi: Inn item #2", + "type": "shop", + "nodeId": "gumi", + "entity": {"mapId": 608, "entityId": 2}, + "shopItemId": 5 + }, + { + "name": "Ryuma: Shop item #1", + "type": "shop", + "nodeId": "ryuma_after_thieves_hideout", + "entity": {"mapId": 615, "entityId": 2}, + "shopItemId": 6 + }, + { + "name": "Ryuma: Shop item #2", + "type": "shop", + "nodeId": "ryuma_after_thieves_hideout", + "entity": {"mapId": 615, "entityId": 3}, + "shopItemId": 7 + }, + { + "name": "Ryuma: Shop item #3", + "type": "shop", + "nodeId": "ryuma_after_thieves_hideout", + "entity": {"mapId": 615, "entityId": 4}, + "shopItemId": 8 + }, + { + "name": "Ryuma: Shop item #4", + "type": "shop", + "nodeId": "ryuma_after_thieves_hideout", + "entity": {"mapId": 615, "entityId": 5}, + "shopItemId": 9 + }, + { + "name": "Ryuma: Shop item #5", + "type": "shop", + "nodeId": "ryuma_after_thieves_hideout", + "entity": {"mapId": 615, "entityId": 6}, + "shopItemId": 10 + }, + { + "name": "Ryuma: Inn item", + "type": "shop", + "nodeId": "ryuma", + "entity": {"mapId": 624, "entityId": 3}, + "shopItemId": 11 + }, + { + "name": "Mercator: Shop item #1", + "type": "shop", + "nodeId": "mercator", + "entity": {"mapId": 679, "entityId": 1}, + "shopItemId": 12 + }, + { + "name": "Mercator: Shop item #2", + "type": "shop", + "nodeId": "mercator", + "entity": {"mapId": 679, "entityId": 2}, + "shopItemId": 13 + }, + { + "name": "Mercator: Shop item #3", + "type": "shop", + "nodeId": "mercator", + "entity": {"mapId": 679, "entityId": 3}, + "shopItemId": 14 + }, + { + "name": "Mercator: Shop item #4", + "type": "shop", + "nodeId": "mercator", + "entity": {"mapId": 679, "entityId": 4}, + "shopItemId": 15 + }, + { + "name": "Mercator: Shop item #5", + "type": "shop", + "nodeId": "mercator", + "entity": {"mapId": 679, "entityId": 5}, + "shopItemId": 16 + }, + { + "name": "Mercator: Shop item #6", + "type": "shop", + "nodeId": "mercator", + "entity": {"mapId": 679, "entityId": 6}, + "shopItemId": 17 + }, + { + "name": "Mercator: Special shop item #1", + "type": "shop", + "nodeId": "mercator_special_shop", + "entity": {"mapId": 696, "entityId": 1}, + "shopItemId": 18 + }, + { + "name": "Mercator: Special shop item #2", + "type": "shop", + "nodeId": "mercator_special_shop", + "entity": {"mapId": 696, "entityId": 2}, + "shopItemId": 19 + }, + { + "name": "Mercator: Special shop item #3", + "type": "shop", + "nodeId": "mercator_special_shop", + "entity": {"mapId": 696, "entityId": 3}, + "shopItemId": 20 + }, + { + "name": "Mercator: Special shop item #4", + "type": "shop", + "nodeId": "mercator_special_shop", + "entity": {"mapId": 696, "entityId": 4}, + "shopItemId": 21 + }, + { + "name": "Mercator: Docks shop item #1", + "type": "shop", + "nodeId": "mercator_repaired_docks", + "entities": [ + {"mapId": 644, "entityId": 3}, + {"mapId": 643, "entityId": 9} + ], + "shopItemId": 22 + }, + { + "name": "Mercator: Docks shop item #2", + "type": "shop", + "nodeId": "mercator_repaired_docks", + "entities": [ + {"mapId": 644, "entityId": 4}, + {"mapId": 643, "entityId": 10} + ], + "shopItemId": 23 + }, + { + "name": "Mercator: Docks shop item #3", + "type": "shop", + "nodeId": "mercator_repaired_docks", + "entities": [ + {"mapId": 644, "entityId": 5}, + {"mapId": 643, "entityId": 11} + ], + "shopItemId": 24 + }, + { + "name": "Verla: Shop item #1", + "type": "shop", + "nodeId": "verla", + "entities": [ + {"mapId": 719, "entityId": 0}, + {"mapId": 720, "entityId": 1} + ], + "shopItemId": 25 + }, + { + "name": "Verla: Shop item #2", + "type": "shop", + "nodeId": "verla", + "entities": [ + {"mapId": 719, "entityId": 1}, + {"mapId": 720, "entityId": 2} + ], + "shopItemId": 26 + }, + { + "name": "Verla: Shop item #3", + "type": "shop", + "nodeId": "verla", + "entities": [ + {"mapId": 719, "entityId": 2}, + {"mapId": 720, "entityId": 3} + ], + "shopItemId": 27 + }, + { + "name": "Verla: Shop item #4", + "type": "shop", + "nodeId": "verla", + "entities": [ + {"mapId": 719, "entityId": 4}, + {"mapId": 720, "entityId": 4} + ], + "shopItemId": 28 + }, + { + "name": "Verla: Shop item #5 (extra item after saving town)", + "type": "shop", + "nodeId": "verla_after_mines", + "entity": {"mapId": 720, "entityId": 5}, + "shopItemId": 29 + }, + { + "name": "Route from Verla to Destel: Kelketo shop item #1", + "type": "shop", + "nodeId": "route_verla_destel", + "entity": {"mapId": 517, "entityId": 1}, + "shopItemId": 30 + }, + { + "name": "Route from Verla to Destel: Kelketo shop item #2", + "type": "shop", + "nodeId": "route_verla_destel", + "entity": {"mapId": 517, "entityId": 2}, + "shopItemId": 31 + }, + { + "name": "Route from Verla to Destel: Kelketo shop item #3", + "type": "shop", + "nodeId": "route_verla_destel", + "entity": {"mapId": 517, "entityId": 3}, + "shopItemId": 32 + }, + { + "name": "Route from Verla to Destel: Kelketo shop item #4", + "type": "shop", + "nodeId": "route_verla_destel", + "entity": {"mapId": 517, "entityId": 4}, + "shopItemId": 33 + }, + { + "name": "Route from Verla to Destel: Kelketo shop item #5", + "type": "shop", + "nodeId": "route_verla_destel", + "entity": {"mapId": 517, "entityId": 5}, + "shopItemId": 34 + }, + { + "name": "Destel: Inn item", + "type": "shop", + "nodeId": "destel", + "entity": {"mapId": 729, "entityId": 2}, + "shopItemId": 35 + }, + { + "name": "Destel: Shop item #1", + "type": "shop", + "nodeId": "destel", + "entity": {"mapId": 733, "entityId": 1}, + "shopItemId": 36 + }, + { + "name": "Destel: Shop item #2", + "type": "shop", + "nodeId": "destel", + "entity": {"mapId": 733, "entityId": 2}, + "shopItemId": 37 + }, + { + "name": "Destel: Shop item #3", + "type": "shop", + "nodeId": "destel", + "entity": {"mapId": 733, "entityId": 3}, + "shopItemId": 38 + }, + { + "name": "Destel: Shop item #4", + "type": "shop", + "nodeId": "destel", + "entity": {"mapId": 733, "entityId": 4}, + "shopItemId": 39 + }, + { + "name": "Destel: Shop item #5", + "type": "shop", + "nodeId": "destel", + "entity": {"mapId": 733, "entityId": 5}, + "shopItemId": 40 + }, + { + "name": "Route to Lake Shrine: Greedly's shop item #1", + "type": "shop", + "nodeId": "route_lake_shrine", + "entity": {"mapId": 526, "entityId": 0}, + "shopItemId": 41 + }, + { + "name": "Route to Lake Shrine: Greedly's shop item #2", + "type": "shop", + "nodeId": "route_lake_shrine", + "entity": {"mapId": 526, "entityId": 2}, + "shopItemId": 42 + }, + { + "name": "Route to Lake Shrine: Greedly's shop item #3", + "type": "shop", + "nodeId": "route_lake_shrine", + "entity": {"mapId": 526, "entityId": 3}, + "shopItemId": 43 + }, + { + "name": "Route to Lake Shrine: Greedly's shop item #4", + "type": "shop", + "nodeId": "route_lake_shrine", + "entity": {"mapId": 526, "entityId": 4}, + "shopItemId": 44 + }, + { + "name": "Kazalt: Shop item #1", + "type": "shop", + "nodeId": "kazalt", + "entity": {"mapId": 747, "entityId": 0}, + "shopItemId": 45 + }, + { + "name": "Kazalt: Shop item #2", + "type": "shop", + "nodeId": "kazalt", + "entity": {"mapId": 747, "entityId": 2}, + "shopItemId": 46 + }, + { + "name": "Kazalt: Shop item #3", + "type": "shop", + "nodeId": "kazalt", + "entity": {"mapId": 747, "entityId": 3}, + "shopItemId": 47 + }, + { + "name": "Kazalt: Shop item #4", + "type": "shop", + "nodeId": "kazalt", + "entity": {"mapId": 747, "entityId": 4}, + "shopItemId": 48 + }, + { + "name": "Kazalt: Shop item #5", + "type": "shop", + "nodeId": "kazalt", + "entity": {"mapId": 747, "entityId": 5}, + "shopItemId": 49 + }, + { + "name": "Massan: Elder reward after freeing Fara in Swamp Shrine", + "type": "reward", + "nodeId": "massan_after_swamp_shrine", + "address": 162337, + "flag": {"byte": "0x1004", "bit": 2}, + "rewardId": 0 + }, + { + "name": "Lake Shrine: Mir reward after beating Duke", + "type": "reward", + "nodeId": "lake_shrine", + "address": 166463, + "flag": {"byte": "0x1003", "bit": 0}, + "rewardId": 1 + }, + { + "name": "Greenmaze: Cutter reward for saving Einstein", + "type": "reward", + "nodeId": "greenmaze_cutter", + "address": 166021, + "flag": {"byte": "0x1024", "bit": 4}, + "rewardId": 2 + }, + { + "name": "Mountainous Area: Zak reward after fighting", + "type": "reward", + "nodeId": "mountainous_area", + "hints": [ + "kept by a threatening guardian" + ], + "address": 166515, + "flag": {"byte": "0x1027", "bit": 0}, + "rewardId": 3 + }, + { + "name": "Route between Gumi and Ryuma: Swordsman Kado reward", + "type": "reward", + "nodeId": "route_gumi_ryuma", + "address": 166219, + "flag": {"byte": "0x101B", "bit": 7}, + "rewardId": 4 + }, + { + "name": "Greenmaze: dwarf hidden in the trees", + "type": "reward", + "nodeId": "greenmaze_pre_whistle", + "address": 166111, + "flag": {"byte": "0x1022", "bit": 7}, + "rewardId": 5 + }, + { + "name": "Mercator: Arthur reward (in castle throne room)", + "type": "reward", + "nodeId": "mercator", + "address": 164191, + "flag": {"byte": "0x101B", "bit": 6}, + "rewardId": 6 + }, + { + "name": "Mercator: Fahl's dojo challenge reward", + "type": "reward", + "nodeId": "mercator", + "address": 165029, + "flag": {"byte": "0x101C", "bit": 4}, + "rewardId": 7 + }, + { + "name": "Ryuma: Mayor's first reward", + "type": "reward", + "nodeId": "ryuma_after_thieves_hideout", + "address": 164731, + "flag": {"byte": "0x1004", "bit": 3}, + "rewardId": 8 + }, + { + "name": "Ryuma: Mayor's second reward", + "type": "reward", + "nodeId": "ryuma_after_thieves_hideout", + "address": 164735, + "flag": {"byte": "0x1004", "bit": 3}, + "rewardId": 9 + } +] diff --git a/worlds/landstalker/data/world_node.py b/worlds/landstalker/data/world_node.py new file mode 100644 index 000000000000..f786f9613fba --- /dev/null +++ b/worlds/landstalker/data/world_node.py @@ -0,0 +1,411 @@ +WORLD_NODES_JSON = { + "massan": { + "name": "Massan", + "hints": [ + "in a village", + "in a region inhabited by bears", + "in the village of Massan" + ] + }, + "massan_cave": { + "name": "Massan Cave", + "hints": [ + "in a large cave", + "in a region inhabited by bears", + "in Massan cave" + ] + }, + "route_massan_gumi": { + "name": "Route between Massan and Gumi", + "hints": [ + "on a route", + "in a region inhabited by bears", + "between Massan and Gumi" + ] + }, + "waterfall_shrine": { + "name": "Waterfall Shrine", + "hints": [ + "in a shrine", + "close to a waterfall", + "in a region inhabited by bears", + "in Waterfall Shrine" + ] + }, + "swamp_shrine": { + "name": "Swamp Shrine", + "hints": [ + "in a shrine", + "near a swamp", + "in a region inhabited by bears", + "in Swamp Shrine" + ] + }, + "massan_after_swamp_shrine": { + "name": "Massan (after Swamp Shrine)", + "hints": [ + "in a village", + "in a region inhabited by bears", + "in the village of Massan" + ] + }, + "gumi_after_swamp_shrine": { + "name": "Gumi (after Swamp Shrine)", + "hints": [ + "in a village", + "in a region inhabited by bears", + "in the village of Gumi" + ] + }, + "gumi": { + "name": "Gumi", + "hints": [ + "in a village", + "in a region inhabited by bears", + "in the village of Gumi" + ] + }, + "route_gumi_ryuma": { + "name": "Route from Gumi to Ryuma", + "hints": [ + "on a route", + "in a region inhabited by bears", + "between Gumi and Ryuma" + ] + }, + "tibor": { + "name": "Tibor", + "hints": [ + "among the trees", + "inside the elder tree called Tibor" + ] + }, + "ryuma": { + "name": "Ryuma", + "hints": [ + "in a town", + "in the town of Ryuma" + ] + }, + "ryuma_after_thieves_hideout": { + "name": "Ryuma (after Thieves Hideout)", + "hints": [ + "in a town", + "in the town of Ryuma" + ] + }, + "ryuma_lighthouse_repaired": { + "name": "Ryuma (repaired lighthouse)", + "hints": [ + "in a town", + "in the town of Ryuma" + ] + }, + "thieves_hideout_pre_key": { + "name": "Thieves Hideout (before keydoor)", + "hints": [ + "close to a waterfall", + "in a large cave", + "in the Thieves' Hideout" + ] + }, + "thieves_hideout_post_key": { + "name": "Thieves Hideout (after keydoor)", + "hints": [ + "close to a waterfall", + "in a large cave", + "in the Thieves' Hideout" + ] + }, + "helga_hut": { + "name": "Witch Helga's Hut", + "hints": [ + "near a swamp", + "in the hut of a witch called Helga" + ] + }, + "mercator": { + "name": "Mercator", + "hints": [ + "in a town", + "in the town of Mercator" + ] + }, + "mercator_repaired_docks": { + "name": "Mercator (docks with repaired lighthouse)", + "hints": [ + "in a town", + "in the town of Mercator" + ] + }, + "mercator_casino": { + "name": "Mercator casino" + }, + "mercator_dungeon": { + "name": "Mercator Dungeon" + }, + "crypt": { + "name": "Crypt", + "hints": [ + "hidden in the depths of Mercator", + "in Mercator crypt" + ] + }, + "mercator_special_shop": { + "name": "Mercator special shop", + "hints": [ + "in a town", + "in the town of Mercator" + ] + }, + "mir_tower_sector": { + "name": "Mir Tower sector", + "hints": [ + "on a route", + "near Mir Tower" + ] + }, + "mir_tower_sector_tree_ledge": { + "name": "Mir Tower sector (ledge behind sacred tree)", + "hints": [ + "on a route", + "among the trees", + "near Mir Tower" + ] + }, + "mir_tower_sector_tree_coast": { + "name": "Mir Tower sector (coast behind sacred tree)", + "hints": [ + "on a route", + "among the trees", + "near Mir Tower" + ] + }, + "twinkle_village": { + "name": "Twinkle village", + "hints": [ + "in a village", + "in Twinkle village" + ] + }, + "mir_tower_pre_garlic": { + "name": "Mir Tower (pre-garlic)", + "hints": [ + "inside a tower", + "in Mir Tower" + ] + }, + "mir_tower_post_garlic": { + "name": "Mir Tower (post-garlic)", + "hints": [ + "inside a tower", + "in Mir Tower" + ] + }, + "greenmaze_pre_whistle": { + "name": "Greenmaze (pre-whistle)", + "hints": [ + "among the trees", + "in the infamous Greenmaze" + ] + }, + "greenmaze_cutter": { + "name": "Greenmaze (Cutter hidden sector)", + "hints": [ + "among the trees", + "in the infamous Greenmaze" + ] + }, + "greenmaze_post_whistle": { + "name": "Greenmaze (post-whistle)", + "hints": [ + "among the trees", + "in the infamous Greenmaze" + ] + }, + "verla_shore": { + "name": "Verla shore", + "hints": [ + "on a route", + "near the town of Verla" + ] + }, + "verla_shore_cliff": { + "name": "Verla shore cliff (accessible from Verla Mines)", + "hints": [ + "on a route", + "near the town of Verla" + ] + }, + "verla": { + "name": "Verla", + "hints": [ + "in a town", + "in the town of Verla" + ] + }, + "verla_after_mines": { + "name": "Verla (after mines)", + "hints": [ + "in a town", + "in the town of Verla" + ] + }, + "verla_mines": { + "name": "Verla Mines", + "hints": [ + "in Verla Mines" + ] + }, + "verla_mines_behind_lava": { + "name": "Verla Mines (behind lava)", + "hints": [ + "in Verla Mines" + ] + }, + "route_verla_destel": { + "name": "Route between Verla and Destel", + "hints": [ + "on a route", + "in Destel region", + "between Verla and Destel" + ] + }, + "destel": { + "name": "Destel", + "hints": [ + "in a village", + "in Destel region", + "in the village of Destel" + ] + }, + "route_after_destel": { + "name": "Route after Destel", + "hints": [ + "on a route", + "near a lake", + "in Destel region", + "on the route to the lake after Destel" + ] + }, + "destel_well": { + "name": "Destel Well", + "hints": [ + "in Destel region", + "in a large cave", + "in Destel Well" + ] + }, + "route_lake_shrine": { + "name": "Route to Lake Shrine", + "hints": [ + "on a route", + "near a lake", + "on the mountainous path to Lake Shrine" + ] + }, + "route_lake_shrine_cliff": { + "name": "Route to Lake Shrine cliff", + "hints": [ + "on a route", + "near a lake", + "on the mountainous path to Lake Shrine" + ] + }, + "lake_shrine": { + "name": "Lake Shrine", + "hints": [ + "in a shrine", + "near a lake", + "in Lake Shrine" + ] + }, + "mountainous_area": { + "name": "Mountainous Area", + "hints": [ + "in a mountainous area" + ] + }, + "king_nole_cave": { + "name": "King Nole's Cave", + "hints": [ + "in a large cave", + "in King Nole's cave" + ] + }, + "kazalt": { + "name": "Kazalt", + "hints": [ + "in King Nole's domain", + "in Kazalt" + ] + }, + "king_nole_labyrinth_pre_door": { + "name": "King Nole's Labyrinth (before door)", + "hints": [ + "in King Nole's domain", + "in King Nole's labyrinth" + ] + }, + "king_nole_labyrinth_post_door": { + "name": "King Nole's Labyrinth (after door)", + "hints": [ + "in King Nole's domain", + "in King Nole's labyrinth" + ] + }, + "king_nole_labyrinth_exterior": { + "name": "King Nole's Labyrinth (exterior)", + "hints": [ + "in King Nole's domain", + "in King Nole's labyrinth" + ] + }, + "king_nole_labyrinth_fall_from_exterior": { + "name": "King Nole's Labyrinth (fall from exterior)", + "hints": [ + "in King Nole's domain", + "in King Nole's labyrinth" + ] + }, + "king_nole_labyrinth_raft_entrance": { + "name": "King Nole's Labyrinth (raft entrance)", + "hints": [ + "in King Nole's domain", + "in King Nole's labyrinth" + ] + }, + "king_nole_labyrinth_raft": { + "name": "King Nole's Labyrinth (raft)", + "hints": [ + "close to a waterfall", + "in King Nole's domain", + "in King Nole's labyrinth" + ] + }, + "king_nole_labyrinth_sacred_tree": { + "name": "King Nole's Labyrinth (sacred tree)", + "hints": [ + "among the trees", + "in King Nole's domain", + "in King Nole's labyrinth" + ] + }, + "king_nole_labyrinth_path_to_palace": { + "name": "King Nole's Labyrinth (path to palace)", + "hints": [ + "in King Nole's domain", + "in King Nole's labyrinth" + ] + }, + "king_nole_palace": { + "name": "King Nole's Palace", + "hints": [ + "in King Nole's domain", + "in King Nole's palace" + ] + }, + "end": { + "name": "The End" + } +} diff --git a/worlds/landstalker/data/world_path.py b/worlds/landstalker/data/world_path.py new file mode 100644 index 000000000000..f7baba358a48 --- /dev/null +++ b/worlds/landstalker/data/world_path.py @@ -0,0 +1,446 @@ +WORLD_PATHS_JSON = [ + { + "fromId": "massan", + "toId": "massan_cave", + "twoWay": True, + "requiredItems": [ + "Axe Magic" + ] + }, + { + "fromId": "massan", + "toId": "massan_after_swamp_shrine", + "requiredNodes": [ + "swamp_shrine" + ] + }, + { + "fromId": "massan", + "toId": "route_massan_gumi", + "twoWay": True + }, + { + "fromId": "route_massan_gumi", + "toId": "waterfall_shrine", + "twoWay": True + }, + { + "fromId": "route_massan_gumi", + "toId": "swamp_shrine", + "twoWay": True, + "weight": 2, + "requiredItems": [ + "Idol Stone" + ] + }, + { + "fromId": "route_massan_gumi", + "toId": "gumi", + "twoWay": True + }, + { + "fromId": "gumi", + "toId": "gumi_after_swamp_shrine", + "requiredNodes": [ + "swamp_shrine" + ] + }, + { + "fromId": "gumi", + "toId": "route_gumi_ryuma" + }, + { + "fromId": "route_gumi_ryuma", + "toId": "ryuma", + "twoWay": True + }, + { + "fromId": "ryuma", + "toId": "ryuma_after_thieves_hideout", + "requiredNodes": [ + "thieves_hideout_post_key" + ] + }, + { + "fromId": "ryuma", + "toId": "ryuma_lighthouse_repaired", + "twoWay": True, + "requiredItems": [ + "Sun Stone" + ] + }, + { + "fromId": "ryuma", + "toId": "thieves_hideout_pre_key", + "twoWay": True + }, + { + "fromId": "thieves_hideout_pre_key", + "toId": "thieves_hideout_post_key", + "requiredItems": [ + "Key" + ] + }, + { + "fromId": "thieves_hideout_post_key", + "toId": "thieves_hideout_pre_key" + }, + { + "fromId": "route_gumi_ryuma", + "toId": "tibor", + "twoWay": True + }, + { + "fromId": "route_gumi_ryuma", + "toId": "helga_hut", + "twoWay": True, + "requiredItems": [ + "Einstein Whistle" + ], + "requiredNodes": [ + "massan" + ] + }, + { + "fromId": "route_gumi_ryuma", + "toId": "mercator", + "twoWay": True, + "weight": 2, + "requiredItems": [ + "Safety Pass" + ] + }, + { + "fromId": "mercator", + "toId": "mercator_dungeon", + "twoWay": True + }, + { + "fromId": "mercator", + "toId": "crypt", + "twoWay": True + }, + { + "fromId": "mercator", + "toId": "mercator_special_shop", + "twoWay": True, + "requiredItems": [ + "Buyer Card" + ] + }, + { + "fromId": "mercator", + "toId": "mercator_casino", + "twoWay": True, + "requiredItems": [ + "Casino Ticket" + ] + }, + { + "fromId": "mercator", + "toId": "mir_tower_sector", + "twoWay": True + }, + { + "fromId": "mir_tower_sector", + "toId": "twinkle_village", + "twoWay": True + }, + { + "fromId": "mir_tower_sector", + "toId": "mir_tower_sector_tree_ledge", + "twoWay": True, + "requiredItems": [ + "Axe Magic" + ] + }, + { + "fromId": "mir_tower_sector", + "toId": "mir_tower_sector_tree_coast", + "twoWay": True, + "requiredItems": [ + "Axe Magic" + ] + }, + { + "fromId": "mir_tower_sector", + "toId": "mir_tower_pre_garlic", + "requiredItems": [ + "Armlet" + ] + }, + { + "fromId": "mir_tower_pre_garlic", + "toId": "mir_tower_sector" + }, + { + "fromId": "mir_tower_pre_garlic", + "toId": "mir_tower_post_garlic", + "requiredItems": [ + "Garlic" + ] + }, + { + "fromId": "mir_tower_post_garlic", + "toId": "mir_tower_pre_garlic" + }, + { + "fromId": "mir_tower_post_garlic", + "toId": "mir_tower_sector" + }, + { + "fromId": "mercator", + "toId": "greenmaze_pre_whistle", + "weight": 2, + "requiredItems": [ + "Key" + ] + }, + { + "fromId": "greenmaze_pre_whistle", + "toId": "greenmaze_post_whistle", + "requiredItems": [ + "Einstein Whistle" + ] + }, + { + "fromId": "greenmaze_pre_whistle", + "toId": "greenmaze_cutter", + "requiredItems": [ + "EkeEke" + ], + "twoWay": True + }, + { + "fromId": "greenmaze_post_whistle", + "toId": "route_massan_gumi" + }, + { + "fromId": "mercator", + "toId": "mercator_repaired_docks", + "requiredNodes": [ + "ryuma_lighthouse_repaired" + ] + }, + { + "fromId": "mercator_repaired_docks", + "toId": "verla_shore" + }, + { + "fromId": "verla_shore", + "toId": "verla", + "twoWay": True + }, + { + "fromId": "verla", + "toId": "verla_after_mines", + "requiredNodes": [ + "verla_mines" + ], + "twoWay": True + }, + { + "fromId": "verla_shore", + "toId": "verla_mines", + "twoWay": True + }, + { + "fromId": "verla_mines", + "toId": "verla_shore_cliff", + "twoWay": True + }, + { + "fromId": "verla_shore_cliff", + "toId": "verla_shore" + }, + { + "fromId": "verla_shore", + "toId": "mir_tower_sector", + "requiredNodes": [ + "verla_mines" + ], + "twoWay": True + }, + { + "fromId": "verla_mines", + "toId": "route_verla_destel" + }, + { + "fromId": "verla_mines", + "toId": "verla_mines_behind_lava", + "twoWay": True, + "requiredItems": [ + "Fireproof" + ] + }, + { + "fromId": "route_verla_destel", + "toId": "destel", + "twoWay": True + }, + { + "fromId": "destel", + "toId": "route_after_destel", + "twoWay": True + }, + { + "fromId": "destel", + "toId": "destel_well", + "twoWay": True + }, + { + "fromId": "destel_well", + "toId": "route_lake_shrine", + "twoWay": True + }, + { + "fromId": "route_lake_shrine", + "toId": "lake_shrine", + "itemsPlacedWhenCrossing": [ + "Sword of Gaia" + ] + }, + { + "fromId": "lake_shrine", + "toId": "route_lake_shrine" + }, + { + "fromId": "lake_shrine", + "toId": "mir_tower_sector" + }, + { + "fromId": "greenmaze_pre_whistle", + "toId": "mountainous_area", + "twoWay": True, + "requiredItems": [ + "Axe Magic" + ] + }, + { + "fromId": "mountainous_area", + "toId": "route_lake_shrine_cliff", + "twoWay": True, + "requiredItems": [ + "Axe Magic" + ] + }, + { + "fromId": "route_lake_shrine_cliff", + "toId": "route_lake_shrine" + }, + { + "fromId": "mountainous_area", + "toId": "king_nole_cave", + "twoWay": True, + "weight": 2, + "requiredItems": [ + "Gola's Eye" + ] + }, + { + "fromId": "king_nole_cave", + "toId": "mercator" + }, + { + "fromId": "king_nole_cave", + "toId": "kazalt", + "itemsPlacedWhenCrossing": [ + "Lithograph" + ] + }, + { + "fromId": "kazalt", + "toId": "king_nole_cave" + }, + { + "fromId": "kazalt", + "toId": "king_nole_labyrinth_pre_door", + "twoWay": True + }, + { + "fromId": "king_nole_labyrinth_pre_door", + "toId": "king_nole_labyrinth_post_door", + "requiredItems": [ + "Key" + ] + }, + { + "fromId": "king_nole_labyrinth_post_door", + "toId": "king_nole_labyrinth_pre_door" + }, + { + "fromId": "king_nole_labyrinth_pre_door", + "toId": "king_nole_labyrinth_exterior", + "requiredItems": [ + "Iron Boots" + ] + }, + { + "fromId": "king_nole_labyrinth_exterior", + "toId": "king_nole_labyrinth_fall_from_exterior", + "requiredItems": [ + "Axe Magic" + ] + }, + { + "fromId": "king_nole_labyrinth_fall_from_exterior", + "toId": "king_nole_labyrinth_pre_door" + }, + { + "fromId": "king_nole_labyrinth_post_door", + "toId": "king_nole_labyrinth_raft_entrance", + "requiredItems": [ + "Snow Spikes" + ] + }, + { + "fromId": "king_nole_labyrinth_raft_entrance", + "toId": "king_nole_labyrinth_post_door" + }, + { + "fromId": "king_nole_labyrinth_raft_entrance", + "toId": "king_nole_labyrinth_raft", + "requiredItems": [ + "Logs" + ] + }, + { + "fromId": "king_nole_labyrinth_raft", + "toId": "king_nole_labyrinth_raft_entrance" + }, + { + "fromId": "king_nole_labyrinth_post_door", + "toId": "king_nole_labyrinth_path_to_palace", + "requiredItems": [ + "Snow Spikes" + ] + }, + { + "fromId": "king_nole_labyrinth_path_to_palace", + "toId": "king_nole_labyrinth_post_door" + }, + { + "fromId": "king_nole_labyrinth_post_door", + "toId": "king_nole_labyrinth_sacred_tree", + "requiredItems": [ + "Axe Magic" + ], + "requiredNodes": [ + "king_nole_labyrinth_raft_entrance" + ] + }, + { + "fromId": "king_nole_labyrinth_path_to_palace", + "toId": "king_nole_palace", + "twoWay": True + }, + { + "fromId": "king_nole_palace", + "toId": "end", + "requiredItems": [ + "Gola's Fang", + "Gola's Horn", + "Gola's Nail" + ] + } +] \ No newline at end of file diff --git a/worlds/landstalker/data/world_region.py b/worlds/landstalker/data/world_region.py new file mode 100644 index 000000000000..3365a9dfa9e2 --- /dev/null +++ b/worlds/landstalker/data/world_region.py @@ -0,0 +1,299 @@ +WORLD_REGIONS_JSON = [ + { + "name": "Massan", + "hintName": "in the village of Massan", + "nodeIds": [ + "massan", + "massan_after_swamp_shrine" + ] + }, + { + "name": "Massan Cave", + "hintName": "in the cave near Massan", + "nodeIds": [ + "massan_cave" + ], + "darkMapIds": [ + 803, 804, 805, 806, 807 + ] + }, + { + "name": "Route between Massan and Gumi", + "canBeHintedAsRequired": False, + "nodeIds": [ + "route_massan_gumi" + ] + }, + { + "name": "Waterfall Shrine", + "hintName": "in the waterfall shrine", + "nodeIds": [ + "waterfall_shrine" + ], + "darkMapIds": [ + 174, 175, 176, 177, 178, 179, 180, 181, 182 + ] + }, + { + "name": "Swamp Shrine", + "hintName": "in the swamp shrine", + "canBeHintedAsRequired": False, + "nodeIds": [ + "swamp_shrine" + ], + "darkMapIds": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 30 + ] + }, + { + "name": "Gumi", + "hintName": "in the village of Gumi", + "nodeIds": [ + "gumi", + "gumi_after_swamp_shrine" + ] + }, + { + "name": "Route between Gumi and Ryuma", + "canBeHintedAsRequired": False, + "nodeIds": [ + "route_gumi_ryuma" + ] + }, + { + "name": "Tibor", + "hintName": "inside Tibor", + "nodeIds": [ + "tibor" + ], + "darkMapIds": [ + 808, 809, 810, 811, 812, 813, 814, 815 + ] + }, + { + "name": "Ryuma", + "hintName": "in the town of Ryuma", + "nodeIds": [ + "ryuma", + "ryuma_after_thieves_hideout", + "ryuma_lighthouse_repaired" + ] + }, + { + "name": "Thieves Hideout", + "hintName": "in the thieves' hideout", + "nodeIds": [ + "thieves_hideout_pre_key", + "thieves_hideout_post_key" + ], + "darkMapIds": [ + 185, 186, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 210, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222 + ] + }, + { + "name": "Witch Helga's Hut", + "hintName": "in witch Helga's hut", + "nodeIds": [ + "helga_hut" + ] + }, + { + "name": "Mercator", + "hintName": "in the town of Mercator", + "nodeIds": [ + "mercator", + "mercator_repaired_docks", + "mercator_casino", + "mercator_special_shop" + ] + }, + { + "name": "Crypt", + "hintName": "in the crypt of Mercator", + "nodeIds": [ + "crypt" + ], + "darkMapIds": [ + 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659 + ] + }, + { + "name": "Mercator Dungeon", + "hintName": "in the dungeon of Mercator", + "nodeIds": [ + "mercator_dungeon" + ], + "darkMapIds": [ + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 76, 80, 81, 82, 91, 92 + ] + }, + { + "name": "Mir Tower sector", + "hintName": "near Mir Tower", + "canBeHintedAsRequired": False, + "nodeIds": [ + "mir_tower_sector", + "mir_tower_sector_tree_ledge", + "mir_tower_sector_tree_coast", + "twinkle_village" + ] + }, + { + "name": "Mir Tower", + "hintName": "inside Mir Tower", + "canBeHintedAsRequired": False, + "nodeIds": [ + "mir_tower_pre_garlic", + "mir_tower_post_garlic" + ], + "darkMapIds": [ + 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, + 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784 + ] + }, + { + "name": "Greenmaze", + "hintName": "in Greenmaze", + "nodeIds": [ + "greenmaze_pre_whistle", + "greenmaze_post_whistle" + ] + }, + { + "name": "Verla Shore", + "canBeHintedAsRequired": False, + "nodeIds": [ + "verla_shore", + "verla_shore_cliff" + ] + }, + { + "name": "Verla", + "hintName": "in the town of Verla", + "nodeIds": [ + "verla", + "verla_after_mines" + ] + }, + { + "name": "Verla Mines", + "hintName": "in the mines near Verla", + "nodeIds": [ + "verla_mines", + "verla_mines_behind_lava" + ], + "darkMapIds": [ + 227, 228, 229, 230, 231, 232, 233, 234, 235, 237, 239, 240, 241, 242, 243, 244, 246, + 247, 248, 250, 253, 254, 255, 256, 258, 259, 266, 268, 269, 471 + ] + }, + { + "name": "Route between Verla and Destel", + "canBeHintedAsRequired": False, + "nodeIds": [ + "route_verla_destel" + ] + }, + { + "name": "Destel", + "hintName": "in the village of Destel", + "nodeIds": [ + "destel" + ] + }, + { + "name": "Route after Destel", + "canBeHintedAsRequired": False, + "nodeIds": [ + "route_after_destel" + ] + }, + { + "name": "Destel Well", + "hintName": "in Destel well", + "nodeIds": [ + "destel_well" + ], + "darkMapIds": [ + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290 + ] + }, + { + "name": "Route to Lake Shrine", + "canBeHintedAsRequired": False, + "nodeIds": [ + "route_lake_shrine", + "route_lake_shrine_cliff" + ] + }, + { + "name": "Lake Shrine", + "hintName": "in the lake shrine", + "nodeIds": [ + "lake_shrine" + ], + "darkMapIds": [ + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, + 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354 + ] + }, + { + "name": "Mountainous Area", + "hintName": "in the mountainous area", + "nodeIds": [ + "mountainous_area" + ] + }, + { + "name": "King Nole's Cave", + "hintName": "in King Nole's cave", + "nodeIds": [ + "king_nole_cave" + ], + "darkMapIds": [ + 145, 147, 150, 152, 154, 155, 156, 158, 160, 161, 162, 164, 166, 170, 171, 172 + ] + }, + { + "name": "Kazalt", + "hintName": "in the hidden town of Kazalt", + "nodeIds": [ + "kazalt" + ] + }, + { + "name": "King Nole's Labyrinth", + "hintName": "in King Nole's labyrinth", + "nodeIds": [ + "king_nole_labyrinth_pre_door", + "king_nole_labyrinth_post_door", + "king_nole_labyrinth_exterior", + "king_nole_labyrinth_fall_from_exterior", + "king_nole_labyrinth_path_to_palace", + "king_nole_labyrinth_raft_entrance", + "king_nole_labyrinth_raft", + "king_nole_labyrinth_sacred_tree" + ], + "darkMapIds": [ + 355, 356, 357, 358, 359, 360, 361, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, 392, 393, 394, 395, 396, 397, 398, 405, 406, 408, 409, 410, 411, 412, 413, + 414, 415, 416, 417, 418, 419, 420, 422, 423 + ] + }, + { + "name": "King Nole's Palace", + "hintName": "in King Nole's palace", + "nodeIds": [ + "king_nole_palace", + "end" + ], + "darkMapIds": [ + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138 + ] + } +] \ No newline at end of file diff --git a/worlds/landstalker/data/world_teleport_tree.py b/worlds/landstalker/data/world_teleport_tree.py new file mode 100644 index 000000000000..830f5547201e --- /dev/null +++ b/worlds/landstalker/data/world_teleport_tree.py @@ -0,0 +1,62 @@ +WORLD_TELEPORT_TREES_JSON = [ + [ + { + "name": "Massan tree", + "treeMapId": 512, + "nodeId": "route_massan_gumi" + }, + { + "name": "Tibor tree", + "treeMapId": 534, + "nodeId": "route_gumi_ryuma" + } + ], + [ + { + "name": "Mercator front gate tree", + "treeMapId": 539, + "nodeId": "route_gumi_ryuma" + }, + { + "name": "Verla shore tree", + "treeMapId": 537, + "nodeId": "verla_shore" + } + ], + [ + { + "name": "Destel sector tree", + "treeMapId": 536, + "nodeId": "route_after_destel" + }, + { + "name": "Lake Shrine sector tree", + "treeMapId": 513, + "nodeId": "route_lake_shrine" + } + ], + [ + { + "name": "Mir Tower sector tree", + "treeMapId": 538, + "nodeId": "mir_tower_sector" + }, + { + "name": "Mountainous area tree", + "treeMapId": 535, + "nodeId": "mountainous_area" + } + ], + [ + { + "name": "Greenmaze entrance tree", + "treeMapId": 510, + "nodeId": "greenmaze_pre_whistle" + }, + { + "name": "Greenmaze end tree", + "treeMapId": 511, + "nodeId": "greenmaze_post_whistle" + } + ] +] \ No newline at end of file diff --git a/worlds/landstalker/docs/en_Landstalker - The Treasures of King Nole.md b/worlds/landstalker/docs/en_Landstalker - The Treasures of King Nole.md new file mode 100644 index 000000000000..90a79f8bd986 --- /dev/null +++ b/worlds/landstalker/docs/en_Landstalker - The Treasures of King Nole.md @@ -0,0 +1,60 @@ +# Landstalker: The Treasures of King Nole + +## Where is the settings page? + +The [player settings page for this game](../player-settings) contains most of the options you need to +configure and export a config file. + +## What does randomization do to this game? + +All items are shuffled while keeping a logic to make every seed completable. + +Some key items could be obtained in a very different order compared to the vanilla game, leading to very unusual situations. + +The world is made as open as possible while keeping the original locks behind the same items & triggers as vanilla +when that makes sense logic-wise. This puts the emphasis on exploration and gameplay by removing all the scenario +and story-related triggers, giving a wide open world to explore. + +## What items and locations get shuffled? + +All items and locations are shuffled. This includes **chests**, items on **ground**, in **shops**, and given by **NPCs**. + +It's also worth noting that all of these items are shuffled among all worlds, meaning every item can be sent to you +by other players. + +## What are the main differences compared to the vanilla game? + +The **Key** is now a unique item and can open several doors without being consumed, making it a standard progression item. +All key doors are gone, except three of them : + - the Mercator castle backdoor (giving access to Greenmaze sector) + - Thieves Hideout middle door (cutting the level in half) + - King Nole's Labyrinth door near entrance + +--- + +The secondary shop of Mercator requiring to do the traders sidequest in the original game is now unlocked by having +**Buyer Card** in your inventory. + +You will need as many **jewels** as specified in the settings to use the teleporter to go to Kazalt and the final dungeon. +If you find and use the **Lithograph**, it will tell you in which world are each one of your jewels. + +Each seed, there is a random dungeon which is chosen to be the "dark dungeon" where you won't see anything unless you +have the **Lantern** in your inventory. Unlike vanilla, King Nole's Labyrinth no longer has the few dark rooms the lantern +was originally intended for. + +The **Statue of Jypta** is introduced as a real item (instead of just being an intro gimmick) and gives you gold over +time while you're walking, the same way Healing Boots heal you when you walk. + + +## What do I need to know for my first seed? + +It's advised you keep Massan as your starting region for your first seed, since taking another starting region might +be significantly harder, both combat-wise and logic-wise. + +Having fully open & shuffled teleportation trees is an interesting way to play, but is discouraged for beginners +as well since it can force you to go in late-game zones with few Life Stocks. + +Overall, the default settings are good for a beginner-friendly seed, and if you don't feel too confident, you can also +lower the combat difficulty to make it more forgiving. + +*Have fun on your adventure!* diff --git a/worlds/landstalker/docs/landstalker_setup_en.md b/worlds/landstalker/docs/landstalker_setup_en.md new file mode 100644 index 000000000000..9f453c146de3 --- /dev/null +++ b/worlds/landstalker/docs/landstalker_setup_en.md @@ -0,0 +1,119 @@ +# Landstalker Setup Guide + +## Required Software + +- [Landstalker Archipelago Client](https://github.com/Dinopony/randstalker-archipelago/releases) (only available on Windows) +- A compatible emulator to run the game + - [RetroArch](https://retroarch.com?page=platforms) with the Genesis Plus GX core + - [Bizhawk 2.9.1 (x64)](https://tasvideos.org/BizHawk/ReleaseHistory) with the Genesis Plus GX core +- Your legally obtained Landstalker US ROM file (which can be acquired on [Steam](https://store.steampowered.com/app/71118/Landstalker_The_Treasures_of_King_Nole/)) + +## Installation Instructions + +- Unzip the Landstalker Archipelago Client archive into its own folder +- Put your Landstalker ROM (`LandStalker_USA.SGD` on the Steam release) inside this folder +- To launch the client, launch `randstalker_archipelago.exe` inside that folder + +Be aware that you might get antivirus warnings about the client program because one of its main features is to spy +on another process's memory (your emulator). This is something antiviruses obviously dislike, and sometimes mistake +for malicious software. + +If you're not trusting the program, you can check its [source code](https://github.com/Dinopony/randstalker-archipelago/) +or test it on a service like Virustotal. + +## Create a Config (.yaml) File + +### What is a config file and why do I need one? + +See the guide on setting up a basic YAML at the Archipelago setup +guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en) + +### Where do I get a config file? + +The [Player Settings Page](../player-settings) on the website allows you to easily configure your personal settings +and export a config file from them. + +## How-to-play + +### Connecting to the Archipelago Server + +Once the game has been created, you need to connect to the server using the Landstalker Archipelago Client. + +To do so, run `randstalker_archipelago.exe` inside the folder you created while installing the software. + +A window will open with a few settings to enter: +- **Host**: Put the server address and port in this field (e.g. `archipelago.gg:12345`) +- **Slot name**: Put the player name you specified in your YAML config file in this field. +- **Password**: If the server has a password, put it there. + +![Landstalker Archipelago Client user interface](/static/generated/docs/Landstalker%20-%20The%20Treasures%20of%20King%20Nole/ls_guide_ap.png) + +Once all those fields were filled appropriately, click on the `Connect to Archipelago` button below to try connecting to +the Archipelago server. + +If this didn't work, double-check your credentials. An error message should be displayed on the console log to the +right that might help you find the cause of the issue. + +### ROM Generation + +When you connected to the Archipelago server, the client fetched all the required data from the server to be able to +build a randomized ROM. + +You should see a window with settings to fill: +- **Input ROM file**: This is the path to your original ROM file for the game. If you are using the Steam release ROM + and placed it inside the client's folder as mentioned above, you don't need to change anything. +- **Output ROM directory**: This is where the randomized ROMs will be put. No need to change this unless you want them + to be created in a very specific folder. + +![Landstalker Archipelago Client user interface](/static/generated/docs/Landstalker%20-%20The%20Treasures%20of%20King%20Nole/ls_guide_rom.png) + +There also a few cosmetic options you can fill before clicking the `Build ROM` button which should create your +randomized seed if everything went right. + +If it didn't, double-check your `Input ROM file` and `Output ROM path`, then retry building the ROM by clicking +the same button again. + +### Connecting to the emulator + +Now that you're connected to the Archipelago server and have a randomized ROM, all we need is to get the client +connected to the emulator. This way, the client will be able to see what's happening while you play and give you in-game +the items you have received from other players. + +You should see the following window: + +![Landstalker Archipelago Client user interface](/static/generated/docs/Landstalker%20-%20The%20Treasures%20of%20King%20Nole/ls_guide_emu.png) + +As written, you have to open the newly generated ROM inside either Retroarch or Bizhawk using the Genesis Plus GX core. +Be careful to select that core, because any other core (e.g. BlastEm) won't work. + +The easiest way to do so is to: +- open the emu of your choice +- if you're using Retroarch and it's your first time, download the Genesis Plus GX core through Retroarch user interface +- click the `Show ROM file in explorer` button +- drag-and-drop the shown ROM file on the emulator window +- press Start to reach file select screen (to ensure game RAM is properly set-up) + +Then, you can click on the `Connect to emulator` button below and it should work. + +If this didn't work, try the following: +- ensure you have loaded your ROM and reached the save select screen +- ensure you are using Genesis Plus GX and not another core (e.g. BlastEm will not work) +- try launching the client in Administrator Mode (right-click on `randstalker_archipelago.exe`, then + `Run as administrator`) +- if all else fails, try using one of those specific emulator versions: + - RetroArch 1.9.0 and Genesis Plus GX 1.7.4 + - Bizhawk 2.9.1 (x64) + +### Play the game + +If all indicators are green and show "Connected," you're good to go! Play the game and enjoy the wonders of isometric +perspective. + +The client is packaged with both an **automatic item tracker** and an **automatic map tracker** for your comfort. + +If you don't know all checks in the game, don't be afraid: you can click the `Where is it?` button that will show +you a screenshot of where the location actually is. + +![Landstalker Archipelago Client user interface](/static/generated/docs/Landstalker%20-%20The%20Treasures%20of%20King%20Nole/ls_guide_client.png) + +Have fun! \ No newline at end of file diff --git a/worlds/landstalker/docs/ls_guide_ap.png b/worlds/landstalker/docs/ls_guide_ap.png new file mode 100644 index 000000000000..674938ce6707 Binary files /dev/null and b/worlds/landstalker/docs/ls_guide_ap.png differ diff --git a/worlds/landstalker/docs/ls_guide_client.png b/worlds/landstalker/docs/ls_guide_client.png new file mode 100644 index 000000000000..a4e0f1ccf3d8 Binary files /dev/null and b/worlds/landstalker/docs/ls_guide_client.png differ diff --git a/worlds/landstalker/docs/ls_guide_emu.png b/worlds/landstalker/docs/ls_guide_emu.png new file mode 100644 index 000000000000..ff9218de12ba Binary files /dev/null and b/worlds/landstalker/docs/ls_guide_emu.png differ diff --git a/worlds/landstalker/docs/ls_guide_rom.png b/worlds/landstalker/docs/ls_guide_rom.png new file mode 100644 index 000000000000..c57554ab43d8 Binary files /dev/null and b/worlds/landstalker/docs/ls_guide_rom.png differ