Add Gameplay settings & make Panes scrollable

This commit is contained in:
Luke Street
2026-04-29 21:52:33 -06:00
parent 6425b452e7
commit b86d6e90e2
7 changed files with 315 additions and 43 deletions
+212 -11
View File
@@ -5,12 +5,44 @@
#include "aurora/gfx.h"
#include "dusk/audio/DuskAudioSystem.h"
#include "dusk/config.hpp"
#include "dusk/livesplit.h"
#include "m_Do/m_Do_main.h"
#include "pane.hpp"
#include "ui.hpp"
#include <algorithm>
#include <climits>
namespace dusk::ui {
namespace {
void reset_for_speedrun_mode() {
mDoMain::developmentMode = -1;
getSettings().game.damageMultiplier.setValue(1);
getSettings().game.instantDeath.setValue(false);
getSettings().game.noHeartDrops.setValue(false);
getSettings().game.infiniteHearts.setValue(false);
getSettings().game.infiniteArrows.setValue(false);
getSettings().game.infiniteBombs.setValue(false);
getSettings().game.infiniteOil.setValue(false);
getSettings().game.infiniteOxygen.setValue(false);
getSettings().game.infiniteRupees.setValue(false);
getSettings().game.enableIndefiniteItemDrops.setValue(false);
getSettings().game.moonJump.setValue(false);
getSettings().game.superClawshot.setValue(false);
getSettings().game.alwaysGreatspin.setValue(false);
getSettings().game.enableFastIronBoots.setValue(false);
getSettings().game.canTransformAnywhere.setValue(false);
getSettings().game.fastSpinner.setValue(false);
getSettings().game.freeMagicArmor.setValue(false);
getSettings().game.enableTurboKeybind.setValue(false);
}
} // namespace
template <typename T>
struct ConfigProps {
@@ -34,11 +66,11 @@ public:
if (!mHelpText.empty() && mRightPane != nullptr) {
listen(Rml::EventId::Focus, [this](Rml::Event&) {
mRightPane->clear();
mRightPane->add_text(mHelpText);
mRightPane->add_rml(mHelpText);
});
listen(Rml::EventId::Mouseover, [this](Rml::Event&) {
mRightPane->clear();
mRightPane->add_text(mHelpText);
mRightPane->add_rml(mHelpText);
});
}
}
@@ -87,23 +119,57 @@ protected:
}
};
class ConfigIntPercentSelect : public ConfigSelect<int> {
class ConfigIntSelect : public ConfigSelect<int> {
public:
ConfigIntPercentSelect(Rml::Element* parent, Props props)
: ConfigSelect(parent, std::move(props)) {}
struct Props {
Rml::String key;
ConfigVar<int>* value;
std::function<void(int)> onChange;
std::function<bool()> isDisabled;
Rml::String helpText;
Pane* rightPane = nullptr;
int min = 0;
int max = INT_MAX;
int step = 1;
Rml::String prefix;
Rml::String suffix;
};
ConfigIntSelect(Rml::Element* parent, Props props)
: ConfigSelect(parent,
{
.key = std::move(props.key),
.value = props.value,
.onChange = std::move(props.onChange),
.isDisabled = std::move(props.isDisabled),
.helpText = std::move(props.helpText),
.rightPane = props.rightPane,
}),
mMin(props.min), mMax(props.max), mStep(props.step), mPrefix(std::move(props.prefix)),
mSuffix(std::move(props.suffix)) {}
protected:
Rml::String get_value() override { return fmt::format("{}%", mVar->getValue()); }
Rml::String get_value() override {
return fmt::format("{}{}{}", mPrefix, mVar->getValue(), mSuffix);
}
bool handle_nav_command(NavCommand cmd) override {
if (cmd == NavCommand::Left) {
set_value(std::max(mVar->getValue() - 1, 0));
set_value(std::clamp(mVar->getValue() - mStep, mMin, mMax));
return true;
} else if (cmd == NavCommand::Right) {
set_value(std::min(mVar->getValue() + 1, 100));
set_value(std::clamp(mVar->getValue() + mStep, mMin, mMax));
return true;
}
return false;
}
private:
int mMin;
int mMax;
int mStep;
Rml::String mPrefix;
Rml::String mSuffix;
};
SettingsWindow::SettingsWindow() {
@@ -112,13 +178,15 @@ SettingsWindow::SettingsWindow() {
auto& rightPane = add_child<Pane>(content, Pane::Direction::Vertical);
leftPane.add_section("Volume");
leftPane.add_child<ConfigIntPercentSelect>(leftPane.root(),
ConfigIntPercentSelect::Props{
leftPane.add_child<ConfigIntSelect>(leftPane.root(),
ConfigIntSelect::Props{
.key = "Master Volume",
.value = &getSettings().audio.masterVolume,
.onChange = [](int value) { audio::SetMasterVolume(value / 100.f); },
.helpText = "Adjusts the volume of all sounds in the game.",
.rightPane = &rightPane,
.max = 100,
.suffix = "%",
});
leftPane.add_section("Effects");
@@ -158,7 +226,7 @@ SettingsWindow::SettingsWindow() {
leftPane.root(), ConfigBoolSelect::Props{
.key = key,
.value = &value,
.isDisabled = [] { return !getSettings().game.speedrunMode; },
.isDisabled = [] { return getSettings().game.speedrunMode; },
.helpText = helpText,
.rightPane = &rightPane,
});
@@ -195,7 +263,114 @@ SettingsWindow::SettingsWindow() {
});
add_tab("Gameplay", [this](Rml::Element* content) {
auto& leftPane = add_child<Pane>(content, Pane::Direction::Vertical);
auto& rightPane = add_child<Pane>(content, Pane::Direction::Vertical);
auto addOption = [&](const Rml::String& key, ConfigVar<bool>& value,
const Rml::String& helpText) {
leftPane.add_child<ConfigBoolSelect>(leftPane.root(), ConfigBoolSelect::Props{
.key = key,
.value = &value,
.helpText = helpText,
.rightPane = &rightPane,
});
};
auto addSpeedrunDisabledOption = [&](const Rml::String& key, ConfigVar<bool>& value,
const Rml::String& helpText) {
leftPane.add_child<ConfigBoolSelect>(
leftPane.root(), ConfigBoolSelect::Props{
.key = key,
.value = &value,
.isDisabled = [] { return getSettings().game.speedrunMode; },
.helpText = helpText,
.rightPane = &rightPane,
});
};
leftPane.add_section("General");
addOption("Mirror Mode", getSettings().game.enableMirrorMode,
"Mirrors the world horizontally, matching the Wii version of the game.");
addOption("Disable Main HUD", getSettings().game.disableMainHUD,
"Disables the main HUD of the game.<br/>Useful for recording or a more immersive "
"experience.");
addOption("Restore Wii 1.0 Glitches", getSettings().game.restoreWiiGlitches,
"Restores patched glitches from Wii USA 1.0, the first released version.");
addOption("Enable Rotating Link Doll", getSettings().game.enableLinkDollRotation,
"Enables rotating Link in the collection menu with the C-Stick.");
leftPane.add_section("Difficulty");
leftPane.add_child<ConfigIntSelect>(
leftPane.root(), ConfigIntSelect::Props{
.key = "Damage Multiplier",
.value = &getSettings().game.damageMultiplier,
.isDisabled = [] { return getSettings().game.speedrunMode; },
.helpText = "Multiplies incoming damage.",
.rightPane = &rightPane,
.min = 1,
.max = 8,
.prefix = "x",
});
addSpeedrunDisabledOption(
"Instant Death", getSettings().game.instantDeath, "Any hit will instantly kill you.");
addSpeedrunDisabledOption("No Heart Drops", getSettings().game.noHeartDrops,
"Hearts will never drop from enemies, pots, and various other places.");
leftPane.add_section("Quality of Life");
addOption("Bigger Wallets", getSettings().game.biggerWallets,
"Wallet sizes are like in the HD version. (500, 1000, 2000)");
addOption("Disable Rupee Cutscenes", getSettings().game.disableRupeeCutscenes,
"Rupees will not play cutscenes after you have collected them the first time.");
addOption("Faster Climbing", getSettings().game.fastClimbing,
"Quicker climbing on ladders and vines like the HD version.");
addOption("Faster Tears of Light", getSettings().game.fastTears,
"Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
addOption("Instant Saves", getSettings().game.instantSaves,
"Skips the delay when writing to the Memory Card.");
addOption("Hold B for Instant Text", getSettings().game.instantText,
"Makes text scroll immediately by holding B.");
addOption("No Climbing Miss Animation", getSettings().game.noMissClimbing,
"Prevents Link from playing a struggle animation when grabbing ledges or climbing on "
"vines.");
addOption("No Rupee Returns", getSettings().game.noReturnRupees,
"Always collect Rupees even if your Wallet is too full.");
addOption("No Sword Recoil", getSettings().game.noSwordRecoil,
"Link will not recoil when his sword hits walls.");
addOption("Skip TV Settings Screen", getSettings().game.hideTvSettingsScreen,
"Skips the TV calibration screen shown when loading a save.");
addOption("Skip Warning Screen", getSettings().game.skipWarningScreen,
"Skips the warning screen shown when starting the game.");
addOption("Sun's Song (R+X)", getSettings().game.sunsSong,
"Allows Wolf Link to howl and change the time of day.");
addOption("Quick Transform (R+Y)", getSettings().game.enableQuickTransform,
"Transform instantly by pressing R and Y simultaneously.");
leftPane.add_section("Speedrunning");
leftPane.add_child<ConfigBoolSelect>(leftPane.root(),
ConfigBoolSelect::Props{
.key = "Speedrun Mode",
.value = &getSettings().game.speedrunMode,
.onChange = [](bool) { reset_for_speedrun_mode(); },
.helpText = "Enables speedrunning options while restricting certain "
"gameplay modifiers.",
.rightPane = &rightPane,
});
leftPane.add_child<ConfigBoolSelect>(
leftPane.root(), ConfigBoolSelect::Props{
.key = "LiveSplit Connection",
.value = &getSettings().game.liveSplitEnabled,
.onChange =
[](bool enabled) {
if (enabled) {
speedrun::connectLiveSplit();
} else {
speedrun::disconnectLiveSplit();
}
},
.isDisabled = [] { return !getSettings().game.speedrunMode; },
.helpText = "Connect to LiveSplit server on localhost:16834.",
.rightPane = &rightPane,
});
});
add_tab("Graphics", [this](Rml::Element* content) {
@@ -246,6 +421,32 @@ SettingsWindow::SettingsWindow() {
});
leftPane.add_section("Resolution");
// TODO: Internal Resolution
leftPane.add_section("Post-Processing");
// TODO: Bloom
// TODO: Bloom Brightness
leftPane.add_section("Rendering");
leftPane.add_child<ConfigBoolSelect>(leftPane.root(),
ConfigBoolSelect::Props{
.key = "Unlock Framerate",
.value = &getSettings().game.enableFrameInterpolation,
.helpText =
"Uses inter-frame interpolation to enable higher frame rates.<br/><br/>Visual "
"artifacts, animation glitches, or instability may occur.",
.rightPane = &rightPane,
});
leftPane.add_child<ConfigBoolSelect>(
leftPane.root(), ConfigBoolSelect::Props{
.key = "Enable Depth of Field",
.value = &getSettings().game.enableDepthOfField,
});
leftPane.add_child<ConfigBoolSelect>(
leftPane.root(), ConfigBoolSelect::Props{
.key = "Enable Mini-Map Shadows",
.value = &getSettings().game.enableMapBackground,
});
});
}