Merge branch 'dev' into controller-support

This commit is contained in:
Az committed 2026-10-15 21:30:54 -04:00
commit e0db03b576
32 files changed
+1057 -116

No files matched your search

+15
View File
@@ -86,3 +86,18 @@ dotnet_diagnostic.SA1133.severity = none
# IDE0001: Simplify Names
dotnet_diagnostic.IDE0001.severity = warning
# IDE0079: Remove unnecessary suppression
dotnet_diagnostic.IDE0079.severity = none
# S3903: Types should be defined in named namespaces
dotnet_diagnostic.S3903.severity = none
# IDE0045: Convert to conditional expression
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
+4
View File
@@ -0,0 +1,4 @@
[*.{cs,vb}]
# SA1602: Enumeration items should be documented.
dotnet_diagnostic.SA1602.severity = none
+7
View File
@@ -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,
};
}
+7 -6
View File
@@ -16,11 +16,11 @@
Your votes have double the power.</string>
<string name="ApiExample.Role.Crewmate.Mayor.TabDescription">You get one extra vote in the meeting!</string>
<string name="ApiExample.Role.Crewmate.Mayor.WikiDescription">The Mayor is a Crewmate role that gets two votes in the meeting, instead of the singular vote other Crewmates get.</string>
<string name="ApiExample.Role.Crewmate.Chamelon">Chamelon</string>
<string name="ApiExample.Role.Crewmate.Chamelon.IntroBlurb">Become one with the environment.</string>
<string name="ApiExample.Role.Crewmate.Chamelon.MedDescription">Fade out of everyone's vision by not moving.</string>
<string name="ApiExample.Role.Crewmate.Chamelon.TabDescription">Stay invisible while not moving.</string>
<string name="ApiExample.Role.Crewmate.Chamelon.WikiDescription">The Chamelon is a Crewmate role that slowly becomes invisible while standing still.</string>
<string name="ApiExample.Role.Crewmate.Chameleon">Chameleon</string>
<string name="ApiExample.Role.Crewmate.Chameleon.IntroBlurb">Become one with the environment.</string>
<string name="ApiExample.Role.Crewmate.Chameleon.MedDescription">Fade out of everyone's vision by not moving.</string>
<string name="ApiExample.Role.Crewmate.Chameleon.TabDescription">Stay invisible while not moving.</string>
<string name="ApiExample.Role.Crewmate.Chameleon.WikiDescription">The Chameleon is a Crewmate role that slowly becomes invisible while standing still.</string>
<string name="ApiExample.Role.Outcast.OutcastKiller">Outcast Killer</string>
<string name="ApiExample.Role.Outcast.OutcastKiller.IntroBlurb">Kill everyone else.</string>
<string name="ApiExample.Role.Outcast.OutcastKiller.MedDescription">Be the last outcast standing.
@@ -73,11 +73,12 @@ Eliminate every other faction.</string>
<string name="settings.miraExample.exampleBool2">Example Bool 2</string>
<string name="settings.miraExample.slider">Slider</string>
<string name="settings.miraExample.exampleNumber">Example Number</string>
<string name="settings.miraExample.exampleMultiplier">Example Multplier</string>
<string name="settings.miraExample.exampleMultiplier">Example Multiplier</string>
<string name="settings.miraExample.exampleEnum">Example Enum</string>
<string name="settings.miraExample.button.exampleButton">Example Button</string>
<string name="gameover.outcastKiller.win">Outcast Killer Wins!</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>
+107
View File
@@ -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));
}
}
+136
View File
@@ -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));
}
}
+132
View File
@@ -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));
}
}
+33 -4
View File
@@ -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>
+115
View File
@@ -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;
}
}
+72
View File
@@ -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;
}
}
+102
View File
@@ -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);
}
}
-2
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using AmongUs.Data;
@@ -462,7 +461,6 @@ public class HideAndSeekMode : AbstractGameMode
}
/// <inheritdoc/>
[SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "Warning cascades into forcing the entire tree to be ternary operators.")]
public override PlayerBodyTypes GetBodyType(PlayerControl player)
{
if (player == null || player.Data == null || player.Data.Role == null)
@@ -43,7 +43,7 @@ public class ModdedPlayerOption : ModdedOption<int>
data.Title = StringName;
data.Type = global::OptionTypes.Player;
Values = new();
Values = [];
AllowNone = allowNone;
Value = DefaultValue;
}
+2 -3
View File
@@ -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;
@@ -100,7 +101,6 @@ public abstract class CustomPaginableMenu : CustomPhoneMenu<CustomPaginableMenu.
: text.Trim().ToLowerInvariant();
}
[SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "Creates a rather large ternary operation.")]
private List<MenuEntry> GetFilteredEntries()
{
var query = NormalizeForSearch(searchText);
@@ -248,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;
+3 -5
View File
@@ -64,11 +64,9 @@ public partial class MiraApiPlugin : BasePlugin, IMiraPlugin
private static MiraPluginManager? PluginManager { get; set; }
internal Harmony Harmony { get; } = new(Id);
public static ConfigEntry<SortingGroups> WikiSortingGroup { get; private set; }
public static ConfigEntry<SortingMethod> WikiSortingMethod { get; private set; }
public static ConfigEntry<SortingFilter> WikiSortingFilter { get; private set; }
internal static ConfigEntry<SortingGroups> WikiSortingGroup { get; private set; }
internal static ConfigEntry<SortingMethod> WikiSortingMethod { get; private set; }
internal static ConfigEntry<SortingFilter> WikiSortingFilter { get; private set; }
/// <inheritdoc />
public override void Load()
+6
View File
@@ -233,4 +233,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);
}
+2 -2
View File
@@ -12,5 +12,5 @@ dotnet_diagnostic.SA1611.severity = none
# SA1615: Element return value should be documented.
dotnet_diagnostic.SA1615.severity = none
# S125: Sections of code should not be commented out.
dotnet_diagnostic.S125.severity = none
# SA1602: Enumeration items should be documented.
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 + ")";
}
}
}
@@ -25,9 +25,9 @@ namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(GameOptionsMenu))]
internal static class GameOptionsMenuPatch
{
private static readonly List<CategoryHeaderMasked> VanillaHeaders = new();
private static readonly List<OptionBehaviour> VanillaOptions = new();
private static readonly List<OptionBehaviour> MainOptions = new();
private static readonly List<CategoryHeaderMasked> VanillaHeaders = [];
private static readonly List<OptionBehaviour> VanillaOptions = [];
private static readonly List<OptionBehaviour> MainOptions = [];
private static readonly Dictionary<AbstractGameMode, List<AbstractOptionGroup>> GameModeGroups = [];
private static readonly Dictionary<AbstractGameMode, List<OptionBehaviour>> GameModeOptions = [];
private static readonly Dictionary<AbstractGameMode, List<CategoryHeaderMasked>> GameModeHeaders = [];
+1 -3
View File
@@ -1,5 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Globalization;
using System.Linq;
using HarmonyLib;
using MiraAPI.GameOptions;
@@ -24,7 +23,6 @@ public static class SettingPatches
/// <returns><see langword="false"/> to skip original method.</returns>
[HarmonyPrefix]
[HarmonyPatch(typeof(FloatGameSetting), nameof(FloatGameSetting.GetValueString))]
[SuppressMessage("Style", "IDE0045:Convert to conditional expression", Justification = "Warning cascades into forcing the entire tree to be ternary operators.")]
public static bool ValueStringPatch(
FloatGameSetting __instance,
ref string __result,
+51 -75
View File
@@ -33,20 +33,13 @@ public static class RoleGuidePatches
private static readonly List<DetailedPanel> RoleEntries = [];
private static readonly List<DetailedPanel> ModifierEntries = [];
public static GameObject SearchComponentContainer;
public static PassiveButton SearchIconButton;
private static GameObject searchComponentContainer;
private static SpriteRenderer _searchIconIdle;
private static SpriteRenderer _searchIconHover;
public static TextMeshPro OrderSortingTmp;
public static PassiveButton SearchSortingOrderButton;
private static SpriteRenderer _searchSortingOrderIdle;
private static SpriteRenderer _searchSortingOrderHover;
public static TextMeshPro GroupSortingTmp;
public static PassiveButton SearchSortingGroupButton;
private static SpriteRenderer _searchSortingGroupIdle;
private static SpriteRenderer _searchSortingGroupHover;
public static TextMeshPro FilterSortingTmp;
public static PassiveButton SearchSortingFilterButton;
private static SpriteRenderer _searchSortingFilterIdle;
private static SpriteRenderer _searchSortingFilterHover;
private static Scroller _rolesScroller;
@@ -153,10 +146,6 @@ public static class RoleGuidePatches
_advancedInfoTabScroller = null!;
_currentAdvancedTabObject = null!;
_loadingSpinner = null;
SearchIconButton = null!;
SearchSortingOrderButton = null!;
SearchSortingGroupButton = null!;
SearchSortingFilterButton = null!;
_searchIconIdle = null!;
_searchIconHover = null!;
_searchSortingOrderIdle = null!;
@@ -260,6 +249,7 @@ public static class RoleGuidePatches
_titleText.transform.GetComponent<TextTranslatorTMP>().Destroy();
}
}
if (!_titleIcon)
{
var titleIconObj = new GameObject("TitleIcon");
@@ -369,18 +359,18 @@ public static class RoleGuidePatches
_guide = instance;
_loading = false;
SearchComponentContainer = new GameObject();
SearchComponentContainer.transform.SetParent(instance.MatchInfoParent.transform);
SearchComponentContainer.transform.localPosition = Vector3.zero;
instance.MatchInfoParent.transform.GetChild(1).transform.SetParent(SearchComponentContainer.transform);
searchComponentContainer = new GameObject();
searchComponentContainer.transform.SetParent(instance.MatchInfoParent.transform);
searchComponentContainer.transform.localPosition = Vector3.zero;
instance.MatchInfoParent.transform.GetChild(1).transform.SetParent(searchComponentContainer.transform);
var sortingOrderButton = Object.Instantiate(
HudManager.Instance.SettingsButton,
SearchComponentContainer.transform);
searchComponentContainer.transform);
sortingOrderButton.gameObject.SetActive(true);
SearchSortingOrderButton = sortingOrderButton.GetComponent<PassiveButton>();
var searchSortingOrderButton = sortingOrderButton.GetComponent<PassiveButton>();
SetupSearchButton(
SearchSortingOrderButton,
searchSortingOrderButton,
"SearchSortingOrderButton",
SwitchMethod,
GetSortMethodSprite(_sortMethod, false),
@@ -391,11 +381,11 @@ public static class RoleGuidePatches
var sortingGroupButton = Object.Instantiate(
sortingOrderButton,
SearchComponentContainer.transform);
searchComponentContainer.transform);
sortingGroupButton.gameObject.SetActive(true);
SearchSortingGroupButton = sortingGroupButton.GetComponent<PassiveButton>();
var searchSortingGroupButton = sortingGroupButton.GetComponent<PassiveButton>();
SetupSearchButton(
SearchSortingGroupButton,
searchSortingGroupButton,
"SearchSortingGroupButton",
SwitchGrouping,
GetSortGroupSprite(_sortGrouping, false),
@@ -406,11 +396,11 @@ public static class RoleGuidePatches
var sortingFilterButton = Object.Instantiate(
sortingOrderButton,
SearchComponentContainer.transform);
searchComponentContainer.transform);
sortingFilterButton.gameObject.SetActive(true);
SearchSortingFilterButton = sortingFilterButton.GetComponent<PassiveButton>();
var searchSortingFilterButton = sortingFilterButton.GetComponent<PassiveButton>();
SetupSearchButton(
SearchSortingFilterButton,
searchSortingFilterButton,
"SearchSortingFilterButton",
SwitchFilter,
GetSortFilterSprite(_sortFilter, false),
@@ -463,9 +453,8 @@ public static class RoleGuidePatches
sortingOrderButton,
sortingOrderButton.transform.parent);
searchIconButton.gameObject.SetActive(true);
SearchIconButton = searchIconButton.GetComponent<PassiveButton>();
SetupSearchButton(
SearchIconButton,
searchIconButton.GetComponent<PassiveButton>(),
"SearchIconButton",
ClearSearchText,
MiraAssets.SearchIconIdleSprite,
@@ -476,7 +465,7 @@ public static class RoleGuidePatches
var searchBox = Object.Instantiate(
instance.TabButtons[2].gameObject,
SearchComponentContainer.transform);
searchComponentContainer.transform);
var searchButton = searchBox.GetComponent<MatchInfoGuideTabButton>();
var tmpText = searchButton.transform.GetChild(0).GetComponent<TextMeshPro>();
tmpText.GetComponent<TextTranslatorTMP>().Destroy();
@@ -621,26 +610,26 @@ public static class RoleGuidePatches
modifiersButton.OnClick.AddListener((Action)(() => OpenModifiersTab()));
instance.TabButtons.Add(modifiersButton);
OrderSortingTmp = Object.Instantiate(
var orderSortingTmp = Object.Instantiate(
_titleText,
sortingOrderButton.transform);
OrderSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.OrderLabel");
OrderSortingTmp.fontSize = OrderSortingTmp.fontSizeMax = 3f;
OrderSortingTmp.alignment = TextAlignmentOptions.Center;
OrderSortingTmp.horizontalAlignment = HorizontalAlignmentOptions.Center;
OrderSortingTmp.transform.localPosition = new Vector3(-0.65f, 0.4f, 0);
orderSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.OrderLabel");
orderSortingTmp.fontSize = orderSortingTmp.fontSizeMax = 3f;
orderSortingTmp.alignment = TextAlignmentOptions.Center;
orderSortingTmp.horizontalAlignment = HorizontalAlignmentOptions.Center;
orderSortingTmp.transform.localPosition = new Vector3(-0.65f, 0.4f, 0);
GroupSortingTmp = Object.Instantiate(
OrderSortingTmp,
var groupSortingTmp = Object.Instantiate(
orderSortingTmp,
sortingGroupButton.transform);
OrderSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.GroupingLabel");
GroupSortingTmp.transform.localPosition = new Vector3(-0.65f, 0.4f, 0);
orderSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.GroupingLabel");
groupSortingTmp.transform.localPosition = new Vector3(-0.65f, 0.4f, 0);
FilterSortingTmp = Object.Instantiate(
OrderSortingTmp,
var filterSortingTmp = Object.Instantiate(
orderSortingTmp,
sortingFilterButton.transform);
FilterSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.FilterLabel");
FilterSortingTmp.transform.localPosition = new Vector3(-0.65f, 0.4f, 0);
filterSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.FilterLabel");
filterSortingTmp.transform.localPosition = new Vector3(-0.65f, 0.4f, 0);
playerButton.transform.localPosition = new Vector3(-3.6f, 0.656f, -0.2f);
settingButton.transform.localPosition = new Vector3(-3.6f, 0.056f, -0.2f);
@@ -731,11 +720,17 @@ public static class RoleGuidePatches
}
}
public static void OpenModifiersTab() => OpenTab(ModifiersTabIndex);
public static void OpenModifiersTab()
{
OpenTab(ModifiersTabIndex);
}
public static void OpenRolesTab() => OpenTab(RolesTabIndex);
public static void OpenRolesTab()
{
OpenTab(RolesTabIndex);
}
public sealed class DetailedPanel(string title, string category, string modId, RoleBehaviour? role, GameModifier? modifier)
private sealed class DetailedPanel(string title, string category, string modId, RoleBehaviour? role, GameModifier? modifier)
{
public MatchInfoRolePanel? Panel { get; set; }
public string Title { get; } = title;
@@ -803,14 +798,7 @@ public static class RoleGuidePatches
private static IEnumerator CoPopulate(float delay)
{
if (delay > 0f)
{
yield return new WaitForSeconds(delay);
}
else
{
yield return null;
}
yield return delay > 0f ? new WaitForSeconds(delay) : null;
if (!_guide)
{
@@ -1048,14 +1036,8 @@ public static class RoleGuidePatches
public static void SwitchFilter()
{
var stepUp = (SortingFilter)((int)_sortFilter + 1);
if (Enum.IsDefined(stepUp))
{
_sortFilter = stepUp;
}
else
{
_sortFilter = SortingFilter.EnabledOnly;
}
_sortFilter = Enum.IsDefined(stepUp) ? stepUp : SortingFilter.EnabledOnly;
MiraApiPlugin.WikiSortingFilter.Value = _sortFilter;
@@ -1094,14 +1076,8 @@ public static class RoleGuidePatches
public static void SwitchMethod()
{
var stepUp = (SortingMethod)((int)_sortMethod + 1);
if (Enum.IsDefined(stepUp))
{
_sortMethod = stepUp;
}
else
{
_sortMethod = SortingMethod.Alphabetical;
}
_sortMethod = Enum.IsDefined(stepUp) ? stepUp : SortingMethod.Alphabetical;
MiraApiPlugin.WikiSortingMethod.Value = _sortMethod;
@@ -1288,7 +1264,7 @@ public static class RoleGuidePatches
}
_advancedInfoTabScroller?.gameObject.SetActive(false);
SearchComponentContainer?.SetActive(true);
searchComponentContainer?.SetActive(true);
UpdateSpinner();
}
@@ -1341,9 +1317,9 @@ public static class RoleGuidePatches
_currentAdvancedTabObject.Destroy();
}
if (SearchComponentContainer)
if (searchComponentContainer)
{
SearchComponentContainer.SetActive(false);
searchComponentContainer.SetActive(false);
}
if (_advancedInfoTabScroller == null)
@@ -1543,20 +1519,20 @@ public static class RoleGuidePatches
}
}
public enum SortingGroups
internal enum SortingGroups
{
Ungrouped,
Faction,
AmountChance,
}
public enum SortingMethod
internal enum SortingMethod
{
Alphabetical,
AlphabeticalDescending,
}
public enum SortingFilter
internal enum SortingFilter
{
EnabledOnly,
DisabledOnly,
+2 -2
View File
@@ -39,10 +39,10 @@ public static class RoleBehaviourStubs
}
/// <summary>
/// Stub method for <see cref="RoleBehaviour.AppendTaskHint(StringBuilder)"/>.
/// Stub method for <see cref="RoleBehaviour.AppendTaskHint(Il2CppSystem.Text.StringBuilder)"/>.
/// </summary>
/// <param name="instance">The <see cref="RoleBehaviour"/> object.</param>
/// <param name="taskStringBuilder">The <see cref="StringBuilder"/> to append the task hint to.</param>
/// <param name="taskStringBuilder">The <see cref="Il2CppSystem.Text.StringBuilder"/> to append the task hint to.</param>
[HarmonyReversePatch]
[HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.AppendTaskHint), typeof(Il2CppSystem.Text.StringBuilder))]
[MethodImpl(MethodImplOptions.NoInlining)]
+4 -1
View File
@@ -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))))
+1
View File
@@ -16,6 +16,7 @@ namespace MiraAPI.Presets;
[RegisterInIl2Cpp]
[SuppressMessage("Style", "IDE0051:Remove unused private members", Justification = "Unity convention.")]
[SuppressMessage("Design", "CA1050:Declare types in namespaces", Justification = "Unity script.")]
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member (Justification: Unity fields; ignore.)
public class SavePresetPopup(nint cppPtr) : Minigame(cppPtr)
{
+1
View File
@@ -12,6 +12,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>
+8 -1
View File
@@ -345,7 +345,14 @@ public interface ICustomRole : IOptionable
/// Gets the list of abilities or other similar information to display below the text of a role's wiki page.
/// </summary>
[HideFromIl2Cpp]
public List<AdvancedWikiAbilityDescription> WikiAbilities => [];
List<AdvancedWikiAbilityDescription> WikiAbilities => [];
}
/// <summary>
/// A data holder of an ability's wiki entry.
/// </summary>
/// <param name="Name">The name of the ability.</param>
/// <param name="AbilityType">The type of the ability.</param>
/// <param name="Description">The description of the ability.</param>
/// <param name="Icon">The icon sprite of the ability.</param>
public record struct AdvancedWikiAbilityDescription(string Name, string AbilityType, string Description, LoadableAsset<Sprite> Icon);
-2
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -162,7 +161,6 @@ public static class MiraLocaleManager
/// <param name="key">The string id to find.</param>
/// <param name="fallback">Fallback string to use if no translation is found.</param>
/// <returns>A <see cref="string"/> based on the key provided.</returns>
[SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "Warning cascades into forcing the entire tree to be ternary operators.")]
public static string Get(MiraLanguage language, string key, string fallback = "")
{
if (Locale.TryGetValue(language, out var translations) &&
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace MiraAPI.Utilities;
@@ -9,7 +8,6 @@ internal sealed class ControllableComparer<T>(T[] forcedToBottom, T[] forcedToTo
where T : IComparable
{
/// <inheritdoc/>
[SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "Warning cascades into forcing the entire tree to be ternary operators.")]
public int Compare(T? x, T? y)
{
if ((forcedToBottom.Contains(x) && forcedToBottom.Contains(y)) || (forcedToTop.Contains(x) && forcedToTop.Contains(y)))
+35 -1
View File
@@ -567,7 +567,28 @@ public static class Extensions
/// <returns>The darkened <see cref="Color32"/>.</returns>
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), byte.MaxValue);
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);
}
/// <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>
@@ -772,4 +793,17 @@ public static class Extensions
userData.keyboardMaps[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>();
}
}
+12 -1
View File
@@ -32,6 +32,11 @@ public static class Helpers
{
private static readonly Regex TrailingZeroRegex = new(@"\.0+(?!\d)", RegexOptions.Compiled, TimeSpan.FromMilliseconds(100));
/// <summary>
/// Gets all of the options stored within the provided type.
/// </summary>
/// <param name="classType">The type to get the options of.</param>
/// <returns>A readonly list of options from the provided type.</returns>
public static ReadOnlyCollection<IModdedOption>? GetModdedOptionsForType(Type classType)
{
var group = ModdedOptionsManager.Groups.FirstOrDefault(x => x.OptionableType == classType);
@@ -39,6 +44,11 @@ public static class Helpers
return group?.Children;
}
/// <summary>
/// Gets the option text of the provided type.
/// </summary>
/// <param name="classType">The type to get the text for.</param>
/// <returns>The options text of the provided type.</returns>
public static string GetOptionsText(Type classType)
{
var group = ModdedOptionsManager.Groups.FirstOrDefault(x => x.OptionableType == classType);
@@ -836,7 +846,7 @@ public static class Helpers
obj.transform.SetParent(parent.Inner.transform);
obj.transform.localPosition = new Vector3(0f, 0.5f, 0f);
var scrollHeight = variableTextSizing
? (desc.textBounds.size.y - 2.5f) + maxTextSize + 0.475f
? desc.textBounds.size.y - 2.5f + maxTextSize + 0.475f
: desc.textBounds.size.y - 2.5f + Mathf.Ceil(num / 2f) * 1.45f;
parent.SetYBoundsMax(Mathf.Clamp(scrollHeight, 0f, 999f));
return obj;
@@ -857,6 +867,7 @@ public static class Helpers
panel.roleDescription.fontSizeMin = 1.5f;
panel.roleDescription.ForceMeshUpdate();
}
panel.roleIcon.sprite = ability.Icon.LoadAsset();
panel.roleIcon.SetSizeLimit(0.13f);
+82
View File
@@ -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);
}
}
+3 -2
View File
@@ -1,7 +1,8 @@
[![](https://dcbadge.limes.pink/api/server/AEfHJGwggC)](https://discord.gg/AEfHJGwggC)
[English] | [简体中文](Translated/README_zh.md)
[![Starlight badge](https://img.shields.io/badge/Download%20On-Starlight-d3b65e?style=for-the-badge)](https://starlight.allofus.dev/mods/mira.api)
[![](https://dcbadge.limes.pink/api/server/AEfHJGwggC)](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