diff --git a/MuzikaGromche/Plugin.cs b/MuzikaGromche/Plugin.cs index cf0056f..02f8e19 100644 --- a/MuzikaGromche/Plugin.cs +++ b/MuzikaGromche/Plugin.cs @@ -30,6 +30,7 @@ namespace MuzikaGromche Language = Language.RUSSIAN, WindUpTimer = 46.3f, Bars = 8, + Palette = Palette.Parse(["#B300FF", "#FFF100", "#00FF51", "#474747", "#FF00B3", "#0070FF"]), }, new Track { @@ -37,6 +38,7 @@ namespace MuzikaGromche Language = Language.RUSSIAN, WindUpTimer = 39f, Bars = 8, + Palette = Palette.Parse(["#FF7F00", "#FFB600", "#FFED00", "#00D1FF", "#6696FB", "#704DF8"]), }, new Track { @@ -44,6 +46,7 @@ namespace MuzikaGromche Language = Language.RUSSIAN, WindUpTimer = 40.7f, Bars = 8, + Palette = Palette.Parse(["#217F87", "#BAFF00", "#73BE25", "#78AB4E", "#FFFF00"]), }, new Track { @@ -51,6 +54,7 @@ namespace MuzikaGromche Language = Language.ENGLISH, WindUpTimer = 34.5f, Bars = 8, + Palette = Palette.Parse(["#A3A3A3", "#BE3D39", "#5CBC69", "#BE3D39", "#BABC5C", "#BE3D39", "#5C96BC", "#BE3D39"]), }, new Track { @@ -58,6 +62,7 @@ namespace MuzikaGromche Language = Language.RUSSIAN, WindUpTimer = 43.2f, Bars = 6, + Palette = Palette.Parse(["#42367E", "#FF9400", "#932A04", "#FF9400", "#932A04", "#42367E", "#FF9400", "#932A04"]), }, new Track { @@ -65,6 +70,7 @@ namespace MuzikaGromche Language = Language.RUSSIAN, WindUpTimer = 37f, Bars = 10, + Palette = Palette.Parse(["#5986FE", "#FEFEDC", "#FF4FDF", "#FEFEDC", "#FFAA23", "#FEFEDC", "#F95A5A", "#FEFEDC"]), } ]; @@ -133,6 +139,24 @@ namespace MuzikaGromche public static readonly Language RUSSIAN = new("RU", "Russian"); } + public record Palette(Color[] Colors) + { + public static Palette DEFAULT = new([Color.magenta, Color.cyan, Color.green, Color.yellow]); + + public static Palette Parse(string[] hexColors) + { + Color[] colors = new Color[hexColors.Length]; + for (int i = 0; i < hexColors.Length; i++) + { + if (!ColorUtility.TryParseHtmlString(hexColors[i], out colors[i])) + { + throw new ArgumentException($"Unable to parse color #{i}: {hexColors}"); + } + } + return new Palette(colors); + } + } + public class Track { public string Name; @@ -214,13 +238,17 @@ namespace MuzikaGromche return beat; } - static readonly List Colors = [Color.magenta, Color.cyan, Color.green, Color.yellow]; + public Palette _Palette = Palette.DEFAULT; + public Palette Palette + { + get => Config.PaletteOverride ?? _Palette; + set => _Palette = value; + } public Color ColorAtBeat(float beat) { int beatIndex = Mod.Positive(Mathf.FloorToInt(beat), Beats); - - return Mod.Index(Colors, beatIndex); + return Mod.Index(Palette.Colors, beatIndex); } } @@ -352,6 +380,8 @@ namespace MuzikaGromche public static bool ShouldSkipWindingPhase { get; private set; } = false; + public static Palette PaletteOverride { get; private set; } = null; + public Config(ConfigFile configFile) : base(PluginInfo.PLUGIN_GUID) { AudioOffset = configFile.Bind("General", "Audio Offset", 0f, new ConfigDescription( @@ -361,6 +391,7 @@ namespace MuzikaGromche #if DEBUG SetupEntriesToSkipWinding(configFile); + SetupEntriesForPaletteOverride(configFile); #endif var chanceRange = new AcceptableValueRange(0, 100); @@ -476,6 +507,78 @@ namespace MuzikaGromche ShouldSkipWindingPhase = syncedEntry.Value; } } + + private void SetupEntriesForPaletteOverride(ConfigFile configFile) + { + const string section = "Palette"; + const int maxCustomPaletteSize = 8; + // Declare and initialize early to avoid "Use of unassigned local variable" + SyncedEntry customPaletteSizeSyncedEntry = null; + var customPaletteSyncedEntries = new SyncedEntry[maxCustomPaletteSize]; + + var loadButton = new GenericButtonConfigItem(section, "Load Palette from the Current Track", + "Override custom palette with the built-in palette of the current track.", "Load", load); + loadButton.ButtonOptions.CanModifyCallback = CanModifyIfHost; + LethalConfigManager.AddConfigItem(loadButton); + + customPaletteSizeSyncedEntry = configFile.BindSyncedEntry(section, "Palette Size", 0, new ConfigDescription( + "Number of colors in the custom palette.\n\nIf set to non-zero, custom palette overrides track's own built-in palette.", + new AcceptableValueRange(0, maxCustomPaletteSize))); + LethalConfigManager.AddConfigItem(new IntSliderConfigItem(customPaletteSizeSyncedEntry.Entry, new IntSliderOptions + { + RequiresRestart = false, + CanModifyCallback = CanModifyIfHost, + })); + CSyncHackAddSyncedEntry(customPaletteSizeSyncedEntry); + customPaletteSizeSyncedEntry.Changed += (sender, args) => apply(); + customPaletteSizeSyncedEntry.SyncHostToLocal(); + + for (int i = 0; i < maxCustomPaletteSize; i++) + { + string entryName = $"Custom Color {i + 1}"; + var customColorSyncedEntry = configFile.BindSyncedEntry(section, entryName, "#FFFFFF", "Choose color for the custom palette"); + customPaletteSyncedEntries[i] = customColorSyncedEntry; + LethalConfigManager.AddConfigItem(new HexColorInputFieldConfigItem(customColorSyncedEntry.Entry, new HexColorInputFieldOptions + { + RequiresRestart = false, + CanModifyCallback = CanModifyIfHost, + })); + CSyncHackAddSyncedEntry(customColorSyncedEntry); + customColorSyncedEntry.Changed += (sender, args) => apply(); + customColorSyncedEntry.SyncHostToLocal(); + } + + apply(); + + void load() + { + var palette = Plugin.CurrentTrack?._Palette ?? Palette.DEFAULT; + var colors = palette.Colors; + var count = Math.Min(colors.Count(), maxCustomPaletteSize); + + customPaletteSizeSyncedEntry.LocalValue = colors.Count(); + for (int i = 0; i < maxCustomPaletteSize; i++) + { + var color = i < count ? colors[i] : Color.white; + string colorHex = $"#{ColorUtility.ToHtmlStringRGB(color)}"; + customPaletteSyncedEntries[i].LocalValue = colorHex; + } + } + + void apply() + { + int size = customPaletteSizeSyncedEntry.Value; + if (size == 0 || size > maxCustomPaletteSize) + { + PaletteOverride = null; + } + else + { + var colors = customPaletteSyncedEntries.Select(entry => entry.Value).Take(size).ToArray(); + PaletteOverride = Palette.Parse(colors); + } + } + } } // farAudio is during windup, Start overrides popGoesTheWeaselTheme