mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-21 13:36:50 -04:00
Add Gameplay settings & make Panes scrollable
This commit is contained in:
+212
-11
@@ -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,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user