diff --git a/BUILDING.md b/BUILDING.md index 32e682705..6798b7362 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -186,9 +186,9 @@ cmake --build build-cmake --target ExtractAssets # Setup cmake project for building for Wii U cmake -H. -Bbuild-wiiu -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/WiiU.cmake # -DCMAKE_BUILD_TYPE:STRING=Release (if you're packaging) # Build project and generate rpx -cmake --build build-wiiu --target soh +cmake --build build-wiiu --target soh # --target soh_wuhb (for building .wuhb) -# Now you can run the executable in ./build-wiiu/soh/soh.rpx +# Now you can run the executable in ./build-wiiu/soh/soh.rpx or the Wii U Homebrew Bundle in ./build-wiiu/soh/soh.wuhb # To develop the project open the repository in VSCode (or your preferred editor) ``` diff --git a/Jenkinsfile b/Jenkinsfile index 55c4b4f28..4ca4820f7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -206,9 +206,10 @@ pipeline { docker exec sohwiiucont scripts/wiiu/build.sh mv build-wiiu/soh/*.rpx soh.rpx + mv build-wiiu/soh/*.wuhb soh.wuhb mv README.md readme.txt - 7z a soh-wiiu.7z soh.rpx readme.txt + 7z a soh-wiiu.7z soh.rpx soh.wuhb readme.txt ''' } diff --git a/libultraship/libultraship/CrashHandler.cpp b/libultraship/libultraship/CrashHandler.cpp index f454ddfdc..13c746027 100644 --- a/libultraship/libultraship/CrashHandler.cpp +++ b/libultraship/libultraship/CrashHandler.cpp @@ -12,6 +12,7 @@ extern "C" void DeinitOTR(void); #include // for dladdr #include #include +#include @@ -135,9 +136,11 @@ static void ErrorHandler(int sig, siginfo_t* sigInfo, void* data) { SPDLOG_CRITICAL("{} {}", i, functionName.c_str()); } - + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SoH has crashed", "SoH Has crashed. Please upload the logs to the support channel in discord.", nullptr); free(symbols); DeinitOTR(); + Ship::Window::GetInstance()->GetLogger()->flush(); + spdlog::shutdown(); exit(1); } diff --git a/libultraship/libultraship/ResourceMgr.cpp b/libultraship/libultraship/ResourceMgr.cpp index 0bf34ffe8..5e1d6f693 100644 --- a/libultraship/libultraship/ResourceMgr.cpp +++ b/libultraship/libultraship/ResourceMgr.cpp @@ -139,7 +139,7 @@ namespace Ship { ToLoad->resource = Res; ResourceCache[Res->file->path] = Res; - SPDLOG_DEBUG("Loaded Resource {} on ResourceMgr thread", ToLoad->file->path); + SPDLOG_TRACE("Loaded Resource {} on ResourceMgr thread", ToLoad->file->path); Res->file = nullptr; } else { diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index 36a10b505..ec97a71ad 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -540,13 +540,12 @@ namespace Ship { if (!ResMan->DidLoadSuccessfully()) { -#ifdef _WIN32 - MessageBox(nullptr, L"Main OTR file not found!", L"Uh oh", MB_OK); -#elif defined(__SWITCH__) +#if defined(__SWITCH__) printf("Main OTR file not found!\n"); #elif defined(__WIIU__) Ship::WiiU::ThrowMissingOTR(MainPath.c_str()); #else + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OTR file not found", "Main OTR file not found. Please generate one", nullptr); SPDLOG_ERROR("Main OTR file not found!"); #endif exit(1); diff --git a/scripts/wiiu/build.sh b/scripts/wiiu/build.sh index c9c54e5f6..fffd767cf 100755 --- a/scripts/wiiu/build.sh +++ b/scripts/wiiu/build.sh @@ -1,4 +1,4 @@ #!/bin/bash cmake -H. -Bbuild-wiiu -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/WiiU.cmake -DCMAKE_BUILD_TYPE:STRING=Release -cmake --build build-wiiu --target soh --config Release +cmake --build build-wiiu --target soh_wuhb --config Release diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index fcd66c93f..ac0b33ea8 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -186,6 +186,7 @@ set(Header_Files__soh__Enhancements__randomizer "soh/Enhancements/randomizer/adult_trade_shuffle.h" "soh/Enhancements/randomizer/randomizer_check_objects.h" "soh/Enhancements/randomizer/draw.h" + "soh/Enhancements/randomizer/rando_hash.h" ) source_group("Header Files\\soh\\Enhancements\\randomizer" FILES ${Header_Files__soh__Enhancements__randomizer}) @@ -2039,6 +2040,13 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "CafeOS") wut_create_rpx(${PROJECT_NAME}) -INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/soh.rpx DESTINATION . COMPONENT ship) +wut_create_wuhb(${PROJECT_NAME} + NAME "Ship of Harkinian" + SHORTNAME "SoH" + AUTHOR "Harbour Masters" + ICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg +) + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/soh.rpx ${CMAKE_CURRENT_BINARY_DIR}/soh.wuhb DESTINATION . COMPONENT ship) endif() diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 62fb0c78f..58fcc6715 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -4,6 +4,7 @@ #include #include "../../UIWidgets.hpp" +#include #include #include #include @@ -699,12 +700,14 @@ void DrawFlagArray32(const std::string& name, uint32_t& flags) { ImGui::PopID(); } -void DrawFlagArray16(const std::string& name, uint16_t& flags) { - ImGui::PushID(name.c_str()); +void DrawFlagArray16(const FlagTable& flagTable, uint16_t row, uint16_t& flags) { + ImGui::PushID((std::to_string(row) + flagTable.name).c_str()); for (int32_t flagIndex = 15; flagIndex >= 0; flagIndex--) { ImGui::SameLine(); ImGui::PushID(flagIndex); + bool hasDescription = !!flagTable.flagDescriptions.contains(row * 16 + flagIndex); uint32_t bitMask = 1 << flagIndex; + ImGui::PushStyleColor(ImGuiCol_FrameBg, hasDescription ? ImVec4(0.16f, 0.29f, 0.48f, 0.54f) : ImVec4(0.16f, 0.29f, 0.48f, 0.24f)); bool flag = (flags & bitMask) != 0; if (ImGui::Checkbox("##check", &flag)) { if (flag) { @@ -713,6 +716,12 @@ void DrawFlagArray16(const std::string& name, uint16_t& flags) { flags &= ~bitMask; } } + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered() && hasDescription) { + ImGui::BeginTooltip(); + ImGui::Text("%s", UIWidgets::WrappedText(flagTable.flagDescriptions.at(row * 16 + flagIndex), 60)); + ImGui::EndTooltip(); + } ImGui::PopID(); } ImGui::PopID(); @@ -927,122 +936,37 @@ void DrawFlagsTab() { } }); - if (ImGui::TreeNode("Event Check Inf Flags")) { - DrawGroupWithBorder([&]() { - ImGui::Text("0"); - UIWidgets::InsertHelpHoverText("Mostly Kokiri Forest related"); - DrawFlagArray16("eci0", gSaveContext.eventChkInf[0]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("1"); - UIWidgets::InsertHelpHoverText("Mostly Lon Lon Ranch related"); - DrawFlagArray16("eci1", gSaveContext.eventChkInf[1]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("2"); - UIWidgets::InsertHelpHoverText("Dodongo Related?"); - DrawFlagArray16("eci2", gSaveContext.eventChkInf[2]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("3"); - UIWidgets::InsertHelpHoverText("Mostly Zora related"); - DrawFlagArray16("eci3", gSaveContext.eventChkInf[3]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("4"); - UIWidgets::InsertHelpHoverText("Random"); - DrawFlagArray16("eci4", gSaveContext.eventChkInf[4]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("5"); - UIWidgets::InsertHelpHoverText("Mostly song learning related"); - DrawFlagArray16("eci5", gSaveContext.eventChkInf[5]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("6"); - UIWidgets::InsertHelpHoverText("Random"); - DrawFlagArray16("eci6", gSaveContext.eventChkInf[6]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("7"); - UIWidgets::InsertHelpHoverText("Boss Battle related"); - DrawFlagArray16("eci7", gSaveContext.eventChkInf[7]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("8"); - UIWidgets::InsertHelpHoverText("Mask related?"); - DrawFlagArray16("eci8", gSaveContext.eventChkInf[8]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("9"); - UIWidgets::InsertHelpHoverText("Mostly carpenter related"); - DrawFlagArray16("eci9", gSaveContext.eventChkInf[9]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("A"); - UIWidgets::InsertHelpHoverText("First-time overworld entrance cs related"); - DrawFlagArray16("eci10", gSaveContext.eventChkInf[10]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("B"); - UIWidgets::InsertHelpHoverText("First-time dungeon entrance cs/trial cs related"); - DrawFlagArray16("eci11", gSaveContext.eventChkInf[11]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("C"); - UIWidgets::InsertHelpHoverText("Random"); - DrawFlagArray16("eci12", gSaveContext.eventChkInf[12]); - }); - - DrawGroupWithBorder([&]() { - ImGui::Text("D"); - UIWidgets::InsertHelpHoverText("Frog songs/GS rewards"); - DrawFlagArray16("eci13", gSaveContext.eventChkInf[13]); - }); - - ImGui::TreePop(); - } - if (ImGui::TreeNode("Inf Table Flags")) { - for (int i = 0; i < 30; i++) { - std::string it_id = "it" + std::to_string(i); - DrawGroupWithBorder([&]() { - ImGui::Text("%2d", i); - DrawFlagArray16(it_id, gSaveContext.infTable[i]); - }); + for (int i = 0; i < flagTables.size(); i++) { + const FlagTable& flagTable = flagTables[i]; + if (flagTable.flagTableType == RANDOMIZER_INF && !gSaveContext.n64ddFlag) { + continue; } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Item Get Inf Flags")) { - for (int i = 0; i < 4; i++) { - std::string igi_id = "igi" + std::to_string(i); - DrawGroupWithBorder([&]() { - ImGui::Text("%d", i); - DrawFlagArray16(igi_id, gSaveContext.itemGetInf[i]); - }); + + if (ImGui::TreeNode(flagTable.name)) { + for (int j = 0; j < flagTable.size + 1; j++) { + DrawGroupWithBorder([&]() { + ImGui::Text(fmt::format("{:<2x}", j).c_str()); + switch (flagTable.flagTableType) { + case EVENT_CHECK_INF: + DrawFlagArray16(flagTable, j, gSaveContext.eventChkInf[j]); + break; + case ITEM_GET_INF: + DrawFlagArray16(flagTable, j, gSaveContext.itemGetInf[j]); + break; + case INF_TABLE: + DrawFlagArray16(flagTable, j, gSaveContext.infTable[j]); + break; + case EVENT_INF: + DrawFlagArray16(flagTable, j, gSaveContext.eventInf[j]); + break; + case RANDOMIZER_INF: + DrawFlagArray16(flagTable, j, gSaveContext.randomizerInf[j]); + break; + } + }); + } + ImGui::TreePop(); } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Event Inf Flags")) { - for (int i = 0; i < 4; i++) { - std::string ei_id = "ei" + std::to_string(i); - DrawGroupWithBorder([&]() { - ImGui::Text("%d", i); - DrawFlagArray16(ei_id, gSaveContext.eventInf[i]); - }); - } - ImGui::TreePop(); } } diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.h b/soh/soh/Enhancements/debugger/debugSaveEditor.h index 084f7cadc..e51414f4c 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.h +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.h @@ -1,3 +1,415 @@ #pragma once +#include +#include +#include +#include +#include + void InitSaveEditor(); + +typedef enum { + EVENT_CHECK_INF, + ITEM_GET_INF, + INF_TABLE, + EVENT_INF, + RANDOMIZER_INF, +} FlagTableType; + +typedef struct { + const char* name; + FlagTableType flagTableType; + uint32_t size; + std::map flagDescriptions; +} FlagTable; + +// Reference https://tcrf.net/Proto:The_Legend_of_Zelda:_Ocarina_of_Time_Master_Quest/Event_Editor +// The source was last referenced on 2022-09-03 and had a last updated value of 2020-05-02 +const std::vector flagTables = { + { "Event Check Inf Flags", EVENT_CHECK_INF, 0x0D, { + { 0x02, "First Spoke to Mido" }, + { 0x03, "Complained About Mido to Saria" }, + { 0x04, "Showed Mido Sword & Shield" }, + { 0x05, "Deku Tree Opened Mouth" }, + { 0x06, "Spoke to Saria After Deku Tree's Death" }, + { 0x07, "Obtained Kokiri Emerald & Deku Tree Dead" }, + { 0x09, "Used Deku Tree Blue Warp" }, + { 0x0A, "Played Saria's Song for Mido as Adult" }, + { 0x0C, "Met Deku Tree" }, + { 0x10, "Spoke to Child Malon at Castle or Market" }, + { 0x11, "Spoke to Ingo at Ranch before Talon returns" }, + { 0x12, "Obtained Pocket Egg" }, + { 0x13, "Woke Talon" }, + { 0x14, "Talon Fled Hyrule Castle" }, + { 0x15, "Spoke to Child Malon at Ranch" }, + { 0x16, "Invited to Sing With Child Malon" }, + { 0x17, "Great Deku Tree is Dead" }, + { 0x18, "Obtained Epona" }, + { 0x19, "Obtained Kokiri's Emerald" }, + { 0x1B, "Rented Horse From Ingo" }, + { 0x1C, "Spoke to Mido After Deku Tree's Death" }, + { 0x1D, "Destroyed the Royal Family's Tomb" }, + { 0x1E, "Won the Cow in Malon's Race" }, + { 0x23, "Bombed Dodongo's Cavern Entrance" }, + { 0x25, "Used Dodongo's Cavern Blue Warp" }, + { 0x2F, "Death Mountain Erupted" }, + { 0x30, "Spoke to a Zora" }, + { 0x31, "Obtained Ruto's Letter" }, + { 0x33, "King Zora Moved Aside" }, + { 0x37, "Used Jabu-Jabu Blue Warp" }, + { 0x38, "Obtained Silver Scale" }, + { 0x39, "Opened Entrance to Zora's Domain" }, + { 0x3A, "Offered Fish to Jabu-Jabu" }, + { 0x3B, "Began Nabooru Battle" }, + { 0x3C, "Finished Nabooru Battle" }, + { 0x40, "Obtained Zelda's Letter" }, + { 0x43, "Obtained Ocarina of Time" }, + { 0x45, "Pulled Master Sword from Pedestal" }, + { 0x48, "Used Forest Temple Blue Warp" }, + { 0x49, "Used Fire Temple Blue Warp" }, + { 0x4A, "Used Water Temple Blue Warp" }, + { 0x4B, "Opened the Door of Time" }, + { 0x4D, "Rainbow Bridge Built by Sages" }, + { 0x4E, "Caught by Hyrule Castle Guards" }, + { 0x4F, "Entered the Master Sword Chamber" }, + { 0x50, "Learned Minuet of Forest" }, + { 0x51, "Learned Bolero of Fire" }, + { 0x52, "Learned Serenade of Water" }, + { 0x54, "Learned Nocturne of Shadow" }, + { 0x55, "Sheik Moved From Sword Pedestal" }, + { 0x57, "Learned Saria's Song" }, + { 0x59, "Learned Zelda's Lullaby" }, + { 0x5A, "Learned Sun's Song" }, + { 0x5B, "Learned Song of Storms" }, + { 0x65, "Played Song of Storms in Windmill" }, + { 0x67, "Drained Well in Kakariko Village" }, + { 0x68, "Played Gerudo Archery Minigame" }, + { 0x69, "Restored Lake Hylia's Water" }, + { 0x6A, "Woke Talon in Kakariko" }, + { 0x6B, "Spoke to Talon After Saving Ranch" }, + { 0x6F, "Spoke to Kaepora Gaebora by Lost Woods" }, + { 0x70, "Began Gohma Battle" }, + { 0x71, "Began King Dodongo Battle" }, + { 0x72, "Began Phantom Ganon Battle" }, + { 0x73, "Began Volvagia Battle" }, + { 0x74, "Began Morpha Battle" }, + { 0x75, "Began Twinrova Battle" }, + { 0x76, "Began Barinade Battle" }, + { 0x77, "Began Bongo Bongo Battle" }, + { 0x78, "Began Ganondorf Battle" }, + { 0x80, "Zelda Fled Hyrule Castle" }, + { 0x82, "Bridge Unlocked (After Zelda Escape Cutscene)" }, + { 0x8C, "Paid Back Keaton Mask Fee" }, + { 0x8D, "Paid Back Skull Mask Fee" }, + { 0x8E, "Paid Back Spooky Mask Fee" }, + { 0x8F, "Paid Back Bunny Hood Fee" }, + { 0x90, "Rescued Red Carpenter" }, + { 0x91, "Rescued Yellow Carpenter" }, + { 0x92, "Rescued Blue Carpenter" }, + { 0x93, "Rescued Green Carpenter" }, + { 0x94, "Spoke to Nabooru in Spirit Temple" }, + { 0x95, "Nabooru Captured by Twinrova" }, + { 0x96, "Spoke to Cursed Man in Skulltula House" }, + { 0x9C, "Played Song for Scarecrow as Adult" }, + { 0xA0, "Entered Hyrule Field" }, + { 0xA1, "Entered Death Mountain Trail" }, + { 0xA3, "Entered Kakariko Village" }, + { 0xA4, "Entered Zora's Domain" }, + { 0xA5, "Entered Hyrule Castle" }, + { 0xA6, "Entered Goron City" }, + { 0xA7, "Entered Temple of Time" }, + { 0xA8, "Entered Deku Tree" }, + { 0xA9, "Learned Song of Time" }, + { 0xAA, "Bongo Bongo Escaped Well" }, + { 0xAC, "Learned Requiem of Spirit" }, + { 0xAD, "Completed Spirit Trial" }, + { 0xB0, "Entered Dodongo's Cavern" }, + { 0xB1, "Entered Lake Hylia" }, + { 0xB2, "Entered Gerudo Valley" }, + { 0xB3, "Entered Gerudo's Fortress" }, + { 0xB4, "Entered Lon Lon Ranch" }, + { 0xB5, "Entered Jabu-Jabu's Belly" }, + { 0xB6, "Entered Graveyard" }, + { 0xB7, "Entered Zora's Fountain" }, + { 0xB8, "Entered Desert Colossus" }, + { 0xB9, "Entered Death Mountain Crater" }, + { 0xBA, "Entered Ganon's Castle (Exterior)" }, + { 0xBB, "Completed Forest Trial" }, + { 0xBC, "Completed Water Trial" }, + { 0xBD, "Completed Shadow Trial" }, + { 0xBE, "Completed Fire Trial" }, + { 0xBF, "Completed Light Trial" }, + { 0xC0, "Nabooru Ordered to Fight by Twinrova" }, + { 0xC1, "Spoke to Saria on Lost Woods Bridge" }, + { 0xC3, "Dispelled Ganon's Tower Barrier" }, + { 0xC4, "Returned to Temple of Time With All Medallions" }, + { 0xC5, "Sheik, Spawned at Master Sword Pedestal as Adult" }, + { 0xC6, "Spoke to Deku Tree Sprout" }, + { 0xC7, "Watched Ganon's Tower Collapse / Caught by Gerudo" }, + { 0xC8, "Obtained Spirit Medallion" }, + { 0xC9, "Demo_Effect, Temple of Time Warp in blue aura + sfx the first time you spawn as Adult" }, + { 0xD0, "Obtained Frogs' Piece of Heart" }, + { 0xD1, "Played Zelda's Lullaby for Frogs" }, + { 0xD2, "Played Epona's Song for Frogs" }, + { 0xD3, "Played Sun's Song for Frogs" }, + { 0xD4, "Played Saria's Song for Frogs" }, + { 0xD5, "Played Song of Time for Frogs" }, + { 0xD6, "Played Song of Storms for Frogs" }, + { 0xDA, "Obtained Adult's Wallet" }, + { 0xDB, "Obtained Stone of Agony" }, + { 0xDC, "Obtained Giant's Wallet" }, + { 0xDD, "Obtained Skulltula House's Bombchu" }, + { 0xDE, "Obtained Skulltula House's Piece of Heart" }, + } }, + { "Item Get Inf Flags", ITEM_GET_INF, 0x03, { + { 0x02, "Obtained Super Cucco Bottle" }, + { 0x03, "Bought Bombchu (Shelf 2: Top Right)" }, + { 0x04, "Bought Bombchu (Shelf 1: Bot. Right)" }, + { 0x05, "Bought Bombchu (Shelf 1: Top Left)" }, + { 0x06, "Bought Bombchu (Shelf 2: Bot. Left)" }, + { 0x07, "Bought Bombchu (Shelf 2: Bot. Right)" }, + { 0x08, "Bought Bombchu (Shelf 1: Top Right)" }, + { 0x09, "Bought Bombchu (Shelf 1: Bot. Left)" }, + { 0x0A, "Bought Bombchu (Shelf 2: Top Left)" }, + { 0x0B, "Obtained Scrub's Heart Piece" }, + { 0x0C, "Obtained Cucco Lady's Bottle" }, + { 0x0D, "Obtained Bullet Bag Upgrade (Market)" }, + { 0x0E, "Obtained Big Quiver" }, + { 0x0F, "Obtained Biggest Quiver" }, + { 0x10, "Obtained Scientist's Heart Piece" }, + { 0x11, "Obtained Bowling Bomb Bag Upgrade" }, + { 0x12, "Obtained Bowling Heart Piece" }, + { 0x13, "Obtained Deku Seeds" }, + { 0x15, "Obtained Roof Guy's Heart Piece" }, + { 0x16, "Obtained Skullkid's Heart Piece" }, + { 0x17, "Obtained Skullkids' Heart Piece" }, + { 0x18, "Obtained Farore's Wind" }, + { 0x19, "Obtained Din's Fire" }, + { 0x1A, "Obtained Nayru's Love" }, + { 0x1C, "Obtained Grave-Dig Heart Piece" }, + { 0x1D, "Obtained Bullet Bag Upgrade (Woods)" }, + { 0x1E, "Obtained Deku Stick Upgrade (Stage)" }, + { 0x1F, "Obtained Deku Nut Upgrade (Stage)" }, + { 0x23, "Obtained Keaton Mask" }, + { 0x24, "Obtained Skull Mask" }, + { 0x25, "Obtained Spooky Mask" }, + { 0x26, "Obtained Bunny Hood" }, + { 0x2A, "Obtained Mask of Truth" }, + { 0x2C, "Obtained Pocket Egg" }, + { 0x2E, "Obtained Cojiro" }, + { 0x30, "Obtained Odd Potion" }, + { 0x31, "Obtained Poacher's Saw" }, + { 0x38, "Sold Keaton Mask" }, + { 0x39, "Sold Skull Mask" }, + { 0x3A, "Sold Spooky Mask" }, + { 0x3B, "Sold Bunny Hood" }, + { 0x3F, "Obtained Mask of Truth" }, + } }, + { "Inf Flags", INF_TABLE, 0x1D, { + { 0x00, "Greeted by Saria" }, + { 0x01, "Spoke to Saria About Obtaining Fairy" }, + { 0x03, "Complained About Mido to Saria" }, + { 0x05, "Spoke to Saria in Saria's House" }, + { 0x0C, "Mido Asked to See Sword & Shield" }, + { 0x15, "Spoke to Mido in Lost Woods as Adult" }, + { 0x19, "Told Mido Saria Won't Return" }, + { 0x1E, "Spoke to Kokiri Girl by Jumping Stones" }, + { 0x22, "Spoke to Kokiri Boy Guarding Forest Exit" }, + { 0x24, "Spoke to Kokiri Boy Cutting Grass" }, + { 0x26, "Spoke to Kokiri Girl on Shop Awning" }, + { 0x28, "Spoke to Kokiri Girl About Training Center" }, + { 0x31, "Spoke to Kokiri Boy on Bed in Mido's House" }, + { 0x51, "Spoke to Kokiri Girl in Saria's House" }, + { 0x59, "Spoke to Know-It-All Bro. About Temple" }, + { 0x61, "Spoke to Know-It-All Bro. About Saria" }, + { 0x66, "Spoke to Deku Tree Sprout After Cutscene" }, + { 0x6C, "Spoke to Dying Knight" }, + { 0x76, "Showed Zelda's Letter to Gate Guard" }, + { 0x77, "Gate Guard Put On Keaton Mask" }, + { 0x7E, "Spoke to Talon in Lon Lon Ranch House" }, + { 0x84, "Spoke to Child Malon at Castle or Market" }, + { 0x85, "Child Malon Said Epona Was Afraid of You" }, + { 0x8B, "Entered Hyrule Castle" }, + { 0x94, "Spoke to Ingo at Ranch Before Talon's Return" }, + { 0x97, "Spoke to Ingo at Ranch" }, + { 0x9A, "Spoke to Ingo as Adult" }, + { 0xA2, "Refused Ingo's 50 Rupee Rental Request" }, + { 0xAB, "Rode a Horse at Ingo's Ranch" }, + { 0xB0, "Spoke to Medigoron as Child" }, + { 0xB1, "Spoke to Medigoron as Adult" }, + { 0xB6, "Spoke to Poe Collector in Ruined Market" }, + { 0xB7, "Spoke to Fado in Kokiri Forest as Child" }, + { 0xB8, "Spoke to Malon After Saving Ranch" }, + { 0xB9, "Spoke to Malon on Horseback" }, + { 0xBC, "Spoke to Carpenter Boss by Tent" }, + { 0xC0, "Spoke to Fat Woman by Market Potion Shop" }, + { 0xC1, "Spoke to Fat Woman After Zelda's Escape" }, + { 0xC2, "Spoke to Burly Man About Talon Search" }, + { 0xC3, "Spoke to Burly Man After Zelda's Escape" }, + { 0xC4, "Spoke to Thief After Zelda's Escape" }, + { 0xC5, "Spoke to Thin Man by Market Target Shop" }, + { 0xC6, "Spoke to Old Woman by Market Fountain" }, + { 0xC7, "Spoke to Old Man by Bombchu Bowling" }, + { 0xC8, "Spoke to Thin Lady by Bombchu Bowling" }, + { 0xC9, "Spoke to Thin Lady After Zelda's Escape (1.0)" }, + { 0xCA, "Spoke to Red Joker in Market" }, + { 0xCB, "Spoke to Blue Joker in Market (1.0) / Dog Lady as Adult (DBG)" }, + { 0xCC, "Spoke to Itchy Lady After Malon Left Town" }, + { 0xCD, "Spoke to Blue Jokester in Market (DBG)" }, + { 0xCE, "Spoke to Thin Lady After Zelda's Escape (DBG)" }, + { 0xD9, "Spoke to Dampé as Child" }, + { 0xE0, "Spoke to Goron by Cavern" }, + { 0xE3, "Spoke to Goron Hiding Stick" }, + { 0xE6, "Spoke to Goron by Woods Exit" }, + { 0xEB, "Spoke to Goron by Bomb Flowers" }, + { 0xF0, "Spoke to Goron at City? Entrance" }, + { 0xF4, "Spoke to Ruby-Crazed Goron" }, + { 0xFC, "Spoke to Goron Shop Owner" }, + { 0x109, "Goron City Doors Unlocked" }, + { 0x10B, "Spoke to Goron Link About Volvagia" }, + { 0x10C, "Stopped Goron Link's Rolling" }, + { 0x10E, "Spoke to Goron Link" }, + { 0x113, "Spoke to Darunia in Goron City" }, + { 0x11A, "Spoke to Darunia in Fire Temple" }, + { 0x11E, "Obtained Bomb Bag Upgrade (Goron City)" }, + { 0x124, "Spoke to Zora Near Zora Shop?" }, + { 0x128, "Spoke to Zora Beside Zora Shop?" }, + { 0x129, "Spoke to Zora Swimming Behind Zora Shop" }, + { 0x138, "Thawed King Zora" }, + { 0x139, "Obtained Zora Tunic" }, + { 0x140, "Ruto in JJ (M7) on Blue Switch" }, + { 0x141, "Ruto in JJ (M2) Meet Ruto" }, + { 0x142, "Ruto in JJ (M3) Talk First Time" }, + { 0x143, "Ruto in JJ (M10) Can Be Escorted" }, + { 0x144, "Ruto in JJ (?) Wants to be Tossed to Sapphire" }, + { 0x145, "Ruto in JJ (M6) on Sapphire platform" }, + { 0x146, "Ruto in JJ (M6) Kidnapped" }, + { 0x147, "Ruto in JJ, Spawns on F1 Instead of B1" }, + { 0x160, "Spoke to Man in Impa's House During Day" }, + { 0x161, "Spoke to Man in Impa's House at Night" }, + { 0x162, "Spoke to Man in Impa's House as Adult" }, + { 0x163, "Spoke to Carpenter Boss's Wife as Child" }, + { 0x164, "Spoke to Carpenter Boss's Wife as Adult" }, + { 0x16C, "Refused Nabooru's Request" }, + { 0x170, "Spoke to Carpenter Boss in Valley" }, + { 0x172, "Spoke to Carpenter Boss in Kakariko" }, + { 0x176, "Spoke to Blue Carpenter in Tent" }, + { 0x178, "Spoke to Green Carpenter in Tent" }, + { 0x17F, "Running Man Suggested a Race" }, + { 0x190, "Obtained Gerudo Archery Piece of Heart" }, + { 0x191, "Obtained Dog Lady's Piece of Heart" }, + { 0x192, "Obtained Deku Stick Upgrade (Lost Woods)" }, + { 0x193, "Obtained Deku Nut Upgrade (Grotto)" }, + { 0x195, "Spoke to Kaepora in Lake Hylia" }, + { 0x196, "Spoke to Cursed Man With <10 Tokens" }, + { 0x197, "Spoke to Cursed Man With 10 Tokens" }, + { 0x198, "Obtained Small Magic Jar" }, + { 0x199, "Caught Cucco by Field Entrance" }, + { 0x19A, "Caught Cucco by Bazaar" }, + { 0x19B, "Caught Cucco by Cucco Pen" }, + { 0x19C, "Caught Cucco Behind Windmill" }, + { 0x19D, "Caught Cucco in Crate" }, + { 0x19E, "Caught Cucco by Skulltula House" }, + { 0x19F, "Caught Cucco Behind Potion Shop" }, + { 0x1A0, "Entered Deku Tree" }, + { 0x1A1, "Entered Dodongo's Cavern" }, + { 0x1A2, "Entered Jabu-Jabu's Belly" }, + { 0x1A3, "Entered Forest Temple" }, + { 0x1A4, "Entered Fire Temple" }, + { 0x1A5, "Entered Water Temple" }, + { 0x1A6, "Entered Spirit Temple" }, + { 0x1A7, "Entered Shadow Temple" }, + { 0x1A8, "Entered Bottom of the Well" }, + { 0x1A9, "Entered Ice Cavern" }, + { 0x1AA, "Entered Ganon's Tower" }, + { 0x1AB, "Entered Gerudo Training Ground" }, + { 0x1AC, "Entered Thieves' Hideout" }, + { 0x1AD, "Entered Ganon's Castle" }, + { 0x1AE, "Entered Ganon's Tower (Collapsing)" }, + { 0x1AF, "Entered Ganon's Castle (Collapsing)" }, + { 0x1D0, "No Sword on B" }, + } }, + { "Event Inf Flags", EVENT_INF, 0x03, { + { 0x00, "Rented Horse From Ingo" }, + { 0x01, "Racing Ingo" }, + { 0x02, "Won First Race With Ingo?" }, + { 0x05, "Lost Race With Ingo?" }, + { 0x06, "Racing Ingo For the Second Time" }, + { 0x08, "Finished Special Cucco Minigame / Began Gerudo Archery" }, + { 0x0A, "Won Special Cucco Minigame?" }, + { 0x0F, "Rented Horse From Ingo" }, + { 0x10, "Racing Running Man" }, + { 0x20, "Market Crowd Text Randomizer" }, + { 0x21, "Market Crowd Text Randomizer" }, + { 0x22, "Market Crowd Text Randomizer" }, + { 0x23, "Market Crowd Text Randomizer" }, + { 0x24, "Market Crowd Text Randomizer" }, + { 0x30, "Entered the Market" }, + } }, + { "Randomizer Inf Flags", RANDOMIZER_INF, 0x03, { + { RAND_INF_DUNGEONS_DONE_DEKU_TREE, "DUNGEONS_DONE_DEKU_TREE" }, + { RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN, "DUNGEONS_DONE_DODONGOS_CAVERN" }, + { RAND_INF_DUNGEONS_DONE_JABU_JABUS_BELLY, "DUNGEONS_DONE_JABU_JABUS_BELLY" }, + { RAND_INF_DUNGEONS_DONE_FOREST_TEMPLE, "DUNGEONS_DONE_FOREST_TEMPLE" }, + { RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE, "DUNGEONS_DONE_FIRE_TEMPLE" }, + { RAND_INF_DUNGEONS_DONE_WATER_TEMPLE, "DUNGEONS_DONE_WATER_TEMPLE" }, + { RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE, "DUNGEONS_DONE_SPIRIT_TEMPLE" }, + { RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE, "DUNGEONS_DONE_SHADOW_TEMPLE" }, + + { RAND_INF_TRIALS_DONE_LIGHT_TRIAL, "TRIALS_DONE_LIGHT_TRIAL" }, + { RAND_INF_TRIALS_DONE_FOREST_TRIAL, "TRIALS_DONE_FOREST_TRIAL" }, + { RAND_INF_TRIALS_DONE_FIRE_TRIAL, "TRIALS_DONE_FIRE_TRIAL" }, + { RAND_INF_TRIALS_DONE_WATER_TRIAL, "TRIALS_DONE_WATER_TRIAL" }, + { RAND_INF_TRIALS_DONE_SPIRIT_TRIAL, "TRIALS_DONE_SPIRIT_TRIAL" }, + { RAND_INF_TRIALS_DONE_SHADOW_TRIAL, "TRIALS_DONE_SHADOW_TRIAL" }, + + { RAND_INF_COWS_MILKED_LINKS_HOUSE_COW, "COWS_MILKED_LINKS_HOUSE_COW" }, + { RAND_INF_COWS_MILKED_HF_COW_GROTTO_COW, "COWS_MILKED_HF_COW_GROTTO_COW" }, + { RAND_INF_COWS_MILKED_LLR_STABLES_LEFT_COW, "COWS_MILKED_LLR_STABLES_LEFT_COW" }, + { RAND_INF_COWS_MILKED_LLR_STABLES_RIGHT_COW, "COWS_MILKED_LLR_STABLES_RIGHT_COW" }, + { RAND_INF_COWS_MILKED_LLR_TOWER_LEFT_COW, "COWS_MILKED_LLR_TOWER_LEFT_COW" }, + { RAND_INF_COWS_MILKED_LLR_TOWER_RIGHT_COW, "COWS_MILKED_LLR_TOWER_RIGHT_COW" }, + { RAND_INF_COWS_MILKED_KAK_IMPAS_HOUSE_COW, "COWS_MILKED_KAK_IMPAS_HOUSE_COW" }, + { RAND_INF_COWS_MILKED_DMT_COW_GROTTO_COW, "COWS_MILKED_DMT_COW_GROTTO_COW" }, + { RAND_INF_COWS_MILKED_GV_COW, "COWS_MILKED_GV_COW" }, + { RAND_INF_COWS_MILKED_JABU_JABUS_BELLY_MQ_COW, "COWS_MILKED_JABU_JABUS_BELLY_MQ_COW" }, + { RAND_INF_COWS_MILKED_HF_COW_GROTTO_GOSSIP_STONE, "COWS_MILKED_HF_COW_GROTTO_GOSSIP_STONE" }, + + { RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT, "SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT" }, + { RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS, "SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS" }, + { RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT, "SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT" }, + { RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY, "SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY" }, + { RAND_INF_SCRUBS_PURCHASED_JABU_JABUS_BELLY_DEKU_SCRUB, "SCRUBS_PURCHASED_JABU_JABUS_BELLY_DEKU_SCRUB" }, + { RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT, "SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT" }, + { RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT, "SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT" }, + { RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_RIGHT, "SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_RIGHT" }, + { RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_LEFT, "SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_LEFT" }, + { RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO, "SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO" }, + { RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_REAR, "SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_REAR" }, + { RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_FRONT, "SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_FRONT" }, + { RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_REAR, "SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_REAR" }, + { RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_FRONT, "SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_FRONT" }, + { RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_LEFT, "SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_LEFT" }, + { RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_RIGHT, "SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_RIGHT" }, + { RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_CENTER, "SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_CENTER" }, + { RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_REAR, "SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_REAR" }, + { RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_FRONT, "SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_FRONT" }, + { RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_REAR, "SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_REAR" }, + { RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT, "SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT" }, + { RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_LEFT, "SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_LEFT" }, + { RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_RIGHT, "SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_RIGHT" }, + { RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_CENTER, "SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_CENTER" }, + { RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_LEFT, "SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_LEFT" }, + { RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_RIGHT, "SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_RIGHT" }, + { RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_CENTER, "SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_CENTER" }, + { RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_LEFT, "SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_LEFT" }, + { RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_RIGHT, "SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_RIGHT" }, + { RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_CENTER, "SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_CENTER" }, + { RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_REAR, "SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_REAR" }, + { RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, "SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT" }, + { RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, "SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT" }, + { RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, "SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT" }, + { RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE, "SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE" }, + { RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB, "SCRUBS_PURCHASED_DMC_DEKU_SCRUB" }, + } }, +}; \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/3drando/menu.cpp b/soh/soh/Enhancements/randomizer/3drando/menu.cpp index 781dbead7..d566dd64a 100644 --- a/soh/soh/Enhancements/randomizer/3drando/menu.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/menu.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "cosmetics.hpp" @@ -541,8 +542,18 @@ std::string GenerateRandomizer(std::unordered_map } Settings::Keysanity.RestoreDelayedOption(); } - - return "./Randomizer/" + Settings::seed + ".json"; + std::ostringstream fileNameStream; + for (int i = 0; i < Settings::hashIconIndexes.size(); i++) { + if (i) { + fileNameStream << '-'; + } + if (Settings::hashIconIndexes[i] < 10) { + fileNameStream << '0'; + } + fileNameStream << std::to_string(Settings::hashIconIndexes[i]); + } + std::string fileName = fileNameStream.str(); + return "./Randomizer/" + fileName + ".json"; } std::string GetInput(const char* hintText) { diff --git a/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp b/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp index ee1a86424..87fba2e50 100644 --- a/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp @@ -39,6 +39,7 @@ int Playthrough_Init(uint32_t seed, std::unordered_map{}(Settings::seed + settingsStr); Random_Init(finalHash); + Settings::hash = std::to_string(finalHash); Logic::UpdateHelpers(); diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.cpp b/soh/soh/Enhancements/randomizer/3drando/settings.cpp index 0e925251f..5c5f17588 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.cpp @@ -20,6 +20,7 @@ using namespace SFX; namespace Settings { std::string seed; + std::string hash; std::string version = RANDOMIZER_VERSION "-" COMMIT_NUMBER; std::array hashIconIndexes; diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.hpp b/soh/soh/Enhancements/randomizer/3drando/settings.hpp index 2daf1fa7f..1498e36cf 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.hpp @@ -863,6 +863,7 @@ void UpdateSettings(std::unordered_map cvarSettin extern std::string seed; extern std::string version; extern std::array hashIconIndexes; + extern std::string hash; extern bool skipChildZelda; diff --git a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp index 461b54cb9..cffd5a115 100644 --- a/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/spoiler_log.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -36,12 +37,16 @@ namespace { std::string placementtxt; } // namespace -static RandomizerHash randomizerHash; static SpoilerData spoilerData; void GenerateHash() { - for (size_t i = 0; i < Settings::hashIconIndexes.size(); i++) { - int number = Settings::seed[i] - '0'; + std::string hash = Settings::hash; + // adds leading 0s to the hash string if it has less than 10 digits. + while (hash.length() < 10) { + hash = "0" + hash; + } + for (size_t i = 0, j = 0; i < Settings::hashIconIndexes.size(); i++, j += 2) { + int number = std::stoi(hash.substr(j, 2)); Settings::hashIconIndexes[i] = number; } @@ -49,20 +54,6 @@ void GenerateHash() { // spoilerData = { 0 }; } -const RandomizerHash& GetRandomizerHash() { - return randomizerHash; -} - -// Returns the randomizer hash as concatenated string, separated by comma. -const std::string GetRandomizerHashAsString() { - std::string hash = ""; - for (const std::string& str : randomizerHash) { - hash += str + ", "; - } - hash.erase(hash.length() - 2); // Erase last comma - return hash; -} - const SpoilerData& GetSpoilerData() { return spoilerData; } @@ -703,7 +694,6 @@ const char* SpoilerLog_Write(int language) { rootNode->SetAttribute("version", Settings::version.c_str()); rootNode->SetAttribute("seed", Settings::seed.c_str()); - rootNode->SetAttribute("hash", GetRandomizerHashAsString().c_str()); jsonData.clear(); @@ -739,12 +729,23 @@ const char* SpoilerLog_Write(int language) { } std::string jsonString = jsonData.dump(4); + std::ostringstream fileNameStream; + for (int i = 0; i < Settings::hashIconIndexes.size(); i ++) { + if (i) { + fileNameStream << '-'; + } + if (Settings::hashIconIndexes[i] < 10) { + fileNameStream << '0'; + } + fileNameStream << std::to_string(Settings::hashIconIndexes[i]); + } + std::string fileName = fileNameStream.str(); std::ofstream jsonFile(Ship::Window::GetPathRelativeToAppDirectory( - (std::string("Randomizer/") + std::string(Settings::seed) + std::string(".json")).c_str())); + (std::string("Randomizer/") + fileName + std::string(".json")).c_str())); jsonFile << std::setw(4) << jsonString << std::endl; jsonFile.close(); - return Settings::seed.c_str(); + return fileName.c_str(); } void PlacementLog_Msg(std::string_view msg) { @@ -764,7 +765,6 @@ bool PlacementLog_Write() { rootNode->SetAttribute("version", Settings::version.c_str()); rootNode->SetAttribute("seed", Settings::seed.c_str()); - rootNode->SetAttribute("hash", GetRandomizerHashAsString().c_str()); // WriteSettings(placementLog, true); // Include hidden settings. // WriteExcludedLocations(placementLog); diff --git a/soh/soh/Enhancements/randomizer/rando_hash.h b/soh/soh/Enhancements/randomizer/rando_hash.h new file mode 100644 index 000000000..236bf2460 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/rando_hash.h @@ -0,0 +1,111 @@ +#pragma once + +#include "randomizerTypes.h" +#include +#include "variables.h" +#include +#include +#include + +std::array gSeedTextures = { { + { dgDekuNutIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 0 }, + { dgDekuStickIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 1 }, + { dgBombIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 2 }, + { dgFairyBowIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 3 }, + { dgFireArrowIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 4 }, + { dgDinsFireIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 5 }, + { dgFairySlingshotIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 6 }, + { dgFairyOcarinaIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 7 }, + { dgOcarinaofTimeIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 8 }, + { dgBombchuIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 9 }, + { dgHookshotIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 10 }, + { dgLongshotIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 11 }, + { dgIceArrowIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 12 }, + { dgFaroresWindIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 13 }, + { dgBoomerangIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 14 }, + { dgLensofTruthIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 15 }, + { dgMagicBeansIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 16 }, + { dgMegatonHammerIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 17 }, + { dgLightArrowIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 18 }, + { dgNayrusLoveIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 19 }, + { dgEmptyBottleIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 20 }, + { dgRedPotionIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 21 }, + { dgGreenPotionIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 22 }, + { dgBluePotionIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 23 }, + { dgBottledFairyIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 24 }, + { dgFishIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 25 }, + { dgMilkFullIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 26 }, + { dgRutosLetterIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 27 }, + { dgBlueFireIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 28 }, + { dgBugIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 29 }, + { dgBigPoeIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 30 }, + { dgMilkhalfIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 31 }, + { dgPoeIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32 }, + { dgZeldasLetterIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 33 }, + { dgKeatonMaskIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 34 }, + { dgSkullMaskIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 35 }, + { dgSpookyMaskIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 36 }, + { dgBunnyHoodIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 37 }, + { dgGoronMaskIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 38 }, + { dgZoraMaskIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 39 }, + { dgGerudoMaskIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 40 }, + { dgMaskofTruthIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 41 }, + { dgSoldOutIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 42 }, + { dgPocketEggIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 43 }, + { dgPocketCuccoIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 44 }, + { dgCojiroIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 45 }, + { dgOddMushroomIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 46 }, + { dgOddPotionIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 47 }, + { dgPoachersSawIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 48 }, + { dgBrokenBiggoronSwordIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 49 }, + { dgPrescriptionIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 50 }, + { dgEyeBallFrogIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 51 }, + { dgEyeDropsIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 52 }, + { dgClaimCheckIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 53 }, + { dgKokiriSwordIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 54 }, + { dgMasterSwordIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 55 }, + { dgBiggoronSwordIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 56 }, + { dgDekuShieldIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 57 }, + { dgHylianShieldIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 58 }, + { dgMirrorShieldIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 59 }, + { dgKokiriTunicIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 60 }, + { dgGoronTunicIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 61 }, + { dgZoraTunicIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 62 }, + { dgKokiriBootsIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 63 }, + { dgIronBootsIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 64 }, + { dgHoverBootsIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 65 }, + { dgBulletBag30IconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 66 }, + { dgBulletBag40IconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 67 }, + { dgBulletBag50IconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 68 }, + { dgQuiver30IconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 69 }, + { dgBombBag20IconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 70 }, + { dgGoronsBraceletIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 71 }, + { dgSilverGauntletsIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 72 }, + { dgGoldenGauntletsIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 73 }, + { dgSilverScaleIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 74 }, + { dgGoldenScaleIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 75 }, + { dgBrokenGiantsKnifeIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 76 }, + { dgAdultsWalletIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 77 }, + { dgGiantsWalletIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 78 }, + { dgDekuSeedsIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 79 }, + { dgFishingPoleIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 80 }, + { dgForestMedallionIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 81 }, + { dgFireMedallionIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 82 }, + { dgWaterMedallionIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 83 }, + { dgSpiritMedallionIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 84 }, + { dgShadowMedallionIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 85 }, + { dgLightMedallionIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 86 }, + { dgKokiriEmeraldIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 87 }, + { dgGoronRubyIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 88 }, + { dgZoraSapphireIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 89 }, + { dgStoneOfAgonyIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 90 }, + { dgGerudosCardIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 91 }, + { dgGoldSkulltulaIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 92 }, + { dgHeartContainerIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 93 }, + { dgBossKeyIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 94 }, + { dgCompassIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 95 }, + { dgDungeonMapIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 96 }, + { dgSmallKeyIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 97 }, + { dgSmallMagicJarIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 98 }, + { dgBigMagicJarIconTex, 24, 24, G_IM_FMT_RGBA, G_IM_SIZ_32b, 99 }, +} }; \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 73ec9a41f..587df0028 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -20,11 +20,11 @@ #include "randomizer_check_objects.h" #include #include "draw.h" +#include "rando_hash.h" using json = nlohmann::json; using namespace std::literals::string_literals; -std::unordered_map gSeedTextures; std::unordered_map SpoilerfileCheckNameToEnum; std::unordered_map SpoilerfileGetNameToEnum; std::unordered_map> EnumToSpoilerfileGetName; @@ -66,36 +66,6 @@ static const char* frenchRupeeNames[36] = { }; Randomizer::Randomizer() { - Sprite bowSprite = { dgFairyBowIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 0 }; - gSeedTextures[0] = bowSprite; - - Sprite bombchuSprite = { dgBombchuIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 1 }; - gSeedTextures[1] = bombchuSprite; - - Sprite beansSprite = { dgMagicBeansIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 2 }; - gSeedTextures[2] = beansSprite; - - Sprite milkSprite = { dgMilkFullIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 3 }; - gSeedTextures[3] = milkSprite; - - Sprite frogSprite = { dgEyeBallFrogIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 4 }; - gSeedTextures[4] = frogSprite; - - Sprite mirrorShieldSprite = { dgMirrorShieldIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 5 }; - gSeedTextures[5] = mirrorShieldSprite; - - Sprite hoverBootsSprite = { dgHoverBootsIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 6 }; - gSeedTextures[6] = hoverBootsSprite; - - Sprite megatonHammerSprite = { dgMegatonHammerIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 7 }; - gSeedTextures[7] = megatonHammerSprite; - - Sprite silverGauntletsSprite = { dgSilverGauntletsIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 8 }; - gSeedTextures[8] = silverGauntletsSprite; - - Sprite ootOcarinaSprite = { dgOcarinaofTimeIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 9 }; - gSeedTextures[9] = ootOcarinaSprite; - for (auto areaIt : RandomizerCheckObjects::GetAllRCObjects()) { for (auto locationIt : areaIt.second) { SpoilerfileCheckNameToEnum[locationIt.rcSpoilerName] = locationIt.rc; diff --git a/soh/soh/GameMenuBar.cpp b/soh/soh/GameMenuBar.cpp index 13a945cfc..c0224cee4 100644 --- a/soh/soh/GameMenuBar.cpp +++ b/soh/soh/GameMenuBar.cpp @@ -238,6 +238,10 @@ namespace GameMenuBar { CVar_SetS32("gInjectSkulltulaCount", 0); // Pull grave during the day CVar_SetS32("gDayGravePull", 0); + // Blue Fire Arrows + CVar_SetS32("gBlueFireArrows", 0); + // Sunlight Arrows + CVar_SetS32("gSunlightArrows", 0); // Rotate link (0 to 2) CVar_SetS32("gPauseLiveLinkRotation", 0); @@ -655,8 +659,6 @@ namespace GameMenuBar { UIWidgets::Tooltip("Allows the D-pad to be used as extra C buttons"); UIWidgets::PaddedEnhancementCheckbox("Allow the cursor to be on any slot", "gPauseAnyCursor", true, false); UIWidgets::Tooltip("Allows the cursor on the pause menu to be over any slot\nSimilar to Rando and Spaceworld 97"); - UIWidgets::PaddedEnhancementCheckbox("Prevent Dropped Ocarina Inputs", "gDpadNoDropOcarinaInput", true, false); - UIWidgets::Tooltip("Prevent dropping inputs when playing the ocarina quickly"); UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL", true, false); UIWidgets::Tooltip("Speak to Navi with L but enter first-person camera with C-Up"); ImGui::EndMenu(); @@ -680,8 +682,6 @@ namespace GameMenuBar { UIWidgets::Tooltip("Prevent forced Navi conversations"); UIWidgets::PaddedEnhancementCheckbox("No Skulltula Freeze", "gSkulltulaFreeze", true, false); UIWidgets::Tooltip("Stops the game from freezing the player when picking up Gold Skulltulas"); - UIWidgets::PaddedEnhancementCheckbox("MM Bunny Hood", "gMMBunnyHood", true, false); - UIWidgets::Tooltip("Wearing the Bunny Hood grants a speed increase like in Majora's Mask"); UIWidgets::PaddedEnhancementCheckbox("Fast Chests", "gFastChests", true, false); UIWidgets::Tooltip("Kick open every chest"); UIWidgets::PaddedEnhancementCheckbox("Skip Pickup Messages", "gFastDrops", true, false); @@ -697,15 +697,26 @@ namespace GameMenuBar { UIWidgets::PaddedEnhancementCheckbox("Skip Scarecrow Song", "gSkipScarecrow", true, false, forceSkipScarecrow, forceSkipScarecrowText, UIWidgets::CheckboxGraphics::Checkmark); UIWidgets::Tooltip("Pierre appears when Ocarina is pulled out. Requires learning scarecrow song."); + UIWidgets::PaddedEnhancementCheckbox("Remember Save Location", "gRememberSaveLocation", true, false); + UIWidgets::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(); + } + + UIWidgets::Spacer(0); + + if (ImGui::BeginMenu("Items")) + { UIWidgets::PaddedEnhancementCheckbox("Instant Putaway", "gInstantPutaway", true, false); UIWidgets::Tooltip("Allow Link to put items away without having to wait around"); UIWidgets::PaddedEnhancementCheckbox("Instant Boomerang Recall", "gFastBoomerang", true, false); UIWidgets::Tooltip("Instantly return the boomerang to Link by pressing its item button while it's in the air"); + UIWidgets::PaddedEnhancementCheckbox("Prevent Dropped Ocarina Inputs", "gDpadNoDropOcarinaInput", true, false); + UIWidgets::Tooltip("Prevent dropping inputs when playing the ocarina quickly"); + UIWidgets::PaddedEnhancementCheckbox("MM Bunny Hood", "gMMBunnyHood", true, false); + UIWidgets::Tooltip("Wearing the Bunny Hood grants a speed increase like in Majora's Mask"); UIWidgets::PaddedEnhancementCheckbox("Mask Select in Inventory", "gMaskSelect", true, false); UIWidgets::Tooltip("After completing the mask trading sub-quest, press A and any direction on the mask slot to change masks"); - UIWidgets::PaddedEnhancementCheckbox("Remember Save Location", "gRememberSaveLocation", true, false); - UIWidgets::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(); } @@ -884,6 +895,10 @@ namespace GameMenuBar { UIWidgets::Tooltip("Injects Golden Skulltula total count in pickup messages"); UIWidgets::PaddedEnhancementCheckbox("Pull grave during the day", "gDayGravePull", true, false); UIWidgets::Tooltip("Allows graves to be pulled when child during the day"); + UIWidgets::PaddedEnhancementCheckbox("Blue Fire Arrows", "gBlueFireArrows", true, false); + UIWidgets::Tooltip("Allows Ice Arrows to melt red ice.\nMay require a room reload if toggled during gameplay."); + UIWidgets::PaddedEnhancementCheckbox("Sunlight Arrows", "gSunlightArrows", true, false); + UIWidgets::Tooltip("Allows Light Arrows to activate sun switches.\nMay require a room reload if toggled during gameplay."); ImGui::EndMenu(); } diff --git a/soh/src/boot/build.c b/soh/src/boot/build.c index f6b3a151f..f4cb1c21b 100644 --- a/soh/src/boot/build.c +++ b/soh/src/boot/build.c @@ -1,4 +1,4 @@ -const char gBuildVersion[] = "ZHORA ALFA (3.1.0)"; +const char gBuildVersion[] = "ZHORA ALFA (4.0.0)"; const char gBuildTeam[] = "github.com/harbourmasters"; const char gBuildDate[] = __DATE__ " " __TIME__; const char gBuildMakeOption[] = ""; diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index d40a0de7a..1612db28f 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -218,7 +218,8 @@ void GivePlayerRandoRewardNocturne(GlobalContext* globalCtx, RandomizerCheck che !Player_InBlockingCsMode(globalCtx, player) && !Flags_GetEventChkInf(0xAA)) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_NOCTURNE_OF_SHADOW); GiveItemEntryWithoutActor(globalCtx, getItemEntry); - Flags_SetEventChkInf(0xAA); + player->pendingFlag.flagID = 0xAA; + player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF; } } @@ -230,7 +231,8 @@ void GivePlayerRandoRewardRequiem(GlobalContext* globalCtx, RandomizerCheck chec !Player_InBlockingCsMode(globalCtx, player)) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_SONG_OF_TIME); GiveItemEntryWithoutActor(globalCtx, getItemEntry); - Flags_SetEventChkInf(0xAC); + player->pendingFlag.flagID = 0xAC; + player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF; } } } diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 2fdaff7c8..8c8ac2711 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -104,7 +104,7 @@ void GiveLinksPocketItem() { gSaveContext.bgsFlag = true; } Item_Give(NULL, getItemEntry.itemId); - } else if (getItemEntry.modIndex == MOD_RANDOMIZER) { + } else if (getItemEntry.modIndex == MOD_RANDOMIZER && getItemEntry.getItemId != RG_ICE_TRAP) { Randomizer_Item_Give(NULL, getItemEntry); } } @@ -406,7 +406,7 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) { gSaveContext.bgsFlag = true; } Item_Give(NULL, getItem.itemId); - } else if (getItem.modIndex == MOD_RANDOMIZER) { + } else if (getItem.modIndex == MOD_RANDOMIZER && getItem.getItemId != RG_ICE_TRAP) { Randomizer_Item_Give(NULL, getItem); } diff --git a/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c index 4951fb9a9..a034938b0 100644 --- a/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c +++ b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c @@ -59,6 +59,27 @@ static ColliderQuadInit sQuadInit = { { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, }; +// Replacement quad used for "Blue Fire Arrows" enhancement +static ColliderQuadInit sIceArrowQuadInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER | AC_TYPE_OTHER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0x00000048, 0x00, 0x00 }, + { 0x00001048, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + static BombableWallInfo sBombableWallInfo[] = { { &object_bwall_Col_000118, object_bwall_DL_000040, 0 }, { &object_bwall_Col_000118, object_bwall_DL_000040, 0 }, @@ -73,6 +94,8 @@ static InitChainEntry sInitChain[] = { ICHAIN_F32(uncullZoneDownward, 400, ICHAIN_STOP), }; +bool blueFireArrowsEnabledOnMudwallLoad = false; + void BgBreakwall_SetupAction(BgBreakwall* this, BgBreakwallActionFunc actionFunc) { this->actionFunc = actionFunc; } @@ -82,6 +105,9 @@ void BgBreakwall_Init(Actor* thisx, GlobalContext* globalCtx) { s32 pad; s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF; + // Initialize this with the mud wall, so it can't be affected by toggling while the actor is loaded + blueFireArrowsEnabledOnMudwallLoad = (CVar_GetS32("gBlueFireArrows", 0)); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); DynaPolyActor_Init(&this->dyna, DPM_UNK); this->bombableWallDList = sBombableWallInfo[wallType].dList; @@ -98,8 +124,15 @@ void BgBreakwall_Init(Actor* thisx, GlobalContext* globalCtx) { } ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f); - Collider_InitQuad(globalCtx, &this->collider); - Collider_SetQuad(globalCtx, &this->collider, &this->dyna.actor, &sQuadInit); + + // If "Blue Fire Arrows" are enabled, set up this collider for them + if (blueFireArrowsEnabledOnMudwallLoad) { + Collider_InitQuad(globalCtx, &this->collider); + Collider_SetQuad(globalCtx, &this->collider, &this->dyna.actor, &sIceArrowQuadInit); + } else { + Collider_InitQuad(globalCtx, &this->collider); + Collider_SetQuad(globalCtx, &this->collider, &this->dyna.actor, &sQuadInit); + } } else { this->dyna.actor.world.pos.y -= 40.0f; } @@ -227,7 +260,21 @@ void BgBreakwall_WaitForObject(BgBreakwall* this, GlobalContext* globalCtx) { * despawn itself. */ void BgBreakwall_Wait(BgBreakwall* this, GlobalContext* globalCtx) { - if (this->collider.base.acFlags & 2) { + bool blueFireArrowHit = false; + // If "Blue Fire Arrows" enabled, check this collider for a hit + if (blueFireArrowsEnabledOnMudwallLoad) { + if (this->collider.base.acFlags & AC_HIT) { + if ((this->collider.base.ac != NULL) && (this->collider.base.ac->id == ACTOR_EN_ARROW)) { + + if (this->collider.base.ac->child != NULL && + this->collider.base.ac->child->id == ACTOR_ARROW_ICE) { + blueFireArrowHit = true; + } + } + } + } + + if (this->collider.base.acFlags & 2 || blueFireArrowHit) { Vec3f effectPos; s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF; diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c index 70a1ea1a6..70390bfa2 100644 --- a/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c @@ -14,6 +14,9 @@ void func_808911BC(BgIceShelter* this); void func_8089107C(BgIceShelter* this, GlobalContext* globalCtx); void func_808911D4(BgIceShelter* this, GlobalContext* globalCtx); +// For "Blue Fire Arrows" enhancement +void MeltOnIceArrowHit(BgIceShelter* this, ColliderCylinder cylinder, s16 type, GlobalContext* globalCtx); + const ActorInit Bg_Ice_Shelter_InitVars = { ACTOR_BG_ICE_SHELTER, ACTORCAT_BG, @@ -32,7 +35,7 @@ static f32 sScales[] = { 0.1f, 0.06f, 0.1f, 0.1f, 0.25f }; static Color_RGBA8 sDustPrimColor = { 250, 250, 250, 255 }; static Color_RGBA8 sDustEnvColor = { 180, 180, 180, 255 }; -static ColliderCylinderInit D_8089170C = { +static ColliderCylinderInit sCylinder1Init = { { COLTYPE_NONE, AT_NONE, @@ -52,7 +55,7 @@ static ColliderCylinderInit D_8089170C = { { 0, 0, 0, { 0, 0, 0 } }, }; -static ColliderCylinderInit D_80891738 = { +static ColliderCylinderInit sCylinder2Init = { { COLTYPE_HARD, AT_NONE, @@ -72,14 +75,45 @@ static ColliderCylinderInit D_80891738 = { { 0, 0, 0, { 0, 0, 0 } }, }; +// This cylinder only used for "Blue Fire Arrows" enhancement +static ColliderCylinderInit sIceArrowCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_OTHER | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, 0, 0, { 0, 0, 0 } }, +}; + +bool blueFireArrowsEnabledOnRedIceLoad = false; + void func_80890740(BgIceShelter* this, GlobalContext* globalCtx) { static s16 cylinderRadii[] = { 47, 33, 44, 41, 100 }; static s16 cylinderHeights[] = { 80, 54, 90, 60, 200 }; s32 pad; s32 type = (this->dyna.actor.params >> 8) & 7; + // Initialize this with the red ice, so it can't be affected by toggling while the actor is loaded + blueFireArrowsEnabledOnRedIceLoad = (CVar_GetS32("gBlueFireArrows", 0)); + Collider_InitCylinder(globalCtx, &this->cylinder1); - Collider_SetCylinder(globalCtx, &this->cylinder1, &this->dyna.actor, &D_8089170C); + // If "Blue Fire Arrows" is enabled, set up a collider on the red ice that responds to them + if (blueFireArrowsEnabledOnRedIceLoad) { + Collider_SetCylinder(globalCtx, &this->cylinder1, &this->dyna.actor, &sIceArrowCylinderInit); + } else { + Collider_SetCylinder(globalCtx, &this->cylinder1, &this->dyna.actor, &sCylinder1Init); + } Collider_UpdateCylinder(&this->dyna.actor, &this->cylinder1); this->cylinder1.dim.radius = cylinderRadii[type]; @@ -87,7 +121,7 @@ void func_80890740(BgIceShelter* this, GlobalContext* globalCtx) { if (type == 0 || type == 1 || type == 4) { Collider_InitCylinder(globalCtx, &this->cylinder2); - Collider_SetCylinder(globalCtx, &this->cylinder2, &this->dyna.actor, &D_80891738); + Collider_SetCylinder(globalCtx, &this->cylinder2, &this->dyna.actor, &sCylinder2Init); Collider_UpdateCylinder(&this->dyna.actor, &this->cylinder2); this->cylinder2.dim.radius = cylinderRadii[type]; this->cylinder2.dim.height = cylinderHeights[type]; @@ -292,7 +326,12 @@ void func_8089107C(BgIceShelter* this, GlobalContext* globalCtx) { this->dyna.actor.parent->freezeTimer = 10000; } } - + // If we have "Blue Fire Arrows" enabled, check both cylinders for a hit + if (blueFireArrowsEnabledOnRedIceLoad) { + MeltOnIceArrowHit(this, this->cylinder1, type, globalCtx); + MeltOnIceArrowHit(this, this->cylinder2, type, globalCtx); + } + // Default blue fire check if (this->cylinder1.base.acFlags & AC_HIT) { this->cylinder1.base.acFlags &= ~AC_HIT; @@ -320,6 +359,24 @@ void func_8089107C(BgIceShelter* this, GlobalContext* globalCtx) { CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->cylinder1.base); } +// For "Blue Fire Arrows" enhancement: If hit by an Ice Arrow, melt the red ice (copied from the default blue fire function above). +void MeltOnIceArrowHit(BgIceShelter* this, ColliderCylinder cylinder, s16 type, GlobalContext* globalCtx) { + if (cylinder.base.acFlags & AC_HIT) { + cylinder.base.acFlags &= ~AC_HIT; + if ((cylinder.base.ac != NULL) && (cylinder.base.ac->id == ACTOR_EN_ARROW)) { + if (cylinder.base.ac->child != NULL && cylinder.base.ac->child->id == ACTOR_ARROW_ICE) { + if (type == 4) { + if (this->dyna.actor.parent != NULL) { + this->dyna.actor.parent->freezeTimer = 50; + } + } + func_808911BC(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ICE_MELT); + } + } + } +} + void func_808911BC(BgIceShelter* this) { this->actionFunc = func_808911D4; this->alpha = 255; diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.c b/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.c index 57c7ac413..631f0293f 100644 --- a/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.c +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.c @@ -96,7 +96,15 @@ void BgJyaLift_DelayMove(BgJyaLift* this, GlobalContext* globalCtx) { if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F) || (this->moveDelay > 0)) { this->moveDelay++; if (this->moveDelay >= 20) { - OnePointCutscene_Init(globalCtx, 3430, -99, &this->dyna.actor, MAIN_CAM); + // The cutscene of the platform lowering will show the central room in an unloaded state if + // Link is not standing on the platform as it lowers. Therefore check for the Sunlight arrows + // enhancement and if it's enabled, check that Link is on the platform. Otherwise skip it. + if (!CVar_GetS32("gSunlightArrows", 0) || (GET_PLAYER(globalCtx)->actor.world.pos.x > -19.0f && + GET_PLAYER(globalCtx)->actor.world.pos.x < 139.0f && + GET_PLAYER(globalCtx)->actor.world.pos.z > -1172.0f && + GET_PLAYER(globalCtx)->actor.world.pos.z < -1009.0f)) { + OnePointCutscene_Init(globalCtx, 3430, -99, &this->dyna.actor, MAIN_CAM); + } BgJyaLift_SetupMove(this); } } diff --git a/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.c b/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.c index 459f20218..1345dd0c7 100644 --- a/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.c +++ b/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.c @@ -73,6 +73,36 @@ static ColliderJntSphInit sColliderJntSphInit = { 1, sColliderJntSphElementInit, }; +// Collider info used for "Sunlight Arrows" +static ColliderJntSphElementInit sColliderLightArrowElementInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00202000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 19 }, 100 }, + }, +}; +// Sphere collider used for "Sunlight Arrows" +static ColliderJntSphInit sColliderLightArrowInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sColliderLightArrowElementInit, +}; + +bool sunSwitchActivatedByLightArrow = false; +bool sunLightArrowsEnabledOnSunSwitchLoad = false; static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_IMMOVABLE }; @@ -92,8 +122,16 @@ static InitChainEntry sInitChain[] = { void ObjLightswitch_InitCollider(ObjLightswitch* this, GlobalContext* globalCtx) { s32 pad; + // Initialize this with the sun switch, so it can't be affected by toggling while the actor is loaded + sunLightArrowsEnabledOnSunSwitchLoad = (CVar_GetS32("gSunlightArrows", 0)); + Collider_InitJntSph(globalCtx, &this->collider); - Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sColliderJntSphInit, this->colliderItems); + // If "Sunlight Arrows" is enabled, set up the collider to allow Light Arrow hits + if (sunLightArrowsEnabledOnSunSwitchLoad) { + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sColliderLightArrowInit, this->colliderItems); + } else { + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sColliderJntSphInit, this->colliderItems); + } Matrix_SetTranslateRotateYXZ(this->actor.world.pos.x, this->actor.world.pos.y + (this->actor.shape.yOffset * this->actor.scale.y), this->actor.world.pos.z, &this->actor.shape.rot); @@ -211,6 +249,26 @@ void ObjLightswitch_Destroy(Actor* thisx, GlobalContext* globalCtx2) { GlobalContext* globalCtx = globalCtx2; ObjLightswitch* this = (ObjLightswitch*)thisx; + // Unset the switch flag on room exit to prevent the rock in the wall from + // vanishing on its own after activating the sun switch by Light Arrow + // Also prevents the cobra mirror from rotating to face the sun on its own + // Makes sun switches temporary when activated by Light Arrows (will turn off on room exit) + if (sunSwitchActivatedByLightArrow) { + switch (this->actor.params >> 4 & 3) { + case OBJLIGHTSWITCH_TYPE_STAY_ON: + case OBJLIGHTSWITCH_TYPE_2: + case OBJLIGHTSWITCH_TYPE_1: + // Except for this one, because we want the chain platform to stay down for good + if (this->actor.room != 25) { + Flags_UnsetSwitch(globalCtx, this->actor.params >> 8 & 0x3F); + } + sunSwitchActivatedByLightArrow = false; + break; + case OBJLIGHTSWITCH_TYPE_BURN: + break; + } + } + Collider_DestroyJntSph(globalCtx, &this->collider); } @@ -221,8 +279,11 @@ void ObjLightswitch_SetupOff(ObjLightswitch* this) { this->color[1] = 125 << 6; this->color[2] = 255 << 6; this->alpha = 255 << 6; + if (sunLightArrowsEnabledOnSunSwitchLoad) { + sunSwitchActivatedByLightArrow = false; + } } - +// A Sun Switch that is currently turned off void ObjLightswitch_Off(ObjLightswitch* this, GlobalContext* globalCtx) { switch (this->actor.params >> 4 & 3) { case OBJLIGHTSWITCH_TYPE_STAY_ON: @@ -230,6 +291,13 @@ void ObjLightswitch_Off(ObjLightswitch* this, GlobalContext* globalCtx) { if (this->collider.base.acFlags & AC_HIT) { ObjLightswitch_SetupTurnOn(this); ObjLightswitch_SetSwitchFlag(this, globalCtx); + // Remember if we've been activated by a Light Arrow, so we can + // prevent the switch from immediately turning back off + if (sunLightArrowsEnabledOnSunSwitchLoad) { + if (this->collider.base.ac != NULL && this->collider.base.ac->id == ACTOR_EN_ARROW) { + sunSwitchActivatedByLightArrow = true; + } + } } break; case OBJLIGHTSWITCH_TYPE_1: @@ -289,13 +357,19 @@ void ObjLightswitch_SetupOn(ObjLightswitch* this) { this->flameRingRotSpeed = -0xAA; this->timer = 0; } - +// A Sun Switch that is currently turned on void ObjLightswitch_On(ObjLightswitch* this, GlobalContext* globalCtx) { switch (this->actor.params >> 4 & 3) { case OBJLIGHTSWITCH_TYPE_STAY_ON: if (!Flags_GetSwitch(globalCtx, this->actor.params >> 8 & 0x3F)) { ObjLightswitch_SetupTurnOff(this); } + // If hit by sunlight after already being turned on, then behave as if originally activated by sunlight + if (sunLightArrowsEnabledOnSunSwitchLoad && (this->collider.base.acFlags & AC_HIT)) { + if (this->collider.base.ac != NULL && this->collider.base.ac->id != ACTOR_EN_ARROW) { + sunSwitchActivatedByLightArrow = false; + } + } break; case OBJLIGHTSWITCH_TYPE_1: if (this->collider.base.acFlags & AC_HIT && !(this->prevFrameACflags & AC_HIT)) { @@ -304,10 +378,19 @@ void ObjLightswitch_On(ObjLightswitch* this, GlobalContext* globalCtx) { } break; case OBJLIGHTSWITCH_TYPE_2: + // If hit by sunlight after already being turned on, then behave as if originally activated by sunlight + if (sunLightArrowsEnabledOnSunSwitchLoad && (this->collider.base.acFlags & AC_HIT)) { + if (this->collider.base.ac != NULL && this->collider.base.ac->id != ACTOR_EN_ARROW) { + sunSwitchActivatedByLightArrow = false; + } + } if (!(this->collider.base.acFlags & AC_HIT)) { if (this->timer >= 7) { - ObjLightswitch_SetupTurnOff(this); - ObjLightswitch_ClearSwitchFlag(this, globalCtx); + // If we aren't using Enhanced Light Arrows, let the switch turn off normally + if (!sunSwitchActivatedByLightArrow) { + ObjLightswitch_SetupTurnOff(this); + ObjLightswitch_ClearSwitchFlag(this, globalCtx); + } } else { this->timer++; }