diff --git a/CMakeLists.txt b/CMakeLists.txt index ec9f14f5b..b9a5afe4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.26.0 FATAL_ERROR) set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE) set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") +set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 7277b2dfb..3511a0caf 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -4,6 +4,7 @@ set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE) project(soh LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") +set(CMAKE_C_STANDARD 17 CACHE STRING "The C standard to use") if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") enable_language(OBJCXX) diff --git a/soh/include/functions.h b/soh/include/functions.h index 86e3bf77f..f29153a9c 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -15,7 +15,7 @@ extern "C" #include #if defined(INCLUDE_GAME_PRINTF) && defined(_DEBUG) -#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, __VA_ARGS__) +#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, ##__VA_ARGS__) #else #define osSyncPrintf(fmt, ...) osSyncPrintfUnused(fmt, ##__VA_ARGS__) #endif diff --git a/soh/include/z64.h b/soh/include/z64.h index 83e1498db..53b76e8fb 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -544,7 +544,7 @@ typedef enum { LANGUAGE_MAX } Language; -#define TODO_TRANSLATE "__Translate_This__" +#define TODO_TRANSLATE "TranslateThis" // TODO get these properties from the textures themselves #define FONT_CHAR_TEX_WIDTH 16 diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index 9865615bd..4cf1f389d 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -265,6 +265,9 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN } } + auto playingFromMenu = CVarGetInteger(CVAR_AUDIO("Playing"), 0); + auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); + // Longest text in Audio Editor ImVec2 columnSize = ImGui::CalcTextSize("Navi - Look/Hey/Watchout (Target Enemy)"); ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit); @@ -291,10 +294,13 @@ void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabN const std::string lockedButton = ICON_FA_LOCK + hiddenKey; const std::string unlockedButton = ICON_FA_UNLOCK + hiddenKey; const int currentValue = CVarGetInteger(cvarKey.c_str(), defaultValue); + const bool isCurrentlyPlaying = currentValue == playingFromMenu || seqData.sequenceId == currentBGM; ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::Text("%s", seqData.label.c_str()); + ImGui::TextColored( + UIWidgets::ColorValues.at(isCurrentlyPlaying ? UIWidgets::Colors::Yellow : UIWidgets::Colors::White), "%s", + seqData.label.c_str()); ImGui::TableNextColumn(); ImGui::PushItemWidth(-FLT_MIN); const int initialValue = map.contains(currentValue) ? currentValue : defaultValue; diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index 3f77d1ed4..8a8227073 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -642,10 +642,14 @@ void SohInputEditorWindow::DrawStickSection(uint8_t port, uint8_t stick, int32_t ImGui::SameLine(); ImGui::BeginGroup(); - DrawStickDirectionLine(ICON_FA_ARROW_UP, port, stick, Ship::UP, color); - DrawStickDirectionLine(ICON_FA_ARROW_DOWN, port, stick, Ship::DOWN, color); - DrawStickDirectionLine(ICON_FA_ARROW_LEFT, port, stick, Ship::LEFT, color); - DrawStickDirectionLine(ICON_FA_ARROW_RIGHT, port, stick, Ship::RIGHT, color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_UP, stick).c_str(), port, stick, Ship::UP, + color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_DOWN, stick).c_str(), port, stick, Ship::DOWN, + color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_LEFT, stick).c_str(), port, stick, Ship::LEFT, + color); + DrawStickDirectionLine(StringHelper::Sprintf("%s##%d", ICON_FA_ARROW_RIGHT, stick).c_str(), port, stick, + Ship::RIGHT, color); ImGui::EndGroup(); ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode(StringHelper::Sprintf("Analog Stick Options##%d", id).c_str())) { @@ -1335,12 +1339,12 @@ void SohInputEditorWindow::DrawOcarinaControlPanel() { ImGui::AlignTextToFramePadding(); ImGui::BulletText("Disable song detection"); - DrawButtonLine(ICON_FA_BAN, 0, BTN_CUSTOM_OCARINA_DISABLE_SONGS); + DrawButtonLine(ICON_FA_BAN "##DisableSongDetection", 0, BTN_CUSTOM_OCARINA_DISABLE_SONGS); ImGui::AlignTextToFramePadding(); ImGui::BulletText("Pitch"); - DrawButtonLine(ICON_FA_ARROW_UP, 0, BTN_CUSTOM_OCARINA_PITCH_UP); - DrawButtonLine(ICON_FA_ARROW_DOWN, 0, BTN_CUSTOM_OCARINA_PITCH_DOWN); + DrawButtonLine(ICON_FA_ARROW_UP "##Pitch", 0, BTN_CUSTOM_OCARINA_PITCH_UP); + DrawButtonLine(ICON_FA_ARROW_DOWN "##Pitch", 0, BTN_CUSTOM_OCARINA_PITCH_DOWN); if (!CVarGetInteger(CVAR_SETTING("CustomOcarina.Enabled"), 0)) { ImGui::EndDisabled(); diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp index 278b8db0c..a9c19f11f 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp @@ -155,12 +155,12 @@ const std::string CustomMessage::GetFrench(MessageFormat format) const { } const std::string CustomMessage::GetForCurrentLanguage(MessageFormat format) const { - return GetForLanguage(((Language)gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language, - format); + return GetForLanguage( + ((Language)gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : (Language)gSaveContext.language, format); } const std::string CustomMessage::GetForLanguage(uint8_t language, MessageFormat format) const { - std::string output = messages[language] != TODO_TRANSLATE ? messages[language] : messages[LANGUAGE_ENG]; + std::string output = !messages[language].starts_with(TODO_TRANSLATE) ? messages[language] : messages[LANGUAGE_ENG]; ProcessMessageFormat(output, format); return output; } diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index d57d5029a..223001711 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -474,6 +474,14 @@ typedef enum { // - `*BgHeavyBlock` VB_FREEZE_LINK_FOR_BLOCK_THROW, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - None + VB_FREEZE_LINK_FOR_FOREST_PILLARS, + // #### `result` // ```c // true @@ -1358,6 +1366,14 @@ typedef enum { // - `*BgTreemouth` VB_PLAY_DEKU_TREE_INTRO_CS, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*DemoKekkai` + VB_PLAY_DISPEL_BARRIER_CS, + // #### `result` // ```c // true @@ -1422,6 +1438,15 @@ typedef enum { // - None VB_PLAY_FIRE_ARROW_CS, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*EnHeishi2` + // - `bool` (clearCamera - true if the code clears a sub-camera, false otherwise) + VB_PLAY_GATE_OPENING_OR_CLOSING_CS, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 08e6d5823..f2b0e782c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -648,6 +648,8 @@ void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAcce if (ctx->GetOption(RSK_SHUFFLE_INTERIOR_ENTRANCES).Is(RO_INTERIOR_ENTRANCE_SHUFFLE_OFF)) { Rando::StaticData::RetrieveItem(RG_GUARD_HOUSE_KEY).ApplyEffect(); } + RegionTable(RR_ROOT)->adultNight = true; + RegionTable(RR_ROOT)->adultDay = true; } else { ApplyAllAdvancmentItems(); } diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index 39e383f89..67043952f 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -68,4 +68,4 @@ void GeneratePlaythrough(); bool CheckBeatable(RandomizerGet ignore=RG_NONE); -void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess); \ No newline at end of file +void ValidateEntrances(bool checkPoeCollectorAccess, bool checkOtherEntranceAccess); diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index 90ddcef06..72c90fa19 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -2412,13 +2412,13 @@ void StaticData::HintTable_Init() { hintTextTable[RHT_ISOLATED_PLACE] = HintText(CustomMessage("an Isolated Place")); - hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage(" It's ordinary.", - /*german*/ "&Sieht aus wie immer.", - /*french*/ "&Elle vous semble %rordinaire%w.")); + hintTextTable[RHT_DUNGEON_ORDINARY] = HintText(CustomMessage("&It's %gordinary%w.", + /*german*/ "&Sieht aus %gwie immer%w.", + /*french*/ "&Elle vous semble %gordinaire%w.")); - hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage(" It's masterful!", - /*german*/ "&Man kann darauf die Worte&%r\"Master Quest\"%w entziffern...", - /*french*/ "&Étrange... les mots %r\"Master&Quest\"%w sont gravés dessus.")); + hintTextTable[RHT_DUNGEON_MASTERFUL] = HintText(CustomMessage("&It's %rmasterful%w!", + /*german*/ "&Man kann darauf die Worte %r\"Master_Quest\"%w entziffern...", + /*french*/ "&Étrange... les mots %r\"Master_Quest\"%w sont gravés dessus.")); // clang-format on } diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 6cf2aa2ab..cdd684f04 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -10,9 +10,10 @@ #include "soh/Enhancements/debugger/performanceTimer.h" #include +#include +#include "3drando/shops.hpp" extern "C" { -extern SaveContext gSaveContext; extern PlayState* gPlayState; } @@ -45,17 +46,71 @@ bool LocationAccess::ConditionsMet(Region* parentRegion, bool calculatingAvailab conditionsMet = true; } - return conditionsMet && - (calculatingAvailableChecks || CanBuy()); // TODO: run CanBuy when price is known due to settings + return conditionsMet && CanBuy(calculatingAvailableChecks); } -bool LocationAccess::CanBuy() const { - return CanBuyAnother(location); +static uint16_t GetMinimumPrice(const Rando::Location* loc) { + extern PriceSettingsStruct shopsanityPrices; + extern PriceSettingsStruct scrubPrices; + extern PriceSettingsStruct merchantPrices; + PriceSettingsStruct priceSettings = loc->GetRCType() == RCTYPE_SHOP ? shopsanityPrices + : loc->GetRCType() == RCTYPE_SCRUB ? scrubPrices + : merchantPrices; + + auto ctx = Rando::Context::GetInstance(); + switch (ctx->GetOption(priceSettings.main).Get()) { + case RO_PRICE_VANILLA: + return loc->GetVanillaPrice(); + case RO_PRICE_CHEAP_BALANCED: + return 0; + case RO_PRICE_BALANCED: + return 0; + case RO_PRICE_FIXED: + return ctx->GetOption(priceSettings.fixedPrice).Get() * 5; + case RO_PRICE_RANGE: { + uint16_t range1 = ctx->GetOption(priceSettings.range1).Get() * 5; + uint16_t range2 = ctx->GetOption(priceSettings.range1).Get() * 5; + return range1 < range2 ? range1 : range2; + } + case RO_PRICE_SET_BY_WALLET: { + if (ctx->GetOption(priceSettings.noWallet).Get()) { + return 0; + } else if (ctx->GetOption(priceSettings.childWallet).Get()) { + return 1; + } else if (ctx->GetOption(priceSettings.adultWallet).Get()) { + return 100; + } else if (ctx->GetOption(priceSettings.giantWallet).Get()) { + return 201; + } else { + return 501; + } + } + default: + return 0; + } +} + +bool LocationAccess::CanBuy(bool calculatingAvailableChecks) const { + const auto& loc = Rando::StaticData::GetLocation(location); + const auto& itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(location); + + if (loc->GetRCType() == RCTYPE_SHOP || loc->GetRCType() == RCTYPE_SCRUB || loc->GetRCType() == RCTYPE_MERCHANT) { + // Checks should only be identified while playing + if (calculatingAvailableChecks && itemLoc->GetCheckStatus() != RCSHOW_IDENTIFIED) { + return CanBuyAnother(GetMinimumPrice(loc)); + } else { + return CanBuyAnother(itemLoc->GetPrice()); + } + } + + return true; } bool CanBuyAnother(RandomizerCheck rc) { - uint16_t price = ctx->GetItemLocation(rc)->GetPrice(); + return CanBuyAnother(ctx->GetItemLocation(rc)->GetPrice()); +} +bool CanBuyAnother(uint16_t price) { if (price > 500) { return logic->HasItem(RG_TYCOON_WALLET); } else if (price > 200) { @@ -275,7 +330,7 @@ bool BeanPlanted(const RandomizerRegion region) { if (gPlayState != nullptr && gPlayState->sceneNum == sceneID) { swch = gPlayState->actorCtx.flags.swch; } else if (sceneID != SCENE_ID_MAX) { - swch = gSaveContext.sceneFlags[sceneID].swch; + swch = Rando::Context::GetInstance()->GetLogic()->GetSaveContext()->sceneFlags[sceneID].swch; } else { swch = 0; } diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 5aed64070..8f7bb82d3 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -100,9 +100,10 @@ class LocationAccess { std::string condition_str; // Makes sure shop locations are buyable - bool CanBuy() const; + bool CanBuy(bool calculatingAvailableChecks) const; }; +bool CanBuyAnother(uint16_t price); bool CanBuyAnother(RandomizerCheck rc); namespace Rando { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp index 44c3fa817..8a8393ba1 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp @@ -25,7 +25,6 @@ void RegionTable_Init_BottomOfTheWell() { }, { //Locations LOCATION(RC_BOTTOM_OF_THE_WELL_FRONT_CENTER_BOMBABLE_CHEST, logic->HasExplosives()), - LOCATION(RC_BOTTOM_OF_THE_WELL_FREESTANDING_KEY, (logic->HasItem(RG_BRONZE_SCALE) || logic->LoweredWaterInsideBotw) && logic->CanUse(RG_STICKS) || logic->CanUse(RG_DINS_FIRE)), LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_FRONT_CHEST, logic->LoweredWaterInsideBotw), LOCATION(RC_BOTTOM_OF_THE_WELL_UNDERWATER_LEFT_CHEST, logic->LoweredWaterInsideBotw), LOCATION(RC_BOTTOM_OF_THE_WELL_NEAR_ENTRANCE_POT_1, logic->CanBreakPots()), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 8888cdac9..70cf890b0 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -20,7 +20,7 @@ void RegionTable_Init_FireTemple() { //Exits Entrance(RR_FIRE_TEMPLE_ENTRYWAY, []{return true;}), Entrance(RR_FIRE_TEMPLE_NEAR_BOSS_ROOM, []{return logic->FireTimer() >= 24;}), - Entrance(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return Here(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);}) && (logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsKeysanity);}), + Entrance(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return Here(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);}) && (logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsFireLoopLocked);}), Entrance(RR_FIRE_TEMPLE_LOOP_EXIT, []{return true;}), Entrance(RR_FIRE_TEMPLE_BIG_LAVA_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 2) && logic->FireTimer() >= 24;}), }); @@ -43,7 +43,7 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_LOOP_ENEMIES] = Region("Fire Temple Loop Enemies", "Fire Temple", {RA_FIRE_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsKeysanity;}), + Entrance(RR_FIRE_TEMPLE_FIRST_ROOM, []{return logic->SmallKeys(RR_FIRE_TEMPLE, 8) || !logic->IsFireLoopLocked;}), Entrance(RR_FIRE_TEMPLE_LOOP_TILES, []{return Here(RR_FIRE_TEMPLE_LOOP_ENEMIES, []{return logic->CanKillEnemy(RE_TORCH_SLUG) && logic->CanKillEnemy(RE_FIRE_KEESE);});}), }); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp index 0ae5ac289..04c1279bd 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp @@ -658,7 +658,7 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_DRAGON_ROOM_ALCOVE] = Region("Water Temple MQ Dragon Room Alcove", "Water Temple", {RA_WATER_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->MQWaterDragonTorches, []{return true;}), + EventAccess(&logic->MQWaterDragonTorches, []{return logic->HasFireSource();}), }, { //Locations diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 90eaf6b83..597feb463 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1389,6 +1389,7 @@ bool Logic::SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmountGlitchless static_cast(GlitchDifficulty::INTERMEDIATE) || GetDifficultyValueFromString(GlitchHover) >= static_cast(GlitchDifficulty::INTERMEDIATE))) { return FireTempleKeys >= requiredAmountGlitched; }*/ + // If the Fire Temple loop lock is removed, Small key Count is set to 1 before starting return GetSmallKeyCount(SCENE_FIRE_TEMPLE) >= requiredAmountGlitchless; case RR_WATER_TEMPLE: @@ -2338,9 +2339,9 @@ void Logic::Reset(bool resetSaveContext /*= true*/) { StartPerformanceTimer(PT_LOGIC_RESET); memset(inLogic, false, sizeof(inLogic)); // Settings-dependent variables - IsKeysanity = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || - ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || - ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE); + IsFireLoopLocked = ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANYWHERE) || + ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OVERWORLD) || + ctx->GetOption(RSK_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_ANY_DUNGEON); // AmmoCanDrop = /*AmmoDrops.IsNot(AMMODROPS_NONE)*/ false; TODO: AmmoDrop setting @@ -2403,7 +2404,7 @@ void Logic::Reset(bool resetSaveContext /*= true*/) { // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in // vanilla FiT - if (!IsKeysanity && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { + if (!IsFireLoopLocked && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { SetSmallKeyCount(SCENE_FIRE_TEMPLE, 1); } } @@ -2504,6 +2505,7 @@ void Logic::Reset(bool resetSaveContext /*= true*/) { ForestOpenBossCorridor = false; ShadowTrialFirstChest = false; MQGTGMazeSwitch = false; + MQGTGRightSideSwitch = false; GTGPlatformSilverRupees = false; MQJabuHolesRoomDoor = false; JabuWestTentacle = false; diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index a1c518e71..0c138a3eb 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -59,7 +59,7 @@ class Logic { bool LightTrialClear = false; // Logical keysanity - bool IsKeysanity = false; + bool IsFireLoopLocked = false; // Bottle Count uint8_t Bottles = 0; diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index f1ecd608c..18a23df53 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -65,7 +65,7 @@ const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi"; const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap"; const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints"; -static const char* englishRupeeNames[190] = { +static const char* englishRupeeNames[188] = { "[P]", "Bad RNG Rolls", "Baht", @@ -111,8 +111,6 @@ static const char* englishRupeeNames[190] = { "Dimes", "Dinars", "DNA", - "Doge", - "Dogecoin", "Doll Hairs", "Dollars", "Dollarydoos", @@ -258,25 +256,25 @@ static const char* englishRupeeNames[190] = { "Zorkmids", }; -static const char* germanRupeeNames[80] = { - "Baht", "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent", - "Diamanten", "Dinar", "Diridari", "Dogecoin", "Dollar", "Draken", "ECU", "Elexit", - "Erz", "Erzbrocken", "Euro", "EXP", "Forint", "Franken", "Freunde", "Gil", - "Gold", "Groschen", "Gulden", "Gummibären", "Heller", "Juwelen", "Karolin", "Kartoffeln", - "Kies", "Knete", "Knochen", "Kohle", "Kraniche", "Kreuzer", "Kronen", "Kronkorken", - "Kröten", "Lira", "Mark", "Mäuse", "Monde", "Moorhühner", "Moos", "Münzen", - "Naira", "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", "Pilze", "Plastiks", - "Pokédollar", "Radieschen", "Rand", "Rappen", "Real", "Rial", "Riyal", "Rubine", - "Rupien", "Saphire", "Schilling", "Seelen", "Septime", "Smaragde", "Steine", "Sterne", - "Sternis", "Tael", "Taler", "Wagenchips", "Won", "Yen", "Yuan", "Zenny", +static const char* germanRupeeNames[79] = { + "Baht", "Bananen", "Bitcoin", "Bonbons", "Bratwürste", "Brause UFOs", "Brötchen", "Cent", + "Diamanten", "Dinar", "Diridari", "Dollar", "Draken", "ECU", "Elexit", "Erz", + "Erzbrocken", "Euro", "EXP", "Forint", "Franken", "Freunde", "Gil", "Gold", + "Groschen", "Gulden", "Gummibären", "Heller", "Juwelen", "Karolin", "Kartoffeln", "Kies", + "Knete", "Knochen", "Kohle", "Kraniche", "Kreuzer", "Kronen", "Kronkorken", "Kröten", + "Lira", "Mark", "Mäuse", "Monde", "Moorhühner", "Moos", "Münzen", "Naira", + "Penunze", "Pesa", "Pfandflaschen", "Pfennig", "Pfund", "Pilze", "Plastiks", "Pokédollar", + "Radieschen", "Rand", "Rappen", "Real", "Rial", "Riyal", "Rubine", "Rupien", + "Saphire", "Schilling", "Seelen", "Septime", "Smaragde", "Steine", "Sterne", "Sternis", + "Tael", "Taler", "Wagenchips", "Won", "Yen", "Yuan", "Zenny", }; -static const char* frenchRupeeNames[40] = { - "Anneaux", "Baguettes", "Balles", "Bananes", "Bitcoin", "Blés", "Bling", "Capsules", - "Centimes", "Champignons", "Clochettes", "Crédits", "Croissants", "Diamants", "Dogecoin", "Dollars", - "Émeraudes", "Éthers", "Étoiles", "Euros", "Florens", "Francs", "Galds", "Gils", - "Grouses", "Halos", "Joyaux", "Lunes", "Mailles", "Munnies", "Orbes", "Orens", - "Pépètes", "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies", +static const char* frenchRupeeNames[39] = { + "Anneaux", "Baguettes", "Balles", "Bananes", "Bitcoin", "Blés", "Bling", "Capsules", + "Centimes", "Champignons", "Clochettes", "Crédits", "Croissants", "Diamants", "Dollars", "Émeraudes", + "Éthers", "Étoiles", "Euros", "Florens", "Francs", "Galds", "Gils", "Grouses", + "Halos", "Joyaux", "Lunes", "Mailles", "Munnies", "Orbes", "Orens", "Pépètes", + "Pièces", "Plastyks", "Pokédollars", "Pokémon", "Radis", "Rubis", "Zennies", }; Randomizer::Randomizer() { @@ -3726,13 +3724,15 @@ void RandomizerSettingsWindow::DrawElement() { } UIWidgets::Spacer(0); - ImGui::BeginDisabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); - if (UIWidgets::Button("Generate Randomizer", - UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR))) { + UIWidgets::ButtonOptions options = UIWidgets::ButtonOptions().Size(ImVec2(250.f, 0.f)).Color(THEME_COLOR); + options.Disabled((gSaveContext.gameMode != GAMEMODE_FILE_SELECT) || GameInteractor::IsSaveLoaded()); + if (options.disabled) { + options.DisabledTooltip("Must be on File Select to generate a randomizer seed."); + } + if (UIWidgets::Button("Generate Randomizer", options)) { ctx->SetSpoilerLoaded(false); GenerateRandomizer(CVarGetInteger(CVAR_RANDOMIZER_SETTING("ManualSeedEntry"), 0) ? seedString : ""); } - ImGui::EndDisabled(); ImGui::SameLine(); if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { @@ -4515,6 +4515,9 @@ CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) { messageEntry.Replace("[[typeHint]]", Rando::StaticData::hintTextTable[RHT_DUNGEON_ORDINARY].GetHintMessage()); } + // BUG: the icon is not in the message yet so are not accounted for, so overflows are possible + messageEntry.AutoFormat(); + return messageEntry; } diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index e95531dde..3a2c5e475 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -1995,7 +1995,7 @@ void RecalculateAvailableChecks(RandomizerRegion startingRegion /* = RR_ROOT */) const auto& ctx = Rando::Context::GetInstance(); std::vector targetLocations; - targetLocations.reserve(RR_MAX); + targetLocations.reserve(RC_MAX); for (auto& location : Rando::StaticData::GetLocationTable()) { RandomizerCheck rc = location.GetRandomizerCheck(); Rando::ItemLocation* itemLocation = ctx->GetItemLocation(rc); @@ -2007,17 +2007,8 @@ void RecalculateAvailableChecks(RandomizerRegion startingRegion /* = RR_ROOT */) std::vector availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true, startingRegion); for (auto& rc : availableChecks) { - const auto& location = Rando::StaticData::GetLocation(rc); const auto& itemLocation = ctx->GetItemLocation(rc); - const auto& region = areaTable[itemLocation->GetParentRegionKey()]; - - if (location->GetRCType() == RCTYPE_SHOP && itemLocation->GetCheckStatus() == RCSHOW_IDENTIFIED) { - if (CanBuyAnother(rc)) { - itemLocation->SetAvailable(true); - } - } else { - itemLocation->SetAvailable(true); - } + itemLocation->SetAvailable(true); } totalChecksAvailable = 0; @@ -2144,7 +2135,10 @@ void CheckTrackerSettingsWindow::DrawElement() { "with your current progress.") .Color(THEME_COLOR))) { enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0); - RecalculateAvailableChecks(); + + if (GameInteractor::IsSaveLoaded(true)) { + RecalculateAvailableChecks(); + } } ImGui::EndDisabled(); diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index 9b7b1fd39..5996fea1f 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -1532,8 +1532,9 @@ void ItemTrackerWindow::DrawElement() { if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Notes"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_MAIN_WINDOW && - CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == - TRACKER_DISPLAY_ALWAYS) { + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == + TRACKER_DISPLAY_ALWAYS)) { DrawNotes(); } EndFloatingWindows(); @@ -1642,7 +1643,10 @@ void ItemTrackerWindow::DrawElement() { if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Notes"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_SEPARATE && - CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_ALWAYS) { + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_WINDOW || + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) != + TRACKER_DISPLAY_COMBO_BUTTON))) { ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver); BeginFloatingWindows("Personal Notes", ImGuiWindowFlags_NoFocusOnAppearing); DrawNotes(true); @@ -1943,7 +1947,10 @@ void ItemTrackerSettingsWindow::DrawElement() { shouldUpdateVectors = true; } - if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_ALWAYS) { + if (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_WINDOW || + (CVarGetInteger(CVAR_TRACKER_ITEM("WindowType"), TRACKER_WINDOW_FLOATING) == TRACKER_WINDOW_FLOATING && + CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.Main"), TRACKER_DISPLAY_ALWAYS) != + TRACKER_DISPLAY_COMBO_BUTTON)) { if (CVarCombobox("Personal notes", CVAR_TRACKER_ITEM("DisplayType.Notes"), displayTypes, ComboboxOptions() .DefaultIndex(SECTION_DISPLAY_HIDDEN) diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index cb8c7eaef..1bed22a71 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -269,9 +269,9 @@ extern "C" void Randomizer_InitSaveFile() { // Remove One Time Scrubs with Scrubsanity off if (Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_OFF) { - Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE); - Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT); - Flags_SetRandomizerInf(RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO); + Flags_SetItemGetInf(ITEMGETINF_DEKU_SCRUB_HEART_PIECE); + Flags_SetInfTable(INFTABLE_BOUGHT_STICK_UPGRADE); + Flags_SetInfTable(INFTABLE_BOUGHT_NUT_UPGRADE); } int startingAge = OTRGlobals::Instance->gRandoContext->GetOption(RSK_SELECTED_STARTING_AGE).Get(); diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index ee3408474..a2db7ee0e 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -250,7 +250,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_LACS_REWARD_COUNT, "GCBK Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardCount"), "", WidgetType::Slider, 9, true); OPT_U8(RSK_LACS_DUNGEON_COUNT, "GCBK Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsDungeonCount"), "", WidgetType::Slider, 8, true); OPT_U8(RSK_LACS_TOKEN_COUNT, "GCBK Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsTokenCount"), "", WidgetType::Slider, 100, true); - OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), "", WidgetType::Combobox, RO_LACS_STANDARD_REWARD); + OPT_U8(RSK_LACS_OPTIONS, "GCBK LACS Reward Options", {"Standard Reward", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LacsRewardOptions"), mOptionDescriptions[RSK_LACS_OPTIONS], WidgetType::Combobox, RO_LACS_STANDARD_REWARD); OPT_U8(RSK_KEYRINGS, "Key Rings", {"Off", "Random", "Count", "Selection"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRings"), mOptionDescriptions[RSK_KEYRINGS], WidgetType::Combobox, RO_KEYRINGS_OFF); OPT_U8(RSK_KEYRINGS_RANDOM_COUNT, "Keyring Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsRandomCount"), "", WidgetType::Slider, 8); OPT_U8(RSK_KEYRINGS_GERUDO_FORTRESS, "Gerudo Fortress Keyring", {"No", "Random", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleKeyRingsGerudoFortress"), "", WidgetType::Combobox, 0); diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index b738e2fc1..9f488efce 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -13,12 +13,14 @@ extern "C" { #include "src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h" #include "src/overlays/actors/ovl_En_Owl/z_en_owl.h" #include "src/overlays/actors/ovl_En_Go2/z_en_go2.h" +#include "src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.h" #include "src/overlays/actors/ovl_En_Ko/z_en_ko.h" #include "src/overlays/actors/ovl_En_Ma1/z_en_ma1.h" #include "src/overlays/actors/ovl_En_Ru2/z_en_ru2.h" #include "src/overlays/actors/ovl_En_Zl4/z_en_zl4.h" #include "src/overlays/actors/ovl_En_Box/z_en_box.h" #include "src/overlays/actors/ovl_Demo_Im/z_demo_im.h" +#include "src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h" #include "src/overlays/actors/ovl_En_Sa/z_en_sa.h" #include "src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h" #include "src/overlays/actors/ovl_En_Tk/z_en_tk.h" @@ -217,6 +219,25 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Entrances"), IS_RANDO) && (entranceFlag != EVENTCHKINF_EPONA_OBTAINED) && entranceIndex != ENTR_SPIRIT_TEMPLE_BOSS_ENTRANCE) { *should = false; + + // Check for dispulsion of Ganon's Tower barrier + switch (entranceIndex) { + case ENTR_INSIDE_GANONS_CASTLE_2: + case ENTR_INSIDE_GANONS_CASTLE_3: + case ENTR_INSIDE_GANONS_CASTLE_4: + case ENTR_INSIDE_GANONS_CASTLE_5: + case ENTR_INSIDE_GANONS_CASTLE_6: + case ENTR_INSIDE_GANONS_CASTLE_7: + if (Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_FOREST_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_WATER_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_SHADOW_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_FIRE_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_LIGHT_TRIAL) && + Flags_GetEventChkInf(EVENTCHKINF_COMPLETED_SPIRIT_TRIAL)) { + Flags_SetEventChkInf(EVENTCHKINF_DISPELLED_GANONS_TOWER_BARRIER); + } + break; + } } break; } @@ -275,6 +296,11 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } switch (actor->id) { case ACTOR_OBJ_SWITCH: { + if ((actor->params == 8224 && gPlayState->sceneNum == SCENE_DODONGOS_CAVERN) || + (actor->params == 6979 && gPlayState->sceneNum == SCENE_WATER_TEMPLE) && + CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding"), 0)) { + break; + } ObjSwitch* switchActor = (ObjSwitch*)actor; switchActor->cooldownTimer = 0; *should = false; @@ -349,6 +375,11 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li } break; } + case VB_FREEZE_LINK_FOR_FOREST_PILLARS: + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) { + *should = false; + } + break; case VB_SHOW_TITLE_CARD: if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.DisableTitleCard"), IS_RANDO)) { *should = false; @@ -441,6 +472,26 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li *should = false; } break; + case VB_PLAY_DISPEL_BARRIER_CS: { + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.OnePoint"), IS_RANDO)) { + static s16 trialEntrances[] = { + 0, + ENTR_INSIDE_GANONS_CASTLE_3, + ENTR_INSIDE_GANONS_CASTLE_6, + ENTR_INSIDE_GANONS_CASTLE_5, + ENTR_INSIDE_GANONS_CASTLE_4, + ENTR_INSIDE_GANONS_CASTLE_7, + ENTR_INSIDE_GANONS_CASTLE_2, + }; + RateLimitedSuccessChime(); + DemoKekkai* kekkai = va_arg(args, DemoKekkai*); + gPlayState->nextEntranceIndex = trialEntrances[kekkai->actor.params]; + gPlayState->transitionTrigger = TRANS_TRIGGER_START; + gPlayState->transitionType = TRANS_TYPE_FADE_BLACK; + *should = false; + } + break; + } case VB_OWL_INTERACTION: { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipOwlInteractions"), IS_RANDO) && *should) { EnOwl* enOwl = va_arg(args, EnOwl*); @@ -720,6 +771,18 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li break; } + case VB_PLAY_GATE_OPENING_OR_CLOSING_CS: { + if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO)) { + EnHeishi2* enHeishi2 = va_arg(args, EnHeishi2*); + enHeishi2->unk_2F2[0] = 0; + + // The second argument determines whether the vanilla code should be run anyway. It + // should be set to `true` ONLY IF said code calls `Play_ClearCamera`, false otherwise. + bool clearCamera = (bool)va_arg(args, int); + *should = clearCamera && enHeishi2->cameraId != MAIN_CAM; + } + break; + } case VB_PLAY_RAINBOW_BRIDGE_CS: { if (CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Story"), IS_RANDO)) { *should = false; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index c0a897290..d0e86931c 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2437,15 +2437,15 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { // 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 (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && - !(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF)) { + bool gsTokensShuffled = Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) != RO_TOKENSANITY_OFF; + if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && !(IS_RANDO && gsTokensShuffled)) { textId = TEXT_GS_NO_FREEZE; } else { textId = TEXT_GS_FREEZE; } // In vanilla, GS token count is incremented prior to the text box displaying // In rando we need to bump the token count by one to show the correct count - s16 gsCount = gSaveContext.inventory.gsTokens + (IS_RANDO ? 1 : 0); + s16 gsCount = gSaveContext.inventory.gsTokens + ((IS_RANDO && gsTokensShuffled) ? 1 : 0); messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId, MF_FORMATTED); messageEntry.Replace("[[gsCount]]", std::to_string(gsCount)); } else if (CVarGetInteger(CVAR_ENHANCEMENT("SkulltulaFreeze"), 0) != 0 && diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 0b78892d4..9305c8ec5 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -336,8 +336,8 @@ void SohMenu::AddMenuEnhancements() { .CVar(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.GlitchAiding")) .Options(CheckboxOptions().Tooltip( "Don't skip cutscenes that are associated with useful glitches. Currently, it is " - "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, and the Box Skip One " - "Point in Jabu.")); + "only the Fire Temple Darunia CS, Forest Temple Poe Sisters CS, Dodongo Boss " + "Door Switch CS, Water Temple Dragon Switch CS, and the Box Skip One Point in Jabu.")); AddWidget(path, "Text", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Skip Pickup Messages", WIDGET_CVAR_CHECKBOX) diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 4ead0d268..ba9e1f703 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -4112,7 +4112,8 @@ void Interface_DrawItemButtons(PlayState* play) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { X_Margins_CD = Left_HUD_Margin; }; - C_Down_BTN_Pos[0] = (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + X_Margins_CD); + C_Down_BTN_Pos[0] = + OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + X_Margins_CD); } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == ANCHOR_RIGHT) { if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { X_Margins_CD = Right_HUD_Margin; diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c index 04aff53be..692497415 100644 --- a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c @@ -6,6 +6,7 @@ #include "z_bg_mori_kaitenkabe.h" #include "objects/object_mori_objects/object_mori_objects.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -97,7 +98,9 @@ void BgMoriKaitenkabe_Wait(BgMoriKaitenkabe* this, PlayState* play) { if ((this->timer > (28 - CVarGetInteger(CVAR_ENHANCEMENT("FasterBlockPush"), 0) * 4)) && !Player_InCsMode(play)) { BgMoriKaitenkabe_SetupRotate(this); - Player_SetCsActionWithHaltedActors(play, &this->dyna.actor, 8); + if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) { + Player_SetCsActionWithHaltedActors(play, &this->dyna.actor, 8); + } Math_Vec3f_Copy(&this->lockedPlayerPos, &player->actor.world.pos); push.x = Math_SinS(this->dyna.unk_158); push.y = 0.0f; @@ -131,7 +134,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) { Math_StepToF(&this->rotSpeed, 0.6f, 0.02f); if (Math_StepToF(&this->rotYdeg, this->rotDirection * 45.0f, this->rotSpeed)) { BgMoriKaitenkabe_SetupWait(this); - Player_SetCsActionWithHaltedActors(play, thisx, 7); + if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) { + Player_SetCsActionWithHaltedActors(play, thisx, 7); + } if (this->rotDirection > 0.0f) { thisx->home.rot.y += 0x2000; } else { @@ -148,7 +153,9 @@ void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, PlayState* play) { this->dyna.unk_150 = 0.0f; player->stateFlags2 &= ~PLAYER_STATE2_MOVING_DYNAPOLY; } - Math_Vec3f_Copy(&player->actor.world.pos, &this->lockedPlayerPos); + if (GameInteractor_Should(VB_FREEZE_LINK_FOR_FOREST_PILLARS, true)) { + Math_Vec3f_Copy(&player->actor.world.pos, &this->lockedPlayerPos); + } } void BgMoriKaitenkabe_Update(Actor* thisx, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c index ad1c76a3d..3d50bf330 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c @@ -7,6 +7,7 @@ #include "z_demo_kekkai.h" #include "objects/object_demo_kekkai/object_demo_kekkai.h" #include "scenes/dungeons/ganontika/ganontika_scene.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/ResourceManagerHelpers.h" #define FLAGS (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED) @@ -257,13 +258,15 @@ void DemoKekkai_TrialBarrierIdle(Actor* thisx, PlayState* play) { CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider1.base); CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider1.base); if (this->collider2.base.acFlags & AC_HIT) { - Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME); - // "I got it" - LOG_STRING("当ったよ"); - this->actor.update = DemoKekkai_TrialBarrierDispel; - this->timer = 0; - play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]); - gSaveContext.cutsceneTrigger = 1; + if (GameInteractor_Should(VB_PLAY_DISPEL_BARRIER_CS, true, this)) { + Sfx_PlaySfxCentered(NA_SE_SY_CORRECT_CHIME); + // "I got it" + LOG_STRING("当ったよ"); + this->actor.update = DemoKekkai_TrialBarrierDispel; + this->timer = 0; + play->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]); + gSaveContext.cutsceneTrigger = 1; + } } CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider2.base); func_8002F974(&this->actor, NA_SE_EV_TOWER_ENERGY - SFX_FLAG); diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h index 4e4a06c18..a8df145c2 100644 --- a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h @@ -6,7 +6,7 @@ struct DemoKekkai; -typedef void (*DemoKekkaiUpdateFunc)(struct DemoKekkai* this, PlayState* play); +typedef void (*DemoKekkaiUpdateFunc)(struct DemoKekkai* thisx, PlayState* play); typedef struct DemoKekkai { /* 0x0000 */ Actor actor; diff --git a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c index 61a10b056..192d749d4 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c +++ b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c @@ -314,19 +314,21 @@ void func_80A5372C(EnHeishi2* this, PlayState* play) { f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); - this->unk_2F2[0] = 200; - this->cameraId = Play_CreateSubCamera(play); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); - Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); - this->unk_280.x = 947.0f; - this->unk_280.y = 1195.0f; - this->unk_280.z = 2682.0f; + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + this->unk_2F2[0] = 200; + this->cameraId = Play_CreateSubCamera(play); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); + Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); + this->unk_280.x = 947.0f; + this->unk_280.y = 1195.0f; + this->unk_280.z = 2682.0f; - this->unk_28C.x = 1164.0f; - this->unk_28C.y = 1145.0f; - this->unk_28C.z = 3014.0f; + this->unk_28C.x = 1164.0f; + this->unk_28C.y = 1145.0f; + this->unk_28C.z = 3014.0f; - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } this->actionFunc = func_80A53850; } @@ -334,11 +336,15 @@ void func_80A53850(EnHeishi2* this, PlayState* play) { BgSpot15Saku* gate; SkelAnime_Update(&this->skelAnime); - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } gate = (BgSpot15Saku*)this->gate; if ((this->unk_2F2[0] == 0) || (gate->unk_168 == 0)) { - Play_ClearCamera(play, this->cameraId); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, true)) { + Play_ClearCamera(play, this->cameraId); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + } Message_CloseTextbox(play); this->unk_30C = 1; Player_SetCsActionWithHaltedActors(play, NULL, 7); @@ -479,23 +485,25 @@ void func_80A53DF8(EnHeishi2* this, PlayState* play) { f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); - this->unk_2F2[0] = 200; - this->cameraId = Play_CreateSubCamera(play); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); - Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); - this->unk_2BC.x = -71.0f; - this->unk_280.x = -71.0f; - this->unk_2BC.y = 571.0f; - this->unk_280.y = 571.0f; - this->unk_2BC.z = -1487.0f; - this->unk_280.z = -1487.0f; - this->unk_298.x = 181.0f; - this->unk_28C.x = 181.0f; - this->unk_298.y = 417.0f; - this->unk_28C.y = 417.0f; - this->unk_298.z = -1079.0f; - this->unk_28C.z = -1079.0f; - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + this->unk_2F2[0] = 200; + this->cameraId = Play_CreateSubCamera(play); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); + Play_ChangeCameraStatus(play, this->cameraId, CAM_STAT_ACTIVE); + this->unk_2BC.x = -71.0f; + this->unk_280.x = -71.0f; + this->unk_2BC.y = 571.0f; + this->unk_280.y = 571.0f; + this->unk_2BC.z = -1487.0f; + this->unk_280.z = -1487.0f; + this->unk_298.x = 181.0f; + this->unk_28C.x = 181.0f; + this->unk_298.y = 417.0f; + this->unk_28C.y = 417.0f; + this->unk_298.z = -1079.0f; + this->unk_28C.z = -1079.0f; + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } this->actionFunc = func_80A53F30; } @@ -503,11 +511,15 @@ void func_80A53F30(EnHeishi2* this, PlayState* play) { BgGateShutter* gate; SkelAnime_Update(&this->skelAnime); - Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, false)) { + Play_CameraSetAtEye(play, this->cameraId, &this->unk_280, &this->unk_28C); + } gate = (BgGateShutter*)this->gate; if ((this->unk_2F2[0] == 0) || (gate->openingState == 0)) { - Play_ClearCamera(play, this->cameraId); - Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + if (GameInteractor_Should(VB_PLAY_GATE_OPENING_OR_CLOSING_CS, true, this, true)) { + Play_ClearCamera(play, this->cameraId); + Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); + } if ((this->unk_30A != 2)) { if (this->unk_30A == 0) { this->actor.textId = 0x2015;