diff --git a/CMake/Packaging-2.cmake b/CMake/Packaging-2.cmake
index c6e501c73..3525ae1e4 100644
--- a/CMake/Packaging-2.cmake
+++ b/CMake/Packaging-2.cmake
@@ -1,6 +1,6 @@
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY 0)
-set(CPACK_COMPONENTS_ALL "ship" "appimage")
+set(CPACK_COMPONENTS_ALL "ship" "extractor" "appimage")
if (NOT CPACK_GENERATOR STREQUAL "External")
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "appimage")
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3091f8824..f7ad4089d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
-project(Ship VERSION 7.1.0 LANGUAGES C CXX)
-set(PROJECT_BUILD_NAME "Sulu Alfa" CACHE STRING "")
+project(Ship VERSION 7.1.1 LANGUAGES C CXX)
+set(PROJECT_BUILD_NAME "Sulu Bravo" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
@@ -98,14 +98,14 @@ set_property(TARGET soh PROPERTY APPIMAGE_ICON_FILE "${CMAKE_BINARY_DIR}/sohIcon
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
install(PROGRAMS "${CMAKE_SOURCE_DIR}/scripts/linux/appimage/soh.sh" DESTINATION . COMPONENT appimage)
-install(FILES "${CMAKE_SOURCE_DIR}/soh.otr" DESTINATION . COMPONENT appimage)
-install(TARGETS ZAPD DESTINATION ./assets/extractor COMPONENT appimage)
-install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/extractor/" DESTINATION ./assets/extractor COMPONENT appimage)
-install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/xml/" DESTINATION ./assets/extractor/xmls COMPONENT appimage)
-install(DIRECTORY "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/filelists/" DESTINATION ./assets/extractor/filelists COMPONENT appimage)
-install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ActorList_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT appimage)
-install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ObjectList_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT appimage)
-install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/SymbolMap_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT appimage)
+install(FILES "${CMAKE_SOURCE_DIR}/soh.otr" DESTINATION . COMPONENT ship)
+install(TARGETS ZAPD DESTINATION ./assets/extractor COMPONENT extractor)
+install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/extractor/" DESTINATION ./assets/extractor COMPONENT extractor)
+install(DIRECTORY "${CMAKE_SOURCE_DIR}/soh/assets/xml/" DESTINATION ./assets/extractor/xmls COMPONENT extractor)
+install(DIRECTORY "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/filelists/" DESTINATION ./assets/extractor/filelists COMPONENT extractor)
+install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ActorList_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT extractor)
+install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ObjectList_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT extractor)
+install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/SymbolMap_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT extractor)
endif()
find_package(Python3 COMPONENTS Interpreter)
@@ -198,4 +198,4 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
endif()
set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_SOURCE_DIR}/CMake/Packaging-2.cmake)
-include(CMake/Packaging.cmake)
+include(CMake/Packaging.cmake)
\ No newline at end of file
diff --git a/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json b/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json
index c1c472591..10b016b32 100644
--- a/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json
+++ b/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json
@@ -1,24 +1,35 @@
{
- "health": "health $0",
- "magic": "magic $0",
- "rupees": "rupees $0",
- "0": "Deku Stick $0",
- "1": "Deku Nut $0",
- "2": "Bomb $0",
- "3": "Fairy Bow $0",
+ "health": "Health - $0 Hearts",
+ "magic": "Magic - $0",
+ "rupees": "Rupees - $0",
+ "floor": "Floor $0",
+ "basement": "Basement $0",
+ "item_menu": "Select Item",
+ "map_menu": "Map - $0",
+ "quest_menu": "Quest Status",
+ "equip_menu": "Equipment",
+ "overworld": "Overworld",
+ "equipped": "$0 - Equipped",
+ "save_prompt": "Would you like to save?",
+ "game_saved": "Game saved",
+ "assigned_to": "Assigned to $0",
+ "0": "Deku Stick - $0",
+ "1": "Deku Nut - $0",
+ "2": "Bomb - $0",
+ "3": "Fairy Bow - $0",
"4": "Fire Arrow",
"5": "Din's Fire",
- "6": "Fairy Slingshot $0",
+ "6": "Fairy Slingshot - $0",
"7": "Fairy Ocarina",
"8": "Ocarina of Time",
- "9": "Bombchu $0",
+ "9": "Bombchu - $0",
"10": "Hookshot",
"11": "Longshot",
"12": "Ice Arrow",
"13": "Farore's Wind",
"14": "Boomerang",
"15": "Lens of Truth",
- "16": "Magic Beans $0",
+ "16": "Magic Beans - $0",
"17": "Megaton Hammer",
"18": "Light Arrow",
"19": "Nayru's Love",
@@ -115,8 +126,8 @@
"110": "Zora Sapphire",
"111": "Stone of Agony",
"112": "Gerudo's Card",
- "113": "Skulltula Token $0",
- "114": "Heart Container $0",
+ "113": "Skulltula Token - $0",
+ "114": "Piece of Heart - $0",
"115": "Piece of Heart",
"116": "Boss Key",
"117": "Compass",
@@ -124,7 +135,7 @@
"119": "Small Key",
"120": "MAGIC SMALL",
"121": "MAGIC LARGE",
- "122": "PIECE OF HEART 2",
+ "122": "Biggoron's Sword",
"123": "INVALID 1",
"124": "INVALID 2",
"125": "INVALID 3",
@@ -217,4 +228,4 @@
"311": "Lon Lon Ranch",
"312": "Question Mark",
"313": "Ganon's Castle"
-}
\ No newline at end of file
+}
diff --git a/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json b/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json
index 58898d70d..820eb4cbc 100644
--- a/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json
+++ b/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json
@@ -1,24 +1,35 @@
{
- "health": "vie $0",
- "magic": "magie $0",
- "rupees": "rubis $0",
- "0": "Bâton Mojo $0",
- "1": "Noix Mojo $0",
- "2": "Bombes $0",
- "3": "Arc des Fées $0",
+ "health": "Vie - $0 Coeurs",
+ "magic": "Magie - $0",
+ "rupees": "Rubis - $0",
+ "floor": "Étage $0",
+ "basement": "Sous-sol $0",
+ "item_menu": "Inventaire",
+ "map_menu": "Carte - $0",
+ "quest_menu": "Statut de la quête",
+ "equip_menu": "Equipment",
+ "overworld": "Surmonde",
+ "equipped": "$0 - Équipé",
+ "save_prompt": "Voulez-vous sauvegarder?",
+ "game_saved": "Jeu sauvegardé",
+ "assigned_to": "Assigné au $0",
+ "0": "Bâton Mojo - $0",
+ "1": "Noix Mojo - $0",
+ "2": "Bombes - $0",
+ "3": "Arc des Fées - $0",
"4": "Flèche de Feu",
"5": "Feu de Din",
- "6": "Lance-Pierre des Fées $0",
+ "6": "Lance-Pierre des Fées - $0",
"7": "Ocarina des Fées",
"8": "Ocarina of Temps",
- "9": "Missiles Teigneux $0",
+ "9": "Missiles Teigneux - $0",
"10": "Grappin",
"11": "Super Grappin",
"12": "Flèche de Glace",
"13": "Vent de Farore",
"14": "Boomerang",
"15": "Monocle de Vérité",
- "16": "Haricot Magique $0",
+ "16": "Haricot Magique - $0",
"17": "Masse des Titans",
"18": "Flèche de Lumière",
"19": "Amour de Nayru",
@@ -115,8 +126,8 @@
"110": "Saphir Zora",
"111": "Pierre de Souffrance",
"112": "Carte Gerudo",
- "113": "Skulltula d'or $0",
- "114": "Coeur d'Énergie $0",
+ "113": "Skulltula d'or - $0",
+ "114": "Quart de Coeur - $0",
"115": "Quart de Coeur",
"116": "Clé d'or",
"117": "Boussole",
@@ -124,7 +135,7 @@
"119": "Petite Clé",
"120": "PETITE BOUTEILLE DE MAGIE",
"121": "GRANDE BOUTEILLE DE MAGIE",
- "122": "QUART DE COEUR 2",
+ "122": "Épée de Biggoron",
"123": "INVALIDE 1",
"124": "INVALIDE 2",
"125": "INVALIDE 3",
@@ -217,4 +228,4 @@
"311": "Ranch Lon Lon",
"312": "Point d'interrogation",
"313": "Château de Ganon"
-}
\ No newline at end of file
+}
diff --git a/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json b/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json
index 460b32877..630d933fc 100644
--- a/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json
+++ b/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json
@@ -1,24 +1,35 @@
{
- "health": "Energie $0",
- "magic": "Magie $0",
- "rupees": "Rubine $0",
- "0": "Deku-Stab $0",
- "1": "Deku-Nuß $0",
- "2": "Bombe $0",
- "3": "Feen-Bogen $0",
+ "health": "Energie - $0 Herzen",
+ "magic": "Magie - $0",
+ "rupees": "Rubine - $0",
+ "floor": "Etage $0",
+ "basement": "Keller $0",
+ "item_menu": "Gegenstände",
+ "map_menu": "Karte - $0",
+ "quest_menu": "Quest Status",
+ "equip_menu": "Ausrüstung",
+ "overworld": "Überwelt",
+ "equipped": "$0 - Ausgerüstet",
+ "save_prompt": "Spielstand sichern?",
+ "game_saved": "Spielstand gesichert",
+ "assigned_to": "$0 zugeordnet",
+ "0": "Deku-Stab - $0",
+ "1": "Deku-Nuß - $0",
+ "2": "Bombe - $0",
+ "3": "Feen-Bogen - $0",
"4": "Feuer-Pfeil",
"5": "Dins Feuerinferno",
- "6": "Feen-Schleuder $0",
+ "6": "Feen-Schleuder - $0",
"7": "Feen-Okarina",
"8": "Okarina der Zeit",
- "9": "Krabbelmine $0",
+ "9": "Krabbelmine - $0",
"10": "Fanghaken",
"11": "Enterhaken",
"12": "Eis-Pfeil",
"13": "Farores Donnersturm",
"14": "Bumerang",
"15": "Auge der Wahrheit",
- "16": "Wundererbsen $0",
+ "16": "Wundererbsen - $0",
"17": "Stahlhammer",
"18": "Licht-Pfeil",
"19": "Nayrus Umarmung",
@@ -115,8 +126,8 @@
"110": "Zora-Saphir",
"111": "Stein des Wissens",
"112": "Gerudo-Paß",
- "113": "Skulltula-Symbol $0",
- "114": "Herzcontainer $0",
+ "113": "Skulltula-Symbol - $0",
+ "114": "Herzteil - $0",
"115": "Herzteil",
"116": "Master-Schlüssel",
"117": "Kompaß",
@@ -124,7 +135,7 @@
"119": "Kleiner Schlüssel",
"120": "MAGIE KLEIN",
"121": "MAGIE GROß",
- "122": "HERZTEIL 2",
+ "122": "Biggoron-Schwert",
"123": "UNGÜLTIG 1",
"124": "UNGÜLTIG 2",
"125": "UNGÜLTIG 3",
@@ -217,4 +228,4 @@
"311": "Lon Lon-Farm",
"312": "Fragezeichen",
"313": "Teufelsturm"
-}
\ No newline at end of file
+}
diff --git a/OTRExporter/assets/accessibility/texts/misc_eng.json b/OTRExporter/assets/accessibility/texts/misc_eng.json
index db59f3deb..6bff3b328 100644
--- a/OTRExporter/assets/accessibility/texts/misc_eng.json
+++ b/OTRExporter/assets/accessibility/texts/misc_eng.json
@@ -14,5 +14,11 @@
"input_button_c_left": "C Left",
"input_button_c_right": "C Right",
"input_analog_stick": "the Analog Stick",
- "input_d_pad": "the D-Pad"
-}
\ No newline at end of file
+ "input_d_pad": "the D-Pad",
+ "input_d_pad_up": "D-Pad Up",
+ "input_d_pad_down": "D-Pad Down",
+ "input_d_pad_left": "D-Pad Left",
+ "input_d_pad_right": "D-Pad Right",
+ "yes": "Yes",
+ "no": "No"
+}
diff --git a/OTRExporter/assets/accessibility/texts/misc_fra.json b/OTRExporter/assets/accessibility/texts/misc_fra.json
index e37268b95..0d9073e50 100644
--- a/OTRExporter/assets/accessibility/texts/misc_fra.json
+++ b/OTRExporter/assets/accessibility/texts/misc_fra.json
@@ -14,5 +14,11 @@
"input_button_c_left": "C Gauche",
"input_button_c_right": "C Droit",
"input_analog_stick": "le Stick Analogique",
- "input_d_pad": "D-Pad"
-}
\ No newline at end of file
+ "input_d_pad": "D-Pad",
+ "input_d_pad_up": "D-Pad Haut",
+ "input_d_pad_down": "D-Pad Bas",
+ "input_d_pad_left": "D-Pad Gauche",
+ "input_d_pad_right": "D-Pad Droit",
+ "yes": "Oui",
+ "no": "Non"
+}
diff --git a/OTRExporter/assets/accessibility/texts/misc_ger.json b/OTRExporter/assets/accessibility/texts/misc_ger.json
index 23b887861..2e07143f0 100644
--- a/OTRExporter/assets/accessibility/texts/misc_ger.json
+++ b/OTRExporter/assets/accessibility/texts/misc_ger.json
@@ -14,5 +14,11 @@
"input_button_c_left": "C Links",
"input_button_c_right": "C Rechts",
"input_analog_stick": "den Analog-Stick",
- "input_d_pad": "das Steuerkreuz"
-}
\ No newline at end of file
+ "input_d_pad": "das Steuerkreuz",
+ "input_d_pad_up": "Steuerkreuz Oben",
+ "input_d_pad_down": "Steuerkreuz Unten",
+ "input_d_pad_left": "Steuerkreuz Links",
+ "input_d_pad_right": "Steuerkreuz Rechts",
+ "yes": "Ja",
+ "no": "Nein"
+}
diff --git a/libultraship b/libultraship
index af368413f..0a5781296 160000
--- a/libultraship
+++ b/libultraship
@@ -1 +1 @@
-Subproject commit af368413f5c61557a7baf2a7a6ab35ba16a7affd
+Subproject commit 0a57812968539176bbeaa76c61532d0d6dec4881
diff --git a/scripts/linux/appimage/soh.sh b/scripts/linux/appimage/soh.sh
index 1afcbb9b7..e503680ac 100644
--- a/scripts/linux/appimage/soh.sh
+++ b/scripts/linux/appimage/soh.sh
@@ -69,6 +69,14 @@ while [[ (! -e "$SHIP_HOME"/oot.otr) || (! -e "$SHIP_HOME"/oot-mq.otr) ]]; do
continue
fi
;;
+ cfecfdc58d650e71a200c81f033de4e6d617a9f6)
+ if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
+ ROM=GC_MQ_D
+ OTRNAME="oot-mq.otr"
+ else
+ continue
+ fi
+ ;;
517bd9714c73cb96c21e7c2ef640d7b55186102f)
if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
ROM=GC_MQ_D
diff --git a/soh/assets/xml/GC_NMQ_PAL_F/code/fbdemo_circle.xml b/soh/assets/xml/GC_NMQ_PAL_F/code/fbdemo_circle.xml
index 5c598c8b4..a3b79fb17 100644
--- a/soh/assets/xml/GC_NMQ_PAL_F/code/fbdemo_circle.xml
+++ b/soh/assets/xml/GC_NMQ_PAL_F/code/fbdemo_circle.xml
@@ -1,14 +1,14 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
+
diff --git a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_En_Ganon_Mant.xml b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_En_Ganon_Mant.xml
index 900ec46ac..a5fc99347 100644
--- a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_En_Ganon_Mant.xml
+++ b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_En_Ganon_Mant.xml
@@ -1,21 +1,21 @@
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Spot.xml b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Spot.xml
index 9a53952f0..d16cca39b 100644
--- a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Spot.xml
+++ b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Spot.xml
@@ -1,10 +1,10 @@
-
-
-
+
+
+
-
-
+
+
diff --git a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Wipe3.xml b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Wipe3.xml
index 11f278866..b1022da79 100644
--- a/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Wipe3.xml
+++ b/soh/assets/xml/GC_NMQ_PAL_F/overlays/ovl_Oceff_Wipe3.xml
@@ -1,10 +1,10 @@
-
-
-
+
+
+
-
-
+
+
diff --git a/soh/assets/xml/N64_PAL_11/overlays/ovl_Bg_Ganon_Otyuka.xml b/soh/assets/xml/N64_PAL_11/overlays/ovl_Bg_Ganon_Otyuka.xml
index 5bba7f35b..99d32f6e6 100644
--- a/soh/assets/xml/N64_PAL_11/overlays/ovl_Bg_Ganon_Otyuka.xml
+++ b/soh/assets/xml/N64_PAL_11/overlays/ovl_Bg_Ganon_Otyuka.xml
@@ -1,8 +1,8 @@
-
-
+
+
-
+
diff --git a/soh/assets/xml/N64_PAL_11/overlays/ovl_File_Choose.xml b/soh/assets/xml/N64_PAL_11/overlays/ovl_File_Choose.xml
index 55c8779e4..de41882eb 100644
--- a/soh/assets/xml/N64_PAL_11/overlays/ovl_File_Choose.xml
+++ b/soh/assets/xml/N64_PAL_11/overlays/ovl_File_Choose.xml
@@ -3,18 +3,20 @@
-
+
+
-
+
+
+
-
+
@@ -24,5 +26,6 @@
+
diff --git a/soh/assets/xml/N64_PAL_11/overlays/ovl_Magic_Fire.xml b/soh/assets/xml/N64_PAL_11/overlays/ovl_Magic_Fire.xml
index b5b7ba82d..0164ab203 100644
--- a/soh/assets/xml/N64_PAL_11/overlays/ovl_Magic_Fire.xml
+++ b/soh/assets/xml/N64_PAL_11/overlays/ovl_Magic_Fire.xml
@@ -1,10 +1,10 @@
-
-
-
+
+
+
-
-
+
+
diff --git a/soh/macosx/soh-macos.sh b/soh/macosx/soh-macos.sh
index 41b406295..b77a5a9fa 100755
--- a/soh/macosx/soh-macos.sh
+++ b/soh/macosx/soh-macos.sh
@@ -44,10 +44,14 @@ if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then
ROM_TYPE=0;;
0227d7c0074f2d0ac935631990da8ec5914597b4)
ROM_TYPE=0;;
+ cfbb98d392e4a9d39da8285d10cbef3974c2f012)
+ ROM_TYPE=0;;
50bebedad9e0f10746a52b07239e47fa6c284d03)
ROM_TYPE=1;;
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
ROM_TYPE=1;;
+ cfecfdc58d650e71a200c81f033de4e6d617a9f6)
+ ROM_TYPE=1;;
517bd9714c73cb96c21e7c2ef640d7b55186102f)
ROM_TYPE=1;;
*)
@@ -130,6 +134,9 @@ if [ ! -e "$SHIP_HOME"/oot.otr ] || [ ! -e "$SHIP_HOME"/oot-mq.otr ]; then
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
+ cfecfdc58d650e71a200c81f033de4e6d617a9f6)
+ ROM=GC_MQ_D
+ OTRNAME="oot-mq.otr";;
517bd9714c73cb96c21e7c2ef640d7b55186102f)
ROM=GC_MQ_D
OTRNAME="oot-mq.otr";;
diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp
index fd24503bb..970685fb5 100644
--- a/soh/soh/Enhancements/audio/AudioEditor.cpp
+++ b/soh/soh/Enhancements/audio/AudioEditor.cpp
@@ -207,6 +207,10 @@ void Draw_SfxTab(const std::string& tabId, SeqType type) {
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
UpdateCurrentBGM(defaultValue, type);
}
+
+ if (currentValue == value) {
+ ImGui::SetItemDefaultFocus();
+ }
}
ImGui::EndCombo();
diff --git a/soh/soh/Enhancements/bootcommands.c b/soh/soh/Enhancements/bootcommands.c
index 05a682101..385a544a6 100644
--- a/soh/soh/Enhancements/bootcommands.c
+++ b/soh/soh/Enhancements/bootcommands.c
@@ -26,6 +26,8 @@ void BootCommands_Init()
CVarClear("gOnFileSelectNameEntry"); // Clear when soh is killed on the file name entry page
CVarClear("gBetterDebugWarpScreenMQMode");
CVarClear("gBetterDebugWarpScreenMQModeScene");
+ CVarClear("gCheatEasyPauseBufferLastInputs");
+ CVarClear("gCheatEasyPauseBufferTimer");
#if defined(__SWITCH__) || defined(__WIIU__)
CVarRegisterInteger("gControlNav", 1); // always enable controller nav on switch/wii u
#endif
diff --git a/soh/soh/Enhancements/debugger/colViewer.cpp b/soh/soh/Enhancements/debugger/colViewer.cpp
index 7aa7ab40a..e46628305 100644
--- a/soh/soh/Enhancements/debugger/colViewer.cpp
+++ b/soh/soh/Enhancements/debugger/colViewer.cpp
@@ -693,11 +693,23 @@ extern "C" void DrawColViewer() {
OPEN_DISPS(gPlayState->state.gfxCtx);
+ uint8_t mirroredWorld = CVarGetInteger("gMirroredWorld", 0);
+ // Col viewer needs inverted culling in mirror mode for both OPA and XLU buffers
+ if (mirroredWorld) {
+ gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING);
+ gSPSetExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING);
+ }
+
opaDl.push_back(gsSPEndDisplayList());
gSPDisplayList(POLY_OPA_DISP++, opaDl.data());
xluDl.push_back(gsSPEndDisplayList());
gSPDisplayList(POLY_XLU_DISP++, xluDl.data());
+ if (mirroredWorld) {
+ gSPClearExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING);
+ gSPClearExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING);
+ }
+
CLOSE_DISPS(gPlayState->state.gfxCtx);
}
diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp
index e50adcd52..8138339c8 100644
--- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp
@@ -548,13 +548,13 @@ static void CalculateWotH() {
static void FastFill(std::vector items, std::vector locations, bool endOnItemsEmpty = false) {
//Loop until locations are empty, or also end if items are empty and the parameters specify to end then
while (!locations.empty() && (!endOnItemsEmpty || !items.empty())) {
- uint32_t loc = RandomElement(locations, true);
- Location(loc)->SetAsHintable();
- PlaceItemInLocation(loc, RandomElement(items, true));
-
if (items.empty() && !endOnItemsEmpty) {
items.push_back(GetJunkItem());
}
+
+ uint32_t loc = RandomElement(locations, true);
+ Location(loc)->SetAsHintable();
+ PlaceItemInLocation(loc, RandomElement(items, true));
}
}
diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp
index 721776418..8b9f37f9e 100644
--- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp
@@ -423,6 +423,11 @@ void HintTable_Init_Exclude_Overworld() {
Text{"#Medigoron# sells", /*french*/"#Medigoron# vend", /*spanish*/"#Medigoron# vende"},
});
+ hintTable[KAK_GRANNYS_SHOP] = HintText::Exclude({
+ // obscure text
+ Text{"the #potion shop lady# sells", /*french*/"la #dame du magasin de potion# vend", /*spanish*/"la #señora de la tienda de pociones# vende" },
+ });
+
hintTable[KAK_IMPAS_HOUSE_FREESTANDING_POH] = HintText::Exclude({
//obscure text
Text{"#imprisoned in a house# lies", /*french*/"#encagé dans une maison# gît", /*spanish*/"#en una casa entre rejas# yace"},
@@ -483,7 +488,7 @@ void HintTable_Init_Exclude_Overworld() {
hintTable[GV_WATERFALL_FREESTANDING_POH] = HintText::Exclude({
//obscure text
- Text{"behind a #desert waterfall# is", /*french*/"#derrière la cascade du désert# se cache", /*spanish*/"tras una #desierta cascada# yace"},
+ Text{"behind a #valley waterfall# is", /*french*/"#derrière la cascade du désert# se cache", /*spanish*/"tras una #desierta cascada# yace"},
});
hintTable[GV_CRATE_FREESTANDING_POH] = HintText::Exclude({
diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp
index c074ffbf2..9b9913c8c 100644
--- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp
@@ -206,8 +206,8 @@ void HintTable_Init_Item() {
hintTable[STONE_OF_AGONY] = HintText::Item({
//obscure text
- Text{"the shake shard", /*french*/"le fragment vibrant", /*spanish*/"el fragmento tintineante"},
- Text{"a blue alarm", /*french*/"une alerte bleue", /*spanish*/"una azul alarma"},
+ Text{"the shake stone", /*french*/"le fragment vibrant", /*spanish*/"el fragmento tintineante"},
+ Text{"a gray alarm", /*french*/"une alerte bleue", /*spanish*/"una azul alarma"},
}, {
//ambiguous text
Text{"a prize of the House of Skulltulas", /*french*/"un prix de la maison des Skulltulas", /*spanish*/"un obsequio de la Casa Skulltula"},
diff --git a/soh/soh/Enhancements/randomizer/3drando/item_list.cpp b/soh/soh/Enhancements/randomizer/3drando/item_list.cpp
index 59d8518f3..dd7533b98 100644
--- a/soh/soh/Enhancements/randomizer/3drando/item_list.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/item_list.cpp
@@ -66,7 +66,7 @@ void ItemTable_Init() { // RandomizerGet
//Progression Items
itemTable[PROGRESSIVE_HOOKSHOT] = Item(RG_PROGRESSIVE_HOOKSHOT, Text{"Progressive Hookshot", "Grappin (prog.)", "Gancho progresivo"}, ITEMTYPE_ITEM, 0x80, true, &ProgressiveHookshot, PROGRESSIVE_HOOKSHOT);
- itemTable[PROGRESSIVE_STRENGTH] = Item(RG_PROGRESSIVE_STRENGTH, Text{"Progressive Strength Upgrade", "Amélioration de Force (prog.)", "Fuerza progresiva"}, ITEMTYPE_ITEM, 0x81, true, &ProgressiveStrength, PROGRESSIVE_STRENGTH);
+ itemTable[PROGRESSIVE_STRENGTH] = Item(RG_PROGRESSIVE_STRENGTH, Text{"Strength Upgrade", "Amélioration de Force (prog.)", "Fuerza progresiva"}, ITEMTYPE_ITEM, 0x81, true, &ProgressiveStrength, PROGRESSIVE_STRENGTH);
itemTable[PROGRESSIVE_BOMB_BAG] = Item(RG_PROGRESSIVE_BOMB_BAG, Text{"Progressive Bomb Bag", "Sac de Bombes (prog.)", "Saco de bombas progresivo"}, ITEMTYPE_ITEM, 0x82, true, &ProgressiveBombBag, PROGRESSIVE_BOMB_BAG);
itemTable[PROGRESSIVE_BOW] = Item(RG_PROGRESSIVE_BOW, Text{"Progressive Bow", "Arc (prog.)", "Arco progresivo"}, ITEMTYPE_ITEM, 0x83, true, &ProgressiveBow, PROGRESSIVE_BOW);
itemTable[PROGRESSIVE_SLINGSHOT] = Item(RG_PROGRESSIVE_SLINGSHOT, Text{"Progressive Slingshot", "Lance-Pierre (prog.)", "Resortera progresiva"}, ITEMTYPE_ITEM, 0x84, true, &ProgressiveBulletBag, PROGRESSIVE_SLINGSHOT);
@@ -207,8 +207,8 @@ void ItemTable_Init() { // RandomizerGet
itemTable[BLUE_POTION_REFILL] = Item(RG_BLUE_POTION_REFILL, Text{"Blue Potion Refill", "Recharge de Potion Bleue", "Recarga de poción azul"}, ITEMTYPE_REFILL, GI_POTION_BLUE, false, &noVariable, NONE);
//Treasure Game
- itemTable[TREASURE_GAME_HEART] = Item(RG_TREASURE_GAME_HEART, Text{"Piece of Heart (Treasure Chest Minigame)", "Quart de Coeur (Chasse-aux-Trésors)", "Pieza de corazón (Cofre del Tesoro)"}, ITEMTYPE_ITEM, GI_HEART_PIECE_WIN, true, &PieceOfHeart, TREASURE_GAME_HEART);
- itemTable[TREASURE_GAME_GREEN_RUPEE] = Item(RG_TREASURE_GAME_GREEN_RUPEE, Text{"Green Rupee (Treasure Chest Minigame)", "Rubis Vert (Chasse-aux-Trésors)", "Rupia Verde (Cofre del Tesoro)"}, ITEMTYPE_ITEM, GI_RUPEE_GREEN_LOSE, false, &noVariable, TREASURE_GAME_GREEN_RUPEE);
+ itemTable[TREASURE_GAME_HEART] = Item(RG_TREASURE_GAME_HEART, Text{"Piece of Heart (WINNER)", "Quart de Coeur (Chasse-aux-Trésors)", "Pieza de corazón (Cofre del Tesoro)"}, ITEMTYPE_ITEM, GI_HEART_PIECE_WIN, true, &PieceOfHeart, TREASURE_GAME_HEART);
+ itemTable[TREASURE_GAME_GREEN_RUPEE] = Item(RG_TREASURE_GAME_GREEN_RUPEE, Text{"Green Rupee (LOSER)", "Rubis Vert (Chasse-aux-Trésors)", "Rupia Verde (Cofre del Tesoro)"}, ITEMTYPE_ITEM, GI_RUPEE_GREEN_LOSE, false, &noVariable, TREASURE_GAME_GREEN_RUPEE);
//Shop Items price
itemTable[BUY_DEKU_NUT_5] = Item(RG_BUY_DEKU_NUT_5, Text{"Buy Deku Nut (5)", "Acheter: Noix Mojo (5)", "Comprar nueces deku (5)"}, ITEMTYPE_SHOP, 0x00, true, &Nuts, DEKU_NUTS_5, 15);
diff --git a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp
index bf0257064..7283635a3 100644
--- a/soh/soh/Enhancements/randomizer/3drando/item_location.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/item_location.cpp
@@ -120,7 +120,7 @@ void LocationTable_Init() {
locationTable[KAK_MAN_ON_ROOF] = ItemLocation::Base (RC_KAK_MAN_ON_ROOF, 0x52, 0x3E, "Kak Man on Roof", KAK_MAN_ON_ROOF, PIECE_OF_HEART, {Category::cKakarikoVillage, Category::cKakariko,}, SpoilerCollectionCheck::ItemGetInf(29), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_KAK_SHOOTING_GALLERY_REWARD, 0x42, 0x30, "Kak Shooting Gallery Reward", KAK_SHOOTING_GALLERY_REWARD, PROGRESSIVE_BOW, {Category::cKakarikoVillage, Category::cKakariko, Category::cMinigame}, SpoilerCollectionCheck::Chest(0x42, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_TRADE_ODD_MUSHROOM] = ItemLocation::Base (RC_KAK_TRADE_ODD_MUSHROOM, 0x4E, 0x20, "Kak Trade Odd Mushroom", KAK_TRADE_ODD_MUSHROOM, ODD_POTION, {Category::cKakarikoVillage, Category::cKakariko, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(56), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
- locationTable[KAK_GRANNYS_SHOP] = ItemLocation::Base (RC_KAK_GRANNYS_SHOP, 0x4E, 0x10, "Kak Granny's Shop", KAK_GRANNYS_SHOP, BLUE_POTION_REFILL, {Category::cKakarikoVillage, Category::cKakariko, Category::cMerchant}, SpoilerCollectionCheck::EventChkInf(0x32), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
+ locationTable[KAK_GRANNYS_SHOP] = ItemLocation::Base (RC_KAK_GRANNYS_SHOP, 0x4E, 0x10, "Kak Granny's Shop", KAK_GRANNYS_SHOP, BLUE_POTION_REFILL, {Category::cKakarikoVillage, Category::cKakariko, Category::cMerchant}, SpoilerCollectionCheck::RandomizerInf(0x4E, 0x88), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, 0x1D, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, CLAIM_CHECK, {Category::cKakarikoVillage, Category::cKakariko,}, SpoilerCollectionCheck::ItemGetInf(36), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_ANJU_AS_CHILD] = ItemLocation::Base (RC_KAK_ANJU_AS_CHILD, 0x52, 0x0F, "Kak Anju as Child", KAK_ANJU_AS_CHILD, EMPTY_BOTTLE, {Category::cKakarikoVillage, Category::cKakariko, Category::cMinigame}, SpoilerCollectionCheck::ItemGetInf(4), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
locationTable[KAK_TRADE_POCKET_CUCCO] = ItemLocation::Base (RC_KAK_TRADE_POCKET_CUCCO, 0x52, 0x0E, "Kak Trade Pocket Cucco", KAK_TRADE_POCKET_CUCCO, COJIRO, {Category::cKakarikoVillage, Category::cKakariko, Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(38), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp
index 4049ee628..aa2e64a78 100644
--- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp
@@ -14,7 +14,7 @@ using namespace Settings;
std::vector NonShopItems = {};
-static std::array, 0xD5> trickNameTable; //Table of trick names for ice traps
+static std::array, 0xD5> trickNameTable; //Table of trick names for ice traps
bool initTrickNames = false; //Indicates if trick ice trap names have been initialized yet
//Set vanilla shop item locations before potentially shuffling
@@ -225,18 +225,19 @@ void InitTrickNames() {
trickNameTable[GI_SWORD_KOKIRI] = {
Text{"Korok Sword", "Épée Korok", "Espada Korok"},
Text{"Hero's Sword", "Épée du Héros", "Espada del héroe"},
- Text{"Razor Sword", "Lame Rasoir", "Espada de esmeril"}};
+ Text{"Butter Knife","Couteau à Beurre","cuchillo de mantequilla"}};
/* trickNameTable[GI_SWORD_MASTER] = {
Text{"Goddess Sword", "Épée de la déesse", "Espada Divina"},
Text{"Gilded Sword", "Excalibur", "Espada de los Sabios"},
Text{"Magical Sword", "Lame dorée", "Fay"}};*/
trickNameTable[GI_SWORD_KNIFE] = {
- Text{"Big Goron's Sword", "Épée de Gros Goron", "Espada de Big Goron"},
- Text{"Fierce Deity's Sword", "Épée du Dieu Démon", "Espada de la Fiera Deidad"},
- Text{"Biggoron's Knife", "Lame de Grogoron", "Daga de Biggoron"}};
+ Text{"Medigoron's Sword", "l'Épée de Medigoron", "La espada de Medigoron"},
+ Text{"Razor Sword", "Lame Rasoir", "Espada de esmeril"},
+ Text{"Royal Claymore", "Claymore Royale", "Royal Claymore"}};
trickNameTable[GI_SWORD_BGS] = {
- Text{"Big Goron's Sword", "Épée de Biggoron", "Espada de Big Goron"},
- Text{"Fierce Deity's Sword", "Épée du dieu démon", "Espada de la Fiera Deidad"},
+ Text{"Power Sword", "Épée de Puissance", "Espada de poder"},
+ Text{"Fierce Deity Sword", "Épée du dieu démon", "Espada de la Fiera Deidad"},
+ Text{"Tempered Sword", "Épée de Légende Nv.2", "Espada Maestra mejorada"},
Text{"Biggoron's Knife", "Lame de Grogoron", "Daga de Biggoron"}};
trickNameTable[GI_SHIELD_DEKU] = {
Text{"Boko Shield", "Bouclier Boko", "Escudo Boko"},
@@ -251,19 +252,23 @@ void InitTrickNames() {
Text{"Magical Shield", "Bouclier Magique", "Escudo arcano"},
Text{"Mirror of Twilight", "Miroir des Ombres", "Espejo del Crepúsculo"}};
trickNameTable[GI_TUNIC_GORON] = {
- Text{"Gerudo Tunic", "Tunique Gerudo", "Sayo gerudo"},
- Text{"Magic Armor", "Armure Magique", "Túnica Goron"},
+ Text{"Gerudo Top", "Tunique Gerudo", "Pechera gerudo"},
+ Text{"Flamebreaker Armor", "Armure de Pierre", " Armadura ignífuga"},
Text{"Red Mail", "Habits Rouges", "Ropas rojas"}};
trickNameTable[GI_TUNIC_ZORA] = {
Text{"Rito Tunic", "Tunique Rito", "Sayo rito"},
+ Text{"Mermaid Suit", "Costume de sirène", "Costume de sirène"},
Text{"Zora Armor", "Armure Zora", "Túnica Zora"},
Text{"Blue Mail", "Habits Bleus", "Ropas azules"}};
trickNameTable[GI_BOOTS_IRON] = {
Text{"Iron Hoofs", "Patins de Plomb", "Botas férreas"},
Text{"Snow Boots", "Bottes de Neige", "Botas de nieve"},
+ Text{"Red Boots", "Bottes rouges", "Botas rojas"},
+ Text{"Zora Greaves", "Bottes Zora", "Zora Greaves"},
Text{"Boots of Power", "Bottes de Puissance", "Botas de plomo"}};
trickNameTable[GI_BOOTS_HOVER] = {
Text{"Hover Hoofs", "Patins des airs", "Botas flotadoras"},
+ Text{"Golden Boots", "Bottes dorées", "Botas de Oro"},
Text{"Pegasus Boots", "Bottes pégase", "Botas de Pegaso"},
Text{"Boots of Speed", "Bottes de vitesse", "Botas del desierto"}};
trickNameTable[GI_WEIRD_EGG] = {
@@ -271,65 +276,81 @@ void InitTrickNames() {
Text{"Lon Lon Egg", "Oeuf Lon Lon", "Huevo Lon Lon"},
Text{"Zora Egg", "Oeuf Zora", "Huevo Zora"}};
trickNameTable[GI_LETTER_ZELDA] = {
- Text{"Ruto's Letter", "Lettre de Ruto", "Carta de Ruto"},
+ Text{"Ruto's Letter", "Lettre de Ruto", "Carta de Ruto"},
Text{"Royal Letter", "Lettre Eoyale", "Carta para Kafei"},
- Text{"Zelda's Business Card", "Carte d'affaires de Zelda", "Carta"}};
+ Text{"Zelda's Business Card", "Carte d'affaires de Zelda", "Carta"},
+ Text{"Letter to Kafei", "Lettre pour Kafei", "Carta para Kafei "},
+ Text{"Goat's Letter", "Lettre de la Chèvre", "Carta de la Cabra"},
+ Text{"Maggie's Letter", "Lettre de Maggy", "Carta de Dolores"}};
trickNameTable[GI_BOOMERANG] = {
+ Text{"Banana", "Banane", "Plátano"},
Text{"Prank Fetch Toy", "Inséparable Bâtonnet", "Bumerang"},
Text{"Gale Boomerang", "Boomerang Tornade", "Bumerán tornado"},
Text{"Magic Boomerang", "Boomerang Magique", "Bumerán mágico"}};
trickNameTable[GI_LENS] = {
Text{"Sheikah-leidoscope", "Sheikah-léidoscope", "Monóculo de la Verdad"},
Text{"Sheikah Sensor", "Sonar Sheikah", "Sensor Sheikah"},
+ Text{"Crystal of Vision", "Cristal de Vision", "Cristal de Visión"},
Text{"Magnifying Lens", "Loupe", "Lente Aumentadora"}};
trickNameTable[GI_HAMMER] = {
Text{"Goron Gavel", "Masse Perforatrice", "Mazo Goron"},
Text{"Magic Hammer", "Marteau Magique", "Martillo mágico"},
Text{"Skull Hammer", "Maillet Ressort", "Martillo de hierro"}};
trickNameTable[GI_STONE_OF_AGONY] = {
- Text{"Shard of Agahnim", "Fragment d'Agahnim", "Piedra de Agahnim"},
+ Text{"Cave Charm", "Charme de grotte", "Amuleto de la cueva"},
+ Text{"Stone of Agahnim", "Fragment d'Agahnim", "Piedra de Agahnim"},
Text{"Shard of Agony", "Fragment de Souffrance", "Piedra de la Agonía"},
Text{"Pirate's Charm", "Pierre de Pirate", "Amuleto Pirata"}};
trickNameTable[GI_DINS_FIRE] = {
Text{"Eldin's Fire", "Feu d'Eldin", "Fuego de Eldin"},
Text{"Din's Blaze", "Flamme de Din", "Poder de Din"},
- Text{"Din's Pearl", "Perle de Din", "Orbe de Din"}};
+ Text{"Magic Lantern", "Lanterne Magique", "Linterna mágica"},
+ Text{"Ether Medallion", "Médaillon d'Éther", "Medallón de Tesoro"},
+ Text{"Bombos Medallion", "Médaillon des Flammes", "Medallón del Temblor"}};
trickNameTable[GI_FARORES_WIND] = {
Text{"Faron's Wind", "Vent de Firone", "Viento de Farone"},
Text{"Farore's Windfall", "Zéphyr de Farore", "Valor de Farore"},
- Text{"Farore's Pearl", "Perle de Farore", "Orbe de Farore"}};
+ Text{"Tingle Air", "Tingle Air", "Tingle de aire"},
+ Text{"Travel Medallion", "Amulette de téléportation", "Medallón Maligno"},
+ Text{"Irene's Taxi", "Le taxi d'Aëline", "El taxi de Airín"}};
trickNameTable[GI_NAYRUS_LOVE] = {
Text{"Lanayru's Love", "Amour de Lanelle", "Amor de Lanayru"},
Text{"Nayru's Passion", "Passion de Nayru", "Sabiduría de Nayru"},
- Text{"Nayru's Pearl", "Perle de Nayru", "Orbe de Nayru"}};
+ Text{"Tingle Shield", "Bouclier Tingle", "Escudo de hormigueo"},
+ Text{"Shield Spell", "Bouclier Magique", "Hechizo de Protección"},
+ Text{"Magic Armor", "Armure Magique", "Armadura mágica"}};
trickNameTable[GI_ARROW_FIRE] = {
- Text{"Soul Arrow", "Flèche des Esprits", "Flecha del Espíritu"},
+ Text{"Fire Rod", "Baguette de feu", "Cetro de fuego"},
Text{"Bomb Arrow", "Flèche-Bombe", "Flecha bomba"},
- Text{"Fire Candy", "Bonbon deFfeu", "Cetro de fuego"}};
+ Text{"Red Candle", "Bougie Rouge", "Vela roja"}};
trickNameTable[GI_ARROW_ICE] = {
- Text{"Shadow Arrow", "Flèche d'Ombre", "Flecha de las Sombras"},
+ Text{"Ice Rod", "Baguette des Glaces", "Cetro de Hielo"},
Text{"Ancient Arrow", "Flèche Archéonique", "Flecha ancestral"},
Text{"Ice Trap Arrow", "Flèche de Piège de Glace", "Cetro de hielo"}};
trickNameTable[GI_ARROW_LIGHT] = {
Text{"Wind Arrow", "Flèche de Vent", "Flecha del Viento"},
+ Text{"Wand of Gamelon", "Baguette de Gamelon", "Varita de Gamelón"},
Text{"Shock Arrow", "Flèches Électriques", "Flecha eléctrica"},
Text{"Silver Arrow", "Flèches d'Argent", "Flecha de plata"}};
trickNameTable[GI_GERUDO_CARD] = {
Text{"Desert Title Deed", "Abonnement Gerudo", "Escritura del desierto"},
+ Text{"Sickle Moon Flag", "Drapeau du croissant de lune", "Bandera de la Luna Creciente"},
+ Text{"Complimentary ID", "Bon de félicitation", "Cupón especial"},
Text{"Gerudo's Card", "Carte Goron", "Tóken Gerudo"},
Text{"Gerudo's Membership Card", "Autographe de Nabooru", "Tarjeta Gerudo"}};
trickNameTable[0xC9] = {
Text{"Funky Bean Pack", "Paquet de Fèves Magiques", "Lote de frijoles mágicos"},
+ Text{"Grapple Berries", "Baies de grappin", "Bayas de garfio"},
Text{"Crenel Bean Pack", "Paquet de Haricots Gonggle", "Lote de alubias mágicas"},
- Text{"Mystic Bean Pack", "Paquet de Haricots Mystiques", "Lote de porotos mágicos"}};
+ Text{"Mystical Seed Pack", "Pack de graines mystiques", "Paquete de semillas místicas"}};
trickNameTable[0xB8] = {
Text{"Diamond Hearts", "Coeurs de Diamant", "Contenedor de diamante"},
Text{"Double Damage", "Double Souffrance", "Doble daño receptivo"},
Text{"Quadruple Defence", "Quadruple Défence", "Defensa cuádruple"}};
trickNameTable[GI_POCKET_EGG] = {
- Text{"Poached Egg", "oeuf à la coque", "Huevo pasado"},
+ Text{"Arpagos Egg", "Oeuf d'Arpagos", "Huevo de Arpagos"},
Text{"Lon Lon Egg", "oeuf Lon Lon", "Huevo Lon Lon"},
Text{"Zora Egg", "oeuf Zora", "Huevo del Pez Viento"}};
trickNameTable[GI_POCKET_CUCCO] = {
@@ -338,113 +359,161 @@ void InitTrickNames() {
Text{"Hatched Cucco", "Cocotte éclose", "Pollo de bolsillo"}};
trickNameTable[GI_COJIRO] = {
Text{"Blucco", "Chair-Qui-Poule", "Cucazul"},
- Text{"Grog's Cucco", "Cocotte de Grog", "Cuco de Grog"},
- Text{"Corijo", "Cojiro", "Corijo"}};
+ Text{"Piyoko", "Piyoko", "Piyoko"},
+ Text{"Dark Cucco", "Cocotte Sombre", "Cucco oscuro"},
+ Text{"Grog's Cucco", "Cocotte de Grog", "Cuco de Grog"}};
trickNameTable[GI_ODD_MUSHROOM] = {
Text{"Magic Mushroom", "Champignon magique", "Champiñón mágico"},
Text{"Endura Shroom", "Champi Vigueur", "Champiñón del bosque"},
+ Text{"Sleepy Toadstool", "Crapaud Fatigué", "Seta durmiente"},
Text{"Mushroom", "Champignon", "Seta"}};
trickNameTable[GI_ODD_POTION] = {
Text{"Odd Medicine", "Élixir suspect", "Poción rara"},
Text{"Granny's Poultice", "Mixture de Granny", "Medicina de la abuela"},
- Text{"Mushroom Poultice", "Mixture de champignon", "Medicina de champiñones"}};
+ Text{"Mushroom Poultice", "Mixture de champignon", "Medicina de champiñones"},
+ Text{"Secret Medicine", "Médicament", "Pócima secreta"},
+ Text{"Mushroom Spores", "Spores de Champignons", "Esporas de hongos"},
+ Text{"Hanyu Spore", "Hanyu Spore", "Espora Hanyu"}};
trickNameTable[GI_SAW] = {
Text{"Carpenter's Saw", "Scie du charpentier", "Sierra del carpintero"},
Text{"Poacher's Sword", "Hache du chasseur", "Espada del capataz"},
+ Text{"Ancient Bladesaw", "Longue Épée Archéonique", "Mandoble ancestral"},
+ Text{"Woodcutter's Axe", "Hache du Bûcheron", "Hacha de leñador"},
Text{"Grog's Saw", "Scie de Grog", "Sierra del Cazador Furtivo"}};
trickNameTable[GI_SWORD_BROKEN] = {
Text{"Broken Biggoron's Sword", "Épée brisée de Grogoron", "Espada de Biggoron rota"},
Text{"Broken Giant's Knife", "Lame des Géants brisée", "Daga gigante rota"},
- Text{"Biggoron's Sword", "Épée de Biggoron", "Espada de Biggoron"}};
+ Text{"Broken Noble Sword", "Épée noble brisée", "Espada noble rota"},
+ Text{"Broken Picori Blade", "Épée Minish brisée", "Espada minish rota"},
+ Text{"Decayed Master Sword", "Épée de légende pourrie", "Espada decadente de leyenda"}};
trickNameTable[GI_PRESCRIPTION] = {
Text{"Biggoron's Prescription", "Ordonnance de Grogoron", "Receta de Biggoron"},
Text{"Eyedrop Prescription", "Ordonnance de gouttes", "Receta ocular"},
- Text{"Urgent Prescription", "Ordonnance urgente", "Prescripción"}};
+ Text{"Urgent Prescription", "Ordonnance urgente", "Prescripción"},
+ Text{"Swordsman's Scroll", "Précis d'escrime", "Esgrimidorium"},
+ Text{"Portrait of Oren", "Portrait d'Orlène", "Retrato de Oren"},
+ Text{"Letter to King Zora", "Lettre au roi Zora", "Carta al Rey Zora"}};
trickNameTable[GI_FROG] = {
Text{"Don Gero", "Don Gero", "Don Gero"},
- Text{"Eyedrop Frog", "Grenouille-qui-louche", "Globo Ocular de Rana"},
- Text{"Frog", "Crapaud", "Rana"}};
+ Text{"Hot-Footed Frog", "Grenouille à pieds chauds", "Rana de patas calientes"},
+ Text{"Lost Swordsmith", "Forgeron perdu", "Espadachín perdido"},
+ Text{"Eyedrop Frog", "Grenouille-qui-louche", "Globo Ocular de Rana"}};
trickNameTable[GI_EYEDROPS] = {
Text{"Biggoron's Eyedrops", "Gouttes de Grogoron", "Gotas de Biggoron"},
Text{"Hyrule's Finest Eyedrops", "Eau du Lac Hylia", "Gotas oculares"},
+ Text{"Moon's Tear", "Larme de Lune", "Lágrima de Luna"},
+ Text{"Engine Grease", "Graisse moteur", "Grasa del motor"},
Text{"Zora Perfume", "Parfum Zora", "Perfume Zora"}};
trickNameTable[GI_CLAIM_CHECK] = {
Text{"Clay Check", "Certificat Grogoron", "Comprobante de Reclamación"},
+ Text{"Ancient Tablet", "Stèle ancienne", "Litografía arcana"},
Text{"Sheikah Slate", "Tablette Sheikah", "Piedra Sheikah"},
Text{"Cyclone Slate", "Ardoise des tornades", "Pizarra de los Torbellinos"}};
trickNameTable[GI_SKULL_TOKEN] = {
Text{"Skulltula Token", "Bon de Skulltula dorée", "Símbolo de Skulltula"},
Text{"Golden Skulltula Spirit", "Pièce de Skulltula dorée", "Tóken de Skulltula Dorada"},
- Text{"Gold Walltula Token", "Jeton de Walltula dorée", "Skulltula dorada"}};
+ Text{"Gold Walltula Token", "Jeton de Walltula dorée", "Skulltula dorada"},
+ Text{"Maiamai", "Ti'gorneau", "Maimai"},
+ Text{"Gratitude Crystal", "Cristal de gratitude", "Gema de gratitud"},
+ Text{"Korok Seed", "Noix korogu", "Semilla de kolog"}};
trickNameTable[0x80] = {
Text{"Progressive Grappling Hook", "Lance-chaîne (prog.)", "Garra progresiva"},
Text{"Progressive Clawshot", "Grappin-griffe (prog.)", "Zarpa progresiva"},
- Text{"Progressive Gripshot", "Grappince (prog.)", "Enganchador progresivo"}};
+ Text{"Progressive Gripshot", "Grappince (prog.)", "Enganchador progresivo"},
+ Text{"Progressive Rope", "Corde (prog.)", "Cuerda progresivo"}};
trickNameTable[0x81] = {
- Text{"Progressive Glove", "Gant de puissance (prog.)", "Guanteletes progresivos"},
- Text{"Progressive Power Bracelet", "Bracelet de force (prog.)", "Brasaletes progresivos"},
- Text{"Progressive Magic Bracelet", "Bracelet magique (prog.)", "Manoplas progresivas"}};
+ Text{"Power Glove", "Gant de Puissance (prog.)", "Guanteletes progresivos"},
+ Text{"Power Bracelet", "Bracelet de Force (prog.)", "Brasaletes progresivos"},
+ Text{"Magic Bracelet", "Bracelet Magique (prog.)", "Manoplas progresivas"}};
trickNameTable[0x82] = {
Text{"Progressive Bomb Capacity", "Capacité de bombes (prog.)", "Mayor capacidad de bombas"},
Text{"Progressive Bomb Pack", "Paquet de bombes (prog.)", "Zurrón de bombas progresivo"},
- Text{"Progressive Bomb Box", "Boîte à bombes (prog.)", "Bolsa de bombas progresiva"}};
+ Text{"Progressive Bomb Box", "Boîte à bombes (prog.)", "Bolsa de bombas progresiva"},
+ Text{"Progressive Blast Mask", "Masque d'Explosion (prog.)", "Máscara explosiva progresiva"},
+ Text{"Progressive Powder Kegs", "Baril de Poudre (prog.)", "Barril de polvo progresivo"},
+ Text{"Progressive Remote Bombs", "Bombes à distance (prog.)", "Bombas remotas progresivas"}};
trickNameTable[0x83] = {
Text{"Progressive Arrow Capacity", "Capacité de flèches (prog.)", "Mayor capacidad de flechas"},
Text{"Progressive Hero's Bow", "Arc du héros (prog.)", "Arco del héroe progresivo"},
- Text{"Progressive Arrow Holder", "Arbalète (prog.)", "Ballesta progresiva"}};
+ Text{"Progressive Arrow Holder", "Arbalète (prog.)", "Ballesta progresiva"},
+ Text{"Progressive Crossbow", "Arbalète (prog.)", "Ballesta progresiva"},
+ Text{"Progressive Sacred Bow", "Arc sacré (prog)", "Arco Sagrado Progresivo"},
+ Text{"Progressive Lynel Bow", "Arc de Lynel (prog.)", "Arco de centaleón Progresivo"}};
trickNameTable[0x84] = {
Text{"Progressive Seed Capacity", "Capacité de graines (prog.)", "Mayor capacidad de semillas"},
+ Text{"Progressive Catapult", "Catapulte (prog.)", "Catapulta progresiva"},
Text{"Progressive Scattershot", "Lance-Pierre rafale (prog.)", "Resortera múltiple progresiva"},
+ Text{"Progressive Seed Launcher", "Lanceur de semences (prog.)", "Lanzador de semillas progresivo"},
Text{"Progressive Seed Satchel", "Sac de graines (prog.)", "Bolsa de semillas progresiva"}};
trickNameTable[0x85] = {
Text{"Progressive Rupee Capacity", "Capacité de rubis (prog.)", "Mayor capacidad de rupias"},
Text{"Progressive Purse", "Sacoche (prog.)", "Cartera de rupias progresiva"},
- Text{"Progressive Rupee Bag", "Sac à rubis (prog.)", "Zurrón de rupias progresivo"}};
+ Text{"Progressive Rupee Bag", "Sac à rubis (prog.)", "Zurrón de rupias progresivo"},
+ Text{"Progressive Rupoor Capacity", "Capacité de Roupir (prog.)", "Capacidad progresiva Rupobre"},
+ Text{"Progressive Spoils Bag", "Sac à Butin (prog.)", "Bolsa de trofeos progresiva"},
+ Text{"Progressive Ruby Bag", "Capacité du sac Ruby (prog.)", "Bolso Ruby progresivo"}};
trickNameTable[0x86] = {
+ Text{"Progressive Flippers", "Palmes de Zora (prog.)", "Aletas de zora progresiva"},
+ Text{"Progressive Dragon's Scale", "Écaille du dragon d'eau (prog.)", "Escama dragón acuático progresiva"},
Text{"Progressive Diving Ability", "Plongée (prog.)", "Buceo progresivo"},
Text{"Progressive Pearl", "Perle (prog.)", "Perla progresiva"},
Text{"Progressive Scute", "Bulle (prog.)", "Fragmento Zora progresivo"}};
trickNameTable[0x87] = {
Text{"Progressive Nut Pack", "Paquet de noix (prog.)", "Mayor capacidad de semillas"},
+ Text{"Progressive Bait Bag", "Sac à Appâts (prog.)", "Bolsa de cebo progresiva"},
+ Text{"Progressive Pear Capacity", "Capacité de poire (prog.)", "Capacidad progresiva de pera"},
Text{"Progressive Nut Bag", "Sac de noix (prog.)", "Bolsa de nueces progresiva"},
Text{"Progressive Husk Capacity", "Capacité de noisettes (prog.)", "Mayor capacidad de castañas"}};
trickNameTable[0x88] = {
- Text{"Progressive Stick Pack", "Paquet de bâtons Mojo (prog.)", "Mayor capacidad de bastones"},
Text{"Progressive Stick Bag", "Sac de bâtons (prog.)", "Mayor capacidad de ramas deku"},
+ Text{"Progressive Stick Pack", "Paquet de bâtons Mojo (prog.)", "Mayor capacidad de bastones"},
+ Text{"Progressive Branch Capacity", "Capacité de la succursale (prog.)", "Capacidad progresiva de la sucursal"},
Text{"Progressive Rod Capacity", "Capacité de tiges (prog.)", "Mayor capacidad de cetros deku"}};
trickNameTable[0x89] = {
Text{"Progressive Bomblings", "Bombinsectes (prog.)", "Bombinsectos progresivos"},
+ Text{"Progressive Sentrobe Bombs", "Bombe de Sphérodrone (prog.)", "Bomba de helicobot progresivo"},
+ Text{"Progressive Bomb-ombs", "Bombe Soldat (prog.)", "Soldado bomba progresivo"},
Text{"Progressive Missiles", "Missiles (prog.)", "Misiles progresivos"},
Text{"Progressive Bombchu Bag", "Sac à Bombchu (prog.)", "Bombachus progresivos"}};
trickNameTable[0x8A] = {
Text{"Progressive Stamina Meter", "Jauge d'endurance (prog.)", "Medidor de vigor progresivo"},
- Text{"Progressive Energy Meter", "Jauge d'énergie (prog.)", "Medidor de energía progresivo"},
+ Text{"Progressive Energy Gauge", "Jauge d'énergie (prog.)", "Medidor de energía progresivo"},
Text{"Progressive Magic Powder", "Poudre magique (prog.)", "Medidor de carga progresivo"}};
trickNameTable[0x8B] = {
Text{"Progressive Memento", "Souvenir (prog.)", "Silbato progresivo"},
+ Text{"Progressive Whistle", "Siffler (prog.)", "Silbido progresivo"},
Text{"Progressive Flute", "Flûte (prog.)", "Flauta progresiva"},
Text{"Progressive Recorder", "Harmonica (prog.)", "Armónica progresiva"}};
trickNameTable[0xD4] = {
Text{"Progressive Titan Blade", "Lame des Titans (prog.)", "Hoja del Titán progresiva"},
Text{"Progressive Goron Knife", "Lame Goron (prog.)", "Daga Goron progresiva"},
- Text{"Progressive Giant Sword", "Épée géante (prog.)", "Espada gigante progresiva"}};
+ Text{"Progressive Giant Sword", "Épée géante (prog.)", "Espada gigante progresiva"},
+ Text{"Progressive Darknut Sword", "Épée de Darknut (prog.)", "Espada Darknut progresiva"},
+ Text{"Progressive Power Sword", "Épée de Puissance (prog.)", "Espada de poder progresiva"},
+ Text{"Progressive Big Stabby", "Gros coup de poignard (prog.)", "Gran puñalada progresiva"}};
trickNameTable[0x0F] = {
+ Text{"Empty Canteen", "Cantine vide", "cantimplora vacía"},
+ Text{"Vial of Winds", "Fiole de vents", "Vial de Vientos"},
+ Text{"Tingle Bottle", "Flacon de Tingle", "Botella de Tingle"},
Text{"Magic Bottle", "Flacon magique", "Frasco feérico"},
Text{"Glass Bottle", "Flacon de verre", "Botella de cristal"},
Text{"Bottle with Water", "Flacon d'eau", "Botella Tingle"}};
trickNameTable[0x14] = {
Text{"Bottle with Chateau Romani", "Flacon de cuvée Romani", "Botella de Reserva Romani"},
- Text{"Bottle with Fresh Milk", "Flacon de lait frais", "Botella de leche fresca"},
- Text{"Bottle with Mystery Milk", "Flacon de lait grand cru", "Botella de leche extra"}};
+ Text{"Bottle with Premium Milk", "Flacon avec lait de qualité supérieure", "Biberón con leche Premium"},
+ Text{"Bottle with Mystery Milk", "Flacon de lait grand cru", "Botella de leche extra"},
+ Text{"Bottle with Fresh Milk", "Flacon de lait frais", "Botella de leche fresca"},};
trickNameTable[0x8C] = {
Text{"Bottle with Red Chu Jelly", "Flacon de gelée Chuchu rouge", "Jugo de Chuchu Rojo"},
+ Text{"Bottle with Hibiscus Potion", "Flacon de potion de Hibiscus", "Botella de poción de Hibisco"},
Text{"Bottle with Medicine of Life", "Flacon d'élixir rouge", "Botella de medicina de la vida"},
Text{"Bottle with Heart Potion", "Flacon de potion de soin", "Botella de poción de salud"}};
trickNameTable[0x8D] = {
Text{"Bottle with Green Chu Jelly", "Flacon de gelée Chuchu verte", "Jugo de Chuchu Verde"},
+ Text{"Bottle with Lamp Oil", "Flacon de Huile à lanterne", "Botella de Aceite de candil "},
Text{"Bottle with Medicine of Magic", "Flacon d'élixir vert", "Botella de medicina mágica"},
Text{"Bottle with Stamina Potion", "Flacon d'Endurol", "Botella de elixir vigorizante"}};
trickNameTable[0x8E] = {
@@ -453,22 +522,28 @@ void InitTrickNames() {
Text{"Bottle with Air Potion", "Flacon de potion d'oxygène", "Botella de oxígeno"}};
trickNameTable[0x8F] = {
Text{"Bottle with Forest Firefly", "Flacon avec une luciole", "Luciérnaga del bosque"},
- Text{"Bottle with Faerie", "Flacon de poudre féérique", "Gran Hada embotellada"},
+ Text{"Bottle with Deku Princess", "Flacon avec Deku Princess", "Botella con Deku Princess"},
Text{"Bottle with Stray Fairy", "Flacon avec une fée perdue", "Hada perdida en una botella"}};
trickNameTable[0x90] = {
Text{"Bottle with Small Jabu-Jabu", "Flacon avec mini Jabu-Jabu", "Lord Chapu-Chapu embotellado"},
+ Text{"Bottle with Reekfish", "Flacon avec Reekfish", "Reekfish embotellada"},
Text{"Bottle with Hyrule Bass", "Flacon avec perche d'Hyrule", "Locha de Hyrule embotellada"},
Text{"Bottle with Hyrule Loach", "Flacon avec loche d'Hyrule", "Perca de Términa embotellada"}};
trickNameTable[0x91] = {
Text{"Bottle with Will-O-Wisp", "Flacon avec feu follet", "Botella de llama azul"},
Text{"Bottle with Ancient Flame", "Flacon de flamme ancienne", "Botella de fuego ancestral"},
+ Text{"Bottle with a Blue Candle", "Flacon avec une bougie bleue", "Botella con una vela azul"},
+ Text{"Bottle with Red Ice", "Flacon de Glace Rouge", "Botella de Hielo rojo"},
Text{"Bottle with Nayru's Flame", "Flacon de flamme de Nayru", "Botella de llamas de Nayru"}};
trickNameTable[0x92] = {
Text{"Bottle with Baby Tektites", "Flacon de bébé Araknon", "Tektites en una botella"},
+ Text{"Bottle with A Beetle", "Flacon avec un scarabée", "Botella con un escarabajo"},
Text{"Bottle with Lanayru Ants", "Flacon de fourmis de Lanelle", "Celestarabajo embotellado"},
- Text{"Bottle with Insects", "Flacon de bibittes", "Saltabosques embotellados"}};
+ Text{"Bottle with Insects", "Flacon de bibittes", "Saltabosques embotellados"},
+ Text{"Bottle with a Golden Bee", "Flacon avec une abeille dorée", "Botella con una abeja dorada"}};
trickNameTable[0x94] = {
Text{"Bottle with Ghini", "Flacon avec Ghini", "Ghini en una botella"},
+ Text{"Bottle with Reapling", "Flacon avec Âme Damnée", "Reapling en una botella"},
Text{"Bottle with Imp Poe", "Flacon avec Spectre", "Espectro en una botella"},
Text{"Bottle with Anti-Fairy", "Flacon avec Tetdoss", "Whisp en una botella"}};
@@ -484,121 +559,146 @@ void InitTrickNames() {
trickNameTable[0xC1] = {
Text{"Ballad of the Goddess", "Chant de la déesse", "Cántico de la Diosa"},
Text{"Song of Healing", "Chant de l'apaisement", "Canción de curación"},
- Text{"Bolero of Fire", "Boléro du feu", "Bolero del fuego"}};
+ Text{"Song of the Hero", "Chant du héros", "Canción del héroe"}};
trickNameTable[0xC2] = {
- Text{"Earth God's Lyric", "Hymne du dieu de la terre", "Melodía del Espíritu de la Tierra"},
+ Text{"Song of Birds","Chant des oiseaux","Cantar del ave"},
Text{"Song of Soaring", "Chant de l'envol", "Canción del viento"},
- Text{"Requiem of Spirit", "Requiem des esprits", "Réquiem del espíritu"}};
+ Text{"Song of Horse", "Chant du cheval", "Chant du cheval"}};
trickNameTable[0xC3] = {
- Text{"Wind God's Aria", "Hymne du dieu du vent", "Melodía del Espíritu del Viento"},
- Text{"Wind's Requiem", "Mélodie du vent", "Melodía del Viento"},
- Text{"Minuet of Forest", "Menuet de la forêt", "Minueto del bosque"}};
+ Text{"Mido's Song", "La chanson de Mido", "La canción de Mido"},
+ Text{"Kass' Theme", "Le thème de Kass", "El tema de Kass"},
+ Text{"Tune of Echoes", "Chant des Échos ", "Melodía del Eco "}};
trickNameTable[0xC4] = {
Text{"Song of Passing", "Mambo de Manbo", "Melodía del transcurrir"},
Text{"Command Melody", "Air du marionnettiste", "Cara al Sol"},
- Text{"Prelude of Light", "Prélude de la lumière", "Preludio de la luz"}};
+ Text{"Moon's Song", "La chanson de Moon", "La canción de la luna"}};
trickNameTable[0xC5] = {
Text{"Song of Double Time", "Chant accéléré", "Canción del doble tiempo"},
Text{"Inverted Song of Time", "Chant du temps inversé", "Canción del tiempo invertida"},
- Text{"Serenade of Water", "Sérénade de l'eau", "Serenata del agua"}};
+ Text{"Tune of Ages", "Chant du Temps", "Melodía del Tiempo"}};
trickNameTable[0xC6] = {
Text{"Ballad of Gales", "Requiem de la tornade", "Melodía del Tornado"},
Text{"Frog's Song of Soul", "Rap des grenouilles", "Canción del alma de la rana"},
- Text{"Nocturne of Shadow", "Nocturne de l'ombre", "Nocturno de la sombra"}};
+ Text{"Wind's Requiem", "Mélodie du vent", "Melodía del Viento"}};
trickNameTable[0xBB] = {
Text{"Saria's Karaoke", "Karaoké de Saria", "Dueto del bosque"},
Text{"Sonata of Awakening", "Sonate de l'éveil", "Sonata del despertar"},
- Text{"Saria's Song", "Chant de Saria", "Canción de Saria"}};
+ Text{"Wind God's Aria", "Hymne du dieu du vent", "Melodía del Espíritu del Viento"}};
trickNameTable[0xBC] = {
Text{"Darunia's Tango", "Tango de Darunia", "Coro del fuego"},
- Text{"Goron Lullaby", "Berceuse des Gorons", "Nana goron"},
- Text{"Zelda's Lullaby", "Berceuse de Zelda", "Nana de Zelda"}};
+ Text{"Tune of Currents", "Chants des Flux", "Melodía de las Corrientes"},
+ Text{"Goron Lullaby", "Berceuse des Gorons", "Nana goron"}};
trickNameTable[0xBD] = {
Text{"Ruto's Blues", "Blues de Ruto", "Sonata del agua"},
Text{"New Wave Bossa Nova", "Bossa-nova des flots", "Bossanova de las olas"},
- Text{"Song of Time", "Chant du temps", "Canción del tiempo"}};
+ Text{"Manbo's Mambo", "Mambo de Manbo", "Mambo de Manbo"}};
trickNameTable[0xBE] = {
Text{"Nabooru's Reggae", "Reggae de Nabooru", "Reggae del espíritu"},
Text{"Elegy of Emptiness", "Hymne du vide", "Elegía al vacío"},
- Text{"Epona's Song", "Chant d'Épona", "Canción de Epona"}};
+ Text{"Earth God's Lyric", "Hymne du dieu de la terre", "Melodía del Espíritu de la Tierra"}};
trickNameTable[0xBF] = {
Text{"Impa's Death Metal", "Death métal d'Impa", "Diurno de la sombra"},
Text{"Oath to Order", "Ode de l'appel", "Oda al orden"},
- Text{"Song of Storms", "Chant des tempêtes", "Canción de la tormenta"}};
+ Text{"Song of Discovery", "Chant des secrets", "Canto revelador"}};
trickNameTable[0xC0] = {
Text{"Rauru's Sing-Along", "Chansonnette de Rauru", "Predulio de luz"},
Text{"Ballad of the Wind Fish", "Ballade sur Poisson-Rêve", "Balada del Piez Viento"},
- Text{"Sun's Song", "Chant du soleil", "Canción del Sol"}};
+ Text{"Song of Light", "Chant de la lumière", "Sonidos de la luz"}};
trickNameTable[0xCB] = {
Text{"Pendant of Courage", "Pendentif du courage", "Colgante del valor"},
+ Text{"Farore's Pearl", "Perle de Farore", "Orbe de Farore"},
+ Text{"Aquanine", "Smaragdine", "Yerbánida"},
Text{"Farore's Emerald", "Émeraude de Farore", "Esmeralda de Farore"},
Text{"Kokiri's Peridot", "Péridot Kokiri", "Ágata de los Kokiri"}};
trickNameTable[0xCC] = {
Text{"Pendant of Power", "Pendentif de la force", "Colgante del poder"},
+ Text{"Din's Pearl", "Perle de Din", "Orbe de Din"},
+ Text{"Crimsonine", "Alzanine", "Bermellina"},
Text{"Din's Ruby", "Rubis de Din", "Rubí de Din"},
Text{"Goron's Garnet", "Grenat Goron", "Topacio de los Goron"}};
trickNameTable[0xCD] = {
Text{"Pendant of Wisdom", "Pendentif de la sagesse", "Colgante de la sabiduría"},
+ Text{"Nayru's Pearl", "Perle de Nayru", "Orbe de Nayru"},
+ Text{"Azurine", "Aquanine", "Azurina"},
Text{"Nayru's Sapphire", "Saphir de Nayru", "Zafiro de Nayru"},
Text{"Zora's Aquamarine", "Aquamarine Zora", "Lapislázuli de los Zora"}};
trickNameTable[0xCE] = {
Text{"Wind Medallion", "Médaillon du vent", "Medallón del Viento"},
+ Text{"Wind Element", "Elément Vent", "Elemento de aire"},
Text{"Saria's Medallion", "Médaillon de Saria", "Medallón de Saria"},
+ Text{"Sign of Air", "Glyphe de l'air", "Glifo de aire"},
Text{"Medallion of Forest", "Médaillon du Temple de la Forêt", "Medalla del Bosque"}};
trickNameTable[0xCF] = {
- Text{"Bombos Medallion", "Médaillon des flammes", "Medallón del Temblor"},
+ Text{"Fire Element", "Elément Feu", "Elemento de fuego"},
Text{"Darunia's Medallion", "Médaillon de Darunia", "Medallón de Darunia"},
+ Text{"Sign of Fire", "Glyphe de feu", "Glifo de fuego"},
Text{"Medallion of Fire", "Médaillon du Temple du Feu", "Medalla del Fuego"}};
trickNameTable[0xD0] = {
+ Text{"Water Element", "Elément Eau", "Elemento de agua"},
Text{"Ice Medallion", "Médaillon de glace", "Medallón Helado"},
Text{"Ruto's Medallion", "Médaillon de Ruto", "Medallón de Ruto"},
+ Text{"Sign of Water", "Glyphe de l'eau", "Glifo de agua"},
Text{"Medallion of Water", "Médaillon du Temple de l'Eau", "Medalla del Agua"}};
trickNameTable[0xD1] = {
- Text{"Quake Medallion", "Médaillon des secousses", "Medallón Llamarada"},
+ Text{"Earth Element", "Elément Terre", "Elemento de tierra"},
Text{"Nabooru's Medallion", "Médaillon de Nabooru", "Medallón de Nabooru"},
+ Text{"Sign of Earth", "Glyphe de la Terre", "Glifo de la tierra"},
Text{"Medallion of Spirit", "Médaillon du Temple de l'Esprit", "Medalla del Espíritu"}};
trickNameTable[0xD2] = {
- Text{"Travel Medallion", "Amulette de téléportation", "Medallón Maligno"},
+ Text{"Fused Shadow", "Cristal d'ombre", "Sombra Fundida"},
Text{"Impa's Medallion", "Médaillon d'Impa", "Medallón de Impa"},
+ Text{"Sign of Illusion", "Glyphe de l'illusion", "Glifo de ilusión"},
Text{"Medallion of Shadow", "Médaillon du Temple de l'Ombre", "Medalla de la Sombra"}};
trickNameTable[0xD3] = {
- Text{"Ether Medallion", "Médaillon d'éther", "Medallón de Tesoro"},
+ Text{"Compass of Light", "Boussole de lumière", "Brújula de Luz"},
Text{"Rauru's Medallion", "Médaillon de Rauru", "Medallón de Rauru"},
+ Text{"Sign of Destiny", "Glyphe du destin", "Glifo del destino"},
Text{"Medallion of Light", "Médaillon du temple de lumière", "Medalla de la Luz"}};
trickNameTable[GI_HEART] = {
Text{"Love", "Bisou", "Te amo"},
- Text{"Heart Container", "Réceptacle de coeur", "Contenedor de corazón"},
- Text{"Piece of Heart", "Quart de coeur", "Pieza de corazón"}};
+ Text{"Life", "Vie", "vida"},
+ Text{"HP", "VP", "VP"}};
trickNameTable[GI_RUPEE_GREEN] = {
- Text{"Green Rupy", "Rupee vert", "Rubia verde"},
+ Text{"False Greg", "Faux Greg", "Falso Greg"},
+ Text{"One Ruby", "Un rubis", "Un rubí"},
+ Text{"Rupoor (1)", "Roupir (1)", "Rupobre (1)"},
Text{"One Rupee", "Un rubis", "Guaraní hyliano"},
Text{"Rupee (1)", "Rubis (1)", "Peso hyliano"}};
trickNameTable[GI_RUPEE_BLUE] = {
- Text{"Blue Rupy", "Rupee bleu", "Rubia azul"},
+ Text{"Blupee", "Bleubi", "Azupia"},
+ Text{"Five Rubies", "Cinq Rubys", "Cinco rubíes"},
Text{"Five Rupees", "Cinq rubis", "Bolívar hyliano"},
- Text{"Rupee (5)", "Rubis (5)", "Peso hyliano"}};
+ Text{"Rupee (5)", "Rubis (5)", "Peso hyliano"},
+ Text{"Rupoor (5)", "Roupir (5)", "Rupobre (5)"}};
trickNameTable[GI_RUPEE_RED] = {
- Text{"Red Rupy", "Rupee rouge", "Rubia roja"},
+ Text{"Big 20", "Grand 20", "Los 20 grandes"},
+ Text{"Twenty Rubies", "vingt rubis", "Veinte rubíes"},
+ Text{"Rupoor (20)", "Roupir (20)", "Rupobre (20)"},
Text{"Twenty Rupees", "Vingt rubis", "Colon hyliano"},
Text{"Rupee (20)", "Rubis (20)", "Peso hyliano"}};
trickNameTable[GI_RUPEE_PURPLE] = {
- Text{"Purple Rupy", "Rupee pourpre", "Rubia morada"},
+ Text{"Purpee", "pourbi", "morupiua"},
+ Text{"Fifty Rubies", "cinquante rubis", "Cincuenta rubíes"},
+ Text{"Rupoor (50)", "Roupir (50)", "Rupobre (50)"},
Text{"Fifty Rupees", "Cinquante rubis", "Balboa hyliano"},
Text{"Rupee (50)", "Rubis (50)", "Peso hyliano"}};
trickNameTable[GI_RUPEE_GOLD] = {
- Text{"Huge Rupy", "Énorme Rupee", "Rubia gigante"},
+ Text{"Hugo", "Or Rubi", "Oro Rubi"},
+ Text{"Two Hundred Rubies", "deux cents rubis", "Doscientos rubíes"},
+ Text{"Diamond", "Diamant", "Diamante"},
+ Text{"Huge Ruby", "Énorme rubis", "Rubi gigante"},
Text{"Two Hundred Rupees", "Deux cent rubis", "Euro hyliano"},
Text{"Rupee (200)", "Rubis (200)", "Dólar hyliano"}};
trickNameTable[GI_HEART_PIECE] = {
- Text{"Piece of Health", "Quart d'énergie", "Pieza de amor"},
- Text{"Recovery Heart", "Coeur d'énergie", "Corazón"},
- Text{"Heart Container", "Réceptacle de coeur", "Contenedor de corazón"}};
+ Text{"Pizza Heart", "Fromage de cœur", "Pieza de Chorizo"},
+ Text{"Little Bit Of Love", "Un peu d'amour", "Un poco de amor"},
+ Text{"Rare Peach Stone", "Pierre de pêche rare", "Pierre de pêche rare"}};
trickNameTable[GI_HEART_CONTAINER_2] = {
- Text{"Health Container", "Réceptacle d'énergie", "Contenedor de amor"},
- Text{"Recovery Heart", "Quart de coeur", "Corazón"},
- Text{"Piece of Heart", "Coeur d'énergie", "Pieza de corazón"}};
+ Text{"Crystal Heart", "Cœur de cristal", "Corazón de cristal"},
+ Text{"Life Heart", "Cœur de vie", "Vida Corazón"},
+ Text{"Lots of Love", "Beaucoup d'amour", "Mucho amor"}};
/*
//Names for individual upgrades, in case progressive names are replaced
diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp
index dc4f41ae0..cb87bc115 100644
--- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp
@@ -402,6 +402,15 @@ static void WriteSettings(const bool printAll = false) {
// 3drando doesn't have a "skip child zelda" setting, manually add it to the spoilerfile
jsonData["settings"]["Skip Child Zelda"] = Settings::skipChildZelda;
+ // 3drando uses an MQ dungeon count of 13 to mean random, manually add that to the spoilerfile as a bool
+ if (Settings::MQDungeonCount.GetSelectedOptionIndex() == 0) {
+ jsonData["settings"]["World Settings:MQ Dungeons"] = "None";
+ } else if (Settings::MQDungeonCount.GetSelectedOptionIndex() == 13) {
+ jsonData["settings"]["World Settings:MQ Dungeons"] = "Random Number";
+ } else {
+ jsonData["settings"]["World Settings:MQ Dungeons"] = "Set Number";
+ }
+
// spoilerLog.RootElement()->InsertEndChild(parentNode);
// for (const uint32_t key : allLocations) {
@@ -757,9 +766,9 @@ static void WriteHints(int language) {
std::string textStr = AutoFormatHintTextString(unformattedHintTextString);
jsonData["hints"][location->GetName()]["hint"] = textStr;
jsonData["hints"][location->GetName()]["type"] = hintTypeNames.find(hintType)->second;
- if (hintType == HINT_TYPE_ITEM || hintType == HINT_TYPE_NAMED_ITEM) {
+ if (hintType == HINT_TYPE_ITEM || hintType == HINT_TYPE_NAMED_ITEM || hintType == HINT_TYPE_WOTH) {
jsonData["hints"][location->GetName()]["item"] = hintedLocation->GetPlacedItemName().GetEnglish();
- if (hintType != HINT_TYPE_NAMED_ITEM) {
+ if (hintType != HINT_TYPE_NAMED_ITEM || hintType == HINT_TYPE_WOTH) {
jsonData["hints"][location->GetName()]["location"] = hintedLocation->GetName();
}
}
diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp
index 03caa6b2d..4423ac7f0 100644
--- a/soh/soh/Enhancements/randomizer/randomizer.cpp
+++ b/soh/soh/Enhancements/randomizer/randomizer.cpp
@@ -348,6 +348,7 @@ std::unordered_map SpoilerfileSettingNameToEn
{ "Timesaver Settings:Complete Mask Quest", RSK_COMPLETE_MASK_QUEST },
{ "Timesaver Settings:Skip Scarecrow's Song", RSK_SKIP_SCARECROWS_SONG },
{ "Timesaver Settings:Enable Glitch-Useful Cutscenes", RSK_ENABLE_GLITCH_CUTSCENES },
+ { "World Settings:MQ Dungeons", RSK_RANDOM_MQ_DUNGEONS },
{ "World Settings:MQ Dungeon Count", RSK_MQ_DUNGEON_COUNT }
};
@@ -1032,6 +1033,15 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_KAK_TOKENS;
}
break;
+ case RSK_RANDOM_MQ_DUNGEONS:
+ if (it.value() == "None") {
+ gSaveContext.randoSettings[index].value = RO_MQ_DUNGEONS_NONE;
+ } else if (it.value() == "Random Number") {
+ gSaveContext.randoSettings[index].value = RO_MQ_DUNGEONS_RANDOM_NUMBER;
+ } else if (it.value() == "Set Number") {
+ gSaveContext.randoSettings[index].value = RO_MQ_DUNGEONS_SET_NUMBER;
+ }
+ break;
case RSK_SKIP_CHILD_ZELDA:
gSaveContext.randoSettings[index].value = it.value();
break;
@@ -3814,7 +3824,10 @@ void RandomizerSettingsWindow::DrawElement() {
"A Giant's Knife and a pack of Bombchus will be added to the item pool, and "
"one of the bottles will contain a Blue Potion.\n\n"
"On (no hints) - Salesmen will be included but won't tell you what you'll get.\n"
- "On (with hints) - Salesmen will be included and you'll know what you're buying."
+ "On (with hints) - Salesmen will be included and you'll know what you're buying.\n"
+ "\n"
+ "Granny's item will only be offered after you have traded in the Odd Mushroom when Shuffle Adult Trade is on. "
+ "Otherwise when off, you will need to have found the Claim Check to buy her item (simulating the trade quest is complete)."
);
UIWidgets::EnhancementCombobox("gRandomizeShuffleMerchants", randoShuffleMerchants, RO_SHUFFLE_MERCHANTS_OFF);
@@ -5260,7 +5273,10 @@ CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) {
break;
}
- if (this->masterQuestDungeons.empty() || this->masterQuestDungeons.size() >= 12) {
+ if (this->randoSettings[RSK_RANDOM_MQ_DUNGEONS] == RO_MQ_DUNGEONS_NONE ||
+ (this->randoSettings[RSK_RANDOM_MQ_DUNGEONS] == RO_MQ_DUNGEONS_SET_NUMBER &&
+ this->randoSettings[RSK_MQ_DUNGEON_COUNT] == 12)
+ ) {
messageEntry.Replace("{{typeHint}}", "");
} else if (ResourceMgr_IsSceneMasterQuest(sceneNum)) {
messageEntry.Replace("{{typeHint}}", mapGetItemHints[0][1], mapGetItemHints[1][1], mapGetItemHints[2][1]);
diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
index 6998c6bb5..d1e5514be 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
+++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp
@@ -567,10 +567,10 @@ void InitializeChecks() {
areasSpoiled |= (1 << rcObj.rcArea);
}
- showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) > 0);
- //Bug: the above will spoil that everything is vanilla if the random count rolled 0.
- // Should use the below instead, but the setting isn't currently saved to the savefile
- //showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) != RO_GENERIC_OFF);
+ showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_RANDOM_NUMBER ||
+ (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
+ OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) < 12)
+ );
UpdateChecks();
UpdateInventoryChecks();
diff --git a/soh/soh/Enhancements/tts/tts.cpp b/soh/soh/Enhancements/tts/tts.cpp
index ab2bbf9d8..128ab4b1e 100644
--- a/soh/soh/Enhancements/tts/tts.cpp
+++ b/soh/soh/Enhancements/tts/tts.cpp
@@ -13,6 +13,7 @@
#include "soh/Enhancements/boss-rush/BossRush.h"
extern "C" {
+extern MapData* gMapData;
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}
@@ -191,28 +192,109 @@ void RegisterOnInterfaceUpdateHook() {
void RegisterOnKaleidoscopeUpdateHook() {
GameInteractor::Instance->RegisterGameHook([](int16_t inDungeonScene) {
if (!CVarGetInteger("gA11yTTS", 0)) return;
-
- static uint16_t prevCursorIndex = 0;
+
+ static int16_t prevCursorIndex = 0;
static uint16_t prevCursorSpecialPos = 0;
static uint16_t prevCursorPoint[5] = { 0 };
-
+ static int16_t prevPromptChoice = -1;
+ static int16_t prevSubState = -1;
+ static int16_t prevState = -1;
+
PauseContext* pauseCtx = &gPlayState->pauseCtx;
Input* input = &gPlayState->state.input[0];
-
- if (pauseCtx->state != 6) {
- //reset cursor index to so it is announced when pause is reopened
- prevCursorIndex = -1;
+
+ // Save game prompt
+ if (pauseCtx->state == 7) {
+ if (pauseCtx->unk_1EC == 1) {
+ // prompt
+ if (prevPromptChoice != pauseCtx->promptChoice) {
+ auto prompt = GetParameritizedText(pauseCtx->promptChoice == 0 ? "yes" : "no", TEXT_BANK_MISC, nullptr);
+ if (prevPromptChoice == -1) {
+ auto translation = GetParameritizedText("save_prompt", TEXT_BANK_KALEIDO, nullptr);
+ SpeechSynthesizer::Instance->Speak((translation + " - " + prompt).c_str(), GetLanguageCode());
+ } else {
+ SpeechSynthesizer::Instance->Speak(prompt.c_str(), GetLanguageCode());
+ }
+
+ prevPromptChoice = pauseCtx->promptChoice;
+ }
+ } else if (pauseCtx->unk_1EC == 4 && prevSubState != 4) {
+ // Saved
+ auto translation = GetParameritizedText("game_saved", TEXT_BANK_KALEIDO, nullptr);
+ SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ }
+ prevSubState = pauseCtx->unk_1EC;
+ prevState = pauseCtx->state;
return;
}
-
+
+ // Announce page when
+ // Kaleido pages are rotating and page halfway rotated
+ // Or Kaleido was just opened
+ if ((pauseCtx->unk_1E4 == 1 && pauseCtx->unk_1EA == 32) || (pauseCtx->state == 4 && prevState != 4)) {
+ uint16_t modeNextPageMap[] = {
+ PAUSE_MAP, PAUSE_EQUIP, PAUSE_QUEST, PAUSE_ITEM, PAUSE_EQUIP, PAUSE_MAP, PAUSE_ITEM, PAUSE_QUEST,
+ };
+ uint16_t nextPage = modeNextPageMap[pauseCtx->mode];
+
+ switch (nextPage) {
+ case PAUSE_ITEM: {
+ auto translation = GetParameritizedText("item_menu", TEXT_BANK_KALEIDO, nullptr);
+ SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ break;
+ }
+ case PAUSE_MAP: {
+ std::string map;
+ if (inDungeonScene) {
+ std::string key = std::to_string(gSaveContext.mapIndex);
+ map = GetParameritizedText(key, TEXT_BANK_SCENES, nullptr);
+ } else {
+ map = GetParameritizedText("overworld", TEXT_BANK_KALEIDO, nullptr);
+ }
+ auto translation = GetParameritizedText("map_menu", TEXT_BANK_KALEIDO, map.c_str());
+ SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ break;
+ }
+ case PAUSE_QUEST: {
+ auto translation = GetParameritizedText("quest_menu", TEXT_BANK_KALEIDO, nullptr);
+ SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ break;
+ }
+ case PAUSE_EQUIP: {
+ auto translation = GetParameritizedText("equip_menu", TEXT_BANK_KALEIDO, nullptr);
+ SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ break;
+ }
+ }
+ prevState = pauseCtx->state;
+ return;
+ }
+
+ prevState = pauseCtx->state;
+
+ if (pauseCtx->state != 6) {
+ // Reset cursor index and values so it is announced when pause is reopened
+ prevCursorIndex = -1;
+ prevPromptChoice = -1;
+ prevSubState = -1;
+ return;
+ }
+
if ((pauseCtx->debugState != 1) && (pauseCtx->debugState != 2)) {
char arg[8];
if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) {
- snprintf(arg, sizeof(arg), "%d", gSaveContext.health);
+ // Normalize hearts to fractional count similar to z_lifemeter
+ int curHeartFraction = gSaveContext.health % 16;
+ int fullHearts = gSaveContext.health / 16;
+ float fraction = ceilf((float)curHeartFraction / 5) * 0.25;
+ float health = (float)fullHearts + fraction;
+ snprintf(arg, sizeof(arg), "%g", health);
auto translation = GetParameritizedText("health", TEXT_BANK_KALEIDO, arg);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
- } else if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) {
- snprintf(arg, sizeof(arg), "%d", gSaveContext.magic);
+ } else if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT) && gSaveContext.magicCapacity != 0) {
+ // Normalize magic to percentage
+ float magicLevel = ((float)gSaveContext.magic / gSaveContext.magicCapacity) * 100;
+ snprintf(arg, sizeof(arg), "%.0f%%", magicLevel);
auto translation = GetParameritizedText("magic", TEXT_BANK_KALEIDO, arg);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
} else if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) {
@@ -236,6 +318,17 @@ void RegisterOnKaleidoscopeUpdateHook() {
if (pauseCtx->cursorSpecialPos > 0) {
return;
}
+
+ std::string buttonNames[] = {
+ "input_button_c_left",
+ "input_button_c_down",
+ "input_button_c_right",
+ "input_d_pad_up",
+ "input_d_pad_down",
+ "input_d_pad_left",
+ "input_d_pad_right",
+ };
+ int8_t assignedTo = -1;
switch (pauseCtx->pageIndex) {
case PAUSE_ITEM:
@@ -248,36 +341,71 @@ void RegisterOnKaleidoscopeUpdateHook() {
case ITEM_BOMBCHU:
case ITEM_SLINGSHOT:
case ITEM_BOW:
- snprintf(arg, sizeof(arg), "%d", AMMO(pauseCtx->cursorItem[PAUSE_ITEM]));
- break;
case ITEM_BEAN:
- snprintf(arg, sizeof(arg), "%d", 0);
+ snprintf(arg, sizeof(arg), "%d", AMMO(pauseCtx->cursorItem[PAUSE_ITEM]));
break;
default:
arg[0] = '\0';
}
-
- if (pauseCtx->cursorItem[PAUSE_ITEM] == 999) {
+
+ if (pauseCtx->cursorItem[PAUSE_ITEM] == PAUSE_ITEM_NONE ||
+ pauseCtx->cursorItem[PAUSE_ITEM] == ITEM_NONE) {
+ prevCursorIndex = -1;
return;
}
-
+
std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_ITEM]);
- auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, arg);
- SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ std::string itemTranslation = GetParameritizedText(key, TEXT_BANK_KALEIDO, arg);
+
+ // Check if item is assigned to a button
+ for (size_t i = 0; i < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); i++) {
+ if (gSaveContext.equips.buttonItems[i + 1] == pauseCtx->cursorItem[PAUSE_ITEM]) {
+ assignedTo = i;
+ break;
+ }
+ }
+
+ if (assignedTo != -1) {
+ auto button = GetParameritizedText(buttonNames[assignedTo], TEXT_BANK_MISC, nullptr);
+ auto translation = GetParameritizedText("assigned_to", TEXT_BANK_KALEIDO, button.c_str());
+ SpeechSynthesizer::Instance->Speak((itemTranslation + " - " + translation).c_str(), GetLanguageCode());
+ } else {
+ SpeechSynthesizer::Instance->Speak(itemTranslation.c_str(), GetLanguageCode());
+ }
break;
}
case PAUSE_MAP:
if (inDungeonScene) {
+ // Dungeon map items
if (pauseCtx->cursorItem[PAUSE_MAP] != PAUSE_ITEM_NONE) {
std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_MAP]);
auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ } else {
+ // Dungeon map floor numbers
+ char arg[8];
+ int cursorPoint = pauseCtx->cursorPoint[PAUSE_MAP];
+
+ // Cursor is on a dungeon floor position
+ if (cursorPoint >= 3 && cursorPoint < 11) {
+ int floorID = gMapData->floorID[gPlayState->interfaceCtx.unk_25A][pauseCtx->dungeonMapSlot - 3];
+ // Normalize so F1 == 0, and negative numbers are basement levels
+ int normalizedFloor = (floorID * -1) + 8;
+ if (normalizedFloor >= 0) {
+ snprintf(arg, sizeof(arg), "%d", normalizedFloor + 1);
+ auto translation = GetParameritizedText("floor", TEXT_BANK_KALEIDO, arg);
+ SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ } else {
+ snprintf(arg, sizeof(arg), "%d", normalizedFloor * -1);
+ auto translation = GetParameritizedText("basement", TEXT_BANK_KALEIDO, arg);
+ SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ }
+ }
}
} else {
std::string key = std::to_string(0x0100 + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]);
auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
- SPDLOG_INFO("Item: {}", key);
}
break;
case PAUSE_QUEST:
@@ -288,16 +416,17 @@ void RegisterOnKaleidoscopeUpdateHook() {
snprintf(arg, sizeof(arg), "%d", gSaveContext.inventory.gsTokens);
break;
case ITEM_HEART_CONTAINER:
- snprintf(arg, sizeof(arg), "%d", ((gSaveContext.inventory.questItems & 0xF) & 0xF) >> 0x1C);
+ snprintf(arg, sizeof(arg), "%d", (gSaveContext.inventory.questItems & 0xF0000000) >> 0x1C);
break;
default:
arg[0] = '\0';
}
-
- if (pauseCtx->cursorItem[PAUSE_QUEST] == 999) {
+
+ if (pauseCtx->cursorItem[PAUSE_QUEST] == PAUSE_ITEM_NONE) {
+ prevCursorIndex = -1;
return;
}
-
+
std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_QUEST]);
auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, arg);
SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
@@ -305,15 +434,51 @@ void RegisterOnKaleidoscopeUpdateHook() {
}
case PAUSE_EQUIP:
{
+ if (pauseCtx->namedItem == PAUSE_ITEM_NONE) {
+ prevCursorIndex = -1;
+ return;
+ }
+
std::string key = std::to_string(pauseCtx->cursorItem[PAUSE_EQUIP]);
- auto translation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr);
- SpeechSynthesizer::Instance->Speak(translation.c_str(), GetLanguageCode());
+ auto itemTranslation = GetParameritizedText(key, TEXT_BANK_KALEIDO, nullptr);
+ uint8_t checkEquipItem = pauseCtx->namedItem;
+
+ // BGS from kaleido reports as ITEM_HEART_PIECE_2 (122)
+ // remap BGS and broken knife to be the BGS item for the current equip check
+ if (checkEquipItem == ITEM_HEART_PIECE_2 || checkEquipItem == ITEM_SWORD_KNIFE) {
+ checkEquipItem = ITEM_SWORD_BGS;
+ }
+
+ // Check if equipment item is currently equipped or assigned to a button
+ if (checkEquipItem >= ITEM_SWORD_KOKIRI && checkEquipItem <= ITEM_BOOTS_HOVER) {
+ uint8_t checkEquipType = (checkEquipItem - ITEM_SWORD_KOKIRI) / 3;
+ uint8_t checkEquipValue = ((checkEquipItem - ITEM_SWORD_KOKIRI) % 3) + 1;
+
+ if (CUR_EQUIP_VALUE(checkEquipType) == checkEquipValue) {
+ itemTranslation = GetParameritizedText("equipped", TEXT_BANK_KALEIDO, itemTranslation.c_str());
+ }
+
+ for (size_t i = 0; i < ARRAY_COUNT(gSaveContext.equips.cButtonSlots); i++) {
+ if (gSaveContext.equips.buttonItems[i + 1] == checkEquipItem) {
+ assignedTo = i;
+ break;
+ }
+ }
+ }
+
+ if (assignedTo != -1) {
+ auto button = GetParameritizedText(buttonNames[assignedTo], TEXT_BANK_MISC, nullptr);
+ auto translation = GetParameritizedText("assigned_to", TEXT_BANK_KALEIDO, button.c_str());
+ SpeechSynthesizer::Instance->Speak((itemTranslation + " - " + translation).c_str(), GetLanguageCode());
+ } else {
+ SpeechSynthesizer::Instance->Speak(itemTranslation.c_str(), GetLanguageCode());
+ }
break;
}
default:
break;
}
-
+
prevCursorIndex = cursorIndex;
memcpy(prevCursorPoint, pauseCtx->cursorPoint, sizeof(prevCursorPoint));
});
diff --git a/soh/soh/Extractor/Extract.cpp b/soh/soh/Extractor/Extract.cpp
index 143a86380..b5eff7bae 100644
--- a/soh/soh/Extractor/Extract.cpp
+++ b/soh/soh/Extractor/Extract.cpp
@@ -44,6 +44,8 @@
#include
#include
#include
+#include
+#include
extern "C" uint32_t CRC32C(unsigned char* data, size_t dataSize);
extern "C" void RomToBigEndian(void* rom, size_t romSize);
@@ -496,14 +498,50 @@ const char* Extractor::GetZapdVerStr() const {
}
}
+std::string Extractor::Mkdtemp() {
+ std::string temp_dir = std::filesystem::temp_directory_path().string();
+
+ // create 6 random alphanumeric characters
+ static const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<> dist(0, sizeof(charset) - 1);
+
+ char randchr[7];
+ for (int i = 0; i < 6; i++) {
+ randchr[i] = charset[dist(gen)];
+ }
+ randchr[6] = '\0';
+
+ std::string tmppath = temp_dir + "/extractor-" + randchr;
+ std::filesystem::create_directory(tmppath);
+ return tmppath;
+}
+
extern "C" int zapd_main(int argc, char** argv);
-bool Extractor::CallZapd() {
+bool Extractor::CallZapd(std::string installPath, std::string exportdir) {
constexpr int argc = 16;
char xmlPath[1024];
char confPath[1024];
std::array argv;
const char* version = GetZapdVerStr();
+ const char* otrFile = IsMasterQuest() ? "oot-mq.otr" : "oot.otr";
+
+ std::string romPath = std::filesystem::absolute(mCurrentRomPath).string();
+ installPath = std::filesystem::absolute(installPath).string();
+ exportdir = std::filesystem::absolute(exportdir).string();
+ // Work this out in the temporary folder
+ std::string tempdir = Mkdtemp();
+ std::string curdir = std::filesystem::current_path().string();
+#ifdef _WIN32
+ std::filesystem::copy(installPath + "/assets", tempdir + "/assets",
+ std::filesystem::copy_options::recursive | std::filesystem::copy_options::update_existing);
+#else
+ std::filesystem::create_symlink(installPath + "/assets", tempdir + "/assets");
+#endif
+
+ std::filesystem::current_path(tempdir);
snprintf(xmlPath, 1024, "assets/extractor/xmls/%s", version);
snprintf(confPath, 1024, "assets/extractor/Config_%s.xml", version);
@@ -513,7 +551,7 @@ bool Extractor::CallZapd() {
argv[2] = "-i";
argv[3] = xmlPath;
argv[4] = "-b";
- argv[5] = mCurrentRomPath.c_str();
+ argv[5] = romPath.c_str();
argv[6] = "-fl";
argv[7] = "assets/extractor/filelists";
argv[8] = "-gsf";
@@ -523,7 +561,7 @@ bool Extractor::CallZapd() {
argv[12] = "-se";
argv[13] = "OTR";
argv[14] = "--otrfile";
- argv[15] = IsMasterQuest() ? "oot-mq.otr" : "oot.otr";
+ argv[15] = otrFile;
#ifdef _WIN32
// Grab a handle to the command window.
@@ -541,6 +579,12 @@ bool Extractor::CallZapd() {
ShowWindow(cmdWindow, SW_HIDE);
#endif
+ std::filesystem::copy(otrFile, exportdir + "/" + otrFile, std::filesystem::copy_options::overwrite_existing);
+
+ // Go back to where this game was executed from
+ std::filesystem::current_path(curdir);
+ std::filesystem::remove_all(tempdir);
+
return 0;
}
diff --git a/soh/soh/Extractor/Extract.h b/soh/soh/Extractor/Extract.h
index e4eb2e5bb..6c9b4a078 100644
--- a/soh/soh/Extractor/Extract.h
+++ b/soh/soh/Extractor/Extract.h
@@ -57,7 +57,8 @@ class Extractor {
bool IsMasterQuest() const;
bool Run(RomSearchMode searchMode = RomSearchMode::Both);
- bool CallZapd();
+ bool CallZapd(std::string installPath, std::string exportdir);
const char* GetZapdStr();
+ std::string Mkdtemp();
};
#endif
diff --git a/soh/soh/Extractor/FastCrc32C.c b/soh/soh/Extractor/FastCrc32C.c
index 5ec097193..d88b04beb 100644
--- a/soh/soh/Extractor/FastCrc32C.c
+++ b/soh/soh/Extractor/FastCrc32C.c
@@ -1,14 +1,28 @@
#include
#include
+// Force the compiler to assume we have support for the CRC32 intrinsic. We will check for our selves later.
+// Clang will define both __llvm__ and __GNUC__ but GCC will only define __GNUC__. So we need to check for __llvm__ first.
+#if ((defined(__llvm__) && (defined(__x86_64__) || defined(__i386__))))
+#pragma clang attribute push(__attribute__((target("crc32"))), apply_to = function)
+#elif ((defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))))
+// GCC Only lets you enable all of sse4.2 so we will for just this file and reset it at the end.
+#pragma GCC push_options
+#pragma GCC target("sse4.2")
+#endif
+
+// Include headers for the CRC32 intrinsic and cpuid instruction on windows. No need to do any other checks because it assumes the target will support CRC32
#ifdef _WIN32
#include
-#elif ((defined(__GNUC__) && defined(__x86_64__) || defined(__i386__)) && defined(__SSE4_2__))
+#include
+// Same as above but these platforms use slightly different headers
+#elif ((defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))))
#include
+#include
#elif defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)
// Nothing cause its a compiler builtin
#else
-#define USE_CRC_TABLE
+#define NO_CRC_INTRIN
#endif
#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)
@@ -16,14 +30,13 @@
#define INTRIN_CRC32_32(crc, value) __asm__("crc32cw %w[c], %w[c], %w[v]" : [c] "+r"(crc) : [v] "r"(value))
#define INTRIN_CRC32_16(crc, value) __asm__("crc32ch %w[c], %w[c], %w[v]" : [c] "+r"(crc) : [v] "r"(value))
#define INTRIN_CRC32_8(crc, value) __asm__("crc32cb %w[c], %w[c], %w[v]" : [c] "+r"(crc) : [v] "r"(value))
-#elif defined(__SSE4_2__) || defined(_MSC_VER)
+#elif defined(__GNUC__) || defined(_MSC_VER)
#define INTRIN_CRC32_64(crc, data) crc = _mm_crc32_u64(crc, data)
#define INTRIN_CRC32_32(crc, data) crc = _mm_crc32_u32(crc, data)
#define INTRIN_CRC32_16(crc, data) crc = _mm_crc32_u16(crc, data)
#define INTRIN_CRC32_8(crc, data) crc = _mm_crc32_u8(crc, data)
#endif
-#ifdef USE_CRC_TABLE
static const uint32_t crc32Table[256] = {
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL,
0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL,
@@ -55,17 +68,13 @@ static const uint32_t crc32Table[256] = {
0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
};
-#endif
+// On platforms that we know will never support a crc32 instruction (such as the WiiU) we will skip compiling this function in.
+#ifndef NO_CRC_INTRIN
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef USE_CRC_TABLE
-uint32_t CRC32C(unsigned char* data, size_t dataSize) {
+static uint32_t CRC32IntrinImpl(unsigned char* data, size_t dataSize) {
uint32_t ret = 0xFFFFFFFF;
int64_t sizeSigned = dataSize;
-
+// Only 64bit platforms support doing a CRC32 operation on a 64bit value
#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__)
while ((sizeSigned -= sizeof(uint64_t)) >= 0) {
INTRIN_CRC32_64(ret, *(uint64_t*)data);
@@ -77,6 +86,7 @@ uint32_t CRC32C(unsigned char* data, size_t dataSize) {
data += sizeof(uint32_t);
}
+// On 32 bit we can only do 32bit operations
#elif defined(_M_IX86) || defined(__i386__)
while ((sizeSigned -= sizeof(uint32_t)) >= 0) {
INTRIN_CRC32_32(ret, *(uint32_t*)data);
@@ -94,17 +104,41 @@ uint32_t CRC32C(unsigned char* data, size_t dataSize) {
return ~ret;
}
-#else
-uint32_t CRC32C(const void* buf, size_t size) {
- const uint8_t* p = buf;
+#endif
+
+static uint32_t CRC32TableImpl(unsigned char* data, size_t dataSize) {
+ const uint8_t* p = data;
uint32_t crc = 0xFFFFFFFF;
- while (size--)
+ while (dataSize--)
crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
return ~crc;
}
+
+uint32_t CRC32C(unsigned char* data, size_t dataSize) {
+#ifndef NO_CRC_INTRIN
+ // Test to make sure the CPU supports the CRC32 intrinsic
+ unsigned int cpuidData[4];
+#ifdef _WIN32
+ __cpuid(cpuidData, 1);
+#elif __APPLE__ || (defined(__aarch64__) && defined(__ARM_FEATURE_CRC32))
+// Every Mac that supports SoH should support this instruction. Also check for ARM64 at the same time
+ return CRC32IntrinImpl(data, dataSize);
+#else
+ __get_cpuid(1, &cpuidData[0], &cpuidData[1], &cpuidData[2], &cpuidData[3]);
#endif
-#ifdef __cplusplus
+
+ if (cpuidData[2] & (1 << 20)) { // bit_SSE4_2
+ return CRC32IntrinImpl(data, dataSize);
+ }
+#endif // NO_CRC_INTRIN
+ return CRC32TableImpl(data, dataSize);
}
+
+#if ((defined(__llvm__) && (defined(__x86_64__) || defined(__i386__))))
+#pragma clang attribute pop
+#elif ((defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))))
+#pragma GCC pop_options
+#else
#endif
diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp
index 6c9663ed7..e05468684 100644
--- a/soh/soh/OTRGlobals.cpp
+++ b/soh/soh/OTRGlobals.cpp
@@ -208,11 +208,11 @@ const char* constCameraStrings[] = {
OTRGlobals::OTRGlobals() {
std::vector OTRFiles;
- std::string mqPath = LUS::Context::GetPathRelativeToAppDirectory("oot-mq.otr");
+ std::string mqPath = LUS::Context::LocateFileAcrossAppDirs("oot-mq.otr", appShortName);
if (std::filesystem::exists(mqPath)) {
OTRFiles.push_back(mqPath);
}
- std::string ootPath = LUS::Context::GetPathRelativeToAppDirectory("oot.otr");
+ std::string ootPath = LUS::Context::LocateFileAcrossAppDirs("oot.otr", appShortName);
if (std::filesystem::exists(ootPath)) {
OTRFiles.push_back(ootPath);
}
@@ -220,7 +220,7 @@ OTRGlobals::OTRGlobals() {
if (std::filesystem::exists(sohOtrPath)) {
OTRFiles.push_back(sohOtrPath);
}
- std::string patchesPath = LUS::Context::GetPathRelativeToAppDirectory("mods");
+ std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName);
if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) {
if (std::filesystem::is_directory(patchesPath)) {
for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath)) {
@@ -248,7 +248,7 @@ OTRGlobals::OTRGlobals() {
OOT_PAL_GC_DBG2
};
// tell LUS to reserve 3 SoH specific threads (Game, Audio, Save)
- context = LUS::Context::CreateInstance("Ship of Harkinian", "soh", "shipofharkinian.json", OTRFiles, {}, 3);
+ context = LUS::Context::CreateInstance("Ship of Harkinian", appShortName, "shipofharkinian.json", OTRFiles, {}, 3);
context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Animation, "Animation", std::make_shared());
context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_PlayerAnimation, "PlayerAnimation", std::make_shared());
@@ -717,8 +717,16 @@ extern "C" void OTRExtScanner() {
extern "C" void InitOTR() {
#if not defined (__SWITCH__) && not defined(__WIIU__)
- if (!std::filesystem::exists(LUS::Context::GetPathRelativeToAppDirectory("oot-mq.otr")) &&
- !std::filesystem::exists(LUS::Context::GetPathRelativeToAppDirectory("oot.otr"))){
+ if (!std::filesystem::exists(LUS::Context::LocateFileAcrossAppDirs("oot-mq.otr", appShortName)) &&
+ !std::filesystem::exists(LUS::Context::LocateFileAcrossAppDirs("oot.otr", appShortName))){
+
+ std::string installPath = LUS::Context::GetAppBundlePath();
+ if (!std::filesystem::exists(installPath + "/assets/extractor")) {
+ Extractor::ShowErrorBox("Extractor assets not found",
+ "No OTR files found. Missing assets/extractor folder needed to generate OTR file. Exiting...");
+ exit(1);
+ }
+
bool generatedOtrIsMQ = false;
if (Extractor::ShowYesNoBox("No OTR Files", "No OTR files found. Generate one now?") == IDYES) {
Extractor extract;
@@ -726,7 +734,7 @@ extern "C" void InitOTR() {
Extractor::ShowErrorBox("Error", "An error occured, no OTR file was generated. Exiting...");
exit(1);
}
- extract.CallZapd();
+ extract.CallZapd(installPath, LUS::Context::GetAppDirectoryPath(appShortName));
generatedOtrIsMQ = extract.IsMasterQuest();
} else {
exit(1);
@@ -736,7 +744,7 @@ extern "C" void InitOTR() {
if (!extract.Run(generatedOtrIsMQ ? RomSearchMode::Vanilla : RomSearchMode::MQ)) {
Extractor::ShowErrorBox("Error", "An error occured, an OTR file may have been generated by a different step. Continuing...");
} else {
- extract.CallZapd();
+ extract.CallZapd(installPath, LUS::Context::GetAppDirectoryPath(appShortName));
}
}
}
@@ -1030,6 +1038,52 @@ extern "C" uint32_t ResourceMgr_GetGameVersion(int index) {
return LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[index];
}
+extern "C" uint32_t ResourceMgr_GetGamePlatform(int index) {
+ uint32_t version = LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[index];
+
+ switch (version) {
+ case OOT_NTSC_US_10:
+ case OOT_NTSC_US_11:
+ case OOT_NTSC_US_12:
+ case OOT_PAL_10:
+ case OOT_PAL_11:
+ return GAME_PLATFORM_N64;
+ case OOT_NTSC_JP_GC:
+ case OOT_NTSC_US_GC:
+ case OOT_PAL_GC:
+ case OOT_NTSC_JP_MQ:
+ case OOT_NTSC_US_MQ:
+ case OOT_PAL_MQ:
+ case OOT_PAL_GC_DBG1:
+ case OOT_PAL_GC_DBG2:
+ case OOT_PAL_GC_MQ_DBG:
+ return GAME_PLATFORM_GC;
+ }
+}
+
+extern "C" uint32_t ResourceMgr_GetGameRegion(int index) {
+ uint32_t version = LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[index];
+
+ switch (version) {
+ case OOT_NTSC_US_10:
+ case OOT_NTSC_US_11:
+ case OOT_NTSC_US_12:
+ case OOT_NTSC_JP_GC:
+ case OOT_NTSC_US_GC:
+ case OOT_NTSC_JP_MQ:
+ case OOT_NTSC_US_MQ:
+ return GAME_REGION_NTSC;
+ case OOT_PAL_10:
+ case OOT_PAL_11:
+ case OOT_PAL_GC:
+ case OOT_PAL_MQ:
+ case OOT_PAL_GC_DBG1:
+ case OOT_PAL_GC_DBG2:
+ case OOT_PAL_GC_MQ_DBG:
+ return GAME_REGION_PAL;
+ }
+}
+
uint32_t IsSceneMasterQuest(s16 sceneNum) {
uint32_t value = 0;
uint8_t mqMode = CVarGetInteger("gBetterDebugWarpScreenMQMode", WARP_MODE_OVERRIDE_OFF);
diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h
index bef92f306..29d4a3f84 100644
--- a/soh/soh/OTRGlobals.h
+++ b/soh/soh/OTRGlobals.h
@@ -6,6 +6,12 @@
#include "SaveManager.h"
#include
+#define GAME_REGION_NTSC 0
+#define GAME_REGION_PAL 1
+
+#define GAME_PLATFORM_N64 0
+#define GAME_PLATFORM_GC 1
+
#ifdef __cplusplus
#include
#include "Enhancements/savestates.h"
@@ -13,6 +19,7 @@
#include
const std::string customMessageTableID = "BaseGameOverrides";
+const std::string appShortName = "soh";
class OTRGlobals
{
@@ -61,6 +68,8 @@ uint32_t ResourceMgr_GameHasMasterQuest();
uint32_t ResourceMgr_GameHasOriginal();
uint32_t ResourceMgr_GetNumGameVersions();
uint32_t ResourceMgr_GetGameVersion(int index);
+uint32_t ResourceMgr_GetGamePlatform(int index);
+uint32_t ResourceMgr_GetGameRegion(int index);
void ResourceMgr_LoadDirectory(const char* resName);
char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize);
uint8_t ResourceMgr_FileExists(const char* resName);
diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp
index 9330feec4..68f64c3a2 100644
--- a/soh/soh/SohMenuBar.cpp
+++ b/soh/soh/SohMenuBar.cpp
@@ -249,16 +249,16 @@ void DrawSettingsMenu() {
if (ImGui::BeginMenu("Graphics")) {
#ifndef __APPLE__
- UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f, "", 1.0f, true);
+ if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f, "", 1.0f, true)) {
+ LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1));
+ };
UIWidgets::Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective form of anti-aliasing");
- // OTRTODO: fix this
- // LUS::SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1));
#endif
#ifndef __WIIU__
- UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true, false);
+ if (UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true, false)) {
+ LUS::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1));
+ };
UIWidgets::Tooltip("Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel");
- // OTRTODO: fix this
- // LUS::SetMSAALevel(CVarGetInteger("gMSAAValue", 1));
#endif
{ // FPS Slider
@@ -1009,6 +1009,9 @@ void DrawEnhancementsMenu() {
ApplyAuthenticGfxPatches();
}
UIWidgets::Tooltip("Fixes authentic out of bounds texture reads, instead loading textures with the correct size");
+ UIWidgets::PaddedEnhancementCheckbox("Fix Poacher's Saw Softlock", "gFixSawSoftlock", true, false, CVarGetInteger("gSkipText", 0),
+ "This is disabled because it is forced on when Skip Text is enabled.", UIWidgets::CheckboxGraphics::Checkmark);
+ UIWidgets::Tooltip("Prevents the Poacher's Saw softlock from mashing through the text, or with Skip Text enabled.");
ImGui::EndMenu();
}
diff --git a/soh/src/code/padmgr.c b/soh/src/code/padmgr.c
index abfa029e1..4735a259c 100644
--- a/soh/src/code/padmgr.c
+++ b/soh/src/code/padmgr.c
@@ -284,6 +284,13 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) {
Fault_AddHungupAndCrash(__FILE__, __LINE__);
}
+ // When 3 frames are left on easy pause buffer, re-apply the last held inputs to the prev inputs
+ // to compute the pressed difference. This makes it so previously held inputs are continued as "held",
+ // but new inputs when unpausing are "pressed" out of the pause menu.
+ if (CVarGetInteger("gCheatEasyPauseBufferTimer", 0) == 3) {
+ input->prev.button = CVarGetInteger("gCheatEasyPauseBufferLastInputs", 0);
+ }
+
buttonDiff = input->prev.button ^ input->cur.button;
input->press.button |= (u16)(buttonDiff & input->cur.button);
input->rel.button |= (u16)(buttonDiff & input->prev.button);
@@ -295,12 +302,6 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) {
uint8_t rumble = (padMgr->rumbleEnable[0] > 0);
OTRControllerCallback(rumble);
- if (CVarGetInteger("gPauseBufferBlockInputFrame", 0)) {
- ControllerBlockGameInput(PAUSE_BUFFER_INPUT_BLOCK_ID);
- } else {
- ControllerUnblockGameInput(PAUSE_BUFFER_INPUT_BLOCK_ID);
- }
-
PadMgr_UnlockPadData(padMgr);
}
diff --git a/soh/src/code/z_camera.c b/soh/src/code/z_camera.c
index e0d827107..325eca828 100644
--- a/soh/src/code/z_camera.c
+++ b/soh/src/code/z_camera.c
@@ -1678,12 +1678,13 @@ s32 Camera_Normal1(Camera* camera) {
Camera_ClampDist(camera, eyeAdjustment.r, norm1->distMin, norm1->distMax, anim->unk_28);
if (anim->startSwingTimer <= 0) {
+ // idle camera re-center
if (CVarGetInteger("gA11yDisableIdleCam", 0)) return;
eyeAdjustment.pitch = atEyeNextGeo.pitch;
eyeAdjustment.yaw =
Camera_LERPCeilS(anim->swingYawTarget, atEyeNextGeo.yaw, 1.0f / camera->yawUpdateRateInv, 0xA);
} else if (anim->swing.unk_18 != 0) {
- if (CVarGetInteger("gA11yDisableIdleCam", 0)) return;
+ // camera adjustments when obstructed/pushed by scene geometry
eyeAdjustment.yaw =
Camera_LERPCeilS(anim->swing.unk_16, atEyeNextGeo.yaw, 1.0f / camera->yawUpdateRateInv, 0xA);
eyeAdjustment.pitch =
@@ -1708,19 +1709,19 @@ s32 Camera_Normal1(Camera* camera) {
if ((camera->status == CAM_STAT_ACTIVE) && (!(norm1->interfaceFlags & 0x10))) {
anim->swingYawTarget = BINANG_ROT180(camera->playerPosRot.rot.y);
if (!CVarGetInteger("gFixCameraSwing", 0)) {
- if (anim->startSwingTimer > 0) {
- func_80046E20(camera, &eyeAdjustment, norm1->distMin, norm1->unk_0C, &sp98, &anim->swing);
- } else {
- sp88 = *eyeNext;
- anim->swing.swingUpdateRate = camera->yawUpdateRateInv = norm1->unk_0C * 2.0f;
- if (Camera_BGCheck(camera, at, &sp88)) {
- anim->swingYawTarget = atEyeNextGeo.yaw;
- anim->startSwingTimer = -1;
+ if (anim->startSwingTimer > 0) {
+ func_80046E20(camera, &eyeAdjustment, norm1->distMin, norm1->unk_0C, &sp98, &anim->swing);
} else {
- *eye = *eyeNext;
+ sp88 = *eyeNext;
+ anim->swing.swingUpdateRate = camera->yawUpdateRateInv = norm1->unk_0C * 2.0f;
+ if (Camera_BGCheck(camera, at, &sp88)) {
+ anim->swingYawTarget = atEyeNextGeo.yaw;
+ anim->startSwingTimer = -1;
+ } else {
+ *eye = *eyeNext;
+ }
+ anim->swing.unk_18 = 0;
}
- anim->swing.unk_18 = 0;
- }
} else {
if (anim->startSwingTimer <= 0) {
anim->swing.swingUpdateRate = camera->yawUpdateRateInv = norm1->unk_0C * 2.0f;
diff --git a/soh/src/code/z_kaleido_setup.c b/soh/src/code/z_kaleido_setup.c
index 94cb91d49..a335c2460 100644
--- a/soh/src/code/z_kaleido_setup.c
+++ b/soh/src/code/z_kaleido_setup.c
@@ -18,15 +18,24 @@ void KaleidoSetup_Update(PlayState* play) {
play->shootingGalleryStatus <= 1 && gSaveContext.magicState != 8 && gSaveContext.magicState != 9 &&
(play->sceneNum != SCENE_BOWLING || !Flags_GetSwitch(play, 0x38))) {
- if (CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0) == 2 && !CHECK_BTN_ALL(input->press.button, BTN_START)) {
- CVarSetInteger("gCheatEasyPauseBufferFrameAdvance", 0);
+ u8 easyPauseBufferEnabled = CVarGetInteger("gCheatEasyPauseBufferEnabled", 0);
+ u8 easyPauseBufferTimer = CVarGetInteger("gCheatEasyPauseBufferTimer", 0);
+
+ // If start is not seen as pressed on the 2nd to last frame then we should end the easy frame advance flow
+ if (easyPauseBufferEnabled && easyPauseBufferTimer == 2 &&
+ !CHECK_BTN_ALL(input->press.button, BTN_START)) {
+ CVarSetInteger("gCheatEasyPauseBufferTimer", 0);
}
if (CHECK_BTN_ALL(input->cur.button, BTN_L) && CHECK_BTN_ALL(input->press.button, BTN_CUP)) {
if (BREG(0)) {
pauseCtx->debugState = 3;
}
- } else if ((CHECK_BTN_ALL(input->press.button, BTN_START) && !CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0)) || CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0) == 1) {
+ } else if ((CHECK_BTN_ALL(input->press.button, BTN_START) && (!easyPauseBufferEnabled || !easyPauseBufferTimer)) ||
+ (easyPauseBufferEnabled && easyPauseBufferTimer == 1)) { // Force Kaleido open when easy pause buffer reaches 0
+ // Remember last held buttons for pause buffer cheat (minus start so easy frame advance works)
+ CVarSetInteger("gCheatEasyPauseBufferLastInputs", input->cur.button & ~(BTN_START));
+
gSaveContext.unk_13EE = gSaveContext.unk_13EA;
if (CHECK_BTN_ALL(input->cur.button, BTN_L))
diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c
index a124ff5ce..9ac51adab 100644
--- a/soh/src/code/z_parameter.c
+++ b/soh/src/code/z_parameter.c
@@ -6442,26 +6442,24 @@ void Interface_Update(PlayState* play) {
gSaveContext.rupees--;
Audio_PlaySoundGeneral(NA_SE_SY_RUPY_COUNT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
}
- if (gSaveContext.rupeeAccumulator == 0) {
- if (gSaveContext.pendingSale != ITEM_NONE) {
- u16 tempSaleItem = gSaveContext.pendingSale;
- u16 tempSaleMod = gSaveContext.pendingSaleMod;
- gSaveContext.pendingSale = ITEM_NONE;
- gSaveContext.pendingSaleMod = MOD_NONE;
- if (tempSaleMod == MOD_NONE) {
- s16 giid = GetGIID(tempSaleItem);
- if (giid == -1) {
- tempSaleMod = MOD_RANDOMIZER;
- } else {
- tempSaleItem = giid;
- }
- }
- GameInteractor_ExecuteOnSaleEndHooks(ItemTable_RetrieveEntry(tempSaleMod, tempSaleItem));
- }
- }
} else {
gSaveContext.rupeeAccumulator = 0;
}
+ if (gSaveContext.rupeeAccumulator == 0 && gSaveContext.pendingSale != ITEM_NONE) {
+ u16 tempSaleItem = gSaveContext.pendingSale;
+ u16 tempSaleMod = gSaveContext.pendingSaleMod;
+ gSaveContext.pendingSale = ITEM_NONE;
+ gSaveContext.pendingSaleMod = MOD_NONE;
+ if (tempSaleMod == MOD_NONE) {
+ s16 giid = GetGIID(tempSaleItem);
+ if (giid == -1) {
+ tempSaleMod = MOD_RANDOMIZER;
+ } else {
+ tempSaleItem = giid;
+ }
+ }
+ GameInteractor_ExecuteOnSaleEndHooks(ItemTable_RetrieveEntry(tempSaleMod, tempSaleItem));
+ }
}
switch (interfaceCtx->unk_1EC) {
diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c
index 352ae03cd..dc5b890ec 100644
--- a/soh/src/code/z_play.c
+++ b/soh/src/code/z_play.c
@@ -1761,12 +1761,11 @@ time_t Play_GetRealTime() {
void Play_Main(GameState* thisx) {
PlayState* play = (PlayState*)thisx;
- if (CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0)) {
- CVarSetInteger("gCheatEasyPauseBufferFrameAdvance", CVarGetInteger("gCheatEasyPauseBufferFrameAdvance", 0) - 1);
- }
- if (CVarGetInteger("gPauseBufferBlockInputFrame", 0)) {
- CVarSetInteger("gPauseBufferBlockInputFrame", CVarGetInteger("gPauseBufferBlockInputFrame", 0) - 1);
+ // Decrease the easy pause buffer timer every frame
+ if (CVarGetInteger("gCheatEasyPauseBufferTimer", 0) > 0) {
+ CVarSetInteger("gCheatEasyPauseBufferTimer", CVarGetInteger("gCheatEasyPauseBufferTimer", 0) - 1);
}
+
if (play->envCtx.unk_EE[2] == 0 && CVarGetInteger("gLetItSnow", 0)) {
play->envCtx.unk_EE[3] = 64;
Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_OBJECT_KANKYO, 0, 0, 0, 0, 0, 0, 3, 0);
diff --git a/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c
index f5733a3f8..21891b219 100644
--- a/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c
+++ b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c
@@ -174,11 +174,20 @@ void EnDs_OfferOddPotion(EnDs* this, PlayState* play) {
}
}
+u8 EnDs_RandoCanGetGrannyItem() {
+ return gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
+ !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP) &&
+ // Traded odd mushroom when adult trade is on
+ ((Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) && Flags_GetItemGetInf(ITEMGETINF_30)) ||
+ // Found claim check when adult trade is off
+ (!Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) &&
+ INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK));
+}
+
s32 EnDs_CheckRupeesAndBottle() {
if (gSaveContext.rupees < 100) {
return 0;
- } else if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
- !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP)) {
+ } else if (EnDs_RandoCanGetGrannyItem()) { // Allow buying the rando item regardless of having a bottle
return 2;
} else if (Inventory_HasEmptyBottle() == 0) {
return 1;
@@ -189,18 +198,14 @@ s32 EnDs_CheckRupeesAndBottle() {
void EnDs_GiveBluePotion(EnDs* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) {
- if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
- (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK) &&
- !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP)) {
+ if (EnDs_RandoCanGetGrannyItem()) {
Flags_SetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP);
}
this->actor.parent = NULL;
this->actionFunc = EnDs_Talk;
} else {
- if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
- (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK) &&
- !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP)) {
+ if (EnDs_RandoCanGetGrannyItem()) {
GetItemEntry entry = Randomizer_GetItemFromKnownCheck(RC_KAK_GRANNYS_SHOP, GI_POTION_BLUE);
GiveItemEntryFromActor(&this->actor, play, entry, 10000.0f, 50.0f);
} else {
@@ -226,9 +231,7 @@ void EnDs_OfferBluePotion(EnDs* this, PlayState* play) {
this->actor.flags &= ~ACTOR_FLAG_WILL_TALK;
GetItemEntry itemEntry;
- if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
- (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK) &&
- !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP)) {
+ if (EnDs_RandoCanGetGrannyItem()) {
itemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_GRANNYS_SHOP, GI_POTION_BLUE);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f);
} else {
@@ -258,8 +261,10 @@ void EnDs_Wait(EnDs* this, PlayState* play) {
Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
player->actor.textId = 0x504A;
this->actionFunc = EnDs_OfferOddPotion;
- } else if ((gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_OFF) ||
- Flags_GetItemGetInf(ITEMGETINF_30)) {
+ } else if (
+ // Always offer blue potion when adult trade is off
+ (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_OFF) ||
+ Flags_GetItemGetInf(ITEMGETINF_30)) { // Traded odd mushroom
player->actor.textId = 0x500C;
this->actionFunc = EnDs_OfferBluePotion;
} else {
diff --git a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c
index c0986aa7f..863288634 100644
--- a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c
+++ b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c
@@ -334,7 +334,12 @@ void func_80A56B40(EnHeishi4* this, PlayState* play) {
return;
}
if (this->type == HEISHI4_AT_MARKET_NIGHT) {
- if (CVarGetInteger("gMarketSneak", 0)) {
+ Player* player = GET_PLAYER(play);
+ // Only allow sneaking when not wearing a mask as that triggers different dialogue. MM Bunny hood disables
+ // these interactions, so bunny hood is fine in that case.
+ if (CVarGetInteger("gMarketSneak", 0) &&
+ (player->currentMask == PLAYER_MASK_NONE ||
+ (player->currentMask == PLAYER_MASK_BUNNY && CVarGetInteger("gMMBunnyHood", 0)))) {
this->actionFunc = EnHeishi4_MarketSneak;
} else {
this->actionFunc = func_80A56614;
diff --git a/soh/src/overlays/actors/ovl_En_Js/z_en_js.c b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c
index 9b558f3fc..30ed425f4 100644
--- a/soh/src/overlays/actors/ovl_En_Js/z_en_js.c
+++ b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c
@@ -136,7 +136,7 @@ void func_80A89160(EnJs* this, PlayState* play) {
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_WASTELAND_BOMBCHU_SALESMAN, GI_BOMBCHUS_10);
gSaveContext.pendingSale = itemEntry.itemId;
gSaveContext.pendingSaleMod = itemEntry.modIndex;
- GiveItemEntryFromActor(&this->actor, play, itemEntry, 90.0f, 10.0f);
+ GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f);
Flags_SetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN);
} else {
GetItemEntry itemEntry = ItemTable_Retrieve(GI_BOMBCHUS_10);
diff --git a/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c
index 34b19aa60..b009212d9 100644
--- a/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c
+++ b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c
@@ -401,8 +401,8 @@ void func_80ABA9B8(EnNiwLady* this, PlayState* play) {
if (!gSaveContext.n64ddFlag) {
func_8002F434(&this->actor, play, GI_POCKET_EGG, 200.0f, 100.0f);
} else {
- // TODO: get-item-rework Adult trade sequence
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_ANJU_AS_ADULT, GI_POCKET_EGG);
+ GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f);
Flags_SetItemGetInf(ITEMGETINF_2C);
}
@@ -436,9 +436,9 @@ void func_80ABAB08(EnNiwLady* this, PlayState* play) {
if (!gSaveContext.n64ddFlag) {
func_8002F434(&this->actor, play, GI_COJIRO, 200.0f, 100.0f);
} else {
- // TODO: get-item-rework Adult trade sequence
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_TRADE_POCKET_CUCCO, GI_COJIRO);
Randomizer_ConsumeAdultTradeItem(play, ITEM_POCKET_CUCCO);
+ GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f);
Flags_SetItemGetInf(ITEMGETINF_2E);
}
this->actionFunc = func_80ABAC00;
@@ -462,21 +462,17 @@ void func_80ABAC00(EnNiwLady* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) {
this->actionFunc = func_80ABAC84;
} else {
+ if (gSaveContext.n64ddFlag) {
+ getItemId = this->getItemEntry.getItemId;
+ GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f);
+ return;
+ }
+
getItemId = this->getItemId;
if (LINK_IS_ADULT) {
- if (!gSaveContext.n64ddFlag) {
- getItemId = !Flags_GetItemGetInf(ITEMGETINF_2C) ? GI_POCKET_EGG : GI_COJIRO;
- } else {
- // TODO: get-item-rework Adult trade sequence
- getItemId = this->getItemEntry.getItemId;
- GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f);
- // Skip setting item flags because that was done earlier
- this->actionFunc = func_80ABA778;
- }
- }
- if (this->getItemEntry.getItemId == GI_NONE) {
- func_8002F434(&this->actor, play, getItemId, 200.0f, 100.0f);
+ getItemId = !Flags_GetItemGetInf(ITEMGETINF_2C) ? GI_POCKET_EGG : GI_COJIRO;
}
+ func_8002F434(&this->actor, play, getItemId, 200.0f, 100.0f);
}
}
@@ -486,10 +482,13 @@ void func_80ABAC84(EnNiwLady* this, PlayState* play) {
}
osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST);
if (LINK_IS_ADULT) {
- if (!Flags_GetItemGetInf(ITEMGETINF_2C)) {
- Flags_SetItemGetInf(ITEMGETINF_2C);
- } else {
- Flags_SetItemGetInf(ITEMGETINF_2E);
+ // Flags for randomizer gives are set in the original message prompt choice handling
+ if (!gSaveContext.n64ddFlag) {
+ if (!Flags_GetItemGetInf(ITEMGETINF_2C)) {
+ Flags_SetItemGetInf(ITEMGETINF_2C);
+ } else {
+ Flags_SetItemGetInf(ITEMGETINF_2E);
+ }
}
this->actionFunc = func_80ABA778;
} else {
diff --git a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c
index d7bd610a2..120a924ff 100644
--- a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c
+++ b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c
@@ -591,6 +591,10 @@ void EnTorch2_Update(Actor* thisx, PlayState* play2) {
input->prev.button = input->cur.button & (u16) ~(BTN_A | BTN_B);
PadUtils_UpdateRelXY(input);
+ if (CVarGetInteger("gMirroredWorld", 0)) {
+ input->rel.stick_x *= -1;
+ }
+
input->press.stick_x += (s8)(input->cur.stick_x - input->prev.stick_x);
input->press.stick_y += (s8)(input->cur.stick_y - input->prev.stick_y);
diff --git a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c
index 1b266f9eb..627d28c17 100644
--- a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c
+++ b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c
@@ -291,7 +291,10 @@ void func_80B20768(EnToryo* this, PlayState* play) {
s16 sp32;
s16 sp30;
- if (this->unk_1E4 == 3) {
+ // Animation Count should be no more than 1 to guarantee putaway is complete after giving the saw
+ // As this is vanilla behavior, it only applies with the Fix toggle or Skip Text enabled.
+ bool checkAnim = (CVarGetInteger("gFixSawSoftlock", 0) != 0 || CVarGetInteger("gSkipText", 0) != 0) ? play->animationCtx.animationCount <= 1 : true;
+ if (this->unk_1E4 == 3 && checkAnim) {
Actor_ProcessTalkRequest(&this->actor, play);
Message_ContinueTextbox(play, this->actor.textId);
this->unk_1E4 = 1;
diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
index d334b6dd2..f36b5d669 100644
--- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
+++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c
@@ -353,7 +353,11 @@ void FileChoose_UpdateRandomizer() {
func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_HORSE, 0, 7, 1);
return;
} else if (CVarGetInteger("gRandoGenerating", 0) == 0 && generating) {
- Audio_PlayFanfare(NA_BGM_HORSE_GOAL);
+ if (SpoilerFileExists(CVarGetString("gSpoilerLog", ""))) {
+ Audio_PlayFanfare(NA_BGM_HORSE_GOAL);
+ } else {
+ func_80078884(NA_SE_SY_OCARINA_ERROR);
+ }
func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_FILE_SELECT, 0, 7, 1);
generating = 0;
return;
diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c
index dba736f8e..44cb67987 100644
--- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c
+++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c
@@ -911,7 +911,13 @@ void FileChoose_DrawOptionsImpl(GameState* thisx) {
}
}
- if (gSaveContext.language == LANGUAGE_GER) {
+ uint8_t versionIndex = ResourceMgr_GameHasMasterQuest() && ResourceMgr_GameHasOriginal();
+ uint8_t isPalN64 = ResourceMgr_GetGameRegion(versionIndex) == GAME_REGION_PAL &&
+ ResourceMgr_GetGamePlatform(versionIndex) == GAME_PLATFORM_N64;
+ uint8_t isPalGC = ResourceMgr_GetGameRegion(versionIndex) == GAME_REGION_PAL &&
+ ResourceMgr_GetGamePlatform(versionIndex) == GAME_PLATFORM_GC;
+
+ if (gSaveContext.language == LANGUAGE_GER && isPalGC) {
gSPVertex(POLY_OPA_DISP++, D_80811E30, 32, 0);
} else {
gSPVertex(POLY_OPA_DISP++, D_80811D30, 32, 0);
@@ -928,13 +934,24 @@ void FileChoose_DrawOptionsImpl(GameState* thisx) {
G_IM_SIZ_8b, gOptionsMenuHeaders[i].width[gSaveContext.language],
gOptionsMenuHeaders[i].height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP,
G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
- gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0);
+
+ if (i == 2 && gSaveContext.language == LANGUAGE_GER && isPalN64) {
+ // Pal N64 German vertex for Z target header are offset by 12 vertices
+ gSP1Quadrangle(POLY_OPA_DISP++, vtx + 12, vtx + 2 + 12, vtx + 3 + 12, vtx + 1 + 12, 0);
+ } else {
+ gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0);
+ }
}
- if (gSaveContext.language == LANGUAGE_GER) {
+ if (gSaveContext.language == LANGUAGE_GER && isPalGC) {
gSPVertex(POLY_OPA_DISP++, D_80812130, 32, 0);
} else {
- gSPVertex(POLY_OPA_DISP++, D_80811F30, 32, 0);
+ // PAL N64 has extra german vertices combined in the regular array instead of a dedicated array
+ if (isPalN64) {
+ gSPVertex(POLY_OPA_DISP++, D_80811F30, 40, 0);
+ } else {
+ gSPVertex(POLY_OPA_DISP++, D_80811F30, 32, 0);
+ }
}
for (i = 0, vtx = 0; i < 4; i++, vtx += 4) {
@@ -981,7 +998,17 @@ void FileChoose_DrawOptionsImpl(GameState* thisx) {
G_IM_SIZ_8b, gOptionsMenuSettings[i].width[gSaveContext.language],
gOptionsMenuSettings[i].height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP,
G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
- gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0);
+ // Pal N64 German vertices for z target options are offset an by 8
+ if (gSaveContext.language == LANGUAGE_GER && isPalN64) {
+ gSP1Quadrangle(POLY_OPA_DISP++, vtx + 8, vtx + 2 + 8, vtx + 3 + 8, vtx + 1 + 8, 0);
+ } else {
+ gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0);
+ }
+ }
+
+ // Pal N64 needs to skip over the extra german vertices to get to brightness vertices
+ if (isPalN64) {
+ vtx += 8;
}
gDPPipeSync(POLY_OPA_DISP++);
diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c
index 4b8a80f9e..fecf4627a 100644
--- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c
+++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c
@@ -3653,10 +3653,8 @@ void KaleidoScope_Update(PlayState* play)
if (CHECK_BTN_ALL(input->press.button, BTN_START) ||
(CHECK_BTN_ALL(input->press.button, BTN_B) && gSaveContext.isBossRush)) {
if (CVarGetInteger("gCheatEasyPauseBufferEnabled", 0) || CVarGetInteger("gCheatEasyInputBufferingEnabled", 0)) {
- CVarSetInteger("gPauseBufferBlockInputFrame", 9);
- }
- if (CVarGetInteger("gCheatEasyPauseBufferEnabled", 0)) {
- CVarSetInteger("gCheatEasyPauseBufferFrameAdvance", 13);
+ // Easy pause buffer is 13 frames, 12 for kaledio to end, and one more to advance a single frame
+ CVarSetInteger("gCheatEasyPauseBufferTimer", 13);
}
Interface_SetDoAction(play, DO_ACTION_NONE);
pauseCtx->state = 0x12;