mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-19 15:51:48 +00:00
Merge branch 'dev' into wiki-improv
This commit is contained in:
19 files changed
+948
-12
No files matched your search
@@ -98,3 +98,6 @@ dotnet_diagnostic.IDE0045.severity = none
|
||||
|
||||
# IDE0046: Convert to conditional expression
|
||||
dotnet_diagnostic.IDE0046.severity = none
|
||||
|
||||
# S1244: Floating point numbers should not be tested for equality
|
||||
dotnet_diagnostic.S1244.severity = none
|
||||
@@ -1,4 +1,6 @@
|
||||
using MiraAPI.Colors;
|
||||
using MiraAPI.Colors.ColorSpaces;
|
||||
using MiraAPI.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example;
|
||||
@@ -14,4 +16,9 @@ public static class ExampleColors
|
||||
public static CustomColor Rose { get; } = new("color.rose", new Color(0.98f, 0.26f, 0.62f));
|
||||
|
||||
public static CustomColor Gold { get; } = new("color.gold", new Color(1.0f, 0.84f, 0.0f));
|
||||
|
||||
public static VariableColor Rainbow { get; } = new("color.rainbow", t => new HsbColor(MathUtilities.PingPong(t, 1f, 0.3f), 1f, 1f))
|
||||
{
|
||||
ColorBrightness = CustomColorBrightness.Lighter,
|
||||
};
|
||||
}
|
||||
@@ -80,4 +80,5 @@ Eliminate every other faction.</string>
|
||||
<string name="color.cerulean">Cerulean</string>
|
||||
<string name="color.rose">Rose</string>
|
||||
<string name="color.gold">Gold</string>
|
||||
<string name="color.rainbow">Rainbow</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using MiraAPI.Utilities;
|
||||
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 key = 1f - cmyk.k;
|
||||
|
||||
var r = (1f - cmyk.c) * key;
|
||||
var g = (1f - cmyk.m) * key;
|
||||
var b = (1f - cmyk.y) * key;
|
||||
|
||||
return new Color(MathUtilities.Clamp01(r), MathUtilities.Clamp01(g), MathUtilities.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, MathF.Max(g, b));
|
||||
|
||||
if (k >= 1f)
|
||||
return new CmykColor(0f, 0f, 0f, 1f, color.a);
|
||||
|
||||
var invK = 1f / (1f - k);
|
||||
|
||||
var c = (1f - r - k) * invK;
|
||||
var m = (1f - g - k) * invK;
|
||||
var y = (1f - b - k) * invK;
|
||||
|
||||
return new CmykColor(MathUtilities.Clamp01(c), MathUtilities.Clamp01(m), MathUtilities.Clamp01(y), MathUtilities.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)
|
||||
{
|
||||
return LerpUnclamped(a, b, MathUtilities.Clamp01(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(
|
||||
MathUtilities.LerpUnclamped(a.c, b.c, t),
|
||||
MathUtilities.LerpUnclamped(a.m, b.m, t),
|
||||
MathUtilities.LerpUnclamped(a.y, b.y, t),
|
||||
MathUtilities.LerpUnclamped(a.k, b.k, t),
|
||||
MathUtilities.LerpUnclamped(a.a, b.a, t));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using MiraAPI.Utilities;
|
||||
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)
|
||||
{
|
||||
if (hsb.s <= float.Epsilon)
|
||||
return new Color(MathUtilities.Clamp01(hsb.b), MathUtilities.Clamp01(hsb.b), MathUtilities.Clamp01(hsb.b), hsb.a);
|
||||
|
||||
var c = hsb.b * hsb.s;
|
||||
var hPrime = hsb.h * 6f;
|
||||
var x = c * (1f - MathF.Abs((hPrime % 2f) - 1f));
|
||||
var m = hsb.b - c;
|
||||
|
||||
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(
|
||||
MathUtilities.Clamp01(r + m),
|
||||
MathUtilities.Clamp01(g + m),
|
||||
MathUtilities.Clamp01(b + m),
|
||||
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)
|
||||
{
|
||||
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 delta = max - min;
|
||||
|
||||
var h = 0f;
|
||||
|
||||
if (delta > 0f)
|
||||
{
|
||||
if (max == color.r)
|
||||
h = (color.g - color.b) / delta % 6f;
|
||||
else if (max == color.g)
|
||||
h = (color.b - color.r) / delta + 2f;
|
||||
else
|
||||
h = (color.r - color.g) / delta + 4f;
|
||||
|
||||
h /= 6f;
|
||||
|
||||
if (h < 0f)
|
||||
h += 1f;
|
||||
}
|
||||
|
||||
var s = max == 0f ? 0f : delta / max;
|
||||
var b = max;
|
||||
|
||||
return new HsbColor(h, s, b, 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)
|
||||
{
|
||||
return LerpUnclamped(a, b, MathUtilities.Clamp01(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 = MathUtilities.Repeat(b.h - a.h, 1f);
|
||||
|
||||
if (hueDifference > 0.5f)
|
||||
hueDifference -= 1f;
|
||||
|
||||
return new HsbColor(
|
||||
MathUtilities.Repeat(a.h + (hueDifference * t), 1f),
|
||||
MathUtilities.LerpUnclamped(a.s, b.s, t),
|
||||
MathUtilities.LerpUnclamped(a.b, b.b, t),
|
||||
MathUtilities.LerpUnclamped(a.a, b.a, t));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using MiraAPI.Utilities;
|
||||
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 <= float.Epsilon)
|
||||
return new Color(MathUtilities.Clamp01(hsl.l), MathUtilities.Clamp01(hsl.l), MathUtilities.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(MathUtilities.Clamp01(r + m), MathUtilities.Clamp01(g + m), MathUtilities.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));
|
||||
|
||||
var h = 0f;
|
||||
|
||||
if (delta > 0f)
|
||||
{
|
||||
if (max == color.r)
|
||||
h = (color.g - color.b) / delta % 6f;
|
||||
else if (max == color.g)
|
||||
h = (color.b - color.r) / delta + 2f;
|
||||
else
|
||||
h = (color.r - color.g) / delta + 4f;
|
||||
|
||||
h /= 6f;
|
||||
|
||||
if (h < 0f)
|
||||
h += 1f;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return LerpUnclamped(a, b, MathUtilities.Clamp01(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 = MathUtilities.Repeat(b.h - a.h, 1f);
|
||||
|
||||
if (hueDifference > 0.5f)
|
||||
hueDifference -= 1f;
|
||||
|
||||
return new HslColor(
|
||||
MathUtilities.Repeat(a.h + (hueDifference * t), 1f),
|
||||
MathUtilities.LerpUnclamped(a.s, b.s, t),
|
||||
MathUtilities.LerpUnclamped(a.l, b.l, t),
|
||||
MathUtilities.LerpUnclamped(a.a, b.a, t));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using MiraAPI.Translation;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using MiraAPI.Translation;
|
||||
using MiraAPI.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -10,17 +11,37 @@ namespace MiraAPI.Colors;
|
||||
/// <param name="name">The name of the option.</param>
|
||||
/// <param name="mainColor">The main color.</param>
|
||||
/// <param name="shadowColor">The shadow color.</param>
|
||||
public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColor)
|
||||
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Internal code.")]
|
||||
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1513:Closing brace should be followed by blank line", Justification = "Looks weird.")]
|
||||
public class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColor)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the main color.
|
||||
/// </summary>
|
||||
public Color32 MainColor { get; set; } = mainColor;
|
||||
public Color32 MainColor
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
InternalMainColor = value;
|
||||
}
|
||||
}
|
||||
= mainColor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shadow color.
|
||||
/// </summary>
|
||||
public Color32 ShadowColor { get; set; } = shadowColor;
|
||||
public Color32 ShadowColor
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
InternalShadowColor = value;
|
||||
}
|
||||
}
|
||||
= shadowColor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the color.
|
||||
@@ -32,6 +53,9 @@ public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 sha
|
||||
/// </summary>
|
||||
public CustomColorBrightness ColorBrightness { get; set; } = CustomColorBrightness.Darker;
|
||||
|
||||
internal Color InternalMainColor = mainColor;
|
||||
internal Color InternalShadowColor = shadowColor;
|
||||
|
||||
/// <inheritdoc cref="CustomColor"/>
|
||||
public CustomColor(StringNames name, Color32 mainColor)
|
||||
: this(name, mainColor, mainColor.GetShadowColor(60))
|
||||
@@ -56,6 +80,11 @@ public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 sha
|
||||
/// </summary>
|
||||
public enum CustomColorBrightness
|
||||
{
|
||||
/// <summary>
|
||||
/// An invalid color.
|
||||
/// </summary>
|
||||
Invalid = -1,
|
||||
|
||||
/// <summary>
|
||||
/// A lighter color.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Colors;
|
||||
|
||||
@@ -15,9 +17,36 @@ public static class PaletteManager
|
||||
public static CustomColor[] RegisteredColors => [.. CustomColors];
|
||||
|
||||
internal static readonly List<CustomColor> CustomColors = [];
|
||||
internal static readonly HashSet<StringNames> VariableColorNames = [];
|
||||
|
||||
private static readonly CustomColorBrightness[] ColorBrightnessMap =
|
||||
[
|
||||
CustomColorBrightness.Darker, // Red
|
||||
CustomColorBrightness.Darker, // Blue
|
||||
CustomColorBrightness.Darker, // Green
|
||||
CustomColorBrightness.Lighter, // Pink
|
||||
CustomColorBrightness.Lighter, // Orange
|
||||
CustomColorBrightness.Lighter, // Yellow
|
||||
CustomColorBrightness.Darker, // Black
|
||||
CustomColorBrightness.Lighter, // White
|
||||
CustomColorBrightness.Darker, // Purple
|
||||
CustomColorBrightness.Darker, // Brown
|
||||
CustomColorBrightness.Lighter, // Cyan
|
||||
CustomColorBrightness.Lighter, // Lime
|
||||
CustomColorBrightness.Darker, // Maroon
|
||||
CustomColorBrightness.Lighter, // Rose
|
||||
CustomColorBrightness.Lighter, // Banana
|
||||
CustomColorBrightness.Darker, // Gray
|
||||
CustomColorBrightness.Darker, // Tan
|
||||
CustomColorBrightness.Lighter, // Coral
|
||||
];
|
||||
|
||||
private static int originalLength;
|
||||
|
||||
internal static void RegisterAllColors()
|
||||
{
|
||||
originalLength = Palette.PlayerColors.Length;
|
||||
|
||||
var colors = CustomColors.Select(x => x.MainColor).ToArray();
|
||||
var shadowColors = CustomColors.Select(x => x.ShadowColor).ToArray();
|
||||
var stringNames = CustomColors.Select(x => x.Name).ToArray();
|
||||
@@ -29,4 +58,90 @@ public static class PaletteManager
|
||||
Palette.TextColors = Palette.TextColors.ToArray().AddRangeToArray(colors);
|
||||
Palette.TextOutlineColors = Palette.TextOutlineColors.ToArray().AddRangeToArray(shadowColors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the provided color ID belongs to a variable 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)
|
||||
{
|
||||
if ((uint)colorId >= (uint)Palette.ColorNames.Length)
|
||||
return false;
|
||||
|
||||
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 (uint)(colorId - originalLength) < (uint)CustomColors.Count;
|
||||
}
|
||||
|
||||
/// <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 Color GetMainColor(int colorId)
|
||||
{
|
||||
if ((uint)colorId >= (uint)Palette.ColorNames.Length)
|
||||
return Color.white;
|
||||
|
||||
return !TryGetCustomColor(colorId, out var custom)
|
||||
? Palette.PlayerColors[colorId]
|
||||
: (custom is VariableColor variable
|
||||
? variable.EvaluateMainColor(Time.time)
|
||||
: custom.InternalMainColor);
|
||||
}
|
||||
|
||||
/// <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 Color GetShadowColor(int colorId)
|
||||
{
|
||||
if ((uint)colorId >= (uint)Palette.ColorNames.Length)
|
||||
return Color.white;
|
||||
|
||||
return !TryGetCustomColor(colorId, out var custom)
|
||||
? Palette.ShadowColors[colorId]
|
||||
: (custom is VariableColor variable
|
||||
? variable.EvaluateShadowColor(Time.time)
|
||||
: custom.InternalShadowColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brightness type of the color associated with the provided ID.
|
||||
/// </summary>
|
||||
/// <param name="colorId">The ID of the color.</param>
|
||||
/// <returns>The brightness type of the provided ID.</returns>
|
||||
public static CustomColorBrightness GetBrightness(int colorId)
|
||||
{
|
||||
if ((uint)colorId >= (uint)Palette.ColorNames.Length)
|
||||
return CustomColorBrightness.Invalid;
|
||||
|
||||
return TryGetCustomColor(colorId, out var custom)
|
||||
? custom.ColorBrightness
|
||||
: ColorBrightnessMap[colorId];
|
||||
}
|
||||
|
||||
internal static bool TryGetCustomColor(int colorId, [NotNullWhen(true)] out CustomColor? custom)
|
||||
{
|
||||
var index = colorId - originalLength;
|
||||
|
||||
if ((uint)index >= (uint)CustomColors.Count)
|
||||
{
|
||||
custom = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
custom = CustomColors[index];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
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>
|
||||
/// <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))
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a delegate that takes in a time value and returns the dynamic main color.
|
||||
/// </summary>
|
||||
public Func<float, Color> MainEvaluator { get; set; } = mainEvaluator;
|
||||
|
||||
/// <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));
|
||||
|
||||
/// <inheritdoc cref="VariableColor"/>
|
||||
public VariableColor(StringNames name, Func<float, Color> mainEvaluator, Color32? mainColor = null, float defaultTime = 0f)
|
||||
: this(name, mainEvaluator, null, mainColor, null, defaultTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="VariableColor"/>
|
||||
public VariableColor(string name, Func<float, Color> mainEvaluator, Color32? mainColor = null, float defaultTime = 0f)
|
||||
: this(name, mainEvaluator, null, mainColor, null, defaultTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="VariableColor"/>
|
||||
public VariableColor(string name, Func<float, Color> mainEvaluator, Func<float, Color>? shadowEvaluator, Color32? mainColor = null, Color32? shadowColor = null, float defaultTime = 0f)
|
||||
: this(MiraLocaleManager.GetOrCreateLocaleString(name), mainEvaluator, shadowEvaluator, mainColor, shadowColor, defaultTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the dynamic main color based on the provided time value.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to evaluate the color against.</param>
|
||||
/// <returns>The dynamically evaluated color, or the static <see cref="CustomColor.MainColor"/> if static variable colors are enabled in the user's settings.</returns>
|
||||
public Color EvaluateMainColor(float time)
|
||||
{
|
||||
return LocalSettingsTabSingleton<MiraApiSettings>.Instance.EnableVariableColors.Value
|
||||
? MainEvaluator(time)
|
||||
: InternalMainColor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the dynamic shadow color based on the provided time value.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to evaluate the color against.</param>
|
||||
/// <returns>The dynamically evaluated color, or the static <see cref="CustomColor.ShadowColor"/> if static variable colors are enabled in the user's settings.</returns>
|
||||
public Color EvaluateShadowColor(float time)
|
||||
{
|
||||
return LocalSettingsTabSingleton<MiraApiSettings>.Instance.EnableVariableColors.Value
|
||||
? ShadowEvaluator(time)
|
||||
: InternalShadowColor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.Colors;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI;
|
||||
|
||||
/// <summary>
|
||||
/// The base class for variable color behaviour on <see cref="SpriteRenderer"/>s.
|
||||
/// </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 abstract class BaseVariableColorBehaviour : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="VariableColor"/> to apply.
|
||||
/// </summary>
|
||||
protected VariableColor? color;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current variable color.
|
||||
/// </summary>
|
||||
/// <param name="newColor">The new <see cref="VariableColor"/>.</param>
|
||||
[HideFromIl2Cpp]
|
||||
public void SetColor(VariableColor? newColor)
|
||||
{
|
||||
color = newColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A mono script that adjusts the material colors of a <see cref="Renderer"/> component that it's attached to based on the provided variable color.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
[SuppressMessage("Style", "IDE0051:Remove unused private members", Justification = "Unity methods.")]
|
||||
public sealed class VariableColorBehaviour : BaseVariableColorBehaviour
|
||||
{
|
||||
private Renderer? rend;
|
||||
|
||||
private static readonly Color VisorColor = Palette.VisorColor;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the new <see cref="Renderer"/> target.
|
||||
/// </summary>
|
||||
/// <param name="newRend">The new <see cref="Renderer"/> that the script must target.</param>
|
||||
public void SetRend(Renderer? newRend)
|
||||
{
|
||||
rend = newRend;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
rend = GetComponent<Renderer>();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (color == null || rend == null)
|
||||
return;
|
||||
|
||||
var time = Time.time;
|
||||
var material = rend.material;
|
||||
|
||||
material.SetColor(ShaderID.VisorColor, VisorColor);
|
||||
|
||||
material.SetColor(ShaderID.BodyColor, color.EvaluateMainColor(time));
|
||||
material.SetColor(ShaderID.BackColor, color.EvaluateShadowColor(time));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A mono script that adjusts the graphic colors of a <see cref="SpriteRenderer"/> component that it's attached to based on the provided variable color.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
[SuppressMessage("Style", "IDE0051:Remove unused private members", Justification = "Unity methods.")]
|
||||
public sealed class BasicVariableColorBehaviour : BaseVariableColorBehaviour
|
||||
{
|
||||
private SpriteRenderer? rend;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the new <see cref="SpriteRenderer"/> target.
|
||||
/// </summary>
|
||||
/// <param name="newRend">The new <see cref="SpriteRenderer"/> that the script must target.</param>
|
||||
public void SetRend(SpriteRenderer? newRend)
|
||||
{
|
||||
rend = newRend;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
rend = GetComponent<SpriteRenderer>();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (color != null && rend != null)
|
||||
rend.color = color.EvaluateMainColor(Time.time);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using MiraAPI.Patches.Stubs;
|
||||
using MiraAPI.Translation;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
@@ -247,8 +248,7 @@ public abstract class CustomPaginableMenu : CustomPhoneMenu<CustomPaginableMenu.
|
||||
|
||||
var wikiClickSound = HudManager.Instance?.MapButton?.ClickSound;
|
||||
|
||||
var searchFocusButton = searchTextbox.gameObject.GetComponent<PassiveButton>()
|
||||
?? searchTextbox.gameObject.AddComponent<PassiveButton>();
|
||||
var searchFocusButton = searchTextbox.gameObject.GetOrAddComponent<PassiveButton>();
|
||||
if (wikiClickSound != null)
|
||||
{
|
||||
searchFocusButton.ClickSound = wikiClickSound;
|
||||
|
||||
@@ -243,4 +243,10 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
|
||||
/// </summary>
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> EnableSabotageBlares { get; private set; } = config.Bind("MiraApi.Accessibility", "MiraApi.EnableSabotageBlare", true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether variable colors will remain static or not.
|
||||
/// </summary>
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> EnableVariableColors { get; private set; } = config.Bind("MiraApi.Accessibility", "MiraApi.EnableVariableColors", true);
|
||||
}
|
||||
@@ -13,4 +13,4 @@ dotnet_diagnostic.SA1611.severity = none
|
||||
dotnet_diagnostic.SA1615.severity = none
|
||||
|
||||
# SA1602: Enumeration items should be documented.
|
||||
dotnet_diagnostic.SA1602.severity = none
|
||||
dotnet_diagnostic.SA1602.severity = none
|
||||
@@ -0,0 +1,110 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
using AmongUs.Data;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Colors;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Colors;
|
||||
|
||||
[HarmonyPatch(typeof(PlayerMaterial))]
|
||||
internal static class SetPlayerMaterialPatch
|
||||
{
|
||||
[HarmonyPatch(nameof(PlayerMaterial.SetColors), typeof(int), typeof(Renderer))]
|
||||
public static bool Prefix(int colorId, Renderer rend)
|
||||
{
|
||||
rend.gameObject.GetOrAddComponent<VariableColorBehaviour>().SetColor(
|
||||
PaletteManager.TryGetCustomColor(colorId, out var color) && color is VariableColor variable
|
||||
? variable
|
||||
: null);
|
||||
return !PaletteManager.IsVariable(colorId);
|
||||
}
|
||||
|
||||
[HarmonyPatch(nameof(PlayerMaterial.SetColors), typeof(Color), typeof(Renderer))]
|
||||
public static void Prefix(Renderer rend)
|
||||
{
|
||||
rend.gameObject.GetOrAddComponent<VariableColorBehaviour>().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
|
||||
{
|
||||
private static readonly Regex RichTags = new(@"<[^>]*>", default, Regex.InfiniteMatchTimeout);
|
||||
|
||||
public static void Postfix(ChatNotification __instance)
|
||||
{
|
||||
if (!__instance.gameObject.active)
|
||||
return;
|
||||
|
||||
var colorId = __instance.player.cosmetics.ColorId;
|
||||
|
||||
if (!PaletteManager.IsVariable(colorId))
|
||||
return;
|
||||
|
||||
var color = PaletteManager.GetMainColor(colorId);
|
||||
var str = color.ToHtmlStringRGBA();
|
||||
var nameText = __instance.playerNameText;
|
||||
nameText.text = "<color=#" + str + ">" + RichTags.Replace(nameText.text, string.Empty);
|
||||
nameText.outlineColor = color.GetShadowColor(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
[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)
|
||||
return;
|
||||
|
||||
var colorId = __instance.player.cosmetics.ColorId;
|
||||
|
||||
if (!PaletteManager.IsVariable(colorId))
|
||||
return;
|
||||
|
||||
var host = GameData.Instance.GetHost();
|
||||
|
||||
if (!host)
|
||||
return;
|
||||
|
||||
var text = PaletteManager.GetMainColor(colorId).ToHtmlStringRGBA();
|
||||
var label = __instance.hostLabel;
|
||||
label.text = TranslationController.Instance.GetString(StringNames.HostNounLabel);
|
||||
|
||||
if (__instance.ShouldBoldenHostLabel(DataManager.Settings.Language.CurrentLanguage))
|
||||
{
|
||||
label.text = "<b>" + label.text + "</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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))))
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<string name="MiraApi.WikiOnBottomRow">Display Wiki on Bottom Row</string>
|
||||
<string name="MiraApi.EnableSabotageFlashes">Enable Sabotage Flashes</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.CustomCategory1">Custom Category 1</string>
|
||||
<string name="MiraApi.CustomCategory2">Custom Category 2</string>
|
||||
|
||||
@@ -620,7 +620,23 @@ public static class Extensions
|
||||
(byte)Mathf.Clamp(color.r - darknessAmount, 0, 255),
|
||||
(byte)Mathf.Clamp(color.g - darknessAmount, 0, 255),
|
||||
(byte)Mathf.Clamp(color.b - darknessAmount, 0, 255),
|
||||
byte.MaxValue);
|
||||
color.a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a darkened version of a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -796,7 +812,7 @@ public static class Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixed version of <see cref="Reactor.Utilities.Extensions.UnityExtensions.SetOutline(Renderer, Color?)"/>.
|
||||
/// Fixed version of <see cref="UnityExtensions.SetOutline(Renderer, Color?)"/>.
|
||||
/// </summary>
|
||||
/// <param name="renderer">The <see cref="Renderer"/> you want to update the outline for.</param>
|
||||
/// <param name="color">The outline <see cref="Color"/>.</param>
|
||||
@@ -855,4 +871,17 @@ public static class Extensions
|
||||
userData.joystickMaps[0].actionElementMaps.Add(map);
|
||||
return action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a component that is attached to the provided <see cref="GameObject"/>, attaching and returning the component if it wasn't found.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The component type.</typeparam>
|
||||
/// <param name="go">The <see cref="GameObject"/> to get the component from.</param>
|
||||
/// <returns>The component.</returns>
|
||||
public static T GetOrAddComponent<T>(this GameObject go)
|
||||
where T : Component
|
||||
{
|
||||
var comp = go.GetComponent<T>();
|
||||
return comp ? comp : go.AddComponent<T>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Provides mathematical utility methods.
|
||||
/// </summary>
|
||||
public static class MathUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Linearly interpolates between two values, clamping the interpolant between 0 and 1.
|
||||
/// </summary>
|
||||
/// <param name="a">The starting value.</param>
|
||||
/// <param name="b">The ending value.</param>
|
||||
/// <param name="t">The interpolation value, automatically clamped to a 0 to 1 range.</param>
|
||||
/// <returns>The interpolated float.</returns>
|
||||
public static float Lerp(float a, float b, float t)
|
||||
{
|
||||
return a + ((b - a) * Clamp01(t));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linearly interpolates between two values without clamping the interpolant.
|
||||
/// </summary>
|
||||
/// <param name="a">The starting value.</param>
|
||||
/// <param name="b">The ending value.</param>
|
||||
/// <param name="t">The interpolation value.</param>
|
||||
/// <returns>The interpolated float.</returns>
|
||||
public static float LerpUnclamped(float a, float b, float t)
|
||||
{
|
||||
return a + ((b - a) * t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps a floating-point value strictly between 0 and 1.
|
||||
/// </summary>
|
||||
/// <param name="value">The floating-point value to clamp.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static float Clamp01(float value)
|
||||
{
|
||||
return Math.Clamp(value, 0f, 1f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ping-pongs a value back and forth between a minimum and maximum value.
|
||||
/// </summary>
|
||||
/// <param name="t">The input time or interpolant.</param>
|
||||
/// <param name="min">The minimum boundary of the ping-pong wave.</param>
|
||||
/// <param name="max">The maximum boundary of the ping-pong wave.</param>
|
||||
/// <param name="speed">The multiplier for the speed of the ping-pong effect.</param>
|
||||
/// <returns>A value oscillating continuously between <paramref name="min"/> and <paramref name="max"/>.</returns>
|
||||
public static float PingPong(float t, float min, float max, float speed = 1f)
|
||||
{
|
||||
return min + PingPong(t, max - min, speed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ping-pongs a value back and forth between 0 and a specified length.
|
||||
/// </summary>
|
||||
/// <param name="t">The input time or interpolant.</param>
|
||||
/// <param name="length">The maximum value to bounce against, starting from 0.</param>
|
||||
/// <param name="speed">The multiplier for the speed of the ping-pong effect.</param>
|
||||
/// <returns>A value oscillating continuously between 0 and <paramref name="length"/>.</returns>
|
||||
public static float PingPong(float t, float length, float speed = 1f)
|
||||
{
|
||||
var dx = speed * t;
|
||||
var floor = (int)MathF.Floor(dx);
|
||||
var phase = floor % 2; // 0 = forward, 1 = backward
|
||||
return Clamp01(((dx - floor) * ((2 * phase) - 1)) + 1 - phase) * length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loops a value strictly between 0 and a maximum length, behaving like a standard float modulo.
|
||||
/// </summary>
|
||||
/// <param name="t">The input value to loop.</param>
|
||||
/// <param name="length">The maximum length of the repeating sequence.</param>
|
||||
/// <returns>The wrapped value smoothly looping back to 0 upon reaching <paramref name="length"/>.</returns>
|
||||
public static float Repeat(float t, float length)
|
||||
{
|
||||
return Math.Clamp(t - (MathF.Floor(t / length) * length), 0f, length);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
[](https://discord.gg/AEfHJGwggC)
|
||||
|
||||
[English] | [简体中文](Translated/README_zh.md)
|
||||
|
||||
[](https://starlight.allofus.dev/mods/mira.api)
|
||||
[](https://discord.gg/AEfHJGwggC)
|
||||
|
||||
> This mod is not affiliated with Among Us or Innersloth LLC, and the content contained therein is not endorsed or otherwise sponsored by Innersloth LLC. Portions of the materials contained herein are property of Innersloth LLC. © Innersloth LLC.
|
||||
|
||||
# Mira API
|
||||
|
||||
Reference in new issue
Block a user