From 1e005c56c29db8d39fe2ac0eb0b5680cfe7b9e4e Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Tue, 29 Sep 2026 19:59:41 +0200 Subject: [PATCH 01/18] feat: added video backgrounds --- .gitignore | 1 + prefabs/map_info_container.tscn | 2 - project.godot | 2 +- scenes/game.tscn | 16 +++- scripts/database/settings/SettingsProfile.cs | 60 +++++++-------- scripts/game/Runner.cs | 78 ++++++++++++++++++-- scripts/game/ui/PauseMenu.cs | 6 ++ scripts/map/MapManager.cs | 4 +- scripts/ui/menu/play/MapInfoContainer.cs | 18 ++--- 9 files changed, 135 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 6696ada6..7e6247e1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ mono_crash.*.json # Other nuget.config *.lscache +addons/gde_gozen diff --git a/prefabs/map_info_container.tscn b/prefabs/map_info_container.tscn index db3771d1..de8eaee5 100644 --- a/prefabs/map_info_container.tscn +++ b/prefabs/map_info_container.tscn @@ -382,7 +382,6 @@ icon = ExtResource("5_1m5pv") icon_alignment = 1 [node name="Video" type="Button" parent="Info/ScrollContainer/HBoxContainer/Subholder/Buttons" unique_id=1985048325] -visible = false layout_mode = 2 tooltip_text = "Add Video" focus_mode = 0 @@ -390,7 +389,6 @@ mouse_default_cursor_shape = 2 theme = ExtResource("4_4d08m") theme_override_colors/font_color = Color(1, 1, 1, 1) theme_override_constants/icon_max_width = 24 -disabled = true icon = ExtResource("6_g02bu") icon_alignment = 1 diff --git a/project.godot b/project.godot index b22af139..cf4f7cca 100644 --- a/project.godot +++ b/project.godot @@ -59,7 +59,7 @@ project/assembly_name="Rhythia" [editor_plugins] -enabled=PackedStringArray() +enabled=PackedStringArray("res://addons/gde_gozen/plugin.cfg") [file_customization] diff --git a/scenes/game.tscn b/scenes/game.tscn index 1d706810..73d957e2 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -41,6 +41,7 @@ [ext_resource type="Script" uid="uid://cvaggwr3rrenl" path="res://scripts/game/renderers/NoteRenderer.cs" id="34_pr1ej"] [ext_resource type="Texture2D" uid="uid://bc0w1ujoshiyk" path="res://user/skins/default/game/visibility_assist.png" id="36_05nbr"] [ext_resource type="Script" uid="uid://c8d872aoiqglo" path="res://scripts/PlayerInputController.cs" id="37_02fa5"] +[ext_resource type="Script" uid="uid://dqo4ee3g73ock" path="res://addons/gde_gozen/VideoPlayback.cs" id="38_0sl42"] [ext_resource type="Script" uid="uid://dlxxg2h4cl5hj" path="res://scripts/game/ui/PauseMenu.cs" id="38_8ool0"] [ext_resource type="Script" uid="uid://c24b7vefbxvbh" path="res://scripts/game/ui/PlaytestOverlay.cs" id="40_m4url"] @@ -220,7 +221,6 @@ texture_filter = 1 texture_repeat = false [sub_resource type="QuadMesh" id="QuadMesh_3p7x2"] -size = Vector2(258.3, 210) [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_d5aou"] @@ -708,8 +708,7 @@ fov = 70.0 size = 5.0 [node name="Video" type="MeshInstance3D" parent="Runner" unique_id=1036410191] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.479675, -0.459572, -99.9929) -visible = false +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -100) material_override = SubResource("StandardMaterial3D_rj3ky") transparency = 1.0 cast_shadow = 0 @@ -721,6 +720,17 @@ transparent_bg = true canvas_item_default_texture_filter = 2 size = Vector2i(1920, 1080) +[node name="VideoPlayer" type="Control" parent="Runner/Video/VideoViewport" unique_id=1903130493] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("38_0sl42") +EnableAudio = false +metadata/_custom_type_script = "uid://dqo4ee3g73ock" + [node name="QuitOverlay" type="Panel" parent="." unique_id=316579954] visible = false modulate = Color(1, 1, 1, 0) diff --git a/scripts/database/settings/SettingsProfile.cs b/scripts/database/settings/SettingsProfile.cs index d4a5eeb0..e739b733 100644 --- a/scripts/database/settings/SettingsProfile.cs +++ b/scripts/database/settings/SettingsProfile.cs @@ -208,14 +208,14 @@ public partial class SettingsProfile /// /// Adjusts the video background dim /// - //[Order] - //public SettingsItem VideoDim { get; private set; } + [Order] + public SettingsItem VideoDim { get; private set; } /// /// Adjusts the scale of the video background /// - //[Order] - //public SettingsItem VideoRenderScale { get; private set; } + [Order] + public SettingsItem VideoRenderScale { get; private set; } /// /// Toggles Grid Guides @@ -916,37 +916,37 @@ public SettingsProfile() }, }; - //VideoDim = new(80) - //{ - //Id = "VideoDim", - //Title = "Video BG Dim", - //Description = "Adjusts the video background dim", - //Section = SettingsSection.Visual, - //Slider = new() - //{ - //Step = 1, - //MinValue = 0, - //MaxValue = 100 - //} - //}; + VideoDim = new(80) + { + Id = "VideoDim", + Title = "Video BG Dim", + Description = "Adjusts the video background dim", + Section = SettingsSection.Visual, + Slider = new() + { + Step = 1, + MinValue = 0, + MaxValue = 100 + } + }; #endregion #region Video - //VideoRenderScale = new(100) - //{ - //Id = "VideoRenderScale", - //Title = "Video BG Render Scale", - //Description = "Adjusts the scale of the video background", - //Section = SettingsSection.Visual, - //Slider = new() - //{ - //Step = 1, - //MinValue = 0, - //MaxValue = 100 - //} - //}; + VideoRenderScale = new(100) + { + Id = "VideoRenderScale", + Title = "Video BG Render Scale", + Description = "Adjusts the scale of the video background", + Section = SettingsSection.Visual, + Slider = new() + { + Step = 1, + MinValue = 0, + MaxValue = 100 + } + }; SimpleHUD = new(false) { diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index 58eb0a8f..38cc8d74 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -55,9 +55,16 @@ public partial class Runner : Node3D [Export] public MeshInstance3D Cursor; - // [Export] public VideoStreamPlayer VideoStreamPlayer; + [Export] + public MeshInstance3D VideoMesh; + + [Export] + public SubViewport VideoViewport; + + [Export] + public VideoPlayback VideoPlayer; - public override void _Ready() + public override async void _Ready() { base._Ready(); @@ -66,7 +73,9 @@ public override void _Ready() Renderers ??= GetNode>("Renderers"); Grid ??= HudManager.GetNode("Grid"); Cursor ??= GetNode("Cursor"); - // VideoStreamPlayer ??= GetNode("Video/VideoViewport/VideoStreamPlayer"); + VideoMesh ??= GetNode("Video"); + VideoViewport ??= GetNode("Video/VideoViewport"); + VideoPlayer ??= GetNode("Video/VideoViewport/VideoPlayer"); } public override void _Process(double delta) @@ -125,7 +134,7 @@ public override void _Process(double delta) } } - // Song state check + // Song and video state check if (Attempt.Map.AudioBuffer != null) { @@ -142,6 +151,26 @@ public override void _Process(double delta) } } + if (Map.VideoBuffer != null && settings.VideoDim < 100 && VideoPlayer.IsOpen()) + { + double offsetProgress = Attempt.Progress - settings.LocalOffset; + double songEnd = SoundManager.Song.Stream.GetLength() * 1000; + + if (VideoPlayer.CurrentFrame >= VideoPlayer.GetVideoFrameCount()) + { + VideoPlayer.Close(); + } + else if (!VideoPlayer.IsPlaying && offsetProgress >= 0 && offsetProgress < songEnd) + { + VideoPlayer.SeekFrame((int)((float)Attempt.Progress / 1000f * VideoPlayer.GetVideoFramerate())); + VideoPlayer.Play(); + } + else if (VideoPlayer.IsPlaying && offsetProgress >= songEnd) + { + VideoPlayer.Close(); + } + } + // Skip check int passedNotes = ObjectIndicesStart[typeof(Note)]; @@ -353,6 +382,23 @@ public void Play() SoundManager.Song.PitchScale = (float)Speed; } + if (Attempt.Map.VideoBuffer != null && settings.VideoDim < 100) + { + double parallax = (double)settings.CameraParallax; + + double meshHeight = 2 * Math.Abs(Camera.Position.Z - VideoMesh.Position.Z) * Math.Tan(settings.FoV * Math.PI / 180 / 2); + double meshWidth = (double)VideoViewport.Size.X / VideoViewport.Size.Y * meshHeight; + + QuadMesh videoQuad = (QuadMesh)VideoMesh.Mesh; + videoQuad.Size = new Vector2((float)meshWidth + 2 * Constants.BOUNDS.X, (float)meshHeight + 2 * Constants.BOUNDS.Y); + + VideoMesh.Visible = true; + VideoMesh.Transparency = (float)(double)settings.VideoDim / 100f; + + VideoPlayer.Path = $"{MapUtil.MapsFolder}/{Attempt.Map.Name}/video.mp4"; + VideoPlayer.PlaybackSpeed = (float)Speed; + } + if (Attempt.IsReplay) { for (int i = 0; i < Attempt.Replays.Length; i++) @@ -373,9 +419,16 @@ public void Pause(bool? pause = null) SoundManager.Song.PitchScale = (float)Speed; SoundManager.Song.StreamPaused = !Playing; + if (Attempt.Map.VideoBuffer != null && Attempt.Progress >= 0) + { + if (Playing && VideoPlayer.IsOpen()) { VideoPlayer.Play(); } + else if (!Playing && VideoPlayer.IsOpen()) { VideoPlayer.Pause(); } + } + if (Playing) { syncSongPosition(); + syncVideoPosition(); } } @@ -413,6 +466,7 @@ public void Seek(double ms) RenderObjects(0); syncSongPosition(); + syncVideoPosition(); // Discord.Client.UpdateEndTime(DateTime.UtcNow.AddSeconds((Time.GetUnixTimeFromSystem() + (Attempt.Map.Length - Attempt.Progress) / 1000 / Speed))); } @@ -460,6 +514,8 @@ public void Stop(bool results = true) return; } + VideoPlayer.Close(); + // give objects a last chance ProcessObjects(); @@ -691,7 +747,19 @@ private void syncSongPosition() } SoundManager.Song.Seek((float)(Attempt.Progress - Attempt.Settings.LocalOffset) / 1000); - // VideoStreamPlayer.StreamPosition = (float)Attempt.Progress / 1000; + } + } + + private void syncVideoPosition() + { + if (Attempt.Map.VideoBuffer != null) + { + if (!VideoPlayer.IsPlaying && VideoPlayer.IsOpen() && Playing) + { + VideoPlayer.Play(); + } + + VideoPlayer.SeekFrame((int)((float)Attempt.Progress / 1000f * VideoPlayer.GetVideoFramerate())); } } } diff --git a/scripts/game/ui/PauseMenu.cs b/scripts/game/ui/PauseMenu.cs index 2c932e1c..be9a4f85 100644 --- a/scripts/game/ui/PauseMenu.cs +++ b/scripts/game/ui/PauseMenu.cs @@ -48,6 +48,12 @@ public void ShowMenu(bool show = true, bool instant = false) SoundManager.Song.PitchScale = (float)attempt.Speed; SoundManager.Song.StreamPaused = !runner.Playing; + if (attempt.Map.VideoBuffer != null && attempt.Progress >= 0 && runner.VideoPlayer.IsOpen()) + { + if (runner.Playing) { runner.VideoPlayer.Play(); } + else if (!runner.Playing) { runner.VideoPlayer.Pause(); } + } + MenuCursor.Instance.UpdateVisible(Shown && SettingsManager.Instance.Settings.UseCursorInMenus.Value); if (Shown) diff --git a/scripts/map/MapManager.cs b/scripts/map/MapManager.cs index 7cdbe2d5..7ed70ad4 100644 --- a/scripts/map/MapManager.cs +++ b/scripts/map/MapManager.cs @@ -65,10 +65,10 @@ public static void InsertVideo(Map map, string path) map.AudioBuffer = oldmap.AudioBuffer; map.CoverBuffer = oldmap.CoverBuffer; - Directory.Delete(map.FolderPath); + Directory.Delete(map.FolderPath, true); MapParser.Encode(map); - map.FolderPath = MapCache.GetMd5Checksum(map.FolderPath); + map.MetadataObjectHash = MapCache.GetMd5Checksum(map.FolderPath); Update(map); } diff --git a/scripts/ui/menu/play/MapInfoContainer.cs b/scripts/ui/menu/play/MapInfoContainer.cs index a12d89e4..62a147f1 100644 --- a/scripts/ui/menu/play/MapInfoContainer.cs +++ b/scripts/ui/menu/play/MapInfoContainer.cs @@ -148,15 +148,15 @@ public override void _Ready() favoriteButton.Icon = Map.Favorite ? skin.UnfavoriteButtonImage : skin.FavoriteButtonImage; }; - //videoButton.Pressed += () => - //{ - // videoDialog.Popup(); - //}; - - //videoDialog.FileSelected += (file) => - //{ - // MapManager.InsertVideo(Map, file); - //}; + videoButton.Pressed += () => + { + videoDialog.Popup(); + }; + + videoDialog.FileSelected += (file) => + { + MapManager.InsertVideo(Map, file); + }; // copyButton.Pressed += () => // { From e44e0b629f4bbc35f9a7624532d84c1ccbdb8c4c Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Fri, 2 Oct 2026 18:37:34 +0200 Subject: [PATCH 02/18] QoL: added video disable and fade in --- prefabs/map_info_container.tscn | 2 +- scripts/database/settings/SettingsProfile.cs | 36 +++++++++++++++++--- scripts/game/Attempt.cs | 5 ++- scripts/game/Runner.cs | 21 +++++++----- scripts/game/ui/PauseMenu.cs | 2 +- scripts/map/Map.cs | 3 ++ scripts/scenes/Game.cs | 8 ++++- scripts/skinning/SkinManager.cs | 4 +-- scripts/skinning/SkinProfile.cs | 4 +-- scripts/ui/menu/play/MapInfoContainer.cs | 21 +++++++++++- 10 files changed, 85 insertions(+), 21 deletions(-) diff --git a/prefabs/map_info_container.tscn b/prefabs/map_info_container.tscn index de8eaee5..2eda6792 100644 --- a/prefabs/map_info_container.tscn +++ b/prefabs/map_info_container.tscn @@ -383,7 +383,7 @@ icon_alignment = 1 [node name="Video" type="Button" parent="Info/ScrollContainer/HBoxContainer/Subholder/Buttons" unique_id=1985048325] layout_mode = 2 -tooltip_text = "Add Video" +tooltip_text = "Disable Video Background" focus_mode = 0 mouse_default_cursor_shape = 2 theme = ExtResource("4_4d08m") diff --git a/scripts/database/settings/SettingsProfile.cs b/scripts/database/settings/SettingsProfile.cs index e739b733..739c4691 100644 --- a/scripts/database/settings/SettingsProfile.cs +++ b/scripts/database/settings/SettingsProfile.cs @@ -115,6 +115,12 @@ public partial class SettingsProfile [Order] public SettingsItem GameSpace { get; private set; } + /// + /// Use void background when video background is visible + /// + // [Order] + // public SettingsItem VideoBgBlankSpace { get; private set; } + /// /// Toggles note hit effects for the game space /// @@ -217,6 +223,12 @@ public partial class SettingsProfile [Order] public SettingsItem VideoRenderScale { get; private set; } + /// + /// Disables video bg for all maps + /// + [Order] + public SettingsItem DisableVideoGlobal { get; private set; } + /// /// Toggles Grid Guides /// @@ -718,6 +730,14 @@ public SettingsProfile() }, }; + // VideoBgBlankSpace = new(false) + // { + // Id = "VideoBgBlankSpace", + // Title = "Use Void Space For Video Backgrounds", + // Description = "Use the void game space when video background is visible", + // Section = SettingsSection.Visual, + // }; + SpaceHitEffects = new(true) { Id = "SpaceHitEffects", @@ -916,6 +936,10 @@ public SettingsProfile() }, }; + #endregion + + #region Video + VideoDim = new(80) { Id = "VideoDim", @@ -930,10 +954,6 @@ public SettingsProfile() } }; - #endregion - - #region Video - VideoRenderScale = new(100) { Id = "VideoRenderScale", @@ -948,6 +968,14 @@ public SettingsProfile() } }; + DisableVideoGlobal = new(false) + { + Id = "DisableVideoGlobal", + Title = "Disable All Video Backgrounds", + Description = "Don't show video backgrounds for any map", + Section = SettingsSection.Visual, + }; + SimpleHUD = new(false) { Id = "SimpleHUD", diff --git a/scripts/game/Attempt.cs b/scripts/game/Attempt.cs index 7aa32df0..3f96a05d 100644 --- a/scripts/game/Attempt.cs +++ b/scripts/game/Attempt.cs @@ -27,6 +27,7 @@ public partial class Attempt : RefCounted public double Speed; public double StartFrom; public double Length; + public bool DisableVideo; public double Accuracy = 100; public double Health = 100; @@ -70,7 +71,8 @@ public Attempt( CameraMode cameraMode, List mods, string[] players = null, - Replay[] replays = null + Replay[] replays = null, + bool disableVideo = false ) { ID = $"{map.Name}_{OS.GetUniqueId()}_{Time.GetDatetimeStringFromUnixTime((long)Time.GetUnixTimeFromSystem())}".Replace(":", "_"); @@ -89,6 +91,7 @@ public Attempt( HasHealthModifier = Modifiers.Any(mod => mod is IHealthModifier); Objects[typeof(Note)] = [.. map.Notes]; HitsInfo = IsReplay ? Replays[0].Notes : new float[Map.Notes.Length]; + DisableVideo = disableVideo; if (IsReplay) { diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index 38cc8d74..bb0df7ac 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -151,7 +151,7 @@ public override void _Process(double delta) } } - if (Map.VideoBuffer != null && settings.VideoDim < 100 && VideoPlayer.IsOpen()) + if (!Attempt.DisableVideo && Attempt.Map.VideoBuffer != null && settings.VideoDim < 100 && VideoPlayer.IsOpen()) { double offsetProgress = Attempt.Progress - settings.LocalOffset; double songEnd = SoundManager.Song.Stream.GetLength() * 1000; @@ -382,7 +382,7 @@ public void Play() SoundManager.Song.PitchScale = (float)Speed; } - if (Attempt.Map.VideoBuffer != null && settings.VideoDim < 100) + if (!Attempt.DisableVideo && Attempt.Map.VideoBuffer != null && settings.VideoDim < 100) { double parallax = (double)settings.CameraParallax; @@ -392,11 +392,16 @@ public void Play() QuadMesh videoQuad = (QuadMesh)VideoMesh.Mesh; videoQuad.Size = new Vector2((float)meshWidth + 2 * Constants.BOUNDS.X, (float)meshHeight + 2 * Constants.BOUNDS.Y); - VideoMesh.Visible = true; - VideoMesh.Transparency = (float)(double)settings.VideoDim / 100f; - VideoPlayer.Path = $"{MapUtil.MapsFolder}/{Attempt.Map.Name}/video.mp4"; VideoPlayer.PlaybackSpeed = (float)Speed; + + VideoMesh.Transparency = 1; + VideoMesh.Visible = true; + // VideoMesh.Transparency = (float)(double)settings.VideoDim / 100f; + + Tween fadeIn = VideoMesh.CreateTween(); + fadeIn.TweenProperty(VideoMesh, "transparency", (float)(double)settings.VideoDim / 100f, Math.Abs(Attempt.Progress) / 1000).SetTrans(Tween.TransitionType.Quad).SetEase(Tween.EaseType.In); // assuming Attempt.Progress < 0 + fadeIn.Play(); } if (Attempt.IsReplay) @@ -419,7 +424,7 @@ public void Pause(bool? pause = null) SoundManager.Song.PitchScale = (float)Speed; SoundManager.Song.StreamPaused = !Playing; - if (Attempt.Map.VideoBuffer != null && Attempt.Progress >= 0) + if (!Attempt.DisableVideo && Attempt.Map.VideoBuffer != null && Attempt.Progress >= 0) { if (Playing && VideoPlayer.IsOpen()) { VideoPlayer.Play(); } else if (!Playing && VideoPlayer.IsOpen()) { VideoPlayer.Pause(); } @@ -752,9 +757,9 @@ private void syncSongPosition() private void syncVideoPosition() { - if (Attempt.Map.VideoBuffer != null) + if (!Attempt.Map.DisableVideo && Attempt.Map.VideoBuffer != null && VideoPlayer.IsOpen()) { - if (!VideoPlayer.IsPlaying && VideoPlayer.IsOpen() && Playing) + if (!VideoPlayer.IsPlaying && Playing) { VideoPlayer.Play(); } diff --git a/scripts/game/ui/PauseMenu.cs b/scripts/game/ui/PauseMenu.cs index be9a4f85..0285ca3d 100644 --- a/scripts/game/ui/PauseMenu.cs +++ b/scripts/game/ui/PauseMenu.cs @@ -48,7 +48,7 @@ public void ShowMenu(bool show = true, bool instant = false) SoundManager.Song.PitchScale = (float)attempt.Speed; SoundManager.Song.StreamPaused = !runner.Playing; - if (attempt.Map.VideoBuffer != null && attempt.Progress >= 0 && runner.VideoPlayer.IsOpen()) + if (!attempt.DisableVideo && attempt.Map.VideoBuffer != null && attempt.Progress >= 0 && runner.VideoPlayer.IsOpen()) { if (runner.Playing) { runner.VideoPlayer.Play(); } else if (!runner.Playing) { runner.VideoPlayer.Pause(); } diff --git a/scripts/map/Map.cs b/scripts/map/Map.cs index 66c9a60d..03d127b6 100644 --- a/scripts/map/Map.cs +++ b/scripts/map/Map.cs @@ -90,6 +90,9 @@ public Texture2D Cover [Ignore] public byte[] VideoBuffer { get; set; } = []; + + public bool DisableVideo { get; set; } + private Note[] notes; [Ignore] diff --git a/scripts/scenes/Game.cs b/scripts/scenes/Game.cs index c8765e0b..4d64297f 100644 --- a/scripts/scenes/Game.cs +++ b/scripts/scenes/Game.cs @@ -224,7 +224,13 @@ public static void Play( StartQueued = true; var parsedMap = MapParser.Decode(map.FolderPath, Rhythia.AudioFilePath); - Attempt = new(parsedMap, speed, startFrom, cameraMode, mods, players, replays); + + bool mapDisableVideo = DatabaseService.Connection.Get(map.Id).DisableVideo; + bool globalVideoDisable = SettingsManager.Instance.Settings.DisableVideoGlobal; + bool disableVideo = mapDisableVideo || globalVideoDisable; + + Attempt = new(parsedMap, speed, startFrom, cameraMode, mods, players, replays, disableVideo: disableVideo); + GD.Print(Attempt.DisableVideo); SceneManager.Load("res://scenes/game.tscn"); } diff --git a/scripts/skinning/SkinManager.cs b/scripts/skinning/SkinManager.cs index 7a538962..1c4b8753 100644 --- a/scripts/skinning/SkinManager.cs +++ b/scripts/skinning/SkinManager.cs @@ -94,8 +94,8 @@ public static void Load() skin.UnfavoriteButtonImage = loadTexture("ui/buttons/unfavorite.png"); skin.CopyButtonImage = loadTexture("ui/buttons/copy.png"); skin.DeleteButtonImage = loadTexture("ui/buttons/delete.png"); - skin.AddVideoButtonImage = loadTexture("ui/buttons/add_video.png"); - skin.RemoveVideoButtonImage = loadTexture("ui/buttons/remove_video.png"); + skin.VideoEnabledButtonImage = loadTexture("ui/buttons/add_video.png"); + skin.VideoDisabledButtonImage = loadTexture("ui/buttons/remove_video.png"); skin.GrabberNormalImage = loadTexture("ui/buttons/grabber_normal.png"); skin.GrabberPressedImage = loadTexture("ui/buttons/grabber_pressed.png"); diff --git a/scripts/skinning/SkinProfile.cs b/scripts/skinning/SkinProfile.cs index 751f9cfb..d00fff81 100644 --- a/scripts/skinning/SkinProfile.cs +++ b/scripts/skinning/SkinProfile.cs @@ -73,9 +73,9 @@ public partial class SkinProfile : RefCounted public ImageTexture DeleteButtonImage { get; set; } = new(); - public ImageTexture AddVideoButtonImage { get; set; } = new(); + public ImageTexture VideoEnabledButtonImage { get; set; } = new(); - public ImageTexture RemoveVideoButtonImage { get; set; } = new(); + public ImageTexture VideoDisabledButtonImage { get; set; } = new(); public ImageTexture GrabberNormalImage { get; set; } = new(); diff --git a/scripts/ui/menu/play/MapInfoContainer.cs b/scripts/ui/menu/play/MapInfoContainer.cs index 62a147f1..f292aa52 100644 --- a/scripts/ui/menu/play/MapInfoContainer.cs +++ b/scripts/ui/menu/play/MapInfoContainer.cs @@ -150,7 +150,15 @@ public override void _Ready() videoButton.Pressed += () => { - videoDialog.Popup(); + Map.DisableVideo = !Map.DisableVideo; + MapManager.Update(Map); + + var skin = SkinManager.Instance.Skin; + + videoButton.TooltipText = Map.DisableVideo ? "Enable Video Background" : "Disable Video Background"; + videoButton.Icon = Map.DisableVideo ? skin.VideoDisabledButtonImage : skin.VideoEnabledButtonImage; + + // videoDialog.Popup(); }; videoDialog.FileSelected += (file) => @@ -457,6 +465,17 @@ public void Setup(Map map) favoriteButton.TooltipText = map.Favorite ? "Unfavorite" : "Favorite"; favoriteButton.Icon = map.Favorite ? SkinManager.Instance.Skin.UnfavoriteButtonImage : SkinManager.Instance.Skin.FavoriteButtonImage; + if (File.Exists($"{MapUtil.MapsFolder}/{Map.Name}/video.mp4")) + { + videoButton.TooltipText = Map.DisableVideo ? "Enable Video Background" : "Disable Video Background"; + videoButton.Icon = Map.DisableVideo ? SkinManager.Instance.Skin.VideoDisabledButtonImage : SkinManager.Instance.Skin.VideoEnabledButtonImage; + } + else + { + videoButton.Disabled = true; + videoButton.TooltipText = "Map Has No Video"; + } + artistLink.Visible = map.ArtistLink != ""; artistLink.Text = string.Format(CultureInfo.CurrentCulture, artistLinkFormat, map.ArtistPlatform); From 8cf717de5b0d70b0901c35eb17efc4e6ad763ed3 Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Fri, 2 Oct 2026 19:34:20 +0200 Subject: [PATCH 03/18] QoL: added toggleable void game space when video is visible --- scripts/SceneManager.cs | 14 ++++++++++---- scripts/database/settings/SettingsProfile.cs | 18 +++++++++--------- scripts/scenes/Game.cs | 7 ++++--- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/scripts/SceneManager.cs b/scripts/SceneManager.cs index b945e033..3b05ecb8 100644 --- a/scripts/SceneManager.cs +++ b/scripts/SceneManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Godot; +using Spaces; public partial class SceneManager : Node { @@ -47,7 +48,7 @@ public static void ReloadCurrentScene() Load(activeScenePath, true); } - public static void Load(string path, bool skipTransition = false, Action callback = null) + public static void Load(string path, bool skipTransition = false, bool forceVoidBg = false, Action callback = null) { bool isSceneLoaded = Scenes.TryGetValue(path, out BaseScene loadedScene); var newScene = isSceneLoaded ? loadedScene : (BaseScene)ResourceLoader.Load(path).Instantiate(); @@ -73,7 +74,7 @@ public static void Load(string path, bool skipTransition = false, Action callbac activeScenePath = path; Scene = newScene; - addScene(newScene); + addScene(newScene, forceVoidBg: forceVoidBg); newScene.Transition.SelfModulate = Color.FromHtml("ffffffff"); Instance @@ -86,7 +87,7 @@ public static void Load(string path, bool skipTransition = false, Action callbac ); } - private static void addScene(BaseScene scene, bool updateSpace = true) + private static void addScene(BaseScene scene, bool updateSpace = true, bool forceVoidBg = false) { if (scene == null || scene.GetParent() == Instance) { @@ -95,7 +96,12 @@ private static void addScene(BaseScene scene, bool updateSpace = true) if (updateSpace) { - addSpace(scene.GetSpace(), scene.AddSpaceAsChild); + if (forceVoidBg) + { + BaseSpace voidSpace = GD.Load("res://prefabs/spaces/void.tscn").Instantiate() as BaseSpace; + addSpace(voidSpace, scene.AddSpaceAsChild); + } + else { addSpace(scene.GetSpace(), scene.AddSpaceAsChild); } } Instance.AddChild(scene); diff --git a/scripts/database/settings/SettingsProfile.cs b/scripts/database/settings/SettingsProfile.cs index 739c4691..790a0f2b 100644 --- a/scripts/database/settings/SettingsProfile.cs +++ b/scripts/database/settings/SettingsProfile.cs @@ -118,8 +118,8 @@ public partial class SettingsProfile /// /// Use void background when video background is visible /// - // [Order] - // public SettingsItem VideoBgBlankSpace { get; private set; } + [Order] + public SettingsItem VideoBgVoidSpace { get; private set; } /// /// Toggles note hit effects for the game space @@ -730,13 +730,13 @@ public SettingsProfile() }, }; - // VideoBgBlankSpace = new(false) - // { - // Id = "VideoBgBlankSpace", - // Title = "Use Void Space For Video Backgrounds", - // Description = "Use the void game space when video background is visible", - // Section = SettingsSection.Visual, - // }; + VideoBgVoidSpace = new(false) + { + Id = "VideoBgVoidSpace", + Title = "Use Void Space For Video Backgrounds", + Description = "Use the void game space when video background is visible", + Section = SettingsSection.Visual, + }; SpaceHitEffects = new(true) { diff --git a/scripts/scenes/Game.cs b/scripts/scenes/Game.cs index 4d64297f..6e685ec9 100644 --- a/scripts/scenes/Game.cs +++ b/scripts/scenes/Game.cs @@ -226,13 +226,14 @@ public static void Play( var parsedMap = MapParser.Decode(map.FolderPath, Rhythia.AudioFilePath); bool mapDisableVideo = DatabaseService.Connection.Get(map.Id).DisableVideo; - bool globalVideoDisable = SettingsManager.Instance.Settings.DisableVideoGlobal; + bool globalVideoDisable = (bool)SettingsManager.Instance.Settings.DisableVideoGlobal; bool disableVideo = mapDisableVideo || globalVideoDisable; Attempt = new(parsedMap, speed, startFrom, cameraMode, mods, players, replays, disableVideo: disableVideo); - GD.Print(Attempt.DisableVideo); - SceneManager.Load("res://scenes/game.tscn"); + bool useVoidSpace = (bool)SettingsManager.Instance.Settings.VideoBgVoidSpace && !disableVideo; + + SceneManager.Load("res://scenes/game.tscn", forceVoidBg: useVoidSpace); } public void Restart() From af36ab7b8434e960e5ca599de0574e7e29e49385 Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Mon, 5 Oct 2026 19:57:49 +0200 Subject: [PATCH 04/18] QoL: added context menu in map list --- scenes/main_menu.tscn | 40 +---- scripts/map/MapManager.cs | 2 +- scripts/ui/menu/play/ContextMenu.cs | 186 +++++++++++++++++++++++ scripts/ui/menu/play/ContextMenu.cs.uid | 1 + scripts/ui/menu/play/MapInfoContainer.cs | 12 +- scripts/ui/menu/play/MapList.cs | 23 ++- 6 files changed, 220 insertions(+), 44 deletions(-) create mode 100644 scripts/ui/menu/play/ContextMenu.cs create mode 100644 scripts/ui/menu/play/ContextMenu.cs.uid diff --git a/scenes/main_menu.tscn b/scenes/main_menu.tscn index cf30d5cd..52e98788 100644 --- a/scenes/main_menu.tscn +++ b/scenes/main_menu.tscn @@ -56,6 +56,7 @@ [ext_resource type="Script" uid="uid://dqi64p5jl23cv" path="res://scripts/ui/menu/VersionLabel.cs" id="45_mb34c"] [ext_resource type="Texture2D" uid="uid://8du82a5dxjdk" path="res://user/skins/default/ui/jukebox_play.png" id="48_q1l3m"] [ext_resource type="Texture2D" uid="uid://bpsvcunhigs6c" path="res://user/skins/default/ui/jukebox_skip.png" id="49_7u5pb"] +[ext_resource type="Script" uid="uid://crvc85t37oayi" path="res://scripts/ui/menu/play/ContextMenu.cs" id="57_sgh51"] [sub_resource type="SystemFont" id="SystemFont_lsskb"] font_names = PackedStringArray("Ubuntu") @@ -2105,11 +2106,13 @@ grow_vertical = 0 mouse_filter = 2 alignment = 2 -[node name="ContextMenu" type="Panel" parent="." unique_id=836062335] +[node name="ContextMenu" type="Panel" parent="." unique_id=836062335 node_paths=PackedStringArray("videoDialog")] visible = false offset_right = 150.0 offset_bottom = 120.0 theme_override_styles/panel = SubResource("StyleBoxEmpty_t0m3h") +script = ExtResource("57_sgh51") +videoDialog = NodePath("../VideoDialog") [node name="Background" type="Panel" parent="ContextMenu" unique_id=1072032974] layout_mode = 1 @@ -2130,45 +2133,16 @@ grow_horizontal = 2 grow_vertical = 2 theme_override_constants/separation = 0 -[node name="Favorite" type="Button" parent="ContextMenu/Container" unique_id=1524374689] -custom_minimum_size = Vector2(0, 30) -layout_mode = 2 -size_flags_vertical = 3 -focus_mode = 0 -mouse_default_cursor_shape = 2 -theme = ExtResource("6_engjn") -theme_override_colors/font_color = Color(1, 1, 0.501961, 1) -theme_override_colors/font_hover_color = Color(1, 1, 0.768627, 1) -theme_override_fonts/font = ExtResource("19_c6nra") -text = "Favorite" -flat = true -alignment = 0 - -[node name="Delete" type="Button" parent="ContextMenu/Container" unique_id=559400908] -custom_minimum_size = Vector2(0, 30) -layout_mode = 2 -size_flags_vertical = 3 -focus_mode = 0 -mouse_default_cursor_shape = 2 -theme = ExtResource("6_engjn") -theme_override_colors/font_color = Color(1, 0.501961, 0.501961, 1) -theme_override_colors/font_hover_color = Color(1, 0.768627, 0.768627, 1) -theme_override_fonts/font = ExtResource("19_c6nra") -text = "Delete" -flat = true -alignment = 0 - -[node name="Video" type="Button" parent="ContextMenu/Container" unique_id=16155190] +[node name="TemplateButton" type="Button" parent="ContextMenu/Container" unique_id=704183841] +visible = false custom_minimum_size = Vector2(0, 30) layout_mode = 2 size_flags_vertical = 3 focus_mode = 0 mouse_default_cursor_shape = 2 theme = ExtResource("6_engjn") -theme_override_colors/font_color = Color(0.501961, 0.501961, 1, 1) -theme_override_colors/font_hover_color = Color(0.768627, 0.768627, 1, 1) theme_override_fonts/font = ExtResource("19_c6nra") -text = "Add Video" +text = "Button" flat = true alignment = 0 diff --git a/scripts/map/MapManager.cs b/scripts/map/MapManager.cs index 7ed70ad4..44653120 100644 --- a/scripts/map/MapManager.cs +++ b/scripts/map/MapManager.cs @@ -84,7 +84,7 @@ public static void RemoveVideo(Map map) map.AudioBuffer = oldmap.AudioBuffer; map.CoverBuffer = oldmap.CoverBuffer; - File.Delete(map.FolderPath); + Directory.Delete(map.FolderPath, true); MapParser.Encode(map); map.MetadataObjectHash = MapCache.GetMd5Checksum(map.FolderPath); diff --git a/scripts/ui/menu/play/ContextMenu.cs b/scripts/ui/menu/play/ContextMenu.cs new file mode 100644 index 00000000..053fc464 --- /dev/null +++ b/scripts/ui/menu/play/ContextMenu.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Godot; + +public partial class ContextMenu : Panel +{ + public static ContextMenu Instance; + + public static bool Shown { get; private set; } + + public static Map Map { get; private set; } + + private static Dictionary subscriptions = []; + + private static bool dialogHasAction = false; + + private Button favoriteButton; + + private Button deleteButton; + + private Button videoButton; + + [Export] + private VBoxContainer container; + + [Export] + private Button templateButton; + + [Export] + private FileDialog videoDialog; + + public override void _Ready() + { + Instance = this; + + container ??= GetNode("Container"); + templateButton ??= GetNode public List Maps = []; + private Panel contextMenu; + private TextureRect mask; private TextureRect selectionCursor; private Panel scrollBar; @@ -107,6 +109,7 @@ public override void _Ready() { Instance = this; + contextMenu = Owner.GetNode("ContextMenu"); mask = GetNode("Mask"); selectionCursor = GetNode("SelectionCursor"); scrollBar = GetNode("ScrollBar"); @@ -574,12 +577,24 @@ private MapButton setupButton(MapButton button) button.UpdateOutline(hovered ? 0.5f : 0); } }; - button.Pressed += () => + + button.GuiInput += action => { - if (dragDistance < 500 && Select(button.Map)) + if (action is InputEventMouseButton mouseButton && mouseButton.Pressed) { - button.Select(); - button.UpdateOutline(1.0f); + if (mouseButton.ButtonIndex == MouseButton.Left) + { + if (dragDistance < 500 && Select(button.Map)) + { + button.Select(); + button.UpdateOutline(1.0f); + } + } + if (mouseButton.ButtonIndex == MouseButton.Right) + { + ContextMenu.Show(mouseButton.GlobalPosition, button.Map); + GetViewport().SetInputAsHandled(); + } } }; From e7b2341c036f43d32c8f3e7a6aab1a98b84885d2 Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Sun, 11 Oct 2026 21:50:44 +0200 Subject: [PATCH 05/18] fix: spawn video player on demand --- scenes/game.tscn | 12 ----------- scripts/game/Runner.cs | 45 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/scenes/game.tscn b/scenes/game.tscn index 73d957e2..8aa9ce7b 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -41,7 +41,6 @@ [ext_resource type="Script" uid="uid://cvaggwr3rrenl" path="res://scripts/game/renderers/NoteRenderer.cs" id="34_pr1ej"] [ext_resource type="Texture2D" uid="uid://bc0w1ujoshiyk" path="res://user/skins/default/game/visibility_assist.png" id="36_05nbr"] [ext_resource type="Script" uid="uid://c8d872aoiqglo" path="res://scripts/PlayerInputController.cs" id="37_02fa5"] -[ext_resource type="Script" uid="uid://dqo4ee3g73ock" path="res://addons/gde_gozen/VideoPlayback.cs" id="38_0sl42"] [ext_resource type="Script" uid="uid://dlxxg2h4cl5hj" path="res://scripts/game/ui/PauseMenu.cs" id="38_8ool0"] [ext_resource type="Script" uid="uid://c24b7vefbxvbh" path="res://scripts/game/ui/PlaytestOverlay.cs" id="40_m4url"] @@ -720,17 +719,6 @@ transparent_bg = true canvas_item_default_texture_filter = 2 size = Vector2i(1920, 1080) -[node name="VideoPlayer" type="Control" parent="Runner/Video/VideoViewport" unique_id=1903130493] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("38_0sl42") -EnableAudio = false -metadata/_custom_type_script = "uid://dqo4ee3g73ock" - [node name="QuitOverlay" type="Panel" parent="." unique_id=316579954] visible = false modulate = Color(1, 1, 1, 0) diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index bb0df7ac..cd737d4d 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Security.Cryptography; +using System.Threading.Tasks; using Godot; public partial class Runner : Node3D @@ -61,7 +62,6 @@ public partial class Runner : Node3D [Export] public SubViewport VideoViewport; - [Export] public VideoPlayback VideoPlayer; public override async void _Ready() @@ -75,7 +75,6 @@ public override async void _Ready() Cursor ??= GetNode("Cursor"); VideoMesh ??= GetNode("Video"); VideoViewport ??= GetNode("Video/VideoViewport"); - VideoPlayer ??= GetNode("Video/VideoViewport/VideoPlayer"); } public override void _Process(double delta) @@ -384,6 +383,8 @@ public void Play() if (!Attempt.DisableVideo && Attempt.Map.VideoBuffer != null && settings.VideoDim < 100) { + spawnVideoPlayer(); + double parallax = (double)settings.CameraParallax; double meshHeight = 2 * Math.Abs(Camera.Position.Z - VideoMesh.Position.Z) * Math.Tan(settings.FoV * Math.PI / 180 / 2); @@ -519,7 +520,9 @@ public void Stop(bool results = true) return; } - VideoPlayer.Close(); + VideoPlayer?.SetCurrentFrame(VideoPlayer.GetVideoFrameCount()); + VideoPlayer?.Close(); + _ = destroyVideoPlayer(); // give objects a last chance ProcessObjects(); @@ -767,4 +770,40 @@ private void syncVideoPosition() VideoPlayer.SeekFrame((int)((float)Attempt.Progress / 1000f * VideoPlayer.GetVideoFramerate())); } } + + private void spawnVideoPlayer() + { + if (VideoPlayer != null) + { + Logger.Error("VideoPlayer is already populated. Skipping creation."); + return; + } + + VideoPlayback player = new VideoPlayback + { + Name = "VideoPlayer", + EnableAudio = false + }; + + player.SetAnchorsPreset(Control.LayoutPreset.FullRect); + + VideoPlayer = player; + + VideoViewport.AddChild(VideoPlayer); + } + + private async Task destroyVideoPlayer() + { + if (VideoPlayer == null) + { + Logger.Error("VideoPlayer is already empty."); + return; + } + + VideoPlayer.QueueFree(); + + await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); + + VideoPlayer = null; + } } From 64e1aba7d85c77fbaf825aef0fa914b8f9813ca4 Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Tue, 13 Oct 2026 20:42:39 +0200 Subject: [PATCH 06/18] fix: fixed double db entry when inserting video --- scripts/game/Runner.cs | 4 ++-- scripts/map/MapManager.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index cd737d4d..dfdc3433 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -775,7 +775,7 @@ private void spawnVideoPlayer() { if (VideoPlayer != null) { - Logger.Error("VideoPlayer is already populated. Skipping creation."); + Logger.Log("VideoPlayer is already populated. Skipping creation."); return; } @@ -796,7 +796,7 @@ private async Task destroyVideoPlayer() { if (VideoPlayer == null) { - Logger.Error("VideoPlayer is already empty."); + Logger.Log("VideoPlayer is already empty. Skipping."); return; } diff --git a/scripts/map/MapManager.cs b/scripts/map/MapManager.cs index 44653120..e8112f88 100644 --- a/scripts/map/MapManager.cs +++ b/scripts/map/MapManager.cs @@ -66,6 +66,7 @@ public static void InsertVideo(Map map, string path) map.CoverBuffer = oldmap.CoverBuffer; Directory.Delete(map.FolderPath, true); + MapCache.RemoveMap(map); MapParser.Encode(map); map.MetadataObjectHash = MapCache.GetMd5Checksum(map.FolderPath); @@ -85,6 +86,7 @@ public static void RemoveVideo(Map map) map.CoverBuffer = oldmap.CoverBuffer; Directory.Delete(map.FolderPath, true); + MapCache.RemoveMap(map); MapParser.Encode(map); map.MetadataObjectHash = MapCache.GetMd5Checksum(map.FolderPath); From 71f1a883958434b2b1ecd905a35b327fe24b66de Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Fri, 16 Oct 2026 18:49:26 +0200 Subject: [PATCH 07/18] feat: added multiple video format support --- scripts/Constants.cs | 2 ++ scripts/game/Runner.cs | 3 +- scripts/map/Map.cs | 7 +++- scripts/map/MapManager.cs | 3 ++ scripts/map/MapParser.cs | 7 ++-- scripts/map/MapUtil.cs | 41 +++++++++++++++++++++++- scripts/ui/menu/play/ContextMenu.cs | 13 +++++++- scripts/ui/menu/play/MapInfoContainer.cs | 2 +- 8 files changed, 69 insertions(+), 9 deletions(-) diff --git a/scripts/Constants.cs b/scripts/Constants.cs index d94a0154..192ba0a8 100644 --- a/scripts/Constants.cs +++ b/scripts/Constants.cs @@ -50,4 +50,6 @@ public partial class Constants : Node public static readonly Color RANKED_COLOR = new(0x00f23cff); public static readonly Color DOWNLOADED_COLOR = new Color(0x4ebf50ff); + + public static readonly string[] SUPPORTED_VIDEO_EXTENSIONS = ["mp4", "mkv", "webm"]; // in order of preference } diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index dfdc3433..dcdeadb5 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -393,12 +393,11 @@ public void Play() QuadMesh videoQuad = (QuadMesh)VideoMesh.Mesh; videoQuad.Size = new Vector2((float)meshWidth + 2 * Constants.BOUNDS.X, (float)meshHeight + 2 * Constants.BOUNDS.Y); - VideoPlayer.Path = $"{MapUtil.MapsFolder}/{Attempt.Map.Name}/video.mp4"; + VideoPlayer.Path = MapUtil.GetMapVideoPath(Map); VideoPlayer.PlaybackSpeed = (float)Speed; VideoMesh.Transparency = 1; VideoMesh.Visible = true; - // VideoMesh.Transparency = (float)(double)settings.VideoDim / 100f; Tween fadeIn = VideoMesh.CreateTween(); fadeIn.TweenProperty(VideoMesh, "transparency", (float)(double)settings.VideoDim / 100f, Math.Abs(Attempt.Progress) / 1000).SetTrans(Tween.TransitionType.Quad).SetEase(Tween.EaseType.In); // assuming Attempt.Progress < 0 diff --git a/scripts/map/Map.cs b/scripts/map/Map.cs index 03d127b6..817c06b2 100644 --- a/scripts/map/Map.cs +++ b/scripts/map/Map.cs @@ -93,6 +93,8 @@ public Texture2D Cover public bool DisableVideo { get; set; } + public string VideoExt { get; set; } = string.Empty; + private Note[] notes; [Ignore] @@ -189,6 +191,8 @@ public Map( public string EncodeMeta() { string path = $"{MapUtil.MapsFolder}/{Name}"; + string? videoPath = MapUtil.GetMapVideoPath(path); + string videoExt = videoPath != null ? Path.GetExtension(videoPath).TrimStart('.') : VideoExt; return Json.Stringify( new Godot.Collections.Dictionary() { @@ -204,8 +208,9 @@ public string EncodeMeta() ["Length"] = Length, ["HasAudio"] = AudioBuffer != null && File.Exists($"{path}/audio.{AudioExt}"), ["HasCover"] = CoverBuffer != null && File.Exists($"{path}/cover.png"), - ["HasVideo"] = VideoBuffer != null && File.Exists($"{path}/video.mp4"), + ["HasVideo"] = VideoBuffer != null && videoPath != null && File.Exists(videoPath), ["AudioExt"] = AudioExt, + ["VideoExt"] = videoExt, }, "\t" ); diff --git a/scripts/map/MapManager.cs b/scripts/map/MapManager.cs index e8112f88..b00d1b5c 100644 --- a/scripts/map/MapManager.cs +++ b/scripts/map/MapManager.cs @@ -55,9 +55,11 @@ public static void InsertVideo(Map map, string path) { Godot.FileAccess file = Godot.FileAccess.Open(path, Godot.FileAccess.ModeFlags.Read); byte[] videoBuffer = file.GetBuffer((long)file.GetLength()); + string ext = Path.GetExtension(path).TrimStart('.'); file.Close(); map.VideoBuffer = videoBuffer; + map.VideoExt = ext; var oldmap = MapParser.Decode(map.FolderPath); @@ -78,6 +80,7 @@ public static void RemoveVideo(Map map) _ = Godot.FileAccess.Open(map.FolderPath, Godot.FileAccess.ModeFlags.Read); map.VideoBuffer = null; + map.VideoExt = string.Empty; var oldmap = MapParser.Decode(map.FolderPath); diff --git a/scripts/map/MapParser.cs b/scripts/map/MapParser.cs index 839ec842..69034ff5 100644 --- a/scripts/map/MapParser.cs +++ b/scripts/map/MapParser.cs @@ -152,7 +152,7 @@ void addAsset(string name, byte[] buffer) if (decodedMap.CoverBuffer != null) addAsset($"cover.png", decodedMap.CoverBuffer); if (decodedMap.VideoBuffer != null) - addAsset($"video.mp4", decodedMap.VideoBuffer); + addAsset($"video{Path.GetExtension(MapUtil.GetMapVideoPath(decodedMap))}", decodedMap.VideoBuffer); } File.WriteAllBytes(exportFilePath, ms.ToArray()); @@ -229,7 +229,7 @@ void addAsset(string name, byte[] buffer) if (map.CoverBuffer != null) addAsset($"cover.png", map.CoverBuffer); if (map.VideoBuffer != null) - addAsset($"video.mp4", map.VideoBuffer); + addAsset($"video{Path.GetExtension(MapUtil.GetMapVideoPath(map))}", map.VideoBuffer); File.WriteAllText(Path.Combine(mapFolderPath, "metadata.json"), map.EncodeMeta()); @@ -708,7 +708,8 @@ public static Map PHXMFolder(string path) if ((bool)metadata["HasVideo"]) { - videoBuffer = File.ReadAllBytes($"{path}/video.mp4"); + string videoPath = File.Exists($"{path}/video.{metadata["VideoExt"]}") ? $"{path}/video.{metadata["VideoExt"]}" : MapUtil.GetMapVideoPath(path); + videoBuffer = File.ReadAllBytes(videoPath); } var notes = DecodePHXMO(objectsBuffer); diff --git a/scripts/map/MapUtil.cs b/scripts/map/MapUtil.cs index 2b5d831d..dc24d6d3 100644 --- a/scripts/map/MapUtil.cs +++ b/scripts/map/MapUtil.cs @@ -1,6 +1,45 @@ -internal static class MapUtil +using System.Collections.Generic; +using System.IO; +using System.Linq; + +internal static class MapUtil { public static string MapsFolder => $"{Constants.USER_FOLDER}/maps"; public static string MapsCacheFolder => $"{Constants.USER_FOLDER}/cache/maps"; + + public static string? GetMapVideoPath(string mapPath) + { + DirectoryInfo dirInfo = new DirectoryInfo(mapPath); + + FileInfo[] filesInfo = dirInfo.GetFiles(); + Dictionary extPath = []; + + foreach (FileInfo file in filesInfo) + { + if (Path.GetFileNameWithoutExtension(file.FullName) == "video" && Constants.SUPPORTED_VIDEO_EXTENSIONS.Contains(file.Extension.TrimStart('.'))) + { + extPath.TryAdd(file.Extension.TrimStart('.'), file.FullName); + } + } + + for (int i = 0; i < Constants.SUPPORTED_VIDEO_EXTENSIONS.Length; i++) + { + if (extPath.TryGetValue(Constants.SUPPORTED_VIDEO_EXTENSIONS[i], out string fullPath)) { return fullPath; } + } + + return null; + } + + public static string? GetMapVideoPath(Map map, bool skipMetadata = false) + { + string mapPath = map.FolderPath; + + if (!string.IsNullOrEmpty(map.VideoExt) && !skipMetadata) + { + return Path.Join(map.FolderPath, $"video.{map.VideoExt}"); + } + + return GetMapVideoPath(mapPath); + } } diff --git a/scripts/ui/menu/play/ContextMenu.cs b/scripts/ui/menu/play/ContextMenu.cs index 053fc464..f1a007b2 100644 --- a/scripts/ui/menu/play/ContextMenu.cs +++ b/scripts/ui/menu/play/ContextMenu.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using Godot; public partial class ContextMenu : Panel @@ -74,12 +75,16 @@ public void SetupButtons() deleteButton = createButton("Delete Map", Color.Color8(255, 128, 128), Color.Color8(255, 196, 196), deleteMap, templateButton); - bool hasVideo = File.Exists(Path.Combine(MapUtil.MapsFolder, Map.Name, "video.mp4")); + bool hasVideo = File.Exists(MapUtil.GetMapVideoPath(Map)); videoButton = hasVideo ? createButton("Remove Video", Color.Color8(128, 128, 255), Color.Color8(196, 196, 255), removeVideo, templateButton) : createButton("Add Video", Color.Color8(128, 128, 255), Color.Color8(196, 196, 255), addVideo, templateButton); if (!dialogHasAction) { + List filters = [.. Constants.SUPPORTED_VIDEO_EXTENSIONS]; + filters = filters.Select(item => "*." + item).ToList(); + + videoDialog.Filters = filters.ToArray(); videoDialog.FileSelected += insertVideo; dialogHasAction = true; } @@ -181,6 +186,12 @@ private void deleteMap() private void insertVideo(string file) { + if (!Constants.SUPPORTED_VIDEO_EXTENSIONS.Contains(Path.GetExtension(file).TrimStart('.'))) + { + _ = ToastNotification.Notify("The file type you selected isn't supported or isn't a video.", 2); + return; + } + MapManager.InsertVideo(Map, file); } } \ No newline at end of file diff --git a/scripts/ui/menu/play/MapInfoContainer.cs b/scripts/ui/menu/play/MapInfoContainer.cs index 0dd9a6f9..7d50a80e 100644 --- a/scripts/ui/menu/play/MapInfoContainer.cs +++ b/scripts/ui/menu/play/MapInfoContainer.cs @@ -465,7 +465,7 @@ public void Setup(Map map) favoriteButton.TooltipText = map.Favorite ? "Unfavorite" : "Favorite"; favoriteButton.Icon = map.Favorite ? SkinManager.Instance.Skin.UnfavoriteButtonImage : SkinManager.Instance.Skin.FavoriteButtonImage; - if (File.Exists($"{MapUtil.MapsFolder}/{Map.Name}/video.mp4")) + if (File.Exists(MapUtil.GetMapVideoPath(Map))) { videoButton.TooltipText = Map.DisableVideo ? "Enable Video Background" : "Disable Video Background"; videoButton.Icon = Map.DisableVideo ? SkinManager.Instance.Skin.VideoDisabledButtonImage : SkinManager.Instance.Skin.VideoEnabledButtonImage; From aaf3db2ef3329888b7af229b8cd5f3de25738d86 Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:30:42 +0200 Subject: [PATCH 08/18] fix(video): reposition setting options recent commit removed visual region --- scripts/database/settings/SettingsProfile.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/database/settings/SettingsProfile.cs b/scripts/database/settings/SettingsProfile.cs index 790a0f2b..ff58f87b 100644 --- a/scripts/database/settings/SettingsProfile.cs +++ b/scripts/database/settings/SettingsProfile.cs @@ -735,7 +735,7 @@ public SettingsProfile() Id = "VideoBgVoidSpace", Title = "Use Void Space For Video Backgrounds", Description = "Use the void game space when video background is visible", - Section = SettingsSection.Visual, + Section = SettingsSection.Spaces, }; SpaceHitEffects = new(true) @@ -945,7 +945,7 @@ public SettingsProfile() Id = "VideoDim", Title = "Video BG Dim", Description = "Adjusts the video background dim", - Section = SettingsSection.Visual, + Section = SettingsSection.Spaces, Slider = new() { Step = 1, @@ -959,7 +959,7 @@ public SettingsProfile() Id = "VideoRenderScale", Title = "Video BG Render Scale", Description = "Adjusts the scale of the video background", - Section = SettingsSection.Visual, + Section = SettingsSection.Spaces, Slider = new() { Step = 1, @@ -973,7 +973,7 @@ public SettingsProfile() Id = "DisableVideoGlobal", Title = "Disable All Video Backgrounds", Description = "Don't show video backgrounds for any map", - Section = SettingsSection.Visual, + Section = SettingsSection.Spaces, }; SimpleHUD = new(false) From 0f2f7bed9fef9e3dde6c9edb697bf4b79f20952e Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Fri, 16 Oct 2026 22:44:31 +0200 Subject: [PATCH 09/18] fix(video): fixed broken restarting restarting from pause menu and replaying from result screen --- scripts/game/Runner.cs | 10 +++++----- scripts/game/ui/PauseMenu.cs | 2 +- scripts/scenes/Game.cs | 16 ++++++++++++++-- scripts/scenes/Results.cs | 5 ++++- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs index dcdeadb5..a2e2aca6 100644 --- a/scripts/game/Runner.cs +++ b/scripts/game/Runner.cs @@ -150,7 +150,7 @@ public override void _Process(double delta) } } - if (!Attempt.DisableVideo && Attempt.Map.VideoBuffer != null && settings.VideoDim < 100 && VideoPlayer.IsOpen()) + if (!Attempt.DisableVideo && Attempt.Map.VideoBuffer != null && settings.VideoDim < 100 && VideoPlayer != null && VideoPlayer.IsOpen()) { double offsetProgress = Attempt.Progress - settings.LocalOffset; double songEnd = SoundManager.Song.Stream.GetLength() * 1000; @@ -424,10 +424,10 @@ public void Pause(bool? pause = null) SoundManager.Song.PitchScale = (float)Speed; SoundManager.Song.StreamPaused = !Playing; - if (!Attempt.DisableVideo && Attempt.Map.VideoBuffer != null && Attempt.Progress >= 0) + if (!Attempt.DisableVideo && Attempt.Map.VideoBuffer != null && Attempt.Progress >= 0 && VideoPlayer != null && VideoPlayer.IsOpen()) { - if (Playing && VideoPlayer.IsOpen()) { VideoPlayer.Play(); } - else if (!Playing && VideoPlayer.IsOpen()) { VideoPlayer.Pause(); } + if (Playing) { VideoPlayer.Play(); } + else if (!Playing) { VideoPlayer.Pause(); } } if (Playing) @@ -759,7 +759,7 @@ private void syncSongPosition() private void syncVideoPosition() { - if (!Attempt.Map.DisableVideo && Attempt.Map.VideoBuffer != null && VideoPlayer.IsOpen()) + if (!Attempt.Map.DisableVideo && Attempt.Map.VideoBuffer != null && VideoPlayer != null && VideoPlayer.IsOpen()) { if (!VideoPlayer.IsPlaying && Playing) { diff --git a/scripts/game/ui/PauseMenu.cs b/scripts/game/ui/PauseMenu.cs index 0285ca3d..fe4fb407 100644 --- a/scripts/game/ui/PauseMenu.cs +++ b/scripts/game/ui/PauseMenu.cs @@ -48,7 +48,7 @@ public void ShowMenu(bool show = true, bool instant = false) SoundManager.Song.PitchScale = (float)attempt.Speed; SoundManager.Song.StreamPaused = !runner.Playing; - if (!attempt.DisableVideo && attempt.Map.VideoBuffer != null && attempt.Progress >= 0 && runner.VideoPlayer.IsOpen()) + if (!attempt.DisableVideo && attempt.Map.VideoBuffer != null && attempt.Progress >= 0 && runner.VideoPlayer != null && runner.VideoPlayer.IsOpen()) { if (runner.Playing) { runner.VideoPlayer.Play(); } else if (!runner.Playing) { runner.VideoPlayer.Pause(); } diff --git a/scripts/scenes/Game.cs b/scripts/scenes/Game.cs index 6e685ec9..09b0344a 100644 --- a/scripts/scenes/Game.cs +++ b/scripts/scenes/Game.cs @@ -225,6 +225,8 @@ public static void Play( var parsedMap = MapParser.Decode(map.FolderPath, Rhythia.AudioFilePath); + parsedMap.Id = map.Id; // add lost id + bool mapDisableVideo = DatabaseService.Connection.Get(map.Id).DisableVideo; bool globalVideoDisable = (bool)SettingsManager.Instance.Settings.DisableVideoGlobal; bool disableVideo = mapDisableVideo || globalVideoDisable; @@ -243,6 +245,12 @@ public void Restart() var oldAttempt = Attempt; var map = MapParser.Decode(oldAttempt.Map.FolderPath, Rhythia.AudioFilePath); + map.Id = oldAttempt.Map.Id; + + // bool mapDisableVideo = DatabaseService.Connection.Get(map.Id).DisableVideo; + // bool globalVideoDisable = (bool)SettingsManager.Instance.Settings.DisableVideoGlobal; + // bool disableVideo = mapDisableVideo || globalVideoDisable; + Attempt = new( map, oldAttempt.Speed, @@ -250,9 +258,13 @@ public void Restart() oldAttempt.CameraMode, oldAttempt.Modifiers, oldAttempt.Players, - oldAttempt.Replays + oldAttempt.Replays, + oldAttempt.DisableVideo ); - SceneManager.ReloadCurrentScene(); + bool useVoidSpace = + (bool)SettingsManager.Instance.Settings.VideoBgVoidSpace && !oldAttempt.DisableVideo && File.Exists(MapUtil.GetMapVideoPath(map)); + + SceneManager.Load("res://scenes/game.tscn", skipTransition: true, forceVoidBg: useVoidSpace); } } diff --git a/scripts/scenes/Results.cs b/scripts/scenes/Results.cs index eec5d68f..4615a254 100644 --- a/scripts/scenes/Results.cs +++ b/scripts/scenes/Results.cs @@ -112,9 +112,11 @@ public override void _Ready() if (FileAccess.FileExists(path)) { Replay replay = new(path); + Map replayMap = MapParser.Decode(replay.MapFilePath); + replayMap.Id = Game.Attempt.Map.Id; SoundManager.Song.Stop(); - Game.Play(MapParser.Decode(replay.MapFilePath), replay.Speed, replay.StartFrom, replay.CameraMode, replay.Modifiers, null, [replay]); + Game.Play(replayMap, replay.Speed, replay.StartFrom, replay.CameraMode, replay.Modifiers, null, [replay]); } }; } @@ -177,6 +179,7 @@ public static void Replay() Map map = MapParser.Decode(attempt.Map.FolderPath); map.Ephemeral = attempt.Map.Ephemeral; + map.Id = attempt.Map.Id; SoundManager.Song.Stop(); Game.Play(map, attempt.Speed, attempt.StartFrom, attempt.CameraMode, attempt.Modifiers); From 424d885ed0ff45b7798d93ac51061bdea9f83b25 Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Sun, 18 Oct 2026 20:42:35 +0200 Subject: [PATCH 10/18] QoL: map list selection cursor locks on right clicked map --- scripts/ui/menu/play/ContextMenu.cs | 3 +++ scripts/ui/menu/play/MapList.cs | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts/ui/menu/play/ContextMenu.cs b/scripts/ui/menu/play/ContextMenu.cs index f1a007b2..913d4fe1 100644 --- a/scripts/ui/menu/play/ContextMenu.cs +++ b/scripts/ui/menu/play/ContextMenu.cs @@ -96,6 +96,8 @@ public void SetupButtons() public static void Show(Vector2 pos, Map map, bool show = true) { + if (!show) { Hide(); } + Shown = show; Map = map; @@ -115,6 +117,7 @@ public static void Show(Vector2 pos, Map map, bool show = true) { Shown = false; Instance.Visible = false; + MapList.Instance?.ToggleSelectionCursor(false, false); } private static Button createButton(string text, Color fontColor, Color fontHoverColor, Action clickAction, Button template = null, bool makeVisible = true) diff --git a/scripts/ui/menu/play/MapList.cs b/scripts/ui/menu/play/MapList.cs index b219de94..3bbb97e8 100644 --- a/scripts/ui/menu/play/MapList.cs +++ b/scripts/ui/menu/play/MapList.cs @@ -64,6 +64,7 @@ public enum SortType public bool DragScroll = false; public bool MouseScroll = false; public bool DisplaySelectionCursor = false; + public bool SelectionCursorLockDisplay = false; public string SearchQuery = ""; public string AuthorQuery = ""; public Bindable Ascending = new(true); @@ -127,7 +128,7 @@ public override void _Ready() MouseExited += () => { - toggleSelectionCursor(false); + ToggleSelectionCursor(false); }; Resized += clear; SkinManager.Instance.Loaded += UpdateSkin; @@ -565,10 +566,10 @@ private MapButton setupButton(MapButton button) { if (hovered) { - hoveredButton = button; + if (!SelectionCursorLockDisplay) { hoveredButton = button; } if (Layout == ListLayout.List) { - toggleSelectionCursor(true); + ToggleSelectionCursor(true); } } @@ -593,6 +594,7 @@ private MapButton setupButton(MapButton button) if (mouseButton.ButtonIndex == MouseButton.Right) { ContextMenu.Show(mouseButton.GlobalPosition, button.Map); + ToggleSelectionCursor(true, true); GetViewport().SetInputAsHandled(); } } @@ -601,9 +603,11 @@ private MapButton setupButton(MapButton button) return button; } - private void toggleSelectionCursor(bool display) + public void ToggleSelectionCursor(bool display, bool? lockDisplay = null) { - if (DisplaySelectionCursor == display) + if (lockDisplay != null) { SelectionCursorLockDisplay = lockDisplay.Value; } + + if (SelectionCursorLockDisplay || DisplaySelectionCursor == display) { return; } From edacb628a0524d9b033392634cb27f49c6b0d035 Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Fri, 23 Oct 2026 17:42:42 +0200 Subject: [PATCH 11/18] fix: fixes for video-bg branch moving to map-browser --- prefabs/spaces/grid.tscn | 4 ++-- scripts/database/settings/SettingsProfile.cs | 8 ++++---- textures/throbber.png.import | 14 ++++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/prefabs/spaces/grid.tscn b/prefabs/spaces/grid.tscn index 3e8f3c95..bfe658d4 100644 --- a/prefabs/spaces/grid.tscn +++ b/prefabs/spaces/grid.tscn @@ -1,7 +1,7 @@ [gd_scene format=3 uid="uid://dtkff13bgd1u4"] [ext_resource type="Script" uid="uid://bxx72dwqlrh2o" path="res://scripts/spaces/Grid.cs" id="1_wac2s"] -[ext_resource type="Texture2D" uid="uid://cnqebcj2iorfr" path="res://textures/spaces/grid/grid_tile.png" id="2_83vuq"] +[ext_resource type="Texture2D" uid="uid://cnqebcj2iorfr" path="res://textures/spaces/grid/grid_tile.png" id="2_wac2s"] [sub_resource type="Environment" id="Environment_ge866"] ambient_light_source = 1 @@ -16,7 +16,7 @@ fog_depth_end = 60.0 [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_n0yhb"] shading_mode = 0 -albedo_texture = ExtResource("2_83vuq") +albedo_texture = ExtResource("2_wac2s") uv1_scale = Vector3(34, 34, 34) texture_filter = 5 billboard_keep_scale = true diff --git a/scripts/database/settings/SettingsProfile.cs b/scripts/database/settings/SettingsProfile.cs index ff58f87b..790a0f2b 100644 --- a/scripts/database/settings/SettingsProfile.cs +++ b/scripts/database/settings/SettingsProfile.cs @@ -735,7 +735,7 @@ public SettingsProfile() Id = "VideoBgVoidSpace", Title = "Use Void Space For Video Backgrounds", Description = "Use the void game space when video background is visible", - Section = SettingsSection.Spaces, + Section = SettingsSection.Visual, }; SpaceHitEffects = new(true) @@ -945,7 +945,7 @@ public SettingsProfile() Id = "VideoDim", Title = "Video BG Dim", Description = "Adjusts the video background dim", - Section = SettingsSection.Spaces, + Section = SettingsSection.Visual, Slider = new() { Step = 1, @@ -959,7 +959,7 @@ public SettingsProfile() Id = "VideoRenderScale", Title = "Video BG Render Scale", Description = "Adjusts the scale of the video background", - Section = SettingsSection.Spaces, + Section = SettingsSection.Visual, Slider = new() { Step = 1, @@ -973,7 +973,7 @@ public SettingsProfile() Id = "DisableVideoGlobal", Title = "Disable All Video Backgrounds", Description = "Don't show video backgrounds for any map", - Section = SettingsSection.Spaces, + Section = SettingsSection.Visual, }; SimpleHUD = new(false) diff --git a/textures/throbber.png.import b/textures/throbber.png.import index f34e29e8..e5eff5ab 100644 --- a/textures/throbber.png.import +++ b/textures/throbber.png.import @@ -3,19 +3,21 @@ importer="texture" type="CompressedTexture2D" uid="uid://c8l35lu3xsl07" -path="res://.godot/imported/throbber.png-a38f9a9185bc57fb76987fda3b847e30.ctex" +path.s3tc="res://.godot/imported/throbber.png-a38f9a9185bc57fb76987fda3b847e30.s3tc.ctex" +path.etc2="res://.godot/imported/throbber.png-a38f9a9185bc57fb76987fda3b847e30.etc2.ctex" metadata={ -"vram_texture": false +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true } [deps] source_file="res://textures/throbber.png" -dest_files=["res://.godot/imported/throbber.png-a38f9a9185bc57fb76987fda3b847e30.ctex"] +dest_files=["res://.godot/imported/throbber.png-a38f9a9185bc57fb76987fda3b847e30.s3tc.ctex", "res://.godot/imported/throbber.png-a38f9a9185bc57fb76987fda3b847e30.etc2.ctex"] [params] -compress/mode=0 +compress/mode=2 compress/high_quality=false compress/lossy_quality=0.7 compress/uastc_level=0 @@ -23,7 +25,7 @@ compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 -mipmaps/generate=false +mipmaps/generate=true mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" @@ -37,4 +39,4 @@ process/normal_map_invert_y=false process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 -detect_3d/compress_to=1 +detect_3d/compress_to=0 From dbfa8afb1ccf4bf4ba27e9a852de5b85f2ae1ef2 Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:48:47 +0200 Subject: [PATCH 12/18] fix: avoid adding VideoExt to db --- scripts/map/Map.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/map/Map.cs b/scripts/map/Map.cs index 817c06b2..286b00de 100644 --- a/scripts/map/Map.cs +++ b/scripts/map/Map.cs @@ -93,6 +93,7 @@ public Texture2D Cover public bool DisableVideo { get; set; } + [Ignore] public string VideoExt { get; set; } = string.Empty; private Note[] notes; From 53fb6259b1b0ece996d2832bbdba8b7f2bcf3fb0 Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:49:07 +0200 Subject: [PATCH 13/18] fix: refresh MapInfoContainer on video add/remove --- scripts/map/MapManager.cs | 2 + scripts/scenes/Game.cs | 20 ++++++--- scripts/scenes/Results.cs | 2 + scripts/ui/menu/play/MapInfoContainer.cs | 52 +++++++++++++----------- scripts/ui/menu/play/ScorePanel.cs | 9 ++++ 5 files changed, 56 insertions(+), 29 deletions(-) diff --git a/scripts/map/MapManager.cs b/scripts/map/MapManager.cs index b00d1b5c..90ffbfb4 100644 --- a/scripts/map/MapManager.cs +++ b/scripts/map/MapManager.cs @@ -73,6 +73,7 @@ public static void InsertVideo(Map map, string path) MapParser.Encode(map); map.MetadataObjectHash = MapCache.GetMd5Checksum(map.FolderPath); Update(map); + MapInfo.Instance.InfoContainer.Refresh(true, true); } public static void RemoveVideo(Map map) @@ -94,6 +95,7 @@ public static void RemoveVideo(Map map) MapParser.Encode(map); map.MetadataObjectHash = MapCache.GetMd5Checksum(map.FolderPath); Update(map); + MapInfo.Instance.InfoContainer.Refresh(true, true); } public static void Delete(Map map, bool quiet = false) diff --git a/scripts/scenes/Game.cs b/scripts/scenes/Game.cs index 09b0344a..92a6bdc5 100644 --- a/scripts/scenes/Game.cs +++ b/scripts/scenes/Game.cs @@ -1,4 +1,8 @@ +using System; using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; using Godot; public partial class Game : BaseScene @@ -224,16 +228,20 @@ public static void Play( StartQueued = true; var parsedMap = MapParser.Decode(map.FolderPath, Rhythia.AudioFilePath); + parsedMap.Id = map.Id; - parsedMap.Id = map.Id; // add lost id + string hash = Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(Path.Combine(map.FolderPath, "objects.phxmo")))); + Map mapRow = DatabaseService.Connection.Table().FirstOrDefault(row => row.ObjectHash == hash); - bool mapDisableVideo = DatabaseService.Connection.Get(map.Id).DisableVideo; + bool mapDisableVideo = mapRow != null && mapRow.DisableVideo; bool globalVideoDisable = (bool)SettingsManager.Instance.Settings.DisableVideoGlobal; bool disableVideo = mapDisableVideo || globalVideoDisable; Attempt = new(parsedMap, speed, startFrom, cameraMode, mods, players, replays, disableVideo: disableVideo); - bool useVoidSpace = (bool)SettingsManager.Instance.Settings.VideoBgVoidSpace && !disableVideo; + var metadata = (Godot.Collections.Dictionary)Json.ParseString(File.ReadAllText($"{map.FolderPath}/metadata.json")); + bool hasVideo = (bool)metadata["HasVideo"]; + bool useVoidSpace = (bool)SettingsManager.Instance.Settings.VideoBgVoidSpace && !disableVideo && hasVideo; SceneManager.Load("res://scenes/game.tscn", forceVoidBg: useVoidSpace); } @@ -245,9 +253,11 @@ public void Restart() var oldAttempt = Attempt; var map = MapParser.Decode(oldAttempt.Map.FolderPath, Rhythia.AudioFilePath); - map.Id = oldAttempt.Map.Id; - // bool mapDisableVideo = DatabaseService.Connection.Get(map.Id).DisableVideo; + // string hash = Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(Path.Combine(map.FolderPath, "objects.phxmo")))); + // Map mapRow = DatabaseService.Connection.Table().FirstOrDefault(row => row.ObjectHash == hash); + + // bool mapDisableVideo = mapRow != null && mapRow.DisableVideo; // bool globalVideoDisable = (bool)SettingsManager.Instance.Settings.DisableVideoGlobal; // bool disableVideo = mapDisableVideo || globalVideoDisable; diff --git a/scripts/scenes/Results.cs b/scripts/scenes/Results.cs index 4615a254..46a6cc14 100644 --- a/scripts/scenes/Results.cs +++ b/scripts/scenes/Results.cs @@ -114,6 +114,7 @@ public override void _Ready() Replay replay = new(path); Map replayMap = MapParser.Decode(replay.MapFilePath); replayMap.Id = Game.Attempt.Map.Id; + replayMap.DisableVideo = Game.Attempt.Map.DisableVideo; SoundManager.Song.Stop(); Game.Play(replayMap, replay.Speed, replay.StartFrom, replay.CameraMode, replay.Modifiers, null, [replay]); @@ -180,6 +181,7 @@ public static void Replay() Map map = MapParser.Decode(attempt.Map.FolderPath); map.Ephemeral = attempt.Map.Ephemeral; map.Id = attempt.Map.Id; + map.DisableVideo = attempt.Map.DisableVideo; SoundManager.Song.Stop(); Game.Play(map, attempt.Speed, attempt.StartFrom, attempt.CameraMode, attempt.Modifiers); diff --git a/scripts/ui/menu/play/MapInfoContainer.cs b/scripts/ui/menu/play/MapInfoContainer.cs index 7d50a80e..13b56cf3 100644 --- a/scripts/ui/menu/play/MapInfoContainer.cs +++ b/scripts/ui/menu/play/MapInfoContainer.cs @@ -403,12 +403,12 @@ public override void _UnhandledInput(InputEvent @event) } } - public void Setup(Map map) + public void Setup(Map map, bool force = false, bool skipAnim = false) { if (map == null) return; - if (Name == map.Name) + if (Name == map.Name && !force) { return; } @@ -418,26 +418,28 @@ public void Setup(Map map) // Transition - Position = Vector2.Zero; - Scale = Vector2.One; - - info.OffsetLeft -= 64; - info.OffsetRight -= 64; - actions.OffsetLeft -= 80; - actions.OffsetRight -= 80; - leaderboard.OffsetLeft -= 96; - leaderboard.OffsetRight -= 96; - - Tween inTween = CreateTween().SetEase(Tween.EaseType.Out).SetParallel(); - inTween.SetTrans(Tween.TransitionType.Quint).TweenProperty(info, "offset_left", 0, 0.5); - inTween.TweenProperty(info, "offset_right", 0, 0.5); - inTween.SetTrans(Tween.TransitionType.Quart).TweenProperty(actions, "offset_left", 0, 0.6); - inTween.TweenProperty(actions, "offset_right", 0, 0.6); - inTween.SetTrans(Tween.TransitionType.Cubic).TweenProperty(leaderboard, "offset_left", 0, 0.7); - inTween.TweenProperty(leaderboard, "offset_right", 0, 0.7); - - OffsetRight = 0; - Position += Vector2.Left * 64; + if (!skipAnim) + { + Position = Vector2.Zero; + Scale = Vector2.One; + + info.OffsetLeft -= 64; + info.OffsetRight -= 64; + actions.OffsetLeft -= 80; + actions.OffsetRight -= 80; + leaderboard.OffsetLeft -= 96; + leaderboard.OffsetRight -= 96; + + Tween inTween = CreateTween().SetEase(Tween.EaseType.Out).SetParallel(); + inTween.SetTrans(Tween.TransitionType.Quint).TweenProperty(info, "offset_left", 0, 0.5); + inTween.TweenProperty(info, "offset_right", 0, 0.5); + inTween.SetTrans(Tween.TransitionType.Quart).TweenProperty(actions, "offset_left", 0, 0.6); + inTween.TweenProperty(actions, "offset_right", 0, 0.6); + inTween.SetTrans(Tween.TransitionType.Cubic).TweenProperty(leaderboard, "offset_left", 0, 0.7); + inTween.TweenProperty(leaderboard, "offset_right", 0, 0.7); + OffsetRight = 0; + Position += Vector2.Left * 64; + } // Info @@ -467,6 +469,7 @@ public void Setup(Map map) if (File.Exists(MapUtil.GetMapVideoPath(Map))) { + videoButton.Disabled = false; videoButton.TooltipText = Map.DisableVideo ? "Enable Video Background" : "Disable Video Background"; videoButton.Icon = Map.DisableVideo ? SkinManager.Instance.Skin.VideoDisabledButtonImage : SkinManager.Instance.Skin.VideoEnabledButtonImage; } @@ -490,9 +493,9 @@ public void Setup(Map map) preview.Setup(map, true); } - public void Refresh() + public void Refresh(bool force = false, bool skipAnim = false) { - Setup(Map); + Setup(Map, force, skipAnim); LoadLeaderboard(Map); } @@ -519,6 +522,7 @@ public void LoadLeaderboard(Map map) { ScorePanel panel = leaderboardScoreTemplate.Instantiate(); + panel.Map = map; lbContainer.AddChild(panel); panel.Setup(Leaderboard.Scores[i]); panel.GetNode("Background").Color = Color.Color8(255, 255, 255, (byte)(i % 2 * 8)); diff --git a/scripts/ui/menu/play/ScorePanel.cs b/scripts/ui/menu/play/ScorePanel.cs index e32b88c4..38b82177 100644 --- a/scripts/ui/menu/play/ScorePanel.cs +++ b/scripts/ui/menu/play/ScorePanel.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; using System.IO; +using System.Security.Cryptography; +using System.Linq; using Godot; public partial class ScorePanel : Panel { + public Map Map; + public Leaderboard.Score Score; public Button Button; @@ -90,6 +94,11 @@ public void Replay() Replay replay = new(replayPath); var map = MapParser.Decode(replay.MapFilePath); + string hash = Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(Path.Combine(map.FolderPath, "objects.phxmo")))); + Map mapRow = DatabaseService.Connection.Table().FirstOrDefault(row => row.ObjectHash == hash); + + map.DisableVideo = mapRow != null && mapRow.DisableVideo; + Game.Play(map, replay.Speed, replay.StartFrom, replay.CameraMode, replay.Modifiers, null, [replay]); } } From ea618ea07ad81a75df271e2a95d2ad16882deecb Mon Sep 17 00:00:00 2001 From: oldcrate <139006122+oldcrate@users.noreply.github.com> Date: Sun, 25 Oct 2026 09:06:30 +0100 Subject: [PATCH 14/18] feat: added video icon in map button --- prefabs/map_button_square.tscn | 61 +++++++++++++++++++++---------- prefabs/map_button_wide.tscn | 55 +++++++++++++++++++--------- scripts/ui/menu/play/MapButton.cs | 15 +++++++- 3 files changed, 91 insertions(+), 40 deletions(-) diff --git a/prefabs/map_button_square.tscn b/prefabs/map_button_square.tscn index 9146a83f..edb3b482 100644 --- a/prefabs/map_button_square.tscn +++ b/prefabs/map_button_square.tscn @@ -5,6 +5,7 @@ [ext_resource type="Texture2D" uid="uid://cacq7g5ijo7sy" path="res://user/skins/default/ui/play/favorite.png" id="2_ywqiq"] [ext_resource type="Texture2D" uid="uid://bp468b5qqj4l1" path="res://user/skins/default/ui/play/grid_cover_background.png" id="3_ii2dr"] [ext_resource type="Theme" uid="uid://dhuhu3mmaew1a" path="res://themes/theme.tres" id="3_xysv8"] +[ext_resource type="Texture2D" uid="uid://degvlg4p0if70" path="res://user/skins/default/ui/buttons/add_video.png" id="6_ii2dr"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ywqiq"] bg_color = Color(1, 1, 1, 1) @@ -115,23 +116,6 @@ grow_horizontal = 2 grow_vertical = 2 expand_mode = 1 -[node name="Favorited" type="TextureRect" parent="Holder" unique_id=2076074980] -visible = false -texture_filter = 6 -custom_minimum_size = Vector2(16, 16) -layout_mode = 1 -anchors_preset = -1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -18.0 -offset_top = 2.0 -offset_right = -2.0 -offset_bottom = 18.0 -grow_horizontal = 0 -pivot_offset = Vector2(8, 8) -texture = ExtResource("2_ywqiq") -expand_mode = 1 - [node name="Separator2" type="ColorRect" parent="Holder" unique_id=972899073] layout_mode = 1 anchors_preset = -1 @@ -189,16 +173,15 @@ anchors_preset = -1 anchor_top = 0.657 anchor_right = 0.721 anchor_bottom = 0.88900006 -offset_left = 39.0 +offset_left = 15.0 offset_top = 3.0199966 -offset_right = 0.05999756 +offset_right = -23.940002 offset_bottom = -8.460007 grow_horizontal = 2 grow_vertical = 0 theme = ExtResource("3_xysv8") text = "0:00" label_settings = SubResource("LabelSettings_hc3kw") -horizontal_alignment = 1 vertical_alignment = 1 autowrap_mode = 3 clip_text = true @@ -249,3 +232,41 @@ offset_right = 15.703194 offset_bottom = -6.6200027 rotation = 1.5707964 color = Color(1, 1, 1, 0.23529412) + +[node name="IconContainer" type="HBoxContainer" parent="Holder" unique_id=2048061899] +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.93600005 +anchor_top = 0.693 +anchor_right = 0.93600005 +anchor_bottom = 0.693 +offset_left = -34.04001 +offset_top = -0.020004272 +offset_right = -0.040008545 +offset_bottom = 15.979996 +grow_horizontal = 0 +theme_override_constants/separation = 2 +alignment = 2 + +[node name="Video" type="TextureRect" parent="Holder/IconContainer" unique_id=542011967] +visible = false +texture_filter = 6 +custom_minimum_size = Vector2(16, 16) +layout_mode = 2 +texture = ExtResource("6_ii2dr") +expand_mode = 1 + +[node name="FavoriteParent" type="Control" parent="Holder/IconContainer" unique_id=1380362991] +visible = false +custom_minimum_size = Vector2(16, 16) +layout_mode = 2 + +[node name="Favorited" type="TextureRect" parent="Holder/IconContainer/FavoriteParent" unique_id=2116415263] +texture_filter = 6 +custom_minimum_size = Vector2(16, 16) +layout_mode = 0 +offset_right = 16.0 +offset_bottom = 16.0 +pivot_offset_ratio = Vector2(0.5, 0.5) +texture = ExtResource("2_ywqiq") +expand_mode = 1 diff --git a/prefabs/map_button_wide.tscn b/prefabs/map_button_wide.tscn index f5a57e60..0eb70e29 100644 --- a/prefabs/map_button_wide.tscn +++ b/prefabs/map_button_wide.tscn @@ -5,6 +5,7 @@ [ext_resource type="Shader" uid="uid://b38n4irr1f443" path="res://user/skins/default/ui/play/map_button_cover.gdshader" id="3_d10q8"] [ext_resource type="Texture2D" uid="uid://cacq7g5ijo7sy" path="res://user/skins/default/ui/play/favorite.png" id="4_ew23d"] [ext_resource type="Theme" uid="uid://dhuhu3mmaew1a" path="res://themes/theme.tres" id="5_mpifo"] +[ext_resource type="Texture2D" uid="uid://degvlg4p0if70" path="res://user/skins/default/ui/buttons/add_video.png" id="6_84wsl"] [ext_resource type="Shader" uid="uid://d0g8fej0676c" path="res://scripts/shaders/linear_alpha_gradient.gdshader" id="6_frxlp"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_4823j"] @@ -49,8 +50,8 @@ shader = ExtResource("6_frxlp") [node name="MapButtonWide" type="Control" unique_id=807495564] self_modulate = Color(0, 0, 0, 0.8784314) -clip_contents = true custom_minimum_size = Vector2(0, 90) +clip_contents = true layout_mode = 3 anchors_preset = 10 anchor_right = 1.0 @@ -99,23 +100,6 @@ grow_vertical = 2 expand_mode = 2 stretch_mode = 6 -[node name="Favorited" type="TextureRect" parent="Holder" unique_id=922682128] -visible = false -texture_filter = 6 -custom_minimum_size = Vector2(24, 24) -layout_mode = 1 -anchors_preset = -1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -32.0 -offset_top = 8.0 -offset_right = -8.0 -offset_bottom = 32.0 -grow_horizontal = 0 -pivot_offset = Vector2(12, 12) -texture = ExtResource("4_ew23d") -expand_mode = 1 - [node name="Title" type="Label" parent="Holder" unique_id=704763863] layout_mode = 1 anchors_preset = -1 @@ -177,3 +161,38 @@ focus_mode = 0 mouse_filter = 1 mouse_default_cursor_shape = 2 flat = true + +[node name="IconContainer" type="HBoxContainer" parent="Holder" unique_id=1116304380] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -32.0 +offset_top = 8.0 +offset_right = -8.0 +offset_bottom = 32.0 +grow_horizontal = 0 +theme_override_constants/separation = 8 + +[node name="Video" type="TextureRect" parent="Holder/IconContainer" unique_id=1926918097] +visible = false +texture_filter = 6 +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 +texture = ExtResource("6_84wsl") +expand_mode = 1 + +[node name="FavoriteParent" type="Control" parent="Holder/IconContainer" unique_id=1211224476] +visible = false +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 + +[node name="Favorited" type="TextureRect" parent="Holder/IconContainer/FavoriteParent" unique_id=922682128] +texture_filter = 6 +custom_minimum_size = Vector2(24, 24) +layout_mode = 0 +offset_right = 24.0 +offset_bottom = 24.0 +pivot_offset_ratio = Vector2(0.5, 0.5) +texture = ExtResource("4_ew23d") +expand_mode = 1 diff --git a/scripts/ui/menu/play/MapButton.cs b/scripts/ui/menu/play/MapButton.cs index 5d34278c..d4b0419e 100644 --- a/scripts/ui/menu/play/MapButton.cs +++ b/scripts/ui/menu/play/MapButton.cs @@ -50,6 +50,8 @@ public partial class MapButton : Control, ISkinnable public Label Title; public TextureRect Cover; public TextureRect Favorited; + public Control FavoriteParent; + public TextureRect VideoIcon; public Button Button; public ShaderMaterial OutlineShader; @@ -61,8 +63,11 @@ public override void _Ready() Button = Holder.GetNode