Compare commits

...
3 Commits
Author SHA1 Message Date
Az 51397d78a7 color improvements 2026-10-26 16:53:02 -04:00
Az 08fd56754e fix small issue 2026-10-26 04:55:38 -04:00
Az 181013efec minor code fixes and nitpicks 2026-10-26 00:52:19 -04:00
14 changed files with 397 additions and 88 deletions

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@ namespace MiraAPI.Example;
public static class ExampleAssets
{
private static readonly Assembly _assembly = typeof(MiraAssets).Assembly;
private static readonly Assembly _assembly = typeof(ExampleAssets).Assembly;
public static LoadableResourceAsset ExampleButton { get; } = new("MiraAPI.Example.Resources.ExampleButton.png", _assembly);
public static LoadableResourceAsset CallMeetingButton { get; } = new("MiraAPI.Example.Resources.CallMeeting.png", _assembly);
+1 -1
View File
@@ -19,7 +19,7 @@ public partial class ExamplePlugin : BasePlugin, IMiraPlugin
{
public ExamplePlugin()
{
MiraLocaleManager.Register("mira.example");
MiraLocaleManager.Register("mira.example", typeof(ExamplePlugin).Assembly);
}
public Harmony Harmony { get; } = new(Id);
+2 -2
View File
@@ -58,13 +58,13 @@ public class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColo
/// <inheritdoc cref="CustomColor"/>
public CustomColor(StringNames name, Color32 mainColor)
: this(name, mainColor, mainColor.GetShadowColor(60))
: this(name, mainColor, mainColor.DarkenAbsolute(60))
{
}
/// <inheritdoc cref="CustomColor"/>
public CustomColor(string name, Color32 mainColor)
: this(name, mainColor, mainColor.GetShadowColor(60))
: this(name, mainColor, mainColor.DarkenAbsolute(60))
{
}
+2 -2
View File
@@ -16,7 +16,7 @@ namespace MiraAPI.Colors;
/// <param name="shadowColor">The static shadow color used when dynamic colors are disabled.</param>
/// <param name="defaultTime">The default time that is used to automatically fill in the static colors when the latter is not provided.</param>
public sealed class VariableColor(StringNames name, Func<float, Color> mainEvaluator, Func<float, Color>? shadowEvaluator, Color32? mainColor = null, Color32? shadowColor = null, float defaultTime = 0f)
: CustomColor(name, mainColor ?? mainEvaluator(defaultTime), shadowColor ?? shadowEvaluator?.Invoke(defaultTime) ?? (mainColor ?? mainEvaluator(defaultTime)).GetShadowColor(60))
: CustomColor(name, mainColor ?? mainEvaluator(defaultTime), shadowColor ?? shadowEvaluator?.Invoke(defaultTime) ?? (mainColor ?? mainEvaluator(defaultTime)).DarkenAbsolute(60))
{
/// <summary>
/// Gets or sets a delegate that takes in a time value and returns the dynamic main color.
@@ -26,7 +26,7 @@ public sealed class VariableColor(StringNames name, Func<float, Color> mainEvalu
/// <summary>
/// Gets or sets a delegate that takes in a time value and returns the dynamic shadow color.
/// </summary>
public Func<float, Color> ShadowEvaluator { get; set; } = shadowEvaluator ?? (t => mainEvaluator(t).GetShadowColor(0.24f));
public Func<float, Color> ShadowEvaluator { get; set; } = shadowEvaluator ?? (t => mainEvaluator(t).DarkenAbsolute(0.24f));
/// <inheritdoc cref="VariableColor"/>
public VariableColor(StringNames name, Func<float, Color> mainEvaluator, Color32? mainColor = null, float defaultTime = 0f)
@@ -97,7 +97,7 @@ public class ModdedNumberOptionAttribute(
var value = opt.Value;
if (ValueType == typeof(int))
return (int)Math.Round(value);
return (int)MathF.Round(value);
return value;
}
@@ -101,7 +101,7 @@ public class ModdedNumberOptionListAttribute(
var value = opt.Value;
if (ValueType == typeof(int))
return (int)Math.Round(value);
return (int)MathF.Round(value);
return value;
}
+1 -1
View File
@@ -51,7 +51,7 @@ public abstract class TimedModifier : BaseModifier
/// <returns>A string with the time remaining.</returns>
public override string GetDescription()
{
return $"Remaining Time: {Math.Round(Duration - TimeRemaining, 0)}s/{Duration}s";
return $"Remaining Time: {MathF.Round(Duration - TimeRemaining, 0)}s/{Duration}s";
}
/// <summary>
@@ -1,4 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using AmongUs.Data;
using HarmonyLib;
@@ -42,7 +41,7 @@ internal static class PlayerTabPatch
}
[HarmonyPatch(typeof(ChatNotification), nameof(ChatNotification.Update))]
internal static class ChatNotifRainbowPatch
internal static class ChatNotifVariablePatch
{
private static readonly Regex RichTags = new(@"<[^>]*>", default, Regex.InfiniteMatchTimeout);
@@ -60,14 +59,13 @@ internal static class ChatNotifRainbowPatch
var str = color.ToHtmlStringRGBA();
var nameText = __instance.playerNameText;
nameText.text = "<color=#" + str + ">" + RichTags.Replace(nameText.text, string.Empty);
nameText.outlineColor = color.GetShadowColor(0.5f);
nameText.outlineColor = color.DarkenAbsolute(0.5f);
}
}
[HarmonyPatch(typeof(HostInfoPanel), nameof(HostInfoPanel.Update))]
internal static class RainbowLobbyInfoPanePatch
internal static class VariableLobbyInfoPanePatch
{
[SuppressMessage("Style", "IDE0045:Convert to conditional expression", Justification = "Operator becomes too large.")]
public static void Postfix(HostInfoPanel __instance)
{
if (!__instance.gameObject.activeInHierarchy)
@@ -464,7 +464,7 @@ public static class LobbyViewPanePatches
if (name is not (StringNames.CrewmateRolesHeader or StringNames.ImpostorRolesHeader))
{
var veryDarkColor = group.Color.DarkenColor(.35f);
var veryDarkColor = group.Color.DarkenRelative(.35f);
categoryHeaderRoleVariant.Title.color = veryDarkColor;
categoryHeaderRoleVariant.Background.color = group.Color;
}
+1
View File
@@ -16,6 +16,7 @@ using MiraAPI.Roles;
using MiraAPI.Translation;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using MiraAPI.Utilities.Components;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using TMPro;
+297 -11
View File
@@ -1,12 +1,26 @@
using UnityEngine;
using System;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace MiraAPI.Utilities;
/// <summary>
/// Extension methods for the <see cref="Color"/> struct.
/// Extension methods for the <see cref="Color"/> and <see cref="Color32"/> structs.
/// </summary>
public static class ColorExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float ToFloat(this byte @byte)
{
return @byte / 255f;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ToByte(this float @float)
{
return (byte)MathF.Round(@float * 255f);
}
/// <summary>
/// Gets the relative luminance of a <see cref="Color"/> (WCAG 2.1 specification). 0 is black, 1 is white.
/// </summary>
@@ -19,6 +33,18 @@ public static class ColorExtensions
(0.0722f * Linearize(color.b));
}
/// <summary>
/// Gets the relative luminance of a <see cref="Color32"/> (WCAG 2.1 specification). 0 is black, 1 is white.
/// </summary>
/// <param name="color"><see cref="Color32"/> to calculate luminance for.</param>
/// <returns>Luminance value between 0 and 1.</returns>
public static float GetRelativeLuminance(this Color32 color)
{
return (0.2126f * Linearize(color.r.ToFloat())) +
(0.7152f * Linearize(color.g.ToFloat())) +
(0.0722f * Linearize(color.b.ToFloat()));
}
/// <summary>
/// Linearizes an sRGB component into linear space.
/// </summary>
@@ -26,7 +52,7 @@ public static class ColorExtensions
/// <returns>The linearized result.</returns>
public static float Linearize(float c)
{
return c <= 0.03928f ? c / 12.92f : Mathf.Pow((c + 0.055f) / 1.055f, 2.4f);
return c <= 0.03928f ? c / 12.92f : MathF.Pow((c + 0.055f) / 1.055f, 2.4f);
}
/// <summary>
@@ -39,11 +65,27 @@ public static class ColorExtensions
{
var luminanceA = colorA.GetRelativeLuminance();
var luminanceB = colorB.GetRelativeLuminance();
return (Mathf.Max(luminanceA, luminanceB) + 0.05f) / (Mathf.Min(luminanceA, luminanceB) + 0.05f);
return (Math.Max(luminanceA, luminanceB) + 0.05f) / (Math.Min(luminanceA, luminanceB) + 0.05f);
}
/// <summary>
/// Generates an accessible alternate <see cref="Color"/> with sufficient contrast.
/// Calculates the contrast ratio between two <see cref="Color32"/>s.
/// </summary>
/// <param name="colorA">First <see cref="Color32"/>.</param>
/// <param name="colorB">Second <see cref="Color32"/>.</param>
/// <returns>Contrast ratio between the two <see cref="Color32"/>s.</returns>
public static float GetContrastRatio(this Color32 colorA, Color32 colorB)
{
var luminanceA = colorA.GetRelativeLuminance();
var luminanceB = colorB.GetRelativeLuminance();
return (Math.Max(luminanceA, luminanceB) + 0.05f) / (Math.Min(luminanceA, luminanceB) + 0.05f);
}
/// <summary>
/// Generates an accessible alternate <see cref="Color"/> by shifting towards black or white
/// based on which extreme offers the highest potential contrast from the original color.
/// </summary>
/// <param name="color">Base <see cref="Color"/>.</param>
/// <param name="desiredRatio">Target contrast ratio (default 4.5f for AA).</param>
@@ -57,10 +99,120 @@ public static class ColorExtensions
}
/// <summary>
/// Finds the <see cref="Color"/> with the minimum contrast ratio to the target <see cref="Color"/>.
/// Generates an accessible alternate <see cref="Color32"/> with sufficient contrast.
/// </summary>
/// <param name="color">Base <see cref="Color32"/>.</param>
/// <param name="desiredRatio">Target contrast ratio (default 4.5f for AA).</param>
/// <returns>Alternate <see cref="Color32"/> with sufficient contrast.</returns>
public static Color32 FindAlternateColor(this Color32 color, float desiredRatio = 4.5f)
{
var colorF = new Color(color.r.ToFloat(), color.g.ToFloat(), color.b.ToFloat());
var result = colorF.FindAlternateColor(desiredRatio);
return new Color32(result.r.ToByte(), result.g.ToByte(), result.b.ToByte(), color.a);
}
/// <summary>
/// Darkens a <see cref="Color"/> towards black until it reaches the target WCAG contrast ratio against a background.
/// </summary>
/// <param name="color">The foreground <see cref="Color"/> to modify.</param>
/// <param name="background">The background <see cref="Color"/> it sits on.</param>
/// <param name="targetRatio">Target contrast ratio (default 4.5f for AA).</param>
/// <returns>A darkened <see cref="Color"/> meeting the contrast ratio, or black if impossible.</returns>
public static Color DarkenToWCAG(this Color color, Color background, float targetRatio = 4.5f)
{
return color.GetContrastRatio(background) >= targetRatio
? color
: FindMinimumContrastColor(color, Color.black, targetRatio);
}
/// <summary>
/// Darkens a <see cref="Color32"/> towards black until it reaches the target WCAG contrast ratio against a background.
/// </summary>
/// <param name="color">The foreground <see cref="Color32"/> to modify.</param>
/// <param name="background">The background <see cref="Color32"/> it sits on.</param>
/// <param name="targetRatio">Target contrast ratio (default 4.5f for AA).</param>
/// <returns>A darkened <see cref="Color32"/> meeting the contrast ratio, or black if impossible.</returns>
public static Color32 DarkenToWCAG(this Color32 color, Color32 background, float targetRatio = 4.5f)
{
var colorF = new Color(color.r.ToFloat(), color.g.ToFloat(), color.b.ToFloat(), color.a.ToFloat());
var bgF = new Color(background.r.ToFloat(), background.g.ToFloat(), background.b.ToFloat(), background.a.ToFloat());
var result = colorF.DarkenToWCAG(bgF, targetRatio);
return new Color32(result.r.ToByte(), result.g.ToByte(), result.b.ToByte(), color.a);
}
/// <summary>
/// Lightens a <see cref="Color"/> towards white until it reaches the target WCAG contrast ratio against a background.
/// </summary>
/// <param name="color">The foreground <see cref="Color"/> to modify.</param>
/// <param name="background">The background <see cref="Color"/> it sits on.</param>
/// <param name="targetRatio">Target contrast ratio (default 4.5f for AA).</param>
/// <returns>A lightened <see cref="Color"/> meeting the contrast ratio, or white if impossible.</returns>
public static Color LightenToWCAG(this Color color, Color background, float targetRatio = 4.5f)
{
return color.GetContrastRatio(background) >= targetRatio
? color
: FindMinimumContrastColor(color, Color.white, targetRatio);
}
/// <summary>
/// Lightens a <see cref="Color32"/> towards white until it reaches the target WCAG contrast ratio against a background.
/// </summary>
/// <param name="color">The foreground <see cref="Color32"/> to modify.</param>
/// <param name="background">The background <see cref="Color32"/> it sits on.</param>
/// <param name="targetRatio">Target contrast ratio (default 4.5f for AA).</param>
/// <returns>A lightened <see cref="Color32"/> meeting the contrast ratio, or white if impossible.</returns>
public static Color32 LightenToWCAG(this Color32 color, Color32 background, float targetRatio = 4.5f)
{
var colorF = new Color(color.r.ToFloat(), color.g.ToFloat(), color.b.ToFloat(), color.a.ToFloat());
var bgF = new Color(background.r.ToFloat(), background.g.ToFloat(), background.b.ToFloat(), background.a.ToFloat());
var result = colorF.LightenToWCAG(bgF, targetRatio);
return new Color32(result.r.ToByte(), result.g.ToByte(), result.b.ToByte(), color.a);
}
/// <summary>
/// Automatically decides whether to lighten or darken the <see cref="Color"/> to achieve WCAG compliance
/// based on which extreme offers the highest potential contrast against the provided background.
/// </summary>
/// <param name="color">The foreground <see cref="Color"/> to modify.</param>
/// <param name="background">The background <see cref="Color"/> it sits on.</param>
/// <param name="targetRatio">Target contrast ratio (default 4.5f for AA).</param>
/// <returns>A modified <see cref="Color"/> meeting the WCAG contrast requirement.</returns>
public static Color EnsureWCAGContrast(this Color color, Color background, float targetRatio = 4.5f)
{
if (color.GetContrastRatio(background) >= targetRatio)
return color;
var blackContrast = Color.black.GetContrastRatio(background);
var whiteContrast = Color.white.GetContrastRatio(background);
return blackContrast > whiteContrast
? color.DarkenToWCAG(background, targetRatio)
: color.LightenToWCAG(background, targetRatio);
}
/// <summary>
/// Automatically decides whether to lighten or darken the <see cref="Color32"/> to achieve WCAG compliance.
/// </summary>
/// <param name="color">The foreground <see cref="Color32"/> to modify.</param>
/// <param name="background">The background <see cref="Color32"/> it sits on.</param>
/// <param name="targetRatio">Target contrast ratio (default 4.5f for AA).</param>
/// <returns>A modified <see cref="Color32"/> meeting the WCAG contrast requirement.</returns>
public static Color32 EnsureWCAGContrast(this Color32 color, Color32 background, float targetRatio = 4.5f)
{
var colorF = new Color(color.r.ToFloat(), color.g.ToFloat(), color.b.ToFloat(), color.a.ToFloat());
var bgF = new Color(background.r.ToFloat(), background.g.ToFloat(), background.b.ToFloat(), background.a.ToFloat());
var result = colorF.EnsureWCAGContrast(bgF, targetRatio);
return new Color32(result.r.ToByte(), result.g.ToByte(), result.b.ToByte(), color.a);
}
/// <summary>
/// Finds the <see cref="Color"/> with the minimum contrast ratio to the target <see cref="Color"/> using binary search.
/// </summary>
/// <param name="baseColor">The base <see cref="Color"/> to start from.</param>
/// <param name="targetColor">The target <see cref="Color"/> to compare against.</param>
/// <param name="targetColor">The target <see cref="Color"/> to approach (usually white or black).</param>
/// <param name="desiredRatio">The desired contrast ratio.</param>
/// <returns>The <see cref="Color"/> with the minimum contrast ratio to the target <see cref="Color"/>.</returns>
public static Color FindMinimumContrastColor(Color baseColor, Color targetColor, float desiredRatio)
@@ -72,18 +224,28 @@ public static class ColorExtensions
? desiredRatio * (baseLuminance + 0.05f) - 0.05f
: (baseLuminance + 0.05f) / desiredRatio - 0.05f;
if (isLightening && targetLuminance > 1f) return Color.white;
if (!isLightening && targetLuminance < 0f) return Color.black;
if (isLightening && targetLuminance > 1f)
return Color.white;
if (!isLightening && targetLuminance < 0f)
return Color.black;
float low = 0f, high = 1f;
var bestColor = targetColor;
for (var i = 0; i < 10; i++)
{
var t = (low + high) / 2f;
var testColor = Color.Lerp(baseColor, targetColor, t);
var testColor = new Color(
baseColor.r + (targetColor.r - baseColor.r) * t,
baseColor.g + (targetColor.g - baseColor.g) * t,
baseColor.b + (targetColor.b - baseColor.b) * t,
baseColor.a);
var testLuminance = testColor.GetRelativeLuminance();
if ((isLightening && testLuminance >= targetLuminance) || (!isLightening && testLuminance <= targetLuminance))
if (isLightening ? testLuminance >= targetLuminance : testLuminance <= targetLuminance)
{
high = t;
bestColor = testColor;
@@ -96,4 +258,128 @@ public static class ColorExtensions
return bestColor;
}
/// <summary>
/// Subtracts a flat amount from the <see cref="Color"/>'s RGB channels.
/// </summary>
/// <param name="color">The original color.</param>
/// <param name="amount">The flat amount to subtract (typically 0.0 to 1.0).</param>
/// <returns>A uniformly darkened <see cref="Color"/>.</returns>
public static Color DarkenAbsolute(this Color color, float amount)
{
return new Color(
Math.Max(0f, color.r - amount),
Math.Max(0f, color.g - amount),
Math.Max(0f, color.b - amount),
color.a);
}
/// <summary>
/// Subtracts a flat byte amount from the <see cref="Color32"/>'s RGB channels.
/// </summary>
/// <param name="color">The original <see cref="Color32"/>.</param>
/// <param name="amount">The flat amount to subtract (0 to 255).</param>
/// <returns>A uniformly darkened <see cref="Color32"/>.</returns>
public static Color32 DarkenAbsolute(this Color32 color, byte amount)
{
return new Color32(
(byte)Math.Max(0, color.r - amount),
(byte)Math.Max(0, color.g - amount),
(byte)Math.Max(0, color.b - amount),
color.a);
}
/// <summary>
/// Adds a flat amount to the <see cref="Color"/>'s RGB channels.
/// </summary>
/// <param name="color">The original color.</param>
/// <param name="amount">The flat amount to add (typically 0.0 to 1.0).</param>
/// <returns>A uniformly lightened <see cref="Color"/>.</returns>
public static Color LightenAbsolute(this Color color, float amount)
{
return new Color(
Math.Min(1f, color.r + amount),
Math.Min(1f, color.g + amount),
Math.Min(1f, color.b + amount),
color.a);
}
/// <summary>
/// Adds a flat byte amount to the <see cref="Color32"/>'s RGB channels.
/// </summary>
/// <param name="color">The original <see cref="Color32"/>.</param>
/// <param name="amount">The flat amount to add (0 to 255).</param>
/// <returns>A uniformly lightened <see cref="Color32"/>.</returns>
public static Color32 LightenAbsolute(this Color32 color, byte amount)
{
return new Color32(
(byte)Math.Min(255, color.r + amount),
(byte)Math.Min(255, color.g + amount),
(byte)Math.Min(255, color.b + amount),
color.a);
}
/// <summary>
/// Darkens the <see cref="Color"/> multiplicatively, preserving the ratio between RGB channels to maintain hue.
/// </summary>
/// <param name="color">The original color.</param>
/// <param name="percentage">The percentage to darken by (e.g., 0.2 for 20% darker).</param>
/// <returns>A ratio-preserved darkened <see cref="Color"/>.</returns>
public static Color DarkenRelative(this Color color, float percentage)
{
var multiplier = Math.Max(0f, Math.Min(1f, 1f - percentage));
return new Color(
color.r * multiplier,
color.g * multiplier,
color.b * multiplier,
color.a);
}
/// <summary>
/// Darkens the <see cref="Color32"/> multiplicatively, preserving the ratio between RGB channels.
/// </summary>
/// <param name="color">The original <see cref="Color32"/>.</param>
/// <param name="percentage">The percentage to darken by (e.g., 0.2 for 20% darker).</param>
/// <returns>A ratio-preserved darkened <see cref="Color32"/>.</returns>
public static Color32 DarkenRelative(this Color32 color, float percentage)
{
var multiplier = Math.Max(0f, Math.Min(1f, 1f - percentage));
return new Color32(
(color.r * multiplier).ToByte(),
(color.g * multiplier).ToByte(),
(color.b * multiplier).ToByte(),
color.a);
}
/// <summary>
/// Lightens the <see cref="Color"/> multiplicatively, preserving the ratio between RGB channels.
/// </summary>
/// <param name="color">The original color.</param>
/// <param name="percentage">The percentage to lighten by (e.g., 0.2 for 20% lighter).</param>
/// <returns>A ratio-preserved lightened <see cref="Color"/>.</returns>
public static Color LightenRelative(this Color color, float percentage)
{
var multiplier = 1f + percentage;
return new Color(
Math.Min(1f, color.r * multiplier),
Math.Min(1f, color.g * multiplier),
Math.Min(1f, color.b * multiplier),
color.a);
}
/// <summary>
/// Lightens the <see cref="Color32"/> multiplicatively, preserving the ratio between RGB channels.
/// </summary>
/// <param name="color">The original <see cref="Color32"/>.</param>
/// <param name="percentage">The percentage to lighten by (e.g., 0.2 for 20% lighter).</param>
/// <returns>A ratio-preserved lightened <see cref="Color32"/>.</returns>
public static Color32 LightenRelative(this Color32 color, float percentage)
{
var multiplier = 1f + percentage;
return new Color32(
Math.Min(255f, color.r * 255f * multiplier / 255f).ToByte(),
Math.Min(255f, color.g * 255f * multiplier / 255f).ToByte(),
Math.Min(255f, color.b * 255f * multiplier / 255f).ToByte(),
color.a);
}
}
@@ -2,7 +2,7 @@ using System;
using Reactor.Utilities.Attributes;
using UnityEngine;
namespace MiraAPI.Utilities;
namespace MiraAPI.Utilities.Components;
/// <summary>
/// A spinning ring used to indicate that content is loading.
+48 -25
View File
@@ -26,6 +26,8 @@ namespace MiraAPI.Utilities;
/// </summary>
public static class Extensions
{
private static readonly Regex DisplayRegex = new(@"([^\^])([A-Z][a-z$])", default, Regex.InfiniteMatchTimeout);
internal static NetData GetNetData(this ICustomRole role)
{
var count = role.GetCount();
@@ -498,8 +500,7 @@ public static class Extensions
/// <returns>A proper string for the provided <see cref="Enum"/>.</returns>
public static string ToDisplayString(this Enum @enum)
{
var regex = new Regex(@"([^\^])([A-Z][a-z$])", default, Regex.InfiniteMatchTimeout);
return regex.Replace(@enum.ToString(), m => $"{m.Groups[1].Value} {m.Groups[2].Value}");
return DisplayRegex.Replace(@enum.ToString(), m => $"{m.Groups[1].Value} {m.Groups[2].Value}");
}
/// <summary>
@@ -511,8 +512,7 @@ public static class Extensions
public static string ToDisplayString<T>(this T @enum)
where T : struct, Enum
{
var regex = new Regex(@"([^\^])([A-Z][a-z$])", default, Regex.InfiniteMatchTimeout);
return regex.Replace(@enum.ToString(), m => $"{m.Groups[1].Value} {m.Groups[2].Value}");
return DisplayRegex.Replace(@enum.ToString(), m => $"{m.Groups[1].Value} {m.Groups[2].Value}");
}
/// <summary>
@@ -539,17 +539,46 @@ public static class Extensions
/// <returns>The distance between the types.</returns>
public static int GetInheritanceDistance(Type from, Type to)
{
if (!from.IsAssignableFrom(to)) return int.MaxValue;
if (from == null || to == null)
throw new ArgumentNullException(from == null ? nameof(from) : nameof(to));
var type = from;
var distance = 0;
while (type != null && type != to)
if (from == to)
return 0;
if (!to.IsGenericTypeDefinition && !to.IsAssignableFrom(from))
return int.MaxValue;
var queue = new Queue<(Type Type, int Distance)>();
var visited = new HashSet<Type>();
queue.Enqueue((from, 0));
visited.Add(from);
while (queue.Count > 0)
{
type = type.BaseType;
distance++;
var (current, dist) = queue.Dequeue();
if (current == to || (current.IsGenericType && current.GetGenericTypeDefinition() == to))
return dist;
if (current.BaseType != null && visited.Add(current.BaseType))
queue.Enqueue((current.BaseType, dist + 1));
var allInterfaces = current.GetInterfaces();
var baseInterfaces = current.BaseType?.GetInterfaces() ?? Type.EmptyTypes;
var directInterfaces = allInterfaces.Except(baseInterfaces).ToList();
var inheritedInterfaces = directInterfaces.SelectMany(i => i.GetInterfaces());
var immediateInterfaces = directInterfaces.Except(inheritedInterfaces);
foreach (var iFace in immediateInterfaces)
{
if (visited.Add(iFace))
queue.Enqueue((iFace, dist + 1));
}
}
return type == to ? distance : int.MaxValue;
return to.IsAssignableFrom(from) ? int.MaxValue - 1 : int.MaxValue;
}
/// <summary>
@@ -578,14 +607,10 @@ public static class Extensions
/// <param name="color">The original <see cref="Color32"/>.</param>
/// <param name="darknessAmount">A darkness amount between 0 and 255.</param>
/// <returns>The darkened <see cref="Color32"/>.</returns>
[Obsolete("Use ColorExtensions.DarkenAbsolute instead. This will be removed in a future version.")]
public static Color32 GetShadowColor(this Color32 color, byte darknessAmount)
{
return
new Color32(
(byte)Mathf.Clamp(color.r - darknessAmount, 0, 255),
(byte)Mathf.Clamp(color.g - darknessAmount, 0, 255),
(byte)Mathf.Clamp(color.b - darknessAmount, 0, 255),
color.a);
return color.DarkenAbsolute(darknessAmount);
}
/// <summary>
@@ -594,14 +619,10 @@ public static class Extensions
/// <param name="color">The original <see cref="Color"/>.</param>
/// <param name="darknessAmount">A darkness amount between 0 and 1.</param>
/// <returns>The darkened <see cref="Color"/>.</returns>
[Obsolete("Use ColorExtensions.DarkenAbsolute instead. This will be removed in a future version.")]
public static Color GetShadowColor(this Color color, float darknessAmount)
{
return
new Color(
MathUtilities.Clamp01(color.r - darknessAmount),
MathUtilities.Clamp01(color.g - darknessAmount),
MathUtilities.Clamp01(color.b - darknessAmount),
color.a);
return color.DarkenAbsolute(darknessAmount);
}
/// <summary>
@@ -690,9 +711,10 @@ public static class Extensions
/// <param name="color">The original <see cref="Color"/>.</param>
/// <param name="amount">A <see langword="float"/> amount between 0 and 1.</param>
/// <returns>The darkened <see cref="Color"/>.</returns>
[Obsolete("Use ColorExtensions.DarkenAbsolute instead. This will be removed in a future version.")]
public static Color DarkenColor(this Color color, float amount = 0.45f)
{
return new Color(color.r - amount, color.g - amount, color.b - amount);
return color.DarkenAbsolute(amount);
}
/// <summary>
@@ -701,9 +723,10 @@ public static class Extensions
/// <param name="color">The original <see cref="Color"/>.</param>
/// <param name="amount">A <see langword="float"/> amount between 0.0 and 1.0.</param>
/// <returns>The lightened <see cref="Color"/>.</returns>
[Obsolete("Use ColorExtensions.LightenAbsolute instead. This will be removed in a future version.")]
public static Color LightenColor(this Color color, float amount = 0.45f)
{
return new Color(color.r + amount, color.g + amount, color.b + amount);
return color.LightenAbsolute(amount);
}
/// <summary>
+37 -36
View File
@@ -8,7 +8,6 @@ using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.PluginLoading;
using MiraAPI.Translation;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities;
using UnityEngine;
namespace MiraAPI.Utilities;
@@ -103,40 +102,42 @@ internal static class GuideOptionPages
}
}
if (entries.Count > 0)
if (entries.Count <= 0)
{
var pageKey = item.Group.MatchInfoGuidePageKey?.Trim();
var combinedKey = string.IsNullOrEmpty(pageKey) ? null : $"{item.Plugin.PluginId}\0{pageKey}";
if (combinedKey != null && combinedPages.TryGetValue(combinedKey, out var combinedPage))
continue;
}
var pageKey = item.Group.MatchInfoGuidePageKey?.Trim();
var combinedKey = string.IsNullOrEmpty(pageKey) ? null : $"{item.Plugin.PluginId}\0{pageKey}";
if (combinedKey != null && combinedPages.TryGetValue(combinedKey, out var combinedPage))
{
combinedPage.Groups.Add(item.Group);
combinedPage.Entries.AddRange(entries);
combinedPage.ModSearchTerms.UnionWith(modSearchTerms);
combinedPage.Order = Math.Min(combinedPage.Order, item.Group.MatchInfoGuidePageOrder);
combinedPage.MenuOrder = Math.Min(
combinedPage.MenuOrder,
GetMenuOrder(item.Group.ParentMenu));
combinedPage.GroupPriority = Math.Min(
combinedPage.GroupPriority,
item.Group.GroupPriority);
}
else
{
var page = new GuideOptionPage(
item.Group,
item.Group.MatchInfoGuidePageTitle,
item.Group.MatchInfoGuidePageOrder,
GetMenuOrder(item.Group.ParentMenu),
item.Group.GroupPriority,
Mathf.Clamp(item.Group.MatchInfoGuidePageColumns, 1, 6),
item.ModAbbreviation,
modSearchTerms,
entries);
pages.Add(page);
if (combinedKey != null)
{
combinedPage.Groups.Add(item.Group);
combinedPage.Entries.AddRange(entries);
combinedPage.ModSearchTerms.UnionWith(modSearchTerms);
combinedPage.Order = Math.Min(combinedPage.Order, item.Group.MatchInfoGuidePageOrder);
combinedPage.MenuOrder = Math.Min(
combinedPage.MenuOrder,
GetMenuOrder(item.Group.ParentMenu));
combinedPage.GroupPriority = Math.Min(
combinedPage.GroupPriority,
item.Group.GroupPriority);
}
else
{
var page = new GuideOptionPage(
item.Group,
item.Group.MatchInfoGuidePageTitle,
item.Group.MatchInfoGuidePageOrder,
GetMenuOrder(item.Group.ParentMenu),
item.Group.GroupPriority,
Mathf.Clamp(item.Group.MatchInfoGuidePageColumns, 1, 6),
item.ModAbbreviation,
modSearchTerms,
entries);
pages.Add(page);
if (combinedKey != null)
{
combinedPages.Add(combinedKey, page);
}
combinedPages.Add(combinedKey, page);
}
}
}
@@ -269,7 +270,7 @@ internal static class GuideOptionPages
}
catch (Exception e)
{
Logger<MiraApiPlugin>.Warning($"GroupVisible threw for {group.GetType().Name}: {e.Message}");
Warning($"GroupVisible threw for {group.GetType().Name}: {e.Message}");
return false;
}
}
@@ -282,7 +283,7 @@ internal static class GuideOptionPages
}
catch (Exception e)
{
Logger<MiraApiPlugin>.Warning($"Visible threw for option '{option.Title}': {e.Message}");
Warning($"Visible threw for option '{option.Title}': {e.Message}");
return false;
}
}
@@ -303,7 +304,7 @@ internal static class GuideOptionPages
}
catch (Exception e)
{
Logger<MiraApiPlugin>.Warning($"Could not format option '{option.Title}' for the guide: {e.Message}");
Warning($"Could not format option '{option.Title}' for the guide: {e.Message}");
return null;
}
}