Add support for per-track palettes, and debug-only synced palette override

Palettes are contributed by @REALJUSTNOTHING
This commit is contained in:
ivan tkachenko 2025-07-16 02:37:37 +03:00
parent 34d8da1562
commit ad77530b6d
1 changed files with 106 additions and 3 deletions

View File

@ -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<Color> 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<int>(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<int> customPaletteSizeSyncedEntry = null;
var customPaletteSyncedEntries = new SyncedEntry<string>[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<int>(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