Merge branch 'dev' into commission

# Conflicts:
#	MiraAPI/Utilities/Extensions.cs
This commit is contained in:
XtraCube committed 2025-06-22 09:01:00 -04:00
commit 16ff2f8492
7 files changed
+215 -57

No files matched your search

@@ -0,0 +1,21 @@
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Invoked before RpcEndGame is called, allowing cancellation of the game end entirely.
/// </summary>
public class BeforeGameEndEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the reason for the game end.
/// </summary>
public GameOverReason Reason { get; }
/// <summary>
/// Initializes a new instance of the <see cref="BeforeGameEndEvent"/> class.
/// </summary>
/// <param name="reason">The reason for the game end.</param>
public BeforeGameEndEvent(GameOverReason reason)
{
Reason = reason;
}
}
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using BepInEx.Configuration; using BepInEx.Configuration;
@@ -175,7 +176,10 @@ public static class ModdedOptionsManager
/// </summary> /// </summary>
/// <param name="__originalMethod">The original setter method.</param> /// <param name="__originalMethod">The original setter method.</param>
/// <param name="value">The new object value.</param> /// <param name="value">The new object value.</param>
#pragma warning disable CA1707
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Harmony naming convention")]
public static void PropertySetterPatch(MethodBase __originalMethod, object value) public static void PropertySetterPatch(MethodBase __originalMethod, object value)
#pragma warning restore CA1707
{ {
var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod() == __originalMethod).Value; var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod() == __originalMethod).Value;
attribute.SetValue(value); attribute.SetValue(value);
@@ -187,7 +191,10 @@ public static class ModdedOptionsManager
/// <param name="__originalMethod">The original getter method.</param> /// <param name="__originalMethod">The original getter method.</param>
/// <param name="__result">The result of the property getter.</param> /// <param name="__result">The result of the property getter.</param>
/// <returns>False so the original getter gets skipped.</returns> /// <returns>False so the original getter gets skipped.</returns>
#pragma warning disable CA1707
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Harmony naming convention")]
public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result) public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result)
#pragma warning restore CA1707
{ {
var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod() == __originalMethod).Value; var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod() == __originalMethod).Value;
__result = attribute.GetValue(); __result = attribute.GetValue();
@@ -113,7 +113,7 @@ public abstract class ModdedOption<T> : IModdedOption
entry.Value = Value; entry.Value = Value;
} }
Rpc<SyncOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [GetNetData()]); Rpc<SyncOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [GetNetData()], true);
} }
OnValueChanged(newValue); OnValueChanged(newValue);
+8 -2
View File
@@ -3,6 +3,7 @@ using System.Globalization;
using MiraAPI.Events; using MiraAPI.Events;
using MiraAPI.Events.Mira; using MiraAPI.Events.Mira;
using MiraAPI.Patches; using MiraAPI.Patches;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using UnityEngine; using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
@@ -36,6 +37,11 @@ public abstract class CustomActionButton
/// </summary> /// </summary>
public abstract LoadableAsset<Sprite> Sprite { get; } public abstract LoadableAsset<Sprite> Sprite { get; }
/// <summary>
/// Gets the format string for the cooldown timer.
/// </summary>
public virtual string CooldownTimerFormatString => "0";
/// <summary> /// <summary>
/// Gets the button's effect duration in seconds. If the button has no effect, set to 0. /// Gets the button's effect duration in seconds. If the button has no effect, set to 0.
/// </summary> /// </summary>
@@ -446,12 +452,12 @@ public abstract class CustomActionButton
{ {
Button.SetFillUp(Timer, EffectDuration); Button.SetFillUp(Timer, EffectDuration);
Button.cooldownTimerText.text = Mathf.CeilToInt(Timer).ToString(NumberFormatInfo.InvariantInfo); Button.cooldownTimerText.text = Timer.ToString(CooldownTimerFormatString, NumberFormatInfo.InvariantInfo);
Button.cooldownTimerText.gameObject.SetActive(true); Button.cooldownTimerText.gameObject.SetActive(true);
} }
else else
{ {
Button.SetCoolDown(Timer, Cooldown); Button.SetCooldownFormat(Timer, Cooldown, CooldownTimerFormatString);
} }
} }
+28 -13
View File
@@ -5,29 +5,44 @@ using MiraAPI.GameEnd;
namespace MiraAPI.Patches.Events; namespace MiraAPI.Patches.Events;
[HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))]
internal static class EndGamePatches internal static class EndGamePatches
{ {
public static bool Prefix(EndGameManager __instance) [HarmonyPatch(typeof(GameManager), nameof(GameManager.RpcEndGame))]
public static class GameManagerPatches
{ {
if (CustomGameOver.Instance is not { } gameOver) public static bool Prefix(GameOverReason endReason)
{ {
return true; var @event = new BeforeGameEndEvent(endReason);
} MiraEventManager.InvokeEvent(@event);
var result = gameOver.BeforeEndGameSetup(__instance); return !@event.IsCancelled;
return result; }
} }
public static void Postfix(EndGameManager __instance) [HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))]
public static class EndGameManagerPatches
{ {
if (CustomGameOver.Instance is { } gameOver) public static bool Prefix(EndGameManager __instance)
{ {
gameOver.AfterEndGameSetup(__instance); if (CustomGameOver.Instance is not { } gameOver)
CustomGameOver.Instance = null; {
return true;
}
var result = gameOver.BeforeEndGameSetup(__instance);
return result;
} }
var @event = new GameEndEvent(__instance); public static void Postfix(EndGameManager __instance)
MiraEventManager.InvokeEvent(@event); {
if (CustomGameOver.Instance is { } gameOver)
{
gameOver.AfterEndGameSetup(__instance);
CustomGameOver.Instance = null;
}
var @event = new GameEndEvent(__instance);
MiraEventManager.InvokeEvent(@event);
}
} }
} }
+91 -18
View File
@@ -37,6 +37,22 @@ public static class OptionsPatches
return false; return false;
} }
[HarmonyPrefix]
[HarmonyPatch(typeof(StringOption), nameof(StringOption.UpdateValue))]
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.UpdateValue))]
public static bool UpdateValuePrefix(OptionBehaviour __instance)
{
return !__instance.IsCustom();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.AdjustButtonsActiveState))]
[HarmonyPatch(typeof(StringOption), nameof(StringOption.AdjustButtonsActiveState))]
public static bool AdjustButtonsPrefix(OptionBehaviour __instance)
{
return !__instance.IsCustom();
}
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.Initialize))] [HarmonyPatch(typeof(NumberOption), nameof(NumberOption.Initialize))]
public static bool NumberInit(NumberOption __instance) public static bool NumberInit(NumberOption __instance)
@@ -46,17 +62,84 @@ public static class OptionsPatches
return true; return true;
} }
__instance.TitleText.text = __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
return false; return false;
} }
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.UpdateValue))] [HarmonyPatch(typeof(NumberOption), nameof(NumberOption.Increase))]
public static bool NumberUpdate(NumberOption __instance) public static bool NumberIncrease(NumberOption __instance)
{ {
return !__instance.IsCustom(); if (!__instance.IsCustom())
{
return true;
}
__instance.Value += __instance.Increment;
if (__instance.Value > __instance.ValidRange.max)
{
__instance.Value = __instance.ValidRange.min;
}
__instance.OnValueChanged.Invoke(__instance);
__instance.AdjustButtonsActiveState();
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.Decrease))]
public static bool NumberDecrease(NumberOption __instance)
{
if (!__instance.IsCustom())
{
return true;
}
__instance.Value -= __instance.Increment;
if (__instance.Value < __instance.ValidRange.min)
{
__instance.Value = __instance.ValidRange.max;
}
__instance.OnValueChanged.Invoke(__instance);
__instance.AdjustButtonsActiveState();
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(StringOption), nameof(StringOption.Increase))]
public static bool StringOptionIncrease(StringOption __instance)
{
if (!__instance.IsCustom())
{
return true;
}
__instance.Value += 1;
if (__instance.Value >= __instance.Values.Length)
{
__instance.Value = 0;
}
__instance.OnValueChanged.Invoke(__instance);
__instance.AdjustButtonsActiveState();
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(StringOption), nameof(StringOption.Decrease))]
public static bool StringOptionDecrease(StringOption __instance)
{
if (!__instance.IsCustom())
{
return true;
}
__instance.Value -= 1;
if (__instance.Value < 0)
{
__instance.Value = __instance.Values.Length - 1;
}
__instance.OnValueChanged.Invoke(__instance);
__instance.AdjustButtonsActiveState();
return false;
} }
[HarmonyPrefix] [HarmonyPrefix]
@@ -68,19 +151,9 @@ public static class OptionsPatches
return true; return true;
} }
__instance.TitleText.text = __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>()); __instance.ValueText.text = TranslationController.Instance.GetString(__instance.Values[__instance.Value], Array.Empty<Object>());
__instance.ValueText.text = TranslationController.Instance.GetString(
__instance.Values[__instance.Value],
Array.Empty<Object>());
return false; return false;
} }
[HarmonyPrefix]
[HarmonyPatch(typeof(StringOption), nameof(StringOption.UpdateValue))]
public static bool StringUpdate(StringOption __instance)
{
return !__instance.IsCustom();
}
} }
+59 -23
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -83,7 +84,52 @@ public static class Extensions
analytics.MinigameClosed(data, taskType, realtimeSinceStartup, isComplete); analytics.MinigameClosed(data, taskType, realtimeSinceStartup, isComplete);
self.StartCoroutine(self.CoDestroySelf()); self.StartCoroutine(self.CoDestroySelf());
} }
/// <summary>
/// Sets the cooldown of a button with a formatted string.
/// </summary>
/// <param name="button">The ActionButton to set the cooldown for.</param>
/// <param name="timer">The current timer value.</param>
/// <param name="maxTimer">The maximum timer value.</param>
/// <param name="format">The format string to use for the timer text.</param>
public static void SetCooldownFormat(this ActionButton button, float timer, float maxTimer, string format="0")
{
var num = Mathf.Clamp(timer / maxTimer, 0f, 1f);
button.isCoolingDown = num > 0f;
button.SetCooldownFill(num);
if (button.isCoolingDown)
{
button.cooldownTimerText.text = timer.ToString(format, NumberFormatInfo.InvariantInfo);
button.cooldownTimerText.gameObject.SetActive(true);
return;
}
button.cooldownTimerText.gameObject.SetActive(false);
}
/// <summary>
/// Sets the fill-up variant of a cooldown button with a formatted string.
/// </summary>
/// <param name="button">The ActionButton to set the cooldown for.</param>
/// <param name="timer">The current timer value.</param>
/// <param name="maxTimer">The maximum timer value.</param>
/// <param name="format">The format string to use for the timer text.</param>
public static void SetFillUpFormat(this ActionButton button, float timer, float maxTimer, string format="0")
{
var num = Mathf.Clamp(timer / maxTimer, 0f, 1f);
button.isCoolingDown = num > 0f;
if (button.isCoolingDown && timer < 3f)
{
button.graphic.transform.localPosition = button.position + (Vector3)UnityEngine.Random.insideUnitCircle * 0.05f;
button.cooldownTimerText.text = timer.ToString(format, NumberFormatInfo.InvariantInfo);
button.cooldownTimerText.gameObject.SetActive(true);
}
else
{
button.graphic.transform.localPosition = button.position;
}
button.SetCooldownFill(num);
}
/// <summary> /// <summary>
/// Gets a PlayerControl from their PlayerVoteArea in a meeting. /// Gets a PlayerControl from their PlayerVoteArea in a meeting.
/// </summary> /// </summary>
@@ -145,6 +191,12 @@ public static class Extensions
return component; return component;
} }
/// <summary>
/// Gets the maximum value from a dictionary of integers, returning the key and value.
/// </summary>
/// <param name="self">The dictionary to search.</param>
/// <param name="tie">Whether there is a tie for the maximum value.</param>
/// <returns>The key-value pair with the maximum value.</returns>
public static KeyValuePair<byte, int> MaxPair(this Dictionary<byte, int> self, out bool tie) public static KeyValuePair<byte, int> MaxPair(this Dictionary<byte, int> self, out bool tie)
{ {
tie = true; tie = true;
@@ -164,6 +216,12 @@ public static class Extensions
return result; return result;
} }
/// <summary>
/// Gets the maximum value from a dictionary of floats, returning the key and value.
/// </summary>
/// <param name="self">The dictionary to search.</param>
/// <param name="tie">Whether there is a tie for the maximum value.</param>
/// <returns>The key-value pair with the maximum value.</returns>
public static KeyValuePair<byte, float> MaxPair(this Dictionary<byte, float> self, out bool tie) public static KeyValuePair<byte, float> MaxPair(this Dictionary<byte, float> self, out bool tie)
{ {
tie = true; tie = true;
@@ -385,18 +443,6 @@ public static class Extensions
return new Color(color.r - amount, color.g - amount, color.b - amount); return new Color(color.r - amount, color.g - amount, color.b - amount);
} }
/// <summary>
/// Gets an alternate color based on the original color.
/// </summary>
/// <param name="color">The original color.</param>
/// <param name="amount">The amount to darken or lighten the original color by between 0.0 and 1.0.</param>
/// <returns>An alternate color that has been darkened or lightened.</returns>
[Obsolete("Use FindAlternateColor for WACG compliance.")]
public static Color GetAlternateColor(this Color color, float amount = 0.45f)
{
return color.IsColorDark() ? LightenColor(color, amount) : DarkenColor(color, amount);
}
/// <summary> /// <summary>
/// Lightens a color by a specified amount. /// Lightens a color by a specified amount.
/// </summary> /// </summary>
@@ -408,16 +454,6 @@ public static class Extensions
return new Color(color.r + amount, color.g + amount, color.b + amount); return new Color(color.r + amount, color.g + amount, color.b + amount);
} }
/// <summary>
/// Checks if a color is dark.
/// </summary>
/// <param name="color">The color to check.</param>
/// <returns>True if the color is dark, false otherwise.</returns>
public static bool IsColorDark(this Color color)
{
return color.r < 0.5f && color is { g: < 0.5f, b: < 0.5f };
}
/// <summary> /// <summary>
/// Gets the nearest dead body to a player. /// Gets the nearest dead body to a player.
/// </summary> /// </summary>