diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt index d563f56d3..a3a209238 100644 --- a/Main/CMakeLists.txt +++ b/Main/CMakeLists.txt @@ -27,7 +27,13 @@ source_group("Include\\FastGUI" FILES ${FAST_GUI_INC}) file(GLOB NUK_INC "${INCROOT}/nuklear/*.h") source_group("Include\\nuklear" FILES ${NUK_INC}) -set(MAIN_SRC ${SRC} ${INC} ${AUDIO_SRC} ${AUDIO_INC} ${FAST_GUI_SRC} ${FAST_GUI_INC} ${NUK_INC}) +file(GLOB NUK_INC "${INCROOT}/nuklear/*.h") +source_group("Include\\nuklear" FILES ${NUK_INC}) + +file(GLOB LIGHT_INC "${INCROOT}/LightPlugin/*.h") +source_group("Include\\LightPlugin" FILES ${LIGHT_INC}) + +set(MAIN_SRC ${SRC} ${INC} ${AUDIO_SRC} ${AUDIO_INC} ${FAST_GUI_SRC} ${FAST_GUI_INC} ${NUK_INC} ${LIGHT_INC}) file(GLOB RESOURCE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc) source_group("" FILES ${RESOURCE_SRC}) diff --git a/Main/include/Application.hpp b/Main/include/Application.hpp index 524f985b7..5cea84fac 100644 --- a/Main/include/Application.hpp +++ b/Main/include/Application.hpp @@ -10,11 +10,11 @@ extern struct GUIState g_guiState; extern class Graphics::Window* g_gameWindow; extern float g_aspectRatio; extern Vector2i g_resolution; -extern class Application* g_application; -extern class JobSheduler* g_jobSheduler; +extern class Application *g_application; +extern class JobSheduler *g_jobSheduler; extern class Input g_input; -extern class SkinConfig* g_skinConfig; -extern class TransitionScreen* g_transition; +extern class SkinConfig *g_skinConfig; +extern class TransitionScreen *g_transition; class SharedTexture; @@ -44,8 +44,8 @@ class Application class Game* LaunchReplay(const String& replayPath, MapDatabase** database = nullptr); void Shutdown(); - void AddTickable(class IApplicationTickable* tickable, class IApplicationTickable* insertBefore = nullptr); - void RemoveTickable(class IApplicationTickable* tickable, bool noDelete = false); + void AddTickable(class IApplicationTickable *tickable, class IApplicationTickable *insertBefore = nullptr); + void RemoveTickable(class IApplicationTickable *tickable, bool noDelete = false); // Current running map path (full file path) String GetCurrentMapPath(); @@ -54,11 +54,11 @@ class Application String GetCurrentSkin(); // Retrieves application command line parameters - const Vector& GetAppCommandLine() const; + const Vector &GetAppCommandLine() const; // Gets a basic template for a render state, with all the application variables initialized RenderState GetRenderStateBase() const; - RenderQueue* GetRenderQueueBase(); + RenderQueue *GetRenderQueueBase(); #ifdef LoadImage #undef LoadImage @@ -102,15 +102,18 @@ class Application void DiscordError(int errorCode, const char* message); void DiscordPresenceMenu(String name); void DiscordPresenceMulti(String secret, int partySize, int partyMax, String id); - void DiscordPresenceSong(const struct BeatmapSettings& song, int64 startTime, int64 endTime); + void DiscordPresenceSong(const struct BeatmapSettings &song, int64 startTime, int64 endTime); void JoinMultiFromInvite(String secret); - void SetUpdateAvailable(const String& version, const String& url, const String& download); + void SetUpdateAvailable(const String &version, const String &url, const String &download); void RunUpdater(); void CheckForUpdate(); void ForceRender(); void SetLuaBindings(struct lua_State* state); + Vector GetLightPluginList(); void RenderTickables(); struct NVGcontext* GetVGContext(); + void SetRgbLights(int left, int pos, Colori color); + void SetButtonLights(uint32 buttonbits); //if empty: no update avaiable //else: index 0 = url, index 1 = version @@ -138,6 +141,7 @@ class Application void m_OnFocusChanged(bool focused); void m_unpackSkins(); void m_loadResponsiveInputSetting(); + void m_InitLightPlugins(); RenderState m_renderStateBase; RenderQueue m_renderQueueBase; @@ -170,8 +174,10 @@ class Application String m_skin; bool m_needSkinReload = false; Timer m_jobTimer; + struct LightPlugin* m_activeLightPlugin = nullptr; //gauge colors, 0 = normal fail, 1 = normal clear, 2 = hard lower, 3 = hard upper Color m_gaugeColors[4] = { Colori(0, 204, 255), Colori(255, 102, 255), Colori(200, 50, 0), Colori(255, 100, 0) }; + Map m_lightPlugins; String m_multiRoomSecret; String m_multiRoomId; @@ -194,7 +200,7 @@ class JacketLoadingJob : public JobBase String imagePath; int w = 0, h = 0; bool web = false; - Application::CachedJacketImage* target; + Application::CachedJacketImage *target; }; void __discordJoinGame(const char* joins); @@ -206,4 +212,4 @@ class SharedTexture { bool Valid(); int nvgTexture = 0; Texture texture; -}; \ No newline at end of file +}; diff --git a/Main/include/GameConfig.hpp b/Main/include/GameConfig.hpp index 8aace8522..95eea9730 100644 --- a/Main/include/GameConfig.hpp +++ b/Main/include/GameConfig.hpp @@ -163,6 +163,8 @@ DefineEnum(GameConfigKeys, WASAPI_Exclusive, MuteUnfocused, PrerenderEffects, + UseLightPlugins, + LightPlugin, CheckForUpdates, OnlyRelease, diff --git a/Main/include/Input.hpp b/Main/include/Input.hpp index 5fa2204ce..bad08dbbf 100644 --- a/Main/include/Input.hpp +++ b/Main/include/Input.hpp @@ -39,6 +39,7 @@ class Input : Unique bool GetButton(Button button) const; float GetAbsoluteLaser(int laser) const; bool Are3BTsHeld() const; + uint32 GetButtonBits() const; // Controller state as a string // Primarily used for debugging diff --git a/Main/include/LightPlugin/LightPlugin.h b/Main/include/LightPlugin/LightPlugin.h new file mode 100644 index 000000000..767a8be66 --- /dev/null +++ b/Main/include/LightPlugin/LightPlugin.h @@ -0,0 +1,29 @@ +#ifdef __cplusplus +extern "C" { +#endif +#include + +typedef struct LightPlugin { + void* handle; + char* (*GetName)(); + void (*SetButtons)(uint32_t bitfield); + void (*Tick)(float deltaTime); + + //6 rgb sections, left/right + pos: (bottom/middle/top) = (0/1/2) + void (*SetLights)(uint8_t left, uint32_t pos, uint8_t r, uint8_t g, uint8_t b); + int (*Init)(void(*)(char*)); + int (*Close)(); +} LightPlugin; + +char* GetName(); +void SetButtons(uint32_t bitfield); +void SetLights(uint8_t left, uint32_t pos, uint8_t r, uint8_t g, uint8_t b); +void Tick(float deltaTime); + +//Return 0 on success +int Init(void(*)(char*)); +int Close(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/Main/src/Application.cpp b/Main/src/Application.cpp index b4f5f5d84..f96f3f86e 100644 --- a/Main/src/Application.cpp +++ b/Main/src/Application.cpp @@ -31,6 +31,7 @@ #endif #include "archive.h" #include "archive_entry.h" +#include "LightPlugin/LightPlugin.h" GameConfig g_gameConfig; SkinConfig *g_skinConfig; @@ -98,6 +99,12 @@ void Application::SetCommandLine(const char *cmdLine) // Split up command line parameters m_commandLine = Path::SplitCommandLine(cmdLine); } + +static void PluginLog(char* msg) +{ + Logf("[Light Plugin]: %s", Logger::Severity::Info, msg); +} + void Application::ApplySettings() { String newskin = g_gameConfig.GetString(GameConfigKeys::Skin); @@ -111,6 +118,25 @@ void Application::ApplySettings() m_loadResponsiveInputSetting(); m_UpdateWindowPosAndShape(); m_OnWindowResized(g_gameWindow->GetWindowSize()); + + //restart light plugin + if (m_activeLightPlugin) + { + m_activeLightPlugin->Close(); + m_activeLightPlugin = nullptr; + } + + String newPlugin = g_gameConfig.GetString(GameConfigKeys::LightPlugin); + if (m_lightPlugins.Contains(newPlugin)) + { + m_activeLightPlugin = &m_lightPlugins.at(newPlugin); + if (m_activeLightPlugin->Init(PluginLog) != 0) + { + Logf("Failed to initialize light plugin: \"%s\"", Logger::Severity::Warning, newPlugin); + m_activeLightPlugin = nullptr; + } + } + m_SaveConfig(); } int32 Application::Run() @@ -219,7 +245,21 @@ NVGcontext *Application::GetVGContext() return g_guiState.vg; } +void Application::SetRgbLights(int left, int pos, Colori color) +{ + if (m_activeLightPlugin) + { + m_activeLightPlugin->SetLights(left, pos, color.x, color.y, color.z); + } +} +void Application::SetButtonLights(uint32 buttonbits) +{ + if (m_activeLightPlugin) + { + m_activeLightPlugin->SetButtons(buttonbits); + } +} Vector Application::GetUpdateAvailable() { @@ -809,6 +849,70 @@ void Application::m_InitDiscord() Discord_Initialize(DISCORD_APPLICATION_ID, &dhe, 1, nullptr); } +void Application::m_InitLightPlugins() +{ + ProfilerScope $("Load Light Plugins"); + + String pluginpath = Path::Absolute("LightPlugins"); + +#if WIN32 + Vector plugins = Files::ScanFiles(pluginpath, "dll"); +#else + Vector plugins = Files::ScanFiles("LightPlugins", "so"); +#endif + + Logf("Found %d light plugins.", Logger::Severity::Info, plugins.size()); + + auto verifyPlugin = [this](const LightPlugin& lp) + { + return lp.Close != nullptr && + lp.GetName != nullptr && + lp.Init != nullptr && + lp.SetButtons != nullptr && + lp.SetLights != nullptr && + lp.Tick != nullptr; + }; + + for (auto& p : plugins) + { + SDL_ClearError(); + void* handle = SDL_LoadObject(*p.fullPath); + if (handle) + { + LightPlugin lp; + lp.Init = (int(*)(void(*)(char*)))SDL_LoadFunction(handle, "Init"); + lp.Close = (int(*)())SDL_LoadFunction(handle, "Close"); + lp.SetButtons = (void(*)(uint32))SDL_LoadFunction(handle, "SetButtons"); + lp.GetName = (char*(*)())SDL_LoadFunction(handle, "GetName"); + lp.SetLights = (void(*)(uint8, uint32, uint8, uint8, uint8))SDL_LoadFunction(handle, "SetLights"); + lp.Tick = (void(*)(float))SDL_LoadFunction(handle, "Tick"); + if (verifyPlugin(lp)) + m_lightPlugins.Add(lp.GetName(), lp); + else + { + Logf("Failed to verify light plugin \"%s\": %s", Logger::Severity::Warning, p.fullPath, SDL_GetError()); + SDL_UnloadObject(handle); + } + } + else { + Logf("Failed to load light plugin \"%s\": %s", Logger::Severity::Warning, p.fullPath, SDL_GetError()); + } + } + + + String newPlugin = g_gameConfig.GetString(GameConfigKeys::LightPlugin); + if (m_lightPlugins.Contains(newPlugin)) + { + m_activeLightPlugin = &m_lightPlugins.at(newPlugin); + int res = m_activeLightPlugin->Init(PluginLog); + if (res != 0) + { + Logf("Failed to initialize light plugin: \"%s\"", Logger::Severity::Warning, newPlugin); + m_activeLightPlugin = nullptr; + } + } +} + SDL_semaphore* renderSema = nullptr; //TODO: move to somewhere better std::atomic rendering; void threadedRenderer() { @@ -1062,6 +1166,11 @@ bool Application::m_Init() m_InitDiscord(); + if (g_gameConfig.GetBool(GameConfigKeys::UseLightPlugins)) + { + m_InitLightPlugins(); + } + CheckedLoad(m_fontMaterial = LoadMaterial("font")); m_fontMaterial->opaque = false; @@ -1228,7 +1337,6 @@ void Application::m_Tick() { tickable->Tick(m_deltaTime); } - // Not minimized / Valid resolution if (g_resolution.x > 0 && g_resolution.y > 0) { @@ -1259,6 +1367,13 @@ void Application::m_Tick() m_needSkinReload = false; ReloadSkin(); } + + // Tick light plugin + if (m_activeLightPlugin) + { + m_activeLightPlugin->Tick(m_deltaTime); + } + } // Checks and clears OpenGL errors @@ -1418,6 +1533,11 @@ void Application::m_Cleanup() delete g_transition; g_transition = nullptr; } + if (m_activeLightPlugin) + { + m_activeLightPlugin->Close(); + m_activeLightPlugin = nullptr; + } //if (m_skinHtpp) //{ @@ -2789,6 +2909,16 @@ void Application::SetLuaBindings(lua_State *state) m_skinHttp.PushFunctions(state); } +Vector Application::GetLightPluginList() +{ + Vector names; + names.Add(""); + for (auto& p : m_lightPlugins) + { + names.Add(p.first); + } + return names; +} bool JacketLoadingJob::Run() diff --git a/Main/src/Game.cpp b/Main/src/Game.cpp index b6e995a16..53814a09c 100755 --- a/Main/src/Game.cpp +++ b/Main/src/Game.cpp @@ -1543,6 +1543,44 @@ class Game_Impl : public Game gauge->SetValue(0.0f); FailCurrentRun(); } + + //lights + { + if (m_scoring.autoplayInfo.IsAutoplayButtons()) + { + int buttonBits = 0; + for (size_t i = 0; i < 6; i++) + { + buttonBits |= (m_scoring.autoplayInfo.buttonAnimationTimer[i] > 0 ? 1 : 0) << i; + } + g_application->SetButtonLights(buttonBits); + } + else + { + g_application->SetButtonLights(g_input.GetButtonBits() & 0b111111); + } + + float brightness = 1.0 - (m_playback.GetBeatTime() * 0.8); + brightness = Math::Clamp(brightness, 0.0f, 1.0f); + + + Color rgbColor = Color::FromHSV(180, 1.0, brightness); + for (size_t i = 0; i < 2; i++) + { + for (size_t j = 0; j < 3; j++) + { + if (j == 0) + { + const Color c(m_track->laserColors[1 - i] * brightness); + g_application->SetRgbLights(i, j, c.ToRGBA8()); + } + else + { + g_application->SetRgbLights(i, j, rgbColor.ToRGBA8()); + } + } + } + } } void BeginAfterGameTransition() diff --git a/Main/src/GameConfig.cpp b/Main/src/GameConfig.cpp index ff752a7fc..706f24186 100644 --- a/Main/src/GameConfig.cpp +++ b/Main/src/GameConfig.cpp @@ -234,6 +234,8 @@ void GameConfig::InitDefaults() Set(GameConfigKeys::CheckForUpdates, true); Set(GameConfigKeys::OnlyRelease, true); // deprecated Set(GameConfigKeys::LimitSettingsFont, false); + Set(GameConfigKeys::UseLightPlugins, false); + Set(GameConfigKeys::LightPlugin, ""); // Multiplayer Set(GameConfigKeys::MultiplayerHost, "usc-multi.drewol.me:39079"); diff --git a/Main/src/Input.cpp b/Main/src/Input.cpp index 8eac90546..6e75caa68 100644 --- a/Main/src/Input.cpp +++ b/Main/src/Input.cpp @@ -262,6 +262,16 @@ bool Input::Are3BTsHeld() const return (bta && btb && btc) || (bta && btb && btd) || (bta && btc && btd) || (btb && btc && btd); } +uint32 Input::GetButtonBits() const +{ + uint32 res = 0; + for (size_t i = 0; i < 7; i++) + { + res = res | ((m_buttonStates[i] ? 1 : 0) << i); + } + return res; +} + String Input::GetControllerStateString() const { if(m_gamepad) diff --git a/Main/src/ScoreScreen.cpp b/Main/src/ScoreScreen.cpp index bc2b371e4..def469470 100644 --- a/Main/src/ScoreScreen.cpp +++ b/Main/src/ScoreScreen.cpp @@ -28,7 +28,7 @@ class ScoreScreen_Impl : public ScoreScreen Graphics::Font m_specialFont; Sample m_applause; Texture m_categorizedHitTextures[4]; - lua_State* m_lua = nullptr; + lua_State *m_lua = nullptr; bool m_autoplay; bool m_autoButtons; bool m_startPressed; @@ -45,6 +45,7 @@ class ScoreScreen_Impl : public ScoreScreen uint32 m_timedHits[2]; int m_irState = IR::ResponseState::Unused; String m_chartHash; + float m_lightsTimer = 0.0f; //promote this to higher scope so i can use it in tick String m_replayPath; @@ -92,6 +93,7 @@ class ScoreScreen_Impl : public ScoreScreen uint32 m_gaugeOption; CollectionDialog m_collDiag; ChartIndex* m_chartIndex; + Color m_lightsColor; void m_PushStringToTable(const char* name, const String& data) { @@ -99,13 +101,13 @@ class ScoreScreen_Impl : public ScoreScreen lua_pushstring(m_lua, data.c_str()); lua_settable(m_lua, -3); } - void m_PushFloatToTable(const char* name, float data) + void m_PushFloatToTable(const char *name, float data) { lua_pushstring(m_lua, name); lua_pushnumber(m_lua, data); lua_settable(m_lua, -3); } - void m_PushIntToTable(const char* name, int data) + void m_PushIntToTable(const char *name, int data) { lua_pushstring(m_lua, name); lua_pushinteger(m_lua, data); @@ -434,8 +436,7 @@ class ScoreScreen_Impl : public ScoreScreen } public: - - void loadScoresFromGame(class Game* game) + void loadScoresFromGame(class Game *game) { Scoring& scoring = game->GetScoring(); Gauge* gauge = scoring.GetTopGauge(); @@ -503,7 +504,7 @@ class ScoreScreen_Impl : public ScoreScreen if (m_displayIndex >= (int)m_stats->size()) return; - const nlohmann::json& data= (*m_stats)[m_displayIndex]; + const nlohmann::json &data = (*m_stats)[m_displayIndex]; //TODO(gauge refactor): options are from flags, multi server needs update for the new options @@ -543,7 +544,7 @@ class ScoreScreen_Impl : public ScoreScreen m_meanHitDelta[0] = data["mean_delta"]; m_medianHitDelta[0] = data["median_delta"]; - m_playerName = static_cast(data.value("name","")); + m_playerName = static_cast(data.value("name", "")); auto samples = data["graph"]; @@ -555,7 +556,7 @@ class ScoreScreen_Impl : public ScoreScreen } m_numPlayersSeen = m_stats->size(); - m_displayId = static_cast((*m_stats)[m_displayIndex].value("uid","")); + m_displayId = static_cast((*m_stats)[m_displayIndex].value("uid", "")); } ScoreScreen_Impl(class Game* game, MultiplayerScreen* multiplayer, @@ -725,6 +726,29 @@ class ScoreScreen_Impl : public ScoreScreen memcpy(res.scorescreenInfo.gaugeSamples, m_gaugeSamples.data(), sizeof(res.scorescreenInfo.gaugeSamples)); } + ClearMark badge = Scoring::CalculateBadge(m_scoredata); + + switch (badge) + { + case ClearMark::Played: + m_lightsColor = Color::FromHSV(0, 1.0, 0.5); + break; + case ClearMark::NormalClear: + m_lightsColor = Color::FromHSV(180, 1.0, 0.8); + break; + case ClearMark::HardClear: + m_lightsColor = Color::FromHSV(10, 1.0, 0.8); + break; + case ClearMark::FullCombo: + m_lightsColor = Color::FromHSV(120, 1.0, 0.8); + break; + case ClearMark::Perfect: + m_lightsColor = Color::FromHSV(30, 1.0, 0.8); + break; + default: + m_lightsColor = Color::FromHSV(0, 0.0, 0.8); + break; + } } ~ScoreScreen_Impl() { @@ -845,7 +869,7 @@ class ScoreScreen_Impl : public ScoreScreen lua_pushstring(m_lua, "highScores"); lua_newtable(m_lua); int scoreIndex = 1; - for (auto& score : *m_stats) + for (auto &score : *m_stats) { lua_pushinteger(m_lua, scoreIndex++); lua_newtable(m_lua); @@ -869,7 +893,7 @@ class ScoreScreen_Impl : public ScoreScreen lua_pushstring(m_lua, "highScores"); lua_newtable(m_lua); int scoreIndex = 1; - for (auto& score : m_highScores) + for (auto &score : m_highScores) { lua_pushinteger(m_lua, scoreIndex++); lua_newtable(m_lua); @@ -1004,7 +1028,7 @@ class ScoreScreen_Impl : public ScoreScreen } virtual bool AsyncFinalize() override { - if(!loader.Finalize()) + if (!loader.Finalize()) return false; m_lua = g_application->LoadScript("result"); @@ -1105,6 +1129,20 @@ class ScoreScreen_Impl : public ScoreScreen m_showStats = g_input.GetButton(Input::Button::FX_0); + m_lightsTimer += deltaTime * 2; + m_lightsTimer = fmodf(m_lightsTimer, Math::pi * 2); + float lightBreathe = (sinf(m_lightsTimer) + 1.0) * 0.4 + 0.2; + Color tempCol(m_lightsColor * lightBreathe); + Colori rgbColor = tempCol.ToRGBA8(); + + for (size_t i = 0; i < 2; i++) + { + for (size_t j = 0; j < 3; j++) + { + g_application->SetRgbLights(i, j, rgbColor); + } + } + g_application->SetButtonLights(0); // Check for new scores if (m_multiplayer && m_numPlayersSeen != (int)m_stats->size()) { @@ -1195,8 +1233,7 @@ class ScoreScreen_Impl : public ScoreScreen void Capture() { - auto luaPopInt = [this] - { + auto luaPopInt = [this] { int a = lua_tonumber(m_lua, lua_gettop(m_lua)); lua_pop(m_lua, 1); return a; @@ -1223,7 +1260,8 @@ class ScoreScreen_Impl : public ScoreScreen { if (g_gameConfig.GetBool(GameConfigKeys::ForcePortrait)) { - x = g_gameWindow->GetWindowSize().x / 2 - g_resolution.x / 2;; + x = g_gameWindow->GetWindowSize().x / 2 - g_resolution.x / 2; + ; y = 0; w = g_resolution.x; h = g_resolution.y; @@ -1259,10 +1297,9 @@ class ScoreScreen_Impl : public ScoreScreen } lua_settop(m_lua, 0); } - }; -ScoreScreen* ScoreScreen::Create(class Game* game) +ScoreScreen *ScoreScreen::Create(class Game *game) { ScoreScreen_Impl* impl = new ScoreScreen_Impl(game, nullptr, "", nullptr, nullptr); return impl; diff --git a/Main/src/SettingsScreen.cpp b/Main/src/SettingsScreen.cpp index c13dffdd4..560f17677 100644 --- a/Main/src/SettingsScreen.cpp +++ b/Main/src/SettingsScreen.cpp @@ -695,6 +695,7 @@ class SettingsPage_System : public SettingsPage void Load() override { m_channels = { "release", "master", "develop" }; + m_lightPlugins = g_application->GetLightPluginList(); String channel = g_gameConfig.GetString(GameConfigKeys::UpdateChannel); if (!m_channels.Contains(channel)) @@ -713,6 +714,8 @@ class SettingsPage_System : public SettingsPage const Vector m_aaModes = { "Off", "2x MSAA", "4x MSAA", "8x MSAA", "16x MSAA" }; Vector m_channels; + Vector m_lightPlugins; + bool m_showWarning = false; protected: void RenderContents() override @@ -732,6 +735,28 @@ class SettingsPage_System : public SettingsPage #endif // _WIN32 ToggleSetting(GameConfigKeys::PrerenderEffects, "Pre-render song effects (experimental)"); + SectionHeader("Lights"); + const bool currentUseLight = g_gameConfig.GetBool(GameConfigKeys::UseLightPlugins); + const bool newUseLight = ToggleInput(currentUseLight, "Enable light plugins (may require restart):"); + + if (currentUseLight && !newUseLight) { + g_gameConfig.Set(GameConfigKeys::UseLightPlugins, false); + } + + if (m_lightPlugins.size() > 1) { + StringSelectionSetting(GameConfigKeys::LightPlugin, m_lightPlugins, "Light plugin:"); + } + m_showWarning = (!currentUseLight && newUseLight) || m_showWarning; + if (m_showWarning) { + nk_label(m_nctx, "Only install plugins from trusted sources as they", NK_TEXT_ALIGN_CENTERED); + nk_label(m_nctx, "can execute arbitrary code on your system.", NK_TEXT_ALIGN_CENTERED); + if (nk_button_label(m_nctx, "Ok")) { + g_gameConfig.Set(GameConfigKeys::UseLightPlugins, true); + m_showWarning = false; + } + } + + SectionHeader("Render"); SetApply(EnumSetting(GameConfigKeys::ResponsiveInputs, "Responsive Inputs (CPU intensive)")); SetApply(ToggleSetting(GameConfigKeys::Fullscreen, "Fullscreen")); diff --git a/Main/src/SongSelect.cpp b/Main/src/SongSelect.cpp index 24517a604..a7133ec30 100644 --- a/Main/src/SongSelect.cpp +++ b/Main/src/SongSelect.cpp @@ -958,9 +958,10 @@ class SongSelect_Impl : public SongSelect bool m_hasRestored = false; Map m_timeSinceButtonPressed; Map m_timeSinceButtonReleased; - lua_State* m_lua = nullptr; + lua_State *m_lua = nullptr; + float m_lightTimer = 0.0f; - MultiplayerScreen* m_multiplayer = nullptr; + MultiplayerScreen *m_multiplayer = nullptr; CollectionDialog m_collDiag; GameplaySettingsDialog m_settDiag; @@ -1646,6 +1647,21 @@ class SongSelect_Impl : public SongSelect m_previewPlayer.Update(deltaTime); m_searchInput->Tick(); m_selectionWheel->SetSearchFieldLua(m_searchInput); + + //tick light + m_lightTimer += deltaTime; + m_lightTimer = fmodf(m_lightTimer, 2); + for (size_t i = 0; i < 2; i++) + { + for (size_t j = 0; j < 3; j++) + { + g_application->SetRgbLights(i, j, Colori::Black); + } + } + if (m_lightTimer >= 1) + g_application->SetButtonLights(1 << 6); + else + g_application->SetButtonLights(0); if (m_collDiag.IsActive()) { m_collDiag.Tick(deltaTime); diff --git a/Main/src/TitleScreen.cpp b/Main/src/TitleScreen.cpp index 09b8444a0..72345be3a 100644 --- a/Main/src/TitleScreen.cpp +++ b/Main/src/TitleScreen.cpp @@ -25,13 +25,14 @@ class TitleScreen_Impl : public TitleScreen lua_State* m_lua = nullptr; LuaBindable* m_luaBinds = nullptr; MapDatabase* m_mapDatabase = nullptr; + float m_lightTimer = 0.0f; void Exit() { g_application->Shutdown(); } - int lExit(lua_State* L) + int lExit(lua_State *L) { Exit(); return 0; @@ -48,19 +49,19 @@ class TitleScreen_Impl : public TitleScreen g_transition->TransitionTo(SongSelect::Create()); } - int lStart(lua_State* L) + int lStart(lua_State *L) { Start(); return 0; } - int lDownloads(lua_State* L) + int lDownloads(lua_State *L) { g_application->AddTickable(new DownloadScreen()); return 0; } - int lMultiplayer(lua_State* L) + int lMultiplayer(lua_State *L) { g_transition->TransitionTo(new MultiplayerScreen()); return 0; @@ -83,7 +84,7 @@ class TitleScreen_Impl : public TitleScreen g_application->AddTickable(SettingsScreen::Create()); } - int lSettings(lua_State* L) + int lSettings(lua_State *L) { Settings(); return 0; @@ -171,7 +172,7 @@ class TitleScreen_Impl : public TitleScreen g_gameWindow->OnFileDropped.Add(this, &TitleScreen_Impl::m_OnFileDropped); return true; } - + TitleScreen_Impl() { g_gameWindow->OnMousePressed.RemoveAll(this); @@ -223,11 +224,28 @@ class TitleScreen_Impl : public TitleScreen g_application->DiscordPresenceMenu("Title Screen"); } + virtual void Tick(float deltaTime) + { + if (IsSuspended()) + return; + m_lightTimer += deltaTime; + Color c = Color::FromHSV(fmodf(m_lightTimer * 180, 360), 1.0, 0.1); + + for (size_t i = 0; i < 2; i++) + { + for (size_t j = 0; j < 3; j++) + { + g_application->SetRgbLights(i,j, c.ToRGBA8()); + } + } + uint32 button = 1 << (int)(m_lightTimer * 10) % 6; + g_application->SetButtonLights(button); + } }; -TitleScreen* TitleScreen::Create() +TitleScreen *TitleScreen::Create() { - TitleScreen_Impl* impl = new TitleScreen_Impl(); + TitleScreen_Impl *impl = new TitleScreen_Impl(); return impl; } \ No newline at end of file diff --git a/bin/LightPlugins/usc-hid.dll b/bin/LightPlugins/usc-hid.dll new file mode 100644 index 000000000..31be635c5 Binary files /dev/null and b/bin/LightPlugins/usc-hid.dll differ