initial rando generator hookup

This commit is contained in:
gymnast86
2026-04-08 00:35:02 -07:00
parent f58342ef12
commit 72bed7a1ba
142 changed files with 26578 additions and 2 deletions
+3 -2
View File
@@ -18,6 +18,7 @@ set(AURORA_ENABLE_CARD ON CACHE BOOL "Enable CARD API support" FORCE)
add_subdirectory(extern/aurora EXCLUDE_FROM_ALL)
add_subdirectory(libs/freeverb)
add_subdirectory(src/dusk/randomizer)
option(DUSK_BUILD_WARNINGS "If off, compiler warnings will be suppressed")
option(DUSK_SELECTED_OPT "If on, selected parts of the project will be compiled with optimizations on Debug, intending to make the game run at 30 FPS. Note for MSVC: you will need to remove '/RTC1' from your debug flags in CMake.")
@@ -94,7 +95,7 @@ message(STATUS "dusk: TP Version: ${DUSK_TP_VERSION}")
source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${JSYSTEM_FILES} ${JSYSTEM_DEBUG_FILES} ${REL_FILES})
source_group("dusk" FILES ${DUSK_FILES})
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 RANDOMIZER_ONLY=${RANDOMIZER_ONLY}
DUSK_TP_VERSION="${DUSK_TP_VERSION}" DUSK_GAME_NAME="${DUSK_GAME_NAME}" DUSK_GAME_VERSION="${DUSK_GAME_VERSION}")
set(GAME_INCLUDE_DIRS
@@ -160,7 +161,7 @@ target_link_libraries(game PUBLIC ${GAME_LIBS})
add_executable(dusk src/dusk/main.cpp)
target_compile_definitions(dusk PRIVATE TARGET_PC AVOID_UB=1 VERSION=0)
target_include_directories(dusk PRIVATE include)
target_link_libraries(dusk PRIVATE game aurora::main freeverb)
target_link_libraries(dusk PRIVATE game aurora::main freeverb randomizer)
add_custom_command(TARGET dusk POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
+77
View File
@@ -0,0 +1,77 @@
cmake_minimum_required(VERSION 3.16)
set(RANDOMIZER_ONLY "0" CACHE STRING "Runs only the randomizer generator")
set(RANDO_SAVE_PATH "${CMAKE_BINARY_DIR}/randomizer/")
add_compile_definitions(RANDO_SAVE_PATH="${RANDO_SAVE_PATH}"
LOGS_PATH="${RANDO_SAVE_PATH}logs/"
DATA_PATH="${RANDO_SAVE_PATH}data/")
if(WRITE_ERROR_LOG)
message("Error Log will be saved")
add_compile_definitions(WRITE_ERROR_LOG)
endif()
if(ENABLE_TIMING)
message("Some events will be timed")
add_compile_definitions(ENABLE_TIMING)
endif()
if(DRY_RUN)
message("Game patching will be skipped")
add_compile_definitions(DRY_RUN)
endif()
if(RANDO_DEBUG)
add_compile_definitions(RANDO_DEBUG)
endif()
if(LOGIC_TESTS)
message("Configuring for Logic Tests")
add_compile_definitions(LOGIC_TESTS)
if(TEST_COUNT)
message("Test Count: " ${TEST_COUNT})
add_compile_definitions(TEST_COUNT=${TEST_COUNT})
endif()
add_compile_definitions(SETTINGS_PATH="${RANDO_SAVE_PATH}randomizer_settings.yaml.test" PREFERENCES_PATH="${RANDO_SAVE_PATH}randomizer_preferences.yaml.test")
else()
add_compile_definitions(SETTINGS_PATH="${RANDO_SAVE_PATH}randomizer_settings.yaml" PREFERENCES_PATH="${RANDO_SAVE_PATH}randomizer_preferences.yaml")
endif()
string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE)
add_compile_definitions(SOURCE_PATH_SIZE=${SOURCE_PATH_SIZE})
# Put data files together for easier manipulation
file(COPY "./data/" DESTINATION "${CMAKE_BINARY_DIR}/randomizer/data/" REGEX "^.*example.*$" EXCLUDE) # World, macros, and location info
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
)
FetchContent_MakeAvailable(yaml-cpp)
message(STATUS "randomizer: Fetching base64pp")
FetchContent_Declare(
base64pp
GIT_REPOSITORY https://github.com/matheusgomes28/base64pp.git
GIT_TAG v0.2.0-rc0
)
FetchContent_MakeAvailable(base64pp)
message(STATUS "randomizer: Fetching zlib-ng")
FetchContent_Declare(
zlib-ng
GIT_REPOSITORY https://github.com/zlib-ng/zlib-ng.git
GIT_TAG 2.3.3
)
FetchContent_MakeAvailable(zlib-ng)
file(GLOB_RECURSE CODE_SOURCES CONFIGURE_DEPENDS "*.cpp" "*.hpp")
add_library(randomizer OBJECT ${CODE_SOURCES})
target_link_libraries(randomizer yaml-cpp::yaml-cpp zlib base64pp)
File diff suppressed because it is too large Load Diff
+791
View File
@@ -0,0 +1,791 @@
# TODO: Add relevant item data
# 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 expendible. Will be placed completely randomly.
# WARNING: ITEM IDS ARE CURRENTLY NOT CORRECT. FIX BEFORE USING FOR ACTUAL GAME PATCHING
- Name: Green Rupee
Importance: Junk
Id: 0x00
- Name: Blue Rupee
Importance: Junk
Id: 0x01
- Name: Yellow Rupee
Importance: Junk
Id: 0x02
- Name: Red Rupee
Importance: Junk
Id: 0x03
- Name: Purple Rupee
Importance: Junk
Id: 0x04
- Name: Orange Rupee
Importance: Junk
Id: 0x05
- Name: Silver Rupee
Importance: Junk
Id: 0x06
- Name: Purple Rupee Links House
Importance: Junk
Id: 0x07
- Name: Bombs 5
Importance: Junk
Id: 0x08
- Name: Bombs 10
Importance: Junk
Id: 0x09
- Name: Bombs 20
Importance: Junk
Id: 0x0A
- Name: Bombs 30
Importance: Junk
Id: 0x0B
- Name: Arrows 10
Importance: Junk
Id: 0x0C
- Name: Arrows 20
Importance: Junk
Id: 0x0D
- Name: Arrows 30
Importance: Junk
Id: 0x0E
- Name: Seeds 50
Importance: Junk
Id: 0x0F
- Name: Water Bombs 5
Importance: Junk
Id: 0x10
- Name: Water Bombs 10
Importance: Junk
Id: 0x11
- Name: Water Bombs 15
Importance: Junk
Id: 0xAE
- Name: Water Bombs 20
Importance: Junk
Id: 0x12
- Name: Water Bombs 30
Importance: Junk
Id: 0x13
- Name: Bomblings 5
Importance: Junk
Id: 0x14
- Name: Bomblings 10
Importance: Junk
Id: 0x15
- Name: Shadow Crystal
Importance: Major
Id: 0x16
- Name: Progressive Sword
Importance: Major
Id: 0x17
- Name: Asheis Sketch
Importance: Major
Id: 0x18
- Name: Aurus Memo
Importance: Major
Id: 0x19
- Name: Ball and Chain
Importance: Major
Id: 0x1A
- Name: Gale Boomerang
Importance: Major
Id: 0x1B
- Name: Bomb Bag
Importance: Major
Id: 0x1C
- Name: Iron Boots
Importance: Major
Id: 0x1D
- Name: Lantern
Importance: Major
Id: 0x1E
- Name: Progressive Bow
Importance: Major
Id: 0x1F
- Name: Progressive Clawshot
Importance: Major
Id: 0x20
- Name: Progressive Dominion Rod
Importance: Major
Id: 0x21
- Name: Progressive Fishing Rod
Importance: Major
Id: 0x22
- Name: Progressive Sky Book
Importance: Major
Id: 0x23
- Name: Slingshot
Importance: Major
Id: 0x24
- Name: Spinner
Importance: Major
Id: 0x25
- Name: Zora Armor
Importance: Major
Id: 0x26
- Name: Progressive Fused Shadow
Importance: Major
Id: 0x27
- Name: Progressive Mirror Shard
Importance: Major
Id: 0x28
- Name: Forest Temple Small Key
Importance: Major
Id: 0x29
Dungeon Small Key: Forest Temple
- Name: Goron Mines Small Key
Importance: Major
Id: 0x2A
Dungeon Small Key: Goron Mines
- Name: Lakebed Temple Small Key
Importance: Major
Id: 0x2B
Dungeon Small Key: Lakebed Temple
- Name: Arbiters Grounds Small Key
Importance: Major
Id: 0x2C
Dungeon Small Key: Arbiters Grounds
- Name: Snowpeak Ruins Small Key
Importance: Major
Id: 0x2D
Dungeon Small Key: Snowpeak Ruins
- Name: Ordon Pumpkin
Importance: Major
Id: 0x2E
- Name: Ordon Cheese
Importance: Major
Id: 0x2F
- Name: Temple of Time Small Key
Importance: Major
Id: 0x30
Dungeon Small Key: Temple of Time
- Name: City in the Sky Small Key
Importance: Major
Id: 0x31
Dungeon Small Key: City in the Sky
- Name: Palace of Twilight Small Key
Importance: Major
Id: 0x32
Dungeon Small Key: Palace of Twilight
- Name: Hyrule Castle Small Key
Importance: Major
Id: 0x33
Dungeon Small Key: Hyrule Castle
- Name: North Faron Woods Gate Key
Importance: Major
Id: 0x34
- Name: Gate Keys
Importance: Major
Id: 0x35
- Name: Gerudo Desert Bulblin Camp Key
Importance: Major
Id: 0x36
- Name: Forest Temple Big Key
Importance: Major
Id: 0x37
Dungeon Big Key: Forest Temple
- Name: Goron Mines Key Shard
Importance: Major
Id: 0x38
Dungeon Big Key: Goron Mines
- Name: Lakebed Temple Big Key
Importance: Major
Id: 0x39
Dungeon Big Key: Lakebed Temple
- Name: Arbiters Grounds Big Key
Importance: Major
Id: 0x3A
Dungeon Big Key: Arbiters Grounds
- Name: Snowpeak Ruins Bedroom Key
Importance: Major
Id: 0x3B
Dungeon Big Key: Snowpeak Ruins
- Name: Temple of Time Big Key
Importance: Major
Id: 0x3C
Dungeon Big Key: Temple of Time
- Name: City in the Sky Big Key
Importance: Major
Id: 0x3D
Dungeon Big Key: City in the Sky
- Name: Palace of Twilight Big Key
Importance: Major
Id: 0xA9
Dungeon Big Key: Palace of Twilight
- Name: Hyrule Castle Big Key
Importance: Major
Id: 0x3E
Dungeon Big Key: Hyrule Castle
- Name: Forest Temple Compass
Importance: Junk
Id: 0x3F
Dungeon Compass: Forest Temple
- Name: Goron Mines Compass
Importance: Junk
Id: 0x40
Dungeon Compass: Goron Mines
- Name: Lakebed Temple Compass
Importance: Junk
Id: 0x41
Dungeon Compass: Lakebed Temple
- Name: Arbiters Grounds Compass
Importance: Junk
Id: 0x42
Dungeon Compass: Arbiters Grounds
- Name: Snowpeak Ruins Compass
Importance: Junk
Id: 0x43
Dungeon Compass: Snowpeak Ruins
- Name: Temple of Time Compass
Importance: Junk
Id: 0x44
Dungeon Compass: Temple of Time
- Name: City in the Sky Compass
Importance: Junk
Id: 0x45
Dungeon Compass: City in the Sky
- Name: Palace of Twilight Compass
Importance: Junk
Id: 0xAA
Dungeon Compass: Palace of Twilight
- Name: Hyrule Castle Compass
Importance: Junk
Id: 0x46
Dungeon Compass: Hyrule Castle
- Name: Forest Temple Dungeon Map
Importance: Junk
Id: 0x47
Dungeon Map: Forest Temple
- Name: Goron Mines Dungeon Map
Importance: Junk
Id: 0x48
Dungeon Map: Goron Mines
- Name: Lakebed Temple Dungeon Map
Importance: Junk
Id: 0x49
Dungeon Map: Lakebed Temple
- Name: Arbiters Grounds Dungeon Map
Importance: Junk
Id: 0x4A
Dungeon Map: Arbiters Grounds
- Name: Snowpeak Ruins Dungeon Map
Importance: Junk
Id: 0x4B
Dungeon Map: Snowpeak Ruins
- Name: Temple of Time Dungeon Map
Importance: Junk
Id: 0x4C
Dungeon Map: Temple of Time
- Name: City in the Sky Dungeon Map
Importance: Junk
Id: 0x4D
Dungeon Map: City in the Sky
- Name: Palace of Twilight Dungeon Map
Importance: Junk
Id: 0xAB
Dungeon Map: Palace of Twilight
- Name: Hyrule Castle Dungeon Map
Importance: Junk
Id: 0x4E
Dungeon Compass: Hyrule Castle
- Name: Male Beetle
Importance: Major
Id: 0x4F
- Name: Female Beetle
Importance: Major
Id: 0x50
- Name: Male Butterfly
Importance: Major
Id: 0x51
- Name: Female Butterfly
Importance: Major
Id: 0x52
- Name: Male Stag Beetle
Importance: Major
Id: 0x53
- Name: Female Stag Beetle
Importance: Major
Id: 0x54
- Name: Male Grasshopper
Importance: Major
Id: 0x55
- Name: Female Grasshopper
Importance: Major
Id: 0x56
- Name: Male Phasmid
Importance: Major
Id: 0x57
- Name: Female Phasmid
Importance: Major
Id: 0x58
- Name: Male Pill Bug
Importance: Major
Id: 0x59
- Name: Female Pill Bug
Importance: Major
Id: 0x5A
- Name: Male Mantis
Importance: Major
Id: 0x5B
- Name: Female Mantis
Importance: Major
Id: 0x5C
- Name: Male Ladybug
Importance: Major
Id: 0x5D
- Name: Female Ladybug
Importance: Major
Id: 0x5E
- Name: Male Snail
Importance: Major
Id: 0x5F
- Name: Female Snail
Importance: Major
Id: 0x60
- Name: Male Dragonfly
Importance: Major
Id: 0x61
- Name: Female Dragonfly
Importance: Major
Id: 0x62
- Name: Male Ant
Importance: Major
Id: 0x63
- Name: Female Ant
Importance: Major
Id: 0x64
- Name: Male Dayfly
Importance: Major
Id: 0x65
- Name: Female Dayfly
Importance: Major
Id: 0x66
- Name: Ordon Shield
Importance: Major
Id: 0x67
- Name: Wooden Shield
Importance: Junk
Id: 0x68
- Name: Hylian Shield
Importance: Major
Id: 0x69
- Name: Magic Armor
Importance: Major
Id: 0x6A
- Name: Poe Soul
Importance: Major
Id: 0x6B
- Name: Piece of Heart
Importance: Junk
Id: 0x6C
- Name: Heart Container
Importance: Junk
Id: 0x6D
- Name: Progressive Hidden Skill
Importance: Major
Id: 0x6E
- Name: Progressive Wallet
Importance: Major
Id: 0x6F
- Name: Giant Bomb Bag
Importance: Minor
Id: 0x70
- Name: Empty Bottle
Importance: Major
Id: 0x71
- Name: Bottle with Lantern Oil
Importance: Major
Id: 0x72
- Name: Bottle with Half Milk
Importance: Major
Id: 0x73
- Name: Bottle with Great Fairies Tears
Importance: Major
Id: 0x74
- Name: Hawkeye
Importance: Minor
Id: 0x75
- Name: Horse Call
Importance: Minor
Id: 0x76
# - Name: Ghost Lantern
# Importance: Minor
# Id: 0xAF
- Name: Foolish Item
Importance: Junk
Id: 0x77
# - 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
- Name: Renados Letter
Importance: Major
Id: 0xA9
- Name: Ilias Charm
Importance: Major
Id: 0xAA
- Name: Invoice
Importance: Major
Id: 0xAB
- Name: Wooden Statue
Importance: Major
Id: 0xAC
# 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: Red Potion Shop
Importance: Junk
Id: 0x106
- Name: Fairy Tears
Importance: Junk
Id: 0x107
File diff suppressed because it is too large Load Diff
+262
View File
@@ -0,0 +1,262 @@
# 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 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
# 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'
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: 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 Damage_Multiplier != OHKO or
(Can_Use_Bottled_Fairy and count(Empty_Bottle, 2))
Can Survive Three Bonks: Bonks_Do_Damage == Off or 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)
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, 12)
Can Complete Eldin Twilight: Eldin_Twilight_Cleared == On or count(Eldin_Twilight_Tear, 12)
Can Complete Lanayru Twilight: Lanayru_Twilight_Cleared == On or count(Lanayru_Twilight_Tear, 12)
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_Castle_Requirements == Open or
(Hyrule_Castle_Requirements == Vanilla and 'Can_Complete_Palace_of_Twilight') or
(Hyrule_Castle_Requirements == Fused_Shadows and count(Progressive_Fused_Shadow, 3)) or
(Hyrule_Castle_Requirements == Mirror_Shards and count(Progressive_Mirror_Shard, 4)) or
(Hyrule_Castle_Requirements == All_Dungeons and Can_Complete_All_Dungeons)
# WARP PORTALS
Ordon Spring Warp Portal: Can_Complete_Prologue
South Faron Woods Warp Portal: Can_Complete_Faron_Twilight
North Faron Woods Warp Portal: Can_Complete_Faron_Twilight
Sacred Grove Warp Portal: Sacred_Grove_Does_Not_Require_Skull_Kid == On or (Has_Sword_For_Temple_of_Time and 'Can_Access_Sacred_Grove_Lower' and Can_Defeat_Shadow_Beast)
Kakariko Gorge Warp Portal: Can_Complete_Eldin_Twilight
Kakariko Village Warp Portal: Can_Complete_Eldin_Twilight
Death Mountain Warp Portal: Can_Complete_Eldin_Twilight
Bridge of Eldin Warp Portal: Can_Defeat_Shadow_Beast and 'Can_Access_Eldin_Field_North_of_Bridge'
Castle Town Warp Portal: Can_Complete_Lanayru_Twilight
Lake Hylia Warp Portal: Can_Complete_Lanayru_Twilight
Upper Zoras River Warp Portal: (Sword or (Can_Defeat_Shadow_Beast and Transform_Anywhere == On)) and 'Can_Access_Upper_Zoras_River'
Zoras Domain Warp Portal: Can_Complete_Lanayru_Twilight
Snowpeak Warp Portal: Snowpeak_Does_Not_Require_Reekfish_Scent == On or (Can_Defeat_Shadow_Beast and 'Can_Access_Snowpeak_Summit_Upper')
Gerudo Desert Warp Portal: Can_Defeat_Shadow_Beast and 'Can_Access_Gerudo_Desert_Cave_of_Ordeals_Plateau'
Mirror Chamber Warp Portal: Can_Defeat_Shadow_Beast and 'Can_Access_Mirror_Chamber_Upper'
Can Talk to Springwater Goron: Nothing # Change later
+660
View File
@@ -0,0 +1,660 @@
# NOTE: Initially copied from SSHDR. Some commented out settings may reference skyward sword
######################
## 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 Castle Requirements
Default Option: Vanilla
Options:
- Open: "The barrier around Hyrule Castle is dispelled from the beginning."
- Fused Shadows: "The player must collect all 3 Fused Shadows."
- Mirror Shards: "The player must collect all 4 Mirror Shards."
- All Dungeons: "The player must complete all dungeons."
- Vanilla: "The player must complete Palace of Twilight."
- Name: Palace of Twilight Requirements
Default Option: Vanilla
Options:
- Open: "The barrier around Hyrule Castle is dispelled from the beginning."
- 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
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."
######################
## Item Pool ##
######################
- Name: Golden Bugs
Default Option: "Off"
Options:
- "Off": "The Golden Bug locations across Hyrule will not be randomized."
- "On": "The Golden Bug locations across Hyrule will be randomized."
- Name: Sky Characters
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Gifts From NPCs
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Shop Items
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Hidden Skills
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Poe Souls
Default Option: Vanilla
Options:
- Vanilla: description
- Overworld: description
- Dungeon: description
- All: description
- 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`."
######################
## 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: Dungeon Rewards Can Be Anywhere
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: No Small Keys on Bosses
Default Option: "Off"
Options:
- "Off": description
- "On": description
# - Name: required_dungeons
# Default Option: 2
# Pretty Name: Required Dungeons
# Pretty Options:
# - 0-7
# Options:
# - 0-7: "Choose how many dungeons will have required items on their dungeon completion checks. Triforces, then swords, then other progress items are chosen, in this order."
- 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 be barren."
######################
## Timesavers ##
######################
- Name: Skip Prologue
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Faron Twilight Cleared
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Eldin Twilight Cleared
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Lanayru Twilight Cleared
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Skip Midna's Desparate Hour
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Skip Minor Cutscenes
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Skip Major Cutscenes
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Fast Iron Boots
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Quick Transform
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Instant Message Text
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Unlock Map Regions
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Increase Spinner Speed
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Open Door of Time
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
######################
# Additional Settings#
######################
- Name: Starting Form
Tracker Important: True
Default Option: Human
Options:
- Human: Start as Human Link
- Wolf: Start as Wolf Link
- Name: Transform Anywhere
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Increase Wallet Capacity
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Shops Display The Replaced Item
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Bonks Do Damage
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- 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."
- Name: Damage Multiplier
Default Option: Vanilla
Options:
- Vanilla: description
- Double: description
- Triple: description
- Quadruple: description
- OHKO: Any damage taken will kill link
- Name: Starting Time of Day
Default Option: Noon
Options:
- Morning: description
- Noon: description
- Evening: description
- Night: description
#############################
# Dungeon Entrance Settings #
#############################
- Name: Lakebed Does Not Require Water Bombs
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Arbiters Does Not Require Bulblin Camp
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Snowpeak Does Not Require Reekfish Scent
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Sacred Grove Does Not Require Skull Kid
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: City Does Not Require Filled Skybook
Tracker Important: True
Default Option: "Off"
Options:
- "Off": description
- "On": description
- Name: Goron Mines Entrance
Tracker Important: True
Default Option: Closed
Options:
- Closed: description
- No Wrestling: description
- Open: description
- Name: Temple of Time Sword Requirement
Tracker Important: True
Default Option: None
Options:
- None: description
- Wooden Sword: description
- Ordon Sword: description
- Master Sword: description
- Light Sword: description
# - Name: chest_type_matches_contents
# Default Option: "Off"
# Pretty Name: Chest Type Matches Contents
# Pretty Options:
# - "Off"
# - "Only Dungeon Items"
# - "All Contents"
# Options:
# - "Off": "Each chest will have the same type as it would in the vanilla game."
# - "only_dungeon_items": "Each chest will have the same type as it would in the vanilla game, unless it contains a dungeon item. If a chest contains a Boss Key, it will appear as a fancy chest. If it contains a Small Key, it will appear as a big blue chest. If it contains a Map, it will appear as a brown, wooden chest."
# - "all_contents": "Each chest will have a different type based on the item inside. Important items will appear in big blue chests. Big Keys will appear in fancy chests. Other items will appear in brown, wooden chests."
# - Name: small_keys_in_fancy_chests
# Default Option: "Off"
# Pretty Name: Small Keys in Fancy Chests
# Pretty Options:
# - "Off"
# - "On"
# Options:
# - "Off": "When Chest Type Matches Contents is not off, Small Keys will appear in big blue chests."
# - "On": "When Chest Type Matches Contents is not off, Small Keys will appear in fancy chests."
# - Name: Path Hints
# Default Option: 3
# Options:
# - 0-7: "Select the number of path hints you would like to generate."
# - Name: barren_hints
# Default Option: 2
# Pretty Name: Barren Hints
# Pretty Options:
# - 0-7
# Options:
# - 0-7: "Select the number of barren hints you would like to generate."
# - Name: location_hints
# Default Option: 7 # assumes always_hints is on
# Pretty Name: Location Hints
# Pretty Options:
# - 0-7
# Options:
# - 0-7: "Select the number of location hints you would like to generate."
# - Name: item_hints
# Default Option: 3
# Pretty Name: Item Hints
# Pretty Options:
# - 0-7
# Options:
# - 0-7: "Select the number of item hints you would like to generate."
# - Name: gossip_stone_hints
# Default Option: "Off"
# Pretty Name: Gossip Stone Hints
# Pretty Options:
# - "Off"
# - "On"
# Options:
# - "Off": "Hints will not be placed on Gossip Stones."
# - "On": "Hints will be placed on Gossip Stones and can be seen by talking to them."
# - Name: cryptic_hint_text
# Default Option: "Off"
# Pretty Name: Cryptic Hint Text
# Pretty Options:
# - "Off"
# - "On"
# Options:
# - "Off": "Item and location hints will use the normal, clear Names for their items and locations."
# - "On": "Item and location hints will use cryptic text for their items and locations."
# - Name: always_hints
# Default Option: "On"
# Pretty Name: Prioritize Remote Location Hints
# Pretty Options:
# - "Off"
# - "On"
# Options:
# - "Off": "All locations will be treated equally for location hints."
# - "On": "Certain locations which are time-consuming to check will be given priority for location hints."
- 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."
# - Name: random_bottle_contents
# Default Option: "Off"
# Pretty Name: Random Bottle Contents
# Pretty Options:
# - "Off"
# - "On"
# Options:
# - "Off": "There will be 3 Empty Bottles, 1 Revitalizing Potion, and 1 Bottle of Mushroom Spores in the item pool."
# - "On": "There will be 5 Bottles in the item pool - each with random contents (e.g. Heart Potion, Hot Pumpkin Soup, Sacred Water, etc...)."
# - Name: trappable_items
# Default Option: "major_items"
# Pretty Name: Trappable Items
# Pretty Options:
# - "Major Items"
# - "Non Major Items"
# - "Any Items"
# Options:
# - "major_items": "Traps can mimic any major item that could be necessary to beat the game."
# - "non_major_items": "Traps can only mimic non-major items that cannot be necessary to beat the game."
# - "any_items": "Traps can mimic any item."
########################
## Preference Options ##
########################
# - Name: language
# type: Preference
# Default Option: english_us
# Pretty Name: In-Game Language
# Pretty Options:
# # - Chinese
# - Dutch
# - English (GB)
# - English (US)
# - French (FR)
# - French (US)
# - German
# - Italian
# # - Japanese
# # - Korean
# # - Russian
# - Spanish (ES)
# - Spanish (US)
# # - Taiwanese
# Options:
# # - chinese: "Status: Broken. The randomized game will use the Chinese language files."
# - dutch: "Status: Incomplete. The randomized game will use the Dutch language files."
# - english_gb: "Status: Complete. The randomized game will use the British English language files."
# - english_us: "Status: Complete. The randomized game will use the American English language files."
# - french_fr: "Status: Incomplete. The randomized game will use the French language files."
# - french_us: "Status: Incomplete. The randomized game will use the French language files."
# - german: "Status: Incomplete. The randomized game will use the German language files."
# - italian: "Status: Incomplete. The randomized game will use the Italian language files."
# # - japanese: "Status: Broken. The randomized game will use the Japanese language files."
# # - korean: "Status: Broken. The randomized game will use the Korean language files."
# # - russian: "Status: Broken. The randomized game will use the Russian language files."
# - spanish_es: "Status: Incomplete. The randomized game will use the Spanish language files."
# - spanish_us: "Status: Incomplete. The randomized game will use the Spanish language files."
# # - taiwanese: "Status: Broken. The randomized game will use the Taiwanese language files."
# - Name: tunic_swap
# type: Preference
# Default Option: "Off"
# Pretty Name: Tunic Swap
# Pretty Options:
# - "Off"
# - "On"
# Options:
# - "Off": "Link will be dressed in his green knight's uniform."
# - "On": "Link will be dressed in his Skyloft outfit seen at the start of the vanilla game."
- Name: Remove Enemy Music
Type: Preference
Default Option: "Off"
Options:
- "Off": "The background music will be interrupted by enemy drums when you get near to an enemy. Additionally, the intense music when Scaldera and Tentalus are vulnerable will play as normal."
- "On": "The background music will continue to play when you get near to enemies. Additionally, the intense music when Scaldera and Tentalus are vulnerable will not play and the background music will continue uninterrupted."
# - Name: low_health_beeping_speed
# type: Preference
# Default Option: normal
# Pretty Name: Low Health Beeping Speed
# Pretty Options:
# - Very Fast
# - Fast
# - Normal
# - Slow
# - Very Slow
# - No Beeping
# - No Beeping or Flashing
# Options:
# - very_fast: "The game will play the low health beep sound every time Link flashes red."
# - fast: "The game will play the low health beep sound every other time Link flashes red."
# - normal: "The game will play the low health beep sound every 3rd time Link flashes red."
# - slow: "The game will play the low health beep sound every 6th time Link flashes red."
# - very_slow: "The game will play the low health beep sound every 12th time Link flashes red."
# - no_beeping: "The game will not play the low health beep sound. Link will still flash red."
# - no_beeping_or_flashing: "The game will not play the low health beep sound. Link will not flash red either."
###############
## Shortcuts ##
###############
#############################
## Logic settings (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."
#######################################
## Extra Starting Inventory Settings ##
#######################################
# - Name: Random Starting Item Count
# Default Option: 0
# Options:
# - 0-7: "Select the number of randomly chosen items you would like to start with. Only a selection of the most useful items are able to be chosen to maximize the benefit of this setting."
# - Name: Starting Hearts
# Default Option: 3
# Options:
# - 3-20: "Select the number of hearts you want to start with."
@@ -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 Castle 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
Transform Anywhere: Random
Trap Item Frequency: Random
Unlock Map Regions: Random
Unrequired Dungeons Are Barren: Random
trappable_items: major_items
Starting Inventory:
{}
Excluded Locations:
[]
@@ -0,0 +1,4 @@
Seed: TESTTESTTEST
Small Keys: Any Dungeon
Big Keys: Any Dungeon
Maps and Compasses: Any Dungeon
@@ -0,0 +1,4 @@
Seed: TESTTESTTEST
Small Keys: Anywhere
Big Keys: Anywhere
Maps and Compasses: Anywhere
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Dungeon Rewards Can Be Anywhere: On
@@ -0,0 +1,5 @@
Seed: TESTTESTTEST
Bonks Do Damage: On
Damage Multiplier: OHKO
Eldin Twilight Cleared: On
Lanayru Twilight Cleared: On
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Eldin Twilight Cleared: On
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Lanayru Twilight Cleared: On
@@ -0,0 +1 @@
Seed: TESTTESTTEST
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Faron Twilight Cleared: On
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Gifts From NPCs: On
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Golden Bugs: On
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Hidden Skills: On
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Hyrule Castle Requirements: All Dungeons
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Hyrule Castle Requirements: Fused Shadows
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Hyrule Castle Requirements: Mirror Shards
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Hyrule Castle Requirements: Open
@@ -0,0 +1,4 @@
Seed: TESTTESTTEST
Small Keys: Keysy
Big Keys: Keysy
Maps and Compasses: Start With
@@ -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
@@ -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"]]
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Faron Woods Logic: Open
@@ -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
@@ -0,0 +1,4 @@
Seed: TESTTESTTEST
Small Keys: Overworld
Big Keys: Overworld
Maps and Compasses: Overworld
@@ -0,0 +1,4 @@
Seed: TESTTESTTEST
Small Keys: Own Dungeon
Big Keys: Own Dungeon
Maps and Compasses: Own Dungeon
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Palace of Twilight Requirements: Fused Shadows
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Palace of Twilight Requirements: Mirror Shards
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Palace of Twilight Requirements: Open
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Poe Souls: All
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Poe Souls: Dungeon
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Poe Souls: Overworld
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Randomize Boss Entrances: On
@@ -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
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Randomize Dungeon Entrances: On
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Randomize Grotto Entrances: On
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Item Scarcity: Minimal
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Item Scarcity: Plentiful
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Shop Items: On
@@ -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
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Skip Midna's Desparate Hour: On
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Skip Prologue: On
@@ -0,0 +1,2 @@
Seed: TESTTESTTEST
Sky Characters: On
@@ -0,0 +1,3 @@
Seed: TESTTESTTEST
Unrequired Dungeons Are Barren: On
Hyrule Castle Requirements: Mirror Shards
@@ -0,0 +1,4 @@
Seed: TESTTESTTEST
Starting Form: Wolf
Skip Prologue: On
Faron Woods Logic: Open
+163
View File
@@ -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_<Area_Name>'
# 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
# - <item>
# - Human_Link
# - Wolf_Link
# - Day
# - Night
# - count(<item>, <number>)
# - 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_Warp_Portal and (Unlock_Map_Regions == On or 'Ordona_Province_Map_Sector')
South Faron Woods Warp Portal: South_Faron_Woods_Warp_Portal and (Unlock_Map_Regions == On or 'Faron_Province_Map_Sector')
North Faron Woods Warp Portal: North_Faron_Woods_Warp_Portal and (Unlock_Map_Regions == On or 'Faron_Province_Map_Sector')
Sacred Grove Warp Portal: Sacred_Grove_Warp_Portal and (Unlock_Map_Regions == On or 'Faron_Province_Map_Sector')
Kakariko Gorge Warp Portal: Kakariko_Gorge_Warp_Portal and (Unlock_Map_Regions == On or 'Eldin_Province_Map_Sector')
Kakariko Village Warp Portal: Kakariko_Village_Warp_Portal and (Unlock_Map_Regions == On or 'Eldin_Province_Map_Sector')
Death Mountain Warp Portal: Death_Mountain_Warp_Portal and (Unlock_Map_Regions == On or 'Eldin_Province_Map_Sector')
Bridge of Eldin Warp Portal: Bridge_of_Eldin_Warp_Portal and (Unlock_Map_Regions == On or 'Eldin_Province_Map_Sector')
Castle Town Warp Portal: Castle_Town_Warp_Portal and (Unlock_Map_Regions == On or 'Lanayru_Province_Map_Sector')
Lake Hylia Warp Portal: Lake_Hylia_Warp_Portal and (Unlock_Map_Regions == On or 'Lanayru_Province_Map_Sector')
Upper Zoras River Warp Portal: Upper_Zoras_River_Warp_Portal and (Unlock_Map_Regions == On or 'Lanayru_Province_Map_Sector')
Zoras Domain Warp Portal: Zoras_Domain_Warp_Portal and (Unlock_Map_Regions == On or 'Lanayru_Province_Map_Sector')
Snowpeak Warp Portal: Snowpeak_Warp_Portal and (Unlock_Map_Regions == On or 'Snowpeak_Province_Map_Sector')
Gerudo Desert Warp Portal: Gerudo_Desert_Warp_Portal and 'Desert_Province_Map_Sector'
Mirror Chamber Warp Portal: Mirror_Chamber_Warp_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
@@ -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
@@ -0,0 +1,404 @@
# 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 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
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
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
# 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
@@ -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
@@ -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: "'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
@@ -0,0 +1,214 @@
# 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
- 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: Nothing
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
@@ -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
@@ -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
Mirror Chamber 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: Light_Sword or 'Can_Place_East_Sol_at_Palace_of_Twilight_Entrance'
Palace of Twilight East Wing First Room West Alcove: 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
@@ -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
@@ -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
@@ -0,0 +1,594 @@
# KAKARIKO GORGE
- Name: Kakariko Gorge
Map Sector: Eldin Province
Region: Kakariko Gorge
Twilight: Eldin
Can Warp: True
Locations:
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 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:
Eldin Spring Underwater Chest: Can_Smash
Kakariko Village Bomb Rock Spire Heart Piece: Bombs and Gale_Boomerang
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: "'Howl_at_Snowpeak_Mountain_Howling_Stone'"
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:
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 Buy Wooden Shield: "'Can_Farm_Lots_of_Rupees'"
Can Fund Malo Mart: "'Can_Farm_Lots_of_Rupees'"
Locations:
Kakariko Village Malo Mart Hylian Shield: "'Can_Farm_Lots_of_Rupees'"
Kakariko Village Malo Mart Hawkeye: "'Can_Farm_Lots_of_Rupees'"
Kakariko Village Malo Mart Red Potion: "'Can_Farm_Lots_of_Rupees'"
Kakariko Village Malo Mart Wooden Shield: "'Can_Farm_Lots_of_Rupees'"
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 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
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
Locations:
Talo Sharpshooting: Day and Can_Climb_Ladders and Bow and 'Can_Complete_Goron_Mines'
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 Trail Twilit Insect on Wall: Can_Defeat_Eldin_Twilit_Insect
Death Mountain Trail Twilit Insect in Hot Spring: Can_Defeat_Eldin_Twilit_Insect
Exits:
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')
Death Mountain Trail: 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
Exits:
Death Mountain Sumo Hall: 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 'Can_Fund_Malo_Mart'"
Locations:
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
Eldin Field Near Castle Town: "'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: "'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 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: "'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
@@ -0,0 +1,385 @@
# 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:
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 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 Twilit Insect 1: Can_Defeat_Faron_Twilit_Insect
Faron Woods Coros House 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: 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: 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
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 Woods Deku Baba Chest: Nothing
Faron Woods Golden Wolf: Nothing
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_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 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
@@ -0,0 +1,188 @@
# 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 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: Nothing
- Name: Mirror Chamber Upper
Map Sector: Desert Province
Region: Mirror Chamber
Can Warp: True
Exits:
Mirror Chamber Portal: Can_Defeat_Shadow_Beast 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: Mirror Chamber 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 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
@@ -0,0 +1,728 @@
# 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 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
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:
Hyrule Field Amphitheater Owl Statue Chest: Restored_Dominion_Rod
Hyrule Field Amphitheater Owl Statue Sky Character: Restored_Dominion_Rod
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
(Increase_Wallet_Capacity == On or 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: Invoice
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
- 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: Outside 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 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: Outside 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: Outside 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: Lake Hylia Bridge
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 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: Lake Hylia Bridge
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 Twilit Insect Between Bridges: Can_Defeat_Lanayru_Twilit_Insect
Lake Hylia Burrowing Twilit Insect: Can_Defeat_Lanayru_Twilit_Insect
Lake Hylia Twilit Insect on Docks: Can_Defeat_Lanayru_Twilit_Insect
Lake Hylia Twilit Bloat: count(Lanayru_Twilight_Tear, 11) and Can_Defeat_Lanayru_Twilit_Insect
Zora's River Twilit Insect 1: Can_Defeat_Lanayru_Twilit_Insect
Zora's River Twilit Insect 2: Can_Defeat_Lanayru_Twilit_Insect
Zora's River Twilit Insect 3: Can_Defeat_Lanayru_Twilit_Insect
Lake Hylia Underwater Chest: Iron_Boots
Lake Hylia Alcove Poe: Night and Can_Use_Senses
Lake Hylia Dock Poe: Night and Can_Use_Senses
Plumm Fruit Balloon Minigame: Can_Howl
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'
Flight By Fowl Ledge Poe: Night and Can_Use_Senses and 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
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
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 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 Twilit Insect: Can_Defeat_Lanayru_Twilit_Insect
Upper Zoras River Female Dragonfly: Nothing
Upper Zoras River Poe: Can_Use_Senses
Exits:
Upper Zoras River Izas House: Can_Open_Doors and Upper_Zoras_River_Warp_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_Warp_Portal
Iza Raging Rapids Minigame: Bow and Upper_Zoras_River_Warp_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 Twilit Insect near Lilypads: Can_Defeat_Lanayru_Twilit_Insect
Zoras Domain Burrowing Twilit Insect: Can_Defeat_Lanayru_Twilit_Insect
Zoras Domain Chest By Mother and Child Isles: Nothing
Zoras Domain Chest Behind Waterfall: Can_Midna_Jump
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
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 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
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 Throne Room Twilit Insect: Can_Defeat_Lanayru_Twilit_Insect
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 Underwater Goron: Water_Bombs and Iron_Boots and Zora_Armor
Exits:
Zoras Domain Top of Waterfall: Nothing
@@ -0,0 +1,165 @@
# 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
Events:
Ordon Spring Warp Portal: Nothing
Locations:
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
Locations:
# We can't assume repeatable non-twilight access to any of faron woods until after the prologue and twilight sections
# are finished. We put this location here when the prologue is on because the north faron woods gate key gets placed
# here as a vanilla item when the prologue is on. This allows the search algorithm to find the key for logical progression
# without needing to assume repeatable access to Faron Woods. Entrance randomizer doesn't need to be taken into account
# because entrance randomizer with the prologue on is not allowed.
Faron Mist Cave Open Chest: Skip_Prologue == Off
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:
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
@@ -0,0 +1,90 @@
- 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
Locations:
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
Can Transform: Never
Exits:
Snowpeak Ruins East Door Exterior: Can_Open_Doors
Snowpeak Ruins Entrance: Nothing
- Name: Snowpeak Ruins West Door Interior
Can Transform: Never
Exits:
Snowpeak Ruins West Door Exterior: Can_Open_Doors
Snowpeak Ruins Entrance: Nothing
+273
View File
@@ -0,0 +1,273 @@
#include "area.hpp"
#include "search.hpp"
#include "world.hpp"
#include <algorithm>
#include <iostream>
#include <unordered_set>
namespace tphdr::logic::area
{
int LocationAccess::_idCounter = 0;
int Area::_idCounter = 0;
LocationAccess::LocationAccess(tphdr::logic::location::Location* loc,
const tphdr::logic::requirement::Requirement& req,
Area* area):
_loc(loc), _req(std::move(req)), _area(area)
{
this->_id = this->_idCounter++;
}
tphdr::logic::location::Location* LocationAccess::GetLocation() const
{
return this->_loc;
}
const tphdr::logic::requirement::Requirement& LocationAccess::GetRequirement()
{
return this->_req;
}
Area* LocationAccess::GetArea() const
{
return this->_area;
}
int LocationAccess::GetID() const
{
return this->_id;
}
EventAccess::EventAccess(const tphdr::logic::requirement::Requirement& req, Area* area, const int& eventIndex):
_req(std::move(req)), _area(area), _eventIndex(eventIndex)
{
}
const tphdr::logic::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, tphdr::logic::world::World* world): _name(name), _world(world)
{
this->_id = this->_idCounter++;
}
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<std::unique_ptr<EventAccess>>& events)
{
this->_events = std::move(events);
}
std::list<EventAccess*> Area::GetEvents() const
{
std::list<EventAccess*> events;
for (const auto& event : this->_events)
{
events.emplace_back(event.get());
}
return events;
}
void Area::SetLocations(std::list<std::unique_ptr<LocationAccess>>& locations)
{
this->_locations = std::move(locations);
}
std::list<LocationAccess*> Area::GetLocations() const
{
std::list<LocationAccess*> locations;
for (const auto& loc : this->_locations)
{
locations.emplace_back(loc.get());
}
return locations;
}
void Area::SetExits(std::list<std::unique_ptr<tphdr::logic::entrance::Entrance>>& exits)
{
this->_exits = std::move(exits);
}
std::list<tphdr::logic::entrance::Entrance*> Area::GetExits() const
{
std::list<tphdr::logic::entrance::Entrance*> exits;
for (const auto& exit : this->_exits)
{
exits.emplace_back(exit.get());
}
return exits;
}
void Area::AddExit(std::unique_ptr<tphdr::logic::entrance::Entrance>& exit)
{
this->_exits.push_back(std::move(exit));
}
void Area::RemoveExit(tphdr::logic::entrance::Entrance* exit)
{
auto removed = std::remove_if(this->_exits.begin(), this->_exits.end(), [&](const auto& e) { return e.get() == exit; });
this->_exits.erase(removed, this->_exits.end());
}
void Area::AddEntrance(tphdr::logic::entrance::Entrance* entrance)
{
this->_entrances.emplace_back(entrance);
}
void Area::RemoveEntrance(tphdr::logic::entrance::Entrance* entrance)
{
auto removed = std::remove(this->_entrances.begin(), this->_entrances.end(), entrance);
this->_entrances.erase(removed, this->_entrances.end());
}
std::list<tphdr::logic::entrance::Entrance*> Area::GetEntrances() const
{
return this->_entrances;
}
tphdr::logic::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<std::string> Area::GetHintRegions()
{
return this->_hintRegions;
}
void Area::SetTwilightCompletedMacroIndex(const int& macroIndex)
{
this->_twilightCompletedMacroIndex = macroIndex;
}
int Area::GetTwilightCompletedMacroIndex() const
{
return this->_twilightCompletedMacroIndex;
}
bool Area::TwilightCleared(tphdr::logic::search::Search* search) const
{
return this->_twilightCompletedMacroIndex == -1 || tphdr::logic::requirement::EvaluateRequirementAtFormTime(
this->GetWorld()->GetMacro(this->_twilightCompletedMacroIndex),
search,
tphdr::logic::requirement::FormTime::ALL,
this->GetWorld());
}
void Area::AssignHintRegionsAndDungeonLocations()
{
std::set<std::string> hintRegions = {};
std::unordered_set<Area*> alreadyChecked = {};
std::list<Area*> 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<std::string> dungeonRegions = {};
std::copy_if(hintRegions.begin(),
hintRegions.end(),
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 loactions 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 tphdr::logic::area
+116
View File
@@ -0,0 +1,116 @@
#pragma once
#include "entrance.hpp"
#include "requirement.hpp"
#include <set>
#include <list>
// Forward Declarations
namespace tphdr::logic::location
{
class Location;
}
namespace tphdr::logic::search
{
class Search;
}
namespace tphdr::logic::world
{
class World;
}
namespace tphdr::logic::area
{
class Area;
class LocationAccess
{
public:
LocationAccess(tphdr::logic::location::Location* loc, const tphdr::logic::requirement::Requirement& req, Area* area);
tphdr::logic::location::Location* GetLocation() const;
const tphdr::logic::requirement::Requirement& GetRequirement();
Area* GetArea() const;
int GetID() const;
private:
static int _idCounter;
int _id = -1;
tphdr::logic::location::Location* _loc = nullptr;
tphdr::logic::requirement::Requirement _req;
Area* _area = nullptr;
};
class EventAccess
{
public:
EventAccess(const tphdr::logic::requirement::Requirement& req, Area* area, const int& eventIndex);
const tphdr::logic::requirement::Requirement& GetRequirement();
Area* GetArea() const;
int GetEventIndex() const;
std::string GetName() const;
private:
tphdr::logic::requirement::Requirement _req;
Area* _area = nullptr;
int _eventIndex = -1;
};
class Area
{
public:
Area(const std::string& name, tphdr::logic::world::World* world);
std::string GetName() const;
void SetHardAssignedRegion(const std::string& _hardAssignedRegion);
std::string GetHardAssignRegion() const;
void SetEvents(std::list<std::unique_ptr<EventAccess>>& events);
std::list<EventAccess*> GetEvents() const;
void SetLocations(std::list<std::unique_ptr<LocationAccess>>& locations);
std::list<LocationAccess*> GetLocations() const;
void SetExits(std::list<std::unique_ptr<tphdr::logic::entrance::Entrance>>& exits);
std::list<tphdr::logic::entrance::Entrance*> GetExits() const;
void AddExit(std::unique_ptr<tphdr::logic::entrance::Entrance>& exit);
void RemoveExit(tphdr::logic::entrance::Entrance* exit);
void AddEntrance(tphdr::logic::entrance::Entrance* entrance);
void RemoveEntrance(tphdr::logic::entrance::Entrance* entrance);
std::list<tphdr::logic::entrance::Entrance*> GetEntrances() const;
tphdr::logic::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<std::string> GetHintRegions();
void SetTwilightCompletedMacroIndex(const int& macroIndex);
int GetTwilightCompletedMacroIndex() const;
bool TwilightCleared(tphdr::logic::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:
static int _idCounter;
int _id = -1;
std::string _name = "";
std::string _hardAssignedRegion = "";
std::set<std::string> _hintRegions = {};
std::list<std::unique_ptr<EventAccess>> _events = {};
std::list<std::unique_ptr<LocationAccess>> _locations = {};
std::list<std::unique_ptr<tphdr::logic::entrance::Entrance>> _exits = {};
std::list<tphdr::logic::entrance::Entrance*> _entrances = {};
tphdr::logic::world::World* _world;
bool _canChangeTime = false;
bool _canTransform = false;
int _twilightCompletedMacroIndex = -1;
};
} // namespace tphdr::logic::area
+127
View File
@@ -0,0 +1,127 @@
#include "dungeon.hpp"
#include "area.hpp"
#include "entrance.hpp"
#include "item.hpp"
#include "world.hpp"
#include "../utility/container.hpp"
#include "../utility/log.hpp"
namespace tphdr::logic::dungeon
{
Dungeon::Dungeon(const std::string& name, tphdr::logic::world::World* world): _name(name), _world(world) {}
std::string Dungeon::GetName() const
{
return this->_name;
}
void Dungeon::SetSmallKey(tphdr::logic::item::Item* item)
{
this->_smallKey = item;
LOG_TO_DEBUG("Set \"" + item->GetName() + "\" as small key for dungeon " + this->_name);
}
tphdr::logic::item::Item* Dungeon::GetSmallKey() const
{
return this->_smallKey;
}
void Dungeon::SetBigKey(tphdr::logic::item::Item* item)
{
this->_bigKey = item;
LOG_TO_DEBUG("Set \"" + item->GetName() + "\" as big key for dungeon " + this->_name);
}
tphdr::logic::item::Item* Dungeon::GetBigKey() const
{
return this->_bigKey;
}
void Dungeon::SetCompass(tphdr::logic::item::Item* item)
{
this->_compass = item;
LOG_TO_DEBUG("Set \"" + item->GetName() + "\" as compass for dungeon " + this->_name);
}
tphdr::logic::item::Item* Dungeon::GetCompass() const
{
return this->_compass;
}
void Dungeon::SetDungeonMap(tphdr::logic::item::Item* item)
{
this->_dungeonMap = item;
LOG_TO_DEBUG("Set \"" + item->GetName() + "\" as dungeon map for dungeon " + this->_name);
}
tphdr::logic::item::Item* Dungeon::GetDungeonMap() const
{
return this->_dungeonMap;
}
void Dungeon::SetStartingArea(tphdr::logic::area::Area* startingArea)
{
this->_startingArea = startingArea;
LOG_TO_DEBUG("Set \"" + startingArea->GetName() + "\" as starting area for dungeon " + this->_name)
}
tphdr::logic::area::Area* Dungeon::GetStartingAreas()
{
return this->_startingArea;
}
void Dungeon::AddStartingEntrance(tphdr::logic::entrance::Entrance* startingEntrance)
{
this->_startingEntrances.insert(startingEntrance);
LOG_TO_DEBUG("Added \"" + startingEntrance->GetOriginalName() + "\" as starting entrance for dungeon " + this->_name)
}
std::unordered_set<tphdr::logic::entrance::Entrance*> Dungeon::GetStartingEntrances() const
{
return this->_startingEntrances;
};
void Dungeon::AddLocation(tphdr::logic::location::Location* location)
{
if (!tphdr::utility::container::ElementInContainer(this->_locations, location))
{
this->_locations.push_back(location);
LOG_TO_DEBUG(location->GetName() + " has been assigned to dungeon " + this->_name);
}
}
tphdr::logic::location::LocationPool Dungeon::GetLocations()
{
return this->_locations;
}
void Dungeon::SetGoalLocation(tphdr::logic::location::Location* goalLocation)
{
this->_goalLocation = goalLocation;
LOG_TO_DEBUG(goalLocation->GetName() + " has been assigned as goal location to dungeon " + this->_name);
}
tphdr::logic::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;
}
bool Dungeon::ShouldBeBarren() const
{
return !this->_required && this->_world->Setting("Unrequired Dungeons Are Barren") == "On";
}
} // namespace tphdr::logic::dungeon
+76
View File
@@ -0,0 +1,76 @@
#pragma once
#include "location.hpp"
#include <unordered_set>
// Forward declarations
namespace tphdr::logic::item
{
class Item;
}
namespace tphdr::logic::area
{
class Area;
}
namespace tphdr::logic::entrance
{
class Entrance;
}
namespace tphdr::logic::world
{
class World;
}
namespace tphdr::logic::dungeon
{
/**
* @brief Holds dungeon specific data
*/
class Dungeon
{
public:
Dungeon(const std::string& name, tphdr::logic::world::World* world);
std::string GetName() const;
void SetSmallKey(tphdr::logic::item::Item* item);
tphdr::logic::item::Item* GetSmallKey() const;
void SetBigKey(tphdr::logic::item::Item* item);
tphdr::logic::item::Item* GetBigKey() const;
void SetCompass(tphdr::logic::item::Item* item);
tphdr::logic::item::Item* GetCompass() const;
void SetDungeonMap(tphdr::logic::item::Item* item);
tphdr::logic::item::Item* GetDungeonMap() const;
void SetStartingArea(tphdr::logic::area::Area* startingArea);
tphdr::logic::area::Area* GetStartingAreas();
void AddStartingEntrance(tphdr::logic::entrance::Entrance* startingEntrance);
std::unordered_set<tphdr::logic::entrance::Entrance*> GetStartingEntrances() const;
void AddLocation(tphdr::logic::location::Location* location);
tphdr::logic::location::LocationPool GetLocations();
void SetGoalLocation(tphdr::logic::location::Location* goalLocation);
tphdr::logic::location::Location* GetGoalLocation();
void SetRequired(const bool& required);
bool IsRequired() const;
/**
* @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 = "";
tphdr::logic::world::World* _world;
tphdr::logic::item::Item* _smallKey;
tphdr::logic::item::Item* _bigKey;
tphdr::logic::item::Item* _compass;
tphdr::logic::item::Item* _dungeonMap;
tphdr::logic::area::Area* _startingArea;
std::unordered_set<tphdr::logic::entrance::Entrance*> _startingEntrances;
tphdr::logic::location::Location* _goalLocation;
tphdr::logic::location::LocationPool _locations = {};
bool _required = false;
};
} // namespace tphdr::logic::dungeon
+337
View File
@@ -0,0 +1,337 @@
#include "entrance.hpp"
#include "area.hpp"
#include "world.hpp"
#include "../utility/log.hpp"
#include "../utility/string.hpp"
namespace tphdr::logic::entrance
{
std::unordered_set<Type> NON_ASSUMED_TYPES = {Type::SPAWN, Type::WARP_PORTAL};
Type TypeFromStr(const std::string& str)
{
std::unordered_map<std::string, Type> 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<Type, std::string> 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<Type, Type> 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(tphdr::logic::area::Area* parentArea,
tphdr::logic::area::Area* connectedArea,
const tphdr::logic::requirement::Requirement& req,
tphdr::logic::world::World* world):
_parentArea(parentArea),
_connectedArea(connectedArea),
_originalConnectedArea(connectedArea),
_req(std::move(req)),
_world(world)
{
this->_originalName = this->GetCurrentName();
this->_computedRequirement._type = tphdr::logic::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::GeneralizeOriginalName()
{
tphdr::utility::str::Erase(this->_originalName, " North", " South", " East", " West", " Right", " Left");
}
tphdr::logic::area::Area* Entrance::GetParentArea() const
{
return this->_parentArea;
}
tphdr::logic::area::Area* Entrance::GetConnectedArea() const
{
return this->_connectedArea;
}
tphdr::logic::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 tphdr::logic::requirement::Requirement& req)
{
this->_req = req;
}
const tphdr::logic::requirement::Requirement& Entrance::GetRequirement()
{
return this->_req;
}
void Entrance::SetComputedRequirement(const tphdr::logic::requirement::Requirement& computedRequirement)
{
this->_computedRequirement = computedRequirement;
}
tphdr::logic::requirement::Requirement Entrance::GetComputedRequirement()
{
return this->_computedRequirement;
}
tphdr::logic::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(tphdr::logic::area::Area* newConnectedArea)
{
this->_connectedArea = newConnectedArea;
newConnectedArea->AddEntrance(this);
}
tphdr::logic::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<Entrance>(root, nullptr, tphdr::logic::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<std::string, std::string> GetParentAndConnectedAreaNames(const std::string& originalName)
{
std::string parentAreaName;
std::string connectedAreaName;
if (tphdr::utility::str::Contains(originalName, " -> "))
{
auto separatorIndex = originalName.find(" -> ");
parentAreaName = originalName.substr(0, separatorIndex);
connectedAreaName = originalName.substr(separatorIndex + 4);
}
else if (tphdr::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 tphdr::logic::entrance
+204
View File
@@ -0,0 +1,204 @@
#pragma once
#include "requirement.hpp"
#include <unordered_set>
#include <map>
#include <string>
// Forward Declarations
namespace tphdr::logic::area
{
class Area;
}
namespace tphdr::logic::world
{
class World;
}
namespace tphdr::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,
};
extern std::unordered_set<Type> NON_ASSUMED_TYPES;
/**
* @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(tphdr::logic::area::Area* parentArea,
tphdr::logic::area::Area* connectedArea,
const tphdr::logic::requirement::Requirement& req,
tphdr::logic::world::World* world);
void SetID(const int& id);
int GetID() const;
std::string GetCurrentName() const;
std::string GetOriginalName() const;
/**
* @brief Removes cardinal/direction specifiers from the entrance's name (North, South, East, West, Left, Right)
*/
void GeneralizeOriginalName();
tphdr::logic::area::Area* GetParentArea() const;
tphdr::logic::area::Area* GetConnectedArea() const;
tphdr::logic::area::Area* GetOriginalConnectedArea() const;
void SetType(const Type& type);
Type GetType() const;
Type GetOriginalType() const;
void SetRequirement(const tphdr::logic::requirement::Requirement& req);
const tphdr::logic::requirement::Requirement& GetRequirement();
void SetComputedRequirement(const tphdr::logic::requirement::Requirement& computedRequirement);
tphdr::logic::requirement::Requirement GetComputedRequirement();
tphdr::logic::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(tphdr::logic::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
*/
tphdr::logic::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;
tphdr::logic::area::Area* _parentArea = nullptr;
tphdr::logic::area::Area* _connectedArea = nullptr;
tphdr::logic::area::Area* _originalConnectedArea = nullptr;
Type _type = Type::INVALID;
Type _originalType = Type::INVALID;
std::string _originalName = "";
tphdr::logic::world::World* _world = nullptr;
/**
* @brief The local requirement for this entrance assuming we have access to its parent area.
*/
tphdr::logic::requirement::Requirement _req;
/**
* @brief The flattened requirement which includes everything necessary to reach this entrance from the root of the
* world graph.
*/
tphdr::logic::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<Entrance*>;
using EntrancePools = std::map<Type, EntrancePool>;
std::tuple<std::string, std::string> GetParentAndConnectedAreaNames(const std::string& originalName);
} // namespace tphdr::logic::entrance
@@ -0,0 +1,780 @@
#include "entrance_shuffle.hpp"
#include "item_pool.hpp"
#include "search.hpp"
#include "../utility/file.hpp"
#include "../utility/random.hpp"
#include "../utility/yaml.hpp"
using namespace tphdr::logic::entrance;
namespace tphdr::logic::entrance_shuffle
{
void ShuffleWorldEntrances(tphdr::logic::world::World* world, tphdr::logic::world::WorldPool& worlds)
{
SetAllEntrancesData(world);
auto entrancePools = CreateEntrancePools(world);
auto targetEntrancePools = CreateTargetPools(entrancePools);
// Set plando entrances first
SetPlandomizedEntrances(world, worlds, entrancePools, targetEntrancePools);
// Then shuffle non-assumed types (currently this is just spawn)
ShuffleNonAssumedEntrancesPools(world, worlds, entrancePools, targetEntrancePools);
// Shuffle the rest of the entrance pools
for (auto& [entranceType, entrancePool] : entrancePools)
{
ShuffleEntrancePool(world, worlds, entrancePool, targetEntrancePools[entranceType]);
}
// Validate the world one last time to ensure everything worked
auto completeItemPool = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
ValidateWorld(world, worlds, nullptr, completeItemPool);
}
void SetAllEntrancesData(tphdr::logic::world::World* world)
{
auto filepath = DATA_PATH "entrance_shuffle_data.yaml";
tphdr::utility::file::Verify(filepath);
// Keep track of which double door entrances are together
std::unordered_map<std::string, std::list<tphdr::logic::entrance::Entrance*>> coupledDoors = {};
auto entranceDataTree = LoadYAML(filepath);
for (const auto& entranceDataNode : entranceDataTree)
{
// Check to make sure all required fields are present
YAMLVerifyFields(entranceDataNode, "Type", "Forward");
auto typeStr = entranceDataNode["Type"].as<std::string>();
auto type = tphdr::logic::entrance::TypeFromStr(typeStr);
if (type == tphdr::logic::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<std::string>());
forwardEntrance->SetType(type);
// TODO: Set actual entrance data
forwardEntrance->SetID(world->GetNewEntranceID());
forwardEntrance->SetPrimary(true);
if (entranceDataNode["Return"])
{
auto& returnEntry = entranceDataNode["Return"];
YAMLVerifyFields(returnEntry, "Connection" /*, "Info" */);
auto returnEntrance = world->GetEntrance(returnEntry["Connection"].as<std::string>());
returnEntrance->SetType(type);
// TODO: Set actual entrance data
returnEntrance->SetID(world->GetNewEntranceID());
forwardEntrance->BindTwoWay(returnEntrance);
// Add double door entrances to their respective tag group
if (entranceDataNode["Door Couple Tag"])
{
auto tag = entranceDataNode["Door Couple Tag"].as<std::string>();
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::find_if(doors.begin(),
doors.end(),
[&](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->GeneralizeOriginalName();
}
}
}
}
EntrancePools CreateEntrancePools(tphdr::logic::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")
{
auto& dungeonPool = entrancePools[Type::DUNGEON];
auto removed = std::remove_if(
dungeonPool.begin(),
dungeonPool.end(),
[](const auto& entrance)
{ return entrance->GetOriginalName() == "Castle Town North Inside Barrier -> Hyrule Castle Entrance"; });
dungeonPool.erase(removed, dungeonPool.end());
}
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::any_of(
mixedPools.begin(),
mixedPools.end(),
[](const auto& pool)
{ return tphdr::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& type : {Type::SPAWN, Type::INTERIOR, Type::CAVE, Type::OVERWORLD, Type::GROTTO})
{
auto entrances = world->GetShuffleableEntrances(type);
for (const auto& entrance : entrances)
{
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(tphdr::logic::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(tphdr::logic::world::World* world,
tphdr::logic::world::WorldPool& worlds,
EntrancePools& entrancePools,
EntrancePools& targetEntrancePools)
{
LOG_TO_DEBUG("Now placing plandomizer entrances");
auto itemPool = tphdr::logic::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("Plandomizer Error: " + entranceToConnect->GetOriginalName() +
" is not an entrance that can be shuffled");
}
if (plandoTarget->GetType() == Type::INVALID)
{
throw std::runtime_error("Plandomizer 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("Plandomizer Error: " + entranceToConnect->GetOriginalName() + "'s type " +
TypeToStr(entranceType) + " 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" &&
tphdr::utility::container::ElementInContainer(entrancePool, entranceToConnect->GetReverse()))
{
entranceToConnect = entranceToConnect->GetReverse();
targetToConnect = targetToConnect->GetReverse();
}
if (tphdr::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, worlds, entranceToConnect, itemPool);
}
validTargetFound = true;
ConfirmReplacement(entranceToConnect, target);
}
catch(const EntranceShuffleError& e)
{
throw std::runtime_error("Could not connect plandomized 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)
{
tphdr::utility::container::Erase(entrancePool, entranceToConnect);
tphdr::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() +
" for some reason could not be found.");
}
}
LOG_TO_DEBUG("All plandomized entrances have been placed.");
}
void ShuffleNonAssumedEntrancesPools(tphdr::logic::world::World* world,
tphdr::logic::world::WorldPool& worlds,
EntrancePools& entrancePools,
EntrancePools& targetEntrancePools)
{
auto completeItemPool = tphdr::logic::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<Entrance*, Entrance*> 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)
{
tphdr::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, worlds, nullptr, completeItemPool);
for (auto& [entrance, target] : rollbacks)
{
ConfirmReplacement(entrance, target);
tphdr::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(tphdr::logic::world::World* world,
tphdr::logic::world::WorldPool& worlds,
EntrancePool& entrancePool,
EntrancePool& targetEntrancePool,
int retries /* = 20*/)
{
while (retries > 0)
{
retries -= 1;
std::unordered_map<Entrance*, Entrance*> rollbacks = {};
try
{
ShuffleEntrances(worlds, 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(world->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(tphdr::logic::world::WorldPool& worlds,
EntrancePool& entrancePool,
EntrancePool& targetEntrancePool,
std::unordered_map<Entrance*, Entrance*>& rollbacks)
{
auto completeItemPool = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
tphdr::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;
}
tphdr::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(worlds, 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(tphdr::logic::world::WorldPool& worlds,
Entrance* entrance,
Entrance* target,
std::unordered_map<Entrance*, Entrance*>& rollbacks,
const tphdr::logic::item_pool::ItemPool& completeItemPool)
{
try
{
CheckEntrancesCompatibility(entrance, target);
ChangeConnections(entrance, target);
ValidateWorld(entrance->GetWorld(), worlds, 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(Entrance* entrance, 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(tphdr::logic::world::World* world,
tphdr::logic::world::WorldPool& worlds,
Entrance* entrance,
const tphdr::logic::item_pool::ItemPool& completeItemPool)
{
// Validate that all logic is still satisfied
auto verifyLogicError = tphdr::logic::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 = tphdr::logic::search::Search::SphereZero(&worlds);
sphereZeroSearch.SearchWorlds();
const auto& foundLocations = sphereZeroSearch._visitedLocations;
auto numSphereZeroLocations = std::count_if(foundLocations.begin(),
foundLocations.end(),
[](const auto& location) { return location->IsProgression(); });
// If there are no sphere zero locations available and we didn't find a 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 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._foundDisconnectedExit)
{
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;
}
} // namespace tphdr::logic::entrance_shuffle
@@ -0,0 +1,54 @@
#pragma once
#include "entrance.hpp"
#include "world.hpp"
namespace tphdr::logic::entrance_shuffle
{
void ShuffleWorldEntrances(tphdr::logic::world::World* world, tphdr::logic::world::WorldPool& worlds);
void SetAllEntrancesData(tphdr::logic::world::World* world);
tphdr::logic::entrance::EntrancePools CreateEntrancePools(tphdr::logic::world::World* world);
tphdr::logic::entrance::EntrancePools CreateTargetPools(tphdr::logic::entrance::EntrancePools& entrancePools);
tphdr::logic::entrance::EntrancePool AssumeEntrancePool(tphdr::logic::entrance::EntrancePool& entrancePool);
void SetPlandomizedEntrances(tphdr::logic::world::World* world,
tphdr::logic::world::WorldPool& worlds,
tphdr::logic::entrance::EntrancePools& entrancePools,
tphdr::logic::entrance::EntrancePools& targetEntrancePools);
void ShuffleNonAssumedEntrancesPools(tphdr::logic::world::World* world,
tphdr::logic::world::WorldPool& worlds,
tphdr::logic::entrance::EntrancePools& entrancePools,
tphdr::logic::entrance::EntrancePools& targetEntrancePools);
void ShuffleEntrancePool(tphdr::logic::world::World* world,
tphdr::logic::world::WorldPool& worlds,
tphdr::logic::entrance::EntrancePool& entrancePool,
tphdr::logic::entrance::EntrancePool& targetEntrancePool,
int retries = 20);
void ShuffleEntrances(tphdr::logic::world::WorldPool& worlds,
tphdr::logic::entrance::EntrancePool& entrancePool,
tphdr::logic::entrance::EntrancePool& targetEntrancePool,
std::unordered_map<tphdr::logic::entrance::Entrance*, tphdr::logic::entrance::Entrance*>& rollbacks);
bool ReplaceEntrance(tphdr::logic::world::WorldPool& worlds,
tphdr::logic::entrance::Entrance* entrance,
tphdr::logic::entrance::Entrance* target,
std::unordered_map<tphdr::logic::entrance::Entrance*, tphdr::logic::entrance::Entrance*>& rollbacks,
const tphdr::logic::item_pool::ItemPool& completeItemPool);
void CheckEntrancesCompatibility(tphdr::logic::entrance::Entrance* entrance, tphdr::logic::entrance::Entrance* target);
void ChangeConnections(tphdr::logic::entrance::Entrance* entrance, tphdr::logic::entrance::Entrance* target);
void RestoreConnections(tphdr::logic::entrance::Entrance* entrance, tphdr::logic::entrance::Entrance* target);
void ConfirmReplacement(tphdr::logic::entrance::Entrance* entrance, tphdr::logic::entrance::Entrance* target);
void DeleteTargetEntrance(tphdr::logic::entrance::Entrance* target);
void ValidateWorld(tphdr::logic::world::World* world,
tphdr::logic::world::WorldPool& worlds,
tphdr::logic::entrance::Entrance* entrance,
const tphdr::logic::item_pool::ItemPool& completeItemPool);
void SetShuffledEntrances(tphdr::logic::entrance::EntrancePools& entrancePools);
tphdr::logic::entrance::EntrancePool GetReverseEntrances(const tphdr::logic::entrance::EntrancePool& entrances);
class EntranceShuffleError: public std::runtime_error
{
public:
explicit EntranceShuffleError(const std::string& message): std::runtime_error(message) {}
};
} // namespace tphdr::logic::entrance_shuffle
+529
View File
@@ -0,0 +1,529 @@
#include "fill.hpp"
#include "item_pool.hpp"
#include "search.hpp"
#include "../utility/random.hpp"
#include "../utility/string.hpp"
#include "../utility/time.hpp"
#include <iostream>
#include <algorithm>
namespace tphdr::logic::fill
{
void FillWorlds(tphdr::logic::world::WorldPool& worlds)
{
// Place each world's restricted items first
for (auto& world : worlds)
{
PlaceRestrictedItems(world, worlds);
}
tphdr::logic::item_pool::ItemPool itemPool = {};
tphdr::logic::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 =
tphdr::utility::container::FilterAndEraseFromVector(itemPool, [](const auto& item) { return item->IsMajor(); });
auto progressLocations =
tphdr::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 =
tphdr::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 = tphdr::logic::search::VerifyLogic(&worlds);
if (verifyLogicError.has_value())
{
throw std::runtime_error("Not all logic satisfied! Reason:\n" + verifyLogicError.value());
}
}
void AssumedFill(tphdr::logic::world::WorldPool& worlds,
tphdr::logic::item_pool::ItemPool& itemsToPlacePool,
const tphdr::logic::item_pool::ItemPool& itemsNotYetPlaced,
tphdr::logic::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";
int count = itemsToPlacePool.size() > 5 ? 5 : itemsToPlacePool.size();
for (int i = 0; i < count; i++)
{
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;
tphdr::utility::random::ShufflePool(itemsToPlacePool);
auto itemsToPlace = itemsToPlacePool;
tphdr::logic::location::LocationPool rollbacks = {};
while (!itemsToPlace.empty())
{
// Get a random item to place
auto itemToPlace = itemsToPlace.back();
itemsToPlace.pop_back();
tphdr::utility::random::ShufflePool(allowedLocations);
tphdr::logic::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 = tphdr::logic::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";
bool canChooseAnyLocation =
search._ownedItems.contains(itemToPlace->GetWorld()->GetGameWinningItem()) && beatableOnlyLogic;
for (const auto& location : allowedLocations)
{
// Get all reachable LocationAccess spots for this location
std::list<tphdr::logic::area::LocationAccess*> 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::any_of(locAccList.begin(),
locAccList.end(),
[&](const auto& la)
{
return canChooseAnyLocation ||
tphdr::logic::requirement::EvaluateLocationRequirement(&search, la) ==
tphdr::logic::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(tphdr::logic::item_pool::ItemPool& itemsToPlace, tphdr::logic::location::LocationPool allowedLocations)
{
auto emptyLocations =
tphdr::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;
}
tphdr::utility::random::ShufflePool(emptyLocations);
for (auto& location : emptyLocations)
{
if (itemsToPlace.empty())
{
break;
}
location->SetCurrentItem(tphdr::utility::random::PopRandomElement(itemsToPlace));
}
}
void PlaceRestrictedItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::world::WorldPool& worlds)
{
PlacePrologueItems(world, worlds);
PlaceGoalLocationItems(world, worlds);
PlaceOwnDungeonItems(world, worlds);
PlaceAnywhereDungeonRewards(world, worlds);
// Determine required dungeons now so that we can place "any dungeon" items appropriately
world->DetermineRequiredDungeons();
PlaceAnyDungeonItems(world, worlds);
PlaceOverworldItems(world, worlds);
}
void PlacePrologueItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::world::WorldPool& worlds)
{
if (world->Setting("Skip Prologue") == "Off")
{
// Filter out the slingshot and progressive swords to place first. The first slingshot and 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 = tphdr::utility::container::FilterAndEraseFromVector(
itemPool,
[](const auto& item)
{
return item->GetName() == "Slingshot" || item->GetName() == "Progressive Sword" ||
item->GetName() == "Lantern" || item->GetName() == "Progressive Fishing Rod" ||
item->IsShadowCrystal();
});
auto completeItemPool = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
AssumedFill(worlds, prologueItems, completeItemPool, world->GetAllLocations());
}
}
void PlaceGoalLocationItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::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();
tphdr::logic::location::LocationPool goalLocations = {};
// Filter out goal locations
goalLocations = tphdr::utility::container::FilterFromVector(
allLocations,
[](const auto& location) { return location->IsGoalLocation() && location->IsEmpty(); });
// Filter out goal items
std::set<std::string> goalItemNames = {"Progressive Mirror Shard", "Progressive Fused Shadow"};
auto goalItems = tphdr::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 goal locations to place dungeon rewards on goal locations.");
}
// Place goal items at goal locations
auto completeItemPool = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
AssumedFill(worlds, goalItems, completeItemPool, goalLocations);
}
void PlaceOwnDungeonItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::world::WorldPool& worlds)
{
for (const auto& [dungeonName, dungeon] : world->GetDungeonTable())
{
// Filter hint signs out of dungeon locations
auto dungeonLocations = dungeon->GetLocations();
tphdr::utility::container::FilterAndEraseFromVector(dungeonLocations,
[](const auto& location)
{ return location->HasCategories("Hint Sign"); });
// 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 = tphdr::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 = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
AssumedFill(worlds, smallKeys, completeItemPool, dungeonLocations);
}
// Big Keys
if (world->Setting("Big Keys") == "Own Dungeon")
{
auto bigKeys = tphdr::utility::container::FilterAndEraseFromVector(world->GetItemPool(),
[&](const auto& item)
{ return item == dungeon_->GetBigKey(); });
auto completeItemPool = tphdr::logic::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 = tphdr::utility::container::FilterAndEraseFromVector(
world->GetItemPool(),
[&](const auto& item) { return item == dungeon_->GetCompass() || item == dungeon_->GetDungeonMap(); });
auto completeItemPool = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
FastFill(mapsCompasses, dungeonLocations);
}
}
}
void PlaceAnywhereDungeonRewards(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::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 goal items
std::set<std::string> goalItemNames = {"Progressive Mirror Shard", "Progressive Fused Shadow"};
auto goalItems = tphdr::utility::container::FilterAndEraseFromVector(
world->GetItemPool(),
[&](const auto& item) { return goalItemNames.contains(item->GetName()); });
// Place the items
auto completeItemPool = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
AssumedFill(worlds, goalItems, completeItemPool, allLocations);
}
void PlaceAnyDungeonItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::world::WorldPool& worlds)
{
tphdr::logic::item_pool::ItemPool anyDungeonItems = {};
tphdr::logic::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<tphdr::logic::dungeon::Dungeon*> nonBarrenDungeons = {};
std::list<tphdr::logic::dungeon::Dungeon*> 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 = tphdr::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::copy(smallKeys.begin(), smallKeys.end(), 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 = tphdr::utility::container::FilterAndEraseFromVector(
world->GetItemPool(),
[&](const auto& item) { return item == dungeon_->GetBigKey(); });
std::copy(bigKeys.begin(), bigKeys.end(), 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 = tphdr::utility::container::FilterAndEraseFromVector(
world->GetItemPool(),
[&](const auto& item) { return item == dungeon_->GetCompass() || item == dungeon_->GetDungeonMap(); });
std::copy(mapsCompasses.begin(), mapsCompasses.end(), 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();
std::copy_if(dungeonLocations.begin(),
dungeonLocations.end(),
std::back_inserter(anyDungeonLocations),
[&](const auto& location) { return dungeon->ShouldBeBarren() || location->IsProgression(); });
}
// Place the dungeon items in the appropriate dungeon locations
auto completeItemPool = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
AssumedFill(worlds, anyDungeonItems, completeItemPool, anyDungeonLocations);
}
}
void PlaceOverworldItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::world::WorldPool& worlds)
{
tphdr::logic::item_pool::ItemPool overworldItems = {};
tphdr::logic::location::LocationPool overworldLocations = world->GetAllLocations();
// Filter out any nonprogress locations
tphdr::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 = tphdr::utility::container::FilterAndEraseFromVector(
world->GetItemPool(),
[&](const auto& item)
{
return item == dungeon_->GetSmallKey() ||
(dungeonName_ == "Snowpeak Ruins" &&
(item->GetName() == "Ordon Pumpkin" || item->GetName() == "Ordon Cheese"));
});
std::copy(smallKeys.begin(), smallKeys.end(), std::back_inserter(overworldItems));
}
// Add big keys to the pool if big keys are overworld
if (world->Setting("Big Keys") == "Overworld")
{
auto bigKeys = tphdr::utility::container::FilterAndEraseFromVector(world->GetItemPool(),
[&](const auto& item)
{ return item == dungeon_->GetBigKey(); });
std::copy(bigKeys.begin(), bigKeys.end(), 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 = tphdr::utility::container::FilterAndEraseFromVector(
world->GetItemPool(),
[&](const auto& item) { return item == dungeon_->GetCompass() || item == dungeon_->GetDungeonMap(); });
std::copy(mapsCompasses.begin(), mapsCompasses.end(), std::back_inserter(overworldItems));
}
// Remove this dungeon's locations from the overworldLocations pool
overworldLocations = tphdr::utility::container::FilterFromVector(
overworldLocations,
[&](const auto& location)
{ return !tphdr::utility::container::ElementInContainer(dungeon_->GetLocations(), location); });
}
// Place the dungeon items in the overworld locations
auto completeItemPool = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
AssumedFill(worlds, overworldItems, completeItemPool, overworldLocations);
}
void CacheExitTimeForms(tphdr::logic::world::WorldPool& worlds)
{
auto completeItemPool = tphdr::logic::item_pool::GetCompleteItemPool(worlds);
auto searchWithItems = tphdr::logic::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] = tphdr::logic::requirement::FormTime::NONE;
for (const auto& formTime : tphdr::logic::requirement::FormTime::ALL_FORM_TIMES)
{
if (formTime & areaFormTimes &&
tphdr::logic::requirement::EvaluateRequirementAtFormTime(req,
&searchWithItems,
formTime,
world.get()))
{
exitTimeFormCache[exit] |= formTime;
}
}
}
}
}
}
} // namespace tphdr::logic::fill
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#include "item.hpp"
#include "world.hpp"
namespace tphdr::logic::fill
{
void FillWorlds(tphdr::logic::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.
*/
void AssumedFill(tphdr::logic::world::WorldPool& worlds,
tphdr::logic::item_pool::ItemPool& itemsToPlacePool,
const tphdr::logic::item_pool::ItemPool& itemsNotYetPlaced,
tphdr::logic::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(tphdr::logic::item_pool::ItemPool& itemsToPlace, tphdr::logic::location::LocationPool allowedLocations);
void PlaceRestrictedItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::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<tphdr::logic::world::World>& world, tphdr::logic::world::WorldPool& worlds);
void PlaceGoalLocationItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::world::WorldPool& worlds);
void PlaceOwnDungeonItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::world::WorldPool& worlds);
void PlaceAnywhereDungeonRewards(std::unique_ptr<tphdr::logic::world::World>& world,
tphdr::logic::world::WorldPool& worlds);
void PlaceAnyDungeonItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::world::WorldPool& worlds);
void PlaceOverworldItems(std::unique_ptr<tphdr::logic::world::World>& world, tphdr::logic::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(tphdr::logic::world::WorldPool& worlds);
} // namespace tphdr::logic::fill
+275
View File
@@ -0,0 +1,275 @@
#include <algorithm>
#include "bits.hpp"
#include "../item.hpp"
#include <iterator>
BitVector::BitVector(std::list<int> bits)
{
for (auto& i : bits)
{
this->set(i);
}
}
bool BitVector::isEmpty() const
{
return bitset.none();
}
std::set<int> 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<int> 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<std::bitset<512>> 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<std::bitset<512>> filtered = {};
for (const auto& candidate : terms)
{
std::vector<int> 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<bool, DNF> DNF::or_useful(const DNF& other)
{
auto filtered_this = terms;
std::vector<std::bitset<512>> 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<std::bitset<512>> 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 tphdr::logic::requirement::Requirement& req)
{
uint32_t expectedCount;
tphdr::logic::item::Item* item;
std::string key;
switch (req._type)
{
case tphdr::logic::requirement::Type::ITEM:
item = std::get<tphdr::logic::item::Item*>(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 tphdr::logic::requirement::Type::COUNT:
expectedCount = std::get<int>(req._args[0]);
item = std::get<tphdr::logic::item::Item*>(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 tphdr::logic::requirement::Type::HEALTH:
// key = std::to_string(std::get<int>(req._args[0]));
// if (heartCount.contains(key))
// {
// return heartCount[key];
// }
// else
// {
// heartCount[key] = counter;
// reverseIndex.push_back(req);
// return bump();
// }
case tphdr::logic::requirement::Type::GOLDEN_BUGS:
key = std::to_string(std::get<int>(req._args[0]));
if (goldenBugCount.contains(key))
{
return goldenBugCount[key];
}
else
{
goldenBugCount[key] = counter;
reverseIndex.push_back(req);
return bump();
}
default:
// Not a flattening requirement
return -1;
}
return -1;
}
@@ -0,0 +1,71 @@
#pragma once
#include "../requirement.hpp"
#include <list>
#include <vector>
#include <bitset>
#include <set>
#include <unordered_map>
class BitVector
{
public:
BitVector() = default;
BitVector(std::list<int>);
bool isEmpty() const;
std::set<int> 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<int> 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<std::bitset<512>> terms);
static DNF True() { return DNF({0}); }
static DNF False() { return DNF(std::vector<std::bitset<512>> {}); }
bool isTriviallyFalse() const;
bool isTriviallyTrue() const;
DNF or_(const DNF& other);
DNF dedup();
std::pair<bool, DNF> or_useful(const DNF& other);
DNF and_(const DNF& other);
std::vector<std::bitset<512>> terms = {};
};
class BitIndex
{
public:
BitIndex() = default;
int bump();
int reqBit(const tphdr::logic::requirement::Requirement& req);
std::unordered_map<std::string, int> itemBits = {};
// std::unordered_map<std::string, int> heartCount = {};
std::unordered_map<std::string, int> goldenBugCount = {};
std::vector<tphdr::logic::requirement::Requirement> reverseIndex = {};
int counter = 0;
};
@@ -0,0 +1,505 @@
#include "flatten.hpp"
#include "../world.hpp"
FlattenSearch::FlattenSearch(tphdr::logic::world::World* world_)
{
world = world_;
for (auto& [name, area] : world->GetAreaTable())
{
for (auto& exit : area->GetExits())
{
auto visit = visitor(exit, this);
visitReq(exit->GetRequirement(), visit, world);
}
for (auto& event : area->GetEvents())
{
auto visit = visitor(event, this);
visitReq(event->GetRequirement(), visit, world);
}
}
auto root = world->GetRootArea();
// Start with all formtimes at the root, false for everything else
auto formTimes = tphdr::logic::requirement::FormTime::ALL_FORM_AND_DAY_TIMES;
formTimes.push_back(tphdr::logic::requirement::FormTime::TWILIGHT);
for (const auto& [areaName, area] : world->GetAreaTable())
{
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<std::string, std::list<tphdr::logic::area::LocationAccess*>> itemLocations = {};
for (auto& [name, area] : world->GetAreaTable())
{
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 = tphdr::logic::requirement::FormTime::ALL_FORM_AND_DAY_TIMES;
formTimes.push_back(tphdr::logic::requirement::FormTime::TWILIGHT);
for (auto& [locName, accessList] : itemLocations)
{
auto expr = DNF::False();
for (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 : tphdr::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(tphdr::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)
// {
// tphdr::logic::area::Area* area2;
// world->GetArea(areaStr, area2);
// if (recentlyUpdatedAreas.contains(area2))
// {
// return true;
// }
// }
return false;
}
void FlattenSearch::tryExits()
{
using namespace tphdr::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 : tphdr::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 tphdr::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(tphdr::logic::area::Area* area)
{
using namespace tphdr::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(tphdr::logic::area::EventAccess* event, const int& formTime)
{
return areaExprs[formTime][event->GetArea()].and_(
evaluatePartialRequirement(bitIndex, event->GetRequirement(), this, formTime));
}
DNF FlattenSearch::tryLocationAtFormTime(tphdr::logic::area::LocationAccess* location, const int& formTime)
{
return areaExprs[formTime][location->GetArea()].and_(
evaluatePartialRequirement(bitIndex, location->GetRequirement(), this, formTime));
}
DNF FlattenSearch::tryExitAtFormTime(tphdr::logic::entrance::Entrance* exit, const int& formTime)
{
return areaExprs[formTime][exit->GetParentArea()].and_(
evaluatePartialRequirement(bitIndex, exit->GetRequirement(), this, formTime));
}
DNF evaluatePartialRequirement(BitIndex& bitIndex,
const tphdr::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;
tphdr::logic::item::Item* item;
int event;
DNF d = DNF();
tphdr::logic::area::Area* area;
switch (req._type)
{
case tphdr::logic::requirement::Type::NOTHING:
return DNF::True();
case tphdr::logic::requirement::Type::IMPOSSIBLE:
return DNF::False();
case tphdr::logic::requirement::Type::OR:
d = DNF::False();
for (auto& arg : req._args)
{
d = d.or_(evaluatePartialRequirement(bitIndex,
std::get<tphdr::logic::requirement::Requirement>(arg),
search,
formTime));
}
return d;
case tphdr::logic::requirement::Type::AND:
d = DNF::True();
for (auto& arg : req._args)
{
d = d.and_(evaluatePartialRequirement(bitIndex,
std::get<tphdr::logic::requirement::Requirement>(arg),
search,
formTime));
}
return d;
case tphdr::logic::requirement::Type::GOLDEN_BUGS:
[[fallthrough]];
case tphdr::logic::requirement::Type::ITEM:
// [[fallthrough]];
// case tphdr::logic::requirement::Type::HEALTH:
bits[bitIndex.reqBit(req)] = 1;
return DNF({bits});
case tphdr::logic::requirement::Type::EVENT:
event = std::get<int>(req._args[0]);
return search->eventExprs[event];
case tphdr::logic::requirement::Type::MACRO:
return evaluatePartialRequirement(bitIndex, search->world->GetMacro(std::get<int>(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 tphdr::logic::requirement::Type::COUNT:
expectedCount = std::get<int>(req._args[0]);
item = std::get<tphdr::logic::item::Item*>(req._args[1]);
for (auto i = 1; i <= expectedCount; i++)
{
tphdr::logic::requirement::Requirement newReq;
if (i == 1)
{
newReq = tphdr::logic::requirement::Requirement {tphdr::logic::requirement::Type::ITEM, {item}};
}
else
{
newReq = tphdr::logic::requirement::Requirement {tphdr::logic::requirement::Type::COUNT, {i, item}};
}
bits[bitIndex.reqBit(newReq)] = 1;
}
return DNF({bits});
case tphdr::logic::requirement::Type::DAY:
return (formTime & tphdr::logic::requirement::FormTime::DAY) ? DNF::True() : DNF::False();
case tphdr::logic::requirement::Type::NIGHT:
return (formTime & tphdr::logic::requirement::FormTime::NIGHT) ? DNF::True() : DNF::False();
case tphdr::logic::requirement::Type::HUMAN_LINK:
return (formTime & tphdr::logic::requirement::FormTime::HUMAN) ? DNF::True() : DNF::False();
case tphdr::logic::requirement::Type::WOLF_LINK:
return (formTime & tphdr::logic::requirement::FormTime::WOLF) ? DNF::True() : DNF::False();
case tphdr::logic::requirement::Type::TWILIGHT:
return (formTime & tphdr::logic::requirement::FormTime::TWILIGHT) ? DNF::True() : DNF::False();
case tphdr::logic::requirement::Type::INVALID:
default:
// actually needs to be some error state?
return DNF::False();
}
return DNF::False();
}
void visitReq(const tphdr::logic::requirement::Requirement& req,
std::function<void(const tphdr::logic::requirement::Requirement& req)> f,
tphdr::logic::world::World* world)
{
f(req);
if (req._type == tphdr::logic::requirement::Type::AND || req._type == tphdr::logic::requirement::Type::OR)
{
for (auto& arg : req._args)
{
visitReq(std::get<tphdr::logic::requirement::Requirement>(arg), f, world);
}
}
else if (req._type == tphdr::logic::requirement::Type::MACRO)
{
visitReq(world->GetMacro(std::get<int>(req._args[0])), f, world);
}
}
@@ -0,0 +1,94 @@
#pragma once
#include "../entrance.hpp"
#include "simplify_algebraic.hpp"
#include "../../utility/log.hpp"
#include <functional>
#include <iostream>
#include <map>
namespace tphdr::logic::area
{
class EventAccess;
class Area;
} // namespace tphdr::logic::area
namespace tphdr::logic::world
{
class World;
}
class FlattenSearch
{
public:
FlattenSearch() = default;
FlattenSearch(tphdr::logic::world::World* world_);
tphdr::logic::world::World* world = nullptr;
BitIndex bitIndex = BitIndex();
// partially computed requirements for areas at a
// given timeform and for events
std::unordered_map<int, DNF> eventExprs = {};
std::unordered_map<int, std::unordered_map<tphdr::logic::area::Area*, DNF>> areaExprs = {};
// nodes we haven't looked at we don't even need to bother with
std::set<tphdr::logic::entrance::Entrance*> exitsToTry = {};
std::set<tphdr::logic::area::EventAccess*> eventsToTry = {};
std::set<tphdr::logic::area::Area*> 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<tphdr::logic::area::Area*> recentlyUpdatedAreas = {};
std::set<int> recentlyUpdatedEvents = {};
std::set<tphdr::logic::area::Area*> newlyUpdatedAreas = {};
std::set<int> newlyUpdatedEvents = {};
std::unordered_map<void*, std::set<int>> remoteEventRequirements = {};
std::unordered_map<void*, std::set<std::string>> remoteAreaRequirements = {};
bool newThingsFound = false;
void doSearch();
bool wasUpdated(tphdr::logic::area::Area* area, void* thing);
void tryExits();
void tryEvents();
void tryTimeFormExpansion();
void andAreaFormTimes(tphdr::logic::area::Area* area);
DNF tryEventAtFormTime(tphdr::logic::area::EventAccess* event, const int& formTime);
DNF tryLocationAtFormTime(tphdr::logic::area::LocationAccess* location, const int& formTime);
DNF tryExitAtFormTime(tphdr::logic::entrance::Entrance* exit, const int& formTime);
};
template<typename T>
std::function<void(const tphdr::logic::requirement::Requirement& req)> visitor(T* thing, FlattenSearch* search)
{
auto thingPtr = (void*)thing;
std::function<void(const tphdr::logic::requirement::Requirement&)> handler =
[=](const tphdr::logic::requirement::Requirement& req)
{
if (req._type == tphdr::logic::requirement::Type::EVENT)
{
if (!search->remoteEventRequirements.contains(thingPtr))
{
search->remoteEventRequirements[thingPtr] = {};
}
search->remoteEventRequirements[thingPtr].insert(std::get<int>(req._args[0]));
}
};
return handler;
}
void visitReq(const tphdr::logic::requirement::Requirement& req,
std::function<void(const tphdr::logic::requirement::Requirement& req)> f,
tphdr::logic::world::World* world);
DNF evaluatePartialRequirement(BitIndex& bitIndex,
const tphdr::logic::requirement::Requirement& req,
FlattenSearch* search,
const int& formTime);
@@ -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.
tphdr::logic::requirement::Requirement DNFToExpr(BitIndex& bitIndex, DNF dnf)
{
if (dnf.isTriviallyFalse())
{
return tphdr::logic::requirement::Requirement {tphdr::logic::requirement::Type::IMPOSSIBLE, {}};
}
if (dnf.isTriviallyTrue())
{
return tphdr::logic::requirement::Requirement {tphdr::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<BitVector> expr = {};
for (const auto& t : dnf.terms)
{
std::list<int> 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 == tphdr::logic::requirement::Type::COUNT)
{
auto count = std::get<int>(req._args[0]);
auto item = std::get<tphdr::logic::item::Item*>(req._args[1]);
for (int i = 1; i < count; i++)
{
auto lesserBit = bitIndex.reqBit(
tphdr::logic::requirement::Requirement {tphdr::logic::requirement::Type::COUNT, {i, item}});
term.clear(lesserBit);
}
}
}
}
auto commonFactors = expr[0].ints();
for (const auto& term : expr)
{
std::set<int> 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<int> varSet = {};
for (auto& term : expr)
{
for (const auto& c : commonFactors)
{
term.clear(c);
}
for (const auto& b : term.ints())
{
varSet.insert(b);
}
}
std::vector<int> variables = std::vector<int>(varSet.begin(), varSet.end());
if (variables.empty())
{
return createAnd(lookupRequirements(bitIndex, commonFactors));
}
std::vector<BitVector> seen = {};
auto kernels = findKernels(expr, variables, BitVector(), seen);
kernels = tphdr::utility::container::FilterFromVector(kernels, [](const auto& k) { return !k.coKernel.isEmpty(); });
// columns are unique cubes in all kernels
std::vector<BitVector> 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<std::vector<int>> 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<int>, std::vector<int>>& 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::tuple<std::vector<int>, std::vector<int>>> allRects = {};
std::vector<int> rows_;
std::vector<int> 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<int>& rows__, const std::vector<int>& 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<BitVector> 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 = tphdr::logic::requirement::Requirement();
product._type = tphdr::logic::requirement::Type::AND;
std::vector<std::bitset<512>> quotBits;
std::vector<std::bitset<512>> 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 = tphdr::logic::requirement::Requirement();
if (!remainder.empty())
{
std::vector<std::bitset<512>> remainderBits;
for (const auto& c : remainder)
{
remainderBits.push_back(c.bitset);
}
sum._type = tphdr::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 = tphdr::logic::requirement::Requirement();
terms._type = tphdr::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);
}
tphdr::logic::requirement::Requirement createAnd(std::vector<tphdr::logic::requirement::Requirement> terms)
{
if (terms.size() > 1)
{
tphdr::logic::requirement::Requirement req;
req._type = tphdr::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<FoundKernel> findKernels(const std::vector<BitVector>& cubes,
const std::vector<int>& variables,
const BitVector& coKernelPath,
std::vector<BitVector>& seenCoKernels,
int minIdx /* = 0 */)
{
std::vector<FoundKernel> 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<BitVector> 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<BitVector>, std::vector<BitVector>> algebraicDivision(const std::vector<BitVector>& expr,
const std::vector<BitVector>& divisor)
{
std::vector<BitVector> 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<BitVector> 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<BitVector> 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<std::bitset<512>> quotBits = {};
for (auto& i : quot)
{
quotBits.push_back(i.bitset);
}
std::vector<std::bitset<512>> divisorBits = {};
for (auto& i : divisor)
{
divisorBits.push_back(i.bitset);
}
DNF product = DNF(quotBits).and_(DNF(divisorBits)).dedup();
std::vector<BitVector> 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};
}
@@ -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 <algorithm>
#include <iterator>
#include <functional>
struct FoundKernel
{
std::vector<BitVector> kernel;
BitVector coKernel;
};
tphdr::logic::requirement::Requirement DNFToExpr(BitIndex& bitIndex, DNF dnf);
tphdr::logic::requirement::Requirement createAnd(std::vector<tphdr::logic::requirement::Requirement> 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<typename Func>
void genRectangles(std::vector<int>& rows, std::vector<int>& cols, std::vector<std::vector<int>>& 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<int> 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<int> 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<typename Func>
void genRectanglesRecursive(std::vector<int>& allRows,
std::vector<int>& allCols,
std::vector<std::vector<int>>& matrix,
const int& index,
std::vector<int> rectRows,
std::vector<int> 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<std::vector<int>> 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<int> rect1Rows;
std::copy_if(allRows.begin(),
allRows.end(),
std::back_inserter(rect1Rows),
[=](const int& row) { return matrix[row][c]; });
std::vector<int> 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<FoundKernel> findKernels(const std::vector<BitVector>& cubes,
const std::vector<int>& variables,
const BitVector& coKernelPath,
std::vector<BitVector>& seenCoKernels,
int minIdx = 0);
std::pair<std::vector<BitVector>, std::vector<BitVector>> algebraicDivision(const std::vector<BitVector>& expr,
const std::vector<BitVector>& divisor);
template<typename Container>
std::vector<tphdr::logic::requirement::Requirement> lookupRequirements(BitIndex& bitIndex, Container r)
{
std::vector<tphdr::logic::requirement::Requirement> reqs;
for (auto& bit : r)
{
reqs.push_back(bitIndex.reverseIndex[bit]);
}
return reqs;
}
+114
View File
@@ -0,0 +1,114 @@
#include "generate.hpp"
#include "entrance_shuffle.hpp"
#include "fill.hpp"
#include "flatten/flatten.hpp"
#include "plandomizer.hpp"
#include "search.hpp"
#include "spoiler_log.hpp"
#include "../seedgen/config.hpp"
#include "../seedgen/settings.hpp"
#include "../utility/log.hpp"
#include "../utility/time.hpp"
#include <iostream>
namespace tphdr::logic::generate
{
tphdr::logic::world::WorldPool GenerateWorlds()
{
#ifdef ENABLE_TIMING
tphdr::utility::time::ScopedTimer<"Seed generation took ", std::chrono::milliseconds> timer;
#endif
tphdr::seedgen::config::Config config;
config.LoadFromFile(SETTINGS_PATH, PREFERENCES_PATH);
tphdr::utility::platform::Log(std::string("Seed: ") + config.GetSeed());
tphdr::logic::world::WorldPool worlds = {};
GenerateRandomizer(config, worlds);
return std::move(worlds);
}
void GenerateRandomizer(tphdr::seedgen::config::Config& config, tphdr::logic::world::WorldPool& worlds)
{
tphdr::seedgen::config::SeedRNG(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 = config.GetHash();
tphdr::utility::platform::Log(std::string("Hash: ") + hash);
// Build all worlds
int worldId = 1;
for (const auto& settings : config.GetSettingsList())
{
std::unique_ptr<tphdr::logic::world::World> world = std::make_unique<tphdr::logic::world::World>(worldId++);
world->SetSettings(settings);
world->ResolveRandomSettings();
world->ResolveConflictingSettings();
world->Build();
worlds.emplace_back(std::move(world));
}
// Give each world a pointer to the world pool
for (auto& world : worlds)
{
world->SetWorlds(&worlds);
}
// Process Plando Data for all worlds
if (config.IsUsingPlandomizer())
{
tphdr::logic::plandomizer::LoadPlandomizerData(worlds, config.GetPlandomizerPath());
}
// Pre Entrance Shuffle Tasks
for (auto& world : worlds)
{
world->PerformPreEntranceShuffleTasks();
}
tphdr::utility::platform::Log("Shuffling Entrances...");
for (auto& world : worlds)
{
tphdr::logic::entrance_shuffle::ShuffleWorldEntrances(world.get(), worlds);
}
// Post Entrance Shuffle Tasks
for (auto& world : worlds)
{
world->PerformPostEntranceShuffleTasks();
}
tphdr::logic::fill::CacheExitTimeForms(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)
tphdr::utility::platform::Log("Flattening...");
FlattenSearch search = FlattenSearch(worlds.at(0).get());
search.doSearch();
tphdr::utility::platform::Log("Filling Worlds...");
tphdr::logic::fill::FillWorlds(worlds);
// Post Fill Tasks
for (auto& world : worlds)
{
world->PerformPostFillTasks();
}
// Generate Playthrough
tphdr::logic::search::GeneratePlaythrough(&worlds);
// TODO: Generate Hints
// Write Logs
if (config.IsGeneratingSpoilerLog())
{
tphdr::logic::spoiler_log::GenerateSpoilerLog(worlds, config);
}
tphdr::logic::spoiler_log::GenerateAntiSpoilerLog(worlds, config);
}
} // namespace tphdr::logic::generate
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "world.hpp"
#include "../seedgen/config.hpp"
namespace tphdr::logic::generate
{
/**
* @brief Generates a complete randomizer seed
*
* @param worlds The list of worlds for the generated randomizer seed
* @return the worldpool which was generated
*/
tphdr::logic::world::WorldPool GenerateWorlds();
/**
* @brief Generates a complete randomizer seed with the provided config
*
* @param config The configuration to use for this seed
* @param worlds The list of worlds for the generated randomizer seed
* @return 0 if no errors. 1 if there were errors
*/
void GenerateRandomizer(tphdr::seedgen::config::Config& config, tphdr::logic::world::WorldPool& worlds);
} // namespace tphdr::logic::generate
+157
View File
@@ -0,0 +1,157 @@
#include "item.hpp"
#include "world.hpp"
namespace tphdr::logic::item
{
Importance ImportanceFromStr(const std::string& str)
{
std::unordered_map<std::string, Importance> 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,
tphdr::logic::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;
}
}
int Item::GetID() const
{
return this->_id;
}
std::string Item::GetName() const
{
return this->_name;
}
tphdr::logic::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<Location*> 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<Item> Nothing =
std::make_unique<Item>(-1, "Nothing", nullptr, Importance::JUNK, false, false, false, false, false);
} // namespace tphdr::logic::item
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include <string>
#include <list>
#include <memory>
namespace tphdr::logic::world
{
class World;
};
class Location;
namespace tphdr::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,
tphdr::logic::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;
tphdr::logic::world::World* GetWorld() const;
Importance GetImportance() const;
bool IsMajor() const;
bool IsMinor() const;
bool isJunk() const;
bool IsGameWinningItem() const;
std::list<Location*> 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;
tphdr::logic::world::World* _world = nullptr;
Importance _importance = INVALID;
bool _gameWinningItem = false;
std::list<Location*> _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<Item> Nothing;
} // namespace tphdr::logic::item
+348
View File
@@ -0,0 +1,348 @@
#include "item_pool.hpp"
#include "world.hpp"
#include <map>
namespace tphdr::logic::item_pool
{
std::map<std::string, int> 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},
{"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},
// 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<std::string, int> 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<std::string, int> 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<std::string, int> 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(tphdr::logic::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 North Faron Woods Gate Key if we're skipping prologue
if (world->Setting("Skip Prologue") == "On")
{
itemPool.at("North Faron Woods Gate Key") = 0;
}
// Remove the bulblin camp key if we're skipping bulblin camp
if (world->Setting("Arbiters Does Not Require Bulblin Camp") == "On")
{
itemPool.at("Gerudo Desert Bulblin Camp Key") = 0;
}
// Remove sky book characters if we're starting with the sky canon open
if (world->Setting("City Does Not Require Filled Skybook") == "On")
{
itemPool.at("Progressive Sky Book") = 0;
}
// Remove Small Keys if we're playing without them
if (world->Setting("Small Keys") == "Keysy")
{
std::list<std::string> 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.at(key) = 0;
}
}
// Remove Big Keys if we're playing without them
if (world->Setting("Big Keys") == "Keysy")
{
std::list<std::string> 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"},
{"Hyrule Castle Big Key"},
};
for (const auto& key : bigKeys)
{
itemPool.at(key) = 0;
}
}
// 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(tphdr::logic::world::World* world)
{
const auto& startingItems = world->GetSettings().GetStartingInventory();
auto& startingItemPool = world->GetStartingItemPool();
auto& itemPool = world->GetItemPool();
// 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);
}
tphdr::utility::container::Erase(itemPool, item, count);
}
}
std::map<std::string, int> GetInitialJunkPool()
{
return initialJunkPool;
}
ItemPool GetCompleteItemPool(tphdr::logic::world::WorldPool& worlds)
{
ItemPool completeItemPool = {};
for (const auto& world : worlds)
{
auto& worldItemPool = world->GetItemPool();
std::copy(worldItemPool.begin(), worldItemPool.end(), std::back_inserter(completeItemPool));
}
return completeItemPool;
}
} // namespace tphdr::logic::item_pool
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <vector>
#include <map>
#include <string>
#include <memory>
// Foward Declarations
namespace tphdr::logic::world
{
class World;
using WorldPool = std::vector<std::unique_ptr<World>>;
} // namespace tphdr::logic::world
namespace tphdr::logic::item
{
class Item;
}
namespace tphdr::logic::item_pool
{
using ItemPool = std::vector<tphdr::logic::item::Item*>;
/**
* @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(tphdr::logic::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(tphdr::logic::world::World* world);
std::map<std::string, int> GetInitialJunkPool();
ItemPool GetCompleteItemPool(tphdr::logic::world::WorldPool& worlds);
} // namespace tphdr::logic::item_pool
+144
View File
@@ -0,0 +1,144 @@
#include "location.hpp"
#include "world.hpp"
#include "../utility/log.hpp"
namespace tphdr::logic::location
{
Location::Location(const int& id,
const std::string& name,
std::unordered_set<std::string> categories,
tphdr::logic::world::World* world,
tphdr::logic::item::Item* originalItem,
const bool& goalLocation,
const std::string& hintPriority):
_id(id),
_name(name),
_categories(categories),
_world(world),
_originalItem(originalItem),
_goalLocation(goalLocation),
_hintPriority(hintPriority)
{
this->_computedRequirement._type = tphdr::logic::requirement::Type::IMPOSSIBLE;
}
int Location::GetID() const
{
return this->_id;
}
std::string Location::GetName() const
{
return this->_name;
}
tphdr::logic::world::World* Location::GetWorld() const
{
return this->_world;
}
bool Location::IsGoalLocation() const
{
return this->_goalLocation;
}
void Location::SetCurrentItem(tphdr::logic::item::Item* item)
{
LOG_TO_DEBUG("Placed " + item->GetName() + " at " + this->GetName());
this->_currentItem = item;
}
tphdr::logic::item::Item* Location::GetCurrentItem() const
{
return this->_currentItem;
}
void Location::RemoveCurrentItem()
{
LOG_TO_DEBUG("Removed " + this->GetCurrentItem()->GetName() + " at " + this->GetName());
this->_currentItem = tphdr::logic::item::Nothing.get();
}
bool Location::IsEmpty() const
{
return this->_currentItem == tphdr::logic::item::Nothing.get();
}
tphdr::logic::item::Item* Location::GetOriginalItem() const
{
return this->_originalItem;
}
tphdr::logic::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;
}
void Location::AddLocationAccess(tphdr::logic::area::LocationAccess* locAcc)
{
this->_locationAccessList.push_back(locAcc);
}
std::list<tphdr::logic::area::LocationAccess*> Location::GetAccessList() const
{
return this->_locationAccessList;
}
void Location::AddForbiddenItem(tphdr::logic::item::Item* forbiddenItem)
{
this->_forbiddenItems.insert(forbiddenItem);
LOG_TO_DEBUG(forbiddenItem->GetName() + " is forbidden from being placed at " + this->GetName());
}
const std::unordered_set<tphdr::logic::item::Item*>& Location::GetForbiddenItems()
{
return this->_forbiddenItems;
}
void Location::SetComputedRequirement(const tphdr::logic::requirement::Requirement& computedRequirement)
{
this->_computedRequirement = computedRequirement;
}
tphdr::logic::requirement::Requirement Location::GetComputedRequirement()
{
return this->_computedRequirement;
}
void Location::SetRegisteredLocationCategories(std::unordered_set<std::string>* registeredLocationCategories)
{
this->_registeredLocationCategories = registeredLocationCategories;
}
} // namespace tphdr::logic::location
+109
View File
@@ -0,0 +1,109 @@
#pragma once
#include "item.hpp"
#include "requirement.hpp"
#include <stdexcept>
#include <unordered_set>
#include <vector>
namespace tphdr::logic::world
{
class World;
}
namespace tphdr::logic::area
{
class LocationAccess;
}
namespace tphdr::logic::location
{
class Location
{
public:
Location(const int& id,
const std::string& name,
std::unordered_set<std::string> categories,
tphdr::logic::world::World* world,
tphdr::logic::item::Item* originalItem,
const bool& goalLocation,
const std::string& hintPriority);
int GetID() const;
std::string GetName() const;
tphdr::logic::world::World* GetWorld() const;
bool IsGoalLocation() const;
void SetCurrentItem(tphdr::logic::item::Item* currentItem);
tphdr::logic::item::Item* GetCurrentItem() const;
void RemoveCurrentItem();
bool IsEmpty() const;
tphdr::logic::item::Item* GetOriginalItem() const;
tphdr::logic::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;
void AddLocationAccess(tphdr::logic::area::LocationAccess* locAcc);
std::list<tphdr::logic::area::LocationAccess*> GetAccessList() const;
void AddForbiddenItem(tphdr::logic::item::Item* forbiddenItem);
const std::unordered_set<tphdr::logic::item::Item*>& GetForbiddenItems();
void SetComputedRequirement(const tphdr::logic::requirement::Requirement& computedRequirement);
tphdr::logic::requirement::Requirement GetComputedRequirement();
void SetRegisteredLocationCategories(std::unordered_set<std::string>* registeredLocationCategories);
/**
* @brief Checks to see if the location has all the passed in categories. If a passed in category was never registred,
* a std::runtime_error will be thrown.
* @param categoryNames paramater pack of string representations of category names
* @returns true if all passed in categories are present, false otherwise
*/
template<class... Types>
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<std::string> _categories = {};
tphdr::logic::world::World* _world;
tphdr::logic::item::Item* _originalItem = tphdr::logic::item::Nothing.get();
bool _goalLocation = false;
tphdr::logic::item::Item* _currentItem = tphdr::logic::item::Nothing.get();
bool _hasKnownVanillaItem = false;
std::list<tphdr::logic::area::LocationAccess*> _locationAccessList = {};
bool _progression = true; // Set as false later if applicable
bool _hinted = false;
std::string _hintPriority = "Never";
std::unordered_set<tphdr::logic::item::Item*> _forbiddenItems = {};
tphdr::logic::requirement::Requirement _computedRequirement;
/**
* @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<std::string>* _registeredLocationCategories = nullptr;
// Potential tracker stuff
tphdr::logic::item::Item* _trackedItem = tphdr::logic::item::Nothing.get();
};
using LocationPool = std::vector<Location*>;
} // namespace tphdr::logic::location
+113
View File
@@ -0,0 +1,113 @@
#include "plandomizer.hpp"
#include "world.hpp"
#include "../utility/yaml.hpp"
#include "../utility/file.hpp"
#include "../utility/log.hpp"
namespace tphdr::logic::plandomizer
{
void LoadPlandomizerData(tphdr::logic::world::WorldPool& worlds, const fspath& filepath, const bool& ignoreErrors /*false*/)
{
// Verify the file exists before trying to open it
// TODO: TRY CATCH HERE
tphdr::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("Plandomizer file locations for " + worldStr +
" is not a map. Please check your syntax before trying again.");
}
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<std::string>();
}
else
{
throw std::runtime_error("Plandomizer Error: Missing key \"item\" in node:\n" +
YAML::Dump(locationNode));
}
if (locationNode.second["World"])
{
worldId = locationNode.second["World"].as<int>();
if (worldId < 1 || worldId > worlds.size())
{
std::string errorMsg = "Plandomizer Error: 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<std::string>();
}
const std::string locationName = locationNode.first.as<std::string>();
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<std::string>();
auto targetName = entranceNode.second.as<std::string>();
auto entrance = world->GetEntrance(entranceName);
auto target = world->GetEntrance(targetName);
world->AddPlandomizedEntrance(entrance, target);
}
}
}
}
}
} // namespace tphdr::logic::plandomizer
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <filesystem>
#include <memory>
#include <vector>
using fspath = std::filesystem::path;
// Forward Declarations
namespace tphdr::logic::world
{
class World;
using WorldPool = std::vector<std::unique_ptr<World>>;
} // namespace tphdr::logic::world
namespace tphdr::logic::plandomizer
{
void LoadPlandomizerData(tphdr::logic::world::WorldPool& worlds, const fspath& filepath, const bool& ignoreErrors = false);
}
+622
View File
@@ -0,0 +1,622 @@
#include "requirement.hpp"
#include "search.hpp"
#include "world.hpp"
#include "../utility/container.hpp"
#include "../utility/log.hpp"
#include "../utility/string.hpp"
#include <algorithm>
#include <ranges>
#include <iostream>
namespace tphdr::logic::requirement
{
namespace FormTime
{
const std::vector<int> ALL_FORM_TIMES = {HUMAN_DAY, HUMAN_NIGHT, WOLF_DAY, WOLF_NIGHT};
const std::vector<int> ALL_FORM_TIMES_AND_TWILIGHT = {HUMAN_DAY, HUMAN_NIGHT, WOLF_DAY, WOLF_NIGHT, TWILIGHT};
const std::vector<int> 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 = "";
tphdr::logic::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<Requirement>(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<Requirement>(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<tphdr::logic::item::Item*>(this->_args[0]);
return item->GetName();
case Type::COUNT:
count = std::get<int>(this->_args[0]);
item = std::get<tphdr::logic::item::Item*>(this->_args[1]);
return "count(" + item->GetName() + ", " + std::to_string(count) + ")";
case Type::EVENT:
eventIndex = std::get<int>(this->_args[0]);
return "'Event_" + std::to_string(eventIndex) + "'";
case Type::MACRO:
macroIndex = std::get<int>(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<int>(this->_args[0]);
return "golden_bugs(" + std::to_string(count) + ")";
default:
return reqStr;
}
return reqStr;
}
Requirement ParseRequirementString(const std::string& reqStr,
tphdr::logic::world::World* world,
const bool& forceLogic /* = false */)
{
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<std::string> 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 != '=')
{
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 = tphdr::logic::requirement::Type::NOTHING;
return req;
}
// Then Human Link...
if (argStr == "Human Link")
{
req._type = tphdr::logic::requirement::Type::HUMAN_LINK;
return req;
}
// Then Wolf Link...
if (argStr == "Wolf Link")
{
req._type = tphdr::logic::requirement::Type::WOLF_LINK;
return req;
}
// Then Twilight...
if (argStr == "Twilight")
{
req._type = tphdr::logic::requirement::Type::TWILIGHT;
return req;
}
// Then an event...
if (argStr[0] == '\'')
{
req._type = tphdr::logic::requirement::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 = tphdr::logic::requirement::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 = tphdr::logic::requirement::Type::ITEM;
req._args.emplace_back(item);
return req;
}
// Then a setting...
else if (tphdr::utility::str::Contains(argStr, "!=", "=="))
{
bool equalComparison = tphdr::utility::str::Contains(argStr, "==");
bool notEqualComparison = tphdr::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();
}
if (result == true)
{
req._type = tphdr::logic::requirement::Type::NOTHING;
}
else
{
req._type = tphdr::logic::requirement::Type::IMPOSSIBLE;
}
return req;
}
// Then a count...
else if (argStr.find("count") != std::string::npos)
{
req._type = tphdr::logic::requirement::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);
// Get the arguments
auto& itemName = splitLogicStr[0];
int count = std::stoi(splitLogicStr[1]);
auto item = world->GetItem(itemName);
req._args.emplace_back(count);
req._args.emplace_back(item);
return req;
}
// Then Day...
if (argStr == "Day")
{
req._type = tphdr::logic::requirement::Type::DAY;
return req;
}
// Then Night...
if (argStr == "Night")
{
req._type = tphdr::logic::requirement::Type::NIGHT;
return req;
}
// And finally a health check
// else if (argStr.find("health") != std::string::npos)
// {
// req._type = tphdr::logic::requirement::Type::HEALTH;
// std::string numHeartsStr(argStr.begin() + argStr.find('(') + 1, argStr.end() - 1);
// int numHearts = std::stoi(numHeartsStr);
// req._args.emplace_back(numHearts);
// return req;
// }
// Check Impossible down here since it's very unlikely
else if (argStr == "Impossible")
{
req._type = tphdr::logic::requirement::Type::IMPOSSIBLE;
return req;
}
// Check golden bugs last since it's least likely
else if (argStr.find("golden bugs") != std::string::npos)
{
req._type = tphdr::logic::requirement::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;
}
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 = tphdr::utility::container::ElementInContainer(splitLogicStr, "and");
bool orType = tphdr::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 = tphdr::logic::requirement::Type::AND;
}
else
{
req._type = tphdr::logic::requirement::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.
tphdr::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& reqStr : splitLogicStr)
{
// Get rid of parenthesis surrounding each deeper expression
if (reqStr[0] == '(')
{
reqStr = reqStr.substr(1, reqStr.length() - 2);
}
req._args.push_back(ParseRequirementString(reqStr, world, forceLogic));
}
}
if (req._type != tphdr::logic::requirement::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 + "\"");
return req;
}
bool EvaluateRequirementAtFormTime(const tphdr::logic::requirement::Requirement& req,
tphdr::logic::search::Search* search,
const int& formTime,
tphdr::logic::world::World* world)
{
tphdr::logic::item::Item* item;
int count;
int eventIndex;
int macroIndex;
switch (req._type)
{
case Type::NOTHING:
return true;
case Type::IMPOSSIBLE:
return false;
case Type::OR:
return std::any_of(
req._args.begin(),
req._args.end(),
[&](const auto& arg)
{ return EvaluateRequirementAtFormTime(std::get<Requirement>(arg), search, formTime, world); });
case Type::AND:
return std::all_of(
req._args.begin(),
req._args.end(),
[&](const auto& arg)
{ return EvaluateRequirementAtFormTime(std::get<Requirement>(arg), search, formTime, world); });
case Type::ITEM:
item = std::get<tphdr::logic::item::Item*>(req._args[0]);
return search->_ownedItems.contains(item);
case Type::COUNT:
count = std::get<int>(req._args[0]);
item = std::get<tphdr::logic::item::Item*>(req._args[1]);
return search->_ownedItems.count(item) >= count;
case Type::EVENT:
eventIndex = std::get<int>(req._args[0]);
return search->_ownedEvents.contains(eventIndex);
case Type::MACRO:
macroIndex = std::get<int>(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<int>(req._args[0]);
return std::count_if(search->_ownedItems.begin(),
search->_ownedItems.end(),
[](const auto& item) { return item->IsGoldenBug(); }) >= count;
default:
return false;
}
return false;
}
EvalSuccess EvaluateEventRequirement(tphdr::logic::search::Search* search, tphdr::logic::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(tphdr::logic::search::Search* search, tphdr::logic::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::UNNECESSARY;
}
// 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 EvaluateLocationRequirement(tphdr::logic::search::Search* search, tphdr::logic::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 tphdr::logic::requirement
+115
View File
@@ -0,0 +1,115 @@
#pragma once
#include <memory>
#include <string>
#include <variant>
#include <vector>
// Forward declarations
namespace tphdr::logic::item
{
class Item;
}
namespace tphdr::logic::entrance
{
class Entrance;
}
namespace tphdr::logic::area
{
class EventAccess;
class LocationAccess;
} // namespace tphdr::logic::area
namespace tphdr::logic::world
{
class World;
}
namespace tphdr::logic::search
{
class Search;
}
namespace tphdr::logic::requirement
{
enum class Type
{
INVALID,
NOTHING,
IMPOSSIBLE,
OR,
AND,
ITEM,
COUNT,
EVENT,
MACRO,
DAY,
NIGHT,
HUMAN_LINK,
WOLF_LINK,
TWILIGHT,
GOLDEN_BUGS,
};
enum class EvalSuccess
{
NONE,
PARTIAL,
COMPLETE,
UNNECESSARY,
};
// 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<int> ALL_FORM_TIMES;
extern const std::vector<int> ALL_FORM_TIMES_AND_TWILIGHT;
extern const std::vector<int> ALL_FORM_AND_DAY_TIMES;
std::string to_string(const int& formTime);
}; // namespace FormTime
struct Requirement;
struct Requirement
{
using Argument = std::variant<int, Requirement, tphdr::logic::item::Item*>;
Type _type = Type::INVALID;
std::vector<Argument> _args;
std::string to_string() const;
};
Requirement ParseRequirementString(const std::string& reqStr,
tphdr::logic::world::World* world,
const bool& forceLogic = false);
bool EvaluateRequirementAtFormTime(const tphdr::logic::requirement::Requirement& req,
tphdr::logic::search::Search* search,
const int& formTime,
tphdr::logic::world::World*);
EvalSuccess EvaluateEventRequirement(tphdr::logic::search::Search* search, tphdr::logic::area::EventAccess* event);
EvalSuccess EvaluateExitRequirement(tphdr::logic::search::Search* search, tphdr::logic::entrance::Entrance* exit);
EvalSuccess EvaluateLocationRequirement(tphdr::logic::search::Search* search,
tphdr::logic::area::LocationAccess* locAccess);
const extern Requirement NO_REQUIREMENT;
const extern Requirement IMPOSSIBLE_REQUIREMENT;
} // namespace tphdr::logic::requirement
+650
View File
@@ -0,0 +1,650 @@
#include "search.hpp"
#include "world.hpp"
#include "../utility/general.hpp"
#include "../utility/platform.hpp"
#include <fstream>
namespace tphdr::logic::search
{
Search::Search(const SearchMode& searchMode,
tphdr::logic::world::WorldPool* worlds,
const tphdr::logic::item_pool::ItemPool& items /* = {} */,
const int& worldToSearch /* = -1 */):
_searchMode(searchMode), _worlds(worlds)
{
// Set the items we should already own
this->_ownedItems.insert(items.begin(), items.end());
// Add starting inventory items for each world
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()
{
// Get all locations which fit criteria to test on each iteration
std::list<tphdr::logic::area::LocationAccess*> 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() ||
tphdr::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 &&
tphdr::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 (tphdr::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 && tphdr::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 (tphdr::logic::requirement::EvaluateEventRequirement(this, event) ==
tphdr::logic::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 unnecessary, we'll just consider it successful and move on
auto evalSuccess = tphdr::logic::requirement::EvaluateExitRequirement(this, exit);
if (evalSuccess == tphdr::logic::requirement::EvalSuccess::UNNECESSARY)
{
this->_successfulExits.insert(exit);
}
else if (tphdr::utility::general::IsAnyOf(evalSuccess,
tphdr::logic::requirement::EvalSuccess::COMPLETE,
tphdr::logic::requirement::EvalSuccess::PARTIAL))
{
this->AddExitToEntranceSpheres(exit);
if (evalSuccess == tphdr::logic::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<tphdr::logic::area::LocationAccess*>& itemLocations)
{
std::list<tphdr::logic::location::Location*> 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 (tphdr::logic::requirement::EvaluateLocationRequirement(this, locAccess) ==
tphdr::logic::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 (tphdr::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(tphdr::logic::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 (tphdr::utility::general::IsAnyOf(this->_searchMode, SearchMode::GENERATE_PLAYTHROUGH, SearchMode::GAME_BEATABLE) &&
location->GetCurrentItem()->IsGameWinningItem())
{
if (std::count_if(this->_ownedItems.begin(),
this->_ownedItems.end(),
[](const auto& item) { return item->IsGameWinningItem(); }) == this->_worlds->size())
{
if (this->_searchMode == SearchMode::GENERATE_PLAYTHROUGH)
{
auto& lastSphere = this->_playthroughSpheres.back();
lastSphere.erase(
std::remove_if(lastSphere.begin(),
lastSphere.end(),
[](const auto& location) { return !location->GetCurrentItem()->IsGameWinningItem(); }),
lastSphere.end());
}
this->_isBeatable = true;
}
}
}
void Search::Explore(tphdr::logic::area::Area* area)
{
for (const auto& event : area->GetEvents())
{
this->_eventsToTry.push_back(event);
}
for (const auto& exit : area->GetExits())
{
auto evalSuccess = tphdr::logic::requirement::EvaluateExitRequirement(this, exit);
switch (evalSuccess)
{
case tphdr::logic::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 tphdr::logic::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 tphdr::logic::requirement::EvalSuccess::NONE:
this->_exitsToTry.push_back(exit);
case tphdr::logic::requirement::EvalSuccess::UNNECESSARY:
this->_foundDisconnectedExit = true;
}
}
}
void Search::ExpandFormTimes(tphdr::logic::area::Area* area)
{
using namespace tphdr::logic::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(tphdr::logic::entrance::Entrance* exit)
{
if (tphdr::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());
}
}
}
}
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 = ":<br/>";
auto& areaFormTime = this->_areaFormTime[area.get()];
if (areaFormTime & tphdr::logic::requirement::FormTime::HUMAN)
{
formTimeStr += " Human";
}
if (areaFormTime & tphdr::logic::requirement::FormTime::WOLF)
{
formTimeStr += " Wolf";
}
if (areaFormTime & tphdr::logic::requirement::FormTime::DAY)
{
formTimeStr += " Day";
}
if (areaFormTime & tphdr::logic::requirement::FormTime::NIGHT)
{
formTimeStr += " Night";
}
if (areaFormTime & tphdr::logic::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())
{
auto 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())
{
auto 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();
auto color = this->_visitedLocations.contains(location) ? "black" : "red";
worldGraph << "\t\"" << location->GetName() << "\"[label=<" << location->GetName() << ":<br/>"
<< location->GetCurrentItem()->GetName() << "> shape=\"plain\" fontcolor=\"" << color << "\"];";
worldGraph << "\t\"" << areaName << "\" -> \"" << location->GetName() << "\"[dir=forward color=\"" << color
<< "\"]";
}
}
worldGraph << "}";
worldGraph.close();
}
std::optional<std::string> VerifyLogic(tphdr::logic::world::WorldPool* worlds,
const tphdr::logic::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::count_if(search._visitedLocations.begin(),
search._visitedLocations.end(),
[&](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<tphdr::logic::location::Location*> 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(tphdr::logic::world::WorldPool* worlds)
{
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<tphdr::logic::location::Location*, tphdr::logic::item::Item*> tempEmptyLocations = {};
// Keep track of all the locations that appear in the playthrough
std::unordered_set<tphdr::logic::location::Location*> 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();
}
}
}
tphdr::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<tphdr::logic::entrance::Entrance*, tphdr::logic::area::Area*> nonRequiredEntrances = {};
for (auto& sphere : entranceSpheres)
{
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.erase(std::remove(sphere.begin(), sphere.end(), entrance), sphere.end());
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)
{
auto sphereCopy = sphere;
for (const auto& location : sphereCopy)
{
if (!playthroughLocationsSet.contains(location))
{
sphere.erase(std::remove(sphere.begin(), sphere.end(), location), sphere.end());
}
}
}
// Remove any empty spheres
newSearch.RemoveEmptySpheres();
worlds->at(0)->SetPlaythroughSpheres(newSearch._playthroughSpheres);
worlds->at(0)->SetEntranceSpheres(newSearch._entranceSpheres);
}
bool GameBeatable(tphdr::logic::world::WorldPool* worlds, const tphdr::logic::item_pool::ItemPool& items /* = {} */)
{
auto search = Search::Beatable(worlds, items);
search.SearchWorlds();
return search._isBeatable;
}
} // namespace tphdr::logic::search
+161
View File
@@ -0,0 +1,161 @@
#pragma once
#include "item_pool.hpp"
#include "../utility/log.hpp"
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <list>
#include <memory>
#include <iostream>
#include <optional>
// Forward Declarations (we have a lot here)
namespace tphdr::logic::world
{
class World;
using WorldPool = std::vector<std::unique_ptr<World>>;
} // namespace tphdr::logic::world
namespace tphdr::logic::item
{
class Item;
}
namespace tphdr::logic::location
{
class Location;
}
namespace tphdr::logic::area
{
class EventAccess;
class LocationAccess;
class Area;
} // namespace tphdr::logic::area
namespace tphdr::logic::entrance
{
class Entrance;
}
namespace tphdr::logic::search
{
enum class SearchMode
{
ACCESSIBLE_LOCATIONS,
GAME_BEATABLE,
ALL_LOCATIONS_REACHABLE,
GENERATE_PLAYTHROUGH,
SPHERE_ZERO,
TRACKER_SPHERES
};
class Search
{
public:
Search(const SearchMode& searchMode,
tphdr::logic::world::WorldPool* worlds,
const tphdr::logic::item_pool::ItemPool& items = {},
const int& worldToSearch = -1);
static auto Accessible(tphdr::logic::world::WorldPool* worlds,
const tphdr::logic::item_pool::ItemPool& items = {},
const int& worldToSearch = -1)
{
return Search(SearchMode::ACCESSIBLE_LOCATIONS, worlds, items, worldToSearch);
}
static auto AllLocationsReachable(tphdr::logic::world::WorldPool* worlds,
const tphdr::logic::item_pool::ItemPool& items = {},
const int& worldToSearch = -1)
{
return Search(SearchMode::ALL_LOCATIONS_REACHABLE, worlds, items, worldToSearch);
}
static auto Playthrough(tphdr::logic::world::WorldPool* worlds,
const tphdr::logic::item_pool::ItemPool& items = {},
const int& worldToSearch = -1)
{
return Search(SearchMode::GENERATE_PLAYTHROUGH, worlds, items, worldToSearch);
}
static auto Beatable(tphdr::logic::world::WorldPool* worlds,
const tphdr::logic::item_pool::ItemPool& items = {},
const int& worldToSearch = -1)
{
return Search(SearchMode::GAME_BEATABLE, worlds, items, worldToSearch);
}
static auto SphereZero(tphdr::logic::world::WorldPool* worlds,
const tphdr::logic::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<tphdr::logic::area::LocationAccess*>& itemLocations);
void ProcessLocation(tphdr::logic::location::Location* location);
void Explore(tphdr::logic::area::Area* area);
void ExpandFormTimes(tphdr::logic::area::Area* area);
void AddExitToEntranceSpheres(tphdr::logic::entrance::Entrance*);
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 <filename> -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;
tphdr::logic::world::WorldPool* _worlds;
int _worldToSearch = -1;
// Search variables
int _sphereNum = 0;
bool _newThingsFound = true;
bool _isBeatable = false;
bool _collectItems = true;
std::unordered_set<int> _ownedEvents;
std::unordered_multiset<tphdr::logic::item::Item*> _ownedItems;
std::list<tphdr::logic::area::EventAccess*> _eventsToTry;
std::list<tphdr::logic::entrance::Entrance*> _exitsToTry;
std::unordered_set<tphdr::logic::location::Location*> _visitedLocations;
std::unordered_set<tphdr::logic::area::Area*> _visitedAreas;
std::unordered_set<tphdr::logic::entrance::Entrance*> _successfulExits;
std::unordered_set<tphdr::logic::entrance::Entrance*> _playthroughEntrances;
bool _foundDisconnectedExit = false;
std::list<std::list<tphdr::logic::location::Location*>> _playthroughSpheres;
std::list<std::list<tphdr::logic::entrance::Entrance*>> _entranceSpheres;
std::unordered_map<tphdr::logic::area::Area*, int> _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<std::string> VerifyLogic(tphdr::logic::world::WorldPool* worlds,
const tphdr::logic::item_pool::ItemPool& items = {});
void GeneratePlaythrough(tphdr::logic::world::WorldPool* worlds);
bool GameBeatable(tphdr::logic::world::WorldPool* worlds, const tphdr::logic::item_pool::ItemPool& items = {});
} // namespace tphdr::logic::search
+247
View File
@@ -0,0 +1,247 @@
#include "spoiler_log.hpp"
#include "entrance_shuffle.hpp"
#include "../utility/file.hpp"
#include "../utility/platform.hpp"
#include "../utility/yaml.hpp"
#include <algorithm>
#include <iostream>
#include <fstream>
namespace tphdr::logic::spoiler_log
{
std::string SpoilerFormatLocation(tphdr::logic::location::Location* location, const size_t& longestNameLength)
{
auto numSpaces = longestNameLength - location->GetName().length();
std::string spaces(numSpaces, ' ');
return location->GetName() + ": " + spaces + location->GetCurrentItem()->GetName();
}
std::string SpoilerFormatEntrance(tphdr::logic::entrance::Entrance* entrance, const size_t& longestNameLength)
{
auto numSpaces = longestNameLength - entrance->GetOriginalName().length();
std::string spaces(numSpaces, ' ');
auto replacement = entrance->GetReplaces();
auto parent = replacement->GetParentArea()->GetName();
auto connected = replacement->GetOriginalConnectedArea()->GetName();
return entrance->GetOriginalName() + ": " + spaces + connected + " from " + parent;
}
void LogBasicInfo(std::ofstream& log, tphdr::seedgen::config::Config& config, tphdr::logic::world::WorldPool& worlds)
{
log << "Twilight Princess HD Randomizer Version: " << "1.0.0" << std::endl;
log << "Seed: " << config.GetSeed() << std::endl;
// TODO: Setting string
log << "Hash: " << config.GetHash() << std::endl;
}
void LogSettings(std::ofstream& log, tphdr::seedgen::config::Config& config, tphdr::logic::world::WorldPool& worlds)
{
log << std::endl << "# Settings" << std::endl;
log << YAML::Dump(config.SettingsToYaml()) << std::endl;
}
void GenerateSpoilerLog(tphdr::logic::world::WorldPool& worlds, tphdr::seedgen::config::Config& config)
{
tphdr::utility::platform::Log("Generating Spoiler Log");
// Create logs folder if it doesn't exist
if (!tphdr::utility::file::dirExists(LOGS_PATH))
{
tphdr::utility::file::create_directories(LOGS_PATH);
}
std::string filepath = std::string(LOGS_PATH) + config.GetHash() + " Spoiler Log.txt";
std::ofstream spoilerLog;
spoilerLog.open(filepath);
LogBasicInfo(spoilerLog, config, worlds);
// Gather worlds with starting inventories
std::list<tphdr::logic::world::World*> 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 << "Starting Inventory:" << std::endl;
for (const auto& world : worldswithStartingInventories)
{
spoilerLog << " World " << world->GetID() << ":" << std::endl;
for (const auto& item : world->GetStartingItemPool())
{
spoilerLog << " - " << item->GetName() << std::endl;
}
}
}
// TODO: Print required dungeons
// Get name lengths for pretty formatting
size_t longestNameLength = 0;
for (const auto& sphere : worlds.at(0)->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 : worlds.at(0)->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 : worlds.at(0)->GetEntranceSpheres())
{
for (const auto& entrance : sphere)
{
longestNameLength = std::max(entrance->GetOriginalName().length(), longestNameLength);
}
}
// Print entrance playthrough
sphereNum = 0;
if (longestNameLength != 0)
{
spoilerLog << std::endl << "Entrance Playthrough:" << std::endl;
}
for (auto& sphere : worlds.at(0)->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->GetOriginalName().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 = tphdr::logic::entrance_shuffle::CreateEntrancePools(world.get());
auto mixedPools = world->GetSettings().GetMixedEntrancePools();
for (auto& [entranceType, entrancePool] : entrancePools)
{
auto typeStr = tphdr::logic::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::sort(entrancePool.begin(),
entrancePool.end(),
[](auto& e1, auto& e2) { return e1->GetID() < e2->GetID(); });
for (const auto& entrance : entrancePool)
{
// Ignore entrances that are impossible
if (entrance->GetRequirement()._type == tphdr::logic::requirement::Type::IMPOSSIBLE)
{
continue;
}
spoilerLog << " " << SpoilerFormatEntrance(entrance, longestNameLength) << std::endl;
}
}
}
}
// TODO: Hints
// Log Settings
LogSettings(spoilerLog, config, worlds);
spoilerLog.close();
tphdr::utility::platform::Log("Wrote spoiler log to " + filepath);
}
void GenerateAntiSpoilerLog(tphdr::logic::world::WorldPool& worlds, tphdr::seedgen::config::Config& config)
{
// Create logs folder if it doesn't exist
if (!tphdr::utility::file::dirExists(LOGS_PATH))
{
tphdr::utility::file::create_directories(LOGS_PATH);
}
std::string filepath = std::string(LOGS_PATH) + config.GetHash() + " Anti-Spoiler Log.txt";
std::ofstream antiSpoilerLog;
antiSpoilerLog.open(filepath);
LogBasicInfo(antiSpoilerLog, config, worlds);
LogSettings(antiSpoilerLog, config, worlds);
}
} // namespace tphdr::logic::spoiler_log
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "world.hpp"
#include "../seedgen/config.hpp"
namespace tphdr::logic::spoiler_log
{
void GenerateSpoilerLog(tphdr::logic::world::WorldPool& worlds, tphdr::seedgen::config::Config& config);
void GenerateAntiSpoilerLog(tphdr::logic::world::WorldPool& worlds, tphdr::seedgen::config::Config& config);
} // namespace tphdr::logic::spoiler_log

Some files were not shown because too many files have changed in this diff Show More