summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Dubé <159546+serprex@users.noreply.github.com>2026-10-05 23:38:48 +0000
committerGitHub <noreply@github.com>2026-10-05 23:38:48 +0000
commit3d8cd5aa887061284ecf8c92bf760de0afb72b80 (patch)
treeefb7403dcd61c3974502be54e64d2e23d7228aa4
parent0ce4c0fccd8fe53e3f6caeca4e6b5d40112b7de6 (diff)
Remove FQwc preset. Skip learning song
Also render exempt settings on preset selection, with values they'll have in-game
-rw-r--r--soh/assets/custom/presets/Speedrun - 2 Faster Quest.json10
-rw-r--r--soh/assets/custom/presets/Speedrun - 3 Faster Quest with Cutscenes.json123
-rw-r--r--soh/soh/Enhancements/speedrun/Speedrun.cpp73
3 files changed, 66 insertions, 140 deletions
diff --git a/soh/assets/custom/presets/Speedrun - 2 Faster Quest.json b/soh/assets/custom/presets/Speedrun - 2 Faster Quest.json
index 70fbdfaa5..0b9dd42df 100644
--- a/soh/assets/custom/presets/Speedrun - 2 Faster Quest.json
+++ b/soh/assets/custom/presets/Speedrun - 2 Faster Quest.json
@@ -18,6 +18,7 @@
"Autosave": 0,
"BetterAmmoRendering": 0,
"BetterFarore": 0,
+ "BetterOwl": 1,
"BombchuBowlingFirstPrize": 4,
"BombchusOOB": 1,
"BowArrowCycle": 1,
@@ -42,7 +43,9 @@
"EquipmentCanBeRemoved": 0,
"FastBoomerang": 0,
"FastChests": 1,
+ "FasterHeavyBlockLift": 1,
"FasterPauseMenu": 0,
+ "FasterShadowShip": 1,
"FileSelectMoreInfo": 1,
"FirstPersonGauntlets": 0,
"FishNeverEscape": 1,
@@ -88,18 +91,19 @@
"ShowDoorLocksOnBothSides": 0,
"SkipArrowAnimation": 1,
"SkipText": 1,
+ "SkulltulaFreeze": 1,
"SlowTextSpeed": 5,
"StaticExplosionRadius": 0,
"StrengthDisabled": 0,
"SwordToggle": 0,
- "TextSpeed": 5,
+ "TextSpeed": 6,
"TimeFlowFileSelect": 0,
"TimeSavers": {
"SkipCutscene": {
"BossIntro": 1,
"Entrances": 1,
"Intro": 1,
- "LearnSong": 0,
+ "LearnSong": 1,
"OnePoint": 0,
"QuickBossDeaths": 1,
"Story": 1
@@ -118,6 +122,8 @@
"exempt": [
"/gDeveloperTools/LogLevel",
"/gDeveloperTools/ResourceLogging",
+ "/gEnhancements/AskToEquip",
+ "/gEnhancements/BetterSaveMenu",
"/gEnhancements/DrawLineupTick",
"/gEnhancements/GraveHoles",
"/gEnhancements/IncludeHeldInputsBufferWindow",
diff --git a/soh/assets/custom/presets/Speedrun - 3 Faster Quest with Cutscenes.json b/soh/assets/custom/presets/Speedrun - 3 Faster Quest with Cutscenes.json
deleted file mode 100644
index d1a2a94f7..000000000
--- a/soh/assets/custom/presets/Speedrun - 3 Faster Quest with Cutscenes.json
+++ /dev/null
@@ -1,123 +0,0 @@
-{
- "blocks": {
- "enhancements": {
- "gCheats": {
- "EasyFrameAdvance": 0,
- "HookshotReachMultiplier": 1.0,
- "MoonJumpOnL": 0,
- "SpeedModifier": {
- "Value": 1.0
- }
- },
- "gDeveloperTools": null,
- "gEnhancements": {
- "3DSceneRender": 0,
- "AlwaysShowDungeonMinimapIcon": 0,
- "AskToEquip": 0,
- "AssignableTunicsAndBoots": 0,
- "Autosave": 0,
- "BetterAmmoRendering": 0,
- "BetterFarore": 0,
- "BombchuBowlingFirstPrize": 4,
- "BombchusOOB": 1,
- "BowArrowCycle": 1,
- "ChildHookshotSoftlock": 1,
- "CustomizeBombchuBowling": 1,
- "CustomizeFishing": 1,
- "CustomizeOcarinaGame": 0,
- "DampeWin": 1,
- "DisableBlackBars": 0,
- "DisableBombBillboarding": 0,
- "DisableCritWiggle": 1,
- "DisableFixedCamera": 0,
- "DisableGrottoRotation": 0,
- "DisableLOD": 1,
- "DisableLinkSwordTrail": 0,
- "DpadEquips": 1,
- "DpadNoDropOcarinaInput": 1,
- "DrawLineupTick": 0,
- "DynamicWalletIcon": 1,
- "EarlyEyeballFrog": 1,
- "EquipmentAlwaysVisible": 0,
- "EquipmentCanBeRemoved": 0,
- "FastBoomerang": 0,
- "FastChests": 1,
- "FasterPauseMenu": 0,
- "FileSelectMoreInfo": 1,
- "FirstPersonGauntlets": 0,
- "FishNeverEscape": 1,
- "FixDampeGoingBackwards": 0,
- "FixDaruniaDanceSpeed": 1,
- "FixDungeonMinimapIcon": 1,
- "FixSawSoftlock": 1,
- "FixVineFall": 1,
- "ForgeTime": 1,
- "GravediggingTourFix": 1,
- "HoverFishing": 1,
- "HyperBosses": 0,
- "HyperEnemies": 0,
- "IncludeHeldInputsBufferWindow": 0,
- "InjectItemCounts": {
- "GoldSkulltula": 1,
- "HeartContainer": 1,
- "HeartPiece": 1
- },
- "ItemUnequip": 0,
- "MinimalUI": 0,
- "MweepSpeed": 5.0,
- "N64WeirdFrames": 1,
- "NewDrops": 0,
- "NoHUDHeartAnimation": 0,
- "NoInputForCredits": 1,
- "OcarinaGame": {
- "StartingNotes": 8
- },
- "PauseAnyCursor": 2,
- "PauseBufferWindow": 5,
- "PauseMenuAnimatedLink": 0,
- "QuickBongoKill": 1,
- "QuickPutaway": 1,
- "QuitFishingAtDoor": 1,
- "RedGanonBlood": 1,
- "RememberMapToggleState": 0,
- "ReworkedTargeting": {
- "Enabled": 0
- },
- "ScaleAdultEquipmentAsChild": 1,
- "SceneSpecificDirtPathFix": 1,
- "ShowDoorLocksOnBothSides": 0,
- "SkipArrowAnimation": 1,
- "SkipText": 1,
- "SlowTextSpeed": 5,
- "StaticExplosionRadius": 0,
- "StrengthDisabled": 0,
- "SwordToggle": 0,
- "TextSpeed": 5,
- "TimeFlowFileSelect": 0,
- "TimeSavers": {
- "SkipCutscene": {
- "Intro": 1,
- "QuickBossDeaths": 1
- }
- },
- "ToTMedallionsColors": 0,
- "ToggleStrength": 0,
- "VisualAgony": 0,
- "WideShutterDoorRange": 1
- },
- "gRandoEnhancements": null
- }
- },
- "exempt": [
- "/gDeveloperTools/LogLevel",
- "/gDeveloperTools/ResourceLogging",
- "/gEnhancements/DrawLineupTick",
- "/gEnhancements/GraveHoles",
- "/gEnhancements/IncludeHeldInputsBufferWindow",
- "/gEnhancements/PauseBufferWindow"
- ],
- "fileType": 2,
- "presetName": "Speedrun - 3 Faster Quest with Cutscenes",
- "isBuiltIn": true,
- "ConfigVersion": 7
-}
diff --git a/soh/soh/Enhancements/speedrun/Speedrun.cpp b/soh/soh/Enhancements/speedrun/Speedrun.cpp
index 6ee47ebb4..00a9686ff 100644
--- a/soh/soh/Enhancements/speedrun/Speedrun.cpp
+++ b/soh/soh/Enhancements/speedrun/Speedrun.cpp
@@ -47,7 +47,8 @@ static const std::array<const char*, 4> sOwnedBlocks = {
};
#define SPEEDRUN_PRESET_NONE "None"
-#define SPEEDRUN_MAX_OPTIONS_ON_SCREEN 6
+#define SPEEDRUN_MAX_OPTIONS_ON_SCREEN 4
+#define SPEEDRUN_EXEMPT_ROWS 4
// {display name, preset name}, ending with "None" which has no preset.
static std::vector<std::pair<std::string, std::string>> sPresetChoices;
@@ -59,6 +60,9 @@ static nlohmann::json sExempt = nlohmann::json::array();
static std::string sPresetName = SPEEDRUN_PRESET_NONE;
static uint32_t sSettingsHash = 0;
static bool sMenuLocked = false;
+// "Name: value" of each exempt setting for the preset picked in the menu, and which preset they were made for.
+static std::vector<std::string> sExemptLines;
+static int sExemptLinesIndex = -1;
// Everything else, menu included, is hidden so settings can't be changed mid-run.
static const std::vector<std::string> sAllowedWindows = {
@@ -104,10 +108,10 @@ static void SetOwnedBlocks(const nlohmann::json& blocks) {
ShipInit::InitAll();
}
-static std::vector<nlohmann::json::json_pointer> GetExemptPaths() {
+static std::vector<nlohmann::json::json_pointer> GetExemptPaths(const nlohmann::json& exempt) {
std::vector<nlohmann::json::json_pointer> paths;
- for (const auto& path : sExempt) {
+ for (const auto& path : exempt) {
try {
paths.emplace_back(path.get<std::string>());
} catch (const std::exception& e) { SPDLOG_ERROR("Speedrun: bad exempt path {}: {}", path.dump(), e.what()); }
@@ -117,8 +121,8 @@ static std::vector<nlohmann::json::json_pointer> GetExemptPaths() {
}
// Copies the player's exempt settings onto blocks. Ones the player never set keep the preset's value.
-static void OverlayExemptSettings(nlohmann::json& blocks, const nlohmann::json& custom) {
- for (const auto& path : GetExemptPaths()) {
+static void OverlayExemptSettings(nlohmann::json& blocks, const nlohmann::json& custom, const nlohmann::json& exempt) {
+ for (const auto& path : GetExemptPaths(exempt)) {
if (custom.contains(path)) {
blocks[path] = custom[path];
}
@@ -253,7 +257,7 @@ extern "C" bool Ship_QuestDebugEnabled(u8 questId) {
// same build and settings. nlohmann sorts object keys, so the dump is stable.
static uint32_t HashSettings() {
nlohmann::json stripped = sSettings;
- for (const auto& path : GetExemptPaths()) {
+ for (const auto& path : GetExemptPaths(sExempt)) {
if (stripped.contains(path)) {
stripped[path.parent_pointer()].erase(path.back());
}
@@ -279,6 +283,41 @@ static void EmitHashNotification() {
});
}
+// The settings a new file gets with this preset: the player's settings, then the preset, then the exempt ones put back.
+static nlohmann::json BuildFileSettings(const std::string& presetKey, const nlohmann::json& custom,
+ const nlohmann::json& exempt) {
+ nlohmann::json settings = custom;
+
+ if (!presetKey.empty()) {
+ // Applied to the file's copy only, never the player's config.
+ settings = applyPresetToBlocks(presetKey, settings, { PRESET_SECTION_ENHANCEMENTS });
+ }
+
+ OverlayExemptSettings(settings, custom, exempt);
+ return settings;
+}
+
+static void UpdateExemptLines(uint8_t presetIndex) {
+ if (sExemptLinesIndex == presetIndex) {
+ return;
+ }
+ sExemptLinesIndex = presetIndex;
+ sExemptLines.clear();
+
+ const std::string& presetKey = sPresetChoices[presetIndex].second;
+ nlohmann::json exempt = GetPresetExempt(presetKey);
+ nlohmann::json settings = BuildFileSettings(presetKey, GetOwnedBlocks(), exempt);
+
+ for (const auto& path : GetExemptPaths(exempt)) {
+ std::string value = "default";
+ if (settings.contains(path)) {
+ const auto& entry = settings[path];
+ value = entry.is_string() ? entry.get<std::string>() : entry.dump();
+ }
+ sExemptLines.push_back(spdlog::fmt_lib::format("{}: {}", path.back(), value));
+ }
+}
+
extern "C" void Speedrun_LoadPresetChoices(FileChooseContext* fileChooseContext) {
// "None" keeps the player's current settings.
sPresetChoices = GetSpeedrunPresets();
@@ -287,6 +326,8 @@ extern "C" void Speedrun_LoadPresetChoices(FileChooseContext* fileChooseContext)
fileChooseContext->speedrunIndex = 0;
fileChooseContext->speedrunOffset = 0;
}
+ // Player may have changed settings since the menu was last open.
+ sExemptLinesIndex = -1;
}
extern "C" void FileChoose_UpdateSpeedrunMenu(GameState* gameState) {
@@ -335,6 +376,15 @@ extern "C" void FileChoose_DrawSpeedrunMenuWindowContents(FileChooseContext* fil
static_cast<f32>(92 + textYOffset), false);
}
}
+
+ // Exempt settings of the selected preset, with the values the new file will get, in columns under the list.
+ UpdateExemptLines(fileChooseContext->speedrunIndex);
+ for (size_t i = 0; i < sExemptLines.size(); i++) {
+ int16_t x = 65 + (int16_t)(i / SPEEDRUN_EXEMPT_ROWS) * 100;
+ int16_t y = 87 + SPEEDRUN_MAX_OPTIONS_ON_SCREEN * 16 + (int16_t)(i % SPEEDRUN_EXEMPT_ROWS) * 7;
+ Interface_DrawTextLine(fileChooseContext->state.gfxCtx, (char*)sExemptLines[i].c_str(), x, y, 180, 180, 180,
+ textAlpha, 0.5f, false);
+ }
}
extern "C" void Speedrun_InitSaveFile(u8 presetIndex) {
@@ -342,16 +392,9 @@ extern "C" void Speedrun_InitSaveFile(u8 presetIndex) {
sPresetName = sPresetChoices[presetIndex].first;
nlohmann::json custom = GetOwnedBlocks();
- sSettings = custom;
sConfigVersion = SOH::GetLatestConfigVersion();
sExempt = GetPresetExempt(presetKey);
-
- if (!presetKey.empty()) {
- // Applied to the file's copy only, never the player's config.
- sSettings = applyPresetToBlocks(presetKey, sSettings, { PRESET_SECTION_ENHANCEMENTS });
- }
-
- OverlayExemptSettings(sSettings, custom);
+ sSettings = BuildFileSettings(presetKey, custom, sExempt);
// Runs are always timed in real time, whatever the "RTA Timing on new files" option says.
gSaveContext.ship.stats.rtaTiming = 1;
@@ -416,7 +459,7 @@ static void LoadSaveSection() {
blocks = GetOwnedBlocks();
}
- OverlayExemptSettings(blocks, custom);
+ OverlayExemptSettings(blocks, custom, sExempt);
// No cosmetics during a run. Some scale Link or his sword, which changes his hitbox and reach.
blocks[CVAR_PREFIX_COSMETIC] = nlohmann::json::object();