From 3bf066f38c0002d3b052b8f23d5e3eecda12629f Mon Sep 17 00:00:00 2001 From: Az <56699741+AlchlcDvl@users.noreply.github.com> Date: Tue, 29 Sep 2026 09:53:18 -0400 Subject: [PATCH] add variable color system + color spaces --- .editorconfig | 3 + MiraAPI/Colors/ColorSpaces/CmykColor.cs | 103 +++++++++++++++++ MiraAPI/Colors/ColorSpaces/HsbColor.cs | 114 +++++++++++++++++++ MiraAPI/Colors/ColorSpaces/HslColor.cs | 117 ++++++++++++++++++++ MiraAPI/Colors/CustomColor.cs | 2 +- MiraAPI/Colors/PaletteManager.cs | 56 ++++++++++ MiraAPI/Colors/VariableColor.cs | 69 ++++++++++++ MiraAPI/Colors/VariableColorBehaviour.cs | 46 ++++++++ MiraAPI/MiraApiSettings.cs | 6 + MiraAPI/Patches/Hud/VariableColorPatches.cs | 98 ++++++++++++++++ MiraAPI/PluginLoading/MiraPluginManager.cs | 5 +- MiraAPI/Resources/Locale/en_US.xml | 1 + 12 files changed, 618 insertions(+), 2 deletions(-) create mode 100644 MiraAPI/Colors/ColorSpaces/CmykColor.cs create mode 100644 MiraAPI/Colors/ColorSpaces/HsbColor.cs create mode 100644 MiraAPI/Colors/ColorSpaces/HslColor.cs create mode 100644 MiraAPI/Colors/VariableColor.cs create mode 100644 MiraAPI/Colors/VariableColorBehaviour.cs create mode 100644 MiraAPI/Patches/Hud/VariableColorPatches.cs diff --git a/.editorconfig b/.editorconfig index af9feb4..c0529a8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -86,3 +86,6 @@ dotnet_diagnostic.SA1133.severity = none # IDE0001: Simplify Names dotnet_diagnostic.IDE0001.severity = warning + +# IDE0079: Remove unnecessary suppression +dotnet_diagnostic.IDE0079.severity = none diff --git a/MiraAPI/Colors/ColorSpaces/CmykColor.cs b/MiraAPI/Colors/ColorSpaces/CmykColor.cs new file mode 100644 index 0000000..569e848 --- /dev/null +++ b/MiraAPI/Colors/ColorSpaces/CmykColor.cs @@ -0,0 +1,103 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using UnityEngine; + +namespace MiraAPI.Colors.ColorSpaces; + +/// +/// A struct that denotes the CMYK (Cyan, Magenta, Yellow, Key) color space. +/// +/// The cyan component. +/// The magenta component. +/// The yellow component. +/// The key component. +/// The alpha component. +[Serializable] +[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Unity parity.")] +public record struct CmykColor(float c, float m, float y, float k, float a = 1f) +{ + /// + /// Implicitly converts a to a Unity . + /// + /// The color being converted. + public static implicit operator Color(CmykColor c) + { + return ToColor(c); + } + + /// + /// Implicitly converts a Unity to a . + /// + /// The color being converted. + public static implicit operator CmykColor(Color c) + { + return FromColor(c); + } + + /// + /// Converts a to a standard Unity RGB . + /// + /// The CMYK color to convert. + /// A Unity representing the same visual color. + public static Color ToColor(CmykColor cmyk) + { + var r = (1f - cmyk.c) * (1f - cmyk.k); + var g = (1f - cmyk.m) * (1f - cmyk.k); + var b = (1f - cmyk.y) * (1f - cmyk.k); + + return new Color(Mathf.Clamp01(r), Mathf.Clamp01(g), Mathf.Clamp01(b), cmyk.a); + } + + /// + /// Converts a standard Unity RGB to a . + /// + /// The Unity Color to convert. + /// A representing the same visual color. + public static CmykColor FromColor(Color color) + { + var r = color.r; + var g = color.g; + var b = color.b; + + var k = 1f - Mathf.Max(r, g, b); + + if (k >= 1f) + return new CmykColor(0f, 0f, 0f, 1f, color.a); + + var c = (1f - r - k) / (1f - k); + var m = (1f - g - k) / (1f - k); + var y = (1f - b - k) / (1f - k); + + return new CmykColor(Mathf.Clamp01(c), Mathf.Clamp01(m), Mathf.Clamp01(y), Mathf.Clamp01(k), color.a); + } + + /// + /// Linearly interpolates between two values. + /// + /// The starting color. + /// The ending color. + /// The interpolation value between the two colors, clamped to a 0 to 1 range. + /// The interpolated . + public static CmykColor Lerp(CmykColor a, CmykColor b, float t) + { + t = Mathf.Clamp01(t); + return LerpUnclamped(a, b, t); + } + + /// + /// Linearly interpolates between two values without clamping the interpolant. + /// + /// The starting color. + /// The ending color. + /// The interpolation value between the two colors. + /// The interpolated . + public static CmykColor LerpUnclamped(CmykColor a, CmykColor b, float t) + { + return new CmykColor( + Mathf.LerpUnclamped(a.c, b.c, t), + Mathf.LerpUnclamped(a.m, b.m, t), + Mathf.LerpUnclamped(a.y, b.y, t), + Mathf.LerpUnclamped(a.k, b.k, t), + Mathf.LerpUnclamped(a.a, b.a, t)); + } +} diff --git a/MiraAPI/Colors/ColorSpaces/HsbColor.cs b/MiraAPI/Colors/ColorSpaces/HsbColor.cs new file mode 100644 index 0000000..4dec19a --- /dev/null +++ b/MiraAPI/Colors/ColorSpaces/HsbColor.cs @@ -0,0 +1,114 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using UnityEngine; + +namespace MiraAPI.Colors.ColorSpaces; + +/// +/// A struct that denotes the HSB (Hue, Saturation, Brightness) color space. +/// +/// The hue component. +/// The saturation component. +/// The brightness component. +/// The alpha component. +[Serializable] +[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Unity parity.")] +public record struct HsbColor(float h, float s, float b, float a = 1f) +{ + /// + /// Implicitly converts an to a Unity . + /// + /// The color being converted. + public static implicit operator Color(HsbColor c) + { + return ToColor(c); + } + + /// + /// Implicitly converts a Unity to an . + /// + /// The color being converted. + public static implicit operator HsbColor(Color c) + { + return FromColor(c); + } + + /// + /// Converts an to a standard Unity RGB . + /// + /// The HSB color to convert. + /// A Unity representing the same visual color. + public static Color ToColor(HsbColor hsb) + { + var r = hsb.b; + var g = hsb.b; + var b = hsb.b; + + if (hsb.s <= Mathf.Epsilon) + return new Color(Mathf.Clamp01(r), Mathf.Clamp01(g), Mathf.Clamp01(b), hsb.a); + + var max = hsb.b; + var dif = hsb.b * hsb.s; + var min = hsb.b - dif; + + var h = hsb.h * 360f; + + (r, g, b) = h switch + { + < 60f => (max, h * dif / 60f + min, min), + < 120f => (-(h - 120f) * dif / 60f + min, max, min), + < 180f => (min, max, (h - 120f) * dif / 60f + min), + < 240f => (min, -(h - 240f) * dif / 60f + min, max), + < 300f => ((h - 240f) * dif / 60f + min, min, max), + <= 360f => (max, min, -(h - 360f) * dif / 60f + min), + _ => (0f, 0f, 0f), + }; + + return new Color(Mathf.Clamp01(r), Mathf.Clamp01(g), Mathf.Clamp01(b), hsb.a); + } + + /// + /// Converts a standard Unity RGB to an . + /// + /// The Unity Color to convert. + /// An representing the same visual color. + public static HsbColor FromColor(Color color) + { + Color.RGBToHSV(color, out var h, out var s, out var v); + return new HsbColor(h, s, v, color.a); + } + + /// + /// Linearly interpolates between two values, using the shortest path for the hue. + /// + /// The starting color. + /// The ending color. + /// The interpolation value between the two colors, clamped to a 0 to 1 range. + /// The interpolated . + public static HsbColor Lerp(HsbColor a, HsbColor b, float t) + { + t = Mathf.Clamp01(t); + return LerpUnclamped(a, b, t); + } + + /// + /// Linearly interpolates between two values without clamping the interpolant. + /// + /// The starting color. + /// The ending color. + /// The interpolation value between the two colors. + /// The interpolated . + public static HsbColor LerpUnclamped(HsbColor a, HsbColor b, float t) + { + var hueDifference = Mathf.Repeat(b.h - a.h, 1f); + + if (hueDifference > 0.5f) + hueDifference -= 1f; + + return new HsbColor( + Mathf.Repeat(a.h + (hueDifference * t), 1f), + Mathf.LerpUnclamped(a.s, b.s, t), + Mathf.LerpUnclamped(a.b, b.b, t), + Mathf.LerpUnclamped(a.a, b.a, t)); + } +} diff --git a/MiraAPI/Colors/ColorSpaces/HslColor.cs b/MiraAPI/Colors/ColorSpaces/HslColor.cs new file mode 100644 index 0000000..824a032 --- /dev/null +++ b/MiraAPI/Colors/ColorSpaces/HslColor.cs @@ -0,0 +1,117 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using UnityEngine; + +namespace MiraAPI.Colors.ColorSpaces; + +/// +/// A struct that denotes the HSL (Hue, Saturation, Lightness) color space. +/// +/// The hue component. +/// The saturation component. +/// The lightness component. +/// The alpha component. +[Serializable] +[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Unity parity.")] +public record struct HslColor(float h, float s, float l, float a = 1f) +{ + /// + /// Implicitly converts an to a Unity . + /// + /// The color being converted. + public static implicit operator Color(HslColor c) + { + return ToColor(c); + } + + /// + /// Implicitly converts a Unity to an . + /// + /// The color being converted. + public static implicit operator HslColor(Color c) + { + return FromColor(c); + } + + /// + /// Converts an to a standard Unity RGB . + /// + /// The HSL color to convert. + /// A Unity representing the same visual color. + public static Color ToColor(HslColor hsl) + { + if (hsl.s <= Mathf.Epsilon) + return new Color(Mathf.Clamp01(hsl.l), Mathf.Clamp01(hsl.l), Mathf.Clamp01(hsl.l), hsl.a); + + var c = (1f - Mathf.Abs(2f * hsl.l - 1f)) * hsl.s; + var hPrime = hsl.h * 6f; + var x = c * (1f - Mathf.Abs((hPrime % 2f) - 1f)); + var m = hsl.l - c / 2f; + + var (r, g, b) = hPrime switch + { + < 1f => (c, x, 0f), + < 2f => (x, c, 0f), + < 3f => (0f, c, x), + < 4f => (0f, x, c), + < 5f => (x, 0f, c), + < 6f => (c, 0f, x), + _ => (0f, 0f, 0f), + }; + + return new Color(Mathf.Clamp01(r + m), Mathf.Clamp01(g + m), Mathf.Clamp01(b + m), hsl.a); + } + + /// + /// Converts a standard Unity RGB to an . + /// + /// The Unity Color to convert. + /// An representing the same visual color. + public static HslColor FromColor(Color color) + { + var max = Mathf.Max(color.r, Mathf.Max(color.g, color.b)); + var min = Mathf.Min(color.r, Mathf.Min(color.g, color.b)); + var l = (max + min) / 2f; + var delta = max - min; + + var s = delta == 0f ? 0f : delta / (1f - Mathf.Abs(2f * l - 1f)); + + Color.RGBToHSV(color, out var h, out _, out _); + + return new HslColor(h, s, l, color.a); + } + + /// + /// Linearly interpolates between two values, using the shortest path for the hue. + /// + /// The starting color. + /// The ending color. + /// The interpolation value between the two colors, clamped to a 0 to 1 range. + /// The interpolated . + public static HslColor Lerp(HslColor a, HslColor b, float t) + { + t = Mathf.Clamp01(t); + return LerpUnclamped(a, b, t); + } + + /// + /// Linearly interpolates between two values without clamping the interpolant. + /// + /// The starting color. + /// The ending color. + /// The interpolation value between the two colors. + /// The interpolated . + public static HslColor LerpUnclamped(HslColor a, HslColor b, float t) + { + var hueDifference = Mathf.Repeat(b.h - a.h, 1f); + + if (hueDifference > 0.5f) + hueDifference -= 1f; + + return new HslColor( + Mathf.Repeat(a.h + (hueDifference * t), 1f), + Mathf.LerpUnclamped(a.s, b.s, t), + Mathf.LerpUnclamped(a.l, b.l, t), + Mathf.LerpUnclamped(a.a, b.a, t)); + } +} diff --git a/MiraAPI/Colors/CustomColor.cs b/MiraAPI/Colors/CustomColor.cs index a778e31..2c60dd5 100644 --- a/MiraAPI/Colors/CustomColor.cs +++ b/MiraAPI/Colors/CustomColor.cs @@ -10,7 +10,7 @@ namespace MiraAPI.Colors; /// The name of the option. /// The main color. /// The shadow color. -public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColor) +public class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColor) { /// /// Gets or sets the main color. diff --git a/MiraAPI/Colors/PaletteManager.cs b/MiraAPI/Colors/PaletteManager.cs index fe7e725..50a0ed0 100644 --- a/MiraAPI/Colors/PaletteManager.cs +++ b/MiraAPI/Colors/PaletteManager.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using HarmonyLib; +using UnityEngine; namespace MiraAPI.Colors; @@ -15,6 +16,8 @@ public static class PaletteManager public static CustomColor[] RegisteredColors => [.. CustomColors]; internal static readonly List CustomColors = []; + internal static readonly HashSet VariableColorNames = []; + internal static readonly Dictionary ColorIdToColorMap = []; internal static void RegisterAllColors() { @@ -22,11 +25,64 @@ public static class PaletteManager var shadowColors = CustomColors.Select(x => x.ShadowColor).ToArray(); var stringNames = CustomColors.Select(x => x.Name).ToArray(); + var originalLength = Palette.PlayerColors.Length; + Palette.PlayerColors = Palette.PlayerColors.ToArray().AddRangeToArray(colors); Palette.ShadowColors = Palette.ShadowColors.ToArray().AddRangeToArray(shadowColors); Palette.ColorNames = Palette.ColorNames.ToArray().AddRangeToArray(stringNames); Palette.TextColors = Palette.TextColors.ToArray().AddRangeToArray(colors); Palette.TextOutlineColors = Palette.TextOutlineColors.ToArray().AddRangeToArray(shadowColors); + + for (var i = 0; i < originalLength + CustomColors.Count; i++) + ColorIdToColorMap[i + originalLength] = CustomColors[i]; + } + + /// + /// Checks to see if the provided color ID belongs to a variant color. + /// + /// The id of the color. + /// true if the color is a variable color; false otherwise. + public static bool IsVariable(int colorId) + { + return VariableColorNames.Contains(Palette.ColorNames[colorId]); + } + + /// + /// Checks to see if the provided color ID belongs to a custom color. + /// + /// The id of the color. + /// true if the color is a custom color; false otherwise. + public static bool IsCustomColor(int colorId) + { + return ColorIdToColorMap.ContainsKey(colorId); + } + + /// + /// Gets the main color of the provided color ID. + /// + /// The id of the color. + /// The main color associated with the provided ID. + public static Color32 GetMainColor(int colorId) + { + return !ColorIdToColorMap.TryGetValue(colorId, out var custom) + ? Palette.PlayerColors[colorId] + : (custom is VariableColor variable + ? variable.EvaluateMainColor() + : custom.MainColor); + } + + /// + /// Gets the shadow color of the provided color ID. + /// + /// The id of the color. + /// The shadow color associated with the provided ID. + public static Color32 GetShadowColor(int colorId) + { + return !ColorIdToColorMap.TryGetValue(colorId, out var custom) + ? Palette.ShadowColors[colorId] + : (custom is VariableColor variable + ? variable.EvaluateShadowColor() + : custom.ShadowColor); } } diff --git a/MiraAPI/Colors/VariableColor.cs b/MiraAPI/Colors/VariableColor.cs new file mode 100644 index 0000000..0206423 --- /dev/null +++ b/MiraAPI/Colors/VariableColor.cs @@ -0,0 +1,69 @@ +using System; +using MiraAPI.LocalSettings; +using MiraAPI.Translation; +using MiraAPI.Utilities; +using UnityEngine; + +namespace MiraAPI.Colors; + +/// +/// Represents a dynamic custom color that changes over time. +/// +/// The name of the color. +/// The function to evaluate the dynamic main color. +/// The function to evaluate the dynamic shadow color. +/// The static main color used when dynamic colors are disabled. +/// The static shadow color used when dynamic colors are disabled. +public sealed class VariableColor(StringNames name, Func mainEvaluator, Func? shadowEvaluator, Color32? mainColor = null, Color32? shadowColor = null) + : CustomColor(name, mainColor ?? mainEvaluator(0f), shadowColor ?? shadowEvaluator?.Invoke(0f) ?? (mainColor ?? mainEvaluator(0f)).GetShadowColor(60)) +{ + /// + /// Gets or sets a delegate that takes a normalized time value and returns the dynamic main color. + /// + public Func MainEvaluator { get; set; } = mainEvaluator; + + /// + /// Gets or sets a delegate that takes a normalized time value and returns the dynamic shadow color. + /// + public Func ShadowEvaluator { get; set; } = shadowEvaluator ?? (t => mainEvaluator(t).GetShadowColor(60)); + + /// + public VariableColor(StringNames name, Func mainEvaluator, Color32? mainColor = null) + : this(name, mainEvaluator, null, mainColor, null) + { + } + + /// + public VariableColor(string name, Func mainEvaluator, Color32? mainColor = null) + : this(name, mainEvaluator, null, mainColor, null) + { + } + + /// + public VariableColor(string name, Func mainEvaluator, Func? shadowEvaluator, Color32? mainColor = null, Color32? shadowColor = null) + : this(MiraLocaleManager.GetOrCreateLocaleString(name), mainEvaluator, shadowEvaluator, mainColor, shadowColor) + { + } + + /// + /// Evaluates the dynamic main color based on the current in-game time. + /// + /// The dynamically evaluated color, or the static if static variant colors are enabled in the user's settings. + public Color32 EvaluateMainColor() + { + return LocalSettingsTabSingleton.Instance.EnableVariableColors.Value + ? MainEvaluator(Time.time) + : MainColor; + } + + /// + /// Evaluates the dynamic shadow color based on the current in-game time. + /// + /// The dynamically evaluated color, or the static if static variant colors are enabled in the user's settings. + public Color32 EvaluateShadowColor() + { + return LocalSettingsTabSingleton.Instance.EnableVariableColors.Value + ? ShadowEvaluator(Time.time) + : ShadowColor; + } +} diff --git a/MiraAPI/Colors/VariableColorBehaviour.cs b/MiraAPI/Colors/VariableColorBehaviour.cs new file mode 100644 index 0000000..1b7efda --- /dev/null +++ b/MiraAPI/Colors/VariableColorBehaviour.cs @@ -0,0 +1,46 @@ +using System.Diagnostics.CodeAnalysis; +using Il2CppInterop.Runtime.Attributes; +using MiraAPI.Colors; +using MiraAPI.Utilities; +using UnityEngine; + +namespace MiraAPI; + +/// +/// A mono script that adjusts the material colors of a component that it's attached to based on the provided variable color. +/// +[SuppressMessage("Style", "IDE0051:Remove unused private members", Justification = "Unity methods.")] +[SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "Unity fields.")] +[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Read above.")] +public sealed class VariableColorBehaviour : MonoBehaviour +{ + private SpriteRenderer rend; + private Material material; + private VariableColor? color; + + /// + /// Sets the current variant color. + /// + /// The new variant color. + [HideFromIl2Cpp] + public void SetColor(VariableColor? col) + { + color = col; + } + + private void Awake() + { + rend = GetComponent(); + material = rend.material; + } + + private void Update() + { + if (!rend || color == null) + return; + + material.SetColor(ShaderID.BodyColor, color.EvaluateMainColor()); + material.SetColor(ShaderID.BackColor, color.EvaluateShadowColor()); + material.SetColor(ShaderID.VisorColor, Palette.VisorColor); + } +} diff --git a/MiraAPI/MiraApiSettings.cs b/MiraAPI/MiraApiSettings.cs index 1560955..ca448b0 100644 --- a/MiraAPI/MiraApiSettings.cs +++ b/MiraAPI/MiraApiSettings.cs @@ -233,4 +233,10 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) /// [LocalToggleSetting] public ConfigEntry EnableSabotageBlares { get; private set; } = config.Bind("MiraApi.Accessibility", "MiraApi.EnableSabotageBlare", true); + + /// + /// Gets whether variant colors will remain static or not. + /// + [LocalToggleSetting] + public ConfigEntry EnableVariableColors { get; private set; } = config.Bind("MiraApi.Accessibility", "MiraApi.EnableVariableColors", true); } diff --git a/MiraAPI/Patches/Hud/VariableColorPatches.cs b/MiraAPI/Patches/Hud/VariableColorPatches.cs new file mode 100644 index 0000000..628a1f4 --- /dev/null +++ b/MiraAPI/Patches/Hud/VariableColorPatches.cs @@ -0,0 +1,98 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using AmongUs.Data; +using HarmonyLib; +using MiraAPI.Colors; +using UnityEngine; + +namespace MiraAPI.Patches.Hud; + +[HarmonyPatch(typeof(PlayerMaterial))] +internal static class SetPlayerMaterialPatch +{ + [HarmonyPatch(nameof(PlayerMaterial.SetColors), typeof(int), typeof(Renderer))] + public static bool Prefix(int colorId, Renderer rend) + { + var r = rend.gameObject.GetComponent() + ?? rend.gameObject.AddComponent(); + r.SetColor(PaletteManager.IsVariable(colorId) ? (VariableColor)PaletteManager.ColorIdToColorMap[colorId] : null); + return !PaletteManager.IsVariable(colorId); + } + + [HarmonyPatch(nameof(PlayerMaterial.SetColors), typeof(Color), typeof(Renderer))] + public static void Prefix(Renderer rend) + { + var r = rend.gameObject.GetComponent() + ?? rend.gameObject.AddComponent(); + r.SetColor(null); + } +} + +[HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.Update))] +internal static class PlayerTabPatch +{ + public static void Postfix(PlayerTab __instance) + { + for (var i = 0; i < __instance.ColorChips.Count; i++) + { + __instance.ColorChips[i].Inner.SpriteColor = PaletteManager.GetMainColor(i); + } + } +} + +[HarmonyPatch(typeof(ChatNotification), nameof(ChatNotification.Update))] +internal static class ChatNotifRainbowPatch +{ + public static void Postfix(ChatNotification __instance) + { + if (!__instance.gameObject.active || !PaletteManager.IsVariable(__instance.player.cosmetics.ColorId)) + return; + + var str = ColorUtility.ToHtmlStringRGB(PaletteManager.GetMainColor(__instance.player.cosmetics.ColorId)); + __instance.playerNameText.text = "" + __instance.playerNameText.text.WithoutRichText(); + } + + private static string WithoutRichText(this string text) + { + var richTagRegex = new Regex(@"<[^>]*>", default, Regex.InfiniteMatchTimeout); + return richTagRegex.Replace(text, string.Empty); + } +} + +[HarmonyPatch(typeof(HostInfoPanel), nameof(HostInfoPanel.Update))] +internal static class RainbowLobbyInfoPanePatch +{ + [SuppressMessage("Style", "IDE0045:Convert to conditional expression", Justification = "Operator becomes too large.")] + public static void Postfix(HostInfoPanel __instance) + { + if (!__instance.gameObject.activeInHierarchy || !PaletteManager.IsVariable(__instance.player.cosmetics.ColorId)) + { + return; + } + + var host = GameData.Instance.GetHost(); + var text = ColorUtility.ToHtmlStringRGB(PaletteManager.GetMainColor(__instance.player.cosmetics.ColorId)); + __instance.hostLabel.text = + TranslationController.Instance.GetString(StringNames.HostNounLabel); + if (__instance.ShouldBoldenHostLabel(DataManager.Settings.Language.CurrentLanguage)) + { + __instance.hostLabel.text = __instance.hostLabel.text.Insert(0, ""); + __instance.hostLabel.text = __instance.hostLabel.text.Insert(__instance.hostLabel.text.Length, ""); + } + + if (AmongUsClient.Instance.AmHost) + { + __instance.playerName.text = (string.IsNullOrEmpty(host.PlayerName) + ? "..." + : $"{host.PlayerName}") + + " " + + TranslationController.Instance.GetString(StringNames.HostYouLabel); + } + else + { + __instance.playerName.text = + (string.IsNullOrEmpty(host.PlayerName) ? "..." : $"{host.PlayerName}") + + " (" + __instance.player.ColorBlindName + ")"; + } + } +} diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index a3b5158..9058eac 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -404,7 +404,7 @@ public sealed class MiraPluginManager foreach (var property in type.GetProperties()) { - if (property.PropertyType != typeof(CustomColor)) + if (property.PropertyType.IsAssignableTo(typeof(CustomColor))) { continue; } @@ -416,6 +416,9 @@ public sealed class MiraPluginManager } PaletteManager.CustomColors.Add(color); + + if (color is VariableColor) + PaletteManager.VariableColorNames.Add(color.Name); } foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(CustomColor)))) diff --git a/MiraAPI/Resources/Locale/en_US.xml b/MiraAPI/Resources/Locale/en_US.xml index 7cd2c1c..be53733 100644 --- a/MiraAPI/Resources/Locale/en_US.xml +++ b/MiraAPI/Resources/Locale/en_US.xml @@ -12,6 +12,7 @@ Display Wiki on Bottom Row Enable Sabotage Flashes Enable Sabotage Blare + Enable Variable Color Changes RETURN TO ROLE SETTINGS Custom Category 1 Custom Category 2