diff --git a/include/ClockModConfig.hpp b/include/ClockModConfig.hpp index a37a8f6..bbd606c 100644 --- a/include/ClockModConfig.hpp +++ b/include/ClockModConfig.hpp @@ -3,14 +3,19 @@ DECLARE_CONFIG(ModConfig) { - CONFIG_VALUE(InSong, bool, "Show During Song", true, "If the Clock should be shown while playing a beatmap."); - CONFIG_VALUE(InReplay, bool, "Show During Replay", true, "If the Clock should be shown while playing a replay."); - CONFIG_VALUE(TwelveToggle, bool, "24/12 Toggle", false, "If time should be in 12 or 24 hour format."); - CONFIG_VALUE(SecToggle, bool, "Show Seconds", false, "If seconds should be displayed."); - CONFIG_VALUE(BattToggle, bool, "Show Battery Percentage", false, "Displays Battery percentage next to the clock."); - CONFIG_VALUE(RainbowClock, bool, "Rainbowify it", false, "Makes the Clock beautiful."); - CONFIG_VALUE(FontSize, float, "Font Size", 3.5, "Changes the Font Size of the Clock (Default: 3.5)"); - CONFIG_VALUE(ClockPosition, bool, "Clock Position (Top/Bottom)", false, "If the Clock should be at the top or the bottom"); + CONFIG_VALUE(ClockType, int, "Clock Type", 0, "Which time the Clock should display."); + CONFIG_VALUE(Stopwatch1Seconds, int, "Stopwatch 1 Seconds", 0, "The saved time on stopwatch 1."); + CONFIG_VALUE(Stopwatch2Seconds, int, "Stopwatch 2 Seconds", 0, "The saved time on stopwatch 2."); + CONFIG_VALUE(Stopwatch1Paused, bool, "Stopwatch 1 Paused", true, "If stopwatch 1 is locked from incrementing."); + CONFIG_VALUE(Stopwatch2Paused, bool, "Stopwatch 2 Paused", true, "If stopwatch 2 is locked from incrementing."); + CONFIG_VALUE(InSong, bool, "Show During Song", true, "If the Clock should be shown while playing a beatmap."); + CONFIG_VALUE(InReplay, bool, "Show During Replay", true, "If the Clock should be shown while playing a replay."); + CONFIG_VALUE(TwelveToggle, bool, "24/12 Toggle", false, "If time should be in 12 or 24 hour format."); + CONFIG_VALUE(SecToggle, bool, "Show Seconds", false, "If seconds should be displayed."); + CONFIG_VALUE(BattToggle, bool, "Show Battery Percentage", false, "Displays Battery percentage next to the clock."); + CONFIG_VALUE(RainbowClock, bool, "Rainbowify it", false, "Makes the Clock beautiful."); + CONFIG_VALUE(FontSize, float, "Font Size", 3.5, "Changes the Font Size of the Clock (Default: 3.5)"); + CONFIG_VALUE(ClockPosition, bool, "Clock Position (Top/Bottom)", false, "If the Clock should be at the top or the bottom"); // CONFIG_VALUE(ClockXOffset, double, "Clock X Offset", 0); // CONFIG_VALUE(ClockYOffset, double, "Clock Y Offset", 0); diff --git a/include/ClockValues.hpp b/include/ClockValues.hpp index c0d4ca3..063f72b 100644 --- a/include/ClockValues.hpp +++ b/include/ClockValues.hpp @@ -1,6 +1,20 @@ #pragma once #include "UnityEngine/Vector3.hpp" #include "UnityEngine/UI/VerticalLayoutGroup.hpp" +#include + +enum class ClockTypes { + CurrentTime, + SessionTime, + Stopwatch1, + Stopwatch2 +}; +static std::string_view clockTypeStrs[] = { + "Current Time", + "Session Time", + "Stopwatch 1", + "Stopwatch 2", +}; // This containts all the ClockPositions struct ClockPos_t { diff --git a/qpm.json b/qpm.json index ea18ef3..88d80ce 100644 --- a/qpm.json +++ b/qpm.json @@ -6,7 +6,7 @@ "info": { "name": "Clock Mod", "id": "ClockMod", - "version": "1.11.0-Dev", + "version": "1.15.0-Dev", "url": null, "additionalData": { "overrideSoName": "libClockMod.so", diff --git a/qpm.shared.json b/qpm.shared.json index bcb8bad..14c0f37 100644 --- a/qpm.shared.json +++ b/qpm.shared.json @@ -7,7 +7,7 @@ "info": { "name": "Clock Mod", "id": "ClockMod", - "version": "1.11.0-Dev", + "version": "1.15.0-Dev", "url": null, "additionalData": { "overrideSoName": "libClockMod.so", diff --git a/shared/ClockUpdater.hpp b/shared/ClockUpdater.hpp index cba811b..1a1fe13 100644 --- a/shared/ClockUpdater.hpp +++ b/shared/ClockUpdater.hpp @@ -27,6 +27,10 @@ DECLARE_CLASS_CODEGEN(ClockMod, ClockUpdater, UnityEngine::MonoBehaviour) { time_t rawtime; struct tm* timeinfo; + double sessionTimeSeconds = 0; + double stopwatch1Seconds = 0; + double stopwatch2Seconds = 0; + static ClockUpdater* instance; std::string _message; @@ -36,10 +40,16 @@ DECLARE_CLASS_CODEGEN(ClockMod, ClockUpdater, UnityEngine::MonoBehaviour) { const time_t getRawTime() const; struct tm* getTimeInfo(); struct tm* getTimeInfoUTC(); + const double getSessionTimeSeconds() const; + const double getStopwatch1Seconds() const; + const double getStopwatch2Seconds() const; + void resetStopwatch1(); + void resetStopwatch2(); TMPro::TextMeshProUGUI* getTextMesh(); void SetColor(UnityEngine::Color color); static ClockUpdater* getInstance(); static std::string getTimeFormat(); + static std::string getStopwatchString(const double totalSeconds); void ShowMessage(std::string message, int duration = 4); }; diff --git a/src/ClockUpdater.cpp b/src/ClockUpdater.cpp index 03efc78..7fd96d2 100644 --- a/src/ClockUpdater.cpp +++ b/src/ClockUpdater.cpp @@ -10,6 +10,7 @@ #include "UnityEngine/BatteryStatus.hpp" #include "UnityEngine/GradientColorKey.hpp" #include "UnityEngine/SystemInfo.hpp" +#include "UnityEngine/Time.hpp" using namespace UnityEngine; using namespace TMPro; @@ -53,6 +54,24 @@ namespace ClockMod { return time; } + // Turns an uncapped duration in seconds into a nicely formatted string + std::string ClockUpdater::getStopwatchString(const double totalSeconds) { + int seconds = (int)totalSeconds % 60; + int minutes = (int)(totalSeconds / 60) % 60; + int hours = (int)(totalSeconds / 60 / 60) % 24; + int days = (int)(totalSeconds / 60 / 60 / 24); + + bool showSeconds = getModConfig().SecToggle.GetValue(); + + std::string stopwatchStr; + if(days > 0) stopwatchStr += fmt::format("{}:", days); + if(hours > 0 || !stopwatchStr.empty() || !showSeconds) stopwatchStr += stopwatchStr.empty() ? fmt::format("{}:", hours) : fmt::format("{:02}:", hours); + stopwatchStr += stopwatchStr.empty() ? fmt::format("{}", minutes) : fmt::format("{:02}", minutes); + if(showSeconds) stopwatchStr += fmt::format(":{:02}", seconds); + + return stopwatchStr; + } + // New Battery Percentage Formatting std::string getBatteryString(float level, UnityEngine::BatteryStatus status, ClockMod::ClockUpdater* instance) { @@ -136,6 +155,8 @@ namespace ClockMod { text = get_gameObject()->GetComponent(); clockParent = get_transform()->GetParent(); + stopwatch1Seconds = getModConfig().Stopwatch1Seconds.GetValue(); + stopwatch2Seconds = getModConfig().Stopwatch2Seconds.GetValue(); getModConfig().ClockColor.AddChangeEvent([this](UnityEngine::Color color) { if (text) text->set_color(color); @@ -185,11 +206,16 @@ namespace ClockMod { text->get_transform()->set_localScale(UnityEngine::Vector3(1, 1, 1)); } - this_time = clock(); + static double lastSessionTimeSeconds = Time::get_realtimeSinceStartup(); + this_time = clock(); + sessionTimeSeconds = Time::get_realtimeSinceStartup(); + if(!getModConfig().Stopwatch1Paused.GetValue()) stopwatch1Seconds += sessionTimeSeconds - lastSessionTimeSeconds; + if(!getModConfig().Stopwatch2Paused.GetValue()) stopwatch2Seconds += sessionTimeSeconds - lastSessionTimeSeconds; time_counter += (double)(this_time - last_time); last_time = this_time; + lastSessionTimeSeconds = sessionTimeSeconds; if (!(time_counter > (double)(NUM_SECONDS * CLOCKS_PER_SEC))) { @@ -197,6 +223,15 @@ namespace ClockMod { } time_counter = 0; + // Scuffed, but not any more than the rest of this codebase + static int stopwatchSaveTimer = 0; + stopwatchSaveTimer++; + if(stopwatchSaveTimer > 5 / NUM_SECONDS && !Config.IsInSong) { // Avoid dropping frames during gameplay + getModConfig().Stopwatch1Seconds.SetValue(stopwatch1Seconds); + getModConfig().Stopwatch2Seconds.SetValue(stopwatch2Seconds); + stopwatchSaveTimer = 0; + } + if (getModConfig().InSong.GetValue() || !Config.noTextAndHUD) { time(&rawtime); timeinfo = localtime(&rawtime); @@ -210,14 +245,11 @@ namespace ClockMod { ClockPos.ap1 = (timeinfo && timeinfo->tm_mon == 3 && timeinfo->tm_mday == 1); std::string clockresult; - - if (_message.empty()) { - // Gets the time using the function at the top. - clockresult = getTimeString((struct tm*)timeinfo); - } - else { - clockresult = _message; - } + if(!_message.empty()) clockresult = _message; + else if(getModConfig().ClockType.GetValue() == static_cast(ClockTypes::SessionTime)) clockresult = getStopwatchString(sessionTimeSeconds); + else if(getModConfig().ClockType.GetValue() == static_cast(ClockTypes::Stopwatch1)) clockresult = getStopwatchString(stopwatch1Seconds); + else if(getModConfig().ClockType.GetValue() == static_cast(ClockTypes::Stopwatch2)) clockresult = getStopwatchString(stopwatch2Seconds); + else clockresult = getTimeString((struct tm*)timeinfo); // Checks, if the clock is set to rainbowify if (getModConfig().RainbowClock.GetValue()) { @@ -265,6 +297,26 @@ namespace ClockMod { return gmtime(&time); } + const double ClockUpdater::getSessionTimeSeconds() const { + return sessionTimeSeconds; + } + + const double ClockUpdater::getStopwatch1Seconds() const { + return stopwatch1Seconds; + } + + const double ClockUpdater::getStopwatch2Seconds() const { + return stopwatch2Seconds; + } + + void ClockUpdater::resetStopwatch1() { + stopwatch1Seconds = 0; + } + + void ClockUpdater::resetStopwatch2() { + stopwatch2Seconds = 0; + } + TMPro::TextMeshProUGUI* ClockUpdater::getTextMesh() { return text; } diff --git a/src/ClockViewContoller.cpp b/src/ClockViewContoller.cpp index 6a1f93b..68c5f89 100644 --- a/src/ClockViewContoller.cpp +++ b/src/ClockViewContoller.cpp @@ -1,5 +1,6 @@ #include "ClockModConfig.hpp" #include "ClockUpdater.hpp" +#include "ClockValues.hpp" #include "ClockViewController.hpp" using namespace ClockMod; @@ -9,6 +10,7 @@ using namespace ClockMod; #include "bsml/shared/BSML/Components/ModalColorPicker.hpp" #include "custom-types/shared/coroutine.hpp" #include "custom-types/shared/macros.hpp" +#include "custom-types/shared/delegate.hpp" #include "HMUI/CurvedTextMeshPro.hpp" #include "HMUI/ScrollView.hpp" @@ -22,6 +24,8 @@ using namespace ClockMod; #include "UnityEngine/Canvas.hpp" #include "UnityEngine/Color.hpp" #include "UnityEngine/GameObject.hpp" +#include "UnityEngine/UI/Button.hpp" +#include "UnityEngine/UI/LayoutElement.hpp" #include "UnityEngine/WaitForSecondsRealtime.hpp" @@ -42,11 +46,17 @@ namespace ClockMod { while (SettingsOpen) { char timeInformation[45]; strftime(timeInformation, sizeof(timeInformation), "Your Timezone - %Z UTC offset - %z", ClockUpdater::getInstance()->getTimeInfo()); + char UTCtime[40]; - strftime(UTCtime, sizeof(UTCtime), std::string("\r\n Current Time in UTC - " + ClockUpdater::getTimeFormat()).c_str(), ClockUpdater::getInstance()->getTimeInfoUTC()); + strftime(UTCtime, sizeof(UTCtime), std::string("\r\nCurrent Time in UTC - " + ClockUpdater::getTimeFormat()).c_str(), ClockUpdater::getInstance()->getTimeInfoUTC()); //strftime(UTCtime, sizeof(UTCtime), std::string("\r\n Current Time in UTC - " + ClockUpdater::getTimeFormat()).c_str(), gmtime(ClockUpdater::getInstance()->getRawTime())); + + std::string sessionTime = "\nSession Time - " + ClockUpdater::getStopwatchString(ClockUpdater::getInstance()->getSessionTimeSeconds()); + std::string stopwatch1Time = "\nStopwatch 1 Time - " + ClockUpdater::getStopwatchString(ClockUpdater::getInstance()->getStopwatch1Seconds()); + std::string stopwatch2Time = "\nStopwatch 2 Time - " + ClockUpdater::getStopwatchString(ClockUpdater::getInstance()->getStopwatch2Seconds()); + if (TimeInfo && SettingsOpen) - TimeInfo->set_text(std::string(timeInformation) + UTCtime); + TimeInfo->set_text(std::string(timeInformation) + UTCtime + sessionTime + stopwatch1Time + stopwatch2Time); co_yield reinterpret_cast(UnityEngine::WaitForSecondsRealtime::New_ctor(0.1)); } co_return; @@ -67,7 +77,7 @@ namespace ClockMod { std::string timeFormat = "Your Timezone - %Z\nUTC offset - %z"; strftime(timeInformation, sizeof(timeInformation), timeFormat.c_str(), instance->getTimeInfo()); // We have to specify sizeDelta here otherwise things will overlap - TimeInfo = BSML::Lite::CreateText(parent, std::string(timeInformation), TMPro::FontStyles::Normal, {0,0}, {0,15}); + TimeInfo = BSML::Lite::CreateText(parent, std::string(timeInformation), TMPro::FontStyles::Normal, {0,0}, {0,5*6}); // TimeInfo = BSML::Lite::CreateText(parent, std::string(timeInformation), TMPro::FontStyles::Normal); ColorPicker = BSML::Lite::CreateColorPickerModal(parent, getModConfig().ClockColor.GetName(), getModConfig().ClockColor.GetValue(), @@ -83,6 +93,38 @@ namespace ClockMod { ); } + // Could optimize these into a small lambda or #define, but there's only two as of now so it's not that big a deal + auto stopwatch1PauseButton = BSML::Lite::CreateUIButton(parent, getModConfig().Stopwatch1Paused.GetValue() ? "Start" : "Pause", {70, -19.9}, {-5, 0}, nullptr); + stopwatch1PauseButton->GetComponent()->set_ignoreLayout(true); + stopwatch1PauseButton->get_transform()->set_localScale({0.9, 0.9, 0.9}); + stopwatch1PauseButton->get_onClick()->AddListener(custom_types::MakeDelegate(std::function([stopwatch1PauseButton](){ + getModConfig().Stopwatch1Paused.SetValue(!getModConfig().Stopwatch1Paused.GetValue()); + if(getModConfig().Stopwatch1Paused.GetValue()) BSML::Lite::SetButtonText(stopwatch1PauseButton, "Start"); + else BSML::Lite::SetButtonText(stopwatch1PauseButton, "Pause"); + }))); + auto stopwatch1ResetButton = BSML::Lite::CreateUIButton(parent, "Reset", {83.5, -19.9}, {-5, 0}, nullptr); + stopwatch1ResetButton->GetComponent()->set_ignoreLayout(true); + stopwatch1ResetButton->get_transform()->set_localScale({0.9, 0.9, 0.9}); + stopwatch1ResetButton->get_onClick()->AddListener(custom_types::MakeDelegate(std::function([](){ + if(ClockMod::ClockUpdater::getInstance()) ClockMod::ClockUpdater::getInstance()->resetStopwatch1(); + }))); + + auto stopwatch2PauseButton = BSML::Lite::CreateUIButton(parent, getModConfig().Stopwatch2Paused.GetValue() ? "Start" : "Pause", {70, -25.5}, {-5, 0}, nullptr); + stopwatch2PauseButton->GetComponent()->set_ignoreLayout(true); + stopwatch2PauseButton->get_transform()->set_localScale({0.9, 0.9, 0.9}); + stopwatch2PauseButton->get_onClick()->AddListener(custom_types::MakeDelegate(std::function([stopwatch2PauseButton](){ + getModConfig().Stopwatch2Paused.SetValue(!getModConfig().Stopwatch2Paused.GetValue()); + if(getModConfig().Stopwatch2Paused.GetValue()) BSML::Lite::SetButtonText(stopwatch2PauseButton, "Start"); + else BSML::Lite::SetButtonText(stopwatch2PauseButton, "Pause"); + }))); + auto stopwatch2ResetButton = BSML::Lite::CreateUIButton(parent, "Reset", {83.5, -25.5}, {-5, 0}, nullptr); + stopwatch2ResetButton->GetComponent()->set_ignoreLayout(true); + stopwatch2ResetButton->get_transform()->set_localScale({0.9, 0.9, 0.9}); + stopwatch2ResetButton->get_onClick()->AddListener(custom_types::MakeDelegate(std::function([](){ + if(ClockMod::ClockUpdater::getInstance()) ClockMod::ClockUpdater::getInstance()->resetStopwatch2(); + }))); + + AddConfigValueDropdownEnum(parent, getModConfig().ClockType, clockTypeStrs); AddConfigValueToggle(parent, getModConfig().InSong); AddConfigValueToggle(parent, getModConfig().InReplay); AddConfigValueToggle(parent, getModConfig().TwelveToggle);