From 86948f2a1fa1b5a815122ad24a4359e23b6a1c4e Mon Sep 17 00:00:00 2001 From: cake Date: Thu, 1 Oct 2026 17:02:57 -0400 Subject: [PATCH 01/15] autoplayer spin patch --- scripts/game/managers/CursorManager.cs | 38 ++++++++++++++++--------- scripts/scenes/Game.cs | 2 +- scripts/shaders/combo_progress.gdshader | 2 +- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/scripts/game/managers/CursorManager.cs b/scripts/game/managers/CursorManager.cs index be41756c..2420df1c 100644 --- a/scripts/game/managers/CursorManager.cs +++ b/scripts/game/managers/CursorManager.cs @@ -125,27 +125,39 @@ public void UpdateCursor(Vector2 inputDelta, int cursorIndex = 0) public void UpdateAutoplayCursor(Vector2 position) { + if (!position.IsFinite()) + return; + EmitSignalOnCursorUpdated(position); var attempt = runner.Attempt; - attempt.RawCursorPosition = position; - attempt.CursorPosition = position.Clamp(-Constants.BOUNDS, Constants.BOUNDS); - - var origin = new Vector3(0, 0, attempt.CameraMode.Name == "Spin" ? 4f : 3.75f); - float parallax = (float)settings.CameraParallax; - camera.Position = origin + new Vector3(attempt.CursorPosition.X, attempt.CursorPosition.Y, 0) * parallax; + position = position.Clamp(-Constants.BOUNDS, Constants.BOUNDS); - if (attempt.CameraMode.Name != "Spin") + if (attempt.CameraMode is CameraSpin) { - camera.Rotation = Vector3.Zero; - } + Vector3 target = new(position.X, position.Y, 0); + Vector3 previousCursor = new(attempt.CursorPosition.X, attempt.CursorPosition.Y, 0); + var origin = new Vector3(0, 0, 3.5f) + previousCursor * (float)attempt.Settings.CameraParallax; - Vector3 cursorPos = new(attempt.CursorPosition.X, attempt.CursorPosition.Y, 0); + var targetRotation = Basis.LookingAt(target - origin, Vector3.Up); + var targetEuler = targetRotation.GetEuler(); + float pitch = targetEuler.X - camera.Rotation.X; + float yaw = Mathf.AngleDifference(camera.Rotation.Y, targetEuler.Y); - if (cursorPos.IsFinite()) - { - cursors[0].Position = cursorPos; + var mouseDelta = new Vector2(-yaw, -pitch) * (120f * Mathf.Pi); + + attempt.CameraMode.Process(attempt, replayManager, camera, cursors[0], mouseDelta, 1f); + return; } + + attempt.RawCursorPosition = position; + attempt.CursorPosition = position; + + Vector3 cursorPosition = new(position.X, position.Y, 0); + camera.Position = new Vector3(0, 0, 3.76f) + cursorPosition * (float)settings.CameraParallax; + camera.Rotation = Vector3.Zero; + + cursors[0].Position = cursorPosition; } // Reset everything to zero so it doesn't have infinite sensitivity diff --git a/scripts/scenes/Game.cs b/scripts/scenes/Game.cs index c8765e0b..4a924949 100644 --- a/scripts/scenes/Game.cs +++ b/scripts/scenes/Game.cs @@ -46,7 +46,7 @@ public override void _Ready() PlayerInputController.OnMouseMove += (relative, absolute) => { - if (!Runner.Playing || Attempt.IsReplay) + if (!Runner.Playing || Attempt.IsReplay || Attempt.Modifiers.Exists(mod => mod is AutoplayModifier)) return; if (Attempt.Settings.AbsoluteInput) diff --git a/scripts/shaders/combo_progress.gdshader b/scripts/shaders/combo_progress.gdshader index 1d019de7..69d172a2 100644 --- a/scripts/shaders/combo_progress.gdshader +++ b/scripts/shaders/combo_progress.gdshader @@ -31,7 +31,7 @@ void fragment() { float dist_to_line = distance(UV, middle + dotp * normalize(point_diff)); float line_alpha = smoothstep((thickness / 256.0) + fwidth(dist_to_line), (thickness / 256.0) - fwidth(dist_to_line), dist_to_line); - + if (abs(dotp) <= distance(point_diff, vec2(0.0, 0.0)) / 2.0 + thickness) { COLOR = mix(COLOR, vec4(background_colour.rgb, 1.0), line_alpha); } From ab4ab844bccf8736d667ca7143b468fa30a1d3a4 Mon Sep 17 00:00:00 2001 From: noobour Date: Sun, 4 Oct 2026 19:03:20 -0400 Subject: [PATCH 02/15] fix: ap setting scores --- scripts/game/Runner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index 58eb0a8f..25ac427c 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -484,7 +484,7 @@ public void Stop(bool results = true) Stats.Instance.GamePlaytime += (Time.GetTicksUsec() - Attempt.TimeStarted) / 1000000; Stats.Instance.TotalDistance += (ulong)Attempt.DistanceMM; - if (Attempt.StartFrom == 0) + if (Attempt.StartFrom == 0 && !autoplayEnabled) { if (!File.Exists($"{Constants.USER_FOLDER}/pbs/{Attempt.Map.Name}")) { From 9200e16441c94f0cffca8ee10beb4e889235c56a Mon Sep 17 00:00:00 2001 From: noobour Date: Sun, 4 Oct 2026 19:39:54 -0400 Subject: [PATCH 03/15] fix: renderer fallback (thx porgyfish) --- scripts/game/Runner.cs | 2 +- user/skins/default/modifiers/autoplay.png.import | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index 25ac427c..7eef5960 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -63,7 +63,7 @@ public override void _Ready() HudManager ??= GetNode("HUD"); Camera ??= GetNode("Camera3D"); - Renderers ??= GetNode>("Renderers"); + Renderers ??= new Godot.Collections.Array(GetNode("Renderers").GetChildren().OfType()); Grid ??= HudManager.GetNode("Grid"); Cursor ??= GetNode("Cursor"); // VideoStreamPlayer ??= GetNode("Video/VideoViewport/VideoStreamPlayer"); diff --git a/user/skins/default/modifiers/autoplay.png.import b/user/skins/default/modifiers/autoplay.png.import index dfbfb9ec..f35b35ff 100644 --- a/user/skins/default/modifiers/autoplay.png.import +++ b/user/skins/default/modifiers/autoplay.png.import @@ -3,7 +3,7 @@ importer="texture" type="CompressedTexture2D" uid="uid://pkytlqp7mt2g" -path="res://.godot/imported/autoplay.png-68ce854bc9795d220ef473f6447c55d9.ctex" +path="res://.godot/imported/autoplay.png-5857cdd45d7c7f2dbdd56b2256a08eff.ctex" metadata={ "vram_texture": false } @@ -11,7 +11,7 @@ metadata={ [deps] source_file="res://user/skins/default/modifiers/autoplay.png" -dest_files=["res://.godot/imported/autoplay.png-68ce854bc9795d220ef473f6447c55d9.ctex"] +dest_files=["res://.godot/imported/autoplay.png-5857cdd45d7c7f2dbdd56b2256a08eff.ctex"] [params] From 9caf4411087061c143271c0e4eae5097f5207371 Mon Sep 17 00:00:00 2001 From: noobour Date: Wed, 14 Oct 2026 15:19:23 -0400 Subject: [PATCH 04/15] feat: auto restart on death + settings cleanup --- scripts/database/settings/SettingsProfile.cs | 260 +++++++++++-------- scripts/database/settings/SettingsSection.cs | 5 +- scripts/game/Runner.cs | 16 +- scripts/game/ui/Skip.cs | 7 +- 4 files changed, 174 insertions(+), 114 deletions(-) diff --git a/scripts/database/settings/SettingsProfile.cs b/scripts/database/settings/SettingsProfile.cs index d4a5eeb0..f20d31a2 100644 --- a/scripts/database/settings/SettingsProfile.cs +++ b/scripts/database/settings/SettingsProfile.cs @@ -8,6 +8,11 @@ public partial class SettingsProfile { #region Gameplay + /// + /// Selected skin for the game + /// + [Order] + public SettingsItem Skin { get; private set; } /// /// Adjusts cursor sensitivity @@ -93,15 +98,15 @@ public partial class SettingsProfile [Order] public SettingsItem FoV { get; private set; } - #endregion - - #region Visual - /// - /// Selected skin for the game + /// Auto restarts on death /// [Order] - public SettingsItem Skin { get; private set; } + public SettingsItem AutoRestartOnDeath { get; private set; } + + #endregion + + #region Space /// /// Overrides the skin's background space for the menu @@ -127,6 +132,16 @@ public partial class SettingsProfile [Order] public SettingsItem SpaceEffects { get; private set; } + /// + /// Enables a dark radial fade to help visibility + /// + [Order] + public SettingsItem VisibilityAssist { get; private set; } + + #endregion + + #region Notes + /// /// Overrides the skin's colorset /// @@ -134,28 +149,32 @@ public partial class SettingsProfile public SettingsItem NoteColors { get; private set; } /// - /// Sets the maximum opacity of the notes + /// Overrides the skin's note mesh /// [Order] - public SettingsItem NoteOpacity { get; private set; } + public SettingsItem NoteMesh { get; private set; } /// - /// Adjusts the note opacity curve, a higher value will make any sort of transparency appear more quickly + /// Sets the size of the notes /// [Order] - public SettingsItem NoteOpacityExponent { get; private set; } + public SettingsItem NoteSize { get; private set; } /// - /// Overrides the skin's note mesh + /// Sets the maximum opacity of the notes /// [Order] - public SettingsItem NoteMesh { get; private set; } + public SettingsItem NoteOpacity { get; private set; } /// - /// Sets the size of the notes + /// Adjusts the note opacity curve, a higher value will make any sort of transparency appear more quickly /// [Order] - public SettingsItem NoteSize { get; private set; } + public SettingsItem NoteOpacityExponent { get; private set; } + + #endregion + + #region Cursor /// /// Adjusts the cursor scale @@ -199,6 +218,9 @@ public partial class SettingsProfile [Order] public SettingsItem UseCursorInMenus { get; private set; } + #endregion + + #region HUD /// /// Adjusts the easing when the HP Bar is updated /// @@ -241,12 +263,6 @@ public partial class SettingsProfile [Order] public SettingsItem AltComboCounter { get; private set; } - /// - /// Enables a dark radial fade to help visibility - /// - [Order] - public SettingsItem VisibilityAssist { get; private set; } - /// /// Toggles a popup on a hit /// @@ -448,6 +464,34 @@ public SettingsProfile() { #region Gameplay + Skin = new("default") + { + Id = "Skin", + Title = "Skin", + Description = "Selected skin for the game", + Section = SettingsSection.Gameplay, + UpdateAction = (_, init) => + { + if (!init) + { + SkinManager.Load(); + } + }, + Buttons = + [ + new() + { + Title = "Skin Folder", + Description = "Open the skin folder", + OnPressed = () => + { + OS.ShellOpen($"{Constants.USER_FOLDER}/skins/{SettingsManager.Instance.Settings.Skin}"); + }, + }, + ], + List = new("default") { Values = ["default"] }, + }; + Sensitivity = new(0.5f) { Id = "Sensitivity", @@ -618,44 +662,24 @@ public SettingsProfile() }, }; - #endregion - - #region Visual - - Skin = new("default") + AutoRestartOnDeath = new(false) { - Id = "Skin", - Title = "Skin", - Description = "Selected skin for the game", - Section = SettingsSection.Visual, - UpdateAction = (_, init) => - { - if (!init) - { - SkinManager.Load(); - } - }, - Buttons = - [ - new() - { - Title = "Skin Folder", - Description = "Open the skin folder", - OnPressed = () => - { - OS.ShellOpen($"{Constants.USER_FOLDER}/skins/{SettingsManager.Instance.Settings.Skin}"); - }, - }, - ], - List = new("default") { Values = ["default"] }, + Id = "AutoRestartOnDeath", + Title = "Auto Restart On Death", + Description = "On death, automatically restart the run instead of going to the results screen", + Section = SettingsSection.Gameplay, }; + #endregion + + #region Spaces + MenuSpace = new("skin") { Id = "MenuSpace", Title = "Menu Space", Description = "Overrides the skin's background space for the menu", - Section = SettingsSection.Visual, + Section = SettingsSection.Spaces, UpdateAction = (_, init) => { if (!init) @@ -689,7 +713,7 @@ public SettingsProfile() Id = "GameSpace", Title = "Game Space", Description = "Overrides the skin's background space for gameplay", - Section = SettingsSection.Visual, + Section = SettingsSection.Spaces, UpdateAction = (_, init) => { if (!init) @@ -723,7 +747,7 @@ public SettingsProfile() Id = "SpaceHitEffects", Title = "Space Hit Effects", Description = "Toggles note hit effects for the game space", - Section = SettingsSection.Visual, + Section = SettingsSection.Spaces, }; SpaceEffects = new(true) @@ -731,15 +755,27 @@ public SettingsProfile() Id = "SpaceEffects", Title = "Space Effects", Description = "Toggles non-hit effects for the game space", - Section = SettingsSection.Visual, + Section = SettingsSection.Spaces, }; + VisibilityAssist = new(false) + { + Id = "VisibilityAssist", + Title = "Visibility Assist", + Description = "Enables a dark radial fade to help with visibility (impacts performance significantly)", + Section = SettingsSection.Spaces, + }; + + #endregion + + #region Notes + NoteColors = new("skin") { Id = "Colors", Title = "Colors", Description = "Overrides the skin's colorset", - Section = SettingsSection.Visual, + Section = SettingsSection.Notes, UpdateAction = (_, init) => { if (!init) @@ -750,40 +786,12 @@ public SettingsProfile() List = new("skin") { Values = ["skin", "default"] }, }; - NoteOpacity = new(1) - { - Id = "NoteOpacity", - Title = "Note Opacity", - Description = "Sets the maximum opacity for the notes", - Section = SettingsSection.Visual, - Slider = new() - { - Step = 0.05f, - MinValue = 0, - MaxValue = 1, - }, - }; - - NoteOpacityExponent = new(1.25) - { - Id = "NoteOpacityExponent", - Title = "Note Opacity Exponent", - Description = "Adjusts the note opacity curve, a higher value will make any sort of transparency appear more quickly", - Section = SettingsSection.Visual, - Slider = new() - { - Step = 0.05f, - MinValue = 1, - MaxValue = 2, - }, - }; - NoteMesh = new("skin") { Id = "NoteMesh", Title = "Note Mesh", Description = "Overrides the skin's note mesh", - Section = SettingsSection.Visual, + Section = SettingsSection.Notes, UpdateAction = (_, init) => { if (!init) @@ -799,7 +807,7 @@ public SettingsProfile() Id = "NoteSize", Title = "Note Size", Description = "Sets the size of the notes, does not change hitboxes", - Section = SettingsSection.Visual, + Section = SettingsSection.Notes, Slider = new() { Step = 0.025f, @@ -808,20 +816,44 @@ public SettingsProfile() }, }; - GridGuides = new(true) + NoteOpacity = new(1) { - Id = "GridGuides", - Title = "Grid Guides", - Description = "Enables grid guides", - Section = SettingsSection.Visual, + Id = "NoteOpacity", + Title = "Note Opacity", + Description = "Sets the maximum opacity for the notes", + Section = SettingsSection.Notes, + Slider = new() + { + Step = 0.05f, + MinValue = 0, + MaxValue = 1, + }, + }; + + NoteOpacityExponent = new(1.25) + { + Id = "NoteOpacityExponent", + Title = "Note Opacity Exponent", + Description = "Adjusts the note opacity curve, a higher value will make any sort of transparency appear more quickly", + Section = SettingsSection.Notes, + Slider = new() + { + Step = 0.05f, + MinValue = 1, + MaxValue = 2, + }, }; + #endregion + + #region Cursor + CursorScale = new(1) { Id = "CursorScale", Title = "Cursor Scale", Description = "Adjusts the cursor scale, does not change hitboxes", - Section = SettingsSection.Visual, + Section = SettingsSection.Cursor, Slider = new() { Step = 0.025f, @@ -835,7 +867,7 @@ public SettingsProfile() Id = "CursorOpacity", Title = "Cursor Opacity", Description = "Adjusts the cursor opacity", - Section = SettingsSection.Visual, + Section = SettingsSection.Cursor, Slider = new() { Step = 0.05f, @@ -849,7 +881,7 @@ public SettingsProfile() Id = "CursorRotation", Title = "Cursor Rotation", Description = "Degrees to rotate the cursor by every second", - Section = SettingsSection.Visual, + Section = SettingsSection.Cursor, Slider = new() { Step = 1, @@ -863,7 +895,7 @@ public SettingsProfile() Id = "CursorTrail", Title = "Cursor Trail", Description = "Toggles a trail for your cursor", - Section = SettingsSection.Visual, + Section = SettingsSection.Cursor, }; TrailTime = new(0.05f) @@ -871,7 +903,7 @@ public SettingsProfile() Id = "TrailTime", Title = "Cursor Trail Time", Description = "Adjusts trail visibility time", - Section = SettingsSection.Visual, + Section = SettingsSection.Cursor, Slider = new() { Step = 0.01f, @@ -885,7 +917,7 @@ public SettingsProfile() Id = "TrailDetail", Title = "Cursor Trail Detail", Description = "Adjusts the detail for the trail, a high value may impact performance", - Section = SettingsSection.Visual, + Section = SettingsSection.Cursor, Slider = new() { Step = 1f, @@ -899,7 +931,19 @@ public SettingsProfile() Id = "UseCursorInMenus", Title = "Use Cursor in Menus", Description = "Uses the skin's cursor instead of the native cursor", - Section = SettingsSection.Visual, + Section = SettingsSection.Cursor, + }; + + #endregion + + #region HUD + + GridGuides = new(true) + { + Id = "GridGuides", + Title = "Grid Guides", + Description = "Enables grid guides", + Section = SettingsSection.HUD, }; HPLerpValue = new(60f) @@ -907,7 +951,7 @@ public SettingsProfile() Id = "HPLerpValue", Title = "HP Bar Easing Value", Description = "Adjusts how smooth, or harsh, the HP Bar eases in while it updates", - Section = SettingsSection.Visual, + Section = SettingsSection.HUD, Slider = new() { Step = 1f, @@ -953,7 +997,7 @@ public SettingsProfile() Id = "SimpleHUD", Title = "Simple HUD", Description = "Hides the regular left and right panels, and instead displays a simple miss counter on the right", - Section = SettingsSection.Visual, + Section = SettingsSection.HUD, }; SuperSimpleHUD = new(false) @@ -961,7 +1005,7 @@ public SettingsProfile() Id = "SuperSimpleHUD", Title = "Super Simple HUD", Description = "Hides health bar, song duration, and song name", - Section = SettingsSection.Visual, + Section = SettingsSection.HUD, }; AltComboCounter = new(false) @@ -969,15 +1013,7 @@ public SettingsProfile() Id = "AltComboCounter", Title = "Alt. Combo Counter", Description = "Moves the Combo Counter to the HUD", - Section = SettingsSection.Visual, - }; - - VisibilityAssist = new(false) - { - Id = "VisibilityAssist", - Title = "Visibility Assist", - Description = "Enables a dark radial fade to help with visibility (impacts performance significantly)", - Section = SettingsSection.Visual, + Section = SettingsSection.HUD, }; HitPopups = new(true) @@ -985,7 +1021,7 @@ public SettingsProfile() Id = "HitPopups", Title = "Hit Score Popups", Description = "Toggles a popup on a hit", - Section = SettingsSection.Visual, + Section = SettingsSection.HUD, }; MissPopups = new(true) @@ -993,9 +1029,13 @@ public SettingsProfile() Id = "MissPopups", Title = "Miss Popups", Description = "Toggles a popup on a miss", - Section = SettingsSection.Visual, + Section = SettingsSection.HUD, }; + #endregion + + #region Video + Fullscreen = new(true) { Id = "Fullscreen", diff --git a/scripts/database/settings/SettingsSection.cs b/scripts/database/settings/SettingsSection.cs index 58d4f2d8..2e2bbf0a 100644 --- a/scripts/database/settings/SettingsSection.cs +++ b/scripts/database/settings/SettingsSection.cs @@ -2,7 +2,10 @@ public enum SettingsSection { None, Gameplay, - Visual, + Spaces, + Notes, + Cursor, + HUD, Audio, Video, Other, diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index 7eef5960..9f964a24 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -612,10 +612,22 @@ private void onHitResultChanged(int noteIndex, HitResult hitResult) if (!Attempt.IsReplay && Attempt.Health <= 0 && Attempt.Alive) { - Fail(); + if (Attempt.Settings.AutoRestartOnDeath && checkFail(hit, Attempt.Health)) + { + SoundManager.FailSound.Play(); + + Attempt.Alive = false; + Attempt.Qualifies = false; + + Results.Replay(); + } + else + { + Fail(); + } } - if (checkFail(hit, Attempt.Health)) + if (!Attempt.Settings.AutoRestartOnDeath && checkFail(hit, Attempt.Health)) { QueueStop(); } diff --git a/scripts/game/ui/Skip.cs b/scripts/game/ui/Skip.cs index 21252179..7edaa0e0 100644 --- a/scripts/game/ui/Skip.cs +++ b/scripts/game/ui/Skip.cs @@ -32,7 +32,7 @@ public override void Process(double delta, Attempt attempt) public void OnSkipAvailable(Attempt attempt) { - if (Runner.ObjectIndicesStart[typeof(Note)] >= attempt.Map.Notes.Length) + if (Runner.ObjectIndicesStart[typeof(Note)] >= attempt.Map.Notes.Length && !Runner.Attempt.IsReplay) { label.Text = "Press Space to complete"; } @@ -40,6 +40,11 @@ public void OnSkipAvailable(Attempt attempt) if (tween != null) return; + if (Runner.Attempt.IsReplay) + { + label.Text = "Seek forward to skip"; + } + tween = CreateTween().SetLoops().SetTrans(Tween.TransitionType.Sine); tween.TweenProperty(label, "modulate", new Color(label.Modulate, 0.25f), 0.75f); tween.TweenProperty(label, "modulate", new Color(label.Modulate, 0.75f), 0.75f); From 9edf893da9cddbbe940f82e1cb03836f9057c50c Mon Sep 17 00:00:00 2001 From: lulusuki Date: Sun, 18 Oct 2026 20:02:36 +0800 Subject: [PATCH 05/15] remove user from contribution list --- scenes/main_menu.tscn | 9 --------- 1 file changed, 9 deletions(-) diff --git a/scenes/main_menu.tscn b/scenes/main_menu.tscn index cf30d5cd..def018c2 100644 --- a/scenes/main_menu.tscn +++ b/scenes/main_menu.tscn @@ -1727,15 +1727,6 @@ text = "[font_size=20]nyarw" fit_content = true horizontal_alignment = 1 -[node name="lulusuki" type="RichTextLabel" parent="Menus/Extras/Contributors/ScrollContainer/VBoxContainer" unique_id=1649530311] -modulate = Color(1, 0.7411765, 1, 1) -layout_mode = 2 -mouse_filter = 1 -bbcode_enabled = true -text = "[font_size=20]lulusuki" -fit_content = true -horizontal_alignment = 1 - [node name="noobour" type="RichTextLabel" parent="Menus/Extras/Contributors/ScrollContainer/VBoxContainer" unique_id=1684835200] modulate = Color(0.05882353, 0.5058824, 0.7176471, 1) layout_mode = 2 From d0d40d37fd4beb5079e4806c1067de56cd05212f Mon Sep 17 00:00:00 2001 From: nyarw Date: Tue, 20 Oct 2026 12:09:11 +0200 Subject: [PATCH 06/15] chore: match namespaces to folder structure and enforce IDE0130 * chore: match namespaces to folder structure and enforce IDE0130 * fix: add new keyword to satisfy EnforceCodeStyleInBuild --- .editorconfig | 2 +- Rhythia.csproj | 3 +++ scripts/SoundManager.cs | 12 ++++++------ scripts/game/Runner.cs | 4 ++-- scripts/game/managers/HudManager.cs | 2 +- scripts/game/managers/ReplayManager.cs | 4 ++-- scripts/game/ui/PanelLeft.cs | 2 +- scripts/game/ui/PanelRight.cs | 2 +- scripts/game/ui/PlaytestOverlay.cs | 8 ++++---- scripts/game/ui/Progress.cs | 2 +- scripts/map/Map.cs | 2 +- scripts/map/MapCache.cs | 2 +- scripts/map/MapManager.cs | 6 +++--- scripts/map/MapParser.cs | 2 +- scripts/scenes/MainMenu.cs | 2 +- scripts/scenes/Results.cs | 8 ++++---- scripts/skinning/SkinManager.cs | 4 ++-- scripts/spaces/CircularTunnel.cs | 2 +- scripts/spaces/Conspiracy.cs | 2 +- scripts/spaces/Galaxy.cs | 2 +- scripts/spaces/Grid.cs | 2 +- scripts/spaces/Relic.cs | 2 +- scripts/spaces/Solid.cs | 2 +- scripts/spaces/Squircles.cs | 2 +- scripts/spaces/TriTunnel.cs | 2 +- scripts/spaces/Tunnel.cs | 2 +- scripts/spaces/Void.cs | 2 +- scripts/spaces/Vortex.cs | 2 +- scripts/spaces/Waves.cs | 2 +- scripts/ui/FlatPreview.cs | 4 ++-- scripts/ui/OptionPopup.cs | 4 ++-- scripts/ui/menu/play/MapButtonSquare.cs | 2 +- scripts/ui/menu/play/MapButtonWide.cs | 6 +++--- scripts/ui/menu/play/MapInfoContainer.cs | 12 ++++++------ scripts/ui/menu/play/Modifiers.cs | 2 +- scripts/ui/menu/play/ScorePanel.cs | 8 ++++---- scripts/util/Audio.cs | 2 +- scripts/util/Misc.cs | 2 +- scripts/util/Networking.cs | 2 +- scripts/util/String.cs | 2 +- 40 files changed, 70 insertions(+), 67 deletions(-) diff --git a/.editorconfig b/.editorconfig index 61eee91c..1769fc9c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -183,7 +183,7 @@ csharp_style_namespace_declarations = file_scoped:warning csharp_style_prefer_primary_constructors = false #Roslyn severities -dotnet_diagnostic.IDE0130.severity = none +dotnet_diagnostic.IDE0130.severity = warning dotnet_diagnostic.CA1305.severity = warning [*.{yaml,yml}] diff --git a/Rhythia.csproj b/Rhythia.csproj index 6905bd64..841b6556 100644 --- a/Rhythia.csproj +++ b/Rhythia.csproj @@ -4,6 +4,9 @@ net10.0 true preview + $(MSBuildProjectDirectory)\scripts\ + + true diff --git a/scripts/SoundManager.cs b/scripts/SoundManager.cs index 0648e8f6..9476a370 100644 --- a/scripts/SoundManager.cs +++ b/scripts/SoundManager.cs @@ -261,7 +261,7 @@ public static void PlayJukebox(Map map, bool setRichPresence = true) JukeboxIndex = MapManager.Maps.FindIndex(x => x.Id == map.Id); - Song.Stream = Util.Audio.LoadFromFile($"{MapUtil.MapsFolder}/{map.Name}/audio.{map.AudioExt}"); + Song.Stream = util.Audio.LoadFromFile($"{MapUtil.MapsFolder}/{map.Name}/audio.{map.AudioExt}"); Song.Play(); Instance.JukeboxPlayed?.Invoke(map); @@ -320,7 +320,7 @@ public static void StartMapSelectionPlayback(Map map, bool setRichPresence = tru } } - Song.Stream = Util.Audio.LoadFromFile($"{MapUtil.MapsFolder}/{map.Name}/audio.{map.AudioExt}"); + Song.Stream = util.Audio.LoadFromFile($"{MapUtil.MapsFolder}/{map.Name}/audio.{map.AudioExt}"); Song.Play(0); Instance.JukeboxPlayed?.Invoke(map); @@ -469,10 +469,10 @@ public static void UpdateJukeboxQueue() public void UpdateSkin(SkinProfile skin) { - HitSound.Stream = Util.Audio.LoadStream(skin.HitSoundBuffer); - MissSound.Stream = Util.Audio.LoadStream(skin.MissSoundBuffer); - FailSound.Stream = Util.Audio.LoadStream(skin.FailSoundBuffer); - MenuMusic.Stream = Util.Audio.LoadStream(skin.MenuMusicBuffer); + HitSound.Stream = util.Audio.LoadStream(skin.HitSoundBuffer); + MissSound.Stream = util.Audio.LoadStream(skin.MissSoundBuffer); + FailSound.Stream = util.Audio.LoadStream(skin.FailSoundBuffer); + MenuMusic.Stream = util.Audio.LoadStream(skin.MenuMusicBuffer); RefreshMenuMusicPlayback(); } diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index 9f964a24..f5c9c8fc 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -349,7 +349,7 @@ public void Play() if (Attempt.Map.AudioBuffer != null) { - SoundManager.Song.Stream = Util.Audio.LoadStream(Attempt.Map.AudioBuffer); + SoundManager.Song.Stream = util.Audio.LoadStream(Attempt.Map.AudioBuffer); SoundManager.Song.PitchScale = (float)Speed; } @@ -405,7 +405,7 @@ public void Seek(double ms) foreach (var entry in Attempt.Objects) { - ObjectIndicesStart[entry.Key] = Util.Misc.BinarySearch(noteTimestamps, ms) + 1; + ObjectIndicesStart[entry.Key] = util.Misc.BinarySearch(noteTimestamps, ms) + 1; ObjectIndicesEnd[entry.Key] = entry.Value.Count; } diff --git a/scripts/game/managers/HudManager.cs b/scripts/game/managers/HudManager.cs index eed54b4e..901d2e6a 100644 --- a/scripts/game/managers/HudManager.cs +++ b/scripts/game/managers/HudManager.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using Godot; -using Util; +using util; public partial class HudManager : Node { diff --git a/scripts/game/managers/ReplayManager.cs b/scripts/game/managers/ReplayManager.cs index 972c86ef..1c2010d0 100644 --- a/scripts/game/managers/ReplayManager.cs +++ b/scripts/game/managers/ReplayManager.cs @@ -177,8 +177,8 @@ public override void _Ready() seekerTimeline.ValueChanged += value => { - string current = $"{Util.String.FormatTime(value * ReplayLength / 1000)}"; - string end = $"{Util.String.FormatTime(ReplayLength / 1000)}"; + string current = $"{util.String.FormatTime(value * ReplayLength / 1000)}"; + string end = $"{util.String.FormatTime(ReplayLength / 1000)}"; seekerTime.Text = $"{current} / {end}"; }; diff --git a/scripts/game/ui/PanelLeft.cs b/scripts/game/ui/PanelLeft.cs index 0a6a2f2d..e7c229a9 100644 --- a/scripts/game/ui/PanelLeft.cs +++ b/scripts/game/ui/PanelLeft.cs @@ -58,7 +58,7 @@ public override void _PhysicsProcess(double delta) public void OnStatsUpdated(Attempt attempt) { - score.Text = Util.String.PadMagnitude(attempt.Score); + score.Text = util.String.PadMagnitude(attempt.Score); multiplier.Text = $"{attempt.ComboMultiplier}x"; altCombo.Text = $"{attempt.Combo}"; diff --git a/scripts/game/ui/PanelRight.cs b/scripts/game/ui/PanelRight.cs index 0607eb6d..25f9e637 100644 --- a/scripts/game/ui/PanelRight.cs +++ b/scripts/game/ui/PanelRight.cs @@ -88,6 +88,6 @@ public void OnStatsUpdated(Attempt attempt) hits.Text = $"{attempt.Hits}"; misses.Text = $"{attempt.Misses}"; simpleMisses.Text = $"{attempt.Misses}"; - sum.Text = Util.String.PadMagnitude(attempt.Sum); + sum.Text = util.String.PadMagnitude(attempt.Sum); } } diff --git a/scripts/game/ui/PlaytestOverlay.cs b/scripts/game/ui/PlaytestOverlay.cs index 78089405..ef52c6cb 100644 --- a/scripts/game/ui/PlaytestOverlay.cs +++ b/scripts/game/ui/PlaytestOverlay.cs @@ -94,7 +94,7 @@ public static void ApplyStartFrom(string input, Map map, LineEdit valueEdit) { if (!input.IsValidFloat()) { - valueEdit.Text = Util.String.FormatTime(1.0); + valueEdit.Text = util.String.FormatTime(1.0); } double value = 0; @@ -114,13 +114,13 @@ public static void ApplyStartFrom(string input, Map map, LineEdit valueEdit) value = Math.Clamp(value * 1000, 0, map.Length); - valueEdit.Text = Util.String.FormatTime(value / 1000); + valueEdit.Text = util.String.FormatTime(value / 1000); } else if (!input.Contains(':')) { if (!input.IsValidFloat()) { - valueEdit.Text = Util.String.FormatTime(1.0); + valueEdit.Text = util.String.FormatTime(1.0); } double value = 0.0; @@ -129,7 +129,7 @@ public static void ApplyStartFrom(string input, Map map, LineEdit valueEdit) value = inputValue; value = Math.Clamp(value, 0, map.Length); - valueEdit.Text = Util.String.FormatTime(value); + valueEdit.Text = util.String.FormatTime(value); } } diff --git a/scripts/game/ui/Progress.cs b/scripts/game/ui/Progress.cs index b8f28852..c7acc720 100644 --- a/scripts/game/ui/Progress.cs +++ b/scripts/game/ui/Progress.cs @@ -13,6 +13,6 @@ public override void Init() public override void Process(double delta, Attempt attempt) { - label.Text = $"{Util.String.FormatTime(Math.Max(0, attempt.Progress) / 1000)} / {Util.String.FormatTime(attempt.Length / 1000)}"; + label.Text = $"{util.String.FormatTime(Math.Max(0, attempt.Progress) / 1000)} / {util.String.FormatTime(attempt.Length / 1000)}"; } } diff --git a/scripts/map/Map.cs b/scripts/map/Map.cs index 2d01a213..9cc5d6a8 100644 --- a/scripts/map/Map.cs +++ b/scripts/map/Map.cs @@ -114,7 +114,7 @@ private Texture2D getCover() if (cover == DefaultCover && File.Exists($"{path}/cover.png")) { byte[] coverBuffer = File.ReadAllBytes($"{path}/cover.png"); - var image = Util.Misc.LoadImageFromBuffer(coverBuffer); + var image = util.Misc.LoadImageFromBuffer(coverBuffer); if (image != null) { diff --git a/scripts/map/MapCache.cs b/scripts/map/MapCache.cs index c05ff403..93bc0b92 100644 --- a/scripts/map/MapCache.cs +++ b/scripts/map/MapCache.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Godot; -using Util; +using util; public static class MapCache { diff --git a/scripts/map/MapManager.cs b/scripts/map/MapManager.cs index ca8f83a1..90b6947e 100644 --- a/scripts/map/MapManager.cs +++ b/scripts/map/MapManager.cs @@ -131,9 +131,9 @@ public static void Delete(Map map) public static void Sanitize(Map map) { - string sanitizedTitle = Util.String.SanitizeZalgo(map.PrettyTitle); - string sanitizedMappers = Util.String.SanitizeZalgo(map.PrettyMappers); - string sanitizedDiffName = Util.String.SanitizeZalgo(map.DifficultyName); + string sanitizedTitle = util.String.SanitizeZalgo(map.PrettyTitle); + string sanitizedMappers = util.String.SanitizeZalgo(map.PrettyMappers); + string sanitizedDiffName = util.String.SanitizeZalgo(map.DifficultyName); bool updated = sanitizedTitle != map.PrettyTitle || sanitizedMappers != map.PrettyMappers || sanitizedDiffName != map.DifficultyName; diff --git a/scripts/map/MapParser.cs b/scripts/map/MapParser.cs index 420ff1eb..f17ac837 100644 --- a/scripts/map/MapParser.cs +++ b/scripts/map/MapParser.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Godot; using Godot.Collections; -using Util; +using util; public partial class MapParser : Node { diff --git a/scripts/scenes/MainMenu.cs b/scripts/scenes/MainMenu.cs index 91309ee9..dbef644b 100644 --- a/scripts/scenes/MainMenu.cs +++ b/scripts/scenes/MainMenu.cs @@ -122,7 +122,7 @@ public override void _Process(double delta) } } - public void Transition(Panel menu, bool instant = false) + public new void Transition(Panel menu, bool instant = false) { if (CurrentMenu == menu) { diff --git a/scripts/scenes/Results.cs b/scripts/scenes/Results.cs index eec5d68f..f8d9e82d 100644 --- a/scripts/scenes/Results.cs +++ b/scripts/scenes/Results.cs @@ -35,8 +35,8 @@ public override void _Ready() holder.GetNode public SettingsItem OptionalPlaytestParameters { get; private set; } + [Order] + /// + /// Hides Notifications, unless they're important + /// + public SettingsItem HideNotifications { get; private set; } + [Order] /// /// Restarts settings to the game's defaults @@ -783,13 +789,6 @@ public SettingsProfile() Title = "Space Hit Effects", Description = "Toggles note hit effects for the game space", Section = SettingsSection.Spaces, - UpdateAction = (_, init) => - { - if ((bool)SpaceHitEffects.GetVariant()) - { - CustomSpaceColor.SetVariant(false); - } - }, }; SpaceEffects = new(true) @@ -1530,6 +1529,14 @@ public SettingsProfile() Section = SettingsSection.Other, }; + HideNotifications = new(false) + { + Id = "HideNotifications", + Title = "Hide Notifications", + Description = "Hides notifications, important ones will still display", + Section = SettingsSection.Other, + }; + ResetToDefaults = new(default) { Id = "ResetToDefaults", @@ -1618,6 +1625,38 @@ private static List getAvailableMeshes() return meshes; } + private static List getAvailableSpaces() + { + List spaces = + [ + "skin", + "circulartunnel", + "conspiracy", + "galaxy", + "grid", + "relic", + "solid", + "squircles", + "tritunnel", + "tunnel", + "void", + "vortex", + "waves", + ]; + string spaceDir = $"{Constants.USER_FOLDER}/spaces"; + + if (Directory.Exists(spaceDir)) + { + string[] objFiles = Directory.GetFiles(spaceDir, "*.tscn"); + foreach (string file in objFiles) + { + spaces.Add(Path.GetFileNameWithoutExtension(file)); + } + } + + return spaces; + } + public static void ImportFromNightlySettings(string settingsPath) { string nightlySettings = File.Exists(settingsPath) ? File.ReadAllText(settingsPath) : null; diff --git a/scripts/game/Attempt.cs b/scripts/game/Attempt.cs index 5bed1f96..3bea801e 100644 --- a/scripts/game/Attempt.cs +++ b/scripts/game/Attempt.cs @@ -51,6 +51,7 @@ public partial class Attempt : RefCounted public Vector2 CursorPosition = Vector2.Zero; public Vector2 RawCursorPosition = Vector2.Zero; + public Vector2 BufferCursorPosition = Vector2.Zero; public double DistanceMM = 0; public ulong FirstNote = 0; diff --git a/scripts/game/camera/CameraLock.cs b/scripts/game/camera/CameraLock.cs index 8db9f854..6dcbf5d6 100644 --- a/scripts/game/camera/CameraLock.cs +++ b/scripts/game/camera/CameraLock.cs @@ -12,6 +12,7 @@ public override void Process( ReplayManager replayManager, Camera3D camera, MeshInstance3D cursor, + MeshInstance3D bufferCursor, Vector2 mouseDelta, float sensitivity ) @@ -29,6 +30,7 @@ float sensitivity { attempt.RawCursorPosition = attempt.IsReplay ? replayManager.CursorPosition : attempt.RawCursorPosition + delta; attempt.CursorPosition = attempt.RawCursorPosition.Clamp(-Constants.BOUNDS, Constants.BOUNDS); + attempt.BufferCursorPosition = attempt.IsReplay ? replayManager.CursorPosition : attempt.BufferCursorPosition + delta; } var origin = new Vector3(0, 0, 3.75f); @@ -45,10 +47,12 @@ float sensitivity camera.Rotation = Vector3.Zero; Vector3 cursorPos = new(attempt.CursorPosition.X, attempt.CursorPosition.Y, 0); + Vector3 bufferCursorPos = new(attempt.BufferCursorPosition.X, attempt.BufferCursorPosition.Y, 0); if (cursorPos.IsFinite()) { cursor.Position = cursorPos; + bufferCursor.Position = bufferCursorPos; } } } diff --git a/scripts/game/camera/CameraMode.cs b/scripts/game/camera/CameraMode.cs index 4826b87b..54bb012e 100644 --- a/scripts/game/camera/CameraMode.cs +++ b/scripts/game/camera/CameraMode.cs @@ -12,6 +12,7 @@ public abstract void Process( ReplayManager replayManager, Camera3D camera, MeshInstance3D cursor, + MeshInstance3D bufferCursor, Vector2 mouseDelta, float sensitivity ); diff --git a/scripts/game/camera/CameraSpin.cs b/scripts/game/camera/CameraSpin.cs index e031761a..bf814465 100644 --- a/scripts/game/camera/CameraSpin.cs +++ b/scripts/game/camera/CameraSpin.cs @@ -1,4 +1,4 @@ -using System; +using System; using Godot; public class CameraSpin : CameraMode @@ -12,6 +12,7 @@ public override void Process( ReplayManager replayManager, Camera3D camera, MeshInstance3D cursor, + MeshInstance3D bufferCursor, Vector2 mouseDelta, float sensitivity ) @@ -48,13 +49,16 @@ float sensitivity // Project Cursor from Camera's "ray cast" attempt.RawCursorPosition = cameraVector2 - lookVector2 * Mathf.Abs(camera.Position.Z / lookVector.Z); + attempt.BufferCursorPosition = attempt.RawCursorPosition; attempt.CursorPosition = attempt.RawCursorPosition.Clamp(-Constants.BOUNDS, Constants.BOUNDS); Vector3 cursorPos = new(attempt.CursorPosition.X, attempt.CursorPosition.Y, 0); + Vector3 bufferCursorPos = new(attempt.BufferCursorPosition.X, attempt.BufferCursorPosition.Y, 0); if (cursorPos.IsFinite()) { cursor.Position = cursorPos; + bufferCursor.Position = bufferCursorPos; } } } diff --git a/scripts/game/managers/CursorManager.cs b/scripts/game/managers/CursorManager.cs index 2420df1c..dde3d263 100644 --- a/scripts/game/managers/CursorManager.cs +++ b/scripts/game/managers/CursorManager.cs @@ -19,6 +19,9 @@ public partial class CursorManager : Node [Export] private MeshInstance3D cursorMesh; + [Export] + private MeshInstance3D bufferCursor; + [Export] private Camera3D camera; @@ -35,6 +38,7 @@ public override void _Ready() defaultCameraTransform = camera.Transform; cursorMesh ??= GetNode("Cursor"); + bufferCursor ??= GetNode("BufferCursor"); playerInputController ??= GetNode("/PlayerInputController"); replayManager ??= GetNode("ReplayManager"); } @@ -120,7 +124,7 @@ public void UpdateCursor(Vector2 inputDelta, int cursorIndex = 0) var attempt = runner.Attempt; - attempt.CameraMode.Process(attempt, replayManager, camera, cursors[cursorIndex], inputDelta, sensitivity); + attempt.CameraMode.Process(attempt, replayManager, camera, cursors[cursorIndex], bufferCursor, inputDelta, sensitivity); } public void UpdateAutoplayCursor(Vector2 position) @@ -146,18 +150,20 @@ public void UpdateAutoplayCursor(Vector2 position) var mouseDelta = new Vector2(-yaw, -pitch) * (120f * Mathf.Pi); - attempt.CameraMode.Process(attempt, replayManager, camera, cursors[0], mouseDelta, 1f); + attempt.CameraMode.Process(attempt, replayManager, camera, cursors[0], bufferCursor, mouseDelta, 1f); return; } attempt.RawCursorPosition = position; attempt.CursorPosition = position; + attempt.BufferCursorPosition = position; Vector3 cursorPosition = new(position.X, position.Y, 0); camera.Position = new Vector3(0, 0, 3.76f) + cursorPosition * (float)settings.CameraParallax; camera.Rotation = Vector3.Zero; cursors[0].Position = cursorPosition; + bufferCursor.Position = cursorPosition; } // Reset everything to zero so it doesn't have infinite sensitivity @@ -166,6 +172,7 @@ private void repositionAbsolute() camera.Rotation = Vector3.Zero; runner.Attempt.RawCursorPosition = Vector2.Zero; runner.Attempt.CursorPosition = Vector2.Zero; + runner.Attempt.BufferCursorPosition = Vector2.Zero; } private void updateCursorRotation(double delta) => diff --git a/scripts/game/ui/Grid.cs b/scripts/game/ui/Grid.cs index 19536c72..620eb038 100644 --- a/scripts/game/ui/Grid.cs +++ b/scripts/game/ui/Grid.cs @@ -7,6 +7,7 @@ public partial class Grid : MeshInstance3D, IUIComponent [Export] public Runner Runner { get; set; } public MeshInstance3D Cursor { get; set; } + public MeshInstance3D BufferCursor { get; set; } public MultiMeshInstance3D CursorTrail { get; set; } public MeshInstance3D GridGuides { get; set; } public MeshInstance3D VisibilityAssist { get; set; } @@ -42,12 +43,22 @@ public void Init() (float)(Constants.CURSOR_SIZE * Runner.Attempt.Settings.CursorScale) ); + BufferCursor ??= GetNode("BufferCursor"); + (BufferCursor.Mesh as QuadMesh).Size = new Vector2( + (float)(Constants.CURSOR_SIZE * Runner.Attempt.Settings.CursorScale), + (float)(Constants.CURSOR_SIZE * Runner.Attempt.Settings.CursorScale) + ); + (Cursor.GetActiveMaterial(0) as StandardMaterial3D).AlbedoTexture = SkinManager.Instance.Skin.CursorImage; + (BufferCursor.GetActiveMaterial(0) as StandardMaterial3D).AlbedoTexture = SkinManager.Instance.Skin.CursorImage; // Cursor Transparency float alpha = Math.Clamp((float)(double)Runner.Attempt.Settings.CursorOpacity, 0, 1); Cursor.Transparency = 1f - alpha; + BufferCursor.Visible = !Runner.Attempt.Settings.CursorDrift && !Runner.Attempt.IsReplay; + BufferCursor.Transparency = 1f - alpha / 4; + CursorTrail ??= GetNode("CursorTrail"); (CursorTrail.MaterialOverride as StandardMaterial3D).AlbedoTexture = SkinManager.Instance.Skin.CursorImage; diff --git a/scripts/game/ui/Progress.cs b/scripts/game/ui/Progress.cs index c7acc720..8eaff04a 100644 --- a/scripts/game/ui/Progress.cs +++ b/scripts/game/ui/Progress.cs @@ -13,6 +13,6 @@ public override void Init() public override void Process(double delta, Attempt attempt) { - label.Text = $"{util.String.FormatTime(Math.Max(0, attempt.Progress) / 1000)} / {util.String.FormatTime(attempt.Length / 1000)}"; + label.Text = $"{util.String.FormatTime(Math.Max(0, attempt.Progress) / 1000)} / {util.String.FormatTime(attempt.Map.Length / 1000)}"; } } diff --git a/scripts/game/ui/ProgressBar.cs b/scripts/game/ui/ProgressBar.cs index 55eb74df..59e85530 100644 --- a/scripts/game/ui/ProgressBar.cs +++ b/scripts/game/ui/ProgressBar.cs @@ -24,7 +24,7 @@ public override void Process(double delta, Attempt attempt) lastProgress = (float)attempt.Progress; - Vector2 progressSize = new Vector2(32 + (float)(Runner.Attempt.Progress / Runner.Attempt.Length) * 1024, 80); + Vector2 progressSize = new Vector2(32 + (float)(Runner.Attempt.Progress / Runner.Attempt.Map.Length) * 1024, 80); if ((int)progressSize.X != (int)progressBarTexture.Size.X) { diff --git a/scripts/ui/SettingsMenu.cs b/scripts/ui/SettingsMenu.cs index b4e57bfc..507e5ea8 100644 --- a/scripts/ui/SettingsMenu.cs +++ b/scripts/ui/SettingsMenu.cs @@ -10,11 +10,15 @@ public partial class SettingsMenu : ColorRect public static SettingsMenu Instance; public bool Shown = false; + public bool WasSkinPreviewToggled = false; private Dictionary settingPanels = []; private Button hideButton; + private Button showSkinPreview; + private Button refreshPreview; private Panel holder; private Panel header; + private Panel skinPreviewPanel; private OptionButton profilesButton; private VBoxContainer sidebar; private Panel categories; @@ -36,7 +40,10 @@ public override void _Ready() hideButton = GetNode