diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 7ad8473f1..24077697f 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -400,6 +400,52 @@ void RegisterShadowTag() { }); } +static bool hasAffectedHealth = false; +void UpdatePermanentHeartLossState() { + if (!GameInteractor::IsSaveLoaded()) return; + + if (!CVarGetInteger("gPermanentHeartLoss", 0) && hasAffectedHealth) { + uint8_t heartContainers = gSaveContext.sohStats.heartContainers; // each worth 16 health + uint8_t heartPieces = gSaveContext.sohStats.heartPieces; // each worth 4 health, but only in groups of 4 + uint8_t startingHealth = 16 * 3; + + + uint8_t newCapacity = startingHealth + (heartContainers * 16) + ((heartPieces - (heartPieces % 4)) * 4); + gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity); + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + hasAffectedHealth = false; + } +} + +void RegisterPermanentHeartLoss() { + GameInteractor::Instance->RegisterGameHook([](int16_t fileNum) { + hasAffectedHealth = false; + UpdatePermanentHeartLossState(); + }); + + GameInteractor::Instance->RegisterGameHook([]() { + if (!CVarGetInteger("gPermanentHeartLoss", 0) || !GameInteractor::IsSaveLoaded()) return; + + if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) { + gSaveContext.healthCapacity -= 16; + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + hasAffectedHealth = true; + } + }); +}; + +void RegisterDeleteFileOnDeath() { + GameInteractor::Instance->RegisterGameHook([]() { + if (!CVarGetInteger("gDeleteFileOnDeath", 0) || !GameInteractor::IsSaveLoaded() || &gPlayState->gameOverCtx == NULL || &gPlayState->pauseCtx == NULL) return; + + if (gPlayState->gameOverCtx.state == GAMEOVER_DEATH_MENU && gPlayState->pauseCtx.state == 9) { + SaveManager::Instance->DeleteZeldaFile(gSaveContext.fileNum); + hasAffectedHealth = false; + std::reinterpret_pointer_cast(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch("reset"); + } + }); +} + struct DayTimeGoldSkulltulas { uint16_t scene; uint16_t room; @@ -1088,6 +1134,8 @@ void InitMods() { RegisterDaytimeGoldSkultullas(); RegisterRupeeDash(); RegisterShadowTag(); + RegisterPermanentHeartLoss(); + RegisterDeleteFileOnDeath(); RegisterHyperBosses(); RegisterHyperEnemies(); RegisterBonkDamage(); diff --git a/soh/soh/Enhancements/mods.h b/soh/soh/Enhancements/mods.h index 2f0430475..8125659ab 100644 --- a/soh/soh/Enhancements/mods.h +++ b/soh/soh/Enhancements/mods.h @@ -9,6 +9,7 @@ extern "C" { void UpdateDirtPathFixState(int32_t sceneNum); void UpdateMirrorModeState(int32_t sceneNum); +void UpdatePermanentHeartLossState(); void InitMods(); #ifdef __cplusplus diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 614a6b690..9a006fb86 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -660,6 +660,14 @@ void DrawEnhancementsMenu() { if (ImGui::BeginMenu("Difficulty Options")) { + UIWidgets::PaddedEnhancementCheckbox("Delete File On Death", "gDeleteFileOnDeath", true, false); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); + UIWidgets::Tooltip("Dying will delete your file\n\n " ICON_FA_EXCLAMATION_TRIANGLE " WARNING " ICON_FA_EXCLAMATION_TRIANGLE "\nTHIS IS NOT REVERSABLE\nUSE AT YOUR OWN RISK!"); + ImGui::PopStyleColor(); + if (UIWidgets::PaddedEnhancementCheckbox("Permanent heart loss", "gPermanentHeartLoss", true, false)) { + UpdatePermanentHeartLossState(); + } + UIWidgets::Tooltip("When you lose 4 quarters of a heart you will permanently lose that heart container.\n\nDisabling this after the fact will restore your heart containers."); ImGui::Text("Damage Multiplier"); UIWidgets::EnhancementCombobox("gDamageMul", allPowers, 0); UIWidgets::Tooltip(