diff --git a/CMakeLists.txt b/CMakeLists.txt index b1c8986597..8478aef4b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -610,6 +610,7 @@ if (DUSK_ENABLE_CODE_MODS AND CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR add_subdirectory(mods/template_mod) add_subdirectory(mods/ao_mod) add_subdirectory(mods/shadow_mod) + add_subdirectory(mods/randomizer) endif () if (APPLE) diff --git a/extern/aurora b/extern/aurora index 81f12f31d2..2bbb1229a9 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 81f12f31d23ec822d8bde2031c91e94c470911eb +Subproject commit 2bbb1229a97707cf0f483a84b366891f6b9c6281 diff --git a/include/Z2AudioLib/Z2SceneMgr.h b/include/Z2AudioLib/Z2SceneMgr.h index 1b3ef2cc0b..27b43d71f4 100644 --- a/include/Z2AudioLib/Z2SceneMgr.h +++ b/include/Z2AudioLib/Z2SceneMgr.h @@ -47,7 +47,7 @@ public: s32 getBgmLoadStatus(u32 wave) { return getWaveLoadStatus(wave, 1); } u8 getDemoSeWaveNum() { return loadedDemoWave; } -private: +// private: /* 0x00 */ JAISoundID BGM_ID; /* 0x04 */ int sceneNum; /* 0x08 */ int timer; diff --git a/include/d/d_stage.h b/include/d/d_stage.h index 53fbbac0d9..eed6f1cfb3 100644 --- a/include/d/d_stage.h +++ b/include/d/d_stage.h @@ -1291,6 +1291,7 @@ public: } void set(const char*, s8, s16, s8, s8, u8); void offEnable() { enabled = 0; } + void onEnable() { enabled = 1; } BOOL isEnable() const { return enabled; } s8 getWipe() const { return wipe; } u8 getWipeSpeed() const { return wipe_speed; } diff --git a/mods/randomizer/CMakeLists.txt b/mods/randomizer/CMakeLists.txt new file mode 100644 index 0000000000..ffaf9a1bb2 --- /dev/null +++ b/mods/randomizer/CMakeLists.txt @@ -0,0 +1,115 @@ +cmake_minimum_required(VERSION 3.25) +project(randomizer CXX) + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(DUSK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." CACHE PATH "Path to dusk source root") + option(DUSK_MOD_USE_FULL_TREE "Use full build instead of the minimal mod SDK" OFF) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + if (DUSK_MOD_USE_FULL_TREE) + add_subdirectory("${DUSK_DIR}" dusk EXCLUDE_FROM_ALL) + else () + add_subdirectory("${DUSK_DIR}/sdk" dusk-sdk EXCLUDE_FROM_ALL) + endif () +endif () + +# Generator dependencies, linked statically into the mod library (no CRT crossing). +include(FetchContent) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "" FORCE) +set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "" FORCE) +set(YAML_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) + +message(STATUS "randomizer: Fetching yaml-cpp") +FetchContent_Declare( + yaml-cpp + GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git + GIT_TAG yaml-cpp-0.9.0 +) +message(STATUS "randomizer: Fetching base64pp") +FetchContent_Declare( + base64pp + GIT_REPOSITORY https://github.com/matheusgomes28/base64pp.git + GIT_TAG v0.2.0-rc0 +) + +message(STATUS "randomizer: Fetching battery-embed") +FetchContent_Declare( + battery-embed + GIT_REPOSITORY https://github.com/batterycenter/embed.git + GIT_TAG fdbae3f +) + +FetchContent_MakeAvailable(yaml-cpp base64pp battery-embed) + +set(RANDOMIZER_GENERATOR_SOURCES + generator/logic/area.cpp + generator/logic/dungeon.cpp + generator/logic/entrance.cpp + generator/logic/entrance_shuffle.cpp + generator/logic/fill.cpp + generator/logic/flatten/bits.cpp + generator/logic/flatten/flatten.cpp + generator/logic/flatten/simplify_algebraic.cpp + generator/logic/hints.cpp + generator/logic/item.cpp + generator/logic/item_pool.cpp + generator/logic/location.cpp + generator/logic/plandomizer.cpp + generator/logic/requirement.cpp + generator/logic/search.cpp + generator/logic/spoiler_log.cpp + generator/logic/world.cpp + generator/randomizer.cpp + generator/seedgen/config.cpp + generator/seedgen/seed.cpp + generator/seedgen/settings.cpp + generator/utility/color.cpp + generator/utility/common.cpp + generator/utility/endian.cpp + generator/utility/file.cpp + generator/utility/log.cpp + generator/utility/path.cpp + generator/utility/platform.cpp + generator/utility/random.cpp + generator/utility/string.cpp + generator/utility/text.cpp + generator/utility/time.cpp +) + +set(RANDOMIZER_SOURCES + src/flags.cpp + src/messages.cpp + src/randomizer_context.cpp + src/stages.cpp + src/tools.cpp + src/verify_item_functions.cpp + src/paths.cpp + src/session.cpp +) + +add_mod(randomizer + FEATURES game + SOURCES src/mod.cpp ${RANDOMIZER_GENERATOR_SOURCES} ${RANDOMIZER_SOURCES} + MOD_JSON mod.json + RES_DIR res +) + +target_link_libraries(randomizer PRIVATE yaml-cpp::yaml-cpp base64pp fmt::fmt) + +string(LENGTH "${CMAKE_CURRENT_SOURCE_DIR}/" RANDOMIZER_SOURCE_PATH_SIZE) +target_compile_definitions(randomizer PRIVATE + RANDO_DATA_PATH="generator/data/" + SOURCE_PATH_SIZE=${RANDOMIZER_SOURCE_PATH_SIZE} +) + +# Embed the generator's YAML data into the mod library. Paths are relative to this +# directory and must match the RANDO_DATA_PATH-based b::embed<> identifiers in code. +file(GLOB_RECURSE RANDOMIZER_DATA RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/generator/data/*") +b_embed(randomizer "res/shadow_crystal.bti") +foreach (RANDOMIZER_FILE IN LISTS RANDOMIZER_DATA) + if (RANDOMIZER_FILE MATCHES "^generator/data/tests") + continue() + endif () + b_embed(randomizer "${RANDOMIZER_FILE}") +endforeach () diff --git a/mods/randomizer/generator/data/entrance_shuffle_data.yaml b/mods/randomizer/generator/data/entrance_shuffle_data.yaml new file mode 100644 index 0000000000..4634003d31 --- /dev/null +++ b/mods/randomizer/generator/data/entrance_shuffle_data.yaml @@ -0,0 +1,2750 @@ +############################### +# SPAWN # +############################### + +- Type: Spawn + Forward: + Connection: Links Spawn -> Outside Links House + Stage: 43 + Room: 1 + Spawn: "01" + Spawn Type: "" + Parameters: "" + State: "FF" + +############################### +# WARP PORTALS # +############################### + +- Type: Warp Portal + Forward: + Connection: Ordon Spring Warp Portal -> Ordon Spring + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: South Faron Woods Warp Portal -> South Faron Woods + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: North Faron Woods Warp Portal -> North Faron Woods + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Sacred Grove Warp Portal -> Sacred Grove Lower + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Kakariko Gorge Warp Portal -> Kakariko Gorge + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Kakariko Village Warp Portal -> Lower Kakariko Village + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Death Mountain Warp Portal -> Death Mountain Volcano + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Bridge of Eldin Warp Portal -> Eldin Field North of Bridge + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Castle Town Warp Portal -> Outside Castle Town West + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Lake Hylia Warp Portal -> Lake Hylia + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Upper Zoras River Warp Portal -> Upper Zoras River + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Zoras Domain Warp Portal -> Zoras Throne Room + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Snowpeak Warp Portal -> Snowpeak Summit Upper + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Gerudo Desert Warp Portal -> Gerudo Desert Cave of Ordeals Plateau + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Warp Portal + Forward: + Connection: Mirror Chamber Warp Portal -> Mirror Chamber Upper + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +############################### +# DUNGEONS # +############################### + +- Type: Dungeon + Forward: + Connection: North Faron Woods -> Forest Temple Entrance + Alias: Faron Woods -> Forest Temple + Stage: 6 + Room: 22 + Spawn: "00" + Spawn Type: "00" + Parameters: "F09F" + State: "FF" + Return: + Connection: Forest Temple Entrance -> North Faron Woods + Alias: Forest Temple -> Faron Woods + Stage: 45 + Room: 6 + Spawn: "01" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Dungeon + Forward: + Connection: Death Mountain Sumo Hall Goron Mines Tunnel -> Goron Mines Entrance + Alias: Death Mountain Sumo Hall -> Goron Mines + Stage: 3 + Room: 1 + Spawn: "01" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + Return: + Connection: Goron Mines Entrance -> Death Mountain Sumo Hall Goron Mines Tunnel + Alias: Goron Mines -> Death Mountain Sumo Hall + Stage: 69 + Room: 0 + Spawn: "01" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Dungeon + Forward: + Connection: Lake Hylia Lakebed Temple Entrance -> Lakebed Temple Entrance + Alias: Lake Hylia -> Lakebed Temple + Stage: 0 + Room: 0 + Spawn: "00" + Spawn Type: "00" + Parameters: "F09F" + State: "FF" + Return: + Connection: Lakebed Temple Entrance -> Lake Hylia Lakebed Temple Entrance + Alias: Lakebed Temple -> Lake Hylia + Stage: 52 + Room: 0 + Spawn: "0B" + Spawn Type: "D0" + Parameters: "F01F" + State: "FF" + +- Type: Dungeon + Forward: + Connection: Outside Arbiters Grounds -> Arbiters Grounds Entrance + Alias: Outside Arbiters Grounds -> Arbiters Grounds + Stage: 24 + Room: 0 + Spawn: "00" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + Return: + Connection: Arbiters Grounds Entrance -> Outside Arbiters Grounds + Alias: Arbiters Grounds -> Outside Arbiters Grounds + Stage: 55 + Room: 3 + Spawn: "03" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Dungeon + Door Couple Tag: Snowpeak Ruins Entrance + Forward: + Connection: Snowpeak Ruins East Door Exterior -> Snowpeak Ruins East Door Interior + Alias: Outside Snowpeak Ruins -> Snowpeak Ruins East Door + Stage: 27 + Room: 0 + Spawn: "02" + Spawn Type: "A0" + Parameters: "F01F" + State: "FF" + Return: + Connection: Snowpeak Ruins East Door Interior -> Snowpeak Ruins East Door Exterior + Alias: Snowpeak Ruins East Door -> Outside Snowpeak Ruins + Stage: 51 + Room: 1 + Spawn: "0A" + Spawn Type: "0B" + Parameters: "F01F" + State: "FF" + +- Type: Dungeon + Door Couple Tag: Snowpeak Ruins Entrance + Forward: + Connection: Snowpeak Ruins West Door Exterior -> Snowpeak Ruins West Door Interior + Alias: Outside Snowpeak Ruins -> Snowpeak Ruins West Door + Stage: 27 + Room: 0 + Spawn: "01" + Spawn Type: "A0" + Parameters: "F01F" + State: "FF" + Return: + Connection: Snowpeak Ruins West Door Interior -> Snowpeak Ruins West Door Exterior + Alias: Snowpeak Ruins West Door -> Outside Snowpeak Ruins + Stage: 51 + Room: 1 + Spawn: "09" + Spawn Type: "0B" + Parameters: "F01F" + State: "FF" + +- Type: Dungeon + Forward: + Connection: Sacred Grove Past Behind Window -> Temple of Time Entrance + Alias: Sacred Grove Past -> Temple of Time + Stage: 9 + Room: 0 + Spawn: "00" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Temple of Time Entrance -> Sacred Grove Past Behind Window + Alias: Temple of Time -> Sacred Grove Past + Stage: 54 + Room: 2 + Spawn: "01" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Dungeon + Forward: + Connection: Lake Hylia -> City in the Sky Entrance + Alias: Lake Hylia -> City in the Sky + Stage: 12 + Room: 0 + Spawn: "02" + Spawn Type: "00" + Parameters: "C00F" + State: "FF" + Return: + Connection: City in the Sky Entrance -> Lake Hylia + Alias: City in the Sky -> Lake Hylia + Stage: 52 + Room: 0 + Spawn: "4D" + Spawn Type: "40" + Parameters: "C00F" + State: "FF" + +- Type: Dungeon + Forward: + Connection: Twilight Realm Portal -> Palace of Twilight Entrance + Alias: Mirror Chamber -> Palace of Twilight + Stage: 15 + Room: 0 + Spawn: "0A" + Spawn Type: "00" + Parameters: "F01F" + State: "E" + Return: + Connection: Palace of Twilight Entrance -> Twilight Realm Portal + Alias: Palace of Twilight -> Mirror Chamber + Stage: 60 + Room: 4 + Spawn: "04" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Dungeon + Forward: + Connection: Castle Town North Inside Barrier -> Hyrule Castle Entrance + Alias: Castle Town -> Hyrule Castle + Stage: 20 + Room: 11 + Spawn: "00" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + Return: + Connection: Hyrule Castle Entrance -> Castle Town North Inside Barrier + Alias: Hyrule Castle -> Castle Town + Stage: 53 + Room: 1 + Spawn: "32" + Spawn Type: "10" + Parameters: "F01F" + State: "FF" + +############################### +# BOSSES # +############################### + +- Type: Boss + Forward: + Connection: Forest Temple Boss Door Room North Side -> Forest Temple Boss Room + Alias: Forest Temple -> Forest Temple Boss Room + Stage: 7 + Room: 50 + Spawn: "01" + Spawn Type: "60" + Parameters: "F01F" + State: "FF" + Return: + Connection: Forest Temple Boss Room -> South Faron Woods + Alias: Forest Temple Boss Room -> Faron Woods + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Boss + Forward: + Connection: Goron Mines Boss Door Room Near Boss Door -> Goron Mines Boss Room + Alias: Goron Mines -> Goron Mines Boss Room + Stage: 4 + Room: 50 + Spawn: "01" + Spawn Type: "60" + Parameters: "F01F" + State: "FF" + Return: + Connection: Goron Mines Boss Room -> Lower Kakariko Village + Alias: Goron Mines Boss Room -> Kakariko Village + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Boss + Forward: + Connection: Lakebed Temple Central Room Past Boss Door -> Lakebed Temple Boss Room + Alias: Lakebed Temple -> Lakebed Temple Boss Room + Stage: 1 + Room: 50 + Spawn: "01" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + Return: + Connection: Lakebed Temple Boss Room -> Lake Hylia Lanayru Spring + Alias: Lakebed Temple Boss Room -> Lanayru Spring + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Boss + Forward: + Connection: Arbiters Grounds Socket Room Near Boss Door -> Arbiters Grounds Boss Room + Alias: Arbiters Grounds -> Arbiters Grounds Boss Room + Stage: 25 + Room: 50 + Spawn: "00" + Spawn Type: "60" + Parameters: "F01F" + State: "FF" + Return: + Connection: Arbiters Grounds Boss Room -> Mirror Chamber Lower + Alias: Arbiters Grounds Boss Room -> Mirror Chamber + Stage: 60 + Room: 4 + Spawn: "00" + Spawn Type: "10" + Parameters: "501F" + State: "FF" + +- Type: Boss + Forward: + Connection: Snowpeak Ruins West Courtyard North Balcony -> Snowpeak Ruins Boss Room + Alias: Snowpeak Ruins -> Snowpeak Ruins Boss Room + Stage: 28 + Room: 50 + Spawn: "01" + Spawn Type: "60" + Parameters: "F01F" + State: "FF" + Return: + Connection: Snowpeak Ruins Boss Room -> Snowpeak Summit Lower + Alias: Snowpeak Ruins Boss Room -> Outside Snowpeak Ruins + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Boss + Forward: + Connection: Temple of Time Crumbling Corridor Near Boss Door -> Temple of Time Boss Room + Alias: Temple of Time -> Temple of Time Boss Room + Stage: 10 + Room: 50 + Spawn: "00" + Spawn Type: "60" + Parameters: "F01F" + State: "FF" + Return: + Connection: Temple of Time Boss Room -> Sacred Grove Past Behind Window + Alias: Temple of Time Boss Room -> Sacred Grove Past + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Boss + Forward: + Connection: City in the Sky North Tower Top -> City in the Sky Boss Room + Alias: City in the Sky -> City in the Sky Boss Room + Stage: 13 + Room: 50 + Spawn: "01" + Spawn Type: "60" + Parameters: "F01F" + State: "FF" + Return: + Connection: City in the Sky Boss Room -> City in the Sky Entrance + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Boss + Forward: + Connection: Palace of Twilight Boss Door Room -> Palace of Twilight Boss Room + Alias: Palace of Twilight -> Palace of Twilight Boss Room + Stage: 16 + Room: 10 + Spawn: "00" + Spawn Type: "60" + Parameters: "F01F" + State: "FF" + Return: + Connection: Palace of Twilight Boss Room -> Palace of Twilight Entrance + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +############################### +# GROTTOS # +############################### + +- Type: Grotto + Forward: + Connection: Ordon Ranch -> Ordon Ranch Grotto + Stage: 35 + Room: 0 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "2" + Return: + Connection: Ordon Ranch Grotto -> Ordon Ranch + Stage: 41 + Room: 0 + Spawn: "05" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Lost Woods Lower Battle Arena -> Lost Woods Baba Serpent Grotto + Alias: Lost Woods -> Lost Woods Baba Serpent Grotto + Stage: 36 + Room: 1 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F01F" + State: "2" + Return: + Connection: Lost Woods Baba Serpent Grotto -> Lost Woods Lower Battle Arena + Alias: Lost Woods Baba Serpent Grotto -> Lost Woods + Stage: 54 + Room: 3 + Spawn: "05" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Faron Field -> Faron Field Corner Grotto + Stage: 36 + Room: 1 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F01F" + State: "1" + Return: + Connection: Faron Field Corner Grotto -> Faron Field + Stage: 56 + Room: 6 + Spawn: "02" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Faron Field -> Faron Field Fishing Grotto + Stage: 39 + Room: 4 + Spawn: "00" + Spawn Type: "00" + Parameters: "F010" + State: "1" + Return: + Connection: Faron Field Fishing Grotto -> Faron Field + Stage: 56 + Room: 6 + Spawn: "03" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Kakariko Gorge -> Kakariko Gorge Keese Grotto + Stage: 36 + Room: 1 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F01F" + State: "3" + Return: + Connection: Kakariko Gorge Keese Grotto -> Kakariko Gorge + Stage: 56 + Room: 3 + Spawn: "04" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Eldin Field -> Eldin Field Bomskit Grotto + Stage: 35 + Room: 0 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "1" + Return: + Connection: Eldin Field Bomskit Grotto -> Eldin Field + Stage: 56 + Room: 0 + Spawn: "0C" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Eldin Field -> Eldin Field Water Bomb Fish Grotto + Stage: 39 + Room: 4 + Spawn: "00" + Spawn Type: "00" + Parameters: "F010" + State: "3" + Return: + Connection: Eldin Field Water Bomb Fish Grotto -> Eldin Field + Stage: 56 + Room: 0 + Spawn: "0D" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Eldin Field Grotto Platform -> Eldin Field Stalfos Grotto + Alias: Eldin Field -> Eldin Field Stalfos Grotto + Stage: 36 + Room: 1 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F01F" + State: "0" + Return: + Connection: Eldin Field Stalfos Grotto -> Eldin Field Grotto Platform + Alias: Eldin Field Stalfos Grotto -> Eldin Field + Stage: 56 + Room: 7 + Spawn: "02" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Lanayru Field -> Lanayru Field Chu Grotto + Stage: 37 + Room: 2 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "3" + Return: + Connection: Lanayru Field Chu Grotto -> Lanayru Field + Stage: 56 + Room: 10 + Spawn: "08" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Lanayru Field -> Lanayru Field Skulltula Grotto + Stage: 38 + Room: 3 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "3" + Return: + Connection: Lanayru Field Skulltula Grotto -> Lanayru Field + Stage: 56 + Room: 10 + Spawn: "06" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Lanayru Field -> Lanayru Field Poe Grotto + Stage: 35 + Room: 0 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "3" + Return: + Connection: Lanayru Field Poe Grotto -> Lanayru Field + Stage: 56 + Room: 10 + Spawn: 07"" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Outside Castle Town West Grotto Ledge -> Outside Castle Town West Helmasaur Grotto + Alias: Outside Castle Town West -> Outside Castle Town West Helmasaur Grotto + Stage: 35 + Room: 0 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "0" + Return: + Connection: Outside Castle Town West Helmasaur Grotto -> Outside Castle Town West Grotto Exit + Alias: Outside Castle Town West Helmasaur Grotto -> Outside Castle Town West + Stage: 57 + Room: 8 + Spawn: "05" + Spawn Type: "90" + Parameters: F01F"" + State: "FF" + +- Type: Grotto + Forward: + Connection: Outside Castle Town South Tektite Grotto Platform -> Outside Castle Town South Tektite Grotto + Alias: Outside Castle Town South -> Outside Castle Town South Tektite Grotto + Stage: 39 + Room: 4 + Spawn: "00" + Spawn Type: "00" + Parameters: "F010" + State: "0" + Return: + Connection: Outside Castle Town South Tektite Grotto -> Outside Castle Town South Tektite Grotto Platform + Alias: Outside Castle Town South Tektite Grotto -> Outside Castle Town South + Stage: 57 + Room: 16 + Spawn: "03" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Lake Hylia Bridge Grotto Ledge -> Lake Hylia Bridge Bubble Grotto + Alias: Lake Hylia Bridge -> Lake Hylia Bridge Bubble Grotto + Stage: 38 + Room: 3 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "0" + Return: + Connection: Lake Hylia Bridge Bubble Grotto -> Lake Hylia Bridge Grotto Ledge + Alias: Lake Hylia Bridge Bubble Grotto -> Lake Hylia Bridge + Stage: 56 + Room: 13 + Spawn: "03" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Lake Hylia Upper Area -> Lake Hylia Water Toadpoli Grotto + Alias: Lake Hylia -> Lake Hylia Water Toadpoli Grotto + Stage: 39 + Room: 4 + Spawn: "00" + Spawn Type: "00" + Parameters: "F010" + State: "2" + Return: + Connection: Lake Hylia Water Toadpoli Grotto -> Lake Hylia Upper Area + Alias: Lake Hylia Water Toadpoli Grotto -> Lake Hylia + Stage: 52 + Room: 0 + Spawn: "04" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Lake Hylia Shell Blade Grotto Ledge -> Lake Hylia Shell Blade Grotto + Alias: Lake Hylia -> Lake Hylia Shell Blade Grotto + Stage: 39 + Room: 4 + Spawn: "00" + Spawn Type: "00" + Parameters: "F010" + State: "4" + Return: + Connection: Lake Hylia Shell Blade Grotto -> Lake Hylia Shell Blade Grotto Ledge + Alias: Lake Hylia Shell Blade Grotto -> Lake Hylia + Stage: 52 + Room: 0 + Spawn: "63" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Gerudo Desert -> Gerudo Desert Skulltula Grotto + Stage: 38 + Room: 3 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "1" + Return: + Connection: Gerudo Desert Skulltula Grotto -> Gerudo Desert + Stage: 59 + Room: 0 + Spawn: "08" + Spawn Type: "90" + Parameters: "F01F" + State: "0" + +- Type: Grotto + Forward: + Connection: Gerudo Desert Basin -> Gerudo Desert Chu Grotto + Alias: Gerudo Desert -> Gerudo Desert Chu Grotto + Stage: 37 + Room: 2 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "1" + Return: + Connection: Gerudo Desert Chu Grotto -> Gerudo Desert Basin + Alias: Gerudo Desert Chu Grotto -> Gerudo Desert + Stage: 59 + Room: 0 + Spawn: "0A" + Spawn Type: "90" + Parameters: "F01F" + State: "0" + +- Type: Grotto + Forward: + Connection: Gerudo Desert North East Ledge -> Gerudo Desert Rock Grotto + Alias: Gerudo Desert -> Gerudo Desert Rock Grotto + Stage: 37 + Room: 2 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "0" + Return: + Connection: Gerudo Desert Rock Grotto -> Gerudo Desert North East Ledge + Alias: Gerudo Desert Rock Grotto -> Gerudo Desert + Stage: 59 + Room: 0 + Spawn: "09" + Spawn Type: "90" + Parameters: "F01F" + State: "0" + +- Type: Grotto + Forward: + Connection: Snowpeak Climb Upper -> Snowpeak Ice Keese Grotto + Alias: Snowpeak Climb -> Snowpeak Ice Keese Grotto + Stage: 37 + Room: 2 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "2" + Return: + Connection: Snowpeak Ice Keese Grotto -> Snowpeak Climb Upper + Alias: Snowpeak Ice Keese Grotto -> Snowpeak Climb + Stage: 51 + Room: 0 + Spawn: "02" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +- Type: Grotto + Forward: + Connection: Snowpeak Climb Upper -> Snowpeak Freezard Grotto + Alias: Snowpeak Climb -> Snowpeak Freezard Grotto + Stage: 38 + Room: 3 + Spawn: "00" + Spawn Type: "C0" + Parameters: "F013" + State: "2" + Return: + Connection: Snowpeak Freezard Grotto -> Snowpeak Climb Upper + Alias: Snowpeak Freezard Grotto -> Snowpeak Climb + Stage: 51 + Room: 0 + Spawn: "01" + Spawn Type: "90" + Parameters: "F01F" + State: "FF" + +############################### +# INTERIORS # +############################### + +- Type: Interior + Forward: + Connection: Outside Links House -> Ordon Links House + Alias: Outside Links House -> Links House + Stage: 65 + Room: 4 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Ordon Links House -> Outside Links House + Alias: Links House -> Outside Links House + Stage: 43 + Room: 1 + Spawn: "03" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Ordon Village -> Ordon Seras Shop + Stage: 65 + Room: 1 + Spawn: "00" + Spawn Type: "10" + Parameters: "F09F" + State: "FF" + Return: + Connection: Ordon Seras Shop -> Ordon Village + Stage: 43 + Room: 0 + Spawn: "05" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Ordon Village -> Ordon Sword House + Stage: 65 + Room: 5 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Ordon Sword House -> Ordon Village + Stage: 43 + Room: 0 + Spawn: "09" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Ordon Village -> Ordon Shield House + Stage: 65 + Room: 2 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Ordon Shield House -> Ordon Village + Stage: 43 + Room: 0 + Spawn: "06" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Ordon Village -> Ordon Shield House Upper Ledge + Stage: + Room: + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + Return: + Connection: Ordon Shield House Upper Ledge -> Ordon Village + Stage: + Room: + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Interior + Forward: + Connection: Ordon Village -> Ordon Fados House + Stage: + Room: + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + Return: + Connection: Ordon Fados House -> Ordon Village + Stage: + Room: + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Interior + Door Couple Tag: Ordon Bos House + Forward: + Connection: Ordon Bos House Left Door Exterior -> Ordon Bos House Left Door Interior + Alias: Ordon Village -> Ordon Bos House Left Door + Stage: 65 + Room: 0 + Spawn: "01" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Ordon Bos House Left Door Interior -> Ordon Bos House Left Door Exterior + Alias: Ordon Bos House Left Door -> Ordon Village + Stage: 43 + Room: 0 + Spawn: "0B" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Door Couple Tag: Ordon Bos House + Forward: + Connection: Ordon Bos House Right Door Exterior -> Ordon Bos House Right Door Interior + Alias: Ordon Village -> Ordon Bos House Right Door + Stage: 65 + Room: 0 + Spawn: "00" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Ordon Bos House Right Door Interior -> Ordon Bos House Right Door Exterior + Alias: Ordon Bos House Right Door -> Ordon Village + Stage: 43 + Room: 0 + Spawn: "04" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: South Faron Woods -> Faron Woods Coros House Lower + Alias: Faron Woods -> Coros House Door + Stage: 67 + Room: 0 + Spawn: "01" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Faron Woods Coros House Lower -> South Faron Woods + Alias: Coros House Door -> Faron Woods + Stage: 45 + Room: 4 + Spawn: "01" + Spawn Type: "A0" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Forward: + Connection: South Faron Woods Coros Ledge -> Faron Woods Coros House Upper + Alias: Faron Woods -> Coros House Window + Stage: 67 + Room: 0 + Spawn: "00" + Spawn Type: "00" + Parameters: "F05F" + State: "FF" + Return: + Connection: Faron Woods Coros House Upper -> South Faron Woods Coros Ledge + Alias: Coros House Window -> Faron Woods + Stage: 45 + Room: 4 + Spawn: "09" + Spawn Type: "00" + Parameters: "F05F" + State: "FF" + +- Type: Interior + Door Couple Tag: Renados Sanctuary Front Door + Forward: + Connection: Renados Sanctuary Front East Door Exterior -> Renados Sanctuary Front East Door Interior + Alias: Kakriko Village -> Renados Sanctuary Front East Door + Stage: 68 + Room: 0 + Spawn: "06" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Renados Sanctuary Front East Door Interior -> Renados Sanctuary Front East Door Exterior + Alias: Renados Sanctuary Front East Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "30" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Door Couple Tag: Renados Sanctuary Front Door + Forward: + Connection: Renados Sanctuary Front West Door Exterior -> Renados Sanctuary Front West Door Interior + Alias: Kakriko Village -> Renados Sanctuary Front West Door + Stage: 68 + Room: 0 + Spawn: "05" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Renados Sanctuary Front West Door Interior -> Renados Sanctuary Front West Door Exterior + Alias: Renados Sanctuary Front West Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "2E" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Door Couple Tag: Renados Sanctuary Back Door + Forward: + Connection: Renados Sanctuary Back East Door Exterior -> Renados Sanctuary Back East Door Interior + Alias: Kakriko Village -> Renados Sanctuary Back East Door + Stage: 68 + Room: 0 + Spawn: "08" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Renados Sanctuary Back East Door Interior -> Renados Sanctuary Back East Door Exterior + Alias: Renados Sanctuary Back East Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "33" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Door Couple Tag: Renados Sanctuary Back Door + Forward: + Connection: Renados Sanctuary Back West Door Exterior -> Renados Sanctuary Back West Door Interior + Alias: Kakriko Village -> Renados Sanctuary Back West Door + Stage: 68 + Room: 0 + Spawn: "07" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Renados Sanctuary Back West Door Interior -> Renados Sanctuary Back West Door Exterior + Alias: Renados Sanctuary Back West Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "32" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Kakariko Renados Sanctuary -> Kakariko Renados Sanctuary Basement + Stage: 75 + Room: 5 + Spawn: "00" + Spawn Type: "FF" + Parameters: "FFFF" + State: "FF" + Return: + Connection: Kakariko Renados Sanctuary Basement -> Kakariko Renados Sanctuary + Stage: 68 + Room: 0 + Spawn: "02" + Spawn Type: "FF" + Parameters: "FFFF" + State: "FF" + +- Type: Interior + Forward: + Connection: Lower Kakariko Village -> Kakariko Malo Mart + Alias: Kakariko Village -> Kakariko Malo Mart + Stage: 68 + Room: 3 + Spawn: "00" + Spawn Type: "10" + Parameters: "f09f" + State: "FF" + Return: + Connection: Kakariko Malo Mart -> Lower Kakariko Village + Alias: Kakariko Malo Mart -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "28" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Door Couple Tag: Elde Inn + Forward: + Connection: Elde Inn North Door Exterior -> Elde Inn North Door Interior + Alias: Kakariko Village -> Edle Inn North Door + Stage: 68 + Room: 2 + Spawn: "02" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Elde Inn North Door Interior -> Elde Inn North Door Exterior + Alias: Elde Inn North Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "31" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Door Couple Tag: Elde Inn + Forward: + Connection: Elde Inn South Door Exterior -> Elde Inn South Door Interior + Alias: Kakariko Village -> Elde Inn South Door + Stage: 68 + Room: 2 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Elde Inn South Door Interior -> Elde Inn South Door Exterior + Alias: Elde Inn South Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "2A" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Kakariko Bug House Door -> Kakariko Bug House + Alias: Kakariko Village -> Kakariko Bug House Door + Stage: 68 + Room: 6 + Spawn: "05" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Kakariko Bug House -> Kakariko Bug House Door + Alias: Kakariko Bug House Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "29" + Spawn Type: "90" + Parameters: "F03F" + State: "FF" + +- Type: Interior + Forward: + Connection: Kakariko Bug House Ceiling Hole -> Kakariko Bug House + Alias: Kakariko Village -> Kakariko Bug House Ceiling Hole + Stage: 68 + Room: 6 + Spawn: "00" + Spawn Type: "00" + Parameters: "F03F" + State: "FF" + Return: + Connection: Kakariko Bug House -> Kakariko Bug House Ceiling Hole + Alias: Kakariko Bug House Ceiling Hole -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "04" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Forward: + Connection: Lower Kakariko Village -> Kakariko Barnes Bomb Shop Lower + Alias: Kakariko Village -> Kakariko Barnes Bomb Shop Door + Stage: 68 + Room: 1 + Spawn: "02" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Kakariko Barnes Bomb Shop Lower -> Lower Kakariko Village + Alias: Kakariko Barnes Bomb Shop Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "2C" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Upper Kakariko Village -> Kakariko Barnes Bomb Shop Upper + Alias: Kakariko Village -> Kakariko Barnes Bomb Shop Window + Stage: 68 + Room: 1 + Spawn: "00" + Spawn Type: "00" + Parameters: "F09F" + State: "FF" + Return: + Connection: Kakariko Barnes Bomb Shop Upper -> Upper Kakariko Village + Alias: Kakariko Barnes Bomb Shop Window -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "0A" + Spawn Type: "10" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Kakariko Watchtower Lower Door -> Kakariko Watchtower Lower Interior + Alias: Kakariko Village -> Kakariko Watchtower Lower Door + Stage: 68 + Room: 4 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Kakariko Watchtower Lower Interior -> Kakariko Watchtower Lower Door + Alias: Kakariko Watchtower Lower Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "2D" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Kakariko Watchtower Dig Spot -> Kakariko Watchtower Lower Interior + Alias: Kakariko Village -> Kakariko Watchtower Dig Spot + Stage: 68 + Room: 4 + Spawn: "01" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Kakariko Watchtower Lower Interior -> Kakariko Watchtower Dig Spot + Alias: Kakariko Watchtower Dig Spot -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "03" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Forward: + Connection: Kakariko Top of Watchtower -> Kakariko Watchtower Upper Interior + Alias: Kakariko Village -> Kakariko Watchtower Upper Door + Stage: 68 + Room: 4 + Spawn: "02" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Kakariko Watchtower Upper Interior -> Kakariko Top of Watchtower + Alias: Kakariko Watchtower Upper Door -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "2F" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Forward: + Connection: Death Mountain Outside Sumo Hall -> Death Mountain Sumo Hall + Alias: Death Mountain -> Death Mountain Sumo Hall + Stage: 69 + Room: 0 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Death Mountain Sumo Hall -> Death Mountain Outside Sumo Hall + Alias: Death Mountain Sumo Hall -> Death Mountain + Stage: 47 + Room: 3 + Spawn: "01" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Forward: + Connection: Death Mountain Lower Elevator -> Death Mountain Sumo Hall Elevator + Alias: Death Mountain Elevator -> Death Mountain Sumo Hall + Stage: 69 + Room: 0 + Spawn: "03" + Spawn Type: "E0" + Parameters: "F01F" + State: "FF" + Return: + Connection: Death Mountain Sumo Hall Elevator -> Death Mountain Lower Elevator + Alias: Death Mountain Sumo Hall -> Death Mountain Elevator + Stage: 47 + Room: 3 + Spawn: "03" + Spawn Type: "E0" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Forward: + Connection: Hidden Village -> Hidden Village Impaz House + Stage: 72 + Room: 0 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Hidden Village Impaz House -> Hidden Village + Stage: 63 + Room: 0 + Spawn: "01" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Upper Zoras River -> Upper Zoras River Izas House + Stage: 49 + Room: 1 + Spawn: "01" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Upper Zoras River Izas House -> Upper Zoras River + Stage: 61 + Room: 0 + Spawn: "04" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Fishing Hole -> Fishing Hole House + Stage: 71 + Room: 0 + Spawn: "00" + Spawn Type: "A0" + Parameters: "F01F" + State: "FF" + Return: + Connection: Fishing Hole House -> Fishing Hole + Stage: 62 + Room: 0 + Spawn: "03" + Spawn Type: "B0" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Forward: + Connection: Castle Town West -> Castle Town STAR Game + Stage: 74 + Room: 7 + Spawn: "03" + Spawn Type: "10" + Parameters: "F090" + State: "FF" + Return: + Connection: Castle Town STAR Game -> Castle Town West + Stage: 53 + Room: 2 + Spawn: "01" + Spawn Type: "10" + Parameters: "F01F" + State: "FF" + + +- Type: Interior + Door Couple Tag: Castle Town Goron House + Forward: + Connection: Castle Town Goron House West Door Exterior -> Castle Town Goron House West Door Interior + Alias: Castle Town Center -> Castle Town Goron House West Door + Stage: 73 + Room: 4 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town Goron House West Door Interior -> Castle Town Goron House West Door Exterior + Alias: Castle Town Goron House West Door -> Castle Town Center + Stage: 53 + Room: 0 + Spawn: "0E" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Door Couple Tag: Castle Town Goron House + Forward: + Connection: Castle Town Goron House East Door Exterior -> Castle Town Goron House East Door Interior + Alias: Castle Town Center -> Castle Town Goron House East Door + Stage: 73 + Room: 4 + Spawn: "02" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town Goron House East Door Interior -> Castle Town Goron House East Door Exterior + Alias: Castle Town Goron House East Door -> Castle Town Center + Stage: 53 + Room: 0 + Spawn: "10" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Forward: + Connection: Castle Town Center -> Castle Town Malo Mart + Stage: 73 + Room: 0 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town Malo Mart -> Castle Town Center + Stage: 53 + Room: 0 + Spawn: "0C" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Door Couple Tag: Castle Town Doctors Office + Forward: + Connection: Castle Town Doctors Office West Door Exterior -> Castle Town Doctors Office West Door Interior + Alias: Castle Town East -> Castle Town Doctors Office West Door + Stage: 73 + Room: 2 + Spawn: "01" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town Doctors Office West Door Interior -> Castle Town Doctors Office West Door Exterior + Alias: Castle Town Doctors Office West Door -> Castle Town East + Stage: 53 + Room: 4 + Spawn: "04" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Door Couple Tag: Castle Town Doctors Office + Forward: + Connection: Castle Town Doctors Office East Door Exterior -> Castle Town Doctors Office East Door Interior + Alias: Castle Town East -> Castle Town Doctors Office East Door + Stage: 73 + Room: 2 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F01F" + State: "FF" + Return: + Connection: Castle Town Doctors Office East Door Interior -> Castle Town Doctors Office East Door Exterior + Alias: Castle Town Doctors Office East Door -> Castle Town East + Stage: 53 + Room: 4 + Spawn: "03" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Castle Town Doctors Office Balcony -> Castle Town Doctors Office Upper + Alias: Castle Town Doctors Office Balcony -> Castle Town Doctors Office + Stage: 73 + Room: 2 + Spawn: "02" + Spawn Type: "10" + Parameters: "F01F" + State: "FF" + Return: + Connection: Castle Town Doctors Office Upper -> Castle Town Doctors Office Balcony + Alias: Castle Town Doctors Office -> Castle Town Doctors Office Balcony + Stage: 53 + Room: 4 + Spawn: "05" + Spawn Type: "00" + Parameters: "F05F" + State: "FF" + +- Type: Interior + Forward: + Connection: Castle Town South -> Castle Town Agithas House + Stage: 73 + Room: 3 + Spawn: "01" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town Agithas House -> Castle Town South + Stage: 53 + Room: 3 + Spawn: "05" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Castle Town South -> Castle Town Seer House + Stage: 73 + Room: 1 + Spawn: "00" + Spawn Type: "10" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town Seer House -> Castle Town South + Stage: 53 + Room: 3 + Spawn: "04" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Interior + Forward: + Connection: Castle Town South -> Castle Town Jovanis House + Stage: 73 + Room: 5 + Spawn: "00" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Castle Town Jovanis House -> Castle Town South + Stage: 53 + Room: 3 + Spawn: "0A" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Interior + Forward: + Connection: Castle Town South -> Castle Town Telmas Bar + Stage: 70 + Room: 5 + Spawn: "00" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town Telmas Bar -> Castle Town South + Stage: 53 + Room: 3 + Spawn: "01" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +############################### +# CAVES # +############################### + +- Type: Cave + Forward: + Connection: South Faron Woods Behind Gate -> Faron Woods Cave South + Alias: South Faron Woods -> Faron Woods Cave + Stage: 40 + Room: 0 + Spawn: "00" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Faron Woods Cave South -> South Faron Woods Behind Gate + Alias: Faron Woods Cave -> South Faron Woods + Stage: 45 + Room: 3 + Spawn: "63" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Cave + Forward: + Connection: Mist Area Near Faron Woods Cave -> Faron Woods Cave North + Alias: Faron Woods Mist Area -> Faron Woods Cave + Stage: 40 + Room: 0 + Spawn: "01" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Faron Woods Cave North -> Mist Area Near Faron Woods Cave + Alias: Faron Woods Cave -> Faron Woods Mist Area + Stage: 45 + Room: 5 + Spawn: "04" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Cave + Forward: + Connection: Mist Area Outside Faron Mist Cave -> Mist Area Faron Mist Cave + Alias: Faron Woods Mist Area -> Faron Woods Mist Cave + Stage: 45 + Room: 14 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Mist Area Faron Mist Cave -> Mist Area Outside Faron Mist Cave + Alias: Faron Woods Mist Cave -> Faron Woods Mist Area + Stage: 45 + Room: 5 + Spawn: "06" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Cave + Forward: + Connection: Kakariko Gorge Cave Entrance -> Eldin Lantern Cave + Alias: Kakariko Gorge -> Eldin Lantern Cave + Stage: 32 + Room: 0 + Spawn: "00" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + Return: + Connection: Eldin Lantern Cave -> Kakariko Gorge Cave Entrance + Alias: Eldin Lantern Cave -> Kakariko Gorge + Stage: 56 + Room: 3 + Spawn: "0F" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Cave + Forward: + Connection: Eldin Field Lava Cave Upper Ledge -> Eldin Field Lava Cave Upper + Alias: Eldin Field -> Eldin Field Lava Cave Upper + Stage: 34 + Room: 10 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Eldin Field Lava Cave Upper -> Eldin Field Lava Cave Upper Ledge + Alias: Eldin Field Lava Cave Upper -> Eldin Field + Stage: 56 + Room: 0 + Spawn: "14" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Cave + Forward: + Connection: Eldin Field Lava Cave Lower -> Eldin Field Lava Cave Lower Ledge + Alias: Eldin Field Lava Cave Lower -> Eldin Field + Stage: 56 + Room: 0 + Spawn: "15" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Eldin Field Lava Cave Lower Ledge -> Eldin Field Lava Cave Lower + Alias: Eldin Field -> Eldin Field Lava Cave Lower + Stage: 34 + Room: 10 + Spawn: "01" + Spawn Type: "00" + Parameters: "F09F" + State: "FF" + +- Type: Cave + Forward: + Connection: Lanayru Field Cave Entrance -> Lanayru Ice Puzzle Cave + Alias: Lanayru Field -> Lanayru Ice Puzzle Cave + Stage: 30 + Room: 0 + Spawn: "00" + Spawn Type: "00" + Parameters: "F09F" + State: "FF" + Return: + Connection: Lanayru Ice Puzzle Cave -> Lanayru Field Cave Entrance + Alias: Lanayru Ice Puzzle Cave -> Lanayru Field + Stage: 56 + Room: 10 + Spawn: "0F" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Cave + Forward: + Connection: Lake Hylia -> Lake Hylia Lanayru Spring + Alias: Lake Hylia -> Lanayru Spring + Stage: 52 + Room: 1 + Spawn: "00" + Spawn Type: "50" + Parameters: "C000" + State: "FF" + Return: + Connection: Lake Hylia Lanayru Spring -> Lake Hylia + Alias: Lanayru Spring -> Lake Hylia + Stage: 52 + Room: 0 + Spawn: "07" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Cave + Forward: + Connection: Lake Hylia Cave Entrance -> Lake Hylia Long Cave + Alias: Lake Hylia -> Lake Hylia Long Cave + Stage: 33 + Room: 0 + Spawn: "00" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Lake Hylia Long Cave -> Lake Hylia Cave Entrance + Alias: Lake Hylia Long Cave -> Lake Hylia + Stage: 52 + Room: 0 + Spawn: "1D" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Cave + Forward: + Connection: Gerudo Desert Cave of Ordeals Plateau -> Cave of Ordeals + Alias: Gerudo Desert -> Cave of Ordeals + Stage: 31 + Room: 0 + Spawn: "00" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Cave of Ordeals -> Gerudo Desert Cave of Ordeals Plateau + Alias: Cave of Ordeals -> Gerudo Desert + Stage: 59 + Room: 0 + Spawn: "06" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +############################### +# OVERWORLD # +############################### + +- Type: Overworld + Forward: + Connection: Outside Links House -> Ordon Village + Stage: 43 + Room: 0 + Spawn: "01" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + Return: + Connection: Ordon Village -> Outside Links House + Stage: 43 + Room: 1 + Spawn: "00" + Spawn Type: "10" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Outside Links House -> Ordon Spring + Stage: 44 + Room: 1 + Spawn: "00" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Ordon Spring -> Outside Links House + Stage: 43 + Room: 1 + Spawn: "02" + Spawn Type: "50" + Parameters: "3091" + State: "FF" + +- Type: Overworld + Forward: + Connection: Ordon Village -> Ordon Ranch Village Pathway + Alias: Ordon Village -> Ordon Ranch + Stage: 41 + Room: 0 + Spawn: "01" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Ordon Ranch Village Pathway -> Ordon Village + Alias: Ordon Ranch -> Ordon Village + Stage: 43 + Room: 0 + Spawn: "00" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Ordon Bridge -> South Faron Woods + Stage: 45 + Room: 0 + Spawn: "00" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: South Faron Woods -> Ordon Bridge + Stage: 44 + Room: 1 + Spawn: "01" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: South Faron Woods Above Owl Statue -> Mist Area Near Owl Statue Chest + Alias: South Faron Woods -> Faron Woods Mist Area + Stage: 45 + Room: 5 + Spawn: "62" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Mist Area Near Owl Statue Chest -> South Faron Woods Above Owl Statue + Alias: Faron Woods Mist Area -> South Faron Woods + Stage: 45 + Room: 8 + Spawn: "02" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Mist Area Near North Faron Woods -> North Faron Woods + Alias: Faron Woods Mist Area -> North Faron Woods + Stage: 45 + Room: 6 + Spawn: "02" + Spawn Type: "00" + Parameters: "F09F" + State: "FF" + Return: + Connection: North Faron Woods -> Mist Area Near North Faron Woods + Alias: North Faron Woods -> Faron Woods Mist Area + Stage: 45 + Room: 11 + Spawn: "00" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: North Faron Lost Woods Ledge -> Lost Woods + Alias: North Faron Woods -> Lost Woods + Stage: 54 + Room: 3 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Lost Woods -> North Faron Lost Woods Ledge + Alias: Lost Woods -> North Faron Woods + Stage: 45 + Room: 6 + Spawn: "03" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Lost Woods Lower Battle Arena -> Sacred Grove Lower + Alias: Lost Woods Lower Battle Arena -> Sacred Grove + Stage: 54 + Room: 1 + Spawn: "03" + Spawn Type: "50" + Parameters: "" + State: "FF" + Return: + Connection: Sacred Grove Lower -> Lost Woods Lower Battle Arena + Alias: Sacred Grove -> Lost Woods Lower Battle Arena + Stage: 54 + Room: 1 + Spawn: "01" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Lost Woods Upper Battle Arena -> Sacred Grove Before Block + Alias: Lost Woods Upper Battle Arena -> Sacred Grove + Stage: 54 + Room: 1 + Spawn: "06" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Sacred Grove Before Block -> Lost Woods Upper Battle Arena + Alias: Sacred Grove -> Lost Woods Upper Battle Arena + Stage: 54 + Room: 3 + Spawn: "02" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Sacred Grove Upper -> Sacred Grove Past + Alias: Sacred Grove -> Sacred Grove Past + Stage: 54 + Room: 2 + Spawn: "00" + Spawn Type: "00" + Parameters: "F012" + State: "FF" + Return: + Connection: Sacred Grove Past -> Sacred Grove Upper + Alias: Sacred Grove Past -> Sacred Grove + Stage: 54 + Room: 1 + Spawn: "05" + Spawn Type: "10" + Parameters: "F032" + State: "FF" + +- Type: Overworld + Forward: + Connection: South Faron Woods -> Faron Field + Stage: 56 + Room: 6 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Faron Field -> South Faron Woods + Stage: 45 + Room: 4 + Spawn: "08" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Faron Field Behind Boulder -> Outside Castle Town South Inside Boulder + Alias: Faron Field -> Outside Castle Town South + Stage: 57 + Room: 16 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Outside Castle Town South -> Faron Field Behind Boulder + Alias: Outside Castle Town South -> Faron Field + Stage: 56 + Room: 6 + Spawn: "01" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Kakariko Gorge Behind Gate -> Lower Kakariko Village + Alias: Kakariko Gorge -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "3C" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Lower Kakariko Village -> Kakariko Gorge Behind Gate + Alias: Kakariko Village -> Kakariko Gorge + Stage: 56 + Room: 3 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Lower Kakariko Village -> Kakariko Graveyard + Alias: Kakariko Village -> Kakariko Graveyard + Stage: 48 + Room: 0 + Spawn: "00" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + Return: + Connection: Kakariko Graveyard -> Lower Kakariko Village + Alias: Kakariko Graveyard -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "06" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Lower Kakariko Village -> Death Mountain Near Kakariko + Alias: Kakariko Village -> Death Mountain + Stage: 47 + Room: 0 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Death Mountain Near Kakariko -> Lower Kakariko Village + Alias: Death Mountain -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "01" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Kakariko Graveyard Pond -> Lake Hylia + Alias: Kakariko Graveyard -> Lake Hylia + Stage: 52 + Room: 0 + Spawn: "19" + Spawn Type: "D0" + Parameters: "00FF" + State: "FF" + Return: + Connection: Lake Hylia -> Kakariko Graveyard Pond + Alias: Lake Hylia -> Kakariko Graveyard + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Overworld + Forward: + Connection: Kakariko Village Behind Gate -> Eldin Field + Alias: Kakariko Village -> Eldin Field + Stage: 56 + Room: 0 + Spawn: "01" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Eldin Field -> Kakariko Village Behind Gate + Alias: Eldin Field -> Kakariko Village + Stage: 46 + Room: 0 + Spawn: "08" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Eldin Field Near Castle Town -> Outside Castle Town East + Alias: Eldin Field -> Outside Castle Town East + Stage: 57 + Room: 17 + Spawn: "01" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Outside Castle Town East -> Eldin Field Near Castle Town + Alias: Outside Castle Town East -> Eldin Field + Stage: 56 + Room: 0 + Spawn: "07" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Eldin Field Outside Hidden Village -> Hidden Village + Alias: Eldin Field -> Hidden Village + Stage: 63 + Room: 0 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Hidden Village -> Eldin Field Outside Hidden Village + Alias: Hidden Village -> Eldin Field + Stage: 56 + Room: 7 + Spawn: "01" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Lanayru Field Near Zoras Domain -> Zoras Domain West Ledge + Alias: Lanayru Field -> Zoras Domain + Stage: 50 + Room: 1 + Spawn: "0F" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Zoras Domain West Ledge -> Lanayru Field Near Zoras Domain + Alias: Zoras Domain -> Lanayru Field + Stage: 56 + Room: 10 + Spawn: "03" + Spawn Type: "00" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Upper Zoras River -> Fishing Hole + Stage: 62 + Room: 0 + Spawn: "02" + Spawn Type: "B0" + Parameters: "F01F" + State: "FF" + Return: + Connection: Fishing Hole -> Upper Zoras River + Stage: 61 + Room: 0 + Spawn: "0A" + Spawn Type: "A0" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Upper Zoras River -> Lanayru Field + Stage: 56 + Room: 10 + Spawn: "02" + Spawn Type: D0"" + Parameters: "00FF" + State: "FF" + Return: + Connection: Lanayru Field -> Upper Zoras River + Stage: + Room: + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Overworld + Forward: + Connection: Upper Zoras River -> Zoras Domain + Stage: 50 + Room: 1 + Spawn: "0A" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Zoras Domain -> Upper Zoras River + Stage: 61 + Room: 0 + Spawn: "07" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Zoras Domain Top of Waterfall -> Zoras Throne Room + Alias: Zoras Domain -> Zoras Throne Room + Stage: 50 + Room: 0 + Spawn: "01" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Zoras Throne Room -> Zoras Domain Top of Waterfall + Alias: Zoras Throne Room -> Zoras Domain + Stage: 50 + Room: 1 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Zoras Domain -> Snowpeak Climb Lower + Alias: Zoras Domain -> Snowpeak Province + Stage: 51 + Room: 0 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Snowpeak Climb Lower -> Zoras Domain + Alias: Snowpeak Province -> Zoras Domain + Stage: 50 + Room: 1 + Spawn: "06" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Snowpeak Climb Upper -> Snowpeak Summit Cave + Alias: Snowpeak Climb -> Snowpeak Summit Cave + Stage: 51 + Room: 2 + Spawn: "08" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Snowpeak Summit Cave -> Snowpeak Climb Upper + Alias: Snowpeak Summit Cave -> Snowpeak Climb + Stage: 51 + Room: 0 + Spawn: "0F" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Lanayru Field -> Outside Castle Town West + Stage: 57 + Room: 8 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Outside Castle Town West -> Lanayru Field + Stage: 56 + Room: 10 + Spawn: "04" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Outside Castle Town West -> Lake Hylia Bridge + Stage: 56 + Room: 12 + Spawn: "01" + Spawn Type: "00" + Parameters: "F01F" + State: "FF" + Return: + Connection: Lake Hylia Bridge -> Outside Castle Town West + Stage: 57 + Room: 8 + Spawn: "02" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Outside Castle Town West -> Castle Town West + Stage: 53 + Room: 2 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town West -> Outside Castle Town West + Stage: 57 + Room: 8 + Spawn: "01" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Castle Town West -> Castle Town Center + Stage: 53 + Room: 0 + Spawn: "04" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town Center -> Castle Town West + Stage: 53 + Room: 2 + Spawn: "02" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Castle Town West -> Castle Town South + Stage: 53 + Room: 3 + Spawn: "08" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town South -> Castle Town West + Stage: 53 + Room: 2 + Spawn: "03" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Castle Town Center -> Castle Town North + Stage: 53 + Room: 1 + Spawn: "00" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Castle Town North -> Castle Town Center + Stage: 53 + Room: 0 + Spawn: "03" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Castle Town Center -> Castle Town East + Stage: 53 + Room: 4 + Spawn: "02" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Castle Town East -> Castle Town Center + Stage: 53 + Room: 0 + Spawn: "06" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Castle Town Center -> Castle Town South + Stage: 53 + Room: 3 + Spawn: "02" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Castle Town South -> Castle Town Center + Stage: 53 + Room: 0 + Spawn: "05" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Castle Town East -> Outside Castle Town East + Stage: 57 + Room: 17 + Spawn: "00" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Outside Castle Town East -> Castle Town East + Stage: 53 + Room: 4 + Spawn: "00" + Spawn Type: "10" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Castle Town East -> Castle Town South + Stage: 53 + Room: 3 + Spawn: "09" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + Return: + Connection: Castle Town South -> Castle Town East + Stage: 53 + Room: 4 + Spawn: "06" + Spawn Type: "50" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Castle Town South -> Outside Castle Town South + Stage: 57 + Room: 16 + Spawn: "01" + Spawn Type: "05" + Parameters: "F09F" + State: "FF" + Return: + Connection: Outside Castle Town South -> Castle Town South + Stage: 53 + Room: 3 + Spawn: "00" + Spawn Type: "10" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Outside Castle Town South -> Lake Hylia + Stage: 57 + Room: 16 + Spawn: "01" + Spawn Type: "05" + Parameters: "F09F" + State: "FF" + Return: + Connection: Lake Hylia -> Outside Castle Town South + Stage: -1 + Room: -1 + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Overworld + Forward: + Connection: Lake Hylia Bridge -> Flight by Fowl + Stage: 52 + Room: 0 + Spawn: "08" + Spawn Type: "B0" + Parameters: "F09F" + State: "FF" + Return: + Connection: Flight by Fowl -> Lake Hylia Bridge + Stage: 56 + Room: 13 + Spawn: "01" + Spawn Type: "A0" + Parameters: "F09F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Lake Hylia -> Gerudo Desert + Stage: 59 + Room: 0 + Spawn: "00" + Spawn Type: "00" + Parameters: "F018" + State: "FF" + Return: + Connection: Gerudo Desert -> Lake Hylia + Stage: + Room: + Spawn: "" + Spawn Type: "" + Parameters: "" + State: "FF" + +- Type: Overworld + Forward: + Connection: Gerudo Desert Outside Bulblin Camp -> Bulblin Camp + Alias: Gerudo Desert -> Bulblin Camp + Stage: 55 + Room: 1 + Spawn: "00" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Bulblin Camp -> Gerudo Desert Outside Bulblin Camp + Alias: Bulblin Camp -> Gerudo Desert + Stage: 59 + Room: 0 + Spawn: "02" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + +- Type: Overworld + Forward: + Connection: Bulblin Camp -> Outside Arbiters Grounds + Stage: 55 + Room: 3 + Spawn: "07" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" + Return: + Connection: Outside Arbiters Grounds -> Bulblin Camp + Stage: 55 + Room: 1 + Spawn: "02" + Spawn Type: "50" + Parameters: "F01F" + State: "FF" diff --git a/mods/randomizer/generator/data/flow_patches.yaml b/mods/randomizer/generator/data/flow_patches.yaml new file mode 100644 index 0000000000..51d99be4ac --- /dev/null +++ b/mods/randomizer/generator/data/flow_patches.yaml @@ -0,0 +1,486 @@ +# Patches for NPC text flows + +# NOTE: All data is expressed in little endian + +0: # zel_00.bmg + # Change the following indices of Midna's flows to jump to our custom choices + # instead. These are all the flow jumps that normally determine which "Talk to Midna" + # text is displayed. + - index: + - 0x1 + - 0x9 + - 0xC + - 0x8F + - 0x192 + - 0x1A4 + - 0x18F + - 0x197 + - 0x1BC + - 0x1C9 + type: event + event: 42 + parameters: 0x00000000 + next node index: Custom Midna Call Begin + +#1: # zel_01.bmg +2: # zel_02.bmg + # Patch Barnes branch to see if you can buy the bomb bag to use + # query 54 (custom event check) instead of query 22 (how many bomb bags the player has) + - index: 0x47C + type: branch + num results: 2 + query: 53 + parameters: 0x004D + next node index: 0x340 + +#3: # zel_03.bmg +4: # zel_04.bmg - Castle Town + # Patch Charlo to check for 100 rupees instead of 30 + - index: 0x34A + type: branch + num results: 2 + query: 6 + parameters: 0x0064 + next node index: 0x222 + # Patch Charlo to take 100 rupees from Link instead of 30 + - index: 0x34E + type: event + event: 41 + parameters: 0x00000064 + next node index: 0x226 + # Patch Charlo to add 100 rupees to his counter instead of 30 + - index: 0x357 + type: event + event: 3 + parameters: 0x00000064 + next node index: 0x22B + # Patch Malo Mart door person to not check for time of day. + # Change from branch node that checks time of day to event node + # that does nothing. + - index: 0x1A5 + type: event + event: 42 + parameters: 0x00000000 + next node index: 0x10C + +5: # zel_05.bmg + # Patch Yeta flow to always give the map check even if the player has obtained the Ordon Cheese + - index: 0x8 + type: event + event: 42 + parameters: 0x00000000 + next node index: 0x8 + + # Patch Yeta flow to always give the map check even if the player has obtained the Ordon Cheese + - index: 0x5 + type: event + event: 42 + parameters: 0x00000000 + next node index: 0x3 + + # Patch Gor Liggs to not set the flag for the third key shard + # Change event index 17 to event index 42 which does nothing + - index: 0x258 + type: event + event: 42 + parameters: 0x00000000 + next node index: 0x1AC + +#6: # zel_06.bmg +#7: # zel_07.bmg +#8: # zel_08.bmg + +# Custom Flow Nodes +9: + # Start of custom midna call tree. Begin by checking if we can change time + # of day to see if we show that choice + - name: Custom Midna Call Begin + type: branch + num results: 2 + query: 54 + parameters: 0x0000 + next node index: 0xFFFF + results: + - Custom Midna Call 3 Choice Select Setup # Can change time of day + - Custom Midna Call 2 Choice Select Setup # Can not change time of day + + ################################################# + # CUSTOM MIDNA CALL 2 CHOICE FLOW # + ################################################# + + - name: Custom Midna Call 2 Choice Select Setup + type: event + event: 13 + parameters: 0x00000003 + next node index: Custom Midna Call 2 Choice Intro + + - name: Custom Midna Call 2 Choice Intro + type: message + inf index: Custom Midna Call Need Something Text + next flow index: Custom Midna Call 2 Choice + + - name: Custom Midna Call 2 Choice + type: message + inf index: Custom Midna Call 2 Choice Text + next flow index: Custom Midna Call 2 Choice Branch + + - name: Custom Midna Call 2 Choice Branch + type: branch + num results: 3 + query: 35 + parameters: 0x0000 + next node index: 0xFFFF + results: + - Custom Midna Call Hints # Hints + - Custom Midna Call Return to Spawn Branch # Return to Spawn + - 0xFFFF # (B button pressed) + + ################################################# + # CUSTOM MIDNA CALL 3 CHOICE FLOW # + ################################################# + + - name: Custom Midna Call 3 Choice Select Setup + type: event + event: 13 + parameters: 0x00000004 + next node index: Custom Midna Call 3 Choice Intro + + - name: Custom Midna Call 3 Choice Intro + type: message + inf index: Custom Midna Call Need Something Text + next flow index: Custom Midna Call 3 Choice + + - name: Custom Midna Call 3 Choice + type: message + inf index: Custom Midna Call 3 Choice Text + next flow index: Custom Midna Call 3 Choice Branch + + - name: Custom Midna Call 3 Choice Branch + type: branch + num results: 4 + query: 36 + parameters: 0x0000 + next node index: 0xFFFF + results: + - Custom Midna Call Hints # Hints + - Custom Midna Call Change Time # Change time of day + - Custom Midna Call Return to Spawn Branch # Return to Spawn + - 0xFFFF # (B button pressed) + + ################################################# + # CUSTOM MIDNA CALL CHOICE OPTIONS # + ################################################# + + - name: Custom Midna Call Hints + type: message + inf index: Custom Midna Call Hints Text + next flow index: 0xFFFF + + - name: Custom Midna Call Change Time + type: event + event: 44 + parameters: 0x00000000 + next node index: 0x116 + + - name: Custom Midna Call Return to Spawn Branch + type: branch + num results: 3 + query: 55 + parameters: 0x0000 + next node index: 0xFFFF + results: + - Return to Spawn Dungeon Choice Select Setup + - Return to Spawn Dungeon No Choice Select Setup + - Return to Base Spawn + + ################################################# + # RETURN TO SPAWN DUNGEON CHOICE FLOW # + ################################################# + + - name: Return to Spawn Dungeon Choice Select Setup + type: event + event: 13 + parameters: 0x00000004 + next node index: Return to Spawn Dungeon Choice Intro + + - name: Return to Spawn Dungeon Choice Intro + type: message + inf index: Return to Spawn Dungeon Intro Text + next flow index: Return to Spawn Dungeon Choice + + - name: Return to Spawn Dungeon Choice + type: message + inf index: Return to Spawn Dungeon Choice Text + next flow index: Return to Spawn Dungeon Choice Branch + + - name: Return to Spawn Dungeon Choice Branch + type: branch + num results: 4 + query: 36 + parameters: 0x0000 + next node index: 0xFFFF + results: + - Return to Dungeon Spawn # Dungeon entrance + - 0xFFFF # Nevermind + - Return to Base Spawn # Spawn + - 0xFFFF # (B button pressed) + + ################################################# + # RETURN TO SPAWN DUNGEON NO CHOICE FLOW # + ################################################# + + - name: Return to Spawn Dungeon No Choice Select Setup + type: event + event: 13 + parameters: 0x00000003 + next node index: Return to Spawn Dungeon No Choice Intro + + - name: Return to Spawn Dungeon No Choice Intro + type: message + inf index: Return to Spawn Dungeon Intro Text + next flow index: Return to Spawn No Dungeon Choice + + - name: Return to Spawn Dungeon No Choice + type: message + inf index: Return to Spawn Dungeon No Choice Text + next flow index: Return to Spawn Dungeon No Choice Branch + + - name: Return to Spawn Dungeon No Choice Branch + type: branch + num results: 3 + query: 35 + parameters: 0x0000 + next node index: 0xFFFF + results: + - 0xFFFF # Nevermind + - Return to Base Spawn # Spawn + - 0xFFFF # (B button pressed) + + ################################################# + # RETURN TO SPAWN EVENTS # + ################################################# + + # Use custom event index 45 to setup returning to spawn. A non-zero + # parameter value indicates to try and use a place override if one + # exists for the current stage + - name: Return to Dungeon Spawn + type: event + event: 45 + parameters: 0x00000001 + next node index: 0x116 + + # Use custom event index 45 to setup returning to spawn. A parameter + # value of zero indicates to always return to the starting spawn + - name: Return to Base Spawn + type: event + event: 45 + parameters: 0x00000000 + next node index: 0x116 + + ################################################# + # HINT SIGNS # + ################################################# + + - name: Ordon Hint Sign + index: 21100 + type: message + inf index: Ordon Hint Sign Text + next flow index: 0xFFFF + + - name: South Faron Woods Hint Sign + index: 21101 + type: message + inf index: South Faron Woods Hint Sign Text + next flow index: 0xFFFF + + - name: Sacred Grove Hint Sign + index: 21102 + type: message + inf index: Sacred Grove Hint Sign Text + next flow index: 0xFFFF + + - name: Faron Field Hint Sign + index: 21103 + type: message + inf index: Faron Field Hint Sign Text + next flow index: 0xFFFF + + - name: Kakariko Gorge Hint Sign + index: 21104 + type: message + inf index: Kakariko Gorge Hint Sign Text + next flow index: 0xFFFF + + - name: Kakariko Village Hint Sign + index: 21105 + type: message + inf index: Kakariko Village Hint Sign Text + next flow index: 0xFFFF + + - name: Kakariko Graveyard Hint Sign + index: 21106 + type: message + inf index: Kakariko Graveyard Hint Sign Text + next flow index: 0xFFFF + + - name: Eldin Field Hint Sign + index: 21107 + type: message + inf index: Eldin Field Hint Sign Text + next flow index: 0xFFFF + + - name: North Eldin Field Hint Sign + index: 21108 + type: message + inf index: North Eldin Field Hint Sign Text + next flow index: 0xFFFF + + - name: Hidden Village Hint Sign + index: 21109 + type: message + inf index: Hidden Village Hint Sign Text + next flow index: 0xFFFF + + - name: Lanayru Field Hint Sign + index: 21110 + type: message + inf index: Lanayru Field Hint Sign Text + next flow index: 0xFFFF + + - name: Beside Castle Town Hint Sign + index: 21111 + type: message + inf index: Beside Castle Town Hint Sign Text + next flow index: 0xFFFF + + - name: Castle Town Center Hint Sign + index: 21112 + type: message + inf index: Castle Town Center Hint Sign Text + next flow index: 0xFFFF + + - name: Outside South Castle Town Hint Sign + index: 21113 + type: message + inf index: Outside South Castle Town Hint Sign Text + next flow index: 0xFFFF + + - name: Lake Hylia Bridge Hint Sign + index: 21114 + type: message + inf index: Lake Hylia Bridge Hint Sign Text + next flow index: 0xFFFF + + - name: Lake Hylia Hint Sign + index: 21115 + type: message + inf index: Lake Hylia Hint Sign Text + next flow index: 0xFFFF + + - name: Lanayru Spring Hint Sign + index: 21116 + type: message + inf index: Lanayru Spring Hint Sign Text + next flow index: 0xFFFF + + - name: Lake Lantern Cave Hint Sign + index: 21117 + type: message + inf index: Lake Lantern Cave Hint Sign Text + next flow index: 0xFFFF + + - name: Fishing Hole Hint Sign + index: 21118 + type: message + inf index: Fishing Hole Hint Sign Text + next flow index: 0xFFFF + + - name: Zoras Domain Hint Sign + index: 21119 + type: message + inf index: Zoras Domain Hint Sign Text + next flow index: 0xFFFF + + - name: Snowpeak Hint Sign + index: 21120 + type: message + inf index: Snowpeak Hint Sign Text + next flow index: 0xFFFF + + - name: Gerudo Desert Hint Sign + index: 21121 + type: message + inf index: Gerudo Desert Hint Sign Text + next flow index: 0xFFFF + + - name: Bulblin Camp Hint Sign + index: 21122 + type: message + inf index: Bulblin Camp Hint Sign Text + next flow index: 0xFFFF + + - name: Forest Temple Hint Sign + index: 21123 + type: message + inf index: Forest Temple Hint Sign Text + next flow index: 0xFFFF + + - name: Goron Mines Hint Sign + index: 21124 + type: message + inf index: Goron Mines Hint Sign Text + next flow index: 0xFFFF + + - name: Lakebed Temple Hint Sign + index: 21125 + type: message + inf index: Lakebed Temple Hint Sign Text + next flow index: 0xFFFF + + - name: Arbiters Grounds Hint Sign + index: 21126 + type: message + inf index: Arbiters Grounds Hint Sign Text + next flow index: 0xFFFF + + - name: Snowpeak Ruins Hint Sign + index: 21127 + type: message + inf index: Snowpeak Ruins Hint Sign Text + next flow index: 0xFFFF + + - name: Temple of Time First Hint Sign + index: 21128 + type: message + inf index: Temple of Time First Hint Sign Text + next flow index: 0xFFFF + + - name: Temple of Time Second Hint Sign + index: 21129 + type: message + inf index: Temple of Time Second Hint Sign Text + next flow index: 0xFFFF + + - name: City in the Sky Hint Sign + index: 21130 + type: message + inf index: City in the Sky Hint Sign Text + next flow index: 0xFFFF + + - name: Palace of Twilight Hint Sign + index: 21131 + type: message + inf index: Palace of Twilight Hint Sign Text + next flow index: 0xFFFF + + - name: Hyrule Castle Hint Sign + index: 21132 + type: message + inf index: Hyrule Castle Hint Sign Text + next flow index: 0xFFFF + + - name: Cave of Ordeals Hint Sign + index: 21133 + type: message + inf index: Cave of Ordeals Hint Sign Text + next flow index: 0xFFFF diff --git a/mods/randomizer/generator/data/items.yaml b/mods/randomizer/generator/data/items.yaml new file mode 100644 index 0000000000..28fc7c08b8 --- /dev/null +++ b/mods/randomizer/generator/data/items.yaml @@ -0,0 +1,1292 @@ +# Item Importance: +# 1. Major - Item can potentially unlock locations. Will be placed in a non-excluded location +# 2. Minor - Item does not unlock locations, but has gameplay utility. Will be placed +# in a non-excluded location if any are empty. +# 3. Junk - Item is expendable. Will be placed completely randomly. + +#- Name: Recovery Heart +# Importance: Junk +# Id: 0x00 + +- Name: Green Rupee + Importance: Junk + Id: 0x01 + +- Name: Blue Rupee + Importance: Junk + Id: 0x02 + +- Name: Yellow Rupee + Importance: Junk + Id: 0x03 + +- Name: Red Rupee + Importance: Junk + Id: 0x04 + +- Name: Purple Rupee + Importance: Junk + Id: 0x05 + +- Name: Orange Rupee + Importance: Junk + Id: 0x06 + +- Name: Silver Rupee + Importance: Junk + Id: 0x07 + +#- Name: Unused (Gives text about combining bombs) +# Importance: +# Id: 0x08 + +#- Name: Unused (Gives text about bombs) +# Importance: +# Id: 0x09 + +- Name: Bombs 5 + Importance: Junk + Id: 0x0A + +- Name: Bombs 10 + Importance: Junk + Id: 0x0B + +- Name: Bombs 20 + Importance: Junk + Id: 0x0C + +- Name: Bombs 30 + Importance: Junk + Id: 0x0D + +- Name: Arrows 10 + Importance: Junk + Id: 0x0E + +- Name: Arrows 20 + Importance: Junk + Id: 0x0F + +- Name: Arrows 30 + Importance: Junk + Id: 0x10 + +#- Name: Arrows 1 +# Importance: Junk +# Id: 0x11 + +- Name: Seeds 50 + Importance: Junk + Id: 0x12 + +- Name: Foolish Item # Custom Rando Item + Importance: Junk + Id: 0x13 + +- Name: Ordon Spring Portal # Custom Rando item + Importance: Major + Id: 0x14 + +- Name: South Faron Portal # Custom Rando Item + Importance: Major + Id: 0x15 + +- Name: Water Bombs 5 + Importance: Junk + Id: 0x16 + +- Name: Water Bombs 10 + Importance: Junk + Id: 0x17 + +- Name: Water Bombs 15 + Importance: Junk + Id: 0x18 + +#- Name: Water Bombs 3 +# Importance: Junk +# Id: 0x19 + +- Name: Bomblings 5 + Importance: Junk + Id: 0x1A + +- Name: Bomblings 10 + Importance: Junk + Id: 0x1B + +#- Name: Bomblings 3 +# Importance: Junk +# Id: 0x1C +# +#- Name: Bomblings 1 +# Importance: Junk +# Id: 0x1D + +#- Name: Fairy +# Importance: Junk +# Id: 0x1E + +#- Name: Recovery Heart x3 +# Importance: Junk +# Id: 0x1F + +#- Name: Small Key +# Importance: Major +# Id: 0x20 + +- Name: Piece of Heart + Importance: Junk + Id: 0x21 + +- Name: Heart Container + Importance: Junk + Id: 0x22 + +#- Name: Dungeon Map +# Importance: Junk +# Id: 0x23 + +#- Name: Compass +# Importance: Junk +# Id: 0x24 + +#- Name: Ooccoo_FT +# Importance: Junk +# Id: 0x25 + +#- Name: Big Key +# Importance: Major +# Id: 0x26 + +#- Name: Ooccoo Jr +# Importance: Junk +# Id: 0x27 + +#- Name: Ordon Sword +# Importance: Major +# Id: 0x28 + +#- Name: Master Sword +# Importance: Major +# Id: 0x29 + +- Name: Ordon Shield + Importance: Major + Id: 0x2A + +- Name: Wooden Shield + Importance: Junk + Id: 0x2B + +- Name: Hylian Shield + Importance: Major + Id: 0x2C + +#- Name: Ooccoo's Note +# Importance: Junk +# Id: 0x2D + +#- Name: Ordon Clothing +# Importance: Junk +# Id: 0x2E + +#- Name: Heros Clothes +# Importance: Junk +# Id: 0x2F + +- Name: Magic Armor + Importance: Major + Id: 0x30 + +- Name: Zora Armor + Importance: Major + Id: 0x31 + +- Name: Shadow Crystal + Importance: Major + Id: 0x32 + +#- Name: Ooccoo Dungeon +# Importance: Junk +# Id: 0x33 + +#- Name: Small Wallet +# Importance: Junk +# Id: 0x34 + +- Name: Progressive Wallet # Also Large Wallet + Importance: Major + Id: 0x35 + +#- Name: Giant Wallet +# Importance: Major +# Id: 0x36 + +#- Name: Unused (Piece of Heart 2 Text) +# Importance: Junk +# Id: 0x37 + +#- Name: Unused (Piece of Heart 3 Text) +# Importance: Junk +# Id: 0x38 + +- Name: Upper Zoras River Portal + Importance: Major + Id: 0x39 + +- Name: Castle Town Portal + Importance: Major + Id: 0x3A + +- Name: Gerudo Desert Portal + Importance: Major + Id: 0x3B + +- Name: North Faron Portal + Importance: Major + Id: 0x3C + +#- Name: Coral Earring +# Importance: Major +# Id: 0x3D + +- Name: Hawkeye + Importance: Minor + Id: 0x3E + +- Name: Progressive Sword # Also Wooden Sword + Importance: Major + Id: 0x3F + +- Name: Gale Boomerang + Importance: Major + Id: 0x40 + +- Name: Spinner + Importance: Major + Id: 0x41 + +- Name: Ball and Chain + Importance: Major + Id: 0x42 + +- Name: Progressive Bow + Importance: Major + Id: 0x43 + +- Name: Progressive Clawshot + Importance: Major + Id: 0x44 + +- Name: Iron Boots + Importance: Major + Id: 0x45 + +- Name: Progressive Dominion Rod + Importance: Major + Id: 0x46 + +#- Name: Double Clawshots +# Importance: Major +# Id: 0x47 + +- Name: Lantern + Importance: Major + Id: 0x48 + +#- Name: Master Sword with Light +# Importance: Major +# Id: 0x49 + +- Name: Progressive Fishing Rod + Importance: Major + Id: 0x4A + +- Name: Slingshot + Importance: Major + Id: 0x4B + +#- Name: Dominion Rod Uncharged +# Importance: Major +# Id: 0x4C + +- Name: Kakariko Gorge Portal + Importance: Major + Id: 0x4D + +- Name: Kakariko Village Portal + Importance: Major + Id: 0x4E + +- Name: Giant Bomb Bag + Importance: Minor + Id: 0x4F + +- Name: Bomb Bag + Importance: Major + Id: 0x50 + +#- Name: Also Bomb Bag (different text) +# Importance: Major +# Id: 0x51 + +- Name: Death Mountain Portal + Importance: Major + Id: 0x52 + +#- Name: Light Arrow +# Importance: Major +# Id: 0x53 + +#- Name: Small Quiver +# Importance: Major +# Id: 0x54 + +#- Name: Big Quiver +# Importance: Major +# Id: 0x55 + +#- Name: Giant Quiver +# Importance: Major +# Id: 0x56 + +- Name: Zoras Domain Portal + Importance: Major + Id: 0x57 + +#- Name: Fising Rod Lure +# Importance: Junk +# Id: 0x58 + +#- Name: Bow Bombs +# Importance: Major +# Id: 0x59 + +#- Name: Bow Hawkeye +# Importance: Junk +# Id: 0x5A + +#- Name: Fishing Rod Bee Larva +# Importance: Junk +# Id: 0x5B + +#- Name: Fishing Rod Coral Earring +# Importance: Major +# Id: 0x5C + +#- Name: Fishing Rod Worm +# Importance: Junk +# Id: 0x5D + +#- Name: Fishing Rod Earring Bee Larva +# Importance: Major +# Id: 0x5E + +#- Name: Fishing Rod Earring Worm +# Importance: Major +# Id: 0x5F + +- Name: Empty Bottle + Importance: Major + Id: 0x60 + +- Name: Red Potion Shop + Importance: Junk + Id: 0x61 + +#- Name: Green Potion Shop +# Importance: Junk +# Id: 0x5F + +- Name: Blue Potion Shop + Importance: Junk + Id: 0x63 + +#- Name: Milk +# Importance: Junk +# Id: 0x64 + +- Name: Bottle with Half Milk + Importance: Major + Id: 0x65 + +#- Name: Lantern Oil Shop +# Importance: Junk +# Id: 0x66 + +#- Name: Water +# Importance: Junk +# Id: 0x67 + +#- Name: Lantern Oil Scooped +# Importance: Junk +# Id: 0x68 + +#- Name: Red Potion Scooped +# Importance: Junk +# Id: 0x69 + +#- Name: Nasty Soup +# Importance: Junk +# Id: 0x6A + +#- Name: Hot Springwater Scooped +# Importance: Junk +# Id: 0x6B + +#- Name: Fairy Bottle +# Importance: Junk +# Id: 0x6C + +#- Name: Hot Springwater Shop +# Importance: Junk +# Id: 0x6D + +#- Name: Lantern Refill Scooped +# Importance: Junk +# Id: 0x6E + +#- Name: Lantern Refill Shop +# Importance: Junk +# Id: 0x6F + +#- Name: Bomb Bag Regular Bombs +# Importance: Junk +# Id: 0x70 + +#- Name: Bomb Bag Water Bombs +# Importance: Junk +# Id: 0x71 + +#- Name: Bomb Bag Bomblings +# Importance: Junk +# Id: 0x72 + +- Name: Fairy Tears + Importance: Junk + Id: 0x73 + +#- Name: Worm +# Importance: Junk +# Id: 0x74 + +- Name: Bottle with Great Fairies Tears + Importance: Major + Id: 0x75 + +#- Name: Bee Larva Scooped +# Importance: Junk +# Id: 0x76 + +#- Name: Rare Chu Jelly +# Importance: Junk +# Id: 0x77 + +#- Name: Red Chu Jelly +# Importance: Junk +# Id: 0x78 + +#- Name: Blue Chu Jelly +# Importance: Junk +# Id: 0x79 + +#- Name: Green Chu Jelly +# Importance: Junk +# Id: 0x7A + +#- Name: Yellow Chu Jelly +# Importance: Junk +# Id: 0x7B + +#- Name: Purple Chu Jelly +# Importance: Junk +# Id: 0x7C + +#- Name: Simple Soup +# Importance: Junk +# Id: 0x7D + +#- Name: Good Soup +# Importance: Junk +# Id: 0x7E + +#- Name: Superb Soup +# Importance: Junk +# Id: 0x7F + +- Name: Renados Letter + Importance: Major + Id: 0x80 + +- Name: Invoice + Importance: Major + Id: 0x81 + +- Name: Wooden Statue + Importance: Major + Id: 0x82 + +- Name: Ilias Charm + Importance: Major + Id: 0x83 + +- Name: Horse Call + Importance: Minor + Id: 0x84 + +- Name: Forest Temple Small Key + Importance: Major + Id: 0x85 + Dungeon Small Key: Forest Temple + +- Name: Goron Mines Small Key + Importance: Major + Id: 0x86 + Dungeon Small Key: Goron Mines + +- Name: Lakebed Temple Small Key + Importance: Major + Id: 0x87 + Dungeon Small Key: Lakebed Temple + +- Name: Arbiters Grounds Small Key + Importance: Major + Id: 0x88 + Dungeon Small Key: Arbiters Grounds + +- Name: Snowpeak Ruins Small Key + Importance: Major + Id: 0x89 + Dungeon Small Key: Snowpeak Ruins + +- Name: Temple of Time Small Key + Importance: Major + Id: 0x8A + Dungeon Small Key: Temple of Time + +- Name: City in the Sky Small Key + Importance: Major + Id: 0x8B + Dungeon Small Key: City in the Sky + +- Name: Palace of Twilight Small Key + Importance: Major + Id: 0x8C + Dungeon Small Key: Palace of Twilight + +- Name: Hyrule Castle Small Key + Importance: Major + Id: 0x8D + Dungeon Small Key: Hyrule Castle + +- Name: Gerudo Desert Bulblin Camp Key + Importance: Major + Id: 0x8E + +- Name: Lake Hylia Portal + Importance: Major + Id: 0x8F + +- Name: Aurus Memo + Importance: Major + Id: 0x90 + +- Name: Asheis Sketch + Importance: Major + Id: 0x91 + +- Name: Forest Temple Big Key + Importance: Major + Id: 0x92 + Dungeon Big Key: Forest Temple + +- Name: Lakebed Temple Big Key + Importance: Major + Id: 0x93 + Dungeon Big Key: Lakebed Temple + +- Name: Arbiters Grounds Big Key + Importance: Major + Id: 0x94 + Dungeon Big Key: Arbiters Grounds + +- Name: Temple of Time Big Key + Importance: Major + Id: 0x95 + Dungeon Big Key: Temple of Time + +- Name: City in the Sky Big Key + Importance: Major + Id: 0x96 + Dungeon Big Key: City in the Sky + +- Name: Palace of Twilight Big Key + Importance: Major + Id: 0x97 + Dungeon Big Key: Palace of Twilight + +- Name: Hyrule Castle Big Key + Importance: Major + Id: 0x98 + Dungeon Big Key: Hyrule Castle + +- Name: Forest Temple Compass + Importance: Junk + Id: 0x99 + Dungeon Compass: Forest Temple + +- Name: Goron Mines Compass + Importance: Junk + Id: 0x9A + Dungeon Compass: Goron Mines + +- Name: Lakebed Temple Compass + Importance: Junk + Id: 0x9B + Dungeon Compass: Lakebed Temple + +#- Name: Lantern Yellow Chu Chu +# Importance: Junk +# Id: 0x9C + +- Name: Bottle with Lantern Oil + Importance: Major + Id: 0x9D + +#- Name: Bee Larva Shop +# Importance: Junk +# Id: 0x9E + +#- Name: Black Chu Jelly +# Importance: Junk +# Id: 0x9F + +#- Name: Tear of Light +# Importance: Junk +# Id: 0xA0 + +#- Name: Vessel of Light Faron +# Importance: Junk +# Id: 0xA1 + +#- Name: Vessel of Light Eldin +# Importance: Junk +# Id: 0xA2 + +#- Name: Vessel of Light Lanayru +# Importance: Junk +# Id: 0xA3 + +#- Name: Vessel of Light Full +# Importance: Junk +# Id: 0xA4 + +- Name: Progressive Mirror Shard + Importance: Major + Id: 0xA5 + +#- Name: Mirror Piece 3 +# Importance: Junk +# Id: 0xA6 + +#- Name: Mirror Piece 4 +# Importance: Junk +# Id: 0xA7 + +- Name: Arbiters Grounds Compass + Importance: Junk + Id: 0xA8 + Dungeon Compass: Arbiters Grounds + +- Name: Snowpeak Ruins Compass + Importance: Junk + Id: 0xA9 + Dungeon Compass: Snowpeak Ruins + +- Name: Temple of Time Compass + Importance: Junk + Id: 0xAA + Dungeon Compass: Temple of Time + +- Name: City in the Sky Compass + Importance: Junk + Id: 0xAB + Dungeon Compass: City in the Sky + +- Name: Palace of Twilight Compass + Importance: Junk + Id: 0xAC + Dungeon Compass: Palace of Twilight + +- Name: Hyrule Castle Compass + Importance: Junk + Id: 0xAD + Dungeon Compass: Hyrule Castle + +- Name: Mirror Chamber Portal + Importance: Major + Id: 0xAE + +- Name: Snowpeak Portal + Importance: Major + Id: 0xAF + +#- Name: Ilias Scent +# Importance: Junk +# Id: 0xB0 + +#- Name: Pumpkin Scent (Unused) +# Importance: Junk +# Id: 0xB1 + +#- Name: Poe Scent +# Importance: Junk +# Id: 0xB2 + +#- Name: Reekfish Scent +# Importance: Junk +# Id: 0xB3 + +#- Name: Youths Scent +# Importance: Junk +# Id: 0xB4 + +#- Name: Medicine Scent +# Importance: Junk +# Id: 0xB5 + +- Name: Forest Temple Dungeon Map + Importance: Junk + Id: 0xB6 + Dungeon Map: Forest Temple + +- Name: Goron Mines Dungeon Map + Importance: Junk + Id: 0xB7 + Dungeon Map: Goron Mines + +- Name: Lakebed Temple Dungeon Map + Importance: Junk + Id: 0xB8 + Dungeon Map: Lakebed Temple + +- Name: Arbiters Grounds Dungeon Map + Importance: Junk + Id: 0xB9 + Dungeon Map: Arbiters Grounds + +- Name: Snowpeak Ruins Dungeon Map + Importance: Junk + Id: 0xBA + Dungeon Map: Snowpeak Ruins + +- Name: Temple of Time Dungeon Map + Importance: Junk + Id: 0xBB + Dungeon Map: Temple of Time + +- Name: City in the Sky Dungeon Map + Importance: Junk + Id: 0xBC + Dungeon Map: City in the Sky + +- Name: Palace of Twilight Dungeon Map + Importance: Junk + Id: 0xBD + Dungeon Map: Palace of Twilight + +- Name: Hyrule Castle Dungeon Map + Importance: Junk + Id: 0xBE + Dungeon Compass: Hyrule Castle + +- Name: Sacred Grove Portal + Importance: Major + Id: 0xBF + +- Name: Male Beetle + Importance: Major + Id: 0xC0 + +- Name: Female Beetle + Importance: Major + Id: 0xC1 + +- Name: Male Butterfly + Importance: Major + Id: 0xC2 + +- Name: Female Butterfly + Importance: Major + Id: 0xC3 + +- Name: Male Stag Beetle + Importance: Major + Id: 0xC4 + +- Name: Female Stag Beetle + Importance: Major + Id: 0xC5 + +- Name: Male Grasshopper + Importance: Major + Id: 0xC6 + +- Name: Female Grasshopper + Importance: Major + Id: 0xC7 + +- Name: Male Phasmid + Importance: Major + Id: 0xC8 + +- Name: Female Phasmid + Importance: Major + Id: 0xC9 + +- Name: Male Pill Bug + Importance: Major + Id: 0xCA + +- Name: Female Pill Bug + Importance: Major + Id: 0xCB + +- Name: Male Mantis + Importance: Major + Id: 0xCC + +- Name: Female Mantis + Importance: Major + Id: 0xCD + +- Name: Male Ladybug + Importance: Major + Id: 0xCE + +- Name: Female Ladybug + Importance: Major + Id: 0xCF + +- Name: Male Snail + Importance: Major + Id: 0xD0 + +- Name: Female Snail + Importance: Major + Id: 0xD1 + +- Name: Male Dragonfly + Importance: Major + Id: 0xD2 + +- Name: Female Dragonfly + Importance: Major + Id: 0xD3 + +- Name: Male Ant + Importance: Major + Id: 0xD4 + +- Name: Female Ant + Importance: Major + Id: 0xD5 + +- Name: Male Dayfly + Importance: Major + Id: 0xD6 + +- Name: Female Dayfly + Importance: Major + Id: 0xD7 + +- Name: Progressive Fused Shadow + Importance: Major + Id: 0xD8 + +#- Name: Fused Shadow 2 +# Importance: Junk +# Id: 0xD9 + +#- Name: Fused Shadow 3 +# Importance: Junk +# Id: 0xDA + +#- Name: Mirror Shard 1 +# Importance: Junk +# Id: 0xDB + +#- Name: Unused +# Importance: Junk +# Id: 0xDC + +#- Name: Unused +# Importance: Junk +# Id: 0xDD + +#- Name: Unused +# Importance: Junk +# Id: 0xDE + +#- Name: Unused +# Importance: Junk +# Id: 0xDF + +- Name: Poe Soul + Importance: Major + Id: 0xE0 + +- Name: Progressive Hidden Skill # Also Ending Blow + Importance: Major + Id: 0xE1 + +#- Name: Shield Attack +# Importance: Major +# Id: 0xE2 +# +#- Name: Back Slice +# Importance: Major +# Id: 0xE3 +# +#- Name: Helm Splitter +# Importance: Major +# Id: 0xE4 +# +#- Name: Mortal Draw +# Importance: Major +# Id: 0xE5 +# +#- Name: Jump Strike +# Importance: Major +# Id: 0xE6 +# +#- Name: Great Spin +# Importance: Major +# Id: 0xE7 + +- Name: Bridge of Eldin Portal + Importance: Major + Id: 0xE8 + +- Name: Progressive Sky Book + Importance: Major + Id: 0xE9 + +#- Name: Partially Filled Sky Book +# Importance: Major +# Id: 0xEA + +#- Name: Completed Sky Book +# Importance: Major +# Id: 0xEB + +#- Name: Ooccoo (City in the Sky) +# Importance: Junk +# Id: 0xEC + +- Name: Purple Rupee Links House + Importance: Junk + Id: 0xED + +- Name: North Faron Woods Gate Key + Importance: Major + Id: 0xEE + +#- Name: Blue Fire +# Importance: Major +# Id: 0xEF + +#- Name: Blue Fire +# Importance: Major +# Id: 0xF0 + +#- Name: Blue Fire +# Importance: Major +# Id: 0xF1 + +#- Name: Blue Fire +# Importance: Major +# Id: 0xF2 + +- Name: Gate Keys + Importance: Major + Id: 0xF3 + +- Name: Ordon Pumpkin + Importance: Major + Id: 0xF4 + +- Name: Ordon Cheese + Importance: Major + Id: 0xF5 + +- Name: Snowpeak Ruins Bedroom Key + Importance: Major + Id: 0xF6 + Dungeon Big Key: Snowpeak Ruins + +#- Name: Surfboard (Unused) +# Importance: Major +# Id: 0xF7 + +#- Name: Got Lantern Back +# Importance: Major +# Id: 0xF8 + +- Name: Goron Mines Key Shard # Also first shard + Importance: Major + Id: 0xF9 + Dungeon Big Key: Goron Mines + +#- Name: Got Lantern Back +# Importance: Major +# Id: 0xFA + +#- Name: Got Lantern Back +# Importance: Major +# Id: 0xFB + +#- Name: Key? +# Importance: Major +# Id: 0xFC + +#- Name: Goron Mines Big Key +# Importance: Major +# Id: 0xFD + +- Name: Coro Key + Importance: Major + Id: 0xFE + +#- Name: Invalid +# Importance: Major +# Id: 0xFF + +# Dummy Items that are used to represent other logical states (for now) + +- Name: Game Beatable + Importance: Major + Id: 0x101 + Game Winning Item: True + +- Name: Hint + Importance: Junk + Id: 0x102 + +- Name: Faron Twilight Tear + Importance: Major + Id: 0x103 + +- Name: Eldin Twilight Tear + Importance: Major + Id: 0x104 + +- Name: Lanayru Twilight Tear + Importance: Major + Id: 0x105 + +# - Name: Ghost Lantern +# Importance: Minor +# Id: 0xAF + +# - Name: Stamp (A) +# Importance: Junk +# Id: 0x78 + +# - Name: Stamp (B) +# Importance: Junk +# Id: 0x79 + +# - Name: Stamp (C) +# Importance: Junk +# Id: 0x7A + +# - Name: Stamp (D) +# Importance: Junk +# Id: 0x7B + +# - Name: Stamp (E) +# Importance: Junk +# Id: 0x7C + +# - Name: Stamp (F) +# Importance: Junk +# Id: 0x7D + +# - Name: Stamp (G) +# Importance: Junk +# Id: 0x7E + +# - Name: Stamp (H) +# Importance: Junk +# Id: 0x7F + +# - Name: Stamp (I) +# Importance: Junk +# Id: 0x80 + +# - Name: Stamp (J) +# Importance: Junk +# Id: 0x81 + +# - Name: Stamp (K) +# Importance: Junk +# Id: 0x82 + +# - Name: Stamp (L) +# Importance: Junk +# Id: 0x83 + +# - Name: Stamp (M) +# Importance: Junk +# Id: 0x84 + +# - Name: Stamp (N) +# Importance: Junk +# Id: 0x85 + +# - Name: Stamp (O) +# Importance: Junk +# Id: 0x86 + +# - Name: Stamp (P) +# Importance: Junk +# Id: 0x87 + +# - Name: Stamp (Q) +# Importance: Junk +# Id: 0x88 + +# - Name: Stamp (R) +# Importance: Junk +# Id: 0x89 + +# - Name: Stamp (S) +# Importance: Junk +# Id: 0x8A + +# - Name: Stamp (T) +# Importance: Junk +# Id: 0x8B + +# - Name: Stamp (U) +# Importance: Junk +# Id: 0x8C + +# - Name: Stamp (V) +# Importance: Junk +# Id: 0x8D + +# - Name: Stamp (W) +# Importance: Junk +# Id: 0x8E + +# - Name: Stamp (X) +# Importance: Junk +# Id: 0x8F + +# - Name: Stamp (Y) +# Importance: Junk +# Id: 0x90 + +# - Name: Stamp (Z) +# Importance: Junk +# Id: 0x91 + +# - Name: Stamp (Rupee) +# Importance: Junk +# Id: 0x92 + +# - Name: Stamp (Treasure Chest) +# Importance: Junk +# Id: 0x93 + +# - Name: Stamp (Piece of Heart) +# Importance: Junk +# Id: 0x94 + +# - Name: Stamp (Heart Container) +# Importance: Junk +# Id: 0x95 + +# - Name: Stamp (Happy Link) +# Importance: Junk +# Id: 0x96 + +# - Name: Stamp (Angry Link) +# Importance: Junk +# Id: 0x97 + +# - Name: Stamp (Sad Link) +# Importance: Junk +# Id: 0x98 + +# - Name: Stamp (Surprised Link) +# Importance: Junk +# Id: 0x99 + +# - Name: Stamp (Wolf Link) +# Importance: Junk +# Id: 0x9A + +# - Name: Stamp (Happy Midna) +# Importance: Junk +# Id: 0x9B + +# - Name: Stamp (Angry Midna) +# Importance: Junk +# Id: 0x9C + +# - Name: Stamp (Sad Midna) +# Importance: Junk +# Id: 0xAD + +# - Name: Stamp (Surprised Midna) +# Importance: Junk +# Id: 0x9D + +# - Name: Stamp (Ooccoo) +# Importance: Junk +# Id: 0x9E + +# - Name: Stamp (Happy Zelda) +# Importance: Junk +# Id: 0x9F + +# - Name: Stamp (Angry Zelda) +# Importance: Junk +# Id: 0xA0 + +# - Name: Stamp (Sad Zelda) +# Importance: Junk +# Id: 0xA1 + +# - Name: Stamp (Surprised Zelda) +# Importance: Junk +# Id: 0xA2 + +# - Name: Stamp (Zant) +# Importance: Junk +# Id: 0xA3 + +# - Name: Stamp (Agitha) +# Importance: Junk +# Id: 0xA4 + +# - Name: Stamp (Malo Mart) +# Importance: Junk +# Id: 0xA5 + +# - Name: Stamp (Cucco) +# Importance: Junk +# Id: 0xA6 + +# - Name: Stamp (Fairy) +# Importance: Junk +# Id: 0xA7 + +# - Name: Stamp (True Midna) +# Importance: Junk +# Id: 0xA8 diff --git a/mods/randomizer/generator/data/locations.yaml b/mods/randomizer/generator/data/locations.yaml new file mode 100644 index 0000000000..d62b36d3aa --- /dev/null +++ b/mods/randomizer/generator/data/locations.yaml @@ -0,0 +1,7478 @@ +# Note: Metadata fields also get added to a location's categories. +# So, for example, the Wooden Sword Chest location has the categories +# listed under the Categories field as well as the category "Chest" + +# ORDONA PROVINCE + +- Name: Wooden Sword Chest + Original Item: Progressive Sword + Categories: + - Overworld + - Ordona Province + - ARC + Metadata: + Chest: + - Stage: 65 + Tbox Id: 4 + +- Name: Links Basement Chest + Original Item: Purple Rupee Links House + Categories: + - Overworld + - Ordona Province + - ARC + Metadata: + Chest: + - Stage: 65 + Tbox Id: 1 + +- Name: Uli Cradle Delivery + Original Item: Progressive Fishing Rod + Categories: + - Overworld + - Ordona Province + - Npc + Metadata: + Name Lookup: + - Uli Cradle Delivery + Event Flag: 0x0301 + +- Name: Ordon Cat Rescue + Original Item: Bottle with Half Milk + Categories: + - Overworld + - Npc + - Ordona Province + Metadata: + Name Lookup: + - Ordon Cat Rescue + Event Flag: 0x1408 + +- Name: Sera Shop Slingshot + Original Item: Slingshot + Categories: + - Overworld + - Ordona Province + Metadata: + Shop: + - Stage: 65 + Item: 0x4B + Event Flag: 0x4902 + +- Name: Ordon Shield + Original Item: Ordon Shield + Categories: + - Overworld + - Ordona Province + Metadata: + Name Lookup: + - Ordon Shield + Switch Flag: + Stage: 65 + Flag: 0x1A + +- Name: Ordon Sword + Original Item: Progressive Sword + Categories: + - Overworld + - Ordona Province + Metadata: + Name Lookup: + - Ordon Sword + Switch Flag: + Stage: 65 + Flag: 0x18 + +- Name: Wrestling With Bo + Original Item: Iron Boots + Categories: + - Overworld + - Ordona Province + - ARC + Metadata: + Chest: + - Stage: 65 + Tbox Id: 2 + +- Name: Ordon Bo Cliff Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x80 + +- Name: Ordon Bo Roof Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x8B + +- Name: Ordon Bo Window Rupee 1 + Original Item: Green Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x8A + +- Name: Ordon Bo Window Rupee 2 + Original Item: Green Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x86 + +- Name: Ordon Hidden Rusl House Rupee + Original Item: Orange Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x88 + +- Name: Ordon Rupee In Grass By Bo + Original Item: Green Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x96 + +- Name: Ordon Rupee In River 1 + Original Item: Green Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x95 + +- Name: Ordon Rupee In River 2 + Original Item: Green Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x91 + +- Name: Ordon Rupee Under Bridge + Original Item: Green Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x82 + +- Name: Ordon Rupee Under Tall Tree 1 + Original Item: Green Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x87 + +- Name: Ordon Rupee Under Tall Tree 2 + Original Item: Green Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x85 + +- Name: Ordon Rusl House Roof Rupee 1 + Original Item: Yellow Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x84 + +- Name: Ordon Rusl House Roof Rupee 2 + Original Item: Yellow Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x81 + +- Name: Ordon Shield House Ledge Grass Rupee + Original Item: Purple Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x94 + +- Name: Ordon Tree Long Branch Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x97 + +- Name: Ordon Tree Short Branch Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Ordona Province + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 43 + Flag: 0x89 + +- Name: Herding Goats Reward + Original Item: Piece of Heart + Categories: + - Overworld + - Npc + - Ordona Province + Metadata: + Name Lookup: + - Herding Goats Reward + Event Flag: 0x4240 + +- Name: Ordon Ranch Grotto Lantern Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (Happy Link) + Categories: + - Overworld + - Ordona Province + - DZX + Metadata: + Chest: + - Stage: 35 + Tbox Id: 7 + +- Name: Ordon Spring Golden Wolf + Original Item: Progressive Hidden Skill + Categories: + - Overworld + - + - Ordona Province + Metadata: + Golden Wolf: + - Flag: 0x3C08 + +- Name: Ordon Spring Warp Portal + Original Item: Ordon Spring Portal + Categories: + - Overworld + - Warp Portal + - Ordona Province + Metadata: + - None + +# FARON PROVINCE + +- Name: South Faron Woods Twilit Insect in Tunnel 1 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x1 + +- Name: South Faron Woods Twilit Insect in Tunnel 2 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x6 + +- Name: Faron Woods Coros House Interior Twilit Insect 1 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 67 + Flag: 0x9 + +- Name: Faron Woods Coros House Interior Twilit Insect 2 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 67 + Flag: 0x4 + +- Name: South Faron Woods Coros House Exterior Twilit Insect + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x0 + +- Name: South Faron Woods Twilit Insect Behind Gate 1 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0xB + +- Name: South Faron Woods Twilit Insect Behind Gate 2 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x5 + +- Name: Faron Mist Twilit Insect on Wall 1 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x12 + +- Name: Faron Mist Twilit Insect on Wall 2 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x11 + +- Name: Faron Mist Twilit Insect on Center Stump 1 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0xE + +- Name: Faron Mist Twilit Insect on Center Stump 2 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0xD + +- Name: Faron Mist Twilit Insect on Center Stump 3 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0xC + +- Name: Faron Mist Burrowing Twilit Insect 1 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x17 + +- Name: Faron Mist Burrowing Twilit Insect 2 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x8 + +- Name: North Faron Woods Twilit Insect 1 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + - Twilit Insect + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x15 + +- Name: North Faron Woods Twilit Insect 2 + Original Item: Faron Twilight Tear + Categories: + - Overworld + - Faron Woods + - Twilit Insect + Metadata: + Twilit Insect: + - Stage: 45 + Flag: 0x14 + +- Name: South Faron Warp Portal + Original Item: South Faron Portal + Categories: + - Overworld + - Warp Portal + - Faron Woods + Metadata: + - None + +- Name: Coro Bottle + Original Item: Bottle with Lantern Oil + Categories: + - Overworld + - Npc + - Faron Woods + - ARC + Metadata: + Name Lookup: + - Coro Bottle + Event Flag: 0x1A08 + +- Name: Faron Woods Coro Boulder Rupee 1 + Original Item: Green Rupee + Categories: + - Overworld + - Faron Woods + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 45 + Flag: 0x80 + +- Name: Faron Woods Coro Boulder Rupee 2 + Original Item: Green Rupee + Categories: + - Overworld + - Faron Woods + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 45 + Flag: 0x81 + +- Name: Faron Woods Coro Boulder Rupee 3 + Original Item: Blue Rupee + Categories: + - Overworld + - Faron Woods + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 45 + Flag: 0x82 + +- Name: Faron Woods Coro Boulder Rupee 4 + Original Item: Yellow Rupee + Categories: + - Overworld + - Faron Woods + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 45 + Flag: 0x83 + +- Name: South Faron Cave Chest + Original Item: Yellow Rupee + Categories: + - Overworld + - Faron Woods + - ARC + Metadata: + Chest: + - Stage: 40 + Tbox Id: 31 + +- Name: Faron Mist South Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Faron Woods + - DZX + Metadata: + Chest: + - Stage: 45 + Tbox Id: 29 + +- Name: Faron Mist Stump Chest + Original Item: Red Rupee + Categories: + - Overworld + - Faron Woods + - DZX + Metadata: + Chest: + - Stage: 45 + Tbox Id: 28 + +- Name: Faron Mist North Chest + Original Item: Yellow Rupee + Categories: + - Overworld + - Faron Woods + - DZX + Metadata: + Chest: + - Stage: 45 + Tbox Id: 27 + +- Name: Faron Mist Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Faron Woods + Metadata: + Poe: + - Stage: 45 + Flag: 0x5D + +- Name: Faron Mist Cave Open Chest + Original Item: North Faron Woods Gate Key + Categories: + - Overworld + - Faron Woods + - Small Key + - ARC + Metadata: + Chest: + - Stage: 45 + Tbox Id: 24 + +- Name: Faron Mist Cave Lantern Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Faron Woods + - ARC + Metadata: + Chest: + - Stage: 45 + Tbox Id: 26 + +- Name: North Faron Warp Portal + Original Item: North Faron Portal + Categories: + - Overworld + - Warp Portal + - Faron Woods + Metadata: + - None + +- Name: Faron Woods Golden Wolf + Original Item: Progressive Hidden Skill + Categories: + - Overworld + - Faron Woods + Metadata: + Golden Wolf: + - Flag: 0x3C10 + +- Name: North Faron Woods Deku Baba Chest + Original Item: Yellow Rupee + Categories: + - Overworld + - Faron Woods + - ARC + Metadata: + Chest: + - Stage: 45 + Tbox Id: 63 + +- Name: Faron Woods Owl Statue Sky Character + Original Item: Progressive Sky Book + Categories: + - Overworld + - Faron Woods + Metadata: + Sky Character: + - Stage: 45 + Room: 8 + Event Flag: 0x6080 + +- Name: Faron Woods Owl Statue Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Faron Woods + - ARC + Metadata: + Chest: + - Stage: 45 + Tbox Id: 30 + +- Name: Faron Field Bridge Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Hyrule Field - Faron Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 6 + +# HD Only +# - Name: Faron Field Corner Grotto Main Chest +# Original Item: Stamp (Wolf Link) +# Categories: +# - Overworld +# # - Hyrule Field - Faron Province + +- Name: Faron Field Corner Grotto Left Chest + Original Item: Red Rupee + Categories: + - Overworld + - Hyrule Field - Faron Province + - DZX + Metadata: + Chest: + - Stage: 36 + Tbox Id: 11 + +- Name: Faron Field Corner Grotto Rear Chest + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Faron Province + - DZX + Metadata: + Chest: + - Stage: 36 + Tbox Id: 13 + +- Name: Faron Field Corner Grotto Right Chest + Original Item: Red Rupee + Categories: + - Overworld + - Hyrule Field - Faron Province + - DZX + Metadata: + Chest: + - Stage: 36 + Tbox Id: 12 + +- Name: Faron Field Female Beetle + Original Item: Female Beetle + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Faron Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x9E + +- Name: Faron Field Male Beetle + Original Item: Male Beetle + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Faron Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x9F + +- Name: Faron Field Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hyrule Field - Faron Province + Metadata: + Poe: + - Stage: 56 + Flag: 0x39 + +- Name: Faron Field Tree Heart Piece + Original Item: Piece of Heart + Categories: + - Overworld + - Hyrule Field - Faron Province + - ARC + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x81 + +- Name: Lost Woods Boulder Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Sacred Grove + Metadata: + Poe: + - Stage: 54 + Flag: 0x10 + +- Name: Lost Woods Lantern Chest + Original Item: Bombs 30 + Categories: + - Overworld + - Sacred Grove + - ARC + Metadata: + Chest: + - Stage: 54 + Tbox Id: 3 + +- Name: Lost Woods Waterfall Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Sacred Grove + Metadata: + Poe: + - Stage: 54 + Flag: 0x11 + +- Name: Sacred Grove Baba Serpent Grotto Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Sacred Grove + - DZX + Metadata: + Chest: + - Stage: 36 + Tbox Id: 3 + +- Name: Sacred Grove Spinner Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (X) + Categories: + - Overworld + - Sacred Grove + - ARC + Metadata: + Chest: + - Stage: 54 + Tbox Id: 2 + +- Name: Sacred Grove Warp Portal + Original Item: Sacred Grove Portal + Categories: + - Overworld + - Warp Portal + - Sacred Grove + Metadata: + - None + +- Name: Sacred Grove Pedestal Master Sword + Original Item: Progressive Sword + Categories: + - Overworld + - Sacred Grove + - Event + Metadata: + Name Lookup: + - Sacred Grove Pedestal Master Sword + Event Flag: 0x2120 + +- Name: Sacred Grove Pedestal Shadow Crystal + Original Item: Shadow Crystal + Categories: + - Overworld + - Sacred Grove + - Event + Metadata: + Name Lookup: + - Sacred Grove Pedestal Shadow Crystal + Event Flag: 0x2120 + +- Name: Sacred Grove Master Sword Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Sacred Grove + Metadata: + Poe: + - Stage: 54 + Flag: 0x0F + +- Name: Sacred Grove Female Snail + Original Item: Female Snail + Categories: + - Overworld + - Golden Bug + - Sacred Grove + - DZX + Metadata: + Freestanding Item: + - Stage: 54 + Flag: 0x98 + +- Name: Sacred Grove Male Snail + Original Item: Male Snail + Categories: + - Overworld + - Golden Bug + - Sacred Grove + - DZX + Metadata: + Freestanding Item: + - Stage: 54 + Flag: 0x99 + +- Name: Sacred Grove Past Owl Statue Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Sacred Grove + - ARC + Metadata: + Chest: + - Stage: 54 + Tbox Id: 1 + +- Name: Sacred Grove Temple of Time Owl Statue Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Sacred Grove + Metadata: + Poe: + - Stage: 54 + Flag: 0x1E + +# ELDIN PROVINCE + +- Name: Sanctuary Basement Twilit Insect 1 + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 75 + Flag: 0x2 + +- Name: Sanctuary Basement Twilit Insect 2 + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 75 + Flag: 0x3 + +- Name: Sanctuary Basement Twilit Insect 3 + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 75 + Flag: 0xC + +- Name: Kakariko Graveyard Twilit Insect + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 48 + Flag: 0x6 + +- Name: Kakariko Malo Mart Twilit Insect + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 68 + Flag: 0x9 + +- Name: Kakariko Inn Pipe Twilit Insect + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 68 + Flag: 0x8 + +- Name: Kakariko Inn Bedroom Twilit Insect + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 68 + Flag: 0x0 + +- Name: Kakariko Bug House Twilit Insect + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 68 + Flag: 0x1 + +- Name: Barnes Bomb Shop Twilit Insect + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 68 + Flag: 0x7 + +- Name: Kakariko Destroyed Building Twilit Insect 1 + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 46 + Flag: 0x4 + +- Name: Kakariko Destroyed Building Twilit Insect 2 + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 46 + Flag: 0x5 + +- Name: Kakariko Destroyed Building Twilit Insect 3 + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 46 + Flag: 0xB + +- Name: Kakariko Watchtower Twilit Insect + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 68 + Flag: 0xA + +- Name: Death Mountain Trail Twilit Insect Near Howling Stone + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 47 + Flag: 0xF + +- Name: Death Mountain Trail Twilit Insect on Wall + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 47 + Flag: 0xE + +- Name: Death Mountain Trail Twilit Insect in Hot Spring + Original Item: Eldin Twilight Tear + Categories: + - Overworld + - Eldin Province + Metadata: + Twilit Insect: + - Stage: 47 + Flag: 0x10 + +- Name: Kakariko Gorge Warp Portal + Original Item: Kakariko Gorge Portal + Categories: + - Overworld + - Warp Portal + - Hyrule Field - Eldin Province + Metadata: + - None + +- Name: Kakariko Gorge Female Pill Bug + Original Item: Female Pill Bug + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Eldin Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x94 + +- Name: Kakariko Gorge Male Pill Bug + Original Item: Male Pill Bug + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Eldin Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x95 + +- Name: Kakariko Gorge Owl Statue Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (E) + Categories: + - Overworld + - Hyrule Field - Eldin Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 4 + +- Name: Kakariko Gorge Owl Statue Sky Character + Original Item: Progressive Sky Book + Categories: + - Overworld + - Hyrule Field - Eldin Province + Metadata: + Sky Character: + - Stage: 56 + Room: 3 + Event Flag: 0x6020 + +- Name: Kakariko Gorge Owl Statue Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Eldin + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x8D + +- Name: Kakariko Gorge Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hyrule Field - Eldin Province + Metadata: + Poe: + - Stage: 56 + Flag: 0x3A + +- Name: Kakariko Gorge Spire Heart Piece + Original Item: Piece of Heart + Categories: + - Overworld + - Hyrule Field - Eldin Province + - ARC + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x82 + +- Name: Kakariko Gorge Double Clawshot Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Hyrule Field - Eldin Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 5 + +- Name: Kakariko Gorge Spire Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Eldin + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x8E + +- Name: Eldin Lantern Cave First Chest + Original Item: Red Rupee + # HD Original Item: Stamp (O) + Categories: + - Overworld + - Eldin Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 32 + Tbox Id: 61 + +- Name: Eldin Lantern Cave Second Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Eldin Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 32 + Tbox Id: 63 + +- Name: Eldin Lantern Cave Lantern Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Eldin Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 32 + Tbox Id: 62 + +- Name: Eldin Lantern Cave Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Eldin Lantern Cave + Metadata: + Poe: + - Stage: 32 + Flag: 0x60 + +- Name: Kakariko Village Warp Portal + Original Item: Kakariko Village Portal + Categories: + - Overworld + - Warp Portal + - Kakariko Village + Metadata: + - None + +- Name: Eldin Spring Underwater Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Kakariko Village + - ARC + Metadata: + Chest: + - Stage: 46 + Tbox Id: 21 + +- Name: Eldin Spring Underwater Boulder Rupee + Original Item: Orange Rupee + Categories: + - Overworld + - Kakariko Village + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 46 + Flag: 0x8D + +- Name: Kakariko Village Bomb Rock Spire Heart Piece + Original Item: Piece of Heart + Categories: + - Overworld + - Kakariko Village + - DZX + Metadata: + Freestanding Item: + - Stage: 46 + Flag: 0x8C + +- Name: Kakariko Village Bell Rupee + Original Item: Silver Rupee + Categories: + - Overworld + - Kakariko Village + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 46 + Flag: 0x8A + +- Name: Kakariko Village Hot Spring Ledge Box Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Kakariko Village + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 46 + Flag: 0x87 + +- Name: Kakariko Village Spring Shortcut Box Rupee 1 + Original Item: Blue Rupee + Categories: + - Overworld + - Kakariko Village + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 46 + Flag: 0x88 + +- Name: Kakariko Village Spring Shortcut Box Rupee 2 + Original Item: Yellow Rupee + Categories: + - Overworld + - Kakariko Village + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 46 + Flag: 0x89 + +- Name: Kakariko Village Malo Mart Hawkeye + Original Item: Hawkeye + Categories: + - Overworld + - Kakariko Village + - ARC + Metadata: + Shop: + - Stage: 68 + Item: 0x3E + Switch Flag: + Stage: 68 + Flag: 0x33 + +- Name: Kakariko Village Malo Mart Hylian Shield + Original Item: Hylian Shield + Categories: + - Overworld + - Kakariko Village + - ARC + Metadata: + Shop: + - Stage: 68 + Item: 0x2C + Switch Flag: + Stage: 68 + Flag: 0x39 + +- Name: Kakariko Village Malo Mart Red Potion + Original Item: Red Potion Shop + Categories: + - Overworld + - Kakariko Village + - ARC + Metadata: + Shop: + - Stage: 68 + Item: 0x61 + Switch Flag: + Stage: 68 + Flag: 0x4 + +- Name: Kakariko Village Malo Mart Wooden Shield + Original Item: Wooden Shield + Categories: + - Overworld + - Kakariko Village + - ARC + Metadata: + Shop: + - Stage: 68 + Item: 0x2B + Switch Flag: + Stage: 68 + Flag: 0x5 + +- Name: Kakariko Inn Chest + Original Item: Red Rupee + Categories: + - Overworld + - Kakariko Village + - ARC + Metadata: + Chest: + - Stage: 68 + Tbox Id: 23 + +- Name: Kakariko Village Female Ant + Original Item: Female Ant + Categories: + - Overworld + - Golden Bug + - Kakariko Village + - DZX + Metadata: + Freestanding Item: + - Stage: 68 + Flag: 0x90 + +- Name: Kakariko Village Ant House Ledge Box Rupee + Original Item: Red Rupee + Categories: + - Overworld + - Kakariko Village + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 46 + Flag: 0x85 + +- Name: Barnes Bomb Bag + Original Item: Bomb Bag + Categories: + - Overworld + - Npc + - Kakariko Village + - ARC + Metadata: + Shop: + - Stage: 68 + Item: 0x50 + Event Flag: 0x0908 + +- Name: Kakariko Village Bomb Shop Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Kakariko Village + Metadata: + Poe: + - Stage: 46 + Flag: 0x5E + +- Name: Kakariko Village Watchtower Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Kakariko Village + Metadata: + Poe: + - Stage: 46 + Flag: 0x5F + +- Name: Kakariko Watchtower Alcove Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Kakariko Village + - ARC + Metadata: + Chest: + - Stage: 46 + Tbox Id: 20 + +- Name: Kakariko Watchtower Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Kakariko Village + - ARC + Metadata: + Chest: + - Stage: 68 + Tbox Id: 17 + +- Name: Talo Sharpshooting + Original Item: Piece of Heart + Categories: + - Overworld + - Npc + - Kakariko Village + - ARC + Metadata: + Name Lookup: + - Talo Sharpshooting + Event Flag: 0x0920 + +- Name: Renados Letter + Original Item: Renados Letter + Categories: + - Overworld + - Npc + - Kakariko Village + - ARC + Metadata: + Name Lookup: + - Renados Letter + Event Flag: 0x0F80 + +- Name: Ilia Memory Reward + Original Item: Horse Call + Categories: + - Overworld + - Npc + - Kakariko Village + Metadata: + Name Lookup: + - Ilia Memory Reward + Event Flag: 0x5E04 + +- Name: Rutelas Blessing + Original Item: Zora Armor + Categories: + - Overworld + - Npc + - Kakariko Graveyard + - ARC + Metadata: + Name Lookup: + - Rutelas Blessing + Event Flag: 0x0804 + +- Name: Gift From Ralis + Original Item: Progressive Fishing Rod + Categories: + - Overworld + - Npc + - Kakariko Graveyard + - ARC + Metadata: + Name Lookup: + - Gift From Ralis + Event Flag: 0x3B80 + +- Name: Kakariko Graveyard Lantern Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Kakariko Graveyard + - ARC + Metadata: + Chest: + - Stage: 48 + Tbox Id: 24 + +- Name: Kakariko Graveyard Male Ant + Original Item: Male Ant + Categories: + - Overworld + - Golden Bug + - Kakariko Graveyard + - DZX + Metadata: + Freestanding Item: + - Stage: 48 + Flag: 0x91 + +- Name: Kakariko Graveyard Underwater Boulder Rupee + Original Item: Red Rupee + Categories: + - Overworld + - Kakariko Graveyard + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 48 + Flag: 0x8E + +- Name: Kakariko Graveyard Golden Wolf + Original Item: Progressive Hidden Skill + Categories: + - Overworld + - Kakariko Graveyard + Metadata: + Golden Wolf: + - Flag: 0x3D80 + +- Name: Kakariko Graveyard Open Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Kakariko Graveyard + Metadata: + Poe: + - Stage: 48 + Flag: 0x58 + +- Name: Kakariko Graveyard Grave Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Kakariko Graveyard + Metadata: + Poe: + - Stage: 48 + Flag: 0x57 + +- Name: Death Mountain Warp Portal + Original Item: Death Mountain Portal + Categories: + - Overworld + - Warp Portal + - Death Mountain + Metadata: + - None + +- Name: Death Mountain Alcove Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Death Mountain + - ARC + Metadata: + Chest: + - Stage: 47 + Tbox Id: 22 + +- Name: Death Mountain Volcano Ledge Rupee 1 + Original Item: Yellow Rupee + Categories: + - Overworld + - Death Mountain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 47 + Flag: 0x81 + +- Name: Death Mountain Volcano Ledge Rupee 2 + Original Item: Yellow Rupee + Categories: + - Overworld + - Death Mountain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 47 + Flag: 0x82 + +- Name: Death Mountain Volcano Ledge Rupee 3 + Original Item: Yellow Rupee + Categories: + - Overworld + - Death Mountain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 47 + Flag: 0x83 + +- Name: Death Mountain Volcano Pipe Ledge Rock Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Death Mountain + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 47 + Flag: 0x80 + +- Name: Death Mountain Trail Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Death Mountain + Metadata: + Poe: + - Stage: 47 + Flag: 0x59 + +- Name: Eldin Field Bomb Rock Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Hyrule Field - Eldin Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 2 + +- Name: Eldin Field Bomskit Grotto Lantern Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (C) + Categories: + - Overworld + - Hyrule Field - Eldin Province + - DZX + Metadata: + Chest: + - Stage: 35 + Tbox Id: 6 + +- Name: Eldin Field Bomskit Grotto Left Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Hyrule Field - Eldin Province + - DZX + Metadata: + Chest: + - Stage: 35 + Tbox Id: 10 + +- Name: Eldin Field Female Grasshopper + Original Item: Female Grasshopper + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Eldin Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x98 + +- Name: Eldin Field Male Grasshopper + Original Item: Male Grasshopper + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Eldin Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x99 + +- Name: Goron Springwater Rush + Original Item: Piece of Heart + Categories: + - Overworld + - Npc + - Hyrule Field - Eldin Province + - Boss + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x80 + +- Name: Eldin Field Water Bomb Fish Grotto Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (Sad Link) + Categories: + - Overworld + - Hyrule Field - Eldin Province + - DZX + Metadata: + Chest: + - Stage: 39 + Tbox Id: 16 + +- Name: Bridge of Eldin Warp Portal + Original Item: Bridge of Eldin Portal + Categories: + - Overworld + - Warp Portal + - Hyrule Field - Eldin Province + Metadata: + - None + +- Name: Bridge of Eldin Female Phasmid + Original Item: Female Phasmid + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Eldin Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x96 + +- Name: Bridge of Eldin Male Phasmid + Original Item: Male Phasmid + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Eldin Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x97 + +- Name: Bridge of Eldin Owl Statue Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Hyrule Field - Eldin Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 3 + +- Name: Bridge of Eldin Owl Statue Sky Character + Original Item: Progressive Sky Book + Categories: + - Overworld + - Hyrule Field - Eldin Province + Metadata: + Sky Character: + - Stage: 56 + Room: 0 + Event Flag: 0x6010 + +- Name: Bridge of Eldin Boulder Rupee + Original Item: Red Rupee + Categories: + - Overworld + - Hyrule Field - Eldin + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x8F + +- Name: Eldin Stockcave Upper Chest + Original Item: Red Rupee + Categories: + - Overworld + - Eldin Stockcave + - ARC + Metadata: + Chest: + - Stage: 34 + Tbox Id: 63 + +- Name: Eldin Stockcave Lantern Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (Piece of Heart) + Categories: + - Overworld + - Eldin Stockcave + - ARC + Metadata: + Chest: + - Stage: 34 + Tbox Id: 61 + +- Name: Eldin Stockcave Lowest Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Eldin Stockcave + - ARC + Metadata: + Chest: + - Stage: 34 + Tbox Id: 62 + +- Name: Eldin Field Stalfos Grotto Left Small Chest + Original Item: Bombs 5 + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Chest: + - Stage: 36 + Tbox Id: 18 + +- Name: Eldin Field Stalfos Grotto Right Small Chest + Original Item: Bombs 5 + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Chest: + - Stage: 36 + Tbox Id: 17 + +- Name: Eldin Field Stalfos Grotto Stalfos Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Chest: + - Stage: 36 + Tbox Id: 2 + +- Name: Skybook From Impaz + Original Item: Progressive Sky Book + Categories: + - Overworld + - Npc + - Hidden Village + - ARC + Metadata: + Name Lookup: + - Skybook From Impaz + Event Flag: 0x5F80 + +- Name: Cats Hide and Seek Minigame + Original Item: Piece of Heart + Categories: + - Overworld + - Npc + - Hidden Village + Metadata: + Freestanding Item: + - Stage: 63 + Flag: 0x8B + +- Name: Hidden Village Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hidden Village + Metadata: + Poe: + - Stage: 63 + Flag: 0x40 + +- Name: Ilia Charm + Original Item: Ilias Charm + Categories: + - Overworld + - Npc + - Hidden Village + - ARC + Metadata: + Name Lookup: + - Ilia Charm + Event Flag: 0x2280 + +# LANAYRU PROVINCE + +- Name: Castle Town Twilit Insect + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 53 + Flag: 0x2C + +- Name: Lake Hylia Twilit Insect Between Bridges + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 52 + Flag: 0x2F + +- Name: Lake Hylia Burrowing Twilit Insect + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 52 + Flag: 0x30 + +- Name: Lake Hylia Twilit Insect Behind Canon + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 52 + Flag: 0x32 + +- Name: Lake Hylia Twilit Insect on Docks + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 52 + Flag: 0x31 + +- Name: Lake Hylia Twilit Bloat + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 52 + Flag: 0x35 + +- Name: Zoras River Twilit Insect 1 + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 49 + Flag: 0x3D + +- Name: Zoras River Twilit Insect 2 + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 49 + Flag: 0x36 + +- Name: Zoras River Twilit Insect 3 + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 49 + Flag: 0x3A + +- Name: Zoras River Twilit Insect 4 + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 49 + Flag: 0x39 + +- Name: Upper Zoras River Twilit Insect + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 61 + Flag: 0x37 + +- Name: Zoras Domain Twilit Insect near Lilypads 1 + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 50 + Flag: 0x3B + +- Name: Zoras Domain Twilit Insect near Lilypads 2 + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 50 + Flag: 0x2E + +- Name: Zoras Domain Burrowing Twilit Insect + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 50 + Flag: 0x2D + +- Name: Zoras Domain Twilit Insect on West Ledge + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + Metadata: + Twilit Insect: + - Stage: 50 + Flag: 0x3C + +- Name: Zoras Domain Throne Room Twilit Insect + Original Item: Lanayru Twilight Tear + Categories: + - Overworld + - Lanayru Province + - Twilit Insect + Metadata: + Twilit Insect: + - Stage: 50 + Flag: 0x3E + +- Name: Lanayru Field Behind Gate Underwater Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (F) + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 7 + +- Name: Lanayru Field North Underwater Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x87 + +- Name: Lanayru Field South Underwater Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x86 + +- Name: Lanayru Field Tree Boulder Rupee + Original Item: Purple Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x88 + +- Name: Lanayru Field Bridge Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hyrule Field - Lanayru Province + Metadata: + Poe: + - Stage: 56 + Flag: 0x33 + +- Name: Lanayru Field Female Stag Beetle + Original Item: Female Stag Beetle + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x9A + +- Name: Lanayru Field Male Stag Beetle + Original Item: Male Stag Beetle + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x9B + +# HD Only location +# - Name: Lanayru Field Chu Grotto Chest +# Original Item: Stamp (I) +# Categories: +# - Overworld +# # - Hyrule Field - Lanayru Province +# - DZX +# Metadata: +# - None + +- Name: Lanayru Field Poe Grotto Left Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hyrule Field - Lanayru Province + Metadata: + Poe: + - Stage: 35 + Flag: 0x0B + +- Name: Lanayru Field Poe Grotto Right Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hyrule Field - Lanayru Province + Metadata: + Poe: + - Stage: 35 + Flag: 0x0A + +- Name: Lanayru Field Skulltula Grotto Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (P) + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Chest: + - Stage: 38 + Tbox Id: 19 + +- Name: Lanayru Ice Block Puzzle Cave Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 30 + Tbox Id: 0 + +- Name: Lanayru Field Spinner Track Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 8 + +- Name: Lanayru Field North Spinner Track Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x89 + +- Name: Lanayru Field South Spinner Track Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x8A + +- Name: Castle Town Warp Portal + Original Item: Castle Town Portal + Categories: + - Overworld + - Warp Portal + - Hyrule Field - Lanayru Province + Metadata: + - None + +- Name: West Hyrule Field Female Butterfly + Original Item: Female Butterfly + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Freestanding Item: + - Stage: 57 + Flag: 0x9C + +- Name: West Hyrule Field Male Butterfly + Original Item: Male Butterfly + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Freestanding Item: + - Stage: 57 + Flag: 0x9D + +- Name: West Hyrule Field Northern Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 57 + Flag: 0x84 + +- Name: West Hyrule Field Southern Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 57 + Flag: 0x85 + +- Name: West Hyrule Field Helmasaur Grotto Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (Angry Link) + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Chest: + - Stage: 35 + Tbox Id: 0 + +- Name: West Hyrule Field Golden Wolf + Original Item: Progressive Hidden Skill + Categories: + - Overworld + - Hyrule Field - Lanayru Province + Metadata: + Golden Wolf: + - Flag: 0x3C04 + +- Name: Hyrule Field Amphitheater Owl Statue Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 57 + Tbox Id: 11 + +- Name: Hyrule Field Amphitheater Owl Statue Sky Character + Original Item: Progressive Sky Book + Categories: + - Overworld + - Hyrule Field - Lanayru Province + Metadata: + Sky Character: + - Stage: 57 + Room: 8 + Switch Flag: + Stage: 57 + Flag: 0x57 + +- Name: Hyrule Field Amphitheater Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hyrule Field - Lanayru Province + Metadata: + Poe: + - Stage: 57 + Flag: 0x49 + +- Name: Charlo Donation Blessing + Original Item: Piece of Heart + Categories: + - Overworld + - Npc + - Castle Town + - ObjectARC + - Boss + Metadata: + Name Lookup: + - Charlo Donation Blessing + Event Flag: 0x2480 + +- Name: STAR Prize 1 + Original Item: Progressive Bow + Categories: + - Overworld + - Npc + - Castle Town + - ARC + Metadata: + Name Lookup: + - STAR Prize 1 + Event Flag: 0x2308 + +- Name: STAR Prize 2 + Original Item: Progressive Bow + Categories: + - Overworld + - Npc + - Castle Town + - ARC + Metadata: + Name Lookup: + - STAR Prize 2 + Event Flag: 0x2301 + +- Name: Castle Town Malo Mart Magic Armor + Original Item: Magic Armor + Categories: + - Overworld + - Castle Town + - ARC + Metadata: + Shop: + - Stage: 73 + Item: 0x30 + Switch Flag: + Stage: 73 + Flag: 0x2B + +# HD Only location +# - Name: Castle Town Malo Mart Stamp +# Original Item: Stamp (Malo Mart) +# Categories: +# - Overworld +# - Castle Town +# - ARC +# - Shop +# Metadata: +# - None + +- Name: North Castle Town Golden Wolf + Original Item: Progressive Hidden Skill + Categories: + - Overworld + - Castle Town + Metadata: + Golden Wolf: + - Flag: 0x3D40 + +- Name: Doctors Office Balcony Chest + Original Item: Red Rupee + Categories: + - Overworld + - Castle Town + - ARC + Metadata: + Chest: + - Stage: 53 + Tbox Id: 1 + +- Name: East Castle Town Bridge Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hyrule Field - Lanayru Province + Metadata: + Poe: + - Stage: 57 + Flag: 0x47 + +- Name: Jovani 20 Poe Soul Reward + Original Item: Bottle with Great Fairies Tears + # HD Original Item: Ghost Lantern + Categories: + - Overworld + - Npc + - Castle Town + - ARC + Metadata: + Name Lookup: + - Jovani 20 Poe Soul Reward + Event Flag: 0x5510 + +- Name: Jovani 60 Poe Soul Reward + Original Item: Silver Rupee + #HD Original Item: Bottle with Great Fairies Tears + Categories: + - Overworld + - Npc + - Castle Town + - ARC + Metadata: + Name Lookup: + - Jovani 60 Poe Soul Reward + Event Flag: 0x3820 + +# HD Only Location +# - Name: Gengle 60 Poe Soul Reward +# Original Item: Stamp (Rupee) +# Categories: +# - Overworld +# - Npc +# - Castle Town +# Metadata: +# - None + +- Name: Jovani House Poe + Original Item: Poe Soul + Categories: + - Overworld + - Castle Town + - Poe + Metadata: + Poe: + - Stage: 73 + Flag: 0x1E + +- Name: Telma Invoice + Original Item: Invoice + Categories: + - Overworld + - Npc + - Castle Town + - ARC + Metadata: + Name Lookup: + - Telma Invoice + Event Flag: 0x2180 + +- Name: Agitha Male Beetle Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC0 + Event Flag: 0x3110 + +- Name: Agitha Female Beetle Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC1 + Event Flag: 0x3108 + +- Name: Agitha Male Butterfly Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC2 + Event Flag: 0x3104 + +- Name: Agitha Female Butterfly Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC3 + Event Flag: 0x3102 + +- Name: Agitha Male Stag Beetle Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC4 + Event Flag: 0x3101 + +- Name: Agitha Female Stag Beetle Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC5 + Event Flag: 0x3280 + +- Name: Agitha Male Grasshopper Reward + Original Item: Progressive Wallet + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC6 + Event Flag: 0x3240 + +- Name: Agitha Female Grasshopper Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC7 + Event Flag: 0x3220 + +- Name: Agitha Male Phasmid Reward + Original Item: Progressive Wallet + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC8 + Event Flag: 0x3210 + +- Name: Agitha Female Phasmid Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xC9 + Event Flag: 0x3208 + +- Name: Agitha Male Pill Bug Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xCA + Event Flag: 0x3204 + +- Name: Agitha Female Pill Bug Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xCB + Event Flag: 0x3202 + +- Name: Agitha Male Mantis Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xCC + Event Flag: 0x3201 + +- Name: Agitha Female Mantis Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xCD + Event Flag: 0x3380 + +- Name: Agitha Male Ladybug Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xCE + Event Flag: 0x3340 + +- Name: Agitha Female Ladybug Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xCF + Event Flag: 0x3320 + +- Name: Agitha Male Snail Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xD0 + Event Flag: 0x3310 + +- Name: Agitha Female Snail Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xD1 + Event Flag: 0x3308 + +- Name: Agitha Male Dragonfly Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xD2 + Event Flag: 0x3304 + +- Name: Agitha Female Dragonfly Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xD3 + Event Flag: 0x3302 + +- Name: Agitha Male Ant Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xD4 + Event Flag: 0x3301 + +- Name: Agitha Female Ant Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xD5 + Event Flag: 0x3480 + +- Name: Agitha Male Dayfly Reward + Original Item: Purple Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xD6 + Event Flag: 0x3440 + +- Name: Agitha Female Dayfly Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Npc + - Castle Town + Metadata: + Bug Reward: + - Item Id: 0xD7 + Event Flag: 0x3420 + +# HD Only Location +# - Name: Agitha 12 Golden Bugs Reward +# Original Item: Stamp (Agitha) +# Categories: +# - Overworld +# - Npc +# - Castle Town +# - Bug Reward +# Metadata: +# - None + +- Name: Outside South Castle Town Tightrope Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 57 + Tbox Id: 12 + +- Name: Outside South Castle Town Fountain Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (Surprised Zelda) + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 57 + Tbox Id: 14 + +- Name: Outside South Castle Town Male Ladybug + Original Item: Male Ladybug + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Freestanding Item: + - Stage: 57 + Flag: 0x91 + +- Name: Outside South Castle Town Female Ladybug + Original Item: Female Ladybug + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Freestanding Item: + - Stage: 57 + Flag: 0x90 + +- Name: Outside South Castle Town Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hyrule Field - Lanayru Province + Metadata: + Poe: + - Stage: 57 + Flag: 0x30 + +- Name: Outside South Castle Town Tektite Grotto Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Chest: + - Stage: 39 + Tbox Id: 1 + +- Name: Outside South Castle Town Golden Wolf + Original Item: Progressive Hidden Skill + Categories: + - Overworld + - Hyrule Field - Lanayru Province + Metadata: + Golden Wolf: + - Flag: 0x3C02 + +- Name: Outside South Castle Town Double Clawshot Chasm Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (V) + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 57 + Tbox Id: 13 + +- Name: Outside South Castle Town Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 57 + Flag: 0x83 + +- Name: Wooden Statue + Original Item: Wooden Statue + Categories: + - Overworld + - Npc + - Hyrule Field - Lanayru Province + - REL + Metadata: + Name Lookup: + - Wooden Statue + Event Flag: 0x2204 + +- Name: Lake Hylia Bridge Owl Statue Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 15 + +- Name: Lake Hylia Bridge Owl Statue Sky Character + Original Item: Progressive Sky Book + Categories: + - Overworld + - Hyrule Field - Lanayru Province + Metadata: + Sky Character: + - Stage: 56 + Room: 13 + Event Flag: 0x6008 + +- Name: Lake Hylia Bridge Owl Statue Boulder Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x8B + +- Name: Lake Hylia Bridge Vines Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 10 + +- Name: Lake Hylia Bridge Female Mantis + Original Item: Female Mantis + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x92 + +- Name: Lake Hylia Bridge Male Mantis + Original Item: Male Mantis + Categories: + - Overworld + - Golden Bug + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x93 + +- Name: Lake Hylia Bridge Cliff Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - ARC + Metadata: + Chest: + - Stage: 56 + Tbox Id: 9 + +- Name: Lake Hylia Bridge Cliff Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Hyrule Field - Lanayru Province + Metadata: + Poe: + - Stage: 56 + Flag: 0x3B + +- Name: Lake Hylia Bridge Bubble Grotto Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (M) + Categories: + - Overworld + - Hyrule Field - Lanayru Province + - DZX + Metadata: + Chest: + - Stage: 38 + Tbox Id: 4 + +- Name: Lake Hylia Bridge Faron Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Hyrule Field - Lanayru + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 56 + Flag: 0x8C + +- Name: Lake Hylia Warp Portal + Original Item: Lake Hylia Portal + Categories: + - Overworld + - Warp Portal + - Ordona Province + Metadata: + - None + +- Name: Auru Gift To Fyer + Original Item: Aurus Memo + Categories: + - Overworld + - Npc + - Lake Hylia + - ARC + Metadata: + Name Lookup: + - Auru Gift To Fyer + Event Flag: 0x2520 + +- Name: Lake Hylia Tower Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Lake Hylia + Metadata: + Poe: + - Stage: 52 + Flag: 0x4C + +- Name: Lake Hylia Water Toadpoli Grotto Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (S) + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 39 + Tbox Id: 5 + +- Name: Lake Lantern Cave First Chest + Original Item: Bomblings 5 + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 1 + +- Name: Lake Lantern Cave Second Chest + Original Item: Yellow Rupee + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 2 + +- Name: Lake Lantern Cave Third Chest + Original Item: Red Rupee + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 6 + +- Name: Lake Lantern Cave Fourth Chest + Original Item: Arrows 10 + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 0 + +- Name: Lake Lantern Cave Fifth Chest + Original Item: Red Rupee + # HD Original Item: Stamp (J) + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 7 + +- Name: Lake Lantern Cave Sixth Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 8 + +- Name: Lake Lantern Cave Seventh Chest + Original Item: Bomblings 5 + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 14 + +- Name: Lake Lantern Cave Eighth Chest + Original Item: Red Rupee + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 10 + +- Name: Lake Lantern Cave Ninth Chest + Original Item: Arrows 10 + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 9 + +- Name: Lake Lantern Cave Tenth Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 5 + +- Name: Lake Lantern Cave Eleventh Chest + Original Item: Bomblings 10 + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 4 + +- Name: Lake Lantern Cave Twelfth Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 11 + +- Name: Lake Lantern Cave Thirteenth Chest + Original Item: Seeds 50 + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 12 + +- Name: Lake Lantern Cave Fourteenth Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (Treasure Chest) + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 13 + +- Name: Lake Lantern Cave End Lantern Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Lake Lantern Cave + - ARC + Metadata: + Chest: + - Stage: 33 + Tbox Id: 3 + +- Name: Lake Lantern Cave First Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Lake Lantern Cave + Metadata: + Poe: + - Stage: 33 + Flag: 0x5E + +- Name: Lake Lantern Cave Second Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Lake Lantern Cave + Metadata: + Poe: + - Stage: 33 + Flag: 0x5D + +- Name: Lake Lantern Cave Final Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Lake Lantern Cave + Metadata: + Poe: + - Stage: 33 + Flag: 0x5F + +- Name: Lake Hylia Underwater Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (W) + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 52 + Tbox Id: 5 + +- Name: Lake Hylia Left Underwater Boulder Rupee + Original Item: Green Rupee + Categories: + - Overworld + - Lake Hylia + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 52 + Flag: 0x9A + +- Name: Lake Hylia Left Underwater Pillar Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Lake Hylia + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 52 + Flag: 0x81 + +- Name: Lake Hylia Right Underwater Boulder Rupee + Original Item: Green Rupee + Categories: + - Overworld + - Lake Hylia + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 52 + Flag: 0x9B + +- Name: Lake Hylia Right Underwater Pillar Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Lake Hylia + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 52 + Flag: 0x80 + +- Name: Lake Hylia Alcove Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Lake Hylia + Metadata: + Poe: + - Stage: 52 + Flag: 0x46 + +- Name: Flight By Fowl Top Platform Reward + Original Item: Orange Rupee + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 52 + Tbox Id: 25 + +- Name: Flight By Fowl Second Platform Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 52 + Tbox Id: 0 + +- Name: Flight By Fowl Third Platform Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (Cucco) + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 52 + Tbox Id: 2 + +- Name: Flight By Fowl Fourth Platform Chest + Original Item: Red Rupee + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 52 + Tbox Id: 8 + +- Name: Flight By Fowl Fifth Platform Chest + Original Item: Yellow Rupee + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 52 + Tbox Id: 7 + +- Name: Isle of Riches Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Lake Hylia + Metadata: + Poe: + - Stage: 52 + Flag: 0x47 + +- Name: Flight By Fowl Ledge Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Lake Hylia + Metadata: + Poe: + - Stage: 52 + Flag: 0x4D + +- Name: Lake Hylia Shell Blade Grotto Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 39 + Tbox Id: 21 + +- Name: Lake Hylia Dock Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Lake Hylia + Metadata: + Poe: + - Stage: 52 + Flag: 0x4B + +- Name: Outside Lanayru Spring Left Statue Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 52 + Tbox Id: 1 + +- Name: Outside Lanayru Spring Right Statue Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Lake Hylia + - DZX + Metadata: + Chest: + - Stage: 52 + Tbox Id: 6 + +- Name: Lanayru Spring Back Room Lantern Chest + Original Item: Piece of Heart + Categories: + - Overworld + - Lake Hylia + - ARC + Metadata: + Chest: + - Stage: 52 + Tbox Id: 29 + +- Name: Lanayru Spring Back Room Left Chest + Original Item: Bombs 5 + Categories: + - Overworld + - Lake Hylia + - ARC + Metadata: + Chest: + - Stage: 52 + Tbox Id: 28 + +- Name: Lanayru Spring Back Room Right Chest + Original Item: Blue Rupee + Categories: + - Overworld + - Lake Hylia + - ARC + Metadata: + Chest: + - Stage: 52 + Tbox Id: 27 + +- Name: Lanayru Spring East Double Clawshot Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Lake Hylia + - ARC + Metadata: + Chest: + - Stage: 52 + Tbox Id: 4 + +- Name: Lanayru Spring West Double Clawshot Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Lake Hylia + - ARC + Metadata: + Chest: + - Stage: 52 + Tbox Id: 3 + +- Name: Lanayru Spring Underwater Left Chest + Original Item: Blue Rupee + Categories: + - Overworld + - Lake Hylia + - ARC + Metadata: + Chest: + - Tbox Id: 12 + Stage: 52 + +- Name: Lanayru Spring Underwater Right Chest + Original Item: Yellow Rupee + Categories: + - Overworld + - Lake Hylia + - ARC + Metadata: + Chest: + - Stage: 52 + Tbox Id: 10 + +- Name: Lanayru Spring Lower Underwater Boulder Rupee + Original Item: Red Rupee + Categories: + - Overworld + - Lake Hylia + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 52 + Flag: 0x8F + +- Name: Lanayru Spring Upper Underwater Boulder Rupee + Original Item: Red Rupee + Categories: + - Overworld + - Lake Hylia + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 52 + Flag: 0x90 + +- Name: Plumm Fruit Balloon Minigame + Original Item: Piece of Heart + Categories: + - Overworld + - Npc + - Lake Hylia + - ARC + Metadata: + Name Lookup: + - Plumm Fruit Balloon Minigame + Event Flag: 0x2380 + +- Name: Upper Zoras River Warp Portal + Original Item: Upper Zoras River Portal + Categories: + - Overworld + - Warp Portal + - Upper Zoras River + Metadata: + - None + +- Name: Upper Zoras River Female Dragonfly + Original Item: Female Dragonfly + Categories: + - Overworld + - Golden Bug + - Upper Zoras River + - DZX + Metadata: + Freestanding Item: + - Stage: 61 + Flag: 0x9E + +- Name: Upper Zoras River Central Underwater Boulder Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Upper Zoras River + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 61 + Flag: 0x91 + +- Name: Upper Zoras River East Underwater Boulder Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Upper Zoras River + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 61 + Flag: 0x93 + +- Name: Upper Zoras River Ledge Boulder Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Upper Zoras River + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 61 + Flag: 0x94 + +- Name: Upper Zoras River West Underwater Boulder Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Upper Zoras River + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 61 + Flag: 0x92 + +- Name: Upper Zoras River Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Upper Zoras River + Metadata: + Poe: + - Stage: 61 + Flag: 0x48 + +- Name: Iza Helping Hand + Original Item: Bomb Bag + Categories: + - Overworld + - Npc + - Upper Zoras River + - ARC + Metadata: + Name Lookup: + - Iza Helping Hand + Event Flag: 0x0B01 + +- Name: Iza Raging Rapids Minigame + Original Item: Giant Bomb Bag + Categories: + - Overworld + - Npc + - Upper Zoras River + - ARC + Metadata: + Name Lookup: + - Iza Raging Rapids Minigame + Event Flag: 0x5908 + +- Name: Fishing Hole Bottle + Original Item: Empty Bottle + Categories: + - Overworld + - Fishing Hole + - ARC + - REL + Metadata: + FLW Message: + - Group: 0 + Message Id: 1822 + Event Flag: 0x3908 + +- Name: Fishing Hole Heart Piece + Original Item: Piece of Heart + Categories: + - Overworld + - Fishing Hole + - ARC + - REL + Metadata: + # Can be picked up as a freestanding item with clawshot + Freestanding Item: + - Stage: 62 + Flag: 0x80 + # Or picked up with the fishing rod during fishing + FLW Message: + - Group: 7 + Message Id: 7564 + - Group: 7 + Message Id: 7578 + + +- Name: Zoras Domain Warp Portal + Original Item: Zoras Domain Portal + Categories: + - Overworld + - Warp Portal + - Zoras Domain + Metadata: + - None + +- Name: Zoras Domain Male Dragonfly + Original Item: Male Dragonfly + Categories: + - Overworld + - Golden Bug + - Zoras Domain + - DZX + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x9F + +- Name: Zoras Domain Mother and Child Isle Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Zoras Domain + Metadata: + Poe: + - Stage: 50 + Flag: 0x4A + +- Name: Zoras Domain Chest Behind Waterfall + Original Item: Red Rupee + Categories: + - Overworld + - Zoras Domain + - ARC + Metadata: + Chest: + - Stage: 50 + Tbox Id: 26 + +- Name: Zoras Domain Chest By Mother and Child Isles + Original Item: Yellow Rupee + Categories: + - Overworld + - Zoras Domain + - ARC + Metadata: + Chest: + - Stage: 50 + Tbox Id: 30 + +- Name: Zoras Domain Waterfall Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Zoras Domain + Metadata: + Poe: + - Stage: 50 + Flag: 0x49 + +- Name: Zoras Domain Behind Waterfall Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x96 + +- Name: Zoras Domain Central Underwater Boulder Rupee + Original Item: Red Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x8E + +- Name: Zoras Domain North Underwater Boulder Rupee + Original Item: Red Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x8D + +- Name: Zoras Domain Shortcut Ledge Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x84 + +- Name: Zoras Domain Shortcut Lower Boulder Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x8C + +- Name: Zoras Domain Shortcut Upper Boulder Rupee + Original Item: Green Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Hidden + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x8B + + +- Name: Zoras Domain Top Ledge Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x83 + +- Name: Zoras Domain Vine Ledge Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x97 + +- Name: Zoras Domain Waterfall Ledge Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x95 + +- Name: Zoras Domain Extinguish All Torches Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Zoras Domain + - ARC + Metadata: + Chest: + - Stage: 50 + Tbox Id: 20 + +- Name: Zoras Domain Light All Torches Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Zoras Domain + - ARC + Metadata: + Chest: + - Stage: 50 + Tbox Id: 19 + +- Name: Zoras Domain Underwater Goron + Original Item: Bomb Bag + Categories: + - Overworld + - Npc + - Zoras Domain + - ARC + Metadata: + Name Lookup: + - Zoras Domain Underwater Goron + Event Flag: 0x3D10 + +- Name: Zoras Domain Throne East Gate Underwater Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x8A + +- Name: Zoras Domain Throne East Underwater Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x86 + +- Name: Zoras Domain Throne Northwest Underwater Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x87 + +- Name: Zoras Domain Throne South Underwater Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x88 + +- Name: Zoras Domain Throne West Gate Underwater Rupee + Original Item: Blue Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x89 + +- Name: Zoras Domain Throne West Underwater Rupee + Original Item: Yellow Rupee + Categories: + - Overworld + - Zoras Domain + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 50 + Flag: 0x85 + +# SNOWPEAK PROVINCE + +- Name: Snowpeak Warp Portal + Original Item: Snowpeak Portal + Categories: + - Overworld + - Warp Portal + - Snowpeak Province + Metadata: + - None + +- Name: Ashei Sketch + Original Item: Asheis Sketch + Categories: + - Overworld + - Npc + - Snowpeak Province + - ARC + Metadata: + Name Lookup: + - Ashei Sketch + Event Flag: 0x2940 + +- Name: Snowpeak Blizzard Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Snowpeak Province + Metadata: + Poe: + - Stage: 51 + Flag: 0x7D + +- Name: Snowpeak Above Freezard Grotto Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Snowpeak Province + Metadata: + Poe: + - Stage: 51 + Flag: 0x7C + +- Name: Snowpeak Poe Among Trees + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Snowpeak Province + Metadata: + Poe: + - Stage: 51 + Flag: 0x7B + +- Name: Snowpeak Freezard Grotto Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Snowpeak Province + - DZX + Metadata: + Chest: + - Stage: 38 + Tbox Id: 15 + +- Name: Snowpeak Cave Ice Lantern Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Snowpeak Province + - ARC + Metadata: + Chest: + - Stage: 51 + Tbox Id: 0 + +- Name: Snowpeak Cave Ice Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Snowpeak Province + Metadata: + Poe: + - Stage: 51 + Flag: 0x7F + +- Name: Snowpeak Icy Summit Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Snowpeak Province + Metadata: + Poe: + - Stage: 51 + Flag: 0x7E + +- Name: Snowboard Racing Prize + Original Item: Piece of Heart + Categories: + - Overworld + - Npc + - Snowpeak Province + - ARC + Metadata: + Name Lookup: + - Snowboard Racing Prize + Event Flag: 0x3B10 + +- Name: Snowboarding Bridge Ledge Bottom Rupee + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x84 + +- Name: Snowboarding Bridge Ledge Middle Rupee + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x83 + +- Name: Snowboarding Bridge Ledge Upper Rupee + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x82 + +- Name: Snowboarding Shortcut Rupee 1 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x88 + +- Name: Snowboarding Shortcut Rupee 2 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x89 + +- Name: Snowboarding Shortcut Rupee 3 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x8A + +- Name: Snowboarding Shortcut Rupee 4 + Original Item: Red Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x8B + +- Name: Snowboarding Shortcut Rupee 5 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x8C + +- Name: Snowboarding Shortcut Rupee 6 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x8D + +- Name: Snowboarding Shortcut Rupee 7 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x8E + +- Name: Snowboarding Shortcut Rupee 8 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x8F + +- Name: Snowboarding Shortcut Rupee 9 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x90 + +- Name: Snowboarding Shortcut Rupee 10 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x91 + +- Name: Snowboarding Shortcut Rupee 11 + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x92 + +- Name: Snowboarding Snowy Tree Top Rupee 1 + Original Item: Blue Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x85 + +- Name: Snowboarding Snowy Tree Top Rupee 2 + Original Item: Red Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x86 + +- Name: Snowboarding Snowy Tree Top Rupee 3 + Original Item: Purple Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x87 + +- Name: Snowboarding Top Left Rupee + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x80 + +- Name: Snowboarding Top Right Rupee + Original Item: Green Rupee + Categories: + - Overworld + - Snowpeak + - Rupee - Freestanding + Metadata: + Freestanding Item: + - Stage: 51 + Flag: 0x81 + +# DESERT PROVINCE + +- Name: Gerudo Desert Warp Portal + Original Item: Gerudo Desert Portal + Categories: + - Overworld + - Warp Portal + - Gerudo Desert + Metadata: + - None + +- Name: Gerudo Desert East Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Gerudo Desert + Metadata: + Poe: + - Stage: 59 + Flag: 0x5C + +- Name: Gerudo Desert Skulltula Grotto Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Gerudo Desert + - DZX + Metadata: + Chest: + - Stage: 38 + Tbox Id: 8 + +- Name: Gerudo Desert Peahat Ledge Chest + Original Item: Red Rupee + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 11 + +- Name: Gerudo Desert East Canyon Chest + Original Item: Red Rupee + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 6 + +- Name: Gerudo Desert South Chest Behind Wooden Gates + Original Item: Orange Rupee + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 0 + +- Name: Gerudo Desert Lone Small Chest + Original Item: Arrows 10 + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 5 + +- Name: Gerudo Desert Male Dayfly + Original Item: Male Dayfly + Categories: + - Overworld + - Golden Bug + - Gerudo Desert + - DZX + Metadata: + Freestanding Item: + - Stage: 59 + Flag: 0x99 + +- Name: Gerudo Desert Female Dayfly + Original Item: Female Dayfly + Categories: + - Overworld + - Golden Bug + - Gerudo Desert + - DZX + Metadata: + Freestanding Item: + - Stage: 59 + Flag: 0x98 + +- Name: Gerudo Desert Owl Statue Chest + Original Item: Orange Rupee + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 1 + +- Name: Gerudo Desert Owl Statue Sky Character + Original Item: Progressive Sky Book + Categories: + - Overworld + - Gerudo Desert + Metadata: + Sky Character: + - Stage: 59 + Room: 0 + Event Flag: 0x6040 + +- Name: Gerudo Desert Poe Above Cave of Ordeals + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Gerudo Desert + Metadata: + Poe: + - Stage: 59 + Flag: 0x5D + +- Name: Gerudo Desert West Canyon Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (R) + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 7 + +- Name: Gerudo Desert North Peahat Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Gerudo Desert + Metadata: + Poe: + - Stage: 59 + Flag: 0x5B + +- Name: Gerudo Desert Rock Grotto First Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Gerudo Desert + Metadata: + Poe: + - Stage: 37 + Flag: 0x0F + +- Name: Gerudo Desert Rock Grotto Second Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Gerudo Desert + Metadata: + Poe: + - Stage: 37 + Flag: 0x10 + +- Name: Gerudo Desert Rock Grotto Lantern Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (Sad Midna) + Categories: + - Overworld + - Gerudo Desert + - DZX + Metadata: + Chest: + - Stage: 37 + Tbox Id: 14 + +- Name: Gerudo Desert Campfire East Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 9 + +- Name: Gerudo Desert Campfire North Chest + Original Item: Red Rupee + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 8 + +- Name: Gerudo Desert Campfire West Chest + Original Item: Arrows 10 + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 10 + +- Name: Gerudo Desert Northeast Chest Behind Gates + Original Item: Red Rupee + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 2 + +- Name: Gerudo Desert Northwest Chest Behind Gates + Original Item: Red Rupee + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 3 + +- Name: Gerudo Desert North Small Chest Before Bulblin Camp + Original Item: Arrows 10 + Categories: + - Overworld + - Gerudo Desert + - ARC + Metadata: + Chest: + - Stage: 59 + Tbox Id: 13 + +- Name: Gerudo Desert Golden Wolf + Original Item: Progressive Hidden Skill + Categories: + - Overworld + - Gerudo Desert + Metadata: + Golden Wolf: + - Flag: 0x3C01 + +- Name: Outside Bulblin Camp Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Gerudo Desert + Metadata: + Poe: + - Stage: 59 + Flag: 0x33 + +- Name: Bulblin Camp First Chest Under Tower At Entrance + Original Item: Arrows 20 + Categories: + - Overworld + - Bulblin Camp + - DZX + Metadata: + Chest: + - Stage: 55 + Tbox Id: 31 + +# In vanilla this chest shares a flag with the other small chest +# in the area. So we change this one to use a different flag +- Name: Bulblin Camp Small Chest in Back of Camp + Original Item: Purple Rupee + Categories: + - Overworld + - Bulblin Camp + - DZX + Metadata: + Chest: + - Stage: 55 + Tbox Id: 30 + +- Name: Bulblin Camp Roasted Boar + Original Item: Piece of Heart + Categories: + - Overworld + - Bulblin Camp + - Boss + Metadata: + Freestanding Item: + - Stage: 55 + Flag: 0x9F + +- Name: Bulblin Guard Key + Original Item: Gerudo Desert Bulblin Camp Key + Categories: + - Overworld + - Bulblin Camp + - Small Key + - DZX + Metadata: + Freestanding Item: + - Stage: 55 + Flag: 0x9A + +- Name: Bulblin Camp Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Bulblin Camp + Metadata: + Poe: + - Stage: 55 + Flag: 0x78 + +- Name: Outside Arbiters Grounds Lantern Chest + Original Item: Purple Rupee + Categories: + - Overworld + - Bulblin Camp + - ARC + Metadata: + Chest: + - Stage: 55 + Tbox Id: 15 + +- Name: Outside Arbiters Grounds Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Bulblin Camp + Metadata: + Poe: + - Stage: 55 + Flag: 0x5A + +# HD Only Location +# - Name: Cave of Ordeals Floor 10 Chest +# Original Item: Stamp (Surprised Link) +# Categories: +# - Overworld +# # - Cave of Ordeals + +# HD Only Location +# - Name: Cave of Ordeals Floor 20 Chest +# Original Item: Stamp (Angry Zelda) +# Categories: +# - Overworld +# # - Cave of Ordeals + +# HD Only Location +# - Name: Cave of Ordeals Floor 30 Chest +# Original Item: Stamp (Happy Midna) +# Categories: +# - Overworld +# # - Cave of Ordeals + +# HD Only Location +# - Name: Cave of Ordeals Floor 40 Chest +# Original Item: Stamp (Heart Container) +# Categories: +# - Overworld +# # - Cave of Ordeals + +# HD Only Location +# - Name: Cave of Ordeals Floor 50 Chest +# Original Item: Stamp (Fairy) +# Categories: +# - Overworld +# # - Cave of Ordeals + +- Name: Cave of Ordeals Floor 17 Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Cave of Ordeals + Metadata: + Poe: + - Stage: 31 + Flag: 0x45 + +- Name: Cave of Ordeals Floor 33 Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Cave of Ordeals + Metadata: + Poe: + - Stage: 31 + Flag: 0x46 + +- Name: Cave of Ordeals Floor 44 Poe + Original Item: Poe Soul + Categories: + - Overworld + - Poe + - Cave of Ordeals + Metadata: + Poe: + - Stage: 31 + Flag: 0x47 + +- Name: Cave of Ordeals Great Fairy Reward + Original Item: Fairy Tears + Categories: + - Overworld + - Npc + - Cave of Ordeals + - ARC + Metadata: + Name Lookup: + - Cave of Ordeals Great Fairy Reward + Event Flag: 0x3E40 + +- Name: Mirror Chamber Warp Portal + Original Item: Mirror Chamber Portal + Categories: + - Overworld + - Warp Portal + - Mirror Chamber + Metadata: + - None + +# FOREST TEMPLE + +- Name: Forest Temple Entrance Vines Chest + Original Item: Yellow Rupee + Categories: + - Dungeon + - Forest Temple + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 7 + +- Name: Forest Temple Central Chest Hanging From Web + Original Item: Forest Temple Compass + Categories: + - Dungeon + - Forest Temple + - Dungeon Items + - Compass + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 20 + +- Name: Forest Temple Central Chest Behind Stairs + Original Item: Red Rupee + Categories: + - Dungeon + - Forest Temple + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 37 + +- Name: Forest Temple Central North Chest + Original Item: Forest Temple Dungeon Map + Categories: + - Dungeon + - Forest Temple + - Dungeon Items + - Dungeon Map + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 39 + +- Name: Forest Temple West Deku Like Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Forest Temple + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 31 + +- Name: Forest Temple Big Baba Key + Original Item: Forest Temple Small Key + Categories: + - Small Key + - Dungeon + - Forest Temple + - REL + Metadata: + Freestanding Item: + - Stage: 6 + Flag: 0x6 # Technically a tbox id + +- Name: Forest Temple Totem Pole Chest + Original Item: Forest Temple Small Key + Categories: + - Dungeon + - Forest Temple + - Small Key + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 25 + +- Name: Forest Temple West Tile Worm Chest Behind Stairs + Original Item: Piece of Heart + Categories: + - Dungeon + - Forest Temple + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 19 + +- Name: Forest Temple West Tile Worm Room Vines Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Forest Temple + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 24 + +- Name: Forest Temple Gale Boomerang + Original Item: Gale Boomerang + Categories: + - Forest Temple + - Dungeon + - Boss + Metadata: + Name Lookup: + - Forest Temple Gale Boomerang + Item Flag: + Stage: 8 + Flag: 0x9D + +- Name: Forest Temple Big Key Chest + Original Item: Forest Temple Big Key + Categories: + - Dungeon + - Forest Temple + - Big Key + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 38 + +- Name: Forest Temple East Water Cave Chest + Original Item: Yellow Rupee + # HD Original Item: Stamp (A) + Categories: + - Dungeon + - Forest Temple + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 26 + +- Name: Forest Temple Second Monkey Under Bridge Chest + Original Item: Yellow Rupee + Categories: + - Dungeon + - Forest Temple + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 9 + +- Name: Forest Temple Windless Bridge Chest + Original Item: Forest Temple Small Key + Categories: + - Dungeon + - Forest Temple + - Small Key + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 4 + +- Name: Forest Temple East Tile Worm Chest + Original Item: Red Rupee + # HD Original Item: Stamp (N) + Categories: + - Dungeon + - Forest Temple + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 1 + +- Name: Forest Temple North Deku Like Chest + Original Item: Forest Temple Small Key + Categories: + - Dungeon + - Forest Temple + - Small Key + - ARC + Metadata: + Chest: + - Stage: 6 + Tbox Id: 2 + +- Name: Forest Temple Diababa Heart Container + Original Item: Heart Container + Categories: + - Heart Container + - Dungeon + - Forest Temple + - Boss + Metadata: + Freestanding Item: + - Stage: 7 + Flag: 0x9F + +- Name: Forest Temple Dungeon Reward + Original Item: Progressive Fused Shadow + Categories: + - Dungeon + - Forest Temple + - Dungeon Reward + - REL + - ARC + Goal Location: True + Metadata: + FLW Message: + - Group: 5 + Message Id: 5001 + Item Flag: + Stage: 7 + Flag: 0x9E + +# GORON MINES + +- Name: Goron Mines Entrance Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 27 + +- Name: Goron Mines Main Magnet Room Bottom Chest + Original Item: Goron Mines Small Key + Categories: + - Dungeon + - Goron Mines + - Small Key + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 11 + +- Name: Goron Mines Gor Amato Key Shard + Original Item: Goron Mines Key Shard + Categories: + - Npc + - Dungeon + - Goron Mines + - Dungeon Items + - Big Key + - ARC + Metadata: + Name Lookup: + - Goron Mines Gor Amato Key Shard + Event Flag: 0x3008 # late + +- Name: Goron Mines Gor Amato Chest + Original Item: Goron Mines Dungeon Map + Categories: + - Dungeon + - Goron Mines + - Dungeon Items + - Dungeon Map + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 17 + +- Name: Goron Mines Gor Amato Small Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 24 + +- Name: Goron Mines Magnet Maze Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 20 + +- Name: Goron Mines Crystal Switch Room Underwater Chest + Original Item: Goron Mines Small Key + Categories: + - Dungeon + - Goron Mines + - Small Key + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 16 + +- Name: Goron Mines Crystal Switch Room Small Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 28 + +- Name: Goron Mines After Crystal Switch Room Magnet Wall Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 9 + +- Name: Goron Mines Outside Beamos Chest + Original Item: Goron Mines Small Key + Categories: + - Dungeon + - Goron Mines + - Small Key + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 15 + +- Name: Goron Mines Outside Underwater Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (H) + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 21 + +- Name: Goron Mines Outside Clawshot Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (U) + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 14 + +- Name: Goron Mines Gor Ebizo Key Shard + Original Item: Goron Mines Key Shard + Categories: + - Npc + - Dungeon + - Goron Mines + - Dungeon Items + - Big Key + - ARC + Metadata: + Name Lookup: + - Goron Mines Gor Ebizo Key Shard + Event Flag: 0x3702 # late + +- Name: Goron Mines Gor Ebizo Chest + Original Item: Yellow Rupee + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 25 + +- Name: Goron Mines Chest Before Dangoro + Original Item: Yellow Rupee + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 13 + +- Name: Goron Mines Dangoro Chest + Original Item: Progressive Bow + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 6 + +- Name: Goron Mines Beamos Room Chest + Original Item: Goron Mines Compass + Categories: + - Dungeon + - Goron Mines + - Dungeon Items + - Compass + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 12 + +- Name: Goron Mines Gor Liggs Key Shard + Original Item: Goron Mines Key Shard + Categories: + - Npc + - Dungeon + - Goron Mines + - Dungeon Items + - Big Key + - ARC + Metadata: + Name Lookup: + - Goron Mines Gor Liggs Key Shard + Event Flag: 0x3701 # late + +- Name: Goron Mines Gor Liggs Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 26 + +- Name: Goron Mines Main Magnet Room Top Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - Goron Mines + - ARC + Metadata: + Chest: + - Stage: 3 + Tbox Id: 30 + +- Name: Goron Mines Fyrus Heart Container + Original Item: Heart Container + Categories: + - Heart Container + - Dungeon + - Goron Mines + - Boss + Metadata: + Freestanding Item: + - Stage: 4 + Flag: 0x9F + +- Name: Goron Mines Dungeon Reward + Original Item: Progressive Fused Shadow + Categories: + - Dungeon + - Goron Mines + - Dungeon Reward + - REL + - ARC + Goal Location: True + Metadata: + FLW Message: + - Group: 5 + Message Id: 6011 + Event Flag: 0x0701 + +# LAKEBED TEMPLE + +- Name: Lakebed Temple Lobby Left Chest + Original Item: Arrows 20 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 28 + +- Name: Lakebed Temple Lobby Rear Chest + Original Item: Water Bombs 10 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 29 + +- Name: Lakebed Temple Stalactite Room Chest + Original Item: Water Bombs 10 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 3 + +- Name: Lakebed Temple Chandelier Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 5 + +- Name: Lakebed Temple Central Room Spire Chest + Original Item: Red Rupee + # HD Original Item: Stamp (K) + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 16 + +- Name: Lakebed Temple Central Room Chest + Original Item: Lakebed Temple Dungeon Map + Categories: + - Dungeon + - Lakebed Temple + - Dungeon Items + - Dungeon Map + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 17 + +- Name: Lakebed Temple Central Room Small Chest + Original Item: Arrows 20 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 1 + +- Name: Lakebed Temple East Lower Waterwheel Stalactite Chest + Original Item: Lakebed Temple Small Key + Categories: + - Dungeon + - Lakebed Temple + - Small Key + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 6 + +- Name: Lakebed Temple East Lower Waterwheel Bridge Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 8 + +- Name: Lakebed Temple East Second Floor Southeast Chest + Original Item: Lakebed Temple Small Key + Categories: + - Dungeon + - Lakebed Temple + - Small Key + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 10 + +- Name: Lakebed Temple East Second Floor Southwest Chest + Original Item: Water Bombs 5 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 24 + +- Name: Lakebed Temple East Water Supply Small Chest + Original Item: Water Bombs 10 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 27 + +- Name: Lakebed Temple East Water Supply Clawshot Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (Y) + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 11 + +- Name: Lakebed Temple Before Deku Toad Alcove Chest + Original Item: Lakebed Temple Small Key + Categories: + - Dungeon + - Lakebed Temple + - Small Key + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 2 + +- Name: Lakebed Temple Before Deku Toad Underwater Left Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 15 + +- Name: Lakebed Temple Before Deku Toad Underwater Right Chest + Original Item: Water Bombs 5 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 14 + +- Name: Lakebed Temple Deku Toad Chest + Original Item: Progressive Clawshot + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 2 + Tbox Id: 0 + +- Name: Lakebed Temple West Lower Small Chest + Original Item: Water Bombs 10 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 30 + +- Name: Lakebed Temple West Second Floor Central Small Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 19 + +- Name: Lakebed Temple West Second Floor Northeast Chest + Original Item: Water Bombs 15 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 13 + +- Name: Lakebed Temple West Second Floor Southeast Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 7 + +- Name: Lakebed Temple West Second Floor Southwest Underwater Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 25 + +- Name: Lakebed Temple West Water Supply Small Chest + Original Item: Water Bombs 10 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 21 + +- Name: Lakebed Temple West Water Supply Chest + Original Item: Lakebed Temple Compass + Categories: + - Dungeon + - Lakebed Temple + - Dungeon Items + - Compass + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 20 + +- Name: Lakebed Temple Underwater Maze Small Chest + Original Item: Water Bombs 5 + Categories: + - Dungeon + - Lakebed Temple + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 18 + +- Name: Lakebed Temple Big Key Chest + Original Item: Lakebed Temple Big Key + Categories: + - Dungeon + - Lakebed Temple + - Big Key + - ARC + Metadata: + Chest: + - Stage: 0 + Tbox Id: 12 + +- Name: Lakebed Temple Morpheel Heart Container + Original Item: Heart Container + Categories: + - Heart Container + - Dungeon + - Lakebed Temple + - Boss + Metadata: + Freestanding Item: + - Stage: 1 + Flag: 0x9F + +- Name: Lakebed Temple Dungeon Reward + Original Item: Progressive Fused Shadow + Categories: + - Dungeon + - Lakebed Temple + - Dungeon Reward + - REL + - ARC + Goal Location: True + Metadata: + FLW Message: + - Group: 5 + Message Id: 7001 + Event Flag: 0x0904 # late + +# ARBITERS GROUNDS + +- Name: Arbiters Grounds Entrance Chest + Original Item: Arbiters Grounds Small Key + Categories: + - Dungeon + - Arbiters Grounds + - Small Key + Metadata: + Chest: + - Stage: 24 + Tbox Id: 23 + +- Name: Arbiters Grounds Torch Room Poe + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - Arbiters Grounds + Metadata: + Poe: + - Stage: 24 + Flag: 0x1E + +- Name: Arbiters Grounds Torch Room East Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 19 + +- Name: Arbiters Grounds Torch Room West Chest + Original Item: Arbiters Grounds Dungeon Map + Categories: + - Dungeon + - Arbiters Grounds + - Dungeon Items + - Dungeon Map + Metadata: + Chest: + - Stage: 24 + Tbox Id: 18 + +- Name: Arbiters Grounds West Small Chest Behind Block + Original Item: Red Rupee + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 25 + +- Name: Arbiters Grounds East Lower Turnable Redead Chest + Original Item: Arbiters Grounds Small Key + Categories: + - Dungeon + - Arbiters Grounds + - Small Key + Metadata: + Chest: + - Stage: 24 + Tbox Id: 6 + +- Name: Arbiters Grounds East Turning Room Poe + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - Arbiters Grounds + Metadata: + Poe: + - Stage: 24 + Flag: 0x1F + +- Name: Arbiters Grounds East Upper Turnable Chest + Original Item: Arbiters Grounds Compass + Categories: + - Dungeon + - Arbiters Grounds + - Dungeon Items + - Compass + Metadata: + Chest: + - Stage: 24 + Tbox Id: 4 + +- Name: Arbiters Grounds East Upper Turnable Redead Chest + Original Item: Arbiters Grounds Small Key + Categories: + - Dungeon + - Arbiters Grounds + - Small Key + Metadata: + Chest: + - Stage: 24 + Tbox Id: 5 + +- Name: Arbiters Grounds Hidden Wall Poe + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - Arbiters Grounds + Metadata: + Poe: + - Stage: 24 + Flag: 0x20 + +- Name: Arbiters Grounds Ghoul Rat Room Chest + Original Item: Arbiters Grounds Small Key + Categories: + - Dungeon + - Arbiters Grounds + - Small Key + Metadata: + Chest: + - Stage: 24 + Tbox Id: 21 + +- Name: Arbiters Grounds West Chandelier Chest + Original Item: Red Rupee + # HD Original Item: Stamp (Surprised Midna) + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 3 + +- Name: Arbiters Grounds West Stalfos Northeast Chest + Original Item: Bombs 5 + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 16 + +- Name: Arbiters Grounds West Stalfos West Chest + Original Item: Bombs 5 + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 17 + +- Name: Arbiters Grounds West Poe + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - Arbiters Grounds + Metadata: + Poe: + - Stage: 24 + Flag: 0x21 + +- Name: Arbiters Grounds North Turning Room Chest + Original Item: Arbiters Grounds Small Key + Categories: + - Dungeon + - Arbiters Grounds + - Small Key + Metadata: + Chest: + - Stage: 24 + Tbox Id: 24 + +- Name: Arbiters Grounds Death Sword Chest + Original Item: Spinner + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 26 + Tbox Id: 11 + +- Name: Arbiters Grounds Spinner Room First Small Chest + Original Item: Bombs 10 + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 28 + +- Name: Arbiters Grounds Spinner Room Second Small Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 27 + +- Name: Arbiters Grounds Spinner Room Lower Central Small Chest + Original Item: Yellow Rupee + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 29 + +- Name: Arbiters Grounds Spinner Room Stalfos Alcove Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 26 + +- Name: Arbiters Grounds Spinner Room Lower North Chest + Original Item: Yellow Rupee + # HD Original Item: Stamp (D) + Categories: + - Dungeon + - Arbiters Grounds + Metadata: + Chest: + - Stage: 24 + Tbox Id: 30 + +- Name: Arbiters Grounds Big Key Chest + Original Item: Arbiters Grounds Big Key + Categories: + - Dungeon + - Arbiters Grounds + - Big Key + Metadata: + Chest: + - Stage: 24 + Tbox Id: 20 + +- Name: Arbiters Grounds Stallord Heart Container + Original Item: Heart Container + Categories: + - Heart Container + - Arbiters Grounds + - Dungeon + - Boss + Metadata: + Freestanding Item: + - Stage: 25 + Flag: 0x9F + +- Name: Arbiters Grounds Dungeon Reward + Original Item: Progressive Mirror Shard + Categories: + - Dungeon + - Arbiters Grounds + - Dungeon Reward + Goal Location: True + Metadata: + Name Lookup: + - Arbiters Grounds Dungeon Reward + Item Flag: + Stage: 25 + Flag: 0x9E + +# SNOWPEAK RUINS + +- Name: Snowpeak Ruins Lobby West Armor Chest + Original Item: Red Rupee + # HD Original Item: Stamp (Q) + Categories: + - Dungeon + - Snowpeak Ruins + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 23 + +- Name: Snowpeak Ruins Lobby East Armor Chest + Original Item: Yellow Rupee + Categories: + - Dungeon + - Snowpeak Ruins + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 22 + +- Name: Snowpeak Ruins Lobby Armor Poe + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - Snowpeak Ruins + Metadata: + Poe: + - Stage: 27 + Flag: 0x15 + +- Name: Snowpeak Ruins Lobby Poe + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - Snowpeak Ruins + Metadata: + Poe: + - Stage: 27 + Flag: 0x72 + +- Name: Snowpeak Ruins Lobby Chandelier Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Snowpeak Ruins + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 21 + +- Name: Snowpeak Ruins Mansion Map + Original Item: Snowpeak Ruins Dungeon Map + Categories: + - Npc + - Dungeon + - Snowpeak Ruins + - Dungeon Items + - Dungeon Map + - ARC + Metadata: + Name Lookup: + - Snowpeak Ruins Mansion Map + Event Flag: 0x0B10 # late + +- Name: Snowpeak Ruins East Courtyard Chest + Original Item: Snowpeak Ruins Small Key + Categories: + - Dungeon + - Snowpeak Ruins + - Small Key + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 15 + +- Name: Snowpeak Ruins East Courtyard Buried Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Snowpeak Ruins + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 14 + +- Name: Snowpeak Ruins Ordon Pumpkin Chest + Original Item: Ordon Pumpkin + Categories: + - Dungeon + - Snowpeak Ruins + - Dungeon Items + - Ordon Pumpkin + - Small Key + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 10 + +- Name: Snowpeak Ruins Courtyard Central Chest + Original Item: Bombs 5 + Categories: + - Dungeon + - Snowpeak Ruins + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 3 + +- Name: Snowpeak Ruins West Courtyard Buried Chest + Original Item: Snowpeak Ruins Small Key + Categories: + - Dungeon + - Snowpeak Ruins + - Small Key + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 13 + +- Name: Snowpeak Ruins West Cannon Room Central Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Snowpeak Ruins + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 20 + +- Name: Snowpeak Ruins West Cannon Room Corner Chest + Original Item: Bombs 5 + Categories: + - Dungeon + - Snowpeak Ruins + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 19 + +- Name: Snowpeak Ruins Wooden Beam Central Chest + Original Item: Red Rupee + # HD Original Item: Stamp (B) + Categories: + - Dungeon + - Snowpeak Ruins + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 1 + +- Name: Snowpeak Ruins Wooden Beam Chandelier Chest + Original Item: Snowpeak Ruins Small Key + Categories: + - Dungeon + - Snowpeak Ruins + - Small Key + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 7 + +- Name: Snowpeak Ruins Wooden Beam Northwest Chest + Original Item: Snowpeak Ruins Compass + Categories: + - Dungeon + - Snowpeak Ruins + - Dungeon Items + - Compass + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 2 + +- Name: Snowpeak Ruins Ball and Chain + Original Item: Ball and Chain + Categories: + - Dungeon + - Snowpeak Ruins + - REL + Metadata: + Name Lookup: + - Snowpeak Ruins Ball and Chain + Switch Flag: + Stage: 29 + Flag: 0x5F + +- Name: Snowpeak Ruins Chest After Darkhammer + Original Item: Ordon Cheese + Categories: + - Dungeon + - Snowpeak Ruins + - Small Key + - ARC + Metadata: + Chest: + - Stage: 29 + Tbox Id: 6 + +- Name: Snowpeak Ruins Broken Floor Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Snowpeak Ruins + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 25 + +- Name: Snowpeak Ruins Ice Room Poe + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - Snowpeak Ruins + Metadata: + Poe: + - Stage: 27 + Flag: 0x7F + +- Name: Snowpeak Ruins Northeast Chandelier Chest + Original Item: Snowpeak Ruins Small Key + Categories: + - Dungeon + - Snowpeak Ruins + - Small Key + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 18 + +- Name: Snowpeak Ruins Chapel Chest + Original Item: Snowpeak Ruins Bedroom Key + Categories: + - Dungeon + - Snowpeak Ruins + - Big Key + - ARC + Metadata: + Chest: + - Stage: 27 + Tbox Id: 11 + +- Name: Snowpeak Ruins Blizzeta Heart Container + Original Item: Heart Container + Categories: + - Heart Container + - Dungeon + - Snowpeak Ruins + - Boss + Metadata: + Freestanding Item: + - Stage: 28 + Flag: 0x9F + +- Name: Snowpeak Ruins Dungeon Reward + Original Item: Progressive Mirror Shard + Categories: + - Dungeon + - Snowpeak Ruins + - Dungeon Reward + - REL + - ARC + Goal Location: True + Metadata: + FLW Message: + - Group: 5 + Message Id: 9301 + Event Flag: 0x2008 + +- Name: Temple of Time Lobby Lantern Chest + Original Item: Temple of Time Small Key + Categories: + - Dungeon + - Temple of Time + - Small Key + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 41 + +- Name: Temple of Time First Staircase Gohma Gate Chest + Original Item: Arrows 30 + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 2 + +- Name: Temple of Time First Staircase Window Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 45 + +- Name: Temple of Time First Staircase Armos Chest + Original Item: Temple of Time Dungeon Map + Categories: + - Dungeon + - Temple of Time + - Dungeon Items + - Dungeon Map + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 1 + +- Name: Temple of Time Poe Behind Gate + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - Temple of Time + Metadata: + Poe: + - Stage: 9 + Flag: 0x19 + +- Name: Temple of Time Armos Antechamber East Chest + Original Item: Temple of Time Small Key + Categories: + - Dungeon + - Temple of Time + - Small Key + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 4 + +- Name: Temple of Time Armos Antechamber North Chest + Original Item: Red Rupee + # HD Original Item: Stamp (L) + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 46 + +- Name: Temple of Time Armos Antechamber Statue Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 5 + +- Name: Temple of Time Moving Wall Beamos Room Chest + Original Item: Temple of Time Compass + Categories: + - Dungeon + - Temple of Time + - Dungeon Items + - Compass + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 6 + +- Name: Temple of Time Moving Wall Dinalfos Room Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 12 + +- Name: Temple of Time Scales Gohma Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 44 + +- Name: Temple of Time Scales Upper Chest + Original Item: Red Rupee + # HD Original Item: Stamp (T) + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 47 + +- Name: Temple of Time Poe Above Scales + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - Temple of Time + Metadata: + Poe: + - Stage: 9 + Flag: 0x18 + +- Name: Temple of Time Floor Switch Puzzle Room Upper Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 48 + +- Name: Temple of Time Big Key Chest + Original Item: Temple of Time Big Key + Categories: + - Dungeon + - Temple of Time + - Big Key + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 8 + +- Name: Temple of Time Gilloutine Chest + Original Item: Temple of Time Small Key + Categories: + - Dungeon + - Temple of Time + - Small Key + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 9 + +- Name: Temple of Time Chest Before Darknut + Original Item: Purple Rupee + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 9 + Tbox Id: 43 + +- Name: Temple of Time Darknut Chest + Original Item: Progressive Dominion Rod + Categories: + - Dungeon + - Temple of Time + - ARC + Metadata: + Chest: + - Stage: 11 + Tbox Id: 0 + +- Name: Temple of Time Armogohma Heart Container + Original Item: Heart Container + Categories: + - Heart Container + - Dungeon + - Temple of Time + - Boss + Metadata: + Freestanding Item: + - Stage: 10 + Flag: 0x9F + +- Name: Temple of Time Dungeon Reward + Original Item: Progressive Mirror Shard + Categories: + - Dungeon + - Temple of Time + - Dungeon Reward + - REL + - ARC + Goal Location: True + Metadata: + FLW Message: + - Group: 5 + Message Id: 9401 + Item Flag: + Stage: 10 + Flag: 0x9E + +# CITY IN THE SKY + +- Name: City in the Sky Underwater East Chest + Original Item: Red Rupee + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 27 + +- Name: City in the Sky Underwater West Chest + Original Item: Water Bombs 15 + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 3 + +- Name: City in the Sky West Wing First Chest + Original Item: City in the Sky Small Key + Categories: + - Dungeon + - City in the Sky + - Small Key + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 5 + +- Name: City in the Sky West Wing Baba Balcony Chest + Original Item: Arrows 20 + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 21 + +- Name: City in the Sky West Wing Narrow Ledge Chest + Original Item: Red Rupee + # HD Original Item: Stamp (Ooccoo) + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 22 + +- Name: City in the Sky West Wing Tile Worm Chest + Original Item: Bombs 10 + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 20 + +- Name: City in the Sky Baba Tower Top Small Chest + Original Item: Yellow Rupee + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 7 + +- Name: City in the Sky Baba Tower Narrow Ledge Chest + Original Item: Arrows 20 + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 23 + +- Name: City in the Sky Baba Tower Alcove Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 6 + +- Name: City in the Sky West Garden Corner Chest + Original Item: Red Rupee + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 15 + +- Name: City in the Sky West Garden Lone Island Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (G) + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 25 + +- Name: City in the Sky Garden Island Poe + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - City in the Sky + Metadata: + Poe: + - Stage: 12 + Flag: 0x54 + +- Name: City in the Sky West Garden Lower Chest + Original Item: Bombs 5 + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 11 + +- Name: City in the Sky West Garden Ledge Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 9 + +- Name: City in the Sky Central Outside Ledge Chest + Original Item: Red Rupee + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 24 + +- Name: City in the Sky Central Outside Poe Island Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (Z) + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 12 + +- Name: City in the Sky Poe Above Central Fan + Original Item: Poe Soul + Categories: + - Dungeon + - Poe + - City in the Sky + Metadata: + Poe: + - Stage: 12 + Flag: 0x55 + +- Name: City in the Sky Big Key Chest + Original Item: City in the Sky Big Key + Categories: + - Dungeon + - City in the Sky + - Big Key + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 13 + +- Name: City in the Sky Chest Below Big Key Chest + Original Item: Red Rupee + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 14 + +- Name: City in the Sky East First Wing Chest After Fans + Original Item: City in the Sky Dungeon Map + Categories: + - Dungeon + - City in the Sky + - Dungeon Items + - Dungeon Map + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 2 + +- Name: City in the Sky East Tile Worm Small Chest + Original Item: Yellow Rupee + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 19 + +- Name: City in the Sky East Wing After Dinalfos Alcove Chest + Original Item: Red Rupee + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 18 + +- Name: City in the Sky East Wing After Dinalfos Ledge Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 26 + +- Name: City in the Sky East Wing Lower Level Chest + Original Item: City in the Sky Compass + Categories: + - Dungeon + - City in the Sky + - Dungeon Items + - Compass + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 4 + +- Name: City in the Sky Aeralfos Chest + Original Item: Progressive Clawshot + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 14 + Tbox Id: 0 + +- Name: City in the Sky Chest Behind North Fan + Original Item: Purple Rupee + Categories: + - Dungeon + - City in the Sky + - ARC + Metadata: + Chest: + - Stage: 12 + Tbox Id: 17 + +- Name: City in the Sky Argorok Heart Container + Original Item: Heart Container + Categories: + - Heart Container + - Dungeon + - City in the Sky + - Boss + Metadata: + Freestanding Item: + - Stage: 13 + Flag: 0x9F + +- Name: City in the Sky Dungeon Reward + Original Item: Progressive Mirror Shard + Categories: + - Dungeon + - City in the Sky + - Dungeon Reward + - REL + - ARC + Goal Location: True + Metadata: + FLW Message: + - Group: 5 + Message Id: 11001 + Event Flag: 0x2002 + +# PALACE OF TWILIGHT + +- Name: Palace of Twilight West Wing First Room Central Chest + Original Item: Palace of Twilight Small Key + Categories: + - Dungeon + - Palace of Twilight + - Small Key + Metadata: + Chest: + - Stage: 15 + Tbox Id: 4 + +- Name: Palace of Twilight West Wing Chest Behind Wall of Darkness + Original Item: Piece of Heart + Categories: + - Dungeon + - Palace of Twilight + Metadata: + Chest: + - Stage: 15 + Tbox Id: 30 + +- Name: Palace of Twilight West Wing Second Room Central Chest + Original Item: Palace of Twilight Small Key + Categories: + - Dungeon + - Palace of Twilight + - Small Key + Metadata: + Chest: + - Stage: 15 + Tbox Id: 5 + +- Name: Palace of Twilight West Wing Second Room Lower South Chest + Original Item: Palace of Twilight Compass + Categories: + - Dungeon + - Palace of Twilight + - Dungeon Items + - Compass + Metadata: + Chest: + - Stage: 15 + Tbox Id: 3 + +- Name: Palace of Twilight West Wing Second Room Southeast Chest + Original Item: Orange Rupee + # HD Original Item: Stamp (Angry Midna) + Categories: + - Dungeon + - Palace of Twilight + Metadata: + Chest: + - Stage: 15 + Tbox Id: 25 + +- Name: Palace of Twilight East Wing First Room Zant Head Chest + Original Item: Palace of Twilight Small Key + Categories: + - Dungeon + - Palace of Twilight + - Small Key + Metadata: + Chest: + - Stage: 15 + Tbox Id: 6 + +- Name: Palace of Twilight East Wing First Room East Alcove Chest + Original Item: Piece of Heart + Categories: + - Dungeon + - Palace of Twilight + Metadata: + Chest: + - Stage: 15 + Tbox Id: 0 + +- Name: Palace of Twilight East Wing First Room North Small Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - Palace of Twilight + Metadata: + Chest: + - Stage: 15 + Tbox Id: 36 + +- Name: Palace of Twilight East Wing First Room West Alcove Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - Palace of Twilight + Metadata: + Chest: + - Stage: 15 + Tbox Id: 27 + +- Name: Palace of Twilight East Wing Second Room Northeast Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - Palace of Twilight + Metadata: + Chest: + - Stage: 15 + Tbox Id: 28 + +- Name: Palace of Twilight East Wing Second Room Northwest Chest + Original Item: Purple Rupee + # HD Original Item: Stamp (Zant) + Categories: + - Dungeon + - Palace of Twilight + Metadata: + Chest: + - Stage: 15 + Tbox Id: 29 + +- Name: Palace of Twilight East Wing Second Room Southeast Chest + Original Item: Palace of Twilight Small Key + Categories: + - Dungeon + - Palace of Twilight + - Small Key + Metadata: + Chest: + - Stage: 15 + Tbox Id: 7 + +- Name: Palace of Twilight East Wing Second Room Southwest Chest + Original Item: Palace of Twilight Dungeon Map + Categories: + - Dungeon + - Palace of Twilight + - Dungeon Items + - Dungeon Map + Metadata: + Chest: + - Stage: 15 + Tbox Id: 33 + +- Name: Palace of Twilight Collect Both Sols + Original Item: Progressive Sword + Categories: + - Cutscene + - Dungeon + - Palace of Twilight + Metadata: + Freestanding Item: + - Stage: 15 + Flag: 0x81 + +- Name: Palace of Twilight Central First Room Chest + Original Item: Palace of Twilight Small Key + Categories: + - Dungeon + - Palace of Twilight + - Small Key + Metadata: + Chest: + - Stage: 15 + Tbox Id: 22 + +- Name: Palace of Twilight Central Outdoor Chest + Original Item: Palace of Twilight Small Key + Categories: + - Dungeon + - Palace of Twilight + - Small Key + Metadata: + Chest: + - Stage: 15 + Tbox Id: 24 + +- Name: Palace of Twilight Big Key Chest + Original Item: Palace of Twilight Big Key + Categories: + - Dungeon + - Palace of Twilight + - Big Key + Metadata: + Chest: + - Stage: 15 + Tbox Id: 23 + +- Name: Palace of Twilight Central Tower Chest + Original Item: Palace of Twilight Small Key + Categories: + - Dungeon + - Palace of Twilight + - Small Key + Metadata: + Chest: + - Stage: 15 + Tbox Id: 26 + +- Name: Palace of Twilight Zant Heart Container + Original Item: Heart Container + Categories: + - Dungeon + - Palace of Twilight + - Heart Container + - Dungeon Reward + Goal Location: True + Metadata: + Freestanding Item: + - Stage: 16 + Flag: 0x80 + +# HYRULE CASTLE + +- Name: Hyrule Castle West Courtyard Central Small Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 5 + +- Name: Hyrule Castle West Courtyard North Small Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 9 + +- Name: Hyrule Castle King Bulblin Key + Original Item: Hyrule Castle Small Key + Categories: + - Npc + - Small Key + - Dungeon + - Hyrule Castle + - REL + Metadata: + Name Lookup: + - Hyrule Castle King Bulblin Key + # Not actually a chest, but uses a chest collection flag + Chest: + - Stage: 20 + Tbox Id: 0x3 + +- Name: Hyrule Castle East Wing Boomerang Puzzle Chest + Original Item: Hyrule Castle Dungeon Map + Categories: + - Dungeon + - Hyrule Castle + - Dungeon Items + - Dungeon Map + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 4 + +- Name: Hyrule Castle East Wing Balcony Chest + Original Item: Yellow Rupee + # HD Original Item: Stamp (Sad Zelda) + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 11 + +- Name: Hyrule Castle Graveyard Grave Switch Room Back Left Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 34 + +- Name: Hyrule Castle Graveyard Grave Switch Room Front Left Chest + Original Item: Green Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 33 + +- Name: Hyrule Castle Graveyard Grave Switch Room Right Chest + Original Item: Orange Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 30 + +- Name: Hyrule Castle Graveyard Owl Statue Chest + Original Item: Hyrule Castle Small Key + Categories: + - Dungeon + - Hyrule Castle + - Small Key + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 7 + +- Name: Hyrule Castle Main Hall Northeast Chest + Original Item: Hyrule Castle Compass + Categories: + - Dungeon + - Hyrule Castle + - Dungeon Items + - Compass + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 8 + +- Name: Hyrule Castle Main Hall Northwest Chest + Original Item: Silver Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 13 + +- Name: Hyrule Castle Main Hall Southwest Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 2 + +- Name: Hyrule Castle Lantern Staircase Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 25 + +- Name: Hyrule Castle Southeast Balcony Tower Chest + Original Item: Hyrule Castle Small Key + Categories: + - Dungeon + - Hyrule Castle + - Small Key + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 1 + +- Name: Hyrule Castle Big Key Chest + Original Item: Hyrule Castle Big Key + Categories: + - Dungeon + - Hyrule Castle + - Big Key + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 0 + +- Name: Hyrule Castle Treasure Room First Chest + Original Item: Orange Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 15 + +- Name: Hyrule Castle Treasure Room Second Chest + Original Item: Seeds 50 + # HD Original Item: Stamp (Happy Zelda) + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 16 + +- Name: Hyrule Castle Treasure Room Third Chest + Original Item: Silver Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 17 + +- Name: Hyrule Castle Treasure Room Fourth Chest + Original Item: Bomblings 10 + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 18 + +- Name: Hyrule Castle Treasure Room Fifth Chest + Original Item: Purple Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 19 + +- Name: Hyrule Castle Treasure Room First Small Chest + Original Item: Arrows 30 + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 35 + +- Name: Hyrule Castle Treasure Room Second Small Chest + Original Item: Green Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 29 + +- Name: Hyrule Castle Treasure Room Third Small Chest + Original Item: Bombs 20 + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 21 + +- Name: Hyrule Castle Treasure Room Fourth Small Chest + Original Item: Arrows 20 + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 23 + +- Name: Hyrule Castle Treasure Room Fifth Small Chest + Original Item: Water Bombs 15 + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 22 + +- Name: Hyrule Castle Treasure Room Sixth Small Chest + Original Item: Red Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 26 + +- Name: Hyrule Castle Treasure Room Seventh Small Chest + Original Item: Yellow Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 27 + +- Name: Hyrule Castle Treasure Room Eighth Small Chest + Original Item: Blue Rupee + Categories: + - Dungeon + - Hyrule Castle + - ARC + Metadata: + Chest: + - Stage: 20 + Tbox Id: 28 + +- Name: Defeat Ganondorf + Original Item: Game Beatable + Categories: + - Placeholder + Metadata: + - None + +# HINT SIGNS + +- Name: Ordon Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: South Faron Woods Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Sacred Grove Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Faron Field Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Kakariko Gorge Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Kakariko Village Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Kakariko Graveyard Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Eldin Field Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: North Eldin Field Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Hidden Village Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Lanayru Field Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Beside Castle Town Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Castle Town Center Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Outside South Castle Town Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Lake Hylia Bridge Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Lake Hylia Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Lanayru Spring Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Lake Lantern Cave Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Fishing Hole Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Zoras Domain Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Snowpeak Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Gerudo Desert Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Bulblin Camp Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None + +- Name: Forest Temple Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: Goron Mines Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: Lakebed Temple Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: Arbiters Grounds Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: Snowpeak Ruins Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: Temple of Time First Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: Temple of Time Second Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: City in the Sky Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: Palace of Twilight Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: Hyrule Castle Hint Sign + Categories: + - Hint Sign + - Dungeon + - Non-Item Location + Metadata: + - None + +- Name: Cave of Ordeals Hint Sign + Categories: + - Hint Sign + - Overworld + - Non-Item Location + Metadata: + - None diff --git a/mods/randomizer/generator/data/macros.yaml b/mods/randomizer/generator/data/macros.yaml new file mode 100644 index 0000000000..a5766aaa39 --- /dev/null +++ b/mods/randomizer/generator/data/macros.yaml @@ -0,0 +1,256 @@ +# Macros are a way to shorten or make more explicit the logical requirements +# for certain things. Macros can be used on logic statements like any item and do +# not need any code modifications to run. Simply add them here and they can be used +# in the world graph files. + +Can Open Doors: Human_Link +Can Climb Ladders: Human_Link +Can Climb Vines: Human_Link +Can Talk to Humans: Human_Link +Can Swing on Monkeys: Human_Link +Can Pickup Bomblings: Human_Link +Can Pull Lakebed Levers: Human_Link +Can Pull Blocks: Human_Link +Can Ride Boars: Human_Link +Can Summon Hawk: Human_Link +Can Talk to Animals: Wolf_Link +Can Use Senses: Wolf_Link +Can Dig: Wolf_Link +Can Howl: Wolf_Link +Can Sniff: Wolf_Link +Can Midna Jump: Wolf_Link +Can Use Tightrope: Wolf_Link +Not Twilight: Human_Link or Wolf_Link + +Slingshot: Slingshot and 'Can_Refill_Slingshot_Seeds' and Human_Link +Lantern: Lantern and 'Can_Refill_Lantern_Oil' and Human_Link +Gale Boomerang: Gale_Boomerang and Human_Link +Iron Boots: Iron_Boots and Human_Link +Bow: Progressive_Bow and 'Can_Refill_Arrows' and Human_Link +Regular Bombs: Bomb_Bag and 'Can_Refill_Regular_Bombs' and Human_Link +Water Bombs: Bomb_Bag and 'Can_Refill_Water_Bombs' and Human_Link +Bombs: Regular_Bombs or Water_Bombs +Bomb Arrows: Bow and Bombs +Spinner: Spinner and Human_Link +Ball and Chain: Ball_and_Chain and Human_Link +Zora Armor: Zora_Armor and Human_Link +Magic Armor: Magic_Armor and Human_Link +Shield: Hylian_Shield or 'Can_Buy_Wooden_Shield' +Bottle: Empty_Bottle and Lantern # Can't empty lantern oil from a bottle until you have the lantern +Asheis Sketch: Asheis_Sketch and Human_Link +Aurus Memo: Aurus_Memo and Human_Link +Renados Letter: Renados_Letter and Human_Link +Invoice: Invoice and Human_Link +Wooden Statue: Wooden_Statue and Human_Link +Ilias Charm: Ilias_Charm and Human_Link + +Fishing Rod: Progressive_Fishing_Rod and Human_Link +Coral Earring: count(Progressive_Fishing_Rod, 2) and Human_Link +Sword: Progressive_Sword and Human_Link +Ordon Sword: count(Progressive_Sword, 2) and Human_Link +Master Sword: count(Progressive_Sword, 3) and Human_Link +Light Sword: count(Progressive_Sword, 4) and Human_Link +Big Quiver: count(Progressive_Bow, 2) and 'Can_Refill_Arrows' and Human_Link +Giant Quiver: count(Progressive_Bow, 3) and 'Can_Refill_Arrows' and Human_Link +Clawshot: Progressive_Clawshot and Human_Link +Double Clawshots: count(Progressive_Clawshot, 2) and Human_Link +Dominion Rod: Progressive_Dominion_Rod and Human_Link +Restored Dominion Rod: count(Progressive_Dominion_Rod, 2) and Human_Link +Big Wallet: Progressive_Wallet +Giant Wallet: count(Progressive_Wallet, 2) +Collosal Wallet: count(Progressive_Wallet, 3) + +Ending Blow: Sword and Progressive_Hidden_Skill +Shield Attack: Shield and count(Progressive_Hidden_Skill, 2) +Back Slice: Sword and count(Progressive_Hidden_Skill, 3) +Helm Splitter: Sword and Shield_Attack and count(Progressive_Hidden_Skill, 4) +Mortal Draw: Sword and count(Progressive_Hidden_Skill, 5) +Jump Strike: Sword and count(Progressive_Hidden_Skill, 6) +Great Spin: Sword and count(Progressive_Hidden_Skill, 7) + +Can Use Back Slice as Sword: Back_Slice_as_Sword == On and count(Progressive_Hidden_Skill, 3) + +Can Do Niche Stuff: Impossible # TODO: Make settings for all of these +Can Do Difficult Combat: Impossible # TODO: Make settings for all of these + +Can Use Hot Spring Water: Bottle and 'Can_Buy_Hot_Spring_Water' +Can Use Bottled Fairy: Bottle and 'Fairy_Access' +# This will have to change if we allow players to start with less than 3 hearts +Can Survive Damage: Logic_Damage_Multiplier != OHKO or Can_Use_Bottled_Fairy +Can Survive One Bonk: Bonks_Do_Damage == Off or Can_Survive_Damage +Can Survive Two Bonks: Bonks_Do_Damage == Off or Logic_Damage_Multiplier != OHKO or + (Can_Use_Bottled_Fairy and count(Empty_Bottle, 2)) +Can Survive Three Bonks: Bonks_Do_Damage == Off or Logic_Damage_Multiplier != OHKO or + (Can_Use_Bottled_Fairy and count(Empty_Bottle, 3)) +Can Smash: Bombs or Ball_and_Chain +Can Break Webs: Lantern or Bombs or (Ball_and_Chain and Ball_and_Chain_Webs == On) +Can Light Torches: Lantern +Can Extinguish Torches: Gale_Boomerang +Can Launch Bombs: Bombs and (Gale_Boomerang or Bow) +Can Break Monkey Cage: Sword or Iron_Boots or Spinner or Ball_and_Chain or Wolf_Link or Bombs or + Bow or Clawshot or (Can_Do_Niche_Stuff and Shield_Attack) +Can Cut Hanging Web: Clawshot or Bow or Gale_Boomerang or Ball_and_Chain +Can Break Wooden Barrier: Sword or Can_Smash or Wolf_Link or Can_Use_Back_Slice_as_Sword +Can Refill Air: Zora_Armor # or glitched logic water bombs +Can Cross Quicksand: Wolf_Link or Spinner +Can Break Armor: Ball_and_Chain +Can Break Ice: Ball_and_Chain +Can Launch Canonball: Bombs +Can Knock Down Hyrule Castle Painting: Bow or (Can_Do_Niche_Stuff and (Bombs or Jump_Strike)) +Can Knock Down Hanging Baba: Bow or Clawshot or Gale_Boomerang or Slingshot + +Has Damaging Item: Sword or Ball_and_Chain or Bow or Bombs or Iron_Boots or Wolf_Link or Spinner +Can Hit Crystal Switch: Clawshot or Has_Damaging_Item +Can Hit Crystal Switch at Range: Clawshot or Bow + +Can Complete MDH: Skip_Midna's_Desparate_Hour == On or ('Can_Access_Castle_Town_South' and 'Can_Complete_Lakebed_Temple') + + + +# REGULAR ENEMIES + +# A lot of enemies use this identical logical requirement +Can Defeat Generic Enemy: Sword or Ball_and_Chain or Bow or Spinner or Wolf_Link or Bombs or + Can_Use_Back_Slice_as_Sword or (Can_Do_Niche_Stuff and Iron_Boots) + +Can Defeat Shadow Beast: Sword or (Wolf_Link and Can_Complete_MDH) +Can Defeat Keese: Sword or Ball_and_Chain or Bow or Spinner or Wolf_Link or Slingshot or + Can_Use_Back_Slice_as_Sword or (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat Fire Keese: Can_Defeat_Keese +Can Defeat Ice Keese: Can_Defeat_Keese +Can Defeat Shadow Keese: Can_Defeat_Keese +Can Defeat Rat: Slingshot or Can_Defeat_Generic_Enemy +Can Defeat Ghoul Rat: Can_Use_Senses +Can Defeat Bokoblin: Can_Defeat_Generic_Enemy or Slingshot +Can Defeat Red Bokoblin: Sword or Ball_and_Chain or Giant_Quiver or Wolf_Link or Bombs or + Can_Use_Back_Slice_as_Sword or (Can_Do_Difficult_Combat and (Iron_Boots or Spinner)) +Can Defeat Bulblin: Can_Defeat_Generic_Enemy +Can Defeat Deku Baba: Sword or Ball_and_Chain or Bow or Spinner or Shield_Attack or Slingshot or Clawshot or Bombs or + Can_Use_Back_Slice_as_Sword or (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat Big Baba: Can_Defeat_Generic_Enemy +Can Defeat Baba Serpent: Can_Defeat_Generic_Enemy +Can Defeat Walltula: Ball_and_Chain or Slingshot or Bow or Gale_Boomerang or Clawshot +Can Defeat Skulltula: Can_Defeat_Generic_Enemy +Can Defeat Deku Like: Bombs +Can Launch Tileworm: Gale_Boomerang +Can Defeat Tileworm: Gale_Boomerang and Can_Defeat_Generic_Enemy +Can Defeat Stalhound: Can_Defeat_Generic_Enemy +Can Defeat Kargarok: Can_Defeat_Generic_Enemy +Can Defeat Goron: Sword or Ball_and_Chain or Bow or Spinner or Shield_Attack or Slingshot or Clawshot or Bombs or + (Can_Do_Niche_Stuff and Iron_Boots) or (Can_Do_Difficult_Combat and Lantern) or Can_Use_Back_Slice_as_Sword +Can Defeat Torch Slug: Sword or Ball_and_Chain or Bow or Wolf_Link or Bombs +Can Defeat Dodongo: Can_Defeat_Generic_Enemy +Can Defeat Beamos: Ball_and_Chain or Bow or Bombs +Can Defeat Leever: Sword or Ball_and_Chain or Bow or Spinner or Wolf_Link or Bombs or (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat Helmasaur: Can_Defeat_Generic_Enemy +Can Defeat Tektite: Can_Defeat_Generic_Enemy +Can Defeat Toado: Sword or Ball_and_Chain or Bow or Spinner or Wolf_Link +Can Defeat Water Toadpoli: Sword or Ball_and_Chain or Bow or Shield_Attack or (Can_Do_Difficult_Combat and Wolf_Link) +Can Defeat Shell Blade: Water_Bombs or (Sword and (Iron_Boots or (Can_Do_Niche_Stuff and Magic_Armor))) +Can Defeat Lizalfos: Sword or Ball_and_Chain or Bow or Wolf_Link or Bombs or + Can_Use_Back_Slice_as_Sword or (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat Dinalfos: Sword or Ball_and_Chain or Wolf_Link +Can Defeat Chu: Can_Defeat_Generic_Enemy or Clawshot +Can Defeat Chu Worm: (Bombs or Clawshot) and (Sword or Ball_and_Chain or Bow or Spinner or Wolf_Link or Can_Use_Back_Slice_as_Sword) +Can Defeat Bubble: Can_Defeat_Generic_Enemy +Can Defeat Fire Bubble: Can_Defeat_Bubble +Can Defeat Ice Bubble: Can_Defeat_Bubble +Can Defeat Skull Kid: Bow +Can Defeat Poe: Can_Use_Senses +Can Defeat Stalchild: Can_Defeat_Generic_Enemy +Can Defeat Stalfos: Can_Smash +Can Defeat Redead Knight: Sword or Ball_and_Chain or Bow or Wolf_Link or Bombs or + Can_Use_Back_Slice_as_Sword or (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat White Wolfos: Sword or Ball_and_Chain or Bow or Spinner or Wolf_Link or Bombs or (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat Chilfos: Can_Defeat_Generic_Enemy +Can Defeat Mini Freezard: Can_Defeat_Generic_Enemy +Can Defeat Freezard: Ball_and_Chain +Can Defeat Young Gohma: Sword or Ball_and_Chain or Bow or Spinner or Wolf_Link or Bombs or + (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat Baby Gohma: Can_Defeat_Generic_Enemy or Slingshot or Clawshot +Can Defeat Armos: Can_Defeat_Generic_Enemy or Clawshot +Can Defeat Zant Head: Sword or Wolf_Link or Can_Use_Back_Slice_as_Sword + +# MINIBOSSES + +Can Defeat Ook: Sword or Ball_and_Chain or Bow or Wolf_Link or Bombs or + Can_Use_Back_Slice_as_Sword or (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat Dangoro: Iron_Boots and (Sword or Wolf_Link or Bomb_Arrows or (Can_Do_Niche_Stuff and Ball_and_Chain)) +Can Defeat Deku Toad: Sword or Ball_and_Chain or Bow or Wolf_Link or Bombs or + Can_Use_Back_Slice_as_Sword or (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat King Bulblin Desert: Sword or Ball_and_Chain or Wolf_Link or Giant_Quiver or Can_Use_Back_Slice_as_Sword or + (Can_Do_Difficult_Combat and (Spinner or Iron_Boots or Bombs or Big_Quiver)) +Can Defeat Deathsword: Can_Use_Senses and Sword and (Clawshot or Bow or Gale_Boomerang) +Can Defeat Darkhammer: Sword or Ball_and_Chain or Bow or Wolf_Link or Bombs or + (Can_Use_Back_Slice_as_Sword and Can_Do_Difficult_Combat) or (Can_Do_Niche_Stuff and Iron_Boots) +Can Defeat Darknut: Sword or (Can_Do_Difficult_Combat and (Bombs or Ball_and_Chain)) +Can Defeat Aerolfos: Clawshot and (Sword or Ball_and_Chain or Wolf_Link or (Can_Do_Niche_Stuff and Iron_Boots)) +Can Defeat Phantom Zant: Sword or Wolf_Link +Can Defeat King Bulblin Castle: Sword or Ball_and_Chain or Wolf_Link or Big_Quiver or (Can_Do_Difficult_Combat and + (Spinner or Iron_Boots or Bombs or Can_Use_Back_Slice_as_Sword)) + +# BOSSES + +Can Defeat Diababa: Can_Launch_Bombs or (Gale_Boomerang and + (Sword or Ball_and_Chain or Wolf_Link or Bombs or + (Can_Do_Difficult_Combat and Can_Use_Back_Slice_as_Sword) or (Can_Do_Niche_Stuff and Iron_Boots))) +Can Defeat Fyrus: Bow and Iron_Boots and (Sword or (Can_Do_Difficult_Combat and Can_Use_Back_Slice_as_Sword)) +Can Defeat Morpheel: Iron_Boots and Clawshot and Sword and Can_Refill_Air +Can Defeat Stallord: Spinner and (Sword or Can_Do_Difficult_Combat) +Can Defeat Blizzeta: Ball_and_Chain +Can Defeat Armogohma: Bow and Dominion_Rod +Can Defeat Argorok: Double_Clawshots and Ordon_Sword and (Iron_Boots or (Can_Do_Niche_Stuff and Magic_Armor)) +Can Defeat Zant: Master_Sword and Gale_Boomerang and (Iron_Boots or (Can_Do_Niche_Stuff and Magic_Armor)) and + Can_Refill_Air and Clawshot and Ball_and_Chain + +Can Open North Faron Woods Gate: North_Faron_Woods_Gate_Key or Small_Keys == Keysy +Can Complete Prologue: Skip_Prologue == On or (Sword and Slingshot and Can_Open_North_Faron_Woods_Gate) + +# You can only use warp portals from certain stages. +# 'Can_Warp' is an event added to any logical area +# which is part of a stage that players can warp from. +Can Use Warp Portals: Wolf_Link and 'Can_Warp' and Can_Complete_Prologue + +Can Free All Monkeys in Forest Temple: "'Can_Free_Monkey_in_Entrance_Room' and 'Can_Free_Monkey_on_Totem' and + 'Can_Free_Monkey_in_Big_Baba_Room' and 'Can_Free_Monkey_in_West_Tileworm_Room' and + 'Can_Free_Monkey_in_East_Tileworm_Room' and 'Can_Free_Monkey_in_Dark_Spider_Room' and + 'Can_Free_Monkey_in_North_Deku_Like_Room'" + +Has Sword For Temple of Time: Temple_of_Time_Sword_Requirement == None or + (Temple_of_Time_Sword_Requirement == Wooden_Sword and Sword) or + (Temple_of_Time_Sword_Requirement == Ordon_Sword and Ordon_Sword) or + (Temple_of_Time_Sword_Requirement == Master_Sword and Master_Sword) or + (Temple_of_Time_Sword_Requirement == Light_Sword and Light_Sword) + +Can Complete Faron Twilight: Faron_Twilight_Cleared == On or count(Faron_Twilight_Tear, 16) +Can Complete Eldin Twilight: Eldin_Twilight_Cleared == On or count(Eldin_Twilight_Tear, 16) +Can Complete Lanayru Twilight: Lanayru_Twilight_Cleared == On or count(Lanayru_Twilight_Tear, 16) +Can Complete All Twilight: Can_Complete_Faron_Twilight and Can_Complete_Eldin_Twilight and Can_Complete_Lanayru_Twilight + +Can Defeat Faron Twilit Insect: Twilight +Can Defeat Eldin Twilit Insect: Twilight +Can Defeat Lanayru Twilit Insect: Twilight and Can_Complete_Eldin_Twilight + +Can Clear Forest: Can_Complete_Faron_Twilight and ('Can_Complete_Forest_Temple' or Faron_Woods_Logic == Open) + +Can Complete All Dungeons: "'Can_Complete_Forest_Temple' and 'Can_Complete_Goron_Mines' and 'Can_Complete_Lakebed_Temple' and + 'Can_Complete_Arbiters_Grounds' and 'Can_Complete_Snowpeak_Ruins' and 'Can_Complete_Temple_of_Time' and + 'Can_Complete_City_in_the_Sky' and 'Can_Complete_Palace_of_Twilight'" + +Can Break Hyrule Castle Barrier: Hyrule_Barrier_Requirements == Open or + (Hyrule_Barrier_Requirements == Vanilla and 'Can_Complete_Palace_of_Twilight') or + (Hyrule_Barrier_Requirements == Fused_Shadows and count(Progressive_Fused_Shadow, Hyrule_Barrier_Fused_Shadows)) or + (Hyrule_Barrier_Requirements == Mirror_Shards and count(Progressive_Mirror_Shard, Hyrule_Barrier_Mirror_Shards)) or + (Hyrule_Barrier_Requirements == Dungeons and dungeons_completed(Hyrule_Barrier_Dungeons)) or + (Hyrule_Barrier_Requirements == Poe_Souls and count(Poe_Soul, Hyrule_Barrier_Poe_Souls)) or + (Hyrule_Barrier_Requirements == Hearts and hearts(Hyrule_Barrier_Hearts)) + +Can Open Hyrule Castle Big Key Gate: Hyrule_Castle_Big_Key_Requirements == None or + (Hyrule_Castle_Big_Key_Requirements == Fused_Shadows and count(Progressive_Fused_Shadow, Hyrule_Castle_Big_Key_Fused_Shadows)) or + (Hyrule_Castle_Big_Key_Requirements == Mirror_Shards and count(Progressive_Mirror_Shard, Hyrule_Castle_Big_Key_Mirror_Shards)) or + (Hyrule_Castle_Big_Key_Requirements == Dungeons and dungeons_completed(Hyrule_Castle_Big_Key_Dungeons)) or + (Hyrule_Castle_Big_Key_Requirements == Poe_Souls and count(Poe_Soul, Hyrule_Castle_Big_Key_Poe_Souls)) or + (Hyrule_Castle_Big_Key_Requirements == Hearts and hearts(Hyrule_Castle_Big_Key_Hearts)) + + +Can Talk to Springwater Goron: Nothing # TODO diff --git a/mods/randomizer/generator/data/object_patches.yaml b/mods/randomizer/generator/data/object_patches.yaml new file mode 100644 index 0000000000..32258b63ec --- /dev/null +++ b/mods/randomizer/generator/data/object_patches.yaml @@ -0,0 +1,4961 @@ +# Patches for game objects that get spawned in + +# Lakebed Temple +D_MN01: + # Room 2 - Outer Bridges Rooms + 2: + # Add hint sign near lower west water wheel + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -4765.38037 + y: -1.21 + z: 272.192261 + angle: + x: 21125 # Flow node id + y: 0x46B9 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Goron Mines +D_MN04: + # Room 17 - Gor Ebizo's Room + 17: + # Add hint sign near Gor Ebizo + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 11394.1855 + y: 2878.65 + z: -17913.05 + angle: + x: 21124 # Flow node id + y: 0xD556 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Forest Temple +D_MN05: + # Room 0 - Main Center Room + 0: + # Add hint sign near west door + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -1972.11682 + y: 3150 + z: 7610.9751 + angle: + x: 21123 # Flow node id + y: 0x6000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Temple of Time +D_MN06: + # Room 0 - Entrance Room + 0: + # Add hint sign near Ooccoo + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -618.9335 + y: 725.0 + z: 3112.2 + angle: + x: 21128 # Flow node id + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + + # Room 4 - Moving Walls Room + 4: + # Add hint sign after first switch area + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -3885.2157 + y: 4450.0 + z: -6353.38135 + angle: + x: 21129 # Flow node id + y: 0x2000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + + +# City in the Sky +D_MN07: + # Stage file objects + Stage: + # change the Savemem actor that changes Link's spawn position + - action: patch + name: Savmem + parameters: 0x00000A03 + position: + x: -15044.2919921875 + y: 0.0 + z: -11283.109375 + angle: + x: 0xFFFF + y: 0xFFFF + z: 0x184D + set id: 0xFFFF + patch: + parameters: 0x00000003 + layers: + - 0 + + # Room 0 - Entrance + 0: + # Add an extra loading zone so players can go down to Lake Hylia + # without needing the clawshot. This loading zone is placed to the + # west of the pond at the entrance and can be jumped into off the broken bridge + - action: add + name: scnChg + parameters: 0xFFFF0001 + position: + x: -3176.0 + y: -600.0 + z: 5480.0 + angle: + x: 0x0FFF + y: 0 + z: 0x0FFF + set id: 0xFFFF + scale: + x: 20 + y: 67 + z: 54 + layers: + - 0 + + # Room 2 - Central Room + 2: + # Add hint sign after first switch area + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 3376.54 + y: 0.0 + z: -12709.0352 + angle: + x: 21130 # Flow node id + y: 0xC846 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + + # Room 6 - West Bridge + 6: + # Delete argorok actor that normally breaks the bridge + - action: delete + name: dr + parameters: 0x00000018 + position: + x: -7075 + y: -200 + z: -11809.4033203125 + angle: + x: 0x0000 + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Palace of Twilight +D_MN08: + # Room 0 - Main Entrance + 0: + # Delete the invisible wall that blocks north access + # This wall normally disappears when both sols are + # placed, but we want players to be able to access + # the north wing if they have Light Sword + - action: delete + name: ClearB + parameters: 0x00003F81 + position: + x: 255.0 + y: 1600.0 + z: 2560.0 + angle: + x: 0x4000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + scale: + x: 20 + y: 10 + z: 20 + layers: + - 14 + + # Delete the TagYami as well so midna doesn't prevent + # the player from going north either + - action: delete + name: TagYami + parameters: 0x00E6E502 + position: + x: 250 + y: 800 + z: 5800 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + scale: + x: 100 + y: 50 + z: 100 + layers: + - 14 + + # Add hint sign to starting area + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 679.0 + y: -200.0 + z: 9311.0 + angle: + x: 21131 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 14 + +# Hyrule Castle +D_MN09: + # Room 11 - Entrance Courtyard Area + 11: + # Add hint sign near central statue + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 0.0 + y: 25.0 + z: 11625.0 + angle: + x: 21132 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Arbiter's Grounds +D_MN10: + # Room 2 - Poe Gate Room + 2: + # Add hint sign near Poe Gate + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -349.4044 + y: 450.0 + z: -2876.90771 + angle: + x: 21126 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Snowpeak Ruins +D_MN11: + # Room 1 - Yeta's Room + 1: + # Add hint sign near Poe Gate + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -530.0 + y: 0.0 + z: -669.69 + angle: + x: 21127 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Cave of Ordeals +D_SB01: + # Room 0 - Main Cave + 0: + # Add hint sign at the start + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -1191.42 + y: 1100.0 + z: -260.65 + angle: + x: 21133 # Flow node id + y: 0xE483 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Lake Hylia Lantern Cave +D_SB03: + # Room 0 - Main Cave + 0: + # Add hint sign about halfway through the cave + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -2897.38672 + y: -1636.68994 + z: -17674.8691 + angle: + x: 21117 # Flow node id + y: 0x3E21 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Ordon Village +F_SP103: + # Room 0 - Main Village + 0: + # Bo's House left Door + - action: patch + name: kdoor + parameters: 0x88000627 + position: + x: 470.482940673828 + y: 500.190002441406 + z: 5323.12890625 + angle: + x: 0x0191 + y: 0x3777 + z: 0xFFFF + set id: 0x00FF + scale: + x: 10 + y: 10 + z: 10 + patch: + # Set angle.x to -1 so the door isn't locked + angle: + x: 0xFFFF + layers: + - 0 + + # Bo's House right door + - action: patch + name: kdoor + parameters: 0x94000627 + position: + x: 500.711181640625 + y: 500.190002441406 + z: 5172.26708984375 + angle: + x: 0x0191 + y: 0xB778 + z: 0xFFFF + set id: 0x00FF + scale: + x: 10 + y: 10 + z: 10 + patch: + # Set angle.x to -1 so the door isn't locked + angle: + x: 0xFFFF + layers: + - 0 + + # Rupee on Rusl's House (they both share the same flag, this gives one a different flag) + - action: patch + name: item + parameters: 0xF3FF8103 + position: + x: -4739.0400390625 + y: 1155.98303222656 + z: 2239.53588867187 + angle: + x: 0x0000 + y: 0x3333 + z: 0x003F + set id: 0xFFFF + patch: + # Give this item a unique flag + parameters: 0xF3FF8403 + layers: + - 0 + - 5 + + # Rupee above Haunch's House (also flag sharing situation) + - action: patch + name: item + parameters: 0xF3FF8701 + position: + x: -1819.2646484375 + y: 984.098937988281 + z: 1665.06237792969 + angle: + x: 0x0000 + y: 0x3333 + z: 0x003F + set id: 0xFFFF + patch: + # Give this item a unique flag + parameters: 0xF3FF8501 + layers: + - 0 + + # Rupee by Bo's Window (also flag sharing situation) + - action: patch + name: item + parameters: 0xF3FF8601 + position: + x: 590.097839355469 + y: 1158.02856445312 + z: 5215.7802734375 + angle: + x: 0x0000 + y: 0x3333 + z: 0x003F + set id: 0xFFFF + patch: + # Give this item a unique flag + parameters: 0xF3FF8A01 + layers: + - 0 + + # Rupee in Ordon River (also flag sharing situation) + - action: patch + name: item + parameters: 0x13FF9501 + position: + x: -4326.0654296875 + y: 28.7633323669434 + z: 4208.9892578125 + angle: + x: 0x0000 + y: 0x999A + z: 0x003F + set id: 0xFFFF + patch: + # Give this item a unique flag + parameters: 0x13FF9101 + layers: + - 0 + + # Add Epona actor to Ordon Village so we don't crash + # when entering on layer 0 + - action: add + name: Horse + parameters: 0x00000148 + position: + x: -1200 + y: 367.4823 + z: 6100 + angle: + x: 0x0000 + y: 0x71C7 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + + # Room 1 - Outside Link's House + 1: + # Delete 2nd Beth actor + - action: delete + name: Besu + parameters: 0x00FFFF10 + position: + x: 907.519104003906 + y: 800 + z: -1572.38488769531 + angle: + x: 0x0032 + y: 0xA2D9 + z: 0x0000 + set id: 0xFFFF + layers: + - 4 + + # Delete 2nd Malo actor + - action: delete + name: Maro + parameters: 0xFFFFFF0F + position: + x: 847.672119140625 + y: 800 + z: -1500.17639160156 + angle: + x: 0x003D + y: 0xA2D9 + z: 0x0000 + set id: 0xFFFF + layers: + - 4 + + # Add hint sign near loading zone to Ordon Village + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 687.89 + y: 800.0 + z: -1424.16 + angle: + x: 21100 # Flow node id + y: 0xA019 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 8 + - 9 + - 12 + +# Ordon Spring +F_SP104: + # Room 1 - Main Spring + 1: + # Golden Wolf in Ordon Spring + - action: patch + name: GWolf + parameters: 0x0E4102FF + position: + x: -1855.69543457031 + y: 311.160461425781 + z: -8084.61572265625 + angle: + x: 0x0BD1 + y: 0x8000 + z: 0x00FF + set id: 0xFFFF + patch: + # Turn the golden wolf into a htPiece actor with the hidden skill item + name: htPiece + parameters: 0xFFFFFFE1 + layers: + - 2 + +# Faron Woods +F_SP108: + # Room 4 - Coro's Clearing + 4: + # Add hint sign near on platform south of Coro's hut + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -12423.8467 + y: 273.277985 + z: -11518.958 + angle: + x: 21101 # Flow node id + y: 0x9228 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 1 + - 2 + - 3 + - 5 + - 9 + - 14 + + # Rupee under boulder near coro (green) + - action: patch + name: item + parameters: 0xF3048001 + position: + x: -14823.521484375 + y: -207.559860229492 + z: -17239.197265625 + angle: + x: 0x0000 + y: 0x2B60 + z: 0x003F + set id: 0xFFFF + patch: + # Give this item a unique flag + parameters: 0xF3048101 + layers: + - 0 + - 1 + - 2 + - 3 + - 5 + - 9 + - 14 + + # Rupee under boulder near coro (blue) + - action: patch + name: item + parameters: 0xF3048002 + position: + x: -14708.259765625 + y: 65.5312805175781 + z: -17336.09375 + angle: + x: 0x0000 + y: 0x2B60 + z: 0x003F + set id: 0xFFFF + patch: + # Give this item a unique flag + parameters: 0xF3048201 + layers: + - 0 + - 1 + - 2 + - 3 + - 5 + - 9 + - 14 + + # Rupee under boulder near coro (yellow) + - action: patch + name: item + parameters: 0xF3048003 + position: + x: -14725.6796875 + y: -29.1403198242188 + z: -17190.748046875 + angle: + x: 0x0000 + y: 0x2B60 + z: 0x003F + set id: 0xFFFF + patch: + # Give this item a unique flag + parameters: 0xF3048301 + layers: + - 0 + - 1 + - 2 + - 3 + - 5 + - 9 + - 14 + + # Room 6 - North Faron Woods + 6: + # Spawn Item for Golden Wolf in North Faron Woods layer 3 + - action: add + name: htPiece + parameters: 0xFFFFFFE1 + position: + x: -36699.4375 + y: 428.600311279297 + z: -23663.64453125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x00FF + set id: 0xFFFF + layers: + - 3 + +# Kakariko Village +F_SP109: + # Room 0 - Main Village + 0: + # Add bomb rock heart piece to twilight layer + - action: add + name: htPiece + parameters: 0x00FF8C21 + position: + x: -2360.0 + y: 1586.455 + z: 9050 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 14 + + # Add hint sign near hot spring + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -3347.52734 + y: 2999.16138 + z: -2865.99341 + angle: + x: 21105 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 2 + - 12 + - 14 + +# Kakariko Graveyard +F_SP111: + # Room 0 - Main graveyard + 0: + # Male Ant + - action: patch + name: I_Ari + parameters: 0x00000F00 + position: + x: 16933.3046875 + y: 500 + z: -769.167724609375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x91 + name: htPiece + parameters: 0x00FF91D4 + layers: + - 0 + - 2 + - 3 + + # Golden Wolf in Kakariko Graveyard + - action: patch + name: GWolf + parameters: 0x037906FF + position: + x: 17575.34375 + y: 500.0 + z: -51.3761901855469 + angle: + x: 0x0BD1 + y: 0xC000 + z: 0x00FF + set id: 0xFFFF + patch: + # Turn the golden wolf into a htPiece actor with the hidden skill item + name: htPiece + parameters: 0xFFFFFFE1 + layers: + - 2 + - 3 + + # Add hint sign behind zora grave + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 21765.9863 + y: 500.0 + z: -62.247 + angle: + x: 21106 # Flow node id + y: 0xC000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 2 + - 3 + - 14 + +# Zora's Domain +F_SP113: + # Room 0 - Throne Room + 0: + # East Gate Underwater Rupee (Blue 1) + - action: patch + name: item + parameters: 0x13FF8A02 + position: + x: 513.140747070313 + y: -1098.53369140625 + z: -163.655395507813 + angle: + x: 0x0000 + y: 0x3333 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x13FFFF02 + layers: + - 0 + - 2 + + # East Gate Underwater Rupee (Blue 2) + - action: patch + name: item + parameters: 0x13FF8A02 + position: + x: 591.445739746094 + y: -1124.66955566406 + z: -107.20272064209 + angle: + x: 0x0000 + y: 0x999A + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x13FFFF02 + layers: + - 0 + - 2 + + # West Gate Underwater Rupee (Blue 1) + - action: patch + name: item + parameters: 0x13FF8902 + position: + x: -414.366485595703 + y: -1080.33239746094 + z: -203.718460083008 + angle: + x: 0x0000 + y: 0xCCCD + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x13FFFF02 + layers: + - 0 + - 2 + + # West Gate Underwater Rupee (Blue 1) + - action: patch + name: item + parameters: 0x13FF8902 + position: + x: -463.534942626953 + y: -1115.705078125 + z: -125.413246154785 + angle: + x: 0x0000 + y: 0x3333 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x13FFFF02 + layers: + - 0 + - 2 + + # Room 1 - Main Zora's Domain Area + 1: + # Male Dragonfly + - action: patch + name: I_Tom + parameters: 0x00000F00 + position: + x: 3653.44995117188 + y: -5167.66455078125 + z: 15500.0009765625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x9F + name: htPiece + parameters: 0x00FF9FD2 + position: + y: -5300 + layers: + - 0 + - 2 + + # Central Underwater Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0x133B8E03 + position: + x: 244.258148193359 + y: -8445.4345703125 + z: 12791.822265625 + angle: + x: 0x0000 + y: 0xDF4A + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x133BFF03 + layers: + - 0 + - 2 + - 13 + - 14 + + # Central Underwater Boulder Rupee (Blue) + - action: patch + name: item + parameters: 0x133B8E02 + position: + x: 167.000732421875 + y: -8493.4326171875 + z: 12821.283203125 + angle: + x: 0x0000 + y: 0xDF4A + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x133BFF02 + layers: + - 0 + - 2 + - 13 + - 14 + + # Central Underwater Boulder Rupee (Green 1) + - action: patch + name: item + parameters: 0x133B8E01 + position: + x: 191.989395141602 + y: -8528.955078125 + z: 12747.720703125 + angle: + x: 0x0000 + y: 0xDF4A + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x133BFF01 + layers: + - 0 + - 2 + - 13 + - 14 + + # Central Underwater Boulder Rupee (Green 2) + - action: patch + name: item + parameters: 0x133B8E01 + position: + x: 192.409286499023 + y: -8430.283203125 + z: 12866.822265625 + angle: + x: 0x0000 + y: 0xDF4A + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x133BFF01 + layers: + - 0 + - 2 + - 13 + - 14 + + # North Underwater Boulder Rupee (Red) + - action: patch + name: item + parameters: 0x133A8D04 + position: + x: 7.37179803848267 + y: -8762.6083984375 + z: 8355.388671875 + angle: + x: 0x0000 + y: 0x8000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x133AFF04 + layers: + - 0 + - 2 + - 13 + - 14 + + # North Underwater Boulder Rupee (Yellow 1) + - action: patch + name: item + parameters: 0x133A8D03 + position: + x: 141.887588500977 + y: -8844.4091796875 + z: 8355.388671875 + angle: + x: 0x0000 + y: 0x8000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x133AFF03 + layers: + - 0 + - 2 + - 13 + - 14 + + # North Underwater Boulder Rupee (Yellow 2) + - action: patch + name: item + parameters: 0x133A8D03 + position: + x: 15.3052892684937 + y: -8880.763671875 + z: 8404.501953125 + angle: + x: 0x0000 + y: 0x8000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x133AFF03 + layers: + - 0 + - 2 + - 13 + - 14 + + # Shortcut Lower Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0xF3248C03 + position: + x: -4717.2822265625 + y: -1214.41479492188 + z: 6721.99853515625 + angle: + x: 0x0000 + y: 0x4000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF324FF03 + layers: + - 0 + - 2 + - 13 + - 14 + + # Shortcut Lower Boulder Rupee (Green 1) + - action: patch + name: item + parameters: 0xF3248C01 + position: + x: -4708.02392578125 + y: -1097.32348632812 + z: 6647.25830078125 + angle: + x: 0x0000 + y: 0x4000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF324FF01 + layers: + - 0 + - 2 + - 13 + - 14 + + # Shortcut Lower Boulder Rupee (Green 2) + - action: patch + name: item + parameters: 0xF3248C01 + position: + x: -4733.75 + y: -1215.96069335938 + z: 6784.78125 + angle: + x: 0x0000 + y: 0x4000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF324FF01 + layers: + - 0 + - 2 + - 13 + - 14 + + # Shortcut Upper Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0xF3658B03 + position: + x: -3786.8876953125 + y: -693.817016601562 + z: 6765.89501953125 + angle: + x: 0x0000 + y: 0x4000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF365FF03 + layers: + - 0 + - 2 + - 13 + - 14 + + # Shortcut Upper Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0xF3658B02 + position: + x: -3756.13256835937 + y: -551.098693847656 + z: 6694.25341796875 + angle: + x: 0x0000 + y: 0x4000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF365FF02 + layers: + - 0 + - 2 + - 13 + - 14 + + # Shortcut Upper Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0xF3658B02 + position: + x: -3769.923828125 + y: -614.487182617187 + z: 6888.47021484375 + angle: + x: 0x0000 + y: 0x4000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF365FF02 + layers: + - 0 + - 2 + - 13 + - 14 + + # Add hint sign on west ledge + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -2964.84839 + y: -2500.0 + z: 17146.2676 + angle: + x: 21119 # Flow node id + y: 0xAF09 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 2 + - 13 + - 14 + +# Snowpeak Province +F_SP114: + # Room 0 - Blizzard Area + 0: + # Add hint sign in front of blizzard area + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 15931.9238 + y: -14389.4912 + z: -17388.1992 + angle: + x: 21120 # Flow node id + y: 0x2C5A + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 4 + + # Room 1 - Snowboard Area + 1: + # Snowboarding Bridge Ledge Bottom Rupee + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -48119.171875 + y: 0 + z: -2599.25610351562 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x84) + parameters: 0xF3FF8401 + layers: + - 0 + - 3 + + # Snowboarding Bridge Ledge Middle Rupee + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -46534.28125 + y: 11544.2265625 + z: -8413.3955078125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x83) + parameters: 0xF3FF8301 + layers: + - 0 + - 3 + + # Snowboarding Bridge Ledge Upper Rupee + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -43177.66796875 + y: 15660.8828125 + z: -9569.9169921875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x82) + parameters: 0xF3FF8201 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 1 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -47645.078125 + y: 0 + z: 39380.15234375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x88) + parameters: 0xF3FF8801 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 2 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -46933.09375 + y: 0 + z: 40671.33984375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x89) + parameters: 0xF3FF8901 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 3 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -44577.45703125 + y: 0 + z: 47715.25390625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x8A) + parameters: 0xF3FF8A01 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 4 + - action: patch + name: item + parameters: 0xF3FFFF04 + position: + x: -44606.390625 + y: 0 + z: 48379.03125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x8B) + parameters: 0xF3FF8B04 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 5 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -44649.34765625 + y: 0 + z: 48967.97265625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x8C) + parameters: 0xF3FF8C01 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 6 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -44645.703125 + y: 0 + z: 50150.0078125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x8D) + parameters: 0xF3FF8D01 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 7 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -44480.6953125 + y: 0 + z: 51324.0078125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x8E) + parameters: 0xF3FF8E01 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 8 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -43814.9921875 + y: 0 + z: 55098.53125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x8F) + parameters: 0xF3FF8F01 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 9 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -43624.30078125 + y: 0 + z: 57275.41796875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x90) + parameters: 0xF3FF9001 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 10 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -43662.1171875 + y: 0 + z: 59659.77734375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x91) + parameters: 0xF3FF9101 + layers: + - 0 + - 3 + + # Snowboarding Shortcut Rupee 11 + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -44644.55859375 + y: 0 + z: 62885.18359375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x92) + parameters: 0xF3FF9201 + layers: + - 0 + - 3 + + # Snowboarding Snowy Tree Top Rupee 1 (Blue) + - action: patch + name: item + parameters: 0xF3FFFF02 + position: + x: -48988.61328125 + y: -27072.4296875 + z: 22625.138671875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x85) + parameters: 0xF3FF8502 + layers: + - 0 + - 3 + + # Snowboarding Snowy Tree Top Rupee 2 (Red) + - action: patch + name: item + parameters: 0xF3FFFF04 + position: + x: -49091.05859375 + y: 0 + z: 25205.296875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x86) + parameters: 0xF3FF8604 + layers: + - 0 + - 3 + + # Snowboarding Snowy Tree Top Rupee 3 (Purple) + - action: patch + name: item + parameters: 0xF3FFFF05 + position: + x: -49256 + y: 0 + z: 27146.310546875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x87) + parameters: 0xF3FF8705 + layers: + - 0 + - 3 + + # Snowboarding Top Left Rupee + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -33332.109375 + y: 15760.8828125 + z: -5062.1845703125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x80) + parameters: 0xF3FF8001 + layers: + - 0 + - 3 + + # Snowboarding Top Right Rupee + - action: patch + name: item + parameters: 0xF3FFFF01 + position: + x: -36254.51953125 + y: 15760.8828125 + z: -13958.623046875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Give this rupee a unique flag (0x81) + parameters: 0xF3FF8101 + layers: + - 0 + - 3 + +# Lake Hylia +F_SP115: + # Room 0 - Main Lake + 0: + # Chest at the top of isle of riches + - action: patch + name: tboxEL1 + parameters: 0x00000106 + position: + x: -102523.015625 + y: -16646.677734375 + z: 43291.4296875 + angle: + x: 0x0000 + y: 0xA16D + z: 0x0000 + set id: 0xFFFF + patch: # Give it a unique tbox id + parameters: 0x00190106 + layers: + - 1 + - 2 + - 3 + - 4 + + # Left Underwater Boulder Rupee (Yellow 1) + - action: patch + name: item + parameters: 0x13599A03 + position: + x: -94876.2734375 + y: -32004.568359375 + z: 40183.83203125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1359FF03 + layers: + - 1 + - 2 + - 3 + + # Left Underwater Boulder Rupee (Yellow 2) + - action: patch + name: item + parameters: 0x13599A03 + position: + x: -94707.8203125 + y: -32004.568359375 + z: 40305.67578125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1359FF03 + layers: + - 1 + - 2 + - 3 + + # Left Underwater Boulder Rupee (Yellow 3) + - action: patch + name: item + parameters: 0x13599A03 + position: + x: -94790.046875 + y: -32004.568359375 + z: 40022.875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1359FF03 + layers: + - 1 + - 2 + - 3 + + # Left Underwater Boulder Rupee (Blue) + - action: patch + name: item + parameters: 0x13599A02 + position: + x: -94650.5 + y: -31915.41015625 + z: 40132.12109375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1359FF02 + layers: + - 1 + - 2 + - 3 + + # Right Underwater Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0x13589B03 + position: + x: -92246.28125 + y: -31720.103515625 + z: 41678.73828125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1358FF03 + layers: + - 1 + - 2 + - 3 + + # Right Underwater Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0x13589B02 + position: + x: -92077.8359375 + y: -31720.103515625 + z: 41800.58203125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1358FF02 + layers: + - 1 + - 2 + - 3 + + # Right Underwater Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0x13589B02 + position: + x: -92156.6796875 + y: -31720.103515625 + z: 41553.30078125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1358FF02 + layers: + - 1 + - 2 + - 3 + + # Spawn Auru on layers 1 & 3 + - action: add + name: Rafrel + parameters: 0x00001D01 + position: + x: -116486.945 + y: -13860.0 + z: 58533.0078 + angle: + x: 0x0000 + y: 0xCCCD + z: 0x0000 + set id: 0xFFFF + layers: + - 1 + - 3 + + # Spawn a red rupee behind the canon so players always have + # enough money for it + - action: add + name: item + parameters: 0xF3FFFF04 + position: + x: -108290.086 + y: -18654.748 + z: 45935.2969 + angle: + x: 0x0000 + y: 0x0001 + z: 0x003F + set id: 0xFFFF + layers: + - 1 + - 2 + - 3 + + # Add hint sign in cucco shack + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -109203.461 + y: -7220.0 + z: 33083.7344 + angle: + x: 21115 # Flow node id + y: 0x64B5 + z: 0x0000 + set id: 0xFFFF + layers: + - 1 + - 2 + - 3 + - 13 + - 14 + + # Room 1 - Lanayru Spring + 1: + # Lower Underwater Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0x131D8F03 + position: + x: -770.449157714844 + y: -1573.41650390625 + z: -357.485656738281 + angle: + x: 0x0000 + y: 0x8000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x131DFF03 + layers: + - 0 + - 2 + - 14 + + # Lower Underwater Boulder Rupee (Blue) + - action: patch + name: item + parameters: 0x131D8F02 + position: + x: -740.873229980469 + y: -1604.72351074219 + z: -263.534973144531 + angle: + x: 0x0000 + y: 0x8000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x131DFF02 + layers: + - 0 + - 2 + - 14 + + # Lower Underwater Boulder Rupee (Green 1) + - action: patch + name: item + parameters: 0x131D8F01 + position: + x: -657.096984863281 + y: -1594.28796386719 + z: -292.812927246094 + angle: + x: 0x0000 + y: 0x8000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x131DFF01 + layers: + - 0 + - 2 + - 14 + + # Lower Underwater Boulder Rupee (Green 2) + - action: patch + name: item + parameters: 0x131D8F01 + position: + x: -787.852966308594 + y: -1514.28125 + z: -297.316223144531 + angle: + x: 0x0000 + y: 0x8000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x131DFF01 + layers: + - 0 + - 2 + - 14 + + # Upper Underwater Boulder Rupee (Yellow 1) + - action: patch + name: item + parameters: 0x13399003 + position: + x: -1201.95251464844 + y: -1100.27172851562 + z: 1692.37805175781 + angle: + x: 0x0000 + y: 0x56C1 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1339FF03 + layers: + - 0 + - 2 + - 14 + + # Upper Underwater Boulder Rupee (Yellow 2) + - action: patch + name: item + parameters: 0x13399003 + position: + x: -1157.26696777344 + y: -1148.73376464844 + z: 1796.97399902344 + angle: + x: 0x0000 + y: 0x56C1 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1339FF03 + layers: + - 0 + - 2 + - 14 + + # Upper Underwater Boulder Rupee (Green) + - action: patch + name: item + parameters: 0x13399001 + position: + x: -1095.27661132812 + y: -1106.08728027344 + z: 1748.41162109375 + angle: + x: 0x0000 + y: 0x56C1 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1339FF01 + layers: + - 0 + - 2 + - 14 + + # Add hint sign underwater + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -309.997833 + y: -1614.82178 + z: 157.970795 + angle: + x: 21116 # Flow node id + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 2 + - 14 + +# Castle Town +F_SP116: + # Room 0 - Central castle Town + 0: + # Shoe shiner in front of Malo Mart + - action: patch + name: shoe + parameters: 0x0434C864 + position: + x: 1031.68518066406 + y: -181.089660644531 + z: 2604.8818359375 + angle: + x: 0x0000 + y: 0x905C + z: 0x0000 + set id: 0xFFFF + patch: + # Make the shoe shiner always appear + parameters: 0x0434F000 + layers: + - 0 + + # Add hint sign south of the central fountain + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 0.0 + y: -200.0 + z: 835.0 + angle: + x: 21112 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + + # Room 1 - North Castle Town + 1: + # Golden Wolf in North Castle Town + - action: patch + name: GWolf + parameters: 0x013207FF + position: + x: 0.0 + y: 800.0 + z: -9500.0 + angle: + x: 0x0BD1 + y: 0x0000 + z: 0x00FF + set id: 0xFFFF + patch: + # Turn the golden wolf into a htPiece actor with the hidden skill item + name: htPiece + parameters: 0xFFFFFFE1 + layers: + - 1 + +# Sacred Grove +F_SP117: + # Room 1 - Pedestal of Time + 1: + # Male Snail + - action: patch + name: I_Kat + parameters: 0x00000F00 + position: + x: 1447.40808105469 + y: 1533.73156738281 + z: 7053.470703125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x99 + name: htPiece + parameters: 0x00FF99D0 + position: + y: 1383 + layers: + - 2 + + # Door to the past + - action: patch + name: smgdoor + parameters: 0x064010FF + position: + x: 0.0 + y: 1725.0 + z: 6900.0 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Give the door a custom flag not tied to the portal + parameters: 0x063010FF + layers: + - 2 + + # Statue guarding door to the past + - action: patch + name: Sekizoa + parameters: 0x3000EE64 + position: + x: 0.0 + y: 1725.0 + z: 7020.0 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Give the statue a custom flag not tied to the portal + parameters: 0x3000EE63 + layers: + - 2 + + # Msg Tag for striking pedestal + - action: patch + name: KMsg + parameters: 0x03AA96C7 + position: + x: 0.0 + y: 1700.0 + z: -5435.0 + angle: + x: 0x8064 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + scale: + x: 0x23 + y: 0x23 + z: 0x23 + patch: + # Give the tag a custom flag not tied to the portal + angle: + x: 0x80FF + layers: + - 2 + + # Spawn in the Master Sword actor + - action: add + name: mstrsrd + parameters: 0x00020110 + position: + x: 0.0 + y: 1700.0 + z: -5435.0 + angle: + x: 0x0147 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 2 + + # Delete shadow beast fight walls + - action: delete + name: Obj_tp + parameters: 0xEF00FF01 + position: + x: 1445.53735351562 + y: 3895.68212890625 + z: -3593.36694335937 + angle: + x: 0x0000 + y: 0x0000 + z: 0xFF64 + set id: 0xFFFF + layers: + - 2 + + # Add hint sign near Door to the Past + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -1543.0 + y: 1725.0 + z: 7964.85498 + angle: + x: 21102 # Flow node id + y: 0x4000 + z: 0x0000 + set id: 0xFFFF + layers: + - 2 + + # Room 2 - Temple of Time + 2: + # Female Snail + - action: patch + name: I_Kat + parameters: 0x00000F10 + position: + x: 405.664825439453 + y: 1382.14660644531 + z: 6069.1953125 + angle: + x: 0x0000 + y: 0x5D27 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x98 + name: htPiece + parameters: 0x00FF98D1 + layers: + - 2 + +# Outside Arbiters Grounds +F_SP118: + # Room 1 - Bulblin Camp + 1: + # Small Key Actor + - action: patch + name: Obj_key + parameters: 0xFFFFFFFF + position: + x: 4470 + y: 300 + z: -2950 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this small key into the rando check + name: htPiece + parameters: 0x00FF9AD7 + position: + x: 4000 + z: -3500 + layers: + - 0 + + # Spawn in the item for the Bulblin Guard Key on + # the layer where the camp is already beaten + - action: add + name: htPiece + parameters: 0x00FF9AD7 + position: + x: 4000 + y: 300 + z: -3500 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 1 + - 2 + - 3 + + # Chest in front of bulblin camp + - action: patch + name: tboxA0 + parameters: 0xFF0FFFC0 + position: + x: 4761.640625 + y: 0 + z: 1580.57019042969 + angle: + x: 0x0000 + y: 0xE000 + z: 0x0FFF + set id: 0xFFFF + patch: + # Give this chest a unique tboxid + parameters: 0xFF0FF7C0 + layers: + - 0 + - 1 + - 2 + - 3 + + # Chest in back of bulblin camp + - action: patch + name: tboxA0 + parameters: 0xFF0FFFC0 + position: + x: 2389.04541015625 + y: 260 + z: -1473.38720703125 + angle: + x: 0x0000 + y: 0x6000 + z: 0x05FF + set id: 0xFFFF + patch: + # Give this chest a unique tboxid + parameters: 0xFF0FF780 + layers: + - 0 + - 1 + - 2 + - 3 + + # Gate to King Bublin 2 Fight + - action: patch + name: CrvGate + parameters: 0xFFFFFFFF + position: + x: 2150.0 + y: 0.0 + z: -450.0 + angle: + x: 0x0000 + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + patch: + # Give the gate a unique flag (0x00) so + # it stays permanently open once it's been unlocked + parameters: 0xFFFF00FF + layers: + - 0 + + # Add hint sign before jump down to lower desert + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -568.556152 + y: 260.0 + z: -3969.31 + angle: + x: 21122 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Hyrule Field +F_SP121: + # Room 0 - Eldin Field / Bridge of Eldin + 0: + # Male Grasshopper + - action: patch + name: I_Bat + parameters: 0x00000F00 + position: + x: 19600 + y: 648.359558105469 + z: 13400 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x99 + name: htPiece + parameters: 0x00FF99C2 + layers: + - 0 + - 6 + - 8 + + # Female Grasshopper + - action: patch + name: I_Bat + parameters: 0x00000F10 + position: + x: -9950 + y: 35.1765174865723 + z: -8950 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x98 + name: htPiece + parameters: 0x00FF98C3 + layers: + - 0 + - 6 + - 8 + + # Male Phasmid + - action: patch + name: I_Nan + parameters: 0x00001F00 + position: + x: 35135 + y: 254.385009765625 + z: -15115.2587890625 + angle: + x: 0xC000 + y: 0x4000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x97 + name: htPiece + parameters: 0x00FF97C8 + layers: + - 0 + - 6 + - 8 + + # Female Phasmid + - action: patch + name: I_Nan + parameters: 0x00001F10 + position: + x: 39919.859375 + y: 1415.41796875 + z: -40367.38671875 + angle: + x: 0xC000 + y: 0x58E3 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x96 + name: htPiece + parameters: 0x00FF96C9 + layers: + - 0 + - 6 + - 8 + + # Yellow Rupee under BoE Boulder 1 + - action: patch + name: item + parameters: 0xF3778F03 + position: + x: 37400 + y: -39.2301330566406 + z: -40200 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF377FF03 + layers: + - 0 + - 6 + - 8 + - 14 + + # Yellow Rupee under BoE Boulder 2 + - action: patch + name: item + parameters: 0xF3778F03 + position: + x: 37500 + y: -91.5920944213867 + z: -39900 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF377FF03 + layers: + - 0 + - 6 + - 8 + - 14 + + # Add hint sign next to Eldin field south small bridge + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -12433.2744 + y: -1075.2218 + z: 20885.7129 + angle: + x: 21107 # Flow node id + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 6 + - 8 + - 14 + + # Room 3 - Kakariko Gorge + 3: + # Male Pill Bug + - action: patch + name: I_Dan + parameters: 0x00000F00 + position: + x: -10000 + y: -7200 + z: 57450 + angle: + x: 0x0000 + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x95 + name: htPiece + parameters: 0x00FF95CA + layers: + - 0 + - 6 + + # Female Pill Bug + - action: patch + name: I_Dan + parameters: 0x00000F10 + position: + x: 500 + y: -6117.943359375 + z: 61700 + angle: + x: 0x0000 + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x94 + name: htPiece + parameters: 0x00FF94CB + layers: + - 0 + - 6 + + # Blue Rupee under Gorge Spire Boulder 1 + - action: patch + name: item + parameters: 0xF37A8E02 + position: + x: -25742.5 + y: -5147.73876953125 + z: 55865.015625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF37AFF02 + layers: + - 0 + - 6 + - 14 + + # Blue Rupee under Gorge Spire Boulder 2 + - action: patch + name: item + parameters: 0xF37A8E02 + position: + x: -25811.0703125 + y: -5130.3076171875 + z: 56205.63671875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF37AFF02 + layers: + - 0 + - 6 + - 14 + + # Green Rupee under Gorge Spire Boulder + - action: patch + name: item + parameters: 0xF37A8E01 + position: + x: -25676.435546875 + y: -5174.85400390625 + z: 56052.64453125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF37AFF01 + layers: + - 0 + - 6 + - 14 + + # Blue Rupee under Gorge Statue Boulder 1 + - action: patch + name: item + parameters: 0xF3788D02 + position: + x: -14013.876953125 + y: -4640.60107421875 + z: 45103.53515625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF378FF02 + layers: + - 0 + - 6 + - 14 + + # Blue Rupee under Gorge Statue Boulder 2 + - action: patch + name: item + parameters: 0xF3788D02 + position: + x: -13793.6083984375 + y: -4736.62060546875 + z: 45056.8984375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF378FF02 + layers: + - 0 + - 6 + - 14 + + # Blue Rupee under Gorge Statue Boulder 3 + - action: patch + name: item + parameters: 0xF3788D02 + position: + x: -13833.4169921875 + y: -4700.61328125 + z: 45221.265625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF378FF02 + layers: + - 0 + - 6 + - 14 + + # Blue Rupee under Gorge Statue Boulder 4 + - action: patch + name: item + parameters: 0xF3788D02 + position: + x: -13939.875 + y: -4664.60595703125 + z: 45198.17578125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0xF378FF02 + layers: + - 0 + - 6 + - 14 + + # Add Epona Jump to post MDH layers + - action: add + name: Hjump + parameters: 0x044FFF02 + position: + x: 5600 + y: -5680 + z: 52055 + angle: + x: 0x0000 + y: 0x4000 + z: 0x0000 + set id: 0xFFFF + scale: + x: 32 + y: 45 + z: 45 + layers: + - 2 + - 6 + + # Add hint sign near owl statue + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -10116.0215 + y: -4923.46191 + z: 43064.4375 + angle: + x: 21104 # Flow node id + y: 0x0789 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 6 + - 14 + + # Room 6 - Faron Field + 6: + # Male Beetle + - action: patch + name: kab_o + parameters: 0x00000F00 + position: + x: -50200 + y: -8810 + z: 86400 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x9F + name: htPiece + parameters: 0x00FF9FC0 + position: + y: -8910 # Lower by 100 so Link can reach it + layers: + - 0 + - 6 + - 9 + - 10 + + # Female Beetle + - action: patch + name: kab_o + parameters: 0x00000F10 + position: + x: -36490 + y: -8100 + z: 74330 + angle: + x: 0x0000 + y: 0x2000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x9E + name: htPiece + parameters: 0x00FF9EC1 + layers: + - 0 + - 6 + - 9 + - 10 + + # Spawn coming from Outside South Castle Town + - action: patch + name: Link + parameters: 0xFF00503F + position: + x: -47839.47265625 + y: -9337.5810546875 + z: 52352.32421875 + angle: + x: 0x0000 + y: 0xF334 + z: 0x0001 + set id: 0xFFFF + patch: + # Change spawn to not be inside rocks if the rocks aren't broken + position: + x: -48296.46875 + y: -9428.5810546875 + z: 53030.3203125 + layers: + - 0 + - 6 + - 9 + - 10 + + # Add hint sign under bridge + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -46039.4922 + y: -9250.0 + z: 81859.2891 + angle: + x: 21103 # Flow node id + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 6 + - 9 + - 10 + + # Room 7 - North Eldin + 7: + # Add Ganon Barriers to block Lanayru Twilight access + # during Eldin Twilight + - action: add + name: Obj_gb + parameters: 0x800F0601 + position: + x: 10778.207 + y: 3096.82666 + z: -62651.0078 + angle: + x: 0xFF5C + y: 0x4000 + z: 0x0000 + set id: 0xFFFF + layers: + - 14 # Twilight layer + + - action: add + name: Obj_gb + parameters: 0x800F0601 + position: + x: 10778.207 + y: 3096.82666 + z: -62921.0078 + angle: + x: 0xFF5C + y: 0x4000 + z: 0x0000 + set id: 0xFFFF + layers: + - 14 # Twilight layer + + - action: add + name: Obj_gb + parameters: 0x800F0601 + position: + x: 10778.207 + y: 3096.82666 + z: -63191.0078 + angle: + x: 0xFF5C + y: 0x4000 + z: 0x0000 + set id: 0xFFFF + layers: + - 14 # Twilight layer + + # Add hint sign next to North Eldin Field spinner track + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 18468.8418 + y: 1580.0 + z: -63560.9531 + angle: + x: 21108 # Flow node id + y: 0xCDC6 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 6 + - 8 + - 14 + + # Room 10 - Lanayru Field + 10: + # Male Stag Beetle + - action: patch + name: I_Kuw + parameters: 0x00000F00 + position: + x: -62724.33203125 + y: -882.901000976563 + z: -33130.4140625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x9B + name: htPiece + parameters: 0x00FF9BC4 + layers: + - 0 + - 6 + + # Female Stag Beetle + - action: patch + name: I_Kuw + parameters: 0x00000F10 + position: + x: -48850 + y: 59.1445922851562 + z: -51350 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x9A + name: htPiece + parameters: 0x00FF9AC5 + layers: + - 0 + - 6 + + # South Underwater Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0x137F8603 + position: + x: -44868.2109375 + y: -4623.99853515625 + z: -29872.623046875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0001 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0x137FFF03 + layers: + - 0 + - 6 + - 13 + - 14 + + # South Underwater Boulder Rupee (Green) + - action: patch + name: item + parameters: 0x137F8601 + position: + x: -44725.27734375 + y: -4658.63720703125 + z: -29682.888671875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0001 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0x137FFF01 + layers: + - 0 + - 6 + - 13 + - 14 + + # South Underwater Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0x137F8602 + position: + x: -44813.50390625 + y: -4589.35986328125 + z: -29537.416015625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0001 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0x137FFF02 + layers: + - 0 + - 6 + - 13 + - 14 + + # South Underwater Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0x137F8602 + position: + x: -44999.03125 + y: -4675.9560546875 + z: -29785.939453125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0001 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0x137FFF02 + layers: + - 0 + - 6 + - 13 + - 14 + + # North Underwater Boulder Rupee (Red 1) + - action: patch + name: item + parameters: 0x137E8704 + position: + x: -45730.50390625 + y: -3624.84008789063 + z: -40552.89453125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0001 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0x137EFF04 + layers: + - 0 + - 6 + - 13 + - 14 + + # North Underwater Boulder Rupee (Red 1) + - action: patch + name: item + parameters: 0x137E8704 + position: + x: -45478.16015625 + y: -3763.39453125 + z: -40783.9609375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0001 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0x137EFF04 + layers: + - 0 + - 6 + - 13 + - 14 + + # North Spinner Track Boulder Rupee (Yellow 1) + - action: patch + name: item + parameters: 0xF30F8903 + position: + x: -76250 + y: 750 + z: -32100 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF30FFF03 + layers: + - 0 + - 6 + - 13 + - 14 + + # North Spinner Track Boulder Rupee (Yellow 2) + - action: patch + name: item + parameters: 0xF30F8903 + position: + x: -75500 + y: 700 + z: -31700 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF30FFF03 + layers: + - 0 + - 6 + - 13 + - 14 + + # Add hint sign at path fork in west Lanayru Field + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -64943.7422 + y: -1359.66711 + z: -31897.8828 + angle: + x: 21110 # Flow node id + y: 0xE223 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 6 + - 13 + - 14 + + # Room 12 - Hylia Bridge-Lanayru Field Transition + 12: + # South Spinner Track Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0xF30E8A02 + position: + x: -93650 + y: -2800 + z: 5000 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF30EFF02 + layers: + - 0 + - 6 + - 13 + - 14 + + # South Spinner Track Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0xF30E8A02 + position: + x: -93400 + y: -2850 + z: 4850 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF30EFF02 + layers: + - 0 + - 6 + - 13 + - 14 + + # South Spinner Track Boulder Rupee (Blue 3) + - action: patch + name: item + parameters: 0xF30E8A02 + position: + x: -93900 + y: -2800 + z: 4750 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF30EFF02 + layers: + - 0 + - 6 + - 13 + - 14 + + # Room 13 - Great Bridge of Hylia + 13: + # Male Mantis + - action: patch + name: I_Kam + parameters: 0x00000F00 + position: + x: -92900 + y: -5441.38720703125 + z: 30350 + angle: + x: 0x0000 + y: 0x4000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x93 + name: htPiece + parameters: 0x00FF93CC + layers: + - 0 + - 6 + + # Female Mantis + - action: patch + name: I_Kam + parameters: 0x00000F10 + position: + x: -88800 + y: -6091.95068359375 + z: 59050 + angle: + x: 0x0000 + y: 0xC000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x92 + name: htPiece + parameters: 0x00FF92CD + layers: + - 0 + - 6 + + # Boulder Near Owl Statue Rupee (Blue 1) + - action: patch + name: item + parameters: 0xF37C8B02 + position: + x: -89951.5546875 + y: -5034.109375 + z: 22045.078125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF37CFF02 + layers: + - 0 + - 6 + - 13 + - 14 + + # Boulder Near Owl Statue Rupee (Blue 2) + - action: patch + name: item + parameters: 0xF37C8B02 + position: + x: -89746.765625 + y: -4973.77587890625 + z: 21901.685546875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF37CFF02 + layers: + - 0 + - 6 + - 13 + - 14 + + # Boulder Near Owl Statue Rupee (Blue 3) + - action: patch + name: item + parameters: 0xF37C8B02 + position: + x: -89632.046875 + y: -5076.08056640625 + z: 22065.515625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF37CFF02 + layers: + - 0 + - 6 + - 13 + - 14 + + # Boulder Near Faron Rupee (Yellow) + - action: patch + name: item + parameters: 0xF37B8C03 + position: + x: -78349.703125 + y: -7331.6181640625 + z: 58471.4375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0005 + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF37BFF03 + layers: + - 0 + - 6 + - 13 + - 14 + + # Add hint sign on platform above owl statue + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -94678.8672 + y: -3900.0 + z: 18410.543 + angle: + x: 21114 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 6 + - 13 + - 14 + +# Outside Castle Town Maps +F_SP122: + # Room 8 - Outside West Castle Town + 8: + # Male Butterfly + - action: patch + name: I_Cho + parameters: 0x00000F00 + position: + x: -75063.46875 + y: -421.140930175781 + z: 16017.3466796875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x9D + name: htPiece + parameters: 0x00FF9DC2 + layers: + - 0 + + # Male Butterfly again (for some reason the one on layer 6 has a different x position) + - action: patch + name: I_Cho + parameters: 0x00000F00 + position: + x: -75063.0546875 + y: -421.140930175781 + z: 16017.3466796875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x9D + name: htPiece + parameters: 0x00FF9DC2 + layers: + - 6 + + # Female Butterfly + - action: patch + name: I_Cho + parameters: 0x00000F10 + position: + x: -79687.9609375 + y: -656.609985351562 + z: 1544.75891113281 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x9C + name: htPiece + parameters: 0x00FF9CCD + layers: + - 0 + - 6 + + # Golden Wolf Outside Castle Town West + - action: patch + name: GWolf + parameters: 0x042903FF + position: + x: -68310.15625 + y: -1050.0 + z: 5925.4560546875 + angle: + x: 0x0BD1 + y: 0xC16D + z: 0x00FF + set id: 0xFFFF + patch: + # Turn the golden wolf into a htPiece actor with the hidden skill item + name: htPiece + parameters: 0xFFFFFFE1 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Northern Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0xF34B8403 + position: + x: -71091.9375 + y: -1082.32336425781 + z: -7070.81005859375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34BFF03 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Northern Boulder Rupee (Green 1) + - action: patch + name: item + parameters: 0xF34B8401 + position: + x: -71149.3515625 + y: -1131.98156738281 + z: -6873.412109375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34BFF01 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Northern Boulder Rupee (Green 2) + - action: patch + name: item + parameters: 0xF34B8401 + position: + x: -71028.1640625 + y: -1086.46154785156 + z: -6809.62451171875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34BFF01 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Northern Boulder Rupee (Green 3) + - action: patch + name: item + parameters: 0xF34B8401 + position: + x: -70900.6015625 + y: -1007.83605957031 + z: -6860.65478515625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34BFF01 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Northern Boulder Rupee (Blue) + - action: patch + name: item + parameters: 0xF34B8402 + position: + x: -70951.6328125 + y: -1090.59973144531 + z: -7000.9873046875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34BFF02 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Southern Boulder Rupee (Yellow 1) + - action: patch + name: item + parameters: 0xF34C8503 + position: + x: -71926.4140625 + y: -1947.20300292969 + z: -542.079528808594 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34BFF03 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Southern Boulder Rupee (Yellow 2) + - action: patch + name: item + parameters: 0xF34C8503 + position: + x: -71668.03125 + y: -1902.7158203125 + z: -393.373565673828 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34BFF03 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Southern Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0xF34C8502 + position: + x: -71791.0859375 + y: -1996.86120605469 + z: -319.543182373047 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34BFF02 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Southern Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0xF34C8502 + position: + x: -71758.1796875 + y: -1955.47937011719 + z: -564.430725097656 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34BFF02 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Add hint sign on platform with helmasaur grotto + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -80699.5 + y: -765.58374 + z: 1903.61462 + angle: + x: 21111 # Flow node id + y: 0x25B7 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 6 + - 13 + - 14 + + # Room 16 - Outside South Castle Town + 16: + # Male Ladybug + - action: patch + name: I_Ten + parameters: 0x00000F00 + position: + x: -45085.2734375 + y: -5971.20849609375 + z: 29910.453125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x91 + name: htPiece + parameters: 0x00FF91CE + position: + y: -6081 # Lower the item so it isn't awkwardly floating + layers: + - 0 + - 1 + - 6 + + # Female Ladybug + - action: patch + name: I_Ten + parameters: 0x00000F10 + position: + x: -54382.18359375 + y: -5481.6298828125 + z: 27425.111328125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x90 + name: htPiece + parameters: 0x00FF90CF + layers: + - 0 + - 1 + - 6 + + # Golden Wolf Outside Castle Town South + - action: patch + name: GWolf + parameters: 0x022A04FF + position: + x: -55927.09375 + y: -6100.0 + z: 25315.892578125 + angle: + x: 0x0BD1 + y: 0xE9F5 + z: 0x00FF + set id: 0xFFFF + patch: + # Turn the golden wolf into a htPiece actor with the hidden skill item + name: htPiece + parameters: 0xFFFFFFE1 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Boulder Rupee (Green 1) + - action: patch + name: item + parameters: 0xF34A8301 + position: + x: -46447.33984375 + y: -5826.849609375 + z: 26237.958984375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34AFF01 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Boulder Rupee (Green 2) + - action: patch + name: item + parameters: 0xF34A8301 + position: + x: -46278.0234375 + y: -5826.849609375 + z: 26476.99609375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34AFF01 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Boulder Rupee (Green 3) + - action: patch + name: item + parameters: 0xF34A8301 + position: + x: -46596.7421875 + y: -5826.849609375 + z: 26277.798828125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34AFF01 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0xF34A8302 + position: + x: -46317.86328125 + y: -5826.849609375 + z: 26138.361328125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34AFF02 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0xF34A8302 + position: + x: -46576.8203125 + y: -5826.849609375 + z: 26486.955078125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34AFF02 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Boulder Rupee (Blue 3) + - action: patch + name: item + parameters: 0xF34A8302 + position: + x: -46377.62890625 + y: -5826.849609375 + z: 26357.4765625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34AFF02 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0xF34A8303 + position: + x: -46359.03515625 + y: -5826.849609375 + z: 26254.951171875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x003F + set id: 0xFFFF + patch: + # Take away the flag from this item so it doesn't conflict + # with the nearby randomized item + parameters: 0xF34AFF03 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Spawn coming from Faron Field + - action: patch + name: Link + parameters: 0xFF00503F + position: + x: -50958.37109375 + y: -7010.82958984375 + z: 38985.1875 + angle: + x: 0x0000 + y: 0x8AAB + z: 0x0000 + set id: 0xFFFF + patch: + # Change spawn to not be inside rocks if the rocks aren't broken + position: + x: -51369.37109375 + y: -6698.830078125 + z: 37038.19140625 + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + + # Add hint sign south of the fountain + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -51500.0 + y: -5500.0 + z: 27368.3086 + angle: + x: 21113 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 4 + - 6 + - 13 + - 14 + +# Gerudo Desert +F_SP124: + # Room 0 - Main Desert + 0: + # Male Dayfly + - action: patch + name: I_Kag + parameters: 0x00000F0F + position: + x: 29089.78125 + y: 412.155059814453 + z: 58985.57421875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x99 + name: htPiece + parameters: 0x00FF99D6 + layers: + - 0 + + # Female Dayfly + - action: patch + name: I_Kag + parameters: 0x00000F1F + position: + x: 11656.46484375 + y: 141.179977416992 + z: 55374.140625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x98 + name: htPiece + parameters: 0x00FF98D7 + layers: + - 0 + + # Golden Wolf in Desert + - action: patch + name: GWolf + parameters: 0x0B3205FF + position: + x: 1112.08215332031 + y: -162.940002441406 + z: 12659.6689453125 + angle: + x: 0x0BD1 + y: 0x0000 + z: 0x00FF + set id: 0xFFFF + patch: + # Turn the golden wolf into a htPiece actor with the hidden skill item + name: htPiece + parameters: 0xFFFFFFE1 + layers: + - 0 + + # Add hint sign before jump down to lower desert + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 20356.23 + y: 556.7 + z: 38694.8047 + angle: + x: 21121 # Flow node id + y: 0x0099 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + +# Mirror Chamber +F_SP125: + # Room 4 - Main Chamber + 4: + # Add barrier to prevent players from going back to the Arbiters Boss Room + # depending on settings + - action: add + name: Obj_gb + parameters: 0x800F0601 + position: + x: 1794.0 + y: 2523.0 + z: -17400.0 + angle: + x: 0xFF7F + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 1 + +# Upper Zora's River +F_SP126: + # Room 0 - Main area + 0: + # Female Dragonfly + - action: patch + name: I_Tom + parameters: 0x00000F10 + position: + x: 5849.5498046875 + y: 80.7757110595703 + z: -1121.2275390625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x9E + name: htPiece + parameters: 0x00FF9ED3 + layers: + - 0 + - 1 + + # Central Underwater Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0x13609102 + position: + x: 1391.9443359375 + y: -1868.77282714844 + z: -1002.97778320312 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1360FF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # Central Underwater Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0x13609102 + position: + x: 1491.43737792969 + y: -1934.05505371094 + z: -802.984436035156 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1360FF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # Central Underwater Boulder Rupee (Blue 3) + - action: patch + name: item + parameters: 0x13609102 + position: + x: 1346.45397949219 + y: -1874.81726074219 + z: -796.263793945313 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1360FF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # Central Underwater Boulder Rupee (Green) + - action: patch + name: item + parameters: 0x13609101 + position: + x: 1533.86730957031 + y: -1853.056640625 + z: -1013.22180175781 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1360FF01 + layers: + - 0 + - 1 + - 13 + - 14 + + # Central Underwater Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0x13609103 + position: + x: 1465.11157226562 + y: -1776.89404296875 + z: -915.513854980469 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1360FF03 + layers: + - 0 + - 1 + - 13 + - 14 + + # West Underwater Boulder Rupee (Yellow 1) + - action: patch + name: item + parameters: 0x135F9203 + position: + x: -909.917846679688 + y: -1426.30310058594 + z: 1181.94140625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x135FFF03 + layers: + - 0 + - 1 + - 13 + - 14 + + # West Underwater Boulder Rupee (Yellow 2) + - action: patch + name: item + parameters: 0x135F9203 + position: + x: -836.750610351562 + y: -1334.42431640625 + z: 1269.40539550781 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x135FFF03 + layers: + - 0 + - 1 + - 13 + - 14 + + # West Underwater Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0x135F9202 + position: + x: -810.4248046875 + y: -1491.58532714844 + z: 1381.93481445312 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x135FFF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # West Underwater Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0x135F9202 + position: + x: -853.017944335938 + y: -1491.58532714844 + z: 1502.61547851562 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x135FFF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # West Underwater Boulder Rupee (Blue 3) + - action: patch + name: item + parameters: 0x135F9202 + position: + x: -648.732666015625 + y: -1410.5869140625 + z: 1270.46142578125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x135FFF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # West Underwater Boulder Rupee (Green 1) + - action: patch + name: item + parameters: 0x135F9201 + position: + x: -767.994873046875 + y: -1410.5869140625 + z: 1171.69738769531 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x135FFF01 + layers: + - 0 + - 1 + - 13 + - 14 + + # West Underwater Boulder Rupee (Green 2) + - action: patch + name: item + parameters: 0x135F9201 + position: + x: -955.408203125 + y: -1432.34753417969 + z: 1388.65539550781 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x135FFF01 + layers: + - 0 + - 1 + - 13 + - 14 + + # West Underwater Boulder Rupee (Green 3) + - action: patch + name: item + parameters: 0x135F9201 + position: + x: -605.872863769531 + y: -1410.5869140625 + z: 1397.17749023438 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x135FFF01 + layers: + - 0 + - 1 + - 13 + - 14 + + # East Underwater Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0x13549303 + position: + x: 3569.236328125 + y: -924.596496582031 + z: 4092.87475585937 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1354FF03 + layers: + - 0 + - 1 + - 13 + - 14 + + # East Underwater Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0x13549302 + position: + x: 3496.06909179687 + y: -1016.47528076172 + z: 4005.4111328125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1354FF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # East Underwater Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0x13549302 + position: + x: 3595.56201171875 + y: -1081.75744628906 + z: 4205.40283203125 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1354FF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # East Underwater Boulder Rupee (Green 1) + - action: patch + name: item + parameters: 0x13549301 + position: + x: 3637.99194335937 + y: -1000.75909423828 + z: 3995.16748046875 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1354FF01 + layers: + - 0 + - 1 + - 13 + - 14 + + # East Underwater Boulder Rupee (Green 1) + - action: patch + name: item + parameters: 0x13549301 + position: + x: 3450.57861328125 + y: -1022.51971435547 + z: 4212.12353515625 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1354FF01 + layers: + - 0 + - 1 + - 13 + - 14 + + # Ledge Boulder Rupee (Yellow) + - action: patch + name: item + parameters: 0x13679403 + position: + x: -11.2600412368774 + y: 587.779846191406 + z: -3185.00219726562 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1367FF03 + layers: + - 0 + - 1 + - 13 + - 14 + + # Ledge Boulder Rupee (Blue 1) + - action: patch + name: item + parameters: 0x13679402 + position: + x: -84.4272842407227 + y: 495.90087890625 + z: -3272.46630859375 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1367FF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # Ledge Boulder Rupee (Blue 2) + - action: patch + name: item + parameters: 0x13679402 + position: + x: 15.0657520294189 + y: 430.618682861328 + z: -3072.47290039063 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1367FF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # Ledge Boulder Rupee (Blue 3) + - action: patch + name: item + parameters: 0x13679402 + position: + x: -129.917633056641 + y: 489.8564453125 + z: -3065.75219726562 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1367FF02 + layers: + - 0 + - 1 + - 13 + - 14 + + # Ledge Boulder Rupee (Green) + - action: patch + name: item + parameters: 0x13679401 + position: + x: 57.4956970214844 + y: 511.617279052734 + z: -3282.71020507812 + angle: + x: 0x0000 + y: 0x0000 + z: 0x000A + set id: 0xFFFF + patch: + # Take away the unique flag from this rupee so it doesn't conflict + # with the one randomizer rupee nearby + parameters: 0x1367FF01 + layers: + - 0 + - 1 + - 13 + - 14 + +# Fishing Pond +F_SP127: + # Room 0 - Main Pond + 0: + # Add hint sign behind hut + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: -2924.74585 + y: 35.0 + z: 8386.28906 + angle: + x: 21118 # Flow node id + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 1 + - 2 + - 3 + - 14 + +# Hidden Village +F_SP128: + # Room 0 - Main Village + 0: + # Add hint sign + - action: add + name: Obj_kn2 + parameters: 0xFFFFFFFF + position: + x: 5161.03 + y: 0.0 + z: -5264.33 + angle: + x: 21109 # Flow node id + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 1 + - 2 + +# Kakariko Village Interiors +R_SP109: + # Room 2 - Elde Inn + 2: + # Delete 2nd Luda actor + - action: delete + name: Lud + parameters: 0x00FFFF07 + position: + x: -1350 + y: 0 + z: -900 + angle: + x: 0x0037 + y: 0x18E3 + z: 0x0000 + set id: 0xFFFF + layers: + - 2 + - 3 + + # Delete 2nd Colin actor + - action: delete + name: Kolin + parameters: 0x00FFFF07 + position: + x: -1150 + y: 0 + z: -800 + angle: + x: 0x0004 + y: 0xC38F + z: 0x0000 + set id: 0xFFFF + layers: + - 2 + - 3 + + # Room 3 - Kak Malo Mart + 3: + # Red Potion Shop item (left side) + - action: patch + name: TGSPITM + parameters: 0x01001E61 + position: + x: -550 + y: 450 + z: -500 + angle: + x: 0x014D + y: 0x0000 + z: 0x74FF + set id: 0xFFFF + patch: + # Change this into a sold out sign until the hawkeye is available + parameters: 0x01FFFFFF + angle: + x: 0x014B + y: 0x8000 + z: 0x0BFF + layers: + - 2 + - 3 + + # Hawkeye Shop Item (left side) + - action: patch + name: TGSPITM + parameters: 0x0100643E + position: + x: -550 + y: 450 + z: -500 + angle: + x: 0x0149 + y: 0x0000 + z: 0x3E33 + set id: 0xFFFF + patch: + # Give the hawkeye shop item a different bought flag + angle: + z: 0x3E3D + layers: + - 2 + - 3 + + # Wooden Shield Shop Item (middle) + - action: patch + name: TGSPITM + parameters: 0x0200322B + position: + x: -650 + y: 450 + z: -500 + angle: + x: 0x0143 + y: 0x0000 + z: 0xFFFF + set id: 0xFFFF + patch: + # Give the wooden shield item a different bought flag + angle: + z: 0xFF05 + layers: + - 2 + - 3 + + # Red Potion Shop item (Right Side) + - action: patch + name: TGSPITM + parameters: 0x03001E61 + position: + x: -750 + y: 450 + z: -500 + angle: + x: 0x014D + y: 0x8000 + z: 0x76FF + set id: 0xFFFF + patch: + # Give this item a unique flag, and make it set the + # flag for the right side sold out sign + angle: + z: 0x3904 + layers: + - 2 + - 3 + + # Right side Sold Out shop item + - action: patch + name: TGSPITM + parameters: 0x03FFFFFF + position: + x: -750 + y: 450 + z: -500 + angle: + x: 0x0147 + y: 0x8000 + z: 0x3976 + set id: 0xFFFF + patch: + # Give the sign a different set of flags + angle: + z: 0x04FF + layers: + - 2 + - 3 + + # Left side Sold Out Sign + - action: patch + name: TGSPITM + parameters: 0x01FFFFFF + position: + x: -550 + y: 450 + z: -500 + angle: + x: 0x014B + y: 0x8000 + z: 0x333D + set id: 0xFFFF + patch: + # Change the sign to the Hylian Shield sold out sign + angle: + x: 0x0147 + z: 0x33FF + layers: + - 2 + - 3 + + # Spawn in a middle item Sold Out Sign + - action: add + name: TGSPITM + parameters: 0x02FFFFFF + position: + x: -650.0 + y: 450.0 + z: -500.0 + angle: + x: 0x0147 + y: 0x8000 + z: 0x05FF + set id: 0xFFFF + layers: + - 2 + - 3 + + # Room 6 - Abandoned House + 6: + # Female Ant + - action: patch + name: I_Ari + parameters: 0x00000F10 + position: + x: 225.092803955078 + y: 16.1963920593262 + z: -17.4444351196289 + angle: + x: 0x0000 + y: 0x8000 + z: 0x0000 + set id: 0xFFFF + patch: + # Turn this bug into a htPiece actor with collectible flag 0x90 + name: htPiece + parameters: 0x00FF90D5 + layers: + - 0 + - 1 + - 2 + - 3 + +# Castle Town Shops +R_SP160: + # Jovani's House + 5: + # Spawn the poe in Jovani's House + - action: add + name: E_hp + parameters: 0xFF031E00 + position: + x: 4531.19 + y: -30.0 + z: 2631.961 + angle: + x: 0x0000 + y: 0x0000 + z: 0x0000 + set id: 0xFFFF + layers: + - 0 + - 1 diff --git a/mods/randomizer/generator/data/settings_list.yaml b/mods/randomizer/generator/data/settings_list.yaml new file mode 100644 index 0000000000..706f5e3bbc --- /dev/null +++ b/mods/randomizer/generator/data/settings_list.yaml @@ -0,0 +1,561 @@ +###################### +## Logic Settings ## +###################### + +- Name: Logic Rules + Default Option: All Locations Reachable + Options: + - All Locations Reachable: "Logic is considered when placing items. Tricks and Glitches can be enabled for consideration below." + - Beatable Only: "Logic is considered when placing items only until the world is beatable. Remaining items are placed without logic consideration. Tricks and Glitches can be enabled for consideration below." + - No Logic: "Maximize randomization, logic is not considered when placing items. MAY BE IMPOSSIBLE TO BEAT." + # - Vanilla: "Items are placed in their vanilla locations." + +###################### +## Access Options ## +###################### + +- Name: Hyrule Barrier Requirements + Need In Game: True + Tracker Important: True + Default Option: Vanilla + Options: + - Open: "The barrier around Hyrule Castle is dispelled from the beginning." + - Vanilla: "The barrier will be dispelled once Palace of Twilight is cleared." + - Fused Shadows: "The barrier will be dispelled once the required number of Fused Shadows have been collected." + - Mirror Shards: "The barrier will be dispelled once the required number of Mirror Shards have been collected." + - Dungeons: "The barrier will be dispelled once the required number of Dungeons have been cleared." + - Poe Souls: "The barrier will be dispelled once the required number of Poe Souls have been collected." + - Hearts: "The barrier will be dispelled once the required number of Hearts have been reached." + +- Name: Hyrule Barrier Fused Shadows + Need In Game: True + Tracker Important: True + Default Option: 1 + Options: + - 1-3: No description available. + +- Name: Hyrule Barrier Mirror Shards + Need In Game: True + Tracker Important: True + Default Option: 1 + Options: + - 1-4: No description available. + +- Name: Hyrule Barrier Dungeons + Need In Game: True + Tracker Important: True + Default Option: 1 + Options: + - 1-8: No description available. + +- Name: Hyrule Barrier Poe Souls + Need In Game: True + Tracker Important: True + Default Option: 1 + Options: + - 1-60: No description available. + +- Name: Hyrule Barrier Hearts + Need In Game: True + Tracker Important: True + Default Option: 4 + Options: + - 4-20: No description available. # Hehe 420 + +- Name: Palace of Twilight Requirements + Need In Game: True + Tracker Important: True + Default Option: Vanilla + Options: + - Open: "The Mirror of Twilight is open at the start of the game." + - Fused Shadows: "The player must collect all 3 Fused Shadows." + - Mirror Shards: "The player must collect all 4 Mirror Shards." + - Vanilla: "The player must complete City in the Sky." + +- Name: Faron Woods Logic + Tracker Important: True + Default Option: Closed + Options: + - Closed: "Midna will block the player from leaving Faron Woods until Forest Temple is completed." + - Open: "Midna will not prevent the player from leaving Faron Woods." + +- Name: Mirror Chamber Access + Need In Game: True + Tracker Important: True + Default Option: Open + Options: + - Open: "The entrance is open and operates like normal. If you start with the Mirror Chamber Portal, you can access the Stallord boss fight without going through Arbiter's Grounds." + - Barrier: "A barrier is placed in front of the Mirror Chamber entrance and goes away once Stallord is defeated." + - Closed: "The Mirror Chamber is isolated from the world and cannot be reached from the Stallord boss room. To access it, players will either need the portal or access from Palace of Twilight." + +###################### +## Item Pool ## +###################### + +- Name: Golden Bugs + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "The Golden Bug locations across Hyrule will give the golden bug at that location in the base game." + - "On": "The Golden Bug locations across Hyrule will be randomized." + +- Name: Sky Characters + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "The Sky Character locations across Hyrule will give Sky Characters." + - "On": "The Sky Character locations across Hyrule will be randomized." + +- Name: Gifts From NPCs + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Locations which involve an NPC giving you a gift will give the item expected from the base game." + - "On": "Locations which involve an NPC giving you a gift will be randomized." + +- Name: Shop Items + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Shop item locations will contain the item they have in the base game." + - "On": "Shop items will be randomized." + +- Name: Hidden Skills + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "The golden wolf locations across Hyrule will give you Hidden Skills. You will be required to get at least one of them to obtain the Ending Blow." + - "On": "The golden wolf locations across Hyrule will be randomized." + +- Name: Hidden Rupees + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Rupees hidden in tricky locations will not be randomized." + - "On": "Rupees hidden in tricky locations will be randomized." + +- Name: Freestanding Rupees + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Rupees out in the open will not be randomized." + - "On": "Rupees out in the open will be randomized." + +- Name: Poe Souls + Tracker Important: True + Default Option: Vanilla + Options: + - Vanilla: "All poes will give poe souls as the item for beating them." + - Overworld: "Overworld poes will give randomized items. Poes in dungeons will still give poe souls." + - Dungeon: "Poes in dungeons will give randomized items. Overworld poes will will give poe souls." + - All: "All poes will be randomized." + +- Name: Ilia Memory Quest + Tracker Important: True + Default Option: Vanilla + Options: + - Vanilla: "The Ilia Memory Quest will work the same way as the base game. You'll start it by beating Temple of Time and then talking to Renado in Kakariko Village." + - Letter: "Renado's Letter will be shuffled into the world somewhere. You'll start the quest by finding Renado's Letter and then showing it to Telma in her bar." + - Invoice: "The Invoice will be shuffled into the world somewhere. You'll start the quest by finding the Invoice and showing it to the Doctor in Castle Town." + - Statue: "The Wooden Statue will be shuffled into the world somewhere. You'll start the quest by finding the Wooden Statue and then showing it to Ilia in Kakariko Village." + - Charm: "Ilia's Charm will be shuffled into the world somewhere. You'll start the quest by finding Ilia's Charm and then showing it to Ilia in Kakariko Village to complete the quest." + +- Name: Item Scarcity + Default Option: Vanilla + Options: + - Vanilla: "No changes to the item pool." + - Minimal: "Removes unrequired items such as Heart Containers and Pieces, Hawkeye, etc. Has as few items as possible for Bomb Bags, Bows, Hidden Skills, and Wallets. No Magic Armor and 1 Hidden Skill if Glitchless logic." + - Plentiful: "One extra copy of major items. There are 17 Heart Containers but no Pieces of Heart. Extra keys if `Keysanity` or `Any Dungeon`." + +- Name: Trap Item Frequency + Default Option: None + Options: + - None: "All items in the game will be genuine." + - Few: "Approximately 12.5% of non-major items will be replaced with 'traps' that don't give the item they appear to be." + - Many: "Approximately 39.1% of non-major items will be replaced with 'traps' that don't give the item they appear to be." + - Mayhem: "Approximately 62.7% of non-major items will be replaced with 'traps' that don't give the item they appear to be." + - Nightmare: "All of the non-major items will be replaced with 'traps' that don't give the item they appear to be." + +###################### +## Dungeon Items ## +###################### + +- Name: Small Keys + Tracker Important: True + Default Option: Vanilla + Random Low: Own Dungeon + Random High: Anywhere + Options: + - Vanilla: "Small Keys will appear in their vanilla locations." + - Own Dungeon: "Small Keys will appear inside their respective dungeon." + - Any Dungeon: "Small Keys can appear inside any dungeon." + # - Own Region: "Small Keys will appear in their dungeon's region." + - Overworld: "Small Keys will only appear in the overworld." + - Anywhere: "Small Keys can appear anywhere." + - Keysy: "Small Keys will not appear anywhere in the world and their locks will start opened." + +- Name: Big Keys + Tracker Important: True + Default Option: Vanilla + Options: + - Vanilla: "Big Keys will appear in their vanilla locations." + - Own Dungeon: "Big Keys will appear inside their respective dungeon." + - Any Dungeon: "Big Keys can appear inside any dungeon." + # - Own Region: "Big Keys appear in their dungeon's region." + - Overworld: "Big Keys will only appear in the overworld." + - Anywhere: "Big Keys can appear anywhere." + - Keysy: "Big Keys will not appear anywhere in the world and boss doors will start opened." + +- Name: Maps and Compasses + Default Option: Vanilla + Options: + - Vanilla: "Maps and Compasses will appear in their vanilla locations." + - Own Dungeon: "Maps and Compasses can appear anywhere inside their respective dungeon." + - Any Dungeon: "Maps and Compasses can appear inside any dungeon." + # - Own Region: "Maps and Compasses will appear in their dungeon's region." + - Overworld: "Maps and Compasses will only appear in the overworld." + - Anywhere: "Maps and Compasses can appear anywhere." + - Start With: "The player starts with all Maps and Compasses." + +- Name: Hyrule Castle Big Key Requirements + Need In Game: True + Tracker Important: True + Default Option: None + Options: + - None: "The gate is opened and the key is randomized according to the respective Big Key settings." + - Fused Shadows: "The gate will open once the required number of Fused Shadows have been collected." + - Mirror Shards: "The gate will open once the required number of Mirror Shards have been collected." + - Dungeons: "The gate will open once the required number of Dungeons have been cleared." + - Poe Souls: "The gate will open once the required number of Poe Souls have been collected." + - Hearts: "The gate will open once the required number of Hearts have been reached." + +- Name: Hyrule Castle Big Key Fused Shadows + Need In Game: True + Tracker Important: True + Default Option: 1 + Options: + - 1-3: No description available. + +- Name: Hyrule Castle Big Key Mirror Shards + Need In Game: True + Tracker Important: True + Default Option: 1 + Options: + - 1-4: No description available. + +- Name: Hyrule Castle Big Key Dungeons + Need In Game: True + Tracker Important: True + Default Option: 1 + Options: + - 1-8: No description available. + +- Name: Hyrule Castle Big Key Poe Souls + Need In Game: True + Tracker Important: True + Default Option: 1 + Options: + - 1-60: No description available. + +- Name: Hyrule Castle Big Key Hearts + Need In Game: True + Tracker Important: True + Default Option: 4 + Options: + - 4-20: No description available. # Hehe 420 + +- Name: Dungeon Rewards Can Be Anywhere + Default Option: "Off" + Options: + - "Off": "Dungeon reward items (Fused Shadows and Mirror Shards) will only appear at the end of dungeons." + - "On": "Dungeon reward items (Fused Shadows and Mirror Shards) can appear anywhere." + +- Name: No Small Keys on Bosses + Default Option: "Off" + Options: + - "Off": "Small keys will not be placed on boss heart container or dungeon reward checks." + - "On": "Small keys can potentially be placed on boss heart container or dungeon reward checks. You may be expected to defeat a dungeon's boss before progressing further into the dungeon." + +- Name: Unrequired Dungeons Are Barren + Tracker Important: True + Default Option: "On" + Options: + - "Off": "Unrequired dungeons may contain items needed to beat the game." + - "On": "Unrequired dungeons will not contain any items necessary to beat the game." + +###################### +## Timesavers ## +###################### + +- Name: Skip Prologue + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "You will be required to play through the prologue section of the game to begin the randomizer. This includes everything up to the second goat herding sequence." + - "On": "The prologue section of the game will be completed from the start." + +- Name: Faron Twilight Cleared + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "You will be required to complete the Castle Sewers and Faron Twilight section of the game." + - "On": "The Castle Sewers and Faron Twilight section of the game will be completed from the start." + +- Name: Eldin Twilight Cleared + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "You will be required to complete the Eldin Twilight section of the game." + - "On": "The Eldin Twilight section of the game will be completed from the start." + +- Name: Lanayru Twilight Cleared + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "You will be required to complete the Lanayru Twilight section of the game." + - "On": "The Lanayru Twilight section of the game will be completed from the start." + +- Name: Skip Midna's Desparate Hour + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "After completing Lakebed Temple, you will be required to complete the Midna's Desperate Hour section of the game. Note that some overworld poes require Midna's Desperate Hour to be completed before they spawn in." + - "On": "The Midna's Desperate Hour section of the game will be completed from the start." + +- Name: Skip Minor Cutscenes + Need In Game: True + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Minor cutscenes such as area introduction cutscenes and Midna text explanations will not be skipped." + - "On": "Minor cutscenes such as area introduction cutscenes and Midna text explanations will not play." + +- Name: Skip Major Cutscenes + Need In Game: True + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "If you want to skip a skippable cutscene, you must press the start button twice while the cutscene is playing." + - "On": "The randomizer will automatically skip any skippable cutscene as fast as possible." + +- Name: Unlock Map Regions + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "The map will not start filled in at the beginning of the randomizer. You will have to travel to the different sections of the world to get them on the map." + - "On": "The map will start as filled in as possible. Certain sections such as Eldin and Lanayru will only be filled in if their Twilight section is cleared from the start." + +- Name: Open Door of Time + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "You will be required to lead the big statue down the Temple of Time to open the big door at the bottom." + - "On": "The big statue will already be set in place at the bottom of the Temple of Time and the door will be opened from the start." + +- Name: Active Goron Mines Magnets + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "The switches to activate magnets in Goron Mines will need to be activated manually." + - "On": "The switches to activate magnets in Goron Mines will be activated from the start except for the highest one in the main room." + +- Name: Lower Hyrule Castle Chandelier + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "The chandeliers in Hyrule Castle will all need to be lowered manually." + - "On": "One of the chandeliers in the Hyrule Castle main hall will be lowered from the start. This skips requiring Gale Boomerang and either Lantern or Bow to complete Hyrule Castle." + +- Name: Skip Bridge Donation + Need In Game: True + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "You will need to donate money at Kakariko Malo Mart to build the bridge between Eldin Field and Castle Town." + - "On": "The bridge between Eldin Field and Castle Town will be automatically built after Eldin and Lanayru Twilight are both completed." + +###################### +# Additional Settings# +###################### + +- Name: Starting Form + Tracker Important: True + Default Option: Human + Options: + - Human: Start as Human Link + - Wolf: Start as Wolf Link + +- Name: Bonks Do Damage + Tracker Important: True + Default Option: "Off" + Options: + - "Off": No description available. + - "On": No description available. + +- Name: Starting Time of Day + Default Option: Noon + Options: + - Morning: "Time of day will start at 9am." + - Noon: "Time of day will start at noon." + - Evening: "Time of day will start at 6pm." + - Night: "Time of day will start at midnight." + +- Name: Logic Transform Anywhere + Default Option: "On" + Options: + - "Off": "You will not be expected to transform in places that you normally can't." + - "On": "You may be expected to transform in places that require turning on the Dusklight Transform Anywhere setting." + +- Name: Logic Increase Wallet Capacity + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Logic will assume you have the same wallet capacities as the base game." + - "On": "Logic will assume that you have the Bigger Wallets Dusklight setting turned on." + +- Name: Logic Damage Multiplier + Default Option: Vanilla + Options: + - Vanilla: "Logic will assume your Dusklight Damage Multiplier is x1." + - Double: "Logic will assume your Dusklight Damage Multiplier is x2." + - Triple: "Logic will assume your Dusklight Damage Multiplier is x3." + - Quadruple: "Logic will assume your Dusklight Damage Multiplier is x4." + - OHKO: "Logic will assume your Dusklight Instant Death setting is turned on." + +############################# +# Dungeon Entrance Settings # +############################# + +- Name: Lakebed Does Not Require Water Bombs + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "The entrance to Lakebed Temple will be blocked by a boulder and require using Water Bombs to blow up." + - "On": "The rock blocking access to the entrance of Lakebed Temple will be gone, meaning you can get in without Water Bombs." + +- Name: Arbiters Does Not Require Bulblin Camp + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Bulblin Camp will have to be completed to access the entrance to Arbiter's Grounds. This will require finding the Gerudo Desert Bulblin Camp Key." + - "On": "Bulblin Camp will be cleared at the start of the randomizer." + +- Name: Snowpeak Does Not Require Reekfish Scent + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Getting the reekfish scent will be required for running through the blizzard on Snowpeak Mountain. Getting the reekfish scent requires the Coral Earring." + - "On": "The randomizer will start you with the reekfish scent, meaning you can go through the Snowpeak Mountain blizzard without needing the Coral Earring." + +- Name: Sacred Grove Does Not Require Skull Kid + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Getting through the Lost Woods will require completing the Skull Kid chase sequence." + - "On": "The Skull Kid chase sequence in the Lost Woods will start already completed." + +- Name: City Does Not Require Filled Skybook + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "The canon to City in the Sky will spawn in Lake Hylia when the Sky Book has been completed." + - "On": "The canon to City in the Sky will be in Lake Hylia from the start." + +- Name: Goron Mines Entrance + Tracker Important: True + Default Option: Closed + Options: + - Closed: "Accessing Goron Mines will require climbing up to the Death Mountain Sumo Hall and wrestling with the Goron Elder." + - No Wrestling: "Accessing Goron Mines will require climbing up to the Death Mountain Sumo Hall, but wrestling the Goron Elder will not be necessary." + - Open: "The elevator shortcut to the Death Mountain Sumo Hall will be open, and can be used to access Goron Mines." + +- Name: Temple of Time Sword Requirement + Need In Game: True + Tracker Important: True + Default Option: None + Options: + - None: "The door to the past will be open from the start." + - Wooden Sword: "The door to the past will require striking at least the Wooden Sword into the Master Sword pedestal." + - Ordon Sword: "The door to the past will require striking at least the Ordon Sword into the Master Sword pedestal." + - Master Sword: "The door to the past will require striking at least the Master Sword into the Master Sword pedestal." + - Light Sword: "The door to the past will require striking the Light Sword into the Master Sword pedestal." + +- Name: Randomize Starting Spawn + Default Option: "Off" + Options: + - "Off": "Link will start outside his house." + - "On": "Link will start in a random area." + +- Name: Randomize Dungeon Entrances + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Entering a dungeon will lead to the vanilla dungeon." + - "On": "Entering a dungeon will lead to a random dungeon." + - "On + Hyrule Castle": "Entering a dungeon will lead to a random dungeon. Hyrule Castle's entrance will be shuffled as well." + +- Name: Randomize Boss Entrances + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Entering a boss door will lead to the intended boss." + - "On": "Entering a boss door will lead to a random boss." + +- Name: Randomize Grotto Entrances + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Entering a grotto will lead to the intended grotto." + - "On": "Entering a grotto will lead to a random grotto." + +- Name: Randomize Cave Entrances + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Entering a door or loadzone that leads to a cave area will lead to the intended cave." + - "On": "Entering a door or loadzone that leads to a cave area will lead to a random cave." + +- Name: Randomize Interior Entrances + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Entering a door or loadzone that leads to an interior area will lead to the intended area." + - "On": "Entering a door or loadzone that leads to an interior area will lead to a random intended area." + +- Name: Randomize Overworld Entrances + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Entering a loadzone that leads to an overworld area will lead to the intended area." + - "On": "Entering a loadzone that leads to an overworld area will lead to a random area." + +- Name: Decouple Double Door Entrances + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Entering a left door will lead to the same location as the corresponding right door." + - "On": "Entering a left door may lead somewhere different than the corresponding right door (only if the door's type is randomized)." + +- Name: Decouple Entrances + Tracker Important: True + Default Option: "Off" + Options: + - "Off": "Entering a location and taking the entrance behind you will take you back to where you came from." + - "On": "Entering a location and taking the entrance behind you will take you to a random location." + + +############################# +## Logic Tricks ## +############################# + +- Name: Back Slice as Sword + Default Option: "Off" + Options: + - "Off": "Back Slicing without a sword can be logically required to do damage." + - "On": "Back Slicing without a sword can be logically required to do damage." + +- Name: Ball and Chain Webs + Default Option: "Off" + Options: + - "Off": "Ball and Chain will not be required to break webs." + - "On": "Ball and Chain may be required to break webs." \ No newline at end of file diff --git a/mods/randomizer/generator/data/startflags.yaml b/mods/randomizer/generator/data/startflags.yaml new file mode 100644 index 0000000000..08b38a996e --- /dev/null +++ b/mods/randomizer/generator/data/startflags.yaml @@ -0,0 +1,609 @@ +# Flag values taken from: https://github.com/lunarsoap5/Randomizer-Web-Generator-1/blob/development/Generator/Assets/Flags.cs + +EventFlags: + - 0x0382 # Gave wooden sword to Talo. Talked to squirrel outside link's house + - 0x0629 # Tame Epona, KB1 trigger activated, Warped Kakariko Bridge Back. + - 0x0F40 # Talked to Doctor for the first time. + - 0x1208 # Can use Sera's Shop. + - 0x1410 # Put Bo outside, ready to wrestle + - 0x0F01 # Got Lantern from Coro (Remove when adding Coro checks) + - 0x0A2F # Bridge of Eldin Stolen, KB1 defeated, KB1 started + - 0x0F68 # Bridge of Eldin Warped Back, forced text when entering dr. clinic, talked to dr before giving invoice + - 0x4088 # Saved monkey from puppets, Visited Gerudo Desert for the first time. + - 0x4118 # Talked to Fado after Faron and Eldin Twilight + - 0x07A0 # Watched Colin CS after KB1, talked to Bo before sumo + - 0x2020 # Master Sword Story Progression + - 0x2010 # Arbiters Grounds Story Progression + - 0x2C10 # Raised the mirror in the Mirror Chamber + - 0x1B38 # Skip Monkey Escort + - 0x1C20 # Talked to Bo after opening boots chest. + - 0x5F20 # Shad leaves sanctuary. + - 0xF701 # Add 256 Rupees to Charlo. + - 0xF8F4 # Add 244 Rupees to Charlo. + - 0x6001 # Talked to Fyer after Lanayru Twilight + - 0x3880 # Talked to Jovani after defeating Poe. + - 0x2208 # Talked to Yeto on top of the mountain after clearing SPR + - 0x3B40 # Won Snowboard race against Yeto. + - 0x2F80 # Talked to Goron outside East Castle Town + - 0x1C10 # Win Sumo round 1 against Bo + - 0x3902 # Released first caught fish in Ordon Day 2 + - 0x1002 # Talked to Jaggle after climbing vines. + - 0x0B20 # Talked to Yeta in Snowpeak for the first time + - 0x4308 # Senses unlocked + - 0x4610 # Rode Epona back to Link's House + - 0x0C10 # Midna accompanies Wolf + - Skip_Prologue == On: + - 0x0404 # Talked to Uli Day 1. + - 0x4510 # Saved Talo + - 0x4A60 # Completed Ordon Day 1 and Finished Sword Training. + - 0x1601 # Completed Ordon Day 2. + - 0x1580 # Watched CS for Goats 2 Done. + - Faron_Twilight_Cleared == On: + - 0x057F # Midna Charge Unlocked, Finished Sewers, Met Zelda in swers, Midna cut prison chain, watched sewers intro CS, Escaped Cell in Sewers. + - 0x0610 # Cleared Faron Twilight + - 0x0C08 # Sword and shield removed from wolf's back. + - Eldin_Twilight_Cleared == On: + - 0x0708 # Cleared Eldin Twilight + - 0x0604 # Map Warping unlocked. + - Lanayru_Twilight_Cleared == On: + - 0x0880 # Zora's Domain Thawed. + - 0x0C02 # Lanayru Twilight Story Flag. + - 0x0A10 # Defeated Kargarok Rider at Lake (allows player to howl for Kargorok.); + - Skip_Minor_Cutscenes == On: + - 0x0140 # Talked to Yeto First Time. + - 0x0390 # Jaggle Calls out to Link, talked to Squirrel as Wolf in Ordon. + - 0x06C0 # CS After beating Ordon Shadow, CS after entering Faron Twilight. + - 0x0702 # First Time Talking to Gor Coron in Sumo Hall + - 0x1501 # Talked to Agitha for the first time. + - 0x2001 # Talked to Telma for the first time. + - 0x5E10 # Midna text after beating Forest Temple. + - 0x1D40 # Listened to Fyer at drained lake. + - 0x2201 # Plumm initial CS watched. + - 0x2310 # STAR initial CS watched. + - 0x2602 # Talked to Yeto on Snowpeak. + - 0x2840 # Used Ooccoo for the first time. + - 0x3704 # Postman twilight text. + - 0x3806 # Hena cabin first time CS, talked to Hena first time. + - 0x3A01 # Talked to Ralis in Graveyard for the first time. + - 0x4002 # Agreed to help Rusl after Snowpeak Ruins. + - 0x4205 # Watched post-ToT Ooccoo CS. Watched Cutscene with Rusl in North Faron Woods. + - 0x4508 # Allows postman letters to show up in inventory. + - 0x4A10 # Saw Talo in cage CS. + - 0x3E02 # City Ooccoo CS watched. + - 0x5940 # Met Postman for the first time. + - 0x5D40 # Midna text after Kargarok flight. + - 0x2502 # Watched cutscene with Yeto on top of mountain + - Faron_Woods_Logic == Open: + - 0x0602 # Forest Temple Story Flag + - 0x0C40 # Talked to Farone after clearing Forest Temple + - 0x5E10 # Midna text after Forest Temple completed + - Skip_Midna's_Desparate_Hour == On: + - 0x0C01 # Midna's Desperate Hour started. + - 0x1E08 # Midna's Deseperate Hour Completed. + - Small_Keys == Keysy: + - 0x0850 # Zora Escort started and completed. + - 0x0480 # Told Yeta about pumpkin. + - 0x0003 # Yeto put pumpkin and cheese in soup. + - 0x1460 # Snowpeak Ruins North and West doors unlocked. + - 0x0120 # Told Yeta about cheese + - Hyrule_Barrier_Requirements == Open: + - 0x4208 # Remove Castle Barrier + - Palace_of_Twilight_Requirements == Open: + - 0x2B08 # Mirror of Twilight Repaired. + - Goron_Mines_Entrance != Closed: + - 0x0706 # Talked to Gor Coron, Won Sumo against Gor Coron. + - Arbiters_Does_Not_Require_Bulblin_Camp == On: + - 0x0B40 # Escaped Burning Tent in Bulblin Camp. + - Snowpeak_Does_Not_Require_Reekfish_Scent == On: + - 0x6120 # Got the reekfish and smelled it (removes void in Snowpeak). + - City_Does_Not_Require_Filled_Skybook == On: + - 0x3B08 # Sky Cannon Repaired. + - Randomize_Starting_Spawn == On: + - 0x057A # Finished Sewers, Midna text after entering Faron Twilight, Met Zelda in sewers, Midna cut prison chain, Watched Sewers intro CS, Escaped cell in sewers. + - Ilia_Memory_Quest >= Letter: + - 0x2004 # ToT Story Progression Flag + - 0x0F80 # Renados Letter Check + - Ilia_Memory_Quest >= Statue: + - 0x2710 # Showed Invoice to Doctor + - 0x2F04 # Got Medicine Scent + - 0x2102 # Talked to Louise after Medicine Scent + - 0x2204 # Got Wooden Statue + - Ilia_Memory_Quest >= Charm: + - 0x2340 # Gave statue to Ilia + - 0x2E08 # HV barrier removed + - 0x2280 # Got Ilia's Charm + - Skip_Bridge_Donation == Off: + - 0xF901 # Add 256 Rupees to Malo Mart. + - 0xFAF4 # Add 244 Rupees to Malo Mart. + +RegionFlags: + Ordona Province: + Index: 0x00 + Flags: + - 0x57 # Spider on Link's Ladder killed. + - 0x63 # Spawn the Chest in Link's House + - 0x7E # Midna jumps to Shop unlocked + - 0x6B # Ordon Spring Portal. + - 0x44 # Midna Text after Ordon Shield (Spawns sword) + - 0x46 # Midna Text after Ordon Sword + - 0x68 # Approach faron wall with Midna + - 0xA0 # Midna allows player to approach Faron Twilight Wall + - 0xBA # Explored area outside Link's house as wolf + - 0x61 # Defeated first bulblin outside link's house + - 0x62 # Defeated second bulblin outside link's house + - 0x60 # Defeated Hugo + - Skip_Minor_Cutscenes == On: + - 0x4A # Ordon Day 3 Intro CS. + - 0x4C # Knocked down Ordon bee nest CS. + - 0x4E # Ordon Ranch first time CS. + - 0x53 # Ilia spring CS watched. + - 0x54 # Ilia spring CS started. + - 0x55 # Ordon Village first time CS. + - 0x56 # Ilia spring CS trigger. + - 0x68 # Approach Faron Twilgiht with Midna CS. + - 0x6E # Enter shield house as wolf CS. + - 0x75 # Midna text after hearing Bo and Jaggle talk about the shield. + - 0x7C # Midna text before jumping to Ordon Shop roof. + - 0x7D # Rusl talking to Uli during wolf night CS. + - 0xB8 # Enter Ordon Village as wolf CS. + + Hyrule Castle Sewers and Rooftops: + Index: 0x01 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x42 # Midna text after first gate in sewers. + - 0x43 # Midna text after exiting to rooftops. + - 0x51 # Zelda tower intro CS. + - 0x57 # Outside top door intro CS. + - 0x5A # Went to the otherside of the fence in sewers CS. + - 0x5B # Top of stairway intro CS. + - 0x5C # Stairway intro CS. + - 0x7B # Midna text when approaching the rooftop guard. + + Faron Woods: + Index: 0x02 + Flags: + - 0x63 # Trill lets you shop at his store. + - 0x48 # Talked to Coro after bugs + - 0x60 # Got Lantern Back from Monkey + - 0x61 # Saw bugs move in Coro's house + - 0x7D # Talked to Midna about Coro spirit + - 0x4E # Saved Monkey from Puppets. + - 0x62 # Midna text before jumping to lost woods + - 0x95 # Midna text after warping to North Faron for bridge. + - 0xBF # Burned First cobweb in faron cave + - 0xBE # Burned second cobweb in faron cave + - Skip_Prologue == On: + - 0x4B # North Faron Gate Unlocked + - Skip_Minor_Cutscenes == On: + - 0x74 # Faron intro CS. + - 0x77 # See Faron Light Spirit from afar CS. + - 0x7C # Entered mist area as human. + - Faron_Twilight_Cleared == On: + - 0x46 # Midna jump 1 mist area. + - 0x47 # Midna jump 1 mist area. + - 0x98 # South Faron Portal. + # Re-enable once key situation is sorted out + # - Small_Keys == Keysy: + # - 0x53 # Coro gate unlocked. + # - 0x4B # North Faron Gate Unlocked. + + Kakariko and Death Mountain: + Index: 0x03 + Flags: + - 0xB9 # Barnes sells water bombs. + - 0xB3 # Colin Rescued CS (Malo Mart is Open). + - 0xA4 # Barnes Sells Bombs. + - 0x42 # Big Rock fell at DMT + - 0xA7 # Unlock Jumps to top of Sanctuary + - 0x9A # Kakariko Village intro CS. + - 0x54 # Custom flag. Sets the sign in Kak Malo mart slot 1 to appear. + - 0x99 # Remove wooden shield from Kak Malo Mart counter. + - Skip_Minor_Cutscenes == On: + - 0x49 # Death mountain intro CS. + - 0x83 # Kakariko Graveyard intro CS. + - 0x8C # Midna text after Meteor fell. + - Eldin_Twilight_Cleared == On: + - 0x14 # Collected Tear From Bomb Storage + - 0x1A # Collected Tear From Bomb Storage + - 0x1B # Collected Tear From Bomb Storage + - 0x67 # Ant house entered from top + - 0x64 # Ant house box pushed + - 0x5E # Defeated Ant house Tears of Light bug + - 0x1E # Collected Tear from Ant house + - 0xBD # Done Midna jumps in ant house. + - Small_Keys == Keysy: + - 0xBA # Followed Rutella to graveyard. + - 0xB6 # Started Rutella escort. + - Goron_Mines_Entrance == Open: + - 0x79 # moved death mountain rock to exit + - 0x8F # moved death mountain rock to hot spring water + - 0xB0 # Goron lets you enter elevator in sumo hall + - Ilia_Memory_Quest >= Charm: + - 0x70 # Darbus destroyed HV rocks + + Lake Hylia and Zoras Domain: + Index: 0x04 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x58 # Talked to Rutella in Lanayru Twilight. + - 0x5F # Zora's domain intro CS twilight. + - 0x67 # Midna text after jumping to Lake from burning bridge. + - 0x6B # Zora's Domain exit flood water cutscene. + - 0x72 # Midna text after arriving at frozen Upper Zora River. + - 0x91 # Midna text after frozen Zora Domain intro CS. + - 0xB0 # Watched CS of Ooccoo running to Sky Cannon. + - Lanayru_Twilight_Cleared == On: + - 0x7F # Lake Hylia has water on Lake Hylia Map. + - Skip_Midna's_Desparate_Hour == On: + - 0x51 # Set flag for MDH Cutscene in Lake Hylia + - Lakebed_Does_Not_Require_Water_Bombs == On: + - 0x70 # Blew up rock in front of lakebed CS. + - 0x78 # Blew up rock in front of lakebed. + + # Not sure what Index 5 is + + Hyrule Field: + Index: 0x06 + Flags: + - 0x4C # Bridge of Eldin Warped back CS. + - 0x7E # Kakariko Gorge placed CS + - 0x83 # Set the flag for the Ganon Barriers in Hyrule Field during Eldin Twilight. + - Skip_Minor_Cutscenes == On: + - 0x68 # Midna text after warping Gorge bridge. + - 0x7C # Midna text after Lanayru Field twilight CS. + - 0x72 # Faron Field intro CS. + - 0x40 # Twilight Lanayru Field intro CS. + - 0x4F # Cutscene of gate outside Kakariko Village. + - 0xB3 # Midna text after entering Lanayru Twilight. + - 0xB4 # Midna text when seeing Lanayru Twilight from far away. + - 0xB6 # Midna text after entering Eldin Twilight. + - 0xB7 # Midna text when seeing Eldin Twilight from far away. + - Lanayru_Twilight_Cleared == On: + - 0x58 # Lake Hylia has water on Hyrule Field Map + - Ilia_Memory_Quest >= Charm: + - 0x43 # Remove HV rocks from Hyrule field + + Lost Woods and Sacred Grove: + Index: 0x07 + Flags: + - 0x58 # Sacred Grove MS Pedestal Map + - Skip_Minor_Cutscenes == On: + - 0x42 # Midna text after pushing block shortcut as human after Grove 2. + - 0x43 # cs after pushing block human + - 0x44 # Lost Woods intro CS. + - Temple_of_Time_Sword_Requirement == None: + - 0x49 # Stairs to Temple of time created. + - 0x4A # Struck master sword pedestal with sword. + - 0x4B # Stairs and window appear and work properly (Past). + - 0xBC # Statue in present is gone. (custom flag) + - Sacred_Grove_Does_Not_Require_Skull_Kid == On: + - 0xB6 # Skull Kid - Human defeated. + - 0xB7 # Lost Woods Turns to day after defeating Skull Kid - Human + - 0x5B # Block pushed down + - 0x42 # Midna text after block pushed down + - 0x43 # cs after pushing block human + + + Snowpeak Province: + Index: 0x08 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x45 # Snowpeak Summit intro CS. + - 0x5E # Midna text outside SPR. + - 0x5F # Snowpeak intro CS. + - Snowpeak_Does_Not_Require_Reekfish_Scent == On: + - 0x49 # Snowpeak summit cs. + - 0x45 # Snowpeak Summit intro CS. + + Castle Town: + Index: 0x09 + Flags: + - 0x40 # Original Jovani Poe killed. It is replaced with a custom actor. + - 0x76 # Jovani Chest CS 2 + - 0x7F # Open Chest to Jovani + - 0x7E # Jovani Chest CS + - 0x50 # Set flag for Midna breaking Barrier CS. + - 0xBC # Spawn Gengle by default as his actor interferes with the poe soul + - Skip_Minor_Cutscenes == On: + - 0x55 # STAR Tent intro CS. + - 0x7D # Jovani House intro CS. + - Ilia_Memory_Quest >= Statue: + - 0x56 # Remove invisible wall from Doctor + + Gerudo Desert: + Index: 0x0A + Flags: + - 0x99 # Desert Entrance CS. + - 0x20 # Set Freestanding key flag. + - 0x7F # Mirror Raised Cutscene Flag (Places Boar at desert entrance) + - Skip_Minor_Cutscenes == On: + - 0x53 # Mirror Chamber Intro CS. + - Arbiters_Does_Not_Require_Bulblin_Camp == On: + - 0x43 # Explored part 9 of the Bulblin camp area + - 0x44 # Explored part 8 of the Bulblin camp area + - 0x45 # Explored part 7 of the Bulblin camp area + - 0x46 # Explored part 6 of the Bulblin camp area + - 0x47 # Explored part 5 of the Bulblin camp area + - 0x4C # Explored part 2 of the Bulblin camp area + - 0x4D # Explored part 4 of the Bulblin camp area + - 0x4E # Explored part 3 of the Bulblin camp area + + Forest Temple: + Index: 0x10 + Flags: + - 0x49 # FT Ook Bridge Destroyed + - Skip_Minor_Cutscenes == On: + - 0x41 # Midna text after getting Boomerang. + - 0x42 # Midna text after Ook breaks the bridge. + - 0x47 # Midna text after freeing first monkey. + - 0x49 # Bridge before Ook broken. + - 0x56 # Bokoblins spot Link in windless bridge room. + - 0x57 # Turned bridge in windless bridge room. + - 0x72 # West Tile Worm room intro CS. + - 0x76 # Second monkey room intro CS. + - 0x7C # Big Baba room intro CS. + - 0x7D # Midna text in room before boss room. + - 0x7E # Midna text after saving monkey after defeating Ook. + - 0x85 # Midna text after opening hanging chest. + - 0x83 # East outside room intro CS. + - 0xB6 # Forest Temple intro CS. + - Small_Keys == Keysy: + - 0x54 # Unlocked door to Second Monkey. + - 0x58 # Unlock windless bridge east door. + - 0x61 # Opened big baba monkey cage. + - 0x74 # Opened tile worm monkey cage. + - Big_Keys == Keysy: + - 0x48 # Unlocked Forest Temple Boss Door. + - 0xED # Got Forest Temple Big Key. + - Maps_and_Compasses == Start_With: + - 0xEE # Got Forest Temple Compass. + - 0xEF # Got Forest Temple Dungeon Map. + + Goron Mines: + Index: 0x11 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x43 # Cut rope of door in outside room CS. + - 0x44 # Pressed second button of the main magnet room of the second floor CS trigger. + - 0x45 # Pressed third button in entrance room CS. + - 0x46 # Pressed second button in entrance room CS. + - 0x47 # Cut rope of door in Toadpoli room CS. + - 0x4A # Pressed first button of the main magnet room on the second floor CS. + - 0x68 # Pressed outside magnet switch for first time CS. + - 0x72 # Main magnet room intro CS. + - 0x73 # Main magnet room intro CS trigger. + - 0x7A # Outside room intro CS. + - 0x80 # Room after Bow chest intro CS. + - 0x81 # Pulled Beamos in outside room CS. + - 0x84 # Open gate in Toadpoli room CS. + - 0x85 # Pressed second button in Toadpoli room CS. + - 0x88 # Magnet maze room intro CS. + - 0x8A # Goron Mines intro CS. + - 0x8B # Pressed first button in entrance room CS. + - 0x8C # Open gate in entrance room CS. + - 0xBC # Main magnet room second floor intro CS. + - 0xBD # Main magnet room second floor intro CS trigger. + - 0xBE # Hit crystal switch in room after bow chest CS. + - 0xBF # Room after Bow chest intro CS trigger. + - Small_Keys == Keysy: + - 0x60 # Unlock north door in toadpoli room. + - 0x62 # Unlock west locked door in main magnet room. + - 0x6C # Unlock east outside door. + - Big_Keys == Keysy: + - 0x48 # Unlocked Goron Mines Boss Door. + - 0xED # Got Goron Mines Big Key. + - Maps_and_Compasses == Start_With: + - 0xEE # Got Goron Mines Compass. + - 0xEF # Got Goron Mines Dungeon Map. + - Active_Goron_Mines_Magnets == On: + - 0xBB # activated magnet from water before first elder + - 0x8F # activated ceiling maze magnet after first elder + - 0x5B # activated main magnet room 1st magnet + - 0x4A # watched main magnet room 1st magnet cs + - 0x61 # activated main magnet room 2nd magnet + - 0x44 # watched main magnet room 2nd magnet cs + - 0x9E # crystal switch room 1st Iron Boots switch pressed + - 0x83 # crystal switch room 1st Iron Boots switch cs shown + - 0x82 # crystal switch room 1st magnet active + - 0x9F # crystal switch room 2nd Iron Boots switch pressed + - 0x85 # crystal switch room 2nd Iron Boots switch cs shown + - 0x86 # crystal switch room 2nd magnet active + - 0x54 # activated outside room magnet + - 0x68 # watched outside room magnet cs + - 0x8d # activated east wing dodongo room magnet + # Note: the final magnet is not activated because there is a + # softlock when you exit the main magnet room through the top door + # as wolf while the gate beyond the door in the Dodongo room is not + # open. Alternatively if we do open this gate, it leads to much + # more complicated logic and some jank where you can walk through + # Dangoro while he sits there (and the gate covering the door + # animation is also a bit jank). Also you can die during the + # Dangoro fight to appear on the north side of his arena. There are + # too many nuances for it to be reasonable for glitchless logic, + # and also every small key door would require 3 keys. + + Lakebed Temple: + Index: 0x12 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x46 # Midna Stalactite text in second room. + - 0x7E # Horizontal wheel is turning in east room CS. + - 0x7F # Horizontal wheel is turning in east room CS trigger. + - 0xA5 # Central room intro CS. + - 0xA6 # South bridge to main room intro CS. + - 0xA7 # Lakebed Temple intro CS. + - 0xAA # Rotate staircase main room CS. + - 0xB4 # East water supply Chu Worm CS. + - Small_Keys == Keysy: + - 0x6B # Unlock east door main room 2F. + - 0x7B # Unlocked door in second east room 2F. + - 0x7C # Unlocked door before Deku Toad. + - Big_Keys == Keysy: + - 0x8A # Unlocked Lakebed Temple Boss Door. + - 0xED # Got Lakebed Temple Big Key. + - Maps_and_Compasses == Start_With: + - 0xEE # Got Lakebed Temple Compass. + - 0xEF # Got Lakebed Temple Dungeon Map. + + Arbiters Grounds: + Index: 0x13 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x5A # Turn walls in third room Basement second floor CS. + - 0x73 # Arbiters Grounds intro CS. + - 0x94 # Risen tracks on pilar before boss CS. + - Small_Keys == Keysy: + - 0x78 # Unlocked door in second east room 2F. + - 0x84 # Unlocked door in elevator room 2B. + - 0x85 # Unlocked door in first room. + - 0x92 # Unlocked door in first east room 1F. + - 0x99 # Unlocked door in fourth east room. + - Big_Keys == Keysy: + - 0x47 # Unlocked Arbiter's Grounds Boss Door. + - 0xED # Got Arbiter's Grounds Big Key. + - Maps_and_Compasses == Start_With: + - 0xEE # Got Arbiter's Grounds Compass. + - 0xEF # Got Arbiter's Grounds Dungeon Map. + + Snowpeak Ruins: + Index: 0x14 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x83 # First Floor Northwest room intro CS. + - 0xA1 # Midna text after finding Bedroom Key. + - 0xA7 # Snowpeak Ruins intro CS. + - 0xAA # Freezard in cage CS. + - 0xAC # Courtyard intro CS. + - 0xAE # Pumpkin room intro CS. + - 0xB2 # Midna text after getting Cheese. + - 0xB4 # Midna text after getting Pumpkin. + - Small_Keys == Keysy: + - 0x4D # Unlock North lobby door. + - 0x4C # Unlock West lobby door. + - 0x6F # Unlock door in southeast room 2F. + - 0x73 # Unlock door in east outside hallway. + - 0x74 # Unlock west door in courtyard. + - 0x70 # Unlock door to lobby from Freezard room. + - Big_Keys == Keysy: + - 0x57 # Unlocked Snowpeak Ruins Boss Door. + - 0xED # Got Snowpeak Ruins Big Key. + - 0x56 # Watched CS of Yeta entering boss room. + - Maps_and_Compasses == Start_With: + - 0xEE # Got Snowpeak Ruins Compass. + - 0xEF # Got Snowpeak Ruins Dungeon Map. + + Temple of Time: + Index: 0x15 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x40 # Midna text telling you to use your senses on the missing statue. + - 0x41 # Midna text after using senses on missing statue. + - 0x4A # Temple of Time intro CS. + - 0x4B # Scales of Time room intro CS. + - 0x4C # CS after changing the balance on the scales for the first time. + - 0x4D # CS trigger after changing the balance on the scales for the first time. + - 0x90 # Pressed button in room 1 for the first time CS. + - 0x91 # Pressed the button on the seventh floor for the first time CS. + - 0x94 # Pressed the button on the fifth floor for the first time CS. + - 0x95 # Pressed buttons on third floor for the first time CS. + - 0x96 # Pressed the button on the second floor for the first time CS. + - 0x54 # statue getting possessed for the first time cs + - Small_Keys == Keysy: + - 0x44 # Unlock door in room 1. + - 0x42 # Unlock door in room 6 on 8F. + - 0x43 # Unlock door in 5F. + - Big_Keys == Keysy: + - 0x7F # Unlocked Temple of Time Boss Door. + - 0xED # Got Temple of Time Big Key. + - Maps_and_Compasses == Start_With: + - 0xEE # Got Temple of Time Compass. + - 0xEF # Got Temple of Time Dungeon Map. + - Open_Door_of_Time == On: + - 0x59 # deactivate statue slot in room 1 (opens door and deactivates statue) + - 0x80 # open big door in room 1 cs part 2 + - 0x81 # open big door in room 1 cs part 1 + - 0xBC # big door in room 1 opens + - 0xBE # open big door in room 1 cs part 1 trigger + - 0xBD # open big door in room 1 cs part 2 trigger + - 0xBF # statue placed in slot in room 1 + + City in the Sky: + Index: 0x16 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x64 # North wing main room intro CS. + - 0x65 # East wing fan room second floor intro CS. + - 0x66 # Went beyond first gate outside shop intro CS. + - 0x67 # City in The Sky intro CS. + - 0x6E # East bridge extended CS. + - Small_Keys == Keysy: + - 0x59 # Unlock east bridge door. + - Big_Keys == Keysy: + - 0x58 # Unlocked City in The Sky Boss Door. + - 0xED # Got City in The Sky Big Key. + - Maps_and_Compasses == Start_With: + - 0xEE # Got City in The Sky Compass. + - 0xEF # Got City in The Sky Dungeon Map. + + Palace of Twilight: + Index: 0x17 + Flags: + - Skip_Minor_Cutscenes == On: + - 0x4D # Phantom Zant 1 CS. + - 0x66 # Midna text when west hand steals sol. + - 0x6F # Midna text about black fog in west room. + - 0x70 # Midna text after finding west sol. + - 0x72 # Midna text trigger when seeing a Twili for the first time. + - 0x78 # Midna text when seeing a Twili for the first time. + - 0x79 # Midna text after Light Sword cutscene. + - 0x95 # Midna text after re-entering west wing after sol was stolen. + - 0x9E # Midna text at dungeon entrance. + - 0xB3 # Watched east wing second room stairs CS. + - Small_Keys == Keysy: + - 0x57 # Unlock door in north room 3. + - 0x58 # Unlock door in east room 2. + - 0x59 # Unlock door in west room 2. + - 0x6C # Unlock door in north room 2. + - 0x7A # Unlock door in norht room 1. + - 0x7B # Unlock door in east room 1. + - 0x7C # Unlock door in west room 1. + - Big_Keys == Keysy: + - 0x56 # Unlocked Palace of Twilight Boss Door. + - 0xED # Got Palace of Twilight Big Key. + - Maps_and_Compasses == Start_With: + - 0xEE # Got Palace of Twilight Compass. + - 0xEF # Got Palace of Twilight Dungeon Map. + + Hyrule Castle: + Index: 0x18 + Flags: + - 0x4B # Watched CS with Allies in HC. + - Skip_Minor_Cutscenes == On: + - 0x4F # Hyrule Castle Graveyard intro CS. + - 0x8C # East garden intro CS. + - 0x8D # East garden intro CS trigger. + - 0x77 # Midna text at the east end of the east garden. + - 0x82 # South garden intro CS. + - 0x99 # Double Darknut room intro CS + - 0xA4 # Midna text after Owl Statue chest in graveyard. + - 0xB7 # Lit southeast torch in second floor north room for the first time CS. + - 0xB8 # Lit northeast torch in second floor north room for the first time CS. + - Small_Keys == Keysy: + - 0x93 # Unlock door outside 3F. + - 0xB0 # Unlock treasure room door. + - 0xA3 # Unlock door in south garden. + - Big_Keys == Keysy and Hyrule_Castle_Big_Key_Requirements == None: + - 0xA1 # Unlocked Hyrule Castle Boss Door. + - 0xED # Got Hyrule Castle Big Key. + - Maps_and_Compasses == Start_With: + - 0xEE # Got Hyrule Castle Compass. + - 0xEF # Got Hyrule Castle Dungeon Map. + - Lower_Hyrule_Castle_Chandelier == On: + - 0x6F # watched double Dinalfos cs 1 + - 0x70 # watched double Dinalfos cs 2 + - 0x85 # watched focus on lowered chandelier cs + - 0x9D # lower the main hall chandelier + - 0xAF # defeated double Dinalfos (opens gates both sides) + - Hyrule_Castle_Big_Key_Requirements == None: + - 0x94 # Open HC BK gate diff --git a/mods/randomizer/generator/data/tests/logic/all random/settings.yaml b/mods/randomizer/generator/data/tests/logic/all random/settings.yaml new file mode 100644 index 0000000000..cdb0701a59 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/all random/settings.yaml @@ -0,0 +1,66 @@ +Seed: TESTTESTTEST +Plandomizer: false +Generate Spoiler Log: true +Arbiters Does Not Require Bulblin Camp: Random +Back Slice as Sword: Random +Ball and Chain Webs: Random +Big Keys: Random +Bonks Do Damage: Random +City Does Not Require Filled Skybook: Random +Damage Multiplier: Random +Dungeon Rewards Can Be Anywhere: Random +Eldin Twilight Cleared: Random +Faron Twilight Cleared: Random +Faron Woods Logic: Random +Fast Iron Boots: Random +Gifts From NPCs: Random +Golden Bugs: Random +Goron Mines Entrance: Random +Hidden Skills: Random +Hyrule Barrier Requirements: Random +Increase Spinner Speed: Random +Increase Wallet Capacity: Random +Instant Message Text: Random +Item Scarcity: Random +Lakebed Does Not Require Water Bombs: Random +Lanayru Twilight Cleared: Random +Logic Rules: Random +Maps and Compasses: Random +No Small Keys on Bosses: Random +Open Door of Time: Random +Palace of Twilight Requirements: Random +Poe Souls: Random +Quick Transform: Random +Random Starting Item Count: 0 +Randomize Starting Spawn: Random +Randomize Dungeon Entrances: Random +Randomize Boss Entrances: Random +Randomize Grotto Entrances: Random +Randomize Cave Entrances: Random +Randomize Interior Entrances: Random +Randomize Overworld Entrances: Random +Decouple Double Door Entrances: Random +Decouple Entrances: Random +Sacred Grove Does Not Require Skull Kid: Random +Shop Items: Random +Shops Display The Replaced Item: Random +Skip Major Cutscenes: Random +Skip Midna's Desparate Hour: Random +Skip Minor Cutscenes: Random +Skip Prologue: Random +Sky Characters: Random +Small Keys: Random +Snowpeak Does Not Require Reekfish Scent: Random +Starting Form: Random +Starting Hearts: 3 +Starting Time of Day: Random +Temple of Time Sword Requirement: Random +Logic Transform Anywhere: Random +Trap Item Frequency: Random +Unlock Map Regions: Random +Unrequired Dungeons Are Barren: Random +trappable_items: major_items +Starting Inventory: + {} +Excluded Locations: + [] \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/any dungeon items/settings.yaml b/mods/randomizer/generator/data/tests/logic/any dungeon items/settings.yaml new file mode 100644 index 0000000000..aec28c687e --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/any dungeon items/settings.yaml @@ -0,0 +1,4 @@ +Seed: TESTTESTTEST +Small Keys: Any Dungeon +Big Keys: Any Dungeon +Maps and Compasses: Any Dungeon \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/anywhere dungeon items/settings.yaml b/mods/randomizer/generator/data/tests/logic/anywhere dungeon items/settings.yaml new file mode 100644 index 0000000000..7a0c1f658b --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/anywhere dungeon items/settings.yaml @@ -0,0 +1,4 @@ +Seed: TESTTESTTEST +Small Keys: Anywhere +Big Keys: Anywhere +Maps and Compasses: Anywhere \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/anywhere dungeon rewards/settings.yaml b/mods/randomizer/generator/data/tests/logic/anywhere dungeon rewards/settings.yaml new file mode 100644 index 0000000000..847ce9dd1d --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/anywhere dungeon rewards/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Dungeon Rewards Can Be Anywhere: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/bonko/settings.yaml b/mods/randomizer/generator/data/tests/logic/bonko/settings.yaml new file mode 100644 index 0000000000..173cd5e7f4 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/bonko/settings.yaml @@ -0,0 +1,5 @@ +Seed: TESTTESTTEST +Bonks Do Damage: On +Damage Multiplier: OHKO +Eldin Twilight Cleared: On +Lanayru Twilight Cleared: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/cleared eldin twilight/settings.yaml b/mods/randomizer/generator/data/tests/logic/cleared eldin twilight/settings.yaml new file mode 100644 index 0000000000..e7210fc32c --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/cleared eldin twilight/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Eldin Twilight Cleared: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/cleared lanayru twilight/settings.yaml b/mods/randomizer/generator/data/tests/logic/cleared lanayru twilight/settings.yaml new file mode 100644 index 0000000000..c7df62930d --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/cleared lanayru twilight/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Lanayru Twilight Cleared: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/default/settings.yaml b/mods/randomizer/generator/data/tests/logic/default/settings.yaml new file mode 100644 index 0000000000..c94c60212a --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/default/settings.yaml @@ -0,0 +1 @@ +Seed: TESTTESTTEST \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/faron twilight cleared/settings.yaml b/mods/randomizer/generator/data/tests/logic/faron twilight cleared/settings.yaml new file mode 100644 index 0000000000..9a59c656ea --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/faron twilight cleared/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Faron Twilight Cleared: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/freestanding rupees/settings.yaml b/mods/randomizer/generator/data/tests/logic/freestanding rupees/settings.yaml new file mode 100644 index 0000000000..e2e8aaaee7 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/freestanding rupees/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Freestanding Rupees: On diff --git a/mods/randomizer/generator/data/tests/logic/gifts from npcs/settings.yaml b/mods/randomizer/generator/data/tests/logic/gifts from npcs/settings.yaml new file mode 100644 index 0000000000..d09610d619 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/gifts from npcs/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Gifts From NPCs: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/golden bugs/settings.yaml b/mods/randomizer/generator/data/tests/logic/golden bugs/settings.yaml new file mode 100644 index 0000000000..9b8cac5311 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/golden bugs/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Golden Bugs: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/hidden rupees/settings.yaml b/mods/randomizer/generator/data/tests/logic/hidden rupees/settings.yaml new file mode 100644 index 0000000000..af6525cebb --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/hidden rupees/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Hidden Rupees: On diff --git a/mods/randomizer/generator/data/tests/logic/hidden skills/settings.yaml b/mods/randomizer/generator/data/tests/logic/hidden skills/settings.yaml new file mode 100644 index 0000000000..1b8d67719e --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/hidden skills/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Hidden Skills: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/hyrule barrier all dungeons/settings.yaml b/mods/randomizer/generator/data/tests/logic/hyrule barrier all dungeons/settings.yaml new file mode 100644 index 0000000000..b422f42720 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/hyrule barrier all dungeons/settings.yaml @@ -0,0 +1,3 @@ +Seed: TESTTESTTEST +Hyrule Barrier Requirements: Dungeons +Hyrule Barrier Dungeons: 8 \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/hyrule barrier fused shadows/settings.yaml b/mods/randomizer/generator/data/tests/logic/hyrule barrier fused shadows/settings.yaml new file mode 100644 index 0000000000..46cc3cee9e --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/hyrule barrier fused shadows/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Hyrule Barrier Requirements: Fused Shadows \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/hyrule barrier hearts/settings.yaml b/mods/randomizer/generator/data/tests/logic/hyrule barrier hearts/settings.yaml new file mode 100644 index 0000000000..fd421ef49e --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/hyrule barrier hearts/settings.yaml @@ -0,0 +1,3 @@ +seed: TESTTESTTEST +Hyrule Barrier Requirements: Hearts +Hyrule Barrier Hearts: 13 \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/hyrule barrier mirror shards/settings.yaml b/mods/randomizer/generator/data/tests/logic/hyrule barrier mirror shards/settings.yaml new file mode 100644 index 0000000000..7b2b9536ea --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/hyrule barrier mirror shards/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Hyrule Barrier Requirements: Mirror Shards \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/hyrule barrier open/settings.yaml b/mods/randomizer/generator/data/tests/logic/hyrule barrier open/settings.yaml new file mode 100644 index 0000000000..390c0a24dd --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/hyrule barrier open/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Hyrule Barrier Requirements: Open \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/hyrule barrier poe souls/settings.yaml b/mods/randomizer/generator/data/tests/logic/hyrule barrier poe souls/settings.yaml new file mode 100644 index 0000000000..fe433aa43d --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/hyrule barrier poe souls/settings.yaml @@ -0,0 +1,3 @@ +seed: TESTTESTTEST +Hyrule Barrier Requirements: Poe Souls +Hyrule Barrier Poe Souls: 30 \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/keysy/settings.yaml b/mods/randomizer/generator/data/tests/logic/keysy/settings.yaml new file mode 100644 index 0000000000..5ccf688d7b --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/keysy/settings.yaml @@ -0,0 +1,4 @@ +Seed: TESTTESTTEST +Small Keys: Keysy +Big Keys: Keysy +Maps and Compasses: Start With \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/max entrance rando/settings.yaml b/mods/randomizer/generator/data/tests/logic/max entrance rando/settings.yaml new file mode 100644 index 0000000000..c95a0803c5 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/max entrance rando/settings.yaml @@ -0,0 +1,16 @@ +Seed: TESTTESTTEST +Faron Woods Logic: Open +Skip Prologue: On +Faron Twilight Cleared: On +Eldin Twilight Cleared: On +Lanayru Twilight Cleared: On +Skip Midna's Desparate Hour: On +Randomize Starting Spawn: On +Randomize Dungeon Entrances: On +Randomize Boss Entrances: On +Randomize Grotto Entrances: On +Randomize Cave Entrances: On +Randomize Interior Entrances: On +Randomize Overworld Entrances: On +Decouple Double Door Entrances: On +Decouple Entrances: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/mixed entrance pools/settings.yaml b/mods/randomizer/generator/data/tests/logic/mixed entrance pools/settings.yaml new file mode 100644 index 0000000000..77babdaf99 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/mixed entrance pools/settings.yaml @@ -0,0 +1,15 @@ +Seed: TESTTESTTEST +Faron Woods Logic: Open +Skip Prologue: On +Faron Twilight Cleared: On +Eldin Twilight Cleared: On +Lanayru Twilight Cleared: On +Skip Midna's Desparate Hour: On +Randomize Starting Spawn: On +Randomize Dungeon Entrances: On +Randomize Grotto Entrances: On +Randomize Cave Entrances: On +Randomize Interior Entrances: On +Randomize Overworld Entrances: On +Mixed Entrance Pools: + [["Dungeon", "Grotto"], ["Overworld", "Cave", "Interior"]] \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/open forest/settings.yaml b/mods/randomizer/generator/data/tests/logic/open forest/settings.yaml new file mode 100644 index 0000000000..9684708bbe --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/open forest/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Faron Woods Logic: Open \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/open start/settings.yaml b/mods/randomizer/generator/data/tests/logic/open start/settings.yaml new file mode 100644 index 0000000000..1c93c6e244 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/open start/settings.yaml @@ -0,0 +1,7 @@ +Seed: TESTTESTTEST +Skip Prologue: On +Faron Woods Logic: Open +Faron Twilight Cleared: On +Eldin Twilight Cleared: On +Lanayru Twilight Cleared: On +Unlock Map Regions: On diff --git a/mods/randomizer/generator/data/tests/logic/overworld items/settings.yaml b/mods/randomizer/generator/data/tests/logic/overworld items/settings.yaml new file mode 100644 index 0000000000..25f6cb1269 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/overworld items/settings.yaml @@ -0,0 +1,4 @@ +Seed: TESTTESTTEST +Small Keys: Overworld +Big Keys: Overworld +Maps and Compasses: Overworld \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/own dungeon items/settings.yaml b/mods/randomizer/generator/data/tests/logic/own dungeon items/settings.yaml new file mode 100644 index 0000000000..5446c402b4 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/own dungeon items/settings.yaml @@ -0,0 +1,4 @@ +Seed: TESTTESTTEST +Small Keys: Own Dungeon +Big Keys: Own Dungeon +Maps and Compasses: Own Dungeon \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/palace fused shadows/settings.yaml b/mods/randomizer/generator/data/tests/logic/palace fused shadows/settings.yaml new file mode 100644 index 0000000000..43c7ddcaa8 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/palace fused shadows/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Palace of Twilight Requirements: Fused Shadows \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/palace mirror shards/settings.yaml b/mods/randomizer/generator/data/tests/logic/palace mirror shards/settings.yaml new file mode 100644 index 0000000000..5bfe56e251 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/palace mirror shards/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Palace of Twilight Requirements: Mirror Shards \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/palace open/settings.yaml b/mods/randomizer/generator/data/tests/logic/palace open/settings.yaml new file mode 100644 index 0000000000..4a2f81e0dd --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/palace open/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Palace of Twilight Requirements: Open \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/poe souls all/settings.yaml b/mods/randomizer/generator/data/tests/logic/poe souls all/settings.yaml new file mode 100644 index 0000000000..47999cb3a1 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/poe souls all/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Poe Souls: All \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/poe souls dungeon/settings.yaml b/mods/randomizer/generator/data/tests/logic/poe souls dungeon/settings.yaml new file mode 100644 index 0000000000..8ca225b4b2 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/poe souls dungeon/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Poe Souls: Dungeon \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/poe souls overworld/settings.yaml b/mods/randomizer/generator/data/tests/logic/poe souls overworld/settings.yaml new file mode 100644 index 0000000000..27b7142f46 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/poe souls overworld/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Poe Souls: Overworld \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/random boss entrances/settings.yaml b/mods/randomizer/generator/data/tests/logic/random boss entrances/settings.yaml new file mode 100644 index 0000000000..d16615de50 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/random boss entrances/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Randomize Boss Entrances: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/random cave entrances/settings.yaml b/mods/randomizer/generator/data/tests/logic/random cave entrances/settings.yaml new file mode 100644 index 0000000000..5f51f783c7 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/random cave entrances/settings.yaml @@ -0,0 +1,8 @@ +Seed: TESTTESTTEST +Faron Woods Logic: Open +Skip Prologue: On +Faron Twilight Cleared: On +Eldin Twilight Cleared: On +Lanayru Twilight Cleared: On +Skip Midna's Desparate Hour: On +Randomize Cave Entrances: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/random dungeon entrances/settings.yaml b/mods/randomizer/generator/data/tests/logic/random dungeon entrances/settings.yaml new file mode 100644 index 0000000000..32fabb63a9 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/random dungeon entrances/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Randomize Dungeon Entrances: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/random grotto entrances/settings.yaml b/mods/randomizer/generator/data/tests/logic/random grotto entrances/settings.yaml new file mode 100644 index 0000000000..792f886bef --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/random grotto entrances/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Randomize Grotto Entrances: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/random interior entrances/settings.yaml b/mods/randomizer/generator/data/tests/logic/random interior entrances/settings.yaml new file mode 100644 index 0000000000..6510d279bf --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/random interior entrances/settings.yaml @@ -0,0 +1,8 @@ +Seed: TESTTESTTEST +Faron Woods Logic: Open +Skip Prologue: On +Faron Twilight Cleared: On +Eldin Twilight Cleared: On +Lanayru Twilight Cleared: On +Skip Midna's Desparate Hour: On +Randomize Interior Entrances: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/random overworld entrances/settings.yaml b/mods/randomizer/generator/data/tests/logic/random overworld entrances/settings.yaml new file mode 100644 index 0000000000..a018903bf5 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/random overworld entrances/settings.yaml @@ -0,0 +1,8 @@ +Seed: TESTTESTTEST +Faron Woods Logic: Open +Skip Prologue: On +Faron Twilight Cleared: On +Eldin Twilight Cleared: On +Lanayru Twilight Cleared: On +Skip Midna's Desparate Hour: On +Randomize Overworld Entrances: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/randomize starting spawn/settings.yaml b/mods/randomizer/generator/data/tests/logic/randomize starting spawn/settings.yaml new file mode 100644 index 0000000000..27784d7b97 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/randomize starting spawn/settings.yaml @@ -0,0 +1,8 @@ +Seed: TESTTESTTEST +Faron Woods Logic: Open +Skip Prologue: On +Faron Twilight Cleared: On +Eldin Twilight Cleared: On +Lanayru Twilight Cleared: On +Skip Midna's Desparate Hour: On +Randomize Starting Spawn: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/scarcity minimal/settings.yaml b/mods/randomizer/generator/data/tests/logic/scarcity minimal/settings.yaml new file mode 100644 index 0000000000..c5de27cabd --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/scarcity minimal/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Item Scarcity: Minimal \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/scarcity plentiful/settings.yaml b/mods/randomizer/generator/data/tests/logic/scarcity plentiful/settings.yaml new file mode 100644 index 0000000000..96e1309d08 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/scarcity plentiful/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Item Scarcity: Plentiful \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/shop items/settings.yaml b/mods/randomizer/generator/data/tests/logic/shop items/settings.yaml new file mode 100644 index 0000000000..bfc793d443 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/shop items/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Shop Items: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/skip dungeon entrance requirements/settings.yaml b/mods/randomizer/generator/data/tests/logic/skip dungeon entrance requirements/settings.yaml new file mode 100644 index 0000000000..3ec52cb217 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/skip dungeon entrance requirements/settings.yaml @@ -0,0 +1,8 @@ +Seed: TESTTESTTEST +Lakebed Does Not Require Water Bombs: On +Arbiters Does Not Require Bulblin Camp: On +Snowpeak Does Not Require Reekfish Scent: On +Sacred Grove Does Not Require Skull Kid: On +City Does Not Require Filled Skybook: On +Goron Mines Entrance: On +Temple of Time Sword Requirement: None \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/skip mdh/settings.yaml b/mods/randomizer/generator/data/tests/logic/skip mdh/settings.yaml new file mode 100644 index 0000000000..d08be69c12 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/skip mdh/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Skip Midna's Desparate Hour: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/skip prologue/settings.yaml b/mods/randomizer/generator/data/tests/logic/skip prologue/settings.yaml new file mode 100644 index 0000000000..4600a7d811 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/skip prologue/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Skip Prologue: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/sky characters/settings.yaml b/mods/randomizer/generator/data/tests/logic/sky characters/settings.yaml new file mode 100644 index 0000000000..e840960681 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/sky characters/settings.yaml @@ -0,0 +1,2 @@ +Seed: TESTTESTTEST +Sky Characters: On \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/unrequired dungeons are barren/settings.yaml b/mods/randomizer/generator/data/tests/logic/unrequired dungeons are barren/settings.yaml new file mode 100644 index 0000000000..99d39af282 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/unrequired dungeons are barren/settings.yaml @@ -0,0 +1,3 @@ +Seed: TESTTESTTEST +Unrequired Dungeons Are Barren: On +Hyrule Barrier Requirements: Mirror Shards \ No newline at end of file diff --git a/mods/randomizer/generator/data/tests/logic/wolf start/settings.yaml b/mods/randomizer/generator/data/tests/logic/wolf start/settings.yaml new file mode 100644 index 0000000000..c509bd8e43 --- /dev/null +++ b/mods/randomizer/generator/data/tests/logic/wolf start/settings.yaml @@ -0,0 +1,4 @@ +Seed: TESTTESTTEST +Starting Form: Wolf +Skip Prologue: On +Faron Woods Logic: Open \ No newline at end of file diff --git a/mods/randomizer/generator/data/text/languages/english.yaml b/mods/randomizer/generator/data/text/languages/english.yaml new file mode 100644 index 0000000000..839d70061e --- /dev/null +++ b/mods/randomizer/generator/data/text/languages/english.yaml @@ -0,0 +1,2417 @@ +# This file contains all custom English text for the dusklight randomizer + +# NOTES FOR TRANSLATORS: +# - You should only be translating the "Text" fields for each element in this file. Do not translate the +# - Text being surrounded by braces '{}' means that the text will be colored. If a text field begins with a brace, +# the entire field must be surrounded with quotation marks. +# - Below each text element, you can specify a given text's gender and/or plurality. If you need additional +# specifiers for pieces of text, let us know. If no gender is provided, the assumption is no gender. If no +# plurality is provided, the assumed plurality is singular. + +# ITEM NAMES +Green Rupee: + Standard: + Text: Green Rupee + Pretty: + Text: a {Green Rupee} + Cryptic: + Text: a {penny} + +Blue Rupee: + Standard: + Text: Blue Rupee + Pretty: + Text: a {Blue Rupee} + Cryptic: + Text: a {fiver} + +Yellow Rupee: + Standard: + Text: Yellow Rupee + Pretty: + Text: a {Yellow Rupee} + Cryptic: + Text: some {change} + +Red Rupee: + Standard: + Text: Red Rupee + Pretty: + Text: a {Red Rupee} + Cryptic: + Text: "{couch cash}" + +Purple Rupee: + Standard: + Text: Purple Rupee + Pretty: + Text: a {Purple Rupee} + Cryptic: + Text: a {good sum} + +Orange Rupee: + Standard: + Text: Orange Rupee + Pretty: + Text: an {Orange Rupee} + Cryptic: + Text: a {payday} + +Silver Rupee: + Standard: + Text: Silver Rupee + Pretty: + Text: a {Silver Rupee} + Cryptic: + Text: "{many riches}" + +Bombs 5: + Standard: + Text: Bombs 5 + Pretty: + Text: "{Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 10: + Standard: + Text: Bombs 10 + Pretty: + Text: "{Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 20: + Standard: + Text: Bombs 20 + Pretty: + Text: "{Bombs (20)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 30: + Standard: + Text: Bombs 30 + Pretty: + Text: "{Bombs (30)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Arrows 10: + Standard: + Text: Arrows 10 + Pretty: + Text: "{Arrows (10)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 20: + Standard: + Text: Arrows 20 + Pretty: + Text: "{Arrows (20)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 30: + Standard: + Text: Arrows 30 + Pretty: + Text: "{Arrows (30)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Seeds 50: + Standard: + Text: Seeds 50 + Pretty: + Text: "{Seeds (50)}" + Plurality: Plural + Cryptic: + Text: some {pellets} + Plurality: Plural + +Foolish Item: + Standard: + Text: Foolish Item + Pretty: + Text: a {Foolish Item} + Cryptic: + Text: a {chilly surprise} + +Ordon Spring Portal: + Standard: + Text: Ordon Spring Portal + Pretty: + Text: the {Ordon Spring Portal} + Cryptic: + Text: a {portal to home} + +South Faron Portal: + Standard: + Text: South Faron Portal + Pretty: + Text: the {South Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Water Bombs 5: + Standard: + Text: Water Bombs 5 + Pretty: + Text: "{Water Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 10: + Standard: + Text: Water Bombs 10 + Pretty: + Text: "{Water Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 15: + Standard: + Text: Water Bombs 15 + Pretty: + Text: "{Water Bombs (15)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Bomblings 5: + Standard: + Text: Bomblings 5 + Pretty: + Text: "{Bomblings (5)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Bomblings 10: + Standard: + Text: Bomblings 10 + Pretty: + Text: "{Bomblings (10)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Piece of Heart: + Standard: + Text: Piece of Heart + Pretty: + Text: a {Piece of Heart} + Cryptic: + Text: some {love} + +Heart Container: + Standard: + Text: Heart Container + Pretty: + Text: a {Heart Container} + Cryptic: + Text: a {lot of love} + +Ordon Shield: + Standard: + Text: Ordon Shield + Pretty: + Text: the {Ordon Shield} + Cryptic: + Text: a {sturdy reminder of home} + +Wooden Shield: + Standard: + Text: Wooden Shield + Pretty: + Text: a {Wooden Shield} + Cryptic: + Text: a {wood protector} + +Hylian Shield: + Standard: + Text: Hylian Shield + Pretty: + Text: the {Hylian Shield} + Cryptic: + Text: an {unbreakable shield} + +Magic Armor: + Standard: + Text: Magic Armor + Pretty: + Text: the {Magic Armor} + Cryptic: + Text: "{magical clothing}" + +Zora Armor: + Standard: + Text: Zora Armor + Pretty: + Text: the {Zora Armor} + Cryptic: + Text: the {fish suit} + +Shadow Crystal: + Standard: + Text: Shadow Crystal + Pretty: + Text: the {Shadow Crystal} + Cryptic: + Text: a {crystal of dark power} + +Progressive Wallet: + Standard: + Text: Progressive Wallet + Pretty: + Text: a {Wallet} + Cryptic: + Text: a {money bag} + +Upper Zoras River Portal: + Standard: + Text: Upper Zoras River Portal + Pretty: + Text: the {Upper Zoras River Portal} + Cryptic: + Text: a {portal to some raging rapids} + +Castle Town Portal: + Standard: + Text: Castle Town Portal + Pretty: + Text: the {Castle Town Portal} + Cryptic: + Text: a {portal to the city} + +Gerudo Desert Portal: + Standard: + Text: Gerudo Desert Portal + Pretty: + Text: the {Gerudo Desert Portal} + Cryptic: + Text: a {portal to a challenging cave} + +North Faron Portal: + Standard: + Text: North Faron Portal + Pretty: + Text: the {North Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Hawkeye: + Standard: + Text: Hawkeye + Pretty: + Text: the {Hawkeye} + Cryptic: + Text: the {zoom-and-enhance} + +Progressive Sword: + Standard: + Text: Progressive Sword + Pretty: + Text: a {Sword} + Cryptic: + Text: a {sharp weapon} + +Gale Boomerang: + Standard: + Text: Gale Boomerang + Pretty: + Text: the {Gale Boomerang} + Cryptic: + Text: the {fairy of winds} + +Spinner: + Standard: + Text: Spinner + Pretty: + Text: the {Spinner} + Cryptic: + Text: the {gear rotator} + +Ball and Chain: + Standard: + Text: Ball and Chain + Pretty: + Text: the {Ball and Chain} + Cryptic: + Text: the {iron weight} + +Progressive Bow: + Standard: + Text: Progressive Bow + Pretty: + Text: a {Bow} + Cryptic: + Text: an {arrow launcher} + +Progressive Clawshot: + Standard: + Text: Progressive Clawshot + Pretty: + Text: a {Clawshot} + Cryptic: + Text: a {chain launcher} + +Iron Boots: + Standard: + Text: Iron Boots + Pretty: + Text: the {Iron Boots} + Plurality: Plural + Cryptic: + Text: the {heavy shoes} + Plurality: Plural + +Progressive Dominion Rod: + Standard: + Text: Dominion Rod + Pretty: + Text: a {Dominion Rod} + Cryptic: + Text: a {rod of control} + +Lantern: + Standard: + Text: Lantern + Pretty: + Text: the {Lantern} + Cryptic: + Text: the {small light} + +Progressive Fishing Rod: + Standard: + Text: Progressive Fishing Rod + Pretty: + Text: a {Fishing Rod} + Cryptic: + Text: a {rod of patience} + +Slingshot: + Standard: + Text: Slingshot + Pretty: + Text: the {Slingshot} + Cryptic: + Text: the {child's toy} + +Kakariko Gorge Portal: + Standard: + Text: Kakariko Gorge Portal + Pretty: + Text: the {Kakariko Gorge Portal} + Cryptic: + Text: a {portal to a big gap} + +Kakariko Village Portal: + Standard: + Text: Kakariko Village Portal + Pretty: + Text: the {Kakariko Village Portal} + Cryptic: + Text: a {portal to a village} + +Giant Bomb Bag: + Standard: + Text: Giant Bomb Bag + Pretty: + Text: a {Giant Bomb Bag} + Cryptic: + Text: an {explosive capacity upgrade} + +Bomb Bag: + Standard: + Text: Bomb Bag + Pretty: + Text: a {Bomb Bag} + Cryptic: + Text: a {bag for explosions} + +Death Mountain Portal: + Standard: + Text: Death Mountain Portal + Pretty: + Text: the {Death Mountain Portal} + Cryptic: + Text: a {portal to a volcano} + +Zoras Domain Portal: + Standard: + Text: Zoras Domain Portal + Pretty: + Text: the {Zora's Domain Portal} + Cryptic: + Text: a {portal to water} + +Empty Bottle: + Standard: + Text: Empty Bottle + Pretty: + Text: an {Empty Bottle} + Cryptic: + Text: + +Red Potion Shop: + Standard: + Text: Red Potion Shop + Pretty: + Text: a {Red Potion} + Cryptic: + Text: a {health refill} + +Blue Potion Shop: + Standard: + Text: Blue Potion Shop + Pretty: + Text: a {Blue Potion} + Cryptic: + Text: a {blue health refill} + +Bottle with Half Milk: + Standard: + Text: Bottle with Half Milk + Pretty: + Text: a {Bottle with Half Milk} + Cryptic: + Text: a {baby bottle} + +Fairy Tears: + Standard: + Text: Fairy Tears + Pretty: + Text: some {Fairy Tears} + Plurality: Plural + Cryptic: + Text: a {refill of great power} + +Bottle with Great Fairies Tears: + Standard: + Text: Bottle with Great Fairies Tears + Pretty: + Text: a {Bottle with Great Fairies Tears} + Cryptic: + Text: a {bottle of great power} + +Renados Letter: + Standard: + Text: Renados Letter + Pretty: + Text: "{Renado's Letter}" + Cryptic: + Text: a {letter from a concerned shaman} + +Invoice: + Standard: + Text: Invoice + Pretty: + Text: the {Invoice} + Cryptic: + Text: the {bill for the doctor} + +Wooden Statue: + Standard: + Text: Wooden Statue + Pretty: + Text: the {Wooden Statue} + Cryptic: + Text: "{memories of home}" + Plurality: Plural + +Ilias Charm: + Standard: + Text: Ilias Charm + Pretty: + Text: "{Ilias Charm}" + Cryptic: + Text: a {friend's item} + +Horse Call: + Standard: + Text: Horse Call + Pretty: + Text: the {Horse Call} + Cryptic: + Text: the {horse beckoner} + +Forest Temple Small Key: + Standard: + Text: Forest Temple Small Key + Pretty: + Text: a {Forest Temple Small Key} + Cryptic: + Text: a {key for a deep forest} + +Goron Mines Small Key: + Standard: + Text: Goron Mines Small Key + Pretty: + Text: a {Goron Mines Small Key} + Cryptic: + Text: a {key for a volcanic mine} + +Lakebed Temple Small Key: + Standard: + Text: Lakebed Temple Small Key + Pretty: + Text: a {Lakebed Temple Small Key} + Cryptic: + Text: a {key for an underground lake} + +Arbiters Grounds Small Key: + Standard: + Text: Arbiters Grounds Small Key + Pretty: + Text: an {Arbiters Grounds Small Key} + Cryptic: + Text: a {key for an ancient prison} + +Snowpeak Ruins Small Key: + Standard: + Text: Snowpeak Ruins Small Key + Pretty: + Text: a {Snowpeak Ruins Small Key} + Cryptic: + Text: a {key for a snowy mansion} + +Temple of Time Small Key: + Standard: + Text: Temple of Time Small Key + Pretty: + Text: a {Temple of Time Small Key} + Cryptic: + Text: a {key for the past} + +City in the Sky Small Key: + Standard: + Text: City in the Sky Small Key + Pretty: + Text: the {City in the Sky Small Key} + Cryptic: + Text: a {key for the skies above} + +Palace of Twilight Small Key: + Standard: + Text: Palace of Twilight Small Key + Pretty: + Text: a {Palace of Twilight Small Key} + Cryptic: + Text: a {key for a another realm} + +Hyrule Castle Small Key: + Standard: + Text: Hyrule Castle Small Key + Pretty: + Text: a {Hyrule Castle Small Key} + Cryptic: + Text: a {key for a kingdom's castle} + +Gerudo Desert Bulblin Camp Key: + Standard: + Text: Gerudo Bulblin Camp Small Key + Pretty: + Text: the {Gerudo Desert Bulblin Camp Key} + Cryptic: + Text: the {key for a desert tent} + +Lake Hylia Portal: + Standard: + Text: Lake Hylia Portal + Pretty: + Text: the {Lake Hylia Portal} + Cryptic: + Text: a {portal to a vast lake} + +Aurus Memo: + Standard: + Text: Aurus Memo + Pretty: + Text: "{Auru's Memo}" + Cryptic: + Text: a {friend's favor} + +Asheis Sketch: + Standard: + Text: Asheis Sketch + Pretty: + Text: "{Ashei's Sketch}" + Cryptic: + Text: a {sketch of a horrific beast} + +Forest Temple Big Key: + Standard: + Text: Forest Temple Big Key + Pretty: + Text: the {Forest Temple Big Key} + Cryptic: + Text: the {key to the twilit parasite} + +Lakebed Temple Big Key: + Standard: + Text: Lakebed Temple Big Key + Pretty: + Text: the {Lakebed Temple Big Key} + Cryptic: + Text: the {key to the twilit aquatic} + +Arbiters Grounds Big Key: + Standard: + Text: Arbiters Grounds Big Key + Pretty: + Text: the {Arbiters Grounds Big Key} + Cryptic: + Text: the {key to the twilit fossil} + +Temple of Time Big Key: + Standard: + Text: Temple of Time Big Key + Pretty: + Text: the {Temple of Time Big Key} + Cryptic: + Text: the {key to the twilit arachnid} + +City in the Sky Big Key: + Standard: + Text: City in the Sky Big Key + Pretty: + Text: the {City in the Sky Big Key} + Cryptic: + Text: the {key to the twilit dragon} + +Palace of Twilight Big Key: + Standard: + Text: Palace of Twilight Big Key + Pretty: + Text: the {Palace of Twilight Big Key} + Cryptic: + Text: the {key to the usurper king} + +Hyrule Castle Big Key: + Standard: + Text: Hyrule Castle Big Key + Pretty: + Text: a {Hyrule Castle Big Key} + Cryptic: + Text: the {key to the castle throne room} + +Forest Temple Compass: + Standard: + Text: Forest Temple Compass + Pretty: + Text: the {Forest Temple Compass} + Cryptic: + Text: the {pointer for a deep forest} + +Goron Mines Compass: + Standard: + Text: Goron Mines Compass + Pretty: + Text: the {Goron Mines Compass} + Cryptic: + Text: the {pointer for a volcano} + +Lakebed Temple Compass: + Standard: + Text: Lakebed Temple Compass + Pretty: + Text: the {Lakebed Temple Compass} + Cryptic: + Text: the {pointer for an underground lake} + +Bottle with Lantern Oil: + Standard: + Text: Bottle with Lantern Oil + Pretty: + Text: a {Bottle with Lantern Oil} + Cryptic: + Text: a {bottle with lighter fluid} + +Progressive Mirror Shard: + Standard: + Text: Progressive Mirror Shard + Pretty: + Text: a {Mirror Shard} + Cryptic: + Text: a {reflective shard of power} + +Arbiters Grounds Compass: + Standard: + Text: Arbiters Grounds Compass + Pretty: + Text: the {Arbiters Grounds Compass} + Cryptic: + Text: the {pointer for an ancient prison} + +Snowpeak Ruins Compass: + Standard: + Text: Snowpeak Ruins Compass + Pretty: + Text: the {Snowpeak Ruins Compass} + Cryptic: + Text: the {pointer for a snowy mansion} + +Temple of Time Compass: + Standard: + Text: Temple of Time Compass + Pretty: + Text: the {Temple of Time Compass} + Cryptic: + Text: the {pointer for the past} + +City in the Sky Compass: + Standard: + Text: City in the Sky Compass + Pretty: + Text: the {City in the Sky Compass} + Cryptic: + Text: the {pointer for the skies above} + +Palace of Twilight Compass: + Standard: + Text: Palace of Twilight Compass + Pretty: + Text: the {Palace of Twilight Compass} + Cryptic: + Text: the {pointer for another realm} + +Hyrule Castle Compass: + Standard: + Text: Hyrule Castle Compass + Pretty: + Text: a {Hyrule Castle Compass} + Cryptic: + Text: the {pointer for the kingdom's castle} + +Mirror Chamber Portal: + Standard: + Text: Mirror Chamber Portal + Pretty: + Text: the {Mirror Chamber Portal} + Cryptic: + Text: a {portal to a coliseum} + +Snowpeak Portal: + Standard: + Text: Snowpeak Portal + Pretty: + Text: the {Snowpeak Portal} + Cryptic: + Text: a {portal to a snowy mountain} + +Forest Temple Dungeon Map: + Standard: + Text: Forest Temple Dungeon Map + Pretty: + Text: the {Forest Temple Dungeon Map} + Cryptic: + Text: the {map for a deep forest} + +Goron Mines Dungeon Map: + Standard: + Text: Goron Mines Dungeon Map + Pretty: + Text: the {Goron Mines Dungeon Map} + Cryptic: + Text: the {map for a volcano} + +Lakebed Temple Dungeon Map: + Standard: + Text: Lakebed Temple Dungeon Map + Pretty: + Text: the {Lakebed Temple Dungeon Map} + Cryptic: + Text: the {map for an underground lake} + +Arbiters Grounds Dungeon Map: + Standard: + Text: Arbiters Grounds Dungeon Map + Pretty: + Text: the {Arbiters Grounds Dungeon Map} + Cryptic: + Text: the {map for an ancient prison} + +Snowpeak Ruins Dungeon Map: + Standard: + Text: Snowpeak Ruins Dungeon Map + Pretty: + Text: the {Snowpeak Ruins Dungeon Map} + Cryptic: + Text: the {map for a snowy mansion} + +Temple of Time Dungeon Map: + Standard: + Text: Temple of Time Dungeon Map + Pretty: + Text: the {Temple of Time Dungeon Map} + Cryptic: + Text: the {map for the past} + +City in the Sky Dungeon Map: + Standard: + Text: City in the Sky Dungeon Map + Pretty: + Text: the {City in the Sky Dungeon Map} + Cryptic: + Text: the {map for the skies above} + +Palace of Twilight Dungeon Map: + Standard: + Text: Palace of Twilight Dungeon Map + Pretty: + Text: the {Palace of Twilight Dungeon Map} + Cryptic: + Text: the {map for another realm} + +Hyrule Castle Dungeon Map: + Standard: + Text: Hyrule Castle Dungeon Map + Pretty: + Text: a {Hyrule Castle Dungeon Map} + Cryptic: + Text: the {map for the kingdom's castle} + +Sacred Grove Portal: + Standard: + Text: Sacred Grove Portal + Pretty: + Text: the {Sacred Grove Portal} + Cryptic: + Text: a {portal to an ancient forest} + +Male Beetle: + Standard: + Text: Male Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Female Beetle: + Standard: + Text: Female Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Male Butterfly: + Standard: + Text: Male Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Female Butterfly: + Standard: + Text: Female Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Male Stag Beetle: + Standard: + Text: Male Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Female Stag Beetle: + Standard: + Text: Female Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Male Grasshopper: + Standard: + Text: Male Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Female Grasshopper: + Standard: + Text: Female Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Male Phasmid: + Standard: + Text: Male Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Female Phasmid: + Standard: + Text: Female Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Male Pill Bug: + Standard: + Text: Male Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Female Pill Bug: + Standard: + Text: Female Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Male Mantis: + Standard: + Text: Male Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Female Mantis: + Standard: + Text: Female Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Male Ladybug: + Standard: + Text: Male Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Female Ladybug: + Standard: + Text: Female Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Male Snail: + Standard: + Text: Male Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Female Snail: + Standard: + Text: Female Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Male Dragonfly: + Standard: + Text: Male Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Female Dragonfly: + Standard: + Text: Female Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Male Ant: + Standard: + Text: Male Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Female Ant: + Standard: + Text: Female Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Male Dayfly: + Standard: + Text: Male Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Female Dayfly: + Standard: + Text: Female Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Progressive Fused Shadow: + Standard: + Text: Progressive Fused Shadow + Pretty: + Text: a {Fused Shadow} + Cryptic: + Text: a {shadow of ultimate power} + +Poe Soul: + Standard: + Text: Poe Soul + Pretty: + Text: a {Poe Soul} + Cryptic: + Text: a {soul of the dead} + +Progressive Hidden Skill: + Standard: + Text: Progressive Hidden Skill + Pretty: + Text: a {Hidden Skill} + Cryptic: + Text: a {forgotten technique} + + +Bridge of Eldin Portal: + Standard: + Text: Bridge of Eldin Portal + Pretty: + Text: the {Bridge of Eldin Portal} + Cryptic: + Text: a {portal to a long bridge} + +Progressive Sky Book: + Standard: + Text: Progressive Sky Book + Pretty: + Text: a {Sky Character} + Cryptic: + Text: a {glyph of the heavens} + +Purple Rupee Links House: + Standard: + Text: Purple Rupee Links House + Pretty: + Text: the {Purple Rupee from your basement} + Cryptic: + Text: "{your savings}" + Plurality: Plural + +North Faron Woods Gate Key: + Standard: + Text: North Faron Woods Gate Key + Pretty: + Text: the {North Faron Woods Gate Key} + Cryptic: + Text: a {key to a northern forest} + +Gate Keys: + Standard: + Text: Gate Keys + Pretty: + Text: the {Gate Keys} + Plurality: Plural + Cryptic: + Text: "{King Bulblin's keys}" + Plurality: Plural + +Ordon Pumpkin: + Standard: + Text: Ordon Pumpkin + Pretty: + Text: the {Ordon Pumpkin} + Cryptic: + Text: a {soup ingredient} + +Ordon Cheese: + Standard: + Text: Ordon Cheese + Pretty: + Text: some {Ordon Cheese} + Cryptic: + Text: a {soup ingredient} + +Snowpeak Ruins Bedroom Key: + Standard: + Text: Snowpeak Ruins Bedroom Key + Pretty: + Text: the {Snowpeak Ruins Bedroom Key} + Cryptic: + Text: the {key to a snowy bedroom} + +Goron Mines Key Shard: + Standard: + Text: Goron Mines Key Shard + Pretty: + Text: a {Goron Mines Key Shard} + Cryptic: + Text: "{one third of a key}" + +Coro Key: + Standard: + Text: Coro Key + Pretty: + Text: "{Coro's Key}" + Cryptic: + Text: a {key to a forest cave} + +Game Beatable: + Standard: + Text: Game Beatable + Pretty: + Text: "{Game Beatable}" + Cryptic: + Text: the {game-winning item} + +Hint: + Standard: + Text: Hint + Pretty: + Text: a {Hint} + Cryptic: + Text: a {piece of knowledge} + +Faron Twilight Tear: + Standard: + Text: Faron Twilight Tear + Pretty: + Text: a {Faron Twilight Tear} + Cryptic: + Text: a {tear of a forest spirit} + +Eldin Twilight Tear: + Standard: + Text: Eldin Twilight Tear + Pretty: + Text: an {Eldin Twilight Tear} + Cryptic: + Text: a {tear of a volcano spirit} + +Lanayru Twilight Tear: + Standard: + Text: Lanayru Twilight Tear + Pretty: + Text: a {Lanayru Twilight Tear} + Cryptic: + Text: a {tear of a lake spirit} + +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Fishing Rod + Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) + +# HINT REGION NAMES +Ordon: + Standard: + Text: Ordon + Pretty: + Text: "{Ordon}" + Cryptic: + Text: a {quaint village} + +Faron Woods: + Standard: + Text: Faron Woods + Pretty: + Text: "{Faron Woods}" + Cryptic: + Text: a {forest} + +Sacred Grove: + Standard: + Text: Sacred Grove + Pretty: + Text: the {Sacred Grove} + Cryptic: + Text: a {hidden grove} + +Faron Field: + Standard: + Text: Faron Field + Pretty: + Text: "{Faron Field}" + Cryptic: + Text: a {field near the forest} + +Kakariko Gorge: + Standard: + Text: Kakariko Gorge + Pretty: + Text: "{Kakariko Gorge}" + Cryptic: + Text: a {field with a large chasm} + +Kakariko Village: + Standard: + Text: Kakariko Village + Pretty: + Text: "{Kakariko Village}" + Cryptic: + Text: a {charming village} + +Kakariko Graveyard: + Standard: + Text: Kakariko Graveyard + Pretty: + Text: the {Kakariko Graveyard} + Cryptic: + Text: a {yard for the dead} + +Death Mountain: + Standard: + Text: Death Mountain + Pretty: + Text: "{Death Mountain}" + Cryptic: + Text: a {volcano path} + +Eldin Field: + Standard: + Text: Eldin Field + Pretty: + Text: "{Eldin Field}" + Cryptic: + Text: a {field near a volcano} + +North Eldin: + Standard: + Text: North Eldin + Pretty: + Text: "{North Eldin}" + Cryptic: + Text: a {narrow gray field} + +Hidden Village: + Standard: + Text: Hidden Village + Pretty: + Text: the {Hidden Village} + Cryptic: + Text: a {secluded settlement} + +Lanayru Field: + Standard: + Text: Lanayru Field + Pretty: + Text: "{Lanayru Field}" + Cryptic: + Text: a {field with a river} + +Beside Castle Town: + Standard: + Text: Beside Castle Town + Pretty: + Text: "{Beside Castle Town}" + Cryptic: + Text: a {field beside a city} + +Castle Town: + Standard: + Text: Castle Town + Pretty: + Text: "{Castle Town}" + Cryptic: + Text: a {city} + +South of Castle Town: + Standard: + Text: South of Castle Town + Pretty: + Text: "{South of Castle Town}" + Cryptic: + Text: a {field south of a city} + +Great Bridge of Hylia: + Standard: + Text: Great Bridge of Hylia + Pretty: + Text: the {Great Bridge of Hylia} + Cryptic: + Text: a {path along a great bridge} + +Lake Hylia: + Standard: + Text: Lake Hylia + Pretty: + Text: "{Lake Hylia}" + Cryptic: + Text: a {vast lake} + +Lanayru Spring: + Standard: + Text: Lanayru Spring + Pretty: + Text: the {Lanayru Spring} + Cryptic: + Text: a {cavernous spring} + +Upper Zoras River: + Standard: + Text: Upper Zoras River + Pretty: + Text: "{Upper Zoras River}" + Cryptic: + Text: a {fork in the river} + +Zoras Domain: + Standard: + Text: Zoras Domain + Pretty: + Text: "{Zoras Domain}" + Cryptic: + Text: the {home of a grand waterfall} + +South Gerudo Desert: + Standard: + Text: South Gerudo Desert + Pretty: + Text: "{South Gerudo Desert}" + Cryptic: + Text: the {southern desert} + +North Gerudo Desert: + Standard: + Text: North Gerudo Desert + Pretty: + Text: "{North Gerudo Desert}" + Cryptic: + Text: the {northern desert} + +Bublin Camp: + Standard: + Text: Bublin Camp + Pretty: + Text: "{Bublin Camp}" + Cryptic: + Text: a {camp of enemies} + +Mirror Chamber: + Standard: + Text: Mirror Chamber + Pretty: + Text: the {Mirror Chamber} + Cryptic: + Text: a {chamber of chains} + +Forest Temple: + Standard: + Text: Forest Temple + Pretty: + Text: the {Forest Temple} + Cryptic: + Text: a {deep forest} + +Goron Mines: + Standard: + Text: Goron Mines + Pretty: + Text: the {Goron Mines} + Cryptic: + Text: a {volcanic mine} + +Lakebed Temple: + Standard: + Text: Lakebed Temple + Pretty: + Text: the {Lakebed Temple} + Cryptic: + Text: an {underground lake} + +Arbiters Grounds: + Standard: + Text: Arbiters Grounds + Pretty: + Text: the {Arbiters Grounds} + Cryptic: + Text: an {ancient prison} + +Snowpeak Ruins: + Standard: + Text: Snowpeak Ruins + Pretty: + Text: the {Snowpeak Ruins} + Cryptic: + Text: a {snowy mansion} + +Temple of Time: + Standard: + Text: Temple of Time + Pretty: + Text: the {Temple of Time} + Cryptic: + Text: the {past} + +City in the Sky: + Standard: + Text: City in the Sky + Pretty: + Text: the {City in the Sky} + Cryptic: + Text: the {skies above} + +Palace of Twilight: + Standard: + Text: Palace of Twilight + Pretty: + Text: the {Palace of Twilight} + Cryptic: + Text: "{another realm}" + +Hyrule Castle: + Standard: + Text: Hyrule Castle + Pretty: + Text: the {Hyrule Castle} + Cryptic: + Text: the {kingdom's castle} + +# NO REQUIRED DUNGEON TEXT +No Required Dungeons Text: + Standard: + Text: No Required Dungeons + +# ITEM GET TEXT +Foolish Get Item Text: + Standard: + Text: |- + a cold wind blows... + +Shadow Crystal Get Item Text: + Standard: + Text: |- + You got the Shadow Crystal! + This is a dark manifestation + of Zant's power that allows + you to transform at will! + +Restored Dominion Rod Text: + Standard: + Text: |- + Power has been restored to + the Dominion Rod! Now it can + be used to imbue statues + with life in the present! + +Forest Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Forest Temple! + +Goron Mines Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Goron Mines! + +Lakebed Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Lakebed Temple! + +Arbiters Grounds Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Arbiter's Grounds! + +Snowpeak Ruins Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Snowpeak Ruins! + +Temple of Time Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Temple of Time! + +City in the Sky Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + City in the Sky! + +Palace of Twilight Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Palace of Twilight! + +Hyrule Castle Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Hyrule Castle! + +Bulblin Camp Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Bulblin Camp! + +Forest Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Forest Temple! + +Lakebed Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Lakebed Temple! + +Arbiters Grounds Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Arbiter's Grounds! + +Temple of Time Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Temple of Time! + +City in the Sky Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + City in the Sky! + +Palace of Twilight Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Palace of Twilight! + +Hyrule Castle Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Hyrule Castle! + +Forest Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Forest Temple! + +Goron Mines Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Goron Mines! + +Lakebed Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Lakebed Temple! + +Mirror Shard 2 Get item Text: + Standard: + Text: |- + You got the second shard of + the Mirror of Twilight! It + has a beautiful shine to it + and feels slightly cold... + +Mirror Shard 3 Get item Text: + Standard: + Text: |- + You got the third shard of + the Mirror of Twilight! It + is covered in dirt and + webs... + +Mirror Shard 4 Get item Text: + Standard: + Text: |- + You got the final shard of + the Mirror of Twilight! It + feels lighter than air... + +Arbiters Grounds Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Arbiter's Grounds! + +Snowpeak Ruins Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Snowpeak Ruins! + +Temple of Time Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Temple of Time! + +City in the Sky Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + City in the Sky! + +Palace of Twilight Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Palace of Twilight! + +Hyrule Castle Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Hyrule Castle! + +# +Forest Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Forest Temple! + +Goron Mines Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Goron Mines! + +Lakebed Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Lakebed Temple! + +Snowpeak Ruins Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Snowpeak Ruins! + +Arbiters Grounds Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Arbiter's Grounds! + +Temple of Time Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Temple of Time! + +City in the Sky Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + City in the Sky! + +Palace of Twilight Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Palace of Twilight! + +Hyrule Castle Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Hyrule Castle! + + +Fused Shadow 1 Get Item Text: + Standard: + Text: |- + You got a Fused Shadow! + It seems to have some moss + growing on it... + +Fused Shadow 2 Get Item Text: + Standard: + Text: |- + You got the second Fused + Shadow! It feels warm to + the touch... + +Fused Shadow 3 Get Item Text: + Standard: + Text: |- + You got the final Fused + Shadow! It feels wet and + smells like fish... + +Mirror Shard 1 Get Item Text: + Standard: + Text: |- + You got the first shard of + the Mirror of Twilight! It + is covered in sand... + +Poe Soul Get Item Text: + Standard: + Text: |- + You got a Poe's Soul! + You've collected {} so far. + +Ending Blow Get Item Text: + Standard: + Text: |- + You learned the Ending Blow! + +Shield Attack Get Item Text: + Standard: + Text: |- + You learned the Shield Attack! + +Back Slice Get Item Text: + Standard: + Text: |- + You learned the Back Slice! + +Helm Splitter Get Item Text: + Standard: + Text: |- + You learned the Helm Splitter! + +Mortal Draw Get Item Text: + Standard: + Text: |- + You learned the Mortal Draw! + +Jump Strike Get Item Text: + Standard: + Text: |- + You learned the Jump Strike! + +Great Spin Get Item Text: + Standard: + Text: |- + You learned the Great Spin! + +Partially Filled Sky Book Get Item Text: + Standard: + Text: |- + You got a Sky Character! + You've collected {} so far. + +Midna Call As Human Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into wolf + <2 way choice 2>Something else + +Midna Call As Wolf Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into human + <2 way choice 2>Something else + +Midna Call As Wolf No Shadow Crystal Two Choice: + Standard: + Text: |- + <2 way choice 1>Warp + <2 way choice 2>Something else + +Midna Call As Human Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into wolf + <3 way choice 2>Warp + <3 way choice 3>Something else + +Midna Call As Wolf Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into human + <3 way choice 2>Warp + <3 way choice 3>Something else + +Slingshot Shop Text Template: + Standard: + Text: |- + : 30 Rupees +# I got this in for the kids. It's just a +# toy, but it stings something AWFUL +# when you get hit by it! + +Slingshot Shop Too Expensive Text Template: + Standard: + Text: |- + is 30 Rupees. If you want it, bring some money with you, all right, m'dear? + +Slingshot Shop Purchase Confirmation Text Template: + Standard: + Text: |- + is 30 Rupees. Do you want to buy it, m'dear? + +Slingshot Shop After Purchase Text Template: + Standard: + Text: |- + What are you doing buying , you naughty thing? You're too old for toys! Will you at least let the kids play with it? + +Barnes Special Offer Text Template: + Standard: + Text: |- + I've got a special offer goin' right now: , just 120 Rupees! How 'bout that? + +Kakariko Malo Mart Wooden Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 50 Rupees. Want one or not? + +Kakariko Malo Mart Wooden Shield Too Expensive Text Template: + Standard: + Text: |- + will cost you 50 Rupees, but you can't afford it. Don't expect a discount just because we're from the same town. + +Kakariko Malo Mart Hylian Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 200 Rupees. Want one or not? + +Kakariko Malo Mart Hylian Shield Too Expensive Text Template: + Standard: + Text: |- + will run you 200 Rupees...but if you have that much, I'll eat my hat. And I don't even HAVE a hat. + +Kakariko Malo Mart Hylian Shield After Purchase Text Template: + Standard: + Text: |- + Well, you bought my last ... so you'd better take good care of it. + +Kakariko Malo Mart Hawkeye Purchase Confirmation Text Template: + Standard: + Text: |- + is 100 Rupees. You want it or not? + +Kakariko Malo Mart Hawkeye Too Expensive Text Template: + Standard: + Text: |- + costs 100 Rupees... but there are people with enough Rupees, and then there's you. The guy with not enough. + +Kakariko Malo Mart Hawkeye After Purchase Text Template: + Standard: + Text: |- + You bought my last ... + +Kakariko Malo Mart Red Potion Too Expensive Text Template: + Standard: + Text: |- + will cost you 30 Rupees, but I won't be donating it to the poor, sorry. + +Kakariko Malo Mart Red Potion Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 30 Rupees. Want some or not? + +Kakariko Malo Mart Red Potion Text Template: + Standard: + Text: |- + : 30 Rupees +# This potion replenishes your +# life energy. Keep it in an empty +# bottle. + +Kakariko Malo Mart Hawkeye Coming Soon Text Template: + Standard: + Text: |- + : COMING SOON + +Kakariko Malo Mart Hawkeye Text Template: + Standard: + Text: |- + : 100 Rupees +# This eyewear allows you to see +# distant objects as if with the eyes +# of a hawk. + +Kakariko Malo Mart Sold Out Text: + Standard: + Text: SOLD OUT + +Kakariko Malo Mart Wooden Shield Text Template: + Standard: + Text: |- + : 50 Rupees +# This is a simple shield. It's made of +# wood, so it will burn away if +# touched by fire. + +Kakariko Malo Mart Hylian Shield Text Template: + Standard: + Text: |- + : 200 Rupees +# LIMITED SUPPLY! +# Don't let them sell out before you +# buy one! + +Chudleys Shop Magic Armor Text Template: + Standard: + Text: |- + + Only for the richest and most + precious customers who value their + lives over their Rupees. + +Castle Town Malo Mart Magic Armor After Purchase Text Template: + Standard: + Text: |- + We have sold out of ! + +Castle Town Malo Mart Magic Armor Text Template: + Standard: + Text: |- + !Special! 598 Rupees +# This is quite a bargain when you +# think of how valuable your life is. +# What's a few Rupees to stay alive? + +Castle Town Malo Mart Magic Armor Sold Out Text Template: + Standard: + Text: |- + + -SOLD OUT- + *This item has been discontinued. + +Charlo Donation Choice Text: + Standard: + Text: |- + <3 way choice 1>100 Rupees + <3 way choice 2>50 Rupees + <3 way choice 3>Sorry... + +Charlo Donation Ask Text Template: + Standard: + Text: |- + For ... + Would you please make a donation? + +Coro Bottle Offer 1 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Coro Bottle Offer 2 Text Template: + Standard: + Text: |- + I have a special, one-time offer of + for only 100 Rupees. How 'bout it, guy? + +Coro Bottle Offer 3 Text Template: + Standard: + Text: |- + Right now we have a 100-Rupee + and 20-Rupee refills to choose from! + +Coro Bottle Offer 4 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Fishing Hole Sign Text Template: + Standard: + Text: |- + DON'T LITTER! + Do NOT toss empty bottles or + here! The fish are CRYING! + + Keep the fishing hole clean! + +Custom Midna Call Need Something Text: + Standard: + Text: |- + Need Something? + +Custom Midna Call 3 Choice Text: + Standard: + Text: |- + <3 way choice 1>Hints + <3 way choice 2>Change time of day + <3 way choice 3>Return to spawn + +Custom Midna Call 2 Choice Text: + Standard: + Text: |- + <2 way choice 1>Hints + <2 way choice 2>Return to spawn + +Custom Midna Call Hints Text: + Standard: + Text: |- + I have no hints to give. + +Return to Spawn Dungeon Intro Text: + Standard: + Text: |- + I'll get you out of here. + Where do you want to go? + +Return to Spawn Dungeon Choice Text: + Standard: + Text: |- + <3 way choice 1>Dungeon entrance + <3 way choice 2>Nevermind + <3 way choice 3>Spawn + +Return to Spawn Dungeon No Choice Text: + Standard: + Text: |- + <2 way choice 1>Nevermind + <2 way choice 2>Spawn + +Midna Hints Required Dungeons Intro Zero Dungeons: + Standard: + Text: |- + There are 0 required dungeons. + +Midna Hints Required Dungeons Intro At Least One Dungeon: + Standard: + Text: |- + There are required dungeons: + +Ordon Hint Sign Text: + Standard: + Text: |- + Ordon Hint Sign. + There are no hints placed here. + +South Faron Woods Hint Sign Text: + Standard: + Text: |- + South Faron Woods Hint Sign. + There are no hints placed here. + +Sacred Grove Hint Sign Text: + Standard: + Text: |- + Sacred Grove Hint Sign. + There are no hints placed here. + +Faron Field Hint Sign Text: + Standard: + Text: |- + Faron Field Hint Sign. + There are no hints placed here. + +Kakariko Gorge Hint Sign Text: + Standard: + Text: |- + Kakariko Gorge Hint Sign. + There are no hints placed here. + +Kakariko Village Hint Sign Text: + Standard: + Text: |- + Kakariko Village Hint Sign. + There are no hints placed here. + +Kakariko Graveyard Hint Sign Text: + Standard: + Text: |- + Kakariko Graveyard Hint Sign. + There are no hints placed here. + +Eldin Field Hint Sign Text: + Standard: + Text: |- + Eldin Field Hint Sign. + There are no hints placed here. + +North Eldin Field Hint Sign Text: + Standard: + Text: |- + North Eldin Field Hint Sign. + There are no hints placed here. + +Hidden Village Hint Sign Text: + Standard: + Text: |- + Hidden Village Hint Sign. + There are no hints placed here. + +Lanayru Field Hint Sign Text: + Standard: + Text: |- + Lanayru Field Hint Sign. + There are no hints placed here. + +Beside Castle Town Hint Sign Text: + Standard: + Text: |- + Beside Castle Town Hint Sign. + There are no hints placed here. + +Castle Town Center Hint Sign Text: + Standard: + Text: |- + Castle Town Center Hint Sign. + There are no hints placed here. + +Outside South Castle Town Hint Sign Text: + Standard: + Text: |- + Outside South Castle Town Hint Sign. + There are no hints placed here. + +Lake Hylia Bridge Hint Sign Text: + Standard: + Text: |- + Lake Hylia Bridge Hint Sign. + There are no hints placed here. + +Lake Hylia Hint Sign Text: + Standard: + Text: |- + Lake Hylia Hint Sign. + There are no hints placed here. + +Lanayru Spring Hint Sign Text: + Standard: + Text: |- + Lanayru Spring Hint Sign. + There are no hints placed here. + +Lake Lantern Cave Hint Sign Text: + Standard: + Text: |- + Lake Lantern Cave Hint Sign. + There are no hints placed here. + +Fishing Hole Hint Sign Text: + Standard: + Text: |- + Fishing Hole Hint Sign. + There are no hints placed here. + +Zoras Domain Hint Sign Text: + Standard: + Text: |- + Zoras Domain Hint Sign. + There are no hints placed here. + +Snowpeak Hint Sign Text: + Standard: + Text: |- + Snowpeak Hint Sign. + There are no hints placed here. + +Gerudo Desert Hint Sign Text: + Standard: + Text: |- + Gerudo Desert Hint Sign. + There are no hints placed here. + +Bulblin Camp Hint Sign Text: + Standard: + Text: |- + Bulblin Camp Hint Sign. + There are no hints placed here. + +Forest Temple Hint Sign Text: + Standard: + Text: |- + Forest Temple Hint Sign. + There are no hints placed here. + +Goron Mines Hint Sign Text: + Standard: + Text: |- + Goron Mines Hint Sign. + There are no hints placed here. + +Lakebed Temple Hint Sign Text: + Standard: + Text: |- + Lakebed Temple Hint Sign. + There are no hints placed here. + +Arbiters Grounds Hint Sign Text: + Standard: + Text: |- + Arbiters Grounds Hint Sign. + There are no hints placed here. + +Snowpeak Ruins Hint Sign Text: + Standard: + Text: |- + Snowpeak Ruins Hint Sign. + There are no hints placed here. + +Temple of Time First Hint Sign Text: + Standard: + Text: |- + Temple of Time First Hint Sign. + There are no hints placed here. + +Temple of Time Second Hint Sign Text: + Standard: + Text: |- + Temple of Time Second Hint Sign. + There are no hints placed here. + +City in the Sky Hint Sign Text: + Standard: + Text: |- + City in the Sky Hint Sign. + There are no hints placed here. + +Palace of Twilight Hint Sign Text: + Standard: + Text: |- + Palace of Twilight Hint Sign. + There are no hints placed here. + +Hyrule Castle Hint Sign Text: + Standard: + Text: |- + Hyrule Castle Hint Sign. + There are no hints placed here. + +Cave of Ordeals Hint Sign Text: + Standard: + Text: |- + Cave of Ordeals Hint Sign. + There are no hints placed here. diff --git a/mods/randomizer/generator/data/text/languages/french.yaml b/mods/randomizer/generator/data/text/languages/french.yaml new file mode 100644 index 0000000000..fde6521baf --- /dev/null +++ b/mods/randomizer/generator/data/text/languages/french.yaml @@ -0,0 +1,2417 @@ +# This file contains all custom French text for the dusklight randomizer + +# NOTES FOR TRANSLATORS: +# - You should only be translating the "Text" fields for each element in this file. Do not translate the +# - Text being surrounded by braces '{}' means that the text will be colored. If a text field begins with a brace, +# the entire field must be surrounded with quotation marks. +# - Below each text element, you can specify a given text's gender and/or plurality. If you need additional +# specifiers for pieces of text, let us know. If no gender is provided, the assumption is no gender. If no +# plurality is provided, the assumed plurality is singular. + +# ITEM NAMES +Green Rupee: + Standard: + Text: Green Rupee + Pretty: + Text: a {Green Rupee} + Cryptic: + Text: a {penny} + +Blue Rupee: + Standard: + Text: Blue Rupee + Pretty: + Text: a {Blue Rupee} + Cryptic: + Text: a {fiver} + +Yellow Rupee: + Standard: + Text: Yellow Rupee + Pretty: + Text: a {Yellow Rupee} + Cryptic: + Text: some {change} + +Red Rupee: + Standard: + Text: Red Rupee + Pretty: + Text: a {Red Rupee} + Cryptic: + Text: "{couch cash}" + +Purple Rupee: + Standard: + Text: Purple Rupee + Pretty: + Text: a {Purple Rupee} + Cryptic: + Text: a {good sum} + +Orange Rupee: + Standard: + Text: Orange Rupee + Pretty: + Text: an {Orange Rupee} + Cryptic: + Text: a {payday} + +Silver Rupee: + Standard: + Text: Silver Rupee + Pretty: + Text: a {Silver Rupee} + Cryptic: + Text: "{many riches}" + +Bombs 5: + Standard: + Text: Bombs 5 + Pretty: + Text: "{Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 10: + Standard: + Text: Bombs 10 + Pretty: + Text: "{Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 20: + Standard: + Text: Bombs 20 + Pretty: + Text: "{Bombs (20)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 30: + Standard: + Text: Bombs 30 + Pretty: + Text: "{Bombs (30)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Arrows 10: + Standard: + Text: Arrows 10 + Pretty: + Text: "{Arrows (10)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 20: + Standard: + Text: Arrows 20 + Pretty: + Text: "{Arrows (20)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 30: + Standard: + Text: Arrows 30 + Pretty: + Text: "{Arrows (30)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Seeds 50: + Standard: + Text: Seeds 50 + Pretty: + Text: "{Seeds (50)}" + Plurality: Plural + Cryptic: + Text: some {pellets} + Plurality: Plural + +Foolish Item: + Standard: + Text: Foolish Item + Pretty: + Text: a {Foolish Item} + Cryptic: + Text: a {chilly surprise} + +Ordon Spring Portal: + Standard: + Text: Ordon Spring Portal + Pretty: + Text: the {Ordon Spring Portal} + Cryptic: + Text: a {portal to home} + +South Faron Portal: + Standard: + Text: South Faron Portal + Pretty: + Text: the {South Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Water Bombs 5: + Standard: + Text: Water Bombs 5 + Pretty: + Text: "{Water Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 10: + Standard: + Text: Water Bombs 10 + Pretty: + Text: "{Water Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 15: + Standard: + Text: Water Bombs 15 + Pretty: + Text: "{Water Bombs (15)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Bomblings 5: + Standard: + Text: Bomblings 5 + Pretty: + Text: "{Bomblings (5)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Bomblings 10: + Standard: + Text: Bomblings 10 + Pretty: + Text: "{Bomblings (10)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Piece of Heart: + Standard: + Text: Piece of Heart + Pretty: + Text: a {Piece of Heart} + Cryptic: + Text: some {love} + +Heart Container: + Standard: + Text: Heart Container + Pretty: + Text: a {Heart Container} + Cryptic: + Text: a {lot of love} + +Ordon Shield: + Standard: + Text: Ordon Shield + Pretty: + Text: the {Ordon Shield} + Cryptic: + Text: a {sturdy reminder of home} + +Wooden Shield: + Standard: + Text: Wooden Shield + Pretty: + Text: a {Wooden Shield} + Cryptic: + Text: a {wood protector} + +Hylian Shield: + Standard: + Text: Hylian Shield + Pretty: + Text: the {Hylian Shield} + Cryptic: + Text: an {unbreakable shield} + +Magic Armor: + Standard: + Text: Magic Armor + Pretty: + Text: the {Magic Armor} + Cryptic: + Text: "{magical clothing}" + +Zora Armor: + Standard: + Text: Zora Armor + Pretty: + Text: the {Zora Armor} + Cryptic: + Text: the {fish suit} + +Shadow Crystal: + Standard: + Text: Shadow Crystal + Pretty: + Text: the {Shadow Crystal} + Cryptic: + Text: a {crystal of dark power} + +Progressive Wallet: + Standard: + Text: Progressive Wallet + Pretty: + Text: a {Wallet} + Cryptic: + Text: a {money bag} + +Upper Zoras River Portal: + Standard: + Text: Upper Zoras River Portal + Pretty: + Text: the {Upper Zoras River Portal} + Cryptic: + Text: a {portal to some raging rapids} + +Castle Town Portal: + Standard: + Text: Castle Town Portal + Pretty: + Text: the {Castle Town Portal} + Cryptic: + Text: a {portal to the city} + +Gerudo Desert Portal: + Standard: + Text: Gerudo Desert Portal + Pretty: + Text: the {Gerudo Desert Portal} + Cryptic: + Text: a {portal to a challenging cave} + +North Faron Portal: + Standard: + Text: North Faron Portal + Pretty: + Text: the {North Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Hawkeye: + Standard: + Text: Hawkeye + Pretty: + Text: the {Hawkeye} + Cryptic: + Text: the {zoom-and-enhance} + +Progressive Sword: + Standard: + Text: Progressive Sword + Pretty: + Text: a {Sword} + Cryptic: + Text: a {sharp weapon} + +Gale Boomerang: + Standard: + Text: Gale Boomerang + Pretty: + Text: the {Gale Boomerang} + Cryptic: + Text: the {fairy of winds} + +Spinner: + Standard: + Text: Spinner + Pretty: + Text: the {Spinner} + Cryptic: + Text: the {gear rotator} + +Ball and Chain: + Standard: + Text: Ball and Chain + Pretty: + Text: the {Ball and Chain} + Cryptic: + Text: the {iron weight} + +Progressive Bow: + Standard: + Text: Progressive Bow + Pretty: + Text: a {Bow} + Cryptic: + Text: an {arrow launcher} + +Progressive Clawshot: + Standard: + Text: Progressive Clawshot + Pretty: + Text: a {Clawshot} + Cryptic: + Text: a {chain launcher} + +Iron Boots: + Standard: + Text: Iron Boots + Pretty: + Text: the {Iron Boots} + Plurality: Plural + Cryptic: + Text: the {heavy shoes} + Plurality: Plural + +Progressive Dominion Rod: + Standard: + Text: Dominion Rod + Pretty: + Text: a {Dominion Rod} + Cryptic: + Text: a {rod of control} + +Lantern: + Standard: + Text: Lantern + Pretty: + Text: the {Lantern} + Cryptic: + Text: the {small light} + +Progressive Fishing Rod: + Standard: + Text: Progressive Fishing Rod + Pretty: + Text: a {Fishing Rod} + Cryptic: + Text: a {rod of patience} + +Slingshot: + Standard: + Text: Slingshot + Pretty: + Text: the {Slingshot} + Cryptic: + Text: the {child's toy} + +Kakariko Gorge Portal: + Standard: + Text: Kakariko Gorge Portal + Pretty: + Text: the {Kakariko Gorge Portal} + Cryptic: + Text: a {portal to a big gap} + +Kakariko Village Portal: + Standard: + Text: Kakariko Village Portal + Pretty: + Text: the {Kakariko Village Portal} + Cryptic: + Text: a {portal to a village} + +Giant Bomb Bag: + Standard: + Text: Giant Bomb Bag + Pretty: + Text: a {Giant Bomb Bag} + Cryptic: + Text: an {explosive capacity upgrade} + +Bomb Bag: + Standard: + Text: Bomb Bag + Pretty: + Text: a {Bomb Bag} + Cryptic: + Text: a {bag for explosions} + +Death Mountain Portal: + Standard: + Text: Death Mountain Portal + Pretty: + Text: the {Death Mountain Portal} + Cryptic: + Text: a {portal to a volcano} + +Zoras Domain Portal: + Standard: + Text: Zoras Domain Portal + Pretty: + Text: the {Zora's Domain Portal} + Cryptic: + Text: a {portal to water} + +Empty Bottle: + Standard: + Text: Empty Bottle + Pretty: + Text: an {Empty Bottle} + Cryptic: + Text: + +Red Potion Shop: + Standard: + Text: Red Potion Shop + Pretty: + Text: a {Red Potion} + Cryptic: + Text: a {health refill} + +Blue Potion Shop: + Standard: + Text: Blue Potion Shop + Pretty: + Text: a {Blue Potion} + Cryptic: + Text: a {blue health refill} + +Bottle with Half Milk: + Standard: + Text: Bottle with Half Milk + Pretty: + Text: a {Bottle with Half Milk} + Cryptic: + Text: a {baby bottle} + +Fairy Tears: + Standard: + Text: Fairy Tears + Pretty: + Text: some {Fairy Tears} + Plurality: Plural + Cryptic: + Text: a {refill of great power} + +Bottle with Great Fairies Tears: + Standard: + Text: Bottle with Great Fairies Tears + Pretty: + Text: a {Bottle with Great Fairies Tears} + Cryptic: + Text: a {bottle of great power} + +Renados Letter: + Standard: + Text: Renados Letter + Pretty: + Text: "{Renado's Letter}" + Cryptic: + Text: a {letter from a concerned shaman} + +Invoice: + Standard: + Text: Invoice + Pretty: + Text: the {Invoice} + Cryptic: + Text: the {bill for the doctor} + +Wooden Statue: + Standard: + Text: Wooden Statue + Pretty: + Text: the {Wooden Statue} + Cryptic: + Text: "{memories of home}" + Plurality: Plural + +Ilias Charm: + Standard: + Text: Ilias Charm + Pretty: + Text: "{Ilias Charm}" + Cryptic: + Text: a {friend's item} + +Horse Call: + Standard: + Text: Horse Call + Pretty: + Text: the {Horse Call} + Cryptic: + Text: the {horse beckoner} + +Forest Temple Small Key: + Standard: + Text: Forest Temple Small Key + Pretty: + Text: a {Forest Temple Small Key} + Cryptic: + Text: a {key for a deep forest} + +Goron Mines Small Key: + Standard: + Text: Goron Mines Small Key + Pretty: + Text: a {Goron Mines Small Key} + Cryptic: + Text: a {key for a volcanic mine} + +Lakebed Temple Small Key: + Standard: + Text: Lakebed Temple Small Key + Pretty: + Text: a {Lakebed Temple Small Key} + Cryptic: + Text: a {key for an underground lake} + +Arbiters Grounds Small Key: + Standard: + Text: Arbiters Grounds Small Key + Pretty: + Text: an {Arbiters Grounds Small Key} + Cryptic: + Text: a {key for an ancient prison} + +Snowpeak Ruins Small Key: + Standard: + Text: Snowpeak Ruins Small Key + Pretty: + Text: a {Snowpeak Ruins Small Key} + Cryptic: + Text: a {key for a snowy mansion} + +Temple of Time Small Key: + Standard: + Text: Temple of Time Small Key + Pretty: + Text: a {Temple of Time Small Key} + Cryptic: + Text: a {key for the past} + +City in the Sky Small Key: + Standard: + Text: City in the Sky Small Key + Pretty: + Text: the {City in the Sky Small Key} + Cryptic: + Text: a {key for the skies above} + +Palace of Twilight Small Key: + Standard: + Text: Palace of Twilight Small Key + Pretty: + Text: a {Palace of Twilight Small Key} + Cryptic: + Text: a {key for a another realm} + +Hyrule Castle Small Key: + Standard: + Text: Hyrule Castle Small Key + Pretty: + Text: a {Hyrule Castle Small Key} + Cryptic: + Text: a {key for a kingdom's castle} + +Gerudo Desert Bulblin Camp Key: + Standard: + Text: Gerudo Bulblin Camp Small Key + Pretty: + Text: the {Gerudo Desert Bulblin Camp Key} + Cryptic: + Text: the {key for a desert tent} + +Lake Hylia Portal: + Standard: + Text: Lake Hylia Portal + Pretty: + Text: the {Lake Hylia Portal} + Cryptic: + Text: a {portal to a vast lake} + +Aurus Memo: + Standard: + Text: Aurus Memo + Pretty: + Text: "{Auru's Memo}" + Cryptic: + Text: a {friend's favor} + +Asheis Sketch: + Standard: + Text: Asheis Sketch + Pretty: + Text: "{Ashei's Sketch}" + Cryptic: + Text: a {sketch of a horrific beast} + +Forest Temple Big Key: + Standard: + Text: Forest Temple Big Key + Pretty: + Text: the {Forest Temple Big Key} + Cryptic: + Text: the {key to the twilit parasite} + +Lakebed Temple Big Key: + Standard: + Text: Lakebed Temple Big Key + Pretty: + Text: the {Lakebed Temple Big Key} + Cryptic: + Text: the {key to the twilit aquatic} + +Arbiters Grounds Big Key: + Standard: + Text: Arbiters Grounds Big Key + Pretty: + Text: the {Arbiters Grounds Big Key} + Cryptic: + Text: the {key to the twilit fossil} + +Temple of Time Big Key: + Standard: + Text: Temple of Time Big Key + Pretty: + Text: the {Temple of Time Big Key} + Cryptic: + Text: the {key to the twilit arachnid} + +City in the Sky Big Key: + Standard: + Text: City in the Sky Big Key + Pretty: + Text: the {City in the Sky Big Key} + Cryptic: + Text: the {key to the twilit dragon} + +Palace of Twilight Big Key: + Standard: + Text: Palace of Twilight Big Key + Pretty: + Text: the {Palace of Twilight Big Key} + Cryptic: + Text: the {key to the usurper king} + +Hyrule Castle Big Key: + Standard: + Text: Hyrule Castle Big Key + Pretty: + Text: a {Hyrule Castle Big Key} + Cryptic: + Text: the {key to the castle throne room} + +Forest Temple Compass: + Standard: + Text: Forest Temple Compass + Pretty: + Text: the {Forest Temple Compass} + Cryptic: + Text: the {pointer for a deep forest} + +Goron Mines Compass: + Standard: + Text: Goron Mines Compass + Pretty: + Text: the {Goron Mines Compass} + Cryptic: + Text: the {pointer for a volcano} + +Lakebed Temple Compass: + Standard: + Text: Lakebed Temple Compass + Pretty: + Text: the {Lakebed Temple Compass} + Cryptic: + Text: the {pointer for an underground lake} + +Bottle with Lantern Oil: + Standard: + Text: Bottle with Lantern Oil + Pretty: + Text: a {Bottle with Lantern Oil} + Cryptic: + Text: a {bottle with lighter fluid} + +Progressive Mirror Shard: + Standard: + Text: Progressive Mirror Shard + Pretty: + Text: a {Mirror Shard} + Cryptic: + Text: a {reflective shard of power} + +Arbiters Grounds Compass: + Standard: + Text: Arbiters Grounds Compass + Pretty: + Text: the {Arbiters Grounds Compass} + Cryptic: + Text: the {pointer for an ancient prison} + +Snowpeak Ruins Compass: + Standard: + Text: Snowpeak Ruins Compass + Pretty: + Text: the {Snowpeak Ruins Compass} + Cryptic: + Text: the {pointer for a snowy mansion} + +Temple of Time Compass: + Standard: + Text: Temple of Time Compass + Pretty: + Text: the {Temple of Time Compass} + Cryptic: + Text: the {pointer for the past} + +City in the Sky Compass: + Standard: + Text: City in the Sky Compass + Pretty: + Text: the {City in the Sky Compass} + Cryptic: + Text: the {pointer for the skies above} + +Palace of Twilight Compass: + Standard: + Text: Palace of Twilight Compass + Pretty: + Text: the {Palace of Twilight Compass} + Cryptic: + Text: the {pointer for another realm} + +Hyrule Castle Compass: + Standard: + Text: Hyrule Castle Compass + Pretty: + Text: a {Hyrule Castle Compass} + Cryptic: + Text: the {pointer for the kingdom's castle} + +Mirror Chamber Portal: + Standard: + Text: Mirror Chamber Portal + Pretty: + Text: the {Mirror Chamber Portal} + Cryptic: + Text: a {portal to a coliseum} + +Snowpeak Portal: + Standard: + Text: Snowpeak Portal + Pretty: + Text: the {Snowpeak Portal} + Cryptic: + Text: a {portal to a snowy mountain} + +Forest Temple Dungeon Map: + Standard: + Text: Forest Temple Dungeon Map + Pretty: + Text: the {Forest Temple Dungeon Map} + Cryptic: + Text: the {map for a deep forest} + +Goron Mines Dungeon Map: + Standard: + Text: Goron Mines Dungeon Map + Pretty: + Text: the {Goron Mines Dungeon Map} + Cryptic: + Text: the {map for a volcano} + +Lakebed Temple Dungeon Map: + Standard: + Text: Lakebed Temple Dungeon Map + Pretty: + Text: the {Lakebed Temple Dungeon Map} + Cryptic: + Text: the {map for an underground lake} + +Arbiters Grounds Dungeon Map: + Standard: + Text: Arbiters Grounds Dungeon Map + Pretty: + Text: the {Arbiters Grounds Dungeon Map} + Cryptic: + Text: the {map for an ancient prison} + +Snowpeak Ruins Dungeon Map: + Standard: + Text: Snowpeak Ruins Dungeon Map + Pretty: + Text: the {Snowpeak Ruins Dungeon Map} + Cryptic: + Text: the {map for a snowy mansion} + +Temple of Time Dungeon Map: + Standard: + Text: Temple of Time Dungeon Map + Pretty: + Text: the {Temple of Time Dungeon Map} + Cryptic: + Text: the {map for the past} + +City in the Sky Dungeon Map: + Standard: + Text: City in the Sky Dungeon Map + Pretty: + Text: the {City in the Sky Dungeon Map} + Cryptic: + Text: the {map for the skies above} + +Palace of Twilight Dungeon Map: + Standard: + Text: Palace of Twilight Dungeon Map + Pretty: + Text: the {Palace of Twilight Dungeon Map} + Cryptic: + Text: the {map for another realm} + +Hyrule Castle Dungeon Map: + Standard: + Text: Hyrule Castle Dungeon Map + Pretty: + Text: a {Hyrule Castle Dungeon Map} + Cryptic: + Text: the {map for the kingdom's castle} + +Sacred Grove Portal: + Standard: + Text: Sacred Grove Portal + Pretty: + Text: the {Sacred Grove Portal} + Cryptic: + Text: a {portal to an ancient forest} + +Male Beetle: + Standard: + Text: Male Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Female Beetle: + Standard: + Text: Female Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Male Butterfly: + Standard: + Text: Male Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Female Butterfly: + Standard: + Text: Female Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Male Stag Beetle: + Standard: + Text: Male Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Female Stag Beetle: + Standard: + Text: Female Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Male Grasshopper: + Standard: + Text: Male Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Female Grasshopper: + Standard: + Text: Female Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Male Phasmid: + Standard: + Text: Male Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Female Phasmid: + Standard: + Text: Female Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Male Pill Bug: + Standard: + Text: Male Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Female Pill Bug: + Standard: + Text: Female Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Male Mantis: + Standard: + Text: Male Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Female Mantis: + Standard: + Text: Female Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Male Ladybug: + Standard: + Text: Male Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Female Ladybug: + Standard: + Text: Female Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Male Snail: + Standard: + Text: Male Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Female Snail: + Standard: + Text: Female Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Male Dragonfly: + Standard: + Text: Male Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Female Dragonfly: + Standard: + Text: Female Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Male Ant: + Standard: + Text: Male Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Female Ant: + Standard: + Text: Female Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Male Dayfly: + Standard: + Text: Male Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Female Dayfly: + Standard: + Text: Female Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Progressive Fused Shadow: + Standard: + Text: Progressive Fused Shadow + Pretty: + Text: a {Fused Shadow} + Cryptic: + Text: a {shadow of ultimate power} + +Poe Soul: + Standard: + Text: Poe Soul + Pretty: + Text: a {Poe Soul} + Cryptic: + Text: a {soul of the dead} + +Progressive Hidden Skill: + Standard: + Text: Progressive Hidden Skill + Pretty: + Text: a {Hidden Skill} + Cryptic: + Text: a {forgotten technique} + + +Bridge of Eldin Portal: + Standard: + Text: Bridge of Eldin Portal + Pretty: + Text: the {Bridge of Eldin Portal} + Cryptic: + Text: a {portal to a long bridge} + +Progressive Sky Book: + Standard: + Text: Progressive Sky Book + Pretty: + Text: a {Sky Character} + Cryptic: + Text: a {glyph of the heavens} + +Purple Rupee Links House: + Standard: + Text: Purple Rupee Links House + Pretty: + Text: the {Purple Rupee from your basement} + Cryptic: + Text: "{your savings}" + Plurality: Plural + +North Faron Woods Gate Key: + Standard: + Text: North Faron Woods Gate Key + Pretty: + Text: the {North Faron Woods Gate Key} + Cryptic: + Text: a {key to a northern forest} + +Gate Keys: + Standard: + Text: Gate Keys + Pretty: + Text: the {Gate Keys} + Plurality: Plural + Cryptic: + Text: "{King Bulblin's keys}" + Plurality: Plural + +Ordon Pumpkin: + Standard: + Text: Ordon Pumpkin + Pretty: + Text: the {Ordon Pumpkin} + Cryptic: + Text: a {soup ingredient} + +Ordon Cheese: + Standard: + Text: Ordon Cheese + Pretty: + Text: some {Ordon Cheese} + Cryptic: + Text: a {soup ingredient} + +Snowpeak Ruins Bedroom Key: + Standard: + Text: Snowpeak Ruins Bedroom Key + Pretty: + Text: the {Snowpeak Ruins Bedroom Key} + Cryptic: + Text: the {key to a snowy bedroom} + +Goron Mines Key Shard: + Standard: + Text: Goron Mines Key Shard + Pretty: + Text: a {Goron Mines Key Shard} + Cryptic: + Text: "{one third of a key}" + +Coro Key: + Standard: + Text: Coro Key + Pretty: + Text: "{Coro's Key}" + Cryptic: + Text: a {key to a forest cave} + +Game Beatable: + Standard: + Text: Game Beatable + Pretty: + Text: "{Game Beatable}" + Cryptic: + Text: the {game-winning item} + +Hint: + Standard: + Text: Hint + Pretty: + Text: a {Hint} + Cryptic: + Text: a {piece of knowledge} + +Faron Twilight Tear: + Standard: + Text: Faron Twilight Tear + Pretty: + Text: a {Faron Twilight Tear} + Cryptic: + Text: a {tear of a forest spirit} + +Eldin Twilight Tear: + Standard: + Text: Eldin Twilight Tear + Pretty: + Text: an {Eldin Twilight Tear} + Cryptic: + Text: a {tear of a volcano spirit} + +Lanayru Twilight Tear: + Standard: + Text: Lanayru Twilight Tear + Pretty: + Text: a {Lanayru Twilight Tear} + Cryptic: + Text: a {tear of a lake spirit} + +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) + +# HINT REGION NAMES +Ordon: + Standard: + Text: Ordon + Pretty: + Text: "{Ordon}" + Cryptic: + Text: a {quaint village} + +Faron Woods: + Standard: + Text: Faron Woods + Pretty: + Text: "{Faron Woods}" + Cryptic: + Text: a {forest} + +Sacred Grove: + Standard: + Text: Sacred Grove + Pretty: + Text: the {Sacred Grove} + Cryptic: + Text: a {hidden grove} + +Faron Field: + Standard: + Text: Faron Field + Pretty: + Text: "{Faron Field}" + Cryptic: + Text: a {field near the forest} + +Kakariko Gorge: + Standard: + Text: Kakariko Gorge + Pretty: + Text: "{Kakariko Gorge}" + Cryptic: + Text: a {field with a large chasm} + +Kakariko Village: + Standard: + Text: Kakariko Village + Pretty: + Text: "{Kakariko Village}" + Cryptic: + Text: a {charming village} + +Kakariko Graveyard: + Standard: + Text: Kakariko Graveyard + Pretty: + Text: the {Kakariko Graveyard} + Cryptic: + Text: a {yard for the dead} + +Death Mountain: + Standard: + Text: Death Mountain + Pretty: + Text: "{Death Mountain}" + Cryptic: + Text: a {volcano path} + +Eldin Field: + Standard: + Text: Eldin Field + Pretty: + Text: "{Eldin Field}" + Cryptic: + Text: a {field near a volcano} + +North Eldin: + Standard: + Text: North Eldin + Pretty: + Text: "{North Eldin}" + Cryptic: + Text: a {narrow gray field} + +Hidden Village: + Standard: + Text: Hidden Village + Pretty: + Text: the {Hidden Village} + Cryptic: + Text: a {secluded settlement} + +Lanayru Field: + Standard: + Text: Lanayru Field + Pretty: + Text: "{Lanayru Field}" + Cryptic: + Text: a {field with a river} + +Beside Castle Town: + Standard: + Text: Beside Castle Town + Pretty: + Text: "{Beside Castle Town}" + Cryptic: + Text: a {field beside a city} + +Castle Town: + Standard: + Text: Castle Town + Pretty: + Text: "{Castle Town}" + Cryptic: + Text: a {city} + +South of Castle Town: + Standard: + Text: South of Castle Town + Pretty: + Text: "{South of Castle Town}" + Cryptic: + Text: a {field south of a city} + +Great Bridge of Hylia: + Standard: + Text: Great Bridge of Hylia + Pretty: + Text: the {Great Bridge of Hylia} + Cryptic: + Text: a {path along a great bridge} + +Lake Hylia: + Standard: + Text: Lake Hylia + Pretty: + Text: "{Lake Hylia}" + Cryptic: + Text: a {vast lake} + +Lanayru Spring: + Standard: + Text: Lanayru Spring + Pretty: + Text: the {Lanayru Spring} + Cryptic: + Text: a {cavernous spring} + +Upper Zoras River: + Standard: + Text: Upper Zoras River + Pretty: + Text: "{Upper Zoras River}" + Cryptic: + Text: a {fork in the river} + +Zoras Domain: + Standard: + Text: Zoras Domain + Pretty: + Text: "{Zoras Domain}" + Cryptic: + Text: the {home of a grand waterfall} + +South Gerudo Desert: + Standard: + Text: South Gerudo Desert + Pretty: + Text: "{South Gerudo Desert}" + Cryptic: + Text: the {southern desert} + +North Gerudo Desert: + Standard: + Text: North Gerudo Desert + Pretty: + Text: "{North Gerudo Desert}" + Cryptic: + Text: the {northern desert} + +Bublin Camp: + Standard: + Text: Bublin Camp + Pretty: + Text: "{Bublin Camp}" + Cryptic: + Text: a {camp of enemies} + +Mirror Chamber: + Standard: + Text: Mirror Chamber + Pretty: + Text: the {Mirror Chamber} + Cryptic: + Text: a {chamber of chains} + +Forest Temple: + Standard: + Text: Forest Temple + Pretty: + Text: the {Forest Temple} + Cryptic: + Text: a {deep forest} + +Goron Mines: + Standard: + Text: Goron Mines + Pretty: + Text: the {Goron Mines} + Cryptic: + Text: a {volcanic mine} + +Lakebed Temple: + Standard: + Text: Lakebed Temple + Pretty: + Text: the {Lakebed Temple} + Cryptic: + Text: an {underground lake} + +Arbiters Grounds: + Standard: + Text: Arbiters Grounds + Pretty: + Text: the {Arbiters Grounds} + Cryptic: + Text: an {ancient prison} + +Snowpeak Ruins: + Standard: + Text: Snowpeak Ruins + Pretty: + Text: the {Snowpeak Ruins} + Cryptic: + Text: a {snowy mansion} + +Temple of Time: + Standard: + Text: Temple of Time + Pretty: + Text: the {Temple of Time} + Cryptic: + Text: the {past} + +City in the Sky: + Standard: + Text: City in the Sky + Pretty: + Text: the {City in the Sky} + Cryptic: + Text: the {skies above} + +Palace of Twilight: + Standard: + Text: Palace of Twilight + Pretty: + Text: the {Palace of Twilight} + Cryptic: + Text: "{another realm}" + +Hyrule Castle: + Standard: + Text: Hyrule Castle + Pretty: + Text: the {Hyrule Castle} + Cryptic: + Text: the {kingdom's castle} + +# NO REQUIRED DUNGEON TEXT +No Required Dungeons Text: + Standard: + Text: No Required Dungeons + +# ITEM GET TEXT +Foolish Get Item Text: + Standard: + Text: |- + a cold wind blows... + +Shadow Crystal Get Item Text: + Standard: + Text: |- + Vous obtenez le Cristal Maudit! + !La sombre manifestation des + pouvoirs de Xanto qui permet + de se transformer à volonté! + +Restored Dominion Rod Text: + Standard: + Text: |- + Power has been restored to + the Dominion Rod! Now it can + be used to imbue statues + with life in the present! + +Forest Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Forest Temple! + +Goron Mines Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Goron Mines! + +Lakebed Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Lakebed Temple! + +Arbiters Grounds Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Arbiter's Grounds! + +Snowpeak Ruins Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Snowpeak Ruins! + +Temple of Time Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Temple of Time! + +City in the Sky Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + City in the Sky! + +Palace of Twilight Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Palace of Twilight! + +Hyrule Castle Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Hyrule Castle! + +Bulblin Camp Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Bulblin Camp! + +Forest Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Forest Temple! + +Lakebed Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Lakebed Temple! + +Arbiters Grounds Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Arbiter's Grounds! + +Temple of Time Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Temple of Time! + +City in the Sky Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + City in the Sky! + +Palace of Twilight Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Palace of Twilight! + +Hyrule Castle Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Hyrule Castle! + +Forest Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Forest Temple! + +Goron Mines Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Goron Mines! + +Lakebed Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Lakebed Temple! + +Mirror Shard 2 Get item Text: + Standard: + Text: |- + You got the second shard of + the Mirror of Twilight! It + has a beautiful shine to it + and feels slightly cold... + +Mirror Shard 3 Get item Text: + Standard: + Text: |- + You got the third shard of + the Mirror of Twilight! It + is covered in dirt and + webs... + +Mirror Shard 4 Get item Text: + Standard: + Text: |- + You got the final shard of + the Mirror of Twilight! It + feels lighter than air... + +Arbiters Grounds Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Arbiter's Grounds! + +Snowpeak Ruins Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Snowpeak Ruins! + +Temple of Time Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Temple of Time! + +City in the Sky Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + City in the Sky! + +Palace of Twilight Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Palace of Twilight! + +Hyrule Castle Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Hyrule Castle! + +# +Forest Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Forest Temple! + +Goron Mines Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Goron Mines! + +Lakebed Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Lakebed Temple! + +Snowpeak Ruins Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Snowpeak Ruins! + +Arbiters Grounds Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Arbiter's Grounds! + +Temple of Time Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Temple of Time! + +City in the Sky Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + City in the Sky! + +Palace of Twilight Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Palace of Twilight! + +Hyrule Castle Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Hyrule Castle! + + +Fused Shadow 1 Get Item Text: + Standard: + Text: |- + You got a Fused Shadow! + It seems to have some moss + growing on it... + +Fused Shadow 2 Get Item Text: + Standard: + Text: |- + You got the second Fused + Shadow! It feels warm to + the touch... + +Fused Shadow 3 Get Item Text: + Standard: + Text: |- + You got the final Fused + Shadow! It feels wet and + smells like fish... + +Mirror Shard 1 Get Item Text: + Standard: + Text: |- + You got the first shard of + the Mirror of Twilight! It + is covered in sand... + +Poe Soul Get Item Text: + Standard: + Text: |- + You got a Poe's Soul! + You've collected {} so far. + +Ending Blow Get Item Text: + Standard: + Text: |- + You learned the Ending Blow! + +Shield Attack Get Item Text: + Standard: + Text: |- + You learned the Shield Attack! + +Back Slice Get Item Text: + Standard: + Text: |- + You learned the Back Slice! + +Helm Splitter Get Item Text: + Standard: + Text: |- + You learned the Helm Splitter! + +Mortal Draw Get Item Text: + Standard: + Text: |- + You learned the Mortal Draw! + +Jump Strike Get Item Text: + Standard: + Text: |- + You learned the Jump Strike! + +Great Spin Get Item Text: + Standard: + Text: |- + You learned the Great Spin! + +Partially Filled Sky Book Get Item Text: + Standard: + Text: |- + You got a Sky Character! + You've collected {} so far. + +Midna Call As Human Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into wolf + <2 way choice 2>Something else + +Midna Call As Wolf Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into human + <2 way choice 2>Something else + +Midna Call As Wolf No Shadow Crystal Two Choice: + Standard: + Text: |- + <2 way choice 1>Warp + <2 way choice 2>Something else + +Midna Call As Human Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into wolf + <3 way choice 2>Warp + <3 way choice 3>Something else + +Midna Call As Wolf Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into human + <3 way choice 2>Warp + <3 way choice 3>Something else + +Slingshot Shop Text Template: + Standard: + Text: |- + : 30 Rupees +# I got this in for the kids. It's just a +# toy, but it stings something AWFUL +# when you get hit by it! + +Slingshot Shop Too Expensive Text Template: + Standard: + Text: |- + is 30 Rupees. If you want it, bring some money with you, all right, m'dear? + +Slingshot Shop Purchase Confirmation Text Template: + Standard: + Text: |- + is 30 Rupees. Do you want to buy it, m'dear? + +Slingshot Shop After Purchase Text Template: + Standard: + Text: |- + What are you doing buying , you naughty thing? You're too old for toys! Will you at least let the kids play with it? + +Barnes Special Offer Text Template: + Standard: + Text: |- + I've got a special offer goin' right now: , just 120 Rupees! How 'bout that? + +Kakariko Malo Mart Wooden Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 50 Rupees. Want one or not? + +Kakariko Malo Mart Wooden Shield Too Expensive Text Template: + Standard: + Text: |- + will cost you 50 Rupees, but you can't afford it. Don't expect a discount just because we're from the same town. + +Kakariko Malo Mart Hylian Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 200 Rupees. Want one or not? + +Kakariko Malo Mart Hylian Shield Too Expensive Text Template: + Standard: + Text: |- + will run you 200 Rupees...but if you have that much, I'll eat my hat. And I don't even HAVE a hat. + +Kakariko Malo Mart Hylian Shield After Purchase Text Template: + Standard: + Text: |- + Well, you bought my last ... so you'd better take good care of it. + +Kakariko Malo Mart Hawkeye Purchase Confirmation Text Template: + Standard: + Text: |- + is 100 Rupees. You want it or not? + +Kakariko Malo Mart Hawkeye Too Expensive Text Template: + Standard: + Text: |- + costs 100 Rupees... but there are people with enough Rupees, and then there's you. The guy with not enough. + +Kakariko Malo Mart Hawkeye After Purchase Text Template: + Standard: + Text: |- + You bought my last ... + +Kakariko Malo Mart Red Potion Too Expensive Text Template: + Standard: + Text: |- + will cost you 30 Rupees, but I won't be donating it to the poor, sorry. + +Kakariko Malo Mart Red Potion Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 30 Rupees. Want some or not? + +Kakariko Malo Mart Red Potion Text Template: + Standard: + Text: |- + : 30 Rupees +# This potion replenishes your +# life energy. Keep it in an empty +# bottle. + +Kakariko Malo Mart Hawkeye Coming Soon Text Template: + Standard: + Text: |- + : COMING SOON + +Kakariko Malo Mart Hawkeye Text Template: + Standard: + Text: |- + : 100 Rupees +# This eyewear allows you to see +# distant objects as if with the eyes +# of a hawk. + +Kakariko Malo Mart Sold Out Text: + Standard: + Text: SOLD OUT + +Kakariko Malo Mart Wooden Shield Text Template: + Standard: + Text: |- + : 50 Rupees +# This is a simple shield. It's made of +# wood, so it will burn away if +# touched by fire. + +Kakariko Malo Mart Hylian Shield Text Template: + Standard: + Text: |- + : 200 Rupees +# LIMITED SUPPLY! +# Don't let them sell out before you +# buy one! + +Chudleys Shop Magic Armor Text Template: + Standard: + Text: |- + + Only for the richest and most + precious customers who value their + lives over their Rupees. + +Castle Town Malo Mart Magic Armor After Purchase Text Template: + Standard: + Text: |- + We have sold out of ! + +Castle Town Malo Mart Magic Armor Text Template: + Standard: + Text: |- + !Special! 598 Rupees +# This is quite a bargain when you +# think of how valuable your life is. +# What's a few Rupees to stay alive? + +Castle Town Malo Mart Magic Armor Sold Out Text Template: + Standard: + Text: |- + + -SOLD OUT- + *This item has been discontinued. + +Charlo Donation Choice Text: + Standard: + Text: |- + <3 way choice 1>100 Rupees + <3 way choice 2>50 Rupees + <3 way choice 3>Sorry... + +Charlo Donation Ask Text Template: + Standard: + Text: |- + For ... + Would you please make a donation? + +Coro Bottle Offer 1 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Coro Bottle Offer 2 Text Template: + Standard: + Text: |- + I have a special, one-time offer of + for only 100 Rupees. How 'bout it, guy? + +Coro Bottle Offer 3 Text Template: + Standard: + Text: |- + Right now we have a 100-Rupee + and 20-Rupee refills to choose from! + +Coro Bottle Offer 4 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Fishing Hole Sign Text Template: + Standard: + Text: |- + DON'T LITTER! + Do NOT toss empty bottles or + here! The fish are CRYING! + + Keep the fishing hole clean! + +Custom Midna Call Need Something Text: + Standard: + Text: |- + Need Something? + +Custom Midna Call 3 Choice Text: + Standard: + Text: |- + <3 way choice 1>Hints + <3 way choice 2>Change time of day + <3 way choice 3>Return to spawn + +Custom Midna Call 2 Choice Text: + Standard: + Text: |- + <2 way choice 1>Hints + <2 way choice 2>Return to spawn + +Custom Midna Call Hints Text: + Standard: + Text: |- + I have no hints to give. + +Return to Spawn Dungeon Intro Text: + Standard: + Text: |- + I'll get you out of here. + Where do you want to go? + +Return to Spawn Dungeon Choice Text: + Standard: + Text: |- + <3 way choice 1>Dungeon entrance + <3 way choice 2>Nevermind + <3 way choice 3>Spawn + +Return to Spawn Dungeon No Choice Text: + Standard: + Text: |- + <2 way choice 1>Nevermind + <2 way choice 2>Spawn + +Midna Hints Required Dungeons Intro Zero Dungeons: + Standard: + Text: |- + There are 0 required dungeons. + +Midna Hints Required Dungeons Intro At Least One Dungeon: + Standard: + Text: |- + There are required dungeons: + +Ordon Hint Sign Text: + Standard: + Text: |- + Ordon Hint Sign. + There are no hints placed here. + +South Faron Woods Hint Sign Text: + Standard: + Text: |- + South Faron Woods Hint Sign. + There are no hints placed here. + +Sacred Grove Hint Sign Text: + Standard: + Text: |- + Sacred Grove Hint Sign. + There are no hints placed here. + +Faron Field Hint Sign Text: + Standard: + Text: |- + Faron Field Hint Sign. + There are no hints placed here. + +Kakariko Gorge Hint Sign Text: + Standard: + Text: |- + Kakariko Gorge Hint Sign. + There are no hints placed here. + +Kakariko Village Hint Sign Text: + Standard: + Text: |- + Kakariko Village Hint Sign. + There are no hints placed here. + +Kakariko Graveyard Hint Sign Text: + Standard: + Text: |- + Kakariko Graveyard Hint Sign. + There are no hints placed here. + +Eldin Field Hint Sign Text: + Standard: + Text: |- + Eldin Field Hint Sign. + There are no hints placed here. + +North Eldin Field Hint Sign Text: + Standard: + Text: |- + North Eldin Field Hint Sign. + There are no hints placed here. + +Hidden Village Hint Sign Text: + Standard: + Text: |- + Hidden Village Hint Sign. + There are no hints placed here. + +Lanayru Field Hint Sign Text: + Standard: + Text: |- + Lanayru Field Hint Sign. + There are no hints placed here. + +Beside Castle Town Hint Sign Text: + Standard: + Text: |- + Beside Castle Town Hint Sign. + There are no hints placed here. + +Castle Town Center Hint Sign Text: + Standard: + Text: |- + Castle Town Center Hint Sign. + There are no hints placed here. + +Outside South Castle Town Hint Sign Text: + Standard: + Text: |- + Outside South Castle Town Hint Sign. + There are no hints placed here. + +Lake Hylia Bridge Hint Sign Text: + Standard: + Text: |- + Lake Hylia Bridge Hint Sign. + There are no hints placed here. + +Lake Hylia Hint Sign Text: + Standard: + Text: |- + Lake Hylia Hint Sign. + There are no hints placed here. + +Lanayru Spring Hint Sign Text: + Standard: + Text: |- + Lanayru Spring Hint Sign. + There are no hints placed here. + +Lake Lantern Cave Hint Sign Text: + Standard: + Text: |- + Lake Lantern Cave Hint Sign. + There are no hints placed here. + +Fishing Hole Hint Sign Text: + Standard: + Text: |- + Fishing Hole Hint Sign. + There are no hints placed here. + +Zoras Domain Hint Sign Text: + Standard: + Text: |- + Zoras Domain Hint Sign. + There are no hints placed here. + +Snowpeak Hint Sign Text: + Standard: + Text: |- + Snowpeak Hint Sign. + There are no hints placed here. + +Gerudo Desert Hint Sign Text: + Standard: + Text: |- + Gerudo Desert Hint Sign. + There are no hints placed here. + +Bulblin Camp Hint Sign Text: + Standard: + Text: |- + Bulblin Camp Hint Sign. + There are no hints placed here. + +Forest Temple Hint Sign Text: + Standard: + Text: |- + Forest Temple Hint Sign. + There are no hints placed here. + +Goron Mines Hint Sign Text: + Standard: + Text: |- + Goron Mines Hint Sign. + There are no hints placed here. + +Lakebed Temple Hint Sign Text: + Standard: + Text: |- + Lakebed Temple Hint Sign. + There are no hints placed here. + +Arbiters Grounds Hint Sign Text: + Standard: + Text: |- + Arbiters Grounds Hint Sign. + There are no hints placed here. + +Snowpeak Ruins Hint Sign Text: + Standard: + Text: |- + Snowpeak Ruins Hint Sign. + There are no hints placed here. + +Temple of Time First Hint Sign Text: + Standard: + Text: |- + Temple of Time First Hint Sign. + There are no hints placed here. + +Temple of Time Second Hint Sign Text: + Standard: + Text: |- + Temple of Time Second Hint Sign. + There are no hints placed here. + +City in the Sky Hint Sign Text: + Standard: + Text: |- + City in the Sky Hint Sign. + There are no hints placed here. + +Palace of Twilight Hint Sign Text: + Standard: + Text: |- + Palace of Twilight Hint Sign. + There are no hints placed here. + +Hyrule Castle Hint Sign Text: + Standard: + Text: |- + Hyrule Castle Hint Sign. + There are no hints placed here. + +Cave of Ordeals Hint Sign Text: + Standard: + Text: |- + Cave of Ordeals Hint Sign. + There are no hints placed here. diff --git a/mods/randomizer/generator/data/text/languages/german.yaml b/mods/randomizer/generator/data/text/languages/german.yaml new file mode 100644 index 0000000000..3a07554aa6 --- /dev/null +++ b/mods/randomizer/generator/data/text/languages/german.yaml @@ -0,0 +1,2417 @@ +# This file contains all custom German text for the dusklight randomizer + +# NOTES FOR TRANSLATORS: +# - You should only be translating the "Text" fields for each element in this file. Do not translate the +# - Text being surrounded by braces '{}' means that the text will be colored. If a text field begins with a brace, +# the entire field must be surrounded with quotation marks. +# - Below each text element, you can specify a given text's gender and/or plurality. If you need additional +# specifiers for pieces of text, let us know. If no gender is provided, the assumption is no gender. If no +# plurality is provided, the assumed plurality is singular. + +# ITEM NAMES +Green Rupee: + Standard: + Text: Green Rupee + Pretty: + Text: a {Green Rupee} + Cryptic: + Text: a {penny} + +Blue Rupee: + Standard: + Text: Blue Rupee + Pretty: + Text: a {Blue Rupee} + Cryptic: + Text: a {fiver} + +Yellow Rupee: + Standard: + Text: Yellow Rupee + Pretty: + Text: a {Yellow Rupee} + Cryptic: + Text: some {change} + +Red Rupee: + Standard: + Text: Red Rupee + Pretty: + Text: a {Red Rupee} + Cryptic: + Text: "{couch cash}" + +Purple Rupee: + Standard: + Text: Purple Rupee + Pretty: + Text: a {Purple Rupee} + Cryptic: + Text: a {good sum} + +Orange Rupee: + Standard: + Text: Orange Rupee + Pretty: + Text: an {Orange Rupee} + Cryptic: + Text: a {payday} + +Silver Rupee: + Standard: + Text: Silver Rupee + Pretty: + Text: a {Silver Rupee} + Cryptic: + Text: "{many riches}" + +Bombs 5: + Standard: + Text: Bombs 5 + Pretty: + Text: "{Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 10: + Standard: + Text: Bombs 10 + Pretty: + Text: "{Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 20: + Standard: + Text: Bombs 20 + Pretty: + Text: "{Bombs (20)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 30: + Standard: + Text: Bombs 30 + Pretty: + Text: "{Bombs (30)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Arrows 10: + Standard: + Text: Arrows 10 + Pretty: + Text: "{Arrows (10)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 20: + Standard: + Text: Arrows 20 + Pretty: + Text: "{Arrows (20)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 30: + Standard: + Text: Arrows 30 + Pretty: + Text: "{Arrows (30)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Seeds 50: + Standard: + Text: Seeds 50 + Pretty: + Text: "{Seeds (50)}" + Plurality: Plural + Cryptic: + Text: some {pellets} + Plurality: Plural + +Foolish Item: + Standard: + Text: Foolish Item + Pretty: + Text: a {Foolish Item} + Cryptic: + Text: a {chilly surprise} + +Ordon Spring Portal: + Standard: + Text: Ordon Spring Portal + Pretty: + Text: the {Ordon Spring Portal} + Cryptic: + Text: a {portal to home} + +South Faron Portal: + Standard: + Text: South Faron Portal + Pretty: + Text: the {South Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Water Bombs 5: + Standard: + Text: Water Bombs 5 + Pretty: + Text: "{Water Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 10: + Standard: + Text: Water Bombs 10 + Pretty: + Text: "{Water Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 15: + Standard: + Text: Water Bombs 15 + Pretty: + Text: "{Water Bombs (15)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Bomblings 5: + Standard: + Text: Bomblings 5 + Pretty: + Text: "{Bomblings (5)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Bomblings 10: + Standard: + Text: Bomblings 10 + Pretty: + Text: "{Bomblings (10)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Piece of Heart: + Standard: + Text: Piece of Heart + Pretty: + Text: a {Piece of Heart} + Cryptic: + Text: some {love} + +Heart Container: + Standard: + Text: Heart Container + Pretty: + Text: a {Heart Container} + Cryptic: + Text: a {lot of love} + +Ordon Shield: + Standard: + Text: Ordon Shield + Pretty: + Text: the {Ordon Shield} + Cryptic: + Text: a {sturdy reminder of home} + +Wooden Shield: + Standard: + Text: Wooden Shield + Pretty: + Text: a {Wooden Shield} + Cryptic: + Text: a {wood protector} + +Hylian Shield: + Standard: + Text: Hylian Shield + Pretty: + Text: the {Hylian Shield} + Cryptic: + Text: an {unbreakable shield} + +Magic Armor: + Standard: + Text: Magic Armor + Pretty: + Text: the {Magic Armor} + Cryptic: + Text: "{magical clothing}" + +Zora Armor: + Standard: + Text: Zora Armor + Pretty: + Text: the {Zora Armor} + Cryptic: + Text: the {fish suit} + +Shadow Crystal: + Standard: + Text: Shadow Crystal + Pretty: + Text: the {Shadow Crystal} + Cryptic: + Text: a {crystal of dark power} + +Progressive Wallet: + Standard: + Text: Progressive Wallet + Pretty: + Text: a {Wallet} + Cryptic: + Text: a {money bag} + +Upper Zoras River Portal: + Standard: + Text: Upper Zoras River Portal + Pretty: + Text: the {Upper Zoras River Portal} + Cryptic: + Text: a {portal to some raging rapids} + +Castle Town Portal: + Standard: + Text: Castle Town Portal + Pretty: + Text: the {Castle Town Portal} + Cryptic: + Text: a {portal to the city} + +Gerudo Desert Portal: + Standard: + Text: Gerudo Desert Portal + Pretty: + Text: the {Gerudo Desert Portal} + Cryptic: + Text: a {portal to a challenging cave} + +North Faron Portal: + Standard: + Text: North Faron Portal + Pretty: + Text: the {North Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Hawkeye: + Standard: + Text: Hawkeye + Pretty: + Text: the {Hawkeye} + Cryptic: + Text: the {zoom-and-enhance} + +Progressive Sword: + Standard: + Text: Progressive Sword + Pretty: + Text: a {Sword} + Cryptic: + Text: a {sharp weapon} + +Gale Boomerang: + Standard: + Text: Gale Boomerang + Pretty: + Text: the {Gale Boomerang} + Cryptic: + Text: the {fairy of winds} + +Spinner: + Standard: + Text: Spinner + Pretty: + Text: the {Spinner} + Cryptic: + Text: the {gear rotator} + +Ball and Chain: + Standard: + Text: Ball and Chain + Pretty: + Text: the {Ball and Chain} + Cryptic: + Text: the {iron weight} + +Progressive Bow: + Standard: + Text: Progressive Bow + Pretty: + Text: a {Bow} + Cryptic: + Text: an {arrow launcher} + +Progressive Clawshot: + Standard: + Text: Progressive Clawshot + Pretty: + Text: a {Clawshot} + Cryptic: + Text: a {chain launcher} + +Iron Boots: + Standard: + Text: Iron Boots + Pretty: + Text: the {Iron Boots} + Plurality: Plural + Cryptic: + Text: the {heavy shoes} + Plurality: Plural + +Progressive Dominion Rod: + Standard: + Text: Dominion Rod + Pretty: + Text: a {Dominion Rod} + Cryptic: + Text: a {rod of control} + +Lantern: + Standard: + Text: Lantern + Pretty: + Text: the {Lantern} + Cryptic: + Text: the {small light} + +Progressive Fishing Rod: + Standard: + Text: Progressive Fishing Rod + Pretty: + Text: a {Fishing Rod} + Cryptic: + Text: a {rod of patience} + +Slingshot: + Standard: + Text: Slingshot + Pretty: + Text: the {Slingshot} + Cryptic: + Text: the {child's toy} + +Kakariko Gorge Portal: + Standard: + Text: Kakariko Gorge Portal + Pretty: + Text: the {Kakariko Gorge Portal} + Cryptic: + Text: a {portal to a big gap} + +Kakariko Village Portal: + Standard: + Text: Kakariko Village Portal + Pretty: + Text: the {Kakariko Village Portal} + Cryptic: + Text: a {portal to a village} + +Giant Bomb Bag: + Standard: + Text: Giant Bomb Bag + Pretty: + Text: a {Giant Bomb Bag} + Cryptic: + Text: an {explosive capacity upgrade} + +Bomb Bag: + Standard: + Text: Bomb Bag + Pretty: + Text: a {Bomb Bag} + Cryptic: + Text: a {bag for explosions} + +Death Mountain Portal: + Standard: + Text: Death Mountain Portal + Pretty: + Text: the {Death Mountain Portal} + Cryptic: + Text: a {portal to a volcano} + +Zoras Domain Portal: + Standard: + Text: Zoras Domain Portal + Pretty: + Text: the {Zora's Domain Portal} + Cryptic: + Text: a {portal to water} + +Empty Bottle: + Standard: + Text: Empty Bottle + Pretty: + Text: an {Empty Bottle} + Cryptic: + Text: + +Red Potion Shop: + Standard: + Text: Red Potion Shop + Pretty: + Text: a {Red Potion} + Cryptic: + Text: a {health refill} + +Blue Potion Shop: + Standard: + Text: Blue Potion Shop + Pretty: + Text: a {Blue Potion} + Cryptic: + Text: a {blue health refill} + +Bottle with Half Milk: + Standard: + Text: Bottle with Half Milk + Pretty: + Text: a {Bottle with Half Milk} + Cryptic: + Text: a {baby bottle} + +Fairy Tears: + Standard: + Text: Fairy Tears + Pretty: + Text: some {Fairy Tears} + Plurality: Plural + Cryptic: + Text: a {refill of great power} + +Bottle with Great Fairies Tears: + Standard: + Text: Bottle with Great Fairies Tears + Pretty: + Text: a {Bottle with Great Fairies Tears} + Cryptic: + Text: a {bottle of great power} + +Renados Letter: + Standard: + Text: Renados Letter + Pretty: + Text: "{Renado's Letter}" + Cryptic: + Text: a {letter from a concerned shaman} + +Invoice: + Standard: + Text: Invoice + Pretty: + Text: the {Invoice} + Cryptic: + Text: the {bill for the doctor} + +Wooden Statue: + Standard: + Text: Wooden Statue + Pretty: + Text: the {Wooden Statue} + Cryptic: + Text: "{memories of home}" + Plurality: Plural + +Ilias Charm: + Standard: + Text: Ilias Charm + Pretty: + Text: "{Ilias Charm}" + Cryptic: + Text: a {friend's item} + +Horse Call: + Standard: + Text: Horse Call + Pretty: + Text: the {Horse Call} + Cryptic: + Text: the {horse beckoner} + +Forest Temple Small Key: + Standard: + Text: Forest Temple Small Key + Pretty: + Text: a {Forest Temple Small Key} + Cryptic: + Text: a {key for a deep forest} + +Goron Mines Small Key: + Standard: + Text: Goron Mines Small Key + Pretty: + Text: a {Goron Mines Small Key} + Cryptic: + Text: a {key for a volcanic mine} + +Lakebed Temple Small Key: + Standard: + Text: Lakebed Temple Small Key + Pretty: + Text: a {Lakebed Temple Small Key} + Cryptic: + Text: a {key for an underground lake} + +Arbiters Grounds Small Key: + Standard: + Text: Arbiters Grounds Small Key + Pretty: + Text: an {Arbiters Grounds Small Key} + Cryptic: + Text: a {key for an ancient prison} + +Snowpeak Ruins Small Key: + Standard: + Text: Snowpeak Ruins Small Key + Pretty: + Text: a {Snowpeak Ruins Small Key} + Cryptic: + Text: a {key for a snowy mansion} + +Temple of Time Small Key: + Standard: + Text: Temple of Time Small Key + Pretty: + Text: a {Temple of Time Small Key} + Cryptic: + Text: a {key for the past} + +City in the Sky Small Key: + Standard: + Text: City in the Sky Small Key + Pretty: + Text: the {City in the Sky Small Key} + Cryptic: + Text: a {key for the skies above} + +Palace of Twilight Small Key: + Standard: + Text: Palace of Twilight Small Key + Pretty: + Text: a {Palace of Twilight Small Key} + Cryptic: + Text: a {key for a another realm} + +Hyrule Castle Small Key: + Standard: + Text: Hyrule Castle Small Key + Pretty: + Text: a {Hyrule Castle Small Key} + Cryptic: + Text: a {key for a kingdom's castle} + +Gerudo Desert Bulblin Camp Key: + Standard: + Text: Gerudo Bulblin Camp Small Key + Pretty: + Text: the {Gerudo Desert Bulblin Camp Key} + Cryptic: + Text: the {key for a desert tent} + +Lake Hylia Portal: + Standard: + Text: Lake Hylia Portal + Pretty: + Text: the {Lake Hylia Portal} + Cryptic: + Text: a {portal to a vast lake} + +Aurus Memo: + Standard: + Text: Aurus Memo + Pretty: + Text: "{Auru's Memo}" + Cryptic: + Text: a {friend's favor} + +Asheis Sketch: + Standard: + Text: Asheis Sketch + Pretty: + Text: "{Ashei's Sketch}" + Cryptic: + Text: a {sketch of a horrific beast} + +Forest Temple Big Key: + Standard: + Text: Forest Temple Big Key + Pretty: + Text: the {Forest Temple Big Key} + Cryptic: + Text: the {key to the twilit parasite} + +Lakebed Temple Big Key: + Standard: + Text: Lakebed Temple Big Key + Pretty: + Text: the {Lakebed Temple Big Key} + Cryptic: + Text: the {key to the twilit aquatic} + +Arbiters Grounds Big Key: + Standard: + Text: Arbiters Grounds Big Key + Pretty: + Text: the {Arbiters Grounds Big Key} + Cryptic: + Text: the {key to the twilit fossil} + +Temple of Time Big Key: + Standard: + Text: Temple of Time Big Key + Pretty: + Text: the {Temple of Time Big Key} + Cryptic: + Text: the {key to the twilit arachnid} + +City in the Sky Big Key: + Standard: + Text: City in the Sky Big Key + Pretty: + Text: the {City in the Sky Big Key} + Cryptic: + Text: the {key to the twilit dragon} + +Palace of Twilight Big Key: + Standard: + Text: Palace of Twilight Big Key + Pretty: + Text: the {Palace of Twilight Big Key} + Cryptic: + Text: the {key to the usurper king} + +Hyrule Castle Big Key: + Standard: + Text: Hyrule Castle Big Key + Pretty: + Text: a {Hyrule Castle Big Key} + Cryptic: + Text: the {key to the castle throne room} + +Forest Temple Compass: + Standard: + Text: Forest Temple Compass + Pretty: + Text: the {Forest Temple Compass} + Cryptic: + Text: the {pointer for a deep forest} + +Goron Mines Compass: + Standard: + Text: Goron Mines Compass + Pretty: + Text: the {Goron Mines Compass} + Cryptic: + Text: the {pointer for a volcano} + +Lakebed Temple Compass: + Standard: + Text: Lakebed Temple Compass + Pretty: + Text: the {Lakebed Temple Compass} + Cryptic: + Text: the {pointer for an underground lake} + +Bottle with Lantern Oil: + Standard: + Text: Bottle with Lantern Oil + Pretty: + Text: a {Bottle with Lantern Oil} + Cryptic: + Text: a {bottle with lighter fluid} + +Progressive Mirror Shard: + Standard: + Text: Progressive Mirror Shard + Pretty: + Text: a {Mirror Shard} + Cryptic: + Text: a {reflective shard of power} + +Arbiters Grounds Compass: + Standard: + Text: Arbiters Grounds Compass + Pretty: + Text: the {Arbiters Grounds Compass} + Cryptic: + Text: the {pointer for an ancient prison} + +Snowpeak Ruins Compass: + Standard: + Text: Snowpeak Ruins Compass + Pretty: + Text: the {Snowpeak Ruins Compass} + Cryptic: + Text: the {pointer for a snowy mansion} + +Temple of Time Compass: + Standard: + Text: Temple of Time Compass + Pretty: + Text: the {Temple of Time Compass} + Cryptic: + Text: the {pointer for the past} + +City in the Sky Compass: + Standard: + Text: City in the Sky Compass + Pretty: + Text: the {City in the Sky Compass} + Cryptic: + Text: the {pointer for the skies above} + +Palace of Twilight Compass: + Standard: + Text: Palace of Twilight Compass + Pretty: + Text: the {Palace of Twilight Compass} + Cryptic: + Text: the {pointer for another realm} + +Hyrule Castle Compass: + Standard: + Text: Hyrule Castle Compass + Pretty: + Text: a {Hyrule Castle Compass} + Cryptic: + Text: the {pointer for the kingdom's castle} + +Mirror Chamber Portal: + Standard: + Text: Mirror Chamber Portal + Pretty: + Text: the {Mirror Chamber Portal} + Cryptic: + Text: a {portal to a coliseum} + +Snowpeak Portal: + Standard: + Text: Snowpeak Portal + Pretty: + Text: the {Snowpeak Portal} + Cryptic: + Text: a {portal to a snowy mountain} + +Forest Temple Dungeon Map: + Standard: + Text: Forest Temple Dungeon Map + Pretty: + Text: the {Forest Temple Dungeon Map} + Cryptic: + Text: the {map for a deep forest} + +Goron Mines Dungeon Map: + Standard: + Text: Goron Mines Dungeon Map + Pretty: + Text: the {Goron Mines Dungeon Map} + Cryptic: + Text: the {map for a volcano} + +Lakebed Temple Dungeon Map: + Standard: + Text: Lakebed Temple Dungeon Map + Pretty: + Text: the {Lakebed Temple Dungeon Map} + Cryptic: + Text: the {map for an underground lake} + +Arbiters Grounds Dungeon Map: + Standard: + Text: Arbiters Grounds Dungeon Map + Pretty: + Text: the {Arbiters Grounds Dungeon Map} + Cryptic: + Text: the {map for an ancient prison} + +Snowpeak Ruins Dungeon Map: + Standard: + Text: Snowpeak Ruins Dungeon Map + Pretty: + Text: the {Snowpeak Ruins Dungeon Map} + Cryptic: + Text: the {map for a snowy mansion} + +Temple of Time Dungeon Map: + Standard: + Text: Temple of Time Dungeon Map + Pretty: + Text: the {Temple of Time Dungeon Map} + Cryptic: + Text: the {map for the past} + +City in the Sky Dungeon Map: + Standard: + Text: City in the Sky Dungeon Map + Pretty: + Text: the {City in the Sky Dungeon Map} + Cryptic: + Text: the {map for the skies above} + +Palace of Twilight Dungeon Map: + Standard: + Text: Palace of Twilight Dungeon Map + Pretty: + Text: the {Palace of Twilight Dungeon Map} + Cryptic: + Text: the {map for another realm} + +Hyrule Castle Dungeon Map: + Standard: + Text: Hyrule Castle Dungeon Map + Pretty: + Text: a {Hyrule Castle Dungeon Map} + Cryptic: + Text: the {map for the kingdom's castle} + +Sacred Grove Portal: + Standard: + Text: Sacred Grove Portal + Pretty: + Text: the {Sacred Grove Portal} + Cryptic: + Text: a {portal to an ancient forest} + +Male Beetle: + Standard: + Text: Male Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Female Beetle: + Standard: + Text: Female Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Male Butterfly: + Standard: + Text: Male Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Female Butterfly: + Standard: + Text: Female Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Male Stag Beetle: + Standard: + Text: Male Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Female Stag Beetle: + Standard: + Text: Female Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Male Grasshopper: + Standard: + Text: Male Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Female Grasshopper: + Standard: + Text: Female Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Male Phasmid: + Standard: + Text: Male Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Female Phasmid: + Standard: + Text: Female Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Male Pill Bug: + Standard: + Text: Male Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Female Pill Bug: + Standard: + Text: Female Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Male Mantis: + Standard: + Text: Male Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Female Mantis: + Standard: + Text: Female Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Male Ladybug: + Standard: + Text: Male Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Female Ladybug: + Standard: + Text: Female Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Male Snail: + Standard: + Text: Male Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Female Snail: + Standard: + Text: Female Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Male Dragonfly: + Standard: + Text: Male Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Female Dragonfly: + Standard: + Text: Female Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Male Ant: + Standard: + Text: Male Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Female Ant: + Standard: + Text: Female Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Male Dayfly: + Standard: + Text: Male Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Female Dayfly: + Standard: + Text: Female Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Progressive Fused Shadow: + Standard: + Text: Progressive Fused Shadow + Pretty: + Text: a {Fused Shadow} + Cryptic: + Text: a {shadow of ultimate power} + +Poe Soul: + Standard: + Text: Poe Soul + Pretty: + Text: a {Poe Soul} + Cryptic: + Text: a {soul of the dead} + +Progressive Hidden Skill: + Standard: + Text: Progressive Hidden Skill + Pretty: + Text: a {Hidden Skill} + Cryptic: + Text: a {forgotten technique} + + +Bridge of Eldin Portal: + Standard: + Text: Bridge of Eldin Portal + Pretty: + Text: the {Bridge of Eldin Portal} + Cryptic: + Text: a {portal to a long bridge} + +Progressive Sky Book: + Standard: + Text: Progressive Sky Book + Pretty: + Text: a {Sky Character} + Cryptic: + Text: a {glyph of the heavens} + +Purple Rupee Links House: + Standard: + Text: Purple Rupee Links House + Pretty: + Text: the {Purple Rupee from your basement} + Cryptic: + Text: "{your savings}" + Plurality: Plural + +North Faron Woods Gate Key: + Standard: + Text: North Faron Woods Gate Key + Pretty: + Text: the {North Faron Woods Gate Key} + Cryptic: + Text: a {key to a northern forest} + +Gate Keys: + Standard: + Text: Gate Keys + Pretty: + Text: the {Gate Keys} + Plurality: Plural + Cryptic: + Text: "{King Bulblin's keys}" + Plurality: Plural + +Ordon Pumpkin: + Standard: + Text: Ordon Pumpkin + Pretty: + Text: the {Ordon Pumpkin} + Cryptic: + Text: a {soup ingredient} + +Ordon Cheese: + Standard: + Text: Ordon Cheese + Pretty: + Text: some {Ordon Cheese} + Cryptic: + Text: a {soup ingredient} + +Snowpeak Ruins Bedroom Key: + Standard: + Text: Snowpeak Ruins Bedroom Key + Pretty: + Text: the {Snowpeak Ruins Bedroom Key} + Cryptic: + Text: the {key to a snowy bedroom} + +Goron Mines Key Shard: + Standard: + Text: Goron Mines Key Shard + Pretty: + Text: a {Goron Mines Key Shard} + Cryptic: + Text: "{one third of a key}" + +Coro Key: + Standard: + Text: Coro Key + Pretty: + Text: "{Coro's Key}" + Cryptic: + Text: a {key to a forest cave} + +Game Beatable: + Standard: + Text: Game Beatable + Pretty: + Text: "{Game Beatable}" + Cryptic: + Text: the {game-winning item} + +Hint: + Standard: + Text: Hint + Pretty: + Text: a {Hint} + Cryptic: + Text: a {piece of knowledge} + +Faron Twilight Tear: + Standard: + Text: Faron Twilight Tear + Pretty: + Text: a {Faron Twilight Tear} + Cryptic: + Text: a {tear of a forest spirit} + +Eldin Twilight Tear: + Standard: + Text: Eldin Twilight Tear + Pretty: + Text: an {Eldin Twilight Tear} + Cryptic: + Text: a {tear of a volcano spirit} + +Lanayru Twilight Tear: + Standard: + Text: Lanayru Twilight Tear + Pretty: + Text: a {Lanayru Twilight Tear} + Cryptic: + Text: a {tear of a lake spirit} + +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) + +# HINT REGION NAMES +Ordon: + Standard: + Text: Ordon + Pretty: + Text: "{Ordon}" + Cryptic: + Text: a {quaint village} + +Faron Woods: + Standard: + Text: Faron Woods + Pretty: + Text: "{Faron Woods}" + Cryptic: + Text: a {forest} + +Sacred Grove: + Standard: + Text: Sacred Grove + Pretty: + Text: the {Sacred Grove} + Cryptic: + Text: a {hidden grove} + +Faron Field: + Standard: + Text: Faron Field + Pretty: + Text: "{Faron Field}" + Cryptic: + Text: a {field near the forest} + +Kakariko Gorge: + Standard: + Text: Kakariko Gorge + Pretty: + Text: "{Kakariko Gorge}" + Cryptic: + Text: a {field with a large chasm} + +Kakariko Village: + Standard: + Text: Kakariko Village + Pretty: + Text: "{Kakariko Village}" + Cryptic: + Text: a {charming village} + +Kakariko Graveyard: + Standard: + Text: Kakariko Graveyard + Pretty: + Text: the {Kakariko Graveyard} + Cryptic: + Text: a {yard for the dead} + +Death Mountain: + Standard: + Text: Death Mountain + Pretty: + Text: "{Death Mountain}" + Cryptic: + Text: a {volcano path} + +Eldin Field: + Standard: + Text: Eldin Field + Pretty: + Text: "{Eldin Field}" + Cryptic: + Text: a {field near a volcano} + +North Eldin: + Standard: + Text: North Eldin + Pretty: + Text: "{North Eldin}" + Cryptic: + Text: a {narrow gray field} + +Hidden Village: + Standard: + Text: Hidden Village + Pretty: + Text: the {Hidden Village} + Cryptic: + Text: a {secluded settlement} + +Lanayru Field: + Standard: + Text: Lanayru Field + Pretty: + Text: "{Lanayru Field}" + Cryptic: + Text: a {field with a river} + +Beside Castle Town: + Standard: + Text: Beside Castle Town + Pretty: + Text: "{Beside Castle Town}" + Cryptic: + Text: a {field beside a city} + +Castle Town: + Standard: + Text: Castle Town + Pretty: + Text: "{Castle Town}" + Cryptic: + Text: a {city} + +South of Castle Town: + Standard: + Text: South of Castle Town + Pretty: + Text: "{South of Castle Town}" + Cryptic: + Text: a {field south of a city} + +Great Bridge of Hylia: + Standard: + Text: Great Bridge of Hylia + Pretty: + Text: the {Great Bridge of Hylia} + Cryptic: + Text: a {path along a great bridge} + +Lake Hylia: + Standard: + Text: Lake Hylia + Pretty: + Text: "{Lake Hylia}" + Cryptic: + Text: a {vast lake} + +Lanayru Spring: + Standard: + Text: Lanayru Spring + Pretty: + Text: the {Lanayru Spring} + Cryptic: + Text: a {cavernous spring} + +Upper Zoras River: + Standard: + Text: Upper Zoras River + Pretty: + Text: "{Upper Zoras River}" + Cryptic: + Text: a {fork in the river} + +Zoras Domain: + Standard: + Text: Zoras Domain + Pretty: + Text: "{Zoras Domain}" + Cryptic: + Text: the {home of a grand waterfall} + +South Gerudo Desert: + Standard: + Text: South Gerudo Desert + Pretty: + Text: "{South Gerudo Desert}" + Cryptic: + Text: the {southern desert} + +North Gerudo Desert: + Standard: + Text: North Gerudo Desert + Pretty: + Text: "{North Gerudo Desert}" + Cryptic: + Text: the {northern desert} + +Bublin Camp: + Standard: + Text: Bublin Camp + Pretty: + Text: "{Bublin Camp}" + Cryptic: + Text: a {camp of enemies} + +Mirror Chamber: + Standard: + Text: Mirror Chamber + Pretty: + Text: the {Mirror Chamber} + Cryptic: + Text: a {chamber of chains} + +Forest Temple: + Standard: + Text: Forest Temple + Pretty: + Text: the {Forest Temple} + Cryptic: + Text: a {deep forest} + +Goron Mines: + Standard: + Text: Goron Mines + Pretty: + Text: the {Goron Mines} + Cryptic: + Text: a {volcanic mine} + +Lakebed Temple: + Standard: + Text: Lakebed Temple + Pretty: + Text: the {Lakebed Temple} + Cryptic: + Text: an {underground lake} + +Arbiters Grounds: + Standard: + Text: Arbiters Grounds + Pretty: + Text: the {Arbiters Grounds} + Cryptic: + Text: an {ancient prison} + +Snowpeak Ruins: + Standard: + Text: Snowpeak Ruins + Pretty: + Text: the {Snowpeak Ruins} + Cryptic: + Text: a {snowy mansion} + +Temple of Time: + Standard: + Text: Temple of Time + Pretty: + Text: the {Temple of Time} + Cryptic: + Text: the {past} + +City in the Sky: + Standard: + Text: City in the Sky + Pretty: + Text: the {City in the Sky} + Cryptic: + Text: the {skies above} + +Palace of Twilight: + Standard: + Text: Palace of Twilight + Pretty: + Text: the {Palace of Twilight} + Cryptic: + Text: "{another realm}" + +Hyrule Castle: + Standard: + Text: Hyrule Castle + Pretty: + Text: the {Hyrule Castle} + Cryptic: + Text: the {kingdom's castle} + +# NO REQUIRED DUNGEON TEXT +No Required Dungeons Text: + Standard: + Text: No Required Dungeons + +# ITEM GET TEXT +Foolish Get Item Text: + Standard: + Text: |- + a cold wind blows... + +Shadow Crystal Get Item Text: + Standard: + Text: |- + Du erhältst den Nachtkristall! + Diese dunkle Manifestation + derMacht Zantos erlaubt es dir, + dichnach Belieben zu verwandeln! + +Restored Dominion Rod Text: + Standard: + Text: |- + Power has been restored to + the Dominion Rod! Now it can + be used to imbue statues + with life in the present! + +Forest Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Forest Temple! + +Goron Mines Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Goron Mines! + +Lakebed Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Lakebed Temple! + +Arbiters Grounds Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Arbiter's Grounds! + +Snowpeak Ruins Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Snowpeak Ruins! + +Temple of Time Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Temple of Time! + +City in the Sky Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + City in the Sky! + +Palace of Twilight Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Palace of Twilight! + +Hyrule Castle Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Hyrule Castle! + +Bulblin Camp Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Bulblin Camp! + +Forest Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Forest Temple! + +Lakebed Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Lakebed Temple! + +Arbiters Grounds Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Arbiter's Grounds! + +Temple of Time Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Temple of Time! + +City in the Sky Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + City in the Sky! + +Palace of Twilight Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Palace of Twilight! + +Hyrule Castle Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Hyrule Castle! + +Forest Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Forest Temple! + +Goron Mines Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Goron Mines! + +Lakebed Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Lakebed Temple! + +Mirror Shard 2 Get item Text: + Standard: + Text: |- + You got the second shard of + the Mirror of Twilight! It + has a beautiful shine to it + and feels slightly cold... + +Mirror Shard 3 Get item Text: + Standard: + Text: |- + You got the third shard of + the Mirror of Twilight! It + is covered in dirt and + webs... + +Mirror Shard 4 Get item Text: + Standard: + Text: |- + You got the final shard of + the Mirror of Twilight! It + feels lighter than air... + +Arbiters Grounds Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Arbiter's Grounds! + +Snowpeak Ruins Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Snowpeak Ruins! + +Temple of Time Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Temple of Time! + +City in the Sky Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + City in the Sky! + +Palace of Twilight Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Palace of Twilight! + +Hyrule Castle Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Hyrule Castle! + +# +Forest Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Forest Temple! + +Goron Mines Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Goron Mines! + +Lakebed Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Lakebed Temple! + +Snowpeak Ruins Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Snowpeak Ruins! + +Arbiters Grounds Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Arbiter's Grounds! + +Temple of Time Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Temple of Time! + +City in the Sky Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + City in the Sky! + +Palace of Twilight Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Palace of Twilight! + +Hyrule Castle Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Hyrule Castle! + + +Fused Shadow 1 Get Item Text: + Standard: + Text: |- + You got a Fused Shadow! + It seems to have some moss + growing on it... + +Fused Shadow 2 Get Item Text: + Standard: + Text: |- + You got the second Fused + Shadow! It feels warm to + the touch... + +Fused Shadow 3 Get Item Text: + Standard: + Text: |- + You got the final Fused + Shadow! It feels wet and + smells like fish... + +Mirror Shard 1 Get Item Text: + Standard: + Text: |- + You got the first shard of + the Mirror of Twilight! It + is covered in sand... + +Poe Soul Get Item Text: + Standard: + Text: |- + You got a Poe's Soul! + You've collected {} so far. + +Ending Blow Get Item Text: + Standard: + Text: |- + You learned the Ending Blow! + +Shield Attack Get Item Text: + Standard: + Text: |- + You learned the Shield Attack! + +Back Slice Get Item Text: + Standard: + Text: |- + You learned the Back Slice! + +Helm Splitter Get Item Text: + Standard: + Text: |- + You learned the Helm Splitter! + +Mortal Draw Get Item Text: + Standard: + Text: |- + You learned the Mortal Draw! + +Jump Strike Get Item Text: + Standard: + Text: |- + You learned the Jump Strike! + +Great Spin Get Item Text: + Standard: + Text: |- + You learned the Great Spin! + +Partially Filled Sky Book Get Item Text: + Standard: + Text: |- + You got a Sky Character! + You've collected {} so far. + +Midna Call As Human Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into wolf + <2 way choice 2>Something else + +Midna Call As Wolf Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into human + <2 way choice 2>Something else + +Midna Call As Wolf No Shadow Crystal Two Choice: + Standard: + Text: |- + <2 way choice 1>Warp + <2 way choice 2>Something else + +Midna Call As Human Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into wolf + <3 way choice 2>Warp + <3 way choice 3>Something else + +Midna Call As Wolf Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into human + <3 way choice 2>Warp + <3 way choice 3>Something else + +Slingshot Shop Text Template: + Standard: + Text: |- + : 30 Rupees +# I got this in for the kids. It's just a +# toy, but it stings something AWFUL +# when you get hit by it! + +Slingshot Shop Too Expensive Text Template: + Standard: + Text: |- + is 30 Rupees. If you want it, bring some money with you, all right, m'dear? + +Slingshot Shop Purchase Confirmation Text Template: + Standard: + Text: |- + is 30 Rupees. Do you want to buy it, m'dear? + +Slingshot Shop After Purchase Text Template: + Standard: + Text: |- + What are you doing buying , you naughty thing? You're too old for toys! Will you at least let the kids play with it? + +Barnes Special Offer Text Template: + Standard: + Text: |- + I've got a special offer goin' right now: , just 120 Rupees! How 'bout that? + +Kakariko Malo Mart Wooden Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 50 Rupees. Want one or not? + +Kakariko Malo Mart Wooden Shield Too Expensive Text Template: + Standard: + Text: |- + will cost you 50 Rupees, but you can't afford it. Don't expect a discount just because we're from the same town. + +Kakariko Malo Mart Hylian Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 200 Rupees. Want one or not? + +Kakariko Malo Mart Hylian Shield Too Expensive Text Template: + Standard: + Text: |- + will run you 200 Rupees...but if you have that much, I'll eat my hat. And I don't even HAVE a hat. + +Kakariko Malo Mart Hylian Shield After Purchase Text Template: + Standard: + Text: |- + Well, you bought my last ... so you'd better take good care of it. + +Kakariko Malo Mart Hawkeye Purchase Confirmation Text Template: + Standard: + Text: |- + is 100 Rupees. You want it or not? + +Kakariko Malo Mart Hawkeye Too Expensive Text Template: + Standard: + Text: |- + costs 100 Rupees... but there are people with enough Rupees, and then there's you. The guy with not enough. + +Kakariko Malo Mart Hawkeye After Purchase Text Template: + Standard: + Text: |- + You bought my last ... + +Kakariko Malo Mart Red Potion Too Expensive Text Template: + Standard: + Text: |- + will cost you 30 Rupees, but I won't be donating it to the poor, sorry. + +Kakariko Malo Mart Red Potion Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 30 Rupees. Want some or not? + +Kakariko Malo Mart Red Potion Text Template: + Standard: + Text: |- + : 30 Rupees +# This potion replenishes your +# life energy. Keep it in an empty +# bottle. + +Kakariko Malo Mart Hawkeye Coming Soon Text Template: + Standard: + Text: |- + : COMING SOON + +Kakariko Malo Mart Hawkeye Text Template: + Standard: + Text: |- + : 100 Rupees +# This eyewear allows you to see +# distant objects as if with the eyes +# of a hawk. + +Kakariko Malo Mart Sold Out Text: + Standard: + Text: SOLD OUT + +Kakariko Malo Mart Wooden Shield Text Template: + Standard: + Text: |- + : 50 Rupees +# This is a simple shield. It's made of +# wood, so it will burn away if +# touched by fire. + +Kakariko Malo Mart Hylian Shield Text Template: + Standard: + Text: |- + : 200 Rupees +# LIMITED SUPPLY! +# Don't let them sell out before you +# buy one! + +Chudleys Shop Magic Armor Text Template: + Standard: + Text: |- + + Only for the richest and most + precious customers who value their + lives over their Rupees. + +Castle Town Malo Mart Magic Armor After Purchase Text Template: + Standard: + Text: |- + We have sold out of ! + +Castle Town Malo Mart Magic Armor Text Template: + Standard: + Text: |- + !Special! 598 Rupees +# This is quite a bargain when you +# think of how valuable your life is. +# What's a few Rupees to stay alive? + +Castle Town Malo Mart Magic Armor Sold Out Text Template: + Standard: + Text: |- + + -SOLD OUT- + *This item has been discontinued. + +Charlo Donation Choice Text: + Standard: + Text: |- + <3 way choice 1>100 Rupees + <3 way choice 2>50 Rupees + <3 way choice 3>Sorry... + +Charlo Donation Ask Text Template: + Standard: + Text: |- + For ... + Would you please make a donation? + +Coro Bottle Offer 1 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Coro Bottle Offer 2 Text Template: + Standard: + Text: |- + I have a special, one-time offer of + for only 100 Rupees. How 'bout it, guy? + +Coro Bottle Offer 3 Text Template: + Standard: + Text: |- + Right now we have a 100-Rupee + and 20-Rupee refills to choose from! + +Coro Bottle Offer 4 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Fishing Hole Sign Text Template: + Standard: + Text: |- + DON'T LITTER! + Do NOT toss empty bottles or + here! The fish are CRYING! + + Keep the fishing hole clean! + +Custom Midna Call Need Something Text: + Standard: + Text: |- + Need Something? + +Custom Midna Call 3 Choice Text: + Standard: + Text: |- + <3 way choice 1>Hints + <3 way choice 2>Change time of day + <3 way choice 3>Return to spawn + +Custom Midna Call 2 Choice Text: + Standard: + Text: |- + <2 way choice 1>Hints + <2 way choice 2>Return to spawn + +Custom Midna Call Hints Text: + Standard: + Text: |- + I have no hints to give. + +Return to Spawn Dungeon Intro Text: + Standard: + Text: |- + I'll get you out of here. + Where do you want to go? + +Return to Spawn Dungeon Choice Text: + Standard: + Text: |- + <3 way choice 1>Dungeon entrance + <3 way choice 2>Nevermind + <3 way choice 3>Spawn + +Return to Spawn Dungeon No Choice Text: + Standard: + Text: |- + <2 way choice 1>Nevermind + <2 way choice 2>Spawn + +Midna Hints Required Dungeons Intro Zero Dungeons: + Standard: + Text: |- + There are 0 required dungeons. + +Midna Hints Required Dungeons Intro At Least One Dungeon: + Standard: + Text: |- + There are required dungeons: + +Ordon Hint Sign Text: + Standard: + Text: |- + Ordon Hint Sign. + There are no hints placed here. + +South Faron Woods Hint Sign Text: + Standard: + Text: |- + South Faron Woods Hint Sign. + There are no hints placed here. + +Sacred Grove Hint Sign Text: + Standard: + Text: |- + Sacred Grove Hint Sign. + There are no hints placed here. + +Faron Field Hint Sign Text: + Standard: + Text: |- + Faron Field Hint Sign. + There are no hints placed here. + +Kakariko Gorge Hint Sign Text: + Standard: + Text: |- + Kakariko Gorge Hint Sign. + There are no hints placed here. + +Kakariko Village Hint Sign Text: + Standard: + Text: |- + Kakariko Village Hint Sign. + There are no hints placed here. + +Kakariko Graveyard Hint Sign Text: + Standard: + Text: |- + Kakariko Graveyard Hint Sign. + There are no hints placed here. + +Eldin Field Hint Sign Text: + Standard: + Text: |- + Eldin Field Hint Sign. + There are no hints placed here. + +North Eldin Field Hint Sign Text: + Standard: + Text: |- + North Eldin Field Hint Sign. + There are no hints placed here. + +Hidden Village Hint Sign Text: + Standard: + Text: |- + Hidden Village Hint Sign. + There are no hints placed here. + +Lanayru Field Hint Sign Text: + Standard: + Text: |- + Lanayru Field Hint Sign. + There are no hints placed here. + +Beside Castle Town Hint Sign Text: + Standard: + Text: |- + Beside Castle Town Hint Sign. + There are no hints placed here. + +Castle Town Center Hint Sign Text: + Standard: + Text: |- + Castle Town Center Hint Sign. + There are no hints placed here. + +Outside South Castle Town Hint Sign Text: + Standard: + Text: |- + Outside South Castle Town Hint Sign. + There are no hints placed here. + +Lake Hylia Bridge Hint Sign Text: + Standard: + Text: |- + Lake Hylia Bridge Hint Sign. + There are no hints placed here. + +Lake Hylia Hint Sign Text: + Standard: + Text: |- + Lake Hylia Hint Sign. + There are no hints placed here. + +Lanayru Spring Hint Sign Text: + Standard: + Text: |- + Lanayru Spring Hint Sign. + There are no hints placed here. + +Lake Lantern Cave Hint Sign Text: + Standard: + Text: |- + Lake Lantern Cave Hint Sign. + There are no hints placed here. + +Fishing Hole Hint Sign Text: + Standard: + Text: |- + Fishing Hole Hint Sign. + There are no hints placed here. + +Zoras Domain Hint Sign Text: + Standard: + Text: |- + Zoras Domain Hint Sign. + There are no hints placed here. + +Snowpeak Hint Sign Text: + Standard: + Text: |- + Snowpeak Hint Sign. + There are no hints placed here. + +Gerudo Desert Hint Sign Text: + Standard: + Text: |- + Gerudo Desert Hint Sign. + There are no hints placed here. + +Bulblin Camp Hint Sign Text: + Standard: + Text: |- + Bulblin Camp Hint Sign. + There are no hints placed here. + +Forest Temple Hint Sign Text: + Standard: + Text: |- + Forest Temple Hint Sign. + There are no hints placed here. + +Goron Mines Hint Sign Text: + Standard: + Text: |- + Goron Mines Hint Sign. + There are no hints placed here. + +Lakebed Temple Hint Sign Text: + Standard: + Text: |- + Lakebed Temple Hint Sign. + There are no hints placed here. + +Arbiters Grounds Hint Sign Text: + Standard: + Text: |- + Arbiters Grounds Hint Sign. + There are no hints placed here. + +Snowpeak Ruins Hint Sign Text: + Standard: + Text: |- + Snowpeak Ruins Hint Sign. + There are no hints placed here. + +Temple of Time First Hint Sign Text: + Standard: + Text: |- + Temple of Time First Hint Sign. + There are no hints placed here. + +Temple of Time Second Hint Sign Text: + Standard: + Text: |- + Temple of Time Second Hint Sign. + There are no hints placed here. + +City in the Sky Hint Sign Text: + Standard: + Text: |- + City in the Sky Hint Sign. + There are no hints placed here. + +Palace of Twilight Hint Sign Text: + Standard: + Text: |- + Palace of Twilight Hint Sign. + There are no hints placed here. + +Hyrule Castle Hint Sign Text: + Standard: + Text: |- + Hyrule Castle Hint Sign. + There are no hints placed here. + +Cave of Ordeals Hint Sign Text: + Standard: + Text: |- + Cave of Ordeals Hint Sign. + There are no hints placed here. diff --git a/mods/randomizer/generator/data/text/languages/italian.yaml b/mods/randomizer/generator/data/text/languages/italian.yaml new file mode 100644 index 0000000000..fdc0b7e5b4 --- /dev/null +++ b/mods/randomizer/generator/data/text/languages/italian.yaml @@ -0,0 +1,2417 @@ +# This file contains all custom Italian text for the dusklight randomizer + +# NOTES FOR TRANSLATORS: +# - You should only be translating the "Text" fields for each element in this file. Do not translate the +# - Text being surrounded by braces '{}' means that the text will be colored. If a text field begins with a brace, +# the entire field must be surrounded with quotation marks. +# - Below each text element, you can specify a given text's gender and/or plurality. If you need additional +# specifiers for pieces of text, let us know. If no gender is provided, the assumption is no gender. If no +# plurality is provided, the assumed plurality is singular. + +# ITEM NAMES +Green Rupee: + Standard: + Text: Green Rupee + Pretty: + Text: a {Green Rupee} + Cryptic: + Text: a {penny} + +Blue Rupee: + Standard: + Text: Blue Rupee + Pretty: + Text: a {Blue Rupee} + Cryptic: + Text: a {fiver} + +Yellow Rupee: + Standard: + Text: Yellow Rupee + Pretty: + Text: a {Yellow Rupee} + Cryptic: + Text: some {change} + +Red Rupee: + Standard: + Text: Red Rupee + Pretty: + Text: a {Red Rupee} + Cryptic: + Text: "{couch cash}" + +Purple Rupee: + Standard: + Text: Purple Rupee + Pretty: + Text: a {Purple Rupee} + Cryptic: + Text: a {good sum} + +Orange Rupee: + Standard: + Text: Orange Rupee + Pretty: + Text: an {Orange Rupee} + Cryptic: + Text: a {payday} + +Silver Rupee: + Standard: + Text: Silver Rupee + Pretty: + Text: a {Silver Rupee} + Cryptic: + Text: "{many riches}" + +Bombs 5: + Standard: + Text: Bombs 5 + Pretty: + Text: "{Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 10: + Standard: + Text: Bombs 10 + Pretty: + Text: "{Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 20: + Standard: + Text: Bombs 20 + Pretty: + Text: "{Bombs (20)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 30: + Standard: + Text: Bombs 30 + Pretty: + Text: "{Bombs (30)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Arrows 10: + Standard: + Text: Arrows 10 + Pretty: + Text: "{Arrows (10)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 20: + Standard: + Text: Arrows 20 + Pretty: + Text: "{Arrows (20)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 30: + Standard: + Text: Arrows 30 + Pretty: + Text: "{Arrows (30)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Seeds 50: + Standard: + Text: Seeds 50 + Pretty: + Text: "{Seeds (50)}" + Plurality: Plural + Cryptic: + Text: some {pellets} + Plurality: Plural + +Foolish Item: + Standard: + Text: Foolish Item + Pretty: + Text: a {Foolish Item} + Cryptic: + Text: a {chilly surprise} + +Ordon Spring Portal: + Standard: + Text: Ordon Spring Portal + Pretty: + Text: the {Ordon Spring Portal} + Cryptic: + Text: a {portal to home} + +South Faron Portal: + Standard: + Text: South Faron Portal + Pretty: + Text: the {South Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Water Bombs 5: + Standard: + Text: Water Bombs 5 + Pretty: + Text: "{Water Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 10: + Standard: + Text: Water Bombs 10 + Pretty: + Text: "{Water Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 15: + Standard: + Text: Water Bombs 15 + Pretty: + Text: "{Water Bombs (15)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Bomblings 5: + Standard: + Text: Bomblings 5 + Pretty: + Text: "{Bomblings (5)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Bomblings 10: + Standard: + Text: Bomblings 10 + Pretty: + Text: "{Bomblings (10)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Piece of Heart: + Standard: + Text: Piece of Heart + Pretty: + Text: a {Piece of Heart} + Cryptic: + Text: some {love} + +Heart Container: + Standard: + Text: Heart Container + Pretty: + Text: a {Heart Container} + Cryptic: + Text: a {lot of love} + +Ordon Shield: + Standard: + Text: Ordon Shield + Pretty: + Text: the {Ordon Shield} + Cryptic: + Text: a {sturdy reminder of home} + +Wooden Shield: + Standard: + Text: Wooden Shield + Pretty: + Text: a {Wooden Shield} + Cryptic: + Text: a {wood protector} + +Hylian Shield: + Standard: + Text: Hylian Shield + Pretty: + Text: the {Hylian Shield} + Cryptic: + Text: an {unbreakable shield} + +Magic Armor: + Standard: + Text: Magic Armor + Pretty: + Text: the {Magic Armor} + Cryptic: + Text: "{magical clothing}" + +Zora Armor: + Standard: + Text: Zora Armor + Pretty: + Text: the {Zora Armor} + Cryptic: + Text: the {fish suit} + +Shadow Crystal: + Standard: + Text: Shadow Crystal + Pretty: + Text: the {Shadow Crystal} + Cryptic: + Text: a {crystal of dark power} + +Progressive Wallet: + Standard: + Text: Progressive Wallet + Pretty: + Text: a {Wallet} + Cryptic: + Text: a {money bag} + +Upper Zoras River Portal: + Standard: + Text: Upper Zoras River Portal + Pretty: + Text: the {Upper Zoras River Portal} + Cryptic: + Text: a {portal to some raging rapids} + +Castle Town Portal: + Standard: + Text: Castle Town Portal + Pretty: + Text: the {Castle Town Portal} + Cryptic: + Text: a {portal to the city} + +Gerudo Desert Portal: + Standard: + Text: Gerudo Desert Portal + Pretty: + Text: the {Gerudo Desert Portal} + Cryptic: + Text: a {portal to a challenging cave} + +North Faron Portal: + Standard: + Text: North Faron Portal + Pretty: + Text: the {North Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Hawkeye: + Standard: + Text: Hawkeye + Pretty: + Text: the {Hawkeye} + Cryptic: + Text: the {zoom-and-enhance} + +Progressive Sword: + Standard: + Text: Progressive Sword + Pretty: + Text: a {Sword} + Cryptic: + Text: a {sharp weapon} + +Gale Boomerang: + Standard: + Text: Gale Boomerang + Pretty: + Text: the {Gale Boomerang} + Cryptic: + Text: the {fairy of winds} + +Spinner: + Standard: + Text: Spinner + Pretty: + Text: the {Spinner} + Cryptic: + Text: the {gear rotator} + +Ball and Chain: + Standard: + Text: Ball and Chain + Pretty: + Text: the {Ball and Chain} + Cryptic: + Text: the {iron weight} + +Progressive Bow: + Standard: + Text: Progressive Bow + Pretty: + Text: a {Bow} + Cryptic: + Text: an {arrow launcher} + +Progressive Clawshot: + Standard: + Text: Progressive Clawshot + Pretty: + Text: a {Clawshot} + Cryptic: + Text: a {chain launcher} + +Iron Boots: + Standard: + Text: Iron Boots + Pretty: + Text: the {Iron Boots} + Plurality: Plural + Cryptic: + Text: the {heavy shoes} + Plurality: Plural + +Progressive Dominion Rod: + Standard: + Text: Dominion Rod + Pretty: + Text: a {Dominion Rod} + Cryptic: + Text: a {rod of control} + +Lantern: + Standard: + Text: Lantern + Pretty: + Text: the {Lantern} + Cryptic: + Text: the {small light} + +Progressive Fishing Rod: + Standard: + Text: Progressive Fishing Rod + Pretty: + Text: a {Fishing Rod} + Cryptic: + Text: a {rod of patience} + +Slingshot: + Standard: + Text: Slingshot + Pretty: + Text: the {Slingshot} + Cryptic: + Text: the {child's toy} + +Kakariko Gorge Portal: + Standard: + Text: Kakariko Gorge Portal + Pretty: + Text: the {Kakariko Gorge Portal} + Cryptic: + Text: a {portal to a big gap} + +Kakariko Village Portal: + Standard: + Text: Kakariko Village Portal + Pretty: + Text: the {Kakariko Village Portal} + Cryptic: + Text: a {portal to a village} + +Giant Bomb Bag: + Standard: + Text: Giant Bomb Bag + Pretty: + Text: a {Giant Bomb Bag} + Cryptic: + Text: an {explosive capacity upgrade} + +Bomb Bag: + Standard: + Text: Bomb Bag + Pretty: + Text: a {Bomb Bag} + Cryptic: + Text: a {bag for explosions} + +Death Mountain Portal: + Standard: + Text: Death Mountain Portal + Pretty: + Text: the {Death Mountain Portal} + Cryptic: + Text: a {portal to a volcano} + +Zoras Domain Portal: + Standard: + Text: Zoras Domain Portal + Pretty: + Text: the {Zora's Domain Portal} + Cryptic: + Text: a {portal to water} + +Empty Bottle: + Standard: + Text: Empty Bottle + Pretty: + Text: an {Empty Bottle} + Cryptic: + Text: + +Red Potion Shop: + Standard: + Text: Red Potion Shop + Pretty: + Text: a {Red Potion} + Cryptic: + Text: a {health refill} + +Blue Potion Shop: + Standard: + Text: Blue Potion Shop + Pretty: + Text: a {Blue Potion} + Cryptic: + Text: a {blue health refill} + +Bottle with Half Milk: + Standard: + Text: Bottle with Half Milk + Pretty: + Text: a {Bottle with Half Milk} + Cryptic: + Text: a {baby bottle} + +Fairy Tears: + Standard: + Text: Fairy Tears + Pretty: + Text: some {Fairy Tears} + Plurality: Plural + Cryptic: + Text: a {refill of great power} + +Bottle with Great Fairies Tears: + Standard: + Text: Bottle with Great Fairies Tears + Pretty: + Text: a {Bottle with Great Fairies Tears} + Cryptic: + Text: a {bottle of great power} + +Renados Letter: + Standard: + Text: Renados Letter + Pretty: + Text: "{Renado's Letter}" + Cryptic: + Text: a {letter from a concerned shaman} + +Invoice: + Standard: + Text: Invoice + Pretty: + Text: the {Invoice} + Cryptic: + Text: the {bill for the doctor} + +Wooden Statue: + Standard: + Text: Wooden Statue + Pretty: + Text: the {Wooden Statue} + Cryptic: + Text: "{memories of home}" + Plurality: Plural + +Ilias Charm: + Standard: + Text: Ilias Charm + Pretty: + Text: "{Ilias Charm}" + Cryptic: + Text: a {friend's item} + +Horse Call: + Standard: + Text: Horse Call + Pretty: + Text: the {Horse Call} + Cryptic: + Text: the {horse beckoner} + +Forest Temple Small Key: + Standard: + Text: Forest Temple Small Key + Pretty: + Text: a {Forest Temple Small Key} + Cryptic: + Text: a {key for a deep forest} + +Goron Mines Small Key: + Standard: + Text: Goron Mines Small Key + Pretty: + Text: a {Goron Mines Small Key} + Cryptic: + Text: a {key for a volcanic mine} + +Lakebed Temple Small Key: + Standard: + Text: Lakebed Temple Small Key + Pretty: + Text: a {Lakebed Temple Small Key} + Cryptic: + Text: a {key for an underground lake} + +Arbiters Grounds Small Key: + Standard: + Text: Arbiters Grounds Small Key + Pretty: + Text: an {Arbiters Grounds Small Key} + Cryptic: + Text: a {key for an ancient prison} + +Snowpeak Ruins Small Key: + Standard: + Text: Snowpeak Ruins Small Key + Pretty: + Text: a {Snowpeak Ruins Small Key} + Cryptic: + Text: a {key for a snowy mansion} + +Temple of Time Small Key: + Standard: + Text: Temple of Time Small Key + Pretty: + Text: a {Temple of Time Small Key} + Cryptic: + Text: a {key for the past} + +City in the Sky Small Key: + Standard: + Text: City in the Sky Small Key + Pretty: + Text: the {City in the Sky Small Key} + Cryptic: + Text: a {key for the skies above} + +Palace of Twilight Small Key: + Standard: + Text: Palace of Twilight Small Key + Pretty: + Text: a {Palace of Twilight Small Key} + Cryptic: + Text: a {key for a another realm} + +Hyrule Castle Small Key: + Standard: + Text: Hyrule Castle Small Key + Pretty: + Text: a {Hyrule Castle Small Key} + Cryptic: + Text: a {key for a kingdom's castle} + +Gerudo Desert Bulblin Camp Key: + Standard: + Text: Gerudo Bulblin Camp Small Key + Pretty: + Text: the {Gerudo Desert Bulblin Camp Key} + Cryptic: + Text: the {key for a desert tent} + +Lake Hylia Portal: + Standard: + Text: Lake Hylia Portal + Pretty: + Text: the {Lake Hylia Portal} + Cryptic: + Text: a {portal to a vast lake} + +Aurus Memo: + Standard: + Text: Aurus Memo + Pretty: + Text: "{Auru's Memo}" + Cryptic: + Text: a {friend's favor} + +Asheis Sketch: + Standard: + Text: Asheis Sketch + Pretty: + Text: "{Ashei's Sketch}" + Cryptic: + Text: a {sketch of a horrific beast} + +Forest Temple Big Key: + Standard: + Text: Forest Temple Big Key + Pretty: + Text: the {Forest Temple Big Key} + Cryptic: + Text: the {key to the twilit parasite} + +Lakebed Temple Big Key: + Standard: + Text: Lakebed Temple Big Key + Pretty: + Text: the {Lakebed Temple Big Key} + Cryptic: + Text: the {key to the twilit aquatic} + +Arbiters Grounds Big Key: + Standard: + Text: Arbiters Grounds Big Key + Pretty: + Text: the {Arbiters Grounds Big Key} + Cryptic: + Text: the {key to the twilit fossil} + +Temple of Time Big Key: + Standard: + Text: Temple of Time Big Key + Pretty: + Text: the {Temple of Time Big Key} + Cryptic: + Text: the {key to the twilit arachnid} + +City in the Sky Big Key: + Standard: + Text: City in the Sky Big Key + Pretty: + Text: the {City in the Sky Big Key} + Cryptic: + Text: the {key to the twilit dragon} + +Palace of Twilight Big Key: + Standard: + Text: Palace of Twilight Big Key + Pretty: + Text: the {Palace of Twilight Big Key} + Cryptic: + Text: the {key to the usurper king} + +Hyrule Castle Big Key: + Standard: + Text: Hyrule Castle Big Key + Pretty: + Text: a {Hyrule Castle Big Key} + Cryptic: + Text: the {key to the castle throne room} + +Forest Temple Compass: + Standard: + Text: Forest Temple Compass + Pretty: + Text: the {Forest Temple Compass} + Cryptic: + Text: the {pointer for a deep forest} + +Goron Mines Compass: + Standard: + Text: Goron Mines Compass + Pretty: + Text: the {Goron Mines Compass} + Cryptic: + Text: the {pointer for a volcano} + +Lakebed Temple Compass: + Standard: + Text: Lakebed Temple Compass + Pretty: + Text: the {Lakebed Temple Compass} + Cryptic: + Text: the {pointer for an underground lake} + +Bottle with Lantern Oil: + Standard: + Text: Bottle with Lantern Oil + Pretty: + Text: a {Bottle with Lantern Oil} + Cryptic: + Text: a {bottle with lighter fluid} + +Progressive Mirror Shard: + Standard: + Text: Progressive Mirror Shard + Pretty: + Text: a {Mirror Shard} + Cryptic: + Text: a {reflective shard of power} + +Arbiters Grounds Compass: + Standard: + Text: Arbiters Grounds Compass + Pretty: + Text: the {Arbiters Grounds Compass} + Cryptic: + Text: the {pointer for an ancient prison} + +Snowpeak Ruins Compass: + Standard: + Text: Snowpeak Ruins Compass + Pretty: + Text: the {Snowpeak Ruins Compass} + Cryptic: + Text: the {pointer for a snowy mansion} + +Temple of Time Compass: + Standard: + Text: Temple of Time Compass + Pretty: + Text: the {Temple of Time Compass} + Cryptic: + Text: the {pointer for the past} + +City in the Sky Compass: + Standard: + Text: City in the Sky Compass + Pretty: + Text: the {City in the Sky Compass} + Cryptic: + Text: the {pointer for the skies above} + +Palace of Twilight Compass: + Standard: + Text: Palace of Twilight Compass + Pretty: + Text: the {Palace of Twilight Compass} + Cryptic: + Text: the {pointer for another realm} + +Hyrule Castle Compass: + Standard: + Text: Hyrule Castle Compass + Pretty: + Text: a {Hyrule Castle Compass} + Cryptic: + Text: the {pointer for the kingdom's castle} + +Mirror Chamber Portal: + Standard: + Text: Mirror Chamber Portal + Pretty: + Text: the {Mirror Chamber Portal} + Cryptic: + Text: a {portal to a coliseum} + +Snowpeak Portal: + Standard: + Text: Snowpeak Portal + Pretty: + Text: the {Snowpeak Portal} + Cryptic: + Text: a {portal to a snowy mountain} + +Forest Temple Dungeon Map: + Standard: + Text: Forest Temple Dungeon Map + Pretty: + Text: the {Forest Temple Dungeon Map} + Cryptic: + Text: the {map for a deep forest} + +Goron Mines Dungeon Map: + Standard: + Text: Goron Mines Dungeon Map + Pretty: + Text: the {Goron Mines Dungeon Map} + Cryptic: + Text: the {map for a volcano} + +Lakebed Temple Dungeon Map: + Standard: + Text: Lakebed Temple Dungeon Map + Pretty: + Text: the {Lakebed Temple Dungeon Map} + Cryptic: + Text: the {map for an underground lake} + +Arbiters Grounds Dungeon Map: + Standard: + Text: Arbiters Grounds Dungeon Map + Pretty: + Text: the {Arbiters Grounds Dungeon Map} + Cryptic: + Text: the {map for an ancient prison} + +Snowpeak Ruins Dungeon Map: + Standard: + Text: Snowpeak Ruins Dungeon Map + Pretty: + Text: the {Snowpeak Ruins Dungeon Map} + Cryptic: + Text: the {map for a snowy mansion} + +Temple of Time Dungeon Map: + Standard: + Text: Temple of Time Dungeon Map + Pretty: + Text: the {Temple of Time Dungeon Map} + Cryptic: + Text: the {map for the past} + +City in the Sky Dungeon Map: + Standard: + Text: City in the Sky Dungeon Map + Pretty: + Text: the {City in the Sky Dungeon Map} + Cryptic: + Text: the {map for the skies above} + +Palace of Twilight Dungeon Map: + Standard: + Text: Palace of Twilight Dungeon Map + Pretty: + Text: the {Palace of Twilight Dungeon Map} + Cryptic: + Text: the {map for another realm} + +Hyrule Castle Dungeon Map: + Standard: + Text: Hyrule Castle Dungeon Map + Pretty: + Text: a {Hyrule Castle Dungeon Map} + Cryptic: + Text: the {map for the kingdom's castle} + +Sacred Grove Portal: + Standard: + Text: Sacred Grove Portal + Pretty: + Text: the {Sacred Grove Portal} + Cryptic: + Text: a {portal to an ancient forest} + +Male Beetle: + Standard: + Text: Male Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Female Beetle: + Standard: + Text: Female Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Male Butterfly: + Standard: + Text: Male Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Female Butterfly: + Standard: + Text: Female Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Male Stag Beetle: + Standard: + Text: Male Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Female Stag Beetle: + Standard: + Text: Female Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Male Grasshopper: + Standard: + Text: Male Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Female Grasshopper: + Standard: + Text: Female Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Male Phasmid: + Standard: + Text: Male Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Female Phasmid: + Standard: + Text: Female Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Male Pill Bug: + Standard: + Text: Male Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Female Pill Bug: + Standard: + Text: Female Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Male Mantis: + Standard: + Text: Male Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Female Mantis: + Standard: + Text: Female Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Male Ladybug: + Standard: + Text: Male Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Female Ladybug: + Standard: + Text: Female Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Male Snail: + Standard: + Text: Male Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Female Snail: + Standard: + Text: Female Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Male Dragonfly: + Standard: + Text: Male Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Female Dragonfly: + Standard: + Text: Female Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Male Ant: + Standard: + Text: Male Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Female Ant: + Standard: + Text: Female Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Male Dayfly: + Standard: + Text: Male Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Female Dayfly: + Standard: + Text: Female Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Progressive Fused Shadow: + Standard: + Text: Progressive Fused Shadow + Pretty: + Text: a {Fused Shadow} + Cryptic: + Text: a {shadow of ultimate power} + +Poe Soul: + Standard: + Text: Poe Soul + Pretty: + Text: a {Poe Soul} + Cryptic: + Text: a {soul of the dead} + +Progressive Hidden Skill: + Standard: + Text: Progressive Hidden Skill + Pretty: + Text: a {Hidden Skill} + Cryptic: + Text: a {forgotten technique} + + +Bridge of Eldin Portal: + Standard: + Text: Bridge of Eldin Portal + Pretty: + Text: the {Bridge of Eldin Portal} + Cryptic: + Text: a {portal to a long bridge} + +Progressive Sky Book: + Standard: + Text: Progressive Sky Book + Pretty: + Text: a {Sky Character} + Cryptic: + Text: a {glyph of the heavens} + +Purple Rupee Links House: + Standard: + Text: Purple Rupee Links House + Pretty: + Text: the {Purple Rupee from your basement} + Cryptic: + Text: "{your savings}" + Plurality: Plural + +North Faron Woods Gate Key: + Standard: + Text: North Faron Woods Gate Key + Pretty: + Text: the {North Faron Woods Gate Key} + Cryptic: + Text: a {key to a northern forest} + +Gate Keys: + Standard: + Text: Gate Keys + Pretty: + Text: the {Gate Keys} + Plurality: Plural + Cryptic: + Text: "{King Bulblin's keys}" + Plurality: Plural + +Ordon Pumpkin: + Standard: + Text: Ordon Pumpkin + Pretty: + Text: the {Ordon Pumpkin} + Cryptic: + Text: a {soup ingredient} + +Ordon Cheese: + Standard: + Text: Ordon Cheese + Pretty: + Text: some {Ordon Cheese} + Cryptic: + Text: a {soup ingredient} + +Snowpeak Ruins Bedroom Key: + Standard: + Text: Snowpeak Ruins Bedroom Key + Pretty: + Text: the {Snowpeak Ruins Bedroom Key} + Cryptic: + Text: the {key to a snowy bedroom} + +Goron Mines Key Shard: + Standard: + Text: Goron Mines Key Shard + Pretty: + Text: a {Goron Mines Key Shard} + Cryptic: + Text: "{one third of a key}" + +Coro Key: + Standard: + Text: Coro Key + Pretty: + Text: "{Coro's Key}" + Cryptic: + Text: a {key to a forest cave} + +Game Beatable: + Standard: + Text: Game Beatable + Pretty: + Text: "{Game Beatable}" + Cryptic: + Text: the {game-winning item} + +Hint: + Standard: + Text: Hint + Pretty: + Text: a {Hint} + Cryptic: + Text: a {piece of knowledge} + +Faron Twilight Tear: + Standard: + Text: Faron Twilight Tear + Pretty: + Text: a {Faron Twilight Tear} + Cryptic: + Text: a {tear of a forest spirit} + +Eldin Twilight Tear: + Standard: + Text: Eldin Twilight Tear + Pretty: + Text: an {Eldin Twilight Tear} + Cryptic: + Text: a {tear of a volcano spirit} + +Lanayru Twilight Tear: + Standard: + Text: Lanayru Twilight Tear + Pretty: + Text: a {Lanayru Twilight Tear} + Cryptic: + Text: a {tear of a lake spirit} + +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) + +# HINT REGION NAMES +Ordon: + Standard: + Text: Ordon + Pretty: + Text: "{Ordon}" + Cryptic: + Text: a {quaint village} + +Faron Woods: + Standard: + Text: Faron Woods + Pretty: + Text: "{Faron Woods}" + Cryptic: + Text: a {forest} + +Sacred Grove: + Standard: + Text: Sacred Grove + Pretty: + Text: the {Sacred Grove} + Cryptic: + Text: a {hidden grove} + +Faron Field: + Standard: + Text: Faron Field + Pretty: + Text: "{Faron Field}" + Cryptic: + Text: a {field near the forest} + +Kakariko Gorge: + Standard: + Text: Kakariko Gorge + Pretty: + Text: "{Kakariko Gorge}" + Cryptic: + Text: a {field with a large chasm} + +Kakariko Village: + Standard: + Text: Kakariko Village + Pretty: + Text: "{Kakariko Village}" + Cryptic: + Text: a {charming village} + +Kakariko Graveyard: + Standard: + Text: Kakariko Graveyard + Pretty: + Text: the {Kakariko Graveyard} + Cryptic: + Text: a {yard for the dead} + +Death Mountain: + Standard: + Text: Death Mountain + Pretty: + Text: "{Death Mountain}" + Cryptic: + Text: a {volcano path} + +Eldin Field: + Standard: + Text: Eldin Field + Pretty: + Text: "{Eldin Field}" + Cryptic: + Text: a {field near a volcano} + +North Eldin: + Standard: + Text: North Eldin + Pretty: + Text: "{North Eldin}" + Cryptic: + Text: a {narrow gray field} + +Hidden Village: + Standard: + Text: Hidden Village + Pretty: + Text: the {Hidden Village} + Cryptic: + Text: a {secluded settlement} + +Lanayru Field: + Standard: + Text: Lanayru Field + Pretty: + Text: "{Lanayru Field}" + Cryptic: + Text: a {field with a river} + +Beside Castle Town: + Standard: + Text: Beside Castle Town + Pretty: + Text: "{Beside Castle Town}" + Cryptic: + Text: a {field beside a city} + +Castle Town: + Standard: + Text: Castle Town + Pretty: + Text: "{Castle Town}" + Cryptic: + Text: a {city} + +South of Castle Town: + Standard: + Text: South of Castle Town + Pretty: + Text: "{South of Castle Town}" + Cryptic: + Text: a {field south of a city} + +Great Bridge of Hylia: + Standard: + Text: Great Bridge of Hylia + Pretty: + Text: the {Great Bridge of Hylia} + Cryptic: + Text: a {path along a great bridge} + +Lake Hylia: + Standard: + Text: Lake Hylia + Pretty: + Text: "{Lake Hylia}" + Cryptic: + Text: a {vast lake} + +Lanayru Spring: + Standard: + Text: Lanayru Spring + Pretty: + Text: the {Lanayru Spring} + Cryptic: + Text: a {cavernous spring} + +Upper Zoras River: + Standard: + Text: Upper Zoras River + Pretty: + Text: "{Upper Zoras River}" + Cryptic: + Text: a {fork in the river} + +Zoras Domain: + Standard: + Text: Zoras Domain + Pretty: + Text: "{Zoras Domain}" + Cryptic: + Text: the {home of a grand waterfall} + +South Gerudo Desert: + Standard: + Text: South Gerudo Desert + Pretty: + Text: "{South Gerudo Desert}" + Cryptic: + Text: the {southern desert} + +North Gerudo Desert: + Standard: + Text: North Gerudo Desert + Pretty: + Text: "{North Gerudo Desert}" + Cryptic: + Text: the {northern desert} + +Bublin Camp: + Standard: + Text: Bublin Camp + Pretty: + Text: "{Bublin Camp}" + Cryptic: + Text: a {camp of enemies} + +Mirror Chamber: + Standard: + Text: Mirror Chamber + Pretty: + Text: the {Mirror Chamber} + Cryptic: + Text: a {chamber of chains} + +Forest Temple: + Standard: + Text: Forest Temple + Pretty: + Text: the {Forest Temple} + Cryptic: + Text: a {deep forest} + +Goron Mines: + Standard: + Text: Goron Mines + Pretty: + Text: the {Goron Mines} + Cryptic: + Text: a {volcanic mine} + +Lakebed Temple: + Standard: + Text: Lakebed Temple + Pretty: + Text: the {Lakebed Temple} + Cryptic: + Text: an {underground lake} + +Arbiters Grounds: + Standard: + Text: Arbiters Grounds + Pretty: + Text: the {Arbiters Grounds} + Cryptic: + Text: an {ancient prison} + +Snowpeak Ruins: + Standard: + Text: Snowpeak Ruins + Pretty: + Text: the {Snowpeak Ruins} + Cryptic: + Text: a {snowy mansion} + +Temple of Time: + Standard: + Text: Temple of Time + Pretty: + Text: the {Temple of Time} + Cryptic: + Text: the {past} + +City in the Sky: + Standard: + Text: City in the Sky + Pretty: + Text: the {City in the Sky} + Cryptic: + Text: the {skies above} + +Palace of Twilight: + Standard: + Text: Palace of Twilight + Pretty: + Text: the {Palace of Twilight} + Cryptic: + Text: "{another realm}" + +Hyrule Castle: + Standard: + Text: Hyrule Castle + Pretty: + Text: the {Hyrule Castle} + Cryptic: + Text: the {kingdom's castle} + +# NO REQUIRED DUNGEON TEXT +No Required Dungeons Text: + Standard: + Text: No Required Dungeons + +# ITEM GET TEXT +Foolish Get Item Text: + Standard: + Text: |- + a cold wind blows... + +Shadow Crystal Get Item Text: + Standard: + Text: |- + Ora hai il Cristallo Maledetto! + Questa manifestazione dei + poterimalvagi di Zant ti permette + sitransformarti quando vuoi. + +Restored Dominion Rod Text: + Standard: + Text: |- + Power has been restored to + the Dominion Rod! Now it can + be used to imbue statues + with life in the present! + +Forest Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Forest Temple! + +Goron Mines Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Goron Mines! + +Lakebed Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Lakebed Temple! + +Arbiters Grounds Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Arbiter's Grounds! + +Snowpeak Ruins Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Snowpeak Ruins! + +Temple of Time Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Temple of Time! + +City in the Sky Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + City in the Sky! + +Palace of Twilight Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Palace of Twilight! + +Hyrule Castle Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Hyrule Castle! + +Bulblin Camp Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Bulblin Camp! + +Forest Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Forest Temple! + +Lakebed Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Lakebed Temple! + +Arbiters Grounds Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Arbiter's Grounds! + +Temple of Time Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Temple of Time! + +City in the Sky Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + City in the Sky! + +Palace of Twilight Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Palace of Twilight! + +Hyrule Castle Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Hyrule Castle! + +Forest Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Forest Temple! + +Goron Mines Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Goron Mines! + +Lakebed Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Lakebed Temple! + +Mirror Shard 2 Get item Text: + Standard: + Text: |- + You got the second shard of + the Mirror of Twilight! It + has a beautiful shine to it + and feels slightly cold... + +Mirror Shard 3 Get item Text: + Standard: + Text: |- + You got the third shard of + the Mirror of Twilight! It + is covered in dirt and + webs... + +Mirror Shard 4 Get item Text: + Standard: + Text: |- + You got the final shard of + the Mirror of Twilight! It + feels lighter than air... + +Arbiters Grounds Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Arbiter's Grounds! + +Snowpeak Ruins Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Snowpeak Ruins! + +Temple of Time Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Temple of Time! + +City in the Sky Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + City in the Sky! + +Palace of Twilight Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Palace of Twilight! + +Hyrule Castle Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Hyrule Castle! + +# +Forest Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Forest Temple! + +Goron Mines Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Goron Mines! + +Lakebed Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Lakebed Temple! + +Snowpeak Ruins Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Snowpeak Ruins! + +Arbiters Grounds Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Arbiter's Grounds! + +Temple of Time Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Temple of Time! + +City in the Sky Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + City in the Sky! + +Palace of Twilight Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Palace of Twilight! + +Hyrule Castle Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Hyrule Castle! + + +Fused Shadow 1 Get Item Text: + Standard: + Text: |- + You got a Fused Shadow! + It seems to have some moss + growing on it... + +Fused Shadow 2 Get Item Text: + Standard: + Text: |- + You got the second Fused + Shadow! It feels warm to + the touch... + +Fused Shadow 3 Get Item Text: + Standard: + Text: |- + You got the final Fused + Shadow! It feels wet and + smells like fish... + +Mirror Shard 1 Get Item Text: + Standard: + Text: |- + You got the first shard of + the Mirror of Twilight! It + is covered in sand... + +Poe Soul Get Item Text: + Standard: + Text: |- + You got a Poe's Soul! + You've collected {} so far. + +Ending Blow Get Item Text: + Standard: + Text: |- + You learned the Ending Blow! + +Shield Attack Get Item Text: + Standard: + Text: |- + You learned the Shield Attack! + +Back Slice Get Item Text: + Standard: + Text: |- + You learned the Back Slice! + +Helm Splitter Get Item Text: + Standard: + Text: |- + You learned the Helm Splitter! + +Mortal Draw Get Item Text: + Standard: + Text: |- + You learned the Mortal Draw! + +Jump Strike Get Item Text: + Standard: + Text: |- + You learned the Jump Strike! + +Great Spin Get Item Text: + Standard: + Text: |- + You learned the Great Spin! + +Partially Filled Sky Book Get Item Text: + Standard: + Text: |- + You got a Sky Character! + You've collected {} so far. + +Midna Call As Human Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into wolf + <2 way choice 2>Something else + +Midna Call As Wolf Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into human + <2 way choice 2>Something else + +Midna Call As Wolf No Shadow Crystal Two Choice: + Standard: + Text: |- + <2 way choice 1>Warp + <2 way choice 2>Something else + +Midna Call As Human Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into wolf + <3 way choice 2>Warp + <3 way choice 3>Something else + +Midna Call As Wolf Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into human + <3 way choice 2>Warp + <3 way choice 3>Something else + +Slingshot Shop Text Template: + Standard: + Text: |- + : 30 Rupees +# I got this in for the kids. It's just a +# toy, but it stings something AWFUL +# when you get hit by it! + +Slingshot Shop Too Expensive Text Template: + Standard: + Text: |- + is 30 Rupees. If you want it, bring some money with you, all right, m'dear? + +Slingshot Shop Purchase Confirmation Text Template: + Standard: + Text: |- + is 30 Rupees. Do you want to buy it, m'dear? + +Slingshot Shop After Purchase Text Template: + Standard: + Text: |- + What are you doing buying , you naughty thing? You're too old for toys! Will you at least let the kids play with it? + +Barnes Special Offer Text Template: + Standard: + Text: |- + I've got a special offer goin' right now: , just 120 Rupees! How 'bout that? + +Kakariko Malo Mart Wooden Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 50 Rupees. Want one or not? + +Kakariko Malo Mart Wooden Shield Too Expensive Text Template: + Standard: + Text: |- + will cost you 50 Rupees, but you can't afford it. Don't expect a discount just because we're from the same town. + +Kakariko Malo Mart Hylian Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 200 Rupees. Want one or not? + +Kakariko Malo Mart Hylian Shield Too Expensive Text Template: + Standard: + Text: |- + will run you 200 Rupees...but if you have that much, I'll eat my hat. And I don't even HAVE a hat. + +Kakariko Malo Mart Hylian Shield After Purchase Text Template: + Standard: + Text: |- + Well, you bought my last ... so you'd better take good care of it. + +Kakariko Malo Mart Hawkeye Purchase Confirmation Text Template: + Standard: + Text: |- + is 100 Rupees. You want it or not? + +Kakariko Malo Mart Hawkeye Too Expensive Text Template: + Standard: + Text: |- + costs 100 Rupees... but there are people with enough Rupees, and then there's you. The guy with not enough. + +Kakariko Malo Mart Hawkeye After Purchase Text Template: + Standard: + Text: |- + You bought my last ... + +Kakariko Malo Mart Red Potion Too Expensive Text Template: + Standard: + Text: |- + will cost you 30 Rupees, but I won't be donating it to the poor, sorry. + +Kakariko Malo Mart Red Potion Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 30 Rupees. Want some or not? + +Kakariko Malo Mart Red Potion Text Template: + Standard: + Text: |- + : 30 Rupees +# This potion replenishes your +# life energy. Keep it in an empty +# bottle. + +Kakariko Malo Mart Hawkeye Coming Soon Text Template: + Standard: + Text: |- + : COMING SOON + +Kakariko Malo Mart Hawkeye Text Template: + Standard: + Text: |- + : 100 Rupees +# This eyewear allows you to see +# distant objects as if with the eyes +# of a hawk. + +Kakariko Malo Mart Sold Out Text: + Standard: + Text: SOLD OUT + +Kakariko Malo Mart Wooden Shield Text Template: + Standard: + Text: |- + : 50 Rupees +# This is a simple shield. It's made of +# wood, so it will burn away if +# touched by fire. + +Kakariko Malo Mart Hylian Shield Text Template: + Standard: + Text: |- + : 200 Rupees +# LIMITED SUPPLY! +# Don't let them sell out before you +# buy one! + +Chudleys Shop Magic Armor Text Template: + Standard: + Text: |- + + Only for the richest and most + precious customers who value their + lives over their Rupees. + +Castle Town Malo Mart Magic Armor After Purchase Text Template: + Standard: + Text: |- + We have sold out of ! + +Castle Town Malo Mart Magic Armor Text Template: + Standard: + Text: |- + !Special! 598 Rupees +# This is quite a bargain when you +# think of how valuable your life is. +# What's a few Rupees to stay alive? + +Castle Town Malo Mart Magic Armor Sold Out Text Template: + Standard: + Text: |- + + -SOLD OUT- + *This item has been discontinued. + +Charlo Donation Choice Text: + Standard: + Text: |- + <3 way choice 1>100 Rupees + <3 way choice 2>50 Rupees + <3 way choice 3>Sorry... + +Charlo Donation Ask Text Template: + Standard: + Text: |- + For ... + Would you please make a donation? + +Coro Bottle Offer 1 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Coro Bottle Offer 2 Text Template: + Standard: + Text: |- + I have a special, one-time offer of + for only 100 Rupees. How 'bout it, guy? + +Coro Bottle Offer 3 Text Template: + Standard: + Text: |- + Right now we have a 100-Rupee + and 20-Rupee refills to choose from! + +Coro Bottle Offer 4 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Fishing Hole Sign Text Template: + Standard: + Text: |- + DON'T LITTER! + Do NOT toss empty bottles or + here! The fish are CRYING! + + Keep the fishing hole clean! + +Custom Midna Call Need Something Text: + Standard: + Text: |- + Need Something? + +Custom Midna Call 3 Choice Text: + Standard: + Text: |- + <3 way choice 1>Hints + <3 way choice 2>Change time of day + <3 way choice 3>Return to spawn + +Custom Midna Call 2 Choice Text: + Standard: + Text: |- + <2 way choice 1>Hints + <2 way choice 2>Return to spawn + +Custom Midna Call Hints Text: + Standard: + Text: |- + I have no hints to give. + +Return to Spawn Dungeon Intro Text: + Standard: + Text: |- + I'll get you out of here. + Where do you want to go? + +Return to Spawn Dungeon Choice Text: + Standard: + Text: |- + <3 way choice 1>Dungeon entrance + <3 way choice 2>Nevermind + <3 way choice 3>Spawn + +Return to Spawn Dungeon No Choice Text: + Standard: + Text: |- + <2 way choice 1>Nevermind + <2 way choice 2>Spawn + +Midna Hints Required Dungeons Intro Zero Dungeons: + Standard: + Text: |- + There are 0 required dungeons. + +Midna Hints Required Dungeons Intro At Least One Dungeon: + Standard: + Text: |- + There are required dungeons: + +Ordon Hint Sign Text: + Standard: + Text: |- + Ordon Hint Sign. + There are no hints placed here. + +South Faron Woods Hint Sign Text: + Standard: + Text: |- + South Faron Woods Hint Sign. + There are no hints placed here. + +Sacred Grove Hint Sign Text: + Standard: + Text: |- + Sacred Grove Hint Sign. + There are no hints placed here. + +Faron Field Hint Sign Text: + Standard: + Text: |- + Faron Field Hint Sign. + There are no hints placed here. + +Kakariko Gorge Hint Sign Text: + Standard: + Text: |- + Kakariko Gorge Hint Sign. + There are no hints placed here. + +Kakariko Village Hint Sign Text: + Standard: + Text: |- + Kakariko Village Hint Sign. + There are no hints placed here. + +Kakariko Graveyard Hint Sign Text: + Standard: + Text: |- + Kakariko Graveyard Hint Sign. + There are no hints placed here. + +Eldin Field Hint Sign Text: + Standard: + Text: |- + Eldin Field Hint Sign. + There are no hints placed here. + +North Eldin Field Hint Sign Text: + Standard: + Text: |- + North Eldin Field Hint Sign. + There are no hints placed here. + +Hidden Village Hint Sign Text: + Standard: + Text: |- + Hidden Village Hint Sign. + There are no hints placed here. + +Lanayru Field Hint Sign Text: + Standard: + Text: |- + Lanayru Field Hint Sign. + There are no hints placed here. + +Beside Castle Town Hint Sign Text: + Standard: + Text: |- + Beside Castle Town Hint Sign. + There are no hints placed here. + +Castle Town Center Hint Sign Text: + Standard: + Text: |- + Castle Town Center Hint Sign. + There are no hints placed here. + +Outside South Castle Town Hint Sign Text: + Standard: + Text: |- + Outside South Castle Town Hint Sign. + There are no hints placed here. + +Lake Hylia Bridge Hint Sign Text: + Standard: + Text: |- + Lake Hylia Bridge Hint Sign. + There are no hints placed here. + +Lake Hylia Hint Sign Text: + Standard: + Text: |- + Lake Hylia Hint Sign. + There are no hints placed here. + +Lanayru Spring Hint Sign Text: + Standard: + Text: |- + Lanayru Spring Hint Sign. + There are no hints placed here. + +Lake Lantern Cave Hint Sign Text: + Standard: + Text: |- + Lake Lantern Cave Hint Sign. + There are no hints placed here. + +Fishing Hole Hint Sign Text: + Standard: + Text: |- + Fishing Hole Hint Sign. + There are no hints placed here. + +Zoras Domain Hint Sign Text: + Standard: + Text: |- + Zoras Domain Hint Sign. + There are no hints placed here. + +Snowpeak Hint Sign Text: + Standard: + Text: |- + Snowpeak Hint Sign. + There are no hints placed here. + +Gerudo Desert Hint Sign Text: + Standard: + Text: |- + Gerudo Desert Hint Sign. + There are no hints placed here. + +Bulblin Camp Hint Sign Text: + Standard: + Text: |- + Bulblin Camp Hint Sign. + There are no hints placed here. + +Forest Temple Hint Sign Text: + Standard: + Text: |- + Forest Temple Hint Sign. + There are no hints placed here. + +Goron Mines Hint Sign Text: + Standard: + Text: |- + Goron Mines Hint Sign. + There are no hints placed here. + +Lakebed Temple Hint Sign Text: + Standard: + Text: |- + Lakebed Temple Hint Sign. + There are no hints placed here. + +Arbiters Grounds Hint Sign Text: + Standard: + Text: |- + Arbiters Grounds Hint Sign. + There are no hints placed here. + +Snowpeak Ruins Hint Sign Text: + Standard: + Text: |- + Snowpeak Ruins Hint Sign. + There are no hints placed here. + +Temple of Time First Hint Sign Text: + Standard: + Text: |- + Temple of Time First Hint Sign. + There are no hints placed here. + +Temple of Time Second Hint Sign Text: + Standard: + Text: |- + Temple of Time Second Hint Sign. + There are no hints placed here. + +City in the Sky Hint Sign Text: + Standard: + Text: |- + City in the Sky Hint Sign. + There are no hints placed here. + +Palace of Twilight Hint Sign Text: + Standard: + Text: |- + Palace of Twilight Hint Sign. + There are no hints placed here. + +Hyrule Castle Hint Sign Text: + Standard: + Text: |- + Hyrule Castle Hint Sign. + There are no hints placed here. + +Cave of Ordeals Hint Sign Text: + Standard: + Text: |- + Cave of Ordeals Hint Sign. + There are no hints placed here. diff --git a/mods/randomizer/generator/data/text/languages/spanish.yaml b/mods/randomizer/generator/data/text/languages/spanish.yaml new file mode 100644 index 0000000000..8bd54d7cc0 --- /dev/null +++ b/mods/randomizer/generator/data/text/languages/spanish.yaml @@ -0,0 +1,2417 @@ +# This file contains all custom Spanish text for the dusklight randomizer + +# NOTES FOR TRANSLATORS: +# - You should only be translating the "Text" fields for each element in this file. Do not translate the +# - Text being surrounded by braces '{}' means that the text will be colored. If a text field begins with a brace, +# the entire field must be surrounded with quotation marks. +# - Below each text element, you can specify a given text's gender and/or plurality. If you need additional +# specifiers for pieces of text, let us know. If no gender is provided, the assumption is no gender. If no +# plurality is provided, the assumed plurality is singular. + +# ITEM NAMES +Green Rupee: + Standard: + Text: Green Rupee + Pretty: + Text: a {Green Rupee} + Cryptic: + Text: a {penny} + +Blue Rupee: + Standard: + Text: Blue Rupee + Pretty: + Text: a {Blue Rupee} + Cryptic: + Text: a {fiver} + +Yellow Rupee: + Standard: + Text: Yellow Rupee + Pretty: + Text: a {Yellow Rupee} + Cryptic: + Text: some {change} + +Red Rupee: + Standard: + Text: Red Rupee + Pretty: + Text: a {Red Rupee} + Cryptic: + Text: "{couch cash}" + +Purple Rupee: + Standard: + Text: Purple Rupee + Pretty: + Text: a {Purple Rupee} + Cryptic: + Text: a {good sum} + +Orange Rupee: + Standard: + Text: Orange Rupee + Pretty: + Text: an {Orange Rupee} + Cryptic: + Text: a {payday} + +Silver Rupee: + Standard: + Text: Silver Rupee + Pretty: + Text: a {Silver Rupee} + Cryptic: + Text: "{many riches}" + +Bombs 5: + Standard: + Text: Bombs 5 + Pretty: + Text: "{Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 10: + Standard: + Text: Bombs 10 + Pretty: + Text: "{Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 20: + Standard: + Text: Bombs 20 + Pretty: + Text: "{Bombs (20)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Bombs 30: + Standard: + Text: Bombs 30 + Pretty: + Text: "{Bombs (30)}" + Plurality: Plural + Cryptic: + Text: some {regular explosives} + Plurality: Plural + +Arrows 10: + Standard: + Text: Arrows 10 + Pretty: + Text: "{Arrows (10)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 20: + Standard: + Text: Arrows 20 + Pretty: + Text: "{Arrows (20)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Arrows 30: + Standard: + Text: Arrows 30 + Pretty: + Text: "{Arrows (30)}" + Plurality: Plural + Cryptic: + Text: some {thin ammo} + +Seeds 50: + Standard: + Text: Seeds 50 + Pretty: + Text: "{Seeds (50)}" + Plurality: Plural + Cryptic: + Text: some {pellets} + Plurality: Plural + +Foolish Item: + Standard: + Text: Foolish Item + Pretty: + Text: a {Foolish Item} + Cryptic: + Text: a {chilly surprise} + +Ordon Spring Portal: + Standard: + Text: Ordon Spring Portal + Pretty: + Text: the {Ordon Spring Portal} + Cryptic: + Text: a {portal to home} + +South Faron Portal: + Standard: + Text: South Faron Portal + Pretty: + Text: the {South Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Water Bombs 5: + Standard: + Text: Water Bombs 5 + Pretty: + Text: "{Water Bombs (5)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 10: + Standard: + Text: Water Bombs 10 + Pretty: + Text: "{Water Bombs (10)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Water Bombs 15: + Standard: + Text: Water Bombs 15 + Pretty: + Text: "{Water Bombs (15)}" + Plurality: Plural + Cryptic: + Text: some {water explosives} + Plurality: Plural + +Bomblings 5: + Standard: + Text: Bomblings 5 + Pretty: + Text: "{Bomblings (5)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Bomblings 10: + Standard: + Text: Bomblings 10 + Pretty: + Text: "{Bomblings (10)}" + Plurality: Plural + Cryptic: + Text: some {crawling explosives} + Plurality: Plural + +Piece of Heart: + Standard: + Text: Piece of Heart + Pretty: + Text: a {Piece of Heart} + Cryptic: + Text: some {love} + +Heart Container: + Standard: + Text: Heart Container + Pretty: + Text: a {Heart Container} + Cryptic: + Text: a {lot of love} + +Ordon Shield: + Standard: + Text: Ordon Shield + Pretty: + Text: the {Ordon Shield} + Cryptic: + Text: a {sturdy reminder of home} + +Wooden Shield: + Standard: + Text: Wooden Shield + Pretty: + Text: a {Wooden Shield} + Cryptic: + Text: a {wood protector} + +Hylian Shield: + Standard: + Text: Hylian Shield + Pretty: + Text: the {Hylian Shield} + Cryptic: + Text: an {unbreakable shield} + +Magic Armor: + Standard: + Text: Magic Armor + Pretty: + Text: the {Magic Armor} + Cryptic: + Text: "{magical clothing}" + +Zora Armor: + Standard: + Text: Zora Armor + Pretty: + Text: the {Zora Armor} + Cryptic: + Text: the {fish suit} + +Shadow Crystal: + Standard: + Text: Shadow Crystal + Pretty: + Text: the {Shadow Crystal} + Cryptic: + Text: a {crystal of dark power} + +Progressive Wallet: + Standard: + Text: Progressive Wallet + Pretty: + Text: a {Wallet} + Cryptic: + Text: a {money bag} + +Upper Zoras River Portal: + Standard: + Text: Upper Zoras River Portal + Pretty: + Text: the {Upper Zoras River Portal} + Cryptic: + Text: a {portal to some raging rapids} + +Castle Town Portal: + Standard: + Text: Castle Town Portal + Pretty: + Text: the {Castle Town Portal} + Cryptic: + Text: a {portal to the city} + +Gerudo Desert Portal: + Standard: + Text: Gerudo Desert Portal + Pretty: + Text: the {Gerudo Desert Portal} + Cryptic: + Text: a {portal to a challenging cave} + +North Faron Portal: + Standard: + Text: North Faron Portal + Pretty: + Text: the {North Faron Portal} + Cryptic: + Text: a {portal to a forest clearing} + +Hawkeye: + Standard: + Text: Hawkeye + Pretty: + Text: the {Hawkeye} + Cryptic: + Text: the {zoom-and-enhance} + +Progressive Sword: + Standard: + Text: Progressive Sword + Pretty: + Text: a {Sword} + Cryptic: + Text: a {sharp weapon} + +Gale Boomerang: + Standard: + Text: Gale Boomerang + Pretty: + Text: the {Gale Boomerang} + Cryptic: + Text: the {fairy of winds} + +Spinner: + Standard: + Text: Spinner + Pretty: + Text: the {Spinner} + Cryptic: + Text: the {gear rotator} + +Ball and Chain: + Standard: + Text: Ball and Chain + Pretty: + Text: the {Ball and Chain} + Cryptic: + Text: the {iron weight} + +Progressive Bow: + Standard: + Text: Progressive Bow + Pretty: + Text: a {Bow} + Cryptic: + Text: an {arrow launcher} + +Progressive Clawshot: + Standard: + Text: Progressive Clawshot + Pretty: + Text: a {Clawshot} + Cryptic: + Text: a {chain launcher} + +Iron Boots: + Standard: + Text: Iron Boots + Pretty: + Text: the {Iron Boots} + Plurality: Plural + Cryptic: + Text: the {heavy shoes} + Plurality: Plural + +Progressive Dominion Rod: + Standard: + Text: Dominion Rod + Pretty: + Text: a {Dominion Rod} + Cryptic: + Text: a {rod of control} + +Lantern: + Standard: + Text: Lantern + Pretty: + Text: the {Lantern} + Cryptic: + Text: the {small light} + +Progressive Fishing Rod: + Standard: + Text: Progressive Fishing Rod + Pretty: + Text: a {Fishing Rod} + Cryptic: + Text: a {rod of patience} + +Slingshot: + Standard: + Text: Slingshot + Pretty: + Text: the {Slingshot} + Cryptic: + Text: the {child's toy} + +Kakariko Gorge Portal: + Standard: + Text: Kakariko Gorge Portal + Pretty: + Text: the {Kakariko Gorge Portal} + Cryptic: + Text: a {portal to a big gap} + +Kakariko Village Portal: + Standard: + Text: Kakariko Village Portal + Pretty: + Text: the {Kakariko Village Portal} + Cryptic: + Text: a {portal to a village} + +Giant Bomb Bag: + Standard: + Text: Giant Bomb Bag + Pretty: + Text: a {Giant Bomb Bag} + Cryptic: + Text: an {explosive capacity upgrade} + +Bomb Bag: + Standard: + Text: Bomb Bag + Pretty: + Text: a {Bomb Bag} + Cryptic: + Text: a {bag for explosions} + +Death Mountain Portal: + Standard: + Text: Death Mountain Portal + Pretty: + Text: the {Death Mountain Portal} + Cryptic: + Text: a {portal to a volcano} + +Zoras Domain Portal: + Standard: + Text: Zoras Domain Portal + Pretty: + Text: the {Zora's Domain Portal} + Cryptic: + Text: a {portal to water} + +Empty Bottle: + Standard: + Text: Empty Bottle + Pretty: + Text: an {Empty Bottle} + Cryptic: + Text: + +Red Potion Shop: + Standard: + Text: Red Potion Shop + Pretty: + Text: a {Red Potion} + Cryptic: + Text: a {health refill} + +Blue Potion Shop: + Standard: + Text: Blue Potion Shop + Pretty: + Text: a {Blue Potion} + Cryptic: + Text: a {blue health refill} + +Bottle with Half Milk: + Standard: + Text: Bottle with Half Milk + Pretty: + Text: a {Bottle with Half Milk} + Cryptic: + Text: a {baby bottle} + +Fairy Tears: + Standard: + Text: Fairy Tears + Pretty: + Text: some {Fairy Tears} + Plurality: Plural + Cryptic: + Text: a {refill of great power} + +Bottle with Great Fairies Tears: + Standard: + Text: Bottle with Great Fairies Tears + Pretty: + Text: a {Bottle with Great Fairies Tears} + Cryptic: + Text: a {bottle of great power} + +Renados Letter: + Standard: + Text: Renados Letter + Pretty: + Text: "{Renado's Letter}" + Cryptic: + Text: a {letter from a concerned shaman} + +Invoice: + Standard: + Text: Invoice + Pretty: + Text: the {Invoice} + Cryptic: + Text: the {bill for the doctor} + +Wooden Statue: + Standard: + Text: Wooden Statue + Pretty: + Text: the {Wooden Statue} + Cryptic: + Text: "{memories of home}" + Plurality: Plural + +Ilias Charm: + Standard: + Text: Ilias Charm + Pretty: + Text: "{Ilias Charm}" + Cryptic: + Text: a {friend's item} + +Horse Call: + Standard: + Text: Horse Call + Pretty: + Text: the {Horse Call} + Cryptic: + Text: the {horse beckoner} + +Forest Temple Small Key: + Standard: + Text: Forest Temple Small Key + Pretty: + Text: a {Forest Temple Small Key} + Cryptic: + Text: a {key for a deep forest} + +Goron Mines Small Key: + Standard: + Text: Goron Mines Small Key + Pretty: + Text: a {Goron Mines Small Key} + Cryptic: + Text: a {key for a volcanic mine} + +Lakebed Temple Small Key: + Standard: + Text: Lakebed Temple Small Key + Pretty: + Text: a {Lakebed Temple Small Key} + Cryptic: + Text: a {key for an underground lake} + +Arbiters Grounds Small Key: + Standard: + Text: Arbiters Grounds Small Key + Pretty: + Text: an {Arbiters Grounds Small Key} + Cryptic: + Text: a {key for an ancient prison} + +Snowpeak Ruins Small Key: + Standard: + Text: Snowpeak Ruins Small Key + Pretty: + Text: a {Snowpeak Ruins Small Key} + Cryptic: + Text: a {key for a snowy mansion} + +Temple of Time Small Key: + Standard: + Text: Temple of Time Small Key + Pretty: + Text: a {Temple of Time Small Key} + Cryptic: + Text: a {key for the past} + +City in the Sky Small Key: + Standard: + Text: City in the Sky Small Key + Pretty: + Text: the {City in the Sky Small Key} + Cryptic: + Text: a {key for the skies above} + +Palace of Twilight Small Key: + Standard: + Text: Palace of Twilight Small Key + Pretty: + Text: a {Palace of Twilight Small Key} + Cryptic: + Text: a {key for a another realm} + +Hyrule Castle Small Key: + Standard: + Text: Hyrule Castle Small Key + Pretty: + Text: a {Hyrule Castle Small Key} + Cryptic: + Text: a {key for a kingdom's castle} + +Gerudo Desert Bulblin Camp Key: + Standard: + Text: Gerudo Bulblin Camp Small Key + Pretty: + Text: the {Gerudo Desert Bulblin Camp Key} + Cryptic: + Text: the {key for a desert tent} + +Lake Hylia Portal: + Standard: + Text: Lake Hylia Portal + Pretty: + Text: the {Lake Hylia Portal} + Cryptic: + Text: a {portal to a vast lake} + +Aurus Memo: + Standard: + Text: Aurus Memo + Pretty: + Text: "{Auru's Memo}" + Cryptic: + Text: a {friend's favor} + +Asheis Sketch: + Standard: + Text: Asheis Sketch + Pretty: + Text: "{Ashei's Sketch}" + Cryptic: + Text: a {sketch of a horrific beast} + +Forest Temple Big Key: + Standard: + Text: Forest Temple Big Key + Pretty: + Text: the {Forest Temple Big Key} + Cryptic: + Text: the {key to the twilit parasite} + +Lakebed Temple Big Key: + Standard: + Text: Lakebed Temple Big Key + Pretty: + Text: the {Lakebed Temple Big Key} + Cryptic: + Text: the {key to the twilit aquatic} + +Arbiters Grounds Big Key: + Standard: + Text: Arbiters Grounds Big Key + Pretty: + Text: the {Arbiters Grounds Big Key} + Cryptic: + Text: the {key to the twilit fossil} + +Temple of Time Big Key: + Standard: + Text: Temple of Time Big Key + Pretty: + Text: the {Temple of Time Big Key} + Cryptic: + Text: the {key to the twilit arachnid} + +City in the Sky Big Key: + Standard: + Text: City in the Sky Big Key + Pretty: + Text: the {City in the Sky Big Key} + Cryptic: + Text: the {key to the twilit dragon} + +Palace of Twilight Big Key: + Standard: + Text: Palace of Twilight Big Key + Pretty: + Text: the {Palace of Twilight Big Key} + Cryptic: + Text: the {key to the usurper king} + +Hyrule Castle Big Key: + Standard: + Text: Hyrule Castle Big Key + Pretty: + Text: a {Hyrule Castle Big Key} + Cryptic: + Text: the {key to the castle throne room} + +Forest Temple Compass: + Standard: + Text: Forest Temple Compass + Pretty: + Text: the {Forest Temple Compass} + Cryptic: + Text: the {pointer for a deep forest} + +Goron Mines Compass: + Standard: + Text: Goron Mines Compass + Pretty: + Text: the {Goron Mines Compass} + Cryptic: + Text: the {pointer for a volcano} + +Lakebed Temple Compass: + Standard: + Text: Lakebed Temple Compass + Pretty: + Text: the {Lakebed Temple Compass} + Cryptic: + Text: the {pointer for an underground lake} + +Bottle with Lantern Oil: + Standard: + Text: Bottle with Lantern Oil + Pretty: + Text: a {Bottle with Lantern Oil} + Cryptic: + Text: a {bottle with lighter fluid} + +Progressive Mirror Shard: + Standard: + Text: Progressive Mirror Shard + Pretty: + Text: a {Mirror Shard} + Cryptic: + Text: a {reflective shard of power} + +Arbiters Grounds Compass: + Standard: + Text: Arbiters Grounds Compass + Pretty: + Text: the {Arbiters Grounds Compass} + Cryptic: + Text: the {pointer for an ancient prison} + +Snowpeak Ruins Compass: + Standard: + Text: Snowpeak Ruins Compass + Pretty: + Text: the {Snowpeak Ruins Compass} + Cryptic: + Text: the {pointer for a snowy mansion} + +Temple of Time Compass: + Standard: + Text: Temple of Time Compass + Pretty: + Text: the {Temple of Time Compass} + Cryptic: + Text: the {pointer for the past} + +City in the Sky Compass: + Standard: + Text: City in the Sky Compass + Pretty: + Text: the {City in the Sky Compass} + Cryptic: + Text: the {pointer for the skies above} + +Palace of Twilight Compass: + Standard: + Text: Palace of Twilight Compass + Pretty: + Text: the {Palace of Twilight Compass} + Cryptic: + Text: the {pointer for another realm} + +Hyrule Castle Compass: + Standard: + Text: Hyrule Castle Compass + Pretty: + Text: a {Hyrule Castle Compass} + Cryptic: + Text: the {pointer for the kingdom's castle} + +Mirror Chamber Portal: + Standard: + Text: Mirror Chamber Portal + Pretty: + Text: the {Mirror Chamber Portal} + Cryptic: + Text: a {portal to a coliseum} + +Snowpeak Portal: + Standard: + Text: Snowpeak Portal + Pretty: + Text: the {Snowpeak Portal} + Cryptic: + Text: a {portal to a snowy mountain} + +Forest Temple Dungeon Map: + Standard: + Text: Forest Temple Dungeon Map + Pretty: + Text: the {Forest Temple Dungeon Map} + Cryptic: + Text: the {map for a deep forest} + +Goron Mines Dungeon Map: + Standard: + Text: Goron Mines Dungeon Map + Pretty: + Text: the {Goron Mines Dungeon Map} + Cryptic: + Text: the {map for a volcano} + +Lakebed Temple Dungeon Map: + Standard: + Text: Lakebed Temple Dungeon Map + Pretty: + Text: the {Lakebed Temple Dungeon Map} + Cryptic: + Text: the {map for an underground lake} + +Arbiters Grounds Dungeon Map: + Standard: + Text: Arbiters Grounds Dungeon Map + Pretty: + Text: the {Arbiters Grounds Dungeon Map} + Cryptic: + Text: the {map for an ancient prison} + +Snowpeak Ruins Dungeon Map: + Standard: + Text: Snowpeak Ruins Dungeon Map + Pretty: + Text: the {Snowpeak Ruins Dungeon Map} + Cryptic: + Text: the {map for a snowy mansion} + +Temple of Time Dungeon Map: + Standard: + Text: Temple of Time Dungeon Map + Pretty: + Text: the {Temple of Time Dungeon Map} + Cryptic: + Text: the {map for the past} + +City in the Sky Dungeon Map: + Standard: + Text: City in the Sky Dungeon Map + Pretty: + Text: the {City in the Sky Dungeon Map} + Cryptic: + Text: the {map for the skies above} + +Palace of Twilight Dungeon Map: + Standard: + Text: Palace of Twilight Dungeon Map + Pretty: + Text: the {Palace of Twilight Dungeon Map} + Cryptic: + Text: the {map for another realm} + +Hyrule Castle Dungeon Map: + Standard: + Text: Hyrule Castle Dungeon Map + Pretty: + Text: a {Hyrule Castle Dungeon Map} + Cryptic: + Text: the {map for the kingdom's castle} + +Sacred Grove Portal: + Standard: + Text: Sacred Grove Portal + Pretty: + Text: the {Sacred Grove Portal} + Cryptic: + Text: a {portal to an ancient forest} + +Male Beetle: + Standard: + Text: Male Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Female Beetle: + Standard: + Text: Female Beetle + Pretty: + Text: the {Beetle} + Cryptic: + Text: a {shiny insect} + +Male Butterfly: + Standard: + Text: Male Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Female Butterfly: + Standard: + Text: Female Butterfly + Pretty: + Text: the {Butterfly} + Cryptic: + Text: a {shiny insect} + +Male Stag Beetle: + Standard: + Text: Male Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Female Stag Beetle: + Standard: + Text: Female Stag Beetle + Pretty: + Text: the {Stag Beetle} + Cryptic: + Text: a {shiny insect} + +Male Grasshopper: + Standard: + Text: Male Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Female Grasshopper: + Standard: + Text: Female Grasshopper + Pretty: + Text: the {Grasshopper} + Cryptic: + Text: a {shiny insect} + +Male Phasmid: + Standard: + Text: Male Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Female Phasmid: + Standard: + Text: Female Phasmid + Pretty: + Text: the {Phasmid} + Cryptic: + Text: a {shiny insect} + +Male Pill Bug: + Standard: + Text: Male Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Female Pill Bug: + Standard: + Text: Female Pill Bug + Pretty: + Text: the {Pill Bug} + Cryptic: + Text: a {shiny insect} + +Male Mantis: + Standard: + Text: Male Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Female Mantis: + Standard: + Text: Female Mantis + Pretty: + Text: the {Mantis} + Cryptic: + Text: a {shiny insect} + +Male Ladybug: + Standard: + Text: Male Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Female Ladybug: + Standard: + Text: Female Ladybug + Pretty: + Text: the {Ladybug} + Cryptic: + Text: a {shiny insect} + +Male Snail: + Standard: + Text: Male Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Female Snail: + Standard: + Text: Female Snail + Pretty: + Text: the {Snail} + Cryptic: + Text: a {shiny insect} + +Male Dragonfly: + Standard: + Text: Male Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Female Dragonfly: + Standard: + Text: Female Dragonfly + Pretty: + Text: the {Dragonfly} + Cryptic: + Text: a {shiny insect} + +Male Ant: + Standard: + Text: Male Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Female Ant: + Standard: + Text: Female Ant + Pretty: + Text: the {Ant} + Cryptic: + Text: a {shiny insect} + +Male Dayfly: + Standard: + Text: Male Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Female Dayfly: + Standard: + Text: Female Dayfly + Pretty: + Text: the {Dayfly} + Cryptic: + Text: a {shiny insect} + +Progressive Fused Shadow: + Standard: + Text: Progressive Fused Shadow + Pretty: + Text: a {Fused Shadow} + Cryptic: + Text: a {shadow of ultimate power} + +Poe Soul: + Standard: + Text: Poe Soul + Pretty: + Text: a {Poe Soul} + Cryptic: + Text: a {soul of the dead} + +Progressive Hidden Skill: + Standard: + Text: Progressive Hidden Skill + Pretty: + Text: a {Hidden Skill} + Cryptic: + Text: a {forgotten technique} + + +Bridge of Eldin Portal: + Standard: + Text: Bridge of Eldin Portal + Pretty: + Text: the {Bridge of Eldin Portal} + Cryptic: + Text: a {portal to a long bridge} + +Progressive Sky Book: + Standard: + Text: Progressive Sky Book + Pretty: + Text: a {Sky Character} + Cryptic: + Text: a {glyph of the heavens} + +Purple Rupee Links House: + Standard: + Text: Purple Rupee Links House + Pretty: + Text: the {Purple Rupee from your basement} + Cryptic: + Text: "{your savings}" + Plurality: Plural + +North Faron Woods Gate Key: + Standard: + Text: North Faron Woods Gate Key + Pretty: + Text: the {North Faron Woods Gate Key} + Cryptic: + Text: a {key to a northern forest} + +Gate Keys: + Standard: + Text: Gate Keys + Pretty: + Text: the {Gate Keys} + Plurality: Plural + Cryptic: + Text: "{King Bulblin's keys}" + Plurality: Plural + +Ordon Pumpkin: + Standard: + Text: Ordon Pumpkin + Pretty: + Text: the {Ordon Pumpkin} + Cryptic: + Text: a {soup ingredient} + +Ordon Cheese: + Standard: + Text: Ordon Cheese + Pretty: + Text: some {Ordon Cheese} + Cryptic: + Text: a {soup ingredient} + +Snowpeak Ruins Bedroom Key: + Standard: + Text: Snowpeak Ruins Bedroom Key + Pretty: + Text: the {Snowpeak Ruins Bedroom Key} + Cryptic: + Text: the {key to a snowy bedroom} + +Goron Mines Key Shard: + Standard: + Text: Goron Mines Key Shard + Pretty: + Text: a {Goron Mines Key Shard} + Cryptic: + Text: "{one third of a key}" + +Coro Key: + Standard: + Text: Coro Key + Pretty: + Text: "{Coro's Key}" + Cryptic: + Text: a {key to a forest cave} + +Game Beatable: + Standard: + Text: Game Beatable + Pretty: + Text: "{Game Beatable}" + Cryptic: + Text: the {game-winning item} + +Hint: + Standard: + Text: Hint + Pretty: + Text: a {Hint} + Cryptic: + Text: a {piece of knowledge} + +Faron Twilight Tear: + Standard: + Text: Faron Twilight Tear + Pretty: + Text: a {Faron Twilight Tear} + Cryptic: + Text: a {tear of a forest spirit} + +Eldin Twilight Tear: + Standard: + Text: Eldin Twilight Tear + Pretty: + Text: an {Eldin Twilight Tear} + Cryptic: + Text: a {tear of a volcano spirit} + +Lanayru Twilight Tear: + Standard: + Text: Lanayru Twilight Tear + Pretty: + Text: a {Lanayru Twilight Tear} + Cryptic: + Text: a {tear of a lake spirit} + +# ITEM NAMES FOR PROGRESSIVE ITEMS +Progressive Wallet x0: + Standard: + Text: Small Wallet + +Progressive Wallet x1: + Standard: + Text: Large Wallet + +Progressive Wallet x2: + Standard: + Text: Giant's Wallet + +Progressive Sword x1: + Standard: + Text: Wooden Sword + +Progressive Sword x2: + Standard: + Text: Ordon Sword + +Progressive Sword x3: + Standard: + Text: Master Sword + +Progressive Sword x4: + Standard: + Text: Light Sword + +Progressive Bow x1: + Standard: + Text: Bow (30 Arrows) + +Progressive Bow x2: + Standard: + Text: Bow (60 Arrows) + +Progressive Bow x3: + Standard: + Text: Bow (100 Arrows) + +Progressive Clawshot x1: + Standard: + Text: Clawshot + +Progressive Clawshot x2: + Standard: + Text: Double Clawshots + +Progressive Dominion Rod x1: + Standard: + Text: Dominion Rod + +Progressive Dominion Rod x2: + Standard: + Text: Restored Dominion Rod + +Progressive Fishing Rod x1: + Standard: + Text: Fishing Rod + +Progressive Fishing Rod x2: + Standard: + Text: Corral Earring + +Progressive Sky Book x1: + Standard: + Text: Sky Book (0/6 Characters) + +Progressive Sky Book x2: + Standard: + Text: Sky Book (1/6 Characters) + +Progressive Sky Book x3: + Standard: + Text: Sky Book (2/6 Characters) + +Progressive Sky Book x4: + Standard: + Text: Sky Book (3/6 Characters) + +Progressive Sky Book x5: + Standard: + Text: Sky Book (4/6 Characters) + +Progressive Sky Book x6: + Standard: + Text: Sky Book (5/6 Characters) + +Progressive Sky Book x7: + Standard: + Text: Sky Book (6/6 Characters) + +# HINT REGION NAMES +Ordon: + Standard: + Text: Ordon + Pretty: + Text: "{Ordon}" + Cryptic: + Text: a {quaint village} + +Faron Woods: + Standard: + Text: Faron Woods + Pretty: + Text: "{Faron Woods}" + Cryptic: + Text: a {forest} + +Sacred Grove: + Standard: + Text: Sacred Grove + Pretty: + Text: the {Sacred Grove} + Cryptic: + Text: a {hidden grove} + +Faron Field: + Standard: + Text: Faron Field + Pretty: + Text: "{Faron Field}" + Cryptic: + Text: a {field near the forest} + +Kakariko Gorge: + Standard: + Text: Kakariko Gorge + Pretty: + Text: "{Kakariko Gorge}" + Cryptic: + Text: a {field with a large chasm} + +Kakariko Village: + Standard: + Text: Kakariko Village + Pretty: + Text: "{Kakariko Village}" + Cryptic: + Text: a {charming village} + +Kakariko Graveyard: + Standard: + Text: Kakariko Graveyard + Pretty: + Text: the {Kakariko Graveyard} + Cryptic: + Text: a {yard for the dead} + +Death Mountain: + Standard: + Text: Death Mountain + Pretty: + Text: "{Death Mountain}" + Cryptic: + Text: a {volcano path} + +Eldin Field: + Standard: + Text: Eldin Field + Pretty: + Text: "{Eldin Field}" + Cryptic: + Text: a {field near a volcano} + +North Eldin: + Standard: + Text: North Eldin + Pretty: + Text: "{North Eldin}" + Cryptic: + Text: a {narrow gray field} + +Hidden Village: + Standard: + Text: Hidden Village + Pretty: + Text: the {Hidden Village} + Cryptic: + Text: a {secluded settlement} + +Lanayru Field: + Standard: + Text: Lanayru Field + Pretty: + Text: "{Lanayru Field}" + Cryptic: + Text: a {field with a river} + +Beside Castle Town: + Standard: + Text: Beside Castle Town + Pretty: + Text: "{Beside Castle Town}" + Cryptic: + Text: a {field beside a city} + +Castle Town: + Standard: + Text: Castle Town + Pretty: + Text: "{Castle Town}" + Cryptic: + Text: a {city} + +South of Castle Town: + Standard: + Text: South of Castle Town + Pretty: + Text: "{South of Castle Town}" + Cryptic: + Text: a {field south of a city} + +Great Bridge of Hylia: + Standard: + Text: Great Bridge of Hylia + Pretty: + Text: the {Great Bridge of Hylia} + Cryptic: + Text: a {path along a great bridge} + +Lake Hylia: + Standard: + Text: Lake Hylia + Pretty: + Text: "{Lake Hylia}" + Cryptic: + Text: a {vast lake} + +Lanayru Spring: + Standard: + Text: Lanayru Spring + Pretty: + Text: the {Lanayru Spring} + Cryptic: + Text: a {cavernous spring} + +Upper Zoras River: + Standard: + Text: Upper Zoras River + Pretty: + Text: "{Upper Zoras River}" + Cryptic: + Text: a {fork in the river} + +Zoras Domain: + Standard: + Text: Zoras Domain + Pretty: + Text: "{Zoras Domain}" + Cryptic: + Text: the {home of a grand waterfall} + +South Gerudo Desert: + Standard: + Text: South Gerudo Desert + Pretty: + Text: "{South Gerudo Desert}" + Cryptic: + Text: the {southern desert} + +North Gerudo Desert: + Standard: + Text: North Gerudo Desert + Pretty: + Text: "{North Gerudo Desert}" + Cryptic: + Text: the {northern desert} + +Bublin Camp: + Standard: + Text: Bublin Camp + Pretty: + Text: "{Bublin Camp}" + Cryptic: + Text: a {camp of enemies} + +Mirror Chamber: + Standard: + Text: Mirror Chamber + Pretty: + Text: the {Mirror Chamber} + Cryptic: + Text: a {chamber of chains} + +Forest Temple: + Standard: + Text: Forest Temple + Pretty: + Text: the {Forest Temple} + Cryptic: + Text: a {deep forest} + +Goron Mines: + Standard: + Text: Goron Mines + Pretty: + Text: the {Goron Mines} + Cryptic: + Text: a {volcanic mine} + +Lakebed Temple: + Standard: + Text: Lakebed Temple + Pretty: + Text: the {Lakebed Temple} + Cryptic: + Text: an {underground lake} + +Arbiters Grounds: + Standard: + Text: Arbiters Grounds + Pretty: + Text: the {Arbiters Grounds} + Cryptic: + Text: an {ancient prison} + +Snowpeak Ruins: + Standard: + Text: Snowpeak Ruins + Pretty: + Text: the {Snowpeak Ruins} + Cryptic: + Text: a {snowy mansion} + +Temple of Time: + Standard: + Text: Temple of Time + Pretty: + Text: the {Temple of Time} + Cryptic: + Text: the {past} + +City in the Sky: + Standard: + Text: City in the Sky + Pretty: + Text: the {City in the Sky} + Cryptic: + Text: the {skies above} + +Palace of Twilight: + Standard: + Text: Palace of Twilight + Pretty: + Text: the {Palace of Twilight} + Cryptic: + Text: "{another realm}" + +Hyrule Castle: + Standard: + Text: Hyrule Castle + Pretty: + Text: the {Hyrule Castle} + Cryptic: + Text: the {kingdom's castle} + +# NO REQUIRED DUNGEON TEXT +No Required Dungeons Text: + Standard: + Text: No Required Dungeons + +# ITEM GET TEXT +Foolish Get Item Text: + Standard: + Text: |- + a cold wind blows... + +Shadow Crystal Get Item Text: + Standard: + Text: |- + ¡Has obtenido el Cristal Oscuro! + Esa manifestación maléfica del + poder de Zant te permite + transformarte cuando quieras! + +Restored Dominion Rod Text: + Standard: + Text: |- + Power has been restored to + the Dominion Rod! Now it can + be used to imbue statues + with life in the present! + +Forest Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Forest Temple! + +Goron Mines Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Goron Mines! + +Lakebed Temple Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Lakebed Temple! + +Arbiters Grounds Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Arbiter's Grounds! + +Snowpeak Ruins Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Snowpeak Ruins! + +Temple of Time Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Temple of Time! + +City in the Sky Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + City in the Sky! + +Palace of Twilight Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Palace of Twilight! + +Hyrule Castle Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for + Hyrule Castle! + +Bulblin Camp Small Key Get Item Text: + Standard: + Text: |- + You got a Small Key for the + Bulblin Camp! + +Forest Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Forest Temple! + +Lakebed Temple Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Lakebed Temple! + +Arbiters Grounds Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Arbiter's Grounds! + +Temple of Time Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Temple of Time! + +City in the Sky Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + City in the Sky! + +Palace of Twilight Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for the + Palace of Twilight! + +Hyrule Castle Big Key Get Item Text: + Standard: + Text: |- + You got the Big Key for + Hyrule Castle! + +Forest Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Forest Temple! + +Goron Mines Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Goron Mines! + +Lakebed Temple Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Lakebed Temple! + +Mirror Shard 2 Get item Text: + Standard: + Text: |- + You got the second shard of + the Mirror of Twilight! It + has a beautiful shine to it + and feels slightly cold... + +Mirror Shard 3 Get item Text: + Standard: + Text: |- + You got the third shard of + the Mirror of Twilight! It + is covered in dirt and + webs... + +Mirror Shard 4 Get item Text: + Standard: + Text: |- + You got the final shard of + the Mirror of Twilight! It + feels lighter than air... + +Arbiters Grounds Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Arbiter's Grounds! + +Snowpeak Ruins Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Snowpeak Ruins! + +Temple of Time Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Temple of Time! + +City in the Sky Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + City in the Sky! + +Palace of Twilight Compass Get Item Text: + Standard: + Text: |- + You got the Compass for the + Palace of Twilight! + +Hyrule Castle Compass Get Item Text: + Standard: + Text: |- + You got the Compass for + Hyrule Castle! + +# +Forest Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Forest Temple! + +Goron Mines Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Goron Mines! + +Lakebed Temple Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Lakebed Temple! + +Snowpeak Ruins Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Snowpeak Ruins! + +Arbiters Grounds Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Arbiter's Grounds! + +Temple of Time Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Temple of Time! + +City in the Sky Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + City in the Sky! + +Palace of Twilight Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for the + Palace of Twilight! + +Hyrule Castle Dungeon Map Get Item Text: + Standard: + Text: |- + You got the Dungeon Map for + Hyrule Castle! + + +Fused Shadow 1 Get Item Text: + Standard: + Text: |- + You got a Fused Shadow! + It seems to have some moss + growing on it... + +Fused Shadow 2 Get Item Text: + Standard: + Text: |- + You got the second Fused + Shadow! It feels warm to + the touch... + +Fused Shadow 3 Get Item Text: + Standard: + Text: |- + You got the final Fused + Shadow! It feels wet and + smells like fish... + +Mirror Shard 1 Get Item Text: + Standard: + Text: |- + You got the first shard of + the Mirror of Twilight! It + is covered in sand... + +Poe Soul Get Item Text: + Standard: + Text: |- + You got a Poe's Soul! + You've collected {} so far. + +Ending Blow Get Item Text: + Standard: + Text: |- + You learned the Ending Blow! + +Shield Attack Get Item Text: + Standard: + Text: |- + You learned the Shield Attack! + +Back Slice Get Item Text: + Standard: + Text: |- + You learned the Back Slice! + +Helm Splitter Get Item Text: + Standard: + Text: |- + You learned the Helm Splitter! + +Mortal Draw Get Item Text: + Standard: + Text: |- + You learned the Mortal Draw! + +Jump Strike Get Item Text: + Standard: + Text: |- + You learned the Jump Strike! + +Great Spin Get Item Text: + Standard: + Text: |- + You learned the Great Spin! + +Partially Filled Sky Book Get Item Text: + Standard: + Text: |- + You got a Sky Character! + You've collected {} so far. + +Midna Call As Human Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into wolf + <2 way choice 2>Something else + +Midna Call As Wolf Two Choice: + Standard: + Text: |- + <2 way choice 1>Transform into human + <2 way choice 2>Something else + +Midna Call As Wolf No Shadow Crystal Two Choice: + Standard: + Text: |- + <2 way choice 1>Warp + <2 way choice 2>Something else + +Midna Call As Human Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into wolf + <3 way choice 2>Warp + <3 way choice 3>Something else + +Midna Call As Wolf Three Choice: + Standard: + Text: |- + <3 way choice 1>Transform into human + <3 way choice 2>Warp + <3 way choice 3>Something else + +Slingshot Shop Text Template: + Standard: + Text: |- + : 30 Rupees +# I got this in for the kids. It's just a +# toy, but it stings something AWFUL +# when you get hit by it! + +Slingshot Shop Too Expensive Text Template: + Standard: + Text: |- + is 30 Rupees. If you want it, bring some money with you, all right, m'dear? + +Slingshot Shop Purchase Confirmation Text Template: + Standard: + Text: |- + is 30 Rupees. Do you want to buy it, m'dear? + +Slingshot Shop After Purchase Text Template: + Standard: + Text: |- + What are you doing buying , you naughty thing? You're too old for toys! Will you at least let the kids play with it? + +Barnes Special Offer Text Template: + Standard: + Text: |- + I've got a special offer goin' right now: , just 120 Rupees! How 'bout that? + +Kakariko Malo Mart Wooden Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 50 Rupees. Want one or not? + +Kakariko Malo Mart Wooden Shield Too Expensive Text Template: + Standard: + Text: |- + will cost you 50 Rupees, but you can't afford it. Don't expect a discount just because we're from the same town. + +Kakariko Malo Mart Hylian Shield Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 200 Rupees. Want one or not? + +Kakariko Malo Mart Hylian Shield Too Expensive Text Template: + Standard: + Text: |- + will run you 200 Rupees...but if you have that much, I'll eat my hat. And I don't even HAVE a hat. + +Kakariko Malo Mart Hylian Shield After Purchase Text Template: + Standard: + Text: |- + Well, you bought my last ... so you'd better take good care of it. + +Kakariko Malo Mart Hawkeye Purchase Confirmation Text Template: + Standard: + Text: |- + is 100 Rupees. You want it or not? + +Kakariko Malo Mart Hawkeye Too Expensive Text Template: + Standard: + Text: |- + costs 100 Rupees... but there are people with enough Rupees, and then there's you. The guy with not enough. + +Kakariko Malo Mart Hawkeye After Purchase Text Template: + Standard: + Text: |- + You bought my last ... + +Kakariko Malo Mart Red Potion Too Expensive Text Template: + Standard: + Text: |- + will cost you 30 Rupees, but I won't be donating it to the poor, sorry. + +Kakariko Malo Mart Red Potion Purchase Confirmation Text Template: + Standard: + Text: |- + will cost you 30 Rupees. Want some or not? + +Kakariko Malo Mart Red Potion Text Template: + Standard: + Text: |- + : 30 Rupees +# This potion replenishes your +# life energy. Keep it in an empty +# bottle. + +Kakariko Malo Mart Hawkeye Coming Soon Text Template: + Standard: + Text: |- + : COMING SOON + +Kakariko Malo Mart Hawkeye Text Template: + Standard: + Text: |- + : 100 Rupees +# This eyewear allows you to see +# distant objects as if with the eyes +# of a hawk. + +Kakariko Malo Mart Sold Out Text: + Standard: + Text: SOLD OUT + +Kakariko Malo Mart Wooden Shield Text Template: + Standard: + Text: |- + : 50 Rupees +# This is a simple shield. It's made of +# wood, so it will burn away if +# touched by fire. + +Kakariko Malo Mart Hylian Shield Text Template: + Standard: + Text: |- + : 200 Rupees +# LIMITED SUPPLY! +# Don't let them sell out before you +# buy one! + +Chudleys Shop Magic Armor Text Template: + Standard: + Text: |- + + Only for the richest and most + precious customers who value their + lives over their Rupees. + +Castle Town Malo Mart Magic Armor After Purchase Text Template: + Standard: + Text: |- + We have sold out of ! + +Castle Town Malo Mart Magic Armor Text Template: + Standard: + Text: |- + !Special! 598 Rupees +# This is quite a bargain when you +# think of how valuable your life is. +# What's a few Rupees to stay alive? + +Castle Town Malo Mart Magic Armor Sold Out Text Template: + Standard: + Text: |- + + -SOLD OUT- + *This item has been discontinued. + +Charlo Donation Choice Text: + Standard: + Text: |- + <3 way choice 1>100 Rupees + <3 way choice 2>50 Rupees + <3 way choice 3>Sorry... + +Charlo Donation Ask Text Template: + Standard: + Text: |- + For ... + Would you please make a donation? + +Coro Bottle Offer 1 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Coro Bottle Offer 2 Text Template: + Standard: + Text: |- + I have a special, one-time offer of + for only 100 Rupees. How 'bout it, guy? + +Coro Bottle Offer 3 Text Template: + Standard: + Text: |- + Right now we have a 100-Rupee + and 20-Rupee refills to choose from! + +Coro Bottle Offer 4 Text Template: + Standard: + Text: |- + And check this out! I have a special, + one-time offer of for only 100 Rupees! How 'bout it, guy? What a bargain! + +Fishing Hole Sign Text Template: + Standard: + Text: |- + DON'T LITTER! + Do NOT toss empty bottles or + here! The fish are CRYING! + + Keep the fishing hole clean! + +Custom Midna Call Need Something Text: + Standard: + Text: |- + Need Something? + +Custom Midna Call 3 Choice Text: + Standard: + Text: |- + <3 way choice 1>Hints + <3 way choice 2>Change time of day + <3 way choice 3>Return to spawn + +Custom Midna Call 2 Choice Text: + Standard: + Text: |- + <2 way choice 1>Hints + <2 way choice 2>Return to spawn + +Custom Midna Call Hints Text: + Standard: + Text: |- + I have no hints to give. + +Return to Spawn Dungeon Intro Text: + Standard: + Text: |- + I'll get you out of here. + Where do you want to go? + +Return to Spawn Dungeon Choice Text: + Standard: + Text: |- + <3 way choice 1>Dungeon entrance + <3 way choice 2>Nevermind + <3 way choice 3>Spawn + +Return to Spawn Dungeon No Choice Text: + Standard: + Text: |- + <2 way choice 1>Nevermind + <2 way choice 2>Spawn + +Midna Hints Required Dungeons Intro Zero Dungeons: + Standard: + Text: |- + There are 0 required dungeons. + +Midna Hints Required Dungeons Intro At Least One Dungeon: + Standard: + Text: |- + There are required dungeons: + +Ordon Hint Sign Text: + Standard: + Text: |- + Ordon Hint Sign. + There are no hints placed here. + +South Faron Woods Hint Sign Text: + Standard: + Text: |- + South Faron Woods Hint Sign. + There are no hints placed here. + +Sacred Grove Hint Sign Text: + Standard: + Text: |- + Sacred Grove Hint Sign. + There are no hints placed here. + +Faron Field Hint Sign Text: + Standard: + Text: |- + Faron Field Hint Sign. + There are no hints placed here. + +Kakariko Gorge Hint Sign Text: + Standard: + Text: |- + Kakariko Gorge Hint Sign. + There are no hints placed here. + +Kakariko Village Hint Sign Text: + Standard: + Text: |- + Kakariko Village Hint Sign. + There are no hints placed here. + +Kakariko Graveyard Hint Sign Text: + Standard: + Text: |- + Kakariko Graveyard Hint Sign. + There are no hints placed here. + +Eldin Field Hint Sign Text: + Standard: + Text: |- + Eldin Field Hint Sign. + There are no hints placed here. + +North Eldin Field Hint Sign Text: + Standard: + Text: |- + North Eldin Field Hint Sign. + There are no hints placed here. + +Hidden Village Hint Sign Text: + Standard: + Text: |- + Hidden Village Hint Sign. + There are no hints placed here. + +Lanayru Field Hint Sign Text: + Standard: + Text: |- + Lanayru Field Hint Sign. + There are no hints placed here. + +Beside Castle Town Hint Sign Text: + Standard: + Text: |- + Beside Castle Town Hint Sign. + There are no hints placed here. + +Castle Town Center Hint Sign Text: + Standard: + Text: |- + Castle Town Center Hint Sign. + There are no hints placed here. + +Outside South Castle Town Hint Sign Text: + Standard: + Text: |- + Outside South Castle Town Hint Sign. + There are no hints placed here. + +Lake Hylia Bridge Hint Sign Text: + Standard: + Text: |- + Lake Hylia Bridge Hint Sign. + There are no hints placed here. + +Lake Hylia Hint Sign Text: + Standard: + Text: |- + Lake Hylia Hint Sign. + There are no hints placed here. + +Lanayru Spring Hint Sign Text: + Standard: + Text: |- + Lanayru Spring Hint Sign. + There are no hints placed here. + +Lake Lantern Cave Hint Sign Text: + Standard: + Text: |- + Lake Lantern Cave Hint Sign. + There are no hints placed here. + +Fishing Hole Hint Sign Text: + Standard: + Text: |- + Fishing Hole Hint Sign. + There are no hints placed here. + +Zoras Domain Hint Sign Text: + Standard: + Text: |- + Zoras Domain Hint Sign. + There are no hints placed here. + +Snowpeak Hint Sign Text: + Standard: + Text: |- + Snowpeak Hint Sign. + There are no hints placed here. + +Gerudo Desert Hint Sign Text: + Standard: + Text: |- + Gerudo Desert Hint Sign. + There are no hints placed here. + +Bulblin Camp Hint Sign Text: + Standard: + Text: |- + Bulblin Camp Hint Sign. + There are no hints placed here. + +Forest Temple Hint Sign Text: + Standard: + Text: |- + Forest Temple Hint Sign. + There are no hints placed here. + +Goron Mines Hint Sign Text: + Standard: + Text: |- + Goron Mines Hint Sign. + There are no hints placed here. + +Lakebed Temple Hint Sign Text: + Standard: + Text: |- + Lakebed Temple Hint Sign. + There are no hints placed here. + +Arbiters Grounds Hint Sign Text: + Standard: + Text: |- + Arbiters Grounds Hint Sign. + There are no hints placed here. + +Snowpeak Ruins Hint Sign Text: + Standard: + Text: |- + Snowpeak Ruins Hint Sign. + There are no hints placed here. + +Temple of Time First Hint Sign Text: + Standard: + Text: |- + Temple of Time First Hint Sign. + There are no hints placed here. + +Temple of Time Second Hint Sign Text: + Standard: + Text: |- + Temple of Time Second Hint Sign. + There are no hints placed here. + +City in the Sky Hint Sign Text: + Standard: + Text: |- + City in the Sky Hint Sign. + There are no hints placed here. + +Palace of Twilight Hint Sign Text: + Standard: + Text: |- + Palace of Twilight Hint Sign. + There are no hints placed here. + +Hyrule Castle Hint Sign Text: + Standard: + Text: |- + Hyrule Castle Hint Sign. + There are no hints placed here. + +Cave of Ordeals Hint Sign Text: + Standard: + Text: |- + Cave of Ordeals Hint Sign. + There are no hints placed here. diff --git a/mods/randomizer/generator/data/text/text_overrides.yaml b/mods/randomizer/generator/data/text/text_overrides.yaml new file mode 100644 index 0000000000..381bddcc72 --- /dev/null +++ b/mods/randomizer/generator/data/text/text_overrides.yaml @@ -0,0 +1,466 @@ +- Name: Foolish Get Item Text + Group: 0 + Message Id: 120 + +# +#- Name: Ordon Spring Portal Get Item Text +# Group: 0 +# Message Id: 121 +# +#- Name: South Faron Portal Get Item Text +# Group: 0 +# Message Id: 122 + +- Name: Shadow Crystal Get Item Text + Group: 0 + Message Id: 151 + +- Name: Restored Dominion Rod Text + Group: 0 + Message Id: 177 + +- Name: Forest Temple Small Key Get Item Text + Group: 0 + Message Id: 234 + +- Name: Goron Mines Small Key Get Item Text + Group: 0 + Message Id: 235 + +- Name: Lakebed Temple Small Key Get Item Text + Group: 0 + Message Id: 236 + +- Name: Arbiters Grounds Small Key Get Item Text + Group: 0 + Message Id: 237 + +- Name: Snowpeak Ruins Small Key Get Item Text + Group: 0 + Message Id: 238 + +- Name: Temple of Time Small Key Get Item Text + Group: 0 + Message Id: 239 + +- Name: City in the Sky Small Key Get Item Text + Group: 0 + Message Id: 240 + +- Name: Palace of Twilight Small Key Get Item Text + Group: 0 + Message Id: 241 + +- Name: Hyrule Castle Small Key Get Item Text + Group: 0 + Message Id: 242 + +- Name: Bulblin Camp Small Key Get Item Text + Group: 0 + Message Id: 243 + +- Name: Forest Temple Big Key Get Item Text + Group: 0 + Message Id: 247 + +- Name: Lakebed Temple Big Key Get Item Text + Group: 0 + Message Id: 248 + +- Name: Arbiters Grounds Big Key Get Item Text + Group: 0 + Message Id: 249 + +- Name: Temple of Time Big Key Get Item Text + Group: 0 + Message Id: 250 + +- Name: City in the Sky Big Key Get Item Text + Group: 0 + Message Id: 251 + +- Name: Palace of Twilight Big Key Get Item Text + Group: 0 + Message Id: 252 + +- Name: Hyrule Castle Big Key Get Item Text + Group: 0 + Message Id: 253 + +- Name: Forest Temple Compass Get Item Text + Group: 0 + Message Id: 254 + +- Name: Goron Mines Compass Get Item Text + Group: 0 + Message Id: 255 + +- Name: Lakebed Temple Compass Get Item Text + Group: 0 + Message Id: 256 + +- Name: Mirror Shard 2 Get item Text + Group: 0 + Message Id: 266 + +- Name: Mirror Shard 3 Get item Text + Group: 0 + Message Id: 267 + +- Name: Mirror Shard 4 Get item Text + Group: 0 + Message Id: 268 + +- Name: Arbiters Grounds Compass Get Item Text + Group: 0 + Message Id: 269 + +- Name: Snowpeak Ruins Compass Get Item Text + Group: 0 + Message Id: 270 + +- Name: Temple of Time Compass Get Item Text + Group: 0 + Message Id: 271 + +- Name: City in the Sky Compass Get Item Text + Group: 0 + Message Id: 272 + +- Name: Palace of Twilight Compass Get Item Text + Group: 0 + Message Id: 273 + +- Name: Hyrule Castle Compass Get Item Text + Group: 0 + Message Id: 274 + +- Name: Forest Temple Dungeon Map Get Item Text + Group: 0 + Message Id: 283 + +- Name: Goron Mines Dungeon Map Get Item Text + Group: 0 + Message Id: 284 + +- Name: Lakebed Temple Dungeon Map Get Item Text + Group: 0 + Message Id: 285 + +- Name: Arbiters Grounds Dungeon Map Get Item Text + Group: 0 + Message Id: 286 + +- Name: Snowpeak Ruins Dungeon Map Get Item Text + Group: 0 + Message Id: 287 + +- Name: Temple of Time Dungeon Map Get Item Text + Group: 0 + Message Id: 288 + +- Name: City in the Sky Dungeon Map Get Item Text + Group: 0 + Message Id: 289 + +- Name: Palace of Twilight Dungeon Map Get Item Text + Group: 0 + Message Id: 290 + +- Name: Hyrule Castle Dungeon Map Get Item Text + Group: 0 + Message Id: 291 + +- Name: Fused Shadow 1 Get Item Text + Group: 0 + Message Id: 317 + +- Name: Fused Shadow 2 Get Item Text + Group: 0 + Message Id: 318 + +- Name: Fused Shadow 3 Get Item Text + Group: 0 + Message Id: 319 + +- Name: Mirror Shard 1 Get Item Text + Group: 0 + Message Id: 320 + +- Name: Poe Soul Get Item Text + Group: 0 + Message Id: 325 + +- Name: Ending Blow Get Item Text + Group: 0 + Message Id: 326 + +- Name: Shield Attack Get Item Text + Group: 0 + Message Id: 327 + +- Name: Back Slice Get Item Text + Group: 0 + Message Id: 328 + +- Name: Helm Splitter Get Item Text + Group: 0 + Message Id: 329 + +- Name: Mortal Draw Get Item Text + Group: 0 + Message Id: 330 + +- Name: Jump Strike Get Item Text + Group: 0 + Message Id: 331 + +- Name: Great Spin Get Item Text + Group: 0 + Message Id: 332 + +- Name: Partially Filled Sky Book Get Item Text + Group: 0 + Message Id: 335 + +- Name: Midna Call As Human Two Choice + Group: 0 + Message Id: 2004 + +- Name: Midna Call As Wolf Two Choice + Group: 0 + Message Id: 2005 + +- Name: Midna Call As Wolf No Shadow Crystal Two Choice + Group: 0 + Message Id: 2039 + +- Name: Midna Call As Human Three Choice + Group: 0 + Message Id: 2040 + +- Name: Midna Call As Wolf Three Choice + Group: 0 + Message Id: 2041 + +- Name: Slingshot Shop Text + Group: 1 + Message Id: 7009 + +- Name: Slingshot Shop Too Expensive Text + Group: 1 + Message Id: 7014 + +- Name: Slingshot Shop Purchase Confirmation Text + Group: 1 + Message Id: 7015 + +- Name: Slingshot Shop After Purchase Text + Group: 1 + Message Id: 7016 + +- Name: Links House Sign + Group: 1 + Message Id: 9005 + +- Name: Barnes Special Offer Text + Group: 2 + Message Id: 5155 + +- Name: Kakariko Malo Mart Wooden Shield Purchase Confirmation Text + Group: 2 + Message Id: 5809 + +- Name: Kakariko Malo Mart Wooden Shield Too Expensive Text + Group: 2 + Message Id: 5810 + +- Name: Kakariko Malo Mart Hylian Shield Purchase Confirmation Text + Group: 2 + Message Id: 5813 + +- Name: Kakariko Malo Mart Hylian Shield Too Expensive Text + Group: 2 + Message Id: 5814 + +- Name: Kakariko Malo Mart Hylian Shield After Purchase Text + Group: 2 + Message Id: 5818 + +- Name: Kakariko Malo Mart Hawkeye Purchase Confirmation Text + Group: 2 + Message Id: 5820 + +- Name: Kakariko Malo Mart Hawkeye Too Expensive Text + Group: 2 + Message Id: 5821 + +- Name: Kakariko Malo Mart Hawkeye After Purchase Text + Group: 2 + Message Id: 5822 + +- Name: Kakariko Malo Mart Red Potion Too Expensive Text + Group: 2 + Message Id: 5824 + +- Name: Kakariko Malo Mart Red Potion Purchase Confirmation Text + Group: 2 + Message Id: 5825 + +- Name: Kakariko Malo Mart Red Potion Text + Group: 2 + Message Id: 5990 + +- Name: Kakariko Malo Mart Hawkeye Coming Soon Text + Group: 2 + Message Id: 5991 + +- Name: Kakariko Malo Mart Hawkeye Text + Group: 2 + Message Id: 5992 + +- Name: Kakariko Malo Mart Sold Out Text + Group: 2 + Message Id: 5996 + +- Name: Kakariko Malo Mart Wooden Shield Text + Group: 2 + Message Id: 5998 + +- Name: Kakariko Malo Mart Hylian Shield Text + Group: 2 + Message Id: 5999 + +- Name: Chudleys Shop Magic Armor Text + Group: 4 + Message Id: 5413 + +- Name: Castle Town Malo Mart Magic Armor After Purchase Text + Group: 4 + Message Id: 5433 + +- Name: Castle Town Malo Mart Magic Armor Text + Group: 4 + Message Id: 5440 + +- Name: Castle Town Malo Mart Magic Armor Sold Out Text + Group: 4 + Message Id: 5451 + +- Name: Charlo Donation Ask Text + Group: 4 + Message Id: 6402 + +- Name: Charlo Donation Choice Text + Group: 4 + Message Id: 6403 + +- Name: Coro Bottle Offer 1 Text + Group: 6 + Message Id: 5216 + +- Name: Coro Bottle Offer 2 Text + Group: 6 + Message Id: 5223 + +- Name: Coro Bottle Offer 3 Text + Group: 6 + Message Id: 5237 + +- Name: Coro Bottle Offer 4 Text + Group: 6 + Message Id: 5253 + +- Name: Fishing Hole Sign Text + Group: 7 + Message Id: 9502 + +# Completely Custom Text IDs +- Name: Custom Midna Call Need Something Text + Attributes: 0x07F60000150D0000FF00000000000400 + +- Name: Custom Midna Call 3 Choice Text + Attributes: 0x07F8000015000000FF00000000000400 + +- Name: Custom Midna Call 2 Choice Text + Attributes: 0x07F8000015000000FF00000000000400 + +- Name: Custom Midna Call Hints Text + Attributes: 0x07F60000150D0000FF00000000000400 + +- Name: Return to Spawn Dungeon Intro Text + Attributes: 0x07F60000150D0000FF00000000000400 + +- Name: Return to Spawn Dungeon Choice Text + Attributes: 0x07F60000150D0000FF00000000000400 + +- Name: Return to Spawn Dungeon No Choice Text + Attributes: 0x07F60000150D0000FF00000000000400 + +- Name: Ordon Hint Sign Text + +- Name: South Faron Woods Hint Sign Text + +- Name: Sacred Grove Hint Sign Text + +- Name: Faron Field Hint Sign Text + +- Name: Kakariko Gorge Hint Sign Text + +- Name: Kakariko Village Hint Sign Text + +- Name: Kakariko Graveyard Hint Sign Text + +- Name: Eldin Field Hint Sign Text + +- Name: North Eldin Field Hint Sign Text + +- Name: Hidden Village Hint Sign Text + +- Name: Lanayru Field Hint Sign Text + +- Name: Beside Castle Town Hint Sign Text + +- Name: Castle Town Center Hint Sign Text + +- Name: Outside South Castle Town Hint Sign Text + +- Name: Lake Hylia Bridge Hint Sign Text + +- Name: Lake Hylia Hint Sign Text + +- Name: Lanayru Spring Hint Sign Text + +- Name: Lake Lantern Cave Hint Sign Text + +- Name: Fishing Hole Hint Sign Text + +- Name: Zoras Domain Hint Sign Text + +- Name: Snowpeak Hint Sign Text + +- Name: Gerudo Desert Hint Sign Text + +- Name: Bulblin Camp Hint Sign Text + +- Name: Forest Temple Hint Sign Text + +- Name: Goron Mines Hint Sign Text + +- Name: Lakebed Temple Hint Sign Text + +- Name: Arbiters Grounds Hint Sign Text + +- Name: Snowpeak Ruins Hint Sign Text + +- Name: Temple of Time First Hint Sign Text + +- Name: Temple of Time Second Hint Sign Text + +- Name: City in the Sky Hint Sign Text + +- Name: Palace of Twilight Hint Sign Text + +- Name: Hyrule Castle Hint Sign Text + +- Name: Cave of Ordeals Hint Sign Text \ No newline at end of file diff --git a/mods/randomizer/generator/data/world/Root.yaml b/mods/randomizer/generator/data/world/Root.yaml new file mode 100644 index 0000000000..b486c5d25e --- /dev/null +++ b/mods/randomizer/generator/data/world/Root.yaml @@ -0,0 +1,163 @@ +# These logic files contain the listings for each predefined logical area. Areas can contain +# events, locations, and exits. +# +# EVENTS are logic variables which become true when their requirement evaluates +# to true and are surrounded with single quotes when used in other logic statements +# (unlike macros which are defined in macros.yaml). It's possible to add new events +# to the logic simply by defining them in an area and then using them in whichever +# logic statements you wish. No modifications to the code are necessary. Every logical +# area that gets defined will also come with an automatically generated 'Can_Access_' +# event. This event is added to the area and has no requirements. If you start a logic statement +# with an event, then the entire statement must be surrounded in double quotes due to how yaml +# is parsed. +# +# LOCATIONS are places which can contain randomized items. Adding new locations +# here will require adding them in locations.yaml as well. +# +# EXITS define how the areas of the world graph connect to each other. Each exit +# is a one way connection, so when making new areas remember to define the exit +# connections both ways if applicable. No modifications to the code are necessary +# for adding new exits. +# +# Each dungeon in the world graph is at least defined on a room by room basis +# (although in many cases rooms are split up into multiple parts). This is to +# help simplify glitched logic (eventually). +# +# NOTE: When writing direct logic requirements for exits, do not write a logic +# statement that requires both human link *and* wolf link to evaluate to true. +# The search and flatten algorithms need to keep track of human-wolf/day-night +# combinations separately and test them one at a time to know which forms +# are allowed through any single exit. This doesn't work if both forms are +# directly required. If you need to write a logic statement for an exit that +# absolutely requires both human and wolf link, then hide away one (or both) +# of the forms behind an event in the area. +# +# See the "Lost Woods Lower Battle Arena -> Lost Woods Baba Serpent Grotto" exit +# for an example of the above. + +# The following logical operators/functions are available for logic statements: +# - and +# - or +# - +# - Human_Link +# - Wolf_Link +# - Day +# - Night +# - count(, ) +# - setting_name [<=, ==, >=, !=] option +# - golden_bugs(count) + +- Name: Root + Exits: + Root Human Day: (Human_Link and (Starting_Form == Human or Shadow_Crystal)) and (Starting_Time_of_Day == Morning or Starting_Time_of_Day == Noon) + Root Human Night: (Human_Link and (Starting_Form == Human or Shadow_Crystal)) and (Starting_Time_of_Day == Evening or Starting_Time_of_Day == Night) + Root Wolf Day: (Wolf_Link and (Starting_Form == Wolf or Shadow_Crystal)) and (Starting_Time_of_Day == Morning or Starting_Time_of_Day == Noon) + Root Wolf Night: (Wolf_Link and (Starting_Form == Wolf or Shadow_Crystal)) and (Starting_Time_of_Day == Evening or Starting_Time_of_Day == Night) + +- Name: Root Human Day + Can Transform: Never + Exits: + Root Exits: Nothing + +- Name: Root Human Night + Can Transform: Never + Exits: + Root Exits: Nothing + +- Name: Root Wolf Day + Can Transform: Never + Exits: + Root Exits: Nothing + +- Name: Root Wolf Night + Can Transform: Never + Exits: + Root Exits: Nothing + +- Name: Root Exits + Can Transform: Never + Exits: + Links Spawn: Nothing + Warp Portals: Can_Use_Warp_Portals + +- Name: Links Spawn + Exits: + Outside Links House: Nothing + +- Name: Warp Portals + Exits: + Ordon Spring Warp Portal: Ordon_Spring_Portal and (Unlock_Map_Regions == On or 'Ordona_Province_Map_Sector') + South Faron Woods Warp Portal: South_Faron_Portal and (Unlock_Map_Regions == On or 'Faron_Province_Map_Sector') + North Faron Woods Warp Portal: North_Faron_Portal and (Unlock_Map_Regions == On or 'Faron_Province_Map_Sector') + Sacred Grove Warp Portal: Sacred_Grove_Portal and (Unlock_Map_Regions == On or 'Faron_Province_Map_Sector') + Kakariko Gorge Warp Portal: Kakariko_Gorge_Portal and (Unlock_Map_Regions == On or 'Eldin_Province_Map_Sector') + Kakariko Village Warp Portal: Kakariko_Village_Portal and (Unlock_Map_Regions == On or 'Eldin_Province_Map_Sector') + Death Mountain Warp Portal: Death_Mountain_Portal and (Unlock_Map_Regions == On or 'Eldin_Province_Map_Sector') + Bridge of Eldin Warp Portal: Bridge_of_Eldin_Portal and (Unlock_Map_Regions == On or 'Eldin_Province_Map_Sector') + Castle Town Warp Portal: Castle_Town_Portal and (Unlock_Map_Regions == On or 'Lanayru_Province_Map_Sector') + Lake Hylia Warp Portal: Lake_Hylia_Portal and (Unlock_Map_Regions == On or 'Lanayru_Province_Map_Sector') + Upper Zoras River Warp Portal: Upper_Zoras_River_Portal and (Unlock_Map_Regions == On or 'Lanayru_Province_Map_Sector') + Zoras Domain Warp Portal: Zoras_Domain_Portal and (Unlock_Map_Regions == On or 'Lanayru_Province_Map_Sector') + Snowpeak Warp Portal: Snowpeak_Portal and (Unlock_Map_Regions == On or 'Snowpeak_Province_Map_Sector') + Gerudo Desert Warp Portal: Gerudo_Desert_Portal and 'Desert_Province_Map_Sector' + Mirror Chamber Warp Portal: Mirror_Chamber_Portal and 'Desert_Province_Map_Sector' + +- Name: Ordon Spring Warp Portal + Exits: + Ordon Spring: Nothing + +- Name: South Faron Woods Warp Portal + Exits: + South Faron Woods: Nothing + +- Name: North Faron Woods Warp Portal + Exits: + North Faron Woods: Nothing + +- Name: Sacred Grove Warp Portal + Exits: + Sacred Grove Lower: Nothing + +- Name: Kakariko Gorge Warp Portal + Exits: + Kakariko Gorge: Nothing + +- Name: Kakariko Village Warp Portal + Exits: + Lower Kakariko Village: Nothing + +- Name: Death Mountain Warp Portal + Exits: + Death Mountain Volcano: Nothing + +- Name: Bridge of Eldin Warp Portal + Exits: + Eldin Field North of Bridge: Nothing + +- Name: Castle Town Warp Portal + Exits: + Outside Castle Town West: Nothing + +- Name: Lake Hylia Warp Portal + Exits: + Lake Hylia: Nothing + +- Name: Upper Zoras River Warp Portal + Exits: + Upper Zoras River: Nothing + +- Name: Zoras Domain Warp Portal + Exits: + Zoras Throne Room: Nothing + +- Name: Snowpeak Warp Portal + Exits: + Snowpeak Summit Upper: Nothing + +- Name: Gerudo Desert Warp Portal + Exits: + Gerudo Desert Cave of Ordeals Plateau: Nothing + +- Name: Mirror Chamber Warp Portal + Exits: + Mirror Chamber Upper: Nothing \ No newline at end of file diff --git a/mods/randomizer/generator/data/world/dungeons/Arbiters Grounds.yaml b/mods/randomizer/generator/data/world/dungeons/Arbiters Grounds.yaml new file mode 100644 index 0000000000..553287c81a --- /dev/null +++ b/mods/randomizer/generator/data/world/dungeons/Arbiters Grounds.yaml @@ -0,0 +1,347 @@ + +# ARBITERS GROUNDS ENTRANCE ROOM + +- Name: Arbiters Grounds Entrance + Region: Arbiters Grounds + Dungeon Start Area: True + Events: + Can Pull Arbiters Entrance Chain: Clawshot or Can_Cross_Quicksand + Exits: + Arbiters Grounds Entrance Past Gate: "'Can_Pull_Arbiters_Entrance_Chain'" + Outside Arbiters Grounds: Nothing + +- Name: Arbiters Grounds Entrance Past Gate + Region: Arbiters Grounds + Events: + Can Refill Lantern Oil: Nothing + Locations: + Arbiters Grounds Entrance Chest: Can_Break_Wooden_Barrier + Exits: + Arbiters Grounds Dark Lantern Room: Arbiters_Grounds_Small_Key or Small_Keys == Keysy + Arbiters Grounds Entrance: "'Can_Pull_Arbiters_Entrance_Chain'" + +# ARBITERS GROUNDS DARK LANTERN ROOM + +- Name: Arbiters Grounds Dark Lantern Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds Torch Room: Lantern + Arbiters Grounds Entrance Past Gate: Nothing + +# ARBITERS GROUNDS TORCH ROOM + +- Name: Arbiters Grounds Torch Room + Region: Arbiters Grounds + Events: + Poe Scent: Can_Use_Senses and Can_Sniff + Can Pull Torch Room Chain: "'Poe_Scent'" + Arbiters Grounds Poe 1: Can_Use_Senses + Locations: + Arbiters Grounds Torch Room East Chest: Nothing + Arbiters Grounds Torch Room West Chest: Nothing + Arbiters Grounds Torch Room Poe: Can_Use_Senses + Arbiters Grounds Hint Sign: Nothing + Exits: + Arbiters Grounds East Turnable Room Middle: Nothing + Arbiters Grounds Torch Room Near East Turnable Room Lower: "'Can_Pull_Torch_Room_Chain'" + Arbiters Grounds West Chandelier Room Near Lower Torch Room: Nothing + Arbiters Grounds Socket Room Near Torch Room: "'Arbiters_Grounds_Poe_1' and 'Arbiters_Grounds_Poe_2' and 'Arbiters_Grounds_Poe_3' and 'Arbiters_Grounds_Poe_4'" + +- Name: Arbiters Grounds Torch Room Near East Turnable Room Lower + Region: Arbiters Grounds + Exits: + Arbiters Grounds East Turnable Room Lower: Nothing + Arbiters Grounds Torch Room: "'Can_Pull_Torch_Room_Chain'" + +- Name: Arbiters Grounds Torch Room Chandelier + Region: Arbiters Grounds + Exits: + Arbiters Grounds Torch Room: Nothing + Arbiters Grounds Ghoul Rat Room: Nothing + Arbiters Grounds West Chandelier Room: Nothing + +# ARBITERS GROUNDS EAST TURNABLE ROOM + +- Name: Arbiters Grounds East Turnable Room Middle + Region: Arbiters Grounds + Exits: + Arbiters Grounds East Chandelier Room Near Turnable Room: count(Arbiters_Grounds_Small_Key, 2) or Small_Keys == Keysy + Arbiters Grounds Torch Room: Nothing + +- Name: Arbiters Grounds East Turnable Room Lower + Region: Arbiters Grounds + Locations: + Arbiters Grounds East Lower Turnable Redead Chest: Nothing + Exits: + Arbiters Grounds Poe 2 Room: Can_Defeat_Redead_Knight and Clawshot + Arbiters Grounds Torch Room Near East Turnable Room Lower: Nothing + +# ARBITERS GROUNDS POE 2 ROOM + +- Name: Arbiters Grounds Poe 2 Room + Region: Arbiters Grounds + Events: + Arbiters Grounds Poe 2: Can_Use_Senses + Locations: + Arbiters Grounds East Turning Room Poe: Can_Use_Senses + +# ARBITERS GROUNDS EAST CHANDELIER ROOM + +- Name: Arbiters Grounds East Chandelier Room Near Turnable Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds East Chandelier Room Past Chandelier: Can_Pull_Blocks + Arbiters Grounds East Turnable Room Middle: Nothing + +- Name: Arbiters Grounds East Chandelier Room Past Chandelier + Region: Arbiters Grounds + Locations: + Arbiters Grounds East Upper Turnable Chest: Nothing + Arbiters Grounds East Upper Turnable Redead Chest: Can_Break_Wooden_Barrier + Exits: + Arbiters Grounds Poe 3 Room Near East Chandelier Room: count(Arbiters_Grounds_Small_Key, 3) or Small_Keys == Keysy + Arbiters Grounds East Chandelier Room Near Turnable Room: Nothing + +# ARBITERS GROUNDS POE 3 ROOM + +- Name: Arbiters Grounds Poe 3 Room Near East Chandelier Room + Region: Arbiters Grounds + Events: + Arbiters Grounds Poe 3: Can_Use_Senses and Can_Defeat_Stalchild and Can_Defeat_Redead_Knight and 'Poe_Scent' + Locations: + Arbiters Grounds Hidden Wall Poe: Can_Use_Senses and Can_Defeat_Stalchild and Can_Defeat_Redead_Knight and 'Poe_Scent' + Exits: + Arbiters Grounds Poe 3 Room Near Ghoul Rat Room: Can_Defeat_Stalchild and Can_Defeat_Redead_Knight + Arbiters Grounds East Chandelier Room Past Chandelier: Impossible # Wall + +- Name: Arbiters Grounds Poe 3 Room Near Ghoul Rat Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds Ghoul Rat Room: Nothing + Arbiters Grounds Poe 3 Room Near East Chandelier Room: Can_Defeat_Stalchild and Can_Defeat_Redead_Knight + +# ARBITERS GROUNDS GOUL RAT ROOM + +- Name: Arbiters Grounds Ghoul Rat Room + Region: Arbiters Grounds + Locations: + Arbiters Grounds Ghoul Rat Room Chest: Nothing + Exits: + Arbiters Grounds Torch Room Chandelier: count(Arbiters_Grounds_Small_Key, 4) or Small_Keys == Keysy + Arbiters Grounds Poe 3 Room Near Ghoul Rat Room: Nothing + +# ARBITERS GROUNDS WEST CHANDELIER ROOM + +- Name: Arbiters Grounds West Chandelier Room Near Lower Torch Room + Region: Arbiters Grounds + Locations: + Arbiters Grounds West Small Chest Behind Block: Nothing + Exits: + Arbiters Grounds West Chandelier Room: "'Can_Push_West_Chandelier_Room_Block'" + +- Name: Arbiters Grounds West Chandelier Room + Region: Arbiters Grounds + Events: + Can Push West Chandelier Room Block: Nothing + Locations: + Arbiters Grounds West Chandelier Chest: Nothing + Exits: + Arbiters Grounds West Chandelier Room Near Lower Torch Room: Nothing + Arbiters Grounds Single Stalfos Room Near West Chandelier Room: Nothing + +- Name: Arbiters Grounds West Chandelier Room High Platform + Region: Arbiters Grounds + Exits: + Arbiters Grounds Poe 4 Room: Nothing + Arbiters Grounds West Chandelier Room: Nothing + +# ARBITERS GROUNDS SINGLE STALFOS ROOM + +- Name: Arbiters Grounds Single Stalfos Room Near West Chandelier Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds Single Stalfos Room Near Lantern Puzzle Room: Can_Break_Wooden_Barrier + Arbiters Grounds West Chandelier Room: Nothing + +- Name: Arbiters Grounds Single Stalfos Room Near Lantern Puzzle Room + Region: Arbiters Grounds + Locations: + Arbiters Grounds West Stalfos West Chest: Nothing + Arbiters Grounds West Stalfos Northeast Chest: Can_Break_Wooden_Barrier + Exits: + Arbiters Grounds Lantern Puzzle Room Near Single Stalfos Room: Can_Defeat_Stalfos + Arbiters Grounds Single Stalfos Room Near West Chandelier Room: Can_Break_Wooden_Barrier + +# ARBITERS GROUNDS LANTERN PUZZLE ROOM + +- Name: Arbiters Grounds Lantern Puzzle Room Near Single Stalfos Room + Region: Arbiters Grounds + Events: + Can Light Arbiters Grounds Lantern Puzzle: Lantern + Exits: + Arbiters Grounds Lantern Puzzle Room Near Poe 4 Room: "'Can_Light_Arbiters_Grounds_Lantern_Puzzle'" + Arbiters Grounds Single Stalfos Room Near Lantern Puzzle Room: Nothing + +- Name: Arbiters Grounds Lantern Puzzle Room Near Poe 4 Room + Exits: + Arbiters Grounds Poe 4 Room: Nothing + Arbiters Grounds Lantern Puzzle Room Near Single Stalfos Room: "'Can_Light_Arbiters_Grounds_Lantern_Puzzle'" + +# Arbiters GROUNDS POE 4 ROOM + +- Name: Arbiters Grounds Poe 4 Room + Region: Arbiters Grounds + Events: + Arbiters Grounds Poe 4: Can_Use_Senses + Locations: + Arbiters Grounds West Poe: Can_Use_Senses + Exits: + Arbiters Grounds West Chandelier Room High Platform: Nothing + Arbiters Grounds Lantern Puzzle Room Near Poe 4 Room: Nothing + +# ARBITERS GROUNDS SOCKET ROOM + +- Name: Arbiters Grounds Socket Room Near Torch Room + Region: Arbiters Grounds + Events: + Can Spin Turning Wall Socket: Spinner + Exits: + Arbiters Grounds Socket Room Near North Turning Room: Clawshot # If the wall is already turned + Arbiters Grounds Socket Room Bottom Tower: "'Can_Spin_Turning_Wall_Socket'" + Arbiters Grounds Socket Room Near Torch Room: Nothing + +- Name: Arbiters Grounds Socket Room Near North Turning Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds North Turning Room Near Socket Room: Nothing + # Can't assume access the other way because of the potential wall + +- Name: Arbiters Grounds Socket Room Near Spinner Room + Region: Arbiters Grounds + Locations: + Arbiters Grounds Big Key Chest: Nothing + Exits: + Arbiters Grounds Socket Room Near Torch Room: Spinner + Arbiters Grounds Spinner Room Near Socket Room: Nothing + +- Name: Arbiters Grounds Socket Room Bottom Tower + Region: Arbiters Grounds + Exits: + Arbiters Grounds Socket Room Near Boss Door: Spinner + Arbiters Grounds Socket Room Near Torch Room: "'Can_Spin_Turning_Wall_Socket'" + +- Name: Arbiters Grounds Socket Room Near Boss Door + Region: Arbiters Grounds + Exits: + Arbiters Grounds Boss Room: Arbiters_Grounds_Big_Key or Big_Keys == Keysy + Arbiters Grounds Socket Room Bottom Tower: Nothing + +# ARBITERS GROUNDS NORTH TURNING ROOM + +- Name: Arbiters Grounds North Turning Room Near Socket Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds North Turning Room Central Column: Nothing + Arbiters Grounds Socket Room Near North Turning Room: Nothing + +- Name: Arbiters Grounds North Turning Room Central Column + Region: Arbiters Grounds + Locations: + Arbiters Grounds North Turning Room Chest: Nothing + Exits: + Arbiters Grounds North Turning Room Near Basement Spike Room: Nothing + Arbiters Grounds North Turning Room Near Socket Room: Clawshot + +- Name: Arbiters Grounds North Turning Room Near Basement Spike Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds Basement Spike Room Near North Turning Room: count(Arbiters_Grounds_Small_Key, 5) or Small_Keys == Keysy + +# ARBITERS GROUNDS BASEMENT SPIKE ROOM + +- Name: Arbiters Grounds Basement Spike Room Near North Turning Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds Basement Spike Room Near Spinner Traps: Can_Defeat_Ghoul_Rat + Arbiters Grounds North Turning Room Near Basement Spike Room: count(Arbiters_Grounds_Small_Key, 5) or Small_Keys == Keysy + +- Name: Arbiters Grounds Basement Spike Room Near Spinner Traps + Region: Arbiters Grounds + Exits: + Arbiters Grounds Triple Stalfos Room Center: Nothing + +# ARBITERS GROUNDS TRIPLE STALFOS ROOM + +- Name: Arbiters Grounds Triple Stalfos Room Center + Region: Arbiters Grounds + Exits: + Arbiters Grounds Triple Stalfos Room Near Miniboss Room: Can_Defeat_Stalfos + Arbiters Grounds Triple Stalfos Room Near Spinner Room: Spinner + +- Name: Arbiters Grounds Triple Stalfos Room Near Miniboss Room + Region: Arbiters Grounds + Exits: + Deathsword Miniboss Room: Nothing + Arbiters Grounds Triple Stalfos Room Near Spinner Room: Spinner + Arbiters Grounds Triple Stalfos Room Center: Nothing + +- Name: Arbiters Grounds Triple Stalfos Room Near Spinner Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds Spinner Room Bottom: Nothing + Arbiters Grounds Triple Stalfos Room Center: Spinner + Arbiters Grounds Triple Stalfos Room Near Miniboss Room: Spinner + +# DEATHSWORD MINIBOSS ROOM + +- Name: Deathsword Miniboss Room + Locations: + Arbiters Grounds Death Sword Chest: Can_Defeat_Deathsword + Exits: + Arbiters Grounds Triple Stalfos Room Near Miniboss Room: Can_Defeat_Deathsword + +# ARBITERS GROUNDS SPINNER ROOM + +- Name: Arbiters Grounds Spinner Room Bottom + Region: Arbiters Grounds + Locations: + Arbiters Grounds Spinner Room First Small Chest: Can_Cross_Quicksand + Arbiters Grounds Spinner Room Second Small Chest: Can_Cross_Quicksand + Arbiters Grounds Spinner Room Lower Central Small Chest: Can_Cross_Quicksand + Exits: + Arbiters Grounds Spinner Room Near Single Stalfos: Spinner + Arbiters Grounds Spinner Room Near Double Stalfos: Spinner + Arbiters Grounds Triple Stalfos Room Near Spinner Room: Can_Cross_Quicksand + +- Name: Arbiters Grounds Spinner Room Near Single Stalfos + Region: Arbiters Grounds + Locations: + Arbiters Grounds Spinner Room Stalfos Alcove Chest: Nothing + Exits: + Arbiters Grounds Spinner Room Bottom: Nothing + +- Name: Arbiters Grounds Spinner Room Near Double Stalfos + Region: Arbiters Grounds + Locations: + Arbiters Grounds Spinner Room Lower North Chest: Nothing + Exits: + Arbiters Grounds Spinner Room Near Socket Room: Spinner + Arbiters Grounds Spinner Room Near Single Stalfos: Nothing + Arbiters Grounds Spinner Room Bottom: Nothing + +- Name: Arbiters Grounds Spinner Room Near Socket Room + Region: Arbiters Grounds + Exits: + Arbiters Grounds Socket Room Near Spinner Room: Nothing + Arbiters Grounds Spinner Room Near Double Stalfos: Nothing + +# ARBITERS GROUNDS BOSS ROOM + +- Name: Arbiters Grounds Boss Room + Events: + Can Complete Arbiters Grounds: Can_Defeat_Stallord + Locations: + Arbiters Grounds Stallord Heart Container: Can_Defeat_Stallord + Arbiters Grounds Dungeon Reward: Can_Defeat_Stallord + Exits: + Mirror Chamber Lower: Can_Defeat_Stallord \ No newline at end of file diff --git a/mods/randomizer/generator/data/world/dungeons/City in the Sky.yaml b/mods/randomizer/generator/data/world/dungeons/City in the Sky.yaml new file mode 100644 index 0000000000..f24ecda18f --- /dev/null +++ b/mods/randomizer/generator/data/world/dungeons/City in the Sky.yaml @@ -0,0 +1,406 @@ +# CITY IN THE SKY ENTRANCE + +- Name: City in the Sky Entrance + Region: City in the Sky + Dungeon Start Area: True + Locations: + City in the Sky Underwater West Chest: Iron_Boots + City in the Sky Underwater East Chest: Iron_Boots + Exits: + Lake Hylia: Clawshot # Or Nothing after fall entrance is set + City in the Sky Oocca Hallway Near Entrance: Can_Hit_Crystal_Switch_at_Range + City in the Sky Shop: Nothing + +# CITY IN THE SKY SHOP + +- Name: City in the Sky Shop + Region: City in the Sky + Events: + Can Refill Regular Bombs: "'Can_Farm_Rupees'" + Can Refill Arrows: "'Can_Farm_Rupees'" + Can Refill Lantern Oil: "'Can_Farm_Rupees'" + Exits: + City in the Sky Entrance: Nothing + +# CITY IN THE SKY OOCCA HALLWAY + +- Name: City in the Sky Oocca Hallway Near Entrance + Region: City in the Sky + Exits: + City in the Sky Oocca Hallway Near Lobby: Clawshot + City in the Sky Entrance: Clawshot + +- Name: City in the Sky Oocca Hallway Near Lobby + Region: City in the Sky + Exits: + City in the Sky Lobby Floor: Nothing + City in the Sky Oocca Hallway Near Entrance: Clawshot + +# CITY IN THE SKY LOBBY + +- Name: City in the Sky Lobby Floor + Region: City in the Sky + Locations: + City in the Sky Hint Sign: Nothing + Exits: + City in the Sky Lobby Floor Near West Bridge: Double_Clawshots + City in the Sky East Bridge Near Lobby: Nothing + City in the Sky North Fan Passageway Near Lobby: Nothing + City in the Sky Lobby Floor Upper West Ledge: Clawshot + City in the Sky Lobby Above Ceiling Fan: Double_Clawshots and 'Can_Disable_City_in_the_Sky_Lobby_Ceiling_Fan' + +- Name: City in the Sky Lobby Floor Near West Bridge + Region: City in the Sky + Exits: + City in the Sky West Bridge Near Lobby: Nothing + City in the Sky Lobby Floor: Clawshot + +- Name: City in the Sky Lobby Floor Upper West Ledge + Region: City in the Sky + Exits: + City in the Sky West Bridge Upper Ledge: Nothing + City in the Sky Lobby Floor: Nothing + +- Name: City in the Sky Lobby Above Ceiling Fan + Region: City in the Sky + Events: + Can Turn on City in the Sky North Fan: Double_Clawshots and 'Can_Disable_City_in_the_Sky_Lobby_Ceiling_Fan' + Locations: + City in the Sky Chest Below Big Key Chest: Nothing + Exits: + City in the Sky Outside Central Tower Ground: Nothing + City in the Sky Lobby Floor: Can_Survive_Damage and 'Can_Disable_City_in_the_Sky_Lobby_Ceiling_Fan' + +- Name: City in the Sky Lobby Above Highest Grating + Region: City in the Sky + Events: + Can Disable City in the Sky Lobby Ceiling Fan: Iron_Boots and Clawshot + Locations: + City in the Sky Big Key Chest: Iron_Boots + Exits: + City in the Sky Lobby Above Ceiling Fan: Nothing + City in the Sky Outside Central Tower Ropes: Nothing + +# CITY IN THE SKY WEST BRIDGE + +- Name: City in the Sky West Bridge Upper Ledge + Region: City in the Sky + Events: + Can Extend City in the Sky West Bridge: Spinner + Exits: + City in the Sky West Bridge Near Lobby: Clawshot + City in the Sky Lobby Floor Upper West Ledge: Nothing + +- Name: City in the Sky West Bridge Near Lobby + Region: City in the Sky + Exits: + City in the Sky West Bridge Near Double Clawshot Maze Room: Double_Clawshots or 'Can_Extend_City_in_the_Sky_West_Bridge' + City in the Sky West Bridge Upper Ledge: Clawshot + City in the Sky Lobby Floor Near West Bridge: Nothing + +- Name: City in the Sky West Bridge Near Double Clawshot Maze Room + Region: City in the Sky + Exits: + City in the Sky Double Clawshot Maze Room Near West Bridge: Nothing + City in the Sky West Bridge Near Lobby: Double_Clawshots or 'Can_Extend_City_in_the_Sky_West_Bridge' + +# CITY IN THE SKY DOUBLE CLAWSHOT MAZE ROOM + +- Name: City in the Sky Double Clawshot Maze Room Near West Bridge + Region: City in the Sky + Locations: + City in the Sky West Wing First Chest: Clawshot + Exits: + City in the Sky Double Clawshot Maze Room Near Baba Tower: Double_Clawshots + City in the Sky West Bridge Near Double Clawshot Maze Room: Nothing + +- Name: City in the Sky Double Clawshot Maze Room Near Baba Tower + Region: City in the Sky + Locations: + City in the Sky West Wing Baba Balcony Chest: Nothing + City in the Sky West Wing Narrow Ledge Chest: Nothing + City in the Sky West Wing Tile Worm Chest: Nothing + Exits: + City in the Sky Baba Tower Bottom: Nothing + +# CITY IN THE SKY BABA TOWER + +- Name: City in the Sky Baba Tower Bottom + Region: City in the Sky + Locations: + City in the Sky Baba Tower Top Small Chest: Can_Defeat_Baba_Serpent and Can_Defeat_Big_Baba and Double_Clawshots + City in the Sky Baba Tower Narrow Ledge Chest: Can_Defeat_Baba_Serpent and Can_Defeat_Big_Baba and Double_Clawshots + City in the Sky Baba Tower Alcove Chest: Can_Defeat_Baba_Serpent and Can_Defeat_Big_Baba and Double_Clawshots + Exits: + City in the Sky Baba Tower Top: Can_Defeat_Baba_Serpent and Can_Defeat_Big_Baba and Double_Clawshots + City in the Sky Double Clawshot Maze Room Near Baba Tower: Nothing + +- Name: City in the Sky Baba Tower Top + Region: City in the Sky + Exits: + City in the Sky West Garden Near Baba Tower: Nothing + City in the Sky Baba Tower Bottom: Double_Clawshots + +# CITY IN THE SKY WEST GARDEN + +- Name: City in the Sky West Garden Near Baba Tower + Region: City in the Sky + Exits: + City in the Sky West Garden Middle: Clawshot + City in the Sky Baba Tower Top: Nothing + +- Name: City in the Sky West Garden Middle + Region: City in the Sky + Locations: + City in the Sky West Garden Corner Chest: Nothing + City in the Sky West Garden Lone Island Chest: Double_Clawshots + City in the Sky Garden Island Poe: Double_Clawshots and Can_Defeat_Poe + Exits: + City in the Sky West Garden Near Peahat Train North: Double_Clawshots + +- Name: City in the Sky West Garden Near Peahat Train North + Region: City in the Sky + Locations: + City in the Sky West Garden Lower Chest: Nothing + Exits: + City in the Sky Peahat Train Room North Near West Garden: Nothing + City in the Sky West Garden Near Baba Tower: Clawshot + +- Name: City in the Sky West Garden Near Peahat Train South + Region: City in the Sky + Locations: + City in the Sky West Garden Ledge Chest: Nothing + Exits: + City in the Sky West Garden Middle: Nothing + City in the Sky Peahat Train Room South Near West Garden: Nothing + +# CITY IN THE SKY PEAHAT TRAIN ROOM + +- Name: City in the Sky Peahat Train Room North Near West Garden + Region: City in the Sky + Exits: + City in the Sky Peahat Train Room South Near West Garden: Double_Clawshots + City in the Sky Peahat Train Room Near Outside Central Tower: Double_Clawshots + City in the Sky West Garden Near Peahat Train North: Nothing + +- Name: City in the Sky Peahat Train Room South Near West Garden + Region: City in the Sky + Exits: + City in the Sky Peahat Train Room North Near West Garden: Double_Clawshots + City in the Sky Peahat Train Room Near Outside Central Tower: Double_Clawshots + City in the Sky West Garden Near Peahat Train South: Nothing + +- Name: City in the Sky Peahat Train Room Near Outside Central Tower + Region: City in the Sky + Exits: + City in the Sky Peahat Train Room North Near West Garden: Double_Clawshots + City in the Sky Peahat Train Room South Near West Garden: Double_Clawshots + City in the Sky Outside Central Tower Ground: Nothing + +# CITY IN THE SKY OUTSIDE CENTRAL TOWER + +- Name: City in the Sky Outside Central Tower Ground + Region: City in the Sky + Exits: + City in the Sky Outside Central Tower Ledge: Clawshot + City in the Sky Lobby Above Ceiling Fan: Nothing + City in the Sky Peahat Train Room Near Outside Central Tower: Nothing + +- Name: City in the Sky Outside Central Tower Ledge + Region: City in the Sky + Exits: + City in the Sky Outside Central Tower Ropes: Can_Use_Tightrope + +- Name: City in the Sky Outside Central Tower Ropes + Region: City in the Sky + Locations: + City in the Sky Central Outside Ledge Chest: Can_Use_Tightrope and Can_Climb_Vines + City in the Sky Central Outside Poe Island Chest: Can_Use_Tightrope and Can_Climb_Vines + City in the Sky Poe Above Central Fan: Can_Use_Tightrope and Can_Defeat_Poe + Exits: + City in the Sky Lobby Above Highest Grating: Nothing + City in the Sky Outside Central Tower Ground: Nothing + +# CITY IN THE SKY EAST BRIDGE + +- Name: City in the Sky East Bridge Near Lobby + Region: City in the Sky + Events: + Can Spin City in the Sky East Bridge: Spinner + Exits: + City in the Sky Lobby Floor: Nothing + City in the Sky East Bridge Near East Helmasaur Room: "'Can_Spin_City_in_the_Sky_East_Bridge'" + +- Name: City in the Sky East Bridge Near East Helmasaur Room + Region: City in the Sky + Exits: + City in the Sky East Helmasaur Room Top Near East Bridge: City_in_the_Sky_Small_Key or Small_Keys == Keysy + City in the Sky East Bridge Near Lobby: "'Can_Spin_City_in_the_Sky_East_Bridge'" + +- Name: City in the Sky Under East Bridge + Region: City in the Sky + Exits: + City in the Sky East Helmasaur Room Bottom Near East Bridge: Nothing + City in the Sky East Bridge Near Lobby: Double_Clawshots and 'Can_Spin_City_in_the_Sky_East_Bridge' + +# CITY IN THE SKY EAST HELMASAUR ROOM + +- Name: City in the Sky East Helmasaur Room Top Near East Bridge + Region: City in the Sky + Events: + Can Hit City in the Sky East Helmasaur Room Crystal Switch: Can_Hit_Crystal_Switch_at_Range + Exits: + City in the Sky East Helmasaur Room Top Near Tower Before Miniboss: "'Can_Hit_City_in_the_Sky_East_Helmasaur_Room_Crystal_Switch'" + City in the Sky East Bridge Near East Helmasaur Room: Nothing + +- Name: City in the Sky East Helmasaur Room Top Near Tower Before Miniboss + Region: City in the Sky + Exits: + City in the Sky Tower Before Miniboss Higher Caged Area: Nothing + City in the Sky East Helmasaur Room Top Near East Tileworm Room: "'Can_Hit_City_in_the_Sky_East_Helmasaur_Room_Crystal_Switch'" + City in the Sky East Helmasaur Room Top Near East Bridge: "'Can_Hit_City_in_the_Sky_East_Helmasaur_Room_Crystal_Switch'" + +- Name: City in the Sky East Helmasaur Room Top Near East Tileworm Room + Region: City in the Sky + Events: + Can Hit City in the Sky East Helmasaur Room Crystal Switch: Can_Hit_Crystal_Switch_at_Range + Exits: + City in the Sky East Tileworm Room Near East Helmasaur Room: Nothing + City in the Sky East Helmasaur Room Top Near Tower Before Miniboss: Nothing + +- Name: City in the Sky East Helmasaur Room Bottom Near Tower Before Miniboss + Region: City in the Sky + Exits: + City in the Sky East Helmasaur Room Bottom Near East Bridge: Double_Clawshots + City in the Sky Tower Before Miniboss Lower Caged Area: Nothing + +- Name: City in the Sky East Helmasaur Room Bottom Near East Bridge + Region: City in the Sky + Locations: + City in the Sky East Wing Lower Level Chest: Nothing + Exits: + City in the Sky Under East Bridge: Nothing + City in the Sky East Helmasaur Room Bottom Near Tower Before Miniboss: Double_Clawshots + +# CITY IN THE SKY EAST TILEWORM ROOM + +- Name: City in the Sky East Tileworm Room Near East Helmasaur Room + Region: City in the Sky + Locations: + City in the Sky East Tile Worm Small Chest: Nothing + Exits: + City in the Sky East Tileworm Room Near Double Dinalfos Room: Can_Launch_Tileworm + City in the Sky East Helmasaur Room Top Near East Tileworm Room: Nothing + +- Name: City in the Sky East Tileworm Room Near Double Dinalfos Room + Region: City in the Sky + Exits: + City in the Sky Double Dinalfos Room Bottom: Nothing + City in the Sky East Tileworm Room Near East Helmasaur Room: Clawshot or Can_Launch_Tileworm + +# CITY IN THE SKY DOUBLE DINALFOS ROOM + +- Name: City in the Sky Double Dinalfos Room Bottom + Region: City in the Sky + Exits: + City in the Sky Double Dinalfos Room Top: Can_Defeat_Dinalfos and Clawshot + City in the Sky East Tileworm Room Near Double Dinalfos Room: Can_Defeat_Dinalfos # Room locks when you go in + +- Name: City in the Sky Double Dinalfos Room Top + Region: City in the Sky + Exits: + City in the Sky Oocca Flight Room Near Double Dinalfos Room: Nothing + City in the Sky Double Dinalfos Room Bottom: Nothing + +# CITY IN THE SKY OOCCA FLIGHT ROOM + +- Name: City in the Sky Oocca Flight Room Near Double Dinalfos Room + Region: City in the Sky + Locations: + City in the Sky East Wing After Dinalfos Alcove Chest: Clawshot + City in the Sky East Wing After Dinalfos Ledge Chest: Nothing + Exits: + City in the Sky Oocca Flight Room Near Tower Before Miniboss: Clawshot + City in the Sky Double Dinalfos Room Top: Nothing + +- Name: City in the Sky Oocca Flight Room Near Tower Before Miniboss + Region: City in the Sky + Exits: + City in the Sky Tower Before Miniboss Top: Nothing + +# CITY IN THE SKY TOWER BEFORE MINIBOSS + +- Name: City in the Sky Tower Before Miniboss Top + Region: City in the Sky + Events: + Can Open City in the Sky Tower Before Miniboss Upper Gate: Clawshot + Exits: + City in the Sky Tower Before Miniboss Higher Caged Area: "'Can_Open_City_in_the_Sky_Tower_Before_Miniboss_Upper_Gate'" + City in the Sky Tower Before Miniboss Bottom: Nothing + +- Name: City in the Sky Tower Before Miniboss Bottom + Region: City in the Sky + Exits: + Aerolfos Miniboss Room: Nothing + City in the Sky Tower Before Miniboss Lower Caged Area: Double_Clawshots + +- Name: City in the Sky Tower Before Miniboss Higher Caged Area + Region: City in the Sky + Locations: + City in the Sky East First Wing Chest After Fans: Nothing + Exits: + City in the Sky East Helmasaur Room Top Near Tower Before Miniboss: Nothing + City in the Sky Tower Before Miniboss Bottom: "'Can_Open_City_in_the_Sky_Tower_Before_Miniboss_Upper_Gate'" + +- Name: City in the Sky Tower Before Miniboss Lower Caged Area + Region: City in the Sky + Exits: + City in the Sky East Helmasaur Room Bottom Near Tower Before Miniboss: Nothing + +# AEROLFOS MINIBOSS ROOM + +- Name: Aerolfos Miniboss Room + Locations: + City in the Sky Aeralfos Chest: Can_Defeat_Aerolfos + Exits: + City in the Sky Tower Before Miniboss Bottom: Can_Defeat_Aerolfos + +# CITY IN THE SKY NORTH FAN PASSAGEWAY + +- Name: City in the Sky North Fan Passageway Near Lobby + Region: City in the Sky + Exits: + City in the Sky North Fan Passageway Near North Tower: Double_Clawshots and 'Can_Turn_on_City_in_the_Sky_North_Fan' + City in the Sky Lobby Floor: Nothing + +- Name: City in the Sky North Fan Passageway Near North Tower + Region: City in the Sky + Locations: + City in the Sky Chest Behind North Fan: Clawshot + Exits: + City in the Sky North Tower Bottom: Nothing + City in the Sky North Fan Passageway Near Lobby: Double_Clawshots and 'Can_Turn_on_City_in_the_Sky_North_Fan' + +# CITY IN THE SKY NORTH TOWER + +- Name: City in the Sky North Tower Bottom + Region: City in the Sky + Exits: + City in the Sky North Tower Top: Can_Defeat_Aerolfos and Double_Clawshots + City in the Sky North Fan Passageway Near North Tower: Nothing + +- Name: City in the Sky North Tower Top + Exits: + City in the Sky Boss Room: City_in_the_Sky_Big_Key or Big_Keys == Keysy + City in the Sky North Tower Bottom: Nothing + +# CITY IN THE SKY BOSS ROOM + +- Name: City in the Sky Boss Room + Events: + Can Complete City in the Sky: Can_Defeat_Argorok + Locations: + City in the Sky Argorok Heart Container: Can_Defeat_Argorok + City in the Sky Dungeon Reward: Can_Defeat_Argorok + Exits: + City in the Sky Entrance: Can_Defeat_Argorok \ No newline at end of file diff --git a/mods/randomizer/generator/data/world/dungeons/Forest Temple.yaml b/mods/randomizer/generator/data/world/dungeons/Forest Temple.yaml new file mode 100644 index 0000000000..fd51b0f64a --- /dev/null +++ b/mods/randomizer/generator/data/world/dungeons/Forest Temple.yaml @@ -0,0 +1,277 @@ + +# FOREST TEMPLE ENTRANCE ROOM + +- Name: Forest Temple Entrance + Region: Forest Temple + Dungeon Start Area: True + Events: + Can Free Monkey in Entrance Room: Can_Break_Monkey_Cage + Locations: + Forest Temple Entrance Vines Chest: Can_Defeat_Walltula or Clawshot + Exits: + Forest Temple Entrance Ledge Above Monkey Cage: Can_Defeat_Walltula and Can_Defeat_Bokoblin and Can_Break_Monkey_Cage and Can_Climb_Vines + North Faron Woods: Nothing + +- Name: Forest Temple Entrance Ledge Above Monkey Cage + Region: Forest Temple + Exits: + Forest Temple Lobby: Nothing + Forest Temple Entrance: Nothing + +# FOREST TEMPLE LOBBY + +- Name: Forest Temple Lobby + Region: Forest Temple + Locations: + Forest Temple Central Chest Behind Stairs: Gale_Boomerang # Incase the player blocks it by lighting the torches + Forest Temple Central Chest Hanging From Web: Can_Cut_Hanging_Web + Exits: + Forest Temple Lobby North Ledge: Can_Light_Torches + Forest Temple Lobby West Ledge: Clawshot or (Can_Swing_on_Monkeys and 'Can_Free_Monkey_on_Totem') + Forest Temple East Water Room Near Lobby: Can_Swing_on_Monkeys and 'Can_Free_Monkey_in_Entrance_Room' + Forest Temple Entrance Ledge Above Monkey Cage: Nothing + +- Name: Forest Temple Lobby North Ledge + Region: Forest Temple + Locations: + Forest Temple Central North Chest: Nothing + Exits: + Forest Temple Outside Center South Ledge: Nothing + Forest Temple Lobby: Nothing + +- Name: Forest Temple Lobby West Ledge + Region: Forest Temple + Locations: + Forest Temple Hint Sign: Nothing + Exits: + Forest Temple Lobby West Ledge Behind Web: Can_Break_Webs + Forest Temple Lobby: Nothing + +- Name: Forest Temple Lobby West Ledge Behind Web + Region: Forest Temple + Exits: + Forest Temple West Main Room: Nothing + Forest Temple Lobby West Ledge: Can_Break_Webs + +# FOREST TEMPLE WEST MAIN ROOM + +- Name: Forest Temple West Main Room + Region: Forest Temple + Locations: + Forest Temple West Deku Like Chest: Can_Defeat_Walltula + Exits: + Forest Temple West Main Room Ledge near Big Baba Room: Can_Defeat_Walltula + Forest Temple West Main Room Behind Boulder: Can_Pickup_Bomblings + Forest Temple West Main Room Ledge near Outside: Can_Climb_Vines + +- Name: Forest Temple West Main Room Ledge near Big Baba Room + Region: Forest Temple + Exits: + Forest Temple Big Baba Room: Nothing + Forest Temple West Main Room: Nothing + +- Name: Forest Temple West Main Room Behind Boulder + Region: Forest Temple + Exits: + Forest Temple West Tileworm Room: Nothing + Forest Temple West Main Room: Can_Smash + +- Name: Forest Temple West Main Room Ledge near Outside + Region: Forest Temple + Exits: + Forest Temple Outside West Ledge: Nothing + Forest Temple West Main Room: Nothing + +# FOREST TEMPLE BIG BABA ROOM + +- Name: Forest Temple Big Baba Room + Region: Forest Temple + Events: + Can Free Monkey in Big Baba Room: count(Forest_Temple_Small_Key, 4) or Small_Keys == Keysy + Locations: + Forest Temple Big Baba Key: Can_Defeat_Big_Baba + Exits: + Forest Temple West Main Room Ledge near Big Baba Room: Nothing + +# FOREST TEMPLE WEST TILEWORM ROOM +- Name: Forest Temple West Tileworm Room + Region: Forest Temple + Events: + Can Free Monkey in West Tileworm Room: Can_Light_Torches and (count(Forest_Temple_Small_Key, 4) or Small_Keys == Keysy) + Locations: + Forest Temple Totem Pole Chest: Can_Survive_One_Bonk + Forest Temple West Tile Worm Room Vines Chest: Nothing + Forest Temple West Tile Worm Chest Behind Stairs: Gale_Boomerang + Exits: + Forest Temple West Main Room Behind Boulder: Nothing + +# FOREST TEMPLE OUTSIDE WEST AND CENTER AREA + +- Name: Forest Temple Outside Center South Ledge + Region: Forest Temple + Exits: + Forest Temple Outside Center North Ledge: Can_Swing_on_Monkeys and 'Can_Free_Monkey_on_Totem' and + 'Can_Free_Monkey_in_Big_Baba_Room' and 'Can_Free_Monkey_in_West_Tileworm_Room' + Forest Temple Lobby North Ledge: Nothing + +- Name: Forest Temple Outside West Ledge + Region: Forest Temple + Exits: + Forest Temple Outside Center North Ledge: Gale_Boomerang + Forest Temple West Main Room Ledge near Outside: Nothing + +- Name: Forest Temple Outside Center North Ledge + Region: Forest Temple + Events: + Can Free Outside Monkey: Gale_Boomerang + Exits: + Ook Miniboss Room: Nothing + Forest Temple Outside West Ledge: Gale_Boomerang + +# OOK MINIBOSS ROOM + +- Name: Ook Miniboss Room + Locations: + Forest Temple Gale Boomerang: Can_Defeat_Ook + Exits: + Forest Temple Outside Center North Ledge: Gale_Boomerang + +# FOREST TEMPLE EAST WATER ROOM + +- Name: Forest Temple East Water Room Near Lobby + Region: Forest Temple + Exits: + Forest Temple East Water Room: Can_Pickup_Bomblings # Can burn the web with the bombling + Forest Temple Lobby: Nothing + +- Name: Forest Temple East Water Room + Region: Forest Temple + Locations: + Forest Temple Big Key Chest: Gale_Boomerang + Forest Temple East Water Cave Chest: Nothing + Exits: + Forest Temple Second Monkey Outside Room: count(Forest_Temple_Small_Key, 4) or Small_Keys == Keysy + Forest Temple Outside East: Nothing + Forest Temple East Water Room Near Lobby: Can_Break_Webs + +# FOREST TEMPLE SECOND MONKEY OUTSIDE ROOM + +- Name: Forest Temple Second Monkey Outside Room + Region: Forest Temple + Events: + Can Free Monkey on Totem: Can_Survive_Three_Bonks and Can_Defeat_Bokoblin + Locations: + Forest Temple Second Monkey Under Bridge Chest: Nothing + Exits: + Forest Temple East Water Room: count(Forest_Temple_Small_Key, 4) or Small_Keys == Keysy + +# FOREST TEMPLE OUTSIDE EAST + +- Name: Forest Temple Outside East + Region: Forest Temple + Exits: + Forest Temple North Cross Room South Side: Nothing + Forest Temple East Water Room: Nothing + +# FOREST TEMPLE NORTH CROSS ROOM + +- Name: Forest Temple North Cross Room South Side + Region: Forest Temple + Locations: + Forest Temple Windless Bridge Chest: Nothing + Exits: + Forest Temple North Cross Room East Side: Gale_Boomerang + Forest Temple North Cross Room West Side: Gale_Boomerang + Forest Temple North Cross Room North Side: Gale_Boomerang + Forest Temple Outside East: Nothing + +- Name: Forest Temple North Cross Room East Side + Region: Forest Temple + Exits: + Forest Temple East Tileworm Room: count(Forest_Temple_Small_Key, 4) or Small_Keys == Keysy + Forest Temple North Cross Room South Side: Gale_Boomerang + Forest Temple North Cross Room West Side: Nothing + Forest Temple North Cross Room North Side: Gale_Boomerang + +- Name: Forest Temple North Cross Room West Side + Region: Forest Temple + Exits: + Forest Temple Dark Spider Room: Nothing + Forest Temple North Cross Room South Side: Gale_Boomerang + Forest Temple North Cross Room East Side: Nothing + Forest Temple North Cross Room North Side: Gale_Boomerang + +- Name: Forest Temple North Cross Room North Side + Region: Forest Temple + Exits: + Forest Temple Boss Door Room South Side: Nothing + Forest Temple North Cross Room South Side: Gale_Boomerang + Forest Temple North Cross Room East Side: Gale_Boomerang + Forest Temple North Cross Room West Side: Gale_Boomerang + +# FOREST TEMPLE EAST TILEWORM ROOM + +- Name: Forest Temple East Tileworm Room + Region: Forest Temple + Events: + Can Free Monkey in East Tileworm Room: Can_Defeat_Tileworm and Can_Defeat_Skulltula and Can_Defeat_Walltula and Gale_Boomerang # or Tileworm Boost + Locations: + Forest Temple East Tile Worm Chest: Can_Defeat_Tileworm and Can_Defeat_Skulltula and Can_Defeat_Walltula and Gale_Boomerang # or Tileworm Boost + Exits: + Forest Temple North Cross Room East Side: count(Forest_Temple_Small_Key, 4) or Small_Keys == Keysy + +# FOREST TEMPLE DARK SPIDER ROOM + +- Name: Forest Temple Dark Spider Room + Region: Forest Temple + Events: + Can Free Monkey in Dark Spider Room: Can_Break_Webs and Can_Break_Monkey_Cage + Exits: + Forest Temple North Cross Room West Side: Nothing + +# FOREST TEMPLE BOSS DOOR ROOM + +- Name: Forest Temple Boss Door Room South Side + Region: Forest Temple + Exits: + Forest Temple Boss Door Room West Side: Gale_Boomerang or Clawshot + Forest Temple Boss Door Room North Side: Can_Swing_on_Monkeys and Can_Free_All_Monkeys_in_Forest_Temple + +- Name: Forest Temple Boss Door Room West Side + Region: Forest Temple + Exits: + Forest Temple Boss Door Room North Side: Clawshot + Forest Temple North Deku Like Room: Nothing + Forest Temple Boss Door Room South Side: Gale_Boomerang # Can do a jumpslash, but it's a bit precise + +- Name: Forest Temple Boss Door Room North Side + Region: Forest Temple + Events: + Fairy Access: Nothing + Exits: + Forest Temple Boss Room: Forest_Temple_Big_Key or Big_Keys == Keysy + Forest Temple Boss Door Room South Side: Can_Swing_on_Monkeys and Can_Free_All_Monkeys_in_Forest_Temple + Forest Temple Boss Door Room West Side: Clawshot + +# FOREST TEMPLE NORTH DEKU LIKE ROOM + +- Name: Forest Temple North Deku Like Room + Region: Forest Temple + Events: + Can Free Monkey in North Deku Like Room: Gale_Boomerang + Locations: + Forest Temple North Deku Like Chest: Can_Defeat_Deku_Like or Gale_Boomerang + Exits: + Forest Temple Boss Door Room West Side: Nothing + +# FOREST TEMPLE BOSS ROOM + +- Name: Forest Temple Boss Room + Events: + Can Complete Forest Temple: Can_Defeat_Diababa + Locations: + Forest Temple Diababa Heart Container: Can_Defeat_Diababa + Forest Temple Dungeon Reward: Can_Defeat_Diababa + Exits: + South Faron Woods: Can_Defeat_Diababa + \ No newline at end of file diff --git a/mods/randomizer/generator/data/world/dungeons/Goron Mines.yaml b/mods/randomizer/generator/data/world/dungeons/Goron Mines.yaml new file mode 100644 index 0000000000..a0836219f4 --- /dev/null +++ b/mods/randomizer/generator/data/world/dungeons/Goron Mines.yaml @@ -0,0 +1,336 @@ + +# GORON MINES ENTRANCE ROOM + +- Name: Goron Mines Entrance + Region: Goron Mines + Dungeon Start Area: True + Exits: + Goron Mines Entrance Room Upper Platforms: Can_Break_Wooden_Barrier and Iron_Boots + Death Mountain Sumo Hall Goron Mines Tunnel: Nothing + +- Name: Goron Mines Entrance Room Upper Platforms + Region: Goron Mines + Events: + Can Open Goron Mines Entrance Room Iron Gate: Iron_Boots + Locations: + Goron Mines Entrance Chest: Nothing + Exits: + Goron Mines Entrance Room Near Central Magnet Room: "'Can_Open_Goron_Mines_Entrance_Room_Iron_Gate'" + Goron Mines Entrance: Nothing + +- Name: Goron Mines Entrance Room Near Central Magnet Room # Behind the gate that opens + Region: Goron Mines + Exits: + Goron Mines Central Magnet Room Near Entrance: Nothing + Goron Mines Entrance Room Upper Platforms: "'Can_Open_Goron_Mines_Entrance_Room_Iron_Gate'" + +# GORON MINES CENTRAL MAGNET ROOM + +- Name: Goron Mines Central Magnet Room Near Entrance + Region: Goron Mines + Locations: + Goron Mines Main Magnet Room Bottom Chest: Nothing + Exits: + Goron Mines Magnet Ceiling Room Lower: Goron_Mines_Small_Key or Small_Keys == Keysy + Goron Mines Central Magnet Room Center Tower: (Active_Goron_Mines_Magnets == On and Iron_Boots) or 'Can_Activate_Central_Tower_Magnet' + Goron Mines Entrance Room Near Central Magnet Room: Nothing + +- Name: Goron Mines Central Magnet Room Center Tower + Region: Goron Mines + Events: + Can Activate Central Tower Magnet: Iron_Boots + Exits: + Goron Mines Central Magnet Room Near Crystal Switch Room: "'Can_Activate_Central_Tower_Magnet'" + Goron Mines Central Magnet Room Near Entrance: "'Can_Activate_Central_Tower_Magnet'" + Goron Mines Magnet Ceiling Room Near Central Magnet Room: Nothing + +- Name: Goron Mines Central Magnet Room Near Crystal Switch Room + Region: Goron Mines + Exits: + Goron Mines Crystal Switch Room Water Side: Nothing + Goron Mines Central Magnet Room Center Tower: "'Can_Activate_Central_Tower_Magnet'" + Goron Mines Central Magnet Room Near Ceiling Dodongo Room: "'Can_Activate_Highest_Central_Magnet'" + +- Name: Goron Mines Central Magnet Room Near Ceiling Dodongo Room + Region: Goron Mines + Events: + Can Activate Highest Central Magnet: Bow and Iron_Boots + Locations: + Goron Mines Main Magnet Room Top Chest: Nothing + Exits: + Goron Mines Central Magnet Room Near Crystal Switch Room: "'Can_Activate_Highest_Central_Magnet'" + Goron Mines Ceiling Dodongo Room Near Central Magnet Room: Nothing + +# GORON MINES MAGNET CEILING ROOM + +- Name: Goron Mines Magnet Ceiling Room Lower + Region: Goron Mines + Exits: + Goron Mines Magnet Ceiling Room Lower Past Stone Wall: Nothing + Goron Mines Central Magnet Room Near Entrance: Nothing + +- Name: Goron Mines Magnet Ceiling Room Lower Past Stone Wall + Region: Goron Mines + Exits: + Goron Mines First Magnet Floor Room Lower: Nothing + +- Name: Goron Mines Magnet Ceiling Room Upper Near First Magnet Floor Room + Region: Goron Mines + Exits: + Goron Mines Magnet Ceiling Room Ceiling: Iron_Boots + Goron Mines Magnet Ceiling Room Lower: Nothing + +- Name: Goron Mines Magnet Ceiling Room Ceiling + Region: Goron Mines + Locations: + Goron Mines Magnet Maze Chest: Nothing + Exits: + Goron Mines Magnet Ceiling Room Near Central Magnet Room: Nothing + Goron Mines Magnet Ceiling Room Upper Near First Magnet Floor Room: Nothing + Goron Mines Magnet Ceiling Room Lower: Nothing + +- Name: Goron Mines Magnet Ceiling Room Near Central Magnet Room + Region: Goron Mines + Exits: + Goron Mines Central Magnet Room Center Tower: Nothing + Goron Mines Magnet Ceiling Room Lower: Nothing + +# GORON MINES FIRST MAGNET FLOOR ROOM + +- Name: Goron Mines First Magnet Floor Room Lower + Region: Goron Mines + Exits: + Goron Mines First Magnet Floor Room Lower Near Gor Amatos Room: Iron_Boots + +- Name: Goron Mines First Magnet Floor Room Lower Near Gor Amatos Room + Region: Goron Mines + Exits: + Goron Mines Gor Amato Room Lower: Nothing + Goron Mines First Magnet Floor Room Lower: Nothing + +- Name: Goron Mines First Magnet Floor Room Upper Near Gor Amatos Room + Region: Goron Mines + Exits: + Goron Mines First Magnet Floor Room Upper Near Magnet Ceiling Room: Iron_Boots + Goron Mines First Magnet Floor Room Lower Near Gor Amatos Room: Nothing + Goron Mines Gor Amato Room Upper: Nothing + +- Name: Goron Mines First Magnet Floor Room Upper Near Magnet Ceiling Room + Region: Goron Mines + Exits: + Goron Mines Magnet Ceiling Room Upper Near First Magnet Floor Room: Nothing + Goron Mines First Magnet Floor Room Upper Near Gor Amatos Room: Iron_Boots + Goron Mines First Magnet Floor Room Lower: Nothing + +# GORON MINES GOR AMATO ROOM + +- Name: Goron Mines Gor Amato Room Lower + Region: Goron Mines + Locations: + Goron Mines Gor Amato Chest: Nothing + Goron Mines Gor Amato Small Chest: Nothing + Goron Mines Gor Amato Key Shard: Can_Talk_to_Humans + Exits: + Goron Mines Gor Amato Room Upper: Can_Climb_Ladders + Goron Mines First Magnet Floor Room Lower Near Gor Amatos Room: Nothing + +- Name: Goron Mines Gor Amato Room Upper + Region: Goron Mines + Exits: + Goron Mines First Magnet Floor Room Upper Near Gor Amatos Room: Nothing + Goron Mines Gor Amato Room Lower: Nothing + +# GORON MINES CRYSTAL SWITCH ROOM + +- Name: Goron Mines Crystal Switch Room Water Side + Region: Goron Mines + Locations: + Goron Mines Crystal Switch Room Underwater Chest: Iron_Boots + Goron Mines Crystal Switch Room Small Chest: Iron_Boots + Exits: + Goron Mines Crystal Switch Room Beamos Side: Can_Hit_Crystal_Switch_at_Range or (Can_Hit_Crystal_Switch and Iron_Boots) + Goron Mines Central Magnet Room Near Crystal Switch Room: Nothing + +- Name: Goron Mines Crystal Switch Room Beamos Side + Region: Goron Mines + Locations: + Goron Mines After Crystal Switch Room Magnet Wall Chest: Iron_Boots + Exits: + Goron Mines Crystal Switch Room Near Outside Room: Bow or (Sword and Iron_Boots) + Goron Mines Crystal Switch Room Water Side: Can_Hit_Crystal_Switch + +- Name: Goron Mines Crystal Switch Room Near Outside Room + Region: Goron Mines + Exits: + Goron Mines Outside Room: count(Goron_Mines_Small_Key, 2) or Small_Keys == Keysy + Goron Mines Crystal Switch Room Beamos Side: Nothing + +# GORON MINES OUTSIDE ROOM + +- Name: Goron Mines Outside Room + Region: Goron Mines + Locations: + Goron Mines Outside Beamos Chest: Nothing + Goron Mines Outside Underwater Chest: Iron_Boots and (Sword or Water_Bombs) # Can also just swim over the barrier + Goron Mines Outside Clawshot Chest: Clawshot and (Bow or Slingshot) + Exits: + Goron Mines Floor Turning Room Near Outside Room: count(Goron_Mines_Small_Key, 3) or Small_Keys == Keysy + Goron Mines Outside Room Near Boss Door Room: Clawshot or (Can_Defeat_Beamos and Iron_Boots and Bow) + +- Name: Goron Mines Outside Room Near Boss Door Room + Region: Goron Mines + Exits: + Goron Mines Boss Door Room Near Outside Room: Nothing + Goron Mines Outside Room: Nothing + +# GORON MINES FLOOR TURNING ROOM + +- Name: Goron Mines Floor Turning Room Near Outside Room + Region: Goron Mines + Exits: + Goron Mines Floor Turning Room Near Gor Ebizo Room Lower: Iron_Boots + Goron Mines Outside Room: Nothing + +- Name: Goron Mines Floor Turning Room Near Gor Ebizo Room Lower + Region: Goron Mines + Exits: + Goron Mines Gor Ebizo Room Lower: Nothing + Goron Mines Floor Turning Room Near Outside Room: Nothing + +- Name: Goron Mines Floor Turning Room Near Gor Ebizo Room Upper + Region: Goron Mines + Exits: + Goron Mines Floor Turning Room Near Miniboss Room: Iron_Boots + Goron Mines Floor Turning Room Near Gor Ebizo Room Lower: Nothing + Goron Mines Floor Turning Room Near Outside Room: Nothing + Goron Mines Gor Ebizo Room Upper: Nothing + +- Name: Goron Mines Floor Turning Room Near Miniboss Room + Region: Goron Mines + Locations: + Goron Mines Chest Before Dangoro: Nothing + Exits: + Dangoro Miniboss Room North Side: Nothing + Goron Mines Floor Turning Room Near Outside Room: Nothing + +# GORON MINES GOR EBIZO ROOM + +- Name: Goron Mines Gor Ebizo Room Lower + Region: Goron Mines + Locations: + Goron Mines Gor Ebizo Chest: Nothing + Goron Mines Gor Ebizo Key Shard: Can_Talk_to_Humans + Goron Mines Hint Sign: Nothing + Exits: + Goron Mines Gor Ebizo Room Upper: Can_Climb_Ladders + Goron Mines Floor Turning Room Near Gor Ebizo Room Lower: Nothing + +- Name: Goron Mines Gor Ebizo Room Upper + Region: Goron Mines + Exits: + Goron Mines Floor Turning Room Near Gor Ebizo Room Upper: Nothing + Goron Mines Gor Ebizo Room Lower: Nothing + +# DANGORO MINIBOSS ROOM + +- Name: Dangoro Miniboss Room North Side + Exits: + Dangoro Miniboss Room South Side: Can_Defeat_Dangoro + Goron Mines Floor Turning Room Near Miniboss Room: Nothing + +- Name: Dangoro Miniboss Room South Side + Exits: + Goron Mines Beamos Circle Room Near Miniboss Room: Nothing + Dangoro Miniboss Room North Side: Can_Defeat_Dangoro + +# GORON MINES BEAMOS CIRCLE ROOM + +- Name: Goron Mines Beamos Circle Room Near Miniboss Room + Region: Goron Mines + Events: + Can Cut Down Iron Platform in Beamos Circle Room: Bow + Locations: + Goron Mines Dangoro Chest: Nothing + Exits: + Goron Mines Beamos Circle Room Center: "'Can_Cut_Down_Iron_Platform_in_Beamos_Circle_Room'" + Dangoro Miniboss Room South Side: Nothing + +- Name: Goron Mines Beamos Circle Room Center + Region: Goron Mines + Events: + Can Kill Beamos in Beamos Circle Room: Can_Defeat_Beamos + Locations: + Goron Mines Beamos Room Chest: "'Can_Kill_Beamos_in_Beamos_Circle_Room'" + Exits: + Goron Mines Beamos Circle Room Near Gor Liggs Room: "'Can_Kill_Beamos_in_Beamos_Circle_Room'" + Goron Mines Beamos Circle Room Near Ceiling Dodongo Room: "'Can_Kill_Beamos_in_Beamos_Circle_Room'" + Goron Mines Beamos Circle Room Near Miniboss Room: "'Can_Cut_Down_Iron_Platform_in_Beamos_Circle_Room'" + +- Name: Goron Mines Beamos Circle Room Near Gor Liggs Room + Region: Goron Mines + Exits: + Goron Mines Gor Liggs Room: Nothing + Goron Mines Beamos Circle Room Center: "'Can_Kill_Beamos_in_Beamos_Circle_Room'" + +- Name: Goron Mines Beamos Circle Room Near Ceiling Dodongo Room + Region: Goron Mines + Exits: + Goron Mines Ceiling Dodongo Room Near Beamos Circle Room: Nothing + Goron Mines Beamos Circle Room Center: "'Can_Kill_Beamos_in_Beamos_Circle_Room'" + +# GORON MINES GOR LIGGS ROOM + +- Name: Goron Mines Gor Liggs Room + Region: Goron Mines + Locations: + Goron Mines Gor Liggs Chest: Nothing + Goron Mines Gor Liggs Key Shard: Can_Talk_to_Humans + Exits: + Goron Mines Beamos Circle Room Near Gor Liggs Room: Nothing + +# GORON MINES CEILING DODONGO ROOM + +- Name: Goron Mines Ceiling Dodongo Room Near Beamos Circle Room + Region: Goron Mines + Exits: + Goron Mines Ceiling Dodongo Room Near Central Magnet Room: Gale_Boomerang or Clawshot or Bow # to get rid of ceiling torch slugs + Goron Mines Beamos Circle Room Near Ceiling Dodongo Room: Nothing + +- Name: Goron Mines Ceiling Dodongo Room Near Ceiling Dodongo + Region: Goron Mines + Events: + Can Hit Ceiling Dodongo Switch: Iron_Boots and Bow + Exits: + Goron Mines Ceiling Dodongo Room Near Central Magnet Room: "'Can_Hit_Ceiling_Dodongo_Switch'" + Goron Mines Ceiling Dodongo Room Near Beamos Circle Room: Gale_Boomerang or Clawshot or Bow # to get rid of ceiling torch slugs + +- Name: Goron Mines Ceiling Dodongo Room Near Central Magnet Room + Region: Goron Mines + Exits: + Goron Mines Central Magnet Room Near Ceiling Dodongo Room: Nothing + Goron Mines Ceiling Dodongo Room Near Ceiling Dodongo: "'Can_Hit_Ceiling_Dodongo_Switch'" + +# GORON MINES BOSS DOOR ROOM + +- Name: Goron Mines Boss Door Room Near Outside Room + Region: Goron Mines + Exits: + Goron Mines Boss Door Room Near Boss Door: Bow + Goron Mines Outside Room Near Boss Door Room: Nothing + +- Name: Goron Mines Boss Door Room Near Boss Door + Region: Goron Mines + Exits: + Goron Mines Boss Room: count(Goron_Mines_Key_Shard, 3) or Big_Keys == Keysy + Goron Mines Boss Door Room Near Outside Room: Bow + +# GORON MINES BOSS ROOM + +- Name: Goron Mines Boss Room + Events: + Can Complete Goron Mines: Can_Defeat_Fyrus + Locations: + Goron Mines Fyrus Heart Container: Can_Defeat_Fyrus + Goron Mines Dungeon Reward: Can_Defeat_Fyrus + Exits: + Lower Kakariko Village: Can_Defeat_Fyrus diff --git a/mods/randomizer/generator/data/world/dungeons/Hyrule Castle.yaml b/mods/randomizer/generator/data/world/dungeons/Hyrule Castle.yaml new file mode 100644 index 0000000000..5d165c2ab3 --- /dev/null +++ b/mods/randomizer/generator/data/world/dungeons/Hyrule Castle.yaml @@ -0,0 +1,215 @@ +# HYRULE CASTLE ENTRANCE + +- Name: Hyrule Castle Entrance + Region: Hyrule Castle + Dungeon Start Area: True + Locations: + Hyrule Castle Hint Sign: Nothing + Exits: + Hyrule Castle Entrance Near West Courtyard: Can_Defeat_Red_Bokoblin + Hyrule Castle Entrance Near East Courtyard: Can_Defeat_Red_Bokoblin + Hyrule Castle Main Hall Near Entrance: Can_Open_Doors and (Hyrule_Castle_Small_Key or Small_Keys == Keysy) + Castle Town North Inside Barrier: Nothing + +- Name: Hyrule Castle Entrance Near West Courtyard + Region: Hyrule Castle + Exits: + Hyrule Castle West Courtyard: Nothing + Hyrule Castle Entrance: Can_Defeat_Red_Bokoblin + +- Name: Hyrule Castle Entrance Near East Courtyard + Region: Hyrule Castle + Exits: + Hyrule Castle East Courtyard Near Entrance: Nothing + Hyrule Castle Entrance: Can_Defeat_Red_Bokoblin + +# HYRULE CASTLE WEST COURTYARD + +- Name: Hyrule Castle West Courtyard + Region: Hyrule Castle + Locations: + Hyrule Castle West Courtyard North Small Chest: Can_Defeat_Bokoblin + Hyrule Castle West Courtyard Central Small Chest: Can_Defeat_Bokoblin + Hyrule Castle King Bulblin Key: Can_Defeat_Bokoblin and Can_Defeat_King_Bulblin_Castle + +# HYRULE CASTLE EAST COURTYARD + +- Name: Hyrule Castle East Courtyard Near Entrance + Region: Hyrule Castle + Exits: + Hyrule Castle East Courtyard Near Graveyard: Human_Link # For riding the boar + Hyrule Castle Entrance Near East Courtyard: Nothing + +- Name: Hyrule Castle East Courtyard Near Graveyard + Region: Hyrule Castle + Locations: + Hyrule Castle East Wing Boomerang Puzzle Chest: Gale_Boomerang + Hyrule Castle East Wing Balcony Chest: Gale_Boomerang and Can_Climb_Ladders + Exits: + Hyrule Castle Graveyard: Can_Dig + Hyrule Castle East Courtyard Near Entrance: Gale_Boomerang and Can_Climb_Ladders + +# HYRULE CASTLE GRAVEYARD + +- Name: Hyrule Castle Graveyard + Region: Hyrule Castle + Events: + Can Refill Lantern Oil: Can_Smash + Locations: + Hyrule Castle Graveyard Grave Switch Room Right Chest: Can_Smash + Hyrule Castle Graveyard Grave Switch Room Front Left Chest: Can_Smash + Hyrule Castle Graveyard Grave Switch Room Back Left Chest: Can_Smash + Hyrule Castle Graveyard Owl Statue Chest: Can_Smash and Lantern and Restored_Dominion_Rod + +# HYRULE CASTLE MAIN HALL + +- Name: Hyrule Castle Main Hall Near Entrance + Region: Hyrule Castle + Exits: + Hyrule Castle Main Hall Bottom: Can_Defeat_Bokoblin and Can_Defeat_Lizalfos + Hyrule Castle Entrance: Nothing + +- Name: Hyrule Castle Main Hall Bottom + Region: Hyrule Castle + Locations: + Hyrule Castle Main Hall Northeast Chest: Clawshot and (Lantern or (Can_Defeat_Bokoblin and Can_Defeat_Lizalfos)) + Exits: + Hyrule Castle Main Hall Near Entrance: Can_Defeat_Bokoblin and Can_Defeat_Lizalfos + Hyrule Castle Main Hall Near First Darknut Room: Double_Clawshots + Hyrule Castle Main Hall Near Double Dinalfos Room: Double_Clawshots and Lower_Hyrule_Castle_Chandelier == On + +- Name: Hyrule Castle Main Hall Near First Darknut Room + Region: Hyrule Castle + Exits: + Hyrule Castle First Darknut Room Near Main Hall: Can_Open_Doors + Hyrule Castle Main Hall Bottom: Nothing + +- Name: Hyrule Castle Main Hall Near Double Darknut Room + Region: Hyrule Castle + Locations: + Hyrule Castle Main Hall Southwest Chest: Nothing + Hyrule Castle Main Hall Northwest Chest: Double_Clawshots + Exits: + Hyrule Castle Main Hall Bottom: Nothing + Hyrule Castle Double Darknut Room: Can_Open_Doors and 'Can_Defeat_Hyrule_Castle_Double_Darknuts' + +- Name: Hyrule Castle Main Hall Near Double Dinalfos Room + Region: Hyrule Castle + Exits: + Hyrule Castle Double Dinalfos Room: Can_Open_Doors and 'Can_Defeat_Hyrule_Castle_Double_Dinalfos' + Hyrule Castle Main Hall Bottom: Nothing + + +# HYRULE CASTLE FIRST DARKNUT ROOM + +- Name: Hyrule Castle First Darknut Room Near Main Hall + Region: Hyrule Castle + Locations: + Hyrule Castle Lantern Staircase Chest: Can_Defeat_Darknut and Lantern and Gale_Boomerang + Exits: + Hyrule Castle First Darknut Room Past Lantern Staircase: Can_Defeat_Darknut and Lantern and Gale_Boomerang + Hyrule Castle Main Hall Near First Darknut Room: Can_Open_Doors + +- Name: Hyrule Castle First Darknut Room Past Lantern Staircase + Region: Hyrule Castle + Exits: + Hyrule Castle Torch Puzzle Room: Can_Open_Doors + Hyrule Castle Hanging Painting Room: Can_Open_Doors + Hyrule Castle First Darknut Room Near Main Hall: Can_Defeat_Darknut + +# HYRULE CASTLE HANGING PAINTING ROOM + +- Name: Hyrule Castle Hanging Painting Room + Region: Hyrule Castle + Exits: + Hyrule Castle Double Darknut Room: Can_Open_Doors and Can_Knock_Down_Hyrule_Castle_Painting + Hyrule Castle First Darknut Room Past Lantern Staircase: Can_Open_Doors + +# HYRULE CASTLE DOUBLE DARKNUT ROOM + +- Name: Hyrule Castle Double Darknut Room + Region: Hyrule Castle + Events: + Can Defeat Hyrule Castle Double Darknuts: Can_Defeat_Darknut + Exits: + Hyrule Castle Main Hall Near Double Darknut Room: Can_Open_Doors and 'Can_Defeat_Hyrule_Castle_Double_Darknuts' + Hyrule Castle Outside Balcony: Can_Open_Doors and 'Can_Defeat_Hyrule_Castle_Double_Darknuts' + Hyrule Castle Hanging Painting Room: Can_Open_Doors + +# HYRULE CASTLE TORCH PUZZLE ROOM + +- Name: Hyrule Castle Torch Puzzle Room + Region: Hyrule Castle + Exits: + Hyrule Castle Double Dinalfos Room: Can_Open_Doors and Lantern + Hyrule Castle First Darknut Room Past Lantern Staircase: Can_Open_Doors + +# HYRULE CASTLE DOUBLE DINALFOS ROOM + +- Name: Hyrule Castle Double Dinalfos Room + Region: Hyrule Castle + Events: + Can Defeat Hyrule Castle Double Dinalfos: Can_Defeat_Dinalfos + Exits: + Hyrule Castle Outside Balcony: Can_Open_Doors and 'Can_Defeat_Hyrule_Castle_Double_Dinalfos' + Hyrule Castle Main Hall Near Double Dinalfos Room: Can_Open_Doors and 'Can_Defeat_Hyrule_Castle_Double_Dinalfos' + Hyrule Castle Torch Puzzle Room: Can_Open_Doors + +# HYRULE CASTLE OUTSIDE BALCONY + +- Name: Hyrule Castle Outside Balcony + Region: Hyrule Castle + Locations: + Hyrule Castle Southeast Balcony Tower Chest: Can_Defeat_Aerolfos + Hyrule Castle Big Key Chest: Can_Open_Hyrule_Castle_Big_Key_Gate + Exits: + Hyrule Castle Final Climb Near Outside Balcony: Can_Open_Doors and (count(Hyrule_Castle_Small_Key, 2) or Small_Keys == Keysy) + Hyrule Castle Double Darknut Room: Can_Open_Doors and 'Can_Defeat_Hyrule_Castle_Double_Darknuts' + Hyrule Castle Double Dinalfos Room: Can_Open_Doors and 'Can_Defeat_Hyrule_Castle_Double_Dinalfos' + +# HYRULE CASTLE FINAL CLIMB + +- Name: Hyrule Castle Final Climb Near Outside Balcony + Region: Hyrule Castle + Events: + Follow Ghost Soldiers: Can_Use_Senses + Exits: + Hyrule Castle Final Climb Top: "'Follow_Ghost_Soldiers' and Can_Defeat_Dinalfos and Can_Defeat_Darknut and Double_Clawshots and Spinner" + Hyrule Castle Outside Balcony: Can_Open_Doors + +- Name: Hyrule Castle Final Climb Top + Region: Hyrule Castle + Events: + Follow Ghost Soldiers: Can_Use_Senses + Exits: + Hyrule Castle Treasure Room: Can_Open_Doors and (count(Hyrule_Castle_Small_Key, 3) or Small_Keys == Keysy) + Hyrule Castle Throne Room: Hyrule_Castle_Big_Key or Big_Keys == Keysy + Hyrule Castle Final Climb Near Outside Balcony: "'Follow_Ghost_Soldiers' and Can_Defeat_Dinalfos and Can_Defeat_Darknut and Double_Clawshots and Spinner" + +# HYRULE CASTLE TREASURE ROOM + +- Name: Hyrule Castle Treasure Room + Region: Hyrule Castle + Locations: + Hyrule Castle Treasure Room First Small Chest: Nothing + Hyrule Castle Treasure Room Second Small Chest: Nothing + Hyrule Castle Treasure Room Third Small Chest: Nothing + Hyrule Castle Treasure Room Fourth Small Chest: Nothing + Hyrule Castle Treasure Room Fifth Small Chest: Nothing + Hyrule Castle Treasure Room Sixth Small Chest: Nothing + Hyrule Castle Treasure Room Seventh Small Chest: Nothing + Hyrule Castle Treasure Room Eighth Small Chest: Nothing + Hyrule Castle Treasure Room First Chest: Nothing + Hyrule Castle Treasure Room Second Chest: Nothing + Hyrule Castle Treasure Room Third Chest: Nothing + Hyrule Castle Treasure Room Fourth Chest: Nothing + Hyrule Castle Treasure Room Fifth Chest: Nothing + Exits: + Hyrule Castle Final Climb Top: Can_Open_Doors + +# HYRULE CASTLE THRONE ROOM + +- Name: Hyrule Castle Throne Room + Region: Hyrule Castle + Locations: + Defeat Ganondorf: Master_Sword and Wolf_Link and Ending_Blow diff --git a/mods/randomizer/generator/data/world/dungeons/Lakebed Temple.yaml b/mods/randomizer/generator/data/world/dungeons/Lakebed Temple.yaml new file mode 100644 index 0000000000..5c6b82ca71 --- /dev/null +++ b/mods/randomizer/generator/data/world/dungeons/Lakebed Temple.yaml @@ -0,0 +1,438 @@ + +# LAKEBED TEMPLE LOBBY + +- Name: Lakebed Temple Entrance + Region: Lakebed Temple + Dungeon Start Area: True + Exits: + Lakebed Temple Lobby: Zora_Armor + Lake Hylia Lakebed Temple Entrance: Nothing + +- Name: Lakebed Temple Lobby + Region: Lakebed Temple + Events: + Can Pull Lakebed Lobby Lever: Can_Pull_Lakebed_Levers + Locations: + Lakebed Temple Lobby Left Chest: Nothing + Lakebed Temple Lobby Rear Chest: Nothing + Exits: + Lakebed Temple Lobby Near Stalactite Room: "'Can_Pull_Lakebed_Lobby_Lever'" + Lakebed Temple Entrance: Zora_Armor + +- Name: Lakebed Temple Lobby Near Stalactite Room + Region: Lakebed Temple + Exits: + Lakebed Temple Stalactite Room Near Lobby: Nothing + Lakebed Temple Lobby: "'Can_Pull_Lakebed_Lobby_Lever'" + +# LAKEBED TEMPLE STALACTITE ROOM + +- Name: Lakebed Temple Stalactite Room Near Lobby + Region: Lakebed Temple + Exits: + Lakebed Temple Lobby Near Stalactite Room: Nothing + Lakebed Temple Stalactite Room Near Circling Current Room: Can_Launch_Bombs + +- Name: Lakebed Temple Stalactite Room Near Circling Current Room + Region: Lakebed Temple + Locations: + Lakebed Temple Stalactite Room Chest: Can_Launch_Bombs + Exits: + Lakebed Temple Circling Current Room South: Nothing + Lakebed Temple Stalactite Room Near Lobby: Nothing + +# LAKEBED TEMPLE CIRCLING CURRENT ROOM + +- Name: Lakebed Temple Circling Current Room South + Region: Lakebed Temple + Exits: + Lakebed Temple Central Room: Can_Open_Doors + Lakebed Temple Stalactite Room Near Circling Current Room: Nothing + +- Name: Lakebed Temple Circling Current Room East Lower + Region: Lakebed Temple + Exits: + Lakebed Temple East Waterwheel Room First Floor Near Circling Current Room: Nothing + Lakebed Temple Central Room: Nothing + +- Name: Lakebed Temple Circling Current Room East Upper + Region: Lakebed Temple + Exits: + Lakebed Temple Outside East Waterwheel Room Second Floor Near Circling Current Room: Nothing + Lakebed Temple Central Room: Nothing + +- Name: Lakebed Temple Circling Current Room West Lower Near Central Room + Region: Lakebed Temple + Locations: + Lakebed Temple Hint Sign: Nothing + Exits: + Lakebed Temple Circling Current Room West Lower Near West Waterwheel Room Lower: "'Can_Pull_East_Water_Supply_Lever' and 'Can_Turn_Lakebed_Staircase_with_Clawshot'" + Lakebed Temple Central Room: Nothing + +- Name: Lakebed Temple Circling Current Room West Lower Near West Waterwheel Room Lower + Region: Lakebed Temple + Exits: + Lakebed Temple Circling Current Room West Lower Near Central Room: "'Can_Pull_East_Water_Supply_Lever' and 'Can_Turn_Lakebed_Staircase_with_Clawshot'" + Lakebed Temple West Waterwheel Room Lower: Nothing + +- Name: Lakebed Temple Circling Current Room West Upper Near Central Room + Region: Lakebed Temple + Exits: + Lakebed Temple Circling Current Room West Upper Near West Waterwheel Room Upper: "'Can_Pull_West_Water_Supply_Lever'" + Lakebed Temple Central Room: Nothing + +- Name: Lakebed Temple Circling Current Room West Upper Near West Waterwheel Room Upper + Region: Lakebed Temple + Exits: + Lakebed Temple Circling Current Room West Upper Near Central Room: "'Can_Pull_West_Water_Supply_Lever'" + Lakebed Temple Outside West Waterwheel Room Upper Near Circling Current Room: Nothing + +# LAKEBED TEMPLE CENTRAL ROOM + +# We're just going to assume that you need to be human for everything in this room +# because I don't feel like splitting this room up into 5 different sections to account +# for two edge cases as wolf link that aren't going to be relevant any time soon +- Name: Lakebed Temple Central Room + Region: Lakebed Temple + Events: + Can Turn Lakebed Staircase: Human_Link + Can Turn Lakebed Staircase with Clawshot: Clawshot + Can Open Lakebed Temple Boss Door: (Lakebed_Temple_Big_Key or Big_Keys == Keysy) and 'Can_Pull_West_Water_Supply_Lever' and 'Can_Pull_East_Water_Supply_Lever' + Locations: + Lakebed Temple Central Room Small Chest: Human_Link + Lakebed Temple Central Room Chest: Human_Link + Lakebed Temple Chandelier Chest: Clawshot + Lakebed Temple Central Room Spire Chest: Iron_Boots and ('Can_Pull_West_Water_Supply_Lever' or 'Can_Pull_East_Water_Supply_Lever') + Exits: + Lakebed Temple Circling Current Room East Lower: "'Can_Turn_Lakebed_Staircase'" + Lakebed Temple Circling Current Room East Upper: "'Can_Turn_Lakebed_Staircase' and (Lakebed_Temple_Small_Key or Small_Keys == Keysy)" + Lakebed Temple Circling Current Room West Lower Near Central Room: "'Can_Turn_Lakebed_Staircase'" + Lakebed Temple Circling Current Room West Upper Near Central Room: "'Can_Turn_Lakebed_Staircase'" + Lakebed Temple Circling Current Room South: Can_Open_Doors and 'Can_Turn_Lakebed_Staircase' + Lakebed Temple Central Room Past Boss Door: "'Can_Open_Lakebed_Temple_Boss_Door'" + +- Name: Lakebed Temple Central Room Past Boss Door + Region: Lakebed Temple + Exits: + Lakebed Temple Boss Room: Nothing + Lakebed Temple Central Room: "'Can_Open_Lakebed_Temple_Boss_Door'" + +# LAKEBED TEMPLE EAST WATERWHEEL ROOM FIRST FLOOR + +- Name: Lakebed Temple East Waterwheel Room First Floor Near Circling Current Room + Region: Lakebed Temple + Exits: + Lakebed Temple East Waterwheel Room Waterwheel: "'Can_Pull_East_Water_Supply_Lever'" + Lakebed Temple East Waterwheel Room First Floor Lowest: Nothing + Lakebed Temple Circling Current Room East Lower: Nothing + +- Name: Lakebed Temple East Waterwheel Room Waterwheel # Being on the turning waterwheel + Region: Lakebed Temple + Exits: + Lakebed Temple East Waterwheel Room First Floor Near Circling Current Room: Nothing + Lakebed Temple East Waterwheel Room First Floor Near Water Jet Room Land Side: Nothing + Lakebed Temple East Waterwheel Room First Floor Near Water Jet Room Water Side: Nothing + +- Name: Lakebed Temple East Waterwheel Room First Floor Near Water Jet Room Land Side + Region: Lakebed Temple + Exits: + Lakebed Temple Water Jet Room Land Side: Nothing + Lakebed Temple East Waterwheel Room Waterwheel: "'Can_Pull_East_Water_Supply_Lever'" + Lakebed Temple East Waterwheel Room First Floor Lowest: Nothing + Lakebed Temple East Waterwheel Room First Floor Near Circling Current Room: Nothing # Can jump down to the right + +- Name: Lakebed Temple East Waterwheel Room First Floor Near Water Jet Room Water Side + Region: Lakebed Temple + Exits: + Lakebed Temple Water Jet Room Water Side Near East Waterwheel Room: Nothing + Lakebed Temple East Waterwheel Room Waterwheel: "'Can_Pull_East_Water_Supply_Lever'" + Lakebed Temple East Waterwheel Room First Floor Lowest: Nothing + +- Name: Lakebed Temple East Waterwheel Room First Floor Lowest + Region: Lakebed Temple + Locations: + Lakebed Temple East Lower Waterwheel Stalactite Chest: Can_Launch_Bombs and Can_Climb_Vines + Lakebed Temple East Lower Waterwheel Bridge Chest: Clawshot and 'Can_Pull_West_Water_Supply_Lever' and 'Can_Turn_Lakebed_Staircase' + Exits: + Lakebed Temple East Waterwheel Room First Floor Near Circling Current Room: Can_Climb_Vines or ('Can_Pull_West_Water_Supply_Lever' and 'Can_Turn_Lakebed_Staircase') + +# LAKEBED TEMPLE OUTSIDE EAST WATERWHEEL ROOM SECOND FLOOR + +- Name: Lakebed Temple Outside East Waterwheel Room Second Floor Near Circling Current Room + Region: Lakebed Temple + Events: + Can Pull Outside East Waterwheel Room Second Floor Lever: Clawshot or (Can_Launch_Bombs and Can_Climb_Vines) + Locations: + Lakebed Temple East Second Floor Southwest Chest: Nothing + Exits: + Lakebed Temple Outside East Waterwheel Room Second Floor North: Clawshot or (Can_Launch_Bombs and Can_Climb_Vines) + Lakebed Temple Circling Current Room East Upper: Nothing + +- Name: Lakebed Temple Outside East Waterwheel Room Second Floor North + Region: Lakebed Temple + Exits: + Lakebed Temple Outside East Waterwheel Room Second Floor North Near East Water Supply Room: Can_Smash + Lakebed Temple East Waterwheel Room Second Floor: Nothing + +- Name: Lakebed Temple Outside East Waterwheel Room Second Floor North Near East Water Supply Room + Region: Lakebed Temple + Exits: + Lakebed Temple East Water Supply Room: Nothing + Lakebed Temple Outside East Waterwheel Room Second Floor North: Can_Smash + +- Name: Lakebed Temple Outside East Waterwheel Room Second Floor South Near East Water Supply Room + Region: Lakebed Temple + Exits: + Lakebed Temple Outside East Waterwheel Room Second Floor South: Nothing + Lakebed Temple East Water Supply Room: Nothing + +- Name: Lakebed Temple Outside East Waterwheel Room Second Floor South + Region: Lakebed Temple + Locations: + Lakebed Temple East Second Floor Southeast Chest: Nothing + Exits: + Lakebed Temple East Waterwheel Room Second Floor: Nothing + +# LAKEBED TEMPLE EAST WATERWHEEL ROOM SECOND FLOOR + +- Name: Lakebed Temple East Waterwheel Room Second Floor + Region: Lakebed Temple + Exits: + Lakebed Temple Outside East Waterwheel Room Second Floor North: Nothing + Lakebed Temple Outside East Waterwheel Room Second Floor South: Nothing + Lakebed Temple East Waterwheel Room First Floor Lowest: Nothing + +# LAKEBED TEMPLE EAST WATER SUPPLY ROOM + +- Name: Lakebed Temple East Water Supply Room + Region: Lakebed Temple + Exits: + Lakebed Temple East Water Supply Room Past Locked Door: count(Lakebed_Temple_Small_Key, 3) or Small_Keys == Keysy or (Small_Keys == Vanilla and count(Lakebed_Temple_Small_Key, 2)) + Lakebed Temple Outside East Waterwheel Room Second Floor North Near East Water Supply Room: Nothing + Lakebed Temple Outside East Waterwheel Room Second Floor South Near East Water Supply Room: Nothing + +- Name: Lakebed Temple East Water Supply Room Past Locked Door + Region: Lakebed Temple + Events: + Can Pull East Water Supply Lever: Can_Climb_Vines and Can_Climb_Ladders and Can_Pull_Lakebed_Levers + Locations: + Lakebed Temple East Water Supply Small Chest: Can_Climb_Vines and Iron_Boots # Boots required incase someone activates the lever and falls down + Lakebed Temple East Water Supply Clawshot Chest: Can_Climb_Vines and Clawshot and Iron_Boots + +# LAKEBED TEMPLE WATER JET ROOM (the room before the miniboss) + +- Name: Lakebed Temple Water Jet Room Water Side Near East Waterwheel Room + Region: Lakebed Temple + Exits: + Lakebed Temple Water Jet Room Water Side Underwater: count(Lakebed_Temple_Small_Key, 3) or Small_Keys == Keysy + Lakebed Temple East Waterwheel Room First Floor Near Water Jet Room Water Side: Nothing + +- Name: Lakebed Temple Water Jet Room Water Side Underwater + Region: Lakebed Temple + Locations: + Lakebed Temple Before Deku Toad Underwater Left Chest: Iron_Boots + Lakebed Temple Before Deku Toad Underwater Right Chest: Iron_Boots + Exits: + Lakebed Temple Water Jet Room Water Side Near East Waterwheel Room: count(Lakebed_Temple_Small_Key, 3) or Small_Keys == Keysy + Lakebed Temple Water Jet Room Water Side Near MiniBoss Room: Iron_Boots and Water_Bombs + +- Name: Lakebed Temple Water Jet Room Water Side Near MiniBoss Room + Region: Lakebed Temple + Exits: + Deku Toad Miniboss Room Water Tunnel: Zora_Armor + Lakebed Temple Water Jet Room Water Side Underwater: Zora_Armor and Iron_Boots and Water_Bombs + +- Name: Lakebed Temple Water Jet Room Land Side + Region: Lakebed Temple + Locations: + Lakebed Temple Before Deku Toad Alcove Chest: Nothing + Exits: + Deku Toad Miniboss Room Near Water Jet Room Land Side: Nothing + Lakebed Temple East Waterwheel Room First Floor Near Water Jet Room Land Side: Nothing + +# DEKU TOAD MINIBOSS ROOM + +- Name: Deku Toad Miniboss Room Water Tunnel + Exits: + Deku Toad Miniboss Room Battle Arena: Zora_Armor + Lakebed Temple Water Jet Room Water Side Near MiniBoss Room: Zora_Armor + +- Name: Deku Toad Miniboss Room Battle Arena + Events: + Can Pull Deku Toad Miniboss Room Lever: Clawshot + Locations: + Lakebed Temple Deku Toad Chest: Can_Defeat_Deku_Toad + Exits: + Deku Toad Miniboss Room Near Water Jet Room Land Side: Clawshot + +- Name: Deku Toad Miniboss Room Near Water Jet Room Land Side + Exits: + Deku Toad Miniboss Room Battle Arena: "'Can_Pull_Deku_Toad_Miniboss_Room_Lever'" + Lakebed Temple Water Jet Room Land Side: Nothing + +# LAKEBED TEMPLE WEST WATERWHEEL ROOM + +- Name: Lakebed Temple West Waterwheel Room Lower + Region: Lakebed Temple + Locations: + Lakebed Temple West Lower Small Chest: Clawshot + Exits: + Lakebed Temple West Waterwheel Room Upper North: Clawshot + Lakebed Temple Circling Current Room West Lower Near West Waterwheel Room Lower: Nothing + Lakebed Temple West Waterwheel Room Lower Near Underwater Maze Room: Clawshot and 'Can_Pull_West_Water_Supply_Lever' + +- Name: Lakebed Temple West Waterwheel Room Upper North + Region: Lakebed Temple + Locations: + Lakebed Temple West Second Floor Central Small Chest: Clawshot + Exits: + Lakebed Temple West Waterwheel Room Upper Near North Door: Clawshot + Lakebed Temple West Waterwheel Room Lower: Clawshot + +- Name: Lakebed Temple West Waterwheel Room Upper Near North Door + Region: Lakebed Temple + Exits: + Lakebed Temple Outside West Waterwheel Room North: Nothing + Lakebed Temple West Waterwheel Room Upper North: Clawshot + +- Name: Lakebed Temple West Waterwheel Room On Waterwheels + Region: Lakebed Temple + Exits: + Lakebed Temple Outside West Waterwheel Room Southeast: Nothing + Lakebed Temple Outside West Waterwheel Room Southwest: Nothing + Lakebed Temple West Waterwheel Room Upper Near North Door: Clawshot + Lakebed Temple West Waterwheel Room Upper North: Clawshot + Lakebed Temple West Waterwheel Room Lower: Nothing + +- Name: Lakebed Temple West Waterwheel Room Lower Near Underwater Maze Room + Region: Lakebed Temple + Exits: + Lakebed Temple Underwater Maze Room Near Waterwheel Room: Nothing + Lakebed Temple West Waterwheel Room Lower: Clawshot and 'Can_Pull_West_Water_Supply_Lever' + +# LAKEBED TEMPLE OUTSIDE WEST WATERWHEEL ROOM + +- Name: Lakebed Temple Outside West Waterwheel Room Upper Near Circling Current Room + Region: Lakebed Temple + Exits: + Lakebed Temple Outside West Waterwheel Room Southeast: "'Can_Pull_Outside_West_Waterwheel_Room_Southeast_Lever'" + Lakebed Temple Circling Current Room West Upper Near West Waterwheel Room Upper: Nothing + +- Name: Lakebed Temple Outside West Waterwheel Room Southeast + Region: Lakebed Temple + Events: + Can Pull Outside West Waterwheel Room Southeast Lever: Clawshot + Locations: + Lakebed Temple West Second Floor Southeast Chest: Nothing + Exits: + Lakebed Temple West Waterwheel Room On Waterwheels: Nothing + Lakebed Temple Outside West Waterwheel Room Upper Near Circling Current Room: "'Can_Pull_Outside_West_Waterwheel_Room_Southeast_Lever'" + +- Name: Lakebed Temple Outside West Waterwheel Room Southwest + Region: Lakebed Temple + Exits: + Lakebed Temple West Waterwheel Room On Waterwheels: Nothing + Lakebed Temple Outside West Waterwheel Room Southwest Pond: "'Can_Pull_West_Water_Supply_Lever'" + +- Name: Lakebed Temple Outside West Waterwheel Room Southwest Pond + Region: Lakebed Temple + Locations: + Lakebed Temple West Second Floor Southwest Underwater Chest: Iron_Boots + Exits: + Lakebed Temple Outside West Waterwheel Room Southwest Near West Water Supply Room: Clawshot or 'Can_Pull_West_Water_Supply_Lever' + Lakebed Temple Outside West Waterwheel Room Southwest: "'Can_Pull_West_Water_Supply_Lever'" + +- Name: Lakebed Temple Outside West Waterwheel Room Southwest Near West Water Supply Room + Region: Lakebed Temple + Exits: + Lakebed Temple West Water Supply Room: Nothing + Lakebed Temple Outside West Waterwheel Room Southwest Pond: Nothing + +- Name: Lakebed Temple Outside West Waterwheel Room Northwest Near West Water Supply Room + Region: Lakebed Temple + Exits: + Lakebed Temple Outside West Waterwheel Room Tektite Area: Nothing + Lakebed Temple West Water Supply Room: Nothing + +- Name: Lakebed Temple Outside West Waterwheel Room Tektite Area + Region: Lakebed Temple + Events: + Can Pull Outside West Waterwheel Room Northeast Lever: Clawshot + Exits: + Lakebed Temple Outside West Waterwheel Room North: "'Can_Pull_Outside_West_Waterwheel_Room_Northeast_Lever'" + Lakebed Temple Outside West Waterwheel Room Northwest Near West Water Supply Room: Clawshot + +- Name: Lakebed Temple Outside West Waterwheel Room North + Region: Lakebed Temple + Locations: + Lakebed Temple West Second Floor Northeast Chest: "'Can_Pull_West_Water_Supply_Lever'" + Exits: + Lakebed Temple Outside West Waterwheel Room Tektite Area: Can_Launch_Bombs + Lakebed Temple West Waterwheel Room Upper Near North Door: Nothing + +# LAKEBED TEMPLE WEST WATER SUPPLY ROOM + +- Name: Lakebed Temple West Water Supply Room + Region: Lakebed Temple + Events: + Can Pull West Water Supply Lever: Clawshot and Can_Climb_Ladders and Iron_Boots + Locations: + Lakebed Temple West Water Supply Small Chest: Clawshot and Iron_Boots + Lakebed Temple West Water Supply Chest: Clawshot and Iron_Boots + Exits: + Lakebed Temple Outside West Waterwheel Room Southwest Near West Water Supply Room: Nothing + Lakebed Temple Outside West Waterwheel Room Northwest Near West Water Supply Room: Nothing + +# LAKEBED TEMPLE UNDERWATER MAZE ROOM + +- Name: Lakebed Temple Underwater Maze Room Near Waterwheel Room + Region: Lakebed Temple + Exits: + Lakebed Temple Underwater Maze Room Near Big Key Room Lower: Zora_Armor and Iron_Boots and Water_Bombs + Lakebed Temple West Waterwheel Room Lower Near Underwater Maze Room: Nothing + +- Name: Lakebed Temple Underwater Maze Room Near Big Key Room Lower + Region: Lakebed Temple + Locations: + Lakebed Temple Underwater Maze Small Chest: Zora_Armor + Exits: + Lakebed Temple Underwater Maze Room Near Waterwheel Room: Zora_Armor and Iron_Boots and Water_Bombs + Lakebed Temple Underwater Maze Room Near Big Key Room Upper: Zora_Armor and Iron_Boots and Water_Bombs + Lakebed Temple Big Key Room Lower: Zora_Armor and Iron_Boots + +- Name: Lakebed Temple Underwater Maze Room Near Big Key Room Upper + Region: Lakebed Temple + Exits: + Lakebed Temple Big Key Room Upper: Nothing + Lakebed Temple Underwater Maze Room Near Big Key Room Lower: Zora_Armor and Iron_Boots and Water_Bombs + +# LAKEBED TEMPLE BIG KEY ROOM + +- Name: Lakebed Temple Big Key Room Upper + Region: Lakebed Temple + Exits: + Lakebed Temple Big Key Room Middle: Clawshot + Lakebed Temple Underwater Maze Room Near Big Key Room Upper: Nothing + +- Name: Lakebed Temple Big Key Room Middle + Region: Lakebed Temple + Locations: + Lakebed Temple Big Key Chest: Nothing + Exits: + Lakebed Temple Big Key Room Lower: Nothing + +- Name: Lakebed Temple Big Key Room Lower + Region: Lakebed Temple + Exits: + Lakebed Temple Underwater Maze Room Near Big Key Room Lower: Zora_Armor and Iron_Boots + +# LAKEBED TEMPLE BOSS ROOM + +- Name: Lakebed Temple Boss Room + Events: + Can Complete Lakebed Temple: Can_Defeat_Morpheel + Locations: + Lakebed Temple Morpheel Heart Container: Can_Defeat_Morpheel + Lakebed Temple Dungeon Reward: Can_Defeat_Morpheel + Exits: + Lake Hylia Lanayru Spring: Can_Defeat_Morpheel diff --git a/mods/randomizer/generator/data/world/dungeons/Palace of Twilight.yaml b/mods/randomizer/generator/data/world/dungeons/Palace of Twilight.yaml new file mode 100644 index 0000000000..1df0f2ab53 --- /dev/null +++ b/mods/randomizer/generator/data/world/dungeons/Palace of Twilight.yaml @@ -0,0 +1,194 @@ +# PALACE OF TWILIGHT ENTRANCE + +- Name: Palace of Twilight Entrance + Region: Palace of Twilight + Dungeon Start Area: True + Events: + Can Place East Sol at Palace of Twilight Entrance: "'Can_Bring_East_Sol_to_Palace_of_Twilight_Entrance'" + Can Place West Sol at Palace of Twilight Entrance: "'Can_Bring_West_Sol_to_Palace_of_Twilight_Entrance'" + Locations: + Palace of Twilight Collect Both Sols: "'Can_Place_East_Sol_at_Palace_of_Twilight_Entrance' and 'Can_Place_West_Sol_at_Palace_of_Twilight_Entrance'" + Palace of Twilight Hint Sign: Nothing + Exits: + Palace of Twilight West Wing First Room Near Entrance: Nothing + Palace of Twilight Entrance Near East Wing First Room: Nothing + Palace of Twilight Entrance Near North Wing: Light_Sword + Twilight Realm Portal: Nothing + +- Name: Palace of Twilight Entrance Near East Wing First Room + Region: Palace of Twilight + Exits: + Palace of Twilight East Wing First Room Near Entrance: Nothing + Palace of Twilight Entrance: "'Can_Place_West_Sol_at_Palace_of_Twilight_Entrance'" + +- Name: Palace of Twilight Entrance Near North Wing + Region: Palace of Twilight + Exits: + Palace of Twilight North Wing First Room Bottom: Nothing + Palace of Twilight Entrance: Light_Sword + +# PALACE OF TWILIGHT WEST WING FIRST ROOM + +- Name: Palace of Twilight West Wing First Room Near Entrance + Region: Palace of Twilight + Locations: + Palace of Twilight West Wing First Room Central Chest: Can_Defeat_Zant_Head + Palace of Twilight West Wing Chest Behind Wall of Darkness: Light_Sword and Clawshot + Exits: + Palace of Twilight West Wing First Room Near Second Room: Clawshot + Palace of Twilight Entrance: Nothing + +- Name: Palace of Twilight West Wing First Room Near Second Room + Region: Palace of Twilight + Exits: + Palace of Twilight West Wing Second Room Near First Room: count(Palace_of_Twilight_Small_Key, 6) or (Small_Keys == Vanilla and count(Palace_of_Twilight_Small_Key, 3)) or Small_Keys == Keysy + Palace of Twilight West Wing First Room Near Entrance: Nothing + +# PALACE OF TWILIGHT WEST WING SECOND ROOM + +- Name: Palace of Twilight West Wing Second Room Near First Room + Region: Palace of Twilight + Exits: + Palace of Twilight West Wing Second Room Middle: Nothing + Palace of Twilight West Wing First Room Near Second Room: Nothing + +- Name: Palace of Twilight West Wing Second Room Middle + Region: Palace of Twilight + Locations: + Palace of Twilight West Wing Second Room Central Chest: Can_Defeat_Zant_Head + Palace of Twilight West Wing Second Room Lower South Chest: Can_Defeat_Zant_Head + Palace of Twilight West Wing Second Room Southeast Chest: Double_Clawshots + Exits: + Palace of Twilight West Wing Second Room Near Phantom Zant Room: Clawshot + Palace of Twilight West Wing Second Room Near First Room: "'Can_Bring_West_Sol_to_Palace_of_Twilight_Entrance'" + +- Name: Palace of Twilight West Wing Second Room Near Phantom Zant Room + Region: Palace of Twilight + Exits: + Palace of Twilight West Wing Phantom Zant Room: count(Palace_of_Twilight_Small_Key, 7) or Small_Keys == Keysy + Palace of Twilight West Wing Second Room Middle: Nothing + +# PALACE OF TWILIGHT WEST WING PHANTOM ZANT ROOM + +- Name: Palace of Twilight West Wing Phantom Zant Room + Region: Palace of Twilight + Events: + Can Bring West Sol to Palace of Twilight Entrance: Can_Defeat_Phantom_Zant and Human_Link # Only human can carry the sol + Exits: + Palace of Twilight West Wing Second Room Near Phantom Zant Room: "'Can_Bring_West_Sol_to_Palace_of_Twilight_Entrance'" + +# PALACE OF TWILIGHT EAST WING FIRST ROOM + +- Name: Palace of Twilight East Wing First Room Near Entrance + Region: Palace of Twilight + Exits: + Palace of Twilight East Wing First Room Bottom: Nothing + Palace of Twilight East Wing First Room Near Second Room: Clawshot + +- Name: Palace of Twilight East Wing First Room Bottom + Region: Palace of Twilight + Locations: + Palace of Twilight East Wing First Room East Alcove Chest: Light_Sword or 'Can_Place_East_Sol_at_Palace_of_Twilight_Entrance' + Palace of Twilight East Wing First Room West Alcove Chest: Light_Sword or 'Can_Place_East_Sol_at_Palace_of_Twilight_Entrance' + Exits: + Palace of Twilight East Wing First Room Near Second Room: Double_Clawshots or Light_Sword or 'Can_Place_East_Sol_at_Palace_of_Twilight_Entrance' + +- Name: Palace of Twilight East Wing First Room Near Second Room + Region: Palace of Twilight + Locations: + Palace of Twilight East Wing First Room North Small Chest: Nothing + Palace of Twilight East Wing First Room Zant Head Chest: Can_Defeat_Zant_Head + Exits: + Palace of Twilight East Wing Second Room Near First Room: count(Palace_of_Twilight_Small_Key, 6) or (Small_Keys == Vanilla and count(Palace_of_Twilight_Small_Key, 3)) or Small_Keys == Keysy + Palace of Twilight East Wing First Room Bottom: Nothing + Palace of Twilight East Wing First Room Near Entrance: Light_Sword or 'Can_Place_East_Sol_at_Palace_of_Twilight_Entrance' + +# PALACE OF TWILIGHT EAST WING SECOND ROOM + +- Name: Palace of Twilight East Wing Second Room Near First Room + Region: Palace of Twilight + Exits: + Palace of Twilight East Wing Second Room Near Phantom Zant Room: Clawshot and Can_Defeat_Zant_Head and Can_Defeat_Shadow_Beast + Palace of Twilight East Wing First Room Near Second Room: Nothing + +- Name: Palace of Twilight East Wing Second Room Near Phantom Zant Room + Region: Palace of Twilight + Locations: + Palace of Twilight East Wing Second Room Northeast Chest: Double_Clawshots + Palace of Twilight East Wing Second Room Northwest Chest: Clawshot + Palace of Twilight East Wing Second Room Southwest Chest: Double_Clawshots + Palace of Twilight East Wing Second Room Southeast Chest: Double_Clawshots and Can_Defeat_Zant_Head and Can_Defeat_Shadow_Beast + Exits: + Palace of Twilight East Wing Phantom Zant Room: count(Palace_of_Twilight_Small_Key, 7) or Small_Keys == Keysy + Palace of Twilight East Wing Second Room Near First Room: Double_Clawshots and 'Can_Place_East_Sol_at_Palace_of_Twilight_Entrance' + +# PALACE OF TWILIGHT EAST PHANTOM ZANT ROOM + +- Name: Palace of Twilight East Wing Phantom Zant Room + Region: Palace of Twilight + Events: + Can Bring East Sol to Palace of Twilight Entrance: Can_Defeat_Phantom_Zant and Human_Link + Exits: + Palace of Twilight East Wing Second Room Near Phantom Zant Room: "'Can_Place_East_Sol_at_Palace_of_Twilight_Entrance'" + +# PALACE OF TWILIGHT NORTH WING FIRST ROOM + +- Name: Palace of Twilight North Wing First Room Bottom + Region: Palace of Twilight + Locations: + Palace of Twilight Central First Room Chest: Light_Sword and Can_Defeat_Zant_Head + Exits: + Palace of Twilight Entrance Near North Wing: Nothing + Palace of Twilight North Wing First Room Top: Light_Sword + +- Name: Palace of Twilight North Wing First Room Top + Region: Palace of Twilight + Exits: + Palace of Twilight North Wing Outside Room: count(Palace_of_Twilight_Small_Key, 5) or Small_Keys == Keysy + Palace of Twilight North Wing First Room Bottom: Nothing + +# PALACE OF TWILIGHT NORTH WING OUTSIDE ROOM + +- Name: Palace of Twilight North Wing Outside Room + Region: Palace of Twilight + Locations: + Palace of Twilight Big Key Chest: Light_Sword and Double_Clawshots + Palace of Twilight Central Outdoor Chest: Light_Sword and Can_Defeat_Zant_Head + Exits: + Palace of Twilight North Wing Tower Bottom: count(Palace_of_Twilight_Small_Key, 6) or Small_Keys == Keysy + Palace of Twilight North Wing First Room Top: Nothing + +# PALACE OF TWILIGHT NORTH WING TOWER + +- Name: Palace of Twilight North Wing Tower Bottom + Region: Palace of Twilight + Locations: + Palace of Twilight Central Tower Chest: Clawshot and Light_Sword and Can_Defeat_Zant_Head + Exits: + Palace of Twilight North Wing Tower Top: Clawshot and Light_Sword + Palace of Twilight North Wing Outside Room: Nothing + +- Name: Palace of Twilight North Wing Tower Top + Region: Palace of Twilight + Exits: + Palace of Twilight Boss Door Room: count(Palace_of_Twilight_Small_Key, 7) or Small_Keys == Keysy + Palace of Twilight North Wing Tower Bottom: Nothing + +# PALACE OF TWILIGHT BOSS DOOR ROOM + +- Name: Palace of Twilight Boss Door Room + Region: Palace of Twilight + Exits: + Palace of Twilight Boss Room: Can_Defeat_Shadow_Beast and (Palace_of_Twilight_Big_Key or Big_Keys == Keysy) + Palace of Twilight North Wing Tower Top: Nothing + +# PALACE OF TWILIGHT BOSS ROOM + +- Name: Palace of Twilight Boss Room + Events: + Can Complete Palace of Twilight: Can_Defeat_Zant + Locations: + Palace of Twilight Zant Heart Container: Can_Defeat_Zant + Exits: + Palace of Twilight Entrance: Can_Defeat_Zant + \ No newline at end of file diff --git a/mods/randomizer/generator/data/world/dungeons/Snowpeak Ruins.yaml b/mods/randomizer/generator/data/world/dungeons/Snowpeak Ruins.yaml new file mode 100644 index 0000000000..b5657258c8 --- /dev/null +++ b/mods/randomizer/generator/data/world/dungeons/Snowpeak Ruins.yaml @@ -0,0 +1,346 @@ +# SNOWPEAK RUINS ENTRANCE + +- Name: Snowpeak Ruins Entrance + Region: Snowpeak Ruins + Dungeon Start Area: True + Locations: + Snowpeak Ruins Lobby West Armor Chest: Can_Break_Armor + Snowpeak Ruins Lobby East Armor Chest: Can_Break_Armor + Snowpeak Ruins Lobby Armor Poe: Can_Break_Armor and Can_Use_Senses + Snowpeak Ruins Lobby Poe: Can_Use_Senses + Exits: + Snowpeak Ruins Entrance Near Caged Freezard Room: Clawshot and 'Can_Break_Snowpeak_Entrance_Ice_Wall_Shortcut' + Snowpeak Ruins Yetas Room: Can_Open_Doors + Snowpeak Ruins Room Below Broken Floor Near Entrance: Can_Open_Doors + Snowpeak Ruins East Door Interior: Can_Open_Doors + Snowpeak Ruins West Door Interior: Can_Open_Doors + +- Name: Snowpeak Ruins Entrance Near Caged Freezard Room + Region: Snowpeak Ruins + Events: + Can Break Snowpeak Entrance Ice Wall Shortcut: Can_Break_Ice + Locations: + Snowpeak Ruins Lobby Chandelier Chest: Ball_and_Chain + Exits: + Snowpeak Ruins Entrance Near Second Floor Mini Freezard Room: Ball_and_Chain + Snowpeak Ruins Caged Freezard Room Second Floor: Can_Open_Doors and (count(Snowpeak_Ruins_Small_Key, 3) or Small_Keys == Keysy) + Snowpeak Ruins Entrance: Nothing + +- Name: Snowpeak Ruins Entrance Near Second Floor Mini Freezard Room + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Second Floor Mini Freezard Room: Nothing + Snowpeak Ruins Entrance Near Caged Freezard Room: Ball_and_Chain + +# SNOWPEAK RUINS YETAS ROOM + +- Name: Snowpeak Ruins Yetas Room + Region: Snowpeak Ruins + Locations: + Snowpeak Ruins Mansion Map: Can_Talk_to_Humans + Snowpeak Ruins Hint Sign: Nothing + Exits: + Snowpeak Ruins Kitchen: Can_Open_Doors + Snowpeak Ruins West Courtyard: Ordon_Pumpkin or Small_Keys == Keysy + Snowpeak Ruins Caged Freezard Room First Floor: Ordon_Cheese or Small_Keys == Keysy + Snowpeak Ruins Entrance: Can_Open_Doors + +# SNOWPEAK RUINS KITCHEN + +- Name: Snowpeak Ruins Kitchen + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Block Puzzle Room Near Kitchen: Can_Open_Doors + Snowpeak Ruins Yetas Room: Can_Open_Doors + +# SNOWPEAK RUINS BLOCK PUZZLE ROOM + +- Name: Snowpeak Ruins Block Puzzle Room Near Kitchen + Region: Snowpeak Ruins + Events: + Can Press Snowpeak Block Puzzle Ice Switch: Can_Break_Ice + Exits: + Snowpeak Ruins East Courtyard Near Block Puzzle Room First Floor: Can_Open_Doors + Snowpeak Ruins Block Puzzle Room Near Northeast Chilfos Room First Floor: "'Can_Push_Snowpeak_Block_Puzzle_Highest_Block'" + Snowpeak Ruins Kitchen: Can_Open_Doors + +- Name: Snowpeak Ruins Block Puzzle Room Near Northeast Chilfos Room First Floor + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Northeast Chilfos Room Near Block Puzzle Room First Floor: Can_Open_Doors + Snowpeak Ruins Block Puzzle Room Near Kitchen: Nothing + Snowpeak Ruins Block Puzzle Room Second Floor: "'Can_Push_Snowpeak_Block_Puzzle_Highest_Block'" + +- Name: Snowpeak Ruins Block Puzzle Room Second Floor + Region: Snowpeak Ruins + Events: + Can Push Snowpeak Block Puzzle Highest Block: Nothing + Exits: + Snowpeak Ruins Second Floor Mini Freezard Room: Can_Open_Doors + Snowpeak Ruins East Courtyard Balcony Near Block Puzzle Room: Can_Open_Doors and 'Can_Press_Snowpeak_Block_Puzzle_Ice_Switch' + Snowpeak Ruins Block Puzzle Room Near Kitchen: "'Can_Push_Snowpeak_Block_Puzzle_Highest_Block'" + +- Name: Snowpeak Ruins Block Puzzle Room Near Northeast Chilfos Room Second Floor + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Northeast Chilfos Room Near Block Puzzle Room Second Floor: Nothing + Snowpeak Ruins Block Puzzle Room Near Northeast Chilfos Room First Floor: Nothing + +# SNOWPEAK RUINS EAST COURTYARD + +- Name: Snowpeak Ruins East Courtyard Near Block Puzzle Room First Floor + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins East Courtyard: Can_Dig + Snowpeak Ruins East Courtyard Hallway: Can_Break_Ice + Snowpeak Ruins Block Puzzle Room Near Kitchen: Can_Open_Doors + +- Name: Snowpeak Ruins East Courtyard + Region: Snowpeak Ruins + Locations: + Snowpeak Ruins East Courtyard Buried Chest: Can_Dig + Snowpeak Ruins East Courtyard Chest: Nothing + Exits: + Snowpeak Ruins East Courtyard Hallway: Can_Open_Doors + Snowpeak Ruins West Courtyard: Can_Break_Ice + +- Name: Snowpeak Ruins East Courtyard Hallway + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Triple Mini Freezard Room: Can_Open_Doors and (count(Snowpeak_Ruins_Small_Key, 4) or Small_Keys == Keysy) + Snowpeak Ruins East Courtyard: Can_Open_Doors + Snowpeak Ruins East Courtyard Near Block Puzzle Room First Floor: Can_Break_Ice + +- Name: Snowpeak Ruins East Courtyard Balcony Near Block Puzzle Room + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins East Courtyard Balcony Near Northeast Chilfos Room: Can_Defeat_Chilfos and Clawshot + Snowpeak Ruins East Courtyard Near Block Puzzle Room First Floor: Nothing + Snowpeak Ruins East Courtyard: Nothing + Snowpeak Ruins East Courtyard Hallway: Nothing + Snowpeak Ruins Block Puzzle Room Second Floor: Can_Open_Doors + +- Name: Snowpeak Ruins East Courtyard Balcony Near Northeast Chilfos Room + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Northeast Chilfos Room Near East Courtyard Balcony: Can_Open_Doors + +# SNOWPEAK RUINS TRIPLE MINI FREEZARD ROOM + +- Name: Snowpeak Ruins Triple Mini Freezard Room + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Northeast Chilfos Room First Floor: Can_Defeat_Mini_Freezard + Snowpeak Ruins East Courtyard Hallway: Can_Defeat_Mini_Freezard and (count(Snowpeak_Ruins_Small_Key, 4) or Small_Keys == Keysy) + +# SNOWPEAK RUINS NORTHEAST CHILLFOS ROOM + +- Name: Snowpeak Ruins Northeast Chilfos Room First Floor + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Northeast Chilfos Room Near Block Puzzle Room First Floor: Can_Defeat_Chilfos and Can_Open_Doors + Snowpeak Ruins Northeast Chilfos Room Near East Courtyard Balcony: Clawshot and 'Can_Break_Snowpeak_Northeast_Chilfos_Room_Ice_Wall_Shortcut' + Snowpeak Ruins Triple Mini Freezard Room: Can_Defeat_Chilfos and Can_Open_Doors + +- Name: Snowpeak Ruins Northeast Chilfos Room Near Block Puzzle Room First Floor + Region: Snowpeak Ruins + Locations: + Snowpeak Ruins Ordon Pumpkin Chest: Nothing + Exits: + Snowpeak Ruins Block Puzzle Room Near Northeast Chilfos Room First Floor: Can_Open_Doors + Snowpeak Ruins Northeast Chilfos Room First Floor: Can_Open_Doors + +- Name: Snowpeak Ruins Northeast Chilfos Room Near East Courtyard Balcony + Region: Snowpeak Ruins + Events: + Can Break Snowpeak Northeast Chilfos Room Ice Wall Shortcut: Can_Break_Ice + Exits: + Snowpeak Ruins Northeast Chilfos Room Near Block Puzzle Room Second Floor: Ball_and_Chain + Snowpeak Ruins Northeast Chilfos Room First Floor: Nothing + +- Name: Snowpeak Ruins Northeast Chilfos Room Near Block Puzzle Room Second Floor + Region: Snowpeak Ruins + Locations: + Snowpeak Ruins Northeast Chandelier Chest: Nothing + Exits: + Snowpeak Ruins Block Puzzle Room Near Northeast Chilfos Room Second Floor: Can_Open_Doors + Snowpeak Ruins Northeast Chilfos Room Near East Courtyard Balcony: Ball_and_Chain + Snowpeak Ruins Northeast Chilfos Room First Floor: Nothing + +# SNOWPEAK RUINS WEST COURTYARD + +- Name: Snowpeak Ruins West Courtyard + Region: Snowpeak Ruins + Locations: + Snowpeak Ruins West Courtyard Buried Chest: Can_Dig + Snowpeak Ruins Courtyard Central Chest: Can_Break_Ice + Exits: + Snowpeak Ruins West Canon Room Near Courtyard: Can_Open_Doors + Snowpeak Ruins East Courtyard: Can_Break_Ice + Snowpeak Ruins West Courtyard Hallway Near Ladder: Can_Open_Doors and (count(Snowpeak_Ruins_Small_Key, 4) or Small_Keys == Keysy) + Darkhammer Miniboss Room: Can_Open_Doors and (Ball_and_Chain or (Can_Launch_Canonball and 'Can_Load_Snowpeak_West_Courtyard_Hallway_Canonball_Holder')) + +- Name: Snowpeak Ruins West Courtyard Hallway Near Ladder + Region: Snowpeak Ruins + Events: + Can Load Snowpeak West Courtyard Hallway Canonball Holder: Human_Link + Exits: + Snowpeak Ruins West Courtyard North Balcony: Can_Climb_Ladders and 'Can_Kill_Snowpeak_Courtyard_Balcony_Freezard' + Snowpeak Ruins West Hallway Near Caged Freezard Room: "'Can_Push_Snowpeak_West_Courtyard_Hallway_Block'" + Snowpeak Ruins West Courtyard: Can_Open_Doors and (count(Snowpeak_Ruins_Small_Key, 4) or Small_Keys == Keysy) + +- Name: Snowpeak Ruins West Hallway Near Caged Freezard Room + Region: Snowpeak Ruins + Events: + Can Load Snowpeak Hallway to Caged Freezard Canonball Holder: Human_Link + Can Push Snowpeak West Courtyard Hallway Block: Nothing + Exits: + Snowpeak Ruins Caged Freezard Room First Floor: Can_Open_Doors + Snowpeak Ruins West Courtyard Hallway Near Ladder: "'Can_Push_Snowpeak_West_Courtyard_Hallway_Block'" + +- Name: Snowpeak Ruins West Courtyard South Balcony + Region: Snowpeak Ruins + Events: + Can Break Snowpeak Balcony Ice Wall Shortcut: Can_Break_Ice + Can Kill Snowpeak Courtyard Balcony Freezard: Can_Launch_Canonball and 'Can_Load_Snowpeak_Double_Freezard_Room_Canonball_Holder' + Exits: + Snowpeak Ruins Double Freezard Room Behind Freezard: Can_Open_Doors + Snowpeak Ruins West Courtyard: Nothing + Snowpeak Ruins West Courtyard Hallway Near Ladder: Nothing + Snowpeak Ruins East Courtyard: Nothing + +- Name: Snowpeak Ruins West Courtyard North Balcony + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Chapel: Can_Open_Doors + Snowpeak Ruins Boss Room: Snowpeak_Ruins_Bedroom_Key or Big_Keys == Keysy + +# SNOWPEAK RUINS WEST CANON ROOM + +- Name: Snowpeak Ruins West Canon Room Near Courtyard + Region: Snowpeak Ruins + Events: + Can Launch West Canon Room Canonballs: Can_Launch_Canonball + Locations: + Snowpeak Ruins West Cannon Room Central Chest: Can_Break_Ice + Snowpeak Ruins West Cannon Room Corner Chest: Can_Break_Ice or 'Can_Launch_West_Canon_Room_Canonballs' + Exits: + Snowpeak Ruins West Canon Room Near Wooden Beam Room: Can_Break_Ice or 'Can_Launch_West_Canon_Room_Canonballs' + Snowpeak Ruins West Courtyard: Can_Open_Doors + +- Name: Snowpeak Ruins West Canon Room Near Wooden Beam Room + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Wooden Beam Room First Floor: Can_Open_Doors + Snowpeak Ruins West Canon Room Near Courtyard: Can_Break_Ice or 'Can_Launch_West_Canon_Room_Canonballs' + +# SNOWPEAK RUINS WOODEN BEAM ROOM + +- Name: Snowpeak Ruins Wooden Beam Room First Floor + Region: Snowpeak Ruins + Locations: + Snowpeak Ruins Wooden Beam Central Chest: Can_Defeat_Ice_Keese + Snowpeak Ruins Wooden Beam Northwest Chest: Can_Defeat_Ice_Keese + Exits: + Snowpeak Ruins West Canon Room Near Wooden Beam Room: Can_Open_Doors + +- Name: Snowpeak Ruins Wooden Beam Room Second Floor + Region: Snowpeak Ruins + Events: + Can Break Snowpeak Wooden Beam Room Ice Shortcut: Can_Break_Ice + Locations: + Snowpeak Ruins Wooden Beam Chandelier Chest: Ball_and_Chain + Exits: + Snowpeak Ruins Wooden Beam Room First Floor: Nothing + Snowpeak Ruins Caged Freezard Room Second Floor: Can_Open_Doors + +# DARKHAMMER MINIBOSS ROOM + +- Name: Darkhammer Miniboss Room + Locations: + Snowpeak Ruins Ball and Chain: Can_Defeat_Darkhammer + Snowpeak Ruins Chest After Darkhammer: Can_Break_Ice and Can_Defeat_Darkhammer + Exits: + Snowpeak Ruins West Courtyard: Can_Open_Doors + +# SNOWPEAK RUINS CAGED FREEZARD ROOM + +- Name: Snowpeak Ruins Caged Freezard Room First Floor + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Caged Freezard Room Second Floor: Can_Break_Ice + Snowpeak Ruins West Hallway Near Caged Freezard Room: Can_Open_Doors + Snowpeak Ruins Yetas Room: Can_Open_Doors + +- Name: Snowpeak Ruins Caged Freezard Room Second Floor + Region: Snowpeak Ruins + Events: + Can Launch Snowpeak Caged Freezard Room Canonballs: Can_Launch_Canonball and Ball_and_Chain and 'Can_Load_Snowpeak_Hallway_to_Caged_Freezard_Canonball_Holder' + Exits: + Snowpeak Ruins Wooden Beam Room Second Floor: Can_Open_Doors + Snowpeak Ruins Room Below Broken Floor: Can_Smash + Snowpeak Ruins Entrance Near Caged Freezard Room: Can_Open_Doors and (count(Snowpeak_Ruins_Small_Key, 3) or Small_Keys == Keysy) + Snowpeak Ruins Double Freezard Room: "'Can_Push_Snowpeak_Double_Freezard_Room_Block'" + +# SNOWPEAK RUINS ROOM BELOW BROKEN FLOOR + +- Name: Snowpeak Ruins Room Below Broken Floor Near Entrance + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins Entrance: Can_Open_Doors + +- Name: Snowpeak Ruins Room Below Broken Floor + Region: Snowpeak Ruins + Locations: + Snowpeak Ruins Broken Floor Chest: Nothing + Exits: + Snowpeak Ruins Caged Freezard Room Second Floor: Clawshot # There's no collision from the bottom + +# SNOWPEAK RUINS SECOND FLOOR MINI FREEZARD ROOM + +- Name: Snowpeak Ruins Second Floor Mini Freezard Room + Region: Snowpeak Ruins + Locations: + Snowpeak Ruins Ice Room Poe: Can_Break_Ice and Can_Use_Senses + Exits: + Snowpeak Ruins Block Puzzle Room Second Floor: Can_Open_Doors + Snowpeak Ruins Double Freezard Room: Can_Open_Doors and (count(Snowpeak_Ruins_Small_Key, 4) or Small_Keys == Keysy) + Snowpeak Ruins Entrance Near Second Floor Mini Freezard Room: Can_Open_Doors + +# SNOWPEAK RUINS DOUBLE FREEZARD ROOM + +- Name: Snowpeak Ruins Double Freezard Room + Region: Snowpeak Ruins + Events: + Can Push Snowpeak Double Freezard Room Block: Can_Defeat_Freezard + Can Load Snowpeak Double Freezard Room Canonball Holder: Can_Defeat_Freezard and 'Can_Launch_Snowpeak_Caged_Freezard_Room_Canonballs' and 'Can_Push_Snowpeak_Double_Freezard_Room_Block' + Exits: + Snowpeak Ruins Double Freezard Room Behind Freezard: Can_Defeat_Freezard + Snowpeak Ruins Caged Freezard Room Second Floor: "'Can_Push_Snowpeak_Double_Freezard_Room_Block'" + Snowpeak Ruins Second Floor Mini Freezard Room: Can_Open_Doors and (count(Snowpeak_Ruins_Small_Key, 4) or Small_Keys == Keysy) + +- Name: Snowpeak Ruins Double Freezard Room Behind Freezard + Region: Snowpeak Ruins + Exits: + Snowpeak Ruins West Courtyard North Balcony: Can_Open_Doors + Snowpeak Ruins Double Freezard Room: Can_Defeat_Freezard + +# SNOWPEAK RUINS CHAPEL + +- Name: Snowpeak Ruins Chapel + Region: Snowpeak Ruins + Locations: + Snowpeak Ruins Chapel Chest: Can_Defeat_Chilfos and Can_Open_Doors + Exits: + Snowpeak Ruins West Courtyard North Balcony: Can_Open_Doors + +# SNOWPEAK RUINS BOSS ROOM + +- Name: Snowpeak Ruins Boss Room + Events: + Can Complete Snowpeak Ruins: Can_Defeat_Blizzeta + Locations: + Snowpeak Ruins Blizzeta Heart Container: Can_Defeat_Blizzeta + Snowpeak Ruins Dungeon Reward: Can_Defeat_Blizzeta + Exits: + Snowpeak Summit Lower: Can_Defeat_Blizzeta \ No newline at end of file diff --git a/mods/randomizer/generator/data/world/dungeons/Temple of Time.yaml b/mods/randomizer/generator/data/world/dungeons/Temple of Time.yaml new file mode 100644 index 0000000000..84d6d01dd0 --- /dev/null +++ b/mods/randomizer/generator/data/world/dungeons/Temple of Time.yaml @@ -0,0 +1,205 @@ +# Bringing down the giant statue is handled via a chain of events for each +# individual traversal between teleporters. + +# TEMPLE OF TIME ENTRANCE + +- Name: Temple of Time Entrance + Region: Temple of Time + Dungeon Start Area: True + Events: + Can Refill Lantern Oil: Nothing + Can Open Door of Time: Dominion_Rod and 'Can_Teleport_Giant_Statue_to_Temple_of_Time_Entrance' + Locations: + Temple of Time Lobby Lantern Chest: Lantern + Temple of Time First Hint Sign: Nothing + Exits: + Temple of Time Yellow Gates Corridor Near Entrance: Temple_of_Time_Small_Key or Small_Keys == Keysy + Temple of Time Entrance Near Crumbling Corridor: Open_Door_of_Time == On or 'Can_Open_Door_of_Time' + Sacred Grove Past Behind Window: Nothing + +- Name: Temple of Time Entrance Near Crumbling Corridor + Region: Temple of Time + Exits: + Temple of Time Crumbling Corridor Near Entrance: Nothing + Temple of Time Entrance: "'Can_Open_Door_of_Time'" + +# TEMPLE OF TIME YELLOW GATES CORRIDOR + +- Name: Temple of Time Yellow Gates Corridor Near Entrance + Region: Temple of Time + Locations: + Temple of Time First Staircase Gohma Gate Chest: Nothing + Exits: + Temple of Time Yellow Gates Corridor Near Central Mechnical Platform Room: Clawshot or Gale_Boomerang or Bow or Ball_and_Chain + Temple of Time Entrance: Nothing + +- Name: Temple of Time Yellow Gates Corridor Near Central Mechnical Platform Room + Region: Temple of Time + Events: + Can Teleport Giant Statue to Temple of Time Entrance: Dominion_Rod and 'Can_Teleport_Giant_Statue_to_Temple_of_Time_Yellow_Gates_Corridor' + Locations: + Temple of Time First Staircase Window Chest: Nothing + Temple of Time First Staircase Armos Chest: Can_Defeat_Armos + Exits: + Temple of Time Central Mechanical Platform Room Bottom: Human_Link # To pick up statues + Temple of Time Yellow Gates Corridor Near Entrance: Clawshot or 'Can_Teleport_Giant_Statue_to_Temple_of_Time_Entrance' + +# TEMPLE OF TIME CENTRAL MECHANICAL PLATFORM ROOM + +- Name: Temple of Time Central Mechanical Platform Room Bottom + Region: Temple of Time + Locations: + Temple of Time Poe Behind Gate: Dominion_Rod and Can_Use_Senses + Exits: + Temple of Time Central Mechanical Platform Room Top: Spinner + Temple of Time Yellow Gates Corridor Near Central Mechnical Platform Room: Nothing + +- Name: Temple of Time Central Mechanical Platform Room Top + Region: Temple of Time + Events: + Can Teleport Giant Statue to Temple of Time Yellow Gates Corridor: Dominion_Rod and 'Can_Teleport_Giant_Statue_to_Temple_of_Time_Central_Mechanical_Platform' + Exits: + Temple of Time Central Mechanical Platform Room Near Armos Antechamber: Human_Link # To pick up statues + Temple of Time Moving Walls Corridor Near Central Platform Room: count(Temple_of_Time_Small_Key, 2) or Small_Keys == Keysy + Temple of Time Central Mechanical Platform Room Bottom: Nothing + +- Name: Temple of Time Central Mechanical Platform Room Near Armos Antechamber + Region: Temple of Time + Exits: + Temple of Time Armos Antechamber: Nothing + Temple of Time Central Mechanical Platform Room Top: Nothing + +# TEMPLE OF TIME ARMOS ANTECHAMBER + +- Name: Temple of Time Armos Antechamber + Region: Temple of Time + Locations: + Temple of Time Armos Antechamber East Chest: Can_Defeat_Armos + Temple of Time Armos Antechamber North Chest: Nothing + Temple of Time Armos Antechamber Statue Chest: Dominion_Rod + Exits: + Temple of Time Central Mechanical Platform Room Near Armos Antechamber: Nothing + +# TEMPLE OF TIME MOVING WALLS CORRIDOR + +- Name: Temple of Time Moving Walls Corridor Near Central Platform Room + Region: Temple of Time + Exits: + Temple of Time Moving Walls Corridor Middle: Bow or Clawshot + Temple of Time Central Mechanical Platform Room Top: Nothing + +- Name: Temple of Time Moving Walls Corridor Middle # This area's logic assumes you already have Bow or Clawshot to get here + Region: Temple of Time + Events: + Can Teleport Giant Statue to Temple of Time Central Mechanical Platform: Dominion_Rod and 'Can_Teleport_Giant_Statue_to_Temple_of_Time_Moving_Walls_Corridor' + Locations: + Temple of Time Moving Wall Beamos Room Chest: Nothing + Temple of Time Moving Wall Dinalfos Room Chest: Dominion_Rod + Temple of Time Second Hint Sign: Nothing + Exits: + Temple of Time Moving Walls Corridor Near Scales Room: Bow or Clawshot + Temple of Time Moving Walls Corridor Near Central Platform Room: Bow or Clawshot + +- Name: Temple of Time Moving Walls Corridor Near Scales Room + Region: Temple of Time + Exits: + Temple of Time Scale Room Bottom: Nothing + Temple of Time Moving Walls Corridor Middle: Bow or Clawshot + +# TEMPLE OF TIME SCALES ROOM + +- Name: Temple of Time Scale Room Bottom + Region: Temple of Time + Locations: + Temple of Time Scales Gohma Chest: Can_Defeat_Young_Gohma and Can_Defeat_Baby_Gohma + Exits: + Temple of Time Scales Room Top: Clawshot and Spinner + Temple of Time Scales Room Near Spike Trap Corridor: Human_Link # Need to throw a statue + +- Name: Temple of Time Scales Room Top + Region: Temple of Time + Locations: + Temple of Time Scales Upper Chest: Nothing + Temple of Time Poe Above Scales: Can_Use_Senses + Exits: + Temple of Time Floor Switch Puzzle Room: Nothing + Temple of Time Scales Room Near Spike Trap Corridor: Nothing + Temple of Time Scale Room Bottom: Nothing + +- Name: Temple of Time Scales Room Near Spike Trap Corridor + Region: Temple of Time + Events: + Can Teleport Giant Statue to Temple of Time Moving Walls Corridor: Dominion_Rod and 'Can_Teleport_Giant_Statue_to_Temple_of_Time_Scales_Room' + Exits: + Temple of Time Spike Trap Corridor Near Scales Room: Nothing + Temple of Time Scale Room Bottom: Nothing + +# TEMPLE OF TIME FLOOR SWITCH PUZZLE ROOM + +- Name: Temple of Time Floor Switch Puzzle Room + Region: Temple of Time + Locations: + Temple of Time Big Key Chest: Can_Defeat_Helmasaur and Clawshot + Temple of Time Floor Switch Puzzle Room Upper Chest: Clawshot + Exits: + Temple of Time Scales Room Top: Nothing + +# TEMPLE OF TIME SPIKE TRAP CORRIDOR + +- Name: Temple of Time Spike Trap Corridor Near Scales Room + Region: Temple of Time + Locations: + Temple of Time Gilloutine Chest: Nothing + Exits: + Temple of Time Spike Trap Corridor Baby Gohma Area: Human_Link # to pickup pot/statue + Temple of Time Scales Room Near Spike Trap Corridor: Nothing + +- Name: Temple of Time Spike Trap Corridor Baby Gohma Area + Region: Temple of Time + Locations: + Temple of Time Chest Before Darknut: Can_Defeat_Armos and Can_Defeat_Baby_Gohma and Can_Defeat_Young_Gohma + Exits: + Temple of Time Spike Trap Corridor Near Miniboss Room: Can_Defeat_Armos + Temple of Time Spike Trap Corridor Near Scales Room: "'Can_Teleport_Giant_Statue_to_Temple_of_Time_Scales_Room'" + +- Name: Temple of Time Spike Trap Corridor Near Miniboss Room + Region: Temple of Time + Events: + Can Teleport Giant Statue to Temple of Time Scales Room: Dominion_Rod and 'Can_Teleport_Giant_Statue_to_Temple_of_Time_Spike_Trap_Corridor' + Exits: + Darknut Miniboss Room: count(Temple_of_Time_Small_Key, 3) or Small_Keys == Keysy + Temple of Time Spike Trap Corridor Baby Gohma Area: "'Can_Teleport_Giant_Statue_to_Temple_of_Time_Scales_Room'" + +# DARKNUT MINIBOSS ROOM + +- Name: Darknut Miniboss Room + Events: + Can Teleport Giant Statue to Temple of Time Spike Trap Corridor: Dominion_Rod and Open_Door_of_Time == Off + Locations: + Temple of Time Darknut Chest: Can_Defeat_Darknut + Exits: + Temple of Time Spike Trap Corridor Near Miniboss Room: "'Can_Teleport_Giant_Statue_to_Temple_of_Time_Spike_Trap_Corridor'" + +# TEMPLE OF TIME CRUMBLING CORRIDOR + +- Name: Temple of Time Crumbling Corridor Near Entrance + Region: Temple of Time + Exits: + Temple of Time Crumbling Corridor Near Boss Door: Dominion_Rod + Temple of Time Entrance Near Crumbling Corridor: Nothing + +- Name: Temple of Time Crumbling Corridor Near Boss Door + Region: Temple of Time + Exits: + Temple of Time Boss Room: Temple_of_Time_Big_Key or Big_Keys == Keysy + +# TEMPLE OF TIME BOSS ROOM + +- Name: Temple of Time Boss Room + Events: + Can Complete Temple of Time: Can_Defeat_Armogohma + Locations: + Temple of Time Armogohma Heart Container: Can_Defeat_Armogohma + Temple of Time Dungeon Reward: Can_Defeat_Armogohma + Exits: + Sacred Grove Past Behind Window: Can_Defeat_Armogohma \ No newline at end of file diff --git a/mods/randomizer/generator/data/world/overworld/Eldin Province.yaml b/mods/randomizer/generator/data/world/overworld/Eldin Province.yaml new file mode 100644 index 0000000000..ca42c4320a --- /dev/null +++ b/mods/randomizer/generator/data/world/overworld/Eldin Province.yaml @@ -0,0 +1,632 @@ + +# KAKARIKO GORGE + +- Name: Kakariko Gorge + Map Sector: Eldin Province + Region: Kakariko Gorge + Twilight: Eldin + Can Warp: True + Locations: + Kakariko Gorge Warp Portal: Nothing + Kakariko Gorge Owl Statue Chest: Restored_Dominion_Rod + Kakariko Gorge Double Clawshot Chest: Double_Clawshots + Kakariko Gorge Spire Heart Piece: Clawshot or Gale_Boomerang + Kakariko Gorge Owl Statue Boulder Rupee: Can_Smash + Kakariko Gorge Spire Boulder Rupee: Can_Smash + Kakariko Gorge Owl Statue Sky Character: Restored_Dominion_Rod + Kakariko Gorge Male Pill Bug: Nothing + Kakariko Gorge Female Pill Bug: Nothing + Kakariko Gorge Poe: Can_Use_Senses and Can_Complete_MDH and Can_Complete_All_Twilight and Night + Kakariko Gorge Hint Sign: Nothing + Exits: + Kakariko Gorge Cave Entrance: Can_Smash + Kakariko Gorge Keese Grotto: Can_Dig + Kakariko Gorge Behind Gate: Nothing + Eldin Field: Can_Smash + Faron Field: Nothing + +- Name: Kakariko Gorge Cave Entrance + Map Sector: Eldin Province + Region: Kakariko Gorge + Twilight: Eldin + Can Warp: True + Exits: + Kakariko Gorge: Can_Smash + Eldin Lantern Cave: Nothing + +- Name: Eldin Lantern Cave + Locations: + Eldin Lantern Cave First Chest: Can_Break_Webs + Eldin Lantern Cave Lantern Chest: Lantern + Eldin Lantern Cave Second Chest: Can_Break_Webs + Eldin Lantern Cave Poe: Can_Break_Webs and Can_Use_Senses + Exits: + Kakariko Gorge Cave Entrance: Nothing + +- Name: Kakariko Gorge Keese Grotto + Exits: + Kakariko Gorge: Nothing + +- Name: Kakariko Gorge Behind Gate + Region: Kakariko Gorge + Twilight: Eldin + Exits: + Lower Kakariko Village: Nothing + Kakariko Gorge: Wolf_Link or Gate_Keys or Small_Keys == Keysy or Twilight + +# KAKARIKO VILLAGE & GRAVEYARD + +- Name: Lower Kakariko Village + Map Sector: Eldin Province + Region: Kakariko Village + Twilight: Eldin + Can Warp: True + Events: + Can Start Springwater Rush: Can_Talk_to_Springwater_Goron + Locations: + Kakariko Village Warp Portal: Nothing + Eldin Spring Underwater Chest: Can_Smash and Iron_Boots + Eldin Spring Underwater Boulder Rupee: Water_Bombs and (Iron_Boots or Zora_Armor) and Can_Complete_Eldin_Twilight + Kakariko Village Bomb Rock Spire Heart Piece: Bombs and Gale_Boomerang + Kakariko Village Bell Rupee: Shadow_Crystal and Can_Launch_Bombs and Can_Complete_Eldin_Twilight + Kakariko Village Hot Spring Ledge Box Rupee: (Shadow_Crystal or Gale_Boomerang) and Can_Complete_Eldin_Twilight + Kakariko Village Spring Shortcut Box Rupee 1: Can_Smash and Can_Complete_Eldin_Twilight + Kakariko Village Spring Shortcut Box Rupee 2: Can_Smash and Can_Complete_Eldin_Twilight + Kakariko Village Ant House Ledge Box Rupee: Gale_Boomerang and Can_Complete_Eldin_Twilight + Kakariko Village Hint Sign: Nothing + Exits: + Renados Sanctuary Front West Door Exterior: Nothing + Renados Sanctuary Front East Door Exterior: Nothing + Renados Sanctuary Back West Door Exterior: Nothing + Renados Sanctuary Back East Door Exterior: Nothing + Kakariko Renados Sanctuary: Twilight + Kakariko Graveyard: Nothing + Kakariko Malo Mart: Twilight or (Can_Open_Doors and Day) + Elde Inn North Door Exterior: Nothing + Elde Inn South Door Exterior: Nothing + Kakariko Elde Inn: Twilight + Kakariko Bug House Door: Nothing + Kakariko Bug House Ceiling Hole: Nothing + Kakariko Barnes Bomb Shop Lower: Twilight or (Can_Open_Doors and Day) + Upper Kakariko Village: Can_Smash or ('Can_Complete_Goron_Mines' and Day) + Death Mountain Near Kakariko: Nothing + Kakariko Village Behind Gate: Not_Twilight + Kakariko Gorge Behind Gate: Nothing + +- Name: Renados Sanctuary Front East Door Exterior + Twilight: Eldin + Can Transform: Never + Exits: + Renados Sanctuary Front East Door Interior: Can_Open_Doors + Lower Kakariko Village: Nothing + +- Name: Renados Sanctuary Front West Door Exterior + Twilight: Eldin + Can Transform: Never + Exits: + Renados Sanctuary Front West Door Interior: Can_Open_Doors + Lower Kakariko Village: Nothing + +- Name: Renados Sanctuary Back East Door Exterior + Twilight: Eldin + Can Transform: Never + Exits: + Renados Sanctuary Back East Door Interior: Can_Open_Doors + Lower Kakariko Village: Nothing + +- Name: Renados Sanctuary Back West Door Exterior + Twilight: Eldin + Can Transform: Never + Exits: + Renados Sanctuary Back West Door Interior: Can_Open_Doors + Lower Kakariko Village: Nothing + +- Name: Renados Sanctuary Front East Door Interior + Twilight: Eldin + Can Transform: Never + Exits: + Renados Sanctuary Front East Door Exterior: Can_Open_Doors + Kakariko Renados Sanctuary: Nothing + +- Name: Renados Sanctuary Front West Door Interior + Twilight: Eldin + Can Transform: Never + Exits: + Renados Sanctuary Front West Door Exterior: Can_Open_Doors + Kakariko Renados Sanctuary: Nothing + +- Name: Renados Sanctuary Back East Door Interior + Twilight: Eldin + Can Transform: Never + Exits: + Renados Sanctuary Back East Door Exterior: Can_Open_Doors + Kakariko Renados Sanctuary: Nothing + +- Name: Renados Sanctuary Back West Door Interior + Twilight: Eldin + Can Transform: Never + Exits: + Renados Sanctuary Back West Door Exterior: Can_Open_Doors + Kakariko Renados Sanctuary: Nothing + +- Name: Kakariko Renados Sanctuary + Twilight: Eldin + Can Transform: If Transform Anywhere + Events: + Can Show Ilia Wooden Statue: Wooden_Statue + Can Show Ilia Ilias Charm: Ilias_Charm + Locations: + Renados Letter: "'Can_Complete_Temple_of_Time'" + Ilia Memory Reward: "'Can_Show_Ilia_Ilias_Charm'" + Exits: + Kakariko Renados Sanctuary Basement: Nothing + Renados Sanctuary Front East Door Interior: Nothing + Renados Sanctuary Front West Door Interior: Nothing + Renados Sanctuary Back East Door Interior: Nothing + Renados Sanctuary Back West Door Interior: Nothing + +- Name: Kakariko Renados Sanctuary Basement + Twilight: Eldin + Locations: + Sanctuary Basement Twilit Insect 1: Can_Defeat_Eldin_Twilit_Insect + Sanctuary Basement Twilit Insect 2: Can_Defeat_Eldin_Twilit_Insect + Sanctuary Basement Twilit Insect 3: Can_Defeat_Eldin_Twilit_Insect + Exits: + Kakariko Renados Sanctuary: Human_Link + +- Name: Kakariko Graveyard + Map Sector: Eldin Province + Region: Kakariko Graveyard + Twilight: Eldin + Can Warp: True + Events: + Can Follow Rutella: Gate_Keys or Small_Keys == Keysy + Locations: + Kakariko Graveyard Lantern Chest: Lantern + Kakariko Graveyard Male Ant: Nothing + Kakariko Graveyard Grave Poe: Can_Use_Senses and Night + Kakariko Graveyard Open Poe: Can_Use_Senses and Night + Kakariko Graveyard Golden Wolf: Can_Complete_Eldin_Twilight and 'Howl_at_Snowpeak_Mountain_Howling_Stone' + Kakariko Graveyard Twilit Insect: Can_Defeat_Eldin_Twilit_Insect + Exits: + Kakariko Graveyard Pond: "'Can_Follow_Rutella'" + Lower Kakariko Village: Nothing + +- Name: Kakariko Graveyard Pond + Map Sector: Eldin Province + Region: Kakariko Graveyard + Twilight: Eldin + Can Warp: True + Locations: + Kakariko Graveyard Underwater Boulder Rupee: (Iron_Boots or Zora_Armor) and Water_Bombs + Rutelas Blessing: "'Can_Follow_Rutella'" + Gift From Ralis: Asheis_Sketch and 'Can_Follow_Rutella' + Kakariko Graveyard Hint Sign: Nothing + Exits: + Lake Hylia: Water_Bombs and (Iron_Boots or Zora_Armor) + Kakariko Graveyard: "'Can_Follow_Rutella'" + +- Name: Kakariko Malo Mart + Twilight: Eldin + Can Transform: If Transform Anywhere + Events: + Can Fund Malo Mart: Can_Talk_to_Humans and Not_Twilight and Can_Complete_Lanayru_Twilight and 'Can_Farm_Lots_of_Rupees' + Locations: + Kakariko Village Malo Mart Hylian Shield: Can_Talk_to_Humans and Not_Twilight and 'Can_Farm_Lots_of_Rupees' + Kakariko Village Malo Mart Hawkeye: "'Can_Start_Talo_Sharpshooting' and 'Can_Farm_Lots_of_Rupees'" + Kakariko Village Malo Mart Red Potion: Can_Talk_to_Humans and Not_Twilight and 'Can_Farm_Lots_of_Rupees' + Kakariko Village Malo Mart Wooden Shield: Can_Talk_to_Humans and Not_Twilight and 'Can_Farm_Lots_of_Rupees' + Kakariko Malo Mart Twilit Insect: Can_Defeat_Eldin_Twilit_Insect + Exits: + Lower Kakariko Village: Nothing + +- Name: Elde Inn North Door Exterior + Twilight: Eldin + Can Transform: Never + Exits: + Elde Inn North Door Interior: Can_Open_Doors and Day + Lower Kakariko Village: Nothing + +- Name: Elde Inn South Door Exterior + Twilight: Eldin + Can Transform: Never + Exits: + Elde Inn South Door Interior: Can_Open_Doors and Day + Lower Kakariko Village: Nothing + +- Name: Elde Inn North Door Interior + Twilight: Eldin + Can Transform: Never + Exits: + Elde Inn North Door Exterior: Can_Open_Doors and Day + Kakariko Elde Inn: Nothing + +- Name: Elde Inn South Door Interior + Twilight: Eldin + Can Transform: Never + Exits: + Elde Inn South Door Exterior: Can_Open_Doors and Day + Kakariko Elde Inn: Nothing + +- Name: Kakariko Elde Inn + Twilight: Eldin + Locations: + Kakariko Inn Chest: Nothing + Kakariko Inn Pipe Twilit Insect: Can_Defeat_Eldin_Twilit_Insect + Kakariko Inn Bedroom Twilit Insect: Can_Defeat_Eldin_Twilit_Insect + Exits: + Elde Inn North Door Interior: Nothing + Elde Inn South Door Interior: Nothing + +- Name: Kakariko Bug House Door + Twilight: Eldin + Can Transform: Never + Exits: + Kakariko Bug House: Can_Open_Doors + Lower Kakariko Village: Nothing + +- Name: Kakariko Bug House Ceiling Hole + Twilight: Eldin + Can Transform: Never + Exits: + Kakariko Bug House: Nothing + Lower Kakariko Village: Nothing + +- Name: Kakariko Bug House + Twilight: Eldin + Locations: + Kakariko Village Female Ant: Nothing + Kakariko Bug House Twilit Insect: Can_Defeat_Eldin_Twilit_Insect + Exits: + Kakariko Bug House Door: Can_Open_Doors + Kakariko Bug House Ceiling Hole: Can_Midna_Jump or Twilight + +- Name: Kakariko Barnes Bomb Shop Lower + Twilight: Eldin + Events: + Can Refill Regular Bombs: "'Can_Farm_Lots_of_Rupees'" + Can Refill Water Bombs: "'Can_Farm_Lots_of_Rupees'" + Locations: + Barnes Bomb Bag: "'Can_Farm_Lots_of_Rupees'" + Exits: + Kakariko Barnes Bomb Shop Upper: Nothing + Lower Kakariko Village: Can_Open_Doors + +- Name: Kakariko Barnes Bomb Shop Upper + Twilight: Eldin + Locations: + Barnes Bomb Shop Twilit Insect: Can_Defeat_Eldin_Twilit_Insect and Can_Survive_One_Bonk + Exits: + Kakariko Barnes Bomb Shop Lower: Nothing + Upper Kakariko Village: Nothing + +- Name: Upper Kakariko Village + Map Sector: Eldin Province + Region: Kakariko Village + Twilight: Eldin + Can Warp: True + Locations: + Kakariko Village Bomb Shop Poe: Can_Use_Senses and Night + Kakariko Village Watchtower Poe: Can_Use_Senses and Night + Kakariko Watchtower Alcove Chest: Can_Smash + Kakariko Destroyed Building Twilit Insect 1: Can_Defeat_Eldin_Twilit_Insect + Kakariko Destroyed Building Twilit Insect 2: Can_Defeat_Eldin_Twilit_Insect + Kakariko Destroyed Building Twilit Insect 3: Can_Defeat_Eldin_Twilit_Insect + Exits: + Kakariko Watchtower Lower Door: Nothing + Kakariko Watchtower Dig Spot: Nothing + Kakariko Top of Watchtower: Day and 'Can_Complete_Goron_Mines' + Kakariko Barnes Bomb Shop Upper: Nothing + Lower Kakariko Village: Nothing + +- Name: Kakariko Watchtower Lower Door + Twilight: Eldin + Can Transform: Never + Exits: + Kakariko Watchtower Lower Interior: Can_Open_Doors + Upper Kakariko Village: Nothing + +- Name: Kakariko Watchtower Dig Spot + Twilight: Eldin + Can Transform: Never + Exits: + Kakariko Watchtower Lower Interior: Can_Dig or Twilight + Upper Kakariko Village: Nothing + +- Name: Kakariko Watchtower Lower Interior + Twilight: Eldin + Locations: + Kakariko Watchtower Twilit Insect: Can_Defeat_Eldin_Twilit_Insect + Exits: + Kakariko Watchtower Upper Interior: Can_Climb_Ladders + Kakariko Watchtower Lower Door: Can_Open_Doors + Kakariko Watchtower Dig Spot: Can_Dig or Twilight + +- Name: Kakariko Watchtower Upper Interior + Twilight: Eldin + Locations: + Kakariko Watchtower Chest: Nothing + Exits: + Kakariko Watchtower Lower Interior: Nothing + Kakariko Top of Watchtower: Can_Open_Doors + +- Name: Kakariko Top of Watchtower + Map Sector: Eldin Province + Region: Kakariko Village + Twilight: Eldin + Can Warp: True + Events: + Can Start Talo Sharpshooting: Day and Can_Climb_Ladders and Bow and 'Can_Complete_Goron_Mines' + Locations: + Talo Sharpshooting: "'Can_Start_Talo_Sharpshooting'" + Exits: + Kakariko Watchtower Upper Interior: Can_Open_Doors + +- Name: Kakariko Village Behind Gate + Map Sector: Eldin Province + Region: Kakariko Village + Twilight: Eldin + Can Warp: True + Exits: + Eldin Field: Nothing + Lower Kakariko Village: Gate_Keys or Small_Keys == Keysy + +# DEATH MOUNTAIN + +- Name: Death Mountain Near Kakariko + Map Sector: Eldin Province + Region: Death Mountain + Twilight: Eldin + Can Warp: True + Exits: + Death Mountain Trail: Iron_Boots or 'Can_Complete_Goron_Mines' or Twilight + Lower Kakariko Village: Nothing + +- Name: Death Mountain Trail + Map Sector: Eldin Province + Region: Death Mountain + Twilight: Eldin + Can Warp: True + Events: + Howl at Death Mountain Howling Stone: Can_Howl + Locations: + Death Mountain Trail Twilit Insect Near Howling Stone: Can_Defeat_Eldin_Twilit_Insect + Death Mountain Alcove Chest: Clawshot or 'Can_Complete_Goron_Mines' + Death Mountain Trail Poe: Can_Use_Senses and 'Can_Complete_Goron_Mines' + Exits: + Death Mountain Volcano: Nothing + Death Mountain Near Kakariko: Nothing + +- Name: Death Mountain Volcano + Map Sector: Eldin Province + Region: Death Mountain + Twilight: Eldin + Can Warp: True + Locations: + Death Mountain Warp Portal: Nothing + Death Mountain Volcano Pipe Ledge Rock Rupee: Can_Complete_Eldin_Twilight and (Can_Defeat_Goron or 'Can_Complete_Goron_Mines') + Death Mountain Trail Twilit Insect on Wall: Can_Defeat_Eldin_Twilit_Insect + Exits: + Death Mountain Hot Spring: Twilight or (Can_Defeat_Goron or 'Can_Complete_Goron_Mines') + Death Mountain Lower Elevator: Goron_Mines_Entrance == Open or 'Can_Access_Death_Mountain_Lower_Elevator' + Death Mountain Outside Sumo Hall: Iron_Boots and (Can_Defeat_Goron or 'Can_Complete_Goron_Mines') and Can_Complete_Eldin_Twilight + Death Mountain Trail: Nothing + +- Name: Death Mountain Hot Spring + Map Sector: Eldin Province + Region: Death Mountain + Twilight: Eldin + Can Warp: True + Events: + Can Buy Wooden Shield: Can_Talk_to_Humans and 'Can_Farm_Rupees' and (Can_Defeat_Goron or 'Can_Complete_Goron_Mines') + Locations: + Death Mountain Trail Twilit Insect in Hot Spring: Can_Defeat_Eldin_Twilit_Insect + Exits: + Death Mountain Volcano: Nothing + +- Name: Death Mountain Lower Elevator + Map Sector: Eldin Province + Region: Death Mountain + Twilight: Eldin + Can Warp: True + Exits: + Death Mountain Sumo Hall Elevator: Iron_Boots + Death Mountain Volcano: Nothing + +- Name: Death Mountain Outside Sumo Hall + Map Sector: Eldin Province + Region: Death Mountain + Twilight: Eldin + Can Warp: True + Locations: + Death Mountain Volcano Ledge Rupee 1: Can_Complete_Eldin_Twilight and (Can_Defeat_Goron or 'Can_Complete_Goron_Mines') + Death Mountain Volcano Ledge Rupee 2: Can_Complete_Eldin_Twilight and (Can_Defeat_Goron or 'Can_Complete_Goron_Mines') + Death Mountain Volcano Ledge Rupee 3: Can_Complete_Eldin_Twilight and (Can_Defeat_Goron or 'Can_Complete_Goron_Mines') + Exits: + Death Mountain Sumo Hall: Nothing + Death Mountain Hot Spring: Nothing + Death Mountain Volcano: Nothing + +- Name: Death Mountain Sumo Hall Elevator + Map Sector: Eldin Province + Region: Death Mountain + Twilight: Eldin + Can Warp: True + Exits: + Death Mountain Sumo Hall: Goron_Mines_Entrance != Closed or 'Can_Wrestle_Goron_Elder' + Death Mountain Lower Elevator: Iron_Boots + +- Name: Death Mountain Sumo Hall + Can Transform: If Transform Anywhere + Events: + Can Wrestle Goron Elder: Iron_Boots + Exits: + Death Mountain Sumo Hall Goron Mines Tunnel: Goron_Mines_Entrance != Closed or 'Can_Wrestle_Goron_Elder' + Death Mountain Sumo Hall Elevator: Goron_Mines_Entrance != Closed or 'Can_Wrestle_Goron_Elder' + Death Mountain Outside Sumo Hall: Nothing + +- Name: Death Mountain Sumo Hall Goron Mines Tunnel + Exits: + Goron Mines Entrance: Nothing + Death Mountain Sumo Hall: Goron_Mines_Entrance != Closed or 'Can_Wrestle_Goron_Elder' + +# ELDIN FIELD + +- Name: Eldin Field + Map Sector: Eldin Province + Region: Eldin Field + Twilight: Eldin + Can Warp: True + Events: + Can Finish Goron Springwater Rush: "'Can_Start_Springwater_Rush' and (Skip_Bridge_Donation == On or 'Can_Fund_Malo_Mart')" + Locations: + Bridge of Eldin Warp Portal: Can_Defeat_Shadow_Beast + Eldin Field Bomb Rock Chest: Can_Smash + Bridge of Eldin Owl Statue Chest: Restored_Dominion_Rod + Goron Springwater Rush: "'Can_Finish_Goron_Springwater_Rush'" + Eldin Field Male Grasshopper: Nothing + Eldin Field Female Grasshopper: Nothing + Bridge of Eldin Male Phasmid: Clawshot or Gale_Boomerang + Eldin Field Hint Sign: Nothing + Exits: + # Only allow logical access to the other side if we've already been there or the bridge donation is skipped + Eldin Field Near Castle Town: (Skip_Bridge_Donation == On and Can_Complete_Eldin_Twilight and Can_Complete_Lanayru_Twilight) or 'Can_Access_Eldin_Field_Near_Castle_Town' + Eldin Field Bomskit Grotto: Can_Dig + Eldin Field Water Bomb Fish Grotto: Can_Dig + Eldin Field North of Bridge: Nothing + Kakariko Gorge: Can_Smash + Kakariko Village Behind Gate: Nothing + +- Name: Eldin Field Near Castle Town + Map Sector: Eldin Province + Region: Eldin Field + Twilight: Eldin + Can Warp: True + Exits: + Outside Castle Town East: Nothing + Eldin Field: Skip_Bridge_Donation == On or 'Can_Fund_Malo_Mart' + +- Name: Eldin Field Bomskit Grotto + Locations: + Eldin Field Bomskit Grotto Left Chest: Nothing + Eldin Field Bomskit Grotto Lantern Chest: Lantern + Exits: + Eldin Field: Nothing + +- Name: Eldin Field Water Bomb Fish Grotto + Events: + Can Refill Water Bombs: Fishing_Rod + Locations: + Eldin Field Water Bomb Fish Grotto Chest: Nothing + Exits: + Eldin Field: Nothing + +- Name: Eldin Field North of Bridge + Map Sector: Eldin Province + Region: North Eldin + Twilight: Eldin + Can Warp: True + Locations: + Bridge of Eldin Boulder Rupee: Can_Smash + Bridge of Eldin Owl Statue Sky Character: Restored_Dominion_Rod + Bridge of Eldin Female Phasmid: Clawshot or Gale_Boomerang + Exits: + Eldin Field Lava Cave Upper Ledge: Clawshot + North Eldin Field: Can_Smash + Eldin Field: Nothing + +- Name: Eldin Field Lava Cave Upper Ledge + Map Sector: Eldin Province + Region: North Eldin + Twilight: Eldin + Can Warp: True + Exits: + Eldin Field Lava Cave Upper: Nothing + Eldin Field North of Bridge: Nothing + +- Name: Eldin Field Lava Cave Upper + Locations: + Eldin Stockcave Upper Chest: Iron_Boots + Exits: + Eldin Field Lava Cave Lower: Iron_Boots + Eldin Field Lava Cave Upper Ledge: Nothing + +- Name: Eldin Field Lava Cave Lower + Locations: + Eldin Stockcave Lantern Chest: Lantern + Eldin Stockcave Lowest Chest: Nothing + Exits: + Eldin Field Lava Cave Lower Ledge: Nothing + +- Name: Eldin Field Lava Cave Lower Ledge + Map Sector: Eldin Province + Region: North Eldin + Twilight: Eldin + Can Warp: True + Exits: + Eldin Field North of Bridge: Clawshot + Eldin Field Lava Cave Lower: Nothing + +- Name: North Eldin Field + Map Sector: Eldin Province + Region: North Eldin + Twilight: Eldin + Can Warp: True + Locations: + North Eldin Field Hint Sign: Nothing + Exits: + Eldin Field Grotto Platform: Spinner + Eldin Field Outside Hidden Village: Ilia_Memory_Quest >= Charm or 'Can_Show_Ilia_Wooden_Statue' + Lanayru Field: Nothing + Eldin Field North of Bridge: Nothing + +- Name: Eldin Field Grotto Platform + Map Sector: Eldin Province + Region: North Eldin + Twilight: Eldin + Can Warp: True + Exits: + Eldin Field Stalfos Grotto: Can_Dig + North Eldin Field: Spinner # TODO: Check Savewarp + +- Name: Eldin Field Stalfos Grotto + Locations: + Eldin Field Stalfos Grotto Right Small Chest: Nothing + Eldin Field Stalfos Grotto Left Small Chest: Nothing + Eldin Field Stalfos Grotto Stalfos Chest: Can_Defeat_Stalfos + Exits: + Eldin Field Grotto Platform: Nothing + +- Name: Eldin Field Outside Hidden Village + Map Sector: Eldin Province + Region: North Eldin + Twilight: Eldin + Can Warp: True + Exits: + Hidden Village: Nothing + North Eldin Field: "'Can_Show_Ilia_Wooden_Statue'" + +# HIDDEN VILLAGE + +- Name: Hidden Village + Map Sector: Eldin Province + Region: Hidden Village + Can Warp: True + Can Transform: If Transform Anywhere + Events: + Howl at Hidden Village Howling Stone: Can_Howl + Locations: + Cats Hide and Seek Minigame: Can_Talk_to_Animals and Bow and Clawshot and 'Can_Show_Ilia_Ilias_Charm' + Ilia Charm: Bow + Hidden Village Poe: Night and Can_Talk_to_Animals and Bow and Clawshot and 'Can_Show_Ilia_Ilias_Charm' + Hidden Village Hint Sign: Nothing + Exits: + Hidden Village Impaz House: Bow and Dominion_Rod and Can_Open_Doors + Eldin Field Outside Hidden Village: Nothing + +- Name: Hidden Village Impaz House + Can Transform: If Transform Anywhere + Locations: + Skybook From Impaz: Bow and Dominion_Rod and 'Can_Access_Hidden_Village' + Exits: + Hidden Village: Nothing diff --git a/mods/randomizer/generator/data/world/overworld/Faron Province.yaml b/mods/randomizer/generator/data/world/overworld/Faron Province.yaml new file mode 100644 index 0000000000..8f4f5df758 --- /dev/null +++ b/mods/randomizer/generator/data/world/overworld/Faron Province.yaml @@ -0,0 +1,396 @@ + +# SOUTH FARON WOODS + +- Name: South Faron Woods + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Events: + Can Refill Lantern Oil: "'Can_Farm_Rupees'" + Locations: + South Faron Warp Portal: Can_Complete_Prologue + Faron Woods Coro Boulder Rupee 1: Can_Smash + Faron Woods Coro Boulder Rupee 2: Can_Smash + Faron Woods Coro Boulder Rupee 3: Can_Smash + Faron Woods Coro Boulder Rupee 4: Can_Smash + Coro Bottle: Can_Complete_Prologue + South Faron Woods Twilit Insect in Tunnel 1: Can_Defeat_Faron_Twilit_Insect + South Faron Woods Twilit Insect in Tunnel 2: Can_Defeat_Faron_Twilit_Insect + South Faron Woods Coros House Exterior Twilit Insect: Can_Defeat_Faron_Twilit_Insect + South Faron Woods Hint Sign: Nothing + Exits: + South Faron Woods Coros Ledge: Can_Midna_Jump or Twilight + Faron Woods Coros House Lower: Can_Open_Doors + South Faron Woods Behind Gate: Nothing # Coro Key is Vanilla if forest is closed + South Faron Woods Owl Statue Area: Can_Smash + Faron Field: Can_Clear_Forest + Ordon Bridge: Nothing + +- Name: South Faron Woods Coros Ledge + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Exits: + South Faron Woods: Nothing + Faron Woods Coros House Upper: Nothing + +- Name: Faron Woods Coros House Upper + Twilight: Faron + Exits: + Faron Woods Coros House Lower: Nothing + South Faron Woods Coros Ledge: Nothing + +- Name: Faron Woods Coros House Lower + Twilight: Faron + Locations: + Faron Woods Coros House Interior Twilit Insect 1: Can_Defeat_Faron_Twilit_Insect + Faron Woods Coros House Interior Twilit Insect 2: Can_Defeat_Faron_Twilit_Insect + Exits: + Faron Woods Coros House Upper: Wolf_Link # Only wolf link can climb the ledge + South Faron Woods: Can_Open_Doors + +- Name: South Faron Woods Owl Statue Area + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Events: + Can Move Faron Woods Owl Statue: Can_Clear_Forest and Restored_Dominion_Rod + Locations: + Faron Woods Owl Statue Sky Character: Can_Clear_Forest and Restored_Dominion_Rod + Exits: + South Faron Woods Above Owl Statue: Can_Midna_Jump and 'Can_Move_Faron_Woods_Owl_Statue' + South Faron Woods: Can_Smash + +- Name: South Faron Woods Above Owl Statue + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Exits: + Mist Area Near Owl Statue Chest: Nothing + South Faron Woods Owl Statue Area: Nothing + +- Name: South Faron Woods Behind Gate + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Locations: + South Faron Woods Twilit Insect Behind Gate 1: Can_Defeat_Faron_Twilit_Insect + South Faron Woods Twilit Insect Behind Gate 2: Can_Defeat_Faron_Twilit_Insect + Exits: + Faron Woods Cave South: Nothing + South Faron Woods: Can_Dig or Can_Clear_Forest or 'Can_Access_South_Faron_Woods' + +# FARON WOODS CAVE + +- Name: Faron Woods Cave South + Twilight: Faron + Exits: + Faron Woods Cave: Nothing + South Faron Woods Behind Gate: Nothing + +- Name: Faron Woods Cave + Twilight: Faron + Locations: + South Faron Cave Chest: Nothing + Exits: + Faron Woods Cave North: Nothing + Faron Woods Cave South: Nothing + +- Name: Faron Woods Cave North + Twilight: Faron + Exits: + Mist Area Near Faron Woods Cave: Nothing + Faron Woods Cave: Nothing + +# MIST AREA + +- Name: Mist Area Near Faron Woods Cave + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Locations: + Faron Mist Twilit Insect on Wall 1: Can_Defeat_Faron_Twilit_Insect + Faron Mist Twilit Insect on Wall 2: Can_Defeat_Faron_Twilit_Insect + Exits: + Mist Area Inside Mist: Lantern + Mist Area Under Owl Statue Chest: Can_Midna_Jump or Twilight + Faron Woods Cave North: Nothing + +- Name: Mist Area Inside Mist + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Locations: + Faron Mist Stump Chest: Lantern and Can_Complete_Prologue + Faron Mist North Chest: Lantern and Can_Complete_Prologue + Faron Mist South Chest: Lantern and Can_Complete_Prologue + Exits: + Mist Area Near Faron Woods Cave: Lantern + Mist Area Under Owl Statue Chest: Lantern + Mist Area Outside Faron Mist Cave: Lantern + Mist Area Near North Faron Woods: Lantern + +- Name: Mist Area Under Owl Statue Chest + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Exits: + Mist Area Inside Mist: Lantern + Mist Area Center Stump: Can_Midna_Jump or Twilight + +- Name: Mist Area Near Owl Statue Chest + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Locations: + Faron Woods Owl Statue Chest: Nothing + Exits: + Mist Area Under Owl Statue Chest: Nothing + South Faron Woods Above Owl Statue: Nothing + +- Name: Mist Area Center Stump + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Locations: + Faron Mist Poe: Can_Use_Senses and Can_Complete_Prologue + Faron Mist Twilit Insect on Center Stump 1: Can_Defeat_Faron_Twilit_Insect + Faron Mist Twilit Insect on Center Stump 2: Can_Defeat_Faron_Twilit_Insect + Faron Mist Twilit Insect on Center Stump 3: Can_Defeat_Faron_Twilit_Insect + Exits: + Mist Area Inside Mist: Lantern + Mist Area Near North Faron Woods: Can_Midna_Jump or Twilight + +- Name: Mist Area Outside Faron Mist Cave + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Exits: + Mist Area Faron Mist Cave: Nothing + Mist Area Inside Mist: Lantern + +- Name: Mist Area Near North Faron Woods + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Locations: + Faron Mist Burrowing Twilit Insect 1: Can_Defeat_Faron_Twilit_Insect + Faron Mist Burrowing Twilit Insect 2: Can_Defeat_Faron_Twilit_Insect + Exits: + North Faron Woods: North_Faron_Woods_Gate_Key or Skip_Prologue == On + Mist Area Near Faron Woods Cave: Can_Midna_Jump or Twilight + Mist Area Inside Mist: Lantern + +- Name: Mist Area Faron Mist Cave + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Locations: + Faron Mist Cave Open Chest: Nothing + Faron Mist Cave Lantern Chest: Lantern + Exits: + Mist Area Outside Faron Mist Cave: Nothing + +# NORTH FARON WOODS + +- Name: North Faron Woods + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can_Warp: True + Can Change Time: True + Events: + Can Refill Lantern Oil: "'Can_Farm_Rupees'" + Can Refill Slingshot Seeds: Can_Defeat_Deku_Baba + Locations: + North Faron Warp Portal: Can_Complete_Prologue + North Faron Woods Deku Baba Chest: Nothing + Faron Woods Golden Wolf: Can_Complete_Faron_Twilight + North Faron Woods Twilit Insect 1: Can_Defeat_Faron_Twilit_Insect + North Faron Woods Twilit Insect 2: Can_Defeat_Faron_Twilit_Insect + Exits: + North Faron Lost Woods Ledge: Can_Midna_Jump + Forest Temple Entrance: Nothing + Mist Area Near North Faron Woods: Nothing + +- Name: North Faron Lost Woods Ledge + Map Sector: Faron Province + Region: Faron Woods + Twilight: Faron + Can Warp: True + Can Change Time: True + Events: + Howl at North Faron Woods Howling Stone: Can_Howl + Exits: + Lost Woods: Nothing + # In North Faron Woods, when you are near the Lost Woods entrance, + # you can save-warp to the main part of North Faron Woods. + North Faron Woods: Nothing + + +# SACRED GROVE + +- Name: Lost Woods + Map Sector: Faron Province + Region: Sacred Grove + Can Warp: True + Can Change Time: True + Events: + Can Refill Arrows: Nothing + Lost Woods Skull Kid: Can_Defeat_Skull_Kid + Locations: + Lost Woods Lantern Chest: Lantern + Lost Woods Waterfall Poe: Can_Use_Senses and Night + Exits: + Lost Woods Lower Battle Arena: Sacred_Grove_Does_Not_Require_Skull_Kid == On or ('Lost_Woods_Skull_Kid' and Wolf_Link) + Lost Woods Upper Battle Arena: Sacred_Grove_Does_Not_Require_Skull_Kid == On or ('Lost_Woods_Skull_Kid' and Wolf_Link) + North Faron Lost Woods Ledge: Nothing + +- Name: Lost Woods Lower Battle Arena + Map Sector: Faron Province + Region: Sacred Grove + Can Warp: True + Can Change Time: True + Events: + Can Smash Lost Woods Rock: Can_Smash + Locations: + Sacred Grove Spinner Chest: Spinner + Lost Woods Boulder Poe: Can_Use_Senses and 'Can_Smash_Lost_Woods_Rock' and (Can_Defeat_Skull_Kid or Sacred_Grove_Does_Not_Require_Skull_Kid == On) + Exits: + # Human Link can't dig into the grotto, so we need to hide away blowing up the rock behind an event. + # By requiring only wolf in the direct logic statement, this tells the search algorithm that only wolf is + # allowed to go through this exit. + Lost Woods Baba Serpent Grotto: Can_Dig and 'Can_Smash_Lost_Woods_Rock' + Sacred Grove Lower: Can_Defeat_Skull_Kid or Sacred_Grove_Does_Not_Require_Skull_Kid == On + +- Name: Lost Woods Upper Battle Arena + Map Sector: Faron Province + Region: Sacred Grove + Can Warp: True + Can Change Time: True + Exits: + Sacred Grove Before Block: Can_Defeat_Skull_Kid or Sacred_Grove_Does_Not_Require_Skull_Kid == On + +- Name: Lost Woods Baba Serpent Grotto + Locations: + Sacred Grove Baba Serpent Grotto Chest: Can_Defeat_Baba_Serpent and Can_Knock_Down_Hanging_Baba + Exits: + Lost Woods Lower Battle Arena: Nothing + +- Name: Sacred Grove Before Block + Map Sector: Faron Province + Region: Sacred Grove + Can Warp: True + Exits: + Sacred Grove Upper: Nothing + Lost Woods Upper Battle Arena: Nothing + +- Name: Sacred Grove Upper + Map Sector: Faron Province + Region: Sacred Grove + Can Warp: True + Locations: + Sacred Grove Hint Sign: Nothing + Exits: + Sacred Grove Lower: Nothing + Sacred Grove Past: Has_Sword_For_Temple_of_Time and 'Can_Access_Sacred_Grove_Lower' and Can_Defeat_Shadow_Beast + +- Name: Sacred Grove Lower + Map Sector: Faron Province + Region: Sacred Grove + Can Warp: True + Locations: + Sacred Grove Warp Portal: Can_Defeat_Shadow_Beast + Sacred Grove Male Snail: Clawshot or Gale_Boomerang + Sacred Grove Master Sword Poe: Can_Use_Senses and Night + Sacred Grove Pedestal Master Sword: Nothing + Sacred Grove Pedestal Shadow Crystal: Nothing + Exits: + Lost Woods Lower Battle Arena: Nothing + Sacred Grove Upper: "'Can_Access_Sacred_Grove_Before_Block'" + +- Name: Sacred Grove Past + Locations: + Sacred Grove Past Owl Statue Chest: Dominion_Rod + Sacred Grove Female Snail: Clawshot or Gale_Boomerang + Sacred Grove Temple of Time Owl Statue Poe: Dominion_Rod and Can_Use_Senses + Exits: + Sacred Grove Past Behind Window: Has_Sword_For_Temple_of_Time + Sacred Grove Upper: Nothing + +- Name: Sacred Grove Past Behind Window + Exits: + Sacred Grove Past: Nothing + Temple of Time Entrance: Nothing + +# FARON FIELD + +- Name: Faron Field + Map Sector: Faron Province + Region: Faron Field + Can_Warp: True + Events: + Can Refill Arrows: Nothing + Locations: + Faron Field Bridge Chest: Clawshot + Faron Field Tree Heart Piece: Clawshot or Gale_Boomerang # or Ball and Chain if trick + Faron Field Male Beetle: Nothing + Faron Field Female Beetle: Clawshot or Gale_Boomerang # or Ball and Chain if trick + Faron Field Poe: Can_Use_Senses and Night + Faron Field Hint Sign: Nothing + Exits: + Faron Field Behind Boulder: Can_Use_Hot_Spring_Water and 'Can_Access_Outside_Castle_Town_South' + Kakariko Gorge: Nothing + Lake Hylia Bridge: Gate_Keys or Small_Keys == Keysy + Faron Field Corner Grotto: Can_Dig + Faron Field Fishing Grotto: Can_Dig + South Faron Woods: Nothing + +- Name: Faron Field Behind Boulder + Map Sector: Faron Province + Region: Faron Field + Can_Warp: True + Exits: + # If you enter Outside Castle Town from here while the boulder is still there, + # you get stuck and are forced to save-warp or portal-warp. + Outside Castle Town South Inside Boulder: Nothing + Faron Field: Can_Use_Hot_Spring_Water and 'Can_Access_Outside_Castle_Town_South' + +- Name: Faron Field Corner Grotto + Locations: + Faron Field Corner Grotto Right Chest: Nothing + Faron Field Corner Grotto Left Chest: Nothing + Faron Field Corner Grotto Rear Chest: Nothing + # Faron Field Corner Grotto Main Chest: Nothing # HD Only + Exits: + Faron Field: Nothing + +- Name: Faron Field Fishing Grotto + Exits: + Faron Field: Nothing diff --git a/mods/randomizer/generator/data/world/overworld/Gerudo Desert.yaml b/mods/randomizer/generator/data/world/overworld/Gerudo Desert.yaml new file mode 100644 index 0000000000..cc2fe4b259 --- /dev/null +++ b/mods/randomizer/generator/data/world/overworld/Gerudo Desert.yaml @@ -0,0 +1,192 @@ + +# GERUDO DESERT + +- Name: Gerudo Desert + Map Sector: Desert Province + Region: South Gerudo Desert + Can Warp: True + Locations: + Gerudo Desert Peahat Ledge Chest: Clawshot + Gerudo Desert East Canyon Chest: Nothing + Gerudo Desert Lone Small Chest: Nothing + Gerudo Desert West Canyon Chest: Clawshot + Gerudo Desert South Chest Behind Wooden Gates: Can_Defeat_Bulblin + Gerudo Desert Owl Statue Chest: Restored_Dominion_Rod + Gerudo Desert Owl Statue Sky Character: Restored_Dominion_Rod + Gerudo Desert Male Dayfly: Nothing + Gerudo Desert Female Dayfly: Nothing + Gerudo Desert East Poe: Can_Use_Senses and Night + Gerudo Desert Hint Sign: Nothing + Exits: + Gerudo Desert Skulltula Grotto: Can_Dig + Gerudo Desert Cave of Ordeals Plateau: Clawshot and Can_Defeat_Shadow_Beast + Gerudo Desert Basin: Nothing + Lake Hylia: Nothing # Modified rando savewarp + +- Name: Gerudo Desert Skulltula Grotto + Locations: + Gerudo Desert Skulltula Grotto Chest: Can_Defeat_Skulltula + Exits: + Gerudo Desert: Nothing + +- Name: Gerudo Desert Cave of Ordeals Plateau + Map Sector: Desert Province + Region: South Gerudo Desert + Can Warp: True + Locations: + Gerudo Desert Warp Portal: Can_Defeat_Shadow_Beast + Gerudo Desert Poe Above Cave of Ordeals: Can_Use_Senses and Night + Exits: + Cave of Ordeals: Nothing + Gerudo Desert: Nothing + +- Name: Gerudo Desert Basin + Map Sector: Desert Province + Region: North Gerudo Desert + Can Warp: True + Locations: + Gerudo Desert Northeast Chest Behind Gates: Can_Defeat_Bulblin and Can_Ride_Boars + Gerudo Desert Campfire North Chest: Nothing + Gerudo Desert Campfire East Chest: Can_Defeat_Bulblin and Can_Ride_Boars + Gerudo Desert Campfire West Chest: Can_Defeat_Bulblin and Can_Ride_Boars + Gerudo Desert Northwest Chest Behind Gates: Can_Defeat_Bulblin and Can_Ride_Boars + Exits: + Gerudo Desert North East Ledge: Clawshot + Gerudo Desert Chu Grotto: Can_Dig + Gerudo Desert Outside Bulblin Camp: Can_Defeat_Bulblin and Can_Ride_Boars + Gerudo Desert: Can_Defeat_Bulblin and Can_Ride_Boars + +- Name: Gerudo Desert North East Ledge + Map Sector: Desert Province + Region: North Gerudo Desert + Can Warp: True + Locations: + Gerudo Desert North Peahat Poe: Night and Can_Defeat_Poe + Exits: + Gerudo Desert Rock Grotto: Can_Dig + Gerudo Desert Basin: Nothing + +- Name: Gerudo Desert Rock Grotto + Locations: + Gerudo Desert Rock Grotto Lantern Chest: Can_Light_Torches + Gerudo Desert Rock Grotto First Poe: Can_Defeat_Poe + Gerudo Desert Rock Grotto Second Poe: Can_Defeat_Poe + Exits: + Gerudo Desert North East Ledge: Nothing + +- Name: Gerudo Desert Chu Grotto + Exits: + Gerudo Desert Basin: Nothing + +- Name: Gerudo Desert Outside Bulblin Camp + Map Sector: Desert Province + Region: North Gerudo Desert + Can Warp: True + Locations: + Gerudo Desert North Small Chest Before Bulblin Camp: Nothing + Outside Bulblin Camp Poe: Night and Can_Defeat_Poe + Gerudo Desert Golden Wolf: "'Howl_at_Lake_Hylia_Howling_Stone'" + Exits: + Bulblin Camp: Nothing + Gerudo Desert Basin: Can_Defeat_Bulblin and 'Can_Access_Gerudo_Desert_Basin' + +# BULBLIN CAMP + +- Name: Bulblin Camp + Map Sector: Desert Province + Region: Bulblin Camp + Can Warp: True + Locations: + Bulblin Camp First Chest Under Tower At Entrance: Nothing + Bulblin Camp Small Chest in Back of Camp: Nothing + Bulblin Camp Roasted Boar: Has_Damaging_Item + Bulblin Camp Poe: Night and Can_Defeat_Poe and (Gerudo_Desert_Bulblin_Camp_Key or Small_Keys == Keysy or Arbiters_Does_Not_Require_Bulblin_Camp == On) + Bulblin Guard Key: Can_Defeat_Bulblin + Bulblin Camp Hint Sign: Nothing + Exits: + Outside Arbiters Grounds: Arbiters_Does_Not_Require_Bulblin_Camp == On or (Can_Defeat_King_Bulblin_Desert and (Gerudo_Desert_Bulblin_Camp_Key or Small_Keys == Keysy)) + Gerudo Desert Outside Bulblin Camp: Nothing + +- Name: Outside Arbiters Grounds + Map Sector: Desert Province + Region: Bulblin Camp + Can Warp: True + Locations: + Outside Arbiters Grounds Lantern Chest: Can_Light_Torches + Outside Arbiters Grounds Poe: Night and Can_Defeat_Poe + Exits: + Arbiters Grounds Entrance: Nothing + Bulblin Camp: Nothing + +# MIRROR CHAMBER + +- Name: Mirror Chamber Lower + Map Sector: Desert Province + Region: None + Can Warp: True + Exits: + Mirror Chamber Upper: Nothing + Arbiters Grounds Boss Room: Mirror_Chamber_Access == Open or (Mirror_Chamber_Access == Barrier and 'Can_Complete_Arbiters_Grounds') + +- Name: Mirror Chamber Upper + Map Sector: Desert Province + Region: Mirror Chamber + Can Warp: True + Locations: + Mirror Chamber Warp Portal: Can_Defeat_Shadow_Beast + Exits: + Twilight Realm Portal: Mirror_Chamber_Portal and + (Palace_of_Twilight_Requirements == Open or + (Palace_of_Twilight_Requirements == Fused_Shadows and count(Progressive_Fused_Shadow, 3)) or + (Palace_of_Twilight_Requirements == Mirror_Shards and count(Progressive_Mirror_Shard, 4)) or + (Palace_of_Twilight_Requirements == Vanilla and 'Can_Complete_City_in_the_Sky')) + Mirror Chamber Lower: Can_Defeat_Shadow_Beast + +- Name: Twilight Realm Portal + Exits: + Palace of Twilight Entrance: Nothing + Mirror Chamber Upper: Can_Defeat_Shadow_Beast + +# CAVE OF ORDEALS + +- Name: Cave of Ordeals + Events: + Can Beat 10 CoO Floors: Can_Defeat_Bokoblin and Can_Defeat_Keese and Can_Defeat_Rat and Can_Defeat_Baba_Serpent and + Can_Defeat_Skulltula and Can_Defeat_Bulblin and Can_Defeat_Torch_Slug and Can_Defeat_Fire_Keese and + Can_Defeat_Dodongo and Can_Defeat_Tektite and Can_Defeat_Lizalfos + Can Beat 20 CoO Floors: Spinner and 'Can_Beat_10_CoO_Floors' and Can_Defeat_Helmasaur and Can_Defeat_Rat and + Can_Defeat_Chu and Can_Defeat_Chu_Worm and Can_Defeat_Bubble and Can_Defeat_Bulblin and + Can_Defeat_Keese and Can_Defeat_Rat and Can_Defeat_Stalhound and Can_Defeat_Poe and Can_Defeat_Leever + Can Beat 30 CoO Floors: Ball_and_Chain and 'Can_Beat_20_CoO_Floors' and Can_Defeat_Bokoblin and Can_Defeat_Ice_Keese and + Can_Defeat_Keese and Can_Defeat_Rat and Can_Defeat_Ghoul_Rat and Can_Defeat_Stalchild and + Can_Defeat_Redead_Knight and Can_Defeat_Bulblin and Can_Defeat_Stalfos and Can_Defeat_Skulltula and + Can_Defeat_Bubble and Can_Defeat_Lizalfos and Can_Defeat_Fire_Bubble + Can Beat 40 CoO Floors: Restored_Dominion_Rod and 'Can_Beat_30_CoO_Floors' and Can_Defeat_Beamos and Can_Defeat_Keese and + Can_Defeat_Torch_Slug and Can_Defeat_Fire_Keese and Can_Defeat_Dodongo and Can_Defeat_Fire_Bubble and + Can_Defeat_Redead_Knight and Can_Defeat_Poe and Can_Defeat_Ghoul_Rat and Can_Defeat_Chu and + Can_Defeat_Ice_Keese and Can_Defeat_Freezard and Can_Defeat_Chilfos and Can_Defeat_Ice_Bubble and + Can_Defeat_Leever and Can_Defeat_Darknut + Can Beat 50 CoO Floors: Double_Clawshots and 'Can_Beat_40_CoO_Floors' and Can_Defeat_Armos and Can_Defeat_Bokoblin and + Can_Defeat_Baba_Serpent and Can_Defeat_Lizalfos and Can_Defeat_Bulblin and Can_Defeat_Dinalfos and + Can_Defeat_Poe and Can_Defeat_Redead_Knight and Can_Defeat_Chu and Can_Defeat_Freezard and + Can_Defeat_Chilfos and Can_Defeat_Ghoul_Rat and Can_Defeat_Rat and Can_Defeat_Stalchild and + Can_Defeat_Aerolfos and Can_Defeat_Darknut + Locations: + # Chest are HD Only + # Cave of Ordeals Floor 10 Chest: "'Can_Beat_10_CoO_Floors'" + # Cave of Ordeals Floor 20 Chest: "'Can_Beat_20_CoO_Floors'" + # Cave of Ordeals Floor 30 Chest: "'Can_Beat_30_CoO_Floors'" + # Cave of Ordeals Floor 40 Chest: "'Can_Beat_40_CoO_Floors'" + # Cave of Ordeals Floor 50 Chest: "'Can_Beat_50_CoO_Floors'" + Cave of Ordeals Hint Sign: Nothing + Cave of Ordeals Great Fairy Reward: "'Can_Beat_50_CoO_Floors'" + Cave of Ordeals Floor 17 Poe: Spinner and 'Can_Beat_10_CoO_Floors' and Can_Defeat_Helmasaur and Can_Defeat_Rat and Can_Defeat_Chu and + Can_Defeat_Chu_Worm and Can_Defeat_Bubble and Can_Defeat_Bulblin and Can_Defeat_Keese and Can_Defeat_Poe + Cave of Ordeals Floor 33 Poe: Restored_Dominion_Rod and 'Can_Beat_30_CoO_Floors' and Can_Defeat_Beamos and Can_Defeat_Keese and + Can_Defeat_Torch_Slug and Can_Defeat_Fire_Keese and Can_Defeat_Dodongo and Can_Defeat_Fire_Bubble and + Can_Defeat_Redead_Knight and Can_Defeat_Poe + Cave of Ordeals Floor 44 Poe: Double_Clawshots and 'Can_Beat_40_CoO_Floors' and Can_Defeat_Armos and Can_Defeat_Bokoblin and + Can_Defeat_Baba_Serpent and Can_Defeat_Lizalfos and Can_Defeat_Bulblin and Can_Defeat_Dinalfos and + Can_Defeat_Poe + Exits: + Gerudo Desert Cave of Ordeals Plateau: Clawshot diff --git a/mods/randomizer/generator/data/world/overworld/Lanayru Province.yaml b/mods/randomizer/generator/data/world/overworld/Lanayru Province.yaml new file mode 100644 index 0000000000..2a26a3e65f --- /dev/null +++ b/mods/randomizer/generator/data/world/overworld/Lanayru Province.yaml @@ -0,0 +1,772 @@ + +# LANAYRU FIELD + +- Name: Lanayru Field + Map Sector: Lanayru Province + Region: Lanayru Field + Twilight: Lanayru + Can Warp: True + Locations: + Lanayru Field Behind Gate Underwater Chest: Iron_Boots + Lanayru Field Tree Boulder Rupee: Can_Smash + Lanayru Field North Underwater Boulder Rupee: Iron_Boots and Water_Bombs + Lanayru Field South Underwater Boulder Rupee: Iron_Boots and Water_Bombs + Lanayru Field Male Stag Beetle: Clawshot or Gale_Boomerang + Lanayru Field Female Stag Beetle: Clawshot or Gale_Boomerang + Lanayru Field Bridge Poe: Night and Can_Use_Senses and Can_Complete_MDH and Can_Complete_All_Twilight + Lanayru Field Hint Sign: Nothing + Exits: + Lanayru Field Near Zoras Domain: Can_Smash + Lanayru Field Cave Entrance: Can_Smash + Lanayru Field Chu Grotto: Can_Dig + Lanayru Field Skulltula Grotto: Can_Dig + Lanayru Field Poe Grotto: Can_Dig + Hyrule Field Near Spinner Rails: Can_Smash + Outside Castle Town West: Nothing + North Eldin Field: Nothing + Upper Zoras River: Impossible # To satisfy entrance rando + +- Name: Lanayru Field Near Zoras Domain + Map Sector: Lanayru Province + Region: Lanayru Field + Twilight: Lanayru + Can Warp: True + Exits: + Zoras Domain West Ledge: Nothing + Lanayru Field: Can_Smash + +- Name: Lanayru Field Cave Entrance + Map Sector: Lanayru Province + Region: Lanayru Field + Twilight: Lanayru + Can Warp: True + Exits: + Lanayru Ice Puzzle Cave: Nothing + Lanayru Field: Can_Smash + +- Name: Lanayru Ice Puzzle Cave + Locations: + Lanayru Ice Block Puzzle Cave Chest: Ball_and_Chain + Exits: + Lanayru Field Cave Entrance: Nothing + +- Name: Lanayru Field Chu Grotto + # Locations: + # Lanayru Field Chu Grotto Chest: Nothing # HD Only + Exits: + Lanayru Field: Nothing + +- Name: Lanayru Field Skulltula Grotto + Locations: + Lanayru Field Skulltula Grotto Chest: Lantern + Exits: + Lanayru Field: Nothing + +- Name: Lanayru Field Poe Grotto + Locations: + Lanayru Field Poe Grotto Left Poe: Can_Use_Senses + Lanayru Field Poe Grotto Right Poe: Can_Use_Senses + Exits: + Lanayru Field: Nothing + +- Name: Hyrule Field Near Spinner Rails + Map Sector: Lanayru Province + Region: Lanayru Field + Twilight: Lanayru + Can Warp: True + Locations: + Lanayru Field Spinner Track Chest: Spinner + Lanayru Field North Spinner Track Boulder Rupee: Can_Smash + Lanayru Field South Spinner Track Boulder Rupee: Can_Smash + Exits: + Lake Hylia Bridge: Can_Smash + Lanayru Field: Can_Smash + +# OUTSIDE CASTLE TOWN WEST + +- Name: Outside Castle Town West + Map Sector: Lanayru Province + Region: Beside Castle Town + Twilight: Lanayru + Can Warp: True + Locations: + Castle Town Warp Portal: Nothing + Hyrule Field Amphitheater Owl Statue Chest: Restored_Dominion_Rod + Hyrule Field Amphitheater Owl Statue Sky Character: Restored_Dominion_Rod + West Hyrule Field Northern Boulder Rupee: Can_Smash + West Hyrule Field Southern Boulder Rupee: Can_Smash + West Hyrule Field Male Butterfly: Nothing + West Hyrule Field Female Butterfly: Gale_Boomerang + Hyrule Field Amphitheater Poe: Can_Use_Senses and Night + West Hyrule Field Golden Wolf: Can_Climb_Vines and 'Howl_at_Upper_Zoras_River_Howling_Stone' + Beside Castle Town Hint Sign: Can_Climb_Vines + Exits: + Outside Castle Town West Grotto Ledge: Clawshot + Castle Town West: Nothing + Lake Hylia Bridge: Nothing + Lanayru Field: Nothing + +- Name: Outside Castle Town West Grotto Ledge + Map Sector: Lanayru Province + Region: Beside Castle Town + Twilight: Lanayru + Can Warp: True + Locations: + West Hyrule Field Female Butterfly: Nothing + Exits: + Outside Castle Town West Helmasaur Grotto: Can_Dig + Outside Castle Town West: Nothing + +- Name: Outside Castle Town West Helmasaur Grotto + Locations: + West Hyrule Field Helmasaur Grotto Chest: Can_Defeat_Helmasaur + Exits: + Outside Castle Town West Grotto Exit: Nothing + +# If you exit the grotto as wolf, you jump over the ledge and fall +# down to Outside Castle Town West +- Name: Outside Castle Town West Grotto Exit + Can Transform: Never + Exits: + Outside Castle Town West Grotto Ledge: Human_Link + Outside Castle Town West: Wolf_Link + +# CASTLE TOWN + +- Name: Castle Town West + Map Sector: Lanayru Province + Region: Castle Town + Twilight: Lanayru + Can Warp: True + Can Transform: If Transform Anywhere + Locations: + Charlo Donation Blessing: "'Can_Farm_Lots_of_Rupees'" + Exits: + Castle Town STAR Game: Nothing + Castle Town Center: Nothing + Castle Town South: Nothing + Outside Castle Town West: Nothing + +- Name: Castle Town STAR Game + Twilight: Lanayru + Can Transform: If Transform Anywhere + Locations: + STAR Prize 1: Clawshot + STAR Prize 2: Double_Clawshots + Exits: + Castle Town West: Nothing + +- Name: Castle Town Center + Map Sector: Lanayru Province + Region: Castle Town + Twilight: Lanayru + Can Warp: True + Can Transform: If Transform Anywhere + Locations: + Castle Town Center Hint Sign: Nothing + Exits: + Castle Town Goron House West Door Exterior: Nothing + Castle Town Goron House East Door Exterior: Nothing + Castle Town Malo Mart: Can_Open_Doors and 'Can_Farm_Rupees' + Castle Town North: Nothing + Castle Town East: Nothing + Castle Town South: Nothing + Castle Town West: Nothing + +- Name: Castle Town Goron House West Door Exterior + Can Transform: Never + Exits: + Castle Town Goron House West Door Interior: Can_Open_Doors + Castle Town Center: Nothing + +- Name: Castle Town Goron House East Door Exterior + Can Transform: Never + Exits: + Castle Town Goron House East Door Interior: Can_Open_Doors + Castle Town Center: Nothing + +- Name: Castle Town Goron House West Door Interior + Can Transform: Never + Exits: + Castle Town Goron House West Door Exterior: Can_Open_Doors + Castle Town Goron House: Nothing + +- Name: Castle Town Goron House East Door Interior + Can Transform: Never + Exits: + Castle Town Goron House East Door Exterior: Can_Open_Doors + Castle Town Goron House: Nothing + +- Name: Castle Town Goron House + Can Transform: If Transform Anywhere + Exits: + Castle Town Goron House West Door Interior: Nothing + Castle Town Goron House East Door Interior: Nothing + Castle Town Goron House Ledge: Nothing + +- Name: Castle Town Goron House Ledge + Twilight: Lanayru + Can Transform: If Transform Anywhere + Exits: + Castle Town Goron House: Nothing + +- Name: Castle Town Malo Mart + Can Transform: If Transform Anywhere + Locations: + # Castle Town Malo Mart Stamp: Can_Talk_to_Humans and 'Can_Farm_Lots_of_Rupees' and 'Can_Fund_Malo_Mart' # HD only + Castle Town Malo Mart Magic Armor: Can_Talk_to_Humans and 'Can_Farm_Lots_of_Rupees' and 'Can_Fund_Malo_Mart' and Big_Wallet + Exits: + Castle Town Center: Can_Open_Doors + +- Name: Castle Town North + Map Sector: Lanayru Province + Region: Castle Town + Twilight: Lanayru + Can Warp: True + Exits: + Castle Town North Behind First Door: Can_Complete_MDH + Castle Town Center: Nothing + +- Name: Castle Town North Behind First Door + Twilight: Lanayru + Locations: + North Castle Town Golden Wolf: "'Howl_at_Hidden_Village_Howling_Stone'" + Exits: + Castle Town North Inside Barrier: Can_Break_Hyrule_Castle_Barrier + Castle Town North: Can_Complete_MDH + +- Name: Castle Town North Inside Barrier + Twilight: Lanayru + Exits: + Hyrule Castle Entrance: Nothing + Castle Town North Behind First Door: Can_Complete_MDH + +- Name: Castle Town East + Map Sector: Lanayru Province + Region: Castle Town + Twilight: Lanayru + Can Warp: True + Exits: + Castle Town Doctors Office West Door Exterior: Nothing + Castle Town Doctors Office East Door Exterior: Nothing + Outside Castle Town East: Nothing + Castle Town South: Nothing + Castle Town Center: Nothing + +- Name: Castle Town Doctors Office West Door Exterior + Can Transform: Never + Exits: + Castle Town Doctors Office West Door Interior: Can_Open_Doors + Castle Town East: Nothing + +- Name: Castle Town Doctors Office East Door Exterior + Can Transform: Never + Exits: + Castle Town Doctors Office East Door Interior: Can_Open_Doors + Castle Town East: Nothing + +- Name: Castle Town Doctors Office West Door Interior + Can Transform: Never + Exits: + Castle Town Doctors Office West Door Exterior: Can_Open_Doors + Castle Town Doctors Office Entrance: Nothing + +- Name: Castle Town Doctors Office East Door Interior + Can Transform: Never + Exits: + Castle Town Doctors Office East Door Exterior: Can_Open_Doors + Castle Town Doctors Office Entrance: Nothing + +- Name: Castle Town Doctors Office Entrance + Can Transform: If Transform Anywhere + Exits: + Castle Town Doctors Office Lower: Ilia_Memory_Quest >= Statue or (Invoice and Can_Talk_to_Humans) + Castle Town Doctors Office West Door Interior: Nothing + Castle Town Doctors Office East Door Interior: Nothing + +- Name: Castle Town Doctors Office Lower + Events: + Medicine Scent: Can_Sniff + Exits: + Castle Town Doctors Office Upper: Wolf_Link + Castle Town Doctors Office Entrance: Invoice and Can_Talk_to_Humans + +- Name: Castle Town Doctors Office Upper + Exits: + Castle Town Doctors Office Lower: Nothing + Castle Town Doctors Office Balcony: Nothing + +- Name: Castle Town Doctors Office Balcony + Twilight: Lanayru + Can Transform: If Transform Anywhere + Locations: + Doctors Office Balcony Chest: Nothing + Exits: + Castle Town East: Nothing + Castle Town Doctors Office Upper: Nothing + +- Name: Outside Castle Town East + Can Transform: If Transform Anywhere + Map Sector: Lanayru Province + Region: Castle Town + Can Warp: True + Locations: + East Castle Town Bridge Poe: Night and Can_Use_Senses + Exits: + Eldin Field Near Castle Town: Nothing + Castle Town East: Nothing + +- Name: Castle Town South + Map Sector: Lanayru Province + Region: Castle Town + Twilight: Lanayru + Can Warp: True + Events: + Can Buy Hot Spring Water: "'Can_Finish_Goron_Springwater_Rush'" + Can Learn About Wooden Statue: "'Medicine_Scent'" + Locations: + Castle Town Twilit Insect: Can_Defeat_Lanayru_Twilit_Insect + Exits: + Castle Town Agithas House: Can_Open_Doors + Castle Town Seer House: Can_Open_Doors + Castle Town Jovanis House: Can_Dig + Castle Town Telmas Bar: Can_Open_Doors + Outside Castle Town South: Nothing + Castle Town West: Nothing + Castle Town East: Nothing + Castle Town Center: Nothing + +- Name: Castle Town Agithas House + Can Transform: If Transform Anywhere + Locations: + Agitha Female Ant Reward: Female_Ant and Can_Talk_to_Humans + Agitha Female Beetle Reward: Female_Beetle and Can_Talk_to_Humans + Agitha Female Butterfly Reward: Female_Butterfly and Can_Talk_to_Humans + Agitha Female Dayfly Reward: Female_Dayfly and Can_Talk_to_Humans + Agitha Female Dragonfly Reward: Female_Dragonfly and Can_Talk_to_Humans + Agitha Female Grasshopper Reward: Female_Grasshopper and Can_Talk_to_Humans + Agitha Female Ladybug Reward: Female_Ladybug and Can_Talk_to_Humans + Agitha Female Mantis Reward: Female_Mantis and Can_Talk_to_Humans + Agitha Female Phasmid Reward: Female_Phasmid and Can_Talk_to_Humans + Agitha Female Pill Bug Reward: Female_Pill_Bug and Can_Talk_to_Humans + Agitha Female Snail Reward: Female_Snail and Can_Talk_to_Humans + Agitha Female Stag Beetle Reward: Female_Stag_Beetle and Can_Talk_to_Humans + Agitha Male Ant Reward: Male_Ant and Can_Talk_to_Humans + Agitha Male Beetle Reward: Male_Beetle and Can_Talk_to_Humans + Agitha Male Butterfly Reward: Male_Butterfly and Can_Talk_to_Humans + Agitha Male Dayfly Reward: Male_Dayfly and Can_Talk_to_Humans + Agitha Male Dragonfly Reward: Male_Dragonfly and Can_Talk_to_Humans + Agitha Male Grasshopper Reward: Male_Grasshopper and Can_Talk_to_Humans + Agitha Male Ladybug Reward: Male_Ladybug and Can_Talk_to_Humans + Agitha Male Mantis Reward: Male_Mantis and Can_Talk_to_Humans + Agitha Male Phasmid Reward: Male_Phasmid and Can_Talk_to_Humans + Agitha Male Pill Bug Reward: Male_Pill_Bug and Can_Talk_to_Humans + Agitha Male Snail Reward: Male_Snail and Can_Talk_to_Humans + Agitha Male Stag Beetle Reward: Male_Stag_Beetle and Can_Talk_to_Humans + # Agitha 12 Golden Bugs Reward: golden_bugs(12) and Can_Talk_to_Humans # HD Only + Exits: + Castle Town South: Can_Open_Doors + +- Name: Castle Town Seer House + Can Transform: If Transform Anywhere + Exits: + Castle Town South: Can_Open_Doors + +- Name: Castle Town Jovanis House + Twilight: Lanayru + Can Transform: If Transform Anywhere + Events: + Can Farm Lots of Rupees: Can_Talk_to_Animals and 'Can_Talk_to_Jovani_in_Telmas_Bar' + Freed Jovani: count(Poe_Soul, 60) + Locations: + Jovani House Poe: Can_Use_Senses + Jovani 20 Poe Soul Reward: count(Poe_Soul, 20) + Jovani 60 Poe Soul Reward: "'Freed_Jovani'" + # Gengle 60 Poe Soul Reward: Can_Talk_to_Animals and 'Can_Talk_to_Jovani_in_Telmas_Bar' # HD Only + Exits: + Castle Town South: Nothing + +- Name: Castle Town Telmas Bar + Can Transform: If Transform Anywhere + Events: + Can Talk to Jovani in Telmas Bar: Can_Talk_to_Humans and 'Freed_Jovani' + Locations: + Telma Invoice: Renados_Letter + Exits: + Castle Town South: Can_Open_Doors + +# OUTSIDE CASTLE TOWN SOUTH + +- Name: Outside Castle Town South + Map Sector: Lanayru Province + Region: South of Castle Town + Twilight: Lanayru + Can Warp: True + Locations: + Outside South Castle Town Tightrope Chest: Clawshot and Can_Use_Tightrope + Outside South Castle Town Fountain Chest: Spinner and Clawshot + Outside South Castle Town Double Clawshot Chasm Chest: Double_Clawshots + Outside South Castle Town Boulder Rupee: Can_Smash + Outside South Castle Town Male Ladybug: Nothing + Outside South Castle Town Female Ladybug: Nothing + Outside South Castle Town Poe: Night and Can_Use_Senses + Outside South Castle Town Golden Wolf: "'Howl_at_North_Faron_Woods_Howling_Stone'" + Wooden Statue: "'Can_Learn_About_Wooden_Statue'" + Outside South Castle Town Hint Sign: Nothing + Exits: + Outside Castle Town South Tektite Grotto Platform: Can_Climb_Vines + Faron Field Behind Boulder: Can_Use_Hot_Spring_Water + Lake Hylia: Nothing + Castle Town South: Nothing + +- Name: Outside Castle Town South Tektite Grotto Platform + Map Sector: Lanayru Province + Region: South of Castle Town + Twilight: Lanayru + Can Warp: True + Exits: + Outside Castle Town South Tektite Grotto: Can_Dig + Outside Castle Town South: Nothing + +- Name: Outside Castle Town South Tektite Grotto + Locations: + Outside South Castle Town Tektite Grotto Chest: Can_Defeat_Tektite + Exits: + Outside Castle Town South Tektite Grotto Platform: Nothing + +# If you enter Outside Castle Town South from there while the boulder is still there, +# you get stuck and are forced to save-warp or portal-warp +- Name: Outside Castle Town South Inside Boulder + Map Sector: Lanayru Province + Region: South of Castle Town + Twilight: Lanayru + Can Warp: True + Exits: + Outside Castle Town South: Can_Use_Hot_Spring_Water and 'Can_Access_Outside_Castle_Town_South' + +# LAKE HYLIA BRIDGE + +- Name: Lake Hylia Bridge + Map Sector: Lanayru Province + Region: Great Bridge of Hylia + Twilight: Lanayru + Can Warp: True + Locations: + Lake Hylia Bridge Vines Chest: Clawshot + Lake Hylia Bridge Owl Statue Chest: Clawshot and Restored_Dominion_Rod + Lake Hylia Bridge Faron Boulder Rupee: Can_Smash + Lake Hylia Bridge Owl Statue Boulder Rupee: Can_Smash + Lake Hylia Bridge Owl Statue Sky Character: Clawshot and Restored_Dominion_Rod + Lake Hylia Bridge Male Mantis: Clawshot or Gale_Boomerang + Lake Hylia Bridge Female Mantis: Clawshot or Gale_Boomerang + Lake Hylia Bridge Hint Sign: Clawshot + Exits: + Lake Hylia Bridge Grotto Ledge: Can_Launch_Bombs and Clawshot + Hyrule Field Near Spinner Rails: Can_Smash + Flight by Fowl: Can_Open_Doors + Faron Field: Gate_Keys or Small_Keys == Keysy + Outside Castle Town West: Nothing + Lake Hylia: Twilight + +- Name: Lake Hylia Bridge Grotto Ledge + Map Sector: Lanayru Province + Region: Great Bridge of Hylia + Twilight: Lanayru + Can Warp: True + Locations: + Lake Hylia Bridge Cliff Chest: Nothing + Lake Hylia Bridge Cliff Poe: Can_Use_Senses and Can_Complete_MDH and Can_Complete_All_Twilight + Exits: + Lake Hylia Bridge Bubble Grotto: Can_Dig + Lake Hylia Bridge: Nothing + +- Name: Lake Hylia Bridge Bubble Grotto + Locations: + Lake Hylia Bridge Bubble Grotto Chest: Can_Defeat_Bubble and Can_Defeat_Fire_Bubble and Can_Defeat_Ice_Bubble + Exits: + Lake Hylia Bridge Grotto Ledge: Nothing + +# LAKE HYLIA + +- Name: Lake Hylia + Map Sector: Lanayru Province + Region: Lake Hylia + Twilight: Lanayru + Can Warp: True + Events: + Can Farm Lots of Rupees: Nothing + Locations: + Lake Hylia Warp Portal: Nothing + Lake Hylia Underwater Chest: Iron_Boots + Lake Hylia Left Underwater Boulder Rupee: Zora_Armor and Iron_Boots and Water_Bombs and Can_Complete_Lanayru_Twilight + Lake Hylia Left Underwater Pillar Rupee: Zora_Armor and Can_Complete_Lanayru_Twilight + Lake Hylia Right Underwater Boulder Rupee: Zora_Armor and Iron_Boots and Water_Bombs and Can_Complete_Lanayru_Twilight + Lake Hylia Right Underwater Pillar Rupee: Zora_Armor and Can_Complete_Lanayru_Twilight + Lake Hylia Alcove Poe: Night and Can_Use_Senses + Lake Hylia Dock Poe: Night and Can_Use_Senses + Plumm Fruit Balloon Minigame: Can_Howl + Lake Hylia Twilit Insect Between Bridges: Can_Defeat_Lanayru_Twilit_Insect + Lake Hylia Burrowing Twilit Insect: Can_Defeat_Lanayru_Twilit_Insect + Lake Hylia Twilit Insect Behind Canon: Can_Defeat_Lanayru_Twilit_Insect + Lake Hylia Twilit Insect on Docks: Can_Defeat_Lanayru_Twilit_Insect + Lake Hylia Twilit Bloat: count(Lanayru_Twilight_Tear, 15) and Can_Defeat_Lanayru_Twilit_Insect + Zoras River Twilit Insect 1: Can_Defeat_Lanayru_Twilit_Insect + Zoras River Twilit Insect 2: Can_Defeat_Lanayru_Twilit_Insect + Zoras River Twilit Insect 3: Can_Defeat_Lanayru_Twilit_Insect + Zoras River Twilit Insect 4: Can_Defeat_Lanayru_Twilit_Insect + Exits: + Lake Hylia Upper Area: Can_Climb_Ladders + Flight by Fowl: Can_Talk_to_Humans + Lake Hylia Lakebed Temple Entrance: Zora_Armor and (Lakebed_Does_Not_Require_Water_Bombs == On or (Iron_Boots and Water_Bombs)) + Lake Hylia Lanayru Spring: Nothing + City in the Sky Entrance: Clawshot and (City_Does_Not_Require_Filled_Skybook == On or count(Progressive_Sky_Book, 7)) + Gerudo Desert: Aurus_Memo + Upper Zoras River: Can_Howl or Twilight + Kakariko Graveyard Pond: Impossible # To satisfy entrance rando + Outside Castle Town South: Impossible # To saitisfy entrance rando + +# Area after the ladder +- Name: Lake Hylia Upper Area + Map Sector: Lanayru Province + Region: Lake Hylia + Twilight: Lanayru + Can Warp: True + Events: + Howl at Lake Hylia Howling Stone: Can_Howl + Locations: + Auru Gift To Fyer: Can_Climb_Ladders + Lake Hylia Tower Poe: Night and Can_Use_Senses + Exits: + Lake Hylia Cave Entrance: Can_Smash + Lake Hylia Water Toadpoli Grotto: Can_Dig + Lake Hylia: Nothing + +- Name: Lake Hylia Cave Entrance + Map Sector: Lanayru Province + Region: Lake Hylia + Can Warp: True + Exits: + Lake Hylia Long Cave: Nothing + Lake Hylia: Can_Smash + +- Name: Lake Hylia Long Cave + Locations: + Lake Lantern Cave First Chest: Can_Smash and Lantern + Lake Lantern Cave Second Chest: Can_Smash and Lantern + Lake Lantern Cave Third Chest: Can_Smash and Lantern + Lake Lantern Cave Fourth Chest: Can_Smash and Lantern + Lake Lantern Cave Fifth Chest: Can_Smash and Lantern + Lake Lantern Cave Sixth Chest: Can_Smash and Lantern + Lake Lantern Cave Seventh Chest: Can_Smash and Lantern + Lake Lantern Cave Eighth Chest: Can_Smash and Lantern + Lake Lantern Cave Ninth Chest: Can_Smash and Lantern + Lake Lantern Cave Tenth Chest: Can_Smash and Lantern + Lake Lantern Cave Eleventh Chest: Can_Smash and Lantern + Lake Lantern Cave Twelfth Chest: Can_Smash and Lantern + Lake Lantern Cave Thirteenth Chest: Can_Smash and Lantern + Lake Lantern Cave Fourteenth Chest: Can_Smash and Lantern + Lake Lantern Cave End Lantern Chest: Can_Smash and Lantern + Lake Lantern Cave First Poe: Can_Smash and Lantern and Can_Use_Senses + Lake Lantern Cave Second Poe: Can_Smash and Lantern and Can_Use_Senses + Lake Lantern Cave Final Poe: Can_Smash and Lantern and Can_Use_Senses + Lake Lantern Cave Hint Sign: Can_Smash and Lantern + Exits: + Lake Hylia Cave Entrance: Nothing + +- Name: Lake Hylia Water Toadpoli Grotto + Locations: + Lake Hylia Water Toadpoli Grotto Chest: Can_Defeat_Water_Toadpoli + Exits: + Lake Hylia Upper Area: Nothing + +- Name: Flight by Fowl + Map Sector: Lanayru Province + Region: Lake Hylia + Twilight: Lanayru + Can Warp: True + Locations: + Outside Lanayru Spring Left Statue Chest: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Outside Lanayru Spring Right Statue Chest: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Flight By Fowl Top Platform Reward: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Flight By Fowl Second Platform Chest: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Flight By Fowl Third Platform Chest: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Flight By Fowl Fourth Platform Chest: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Flight By Fowl Fifth Platform Chest: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Isle of Riches Poe: Night and Can_Use_Senses and Can_Talk_to_Humans and 'Can_Farm_Rupees' + Lake Hylia Hint Sign: Nothing + Exits: + Lake Hylia Shell Blade Grotto Ledge: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Lake Hylia Bridge: Can_Open_Doors + Lake Hylia: Can_Talk_to_Humans + +- Name: Lake Hylia Shell Blade Grotto Ledge + Map Sector: Lanayru Province + Region: Lake Hylia + Twilight: Lanayru + Can Warp: True + Locations: + Flight By Fowl Ledge Poe: Night and Can_Use_Senses + Exits: + Lake Hylia Shell Blade Grotto: Can_Dig + Lake Hylia: Nothing + +- Name: Lake Hylia Shell Blade Grotto + Locations: + Lake Hylia Shell Blade Grotto Chest: Can_Defeat_Shell_Blade + Exits: + Lake Hylia Shell Blade Grotto Ledge: Nothing + +- Name: Lake Hylia Lanayru Spring + Map Sector: Lanayru Province + Region: Lanayru Spring + Twilight: Lanayru + Can Warp: True + Locations: + Lanayru Spring Underwater Left Chest: Iron_Boots or (Can_Do_Niche_Stuff and Magic_Armor) + Lanayru Spring Underwater Right Chest: Iron_Boots or (Can_Do_Niche_Stuff and Magic_Armor) + Lanayru Spring Back Room Left Chest: Clawshot + Lanayru Spring Back Room Right Chest: Clawshot + Lanayru Spring Back Room Lantern Chest: Clawshot and Lantern + Lanayru Spring East Double Clawshot Chest: Double_Clawshots + Lanayru Spring West Double Clawshot Chest: Double_Clawshots + Lanayru Spring Lower Underwater Boulder Rupee: Iron_Boots and Water_Bombs + Lanayru Spring Upper Underwater Boulder Rupee: Iron_Boots and Water_Bombs + Lanayru Spring Hint Sign: Iron_Boots or (Can_Do_Niche_Stuff and Magic_Armor) + Exits: + Lake Hylia: Nothing + +- Name: Lake Hylia Lakebed Temple Entrance + Map Sector: Lanayru Province + Region: Lake Hylia + Twilight: Lanayru + Exits: + Lakebed Temple Entrance: Nothing + Lake Hylia: Zora_Armor and ((Lakebed_Does_Not_Require_Water_Bombs == On) or (Iron_Boots and Water_Bombs)) + + +# UPPER ZORAS RIVER + +- Name: Upper Zoras River + Map Sector: Lanayru Province + Region: Upper Zoras River + Twilight: Lanayru + Can Warp: True + Events: + Howl at Upper Zoras River Howling Stone: Can_Howl + Locations: + Upper Zoras River Warp Portal: Sword or (Can_Defeat_Shadow_Beast and Logic_Transform_Anywhere == On) + Upper Zoras River Ledge Boulder Rupee: Can_Smash + Upper Zoras River East Underwater Boulder Rupee: Iron_Boots and Water_Bombs + Upper Zoras River West Underwater Boulder Rupee: Iron_Boots and Water_Bombs + Upper Zoras River Central Underwater Boulder Rupee: Iron_Boots and Water_Bombs + Upper Zoras River Female Dragonfly: Nothing + Upper Zoras River Poe: Can_Use_Senses + Upper Zoras River Twilit Insect: Can_Defeat_Lanayru_Twilit_Insect + Exits: + Upper Zoras River Izas House: Can_Open_Doors and Upper_Zoras_River_Portal + Fishing Hole: Can_Open_Doors + Zoras Domain: Nothing + Lanayru Field: Nothing + +- Name: Upper Zoras River Izas House + Can Warp: True + Locations: + Iza Helping Hand: Bow and Upper_Zoras_River_Portal + Iza Raging Rapids Minigame: Bow and Upper_Zoras_River_Portal + Exits: + Upper Zoras River: Can_Open_Doors + +- Name: Fishing Hole + Map Sector: Lanayru Province + Region: Upper Zoras River + Can Warp: True + Locations: + Fishing Hole Heart Piece: Clawshot + Fishing Hole Bottle: Fishing_Rod + Fishing Hole Hint Sign: Nothing + Exits: + Fishing Hole House: Can_Open_Doors + Upper Zoras River: Can_Open_Doors + +- Name: Fishing Hole House + Locations: + Fishing Hole Heart Piece: "'Can_Farm_Rupees'" + Exits: + Fishing Hole: Can_Open_Doors + +# ZORAS DOMAIN + +- Name: Zoras Domain + Map Sector: Lanayru Province + Region: Zoras Domain + Twilight: Lanayru + Can Warp: True + Events: + Reekfish Scent: Coral_Earring + Locations: + Zoras Domain Chest By Mother and Child Isles: Nothing + Zoras Domain Chest Behind Waterfall: Can_Midna_Jump + Zoras Domain Central Underwater Boulder Rupee: Can_Complete_Lanayru_Twilight and Iron_Boots and Water_Bombs + Zoras Domain North Underwater Boulder Rupee: Can_Complete_Lanayru_Twilight and Iron_Boots and Water_Bombs + Zoras Domain Male Dragonfly: Nothing + Zoras Domain Mother and Child Isle Poe: Can_Use_Senses + Zoras Domain Waterfall Poe: Can_Midna_Jump and Can_Use_Senses + Zoras Domain Twilit Insect near Lilypads 1: Can_Defeat_Lanayru_Twilit_Insect + Zoras Domain Twilit Insect near Lilypads 2: Can_Defeat_Lanayru_Twilit_Insect + Zoras Domain Burrowing Twilit Insect: Can_Defeat_Lanayru_Twilit_Insect + Exits: + Zoras Domain West Ledge: Clawshot or Can_Midna_Jump or Twilight + Zoras Domain Top of Waterfall: Clawshot or Can_Midna_Jump or Twilight + Snowpeak Climb Lower: Not_Twilight + Upper Zoras River: Nothing + +- Name: Zoras Domain West Ledge + Map Sector: Lanayru Province + Region: Zoras Domain + Twilight: Lanayru + Can Warp: True + Locations: + Zoras Domain Twilit Insect on West Ledge: Can_Defeat_Lanayru_Twilit_Insect + Zoras Domain Hint Sign: Nothing + Exits: + Zoras Domain Top of Waterfall: Can_Smash + Zoras Domain: Nothing + Lanayru Field Near Zoras Domain: Nothing + +- Name: Zoras Domain Top of Waterfall + Map Sector: Lanayru Province + Region: Zoras Domain + Twilight: Lanayru + Can Warp: True + Locations: + Zoras Domain Behind Waterfall Rupee: Can_Complete_Lanayru_Twilight + Zoras Domain Top Ledge Rupee: Can_Complete_Lanayru_Twilight + Zoras Domain Vine Ledge Rupee: Can_Complete_Lanayru_Twilight + Zoras Domain Waterfall Ledge Rupee: Can_Complete_Lanayru_Twilight + Zoras Domain Shortcut Ledge Rupee: Can_Complete_Lanayru_Twilight + Zoras Domain Shortcut Lower Boulder Rupee: Can_Smash + Zoras Domain Shortcut Upper Boulder Rupee: Can_Smash + Exits: + Zoras Throne Room: Nothing + Zoras Domain West Ledge: Can_Smash + Zoras Domain: Nothing + +- Name: Zoras Throne Room + Map Sector: Lanayru Province + Region: Zoras Domain + Twilight: Lanayru + Can Warp: True + Locations: + Zoras Domain Warp Portal: Nothing + Zoras Domain Light All Torches Chest: Can_Light_Torches and Iron_Boots + Zoras Domain Extinguish All Torches Chest: Can_Extinguish_Torches and Iron_Boots + Zoras Domain Throne East Gate Underwater Rupee: Can_Complete_Lanayru_Twilight and Iron_Boots + Zoras Domain Throne East Underwater Rupee: Can_Complete_Lanayru_Twilight and (Iron_Boots or Zora_Armor) + Zoras Domain Throne Northwest Underwater Rupee: Can_Complete_Lanayru_Twilight and (Iron_Boots or Zora_Armor) + Zoras Domain Throne South Underwater Rupee: Can_Complete_Lanayru_Twilight and (Iron_Boots or Zora_Armor) + Zoras Domain Throne West Gate Underwater Rupee: Can_Complete_Lanayru_Twilight and Iron_Boots + Zoras Domain Throne West Underwater Rupee: Can_Complete_Lanayru_Twilight and (Iron_Boots or Zora_Armor) + Zoras Domain Underwater Goron: Water_Bombs and Iron_Boots and Zora_Armor + Zoras Domain Throne Room Twilit Insect: Can_Defeat_Lanayru_Twilit_Insect + Exits: + Zoras Domain Top of Waterfall: Nothing diff --git a/mods/randomizer/generator/data/world/overworld/Ordona Province.yaml b/mods/randomizer/generator/data/world/overworld/Ordona Province.yaml new file mode 100644 index 0000000000..ff80eda567 --- /dev/null +++ b/mods/randomizer/generator/data/world/overworld/Ordona Province.yaml @@ -0,0 +1,173 @@ + +# OUTSIDE LINK'S HOUSE + +- Name: Outside Links House + Map Sector: Ordona Province + Region: Ordon + Can Warp: True + Can Change Time: True + Locations: + Ordon Hint Sign: Nothing + Exits: + Ordon Village: Nothing + Ordon Spring: Nothing + Ordon Links House: Can_Climb_Ladders and Can_Open_Doors + +- Name: Ordon Links House + Exits: + Outside Links House: Can_Open_Doors + Locations: + Wooden Sword Chest: Nothing + Links Basement Chest: Lantern + +# ORDON SPRING + +- Name: Ordon Spring + Map Sector: Ordona Province + Region: Ordon + Can Warp: True + Can Change Time: True + Locations: + Ordon Spring Warp Portal: Nothing + Ordon Spring Golden Wolf: "'Howl_at_Death_Mountain_Howling_Stone'" + Exits: + Outside Links House: Nothing + Ordon Bridge: Skip_Prologue == On or ('Can_Access_Outside_Links_House' and Sword and Slingshot) + +- Name: Ordon Bridge + Map Sector: Ordona Province + Region: Ordon + Can Warp: True + Exits: + South Faron Woods: Can_Complete_Prologue + Ordon Spring: Skip_Prologue == On or ('Can_Access_Outside_Links_House' and Sword and Slingshot) + +# ORDON VILLAGE + +- Name: Ordon Village + Map Selector: Ordona Province + Region: Ordon + Can Warp: True + Can Change Time: True + Events: + Can Farm Rupees: Nothing # Can break pumpkins + Can Refill Slingshot Seeds: Nothing # Can break pumpkins + Fish for Ordon Cat: Day and Fishing_Rod + Locations: + Ordon Rupee Under Tall Tree 1: Nothing + Ordon Rupee Under Tall Tree 2: Nothing + Ordon Tree Long Branch Rupee: Can_Climb_Vines + Ordon Tree Short Branch Rupee: Can_Climb_Vines + Ordon Bo Cliff Rupee: Can_Climb_Ladders and Can_Summon_Hawk + Ordon Bo Roof Rupee: Can_Climb_Ladders + Ordon Bo Window Rupee 1: Can_Climb_Ladders + Ordon Bo Window Rupee 2: Can_Climb_Ladders + Ordon Rupee In Grass By Bo: Nothing + Ordon Rusl House Roof Rupee 1: Human_Link + Ordon Rusl House Roof Rupee 2: Human_Link + Ordon Hidden Rusl House Rupee: Gale_Boomerang or Clawshot + Ordon Rupee In River 1: Nothing + Ordon Rupee In River 2: Nothing + Ordon Rupee Under Bridge: Nothing + Ordon Shield House Ledge Grass Rupee: Night and (Gale_Boomerang or Clawshot) + Uli Cradle Delivery: Day and Human_Link + Exits: + Ordon Seras Shop: Day and Can_Open_Doors + Ordon Sword House: Day and Can_Open_Doors + Ordon Shield House: Day and Can_Open_Doors + Ordon Shield House Upper Ledge: Impossible # To satisfy Entrance Rando + Ordon Fados House: Day and Can_Open_Doors + Ordon Bos House Left Door Exterior: Nothing + Ordon Bos House Right Door Exterior: Nothing + Ordon Ranch Village Pathway: Nothing + Outside Links House: Nothing + +- Name: Ordon Seras Shop + Can Transform: If Transform Anywhere + Events: + Can Refill Lantern Oil: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Locations: + Ordon Cat Rescue: "'Fish_for_Ordon_Cat'" + Sera Shop Slingshot: Can_Talk_to_Humans and 'Can_Farm_Rupees' + Exits: + Ordon Village: Nothing + +- Name: Ordon Sword House + Locations: + Ordon Sword: Can_Complete_Prologue or Faron_Twilight_Cleared == On + Exits: + Ordon Village: Can_Open_Doors + +- Name: Ordon Shield House + Exits: + Ordon Shield House Upper Ledge: Wolf_Link + Ordon Village: Can_Open_Doors + +- Name: Ordon Shield House Upper Ledge + Locations: + Ordon Shield: (Can_Complete_Prologue or Faron_Twilight_Cleared == On) and Can_Survive_Two_Bonks + Exits: + Ordon Village: Wolf_Link + +- Name: Ordon Fados House + Exits: + Ordon Village: Can_Open_Doors + +- Name: Ordon Bos House Left Door Exterior + Can Transform: Never + Exits: + Ordon Bos House Left Door Interior: Can_Open_Doors + Ordon Village: Nothing + +- Name: Ordon Bos House Right Door Exterior + Can Transform: Never + Exits: + Ordon Bos House Right Door Interior: Can_Open_Doors + Ordon Village: Nothing + +- Name: Ordon Bos House Left Door Interior + Can Transform: Never + Exits: + Ordon Bos House Left Door Exterior: Can_Open_Doors + Ordon Bos House: Nothing + +- Name: Ordon Bos House Right Door Interior + Can Transform: Never + Exits: + Ordon Bos House Right Door Exterior: Can_Open_Doors + Ordon Bos House: Nothing + +- Name: Ordon Bos House + Can Transform: Never + Locations: + Wrestling With Bo: Human_Link + Exits: + Ordon Bos House Right Door Interior: Nothing + Ordon Bos House Right Door Interior: Nothing + +# ORDON RANCH + +- Name: Ordon Ranch Village Pathway + Map Sector: Ordona Province + Region: Ordon + Can Warp: True + Can Change Time: True + Exits: + Ordon Village: Nothing + Ordon Ranch: Day + +- Name: Ordon Ranch + Map Sector: Ordona Province + Region: Ordon + Can Warp: True + Locations: + Herding Goats Reward: Can_Complete_Prologue and Day + Exits: + Ordon Ranch Grotto: Wolf_Link + Ordon Ranch Village Pathway: Day + +- Name: Ordon Ranch Grotto + Locations: + Ordon Ranch Grotto Lantern Chest: Can_Light_Torches + Exits: + Ordon Ranch: Nothing diff --git a/mods/randomizer/generator/data/world/overworld/Snowpeak Province.yaml b/mods/randomizer/generator/data/world/overworld/Snowpeak Province.yaml new file mode 100644 index 0000000000..e0bcc777b8 --- /dev/null +++ b/mods/randomizer/generator/data/world/overworld/Snowpeak Province.yaml @@ -0,0 +1,116 @@ +- Name: Snowpeak Climb Lower + Map Sector: Snowpeak Province + Region: Snowpeak Mountain + Can Warp: True + Locations: + Ashei Sketch: Can_Talk_to_Humans + Snowpeak Hint Sign: Nothing + Exits: + Snowpeak Climb Upper: Snowpeak_Does_Not_Require_Reekfish_Scent == On or 'Reekfish_Scent' + Zoras Domain: Nothing + +- Name: Snowpeak Climb Upper + Map Sector: Snowpeak Province + Region: Snowpeak Mountain + Can Warp: True + Events: + Howl at Snowpeak Mountain Howling Stone: Can_Howl + Locations: + Snowpeak Above Freezard Grotto Poe: Can_Use_Senses + Snowpeak Blizzard Poe: Can_Use_Senses + Snowpeak Poe Among Trees: Can_Use_Senses and Night + Exits: + Snowpeak Ice Keese Grotto: Can_Dig + Snowpeak Freezard Grotto: Can_Dig + Snowpeak Summit Cave: Can_Dig + Snowpeak Climb Lower: Nothing + +- Name: Snowpeak Ice Keese Grotto + Exits: + Snowpeak Climb Upper: Nothing + +- Name: Snowpeak Freezard Grotto + Locations: + Snowpeak Freezard Grotto Chest: Can_Defeat_Freezard + Exits: + Snowpeak Climb Upper: Nothing + +- Name: Snowpeak Summit Cave + Map Sector: Snowpeak Province + Region: Snowpeak Mountain + Can Warp: True + Locations: + Snowpeak Cave Ice Lantern Chest: Can_Light_Torches and Ball_and_Chain + Snowpeak Cave Ice Poe: Ball_and_Chain and Can_Use_Senses + Exits: + Snowpeak Summit Upper: Can_Climb_Vines + Snowpeak Climb Upper: Can_Dig + +- Name: Snowpeak Summit Upper + Map Sector: Snowpeak Province + Region: Snowpeak Mountain + Can Warp: True + Events: + Snowboarding Rupees: "(Snowpeak_Portal or Can_Defeat_Shadow_Beast) + and ((Bonks_Do_Damage == Off or (Bonks_Do_Damage == On + and (Logic_Damage_Multiplier != OHKO or Can_Use_Bottled_Fairy))) or 'Can_Complete_Snowpeak_Ruins')" + Locations: + Snowpeak Warp Portal: Can_Defeat_Shadow_Beast + Snowboarding Bridge Ledge Bottom Rupee: "'Snowboarding_Rupees'" + Snowboarding Bridge Ledge Middle Rupee: "'Snowboarding_Rupees'" + Snowboarding Bridge Ledge Upper Rupee: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 1: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 2: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 3: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 4: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 5: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 6: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 7: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 8: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 9: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 10: "'Snowboarding_Rupees'" + Snowboarding Shortcut Rupee 11: "'Snowboarding_Rupees'" + Snowboarding Snowy Tree Top Rupee 1: "'Snowboarding_Rupees'" + Snowboarding Snowy Tree Top Rupee 2: "'Snowboarding_Rupees'" + Snowboarding Snowy Tree Top Rupee 3: "'Snowboarding_Rupees'" + Snowboarding Top Left Rupee: "'Snowboarding_Rupees'" + Snowboarding Top Right Rupee: "'Snowboarding_Rupees'" + Snowboard Racing Prize: Can_Defeat_Shadow_Beast and 'Can_Complete_Snowpeak_Ruins' + Exits: + Snowpeak Summit Lower: Can_Defeat_Shadow_Beast and Can_Survive_One_Bonk + +- Name: Snowpeak Summit Lower + Map Sector: Snowpeak Province + Region: Snowpeak Mountain + Can Warp: True + Locations: + Snowpeak Icy Summit Poe: Can_Use_Senses + Exits: + Snowpeak Ruins East Door Exterior: Nothing + Snowpeak Ruins West Door Exterior: Nothing + +- Name: Snowpeak Ruins East Door Exterior + Can Transform: Never + Exits: + Snowpeak Ruins East Door Interior: Can_Open_Doors + Snowpeak Summit Lower: Nothing + +- Name: Snowpeak Ruins West Door Exterior + Can Transform: Never + Exits: + Snowpeak Ruins West Door Interior: Can_Open_Doors + Snowpeak Summit Lower: Nothing + +- Name: Snowpeak Ruins East Door Interior + Region: Snowpeak Ruins + Can Transform: Never + Exits: + Snowpeak Ruins East Door Exterior: Can_Open_Doors + Snowpeak Ruins Entrance: Nothing + +- Name: Snowpeak Ruins West Door Interior + Region: Snowpeak Ruins + Can Transform: Never + Exits: + Snowpeak Ruins West Door Exterior: Can_Open_Doors + Snowpeak Ruins Entrance: Nothing diff --git a/mods/randomizer/generator/logic/area.cpp b/mods/randomizer/generator/logic/area.cpp new file mode 100644 index 0000000000..96f173f7a1 --- /dev/null +++ b/mods/randomizer/generator/logic/area.cpp @@ -0,0 +1,267 @@ +#include "area.hpp" + +#include "search.hpp" +#include "world.hpp" +#include "../randomizer.hpp" + +#include +#include + +namespace randomizer::logic::area +{ + + LocationAccess::LocationAccess(location::Location* loc, + const requirement::Requirement& req, + Area* area): + _loc(loc), _req(std::move(req)), _area(area) + { + this->_id = area->GetWorld()->GetRandomizer()->GetNewLocAccID(); + } + + location::Location* LocationAccess::GetLocation() const + { + return this->_loc; + } + const requirement::Requirement& LocationAccess::GetRequirement() + { + return this->_req; + } + Area* LocationAccess::GetArea() const + { + return this->_area; + } + int LocationAccess::GetID() const + { + return this->_id; + } + + EventAccess::EventAccess(const requirement::Requirement& req, Area* area, const int& eventIndex): + _req(std::move(req)), _area(area), _eventIndex(eventIndex) + { + } + + const requirement::Requirement& EventAccess::GetRequirement() + { + return this->_req; + } + Area* EventAccess::GetArea() const + { + return this->_area; + } + int EventAccess::GetEventIndex() const + { + return this->_eventIndex; + } + + std::string EventAccess::GetName() const + { + return this->_area->GetWorld()->GetEventName(this->_eventIndex); + } + + Area::Area(const std::string& name, world::World* world): _name(name), _world(world) + { + this->_id = world->GetRandomizer()->GetNewAreaID(); + } + + std::string Area::GetName() const + { + return this->_name; + } + void Area::SetHardAssignedRegion(const std::string& _hardAssignedRegion) + { + this->_hardAssignedRegion = _hardAssignedRegion; + } + std::string Area::GetHardAssignRegion() const + { + return this->_hardAssignedRegion; + } + void Area::SetEvents(std::list>& events) + { + this->_events = std::move(events); + } + + std::list Area::GetEvents() const + { + std::list events; + for (const auto& event : this->_events) + { + events.emplace_back(event.get()); + } + return events; + } + + void Area::SetLocations(std::list>& locations) + { + this->_locations = std::move(locations); + } + + std::list Area::GetLocations() const + { + std::list locations; + for (const auto& loc : this->_locations) + { + locations.emplace_back(loc.get()); + } + return locations; + } + + void Area::SetExits(std::list>& exits) + { + this->_exits = std::move(exits); + } + + std::list Area::GetExits() const + { + std::list exits; + for (const auto& exit : this->_exits) + { + exits.emplace_back(exit.get()); + } + return exits; + } + + void Area::AddExit(std::unique_ptr& exit) + { + this->_exits.push_back(std::move(exit)); + } + + void Area::RemoveExit(entrance::Entrance* exit) + { + std::erase_if(this->_exits, [&](const auto& e) { return e.get() == exit; }); + } + + void Area::AddEntrance(entrance::Entrance* entrance) + { + this->_entrances.emplace_back(entrance); + } + + void Area::RemoveEntrance(entrance::Entrance* entrance) + { + std::erase(this->_entrances, entrance); + } + + std::list Area::GetEntrances() const + { + return this->_entrances; + } + world::World* Area::GetWorld() const + { + return this->_world; + } + void Area::SetCanChangeTime(const bool& canChangeTime) + { + this->_canChangeTime = canChangeTime; + } + bool Area::CanChangeTime() const + { + return this->_canChangeTime; + } + void Area::SetCanTransform(const bool& canTransform) + { + this->_canTransform = canTransform; + } + bool Area::CanTransform() const + { + return this->_canTransform; + } + void Area::AddHintRegion(const std::string& region) + { + this->_hintRegions.emplace(region); + } + std::set Area::GetHintRegions() + { + return this->_hintRegions; + } + void Area::SetTwilightCompletedMacroIndex(const int& macroIndex) + { + this->_twilightCompletedMacroIndex = macroIndex; + } + int Area::GetTwilightCompletedMacroIndex() const + { + return this->_twilightCompletedMacroIndex; + } + + bool Area::TwilightCleared(search::Search* search) const + { + return this->_twilightCompletedMacroIndex == -1 || requirement::EvaluateRequirementAtFormTime( + this->GetWorld()->GetMacro(this->_twilightCompletedMacroIndex), + search, + requirement::FormTime::ALL, + this->GetWorld()); + } + + void Area::AssignHintRegionsAndDungeonLocations() + { + std::set hintRegions = {}; + std::unordered_set alreadyChecked = {}; + std::list areaQueue = {this}; + + while (!areaQueue.empty()) + { + auto area = areaQueue.back(); + areaQueue.pop_back(); + alreadyChecked.insert(area); + + // If this area has a hard assigned region, then we won't assign it any other regions + auto hardAssignedRegion = area->GetHardAssignRegion(); + if (hardAssignedRegion != "") + { + // If the region is None, then don't assign it. None is meant to be a blocker that prevents other regions + // from assigning themselves through this area + if (hardAssignedRegion != "None") + { + hintRegions.insert(hardAssignedRegion); + } + continue; + } + + // If this area isn't assigned any hint regions, add its entrancs' parent areas to the queue as long as they + // haven't been checked yet + for (const auto& entrance : area->GetEntrances()) + { + if (!alreadyChecked.contains(entrance->GetParentArea())) + { + areaQueue.push_back(entrance->GetParentArea()); + } + } + } + + // When determining which regions to assign the area to, overworld regions will take complete priority over dungeon + // regions. Dungeon regions should only be assigned if dungeons are the only regions listed. So if we have any overworld + // hint regions, filter out the dungeon ones. + const auto& dungeons = this->GetWorld()->GetDungeonTable(); + std::set dungeonRegions = {}; + std::ranges::copy_if(hintRegions, + std::inserter(dungeonRegions, dungeonRegions.begin()), + [&](const auto& hintRegion) { return dungeons.contains(hintRegion); }); + // If we have less dungeons than total hint regions, we have at least one overworld hint region + // So erase all the dungeons in that case. + if (dungeonRegions.size() < hintRegions.size()) + { + for (const auto& dungeon : dungeonRegions) + { + hintRegions.erase(dungeon); + } + } + + // Assign the found hint regions to the area + for (const auto& region : hintRegions) + { + this->AddHintRegion(region); + LOG_TO_DEBUG("Assigned \"" + region + "\" as hint region to \"" + this->GetName() + "\""); + + // Also assign any locations in this area to the dungeon if there are any dungeon regions + if (dungeons.contains(region)) + { + auto locAccs = this->GetLocations(); + auto dungeon = this->GetWorld()->GetDungeon(region); + for (const auto& locAcc : locAccs) + { + auto location = locAcc->GetLocation(); + dungeon->AddLocation(location); + } + } + } + } + +} // namespace randomizer::logic::area diff --git a/mods/randomizer/generator/logic/area.hpp b/mods/randomizer/generator/logic/area.hpp new file mode 100644 index 0000000000..628c7bc825 --- /dev/null +++ b/mods/randomizer/generator/logic/area.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include "entrance.hpp" +#include "requirement.hpp" + +#include +#include +#include + +// Forward Declarations +namespace randomizer::logic::location +{ + class Location; +} + +namespace randomizer::logic::search +{ + class Search; +} + +namespace randomizer::logic::world +{ + class World; +} + +namespace randomizer::logic::area +{ + class Area; + class LocationAccess + { + public: + LocationAccess(location::Location* loc, const requirement::Requirement& req, Area* area); + + location::Location* GetLocation() const; + const requirement::Requirement& GetRequirement(); + Area* GetArea() const; + int GetID() const; + + private: + int _id = -1; + location::Location* _loc = nullptr; + requirement::Requirement _req; + Area* _area = nullptr; + }; + + class EventAccess + { + public: + EventAccess(const requirement::Requirement& req, Area* area, const int& eventIndex); + + const requirement::Requirement& GetRequirement(); + Area* GetArea() const; + int GetEventIndex() const; + std::string GetName() const; + + private: + requirement::Requirement _req; + Area* _area = nullptr; + int _eventIndex = -1; + }; + + class Area + { + public: + Area(const std::string& name, world::World* world); + + std::string GetName() const; + void SetHardAssignedRegion(const std::string& _hardAssignedRegion); + std::string GetHardAssignRegion() const; + void SetEvents(std::list>& events); + std::list GetEvents() const; + void SetLocations(std::list>& locations); + std::list GetLocations() const; + void SetExits(std::list>& exits); + std::list GetExits() const; + void AddExit(std::unique_ptr& exit); + void RemoveExit(entrance::Entrance* exit); + void AddEntrance(entrance::Entrance* entrance); + void RemoveEntrance(entrance::Entrance* entrance); + std::list GetEntrances() const; + world::World* GetWorld() const; + void SetCanChangeTime(const bool& canChangeTime); + bool CanChangeTime() const; + void SetCanTransform(const bool& canTransform); + bool CanTransform() const; + void AddHintRegion(const std::string& region); + std::set GetHintRegions(); + void SetTwilightCompletedMacroIndex(const int& macroIndex); + int GetTwilightCompletedMacroIndex() const; + bool TwilightCleared(search::Search* search) const; + + /** + * @brief Assigns this area's hint regions(s) as well as assigns any locations within the area to a dungeon if the + * area's hint region is a dungeon + */ + void AssignHintRegionsAndDungeonLocations(); + + private: + int _id = -1; + std::string _name = ""; + std::string _hardAssignedRegion = ""; + std::set _hintRegions = {}; + std::list> _events = {}; + std::list> _locations = {}; + std::list> _exits = {}; + std::list _entrances = {}; + world::World* _world; + bool _canChangeTime = false; + bool _canTransform = false; + int _twilightCompletedMacroIndex = -1; + }; + +} // namespace randomizer::logic::area diff --git a/mods/randomizer/generator/logic/dungeon.cpp b/mods/randomizer/generator/logic/dungeon.cpp new file mode 100644 index 0000000000..2a0bf870da --- /dev/null +++ b/mods/randomizer/generator/logic/dungeon.cpp @@ -0,0 +1,137 @@ +#include "dungeon.hpp" + +#include "area.hpp" +#include "entrance.hpp" +#include "item.hpp" +#include "world.hpp" + +#include "../utility/container.hpp" +#include "../utility/log.hpp" + +namespace randomizer::logic::dungeon +{ + Dungeon::Dungeon(const std::string& name, world::World* world): _name(name), _world(world) {} + + std::string Dungeon::GetName() const + { + return this->_name; + } + + void Dungeon::SetSmallKey(item::Item* item) + { + this->_smallKey = item; + LOG_TO_DEBUG("Set \"" + item->GetName() + "\" as small key for dungeon " + this->_name); + } + + item::Item* Dungeon::GetSmallKey() const + { + return this->_smallKey; + } + + void Dungeon::SetBigKey(item::Item* item) + { + this->_bigKey = item; + LOG_TO_DEBUG("Set \"" + item->GetName() + "\" as big key for dungeon " + this->_name); + } + + item::Item* Dungeon::GetBigKey() const + { + return this->_bigKey; + } + + void Dungeon::SetCompass(item::Item* item) + { + this->_compass = item; + LOG_TO_DEBUG("Set \"" + item->GetName() + "\" as compass for dungeon " + this->_name); + } + + item::Item* Dungeon::GetCompass() const + { + return this->_compass; + } + + void Dungeon::SetDungeonMap(item::Item* item) + { + this->_dungeonMap = item; + LOG_TO_DEBUG("Set \"" + item->GetName() + "\" as dungeon map for dungeon " + this->_name); + } + + item::Item* Dungeon::GetDungeonMap() const + { + return this->_dungeonMap; + } + + void Dungeon::SetStartingArea(area::Area* startingArea) + { + this->_startingArea = startingArea; + LOG_TO_DEBUG("Set \"" + startingArea->GetName() + "\" as starting area for dungeon " + this->_name) + } + + area::Area* Dungeon::GetStartingAreas() + { + return this->_startingArea; + } + + void Dungeon::AddStartingEntrance(entrance::Entrance* startingEntrance) + { + this->_startingEntrances.insert(startingEntrance); + LOG_TO_DEBUG("Added \"" + startingEntrance->GetOriginalName() + "\" as starting entrance for dungeon " + this->_name) + } + + std::unordered_set Dungeon::GetStartingEntrances() const + { + return this->_startingEntrances; + }; + + void Dungeon::AddLocation(location::Location* location) + { + if (!utility::container::ElementInContainer(this->_locations, location)) + { + this->_locations.push_back(location); + LOG_TO_DEBUG(location->GetName() + " has been assigned to dungeon " + this->_name); + } + } + + location::LocationPool Dungeon::GetLocations() + { + return this->_locations; + } + + void Dungeon::SetGoalLocation(location::Location* goalLocation) + { + this->_goalLocation = goalLocation; + LOG_TO_DEBUG(goalLocation->GetName() + " has been assigned as goal location to dungeon " + this->_name); + } + + location::Location* Dungeon::GetGoalLocation() + { + return this->_goalLocation; + } + + void Dungeon::SetRequired(const bool& required) + { + this->_required = required; + LOG_TO_DEBUG(this->_name + " has been set as required."); + } + + bool Dungeon::IsRequired() const + { + return this->_required; + } + + void Dungeon::AddOutsideDependentLocation(location::Location* location) { + this->_outsideDependentLocations.push_back(location); + } + + std::list Dungeon::GetOutsideDependentLocations() { + return this->_outsideDependentLocations; + } + + bool Dungeon::ShouldBeBarren() const + { + return !this->_required && + this->_world->Setting("Unrequired Dungeons Are Barren") == "On" && + this->_name != "Hyrule Castle"; + } + +} // namespace randomizer::logic::dungeon diff --git a/mods/randomizer/generator/logic/dungeon.hpp b/mods/randomizer/generator/logic/dungeon.hpp new file mode 100644 index 0000000000..d1fdafa824 --- /dev/null +++ b/mods/randomizer/generator/logic/dungeon.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include "location.hpp" + +#include + +// Forward declarations +namespace randomizer::logic::item +{ + class Item; +} + +namespace randomizer::logic::area +{ + class Area; +} + +namespace randomizer::logic::entrance +{ + class Entrance; +} +namespace randomizer::logic::world +{ + class World; +} + +namespace randomizer::logic::dungeon +{ + /** + * @brief Holds dungeon specific data + */ + class Dungeon + { + public: + Dungeon(const std::string& name, world::World* world); + + std::string GetName() const; + void SetSmallKey(item::Item* item); + item::Item* GetSmallKey() const; + void SetBigKey(item::Item* item); + item::Item* GetBigKey() const; + void SetCompass(item::Item* item); + item::Item* GetCompass() const; + void SetDungeonMap(item::Item* item); + item::Item* GetDungeonMap() const; + void SetStartingArea(area::Area* startingArea); + area::Area* GetStartingAreas(); + void AddStartingEntrance(entrance::Entrance* startingEntrance); + std::unordered_set GetStartingEntrances() const; + void AddLocation(location::Location* location); + location::LocationPool GetLocations(); + void SetGoalLocation(location::Location* goalLocation); + location::Location* GetGoalLocation(); + void SetRequired(const bool& required); + bool IsRequired() const; + void AddOutsideDependentLocation(location::Location* location); + std::list GetOutsideDependentLocations(); + + /** + * @brief Returns whether or not the dungeon should be barren given the current settings and placement of dungeon + * rewards and/or plandomized items + */ + bool ShouldBeBarren() const; + + private: + std::string _name = ""; + world::World* _world; + item::Item* _smallKey; + item::Item* _bigKey; + item::Item* _compass; + item::Item* _dungeonMap; + area::Area* _startingArea; + std::unordered_set _startingEntrances; + location::Location* _goalLocation; + location::LocationPool _locations = {}; + // Locations which depend on beating this dungeon + std::list _outsideDependentLocations = {}; + bool _required = false; + }; +} // namespace randomizer::logic::dungeon diff --git a/mods/randomizer/generator/logic/entrance.cpp b/mods/randomizer/generator/logic/entrance.cpp new file mode 100644 index 0000000000..b27d8d2c47 --- /dev/null +++ b/mods/randomizer/generator/logic/entrance.cpp @@ -0,0 +1,359 @@ +#include "entrance.hpp" + +#include "area.hpp" +#include "world.hpp" +#include "../utility/log.hpp" +#include "../utility/string.hpp" + +namespace randomizer::logic::entrance +{ + + Type TypeFromStr(const std::string& str) + { + std::unordered_map types = {{"Spawn", Type::SPAWN}, + {"Warp Portal", Type::WARP_PORTAL}, + {"Dungeon", Type::DUNGEON}, + {"Boss", Type::BOSS}, + {"Grotto", Type::GROTTO}, + {"Mixed Pool 1", Type::MIXED_POOL_1}, + {"Mixed Pool 2", Type::MIXED_POOL_2}, + {"Mixed Pool 3", Type::MIXED_POOL_3}, + {"Mixed Pool 4", Type::MIXED_POOL_4}, + {"Mixed Pool 5", Type::MIXED_POOL_5}, + {"Cave", Type::CAVE}, + {"Interior", Type::INTERIOR}, + {"Overworld", Type::OVERWORLD}}; + + if (!types.contains(str)) + { + return Type::INVALID; + } + + return types.at(str); + } + + std::string TypeToStr(const Type& type) + { + std::unordered_map types = {{Type::SPAWN, "Spawn"}, + {Type::WARP_PORTAL, "Warp Portal"}, + {Type::DUNGEON, "Dungeon"}, + {Type::DUNGEON_REVERSE, "Dungeon Reverse"}, + {Type::BOSS, "Boss"}, + {Type::BOSS_REVERSE, "Boss Reverse"}, + {Type::GROTTO, "Grotto"}, + {Type::GROTTO_REVERSE, "Grotto Reverse"}, + {Type::MIXED_POOL_1, "Mixed Pool 1"}, + {Type::MIXED_POOL_2, "Mixed Pool 2"}, + {Type::MIXED_POOL_3, "Mixed Pool 3"}, + {Type::MIXED_POOL_4, "Mixed Pool 4"}, + {Type::MIXED_POOL_5, "Mixed Pool 5"}, + {Type::CAVE, "Cave"}, + {Type::CAVE_REVERSE, "Cave Reverse"}, + {Type::INTERIOR, "Interior"}, + {Type::INTERIOR_REVERSE, "Interior Reverse"}, + {Type::OVERWORLD, "Overworld"}}; + + if (!types.contains(type)) + { + return "INVALID"; + } + + return types.at(type); + } + + Type TypeToReverse(const Type& type) + { + std::unordered_map reverse = {{Type::DUNGEON, Type::DUNGEON_REVERSE}, + {Type::DUNGEON_REVERSE, Type::DUNGEON}, + {Type::BOSS, Type::BOSS_REVERSE}, + {Type::BOSS_REVERSE, Type::BOSS}, + {Type::GROTTO, Type::GROTTO_REVERSE}, + {Type::GROTTO_REVERSE, Type::GROTTO}, + {Type::CAVE, Type::CAVE_REVERSE}, + {Type::CAVE_REVERSE, Type::CAVE}, + {Type::INTERIOR, Type::INTERIOR_REVERSE}, + {Type::INTERIOR_REVERSE, Type::INTERIOR}, + // Yes, this is intentional for the overworld type + {Type::OVERWORLD, Type::OVERWORLD}}; + + if (!reverse.contains(type)) + { + return Type::INVALID; + } + + return reverse.at(type); + } + + Entrance::Entrance(area::Area* parentArea, + area::Area* connectedArea, + const requirement::Requirement& req, + world::World* world): + _parentArea(parentArea), + _connectedArea(connectedArea), + _originalConnectedArea(connectedArea), + _req(std::move(req)), + _world(world) + { + this->_originalName = this->GetCurrentName(); + this->_computedRequirement._type = requirement::Type::IMPOSSIBLE; + } + + void Entrance::SetID(const int& id) + { + this->_id = id; + } + + int Entrance::GetID() const + { + return this->_id; + } + + std::string Entrance::GetCurrentName() const + { + std::string parentName = this->_parentArea ? this->_parentArea->GetName() : "None"; + std::string connectedName = this->_connectedArea ? this->_connectedArea->GetName() : "None"; + return parentName + " -> " + connectedName; + } + + std::string Entrance::GetOriginalName() const + { + return this->_originalName; + } + + void Entrance::SetAlias(const std::string& alias) + { + this->_alias = alias; + // If there's no alias, just use the original name + if (this->_alias.empty()) + { + this->_alias = this->_originalName; + } + } + + std::string Entrance::GetAlias() const + { + return this->_alias; + } + + std::string Entrance::GetAliasFrom() + { + std::string parentAreaAlias = this->_alias.substr(0, this->_alias.find(" -> ")); + std::string connectedAreaAlias = this->_alias.substr(this->_alias.find(" -> ") + 4); + return connectedAreaAlias + " from " + parentAreaAlias; + } + + void Entrance::GeneralizeName() + { + randomizer::utility::str::Erase(this->_originalName, " North", " South", " East", " West", " Right", " Left"); + randomizer::utility::str::Erase(this->_alias, " North", " South", " East", " West", " Right", " Left"); + } + + area::Area* Entrance::GetParentArea() const + { + return this->_parentArea; + } + + area::Area* Entrance::GetConnectedArea() const + { + return this->_connectedArea; + } + + area::Area* Entrance::GetOriginalConnectedArea() const + { + return this->_originalConnectedArea; + } + + void Entrance::SetType(const Type& type) + { + this->_type = type; + if (this->_originalType == Type::INVALID) + { + this->_originalType = type; + } + } + + Type Entrance::GetType() const + { + return this->_type; + } + + Type Entrance::GetOriginalType() const + { + return this->_originalType; + } + + void Entrance::SetRequirement(const requirement::Requirement& req) + { + this->_req = req; + } + + const requirement::Requirement& Entrance::GetRequirement() + { + return this->_req; + } + + void Entrance::SetComputedRequirement(const requirement::Requirement& computedRequirement) + { + this->_computedRequirement = computedRequirement; + } + + requirement::Requirement Entrance::GetComputedRequirement() + { + return this->_computedRequirement; + } + + world::World* Entrance::GetWorld() const + { + return this->_world; + } + + bool Entrance::CanStartAt() const + { + return this->_canStartAt; + } + + void Entrance::SetShuffled(const bool& shuffled) + { + this->_shuffled = shuffled; + } + + bool Entrance::IsShuffled() const + { + return this->_shuffled; + } + + void Entrance::SetDecoupled(const bool& decoupled) + { + this->_decoupled = decoupled; + } + + bool Entrance::IsDecoupled() const + { + return this->_decoupled; + } + + void Entrance::SetDisbled(const bool& disabled) + { + this->_disabled = disabled; + LOG_TO_DEBUG(this->GetOriginalName() + " disabled status set to " + (disabled ? "True" : "False")); + } + + bool Entrance::IsDisabled() const + { + return this->_disabled; + } + + void Entrance::SetPrimary(const bool& primary) + { + this->_primary = primary; + LOG_TO_DEBUG(this->GetOriginalName() + " primary status set to " + (primary ? "True" : "False")); + } + + bool Entrance::IsPrimary() const + { + return this->_primary; + } + + void Entrance::SetTarget(const bool& target) + { + this->_target = target; + } + + bool Entrance::IsTarget() const + { + return this->_target; + } + + void Entrance::SetReplaces(Entrance* replaces) + { + this->_replaces = replaces; + } + + Entrance* Entrance::GetReplaces() const + { + return this->_replaces; + } + + void Entrance::SetReverse(Entrance* reverse) + { + this->_reverse = reverse; + } + + Entrance* Entrance::GetReverse() const + { + return this->_reverse; + } + + Entrance* Entrance::GetAssumed() const + { + return this->_assumed; + } + + void Entrance::Connect(area::Area* newConnectedArea) + { + this->_connectedArea = newConnectedArea; + newConnectedArea->AddEntrance(this); + } + + area::Area* Entrance::Disconnect() + { + this->_connectedArea->RemoveEntrance(this); + auto previouslyConnected = this->_connectedArea; + this->_connectedArea = nullptr; + return previouslyConnected; + } + + void Entrance::BindTwoWay(Entrance* returnEntrance) + { + this->SetReverse(returnEntrance); + returnEntrance->SetReverse(this); + } + + Entrance* Entrance::GetNewTarget() + { + auto root = this->_world->GetRootArea(); + auto targetEntrance = + std::make_unique(root, nullptr, requirement::NO_REQUIREMENT, this->_world); + auto target = targetEntrance.get(); + root->AddExit(targetEntrance); // This moves the variable, so we have to use the pointer for the rest of the function + target->Connect(this->_connectedArea); + target->SetReplaces(this); + target->SetTarget(true); + return target; + } + + Entrance* Entrance::AssumeReachable() + { + if (this->_assumed == nullptr) + { + this->_assumed = this->GetNewTarget(); + this->Disconnect(); + } + return this->_assumed; + } + + std::tuple GetParentAndConnectedAreaNames(const std::string& originalName) + { + std::string parentAreaName; + std::string connectedAreaName; + if (randomizer::utility::str::Contains(originalName, " -> ")) + { + auto separatorIndex = originalName.find(" -> "); + parentAreaName = originalName.substr(0, separatorIndex); + connectedAreaName = originalName.substr(separatorIndex + 4); + } + else if (randomizer::utility::str::Contains(originalName, " from ")) + { + auto separatorIndex = originalName.find(" from "); + connectedAreaName = originalName.substr(0, separatorIndex); + parentAreaName = originalName.substr(separatorIndex + 6); + } + else + { + throw std::runtime_error("Could not parse area names from entrance string \"" + originalName + + "\". Please make sure your syntax is correct"); + } + + return {parentAreaName, connectedAreaName}; + } +} // namespace randomizer::logic::entrance diff --git a/mods/randomizer/generator/logic/entrance.hpp b/mods/randomizer/generator/logic/entrance.hpp new file mode 100644 index 0000000000..8159d4353c --- /dev/null +++ b/mods/randomizer/generator/logic/entrance.hpp @@ -0,0 +1,211 @@ +#pragma once + +#include "requirement.hpp" + +#include +#include +#include + +// Forward Declarations +namespace randomizer::logic::area +{ + class Area; +} + +namespace randomizer::logic::world +{ + class World; +} + +namespace randomizer::logic::entrance +{ + enum Type + { + INVALID = 0, + // The order of this enum is also the order in which the different types of entrances + // will be shuffled. So this ordering is important. Generally we want to shuffle entrances + // near the "outside" of the world graph first (dungeon entrances/grottos) and then follow that + // up with entrance types that are closer to the "inside" of the world graph which have more + // consequences to being shuffled. The mixed pools are thrown in the middle since this is the most stable + // place for them to be to not interfere with the ordering too much. + SPAWN, + WARP_PORTAL, + GROTTO, + GROTTO_REVERSE, + BOSS, + BOSS_REVERSE, + DUNGEON, + DUNGEON_REVERSE, + MIXED_POOL_1, + MIXED_POOL_2, + MIXED_POOL_3, + MIXED_POOL_4, + MIXED_POOL_5, + CAVE, + CAVE_REVERSE, + INTERIOR, + INTERIOR_REVERSE, + OVERWORLD, + ALL, + }; + + static const std::unordered_set NON_ASSUMED_TYPES = {SPAWN, WARP_PORTAL}; + + /** + * @brief Takes a string representation of a Type and returns the + * associated enum value. + * + * @param str The string representation of a Type. + * @return The associated enum value for the passed in type. + */ + Type TypeFromStr(const std::string& str); + + std::string TypeToStr(const Type& type); + + Type TypeToReverse(const Type& type); + + class Entrance + { + public: + Entrance(area::Area* parentArea, + area::Area* connectedArea, + const requirement::Requirement& req, + world::World* world); + + void SetID(const int& id); + int GetID() const; + std::string GetCurrentName() const; + std::string GetOriginalName() const; + void SetAlias(const std::string& alias); + std::string GetAlias() const; + /* + * @brief Gets the alias in the "connected area from parent area" format + */ + std::string GetAliasFrom(); + /** + * @brief Removes cardinal/direction specifiers from the entrance's name/alias (North, South, East, West, Left, Right) + */ + void GeneralizeName(); + area::Area* GetParentArea() const; + area::Area* GetConnectedArea() const; + area::Area* GetOriginalConnectedArea() const; + void SetType(const Type& type); + Type GetType() const; + Type GetOriginalType() const; + void SetRequirement(const requirement::Requirement& req); + const requirement::Requirement& GetRequirement(); + void SetComputedRequirement(const requirement::Requirement& computedRequirement); + requirement::Requirement GetComputedRequirement(); + world::World* GetWorld() const; + bool CanStartAt() const; + void SetShuffled(const bool& shuffled); + bool IsShuffled() const; + void SetDecoupled(const bool& decoupled); + bool IsDecoupled() const; + void SetDisbled(const bool& disabled); + bool IsDisabled() const; + void SetPrimary(const bool& primary); + bool IsPrimary() const; + void SetTarget(const bool& target); + bool IsTarget() const; + + void SetReplaces(Entrance* replaces); + Entrance* GetReplaces() const; + void SetReverse(Entrance* reverse); + Entrance* GetReverse() const; + Entrance* GetAssumed() const; + + /** + * @brief Connect this entrance to the passed in area, and add this entrance to the list of entrances for the passed in + * area + * + * @param newConnectedArea The area to connect this entrance to + */ + void Connect(area::Area* newConnectedArea); + + /** + * @brief Disconnect this entrance from the area it leads to. Will also remove this entrance from it's connected area's + * entrances. + * + * @return The area this entrance was previously connected to + */ + area::Area* Disconnect(); + + /** + * @brief Links two entrances by setting them as each others' reverse entrance + */ + void BindTwoWay(Entrance* returnEntrance); + + /** + * @brief Creates a new target entrance that corresponds to where this one leads, and + * attaches it to the root of the world graph. + */ + Entrance* GetNewTarget(); + + /** + * @brief Create this entrance's target and disconnect it from the original entrance. + * This assumes reachable access to the entrance for the entrance shuffling algorithm + */ + Entrance* AssumeReachable(); + + private: + int _id = -1; + area::Area* _parentArea = nullptr; + area::Area* _connectedArea = nullptr; + area::Area* _originalConnectedArea = nullptr; + Type _type = Type::INVALID; + Type _originalType = Type::INVALID; + std::string _originalName = ""; + std::string _alias = ""; + world::World* _world = nullptr; + + /** + * @brief The local requirement for this entrance assuming we have access to its parent area. + */ + requirement::Requirement _req; + + /** + * @brief The flattened requirement which includes everything necessary to reach this entrance from the root of the + * world graph. + */ + requirement::Requirement _computedRequirement; + + // Variables used for entrance shuffling + bool _canStartAt = false; + bool _shuffled = false; + bool _decoupled = false; + bool _disabled = false; + + // A target entrance is one created to mimic the effect of going + // through a specific real entrance. The target is attatched to + // the root of the world graph and is connected to it's correpsonding + // entrance's connected area. + bool _target = false; + + // Primary entrances are those that we think of as + // "going into" areas. Entering dungeons, entering grottos, + // and entering doors are all primary entrances. The opposite + // idea, "leaving" areas, are not primary entrances. + bool _primary = false; + + // The reverse is the entrance that sends the player in the + // natural opposite direction of this entrance. The reverse entrance + // of entering a grotto would be the entrance that leaves the grotto + Entrance* _reverse = nullptr; + + // If the entrance is shuffled, _replaces is the target entrance that replaces + // this one. If this *is* a target entrance, then _replaces holds the + // entrance that this target *corresponds* to. + Entrance* _replaces = nullptr; + + // If the entrance is shuffled, _assumed is the target entrance that *corresponds* + // to this one. So if the entrance is North Faron Woods -> Forest Temple Entrance, + // then _assumed is the target entrance Root -> Forest Temple Entrance + Entrance* _assumed = nullptr; + }; + + using EntrancePool = std::vector; + using EntrancePools = std::map; + + std::tuple GetParentAndConnectedAreaNames(const std::string& originalName); +} // namespace randomizer::logic::entrance diff --git a/mods/randomizer/generator/logic/entrance_shuffle.cpp b/mods/randomizer/generator/logic/entrance_shuffle.cpp new file mode 100644 index 0000000000..cc63431318 --- /dev/null +++ b/mods/randomizer/generator/logic/entrance_shuffle.cpp @@ -0,0 +1,800 @@ +#include "entrance_shuffle.hpp" + +#include "item_pool.hpp" +#include "search.hpp" +#include "../randomizer.hpp" +#include "../utility/random.hpp" +#include "../utility/yaml.hpp" + +#include + +using namespace randomizer::logic::entrance; + +namespace randomizer::logic::entrance_shuffle +{ + void ShuffleWorldEntrances(world::World* world) + { + SetAllEntrancesData(world); + + auto entrancePools = CreateEntrancePools(world); + auto targetEntrancePools = CreateTargetPools(entrancePools); + + // Set plando entrances first + try { + SetPlandomizedEntrances(world, entrancePools, targetEntrancePools); + } catch (std::runtime_error& e) { + throw std::runtime_error("Plandomizer Error: " + std::string(e.what())); + } + + // Then shuffle non-assumed types (currently this is just spawn) + ShuffleNonAssumedEntrancesPools(world, entrancePools, targetEntrancePools); + + // Shuffle the rest of the entrance pools + for (auto& [entranceType, entrancePool] : entrancePools) + { + ShuffleEntrancePool(entrancePool, targetEntrancePools[entranceType]); + } + + // Validate the world one last time to ensure everything worked + auto completeItemPool = item_pool::GetCompleteItemPool(world->GetRandomizer()->GetWorlds()); + ValidateWorld(world, nullptr, completeItemPool); + } + + void SetAllEntrancesData(world::World* world) + { + // Keep track of which double door entrances are together + std::unordered_map> coupledDoors = {}; + + auto entranceDataTree = LOAD_EMBED_YAML(RANDO_DATA_PATH "entrance_shuffle_data.yaml"); + for (const auto& entranceDataNode : entranceDataTree) + { + // Check to make sure all required fields are present + YAMLVerifyFields(entranceDataNode, "Type", "Forward"); + + auto typeStr = entranceDataNode["Type"].as(); + auto type = entrance::TypeFromStr(typeStr); + if (type == entrance::Type::INVALID) + { + throw std::runtime_error("Unknown entrance type \"" + typeStr + "\" in entrance shuffle node:\n" + + YAML::Dump(entranceDataNode)); + } + + auto& forwardEntry = entranceDataNode["Forward"]; + // Check to make sure all required fields are present for the forward entry + YAMLVerifyFields(forwardEntry, "Connection" /*, "Info" */); + + auto forwardEntrance = world->GetEntrance(forwardEntry["Connection"].as()); + forwardEntrance->SetType(type); + // TODO: Set actual entrance data + forwardEntrance->SetID(world->GetNewEntranceID()); + forwardEntrance->SetPrimary(true); + forwardEntrance->SetAlias( + forwardEntry["Alias"] ? forwardEntry["Alias"].as() : ""); + + if (entranceDataNode["Return"]) + { + auto& returnEntry = entranceDataNode["Return"]; + YAMLVerifyFields(returnEntry, "Connection" /*, "Info" */); + + auto returnEntrance = world->GetEntrance(returnEntry["Connection"].as()); + returnEntrance->SetType(type); + // TODO: Set actual entrance data + returnEntrance->SetID(world->GetNewEntranceID()); + returnEntrance->SetAlias( + returnEntry["Alias"] ? returnEntry["Alias"].as() : ""); + forwardEntrance->BindTwoWay(returnEntrance); + + // Add double door entrances to their respective tag group + if (entranceDataNode["Door Couple Tag"]) + { + auto tag = entranceDataNode["Door Couple Tag"].as(); + if (!coupledDoors.contains(tag)) + { + coupledDoors[tag] = {}; + } + coupledDoors.at(tag).push_back(forwardEntrance); + coupledDoors.at(tag).push_back(returnEntrance); + } + } + } + + // If double doors are coupled, add the coupled door's info to the main door, remove the coupled door entrance, and + // rename the main door to be more general + if (world->Setting("Decouple Double Door Entrances") == "Off") + { + for (auto& [tag, doors] : coupledDoors) + { + while (!doors.empty()) + { + auto mainDoor = doors.back(); + doors.pop_back(); + + auto coupledDoorItr = + std::ranges::find_if(doors, + [&](const auto& door) { return door->IsPrimary() == mainDoor->IsPrimary(); }); + auto coupledDoor = *coupledDoorItr; + + // TODO: Add the coupled door's info to the main door + + // Completely remove the coupled door from the world graph + doors.erase(coupledDoorItr); + coupledDoor->GetConnectedArea()->RemoveEntrance(coupledDoor); + coupledDoor->GetParentArea()->RemoveExit(coupledDoor); + + // Change the main door's name to be more general + mainDoor->GeneralizeName(); + } + } + } + } + + EntrancePools CreateEntrancePools(world::World* world) + { + EntrancePools entrancePools = {}; + + // Spawn + if (world->Setting("Randomize Starting Spawn") == "On") + { + entrancePools[Type::SPAWN] = world->GetShuffleableEntrances(Type::SPAWN); + } + + // Dungeon Entrances + if (world->Setting("Randomize Dungeon Entrances") >= "On") + { + entrancePools[Type::DUNGEON] = world->GetShuffleableEntrances(Type::DUNGEON, /*onlyPrimary = */ true); + + // Remove Hyrule Castle if it's not being shuffled + if (world->Setting("Randomize Dungeon Entrances") != "On + Hyrule Castle") + { + std::erase_if(entrancePools[Type::DUNGEON], [](const auto& entrance) { + return entrance->GetOriginalName() == "Castle Town North Inside Barrier -> Hyrule Castle Entrance"; + }); + } + + if (world->Setting("Decouple Entrances") == "On") + { + entrancePools[Type::DUNGEON_REVERSE] = GetReverseEntrances(entrancePools[Type::DUNGEON]); + } + } + + // Boss Entrances + if (world->Setting("Randomize Boss Entrances") == "On") + { + entrancePools[Type::BOSS] = world->GetShuffleableEntrances(Type::BOSS, /*onlyPrimary = */ true); + + if (world->Setting("Decouple Entrances") == "On") + { + entrancePools[Type::BOSS_REVERSE] = GetReverseEntrances(entrancePools[Type::BOSS]); + } + } + + // Grotto Entrances + if (world->Setting("Randomize Grotto Entrances") == "On") + { + entrancePools[Type::GROTTO] = world->GetShuffleableEntrances(Type::GROTTO, /*onlyPrimary = */ true); + + if (world->Setting("Decouple Entrances") == "On") + { + entrancePools[Type::GROTTO_REVERSE] = GetReverseEntrances(entrancePools[Type::GROTTO]); + } + } + + // Cave Entrances + if (world->Setting("Randomize Cave Entrances") == "On") + { + entrancePools[Type::CAVE] = world->GetShuffleableEntrances(Type::CAVE, /*onlyPrimary = */ true); + + if (world->Setting("Decouple Entrances") == "On") + { + entrancePools[Type::CAVE_REVERSE] = GetReverseEntrances(entrancePools[Type::CAVE]); + } + } + + // Interior Entrances + if (world->Setting("Randomize Interior Entrances") == "On") + { + entrancePools[Type::INTERIOR] = world->GetShuffleableEntrances(Type::INTERIOR, /*onlyPrimary = */ true); + + if (world->Setting("Decouple Entrances") == "On") + { + entrancePools[Type::INTERIOR_REVERSE] = GetReverseEntrances(entrancePools[Type::INTERIOR]); + } + } + + // Overworld Entrances + if (world->Setting("Randomize Overworld Entrances") == "On") + { + // Normally we allow any overworld entrances to link together. + // However, if overworld entrances are mixed with other entrance types + // that expect to only match with exclusively primary or non-primary + // entrances, we have to separate overworld entrances by their primary/ + // non-primary distinction to fit with the other entrances + const auto& mixedPools = world->GetSettings().GetMixedEntrancePools(); + bool excludeOverworldReverse = + world->Setting("Decouple Entrances") == "Off" && + std::ranges::any_of(mixedPools, [](const auto& pool) { + return randomizer::utility::container::ElementInContainer(pool, "Overworld"); + }); /*Overworld in a mixed pool*/ + entrancePools[Type::OVERWORLD] = + world->GetShuffleableEntrances(Type::OVERWORLD, /*onlyPrimary = */ excludeOverworldReverse); + } + + // Match pool types + for (auto& [entranceType, entrancePool] : entrancePools) + { + for (auto& entrance : entrancePool) + { + entrance->SetType(entranceType); + } + } + + SetShuffledEntrances(entrancePools); + + // Set appropriate types as decoupled + auto potentiallyDecoupledTypes = { + Type::DUNGEON, + Type::DUNGEON_REVERSE, + Type::BOSS, + Type::BOSS_REVERSE, + Type::GROTTO, + Type::GROTTO_REVERSE, + Type::CAVE, + Type::CAVE_REVERSE, + Type::INTERIOR, + Type::INTERIOR_REVERSE, + Type::OVERWORLD, + }; + if (world->Setting("Decouple Entrances") == "On") + { + for (const auto& type : potentiallyDecoupledTypes) + { + if (entrancePools.contains(type)) + { + for (auto& entrance : entrancePools.at(type)) + { + entrance->SetDecoupled(true); + } + } + } + } + + // Combine the Mixed pools into their respective pools + const auto& mixedPoolList = world->GetSettings().GetMixedEntrancePools(); + int counter = 1; + for (const auto& mixedPool : mixedPoolList) + { + auto mixedType = TypeFromStr("Mixed Pool " + std::to_string(counter)); + for (const auto& typeStr : mixedPool) + { + auto type = TypeFromStr(typeStr); + if (type == Type::INVALID) + { + throw std::runtime_error("Unknown entrance type \"" + typeStr + "\" in mixed pools"); + } + // Only bother with entrance types that are being shuffled + for (const auto& entranceType : {type, TypeToReverse(type)}) + { + if (entrancePools.contains(entranceType)) + { + // Create the Mixed Pool entry if it doesn't exist + if (!entrancePools.contains(mixedType)) + { + entrancePools[mixedType] = {}; + } + for (const auto& entrance : entrancePools.at(entranceType)) + { + entrancePools.at(mixedType).push_back(entrance); + } + // Delete the original pool once it's been added + entrancePools.erase(entranceType); + } + } + } + counter += 1; + } + + return entrancePools; + } + + EntrancePools CreateTargetPools(EntrancePools& entrancePools) + { + EntrancePools targetEntrancePools = {}; + for (auto& [type, entrancePool] : entrancePools) + { + if (type == Type::SPAWN) + { + EntrancePool spawnPool = {}; + auto world = entrancePool[0]->GetWorld(); + // Get all the entrances of these types to use as spawn targets + for (const auto& typeForSpawn : {Type::SPAWN, Type::INTERIOR, Type::CAVE, Type::OVERWORLD, Type::GROTTO}) + { + for (const auto& entrance : world->GetShuffleableEntrances(typeForSpawn)) + { + auto newTarget = entrance->GetNewTarget(); + spawnPool.push_back(newTarget); + + // Don't assume we have access to random spawn targets. We're only connecting to one of them + // so assuming we have access to all of them would be erroneous. + newTarget->SetRequirement(requirement::IMPOSSIBLE_REQUIREMENT); + } + } + targetEntrancePools[type] = spawnPool; + for (auto& entrance : entrancePool) + { + entrance->Disconnect(); + } + } + else + { + targetEntrancePools[type] = AssumeEntrancePool(entrancePool); + } + } + return targetEntrancePools; + } + + EntrancePool AssumeEntrancePool(EntrancePool& entrancePool) + { + EntrancePool assumedPool = {}; + for (auto& entrance : entrancePool) + { + auto assumedForward = entrance->AssumeReachable(); + if (entrance->GetReverse() && !entrance->IsDecoupled()) + { + auto assumedReturn = entrance->GetReverse()->AssumeReachable(); + assumedForward->BindTwoWay(assumedReturn); + } + assumedPool.push_back(assumedForward); + } + return assumedPool; + } + + void SetPlandomizedEntrances(world::World* world, + EntrancePools& entrancePools, + EntrancePools& targetEntrancePools) + { + LOG_TO_DEBUG("Now placing plandomizer entrances"); + auto& worlds = world->GetRandomizer()->GetWorlds(); + auto itemPool = item_pool::GetCompleteItemPool(worlds); + + for (auto& [plandoEntrance, plandoTarget] : world->GetPlandomizerEntrances()) + { + auto entranceToConnect = plandoEntrance; + auto targetToConnect = plandoTarget; + auto entranceType = plandoEntrance->GetType(); + + // Throw error if entrance/target types are not shuffleable + if (entranceType == Type::INVALID) + { + throw std::runtime_error(entranceToConnect->GetOriginalName() + + " is not an entrance that can be shuffled"); + } + if (plandoTarget->GetType() == Type::INVALID) + { + throw std::runtime_error(plandoTarget->GetOriginalName() + + " is not an entrance that can be shuffled"); + } + + // Throw error if entrance type is shuffleable, but the type itself is not randomized currently + if (!entrancePools.contains(entranceType)) + { + throw std::runtime_error("Entrance type " + TypeToStr(entranceType) + " for " + + entranceToConnect->GetOriginalName() + " is not being shuffled and thus can't be plandomized."); + } + + // Get the appropriate pools + auto& entrancePool = entrancePools.at(entranceType); + auto& targetPool = targetEntrancePools.at(entranceType); + + // If entrances are coupled, but the user tries to plandomize a non-primary connection, get the primary connection + // instead + if (world->Setting("Decouple Entrances") == "Off" && + utility::container::ElementInContainer(entrancePool, entranceToConnect->GetReverse())) + { + entranceToConnect = entranceToConnect->GetReverse(); + targetToConnect = targetToConnect->GetReverse(); + } + + if (utility::container::ElementInContainer(entrancePool, entranceToConnect)) + { + bool validTargetFound = false; + for (auto& target : targetPool) + { + // If we've found the proper target + if (targetToConnect == target->GetReplaces()) + { + try + { + CheckEntrancesCompatibility(entranceToConnect, target); + ChangeConnections(entranceToConnect, target); + // If the spawn entrance isn't placed, then we can't validate the world + if (world->GetEntrance("Links Spawn -> Outside Links House")->GetConnectedArea() != nullptr) + { + ValidateWorld(world, entranceToConnect, itemPool); + } + validTargetFound = true; + ConfirmReplacement(entranceToConnect, target); + } + catch(const EntranceShuffleError& e) + { + throw std::runtime_error("Could not connect entrance " + + entranceToConnect->GetOriginalName() + " to " + target->GetOriginalName() + + " Reason:\n" + e.what()); + } + if (validTargetFound) + { + break; + } + } + } + + // If we found our target, delete the entrance and it's now connected target from their respective pools + if (validTargetFound) + { + utility::container::Erase(entrancePool, entranceToConnect); + utility::container::Erase(targetPool, targetToConnect->GetAssumed()); + } + // Otherwise, the target is invalid + else + { + throw std::runtime_error("Entrance " + targetToConnect->GetOriginalName() + " is not a valid target for " + + entranceToConnect->GetOriginalName()); + } + } + else + { + throw std::runtime_error("Plandomizer Error: " + entranceToConnect->GetOriginalName() + + " could not be found."); + } + } + + LOG_TO_DEBUG("All plandomized entrances have been placed."); + } + + void ShuffleNonAssumedEntrancesPools(world::World* world, + EntrancePools& entrancePools, + EntrancePools& targetEntrancePools) + { + // If we aren't shuffling any non-assumed types, return early + if (std::ranges::none_of(entrancePools | std::ranges::views::keys, [](const auto& type) { + return NON_ASSUMED_TYPES.contains(type); + })) { + return; + } + + auto& worlds = world->GetRandomizer()->GetWorlds(); + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + + // The idea here is we want to try shuffling all the non-assumed entrances + // at the same time since we can't validate the world after each one individually. + // (That would require assuming access to entrances which we can't guarantee access to.) + // Realistically, this should never take more than 1 or 2 tries unless there's some wacky + // plandomizer stuff going on. Currently, the only non-assumed entrance we're shuffling + // is the randomized spawn, but if we ever shuffle warp portals, they'll go here too. + + int retries = 20; + while (retries > 0) + { + std::unordered_map rollbacks = {}; + // Connect each non-assumed entrance to a random target in its pool + for (auto& [entranceType, entrancePool] : entrancePools) + { + if (NON_ASSUMED_TYPES.contains(entranceType)) + { + auto& targetEntrancePool = targetEntrancePools.at(entranceType); + for (auto& entrance : entrancePool) + { + randomizer::utility::random::ShufflePool(targetEntrancePool); + + // Loop through and find a valid target entrance to connect to + for (auto& target : targetEntrancePool) + { + // If this target has already been used, skip over it + if (target->GetConnectedArea() == nullptr) + { + continue; + } + + LOG_TO_DEBUG("Attempting to connect " + entrance->GetOriginalName() + " to " + + target->GetConnectedArea()->GetName() + " [W" + + std::to_string(entrance->GetWorld()->GetID()) + "]"); + ChangeConnections(entrance, target); + rollbacks[entrance] = target; + break; + } + } + } + } + + // After each entrance is connected, then try to validate the world + bool successfulConnection = false; + try + { + ValidateWorld(world, nullptr, completeItemPool); + for (auto& [entrance, target] : rollbacks) + { + ConfirmReplacement(entrance, target); + utility::container::Erase(targetEntrancePools[entrance->GetType()], target); + } + // Once we've made a valid world, delete all other targets that didn't get used + for (auto& [entranceType, targetPool] : targetEntrancePools) + { + if (NON_ASSUMED_TYPES.contains(entranceType)) + { + for (auto& target : targetPool) + { + DeleteTargetEntrance(target); + } + // Also delete the non-assumed entrance type from the pool + entrancePools.erase(entranceType); + } + } + successfulConnection = true; + } + catch(const EntranceShuffleError& e) + { + // If we're unsuccessful, revert all connections and try again + LOG_TO_DEBUG(std::string("Failed to connect non-assumed entrances. Reason: ") + e.what()); + retries -= 1; + for (auto& [entrance, target] : rollbacks) + { + RestoreConnections(entrance, target); + } + } + if(successfulConnection) + { + break; + } + } + + if (retries <= 0) + { + throw std::runtime_error("Ran out of retries when attempting to place non-assumed entrances"); + } + } + + void ShuffleEntrancePool(EntrancePool& entrancePool, + EntrancePool& targetEntrancePool, + int retries /* = 20*/) + { + while (retries > 0) + { + retries -= 1; + std::unordered_map rollbacks = {}; + try + { + ShuffleEntrances(entrancePool, targetEntrancePool, rollbacks); + for (auto& [entrance, target] : rollbacks) + { + ConfirmReplacement(entrance, target); + } + return; + } + catch(const EntranceShuffleError& e) + { + for (auto& [entrance, target] : rollbacks) + { + RestoreConnections(entrance, target); + } + LOG_TO_DEBUG("Failed to place all entrances in a pool for World " + std::to_string(entrancePool[0]->GetWorld()->GetID()) + + ". Will retry " + std::to_string(retries) + " more times"); + LOG_TO_DEBUG(e.what()); + } + } + + throw std::runtime_error( + "Ran out of retries when shuffling entrances. If you see this error, try using a few different seeds to see if any " + "generate successfully."); + } + + void ShuffleEntrances(EntrancePool& entrancePool, + EntrancePool& targetEntrancePool, + std::unordered_map& rollbacks) + { + // This shouldn't be empty, but just incase + if (entrancePool.empty()) { + return; + } + + auto& worlds = entrancePool.front()->GetWorld()->GetRandomizer()->GetWorlds(); + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + utility::random::ShufflePool(entrancePool); + + for (auto& entrance : entrancePool) + { + // If this entrance is already connected, don't connect it to another area + if (entrance->GetConnectedArea() != nullptr) + { + continue; + } + utility::random::ShufflePool(targetEntrancePool); + + // Loop through and find a valid target entrance to connect to + for (auto& target : targetEntrancePool) + { + // If this target has already been used, skip over it + if (target->GetConnectedArea() == nullptr) + { + continue; + } + + LOG_TO_DEBUG("Attempting to connect " + entrance->GetOriginalName() + " to " + + target->GetConnectedArea()->GetName() + " from " + + target->GetReplaces()->GetParentArea()->GetName() + " [W" + + std::to_string(entrance->GetWorld()->GetID()) + + "]"); + if (ReplaceEntrance(entrance, target, rollbacks, completeItemPool)) + { + break; + } + } + + // If this entrance was unable to connect to target, throw an error + if (entrance->GetConnectedArea() == nullptr) + { + throw EntranceShuffleError("No more valid entrances to replace " + entrance->GetOriginalName() + " in world " + + std::to_string(entrance->GetWorld()->GetID())); + } + } + + // Check to make sure there are no dangling targets. If there are, something is very wrong + for (auto& target : targetEntrancePool) + { + if (target->GetConnectedArea() != nullptr) + { + throw std::runtime_error("Dangling Target Entrance " + target->GetReplaces()->GetOriginalName()); + } + } + } + + bool ReplaceEntrance(Entrance* entrance, + Entrance* target, + std::unordered_map& rollbacks, + const item_pool::ItemPool& completeItemPool) + { + try + { + CheckEntrancesCompatibility(entrance, target); + ChangeConnections(entrance, target); + ValidateWorld(entrance->GetWorld(), entrance, completeItemPool); + rollbacks[entrance] = target; + return true; + } + catch(const EntranceShuffleError& e) + { + LOG_TO_DEBUG("Failed to connect " + entrance->GetOriginalName() + " to " + + target->GetReplaces()->GetOriginalName() + " (Reason: " + e.what() + ") World " + + std::to_string(entrance->GetWorld()->GetID())); + if (entrance->GetConnectedArea() != nullptr) + { + RestoreConnections(entrance, target); + } + } + return false; + } + + void CheckEntrancesCompatibility(const Entrance* entrance, const Entrance* target) + { + if (entrance->GetReverse() && entrance->GetReverse() == target->GetReplaces()) + { + throw EntranceShuffleError("Attempted self-connection"); + } + } + + void ChangeConnections(Entrance* entrance, Entrance* target) + { + entrance->Connect(target->Disconnect()); + entrance->SetReplaces(target->GetReplaces()); + // If entrances are coupled, set the opposite connection as well + if (entrance->GetReverse() != nullptr && !entrance->IsDecoupled()) + { + target->GetReplaces()->GetReverse()->Connect(entrance->GetReverse()->GetAssumed()->Disconnect()); + target->GetReplaces()->GetReverse()->SetReplaces(entrance->GetReverse()); + } + } + + void RestoreConnections(Entrance* entrance, Entrance* target) + { + target->Connect(entrance->Disconnect()); + entrance->SetReplaces(nullptr); + if (entrance->GetReverse() && !entrance->IsDecoupled()) + { + entrance->GetReverse()->GetAssumed()->Connect(target->GetReplaces()->GetReverse()->Disconnect()); + target->GetReplaces()->GetReverse()->SetReplaces(nullptr); + } + } + + void ConfirmReplacement(Entrance* entrance, Entrance* target) + { + DeleteTargetEntrance(target); + LOG_TO_DEBUG("Finalized Connection " + entrance->GetOriginalName() + " to " + entrance->GetConnectedArea()->GetName() + + " [W" + std::to_string(entrance->GetWorld()->GetID()) + "]"); + if (entrance->GetReverse() != nullptr && !entrance->IsDecoupled()) + { + auto replacedReverse = entrance->GetReplaces()->GetReverse(); + LOG_TO_DEBUG("Finalized Connection " + replacedReverse->GetOriginalName() + " to " + + replacedReverse->GetConnectedArea()->GetName() + " [W" + + std::to_string(entrance->GetWorld()->GetID()) + "]"); + DeleteTargetEntrance(entrance->GetReverse()->GetAssumed()); + } + } + + void DeleteTargetEntrance(Entrance* target) + { + if (target->GetConnectedArea() != nullptr) + { + target->Disconnect(); + } + if (target->GetParentArea() != nullptr) + { + target->GetParentArea()->RemoveExit(target); + } + } + + void ValidateWorld(world::World* world, + Entrance* entrance, + const item_pool::ItemPool& completeItemPool) + { + // Validate that all logic is still satisfied + auto& worlds = world->GetRandomizer()->GetWorlds(); + auto verifyLogicError = search::VerifyLogic(&worlds, completeItemPool); + if (verifyLogicError.has_value()) + { + throw EntranceShuffleError("Not all logic is satisfied! Reason:\n" + verifyLogicError.value()); + } + + // Check to make sure there's at least 1 sphere zero location available + auto sphereZeroSearch = search::Search::SphereZero(&worlds); + sphereZeroSearch.SearchWorlds(); + const auto& foundLocations = sphereZeroSearch._visitedLocations; + const auto numSphereZeroLocations = std::ranges::count_if(foundLocations, [](const auto& location) { + return location->IsProgression(); + }); + + // If there are no sphere zero locations available and we didn't find an accessible disconnected exit, then this world will not + // be valid. Often times when many entrances are randomized we won't find any locations, but will find accessible disconnected + // exits that haven't been shuffled yet. In this case we can usually wait until these exits are connected and more often + // than not this will lead us to sphere zero locations. + if (numSphereZeroLocations == 0 && !sphereZeroSearch.HasAccessibleDisconnectedExit()) + { + throw EntranceShuffleError("No sphere 0 locations reachable at the start!"); + } + } + + void SetShuffledEntrances(EntrancePools& entrancePools) + { + for (auto& [entranceType, entrancePool] : entrancePools) + { + for (auto& entrance : entrancePool) + { + entrance->SetShuffled(true); + if (entrance->GetReverse() != nullptr) + { + entrance->GetReverse()->SetShuffled(true); + } + } + } + } + + EntrancePool GetReverseEntrances(const EntrancePool& entrances) + { + EntrancePool reverseEntrances = {}; + for (const auto& entrance : entrances) + { + reverseEntrances.push_back(entrance->GetReverse()); + } + return reverseEntrances; + } + + const std::set& GetPossibleMixedPoolTypes() { + static std::set possibleMixedPoolTypes = { + TypeToStr(DUNGEON), + TypeToStr(BOSS), + TypeToStr(GROTTO), + TypeToStr(INTERIOR), + TypeToStr(CAVE), + TypeToStr(OVERWORLD), + }; + + return possibleMixedPoolTypes; + } +} // namespace randomizer::logic::entrance_shuffle diff --git a/mods/randomizer/generator/logic/entrance_shuffle.hpp b/mods/randomizer/generator/logic/entrance_shuffle.hpp new file mode 100644 index 0000000000..88c4939672 --- /dev/null +++ b/mods/randomizer/generator/logic/entrance_shuffle.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "entrance.hpp" +#include "world.hpp" + +namespace randomizer::logic::entrance_shuffle +{ + void ShuffleWorldEntrances(world::World* world); + void SetAllEntrancesData(world::World* world); + entrance::EntrancePools CreateEntrancePools(world::World* world); + entrance::EntrancePools CreateTargetPools(entrance::EntrancePools& entrancePools); + entrance::EntrancePool AssumeEntrancePool(entrance::EntrancePool& entrancePool); + void SetPlandomizedEntrances(world::World* world, + entrance::EntrancePools& entrancePools, + entrance::EntrancePools& targetEntrancePools); + void ShuffleNonAssumedEntrancesPools(world::World* world, + entrance::EntrancePools& entrancePools, + entrance::EntrancePools& targetEntrancePools); + void ShuffleEntrancePool(entrance::EntrancePool& entrancePool, + entrance::EntrancePool& targetEntrancePool, + int retries = 20); + void ShuffleEntrances(entrance::EntrancePool& entrancePool, + entrance::EntrancePool& targetEntrancePool, + std::unordered_map& rollbacks); + bool ReplaceEntrance(entrance::Entrance* entrance, + entrance::Entrance* target, + std::unordered_map& rollbacks, + const item_pool::ItemPool& completeItemPool); + + void CheckEntrancesCompatibility(const entrance::Entrance* entrance, + const entrance::Entrance* target); + void ChangeConnections(entrance::Entrance* entrance, entrance::Entrance* target); + void RestoreConnections(entrance::Entrance* entrance, entrance::Entrance* target); + void ConfirmReplacement(entrance::Entrance* entrance, entrance::Entrance* target); + void DeleteTargetEntrance(entrance::Entrance* target); + void ValidateWorld(world::World* world, + entrance::Entrance* entrance, + const item_pool::ItemPool& completeItemPool); + + void SetShuffledEntrances(entrance::EntrancePools& entrancePools); + entrance::EntrancePool GetReverseEntrances(const entrance::EntrancePool& entrances); + const std::set& GetPossibleMixedPoolTypes(); + + class EntranceShuffleError: public std::runtime_error + { + public: + explicit EntranceShuffleError(const std::string& message): std::runtime_error(message) {} + }; +} // namespace randomizer::logic::entrance_shuffle diff --git a/mods/randomizer/generator/logic/fill.cpp b/mods/randomizer/generator/logic/fill.cpp new file mode 100644 index 0000000000..8825377301 --- /dev/null +++ b/mods/randomizer/generator/logic/fill.cpp @@ -0,0 +1,550 @@ +#include "fill.hpp" + +#include "item_pool.hpp" +#include "search.hpp" +#include "../utility/random.hpp" +#include "../utility/string.hpp" + +#include +#include + +namespace randomizer::logic::fill +{ + void FillWorlds(world::WorldPool& worlds) + { + // Place each world's restricted items first + for (auto& world : worlds) + { + PlaceRestrictedItems(world, worlds); + } + + item_pool::ItemPool itemPool = {}; + location::LocationPool locationPool = {}; + + // Combine all worlds' item pools and location pools + for (const auto& world : worlds) + { + for (const auto& item : world->GetItemPool()) + { + itemPool.emplace_back(item); + } + for (const auto& location : world->GetAllLocations()) + { + locationPool.emplace_back(location); + } + } + + // Place remaining major items in progress locations + auto majorItems = + utility::container::FilterAndEraseFromVector(itemPool, [](const auto& item) { return item->IsMajor(); }); + auto progressLocations = + utility::container::FilterFromVector(locationPool, + [](const auto& location) { return location->IsProgression(); }); + AssumedFill(worlds, majorItems, itemPool, progressLocations); + + // Place Minor items in progression locations if possible + auto minorItems = + utility::container::FilterAndEraseFromVector(itemPool, [](const auto& item) { return item->IsMinor(); }); + FastFill(minorItems, progressLocations); + + // If there are still minor items left, add them back to the main item pool + for (const auto& minorItem : minorItems) + { + itemPool.push_back(minorItem); + } + + // Then place everything else anywhere + FastFill(itemPool, locationPool); + + // Verify that all logic is satisfied + auto verifyLogicError = search::VerifyLogic(&worlds); + if (verifyLogicError.has_value()) + { + throw std::runtime_error("Not all logic satisfied! Reason:\n" + verifyLogicError.value()); + } + } + + void AssumedFill(world::WorldPool& worlds, + item_pool::ItemPool& itemsToPlacePool, + const item_pool::ItemPool& itemsNotYetPlaced, + location::LocationPool allowedLocations, + const int& worldToFill /* = -1 */) + { + // Assumed Fill may sometimes place items in such a way that accidentally locks out being able to place specific items + // later on. Allow the algorithm to retry a reasonable amount of times before returning an error. + int retries = 10; + bool unsuccessfulPlacement = true; + while (unsuccessfulPlacement) + { + if (retries <= 0) + { + std::string errorMsg = "Ran out of retries while attempting to place the following items:\n"; + const auto count = itemsToPlacePool.size() > 5 ? 5 : itemsToPlacePool.size(); + + for (int i = 0; i < count; i++) + { + const auto& item = itemsToPlacePool[i]; + errorMsg += "- " + item->GetName() + "\n"; + } + + if (count < itemsToPlacePool.size()) + { + errorMsg += "- (" + std::to_string(itemsToPlacePool.size() - count) + " more)"; + } + + throw std::runtime_error(errorMsg); + } + + retries -= 1; + unsuccessfulPlacement = false; + + utility::random::ShufflePool(itemsToPlacePool); + auto itemsToPlace = itemsToPlacePool; + location::LocationPool rollbacks = {}; + + while (!itemsToPlace.empty()) + { + // Get a random item to place + auto itemToPlace = itemsToPlace.back(); + itemsToPlace.pop_back(); + + utility::random::ShufflePool(allowedLocations); + location::Location* spotToFill = nullptr; + + // Assume we have all the items which haven't been played yet, except the one we're about to place + auto assumedItems = itemsNotYetPlaced; + assumedItems.insert(assumedItems.end(), itemsToPlace.begin(), itemsToPlace.end()); + auto search = search::Search::Accessible(&worlds, assumedItems, worldToFill); + search.SearchWorlds(); + // search.DumpWorldGraph(); + // return 1; + + // Loop through the shuffled locations until we find a valid one. + // If a world is only checking for beatable logic, then we can ignore + // any access checks and just choose a random location if the world is already beatable + auto beatableOnlyLogic = itemToPlace->GetWorld()->Setting("Logic Rules") == "Beatable Only"; + auto noLogic = itemToPlace->GetWorld()->Setting("Logic Rules") == "No Logic"; + bool canChooseAnyLocation = + noLogic || (search._ownedItems.contains(itemToPlace->GetWorld()->GetGameWinningItem()) && beatableOnlyLogic); + + for (const auto& location : allowedLocations) + { + // Get all reachable LocationAccess spots for this location + std::list locAccList; + for (const auto& locAcc : location->GetAccessList()) + { + if (canChooseAnyLocation || search._visitedAreas.contains(locAcc->GetArea())) + { + locAccList.push_back(locAcc); + } + } + + // If this location is not empty, or has no potentially reachable LocationAccess spot, or is forbidden from + // having this item, then we can't place the item here + if (!location->IsEmpty() || locAccList.empty() || location->GetForbiddenItems().contains(itemToPlace)) + { + continue; + } + + // If any of the LocationAccess spots evaluate to complete, then we can place an item here + if (std::ranges::any_of(locAccList, [&](const auto& la) { + return canChooseAnyLocation || + requirement::EvaluateLocationRequirement(&search, la) == requirement::EvalSuccess::COMPLETE; + })) + { + spotToFill = location; + break; + } + } + + // If we couldn't find a spot to place this item, undo all item placements within this fill attempt and try + // again from the top. + if (spotToFill == nullptr) + { + LOG_TO_DEBUG("No accessible locations to place " + itemToPlace->GetName() + ". Retrying " + + std::to_string(retries) + " more times."); + for (auto& location : rollbacks) + { + itemsToPlace.push_back(location->GetCurrentItem()); + location->RemoveCurrentItem(); + } + + // Also add back the randomly selected item + itemsToPlace.push_back(itemToPlace); + rollbacks.clear(); + // Break out of the item placement loop and flag an unsuccessful placement attempt to try again + unsuccessfulPlacement = true; + break; + } + + // Place the item at the location + spotToFill->SetCurrentItem(itemToPlace); + rollbacks.push_back(spotToFill); + } + } + } + + void FastFill(item_pool::ItemPool& itemsToPlace, location::LocationPool allowedLocations) + { + auto emptyLocations = + utility::container::FilterFromVector(allowedLocations, + [](const auto& location) { return location->IsEmpty(); }); + + if (itemsToPlace.size() > emptyLocations.size()) + { + std::cout << "WARNING: More items than locations when placing items with fast fill. Items: " << itemsToPlace.size() + << " Locations: " << emptyLocations.size() << std::endl; + } + + utility::random::ShufflePool(emptyLocations); + for (auto& location : emptyLocations) + { + if (itemsToPlace.empty()) + { + break; + } + location->SetCurrentItem(utility::random::PopRandomElement(itemsToPlace)); + } + } + + void PlaceRestrictedItems(std::unique_ptr& world, world::WorldPool& worlds) + { + PlaceGoalLocationItems(world, worlds); + PlaceOwnDungeonItems(world, worlds); + PlacePrologueItems(world, worlds); + PlaceAnywhereDungeonRewards(world, worlds); + PlaceAnyDungeonItems(world, worlds); + PlaceOverworldItems(world, worlds); + } + + void PlacePrologueItems(std::unique_ptr& world, world::WorldPool& worlds) + { + if (world->Setting("Skip Prologue") == "On") { + return; + } + + auto locations = world->GetAllLocations(); + // Filter out excluded locations + utility::container::FilterAndEraseFromVector(locations, [](const auto& location) { + return !location->IsProgression(); + }); + + // Filter out the slingshot and progressive swords to place first. The slingshot and first sword have a very limited + // pool of locations and have to be found in the intro. We also include the lantern, shadow crystal, and progressive + // fishing rod because those items can lock prologue locations also. + auto& itemPool = world->GetItemPool(); + auto prologueItems = utility::container::FilterAndEraseFromVector( + itemPool, + [](const auto& item) + { + return item->GetName() == "Slingshot" || item->GetName() == "Progressive Sword" || + item->GetName() == "Lantern" || item->GetName() == "Progressive Fishing Rod" || + item->GetName() == "North Faron Woods Gate Key" || + item->IsShadowCrystal(); + }); + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + AssumedFill(worlds, prologueItems, completeItemPool, locations); + } + + void PlaceGoalLocationItems(std::unique_ptr& world, world::WorldPool& worlds) + { + // If dungeon rewards can be anywhere, then return early and place them later + if (world->Setting("Dungeon Rewards Can Be Anywhere") == "On") + { + return; + } + + auto allLocations = world->GetAllLocations(); + location::LocationPool goalLocations = {}; + + // Filter out goal locations + goalLocations = utility::container::FilterFromVector(allLocations, [](const auto& location) { + return location->IsGoalLocation() && location->IsEmpty() && location->IsProgression(); + }); + + // Filter out goal items + std::set goalItemNames = {"Progressive Mirror Shard", "Progressive Fused Shadow"}; + + auto goalItems = utility::container::FilterAndEraseFromVector( + world->GetItemPool(), + [&](const auto& item) { return goalItemNames.contains(item->GetName()); }); + + // Return an error if there aren't enough goal locations + if (goalItems.size() > goalLocations.size()) + { + throw std::runtime_error("Not enough available locations to place dungeon rewards at the end of dungeons."); + } + + // Place goal items at goal locations + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + AssumedFill(worlds, goalItems, completeItemPool, goalLocations); + + // Determine required dungeons now that we placed goal location items + world->DetermineRequiredDungeons(); + } + + void PlaceOwnDungeonItems(std::unique_ptr& world, world::WorldPool& worlds) + { + for (const auto& [dungeonName, dungeon] : world->GetDungeonTable()) + { + // Filter hint signs out of dungeon locations + auto dungeonLocations = dungeon->GetLocations(); + utility::container::FilterAndEraseFromVector(dungeonLocations, [](const auto& location) { + return location->HasCategories("Non-Item Location"); + }); + + // Filter out excluded locations if this dungeon is required + if (dungeon->IsRequired()) { + utility::container::FilterAndEraseFromVector(dungeonLocations, [](const auto& location) { + return !location->IsProgression(); + }); + } + + // Clang doesn't like passing structured binding variables to lambda functions via reference, so we create these + // temporary variables to serve the purpose + auto& dungeon_ = dungeon; + auto& dungeonName_ = dungeonName; + + // Small Keys + if (world->Setting("Small Keys") == "Own Dungeon") + { + auto smallKeys = utility::container::FilterAndEraseFromVector( + world->GetItemPool(), + [&](const auto& item) + { + return item == dungeon_->GetSmallKey() || + (dungeonName_ == "Snowpeak Ruins" && + (item->GetName() == "Ordon Pumpkin" || item->GetName() == "Ordon Cheese")); + }); + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + AssumedFill(worlds, smallKeys, completeItemPool, dungeonLocations); + } + + // Big Keys + if (world->Setting("Big Keys") == "Own Dungeon") + { + auto bigKeys = utility::container::FilterAndEraseFromVector(world->GetItemPool(), + [&](const auto& item) + { return item == dungeon_->GetBigKey(); }); + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + AssumedFill(worlds, bigKeys, completeItemPool, dungeonLocations); + } + + // Place maps and compasses last with fast fill since they're junk items + if (world->Setting("Maps and Compasses") == "Own Dungeon") + { + auto mapsCompasses = utility::container::FilterAndEraseFromVector( + world->GetItemPool(), + [&](const auto& item) { return item == dungeon_->GetCompass() || item == dungeon_->GetDungeonMap(); }); + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + FastFill(mapsCompasses, dungeonLocations); + } + } + } + + void PlaceAnywhereDungeonRewards(std::unique_ptr& world, world::WorldPool& worlds) + { + // If dungeon rewards can't be anywhere, then return early as we placed them earlier + if (world->Setting("Dungeon Rewards Can Be Anywhere") == "Off") + { + return; + } + + auto allLocations = world->GetAllLocations(); + // Filter out any nonprogress locations + utility::container::FilterAndEraseFromVector(allLocations, [](const auto& location) { + return !location->IsProgression(); + }); + + // Filter out goal items + std::set goalItemNames = {"Progressive Mirror Shard", "Progressive Fused Shadow"}; + + auto goalItems = utility::container::FilterAndEraseFromVector( + world->GetItemPool(), + [&](const auto& item) { return goalItemNames.contains(item->GetName()); }); + + // Place the items + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + AssumedFill(worlds, goalItems, completeItemPool, allLocations); + + // Determine required dungeons now that we placed goal location items + world->DetermineRequiredDungeons(); + } + + void PlaceAnyDungeonItems(std::unique_ptr& world, world::WorldPool& worlds) + { + item_pool::ItemPool anyDungeonItems = {}; + location::LocationPool anyDungeonLocations = {}; + + // Split the placement of any dungeon items into two pools. Dungeon items from dungeons which should be barren + // will only be distributed among barren dungeons, where as items from nonbarren dungeons will be distributed + // among nonbarren dungeons + std::list nonBarrenDungeons = {}; + std::list barrenDungeons = {}; + for (const auto& [dungeonName, dungeon] : world->GetDungeonTable()) + { + if (dungeon->ShouldBeBarren()) + { + barrenDungeons.push_back(dungeon.get()); + } + else + { + nonBarrenDungeons.push_back(dungeon.get()); + } + } + + // Loop through each pool separately + for (const auto& dungeons : {nonBarrenDungeons, barrenDungeons}) + { + anyDungeonItems.clear(); + anyDungeonLocations.clear(); + // Gather all the appropriate items and locations for the dungeon in this pool + for (const auto& dungeon : dungeons) + { + // Clang doesn't like passing structured binding variables to lambda functions via reference, so we create these + // temporary variables to serve the purpose + auto& dungeon_ = dungeon; + // Add small keys to the pool if small keys are any dungeon + if (world->Setting("Small Keys") == "Any Dungeon") + { + auto smallKeys = utility::container::FilterAndEraseFromVector( + world->GetItemPool(), + [&](const auto& item) + { + return item == dungeon_->GetSmallKey() || + (dungeon_->GetName() == "Snowpeak Ruins" && + (item->GetName() == "Ordon Pumpkin" || item->GetName() == "Ordon Cheese")); + }); + std::ranges::copy(smallKeys, std::back_inserter(anyDungeonItems)); + } + + // Add big keys to the pool if big keys are any dungeon + if (world->Setting("Big Keys") == "Any Dungeon") + { + auto bigKeys = utility::container::FilterAndEraseFromVector( + world->GetItemPool(), + [&](const auto& item) { return item == dungeon_->GetBigKey(); }); + std::ranges::copy(bigKeys, std::back_inserter(anyDungeonItems)); + } + + // Add maps and compasses to the pool if maps and compasses are any dungeon + if (world->Setting("Maps and Compasses") == "Any Dungeon") + { + auto mapsCompasses = utility::container::FilterAndEraseFromVector( + world->GetItemPool(), + [&](const auto& item) { return item == dungeon_->GetCompass() || item == dungeon_->GetDungeonMap(); }); + std::ranges::copy(mapsCompasses, std::back_inserter(anyDungeonItems)); + } + + // Add this dungeon's locations to the anyDungeonLocations pool. If this is a nonbarren dungeon, only include + // locations which are still progression. If it's a barren dungeon, include all the locations + auto dungeonLocations = dungeon->GetLocations(); + // Filter out non-item locations (i.e. hint signs) + utility::container::FilterAndEraseFromVector(dungeonLocations, [](const auto& location) { + return location->HasCategories("Non-Item Location"); + }); + + std::ranges::copy_if(dungeonLocations, + std::back_inserter(anyDungeonLocations), + [&](const auto& location) { return dungeon->ShouldBeBarren() || location->IsProgression(); }); + } + + // Place the dungeon items in the appropriate dungeon locations + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + AssumedFill(worlds, anyDungeonItems, completeItemPool, anyDungeonLocations); + } + } + + void PlaceOverworldItems(std::unique_ptr& world, world::WorldPool& worlds) + { + item_pool::ItemPool overworldItems = {}; + location::LocationPool overworldLocations = world->GetAllLocations(); + // Filter out any nonprogress locations + utility::container::FilterAndEraseFromVector(overworldLocations, + [](const auto& location) { return !location->IsProgression(); }); + + for (const auto& [dungeonName, dungeon] : world->GetDungeonTable()) + { + // Clang doesn't like passing structured binding variables to lambda functions via reference, so we create these + // temporary variables to serve the purpose + auto& dungeon_ = dungeon; + auto& dungeonName_ = dungeonName; + + // Add small keys to the pool if small keys are overworld + if (world->Setting("Small Keys") == "Overworld") + { + auto smallKeys = utility::container::FilterAndEraseFromVector( + world->GetItemPool(), + [&](const auto& item) + { + return item == dungeon_->GetSmallKey() || + (dungeonName_ == "Snowpeak Ruins" && + (item->GetName() == "Ordon Pumpkin" || item->GetName() == "Ordon Cheese")); + }); + std::ranges::copy(smallKeys, std::back_inserter(overworldItems)); + } + + // Add big keys to the pool if big keys are overworld + if (world->Setting("Big Keys") == "Overworld") + { + auto bigKeys = utility::container::FilterAndEraseFromVector(world->GetItemPool(), + [&](const auto& item) + { return item == dungeon_->GetBigKey(); }); + std::ranges::copy(bigKeys, std::back_inserter(overworldItems)); + } + + // Add maps and compasses to the pool if maps and compasses are overworld + if (world->Setting("Maps and Compasses") == "Overworld") + { + auto mapsCompasses = utility::container::FilterAndEraseFromVector( + world->GetItemPool(), + [&](const auto& item) { return item == dungeon_->GetCompass() || item == dungeon_->GetDungeonMap(); }); + std::ranges::copy(mapsCompasses, std::back_inserter(overworldItems)); + } + + // Remove this dungeon's locations from the overworldLocations pool + overworldLocations = utility::container::FilterFromVector( + overworldLocations, + [&](const auto& location) + { return !utility::container::ElementInContainer(dungeon_->GetLocations(), location); }); + } + + // Place the dungeon items in the overworld locations + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + AssumedFill(worlds, overworldItems, completeItemPool, overworldLocations); + } + + void CacheExitTimeForms(world::WorldPool& worlds) + { + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + auto searchWithItems = search::Search::AllLocationsReachable(&worlds, completeItemPool); + searchWithItems.SearchWorlds(); + + for (auto& world : worlds) + { + LOG_TO_DEBUG("Caching timeforms for world " + std::to_string(world->GetID())); + auto& exitTimeFormCache = world->GetExitTimeFormCache(); + exitTimeFormCache.clear(); + for (const auto& [areaName, area] : world->GetAreaTable()) + { + const auto& areaFormTimes = searchWithItems._areaFormTime[area.get()]; + for (const auto& exit : area->GetExits()) + { + auto req = exit->GetRequirement(); + exitTimeFormCache[exit] = requirement::FormTime::NONE; + for (const auto& formTime : requirement::FormTime::ALL_FORM_TIMES) + { + if (formTime & areaFormTimes && + requirement::EvaluateRequirementAtFormTime(req, + &searchWithItems, + formTime, + world.get())) + { + exitTimeFormCache[exit] |= formTime; + } + } + } + } + } + } +} // namespace randomizer::logic::fill diff --git a/mods/randomizer/generator/logic/fill.hpp b/mods/randomizer/generator/logic/fill.hpp new file mode 100644 index 0000000000..c3c36ec569 --- /dev/null +++ b/mods/randomizer/generator/logic/fill.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "item.hpp" +#include "world.hpp" + +namespace randomizer::logic::fill +{ + + void FillWorlds(world::WorldPool& worlds); + + /** + * @brief Assumed fill is an algorithm which statistically places items more + * evenly across the world compared to forward fill. The idea is that + * we first start with all the items, take an item out, search for + * available locations (picking up any placed items along the way), + * and choose a random location of the available ones to place the item. + * Repeat for all items in the itemsToPlacePool. + * + * @param worlds The worlds to fill with items + * @param itemsToPlacePool The pool of items which we want to place + * @param itemsNotYetPlaced The pool of items which aren't placed yet, but will be later. + * This is important for the assumed fill algorithm since we need to assume we have these items. + * @param allowedLocations Locations where items in itemsToPlacePool are allowed to be filled. + * @param worldToFill A specific world to fill. If -1 (default), then all worlds are considered + */ + void AssumedFill(world::WorldPool& worlds, + item_pool::ItemPool& itemsToPlacePool, + const item_pool::ItemPool& itemsNotYetPlaced, + location::LocationPool allowedLocations, + const int& worldToFill = -1); + + /** + * @brief Places items in locations completely randomly without any logic checks. + * + * @param itemsToPlace The pool of items to place + * @param allowedLocations The locations where the items can be placed + */ + void FastFill(item_pool::ItemPool& itemsToPlace, location::LocationPool allowedLocations); + + void PlaceRestrictedItems(std::unique_ptr& world, world::WorldPool& worlds); + + /** + * @brief If the prologue is not being skipped, place the sword and slingshot early on to prevent possible placement + * failures later. + * + * @param world The world to place the prologue items in + * @param worlds All the worlds being generated + */ + void PlacePrologueItems(std::unique_ptr& world, world::WorldPool& worlds); + + void PlaceGoalLocationItems(std::unique_ptr& world, world::WorldPool& worlds); + + void PlaceOwnDungeonItems(std::unique_ptr& world, world::WorldPool& worlds); + + void PlaceAnywhereDungeonRewards(std::unique_ptr& world, + world::WorldPool& worlds); + + void PlaceAnyDungeonItems(std::unique_ptr& world, world::WorldPool& worlds); + + void PlaceOverworldItems(std::unique_ptr& world, world::WorldPool& worlds); + + /** + * @brief Cache all the possible timeforms for each exit. This way, the search algorithm doesn't end up testing for + * timeforms that we know ahead of time wouldn't be possible anyway + * @param worlds The worlds to calculate and cache the possible timeforms for + */ + void CacheExitTimeForms(world::WorldPool& worlds); +} // namespace randomizer::logic::fill diff --git a/mods/randomizer/generator/logic/flatten/bits.cpp b/mods/randomizer/generator/logic/flatten/bits.cpp new file mode 100644 index 0000000000..b95a699e75 --- /dev/null +++ b/mods/randomizer/generator/logic/flatten/bits.cpp @@ -0,0 +1,287 @@ +#include + +#include "bits.hpp" +#include "../item.hpp" + +#include + +BitVector::BitVector(const std::list& bits) +{ + for (auto& i : bits) + { + this->set(i); + } +} + +bool BitVector::isEmpty() const +{ + return bitset.none(); +} + +std::set BitVector::ints() const +{ + return intset; +} + +void BitVector::set(const int& i) +{ + bitset.set(i, true); + intset.insert(i); +} + +void BitVector::clear(const int& i) +{ + if (intset.contains(i)) + { + intset.erase(i); + bitset.set(i, false); + } +} + +bool BitVector::test(const int& i) const +{ + return intset.contains(i); +} + +int BitVector::size() const +{ + return intset.size(); +} + +void BitVector::and_(const BitVector& other) +{ + std::set intersection = {}; + std::set_intersection(intset.begin(), + intset.end(), + other.intset.begin(), + other.intset.end(), + std::inserter(intersection, intersection.begin())); + intset = intersection; + bitset &= other.bitset; +} + +void BitVector::or_(const BitVector& other) +{ + intset.insert(other.intset.begin(), other.intset.end()); + bitset |= other.bitset; +} + +bool BitVector::isSubsetOf(const BitVector& other) const +{ + return (bitset | other.bitset) == other.bitset; +} + +bool BitVector::equals(const BitVector& other) const +{ + return bitset == other.bitset; +} + +bool includedIn(const std::bitset<512>& a, const std::bitset<512>& b) +{ + return (a | b) == b; +} + +DNF::DNF(std::vector> terms_): terms(terms_) {} + +bool DNF::isTriviallyFalse() const +{ + return terms.size() == 0; +} + +bool DNF::isTriviallyTrue() const +{ + return std::any_of(terms.begin(), terms.end(), [](const auto& i) { return i == 0; }); +} + +DNF DNF::or_(const DNF& other) +{ + auto new_terms = terms; + new_terms.insert(new_terms.end(), other.terms.begin(), other.terms.end()); + return DNF(new_terms); +} + +// Removes all redundent terms +DNF DNF::dedup() +{ + std::vector> filtered = {}; + for (const auto& candidate : terms) + { + std::vector toPop = {}; + bool nextTerm = false; + for (int existing_idx = 0; existing_idx < filtered.size(); existing_idx++) + { + const auto& existing = filtered[existing_idx]; + if (includedIn(existing, candidate)) + { + // Existing requires fewer or equal things than candidate + nextTerm = true; + break; + } + else if (includedIn(candidate, existing)) + { + // Candidate requires strictly fewer things than existing + toPop.push_back(existing_idx); + } + } + + if (!nextTerm) + { + // Did not break to next term + for (auto c_iter = toPop.rbegin(); c_iter != toPop.rend(); c_iter++) + { + const auto& c = *c_iter; + if (c == filtered.size() - 1) + { + filtered.pop_back(); + } + else + { + // Remove c without shifting elements by replacing + // it with the last element + filtered[c] = filtered.back(); + filtered.pop_back(); + } + } + filtered.push_back(candidate); + } + } + + return DNF(filtered); +} + +// Returns useful, self.or_(other) +// useful is True if other contained at least one term that +// was not redundant. +std::pair DNF::or_useful(const DNF& other) +{ + auto filtered_this = terms; + std::vector> filtered_other = {}; + bool useful = false; + + for (const auto& candidate : other.terms) + { + bool nextTerm = false; + for (const auto& existing : filtered_this) + { + if (includedIn(existing, candidate)) + { + nextTerm = true; + break; + } + } + + if (!nextTerm) + { + filtered_other.push_back(candidate); + useful = true; + } + } + + filtered_this.insert(filtered_this.end(), filtered_other.begin(), filtered_other.end()); + return {useful, DNF(filtered_this)}; +} + +DNF DNF::and_(const DNF& other) +{ + std::vector> d = {}; + for (const auto& t1 : terms) + { + for (const auto& t2 : other.terms) + { + d.push_back(t1 | t2); + } + } + + // Dedup incase things are getting too big + DNF dnf = DNF(d); + if (d.size() > 500) + { + dnf = dnf.dedup(); + } + return dnf; +} + +int BitIndex::bump() +{ + auto c = counter; + counter++; + return c; +} + +int BitIndex::reqBit(const randomizer::logic::requirement::Requirement& req) +{ + uint32_t expectedCount; + randomizer::logic::item::Item* item; + std::string key; + + switch (req._type) + { + case randomizer::logic::requirement::Type::ITEM: + item = std::get(req._args[0]); + key = item->GetName() + "::1"; + if (itemBits.contains(key)) + { + return itemBits[key]; + } + else + { + itemBits[key] = counter; + reverseIndex.push_back(req); + return bump(); + } + case randomizer::logic::requirement::Type::COUNT: + expectedCount = std::get(req._args[0]); + item = std::get(req._args[1]); + key = item->GetName() + "::" + std::to_string(expectedCount); + if (itemBits.contains(key)) + { + return itemBits[key]; + } + else + { + itemBits[key] = counter; + reverseIndex.push_back(req); + return bump(); + } + case randomizer::logic::requirement::Type::GOLDEN_BUGS: + key = std::to_string(std::get(req._args[0])); + if (goldenBugCount.contains(key)) + { + return goldenBugCount[key]; + } + else + { + goldenBugCount[key] = counter; + reverseIndex.push_back(req); + return bump(); + } + case randomizer::logic::requirement::Type::HEARTS: + key = std::to_string(std::get(req._args[0])); + if (heartCount.contains(key)) + { + return heartCount[key]; + } + else + { + heartCount[key] = counter; + reverseIndex.push_back(req); + return bump(); + } + case randomizer::logic::requirement::Type::DUNGEONS_COMPLETED: + key = std::to_string(std::get(req._args[0])); + if (dungeonCompletedCount.contains(key)) + { + return dungeonCompletedCount[key]; + } + else + { + dungeonCompletedCount[key] = counter; + reverseIndex.push_back(req); + return bump(); + } + default: + // Not a flattening requirement + return -1; + } + return -1; +} diff --git a/mods/randomizer/generator/logic/flatten/bits.hpp b/mods/randomizer/generator/logic/flatten/bits.hpp new file mode 100644 index 0000000000..c61e8e9743 --- /dev/null +++ b/mods/randomizer/generator/logic/flatten/bits.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include "../requirement.hpp" + +#include +#include +#include +#include +#include + +class BitVector +{ + public: + BitVector() = default; + explicit BitVector(const std::list& bits); + + bool isEmpty() const; + std::set ints() const; + void set(const int& i); + void clear(const int& i); + bool test(const int& i) const; + int size() const; + void and_(const BitVector& other); + void or_(const BitVector& other); + bool isSubsetOf(const BitVector& other) const; + bool equals(const BitVector& other) const; + + std::bitset<512> bitset; + std::set intset; +}; + +bool includedIn(const std::bitset<512>& a, const std::bitset<512>& b); + +// A logical expression in disjunctive normal form. +// Disjuncts are bit-vectors, but we don't use the BitVector class here +// because it doesn't seem necessary since our bitvectors are small +// and we only need bit set access after the propagation code is done +class DNF +{ + public: + DNF() = default; + DNF(std::vector> terms); + + static DNF True() { return DNF({0}); } + + static DNF False() { return DNF(std::vector> {}); } + + bool isTriviallyFalse() const; + bool isTriviallyTrue() const; + DNF or_(const DNF& other); + DNF dedup(); + std::pair or_useful(const DNF& other); + DNF and_(const DNF& other); + + std::vector> terms = {}; +}; + +class BitIndex +{ + public: + BitIndex() = default; + + int bump(); + int reqBit(const randomizer::logic::requirement::Requirement& req); + + std::unordered_map itemBits = {}; + std::unordered_map heartCount = {}; + std::unordered_map goldenBugCount = {}; + std::unordered_map dungeonCompletedCount = {}; + std::vector reverseIndex = {}; + int counter = 0; +}; diff --git a/mods/randomizer/generator/logic/flatten/flatten.cpp b/mods/randomizer/generator/logic/flatten/flatten.cpp new file mode 100644 index 0000000000..bc6410fb57 --- /dev/null +++ b/mods/randomizer/generator/logic/flatten/flatten.cpp @@ -0,0 +1,510 @@ +#include "flatten.hpp" + +#include + +#include "../world.hpp" + +FlattenSearch::FlattenSearch(randomizer::logic::world::World* world_) +{ + world = world_; + + for (const auto& area : world->GetAreaTable() | std::views::values) + { + for (const auto& exit : area->GetExits()) + { + auto visit = visitor(exit, this); + visitReq(exit->GetRequirement(), visit, world); + } + + for (const auto& event : area->GetEvents()) + { + auto visit = visitor(event, this); + visitReq(event->GetRequirement(), visit, world); + } + } + + const auto root = world->GetRootArea(); + // Start with all formtimes at the root, false for everything else + auto formTimes = randomizer::logic::requirement::FormTime::ALL_FORM_AND_DAY_TIMES; + formTimes.push_back(randomizer::logic::requirement::FormTime::TWILIGHT); + for (const auto& area : world->GetAreaTable() | std::views::values) + { + for (const auto& formTime : formTimes) + { + if (area.get() == root) + { + areaExprs[formTime][area.get()] = DNF::True(); + } + else + { + areaExprs[formTime][area.get()] = DNF::False(); + } + } + } + newlyUpdatedAreas.insert(root); + newThingsFound = true; + + for (auto& exit : root->GetExits()) + { + if (exit->GetConnectedArea() != nullptr) + { + exitsToTry.insert(exit); + } + } +} + +void FlattenSearch::doSearch() +{ + // This algorithm works in three stages: + // 1. Compute area and event requirements -> DNFs + // 2. Compute location requirement -> DNF + // 3. Simplify location requirement -> Requirement + + // This is step 1. This computes everything that requirements + // can depend on in a fixpoint algorithm - namely, area access and events. + newThingsFound = true; + while (newThingsFound) + { + recentlyUpdatedAreas = newlyUpdatedAreas; + recentlyUpdatedEvents = newlyUpdatedEvents; + newlyUpdatedAreas = {}; + newlyUpdatedEvents = {}; + newThingsFound = false; + tryExits(); + tryEvents(); + tryTimeFormExpansion(); + } + + std::unordered_map> itemLocations = {}; + for (const auto& area : world->GetAreaTable() | std::views::values) + { + for (auto& locAccess : area->GetLocations()) + { + auto locationName = locAccess->GetLocation()->GetName(); + if (!itemLocations.contains(locationName)) + { + itemLocations[locationName] = {}; + } + itemLocations[locationName].push_back(locAccess); + } + } + // TODO this immediately combines the "local" requirements with the implicit + // area requirement. It has been hypothesized that converting them + // separately may produce better tooltips, but at that point you need the + // TWWR-Tracker boolean-expression multi-level simplification code + + // Step 2: for every location, OR all the ways to access it + + auto formTimes = randomizer::logic::requirement::FormTime::ALL_FORM_AND_DAY_TIMES; + formTimes.push_back(randomizer::logic::requirement::FormTime::TWILIGHT); + for (auto& [locName, accessList] : itemLocations) + { + auto expr = DNF::False(); + for (const auto& locAcc : accessList) + { + for (const auto& formTime : formTimes) + { + expr = expr.or_(tryLocationAtFormTime(locAcc, formTime)); + } + } + + // Step 3: simplify + auto location = world->GetLocation(locName); + location->SetComputedRequirement(DNFToExpr(bitIndex, expr.dedup())); + // world->locationTable[locName]->computedRequirement.simplifyParenthesis(); + // world->locationTable[locName]->computedRequirement.sortArgs(); + } + + // Do the same for any shuffled entrances so that we can give them tooltips in the tracker + for (auto& [name, area] : world->GetAreaTable()) + { + for (auto& exit : area->GetExits()) + { + if (exit->IsShuffled()) + { + auto expr = DNF::False(); + auto& validFormTimes = exit->GetWorld()->GetExitTimeFormCache()[exit]; + for (const auto& formTime : randomizer::logic::requirement::FormTime::ALL_FORM_TIMES) + { + if (formTime & validFormTimes) + { + expr = expr.or_(tryExitAtFormTime(exit, formTime)); + } + } + exit->SetComputedRequirement(DNFToExpr(bitIndex, expr.dedup())); + } + } + } +} + +// Check for a thing in area whether its logical dependencies +// have recently been updated. +bool FlattenSearch::wasUpdated(randomizer::logic::area::Area* area, void* thing) +{ + if (recentlyUpdatedAreas.contains(area)) + { + return true; + } + + auto& remoteEventReqs = remoteEventRequirements[thing]; + for (auto& event : remoteEventReqs) + { + if (recentlyUpdatedEvents.contains(event)) + { + return true; + } + } + // auto& remoteAreaReqs = remoteAreaRequirements[thing]; + // for (auto& areaStr : remoteAreaReqs) + // { + // randomizer::logic::area::Area* area2; + // world->GetArea(areaStr, area2); + // if (recentlyUpdatedAreas.contains(area2)) + // { + // return true; + // } + // } + + return false; +} + +void FlattenSearch::tryExits() +{ + using namespace randomizer::logic::requirement; + auto exits = exitsToTry; + for (auto& exit : exits) + { + if (!wasUpdated(exit->GetParentArea(), (void*)exit)) + { + continue; + } + auto& validFormTimes = exit->GetWorld()->GetExitTimeFormCache()[exit]; + auto connectedTwilight = exit->GetConnectedArea()->GetTwilightCompletedMacroIndex() != -1; + if (connectedTwilight) + { + validFormTimes |= FormTime::TWILIGHT; + } + for (const auto& formTime : FormTime::ALL_FORM_TIMES_AND_TWILIGHT) + { + if (formTime & validFormTimes) + { + auto connectedArea = exit->GetConnectedArea(); + auto& oldExpr = areaExprs[formTime][connectedArea]; + auto newPartial = tryExitAtFormTime(exit, formTime); + + // Add the twilight completed macro for access to this area if it's part of a twilight + if (connectedTwilight && formTime != FormTime::TWILIGHT) + { + auto& oldExprTwilight = areaExprs[FormTime::TWILIGHT][connectedArea]; + auto [useful, newExpr] = oldExprTwilight.or_useful(newPartial); + if (useful) + { + newlyUpdatedAreas.insert(connectedArea); + newThingsFound = true; + areaExprs[FormTime::TWILIGHT][connectedArea] = newExpr.dedup(); + for (auto& event : connectedArea->GetEvents()) + { + eventsToTry.insert(event); + } + for (auto& areaExit : connectedArea->GetExits()) + { + if (areaExit->GetConnectedArea() != nullptr) + { + exitsToTry.insert(areaExit); + } + } + areasToTry.insert(connectedArea); + } + + newPartial = newPartial.and_( + evaluatePartialRequirement(bitIndex, + exit->GetWorld()->GetMacro(connectedArea->GetTwilightCompletedMacroIndex()), + this, + 0)); + } + + auto [useful, newExpr] = oldExpr.or_useful(newPartial); + if (useful) + { + newlyUpdatedAreas.insert(connectedArea); + newThingsFound = true; + areaExprs[formTime][connectedArea] = newExpr.dedup(); + for (auto& event : connectedArea->GetEvents()) + { + eventsToTry.insert(event); + } + for (auto& areaExit : connectedArea->GetExits()) + { + if (areaExit->GetConnectedArea() != nullptr) + { + exitsToTry.insert(areaExit); + } + } + areasToTry.insert(connectedArea); + } + } + } + } +} + +void FlattenSearch::tryEvents() +{ + for (auto& event : eventsToTry) + { + if (!wasUpdated(event->GetArea(), (void*)event)) + { + continue; + } + + auto& oldExpr = eventExprs[event->GetEventIndex()]; + auto newPartial = DNF::False(); + for (const auto& formTime : randomizer::logic::requirement::FormTime::ALL_FORM_AND_DAY_TIMES) + { + newPartial = newPartial.or_(tryEventAtFormTime(event, formTime)); + } + auto [useful, newExpr] = oldExpr.or_useful(newPartial); + if (useful) + { + newlyUpdatedEvents.insert(event->GetEventIndex()); + newThingsFound = true; + eventExprs[event->GetEventIndex()] = newExpr.dedup(); + } + } +} + +void FlattenSearch::tryTimeFormExpansion() +{ + using namespace randomizer::logic::requirement; + for (auto& area : areasToTry) + { + if (!recentlyUpdatedAreas.contains(area)) + { + continue; + } + if (area->CanTransform()) + { + auto shadowCrystal = area->GetWorld()->GetShadowCrystal(); + auto shadowCrystalDNF = evaluatePartialRequirement(bitIndex, Requirement {Type::ITEM, {shadowCrystal}}, this, 0); + for (const auto& formTime : FormTime::ALL_FORM_TIMES) + { + auto& oldExpr = areaExprs[formTime][area]; + int oppositeFormTime = FormTime::NONE; + switch (formTime) + { + case FormTime::HUMAN_DAY: + oppositeFormTime = FormTime::WOLF_DAY; + break; + case FormTime::HUMAN_NIGHT: + oppositeFormTime = FormTime::WOLF_NIGHT; + break; + case FormTime::WOLF_DAY: + oppositeFormTime = FormTime::HUMAN_DAY; + break; + case FormTime::WOLF_NIGHT: + oppositeFormTime = FormTime::HUMAN_NIGHT; + } + auto newPartial = areaExprs[oppositeFormTime][area]; + if (!newPartial.isTriviallyFalse()) + { + // Transforming requires shadow crystal + newPartial = newPartial.and_(shadowCrystalDNF); + auto [useful, newExpr] = oldExpr.or_useful(newPartial); + if (useful) + { + newlyUpdatedAreas.insert(area); + newThingsFound = true; + areaExprs[formTime][area] = newExpr.dedup(); + } + } + } + } + if (area->CanChangeTime()) + { + for (const auto& formTime : FormTime::ALL_FORM_TIMES) + { + auto& oldExpr = areaExprs[formTime][area]; + int oppositeFormTime = FormTime::NONE; + switch (formTime) + { + case FormTime::HUMAN_DAY: + oppositeFormTime = FormTime::HUMAN_NIGHT; + break; + case FormTime::HUMAN_NIGHT: + oppositeFormTime = FormTime::HUMAN_DAY; + break; + case FormTime::WOLF_DAY: + oppositeFormTime = FormTime::WOLF_NIGHT; + break; + case FormTime::WOLF_NIGHT: + oppositeFormTime = FormTime::WOLF_DAY; + } + auto newPartial = areaExprs[oppositeFormTime][area]; + if (!newPartial.isTriviallyFalse()) + { + auto [useful, newExpr] = oldExpr.or_useful(newPartial); + if (useful) + { + newlyUpdatedAreas.insert(area); + newThingsFound = true; + areaExprs[formTime][area] = newExpr.dedup(); + } + } + } + } + this->andAreaFormTimes(area); + } +} + +void FlattenSearch::andAreaFormTimes(randomizer::logic::area::Area* area) +{ + using namespace randomizer::logic::requirement; + + auto& areaHumanDay = this->areaExprs[FormTime::HUMAN_DAY][area]; + auto& areaWolfDay = this->areaExprs[FormTime::WOLF_DAY][area]; + auto& areaHumanNight = this->areaExprs[FormTime::HUMAN_NIGHT][area]; + auto& areaWolfNight = this->areaExprs[FormTime::WOLF_NIGHT][area]; + + this->areaExprs[FormTime::DAY][area] = areaHumanDay.and_(areaWolfDay); + this->areaExprs[FormTime::NIGHT][area] = areaHumanNight.and_(areaWolfNight); +} + +DNF FlattenSearch::tryEventAtFormTime(randomizer::logic::area::EventAccess* event, const int& formTime) +{ + return areaExprs[formTime][event->GetArea()].and_( + evaluatePartialRequirement(bitIndex, event->GetRequirement(), this, formTime)); +} + +DNF FlattenSearch::tryLocationAtFormTime(randomizer::logic::area::LocationAccess* location, const int& formTime) +{ + return areaExprs[formTime][location->GetArea()].and_( + evaluatePartialRequirement(bitIndex, location->GetRequirement(), this, formTime)); +} + +DNF FlattenSearch::tryExitAtFormTime(randomizer::logic::entrance::Entrance* exit, const int& formTime) +{ + return areaExprs[formTime][exit->GetParentArea()].and_( + evaluatePartialRequirement(bitIndex, exit->GetRequirement(), this, formTime)); +} + +DNF evaluatePartialRequirement(BitIndex& bitIndex, + const randomizer::logic::requirement::Requirement& req, + FlattenSearch* search, + const int& formTime) +{ + uint32_t expectedCount = 0; + uint32_t expectedHearts = 0; + uint32_t totalHearts = 0; + std::bitset<512> bits = 0; + randomizer::logic::item::Item* item; + int event; + DNF d = DNF(); + randomizer::logic::area::Area* area; + + switch (req._type) + { + case randomizer::logic::requirement::Type::NOTHING: + return DNF::True(); + + case randomizer::logic::requirement::Type::IMPOSSIBLE: + return DNF::False(); + + case randomizer::logic::requirement::Type::OR: + d = DNF::False(); + for (auto& arg : req._args) + { + d = d.or_(evaluatePartialRequirement(bitIndex, + std::get(arg), + search, + formTime)); + } + return d; + + case randomizer::logic::requirement::Type::AND: + d = DNF::True(); + for (auto& arg : req._args) + { + d = d.and_(evaluatePartialRequirement(bitIndex, + std::get(arg), + search, + formTime)); + } + return d; + + case randomizer::logic::requirement::Type::ITEM: + [[fallthrough]]; + case randomizer::logic::requirement::Type::GOLDEN_BUGS: + [[fallthrough]]; + case randomizer::logic::requirement::Type::HEARTS: + [[fallthrough]]; + case randomizer::logic::requirement::Type::DUNGEONS_COMPLETED: + bits[bitIndex.reqBit(req)] = 1; + return DNF({bits}); + + case randomizer::logic::requirement::Type::EVENT: + event = std::get(req._args[0]); + return search->eventExprs[event]; + + case randomizer::logic::requirement::Type::MACRO: + return evaluatePartialRequirement(bitIndex, search->world->GetMacro(std::get(req._args[0])), search, formTime); + + // count requirements frequently have to unify with weaker terms, + // so a count requirement always requires all lesser item counts too. + // this ensures redundant terms can be eliminated + case randomizer::logic::requirement::Type::COUNT: + expectedCount = std::get(req._args[0]); + item = std::get(req._args[1]); + for (auto i = 1; i <= expectedCount; i++) + { + randomizer::logic::requirement::Requirement newReq; + if (i == 1) + { + newReq = randomizer::logic::requirement::Requirement {randomizer::logic::requirement::Type::ITEM, {item}}; + } + else + { + newReq = randomizer::logic::requirement::Requirement {randomizer::logic::requirement::Type::COUNT, {i, item}}; + } + bits[bitIndex.reqBit(newReq)] = 1; + } + return DNF({bits}); + + case randomizer::logic::requirement::Type::DAY: + return (formTime & randomizer::logic::requirement::FormTime::DAY) ? DNF::True() : DNF::False(); + + case randomizer::logic::requirement::Type::NIGHT: + return (formTime & randomizer::logic::requirement::FormTime::NIGHT) ? DNF::True() : DNF::False(); + + case randomizer::logic::requirement::Type::HUMAN_LINK: + return (formTime & randomizer::logic::requirement::FormTime::HUMAN) ? DNF::True() : DNF::False(); + + case randomizer::logic::requirement::Type::WOLF_LINK: + return (formTime & randomizer::logic::requirement::FormTime::WOLF) ? DNF::True() : DNF::False(); + + case randomizer::logic::requirement::Type::TWILIGHT: + return (formTime & randomizer::logic::requirement::FormTime::TWILIGHT) ? DNF::True() : DNF::False(); + + case randomizer::logic::requirement::Type::INVALID: + default: + // actually needs to be some error state? + return DNF::False(); + } + return DNF::False(); +} + +void visitReq(const randomizer::logic::requirement::Requirement& req, + std::function f, + randomizer::logic::world::World* world) +{ + f(req); + if (req._type == randomizer::logic::requirement::Type::AND || req._type == randomizer::logic::requirement::Type::OR) + { + for (auto& arg : req._args) + { + visitReq(std::get(arg), f, world); + } + } + else if (req._type == randomizer::logic::requirement::Type::MACRO) + { + visitReq(world->GetMacro(std::get(req._args[0])), f, world); + } +} diff --git a/mods/randomizer/generator/logic/flatten/flatten.hpp b/mods/randomizer/generator/logic/flatten/flatten.hpp new file mode 100644 index 0000000000..dc7d0f7760 --- /dev/null +++ b/mods/randomizer/generator/logic/flatten/flatten.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include "../entrance.hpp" +#include "simplify_algebraic.hpp" +#include "../../utility/log.hpp" + +#include +#include +#include + +namespace randomizer::logic::area +{ + class EventAccess; + class Area; +} // namespace randomizer::logic::area + +namespace randomizer::logic::world +{ + class World; +} + +class FlattenSearch +{ + public: + FlattenSearch() = default; + FlattenSearch(randomizer::logic::world::World* world_); + + randomizer::logic::world::World* world = nullptr; + BitIndex bitIndex = BitIndex(); + + // partially computed requirements for areas at a + // given timeform and for events + std::unordered_map eventExprs = {}; + std::unordered_map> areaExprs = {}; + + // nodes we haven't looked at we don't even need to bother with + std::set exitsToTry = {}; + std::set eventsToTry = {}; + std::set areasToTry = {}; + + // we only re-check an exit or an event if its dependencies changed. + // dependencies can be the implicit parent area (for events and exits), + // formtime expansion in the area, and "remote" requirements arising + // from the expression itself mentioning an event or an area via can_access + std::set recentlyUpdatedAreas = {}; + std::set recentlyUpdatedEvents = {}; + + std::set newlyUpdatedAreas = {}; + std::set newlyUpdatedEvents = {}; + + std::unordered_map> remoteEventRequirements = {}; + std::unordered_map> remoteAreaRequirements = {}; + bool newThingsFound = false; + + void doSearch(); + bool wasUpdated(randomizer::logic::area::Area* area, void* thing); + void tryExits(); + void tryEvents(); + void tryTimeFormExpansion(); + void andAreaFormTimes(randomizer::logic::area::Area* area); + + DNF tryEventAtFormTime(randomizer::logic::area::EventAccess* event, const int& formTime); + DNF tryLocationAtFormTime(randomizer::logic::area::LocationAccess* location, const int& formTime); + DNF tryExitAtFormTime(randomizer::logic::entrance::Entrance* exit, const int& formTime); +}; + +template +std::function visitor(T* thing, FlattenSearch* search) +{ + auto thingPtr = (void*)thing; + std::function handler = + [=](const randomizer::logic::requirement::Requirement& req) + { + if (req._type == randomizer::logic::requirement::Type::EVENT) + { + if (!search->remoteEventRequirements.contains(thingPtr)) + { + search->remoteEventRequirements[thingPtr] = {}; + } + search->remoteEventRequirements[thingPtr].insert(std::get(req._args[0])); + } + }; + + return handler; +} + +void visitReq(const randomizer::logic::requirement::Requirement& req, + std::function f, + randomizer::logic::world::World* world); + +DNF evaluatePartialRequirement(BitIndex& bitIndex, + const randomizer::logic::requirement::Requirement& req, + FlattenSearch* search, + const int& formTime); diff --git a/mods/randomizer/generator/logic/flatten/simplify_algebraic.cpp b/mods/randomizer/generator/logic/flatten/simplify_algebraic.cpp new file mode 100644 index 0000000000..37ba38d0eb --- /dev/null +++ b/mods/randomizer/generator/logic/flatten/simplify_algebraic.cpp @@ -0,0 +1,424 @@ +#include "simplify_algebraic.hpp" +#include "../../utility/container.hpp" + +// Turns a bit-based DNF (a two-level sum-of-products) back into +// a readable multi-level requirement. +randomizer::logic::requirement::Requirement DNFToExpr(BitIndex& bitIndex, DNF dnf) +{ + if (dnf.isTriviallyFalse()) + { + return randomizer::logic::requirement::Requirement {randomizer::logic::requirement::Type::IMPOSSIBLE, {}}; + } + + if (dnf.isTriviallyTrue()) + { + return randomizer::logic::requirement::Requirement {randomizer::logic::requirement::Type::NOTHING, {}}; + } + + // really make sure no dupes exist, not sure if needed + dnf = dnf.dedup(); + + // Map to BitVectors. Since DNFs don't offer bit-level access, + // we have to manually go through every bit to build BitVectors. + // This is definitely not cheap but it probably saves more time + // than keeping the intsets around during search + std::vector expr = {}; + for (const auto& t : dnf.terms) + { + std::list bits = {}; + for (int bit = 0; bit < bitIndex.counter; bit++) + { + if (t.test(bit)) + { + bits.push_back(bit); + } + } + expr.emplace_back(bits); + } + + // at this point we must remove weaker requirements. E.g. + // imagine Beedle existed in this rando and an item required + // (Wallet x1 and Wallet x2) or (Wallet x1 and ExtraWallet x1 and ExtraWallet x2) + // then this code would pull out Wallet x1 first, resulting in + // Wallet x1 and (Wallet x2 or ExtraWallet x1 and ExtraWallet x2) which is not + // reasonable at all and at that point not even the TWWR-Tracker simplifications can save us + for (auto& term : expr) + { + for (const auto& bit : term.ints()) + { + auto& req = bitIndex.reverseIndex[bit]; + if (req._type == randomizer::logic::requirement::Type::COUNT) + { + auto count = std::get(req._args[0]); + auto item = std::get(req._args[1]); + for (int i = 1; i < count; i++) + { + auto lesserBit = bitIndex.reqBit( + randomizer::logic::requirement::Requirement {randomizer::logic::requirement::Type::COUNT, {i, item}}); + term.clear(lesserBit); + } + } + } + } + + auto commonFactors = expr[0].ints(); + for (const auto& term : expr) + { + std::set intersection = {}; + std::set_intersection(commonFactors.begin(), + commonFactors.end(), + term.intset.begin(), + term.intset.end(), + std::inserter(intersection, intersection.begin())); + commonFactors = intersection; + } + + // build a list of variables that appear in our expression, + // excluding common factors. + std::set varSet = {}; + for (auto& term : expr) + { + for (const auto& c : commonFactors) + { + term.clear(c); + } + for (const auto& b : term.ints()) + { + varSet.insert(b); + } + } + + std::vector variables = std::vector(varSet.begin(), varSet.end()); + + if (variables.empty()) + { + return createAnd(lookupRequirements(bitIndex, commonFactors)); + } + + std::vector seen = {}; + auto kernels = findKernels(expr, variables, BitVector(), seen); + kernels = randomizer::utility::container::FilterFromVector(kernels, [](const auto& k) { return !k.coKernel.isEmpty(); }); + + // columns are unique cubes in all kernels + std::vector columns = {}; + for (const auto& kernel : kernels) + { + for (const auto& kCube : kernel.kernel) + { + if (std::none_of(columns.begin(), columns.end(), [&](const auto& c) { return kCube.equals(c); })) + { + columns.push_back(kCube); + } + } + } + + // rows are unique co-kernels + auto& rows = kernels; + if (!rows.empty() && !columns.empty()) + { + std::vector> matrix = {}; + for (const auto& row : rows) + { + matrix.emplace_back(columns.size(), 0); + } + // create a matrix that is 1 where column cubes appear in row kernels. + // since kernels are the result of a single division (by the co-kernel), + // this essentially creates ones where division by another cube would be possible + for (int col = 0; col < columns.size(); col++) + { + auto& kCube = columns[col]; + for (int row = 0; row < rows.size(); row++) + { + auto& coKernel = rows[row]; + if (std::any_of(coKernel.kernel.begin(), coKernel.kernel.end(), [&](const auto& k) { return kCube.equals(k); })) + { + matrix[row][col] = 1; + } + } + } + + // Find the best rectangle. This optimizes for #literals saved + // in the resulting expression, which is a good heuristic for + // minimizing the length of the expression. + auto rowWeight = [&](const int& row) { return rows[row].coKernel.size() + 1; }; + auto colWeight = [&](const int& col) { return columns[col].size(); }; + + auto value = [&](const int& col, const int& row) + { + auto cpy = rows[row].coKernel; + cpy.or_(columns[col]); + return cpy.size(); + }; + + auto literalsSaved = [&](const std::tuple, std::vector>& rect) + { + auto [rectRows, rectCols] = rect; + int weight = 0; + for (const auto& row : rectRows) + { + for (const auto& col : rectCols) + { + if (matrix[row][col]) + { + weight += value(col, row); + } + } + } + + for (const auto& row : rectRows) + { + weight -= rowWeight(row); + } + for (const auto& col : rectCols) + { + weight -= colWeight(col); + } + + return weight; + }; + + std::vector, std::vector>> allRects = {}; + std::vector rows_; + std::vector cols_; + for (int i = 0; i < rows.size(); i++) + { + rows_.push_back(i); + } + for (int i = 0; i < columns.size(); i++) + { + cols_.push_back(i); + } + genRectangles(rows_, + cols_, + matrix, + [&](const std::vector& rows__, const std::vector& cols__) + { allRects.push_back({rows__, cols__}); }); + + if (!allRects.empty()) + { + auto& [bestRows, bestCols] = *std::max_element(allRects.begin(), + allRects.end(), + [&](const auto& rect1, const auto& rect2) + { return literalsSaved(rect1) < literalsSaved(rect2); }); + + // divisor is created by OR-ing column cubes + std::vector divisor = {}; + for (const auto& c : bestCols) + { + divisor.push_back(columns[c]); + } + auto [quot, remainder] = algebraicDivision(expr, divisor); + + // and re-assemble a Requirement that sort of looks like + // common_factors * (quotient * divisor + remainder) + auto product = randomizer::logic::requirement::Requirement(); + product._type = randomizer::logic::requirement::Type::AND; + std::vector> quotBits; + std::vector> divisorBits; + for (const auto& c : quot) + { + quotBits.push_back(c.bitset); + } + for (const auto& c : divisor) + { + divisorBits.push_back(c.bitset); + } + product._args.push_back(DNFToExpr(bitIndex, DNF(quotBits))); + product._args.push_back(DNFToExpr(bitIndex, DNF(divisorBits))); + + auto sum = randomizer::logic::requirement::Requirement(); + if (!remainder.empty()) + { + std::vector> remainderBits; + for (const auto& c : remainder) + { + remainderBits.push_back(c.bitset); + } + sum._type = randomizer::logic::requirement::Type::OR; + sum._args.push_back(product); + sum._args.push_back(DNFToExpr(bitIndex, DNF(remainderBits))); + } + else + { + sum = product; + } + auto terms = lookupRequirements(bitIndex, commonFactors); + terms.push_back(sum); + return createAnd(terms); + } + } + + // here we didn't do our complicated rectangle extraction, so just extract + // the common factors + auto terms = randomizer::logic::requirement::Requirement(); + terms._type = randomizer::logic::requirement::Type::OR; + for (const auto& c : expr) + { + terms._args.push_back(createAnd(lookupRequirements(bitIndex, c.ints()))); + } + + // common_factor1 AND common_factor2 AND ... AND (terms without common factors ORed) + auto finalTerms = lookupRequirements(bitIndex, commonFactors); + finalTerms.push_back(terms); + return createAnd(finalTerms); +} + +randomizer::logic::requirement::Requirement createAnd(std::vector terms) +{ + if (terms.size() > 1) + { + randomizer::logic::requirement::Requirement req; + req._type = randomizer::logic::requirement::Type::AND; + for (auto& term : terms) + { + req._args.push_back(term); + } + return req; + } + return terms[0]; +} + +// Recursively computes kernels and co-kernels of the expression `cubes`. +// A co-kernel is a cube (product term) such that for `expr / co-kernel = kernel`, +// `kernel` contains at least two terms but there's no factor to factor out. + +// This effectively tries every combination of variables in this expression +// as a co-kernel. seenCoKernels is a bit of book-keeping to not create +// duplicate kernels, and min_idx ensures we don't try e.g. ab and ba separately +std::vector findKernels(const std::vector& cubes, + const std::vector& variables, + const BitVector& coKernelPath, + std::vector& seenCoKernels, + int minIdx /* = 0 */) +{ + std::vector kernels = {}; + for (int idx = 0; idx < variables.size(); idx++) + { + auto& bit = variables[idx]; + // we won't find any useful kernels by trying these *again* + if (idx < minIdx) + { + continue; + } + + std::vector s = {}; + for (auto& c : cubes) + { + if (c.test(bit)) + { + s.push_back(c); + } + } + + if (s.size() >= 2) + { + auto co = s[0]; + for (const auto& c : s) + { + co.and_(c); + } + auto subPath = coKernelPath; + subPath.or_(co); + auto [quot, remainder] = algebraicDivision(cubes, {co}); + auto subKernels = findKernels(quot, variables, subPath, seenCoKernels, idx + 1); + + for (const auto& sub : subKernels) + { + if (std::none_of(seenCoKernels.begin(), + seenCoKernels.end(), + [=](const auto& seenCo) { return seenCo.equals(sub.coKernel); })) + { + seenCoKernels.push_back(sub.coKernel); + kernels.push_back(sub); + } + } + } + } + + // cube-free expr is always its own kernel, with trivial co-kernel 1 + if (std::none_of(seenCoKernels.begin(), + seenCoKernels.end(), + [=](const auto& seenCo) { return seenCo.equals(coKernelPath); })) + { + kernels.push_back(FoundKernel {cubes, coKernelPath}); + } + + return kernels; +} + +// Computes the algebraic division of expr / divisor, returning +// the quotient and the remainder. These satisfy the formula + +// expr = quotient * divisor + remainder +std::pair, std::vector> algebraicDivision(const std::vector& expr, + const std::vector& divisor) +{ + std::vector quot = {}; + // for every "cube"/product term in our divisor... + for (const auto& divCube : divisor) + { + // get a list of all cubes that this can be divided by + std::vector c = {}; + std::copy_if(expr.begin(), expr.end(), std::back_inserter(c), [=](const auto& e) { return divCube.isSubsetOf(e); }); + + if (c.empty()) + { + // division not possible, remainder is the entire expression + return {{}, expr}; + } + + // "cross out" the bits of this divisor cube + for (auto& ci : c) + { + for (const auto& bit : divCube.ints()) + { + ci.clear(bit); + } + } + + // compute the intersection of the divided expr with the divided expr in other cubes + if (quot.empty()) + { + quot = c; + } + else + { + // this is literally set intersection, NOT an OR or an AND + std::vector newQuot = {}; + std::copy_if(quot.begin(), + quot.end(), + std::back_inserter(newQuot), + [=](const auto& qc) + { return std::any_of(c.begin(), c.end(), [=](const auto& cc) { return cc.equals(qc); }); }); + quot = newQuot; + } + } + + // finally, compute the remainder essentially by computing + // remainder = expr - quotient * divisor + // * is AND + std::vector> quotBits = {}; + for (auto& i : quot) + { + quotBits.push_back(i.bitset); + } + std::vector> divisorBits = {}; + for (auto& i : divisor) + { + divisorBits.push_back(i.bitset); + } + DNF product = DNF(quotBits).and_(DNF(divisorBits)).dedup(); + + std::vector remainder = {}; + std::copy_if(expr.begin(), + expr.end(), + std::back_inserter(remainder), + [=](const auto& e) + { + return std::none_of(product.terms.begin(), + product.terms.end(), + [=](const auto& productTerm) { return includedIn(productTerm, e.bitset); }); + }); + + return {quot, remainder}; +} diff --git a/mods/randomizer/generator/logic/flatten/simplify_algebraic.hpp b/mods/randomizer/generator/logic/flatten/simplify_algebraic.hpp new file mode 100644 index 0000000000..eecc2db6a3 --- /dev/null +++ b/mods/randomizer/generator/logic/flatten/simplify_algebraic.hpp @@ -0,0 +1,186 @@ +// Algebraic simplification techniques treat all requirements as unrelated variables +// and allow us to turn our two-level DNF/sum-of-products form into a simpler +// multi-level expression. + +// The approach taken here is mostly used in hardware logic synthesis, and described in: + +// * https://faculty.sist.shanghaitech.edu.cn/faculty/zhoupq/Teaching/Spr16/07-Multi-Level-Logic-Synthesis.pdf +// * Some lecture slides about the topic. These make the concepts of kernels, +// algebraic division, and rectangles very accessible, but e.g. how to actually find rectangles is left open. +// * Rudell 1989, Logic Synthesis for VLSI Design +// https://www2.eecs.berkeley.edu/Pubs/TechRpts/1989/ERL-89-49.pdf (pp. 41-70) +// * Rudell's PhD thesis is where this stuff was originally researched. +// The pseudocode for generating prime rectangles is found there and quite useful. + +// This approach does not exploit boolean properties like x & !x = false or x | !x = true +// but logic doesn't need this since we only have positive terms. The other thing +// these techniques don't handle are "implies" relations like Beetle x 2 => Beetle x 1, +// so we may use different techniques for those. + +#pragma once + +#include "bits.hpp" +#include +#include +#include + +struct FoundKernel +{ + std::vector kernel; + BitVector coKernel; +}; + +randomizer::logic::requirement::Requirement DNFToExpr(BitIndex& bitIndex, DNF dnf); + +randomizer::logic::requirement::Requirement createAnd(std::vector terms); + +// Generates all prime rectangles in this matrix. A rectangle is a set of columns and rows +// such that for every row and column, matrix[row][colum] is not zero. A prime rectangle +// is a rectangle that is not included in any other rectangle. +template +void genRectangles(std::vector& rows, std::vector& cols, std::vector>& matrix, Func callback) +{ + // generate trivial prime rectangles first + // trivial rectangles are rectangles with only + // one row or one column + for (const auto& row : rows) + { + // Find the ones in this row + std::vector ones = {}; + std::copy_if(cols.begin(), cols.end(), std::back_inserter(ones), [=](const int& c) { return matrix[row][c]; }); + // if this row has ones and there's no other row that + // has ones in the same positions, this row is part of + // a trivial row prime rectangle + if (!ones.empty() and + std::none_of( + rows.begin(), + rows.end(), + [=](const int& r) + { return r != row && std::all_of(ones.begin(), ones.end(), [=](const int& c) { return matrix[r][c]; }); })) + { + callback({row}, ones); + } + } + + for (const auto& col : cols) + { + // Same as above + std::vector ones = {}; + std::copy_if(rows.begin(), rows.end(), std::back_inserter(ones), [=](const int& r) { return matrix[r][col]; }); + + if (!ones.empty() and + std::none_of( + rows.begin(), + rows.end(), + [=](const int& c) + { return c != col && std::all_of(ones.begin(), ones.end(), [=](const int& r) { return matrix[r][c]; }); })) + { + callback(ones, {col}); + } + } + + genRectanglesRecursive(rows, cols, matrix, 0, {}, {}, callback); +} + +// Recursively generates non-trivial prime rectangles based on the +// existing prime rectangle given by matrix and rect_cols. Rectangles generated +// by this function will have fewer rows but more columns than the passed rectangle. + +// Args: +// all_rows: A list of all row indices, for convenience. +// all_cols: A list of all column indices, for convenience. +// matrix: The matrix being searched for rectangles. +// index: Grow the rectangle starting from this column +// rect_rows: Rows of the prime rectangle. +// rect_cols: Columns of the prime rectangle. +// callback: Called for every prime rectangle. +template +void genRectanglesRecursive(std::vector& allRows, + std::vector& allCols, + std::vector>& matrix, + const int& index, + std::vector rectRows, + std::vector rectCols, + Func callback) +{ + // do not consider columns before the starting index, and require + // this column to have two or more ones (otherwise we'd generate a trivial rectangle) + for (const auto& c : allCols) + { + if (c >= index && std::count_if(allRows.begin(), allRows.end(), [=](const int& row) { return matrix[row][c]; }) >= 2) + { + // create submatrix, only keeping rows where the column has a one + // all other rows are zeroed + std::vector> m1 = {}; + for (int rowIdx = 0; rowIdx < matrix.size(); rowIdx++) + { + auto& row = matrix[rowIdx]; + m1.push_back(matrix[rowIdx][c] ? row : std::vector(row.size(), 0)); + } + + // create new rect rows based on this column. If we had an existing + // rectangle in the recursive case, this shrinks the rectangle, otherwise + // it creates the first rectangle + std::vector rect1Rows; + std::copy_if(allRows.begin(), + allRows.end(), + std::back_inserter(rect1Rows), + [=](const int& row) { return matrix[row][c]; }); + std::vector rect1Cols = rectCols; + + bool prune = false; + // add column c and all columns with EXACTLY the same number of ones + for (const auto& c1 : allCols) + { + if (std::count_if(allRows.begin(), allRows.end(), [=](const int& row) { return m1[row][c1]; }) == + std::count_if(allRows.begin(), allRows.end(), [=](const int& row) { return matrix[row][c]; })) + { + if (c1 < c) + { + // "if a column of 1's occurs for a column index less than + // the starting index, then all rectangles in the current + // submatrix have already been examined when that column + // was processed" (Rudell) + prune = true; + break; + } + else + { + // add the column to our rectangle + rect1Cols.push_back(c1); + for (const auto& row : allRows) + { + m1[row][c1] = 0; + } + } + } + } + + if (!prune) + { + callback(rect1Rows, rect1Cols); + genRectanglesRecursive(allRows, allCols, m1, c, rect1Rows, rect1Cols, callback); + } + } + } +} + +std::vector findKernels(const std::vector& cubes, + const std::vector& variables, + const BitVector& coKernelPath, + std::vector& seenCoKernels, + int minIdx = 0); + +std::pair, std::vector> algebraicDivision(const std::vector& expr, + const std::vector& divisor); + +template +std::vector lookupRequirements(BitIndex& bitIndex, Container r) +{ + std::vector reqs; + for (auto& bit : r) + { + reqs.push_back(bitIndex.reverseIndex[bit]); + } + return reqs; +} diff --git a/mods/randomizer/generator/logic/hints.cpp b/mods/randomizer/generator/logic/hints.cpp new file mode 100644 index 0000000000..c29a3f01de --- /dev/null +++ b/mods/randomizer/generator/logic/hints.cpp @@ -0,0 +1,136 @@ +#include "hints.hpp" + +#include "../utility/text.hpp" +#include "world.hpp" + +#include + +namespace randomizer::logic::hints { + + static const std::list> dungeonColors = { + {"Forest Temple", ""}, + {"Goron Mines", ""}, + {"Lakebed Temple", ""}, + {"Arbiters Grounds", ""}, + {"Snowpeak Ruins", ""}, + {"Temple of Time", ""}, + {"City in the Sky", ""}, + {"Palace of Twilight", ""}, + // {"Hyrule Castle", ""} + }; + + // Tell the player which dungeons are required on the sign in front of Link's House + static void GenerateRequiredDungeonsHint(world::WorldPool& worlds) { + for (const auto& world : worlds) { + auto& requiredDungeonText = world->AddNewText("Links House Sign"); + // Use dungeonColors to loop through in base game dungeon order + for (const auto& [dungeonName, color] : dungeonColors) { + auto dungeon = world->GetDungeon(dungeonName); + if (dungeon->IsRequired()) { + requiredDungeonText += color + getTextObject(dungeonName) + "\n"; + } + } + + if (requiredDungeonText.Empty()) { + requiredDungeonText += getTextObject("No Required Dungeons Text"); + } + } + } + + static void doItemTextReplacement(const std::unique_ptr& world, + const std::string& locationName, + const std::list& textNames, + Text::Color color) { + auto itemName = world->GetLocation(locationName)->GetCurrentItem()->GetName(); + auto itemStandardName = addColor(getTextObject(itemName), color, 1, true); + auto itemPrettyName = addColor(getTextObject(itemName, Text::PRETTY), color); + for (const auto& textName : textNames) { + auto& text = world->AddNewText(textName); + text = getTextObject(textName + " Template"); + text.Replace("", itemStandardName); + text.Replace("", itemPrettyName); + text.Capitalize(); + text.BreakLines(); + } + } + + static void GenerateItemTextReplacements(world::WorldPool& worlds) { + for (const auto& world : worlds) { + doItemTextReplacement(world, "Fishing Hole Bottle", {"Fishing Hole Sign Text"}, Text::GREEN); + doItemTextReplacement(world, "Charlo Donation Blessing", {"Charlo Donation Ask Text"}, Text::GREEN); + doItemTextReplacement(world, "Sera Shop Slingshot", {"Slingshot Shop Text", + "Slingshot Shop Too Expensive Text", "Slingshot Shop Purchase Confirmation Text", + "Slingshot Shop After Purchase Text"}, Text::ORANGE); + + doItemTextReplacement(world, "Barnes Bomb Bag", {"Barnes Special Offer Text"}, Text::ORANGE); + doItemTextReplacement(world, "Kakariko Village Malo Mart Wooden Shield", {"Kakariko Malo Mart Wooden Shield Purchase Confirmation Text", + "Kakariko Malo Mart Wooden Shield Too Expensive Text", "Kakariko Malo Mart Wooden Shield Text"}, Text::ORANGE); + + doItemTextReplacement(world, "Kakariko Village Malo Mart Hylian Shield", {"Kakariko Malo Mart Hylian Shield Purchase Confirmation Text", + "Kakariko Malo Mart Hylian Shield Too Expensive Text", "Kakariko Malo Mart Hylian Shield After Purchase Text", + "Kakariko Malo Mart Hylian Shield Text"}, Text::ORANGE); + + doItemTextReplacement(world, "Kakariko Village Malo Mart Red Potion", {"Kakariko Malo Mart Red Potion Too Expensive Text", + "Kakariko Malo Mart Red Potion Purchase Confirmation Text", "Kakariko Malo Mart Red Potion Text"}, Text::ORANGE); + + doItemTextReplacement(world, "Kakariko Village Malo Mart Hawkeye", {"Kakariko Malo Mart Hawkeye Purchase Confirmation Text", + "Kakariko Malo Mart Hawkeye Too Expensive Text", "Kakariko Malo Mart Hawkeye After Purchase Text", + "Kakariko Malo Mart Hawkeye Coming Soon Text", "Kakariko Malo Mart Hawkeye Text"}, Text::ORANGE); + + doItemTextReplacement(world, "Castle Town Malo Mart Magic Armor", {"Chudleys Shop Magic Armor Text", + "Castle Town Malo Mart Magic Armor After Purchase Text", "Castle Town Malo Mart Magic Armor Text", + "Castle Town Malo Mart Magic Armor Sold Out Text"}, Text::ORANGE); + + doItemTextReplacement(world, "Coro Bottle", {"Coro Bottle Offer 1 Text", + "Coro Bottle Offer 2 Text", "Coro Bottle Offer 3 Text", "Coro Bottle Offer 4 Text"}, Text::ORANGE); + } + } + + void GenerateMidnaHintsText(world::WorldPool& worlds) { + for (const auto& world : worlds) { + auto& midnaHintText = world->AddNewText("Custom Midna Call Hints Text"); + + // Put required dungeons on Midna. + // First loop through to get required number + int numRequiredDungeons = 0; + for (const auto& dungeon : world->GetDungeonTable() | std::views::values) { + if (dungeon->IsRequired()) { + ++numRequiredDungeons; + } + } + + // Set the text for the number of required dungeons + if (numRequiredDungeons > 0) { + midnaHintText += getTextObject("Midna Hints Required Dungeons Intro At Least One Dungeon"); + midnaHintText.Replace("", std::to_string(numRequiredDungeons)); + // Add newlines to begin listing dungeons on the next textbox + midnaHintText += "\n\n\n\n"; + + // Then loop through again to add the dungeon names. + // Use dungeonColors to loop through in base game dungeon order + int displayedRequiredDungeons = 0; + for (const auto& [dungeonName, color] : dungeonColors) { + auto dungeon = world->GetDungeon(dungeonName); + if (dungeon->IsRequired()) { + ++displayedRequiredDungeons; + midnaHintText += color + getTextObject(dungeonName) + ""; + // Add newline after every dungeon except the last one + if (displayedRequiredDungeons < numRequiredDungeons) { + midnaHintText += "\n"; + } + } + } + } else { + midnaHintText += getTextObject("Midna Hints Required Dungeons Intro Zero Dungeons"); + } + + midnaHintText.BreakLines(); + } + } + + void GenerateAllHints(world::WorldPool& worlds) { + GenerateRequiredDungeonsHint(worlds); + GenerateItemTextReplacements(worlds); + GenerateMidnaHintsText(worlds); + } +} diff --git a/mods/randomizer/generator/logic/hints.hpp b/mods/randomizer/generator/logic/hints.hpp new file mode 100644 index 0000000000..010bfec287 --- /dev/null +++ b/mods/randomizer/generator/logic/hints.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include "world.hpp" + +namespace randomizer::logic::world { +class World; +using WorldPool = std::vector>; +} + +namespace randomizer::logic::hints { + + void GenerateAllHints(world::WorldPool& worldPool); + +} \ No newline at end of file diff --git a/mods/randomizer/generator/logic/item.cpp b/mods/randomizer/generator/logic/item.cpp new file mode 100644 index 0000000000..d35fc4a63d --- /dev/null +++ b/mods/randomizer/generator/logic/item.cpp @@ -0,0 +1,166 @@ +#include "item.hpp" + +#include "world.hpp" + +namespace randomizer::logic::item +{ + + Importance ImportanceFromStr(const std::string& str) + { + const std::unordered_map importances = { + {"Major", Importance::MAJOR}, + {"Minor", Importance::MINOR}, + {"Junk", Importance::JUNK} + }; + + if (!importances.contains(str)) + { + return Importance::INVALID; + } + + return importances.at(str); + } + + Item::Item(const int& id, + const std::string& name, + world::World* world, + const Importance& importance, + const bool& gameWinningItem, + const bool& dungeonSmallKey, + const bool& bigKey, + const bool& compass, + const bool& dungeonMap): + _id(id), + _name(name), + _world(world), + _importance(importance), + _gameWinningItem(gameWinningItem), + _dungeonSmallKey(dungeonSmallKey), + _bigKey(bigKey), + _compass(compass), + _dungeonMap(dungeonMap) + { + if (name.starts_with("Male") || name.starts_with("Female")) + { + this->_goldenBug = true; + } + else if (name == "Shadow Crystal") + { + this->_shadowCrystal = true; + } + else if (name.starts_with("Bottle") || name == "Empty Bottle") + { + this->_bottle = true; + } + else if (name.starts_with("Stamp")) + { + this->_stamp = true; + } + // Make hearts major items if they're required for anything + else if ((name == "Piece of Heart" || name == "Heart Container") && + ((world->Setting("Hyrule Barrier Requirements") == "Hearts") || + (world->Setting("Hyrule Castle Big Key Requirements") == "Hearts"))) + { + this->_importance = Importance::MAJOR; + } + } + + int Item::GetID() const + { + return this->_id; + } + + std::string Item::GetName() const + { + return this->_name; + } + + world::World* Item::GetWorld() const + { + return this->_world; + } + + Importance Item::GetImportance() const + { + return this->_importance; + } + + bool Item::IsMajor() const + { + return this->_importance == Importance::MAJOR; + } + + bool Item::IsMinor() const + { + return this->_importance == Importance::MINOR; + } + + bool Item::isJunk() const + { + return this->_importance == Importance::JUNK; + } + + bool Item::IsGameWinningItem() const + { + return this->_gameWinningItem; + } + + std::list Item::GetChainLocations() const + { + return this->_chainLocations; + } + + bool Item::IsDungeonSmallKey() const + { + return this->_dungeonSmallKey; + } + + bool Item::IsBigKey() const + { + return this->_bigKey; + } + + bool Item::IsDungeonMap() const + { + return this->_dungeonMap; + } + + bool Item::IsCompass() const + { + return this->_compass; + } + + bool Item::IsGoldenBug() const + { + return this->_goldenBug; + } + + bool Item::IsShadowCrystal() const + { + return this->_shadowCrystal; + } + + bool Item::IsBottle() const + { + return this->_bottle; + } + + bool Item::IsStamp() const + { + return this->_stamp; + } + + bool Item::operator==(const Item& rhs) const + { + return this->_id == rhs._id && this->_world->GetID() == rhs._world->GetID(); + } + + bool Item::operator<(const Item& rhs) const + { + return (this->_world->GetID() == rhs._world->GetID()) ? this->_id < rhs._id + : this->_world->GetID() < rhs._world->GetID(); + } + + std::unique_ptr Nothing = + std::make_unique(-1, "Nothing", nullptr, Importance::JUNK, false, false, false, false, false); +} // namespace randomizer::logic::item diff --git a/mods/randomizer/generator/logic/item.hpp b/mods/randomizer/generator/logic/item.hpp new file mode 100644 index 0000000000..089e953431 --- /dev/null +++ b/mods/randomizer/generator/logic/item.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include + +namespace randomizer::logic::world +{ + class World; +}; +class Location; +namespace randomizer::logic::item +{ + enum Importance + { + INVALID, + MAJOR, + MINOR, + JUNK, + }; + + Importance ImportanceFromStr(const std::string& str); + + class Item + { + public: + Item() = default; + Item(const int& id, + const std::string& name, + world::World* world, + const Importance& importance, + const bool& gameWinningItem, + const bool& dungeonSmallKey, + const bool& bigKey, + const bool& compass, + const bool& dungeonMap); + + int GetID() const; + std::string GetName() const; + world::World* GetWorld() const; + Importance GetImportance() const; + bool IsMajor() const; + bool IsMinor() const; + bool isJunk() const; + bool IsGameWinningItem() const; + std::list GetChainLocations() const; + bool IsDungeonSmallKey() const; + bool IsBigKey() const; + bool IsDungeonMap() const; + bool IsCompass() const; + bool IsGoldenBug() const; + bool IsShadowCrystal() const; + bool IsBottle() const; + bool IsStamp() const; + + bool operator==(const Item& rhs) const; + bool operator<(const Item& rhs) const; + + private: + int _id = -1; + std::string _name; + world::World* _world = nullptr; + Importance _importance = INVALID; + bool _gameWinningItem = false; + std::list _chainLocations; + + bool _dungeonSmallKey = false; + bool _bigKey = false; + bool _dungeonMap = false; + bool _compass = false; + bool _goldenBug = false; + bool _bottle = false; + bool _shadowCrystal = false; + bool _stamp = false; + }; + + extern std::unique_ptr Nothing; +} // namespace randomizer::logic::item diff --git a/mods/randomizer/generator/logic/item_pool.cpp b/mods/randomizer/generator/logic/item_pool.cpp new file mode 100644 index 0000000000..498aaf5798 --- /dev/null +++ b/mods/randomizer/generator/logic/item_pool.cpp @@ -0,0 +1,480 @@ +#include "item_pool.hpp" + +#include "world.hpp" + +#include + +namespace randomizer::logic::item_pool +{ + + std::map minimalItemPool = { + {"Shadow Crystal", 1}, + {"Slingshot", 1}, + {"Lantern", 1}, + {"Gale Boomerang", 1}, + {"Iron Boots", 1}, + {"Bomb Bag", 1}, + {"Spinner", 1}, + {"Ball and Chain", 1}, + + {"Progressive Fishing Rod", 2}, + {"Progressive Sword", 4}, + {"Progressive Bow", 1}, + {"Progressive Clawshot", 2}, + {"Progressive Dominion Rod", 2}, + {"Progressive Wallet", 2}, + {"Progressive Sky Book", 7}, + + {"Aurus Memo", 1}, + {"Asheis Sketch", 1}, + {"Renados Letter", 1}, + {"Invoice", 1}, + {"Wooden Statue", 1}, + {"Ilias Charm", 1}, + {"Zora Armor", 1}, + {"Hylian Shield", 1}, + {"Ordon Shield", 1}, + {"Empty Bottle", 4}, + {"Progressive Hidden Skill", 1}, + {"Poe Soul", 60}, + + {"Progressive Fused Shadow", 3}, + {"Progressive Mirror Shard", 4}, + + // Golden Bugs + {"Male Ant", 1}, + {"Female Ant", 1}, + {"Male Beetle", 1}, + {"Female Beetle", 1}, + {"Male Pill Bug", 1}, + {"Female Pill Bug", 1}, + {"Male Phasmid", 1}, + {"Female Phasmid", 1}, + {"Male Grasshopper", 1}, + {"Female Grasshopper", 1}, + {"Male Stag Beetle", 1}, + {"Female Stag Beetle", 1}, + {"Male Butterfly", 1}, + {"Female Butterfly", 1}, + {"Male Ladybug", 1}, + {"Female Ladybug", 1}, + {"Male Mantis", 1}, + {"Female Mantis", 1}, + {"Male Dragonfly", 1}, + {"Female Dragonfly", 1}, + {"Male Dayfly", 1}, + {"Female Dayfly", 1}, + {"Male Snail", 1}, + {"Female Snail", 1}, + + // Keys + {"Gate Keys", 1}, + {"Gerudo Desert Bulblin Camp Key", 1}, + {"North Faron Woods Gate Key", 1}, + {"Forest Temple Small Key", 4}, + {"Goron Mines Small Key", 3}, + {"Lakebed Temple Small Key", 3}, + {"Arbiters Grounds Small Key", 5}, + {"Snowpeak Ruins Small Key", 4}, + {"Ordon Pumpkin", 1}, + {"Ordon Cheese", 1}, + {"Temple of Time Small Key", 3}, + {"City in the Sky Small Key", 1}, + {"Palace of Twilight Small Key", 7}, + {"Hyrule Castle Small Key", 3}, + + // Big Keys + {"Forest Temple Big Key", 1}, + {"Goron Mines Key Shard", 3}, + {"Lakebed Temple Big Key", 1}, + {"Arbiters Grounds Big Key", 1}, + {"Snowpeak Ruins Bedroom Key", 1}, + {"Temple of Time Big Key", 1}, + {"City in the Sky Big Key", 1}, + {"Palace of Twilight Big Key", 1}, + {"Hyrule Castle Big Key", 1}, + + // Maps and Compasses + {"Forest Temple Compass", 1}, + {"Goron Mines Compass", 1}, + {"Lakebed Temple Compass", 1}, + {"Arbiters Grounds Compass", 1}, + {"Snowpeak Ruins Compass", 1}, + {"Temple of Time Compass", 1}, + {"City in the Sky Compass", 1}, + {"Palace of Twilight Compass", 1}, + {"Hyrule Castle Compass", 1}, + {"Forest Temple Dungeon Map", 1}, + {"Goron Mines Dungeon Map", 1}, + {"Lakebed Temple Dungeon Map", 1}, + {"Arbiters Grounds Dungeon Map", 1}, + {"Snowpeak Ruins Dungeon Map", 1}, + {"Temple of Time Dungeon Map", 1}, + {"City in the Sky Dungeon Map", 1}, + {"Palace of Twilight Dungeon Map", 1}, + {"Hyrule Castle Dungeon Map", 1}, + + // Warp Portals + {"Ordon Spring Portal", 1}, + {"South Faron Portal", 1}, + {"North Faron Portal", 1}, + {"Kakariko Gorge Portal", 1}, + {"Kakariko Village Portal", 1}, + {"Death Mountain Portal", 1}, + {"Bridge of Eldin Portal", 1}, + {"Zoras Domain Portal", 1}, + {"Lake Hylia Portal", 1}, + {"Castle Town Portal", 1}, + {"Upper Zoras River Portal", 1}, + {"Snowpeak Portal", 1}, + {"Gerudo Desert Portal", 1}, + {"Mirror Chamber Portal", 1}, + + // Tears + {"Faron Twilight Tear", 16}, + {"Eldin Twilight Tear", 16}, + {"Lanayru Twilight Tear", 16}, + + // Junk we should always have + {"Purple Rupee Links House", 1}, + {"Green Rupee", 2}, + {"Orange Rupee", 50}, + {"Silver Rupee", 2}, + }; + + // This is intended to be added on top of the minimal item pool + std::map standardItemPool = { + {"Bomb Bag", 2}, + {"Progressive Bow", 2}, + {"Progressive Wallet", 1}, + {"Magic Armor", 1}, + {"Hawkeye", 1}, + {"Giant Bomb Bag", 1}, + {"Horse Call", 1}, + // {"Bottle with Half Milk", 1}, // Special bottles replace Empty Bottles after the fill algorithm is done + // {"Bottle with Lantern Oil", 1}, + // {"Bottle with Great Fairies Tears", 1}, + {"Progressive Hidden Skill", 6}, + + {"Heart Container", 8}, + {"Piece of Heart", 45}, + }; + + // This is intended to be added on top of the minimal and standard pools + std::map plentifulItemPool = { + {"Shadow Crystal", 1}, + {"Slingshot", 1}, + {"Lantern", 1}, + {"Gale Boomerang", 1}, + {"Iron Boots", 1}, + {"Bomb Bag", 1}, + {"Spinner", 1}, + {"Ball and Chain", 1}, + + {"Progressive Fishing Rod", 1}, + {"Progressive Sword", 4}, + {"Progressive Bow", 1}, + {"Progressive Clawshot", 1}, + {"Progressive Dominion Rod", 1}, + {"Progressive Wallet", 1}, + {"Progressive Sky Book", 1}, + + {"Aurus Memo", 1}, + {"Asheis Sketch", 1}, + // {"Renados Letter", 1}, Vanilla until flag issues are figured out + {"Zora Armor", 1}, + {"Magic Armor", 1}, + {"Hylian Shield", 1}, + {"Empty Bottle", 1}, + {"Progressive Hidden Skill", 1}, + + // Keys + {"Gate Keys", 1}, + {"Forest Temple Small Key", 1}, + {"Goron Mines Small Key", 1}, + {"Lakebed Temple Small Key", 1}, + {"Arbiters Grounds Small Key", 1}, + {"Snowpeak Ruins Small Key", 1}, + {"Ordon Pumpkin", 1}, + {"Ordon Cheese", 1}, + {"Temple of Time Small Key", 1}, + {"City in the Sky Small Key", 1}, + {"Palace of Twilight Small Key", 1}, + {"Hyrule Castle Small Key", 1}, + + // Big Keys + {"Forest Temple Big Key", 1}, + {"Goron Mines Key Shard", 1}, + {"Lakebed Temple Big Key", 1}, + {"Arbiters Grounds Big Key", 1}, + {"Snowpeak Ruins Bedroom Key", 1}, + {"Temple of Time Big Key", 1}, + {"City in the Sky Big Key", 1}, + {"Palace of Twilight Big Key", 1}, + {"Hyrule Castle Big Key", 1}, + }; + + std::map initialJunkPool = { + {"Bombs 5", 8}, + {"Bombs 10", 2}, + {"Bombs 20", 1}, + {"Bombs 30", 1}, + {"Arrows 10", 13}, + {"Arrows 20", 6}, + {"Arrows 30", 2}, + {"Seeds 50", 2}, + {"Water Bombs 5", 3}, + {"Water Bombs 10", 5}, + {"Water Bombs 15", 3}, + {"Bomblings 5", 2}, + {"Bomblings 10", 2}, + {"Blue Rupee", 1}, + {"Yellow Rupee", 6}, + {"Red Rupee", 6}, + {"Purple Rupee", 12}, + }; + + void GenerateItemPool(world::World* world) + { + auto itemPool = minimalItemPool; + + // Minimal item pool things + if (world->Setting("Item Scarcity") == "Minimal") + { + // If glitched logic, include magic armor + } + + // Add the vanilla item pool if necessary + if (world->Setting("Item Scarcity").IsAnyOf("Vanilla", "Plentiful")) + { + for (const auto& [itemName, count] : standardItemPool) + { + itemPool[itemName] += count; + } + } + + // Add the plentiful item pool if necessary + if (world->Setting("Item Scarcity") == "Plentiful") + { + for (const auto& [itemName, count] : plentifulItemPool) + { + itemPool[itemName] += count; + } + } + + // Remove the Tears for Twilight sections if they're cleared + if (world->Setting("Faron Twilight Cleared") == "On") { + itemPool.erase("Faron Twilight Tear"); + } + + if (world->Setting("Eldin Twilight Cleared") == "On") { + itemPool.erase("Eldin Twilight Tear"); + } + + if (world->Setting("Lanayru Twilight Cleared") == "On") { + itemPool.erase("Lanayru Twilight Tear"); + } + + // Remove items depending on Ilia Memory Quest setting + const auto& iliaQuest = world->Setting("Ilia Memory Quest"); + if (iliaQuest > "Letter") { + itemPool.erase("Renados Letter"); + } + if (iliaQuest > "Invoice") { + itemPool.erase("Invoice"); + } + if (iliaQuest > "Statue") { + itemPool.erase("Wooden Statue"); + } + + // Remove the North Faron Woods Gate Key if we're skipping prologue + if (world->Setting("Skip Prologue") == "On") + { + itemPool.erase("North Faron Woods Gate Key"); + } + + // Remove the bulblin camp key if we're skipping bulblin camp + if (world->Setting("Arbiters Does Not Require Bulblin Camp") == "On") + { + itemPool.erase("Gerudo Desert Bulblin Camp Key"); + } + + // Remove sky book characters if we're starting with the sky canon open + if (world->Setting("City Does Not Require Filled Skybook") == "On") + { + itemPool.erase("Progressive Sky Book"); + } + + // Remove Small Keys if we're playing without them + if (world->Setting("Small Keys") == "Keysy") + { + std::list smallKeys = { + {"Gate Keys"}, + {"Forest Temple Small Key"}, + {"Goron Mines Small Key"}, + {"Lakebed Temple Small Key"}, + {"Arbiters Grounds Small Key"}, + {"Snowpeak Ruins Small Key"}, + {"Ordon Pumpkin"}, + {"Ordon Cheese"}, + {"Temple of Time Small Key"}, + {"City in the Sky Small Key"}, + {"Palace of Twilight Small Key"}, + {"Hyrule Castle Small Key"}, + }; + for (const auto& key : smallKeys) + { + itemPool.erase(key); + } + } + + // Remove Big Keys if we're playing without them + if (world->Setting("Big Keys") == "Keysy") + { + std::list bigKeys = { + {"Forest Temple Big Key"}, + {"Goron Mines Key Shard"}, + {"Lakebed Temple Big Key"}, + {"Arbiters Grounds Big Key"}, + {"Snowpeak Ruins Bedroom Key"}, + {"Temple of Time Big Key"}, + {"City in the Sky Big Key"}, + {"Palace of Twilight Big Key"}, + }; + + if (world->Setting("Hyrule Castle Big Key Requirements") == "None") + { + bigKeys.emplace_back("Hyrule Castle Big Key"); + } + + for (const auto& key : bigKeys) + { + itemPool.erase(key); + } + } + + // Add items to the world's _itemPool + auto& worldItemPool = world->GetItemPool(); + for (const auto& [itemName, count] : itemPool) + { + auto item = world->GetItem(itemName); + for (auto i = 0; i < count; i++) + { + worldItemPool.push_back(item); + } + } + } + + void GenerateStartingItemPool(world::World* world) + { + auto startingItems = world->GetSettings().GetStartingInventory(); + auto& startingItemPool = world->GetStartingItemPool(); + auto& itemPool = world->GetItemPool(); + + // Add Maps and Compasses to starting items if we start with them + if (world->Setting("Maps and Compasses") == "Start With") + { + std::list mapsAndCompasses = { + {"Forest Temple Compass"}, + {"Goron Mines Compass"}, + {"Lakebed Temple Compass"}, + {"Arbiters Grounds Compass"}, + {"Snowpeak Ruins Compass"}, + {"Temple of Time Compass"}, + {"City in the Sky Compass"}, + {"Palace of Twilight Compass"}, + {"Hyrule Castle Compass"}, + {"Forest Temple Dungeon Map"}, + {"Goron Mines Dungeon Map"}, + {"Lakebed Temple Dungeon Map"}, + {"Arbiters Grounds Dungeon Map"}, + {"Snowpeak Ruins Dungeon Map"}, + {"Temple of Time Dungeon Map"}, + {"City in the Sky Dungeon Map"}, + {"Palace of Twilight Dungeon Map"}, + {"Hyrule Castle Dungeon Map"}, + }; + + for (const auto& itemName : mapsAndCompasses) + { + startingItems[itemName] = 1; + } + } + + // Handle warp portals + startingItems["Ordon Spring Portal"] = 1; + if (world->Setting("Faron Twilight Cleared") == "On") + { + startingItems["South Faron Portal"] = 1; + startingItems["North Faron Portal"] = 1; + } + + if (world->Setting("Eldin Twilight Cleared") == "On") + { + startingItems["Kakariko Gorge Portal"] = 1; + startingItems["Kakariko Village Portal"] = 1; + startingItems["Death Mountain Portal"] = 1; + } + + if (world->Setting("Lanayru Twilight Cleared") == "On") + { + startingItems["Zoras Domain Portal"] = 1; + startingItems["Lake Hylia Portal"] = 1; + startingItems["Castle Town Portal"] = 1; + } + + // Automatically give players the Mirror Chamber Portal if Mirror Chamber Access is closed + // and they aren't both randomizing and decoupling dungeon entrances. Otherwise, there's no + // way to access the chamber + if (world->Setting("Mirror Chamber Access") == "Closed" && + !(world->Setting("Randomize Dungeon Entrances") == "On" && world->Setting("Decouple Entrances") == "On")) + { + startingItems["Mirror Chamber Portal"] = 1; + } + + // Add each item to the world's _startingItemPool and erase it from the regular _itemPool + for (const auto& [itemName, count] : startingItems) + { + auto item = world->GetItem(itemName); + for (auto i = 0; i < count; i++) + { + startingItemPool.push_back(item); + } + utility::container::Erase(itemPool, item, count); + } + } + + std::map GetInitialJunkPool() + { + return initialJunkPool; + } + + ItemPool GetCompleteItemPool(const world::WorldPool& worlds) + { + ItemPool completeItemPool = {}; + for (const auto& world : worlds) + { + auto& worldItemPool = world->GetItemPool(); + std::ranges::copy(worldItemPool, std::back_inserter(completeItemPool)); + } + + return completeItemPool; + } + + const std::map& GetValidStartingInventoryItems() { + static std::map validStartingInventoryItems{}; + if (validStartingInventoryItems.empty()) { + validStartingInventoryItems = minimalItemPool; + for (const auto& [item, count] : standardItemPool) { + validStartingInventoryItems[item] += count; + } + + // Remove junk + validStartingInventoryItems.erase("Purple Rupee Links House"); + validStartingInventoryItems.erase("Green Rupee"); + validStartingInventoryItems.erase("Orange Rupee"); + validStartingInventoryItems.erase("Silver Rupee"); + } + return validStartingInventoryItems; + } +} // namespace randomizer::logic::item_pool diff --git a/mods/randomizer/generator/logic/item_pool.hpp b/mods/randomizer/generator/logic/item_pool.hpp new file mode 100644 index 0000000000..178693c91f --- /dev/null +++ b/mods/randomizer/generator/logic/item_pool.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include + +// Foward Declarations +namespace randomizer::logic::world +{ + class World; + using WorldPool = std::vector>; +} // namespace randomizer::logic::world + +namespace randomizer::logic::item +{ + class Item; +} + +namespace randomizer::logic::item_pool +{ + using ItemPool = std::vector; + + /** + * @brief Generates and sets the item pool of randomized items for a single world. + * + * @param world The world to generate the item pool for + */ + void GenerateItemPool(world::World* world); + + /** + * @brief Generates and sets the starting item pool for a single world. Starting items will be + * subtracted from the world's regular item pool, so be sure to call GenerateItemPool first + * + * @param world The world to generate the starting item pool for + */ + void GenerateStartingItemPool(world::World* world); + + std::map GetInitialJunkPool(); + + ItemPool GetCompleteItemPool(const world::WorldPool& worlds); + + const std::map& GetValidStartingInventoryItems(); +} // namespace randomizer::logic::item_pool diff --git a/mods/randomizer/generator/logic/location.cpp b/mods/randomizer/generator/logic/location.cpp new file mode 100644 index 0000000000..bed3de3dfe --- /dev/null +++ b/mods/randomizer/generator/logic/location.cpp @@ -0,0 +1,164 @@ +#include "location.hpp" + +#include "world.hpp" +#include "../utility/log.hpp" + +namespace randomizer::logic::location +{ + Location::Location(const int& id, + const std::string& name, + const std::unordered_set& categories, + world::World* world, + item::Item* originalItem, + const bool& goalLocation, + const std::string& hintPriority, + const YAML::Node& metadata): + _id(id), + _name(name), + _categories(categories), + _world(world), + _originalItem(originalItem), + _goalLocation(goalLocation), + _hintPriority(hintPriority), + _metadata(metadata) + { + this->_computedRequirement._type = requirement::Type::IMPOSSIBLE; + } + + int Location::GetID() const + { + return this->_id; + } + + std::string Location::GetName() const + { + return this->_name; + } + + world::World* Location::GetWorld() const + { + return this->_world; + } + + bool Location::IsGoalLocation() const + { + return this->_goalLocation; + } + + void Location::SetCurrentItem(item::Item* item) + { + LOG_TO_DEBUG("Placed " + item->GetName() + " at " + this->GetName()); + this->_currentItem = item; + } + + item::Item* Location::GetCurrentItem() const + { + return this->_currentItem; + } + + void Location::RemoveCurrentItem() + { + LOG_TO_DEBUG("Removed " + this->GetCurrentItem()->GetName() + " at " + this->GetName()); + this->_currentItem = item::Nothing.get(); + } + + bool Location::IsEmpty() const + { + return this->_currentItem == item::Nothing.get(); + } + + item::Item* Location::GetOriginalItem() const + { + return this->_originalItem; + } + + item::Item* Location::GetTrackedItem() const + { + return this->_trackedItem; + } + + void Location::SetKnownVanillaItem(const bool& hasKnownVanillaItem) + { + this->_hasKnownVanillaItem = hasKnownVanillaItem; + } + + bool Location::HasKnownVanillaItem() const + { + return this->_hasKnownVanillaItem; + } + + void Location::SetProgression(const bool& progression) + { + this->_progression = progression; + LOG_TO_DEBUG(this->GetName() + " progression status set to " + (progression ? " true" : "false")); + } + + bool Location::IsProgression() const + { + return this->_progression; + } + + void Location::SetHinted(const bool& hinted) + { + this->_hinted = hinted; + } + + bool Location::IsHinted() const + { + return this->_hinted; + } + + const YAML::Node& Location::GetMetadata() const + { + return this->_metadata; + } + + void Location::AddLocationAccess(area::LocationAccess* locAcc) + { + this->_locationAccessList.push_back(locAcc); + } + + std::list Location::GetAccessList() const + { + return this->_locationAccessList; + } + + void Location::AddForbiddenItem(item::Item* forbiddenItem) + { + this->_forbiddenItems.insert(forbiddenItem); + LOG_TO_DEBUG(forbiddenItem->GetName() + " is forbidden from being placed at " + this->GetName()); + } + + const std::unordered_set& Location::GetForbiddenItems() + { + return this->_forbiddenItems; + } + + void Location::SetComputedRequirement(const requirement::Requirement& computedRequirement) + { + this->_computedRequirement = computedRequirement; + } + + requirement::Requirement Location::GetComputedRequirement() + { + return this->_computedRequirement; + } + + void Location::SetRegisteredLocationCategories(std::unordered_set* registeredLocationCategories) + { + this->_registeredLocationCategories = registeredLocationCategories; + } + + const std::set& GetAllRandomizerLocationNames() { + static std::set locationNames{}; + + if (locationNames.empty()) { + auto locationDataTree = LOAD_EMBED_YAML(RANDO_DATA_PATH "locations.yaml"); + for (const auto& locationNode : locationDataTree) { + locationNames.insert(locationNode["Name"].as()); + } + } + + return locationNames; + } +} // namespace randomizer::logic::location diff --git a/mods/randomizer/generator/logic/location.hpp b/mods/randomizer/generator/logic/location.hpp new file mode 100644 index 0000000000..62f469d622 --- /dev/null +++ b/mods/randomizer/generator/logic/location.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include "item.hpp" +#include "requirement.hpp" +#include "../utility/yaml.hpp" + +#include +#include +#include + +namespace randomizer::logic::world +{ + class World; +} + +namespace randomizer::logic::area +{ + class LocationAccess; +} + +namespace randomizer::logic::location +{ + class Location + { + public: + Location(const int& id, + const std::string& name, + const std::unordered_set& categories, + world::World* world, + item::Item* originalItem, + const bool& goalLocation, + const std::string& hintPriority, + const YAML::Node& metadata); + + int GetID() const; + std::string GetName() const; + world::World* GetWorld() const; + bool IsGoalLocation() const; + void SetCurrentItem(item::Item* currentItem); + item::Item* GetCurrentItem() const; + void RemoveCurrentItem(); + bool IsEmpty() const; + item::Item* GetOriginalItem() const; + item::Item* GetTrackedItem() const; + void SetKnownVanillaItem(const bool& hasKnownVanillaItem); + bool HasKnownVanillaItem() const; + void SetProgression(const bool& progression); + bool IsProgression() const; + void SetHinted(const bool& hinted); + bool IsHinted() const; + const YAML::Node& GetMetadata() const; + void AddLocationAccess(area::LocationAccess* locAcc); + std::list GetAccessList() const; + void AddForbiddenItem(item::Item* forbiddenItem); + const std::unordered_set& GetForbiddenItems(); + void SetComputedRequirement(const requirement::Requirement& computedRequirement); + requirement::Requirement GetComputedRequirement(); + void SetRegisteredLocationCategories(std::unordered_set* registeredLocationCategories); + + /** + * @brief Checks to see if the location has all the passed in categories. If a passed in category was never registered, + * a std::runtime_error will be thrown. + * @param categoryNames parameter pack of string representations of category names + * @returns true if all passed in categories are present, false otherwise + */ + template + bool HasCategories(Types... categoryNames) const + { + for (const auto& categoryName : {categoryNames...}) + { + if (this->_registeredLocationCategories != nullptr && + !this->_registeredLocationCategories->contains(categoryName)) + { + throw std::runtime_error(std::string("Category \"") + categoryName + "\" is not used by any locations"); + } + if (!this->_categories.contains(categoryName)) + { + return false; + } + } + + return true; + } + + private: + int _id = -1; + std::string _name = ""; + std::unordered_set _categories = {}; + world::World* _world; + item::Item* _originalItem = item::Nothing.get(); + bool _goalLocation = false; + item::Item* _currentItem = item::Nothing.get(); + bool _hasKnownVanillaItem = false; + std::list _locationAccessList = {}; + bool _progression = true; // Set as false later if applicable + bool _hinted = false; + std::string _hintPriority = "Never"; + std::unordered_set _forbiddenItems = {}; + requirement::Requirement _computedRequirement; + YAML::Node _metadata{}; + /** + * @brief _registeredLocationCategories is the set of all categories that are processed after reading locations.yaml. + * This structure is held in the World class and every location in that world has a pointer to it. + * We can't call it from the world directly since the function we want to use it in is templated in this class. + */ + std::unordered_set* _registeredLocationCategories = nullptr; + + // Potential tracker stuff + item::Item* _trackedItem = item::Nothing.get(); + }; + + using LocationPool = std::vector; + + /** + * + * @return A set of all randomizer location names + */ + const std::set& GetAllRandomizerLocationNames(); +} // namespace randomizer::logic::location diff --git a/mods/randomizer/generator/logic/plandomizer.cpp b/mods/randomizer/generator/logic/plandomizer.cpp new file mode 100644 index 0000000000..a0cd43cee4 --- /dev/null +++ b/mods/randomizer/generator/logic/plandomizer.cpp @@ -0,0 +1,111 @@ +#include "plandomizer.hpp" + +#include "world.hpp" + +#include "../utility/yaml.hpp" +#include "../utility/file.hpp" + +namespace randomizer::logic::plandomizer +{ + void LoadPlandomizerData(world::WorldPool& worlds, const fspath& filepath, const bool& ignoreErrors /*false*/) + { + // Verify the file exists before trying to open it + utility::file::Verify(filepath); + + auto plandoTree = LoadYAML(filepath); + + for (auto& world : worlds) + { + int worldId = world->GetID(); + std::string worldStr = "World " + std::to_string(world->GetID()); + if (plandoTree[worldStr]) + { + const auto& worldNode = plandoTree[worldStr]; + if (worldNode["Locations"]) + { + const auto& locations = worldNode["Locations"]; + if (!locations.IsMap()) + { + if (ignoreErrors) + { + return; + } + + throw std::runtime_error("Locations for " + worldStr + + " is not a map. Please check your plandomizer file syntax."); + } + + for (const auto& locationNode : locations) + { + // If the location object has children instead of a value, then parse the item name and potential + // world id from those children. If no world id is given, the current world will be used. + std::string itemName; + if (locationNode.second.IsMap()) + { + if (locationNode.second["Item"]) + { + itemName = locationNode.second["Item"].as(); + } + else + { + throw std::runtime_error("Missing key \"item\" in node:\n" + + YAML::Dump(locationNode)); + } + + if (locationNode.second["World"]) + { + worldId = locationNode.second["World"].as(); + if (worldId < 1 || worldId > worlds.size()) + { + std::string errorMsg = "Bad World ID \"" + std::to_string(worldId) + + "\"\nOnly " + std::to_string(worlds.size()) + + " worlds are being generated."; + throw std::runtime_error(errorMsg); + } + } + } + // Otherwise treat the value as an item for the same world as the location + else + { + itemName = locationNode.second.as(); + } + + const std::string locationName = locationNode.first.as(); + auto location = world->GetLocation(locationName); + + auto& itemWorld = worlds.at(worldId - 1); + auto item = itemWorld->GetItem(itemName); + + world->AddPlandomizedLocation(location, item); + } + } + + if (worldNode["Entrances"]) + { + const auto& entrances = worldNode["Entrances"]; + if (!entrances.IsMap()) + { + if (ignoreErrors) + { + return; + } + + throw std::runtime_error("Plandomizer file entrances for " + worldStr + + " is not a map. Please check your syntax before trying again."); + } + + for (const auto& entranceNode : entrances) + { + auto entranceName = entranceNode.first.as(); + auto targetName = entranceNode.second.as(); + + auto entrance = world->GetEntrance(entranceName); + auto target = world->GetEntrance(targetName); + + world->AddPlandomizedEntrance(entrance, target); + } + } + } + } + } +} // namespace randomizer::logic::plandomizer diff --git a/mods/randomizer/generator/logic/plandomizer.hpp b/mods/randomizer/generator/logic/plandomizer.hpp new file mode 100644 index 0000000000..5436d2c25b --- /dev/null +++ b/mods/randomizer/generator/logic/plandomizer.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +using fspath = std::filesystem::path; + +// Forward Declarations +namespace randomizer::logic::world +{ + class World; + using WorldPool = std::vector>; +} // namespace randomizer::logic::world + +namespace randomizer::logic::plandomizer +{ + void LoadPlandomizerData(world::WorldPool& worlds, const fspath& filepath, const bool& ignoreErrors = false); +} diff --git a/mods/randomizer/generator/logic/requirement.cpp b/mods/randomizer/generator/logic/requirement.cpp new file mode 100644 index 0000000000..a567a02f74 --- /dev/null +++ b/mods/randomizer/generator/logic/requirement.cpp @@ -0,0 +1,788 @@ +#include "requirement.hpp" + +#include "search.hpp" +#include "world.hpp" +#include "../utility/container.hpp" +#include "../utility/log.hpp" +#include "../utility/string.hpp" + +#include +#include + +namespace randomizer::logic::requirement +{ + namespace FormTime + { + const std::vector ALL_FORM_TIMES = {HUMAN_DAY, HUMAN_NIGHT, WOLF_DAY, WOLF_NIGHT}; + const std::vector ALL_FORM_TIMES_AND_TWILIGHT = {HUMAN_DAY, HUMAN_NIGHT, WOLF_DAY, WOLF_NIGHT, TWILIGHT}; + const std::vector ALL_FORM_AND_DAY_TIMES = {HUMAN_DAY, HUMAN_NIGHT, WOLF_DAY, WOLF_NIGHT, DAY, NIGHT}; + + std::string to_string(const int& formTime) + { + std::string formTimeStr = ""; + if (formTime & HUMAN_DAY) + formTimeStr += " Human_Day"; + if (formTime & HUMAN_NIGHT) + formTimeStr += " Human_Night"; + if (formTime & WOLF_DAY) + formTimeStr += " Wolf_Day"; + if (formTime & WOLF_NIGHT) + formTimeStr += " Wolf_Night"; + if (formTime & TWILIGHT) + formTimeStr += " Twilight"; + return formTimeStr; + } + } // namespace FormTime + + const extern Requirement NO_REQUIREMENT = Requirement{Type::NOTHING, {}}; + const extern Requirement IMPOSSIBLE_REQUIREMENT = Requirement{Type::IMPOSSIBLE, {}}; + + std::string Requirement::to_string() const + { + std::string reqStr = ""; + item::Item* item; + Requirement nestedReq; + int count; + int eventIndex; + int macroIndex; + switch (this->_type) + { + case Type::NOTHING: + return "Nothing"; + + case Type::IMPOSSIBLE: + return "Impossible (Please discover an entrance first)"; + + case Type::OR: + for (const auto& arg : this->_args) + { + nestedReq = std::get(arg); + if (nestedReq._type == Type::AND || nestedReq._type == Type::OR) + { + reqStr += "("; + reqStr += nestedReq.to_string(); + reqStr += ")"; + } + else + { + reqStr += nestedReq.to_string(); + } + reqStr += " or "; + } + // pop off the last " or " + for (auto i = 0; i < 4; i++) + { + reqStr.pop_back(); + } + return reqStr; + + case Type::AND: + for (const auto& arg : this->_args) + { + nestedReq = std::get(arg); + if (nestedReq._type == Type::AND || nestedReq._type == Type::OR) + { + reqStr += "("; + reqStr += nestedReq.to_string(); + reqStr += ")"; + } + else + { + reqStr += nestedReq.to_string(); + } + reqStr += " and "; + } + // pop off the last " and " + for (auto i = 0; i < 5; i++) + { + reqStr.pop_back(); + } + return reqStr; + + case Type::ITEM: + item = std::get(this->_args[0]); + return item->GetName(); + + case Type::COUNT: + count = std::get(this->_args[0]); + item = std::get(this->_args[1]); + return "count(" + item->GetName() + ", " + std::to_string(count) + ")"; + + case Type::EVENT: + eventIndex = std::get(this->_args[0]); + return "'Event_" + std::to_string(eventIndex) + "'"; + + case Type::MACRO: + macroIndex = std::get(this->_args[0]); + return "'Macro_" + std::to_string(macroIndex) + "'"; + + case Type::DAY: + return "Day"; + + case Type::NIGHT: + return "Night"; + + case Type::HUMAN_LINK: + return "Human Link"; + + case Type::WOLF_LINK: + return "Wolf Link"; + + case Type::TWILIGHT: + return "Twilight"; + + case Type::GOLDEN_BUGS: + count = std::get(this->_args[0]); + return "golden_bugs(" + std::to_string(count) + ")"; + + case Type::HEARTS: + count = std::get(this->_args[0]); + return "hearts(" + std::to_string(count) + ")"; + + case Type::DUNGEONS_COMPLETED: + count = std::get(this->_args[0]); + return "dungeons_completed(" + std::to_string(count) + ")"; + + default: + return reqStr; + } + return reqStr; + } + + Requirement ParseRequirementString(const std::string& reqStr, + world::World* world, + const bool& forceLogic /* = false */) + { + if (world->Setting("Logic Rules") == "No Logic" && !forceLogic) { + return NO_REQUIREMENT; + } + + Requirement req; + std::string logicStr(reqStr); + // First, we make sure that the expression has no missing or extra parenthesis + // and that the nesting level at the beginning is the same at the end. + // + // Logic expressions are split up via spaces, but we only want to evaluate the parts of + // the expression at the highest nesting level for the string that was passed in. + // (We'll recursively call the function later to evaluate deeper levels.) So we replace + // all the spaces on the highest nesting level with an arbitrarily chosen delimeter that shouldn't appear anywhere + // in a logic statement (in req case: '+'). + int nestingLevel = 1; + constexpr char delimeter = '+'; + for (auto& ch : logicStr) + { + if (ch == '(') + { + nestingLevel++; + } + else if (ch == ')') + { + nestingLevel--; + } + + if (nestingLevel == 1 && ch == ' ') + { + ch = delimeter; + } + } + + // If the nesting level isn't the same as what we started with, then the logic + // expression is invalid. + if (nestingLevel != 1) + { + throw std::runtime_error("Extra or missing parenthesis within expression: \"" + reqStr + "\""); + } + + // Next we split up the expression by the delimeter in the previous step + size_t pos = 0; + std::vector splitLogicStr = {}; + while ((pos = logicStr.find(delimeter)) != std::string::npos) + { + // When parsing setting checks, take the entire expression + // and the three components individually + auto& chBefore = logicStr[pos - 1]; + auto& chAfter = logicStr[pos + 1]; + if (chBefore != '!' && chAfter != '!' && chBefore != '=' && chAfter != '=' && + chBefore != '>' && chAfter != '>' && chBefore != '<' && chAfter != '<') + { + splitLogicStr.push_back(logicStr.substr(0, pos)); + logicStr.erase(0, pos + 1); + } + else + { + logicStr.erase(logicStr.begin() + pos); + } + } + splitLogicStr.push_back(logicStr); + + // Once we have the different parts of our expression, we can use the number + // of parts we have to determine what kind of expression it is. + + // If we only have one part... + if (splitLogicStr.size() == 1) + { + std::string argStr = splitLogicStr[0]; + std::ranges::replace(argStr, '_', ' '); + // First, see if we have nothing + if (argStr == "Nothing") + { + req._type = Type::NOTHING; + return req; + } + + // Then Human Link... + if (argStr == "Human Link") + { + req._type = Type::HUMAN_LINK; + return req; + } + + // Then Wolf Link... + if (argStr == "Wolf Link") + { + req._type = Type::WOLF_LINK; + return req; + } + + // Then Twilight... + if (argStr == "Twilight") + { + req._type = Type::TWILIGHT; + return req; + } + + // Then an event... + if (argStr[0] == '\'') + { + req._type = Type::EVENT; + std::string eventName(argStr.begin() + 1, argStr.end() - 1); // Remove quotes + int eventId = world->GetEventIndex(eventName); + + req._args.emplace_back(eventId); + return req; + } + + // NOTE: Checking macros *MUST* come before checking items. Some macros use the exact same name as an item + // and we want the macro to be used in req case instead of just the item + + // Then a macro... + if (world->GetMacroIndex(argStr) != -1) + { + req._type = Type::MACRO; + req._args.emplace_back(world->GetMacroIndex(argStr)); + return req; + } + + // Then an item... + if (world->GetItem(argStr, true) != nullptr) + { + auto item = world->GetItem(argStr); + req._type = Type::ITEM; + req._args.emplace_back(item); + return req; + } + + // Then a setting... + else if (utility::str::Contains(argStr, "!=", "==", ">=", "<=")) + { + bool equalComparison = utility::str::Contains(argStr, "=="); + bool notEqualComparison = utility::str::Contains(argStr, "!="); + bool gteComparison = utility::str::Contains(argStr, ">="); + bool lteComparison = utility::str::Contains(argStr, "<="); + + // Split up the comparison using the second comparison character (which will always be '=') + auto compPos = argStr.rfind('='); + std::string optionName(argStr.begin() + (compPos + 1), argStr.end()); + std::string settingName(argStr.begin(), argStr.begin() + (compPos - 1)); + + // Check using the appropriate comparison function + bool result = false; + if (equalComparison) + { + result = world->Setting(settingName) == optionName.c_str(); + } + else if (notEqualComparison) + { + result = world->Setting(settingName) != optionName.c_str(); + } + else if (gteComparison) + { + result = world->Setting(settingName) >= optionName.c_str(); + } + else if (lteComparison) + { + result = world->Setting(settingName) <= optionName.c_str(); + } + + if (result == true) + { + req._type = Type::NOTHING; + } + else + { + req._type = Type::IMPOSSIBLE; + } + return req; + } + // Then a count... + else if (argStr.find("count") != std::string::npos) + { + req._type = Type::COUNT; + // Since a count has two arguments (a number and an item), we have + // to split up the string in the parenthesis into those arguments. + + // Get rid of parenthesis + std::string countArgs(argStr.begin() + argStr.find('(') + 1, argStr.end() - 1); + // Erase any spaces + // countArgs.erase(std::remove(countArgs.begin(), countArgs.end(), ' '), countArgs.end()); + + // Split up the arguments + pos = 0; + splitLogicStr = {}; + while ((pos = countArgs.find(", ")) != std::string::npos) + { + splitLogicStr.push_back(countArgs.substr(0, pos)); + countArgs.erase(0, pos + 2); + } + splitLogicStr.push_back(countArgs); + + // For the count, if a setting is passed in, use the setting's value instead + auto& countStr = splitLogicStr[1]; + if (seedgen::settings::GetAllSettingsInfo()->contains(countStr)) + { + countStr = world->Setting(countStr).GetCurrentOption(); + } + + // Get the arguments + auto& itemName = splitLogicStr[0]; + int count = std::stoi(countStr); + auto item = world->GetItem(itemName); + req._args.emplace_back(count); + req._args.emplace_back(item); + return req; + } + + // Then Day... + if (argStr == "Day") + { + req._type = Type::DAY; + return req; + } + + // Then Night... + if (argStr == "Night") + { + req._type = Type::NIGHT; + return req; + } + + // Then health + else if (argStr.find("hearts") != std::string::npos) + { + req._type = Type::HEARTS; + std::string numHeartsStr(argStr.begin() + argStr.find('(') + 1, argStr.end() - 1); + + // If the string for the count is a setting, use the settings current option instead + if (seedgen::settings::GetAllSettingsInfo()->contains(numHeartsStr)) + { + numHeartsStr = world->Setting(numHeartsStr).GetCurrentOption(); + } + + int numHearts = std::stoi(numHeartsStr); + req._args.emplace_back(numHearts); + return req; + } + + // Then Impossible... + else if (argStr == "Impossible") + { + req._type = Type::IMPOSSIBLE; + return req; + } + + // Then golden bugs... + else if (argStr.find("golden bugs") != std::string::npos) + { + req._type = Type::GOLDEN_BUGS; + // Get rid of parenthesis + std::string countArg(argStr.begin() + argStr.find('(') + 1, argStr.end() - 1); + int count = std::stoi(countArg); + req._args.emplace_back(count); + return req; + } + + // Then dungeons completed + else if (argStr.find("dungeons completed") != std::string::npos) + { + req._type = Type::DUNGEONS_COMPLETED; + // Get rid of parenthesis + std::string countStr(argStr.begin() + argStr.find('(') + 1, argStr.end() - 1); + + // For the count, if a setting is passed in, use the setting's value instead + if (seedgen::settings::GetAllSettingsInfo()->contains(countStr)) + { + countStr = world->Setting(countStr).GetCurrentOption(); + } + + int count = std::stoi(countStr); + req._args.emplace_back(count); + return req; + } + + throw std::runtime_error("Unrecognized logic symbol: \"" + reqStr + "\""); + } + + // If our expression has two parts, then we don't know what that is + if (splitLogicStr.size() == 2) + { + throw std::runtime_error("Unrecognized 2 part expression: " + reqStr); + } + + // If we have more than two parts to our expression, then we have either "and" + // or "or". + bool andType = randomizer::utility::container::ElementInContainer(splitLogicStr, "and"); + bool orType = randomizer::utility::container::ElementInContainer(splitLogicStr, "or"); + + // If we have both of them, there's a problem with the logic expression + if (andType && orType) + { + throw std::runtime_error("\"and\" & \"or\" in same nesting level when parsing \"" + reqStr + "\""); + } + + if (andType || orType) + { + // Set the appropriate type + if (andType) + { + req._type = Type::AND; + } + else + { + req._type = Type::OR; + } + + // Once we know the type, we can erase the "and"s or "or"s and are left with just the deeper + // expressions to be logically operated on. + randomizer::utility::container::FilterAndEraseFromVector(splitLogicStr, + [](const std::string& arg) + { return arg == "and" || arg == "or"; }); + + // For each deeper expression, parse it and add it as an argument to the + // Requirement + for (auto& newReqStr : splitLogicStr) + { + // Get rid of parenthesis surrounding each deeper expression + if (newReqStr[0] == '(') + { + newReqStr = newReqStr.substr(1, newReqStr.length() - 2); + } + req._args.push_back(ParseRequirementString(newReqStr, world, forceLogic)); + } + } + + if (req._type != Type::INVALID) + { + return req; + } + // If we've reached req point, we weren't able to determine a logical operator within the expression + throw std::runtime_error("Could not determine logical operator type from expression: \"" + reqStr + "\""); + } + + bool EvaluateSimpleRequirement(const Requirement& req, world::World* world) + { + item::Item* item; + item::Item* heartPiece; + item::Item* heartContainer; + int count; + int macroIndex; + switch (req._type) + { + case Type::NOTHING: + return true; + + case Type::IMPOSSIBLE: + return false; + + case Type::OR: + return std::ranges::any_of( + req._args, + [&](const auto& arg) + { return EvaluateSimpleRequirement(std::get(arg), world); }); + + case Type::AND: + return std::ranges::all_of( + req._args, + [&](const auto& arg) + { return EvaluateSimpleRequirement(std::get(arg), world); }); + + case Type::ITEM: + item = std::get(req._args[0]); + return randomizer::utility::container::ElementInContainer(world->GetStartingItemPool(), item); + + case Type::COUNT: + count = std::get(req._args[0]); + item = std::get(req._args[1]); + return std::ranges::count(world->GetStartingItemPool(), item) >= count; + + case Type::MACRO: + macroIndex = std::get(req._args[0]); + return EvaluateSimpleRequirement(world->GetMacro(macroIndex), world); + + case Type::GOLDEN_BUGS: + count = std::get(req._args[0]); + return std::ranges::count_if(world->GetStartingItemPool(), + [](const auto& item) { return item->IsGoldenBug(); }) >= count; + + case Type::HEARTS: + count = std::get(req._args[0]); + heartPiece = world->GetItem("Piece of Heart"); + heartContainer = world->GetItem("Heart Container"); + return std::ranges::count(world->GetStartingItemPool(), heartPiece) + + std::ranges::count(world->GetStartingItemPool(), heartContainer) * 5 >= count * 5; + default: + return false; + } + } + + bool EvaluateRequirementAtFormTime(const Requirement& req, + search::Search* search, + const int& formTime, + world::World* world) + { + item::Item* item; + item::Item* heartPiece; + item::Item* heartContainer; + int count; + int eventIndex; + int macroIndex; + switch (req._type) + { + case Type::NOTHING: + return true; + + case Type::IMPOSSIBLE: + return false; + + case Type::OR: + return std::ranges::any_of( + req._args, + [&](const auto& arg) + { return EvaluateRequirementAtFormTime(std::get(arg), search, formTime, world); }); + + case Type::AND: + return std::ranges::all_of( + req._args, + [&](const auto& arg) + { return EvaluateRequirementAtFormTime(std::get(arg), search, formTime, world); }); + + case Type::ITEM: + item = std::get(req._args[0]); + return search->_ownedItems.contains(item); + + case Type::COUNT: + count = std::get(req._args[0]); + item = std::get(req._args[1]); + return search->_ownedItems.count(item) >= count; + + case Type::EVENT: + eventIndex = std::get(req._args[0]); + return search->_ownedEvents.contains(eventIndex); + + case Type::MACRO: + macroIndex = std::get(req._args[0]); + return EvaluateRequirementAtFormTime(world->GetMacro(macroIndex), search, formTime, world); + + case Type::DAY: + return formTime & FormTime::DAY; + + case Type::NIGHT: + return formTime & FormTime::NIGHT; + + case Type::HUMAN_LINK: + return formTime & FormTime::HUMAN; + + case Type::WOLF_LINK: + return formTime & FormTime::WOLF; + + case Type::TWILIGHT: + return formTime & FormTime::TWILIGHT; + + case Type::GOLDEN_BUGS: + count = std::get(req._args[0]); + return std::ranges::count_if(search->_ownedItems, + [](const auto& ownedItem) { return ownedItem->IsGoldenBug(); }) >= count; + + case Type::HEARTS: + count = std::get(req._args[0]); + heartPiece = world->GetItem("Piece of Heart"); + heartContainer = world->GetItem("Heart Container"); + return search->_ownedItems.count(heartPiece) + + (search->_ownedItems.count(heartContainer) + 3) * 5 >= count * 5; + + case Type::DUNGEONS_COMPLETED: + count = std::get(req._args[0]); + return std::ranges::count_if(search->_ownedEvents, [&](const int eventId) { + const std::list dungeonCompletionEvents = { + "Can Complete Forest Temple", + "Can Complete Goron Mines", + "Can Complete Lakebed Temple", + "Can Complete Arbiters Grounds", + "Can Complete Snowpeak Ruins", + "Can Complete Temple of Time", + "Can Complete City in the Sky", + "Can Complete Palace of Twilight" + }; + for (const auto& eventName : dungeonCompletionEvents) + { + if (world->GetEventIndex(eventName) == eventId) + { + return true; + } + } + return false; + }) >= count; + + default: + return false; + } + return false; + } + + EvalSuccess EvaluateEventRequirement(search::Search* search, area::EventAccess* event) + { + auto& formTime = search->_areaFormTime[event->GetArea()]; + if (EvaluateRequirementAtFormTime(event->GetRequirement(), search, formTime, event->GetArea()->GetWorld())) + { + return EvalSuccess::COMPLETE; + } + return EvalSuccess::NONE; + } + + EvalSuccess EvaluateExitRequirement(search::Search* search, entrance::Entrance* exit) + { + // Some exits in the middle of entrance shuffling will not have a connected area. Ignore these + if (exit->GetConnectedArea() == nullptr) + { + return EvalSuccess::DISCONNECTED; + } + + // If the exit is currently disabled, don't try it + if (exit->IsDisabled()) + { + return EvalSuccess::NONE; + } + + auto& exitFormTimeCache = exit->GetWorld()->GetExitTimeFormCache(); + auto parentArea = exit->GetParentArea(); + auto connectedArea = exit->GetConnectedArea(); + auto parentAreaFormTime = search->_areaFormTime[parentArea]; + auto& connectedAreaFormTime = search->_areaFormTime[connectedArea]; + auto potentialExitFormTimes = (exitFormTimeCache.contains(exit) ? exitFormTimeCache[exit] : FormTime::ALL); + + // LOG_TO_DEBUG("Trying " + connectedArea->GetName()); + + auto connectedAreaTwilightCleared = connectedArea->TwilightCleared(search); + if (!connectedAreaTwilightCleared) + { + // LOG_TO_DEBUG("Added Twilight"); + parentAreaFormTime |= FormTime::TWILIGHT; + potentialExitFormTimes |= FormTime::TWILIGHT; + } + + // Calculate the potential form times that we could spread to the connected area. These are the form times + // which the connected area does not have that the parent area has, and that the exit can potentially pass on + // to the connected area + auto potentialFormTimeSpread = ~connectedAreaFormTime & (parentAreaFormTime & potentialExitFormTimes); + + // LOG_TO_DEBUG("Potential spreads: " + FormTime::to_string(potentialFormTimeSpread)); + + // If there's no potential to spread FormTime, then return early + if (potentialFormTimeSpread == FormTime::NONE) + { + // LOG_TO_DEBUG("No potential formtime spread"); + return EvalSuccess::NONE; + } + + // Check each form time individually and spread the ones which succeed. If any of them pass, set the evaluation success + // to partial. + auto evalSuccess = EvalSuccess::NONE; + const auto& formTimes = connectedAreaTwilightCleared ? FormTime::ALL_FORM_TIMES : FormTime::ALL_FORM_TIMES_AND_TWILIGHT; + for (const auto& formTime : formTimes) + { + if (formTime & potentialFormTimeSpread) + { + if (EvaluateRequirementAtFormTime(exit->GetRequirement(), search, formTime, exit->GetWorld())) + { + if (!connectedAreaTwilightCleared) + { + if (~connectedAreaFormTime & FormTime::TWILIGHT) + { + // LOG_TO_DEBUG("Spread Twilight to " + connectedArea->GetName()); + connectedAreaFormTime |= FormTime::TWILIGHT; + evalSuccess = EvalSuccess::PARTIAL; + } + } + else if (formTime != FormTime::TWILIGHT) + { + // LOG_TO_DEBUG("Spread" + FormTime::to_string(formTime) + " to " + connectedArea->GetName()); + connectedAreaFormTime |= formTime; + evalSuccess = EvalSuccess::PARTIAL; + } + } + } + else + { + // LOG_TO_DEBUG(FormTime::to_string(formTime) + " is not a potential timespread."); + } + } + + if (evalSuccess != EvalSuccess::NONE) + { + search->ExpandFormTimes(connectedArea); + // If the connected area now has complete access, then we mark a complete success instead of just a partial one + } + + if (connectedAreaTwilightCleared && ((connectedAreaFormTime & potentialExitFormTimes) == potentialExitFormTimes)) + { + evalSuccess = EvalSuccess::COMPLETE; + } + + return evalSuccess; + } + + EvalSuccess EvaluateDisconnectedExitRequiremrnt(search::Search* search, entrance::Entrance* exit) + { + // If the exit is currently disabled, don't try it + if (exit->IsDisabled()) + { + return EvalSuccess::NONE; + } + + const auto parentArea = exit->GetParentArea(); + const auto parentAreaFormTime = search->_areaFormTime[parentArea]; + + // Check each form time individually and spread the ones which succeed. If any of them pass, set the evaluation success + // to partial. + for (const auto& formTime : FormTime::ALL_FORM_TIMES) + { + if (formTime & parentAreaFormTime) + { + if (EvaluateRequirementAtFormTime(exit->GetRequirement(), search, formTime, exit->GetWorld())) + { + return EvalSuccess::PARTIAL; + } + } + } + return EvalSuccess::NONE; + } + + EvalSuccess EvaluateLocationRequirement(search::Search* search, area::LocationAccess* locAccess) + { + auto& formTime = search->_areaFormTime[locAccess->GetArea()]; + if (EvaluateRequirementAtFormTime(locAccess->GetRequirement(), search, formTime, locAccess->GetArea()->GetWorld())) + { + return EvalSuccess::COMPLETE; + } + return EvalSuccess::NONE; + } +} // namespace randomizer::logic::requirement diff --git a/mods/randomizer/generator/logic/requirement.hpp b/mods/randomizer/generator/logic/requirement.hpp new file mode 100644 index 0000000000..5130d4e340 --- /dev/null +++ b/mods/randomizer/generator/logic/requirement.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include + +// Forward declarations +namespace randomizer::logic::item +{ + class Item; +} + +namespace randomizer::logic::entrance +{ + class Entrance; +} + +namespace randomizer::logic::area +{ + class EventAccess; + class LocationAccess; +} // namespace randomizer::logic::area + +namespace randomizer::logic::world +{ + class World; +} + +namespace randomizer::logic::search +{ + class Search; +} + +namespace randomizer::logic::requirement +{ + enum class Type + { + INVALID, + NOTHING, + IMPOSSIBLE, + OR, + AND, + ITEM, + COUNT, + EVENT, + MACRO, + DAY, + NIGHT, + HUMAN_LINK, + WOLF_LINK, + TWILIGHT, + GOLDEN_BUGS, + HEARTS, + DUNGEONS_COMPLETED, + }; + + enum class EvalSuccess + { + NONE, + PARTIAL, + COMPLETE, + DISCONNECTED, + }; + + // FormTime is a set of flags that cover all the possible cases of human-wolf/day-night combinations that are needed + // for logic to work + namespace FormTime + { + enum + { + NONE = 0b0000, + HUMAN_DAY = 0b0001, + HUMAN_NIGHT = 0b0010, + WOLF_DAY = 0b0100, + WOLF_NIGHT = 0b1000, + HUMAN = HUMAN_DAY | HUMAN_NIGHT, + WOLF = WOLF_DAY | WOLF_NIGHT, + DAY = HUMAN_DAY | WOLF_DAY, + NIGHT = HUMAN_NIGHT | WOLF_NIGHT, + ALL = 0b1111, + TWILIGHT = 0b10000, + }; + + extern const std::vector ALL_FORM_TIMES; + extern const std::vector ALL_FORM_TIMES_AND_TWILIGHT; + extern const std::vector ALL_FORM_AND_DAY_TIMES; + + std::string to_string(const int& formTime); + }; // namespace FormTime + + struct Requirement; + struct Requirement + { + using Argument = std::variant; + Type _type = Type::INVALID; + std::vector _args; + + std::string to_string() const; + }; + + Requirement ParseRequirementString(const std::string& reqStr, + world::World* world, + const bool& forceLogic = false); + + /** + * @brief Evaluates a requirement assuming it meets a simplistic criteria. This is used + * for checking settings when reading them in from, for example, startflags.yaml + * + * @param req - The simple requirement + * @param world - The world this requirement is for + * @return true if the requirment holds, false otherwise + */ + bool EvaluateSimpleRequirement(const Requirement& req, world::World* world); + + bool EvaluateRequirementAtFormTime(const Requirement& req, + search::Search* search, + const int& formTime, + world::World*); + EvalSuccess EvaluateEventRequirement(search::Search* search, area::EventAccess* event); + EvalSuccess EvaluateExitRequirement(search::Search* search, entrance::Entrance* exit); + EvalSuccess EvaluateDisconnectedExitRequiremrnt(search::Search* search, entrance::Entrance* exit); + EvalSuccess EvaluateLocationRequirement(search::Search* search, + area::LocationAccess* locAccess); + + const extern Requirement NO_REQUIREMENT; + const extern Requirement IMPOSSIBLE_REQUIREMENT; +} // namespace randomizer::logic::requirement diff --git a/mods/randomizer/generator/logic/search.cpp b/mods/randomizer/generator/logic/search.cpp new file mode 100644 index 0000000000..8238724ab8 --- /dev/null +++ b/mods/randomizer/generator/logic/search.cpp @@ -0,0 +1,664 @@ +#include "search.hpp" + +#include "world.hpp" +#include "../randomizer.hpp" +#include "../utility/general.hpp" +#include "../utility/platform.hpp" + +#include +#include + +namespace randomizer::logic::search +{ + Search::Search(): _searchMode(SearchMode::NO_SEARCH) { + + } + + Search::Search(const SearchMode& searchMode, + world::WorldPool* worlds, + const item_pool::ItemPool& items /* = {} */, + const int& worldToSearch /* = -1 */, + bool startingInventory /*= true */): + _searchMode(searchMode), _worlds(worlds), _startingInventory(startingInventory) + { + // Set the items we should already own + this->_ownedItems.insert(items.begin(), items.end()); + + // Add starting inventory items for each world + if (this->_startingInventory) { + for (const auto& world : *(this->_worlds)) + { + if (worldToSearch == -1 || world->GetID() == worldToSearch) + { + const auto& startingInventory = world->GetStartingItemPool(); + this->_ownedItems.insert(startingInventory.begin(), startingInventory.end()); + } + } + } + + // Set search starting properties and add each world's root exits to _exitsToTry + for (const auto& world : *(this->_worlds)) + { + if (worldToSearch == -1 || world->GetID() == worldToSearch) + { + auto root = world->GetRootArea(); + this->_visitedAreas.emplace(root); + world->SetSearchStartingProperties(this); + for (const auto& exit : root->GetExits()) + { // Don't add target exits if we're doing a sphere zero search + if (!exit->IsDisabled() && (this->_searchMode != SearchMode::SPHERE_ZERO || !exit->IsTarget())) + { + this->_exitsToTry.emplace_back(exit); + } + } + } + } + } + + void Search::SearchWorlds() + { + if (this->_searchMode == SearchMode::NO_SEARCH) { + return; + } + // Get all locations which fit criteria to test on each iteration + std::list itemLocations = {}; + for (const auto& world : *(this->_worlds)) + { + for (const auto& [areaName, area] : world->GetAreaTable()) + { + for (const auto& locAccess : area->GetLocations()) + { + // Only add locations that aren't empty, unless we're searching with one of the modes below + if (!locAccess->GetLocation()->IsEmpty() || + utility::general::IsAnyOf(this->_searchMode, + SearchMode::ACCESSIBLE_LOCATIONS, + SearchMode::ALL_LOCATIONS_REACHABLE, + SearchMode::SPHERE_ZERO, + SearchMode::TRACKER_SPHERES)) + { + itemLocations.emplace_back(locAccess); + } + } + } + } + + // Main Searching Loop + // Keep iterating while new things are being found, but if the search is beatable and we're either generating the + // playthrough or checking for beatability, exit early. + this->_newThingsFound = true; + while ( + this->_newThingsFound && + !(this->_isBeatable && + utility::general::IsAnyOf(this->_searchMode, SearchMode::GENERATE_PLAYTHROUGH, SearchMode::GAME_BEATABLE))) + { + // Keep track of making logical progress. We want to keep iterating as long as we're finding new things on each + // iteration + this->_newThingsFound = false; + + // Add an empty sphere if we're generating the playthrough or tracker spheres + if (utility::general::IsAnyOf(this->_searchMode, + SearchMode::GENERATE_PLAYTHROUGH, + SearchMode::TRACKER_SPHERES)) + { + this->_playthroughSpheres.push_back({}); + this->_entranceSpheres.push_back({}); + } + + // Process Events and Exits at least once. If we're calculating spheres, then keep repeating these until nothing + // new is found. + do + { + this->_newThingsFound = false; + this->ProcessEvents(); + this->ProcessExits(); + } while (this->_newThingsFound && utility::general::IsAnyOf(this->_searchMode, + SearchMode::GENERATE_PLAYTHROUGH, + SearchMode::TRACKER_SPHERES)); + + this->ProcessLocations(itemLocations); + this->_sphereNum += 1; + } + } + + void Search::ProcessEvents() + { + for (const auto& event : this->_eventsToTry) + { + // Ignore the event if we've already found it, or we're not searching its world at the moment + if (this->_ownedEvents.contains(event->GetEventIndex()) || + (this->_worldToSearch != -1 && event->GetArea()->GetWorld()->GetID() != this->_worldToSearch)) + { + continue; + } + + if (requirement::EvaluateEventRequirement(this, event) == + requirement::EvalSuccess::COMPLETE) + { + this->_newThingsFound = true; + this->_ownedEvents.insert(event->GetEventIndex()); + } + } + } + + void Search::ProcessExits() + { + for (const auto& exit : this->_exitsToTry) + { + // Ignore the exit if we've already completed it, or we're not searching its world at the moment + if (this->_successfulExits.contains(exit) || + (this->_worldToSearch != -1 && this->_worldToSearch != exit->GetWorld()->GetID())) + { + continue; + } + + // If the exit is successful + auto evalSuccess = requirement::EvaluateExitRequirement(this, exit); + if (utility::general::IsAnyOf(evalSuccess, + requirement::EvalSuccess::COMPLETE, + requirement::EvalSuccess::PARTIAL)) + { + this->AddExitToEntranceSpheres(exit); + if (evalSuccess == requirement::EvalSuccess::COMPLETE) + { + this->_successfulExits.insert(exit); + } + this->_newThingsFound = true; + + // If this exit's connected region hasn't been explored yet, then explore it + if (!this->_visitedAreas.contains(exit->GetConnectedArea())) + { + this->_visitedAreas.insert(exit->GetConnectedArea()); + this->Explore(exit->GetConnectedArea()); + } + } + } + } + + void Search::ProcessLocations(std::list& itemLocations) + { + std::list accessibleThisIteration = {}; + // Loop through all possible item locations for this search + for (const auto& locAccess : itemLocations) + { + auto location = locAccess->GetLocation(); + auto world = location->GetWorld(); + + // If we've already visited this location, or have *not* visited this area, or aren't searching this world, + // then ignore the location this time + if (this->_visitedLocations.contains(location) || !this->_visitedAreas.contains(locAccess->GetArea()) || + (this->_worldToSearch != -1 && world->GetID() != this->_worldToSearch)) + { + continue; + } + + // If the location's requirement is met + if (requirement::EvaluateLocationRequirement(this, locAccess) == + requirement::EvalSuccess::COMPLETE) + { + this->_visitedLocations.insert(location); + this->_newThingsFound = true; + // If we're calculating spheres, then process this location later for accurate sphere calculation. Otherwise + // process it now for slightly faster searching + if (utility::general::IsAnyOf(this->_searchMode, + SearchMode::GENERATE_PLAYTHROUGH, + SearchMode::TRACKER_SPHERES)) + { + accessibleThisIteration.push_back(location); + } + else + { + this->ProcessLocation(location); + } + } + } + + for (const auto& location : accessibleThisIteration) + { + this->ProcessLocation(location); + if (this->_isBeatable) + { + return; + } + } + } + + void Search::ProcessLocation(location::Location* location) + { + // Don't return if we aren't collecting items + if (!this->_collectItems) + { + return; + } + + // Add the tracked item if we're doing tracker sphere tracking + if (this->_searchMode == SearchMode::TRACKER_SPHERES) + { + this->_ownedItems.insert(location->GetTrackedItem()); + } + // Otherwise add the current item as usual + else + { + this->_ownedItems.insert(location->GetCurrentItem()); + } + + // If we just added the shadow crystal, expand timeforms for all areas we've visited so far + if (location->GetCurrentItem()->IsShadowCrystal()) + { + for (auto& area : this->_visitedAreas) + { + if (area->GetWorld()->GetID() == location->GetWorld()->GetID()) + { + this->ExpandFormTimes(area); + } + } + } + + // If we're generating spheres and the location has a major item, add the location to the last sphere + if (this->_searchMode == SearchMode::TRACKER_SPHERES || + (this->_searchMode == SearchMode::GENERATE_PLAYTHROUGH && location->GetCurrentItem()->IsMajor())) + { + this->_playthroughSpheres.back().push_back(location); + } + + // If we're generating the playthrough or just checking for beatability, then we can stop searching early if we've + // found all world's game winning items + if (utility::general::IsAnyOf(this->_searchMode, SearchMode::GENERATE_PLAYTHROUGH, SearchMode::GAME_BEATABLE) && + location->GetCurrentItem()->IsGameWinningItem()) + { + if (std::ranges::count_if(this->_ownedItems, [](const auto& item) { + return item->IsGameWinningItem(); + }) == this->_worlds->size()) + { + if (this->_searchMode == SearchMode::GENERATE_PLAYTHROUGH) + { + auto& lastSphere = this->_playthroughSpheres.back(); + std::erase_if(lastSphere,[](const auto& loc) { return !loc->GetCurrentItem()->IsGameWinningItem(); }); + } + this->_isBeatable = true; + } + } + } + + void Search::Explore(area::Area* area) + { + for (const auto& event : area->GetEvents()) + { + this->_eventsToTry.push_back(event); + } + + for (const auto& exit : area->GetExits()) + { + auto evalSuccess = requirement::EvaluateExitRequirement(this, exit); + switch (evalSuccess) + { + case requirement::EvalSuccess::COMPLETE: + this->_successfulExits.insert(exit); + this->AddExitToEntranceSpheres(exit); + if (!this->_visitedAreas.contains(exit->GetConnectedArea())) + { + this->_visitedAreas.insert(exit->GetConnectedArea()); + this->Explore(exit->GetConnectedArea()); + } + case requirement::EvalSuccess::PARTIAL: + this->_exitsToTry.push_back(exit); + this->AddExitToEntranceSpheres(exit); + if (!this->_visitedAreas.contains(exit->GetConnectedArea())) + { + this->_visitedAreas.insert(exit->GetConnectedArea()); + this->Explore(exit->GetConnectedArea()); + } + case requirement::EvalSuccess::NONE: + [[fallthrough]]; + case requirement::EvalSuccess::DISCONNECTED: + this->_exitsToTry.push_back(exit); + } + } + } + + void Search::ExpandFormTimes(area::Area* area) + { + using namespace requirement; + + auto& areaFormTime = this->_areaFormTime[area]; + auto twilightCleared = area->TwilightCleared(this); + + auto shadowCrystal = area->GetWorld()->GetShadowCrystal(); + // Check if we can add additional form times to the area + if (area->CanChangeTime() && area->CanTransform() && this->_ownedItems.contains(shadowCrystal) && twilightCleared) + { + // LOG_TO_DEBUG("Spread All to " + area->GetName()); + areaFormTime |= FormTime::ALL; + } + // This might look backwards at first glance, but spreading formtime by the form spreads both day and night for the form + else if (area->CanChangeTime() && twilightCleared) + { + if (areaFormTime & FormTime::WOLF) + { + // LOG_TO_DEBUG("Spread Day/Night to " + area->GetName()); + areaFormTime |= FormTime::WOLF; + } + else if (areaFormTime & FormTime::HUMAN) + { + // LOG_TO_DEBUG("Spread Day/Night to " + area->GetName()); + areaFormTime |= FormTime::HUMAN; + } + } + // Same as above except with spreading time spreads the form + else if (area->CanTransform() && this->_ownedItems.contains(shadowCrystal) && twilightCleared) + { + if (areaFormTime & FormTime::NIGHT) + { + // LOG_TO_DEBUG("Spread Human/Wolf to " + area->GetName()); + areaFormTime |= FormTime::NIGHT; + } + + if (areaFormTime & FormTime::DAY) + { + // LOG_TO_DEBUG("Spread Human/Wolf to " + area->GetName()); + areaFormTime |= FormTime::DAY; + } + } + } + + void Search::AddExitToEntranceSpheres(entrance::Entrance* exit) + { + if (utility::general::IsAnyOf(this->_searchMode, + SearchMode::GENERATE_PLAYTHROUGH, + SearchMode::TRACKER_SPHERES) && + exit->IsShuffled()) + { + if (!this->_playthroughEntrances.contains(exit)) + { + this->_entranceSpheres.back().push_back(exit); + this->_playthroughEntrances.insert(exit); + if (!exit->IsDecoupled() && exit->GetReplaces()->GetReverse()) + { + this->_playthroughEntrances.insert(exit->GetReplaces()->GetReverse()); + } + } + } + } + + bool Search::HasAccessibleDisconnectedExit() + { + for (const auto& exit : this->_exitsToTry) + { + if (exit->GetConnectedArea() == nullptr && + requirement::EvaluateDisconnectedExitRequiremrnt(this, exit) != requirement::EvalSuccess::NONE) + { + return true; + } + } + return false; + } + + void Search::RemoveEmptySpheres() + { + // Get rid of any empty spheres in both the item playthrough and entrance playthrough + // based only on if the item playthrough has empty spheres. Both the playthroughs + // will have the same number of spheres, so we only need to conditionally + // check one of them. + auto itemItr = this->_playthroughSpheres.begin(); + auto entranceItr = this->_entranceSpheres.begin(); + while (itemItr != this->_playthroughSpheres.end()) + { + if (itemItr->empty() && entranceItr->empty()) + { + itemItr = this->_playthroughSpheres.erase(itemItr); + entranceItr = this->_entranceSpheres.erase(entranceItr); + } + else + { + ++itemItr; // Only incremement if we don't erase + ++entranceItr; + } + } + } + + void Search::DumpWorldGraph(const int& worldNum /* = 0 */) + { + auto& world = this->_worlds->at(worldNum); + std::cout << "Now dumping search graph for world " << worldNum << std::endl; + std::ofstream worldGraph; + std::string filepath = "World.gv"; + worldGraph.open(filepath); + worldGraph << "digraph {\n\tcenter=true;\n"; + for (const auto& [areaName, area] : world->GetAreaTable()) + { + auto color = this->_visitedAreas.contains(area.get()) ? "black" : "red"; + std::string formTimeStr = ":
"; + auto& areaFormTime = this->_areaFormTime[area.get()]; + if (areaFormTime & requirement::FormTime::HUMAN) + { + formTimeStr += " Human"; + } + if (areaFormTime & requirement::FormTime::WOLF) + { + formTimeStr += " Wolf"; + } + if (areaFormTime & requirement::FormTime::DAY) + { + formTimeStr += " Day"; + } + if (areaFormTime & requirement::FormTime::NIGHT) + { + formTimeStr += " Night"; + } + if (areaFormTime & requirement::FormTime::TWILIGHT) + { + formTimeStr += " Twilight"; + } + + worldGraph << "\t\"" << areaName << "\"[label=<" << areaName << formTimeStr << "> shape=\"plain\" fontcolor=\"" + << color << "\"];\n"; + + // Make edge connections defined by events + for (const auto& event : area->GetEvents()) + { + color = this->_ownedEvents.contains(event->GetEventIndex()) ? "blue" : "red"; + auto eventName = world->GetEventName(event->GetEventIndex()); + worldGraph << "\t\"" << eventName << "\"[label=<" << eventName << "> shape=\"plain\" fontcolor=\"" << color + << "\"];"; + worldGraph << "\t\"" << areaName << "\" -> \"" << eventName << "\"[dir=forward color=\"" << color << "\"]"; + } + + // Make edge connections defined by exits + for (const auto& exit : area->GetExits()) + { + if (exit->GetConnectedArea()) + { + color = this->_successfulExits.contains(exit) ? "black" : "red"; + worldGraph << "\t\"" << areaName << "\" -> \"" << exit->GetConnectedArea()->GetName() + << "\"[dir=forward color=\"" << color << "\"]"; + } + } + + // Make edge connections between areas and their locations + for (const auto& locAccess : area->GetLocations()) + { + auto location = locAccess->GetLocation(); + color = this->_visitedLocations.contains(location) ? "black" : "red"; + worldGraph << "\t\"" << location->GetName() << "\"[label=<" << location->GetName() << ":
" + << location->GetCurrentItem()->GetName() << "> shape=\"plain\" fontcolor=\"" << color << "\"];"; + worldGraph << "\t\"" << areaName << "\" -> \"" << location->GetName() << "\"[dir=forward color=\"" << color + << "\"]"; + } + } + + worldGraph << "}"; + worldGraph.close(); + } + + std::optional VerifyLogic(world::WorldPool* worlds, + const item_pool::ItemPool& items /* = {} */) + { + // Run an all locations reachable search + auto search = Search::AllLocationsReachable(worlds, items); + search.SearchWorlds(); + + for (const auto& world : *worlds) + { + // If all locations should be reachable, make sure they're all reachable + if (world->Setting("Logic Rules") == "All Locations Reachable") + { + auto numlocationsReached = + std::ranges::count_if(search._visitedLocations, [&](const auto& location) { + return location->GetWorld() == world.get(); + }); + auto allLocations = world->GetAllLocations(/*includeNonItemLocations = */ true); + + if (numlocationsReached != allLocations.size()) + { + std::string errorMsg = "Not all locations reachable! Missing locations:\n"; + // Gather all the missing locations + std::vector unreachedLocations = {}; + for (const auto& location : allLocations) + { + if (!search._visitedLocations.contains(location)) + { + unreachedLocations.push_back(location); + } + } + // Only print the first 5 so we don't clog the error message + for (auto i = 0; i < unreachedLocations.size(); i++) + { + errorMsg += "- " + unreachedLocations[i]->GetName() + "\n"; + if (i == 4 && i != unreachedLocations.size()) + { + errorMsg += "(" + std::to_string(unreachedLocations.size() - i) + " more)"; + break; + } + } + return errorMsg; + } + } + } + + return std::nullopt; + } + + void GeneratePlaythrough(Randomizer* randomizer) + { + auto& worlds = randomizer->GetWorlds(); + LOG_TO_DEBUG("Generating Playthrough"); + // Generate Initial Playthrough + auto playthroughSearch = Search::Playthrough(&worlds); + playthroughSearch.SearchWorlds(); + + auto& playthroughSpheres = playthroughSearch._playthroughSpheres; + + // Keep track of all locations we temporaily take items away from so we can give them back after playthrough calculation + std::unordered_map tempEmptyLocations = {}; + // Keep track of all the locations that appear in the playthrough + std::unordered_set playthroughLocationsSet = {}; + for (const auto& sphere : playthroughSpheres) + { + for (const auto& location : sphere) + { + playthroughLocationsSet.insert(location); + } + } + + // Remove all items from locations that are not part of the playthrough set + for (const auto& world : worlds) + { + for (const auto& location : world->GetAllLocations()) + { + if (!playthroughLocationsSet.contains(location)) + { + tempEmptyLocations[location] = location->GetCurrentItem(); + location->RemoveCurrentItem(); + } + } + } + + utility::platform::Log("Paring down playthrough"); + // Pare down the playthrough in reverse order so we're paring it down from highest to lowest sphere. + // This way, lower sphere items will be prioritized for the playthrough + playthroughSpheres.reverse(); + for (const auto& sphere : playthroughSpheres) + { + for (auto& location : sphere) + { + auto itemAtLocation = location->GetCurrentItem(); + location->RemoveCurrentItem(); + + // If the game is beatable, temporarily take this item away and erase the location from the playthrough + // locations + if (GameBeatable(&worlds)) + { + tempEmptyLocations[location] = itemAtLocation; + playthroughLocationsSet.erase(location); + } + else + { + location->SetCurrentItem(itemAtLocation); + } + } + } + + // Generate a new playthrough search incase some spheres were flattened by the previous generation having access + // to extra items + auto newSearch = Search::Playthrough(&worlds); + newSearch.SearchWorlds(); + + // Now do the same process for entrances to pare down the entrance playthrough + auto& entranceSpheres = newSearch._entranceSpheres; + std::unordered_map nonRequiredEntrances = {}; + + for (auto& sphere : entranceSpheres) + { + // Make a copy to avoid iterator invalidation + auto sphereCopy = sphere; + for (const auto& entrance : sphereCopy) + { + auto connectedArea = entrance->Disconnect(); + if (GameBeatable(&worlds)) + { + // If the game is still beatable then this entrance is not required + sphere.remove(entrance); + nonRequiredEntrances[entrance] = connectedArea; + } + else + { + // If the entrance is required, reconnect it + entrance->Connect(connectedArea); + } + } + } + + // Reconnect all non-required entrances + for (auto& [entrance, connectedArea] : nonRequiredEntrances) + { + entrance->Connect(connectedArea); + } + + // Give items back their locations + for (auto& [location, item] : tempEmptyLocations) + { + location->SetCurrentItem(item); + } + + // Erase all locations not in the playthrough locations set + for (auto& sphere : newSearch._playthroughSpheres) + { + sphere.remove_if([&](const auto& location) { + return !playthroughLocationsSet.contains(location); + }); + } + + // Remove any empty spheres + newSearch.RemoveEmptySpheres(); + + randomizer->GetPlaythroughSpheres() = newSearch._playthroughSpheres; + randomizer->GetEntranceSpheres() = newSearch._entranceSpheres; + } + + bool GameBeatable(world::WorldPool* worlds, const item_pool::ItemPool& items /* = {} */) + { + auto search = Search::Beatable(worlds, items); + search.SearchWorlds(); + return search._isBeatable; + } + +} // namespace randomizer::logic::search diff --git a/mods/randomizer/generator/logic/search.hpp b/mods/randomizer/generator/logic/search.hpp new file mode 100644 index 0000000000..c71b2ae6f0 --- /dev/null +++ b/mods/randomizer/generator/logic/search.hpp @@ -0,0 +1,176 @@ +#pragma once + +#include "item_pool.hpp" +#include "../utility/log.hpp" + +#include +#include +#include +#include +#include +#include + +// Forward Declarations (we have a lot here) +namespace randomizer +{ + class Randomizer; +} + +namespace randomizer::logic::world +{ + class World; + using WorldPool = std::vector>; +} // namespace randomizer::logic::world + +namespace randomizer::logic::item +{ + class Item; +} + +namespace randomizer::logic::location +{ + class Location; +} + +namespace randomizer::logic::area +{ + class EventAccess; + class LocationAccess; + class Area; +} // namespace randomizer::logic::area + +namespace randomizer::logic::entrance +{ + class Entrance; +} + +namespace randomizer::logic::search +{ + enum class SearchMode + { + NO_SEARCH, + ACCESSIBLE_LOCATIONS, + GAME_BEATABLE, + ALL_LOCATIONS_REACHABLE, + GENERATE_PLAYTHROUGH, + SPHERE_ZERO, + TRACKER_SPHERES + }; + + class Search + { + public: + Search(); + Search(const SearchMode& searchMode, + world::WorldPool* worlds, + const item_pool::ItemPool& items = {}, + const int& worldToSearch = -1, + bool startingInventory = true); + + static auto Accessible(world::WorldPool* worlds, + const item_pool::ItemPool& items = {}, + const int& worldToSearch = -1) + { + return Search(SearchMode::ACCESSIBLE_LOCATIONS, worlds, items, worldToSearch); + } + + static auto AccessibleNoStartingInventory(world::WorldPool* worlds, + const item_pool::ItemPool& items = {}, + const int& worldToSearch = -1) + { + return Search(SearchMode::ACCESSIBLE_LOCATIONS, worlds, items, worldToSearch, false); + } + + static auto AllLocationsReachable(world::WorldPool* worlds, + const item_pool::ItemPool& items = {}, + const int& worldToSearch = -1) + { + return Search(SearchMode::ALL_LOCATIONS_REACHABLE, worlds, items, worldToSearch); + } + + static auto Playthrough(world::WorldPool* worlds, + const item_pool::ItemPool& items = {}, + const int& worldToSearch = -1) + { + return Search(SearchMode::GENERATE_PLAYTHROUGH, worlds, items, worldToSearch); + } + + static auto Beatable(world::WorldPool* worlds, + const item_pool::ItemPool& items = {}, + const int& worldToSearch = -1) + { + return Search(SearchMode::GAME_BEATABLE, worlds, items, worldToSearch); + } + + static auto SphereZero(world::WorldPool* worlds, + const item_pool::ItemPool& items = {}, + const int& worldToSearch = -1) + { + return Search(SearchMode::SPHERE_ZERO, worlds, items, worldToSearch); + } + + void SearchWorlds(); + + /** + * @brief Loop through and see if there are any events that are now accessible. Add them to the ownedEvents list if + * they are. + * + */ + void ProcessEvents(); + void ProcessExits(); + void ProcessLocations(std::list& itemLocations); + void ProcessLocation(location::Location* location); + void Explore(area::Area* area); + void ExpandFormTimes(area::Area* area); + + void AddExitToEntranceSpheres(entrance::Entrance*); + bool HasAccessibleDisconnectedExit(); + void RemoveEmptySpheres(); + + /** + * @brief Will dump a file which can be turned into a visual graph using graphviz + * https://graphviz.org/download/ + * Use this command to generate the graph: "dot -Tsvg -o world.svg" + * Then, open world.svg in a browser and CTRL + F to find the area of interest + */ + void DumpWorldGraph(const int& world = 0); + + SearchMode _searchMode; + world::WorldPool* _worlds; + int _worldToSearch = -1; + + // Search variables + int _sphereNum = 0; + bool _newThingsFound = true; + bool _isBeatable = false; + bool _collectItems = true; + bool _startingInventory = true; + std::unordered_set _ownedEvents; + std::unordered_multiset _ownedItems; + + std::list _eventsToTry; + std::list _exitsToTry; + std::unordered_set _visitedLocations; + std::unordered_set _visitedAreas; + std::unordered_set _successfulExits; + std::unordered_set _playthroughEntrances; + + std::list> _playthroughSpheres; + std::list> _entranceSpheres; + + std::unordered_map _areaFormTime; + }; + + /** + * @brief Verifies that necessary logic for all worlds is satisfied. + * + * @param worlds The worlds to verify logic for + * @param items The pool of items that haven't been placed yet + * + * @return An optional value that holds a string explaining why the logic was not satisfied if validation failed + */ + std::optional VerifyLogic(world::WorldPool* worlds, + const item_pool::ItemPool& items = {}); + void GeneratePlaythrough(randomizer::Randomizer* randomizer); + bool GameBeatable(world::WorldPool* worlds, const item_pool::ItemPool& items = {}); +} // namespace randomizer::logic::search diff --git a/mods/randomizer/generator/logic/spoiler_log.cpp b/mods/randomizer/generator/logic/spoiler_log.cpp new file mode 100644 index 0000000000..ba93bceaa0 --- /dev/null +++ b/mods/randomizer/generator/logic/spoiler_log.cpp @@ -0,0 +1,272 @@ +#include "spoiler_log.hpp" + +#include "entrance_shuffle.hpp" +#include "../randomizer.hpp" +#include "../utility/file.hpp" +#include "../utility/platform.hpp" +#include "../utility/yaml.hpp" + +#include +#include +#include + +namespace randomizer::logic::spoiler_log +{ + std::string SpoilerFormatLocation(const location::Location* location, const size_t& longestNameLength) + { + const auto numSpaces = longestNameLength - location->GetName().length(); + const std::string spaces(numSpaces, ' '); + + return location->GetName() + ": " + spaces + location->GetCurrentItem()->GetName(); + } + + std::string SpoilerFormatEntrance(const entrance::Entrance* entrance, const size_t& longestNameLength) + { + const auto numSpaces = longestNameLength - entrance->GetAlias().length(); + const std::string spaces(numSpaces, ' '); + const auto replacement = entrance->GetReplaces(); + + return entrance->GetAlias() + ": " + spaces + replacement->GetAliasFrom(); + } + + void LogBasicInfo(std::ofstream& log, Randomizer* randomizer) + { + // TODO: print mod version instead of dusklight version + // log << "Dusklight Version: " << DUSK_WC_DESCRIBE << std::endl; + log << "Seed: " << randomizer->GetConfig().GetSeed() << std::endl; + log << "Permalink: " << randomizer->GetConfig().GetPermalink() << std::endl; + log << "Hash: " << randomizer->GetConfig().GetHash() << std::endl; + } + + void LogSettings(std::ofstream& log, Randomizer* randomizer) + { + log << std::endl << "# Settings" << std::endl; + log << YAML::Dump(randomizer->GetConfig().SettingsToYaml()) << std::endl; + } + + void GenerateSpoilerLog(Randomizer* randomizer) + { + utility::platform::Log("Generating Spoiler Log"); + + // Create folders + if (!utility::file::dirExists(randomizer->GetSeedOutputPath())) + { + utility::file::create_directories(randomizer->GetSeedOutputPath()); + } + + auto& config = randomizer->GetConfig(); + auto& worlds = randomizer->GetWorlds(); + + std::filesystem::path filepath = randomizer->GetSeedOutputPath() / (config.GetHash() + " Spoiler Log.txt"); + std::ofstream spoilerLog; + spoilerLog.open(filepath); + + LogBasicInfo(spoilerLog, randomizer); + + // Gather worlds with starting inventories + std::list worldsWithStartingInventories = {}; + for (const auto& world : worlds) + { + if (!world->GetStartingItemPool().empty()) + { + worldsWithStartingInventories.push_back(world.get()); + } + } + // Print starting inventories if there are any + if (!worldsWithStartingInventories.empty()) + { + spoilerLog << std::endl << "All Starting Items:" << std::endl; + for (const auto& world : worldsWithStartingInventories) + { + spoilerLog << " World " << world->GetID() << ":" << std::endl; + for (const auto& item : world->GetStartingItemPool()) + { + spoilerLog << " - " << item->GetName() << std::endl; + } + } + } + + // Gather worlds with required dungeons + std::list worldsWithRequiredDungeons = {}; + for (const auto& world : worlds) + { + for (const auto& [dungeonName, dungeon] : world->GetDungeonTable()) { + if (dungeon->IsRequired()) { + worldsWithRequiredDungeons.push_back(world.get()); + break; + } + } + } + // Print required dungeons if there are any + if (!worldsWithRequiredDungeons.empty()) + { + spoilerLog << std::endl << "Required Dungeons:" << std::endl; + for (const auto& world : worldsWithRequiredDungeons) + { + spoilerLog << " World " << world->GetID() << ":" << std::endl; + for (const auto& [dungeonName, dungeon] : world->GetDungeonTable()) { + if (dungeon->IsRequired()) { + spoilerLog << " - " << dungeonName << std::endl; + } + } + } + } + + // Get name lengths for pretty formatting + size_t longestNameLength = 0; + for (const auto& sphere : randomizer->GetPlaythroughSpheres()) + { + for (const auto& location : sphere) + { + longestNameLength = std::max(location->GetName().length(), longestNameLength); + } + } + + // Print playthrough + int sphereNum = 0; + spoilerLog << std::endl << "Playthrough:" << std::endl; + for (auto& sphere : randomizer->GetPlaythroughSpheres()) + { + sphereNum += 1; + spoilerLog << " Sphere " << sphereNum << ":" << std::endl; + sphere.sort([](const auto& a, const auto& b) { return a->GetName()[0] < b->GetName()[0]; }); + for (const auto& location : sphere) + { + spoilerLog << " " << SpoilerFormatLocation(location, longestNameLength) << std::endl; + } + } + + // Get name lengths for pretty formatting + longestNameLength = 0; + for (const auto& sphere : randomizer->GetEntranceSpheres()) + { + for (const auto& entrance : sphere) + { + longestNameLength = std::max(entrance->GetAlias().length(), longestNameLength); + } + } + + // Print entrance playthrough + sphereNum = 0; + if (longestNameLength != 0) + { + spoilerLog << std::endl << "Entrance Playthrough:" << std::endl; + } + for (auto& sphere : randomizer->GetEntranceSpheres()) + { + sphereNum += 1; + if (sphere.empty()) + { + continue; + } + spoilerLog << " Sphere " << sphereNum << ":" << std::endl; + sphere.sort([](auto& e1, auto& e2) { return e1->GetID() < e2->GetID(); }); + for (const auto& entrance : sphere) + { + spoilerLog << " " << SpoilerFormatEntrance(entrance, longestNameLength) << std::endl; + } + } + + // Recalculate longest name length for all locations + longestNameLength = 0; + for (const auto& world : worlds) + { + for (const auto& location : world->GetAllLocations()) + { + longestNameLength = std::max(location->GetName().length(), longestNameLength); + } + } + + // Print All Locations + spoilerLog << std::endl << "All Locations:" << std::endl; + for (const auto& world : worlds) + { + spoilerLog << " World " << world->GetID() << ":" << std::endl; + for (const auto& location : world->GetAllLocations()) + { + spoilerLog << " " << SpoilerFormatLocation(location, longestNameLength) << std::endl; + } + } + + // Recalculate longest name length for all shuffled entrances + longestNameLength = 0; + for (const auto& world : worlds) + { + for (const auto& entrance : world->GetShuffledEntrances()) + { + longestNameLength = std::max(entrance->GetAlias().length(), longestNameLength); + } + } + // Print all randomized entrances + if (longestNameLength != 0) + { + spoilerLog << std::endl << "All Entrances:" << std::endl; + } + for (const auto& world : worlds) + { + auto entrances = world->GetShuffledEntrances(); + if (!entrances.empty()) + { + spoilerLog << " World " << world->GetID() << ":" << std::endl; + // Create entrance pools to easily separate the entrances by type + auto entrancePools = entrance_shuffle::CreateEntrancePools(world.get()); + auto mixedPools = world->GetSettings().GetMixedEntrancePools(); + for (auto& [entranceType, entrancePool] : entrancePools) + { + auto typeStr = entrance::TypeToStr(entranceType); + // If this is a mixed pool, display the types it mixed + if (typeStr.starts_with("Mixed Pool")) + { + typeStr += " ("; + auto& pool = mixedPools.front(); + for (const auto& type : pool) + { + typeStr += type + " + "; + } + typeStr.erase(typeStr.end() - 3, typeStr.end()); // Remove the last " + " + typeStr += ")"; + mixedPools.pop_front(); + } + spoilerLog << " " << typeStr << ":" << std::endl; + std::ranges::sort(entrancePool, [](auto& e1, auto& e2) { + return e1->GetID() < e2->GetID(); + }); + for (const auto& entrance : entrancePool) + { + // Ignore entrances that are impossible + if (entrance->GetRequirement()._type == requirement::Type::IMPOSSIBLE) + { + continue; + } + spoilerLog << " " << SpoilerFormatEntrance(entrance, longestNameLength) << std::endl; + } + } + } + } + + // TODO: Hints + + // Log Settings + LogSettings(spoilerLog, randomizer); + + spoilerLog.close(); + + utility::platform::Log("Wrote spoiler log to " + filepath.string()); + } + + void GenerateAntiSpoilerLog(Randomizer* randomizer) + { + // Create logs folder if it doesn't exist + if (!utility::file::dirExists(randomizer->GetSeedOutputPath())) + { + utility::file::create_directories(randomizer->GetSeedOutputPath()); + } + + std::filesystem::path filepath = randomizer->GetSeedOutputPath() / (randomizer->GetConfig().GetHash() + " Anti-Spoiler Log.txt"); + std::ofstream antiSpoilerLog; + antiSpoilerLog.open(filepath); + + LogBasicInfo(antiSpoilerLog, randomizer); + LogSettings(antiSpoilerLog, randomizer); + } +} // namespace randomizer::logic::spoiler_log diff --git a/mods/randomizer/generator/logic/spoiler_log.hpp b/mods/randomizer/generator/logic/spoiler_log.hpp new file mode 100644 index 0000000000..4ed5ccf64a --- /dev/null +++ b/mods/randomizer/generator/logic/spoiler_log.hpp @@ -0,0 +1,13 @@ +#pragma once + +// Forward Declarations +namespace randomizer +{ + class Randomizer; +} + +namespace randomizer::logic::spoiler_log +{ + void GenerateSpoilerLog(Randomizer* randomizer); + void GenerateAntiSpoilerLog(Randomizer* randomizer); +} // namespace randomizer::logic::spoiler_log diff --git a/mods/randomizer/generator/logic/world.cpp b/mods/randomizer/generator/logic/world.cpp new file mode 100644 index 0000000000..9b97a4d3b3 --- /dev/null +++ b/mods/randomizer/generator/logic/world.cpp @@ -0,0 +1,1349 @@ +#include "world.hpp" + + +#include "search.hpp" +#include "../randomizer.hpp" +#include "../utility/exception.hpp" +#include "../utility/file.hpp" +#include "../utility/general.hpp" +#include "../utility/log.hpp" +#include "../utility/platform.hpp" +#include "../utility/random.hpp" +#include "../utility/string.hpp" +#include "../utility/yaml.hpp" + +#include +#include +#include +#include + +namespace randomizer::logic::world +{ + World::World(const int& id, Randomizer* randomizer) : + _id(id), _randomizer(randomizer) + {} + + int World::GetID() const + { + return this->_id; + } + void World::SetSettings(const seedgen::settings::Settings& settings) + { + _settings = settings; + } + const seedgen::settings::Settings& World::GetSettings() const + { + return this->_settings; + } + void World::SetRandomizer(Randomizer* randomizer) + { + this->_randomizer = randomizer; + } + Randomizer* World::GetRandomizer() const + { + return this->_randomizer; + } + + void World::ResolveRandomSettings() + { + for (auto& [name, setting] : this->_settings.GetMap()) + { + setting.ResolveIfRandom(); + } + } + + void World::ResolveConflictingSettings() + { + // If Bonks Do Damage is On and the Damage Multiplier is OHKO and Eldin or Lanayru Twilight are not cleared, then + // this creates a logically impossible scenario. We can't guarantee repeatable access to a bottled fairy in twilight + // unless the player starts with the shadow crystal in their inventory. Turn off Bonks Do Damage in this case. + bool bonksDoDamage = this->Setting("Bonks Do Damage") == "On"; + bool ohko = this->Setting("Logic Damage Multiplier") == "OHKO"; + bool eldinTwilightNotCleared = this->Setting("Eldin Twilight Cleared") == "Off"; + bool lanayruTwilightNotCleared = this->Setting("Lanayru Twilight Cleared") == "Off"; + if (bonksDoDamage && ohko && (eldinTwilightNotCleared || lanayruTwilightNotCleared)) + { + this->Setting("Bonks Do Damage").SetCurrentOption("Off"); + LOG_TO_DEBUG("Changing Bonks Do Damage to Off"); + } + + // If we're starting as wolf link, the prologue has to be skipped + if (this->Setting("Starting Form") == "Wolf" && this->Setting("Skip Prologue") == "Off") + { + this->Setting("Skip Prologue").SetCurrentOption("On"); + LOG_TO_DEBUG("Turning off Prologue due to Wolf Start"); + } + } + + void World::Build() + { + utility::platform::Log(std::string("Building World ") + std::to_string(this->GetID())); + this->BuildItemTable(); + this->BuildLocationTable(); + this->LoadLogicMacros(); + this->LoadWorldGraph(); + // TODO: Verify Hint Data + this->GenerateItemPools(); + } + + void World::BuildItemTable() + { + LOG_TO_DEBUG("Building Item Table for World " + std::to_string(this->GetID())); + auto itemDataTree = LOAD_EMBED_YAML(RANDO_DATA_PATH "items.yaml"); + // Process all nodes of the yaml file. Each node contains one item + for (const auto& itemNode : itemDataTree) + { + // Check to make sure all required fields are present + YAMLVerifyFields(itemNode, "Name", "Importance", "Id"); + + // Required Fields + auto id = itemNode["Id"].as(); + auto name = itemNode["Name"].as(); + auto importanceStr = itemNode["Importance"].as(); + auto importance = item::ImportanceFromStr(importanceStr); + if (importance == item::Importance::INVALID) + { + throw std::runtime_error(std::string("Unknown importance \"") + importanceStr + "\" from item node:\n" + + YAML::Dump(itemNode)); + } + + LOG_TO_DEBUG("Processing new item " + name + "\tid: " + std::to_string(id)); + + // Optional fields + auto gameWinningItem = itemNode["Game Winning Item"].as(false); + auto dungeonSmallKey = itemNode["Dungeon Small Key"].as(""); + auto dungeonBigKey = itemNode["Dungeon Big Key"].as(""); + auto dungeonCompass = itemNode["Dungeon Compass"].as(""); + auto dungeonMap = itemNode["Dungeon Map"].as(""); + + // Make the item and insert it into the item table + auto item = std::make_unique(id, name, this, importance, gameWinningItem, + dungeonSmallKey != "", dungeonBigKey != "", dungeonCompass != "", dungeonMap != ""); + + this->_itemTable.try_emplace(name, std::move(item)); + + // Assign dungeon items to dungeons + auto curItem = this->GetItem(name); + if (dungeonSmallKey != "") + { + this->GetDungeon(dungeonSmallKey)->SetSmallKey(curItem); + } + else if (dungeonBigKey != "") + { + this->GetDungeon(dungeonBigKey)->SetBigKey(curItem); + } + else if (dungeonCompass != "") + { + this->GetDungeon(dungeonCompass)->SetCompass(curItem); + } + else if (dungeonMap != "") + { + this->GetDungeon(dungeonMap)->SetDungeonMap(curItem); + } + + // Put item into itemIdTable as well + this->_itemIdTable.try_emplace(id, curItem); + } + } + + void World::BuildLocationTable() + { + LOG_TO_DEBUG("Building Location Table for World " + std::to_string(this->GetID())); + auto locationDataTree = LOAD_EMBED_YAML(RANDO_DATA_PATH "locations.yaml"); + + // Process all nodes of the yaml file. Each node contains one location + int locationIdCounter = 0; + for (const auto& locationNode : locationDataTree) + { + // Check to make sure all required fields are present + YAMLVerifyFields(locationNode, "Name", "Categories", "Metadata"); + + // Required Fields + auto name = locationNode["Name"].as(); + std::unordered_set categories = {}; + for (const auto& category : locationNode["Categories"]) + { + categories.insert(category.as()); + // Add the category to the registered location categories for this world. + // When checking a locations categories, we can check to make sure the category + // is in here to make sure proper categories are being checked. + this->_registeredLocationCategories.insert(category.as()); + } + + // Optional Fields + auto originalItemName = locationNode["Original Item"].as("Nothing"); + + // If this location should be removed based on settings, don't insert it into the location table + if (ShouldRemoveLocation(name, originalItemName)) + { + this->_intentionallyRemovedLocations.insert(name); + continue; + } + + auto originalItem = this->GetItem(originalItemName); + auto goalLocation = locationNode["Goal Location"].as(false); + auto hintPriority = locationNode["Hint Priority"].as("Never"); + auto metadata = locationNode["Metadata"]; + + // Add metadata fields to categories as well + if (metadata.IsMap()) { + for (const auto& fieldNode : metadata) { + const auto& category = fieldNode.first.as(); + categories.insert(category); + this->_registeredLocationCategories.insert(category); + } + } + + auto location = std::make_unique(locationIdCounter++, + name, + categories, + this, + originalItem, + goalLocation, + hintPriority, + metadata); + + LOG_TO_DEBUG("Processing new location " + name + "\tid: " + std::to_string(locationIdCounter - 1) + + "\toriginal item: " + originalItemName); + + location->SetRegisteredLocationCategories(&this->_registeredLocationCategories); + + this->_locationTable.emplace(name, std::move(location)); + } + } + + void World::LoadLogicMacros() + { + LOG_TO_DEBUG("Loading Macros for World " + std::to_string(this->GetID())); + + auto macrosDataTree = LOAD_EMBED_YAML(RANDO_DATA_PATH "macros.yaml"); + + // Process all nodes of the yaml file. Each node contains one macro + int macroIdCounter = 0; + for (const auto& macroNode : macrosDataTree) + { + auto macroName = macroNode.first.as(); + auto macroReqStr = macroNode.second.as(); + + // Process the macro + this->_macros[macroIdCounter] = requirement::ParseRequirementString(macroReqStr, this, true); + + // Store it + this->_macroIndexes[macroName] = macroIdCounter; + LOG_TO_DEBUG("\"" + macroName + "\" assigned macro index of " + std::to_string(macroIdCounter)); + macroIdCounter += 1; + } + } + + void World::LoadWorldGraph() + { + LOG_TO_DEBUG("Loading world graph for World " + std::to_string(this->GetID())); + + std::unordered_set definedEvents = {}; + std::unordered_set definedAreas = {}; + + auto files = std::to_array({ + GET_EMBED_DATA(RANDO_DATA_PATH "world/Root.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/overworld/Ordona Province.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/overworld/Faron Province.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/overworld/Eldin Province.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/overworld/Lanayru Province.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/overworld/Gerudo Desert.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/overworld/Snowpeak Province.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/dungeons/Forest Temple.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/dungeons/Goron Mines.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/dungeons/Lakebed Temple.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/dungeons/Arbiters Grounds.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/dungeons/Snowpeak Ruins.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/dungeons/Temple of Time.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/dungeons/City in the Sky.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/dungeons/Palace of Twilight.yaml"), + GET_EMBED_DATA(RANDO_DATA_PATH "world/dungeons/Hyrule Castle.yaml"), + }); + + // Loop through and process all files + for (const auto& file : files) + { + auto worldDataTree = YAML::Load(file); + for (const auto& areaNode : worldDataTree) + { + YAMLVerifyFields(areaNode, "Name"); + + // Required Fields + auto areaName = areaNode["Name"].as(); + + // Optional Fields + auto mapSector = areaNode["Map Sector"].as(""); + auto region = areaNode["Region"].as(""); + auto twilight = areaNode["Twilight"].as(""); + auto dungeonStartArea = areaNode["Dungeon Start Area"].as(false); + auto canWarp = areaNode["Can Warp"].as(false); + auto canChangeTime = areaNode["Can Change Time"].as(false); + auto canTransformStr = areaNode["Can Transform"].as("Always"); + + // Copy our events map so we can add autogenerated events to it + std::map eventNodes = {}; + if (areaNode["Events"]) + { + for (const auto& eventNode : areaNode["Events"]) + { + auto eventName = eventNode.first.as(); + auto eventReqStr = eventNode.second.as(); + eventNodes.emplace(eventName, eventReqStr); + } + } + + // Add an event for accessing this area + eventNodes.emplace("Can Access " + areaName, "Nothing"); + + // If we can warp, add the Can Warp event + if (canWarp) + { + eventNodes.emplace("Can Warp", "Nothing"); + } + + // If this area unlocks a map sector, add the event for the map sector + if (mapSector != "") + { + eventNodes.emplace(mapSector + " Map Sector", "Nothing"); + } + + // Create and get the area object now so we can pass it to all the other things + // which need a pointer to it + auto area = this->GetArea(areaName, /*createIfNotFound = */ true); + definedAreas.emplace(areaName); + + // Set if the area can change time + area->SetCanChangeTime(canChangeTime); + + // If this area is in a dungeon, check and set the dungeon start area + if (this->_dungeons.contains(region)) + { + auto dungeon = this->GetDungeon(region); + if (dungeonStartArea) + { + dungeon->SetStartingArea(area); + } + } + + // Set hint region stuff + if (region != "") + { + area->SetHardAssignedRegion(region); + area->AddHintRegion(region); + } + + // Set the transform status + // Check to make sure a valid string is used for Can Transform + const std::unordered_set validTransformStatuses = {"Always", "If Transform Anywhere", "Never"}; + if (!validTransformStatuses.contains(canTransformStr)) + { + throw std::runtime_error("Unknown Can Transform Status \"" + canTransformStr + "\" in area \"" + areaName + + "\"."); + } + + auto canTransform = canTransformStr == "Always" || + (this->Setting("Logic Transform Anywhere") == "On" && canTransformStr == "If Transform Anywhere"); + + area->SetCanTransform(canTransform); + + // Set the completed twilight macro index if necessary + if (twilight != "") + { + auto twilightMacro = "Can Complete " + twilight + " Twilight"; + auto canCompleteTwilightMacroIndex = this->GetMacroIndex(twilightMacro); + + if (canCompleteTwilightMacroIndex == -1) + { + throw std::runtime_error('\"' + twilightMacro + + "\" is not a macro that exists when trying to set twilight macro for " + + area->GetName()); + } + + // Only bother with this if the setting for clearing this twilight is off + if (this->Setting(twilight + " Twilight Cleared") == "Off") + { + area->SetTwilightCompletedMacroIndex(canCompleteTwilightMacroIndex); + } + } + + // Lists of events, locations, and exits that we pass along to the area object + std::list> events = {}; + std::list> locations = {}; + std::list> exits = {}; + + // Process events + for (const auto& [eventName, eventReqStr] : eventNodes) + { + // Parse the requirement string + auto eventReq = requirement::ParseRequirementString(eventReqStr, this); + + // Create the EventAccess wrapper and put it into the list of events for this area + auto eventIndex = this->GetEventIndex(eventName); + auto event = std::make_unique(eventReq, area, eventIndex); + events.emplace_back(std::move(event)); + definedEvents.emplace(eventIndex); + } + + // Process locations + if (areaNode["Locations"]) + { + for (const auto& locationNode : areaNode["Locations"]) + { + // Get location name and requirement string + auto locationName = locationNode.first.as(); + auto locationReqStr = locationNode.second.as(); + + // Ignore the location if it's been intentionally removed + if (this->_intentionallyRemovedLocations.contains(locationName)) + { + continue; + } + + auto location = this->GetLocation(locationName); + + // If this location is in a twilight section, and is not a twilit insect, add the Not_Twilight macro. + // We can't assume repeatable access to non-insect locations in twilights. + if (twilight != "" && !location->GetOriginalItem()->GetName().ends_with("Twilight Tear")) + { + locationReqStr = "Not_Twilight and (" + locationReqStr + ")"; + LOG_TO_DEBUG("Adding Not_Twilight check to requirement for " + locationName); + } + + // Parse the requirement string + auto locationReq = requirement::ParseRequirementString(locationReqStr, this); + + // Create the LocationAccess wrapper and put it into the list of locations for this area + + auto locationAccess = std::make_unique(location, locationReq, area); + locations.emplace_back(std::move(locationAccess)); + + // Also add this LocationAccess to the locations list of access points + location->AddLocationAccess(locations.back().get()); + } + } + + // Process Exits + if (areaNode["Exits"]) + { + for (const auto& exitNode : areaNode["Exits"]) + { + // Get the connected area and requirement string + auto connectedAreaName = exitNode.first.as(); + auto entranceReqStr = exitNode.second.as(); + auto connectedArea = this->GetArea(connectedAreaName, /*createIfNotFound = */ true); + + // Parse the requirement string + auto entranceReq = requirement::ParseRequirementString(entranceReqStr, this); + + // Create the Entrance object and put it into the list of exits for this area + auto entrance = + std::make_unique(area, connectedArea, entranceReq, this); + exits.emplace_back(std::move(entrance)); + } + } + + area->SetEvents(events); + area->SetLocations(locations); + area->SetExits(exits); + } + } + + // Make sure that all used events are defined + for (const auto& [eventName, eventIndex] : this->_eventIndexes) + { + if (!definedEvents.contains(eventIndex)) + { + throw std::runtime_error("Event \"" + eventName + "\" is used but never defined."); + } + } + + // Make sure all used areas are defined + for (const auto& [areaName, area] : this->_areaTable) + { + if (!definedAreas.contains(areaName)) + { + throw std::runtime_error("Area \"" + areaName + "\" is used but never defined."); + } + } + + // Pass a pointer for each exit to the entrance list for the area it connects to + for (const auto& [areaName, area] : this->_areaTable) + { + for (const auto& exit : area->GetExits()) + { + exit->GetConnectedArea()->AddEntrance(exit); + } + } + } + + bool World::EvaluateSettingCondition(const std::string& condition) + { + auto req = requirement::ParseRequirementString(condition, this, true); + return requirement::EvaluateSimpleRequirement(req, this); + } + + void World::GenerateItemPools() + { + LOG_TO_DEBUG("Now building item pools"); + item_pool::GenerateItemPool(this); + item_pool::GenerateStartingItemPool(this); + + LOG_TO_DEBUG("Item Pool for world " + std::to_string(this->GetID()) + ":"); + for (const auto& item : this->_itemPool) + { + LOG_TO_DEBUG("- " + item->GetName()); + } + LOG_TO_DEBUG("Starting Inventory for world " + std::to_string(this->GetID()) + ":"); + for (const auto& item : this->_startingItemPool) + { + LOG_TO_DEBUG("- " + item->GetName()); + } + } + + bool World::ShouldRemoveLocation(const std::string& locationName, const std::string& originalItemName) + { + // Twilight Tears + if (originalItemName == "Faron Twilight Tear" && this->Setting("Faron Twilight Cleared") == "On") + { + LOG_TO_DEBUG("Removing " + locationName + " because Faron Twilight is cleared."); + return true; + } + + if (originalItemName == "Eldin Twilight Tear" && this->Setting("Eldin Twilight Cleared") == "On") + { + LOG_TO_DEBUG("Removing " + locationName + " because Eldin Twilight is cleared."); + return true; + } + + if (originalItemName == "Lanayru Twilight Tear" && this->Setting("Lanayru Twilight Cleared") == "On") + { + LOG_TO_DEBUG("Removing " + locationName + " because Lanayru Twilight is cleared."); + return true; + } + + // Ilia Memory Quest + const auto& iliaQuest = this->Setting("Ilia Memory Quest"); + if ((iliaQuest >= "Letter" && locationName == "Renados Letter") || + (iliaQuest >= "Invoice" && locationName == "Telma Invoice") || + (iliaQuest >= "Statue" && locationName == "Wooden Statue") || + (iliaQuest >= "Charm" && locationName == "Ilia Charm")) + { + LOG_TO_DEBUG("Removing " + locationName + " because Ilia Memory Quest is " + iliaQuest.GetCurrentOption() + "."); + return true; + } + + return false; + } + + void World::PerformPreEntranceShuffleTasks() + { + this->PlaceVanillaItems(); + this->SetNonProgressLocations(); + this->SanitizeItemPool(); + this->PlacePlandomizerItems(); + } + + void World::PlaceVanillaItems() + { + LOG_TO_DEBUG("Now placing vanilla items"); + + for (auto& [locationName, location] : this->_locationTable) + { + auto originalItem = location->GetOriginalItem(); + auto originalItemName = originalItem->GetName(); + + // Place all vanilla items + // Vanilla Small Keys + if ((this->Setting("Small Keys") == "Vanilla" && + (originalItem->IsDungeonSmallKey() || + utility::str::Contains(originalItemName, "Ordon Pumpkin", "Ordon Cheese"))) || + // Vanilla Big Keys (only include Hyrule Castle Big Key if it has no requirements) + (this->Setting("Big Keys") == "Vanilla" && originalItem->IsBigKey() && + (originalItemName != "Hyrule Castle Big Key" || this->Setting("Hyrule Castle Big Key Requirements") == "None")) || + // Vanilla Maps and Compasses + (this->Setting("Maps and Compasses") == "Vanilla" && + (originalItem->IsDungeonMap() || originalItem->IsCompass())) || + // Hyrule Castle Big Key + (originalItemName == "Hyrule Castle Big Key" && this->Setting("Hyrule Castle Big Key Requirements") != "None") || + // Vanilla Poe Souls + (originalItemName == "Poe Soul" && + (this->Setting("Poe Souls") == "Vanilla" || + (this->Setting("Poe Souls") == "Dungeon" && location->HasCategories("Overworld")) || + (this->Setting("Poe Souls") == "Overworld" && location->HasCategories("Dungeon")))) || + // Vanilla Golden Bugs + (this->Setting("Golden Bugs") == "Off" && location->HasCategories("Golden Bug")) || + // Sky Characters + (this->Setting("Sky Characters") == "Off" && location->HasCategories("Sky Character")) || + // NPC Gifts + (this->Setting("Gifts From NPCs") == "Off" && location->HasCategories("Npc")) || + // Shop Items + (this->Setting("Shop Items") == "Off" && location->HasCategories("Shop")) || + // Hidden Skills + (this->Setting("Hidden Skills") == "Off" && location->HasCategories("Golden Wolf")) || + // Hidden Rupees + (this->Setting("Hidden Rupees") == "Off" && location->HasCategories("Rupee - Hidden")) || + // Freestanding Rupees + (this->Setting("Freestanding Rupees") == "Off" && location->HasCategories("Rupee - Freestanding")) || + // Warp Portals + (location->HasCategories("Warp Portal")) || + // Some locations which will always be vanilla for the time being + (utility::str::Contains(locationName, + "Renados Letter", + "Telma Invoice", + "Wooden Statue", + "Ilia Charm", + "Defeat Ganondorf", + "Twilit Insect", + "Twilit Bloat"))) + { + // Change bottled items to all be empty bottles. It's much easier logically to only have to worry about a single + // item as a bottle instead of all bottled items as bottles for the search algorithm. Other contents will + // replace the empty bottles after all items have been placed + if (originalItem->IsBottle()) + { + originalItem = this->GetItem("Empty Bottle"); + } + + // Don't place stamps for now + if (originalItem->IsStamp()) + { + originalItem = this->GetItem("Purple Rupee"); + } + + location->SetCurrentItem(originalItem); + location->SetKnownVanillaItem(true); + utility::container::Erase(this->_itemPool, originalItem); + } + } + } + + void World::PlacePlandomizerItems() + { + for (auto& [location, item] : this->_plandomizerLocations) + { + if (!location->IsEmpty()) + { + throw std::runtime_error("Cannot plandomize \"" + item->GetName() + "\" at \"" + location->GetName() + + "\" because vanilla item \"" + location->GetCurrentItem()->GetName() + + "\" already exists there."); + } + location->SetCurrentItem(item); + utility::container::Erase(this->_itemPool, item); + } + + // If no world has entrance randomizer enabled, check to see if our plandomized item placements work + if (std::ranges::none_of(this->GetRandomizer()->GetWorlds(), [](const auto& world) { + return world->AnyEntranceRandomizerEnabled(); + })) { + if (!this->_plandomizerLocations.empty() && Setting("Logic Rules") != "No Logic") { + auto& worlds = this->GetRandomizer()->GetWorlds(); + auto completeItemPool = item_pool::GetCompleteItemPool(worlds); + auto verifyLogicError = search::VerifyLogic(&worlds, completeItemPool); + if (verifyLogicError.has_value()) + { + throw std::runtime_error("Plandomizer item placements do not work! Reason:\n" + verifyLogicError.value()); + } + } + } + } + + void World::SetNonProgressLocations() + { + LOG_TO_DEBUG("Now setting nonprogress locations for world " + std::to_string(this->GetID())); + + // Any manually excluded locations are nonprogress + for (const auto& locationName : this->_settings.GetExcludedLocations()) + { + auto location = this->GetLocation(locationName); + location->SetProgression(false); + } + + // Some locations not being randomized can conflict with other settings. When + // the appropriate location and setting conflict, these locations should have their item + // removed and be set to nonprogress. + for (auto& [locationName, location] : this->_locationTable) + { + auto originalItem = location->GetOriginalItem(); + auto originalItemName = originalItem->GetName(); + + // If an NPC gives a key when not randomized, but keys are keysy (keys shouldn't exist) + if ((this->Setting("Gifts From NPCs") == "Off" && location->HasCategories("Npc") && + ((this->Setting("Small Keys") == "Keysy" && originalItem->IsDungeonSmallKey()) || + (this->Setting("Big Keys") == "Keysy" && originalItem->IsBigKey()) || + (this->Setting("Maps and Compasses") == "Start With" && + (originalItem->IsDungeonMap() || originalItem->IsCompass())))) || + // Sky Characters are not randomized, but City in the Sky doesn't require Sky Book Characters (Sky characters + // shouldn't exist) + (this->Setting("Sky Characters") == "Off" && this->Setting("City Does Not Require Filled Skybook") == "On" && + location->HasCategories("Sky Character")) || + // We're starting with a shop item, but shop items aren't randomized + (this->Setting("Shop Items") == "Off" && location->HasCategories("Shop") && + utility::container::ElementInContainer(this->_startingItemPool, originalItem))) + { + location->RemoveCurrentItem(); + location->SetKnownVanillaItem(false); + location->SetProgression(false); + } + } + } + + void World::SetTrackerNonProgressLocations() { + for (auto& [locationName, location] : this->_locationTable) { + auto originalItemName = location->GetOriginalItem()->GetName(); + // Poe Souls + if ((originalItemName == "Poe Soul" && + (this->Setting("Poe Souls") == "Vanilla" || + (this->Setting("Poe Souls") == "Dungeon" && location->HasCategories("Overworld")) || + (this->Setting("Poe Souls") == "Overworld" && location->HasCategories("Dungeon")))) || + // Vanilla Golden Bugs + (this->Setting("Golden Bugs") == "Off" && location->HasCategories("Golden Bug")) || + // Sky Characters + (this->Setting("Sky Characters") == "Off" && location->HasCategories("Sky Character")) || + // NPC Gifts + (this->Setting("Gifts From NPCs") == "Off" && location->HasCategories("Npc")) || + // Shop Items + (this->Setting("Shop Items") == "Off" && location->HasCategories("Shop")) || + // Hidden Skills + (this->Setting("Hidden Skills") == "Off" && location->HasCategories("Golden Wolf")) || + // Hidden Rupees + (this->Setting("Hidden Rupees") == "Off" && location->HasCategories("Rupee - Hidden")) || + // Freestanding Rupees + (this->Setting("Freestanding Rupees") == "Off" && location->HasCategories("Rupee - Freestanding"))) { + location->SetProgression(false); + } + } + } + + void World::PerformPostEntranceShuffleTasks() + { + this->AssignAreaProperties(); + this->AssignGoalLocations(); + this->DetermineDungeonDependentLocations(); + this->SetForbiddenItems(); + } + + void World::AssignAreaProperties() + { + for (auto& [areaName, area] : this->_areaTable) + { + area->AssignHintRegionsAndDungeonLocations(); + } + + for (auto& [areaName, area] : this->_areaTable) + { + // Also assign dungeons their starting entrance + for (const auto& exit : area->GetExits()) + { + auto parentRegions = exit->GetParentArea()->GetHintRegions(); + auto connectedRegions = exit->GetConnectedArea()->GetHintRegions(); + for (auto& [dungeonName, dungeon] : this->_dungeons) + { + // If this exit leads into a dungeon and its parent area is not part of the dungeon + // then this is the entrance that leads into the dungeon + if (connectedRegions.contains(dungeonName) && !parentRegions.contains(dungeonName)) + { + dungeon->AddStartingEntrance(exit); + } + } + } + } + } + + void World::AssignGoalLocations() + { + std::unordered_map dungeonGoalLocations = {}; + for (const auto& [dungeonName, dungeon] : this->_dungeons) + { + dungeonGoalLocations[dungeonName] = {}; + } + // Collect all the possible goal locations for each dungeon + for (auto& [areaName, area] : this->_areaTable) + { + for (const auto& locAcc : area->GetLocations()) + { + auto location = locAcc->GetLocation(); + if (location->IsGoalLocation()) + { + for (const auto& region : area->GetHintRegions()) + { + if (dungeonGoalLocations.contains(region)) + { + dungeonGoalLocations.at(region).push_back(location); + } + } + } + } + } + + // Set a single goal location for each dungeon + for (auto& [dungeonName, dungeon] : this->_dungeons) + { + auto& possibleGoalLocations = dungeonGoalLocations.at(dungeonName); + // If a goal location becomes unreachable due to beatable only logic, then it's possible a dungeon may not be + // assigned a goal location. Dungeons without a goal location cannot be chosen as required dungeons. + if (!possibleGoalLocations.empty()) + { + dungeon->SetGoalLocation(utility::random::RandomElement(possibleGoalLocations)); + } + else + { + LOG_TO_DEBUG("No goal location could be chosen for " + dungeonName); + } + } + } + + void World::SetForbiddenItems() + { + // Prevent small keys from appearing on bosses if the setting is on + if (this->Setting("No Small Keys on Bosses") == "On") + { + // Gather all boss locations (heart container and dungeon reward checks) + auto bossLocations = this->GetAllLocations(); + utility::container::FilterAndEraseFromVector( + bossLocations, + [](const auto& location) + { return !utility::str::Contains(location->GetName(), "Heart Container", "Dungeon Reward"); }); + + // Gather all small key items + item_pool::ItemPool smallKeys = {}; + for (const auto& [itemName, item] : this->_itemTable) + { + if (item->IsDungeonSmallKey() || utility::general::IsAnyOf(itemName, + "Ordon Pumpkin", + "Ordon Cheese", + "North Faron Woods Gate Key", + "Gerudo Desert Bulblin Camp Key")) + { + smallKeys.push_back(item.get()); + } + } + + // Set the small keys as forbidden on the boss locations + for (auto& location : bossLocations) + { + for (const auto& smallKey : smallKeys) + { + location->AddForbiddenItem(smallKey); + } + } + } + } + + void World::DetermineDungeonDependentLocations() + { + for (const auto& [dungeonName, dungeon] : this->_dungeons) + { + // Hyrule Castle is implicitly required + if (dungeonName == "Hyrule Castle") { + continue; + } + + // Disable the dungeon's starting entrances + for (auto& entrance : dungeon->GetStartingEntrances()) + { + entrance->SetDisbled(true); + } + + // Run an accessibility search to see which locations inherently require accessing this dungeon + auto completeItemPool = item_pool::GetCompleteItemPool(this->_randomizer->GetWorlds()); + auto search = search::Search::Accessible(&this->_randomizer->GetWorlds(), completeItemPool); + search.SearchWorlds(); + for (auto& location : this->_locationTable | std::ranges::views::values) { + // Don't check locations which are part of this dungeon + if (utility::container::ElementInContainer(dungeon->GetLocations(), location.get())) { + continue; + } + + // If the search does not contain this location, then the location is dependent on accessing this dungeon + if (!search._visitedLocations.contains(location.get())) { + dungeon->AddOutsideDependentLocation(location.get()); + } + } + + // Re-enable the dungeon's entrances + for (auto& entrance : dungeon->GetStartingEntrances()) + { + entrance->SetDisbled(false); + } + } + } + + // For no logic, we're purely going to base whether the dungeon is required on the Hyrule Castle + // Barrier requirements and Hyrule Castle Big Key chest requirements + bool World::IsNoLogicRequiredDungeon(const std::unique_ptr& dungeon) { + auto& barrierRequirements = this->Setting("Hyrule Barrier Requirements"); + auto& bigKeyRequirements = this->Setting("Hyrule Castle Big Key Requirements"); + auto barrierDungeonCount = this->Setting("Hyrule Barrier Dungeons").GetCurrentOptionAsNumber(); + auto bigkeyDungeonCount = this->Setting("Hyrule Castle Big Key Dungeons").GetCurrentOptionAsNumber(); + + // If all dungeons are required, then always return true + if ((barrierRequirements == "Dungeons" && barrierDungeonCount == 8) || + (bigKeyRequirements == "Dungeons" && bigkeyDungeonCount == 8)) + { + return true; + } + + bool dungeonHasFusedShadow = std::ranges::any_of(dungeon->GetLocations(), [](const auto& location) { + return location->GetCurrentItem()->GetName() == "Progressive Fused Shadow"; + }); + bool dungeonHasMirrorShard = std::ranges::any_of(dungeon->GetLocations(), [](const auto& location) { + return location->GetCurrentItem()->GetName() == "Progressive Mirror Shard"; + }); + + if (dungeonHasFusedShadow && (barrierRequirements == "Fused Shadows" || bigKeyRequirements == "Fused Shadows")) { + return true; + } + + if (dungeonHasMirrorShard && (barrierRequirements == "Mirror Shards" || bigKeyRequirements == "Mirror Shards")) { + return true; + } + + if (barrierRequirements == "Vanilla" && (dungeon->GetName() == "Palace of Twilight" || + (this->Setting("Palace of Twilight Requirements") == "Vanilla" && dungeon->GetName() == "City in the Sky"))) + { + return true; + } + + return false; + } + + void World::DetermineRequiredDungeons() + { + for (const auto& [dungeonName, dungeon] : this->_dungeons) + { + // To determine if a dungeon is required, we're going to disable all of its entrances and then check to see + // that the game is still beatable. If the game is not beatable with the dungeon entrances disabled, then the + // dungeon is required. For no logic, we determine required dungeons differently since otherwise no dungeon + // would be required. + + // Hyrule Castle is implicitly required + if (dungeonName == "Hyrule Castle") { + continue; + } + + // Disable the dungeon's starting entrances + for (auto& entrance : dungeon->GetStartingEntrances()) + { + entrance->SetDisbled(true); + } + + // Check if the game is beatable, set dungeon as required if so. If the dungeon is not required and barren + // unrequired dungeons is on, then set all the locations in the unrequired dungeon as nonprogress. + auto completeItemPool = item_pool::GetCompleteItemPool(this->_randomizer->GetWorlds()); + if (!search::GameBeatable(&this->_randomizer->GetWorlds(), completeItemPool) || + (this->Setting("Logic Rules") == "No Logic" && this->IsNoLogicRequiredDungeon(dungeon))) + { + dungeon->SetRequired(true); + } + else if (this->Setting("Unrequired Dungeons Are Barren") == "On") + { + for (auto& location : dungeon->GetLocations()) + { + location->SetProgression(false); + } + for (auto& location : dungeon->GetOutsideDependentLocations()) + { + location->SetProgression(false); + } + } + + // Re-enable the dungeon's entrances + for (auto& entrance : dungeon->GetStartingEntrances()) + { + entrance->SetDisbled(false); + } + } + } + + void World::SanitizeItemPool() + { + auto junkPool = item_pool::GetInitialJunkPool(); + + // Depending on the Trap item Frequency setting, add some amount of ice traps to the pool + if (this->Setting("Trap Item Frequency") == "Few") + { + junkPool.emplace("Foolish Item", 6); + } + else if (this->Setting("Trap Item Frequency") == "Many") + { + junkPool.emplace("Foolish Item", 27); + } + else if (this->Setting("Trap Item Frequency") == "Mayhem") + { + junkPool.emplace("Foolish Item", 64); + } + else if (this->Setting("Trap Item Frequency") == "Nightmare") + { + junkPool.clear(); + junkPool.emplace("Foolish Item", 1); + } + + // Create an actual item pool from the junk items + item_pool::ItemPool mainJunkPool = {}; + for (const auto& [itemName, count] : junkPool) + { + auto item = this->GetItem(itemName); + for (auto i = 0; i < count; i++) + { + mainJunkPool.push_back(item); + } + } + + auto allItemLocations = this->GetAllLocations(); + const auto numEmptyLocations = std::ranges::count_if(allItemLocations, [](const auto& location) { + return location->IsEmpty(); + }); + + // Create a copy of the real pool we just made. When adding junk items we want to add all the items from the junk pool + // once if possible, then if there's more space left pick randomly from the full pool + auto mainJunkPoolCopy = mainJunkPool; + + // Add items until the pool's size matches the number of empty locations + while (this->_itemPool.size() < numEmptyLocations) + { + item::Item* randomJunkItem; + if (!mainJunkPool.empty()) + { + randomJunkItem = utility::random::PopRandomElement(mainJunkPool); + } + else + { + randomJunkItem = utility::random::RandomElement(mainJunkPoolCopy); + } + this->_itemPool.emplace_back(randomJunkItem); + LOG_TO_DEBUG("Added junk item \"" + randomJunkItem->GetName() + "\" to item pool for world " + + std::to_string(this->GetID())); + } + } + + void World::SetSearchStartingProperties(search::Search* search) const + { + // Set the root area to have all player forms and times of day (necessary for entrance rando validation) + const auto root = this->GetRootArea(); + search->_areaFormTime[root] = requirement::FormTime::ALL; + } + + void World::PerformPostFillTasks() + { + this->FinalizeBottleContents(); + } + + void World::FinalizeBottleContents() + { + // Replace 3 bottles with other bottle contents we currently use. + const auto bottleWithGreatFairiesTears = this->GetItem("Bottle with Great Fairies Tears"); + const auto bottleWithHalfMilk = this->GetItem("Bottle with Half Milk"); + const auto bottleWithLanternOil = this->GetItem("Bottle with Lantern Oil"); + const auto emptyBottle = this->GetItem("Empty Bottle"); + item_pool::ItemPool bottlePool = {bottleWithGreatFairiesTears, + bottleWithHalfMilk, + bottleWithLanternOil, + emptyBottle}; + + // If npc gifts are vanilla, then set those vanilla bottles appropriately + if (this->Setting("Gifts From NPCs") == "Off") + { + for (auto& [locationName, location] : this->_locationTable) + { + auto originalItem = location->GetOriginalItem(); + if (location->HasCategories("Npc") && originalItem->IsBottle()) + { + location->SetCurrentItem(originalItem); + } + } + } + // Otherwise gather all the locations which have a bottle and replace the bottles at those locations instead + else + { + // Gather the bottle locations + location::LocationPool bottleLocations = {}; + for (auto& [locationName, location] : this->_locationTable) + { + auto originalItem = location->GetCurrentItem(); + if (originalItem->IsBottle()) + { + bottleLocations.push_back(location.get()); + } + } + + // Place the new bottle items + utility::random::ShufflePool(bottleLocations); + for (auto& bottleLocation : bottleLocations) + { + if (!bottlePool.empty()) { + bottleLocation->SetCurrentItem(utility::random::PopRandomElement(bottlePool)); + } else { + bottleLocation->SetCurrentItem(this->GetItem("Empty Bottle")); + } + } + } + } + + void World::AddPlandomizedLocation(location::Location* location, item::Item* item) + { + if (this->_plandomizerLocations.contains(location)) + { + throw std::runtime_error("Plandomizer Error: multiple entries for \"" + location->GetName() + "\" in world " + + std::to_string(this->_id)); + } + this->_plandomizerLocations[location] = item; + } + + void World::AddPlandomizedEntrance(entrance::Entrance* entrance, entrance::Entrance* target) + { + for (const auto& [plandoEntrance, plandoTarget] : this->_plandomizerEntrances) + { + if (plandoEntrance == entrance) + { + throw std::runtime_error("Plandomizer Error: multiple entries for \"" + entrance->GetOriginalName() + + "\" in world " + std::to_string(this->_id)); + } + if (plandoTarget == target) + { + throw std::runtime_error("Plandomizer Error: multiple entrances target \"" + target->GetOriginalName() + + "\" in world " + std::to_string(this->_id)); + } + } + this->_plandomizerEntrances[entrance] = target; + } + + std::unordered_map World::GetPlandomizerEntrances() + { + return this->_plandomizerEntrances; + } + + dungeon::Dungeon* World::GetDungeon(const std::string& name) + { + if (!this->_dungeons.contains(name)) + { + this->_dungeons.emplace(name, std::make_unique(name, this)); + LOG_TO_DEBUG("Added new dungeon \"" + name + "\" to world " + std::to_string(this->_id)); + } + return this->_dungeons.at(name).get(); + } + + const std::map>& World::GetDungeonTable() const + { + return this->_dungeons; + } + + item::Item* World::GetItem(const std::string& name, const bool& ignoreError /*= false*/) + { + if (name == "Nothing") + { + return item::Nothing.get(); + } + + if (!this->_itemTable.contains(name)) + { + if (!ignoreError) + { + throw std::runtime_error("Unknown item name \"" + name + "\""); + } + return nullptr; + } + return this->_itemTable.at(name).get(); + } + + item::Item* World::GetItem(uint16_t id, const bool& ignoreError /*= false*/) { + if (!this->_itemIdTable.contains(id)) + { + if (!ignoreError) + { + throw std::runtime_error("Unknown item id \"" + std::to_string(id) + "\""); + } + return item::Nothing.get(); + } + return this->_itemIdTable.at(id); + } + + item::Item* World::GetGameWinningItem() const + { + return this->_itemTable.at("Game Beatable").get(); + } + + item::Item* World::GetShadowCrystal() + { + return this->_itemTable.at("Shadow Crystal").get(); + } + + item_pool::ItemPool& World::GetItemPool() + { + return this->_itemPool; + } + + item_pool::ItemPool& World::GetStartingItemPool() + { + return this->_startingItemPool; + } + + location::Location* World::GetLocation(const std::string& name) + { + if (!this->_locationTable.contains(name)) + { + throw std::runtime_error("Unknown location name \"" + name + "\""); + } + return this->_locationTable.at(name).get(); + } + + location::LocationPool World::GetAllLocations(const bool& includeNonItemLocations /* = false */) + { + location::LocationPool locationPool = {}; + for (const auto& [locationName, location] : this->_locationTable) + { + if (includeNonItemLocations || !location->HasCategories("Non-Item Location")) + { + locationPool.emplace_back(location.get()); + } + } + return locationPool; + } + + area::Area* World::GetArea(const std::string& name, const bool& createIfNotFound /* = false */) + { + if (!this->_areaTable.contains(name)) + { + if (createIfNotFound) + { + this->_areaTable.emplace(name, std::make_unique(name, this)); + } + else + { + throw std::runtime_error("Unknown area name \"" + name + "\""); + } + } + return this->_areaTable.at(name).get(); + } + + area::Area* World::GetRootArea() const + { + return this->_areaTable.at("Root").get(); + } + + const std::map>& World::GetAreaTable() const + { + return this->_areaTable; + } + + entrance::Entrance* World::GetEntrance(const std::string& originalName) + { + auto [parentAreaName, connectedAreaName] = entrance::GetParentAndConnectedAreaNames(originalName); + auto parentArea = this->GetArea(parentAreaName); + auto connectedArea = this->GetArea(connectedAreaName); + for (const auto& exit : parentArea->GetExits()) + { + if (exit->GetOriginalConnectedArea() == connectedArea) + { + return exit; + } + } + + throw std::runtime_error("\"" + originalName + "\" is not a known connection"); + } + + int World::GetNewEntranceID() + { + return this->_entranceIdCounter++; + } + + entrance::EntrancePool World::GetShuffleableEntrances(const entrance::Type& type, + bool onlyPrimary /* = false */) + { + entrance::EntrancePool shuffleableEntrances = {}; + for (const auto& [areaName, area] : this->GetAreaTable()) + { + for (const auto& exit : area->GetExits()) + { + if ((type == exit->GetType() || type == entrance::Type::ALL) && + (!onlyPrimary || exit->IsPrimary()) && exit->GetType() != entrance::Type::INVALID) + { + shuffleableEntrances.push_back(exit); + } + } + } + return shuffleableEntrances; + } + + entrance::EntrancePool World::GetShuffledEntrances( + const entrance::Type& type /* = entrance::Type::ALL */, + bool onlyPrimary /* = false */) + { + auto entrances = this->GetShuffleableEntrances(type, onlyPrimary); + + // Remove any entrances which aren't shuffled + utility::container::FilterAndEraseFromVector(entrances, [](const auto& e) { return !e->IsShuffled(); }); + + return entrances; + } + + std::unordered_map& World::GetExitTimeFormCache() + { + return this->_exitTimeFormCache; + } + + int World::GetMacroIndex(const std::string& macroName) const + { + if (this->_macroIndexes.contains(macroName)) + { + return this->_macroIndexes.at(macroName); + } + return -1; + } + + const requirement::Requirement& World::GetMacro(const int& macroIndex) + { + return this->_macros.at(macroIndex); + } + + int World::GetEventIndex(const std::string& eventName, bool addIfNone /*= true*/) + { + // If the event doesn't exist + if (!this->_eventIndexes.contains(eventName)) + { + if (addIfNone) + { + auto index = this->_randomizer->GetNewEventID(); + this->_eventIndexes.emplace(eventName, index); + this->_eventNames.emplace(index, eventName); + LOG_TO_DEBUG("Event \"" + eventName + "\" was assigned eventIndex " + std::to_string(index)); + } + else + { + throw std::runtime_error("Event \"" + eventName + "\" does not exist"); + } + } + + return this->_eventIndexes.at(eventName); + } + + std::string World::GetEventName(const int& eventIndex) + { + if (!this->_eventNames.contains(eventIndex)) + { + LOG_TO_ERROR("Invalid Event Index"); + } + return this->_eventNames.at(eventIndex); + } + + seedgen::settings::Setting& World::Setting(const std::string& settingName) + { + auto& settings = this->_settings; + // Check to make sure the setting exists + if (!settings.GetMap().contains(settingName)) + { + throw std::runtime_error("Setting \"" + settingName + "\" is not a known setting"); + } + return settings.GetMap().at(settingName); + } + + bool World::AnyEntranceRandomizerEnabled() { + return Setting("Randomize Starting Spawn") != "Off" || + Setting("Randomize Dungeon Entrances") != "Off" || + Setting("Randomize Boss Entrances") != "Off" || + Setting("Randomize Grotto Entrances") != "Off" || + Setting("Randomize Cave Entrances") != "Off" || + Setting("Randomize Interior Entrances") != "Off" || + Setting("Randomize Overworld Entrances") != "Off"; + } +} // namespace randomizer::logic::world diff --git a/mods/randomizer/generator/logic/world.hpp b/mods/randomizer/generator/logic/world.hpp new file mode 100644 index 0000000000..4b7beaacb7 --- /dev/null +++ b/mods/randomizer/generator/logic/world.hpp @@ -0,0 +1,197 @@ +#pragma once + +#include "area.hpp" +#include "dungeon.hpp" +#include "item.hpp" +#include "item_pool.hpp" +#include "location.hpp" +#include "requirement.hpp" + +#include "../seedgen/settings.hpp" +#include "../utility/log.hpp" +#include "../utility/text.hpp" + +#include +#include +#include +#include + +// Forward Declarations +namespace randomizer +{ + class Randomizer; +} + +namespace randomizer::logic::search +{ + class Search; +} + +namespace randomizer::logic::world +{ + class World; + using WorldPool = std::vector>; + + class World + { + public: + World(const int& id, Randomizer* randomizer); + + int GetID() const; + void SetSettings(const seedgen::settings::Settings& settings); + const seedgen::settings::Settings& GetSettings() const; + void SetRandomizer(Randomizer* randomizer); + Randomizer* GetRandomizer() const; + + /** + * @brief Resolves all remaining random settings within a specific world + */ + void ResolveRandomSettings(); + + /** + * @brief Resolves settings that conflict with each other. Ideally will only resolve settings that conflict due to + * having their current option randomly chosen. + */ + void ResolveConflictingSettings(); + void Build(); + void BuildItemTable(); + void BuildLocationTable(); + void LoadLogicMacros(); + void LoadWorldGraph(); + bool EvaluateSettingCondition(const std::string& condition); + + /** + * @brief Generate the main item pool and starting item pool for this world. + */ + void GenerateItemPools(); + + /** + * @brief Decides if a location should be removed depending on settings. + * @param locationName The name of the location + * @param originalItemName The name of the original item at the location + * + * @return true if the location should be removed. False otherwise + */ + bool ShouldRemoveLocation(const std::string& locationName, const std::string& originalItemName); + + /** + * @brief Perform all tasks which must be complete before shuffling entrances. + */ + void PerformPreEntranceShuffleTasks(); + void PlaceVanillaItems(); + void PlacePlandomizerItems(); + void SetNonProgressLocations(); + void SetTrackerNonProgressLocations(); + + /** + * @brief Perform all tasks which require shuffled entrances to be set, but before running the main item placement + * algorithm. + */ + void PerformPostEntranceShuffleTasks(); + void AssignAreaProperties(); + void AssignGoalLocations(); + + /** + * @brief Forbid items from being in certain locations depending on settings + */ + void SetForbiddenItems(); + + /** + * @brief STUB: Would choose required dungeons ahead of placing any non-vanilla and non-plandomized items. Not really + * required unless we let users choose a specific amount of directly required dungeons + */ + void DetermineDungeonDependentLocations(); + + bool IsNoLogicRequiredDungeon(const std::unique_ptr& dungeon); + + /** + * @brief Determines which dungeons are required based on placed items. Sets required dungeons as such in their + * properties. If "Unrequired Dungeons Are Barren" is "On", then unrequired dungeons will have all their locations + * progression status set to false. + */ + void DetermineRequiredDungeons(); + + /** + * @brief Adds junk to the main pool until the number of items in the pool matches the total number of + * currently empty locations. + */ + void SanitizeItemPool(); + void SetSearchStartingProperties(search::Search* search) const; + void PerformPostFillTasks(); + void FinalizeBottleContents(); + void AddPlandomizedLocation(location::Location* location, item::Item* item); + void AddPlandomizedEntrance(entrance::Entrance* entrance, entrance::Entrance* target); + std::unordered_map GetPlandomizerEntrances(); + + dungeon::Dungeon* GetDungeon(const std::string& name); + const std::map>& GetDungeonTable() const; + item::Item* GetItem(const std::string& name, const bool& ignoreError = false); + item::Item* GetItem(uint16_t id, const bool& ignoreError = false); + item::Item* GetShadowCrystal(); + item::Item* GetGameWinningItem() const; + item_pool::ItemPool& GetItemPool(); + item_pool::ItemPool& GetStartingItemPool(); + location::Location* GetLocation(const std::string& name); + location::LocationPool GetAllLocations(const bool& includeNonItemLocations = false); + area::Area* GetArea(const std::string& name, const bool& createIfNotFound = false); + area::Area* GetRootArea() const; + const std::map>& GetAreaTable() const; + entrance::Entrance* GetEntrance(const std::string& originalName); + int GetNewEntranceID(); + entrance::EntrancePool GetShuffleableEntrances(const entrance::Type& type, + bool onlyPrimary = false); + entrance::EntrancePool GetShuffledEntrances( + const entrance::Type& type = entrance::Type::ALL, + bool onlyPrimary = false); + std::unordered_map& GetExitTimeFormCache(); + + int GetMacroIndex(const std::string& macroName) const; + const requirement::Requirement& GetMacro(const int& macroIndex); + int GetEventIndex(const std::string& eventName, bool addIfNone = true); + std::string GetEventName(const int& eventIndex); + + seedgen::settings::Setting& Setting(const std::string& settingName); + bool AnyEntranceRandomizerEnabled(); + + TextDatabase& GetTextDatabase() { return this->_textDatabase; } + const std::string& GetText(const std::string& name, Text::Type type = Text::STANDARD, Text::Language language = Text::ENGLISH) { + if (!this->_textDatabase.at(name).at(type).mText.at(language).empty()) { + return this->_textDatabase.at(name).at(type).mText.at(language); + } + + return this->_textDatabase.at(name).at(type).mText.at(Text::ENGLISH); + } + // Make a new custom text entry for this world specifically and return a reference to it + Text& AddNewText(const std::string& name, Text::Type type = Text::STANDARD) { + return this->_textDatabase[name][type]; + } + + private: + int _id = -1; + int _entranceIdCounter = 0; + + seedgen::settings::Settings _settings; + std::map> _itemTable = {}; + std::map _itemIdTable = {}; + std::map> _locationTable = {}; + std::unordered_set _intentionallyRemovedLocations = {}; + std::unordered_set _registeredLocationCategories = {}; + std::map> _areaTable = {}; + std::map> _dungeons = {}; + std::map _macros = {}; + std::unordered_map _macroIndexes = {}; + std::unordered_map _eventIndexes = {}; + std::unordered_map _eventNames = {}; + item_pool::ItemPool _itemPool = {}; + item_pool::ItemPool _startingItemPool = {}; + std::unordered_map _exitTimeFormCache = {}; + // Custom text for this world specifically + TextDatabase _textDatabase = {}; + + // Plandomizer Data + std::unordered_map _plandomizerLocations = {}; + std::unordered_map _plandomizerEntrances = {}; + + Randomizer* _randomizer = nullptr; + }; +} // namespace randomizer::logic::world diff --git a/mods/randomizer/generator/randomizer.cpp b/mods/randomizer/generator/randomizer.cpp new file mode 100644 index 0000000000..7f8cec0adf --- /dev/null +++ b/mods/randomizer/generator/randomizer.cpp @@ -0,0 +1,173 @@ +#include "randomizer.hpp" + +#include "logic/entrance_shuffle.hpp" +#include "logic/fill.hpp" +#include "logic/flatten/flatten.hpp" +#include "logic/hints.hpp" +#include "logic/plandomizer.hpp" +#include "logic/search.hpp" +#include "logic/spoiler_log.hpp" +#include "logic/world.hpp" +#include "seedgen/config.hpp" +#include "seedgen/settings.hpp" +#include "utility/time.hpp" + +#include + +#include "../src/paths.hpp" +#include "../src/randomizer_context.hpp" + +namespace randomizer +{ + logic::world::World* Randomizer::GetWorld(int worldId /*= 1*/) { + auto worldIndex = worldId - 1; + if (worldIndex < this->_worlds.size()) { + return this->_worlds.at(worldIndex).get(); + } + + return nullptr; + } + + std::optional Randomizer::Generate() + { + try + { + GenerateWorlds(); + } + catch(const std::exception& e) + { + std::cout << "============================================================" << std::endl; + std::cout << "The following exception occured: " << e.what() << std::endl; + return e.what(); + } + + return std::nullopt; + } + + void Randomizer::GenerateTrackerWorld() { + auto contextHash = randomizer_GetContext().mHash; + + if (contextHash.empty()) { + return; + } + + std::filesystem::path seedSettings = ::randomizer::paths::GetRandomizerSeedsPath() / + contextHash / (contextHash + " Anti-Spoiler Log.txt"); + + this->_config.LoadFromFile(seedSettings, GetPrefPath()); + this->_config.SetHash(contextHash); + + std::unique_ptr world = std::make_unique(1, this); + world->SetSettings(this->_config.GetSettingsList().front()); + // Always use logic when building a tracker world + world->Setting("Logic Rules").SetCurrentOption("All Locations Reachable"); + world->Build(); + this->_worlds.emplace_back(std::move(world)); + + auto trackerWorld = this->_worlds.at(0).get(); + trackerWorld->SetNonProgressLocations(); + trackerWorld->SetTrackerNonProgressLocations(); + trackerWorld->AssignAreaProperties(); + trackerWorld->AssignGoalLocations(); + + // Cache exit form times. This *must* run before conducting the flattening search, otherwise + // the flattening search will pollute the exit timeform cache with a bunch of zeros + logic::fill::CacheExitTimeForms(this->_worlds); + + // Set raw requirements for each location + FlattenSearch search = FlattenSearch(trackerWorld); + search.doSearch(); + } + + void Randomizer::GenerateWorlds() + { + utility::time::ScopedTimer<"Seed generation took ", std::chrono::milliseconds> timer; + this->_config.LoadFromFile(GetConfigPath(), GetPrefPath()); + // Set permalink now so that resolving random settings doesn't change it + this->_config.SetPermalink(this->_config.GetPermalink()); + + utility::platform::Log(std::string("Seed: ") + this->_config.GetSeed()); + + seedgen::config::SeedRNG(this->_config, true, false); + // Set the hash now before anything else random is decided. This allows us to show the hash for a seed + // before generating it later + auto hash = this->_config.GetHash(); + utility::platform::Log(std::string("Hash: ") + hash); + + // Build all worlds + int worldId = 1; + for (const auto& settings : this->_config.GetSettingsList()) + { + std::unique_ptr world = std::make_unique(worldId++, this); + world->SetSettings(settings); + world->ResolveRandomSettings(); + world->ResolveConflictingSettings(); + world->Build(); + this->_worlds.emplace_back(std::move(world)); + } + + // Process Plando Data for all worlds + if (this->_config.IsUsingPlandomizer()) + { + try { + logic::plandomizer::LoadPlandomizerData(this->_worlds, this->_config.GetPlandomizerPath()); + } catch (const std::runtime_error& e) { + throw std::runtime_error("Plandomizer Error: " + std::string(e.what())); + } + } + + // Pre Entrance Shuffle Tasks + for (auto& world : this->_worlds) + { + world->PerformPreEntranceShuffleTasks(); + } + + utility::platform::Log("Shuffling Entrances..."); + for (auto& world : this->_worlds) + { + logic::entrance_shuffle::ShuffleWorldEntrances(world.get()); + } + + // Post Entrance Shuffle Tasks + for (auto& world : this->_worlds) + { + world->PerformPostEntranceShuffleTasks(); + } + logic::fill::CacheExitTimeForms(this->_worlds); + + // Flattening isn't used for anything yet, but flattens down the requirements for + // each location and entrance into a single statement. This will be useful for hints and could potentially + // be used to speed up the fill algorithm (but the fill algorithm is already pretty fast, so we'd only gain maybe like + // 0.2 seconds back or something) + utility::platform::Log("Flattening..."); + FlattenSearch search = FlattenSearch(this->_worlds.at(0).get()); + search.doSearch(); + + utility::platform::Log("Filling Worlds..."); + logic::fill::FillWorlds(this->_worlds); + + // Post Fill Tasks + for (auto& world : this->_worlds) + { + world->PerformPostFillTasks(); + } + + // Generate Playthrough + logic::search::GeneratePlaythrough(this); + + // Generate Hints + logic::hints::GenerateAllHints(this->_worlds); + + // Write Logs + if (this->_config.IsGeneratingSpoilerLog()) + { + logic::spoiler_log::GenerateSpoilerLog(this); + } + logic::spoiler_log::GenerateAntiSpoilerLog(this); + } + + std::filesystem::path Randomizer::GetSeedOutputPath() + { + return this->_baseOutputPath / "seeds" / this->_config.GetHash(); + } +} // namespace randomizer diff --git a/mods/randomizer/generator/randomizer.hpp b/mods/randomizer/generator/randomizer.hpp new file mode 100644 index 0000000000..acfed47509 --- /dev/null +++ b/mods/randomizer/generator/randomizer.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "logic/world.hpp" + +#include + +namespace randomizer +{ + class Randomizer + { + public: + Randomizer() = delete; + Randomizer(const std::filesystem::path& baseOutputPath) : _baseOutputPath(baseOutputPath) {} + + /** + * @brief Generates a complete randomizer seed + * + * @return a std::optional containing a message if there was an error. + */ + std::optional Generate(); + void GenerateWorlds(); + void GenerateTrackerWorld(); + + auto& GetConfig() { return this->_config; } + auto& GetWorlds() { return this->_worlds; } + /** + * @param worldId + * @return The world with the specified Id. If no Id is specified, the first world will + * be returned. If a world with the Id does not exist, a nullptr will be returned. + */ + logic::world::World* GetWorld(int worldId = 1); + + int GetNewEventID() { return ++(this->_eventIdCounter); } + int GetNewAreaID() { return ++(this->_areaIdCounter); } + int GetNewLocAccID() { return ++(this->_locAccIdCounter); } + + auto& GetPlaythroughSpheres() { return this->_playthroughSpheres; } + auto& GetEntranceSpheres() { return this->_entranceSpheres; } + + std::filesystem::path GetSeedOutputPath(); + std::filesystem::path GetBaseOutputPath() const { return this->_baseOutputPath; }; + void SetBaseOutputPath(const std::filesystem::path& path) { this->_baseOutputPath = path; }; + + std::filesystem::path GetConfigPath() const { return this->GetBaseOutputPath() / "settings.yaml"; } + std::filesystem::path GetPrefPath() const { return this->GetBaseOutputPath() / "preferences.yaml"; } + private: + seedgen::config::Config _config{}; + logic::world::WorldPool _worlds{}; + + int _eventIdCounter{}; + int _areaIdCounter{}; + int _locAccIdCounter{}; + + // Playthrough data + std::list> _playthroughSpheres{}; + std::list> _entranceSpheres{}; + + std::filesystem::path _baseOutputPath{}; + }; +} // namespace randomizer diff --git a/mods/randomizer/generator/seedgen/config.cpp b/mods/randomizer/generator/seedgen/config.cpp new file mode 100644 index 0000000000..dc01960972 --- /dev/null +++ b/mods/randomizer/generator/seedgen/config.cpp @@ -0,0 +1,626 @@ +#include "config.hpp" + +#include "packed_bits.hpp" +#include "seed.hpp" +#include "../utility/base64pp.hpp" +#include "../utility/crc32.hpp" +#include "../utility/log.hpp" +#include "../utility/platform.hpp" +#include "../utility/random.hpp" +#include "../utility/yaml.hpp" +#include "../logic/entrance_shuffle.hpp" + +#include +#include + +// Fields which aren't part of settings_list.yaml +constexpr std::string_view SEED = "Seed"; +constexpr std::string_view PLANDOMIZER = "Plandomizer"; +constexpr std::string_view PLANDOMIZER_PATH = "Plandomizer Path"; +constexpr std::string_view STARTING_INVENTORY = "Starting Inventory"; +constexpr std::string_view EXCLUDED_LOCATIONS = "Excluded Locations"; +constexpr std::string_view MIXED_ENTRANCE_POOLS = "Mixed Entrance Pools"; +constexpr std::string_view GENERATE_SPOILER_LOG = "Generate Spoiler Log"; + +namespace randomizer::seedgen::config +{ + Config::Config() { + // Create at least one player's settings + this->_settingsList.push_front(settings::Settings()); + } + + Config::Config(const fspath& settingsPath, const fspath& preferencesPath) { + // Create at least one player's settings + this->_settingsList.push_front(settings::Settings()); + LoadFromFile(settingsPath, preferencesPath); + } + + void Config::ResetSettingsToDefault() { + for (auto& settings : this->_settingsList) { + for (auto& [settingName, setting] : settings.GetMap()) { + if (setting.GetInfo()->GetType() == settings::Type::STANDARD) { + setting.SetCurrentOption(setting.GetInfo()->GetDefaultOption()); + } + } + settings.GetModifiableExcludedLocations().clear(); + settings.GetModifiableStartingInventory().clear(); + settings.GetModifiableMixedEntrancePools().clear(); + } + } + + void Config::ResetPreferencesToDefault() { + for (auto& settings : this->_settingsList) { + for (auto& [settingName, setting] : settings.GetMap()) { + if (setting.GetInfo()->GetType() == settings::Type::PREFERENCE) { + setting.SetCurrentOption(setting.GetInfo()->GetDefaultOption()); + } + } + this->_plandomizerPath = ""; + } + } + + void Config::LoadFromFile(const fspath& settingsPath, + const fspath& preferencesPath, + bool createIfNotFound /*= true*/, + bool allowRewrite /*= true*/) + { + // Create files for settings/preferences if they don't exist + if (!std::filesystem::exists(settingsPath)) + { + if (createIfNotFound) + { + WriteSettingsToFile(settingsPath); + } + else + { + throw std::runtime_error("Could not open settings file at \"" + settingsPath.generic_string() + "\""); + } + } + + if (!std::filesystem::exists(preferencesPath)) + { + if (createIfNotFound) + { + WritePreferencesToFile(preferencesPath); + } + else + { + throw std::runtime_error("Could not open preferences file at \"" + preferencesPath.generic_string() + "\""); + } + } + + auto& settings = this->_settingsList.front(); + settings.GetModifiableExcludedLocations().clear(); + settings.GetModifiableMixedEntrancePools().clear(); + settings.GetModifiableStartingInventory().clear(); + + // Load settings info + auto settingInfoMap = settings::GetAllSettingsInfo(); + + // Read in settings and preferences. If we have to change anything, + // rewrite the appropriate file if allowed. + bool rewriteSettings = false; + auto settingsTree = LoadYAML(settingsPath); + + // Loop through all setting fields + for (const auto& settingNode : settingsTree) + { + const auto& settingName = settingNode.first.as(); + // Insert the setting if it's in the info map + if (settingInfoMap->contains(settingName)) + { + auto& settingInfo = settingInfoMap->at(settingName); + auto settingOption = settingNode.second.as(); + + // If the option doesn't exist, revert to default and rewrite later if necessary + if (settingInfo->GetIndexOfOption(settingOption) == -1) + { + utility::platform::Log(std::string("Setting \"") + settingName + "\" has no option \"" + + settingOption + "\". Reverting to default \"" + + settingInfo->GetDefaultOption() + "\""); + settingOption = settingInfo->GetDefaultOption(); + rewriteSettings = true; + } + + settings.GetMap().at(settingName).SetCurrentOption(settingOption); + } + // Special handling for starting inventory + else if (settingName == STARTING_INVENTORY) + { + for (const auto& inventoryNode : settingNode.second) + { + const auto& itemName = inventoryNode.first.as(); + const auto& count = inventoryNode.second.as(); + + settings.AddStartingItem(itemName, count); + } + } + // Special Handling for Excluded Locations + else if (settingName == EXCLUDED_LOCATIONS) + { + for (const auto& locationNode : settingNode.second) + { + const auto& locationName = locationNode.as(); + settings.AddExcludedLocation(locationName); + } + } + // Special Handling for Mixed Entrance Pools + else if (settingName == MIXED_ENTRANCE_POOLS) + { + for (const auto& poolNode : settingNode.second) + { + if (!poolNode.IsSequence()) + { + throw std::runtime_error("Mixed Entrance Pools is not a nested sequence of strings"); + } + settings.AddMixedPool(poolNode.as>()); + } + } + // Special handling for Seed + else if (settingName == SEED) + { + const auto& seed = settingNode.second.as(); + this->_seed = seed; + + // If seed is empty string, generate a new one + if (this->_seed.empty()) + { + this->_seed = seed::GenerateSeed(); + } + } + // Special handling for Plandomizer + else if (settingName == PLANDOMIZER) + { + const auto& plandomizer = settingNode.second.as(false); + this->_isUsingPlandomizer = plandomizer; + } + } + + // Loop through all preference fields + bool rewritePreferences = false; + auto preferencesTree = LoadYAML(preferencesPath); + for (const auto& preferenceNode : preferencesTree) + { + const auto& preferenceName = preferenceNode.first.as(); + // Insert the preference if it's in the info map + if (settingInfoMap->contains(preferenceName)) + { + auto& preferenceInfo = settingInfoMap->at(preferenceName); + auto preferenceOption = preferenceNode.second.as(); + + // If the option doesn't exist, revert to default and rewrite later if necessary + if (preferenceInfo->GetIndexOfOption(preferenceOption) == -1) + { + utility::platform::Log(std::string("Preference \"") + preferenceName + " has no option \"" + + preferenceOption + "\". Reverting to default \"" + + preferenceInfo->GetDefaultOption() + "\""); + preferenceOption = preferenceInfo->GetDefaultOption(); + rewritePreferences = true; + } + + settings.GetMap().at(preferenceName).SetCurrentOption(preferenceOption); + } + else if (preferenceName == PLANDOMIZER_PATH) + { + const auto& plandomizerPath = preferenceNode.second.as(); + this->_plandomizerPath = plandomizerPath; + } + } + + // Rewrite the file(s) if any settings or preferences are missing + for (auto& [settingName, settingInfo] : *settingInfoMap) + { + if (!settingsTree[settingName]) + { + utility::platform::Log(std::string("Added missing setting \"") + settingName + "\""); + if (settingInfo->GetType() == settings::Type::STANDARD) + { + rewriteSettings = true; + } + else if (settingInfo->GetType() == settings::Type::PREFERENCE) + { + rewritePreferences = true; + } + } + } + if (!settingsTree[SEED]) + { + this->_seed = seed::GenerateSeed(); + utility::platform::Log("Seed is missing. Generated new seed."); + rewriteSettings = true; + } + if (!settingsTree[PLANDOMIZER] || !settingsTree[GENERATE_SPOILER_LOG] || !settingsTree[STARTING_INVENTORY] || + !settingsTree[EXCLUDED_LOCATIONS] || !settingsTree[MIXED_ENTRANCE_POOLS]) + { + rewriteSettings = true; + } + if (!preferencesTree[PLANDOMIZER_PATH]) + { + rewritePreferences = true; + } + + // Rewrite files if deemed necessary + if (allowRewrite && rewriteSettings) + { + utility::platform::Log(std::string("Rewriting ") + settingsPath.generic_string()); + this->WriteSettingsToFile(settingsPath); + } + if (allowRewrite && rewritePreferences) + { + utility::platform::Log(std::string("Rewriting ") + preferencesPath.generic_string()); + this->WritePreferencesToFile(preferencesPath); + } + } + + YAML::Node Config::SettingsToYaml() + { + YAML::Node out; + for (auto& settings : this->_settingsList) + { + out[SEED] = this->_seed; + out[PLANDOMIZER] = this->_isUsingPlandomizer; + out[GENERATE_SPOILER_LOG] = this->_isGeneratingSpoilerLog; + + // Sort settings by id to keep relevant settings close together in the settings file + std::list sortedNames = {}; + for (auto& [settingName, setting] : settings.GetMap()) + { + sortedNames.push_back(settingName); + } + sortedNames.sort( + [&](const auto& a, const auto& b) + { return settings.GetMap().at(a).GetInfo()->GetID() < settings.GetMap().at(b).GetInfo()->GetID(); }); + + for (const auto& settingName : sortedNames) + { + auto& setting = settings.GetMap().at(settingName); + if (setting.GetInfo()->GetType() == settings::Type::STANDARD) + { + out[settingName] = setting.GetCurrentOption(); + } + } + + out[STARTING_INVENTORY] = std::map(); + for (const auto& [itemName, count] : settings.GetStartingInventory()) + { + out[STARTING_INVENTORY][itemName] = count; + } + + out[EXCLUDED_LOCATIONS] = std::list(); + for (const auto& locationName : settings.GetExcludedLocations()) + { + out[EXCLUDED_LOCATIONS].push_back(locationName); + } + + out[MIXED_ENTRANCE_POOLS] = std::list>(); + int i = 0; + for (const auto& pool : settings.GetMixedEntrancePools()) + { + out[MIXED_ENTRANCE_POOLS].push_back({}); + for (const auto& type : pool) + { + out[MIXED_ENTRANCE_POOLS][i].push_back(type); + } + i += 1; + } + } + + return out; + } + + YAML::Node Config::PreferencesToYaml() + { + YAML::Node out; + for (auto& settings : this->_settingsList) + { + out[PLANDOMIZER_PATH] = this->_plandomizerPath.generic_string(); + for (auto& [settingName, setting] : settings.GetMap()) + { + if (setting.GetInfo()->GetType() == settings::Type::PREFERENCE) + { + out[settingName] = setting.GetCurrentOption(); + } + } + } + + return out; + } + + void Config::WriteSettingsToFile(const fspath& settingsPath) + { + std::ofstream outputFile(settingsPath); + if (outputFile.is_open() == false) + { + throw std::runtime_error("Unable to open settings file \"" + settingsPath.generic_string() + "\" for writing."); + } + + outputFile << this->SettingsToYaml(); + outputFile.close(); + } + + void Config::WritePreferencesToFile(const fspath& preferencesPath) + { + std::ofstream outputFile(preferencesPath); + if (outputFile.is_open() == false) + { + throw std::runtime_error("Unable to open preferences file \"" + preferencesPath.generic_string() + + "\" for writing."); + } + + outputFile << this->PreferencesToYaml(); + outputFile.close(); + } + + void Config::WriteToFile(const fspath& settingsPath, const fspath& preferencesPath) { + WriteSettingsToFile(settingsPath); + WritePreferencesToFile(preferencesPath); + } + + std::string Config::GetHash(bool generateIfEmpty) + { + if (this->_hash.empty() && generateIfEmpty) + { + this->_hash = seed::GenerateHash(); + } + + return this->_hash; + } + + std::string Config::GetPermalink() { + // If a permalink was set, return that instead + if (!this->_permalink.empty()) { + return this->_permalink; + } + + std::string permalink{}; + + // TODO: print mod version instead of dusklight version? + /*permalink += DUSK_WC_DESCRIBE; + permalink += '\0';*/ + permalink += std::to_string(settings::GetSettingInfoHash()); + permalink += '\0'; + permalink += this->_seed; + permalink += '\0'; + + // Pack the settings up + PackedBitsWriter bitsWriter{}; + // Regular Settings + for (const auto& [settingName, setting] : GetSettings().GetMap()) { + if (setting.GetInfo()->GetType() != settings::Type::STANDARD) { + continue; + } + + auto optionIndex = setting.GetCurrentOptionIndex(); + auto bitLength = setting.GetInfo()->GetOptionsBitLength(); + bitsWriter.write(optionIndex, bitLength); + } + // Starting Items + const auto& startingInventory = GetSettings().GetStartingInventory(); + for (const auto& [itemName, maxCount] : logic::item_pool::GetValidStartingInventoryItems()) { + int count = 0; + if (startingInventory.contains(itemName)) { + count = startingInventory.at(itemName); + } + + int numBits = std::bit_width(static_cast(maxCount)); + bitsWriter.write(count, numBits); + } + // Excluded Locations + for (const auto& locationName : logic::location::GetAllRandomizerLocationNames()) { + if (GetSettings().GetExcludedLocations().contains(locationName)) { + bitsWriter.write(1, 1); + } else { + bitsWriter.write(0, 1); + } + } + // Mixed Entrance Pools + const auto& mixedEntrancePools = GetSettings().GetMixedEntrancePools(); + const auto& possibleMixedPoolTypes = logic::entrance_shuffle::GetPossibleMixedPoolTypes(); + for (const auto& entranceType : possibleMixedPoolTypes) { + uint32_t poolIndex = 0; + uint32_t counter = 0; + for (const auto& pool : mixedEntrancePools) { + counter += 1; + if (utility::container::ElementInContainer(pool, entranceType)) { + poolIndex = counter; + break; + } + } + bitsWriter.write(poolIndex, std::bit_width(possibleMixedPoolTypes.size())); + } + + bitsWriter.flush(); + for (auto byte : bitsWriter.bytes) { + permalink += byte; + } + permalink = b64_encode(permalink); + + return permalink; + } + + std::optional Config::LoadFromPermalink(std::string b64permalink) { + + // Strip trailing spaces + std::erase_if(b64permalink, [](unsigned char ch){ return std::isspace(ch); }); + + std::string permalink = b64_decode(b64permalink); + // Empty string gets returned if there was an error + if (permalink.empty()) { + return "Pasted permalink is invalid and could not be decoded. (You likely miscopied it.)"; + } + + // Split the string into 4 parts along the null terminator delimiter + // 1st part - Version string + // 2nd part - setting info hash + // 3rd part - seed string + // 4th part - packed bits representing settings + std::vector permaParts = {}; + constexpr char delimiter = '\0'; + size_t pos = permalink.find(delimiter); + while (pos != std::string::npos) { + if (permaParts.size() != 3) { + permaParts.push_back(permalink.substr(0, pos)); + permalink.erase(0, pos + 1); + } + else { + permaParts.push_back(permalink); + break; + } + + pos = permalink.find(delimiter); + } + + if (permaParts.size() != 4) { + return "Pasted permalink does not have the expected number of parts."; + } + + const auto& permaVersion = permaParts[0]; + const auto& permaSettingsInfoHash = permaParts[1]; + const auto& permaSeed = permaParts[2]; + const auto& permaPackedSettings = permaParts[3]; + + if (permaSettingsInfoHash != std::to_string(settings::GetSettingInfoHash())) { + // TODO: print mod version instead of dusklight version? + return fmt::format("Pasted permalink was generated with an incompatible Dusklight version.\n" + "Your version: {}\nPermalink version: {}", /*DUSK_WC_DESCRIBE*/ 0, permaVersion); + } + + const std::vector bytes(permaPackedSettings.begin(), permaPackedSettings.end()); + PackedBitsReader bitsReader{bytes}; + Config newConfig{}; + + for (auto& [settingName, setting] : newConfig.GetSettings().GetMap()) { + if (setting.GetInfo()->GetType() != settings::Type::STANDARD) { + continue; + } + + auto bitLength = setting.GetInfo()->GetOptionsBitLength(); + auto optionIndex = bitsReader.read(bitLength); + setting.SetCurrentOption(optionIndex); + } + // Starting Items + auto& startingInventory = newConfig.GetSettings().GetModifiableStartingInventory(); + for (const auto& [itemName, maxCount] : logic::item_pool::GetValidStartingInventoryItems()) { + int count = 0; + int numBits = std::bit_width(static_cast(maxCount)); + count = bitsReader.read(numBits); + + if (count > 0) { + startingInventory[itemName] = count; + } + } + // Excluded Locations + auto& excludedLocations = newConfig.GetSettings().GetModifiableExcludedLocations(); + for (const auto& locationName : logic::location::GetAllRandomizerLocationNames()) { + if (bitsReader.read(1) == 1) { + excludedLocations.insert(locationName); + } + } + + // Mixed Entrance Pools + auto& mixedEntrancePools = newConfig.GetSettings().GetModifiableMixedEntrancePools(); + const auto& possibleMixedPoolTypes = logic::entrance_shuffle::GetPossibleMixedPoolTypes(); + for (const auto& entranceType : possibleMixedPoolTypes) { + auto poolIndex = bitsReader.read(std::bit_width(possibleMixedPoolTypes.size())); + if (poolIndex == 0) { + continue; + } + poolIndex -= 1; + if (poolIndex < possibleMixedPoolTypes.size()) { + while (poolIndex >= mixedEntrancePools.size()) { + mixedEntrancePools.push_back({}); + } + auto& pool = *std::next(mixedEntrancePools.begin(), poolIndex); + pool.push_back(entranceType); + } + } + + if (!bitsReader.reached_last_byte()) { + return "Pasted permalink is incorrect length. (You likely miscopied it.)"; + } + + // Once we've gotten all the info, copy it over to this config + this->SetSeed(permaSeed); + for (auto& settings : this->_settingsList) { + for (auto& [settingName, setting] : settings.GetMap()) { + if (setting.GetInfo()->GetType() == settings::Type::STANDARD) { + setting.SetCurrentOption(newConfig.GetSettings().GetMap().at(settingName).GetCurrentOptionIndex()); + } + } + settings.GetModifiableExcludedLocations() = newConfig.GetSettings().GetExcludedLocations(); + settings.GetModifiableStartingInventory() = newConfig.GetSettings().GetStartingInventory(); + settings.GetModifiableMixedEntrancePools() = newConfig.GetSettings().GetMixedEntrancePools(); + } + + return std::nullopt; + } + + int SeedRNG(Config& config, + bool resolveNonStandardRandom /* = false */, + bool ignoreInvalidPlandomizer /* = true */) + { + // Seed with system time incase we have to choose random preferences during seeding + auto seed = static_cast(std::random_device {}()); + utility::random::RandomInit(seed); + + // Seed the rng using a combination of the seed and standard settings + std::string hashStr = config.GetSeed(); + for (auto& settings : config.GetSettingsList()) + { + for (auto& [settingName, setting] : settings.GetMap()) + { + if (setting.GetInfo()->GetType() == settings::Type::STANDARD) + { + hashStr += settingName + setting.GetCurrentOption(); + } + else if (resolveNonStandardRandom) + { + setting.ResolveIfRandom(); + } + } + + // Special handling for other settings + for (const auto& [itemName, count] : settings.GetStartingInventory()) + { + hashStr += itemName + std::to_string(count); + } + + for (const auto& locationName : settings.GetExcludedLocations()) + { + hashStr += locationName; + } + + for (const auto& pool : settings.GetMixedEntrancePools()) + { + for (const auto& type : pool) + { + hashStr += type; + } + } + } + + // Change the seed if we're using plandomizer + if (config.IsUsingPlandomizer()) + { + std::string plandomizerContents; + auto retVal = utility::file::GetContents(config.GetPlandomizerPath(), plandomizerContents); + if (!ignoreInvalidPlandomizer && retVal != 0) + { + LOG_TO_ERROR("Could not read plandomizer file at \"" + config.GetPlandomizerPath().generic_string() + "\""); + return 1; + } + hashStr += plandomizerContents; + } + + // Change the seed if we're generating a spoiler log + if (config.IsGeneratingSpoilerLog()) + { + hashStr += "Spoiler Log: True"; + } + + const size_t integerSeed = utility::crc32(hashStr.data(), hashStr.length()); + utility::random::RandomInit(integerSeed); + + return 0; + } +} // namespace randomizer::seedgen::config diff --git a/mods/randomizer/generator/seedgen/config.hpp b/mods/randomizer/generator/seedgen/config.hpp new file mode 100644 index 0000000000..888075d457 --- /dev/null +++ b/mods/randomizer/generator/seedgen/config.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include "settings.hpp" +#include "../utility/path.hpp" + +// forward declaration +namespace YAML +{ + class Node; +} + +namespace randomizer::seedgen::config +{ + + class Config + { + public: + Config(); + Config(const fspath& settingsPath, const fspath& preferencesPath); + + fspath GetPlandomizerPath() const { return this->_plandomizerPath; } + void SetSeed(const std::string& newSeed) { this->_seed = newSeed; } + std::string GetSeed() const { return this->_seed; } + auto& GetSettingsList() { return this->_settingsList; } + auto& GetSettings() { return this->_settingsList.front();} + bool IsUsingPlandomizer() const { return this->_isUsingPlandomizer; } + bool IsGeneratingSpoilerLog() const { return this->_isGeneratingSpoilerLog; } + void ResetSettingsToDefault(); + void ResetPreferencesToDefault(); + + void LoadFromFile(const fspath& settingsPath, + const fspath& preferencesPath, + bool createIfNotFound = true, + bool allowRewrite = true); + YAML::Node SettingsToYaml(); + YAML::Node PreferencesToYaml(); + void WriteSettingsToFile(const fspath& filePath); + void WritePreferencesToFile(const fspath& preferencesPath); + void WriteToFile(const fspath& filePath, const fspath& preferencesPath); + + std::optional LoadFromPermalink(std::string b64permalink); + std::string GetPermalink(); + void SetPermalink(const std::string& newPermalink) { this->_permalink = newPermalink; } + + /** + * @brief Returns the hash for the config. + * @param generateIfEmpty Generates a new hash if the current hash is empty + * + * @return The hash as a string + */ + std::string GetHash(bool generateIfEmpty = true); + void SetHash(const std::string& newHash) { this->_hash = newHash; } + + private: + fspath _plandomizerPath; + + std::string _seed; + std::string _hash; + std::string _permalink; + std::list _settingsList; + bool _isUsingPlandomizer = false; + bool _isGeneratingSpoilerLog = true; + }; + + int SeedRNG(Config& config, bool resolveNonStandardRandom = false, bool ignoreInvalidPlandomizer = true); +} // namespace randomizer::seedgen::config diff --git a/mods/randomizer/generator/seedgen/packed_bits.hpp b/mods/randomizer/generator/seedgen/packed_bits.hpp new file mode 100644 index 0000000000..d69d8c0fae --- /dev/null +++ b/mods/randomizer/generator/seedgen/packed_bits.hpp @@ -0,0 +1,109 @@ +#pragma once + +// Packed Bits classes copied from the original Wind Waker Randomizer +class PackedBitsWriter +{ + public: + PackedBitsWriter() = default; + ~PackedBitsWriter() = default; + + uint8_t bits_left_in_byte = 8; + size_t current_byte = 0; + std::vector bytes = {}; + + template + void write(T value, size_t length) + { + size_t bits_to_read = 0; + while (length > 0) + { + if (length >= bits_left_in_byte) + { + bits_to_read = bits_left_in_byte; + } + else + { + bits_to_read = length; + } + + size_t mask = (1 << bits_to_read) - 1; + current_byte |= (value & mask) << (8 - bits_left_in_byte); + + bits_left_in_byte -= bits_to_read; + length -= bits_to_read; + value >>= bits_to_read; + + if (bits_left_in_byte > 0) + { + continue; + } + + flush(); + } + } + + void flush() + { + bytes.push_back(current_byte); + current_byte = 0; + bits_left_in_byte = 8; + } +}; + +class PackedBitsReader +{ + public: + PackedBitsReader(const std::vector& bytes_): bytes(bytes_) {} + ~PackedBitsReader() = default; + + size_t current_bit_index = 0; + size_t current_byte_index = 0; + std::vector bytes = {}; + + size_t read(size_t length) + { + size_t bits_read = 0; + size_t value = 0; + size_t bits_left_to_read = length; + + while (bits_read != length) + { + size_t bits_to_read = 0; + if (bits_left_to_read > 8) + { + bits_to_read = 8; + } + else + { + bits_to_read = bits_left_to_read; + } + + if (bits_to_read + current_bit_index > 8) + { + bits_to_read = 8 - current_bit_index; + } + + size_t mask = ((1 << bits_to_read) - 1) << current_bit_index; + + if (current_byte_index >= bytes.size()) + { + return static_cast(-1); + } + + size_t current_byte = bytes[current_byte_index]; + value = ((current_byte & mask) >> current_bit_index) << bits_read | value; + + current_bit_index += bits_to_read; + current_byte_index += current_bit_index >> 3; + current_bit_index %= 8; + bits_left_to_read -= bits_to_read; + bits_read += bits_to_read; + } + + return value; + } + + bool reached_last_byte() const { + return current_byte_index == bytes.size() - 1; + } +}; diff --git a/mods/randomizer/generator/seedgen/seed.cpp b/mods/randomizer/generator/seedgen/seed.cpp new file mode 100644 index 0000000000..30407f2960 --- /dev/null +++ b/mods/randomizer/generator/seedgen/seed.cpp @@ -0,0 +1,118 @@ +#include "seed.hpp" + +#include "../utility/random.hpp" + +#include + +namespace randomizer::seedgen::seed +{ + static constexpr const char* nouns[] = { + "Aeralfos", "Agitha", "Ant", "Argorok", "Armos", "Ashei", "Auru", "BackSlice", "Bari", + "Barnes", "Beamos", "Beth", "BigBaba", "Blizzeta", "Bo", "Bokoblin", "Bombfish", "Borville", + "Bulblin", "Butterfly", "CastleTown", "Charlo", "Cheese", "Chilfos", "Chu", "Chudley", "Clawshot", + "Colin", "Coro", "Cucco", "Dangoro", "Darbus", "Darkhammer", "Darknut", "Dayfly", "DeathSword", + "DekuToad", "Dodongo", "Dragonfly", "Dynalfos", "Eldin", "Epona", "Fado", "Fairy", "Falbi", + "Fanadi", "Faron", "Freezard", "Fyer", "Ganondorf", "Gengle", "GhoulRat", "Gibdo", "Goron", + "GreatSpin", "Greengill", "Guay", "Hanch", "Hawkeye", "Helmasaur", "Hena", "Hornet", "HorseGrass", + "Hylian", "Jaggle", "Jovani", "JumpStrike", "Keese", "Kili", "Ladybug", "Lanayru", "Lantern", + "Leever", "Link", "Lizalfos", "Louise", "Luda", "Malo", "Malver", "Mantis", "Midna", + "Misha", "Moldorm", "Morpheel", "Ooccoo", "Ordona", "Pergie", "Phasmid", "Plumm", "Poe", + "Postman", "Pumpkin", "Puppet", "Purdy", "Ralis", "Reekfish", "Renado", "Rupee", "Rusl", + "Rutela", "Sage", "Sera", "Shad", "ShellBlade", "Sketch", "SkullKid", "Skulltula", "SkyBook", + "Snail", "Snowpeak", "Soal", "Soldier", "Spinner", "Stalfos", "Stallord", "Talo", "Tektite", + "Telma", "Temple", "TileWorm", "Toadpoli", "Trill", "Twilight", "Uli", "WolfLink", "Zant", + "Zelda", "Zora" + }; + + static constexpr const char* adjectives[] = { + "Abnormal", "Absent", "Absolute", "Abstract", "Absurd", "Accurate", "Active", "Actual", + "Adjacent", "Aesthetic", "Aggressive", "Alert", "Alien", "Alternate", "Amazing", "Ambitious", + "Amusing", "Ancient", "Angry", "Anxious", "Apparent", "Artistic", "Astute", "Atomic", + "Atrocious", "Attractive", "Authentic", "Average", "Awful", "Awkward", "Bad", "Bashful", + "Basic", "Beautiful", "Big", "Bitter", "Bizarre", "Blue", "Bold", "Brainy", + "Brave", "Bright", "Brilliant", "Busy", "Callous", "Calm", "Capable", "Careful", + "Casual", "Cautious", "Central", "Cheap", "Cheerful", "Chemical", "Chilly", "Chronic", + "Chummy", "Circular", "Civil", "Classic", "Clean", "Clever", "Clinical", "Clumsy", + "Coastal", "Cognitive", "Coherent", "Cold", "Colorful", "Comical", "Commercial", "Common", + "Compact", "Competent", "Complete", "Complex", "Concise", "Concrete", "Confident", "Confused", + "Consistent", "Constant", "Contrary", "Cool", "Corny", "Corporate", "Correct", "Cosmic", + "Costly", "Courteous", "Cranky", "Crazy", "Creative", "Credible", "Creepy", "Criminal", + "Critical", "Curious", "Current", "Custom", "Cute", "Daily", "Damp", "Dangerous", + "Dapper", "Dark", "Deadly", "Decent", "Decisive", "Defeated", "Defensive", "Defiant", + "Delicate", "Delightful", "Desperate", "Detached", "Determined", "Different", "Difficult", "Digital", + "Diligent", "Disastrous", "Disgusted", "Distant", "Disturbed", "Divine", "Dizzy", "Dominant", + "Double", "Doubtful", "Dramatic", "Dreadful", "Droll", "Dull", "Dynamic", "Early", + "Effective", "Elated", "Elderly", "Electric", "Elegant", "Empty", "Endless", "Enormous", + "Entire", "Equal", "Essential", "Eternal", "Evil", "Excellent", "Exotic", "Expensive", + "Explicit", "Extreme", "Factual", "Faithful", "False", "Famous", "Fancy", "Fantastic", + "Fast", "Fatal", "Favorite", "Fellow", "Fierce", "Final", "Financial", "Foolish", + "Formal", "Fortified", "Fortunate", "Frantic", "Free", "Frenzied", "Fresh", "Friendly", + "Functional", "Funny", "Furious", "Future", "Galactic", "Generous", "Genial", "Gentle", + "Genuine", "Giant", "Glad", "Glass", "Glittery", "Gloomy", "Glorious", "Golden", + "Good", "Gothic", "Graceful", "Gradual", "Grand", "Great", "Grim", "Gross", + "Grumpy", "Guilty", "Handsome", "Happy", "Harmful", "Harsh", "Healthy", "Hearty", + "Heavy", "Helpful", "Historic", "Honest", "Hostile", "Huge", "Hungry", "Hyper", + "Impartial", "Implicit", "Important", "Impressive", "Indirect", "Indoor", "Infinite", "Inherent", + "Initial", "Inner", "Innocent", "Inspiring", "Instant", "Intense", "Internal", "Inventive", + "Jarring", "Jealous", "Jolly", "Joyful", "Junior", "Kind", "Kooky", "Late", + "Lazy", "Lesser", "Lethargic", "Light", "Likable", "Linear", "Linguistic", "Liquid", + "Little", "Lively", "Local", "Logical", "Lonely", "Loud", "Lovely", "Loyal", + "Lucky", "Lunar", "Mad", "Magical", "Magnetic", "Mainstream", "Majestic", "Major", + "Malicious", "Manual", "Marine", "Marvellous", "Massive", "Maximum", "Mean", "Meaningful", + "Medical", "Medieval", "Medium", "Mellow", "Mental", "Mere", "Middle", "Mighty", + "Mild", "Minimal", "Mobile", "Modest", "Monthly", "Moral", "Motionless", "Muddy", + "Mundane", "Musical", "Mutual", "Nasty", "Natural", "Nearby", "Neat", "Negative", + "Nerdy", "Nervous", "Neutral", "Nice", "Nimble", "Noble", "Noisy", "Notable", + "Objective", "Obnoxious", "Obscure", "Obvious", "Odd", "Offensive", "Official", "Okay", + "Old", "Only", "Opposite", "Optical", "Optional", "Organic", "Organized", "Outdoor", + "Painful", "Paper", "Parallel", "Past", "Patient", "Peaceful", "Peppy", "Perfect", + "Permanent", "Persistent", "Personal", "Petty", "Pink", "Plain", "Platinum", "Plausible", + "Pleasant", "Polite", "Popular", "Portable", "Positive", "Potential", "Powerful", "Practical", + "Precious", "Pretty", "Previous", "Primitive", "Private", "Probable", "Productive", "Profound", + "Prominent", "Proper", "Protective", "Public", "Pure", "Purple", "Purposeful", "Puzzled", + "Quick", "Quiet", "Quirky", "Random", "Rapid", "Rational", "Recent", "Redundant", + "Refined", "Regretful", "Regular", "Relaxed", "Relevant", "Remote", "Reserved", "Resident", + "Responsive", "Retail", "Rigid", "Rival", "Romantic", "Rotten", "Royal", "Rubber", + "Rude", "Sacred", "Sad", "Safe", "Scary", "Seasonal", "Secret", "Secured", + "Selective", "Senior", "Sensible", "Serious", "Severe", "Shady", "Shallow", "Sharp", + "Sheer", "Shiny", "Short", "Sick", "Sideways", "Silent", "Silly", "Silver", + "Similar", "Simple", "Sincere", "Skilled", "Skittish", "Sleepy", "Slow", "Small", + "Smart", "Smug", "Snazzy", "Snooty", "Solar", "Solid", "Somber", "Spare", + "Specific", "Spiteful", "Splendid", "Spooky", "Spotless", "Spry", "Square", "Stable", + "Standard", "Startled", "Static", "Steady", "Stern", "Stone", "Stylish", "Subsequent", + "Successful", "Sudden", "Suitable", "Sunny", "Super", "Supportive", "Surplus", "Suspicious", + "Sweet", "Symbolic", "Talkative", "Tall", "Tearful", "Technical", "Terrible", "Thankful", + "Thoughtful", "Thrilled", "Tidy", "Tired", "Total", "Tough", "Toxic", "Tragic", + "Tremendous", "Trivial", "Tropical", "Troubled", "Truthful", "Typical", "Ultimate", "Ultra", + "Unaware", "Uncertain", "Unfair", "Unforeseen", "Uniform", "Unique", "Unknown", "Unlawful", + "Unlikely", "Unreal", "Upbeat", "Upset", "Urban", "Useful", "Usual", "Vague", + "Valid", "Verbal", "Vertical", "Vicious", "Vigorous", "Villainous", "Virtual", "Visible", + "Vital", "Vivid", "Warm", "Weekly", "Weird", "Wholesome", "Wicked", "Wise", + "Wistful", "Witty", "Wonderful", "Wooden", "Worried", "Wrong", "Young", "Zany" + }; + + int GetRandValue(int min, int max) { + std::uniform_int_distribution distribution(min, max); + std::random_device rd; + std::mt19937 engine(rd()); + return distribution(engine); + } + + std::string GenerateSeed() + { + const std::string adjective1 = adjectives[GetRandValue(0, std::size(adjectives) - 1)]; + const std::string adjective2 = adjectives[GetRandValue(0, std::size(adjectives) - 1)]; + const std::string noun = nouns[GetRandValue(0, std::size(nouns) - 1)]; + + return adjective1 + adjective2 + noun; + } + + std::string GenerateHash() + { + const std::string noun1 = utility::random::RandomElement(nouns); + const std::string noun2 = utility::random::RandomElement(nouns); + const std::string noun3 = utility::random::RandomElement(nouns); + + return noun1 + " " + noun2 + " " + noun3; + } +} // namespace randomizer::seedgen::seed diff --git a/mods/randomizer/generator/seedgen/seed.hpp b/mods/randomizer/generator/seedgen/seed.hpp new file mode 100644 index 0000000000..e5e70e038a --- /dev/null +++ b/mods/randomizer/generator/seedgen/seed.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "config.hpp" + +namespace randomizer::seedgen::seed +{ + /** + * @brief Generates a random sequence of 3 words to be used as a seed. + * + * @return The sequence of words as a string + */ + std::string GenerateSeed(); + + /** + * @brief Generates a random sequence of 3 nouns to be used as a verification hash. + * + * @return The sequence of words as a string + */ + std::string GenerateHash(); + + std::string HashForConfig(const config::Config& config); +} // namespace randomizer::seedgen::seed diff --git a/mods/randomizer/generator/seedgen/settings.cpp b/mods/randomizer/generator/seedgen/settings.cpp new file mode 100644 index 0000000000..56482a6323 --- /dev/null +++ b/mods/randomizer/generator/seedgen/settings.cpp @@ -0,0 +1,415 @@ +#include "settings.hpp" + +#include "../utility/crc32.hpp" +#include "../utility/endian.hpp" +#include "../utility/log.hpp" +#include "../utility/container.hpp" +#include "../utility/file.hpp" +#include "../utility/random.hpp" +#include "../utility/string.hpp" +#include "../utility/yaml.hpp" +#include "../logic/location.hpp" +#include "../logic/entrance_shuffle.hpp" + +#include +#include +#include + +namespace randomizer::seedgen::settings +{ + + Type TypeFromStr(const std::string& str) + { + std::unordered_map types = {{"Standard", Type::STANDARD}, {"Preference", Type::PREFERENCE}}; + + if (!types.contains(str)) + { + return Type::INVALID; + } + + return types.at(str); + } + + SettingInfo::SettingInfo(int id, + const std::string& name, + Type type, + const std::vector& options, + const std::vector& descriptions, + int defaultOptionIndex, + bool hasRandomOption, + int randomOptionIndex, + int randomLow, + int randomHigh, + bool trackerImportant, + bool needInGame): + _id(id), + _name(name), + _type(type), + _options(options), + _descriptions(descriptions), + _defaultOptionIndex(defaultOptionIndex), + _hasRandomOption(hasRandomOption), + _randomOptionIndex(randomOptionIndex), + _randomLow(randomLow), + _randomHigh(randomHigh), + _trackerImportant(trackerImportant), + _needInGame(needInGame) + { + // The logic expression of a setting replaces spaces with underscores, + // and removes apostraphes and parenthesis + auto logicName = name; + std::ranges::replace(logicName, ' ', '_'); + utility::str::Erase(logicName, "'", ")", "("); + this->_logicName = logicName; + + // Same for logic expressions of options for this setting + for (const auto& option : options) + { + auto logicOption = option; + std::ranges::replace(logicOption, ' ', '_'); + utility::str::Erase(logicOption, "'", ")", "("); + this->_logicOptions.push_back(logicOption); + } + + // Note: Assumes an option is never a negative number + this->_optionsAreNumbers = std::ranges::all_of(options, [](const std::string& option) { + return !option.empty() && std::ranges::all_of(option, ::isdigit); + }); + + // Set options bitlength + this->_optionsBitLength = std::bit_width(this->_options.size()); + } + + std::string SettingInfo::GetDefaultOption() const + { + return this->_options[this->_defaultOptionIndex]; + } + + int SettingInfo::GetIndexOfOption(const std::string& option) const + { + return utility::container::GetIndex(this->_options, option); + } + + std::string SettingInfo::GetRandomOption() const + { + return this->_options.at(this->_randomOptionIndex); + } + + Setting::Setting(SettingInfo* info, const std::string& option): _info(info) + { + this->_currentOptionIndex = info->GetIndexOfOption(option); + } + + void Setting::SetCurrentOption(int newOptionIndex) + { + if (newOptionIndex >= this->GetInfo()->GetOptions().size()) + { + throw std::runtime_error(std::string("Index ") + std::to_string(newOptionIndex) + + " is out of bounds for setting \"" + this->GetInfo()->GetName() + "\""); + } + this->_currentOptionIndex = newOptionIndex; + } + + void Setting::SetCurrentOption(const std::string& optionName) + { + int optionNameIndex = this->GetInfo()->GetIndexOfOption(optionName); + if (optionNameIndex == -1) + { + throw std::runtime_error(std::string("\"") + optionName + "\" is not a valid option for setting \"" + + this->GetInfo()->GetName() + "\""); + } + this->SetCurrentOption(optionNameIndex); + } + + std::string Setting::GetCurrentOption() const + { + return this->_info->GetOptions()[this->_currentOptionIndex]; + } + + int Setting::GetCurrentOptionAsNumber() const { + try { + return std::stoi(this->GetCurrentOption()); + } catch (const std::invalid_argument&) { + throw std::runtime_error("Option \"" + GetCurrentOption() + "\" for setting \"" + this->GetInfo()->GetName() + + "\" cannot be turned into a number"); + } + } + + void Setting::ResolveIfRandom() + { + if (this->GetCurrentOptionIndex() == this->GetInfo()->GetRandomOptionIndex()) + { + this->_isUsingRandomOption = true; + auto randomOption = + utility::random::Random(this->GetInfo()->GetRandomLow(), this->GetInfo()->GetRandomHigh()); + this->SetCurrentOption(randomOption); + LOG_TO_DEBUG("Chose \"" + this->GetInfo()->GetOptions()[randomOption] + " as random option for setting \"" + + this->GetInfo()->GetName()); + } + } + + bool Setting::operator==(const char* optionName) const + { + int optionNameIndex = this->GetInfo()->GetIndexOfOption(optionName); + if (optionNameIndex == -1) + { + throw std::runtime_error(std::string("\"") + optionName + "\" is not a valid option for setting \"" + + this->GetInfo()->GetName() + "\""); + } + return this->_currentOptionIndex == optionNameIndex; + } + + bool Setting::operator!=(const char* optionName) const + { + return !(*this == optionName); + } + + bool Setting::operator>=(const char* optionName) const + { + int optionNameIndex = this->GetInfo()->GetIndexOfOption(optionName); + if (optionNameIndex == -1) + { + throw std::runtime_error(std::string("\"") + optionName + "\" is not a valid option for setting \"" + + this->GetInfo()->GetName() + "\""); + } + return this->_currentOptionIndex >= optionNameIndex; + } + + bool Setting::operator<=(const char* optionName) const + { + int optionNameIndex = this->GetInfo()->GetIndexOfOption(optionName); + if (optionNameIndex == -1) + { + throw std::runtime_error(std::string("\"") + optionName + "\" is not a valid option for setting \"" + + this->GetInfo()->GetName() + "\""); + } + return this->_currentOptionIndex <= optionNameIndex; + } + + bool Setting::operator>(const char* optionName) const + { + int optionNameIndex = this->GetInfo()->GetIndexOfOption(optionName); + if (optionNameIndex == -1) + { + throw std::runtime_error(std::string("\"") + optionName + "\" is not a valid option for setting \"" + + this->GetInfo()->GetName() + "\""); + } + return this->_currentOptionIndex > optionNameIndex; + } + + bool Setting::operator<(const char* optionName) const + { + int optionNameIndex = this->GetInfo()->GetIndexOfOption(optionName); + if (optionNameIndex == -1) + { + throw std::runtime_error(std::string("\"") + optionName + "\" is not a valid option for setting \"" + + this->GetInfo()->GetName() + "\""); + } + return this->_currentOptionIndex < optionNameIndex; + } + + Settings::Settings() { + // Load settings info and set defaults + auto settingInfoMap = GetAllSettingsInfo(); + for (auto& [settingName, settingInfo] : *settingInfoMap) { + InsertSetting(settingName, Setting(settingInfo.get(), settingInfo->GetDefaultOption())); + } + } + + void Settings::InsertSetting(const std::string& settingName, Setting setting) + { + this->_map.emplace(settingName, setting); + } + + void Settings::AddStartingItem(const std::string& itemName, const int& count /*= 1*/) + { + if (!this->_startingInventory.contains(itemName)) + { + this->_startingInventory.emplace(itemName, 0); + } + this->_startingInventory.at(itemName) += count; + } + + void Settings::AddExcludedLocation(const std::string& locationName) + { + this->_excludedLocations.insert(locationName); + } + + void Settings::AddMixedPool(const std::list& pool) + { + this->_mixedEntrancePools.push_back(pool); + } + + SettingInfoMap_t* GetAllSettingsInfo() + { + static std::unique_ptr settingInfoMap = std::make_unique(); + + // If we haven't loaded in our setting info yet, do so now + if (settingInfoMap->empty()) + { + settingInfoMap = LoadAllSettingsInfo(); + } + + return settingInfoMap.get(); + } + + std::unique_ptr LoadAllSettingsInfo() + { + std::unique_ptr settingInfoMap = std::make_unique(); + auto settingsDataTree = LOAD_EMBED_YAML(RANDO_DATA_PATH "settings_list.yaml"); + + // Process all nodes of the yaml file. Each node contains one setting + int settingIdCounter = 0; + for (const auto& settingNode : settingsDataTree) + { + // Check to make sure all required fields are present + const auto requiredFields = {"Name", "Default Option", "Options"}; + for (const auto& field : requiredFields) + { + if (!settingNode[field]) + { + throw std::runtime_error(std::string("Field \"") + field + "\" is missing from settings list node:\n" + + YAML::Dump(settingNode)); + } + } + + // Required Fields + const auto& name = settingNode["Name"].as(); + const auto& defaultOption = settingNode["Default Option"].as(); + std::vector options = {}; + std::vector descriptions = {}; + for (const auto& optionNodes : settingNode["Options"]) + { + for (const auto& optionNode : optionNodes) + { + const auto& option = optionNode.first.as(); + const auto& description = optionNode.second.as(); + + // If we're specifying a range, then include all numbers in the range + if (randomizer::utility::str::Contains(option, "-")) + { + // Fill in all the options between the lower and upper bounds + auto ops = randomizer::utility::str::Split(option, '-'); + int lowerBound = std::stoi(ops[0]); + int upperBound = std::stoi(ops[1]); + for (auto i = lowerBound; i <= upperBound; i++) + { + options.push_back(std::to_string(i)); + descriptions.push_back(description); + } + } + else + { + options.push_back(option); + descriptions.push_back(description); + } + } + } + + // Calculate default option index + auto defaultOptionIndex = utility::container::GetIndex(options, defaultOption); + if (defaultOptionIndex == -1) + { + throw std::runtime_error(std::string("Default Option \"") + defaultOption + "\" is not defined for setting \"" + + name + "\""); + } + + // Optional fields. If found, use the field value. If not found, use a default + const auto& type = TypeFromStr(settingNode["Type"] ? settingNode["Type"].as() : "Standard"); + if (type == Type::INVALID) + { + throw std::runtime_error(std::string("Unknown setting type \"") + settingNode["Type"].as() + + "\" for setting \"" + name + "\""); + } + + const auto& trackerImportant = + settingNode["Tracker Important"] ? settingNode["Tracker Important"].as() : false; + const auto& needInGame = + settingNode["Need In Game"] ? settingNode["Need In Game"].as() : false; + const auto& hasRandomOption = + settingNode["Autogenerate Random"] ? settingNode["Autogenerate Random"].as() : true; + const auto& randomAlias = settingNode["Random Alias"] ? settingNode["Random Alias"].as() : "Random"; + + int randomLow = 0; + int randomHigh = options.size() - 1; + if (settingNode["Random Low"]) + { + auto randomLowStr = settingNode["Random Low"].as(); + randomLow = utility::container::GetIndex(options, randomLowStr); + if (randomLow == -1) + { + throw std::runtime_error(std::string("Random Low Option \"") + randomLowStr + + "\" is not defined for setting \"" + name + "\""); + } + } + if (settingNode["Random High"]) + { + auto randomHighStr = settingNode["Random High"].as(); + randomHigh = utility::container::GetIndex(options, randomHighStr); + if (randomHigh == -1) + { + throw std::runtime_error(std::string("Random High Option \"") + randomHighStr + + "\" is not defined for setting \"" + name + "\""); + } + } + + // Generate the random option if it's not already there + if (hasRandomOption && utility::container::GetIndex(options, randomAlias) != -1) + { + options.push_back(randomAlias); + descriptions.push_back("A random option will be chosen"); + } + + int randomOptionIndex = utility::container::GetIndex(options, randomAlias); + + // Insert the data for the setting + auto info = std::make_unique(settingIdCounter++, + name, + type, + options, + descriptions, + defaultOptionIndex, + hasRandomOption, + randomOptionIndex, + randomLow, + randomHigh, + trackerImportant, + needInGame); + settingInfoMap->emplace(name, std::move(info)); + } + + return std::move(settingInfoMap); + } + + uint32_t GetSettingInfoHash() { + static uint32_t settingsInfoHash = 0; + if (settingsInfoHash == 0) { + auto allSettingInfo = GetAllSettingsInfo(); + for (const auto& [settingName, settingInfo] : *allSettingInfo) { + settingsInfoHash = utility::crc32(settingName.data(), settingName.length(), settingsInfoHash); + for (const auto& optionName : settingInfo->GetOptions()) { + settingsInfoHash = utility::crc32(optionName.data(), optionName.length(), settingsInfoHash); + } + } + + for (const auto& [itemName, maxRef] : logic::item_pool::GetValidStartingInventoryItems()) { + int maxCount = maxRef; + settingsInfoHash = utility::crc32(itemName.data(), itemName.length(), settingsInfoHash); + if constexpr (std::endian::native == std::endian::big) { + maxCount = Utility::Endian::byteswap(maxCount); + } + settingsInfoHash = utility::crc32(&maxCount, sizeof(maxCount), settingsInfoHash); + } + + for (const auto& locationName : logic::location::GetAllRandomizerLocationNames()) { + settingsInfoHash = utility::crc32(locationName.data(), locationName.length(), settingsInfoHash); + } + + for (const auto& entranceType : logic::entrance_shuffle::GetPossibleMixedPoolTypes()) { + settingsInfoHash = utility::crc32(entranceType.data(), entranceType.length(), settingsInfoHash); + } + } + return settingsInfoHash; + } + +}; // namespace randomizer::seedgen::settings diff --git a/mods/randomizer/generator/seedgen/settings.hpp b/mods/randomizer/generator/seedgen/settings.hpp new file mode 100644 index 0000000000..66c3b68fdb --- /dev/null +++ b/mods/randomizer/generator/seedgen/settings.hpp @@ -0,0 +1,233 @@ +#pragma once + +#include "../utility/container.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace randomizer::seedgen::settings +{ + class SettingInfo; + using SettingInfoMap_t = std::map>; + + /** + * @brief Enum for different types of settings. + * + * Standard settings will affect the rng used for seed generation. + * Preference settings will not affect the rng used for seed generation. + */ + enum Type + { + INVALID = 0, + STANDARD, + PREFERENCE, + }; + + /** + * @brief Takes a string representation of a Type and returns the + * associated enum value. + * + * @param str The string representation of a Type. + * @return The associated enum value for the passed in type. + */ + Type TypeFromStr(const std::string& str); + + /** + * @brief SettingInfo holds static info about a setting. + * + * Data should only ever be set once when reading from settings.yaml + * and creating appropriate info entries. + * + */ + class SettingInfo + { + public: + SettingInfo(int id, + const std::string& name, + Type type, + const std::vector& options, + const std::vector& descriptions, + int defaultOptionIndex, + bool hasRandomOption, + int randomOptionIndex, + int randomLow, + int randomHigh, + bool trackerImportant, + bool needInGame); + + int GetID() const { return this->_id; } + + /** + * @brief Returns the setting's name as displayed in a UI. + */ + std::string GetName() const { return this->_name; } + + /** + * @brief Returns the type of the setting. + */ + Type GetType() const { return this->_type; } + + /** + * @brief Returns a vector of strings of the setting's available options. + */ + const std::vector& GetOptions() const { return this->_options; } + + /** + * @brief Returns a vector of strings of the setting's options' descriptions. + */ + const std::vector& GetDescriptions() const { return this->_descriptions; } + + /** + * @brief Returns the index of the default option in the options vector for the setting. + */ + int GetDefaultOptionIndex() const { return this->_defaultOptionIndex; } + + /** + * @brief Returns the string representation of the default option for the setting. + */ + std::string GetDefaultOption() const; + int GetIndexOfOption(const std::string& option) const; + bool HasRandomOption() const { return this->_hasRandomOption; } + int GetRandomOptionIndex() const { return this->_randomOptionIndex; } + std::string GetRandomOption() const; + int GetRandomLow() const { return this->_randomLow; } + int GetRandomHigh() const { return this->_randomHigh; } + bool TrackerImportant() const { return this->_trackerImportant; } + bool NeedInGame() const { return this->_needInGame; } + bool OptionsAreNumbers() const { return this->_optionsAreNumbers; } + int GetOptionsBitLength() const {return this->_optionsBitLength; } + + private: + int _id = -1; + std::string _name = ""; + Type _type = INVALID; + std::vector _options = {}; + std::vector _descriptions = {}; + int _defaultOptionIndex = 0; + bool _hasRandomOption = true; + int _randomOptionIndex = 0; // The index of this setting's random option + int _randomLow = 0; // Lower bound when choosing a random option + int _randomHigh = 0; // Upper bound when choosing a random option + bool _trackerImportant = false; // Whether or not this setting can affect trackers + bool _needInGame = false; // Whether or not we need to read this setting during gameplay + bool _optionsAreNumbers = false;// Whether this setting's options are all numbers + int _optionsBitLength = 0; + + // Variables that hold the setting's name and options when being checked + // in a logical requirement string. + std::string _logicName = ""; + std::vector _logicOptions = {}; + }; + + /** + * @brief Setting holds the data for a single setting in a specific world. + * + * A setting's current option index can change depending on certain + * circumstances (i.e. the user changes it, or it conflicts with another setting) + */ + class Setting + { + public: + Setting(SettingInfo* info, const std::string& option); + + void SetCurrentOption(const std::string& newOption); + void SetCurrentOption(int newOptionIndex); + std::string GetCurrentOption() const; + int GetCurrentOptionAsNumber() const; + int GetCurrentOptionIndex() const { return this->_currentOptionIndex; } + bool IsUsingRandomOption() const { return this->_isUsingRandomOption; } + SettingInfo* GetInfo() const { return this->_info; } + const std::string& GetCustomOption() const { return this->_customOption; } + void ResolveIfRandom(); + + bool operator==(const char* optionName) const; + bool operator!=(const char* optionName) const; + bool operator>=(const char* optionName) const; + bool operator<=(const char* optionName) const; + bool operator>(const char* optionName) const; + bool operator<(const char* optionName) const; + + template + bool IsAnyOf(Types... optionNames) + { + // Check to make sure all listed options exist + for (const auto& optionName : {optionNames...}) + { + if (!utility::container::ElementInContainer(this->GetInfo()->GetOptions(), optionName)) + { + throw std::runtime_error("\"" + std::string(optionName) + "\" is not a known option for setting \"" + + this->GetInfo()->GetName() + "\""); + } + } + + // Check if any of the options are the current one + for (const auto& optionName : {optionNames...}) + { + if (optionName == this->GetCurrentOption()) + { + return true; + } + } + return false; + } + + private: + int _currentOptionIndex = -1; + bool _isUsingRandomOption = false; + SettingInfo* _info = nullptr; + std::string _customOption = ""; // For things like hex color strings + }; + + /** + * @brief Settings holds all of the settings for a specific world. + * + */ + class Settings + { + public: + Settings(); + + void InsertSetting(const std::string& settingName, Setting setting); + void AddStartingItem(const std::string& itemName, const int& count = 1); + void AddExcludedLocation(const std::string& locationName); + void AddMixedPool(const std::list& pool); + std::map& GetMap() { return this->_map; } + const std::map& GetStartingInventory() const { return this->_startingInventory; } + const std::set& GetExcludedLocations() const { return this->_excludedLocations; } + const std::list>& GetMixedEntrancePools() const { return this->_mixedEntrancePools; } + std::map& GetModifiableStartingInventory() { return this->_startingInventory; } + std::set& GetModifiableExcludedLocations() { return this->_excludedLocations; } + std::list>& GetModifiableMixedEntrancePools() { return this->_mixedEntrancePools; } + + private: + std::map _map = {}; + std::map _startingInventory = {}; + std::set _excludedLocations = {}; + std::list> _mixedEntrancePools = {}; + }; + + /** + * @brief Gets the map of each setting name to its info + * @return pointer to the setting info map + */ + SettingInfoMap_t* GetAllSettingsInfo(); + + /** + * @brief Reads settings_list.yaml and loads in all setting data + */ + std::unique_ptr LoadAllSettingsInfo(); + + /** + * @brief Generates a hash based on the current settings info. This can be used + * to determine if settings between different permalinks are valid + * @return the hash for the current settings info + */ + uint32_t GetSettingInfoHash(); + +}; // namespace randomizer::seedgen::settings diff --git a/mods/randomizer/generator/test/test.cpp b/mods/randomizer/generator/test/test.cpp new file mode 100644 index 0000000000..f1e41e14c2 --- /dev/null +++ b/mods/randomizer/generator/test/test.cpp @@ -0,0 +1,42 @@ +#include "test.hpp" + +#include "../randomizer.hpp" +#include "../utility/string.hpp" + +#include +#include + +namespace randomizer::test::test +{ + void RunTests() + { + for (const auto& entry : std::filesystem::recursive_directory_iterator(RANDO_LOGIC_TESTS_PATH)) + { + if (entry.path().generic_string().ends_with("settings.yaml")) + { + auto pathFolders = utility::str::Split(entry.path().generic_string(), '/'); + auto& testName = pathFolders[pathFolders.size() - 2]; + std::filesystem::remove(SETTINGS_PATH); + std::filesystem::copy_file(entry, SETTINGS_PATH); + + std::cout << "Testing " << testName << std::endl; + + try { + Randomizer r{RANDO_SAVE_PATH}; + r.GenerateWorlds(); + } + catch(const std::exception& e) { + std::cout << "Test \"" << testName << "\" failed! Failed settings saved to " << SETTINGS_PATH << std::endl; + std::cout << "Error Message: " << e.what() << std::endl; + throw; + } + + std::filesystem::remove(SETTINGS_PATH); + } + } + // Remove test preferences + std::filesystem::remove(PREFERENCES_PATH); + + std::cout << "All Settings Tests passed" << std::endl; + } +} // namespace randomizer::test::test diff --git a/mods/randomizer/generator/test/test.hpp b/mods/randomizer/generator/test/test.hpp new file mode 100644 index 0000000000..8e35574781 --- /dev/null +++ b/mods/randomizer/generator/test/test.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace randomizer::test::test +{ + void RunTests(); +} // namespace randomizer::test::test diff --git a/mods/randomizer/generator/utility/base64pp.hpp b/mods/randomizer/generator/utility/base64pp.hpp new file mode 100644 index 0000000000..a74a7b2d5a --- /dev/null +++ b/mods/randomizer/generator/utility/base64pp.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "base64pp/base64pp.h" + +// extra wrappers for convenience +inline std::string b64_encode(const std::string& asciiStr) { + std::vector permalinkBytes = {}; + for(const char& ch : asciiStr) + { + permalinkBytes.push_back(ch); + } + + std::span span(permalinkBytes.begin(), permalinkBytes.end()); + + return base64pp::encode(span); +} + +inline std::string b64_decode(const std::string& b64Str) { + const auto optional = base64pp::decode(b64Str); + if(!optional.has_value()) + { + // Return an empty string if there was an error + return ""; + } + + const auto& bytes = optional.value(); + return {bytes.begin(), bytes.end()}; +} \ No newline at end of file diff --git a/mods/randomizer/generator/utility/color.cpp b/mods/randomizer/generator/utility/color.cpp new file mode 100644 index 0000000000..4e1792c3d4 --- /dev/null +++ b/mods/randomizer/generator/utility/color.cpp @@ -0,0 +1,277 @@ +#include "../utility/color.hpp" +#include "../utility/string.hpp" + +#include + +HSV RGBToHSV(const double& r, const double& g, const double& b) { + double min, max, delta; + + HSV out; + + min = r < g ? r : g; + min = min < b ? min : b; + + max = r > g ? r : g; + max = max > b ? max : b; + + out.V = max; + delta = max - min; + + if(max > 0.0) { // NOTE: if Max is == 0, this divide would cause a crash + out.S = (delta / max); // s + } else { + // if max is 0, then r = g = b = 0 + // s = 0, h is undefined + out.S = 0.0; + } + + if (delta < 0.001) { + out.H = 0.0; + } else { + if (r >= max) { + out.H = (g - b) / delta; // between yellow & magenta + } else if (g >= max) { + out.H = 2.0 + (b - r) / delta; // between cyan & yellow + } else { + out.H = 4.0 + (r - g) / delta; // between magenta & cyan + } + + out.H *= 60.0; // degrees + + if(out.H < 0.0) { + out.H += 360.0; + } + } + return out; +} + +HSV RGBToHSV(RGBA color) { + return RGBToHSV(color.R, color.G, color.B); +} + +RGBA HSVToRGB(const HSV& hsv) { + double hh, p, q, t, ff; + long i; + RGBA out; + + if(hsv.S <= 0.0) { + out.R = hsv.V; + out.G = hsv.V; + out.B = hsv.V; + return out; + } + hh = hsv.H; + if (hh >= 360.0) hh = 0.0; + hh /= 60.0; + i = (long)hh; + ff = hh - i; + p = hsv.V * (1.0 - hsv.S); + q = hsv.V * (1.0 - (hsv.S * ff)); + t = hsv.V * (1.0 - (hsv.S * (1.0 - ff))); + + switch(i) { + case 0: + out.R = hsv.V; + out.G = t; + out.B = p; + break; + case 1: + out.R = q; + out.G = hsv.V; + out.B = p; + break; + case 2: + out.R = p; + out.G = hsv.V; + out.B = t; + break; + case 3: + out.R = p; + out.G = q; + out.B = hsv.V; + break; + case 4: + out.R = t; + out.G = p; + out.B = hsv.V; + break; + case 5: + default: + out.R = hsv.V; + out.G = p; + out.B = q; + break; + } + return out; +} + +HSV color16BitToHSV(const uint16_t& color) { + double r = (color & 0xF800) >> 11; + double g = (color & 0x07E0) >> 5; + double b = (color & 0x001F); + return RGBToHSV(r / 31.0, g / 63.0, b / 31.0); +} + +uint16_t colorHSVTo16Bit(const HSV& hsv) { + auto colorRGB = HSVToRGB(hsv); + + uint16_t color565 = 0; + color565 |= uint16_t(round(colorRGB.R * 31.0)) << 11; + color565 |= uint16_t(round(colorRGB.G * 63.0)) << 5; + color565 |= uint16_t(round(colorRGB.B * 31.0)); + + return color565; +} + +uint16_t hexColorStrTo16Bit(const std::string& hexColor) { + auto hex = std::stoi(hexColor, nullptr, 16); + + double r = ((hex & 0xFF0000) >> 16) / 255.0f; + double g = ((hex & 0x00FF00) >> 8) / 255.0f; + double b = (hex & 0x0000FF) / 255.0f; + + auto colorHSV = RGBToHSV(r, g, b); + return colorHSVTo16Bit(colorHSV); +} + +RGBA hexColorStrToRGB(const std::string& hexColor) { + auto hex = std::stoi(hexColor, nullptr, 16); + + double r = ((hex & 0xFF0000) >> 16) / 255.0f; + double g = ((hex & 0x00FF00) >> 8) / 255.0f; + double b = (hex & 0x0000FF) / 255.0f; + + return RGBA(r, g, b, 1); +} + +std::string RGBToHexColorStr(const RGBA& color) { + + int c = 0; + + c |= int(color.R * 255) << 16; + c |= int(color.G * 255) << 8; + c |= int(color.B * 255); + + return randomizer::utility::str::intToHex(c, 6, false); +} + +bool isValidHexColor(const std::string& hexColor) { + return hexColor.find_first_not_of("0123456789ABCDEFabcdef") == std::string_view::npos && hexColor.length() == 6; +} + +// Takes 16-bit base, replacement, and current colors. +// Outputs what the new 16-bit color in place of the current color should +// be based on the difference between the base and replacement colors +uint16_t colorExchange(const uint16_t& baseColor, const uint16_t& replacementColor, const uint16_t& curColor) { + + // Translate 16-bit colors into HSV color space + auto baseColorHSV = color16BitToHSV(baseColor); + auto replacementColorHSV = color16BitToHSV(replacementColor); + auto curColorHSV = color16BitToHSV(curColor); + + // Calculate difference between base and replacement colors + double sChange = replacementColorHSV.S - baseColorHSV.S; + double vChange = replacementColorHSV.V - baseColorHSV.V; + + // Prevent issues when recoloring black/white/grey parts of a texture where the base color is not black/white/grey. + if (curColorHSV.S == 0.0) { + curColorHSV.S = baseColorHSV.S; + } + + // Create new color from current color based on difference between base and replacement + HSV newColorHSV; + newColorHSV.H = replacementColorHSV.H; + newColorHSV.S = curColorHSV.S + sChange; + newColorHSV.V = curColorHSV.V + vChange; + + newColorHSV.S = std::max(0.0, std::min(1.0, newColorHSV.S)); + newColorHSV.V = std::max(0.0, std::min(1.0, newColorHSV.V)); + + return colorHSVTo16Bit(newColorHSV); +} + +std::string HSVShiftColor(const std::string& hexColor, const int& hShift, const int& vShift) { + auto colorRGB = hexColorStrToRGB(hexColor); + auto colorHSV = RGBToHSV(colorRGB); + int h = colorHSV.H; + int s = round(colorHSV.S * 100); + int v = round(colorHSV.V * 100); + + h += hShift; + h %= 360; + + auto origV = v; + v += vShift; + if (v < 0) { + v = 0; + } + if (v > 100) { + v = 100; + } + if (v < 30 && origV >= 30) { + v = 30; + } + if (v > 90 and origV <= 90) { + v = 90; + } + + auto vDiff = v - origV; + + // Instead of shifting saturation separately, we simply make it relative to the value shift. + // As value increases we want saturation to decrease and vice versa. + // This is because bright colors look bad if they are too saturated, and dark colors look bland if they aren't saturated enough. + auto origS = s; + if (origS < 15 && vShift > 0) { + // For colors that were originally very unsaturated, we want saturation to increase regardless of which direction value is shifting in. + if (origV < 30) { + // Very dark, nearly black. Needs extra saturation for the change to be noticeable. + s += (vShift * 2); + } else { + // Not that dark, probably grey or whitish. + s += vShift; + } + } else { + s -= vDiff; + } + + if (s < 0) { + s = 0; + } + if (s > 100) { + s = 100; + } + if (s < 5 && origS >= 5) { + s = 5; + } + if (s > 80 && origS <= 80) { + s = 80; + } + + auto newColorHSV = HSV(h, s / 100.0f, v / 100.0f); + auto newColorRGB = HSVToRGB(newColorHSV); + return RGBToHexColorStr(newColorRGB); +} + +std::pair get_random_h_and_v_shifts_for_custom_color(const std::string& hexColor) { + auto colorRGB = hexColorStrToRGB(hexColor); + auto colorHSV = RGBToHSV(colorRGB); + + int s = round(colorHSV.S * 100); + int v = round(colorHSV.V * 100); + + int minVShift = -40; + int maxVShift = 40; + + if (s < 10) { + // For very unsaturated colors, we want to limit the range of value + // randomization to exclude results that wouldn't change anything anyway. + // This effectively stops white and black from having a 50% chance to not change at all. + minVShift = std::max(-40, 0-v); + maxVShift = std::min(40, 100-v); + } + + auto hShift = rand() % 360; + auto vShift = (rand() % (maxVShift - minVShift)) + minVShift; + + return {hShift, vShift}; +} diff --git a/mods/randomizer/generator/utility/color.hpp b/mods/randomizer/generator/utility/color.hpp new file mode 100644 index 0000000000..fbff111bcd --- /dev/null +++ b/mods/randomizer/generator/utility/color.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include "../utility/common.hpp" + +template requires std::is_arithmetic_v +struct RGBA { + T R = 0; + T G = 0; + T B = 0; + T A = std::numeric_limits::max(); + + RGBA() = default; + + RGBA(const T& val, const T& alpha) : + R(val), + G(val), + B(val), + A(alpha) + {} + + RGBA(const T& r_, const T& g_, const T& b_ , const T& a_) : + R(r_), + G(g_), + B(b_), + A(a_) + {} +}; + +using RGBA8 = RGBA; + +template +bool readRGBA(std::istream& in, const std::streamoff& offset, RGBA& out) { + in.seekg(offset, std::ios::beg); + + if(!in.read(reinterpret_cast(&out.R), sizeof(out.R))) return false; + if(!in.read(reinterpret_cast(&out.G), sizeof(out.G))) return false; + if(!in.read(reinterpret_cast(&out.B), sizeof(out.B))) return false; + if(!in.read(reinterpret_cast(&out.A), sizeof(out.A))) return false; + + if constexpr (sizeof(T) > 1) { + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.R); + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.G); + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.B); + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.A); + } + + return true; +} + +template +void writeRGBA(std::ostream& out, const RGBA& color) { + if constexpr (sizeof(T) > 1) { + T R_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, color.R); + T G_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, color.G); + T B_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, color.B); + T A_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, color.A); + + out.write(reinterpret_cast(&R_BE), sizeof(R_BE)); + out.write(reinterpret_cast(&G_BE), sizeof(G_BE)); + out.write(reinterpret_cast(&B_BE), sizeof(B_BE)); + out.write(reinterpret_cast(&A_BE), sizeof(A_BE)); + return; + } + else { + out.write(reinterpret_cast(&color.R), sizeof(color.R)); + out.write(reinterpret_cast(&color.G), sizeof(color.G)); + out.write(reinterpret_cast(&color.B), sizeof(color.B)); + out.write(reinterpret_cast(&color.A), sizeof(color.A)); + } + + return; +} + +struct HSV { + double H = 0; + double S = 0; + double V = 0; + + HSV() = default; + + HSV(const double& h_, const double& s_, const double& v_) : + H(h_), + S(s_), + V(v_) + {} +}; + +HSV RGBToHSV(const double& r, const double& g, const double& b); + +HSV RGBToHSV(RGBA color); + +RGBA HSVToRGB(const HSV& hsv); + +HSV color16BitToHSV(const uint16_t& color); + +uint16_t colorHSVTo16Bit(const HSV& hsv); + +RGBA hexColorStrToRGB(const std::string& hexColor); + +std::string RGBToHexColorStr(const RGBA& color); + +uint16_t hexColorStrTo16Bit(const std::string& hexColor); + +bool isValidHexColor(const std::string& hexColor); + +uint16_t colorExchange(const uint16_t& baseColor, const uint16_t& replacementColor, const uint16_t& curColor); + +std::string HSVShiftColor(const std::string& hexColor, const int& hShift, const int& vShift); + +std::pair get_random_h_and_v_shifts_for_custom_color(const std::string& hexColor); diff --git a/mods/randomizer/generator/utility/common.cpp b/mods/randomizer/generator/utility/common.cpp new file mode 100644 index 0000000000..4a0210b5ad --- /dev/null +++ b/mods/randomizer/generator/utility/common.cpp @@ -0,0 +1,47 @@ +#include "common.hpp" + +std::string readNullTerminatedStr(std::istream& in, const unsigned int& offset) { + in.seekg(offset, std::ios::beg); + + std::string ret; + char character = '\0'; + do { + if (!in.read(&character, sizeof(char))) { + ret.clear(); + return ret; + } + ret += character; + } while (character != '\0'); + + return ret; +} + +std::u16string readNullTerminatedWStr(std::istream& in, const unsigned int offset) { + in.seekg(offset, std::ios::beg); + + std::u16string ret; + char16_t character = u'\0'; + do { + if (!in.read(reinterpret_cast(&character), sizeof(char16_t))) { + ret.clear(); + return ret; + } + ret += character; + } while (character != u'\0'); + + return ret; +} + + +size_t padToLen(std::ostream& out, const unsigned int& len, const char pad) { + if (len == 0) return 0; //don't pad to no alignment (also cant % by 0) + + size_t padLen = len - (static_cast(out.tellp()) % len); + if (padLen == len) return 0; //doesnt write any padding, return length 0 + + for (size_t i = 0; i < padLen; i++) { + out.write(&pad, 1); + } + + return padLen; //return number of bytes written +} diff --git a/mods/randomizer/generator/utility/common.hpp b/mods/randomizer/generator/utility/common.hpp new file mode 100644 index 0000000000..b625c33a9a --- /dev/null +++ b/mods/randomizer/generator/utility/common.hpp @@ -0,0 +1,199 @@ +#pragma once + +#include +#include +#include +#include + +#include "../utility/endian.hpp" + + +template requires std::is_arithmetic_v +struct vec2 { + T X; + T Y; + + vec2() = default; + vec2(const T& val) : + X(val), + Y(val) + {} +}; + +template requires std::is_arithmetic_v +struct vec3 { + T X; + T Y; + T Z; + + vec3() = default; + vec3(const T& val) : + X(val), + Y(val), + Z(val) + {} + vec3(const T& x_, const T& y_, const T& z_) : + X(x_), + Y(y_), + Z(z_) + {} +}; + +template requires std::is_arithmetic_v +struct vec4 { + T A; + T B; + T C; + T D; + + vec4() = default; + vec4(const T& val) : + A(val), + B(val), + C(val), + D(val) + {} +}; + + +template +bool readVec2(std::istream& in, const std::streamoff offset, vec2& out) { + in.seekg(offset, std::ios::beg); + + if (!in.read(reinterpret_cast(&out.X), sizeof(out.X))) return false; + if (!in.read(reinterpret_cast(&out.Y), sizeof(out.Y))) return false; + + if constexpr (sizeof(T) > 1) { + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.X); + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.Y); + } + + return true; +} + +template +void writeVec2(std::ostream& out, const vec2& vec) { + if constexpr (sizeof(T) > 1) { + T X_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, vec.X); + T Y_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, vec.Y); + + out.write(reinterpret_cast(&X_BE), sizeof(X_BE)); + out.write(reinterpret_cast(&Y_BE), sizeof(Y_BE)); + return; + } + else { + out.write(reinterpret_cast(&vec.X), sizeof(vec.X)); + out.write(reinterpret_cast(&vec.Y), sizeof(vec.Y)); + return; + } +} + + +template +bool readVec3(std::istream& in, const std::streamoff offset, vec3& out) { + in.seekg(offset, std::ios::beg); + + if (!in.read(reinterpret_cast(&out.X), sizeof(out.X))) return false; + if (!in.read(reinterpret_cast(&out.Y), sizeof(out.Y))) return false; + if (!in.read(reinterpret_cast(&out.Z), sizeof(out.Z))) return false; + + if constexpr (sizeof(T) > 1) { + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.X); + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.Y); + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.Z); + } + + return true; +} + +template +void writeVec3(std::ostream& out, const vec3& vec) { + if constexpr (sizeof(T) > 1) { + T X_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, vec.X); + T Y_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, vec.Y); + T Z_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, vec.Z); + + out.write(reinterpret_cast(&X_BE), sizeof(X_BE)); + out.write(reinterpret_cast(&Y_BE), sizeof(Y_BE)); + out.write(reinterpret_cast(&Z_BE), sizeof(Z_BE)); + return; + } + else { + out.write(reinterpret_cast(&vec.X), sizeof(vec.X)); + out.write(reinterpret_cast(&vec.Y), sizeof(vec.Y)); + out.write(reinterpret_cast(&vec.Z), sizeof(vec.Z)); + return; + } +} + + +template +bool readVec4(std::istream& in, const std::streamoff offset, vec4& out) { + in.seekg(offset, std::ios::beg); + + if (!in.read(reinterpret_cast(&out.A), sizeof(out.A))) return false; + if (!in.read(reinterpret_cast(&out.B), sizeof(out.B))) return false; + if (!in.read(reinterpret_cast(&out.C), sizeof(out.C))) return false; + if (!in.read(reinterpret_cast(&out.D), sizeof(out.D))) return false; + + if constexpr (sizeof(T) > 1) { + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.A); + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.B); + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.C); + Utility::Endian::toPlatform_inplace(Utility::Endian::Type::Big, out.D); + } + + return true; +} + +template +void writeVec4(std::ostream& out, const vec4& vec) { + if constexpr (sizeof(T) > 1) { + T A_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, vec.A); + T B_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, vec.B); + T C_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, vec.C); + T D_BE = Utility::Endian::toPlatform(Utility::Endian::Type::Big, vec.D); + + out.write(reinterpret_cast(&A_BE), sizeof(A_BE)); + out.write(reinterpret_cast(&B_BE), sizeof(B_BE)); + out.write(reinterpret_cast(&C_BE), sizeof(C_BE)); + out.write(reinterpret_cast(&D_BE), sizeof(D_BE)); + return; + } + else { + out.write(reinterpret_cast(&vec.A), sizeof(vec.A)); + out.write(reinterpret_cast(&vec.B), sizeof(vec.B)); + out.write(reinterpret_cast(&vec.C), sizeof(vec.C)); + out.write(reinterpret_cast(&vec.D), sizeof(vec.D)); + return; + } +} + +std::string readNullTerminatedStr(std::istream& in, const unsigned int& offset); + +std::u16string readNullTerminatedWStr(std::istream& in, const unsigned int offset); + +template requires requires { + requires std::is_enum_v; + error_enum::NONE; + error_enum::REACHED_EOF; + error_enum::UNEXPECTED_VALUE; +} +error_enum readPadding(std::istream& in, const unsigned int& len, const char* val = nullptr) { + if (in.tellg() % len != 0) { + const size_t& padding_size = len - (static_cast(in.tellg()) % len); + + std::string padding(padding_size, '\0'); + if (!in.read(&padding[0], static_cast(padding_size))) return error_enum::REACHED_EOF; + + if(val != nullptr) { + for (const char& character : padding) { + if (character != *val) return error_enum::UNEXPECTED_VALUE; + } + } + } + + return error_enum::NONE; +} + +size_t padToLen(std::ostream& out, const unsigned int& len, const char pad = '\x00'); diff --git a/mods/randomizer/generator/utility/container.hpp b/mods/randomizer/generator/utility/container.hpp new file mode 100644 index 0000000000..3ed0fc7217 --- /dev/null +++ b/mods/randomizer/generator/utility/container.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +namespace randomizer::utility::container +{ + template + int GetIndex(const Container& container, const T& element) + { + auto it = std::find(container.begin(), container.end(), element); + if (it == container.end()) + { + return -1; + } + return std::distance(container.begin(), it); + } + + template + bool ElementInContainer(const Container& container, const T& element) + { + auto it = std::find(container.begin(), container.end(), element); + if (it == container.end()) + { + return false; + } + return true; + } + + template + std::vector FilterFromVector(std::vector& vector, Predicate pred, bool eraseAfterFilter = false) + { + std::vector filteredPool = {}; + std::copy_if(vector.begin(), vector.end(), std::back_inserter(filteredPool), pred); + + if (eraseAfterFilter) + { + std::erase_if(vector, pred); + } + + return filteredPool; + } + + template + std::vector FilterAndEraseFromVector(std::vector& vector, Predicate pred) + { + return FilterFromVector(vector, pred, true); + } + + /** + * @brief Erases a number of elements a container. If there are no more of the specified element to erase, then nothing + * happens. + * + * @param container The container to erase elements from + * @param element The value of the element to erase from the container + * @param numberToErase The number of elements of the specified value to erase (default 1) + */ + template + void Erase(Container& container, T element, int numberToErase = 1) + { + for (int i = 0; i < numberToErase; i++) + { + auto itr = std::find(container.begin(), container.end(), element); + if (itr != container.end()) + { + container.erase(itr); + } + } + } +} // namespace randomizer::utility::container diff --git a/mods/randomizer/generator/utility/crc32.hpp b/mods/randomizer/generator/utility/crc32.hpp new file mode 100644 index 0000000000..a026fcab64 --- /dev/null +++ b/mods/randomizer/generator/utility/crc32.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace randomizer::utility { + +// Generates standard lookup table +constexpr std::array generate_crc32_table() { + std::array table{}; + for (uint32_t i = 0; i < 256; ++i) { + uint32_t ch = i; + for (size_t j = 0; j < 8; ++j) { + ch = (ch & 1) ? (0xEDB88320 ^ (ch >> 1)) : (ch >> 1); + } + table[i] = ch; + } + return table; +} + +inline constexpr std::array crc32_table = generate_crc32_table(); + +inline uint32_t crc32(const void* data, size_t length, uint32_t previous_crc = 0) { + const auto* bytes = static_cast(data); + uint32_t crc = ~previous_crc; + + for (size_t i = 0; i < length; ++i) { + auto index = static_cast(crc ^ bytes[i]); + crc = (crc >> 8) ^ crc32_table[index]; + } + + return ~crc; +} + +} \ No newline at end of file diff --git a/mods/randomizer/generator/utility/endian.cpp b/mods/randomizer/generator/utility/endian.cpp new file mode 100644 index 0000000000..ce58638023 --- /dev/null +++ b/mods/randomizer/generator/utility/endian.cpp @@ -0,0 +1,103 @@ +#include "../utility/endian.hpp" + +#ifndef __cpp_lib_endian + #include "../utility/platform.hpp" +#endif + +namespace Utility::Endian +{ + + #ifdef __cpp_lib_endian + #pragma message("Using C++20 endianness") + #else + #pragma message("Using runtime endian check") + + Type getEndian() { + static const uint16_t TestVal = 0x0001; + static const uint8_t tester = *reinterpret_cast(&TestVal); + + if(tester == 0x00) { + return Type::Big; + } + else if (tester == 0x01) { + return Type::Little; + } + else { + randomizer::utility::platform::Log("Warning: Could not determine endianness!"); + randomizer::utility::platform::Log("Using little endian as default"); + return Type::Little; + } + } + #endif + + uint64_t byteswap(const uint64_t& value) + { + return ((value & 0xFF00000000000000) >> 56) | + ((value & 0x00FF000000000000) >> 40) | + ((value & 0x0000FF0000000000) >> 24) | + ((value & 0x000000FF00000000) >> 8) | + ((value & 0x00000000FF000000) << 8) | + ((value & 0x0000000000FF0000) << 24) | + ((value & 0x000000000000FF00) << 40) | + ((value & 0x00000000000000FF) << 56); + } + + uint32_t byteswap(const uint32_t& value) + { + return ((value & 0xFF000000) >> 24) | + ((value & 0x00FF0000) >> 8) | + ((value & 0x0000FF00) << 8) | + ((value & 0x000000FF) << 24); + } + + uint32_t byteswap24(const uint32_t& value) + { + return ((value & 0x00FF0000) >> 16) | + ((value & 0x0000FF00)) | + ((value & 0x000000FF) << 16); + } + + uint16_t byteswap(const uint16_t& value) + { + return ((value & 0xFF00) >> 8) | ((value & 0x00FF) << 8); + } + + int64_t byteswap(const int64_t& value) + { + return std::bit_cast(byteswap(std::bit_cast(value))); + } + + int32_t byteswap(const int32_t& value) + { + return std::bit_cast(byteswap(std::bit_cast(value))); + } + + int16_t byteswap(const int16_t& value) + { + return std::bit_cast(byteswap(std::bit_cast(value))); + } + + float byteswap(const float& value) + { + return std::bit_cast(byteswap(std::bit_cast(value))); + } + + double byteswap(const double& value) + { + return std::bit_cast(byteswap(std::bit_cast(value))); + } + + char16_t byteswap(const char16_t& value) + { + return std::bit_cast(byteswap(std::bit_cast(value))); + } + + std::u16string byteswap(const std::u16string& value) { + std::u16string result = value; + for(char16_t& character : result) { + character = byteswap(character); + } + + return result; + } +} diff --git a/mods/randomizer/generator/utility/endian.hpp b/mods/randomizer/generator/utility/endian.hpp new file mode 100644 index 0000000000..944c4869d4 --- /dev/null +++ b/mods/randomizer/generator/utility/endian.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include + + + +namespace Utility::Endian +{ + enum struct Type { + Big = 0, + Little = 1 + }; + +#ifdef __cpp_lib_endian + //use the c++20 api if possible + constexpr Type target = std::endian::native == std::endian::big ? Type::Big : Type::Little; + constexpr inline bool isBE() { return target == Type::Big; } +#else + //do a runtime check otherwise + Type getEndian(); + const Type target = getEndian(); + inline bool isBE() { return target == Type::Big; } +#endif + + uint64_t byteswap(const uint64_t& value); + + uint32_t byteswap(const uint32_t& value); + + uint32_t byteswap24(const uint32_t& value); //used in FST files + + uint16_t byteswap(const uint16_t& value); + + int64_t byteswap(const int64_t& value); + + int32_t byteswap(const int32_t& value); + + int16_t byteswap(const int16_t& value); + + [[deprecated("Platform may silently set NaN bits, bit_cast to uint32_t first if possible.")]] float byteswap(const float& value); + + [[deprecated("Platform may silently set NaN bits, bit_cast to uint64_t first if possible.")]] double byteswap(const double& value); + + char16_t byteswap(const char16_t& value); + + std::u16string byteswap(const std::u16string& value); + + template + concept CanByteswap = sizeof(T) > 1; + + template requires CanByteswap && (!std::is_enum_v) + constexpr T toPlatform(const Type& src, const T& value) { + if (src != target) return byteswap(value); + return value; + } + + //for enums + template> requires CanByteswap && std::is_enum_v + constexpr T toPlatform(const Type& src, const T& value) { + if (src != target) return static_cast(byteswap(static_cast(value))); + return value; + } + + //doesn't work for enums + template requires CanByteswap && (!std::is_enum_v) + constexpr void toPlatform_inplace(const Type& src, T& value) { + if (src != target) value = byteswap(value); + } + + //for enums + template> requires CanByteswap && std::is_enum_v + constexpr void toPlatform_inplace(const Type& src, T& value) { + if (src != target) value = static_cast(byteswap(static_cast(value))); + } +} diff --git a/mods/randomizer/generator/utility/exception.hpp b/mods/randomizer/generator/utility/exception.hpp new file mode 100644 index 0000000000..30fb844c5d --- /dev/null +++ b/mods/randomizer/generator/utility/exception.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "../utility/log.hpp" + +#define RUNTIME_ERROR(msg) std::runtime_error(std::string(msg) + " on line " TOSTRING(__LINE__) " of " __FILENAME__) diff --git a/mods/randomizer/generator/utility/file.cpp b/mods/randomizer/generator/utility/file.cpp new file mode 100644 index 0000000000..dccc2b0149 --- /dev/null +++ b/mods/randomizer/generator/utility/file.cpp @@ -0,0 +1,218 @@ +#include "../utility/file.hpp" +#include "../utility/log.hpp" +#include "../utility/path.hpp" +#include "../utility/platform.hpp" +#ifdef DEVKITPRO +#include "../utility/thread_local.hpp" +#endif + +#include +#include +#include + +#if defined(QT_GUI) && defined(EMBED_DATA) +#include +#include +#endif + +namespace randomizer::utility::file +{ + bool isRoot(const fspath& fsPath) + { + static const std::regex rootFilesystem(R"(^fs:\/vol\/[^\/:]+\/?$)"); + + const std::string path = fsPath.string(); + + if (path.size() >= 2 && path.ends_with(":")) + return true; + if (path.size() >= 3 && path.ends_with(":/")) + return true; + if (std::regex_match(path, rootFilesystem)) + return true; + + return false; + }; + +#ifdef DEVKITPRO + static constexpr int FILE_BUF_SIZE = 25 * 1024 * 1024; + class AlignedBufferWrapper + { + private: + alignas(0x40) char buffer[FILE_BUF_SIZE]; + + public: + char* getBuffer() { return buffer; } + }; + static ThreadLocal buf; +#endif + + bool copy_file(const fspath& from, const fspath& to) + { + randomizer::utility::platform::Log("Copying " + Utility::toUtf8String(to)); +#ifdef DEVKITPRO + // use a buffer to speed up file copying + + std::ifstream src(from, std::ios::binary); + std::ofstream dst(to, std::ios::binary); + if (!src.is_open()) + { + ErrorLog::getInstance().log("Failed to open " + from.string()); + return false; + } + if (!dst.is_open()) + { + ErrorLog::getInstance().log("Failed to open " + to.string()); + return false; + } + + while (src) + { + src.read(buf.get().getBuffer(), FILE_BUF_SIZE); + dst.write(buf.get().getBuffer(), src.gcount()); + } + return true; +#else +// GNU on windows currently has a bug where you can't copy over a file that already exists +// even if you pass std::filesystem::copy_options::overwrite_existing. So delete the copy location +// file in this case +#if defined(WIN32) && defined(__GNUG__) + std::filesystem::remove(to); +#endif + return std::filesystem::copy_file(from, to, std::filesystem::copy_options::overwrite_existing); +#endif + } + + bool copy(const fspath& from, const fspath& to) + { +#ifdef DEVKITPRO + // based on https://github.com/emiyl/dumpling/blob/12935ede46e9720fdec915cdb430d10eb7df54a7/source/app/dumping.cpp#L208 + + DIR* dirHandle; + if ((dirHandle = opendir(from.string().c_str())) == nullptr) + { + ErrorLog::getInstance().log("Couldn't open directory to copy files from: " + to.string()); + return false; + } + + randomizer::utility::file::create_directories(to); + + // Loop over directory contents + struct dirent* dirEntry; + while ((dirEntry = readdir(dirHandle)) != nullptr) + { + const std::string entrySrcPath = from / dirEntry->d_name; + const std::string entryDstPath = to / dirEntry->d_name; + + // Use lstat since readdir returns DT_REG for symlinks + struct stat fileStat; + if (lstat(entrySrcPath.c_str(), &fileStat) != 0) + { + ErrorLog::getInstance().log("Couldn't check what type this file/folder was: " + entrySrcPath); + return false; + } + + if (S_ISLNK(fileStat.st_mode)) + { + continue; + } + else if (S_ISREG(fileStat.st_mode)) + { + // Copy file + if (!copy_file(entrySrcPath, entryDstPath)) + { + ErrorLog::getInstance().log("Failed to copy file: " + entrySrcPath); + closedir(dirHandle); + return false; + } + } + else if (S_ISDIR(fileStat.st_mode)) + { + // Ignore root and parent folder entries + if (std::strncmp(dirEntry->d_name, ".", 1) == 0 || std::strncmp(dirEntry->d_name, "..", 2) == 0) + continue; + + // Copy all the files in this subdirectory + if (!copy(entrySrcPath, entryDstPath)) + { + ErrorLog::getInstance().log("Failed to copy dir: " + entrySrcPath); + closedir(dirHandle); + return false; + } + } + } + + closedir(dirHandle); +#else + std::filesystem::copy(from, to, std::filesystem::copy_options::recursive); +#endif + + return true; + } + + // Short function for getting the string data from a file + int GetContents(const fspath& filename, std::string& fileContents, bool resourceFile /*= false*/) + { + if (resourceFile) + { +// If this is a resource file and the data has been embedded, then load it from +// the embedded resources file +#if defined(QT_GUI) && defined(EMBED_DATA) + QResource file(Utility::toQString(filename)); + if (!file.isValid()) + { + return 1; + } + + QByteArray data = file.uncompressedData(); + if (data.isNull()) + { + return 1; + } + + fileContents = data.toStdString(); + return 0; +#endif + } + + // Otherwise load it normally + auto ss = std::stringstream {}; + if (const auto err = GetContents(filename, ss); err != 0) + return err; + fileContents = ss.str(); + return 0; + } + + // Short function for getting the string data from a file + int GetContents(const fspath& filename, std::stringstream& fileContents) + { + // Otherwise load it normally + std::ifstream file(filename, std::ios::binary); + if (!file.is_open()) + { + LOG_TO_ERROR("Unable to open file \"" + Utility::toUtf8String(filename) + "\""); + return 1; + } + +#ifdef DEVKITPRO + while (file) + { + file.read(buf.get().getBuffer(), FILE_BUF_SIZE); + fileContents.write(buf.get().getBuffer(), file.gcount()); + } +#else + fileContents << file.rdbuf(); +#endif + + return 0; + } + + void Verify(const fspath& filename) + { + std::ifstream file(filename); + if (!file.is_open()) + { + throw std::runtime_error("Could not open " + Utility::toUtf8String(filename)); + } + file.close(); + } +} // namespace randomizer::utility::file diff --git a/mods/randomizer/generator/utility/file.hpp b/mods/randomizer/generator/utility/file.hpp new file mode 100644 index 0000000000..3f98c0e7ab --- /dev/null +++ b/mods/randomizer/generator/utility/file.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include +#include + +#include "../utility/path.hpp" + +#ifdef DEVKITPRO + #include + #include +#endif + +namespace randomizer::utility::file +{ + //std::filesystem is partially broken on Wii U, these are cross-platform replacements + + inline std::ostream& seek(std::ostream& stream, const std::streamoff& off, const std::ios::seekdir& way = std::ios::beg) { + //#ifdef DEVKITPRO + //Wii U crashes if you seek past eof, most other platforms extend the file + //Handle writing the extra padding manually + + switch(way) { + case std::ios::cur: + { + const std::streamoff& cur = stream.tellp(); + if(off > 0) { + stream.seekp(0, std::ios::end); + if(stream.tellp() < (cur + off)) { + const std::string buffer((cur + off) - stream.tellp(), '\0'); + stream.write(&buffer[0], buffer.size()); + } + } + else if ((cur + off) < 0) { + //can't seek before start of file, seek to beginning as failsafe + return stream.seekp(0, std::ios::beg); + } + return stream.seekp(cur + off, std::ios::beg); + } + case std::ios::end: + //BUG: seek to std::ios::end doesn't seem to work on MLC, find workaround? (relevant uses are currently replaced) + { + stream.seekp(0, std::ios::end); + if(off > 0) { + const std::string buffer(off, '\0'); + stream.write(&buffer[0], buffer.size()); + } + else if((-off) > stream.tellp()) { + //Can't seek before start of file, seek to beginning as failsafe + return stream.seekp(0, std::ios::beg); + } + return stream.seekp(off, std::ios::end); + } + case std::ios::beg: + [[fallthrough]]; + default: + { + if(off < 0) { + //can't seek before start of file, seek to beginning as failsafe + stream.seekp(0, std::ios::beg); + } + stream.seekp(0, std::ios::end); + if(stream.tellp() < off) { + const std::string buffer(off - stream.tellp(), '\0'); + stream.write(&buffer[0], buffer.size()); + } + return stream.seekp(off, std::ios::beg); + } + } + //#else + // return stream.seekp(off, way); + //#endif + } + + //from https://github.com/emiyl/dumpling/blob/5dc5131243385050e45339779e75a2eaad31f1e4/source/app/filesystem.cpp#L177 + bool isRoot(const fspath& fsPath); + + //from https://github.com/emiyl/dumpling/blob/5dc5131243385050e45339779e75a2eaad31f1e4/source/app/filesystem.cpp#L193 + inline bool dirExists(const fspath& fsPath) { + #ifdef DEVKITPRO + static struct stat existStat; + if (isRoot(fsPath)) return true; + if (lstat(fsPath.string().c_str(), &existStat) == 0 && S_ISDIR(existStat.st_mode)) return true; + return false; + #else + return std::filesystem::is_directory(fsPath); + #endif + } + + inline bool create_directories(const fspath& fsPath) { + #ifdef DEVKITPRO + std::string temp = fsPath.string(); + if(temp.back() == '/') temp.pop_back(); + for(size_t i = 0; i < temp.size(); i++) { + if(temp[i] == '/') { + const std::string& sub = temp.substr(0, i); + if (!dirExists(sub)) { + mkdir(sub.c_str(), ACCESSPERMS); + } + } + } + mkdir(temp.c_str(), ACCESSPERMS); + #else + std::filesystem::create_directories(fsPath); + #endif + + return true; + } + + bool copy_file(const fspath& from, const fspath& to); + + bool copy(const fspath& from, const fspath& to); + + int GetContents(const fspath& filename, std::string& fileContents, bool resourceFile = false); + + int GetContents(const fspath& filename, std::stringstream& fileContents); + + void Verify(const fspath& filename); +} diff --git a/mods/randomizer/generator/utility/general.hpp b/mods/randomizer/generator/utility/general.hpp new file mode 100644 index 0000000000..815e61a5f9 --- /dev/null +++ b/mods/randomizer/generator/utility/general.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace randomizer::utility::general +{ + template + bool IsAnyOf(First&& first, T&&... t) + { + return ((first == t) || ...); + } +} // namespace randomizer::utility::general diff --git a/mods/randomizer/generator/utility/log.cpp b/mods/randomizer/generator/utility/log.cpp new file mode 100644 index 0000000000..ca4337c10c --- /dev/null +++ b/mods/randomizer/generator/utility/log.cpp @@ -0,0 +1,101 @@ +#include "../utility/log.hpp" +#include "../utility/time.hpp" + +#define RANDOMIZER_VERSION "1.0.0" + +namespace randomizer::utility::log +{ + LogInfo::LogInfo() {} + + LogInfo::~LogInfo() {} + + LogInfo& LogInfo::getInstance() + { + static LogInfo s_Instance; + return s_Instance; + } + + const randomizer::seedgen::config::Config& LogInfo::getConfig() + { + return getInstance().config; + } + + const std::string& LogInfo::getSeedHash() + { + return getInstance().seedHash; + } + + ErrorLog::ErrorLog() + { +#ifdef RANDO_ERROR_LOG + output.open(LOG_PATH); + output << "Program opened " << randomizer::utility::time::ProgramTime::getDateStr(); // time string ends with \n + output << "Dusk Randomizer Version " << RANDOMIZER_VERSION << std::endl; + output << std::endl << std::endl; +#endif + } + + ErrorLog::~ErrorLog() + { +#ifdef RANDO_ERROR_LOG + output.close(); +#endif + } + + ErrorLog& ErrorLog::getInstance() + { + static ErrorLog s_Instance; + return s_Instance; + } + + void ErrorLog::log(const std::string& msg, const bool& timestamp) + { +#ifdef RANDO_ERROR_LOG + if (timestamp) + output << "[" << randomizer::utility::time::ProgramTime::getTimeStr() << "] "; + output << msg << std::endl; +#endif + lastErrors.push_front(msg); + } + + std::string ErrorLog::getLastErrors() const + { + std::string retStr = ""; + for (auto& error : lastErrors) + { + retStr += error + "\n"; + } + return retStr; + } + + void ErrorLog::clearLastErrors() + { + lastErrors.clear(); + } + + DebugLog::DebugLog() + { + output.open(LOG_PATH); + output << "Program opened " << randomizer::utility::time::ProgramTime::getDateStr(); // time string ends with \n + output << "Dusk Randomizer Version " << RANDOMIZER_VERSION << std::endl; + output << std::endl << std::endl; + } + + DebugLog::~DebugLog() + { + output.close(); + } + + DebugLog& DebugLog::getInstance() + { + static DebugLog s_Instance; + return s_Instance; + } + + void DebugLog::log(const std::string& msg, const bool& timestamp) + { + if (timestamp) + output << "[" << randomizer::utility::time::ProgramTime::getTimeStr() << "] "; + output << msg << std::endl; + } +} // namespace randomizer::utility::log diff --git a/mods/randomizer/generator/utility/log.hpp b/mods/randomizer/generator/utility/log.hpp new file mode 100644 index 0000000000..8c1204fcbc --- /dev/null +++ b/mods/randomizer/generator/utility/log.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include + +#include "../seedgen/config.hpp" +#include "../utility/path.hpp" + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define __FILENAME__ (&__FILE__[SOURCE_PATH_SIZE]) + +namespace randomizer::utility::log +{ + class LogInfo + { + private: + seedgen::config::Config config; + std::string seedHash; + + LogInfo(); + ~LogInfo(); + + static LogInfo& getInstance(); + + public: + LogInfo(const LogInfo&) = delete; + LogInfo& operator=(const LogInfo&) = delete; + + static void setConfig(const seedgen::config::Config& config_) { getInstance().config = config_; } + static void setSeedHash(const std::string& seedHash_) { getInstance().seedHash = seedHash_; } + static const seedgen::config::Config& getConfig(); + static const std::string& getSeedHash(); + }; + + class ErrorLog + { + private: + static constexpr size_t MAX_ERRORS = 5; + + std::ofstream output; + std::list lastErrors; + + ErrorLog(); + ~ErrorLog(); + + public: + const fspath LOG_PATH = Utility::get_app_save_path() / "Error Log.txt"; + + ErrorLog(const ErrorLog&) = delete; + ErrorLog& operator=(const ErrorLog&) = delete; + + static ErrorLog& getInstance(); + void log(const std::string& msg, const bool& timestamp = true); + std::string getLastErrors() const; + void clearLastErrors(); + }; + +#define LOG_ERR_AND_RETURN(error) \ + { \ + ErrorLog::getInstance().log(std::string("Encountered " #error " on line " TOSTRING(__LINE__) " of ") + __FILENAME__); \ + return error; \ + } + +#define LOG_AND_RETURN_IF_ERR(func) \ + { \ + if (const auto error = func; error != decltype(error)::NONE) \ + { \ + ErrorLog::getInstance().log(std::string("Encountered error on line " TOSTRING(__LINE__) " of ") + __FILENAME__); \ + return error; \ + } \ + } + +#define LOG_ERR_AND_RETURN_BOOL(error) \ + { \ + ErrorLog::getInstance().log(std::string("Encountered " #error " on line " TOSTRING(__LINE__) " of ") + __FILENAME__); \ + return false; \ + } + +#define LOG_AND_RETURN_BOOL_IF_ERR(func) \ + { \ + if (const auto error = func; error != decltype(error)::NONE) \ + { \ + ErrorLog::getInstance().log(std::string("Encountered error on line " TOSTRING(__LINE__) " of ") + __FILENAME__); \ + return false; \ + } \ + } + + class DebugLog + { + private: + std::ofstream output; + + DebugLog(); + ~DebugLog(); + + public: + const fspath LOG_PATH = Utility::get_app_save_path() / "Debug Log.txt"; + + DebugLog(const DebugLog&) = delete; + DebugLog& operator=(const DebugLog&) = delete; + + static DebugLog& getInstance(); + void log(const std::string& msg, const bool& timestamp = true); + }; +} // namespace randomizer::utility::log + +#ifdef RANDO_DEBUG +#define LOG_TO_DEBUG(message) \ + randomizer::utility::log::DebugLog::getInstance().log(std::string("Message on line " TOSTRING(__LINE__) " of ") + \ + __FILENAME__ + std::string(": " + std::string(message))); +#else +#define LOG_TO_DEBUG(message) +#endif + +#define LOG_TO_ERROR(message) \ + randomizer::utility::log::ErrorLog::getInstance().log(std::string("Message on line " TOSTRING(__LINE__) " of ") + \ + __FILENAME__ + std::string(": " + std::string(message))); diff --git a/mods/randomizer/generator/utility/math.hpp b/mods/randomizer/generator/utility/math.hpp new file mode 100644 index 0000000000..ac48964ee1 --- /dev/null +++ b/mods/randomizer/generator/utility/math.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +template requires std::is_arithmetic_v +T roundUp(const T& val, const T& multiple) { + if(val % multiple == 0) return val; + return val + multiple - (val % multiple); +} diff --git a/mods/randomizer/generator/utility/path.cpp b/mods/randomizer/generator/utility/path.cpp new file mode 100644 index 0000000000..970f35153d --- /dev/null +++ b/mods/randomizer/generator/utility/path.cpp @@ -0,0 +1,71 @@ +#include "path.hpp" +#include "file.hpp" + +#if defined(QT_GUI) + #if defined(__APPLE__) + #include + #else + #include + #endif +#endif + +namespace Utility { + fspath get_data_path() { + #if defined(QT_GUI) + #if defined(EMBED_DATA) + return ":/"; + #else + return fromQString(QCoreApplication::applicationDirPath()) / "data/"; + #endif + #elif defined(DEVKITPRO) + return "/vol/content/"; + #elif defined(APPLE) + return "../../../data/"; + #else + return "./data/"; + #endif + } + + fspath get_app_save_path() { + fspath path; + #if defined(__APPLE__) && defined(QT_GUI) + path = fromQString(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + #elif defined(QT_GUI) + path = fromQString(QCoreApplication::applicationDirPath()); + #elif defined(DEVKITPRO) + return "/vol/save/"; + #else + return "./"; + #endif + + if (!std::filesystem::is_directory(path)) + { + randomizer::utility::file::create_directories(path); + } + + return path; + } + + fspath get_logs_path() { + const fspath path = get_app_save_path() / "logs/"; + + if (!std::filesystem::is_directory(path)) + { + randomizer::utility::file::create_directories(path); + } + + return path; + } + + fspath get_temp_dir() { + // could get the OS-provided temp folder with Qt but it might be harder to find and debug should we use it for anything + const fspath path = get_app_save_path() / "temp/"; + + if (!std::filesystem::is_directory(path)) + { + randomizer::utility::file::create_directories(path); + } + + return path; + } +} diff --git a/mods/randomizer/generator/utility/path.hpp b/mods/randomizer/generator/utility/path.hpp new file mode 100644 index 0000000000..cf8fa666ae --- /dev/null +++ b/mods/randomizer/generator/utility/path.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#ifdef QT_GUI + #include +#endif + +using fspath = std::filesystem::path; + +namespace Utility { + fspath get_data_path(); + fspath get_app_save_path(); + fspath get_logs_path(); + fspath get_temp_dir(); + + // On Windows, fspath.string() will throw an exception if the character requires some kind of Unicode/non-ANSI representation + // using .u8string() fixes this, but a lot of the randomizer still expects std::string which is not implicitly convertible + // std::string should still properly store a UTF-8 string, so this wrapper does that conversion + inline std::string toUtf8String(const fspath& path) { + const std::u8string& pathStr = path.u8string(); + return std::string(pathStr.begin(), pathStr.end()); + } + + #ifdef QT_GUI + // Wrapper for path -> QString + // Use a wide string type to cover Windows where paths are UTF-16 encoded (and hopefully still be fine on other platforms) + // Also use the "generic" version with '/' separators because the Windows '\' breaks some paths + inline QString toQString(const fspath& path) { return QString::fromStdU32String(path.generic_u32string()); } + inline fspath fromQString(const QString& path) { return path.toStdU32String(); } + #endif +} diff --git a/mods/randomizer/generator/utility/platform.cpp b/mods/randomizer/generator/utility/platform.cpp new file mode 100644 index 0000000000..94d9195b34 --- /dev/null +++ b/mods/randomizer/generator/utility/platform.cpp @@ -0,0 +1,263 @@ +#include "../utility/platform.hpp" +#include "../utility/log.hpp" + +#include +#include + +#ifdef PLATFORM_DKP +#include + +#include + +#define PRINTF_BUFFER_LENGTH 2048 + +static bool mochaOpen = false; +static bool MLCMounted = false; +static bool USBMounted = false; +static bool DiscMounted = false; +#endif + +static std::mutex printMut; + +#ifdef PLATFORM_DKP +static bool flushVolume(const std::string& vol) +{ + const FSAClientHandle handle = FSAAddClient(NULL); + if (handle < 0) + { + return false; + } + + if (FSAFlushVolume(handle, vol.c_str()) != FS_ERROR_OK) + { + return false; + } + + if (FSADelClient(handle) != FS_ERROR_OK) + { + return false; + } + + return true; +} + +bool initMocha() +{ + randomizer::utility::platform::Log("Starting libmocha..."); + + if (const MochaUtilsStatus status = Mocha_InitLibrary(); status != MOCHA_RESULT_SUCCESS) + { + ErrorLog::getInstance().log(std::string("Mocha_InitLibrary() failed, error ") + Mocha_GetStatusStr(status)); + return false; + } + + randomizer::utility::platform::Log("Mocha initialized"); + return true; +} + +void closeMocha() +{ + if (MLCMounted) + { + if (!flushVolume("/vol/storage_mlc01")) + { // maybe check if we wrote to MLC + ErrorLog::getInstance().log("Could not flush MLC"); + } + if (const MochaUtilsStatus status = Mocha_UnmountFS("storage_mlc01"); status != MOCHA_RESULT_SUCCESS) + { + ErrorLog::getInstance().log(std::string("Error unmounting MLC: ") + Mocha_GetStatusStr(status)); + } + MLCMounted = false; + } + + if (USBMounted) + { + if (!flushVolume("/vol/storage_usb01")) + { // maybe check if we wrote to USB + ErrorLog::getInstance().log("Could not flush USB"); + } + if (const MochaUtilsStatus status = Mocha_UnmountFS("storage_usb01"); status != MOCHA_RESULT_SUCCESS) + { + ErrorLog::getInstance().log(std::string("Error unmounting USB: ") + Mocha_GetStatusStr(status)); + } + USBMounted = false; + } + + if (DiscMounted) + { + if (const MochaUtilsStatus status = Mocha_UnmountFS("storage_odd_content"); status != MOCHA_RESULT_SUCCESS) + { + ErrorLog::getInstance().log(std::string("Error unmounting disc: ") + Mocha_GetStatusStr(status)); + } + DiscMounted = false; + } + + if (const MochaUtilsStatus status = Mocha_DeInitLibrary(); status != MOCHA_RESULT_SUCCESS) + { + ErrorLog::getInstance().log(std::string("Mocha_DeinitLibrary() failed, error ") + Mocha_GetStatusStr(status)); + } + + return; +} + +namespace utility +{ + bool mountDeviceAndConvertPath(fspath& path) + { + if (path.string().starts_with("/vol/storage_mlc01")) + { + if (!MLCMounted) + { + randomizer::utility::platform::Log("Attempting to mount MLC"); + if (const MochaUtilsStatus status = Mocha_MountFS("storage_mlc01", nullptr, "/vol/storage_mlc01"); + status != MOCHA_RESULT_SUCCESS) + { + ErrorLog::getInstance().log(std::string("Failed to mount MLC: ") + Mocha_GetStatusStr(status)); + return false; + } + + MLCMounted = true; + } + } + else if (path.string().starts_with("/vol/storage_usb01")) + { + if (!USBMounted) + { + randomizer::utility::platform::Log("Attempting to mount USB"); + if (const MochaUtilsStatus status = Mocha_MountFS("storage_usb01", nullptr, "/vol/storage_usb01"); + status != MOCHA_RESULT_SUCCESS) + { + ErrorLog::getInstance().log(std::string("Failed to mount USB: ") + Mocha_GetStatusStr(status)); + return false; + } + + USBMounted = true; + } + } + else if (path.string().starts_with("/vol/storage_odd")) + { + if (!DiscMounted) + { + randomizer::utility::platform::Log("Attempting to mount disc"); + if (const MochaUtilsStatus status = Mocha_MountFS("storage_odd03", "/dev/odd03", "/vol/storage_odd_content"); + status != MOCHA_RESULT_SUCCESS) + { + ErrorLog::getInstance().log(std::string("Failed to mount disc: ") + Mocha_GetStatusStr(status)); + return false; + } + + DiscMounted = true; + } + } + else + { + return false; + } + + // https://github.com/emiyl/dumpling/blob/9290dad8f8d91cc3ef4c4b9602898d244a2a1454/source/app/filesystem.cpp#L130 + std::string working_path = path.string().substr(5); + if (const auto& driveEnd = working_path.find_first_of('/'); driveEnd != std::string::npos) + { + // Return mount path + the path after it + working_path.replace(driveEnd, 1, ":/", 2); + } + else + { + // Return just the mount path + working_path.append(":"); + } + path = working_path; + + return true; + } +} // namespace utility +#endif + +namespace randomizer::utility::platform +{ + void Log(const std::string& str) + { + std::unique_lock lock(printMut); +#ifdef PLATFORM_DKP + LogConsoleWrite(str.c_str()); + + if (ProcIsForeground()) + { + LogConsoleDraw(); + } +#else +#ifndef LOGIC_TESTS + printf("%s\n", str.c_str()); + fflush(stdout); // vscode debug console works better with this +#endif +#endif + lock.unlock(); + } + + bool Init() + { +#ifdef PLATFORM_DKP + ProcInit(); + ConsoleScreenInit(); + + initHomeMenu(); + initEnergySaver(); + + setHomeMenuEnable(false); + setDim(false); + setAPD(false); + + if (!initMocha()) + { + ErrorLog::getInstance().log("Failed to init libmocha"); + return false; + } + mochaOpen = true; +#endif + return true; + } + + bool IsRunning() + { +#ifdef PLATFORM_DKP + return ProcIsRunning(); +#else + return true; // not sure if it's worth doing anything for this +#endif + } + + void waitForPlatformStop() + { +#ifdef PLATFORM_DKP // only need to wait on console + while (IsRunning()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(33)); // Check ~30 times a second + } +#endif + } + + void Shutdown() + { +#ifdef PLATFORM_DKP + if (mochaOpen) + { + closeMocha(); + mochaOpen = false; + } + + resetHomeMenu(); + resetEnergySaver(); + + if (IsRunning()) + { + ProcExit(); + } + waitForPlatformStop(); +#endif + } +} // namespace randomizer::utility::platform diff --git a/mods/randomizer/generator/utility/platform.hpp b/mods/randomizer/generator/utility/platform.hpp new file mode 100644 index 0000000000..a4a5e3c020 --- /dev/null +++ b/mods/randomizer/generator/utility/platform.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#ifdef DEVKITPRO + #define PLATFORM_DKP + + #include "../utility/path.hpp" +#elif defined(_MSC_VER) + #define PLATFORM_MSVC +#elif defined(__GNUC__) || defined(__GNUG__) + #define PLATFORM_GCC +#elif defined(__clang__) + #define PLATFORM_CLANG +#else + #error UNKNOWN PLATFORM +#endif + +namespace randomizer::utility::platform +{ + void Log(const std::string& str); + + bool Init(); + + bool IsRunning(); + + void waitForPlatformStop(); + + void Shutdown(); + +#ifdef DEVKITPRO + bool mountDeviceAndConvertPath(fspath& path); +#endif +} diff --git a/mods/randomizer/generator/utility/random.cpp b/mods/randomizer/generator/utility/random.cpp new file mode 100644 index 0000000000..7631196e74 --- /dev/null +++ b/mods/randomizer/generator/utility/random.cpp @@ -0,0 +1,40 @@ +#include "../utility/random.hpp" + +namespace randomizer::utility::random +{ + static bool init = false; + static std::mt19937_64 generator; + + // Initialize with seed specified + void RandomInit(size_t seed) + { + init = true; + generator = std::mt19937_64 {seed}; + } + + // Returns a random integer in range [min, max-1] + uint32_t Random(int min, int max) + { + if (!init) + { + // No seed given, get a random number from device to seed + const auto seed = static_cast(std::random_device {}()); + RandomInit(seed); + } + + auto number = generator(); + return min + (number % (max - min)); + } + + // Returns a random floating point number in [0.0, 1.0] + double RandomDouble() + { + auto number = generator(); + return (double)number / (double)generator.max(); + } + + std::mt19937_64& GetGenerator() + { + return generator; + } +} // namespace randomizer::utility::random diff --git a/mods/randomizer/generator/utility/random.hpp b/mods/randomizer/generator/utility/random.hpp new file mode 100644 index 0000000000..8a256b5b81 --- /dev/null +++ b/mods/randomizer/generator/utility/random.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace randomizer::utility::random +{ + void RandomInit(size_t seed); + uint32_t Random(int min, int max); + double RandomDouble(); + std::mt19937_64& GetGenerator(); + + /** + * @brief Will get and erase a random element out of a vector + * + * @param vector The vector to get a random element from + * @return a random element from the vector + */ + template + T PopRandomElement(std::vector& vector) + { + const auto idx = Random(0, vector.size()); + T selected = vector[idx]; + vector.erase(vector.begin() + idx); + return selected; + } + + template + auto& RandomElement(Container& container) + { + return container[Random(0, std::size(container))]; + } + template + const auto& RandomElement(const Container& container) + { + return container[Random(0, std::size(container))]; + } + + // Shuffle items within a vector or array + template + void ShufflePool(std::vector& vector) + { + for (std::size_t i = 0; i + 1 < vector.size(); i++) + { + std::swap(vector[i], vector[Random(i, vector.size())]); + } + } + template + void ShufflePool(std::array& arr) + { + for (std::size_t i = 0; i + 1 < arr.size(); i++) + { + std::swap(arr[i], arr[Random(i, arr.size())]); + } + } +} // namespace randomizer::utility::random diff --git a/mods/randomizer/generator/utility/string.cpp b/mods/randomizer/generator/utility/string.cpp new file mode 100644 index 0000000000..76bc5717a7 --- /dev/null +++ b/mods/randomizer/generator/utility/string.cpp @@ -0,0 +1,92 @@ +#include "../utility/string.hpp" + +#include +#include +#include + +namespace randomizer::utility::str { + //can't use codecvt on Wii U, deprecated in c++17 and g++ hates it + //Borrowed from https://docs.microsoft.com/en-us/cpp/standard-library/codecvt-class?view=msvc-170#out + std::string toUTF8(const std::u16string& str) { + if(str.empty()) return ""; + + std::string ret; + ret.resize(str.size()); + char* pszNext; + const char16_t* pwszNext; + std::mbstate_t state = {0}; // zero-initialization represents the initial conversion state for mbstate_t + std::locale loc("C"); + int res = std::use_facet>(loc).out(state, str.c_str(), &str[str.size()], pwszNext, + &ret[0], &ret[ret.size()], pszNext); + + if(res == std::codecvt_base::error) return ""; + return ret; + } + + std::u16string toUTF16(const std::string& str) + { + if(str.empty()) return u""; + + std::u16string ret; + ret.resize(str.size()); + const char* pszNext; + char16_t* pwszNext; + std::mbstate_t state = {0}; // zero-initialization represents the initial conversion state for mbstate_t + std::locale loc("C"); + int res = std::use_facet>(loc).in(state, str.c_str(), &str[str.size()], pszNext, + &ret[0], &ret[ret.size()], pwszNext); + + if(res == std::codecvt_base::error) return u""; + + // Remove extra null terminators that may have been created from multi-byte + // UTF-8 characters + while(ret.size() > 0 && ret[ret.size() - 1] == u'\0') + { + ret.pop_back(); + } + + return ret; + } + + // Takes in a string and returns an optional integer if the string + // could be turned into one + std::optional toInt(std::string_view str) { + // Trim leading/trailing whitespace + auto start = str.find_first_not_of(" \t\r\n"); + if (start == std::string_view::npos) { + return std::nullopt; + } + str.remove_prefix(start); + + auto end = str.find_last_not_of(" \t\r\n"); + if (end != std::string_view::npos) { + str = str.substr(0, end + 1); + } + + if (str.empty()) { + return std::nullopt; + } + + // Identify base (only decimal/hexadecimal handled) + int base = 10; + if (str.size() > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + base = 16; + str.remove_prefix(2); + } + + if (str.empty()) { + return std::nullopt; + } + + // Parse the remaining absolute string + int value = 0; + auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value, base); + + // Ensure conversion succeeded and consumed the entire trimmed string + if (ec == std::errc{} && ptr == str.data() + str.size()) { + return value; + } + + return std::nullopt; + } +} diff --git a/mods/randomizer/generator/utility/string.hpp b/mods/randomizer/generator/utility/string.hpp new file mode 100644 index 0000000000..5c1d1944d0 --- /dev/null +++ b/mods/randomizer/generator/utility/string.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace randomizer::utility::str { + std::string toUTF8(const std::u16string& str); + + std::u16string toUTF16(const std::string& str); + + template + concept StringType = std::derived_from>; + + template requires StringType + std::vector Split(const T& string, const typename T::value_type delim) { + std::vector ret; + T tail = string; + auto index = tail.find_first_of(delim); + + while (index != T::npos) { + ret.push_back(tail.substr(0, index)); + tail = tail.substr(index + 1); + index = tail.find_first_of(delim); + } + ret.push_back(tail); //add anything after last line break + + return ret; + } + + template requires StringType + T Merge(const std::vector& lines, const typename T::value_type separator) { + T ret; + for (const T& segment : lines) { + ret += segment + separator; + } + + return ret; + } + + template requires StringType + T assureNullTermination(const T& string) { + if(!string.empty() && string.back() == typename T::value_type(0)) return string; + + return string + typename T::value_type(0); + } + + template requires std::integral + std::string intToHex(const T& i, const bool& base = true) + { + std::stringstream stream; + stream << std::hex << (base ? std::showbase : std::noshowbase) << i; + return stream.str(); + } + + template requires std::integral + std::string intToHex(const T& i, const std::streamsize& width, const bool& base = true) + { + std::stringstream stream; + stream << std::hex << (base ? std::showbase : std::noshowbase) << std::setfill('0') << std::setw(width) << i; + return stream.str(); + } + + /** + * @brief Checks to see if any of the passed in substrings are within a string + * + * @param str The string to check for substrings + * @param substrs Paramater Pack of strings to test against the first argument + * + * @return true if any of the passed in substrings are found within the string, false otherwise + */ + template + bool Contains(const std::string& str, Types... substrs) + { + for (const auto& substr : {substrs...}) + { + if (str.find(substr) != std::string::npos) + { + return true; + } + } + return false; + } + + //wrapper for a constexpr string, for use in other templates + template + struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { + std::copy_n(str, N, value); + } + + constexpr operator std::string_view() const { + return std::string_view(value, N - 1); //leave out null terminator + } + + char value[N]; + }; + + template + void Erase(std::string& s, Types... tokens) + { + for (const auto& token : {tokens...}) + { + while (randomizer::utility::str::Contains(s, token)) + { + s.erase(s.find(token), strlen(token)); + } + } + } + + std::optional toInt(std::string_view str); +} diff --git a/mods/randomizer/generator/utility/text.cpp b/mods/randomizer/generator/utility/text.cpp new file mode 100644 index 0000000000..70f830697a --- /dev/null +++ b/mods/randomizer/generator/utility/text.cpp @@ -0,0 +1,437 @@ +#include "text.hpp" + +#include "yaml.hpp" + +#include + +#include + +#include "JSystem/JUtility/JUTFont.h" +#include "m_Do/m_Do_ext.h" + +namespace randomizer { + + Text::Text(const std::string& str) { + for (auto& text : mText) { + text = str; + } + } + + void Text::Replace(const std::string& oldStr, const Text& replacementText, int count/* = 1*/) { + for (size_t i = 0; i < mText.size(); ++i) { + auto& curString = mText[i]; + for (int i = 0; i < count; ++i) { + if (auto startPos = curString.find(oldStr); startPos != std::string::npos) { + curString.replace(startPos, oldStr.length(), replacementText.mText[i]); + } + } + } + } + + void Text::Replace(const std::string& oldStr, const std::string& replacementText, int count/* = 1*/) { + for (size_t i = 0; i < mText.size(); ++i) { + auto& curString = mText[i]; + for (int i = 0; i < count; ++i) { + if (auto startPos = curString.find(oldStr); startPos != std::string::npos) { + curString.replace(startPos, oldStr.length(), replacementText); + } + } + } + } + + void Text::Capitalize() { + try { + // Determine the platform-specific locale string +#if defined(_WIN32) || defined(_WIN64) + const char* localeName = "English_United States.1252"; +#else + const char* localeName = "en_US.iso88591"; +#endif + + static const std::locale latin1Locale(localeName); + + for (auto& text : mText) { + if (!text.empty()) { + text[0] = std::toupper(text[0], latin1Locale); + } + } + } catch (const std::runtime_error&) { + // Fallback incase the system completely lacks the requested locale definition + for (auto& text : mText) { + if (!text.empty()) { + text[0] = static_cast(std::toupper(static_cast(text[0]))); + } + } + } + } + + void Text::BreakLines(int maxLineWidth /*= MAX_LINE_WIDTH_ITEM_TEXTBOX*/) { + for (auto& text : mText) { + breakLines(text, maxLineWidth); + } + } + + bool Text::Empty() const { + for (auto& text : mText) { + if (!text.empty()) { + return false; + } + } + return true; + } + + Text& Text::operator+=(const Text& rhs) { + for (size_t i = 0; i < mText.size(); ++i) { + mText[i] += rhs.mText[i]; + } + return *this; + } + + Text& Text::operator+=(const std::string& rhs) { + for (auto& text : mText) { + text += rhs; + } + return *this; + } + + Text operator+(Text lhs, const Text& rhs) { + lhs += rhs; + return lhs; + } + + Text operator+(Text lhs, const std::string& rhs) { + for (auto& text : lhs.mText) { + text += rhs; + } + return lhs; + } + + Text operator+(const std::string& lhs, const Text& rhs) { + return Text(lhs) + rhs; + } + + Text::Type stringToType(const std::string& str) { + std::unordered_map strToType = { + {"Standard", Text::Type::STANDARD}, + {"Pretty", Text::Type::PRETTY}, + {"Cryptic", Text::Type::CRYPTIC}, + }; + + if (strToType.contains(str)) + { + return strToType.at(str); + } + + throw std::runtime_error("Text type \"" + str + "\" is not recognized."); + } + + Text::Language stringToLanguage(const std::string& str) { + std::unordered_map strToLanguage = { + {"english", Text::ENGLISH}, + {"spanish", Text::SPANISH}, + {"french", Text::FRENCH}, + {"german", Text::GERMAN}, + {"italian", Text::ITALIAN}, + {"japanese", Text::JAPANESE} + }; + + if (strToLanguage.contains(str)) + { + return strToLanguage.at(str); + } + + throw std::runtime_error("Language \"" + str + "\" is not recognized."); + } + + + std::string languageToString(Text::Language language) { + switch (language) { + case Text::ENGLISH: + return "english"; + case Text::SPANISH: + return "spanish"; + case Text::FRENCH: + return "french"; + case Text::GERMAN: + return "german"; + case Text::ITALIAN: + return "italian"; + case Text::JAPANESE: + return "japanese"; + default: + return "unknown language enum"; + } + } + + Text::Gender stringToGender(const std::string& str) + { + std::unordered_map strToGender = { + {"Masculine", Text::Gender::MASCULINE}, + {"Feminine", Text::Gender::FEMININE} + }; + + if (strToGender.contains(str)) + { + return strToGender.at(str); + } + + return Text::Gender::NEUTRAL; + } + + Text::Plurality stringToPlurality(const std::string& str) + { + if (str == "Plural") return Text::Plurality::PLURAL; + return Text::Plurality::SINGULAR; + } + + std::string UTF8ToLatin1(const std::string& utf8Str) { + std::string latin1Str; + // The output string will be equal to or shorter than the UTF-8 string + latin1Str.reserve(utf8Str.length()); + + size_t read_pos = 0; + size_t len = utf8Str.length(); + + while (read_pos < len) { + unsigned char c = utf8Str[read_pos]; + + if (c < 0x80) { + // Standard ASCII (0x00 - 0x7F) + latin1Str.push_back(c); + ++read_pos; + } + else if ((c & 0xE0) == 0xC0 && (read_pos + 1 < len)) { + // Two-byte UTF-8 sequence (0xC0 - 0xDF) + unsigned char next_byte = utf8Str[read_pos + 1]; + + // Reconstruct the Latin-1 character value + unsigned char latin1_char = ((c & 0x1F) << 6) | (next_byte & 0x3F); + + latin1Str.push_back(latin1_char); + read_pos += 2; + } + else { + // Multi-byte sequences out of Latin-1 range (or malformed bytes) + throw std::runtime_error(fmt::format("Invalid bytes when converting to Latin1 with \"{}\"", utf8Str)); + } + } + + return latin1Str; + } + + static void LoadTextData(TextDatabase& tb) { + struct LanguageEntry { + std::string language; + std::string languageData; + }; + auto files = std::to_array({ + {"english", GET_EMBED_DATA(RANDO_DATA_PATH "text/languages/english.yaml")}, + {"spanish", GET_EMBED_DATA(RANDO_DATA_PATH "text/languages/spanish.yaml")}, + {"french", GET_EMBED_DATA(RANDO_DATA_PATH "text/languages/french.yaml")}, + {"german", GET_EMBED_DATA(RANDO_DATA_PATH "text/languages/german.yaml")}, + {"italian", GET_EMBED_DATA(RANDO_DATA_PATH "text/languages/italian.yaml")}, + }); + + for (const auto& file : files) { + auto language = stringToLanguage(file.language); + auto textData = LOAD_EMBED_DATA(file.languageData); + for (const auto& textNode : textData) { + const auto& name = textNode.first.as(); + for (const auto& typeNode : textNode.second) { + auto type = stringToType(typeNode.first.as()); + auto typeData = typeNode.second; + const auto& text = typeData["Text"].as(); + if (language != Text::JAPANESE) { + tb[name][type].mText[language] = UTF8ToLatin1(text); + } else { + // Probably have to handle Japanese another way at some point + tb[name][type].mText[language] = text; + } + if (typeData["Gender"]) { + tb[name][type].mGender[language] = stringToGender(typeData["Gender"].as()); + } + if (typeData["Plurality"]) { + tb[name][type].mPlurality[language] = stringToPlurality(typeData["Plurality"].as()); + } + } + } + } + } + + const TextDatabase& getTextDatabase() { + static TextDatabase tb{}; + + // If database is empty, load it up + if (tb.empty()) { + LoadTextData(tb); + } + + return tb; + } + + bool textObjectExists(const std::string& name) { + return getTextDatabase().contains(name); + } + + const Text& getTextObject(const std::string& name, Text::Type type /*= Text::STANDARD*/) + { + const auto& tb = getTextDatabase(); + if (!tb.contains(name)) { + throw std::runtime_error("Text name \"" + name + "\" is not recognized."); + } + return tb.at(name).at(type); + } + + const std::string& getTextStr(const std::string& name, + Text::Type type /*= Text::STANDARD*/, + Text::Language language /*= Text::ENGLISH*/) + { + const auto& tb = getTextDatabase(); + if (!tb.contains(name)) { + throw std::runtime_error("Text name \"" + name + "\" is not recognized."); + } + + if (!tb.at(name).at(type).mText.at(language).empty()) { + return tb.at(name).at(type).mText.at(language); + } + + // Return english if the other language's string is empty + return tb.at(name).at(type).mText.at(language); + } + + Text addColor(const Text& t, Text::Color color, int count /* = 1*/, bool forceAround /* = false*/) { + const static std::unordered_map colorStrings = { + {Text::WHITE, ""}, + {Text::RED, ""}, + {Text::GREEN, ""}, + {Text::LIGHT_BLUE, ""}, + {Text::YELLOW, ""}, + {Text::PURPLE, ""}, + {Text::ORANGE, ""}, + {Text::DARK_GREEN, ""}, + {Text::BLUE, ""}, + {Text::SILVER, ""}, + }; + + if (color == Text::Color::RAW) { + return t; + } + + if (!colorStrings.contains(color)) { + throw std::runtime_error("Color enum value \"" + std::to_string(color) + "\" is not recognized."); + } + + Text text = t; + if (forceAround) { + text = colorStrings.at(color) + text + colorStrings.at(Text::WHITE); + } + text.Replace("{", colorStrings.at(color), count); + text.Replace("}", colorStrings.at(Text::WHITE), count); + return text; + } + + using namespace std::string_view_literals; + static const std::unordered_map messageCodes = { + {"", "\x1A\x05\x00\x00\x00"sv}, + {"", "\x1A\x05\x00\x00\x01"sv}, + {"", "\x1A\x05\x00\x00\x02"sv}, + {"", "\x1A\x05\x00\x00\x20"sv}, + {"", "\x1A\x05\x06\x00\x02"sv}, + {"", "\x1A\x05\x06\x00\x03"sv}, + {"<2 way choice 1>", "\x1A\x06\x00\x00\x08\x01"sv}, + {"<2 way choice 2>", "\x1A\x06\x00\x00\x08\x02"sv}, + {"<3 way choice 1>", "\x1A\x06\x00\x00\x09\x01"sv}, + {"<3 way choice 2>", "\x1A\x06\x00\x00\x09\x02"sv}, + {"<3 way choice 3>", "\x1A\x06\x00\x00\x09\x03"sv}, + {"", "\x1A\x06\xFF\x00\x00\x00"sv}, + {"", "\x1A\x06\xFF\x00\x00\x01"sv}, + {"", "\x1A\x06\xFF\x00\x00\x02"sv}, + {"", "\x1A\x06\xFF\x00\x00\x03"sv}, + {"", "\x1A\x06\xFF\x00\x00\x04"sv}, + {"", "\x1A\x06\xFF\x00\x00\x06"sv}, + {"", "\x1A\x06\xFF\x00\x00\x08"sv}, + // custom colors + {"", "\x1A\x06\xFF\x00\x00\x09"sv}, + {"", "\x1A\x06\xFF\x00\x00\x0A"sv}, + {"", "\x1A\x06\xFF\x00\x00\x0B"sv}, + }; + + void breakLines(std::string& str, int maxLineWidth) { + + // Randomizer Only shouldn't rely on needing access to the iso +#ifndef RANDOMIZER_ONLY + // Get game's font + auto gameFont = mDoExt_getMesgFont(); +#endif + int curLineWidth = 0; + size_t i = 0; + size_t previousSpace = 0; + while (i < str.length()) { + + // Skip over control codes since they don't get displayed + std::string code{}; + for (const auto& [messageCode, replacement] : messageCodes) { + if (str.substr(i, messageCode.length()) == messageCode) { + code = messageCode; + break; + } + } + + if (!code.empty()) { + // Assume worst case for player name width. + // 8 chars max * max char width + if (code == "") { + curLineWidth += 8 * 21; + } + i += code.length(); + continue; + } + + // Keep track of the previous space to replace with + // a line break when we reach the maximum width + if (str[i] == ' ') { + previousSpace = i; + } + // If we encounter an already inserted newline, reset the counter + else if (str[i] == '\n') { + curLineWidth = 0; + ++i; + continue; + } + + JUTFont::TWidth width{}; +#ifndef RANDOMIZER_ONLY + gameFont->getWidthEntry(str[i], &width); +#else + // Assume worst case with no iso access + width.field_0x1 = 21; +#endif + curLineWidth += /*width.field_0x0 + */width.field_0x1; + // If we exceed the maximum line width, replace the + // previous space with a newline and start counting + // from the newline again + if (curLineWidth > maxLineWidth) { + str[previousSpace] = '\n'; + i = previousSpace; + curLineWidth = 0; + } + + ++i; + } + +#ifndef RANDOMIZER_ONLY + // Free game's font + mDoExt_removeMesgFont(); +#endif + } + + void applyMessageCodes(std::string& str) { + for (const auto& [code, replacement] : messageCodes) { + size_t pos = 0; + while ((pos = str.find(code, pos)) != std::string::npos) { + str.replace(pos, code.length(), replacement); + pos += replacement.length(); + } + } + } +}; // namespace Text diff --git a/mods/randomizer/generator/utility/text.hpp b/mods/randomizer/generator/utility/text.hpp new file mode 100644 index 0000000000..f442575b0b --- /dev/null +++ b/mods/randomizer/generator/utility/text.hpp @@ -0,0 +1,123 @@ +#pragma once + +#include +#include +#include + +namespace randomizer { + class Text { + public: + enum Language { + // First 5 match ordering of dSv_config_language in d_save.h + ENGLISH, + GERMAN, + FRENCH, + SPANISH, + ITALIAN, + // End of ordering for dSv_config_language + JAPANESE, // Not supported yet + LANGUAGE_MAX + }; + + enum Type + { + STANDARD = 0, + PRETTY, + CRYPTIC, + TYPE_MAX + }; + + enum Color + { + RAW = 0, + WHITE, + RED, + GREEN, + LIGHT_BLUE, + YELLOW, + PURPLE, + ORANGE, + DARK_GREEN, + BLUE, + SILVER, + }; + + enum Gender + { + NEUTRAL = 0, + MASCULINE, + FEMININE, + GENDER_MAX, + }; + + enum Plurality + { + SINGULAR = 0, + PLURAL, + PLURALITY_MAX, + }; + + static constexpr size_t MAX_LINE_WIDTH_ITEM_TEXTBOX = 441; + static constexpr size_t MAX_LINE_WIDTH_NORMAL_TEXTBOX = 750; + + Text() = default; + explicit Text(const std::string& str); + + std::array mText{}; + std::array mGender{}; + std::array mPlurality{}; + + /** + * + * @param oldStr the string to replace + * @param replacementText the Text object to replace the old string + * @param count the number of occurrences to replace + */ + void Replace(const std::string& oldStr, const Text& replacementText, int count = 1); + void Replace(const std::string& oldStr, const std::string& replacementText, int count = 1); + void BreakLines(int maxLineWidth = MAX_LINE_WIDTH_NORMAL_TEXTBOX); + void Capitalize(); + bool Empty() const; + Text& operator+=(const Text& rhs); + Text& operator+=(const std::string& rhs); + friend Text operator+(Text lhs, Text& rhs); + friend Text operator+(Text lhs, const std::string& rhs); + friend Text operator+(const std::string& lhs, const Text& rhs); + }; + + inline constexpr std::array supportedLanguages = { + Text::ENGLISH, + Text::SPANISH, + Text::FRENCH, + Text::GERMAN, + Text::ITALIAN + }; + + // std::u16string apply_name_color(std::u16string str, const Color& color); + // std::u16string word_wrap_string(const std::u16string& string, const size_t& max_line_len); //IMPROVEMENT: use font data to do this "properly" + // std::string pad_str_4_lines(const std::string& string); + // std::u16string pad_str_4_lines(const std::u16string& string); + + Text::Language stringToLanguage(const std::string& str); + std::string languageToString(Text::Language language); + Text::Gender stringToGender(const std::string& str); + Text::Plurality stringToPlurality(const std::string& str); + + // Retrieval of Text objects keyed by name and type (standard, pretty, cryptic) + using TextDatabase = std::unordered_map>; + + const TextDatabase& getTextDatabase(); + + bool textObjectExists(const std::string& name); + const Text& getTextObject(const std::string& name, Text::Type type = Text::STANDARD); + const std::string& getTextStr(const std::string& name, Text::Type type = Text::STANDARD, Text::Language language = Text::ENGLISH); + + + Text addColor(const Text& text, Text::Color color, int count = 1, bool forceAround = false); + + // Adds newlines in appropriate places to properly break the text string for textboxes + void breakLines(std::string& str, int maxLineWidth); + + // Replaces the message codes in the string with the ingame hex equivalents + void applyMessageCodes(std::string&); +}; // namespace Text diff --git a/mods/randomizer/generator/utility/thread_local.hpp b/mods/randomizer/generator/utility/thread_local.hpp new file mode 100644 index 0000000000..5a5e3814ec --- /dev/null +++ b/mods/randomizer/generator/utility/thread_local.hpp @@ -0,0 +1,41 @@ +#pragma once + +#ifdef DEVKITPRO +#include +#include +#include +#endif + +enum struct DataIDs : uint32_t { +#ifdef DEVKITPRO + FILE_OP_BUFFER = OS_THREAD_SPECIFIC_0 +#else + FILE_OP_BUFFER = 0 +#endif +}; + +template +class ThreadLocal { +private: +#ifdef DEVKITPRO //TODO: somehow unregister data on all threads during destruct? + std::list data; +#else + inline static thread_local T data; +#endif + +public: + T& get() { + #ifdef DEVKITPRO + const OSThreadSpecificID& id = static_cast(ID); + if(OSGetThreadSpecific(id) == nullptr) { + data.emplace_back(); + OSSetThreadSpecific(id, &data.back()); + } + return *reinterpret_cast(OSGetThreadSpecific(id)); + #else + return data; + #endif + } + + ThreadLocal() = default; +}; diff --git a/mods/randomizer/generator/utility/time.cpp b/mods/randomizer/generator/utility/time.cpp new file mode 100644 index 0000000000..d172827ef0 --- /dev/null +++ b/mods/randomizer/generator/utility/time.cpp @@ -0,0 +1,79 @@ +#include "../utility/time.hpp" + +using namespace std::chrono; +using namespace std::literals::chrono_literals; + +namespace randomizer::utility::time +{ + ProgramTime::ProgramTime(): openTime(Clock_t::now()) {} + + ProgramTime& ProgramTime::getInstance() + { + static ProgramTime s_Instance; + return s_Instance; + } + + ProgramTime::TimePoint_t ProgramTime::getOpenedTime() + { + return getInstance().openTime; + } + + ProgramTime::Duration_t ProgramTime::getElapsedTime() + { + return Clock_t::now() - getOpenedTime(); + } +} // namespace randomizer::utility::time +#if __has_include() && !defined(__APPLE__) +#include +namespace randomizer::utility::time +{ + std::string ProgramTime::getDateStr() + { + return std::format("{0:%a, %b %d, %Y, %I:%M:%S %p%n}", round(getOpenedTime())); + } + + std::string ProgramTime::getTimeStr() + { + return std::format("{:%T}", round(getElapsedTime())); + } +} // namespace randomizer::utility::time +#else +#include +#include +namespace randomizer::utility::time +{ + std::string ProgramTime::getDateStr() + { + const time_t point = Clock_t::to_time_t(ProgramTime::getOpenedTime()); + + static std::mutex localtimeMut; // std::ctime is not thread safe + std::unique_lock lock(localtimeMut); + return std::ctime(&point); // time string ends with \n + } + + std::string ProgramTime::getTimeStr() + { + Duration_t duration = getElapsedTime(); + std::stringstream ret; + ret << std::setfill('0'); + + const hours hr = duration_cast(duration); + ret << std::setw(2) << hr.count() << ":"; + duration -= hr; + const minutes min = duration_cast(duration); + ret << std::setw(2) << min.count() << ":"; + duration -= min; + const seconds sec = duration_cast(duration); + ret << std::setw(2) << sec.count() << "."; + duration -= sec; + const milliseconds ms = duration_cast(duration); + ret << std::setw(3) << ms.count(); + + return ret.str(); + } +} // namespace randomizer::utility::time +#endif +namespace randomizer::utility::time +{ + static const ProgramTime& temp = ProgramTime::getInstance(); // inaccessible global to create instance when program starts +}; // namespace randomizer::utility::time diff --git a/mods/randomizer/generator/utility/time.hpp b/mods/randomizer/generator/utility/time.hpp new file mode 100644 index 0000000000..464fbad576 --- /dev/null +++ b/mods/randomizer/generator/utility/time.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include "../utility/log.hpp" +#include "../utility/string.hpp" +#include "../utility/platform.hpp" + +namespace randomizer::utility::time +{ + template + concept DurationType = std::same_as>; + + template + requires DurationType + class Timer + { + public: + typename Clock::duration getElapsed() const + { + end = Clock::now(); + return end - begin; + } + + protected: + typename Clock::time_point begin; + typename Clock::time_point end; + + void start() { begin = Clock::now(); } + + void stop() + { + end = Clock::now(); + duration = end - begin; + } + + void print() const + { + std::stringstream message; + message << stem << (stem.back() == ' ' ? "" : " ") << std::chrono::duration_cast(duration); + + randomizer::utility::platform::Log(message.str()); + LOG_TO_DEBUG(message.str() + '\n'); + } + + private: + typename Clock::duration duration; + + static constexpr std::string_view stem = Message; + }; + + template + requires DurationType + class ScopedTimer: public Timer + { + public: + ScopedTimer() { Timer::start(); } + + ~ScopedTimer() + { + Timer::stop(); + Timer::print(); + } + }; + + class ProgramTime + { + private: + using Clock_t = std::chrono::system_clock; + using TimePoint_t = Clock_t::time_point; + using Duration_t = Clock_t::duration; + + const TimePoint_t openTime; + static TimePoint_t getOpenedTime(); + static Duration_t getElapsedTime(); + + ProgramTime(); + ~ProgramTime() = default; + + public: + ProgramTime(const ProgramTime&) = delete; + ProgramTime& operator=(const ProgramTime&) = delete; + + static ProgramTime& getInstance(); + static std::string getTimeStr(); + static std::string getDateStr(); + }; + +} // namespace randomizer::utility::time diff --git a/mods/randomizer/generator/utility/yaml.hpp b/mods/randomizer/generator/utility/yaml.hpp new file mode 100644 index 0000000000..aa7c01a248 --- /dev/null +++ b/mods/randomizer/generator/utility/yaml.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "yaml-cpp/yaml.h" + +#include "../utility/file.hpp" +#include "../utility/path.hpp" +#include "battery/embed.hpp" + +#define GET_EMBED_DATA(path) b::embed().str() +#define LOAD_EMBED_DATA(data) YAML::Load(data) + +#define LOAD_EMBED_YAML(path) LOAD_EMBED_DATA(GET_EMBED_DATA(path)) + +// this wrapper is here to avoid path encoding issues +// removes any possible path -> string oddities or the need to open the file manually +inline YAML::Node LoadYAML(const fspath& path, const bool& resourceFile = false) { + std::string file; + if (randomizer::utility::file::GetContents(path, file, resourceFile) != 0) { + throw YAML::BadFile( + path.string()); // exception is bad (unhandled) but it matches the old behavior + } + + return YAML::Load(file); +} + +template +void YAMLVerifyFields(const YAML::Node& node, Fields... requiredFields) { + for (const auto& field : {requiredFields...}) { + if (!node[field]) { + throw std::runtime_error(std::string("Field \"") + field + + "\" is missing from node:\n" + YAML::Dump(node)); + } + } +} diff --git a/mods/randomizer/mod.json b/mods/randomizer/mod.json new file mode 100644 index 0000000000..5fea5ed641 --- /dev/null +++ b/mods/randomizer/mod.json @@ -0,0 +1,7 @@ +{ + "id": "dev.twilitrealm.randomizer", + "name": "Randomizer", + "version": "1.0.0", + "author": "Twilit Realm", + "description": "Dusklight Randomizer" +} diff --git a/mods/randomizer/res/.gitkeep b/mods/randomizer/res/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mods/randomizer/res/shadow_crystal.bti b/mods/randomizer/res/shadow_crystal.bti new file mode 100644 index 0000000000..9d43aeb438 Binary files /dev/null and b/mods/randomizer/res/shadow_crystal.bti differ diff --git a/mods/randomizer/src/custom_flow_ids.hpp b/mods/randomizer/src/custom_flow_ids.hpp new file mode 100644 index 0000000000..faa355d899 --- /dev/null +++ b/mods/randomizer/src/custom_flow_ids.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "dolphin/types.h" + +inline constexpr u16 BASE_CUSTOM_MSG_AND_FLOW_ID = 21000; +inline constexpr u16 CUSTOM_BMG_GROUP = 9; diff --git a/mods/randomizer/src/flags.cpp b/mods/randomizer/src/flags.cpp new file mode 100644 index 0000000000..91f60d658b --- /dev/null +++ b/mods/randomizer/src/flags.cpp @@ -0,0 +1,25 @@ +#include "flags.h" +#include "stages.h" +#include "tools.h" + +goldenWolfFlags getCurrentGoldenWolfFlags(u8 roomNo) { + switch (getStageID()) { + case Ordon_Spring: + return {0x41, HOWLED_AT_DEATH_MOUNTAIN_STONE, GOT_SKILL_FROM_ORDON_WOLF}; + case Faron_Woods: + return {0xFF, 0xFFFF, 0x3C10}; // Custom flag for rando + case Kakariko_Graveyard: + return {0x79, HOWLED_AT_SNOWPEAK_STONE, GOT_SKILL_FROM_GRAVEYARD_WOLF}; + case Outside_Castle_Town: + if (roomNo == 8) { + return {0x29, HOWLED_AT_UPPER_ZORAS_RIVER_STONE, GOT_SKILL_FROM_WEST_CT_WOLF}; + } + return {0x2A, HOWLED_AT_SACRED_GROVE_OUTSIDE_STONE, GOT_SKILL_FROM_SOUTH_CT_FIELD_WOLF}; + case Castle_Town: + return {0x32, HOWLED_AT_HIDDEN_VILLAGE_STONE, GOT_SKILL_FROM_BARRIER_WOLF}; + case Gerudo_Desert: + return {0x32, HOWLED_AT_LAKE_HYLIA_STONE, GOT_SKILL_FROM_BULBLIN_CAMP_WOLF}; + default: + return {0xFF, 0xFFFF, 0xFFFF}; + } +} \ No newline at end of file diff --git a/mods/randomizer/src/flags.h b/mods/randomizer/src/flags.h new file mode 100644 index 0000000000..89911a4dae --- /dev/null +++ b/mods/randomizer/src/flags.h @@ -0,0 +1,628 @@ +#pragma once + +#include + +enum EventFlags +{ + CHEESE_PUT_IN_SOUP = 0x0001, + PUMPKIN_PUT_IN_SOUP = 0x0002, + LOST_TO_GOR_CORON_IN_SUMO = 0x0004, + TALKED_TO_RENADO_AFTER_GORON_MINES = 0x0008, + YETO_TOOK_CHEESE = 0x0010, + YETO_TOOK_PUMPKIN = 0x0020, + MIDNA_TEXT_ABOUT_FINDING_GORGE_BRIDGE = 0x0080, + TALKED_TO_COLIN_OUTSIDE_LINKS_HOUSE = 0x104, + TALKED_TO_FADO_BEFORE_GOATS_1 = 0x110, + TOLD_YETA_ABOUT_CHEESE = 0x120, + TALKED_TO_YETO_IN_SPR_FOR_FIRST_TIME = 0x140, + CAN_FIGHT_TWILIGHT_BLOAT = 0x180, + TALKED_TO_SERA_BEFORE_CAT_RESCUED = 0x201, + TALKED_TO_COLIN_THROUGH_SPRING_GATE = 0x202, + FINISHED_SLINGSHOT_TRAINING = 0x220, + FINISHED_SWORD_TRAINING = 0x240, + FAILED_TO_CATCH_GOAT_AT_BO_HOUSE = 0x280, + GOT_FISHING_ROD_FROM_ULI = 0x301, + GAVE_WOODEN_SWORD_TO_TALO = 0x302, + BROUGHT_CRADLE_TO_ULI = 0x304, + TALKED_TO_KIDS_OUTSIDE_LINKS_HOUSE = 0x308, + JAGGLE_ASKED_TO_LOOK_UP_PILLAR = 0x310, + TALKED_TO_PERGIE = 0x340, + TALKED_TO_SQUIRREL_AFTER_FARON_TWILIGHT = 0x380, + TALKED_TO_ULI = 0x404, + ULI_RAN_DOWN_RIVER = 0x408, + TALKED_TO_ULI_BEFORE_GETTING_CRADLE = 0x410, + UNK_0420 = 0x420, + REFUSED_SWORD_TRAINING = 0x440, + TOLD_YETA_ABOUT_PUMPKIN = 0x480, + MIDNA_CHARGE_UNLOCKED = 0x501, + FINISHED_SEWERS = 0x502, + MIDNA_TEXT_AFTER_ENTERING_FARON_TWILIGHT = 0x504, + MET_ZELDA_IN_SEWERS = 0x508, + MIDNA_CUT_PRISON_CHAIN = 0x510, + WATCHED_SEWERS_INTRO_CUTSCENE = 0x520, + ESCAPED_CELL_IN_SEWERS = 0x540, + ENTERED_ORDON_SPRING_DAY_3 = 0x580, + EPONA_TAMED = 0x601, + FOREST_TEMPLE_CLEARED = 0x602, + MAP_WARPING_UNLOCKED = 0x604, + KING_BULBLIN_1_TRIGGER_ACTIVATED = 0x608, + CLEARED_FARON_TWILIGHT = 0x610, + WARPED_KAKARIKO_GORGE_BRIDGE_BACK = 0x620, + WATCHED_FARON_TWILIGHT_INTRO_CUTSCENE = 0x640, + WATCHED_FARONE_CUTSCENE_AFTER_OPENING_ORDON_SPRING_WARP = 0x680, + GORON_MINES_CLEARED = 0x701, + FIRST_TIME_TALKING_TO_GOR_CORON = 0x702, + WON_SUMO_AGAINST_GOR_CORON = 0x704, + CLEARED_ELDIN_TWILIGHT = 0x708, + WATCHED_ELDIN_TWILIGHT_SANCTUARY_CUTSCENE = 0x710, + TALKED_TO_BO_AFTER_TAMING_EPONA = 0x720, + STARTED_SUMO_AGAINST_GOR_CORON = 0x740, + WATCHED_COLIN_CUTSCENE_AFTER_KING_BULBLIN_1 = 0x780, + TALKED_WITH_TALO_TO_START_ARCHERY_MINIGAME = 0x801, + GOT_ZORA_ARMOR_FROM_RUTELA = 0x804, + ZORA_ESCORT_CLEARED = 0x810, + ENTERED_TELMAS_BAR_AFTER_LANAYRU_TWILIGHT = 0x820, + WAGON_ESCORT_STARTED = 0x840, + WARPED_METEOR_TO_ZORAS_DOMAIN = 0x880, + LISTENED_TO_IZA_SPIRIT_AFTER_KILLING_BUG_NEXT_TO_HER = 0x901, + STARTED_IZA_1_MINIGAME = 0x902, + LAKEBED_TEMPLE_CLEARED = 0x904, + BOUGHT_BARNES_BOMB_BAG = 0x908, + WON_ARCHERY_MINIGAME_IN_KAKARIKO_WITH_HAWKEYE = 0x910, + WON_ARCHERY_MINIGAME_IN_KAKARIKO_LEGITIMATELY = 0x920, + STARTED_ARCHERY_MINIGAME_IN_KAKARIKO = 0x940, + TALKED_TO_TALO_AFTER_WINNING_ARCHERY_MINIGAME_LEGITIMATELY = 0x980, + KING_BULBLIN_1_HIT_ONCE_DURING_PHASE_2 = 0xA01, + KING_BULBLIN_1_PHASE_1_DONE = 0xA02, + KING_BULBLIN_1_FIGHT_STARTED = 0xA04, + KING_BULBLIN_1_DEFEATED = 0xA08, + STARTED_KARGOROK_FLIGHT_UP_ZORAS_RIVER_DURING_TWILIGHT = 0xA10, + BRIDGE_OF_ELDIN_STOLEN = 0xA20, + THREW_FIRST_GORON_OFF_LEDGE_WHILE_SCALING_DMT = 0xA40, + LEFT_AFTER_AGREEING_TO_HELP_IZA_1 = 0xA80, + IZA_1_MINIGAME_DONE = 0xB01, + IZA_1_MINIGAME_UNLOCKED = 0xB02, + AGREED_TO_HELP_IZA = 0xB04, + LEFT_AFTER_AGREEING_TO_HELP_IZA = 0xB08, + GOT_SNOWPEAK_RUINS_MAP_FROM_YETA = 0xB10, + TALKED_TO_YETA_IN_SNOWPEAK_RUINS_FOR_THE_FIRST_TIME = 0xB20, + ESCAPED_BURNING_TENT_IN_BULBLIN_CAMP = 0xB40, + DECLINED_TO_HELP_IZA = 0xB80, + MIDNAS_DESPERATE_HOUR_STARTED = 0xC01, + CLEARED_LANAYRU_TWILIGHT = 0xC02, + TALKED_TO_KID_GORON_SHOP_IN_KAKARIKO_AT_NIGHT = 0xC04, + REMOVE_SWORD_SHIELD_FROM_WOLF_BACK = 0xC08, + MIDNA_ACCOMPANIES_WOLF = 0xC10, + TALKED_WITH_FARONE_AFTER_CLEARING_FOREST_TEMPLE = 0xC40, + MET_RUTELLA_AFTER_WARPING_METEOR_TO_ZORAS_DOMAIN = 0xC80, + ENTERED_ORDON_SHIELD_HOUSE_AS_WOLF_AT_NIGHT = 0xD01, + TALKED_TO_ONE_OF_THE_FROGS_OUTSIDE_RUSLS_HOUSE_AS_WOLF_AT_NIGHT = 0xD02, + TRANSFORMING_UNLOCKED = 0xD04, + TALKED_WITH_CLERK_AT_CASTLE_TOWN_MALO_MART = 0xD08, + TALKED_WITH_YETA_AFTER_GETTING_BEDROOM_KEY = 0xD10, + MIDNA_TEXT_AFTER_ORDON_SHIELD_OBTAINED = 0xD80, + START_ILIA_MEMORY_SIDEQUEST = 0xE01, + TWILIGHT_BLOAT_TEAR_APPEARS_ON_MAP = 0xE02, + MALO_TEXT_AFTER_LEAVING_SHOP_MENU = 0xE08, + LISTENED_TO_FYER_SPIRIT_IN_FILLED_LAKE_HYLIA_DURING_TWILIGHT = 0xE10, + TALKED_TO_RIGHT_GORON_IN_HOTSPRING_AFTER_KING_BULBLIN_1 = 0xE20, + TALKED_TO_SECOND_GORON_IN_DMT_AFTER_CLEARING_GORON_MINES = 0xE40, + TALKED_TO_BROWN_CUCOO_OUTSIDE_BOS_HOUSE_AS_WOLF_AT_NIGHT = 0xE80, + GOT_LANTERN_FROM_CORO = 0xF01, + IZA_TEXT_AFTER_IZA_1_DONE = 0xF02, + TALKED_WITH_FYER_AFTER_GOING_TO_DESERT = 0xF04, + WARPED_BRIDGE_OF_ELDIN_BACK = 0xF08, + FUNDED_CASTLE_TOWN_MALO_MART = 0xF10, + TALKED_WITH_DOCTOR_BEFORE_GIVING_INVOICE = 0xF20, + FORCED_TEXT_WHEN_ENTERING_DOCTORS_CLINIC_FOR_THE_FIRST_TIME = 0xF40, + GOT_RENADOS_LETTER = 0xF80, + SERAS_CAT_RETURNED_TO_SHOP = 0x1001, + ORDON_DAY_2_TALKED_TO_JAGGLE_ON_THE_PILLAR = 0x1002, + ORDON_DAY_2_L_TARGET_TALKED_TO_JAGGLE_ON_THE_PILLAR = 0x1004, + ORDON_DAY_2_TALKED_TO_COLIN_AFTER_FISHING_ROD = 0x1008, + WATCHED_START_OF_GAME_CUTSCENE = 0x1010, + ORDON_DAY_2_TALKED_TO_COLIN_BEFORE_FISHING_ROD = 0x1020, + ORDON_DAY_3_TALKED_TO_RUSL = 0x1040, + ORDON_DAY_3_TALKED_TO_PERGIE = 0x1080, + TALKED_TO_SERA_AFTER_GETTING_BOTTLE = 0x1101, + ORDON_DAY_3_TALKED_TO_ULI = 0x1102, + ORDON_DAY_2_STARTED_SWORD_TRAINING = 0x1104, + TALKED_TO_GOR_EBIZO_AFTER_COMPLETING_HOTSPRING_MINIGAME = 0x1108, + TALKED_WITH_YETA_AFTER_SHE_WALKS_UP_TO_BEDROOM = 0x1110, + HANCH_IS_ATTACKED_BY_BEES = 0x1120, + ORDON_DAY_2_TALKED_TO_JAGGLE_AFTER_CALLING_DOWN_EAGLE = 0x1140, + ORDON_DAY_2_TALKED_TO_HANCH_AFTER_TOUCHING_BEE_NEST = 0x1180, + SOUTH_FARON_WARP_FIGHT_STARTED = 0x1202, + ORDON_DAY_2_TALKED_TO_SERA_BEFORE_CAT_RETURNS = 0x1204, + TALKED_TO_SERA_AFTER_CAT_RETURNS = 0x1208, + HEARD_BO_TEXT_AFTER_SUMO_FIGHT = 0x1210, + TALK_TO_GORON_IN_FRONT_OF_DM_SHOP_AFTER_WINNING_GOR_CORON_SUMO_MATCH = 0x1302, + TALKED_TO_IZA_BEFORE_UZR_PORTAL = 0x1304, + WATCHED_CUTSCENE_AFTER_GORON_MINES = 0x1320, + LOST_SUMO_TO_GOR_CORON_TWICE = 0x1340, + TALKED_TO_FARONE_IN_FARON_TWILIGHT = 0x1380, + ORDON_DAY_3_COLIN_WENT_TO_SEE_ILIA_IN_ORDON_SPRING = 0x1402, + GOT_BOTTLE_FROM_SERA = 0x1408, + WATCHED_CUTSCENE_AFTER_GETTING_KNOCKED_OFF_DM_LEDGE_BY_GORON = 0x1410, + TALKED_WITH_YETA_AFTER_GIVING_CHEESE = 0x1420, + TALKED_WITH_YETA_AFTER_GIVING_PUMPKIN = 0x1440, + ENTERED_FYERS_CANON_FOR_THE_FIRST_TIME = 0x1480, + TALKED_TO_AGITHA_IN_HER_CASTLE_FOR_THE_FIRST_TIME = 0x1501, + TALKED_TO_HANCH_AFTER_HE_JUMPS_IN_THE_WATER_ORDON_DAY_2_OR_3 = 0x1502, + ORDON_DAY_2_JAGGLE_SCOLDS_YOU_FOR_BREAKING_ALL_THE_PUMPKINS_NEXT_TO_HIM = 0x1504, + HANCH_HIT_BEE_NEST_AND_DOVE_IN_WATER = 0x1508, + ORDON_DAY_2_TOUCHED_ORDON_HANGING_BEE_NEST = 0x1510, + ORDON_DAY_2_TOOK_DOWN_ORDON_BEE_NEST_WITH_HAWK = 0x1520, + WARPING_IN_LANAYRU_PROVINCE_DISABLED = 0x1540, + WATCHED_CUTSCENE_AFTER_GOATS_2 = 0x1580, + ORDON_DAY_2_DONE = 0x1601, + TOOK_DOWN_ORDON_BEE_NEST_WITH_SLINGSHOT = 0x1602, + GOT_A_LETTER_FROM_AGITHA = 0x1604, + ORDON_DAY_2_TALKED_TO_BO_AFTER_CATCHING_GOAT = 0x1620, + ORDON_DAY_2_CAUGHT_GOAT_IN_FRONT_OF_BOS_HOUSE = 0x1640, + HANCH_HIT_BEE_NEST_AND_DOVE_IN_WATER_TRIGGER = 0x1680, + TALKED_TO_FEMALE_OWNER_OF_GROCERY_STORE_IN_SOUTH_CASTLE_TOWN = 0x1701, + TALKED_TO_HYRULE_SOLDIER_IN_SOUTH_CASTLE_TOWN = 0x1702, + TALKED_TO_STALKER_OUTSIDE_AGITHAS_CASTLE = 0x1704, + ORDON_DAY_3_TALKED_TO_BO = 0x1710, + ORDON_DAY_2_TALKED_TO_HANCH_AFTER_TAKING_DOWN_BEE_NEST = 0x1780, + WATCHED_CUTSCENE_BETWEEN_RUSL_ULI_AFTER_ORDON_SHIELD = 0x1801, + CHECKED_TRILLS_MONEY_BOX = 0x1804, + CAUGHT_BY_RUSL_IN_ORDON_AS_WOLF_AT_NIGHT = 0x1808, + ORDON_DAY_2_TALO_RAN_AFTER_MONKEY_AFTER_SWORD_TRAINING = 0x1880, + WATCHED_ELDIN_SPIRIT_CUTSCENE_AFTER_OPENING_KAKARIKO_WARP = 0x1901, + SUCCESSFULLY_LISTENED_TO_BO_AND_JAGGLES_CONVERSATION_AS_WOLF = 0x1904, + COLIN_STOPPED_YOU_FROM_GETTING_ON_EPONA_ORDON_DAY_2 = 0x1908, + FAILED_TO_LISTEN_TO_BO_AND_JAGGLES_CONVERSATION_AS_WOLF = 0x1910, + SCARED_OFF_HANCH_AS_WOLF_AT_NIGHT = 0x1920, + HANCH_SPOTTED_YOU_AS_WOLF_AT_NIGHT = 0x1940, + TRIED_TO_ENTER_FARON_TWILIGHT_WITHOUT_SWORD_SHIELD = 0x1980, + TALKED_TO_ELDIN_SPIRIT_IN_ELDIN_TWILIGHT = 0x1A01, + LISTENED_TO_FIRST_GUARD_IN_SEWERS = 0x1A02, + BOUGHT_COROS_OIL_BOTTLE = 0x1A08, + TALKED_TO_CORO_AFTER_FARON_TWILIGHT = 0x1A10, + TALKED_TO_WHITE_CUCOO_NEAR_RUSLS_HOUSE_AS_WOLF_AT_NIGHT = 0x1A20, + TALKED_TO_SERAS_CAT_AS_WOLF_AT_NIGHT = 0x1A40, + TALKED_TO_MIDNA_AFTER_ESCAPING_CELL_IN_SEWERS = 0x1A80, + TRIED_TO_LEAVE_MIST_AREA_WITHOUT_PICKING_UP_LANTERN = 0x1B01, + LISTENED_TO_FIRST_GORON_SPIRIT_IN_DEATH_MOUNTAIN_TWILIGHT = 0x1B02, + PICKED_UP_LANTERN_AFTER_MONKEY_STEALING_SEQUENCE = 0x1B08, + MONKEY_DROPPED_YOUR_LANTERN = 0x1B10, + MONKEY_STOLE_YOUR_LANTERN = 0x1B20, + LISTENED_TO_CORO_SPIRIT_BEFORE_KILLING_BUGS_IN_HIS_HOUSE = 0x1B40, + TALKED_TO_MALO_AFTER_KING_BULBLIN_1 = 0x1C01, + SAW_MIDNA_TEXT_AFTER_TRYING_TO_WARP_AN_OBJECT_TO_THE_WRONG_PLACE = 0x1C02, + SACRED_GROVE_STATUE_PUZZLE_COMPLETED = 0x1C04, + WIN_SUMO_ROUND_1_AGAINST_BO = 0x1C10, + BO_TALKED_TO_YOU_AFTER_OPENING_IRON_BOOTS_CHEST = 0x1C20, + ACCEPTED_TO_KEEP_BOS_SECRET = 0x1C40, + LISTENED_TO_SECOND_GORON_SPIRIT_IN_DEATH_MOUNTAIN_TWILIGHT = 0x1C80, + MINI_MAP_RETRACTED = 0x1D01, + TALKED_TO_TALO_AFTER_KING_BULBLIN_1 = 0x1D02, + TALKED_TO_LUDA_AFTER_KING_BULBLIN_1 = 0x1D04, + TALKED_TO_DARBUS_AFTER_DEFEATING_FYRUS = 0x1D08, + TALKED_TO_BARNES_AFTER_GORON_MINES = 0x1D10, + TALKED_TO_BARNES_AFTER_KING_BULBLIN_1 = 0x1D20, + LISTENED_TO_FYER_SPIRIT_IN_DRAINED_LAKE_HYLIA_DURING_LANAYRU_TWILIGHT = 0x1D40, + TALKED_TO_SERA_A_SECOND_TIME_AFTER_FARON_TWILIGHT = 0x1E02, + TALKED_TO_SERA_AFTER_FARON_TWILIGHT = 0x1E04, + MIDNAS_DESPERATE_HOUR_COMPLETED = 0x1E08, + TALKED_TO_CORO_AFTER_FOREST_TEMPLE = 0x1E20, + TALK_TO_MALO_AFTER_FUNDRAISING_IS_OPEN = 0x1E40, + MALO_MART_FUNDRAISING_STARTS = 0x1E80, + HEARD_FORCED_MIDNA_TEXT_E3_2006_GORON_MINES = 0x1F04, + HEARD_MIDNA_TEXT_ONTOP_OF_MAGNET_SWITCH_E3_2006_GORON_MINES = 0x1F08, + FYRUS_IS_ON_THE_GROUND = 0x1F10, + KNOCKED_FYRUS_DOWN_FOR_THE_FIRST_TIME = 0x1F20, + FYRUS_IS_STUNNED = 0x1F40, + SHOT_FYRUS_EYE_FOR_THE_FIRST_TIME = 0x1F80, + TALKED_WITH_TELMA_AFTER_GETTING_MASTER_SWORD = 0x2001, + CITY_IN_THE_SKY_CLEARED = 0x2002, + TEMPLE_OF_TIME_CLEARED = 0x2004, + SNOWPEAK_RUINS_CLEARED = 0x2008, + ARBITERS_GROUNDS_CLEARED = 0x2010, + GOT_MASTER_SWORD = 0x2020, + WATCHED_TELMA_ILIA_SPIRIT_CUTSCENE_IN_THE_BAR_DURING_LANAYRU_TWILIGHT = 0x2101, + TALKED_TO_LOUISE_ABOUT_THE_STOLEN_STATUE = 0x2102, + GAVE_TELMA_RENADOS_LETTER = 0x2180, + TALKED_TO_PLUMM_AS_WOLF_FOR_THE_FIRST_TIME = 0x2201, + GOT_A_HIGH_SCORE_IN_PLUMMS_MINIGAME = 0x2202, + GOT_WOOD_STATUE = 0x2204, + TALKED_TO_YETO_ON_TOP_OF_THE_MOUNTAIN_AFTER_CLEARING_SPR = 0x2208, + MALO_MART_CASTLE_TOWN_BRANCH_IS_OPEN = 0x2210, + GOT_ILIAS_SCENT = 0x2220, + GOT_YOUTHS_SCENT = 0x2240, + GOT_ILIAS_CHARM = 0x2280, + CLEARED_STAR_2 = 0x2301, + STARTED_STAR_2 = 0x2302, + MAGIC_UNLOCKED = 0x2304, + CLEARED_STAR_1 = 0x2308, + ENTERED_STAR_FOR_THE_FIRST_TIME = 0x2310, + GAVE_ILIA_HER_CHARM = 0x2320, + GAVE_ILIA_THE_WOOD_STATUE = 0x2340, + WON_PLUMMS_HEART_PIECE = 0x2380, + TALKED_TO_SHAD_AFTER_FINISHING_CITY_IN_THE_SKY = 0x2401, + TALKED_TO_SHAD_FOR_A_SECOND_TIME_IN_TELMAS_BAR = 0x2402, + TALKED_TO_SHAD_IN_TELMAS_BAR = 0x2404, + TALKED_TO_AURU_IN_TELMAS_BAR = 0x2408, + TALKED_TO_CENTRAL_CASTLE_TOWN_SHOP_CLERK = 0x2410, + TALKED_TO_LOUISE_AFTER_BEING_THROWN_OUT_DURING_MDH = 0x2420, + WATCHED_LOUISE_CUTSCENE_DURING_MIDNAS_DESPERATE_HOUR = 0x2440, + DONATED_1000_RUPEES_TO_CHARLO = 0x2480, + WATCHED_CUTSCENE_WITH_YETO_ON_TOP_OF_MOUNTAIN = 0x2502, + TALKED_TO_YETO_ON_TOP_OF_MOUNTAIN_AS_WOLF = 0x2504, + GOT_AURUS_MEMO = 0x2510, + TALKED_TO_AURU_IN_LAKE_HYLIA = 0x2520, + SHAD_USED_COMPLETED_SKYBOOK = 0x2540, + SHAD_CASTS_UNFINISHED_SPELL_ON_STATUE = 0x2580, + SOL_FLAG_1 = 0x2601, + SOL_FLAG_2 = 0x2602, + PALACE_WEST_SOL_TAKEN_IN_PHANTOM_ZANT_ROOM = 0x2604, + SOL_FLAG_3 = 0x2608, + PALACE_WEST_SOL_TAKEN_OUT_PHANTOM_ZANT_ROOM = 0x2610, + PALACE_EAST_SOL_TAKEN_OUTSIDE = 0x2620, + PALACE_WEST_SOL_TAKEN_OUTSIDE = 0x2640, + SHOWED_AURUS_MEMO_TO_FYER = 0x2680, + TALKED_TO_UNDERWATER_ZORA_ABOUT_GORON_IN_THRONE_ROOM_AFTER_RELEASE = 0x2701, + TALKED_TO_UNDERWATER_ZORA_ABOUT_GORON_IN_THRONE_ROOM_BEFORE_RELEASE = 0x2702, + TALKED_TO_WEST_ZORA_IN_DOMAIN_THRONE_ROOM_AFTER_LANAYRU_TWILIGHT = 0x2704, + TALKED_TO_SWIMMING_ZORA_IN_ZORAS_DOMAIN_BEFORE_LAKEBED = 0x2708, + GAVE_INVOICE_TO_DOCTOR = 0x2710, + PALACE_EAST_SOL_TAKEN_IN_PHANTOM_ZANT_ROOM = 0x2720, + SOL_FLAG_4 = 0x2740, + PALACE_EAST_SOL_TAKEN_OUT_OF_PHANTOM_ZANT_ROOM = 0x2780, + SAW_ULI_TEXT_BEFORE_LEAVING_RUSLS_HOUSE = 0x2801, + TALKED_TO_ULI_A_SECOND_TIME_AFTER_FARON_TWILIGHT = 0x2802, + TALKED_TO_ULI_AFTER_FARON_TWILIGHT = 0x2804, + SAW_FORCED_ULI_TEXT_IN_RUSLS_HOUSE_AFTER_FARON_TWILIGHT = 0x2808, + TALKED_TO_JAGGLE_A_SECOND_TIME_AFTER_FARON_TWILIGHT = 0x2810, + TALKED_TO_JAGGLE_AFTER_FARON_TWILIGHT = 0x2820, + USED_OOCCOO_FOR_THE_FIRST_TIME = 0x2840, + HELM_SPLITTER_UNLOCKED = 0x2901, + BACKSLICE_UNLOCKED = 0x2902, + ENDING_BLOW_UNLOCKED = 0x2904, + SHIELD_ATTACK_UNLOCKED = 0x2908, + LISTENED_TO_LADY_SPIRITS_TALKING_IN_WEST_CASTLE_TOWN_DURING_TWILIGHT = 0x2910, + FREED_UNDERWATER_GORON_IN_ZORAS_DOMAIN = 0x2920, + GOT_ASHEIS_SKETCH = 0x2940, + TALKED_TO_ASHEI_IN_TELMAS_BAR = 0x2980, + TALKED_TO_ZORA_SOLDIER_NEAR_LAKEBED_ENTRANCE_AFTER_OPENING = 0x2A01, + TALKED_TO_ZORA_SOLDIER_NEAR_LAKEBED_ENTRANCE_BEFORE_OPENING = 0x2A02, + TALKED_TO_ASHEI_IN_TELMAS_BAR_AFTER_FINISHING_SPR = 0x2A04, + ORDON_DAY_2_LIT_COROS_POT = 0x2A10, + GREAT_SPIN_UNLOCKED = 0x2A20, + JUMP_STRIKE_UNLOCKED = 0x2A40, + MORTAL_DRAW_UNLOCKED = 0x2A80, + TALKED_TO_DOCTOR_AFTER_RESTORING_ILIAS_MEMORY = 0x2B02, + SHOWED_WOOD_STATUE_TO_DOCTOR = 0x2B04, + FIXED_THE_MIRROR_OF_TWILIGHT = 0x2B08, + TALKED_TO_LADIES_OUTSIDE_CENTRAL_CASTLE_TOWN_SHOP_BEFORE_MALO_MART = 0x2B10, + UNK_2B20 = 0x2B20, + TALKED_TO_GOR_CORON_AFTER_CLEARING_GORON_MINES = 0x2C01, + SAVED_MONKEY_IN_FARON_TWILIGHT = 0x2C02, + UNK_2C04 = 0x2C04, + TALKED_TO_RUSL_IN_TELMAS_BAR = 0x2C08, + RAISED_MIRROR_IN_MIRROR_CHAMBER = 0x2C10, + UNK_2C20 = 0x2C20, + UNK_2C40 = 0x2C40, + TALKED_TO_TELMA_AFTER_RECOVERING_ILIAS_MEMORY = 0x2C80, + TALKED_TO_DARBUS_AFTER_RESTORING_ILIAS_MEMORY = 0x2D01, + UNK_2D02 = 0x2D02, + UNK_2D04 = 0x2D04, + TALKED_TO_GORON_BY_BARNES_SHOP_AFTER_GORON_MINES = 0x2D08, + TALKED_TO_GORON_IN_KAKARIKO_HOTSPRING_AFTER_GORON_MINES = 0x2D10, + TALKED_TO_GORON_BY_KAKARIKO_WATCHTOWER_AFTER_GORON_MINES = 0x2D20, + TALKED_TO_GORON_IN_FRONT_OF_KAKARIKO_INN_AFTER_GORON_MINES = 0x2D40, + TALKED_TO_FIRST_GORON_ON_DEATH_MOUNTAIN_TRAIL_AFTER_GORON_MINES = 0x2D80, + TALKED_TO_ADULT_GORON_AFTER_OPENING_HOTSPRING_WATER_SHOP_IN_CASTLE_TOWN = 0x2E01, + TALKED_TO_AGITHA_FOR_THE_FIRST_TIME_OUTSIDE_SOUTH_CASTLE_TOWN = 0x2E02, + GAVE_ALL_24_GOLDEN_BUGS_TO_AGITHA = 0x2E04, + HIDDEN_VILLAGE_BARRIER_REMOVED = 0x2E08, + HELPED_OUTSIDE_SOUTH_CASTLE_TOWN_GORON = 0x2E10, + BRIDGE_REPAIR_FUNDRAISING_COMPLETED = 0x2E20, + TALKED_TO_GOR_EBIZO_IN_KAKARIKO = 0x2E40, + TALKED_TO_DARBUS_AFTER_CLEARING_GORON_MINES = 0x2E80, + TALKED_TO_GORON_OUTSIDE_SOUTH_CASTLE_TOWN = 0x2F01, + UNK_2F02 = 0x2F02, + GOT_MEDICINE_SCENT = 0x2F04, + TALKED_TO_BARNES_AFTER_UNLOCKING_BOMBLINGS = 0x2F08, + TALKED_TO_BARNES_AFTER_UNLOCKING_WATER_BOMBS = 0x2F10, + TALKED_TO_CHILD_GORON_BEFORE_OPENING_HOTSPRING_WATER_SHOP_IN_CASTLE_TOWN = 0x2F20, + TALKED_TO_CHILD_GORON_AFTER_OPENING_HOTSPRING_WATER_SHOP_IN_CASTLE_TOWN = 0x2F40, + TALKED_TO_GORON_OUTSIDE_EAST_CASTLE_TOWN = 0x2F80, + TALKED_TO_GOR_LIGGS_AFTER_UNK = 0x3001, + STARTED_HOTSPRING_WATER_MINIGAME = 0x3002, + TALKED_TO_GOR_LIGGS_IN_KAKARIKO_VILLAGE = 0x3004, + TALKED_TO_GOR_AMATO_IN_GORON_MINES = 0x3008, + UNK_3010 = 0x3010, + TALKED_TO_GORON_OUTSIDE_SOUTH_CASTLE_TOWN_AFTER_GIVING_WATER = 0x3020, + GAVE_HOTSPRING_WATER_TO_GORON_OUTSIDE_SOUTH_CASTLE_TOWN = 0x3040, + TALKED_TO_JOVANI_FOR_THE_FIRST_TIME_BEFORE_POE = 0x3080, + M_STAG_BEETLE_TURNED_IN = 0x3101, + F_BUTTERFLY_TURNED_IN = 0x3102, + M_BUTTERFLY_TURNED_IN = 0x3104, + F_BEETLE_TURNED_IN = 0x3108, + M_BEETLE_TURNED_IN = 0x3110, + WARPED_SKY_CANNON_TO_LAKE_HYLIA = 0x3120, + TALKED_TO_GOR_LIGGS_AFTER_TALKING_TO_GORON_OUTSIDE_EAST_CASTLE_TOWN = 0x3140, + TALKED_TO_GOR_LIGGS_AFTER_BRIDGE_HAS_BEEN_FIXED = 0x3180, + M_MANTIS_TURNED_IN = 0x3201, + F_PILLBUG_TURNED_IN = 0x3202, + M_PILLBUG_TURNED_IN = 0x3204, + F_PHASMID_TURNED_IN = 0x3208, + M_PHASMID_TURNED_IN = 0x3210, + F_GRASSHOPPER_TURNED_IN = 0x3220, + M_GRASSHOPPER_TURNED_IN = 0x3240, + F_STAG_BEETLE_TURNED_IN = 0x3280, + M_ANT_TURNED_IN = 0x3301, + F_DRAGONFLY_TURNED_IN = 0x3302, + M_DRAGONFLY_TURNED_IN = 0x3304, + F_SNAIL_TURNED_IN = 0x3308, + M_SNAIL_TURNED_IN = 0x3310, + F_LADYBUG_TURNED_IN = 0x3320, + M_LADYBUG_TURNED_IN = 0x3340, + F_MANTIS_TURNED_IN = 0x3380, + F_DAYFLY_TURNED_IN = 0x3420, + M_DAYFLY_TURNED_IN = 0x3440, + F_ANT_TURNED_IN = 0x3480, + TALKED_TO_GOR_LIGGS_IN_GORON_MINES = 0x3701, + TALKED_TO_GOR_EBIZO_IN_GORON_MINES = 0x3702, + TALKED_TO_POSTMAN_FOR_THE_FIRST_TIME = 0x3704, + TALKED_TO_GOR_LIGGS_AFTER_TALKING_TO_GORON_OUTSIDE_EAST_CASTLE_TOWN_BEFORE_FIXING_BRIDGE = 0x3710, + TEMP_USED_AFTER_PAYING_TO_FISH_WITH_HENA = 0x3801, + TALKED_TO_HENA_FOR_THE_FIRST_TIME = 0x3802, + ENTERED_HENAS_CABIN_FOR_THE_FIRST_TIME = 0x3804, + TALKED_TO_GENGLE_AFTER_COLLECTING_20_POE_SOULS = 0x3808, + TALKED_TO_JOVANI_AFTER_COLLECTING_60_POE_SOULS = 0x3820, + UNK_3840 = 0x3840, + TALKED_TO_JOVANI_AFTER_DEFEATING_THE_POE_IN_HIS_HOUSE = 0x3880, + WON_2ND_SUMO_ROUND_AGAINST_BO = 0x3901, + RELEASED_FIRST_CAUGHT_FISH_ORDON_DAY_2 = 0x3902, + BEAT_ROLLGOAL_1_8 = 0x3904, + CAUGHT_THE_FISHING_BOTTLE = 0x3908, + RESERVED_FOR_FISHING_1 = 0x3910, + CAUGHT_THE_SINKING_LURE = 0x3920, + WENT_FISHING_WITH_HENA_FOR_THE_FIRST_TIME = 0x3940, + TEMP_USED_AFTER_PAYING_TO_FISH_WITH_HENA_2 = 0x3980, + TALKED_WITH_RALIS_IN_KAKARIKO_GRAVEYARD = 0x3A01, + TALKED_TO_RALIS_IN_ZORAS_DOMAIN_THRONE_ROOM = 0x3A02, + HOWLED_AT_HIDDEN_VILLAGE_STONE = 0x3A04, + HOWLED_AT_SNOWPEAK_STONE = 0x3A08, + HOWLED_AT_LAKE_HYLIA_STONE = 0x3A10, + HOWLED_AT_SACRED_GROVE_OUTSIDE_STONE = 0x3A20, + HOWLED_AT_UPPER_ZORAS_RIVER_STONE = 0x3A40, + HOWLED_AT_DEATH_MOUNTAIN_STONE = 0x3A80, + TALKED_WITH_LUDA_AFTER_RALIS_RETURNS_TO_ZORAS_DOMAIN = 0x3B01, + TALKED_WITH_LUDA_WHILE_RALIS_IS_IN_KAKARIKO_GRAVEYARD = 0x3B02, + TALKED_TO_FYER_ABOUT_REPAIRING_THE_SKY_CANNON = 0x3B04, + SKY_CANNON_REPAIRED = 0x3B08, + WON_SNOWBOARD_RACE_AGAINST_YETA = 0x3B10, + TALKED_TO_YETA_AFTER_WINNING_RACE_AGAINST_YETO = 0x3B20, + WON_SNOWBOARD_RACE_AGAINST_YETO = 0x3B40, + GOT_CORAL_EARRING_FROM_RALIS = 0x3B80, + GOT_SKILL_FROM_BULBLIN_CAMP_WOLF = 0x3C01, + GOT_SKILL_FROM_SOUTH_CT_FIELD_WOLF = 0x3C02, + GOT_SKILL_FROM_WEST_CT_WOLF = 0x3C04, + GOT_SKILL_FROM_ORDON_WOLF = 0x3C08, + TALKED_TO_WHITE_CUCCOO_INSIDE_FENCES_NEXT_TO_BOS_HOUSE_AS_WOLF_AT_NIGHT = 0x3C20, + TALKED_TO_GORON_OUTSIDE_BARNES_SHOP_AFTER_UNLOCKING_BOMBLINGS = 0x3C40, + TALKED_TO_GORON_OUTSIDE_BARNES_SHOP_AFTER_UNLOCKING_WATER_BOMBS = 0x3C80, + ELDIN_SPRING_HAS_FARIES = 0x3D01, + FARON_SPRING_HAS_FARIES = 0x3D02, + ORDON_SPRING_HAS_FARIES = 0x3D04, + ENTER_RUSLS_HOUSE_AFTER_FARON_TWILIGHT = 0x3D08, + UNK_3D10 = 0x3D10, + GOT_SKILL_FROM_BARRIER_WOLF = 0x3D40, + GOT_SKILL_FROM_GRAVEYARD_WOLF = 0x3D80, + TALKED_WITH_COLIN_WHILE_RALIS_IS_IN_KAKARIKO_GRAVEYARD = 0x3E01, + CITY_OOCCOO_CS_WATCHED = 0x3E02, + FOUND_OOCCOO_FOR_THE_SECOND_TIME = 0x3E04, + FOUND_OOCCOO_FOR_THE_FIRST_TIME = 0x3E08, + OOCCOO_NOW_HAS_OOCCOO_JR_NEXT_TO_HER = 0x3E10, + OOCCOO_MET_BUT_DOESNT_HAVE_OOCCOO_JR_YET_UNSET_ONCE_JR_MET = 0x3E20, + SPRING_SPIRITS_CAN_GIVE_FARY_TEARS = 0x3E40, + LANAYRU_SPRING_HAS_FARIES = 0x3E80, + TALKED_TO_SERA_A_SECOND_TIME_AFTER_ELDIN_TWILIGHT = 0x3F01, + TALKED_TO_SERA_AFTER_ELDIN_TWILIGHT = 0x3F02, + TALKED_TO_JAGGLE_AFTER_ELDIN_TWILIGHT = 0x3F04, + TALKED_TO_ULI_AFTER_GM = 0x3F08, + TALKED_TO_ULI_A_SECOND_TIME_AFTER_ELDIN_TWILIGHT = 0x3F10, + TALKED_TO_ULI_AFTER_ELDIN_TWILIGHT = 0x3F20, + TALKED_WITH_COLIN_AFTER_RALIS_RETURNS_TO_ZORAS_DOMAIN = 0x3F40, + TALKED_TO_RUSL_IN_TELMAS_BAR_AFTER_FINISHING_TOT = 0x4001, + RUSL_IN_N_FARON_SUMMONS_GOLD_CUCCO = 0x4002, + DECLINED_TO_HELP_RUSL_IN_N_FARON_OFF_AFTER_SAYING_YES = 0x4004, + VISITED_DESERT_FOR_THE_FIRST_TIME = 0x4008, + TALKED_TO_ZORA_BOMB_SELLER_BY_LAKEBED_ENTRANCE_BEFORE_OPENING = 0x4010, + TALK_TO_HANCH_AFTER_ELDIN_TWILIGHT = 0x4020, + TALK_TO_HANCH_AFTER_FARON_TWILIGHT = 0x4040, + SAVED_MONKEY_FROM_PUPPETS = 0x4080, + TALKED_TO_UNDERWATER_GORON_IN_ZORAS_DOMAIN_AFTER_X_ = 0x4104, + TALK_TO_FADO_AFTER_FARON_TWILIGHT = 0x4108, + TALK_TO_FADO_AFTER_ELDIN_TWILIGHT = 0x4110, + TALK_TO_PERGIE_AFTER_FARON_TWILIGHT = 0x4140, + TALK_TO_PERGIE_AFTER_ELDIN_TWILIGHT = 0x4180, + WATCHED_POST_TOT_OOCCOO_CS = 0x4201, + TRIGGERED_MONKEY_PUPPET_SCENE = 0x4202, + WATCHED_CUTSCENE_WITH_RUSL_IN_N_FARON_AFTER_FINISHING_SPR = 0x4204, + BARRIER_GONE = 0x4208, + MIDNA_TEXT_AFTER_WARPING_BACK_TO_FARON_IN_ELDIN_TWILIGHT = 0x4220, + GOATS_3_DONE = 0x4240, + PALACE_WEST_WING_SOL_IN_WEST_SLOT = 0x4302, + SENSES_UNLOCKED = 0x4308, + LISTENED_TO_ADULT_SPIRITS_NEXT_TO_FOUNTAIN_IN_CASTLE_TOWN_TWILIGHT = 0x4310, + TALKED_TO_JAGGLE_ORDON_DAY_3 = 0x4320, + TALKED_TO_EAST_CT_GUARD = 0x4380, + PALACE_EAST_WING_SOL_IN_WEST_SLOT = 0x4408, + PALACE_WEST_WING_SOL_IN_EAST_SLOT = 0x4420, + POSTMAN_LEAVES_FOR_THE_FIRST_TIME_1 = 0x4504, + POSTMAN_LEAVES_FOR_THE_FIRST_TIME_2 = 0x4508, + ORDON_DAY_2_OVER = 0x4510, + LISTENED_TO_SOLDIER_SPIRITS_IN_TELMAS_BAR_DURING_TWILIGHT_1 = 0x4520, + TOOK_CRADLE_FROM_MONKEY_DAY_2 = 0x4601, + TALKED_TO_FADO_DAY_2 = 0x4602, + RODE_EPONA_BACK_TO_LINKS_HOUSE_ORDON_DAY_1 = 0x4610, + REFUSE_TO_GIVE_WOODEN_SWORD_SECOND_TIME_ORDON_DAY_3 = 0x4620, + REFUSE_TO_GIVE_WOODEN_SWORD_FIRST_TIME_ORDON_DAY_3 = 0x4640, + UNK_4680 = 0x4680, + TALKED_TO_BO_ORDON_DAY_1 = 0x4701, + TALKED_TO_RUSL_ORDON_DAY_1 = 0x4702, + TALKED_TO_ILIA_BEFORE_CALLING_EPONA_ORDON_DAY_1 = 0x4704, + TALKED_TO_ILIA_AFTER_CALLING_EPONA_ORDON_DAY_1 = 0x4708, + USED_HAWK_GRASS_FOR_THE_FIRST_TIME_ORDON_DAY_2 = 0x4710, + CALLED_EPONA_IN_ORDON_SPRING = 0x4720, + TALKED_TO_HANCH_BEFORE_BEE_ATTACK_ORDON_DAY_2 = 0x4740, + TALKED_TO_BETH_ORDON_DAY_3 = 0x4780, + TALKED_TO_BETH_DURING_TALO_RESCUE_SEQUENCE = 0x4801, + TALKED_TO_MALO_DURING_TALO_RESCUE_SEQUENCE = 0x4804, + TALKED_TO_SERA_ORDON_DAY_1 = 0x4810, + FAILED_TO_CATCH_GOAT_IN_FRONT_OF_BOS_HOUSE_ORDON_DAY_3 = 0x4820, + CAUGHT_GOAT_IN_FRONT_OF_BOS_HOUSE_ORDON_DAY_3 = 0x4840, + TALKED_TO_BO_AFTER_CATCHING_A_GOAT_ORDON_DAY_3 = 0x4880, + TALKED_TO_TALO_IN_CAGE_DAY_2 = 0x4901, + BOUGHT_SLINGSHOT_FROM_SERA = 0x4902, + TALKED_TO_BO_START_OF_DAY_2 = 0x4908, + TALKED_TO_BO_AFTER_CATCHING_GOAT_DAY_2 = 0x4910, + TALKED_TO_COLIN_DURING_TALO_RESCUE_SEQUENCE = 0x4920, + STARTED_SLINGSHOT_TUTORIAL = 0x4A02, + SACRED_GROVE_STATUES_SWITCHED = 0x4A08, + SAW_TALO_IN_CAGE_CUTSCENE_ORDON_DAY_2 = 0x4A10, + TALO_CHASES_MONKEY = 0x4A20, + ORDON_DAY_1_FINISHED = 0x4A40, + ZOOMED_IN_ON_FISH_TANK_HENAS_HUT = 0x4A80, + TALKED_TO_TALO_AFTER_GIVING_HIM_THE_WOODEN_SWORD_DAY_3 = 0x4B02, + TALKED_TO_LANAYRU_SPIRIT_IN_TWILIGHT = 0x4B04, + TALKED_TO_KIDS_AFTER_FINISHING_SLINGSHOT_TUTORIAL = 0x4B08, + TALKED_TO_KIDS_AFTER_HITTING_OBJECT_SLINGSHOT_TUTORIAL = 0x4B10, + TALKED_TO_KIDS_BEFORE_HITTING_OBJECT_SLINGSHOT_TUTORIAL = 0x4B20, + BROKE_A_PUMPKIN_FIRST_SLINGSHOT_TUTORIAL = 0x4B40, + BROKE_A_TARGET_FIRST_SLINGSHOT_TUTORIAL = 0x4B80, + RESCUED_TALO_AND_THE_MONKEY_ORDON_DAY_2 = 0x4C01, + TALKED_TO_HANCH_ORDON_DAY_3 = 0x4C04, + TALKED_TO_HANCH_AFTER_TALKING_TO_SERA_ORDON_DAY_2 = 0x4C08, + UNK_4C20 = 0x4C20, + TALKED_TO_ZORA_CLOSE_TO_FYER_LAKE_HYLIA_AFTER_LANAYRU_TWILIGHT = 0x4C40, + PUT_BEE_LARVA_IN_BOTTLE_ORDON_DAY_2 = 0x4C80, + ZOOMED_IN_ON_LURES_1_HENA = 0x4D01, + WATCHED_CUTSCENE_AFTER_BEING_CAPTURED_IN_FARON_TWILIGHT = 0x4D08, + TALKED_TO_GENGLE_AFTER_TALKING_TO_JOVANI_IN_THE_BAR = 0x4D10, + TALKED_TO_JOVANI_IN_BAR = 0x4D40, + GOT_BOTTLE_FROM_JOVANI = 0x4D80, + ZOOMED_IN_ON_BOOK_HENAS_HUT = 0x4E01, + ZOOMED_IN_ON_CARPET_HENAS_HUT = 0x4E02, + ZOOMED_IN_ON_JARS_HENAS_HUT = 0x4E04, + ZOOMED_IN_ON_HAT_HENAS_HUT = 0x4E08, + ZOOMED_IN_ON_CANOE_HENAS_HUT = 0x4E10, + HENA_BEAT_ROLLGOAL_1_8_FROG_LURE = 0x4E20, + HENA_ZOOMED_IN_ON_LURES_2 = 0x4E80, + HENA_ZOOMED_IN_ON_LINK_LOACH_PIC_2 = 0x4F01, + HENA_ZOOMED_IN_ON_LINK_LOACH_PIC_1 = 0x4F02, + COMPARE_HENA_AND_IZA_PICTURES_HENAS_HUT = 0x4F04, + ZOOMED_IN_ON_HENA_PICTURE_LEFT = 0x4F08, + ZOOMED_IN_ON_HENA_PICTURE_RIGHT = 0x4F10, + ZOOMED_IN_ON_IZA_PICTURE_HENAS_HUT = 0x4F20, + ZOOMED_IN_ON_CORO_PICTURE_HENAS_HUT = 0x4F40, + ZOOMED_IN_ON_FISHERMAN_PICTURE_HENAS_HUT = 0x4F80, + UNK_5001 = 0x5001, + CAUGHT_AN_ADULT_HYLIAN_LOACH = 0x5002, + CAUGHT_FIRST_FISH_WITH_HENA = 0x5004, + CAUGHT_AN_ORDON_CATFISH_NON_BOAT = 0x5008, + CAUGHT_A_BABY_HYLIAN_LOACH = 0x5010, + CAUGHT_A_HYLIAN_PIKE_NON_BOAT = 0x5020, + CAUGHT_A_HYRULE_BASS_NON_BOAT = 0x5040, + CAUGHT_A_GREENGILL = 0x5080, + ZOOMED_IN_ON_ROLLGOAL_HENAS_HUT = 0x5102, + HENA_TALKS_ABOUT_HARDER_ROLLGOAL_BEFORE_2_1 = 0x5108, + BEAT_ROLLGOAL_LEVEL_HENAS_HUT = 0x5110, + LISTENED_TO_TWO_ZORA_SPIRITS_IN_DRAINED_LAKE_HYLIA_TWILIGHT = 0x5120, + LISTENED_TO_LONE_ZORA_SPIRIT_IN_DRAINED_LAKE_HYLIA_TWILIGHT = 0x5140, + LISTENED_TO_SOLDIER_SPIRITS_IN_TELMAS_BAR_DURING_TWILIGHT_2 = 0x5180, + DANGORO_WENT_IN_A_BALL_FOR_THE_FIRST_TIME = 0x5204, + THREW_DANGORO_IN_LAVA_FOR_THE_FIRST_TIME = 0x5208, + FYRUS_GETS_UP_FIRST_TIME = 0x5210, + FYRUS_KNOCKED_DOWN_FIRST_TIME = 0x5220, + OOCCOO_SHOPKEEPER_OPENING_TEXT_READ = 0x5301, + TALKED_TO_GUY_OUTSIDE_CT_MALO_MART = 0x5308, + TALKED_TO_LADY_OUTSIDE_AGITHAS_CASTLE = 0x5401, + TALKED_TO_NPC_BY_SPRINGWATER = 0x5404, + TALKED_TO_NPC_BY_SPRINGWATER_SHOP_BEFORE_FIXING = 0x5408, + PALACE_OF_TWILIGHT_CLEARED = 0x5410, + UPDATE_SHARDS_TO_HAVE_AT_LEAST_ARBITERS_SHARD = 0x5420, + USED_SENSES_TO_SEE_STATUE_GHOST_IN_TEMPLE_OF_TIME_FIRST_ROOM = 0x5440, + UNK_5502 = 0x5502, + TALKED_TO_SOLDIER_IN_SOUTH_CASTLE_TOWN_BY_FLOWER_SHOP = 0x5504, + FIRST_FROG_LURE_FAIL_WITH_HENA = 0x5508, + TALKED_TO_JOVANI_AFTER_COLLECTING_20_POE_SOULS = 0x5510, + TALKED_TO_PLUMM_AS_HUMAN_AFTER_LANAYRU_TWILIGHT = 0x5520, + TALKED_WITH_CAT_AFTER_MIDNAS_DESPERATE_HOUR = 0x5601, + LISTENED_TO_IZA_SPIRIT_DURING_TWILIGHT_WHILE_DOMAIN_IS_STILL_FROZEN = 0x5604, + CAUGHT_A_REEKFISH = 0x5608, + TALKED_TO_THE_WEST_CT_DOG = 0x5710, + TALKED_WITH_BLACKWHITE_CAT_AFTER_SAVING_JOVANI = 0x5740, + TALKED_TO_SHOE_SHINER_IN_CENTRAL_CASTLE_TOWN = 0x5801, + TALKED_TO_RENADO_AFTER_RESTORING_ILIAS_MEMORY = 0x5804, + TALKED_TO_SOUTH_STARING_MAN_IN_EAST_CASTLE_TOWN = 0x5810, + TALKED_TO_NORTH_STARING_MAN_IN_EAST_CASTLE_TOWN = 0x5820, + TALKED_TO_FRUIT_SHOP_SELLER_IN_SOUTH_CASTLE_TOWN = 0x5840, + TALKED_TO_THE_CT_SHOE_SHINER = 0x5901, + IZA_2_MINIGAME_DONE = 0x5908, + UNK_5920 = 0x5920, + ENCOUNTER_POSTMAN_FOR_THE_FIRST_TIME = 0x5940, + TRIED_TO_ENTER_CENTRAL_CASTLE_TOWN_SHOP_WITH_DIRTY_SHOES = 0x5980, + AGREED_TO_DO_CAT_MINIGAME = 0x5B02, + TALKED_TO_HIDDEN_VILLAGE_CUCCO = 0x5B04, + CAT_MINIGAME_DONE = 0x5B08, + TALKED_TO_OWL_ORDON_WOLF_NIGHT = 0x5B10, + GENEROUS_WITH_TRILL_1 = 0x5C01, + TALKED_TO_SERA_ORDON_DAY_3 = 0x5C02, + CHEAP_WITH_TRILL_TEXT_AFTER_LEAVING = 0x5C04, + STOLE_FROM_TRILL_OR_TALKED_WITH_HIM_AS_WOLF = 0x5C08, + UNK_5C10 = 0x5C10, + TALKED_TO_IZA_SPIRIT_AFTER_MELTING_ZORAS_DOMAIN_TWILIGHT = 0x5C20, + SCOOPED_COROS_NASTY_SOUP = 0x5C80, + CAN_NOW_WARP_METEOR = 0x5D01, + GENEROUS_WITH_TRILL_2 = 0x5D02, + CHEAP_WITH_TRILL_TEXT_AFTER_PAYING = 0x5D04, + MIDNA_TEXT_AFTER_FROZEN_ZORAS_DOMAIN_TWILIGHT_INTRO_CS = 0x5D10, + MIDNA_TEXT_AFTER_TWILIGHT_KAGOROK_FLIGHT = 0x5D20, + MIDNA_TEXT_AFTER_LANDING_IN_LAKE_HYLIA_DURING_LANAYRU_TWILIGHT = 0x5D40, + ILIA_TEXT_AFTER_HORSE_CALL_CS = 0x5E04, + MIDNA_TEXT_AFTER_FOREST_TEMPLE_DONE = 0x5E10, + UNK_5E20 = 0x5E20, + FORCED_MIDNA_TEXT_AFTER_TOUCHING_FOG_IN_PALACE_OF_TWILIGHT = 0x5E40, + FORCED_MIDNA_TEXT_AFTER_LANDING_ON_THE_FLIGHT_BY_FOUL_PLATFORM = 0x5E80, + GOT_AN_APPLE_FROM_FRUIT_STAND_IN_SOUTH_CASTLE_TOWN = 0x5F02, + TALKED_TO_ZORA_BY_WATERFALL_IN_ZORAS_DOMAIN = 0x5F04, + FYER_REACTS_TO_SPECIAL_REPAIRS = 0x5F08, + WATCHED_FIRST_CANNON_CS_IN_BASEMENT = 0x5F10, + SHAD_LEAVES_SO_YOU_CAN_WARP = 0x5F20, + ANCIENT_SKYBOOK_FROM_IMPAZ = 0x5F80, + TALKED_TO_FYER_AFTER_LANAYRU_TWILIGHT = 0x6001, + ASKED_FYER_FOR_SPECIAL_REPAIRS_BEFORE_WARPING_THE_CANNON = 0x6002, + SHAD_COMES_BACK_AFTER_ALL_LETTERS_WERE_GOTTEN = 0x6004, + LAKE_HYLIA_SKY_LETTER = 0x6008, + BRIDGE_OF_ELDIN_SKY_LETTER = 0x6010, + GORGE_SKY_LETTER = 0x6020, + DESERT_SKY_LETTER = 0x6040, + FARON_SKY_LETTER = 0x6080, + BOUGHT_HYLIAN_SHIELD_AT_MALO_MART = 0x6102, + MIDNA_TEXT_AFTER_YOU_ENTERED_ELDIN_TWILIGHT = 0x6104, + TRILL_WILL_TRY_TO_KILL_YOU = 0x6110, + GOT_REEKFISH_SCENT = 0x6120, + REMOVE_MIDNA_FROM_Z = 0x6140, + TALKED_TO_LAZY_GORON = 0x6180, + AMPITHEATER_SKYLETTER = 0x6204, + STARTED_STAR_1 = 0x6208, + GOT_POE_SCENT = 0x6210, + GIRLS_IN_CASTLE_TOWN_START_CHASING_LINK = 0x6220, + TALKED_TO_COLIN_ORDON_DAY_1 = 0x6280, + HENA_BEAT_ROLLGOAL_8_8 = 0x6302 +}; + +struct goldenWolfFlags { + u8 mapMarkerFlag{}; + u16 howledAtStoneFlag{}; + u16 obtainedItemFlag{}; +}; + +goldenWolfFlags getCurrentGoldenWolfFlags(u8 roomNo); \ No newline at end of file diff --git a/mods/randomizer/src/item_ids.h b/mods/randomizer/src/item_ids.h new file mode 100644 index 0000000000..085e260878 --- /dev/null +++ b/mods/randomizer/src/item_ids.h @@ -0,0 +1,262 @@ +#pragma once + +// Randomizer item ids. Mostly the same, but we use most unused +// entries for custom portals and keys +enum { + /* 0x00 */ dItemNo_Randomizer_HEART_e, + /* 0x01 */ dItemNo_Randomizer_GREEN_RUPEE_e, + /* 0x02 */ dItemNo_Randomizer_BLUE_RUPEE_e, + /* 0x03 */ dItemNo_Randomizer_YELLOW_RUPEE_e, + /* 0x04 */ dItemNo_Randomizer_RED_RUPEE_e, + /* 0x05 */ dItemNo_Randomizer_PURPLE_RUPEE_e, + /* 0x06 */ dItemNo_Randomizer_ORANGE_RUPEE_e, + /* 0x07 */ dItemNo_Randomizer_SILVER_RUPEE_e, + /* 0x08 */ dItemNo_Randomizer_S_MAGIC_e, + /* 0x09 */ dItemNo_Randomizer_L_MAGIC_e, + /* 0x0A */ dItemNo_Randomizer_BOMB_5_e, + /* 0x0B */ dItemNo_Randomizer_BOMB_10_e, + /* 0x0C */ dItemNo_Randomizer_BOMB_20_e, + /* 0x0D */ dItemNo_Randomizer_BOMB_30_e, + /* 0x0E */ dItemNo_Randomizer_ARROW_10_e, + /* 0x0F */ dItemNo_Randomizer_ARROW_20_e, + /* 0x10 */ dItemNo_Randomizer_ARROW_30_e, + /* 0x11 */ dItemNo_Randomizer_ARROW_1_e, + /* 0x12 */ dItemNo_Randomizer_PACHINKO_SHOT_e, + /* 0x13 */ dItemNo_Randomizer_FOOLISH_ITEM_e, + /* 0x14 */ dItemNo_Randomizer_ORDON_PORTAL_e, + /* 0x15 */ dItemNo_Randomizer_SOUTH_FARON_PORTAL_e, + /* 0x16 */ dItemNo_Randomizer_WATER_BOMB_5_e, + /* 0x17 */ dItemNo_Randomizer_WATER_BOMB_10_e, + /* 0x18 */ dItemNo_Randomizer_WATER_BOMB_20_e, + /* 0x19 */ dItemNo_Randomizer_WATER_BOMB_30_e, + /* 0x1A */ dItemNo_Randomizer_BOMB_INSECT_5_e, + /* 0x1B */ dItemNo_Randomizer_BOMB_INSECT_10_e, + /* 0x1C */ dItemNo_Randomizer_BOMB_INSECT_20_e, + /* 0x1D */ dItemNo_Randomizer_BOMB_INSECT_30_e, + /* 0x1E */ dItemNo_Randomizer_RECOVERY_FAILY_e, + /* 0x1F */ dItemNo_Randomizer_TRIPLE_HEART_e, + /* 0x20 */ dItemNo_Randomizer_SMALL_KEY_e, + /* 0x21 */ dItemNo_Randomizer_KAKERA_HEART_e, + /* 0x22 */ dItemNo_Randomizer_UTAWA_HEART_e, + /* 0x23 */ dItemNo_Randomizer_MAP_e, + /* 0x24 */ dItemNo_Randomizer_COMPUS_e, + /* 0x25 */ dItemNo_Randomizer_DUNGEON_EXIT_e, + /* 0x26 */ dItemNo_Randomizer_BOSS_KEY_e, + /* 0x27 */ dItemNo_Randomizer_DUNGEON_BACK_e, + /* 0x28 */ dItemNo_Randomizer_SWORD_e, + /* 0x29 */ dItemNo_Randomizer_MASTER_SWORD_e, + /* 0x2A */ dItemNo_Randomizer_WOOD_SHIELD_e, + /* 0x2B */ dItemNo_Randomizer_SHIELD_e, + /* 0x2C */ dItemNo_Randomizer_HYLIA_SHIELD_e, + /* 0x2D */ dItemNo_Randomizer_TKS_LETTER_e, + /* 0x2E */ dItemNo_Randomizer_WEAR_CASUAL_e, + /* 0x2F */ dItemNo_Randomizer_WEAR_KOKIRI_e, + /* 0x30 */ dItemNo_Randomizer_ARMOR_e, + /* 0x31 */ dItemNo_Randomizer_WEAR_ZORA_e, + /* 0x32 */ dItemNo_Randomizer_MAGIC_LV1_e, + /* 0x33 */ dItemNo_Randomizer_DUNGEON_EXIT_2_e, + /* 0x34 */ dItemNo_Randomizer_WALLET_LV1_e, + /* 0x35 */ dItemNo_Randomizer_WALLET_LV2_e, + /* 0x36 */ dItemNo_Randomizer_WALLET_LV3_e, + /* 0x37 */ dItemNo_Randomizer_NOENTRY_55_e, + /* 0x38 */ dItemNo_Randomizer_NOENTRY_56_e, + /* 0x39 */ dItemNo_Randomizer_UPPER_ZORAS_RIVER_PORTAL_e, + /* 0x3A */ dItemNo_Randomizer_CASTLE_TOWN_PORTAL_e, + /* 0x3B */ dItemNo_Randomizer_GERUDO_DESERT_PORTAL_e, + /* 0x3C */ dItemNo_Randomizer_NORTH_FARON_PORTAL_e, + /* 0x3D */ dItemNo_Randomizer_ZORAS_JEWEL_e, + /* 0x3E */ dItemNo_Randomizer_HAWK_EYE_e, + /* 0x3F */ dItemNo_Randomizer_WOOD_STICK_e, + /* 0x40 */ dItemNo_Randomizer_BOOMERANG_e, + /* 0x41 */ dItemNo_Randomizer_SPINNER_e, + /* 0x42 */ dItemNo_Randomizer_IRONBALL_e, + /* 0x43 */ dItemNo_Randomizer_BOW_e, + /* 0x44 */ dItemNo_Randomizer_HOOKSHOT_e, + /* 0x45 */ dItemNo_Randomizer_HVY_BOOTS_e, + /* 0x46 */ dItemNo_Randomizer_COPY_ROD_e, + /* 0x47 */ dItemNo_Randomizer_W_HOOKSHOT_e, + /* 0x48 */ dItemNo_Randomizer_KANTERA_e, + /* 0x49 */ dItemNo_Randomizer_LIGHT_SWORD_e, + /* 0x4A */ dItemNo_Randomizer_FISHING_ROD_1_e, + /* 0x4B */ dItemNo_Randomizer_PACHINKO_e, + /* 0x4C */ dItemNo_Randomizer_COPY_ROD_2_e, + /* 0x4D */ dItemNo_Randomizer_KAKARIKO_GORGE_PORTAL_e, + /* 0x4E */ dItemNo_Randomizer_KAKARIKO_VILLAGE_PORTAL_e, + /* 0x4F */ dItemNo_Randomizer_BOMB_BAG_LV2_e, + /* 0x50 */ dItemNo_Randomizer_BOMB_BAG_LV1_e, + /* 0x51 */ dItemNo_Randomizer_BOMB_IN_BAG_e, + /* 0x52 */ dItemNo_Randomizer_DEATH_MOUNTAIN_PORTAL_e, + /* 0x53 */ dItemNo_Randomizer_LIGHT_ARROW_e, + /* 0x54 */ dItemNo_Randomizer_ARROW_LV1_e, + /* 0x55 */ dItemNo_Randomizer_ARROW_LV2_e, + /* 0x56 */ dItemNo_Randomizer_ARROW_LV3_e, + /* 0x57 */ dItemNo_Randomizer_ZORAS_DOMAIN_PORTAL_e, + /* 0x58 */ dItemNo_Randomizer_LURE_ROD_e, + /* 0x59 */ dItemNo_Randomizer_BOMB_ARROW_e, + /* 0x5A */ dItemNo_Randomizer_HAWK_ARROW_e, + /* 0x5B */ dItemNo_Randomizer_BEE_ROD_e, + /* 0x5C */ dItemNo_Randomizer_JEWEL_ROD_e, + /* 0x5D */ dItemNo_Randomizer_WORM_ROD_e, + /* 0x5E */ dItemNo_Randomizer_JEWEL_BEE_ROD_e, + /* 0x5F */ dItemNo_Randomizer_JEWEL_WORM_ROD_e, + /* 0x60 */ dItemNo_Randomizer_EMPTY_BOTTLE_e, + /* 0x61 */ dItemNo_Randomizer_RED_BOTTLE_e, + /* 0x62 */ dItemNo_Randomizer_GREEN_BOTTLE_e, + /* 0x63 */ dItemNo_Randomizer_BLUE_BOTTLE_e, + /* 0x64 */ dItemNo_Randomizer_MILK_BOTTLE_e, + /* 0x65 */ dItemNo_Randomizer_HALF_MILK_BOTTLE_e, + /* 0x66 */ dItemNo_Randomizer_OIL_BOTTLE_e, + /* 0x67 */ dItemNo_Randomizer_WATER_BOTTLE_e, + /* 0x68 */ dItemNo_Randomizer_OIL_BOTTLE_2_e, + /* 0x69 */ dItemNo_Randomizer_RED_BOTTLE_2_e, + /* 0x6A */ dItemNo_Randomizer_UGLY_SOUP_e, + /* 0x6B */ dItemNo_Randomizer_HOT_SPRING_e, + /* 0x6C */ dItemNo_Randomizer_FAIRY_e, + /* 0x6D */ dItemNo_Randomizer_HOT_SPRING_2_e, + /* 0x6E */ dItemNo_Randomizer_OIL2_e, + /* 0x6F */ dItemNo_Randomizer_OIL_e, + /* 0x70 */ dItemNo_Randomizer_NORMAL_BOMB_e, + /* 0x71 */ dItemNo_Randomizer_WATER_BOMB_e, + /* 0x72 */ dItemNo_Randomizer_POKE_BOMB_e, + /* 0x73 */ dItemNo_Randomizer_FAIRY_DROP_e, + /* 0x74 */ dItemNo_Randomizer_WORM_e, + /* 0x75 */ dItemNo_Randomizer_DROP_BOTTLE_e, + /* 0x76 */ dItemNo_Randomizer_BEE_CHILD_e, + /* 0x77 */ dItemNo_Randomizer_CHUCHU_RARE_e, + /* 0x78 */ dItemNo_Randomizer_CHUCHU_RED_e, + /* 0x79 */ dItemNo_Randomizer_CHUCHU_BLUE_e, + /* 0x7A */ dItemNo_Randomizer_CHUCHU_GREEN_e, + /* 0x7B */ dItemNo_Randomizer_CHUCHU_YELLOW_e, + /* 0x7C */ dItemNo_Randomizer_CHUCHU_PURPLE_e, + /* 0x7D */ dItemNo_Randomizer_LV1_SOUP_e, + /* 0x7E */ dItemNo_Randomizer_LV2_SOUP_e, + /* 0x7F */ dItemNo_Randomizer_LV3_SOUP_e, + /* 0x80 */ dItemNo_Randomizer_LETTER_e, + /* 0x81 */ dItemNo_Randomizer_BILL_e, + /* 0x82 */ dItemNo_Randomizer_WOOD_STATUE_e, + /* 0x83 */ dItemNo_Randomizer_IRIAS_PENDANT_e, + /* 0x84 */ dItemNo_Randomizer_HORSE_FLUTE_e, + /* 0x85 */ dItemNo_Randomizer_FOREST_SMALL_KEY_e, + /* 0x86 */ dItemNo_Randomizer_MINES_SMALL_KEY_e, + /* 0x87 */ dItemNo_Randomizer_LAKEBED_SMALL_KEY_e, + /* 0x88 */ dItemNo_Randomizer_ARBITERS_SMALL_KEY_e, + /* 0x89 */ dItemNo_Randomizer_SNOWPEAK_SMALL_KEY_e, + /* 0x8A */ dItemNo_Randomizer_TEMPLE_OF_TIME_SMALL_KEY_e, + /* 0x8B */ dItemNo_Randomizer_CITY_SMALL_KEY_e, + /* 0x8C */ dItemNo_Randomizer_PALACE_SMALL_KEY_e, + /* 0x8D */ dItemNo_Randomizer_HYRULE_SMALL_KEY_e, + /* 0x8E */ dItemNo_Randomizer_CAMP_SMALL_KEY_e, + /* 0x8F */ dItemNo_Randomizer_LAKE_HYLIA_PORTAL_e, + /* 0x90 */ dItemNo_Randomizer_RAFRELS_MEMO_e, + /* 0x91 */ dItemNo_Randomizer_ASHS_SCRIBBLING_e, + /* 0x92 */ dItemNo_Randomizer_FOREST_BOSS_KEY_e, + /* 0x93 */ dItemNo_Randomizer_LAKEBED_BOSS_KEY_e, + /* 0x94 */ dItemNo_Randomizer_ARBITERS_BOSS_KEY_e, + /* 0x95 */ dItemNo_Randomizer_TEMPLE_OF_TIME_BOSS_KEY_e, + /* 0x96 */ dItemNo_Randomizer_CITY_BOSS_KEY_e, + /* 0x97 */ dItemNo_Randomizer_PALACE_BOSS_KEY_e, + /* 0x98 */ dItemNo_Randomizer_HYRULE_BOSS_KEY_e, + /* 0x99 */ dItemNo_Randomizer_FOREST_COMPASS_e, + /* 0x9A */ dItemNo_Randomizer_MINES_COMPASS_e, + /* 0x9B */ dItemNo_Randomizer_LAKEBED_COMPASS_e, + /* 0x9C */ dItemNo_Randomizer_CHUCHU_YELLOW2_e, + /* 0x9D */ dItemNo_Randomizer_OIL_BOTTLE3_e, + /* 0x9E */ dItemNo_Randomizer_SHOP_BEE_CHILD_e, + /* 0x9F */ dItemNo_Randomizer_CHUCHU_BLACK_e, + /* 0xA0 */ dItemNo_Randomizer_LIGHT_DROP_e, + /* 0xA1 */ dItemNo_Randomizer_DROP_CONTAINER_e, + /* 0xA2 */ dItemNo_Randomizer_DROP_CONTAINER02_e, + /* 0xA3 */ dItemNo_Randomizer_DROP_CONTAINER03_e, + /* 0xA4 */ dItemNo_Randomizer_FILLED_CONTAINER_e, + /* 0xA5 */ dItemNo_Randomizer_MIRROR_PIECE_2_e, + /* 0xA6 */ dItemNo_Randomizer_MIRROR_PIECE_3_e, + /* 0xA7 */ dItemNo_Randomizer_MIRROR_PIECE_4_e, + /* 0xA8 */ dItemNo_Randomizer_ARBITERS_COMPASS_e, + /* 0xA9 */ dItemNo_Randomizer_SNOWPEAK_COMPASS_e, + /* 0xAA */ dItemNo_Randomizer_TEMPLE_OF_TIME_COMPASS_e, + /* 0xAB */ dItemNo_Randomizer_CITY_COMPASS_e, + /* 0xAC */ dItemNo_Randomizer_PALACE_COMPASS_e, + /* 0xAD */ dItemNo_Randomizer_HYRULE_COMPASS_e, + /* 0xAE */ dItemNo_Randomizer_MIRROR_CHAMBER_PORTAL_e, + /* 0xAF */ dItemNo_Randomizer_SNOWPEAK_PORTAL_e, + /* 0xB0 */ dItemNo_Randomizer_SMELL_YELIA_POUCH_e, + /* 0xB1 */ dItemNo_Randomizer_SMELL_PUMPKIN_e, + /* 0xB2 */ dItemNo_Randomizer_SMELL_POH_e, + /* 0xB3 */ dItemNo_Randomizer_SMELL_FISH_e, + /* 0xB4 */ dItemNo_Randomizer_SMELL_CHILDREN_e, + /* 0xB5 */ dItemNo_Randomizer_SMELL_MEDICINE_e, + /* 0xB6 */ dItemNo_Randomizer_FOREST_MAP_e, + /* 0xB7 */ dItemNo_Randomizer_MINES_MAP_e, + /* 0xB8 */ dItemNo_Randomizer_LAKEBED_MAP_e, + /* 0xB9 */ dItemNo_Randomizer_ARBITERS_MAP_e, + /* 0xBA */ dItemNo_Randomizer_SNOWPEAK_MAP_e, + /* 0xBB */ dItemNo_Randomizer_TEMPLE_OF_TIME_MAP_e, + /* 0xBC */ dItemNo_Randomizer_CITY_MAP_e, + /* 0xBD */ dItemNo_Randomizer_PALACE_MAP_e, + /* 0xBE */ dItemNo_Randomizer_HYRULE_MAP_e, + /* 0xBF */ dItemNo_Randomizer_SACRED_GROVE_PORTAL_e, + /* 0xC0 */ dItemNo_Randomizer_M_BEETLE_e, + /* 0xC1 */ dItemNo_Randomizer_F_BEETLE_e, + /* 0xC2 */ dItemNo_Randomizer_M_BUTTERFLY_e, + /* 0xC3 */ dItemNo_Randomizer_F_BUTTERFLY_e, + /* 0xC4 */ dItemNo_Randomizer_M_STAG_BEETLE_e, + /* 0xC5 */ dItemNo_Randomizer_F_STAG_BEETLE_e, + /* 0xC6 */ dItemNo_Randomizer_M_GRASSHOPPER_e, + /* 0xC7 */ dItemNo_Randomizer_F_GRASSHOPPER_e, + /* 0xC8 */ dItemNo_Randomizer_M_NANAFUSHI_e, + /* 0xC9 */ dItemNo_Randomizer_F_NANAFUSHI_e, + /* 0xCA */ dItemNo_Randomizer_M_DANGOMUSHI_e, + /* 0xCB */ dItemNo_Randomizer_F_DANGOMUSHI_e, + /* 0xCC */ dItemNo_Randomizer_M_MANTIS_e, + /* 0xCD */ dItemNo_Randomizer_F_MANTIS_e, + /* 0xCE */ dItemNo_Randomizer_M_LADYBUG_e, + /* 0xCF */ dItemNo_Randomizer_F_LADYBUG_e, + /* 0xD0 */ dItemNo_Randomizer_M_SNAIL_e, + /* 0xD1 */ dItemNo_Randomizer_F_SNAIL_e, + /* 0xD2 */ dItemNo_Randomizer_M_DRAGONFLY_e, + /* 0xD3 */ dItemNo_Randomizer_F_DRAGONFLY_e, + /* 0xD4 */ dItemNo_Randomizer_M_ANT_e, + /* 0xD5 */ dItemNo_Randomizer_F_ANT_e, + /* 0xD6 */ dItemNo_Randomizer_M_MAYFLY_e, + /* 0xD7 */ dItemNo_Randomizer_F_MAYFLY_e, + /* 0xD8 */ dItemNo_Randomizer_FUSED_SHADOW_1_e, + /* 0xD9 */ dItemNo_Randomizer_FUSED_SHADOW_2_e, + /* 0xDA */ dItemNo_Randomizer_FUSED_SHADOW_3_e, + /* 0xDB */ dItemNo_Randomizer_MIRROR_PIECE_1_e, + /* 0xDC */ dItemNo_Randomizer_NOENTRY_220_e, + /* 0xDD */ dItemNo_Randomizer_NOENTRY_221_e, + /* 0xDE */ dItemNo_Randomizer_NOENTRY_222_e, + /* 0xDF */ dItemNo_Randomizer_NOENTRY_223_e, + /* 0xE0 */ dItemNo_Randomizer_POU_SPIRIT_e, + /* 0xE1 */ dItemNo_Randomizer_ENDING_BLOW_e, + /* 0xE2 */ dItemNo_Randomizer_SHIELD_ATTACK_e, + /* 0xE3 */ dItemNo_Randomizer_BACK_SLICE_e, + /* 0xE4 */ dItemNo_Randomizer_HELM_SPLITTER_e, + /* 0xE5 */ dItemNo_Randomizer_MORTAL_DRAW_e, + /* 0xE6 */ dItemNo_Randomizer_JUMP_STRIKE_e, + /* 0xE7 */ dItemNo_Randomizer_GREAT_SPIN_e, + /* 0xE8 */ dItemNo_Randomizer_ELDIN_BRIDGE_PORTAL_e, + /* 0xE9 */ dItemNo_Randomizer_ANCIENT_DOCUMENT_e, + /* 0xEA */ dItemNo_Randomizer_AIR_LETTER_e, + /* 0xEB */ dItemNo_Randomizer_ANCIENT_DOCUMENT2_e, + /* 0xEC */ dItemNo_Randomizer_LV7_DUNGEON_EXIT_e, + /* 0xED */ dItemNo_Randomizer_LINKS_SAVINGS_e, + /* 0xEE */ dItemNo_Randomizer_SMALL_KEY2_e, + /* 0xEF */ dItemNo_Randomizer_POU_FIRE1_e, + /* 0xF0 */ dItemNo_Randomizer_POU_FIRE2_e, + /* 0xF1 */ dItemNo_Randomizer_POU_FIRE3_e, + /* 0xF2 */ dItemNo_Randomizer_POU_FIRE4_e, + /* 0xF3 */ dItemNo_Randomizer_BOSSRIDER_KEY_e, + /* 0xF4 */ dItemNo_Randomizer_TOMATO_PUREE_e, + /* 0xF5 */ dItemNo_Randomizer_TASTE_e, + /* 0xF6 */ dItemNo_Randomizer_LV5_BOSS_KEY_e, + /* 0xF7 */ dItemNo_Randomizer_SURFBOARD_e, + /* 0xF8 */ dItemNo_Randomizer_KANTERA2_e, + /* 0xF9 */ dItemNo_Randomizer_L2_KEY_PIECES1_e, + /* 0xFA */ dItemNo_Randomizer_L2_KEY_PIECES2_e, + /* 0xFB */ dItemNo_Randomizer_L2_KEY_PIECES3_e, + /* 0xFC */ dItemNo_Randomizer_KEY_OF_CARAVAN_e, + /* 0xFD */ dItemNo_Randomizer_LV2_BOSS_KEY_e, + /* 0xFE */ dItemNo_Randomizer_KEY_OF_FILONE_e, + /* 0xFF */ dItemNo_Randomizer_NONE_e, +}; \ No newline at end of file diff --git a/mods/randomizer/src/messages.cpp b/mods/randomizer/src/messages.cpp new file mode 100644 index 0000000000..ce994468d6 --- /dev/null +++ b/mods/randomizer/src/messages.cpp @@ -0,0 +1,133 @@ +#include "messages.hpp" + +#include "JSystem/JMessage/control.h" +#include "d/d_msg_class.h" +#include "d/d_com_inf_game.h" +#include "randomizer_context.hpp" +#include "custom_flow_ids.hpp" +#include "utilities.h" + +#include + +static JMSMesgEntry_c defaultJMSMesgEntry{ + .string_offset = 0, + .message_id = 0, + .event_label_id = 0, + .se_speaker = 0x24, + .fuki_kind = 0x00, + .output_type = 0x00, + .fuki_pos_type = 0x00, + .unk_0xc = 0xFF, + .unk_0xd = 0x00, + .se_mood = 0x00, + .camera_id = 0x00, + .base_anm_id = 0x02, + .face_anm_id = 0x03, + .unk_0x12 = 0x0400, +}; + +// Format certain messages that need to have dynamic info in them +char* GetFormatedTextOverride(u32 key, std::string& text) { + // Store formatted message in static buffer so it never goes away. + // This is fine as long as we only ever need to format messages + // for textboxes, but will cause issues if we need to use it for + // other UI elements + static std::array buf; + u32 value{}; + char* outIt; + // For item counts, execItemGet hasn't run yet, so add one to the count + switch (key) { + case (0 << 16) | 325: // Group 0, id 325 + // Poe Soul get item text + value = dComIfGs_getPohSpiritNum() + 1; + outIt = fmt::vformat_to(buf.data(), text, fmt::make_format_args(value)); + break; + case (0 << 16) | 335: // Group 0, id 335 + // Sky book characters get item text + value = getAncientDocumentNum() + 1; + outIt = fmt::vformat_to(buf.data(), text, fmt::make_format_args(value)); + break; + default: + // No override, return original text + return text.data(); + } + + // Null-terminate + size_t len = std::distance(buf.data(), outIt); + buf[len] = '\0'; + + // Return overriden text + return buf.data(); +} + +u8 getLanguageForOverride() { + u8 language = randomizer::Text::ENGLISH; + + // TODO: add service or something to check game language + /*if (dusk::version::isRegionPal()) { + language = dComIfGs_getPalLanguage(); + }*//* else if (dusk::version::isRegionJpn()) { + language = randomizer::Text::JAPANESE; + }*/ + + return language; +} + +void HandleTextOverrides(JMessage::TControl* control, JMessage::TProcessor const* pProcessor, int groupID, int index) { + if (randomizer_IsActive()) { + // Get the entry for this message + auto entry = static_cast(pProcessor->getMessageEntry_messageCode(groupID, index)); + if (!entry) { + return; + } + + // If the message id is >= 5000 then it's part of the stage file's message group + // Otherwise it's part of group 0 + auto msgId = entry->message_id.host(); + u16 group = 0; + if (msgId >= 5000) { + group = dComIfGp_getStageStagInfo()->mMsgGroup; + } + + u32 key = (group << 16) | msgId; + auto& textOverrides = randomizer_GetContext().mTextOverrides; + u8 language = getLanguageForOverride(); + if (textOverrides.at(language).contains(key)) { + control->pMessageText_begin_ = GetFormatedTextOverride(key, textOverrides[language][key]); + } + } +} + +bool HandleCustomText(JMessage::TControl* control, u16 msgId) { + if (randomizer_IsActive()) { + u32 key = (CUSTOM_BMG_GROUP << 16) | msgId; + auto& textOverrides = randomizer_GetContext().mTextOverrides; + u8 language = getLanguageForOverride(); + if (textOverrides.at(language).contains(key)) { + control->pMessageText_begin_ = GetFormatedTextOverride(key, textOverrides[language][key]); + + // Get the attributes for this text-box if they were specified + auto& attributeOverrides = randomizer_GetContext().mAttributeOverrides; + if (attributeOverrides.contains(key)) { + control->pEntry_ = reinterpret_cast(&attributeOverrides[key]); + // Otherwise, use the default entry + } else { + defaultJMSMesgEntry.message_id = msgId; + control->pEntry_ = &defaultJMSMesgEntry; + } + return true; + } + } + return false; +} + +// Used in special cases +char* GetTextOverride(s16 groupID, u32 messageId) { + u32 key = (groupID << 16) | messageId; + auto& textOverrides = randomizer_GetContext().mTextOverrides; + u8 language = getLanguageForOverride(); + if (textOverrides.at(language).contains(key)) { + return GetFormatedTextOverride(key, textOverrides[language][key]); + } + return NULL; +} diff --git a/mods/randomizer/src/messages.hpp b/mods/randomizer/src/messages.hpp new file mode 100644 index 0000000000..17bb414802 --- /dev/null +++ b/mods/randomizer/src/messages.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +// Forward declaration +namespace JMessage { +struct TProcessor; +struct TControl; +} + +void HandleTextOverrides(JMessage::TControl* control, JMessage::TProcessor const* pProcessor, int groupID, int index); + +bool HandleCustomText(JMessage::TControl* control, u16 msgId); + +char* GetTextOverride(s16 groupID, u32 messageId); \ No newline at end of file diff --git a/mods/randomizer/src/mod.cpp b/mods/randomizer/src/mod.cpp new file mode 100644 index 0000000000..60a37daedf --- /dev/null +++ b/mods/randomizer/src/mod.cpp @@ -0,0 +1,42 @@ +#include "mods/service.hpp" +#include "mods/svc/log.h" + +#include "session.hpp" + +DEFINE_MOD(); +IMPORT_SERVICE(HostService, svc_host); +IMPORT_SERVICE(LogService, svc_log); +IMPORT_SERVICE(HookService, svc_hook); +IMPORT_SERVICE(UiService, svc_ui); +IMPORT_SERVICE(ResourceService, svc_res); +IMPORT_SERVICE(ConfigService, svc_config); + +extern "C" { + +MOD_EXPORT ModResult mod_initialize(ModError* error) { + ModResult result = randomizer::session::initialize({ + mod_ctx, + svc_host, + svc_log, + svc_hook, + svc_ui, + svc_res, + svc_config + }); + if (result != MOD_OK) { + return mods::set_error(error, result, "failed to initialize session"); + } + + svc_log->info(mod_ctx, "randomizer initialized"); + return MOD_OK; +} + +MOD_EXPORT ModResult mod_update(ModError*) { + return MOD_OK; +} + +MOD_EXPORT ModResult mod_shutdown(ModError*) { + svc_log->info(mod_ctx, "randomizer unloaded"); + return MOD_OK; +} +} diff --git a/mods/randomizer/src/paths.cpp b/mods/randomizer/src/paths.cpp new file mode 100644 index 0000000000..b418557998 --- /dev/null +++ b/mods/randomizer/src/paths.cpp @@ -0,0 +1,24 @@ +#include "paths.hpp" + +#include "session.hpp" + +namespace randomizer::paths { + +std::filesystem::path GetRandomizerPath() { + // TODO: need a more permanent directory than this + return session::svc_mng.host->mod_dir(session::svc_mng.mod_ctx); +} + +std::filesystem::path GetRandomizerSettingsPath() { + return GetRandomizerPath() / "settings.yaml"; +} + +std::filesystem::path GetRandomizerPreferencesPath() { + return GetRandomizerPath() / "preferences.yaml"; +} + +std::filesystem::path GetRandomizerSeedsPath() { + return GetRandomizerPath() / "seeds"; +} + +} // namespace randomizer::paths diff --git a/mods/randomizer/src/paths.hpp b/mods/randomizer/src/paths.hpp new file mode 100644 index 0000000000..3b7e49c86d --- /dev/null +++ b/mods/randomizer/src/paths.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace randomizer::paths { + +// Root of the randomizer's writable data (settings, preferences, generated seeds), +// under the host's configured data path: /randomizer/. +std::filesystem::path GetRandomizerPath(); +std::filesystem::path GetRandomizerSettingsPath(); +std::filesystem::path GetRandomizerPreferencesPath(); +std::filesystem::path GetRandomizerSeedsPath(); + +} // namespace randomizer::paths diff --git a/mods/randomizer/src/randomizer_context.cpp b/mods/randomizer/src/randomizer_context.cpp new file mode 100644 index 0000000000..2267e686bd --- /dev/null +++ b/mods/randomizer/src/randomizer_context.cpp @@ -0,0 +1,1652 @@ +#include "randomizer_context.hpp" + +#include "session.hpp" +#include "paths.hpp" +#include "flags.h" +#include "tools.h" +#include "stages.h" +#include "verify_item_functions.h" +#include "item_ids.h" +#include "../generator/utility/crc32.hpp" +#include "../generator/utility/endian.hpp" +#include "../generator/utility/yaml.hpp" +#include "../generator/randomizer.hpp" +#include "../generator/utility/text.hpp" +#include "../generator/utility/string.hpp" + +#include + +#include "custom_flow_ids.hpp" +#include "d/actor/d_a_alink.h" +#include "d/d_com_inf_game.h" +#include "d/d_meter2.h" +#include "d/d_meter2_draw.h" +#include "d/d_meter2_info.h" +#include "d/d_msg_class.h" +#include "d/d_msg_flow.h" +#include "fmt/format.h" +#include "m_Do/m_Do_audio.h" + +std::optional RandomizerContext::WriteToFile() { + + std::ofstream seedData(this->GetSeedDataPath()); + if (!seedData.is_open()) { + return "Could not open seed data file"; + } + + YAML::Node out{}; + + for (const auto& [setting, option] : this->mSettings) { + out["mSettings"][setting] = option; + } + + // NOTE: When dumping u8s, they must be converted to u16s (or higher), otherwise they get dumped + // as single characters and not numbers + + out["mStartEventFlags"] = this->mStartEventFlags; + for (const auto& [region, flags] : this->mStartRegionFlags) { + const std::list u16Flags(flags.begin(), flags.end()); + out["mStartRegionFlags"][static_cast(region)] = u16Flags; + } + + const std::list u16Inventory(this->mStartingInventory.begin(), this->mStartingInventory.end()); + out["mStartingInventory"] = u16Inventory; + + const std::unordered_map u16ChestOverrides(this->mTreasureChestOverrides.begin(), this->mTreasureChestOverrides.end()); + out["mTreasureChestOverrides"] = u16ChestOverrides; + + const std::unordered_map u16PoeOverrides(this->mPoeOverrides.begin(), this->mPoeOverrides.end()); + out["mPoeOverrides"] = u16PoeOverrides; + + const std::unordered_map u16FreestandingItemOverrides(this->mFreestandingItemOverrides.begin(), this->mFreestandingItemOverrides.end()); + out["mFreestandingItemOverrides"] = u16FreestandingItemOverrides; + + const std::unordered_map u16BugRewardOverrides(this->mBugRewardOverrides.begin(), this->mBugRewardOverrides.end()); + out["mBugRewardOverrides"] = u16BugRewardOverrides; + + const std::unordered_map u16SkyCharacterOverrides(this->mSkyCharacterOverrides.begin(), this->mSkyCharacterOverrides.end()); + out["mSkyCharacterOverrides"] = u16SkyCharacterOverrides; + + const std::unordered_map u16GoldenWolfOverrides(this->mGoldenWolfOverrides.begin(), this->mGoldenWolfOverrides.end()); + out["mGoldenWolfOverrides"] = u16GoldenWolfOverrides; + + const std::unordered_map u16ShopOverrides(this->mShopOverrides.begin(), this->mShopOverrides.end()); + out["mShopOverrides"] = u16ShopOverrides; + + out["mTwilitInsectOverrides"] = mTwilitInsectOverrides; + + for (const auto& [key, data] : this->mFlowItemMessageOverrides) { + auto node = out["mFlowItemMessageOverrides"][key]; + node["itemId"] = data.itemId; + node["stage"] = data.stage; + node["flag"] = data.flag; + } + + for (const auto& [name, data] : this->mItemLocations) { + auto node = out["mItemLocations"][name]; + node["itemId"] = data.itemId; + node["stage"] = data.stage; + node["flag"] = data.flag; + } + + out["mStartHour"] = static_cast(this->mStartHour); + out["mMapBits"] = static_cast(this->mMapBits); + + for (const auto& [stageRoomLayer, actorPatches] : this->mObjectPatches) { + for (const auto& [actorCRC, actorPatch] : actorPatches) { + out["mObjectPatches"][stageRoomLayer][actorCRC] = ContainerToHexString(actorPatch); + } + } + + for (const auto& [stageRoomLayer, newActors] : this->mObjectAdditions) { + for (const auto& actor : newActors) { + out["mObjectAdditions"][stageRoomLayer].push_back(ContainerToHexString(actor)); + } + } + + + out["mFlowPatches"] = this->mFlowPatches; + + for (const auto& [key, branchOverrides]: this->mFlowPatchesBranchOverrides) { + for (auto override : branchOverrides) { + out["mFlowPatchesBranchOverrides"][key].push_back(override); + } + } + + // Dump text overrides as binary to avoid losing intentional null characters + YAML::Emitter textData; + textData << YAML::BeginMap; + textData << YAML::Key << "mTextOverrides"; + textData << YAML::BeginMap; + for (auto language : randomizer::supportedLanguages) { + auto languageStr = randomizer::languageToString(language); + textData << YAML::Key << languageStr; + textData << YAML::BeginMap; + for (const auto& [key, text] : this->mTextOverrides[language]) { + textData << YAML::Key << key; + textData << YAML::Value << YAML::Binary(reinterpret_cast(text.data()), text.size()); + } + textData << YAML::EndMap; + } + textData << YAML::EndMap; + textData << YAML::EndMap; + + for (const auto& [key, override] : mAttributeOverrides) { + out["mAttributeOverrides"][key] = ContainerToHexString(override); + } + + for (const auto& [key, override] : mEntranceOverrides) { + out["mEntranceOverrides"][key] = std::bit_cast(override); + } + + for (const auto& [key, override] : mReturnToPlaceOverrides) { + out["mReturnToPlaceOverrides"][key] = std::bit_cast(override); + } + + seedData << YAML::Dump(out); + seedData << '\n' << textData.c_str(); + seedData.close(); + + return std::nullopt; +} + +std::optional RandomizerContext::LoadFromHash(const std::string& hash) { + this->mHash = hash; + + if (!std::filesystem::exists(this->GetSeedDataPath())) { + randomizer::session::LogError(fmt::format("Failed to load Hash: {}", hash).c_str()); + mHash.clear(); + return std::nullopt; + } + + auto in = LoadYAML(this->GetSeedDataPath()); + + // Necessary settings + for (const auto& settingNode : in["mSettings"] ) { + const auto& setting = settingNode.first.as(); + const auto& option = settingNode.second.as(); + this->mSettings[setting] = option; + } + + // Event flags + for (const auto& flag : in["mStartEventFlags"]) { + this->mStartEventFlags.push_back(flag.as()); + } + // Region Flags + for (const auto& regionNode : in["mStartRegionFlags"]) { + const auto& regionId = regionNode.first.as(); + for (const auto& flag : regionNode.second) { + this->mStartRegionFlags[regionId].push_back(flag.as()); + } + } + + // Starting inventory + for (const auto& itemId : in["mStartingInventory"]) { + this->mStartingInventory.push_back(itemId.as()); + } + + // Chest overrides + for (const auto& chestNode : in["mTreasureChestOverrides"]) { + u16 key = chestNode.first.as(); + u8 itemId = chestNode.second.as(); + this->mTreasureChestOverrides[key] = itemId; + } + + // Poe Overrides + for (const auto& poeNode : in["mPoeOverrides"]) { + u16 key = poeNode.first.as(); + u8 itemId = poeNode.second.as(); + this->mPoeOverrides[key] = itemId; + } + + // Freestanding overrides + for (const auto& itemNode : in["mFreestandingItemOverrides"]) { + u16 key = itemNode.first.as(); + u8 itemId = itemNode.second.as(); + this->mFreestandingItemOverrides[key] = itemId; + } + + // Bug Rewards + for (const auto& bugNode : in["mBugRewardOverrides"]) { + u8 bugItemId = bugNode.first.as(); + u8 itemId = bugNode.second.as(); + this->mBugRewardOverrides[bugItemId] = itemId; + } + + // Sky Characters + for (const auto& skyCharacterNode : in["mSkyCharacterOverrides"]) { + u16 key = skyCharacterNode.first.as(); + u8 itemId = skyCharacterNode.second.as(); + this->mSkyCharacterOverrides[key] = itemId; + } + + // Golden Wolves + for (const auto& goldenWolfNode : in["mGoldenWolfOverrides"]) { + u16 key = goldenWolfNode.first.as(); + u8 itemId = goldenWolfNode.second.as(); + this->mGoldenWolfOverrides[key] = itemId; + } + + // Shop Items + for (const auto& shopNode : in["mShopOverrides"]) { + u16 key = shopNode.first.as(); + u8 itemId = shopNode.second.as(); + this->mShopOverrides[key] = itemId; + } + + for (const auto& twilitInsectNode : in["mTwilitInsectOverrides"]) { + u16 key = twilitInsectNode.first.as(); + u16 itemId = twilitInsectNode.second.as(); + this->mTwilitInsectOverrides[key] = itemId; + } + + // Helper function for getting the item data out of a YAML node + auto retrieveItemData = [](auto& itemData, const YAML::Node& node) { + itemData.itemId = node["itemId"].as(); + itemData.stage = node["stage"].as(); + itemData.flag = node["flag"].as(); + }; + + // FLW Override items + for (const auto& flwNode : in["mFlowItemMessageOverrides"]) { + u32 key = flwNode.first.as(); + retrieveItemData(this->mFlowItemMessageOverrides[key], flwNode.second); + } + + // Items we call by location name + for (const auto& locationNode : in["mItemLocations"]) { + const auto& locationName = locationNode.first.as(); + retrieveItemData(this->mItemLocations[locationName], locationNode.second); + } + + // Starting hour + this->mStartHour = in["mStartHour"].as(); + // Starting map bits + this->mMapBits = in["mMapBits"].as(); + + // Object Patches + for (const auto& stageRoomLayerNode: in["mObjectPatches"]) { + u32 stageRoomLayer = stageRoomLayerNode.first.as(); + for (const auto& actorPatchNode : stageRoomLayerNode.second) { + u32 actorCRC = actorPatchNode.first.as(); + this->mObjectPatches[stageRoomLayer][actorCRC] = HexToBytes(actorPatchNode.second.as()); + } + } + + // Object Additions + for (const auto& stageNode: in["mObjectAdditions"]) { + u32 stageRoomLayer = stageNode.first.as(); + for (const auto& objectData : stageNode.second) { + this->mObjectAdditions[stageRoomLayer].emplace_back(HexToBytes(objectData.as())); + } + } + + // Flow Patches + for (const auto& flowNode: in["mFlowPatches"]) { + auto key = flowNode.first.as(); + auto value = flowNode.second.as(); + this->mFlowPatches[key] = value; + } + + // Flow Patch Branch Overrides + for (const auto& flowNode : in["mFlowPatchesBranchOverrides"]) { + auto key = flowNode.first.as(); + for (const auto& branchNode : flowNode.second) { + auto override = branchNode.as(); + this->mFlowPatchesBranchOverrides[key].push_back(override); + } + } + + // Text Overrides + for (const auto& languageNode: in["mTextOverrides"]) { + const auto& languageStr = languageNode.first.as(); + auto language = randomizer::stringToLanguage(languageStr); + for (const auto& textNode : languageNode.second) { + auto key = textNode.first.as(); + auto binary = textNode.second.as(); + std::string text(reinterpret_cast(binary.data()), binary.size()); + this->mTextOverrides[language][key] = std::move(text); + } + } + + // Attribute Overrides + for (const auto& attributeNode : in["mAttributeOverrides"]) { + auto key = attributeNode.first.as(); + std::vector overrideVec = HexToBytes(attributeNode.second.as()); + std::array override{}; + std::copy(overrideVec.begin(), overrideVec.end(), override.begin()); + this->mAttributeOverrides[key] = override; + } + + // Entrance Overrides + for (const auto& entranceNode : in["mEntranceOverrides"]) { + auto key = entranceNode.first.as(); + auto override = std::bit_cast(entranceNode.second.as()); + this->mEntranceOverrides[key] = override; + } + + // Return to Place Overrides + for (const auto& entranceNode : in["mReturnToPlaceOverrides"]) { + auto key = entranceNode.first.as(); + auto override = std::bit_cast(entranceNode.second.as()); + this->mReturnToPlaceOverrides[key] = override; + } + + // TODO: setup ui service to allow pushing toasts + /*dusk::ui::push_toast(dusk::ui::Toast{ + .title = "Randomizer", + .content = fmt::format("Loaded Randomizer Seed {}", this->mHash), + .duration = std::chrono::seconds(3), + });*/ + return std::nullopt; +} + +std::filesystem::path RandomizerContext::GetSeedDataPath() const { + return ::randomizer::paths::GetRandomizerSeedsPath() / this->mHash / "seed.dat"; +} + +int RandomizerContext::SettingToEnum(const std::string& settingName) { + static const std::map nameToEnum = { + {"Hyrule Barrier Dungeons", HYRULE_BARRIER_DUNGEONS}, + {"Hyrule Barrier Requirements", HYRULE_BARRIER_REQUIREMENTS}, + {"Hyrule Barrier Fused Shadows", HYRULE_BARRIER_FUSED_SHADOWS}, + {"Hyrule Barrier Mirror Shards", HYRULE_BARRIER_MIRROR_SHARDS}, + {"Hyrule Castle Big Key Requirements", HYRULE_BIG_KEY_REQUIREMENTS}, + {"Hyrule Barrier Poe Souls", HYRULE_BARRIER_POE_SOULS}, + {"Hyrule Barrier Hearts", HYRULE_BARRIER_HEARTS}, + {"Hyrule Castle Big Key Mirror Shards", HYRULE_BIG_KEY_MIRROR_SHARDS}, + {"Hyrule Castle Big Key Fused Shadows", HYRULE_BIG_KEY_FUSED_SHADOWS}, + {"Hyrule Castle Big Key Dungeons", HYRULE_BIG_KEY_DUNGEONS}, + {"Hyrule Castle Big Key Poe Souls", HYRULE_BIG_KEY_POE_SOULS}, + {"Hyrule Castle Big Key Hearts", HYRULE_BIG_KEY_HEARTS}, + {"Palace of Twilight Requirements", PALACE_OF_TWILIGHT_REQUIREMENTS}, + {"Temple of Time Sword Requirement", TEMPLE_OF_TIME_SWORD_REQUIREMENT}, + {"Skip Minor Cutscenes", SKIP_MINOR_CUTSCENES}, + {"Skip Major Cutscenes", SKIP_MAJOR_CUTSCENES}, + {"Skip Bridge Donation", SKIP_BRIDGE_DONATION}, + {"Mirror Chamber Access", MIRROR_CHAMBER_ACCESS}, + }; + + if (nameToEnum.contains(settingName)) { + return nameToEnum.at(settingName); + } + + return -1; +} + +int RandomizerContext::OptionToEnum(const std::string& optionName) { + static const std::map nameToEnum = { + {"On", ON}, + {"Off", OFF}, + {"None", NONE}, + {"Vanilla", VANILLA}, + {"Open", OPEN}, + {"Fused Shadows", FUSED_SHADOWS}, + {"Mirror Shards", MIRROR_SHARDS}, + {"Poe Souls", POE_SOULS}, + {"Hearts", HEARTS}, + {"Dungeons", DUNGEONS}, + {"Wooden Sword", WOODEN_SWORD}, + {"Ordon Sword", ORDON_SWORD}, + {"Master Sword", MASTER_SWORD}, + {"Light Sword", LIGHT_SWORD}, + {"Closed", CLOSED}, + {"Barrier", BARRIER}, + }; + + if (nameToEnum.contains(optionName)) { + return nameToEnum.at(optionName); + } + + return -1; +} + +RandomizerState g_randomizerState; + +int RandomizerState::_create() { + mInitialized = true; + mEventItemStatus = QUEUE_EMPTY; + mHasPendingToDChange = false; + // g_customMenuRing._initialize(); + for (int i = 0; i < EVENT_ITEM_QUEUE_SIZE; i++) { + mEventItemQueue[i] = 0; + } + return 1; +} + +int RandomizerState::_delete() { + mInitialized = false; + return 1; +} + +static bool checkFoolishItemEffectReady() +{ + // Verify Link is loaded on the map. + if (!daAlink_getAlinkActorClass()) + { + return false; + } + + // Ensure Link is not in a cutscene + if (daAlink_getAlinkActorClass()->checkEventRun()) + { + return false; + } + + // Make sure Link isn't riding anything + if (daAlink_getAlinkActorClass()->checkRide()) + { + return false; + } + + // Ensure there are pointers to the mMeterClass and mpMeterDraw structs + if (!dMeter2Info_getMeterClass()) + { + return false; + } + + if (!dMeter2Info_getMeterClass()->getMeterDrawPtr()) + { + return false; + } + + // Make sure Z button isn't dimmed + if (dMeter2Info_getMeterClass()->getMeterDrawPtr()->getButtonZAlpha() != 1.f) + { + return false; + } + + switch (daAlink_getAlinkActorClass()->mProcID) + { + case daAlink_c::PROC_TALK: + case daAlink_c::PROC_WOLF_SWIM_MOVE: + case daAlink_c::PROC_SWIM_MOVE: + case daAlink_c::PROC_SWIM_WAIT: + case daAlink_c::PROC_WOLF_SWIM_WAIT: + case daAlink_c::PROC_SWIM_UP: + case daAlink_c::PROC_SWIM_DIVE: + { + return false; + } + default: + { + break; + } + } + return true; +} + +static void handleFoolishItem() { + u32 count = g_randomizerState.mFoolishItemCount; + if (count == 0) { + return; + } + + if (!checkFoolishItemEffectReady()) + { + return; + } + + // Failsafe: Make sure the count does not somehow exceed 100 + if (count > 100) { + count = 100; + } + + // Reset count + g_randomizerState.mFoolishItemCount = 0; + + /* Store the currently loaded sound wave to local variables as we will need to load them back later. + * We use this method because if we just loaded the sound waves every time the item was gotten, we'd + * eventually run out of memory so it is safer to unload everything and load it back in. */ + + auto sceneMgr = Z2GetSceneMgr(); + const u32 seWave1 = Z2AudioMgr::getInterface()->loadedSeWave_1; + const u32 seWave2 = Z2AudioMgr::getInterface()->loadedSeWave_2; + sceneMgr->eraseSeWave(seWave1); + sceneMgr->eraseSeWave(seWave2); + sceneMgr->loadSeWave(0x46); + mDoAud_seStartLevel(0x10040, nullptr, 0, 0); + sceneMgr->loadSeWave(seWave1); + sceneMgr->loadSeWave(seWave2); + + // Initiate the appropriate visual damage process + if (daAlink_getAlinkActorClass()->checkWolf()) + { + daAlink_getAlinkActorClass()->procWolfDamageInit(nullptr); + } + else + { + daAlink_getAlinkActorClass()->procDamageInit(nullptr, 0); + } + + daPy_py_c::setPlayerDamage(count, TRUE); +} + +/* + * Updates flags for Hyrule Castle Barrier, Palace of Twilight Access, + * and Hyrule Castle Big Key chest. Maybe a bit overkill to check this every frame, but + * it keeps it all in one place for now. + */ +static void updateGoalFlags() { + auto& settings = randomizer_GetContext().mSettings; + + // Hyrule Castle Barrier + if (!dComIfGs_isEventBit(BARRIER_GONE)) { + bool destroyBarrier = false; + switch (settings[RandomizerContext::HYRULE_BARRIER_REQUIREMENTS]) { + case RandomizerContext::VANILLA: + destroyBarrier = dComIfGs_isEventBit(PALACE_OF_TWILIGHT_CLEARED); + break; + case RandomizerContext::FUSED_SHADOWS: + destroyBarrier = numFusedShadows() >= settings[RandomizerContext::HYRULE_BARRIER_FUSED_SHADOWS]; + break; + case RandomizerContext::MIRROR_SHARDS: + destroyBarrier = numMirrorShards() >= settings[RandomizerContext::HYRULE_BARRIER_MIRROR_SHARDS]; + break; + case RandomizerContext::DUNGEONS: + destroyBarrier = numCompletedDungeons() >= settings[RandomizerContext::HYRULE_BARRIER_DUNGEONS]; + break; + case RandomizerContext::POE_SOULS: + destroyBarrier = dComIfGs_getPohSpiritNum() >= settings[RandomizerContext::HYRULE_BARRIER_POE_SOULS]; + break; + case RandomizerContext::HEARTS: + destroyBarrier = dComIfGs_getMaxLife() >= 5 * settings[RandomizerContext::HYRULE_BARRIER_HEARTS]; + break; + default: + break; + } + + if (destroyBarrier) { + dComIfGs_onEventBit(BARRIER_GONE); + } + } + + // Hyrule Castle Big Key Gate + if (!dComIfGs_isStageSwitch(0x18, 0x4B)) { + bool openGate = false; + switch (settings[RandomizerContext::HYRULE_BIG_KEY_REQUIREMENTS]) { + case RandomizerContext::FUSED_SHADOWS: + openGate = numFusedShadows() >= settings[RandomizerContext::HYRULE_BIG_KEY_FUSED_SHADOWS]; + break; + case RandomizerContext::MIRROR_SHARDS: + openGate = numMirrorShards() >= settings[RandomizerContext::HYRULE_BIG_KEY_MIRROR_SHARDS]; + break; + case RandomizerContext::DUNGEONS: + openGate = numCompletedDungeons() >= settings[RandomizerContext::HYRULE_BIG_KEY_DUNGEONS]; + break; + case RandomizerContext::POE_SOULS: + openGate = dComIfGs_getPohSpiritNum() >= settings[RandomizerContext::HYRULE_BIG_KEY_POE_SOULS]; + break; + case RandomizerContext::HEARTS: + openGate = dComIfGs_getMaxLife() >= 5 * settings[RandomizerContext::HYRULE_BIG_KEY_HEARTS]; + break; + default: + break; + } + + if (openGate) { + dComIfGs_onStageSwitch(0x18, 0x4B); + } + } + + // Palace of Twilight Access + if (!dComIfGs_isEventBit(FIXED_THE_MIRROR_OF_TWILIGHT)) { + bool openPalace = false; + switch (settings[RandomizerContext::PALACE_OF_TWILIGHT_REQUIREMENTS]) { + case RandomizerContext::VANILLA: + openPalace = dComIfGs_isEventBit(CITY_IN_THE_SKY_CLEARED); + break; + case RandomizerContext::FUSED_SHADOWS: + openPalace = numFusedShadows() >= 3; + break; + case RandomizerContext::MIRROR_SHARDS: + openPalace = numMirrorShards() >= 4; + break; + default: + break; + } + + if (openPalace) { + dComIfGs_onEventBit(FIXED_THE_MIRROR_OF_TWILIGHT); + } + } +} + +int RandomizerState::execute() { + if (!mInitialized) { + return 0; + } + + // Always check for and handle time of day changes + if (getTimeChange() != NO_CHANGE) { + handleTimeSpeed(); + } + + bool currentReloadingState; + // Any custom functionality that relies on Link's actor being on a stage + if (daAlink_getAlinkActorClass()) { + currentReloadingState = daAlink_getAlinkActorClass()->checkRestartRoom(); + // Handle giving item to the player at any time. + initGiveItemToPlayer(); + } + else { + currentReloadingState = true; + } + + bool prevReloadingState = getRoomReloadingState(); + if (!currentReloadingState) { + if (prevReloadingState) { + offLoad(); + } + } + setRoomReloadingState(currentReloadingState); + + if (getStageID() != Title_Screen) { + handleFoolishItem(); + } + + return 1; +} + +int RandomizerState::draw() { + return 1; +} + +void RandomizerState::handlePoeItem(u8 bitSw) +{ + u16 key = getStageID() << 8 | bitSw; + u8 item = randomizer_GetContext().mPoeOverrides[key]; + addItemToEventQueue(item); + daAlink_getAlinkActorClass()->procWolfAtnActorMoveInit(); +} + +void RandomizerState::addItemToEventQueue(u8 item) +{ + for (int i = 0; i < EVENT_ITEM_QUEUE_SIZE; i++) + { + if (mEventItemQueue[i] == 0) + { + mEventItemQueue[i] = item; + break; + } + } +} + +void RandomizerState::initGiveItemToPlayer() +{ + switch (daAlink_getAlinkActorClass()->mProcID) + { + case daAlink_c::PROC_WAIT: + case daAlink_c::PROC_TIRED_WAIT: + case daAlink_c::PROC_MOVE: + case daAlink_c::PROC_WOLF_WAIT: + case daAlink_c::PROC_WOLF_TIRED_WAIT: + case daAlink_c::PROC_WOLF_MOVE: + case daAlink_c::PROC_ATN_MOVE: + case daAlink_c::PROC_WOLF_ATN_AC_MOVE: + { + // Check if link is currently in a cutscene + if (daAlink_getAlinkActorClass()->checkEventRun()) + { + break; + } + + // Ensure that link is not currently in a message-based event. + int event_item_id = 0; + if (daAlink_getAlinkActorClass()->mMsgFlow.getEventId(&event_item_id) != 0) + { + break; + } + + u8 itemToGive = 0xFF; + + for (int i = 0; i < EVENT_ITEM_QUEUE_SIZE; i++) + { + const u8 storedItem = mEventItemQueue[i]; + + if (storedItem) + { + const u8 giveItemToPlayerStatus = getGiveItemToPlayerStatus(); + + // If we have the call to clear the queue, then we want to clear the item and break out. + if (giveItemToPlayerStatus == CLEAR_QUEUE) + { + mEventItemQueue[i] = 0; + setGiveItemToPlayerStatus(QUEUE_EMPTY); + break; + } + + // If the queue is empty and we have an item to give, update the queue state. + else if (giveItemToPlayerStatus == QUEUE_EMPTY) + { + setGiveItemToPlayerStatus(ITEM_IN_QUEUE); + } + + itemToGive = verifyProgressiveItem(storedItem); + break; + } + } + + // if there is no item to give, break out of the case. + if (itemToGive == 0xFF) + { + break; + } + + g_dComIfG_gameInfo.play.getEvent()->setGtItm(itemToGive); + + // Set the process value for getting an item to start the "get item" cutscene when next available. + daAlink_getAlinkActorClass()->mProcID = daAlink_c::PROC_GET_ITEM; + + // Get the event index for the "Get Item" event. + const s16 eventIdx = dComIfGp_getEventManager().getEventIdx((fopAc_ac_c*)daAlink_getAlinkActorClass(),"DEFAULT_GETITEM",0xFF); + + // Finally we want to modify the event stack to prioritize our custom event so that it happens next. + fopAcM_orderChangeEventId(daAlink_getAlinkActorClass(), eventIdx, 1, 0xFFFF); + } + default: + { + break; + } + } +} + +void RandomizerState::handleTimeOfDayChange() +{ + if (dComIfGp_roomControl_getTimePass()) + { + // No point in changing values if we are already changing the time. + if (getTimeChange() == NO_CHANGE) + { + if (!dKy_daynight_check()) // Day time + { + setTimeChange(CHANGE_TO_NIGHT); + } + else + { + setTimeChange(CHANGE_TO_DAY); + } + g_env_light.time_change_rate = 1.f; // Increase time speed + } + } + else + { + if (!dKy_daynight_check()) // Day time + { + dComIfGs_setTime(285.f); + } + else + { + dComIfGs_setTime(105.f); + } + + static_cast(dComIfGp_getNextStartStage())->onEnable(); + } +} + +void RandomizerState::handleTimeSpeed() +{ + + if (!dKy_daynight_check()) // Day time + { + if (getTimeChange() == CHANGE_TO_DAY) + { + g_env_light.time_change_rate = 0.012f; // Set time speed to normal + setTimeChange(NO_CHANGE); + } + } + else if (getTimeChange() == CHANGE_TO_NIGHT) + { + g_env_light.time_change_rate = 0.012f; // Set time speed to normal + setTimeChange(NO_CHANGE); + } +} + +void RandomizerState::offLoad() +{ + if ((getStageID() == City_in_the_Sky) && (dStage_roomControl_c::mStayNo == 0) && (dComIfGp_getStartStagePoint() == 3)) + { + // Fan in the main room active + dComIfGs_offSaveSwitch(0xA); + + // Main Room 1F explored + dComIfGs_offSaveSwitch(0xF); + } + + if (playerIsInRoomStage(1, allStages[Sacred_Grove])) + { + // If the portal in SG isn't active then we want to spawn the shadow beasts. + if (!dComIfGs_isSaveSwitch(0x64)) + { + dComIfGs_onSvOneZoneSwitch(0, 0xE); + } + } + + if ((getStageID() == Ordon_Ranch) && (dComIfGp_getStartStagePoint() == 1)) + { + // Clear the danBit that starts a conversation when entering the ranch so the player can do goats as needed. + dComIfGs_offSaveDunSwitch(0x0); + } + + // Check and update our goal flags + updateGoalFlags(); +} + +RandomizerContext& randomizer_GetContext() { + static RandomizerContext instance; + return instance; +} + +bool randomizer_IsActive() { + return (!playerIsOnTitleScreen() || randomizer_GetContext().mCreatingSave) && !randomizer_GetContext().mHash.empty(); +} + +std::vector HexToBytes(std::string hex) { + std::vector bytes; + // Strip "0x" if present + if (hex.substr(0, 2) == "0x") hex = hex.substr(2); + + for (size_t i = 0; i < hex.length(); i += 2) { + std::string byteString = hex.substr(i, 2); + u8 byte = static_cast(strtol(byteString.c_str(), nullptr, 16)); + bytes.push_back(byte); + } + return bytes; +} + +int randomizer_getItemAtLocation(const std::string& locationName) { + return randomizer_GetContext().mItemLocations[locationName].itemId; +} + +void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo, s16& pointNo, s8& mapLayer) { + RandomizerContext::EntranceOverride override = { + static_cast(getStageID(stageName)), roomNo, static_cast(pointNo), mapLayer + }; + + int key = std::bit_cast(override); + if (randomizer_GetContext().mEntranceOverrides.contains(key)) { + auto& newOverride = randomizer_GetContext().mEntranceOverrides[key]; + stageName = allStages[newOverride.stageId]; + pointNo = newOverride.pointNo; + roomNo = newOverride.roomNo; + mapLayer = newOverride.mapLayer; + } +} + +static void randomizer_setTempFlag(RandomizerContext::itemLocationData data) { + // If stage is 0xFF, then this is an event flag + if (data.stage == 0xFF) { + g_randomizerState.mTrackerTempEventFlag = data.flag; + } + // If it's less than 0x80 then it's a switch flag + else if (data.flag < 0x80) { + g_randomizerState.mTrackerTempSwitchFlag.stage = getStageSaveId(data.stage); + g_randomizerState.mTrackerTempSwitchFlag.flag = data.flag; + } + // Otherwise it's an item flag. Currently, any item flags that go through here are custom + // so we just set the bit directly. + else { + dComIfGs_onItem(data.flag, getStageSaveId(data.stage)); + } +} + +void randomizer_setTempFlagForLocation(const std::string& locationName) { + randomizer_setTempFlag(randomizer_GetContext().mItemLocations[locationName]); +} + +void randomizer_setTempFlagForFLWOverride(u32 key) { + randomizer_setTempFlag(randomizer_GetContext().mFlowItemMessageOverrides[key]); +} + +bool randomizer_checkTempleOfTimeRequirement() { + auto swordRequirement = randomizer_GetContext().mSettings[RandomizerContext::TEMPLE_OF_TIME_SWORD_REQUIREMENT]; + u8 roomNo = dComIfGp_getStartStageRoomNo(); + + // Don't strike the pedestal again if we've already set the flag for striking it + if (roomNo == 1 && dComIfGs_isSwitch(0x63, roomNo)) { + return false; + } + + // Make sure we have a sword in Link's hands. + auto equippedSword = dComIfGs_getSelectEquipSword(); + if (equippedSword != 0xFF) { + // Fallthrough is intentional to check each potential sword requirement below the current equipped sword + switch (equippedSword) { + case dItemNo_LIGHT_SWORD_e: + if (swordRequirement == RandomizerContext::LIGHT_SWORD) { + return true; + } + case dItemNo_MASTER_SWORD_e: + if (swordRequirement == RandomizerContext::MASTER_SWORD) { + return true; + } + case dItemNo_SWORD_e: + if (swordRequirement == RandomizerContext::ORDON_SWORD) { + return true; + } + case dItemNo_WOOD_STICK_e: + if (swordRequirement == RandomizerContext::WOODEN_SWORD) { + return true; + } + default: + return false; + } + } + + return false; +} + +bool randomizer_mirrorChamberWallShouldExist() { + auto mirrorChamberAccess = randomizer_GetContext().mSettings[RandomizerContext::MIRROR_CHAMBER_ACCESS]; + return mirrorChamberAccess == RandomizerContext::CLOSED || + (mirrorChamberAccess == RandomizerContext::BARRIER && !dComIfGs_isStageBossEnemy(0x13)); +} + +void randomizer_returnToSpawn(bool tryOverride) { + + auto& placeOverrides = randomizer_GetContext().mReturnToPlaceOverrides; + auto stageId = getStageID(); + + // If we're trying to override the default return to spawn + if (tryOverride && placeOverrides.contains(stageId)) { + auto entrance = placeOverrides[stageId]; + + // If in lakebed temple, spawn on land if shadow crystal is obtained like vanilla + if (entrance.stageId == Lakebed_Temple && dComIfGs_isEventBit(TRANSFORMING_UNLOCKED)) { + entrance.pointNo = 2; + } + + dComIfGp_setNextStage(allStages[entrance.stageId], entrance.pointNo, entrance.roomNo, entrance.mapLayer); + return; + } + + // If a player hasn't completed a twilight/MDH, we want to unset the transform flag so they aren't forced to be wolf + // unnecessarily. + for (int32_t i = 0; i < 4; i++) { + if (!dComIfGs_isDarkClearLV(i)) { + dComIfGs_offTransformLV(i); + } + } + + // If Midna's Desperate Hour is not complete, unset the flags that trigger it incase the player + // used return to spawn while MDH was active + if (!dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED)) { + dComIfGs_offStageSwitch(4, 0xE); + dComIfGs_offEventBit(MIDNAS_DESPERATE_HOUR_STARTED); + } + + // Turn the player back into Link if they are currently wolf + dComIfGs_setTransformStatus(TF_STATUS_HUMAN); + + // Return to spawn. If the spawn has been randomized, that's taken care of within the function + dComIfGp_setNextStage("F_SP103", 1, 1, -1); +} + +u8 randomizer_getRandomFoolishItemModelID() { + static constexpr auto foolishItemModels = std::to_array({ + dItemNo_Randomizer_ARMOR_e, + dItemNo_Randomizer_WOOD_STICK_e, + dItemNo_Randomizer_WOOD_SHIELD_e, + dItemNo_Randomizer_HYLIA_SHIELD_e, + dItemNo_Randomizer_MAGIC_LV1_e, + dItemNo_Randomizer_FISHING_ROD_1_e, + dItemNo_Randomizer_HAWK_EYE_e, + dItemNo_Randomizer_BOOMERANG_e, + dItemNo_Randomizer_SPINNER_e, + dItemNo_Randomizer_IRONBALL_e, + dItemNo_Randomizer_BOW_e, + dItemNo_Randomizer_COPY_ROD_e, + dItemNo_Randomizer_HOOKSHOT_e, + dItemNo_Randomizer_HVY_BOOTS_e, + dItemNo_Randomizer_PACHINKO_e, + dItemNo_Randomizer_BOMB_BAG_LV1_e, + dItemNo_Randomizer_ANCIENT_DOCUMENT_e, + }); + + u8 selectedModal = foolishItemModels[static_cast(cM_rnd() * foolishItemModels.size()) % foolishItemModels.size()]; + return verifyProgressiveItem(selectedModal); +} + +u32 getActorPatchesCurrentStageKey(u8 roomNo) { + u32 actorPatchesStageKey{}; + actorPatchesStageKey |= getStageID(dComIfGp_getStartStageName()) << 16; + actorPatchesStageKey |= roomNo << 8; + actorPatchesStageKey |= dComIfG_play_c::getLayerNo(0); + return actorPatchesStageKey; +} + +u32 getStageObjCRC32(u8* data, size_t size) { + return randomizer::utility::crc32(data, size); +} + +stage_tgsc_data_class parseObjData(const YAML::Node& objectNode) { + using namespace Utility::Endian; + // Get all the data for the actor (with endian shenanigans) + stage_tgsc_data_class object{}; + const auto& actorName = objectNode["name"].as(); + strncpy(object.name, actorName.c_str(), 8); + object.base.parameters = toPlatform(target, objectNode["parameters"].as()); + object.base.position.x = toPlatform(target, objectNode["position"]["x"].as()); + object.base.position.y = toPlatform(target, objectNode["position"]["y"].as()); + object.base.position.z = toPlatform(target, objectNode["position"]["z"].as()); + // Have to retrieve as u16 and then cast as s16 because otherwise yaml-cpp + // complains about values over 32767 not fitting in s16 + object.base.angle.x = toPlatform(target, static_cast(objectNode["angle"]["x"].as())); + object.base.angle.y = toPlatform(target, static_cast(objectNode["angle"]["y"].as())); + object.base.angle.z = toPlatform(target, static_cast(objectNode["angle"]["z"].as())); + object.base.setID = toPlatform(target, static_cast(objectNode["set id"].as())); + + if (objectNode["scale"]) { + object.scale.x = objectNode["scale"]["x"].as(); + object.scale.y = objectNode["scale"]["y"].as(); + object.scale.z = objectNode["scale"]["z"].as(); + } else { + object.scale = fopAcM_prmScale_class{0, 0, 0}; + } + + return object; +} + +void parseObjPatchData(stage_tgsc_data_class& object, const YAML::Node& patchNode) { + using namespace Utility::Endian; + if (patchNode["name"]) { + const auto& newName = patchNode["name"].as(); + strncpy(object.name, newName.c_str(), 8); + } + if (patchNode["parameters"]) { + object.base.parameters = toPlatform(target, patchNode["parameters"].as()); + } + if (auto patchPosition = patchNode["position"]) { + if (patchPosition["x"]) { + object.base.position.x = toPlatform(target, patchPosition["x"].as()); + } + if (patchPosition["y"]) { + object.base.position.y = toPlatform(target, patchPosition["y"].as()); + } + if (patchPosition["z"]) { + object.base.position.z = toPlatform(target, patchPosition["z"].as()); + } + } + if (auto patchAngle = patchNode["angle"]) { + // Have to retrieve as u16 and then cast as s16 because otherwise yaml-cpp + // complains about values over 32767 not fitting in s16 + if (patchAngle["x"]) { + object.base.angle.x = toPlatform(target, static_cast(patchAngle["x"].as())); + } + if (patchAngle["y"]) { + object.base.angle.y = toPlatform(target, static_cast(patchAngle["y"].as())); + } + if (patchAngle["z"]) { + object.base.angle.z = toPlatform(target, static_cast(patchAngle["z"].as())); + } + } + if (auto patchScale = patchNode["scale"]) { + // Have to retrieve as u16 and then cast as s16 because otherwise yaml-cpp + // complains about values over 32767 not fitting in s16 + if (patchScale["x"]) { + object.scale.x = toPlatform(target, static_cast(patchScale["x"].as())); + } + if (patchScale["y"]) { + object.scale.y = toPlatform(target, static_cast(patchScale["y"].as())); + } + if (patchScale["z"]) { + object.scale.z = toPlatform(target, static_cast(patchScale["z"].as())); + } + } +} + +RandomizerContext WriteSeedData(randomizer::logic::world::World* world) { + RandomizerContext randoData{}; + + // Settings we need to check ingame + for (const auto& [setting, info] : *randomizer::seedgen::settings::GetAllSettingsInfo()) { + if (info->NeedInGame()) { + auto settingEnum = RandomizerContext::SettingToEnum(setting); + if (settingEnum == -1) { + throw std::runtime_error("Setting \"" + setting + "\" does not have an associated enum value"); + } + auto option = world->Setting(setting).GetCurrentOption(); + int optionEnum{}; + // If this setting's options are just numbers, get the numeric value + if (info->OptionsAreNumbers()) { + optionEnum = world->Setting(setting).GetCurrentOptionAsNumber(); + } else { + optionEnum = RandomizerContext::OptionToEnum(option); + } + if (optionEnum == -1) { + throw std::runtime_error("Option \"" + option + "\" for setting \"" + setting + "\" does not have an associated enum value"); + } + randoData.mSettings[settingEnum] = optionEnum; + } + } + + // Set data for all locations + for (const auto& location : world->GetAllLocations()) { + const auto& metaData = location->GetMetadata(); + + // Chest Overrides + // Keyed by u16 of 0xFF00 (stage index) and 0x00FF (tbox id) + if (location->HasCategories("Chest")) { + for (const auto& chestNode : metaData["Chest"]) { + u8 stage = chestNode["Stage"].as(); + u8 tboxId = chestNode["Tbox Id"].as(); + u8 itemId = location->GetCurrentItem()->GetID(); + u16 key = (stage << 8) | tboxId; + randoData.mTreasureChestOverrides[key] = itemId; + } + } + + // Poe Overrides + // Keyed by u16 of 0xFF00 (stage index) and 0x00FF (collectible flag) + if (location->HasCategories("Poe")) { + for (const auto& poeNode : metaData["Poe"]) { + const auto& stage = poeNode["Stage"].as(); + const auto& flag = poeNode["Flag"].as(); + u8 itemId = location->GetCurrentItem()->GetID(); + u16 key = (stage << 8) | flag; + randoData.mPoeOverrides[key] = itemId; + } + } + + // Freestanding Overrides + // Keyed by the stage index and collectible flag of the item + if (location->HasCategories("Freestanding Item")) { + for (const auto& freestandingItemNode: metaData["Freestanding Item"]) { + u8 stage = freestandingItemNode["Stage"].as(); + u8 flag = freestandingItemNode["Flag"].as(); + u8 itemId = location->GetCurrentItem()->GetID(); + u16 key = (stage << 8) | flag; + randoData.mFreestandingItemOverrides[key] = itemId; + } + } + + // Bug Rewards + // Keyed by the item id of the original bug + if (location->HasCategories("Bug Reward")) { + for (const auto& bugRewardNode : metaData["Bug Reward"]) { + u8 bugItemId = bugRewardNode["Item Id"].as(); + u8 itemId = location->GetCurrentItem()->GetID(); + randoData.mBugRewardOverrides[bugItemId] = itemId; + } + } + + // Sky Characters + // Keyed by u16 of 0xFF00 (stage index) and 0x00FF (roomNo) + if (location->HasCategories("Sky Character")) { + for (const auto& skyCharacterNode : metaData["Sky Character"]) { + u8 stageIdx = skyCharacterNode["Stage"].as(); + u8 roomNo = skyCharacterNode["Room"].as(); + u8 itemId = location->GetCurrentItem()->GetID(); + u16 key = (stageIdx << 8) | roomNo; + randoData.mSkyCharacterOverrides[key] = itemId; + } + } + + // Golden Wolves + // Keyed by u16 of the event flag for obtaining the golden wolf item + if (location->HasCategories("Golden Wolf")) { + for (const auto& goldenWolfNode : metaData["Golden Wolf"]) { + u16 flag = goldenWolfNode["Flag"].as(); + u8 itemId = location->GetCurrentItem()->GetID(); + randoData.mGoldenWolfOverrides[flag] = itemId; + } + } + + // Shop Items + // Keyed by u16 of the stage and original shop item + if (location->HasCategories("Shop") && world->Setting("Shop Items") == "On") { + for (const auto& shopNode : metaData["Shop"]) { + u8 stage = shopNode["Stage"].as(); + u8 originalItem = shopNode["Item"].as(); + u16 key = (stage << 8) | originalItem; + randoData.mShopOverrides[key] = location->GetCurrentItem()->GetID(); + } + } + + // Twilit Insect Overrides + // Keyed by u16 of 0xFF00 (stage index) and 0x00FF (flag, which is a tbox id) + if (location->HasCategories("Twilit Insect")) { + for (const auto& twilitInsectNode : metaData["Twilit Insect"]) { + u8 stage = twilitInsectNode["Stage"].as(); + u8 tboxId = twilitInsectNode["Flag"].as(); + u16 itemId = location->GetCurrentItem()->GetID(); + u16 key = (stage << 8) | tboxId; + randoData.mTwilitInsectOverrides[key] = itemId; + } + } + + // Helper function for getting flag values + auto getNodeFlags = [](auto& itemData, const YAML::Node& metaData) { + if (metaData["Event Flag"]) { + itemData.flag = metaData["Event Flag"].as(); + } else if (metaData["Switch Flag"]) { + itemData.stage = metaData["Switch Flag"]["Stage"].as(); + itemData.flag = metaData["Switch Flag"]["Flag"].as(); + } else if (metaData["Item Flag"]) { + itemData.stage = metaData["Item Flag"]["Stage"].as(); + itemData.flag = metaData["Item Flag"]["Flag"].as(); + } + }; + + // Items that we determine the text of and then give during a FLW message + if (location->HasCategories("FLW Message")) { + for (const auto& flwMessageNode : metaData["FLW Message"]) { + u8 group = flwMessageNode["Group"].as(); + u16 messageId = flwMessageNode["Message Id"].as(); + u32 key = (group << 16) | messageId; + randoData.mFlowItemMessageOverrides[key].itemId = location->GetCurrentItem()->GetID(); + getNodeFlags(randoData.mFlowItemMessageOverrides[key], metaData); + } + } + + // Items that we lookup just by calling their location name + if (location->HasCategories("Name Lookup")) { + for (const auto& locationNameNode : metaData["Name Lookup"]) { + const auto& locationName = locationNameNode.as(); + const int itemId = location->GetCurrentItem()->GetID(); + randoData.mItemLocations[locationName].itemId = itemId; + getNodeFlags(randoData.mItemLocations[locationName], metaData); + } + } + } + + // Set starting inventory + for (const auto& item: world->GetStartingItemPool()) { + randoData.mStartingInventory.push_back(item->GetID()); + } + + // Set starting flags + auto startFlags = LOAD_EMBED_YAML(RANDO_DATA_PATH "startflags.yaml"); + // Event Flags + for (const auto& flagNode : startFlags["EventFlags"]) { + if (flagNode.IsScalar()) { + const auto& flag = flagNode.as(); + randoData.mStartEventFlags.push_back(flag); + } else if (flagNode.IsMap()) { + const auto& condition = flagNode.begin()->first.as(); + if (world->EvaluateSettingCondition(condition)) { + randomizer::session::LogDebug(fmt::format("Setting flags for {}", condition).c_str()); + for (const auto& conditionalFlag : flagNode.begin()->second) { + const auto& flag = conditionalFlag.as(); + randoData.mStartEventFlags.push_back(flag); + } + } + } + } + + // Region Flags + for (const auto& regionNode : startFlags["RegionFlags"]) { + const auto& region = regionNode.first.as(); + const auto& index = regionNode.second["Index"].as(); + const auto& flags = regionNode.second["Flags"]; + randomizer::session::LogDebug(fmt::format("Setting region flags for {}", region).c_str()); + // This seems kinda scuffed so maybe we change it later + for (const auto& flagNode : flags) { + if (flagNode.IsScalar()) { + const auto& flag = flagNode.as(); + randoData.mStartRegionFlags[index].push_back(flag); + } else if (flagNode.IsMap()) { + const auto& condition = flagNode.begin()->first.as(); + if (world->EvaluateSettingCondition(condition)) { + for (const auto& conditionalFlag : flagNode.begin()->second) { + const auto& flag = conditionalFlag.as(); + randoData.mStartRegionFlags[index].push_back(flag); + } + } + } + } + } + + if (world->Setting("Unlock Map Regions") == "On") + { + auto& bits = randoData.mMapBits; + bits = 0x20; + if (world->Setting("Snowpeak Does Not Require Reekfish Scent") == "On") {bits |= 0x40;} + if (world->Setting("Lanayru Twilight Cleared") == "On") {bits |= 0x10;} + if (world->Setting("Eldin Twilight Cleared") == "On") {bits |= 0x08;} + if (world->Setting("Faron Twilight Cleared") == "On") {bits |= 0x04;} + if (world->Setting("Skip Prologue") == "On") {bits |= 0x02;} + } + + // Set starting time of day + const auto startTimeSetting = world->Setting("Starting Time of Day"); + if (startTimeSetting == "Morning") + randoData.mStartHour = 6; + else if (startTimeSetting == "Noon") + randoData.mStartHour = 12; + else if (startTimeSetting == "Evening") + randoData.mStartHour = 18; + else if (startTimeSetting == "Night") + randoData.mStartHour = 24; + + // Actor Patches + auto actorPatches = LOAD_EMBED_YAML(RANDO_DATA_PATH "object_patches.yaml"); + for (const auto& stageNode : actorPatches) { + const auto& stageName = stageNode.first.as(); + for (const auto& roomNode : stageNode.second) { + u8 roomNo{}; + // Special value for actors always on the stage and not just one specific room + if (roomNode.first.as() == "Stage") { + roomNo = RandomizerContext::ROOM_STAGE; + } else { + roomNo = roomNode.first.as(); + } + for (const auto& objectNode : roomNode.second) { + const auto& action = objectNode["action"].as(); + + // Get all the data for the actor (with endian shenanigans) + auto object = parseObjData(objectNode); + + size_t objDataSize = RandomizerContext::TGSC_CRC_SIZE; + // If the scale of this object is all zeros, it's an ACTR + if (object.scale.x == 0 && object.scale.y == 0 && object.scale.z == 0) { + objDataSize = RandomizerContext::ACTR_CRC_SIZE; + } + + // Create unique hash based off of actor data + u32 objectCRC32 = getStageObjCRC32(reinterpret_cast(&object), objDataSize); + + // Depending on the action, store data on this actor + std::vector actorData(0); + // If we're patching this object, Then override the object with whatever parts are being patched + // and add that patch data to our actorData + if (action == "patch") { + parseObjPatchData(object, objectNode["patch"]); + actorData.resize(objDataSize); + std::memcpy(actorData.data(), &object, objDataSize); + } else if (action == "add") { + // If we're adding the object, add it's regular data to the actorData + actorData.resize(objDataSize); + std::memcpy(actorData.data(), &object, objDataSize); + } else if (action == "delete") { + // If we're deleting this actor, give it a specific size to indicate we're deleting it + actorData.resize(RandomizerContext::OBJ_DELETE_SIZE); + } else { + // Unknown action. Don't continue + throw std::runtime_error("object patch action \"" + action + "\" not recognized"); + } + + // Loop through all of our layers to apply this action to + for (const auto& layerNode : objectNode["layers"]) { + u8 layerNo = layerNode.as(); + // Create key based off of stage index, room, and layer + u32 stageRoomLayerKey{}; + stageRoomLayerKey |= getStageID(stageName.c_str()) << 16; + stageRoomLayerKey |= roomNo << 8; + stageRoomLayerKey |= layerNo; + + if (action == "add") { + randoData.mObjectAdditions[stageRoomLayerKey].push_back(actorData); + } else { // patch or delete + randoData.mObjectPatches[stageRoomLayerKey][objectCRC32] = actorData; + } + } + } + } + } + + // Give custom flows and messages new indices as we read them in + std::unordered_map customMessageIDs{}; + std::unordered_map customFlowIDs{}; + std::unordered_set usedMessageIDs{}; + std::unordered_set usedFlowIDs{}; + u16 curCustomMessageID = BASE_CUSTOM_MSG_AND_FLOW_ID; + u16 curCustomFlowID = BASE_CUSTOM_MSG_AND_FLOW_ID; + + // Helper functions for assigning new custom flow IDs/message IDs + auto handleCustomID = [](const YAML::Node& node, auto& customIds, auto& usedIds, u16& curCustomID) { + u16 resultIndex{}; + // Check to see if we're setting a custom index + auto resultStr = node.as(); + auto resultInt = randomizer::utility::str::toInt(resultStr); + // If we have a regular index, then use that directly + if (resultInt.has_value()) { + resultIndex = resultInt.value(); + } else { + // If we don't, assume we're setting the index as custom + if (customIds.contains(resultStr)) { + resultIndex = customIds[resultStr]; + } else { + while (usedIds.contains(curCustomID)) { + ++curCustomID; + } + auto newIndex = curCustomID++; + resultIndex = newIndex; + customIds[resultStr] = newIndex; + } + } + + usedIds.insert(resultIndex); + return resultIndex; + }; + + auto handleCustomFlowID = [&](const YAML::Node& node) { + return handleCustomID(node, customFlowIDs, usedFlowIDs, curCustomFlowID); + }; + + auto handleCustomMessageID = [&](const YAML::Node& node) { + return handleCustomID(node, customMessageIDs, usedMessageIDs, curCustomMessageID); + }; + + // Flow Patches + auto flowPatches = LOAD_EMBED_YAML(RANDO_DATA_PATH "flow_patches.yaml"); + for (const auto& groupNode : flowPatches) { + u8 groupNo = groupNode.first.as(); + for (const auto& flowNode : groupNode.second) { + std::string name{}; + std::list indices{}; + if (flowNode["index"]) { + // If we're specifying a sequence of indices + if (flowNode["index"].IsSequence()) { + for (const auto& indexNode : flowNode["index"]) { + auto index = indexNode.as(); + indices.push_back(index); + usedFlowIDs.insert(index); + } + name = std::to_string(indices.front()); + } + // If we have just a single index + else if (flowNode["index"].IsScalar()) { + auto index = flowNode["index"].as(); + indices.push_back(index); + name = std::to_string(index); + usedFlowIDs.insert(index); + } + + // If we're specifying an index as well as a name, add the index to the custom + // ids + if (flowNode["name"]) { + name = flowNode["name"].as(); + customFlowIDs[name] = indices.front(); + } + } else { + name = flowNode["name"].as(); + indices.push_back(handleCustomFlowID(flowNode["name"])); + } + + const auto& type = flowNode["type"].as(); + u64 value{}; + if (type == "branch") { + auto branch = reinterpret_cast(&value); + branch->type = 2; + branch->field_0x1 = flowNode["num results"].as(); + branch->query_idx = flowNode["query"].as(); + branch->param = flowNode["parameters"].as(); + branch->next_node_idx = flowNode["next node index"].as(); + // If we're using custom result indices + if (flowNode["results"]) { + auto& results = flowNode["results"]; + if (results.size() != branch->field_0x1) { + throw std::runtime_error(fmt::format("Flow results size for {} " + "do not match num results. (expected: {}. size: {})", name, branch->field_0x1, results.size())); + } + for (const auto& resultNode : results) { + auto resultIndex = handleCustomFlowID(resultNode); + for (auto index : indices) { + u32 key = (groupNo << 16) | index; + randoData.mFlowPatchesBranchOverrides[key].push_back(resultIndex); + } + } + } + } + else if (type == "event") { + auto event = reinterpret_cast(&value); + event->type = 3; + event->event_idx = flowNode["event"].as(); + event->next_node_idx = handleCustomFlowID(flowNode["next node index"]); + u32 params = flowNode["parameters"].as(); + event->params[0] = (params >> 24) & 0xFF; + event->params[1] = (params >> 16) & 0xFF; + event->params[2] = (params >> 8) & 0xFF; + event->params[3] = params & 0xFF; + } else if (type == "message") { + auto message = reinterpret_cast(&value); + message->type = 1; + message->msg_index = handleCustomMessageID(flowNode["inf index"]); + message->next_node_idx = handleCustomFlowID(flowNode["next flow index"]); + } + for (auto index : indices) { + u32 key = (groupNo << 16) | index; + randoData.mFlowPatches[key] = value; + } + } + } + + // Text Overrides + auto textOverrides = LOAD_EMBED_YAML(RANDO_DATA_PATH "text/text_overrides.yaml"); + for (const auto& overrideNode : textOverrides) { + const auto& name = overrideNode["Name"].as(); + u8 group; + u16 messageId; + if (overrideNode["Group"]) { + group = overrideNode["Group"].as(); + } else { + // If no group specified, assume custom bmg group + group = CUSTOM_BMG_GROUP; + } + if (overrideNode["Message Id"]) { + messageId = overrideNode["Message Id"].as(); + } else { + // If no message id specified, assume a custom one + messageId = handleCustomMessageID(overrideNode["Name"]); + } + u32 key = (group << 16) | messageId; + for (auto language : randomizer::supportedLanguages) { + std::string text; + if (world->GetTextDatabase().contains(name)) { + text = world->GetText(name, randomizer::Text::STANDARD, language); + } else { + text = randomizer::getTextStr(name, randomizer::Text::STANDARD, language); + } + + randomizer::applyMessageCodes(text); + randoData.mTextOverrides[language][key] = text; + } + + // If we have custom attributes + if (overrideNode["Attributes"]) { + auto attributesStr = overrideNode["Attributes"].as(); + auto attributesVec = HexToBytes(attributesStr); + if (attributesVec.size() != 16) { + throw std::runtime_error(fmt::format("Attributes for Text Override {} " + "are the wrong length. (Expected: 16, Actual: {}", name, attributesVec.size())); + } + + std::array attributes{}; + for (size_t i = 0; i < attributesVec.size(); ++i) { + attributes[i + 4] = attributesVec[i]; + } + + // Set the message id in the attribute data + attributes[4] = messageId >> 8; + attributes[5] = messageId & 0xFF; + + randoData.mAttributeOverrides[key] = attributes; + } + } + + // Entrance Overrides + if (world->Setting("Mirror Chamber Access") == "Closed") { + // Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance + // if mirror chamber access is closed + RandomizerContext::EntranceOverride original = { + StageIDs::Mirror_Chamber, + 4, + 0, + -1 + }; + + RandomizerContext::EntranceOverride override = { + StageIDs::Bulblin_Camp, + 3, + 3, + -1 + }; + + randoData.mEntranceOverrides[std::bit_cast(original)] = override; + } + + // Vanilla Return to Place Overrides. Will need to change when boss/miniboss ER is implemented + static const std::list, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{ + {{Forest_Temple, Ook, Diababa}, {Forest_Temple, 22, 0, -1}}, + {{Goron_Mines, Dangoro, Fyrus}, {Goron_Mines, 1, 0, -1}}, + {{Lakebed_Temple, Deku_Toad, Morpheel}, {Lakebed_Temple, 0, 0, -1}}, + {{Arbiters_Grounds, Death_Sword, Stallord}, {Arbiters_Grounds, 0, 0, -1}}, + {{Snowpeak_Ruins, Darkhammer, Blizzeta}, {Snowpeak_Ruins, 0, 0, -1}}, + {{Temple_of_Time, Darknut, Armogohma}, {Temple_of_Time, 0, 0, -1}}, + {{City_in_the_Sky, Aeralfos, Argorok}, {City_in_the_Sky, 0, 3, -1}}, + {{Palace_of_Twilight, Phantom_Zant_1, + Phantom_Zant_2, Zant_Main_Room, Zant_Fight}, {Palace_of_Twilight, 0, 0, -1}}, + {{Hyrule_Castle, Ganondorf_Castle, Ganondorf_Field}, {Hyrule_Castle, 11, 0, -1}}, + }; + + // Return to Place Overrides + for (const auto& [stages, returnPlace] : defaultPlaceOverrides) { + for (auto stage : stages) { + randoData.mReturnToPlaceOverrides[stage] = returnPlace; + } + } + + return std::move(randoData); +} + +static void DeleteFailedGenerationFiles(randomizer::Randomizer& rando) { + // If the hash is empty, then we never generated any files + if (!rando.GetConfig().GetHash().empty()) { + std::filesystem::remove_all(rando.GetSeedOutputPath()); + } +} + +bool GenerateAndWriteSeed(std::string& generationStatusMsg) { + auto r = randomizer::Randomizer{::randomizer::paths::GetRandomizerPath()}; + + auto generationResult = r.Generate(); + if (generationResult.has_value()) { + generationStatusMsg = fmt::format("Failed to generate seed. Reason:\n{}", generationResult.value()); + DeleteFailedGenerationFiles(r); + return false; + } + + const auto world = r.GetWorld(); + RandomizerContext randoData{}; + try { + randoData = WriteSeedData(world); + } catch (const std::runtime_error& e) { + generationStatusMsg = + fmt::format("Failed to write seed data. Reason:\n{}", e.what()); + DeleteFailedGenerationFiles(r); + return false; + } + + randoData.mHash = r.GetConfig().GetHash(); + auto writeToFileResult = randoData.WriteToFile(); + if (writeToFileResult.has_value()) { + generationStatusMsg = + fmt::format("Failed to write seed data to file. Reason:\n{}", writeToFileResult.value()); + DeleteFailedGenerationFiles(r); + return false; + } + + generationStatusMsg = fmt::format("Seed generated! Hash: {}", randoData.mHash); + return true; +} \ No newline at end of file diff --git a/mods/randomizer/src/randomizer_context.hpp b/mods/randomizer/src/randomizer_context.hpp new file mode 100644 index 0000000000..fd16f7e60a --- /dev/null +++ b/mods/randomizer/src/randomizer_context.hpp @@ -0,0 +1,287 @@ +#ifndef DUSK_RANDOMIZER_CONTEXT_HPP +#define DUSK_RANDOMIZER_CONTEXT_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../generator/randomizer.hpp" + +/* + * Class holding all the information necessary for playing + * the current randomizer seed + */ +class RandomizerContext { +public: + static constexpr size_t ACTR_CRC_SIZE = 32; + static constexpr size_t TGSC_CRC_SIZE = 35; // 3 extra bytes for scale x, y, z + static constexpr size_t OBJ_DELETE_SIZE = 1; + static constexpr u8 ROOM_STAGE = 0xFF; + + RandomizerContext() = default; + + bool mCreatingSave{false}; + u32 mSeedID{0}; + std::string mHash{""}; + + // Maps enum of necessary setting to enum of value + std::unordered_map mSettings{}; + + std::list mStartEventFlags{}; + std::unordered_map> mStartRegionFlags{}; + std::list mStartingInventory{}; + + struct itemLocationData{ + int itemId{0xFF}; + int stage{0xFF}; + u16 flag{0xFFFF}; + }; + + std::unordered_map mTreasureChestOverrides{}; + std::unordered_map mPoeOverrides{}; + std::unordered_map mFreestandingItemOverrides{}; + std::unordered_map mBugRewardOverrides{}; + std::unordered_map mSkyCharacterOverrides{}; + std::unordered_map mGoldenWolfOverrides{}; + std::unordered_map mShopOverrides{}; + std::unordered_map mTwilitInsectOverrides{}; // Just used in tracker for now + std::unordered_map mFlowItemMessageOverrides{}; + std::unordered_map mItemLocations{}; + + u8 mStartHour{0}; + u8 mMapBits{}; + + std::unordered_map>> mObjectPatches{}; + std::unordered_map>> mObjectAdditions{}; + // std::unordered_map> mTgscDeletions{}; + std::unordered_map mFlowPatches{}; + std::unordered_map> mFlowPatchesBranchOverrides{}; + std::unordered_map> mAttributeOverrides{}; + + // struct TextOverride { + // std::array mAttributes{}; + // std::string mText{}; + // }; + // Map of language -> map of key -> string + std::unordered_map> mTextOverrides{}; + + struct EntranceOverride { + u8 stageId = 0xFF; + s8 roomNo = -1; + s8 pointNo = -1; + s8 mapLayer = -1; + }; + + // keyed by stageId << 24 | pointNo << 16 | roomNo << 8 | mapLayer + std::unordered_map mEntranceOverrides{}; + + // Overrides for returning to spawn. Keyed by stageId + std::unordered_map mReturnToPlaceOverrides{}; + + std::optional WriteToFile(); + std::optional LoadFromHash(const std::string& hash); + std::filesystem::path GetSeedDataPath() const; + + enum Settings { + HYRULE_BARRIER_REQUIREMENTS, + HYRULE_BARRIER_FUSED_SHADOWS, + HYRULE_BARRIER_MIRROR_SHARDS, + HYRULE_BARRIER_POE_SOULS, + HYRULE_BARRIER_HEARTS, + HYRULE_BARRIER_DUNGEONS, + HYRULE_BIG_KEY_REQUIREMENTS, + HYRULE_BIG_KEY_FUSED_SHADOWS, + HYRULE_BIG_KEY_MIRROR_SHARDS, + HYRULE_BIG_KEY_POE_SOULS, + HYRULE_BIG_KEY_HEARTS, + HYRULE_BIG_KEY_DUNGEONS, + PALACE_OF_TWILIGHT_REQUIREMENTS, + TEMPLE_OF_TIME_SWORD_REQUIREMENT, + SKIP_MINOR_CUTSCENES, + SKIP_MAJOR_CUTSCENES, + SKIP_BRIDGE_DONATION, + MIRROR_CHAMBER_ACCESS, + }; + + enum Options { + ON, + OFF, + NONE, + VANILLA, + OPEN, + FUSED_SHADOWS, + MIRROR_SHARDS, + POE_SOULS, + HEARTS, + DUNGEONS, + WOODEN_SWORD, + ORDON_SWORD, + MASTER_SWORD, + LIGHT_SWORD, + BARRIER, + CLOSED, + }; + + static int SettingToEnum(const std::string& settingName); + + static int OptionToEnum(const std::string& optionName); +}; + +/* + * Class holding seed-agnostic dynamic information about current randomizer play. + * This gets reset when resetting to the title screen. + */ +class RandomizerState { +public: + enum TimeChange { + NO_CHANGE = 0, + CHANGE_TO_NIGHT, + CHANGE_TO_DAY, + }; + + enum EventItemStatus { + QUEUE_EMPTY, + ITEM_IN_QUEUE, + CLEAR_QUEUE, + }; + + static constexpr u8 EVENT_ITEM_QUEUE_SIZE = 10; + + RandomizerState() {mInitialized = false;} + + int _create(); + int _delete(); + int execute(); + int draw(); + void addItemToEventQueue(u8 item); + void initGiveItemToPlayer(); + //void handleBonkDamage(); + void handleTimeOfDayChange(); + void handleTimeSpeed(); + void offLoad(); + + void handlePoeItem(u8 bitSw); + + u8 getGiveItemToPlayerStatus() const { return mEventItemStatus;} + u8 getTimeChange() const { return mTimeChange; } + bool getRoomReloadingState() const { return mRoomReloadingState; } + bool getHasPendingToDChange() const { return mHasPendingToDChange; } + + void setGiveItemToPlayerStatus(u8 status) { mEventItemStatus = status;} + void setHasPendingToDChange(bool hasPending) { mHasPendingToDChange = hasPending; } + void setTimeChange(u8 newTimeChange) { mTimeChange = newTimeChange; } + void setRoomReloadingState(bool newState) { mRoomReloadingState = newState; } + + bool mInitialized{false}; + int mFileNum{-1}; + u8 mEventItemStatus{}; + bool mHasPendingToDChange{false}; + u8 mTimeChange{}; + u8 mEventItemQueue[EVENT_ITEM_QUEUE_SIZE]; + bool mRoomReloadingState{false}; + + // Used to store an item id for a flow message override so that we can give the item + // once the textbox is closed instead of when the message appears. This lines up + // more naturally with the timing of when the game normally gives items and affects + // things like the sound of the rupee counter going up. + u8 mFlowMessageItemId{0}; + + int mFoolishItemCount{0}; + bool mUpdateTracker{false}; + bool mShowTracker{false}; + u16 mTrackerTempEventFlag{0}; + struct { + int stage{-1}; + int flag{-1}; + } mTrackerTempSwitchFlag; + struct { + int stage{-1}; + int flag{-1}; + } mTrackerTempItemFlag; +}; + +extern RandomizerState g_randomizerState; + +RandomizerContext& randomizer_GetContext(); + +bool randomizer_IsActive(); + +int randomizer_getItemAtLocation(const std::string& locationName); + +/* + * @brief Overrides the given entrance paramaters if an override exists for them + */ +void randomizer_checkAndOverrideEntranceData(const char*& i_Name, s8& i_RoomNo, s16& i_Point, s8& i_Layer); +/* + * @brief Puts the associated flag into the randomizer state's temporary flag + * variable. This allows the tracker/Archipelago to know a location has been checked + * when the item is received instead of some indeterminate amount of time afterward. + */ +void randomizer_setTempFlagForLocation(const std::string& locationName); + +void randomizer_setTempFlagForFLWOverride(u32 key); + +bool randomizer_checkTempleOfTimeRequirement(); + +bool randomizer_mirrorChamberWallShouldExist(); + +void randomizer_returnToSpawn(bool tryDungeon); + +u8 randomizer_getRandomFoolishItemModelID(); + +/** + * Helper function to convert raw bytes of a container to a hex string + */ +template +std::string ContainerToHexString(const T& container, bool includePrefix = true) { + std::ostringstream oss; + + if (includePrefix) { + oss << "0x"; + } + + // Get the raw byte pointer to the start of the data + const auto* rawBytes = reinterpret_cast(container.data()); + + // Calculate total byte size (number of elements * size of each element) + size_t totalBytes = container.size() * sizeof(typename T::value_type); + + oss << std::hex << std::setfill('0') << std::uppercase; + + for (size_t i = 0; i < totalBytes; ++i) { + // static_cast to not u8 is necessary so oss treats it as a number, not a char + oss << std::setw(2) << static_cast(rawBytes[i]); + } + + return oss.str(); +} + +/** + * Helper function to convert hex string to raw bytes + */ +std::vector HexToBytes(std::string hex); + +/* + * Gets the current stage id, room no, and layer no in the format for a key in mActorPatches + */ +u32 getActorPatchesCurrentStageKey(u8 roomNo); + +/* + * Gets the CRC32 hash of an actors name, parameters, position, and angle + */ +u32 getStageObjCRC32(u8* data, size_t size); + +/* + * Generates a seed and writes the necessary seed files to the players seed directory + * Returns true if generation was successful, false otherwise. + */ +bool GenerateAndWriteSeed(std::string& generationStatusMsg); + +#endif //DUSK_RANDOMIZER_CONTEXT_HPP diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp new file mode 100644 index 0000000000..76d8b0c5a9 --- /dev/null +++ b/mods/randomizer/src/session.cpp @@ -0,0 +1,13 @@ +#include "session.hpp" + +namespace randomizer::session { +ServiceManager svc_mng; + +ModResult initialize(const ServiceManager& services) { + svc_mng = services; + + return MOD_OK; +} + + +} \ No newline at end of file diff --git a/mods/randomizer/src/session.hpp b/mods/randomizer/src/session.hpp new file mode 100644 index 0000000000..098488bf74 --- /dev/null +++ b/mods/randomizer/src/session.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "mods/svc/host.h" +#include "mods/svc/log.h" +#include "mods/svc/config.h" +#include "mods/svc/hook.h" +#include "mods/svc/ui.h" +#include "mods/svc/resource.h" + +namespace randomizer::session { +struct ServiceManager { + ModContext* mod_ctx; + const HostService* host; + const LogService* log; + const HookService* hook; + const UiService* ui; + const ResourceService* resource; + const ConfigService* config; +}; + +extern ServiceManager svc_mng; + +ModResult initialize(const ServiceManager& services); + +inline void LogError(const char* msg) { + svc_mng.log->error(svc_mng.mod_ctx, msg); +} + +inline void LogDebug(const char* msg) { + svc_mng.log->debug(svc_mng.mod_ctx, msg); +} + +inline void LogWarn(const char* msg) { + svc_mng.log->warn(svc_mng.mod_ctx, msg); +} +} \ No newline at end of file diff --git a/mods/randomizer/src/stages.cpp b/mods/randomizer/src/stages.cpp new file mode 100644 index 0000000000..904cf5e70f --- /dev/null +++ b/mods/randomizer/src/stages.cpp @@ -0,0 +1,82 @@ +#include "stages.h" + +const char allStages[78][8] = { + "D_MN01", // 0 + "D_MN01A", // 1 + "D_MN01B", // 2 + "D_MN04", // 3 + "D_MN04A", // 4 + "D_MN04B", // 5 + "D_MN05", // 6 + "D_MN05A", // 7 + "D_MN05B", // 8 + "D_MN06", // 9 + "D_MN06A", // 10 + "D_MN06B", // 11 + "D_MN07", // 12 + "D_MN07A", // 13 + "D_MN07B", // 14 + "D_MN08", // 15 + "D_MN08A", // 16 + "D_MN08B", // 17 + "D_MN08C", // 18 + "D_MN08D", // 19 + "D_MN09", // 20 + "D_MN09A", // 21 + "D_MN09B", // 22 + "D_MN09C", // 23 + "D_MN10", // 24 + "D_MN10A", // 25 + "D_MN10B", // 26 + "D_MN11", // 27 + "D_MN11A", // 28 + "D_MN11B", // 29 + "D_SB00", // 30 + "D_SB01", // 31 + "D_SB02", // 32 + "D_SB03", // 33 + "D_SB04", // 34 + "D_SB05", // 35 + "D_SB06", // 36 + "D_SB07", // 37 + "D_SB08", // 38 + "D_SB09", // 39 + "D_SB10", // 40 + "F_SP00", // 41 + "F_SP102", // 42 + "F_SP103", // 43 + "F_SP104", // 44 + "F_SP108", // 45 + "F_SP109", // 46 + "F_SP110", // 47 + "F_SP111", // 48 + "F_SP112", // 49 + "F_SP113", // 50 + "F_SP114", // 51 + "F_SP115", // 52 + "F_SP116", // 53 + "F_SP117", // 54 + "F_SP118", // 55 + "F_SP121", // 56 + "F_SP122", // 57 + "F_SP123", // 58 + "F_SP124", // 59 + "F_SP125", // 60 + "F_SP126", // 61 + "F_SP127", // 62 + "F_SP128", // 63 + "F_SP200", // 64 + "R_SP01", // 65 + "R_SP107", // 66 + "R_SP108", // 67 + "R_SP109", // 68 + "R_SP110", // 69 + "R_SP116", // 70 + "R_SP127", // 71 + "R_SP128", // 72 + "R_SP160", // 73 + "R_SP161", // 74 + "R_SP209", // 75 + "R_SP300", // 76 + "R_SP301" // 77 +}; \ No newline at end of file diff --git a/mods/randomizer/src/stages.h b/mods/randomizer/src/stages.h new file mode 100644 index 0000000000..607c4a7959 --- /dev/null +++ b/mods/randomizer/src/stages.h @@ -0,0 +1,85 @@ +#pragma once + +enum StageIDs + { + Lakebed_Temple = 0x0, + Morpheel = 0x1, + Deku_Toad, + Goron_Mines, + Fyrus, + Dangoro, + Forest_Temple, + Diababa, + Ook, + Temple_of_Time, + Armogohma, + Darknut, + City_in_the_Sky, + Argorok, + Aeralfos, + Palace_of_Twilight, + Zant_Main_Room, + Phantom_Zant_1, + Phantom_Zant_2, + Zant_Fight, + Hyrule_Castle, + Ganondorf_Castle, + Ganondorf_Field, + Ganondorf_Defeated, + Arbiters_Grounds, + Stallord, + Death_Sword, + Snowpeak_Ruins, + Blizzeta, + Darkhammer, + Lanayru_Ice_Puzzle_Cave, + Cave_of_Ordeals, + Eldin_Long_Cave, + Lake_Hylia_Long_Cave, + Eldin_Goron_Stockcave, + Grotto_1, + Grotto_2, + Grotto_3, + Grotto_4, + Grotto_5, + Faron_Woods_Cave, + Ordon_Ranch, + Title_Screen, + Ordon_Village, + Ordon_Spring, + Faron_Woods, + Kakariko_Village, + Death_Mountain, + Kakariko_Graveyard, + Zoras_River, + Zoras_Domain, + Snowpeak, + Lake_Hylia, + Castle_Town, + Sacred_Grove, + Bulblin_Camp, + Hyrule_Field, + Outside_Castle_Town, + Bulblin_2, + Gerudo_Desert, + Mirror_Chamber, + Upper_Zoras_River, + Fishing_Pond, + Hidden_Village, + Hidden_Skill, + Ordon_Village_Interiors, + Hyrule_Castle_Sewers, + Faron_Woods_Interiors, + Kakariko_Village_Interiors, + Death_Mountain_Interiors, + Castle_Town_Interiors, + Fishing_Pond_Interiors, + Hidden_Village_Interiors, + Castle_Town_Shops, + Star_Game, + Kakariko_Graveyard_Interiors, + Light_Arrows_Cutscene, + Hyrule_Castle_Cutscenes + }; + +extern const char allStages[78][8]; diff --git a/mods/randomizer/src/tools.cpp b/mods/randomizer/src/tools.cpp new file mode 100644 index 0000000000..1236aae7e8 --- /dev/null +++ b/mods/randomizer/src/tools.cpp @@ -0,0 +1,748 @@ +#include "tools.h" + +#include "../generator/logic/world.hpp" +#include "d/actor/d_a_alink.h" +#include "d/d_com_inf_game.h" +#include "d/d_item.h" +#include "d/d_item_data.h" +#include "f_op/f_op_actor_mng.h" +#include "fmt/format.h" +#include "item_ids.h" +#include "randomizer_context.hpp" +#include "session.hpp" +#include "stages.h" +#include "utilities.h" +#include "verify_item_functions.h" + +bool playerIsInRoomStage(s32 room, const char* stage) +{ + // Only check room if it is valid + // Room numbers are normally stored as int8_t, so the highest positive value is 127 + if ((room < 0) || (room > 127)) + { + return false; + } + + if (room != dStage_roomControl_c::mStayNo) + { + return false; + } + + // Only check stage if it is valid + if (!stage) + { + return false; + } + + return daAlink_c::checkStageName(stage); +} + +void checkTransformFromWolf() +{ + if (dComIfGs_getTransformStatus()) + { + daAlink_getAlinkActorClass()->procCoMetamorphoseInit(); + } +} + +u8 setNextWarashibeItem() +{ + static const u8 questItemsList[] = { + dItemNo_Randomizer_LETTER_e, + dItemNo_Randomizer_BILL_e, + dItemNo_Randomizer_WOOD_STATUE_e, + dItemNo_Randomizer_IRIAS_PENDANT_e, + dItemNo_Randomizer_HORSE_FLUTE_e + }; + + u32 listLength = sizeof(questItemsList) / sizeof(questItemsList[0]); + + u8 newItem = 0xFF; // null by default + + for (u32 i = 0; i < listLength; i++) + { + const u32 item = questItemsList[i]; + const u8 slotItem = dComIfGs_getItem(21, 0); + if (item == slotItem) + { + newItem = item; + u32 j = i; + do + { + j = (j + 1) % listLength; // Move to next index, wrapping around if needed. + if (checkItemGet(questItemsList[j], 1)) + { + newItem = questItemsList[j]; + break; + } + } while (j != i); + + // If the item to switch to is the same as the current item and we don't have the item anymore, null the slot + if ((newItem == item) && !checkItemGet(item, 1)) + { + newItem = 0xFF; + } + dComIfGs_setItem(21, newItem); + + break; + } + } + return newItem; +} + +void offWarashibeItem(u8 item) +{ + g_dComIfG_gameInfo.info.getSavedata().getPlayer().getGetItem().offFirstBit(item); + setNextWarashibeItem(); +} + +int initCreatePlayerItem(u32 item, u32 flag, const cXyz* pos, int roomNo, const csXyz* angle, const cXyz* scale) +{ + u32 params = 0xFF0000 | ((flag & 0xFF) << 0x8) | (item & 0xFF); + return fopAcM_create(539, params, pos, roomNo, angle, scale, -1); +} + +int getStageID(const char* stage) +{ + int loopCount = sizeof(allStages) / sizeof(allStages[0]); + for (int i = 0; i < loopCount; i++) + { + // If stage is NULL, check for current stage + if (stage == NULL) { + if (daAlink_c::checkStageName(allStages[i])) return i; + } else if (strcmp(stage, allStages[i]) == 0) { + return i; + } + } + // Didn't find the current stage for some reason + return -1; +} + +bool playerIsOnTitleScreen() { + // Player is either on title screen movie stage (S_MV000) or on title screen map layer 10 + return strcmp(dComIfGp_getStartStageName(), "S_MV000") == 0 || + (strcmp(dComIfGp_getStartStageName(), "F_SP102") == 0 && dComIfG_play_c::getLayerNo(0) == 10); +} + +u16 getItemMessageID(u8 itemId) { + // If heart piece, choose from the different heart piece messages + if (itemId == dItemNo_Randomizer_KAKERA_HEART_e) { + static u32 const heartPieceMessage[5] = {0x86, 0x9C, 0x9D, 0x9E, 0x9F}; + return heartPieceMessage[dComIfGs_getMaxLife() % 5]; + } + + return itemId + 0x65; +} + +int numCompletedDungeons() { + int numCompleted{0}; + // Loop through dungeon area node ids + for (int i = 0x10; i < 0x18; ++i) { + numCompleted += dComIfGs_isStageBossEnemy(i); + } + return numCompleted; +} + +int numFusedShadows() { + int numFusedShadows{0}; + for (int i = 0; i < 3; ++i) { + numFusedShadows += dComIfGs_isCollectCrystal(i); + } + return numFusedShadows; +} + +int numMirrorShards() { + int numMirrorShards{0}; + for (int i = 0; i < 4; ++i) { + numMirrorShards += dComIfGs_isCollectMirror(i); + } + return numMirrorShards; +} + +int getTempleKeysFound(int saveId) { + static std::unordered_map> keyDoorFlags = { + {0xA, {0x0}}, + {0x10, {0x7, 0xB, 0x2B, 0x3E}}, + {0x11, {0x33, 0x3D, 0x3F}}, + {0x12, {0x23, 0x24, 0x34}}, + {0x13, {0x27, 0x46, 0x4D, 0x5A, 0x5B}}, + {0x14, {0x2B, 0x2C, 0x2F, 0x30}}, + {0x15, {0x1B, 0x1C, 0x1D}}, + {0x16, {0x6}}, + {0x17, {0x6, 0x7, 0x8, 0xB, 0x23, 0x24, 0x25}}, + {0x18, {0x4C, 0x6F, 0x7C}} + }; + + int count = getAreaKeyNum(saveId); + + // Add number of unlocked key doors for this dungeon to current key count + for (auto flag : keyDoorFlags[saveId]) { + if (tracker_isStageSwitch(saveId, flag)) { + count += 1; + } + } + + return count; +} + +bool isTempleBigKeyFound(int stage) { + // The boss key never gets taken away unlike small keys + return dComIfGs_isDungeonItemBossKey(stage); +} + +randomizer::logic::item_pool::ItemPool getSaveItemPool(randomizer::logic::world::World* world) { + randomizer::logic::item_pool::ItemPool pool{}; + + // Item wheel items + for (int i = 0; i < MAX_ITEM_SLOTS; ++i) { + switch (dComIfGs_getItem(i, false)) { + case dItemNo_Randomizer_HAWK_EYE_e: + pool.push_back(world->GetItem("Hawkeye", true)); + break; + case dItemNo_Randomizer_BOOMERANG_e: + pool.push_back(world->GetItem("Gale Boomerang", true)); + break; + case dItemNo_Randomizer_SPINNER_e: + pool.push_back(world->GetItem("Spinner", true)); + break; + case dItemNo_Randomizer_IRONBALL_e: + pool.push_back(world->GetItem("Ball and Chain", true)); + break; + case dItemNo_Randomizer_BOW_e: + pool.push_back(world->GetItem("Progressive Bow", true)); + break; + case dItemNo_Randomizer_W_HOOKSHOT_e: + pool.push_back(world->GetItem("Progressive Clawshot", true)); + [[fallthrough]]; + case dItemNo_Randomizer_HOOKSHOT_e: + pool.push_back(world->GetItem("Progressive Clawshot", true)); + break; + case dItemNo_Randomizer_HVY_BOOTS_e: + pool.push_back(world->GetItem("Iron Boots", true)); + break; + case dItemNo_Randomizer_COPY_ROD_e: + pool.push_back(world->GetItem("Progressive Dominion Rod", true)); + // Powered up dominion rod + if (dComIfGs_isEventBit(0x2580)) { + pool.push_back(world->GetItem("Progressive Dominion Rod", true)); + } + break; + case dItemNo_Randomizer_KANTERA_e: + pool.push_back(world->GetItem("Lantern", true)); + break; + case dItemNo_Randomizer_JEWEL_ROD_e: + case dItemNo_Randomizer_JEWEL_BEE_ROD_e: + case dItemNo_Randomizer_JEWEL_WORM_ROD_e: + pool.push_back(world->GetItem("Progressive Fishing Rod", true)); + [[fallthrough]]; + case dItemNo_Randomizer_FISHING_ROD_1_e: + case dItemNo_Randomizer_BEE_ROD_e: + case dItemNo_Randomizer_WORM_ROD_e: + pool.push_back(world->GetItem("Progressive Fishing Rod", true)); + break; + case dItemNo_Randomizer_PACHINKO_e: + pool.push_back(world->GetItem("Slingshot", true)); + break; + case dItemNo_Randomizer_BOMB_BAG_LV1_e: + case dItemNo_Randomizer_NORMAL_BOMB_e: + case dItemNo_Randomizer_WATER_BOMB_e: + case dItemNo_Randomizer_POKE_BOMB_e: + pool.push_back(world->GetItem("Bomb Bag", true)); + break; + case dItemNo_Randomizer_RAFRELS_MEMO_e: + pool.push_back(world->GetItem("Aurus Memo", true)); + break; + case dItemNo_Randomizer_ASHS_SCRIBBLING_e: + pool.push_back(world->GetItem("Asheis Sketch", true)); + break; + case dItemNo_Randomizer_ANCIENT_DOCUMENT_e: + case dItemNo_Randomizer_ANCIENT_DOCUMENT2_e: + case dItemNo_Randomizer_AIR_LETTER_e: + pool.push_back(world->GetItem("Progressive Sky Book", true)); + break; + case dItemNo_Randomizer_EMPTY_BOTTLE_e: + case dItemNo_Randomizer_RED_BOTTLE_e: + case dItemNo_Randomizer_GREEN_BOTTLE_e: + case dItemNo_Randomizer_BLUE_BOTTLE_e: + case dItemNo_Randomizer_MILK_BOTTLE_e: + case dItemNo_Randomizer_HALF_MILK_BOTTLE_e: + case dItemNo_Randomizer_OIL_BOTTLE_e: + case dItemNo_Randomizer_WATER_BOTTLE_e: + case dItemNo_Randomizer_OIL_BOTTLE_2_e: + case dItemNo_Randomizer_RED_BOTTLE_2_e: + case dItemNo_Randomizer_UGLY_SOUP_e: + case dItemNo_Randomizer_HOT_SPRING_e: + case dItemNo_Randomizer_FAIRY_e: + case dItemNo_Randomizer_HOT_SPRING_2_e: + case dItemNo_Randomizer_OIL2_e: + case dItemNo_Randomizer_OIL_e: + case dItemNo_Randomizer_FAIRY_DROP_e: + case dItemNo_Randomizer_DROP_BOTTLE_e: + case dItemNo_Randomizer_BEE_CHILD_e: + case dItemNo_Randomizer_CHUCHU_RARE_e: + case dItemNo_Randomizer_CHUCHU_RED_e: + case dItemNo_Randomizer_CHUCHU_BLUE_e: + case dItemNo_Randomizer_CHUCHU_GREEN_e: + case dItemNo_Randomizer_CHUCHU_YELLOW_e: + case dItemNo_Randomizer_CHUCHU_PURPLE_e: + case dItemNo_Randomizer_LV1_SOUP_e: + case dItemNo_Randomizer_LV2_SOUP_e: + case dItemNo_Randomizer_LV3_SOUP_e: + case dItemNo_Randomizer_OIL_BOTTLE3_e: + case dItemNo_Randomizer_CHUCHU_BLACK_e: + pool.push_back(world->GetItem("Empty Bottle", true)); + break; + default: + break; + } + } + + // Shadow Crystal + if (dComIfGs_isEventBit(0xD04)) { + pool.push_back(world->GetItem("Shadow Crystal", true)); + } + + // Showed Auru's Memo to Fyer + if (dComIfGs_isEventBit(0x2680)) { + pool.push_back(world->GetItem("Aurus Memo", true)); + } + + // Showed Ralis Ashei's Sketch + if (dComIfGs_isEventBit(0x3B80)) { + pool.push_back(world->GetItem("Asheis Sketch", true)); + } + + // Fused Shadows + for (int i = 0; i < numFusedShadows(); ++i) { + pool.push_back(world->GetItem("Progressive Fused Shadow", true)); + } + + // Mirror Shards + for (int i = 0; i < numMirrorShards(); ++i) { + pool.push_back(world->GetItem("Progressive Mirror Shard", true)); + } + + // Poe Souls + for (int i = 0; i < dComIfGs_getPohSpiritNum(); ++i) { + pool.push_back(world->GetItem("Poe Soul", true)); + } + + // Hearts + for (int i = 0; i < dComIfGs_getMaxLife(); ++i) { + pool.push_back(world->GetItem("Piece of Heart", true)); + } + + // Sky Book characters + for (int i = 0; i < getAncientDocumentNum(); ++i) { + pool.push_back(world->GetItem("Progressive Sky Book", true)); + } + + // Small Keys + static std::unordered_map keyRegionItemNameMap = { + {0xA, "Gerudo Desert Bulblin Camp Key"}, + {0x10, "Forest Temple Small Key"}, + {0x11, "Goron Mines Small Key"}, + {0x12, "Lakebed Temple Small Key"}, + {0x13, "Arbiters Grounds Small Key"}, + {0x14, "Snowpeak Ruins Small Key"}, + {0x15, "Temple of Time Small Key"}, + {0x16, "City in the Sky Small Key"}, + {0x17, "Palace of Twilight Small Key"}, + {0x18, "Hyrule Castle Small Key"}, + }; + for (auto& [stage, keyName] : keyRegionItemNameMap) { + for (int i = 0; i < getTempleKeysFound(stage); ++i) { + pool.push_back(world->GetItem(keyName, true)); + } + } + + // Gate Keys + if (haveItem(dItemNo_Randomizer_BOSSRIDER_KEY_e)) { + pool.push_back(world->GetItem(dItemNo_Randomizer_BOSSRIDER_KEY_e, true)); + } + + // Big Keys + static std::unordered_map bigKeyRegionItemNameMap = { + {0x10, "Forest Temple Big Key"}, + {0x12, "Lakebed Temple Big Key"}, + {0x13, "Arbiters Grounds Big Key"}, + {0x14, "Snowpeak Ruins Bedroom Key"}, + {0x15, "Temple of Time Big Key"}, + {0x16, "City in the Sky Big Key"}, + {0x17, "Palace of Twilight Big Key"}, + {0x18, "Hyrule Castle Big Key"}, + }; + for (auto& [stage, keyName] : bigKeyRegionItemNameMap) { + if (isTempleBigKeyFound(stage)) { + pool.push_back(world->GetItem(keyName, true)); + } + } + + // Goron Mines Key Shards + if (haveItem(dItemNo_Randomizer_L2_KEY_PIECES3_e)) { + for (int i = 0; i < 3; ++i) { + pool.push_back(world->GetItem("Goron Mines Key Shard", true)); + } + } + + // Ordon Pumpkin + if (haveItem(dItemNo_Randomizer_TOMATO_PUREE_e)) { + pool.push_back(world->GetItem(dItemNo_Randomizer_TOMATO_PUREE_e, true)); + } + + // Ordon Cheese + if (haveItem(dItemNo_Randomizer_TASTE_e)) { + pool.push_back(world->GetItem(dItemNo_Randomizer_TASTE_e, true)); + } + + // Golden Bugs + for (int i = dItemNo_Randomizer_M_BEETLE_e; i < dItemNo_Randomizer_F_MAYFLY_e; ++i) { + if (haveItem(i)) { + pool.push_back(world->GetItem(i, true)); + } + } + + // Ilia quest items + for (int i = dItemNo_Randomizer_LETTER_e; i < dItemNo_Randomizer_HORSE_FLUTE_e; ++i) { + if (haveItem(i)) { + pool.push_back(world->GetItem(i, true)); + } + } + + // Warp Portals + // Item ids are scattered so we have to explicitly list them all + static const int portals[] = { + dItemNo_Randomizer_ORDON_PORTAL_e, + dItemNo_Randomizer_SOUTH_FARON_PORTAL_e, + dItemNo_Randomizer_NORTH_FARON_PORTAL_e, + dItemNo_Randomizer_SACRED_GROVE_PORTAL_e, + dItemNo_Randomizer_KAKARIKO_GORGE_PORTAL_e, + dItemNo_Randomizer_KAKARIKO_VILLAGE_PORTAL_e, + dItemNo_Randomizer_DEATH_MOUNTAIN_PORTAL_e, + dItemNo_Randomizer_ELDIN_BRIDGE_PORTAL_e, + dItemNo_Randomizer_CASTLE_TOWN_PORTAL_e, + dItemNo_Randomizer_UPPER_ZORAS_RIVER_PORTAL_e, + dItemNo_Randomizer_ZORAS_DOMAIN_PORTAL_e, + dItemNo_Randomizer_SNOWPEAK_PORTAL_e, + dItemNo_Randomizer_GERUDO_DESERT_PORTAL_e, + dItemNo_Randomizer_MIRROR_CHAMBER_PORTAL_e, + }; + for (auto portal : portals) { + if (haveItem(portal)) { + pool.push_back(world->GetItem(portal, true)); + } + } + + // Swords + static const int swords[] = { + dItemNo_Randomizer_WOOD_STICK_e, + dItemNo_Randomizer_SWORD_e, + dItemNo_Randomizer_MASTER_SWORD_e, + dItemNo_Randomizer_LIGHT_SWORD_e, + }; + for (auto sword : swords) { + if (haveItem(sword)) { + pool.push_back(world->GetItem("Progressive Sword", true)); + } + } + + // Other Equipment + static const int equipment[] = { + dItemNo_Randomizer_WOOD_SHIELD_e, + dItemNo_Randomizer_HYLIA_SHIELD_e, + dItemNo_Randomizer_WEAR_ZORA_e, + dItemNo_Randomizer_ARMOR_e, + }; + for (auto item : equipment) { + if (haveItem(item)) { + pool.push_back(world->GetItem(item, true)); + } + } + + // Hidden Skills + static const int hiddenSkills[] = { + dItemNo_Randomizer_ENDING_BLOW_e, + dItemNo_Randomizer_SHIELD_ATTACK_e, + dItemNo_Randomizer_BACK_SLICE_e, + dItemNo_Randomizer_HELM_SPLITTER_e, + dItemNo_Randomizer_MORTAL_DRAW_e, + dItemNo_Randomizer_JUMP_STRIKE_e, + dItemNo_Randomizer_GREAT_SPIN_e, + }; + for (auto skill : hiddenSkills) { + if (haveItem(skill)) { + pool.push_back(world->GetItem("Progressive Hidden Skill", true)); + } + } + + // Wallets + switch (dComIfGs_getWalletSize()) { + case GIANT_WALLET: + pool.push_back(world->GetItem("Progressive Wallet", true)); + [[fallthrough]]; + case BIG_WALLET: + pool.push_back(world->GetItem("Progressive Wallet", true)); + [[fallthrough]]; + default: + break; + } + + // Twilight Tears + for (int i = 0; i < dComIfGs_getLightDropNum(0); ++i) { + pool.push_back(world->GetItem("Faron Twilight Tear", true)); + } + for (int i = 0; i < dComIfGs_getLightDropNum(1); ++i) { + pool.push_back(world->GetItem("Eldin Twilight Tear", true)); + } + for (int i = 0; i < dComIfGs_getLightDropNum(2); ++i) { + pool.push_back(world->GetItem("Lanayru Twilight Tear", true)); + } + + return pool; +} + +bool isLocationObtained(randomizer::logic::location::Location* location) { + auto& locationMeta = location->GetMetadata(); + if (auto& chestNode = locationMeta["Chest"]) { + auto tboxId = chestNode[0]["Tbox Id"].as(); + auto stageId = getStageSaveId(chestNode[0]["Stage"].as()); + return dComIfGs_isStageTbox(stageId, tboxId); + } + if (auto& poeNode = locationMeta["Poe"]) { + auto flag = poeNode[0]["Flag"].as(); + auto stageId = getStageSaveId(poeNode[0]["Stage"].as()); + return tracker_isStageSwitch(stageId, flag); + } + if (auto& freeStandingItemNode = locationMeta["Freestanding Item"]) { + auto flag = freeStandingItemNode[0]["Flag"].as(); + auto stageId = getStageSaveId(freeStandingItemNode[0]["Stage"].as()); + // big baba uses tbox, hardcode this edge case + if (location->GetName() == "Forest Temple Big Baba Key") { + return dComIfGs_isStageTbox(stageId, flag); + }else { + return tracker_isStageItem(stageId, flag); + } + } + if (auto& eventFlagNode = locationMeta["Event Flag"]) { + auto flag = eventFlagNode.as(); + return tracker_isEventBit(flag); + } + if (auto& wolfNode = locationMeta["Golden Wolf"]) { + auto flag = wolfNode[0]["Flag"].as(); + return tracker_isEventBit(flag); + } + if (auto& switchFlagNode = locationMeta["Switch Flag"]) { + auto flag = switchFlagNode["Flag"].as(); + auto stageId = getStageSaveId(switchFlagNode["Stage"].as()); + return tracker_isStageSwitch(stageId, flag); + } + if (auto& itemFlagNode = locationMeta["Item Flag"]) { + auto flag = itemFlagNode["Flag"].as(); + auto stageId = getStageSaveId(itemFlagNode["Stage"].as()); + return tracker_isStageItem(stageId, flag); + } + if (auto& twilitInsectNode = locationMeta["Twilit Insect"]) { + auto flag = twilitInsectNode[0]["Flag"].as(); + auto stageId = getStageSaveId(twilitInsectNode[0]["Stage"].as()); + return dComIfGs_isStageTbox(stageId, flag); + } + return false; +} + +int getLocationItem(randomizer::logic::location::Location* location) { + auto& locationMeta = location->GetMetadata(); + auto& context = randomizer_GetContext(); + + if (auto& chestNode = locationMeta["Chest"]) { + auto tboxId = chestNode[0]["Tbox Id"].as(); + auto stage = chestNode[0]["Stage"].as(); + auto key = (stage << 8) | tboxId; + return context.mTreasureChestOverrides[key]; + } + if (auto& poeNode = locationMeta["Poe"]) { + auto flag = poeNode[0]["Flag"].as(); + auto stage = poeNode[0]["Stage"].as(); + auto key = (stage << 8) | flag; + return context.mPoeOverrides[key]; + } + if (auto& freeStandingItemNode = locationMeta["Freestanding Item"]) { + auto flag = freeStandingItemNode[0]["Flag"].as(); + auto stage = freeStandingItemNode[0]["Stage"].as(); + auto key = (stage << 8) | flag; + return context.mFreestandingItemOverrides[key]; + } + if (auto& bugRewardNode = locationMeta["Bug Reward"]) { + u8 bugItemId = bugRewardNode[0]["Item Id"].as(); + return context.mBugRewardOverrides[bugItemId]; + } + if (auto& skyCharacterNode = locationMeta["Sky Character"]) { + u8 stageIdx = skyCharacterNode[0]["Stage"].as(); + u8 roomNo = skyCharacterNode[0]["Room"].as(); + u16 key = (stageIdx << 8) | roomNo; + return context.mSkyCharacterOverrides[key]; + } + if (auto& wolfNode = locationMeta["Golden Wolf"]) { + auto flag = wolfNode[0]["Flag"].as(); + return context.mGoldenWolfOverrides[flag]; + } + if (auto& shopNode = locationMeta["Shop"]) { + u8 stage = shopNode[0]["Stage"].as(); + u8 originalItem = shopNode[0]["Item"].as(); + u16 key = (stage << 8) | originalItem; + return context.mShopOverrides[key]; + } + if (auto& twilitInsectNode = locationMeta["Twilit Insect"]) { + auto flag = twilitInsectNode[0]["Flag"].as(); + auto stage = twilitInsectNode[0]["Stage"].as(); + auto key = (stage << 8) | flag; + return context.mTwilitInsectOverrides[key]; + } + if (auto& flwNode = locationMeta["FLW Message"]) { + auto group = flwNode[0]["Group"].as(); + auto messageId = flwNode[0]["Message Id"].as(); + u32 key = (group << 16) | messageId; + return context.mFlowItemMessageOverrides[key].itemId; + } + if (auto& nameNode = locationMeta["Name Lookup"]) { + auto name = nameNode[0].as(); + return context.mItemLocations[name].itemId; + } + return -1; +} + +int getStageSaveId(int id) { + switch (id) { + case 41: // F_SP00 (Ordon Ranch) + case 43: // F_SP103 (Ordon Village / Outside Link's House) + case 44: // F_SP104 (Ordon Spring) + case 65: // R_SP01 (Ordon House Interiors) + return 0x0; + case 66: // R_SP107 (Castle Town Sewers) + return 0x1; + case 40: // D_SB10 (Faron Woods Cave) + case 45: // F_SP108 (Faron Woods) + case 67: // R_SP108 (Coro's House) + return 0x2; + case 46: // F_SP109 (Kakariko Village) + case 47: // F_SP110 (Death Mountain Trail) + case 48: // F_SP111 (Kakariko Graveyard) + case 63: // F_SP128 (Hidden Village) + case 68: // R_SP109 (Kakariko Interiors) + case 69: // R_SP110 (Goron Elder's Hall) + case 72: // R_SP128 (Impaz's House) + case 75: // R_SP209 (Sanctuary Basement) + return 0x3; + case 49: // F_SP112 (Zora's River) + case 50: // F_SP113 (Zora's Domain) + case 52: // F_SP115 (Lake Hylia) + case 61: // F_SP126 (Upper Zora's River) + case 71: // R_SP127 (Hena's Cabin) + return 0x4; + case 56: // F_SP121 (Hyrule Field) + case 57: // F_SP122 (Outside Castle Town) + case 58: // F_SP123 (King Bulblin 2) + case 64: // F_SP200 (Wolf Howling Cutscene Map) + return 0x6; + case 54: // F_SP117 (Lost Woods) + return 0x7; + case 51: // F_SP114 (Snowpeak Mountain) + return 0x8; + case 53: // F_SP116 (Castle Town) + case 70: // R_SP116 (Telma's Bar / Secret Passage) + case 73: // R_SP160 (Hyrule Castle Town Interiors) + case 74: // R_SP161 (STAR Tent) + return 0x9; + case 55: // F_SP118 (Bulblin Camp) + case 59: // F_SP124 (Gerudo Desert) + case 60: // F_SP125 (Mirror Chamber) + return 0xA; + case 62: // F_SP127 (Fishing Pond) + return 0xB; + case 6: // D_MN05 (Forest Temple) + case 7: // D_MN05A (Diababa Arena) + case 8: // D_MN05B (Ook Arena) + return 0x10; + case 3: // D_MN04 (Goron Mines) + case 4: // D_MN04A (Fyrus Arena) + case 5: // D_MN04B (Dangoro Arena) + return 0x11; + case 0: // D_MN01 (Lakebed Temple) + case 1: // D_MN01A (Morpheel Arena) + case 2: // D_MN01B (Deku Toad Arena) + return 0x12; + case 24: // D_MN10 (Arbiter's Grounds) + case 25: // D_MN10A (Stallord Arena) + case 26: // D_MN10B (Death Sword Arena) + return 0x13; + case 27: // D_MN11 (Snowpeak Ruins) + case 28: // D_MN11A (Blizzeta Arena) + case 29: // D_MN11B (Darkhammer Arena) + return 0x14; + case 9: // D_MN06 (Temple of Time) + case 10: // D_MN06A (Armogohma Arena) + case 11: // D_MN06B (Darknut Arena) + return 0x15; + case 12: // D_MN07 (City in the Sky) + case 13: // D_MN07A (Argorok Arena) + case 14: // D_MN07B (Aeralfos Arena) + return 0x16; + case 15: // D_MN08 (Palace of Twilight) + case 16: // D_MN08A (Palace of Twilight Throne Room) + case 17: // D_MN08B (Phantom Zant Arena 1) + case 18: // D_MN08C (Phantom Zant Arena 2) + case 19: // D_MN08D (Zant Arenas) + return 0x17; + case 20: // D_MN09 (Hyrule Castle) + case 21: // D_MN09A (Hyrule Castle Throne Room) + case 22: // D_MN09B (Horseback Ganondorf Arena) + case 23: // D_MN09C (Dark Lord Ganondorf Arena) + return 0x18; + case 30: // D_SB00 (Ice Cavern) + case 31: // D_SB01 (Cave Of Ordeals) + case 32: // D_SB02 (Kakariko Gorge Cavern) + return 0x19; + case 33: // D_SB03 (Lake Hylia Cavern) + case 34: // D_SB04 (Goron Stockcave) + return 0x1A; + case 35: // D_SB05 (Grotto 1) + case 36: // D_SB06 (Grotto 2) + case 37: // D_SB07 (Grotto 3) + case 38: // D_SB08 (Grotto 4) + case 39: // D_SB09 (Grotto 5) + return 0x1B; + case 42: // F_SP102 (Title Screen / King Bulblin 1) + return 0xFF; + default: + randomizer::session::LogWarn(fmt::format("Failed to find Save Id for ID: {}" , id).c_str()); + return -1; + } +} + +int getStageSaveId(const char* stage) { + int id = getStageID(stage); + return getStageSaveId(id); +} + +bool tracker_isEventBit(u16 flag) { + return g_randomizerState.mTrackerTempEventFlag == flag || dComIfGs_isEventBit(flag); +} + +bool tracker_isStageSwitch(int stage, int flag) { + return dComIfGs_isStageSwitch(stage, flag) || + (g_randomizerState.mTrackerTempSwitchFlag.flag == flag && + g_randomizerState.mTrackerTempSwitchFlag.stage == stage); +} + +bool tracker_isStageItem(int stage, int flag) { + if (g_randomizerState.mTrackerTempItemFlag.flag == flag && + g_randomizerState.mTrackerTempItemFlag.stage == stage) { + return true; + } + + if (dComIfGp_getStageStagInfo() && stage == dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo())) { + return dComIfGs_isItem(flag, -1); + } else { + // Need to subtract 0x80 (MEMORY_ITEM constant in d_save.cpp) because the above function does it + return g_dComIfG_gameInfo.info.getSavedata().getSave(stage).getBit().isItem(flag - 0x80); + } +} \ No newline at end of file diff --git a/mods/randomizer/src/tools.h b/mods/randomizer/src/tools.h new file mode 100644 index 0000000000..bb1c8f79b6 --- /dev/null +++ b/mods/randomizer/src/tools.h @@ -0,0 +1,54 @@ +#pragma once + +#include "dolphin/types.h" +#include "../generator/logic/item_pool.hpp" +#include "SSystem/SComponent/c_xyz.h" +#include "SSystem/SComponent/c_sxyz.h" + +namespace randomizer::logic::location { +class Location; +} +namespace randomizer::logic::world { +class World; +} + +bool playerIsInRoomStage(s32 room, const char* stage); +void checkTransformFromWolf(); +u8 setNextWarashibeItem(); +void offWarashibeItem(u8 item); +int initCreatePlayerItem(u32 item, u32 flag, const cXyz* pos, int roomNo, const csXyz* angle, const cXyz* scale); +/* + * Returns the ID of the passed in stage name. If no stage name is passed in, the id of the current + * stage is returned + */ +int getStageID(const char* stage = NULL); +bool playerIsOnTitleScreen(); +u16 getItemMessageID(u8 itemId); +int numCompletedDungeons(); +int numFusedShadows(); +int numMirrorShards(); +int getTempleKeysFound(int saveId); + +/* + * Reads the current player inventory and returns an ItemPool that can be used for logic searches + * + */ +randomizer::logic::item_pool::ItemPool getSaveItemPool(randomizer::logic::world::World* world); + +/* + * Finds locations relevant flag in save (using its metadata) and checks if it's been set. + */ +bool isLocationObtained(randomizer::logic::location::Location* location); + +/* + * Pulls location item data from rando context using locations metadata. + */ +int getLocationItem(randomizer::logic::location::Location* location); + +// Used to get a stage's Area ID used for save flags +int getStageSaveId(int id); +int getStageSaveId(const char* stage); + +bool tracker_isEventBit(u16 flag); +bool tracker_isStageSwitch(int stage, int flag); +bool tracker_isStageItem(int stage, int flag); \ No newline at end of file diff --git a/mods/randomizer/src/utilities.h b/mods/randomizer/src/utilities.h new file mode 100644 index 0000000000..448ea50e24 --- /dev/null +++ b/mods/randomizer/src/utilities.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +inline u8 getAncientDocumentNum() { + // TODO + return 0; +} + +inline u8 getAreaKeyNum(int) { + // TODO + return 0; +} \ No newline at end of file diff --git a/mods/randomizer/src/verify_item_functions.cpp b/mods/randomizer/src/verify_item_functions.cpp new file mode 100644 index 0000000000..5a050de16b --- /dev/null +++ b/mods/randomizer/src/verify_item_functions.cpp @@ -0,0 +1,295 @@ +#include "verify_item_functions.h" + +#include + +#include "d/d_com_inf_game.h" +#include "d/d_item.h" +#include "d/d_item_data.h" +#include "item_ids.h" +#include "utilities.h" + +bool haveItem(u32 item) { + return checkItemGet((u8)item, 1); +} + +template +u32 getProgressiveItem(const std::array& progressiveItemsList) { + u32 listLength = N; + for (int i = 0; i < listLength; i++) + { + const u32 item = progressiveItemsList[i]; + if (!haveItem(item)) + { + return item; + } + } + + // All previous obtained, so return last upgrade + return progressiveItemsList[listLength - 1]; +} + +u32 getProgressiveSword() { + static constexpr std::array progressiveItemsList = { + dItemNo_Randomizer_WOOD_STICK_e, + dItemNo_Randomizer_SWORD_e, + dItemNo_Randomizer_MASTER_SWORD_e, + dItemNo_Randomizer_LIGHT_SWORD_e, + }; + + return getProgressiveItem(progressiveItemsList); +}; + +u32 getProgressiveBow() { + static constexpr std::array progressiveItemsList = { + dItemNo_Randomizer_BOW_e, + dItemNo_Randomizer_ARROW_LV2_e, + dItemNo_Randomizer_ARROW_LV3_e, + }; + + return getProgressiveItem(progressiveItemsList); +}; + +u32 getProgressiveSkill() { + static constexpr std::array progressiveItemsList = { + dItemNo_Randomizer_ENDING_BLOW_e, + dItemNo_Randomizer_SHIELD_ATTACK_e, + dItemNo_Randomizer_BACK_SLICE_e, + dItemNo_Randomizer_HELM_SPLITTER_e, + dItemNo_Randomizer_MORTAL_DRAW_e, + dItemNo_Randomizer_JUMP_STRIKE_e, + dItemNo_Randomizer_GREAT_SPIN_e, + }; + + return getProgressiveItem(progressiveItemsList); +}; + +u32 getProgressiveSkybook() { + if (!haveItem(dItemNo_Randomizer_ANCIENT_DOCUMENT2_e)) + { + if (haveItem(dItemNo_Randomizer_ANCIENT_DOCUMENT_e)) + { + if (getAncientDocumentNum() != 5) + { + return dItemNo_Randomizer_AIR_LETTER_e; + } + } + else + { + return dItemNo_Randomizer_ANCIENT_DOCUMENT_e; + } + } + + // All previous obtained, so return last upgrade + return dItemNo_Randomizer_ANCIENT_DOCUMENT2_e; +}; + +u32 getProgressiveKeyShard() { + static constexpr std::array progressiveItemsList = { + dItemNo_Randomizer_L2_KEY_PIECES1_e, + dItemNo_Randomizer_L2_KEY_PIECES2_e, + dItemNo_Randomizer_LV2_BOSS_KEY_e, + }; + + return getProgressiveItem(progressiveItemsList); +}; + +u32 getProgressiveMirrorShard() { + static constexpr std::array progressiveItemsList = { + dItemNo_Randomizer_MIRROR_PIECE_1_e, + dItemNo_Randomizer_MIRROR_PIECE_2_e, + dItemNo_Randomizer_MIRROR_PIECE_3_e, + dItemNo_Randomizer_MIRROR_PIECE_4_e, + }; + + return getProgressiveItem(progressiveItemsList); +}; + +u32 getProgressiveFusedShadow() { + static constexpr std::array progressiveItemsList = { + dItemNo_Randomizer_FUSED_SHADOW_1_e, + dItemNo_Randomizer_FUSED_SHADOW_2_e, + dItemNo_Randomizer_FUSED_SHADOW_3_e, + }; + + return getProgressiveItem(progressiveItemsList); +}; + +u8 getWarashibeItemCount() { + static constexpr u8 itemsList[] = { + dItemNo_Randomizer_LETTER_e, + dItemNo_Randomizer_BILL_e, + dItemNo_Randomizer_WOOD_STATUE_e, + dItemNo_Randomizer_IRIAS_PENDANT_e, + dItemNo_Randomizer_HORSE_FLUTE_e + }; + u8 count = 0; + + u32 listLength = sizeof(itemsList) / sizeof(itemsList[0]); + for (int i = 0; i < listLength; i++) + { + const u32 item = itemsList[i]; + if (haveItem(item)) + { + count++; + } + } + return count; +}; + +u32 verifyProgressiveItem(u32 item) +{ + switch (item) + { + case dItemNo_Randomizer_WOOD_STICK_e: + case dItemNo_Randomizer_SWORD_e: + case dItemNo_Randomizer_MASTER_SWORD_e: + case dItemNo_Randomizer_LIGHT_SWORD_e: + { + item = getProgressiveSword(); + break; + } + case dItemNo_Randomizer_BOW_e: + case dItemNo_Randomizer_ARROW_LV2_e: + case dItemNo_Randomizer_ARROW_LV3_e: + { + item = getProgressiveBow(); + break; + } + case dItemNo_WALLET_LV2_e: + case dItemNo_WALLET_LV3_e: + { + if (haveItem(dItemNo_WALLET_LV2_e)) + { + item = dItemNo_WALLET_LV3_e; + } + else + { + item = dItemNo_WALLET_LV2_e; + } + break; + } + case dItemNo_Randomizer_ENDING_BLOW_e: + case dItemNo_Randomizer_SHIELD_ATTACK_e: + case dItemNo_Randomizer_BACK_SLICE_e: + case dItemNo_Randomizer_HELM_SPLITTER_e: + case dItemNo_Randomizer_MORTAL_DRAW_e: + case dItemNo_Randomizer_JUMP_STRIKE_e: + case dItemNo_Randomizer_GREAT_SPIN_e: + { + item = getProgressiveSkill(); + break; + } + case dItemNo_Randomizer_HOOKSHOT_e: + case dItemNo_Randomizer_W_HOOKSHOT_e: + { + // If we have either clawshot, we want to return the double no matter what. + // We check for both in this case because the game unsets the clawshot flag once the double has been obtained. + if (haveItem(dItemNo_Randomizer_HOOKSHOT_e) || haveItem(dItemNo_Randomizer_W_HOOKSHOT_e)) + { + item = dItemNo_Randomizer_W_HOOKSHOT_e; + } + else + { + item = dItemNo_Randomizer_HOOKSHOT_e; + } + break; + } + case dItemNo_Randomizer_ANCIENT_DOCUMENT_e: + case dItemNo_Randomizer_AIR_LETTER_e: + case dItemNo_Randomizer_ANCIENT_DOCUMENT2_e: + { + item = getProgressiveSkybook(); + break; + } + case dItemNo_Randomizer_L2_KEY_PIECES1_e: + case dItemNo_Randomizer_L2_KEY_PIECES2_e: + case dItemNo_Randomizer_LV2_BOSS_KEY_e: + { + item = getProgressiveKeyShard(); + break; + } + case dItemNo_Randomizer_COPY_ROD_e: + case dItemNo_Randomizer_COPY_ROD_2_e: + { + if (!haveItem(dItemNo_Randomizer_COPY_ROD_e)) + { + item = dItemNo_Randomizer_COPY_ROD_e; + } + else + { + item = dItemNo_Randomizer_COPY_ROD_2_e; + } + break; + } + case dItemNo_Randomizer_FISHING_ROD_1_e: + case dItemNo_Randomizer_ZORAS_JEWEL_e: + { + if (haveItem(dItemNo_Randomizer_FISHING_ROD_1_e)) + { + item = dItemNo_Randomizer_ZORAS_JEWEL_e; + } + else + { + item = dItemNo_Randomizer_FISHING_ROD_1_e; + } + break; + } + case dItemNo_Randomizer_MIRROR_PIECE_1_e: + case dItemNo_Randomizer_MIRROR_PIECE_2_e: + case dItemNo_Randomizer_MIRROR_PIECE_3_e: + case dItemNo_Randomizer_MIRROR_PIECE_4_e: + { + item = getProgressiveMirrorShard(); + break; + } + case dItemNo_Randomizer_FUSED_SHADOW_1_e: + case dItemNo_Randomizer_FUSED_SHADOW_2_e: + case dItemNo_Randomizer_FUSED_SHADOW_3_e: + { + item = getProgressiveFusedShadow(); + break; + } + case dItemNo_Randomizer_ARROW_10_e: + case dItemNo_Randomizer_ARROW_20_e: + case dItemNo_Randomizer_ARROW_30_e: + { + if (!haveItem(dItemNo_Randomizer_BOW_e)) + { + item = dItemNo_Randomizer_BLUE_RUPEE_e; + } + break; + } + case dItemNo_Randomizer_BOMB_5_e: + case dItemNo_Randomizer_BOMB_10_e: + case dItemNo_Randomizer_BOMB_20_e: + case dItemNo_Randomizer_BOMB_30_e: + case dItemNo_Randomizer_WATER_BOMB_5_e: + case dItemNo_Randomizer_WATER_BOMB_10_e: + case dItemNo_Randomizer_WATER_BOMB_20_e: + case dItemNo_Randomizer_WATER_BOMB_30_e: + case dItemNo_Randomizer_BOMB_INSECT_5_e: + case dItemNo_Randomizer_BOMB_INSECT_10_e: + case dItemNo_Randomizer_BOMB_INSECT_20_e: + case dItemNo_Randomizer_BOMB_INSECT_30_e: + { + if (!haveItem(dItemNo_Randomizer_BOMB_BAG_LV1_e)) + { + item = dItemNo_Randomizer_BLUE_RUPEE_e; + } + break; + } + case dItemNo_Randomizer_PACHINKO_SHOT_e: + { + if (!haveItem(dItemNo_Randomizer_PACHINKO_e)) + { + item = dItemNo_Randomizer_BLUE_RUPEE_e; + } + break; + } + default: + { + break; + } + } + return item; +} \ No newline at end of file diff --git a/mods/randomizer/src/verify_item_functions.h b/mods/randomizer/src/verify_item_functions.h new file mode 100644 index 0000000000..f451a9c4a2 --- /dev/null +++ b/mods/randomizer/src/verify_item_functions.h @@ -0,0 +1,15 @@ +#pragma once + +#include "dolphin/types.h" + +bool haveItem(u32 item); +u32 getProgressiveSword(); +u32 getProgressiveBow(); +u32 getProgressiveSkill(); +u32 getProgressiveSkybook(); +u32 getProgressiveKeyShard(); +u32 getProgressiveMirrorShard(); +u32 getProgressiveFusedShadow(); +u8 getWarashibeItemCount(); +u32 verifyProgressiveItem(u32 item); +