From a82ce7966eefb5bb054be024bdc728eb93f3ba96 Mon Sep 17 00:00:00 2001 From: Thinkgraser Date: Fri, 22 Aug 2025 00:14:00 -0500 Subject: [PATCH 1/5] PB Gap counter added and separator setting added to Score and Personal Best counter --- include/customtypes/settings.hpp | 2 +- include/migration.hpp | 7 ++++ include/sourceui.hpp | 2 ++ include/utils.hpp | 2 +- qpm.json | 2 +- qpm.shared.json | 2 +- shared/options.hpp | 1 + shared/sources.hpp | 10 ++++++ shared/types.hpp | 7 ++++ src/config.cpp | 6 ++++ src/customtypes/settings.cpp | 2 +- src/editor.cpp | 2 +- src/environment.cpp | 2 +- src/events.cpp | 2 ++ src/hooks.cpp | 2 +- src/sources.cpp | 57 ++++++++++++++++++++++++++------ src/sourceui.cpp | 50 ++++++++++++++++++++++++++++ src/utils.cpp | 2 +- 18 files changed, 141 insertions(+), 19 deletions(-) diff --git a/include/customtypes/settings.hpp b/include/customtypes/settings.hpp index 07d473c..21f24e8 100644 --- a/include/customtypes/settings.hpp +++ b/include/customtypes/settings.hpp @@ -378,4 +378,4 @@ DECLARE_CLASS_CODEGEN_INTERFACES(Qounters, SpritesListSource, UnityEngine::MonoB static inline std::string const ReuseIdentifier = "QountersCustomListSource"; }; -#undef UES +#undef UES \ No newline at end of file diff --git a/include/migration.hpp b/include/migration.hpp index 97d5f3b..148fef0 100644 --- a/include/migration.hpp +++ b/include/migration.hpp @@ -32,6 +32,13 @@ namespace Qounters::Migration { VALUE(std::string, DefaultColor); }; + DECLARE_JSON_STRUCT(PBGap) { + VALUE(bool, Enabled); + VALUE(std::string, Position); + VALUE(float, Distance); + VALUE(int, TextSize); + }; + DECLARE_JSON_STRUCT(AverageCut) { VALUE(bool, Enabled); VALUE(std::string, Position); diff --git a/include/sourceui.hpp b/include/sourceui.hpp index 62f5af1..575638a 100644 --- a/include/sourceui.hpp +++ b/include/sourceui.hpp @@ -10,6 +10,7 @@ namespace Qounters { void ScoreUI(UnityEngine::GameObject* parent, UnparsedJSON options); void RankUI(UnityEngine::GameObject* parent, UnparsedJSON options); void PersonalBestUI(UnityEngine::GameObject* parent, UnparsedJSON options); + void PBGapUI(UnityEngine::GameObject* parent, UnparsedJSON options); void ComboUI(UnityEngine::GameObject* parent, UnparsedJSON options); void MultiplierUI(UnityEngine::GameObject* parent, UnparsedJSON options); void HealthUI(UnityEngine::GameObject* parent, UnparsedJSON options); @@ -42,6 +43,7 @@ namespace Qounters { void PlayerUI(UnityEngine::GameObject* parent, UnparsedJSON options); void RankUI(UnityEngine::GameObject* parent, UnparsedJSON options); void PersonalBestUI(UnityEngine::GameObject* parent, UnparsedJSON options); + void PBGapUI(UnityEngine::GameObject* parent, UnparsedJSON options); void ComboUI(UnityEngine::GameObject* parent, UnparsedJSON options); void MultiplierUI(UnityEngine::GameObject* parent, UnparsedJSON options); void HealthUI(UnityEngine::GameObject* parent, UnparsedJSON options); diff --git a/include/utils.hpp b/include/utils.hpp index 7a1fa9b..7bac7da 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -46,4 +46,4 @@ namespace Qounters::Utils { void RebuildWithScrollPosition(UnityEngine::GameObject* scrollView); UnityEngine::RectTransform* GetScrollViewTop(UnityEngine::GameObject* scrollView); -} +} \ No newline at end of file diff --git a/qpm.json b/qpm.json index 6d5eceb..8ece514 100644 --- a/qpm.json +++ b/qpm.json @@ -5,7 +5,7 @@ "info": { "name": "Qounters++", "id": "qounters++", - "version": "1.1.0", + "version": "1.1.1", "url": "https://github.com/Metalit/QountersPlusPlus", "additionalData": { "overrideSoName": "libqountersplusplus.so" diff --git a/qpm.shared.json b/qpm.shared.json index 3b677cc..6eb3687 100644 --- a/qpm.shared.json +++ b/qpm.shared.json @@ -7,7 +7,7 @@ "info": { "name": "Qounters++", "id": "qounters++", - "version": "1.1.0", + "version": "1.1.1", "url": "https://github.com/Metalit/QountersPlusPlus", "additionalData": { "overrideSoName": "libqountersplusplus.so" diff --git a/shared/options.hpp b/shared/options.hpp index 92bf615..6a6adad 100644 --- a/shared/options.hpp +++ b/shared/options.hpp @@ -10,6 +10,7 @@ namespace Qounters::Options { extern std::vector const FillStrings; extern std::vector const TypeStrings; extern std::vector const AnchorStrings; + extern std::vector const SeparatorStrings; extern std::vector const BaseGameObjectStrings; DECLARE_JSON_STRUCT(Gradient) { diff --git a/shared/sources.hpp b/shared/sources.hpp index 68f0cdf..91b3dc5 100644 --- a/shared/sources.hpp +++ b/shared/sources.hpp @@ -68,6 +68,7 @@ namespace Qounters::Sources { inline std::string const ScoreName = "Score"; inline std::string const RankName = "Rank"; inline std::string const PersonalBestName = "Personal Best"; + inline std::string const PBGapName = "PB Gap"; inline std::string const ComboName = "Combo"; inline std::string const MultiplierName = "Multiplier"; inline std::string const HealthName = "Health"; @@ -89,6 +90,7 @@ namespace Qounters::Sources { VALUE_DEFAULT(int, Saber, (int) Types::Sabers::Both); VALUE_DEFAULT(int, Decimals, 1); VALUE_DEFAULT(bool, Percentage, true); + VALUE_DEFAULT(int, Separator, (int) Types::Separators::Gap); }; DECLARE_JSON_STRUCT(Rank) { VALUE_DEFAULT(int, Saber, (int) Types::Sabers::Both); @@ -100,6 +102,13 @@ namespace Qounters::Sources { VALUE_DEFAULT(bool, Percentage, true); VALUE_DEFAULT(bool, HideFirstScore, true); VALUE_DEFAULT(bool, Label, true); + VALUE_DEFAULT(int, Separator, (int) Types::Separators::None); + }; + DECLARE_JSON_STRUCT(PBGap) { + VALUE_DEFAULT(bool, Percentage, false); + VALUE_DEFAULT(int, Decimals, 2); + VALUE_DEFAULT(bool, Sign, false); + VALUE_DEFAULT(int, Separator, (int) Types::Separators::None); }; DECLARE_JSON_STRUCT(Combo) { VALUE_DEFAULT(int, Saber, (int) Types::Sabers::Both); @@ -186,6 +195,7 @@ namespace Qounters::Sources { std::string GetScore(UnparsedJSON options); std::string GetRank(UnparsedJSON options); std::string GetPersonalBest(UnparsedJSON options); + std::string GetPBGap(UnparsedJSON options); std::string GetCombo(UnparsedJSON options); std::string GetMultiplier(UnparsedJSON options); std::string GetHealth(UnparsedJSON options); diff --git a/shared/types.hpp b/shared/types.hpp index 2142d51..5aa3ff1 100644 --- a/shared/types.hpp +++ b/shared/types.hpp @@ -18,6 +18,13 @@ namespace Qounters::Types { Enable, }; + enum class Separators { + None, + Gap, + Comma, + Period, + }; + template using SourceFn = std::function; using SourceUIFn = std::function; diff --git a/src/config.cpp b/src/config.cpp index 0b09052..d6a8721 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -58,6 +58,12 @@ std::vector const Options::AnchorStrings = { "Bottom", "Center", }; +std::vector const Options::SeparatorStrings = { + "None", + "Gap", + "Comma", + "Period", +}; std::vector const Options::BaseGameObjectStrings = { "Multiplier Ring", "Song Time Panel", // "Health Bar", diff --git a/src/customtypes/settings.cpp b/src/customtypes/settings.cpp index 4920938..8b3682e 100644 --- a/src/customtypes/settings.cpp +++ b/src/customtypes/settings.cpp @@ -1806,4 +1806,4 @@ int SpritesListSource::NumberOfCells() { void SpritesListSource::OnImageClicked(int imageIdx) { imageClickedCallback(imageIdx); -} +} \ No newline at end of file diff --git a/src/editor.cpp b/src/editor.cpp index 64ba6eb..1297e8b 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -746,4 +746,4 @@ Options::Component& Editor::GetSelectedComponent(int actionId) { nextUndoComponent = ret; } return ret; -} +} \ No newline at end of file diff --git a/src/environment.cpp b/src/environment.cpp index 805a287..3be2e97 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -511,4 +511,4 @@ void Environment::OnSceneEnd() { Object::FindObjectOfType()->FadeIn(); localPlayer = nullptr; localFakeConnectedPlayer = nullptr; -} +} \ No newline at end of file diff --git a/src/events.cpp b/src/events.cpp index e55149c..f58f988 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -19,6 +19,7 @@ static std::map>> eventS {Types::Sources::Shape, Sources::Shape::ScoreName}, {Types::Sources::Color, Sources::Color::RankName}, {Types::Sources::Color, Sources::Color::PersonalBestName}, + {Types::Sources::Text, Sources::Text::PBGapName}, {Types::Sources::Enable, Sources::Enable::PercentageName}, }}, {(int) MetaCore::Events::NoteCut, @@ -78,6 +79,7 @@ static std::map>> eventS { {Types::Sources::Text, Sources::Text::PPName}, {Types::Sources::Text, Sources::Text::PersonalBestName}, + {Types::Sources::Text, Sources::Text::PBGapName}, {Types::Sources::Text, Sources::Text::NotesName}, {Types::Sources::Color, Sources::Color::PlayerName}, {Types::Sources::Color, Sources::Color::PersonalBestName}, diff --git a/src/hooks.cpp b/src/hooks.cpp index 9fe6caf..2e79ce0 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -352,4 +352,4 @@ MAKE_AUTO_HOOK_MATCH(ImageView_OnPopulateMesh, &HMUI::ImageView::OnPopulateMesh, PopulateMeshHSVGradient(self, toFill, hsv->modified, hsv->modifier, hsv->elements); else ImageView_OnPopulateMesh(self, toFill); -} +} \ No newline at end of file diff --git a/src/sources.cpp b/src/sources.cpp index 3e94e64..3bd8ee4 100644 --- a/src/sources.cpp +++ b/src/sources.cpp @@ -18,6 +18,7 @@ std::vector, Types {Sources::Text::ScoreName, {Sources::Text::GetScore, Sources::Text::ScoreUI}}, {Sources::Text::RankName, {Sources::Text::GetRank, Sources::Text::RankUI}}, {Sources::Text::PersonalBestName, {Sources::Text::GetPersonalBest, Sources::Text::PersonalBestUI}}, + {Sources::Text::PBGapName, {Sources::Text::GetPBGap, Sources::Text::PBGapUI}}, {Sources::Text::ComboName, {Sources::Text::GetCombo, Sources::Text::ComboUI}}, {Sources::Text::MultiplierName, {Sources::Text::GetMultiplier, Sources::Text::MultiplierUI}}, {Sources::Text::HealthName, {Sources::Text::GetHealth, Sources::Text::HealthUI}}, @@ -188,16 +189,9 @@ std::string Sources::Text::GetScore(UnparsedJSON unparsed) { ratio *= 100; return Strings::FormatDecimals(ratio, opts.Decimals) + "%"; } else { - // spaces between every three digits, and pad zeroes if below 100 - auto number = fmt::format("{:03}", score); if (score < 1000) - return number; - size_t len = number.size(); - for (int i = 1; i <= (len - 1) / 3; i++) { - size_t split = len - 3 * i; - number = number.substr(0, split) + " " + number.substr(split); - } - return number; + return fmt::format("{:03}", score); + return Strings::FormatNumber(score, opts.Separator); } } std::string Sources::Text::GetRank(UnparsedJSON unparsed) { @@ -236,6 +230,7 @@ std::string Sources::Text::GetRank(UnparsedJSON unparsed) { std::string Sources::Text::GetPersonalBest(UnparsedJSON unparsed) { auto opts = unparsed.Parse(); + int best = Stats::GetBestScore(); if (best == -1) { if (opts.HideFirstScore) @@ -253,9 +248,51 @@ std::string Sources::Text::GetPersonalBest(UnparsedJSON unparsed) { ratio = max > 0 ? best / (Stats::GetModifierMultiplier(true, true) * max) : 1; text = Strings::FormatDecimals(ratio * 100, opts.Decimals) + "%"; } else - text = Environment::InSettings() && max == 1 ? "0" : std::to_string(best); + text = Environment::InSettings() && max == 1 ? "0" : Strings::FormatNumber(best, opts.Separator); return opts.Label ? "PB: " + text : text; } + +std::string Sources::Text::GetPBGap(UnparsedJSON unparsed) { + auto opts = unparsed.Parse(); + int best = Stats::GetBestScore(); + if (best == -1) { + best = 0; + } + int songMax = Stats::GetSongMaxScore(); + double current = Stats::GetScore((int) Types::Sabers::Both); + // PB modifiers would be applied to best score + current *= Stats::GetModifierMultiplier(true, true); + int max = Stats::GetMaxScore((int) Types::Sabers::Both); + + double bestRatio = songMax > 0 ? (static_cast(best) / songMax) : 1.0; + double ratio = max > 0 ? (current / max) : 1.0; + + + // rounded absolute difference + + std::string text; + + if (opts.Percentage) { + // percentage difference relative to current max + double percentDiff = (max > 0) ? ((ratio - bestRatio) * 100.0) : 0.0; + if (opts.Sign) { + text = (percentDiff >= 0 ? "+" : "") + Strings::FormatDecimals(percentDiff, opts.Decimals) + "%"; + } else { + text = Strings::FormatDecimals(std::abs(percentDiff), opts.Decimals) + "%"; + } + } else { + int difference = static_cast(std::round((ratio - bestRatio) * max)); + // raw score difference + if (opts.Sign) { + text = (difference >= 0 ? "+" : "") + Strings::FormatNumber(difference, opts.Separator); + } else { + text = Strings::FormatNumber(std::abs(difference), opts.Separator); + } + } + + return text; +} + std::string Sources::Text::GetCombo(UnparsedJSON unparsed) { auto opts = unparsed.Parse(); diff --git a/src/sourceui.cpp b/src/sourceui.cpp index ae40ef0..a43dbaa 100644 --- a/src/sourceui.cpp +++ b/src/sourceui.cpp @@ -49,6 +49,14 @@ void Sources::Text::ScoreUI(GameObject* parent, UnparsedJSON unparsed) { Editor::FinalizeAction(); }); BSML::Lite::AddHoverHint(percentage, "Show the score as a percentage instead of absolute value"); + + auto separator = MUI::CreateDropdownEnum(parent, "Separator", opts.Separator, Options::SeparatorStrings, [](int val) { + static int id = Editor::GetActionId(); + opts.Separator = val; + Editor::SetSourceOptions(id, opts); + Editor::FinalizeAction(); + }); + BSML::Lite::AddHoverHint(separator, "The separator to use in the number if absolute value"); } void Sources::Text::RankUI(GameObject* parent, UnparsedJSON unparsed) { static Rank opts; @@ -113,6 +121,48 @@ void Sources::Text::PersonalBestUI(GameObject* parent, UnparsedJSON unparsed) { Editor::FinalizeAction(); }); BSML::Lite::AddHoverHint(label, "Labels the text with \"PB: \""); + + auto separator = MUI::CreateDropdownEnum(parent, "Separator", opts.Separator, Options::SeparatorStrings, [](int val) { + static int id = Editor::GetActionId(); + opts.Separator = val; + Editor::SetSourceOptions(id, opts); + Editor::FinalizeAction(); + }); + BSML::Lite::AddHoverHint(separator, "The separator to use in the number if absolute value"); +} +void Sources::Text::PBGapUI(GameObject* parent, UnparsedJSON unparsed) { + static PBGap opts; + opts = unparsed.Parse(); + auto percentage = BSML::Lite::CreateToggle(parent, "Percentage", opts.Percentage, [](bool val) { + static int id = Editor::GetActionId(); + opts.Percentage = val; + Editor::SetSourceOptions(id, opts); + Editor::FinalizeAction(); + }); + BSML::Lite::AddHoverHint(percentage, "Display the difference from personal best as a percentage instead of absolute value"); + auto decimals = BSML::Lite::CreateIncrementSetting(parent, "Decimals", 0, 1, opts.Decimals, 0, 10, [](float val) { + static int id = Editor::GetActionId(); + opts.Decimals = val; + Editor::SetSourceOptions(id, opts); + Editor::FinalizeAction(); + }); + BSML::Lite::AddHoverHint(decimals, "The number of decimals to show, if a percentage"); + + auto sign = BSML::Lite::CreateToggle(parent, "Sign", opts.Sign, [](bool val) { + static int id = Editor::GetActionId(); + opts.Sign = val; + Editor::SetSourceOptions(id, opts); + Editor::FinalizeAction(); + }); + BSML::Lite::AddHoverHint(sign, "Display a positive or negative sign next to the difference"); + + auto separator = MUI::CreateDropdownEnum(parent, "Separator", opts.Separator, Options::SeparatorStrings, [](int val) { + static int id = Editor::GetActionId(); + opts.Separator = val; + Editor::SetSourceOptions(id, opts); + Editor::FinalizeAction(); + }); + BSML::Lite::AddHoverHint(separator, "The separator to use in the number if absolute value"); } void Sources::Text::ComboUI(GameObject* parent, UnparsedJSON unparsed) { static Combo opts; diff --git a/src/utils.cpp b/src/utils.cpp index d07903e..15ba363 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -350,4 +350,4 @@ void Utils::RebuildWithScrollPosition(UnityEngine::GameObject* scrollView) { UnityEngine::RectTransform* Utils::GetScrollViewTop(UnityEngine::GameObject* scrollView) { return scrollView->transform->parent->parent->parent->GetComponent(); -} +} \ No newline at end of file From 2a9cbe38a5a40352f1668d35c7c0a89ac27b37d9 Mon Sep 17 00:00:00 2001 From: Thinkgraser Date: Fri, 22 Aug 2025 00:38:50 -0500 Subject: [PATCH 2/5] changed to show positive and negative sign on PB Gap by default --- shared/sources.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/sources.hpp b/shared/sources.hpp index 91b3dc5..748d369 100644 --- a/shared/sources.hpp +++ b/shared/sources.hpp @@ -107,7 +107,7 @@ namespace Qounters::Sources { DECLARE_JSON_STRUCT(PBGap) { VALUE_DEFAULT(bool, Percentage, false); VALUE_DEFAULT(int, Decimals, 2); - VALUE_DEFAULT(bool, Sign, false); + VALUE_DEFAULT(bool, Sign, true); VALUE_DEFAULT(int, Separator, (int) Types::Separators::None); }; DECLARE_JSON_STRUCT(Combo) { From d877ee096d4b39b308971634ea2f6f85b08819c6 Mon Sep 17 00:00:00 2001 From: Thinkgraser Date: Fri, 22 Aug 2025 02:48:49 -0500 Subject: [PATCH 3/5] Added format function --- .vscode/settings.json | 8 +++++++- src/functions/format.hpp | 31 +++++++++++++++++++++++++++++++ src/sources.cpp | 12 +++++------- src/sourceui.cpp | 6 +++--- 4 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 src/functions/format.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index ce816bc..08ecc9b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -112,6 +112,12 @@ "xstddef": "cpp", "xtr1common": "cpp", "xtree": "cpp", - "xutility": "cpp" + "xutility": "cpp", + "execution": "cpp", + "format": "cpp", + "numbers": "cpp", + "print": "cpp", + "stop_token": "cpp", + "__verbose_abort": "cpp" } } \ No newline at end of file diff --git a/src/functions/format.hpp b/src/functions/format.hpp new file mode 100644 index 0000000..ac0c3dd --- /dev/null +++ b/src/functions/format.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include "../shared/types.hpp" // for Qounters::Types::Separators + +namespace Format { + inline std::string FormatNumber(int value, int separator) { + std::string seperatorString; + switch ((Qounters::Types::Separators) separator) { + case Qounters::Types::Separators::None: + return std::to_string(value); + case Qounters::Types::Separators::Gap: + seperatorString = " "; + break; + case Qounters::Types::Separators::Comma: + seperatorString = ","; + break; + case Qounters::Types::Separators::Period: + seperatorString = "."; + break; + } + + std::string num = std::to_string(std::abs(value)); + int insertPosition = static_cast(num.length()) - 3; + while (insertPosition > 0) { + num.insert(insertPosition, seperatorString); + insertPosition -= 3; + } + return (value < 0 ? "-" + num : num); + } + +} \ No newline at end of file diff --git a/src/sources.cpp b/src/sources.cpp index 3bd8ee4..6b9fa93 100644 --- a/src/sources.cpp +++ b/src/sources.cpp @@ -9,6 +9,7 @@ #include "pp.hpp" #include "sourceui.hpp" #include "utils.hpp" +#include "functions/format.hpp" using namespace Qounters; using namespace MetaCore; @@ -191,7 +192,7 @@ std::string Sources::Text::GetScore(UnparsedJSON unparsed) { } else { if (score < 1000) return fmt::format("{:03}", score); - return Strings::FormatNumber(score, opts.Separator); + return Format::FormatNumber(score, opts.Separator); } } std::string Sources::Text::GetRank(UnparsedJSON unparsed) { @@ -248,7 +249,7 @@ std::string Sources::Text::GetPersonalBest(UnparsedJSON unparsed) { ratio = max > 0 ? best / (Stats::GetModifierMultiplier(true, true) * max) : 1; text = Strings::FormatDecimals(ratio * 100, opts.Decimals) + "%"; } else - text = Environment::InSettings() && max == 1 ? "0" : Strings::FormatNumber(best, opts.Separator); + text = Environment::InSettings() && max == 1 ? "0" : Format::FormatNumber(best, opts.Separator); return opts.Label ? "PB: " + text : text; } @@ -267,9 +268,6 @@ std::string Sources::Text::GetPBGap(UnparsedJSON unparsed) { double bestRatio = songMax > 0 ? (static_cast(best) / songMax) : 1.0; double ratio = max > 0 ? (current / max) : 1.0; - - // rounded absolute difference - std::string text; if (opts.Percentage) { @@ -284,9 +282,9 @@ std::string Sources::Text::GetPBGap(UnparsedJSON unparsed) { int difference = static_cast(std::round((ratio - bestRatio) * max)); // raw score difference if (opts.Sign) { - text = (difference >= 0 ? "+" : "") + Strings::FormatNumber(difference, opts.Separator); + text = (difference >= 0 ? "+" : "") + Format::FormatNumber(difference, opts.Separator); } else { - text = Strings::FormatNumber(std::abs(difference), opts.Separator); + text = Format::FormatNumber(std::abs(difference), opts.Separator); } } diff --git a/src/sourceui.cpp b/src/sourceui.cpp index a43dbaa..dc63cd9 100644 --- a/src/sourceui.cpp +++ b/src/sourceui.cpp @@ -56,7 +56,7 @@ void Sources::Text::ScoreUI(GameObject* parent, UnparsedJSON unparsed) { Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(separator, "The separator to use in the number if absolute value"); + BSML::Lite::AddHoverHint(separator, "The thousands separator style to use if absolute value"); } void Sources::Text::RankUI(GameObject* parent, UnparsedJSON unparsed) { static Rank opts; @@ -128,7 +128,7 @@ void Sources::Text::PersonalBestUI(GameObject* parent, UnparsedJSON unparsed) { Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(separator, "The separator to use in the number if absolute value"); + BSML::Lite::AddHoverHint(separator, "The thousands separator style to use if absolute value"); } void Sources::Text::PBGapUI(GameObject* parent, UnparsedJSON unparsed) { static PBGap opts; @@ -162,7 +162,7 @@ void Sources::Text::PBGapUI(GameObject* parent, UnparsedJSON unparsed) { Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(separator, "The separator to use in the number if absolute value"); + BSML::Lite::AddHoverHint(separator, "The thousands separator style to use if absolute value"); } void Sources::Text::ComboUI(GameObject* parent, UnparsedJSON unparsed) { static Combo opts; From 991afd183e19a82bef29590a8c9cef02cbfef801 Mon Sep 17 00:00:00 2001 From: Thinkgraser Date: Mon, 25 Aug 2025 02:56:26 -0500 Subject: [PATCH 4/5] Moved PBGap into PersonalBest and added visual disable of settings when don't apply -Merged PBGap and Personal Best Together -Added GetScoreRatio and GetBestScoreRatio functions into Utils -Added disabling settings that aren't applicable and graying them out (Only in PersonalBest and Score) --- .vscode/settings.json | 8 +- include/customtypes/settings.hpp | 2 +- include/migration.hpp | 7 -- include/sourceui.hpp | 2 - include/utils.hpp | 3 + qpm.json | 2 +- qpm.shared.json | 2 +- shared/sources.hpp | 13 ++- src/customtypes/settings.cpp | 2 +- src/editor.cpp | 2 +- src/environment.cpp | 15 +++- src/events.cpp | 3 +- src/functions/format.hpp | 31 ------- src/hooks.cpp | 2 +- src/sources.cpp | 104 ++++++++--------------- src/sourceui.cpp | 140 ++++++++++++++++++++++--------- src/utils.cpp | 50 ++++++++++- 17 files changed, 216 insertions(+), 172 deletions(-) delete mode 100644 src/functions/format.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 08ecc9b..ce816bc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -112,12 +112,6 @@ "xstddef": "cpp", "xtr1common": "cpp", "xtree": "cpp", - "xutility": "cpp", - "execution": "cpp", - "format": "cpp", - "numbers": "cpp", - "print": "cpp", - "stop_token": "cpp", - "__verbose_abort": "cpp" + "xutility": "cpp" } } \ No newline at end of file diff --git a/include/customtypes/settings.hpp b/include/customtypes/settings.hpp index 21f24e8..07d473c 100644 --- a/include/customtypes/settings.hpp +++ b/include/customtypes/settings.hpp @@ -378,4 +378,4 @@ DECLARE_CLASS_CODEGEN_INTERFACES(Qounters, SpritesListSource, UnityEngine::MonoB static inline std::string const ReuseIdentifier = "QountersCustomListSource"; }; -#undef UES \ No newline at end of file +#undef UES diff --git a/include/migration.hpp b/include/migration.hpp index 148fef0..97d5f3b 100644 --- a/include/migration.hpp +++ b/include/migration.hpp @@ -32,13 +32,6 @@ namespace Qounters::Migration { VALUE(std::string, DefaultColor); }; - DECLARE_JSON_STRUCT(PBGap) { - VALUE(bool, Enabled); - VALUE(std::string, Position); - VALUE(float, Distance); - VALUE(int, TextSize); - }; - DECLARE_JSON_STRUCT(AverageCut) { VALUE(bool, Enabled); VALUE(std::string, Position); diff --git a/include/sourceui.hpp b/include/sourceui.hpp index 575638a..62f5af1 100644 --- a/include/sourceui.hpp +++ b/include/sourceui.hpp @@ -10,7 +10,6 @@ namespace Qounters { void ScoreUI(UnityEngine::GameObject* parent, UnparsedJSON options); void RankUI(UnityEngine::GameObject* parent, UnparsedJSON options); void PersonalBestUI(UnityEngine::GameObject* parent, UnparsedJSON options); - void PBGapUI(UnityEngine::GameObject* parent, UnparsedJSON options); void ComboUI(UnityEngine::GameObject* parent, UnparsedJSON options); void MultiplierUI(UnityEngine::GameObject* parent, UnparsedJSON options); void HealthUI(UnityEngine::GameObject* parent, UnparsedJSON options); @@ -43,7 +42,6 @@ namespace Qounters { void PlayerUI(UnityEngine::GameObject* parent, UnparsedJSON options); void RankUI(UnityEngine::GameObject* parent, UnparsedJSON options); void PersonalBestUI(UnityEngine::GameObject* parent, UnparsedJSON options); - void PBGapUI(UnityEngine::GameObject* parent, UnparsedJSON options); void ComboUI(UnityEngine::GameObject* parent, UnparsedJSON options); void MultiplierUI(UnityEngine::GameObject* parent, UnparsedJSON options); void HealthUI(UnityEngine::GameObject* parent, UnparsedJSON options); diff --git a/include/utils.hpp b/include/utils.hpp index 7bac7da..8ce934e 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -16,6 +16,9 @@ namespace Qounters::Utils { std::tuple GetBeatmapDetails(GlobalNamespace::BeatmapKey beatmap); std::string GetBeatmapIdentifier(GlobalNamespace::BeatmapKey beatmap); std::vector GetSimplifiedRequirements(GlobalNamespace::BeatmapKey beatmap); + std::string FormatNumber(int value, int separator); + double GetScoreRatio(bool includeModifiers = true, int saber = (int) Types::Sabers::Both); + double GetBestScoreRatio(); template U* ptr_cast(T* inst) { diff --git a/qpm.json b/qpm.json index 8ece514..6d5eceb 100644 --- a/qpm.json +++ b/qpm.json @@ -5,7 +5,7 @@ "info": { "name": "Qounters++", "id": "qounters++", - "version": "1.1.1", + "version": "1.1.0", "url": "https://github.com/Metalit/QountersPlusPlus", "additionalData": { "overrideSoName": "libqountersplusplus.so" diff --git a/qpm.shared.json b/qpm.shared.json index 6eb3687..3b677cc 100644 --- a/qpm.shared.json +++ b/qpm.shared.json @@ -7,7 +7,7 @@ "info": { "name": "Qounters++", "id": "qounters++", - "version": "1.1.1", + "version": "1.1.0", "url": "https://github.com/Metalit/QountersPlusPlus", "additionalData": { "overrideSoName": "libqountersplusplus.so" diff --git a/shared/sources.hpp b/shared/sources.hpp index 748d369..02c1e5d 100644 --- a/shared/sources.hpp +++ b/shared/sources.hpp @@ -57,6 +57,7 @@ namespace Qounters::Sources { } extern std::vector const AverageCutPartStrings; + extern std::vector const PBDisplayStrings; extern std::vector const NotesDisplayStrings; extern std::vector const PPLeaderboardStrings; extern std::vector const SaberSpeedModeStrings; @@ -68,7 +69,6 @@ namespace Qounters::Sources { inline std::string const ScoreName = "Score"; inline std::string const RankName = "Rank"; inline std::string const PersonalBestName = "Personal Best"; - inline std::string const PBGapName = "PB Gap"; inline std::string const ComboName = "Combo"; inline std::string const MultiplierName = "Multiplier"; inline std::string const HealthName = "Health"; @@ -98,17 +98,17 @@ namespace Qounters::Sources { VALUE_DEFAULT(bool, NegativeModifiers, true); }; DECLARE_JSON_STRUCT(PersonalBest) { + enum class Displays { + PersonalBest, + PBGap, + }; VALUE_DEFAULT(int, Decimals, 1); VALUE_DEFAULT(bool, Percentage, true); VALUE_DEFAULT(bool, HideFirstScore, true); VALUE_DEFAULT(bool, Label, true); VALUE_DEFAULT(int, Separator, (int) Types::Separators::None); - }; - DECLARE_JSON_STRUCT(PBGap) { - VALUE_DEFAULT(bool, Percentage, false); - VALUE_DEFAULT(int, Decimals, 2); + VALUE_DEFAULT(int, Display, (int) Displays::PersonalBest); VALUE_DEFAULT(bool, Sign, true); - VALUE_DEFAULT(int, Separator, (int) Types::Separators::None); }; DECLARE_JSON_STRUCT(Combo) { VALUE_DEFAULT(int, Saber, (int) Types::Sabers::Both); @@ -195,7 +195,6 @@ namespace Qounters::Sources { std::string GetScore(UnparsedJSON options); std::string GetRank(UnparsedJSON options); std::string GetPersonalBest(UnparsedJSON options); - std::string GetPBGap(UnparsedJSON options); std::string GetCombo(UnparsedJSON options); std::string GetMultiplier(UnparsedJSON options); std::string GetHealth(UnparsedJSON options); diff --git a/src/customtypes/settings.cpp b/src/customtypes/settings.cpp index 8b3682e..4920938 100644 --- a/src/customtypes/settings.cpp +++ b/src/customtypes/settings.cpp @@ -1806,4 +1806,4 @@ int SpritesListSource::NumberOfCells() { void SpritesListSource::OnImageClicked(int imageIdx) { imageClickedCallback(imageIdx); -} \ No newline at end of file +} diff --git a/src/editor.cpp b/src/editor.cpp index 1297e8b..64ba6eb 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -746,4 +746,4 @@ Options::Component& Editor::GetSelectedComponent(int actionId) { nextUndoComponent = ret; } return ret; -} \ No newline at end of file +} diff --git a/src/environment.cpp b/src/environment.cpp index 3be2e97..a01a1cb 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -44,6 +44,7 @@ #include "GlobalNamespace/SongPreviewPlayer.hpp" #include "GlobalNamespace/StandardLevelScenesTransitionSetupDataSO.hpp" #include "GlobalNamespace/UIKeyboardManager.hpp" +#include "GlobalNamespace/VRController.hpp" #include "GlobalNamespace/VRRenderingParamsSetup.hpp" #include "HMUI/ScreenSystem.hpp" #include "HMUI/ViewController.hpp" @@ -80,6 +81,8 @@ static MenuEnvironmentManager* menuEnvironment; static SongPreviewPlayer* songPreview; static VRUIControls::VRInputModule* vrInput; static UIKeyboardManager* keyboardManager; +static Transform* leftController; +static Transform* rightController; static GameObject* menuEnv; static bool wasNoTextsAndHuds = false; static GameObject* localPlayer; @@ -328,6 +331,12 @@ EnvironmentInfoSO* Environment::CurrentSettingsEnvironment() { void Environment::SetPlayerActive(bool active) { localPlayer->active = active; + for (auto controller : {leftController, rightController}) { + for (int i = 0; i < controller->childCount; i++) { + if (controller->GetChild(i)->name->Contains("CustomModels")) + controller->GetChild(i)->gameObject->active = !active; + } + } // reesabers doesn't seem to use the right color scheme even initially UpdateSaberColors(); } @@ -452,6 +461,8 @@ void Environment::OnSceneStart() { vrInput->_vrPointer->_leftVRController->gameObject->active = false; vrInput->_vrPointer->_rightVRController->gameObject->active = false; } + leftController = newInput->_vrPointer->_leftVRController->_viewAnchorTransform; + rightController = newInput->_vrPointer->_rightVRController->_viewAnchorTransform; menuEnv->active = false; @@ -507,8 +518,10 @@ void Environment::OnSceneEnd() { keyboardManager->OnDestroy(); keyboardManager->_vrInputModule = vrInput->i___GlobalNamespace__IVRInputModule(); } + leftController = nullptr; + rightController = nullptr; menuEnv->active = true; Object::FindObjectOfType()->FadeIn(); localPlayer = nullptr; localFakeConnectedPlayer = nullptr; -} \ No newline at end of file +} diff --git a/src/events.cpp b/src/events.cpp index f58f988..ed36d6d 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -19,7 +19,7 @@ static std::map>> eventS {Types::Sources::Shape, Sources::Shape::ScoreName}, {Types::Sources::Color, Sources::Color::RankName}, {Types::Sources::Color, Sources::Color::PersonalBestName}, - {Types::Sources::Text, Sources::Text::PBGapName}, + {Types::Sources::Text, Sources::Text::PersonalBestName}, {Types::Sources::Enable, Sources::Enable::PercentageName}, }}, {(int) MetaCore::Events::NoteCut, @@ -79,7 +79,6 @@ static std::map>> eventS { {Types::Sources::Text, Sources::Text::PPName}, {Types::Sources::Text, Sources::Text::PersonalBestName}, - {Types::Sources::Text, Sources::Text::PBGapName}, {Types::Sources::Text, Sources::Text::NotesName}, {Types::Sources::Color, Sources::Color::PlayerName}, {Types::Sources::Color, Sources::Color::PersonalBestName}, diff --git a/src/functions/format.hpp b/src/functions/format.hpp deleted file mode 100644 index ac0c3dd..0000000 --- a/src/functions/format.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include "../shared/types.hpp" // for Qounters::Types::Separators - -namespace Format { - inline std::string FormatNumber(int value, int separator) { - std::string seperatorString; - switch ((Qounters::Types::Separators) separator) { - case Qounters::Types::Separators::None: - return std::to_string(value); - case Qounters::Types::Separators::Gap: - seperatorString = " "; - break; - case Qounters::Types::Separators::Comma: - seperatorString = ","; - break; - case Qounters::Types::Separators::Period: - seperatorString = "."; - break; - } - - std::string num = std::to_string(std::abs(value)); - int insertPosition = static_cast(num.length()) - 3; - while (insertPosition > 0) { - num.insert(insertPosition, seperatorString); - insertPosition -= 3; - } - return (value < 0 ? "-" + num : num); - } - -} \ No newline at end of file diff --git a/src/hooks.cpp b/src/hooks.cpp index 2e79ce0..9fe6caf 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -352,4 +352,4 @@ MAKE_AUTO_HOOK_MATCH(ImageView_OnPopulateMesh, &HMUI::ImageView::OnPopulateMesh, PopulateMeshHSVGradient(self, toFill, hsv->modified, hsv->modifier, hsv->elements); else ImageView_OnPopulateMesh(self, toFill); -} \ No newline at end of file +} diff --git a/src/sources.cpp b/src/sources.cpp index 6b9fa93..e4b6aa7 100644 --- a/src/sources.cpp +++ b/src/sources.cpp @@ -9,7 +9,6 @@ #include "pp.hpp" #include "sourceui.hpp" #include "utils.hpp" -#include "functions/format.hpp" using namespace Qounters; using namespace MetaCore; @@ -19,7 +18,6 @@ std::vector, Types {Sources::Text::ScoreName, {Sources::Text::GetScore, Sources::Text::ScoreUI}}, {Sources::Text::RankName, {Sources::Text::GetRank, Sources::Text::RankUI}}, {Sources::Text::PersonalBestName, {Sources::Text::GetPersonalBest, Sources::Text::PersonalBestUI}}, - {Sources::Text::PBGapName, {Sources::Text::GetPBGap, Sources::Text::PBGapUI}}, {Sources::Text::ComboName, {Sources::Text::GetCombo, Sources::Text::ComboUI}}, {Sources::Text::MultiplierName, {Sources::Text::GetMultiplier, Sources::Text::MultiplierUI}}, {Sources::Text::HealthName, {Sources::Text::GetHealth, Sources::Text::HealthUI}}, @@ -93,7 +91,11 @@ Sources::PremadeInfo* Sources::GetPremadeInfo(std::string const& mod, std::strin } return nullptr; } +std::vector const Sources::PBDisplayStrings = { + "Personal Best", + "PB Gap", +}; std::vector const Sources::AverageCutPartStrings = { "Preswing", "Postswing", @@ -185,14 +187,13 @@ std::string Sources::Text::GetScore(UnparsedJSON unparsed) { int score = Stats::GetScore(opts.Saber); if (opts.Percentage) { - int max = Stats::GetMaxScore(opts.Saber); - double ratio = max > 0 ? score / (double) max : 1; + double ratio = Utils::GetScoreRatio(false, opts.Saber); ratio *= 100; return Strings::FormatDecimals(ratio, opts.Decimals) + "%"; } else { if (score < 1000) return fmt::format("{:03}", score); - return Format::FormatNumber(score, opts.Separator); + return Utils::FormatNumber(score, opts.Separator); } } std::string Sources::Text::GetRank(UnparsedJSON unparsed) { @@ -230,67 +231,48 @@ std::string Sources::Text::GetRank(UnparsedJSON unparsed) { } std::string Sources::Text::GetPersonalBest(UnparsedJSON unparsed) { auto opts = unparsed.Parse(); - - + bool pbGap = opts.Display == (int) PersonalBest::Displays::PBGap; int best = Stats::GetBestScore(); if (best == -1) { - if (opts.HideFirstScore) + if (opts.HideFirstScore && !pbGap) return opts.Label ? "PB: --" : "--"; else best = 0; } - int max = Stats::GetSongMaxScore(); - std::string text; - if (opts.Percentage) { - double ratio; - if (Environment::InSettings()) - ratio = Playtest::GetOverridePBRatio(); - else - ratio = max > 0 ? best / (Stats::GetModifierMultiplier(true, true) * max) : 1; - text = Strings::FormatDecimals(ratio * 100, opts.Decimals) + "%"; - } else - text = Environment::InSettings() && max == 1 ? "0" : Format::FormatNumber(best, opts.Separator); - return opts.Label ? "PB: " + text : text; -} - -std::string Sources::Text::GetPBGap(UnparsedJSON unparsed) { - auto opts = unparsed.Parse(); - int best = Stats::GetBestScore(); - if (best == -1) { - best = 0; - } int songMax = Stats::GetSongMaxScore(); - double current = Stats::GetScore((int) Types::Sabers::Both); - // PB modifiers would be applied to best score - current *= Stats::GetModifierMultiplier(true, true); - int max = Stats::GetMaxScore((int) Types::Sabers::Both); - - double bestRatio = songMax > 0 ? (static_cast(best) / songMax) : 1.0; - double ratio = max > 0 ? (current / max) : 1.0; - std::string text; - - if (opts.Percentage) { - // percentage difference relative to current max - double percentDiff = (max > 0) ? ((ratio - bestRatio) * 100.0) : 0.0; - if (opts.Sign) { - text = (percentDiff >= 0 ? "+" : "") + Strings::FormatDecimals(percentDiff, opts.Decimals) + "%"; + int maxScore = Stats::GetMaxScore((int) Types::Sabers::Both); + double ratio = Utils::GetScoreRatio(); + double bestRatio; + if (Environment::InSettings()) + bestRatio = Playtest::GetOverridePBRatio(); + else + bestRatio = songMax > 0 ? (!pbGap ? (double) best / (Stats::GetModifierMultiplier(true, true) * songMax) : Utils::GetBestScoreRatio()) : 1; + if (pbGap) { + if (opts.Percentage) { + double percentDiff = (maxScore > 0) ? ((ratio - bestRatio) * 100.0) : 0.0; + if (opts.Sign) { + text = (percentDiff >= 0 ? "+" : "") + Strings::FormatDecimals(percentDiff, opts.Decimals) + "%"; + } else { + text = Strings::FormatDecimals(std::abs(percentDiff), opts.Decimals) + "%"; + } } else { - text = Strings::FormatDecimals(std::abs(percentDiff), opts.Decimals) + "%"; + int difference = static_cast(std::round((ratio - bestRatio) * maxScore)); + if (opts.Sign) { + text = (difference >= 0 ? "+" : "") + Utils::FormatNumber(difference, opts.Separator); + } else { + text = Utils::FormatNumber(std::abs(difference), opts.Separator); + } } } else { - int difference = static_cast(std::round((ratio - bestRatio) * max)); - // raw score difference - if (opts.Sign) { - text = (difference >= 0 ? "+" : "") + Format::FormatNumber(difference, opts.Separator); + if (opts.Percentage) { + text = Strings::FormatDecimals(bestRatio * 100, opts.Decimals) + "%"; } else { - text = Format::FormatNumber(std::abs(difference), opts.Separator); + text = Environment::InSettings() && songMax == 1 ? "0" : Utils::FormatNumber(best, opts.Separator); } } - - return text; + return opts.Label ? (pbGap ? "PB Gap: " : "PB: ") + text : text; } - std::string Sources::Text::GetCombo(UnparsedJSON unparsed) { auto opts = unparsed.Parse(); @@ -422,9 +404,7 @@ float Sources::Shape::GetStatic(UnparsedJSON unparsed) { float Sources::Shape::GetScore(UnparsedJSON unparsed) { auto opts = unparsed.Parse(); - int score = Stats::GetScore(opts.Saber); - int max = Stats::GetMaxScore(opts.Saber); - return max > 0 ? score / (double) max : 1; + return Utils::GetScoreRatio(false, opts.Saber); } float Sources::Shape::GetMultiplier(UnparsedJSON unparsed) { auto opts = unparsed.Parse(); @@ -506,15 +486,8 @@ UnityEngine::Color Sources::Color::GetRank(UnparsedJSON unparsed) { } UnityEngine::Color Sources::Color::GetPersonalBest(UnparsedJSON unparsed) { auto opts = unparsed.Parse(); - - double best = Stats::GetBestScore(); - int songMax = Stats::GetSongMaxScore(); - double current = Stats::GetScore((int) Types::Sabers::Both); - // PB modifiers would be applied to best score - current *= Stats::GetModifierMultiplier(true, true); - int max = Stats::GetMaxScore((int) Types::Sabers::Both); - double bestRatio = songMax > 0 ? best / songMax : 1; - double ratio = max > 0 ? current / max : 1; + double bestRatio = Utils::GetBestScoreRatio(); + double ratio = Utils::GetScoreRatio(); return ratio >= bestRatio ? opts.Better : opts.Worse; } @@ -565,10 +538,7 @@ bool Sources::Enable::GetFullCombo(UnparsedJSON unparsed) { } bool Sources::Enable::GetPercentage(UnparsedJSON unparsed) { auto opts = unparsed.Parse(); - - int score = Stats::GetScore(opts.Saber); - int max = Stats::GetMaxScore(opts.Saber); - float percent = max > 0 ? score / (double) max : 1; + float percent = Utils::GetScoreRatio(false, opts.Saber); return percent * 100 >= opts.Percent; } bool Sources::Enable::GetFailed(UnparsedJSON unparsed) { diff --git a/src/sourceui.cpp b/src/sourceui.cpp index dc63cd9..d17bab5 100644 --- a/src/sourceui.cpp +++ b/src/sourceui.cpp @@ -1,5 +1,7 @@ #include "sourceui.hpp" +#include "UnityEngine/Events/UnityAction_1.hpp" +#include "UnityEngine/UI/Toggle.hpp" #include "bsml/shared/BSML-Lite.hpp" #include "editor.hpp" #include "metacore/shared/ui.hpp" @@ -34,21 +36,31 @@ void Sources::Text::ScoreUI(GameObject* parent, UnparsedJSON unparsed) { }); BSML::Lite::AddHoverHint(saber, "The saber to show the score for"); - auto inc = BSML::Lite::CreateIncrementSetting(parent, "Decimals", 0, 1, opts.Decimals, 0, 10, [](float val) { + auto percentage = BSML::Lite::CreateToggle(parent, "Percentage", opts.Percentage, [parent](bool val) { static int id = Editor::GetActionId(); - opts.Decimals = val; + opts.Percentage = val; + // logic to enable/disable decimals + auto decimalGO = parent->Find("decimals"); + auto decimals = decimalGO->GetComponent(); + decimals->set_interactable(val); + auto decimalCanvasGroup = decimals->get_gameObject()->GetComponent(); + if (val) { + decimalCanvasGroup->set_alpha(1); + } else { + decimalCanvasGroup->set_alpha(0.5f); // dimmed + } Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(inc, "The number of decimals in the score, if a percentage"); + BSML::Lite::AddHoverHint(percentage, "Show the score as a percentage instead of absolute value"); - auto percentage = BSML::Lite::CreateToggle(parent, "Percentage", opts.Percentage, [](bool val) { + auto inc = BSML::Lite::CreateIncrementSetting(parent, "Decimals", 0, 1, opts.Decimals, 0, 10, [](float val) { static int id = Editor::GetActionId(); - opts.Percentage = val; + opts.Decimals = val; Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(percentage, "Show the score as a percentage instead of absolute value"); + BSML::Lite::AddHoverHint(inc, "The number of decimals in the score, if a percentage"); auto separator = MUI::CreateDropdownEnum(parent, "Separator", opts.Separator, Options::SeparatorStrings, [](int val) { static int id = Editor::GetActionId(); @@ -57,6 +69,16 @@ void Sources::Text::ScoreUI(GameObject* parent, UnparsedJSON unparsed) { Editor::FinalizeAction(); }); BSML::Lite::AddHoverHint(separator, "The thousands separator style to use if absolute value"); + + // logic to set intractability of decimals on initialization + inc->set_name("decimals"); + inc->set_interactable(opts.Percentage); + auto decimalCanvasGroup = inc->get_gameObject()->AddComponent(); + if (opts.Percentage) { + decimalCanvasGroup->set_alpha(1); + } else { + decimalCanvasGroup->set_alpha(0.5f); // dimmed + } } void Sources::Text::RankUI(GameObject* parent, UnparsedJSON unparsed) { static Rank opts; @@ -90,29 +112,30 @@ void Sources::Text::PersonalBestUI(GameObject* parent, UnparsedJSON unparsed) { static PersonalBest opts; opts = unparsed.Parse(); - auto decimals = BSML::Lite::CreateIncrementSetting(parent, "Decimals", 0, 1, opts.Decimals, 0, 10, [](float val) { - static int id = Editor::GetActionId(); - opts.Decimals = val; - Editor::SetSourceOptions(id, opts); - Editor::FinalizeAction(); - }); - BSML::Lite::AddHoverHint(decimals, "The number of decimals to show, if a percentage"); - - auto percentage = BSML::Lite::CreateToggle(parent, "Percentage", opts.Percentage, [](bool val) { + auto percentage = BSML::Lite::CreateToggle(parent, "Percentage", opts.Percentage, [parent](bool val) { static int id = Editor::GetActionId(); opts.Percentage = val; + auto decimalGO = parent->Find("decimals"); + auto decimals = decimalGO->GetComponent(); + decimals->set_interactable(val); + auto decimalCanvasGroup = decimals->get_gameObject()->GetComponent(); + if (val) { + decimalCanvasGroup->set_alpha(1); + } else { + decimalCanvasGroup->set_alpha(0.5f); // dimmed + } Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(percentage, "Display the personal best as a percentage instead of absolute value"); + BSML::Lite::AddHoverHint(percentage, "Display the difference from personal best as a percentage instead of absolute value"); - auto showZero = BSML::Lite::CreateToggle(parent, "Show 0 On First Score", !opts.HideFirstScore, [](bool val) { + auto decimals = BSML::Lite::CreateIncrementSetting(parent, "Decimals", 0, 1, opts.Decimals, 0, 10, [](float val) { static int id = Editor::GetActionId(); - opts.HideFirstScore = !val; + opts.Decimals = val; Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(showZero, "Shows 0 if you have no personal best instead of \"--\""); + BSML::Lite::AddHoverHint(decimals, "The number of decimals to show, if a percentage"); auto label = BSML::Lite::CreateToggle(parent, "Label Text", opts.Label, [](bool val) { static int id = Editor::GetActionId(); @@ -120,7 +143,7 @@ void Sources::Text::PersonalBestUI(GameObject* parent, UnparsedJSON unparsed) { Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(label, "Labels the text with \"PB: \""); + BSML::Lite::AddHoverHint(label, "Labels the text with \"PB: \" or \"PB Gap: \""); auto separator = MUI::CreateDropdownEnum(parent, "Separator", opts.Separator, Options::SeparatorStrings, [](int val) { static int id = Editor::GetActionId(); @@ -129,24 +152,30 @@ void Sources::Text::PersonalBestUI(GameObject* parent, UnparsedJSON unparsed) { Editor::FinalizeAction(); }); BSML::Lite::AddHoverHint(separator, "The thousands separator style to use if absolute value"); -} -void Sources::Text::PBGapUI(GameObject* parent, UnparsedJSON unparsed) { - static PBGap opts; - opts = unparsed.Parse(); - auto percentage = BSML::Lite::CreateToggle(parent, "Percentage", opts.Percentage, [](bool val) { + + auto display = MUI::CreateDropdownEnum(parent, "Display", opts.Display, PBDisplayStrings, [parent](int val) { static int id = Editor::GetActionId(); - opts.Percentage = val; - Editor::SetSourceOptions(id, opts); - Editor::FinalizeAction(); - }); - BSML::Lite::AddHoverHint(percentage, "Display the difference from personal best as a percentage instead of absolute value"); - auto decimals = BSML::Lite::CreateIncrementSetting(parent, "Decimals", 0, 1, opts.Decimals, 0, 10, [](float val) { - static int id = Editor::GetActionId(); - opts.Decimals = val; - Editor::SetSourceOptions(id, opts); - Editor::FinalizeAction(); - }); - BSML::Lite::AddHoverHint(decimals, "The number of decimals to show, if a percentage"); + opts.Display = val; + // logic to enable/disable sign and showZero + auto signGO = parent->get_transform()->Find("sign"); + auto sign = signGO->GetComponent(); + auto showZeroGO = parent->get_transform()->Find("showZero"); + auto showZero = showZeroGO->GetComponent(); + sign->set_interactable(val == (int) PersonalBest::Displays::PBGap); + showZero->set_interactable(val == (int) PersonalBest::Displays::PersonalBest); + auto signCanvasGroup = sign->get_gameObject()->GetComponent(); + auto zeroCanvasGroup = showZero->get_gameObject()->GetComponent(); + if (val) { + signCanvasGroup->set_alpha(1); + zeroCanvasGroup->set_alpha(0.5f); // dimmed + } else { + signCanvasGroup->set_alpha(0.5f); // dimmed + zeroCanvasGroup->set_alpha(1); + } + Editor::SetSourceOptions(id, opts); + Editor::FinalizeAction(); + }); + BSML::Lite::AddHoverHint(display, "The personal best related value to show"); auto sign = BSML::Lite::CreateToggle(parent, "Sign", opts.Sign, [](bool val) { static int id = Editor::GetActionId(); @@ -154,15 +183,44 @@ void Sources::Text::PBGapUI(GameObject* parent, UnparsedJSON unparsed) { Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(sign, "Display a positive or negative sign next to the difference"); + BSML::Lite::AddHoverHint(sign, "Display a positive or negative sign next to the difference if PB Gap is enabled"); - auto separator = MUI::CreateDropdownEnum(parent, "Separator", opts.Separator, Options::SeparatorStrings, [](int val) { + auto showZero = BSML::Lite::CreateToggle(parent, "Show 0 On First Score", !opts.HideFirstScore, [](bool val) { static int id = Editor::GetActionId(); - opts.Separator = val; + opts.HideFirstScore = !val; Editor::SetSourceOptions(id, opts); Editor::FinalizeAction(); }); - BSML::Lite::AddHoverHint(separator, "The thousands separator style to use if absolute value"); + BSML::Lite::AddHoverHint(showZero, "Shows 0 if you have no personal best instead of \"--\" if PB Gap is disabled"); + + // logic to set intractability of the settings on initialization + // decimals + decimals->set_name("decimals"); + decimals->set_interactable(opts.Percentage); + auto decimalCanvasGroup = decimals->get_gameObject()->AddComponent(); + if (opts.Percentage) { + decimalCanvasGroup->set_alpha(1); + } else { + decimalCanvasGroup->set_alpha(0.5f); // dimmed + } + // Sign + sign->set_name("sign"); + sign->set_interactable(opts.Display == (int) PersonalBest::Displays::PBGap); + auto signCanvasGroup = sign->get_gameObject()->AddComponent(); + if (opts.Display == (int) PersonalBest::Displays::PBGap) { + signCanvasGroup->set_alpha(1); + } else { + signCanvasGroup->set_alpha(0.5f); // dimmed + } + // showZero + showZero->set_name("showZero"); + showZero->set_interactable(opts.Display == (int) PersonalBest::Displays::PersonalBest); + auto zeroCanvasGroup = showZero->get_gameObject()->AddComponent(); + if (opts.Display == (int) PersonalBest::Displays::PersonalBest) { + zeroCanvasGroup->set_alpha(1); + } else { + zeroCanvasGroup->set_alpha(0.5f); // dimmed + } } void Sources::Text::ComboUI(GameObject* parent, UnparsedJSON unparsed) { static Combo opts; diff --git a/src/utils.cpp b/src/utils.cpp index 15ba363..d28a17b 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "GlobalNamespace/BeatmapCharacteristicSO.hpp" #include "GlobalNamespace/BeatmapDifficulty.hpp" @@ -23,9 +24,11 @@ #include "customtypes/settings.hpp" #include "main.hpp" #include "metacore/shared/delegates.hpp" +#include "metacore/shared/stats.hpp" #include "metacore/shared/ui.hpp" #include "songcore/shared/SongCore.hpp" #include "songcore/shared/SongLoader/CustomBeatmapLevel.hpp" +#include "types.hpp" using namespace Qounters; using namespace MetaCore; @@ -90,6 +93,51 @@ std::vector Utils::GetSimplifiedRequirements(BeatmapKey beatmap) { return ret; } +std::string Utils::FormatNumber(int value, int separator) { + std::string seperatorString; + switch ((Qounters::Types::Separators) separator) { + case Qounters::Types::Separators::None: + return std::to_string(value); + case Qounters::Types::Separators::Gap: + seperatorString = " "; + break; + case Qounters::Types::Separators::Comma: + seperatorString = ","; + break; + case Qounters::Types::Separators::Period: + seperatorString = "."; + break; + } + + std::string num = std::to_string(std::abs(value)); + int insertPosition = static_cast(num.length()) - 3; + while (insertPosition > 0) { + num.insert(insertPosition, seperatorString); + insertPosition -= 3; + } + return (value < 0 ? "-" + num : num); +} + +double Utils::GetScoreRatio(bool includeModifiers, int saber) { + double current = Stats::GetScore((int) saber); + if (includeModifiers) { + current *= Stats::GetModifierMultiplier(true, true); + } + int maxScore = Stats::GetMaxScore((int) saber); + double ratio = maxScore > 0 ? current / maxScore : 1.0; + return ratio; +} + +double Utils::GetBestScoreRatio() { + int songMax = Stats::GetSongMaxScore(); + double best = Stats::GetBestScore(); + if (best == -1) { + best = 0; + } + double ratio = songMax > 0 ? best / songMax : 1; + return ratio; +} + BSML::ColorSetting* Utils::CreateColorPicker( UnityEngine::GameObject* parent, std::string name, @@ -350,4 +398,4 @@ void Utils::RebuildWithScrollPosition(UnityEngine::GameObject* scrollView) { UnityEngine::RectTransform* Utils::GetScrollViewTop(UnityEngine::GameObject* scrollView) { return scrollView->transform->parent->parent->parent->GetComponent(); -} \ No newline at end of file +} From d46d09842c2b7d96e2d0fec883577ca0076a4612 Mon Sep 17 00:00:00 2001 From: Thinkgraser Date: Mon, 25 Aug 2025 04:55:42 -0500 Subject: [PATCH 5/5] Changed Display String to Best Score --- src/sources.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sources.cpp b/src/sources.cpp index e4b6aa7..fcac9e1 100644 --- a/src/sources.cpp +++ b/src/sources.cpp @@ -92,7 +92,7 @@ Sources::PremadeInfo* Sources::GetPremadeInfo(std::string const& mod, std::strin return nullptr; } std::vector const Sources::PBDisplayStrings = { - "Personal Best", + "Best Score", "PB Gap", };