From 00a2a8556a5568d3a018869cc6629b71176f87af Mon Sep 17 00:00:00 2001 From: James Date: Thu, 21 May 2026 18:04:18 -0600 Subject: [PATCH 1/2] Introduce weighted trap selection for Extra Traps This commit replaces the previous vector-based trap enabling system with a new weighted trap selection mechanism. The `selectWeightedTrap` function is introduced to calculate weights for each trap type based on user-defined settings and select traps using a random weighted approach. The `getEnabledAddTraps` function is removed, and `RollRandomTrap` is updated to use the new weighted system. A fallback ensures that an "Ice Trap" is selected if no valid trap is chosen. A new configuration option, `ExtraTraps.WeightedTraps`, is added to toggle the weighted system. The UI is updated to include sliders for setting individual trap weights (0-100) and hides checkboxes for traps when the weighted system is enabled. Additional logging is added to trace the trap selection process, including weights and the selected trap. --- soh/soh/Enhancements/ExtraTraps.cpp | 45 +++++-- soh/soh/SohGui/SohMenuEnhancements.cpp | 159 +++++++++++++++++++++---- 2 files changed, 171 insertions(+), 33 deletions(-) diff --git a/soh/soh/Enhancements/ExtraTraps.cpp b/soh/soh/Enhancements/ExtraTraps.cpp index b506582df80..a002a6503db 100644 --- a/soh/soh/Enhancements/ExtraTraps.cpp +++ b/soh/soh/Enhancements/ExtraTraps.cpp @@ -48,22 +48,36 @@ const std::array teleportDestinations = { ENTR_TEMPLE_OF_TIME_WARP_PAD, }; -std::vector getEnabledAddTraps() { - std::vector enabledAddTraps; +AltTrapType selectWeightedTrap(uint64_t* state) { + float weights[ADD_TRAP_MAX] = { 0.0f }; + float totalWeight = 0.0f; + + bool weighted = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0); + for (int i = 0; i < ADD_TRAP_MAX; i++) { - if (CVarGetInteger(altTrapTypeCvars[i], 0)) { - if (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE && - (i == ADD_VOID_TRAP || i == ADD_TELEPORT_TRAP)) { - continue; // don't add void or teleport if you're holding the fishing pole, as this causes issues - } - enabledAddTraps.push_back(static_cast(i)); + float weight = weighted ? CVarGetInteger(altTrapTypeCvars[i], 0) : 1; + if (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE && (i == ADD_VOID_TRAP || i == ADD_TELEPORT_TRAP)) { + weight = 0; // don't add void or teleport if you're holding the fishing pole, as this causes issues } + totalWeight += weight; + weights[i] = totalWeight; + SPDLOG_TRACE("TRAP trap:{0} weight:{1} position:{2}", altTrapTypeCvars[i], weight, totalWeight); } - if (enabledAddTraps.size() == 0) { - enabledAddTraps.push_back(ADD_ICE_TRAP); + + if (totalWeight == 0.0f) // No weights? Just return an invalid value. + return ADD_TRAP_MAX; + + double target = ShipUtils::RandomDouble(state) * totalWeight; + + for (int i = 0; i < ADD_TRAP_MAX; i++) { + if (weights[i] >= target) { + SPDLOG_TRACE("SELECTED {0} {1} {2}", target, i, altTrapTypeCvars[i]); + return (AltTrapType)i; + } } - return enabledAddTraps; -}; + + return ADD_TRAP_MAX; +} static void RollRandomTrap(uint64_t seed) { uint64_t finalSeed = seed + (IS_RANDO ? static_cast(Rando::Context::GetInstance()->GetSeed()) @@ -71,7 +85,12 @@ static void RollRandomTrap(uint64_t seed) { uint64_t state; ShipUtils::RandInit(finalSeed, &state); - roll = ShipUtils::RandomElement(getEnabledAddTraps(), &state); + roll = selectWeightedTrap(&state); + if (roll == ADD_TRAP_MAX) // If it failed to pick a trap, fallback to a basic ice trap. + { + roll = ADD_ICE_TRAP; + } + switch (roll) { case ADD_ICE_TRAP: GameInteractor::RawAction::FreezePlayer(); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index fc4f9f95c80..8944c674c89 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -1658,58 +1658,177 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Additional Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Enabled")) .Options(CheckboxOptions().Tooltip("Enables additional Trap variants.")); + AddWidget(path, "Trap Options", WIDGET_SEPARATOR_TEXT).PreFunc([](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + + AddWidget(path, "Weighted Traps", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps")) + .PreFunc( + [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + AddWidget(path, "Tier 1 Traps:", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); AddWidget(path, "Freeze Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Ice")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Freeze Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Ice")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); + AddWidget(path, "Burn Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Burn")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Burn Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Burn")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); + AddWidget(path, "Shock Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Shock")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Shock Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Shock")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); + AddWidget(path, "Tier 2 Traps:", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + AddWidget(path, "Knockback Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Knockback")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Knockback Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Knockback")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); + AddWidget(path, "Speed Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Speed")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Speed Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Speed")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); + AddWidget(path, "Bomb Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Bomb")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Bomb Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Bomb")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); + AddWidget(path, "Tier 3 Traps:", WIDGET_TEXT).PreFunc([](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + AddWidget(path, "Void Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Void")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Void Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Void")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); + AddWidget(path, "Ammo Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Ammo")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Ammo Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Ammo")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); + AddWidget(path, "Death Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Kill")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Death Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Kill")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); + + AddWidget(path, "Teleport Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Teleport")) - .PreFunc( - [](WidgetInfo& info) { info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0; }); + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) != 0; + }); + + AddWidget(path, "Teleport Traps Weight", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("ExtraTraps.Teleport")) + .PreFunc([](WidgetInfo& info) { + info.isHidden = CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.Enabled"), 0) == 0 || + CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; + }) + .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); // Cheats path.sidebarName = "Cheats"; From 83451d52814fc56a0bb331d5a5095dd10a9fb070 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 24 May 2026 12:00:28 -0600 Subject: [PATCH 2/2] Reworked extra traps to avoid global state for various traps. Deferred traps and status traps (slow movement) now get placed into a time queue to be executed after x many game ticks. This should make it easier to add more traps. --- soh/soh/Enhancements/ExtraTraps.cpp | 139 ++++++++++++++----------- soh/soh/SohGui/SohMenuEnhancements.cpp | 1 - 2 files changed, 76 insertions(+), 64 deletions(-) diff --git a/soh/soh/Enhancements/ExtraTraps.cpp b/soh/soh/Enhancements/ExtraTraps.cpp index a002a6503db..ca1f4787d14 100644 --- a/soh/soh/Enhancements/ExtraTraps.cpp +++ b/soh/soh/Enhancements/ExtraTraps.cpp @@ -29,11 +29,6 @@ typedef enum { ADD_TRAP_MAX } AltTrapType; -static AltTrapType roll = ADD_TRAP_MAX; -static int statusTimer = -1; -static int eventTimer = -1; -static EntranceIndex teleportRoll = ENTR_MAX; - const char* altTrapTypeCvars[] = { CVAR_ENHANCEMENT("ExtraTraps.Ice"), CVAR_ENHANCEMENT("ExtraTraps.Burn"), CVAR_ENHANCEMENT("ExtraTraps.Shock"), CVAR_ENHANCEMENT("ExtraTraps.Knockback"), @@ -48,7 +43,22 @@ const std::array teleportDestinations = { ENTR_TEMPLE_OF_TIME_WARP_PAD, }; -AltTrapType selectWeightedTrap(uint64_t* state) { +typedef struct { + AltTrapType trap; + uint16_t ticks; + void (*execute)(Player*, uint32_t); + uint32_t state; +} EventTimer; + +static std::vector timers = std::vector(); + +static void queueTimer(AltTrapType trap, uint16_t ticks, void (*execute)(Player*, uint32_t), uint32_t state = 0) { + + EventTimer m = { .trap = trap, .ticks = ticks, .execute = execute, .state = state }; + timers.push_back(m); +} + +static AltTrapType selectWeightedTrap(uint64_t* state) { float weights[ADD_TRAP_MAX] = { 0.0f }; float totalWeight = 0.0f; @@ -59,6 +69,7 @@ AltTrapType selectWeightedTrap(uint64_t* state) { if (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE && (i == ADD_VOID_TRAP || i == ADD_TELEPORT_TRAP)) { weight = 0; // don't add void or teleport if you're holding the fishing pole, as this causes issues } + totalWeight += weight; weights[i] = totalWeight; SPDLOG_TRACE("TRAP trap:{0} weight:{1} position:{2}", altTrapTypeCvars[i], weight, totalWeight); @@ -67,7 +78,7 @@ AltTrapType selectWeightedTrap(uint64_t* state) { if (totalWeight == 0.0f) // No weights? Just return an invalid value. return ADD_TRAP_MAX; - double target = ShipUtils::RandomDouble(state) * totalWeight; + double target = ShipUtils::RandomDouble(state) * totalWeight; for (int i = 0; i < ADD_TRAP_MAX; i++) { if (weights[i] >= target) { @@ -84,52 +95,85 @@ static void RollRandomTrap(uint64_t seed) { : gSaveContext.ship.stats.fileCreatedAt); uint64_t state; ShipUtils::RandInit(finalSeed, &state); - - roll = selectWeightedTrap(&state); + AltTrapType roll = selectWeightedTrap(&state); if (roll == ADD_TRAP_MAX) // If it failed to pick a trap, fallback to a basic ice trap. { roll = ADD_ICE_TRAP; } switch (roll) { - case ADD_ICE_TRAP: + case ADD_ICE_TRAP: { GameInteractor::RawAction::FreezePlayer(); break; - case ADD_BURN_TRAP: + } + case ADD_BURN_TRAP: { GameInteractor::RawAction::BurnPlayer(); break; - case ADD_SHOCK_TRAP: + } + case ADD_SHOCK_TRAP: { GameInteractor::RawAction::ElectrocutePlayer(); break; - case ADD_KNOCK_TRAP: - eventTimer = 3; + } + case ADD_KNOCK_TRAP: { + queueTimer(ADD_KNOCK_TRAP, 3, + [](Player* player, uint32_t state) { GameInteractor::RawAction::KnockbackPlayer(1); }); break; - case ADD_SPEED_TRAP: + } + case ADD_SPEED_TRAP: { Audio_PlaySoundGeneral(NA_SE_VO_KZ_MOVE, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + + for (int i = 0; i < timers.size(); i++) { + if (timers[i].trap == ADD_SPEED_TRAP) { + timers[i].ticks += 200; + return; + } + } GameInteractor::State::MovementSpeedMultiplier = 0.5f; - statusTimer = 200; Notification::Emit({ .message = "Speed Decreased!" }); + queueTimer(ADD_SPEED_TRAP, 200, [](Player* player, uint32_t state) { + GameInteractor::State::MovementSpeedMultiplier = 1.0f; + Notification::Emit({ .message = "Speed Restored!" }); + }); break; - case ADD_BOMB_TRAP: - eventTimer = 3; + } + case ADD_BOMB_TRAP: { + queueTimer(ADD_BOMB_TRAP, 3, + [](Player* player, uint32_t state) { GameInteractor::RawAction::SpawnActor(ACTOR_EN_BOM, 1); }); break; - case ADD_VOID_TRAP: + } + case ADD_VOID_TRAP: { Audio_PlaySoundGeneral(NA_SE_EN_GANON_LAUGH, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); - eventTimer = 3; + queueTimer(ADD_VOID_TRAP, 3, [](Player* player, uint32_t state) { Play_TriggerRespawn(gPlayState); }); break; - case ADD_AMMO_TRAP: - eventTimer = 3; + } + case ADD_AMMO_TRAP: { Notification::Emit({ .message = "Ammo Halved!" }); + queueTimer(ADD_AMMO_TRAP, 3, [](Player* player, uint32_t state) { + AMMO(ITEM_STICK) = static_cast(floor(AMMO(ITEM_STICK) * 0.5f)); + AMMO(ITEM_NUT) = static_cast(floor(AMMO(ITEM_NUT) * 0.5f)); + AMMO(ITEM_SLINGSHOT) = static_cast(floor(AMMO(ITEM_SLINGSHOT) * 0.5f)); + AMMO(ITEM_BOW) = static_cast(floor(AMMO(ITEM_BOW) * 0.5f)); + AMMO(ITEM_BOMB) = static_cast(floor(AMMO(ITEM_BOMB) * 0.5f)); + AMMO(ITEM_BOMBCHU) = static_cast(floor(AMMO(ITEM_BOMBCHU) * 0.5f)); + Audio_PlaySoundGeneral(NA_SE_VO_FR_SMILE_0, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + }); break; - case ADD_KILL_TRAP: + } + case ADD_KILL_TRAP: { GameInteractor::RawAction::SetPlayerHealth(0); break; - case ADD_TELEPORT_TRAP: - eventTimer = 3; - teleportRoll = ShipUtils::RandomElement(teleportDestinations, &state); + } + case ADD_TELEPORT_TRAP: { + EntranceIndex teleportRoll = ShipUtils::RandomElement(teleportDestinations, &state); + + queueTimer( + ADD_TELEPORT_TRAP, 3, + [](Player* player, uint32_t state) { GameInteractor::RawAction::TeleportPlayer(state); }, teleportRoll); break; + } default: break; } @@ -137,47 +181,16 @@ static void RollRandomTrap(uint64_t seed) { static void OnPlayerUpdate() { Player* player = GET_PLAYER(gPlayState); - if (statusTimer == 0) { - GameInteractor::State::MovementSpeedMultiplier = 1.0f; - } - if (eventTimer == 0) { - switch (roll) { - case ADD_KNOCK_TRAP: - GameInteractor::RawAction::KnockbackPlayer(1); - break; - case ADD_BOMB_TRAP: - GameInteractor::RawAction::SpawnActor(ACTOR_EN_BOM, 1); - break; - case ADD_VOID_TRAP: - Play_TriggerRespawn(gPlayState); - break; - case ADD_AMMO_TRAP: - AMMO(ITEM_STICK) = static_cast(floor(AMMO(ITEM_STICK) * 0.5f)); - AMMO(ITEM_NUT) = static_cast(floor(AMMO(ITEM_NUT) * 0.5f)); - AMMO(ITEM_SLINGSHOT) = static_cast(floor(AMMO(ITEM_SLINGSHOT) * 0.5f)); - AMMO(ITEM_BOW) = static_cast(floor(AMMO(ITEM_BOW) * 0.5f)); - AMMO(ITEM_BOMB) = static_cast(floor(AMMO(ITEM_BOMB) * 0.5f)); - AMMO(ITEM_BOMBCHU) = static_cast(floor(AMMO(ITEM_BOMBCHU) * 0.5f)); - Audio_PlaySoundGeneral(NA_SE_VO_FR_SMILE_0, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, - &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); - break; - case ADD_TELEPORT_TRAP: { - GameInteractor::RawAction::TeleportPlayer(teleportRoll); - break; - } - default: - break; + for (int i = timers.size() - 1; i >= 0; i--) { + auto& n = timers[i]; + if (--n.ticks == 0) { + n.execute(player, n.state); + timers.erase(timers.begin() + i); } } - if (statusTimer >= 0) { - statusTimer--; - } - if (eventTimer >= 0) { - eventTimer--; - } } -void RegisterExtraTraps() { +static void RegisterExtraTraps() { COND_HOOK(OnPlayerUpdate, CVAR_EXTRA_TRAPS_VALUE, OnPlayerUpdate); COND_VB_SHOULD(VB_SHORT_CIRCUIT_GIVE_ITEM_PROCESS, true, { diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 8944c674c89..50a2006c8ab 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -1813,7 +1813,6 @@ void SohMenu::AddMenuEnhancements() { CVarGetInteger(CVAR_ENHANCEMENT("ExtraTraps.WeightedTraps"), 0) == 0; }) .Options(IntSliderOptions().DefaultValue(0).Min(0).Max(100).Tooltip("Set to zero (0) to disable.")); - AddWidget(path, "Teleport Traps", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ExtraTraps.Teleport"))