diff --git a/BUILDING.md b/BUILDING.md index 7606032ba..f68794783 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -2,9 +2,27 @@ ## Windows -1. Requires Visual Studio 2022 Community Edition && `python3, cmake, git` (can be installed via chocolatey or manually) -2. Clone the Ship of Harkinian repository -3. Place one or more [compatible](#compatible-roms) roms in the `OTRExporter` directory with namings of your choice +Requires: + * At least 8GB of RAM (machines with 4GB have seen complier failures) + * Visual Studio 2022 Community Edition with the C++ feature set + * One of the Windows SDKs that comes with Visual Studio, for example the current Windows 10 version 10.0.19041.0 + * The `MSVC v142 - VS 2019 C++ build tools` component of Visual Studio + * Python 3 (can be installed manually or as part of Visual Studio) + * Git (can be installed manually or as part of Visual Studio) + * Cmake (can be installed via chocolatey or manually) + +During installation, check the "Desktop development with C++" feature set: + +![image](https://user-images.githubusercontent.com/30329717/183511274-d11aceea-7900-46ec-acb6-3f2cc110021a.png) +Doing so should also check one of the Windows SDKs by default. Then, in the installation details in the right-hand column, make sure you also check the v142 toolset. + +You can also find the v142 toolset by searching through the individual components tab: + +![image](https://user-images.githubusercontent.com/30329717/183521169-ead6a73b-a1bf-4e99-aab8-441746d8f08e.png) +While you're there, you can also install Python 3 and Git if needed. + +1. Clone the Ship of Harkinian repository +2. Place one or more [compatible](#compatible-roms) roms in the `OTRExporter` directory with namings of your choice _Note: Instructions assume using powershell_ ```powershell @@ -30,8 +48,10 @@ With the cmake build system you have two options for working on the project: #### Visual Studio To develop using Visual Studio you only need to use cmake to generate the solution file: ```powershell -# Generates Ship.sln at `build/x64` +# Generates Ship.sln at `build/x64` for Visual Studio 2022 & 'C:\Program Files\CMake\bin\cmake' -S . -B "build/x64" -G "Visual Studio 17 2022" -T v142 -A x64 +# or for Visual Studio 2019 +& 'C:\Program Files\CMake\bin\cmake' -S . -B "build/x64" -G "Visual Studio 16 2019" -T v142 -A x64 ``` #### Visual Studio Code or another editor @@ -53,7 +73,7 @@ cd "build/x64" ``` ## Linux -1. Requires `gcc >= 10, x11, curl, python3, sdl2 >= 2.0.22, libpng, glew >= 2.2, ninja, cmake, lld` +Requires `gcc >= 10, x11, curl, python3, sdl2 >= 2.0.22, libpng, glew >= 2.2, ninja, cmake, lld` **Important: For maximum performance make sure you have ninja build tools installed!** @@ -91,7 +111,7 @@ cpack -G External (creates appimage) ``` ## macOS -1. Requires Xcode (or xcode-tools) && `sdl2, libpng, glew, ninja, cmake` (can be installed via homebrew, macports, etc) +Requires Xcode (or xcode-tools) && `sdl2, libpng, glew, ninja, cmake` (can be installed via homebrew, macports, etc) **Important: For maximum performance make sure you have ninja build tools installed!** diff --git a/CMake/genscript.cmake b/CMake/genscript.cmake new file mode 100644 index 000000000..10089a4fd --- /dev/null +++ b/CMake/genscript.cmake @@ -0,0 +1,5 @@ +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/extract_assets.py filedata) +string(REGEX REPLACE "zapd_exe = .*exec_cmd =" "zapd_exe = \"${program}\"\n exec_cmd =" filedata "${filedata}") +file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/extract_assets_cmake2.py" "${filedata}") +file(CHMOD "${CMAKE_CURRENT_SOURCE_DIR}/extract_assets_cmake2.py" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + diff --git a/CMakeLists.txt b/CMakeLists.txt index fb31fc818..ef21271f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,9 @@ add_compile_options($<$:/MP>) if (CMAKE_SYSTEM_NAME STREQUAL "Windows") include(cmake/automate-vcpkg.cmake) +set(VCPKG_TRIPLET x64-windows-static) +set(VCPKG_TARGET_TRIPLET x64-windows-static) + vcpkg_bootstrap() vcpkg_install_packages(zlib bzip2 libpng) endif() @@ -42,11 +45,16 @@ endif() ################################################################################ # Global configuration types ################################################################################ -set(CMAKE_CONFIGURATION_TYPES - "Debug" - "Release" - CACHE STRING "" FORCE -) +if (CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") +set(CMAKE_C_FLAGS_DEBUG "-O3 -ffast-math") +set(CMAKE_CXX_FLAGS_DEBUG "-O3 -ffast-math") +set(CMAKE_C_FLAGS_RELEASE "-O3 -ffast-math -DNDEBUG") +set(CMAKE_CXX_FLAGS_RELEASE "-O3 -ffast-math -DNDEBUG") +else() +set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG") +set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") +set(CMAKE_OBJCXX_FLAGS_RELEASE "-O2 -DNDEBUG") +endif() if(NOT CMAKE_BUILD_TYPE ) set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build." FORCE) @@ -126,25 +134,19 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") INSTALL(PROGRAMS "${CMAKE_SOURCE_DIR}/scripts/linux/appimage/soh.sh" DESTINATION . COMPONENT appimage) endif() -file(READ ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets.py filedata) -string(REGEX REPLACE "../ZAPDTR/ZAPD.out" "${CMAKE_BINARY_DIR}/ZAPD/ZAPD.out" filedata "${filedata}") -string(REGEX REPLACE "x64" "..\\\\\\\\x64" filedata "${filedata}") -string(REGEX REPLACE "Release" "${CMAKE_BUILD_TYPE}" filedata "${filedata}") -file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py" "${filedata}") -file(CHMOD "${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - find_package(Python3 COMPONENTS Interpreter) add_custom_target( ExtractAssets + COMMAND ${CMAKE_COMMAND} -Dprogram=$ -P ${CMAKE_SOURCE_DIR}/CMake/genscript.cmake COMMAND ${CMAKE_COMMAND} -E rm -f oot.otr - COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${CMAKE_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${CMAKE_BINARY_DIR}/soh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter COMMENT "Running asset extraction..." DEPENDS ZAPD - + BYPRODUCTS oot.otr ${CMAKE_SOURCE_DIR}/oot.otr ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py ) if(CMAKE_SYSTEM_NAME MATCHES "Linux") diff --git a/Jenkinsfile b/Jenkinsfile index a15b2ac12..80df7b0cc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,12 +8,35 @@ pipeline { } stages { + stage('Generate Assets') { + options { + timeout(time: 10) + } + agent { + label "SoH-Mac-Builders" + } + steps { + checkout([ + $class: 'GitSCM', + branches: scm.branches, + doGenerateSubmoduleConfigurations: scm.doGenerateSubmoduleConfigurations, + extensions: scm.extensions, + userRemoteConfigs: scm.userRemoteConfigs + ]) + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + sh ''' + cp ../../ZELOOTD.z64 OTRExporter/baserom_non_mq.z64 + + cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release + cmake --build build-cmake --target ExtractAssets --config Release + ''' + stash includes: 'soh/assets/**/*', name: 'assets' + } + } + } stage('Build SoH') { parallel { stage ('Build Windows') { - options { - timeout(time: 20) - } environment { PLATFORM='x64' PYTHON='C:\\Users\\jenkins\\AppData\\Local\\Programs\\Python\\Python310\\python.exe' @@ -34,12 +57,9 @@ pipeline { ]) catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { - bat """ - - xcopy "..\\..\\ZELOOTD.z64" "OTRExporter\\" - + unstash 'assets' + bat """ "${env.CMAKE}" -S . -B "build\\${env.PLATFORM}" -G "Visual Studio 17 2022" -T ${env.TOOLSET} -A ${env.PLATFORM} -D Python_EXECUTABLE=${env.PYTHON} -D CMAKE_BUILD_TYPE:STRING=Release - "${env.CMAKE}" --build ".\\build\\${env.PLATFORM}" --target ExtractAssets --config Release "${env.CMAKE}" --build ".\\build\\${env.PLATFORM}" --config Release cd ".\\build\\${env.PLATFORM}" "${env.CPACK}" -G ZIP @@ -47,8 +67,8 @@ pipeline { move "_packages\\*.zip" "soh.zip" """ - archiveArtifacts artifacts: 'soh.zip', followSymlinks: false, onlyIfSuccessful: true } + archiveArtifacts artifacts: 'soh.zip', followSymlinks: false, onlyIfSuccessful: true } post { always { @@ -57,9 +77,6 @@ pipeline { } } stage ('Build Linux') { - options { - timeout(time: 20) - } agent { label "SoH-Linux-Builders" } @@ -72,9 +89,9 @@ pipeline { userRemoteConfigs: scm.userRemoteConfigs ]) catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + unstash 'assets' sh ''' - - cp ../../ZELOOTD.z64 OTRExporter/baserom_non_mq.z64 + if docker ps -aq --filter "name=sohcont" | grep -q .; then docker rm -f sohcont; fi docker build . -t soh docker run --name sohcont -dit --rm -v $(pwd):/soh soh /bin/bash docker exec sohcont scripts/linux/appimage/build.sh @@ -86,11 +103,12 @@ pipeline { ''' } - sh 'sudo docker container stop sohcont' archiveArtifacts artifacts: 'soh-linux.7z', followSymlinks: false, onlyIfSuccessful: true } post { always { + sh 'sudo docker container stop sohcont' + sh 'docker images --quiet --filter=dangling=true | xargs --no-run-if-empty docker rmi' // Clean dangling docker images step([$class: 'WsCleanup']) // Clean workspace } } @@ -108,11 +126,9 @@ pipeline { userRemoteConfigs: scm.userRemoteConfigs ]) catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + unstash 'assets' sh ''' - cp ../../ZELOOTD.z64 OTRExporter/baserom_non_mq.z64 - cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" - cmake --build build-cmake --target ExtractAssets -- cmake --build build-cmake --config Release -- (cd build-cmake && cpack) @@ -131,9 +147,6 @@ pipeline { } } stage ('Build Switch') { - options { - timeout(time: 20) - } agent { label "SoH-Linux-Builders" } @@ -146,12 +159,12 @@ pipeline { userRemoteConfigs: scm.userRemoteConfigs ]) catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { - sh ''' - - cp ../../ZELOOTD.z64 OTRExporter/baserom_non_mq.z64 + unstash 'assets' + sh ''' + if docker ps -aq --filter "name=sohswitchcont" | grep -q .; then docker rm -f sohswitchcont; fi docker build . -t sohswitch - docker run --name sohcont -dit --rm -v $(pwd):/soh sohswitch /bin/bash - docker exec sohcont scripts/switch/build.sh + docker run --name sohswitchcont -dit --rm -v $(pwd):/soh sohswitch /bin/bash + docker exec sohswitchcont scripts/switch/build.sh mv build-switch/soh/*.nro soh.nro mv README.md readme.txt @@ -160,11 +173,12 @@ pipeline { ''' } - sh 'sudo docker container stop sohcont' archiveArtifacts artifacts: 'soh-switch.7z', followSymlinks: false, onlyIfSuccessful: true } post { always { + sh 'sudo docker container stop sohswitchcont' + sh 'docker images --quiet --filter=dangling=true | xargs --no-run-if-empty docker rmi' // Clean dangling docker images step([$class: 'WsCleanup']) // Clean workspace } } diff --git a/ZAPDTR/ZAPD/ZResource.cpp b/ZAPDTR/ZAPD/ZResource.cpp index 89238d9a0..7536fa019 100644 --- a/ZAPDTR/ZAPD/ZResource.cpp +++ b/ZAPDTR/ZAPD/ZResource.cpp @@ -348,11 +348,14 @@ std::string ZResource::GetSourceOutputHeader([[maybe_unused]] const std::string& str += StringHelper::Sprintf("#define d%s \"__OTR__%s/%s\"", name.c_str(), outName.c_str(), nameStr.c_str()); if (nameSet && nameSet->find(name) == nameSet->end()) { + str += StringHelper::Sprintf(R"( #ifdef _WIN32 - str += StringHelper::Sprintf("\nstatic const __declspec(align(2)) char %s[] = d%s;", name.c_str(), name.c_str()); +static const __declspec(align(2)) char %s[] = d%s; #else - str += StringHelper::Sprintf("\nstatic const char %s[] __attribute__((aligned (2))) = d%s;", name.c_str(), name.c_str()); +static const char %s[] __attribute__((aligned (2))) = d%s; #endif + )", name.c_str(), name.c_str(), name.c_str(), name.c_str()); + if (nameSet) { nameSet->insert(name); } diff --git a/libultraship/libultraship/Console.cpp b/libultraship/libultraship/Console.cpp index b7158bd3b..83737e738 100644 --- a/libultraship/libultraship/Console.cpp +++ b/libultraship/libultraship/Console.cpp @@ -100,11 +100,15 @@ namespace Ship { } void Console::Draw() { + if (!this->opened) { + CVar_SetS32("gConsoleEnabled", 0); + return; + } + bool input_focus = false; - if (!this->opened) return; ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); - ImGui::Begin("Console", nullptr, ImGuiWindowFlags_NoFocusOnAppearing); + ImGui::Begin("Console", &this->opened, ImGuiWindowFlags_NoFocusOnAppearing); const ImVec2 pos = ImGui::GetWindowPos(); const ImVec2 size = ImGui::GetWindowSize(); // SohImGui::ShowCursor(ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows | ImGuiHoveredFlags_RectOnly), SohImGui::Dialogues::dConsole); diff --git a/libultraship/libultraship/ControlDeck.cpp b/libultraship/libultraship/ControlDeck.cpp index 31eb90067..7437afeee 100644 --- a/libultraship/libultraship/ControlDeck.cpp +++ b/libultraship/libultraship/ControlDeck.cpp @@ -6,6 +6,7 @@ #include "KeyboardController.h" #include "SDLController.h" #include +#include "Cvar.h" namespace Ship { uint8_t* controllerBits; @@ -51,18 +52,31 @@ namespace Ship { *controllerBits |= (backend->Connected()) << slot; } - void ControlDeck::WriteToPad(OSContPad* pad) const { - for (size_t i = 0; i < virtualDevices.size(); i++) { - const std::shared_ptr backend = physicalDevices[virtualDevices[i]]; - if (backend->GetGuid() == "Auto") { - for (const auto& device : physicalDevices) { - device->Read(&pad[i], i); - } - continue; - } - backend->Read(&pad[i], i); - } - } + void ControlDeck::WriteToPad(OSContPad* pad) const { + + #ifdef __SWITCH__ + bool shouldBlockGameInput = CVar_GetS32("gOpenMenuBar", 0); + #else + bool shouldBlockGameInput = CVar_GetS32("gOpenMenuBar", 0) && CVar_GetS32("gControlNav", 0); + #endif + + for (size_t i = 0; i < virtualDevices.size(); i++) { + const std::shared_ptr backend = physicalDevices[virtualDevices[i]]; + if (backend->GetGuid() == "Auto") { + for (const auto& device : physicalDevices) { + if(shouldBlockGameInput && device->GetGuid() != "Keyboard") { + continue; + } + device->Read(&pad[i], i); + } + continue; + } + if(shouldBlockGameInput && backend->GetGuid() != "Keyboard") { + continue; + } + backend->Read(&pad[i], i); + } + } #define NESTED(key, ...) StringHelper::Sprintf("Controllers.%s.Slot_%d." key, device->GetGuid().c_str(), slot, __VA_ARGS__) diff --git a/libultraship/libultraship/ImGuiImpl.cpp b/libultraship/libultraship/ImGuiImpl.cpp index 79af829e3..93f30455c 100644 --- a/libultraship/libultraship/ImGuiImpl.cpp +++ b/libultraship/libultraship/ImGuiImpl.cpp @@ -60,9 +60,10 @@ bool oldCursorState = true; #define EXPERIMENTAL() \ ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 50, 50, 255)); \ + InsertPadding(3.0f); \ ImGui::Text("Experimental"); \ ImGui::PopStyleColor(); \ - ImGui::Separator(); + PaddedSeparator(false, true); #define TOGGLE_BTN ImGuiKey_F1 #define TOGGLE_PAD_BTN ImGuiKey_GamepadBack #define HOOK(b) if(b) needs_save = true; @@ -97,6 +98,7 @@ namespace SohImGui { bool p_open = false; bool needs_save = false; int lastBackendID = 0; + bool statsWindowOpen; const char* filters[3] = { "Three-Point", @@ -384,6 +386,7 @@ namespace SohImGui { io = &ImGui::GetIO(); io->ConfigFlags |= ImGuiConfigFlags_DockingEnable; io->Fonts->AddFontDefault(); + statsWindowOpen = CVar_GetS32("gStatsEnabled", 0); #ifdef __SWITCH__ Ship::Switch::SetupFont(io->Fonts); #endif @@ -592,31 +595,43 @@ namespace SohImGui { else ImGui::Text(text, static_cast(100 * val)); + InsertPadding(); + if(PlusMinusButton) { std::string MinusBTNName = " - ##"; MinusBTNName += cvarName; if (ImGui::Button(MinusBTNName.c_str())) { - val -= 0.1f; + if (!isPercentage) + val -= 0.1f; + else + val -= 0.01f; CVar_SetFloat(cvarName, val); needs_save = true; } ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); } - + if (PlusMinusButton) { + ImGui::PushItemWidth(ImGui::GetWindowSize().x - 79.0f); + } if (ImGui::SliderFloat(id, &val, min, max, format)) { CVar_SetFloat(cvarName, val); needs_save = true; } - + if (PlusMinusButton) { + ImGui::PopItemWidth(); + } if(PlusMinusButton) { std::string PlusBTNName = " + ##"; PlusBTNName += cvarName; ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); if (ImGui::Button(PlusBTNName.c_str())) { - val += 0.1f; + if (!isPercentage) + val += 0.1f; + else + val += 0.01f; CVar_SetFloat(cvarName, val); needs_save = true; } @@ -822,7 +837,12 @@ namespace SohImGui { GlobalCtx2::GetInstance()->GetWindow()->SetMenuBar(menu_bar); ShowCursor(menu_bar, Dialogues::dMenubar); GlobalCtx2::GetInstance()->GetWindow()->GetControlDeck()->SaveControllerSettings(); - if (CVar_GetS32("gControlNav", 0)) { + #ifdef __SWITCH__ + bool enableControllerNavigation = true; + #else + bool enableControllerNavigation = CVar_GetS32("gControlNav", 0); + #endif + if (enableControllerNavigation) { if (CVar_GetS32("gOpenMenuBar", 0)) { io->ConfigFlags |=ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_NavEnableKeyboard; } else { @@ -860,6 +880,9 @@ namespace SohImGui { ImGui::SetCursorPos(ImVec2(25, 0)); } + static ImVec2 windowPadding(8.0f, 8.0f); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, windowPadding); if (ImGui::BeginMenu("Shipwright")) { if (ImGui::MenuItem("Reset", #if __APPLE__ @@ -873,134 +896,145 @@ namespace SohImGui { ImGui::EndMenu(); } - ImGui::Separator(); - ImGui::SetCursorPosY(0.0f); - if (ImGui::BeginMenu("Audio")) { - EnhancementSliderFloat("Master Volume: %d %%", "##Master_Vol", "gGameMasterVolume", 0.0f, 1.0f, "", 1.0f, true); - - BindAudioSlider("Main Music Volume: %d %%", "gMainMusicVolume", 1.0f, SEQ_BGM_MAIN); - BindAudioSlider("Sub Music Volume: %d %%", "gSubMusicVolume", 1.0f, SEQ_BGM_SUB); - BindAudioSlider("Sound Effects Volume: %d %%", "gSFXMusicVolume", 1.0f, SEQ_SFX); - BindAudioSlider("Fanfare Volume: %d %%", "gFanfareVolume", 1.0f, SEQ_FANFARE); - - ImGui::EndMenu(); - } - - ImGui::SetCursorPosY(0.0f); - - if (ImGui::BeginMenu("Controller")) + if (ImGui::BeginMenu("Settings")) { - EnhancementCheckbox("Use Controller Navigation", "gControlNav"); - Tooltip("Allows controller navigation of the menu bar\nD-pad to move between items, A to select, and X to grab focus on the menu bar"); + if (ImGui::BeginMenu("Audio")) { + EnhancementSliderFloat("Master Volume: %d %%", "##Master_Vol", "gGameMasterVolume", 0.0f, 1.0f, "", 1.0f, true); + InsertPadding(); + BindAudioSlider("Main Music Volume: %d %%", "gMainMusicVolume", 1.0f, SEQ_BGM_MAIN); + InsertPadding(); + BindAudioSlider("Sub Music Volume: %d %%", "gSubMusicVolume", 1.0f, SEQ_BGM_SUB); + InsertPadding(); + BindAudioSlider("Sound Effects Volume: %d %%", "gSFXMusicVolume", 1.0f, SEQ_SFX); + InsertPadding(); + BindAudioSlider("Fanfare Volume: %d %%", "gFanfareVolume", 1.0f, SEQ_FANFARE); - EnhancementCheckbox("Controller Configuration", "gControllerConfigurationEnabled"); - controller->Opened = CVar_GetS32("gControllerConfigurationEnabled", 0); - - ImGui::Separator(); - - // TODO mutual exclusions -- There should be some system to prevent conclifting enhancements from being selected - EnhancementCheckbox("D-pad Support on Pause and File Select", "gDpadPauseName"); - Tooltip("Enables Pause and File Select screen navigation with the D-pad\nIf used with D-pad as Equip Items, you must hold C-Up to equip instead of navigate"); - EnhancementCheckbox("D-pad Support in Ocarina and Text Choice", "gDpadOcarinaText"); - EnhancementCheckbox("D-pad Support for Browsing Shop Items", "gDpadShop"); - EnhancementCheckbox("D-pad as Equip Items", "gDpadEquips"); - Tooltip("Allows the D-pad to be used as extra C buttons"); - EnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL"); - Tooltip("Speak to Navi with L but enter first-person camera with C-Up"); - ImGui::Separator(); - - EnhancementCheckbox("Show Inputs", "gInputEnabled"); - Tooltip("Shows currently pressed inputs on the bottom right of the screen"); - - EnhancementSliderFloat("Input Scale: %.1f", "##Input", "gInputScale", 1.0f, 3.0f, "", 1.0f, false); - Tooltip("Sets the on screen size of the displayed inputs from the Show Inputs setting"); - - ImGui::EndMenu(); - } - - ImGui::SetCursorPosY(0.0f); - - if (ImGui::BeginMenu("Graphics")) - { -#ifndef __APPLE__ - EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f, "", 1.0f, true); - Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective form of anti-aliasing"); - gfx_current_dimensions.internal_mul = CVar_GetFloat("gInternalResolution", 1); -#endif - EnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true); - Tooltip("Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel"); - gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); - - if (impl.backend == Backend::DX11) - { - const char* cvar = "gExtraLatencyThreshold"; - int val = CVar_GetS32(cvar, 80); - val = MAX(MIN(val, 360), 0); - int fps = val; - - if (fps == 0) - { - ImGui::Text("Jitter fix: Off"); - } - else - { - ImGui::Text("Jitter fix: >= %d FPS", fps); - } - - std::string MinusBTNELT = " - ##ExtraLatencyThreshold"; - std::string PlusBTNELT = " + ##ExtraLatencyThreshold"; - if (ImGui::Button(MinusBTNELT.c_str())) { - val--; - CVar_SetS32(cvar, val); - needs_save = true; - } - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - - if (ImGui::SliderInt("##ExtraLatencyThreshold", &val, 0, 360, "", ImGuiSliderFlags_AlwaysClamp)) - { - CVar_SetS32(cvar, val); - needs_save = true; - } - - Tooltip("When Interpolation FPS setting is at least this threshold, add one frame of input lag (e.g. 16.6 ms for 60 FPS) in order to avoid jitter. This setting allows the CPU to work on one frame while GPU works on the previous frame.\nThis setting should be used when your computer is too slow to do CPU + GPU work in time."); - - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - if (ImGui::Button(PlusBTNELT.c_str())) { - val++; - CVar_SetS32(cvar, val); - needs_save = true; - } + ImGui::EndMenu(); } + InsertPadding(); - ImGui::Text("Renderer API (Needs reload)"); - if (ImGui::BeginCombo("##RApi", backends[lastBackendID].second)) { - for (uint8_t i = 0; i < sizeof(backends) / sizeof(backends[0]); i++) { - if (ImGui::Selectable(backends[i].second, i == lastBackendID)) { - pConf->setString("Window.GfxBackend", backends[i].first); - lastBackendID = i; + if (ImGui::BeginMenu("Controller")) { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2 (12.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); + if (ImGui::Button(GetWindowButtonText("Controller Configuration", CVar_GetS32("gControllerConfigurationEnabled", 0)).c_str())) + { + bool currentValue = CVar_GetS32("gControllerConfigurationEnabled", 0); + CVar_SetS32("gControllerConfigurationEnabled", !currentValue); + needs_save = true; + controller->Opened = CVar_GetS32("gControllerConfigurationEnabled", 0); + } + ImGui::PopStyleColor(1); + ImGui::PopStyleVar(3); + #ifndef __SWITCH__ + PaddedEnhancementCheckbox("Use Controller Navigation", "gControlNav", true, false); + Tooltip("Allows controller navigation of the menu bar\nD-pad to move between items, A to select, and X to grab focus on the menu bar"); + #endif + PaddedEnhancementCheckbox("Show Inputs", "gInputEnabled", true, false); + Tooltip("Shows currently pressed inputs on the bottom right of the screen"); + InsertPadding(); + ImGui::PushItemWidth(ImGui::GetWindowSize().x - 20.0f); + EnhancementSliderFloat("Input Scale: %.1f", "##Input", "gInputScale", 1.0f, 3.0f, "", 1.0f, false); + Tooltip("Sets the on screen size of the displayed inputs from the Show Inputs setting"); + ImGui::PopItemWidth(); + + ImGui::EndMenu(); + } + + InsertPadding(); + + if (ImGui::BeginMenu("Graphics")) { + #ifndef __APPLE__ + EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f, "", 1.0f, true, true); + Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective form of anti-aliasing"); + gfx_current_dimensions.internal_mul = CVar_GetFloat("gInternalResolution", 1); + #endif + PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, false, true, false); + Tooltip("Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel"); + gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); + + if (impl.backend == Backend::DX11) + { + const char* cvar = "gExtraLatencyThreshold"; + int val = CVar_GetS32(cvar, 80); + val = MAX(MIN(val, 360), 0); + int fps = val; + + InsertPadding(); + + if (fps == 0) + { + ImGui::Text("Jitter fix: Off"); } + else + { + ImGui::Text("Jitter fix: >= %d FPS", fps); + } + + std::string MinusBTNELT = " - ##ExtraLatencyThreshold"; + std::string PlusBTNELT = " + ##ExtraLatencyThreshold"; + if (ImGui::Button(MinusBTNELT.c_str())) { + val--; + CVar_SetS32(cvar, val); + needs_save = true; + } + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); + ImGui::PushItemWidth(ImGui::GetWindowSize().x - 79.0f); + if (ImGui::SliderInt("##ExtraLatencyThreshold", &val, 0, 360, "", ImGuiSliderFlags_AlwaysClamp)) + { + CVar_SetS32(cvar, val); + needs_save = true; + } + ImGui::PopItemWidth(); + Tooltip("When Interpolation FPS setting is at least this threshold, add one frame of input lag (e.g. 16.6 ms for 60 FPS) in order to avoid jitter. This setting allows the CPU to work on one frame while GPU works on the previous frame.\nThis setting should be used when your computer is too slow to do CPU + GPU work in time."); + + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); + if (ImGui::Button(PlusBTNELT.c_str())) { + val++; + CVar_SetS32(cvar, val); + needs_save = true; + } + + InsertPadding(); } - ImGui::EndCombo(); + + ImGui::Text("Renderer API (Needs reload)"); + if (ImGui::BeginCombo("##RApi", backends[lastBackendID].second)) { + for (uint8_t i = 0; i < sizeof(backends) / sizeof(backends[0]); i++) { + if (ImGui::Selectable(backends[i].second, i == lastBackendID)) { + pConf->setString("Window.GfxBackend", backends[i].first); + lastBackendID = i; + } + } + ImGui::EndCombo(); + } + + EXPERIMENTAL(); + + ImGui::Text("Texture Filter (Needs reload)"); + EnhancementCombobox("gTextureFilter", filters, 3, 0); + + InsertPadding(); + + overlay->DrawSettings(); + + ImGui::EndMenu(); } - EXPERIMENTAL(); - ImGui::Text("Texture Filter (Needs reload)"); - EnhancementCombobox("gTextureFilter", filters, 3, 0); - overlay->DrawSettings(); - ImGui::EndMenu(); - } + InsertPadding(); - ImGui::SetCursorPosY(0.0f); - - if (ImGui::BeginMenu("Languages")) { - EnhancementRadioButton("English", "gLanguages", 0); - EnhancementRadioButton("German", "gLanguages", 1); - EnhancementRadioButton("French", "gLanguages", 2); + if (ImGui::BeginMenu("Languages")) { + EnhancementRadioButton("English", "gLanguages", 0); + EnhancementRadioButton("German", "gLanguages", 1); + EnhancementRadioButton("French", "gLanguages", 2); + ImGui::EndMenu(); + } ImGui::EndMenu(); } @@ -1008,84 +1042,134 @@ namespace SohImGui { if (ImGui::BeginMenu("Enhancements")) { + + const char* enhancementPresets[4] = { "Default", "Vanilla Plus", "Enhanced", "Randomizer"}; + PaddedText("Enhancement Presets", false, true); + SohImGui::EnhancementCombobox("gSelectEnhancementPresets", enhancementPresets, 4, 0); + Tooltip( + "Default - Set all enhancements to their default values. The true vanilla SoH experience.\n" + "\n" + "Vanilla Plus - Adds Quality of Life features that enhance your experience, but don't alter gameplay. Recommended for a first playthrough of OoT.\n" + "\n" + "Enhanced - The \"Vanilla Plus\" preset, but with more quality of life enhancements that might alter gameplay slightly. Recommended for returning players.\n" + "\n" + "Randomizer - The \"Enhanced\" preset, plus any other enhancements that are recommended for playing Randomizer." + ); + + InsertPadding(); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f)); + if (ImGui::Button("Apply Preset")) { + applyEnhancementPresets(); + } + ImGui::PopStyleVar(1); + + PaddedSeparator(); + + if (ImGui::BeginMenu("Controls")) { + // TODO mutual exclusions -- There should be some system to prevent conclifting enhancements from being selected + EnhancementCheckbox("D-pad Support on Pause and File Select", "gDpadPauseName"); + Tooltip("Enables Pause and File Select screen navigation with the D-pad\nIf used with D-pad as Equip Items, you must hold C-Up to equip instead of navigate"); + PaddedEnhancementCheckbox("D-pad Support in Ocarina and Text Choice", "gDpadOcarinaText", true, false); + PaddedEnhancementCheckbox("D-pad Support for Browsing Shop Items", "gDpadShop", true, false); + PaddedEnhancementCheckbox("D-pad as Equip Items", "gDpadEquips", true, false); + Tooltip("Allows the D-pad to be used as extra C buttons"); + PaddedEnhancementCheckbox("Allow the cursor to be on any slot", "gPauseAnyCursor", true, false); + Tooltip("Allows the cursor on the pause menu to be over any slot\nSimilar to Rando and Spaceworld 97"); + PaddedEnhancementCheckbox("Prevent Dropped Ocarina Inputs", "gDpadNoDropOcarinaInput", true, false); + Tooltip("Prevent dropping inputs when playing the ocarina quickly"); + PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL", true, false); + Tooltip("Speak to Navi with L but enter first-person camera with C-Up"); + ImGui::EndMenu(); + } + + InsertPadding(); + if (ImGui::BeginMenu("Gameplay")) { if (ImGui::BeginMenu("Time Savers")) { - EnhancementSliderInt("Text Speed: %dx", "##TEXTSPEED", "gTextSpeed", 1, 5, ""); - EnhancementSliderInt("King Zora Speed: %dx", "##MWEEPSPEED", "gMweepSpeed", 1, 5, ""); - EnhancementSliderInt("Biggoron Forge Time: %d days", "##FORGETIME", "gForgeTime", 0, 3, ""); + PaddedEnhancementSliderInt("Text Speed: %dx", "##TEXTSPEED", "gTextSpeed", 1, 5, "", 1, false, false, true); + PaddedEnhancementSliderInt("King Zora Speed: %dx", "##MWEEPSPEED", "gMweepSpeed", 1, 5, "", 1, false, false, true); + EnhancementSliderInt("Biggoron Forge Time: %d days", "##FORGETIME", "gForgeTime", 0, 3, "", 3); Tooltip("Allows you to change the number of days it takes for Biggoron to forge the Biggoron Sword"); - EnhancementSliderInt("Vine/Ladder Climb speed +%d", "##CLIMBSPEED", "gClimbSpeed", 0, 12, ""); - + PaddedEnhancementSliderInt("Vine/Ladder Climb speed +%d", "##CLIMBSPEED", "gClimbSpeed", 0, 12, "", 0); EnhancementCheckbox("Faster Block Push", "gFasterBlockPush"); - EnhancementCheckbox("Faster Heavy Block Lift", "gFasterHeavyBlockLift"); + PaddedEnhancementCheckbox("Faster Heavy Block Lift", "gFasterHeavyBlockLift", true, false); Tooltip("Speeds up lifting silver rocks and obelisks"); - EnhancementCheckbox("No Forced Navi", "gNoForcedNavi"); + PaddedEnhancementCheckbox("No Forced Navi", "gNoForcedNavi", true, false); Tooltip("Prevent forced Navi conversations"); - EnhancementCheckbox("No Skulltula Freeze", "gSkulltulaFreeze"); + PaddedEnhancementCheckbox("No Skulltula Freeze", "gSkulltulaFreeze", true, false); Tooltip("Stops the game from freezing the player when picking up Gold Skulltulas"); - EnhancementCheckbox("MM Bunny Hood", "gMMBunnyHood"); + PaddedEnhancementCheckbox("MM Bunny Hood", "gMMBunnyHood", true, false); Tooltip("Wearing the Bunny Hood grants a speed increase like in Majora's Mask"); - EnhancementCheckbox("Fast Chests", "gFastChests"); + PaddedEnhancementCheckbox("Fast Chests", "gFastChests", true, false); Tooltip("Kick open every chest"); - EnhancementCheckbox("Skip Pickup Messages", "gFastDrops"); + PaddedEnhancementCheckbox("Skip Pickup Messages", "gFastDrops", true, false); Tooltip("Skip pickup messages for new consumable items and bottle swipes"); - EnhancementCheckbox("Better Owl", "gBetterOwl"); + PaddedEnhancementCheckbox("Better Owl", "gBetterOwl", true, false); Tooltip("The default response to Kaepora Gaebora is always that you understood what he said"); - EnhancementCheckbox("Fast Ocarina Playback", "gFastOcarinaPlayback"); + PaddedEnhancementCheckbox("Fast Ocarina Playback", "gFastOcarinaPlayback", true, false); Tooltip("Skip the part where the Ocarina playback is called when you play a song"); - EnhancementCheckbox("Prevent Dropped Ocarina Inputs", "gDpadNoDropOcarinaInput"); - Tooltip("Prevent dropping inputs when playing the ocarina quickly"); - EnhancementCheckbox("Instant Putaway", "gInstantPutaway"); + PaddedEnhancementCheckbox("Instant Putaway", "gInstantPutaway", true, false); Tooltip("Allow Link to put items away without having to wait around"); - EnhancementCheckbox("Mask Select in Inventory", "gMaskSelect"); + PaddedEnhancementCheckbox("Instant Boomerang Recall", "gFastBoomerang", true, false); + Tooltip("Instantly return the boomerang to Link by pressing its item button while it's in the air"); + PaddedEnhancementCheckbox("Mask Select in Inventory", "gMaskSelect", true, false); Tooltip("After completing the mask trading sub-quest, press A and any direction on the mask slot to change masks"); - EnhancementCheckbox("Remember Save Location", "gRememberSaveLocation"); + PaddedEnhancementCheckbox("Remember Save Location", "gRememberSaveLocation", true, false); Tooltip("When loading a save, places Link at the last entrance he went through.\n" "This doesn't work if the save was made in a grotto."); ImGui::EndMenu(); } + InsertPadding(); + if (ImGui::BeginMenu("Difficulty Options")) { ImGui::Text("Damage Multiplier"); EnhancementCombobox("gDamageMul", powers, 9, 0); - Tooltip("Modifies all sources of damage not affected by other sliders\n\ -2x: Can survive all common attacks from the start of the game\n\ -4x: Dies in 1 hit to any substantial attack from the start of the game\n\ -8x: Can only survive trivial damage from the start of the game\n\ -16x: Can survive all common attacks with max health without double defense\n\ -32x: Can survive all common attacks with max health and double defense\n\ -64x: Can survive trivial damage with max health without double defense\n\ -128x: Can survive trivial damage with max health and double defense\n\ -256x: Cannot survive damage"); - ImGui::Text("Fall Damage Multiplier"); + Tooltip( + "Modifies all sources of damage not affected by other sliders\n\ + 2x: Can survive all common attacks from the start of the game\n\ + 4x: Dies in 1 hit to any substantial attack from the start of the game\n\ + 8x: Can only survive trivial damage from the start of the game\n\ + 16x: Can survive all common attacks with max health without double defense\n\ + 32x: Can survive all common attacks with max health and double defense\n\ + 64x: Can survive trivial damage with max health without double defense\n\ + 128x: Can survive trivial damage with max health and double defense\n\ + 256x: Cannot survive damage" + ); + PaddedText("Fall Damage Multiplier", true, false); EnhancementCombobox("gFallDamageMul", powers, 8, 0); - Tooltip("Modifies all fall damage\n\ -2x: Can survive all fall damage from the start of the game\n\ -4x: Can only survive short fall damage from the start of the game\n\ -8x: Cannot survive any fall damage from the start of the game\n\ -16x: Can survive all fall damage with max health without double defense\n\ -32x: Can survive all fall damage with max health and double defense\n\ -64x: Can survive short fall damage with double defense\n\ -128x: Cannot survive fall damage"); - ImGui::Text("Void Damage Multiplier"); + Tooltip( + "Modifies all fall damage\n\ + 2x: Can survive all fall damage from the start of the game\n\ + 4x: Can only survive short fall damage from the start of the game\n\ + 8x: Cannot survive any fall damage from the start of the game\n\ + 16x: Can survive all fall damage with max health without double defense\n\ + 32x: Can survive all fall damage with max health and double defense\n\ + 64x: Can survive short fall damage with double defense\n\ + 128x: Cannot survive fall damage" + ); + PaddedText("Void Damage Multiplier", true, false); EnhancementCombobox("gVoidDamageMul", powers, 7, 0); - Tooltip("Modifies damage taken after falling into a void\n\ -2x: Can survive void damage from the start of the game\n\ -4x: Cannot survive void damage from the start of the game\n\ -8x: Can survive void damage twice with max health without double defense\n\ -16x: Can survive void damage with max health without double defense\n\ -32x: Can survive void damage with max health and double defense\n\ -64x: Cannot survive void damage"); - - EnhancementCheckbox("No Random Drops", "gNoRandomDrops"); + Tooltip( + "Modifies damage taken after falling into a void\n\ + 2x: Can survive void damage from the start of the game\n\ + 4x: Cannot survive void damage from the start of the game\n\ + 8x: Can survive void damage twice with max health without double defense\n\ + 16x: Can survive void damage with max health without double defense\n\ + 32x: Can survive void damage with max health and double defense\n\ + 64x: Cannot survive void damage" + ); + PaddedEnhancementCheckbox("No Random Drops", "gNoRandomDrops", true, false); Tooltip("Disables random drops, except from the Goron Pot, Dampe, and bosses"); - EnhancementCheckbox("No Heart Drops", "gNoHeartDrops"); + PaddedEnhancementCheckbox("No Heart Drops", "gNoHeartDrops", true, false); Tooltip("Disables heart drops, but not heart placements, like from a Deku Scrub running off\nThis simulates Hero Mode from other games in the series"); - EnhancementCheckbox("Always Win Goron Pot", "gGoronPot"); + PaddedEnhancementCheckbox("Always Win Goron Pot", "gGoronPot", true, false); Tooltip("Always get the heart piece/purple rupee from the spinning Goron pot"); + InsertPadding(); if (ImGui::BeginMenu("Potion Values")) { @@ -1096,6 +1180,8 @@ namespace SohImGui { EnhancementCheckbox("Red Potion Percent Restore", "gRedPercentRestore"); Tooltip("Toggles from Red Potions restoring a fixed amount of health to a percent of the player's current max health"); + ImGui::Separator(); + EnhancementCheckbox("Change Green Potion Effect", "gGreenPotionEffect"); Tooltip("Enable the following changes to the amount of mana restored by Green Potions"); EnhancementSliderInt("Green Potion Mana: %d", "##GREENPOTIONMANA", "gGreenPotionMana", 1, 100, "", 0, true); @@ -1103,6 +1189,8 @@ namespace SohImGui { EnhancementCheckbox("Green Potion Percent Restore", "gGreenPercentRestore"); Tooltip("Toggles from Green Potions restoring a fixed amount of mana to a percent of the player's current max mana"); + ImGui::Separator(); + EnhancementCheckbox("Change Blue Potion Effects", "gBluePotionEffects"); Tooltip("Enable the following changes to the amount of health and mana restored by Blue Potions"); EnhancementSliderInt("Blue Potion Health: %d", "##BLUEPOTIONHEALTH", "gBluePotionHealth", 1, 100, "", 0, true); @@ -1110,11 +1198,15 @@ namespace SohImGui { EnhancementCheckbox("Blue Potion Health Percent Restore", "gBlueHealthPercentRestore"); Tooltip("Toggles from Blue Potions restoring a fixed amount of health to a percent of the player's current max health"); + ImGui::Separator(); + EnhancementSliderInt("Blue Potion Mana: %d", "##BLUEPOTIONMANA", "gBluePotionMana", 1, 100, "", 0, true); Tooltip("Changes the amount of mana restored by Blue Potions, base max mana is 48, max upgraded mana is 96"); EnhancementCheckbox("Blue Potion Mana Percent Restore", "gBlueManaPercentRestore"); Tooltip("Toggles from Blue Potions restoring a fixed amount of mana to a percent of the player's current max mana"); + ImGui::Separator(); + EnhancementCheckbox("Change Milk Effect", "gMilkEffect"); Tooltip("Enable the following changes to the amount of health restored by Milk"); EnhancementSliderInt("Milk Health: %d", "##MILKHEALTH", "gMilkHealth", 1, 100, "", 0, true); @@ -1122,12 +1214,16 @@ namespace SohImGui { EnhancementCheckbox("Milk Percent Restore", "gMilkPercentRestore"); Tooltip("Toggles from Milk restoring a fixed amount of health to a percent of the player's current max health"); + ImGui::Separator(); + EnhancementCheckbox("Separate Half Milk Effect", "gSeparateHalfMilkEffect"); Tooltip("Enable the following changes to the amount of health restored by Half Milk\nIf this is disabled, Half Milk will behave the same as Full Milk."); EnhancementSliderInt("Half Milk Health: %d", "##HALFMILKHEALTH", "gHalfMilkHealth", 1, 100, "", 0, true); Tooltip("Changes the amount of health restored by Half Milk"); EnhancementCheckbox("Half Milk Percent Restore", "gHalfMilkPercentRestore"); Tooltip("Toggles from Half Milk restoring a fixed amount of health to a percent of the player's current max health"); + + ImGui::Separator(); EnhancementCheckbox("Change Fairy Effect", "gFairyEffect"); Tooltip("Enable the following changes to the amount of health restored by Fairies"); @@ -1135,6 +1231,8 @@ namespace SohImGui { Tooltip("Changes the amount of health restored by Fairies"); EnhancementCheckbox("Fairy Percent Restore", "gFairyPercentRestore"); Tooltip("Toggles from Fairies restoring a fixed amount of health to a percent of the player's current max health"); + + ImGui::Separator(); EnhancementCheckbox("Change Fairy Revive Effect", "gFairyReviveEffect"); Tooltip("Enable the following changes to the amount of health restored by Fairy Revivals"); @@ -1146,14 +1244,16 @@ namespace SohImGui { ImGui::EndMenu(); } + InsertPadding(); + if (ImGui::BeginMenu("Fishing")) { EnhancementCheckbox("Instant Fishing", "gInstantFishing"); Tooltip("All fish will be caught instantly"); - EnhancementCheckbox("Guarantee Bite", "gGuaranteeFishingBite"); + PaddedEnhancementCheckbox("Guarantee Bite", "gGuaranteeFishingBite", true, false); Tooltip("When a line is stable, guarantee bite. Otherwise use default logic"); - EnhancementSliderInt("Child Minimum Weight: %d", "##cMinimumWeight", "gChildMinimumWeightFish", 6, 10, "", 10); + PaddedEnhancementSliderInt("Child Minimum Weight: %d", "##cMinimumWeight", "gChildMinimumWeightFish", 6, 10, "", 10, false, true, false); Tooltip("The minimum weight for the unique fishing reward as a child"); - EnhancementSliderInt("Adult Minimum Weight: %d", "##aMinimumWeight", "gAdultMinimumWeightFish", 8, 13, "", 13); + PaddedEnhancementSliderInt("Adult Minimum Weight: %d", "##aMinimumWeight", "gAdultMinimumWeightFish", 8, 13, "", 13, false, true, false); Tooltip("The minimum weight for the unique fishing reward as an adult"); ImGui::EndMenu(); } @@ -1161,37 +1261,41 @@ namespace SohImGui { ImGui::EndMenu(); } + InsertPadding(); + if (ImGui::BeginMenu("Reduced Clutter")) { EnhancementCheckbox("Mute Low HP Alarm", "gLowHpAlarm"); Tooltip("Disable the low HP beeping sound"); - EnhancementCheckbox("Minimal UI", "gMinimalUI"); + PaddedEnhancementCheckbox("Minimal UI", "gMinimalUI", true, false); Tooltip("Hides most of the UI when not needed\nNote: Doesn't activate until after loading a new scene"); - EnhancementCheckbox("Disable Navi Call Audio", "gDisableNaviCallAudio"); + PaddedEnhancementCheckbox("Disable Navi Call Audio", "gDisableNaviCallAudio", true, false); Tooltip("Disables the voice audio when Navi calls you"); ImGui::EndMenu(); } + InsertPadding(); + EnhancementCheckbox("Visual Stone of Agony", "gVisualAgony"); Tooltip("Displays an icon and plays a sound when Stone of Agony should be activated, for those without rumble"); - EnhancementCheckbox("Assignable Tunics and Boots", "gAssignableTunicsAndBoots"); + PaddedEnhancementCheckbox("Assignable Tunics and Boots", "gAssignableTunicsAndBoots", true, false); Tooltip("Allows equipping the tunic and boots to c-buttons"); - EnhancementCheckbox("Equipment Toggle", "gEquipmentCanBeRemoved"); + PaddedEnhancementCheckbox("Equipment Toggle", "gEquipmentCanBeRemoved", true, false); Tooltip("Allows equipment to be removed by toggling it off on\nthe equipment subscreen."); - EnhancementCheckbox("Link's Cow in Both Time Periods", "gCowOfTime"); + PaddedEnhancementCheckbox("Link's Cow in Both Time Periods", "gCowOfTime", true, false); Tooltip("Allows the Lon Lon Ranch obstacle course reward to be shared across time periods"); - EnhancementCheckbox("Enable visible guard vision", "gGuardVision"); - EnhancementCheckbox("Enable passage of time on file select", "gTimeFlowFileSelect"); - EnhancementCheckbox("Allow the cursor to be on any slot", "gPauseAnyCursor"); - Tooltip("Allows the cursor on the pause menu to be over any slot\nSimilar to Rando and Spaceworld 97"); - EnhancementCheckbox("Count Golden Skulltulas", "gInjectSkulltulaCount"); + PaddedEnhancementCheckbox("Enable visible guard vision", "gGuardVision", true, false); + PaddedEnhancementCheckbox("Enable passage of time on file select", "gTimeFlowFileSelect", true, false); + PaddedEnhancementCheckbox("Count Golden Skulltulas", "gInjectSkulltulaCount", true, false); Tooltip("Injects Golden Skulltula total count in pickup messages"); - EnhancementCheckbox("Pull grave during the day", "gDayGravePull"); + PaddedEnhancementCheckbox("Pull grave during the day", "gDayGravePull", true, false); Tooltip("Allows graves to be pulled when child during the day"); ImGui::EndMenu(); } + InsertPadding(); + if (ImGui::BeginMenu("Graphics")) { if (ImGui::BeginMenu("Animated Link in Pause Menu")) { @@ -1203,11 +1307,10 @@ namespace SohImGui { Tooltip("Allow you to rotate Link on the Equipment menu with the C-buttons\nUse C-Up or C-Down to reset Link's rotation"); EnhancementRadioButton("Rotate Link with Right Stick", "gPauseLiveLinkRotation", 3); Tooltip("Allow you to rotate Link on the Equipment menu with the Right Stick\nYou can zoom in by pointing up and reset Link's rotation by pointing down"); - if (CVar_GetS32("gPauseLiveLinkRotation", 0) != 0) { EnhancementSliderInt("Rotation Speed: %d", "##MinRotationSpeed", "gPauseLiveLinkRotationSpeed", 1, 20, ""); } - ImGui::Separator(); + PaddedSeparator(); ImGui::Text("Static loop"); EnhancementRadioButton("Disabled", "gPauseLiveLink", 0); EnhancementRadioButton("Idle (standing)", "gPauseLiveLink", 1); @@ -1224,7 +1327,7 @@ namespace SohImGui { EnhancementRadioButton("Hand on hip", "gPauseLiveLink", 12); EnhancementRadioButton("Spin attack charge", "gPauseLiveLink", 13); EnhancementRadioButton("Look at hand", "gPauseLiveLink", 14); - ImGui::Separator(); + PaddedSeparator(); ImGui::Text("Randomize"); EnhancementRadioButton("Random", "gPauseLiveLink", 15); Tooltip("Randomize the animation played each time you open the menu"); @@ -1238,70 +1341,91 @@ namespace SohImGui { ImGui::EndMenu(); } - EnhancementCheckbox("N64 Mode", "gN64Mode"); + PaddedEnhancementCheckbox("N64 Mode", "gN64Mode", true, false); Tooltip("Sets aspect ratio to 4:3 and lowers resolution to 240p, the N64's native resolution"); - EnhancementCheckbox("Enable 3D Dropped items/projectiles", "gNewDrops"); + PaddedEnhancementCheckbox("Enable 3D Dropped items/projectiles", "gNewDrops", true, false); Tooltip("Change most 2D items and projectiles on the overworld to their 3D versions"); - EnhancementCheckbox("Disable Black Bar Letterboxes", "gDisableBlackBars"); + PaddedEnhancementCheckbox("Disable Black Bar Letterboxes", "gDisableBlackBars", true, false); Tooltip("Disables Black Bar Letterboxes during cutscenes and Z-targeting\nNote: there may be minor visual glitches that were covered up by the black bars\nPlease disable this setting before reporting a bug"); - EnhancementCheckbox("Dynamic Wallet Icon", "gDynamicWalletIcon"); + PaddedEnhancementCheckbox("Dynamic Wallet Icon", "gDynamicWalletIcon", true, false); Tooltip("Changes the rupee in the wallet icon to match the wallet size you currently have"); - EnhancementCheckbox("Always show dungeon entrances", "gAlwaysShowDungeonMinimapIcon"); + PaddedEnhancementCheckbox("Always show dungeon entrances", "gAlwaysShowDungeonMinimapIcon", true, false); Tooltip("Always shows dungeon entrance icons on the minimap"); ImGui::EndMenu(); } + InsertPadding(); + if (ImGui::BeginMenu("Fixes")) { EnhancementCheckbox("Fix L&R Pause menu", "gUniformLR"); Tooltip("Makes the L and R buttons in the pause menu the same color"); - EnhancementCheckbox("Fix L&Z Page switch in Pause menu", "gNGCKaleidoSwitcher"); + PaddedEnhancementCheckbox("Fix L&Z Page switch in Pause menu", "gNGCKaleidoSwitcher", true, false); Tooltip("Makes L and R switch pages like on the GameCube\nZ opens the Debug Menu instead"); - EnhancementCheckbox("Fix Dungeon entrances", "gFixDungeonMinimapIcon"); + PaddedEnhancementCheckbox("Fix Dungeon entrances", "gFixDungeonMinimapIcon", true, false); Tooltip("Removes the dungeon entrance icon on the top-left corner of the screen when no dungeon is present on the current map"); - EnhancementCheckbox("Fix Two Handed idle animations", "gTwoHandedIdle"); + PaddedEnhancementCheckbox("Fix Two Handed idle animations", "gTwoHandedIdle", true, false); Tooltip("Re-enables the two-handed idle animation, a seemingly finished animation that was disabled on accident in the original game"); - EnhancementCheckbox("Fix the Gravedigging Tour Glitch", "gGravediggingTourFix"); + PaddedEnhancementCheckbox("Fix the Gravedigging Tour Glitch", "gGravediggingTourFix", true, false); Tooltip("Fixes a bug where the Gravedigging Tour Heart Piece disappears if the area reloads"); - EnhancementCheckbox("Fix Deku Nut upgrade", "gDekuNutUpgradeFix"); + PaddedEnhancementCheckbox("Fix Deku Nut upgrade", "gDekuNutUpgradeFix", true, false); Tooltip("Prevents the Forest Stage Deku Nut upgrade from becoming unobtainable after receiving the Poacher's Saw"); - EnhancementCheckbox("Fix Navi text HUD position", "gNaviTextFix"); + PaddedEnhancementCheckbox("Fix Navi text HUD position", "gNaviTextFix", true, false); Tooltip("Correctly centers the Navi text prompt on the HUD's C-Up button"); - EnhancementCheckbox("Fix Anubis fireballs", "gAnubisFix"); + PaddedEnhancementCheckbox("Fix Anubis fireballs", "gAnubisFix", true, false); Tooltip("Make Anubis fireballs do fire damage when reflected back at them with the Mirror Shield"); - EnhancementCheckbox("Fix Megaton Hammer crouch stab", "gCrouchStabHammerFix"); + PaddedEnhancementCheckbox("Fix Megaton Hammer crouch stab", "gCrouchStabHammerFix", true, false); Tooltip("Make the Megaton Hammer's crouch stab able to destroy rocks without first swinging it normally"); if (CVar_GetS32("gCrouchStabHammerFix", 0) == 0) { CVar_SetS32("gCrouchStabFix", 0); } else { - EnhancementCheckbox("Remove power crouch stab", "gCrouchStabFix"); + PaddedEnhancementCheckbox("Remove power crouch stab", "gCrouchStabFix", true, false); Tooltip("Make crouch stabbing always do the same damage as a regular slash"); } ImGui::EndMenu(); } + InsertPadding(); + if (ImGui::BeginMenu("Restoration")) { EnhancementCheckbox("Red Ganon blood", "gRedGanonBlood"); Tooltip("Restore the original red blood from NTSC 1.0/1.1. Disable for green blood"); - EnhancementCheckbox("Fish while hovering", "gHoverFishing"); + PaddedEnhancementCheckbox("Fish while hovering", "gHoverFishing", true, false); Tooltip("Restore a bug from NTSC 1.0 that allows casting the Fishing Rod while using the Hover Boots"); - EnhancementCheckbox("N64 Weird Frames", "gN64WeirdFrames"); + PaddedEnhancementCheckbox("N64 Weird Frames", "gN64WeirdFrames", true, false); Tooltip("Restores N64 Weird Frames allowing weirdshots to behave the same as N64"); - EnhancementCheckbox("Bombchus out of bounds", "gBombchusOOB"); + PaddedEnhancementCheckbox("Bombchus out of bounds", "gBombchusOOB", true, false); Tooltip("Allows bombchus to explode out of bounds\nSimilar to GameCube and Wii VC"); ImGui::EndMenu(); } - EnhancementCheckbox("Autosave", "gAutosave"); + PaddedEnhancementCheckbox("Autosave", "gAutosave", true, false); Tooltip("Automatically save the game every time a new area is entered or item is obtained\n" "To disable saving when obtaining an item, manually set gAutosaveAllItems and gAutosaveMajorItems to 0\n" "gAutosaveAllItems takes priority over gAutosaveMajorItems if both are set to 1\n" "gAutosaveMajorItems excludes rupees and health/magic/ammo refills (but includes bombchus)"); + InsertPadding(); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); + static ImVec2 buttonSize(200.0f, 0.0f); + if (ImGui::Button(GetWindowButtonText("Cosmetics Editor", CVar_GetS32("gCosmeticsEditorEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gCosmeticsEditorEnabled", 0); + CVar_SetS32("gCosmeticsEditorEnabled", !currentValue); + needs_save = true; + customWindows["Cosmetics Editor"].enabled = CVar_GetS32("gCosmeticsEditorEnabled", 0); + } + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(1); + EXPERIMENTAL(); const char* fps_cvar = "gInterpolationFPS"; @@ -1336,7 +1460,7 @@ namespace SohImGui { } ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); - + ImGui::PushItemWidth(ImGui::GetWindowSize().x - 79.0f); if (ImGui::SliderInt("##FPSInterpolation", &val, minFps, maxFps, "", ImGuiSliderFlags_AlwaysClamp)) { if (val > 360) @@ -1347,11 +1471,11 @@ namespace SohImGui { { val = 20; } - + CVar_SetS32(fps_cvar, val); needs_save = true; } - + ImGui::PopItemWidth(); Tooltip("Interpolate extra frames to get smoother graphics\n" "Set to match your monitor's refresh rate, or a divisor of it\n" "A higher target FPS than your monitor's refresh rate will just waste resources, " @@ -1367,8 +1491,11 @@ namespace SohImGui { needs_save = true; } } + if (impl.backend == Backend::DX11) { + InsertPadding(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f)); if (ImGui::Button("Match Refresh Rate")) { int hz = roundf(gfx_get_detected_hz()); @@ -1378,24 +1505,26 @@ namespace SohImGui { needs_save = true; } } + ImGui::PopStyleVar(1); + InsertPadding(); } EnhancementCheckbox("Disable LOD", "gDisableLOD"); Tooltip("Turns off the Level of Detail setting, making models use their higher-poly variants at any distance"); - EnhancementCheckbox("Disable Draw Distance", "gDisableDrawDistance"); + PaddedEnhancementCheckbox("Disable Draw Distance", "gDisableDrawDistance", true, false); Tooltip("Turns off the objects draw distance, making objects being visible from a longer range"); if (CVar_GetS32("gDisableDrawDistance", 0) == 0) { CVar_SetS32("gDisableKokiriDrawDistance", 0); } else if (CVar_GetS32("gDisableDrawDistance", 0) == 1) { - EnhancementCheckbox("Kokiri Draw Distance", "gDisableKokiriDrawDistance"); + PaddedEnhancementCheckbox("Kokiri Draw Distance", "gDisableKokiriDrawDistance", true, false); Tooltip("The Kokiri are mystical beings that fade into view when approached\nEnabling this will remove their draw distance"); } - EnhancementCheckbox("Skip Text", "gSkipText"); + PaddedEnhancementCheckbox("Skip Text", "gSkipText", true, false); Tooltip("Holding down B skips text\nKnown to cause a cutscene softlock in Water Temple\nSoftlock can be fixed by pressing D-Right in Debug mode"); - - EnhancementCheckbox("Free Camera", "gFreeCamera"); + PaddedEnhancementCheckbox("Free Camera", "gFreeCamera", true, false); Tooltip("Enables camera control\nNote: You must remap C buttons off of the right stick in the controller config menu, and map the camera stick to the right stick."); #ifdef __SWITCH__ + InsertPadding(); int slot = CVar_GetS32("gSwitchPerfMode", (int)SwitchProfiles::STOCK); ImGui::Text("Switch performance mode"); if (ImGui::BeginCombo("##perf", SWITCH_CPU_PROFILES[slot])) { @@ -1421,37 +1550,36 @@ namespace SohImGui { { if (ImGui::BeginMenu("Infinite...")) { EnhancementCheckbox("Money", "gInfiniteMoney"); - EnhancementCheckbox("Health", "gInfiniteHealth"); - EnhancementCheckbox("Ammo", "gInfiniteAmmo"); - EnhancementCheckbox("Magic", "gInfiniteMagic"); - EnhancementCheckbox("Nayru's Love", "gInfiniteNayru"); - EnhancementCheckbox("Epona Boost", "gInfiniteEpona"); + PaddedEnhancementCheckbox("Health", "gInfiniteHealth", true, false); + PaddedEnhancementCheckbox("Ammo", "gInfiniteAmmo", true, false); + PaddedEnhancementCheckbox("Magic", "gInfiniteMagic", true, false); + PaddedEnhancementCheckbox("Nayru's Love", "gInfiniteNayru", true, false); + PaddedEnhancementCheckbox("Epona Boost", "gInfiniteEpona", true, false); ImGui::EndMenu(); } - EnhancementCheckbox("No Clip", "gNoClip"); + PaddedEnhancementCheckbox("No Clip", "gNoClip", true, false); Tooltip("Allows you to walk through walls"); - EnhancementCheckbox("Climb Everything", "gClimbEverything"); + PaddedEnhancementCheckbox("Climb Everything", "gClimbEverything", true, false); Tooltip("Makes every surface in the game climbable"); - EnhancementCheckbox("Moon Jump on L", "gMoonJumpOnL"); + PaddedEnhancementCheckbox("Moon Jump on L", "gMoonJumpOnL", true, false); Tooltip("Holding L makes you float into the air"); - EnhancementCheckbox("Super Tunic", "gSuperTunic"); + PaddedEnhancementCheckbox("Super Tunic", "gSuperTunic", true, false); Tooltip("Makes every tunic have the effects of every other tunic"); - EnhancementCheckbox("Easy ISG", "gEzISG"); + PaddedEnhancementCheckbox("Easy ISG", "gEzISG", true, false); Tooltip("Passive Infinite Sword Glitch\nIt makes your sword's swing effect and hitbox stay active indefinitely"); - EnhancementCheckbox("Unrestricted Items", "gNoRestrictItems"); + PaddedEnhancementCheckbox("Unrestricted Items", "gNoRestrictItems", true, false); Tooltip("Allows you to use any item at any location"); - EnhancementCheckbox("Freeze Time", "gFreezeTime"); + PaddedEnhancementCheckbox("Freeze Time", "gFreezeTime", true, false); Tooltip("Freezes the time of day"); - EnhancementCheckbox("Drops Don't Despawn", "gDropsDontDie"); + PaddedEnhancementCheckbox("Drops Don't Despawn", "gDropsDontDie", true, false); Tooltip("Drops from enemies, grass, etc. don't disappear after a set amount of time"); - EnhancementCheckbox("Fireproof Deku Shield", "gFireproofDekuShield"); + PaddedEnhancementCheckbox("Fireproof Deku Shield", "gFireproofDekuShield", true, false); Tooltip("Prevents the Deku Shield from burning on contact with fire"); - EnhancementCheckbox("Shield with Two-Handed Weapons", "gShieldTwoHanded"); + PaddedEnhancementCheckbox("Shield with Two-Handed Weapons", "gShieldTwoHanded", true, false); Tooltip("This allows you to put up your shield with any two-handed weapon in hand except for Deku Sticks"); - Tooltip("This allows you to put up your shield with any two-handed weapon in hand\nexcept for Deku Sticks"); - EnhancementCheckbox("Time Sync", "gTimeSync"); + PaddedEnhancementCheckbox("Time Sync", "gTimeSync", true, false); Tooltip("This syncs the ingame time with the real world time"); { @@ -1465,7 +1593,7 @@ namespace SohImGui { ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f); } - EnhancementCheckbox("Enable Beta Quest", "gEnableBetaQuest"); + PaddedEnhancementCheckbox("Enable Beta Quest", "gEnableBetaQuest", true, false); Tooltip("Turns on OoT Beta Quest. *WARNING* This will reset your game."); betaQuestEnabled = CVar_GetS32("gEnableBetaQuest", 0); if (betaQuestEnabled) { @@ -1484,9 +1612,6 @@ namespace SohImGui { ImGui::SliderInt("##BetaQuest", &betaQuestWorld, 0, 8, "", ImGuiSliderFlags_AlwaysClamp); Tooltip("Set the Beta Quest world to explore. *WARNING* Changing this will reset your game.\nCtrl+Click to type in a value."); - ImGui::Text("After Slider Beta Quest World: %d", betaQuestWorld); - - ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); if (ImGui::Button(" + ##BetaQuest")) { @@ -1499,8 +1624,6 @@ namespace SohImGui { else if (betaQuestWorld < 0) { betaQuestWorld = 0; } - - ImGui::Text("After Clamp Beta Quest World: %d", betaQuestWorld); } else { lastBetaQuestWorld = betaQuestWorld = 0xFFEF; @@ -1522,7 +1645,7 @@ namespace SohImGui { if (!isBetaQuestEnabled) { ImGui::PopItemFlag(); - ImGui::PopStyleVar(); + ImGui::PopStyleVar(1); } } @@ -1535,9 +1658,9 @@ namespace SohImGui { { EnhancementCheckbox("OoT Debug Mode", "gDebugEnabled"); Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip with L + D-pad Right, and open the debug menu with L on the pause screen"); - EnhancementCheckbox("OoT Skulltula Debug", "gSkulltulaDebugEnabled"); + PaddedEnhancementCheckbox("OoT Skulltula Debug", "gSkulltulaDebugEnabled", true, false); Tooltip("Enables Skulltula Debug, when moving the cursor in the menu above various map icons (boss key, compass, map screen locations, etc) will set the GS bits in that area.\nUSE WITH CAUTION AS IT DOES NOT UPDATE THE GS COUNT."); - EnhancementCheckbox("Fast File Select", "gSkipLogoTitle"); + PaddedEnhancementCheckbox("Fast File Select", "gSkipLogoTitle", true, false); Tooltip("Load the game to the selected menu or file\n\"Zelda Map Select\" require debug mode else you will fallback to File choose menu\nUsing a file number that don't have save will create a save file only if you toggle on \"Create a new save if none ?\" else it will bring you to the File choose menu"); if (CVar_GetS32("gSkipLogoTitle", 0)) { const char* FastFileSelect[5] = { @@ -1549,46 +1672,91 @@ namespace SohImGui { }; ImGui::Text("Loading :"); EnhancementCombobox("gSaveFileID", FastFileSelect, 5, 0); - EnhancementCheckbox("Create a new save if none", "gCreateNewSave"); + PaddedEnhancementCheckbox("Create a new save if none", "gCreateNewSave", true, false); Tooltip("Enable the creation of a new save file if none exist in the File number selected\nNo file name will be assigned please do in Save editor once you see the first text else your save file name will be named \"00000000\"\nIf disabled you will fall back in File select menu"); }; - ImGui::Separator(); - EnhancementCheckbox("Stats", "gStatsEnabled"); + PaddedSeparator(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); + static ImVec2 buttonSize(160.0f, 0.0f); + if (ImGui::Button(GetWindowButtonText("Stats", CVar_GetS32("gStatsEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gStatsEnabled", 0); + CVar_SetS32("gStatsEnabled", !currentValue); + statsWindowOpen = true; + needs_save = true; + } Tooltip("Shows the stats window, with your FPS and frametimes, and the OS you're playing on"); - EnhancementCheckbox("Console", "gConsoleEnabled"); + InsertPadding(); + if (ImGui::Button(GetWindowButtonText("Console", CVar_GetS32("gConsoleEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gConsoleEnabled", 0); + CVar_SetS32("gConsoleEnabled", !currentValue); + needs_save = true; + console->opened = CVar_GetS32("gConsoleEnabled", 0); + } Tooltip("Enables the console window, allowing you to input commands, type help for some examples"); - console->opened = CVar_GetS32("gConsoleEnabled", 0); + InsertPadding(); + if (ImGui::Button(GetWindowButtonText("Save Editor", CVar_GetS32("gSaveEditorEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gSaveEditorEnabled", 0); + CVar_SetS32("gSaveEditorEnabled", !currentValue); + needs_save = true; + customWindows["Save Editor"].enabled = CVar_GetS32("gSaveEditorEnabled", 0); + } + InsertPadding(); + if (ImGui::Button(GetWindowButtonText("Collision Viewer", CVar_GetS32("gCollisionViewerEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gCollisionViewerEnabled", 0); + CVar_SetS32("gCollisionViewerEnabled", !currentValue); + needs_save = true; + customWindows["Collision Viewer"].enabled = CVar_GetS32("gCollisionViewerEnabled", 0); + } + InsertPadding(); + if (ImGui::Button(GetWindowButtonText("Actor Viewer", CVar_GetS32("gActorViewerEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gActorViewerEnabled", 0); + CVar_SetS32("gActorViewerEnabled", !currentValue); + needs_save = true; + customWindows["Actor Viewer"].enabled = CVar_GetS32("gActorViewerEnabled", 0); + } + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(1); ImGui::EndMenu(); } - for (const auto& category : windowCategories) { - ImGui::SetCursorPosY(0.0f); - if (category.first != "Randomizer") { - if (ImGui::BeginMenu(category.first.c_str())) { - for (const std::string& name : category.second) { - std::string varName(name); - varName.erase(std::remove_if(varName.begin(), varName.end(), [](unsigned char x) { return std::isspace(x); }), varName.end()); - std::string toggleName = "g" + varName + "Enabled"; - - EnhancementCheckbox(name.c_str(), toggleName.c_str()); - customWindows[name].enabled = CVar_GetS32(toggleName.c_str(), 0); - } - ImGui::EndMenu(); - } - } - } - - // Randomizer Menu ImGui::SetCursorPosY(0.0f); + if (ImGui::BeginMenu("Randomizer")) { - EnhancementCheckbox("Randomizer Settings", "gRandomizerSettingsEnabled"); - customWindows["Randomizer Settings"].enabled = CVar_GetS32("gRandomizerSettingsEnabled", 0); - EnhancementCheckbox("Item Tracker", "gItemTrackerEnabled"); - customWindows["Item Tracker"].enabled = CVar_GetS32("gItemTrackerEnabled", 0); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); + static ImVec2 buttonSize(200.0f, 0.0f); + if (ImGui::Button(GetWindowButtonText("Randomizer Settings", CVar_GetS32("gRandomizerSettingsEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gRandomizerSettingsEnabled", 0); + CVar_SetS32("gRandomizerSettingsEnabled", !currentValue); + needs_save = true; + customWindows["Randomizer Settings"].enabled = CVar_GetS32("gRandomizerSettingsEnabled", 0); + } + InsertPadding(); + if (ImGui::Button(GetWindowButtonText("Item Tracker", CVar_GetS32("gItemTrackerEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gItemTrackerEnabled", 0); + CVar_SetS32("gItemTrackerEnabled", !currentValue); + needs_save = true; + customWindows["Item Tracker"].enabled = CVar_GetS32("gItemTrackerEnabled", 0); + } + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(1); + + PaddedSeparator(); - ImGui::Separator(); if (ImGui::BeginMenu("Rando Enhancements")) { EnhancementCheckbox("Quest Item Fanfares", "gRandoQuestItemFanfares"); @@ -1599,8 +1767,11 @@ namespace SohImGui { ); ImGui::EndMenu(); } + ImGui::EndMenu(); } + + ImGui::PopStyleVar(1); ImGui::EndMenuBar(); } @@ -1616,10 +1787,14 @@ namespace SohImGui { ImGui::End(); ImGui::PopStyleColor(); } + if (CVar_GetS32("gStatsEnabled", 0)) { + if (!statsWindowOpen) { + CVar_SetS32("gStatsEnabled", 0); + } const float framerate = ImGui::GetIO().Framerate; ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); - ImGui::Begin("Debug Stats", nullptr, ImGuiWindowFlags_NoFocusOnAppearing); + ImGui::Begin("Debug Stats", &statsWindowOpen, ImGuiWindowFlags_NoFocusOnAppearing); #ifdef _WIN32 ImGui::Text("Platform: Windows"); @@ -1748,6 +1923,319 @@ namespace SohImGui { } } + void applyEnhancementPresets(void) { + + switch (CVar_GetS32("gSelectEnhancementPresets", 0)) { + // Default + case 0: + applyEnhancementPresetDefault(); + break; + + // Vanilla Plus + case 1: + applyEnhancementPresetDefault(); + applyEnhancementPresetVanillaPlus(); + break; + + // Enhanced + case 2: + applyEnhancementPresetDefault(); + applyEnhancementPresetVanillaPlus(); + applyEnhancementPresetEnhanced(); + break; + + // Randomizer + case 3: + applyEnhancementPresetDefault(); + applyEnhancementPresetVanillaPlus(); + applyEnhancementPresetEnhanced(); + applyEnhancementPresetRandomizer(); + break; + } + } + + void applyEnhancementPresetDefault(void) { + // Text Speed (1 to 5) + CVar_SetS32("gTextSpeed", 1); + // King Zora Speed (1 to 5) + CVar_SetS32("gMweepSpeed", 1); + // Biggoron Forge Time (0 to 3) + CVar_SetS32("gForgeTime", 3); + // Vine/Ladder Climb speed (+0 to +12) + CVar_SetS32("gClimbSpeed", 0); + // Faster Block Push + CVar_SetS32("gFasterBlockPush", 0); + // No Forced Navi + CVar_SetS32("gNoForcedNavi", 0); + // No Skulltula Freeze + CVar_SetS32("gSkulltulaFreeze", 0); + // MM Bunny Hood + CVar_SetS32("gMMBunnyHood", 0); + // Fast Chests + CVar_SetS32("gFastChests", 0); + // Fast Drops + CVar_SetS32("gFastDrops", 0); + // Better Owl + CVar_SetS32("gBetterOwl", 0); + // Fast Ocarina Playback + CVar_SetS32("gFastOcarinaPlayback", 0); + // Prevent Dropped Ocarina Inputs + CVar_SetS32("gDpadNoDropOcarinaInput", 0); + // Instant Putaway + CVar_SetS32("gInstantPutaway", 0); + // Mask Select in Inventory + CVar_SetS32("gMaskSelect", 0); + + // Damage Multiplier (0 to 8) + CVar_SetS32("gDamageMul", 0); + // Fall Damage Multiplier (0 to 7) + CVar_SetS32("gFallDamageMul", 0); + // Void Damage Multiplier (0 to 6) + CVar_SetS32("gVoidDamageMul", 0); + // No Random Drops + CVar_SetS32("gNoRandomDrops", 0); + // No Heart Drops + CVar_SetS32("gNoHeartDrops", 0); + // Always Win Goron Pot + CVar_SetS32("gGoronPot", 0); + + // Change Red Potion Effect + CVar_SetS32("gRedPotionEffect", 0); + // Red Potion Health (1 to 100) + CVar_SetS32("gRedPotionHealth", 1); + // Red Potion Percent Restore + CVar_SetS32("gRedPercentRestore", 0); + // Change Green Potion Effect + CVar_SetS32("gGreenPotionEffect", 0); + // Green Potion Mana (1 to 100) + CVar_SetS32("gGreenPotionMana", 1); + // Green Potion Percent Restore + CVar_SetS32("gGreenPercentRestore", 0); + // Change Blue Potion Effects + CVar_SetS32("gBluePotionEffects", 0); + // Blue Potion Health (1 to 100) + CVar_SetS32("gBluePotionHealth", 1); + // Blue Potion Health Percent Restore + CVar_SetS32("gBlueHealthPercentRestore", 0); + // Blue Potion Mana (1 to 100) + CVar_SetS32("gBluePotionMana", 1); + // Blue Potion Mana Percent Restore + CVar_SetS32("gBlueManaPercentRestore", 0); + // Change Milk Effect + CVar_SetS32("gMilkEffect", 0); + // Milk Health (1 to 100) + CVar_SetS32("gMilkHealth", 1); + // Milk Percent Restore + CVar_SetS32("gMilkPercentRestore", 0); + // Separate Half Milk Effect + CVar_SetS32("gSeparateHalfMilkEffect", 0); + // Half Milk Health (1 to 100) + CVar_SetS32("gHalfMilkHealth", 0); + // Half Milk Percent Restore + CVar_SetS32("gHalfMilkPercentRestore", 0); + // Change Fairy Effect + CVar_SetS32("gFairyEffect", 0); + // Fairy (1 to 100) + CVar_SetS32("gFairyHealth", 1); + // Fairy Percent Restore + CVar_SetS32("gFairyPercentRestore", 0); + // Change Fairy Revive Effect + CVar_SetS32("gFairyReviveEffect", 0); + // Fairy Revival (1 to 100) + CVar_SetS32("gFairyReviveHealth", 1); + // Fairy Revive Percent Restore + CVar_SetS32("gFairyRevivePercentRestore", 0); + + // Instant Fishing + CVar_SetS32("gInstantFishing", 0); + // Guarantee Bite + CVar_SetS32("gGuaranteeFishingBite", 0); + // Child Minimum Weight (6 to 10) + CVar_SetS32("gChildMinimumWeightFish", 10); + // Adult Minimum Weight (8 to 13) + CVar_SetS32("gAdultMinimumWeightFish", 13); + + // Mute Low HP Alarm + CVar_SetS32("gLowHpAlarm", 0); + // Minimal UI + CVar_SetS32("gMinimalUI", 0); + // Disable Navi Call Audio + CVar_SetS32("gDisableNaviCallAudio", 0); + + // Visual Stone of Agony + CVar_SetS32("gVisualAgony", 0); + // Assignable Tunics and Boots + CVar_SetS32("gAssignableTunicsAndBoots", 0); + // Link's Cow in Both Time Periods + CVar_SetS32("gCowOfTime", 0); + // Enable visible guard vision + CVar_SetS32("gGuardVision", 0); + // Enable passage of time on file select + CVar_SetS32("gTimeFlowFileSelect", 0); + // Allow the cursor to be on any slot + CVar_SetS32("gPauseAnyCursor", 0); + // Count Golden Skulltulas + CVar_SetS32("gInjectSkulltulaCount", 0); + // Pull grave during the day + CVar_SetS32("gDayGravePull", 0); + + // Rotate link (0 to 2) + CVar_SetS32("gPauseLiveLinkRotation", 0); + // Pause link animation (0 to 16) + CVar_SetS32("gPauseLiveLink", 0); + // Frames to wait + CVar_SetS32("gMinFrameCount", 1); + + // N64 Mode + CVar_SetS32("gN64Mode", 0); + // Enable 3D Dropped items/projectiles + CVar_SetS32("gNewDrops", 0); + // Disable Black Bar Letterboxes + CVar_SetS32("gDisableBlackBars", 0); + // Dynamic Wallet Icon + CVar_SetS32("gDynamicWalletIcon", 0); + // Always show dungeon entrances + CVar_SetS32("gAlwaysShowDungeonMinimapIcon", 0); + + // Fix L&R Pause menu + CVar_SetS32("gUniformLR", 0); + // Fix L&Z Page switch in Pause menu + CVar_SetS32("gNGCKaleidoSwitcher", 0); + // Fix Dungeon entrances + CVar_SetS32("gFixDungeonMinimapIcon", 0); + // Fix Two Handed idle animations + CVar_SetS32("gTwoHandedIdle", 0); + // Fix the Gravedigging Tour Glitch + CVar_SetS32("gGravediggingTourFix", 0); + // Fix Deku Nut upgrade + CVar_SetS32("gDekuNutUpgradeFix", 0); + // Fix Navi text HUD position + CVar_SetS32("gNaviTextFix", 0); + // Fix Anubis fireballs + CVar_SetS32("gAnubisFix", 0); + // Fix Megaton Hammer crouch stab + CVar_SetS32("gCrouchStabHammerFix", 0); + // Fix all crouch stab + CVar_SetS32("gCrouchStabFix", 0); + + // Red Ganon blood + CVar_SetS32("gRedGanonBlood", 0); + // Fish while hovering + CVar_SetS32("gHoverFishing", 0); + // N64 Weird Frames + CVar_SetS32("gN64WeirdFrames", 0); + // Bombchus out of bounds + CVar_SetS32("gBombchusOOB", 0); + } + + void applyEnhancementPresetVanillaPlus(void) { + // Text Speed (1 to 5) + CVar_SetS32("gTextSpeed", 5); + // King Zora Speed (1 to 5) + CVar_SetS32("gMweepSpeed", 2); + // Faster Block Push + CVar_SetS32("gFasterBlockPush", 1); + // Better Owl + CVar_SetS32("gBetterOwl", 1); + // Prevent Dropped Ocarina Inputs + CVar_SetS32("gDpadNoDropOcarinaInput", 1); + + // Assignable Tunics and Boots + CVar_SetS32("gAssignableTunicsAndBoots", 1); + // Enable passage of time on file select + CVar_SetS32("gTimeFlowFileSelect", 1); + // Count Golden Skulltulas + CVar_SetS32("gInjectSkulltulaCount", 1); + + // Pause link animation (0 to 16) + CVar_SetS32("gPauseLiveLink", 1); + + // Dynamic Wallet Icon + CVar_SetS32("gDynamicWalletIcon", 1); + // Always show dungeon entrances + CVar_SetS32("gAlwaysShowDungeonMinimapIcon", 1); + + // Fix L&R Pause menu + CVar_SetS32("gUniformLR", 1); + // Fix Dungeon entrances + CVar_SetS32("gFixDungeonMinimapIcon", 1); + // Fix Two Handed idle animations + CVar_SetS32("gTwoHandedIdle", 1); + // Fix the Gravedigging Tour Glitch + CVar_SetS32("gGravediggingTourFix", 1); + // Fix Deku Nut upgrade + CVar_SetS32("gDekuNutUpgradeFix", 1); + + // Red Ganon blood + CVar_SetS32("gRedGanonBlood", 1); + // Fish while hovering + CVar_SetS32("gHoverFishing", 1); + // N64 Weird Frames + CVar_SetS32("gN64WeirdFrames", 1); + // Bombchus out of bounds + CVar_SetS32("gBombchusOOB", 1); + } + + void applyEnhancementPresetEnhanced(void) { + // King Zora Speed (1 to 5) + CVar_SetS32("gMweepSpeed", 5); + // Biggoron Forge Time (0 to 3) + CVar_SetS32("gForgeTime", 0); + // Vine/Ladder Climb speed (+0 to +12) + CVar_SetS32("gClimbSpeed", 1); + // No Forced Navi + CVar_SetS32("gNoForcedNavi", 1); + // No Skulltula Freeze + CVar_SetS32("gSkulltulaFreeze", 1); + // MM Bunny Hood + CVar_SetS32("gMMBunnyHood", 1); + // Fast Chests + CVar_SetS32("gFastChests", 1); + // Fast Drops + CVar_SetS32("gFastDrops", 1); + // Fast Ocarina Playback + CVar_SetS32("gFastOcarinaPlayback", 1); + // Instant Putaway + CVar_SetS32("gInstantPutaway", 1); + // Mask Select in Inventory + CVar_SetS32("gMaskSelect", 1); + + // Disable Navi Call Audio + CVar_SetS32("gDisableNaviCallAudio", 1); + + // Count Golden Skulltulas + CVar_SetS32("gInjectSkulltulaCount", 1); + + // Enable 3D Dropped items/projectiles + CVar_SetS32("gNewDrops", 1); + + // Fix Anubis fireballs + CVar_SetS32("gAnubisFix", 1); + } + + void applyEnhancementPresetRandomizer(void) { + // Instant Fishing + CVar_SetS32("gInstantFishing", 1); + // Guarantee Bite + CVar_SetS32("gGuaranteeFishingBite", 1); + // Child Minimum Weight (6 to 10) + CVar_SetS32("gChildMinimumWeightFish", 6); + // Adult Minimum Weight (8 to 13) + CVar_SetS32("gAdultMinimumWeightFish", 8); + + // Visual Stone of Agony + CVar_SetS32("gVisualAgony", 1); + // Allow the cursor to be on any slot + CVar_SetS32("gPauseAnyCursor", 1); + // Pull grave during the day + CVar_SetS32("gDayGravePull", 1); + + // Pause link animation (0 to 16) + CVar_SetS32("gPauseLiveLink", 16); + // Frames to wait + CVar_SetS32("gMinFrameCount", 200); + } + void Render() { ImGui::Render(); ImGuiRenderDrawData(ImGui::GetDrawData()); @@ -1916,7 +2404,7 @@ namespace SohImGui { #endif ImGui::GetCurrentWindow()->Size.x += frameHeight; - ImGui::Dummy(ImVec2(0.0f, 0.0f)); + InsertPadding(); ImGui::EndGroup(); } @@ -1952,4 +2440,56 @@ namespace SohImGui { std::string BreakTooltip(const std::string& text, int lineLength) { return BreakTooltip(text.c_str(), lineLength); } + + void InsertPadding(float extraVerticalPadding) { + ImGui::Dummy(ImVec2(0.0f, extraVerticalPadding)); + } + + void PaddedSeparator(bool padTop, bool padBottom, float extraVerticalTopPadding, float extraVerticalBottomPadding) { + if (padTop) { + ImGui::Dummy(ImVec2(0.0f, extraVerticalTopPadding)); + } + ImGui::Separator(); + if (padBottom) { + ImGui::Dummy(ImVec2(0.0f, extraVerticalBottomPadding)); + } + } + + void PaddedEnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue, bool PlusMinusButton, bool padTop, bool padBottom) { + if (padTop) { + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + } + EnhancementSliderInt(text, id, cvarName, min, max, format, defaultValue, PlusMinusButton); + if (padBottom) { + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + } + } + + void PaddedEnhancementCheckbox(const char* text, const char* cvarName, bool padTop, bool padBottom) { + if (padTop) { + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + } + EnhancementCheckbox(text, cvarName); + if (padBottom) { + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + } + } + + void PaddedText(const char* text, bool padTop, bool padBottom) { + if (padTop) { + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + } + ImGui::Text("%s", text); + if (padBottom) { + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + } + } + + std::string GetWindowButtonText(const char* text, bool menuOpen) { + char buttonText[100] = ""; + if(menuOpen) { strcat(buttonText,"> "); } + strcat(buttonText, text); + if (!menuOpen) { strcat(buttonText, " "); } + return buttonText; + } } diff --git a/libultraship/libultraship/ImGuiImpl.h b/libultraship/libultraship/ImGuiImpl.h index 6913fce61..fd0aced6a 100644 --- a/libultraship/libultraship/ImGuiImpl.h +++ b/libultraship/libultraship/ImGuiImpl.h @@ -86,6 +86,12 @@ namespace SohImGui { void EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors, bool allow_rainbow = true, bool has_alpha=false, bool TitleSameLine=false); void EnhancementCombo(const std::string& name, const char* cvarName, const std::vector& items, int defaultValue = 0); + void applyEnhancementPresets(void); + void applyEnhancementPresetDefault(void); + void applyEnhancementPresetVanillaPlus(void); + void applyEnhancementPresetEnhanced(void); + void applyEnhancementPresetRandomizer(void); + void DrawMainMenuAndCalculateGameSize(void); void DrawFramebufferAndGameInput(void); @@ -106,6 +112,12 @@ namespace SohImGui { void EndGroupPanel(float minHeight = 0.0f); std::string BreakTooltip(const char* text, int lineLength = 60); std::string BreakTooltip(const std::string& text, int lineLength = 60); + void InsertPadding(float extraVerticalPadding = 0.0f); + void PaddedSeparator(bool padTop = true, bool padBottom = true, float extraVerticalTopPadding = 0.0f, float extraVerticalBottomPadding = 0.0f); + void PaddedEnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue = 0, bool PlusMinusButton = false, bool padTop = true, bool padBottom = true); + void PaddedEnhancementCheckbox(const char* text, const char* cvarName, bool padTop = true, bool padBottom = true); + void PaddedText(const char* text, bool padTop = true, bool padBottom = true); + std::string GetWindowButtonText(const char* text, bool menuOpen); } -#endif \ No newline at end of file +#endif diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp index 23cff4698..76573c084 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp @@ -166,10 +166,12 @@ static void gfx_sdl_init(const char *game_name, bool start_in_fullscreen, uint32 #ifdef __SWITCH__ // For Switch we need to set the window width before creating the window Ship::Switch::GetDisplaySize(&window_width, &window_height); + width = window_width; + height = window_height; #endif wnd = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - window_width, window_height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); #ifndef __SWITCH__ SDL_GL_GetDrawableSize(wnd, &window_width, &window_height); diff --git a/libultraship/libultraship/ResourceMgr.cpp b/libultraship/libultraship/ResourceMgr.cpp index bcc783993..4c517b39d 100644 --- a/libultraship/libultraship/ResourceMgr.cpp +++ b/libultraship/libultraship/ResourceMgr.cpp @@ -82,19 +82,14 @@ namespace Ship { break; } - //Lock.lock(); std::shared_ptr ToLoad = FileLoadQueue.front(); FileLoadQueue.pop(); - //Lock.unlock(); OTR->LoadFile(ToLoad->path, true, ToLoad); - //Lock.lock(); if (!ToLoad->bHasLoadError) FileCache[ToLoad->path] = ToLoad->bIsLoaded && !ToLoad->bHasLoadError ? ToLoad : nullptr; - //Lock.unlock(); - SPDLOG_DEBUG("Loaded File {} on ResourceMgr thread", ToLoad->path); ToLoad->FileLoadNotifier.notify_all(); @@ -117,10 +112,8 @@ namespace Ship { } std::shared_ptr ToLoad = nullptr; - //ResLock.lock(); ToLoad = ResourceLoadQueue.front(); ResourceLoadQueue.pop(); - //ResLock.unlock(); // Wait for the underlying File to complete loading { @@ -148,9 +141,6 @@ namespace Ship { SPDLOG_DEBUG("Loaded Resource {} on ResourceMgr thread", ToLoad->file->path); - // Disabled for now because it can cause random crashes - //FileCache[Res->File->path] = nullptr; - //FileCache.erase(FileCache.find(Res->File->path)); Res->file = nullptr; } else { @@ -159,9 +149,6 @@ namespace Ship { SPDLOG_ERROR("Resource load FAILED {} on ResourceMgr thread", ToLoad->file->path); } - - //ResLock.lock(); - //ResLock.unlock(); } } else diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index 0db33792e..f4f047428 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -19,7 +19,7 @@ #include #include #include "Console.h" -#include "Cvar.h" +#include "ImGuiImpl.h" #include @@ -67,10 +67,9 @@ extern "C" { pad->gyro_x = 0; pad->gyro_y = 0; - if (!CVar_GetS32("gOpenMenuBar", 0)) { - Ship::GlobalCtx2::GetInstance()->GetWindow()->GetControlDeck()->WriteToPad(pad); - } + if (SohImGui::controller->Opened) return; + Ship::GlobalCtx2::GetInstance()->GetWindow()->GetControlDeck()->WriteToPad(pad); Ship::ExecuteHooks(pad); } diff --git a/scripts/linux/appimage/build.sh b/scripts/linux/appimage/build.sh index bad6b00c5..86f46fb90 100755 --- a/scripts/linux/appimage/build.sh +++ b/scripts/linux/appimage/build.sh @@ -1,7 +1,6 @@ #!/bin/bash cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -cmake --build build-cmake --target ExtractAssets -- cmake --build build-cmake --config Release -- (cd build-cmake && cpack -G External) diff --git a/scripts/switch/build.sh b/scripts/switch/build.sh index b5b137652..2a208dd43 100755 --- a/scripts/switch/build.sh +++ b/scripts/switch/build.sh @@ -1,7 +1,4 @@ #!/bin/bash -cmake --no-warn-unused-cli -H. -Bbuild-linux -GNinja -cmake --build build-linux --target ExtractAssets - cmake -H. -Bbuild-switch -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake cmake --build build-switch --target soh_nro diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 54700d65d..5342374f0 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -215,6 +215,13 @@ set(Header_Files__soh__Enhancements__randomizer__3drando ) source_group("Header Files\\soh\\Enhancements\\randomizer\\3drando" FILES ${Header_Files__soh__Enhancements__randomizer__3drando}) +set(Header_Files__soh__Enhancements__custom_message + "soh/Enhancements/custom-message/CustomMessageTypes.h" + "soh/Enhancements/custom-message/CustomMessageManager.h" +) + +source_group("Header Files\\soh\\Enhancements\\custom-message" FILES ${Header_Files__soh__Enhancements__custom_message}) + set(Source_Files__soh "soh/GbiWrap.cpp" "soh/OTRAudio.h" @@ -326,6 +333,12 @@ set(Source_Files__soh__Enhancements__randomizer__3drando__location_access ) source_group("Source Files\\soh\\Enhancements\\randomizer\\3drando\\location_access" FILES ${Source_Files__soh__Enhancements__randomizer__3drando__location_access}) +set(Source_Files__soh__Enhancements__custom_message + "soh/Enhancements/custom-message/CustomMessageManager.cpp" +) + +source_group("Source Files\\soh\\Enhancements\\custom-message" FILES ${Source_Files__soh__Enhancements__custom_message}) + set(Source_Files__src__boot "src/boot/assert.c" "src/boot/boot_main.c" @@ -1534,6 +1547,7 @@ set(ALL_FILES ${Header_Files__soh__Enhancements__debugger} ${Header_Files__soh__Enhancements__randomizer} ${Header_Files__soh__Enhancements__randomizer__3drando} + ${Header_Files__soh__Enhancements__custom_message} ${Source_Files__soh} ${Source_Files__soh__Enhancements} ${Source_Files__soh__Enhancements__cosmetics} @@ -1542,6 +1556,7 @@ set(ALL_FILES ${Source_Files__soh__Enhancements__randomizer__3drando} ${Source_Files__soh__Enhancements__randomizer__3drando__hint_list} ${Source_Files__soh__Enhancements__randomizer__3drando__location_access} + ${Source_Files__soh__Enhancements__custom_message} ${Source_Files__src__boot} ${Source_Files__src__buffers} ${Source_Files__src__code} @@ -1805,7 +1820,6 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") $<$:-fpermissive> $<$:-Wno-deprecated-enum-enum-conversion> -pthread - -O3 -ffast-math ) target_link_options(${PROJECT_NAME} PRIVATE @@ -1979,13 +1993,16 @@ endif() if(CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch") -set_target_properties(soh PROPERTIES - APP_TITLE "Ship of Harkirian" - APP_AUTHOR "Ship" - APP_VERSION "3.0.0" - ICON "icon.jpg") - -nx_create_nro(soh) +nx_generate_nacp(Ship.nacp + NAME "Ship of Harkinian" + AUTHOR "Harbour Masters" + VERSION "3.1.0" +) + +nx_create_nro(soh + NACP Ship.nacp + ICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg +) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/soh.nro DESTINATION . COMPONENT ship) diff --git a/soh/include/z64effect.h b/soh/include/z64effect.h index 8dce5b6a3..861eb9070 100644 --- a/soh/include/z64effect.h +++ b/soh/include/z64effect.h @@ -25,7 +25,7 @@ typedef struct { /* 0x0C */ Vec3f position; /* 0x18 */ Vec3s unkVelocity; /* 0x1E */ Vec3s unkPosition; - /* 0x24 */ s32 epoch; + /* 0x24 */ u32 epoch; } EffectSparkElement; // size = 0x28 typedef struct { @@ -118,7 +118,7 @@ typedef struct { /* 0x10 */ f32 startX; /* 0x14 */ s16 yaw; /* 0x16 */ s16 pitch; - /* 0x18 */ s32 epoch; + /* 0x18 */ u32 epoch; } EffectShieldParticleElement; // size = 0x1C typedef struct { diff --git a/soh/include/z64player.h b/soh/include/z64player.h index 6e98b2846..2f2ac1008 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -638,6 +638,7 @@ typedef struct Player { /* 0x0A88 */ Vec3f unk_A88; // previous body part 0 position /* 0x0A89 */ bool pendingIceTrap; /* 0x0A95 */ PendingFlag pendingFlag; -} Player; // size = 0xAA1 + /* 0x0AA1 */ u8 boomerangQuickRecall; // Has the player pressed the boomerang button while it's in the air still? +} Player; // size = 0xAA2 #endif diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index c565ce52b..52e81ea24 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -1124,7 +1124,7 @@ void DrawCosmeticsEditor(bool& open) { return; } ImGui::SetNextWindowSize(ImVec2(465, 430), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("Cosmetics Editor", &open, ImGuiWindowFlags_NoFocusOnAppearing)) { + if (!ImGui::Begin("Cosmetics Editor", &open)) { ImGui::End(); return; } @@ -1162,7 +1162,7 @@ void InitCosmeticsEditor() { //This allow to hide a window without disturbing the player nor adding things in menu //LoadRainbowColor() will this way run in background once it's window is activated //ImGui::SetNextItemWidth(0.0f); - SohImGui::AddWindow("Cosmetics", "Rainbowfunction", LoadRainbowColor, true, true); + SohImGui::AddWindow("Enhancements", "Rainbowfunction", LoadRainbowColor, true, true); //Draw the bar in the menu. - SohImGui::AddWindow("Cosmetics", "Cosmetics Editor", DrawCosmeticsEditor); -} \ No newline at end of file + SohImGui::AddWindow("Enhancements", "Cosmetics Editor", DrawCosmeticsEditor); +} diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp new file mode 100644 index 000000000..d7dd08991 --- /dev/null +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp @@ -0,0 +1,150 @@ +#include "CustomMessageManager.h" +#include + +using namespace std::literals::string_literals; + +CustomMessageManager::CustomMessageManager() { + this->textBoxSpecialCharacters = { { "À", 0x80 }, { "î", 0x81 }, { "Â", 0x82 }, { "Ä", 0x83 }, { "Ç", 0x84 }, + { "È", 0x85 }, { "É", 0x86 }, { "Ê", 0x87 }, { "Ë", 0x88 }, { "Ï", 0x89 }, + { "Ô", 0x8A }, { "Ö", 0x8B }, { "Ù", 0x8C }, { "Û", 0x8D }, { "Ü", 0x8E }, + { "ß", 0x8F }, { "à", 0x90 }, { "á", 0x91 }, { "â", 0x92 }, { "ä", 0x93 }, + { "ç", 0x94 }, { "è", 0x95 }, { "é", 0x96 }, { "ê", 0x97 }, { "ë", 0x98 }, + { "ï", 0x99 }, { "ô", 0x9A }, { "ö", 0x9B }, { "ù", 0x9C }, { "û", 0x9D }, + { "ü", 0x9E } }; + this->colors = { { "w", QM_WHITE }, { "r", QM_RED }, { "g", QM_GREEN }, { "b", QM_BLUE }, + { "c", QM_LBLUE }, { "p", QM_PINK }, { "y", QM_YELLOW }, { "B", QM_BLACK } }; +} + +CustomMessageManager::~CustomMessageManager() { + this->textBoxSpecialCharacters.clear(); + this->colors.clear(); + this->messageTables.clear(); +} + +void CustomMessageManager::ReplaceSpecialCharacters(std::string& string) { + // add special characters + for (auto specialCharacterPair : this->textBoxSpecialCharacters) { + size_t start_pos = 0; + std::string textBoxSpecialCharacterString = ""s; + textBoxSpecialCharacterString += specialCharacterPair.second; + while ((start_pos = string.find(specialCharacterPair.first, 0)) != std::string::npos) { + string.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString); + start_pos += textBoxSpecialCharacterString.length(); + } + } +} + +void CustomMessageManager::ReplaceColors(std::string& string) { + for (auto colorPair : colors) { + std::string textToReplace = "%"; + textToReplace += colorPair.first; + size_t start_pos = 0; + while ((start_pos = string.find(textToReplace)) != std::string::npos) { + string.replace(start_pos, textToReplace.length(), COLOR(colorPair.second)); + start_pos += textToReplace.length(); + } + } +} + +void CustomMessageManager::FormatCustomMessage(std::string& message, ItemID iid) { + message.insert(0, ITEM_OBTAINED(iid)); + size_t start_pos = 0; + std::replace(message.begin(), message.end(), '&', NEWLINE()[0]); + while ((start_pos = message.find('^', start_pos)) != std::string::npos) { + message.replace(start_pos, 1, WAIT_FOR_INPUT() + ITEM_OBTAINED(iid)); + start_pos += 3; + } + std::replace(message.begin(), message.end(), '@', PLAYER_NAME()[0]); + ReplaceSpecialCharacters(message); + ReplaceColors(message); + message += MESSAGE_END(); +} + +void CustomMessageManager::FormatCustomMessage(std::string& message) { + size_t start_pos = 0; + std::replace(message.begin(), message.end(), '&', NEWLINE()[0]); + std::replace(message.begin(), message.end(), '^', WAIT_FOR_INPUT()[0]); + std::replace(message.begin(), message.end(), '@', PLAYER_NAME()[0]); + ReplaceSpecialCharacters(message); + ReplaceColors(message); + message += MESSAGE_END(); +} + +bool CustomMessageManager::InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages) { + auto foundMessageTable = messageTables.find(tableID); + if (foundMessageTable == messageTables.end()) { + return false; + } + auto& messageTable = foundMessageTable->second; + auto messageInsertResult = messageTable.emplace(textID, messages); + return messageInsertResult.second; +} + + + +bool CustomMessageManager::CreateGetItemMessage(std::string tableID, GetItemID giid, ItemID iid, CustomMessageEntry messageEntry) { + FormatCustomMessage(messageEntry.english, iid); + FormatCustomMessage(messageEntry.german, iid); + FormatCustomMessage(messageEntry.french, iid); + const uint16_t textID = giid; + return InsertCustomMessage(tableID, textID, messageEntry); +} + +bool CustomMessageManager::CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messageEntry) { + FormatCustomMessage(messageEntry.english); + FormatCustomMessage(messageEntry.german); + FormatCustomMessage(messageEntry.french); + return InsertCustomMessage(tableID, textID, messageEntry); +} + +CustomMessageEntry CustomMessageManager::RetrieveMessage(std::string tableID, uint16_t textID) { + std::unordered_map::const_iterator foundMessageTable = messageTables.find(tableID); + if (foundMessageTable == messageTables.end()) { + return NULL_CUSTOM_MESSAGE; + } + CustomMessageTable messageTable = foundMessageTable->second; + std::unordered_map::const_iterator foundMessage = messageTable.find(textID); + if (foundMessage == messageTable.end()) { + return NULL_CUSTOM_MESSAGE; + } + CustomMessageEntry message = foundMessage->second; + return message; +} + +bool CustomMessageManager::ClearMessageTable(std::string tableID) { + auto foundMessageTable = messageTables.find(tableID); + if (foundMessageTable == messageTables.end()) { + return false; + } + auto& messageTable = foundMessageTable->second; + messageTable.clear(); +} + +bool CustomMessageManager::AddCustomMessageTable(std::string tableID) { + CustomMessageTable newMessageTable; + return messageTables.emplace(tableID, newMessageTable).second; +} + +std::string CustomMessageManager::MESSAGE_END() { + return "\x02"s; +} + +std::string CustomMessageManager::ITEM_OBTAINED(uint8_t x) { + return "\x13"s + char(x); +} + +std::string CustomMessageManager::NEWLINE() { + return "\x01"s; +} + +std::string CustomMessageManager::COLOR(uint8_t x) { + return "\x05"s + char(x); +} + +std::string CustomMessageManager::WAIT_FOR_INPUT() { + return "\x04"s; +} + +std::string CustomMessageManager::PLAYER_NAME() { + return "\x0F"s; +} diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.h b/soh/soh/Enhancements/custom-message/CustomMessageManager.h new file mode 100644 index 000000000..0681073b8 --- /dev/null +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.h @@ -0,0 +1,133 @@ +#pragma once +#include +#include +#include "../../../include/z64item.h" + +#undef MESSAGE_END + +#define QM_WHITE 0x00 +#define QM_RED 0x41 +#define QM_GREEN 0x42 +#define QM_BLUE 0x43 +#define QM_LBLUE 0x44 +#define QM_PINK 0x45 +#define QM_YELLOW 0x46 +#define QM_BLACK 0x47 + +#ifndef MESSAGE_DATA_STATIC_H + +typedef enum { + /* 0 */ TEXTBOX_TYPE_BLACK, + /* 1 */ TEXTBOX_TYPE_WOODEN, + /* 2 */ TEXTBOX_TYPE_BLUE, + /* 3 */ TEXTBOX_TYPE_OCARINA, + /* 4 */ TEXTBOX_TYPE_NONE_BOTTOM, + /* 5 */ TEXTBOX_TYPE_NONE_NO_SHADOW, + /* 11 */ TEXTBOX_TYPE_CREDITS = 11 +} TextBoxType; + +typedef enum { + /* 0 */ TEXTBOX_BG_CROSS +} TextBoxBackground; + +typedef enum { + /* 0 */ TEXTBOX_POS_VARIABLE, + /* 1 */ TEXTBOX_POS_TOP, + /* 2 */ TEXTBOX_POS_MIDDLE, + /* 3 */ TEXTBOX_POS_BOTTOM +} TextBoxPosition; + +#endif + +typedef struct { + TextBoxType textBoxType; + TextBoxPosition textBoxPos; + std::string english; + std::string german; + std::string french; +} CustomMessageEntry; + +// Message Entry without the text type and position, useful for when +// you need an array of these to loop over for registration +// that will all have the same textbox type and position. +typedef struct { + std::string english; + std::string german; + std::string french; +} CustomMessageMinimal; + +#define NULL_CUSTOM_MESSAGE \ + { (TextBoxType)(-1), (TextBoxPosition)(-1), "", "", "" } + +typedef std::unordered_map CustomMessageTable; + +class CustomMessageManager { + private: + std::unordered_map textBoxSpecialCharacters; + std::unordered_map colors; + std::unordered_map messageTables; + + void ReplaceSpecialCharacters(std::string &string); + void ReplaceColors(std::string& string); + bool InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages); + + std::string MESSAGE_END(); + std::string ITEM_OBTAINED(uint8_t x); + std::string NEWLINE(); + std::string COLOR(uint8_t x); + std::string WAIT_FOR_INPUT(); + std::string PLAYER_NAME(); + + public: + static CustomMessageManager* Instance; + + CustomMessageManager(); + ~CustomMessageManager(); + + /* + Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, + with the provided giid (getItemID) as its key. This function also inserts the icon corresponding to + the provided iid (itemID) at the beginning of each page of the textbox. + */ + bool CreateGetItemMessage(std::string tableID, GetItemID giid, ItemID iid, CustomMessageEntry messages); + + /* + Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, + with the provided textID as its key. + */ + bool CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages); + + /* + Retrieves a message from the table with id tableID with the provided textID. + Returns a NULL_CUSTOM_MESSAGE if the message or table does not exist. + */ + CustomMessageEntry RetrieveMessage(std::string tableID, uint16_t textID); + + /* + Empties out the message table identified by tableID. + Returns true if successful and false if not (for instance + if a table with the provided tableID does not exist). + */ + bool ClearMessageTable(std::string tableID); + + /* + Creates an empty CustomMessageTable accessible at the provided + tableID, returns true if creation was successful and false + if not. + */ + bool AddCustomMessageTable(std::string tableID); + + /* + Replaces special characters and certain symbols with control codes + & for newline, ^ for wait-for-input, and @ for the player name, + as well as % for colors (i.e. %r for red and %w for white). + */ + void FormatCustomMessage(std::string& message, ItemID iid); + + /* + Replaces special characters and certain symbols with control codes + & for newline, ^ for wait-for-input, and @ for the player name, + as well as % for colors (i.e. %r for red and %w for white). + */ + void FormatCustomMessage(std::string& message); +}; diff --git a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h new file mode 100644 index 000000000..d2f30f123 --- /dev/null +++ b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h @@ -0,0 +1,33 @@ +#pragma once + +typedef enum { + TEXT_GS_NO_FREEZE = 0xB4, + TEXT_GS_FREEZE = 0xB5, + TEXT_RANDOMIZER_CUSTOM_ITEM = 0xF8, + TEXT_SCRUB_POH = 0x10A2, + TEXT_SCRUB_STICK_UPGRADE = 0x10DC, + TEXT_SCRUB_NUT_UPGRADE = 0x10DD, + TEXT_RANDOMIZER_GOSSIP_STONE_HINTS = 0x2053, + TEXT_ALTAR_CHILD = 0x7040, + TEXT_ALTAR_ADULT = 0x7088, + TEXT_GANONDORF = 0x70CC, + TEXT_GANONDORF_NOHINT = 0x70CD +} TextIDs; + +#ifdef __cplusplus + +typedef struct { + GetItemID giid; + ItemID iid; + std::string english; + std::string german; + std::string french; +} GetItemMessage; + +#define GIMESSAGE(giid, iid, english, german, french) \ + { giid, iid, english, german, french } + +#define GIMESSAGE_UNTRANSLATED(giid, iid, message) \ + { giid, iid, message, message, message } + +#endif diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 725c6e659..250f4e3f1 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -14,13 +14,20 @@ #include "3drando/rando_main.hpp" #include #include "Lib/ImGui/imgui_internal.h" +#include +#include using json = nlohmann::json; +using namespace std::literals::string_literals; std::unordered_map gSeedTextures; u8 generated; +const std::string Randomizer::getItemMessageTableID = "Randomizer"; +const std::string Randomizer::hintMessageTableID = "RandomizerHints"; +const std::string Randomizer::scrubMessageTableID = "RandomizerScrubs"; + Randomizer::Randomizer() { Sprite bowSprite = { dgFairyBowIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 0 }; gSeedTextures[0] = bowSprite; @@ -1399,14 +1406,15 @@ std::unordered_map SpoilerfileSettingNameToEn { "Open Settings:Random Ganon's Trials", RSK_RANDOM_TRIALS }, { "Open Settings:Trial Count", RSK_TRIAL_COUNT }, { "Shuffle Settings:Shuffle Cows", RSK_SHUFFLE_COWS }, + { "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS }, { "Start with Deku Shield", RSK_STARTING_DEKU_SHIELD }, { "Start with Kokiri Sword", RSK_STARTING_KOKIRI_SWORD }, { "Start with Fairy Ocarina", RSK_STARTING_OCARINA }, { "Shuffle Dungeon Items:Start with Maps/Compasses", RSK_STARTING_MAPS_COMPASSES }, { "Shuffle Dungeon Items:Ganon's Boss Key", RSK_GANONS_BOSS_KEY }, { "Misc Settings:Gossip Stone Hints", RSK_GOSSIP_STONE_HINTS }, - { "Misc Settings:Hint Clarity", RSK_HINT_CLARITY}, - { "Misc Settings:Hint Distribution", RSK_HINT_DISTRIBUTION}, + { "Misc Settings:Hint Clarity", RSK_HINT_CLARITY }, + { "Misc Settings:Hint Distribution", RSK_HINT_DISTRIBUTION }, { "Skip Child Zelda", RSK_SKIP_CHILD_ZELDA }, { "Start with Consumables", RSK_STARTING_CONSUMABLES }, { "Start with Max Rupees", RSK_FULL_WALLETS }, @@ -1482,6 +1490,26 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) { ParseHintLocationsFile(spoilerFileName); } + CustomMessageManager::Instance->ClearMessageTable(Randomizer::hintMessageTableID); + CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::hintMessageTableID); + + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD, + { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.childAltarText, + gSaveContext.childAltarText, gSaveContext.childAltarText }); + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT, + { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.adultAltarText, + gSaveContext.adultAltarText, gSaveContext.adultAltarText }); + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, TEXT_GANONDORF, + { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonHintText, + gSaveContext.ganonHintText, gSaveContext.ganonHintText }); + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT, + { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonText, + gSaveContext.ganonText, gSaveContext.ganonText }); + this->childAltarText = gSaveContext.childAltarText; this->adultAltarText = gSaveContext.adultAltarText; this->ganonHintText = gSaveContext.ganonHintText; @@ -1490,6 +1518,8 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) { for (auto hintLocation : gSaveContext.hintLocations) { if(hintLocation.check == RC_LINKS_POCKET) break; this->hintLocations[hintLocation.check] = hintLocation.hintText; + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, hintLocation.check, { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, hintLocation.hintText, hintLocation.hintText, hintLocation.hintText }); } } @@ -1702,6 +1732,18 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { } else if (it.value() == "Skip") { gSaveContext.randoSettings[index].value = 1; } + break; + case RSK_SHUFFLE_TOKENS: + if (it.value() == "Off") { + gSaveContext.randoSettings[index].value = 0; + } else if (it.value() == "Dungeons") { + gSaveContext.randoSettings[index].value = 1; + } else if (it.value() == "Overworld") { + gSaveContext.randoSettings[index].value = 2; + } else if (it.value() == "All Tokens") { + gSaveContext.randoSettings[index].value = 3; + } + break; } index++; } @@ -1802,57 +1844,8 @@ std::string AltarIconString(char iconChar) { std::string FormatJsonHintText(std::string jsonHint) { std::string formattedHintMessage = jsonHint; - char newLine = 0x01; - char playerName = 0x0F; - char nextBox = 0x04; - std::replace(formattedHintMessage.begin(), formattedHintMessage.end(), '&', newLine); - std::replace(formattedHintMessage.begin(), formattedHintMessage.end(), '^', nextBox); - std::replace(formattedHintMessage.begin(), formattedHintMessage.end(), '@', playerName); - std::unordered_map textBoxSpecialCharacters = { - {"À", 0x80 }, - {"î", 0x81 }, - {"Â", 0x82 }, - {"Ä", 0x83 }, - {"Ç", 0x84 }, - {"È", 0x85 }, - {"É", 0x86 }, - {"Ê", 0x87 }, - {"Ë", 0x88 }, - {"Ï", 0x89 }, - {"Ô", 0x8A }, - {"Ö", 0x8B }, - {"Ù", 0x8C }, - {"Û", 0x8D }, - {"Ü", 0x8E }, - {"ß", 0x8F }, - {"à", 0x90 }, - {"á", 0x91 }, - {"â", 0x92 }, - {"ä", 0x93 }, - {"ç", 0x94 }, - {"è", 0x95 }, - {"é", 0x96 }, - {"ê", 0x97 }, - {"ë", 0x98 }, - {"ï", 0x99 }, - {"ô", 0x9A }, - {"ö", 0x9B }, - {"ù", 0x9C }, - {"û", 0x9D }, - {"ü", 0x9E } - }; - - // add special characters - for (auto specialCharacterPair : textBoxSpecialCharacters) { - size_t start_pos = 0; - std::string textBoxSpecialCharacterString = ""; - textBoxSpecialCharacterString += specialCharacterPair.second; - while((start_pos = formattedHintMessage.find(specialCharacterPair.first, start_pos)) != std::string::npos) { - formattedHintMessage.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString); - start_pos += textBoxSpecialCharacterString.length(); - } - } + CustomMessageManager::Instance->FormatCustomMessage(formattedHintMessage); // add icons to altar text for (char iconChar : {'0', '1', '2', '3', '4', '5', '6', '7', '8', 'o', 'c', 'i', 'l', 'b', 'L', 'k'}) { @@ -2413,10 +2406,6 @@ std::string Randomizer::GetGanonHintText() const { return ganonHintText; } -std::string Randomizer::GetHintFromCheck(RandomizerCheck check) { - return this->hintLocations[check]; -} - u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) { return this->randoSettings[randoSettingKey]; } @@ -3449,7 +3438,8 @@ void GenerateRandomizerImgui() { // if we skip child zelda, we start with zelda's letter, and malon starts // at the ranch, so we should *not* shuffle the weird egg - cvarSettings[RSK_SHUFFLE_WEIRD_EGG] = ((CVar_GetS32("gRandomizeSkipChildZelda", 0) == 0) && CVar_GetS32("gRandomizeShuffleWeirdEgg", 0)); + cvarSettings[RSK_SHUFFLE_WEIRD_EGG] = ((CVar_GetS32("gRandomizeSkipChildZelda", 0) == 0) && + CVar_GetS32("gRandomizeShuffleWeirdEgg", 0)); cvarSettings[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD] = CVar_GetS32("gRandomizeShuffleGerudoToken", 0); cvarSettings[RSK_SHUFFLE_FROG_SONG_RUPEES] = CVar_GetS32("gRandomizeShuffleFrogSongRupees", 0); @@ -3477,7 +3467,10 @@ void GenerateRandomizerImgui() { cvarSettings[RSK_CUCCO_COUNT] = CVar_GetS32("gRandomizeCuccosToReturn", 7); cvarSettings[RSK_BIG_POE_COUNT] = CVar_GetS32("gRandomizeBigPoeTargetCount", 10); - cvarSettings[RSK_SKIP_CHILD_STEALTH] = CVar_GetS32("gRandomizeSkipChildStealth", 0); + // If we skip child zelda, skip child stealth is pointless, so this needs to be reflected in the spoiler log + cvarSettings[RSK_SKIP_CHILD_STEALTH] = + !CVar_GetS32("gRandomizeSkipChildZelda", 0) && CVar_GetS32("gRandomizeSkipChildStealth", 0); + cvarSettings[RSK_SKIP_EPONA_RACE] = CVar_GetS32("gRandomizeSkipEponaRace", 0); cvarSettings[RSK_SKIP_TOWER_ESCAPE] = CVar_GetS32("gRandomizeSkipTowerEscape", 0); @@ -4390,8 +4383,74 @@ void DrawRandoEditor(bool& open) { ImGui::End(); } +void CreateGetItemMessages(std::vector messageEntries) { + CustomMessageManager* customMessageManager = CustomMessageManager::Instance; + customMessageManager->AddCustomMessageTable(Randomizer::getItemMessageTableID); + for (GetItemMessage messageEntry : messageEntries) { + if (messageEntry.giid == GI_ICE_TRAP) { + customMessageManager->CreateMessage(Randomizer::getItemMessageTableID, messageEntry.giid, + { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, messageEntry.english, + messageEntry.german, messageEntry.french }); + } else { + customMessageManager->CreateGetItemMessage(Randomizer::getItemMessageTableID, messageEntry.giid, + messageEntry.iid, + { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, messageEntry.english, + messageEntry.german, messageEntry.french }); + } + } +} + +void CreateScrubMessages() { + CustomMessageManager* customMessageManager = CustomMessageManager::Instance; + customMessageManager->AddCustomMessageTable(Randomizer::scrubMessageTableID); + const std::vector prices = { 10, 40 }; + for (u8 price : prices) { + customMessageManager->CreateMessage(Randomizer::scrubMessageTableID, price, + { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, + "\x12\x38\x82\All right! You win! In return for&sparing me, I will sell you a&%gmysterious item%w!&%r" + + std::to_string(price) + " Rupees%w it is!\x07\x10\xA3", + // RANDTODO: Translate the below string to German. + "\x12\x38\x82\All right! You win! In return for&sparing me, I will sell you a&%gmysterious item%w!&%r" + + std::to_string(price) + " Rupees%w it is!\x07\x10\xA3", + "\x12\x38\x82J'abandonne! Tu veux bien m'acheter&un %gobjet mystérieux%w?&Ça fera %r" + + std::to_string(price) + " Rubis%w!\x07\x10\xA3" + }); + } +} + +void Randomizer::CreateCustomMessages() { + // RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED + // with GIMESSAGE(getItemID, itemID, english, german, french). + const std::vector getItemMessages = { + GIMESSAGE(GI_ICE_TRAP, ITEM_NONE, "\x08\x06\x30You are a %bFOWL%w!\x0E\x20", + "\x08\x06\x15 Du bist ein %bDUMMKOPF%w!\x0E\x20", "\x08\x06\x50%bIDIOT%w\x0E\x20"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE, + "You got a %rBottle with Blue &Fire%w! Use it to melt Red Ice!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BIG_POE, ITEM_BIG_POE, + "You got a %rBig Poe in a Bottle%w!&Sell it to the Ghost Shop!"), + GIMESSAGE_UNTRANSLATED( + GI_BOTTLE_WITH_BLUE_POTION, ITEM_POTION_BLUE, + "You got a %rBottle of Blue Potion%w!&Drink it to replenish your&%ghealth%w and %bmagic%w!"), + GIMESSAGE_UNTRANSLATED( + GI_BOTTLE_WITH_FISH, ITEM_FISH, + "You got a %rFish in a Bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BUGS, ITEM_BUG, + "You got a %rBug in a Bottle%w!&They love to burrow in&dirt holes!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_FAIRY, ITEM_FAIRY, "You got a %rFairy in a Bottle%w!&Use it wisely!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED, + "You got a %rBottle of Red Potion%w!&Drink it to replenish your&%ghealth%w!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_GREEN_POTION, ITEM_POTION_GREEN, + "You got a %rBottle of Green Potion%w!&Drink it to replenish your&%bmagic%w!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_POE, ITEM_POE, + "You got a %rPoe in a Bottle%w!&That creepy Ghost Shop might&be interested in this..."), + }; + CreateGetItemMessages(getItemMessages); + CreateScrubMessages(); +} + void InitRando() { SohImGui::AddWindow("Randomizer", "Randomizer Settings", DrawRandoEditor); + Randomizer::CreateCustomMessages(); } extern "C" { diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h index f4101e8fd..1994cfdc4 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -4,7 +4,8 @@ #include #include "../../../include/ultra64.h" #include "../../../include/z64item.h" -#include "soh/Enhancements/randomizer/randomizerTypes.h" +#include +#include class Randomizer { private: @@ -25,6 +26,10 @@ class Randomizer { Randomizer(); ~Randomizer(); + static const std::string getItemMessageTableID; + static const std::string hintMessageTableID; + static const std::string scrubMessageTableID; + static Sprite* GetSeedTexture(uint8_t index); s16 GetItemModelFromId(s16 itemId); s32 GetItemIDFromGetItemID(s32 getItemId); @@ -38,9 +43,9 @@ class Randomizer { std::string GetAdultAltarText() const; std::string GetGanonText() const; std::string GetGanonHintText() const; - std::string GetHintFromCheck(RandomizerCheck check); GetItemID GetRandomizedItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId); GetItemID GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum); + static void CreateCustomMessages(); }; #ifdef __cplusplus diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 48ecceb07..b85d092fb 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -32,7 +32,6 @@ #include "Enhancements/cosmetics/CosmeticsEditor.h" #include "Enhancements/debugconsole.h" #include "Enhancements/debugger/debugger.h" -#include "Enhancements/randomizer/randomizer.h" #include #include "Enhancements/n64_weird_frame_data.inc" #include "soh/frame_interpolation.h" @@ -40,6 +39,7 @@ #include "macros.h" #include #include "Hooks.h" +#include #ifdef __APPLE__ #include @@ -52,9 +52,12 @@ #endif #include +#include +#include OTRGlobals* OTRGlobals::Instance; SaveManager* SaveManager::Instance; +CustomMessageManager* CustomMessageManager::Instance; OTRGlobals::OTRGlobals() { context = Ship::GlobalCtx2::CreateInstance("Ship of Harkinian"); @@ -171,6 +174,7 @@ extern "C" void InitOTR() { #endif OTRGlobals::Instance = new OTRGlobals(); SaveManager::Instance = new SaveManager(); + CustomMessageManager::Instance = new CustomMessageManager(); auto t = OTRGlobals::Instance->context->GetResourceManager()->LoadFile("version"); if (!t->bHasLoadError) @@ -1431,99 +1435,39 @@ extern "C" RandomizerCheck Randomizer_GetCheckFromActor(s16 sceneNum, s16 actorI return OTRGlobals::Instance->gRandomizer->GetCheckFromActor(sceneNum, actorId, actorParams); } -extern "C" int CopyScrubMessage(u16 scrubTextId, char* buffer, const int maxBufferSize) { - std::string scrubText(""); - int language = CVar_GetS32("gLanguages", 0); +extern "C" CustomMessageEntry Randomizer_GetScrubMessage(u16 scrubTextId) { int price = 0; switch (scrubTextId) { - case 0x10A2: + case TEXT_SCRUB_POH: price = 10; break; - case 0x10DC: - case 0x10DD: + case TEXT_SCRUB_STICK_UPGRADE: + case TEXT_SCRUB_NUT_UPGRADE: price = 40; break; } - switch (language) { - case 0: default: - scrubText += 0x12; // add the sound - scrubText += 0x38; // sound id - scrubText += 0x82; // sound id - scrubText += "All right! You win! In return for"; - scrubText += 0x01; // newline - scrubText += "sparing me, I will sell you a"; - scrubText += 0x01; // newline - scrubText += 0x05; // change the color - scrubText += 0x42; // green - scrubText += "mysterious item"; - scrubText += 0x05; // change the color - scrubText += 0x40; // white - scrubText += "!"; - scrubText += 0x01; // newline - scrubText += 0x05; // change the color - scrubText += 0x41; // red - scrubText += std::to_string(price); - scrubText += price > 1 ? " Rupees" : " Rupee"; - scrubText += 0x05; // change the color - scrubText += 0x40; // white - scrubText += " it is!"; - scrubText += 0x07; // go to a new message - scrubText += 0x10; // message id - scrubText += 0xA3; // message id - break; - case 2: - scrubText += 0x12; // add the sound - scrubText += 0x38; // sound id - scrubText += 0x82; // sound id - scrubText += "J'abandonne! Tu veux bien m'acheter"; - scrubText += 0x01; // newline - scrubText += "un "; - scrubText += 0x05; // change the color - scrubText += 0x42; // green - scrubText += "objet myst\x96rieux"; - //scrubText += "; - scrubText += 0x05; // change the color - scrubText += 0x40; // white - scrubText += "?"; - scrubText += 0x01; // newline - scrubText += "\x84"; - scrubText += "a fera "; - scrubText += 0x05; // change the color - scrubText += 0x41; // red - scrubText += std::to_string(price) + " Rubis"; - scrubText += 0x05; // change the color - scrubText += 0x40; // white - scrubText += "!"; - scrubText += 0x07; // go to a new message - scrubText += 0x10; // message id - scrubText += 0xA3; // message id - break; - } - - return CopyStringToCharBuffer(scrubText, buffer, maxBufferSize); + return CustomMessageManager::Instance->RetrieveMessage(Randomizer::scrubMessageTableID, price); } -extern "C" int Randomizer_CopyAltarMessage(char* buffer, const int maxBufferSize) { - const std::string& altarText = (LINK_IS_ADULT) ? OTRGlobals::Instance->gRandomizer->GetAdultAltarText() - : OTRGlobals::Instance->gRandomizer->GetChildAltarText(); - return CopyStringToCharBuffer(altarText, buffer, maxBufferSize); +extern "C" CustomMessageEntry Randomizer_GetAltarMessage() { + return (LINK_IS_ADULT) + ? CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT) + : CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD); } -extern "C" int Randomizer_CopyGanonText(char* buffer, const int maxBufferSize) { - const std::string& ganonText = OTRGlobals::Instance->gRandomizer->GetGanonText(); - return CopyStringToCharBuffer(ganonText, buffer, maxBufferSize); +extern "C" CustomMessageEntry Randomizer_GetGanonText() { + return CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT); } -extern "C" int Randomizer_CopyGanonHintText(char* buffer, const int maxBufferSize) { - const std::string& ganonText = OTRGlobals::Instance->gRandomizer->GetGanonHintText(); - return CopyStringToCharBuffer(ganonText, buffer, maxBufferSize); +extern "C" CustomMessageEntry Randomizer_GetGanonHintText() { + return CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_GANONDORF); } -extern "C" int Randomizer_CopyHintFromCheck(RandomizerCheck check, char* buffer, const int maxBufferSize) { - // we don't want to make a copy of the std::string returned from GetHintFromCheck +extern "C" CustomMessageEntry Randomizer_GetHintFromCheck(RandomizerCheck check) { + // we don't want to make a copy of the std::string returned from GetHintFromCheck // so we're just going to let RVO take care of it - const std::string& hintText = OTRGlobals::Instance->gRandomizer->GetHintFromCheck(check); - return CopyStringToCharBuffer(hintText, buffer, maxBufferSize); + const CustomMessageEntry hintText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, check); + return hintText; } extern "C" s32 Randomizer_GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum) { @@ -1542,3 +1486,97 @@ extern "C" bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomize extern "C" bool Randomizer_ItemIsIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId) { return gSaveContext.n64ddFlag && Randomizer_GetItemIdFromKnownCheck(randomizerCheck, ogId) == GI_ICE_TRAP; } + +extern "C" CustomMessageEntry Randomizer_GetCustomGetItemMessage(GetItemID giid, char* buffer, const int maxBufferSize) { + const CustomMessageEntry getItemText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, giid); + return getItemText; +} + +extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) { + MessageContext* msgCtx = &globalCtx->msgCtx; + uint16_t textId = msgCtx->textId; + Font* font = &msgCtx->font; + char* buffer = font->msgBuf; + const int maxBufferSize = sizeof(font->msgBuf); + CustomMessageEntry messageEntry; + if (gSaveContext.n64ddFlag) { + if (textId == TEXT_RANDOMIZER_CUSTOM_ITEM) { + messageEntry = + Randomizer_GetCustomGetItemMessage((GetItemID)GET_PLAYER(globalCtx)->getItemId, buffer, maxBufferSize); + } else if (textId == TEXT_RANDOMIZER_GOSSIP_STONE_HINTS && Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) != 0 && + (Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 1 || + (Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 2 && + Player_GetMask(globalCtx) == PLAYER_MASK_TRUTH) || + (Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 3 && CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) { + + s16 actorParams = msgCtx->talkActor->params; + + // if we're in a generic grotto + if (globalCtx->sceneNum == 62 && actorParams == 14360) { + // look for the chest in the actorlist to determine + // which grotto we're in + int numOfActorLists = + sizeof(globalCtx->actorCtx.actorLists) / sizeof(globalCtx->actorCtx.actorLists[0]); + for (int i = 0; i < numOfActorLists; i++) { + if (globalCtx->actorCtx.actorLists[i].length) { + if (globalCtx->actorCtx.actorLists[i].head->id == 10) { + // set the params for the hint check to be negative chest params + actorParams = 0 - globalCtx->actorCtx.actorLists[i].head->params; + } + } + } + } + + RandomizerCheck hintCheck = + Randomizer_GetCheckFromActor(globalCtx->sceneNum, msgCtx->talkActor->id, actorParams); + + messageEntry = Randomizer_GetHintFromCheck(hintCheck); + } else if (textId == TEXT_ALTAR_CHILD || textId == TEXT_ALTAR_ADULT) { + // rando hints at altar + messageEntry = Randomizer_GetAltarMessage(); + } else if (textId == TEXT_GANONDORF) { + if (INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT) { + messageEntry = Randomizer_GetGanonText(); + } else { + messageEntry = Randomizer_GetGanonHintText(); + } + } else if (textId == TEXT_SCRUB_POH || textId == TEXT_SCRUB_STICK_UPGRADE || textId == TEXT_SCRUB_NUT_UPGRADE) { + messageEntry = Randomizer_GetScrubMessage(textId); + } + } + if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) { + if (CVar_GetS32("gInjectSkulltulaCount", 0) != 0) { + // The freeze text cannot be manually dismissed and must be auto-dismissed. + // This is fine and even wanted when skull tokens are not shuffled, but when + // when they are shuffled we don't want to be able to manually dismiss the box. + // Otherwise if we get a token from a chest or an NPC we get stuck in the ItemGet + // animation until the text box auto-dismisses. + // RANDOTODO: Implement a way to determine if an item came from a skulltula and + // inject the auto-dismiss control code if it did. + if (CVar_GetS32("gSkulltulaFreeze", 0) != 0 && + !(gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) > 0)) { + textId = TEXT_GS_NO_FREEZE; + } else { + textId = TEXT_GS_FREEZE; + } + messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId); + } + } + if (messageEntry.textBoxType != -1) { + font->charTexBuf[0] = (messageEntry.textBoxType << 4) | messageEntry.textBoxPos; + switch (gSaveContext.language) { + case LANGUAGE_FRA: + return msgCtx->msgLength = font->msgLength = + CopyStringToCharBuffer(messageEntry.french, buffer, maxBufferSize); + case LANGUAGE_GER: + return msgCtx->msgLength = font->msgLength = + CopyStringToCharBuffer(messageEntry.german, buffer, maxBufferSize); + + case LANGUAGE_ENG: + default: + return msgCtx->msgLength = font->msgLength = + CopyStringToCharBuffer(messageEntry.english, buffer, maxBufferSize); + } + } + return false; +} diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index fca7baea6..ae8f799b9 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -10,6 +10,8 @@ #include "Enhancements/savestates.h" #include "Enhancements/randomizer/randomizer.h" +const std::string customMessageTableID = "BaseGameOverrides"; + class OTRGlobals { public: @@ -91,10 +93,6 @@ Sprite* GetSeedTexture(uint8_t index); void Randomizer_LoadSettings(const char* spoilerFileName); u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 actorParams, s16 sceneNum); -int Randomizer_CopyAltarMessage(char* buffer, const int maxBufferSize); -int Randomizer_CopyHintFromCheck(RandomizerCheck check, char* buffer, const int maxBufferSize); -int Randomizer_CopyGanonText(char* buffer, const int maxBufferSize); -int Randomizer_CopyGanonHintText(char* buffer, const int maxBufferSize); void Randomizer_LoadHintLocations(const char* spoilerFileName); void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent); s16 Randomizer_GetItemModelFromId(s16 itemId); @@ -103,6 +101,7 @@ s32 Randomizer_GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s32 Randomizer_GetItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId); bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor); bool Randomizer_ItemIsIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId); +int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx); #endif #endif diff --git a/soh/soh/z_message_OTR.cpp b/soh/soh/z_message_OTR.cpp index 8c1d26a75..c6be43964 100644 --- a/soh/soh/z_message_OTR.cpp +++ b/soh/soh/z_message_OTR.cpp @@ -1,11 +1,13 @@ #include "OTRGlobals.h" #include "ResourceMgr.h" #include "Scene.h" -#include "message_data_static.h" #include "Utils/StringHelper.h" #include "global.h" #include "vt.h" #include +#include +#include +#include extern "C" MessageTableEntry* sNesMessageEntryTablePtr; extern "C" MessageTableEntry* sGerMessageEntryTablePtr; @@ -92,4 +94,22 @@ extern "C" void OTRMessage_Init() sStaffMessageEntryTablePtr[i].segment = file2->messages[i].msg.c_str(); sStaffMessageEntryTablePtr[i].msgSize = file2->messages[i].msg.size(); } -} \ No newline at end of file + + CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID); + CustomMessageManager::Instance->CreateGetItemMessage( + customMessageTableID, (GetItemID)TEXT_GS_NO_FREEZE, ITEM_SKULL_TOKEN, + { + TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, + "You got a %rGold Skulltula Token%w!&You've collected %r\x19%w tokens&in total!\x0E\x3C", + "Du erhälst ein %rGoldene&Skulltula-Symbol%w! Du hast&insgesamt %r\x19%w symbol gesammelt!\x0E\x3C", + "Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!\x0E\x3C" + } + ); + CustomMessageManager::Instance->CreateGetItemMessage( + customMessageTableID, (GetItemID)TEXT_GS_FREEZE, ITEM_SKULL_TOKEN, + { + TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, + "You got a %rGold Skulltula Token%w!&You've collected %r\x19%w tokens&in total!", + "Du erhälst ein %rGoldene&Skulltula-Symbol%w! Du hast&insgesamt %r\x19%w symbol gesammelt!", + "Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!" }); +} diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index c788a6e38..45e84a050 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -3961,7 +3961,7 @@ void Actor_DrawDoorLock(GlobalContext* globalCtx, s32 frame, s32 type) { f32 chainsTranslateX; f32 chainsTranslateY; f32 rotZStep; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; entry = &sDoorLocksInfo[type]; diff --git a/soh/src/code/z_demo.c b/soh/src/code/z_demo.c index 885446271..a447f549c 100644 --- a/soh/src/code/z_demo.c +++ b/soh/src/code/z_demo.c @@ -492,9 +492,10 @@ void Cutscene_Command_Terminator(GlobalContext* globalCtx, CutsceneContext* csCt s32 temp = 0; // Automatically skip certain cutscenes when in rando - // cmd->base == 33: Zelda escaping with impa cutscene // cmd->base == 8: Traveling back/forward in time cutscene - bool randoCsSkip = (gSaveContext.n64ddFlag && (cmd->base == 33 || cmd->base == 8)); + // cmd->base == 24: Dropping a fish for Jabu Jabu + // cmd->base == 33: Zelda escaping with impa cutscene + bool randoCsSkip = (gSaveContext.n64ddFlag && (cmd->base == 8 || cmd->base == 24 || cmd->base == 33)); bool debugCsSkip = (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_START) && (gSaveContext.fileNum != 0xFEDC) && CVar_GetS32("gDebugEnabled", 0)); @@ -2090,6 +2091,8 @@ void Cutscene_HandleConditionalTriggers(GlobalContext* globalCtx) { osSyncPrintf("\ngame_info.mode=[%d] restart_flag", ((void)0, gSaveContext.respawnFlag)); if ((gSaveContext.gameMode == 0) && (gSaveContext.respawnFlag <= 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) { + const bool bShouldTowerRandoSkip = + (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_TOWER_ESCAPE)); if ((gSaveContext.entranceIndex == 0x01E1) && !Flags_GetEventChkInf(0xAC)) { if (!gSaveContext.n64ddFlag) { Flags_SetEventChkInf(0xAC); @@ -2118,14 +2121,15 @@ void Cutscene_HandleConditionalTriggers(GlobalContext* globalCtx) { gSaveContext.entranceIndex = 0x0053; gSaveContext.cutsceneIndex = 0xFFF8; } - } else if (!Flags_GetEventChkInf(0xC7) && - (gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANON_DEMO)) { + } else if ((!Flags_GetEventChkInf(0xC7) && + gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANON_DEMO) || + (bShouldTowerRandoSkip && + gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANON_FINAL)) { Flags_SetEventChkInf(0xC7); gSaveContext.entranceIndex = 0x0517; - // If we are rando and tower escape skip is on, then set the flag to say we saw the towers fall // and exit. - if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_TOWER_ESCAPE)) { + if (bShouldTowerRandoSkip) { return; } gSaveContext.cutsceneIndex = 0xFFF0; diff --git a/soh/src/code/z_kankyo.c b/soh/src/code/z_kankyo.c index de8634f32..7fd9a7480 100644 --- a/soh/src/code/z_kankyo.c +++ b/soh/src/code/z_kankyo.c @@ -1459,7 +1459,7 @@ void Environment_DrawLensFlare(GlobalContext* globalCtx, EnvironmentContext* env LENS_FLARE_RING, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, }; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(gfxCtx); @@ -1642,7 +1642,7 @@ void Environment_DrawRain(GlobalContext* globalCtx, View* view, GraphicsContext* Vec3f unused = { 0.0f, 0.0f, 0.0f }; Vec3f windDirection = { 0.0f, 0.0f, 0.0f }; Player* player = GET_PLAYER(globalCtx); - static s32 epoch = 0; + static u32 epoch = 0; epoch++; if (!(globalCtx->cameraPtrs[0]->unk_14C & 0x100) && (globalCtx->envCtx.unk_EE[2] == 0)) { @@ -1925,7 +1925,7 @@ void Environment_DrawLightning(GlobalContext* globalCtx, s32 unused) { s32 pad[2]; Vec3f unused1 = { 0.0f, 0.0f, 0.0f }; Vec3f unused2 = { 0.0f, 0.0f, 0.0f }; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 400f0a275..071cc18a7 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -413,7 +413,7 @@ void HealthMeter_Draw(GlobalContext* globalCtx) { s32 curCombineModeSet = 0; u8* curBgImgLoaded = NULL; s32 ddHeartCountMinusOne = gSaveContext.inventory.defenseHearts - 1; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(gfxCtx); diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index 5c3b97da6..104b7f1e4 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -277,11 +277,15 @@ void Gameplay_Init(GameState* thisx) { u8 tempSetupIndex; s32 pad[2]; - if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_CHILD_STEALTH)) { + // Skip Child Stealth when option is enabled, Zelda's Letter isn't obtained and Impa's reward hasn't been received + // eventChkInf[4] & 1 = Got Zelda's Letter + // eventChkInf[5] & 0x200 = Got Impa's reward + // entranceIndex 0x7A, Castle Courtyard - Day from crawlspace + // entranceIndex 0x400, Zelda's Courtyard + if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_CHILD_STEALTH) && + !(gSaveContext.eventChkInf[4] & 1) && !(gSaveContext.eventChkInf[5] & 0x200)) { if (gSaveContext.entranceIndex == 0x7A) { gSaveContext.entranceIndex = 0x400; - } else if (gSaveContext.entranceIndex == 0x296) { - gSaveContext.entranceIndex = 0x23D; } } diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index ddc28450f..8aa46ceb4 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -799,12 +799,12 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) { gSaveContext.eventChkInf[1] |= (1 << 3); gSaveContext.eventChkInf[1] |= (1 << 4); + // Set "Got Zelda's Letter" flag. Also ensures Saria is back at SFM. TODO: Is this flag used for anything else? + gSaveContext.eventChkInf[4] |= 1; + // Got item from impa gSaveContext.eventChkInf[5] |= 0x200; - // make sure saria is at SFM - gSaveContext.eventChkInf[4] |= (1 << 0); - // set this at the end to ensure we always start with the letter // this is for the off chance we got the weird egg from impa (which should never happen) INV_CONTENT(ITEM_LETTER_ZELDA) = ITEM_LETTER_ZELDA; diff --git a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h index 401eb8b22..c25205729 100644 --- a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h +++ b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h @@ -24,7 +24,7 @@ typedef struct { /* 0x36 */ f32 pitch; /* 0x36 */ f32 yaw; /* 0x40 */ f32 roll; - /* 0x44 */ s32 epoch; + /* 0x44 */ u32 epoch; } BgDyYoseizoParticle; // size = 0x48 typedef struct BgDyYoseizo { diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.h b/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.h index 4198781b0..8ccac6a95 100644 --- a/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.h +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.h @@ -13,7 +13,7 @@ typedef struct { /* 0x0C */ Vec3f vel; /* 0x18 */ s16 rotVelX; /* 0x1A */ s16 rotVelY; - /* 0x1C */ s32 epoch; + /* 0x1C */ u32 epoch; } BgJyaMegamiPiece; // size = 0x20 typedef struct BgJyaMegami { diff --git a/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.c b/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.c index d74a39624..50b3e1fc4 100644 --- a/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.c +++ b/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.c @@ -261,7 +261,7 @@ void BgSpot00Hanebasi_DrawTorches(Actor* thisx, GlobalContext* globalCtx2) { GlobalContext* globalCtx = globalCtx2; f32 angle; s32 i; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.h b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.h index f920b44a6..ddc76960c 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.h +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.h @@ -17,7 +17,7 @@ typedef struct { /* 0x26 */ Color_RGB8 color; /* 0x2A */ s16 alpha; /* 0x2C */ f32 unk_2C; - /* 0x30 */ s32 epoch; + /* 0x30 */ u32 epoch; } BossDodongoEffect; // Size = 0x34 typedef struct BossDodongo { diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c index 8afb08ba1..b1bfeb5de 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c @@ -1835,7 +1835,7 @@ void BossFd_DrawBody(GlobalContext* globalCtx, BossFd* this) { s16 i; f32 temp_float; Mtx* tempMat = Graph_Alloc(globalCtx->state.gfxCtx, 18 * sizeof(Mtx)); - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.h b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.h index 3bc77b0b7..7133b78a6 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.h +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.h @@ -87,7 +87,7 @@ typedef struct BossFd2 { /* 0x1394 */ BossFd2Cam camData; /* 0x141C */ ColliderJntSph collider; /* 0x143C */ ColliderJntSphElement elements[9]; - /* 0x167C */ s32 epoch; + /* 0x167C */ u32 epoch; } BossFd2; // size = 0x1680 #endif diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c index 10e118c17..da84d0f5e 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -3356,7 +3356,7 @@ void BossGanon_DrawShock(BossGanon* this, GlobalContext* globalCtx) { s32 pad; GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; s16 i; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(gfxCtx); @@ -3463,7 +3463,7 @@ void BossGanon_DrawBigMagicCharge(BossGanon* this, GlobalContext* globalCtx) { f32 yRot; GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; s16 i; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(gfxCtx); @@ -4158,7 +4158,7 @@ void BossGanon_LightBall_Draw(Actor* thisx, GlobalContext* globalCtx) { s16 i; f32 alpha; s32 pad; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index ecc084be3..1f5455e24 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -2464,7 +2464,7 @@ void func_80904340(BossGanon2* this, GlobalContext* globalCtx) { f32 angle; f32 sin; f32 cos; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -2637,7 +2637,7 @@ void BossGanon2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dLis void func_80904D88(BossGanon2* this, GlobalContext* globalCtx) { s32 pad; s16 i; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -2701,7 +2701,7 @@ void func_80904FC8(BossGanon2* this, GlobalContext* globalCtx) { void func_8090523C(BossGanon2* this, GlobalContext* globalCtx) { Player* player; f32 phi_f20; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -2933,7 +2933,7 @@ void func_809060E8(GlobalContext* globalCtx) { BossGanon2Effect* effect; s16 i; BossGanon2Effect* effects; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; effects = effect = globalCtx->specialEffects; diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c index b7756765a..ae326d696 100644 --- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -2443,7 +2443,7 @@ void BossMo_DrawTentacle(BossMo* this, GlobalContext* globalCtx) { f32 phi_f20; f32 phi_f22; Vec3f sp110; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c index 1a1a93b07..31d1488a7 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -2706,7 +2706,7 @@ s32 BossSst_OverrideHandTrailDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** void BossSst_DrawHand(Actor* thisx, GlobalContext* globalCtx) { BossSst* this = (BossSst*)thisx; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.h b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.h index 32c12b7fd..ea287fb1b 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.h +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.h @@ -17,7 +17,7 @@ typedef struct { /* 0x0020 */ s16 move; /* 0x0022 */ s16 status; /* 0x0024 */ u8 alpha; - /* 0x0028 */ s32 epoch; + /* 0x0028 */ u32 epoch; } BossSstEffect; // size = 0x2C typedef struct { diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c index 1252df4bf..533420b06 100644 --- a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c @@ -3329,7 +3329,7 @@ void func_80942180(BossTw* this, GlobalContext* globalCtx) { void func_809426F0(BossTw* this, GlobalContext* globalCtx) { s32 pad; s16 i; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -4418,7 +4418,7 @@ void BossTw_BlastDraw(Actor* thisx, GlobalContext* globalCtx2) { f32 scaleFactor; s16 tailIdx; s16 i; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -4492,7 +4492,7 @@ void BossTw_DrawDeathBall(Actor* thisx, GlobalContext* globalCtx2) { f32 scaleFactor; s16 tailIdx; s16 i; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.h b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.h index 1a4a31438..5560f58d7 100644 --- a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.h +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.h @@ -43,7 +43,7 @@ typedef struct { /* 0x002E */ s16 work[EFF_WORK_MAX]; /* 0x0034 */ f32 workf[EFF_FWORK_MAX]; /* 0x0044 */ Actor* target; - s32 epoch; + u32 epoch; } BossTwEffect; typedef enum { diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c index b4236ef25..c8134866a 100644 --- a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c @@ -4006,7 +4006,7 @@ void BossVa_DrawDoor(GlobalContext* globalCtx, s16 scale) { f32 yScale; f32 segAngle = 0.0f; s32 i; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.c b/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.c index fd131a244..bd1bafc0a 100644 --- a/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.c +++ b/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.c @@ -564,7 +564,7 @@ void func_80967FFC(Actor* thisx, GlobalContext* globalCtx) { Demo6K* this = (Demo6K*)thisx; s32 pad; u16 timer1 = this->timer1; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -695,7 +695,7 @@ void func_809688C4(Actor* thisx, GlobalContext* globalCtx2) { GlobalContext* globalCtx = globalCtx2; u32 frames = globalCtx->state.frames; s32 i; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; if ((i = (globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[1] != NULL)) && diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c index 6ca6a025b..ca162f584 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c @@ -522,7 +522,7 @@ void DemoKankyo_DrawRain(Actor* thisx, GlobalContext* globalCtx) { f32 translateY; f32 translateZ; s16 j; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -663,7 +663,7 @@ void DemoKankyo_DrawClouds(Actor* thisx, GlobalContext* globalCtx) { f32 dx; f32 dy; f32 dz; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -784,7 +784,7 @@ void DemoKankyo_DrawWarpSparkles(Actor* thisx, GlobalContext* globalCtx) { f32 translateZ; PosRot posRot; u8 linkAge = gSaveContext.linkAge; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); @@ -933,7 +933,7 @@ void DemoKankyo_DrawSparkles(Actor* thisx, GlobalContext* globalCtx) { f32 scale; s16 i; PosRot posRot; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.h b/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.h index 6e7fa6693..596159815 100644 --- a/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.h +++ b/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.h @@ -19,7 +19,7 @@ typedef struct { /* 0x30 */ char unk_2C[4]; /* 0x34 */ f32 scale; /* 0x38 */ char unk_34[8]; - /* 0x3C */ s32 epoch; + /* 0x3C */ u32 epoch; } EfcErupcParticles; // size 0x40 #define EFC_ERUPC_NUM_PARTICLES 100 diff --git a/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.c b/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.c index e3af7ba4d..b9bd8e311 100644 --- a/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.c +++ b/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.c @@ -269,7 +269,7 @@ void EffDust_DrawFunc_8099E4F4(Actor* thisx, GlobalContext* globalCtx2) { f32* distanceTraveled; s32 i; f32 aux; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(gfxCtx); @@ -321,7 +321,7 @@ void EffDust_DrawFunc_8099E784(Actor* thisx, GlobalContext* globalCtx2) { s32 i; f32 aux; Player* player = GET_PLAYER(globalCtx); - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(gfxCtx); diff --git a/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.h b/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.h index 848bcdae9..aa6359c48 100644 --- a/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.h +++ b/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.h @@ -19,7 +19,7 @@ typedef struct EnAnubiceFire { /* 0x015E */ s16 unk_15E; /* 0x0178 */ Vec3f unk_160[6]; /* 0x01A8 */ ColliderCylinder cylinder; - /* 0x01F4 */ s32 epoch; + /* 0x01F4 */ u32 epoch; } EnAnubiceFire; // size = 0x01F8 #endif diff --git a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.h b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.h index a43c33e81..1996a09bc 100644 --- a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.h +++ b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.h @@ -32,7 +32,7 @@ typedef struct EnBa { /* 0x031C */ s16 unk31C; /* 0x0320 */ ColliderJntSph collider; /* 0x0340 */ ColliderJntSphElement colliderItems[2]; - /* 0x03C0 */ s32 epoch; + /* 0x03C0 */ u32 epoch; } EnBa; // size = 0x03C4 #endif diff --git a/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.c b/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.c index f041d55bd..e79d14109 100644 --- a/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.c +++ b/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.c @@ -167,12 +167,12 @@ void EnBoom_Fly(EnBoom* this, GlobalContext* globalCtx) { // Decrement the return timer and check if it's 0. If it is, check if Link can catch it and handle accordingly. // Otherwise handle grabbing and colliding. - if (DECR(this->returnTimer) == 0) { + if (DECR(this->returnTimer) == 0 || player->boomerangQuickRecall) { distFromLink = Math_Vec3f_DistXYZ(&this->actor.world.pos, &player->actor.focus.pos); this->moveTo = &player->actor; // If the boomerang is less than 40 units away from Link, he can catch it. - if (distFromLink < 40.0f) { + if (distFromLink < 40.0f || player->boomerangQuickRecall) { target = this->grabbed; if (target != NULL) { Math_Vec3f_Copy(&target->world.pos, &player->actor.world.pos); @@ -187,7 +187,8 @@ void EnBoom_Fly(EnBoom* this, GlobalContext* globalCtx) { } } // Set player flags and kill the boomerang beacause Link caught it. - player->stateFlags1 &= ~0x02000000; + player->stateFlags1 &= ~PLAYER_STATE1_25; + player->boomerangQuickRecall = false; Actor_Kill(&this->actor); } } else { diff --git a/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.h b/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.h index e842ce3a0..87f9370d6 100644 --- a/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.h +++ b/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.h @@ -16,7 +16,7 @@ typedef struct EnBx { /* 0x01B4 */ Vec3s unk_1B4[4]; /* 0x01CC */ ColliderCylinder collider; /* 0x0218 */ ColliderQuad colliderQuad; - /* 0x0298 */ s32 epoch; + /* 0x0298 */ u32 epoch; } EnBx; // size = 0x029C #endif diff --git a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h index 4c9b92584..f6328ea8d 100644 --- a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h +++ b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h @@ -88,7 +88,7 @@ typedef struct EnClearTagEffect { /* 0x0058 */ f32 rotationX; /* 0x005C */ f32 floorHeight; /* 0x0060 */ Vec3f floorTangent; - /* 0x006C */ s32 epoch; + /* 0x006C */ u32 epoch; } EnClearTagEffect; // size = 0x70 #define CLEAR_TAG_EFFECT_MAX_COUNT 100 diff --git a/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.h b/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.h index b4c7db491..9e3aca0eb 100644 --- a/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.h +++ b/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.h @@ -14,7 +14,7 @@ typedef struct { /* 0x0010 */ u8 isAlive; /* 0x0014 */ Vec3f moveDirection; /* 0x0020 */ Vec3f rot; - /* 0x002C */ s32 epoch; + /* 0x002C */ u32 epoch; } EnEncount2Particle; // size = 0x30 typedef struct EnEncount2 { diff --git a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.h b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.h index 7dc990a7e..0ee372369 100644 --- a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.h +++ b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.h @@ -25,7 +25,7 @@ typedef struct { /* 0x0014 */ Vec3f pos; /* 0x0020 */ Vec3f velocity; /* 0x002C */ Vec3f accel; - s32 epoch; + u32 epoch; } EnFdEffect; // size = 0x38 typedef struct EnFd { diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.h b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.h index 0230a2c3d..79279a71f 100644 --- a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.h +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.h @@ -19,7 +19,7 @@ typedef struct { /* 0x0014 */ Vec3f pos; /* 0x0020 */ Vec3f velocity; /* 0x002C */ Vec3f accel; - s32 epoch; + u32 epoch; } EnFwEffect; typedef struct EnFw { diff --git a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.h b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.h index 471a2fce2..7dab02bbc 100644 --- a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.h +++ b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.h @@ -21,7 +21,7 @@ typedef struct { /* 0x0030 */ f32 xyScale; // /* 0x0034 */ f32 xyScaleTarget; /* 0x0038 */ u8 isTimerMod8; // conditional, used to run CollisionCheck_SetAT - s32 epoch; + u32 epoch; } EnFzEffectSsIceSmoke; // size = 0x3C typedef struct EnFz { diff --git a/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.h b/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.h index 366a7d120..38b6de292 100644 --- a/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.h +++ b/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.h @@ -32,7 +32,7 @@ typedef struct { /* 0x12 */ u8 flag; /* 0x14 */ Vec3f velocity; /* 0x20 */ Vec3f rot; - s32 epoch; + u32 epoch; } EnGSwitchEffect; // size = 0x2C typedef struct EnGSwitch { diff --git a/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.h b/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.h index 48908d7e5..e72a33f14 100644 --- a/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.h +++ b/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.h @@ -27,7 +27,7 @@ typedef struct { /* 0x20 */ f32 unk_20; /* 0x24 */ f32 unk_24; /* 0x28 */ f32 unk_28; - s32 epoch; + u32 epoch; } EnGbCagedSoul; // size = 0x2C typedef struct EnGb { diff --git a/soh/src/overlays/actors/ovl_En_Go/z_en_go.h b/soh/src/overlays/actors/ovl_En_Go/z_en_go.h index 94bad5bab..e54d45bfc 100644 --- a/soh/src/overlays/actors/ovl_En_Go/z_en_go.h +++ b/soh/src/overlays/actors/ovl_En_Go/z_en_go.h @@ -34,7 +34,7 @@ typedef struct { /* 0x0014 */ Vec3f pos; /* 0x0020 */ Vec3f velocity; /* 0x002C */ Vec3f accel; - s32 epoch; + u32 epoch; } EnGoEffect; // size = 0x38 typedef struct EnGo { diff --git a/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c index d2d9fac4b..5a313a07f 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c +++ b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c @@ -112,14 +112,23 @@ void EnHeishi1_Init(Actor* thisx, GlobalContext* globalCtx) { } } + // eventChkInf[4] & 1 = Got Zelda's Letter + // eventChkInf[5] & 0x200 = Got item from impa + // eventChkInf[8] & 1 = Ocarina thrown in moat + bool metZelda = (gSaveContext.eventChkInf[4] & 1) && (gSaveContext.eventChkInf[5] & 0x200); + if (this->type != 5) { - if (((gSaveContext.dayTime < 0xB888) || IS_DAY) && (gSaveContext.n64ddFlag || !(gSaveContext.eventChkInf[8] & 1))) { + if ((gSaveContext.dayTime < 0xB888 || IS_DAY) && + ((!gSaveContext.n64ddFlag && !(gSaveContext.eventChkInf[8] & 1)) || + (gSaveContext.n64ddFlag && !metZelda))) { this->actionFunc = EnHeishi1_SetupWalk; } else { Actor_Kill(&this->actor); } } else { - if ((gSaveContext.dayTime >= 0xB889) || !IS_DAY || (!gSaveContext.n64ddFlag && (gSaveContext.eventChkInf[8] & 1))) { + if ((gSaveContext.dayTime >= 0xB889) || !IS_DAY || + (!gSaveContext.n64ddFlag && gSaveContext.eventChkInf[8] & 1) || + (gSaveContext.n64ddFlag && metZelda)) { this->actionFunc = EnHeishi1_SetupWaitNight; } else { Actor_Kill(&this->actor); diff --git a/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.h b/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.h index 78d52af33..5c481e5e6 100644 --- a/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.h +++ b/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.h @@ -18,7 +18,7 @@ typedef struct { /* 0x002C */ f32 scale; /* 0x0030 */ f32 unk_30; /* 0x0034 */ u8 timer; - s32 epoch; + u32 epoch; } EnNiwFeather; // size = 0x0038 typedef struct EnNiw { diff --git a/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.h b/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.h index fa57bb2f3..eb4d9fb1d 100644 --- a/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.h +++ b/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.h @@ -23,7 +23,7 @@ typedef struct EnNwcChick { /* 0x36 */ u16 height; /* 0x38 */ CollisionPoly* floorPoly; /* 0x44 */ char unk_3C[0x20]; - s32 epoch; + u32 epoch; } EnNwcChick; // size = 0x5C typedef struct EnNwc { diff --git a/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.h b/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.h index ebc358254..407a2d761 100644 --- a/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.h +++ b/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.h @@ -30,7 +30,7 @@ typedef struct EnNy { /* 0x01F0 */ f32 unk_1F0; /* 0x01F4 */ f32 unk_1F4; /* 0x01F8 */ Vec3f unk_1F8[16]; - s32 epoch; + u32 epoch; } EnNy; // size = 0x02B8 #endif diff --git a/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h b/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h index cc55a1dc9..0e9464738 100644 --- a/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h +++ b/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h @@ -29,7 +29,7 @@ typedef struct EnPoSisters { /* 0x029C */ LightInfo lightInfo; /* 0x02AC */ ColliderCylinder collider; /* 0x02F8 */ MtxF unk_2F8; - s32 epoch; + u32 epoch; } EnPoSisters; // size = 0x0338 #endif diff --git a/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c index 4b55990da..0abbb4cd3 100644 --- a/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c +++ b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c @@ -760,6 +760,14 @@ void func_80AEC2C0(EnRu1* this, GlobalContext* globalCtx) { func_80AEC070(this, globalCtx, something); } +// Convenience function used so that Ruto always spawns in Jabu in rando, even after she's been kidnapped +// Equivalent to !(gSaveContext.infTable[20] & 0x20) in vanilla +bool shouldSpawnRuto() { + // gSaveContext.infTable[20] & 0x40 check is to prevent Ruto from spawning during the short period of time when + // she's on the Zora's Sapphire pedestal but hasn't been kidnapped yet (would result in multiple Rutos otherwise) + return !(gSaveContext.infTable[20] & 0x20) || (gSaveContext.n64ddFlag && (gSaveContext.infTable[20] & 0x40)); +} + void func_80AEC320(EnRu1* this, GlobalContext* globalCtx) { s8 actorRoom; @@ -767,8 +775,7 @@ void func_80AEC320(EnRu1* this, GlobalContext* globalCtx) { func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); this->action = 7; EnRu1_SetMouthIndex(this, 1); - } else if ((gSaveContext.infTable[20] & 0x80) && !(gSaveContext.infTable[20] & 1) && - !(gSaveContext.infTable[20] & 0x20)) { + } else if ((gSaveContext.infTable[20] & 0x80) && !(gSaveContext.infTable[20] & 1) && shouldSpawnRuto()) { if (!func_80AEB020(this, globalCtx)) { func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); actorRoom = this->actor.room; @@ -1172,7 +1179,7 @@ void func_80AED414(EnRu1* this, GlobalContext* globalCtx) { void func_80AED44C(EnRu1* this, GlobalContext* globalCtx) { s8 actorRoom; - if ((gSaveContext.infTable[20] & 2) && !(gSaveContext.infTable[20] & 0x20) && !(gSaveContext.infTable[20] & 1) && + if ((gSaveContext.infTable[20] & 2) && shouldSpawnRuto() && !(gSaveContext.infTable[20] & 1) && !(gSaveContext.infTable[20] & 0x80)) { if (!func_80AEB020(this, globalCtx)) { func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); @@ -2179,7 +2186,7 @@ void func_80AEFF40(EnRu1* this, GlobalContext* globalCtx) { void func_80AEFF94(EnRu1* this, GlobalContext* globalCtx) { s8 actorRoom; - if ((gSaveContext.infTable[20] & 2) && (gSaveContext.infTable[20] & 1) && !(gSaveContext.infTable[20] & 0x20) && + if ((gSaveContext.infTable[20] & 2) && (gSaveContext.infTable[20] & 1) && shouldSpawnRuto() && (!(func_80AEB020(this, globalCtx)))) { func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); actorRoom = this->actor.room; diff --git a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c index 944a62850..73a29bf0b 100644 --- a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c +++ b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -108,6 +108,7 @@ void func_80AFB768(EnSi* this, GlobalContext* globalCtx) { giveItemId = sGetItemTable[getItemId - 1].itemId; Item_Give(globalCtx, giveItemId); } + player->getItemId = getItemId; } else { Item_Give(globalCtx, giveItemId); } @@ -150,6 +151,7 @@ void func_80AFB89C(EnSi* this, GlobalContext* globalCtx) { giveItemId = sGetItemTable[getItemId - 1].itemId; Item_Give(globalCtx, giveItemId); } + player->getItemId = getItemId; } else { Item_Give(globalCtx, giveItemId); } diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h b/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h index dfa493817..aa9b5dde8 100644 --- a/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h +++ b/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h @@ -18,7 +18,7 @@ typedef struct { /* 0x2C */ f32 unk_2C; /* 0x30 */ f32 unk_30; /* 0x34 */ u8 unk_34; - s32 epoch; + u32 epoch; } EnSyatekiNiw_1; // size = 0x38 typedef struct EnSyatekiNiw { diff --git a/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c b/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c index dad7d25e5..85e47a7e7 100644 --- a/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c +++ b/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c @@ -460,10 +460,13 @@ void func_80B14AF4(EnTa* this, GlobalContext* globalCtx) { void func_80B14B6C(EnTa* this, GlobalContext* globalCtx) { if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) { - OnePointCutscene_Init(globalCtx, 4175, -99, &this->actor, MAIN_CAM); + s16 csCamIdx = OnePointCutscene_Init(globalCtx, 4175, -99, &this->actor, MAIN_CAM); func_80B13AA0(this, func_80B14AF4, func_80B167C0); this->unk_2CC = 5; gSaveContext.eventChkInf[1] |= 0x10; + if (gSaveContext.n64ddFlag) { + OnePointCutscene_EndCutscene(globalCtx, csCamIdx); + } Animation_PlayOnce(&this->skelAnime, &gTalonRunTransitionAnim); this->currentAnimation = &gTalonRunAnim; } diff --git a/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.h b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.h index 82fb6eef3..1dff6a252 100644 --- a/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.h +++ b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.h @@ -17,7 +17,7 @@ typedef struct EnTkEff { /* 0x0014 */ Vec3f pos; /* 0x0020 */ Vec3f speed; /* 0x002C */ Vec3f accel; - s32 epoch; + u32 epoch; } EnTkEff; // size = 0x0038 struct EnTk; diff --git a/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.h b/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.h index d16613fcf..c6ca0eb80 100644 --- a/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.h +++ b/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.h @@ -56,7 +56,7 @@ typedef struct { /* 0x28 */ f32 scale; /* 0x2C */ f32 lerpFactor; /* 0x30 */ u8 state; - s32 epoch; + u32 epoch; } EnViewerFireEffect; // size = 0x34 typedef struct EnViewer { diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c index 5f404c4ad..1cb3b53e9 100644 --- a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -90,7 +90,7 @@ typedef struct { /* 0x32 */ s16 timer; /* 0x34 */ u8 shouldDraw; /* 0x38 */ f32 drawDistance; - s32 epoch; + u32 epoch; } FishingProp; // size = 0x3C typedef enum { @@ -116,7 +116,7 @@ typedef struct { /* 0x40 */ s16 unk_40; /* 0x42 */ s16 unk_42; /* 0x44 */ u8 shouldDraw; - s32 epoch; + u32 epoch; } FishingGroupFish; // size = 0x48 #define LINE_SEG_COUNT 200 @@ -1766,7 +1766,7 @@ static f32 sSinkingLureSizes[] = { void Fishing_DrawSinkingLure(GlobalContext* globalCtx) { s16 i; f32 scale; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; OPEN_DISPS(globalCtx->state.gfxCtx); diff --git a/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c b/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c index c332590dd..9ea675b55 100644 --- a/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c +++ b/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c @@ -482,7 +482,7 @@ void MirRay_Draw(Actor* thisx, GlobalContext* globalCtx) { s32 i; MirRayShieldReflection reflection[6]; s32 temp; - static s32 epoch = 0; + static u32 epoch = 0; epoch++; this->reflectIntensity = 0.0f; diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 5bf9394d7..1b70d3e4f 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -20,6 +20,7 @@ #include "objects/gameplay_keep/gameplay_keep.h" #include "objects/object_link_child/object_link_child.h" #include "textures/icon_item_24_static/icon_item_24_static.h" +#include #define GET_ITEM(itemId, objectId, drawId, textId, field, chestAnim) \ { itemId, field, (chestAnim != CHEST_ANIM_SHORT ? 1 : -1) * (drawId + 1), textId, objectId } @@ -138,10 +139,10 @@ s32 func_808353D8(Player* this, GlobalContext* globalCtx); s32 func_80835588(Player* this, GlobalContext* globalCtx); s32 func_808356E8(Player* this, GlobalContext* globalCtx); s32 func_80835800(Player* this, GlobalContext* globalCtx); -s32 func_80835884(Player* this, GlobalContext* globalCtx); -s32 func_808358F0(Player* this, GlobalContext* globalCtx); -s32 func_808359FC(Player* this, GlobalContext* globalCtx); -s32 func_80835B60(Player* this, GlobalContext* globalCtx); +s32 func_80835884(Player* this, GlobalContext* globalCtx); // Start aiming boomerang +s32 func_808358F0(Player* this, GlobalContext* globalCtx); // Aim boomerang +s32 func_808359FC(Player* this, GlobalContext* globalCtx); // Throw boomerang +s32 func_80835B60(Player* this, GlobalContext* globalCtx); // Boomerang active s32 func_80835C08(Player* this, GlobalContext* globalCtx); void func_80835F44(GlobalContext* globalCtx, Player* this, s32 item); void func_80839F90(Player* this, GlobalContext* globalCtx); @@ -479,8 +480,8 @@ static s32 D_80853604 = 0; static s32 D_80853608 = 0; static s32 D_8085360C = 0; static s16 D_80853610 = 0; -static s32 D_80853614 = 0; -static s32 D_80853618 = 0; +static s32 D_80853614 = 0; // Held item button just pressed? +static s32 D_80853618 = 0; // Held item button currently down? static u16 D_8085361C[] = { NA_SE_VO_LI_SWEAT, @@ -645,15 +646,15 @@ GetItemEntry sGetItemTable[] = { GET_ITEM(ITEM_DOUBLE_MAGIC, OBJECT_GI_MAGICPOT, GID_MAGIC_LARGE, 0xE8, 0x80, CHEST_ANIM_LONG), GET_ITEM(ITEM_DOUBLE_DEFENSE, OBJECT_GI_HEARTS, GID_HEART_CONTAINER, 0xE9, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_RED_POTION, OBJECT_GI_LIQUID, GID_POTION_RED, 0x43, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_GREEN_POTION, OBJECT_GI_LIQUID, GID_POTION_GREEN, 0x44, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_BLUE_POTION, OBJECT_GI_LIQUID, GID_POTION_BLUE, 0x45, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_FAIRY, OBJECT_GI_BOTTLE, GID_BOTTLE, 0x46, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_FISH, OBJECT_GI_FISH, GID_FISH, 0x47, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, 0x5D, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_BUGS, OBJECT_GI_INSECT, GID_BUG, 0x7A, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_POE, OBJECT_GI_GHOST, GID_POE, 0x97, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, 0xF9, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_RED_POTION, OBJECT_GI_LIQUID, GID_POTION_RED, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_GREEN_POTION, OBJECT_GI_LIQUID, GID_POTION_GREEN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_BLUE_POTION, OBJECT_GI_LIQUID, GID_POTION_BLUE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_FAIRY, OBJECT_GI_BOTTLE, GID_BOTTLE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_FISH, OBJECT_GI_FISH, GID_FISH, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_BUGS, OBJECT_GI_INSECT, GID_BUG, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_POE, OBJECT_GI_GHOST, GID_POE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), GET_ITEM_NONE, GET_ITEM_NONE, @@ -947,6 +948,7 @@ static s8 sItemActionParams[] = { static u8 sMaskMemory; +// Used to map action params to update functions static s32(*D_80853EDC[])(Player* this, GlobalContext* globalCtx) = { func_8083485C, func_8083485C, func_8083485C, func_808349DC, func_808349DC, func_808349DC, func_8083485C, func_8083485C, func_8083501C, func_8083501C, func_8083501C, func_8083501C, func_8083501C, func_8083501C, @@ -2721,6 +2723,10 @@ s32 func_80835B60(Player* this, GlobalContext* globalCtx) { return 1; } + if (D_80853614 && CVar_GetS32("gFastBoomerang", 0)) { + this->boomerangQuickRecall = true; + } + return 0; } diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.h b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.h index 93dc28031..8fe9efc02 100644 --- a/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.h +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.h @@ -11,7 +11,7 @@ typedef struct { /* 0x24 */ s16 scale; /* 0x26 */ s16 scaleStep; /* 0x28 */ u8 drawMode; - /* 0x29 */ s32 epoch; + /* 0x29 */ u32 epoch; } EffectSsBomb2InitParams; // size = 0x30 #endif diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c index 8bb4cc431..675fc1191 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c @@ -12,12 +12,12 @@ static s16 sEquipMoveTimer = 10; bool gSelectingMask; static s16 sAmmoVtxOffset[] = { - 0, 2, 4, 6, 99, 99, 8, 99, 99, 10, 99, 99, 99, 99, 99, 99, 12, + 0, 2, 4, 6, 99, 99, 8, 99, 10, 99, 99, 99, 99, 99, 12, }; extern const char* _gAmmoDigit0Tex[]; -void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx, s16 item) { +void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx, s16 item, int slot) { s16 ammo; s16 i; @@ -51,7 +51,7 @@ void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx, gDPPipeSync(POLY_KAL_DISP++); if (i != 0) { - gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[item] + 31) * 4], 4, 0); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[slot] + 31) * 4], 4, 0); gDPLoadTextureBlock(POLY_KAL_DISP++, ((u8*)_gAmmoDigit0Tex[i]), G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, @@ -60,7 +60,7 @@ void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx, gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); } - gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[item] + 32) * 4], 4, 0); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[slot] + 32) * 4], 4, 0); gDPLoadTextureBlock(POLY_KAL_DISP++, ((u8*)_gAmmoDigit0Tex[ammo]), G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, @@ -499,7 +499,7 @@ void KaleidoScope_DrawItemSelect(GlobalContext* globalCtx) { for (i = 0; i < 15; i++) { if ((gAmmoItems[i] != ITEM_NONE) && (gSaveContext.inventory.items[i] != ITEM_NONE)) { - KaleidoScope_DrawAmmoCount(pauseCtx, globalCtx->state.gfxCtx, gSaveContext.inventory.items[i]); + KaleidoScope_DrawAmmoCount(pauseCtx, globalCtx->state.gfxCtx, gSaveContext.inventory.items[i], i); } }