add variable color system + color spaces

This commit is contained in:
Az committed 2026-09-29 09:53:18 -04:00
1 parent bfee7d44a2
commit 3bf066f38c
12 files changed
+618 -2

No files matched your search

+3
View File
@@ -86,3 +86,6 @@ dotnet_diagnostic.SA1133.severity = none
# IDE0001: Simplify Names # IDE0001: Simplify Names
dotnet_diagnostic.IDE0001.severity = warning dotnet_diagnostic.IDE0001.severity = warning
# IDE0079: Remove unnecessary suppression
dotnet_diagnostic.IDE0079.severity = none
+103
View File
@@ -0,0 +1,103 @@
using System;
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
namespace MiraAPI.Colors.ColorSpaces;
/// <summary>
/// A struct that denotes the CMYK (Cyan, Magenta, Yellow, Key) color space.
/// </summary>
/// <param name="c">The cyan component.</param>
/// <param name="m">The magenta component.</param>
/// <param name="y">The yellow component.</param>
/// <param name="k">The key component.</param>
/// <param name="a">The alpha component.</param>
[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)
{
/// <summary>
/// Implicitly converts a <see cref="CmykColor"/> to a Unity <see cref="Color"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator Color(CmykColor c)
{
return ToColor(c);
}
/// <summary>
/// Implicitly converts a Unity <see cref="Color"/> to a <see cref="CmykColor"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator CmykColor(Color c)
{
return FromColor(c);
}
/// <summary>
/// Converts a <see cref="CmykColor"/> to a standard Unity RGB <see cref="Color"/>.
/// </summary>
/// <param name="cmyk">The CMYK color to convert.</param>
/// <returns>A Unity <see cref="Color"/> representing the same visual color.</returns>
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);
}
/// <summary>
/// Converts a standard Unity RGB <see cref="Color"/> to a <see cref="CmykColor"/>.
/// </summary>
/// <param name="color">The Unity Color to convert.</param>
/// <returns>A <see cref="CmykColor"/> representing the same visual color.</returns>
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);
}
/// <summary>
/// Linearly interpolates between two <see cref="CmykColor"/> values.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors, clamped to a 0 to 1 range.</param>
/// <returns>The interpolated <see cref="CmykColor"/>.</returns>
public static CmykColor Lerp(CmykColor a, CmykColor b, float t)
{
t = Mathf.Clamp01(t);
return LerpUnclamped(a, b, t);
}
/// <summary>
/// Linearly interpolates between two <see cref="CmykColor"/> values without clamping the interpolant.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors.</param>
/// <returns>The interpolated <see cref="CmykColor"/>.</returns>
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));
}
}
+114
View File
@@ -0,0 +1,114 @@
using System;
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
namespace MiraAPI.Colors.ColorSpaces;
/// <summary>
/// A struct that denotes the HSB (Hue, Saturation, Brightness) color space.
/// </summary>
/// <param name="h">The hue component.</param>
/// <param name="s">The saturation component.</param>
/// <param name="b">The brightness component.</param>
/// <param name="a">The alpha component.</param>
[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)
{
/// <summary>
/// Implicitly converts an <see cref="HsbColor"/> to a Unity <see cref="Color"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator Color(HsbColor c)
{
return ToColor(c);
}
/// <summary>
/// Implicitly converts a Unity <see cref="Color"/> to an <see cref="HsbColor"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator HsbColor(Color c)
{
return FromColor(c);
}
/// <summary>
/// Converts an <see cref="HsbColor"/> to a standard Unity RGB <see cref="Color"/>.
/// </summary>
/// <param name="hsb">The HSB color to convert.</param>
/// <returns>A Unity <see cref="Color"/> representing the same visual color.</returns>
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);
}
/// <summary>
/// Converts a standard Unity RGB <see cref="Color"/> to an <see cref="HsbColor"/>.
/// </summary>
/// <param name="color">The Unity Color to convert.</param>
/// <returns>An <see cref="HsbColor"/> representing the same visual color.</returns>
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);
}
/// <summary>
/// Linearly interpolates between two <see cref="HsbColor"/> values, using the shortest path for the hue.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors, clamped to a 0 to 1 range.</param>
/// <returns>The interpolated <see cref="HsbColor"/>.</returns>
public static HsbColor Lerp(HsbColor a, HsbColor b, float t)
{
t = Mathf.Clamp01(t);
return LerpUnclamped(a, b, t);
}
/// <summary>
/// Linearly interpolates between two <see cref="HsbColor"/> values without clamping the interpolant.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors.</param>
/// <returns>The interpolated <see cref="HsbColor"/>.</returns>
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));
}
}
+117
View File
@@ -0,0 +1,117 @@
using System;
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
namespace MiraAPI.Colors.ColorSpaces;
/// <summary>
/// A struct that denotes the HSL (Hue, Saturation, Lightness) color space.
/// </summary>
/// <param name="h">The hue component.</param>
/// <param name="s">The saturation component.</param>
/// <param name="l">The lightness component.</param>
/// <param name="a">The alpha component.</param>
[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)
{
/// <summary>
/// Implicitly converts an <see cref="HslColor"/> to a Unity <see cref="Color"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator Color(HslColor c)
{
return ToColor(c);
}
/// <summary>
/// Implicitly converts a Unity <see cref="Color"/> to an <see cref="HslColor"/>.
/// </summary>
/// <param name="c">The color being converted.</param>
public static implicit operator HslColor(Color c)
{
return FromColor(c);
}
/// <summary>
/// Converts an <see cref="HslColor"/> to a standard Unity RGB <see cref="Color"/>.
/// </summary>
/// <param name="hsl">The HSL color to convert.</param>
/// <returns>A Unity <see cref="Color"/> representing the same visual color.</returns>
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);
}
/// <summary>
/// Converts a standard Unity RGB <see cref="Color"/> to an <see cref="HslColor"/>.
/// </summary>
/// <param name="color">The Unity Color to convert.</param>
/// <returns>An <see cref="HslColor"/> representing the same visual color.</returns>
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);
}
/// <summary>
/// Linearly interpolates between two <see cref="HslColor"/> values, using the shortest path for the hue.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors, clamped to a 0 to 1 range.</param>
/// <returns>The interpolated <see cref="HslColor"/>.</returns>
public static HslColor Lerp(HslColor a, HslColor b, float t)
{
t = Mathf.Clamp01(t);
return LerpUnclamped(a, b, t);
}
/// <summary>
/// Linearly interpolates between two <see cref="HslColor"/> values without clamping the interpolant.
/// </summary>
/// <param name="a">The starting color.</param>
/// <param name="b">The ending color.</param>
/// <param name="t">The interpolation value between the two colors.</param>
/// <returns>The interpolated <see cref="HslColor"/>.</returns>
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));
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ namespace MiraAPI.Colors;
/// <param name="name">The name of the option.</param> /// <param name="name">The name of the option.</param>
/// <param name="mainColor">The main color.</param> /// <param name="mainColor">The main color.</param>
/// <param name="shadowColor">The shadow color.</param> /// <param name="shadowColor">The shadow color.</param>
public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColor) public class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColor)
{ {
/// <summary> /// <summary>
/// Gets or sets the main color. /// Gets or sets the main color.
+56
View File
@@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using HarmonyLib; using HarmonyLib;
using UnityEngine;
namespace MiraAPI.Colors; namespace MiraAPI.Colors;
@@ -15,6 +16,8 @@ public static class PaletteManager
public static CustomColor[] RegisteredColors => [.. CustomColors]; public static CustomColor[] RegisteredColors => [.. CustomColors];
internal static readonly List<CustomColor> CustomColors = []; internal static readonly List<CustomColor> CustomColors = [];
internal static readonly HashSet<StringNames> VariableColorNames = [];
internal static readonly Dictionary<int, CustomColor> ColorIdToColorMap = [];
internal static void RegisterAllColors() internal static void RegisterAllColors()
{ {
@@ -22,11 +25,64 @@ public static class PaletteManager
var shadowColors = CustomColors.Select(x => x.ShadowColor).ToArray(); var shadowColors = CustomColors.Select(x => x.ShadowColor).ToArray();
var stringNames = CustomColors.Select(x => x.Name).ToArray(); var stringNames = CustomColors.Select(x => x.Name).ToArray();
var originalLength = Palette.PlayerColors.Length;
Palette.PlayerColors = Palette.PlayerColors.ToArray().AddRangeToArray(colors); Palette.PlayerColors = Palette.PlayerColors.ToArray().AddRangeToArray(colors);
Palette.ShadowColors = Palette.ShadowColors.ToArray().AddRangeToArray(shadowColors); Palette.ShadowColors = Palette.ShadowColors.ToArray().AddRangeToArray(shadowColors);
Palette.ColorNames = Palette.ColorNames.ToArray().AddRangeToArray(stringNames); Palette.ColorNames = Palette.ColorNames.ToArray().AddRangeToArray(stringNames);
Palette.TextColors = Palette.TextColors.ToArray().AddRangeToArray(colors); Palette.TextColors = Palette.TextColors.ToArray().AddRangeToArray(colors);
Palette.TextOutlineColors = Palette.TextOutlineColors.ToArray().AddRangeToArray(shadowColors); Palette.TextOutlineColors = Palette.TextOutlineColors.ToArray().AddRangeToArray(shadowColors);
for (var i = 0; i < originalLength + CustomColors.Count; i++)
ColorIdToColorMap[i + originalLength] = CustomColors[i];
}
/// <summary>
/// Checks to see if the provided color ID belongs to a variant color.
/// </summary>
/// <param name="colorId">The id of the color.</param>
/// <returns><c>true</c> if the color is a variable color; <c>false</c> otherwise.</returns>
public static bool IsVariable(int colorId)
{
return VariableColorNames.Contains(Palette.ColorNames[colorId]);
}
/// <summary>
/// Checks to see if the provided color ID belongs to a custom color.
/// </summary>
/// <param name="colorId">The id of the color.</param>
/// <returns><c>true</c> if the color is a custom color; <c>false</c> otherwise.</returns>
public static bool IsCustomColor(int colorId)
{
return ColorIdToColorMap.ContainsKey(colorId);
}
/// <summary>
/// Gets the main color of the provided color ID.
/// </summary>
/// <param name="colorId">The id of the color.</param>
/// <returns>The main color associated with the provided ID.</returns>
public static Color32 GetMainColor(int colorId)
{
return !ColorIdToColorMap.TryGetValue(colorId, out var custom)
? Palette.PlayerColors[colorId]
: (custom is VariableColor variable
? variable.EvaluateMainColor()
: custom.MainColor);
}
/// <summary>
/// Gets the shadow color of the provided color ID.
/// </summary>
/// <param name="colorId">The id of the color.</param>
/// <returns>The shadow color associated with the provided ID.</returns>
public static Color32 GetShadowColor(int colorId)
{
return !ColorIdToColorMap.TryGetValue(colorId, out var custom)
? Palette.ShadowColors[colorId]
: (custom is VariableColor variable
? variable.EvaluateShadowColor()
: custom.ShadowColor);
} }
} }
+69
View File
@@ -0,0 +1,69 @@
using System;
using MiraAPI.LocalSettings;
using MiraAPI.Translation;
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI.Colors;
/// <summary>
/// Represents a dynamic custom color that changes over time.
/// </summary>
/// <param name="name">The name of the color.</param>
/// <param name="mainEvaluator">The function to evaluate the dynamic main color.</param>
/// <param name="shadowEvaluator">The function to evaluate the dynamic shadow color.</param>
/// <param name="mainColor">The static main color used when dynamic colors are disabled.</param>
/// <param name="shadowColor">The static shadow color used when dynamic colors are disabled.</param>
public sealed class VariableColor(StringNames name, Func<float, Color32> mainEvaluator, Func<float, Color32>? shadowEvaluator, Color32? mainColor = null, Color32? shadowColor = null)
: CustomColor(name, mainColor ?? mainEvaluator(0f), shadowColor ?? shadowEvaluator?.Invoke(0f) ?? (mainColor ?? mainEvaluator(0f)).GetShadowColor(60))
{
/// <summary>
/// Gets or sets a delegate that takes a normalized time value and returns the dynamic main color.
/// </summary>
public Func<float, Color32> MainEvaluator { get; set; } = mainEvaluator;
/// <summary>
/// Gets or sets a delegate that takes a normalized time value and returns the dynamic shadow color.
/// </summary>
public Func<float, Color32> ShadowEvaluator { get; set; } = shadowEvaluator ?? (t => mainEvaluator(t).GetShadowColor(60));
/// <inheritdoc cref="VariableColor"/>
public VariableColor(StringNames name, Func<float, Color32> mainEvaluator, Color32? mainColor = null)
: this(name, mainEvaluator, null, mainColor, null)
{
}
/// <inheritdoc cref="VariableColor"/>
public VariableColor(string name, Func<float, Color32> mainEvaluator, Color32? mainColor = null)
: this(name, mainEvaluator, null, mainColor, null)
{
}
/// <inheritdoc cref="VariableColor"/>
public VariableColor(string name, Func<float, Color32> mainEvaluator, Func<float, Color32>? shadowEvaluator, Color32? mainColor = null, Color32? shadowColor = null)
: this(MiraLocaleManager.GetOrCreateLocaleString(name), mainEvaluator, shadowEvaluator, mainColor, shadowColor)
{
}
/// <summary>
/// Evaluates the dynamic main color based on the current in-game time.
/// </summary>
/// <returns>The dynamically evaluated color, or the static <see cref="CustomColor.MainColor"/> if static variant colors are enabled in the user's settings.</returns>
public Color32 EvaluateMainColor()
{
return LocalSettingsTabSingleton<MiraApiSettings>.Instance.EnableVariableColors.Value
? MainEvaluator(Time.time)
: MainColor;
}
/// <summary>
/// Evaluates the dynamic shadow color based on the current in-game time.
/// </summary>
/// <returns>The dynamically evaluated color, or the static <see cref="CustomColor.ShadowColor"/> if static variant colors are enabled in the user's settings.</returns>
public Color32 EvaluateShadowColor()
{
return LocalSettingsTabSingleton<MiraApiSettings>.Instance.EnableVariableColors.Value
? ShadowEvaluator(Time.time)
: ShadowColor;
}
}
+46
View File
@@ -0,0 +1,46 @@
using System.Diagnostics.CodeAnalysis;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Colors;
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI;
/// <summary>
/// A mono script that adjusts the material colors of a <see cref="SpriteRenderer"/> component that it's attached to based on the provided variable color.
/// </summary>
[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;
/// <summary>
/// Sets the current variant color.
/// </summary>
/// <param name="col">The new variant color.</param>
[HideFromIl2Cpp]
public void SetColor(VariableColor? col)
{
color = col;
}
private void Awake()
{
rend = GetComponent<SpriteRenderer>();
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);
}
}
+6
View File
@@ -233,4 +233,10 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
/// </summary> /// </summary>
[LocalToggleSetting] [LocalToggleSetting]
public ConfigEntry<bool> EnableSabotageBlares { get; private set; } = config.Bind("MiraApi.Accessibility", "MiraApi.EnableSabotageBlare", true); public ConfigEntry<bool> EnableSabotageBlares { get; private set; } = config.Bind("MiraApi.Accessibility", "MiraApi.EnableSabotageBlare", true);
/// <summary>
/// Gets whether variant colors will remain static or not.
/// </summary>
[LocalToggleSetting]
public ConfigEntry<bool> EnableVariableColors { get; private set; } = config.Bind("MiraApi.Accessibility", "MiraApi.EnableVariableColors", true);
} }
@@ -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<VariableColorBehaviour>()
?? rend.gameObject.AddComponent<VariableColorBehaviour>();
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<VariableColorBehaviour>()
?? rend.gameObject.AddComponent<VariableColorBehaviour>();
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 = "<color=#" + str + ">" + __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, "<b>");
__instance.hostLabel.text = __instance.hostLabel.text.Insert(__instance.hostLabel.text.Length, "</b>");
}
if (AmongUsClient.Instance.AmHost)
{
__instance.playerName.text = (string.IsNullOrEmpty(host.PlayerName)
? "..."
: $"<color=#{text}>{host.PlayerName}</color>")
+ " <size=90%><b><font=\"Barlow-BoldItalic SDF\" material=\"Barlow-BoldItalic SDF Outline\">" +
TranslationController.Instance.GetString(StringNames.HostYouLabel);
}
else
{
__instance.playerName.text =
(string.IsNullOrEmpty(host.PlayerName) ? "..." : $"<color=#{text}>{host.PlayerName}</color>") +
" (" + __instance.player.ColorBlindName + ")";
}
}
}
+4 -1
View File
@@ -404,7 +404,7 @@ public sealed class MiraPluginManager
foreach (var property in type.GetProperties()) foreach (var property in type.GetProperties())
{ {
if (property.PropertyType != typeof(CustomColor)) if (property.PropertyType.IsAssignableTo(typeof(CustomColor)))
{ {
continue; continue;
} }
@@ -416,6 +416,9 @@ public sealed class MiraPluginManager
} }
PaletteManager.CustomColors.Add(color); 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)))) foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(CustomColor))))
+1
View File
@@ -12,6 +12,7 @@
<string name="MiraApi.WikiOnBottomRow">Display Wiki on Bottom Row</string> <string name="MiraApi.WikiOnBottomRow">Display Wiki on Bottom Row</string>
<string name="MiraApi.EnableSabotageFlashes">Enable Sabotage Flashes</string> <string name="MiraApi.EnableSabotageFlashes">Enable Sabotage Flashes</string>
<string name="MiraApi.EnableSabotageBlare">Enable Sabotage Blare</string> <string name="MiraApi.EnableSabotageBlare">Enable Sabotage Blare</string>
<string name="MiraApi.EnableVariableColors">Enable Variable Color Changes</string>
<string name="MiraApi.ReturnToRoleSettings">RETURN TO ROLE SETTINGS</string> <string name="MiraApi.ReturnToRoleSettings">RETURN TO ROLE SETTINGS</string>
<string name="MiraApi.CustomCategory1">Custom Category 1</string> <string name="MiraApi.CustomCategory1">Custom Category 1</string>
<string name="MiraApi.CustomCategory2">Custom Category 2</string> <string name="MiraApi.CustomCategory2">Custom Category 2</string>