mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-21 05:37:32 +00:00
Compare commits
51
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5628b79814 | ||
|
|
72a44f65f1 | ||
|
|
db6a336a63 | ||
|
|
90b23ec2ca | ||
|
|
20e927208c | ||
|
|
13ef3bdd49 | ||
|
|
b3ff6755bb | ||
|
|
67604600cd | ||
|
|
6e35de6185 | ||
|
|
a26ec51c7d | ||
|
|
3f6ae4cc41 | ||
|
|
15a2cdcd5b | ||
|
|
372a9e3aa9 | ||
|
|
10a9dcfa34 | ||
|
|
d46fdb0185 | ||
|
|
a361afbcbf | ||
|
|
24cdac0243 | ||
|
|
5fe75843ad | ||
|
|
a0436dcbba | ||
|
|
35be381a77 | ||
|
|
1298beb517 | ||
|
|
d41c19a176 | ||
|
|
476f4400ad | ||
|
|
2b46d90570 | ||
|
|
ae4aaf7af9 | ||
|
|
757b9436ca | ||
|
|
7ea10afffb | ||
|
|
0ab80c42f9 | ||
|
|
bc144568f4 | ||
|
|
07f402e659 | ||
|
|
1a6aed539a | ||
|
|
023cdaa470 | ||
|
|
98dd5ca056 | ||
|
|
1f3e21ce19 | ||
|
|
15e441dd71 | ||
|
|
8c67bbbd44 | ||
|
|
aa584a8f44 | ||
|
|
a8a584b61b | ||
|
|
acc4b1c8dd | ||
|
|
f9600bc906 | ||
|
|
f3da06870f | ||
|
|
9180dec9e1 | ||
|
|
ef6dedbd55 | ||
|
|
5d3c3ed319 | ||
|
|
5b22d667fd | ||
|
|
fb894c9f2c | ||
|
|
d0449f57b5 | ||
|
|
5ffd591258 | ||
|
|
cc0ac1b988 | ||
|
|
00b96f6e61 | ||
|
|
6cb7f9f145 |
No files matched your search
+1
-2
@@ -1,2 +1 @@
|
||||
[*.{cs,vb}]
|
||||
dotnet_diagnostic.CA1707.severity = none
|
||||
[*.{cs,vb}]
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Reactor" Version="2.3.1" />
|
||||
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.697" Private="false" ExcludeAssets="runtime;native" />
|
||||
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2025.3.25" PrivateAssets="all" />
|
||||
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.735" Private="false" ExcludeAssets="runtime;native" />
|
||||
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2025.4.15" PrivateAssets="all" />
|
||||
|
||||
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" />
|
||||
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.1.0-rc.1" PrivateAssets="all" ExcludeAssets="runtime" />
|
||||
|
||||
@@ -9,10 +9,7 @@ using MiraAPI.Events.Vanilla.Player;
|
||||
using MiraAPI.Example.Buttons.Freezer;
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example;
|
||||
|
||||
@@ -21,7 +18,7 @@ public static class ExampleEventHandlers
|
||||
public static void Initialize()
|
||||
{
|
||||
// You can register event handlers with the MiraEventManager class.
|
||||
MiraEventManager.RegisterEventHandler<BeforeMurderEvent>(@event =>
|
||||
var handle = MiraEventManager.RegisterEventHandler<BeforeMurderEvent>(@event =>
|
||||
{
|
||||
Logger<ExamplePlugin>.Info($"{@event.Source.Data.PlayerName} is about to kill {@event.Target.Data.PlayerName}");
|
||||
});
|
||||
@@ -35,6 +32,9 @@ public static class ExampleEventHandlers
|
||||
{
|
||||
Logger<ExamplePlugin>.Info($"{@event.Player.Data.PlayerName} completed {@event.Task.TaskType.ToString()}");
|
||||
});
|
||||
|
||||
// You can also unregister event handlers using the handle returned from RegisterEventHandler.
|
||||
MiraEventManager.UnregisterEventHandler(handle);
|
||||
}
|
||||
|
||||
// If you want to add extra votes to a player, do something like this.
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using Il2CppSystem.Collections.Generic;
|
||||
using MiraAPI.Example.Options.Modifiers;
|
||||
using MiraAPI.Example.Options.Modifiers;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using UnityEngine;
|
||||
using Random = System.Random;
|
||||
|
||||
namespace MiraAPI.Example.Modifiers;
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example.Modifiers;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AmongUs.GameOptions;
|
||||
using MiraAPI.Patches.Stubs;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities;
|
||||
using TMPro;
|
||||
@@ -14,6 +15,8 @@ public class ChameloenRole : CrewmateRole, ICustomRole
|
||||
public Color RoleColor => Palette.AcceptedGreen;
|
||||
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
|
||||
|
||||
private bool _shouldHide;
|
||||
|
||||
public CustomRoleConfiguration Configuration => new(this)
|
||||
{
|
||||
OptionsScreenshot = ExampleAssets.Banner,
|
||||
@@ -22,35 +25,54 @@ public class ChameloenRole : CrewmateRole, ICustomRole
|
||||
|
||||
public override void Initialize(PlayerControl player)
|
||||
{
|
||||
Player = player;
|
||||
Logger<ExamplePlugin>.Error("Initializing ChamelonRole for player: " + player.PlayerId);
|
||||
Logger<ExamplePlugin>.Info("Initializing ChamelonRole for player: " + player.PlayerId);
|
||||
RoleBehaviourStubs.Initialize(this, player);
|
||||
_shouldHide = true;
|
||||
}
|
||||
|
||||
public void PlayerControlFixedUpdate(PlayerControl playerControl)
|
||||
public void FixedUpdate()
|
||||
{
|
||||
if (playerControl.MyPhysics.Velocity.magnitude > 0)
|
||||
if (!Player || !_shouldHide)
|
||||
{
|
||||
SpriteRenderer rend = playerControl.cosmetics.currentBodySprite.BodySprite;
|
||||
TextMeshPro tmp = playerControl.cosmetics.nameText;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Player.MyPhysics.Velocity.magnitude > 0)
|
||||
{
|
||||
var rend = Player.cosmetics.currentBodySprite.BodySprite;
|
||||
var tmp = Player.cosmetics.nameText;
|
||||
tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, 1), Time.deltaTime * 4f);
|
||||
rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, 1), Time.deltaTime * 4f);
|
||||
|
||||
foreach (var cosmetic in playerControl.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
foreach (var cosmetic in Player.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
{
|
||||
cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, 1), Time.deltaTime * 4f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SpriteRenderer rend = playerControl.cosmetics.currentBodySprite.BodySprite;
|
||||
TextMeshPro tmp = playerControl.cosmetics.nameText;
|
||||
tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
SpriteRenderer rend = Player.cosmetics.currentBodySprite.BodySprite;
|
||||
TextMeshPro tmp = Player.cosmetics.nameText;
|
||||
tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, Player.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, Player.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
|
||||
foreach (var cosmetic in playerControl.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
foreach (var cosmetic in Player.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
{
|
||||
cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, Player.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deinitialize(PlayerControl targetPlayer)
|
||||
{
|
||||
Logger<ExamplePlugin>.Info("Deinitializing ChamelonRole for player: " + targetPlayer.PlayerId);
|
||||
RoleBehaviourStubs.Deinitialize(this, targetPlayer);
|
||||
_shouldHide = false;
|
||||
foreach (var cosmetic in Player.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>(true))
|
||||
{
|
||||
cosmetic.color = Color.white;
|
||||
}
|
||||
Player.cosmetics.currentBodySprite.BodySprite.color = Color.white;
|
||||
Player.cosmetics.nameText.color = Color.white;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Event handle to use when unregistering an event.
|
||||
/// </summary>
|
||||
public class MiraEventHandle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the type of event.
|
||||
/// </summary>
|
||||
public Type EventType { get; }
|
||||
|
||||
internal MiraEventWrapper EventWrapper { get; }
|
||||
|
||||
internal MiraEventHandle(Type type, MiraEventWrapper eventWrapper)
|
||||
{
|
||||
EventType = type;
|
||||
EventWrapper = eventWrapper;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,14 @@ public static class MiraEventManager
|
||||
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
((Action<T>)handler.EventHandler).Invoke(eventInstance);
|
||||
try
|
||||
{
|
||||
((Action<T>)handler.EventHandler).Invoke(eventInstance);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error invoking event handler for {typeof(T).Name}: {ex.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -50,7 +57,14 @@ public static class MiraEventManager
|
||||
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
handler.EventHandler.DynamicInvoke(eventInstance);
|
||||
try
|
||||
{
|
||||
handler.EventHandler.DynamicInvoke(eventInstance);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error invoking event handler for {type.Name}: {ex.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -62,12 +76,12 @@ public static class MiraEventManager
|
||||
/// <param name="type">The type of event.</param>
|
||||
/// <param name="methodInfo">The MethodInfo of the event handler.</param>
|
||||
/// <param name="priority">The priority of the event handler. Lower values are called first.</param>
|
||||
public static void RegisterEventHandler(Type type, MethodInfo methodInfo, int priority = 0)
|
||||
/// <returns>An event handle to use when unregistering the event.</returns>
|
||||
public static MiraEventHandle RegisterEventHandler(Type type, MethodInfo methodInfo, int priority = 0)
|
||||
{
|
||||
if (!type.IsSubclassOf(typeof(MiraEvent)))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Type must be a subclass of MiraEvent: {type.FullName}");
|
||||
return;
|
||||
throw new InvalidOperationException($"Type must be a subclass of MiraEvent: {type.FullName}");
|
||||
}
|
||||
|
||||
EventWrappers.TryAdd(type, []);
|
||||
@@ -84,6 +98,7 @@ public static class MiraEventManager
|
||||
}
|
||||
|
||||
handlers.Insert(index, eventWrapper);
|
||||
return new MiraEventHandle(type, eventWrapper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,7 +107,8 @@ public static class MiraEventManager
|
||||
/// <param name="handler">The callback method/handler for the event.</param>
|
||||
/// <param name="priority">The priority of the event handler. Lower values are called first.</param>
|
||||
/// <typeparam name="T">Type of event.</typeparam>
|
||||
public static void RegisterEventHandler<T>(Action<T> handler, int priority = 0) where T : MiraEvent
|
||||
/// <returns>An event handle to use when unregistering the event.</returns>
|
||||
public static MiraEventHandle RegisterEventHandler<T>(Action<T> handler, int priority = 0) where T : MiraEvent
|
||||
{
|
||||
EventWrappers.TryAdd(typeof(T), []);
|
||||
|
||||
@@ -107,5 +123,30 @@ public static class MiraEventManager
|
||||
}
|
||||
|
||||
handlers.Insert(index, eventWrapper);
|
||||
return new MiraEventHandle(typeof(T), eventWrapper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister an event using the event handle.
|
||||
/// </summary>
|
||||
/// <param name="eventHandle">A handle to the event.</param>
|
||||
/// <returns>True if the event was unregistered successfully, false otherwise.</returns>
|
||||
public static bool UnregisterEventHandler(MiraEventHandle eventHandle)
|
||||
{
|
||||
if (!EventWrappers.TryGetValue(eventHandle.EventType, out var handlers))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!handlers.Remove(eventHandle.EventWrapper))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handlers.Count == 0)
|
||||
{
|
||||
EventWrappers.Remove(eventHandle.EventType);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,21 @@ public class AfterMurderEvent : MiraEvent
|
||||
/// </summary>
|
||||
public PlayerControl Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player's body, if it exists.
|
||||
/// </summary>
|
||||
public DeadBody? DeadBody { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AfterMurderEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="source">The killer.</param>
|
||||
/// <param name="target">The killed player.</param>
|
||||
public AfterMurderEvent(PlayerControl source, PlayerControl target)
|
||||
/// <param name="deadBody">The player's vody, if it exists.</param>
|
||||
public AfterMurderEvent(PlayerControl source, PlayerControl target, DeadBody? deadBody)
|
||||
{
|
||||
Source = source;
|
||||
Target = target;
|
||||
DeadBody = deadBody;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ public class UpdateSystemEvent : MiraCancelableEvent
|
||||
/// Initializes a new instance of the <see cref="UpdateSystemEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="systemType">The SystemType being updated.</param>
|
||||
/// <param name="player">The player that is updating the system.</param>
|
||||
/// <param name="amount">Amount to update System to.</param>
|
||||
public UpdateSystemEvent(SystemTypes systemType, PlayerControl player, byte amount)
|
||||
{
|
||||
|
||||
@@ -15,14 +15,21 @@ public class PlayerDeathEvent : MiraEvent
|
||||
/// </summary>
|
||||
public DeathReason DeathReason { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dead body associated with the player, if any.
|
||||
/// </summary>
|
||||
public DeadBody? DeadBody { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayerDeathEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player who died.</param>
|
||||
/// <param name="reason">The reason the player died.</param>
|
||||
public PlayerDeathEvent(PlayerControl player, DeathReason reason)
|
||||
/// <param name="deadBody">The player's body, if it exists.</param>
|
||||
public PlayerDeathEvent(PlayerControl player, DeathReason reason, DeadBody? deadBody)
|
||||
{
|
||||
Player = player;
|
||||
DeathReason = reason;
|
||||
DeadBody = deadBody;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
[*.cs]
|
||||
dotnet_diagnostic.CS1591.severity = none
|
||||
@@ -21,7 +21,7 @@ public static class CustomGameModeManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current gamemode.
|
||||
/// Gets the current gamemode.
|
||||
/// </summary>
|
||||
public static CustomGameMode? ActiveMode { get; internal set; } = new DefaultMode();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BepInEx.Configuration;
|
||||
@@ -45,7 +46,9 @@ public static class ModdedOptionsManager
|
||||
pluginInfo.InternalOptionGroups.Add(group);
|
||||
|
||||
typeof(OptionGroupSingleton<>).MakeGenericType(type)
|
||||
#pragma warning disable S3011
|
||||
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
|
||||
#pragma warning restore S3011
|
||||
.SetValue(null, group);
|
||||
|
||||
return true;
|
||||
@@ -173,8 +176,10 @@ public static class ModdedOptionsManager
|
||||
/// </summary>
|
||||
/// <param name="__originalMethod">The original setter method.</param>
|
||||
/// <param name="value">The new object value.</param>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
#pragma warning disable CA1707
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Harmony naming convention")]
|
||||
public static void PropertySetterPatch(MethodBase __originalMethod, object value)
|
||||
#pragma warning restore CA1707
|
||||
{
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod() == __originalMethod).Value;
|
||||
attribute.SetValue(value);
|
||||
@@ -186,8 +191,10 @@ public static class ModdedOptionsManager
|
||||
/// <param name="__originalMethod">The original getter method.</param>
|
||||
/// <param name="__result">The result of the property getter.</param>
|
||||
/// <returns>False so the original getter gets skipped.</returns>
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable CA1707
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Harmony naming convention")]
|
||||
public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result)
|
||||
#pragma warning restore CA1707
|
||||
{
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod() == __originalMethod).Value;
|
||||
__result = attribute.GetValue();
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace MiraAPI.GameOptions;
|
||||
|
||||
#pragma warning disable CA1852
|
||||
internal class ModifierOptionGroup : AbstractOptionGroup
|
||||
#pragma warning restore CA1852
|
||||
{
|
||||
public override string GroupName { get; }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Globalization;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Mira;
|
||||
using MiraAPI.Patches;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
@@ -36,6 +37,11 @@ public abstract class CustomActionButton
|
||||
/// </summary>
|
||||
public abstract LoadableAsset<Sprite> Sprite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the format string for the cooldown timer.
|
||||
/// </summary>
|
||||
public virtual string CooldownTimerFormatString => "0";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the button's effect duration in seconds. If the button has no effect, set to 0.
|
||||
/// </summary>
|
||||
@@ -180,7 +186,7 @@ public abstract class CustomActionButton
|
||||
|
||||
if (!moveButton) return;
|
||||
|
||||
if (HudManagerPatches._bottomLeft == null || HudManagerPatches._bottomRight == null)
|
||||
if (HudManagerPatches.BottomLeft == null || HudManagerPatches.BottomRight == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -188,17 +194,17 @@ public abstract class CustomActionButton
|
||||
switch (location)
|
||||
{
|
||||
case ButtonLocation.BottomLeft:
|
||||
var gridArrange = HudManagerPatches._bottomLeft.GetComponent<GridArrange>();
|
||||
var aspectPosition = HudManagerPatches._bottomLeft.GetComponent<AspectPosition>();
|
||||
var gridArrange = HudManagerPatches.BottomLeft.GetComponent<GridArrange>();
|
||||
var aspectPosition = HudManagerPatches.BottomLeft.GetComponent<AspectPosition>();
|
||||
|
||||
Button.transform.SetParent(HudManagerPatches._bottomLeft.transform);
|
||||
Button.transform.SetParent(HudManagerPatches.BottomLeft.transform);
|
||||
|
||||
gridArrange.Start();
|
||||
gridArrange.ArrangeChilds();
|
||||
aspectPosition.AdjustPosition();
|
||||
break;
|
||||
case ButtonLocation.BottomRight:
|
||||
Button.transform.SetParent(HudManagerPatches._bottomRight);
|
||||
Button.transform.SetParent(HudManagerPatches.BottomRight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -435,23 +441,23 @@ public abstract class CustomActionButton
|
||||
{
|
||||
if (CanUse())
|
||||
{
|
||||
Button.SetEnabled();
|
||||
Button!.SetEnabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
Button.SetDisabled();
|
||||
Button!.SetDisabled();
|
||||
}
|
||||
|
||||
if (EffectActive)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
Button.SetCoolDown(Timer, Cooldown);
|
||||
Button.SetCooldownFormat(Timer, Cooldown, CooldownTimerFormatString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@ public static class CustomButtonManager
|
||||
CustomButtons.Add(button);
|
||||
pluginInfo.InternalButtons.Add(button);
|
||||
typeof(CustomButtonSingleton<>).MakeGenericType(buttonType)
|
||||
#pragma warning disable S3011
|
||||
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
|
||||
#pragma warning restore S3011
|
||||
.SetValue(null, button);
|
||||
|
||||
ButtonEventTypes.Add(buttonType, typeof(MiraButtonClickEvent<>).MakeGenericType(buttonType));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace MiraAPI.Hud;
|
||||
|
||||
@@ -13,7 +14,6 @@ public static class CustomButtonSingleton<T> where T : CustomActionButton
|
||||
/// <summary>
|
||||
/// Gets the instance of the button.
|
||||
/// </summary>
|
||||
#pragma warning disable CA1000
|
||||
[SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "This is a utility class to get the instance of a custom action button.")]
|
||||
public static T Instance => _instance ??= CustomButtonManager.CustomButtons.OfType<T>().Single();
|
||||
#pragma warning restore CA1000
|
||||
}
|
||||
@@ -3,14 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.Modifiers.ModifierDisplay;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Modifiers;
|
||||
@@ -72,7 +69,15 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
foreach (var modifier in _toRemove.ToArray())
|
||||
{
|
||||
_toRemove.Remove(modifier);
|
||||
modifier.OnDeactivate();
|
||||
try
|
||||
{
|
||||
modifier.OnDeactivate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error while deactivating modifier {modifier.ModifierName}: {e.ToString()}");
|
||||
}
|
||||
|
||||
Modifiers.Remove(modifier);
|
||||
}
|
||||
|
||||
@@ -82,7 +87,14 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
_toAdd.Remove(modifier);
|
||||
Modifiers.Add(modifier);
|
||||
modifier.Initialized = true;
|
||||
modifier.OnActivate();
|
||||
try
|
||||
{
|
||||
modifier.OnActivate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error while activating modifier {modifier.ModifierName}: {e.ToString()}");
|
||||
}
|
||||
|
||||
if (modifier is TimedModifier { AutoStart: true } timer)
|
||||
{
|
||||
@@ -102,7 +114,14 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
|
||||
foreach (var modifier in ActiveModifiers)
|
||||
{
|
||||
modifier.FixedUpdate();
|
||||
try
|
||||
{
|
||||
modifier.FixedUpdate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error while (fixed) updating modifier {modifier.ModifierName}: {e.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
if (_player.AmOwner && ModifierDisplay && HudManager.InstanceExists)
|
||||
@@ -115,7 +134,14 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
foreach (var modifier in ActiveModifiers)
|
||||
{
|
||||
modifier.Update();
|
||||
try
|
||||
{
|
||||
modifier.Update();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Error while updating modifier {modifier.ModifierName}: {e.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace MiraAPI.Modifiers.ModifierDisplay;
|
||||
/// The code used to display Mira modifiers.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
public class ModifierDisplayComponent(nint ptr) : MonoBehaviour(ptr)
|
||||
public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance of the Modifier Display.
|
||||
@@ -34,8 +34,8 @@ public class ModifierDisplayComponent(nint ptr) : MonoBehaviour(ptr)
|
||||
private PassiveButton _nextButton = null!;
|
||||
private PassiveButton _backButton = null!;
|
||||
|
||||
private int _currentPage = 0;
|
||||
private const int _itemsPerPage = 3;
|
||||
private int _currentPage;
|
||||
private const int ItemsPerPage = 3;
|
||||
|
||||
private readonly Dictionary<BaseModifier, ModifierUiComponent> _modifiers = [];
|
||||
|
||||
@@ -142,10 +142,10 @@ public class ModifierDisplayComponent(nint ptr) : MonoBehaviour(ptr)
|
||||
|
||||
private void UpdatePage()
|
||||
{
|
||||
var totalPages = Mathf.CeilToInt((float)_modifiers.Count / _itemsPerPage);
|
||||
var totalPages = Mathf.CeilToInt((float)_modifiers.Count / ItemsPerPage);
|
||||
_currentPage = Mathf.Clamp(_currentPage, 0, Mathf.Max(0, totalPages - 1));
|
||||
|
||||
if (_modifiers.Count <= _itemsPerPage)
|
||||
if (_modifiers.Count <= ItemsPerPage)
|
||||
{
|
||||
_modifiers.Do(x => x.Value.gameObject.SetActive(true));
|
||||
_pagination.gameObject.SetActive(false);
|
||||
@@ -162,8 +162,8 @@ public class ModifierDisplayComponent(nint ptr) : MonoBehaviour(ptr)
|
||||
_backButton.gameObject.SetActive(true);
|
||||
|
||||
_modifiers.Do(x => x.Value.gameObject.SetActive(false));
|
||||
var start = _currentPage * _itemsPerPage;
|
||||
var end = Mathf.Min(start + _itemsPerPage, _modifiers.Count);
|
||||
var start = _currentPage * ItemsPerPage;
|
||||
var end = Mathf.Min(start + ItemsPerPage, _modifiers.Count);
|
||||
var modifiers = _modifiers.ToArray();
|
||||
|
||||
for (var i = start; i < end; i++)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace MiraAPI.Modifiers.ModifierDisplay;
|
||||
/// The code placed on every Modifier HUD object. Used to handle updating.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
public class ModifierUiComponent(nint ptr) : MonoBehaviour(ptr)
|
||||
public class ModifierUiComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the modifier which this component is for.
|
||||
@@ -17,12 +17,13 @@ public class ModifierUiComponent(nint ptr) : MonoBehaviour(ptr)
|
||||
[HideFromIl2Cpp]
|
||||
public BaseModifier? Modifier { get; internal set; }
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
private SpriteRenderer modBg = null!;
|
||||
private TextMeshPro desc = null!;
|
||||
private SpriteRenderer icon = null!;
|
||||
private TextMeshPro nameText = null!;
|
||||
private RectTransform descRect = null!;
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
private void Awake()
|
||||
{
|
||||
if (Modifier == null) return;
|
||||
|
||||
@@ -30,7 +30,7 @@ public static class ModifierExtensions
|
||||
_ = ModifierManager.GetModifierType(typeId) ?? throw new InvalidOperationException(
|
||||
$"Modifier with ID {typeId} is not registered.");
|
||||
|
||||
Rpc<AddModifierRpc>.Instance.Send(target, new ModifierData(typeId, Guid.NewGuid(), args));
|
||||
Rpc<AddModifierRpc>.Instance.Send(target, new ModifierData(typeId, Guid.NewGuid(), args), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -63,7 +63,7 @@ public static class ModifierExtensions
|
||||
/// </summary>
|
||||
/// <param name="target">The player to remove the modifier from.</param>
|
||||
/// <param name="uniqueId">The unique ID of the modifier.</param>
|
||||
[MethodRpc((uint)MiraRpc.RemoveModifier)]
|
||||
[MethodRpc((uint)MiraRpc.RemoveModifier, SendImmediately = true)]
|
||||
public static void RpcRemoveModifier(this PlayerControl target, Guid uniqueId)
|
||||
{
|
||||
target.RemoveModifier(uniqueId);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using MiraAPI.Utilities;
|
||||
@@ -11,7 +12,7 @@ namespace MiraAPI.Modifiers;
|
||||
/// </summary>
|
||||
public static class ModifierFactory
|
||||
{
|
||||
private static readonly Dictionary<(Type, Type[]), Func<object[], BaseModifier>> _constructorCache = [];
|
||||
private static readonly Dictionary<(Type, Type[]), Func<object[], BaseModifier>> ConstructorCache = [];
|
||||
|
||||
private static Func<object[], BaseModifier> CreateConstructor(Type type, params object[] args)
|
||||
{
|
||||
@@ -45,13 +46,13 @@ public static class ModifierFactory
|
||||
/// <returns>An instance of the modifier.</returns>
|
||||
public static BaseModifier CreateInstance(Type type, params object[] args)
|
||||
{
|
||||
var argTypes = args.Select(arg => arg?.GetType() ?? typeof(object)).ToArray();
|
||||
var argTypes = args.Select(arg => arg.GetType()).ToArray();
|
||||
var key = (type, argTypes);
|
||||
|
||||
if (!_constructorCache.TryGetValue(key, out var constructor))
|
||||
if (!ConstructorCache.TryGetValue(key, out var constructor))
|
||||
{
|
||||
constructor = CreateConstructor(type, args);
|
||||
_constructorCache[key] = constructor;
|
||||
ConstructorCache[key] = constructor;
|
||||
}
|
||||
|
||||
return constructor(args);
|
||||
@@ -95,6 +96,7 @@ public static class ModifierFactory<T> where T : BaseModifier
|
||||
/// </summary>
|
||||
/// <param name="args">Parameters for the constructor.</param>
|
||||
/// <returns>An instance of the modifier.</returns>
|
||||
[SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "This is a factory class.")]
|
||||
public static T CreateInstance(params object[] args)
|
||||
{
|
||||
return Constructor(args);
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Runtime.Serialization;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using Random = System.Random;
|
||||
|
||||
@@ -264,7 +264,7 @@ public static class CustomMurderRpc
|
||||
deadBody.enabled = true;
|
||||
}
|
||||
|
||||
var afterMurderEvent = new AfterMurderEvent(source, target);
|
||||
var afterMurderEvent = new AfterMurderEvent(source, target, deadBody);
|
||||
MiraEventManager.InvokeEvent(afterMurderEvent);
|
||||
|
||||
if (!isParticipant)
|
||||
|
||||
@@ -46,7 +46,7 @@ public enum MiraRpc : uint
|
||||
RemoveVote,
|
||||
|
||||
/// <summary>
|
||||
/// Custom RPC to replace Among Us CastVote
|
||||
/// Custom RPC to replace Among Us CastVote.
|
||||
/// </summary>
|
||||
CastVote,
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
[*.{cs,vb}]
|
||||
dotnet_diagnostic.CA1707.severity = none
|
||||
dotnet_diagnostic.CS1591.severity = none
|
||||
dotnet_diagnostic.SA1611.severity = none
|
||||
dotnet_diagnostic.SA1615.severity = none
|
||||
@@ -5,29 +5,44 @@ using MiraAPI.GameEnd;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
[HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))]
|
||||
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 result;
|
||||
return !@event.IsCancelled;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
CustomGameOver.Instance = null;
|
||||
if (CustomGameOver.Instance is not { } gameOver)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var result = gameOver.BeforeEndGameSetup(__instance);
|
||||
return result;
|
||||
}
|
||||
|
||||
var @event = new GameEndEvent(__instance);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
public static void Postfix(EndGameManager __instance)
|
||||
{
|
||||
if (CustomGameOver.Instance is { } gameOver)
|
||||
{
|
||||
gameOver.AfterEndGameSetup(__instance);
|
||||
CustomGameOver.Instance = null;
|
||||
}
|
||||
|
||||
var @event = new GameEndEvent(__instance);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Utilities;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
@@ -13,13 +14,17 @@ public static class KillAnimationPatches
|
||||
/// <summary>
|
||||
/// Used to trigger the <see cref="AfterMurderEvent"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The killer.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(KillAnimation), nameof(KillAnimation.CoPerformKill))]
|
||||
public static void KillAnimPostfix(PlayerControl source, PlayerControl target)
|
||||
[HarmonyPatch(typeof(KillAnimation._CoPerformKill_d__2), nameof(KillAnimation._CoPerformKill_d__2.MoveNext))]
|
||||
public static void KillAnimPostfix(KillAnimation._CoPerformKill_d__2 __instance)
|
||||
{
|
||||
var afterMurderEvent = new AfterMurderEvent(source, target);
|
||||
// Checking if state is -1 to wait for the end of coroutine.
|
||||
if (__instance.__1__state != -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var afterMurderEvent = new AfterMurderEvent(__instance.source, __instance.target, Helpers.GetBodyById(__instance.target.PlayerId));
|
||||
MiraEventManager.InvokeEvent(afterMurderEvent);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,52 @@
|
||||
using HarmonyLib;
|
||||
using AmongUs.InnerNet.GameDataMessages;
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Meeting;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class ReportBodyEventPatch
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CmdReportDeadBody))]
|
||||
public static bool PlayerControlCmdReportDeadBodyPrefix(PlayerControl __instance, NetworkedPlayerInfo? target)
|
||||
private static bool ReportBodyEventHook(PlayerControl source, NetworkedPlayerInfo? target)
|
||||
{
|
||||
var body = target != null ? Helpers.GetBodyById(target.PlayerId) : null;
|
||||
|
||||
var @event = new ReportBodyEvent(__instance, target, body);
|
||||
var @event = new ReportBodyEvent(source, target, body);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
return !@event.IsCancelled;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.ReportDeadBody))]
|
||||
public static bool PlayerControlCmdReportDeadBodyPrefix(PlayerControl __instance, NetworkedPlayerInfo? target)
|
||||
{
|
||||
return ReportBodyEventHook(__instance, target);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.LateBroadcastReliableMessage))]
|
||||
public static bool LateBroadcastReliableMessagePrefix(IGameDataMessage rpcMessage)
|
||||
{
|
||||
var reportBodyMessage = rpcMessage.TryCast<RpcReportDeadBodyMessage>();
|
||||
|
||||
if (reportBodyMessage == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var source = AmongUsClient.Instance.FindObjectByNetId<PlayerControl>(reportBodyMessage.rpcObjectNetId);
|
||||
if (source == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("Failed to find source player for RpcReportDeadBodyMessage with NetId: " + reportBodyMessage.rpcObjectNetId);
|
||||
return true;
|
||||
}
|
||||
|
||||
var target = GameData.Instance.GetPlayerById(reportBodyMessage.targetPlayerId);
|
||||
|
||||
return ReportBodyEventHook(source, target);
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,10 @@ public static class VentEventPatches
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.RpcEnterVent))]
|
||||
public static bool PlayerPhysicsRpcEnterVentPrefix(PlayerPhysics __instance, int id)
|
||||
public static bool PlayerPhysicsRpcEnterVentPrefix(PlayerPhysics __instance, int ventId)
|
||||
{
|
||||
var pc = __instance.myPlayer;
|
||||
var vent = ShipStatus.Instance.AllVents.FirstOrDefault(v => v.Id == id);
|
||||
var vent = ShipStatus.Instance.AllVents.FirstOrDefault(v => v.Id == ventId);
|
||||
|
||||
var @event = new EnterVentEvent(pc, vent);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
@@ -40,10 +40,10 @@ public static class VentEventPatches
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.RpcExitVent))]
|
||||
public static bool PlayerPhysicsRpcExitVentPrefix(PlayerPhysics __instance, int id)
|
||||
public static bool PlayerPhysicsRpcExitVentPrefix(PlayerPhysics __instance, int ventId)
|
||||
{
|
||||
var pc = __instance.myPlayer;
|
||||
var vent = ShipStatus.Instance.AllVents.First(v => v.Id == id);
|
||||
var vent = ShipStatus.Instance.AllVents.First(v => v.Id == ventId);
|
||||
|
||||
var @event = new ExitVentEvent(pc, vent);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[*.cs]
|
||||
dotnet_diagnostic.S125.severity = none
|
||||
@@ -4,7 +4,7 @@ using MiraAPI.Hud;
|
||||
namespace MiraAPI.Patches.Hud;
|
||||
|
||||
/// <summary>
|
||||
/// Reset button patches
|
||||
/// Reset button patches.
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public static class ButtonResetPatches
|
||||
|
||||
@@ -13,9 +13,9 @@ namespace MiraAPI.Patches;
|
||||
public static class HudManagerPatches
|
||||
{
|
||||
// Custom buttons parent.
|
||||
internal static GameObject? _bottomLeft;
|
||||
internal static Transform? _bottomRight;
|
||||
internal static Transform? _buttons;
|
||||
public static GameObject? BottomLeft { get; private set; }
|
||||
public static Transform? BottomRight { get; private set; }
|
||||
public static Transform? Buttons { get; private set; }
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
@@ -36,30 +36,30 @@ public static class HudManagerPatches
|
||||
[HarmonyPatch(nameof(HudManager.Start))]
|
||||
public static void StartPostfix(HudManager __instance)
|
||||
{
|
||||
if (_buttons == null)
|
||||
if (Buttons == null)
|
||||
{
|
||||
_buttons = __instance.transform.Find("Buttons");
|
||||
Buttons = __instance.transform.Find("Buttons");
|
||||
}
|
||||
|
||||
if (_bottomRight == null)
|
||||
if (BottomRight == null)
|
||||
{
|
||||
_bottomRight = _buttons.Find("BottomRight");
|
||||
BottomRight = Buttons.Find("BottomRight");
|
||||
}
|
||||
|
||||
if (_bottomLeft == null)
|
||||
if (BottomLeft == null)
|
||||
{
|
||||
_bottomLeft = Object.Instantiate(_bottomRight.gameObject, _buttons);
|
||||
BottomLeft = Object.Instantiate(BottomRight.gameObject, Buttons);
|
||||
}
|
||||
|
||||
foreach (var t in _bottomLeft.GetComponentsInChildren<ActionButton>(true))
|
||||
foreach (var t in BottomLeft.GetComponentsInChildren<ActionButton>(true))
|
||||
{
|
||||
t.gameObject.Destroy();
|
||||
}
|
||||
|
||||
var gridArrange = _bottomLeft.GetComponent<GridArrange>();
|
||||
var aspectPosition = _bottomLeft.GetComponent<AspectPosition>();
|
||||
var gridArrange = BottomLeft.GetComponent<GridArrange>();
|
||||
var aspectPosition = BottomLeft.GetComponent<AspectPosition>();
|
||||
|
||||
_bottomLeft.name = "BottomLeft";
|
||||
BottomLeft.name = "BottomLeft";
|
||||
gridArrange.Alignment = GridArrange.StartAlign.Right;
|
||||
aspectPosition.Alignment = AspectPosition.EdgeAlignments.LeftBottom;
|
||||
|
||||
@@ -67,8 +67,8 @@ public static class HudManagerPatches
|
||||
{
|
||||
var location = button.Location switch
|
||||
{
|
||||
ButtonLocation.BottomLeft => _bottomLeft.transform,
|
||||
ButtonLocation.BottomRight => _bottomRight,
|
||||
ButtonLocation.BottomLeft => BottomLeft.transform,
|
||||
ButtonLocation.BottomRight => BottomRight,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ public static class NameplatesTabPatches
|
||||
var x = (NamePlateViewData viewdata) =>
|
||||
{
|
||||
colorChip.transform.GetChild(1).GetComponent<SpriteRenderer>().sprite = viewdata?.Image;
|
||||
//(colorChip as NameplateChip).image.sprite = viewdata?.Image;
|
||||
// (colorChip as NameplateChip).image.sprite = viewdata?.Image;
|
||||
};
|
||||
__instance.StartCoroutine(AddressableAssetExtensions.CoLoadAssetAsync<NamePlateViewData>(__instance, namePlate.GetAssetReference(), x));
|
||||
colorChip.transform.localPosition = new Vector3(position.x, position.y, -1f);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace MiraAPI.Patches;
|
||||
|
||||
/// <summary>
|
||||
/// Used to fix the Minigame crash errors (credits Submerged/PH Gaming for the fix)
|
||||
/// Used to fix the Minigame crash errors (credits Submerged/PH Gaming for the fix).
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public static class MinigameBeginErrorPatch
|
||||
|
||||
@@ -32,11 +32,30 @@ internal static class GameSettingMenuPatches
|
||||
private static PassiveButton? _modifiersButton;
|
||||
private static PassiveButton? _smallRolesButton;
|
||||
|
||||
private static Dictionary<int, Vector3> OptionScrolls = [];
|
||||
private static Dictionary<int, Vector3> OptionsPositions { get; } = [];
|
||||
private static Dictionary<int, Vector3> ModifiersPositions { get; } = [];
|
||||
|
||||
private static void SaveScrollPositions(GameSettingMenu gameSettingMenu)
|
||||
{
|
||||
if (_modifiersTab)
|
||||
{
|
||||
ModifiersPositions[SelectedModIdx] = _modifiersTab!.scrollBar.Inner.localPosition;
|
||||
}
|
||||
|
||||
RoleSettingMenuPatches.RolePositions[SelectedModIdx] = gameSettingMenu.RoleSettingsTab.scrollBar.Inner.localPosition;
|
||||
OptionsPositions[SelectedModIdx] = gameSettingMenu.GameSettingsTab.scrollBar.Inner.localPosition;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GameSettingMenu.ChangeTab))]
|
||||
|
||||
public static void ChangeTabPrefix(GameSettingMenu __instance)
|
||||
{
|
||||
SaveScrollPositions(__instance);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(GameSettingMenu.ChangeTab))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void ChangeTabPostfix(GameSettingMenu __instance, int tabNum, bool previewOnly)
|
||||
{
|
||||
if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly)
|
||||
@@ -90,7 +109,6 @@ internal static class GameSettingMenuPatches
|
||||
/// <param name="__instance">The GameSettingMenu instance.</param>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GameSettingMenu.Start))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void StartPrefix(GameSettingMenu __instance)
|
||||
{
|
||||
_roleBtnOgPos = __instance.RoleSettingsButton.transform.localPosition;
|
||||
@@ -123,11 +141,7 @@ internal static class GameSettingMenuPatches
|
||||
passiveButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
RoleSettingMenuPatches.Scrolls[SelectedModIdx] =
|
||||
__instance.RoleSettingsTab.scrollBar.Inner.localPosition;
|
||||
OptionScrolls[SelectedModIdx] =
|
||||
__instance.GameSettingsTab.scrollBar.Inner.localPosition;
|
||||
|
||||
SaveScrollPositions(__instance);
|
||||
SelectedModIdx += 1;
|
||||
if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins.Length)
|
||||
{
|
||||
@@ -146,11 +160,7 @@ internal static class GameSettingMenuPatches
|
||||
backButton.gameObject.GetComponent<PassiveButton>().OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
RoleSettingMenuPatches.Scrolls[SelectedModIdx] =
|
||||
__instance.RoleSettingsTab.scrollBar.Inner.localPosition;
|
||||
OptionScrolls[SelectedModIdx] =
|
||||
__instance.GameSettingsTab.scrollBar.Inner.localPosition;
|
||||
|
||||
SaveScrollPositions(__instance);
|
||||
SelectedModIdx -= 1;
|
||||
if (SelectedModIdx < 0)
|
||||
{
|
||||
@@ -436,29 +446,7 @@ internal static class GameSettingMenuPatches
|
||||
|
||||
if (_modifiersTab?.Children?.Count > 0)
|
||||
{
|
||||
CleanupSettings(_modifiersTab);
|
||||
}
|
||||
|
||||
void CleanupSettings(GameOptionsMenu gameOptMenu)
|
||||
{
|
||||
ClearOptions(gameOptMenu.Children);
|
||||
gameOptMenu.Children = null;
|
||||
|
||||
gameOptMenu.settingsContainer
|
||||
.GetComponentsInChildren<CategoryHeaderMasked>()
|
||||
.ToList()
|
||||
.ForEach(header => header.gameObject.DestroyImmediate());
|
||||
|
||||
gameOptMenu.Initialize();
|
||||
if (OptionScrolls.TryGetValue(SelectedModIdx, out var pos))
|
||||
{
|
||||
gameOptMenu.scrollBar.Inner.localPosition = pos;
|
||||
gameOptMenu.scrollBar.UpdateScrollBars();
|
||||
}
|
||||
else
|
||||
{
|
||||
gameOptMenu.scrollBar.ScrollToTop();
|
||||
}
|
||||
CleanupSettings(_modifiersTab, true);
|
||||
}
|
||||
|
||||
void CleanupRoleSettings(RolesSettingsMenu rolesMenu)
|
||||
@@ -485,17 +473,38 @@ internal static class GameSettingMenuPatches
|
||||
rolesMenu.AdvancedRolesSettings.gameObject.SetActive(false);
|
||||
rolesMenu.RoleChancesSettings.gameObject.SetActive(true);
|
||||
rolesMenu.SetQuotaTab();
|
||||
if (SelectedModIdx == 0)
|
||||
if (RoleSettingMenuPatches.RolePositions.TryGetValue(SelectedModIdx, out var pos))
|
||||
{
|
||||
if (RoleSettingMenuPatches.Scrolls.TryGetValue(0, out var pos))
|
||||
{
|
||||
rolesMenu.scrollBar.Inner.localPosition = pos;
|
||||
rolesMenu.scrollBar.UpdateScrollBars();
|
||||
}
|
||||
else
|
||||
{
|
||||
rolesMenu.scrollBar.ScrollToTop();
|
||||
}
|
||||
rolesMenu.scrollBar.Inner.localPosition = pos;
|
||||
rolesMenu.scrollBar.UpdateScrollBars();
|
||||
}
|
||||
else
|
||||
{
|
||||
rolesMenu.scrollBar.ScrollToTop();
|
||||
}
|
||||
}
|
||||
|
||||
void CleanupSettings(GameOptionsMenu gameOptMenu, bool modifiers = false)
|
||||
{
|
||||
ClearOptions(gameOptMenu.Children);
|
||||
gameOptMenu.Children = null;
|
||||
|
||||
gameOptMenu.settingsContainer
|
||||
.GetComponentsInChildren<CategoryHeaderMasked>()
|
||||
.ToList()
|
||||
.ForEach(header => header.gameObject.DestroyImmediate());
|
||||
|
||||
gameOptMenu.Initialize();
|
||||
|
||||
var positions = modifiers ? ModifiersPositions : OptionsPositions;
|
||||
if (positions.TryGetValue(SelectedModIdx, out var pos))
|
||||
{
|
||||
gameOptMenu.scrollBar.Inner.localPosition = pos;
|
||||
gameOptMenu.scrollBar.UpdateScrollBars();
|
||||
}
|
||||
else
|
||||
{
|
||||
gameOptMenu.scrollBar.ScrollToTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,14 +113,16 @@ public static class LobbyViewPanePatches
|
||||
menu.scrollBar.ScrollToTop();
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.SetTab))]
|
||||
public static bool SetTabPatch(LobbyViewSettingsPane __instance)
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.ChangeTab))]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.RefreshTab))]
|
||||
// CHANGED BECAUSE OF INLINING
|
||||
public static void SetTabPatch(LobbyViewSettingsPane __instance)
|
||||
{
|
||||
if (__instance.currentTab != ModifiersTabName || SelectedMod == null)
|
||||
{
|
||||
ModifiersTabButton?.SelectButton(false);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
__instance.taskTabButton.SelectButton(false);
|
||||
@@ -130,7 +132,6 @@ public static class LobbyViewPanePatches
|
||||
var filteredGroups = SelectedMod.InternalOptionGroups
|
||||
.Where(x => x.GroupVisible() && (x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true));
|
||||
DrawOptions(__instance, filteredGroups);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
@@ -142,6 +143,11 @@ public static class LobbyViewPanePatches
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance.currentTab == ModifiersTabName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var filteredGroups = SelectedMod.InternalOptionGroups
|
||||
.Where(x => x.OptionableType == null && x.GroupVisible.Invoke());
|
||||
DrawOptions(__instance, filteredGroups);
|
||||
@@ -157,6 +163,11 @@ public static class LobbyViewPanePatches
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance.currentTab == ModifiersTabName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DrawRolesTab(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using HarmonyLib;
|
||||
using Il2CppSystem;
|
||||
using MiraAPI.Roles;
|
||||
using UnityEngine;
|
||||
using Object = Il2CppSystem.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Options;
|
||||
|
||||
@@ -23,15 +25,13 @@ public static class NotificationPopperPatch
|
||||
return true;
|
||||
}
|
||||
|
||||
var text = teamType == RoleTeamTypes.Crewmate
|
||||
? Palette.CrewmateSettingChangeText.ToTextColor()
|
||||
: Palette.ImpostorRed.ToTextColor();
|
||||
var textColor = customRole.OptionsMenuColor.ToTextColor();
|
||||
|
||||
var item = TranslationController.Instance.GetString(
|
||||
StringNames.LobbyChangeSettingNotificationRole,
|
||||
string.Concat(
|
||||
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">",
|
||||
text,
|
||||
textColor,
|
||||
TranslationController.Instance.GetString(key, Array.Empty<Object>()),
|
||||
"</color></font>"
|
||||
),
|
||||
|
||||
@@ -23,8 +23,32 @@ public static class OptionsPatches
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.UpdateValue))]
|
||||
[HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.Toggle))]
|
||||
// UpdateValue was inlined.
|
||||
public static bool ToggleUpdate(ToggleOption __instance)
|
||||
{
|
||||
if (!__instance.IsCustom())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.CheckMark.enabled = !__instance.CheckMark.enabled;
|
||||
__instance.OnValueChanged.Invoke(__instance);
|
||||
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();
|
||||
}
|
||||
@@ -38,17 +62,84 @@ public static class OptionsPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.TitleText.text =
|
||||
TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
|
||||
__instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.UpdateValue))]
|
||||
public static bool NumberUpdate(NumberOption __instance)
|
||||
[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.Increase))]
|
||||
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]
|
||||
@@ -60,19 +151,9 @@ public static class OptionsPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.TitleText.text =
|
||||
TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
__instance.ValueText.text = TranslationController.Instance.GetString(
|
||||
__instance.Values[__instance.Value],
|
||||
Array.Empty<Object>());
|
||||
__instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
__instance.ValueText.text = TranslationController.Instance.GetString(__instance.Values[__instance.Value], Array.Empty<Object>());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(StringOption), nameof(StringOption.UpdateValue))]
|
||||
public static bool StringUpdate(StringOption __instance)
|
||||
{
|
||||
return !__instance.IsCustom();
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,21 @@ namespace MiraAPI.Patches.Options;
|
||||
[HarmonyPatch(typeof(RolesSettingsMenu))]
|
||||
public static class RoleSettingMenuPatches
|
||||
{
|
||||
public static Dictionary<int, Vector3> Scrolls { get; } = [];
|
||||
private static Dictionary<RoleOptionsGroup, bool> RoleGroupHidden { get; } = [];
|
||||
private static List<GameObject> Headers { get; } = [];
|
||||
private static List<RoleOptionSetting> RoleOptionSettings { get; } = [];
|
||||
public static Dictionary<int, Vector3> RolePositions { get; set; } = [];
|
||||
|
||||
private static Dictionary<RoleOptionsGroup, bool> RoleGroupHidden { get; set; } = [];
|
||||
private static List<GameObject> Headers { get; set; } = [];
|
||||
private static List<RoleOptionSetting> RoleOptionSettings { get; set; } = [];
|
||||
|
||||
private static float ScrollerNum { get; set; } = 0.522f;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
|
||||
public static void SetQuotaTabPostfix(RolesSettingsMenu __instance)
|
||||
{
|
||||
__instance.scrollBar.SetScrollBounds(__instance);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))]
|
||||
public static bool SetQuotaTabPatch(RolesSettingsMenu __instance)
|
||||
@@ -226,14 +234,20 @@ public static class RoleSettingMenuPatches
|
||||
ScrollerNum -= 0.4f;
|
||||
}
|
||||
}
|
||||
__instance.scrollBar.SetScrollBounds();
|
||||
__instance.scrollBar.SetScrollBounds(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void SetScrollBounds(this Scroller scroller)
|
||||
private static void SetScrollBounds(this Scroller scroller, RolesSettingsMenu roleMenu)
|
||||
{
|
||||
scroller.CalculateAndSetYBounds(1 + 1.5f * Headers.Count + RoleOptionSettings.Count, 1f, 6f, 0.43f);
|
||||
if (Scrolls.TryGetValue(GameSettingMenuPatches.SelectedModIdx, out var scroll))
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0)
|
||||
{
|
||||
scroller.CalculateAndSetYBounds(roleMenu.roleChances.Count + 3, 1f, 6f, 0.43f);
|
||||
return;
|
||||
}
|
||||
|
||||
scroller.CalculateAndSetYBounds(1.5f * Headers.Count + RoleOptionSettings.Count, 1f, 6f, 0.43f);
|
||||
if (RolePositions.TryGetValue(GameSettingMenuPatches.SelectedModIdx, out var scroll))
|
||||
{
|
||||
scroller.Inner.localPosition = scroll;
|
||||
scroller.UpdateScrollBars();
|
||||
@@ -255,12 +269,12 @@ public static class RoleSettingMenuPatches
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.OpenChancesTab))]
|
||||
public static void OpenChancesTabPostfix(RolesSettingsMenu __instance)
|
||||
{
|
||||
if (GameSettingMenuPatches.SelectedModIdx == 0)
|
||||
if (!__instance.scrollBar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
__instance.scrollBar.SetScrollBounds();
|
||||
__instance.scrollBar.SetScrollBounds(__instance);
|
||||
}
|
||||
|
||||
private static void ValueChanged(OptionBehaviour obj)
|
||||
@@ -353,7 +367,6 @@ public static class RoleSettingMenuPatches
|
||||
newOpt.Initialize();
|
||||
}
|
||||
|
||||
Scrolls[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition;
|
||||
__instance.scrollBar.CalculateAndSetYBounds(__instance.advancedSettingChildren.Count + 3, 1f, 6f, 0.45f);
|
||||
__instance.scrollBar.ScrollToTop();
|
||||
}
|
||||
@@ -366,7 +379,7 @@ public static class RoleSettingMenuPatches
|
||||
return;
|
||||
}
|
||||
|
||||
Scrolls[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition;
|
||||
RolePositions[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition;
|
||||
|
||||
__instance.roleDescriptionText.text = customRole.RoleLongDescription;
|
||||
__instance.roleTitleText.text = TranslationController.Instance.GetString(
|
||||
|
||||
@@ -5,7 +5,7 @@ using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Events.Vanilla.Player;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Voting;
|
||||
using Reactor.Utilities.Extensions;
|
||||
|
||||
@@ -41,7 +41,7 @@ internal static class PlayerControlPatches
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void PlayerControlDiePostfix(PlayerControl __instance, DeathReason reason)
|
||||
{
|
||||
var deathEvent = new PlayerDeathEvent(__instance, reason);
|
||||
var deathEvent = new PlayerDeathEvent(__instance, reason, Helpers.GetBodyById(__instance.PlayerId));
|
||||
MiraEventManager.InvokeEvent(deathEvent);
|
||||
|
||||
var modifiersComponent = __instance.GetComponent<ModifierComponent>();
|
||||
@@ -68,6 +68,7 @@ internal static class PlayerControlPatches
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(PlayerControl.RpcMurderPlayer))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
// Note: This method is partially inlined in 2025.5.20 but we already use custom murder RPCs.
|
||||
public static void PlayerControlMurderPrefix(PlayerControl __instance, PlayerControl target, ref bool didSucceed)
|
||||
{
|
||||
var beforeMurderEvent = new BeforeMurderEvent(__instance, target);
|
||||
@@ -81,11 +82,6 @@ internal static class PlayerControlPatches
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void PlayerControlFixedUpdatePostfix(PlayerControl __instance)
|
||||
{
|
||||
if (__instance.Data?.Role is ICustomRole customRole)
|
||||
{
|
||||
customRole.PlayerControlFixedUpdate(__instance);
|
||||
}
|
||||
|
||||
if (!__instance.AmOwner)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Events.Vanilla.Meeting;
|
||||
|
||||
@@ -10,7 +10,7 @@ public static class GameStartupPatch
|
||||
private static bool _runOnce;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for registering roles when the game opens, might be a janky solution, but it works
|
||||
/// This is used for registering roles when the game opens, might be a janky solution, but it works.
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(MainMenuManager.Start))]
|
||||
|
||||
@@ -7,7 +7,8 @@ namespace MiraAPI.Patches.Roles;
|
||||
/// <summary>
|
||||
/// Patches the haunt menu to show the actual role name rather then team.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.SetFilterText))]
|
||||
[HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.SetHauntTarget))]
|
||||
// SetFilterText was INLINED
|
||||
public static class HauntMenuMinigamePatch
|
||||
{
|
||||
public static void Postfix(HauntMenuMinigame __instance)
|
||||
@@ -24,4 +25,4 @@ public static class HauntMenuMinigamePatch
|
||||
__instance.FilterText.text = role.NiceName;
|
||||
__instance.FilterText.color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,6 @@ public static class HudManagerPatches
|
||||
|
||||
if (role is ICustomRole { Configuration.RoleHintType: RoleHintType.RoleTab } customRole)
|
||||
{
|
||||
customRole.HudUpdate(__instance);
|
||||
|
||||
if (_roleTab == null)
|
||||
{
|
||||
_roleTab = CustomRoleManager.CreateRoleTab(customRole);
|
||||
|
||||
@@ -14,6 +14,8 @@ public static class RoleManagerPatches
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(RoleManager.SetRole))]
|
||||
// NOTE: As of 2025.5.20 update, this is INLINED in ONE place: GameManager::ReviveEveryoneFreeplay(void).
|
||||
// Should not affect normal gameplay, but may cause issues in Freeplay.
|
||||
public static bool SetRolePatch(RoleManager __instance, PlayerControl targetPlayer, RoleTypes roleType)
|
||||
{
|
||||
if (!targetPlayer)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using Il2CppSystem.Collections.Generic;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities;
|
||||
|
||||
@@ -12,7 +13,24 @@ namespace MiraAPI.Patches.Roles;
|
||||
public static class RoleOptionsCollectionPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Set the role chance for custom Launchpad roles based on config
|
||||
/// This patch fixes GetNumPerGame being inlined (2025.5.20) in the original code.
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(RoleOptionsCollectionV09.AnyRolesEnabled))]
|
||||
public static bool AnyRolesEnabledPrefix(RoleOptionsCollectionV09 __instance, ref bool __result)
|
||||
{
|
||||
foreach (KeyValuePair<RoleTypes, RoleDataV09> keyValuePair in __instance.roles)
|
||||
{
|
||||
if (__instance.GetNumPerGame(keyValuePair.Key) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the role chance for custom Launchpad roles based on config.
|
||||
/// </summary>
|
||||
/// <returns>Return false to skip original method, true to not.</returns>
|
||||
[HarmonyPrefix]
|
||||
@@ -42,7 +60,7 @@ public static class RoleOptionsCollectionPatch
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the amount for custom Launchpad roles based on config
|
||||
/// Set the amount for custom Launchpad roles based on config.
|
||||
/// </summary>
|
||||
/// <returns>Return false to skip original method, true to not.</returns>
|
||||
[HarmonyPrefix]
|
||||
|
||||
@@ -4,7 +4,7 @@ using MiraAPI.Roles;
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
[HarmonyPatch(typeof(SabotageButton))]
|
||||
public class SabotageButtonPatch
|
||||
public static class SabotageButtonPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Patches the Sabotage button to check if the player's custom role can use sabotage.
|
||||
|
||||
@@ -8,7 +8,6 @@ using MiraAPI.Events.Vanilla.Meeting.Voting;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Voting;
|
||||
using Reactor.Utilities;
|
||||
|
||||
namespace MiraAPI.Patches.Voting;
|
||||
|
||||
@@ -206,6 +205,7 @@ internal static class MeetingHudPatches
|
||||
// TODO: figure out a way to do host-authorization since right now any player can send RpcCastVote
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(MeetingHud.CmdCastVote))]
|
||||
// Although this method is inlined in MeetingHud.Confirm, the next patch fixes that.
|
||||
public static bool CmdCastVoteOverridePatch(MeetingHud __instance, byte playerId, byte suspectIdx)
|
||||
{
|
||||
VotingUtils.RpcCastVote(PlayerControl.LocalPlayer, playerId, suspectIdx);
|
||||
|
||||
@@ -2,8 +2,19 @@
|
||||
|
||||
namespace MiraAPI.PluginLoading;
|
||||
|
||||
/// <summary>
|
||||
/// The interface that all Mira plugins must implement.
|
||||
/// </summary>
|
||||
public interface IMiraPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name to display on the options menu.
|
||||
/// </summary>
|
||||
string OptionsTitleText { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BepInEx configuration file for the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The BepInEx configuration file for the plugin.</returns>
|
||||
public ConfigFile GetConfigFile();
|
||||
}
|
||||
@@ -55,21 +55,29 @@ public sealed class MiraPluginManager
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
foreach (var method in type.GetMethods())
|
||||
{
|
||||
var eventAttribute = method.GetCustomAttribute<RegisterEventAttribute>();
|
||||
if (eventAttribute != null)
|
||||
if (eventAttribute == null)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 1 || !parameters[0].ParameterType.IsSubclassOf(typeof(MiraEvent)))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Invalid event registration method {method.Name} in {type.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var paramType = parameters[0].ParameterType;
|
||||
MiraEventManager.RegisterEventHandler(paramType, method, eventAttribute.Priority);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!method.IsStatic)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Event method {method.Name} in {type.Name} must be static.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 1 || !parameters[0].ParameterType.IsSubclassOf(typeof(MiraEvent)))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Invalid event registration method {method.Name} in {type.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var paramType = parameters[0].ParameterType;
|
||||
MiraEventManager.RegisterEventHandler(paramType, method, eventAttribute.Priority);
|
||||
}
|
||||
|
||||
if (RegisterModifier(type, info))
|
||||
@@ -82,13 +90,13 @@ public sealed class MiraPluginManager
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RegisterRoleAttribute(type, info, out var role))
|
||||
if (RegisterRole(type, info, out var role))
|
||||
{
|
||||
roles.Add(role);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RegisterButtonAttribute(type, info))
|
||||
if (RegisterButton(type, info))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -164,6 +172,12 @@ public sealed class MiraPluginManager
|
||||
|
||||
foreach (var property in type.GetProperties())
|
||||
{
|
||||
if (property.GetMethod?.IsStatic == true)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Option property {property.Name} in {type.Name} must not be static.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property.PropertyType.IsAssignableTo(typeof(IModdedOption)))
|
||||
{
|
||||
ModdedOptionsManager.RegisterPropertyOption(type, property, pluginInfo);
|
||||
@@ -179,16 +193,21 @@ public sealed class MiraPluginManager
|
||||
ModdedOptionsManager.RegisterAttributeOption(type, attribute, property, pluginInfo);
|
||||
}
|
||||
|
||||
foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(IModdedOption))))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"{field.Name} is a field, not a property. Use properties for options.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Failed to register options for {type.Name}: {e}");
|
||||
Logger<MiraApiPlugin>.Error($"Failed to register options for {type.Name}: {e.ToString()}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool RegisterRoleAttribute(Type type, MiraPluginInfo pluginInfo, [NotNullWhen(true)] out Type? role)
|
||||
private static bool RegisterRole(Type type, MiraPluginInfo pluginInfo, [NotNullWhen(true)] out Type? role)
|
||||
{
|
||||
role = null;
|
||||
try
|
||||
@@ -244,6 +263,11 @@ public sealed class MiraPluginManager
|
||||
|
||||
PaletteManager.CustomColors.Add(color);
|
||||
}
|
||||
|
||||
foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(CustomColor))))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"{field.Name} is a field, not a property. Use properties for colors.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -264,7 +288,7 @@ public sealed class MiraPluginManager
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RegisterButtonAttribute(Type type, MiraPluginInfo pluginInfo)
|
||||
private static bool RegisterButton(Type type, MiraPluginInfo pluginInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
File renamed without changes.
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
@@ -8,12 +9,11 @@ namespace MiraAPI.Roles;
|
||||
/// <typeparam name="T">The role you are trying to access.</typeparam>
|
||||
public static class CustomRoleSingleton<T> where T : ICustomRole
|
||||
{
|
||||
private static ICustomRole? _instance;
|
||||
private static T? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the role.
|
||||
/// </summary>
|
||||
#pragma warning disable CA1000
|
||||
public static ICustomRole Instance => _instance ??= CustomRoleManager.CustomRoles.Values.OfType<T>().Single();
|
||||
#pragma warning restore CA1000
|
||||
[SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "This is a utility class to get the instance of a custom role.")]
|
||||
public static T Instance => _instance ??= CustomRoleManager.CustomRoles.Values.OfType<T>().Single();
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public static class CustomRoleUtils
|
||||
public static StringBuilder CreateForRole(ICustomRole role)
|
||||
{
|
||||
var taskStringBuilder = new StringBuilder();
|
||||
taskStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{role.RoleColor.ToTextColor()}You are a <b>{role.RoleName}.</b></color>");
|
||||
taskStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{role.RoleColor.ToTextColor()}Your role is <b>{role.RoleName}.</b></color>");
|
||||
taskStringBuilder.Append("<size=70%>");
|
||||
taskStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{role.RoleLongDescription}");
|
||||
return taskStringBuilder;
|
||||
|
||||
@@ -73,14 +73,6 @@ public interface ICustomRole : IOptionable
|
||||
/// </summary>
|
||||
MiraPluginInfo ParentMod => CustomRoleManager.FindParentMod(this);
|
||||
|
||||
/// <summary>
|
||||
/// This method runs on the PlayerControl.FixedUpdate method for ALL players with this role.
|
||||
/// </summary>
|
||||
/// <param name="playerControl">The PlayerControl that has this role.</param>
|
||||
void PlayerControlFixedUpdate(PlayerControl playerControl)
|
||||
{
|
||||
}
|
||||
|
||||
internal ConfigDefinition NumConfigDefinition => new("Roles", $"Num {GetType().FullName}");
|
||||
internal ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance {GetType().FullName}");
|
||||
|
||||
@@ -222,14 +214,6 @@ public interface ICustomRole : IOptionable
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method runs on the HudManager.Update method ONLY when the LOCAL player has this role.
|
||||
/// </summary>
|
||||
/// <param name="hudManager">Reference to HudManager instance.</param>
|
||||
void HudUpdate(HudManager hudManager)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a custom ejection message for the role. Return null to use the default message.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Enum for modded role teams.
|
||||
/// </summary>
|
||||
public enum ModdedRoleTeams
|
||||
{
|
||||
/// <summary>
|
||||
/// The crewmate team.
|
||||
/// </summary>
|
||||
Crewmate,
|
||||
|
||||
/// <summary>
|
||||
/// The impostor team.
|
||||
/// </summary>
|
||||
Impostor,
|
||||
|
||||
/// <summary>
|
||||
/// Custom team, catchall for any other teams.
|
||||
/// </summary>
|
||||
Custom,
|
||||
}
|
||||
+16
-1
@@ -2,9 +2,18 @@
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Utility class to get the ID of a role.
|
||||
/// </summary>
|
||||
public static class RoleId
|
||||
{
|
||||
public static ushort Get<T>() where T : RoleBehaviour
|
||||
/// <summary>
|
||||
/// Gets the ID of a role.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the role.</typeparam>
|
||||
/// <returns>The role ID as a ushort.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the role is not registered.</exception>
|
||||
public static ushort Get<T>() where T : ICustomRole
|
||||
{
|
||||
if (!CustomRoleManager.RoleIds.TryGetValue(typeof(T), out var roleId))
|
||||
{
|
||||
@@ -14,6 +23,12 @@ public static class RoleId
|
||||
return roleId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID of a role.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of the role.</param>
|
||||
/// <returns>The ID as a ushort.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the role is not registered.</exception>
|
||||
public static ushort Get(Type type)
|
||||
{
|
||||
if (!CustomRoleManager.RoleIds.TryGetValue(type, out var roleId))
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace MiraAPI.Utilities.Assets;
|
||||
/// A utility class for loading assets from embedded resources.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the embedded resource.</param>
|
||||
/// <param name="pixelsPerUnit">The pixels per unit for the loaded sprite.</param>
|
||||
public class LoadableResourceAsset(string path, float pixelsPerUnit=100f) : LoadableAsset<Sprite>
|
||||
{
|
||||
private readonly Assembly _assembly = Assembly.GetCallingAssembly();
|
||||
@@ -14,9 +15,9 @@ public class LoadableResourceAsset(string path, float pixelsPerUnit=100f) : Load
|
||||
/// <inheritdoc />
|
||||
public override Sprite LoadAsset()
|
||||
{
|
||||
if (LoadedAsset != null)
|
||||
if (LoadedAsset)
|
||||
{
|
||||
return LoadedAsset;
|
||||
return LoadedAsset!;
|
||||
}
|
||||
|
||||
return LoadedAsset = SpriteTools.LoadSpriteFromPath(path, _assembly, pixelsPerUnit);
|
||||
|
||||
@@ -4,7 +4,7 @@ using UnityEngine;
|
||||
namespace MiraAPI.Utilities.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// Various assets used throughout Mira API.
|
||||
/// A static class that contains various assets used in the Mira API.
|
||||
/// </summary>
|
||||
public static class MiraAssets
|
||||
{
|
||||
@@ -25,21 +25,40 @@ public static class MiraAssets
|
||||
RoundedBox = new LoadableAssetWrapper<Sprite>(boxSprite);
|
||||
}
|
||||
|
||||
internal static readonly AssetBundle MiraAssetBundle = AssetBundleManager.Load("mirabundle");
|
||||
internal static readonly LoadableAsset<GameObject> ModifierDisplay = new LoadableBundleAsset<GameObject>("Modifiers", MiraAssetBundle);
|
||||
internal static readonly LoadableAsset<GameObject> PresetSavePopup = new LoadableBundleAsset<GameObject>("PresetSavePopup", MiraAssetBundle);
|
||||
internal static readonly LoadableAsset<Sprite> RefreshIcon = new LoadableBundleAsset<Sprite>("refresh", MiraAssetBundle);
|
||||
internal static readonly LoadableAsset<Sprite> FolderIcon = new LoadableBundleAsset<Sprite>("freePlay_folderTaskRoom", MiraAssetBundle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the accepted teal color used for various selections in the UI.
|
||||
/// Gets the color used for teal highlighting in UI.
|
||||
/// </summary>
|
||||
public static Color32 AcceptedTeal { get; } = new(43, 233, 198, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the empty sprite.
|
||||
/// Gets the Mira API asset bundle.
|
||||
/// </summary>
|
||||
public static LoadableAsset<Sprite> Empty { get; } = new LoadableResourceAsset("MiraAPI.Resources.Empty.png");
|
||||
public static AssetBundle MiraAssetBundle { get; } = AssetBundleManager.Load("mirabundle");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ModifierDisplay prefab.
|
||||
/// </summary>
|
||||
public static LoadableAsset<GameObject> ModifierDisplay { get; } = new LoadableBundleAsset<GameObject>("Modifiers", MiraAssetBundle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Popup prefab for saving presets.
|
||||
/// </summary>
|
||||
public static LoadableAsset<GameObject> PresetSavePopup { get; } = new LoadableBundleAsset<GameObject>("PresetSavePopup", MiraAssetBundle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Refresh Icon sprite.
|
||||
/// </summary>
|
||||
public static LoadableAsset<Sprite> RefreshIcon { get; } = new LoadableBundleAsset<Sprite>("refresh", MiraAssetBundle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Folder Icon sprite.
|
||||
/// </summary>
|
||||
public static LoadableAsset<Sprite> FolderIcon { get; } = new LoadableBundleAsset<Sprite>("freePlay_folderTaskRoom", MiraAssetBundle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the empty sprite asset.
|
||||
/// </summary>
|
||||
public static LoadableResourceAsset Empty { get; } = new("MiraAPI.Resources.Empty.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RoundedBox sprite, which is a rounded rectangle used for UI elements.
|
||||
@@ -47,32 +66,32 @@ public static class MiraAssets
|
||||
public static LoadableAsset<Sprite> RoundedBox { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the NextButton sprite.
|
||||
/// Gets the Next Button sprite.
|
||||
/// </summary>
|
||||
public static LoadableAsset<Sprite> NextButton { get; } = new LoadableResourceAsset("MiraAPI.Resources.NextButton.png");
|
||||
public static LoadableResourceAsset NextButton { get; } = new("MiraAPI.Resources.NextButton.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active variant of the NextButton sprite.
|
||||
/// Gets the highlighted Next Button sprite.
|
||||
/// </summary>
|
||||
public static LoadableAsset<Sprite> NextButtonActive { get; } = new LoadableResourceAsset("MiraAPI.Resources.NextButtonActive.png");
|
||||
public static LoadableResourceAsset NextButtonActive { get; } = new("MiraAPI.Resources.NextButtonActive.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Cog icon sprite.
|
||||
/// Gets the Cog icon used in Role Settings Menu.
|
||||
/// </summary>
|
||||
public static LoadableAsset<Sprite> Cog { get; } = new LoadableResourceAsset("MiraAPI.Resources.Cog.png");
|
||||
public static LoadableResourceAsset Cog { get; } = new("MiraAPI.Resources.Cog.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Checkmark icon sprite.
|
||||
/// Gets the Checkmark Box used in the Settings Menu.
|
||||
/// </summary>
|
||||
public static LoadableAsset<Sprite> Checkmark { get; } = new LoadableResourceAsset("MiraAPI.Resources.Checkmark.png");
|
||||
public static LoadableResourceAsset CheckmarkBox { get; } = new("MiraAPI.Resources.CheckMarkBox.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Checkmark Box sprite.
|
||||
/// Gets the Checkmark used in the Settings Menu.
|
||||
/// </summary>
|
||||
public static LoadableAsset<Sprite> CheckmarkBox { get; } = new LoadableResourceAsset("MiraAPI.Resources.CheckMarkBox.png");
|
||||
public static LoadableResourceAsset Checkmark { get; } = new("MiraAPI.Resources.Checkmark.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom category header sprite used in the options menu.
|
||||
/// Gets the white CategoryHeader used in the Settings Menu.
|
||||
/// </summary>
|
||||
public static LoadableAsset<Sprite> CategoryHeader { get; } = new LoadableResourceAsset("MiraAPI.Resources.CategoryHeader.png");
|
||||
public static LoadableResourceAsset CategoryHeader { get; } = new("MiraAPI.Resources.CategoryHeader.png");
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -84,6 +85,51 @@ public static class Extensions
|
||||
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>
|
||||
/// Gets a PlayerControl from their PlayerVoteArea in a meeting.
|
||||
/// </summary>
|
||||
@@ -145,6 +191,12 @@ public static class Extensions
|
||||
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)
|
||||
{
|
||||
tie = true;
|
||||
@@ -164,6 +216,12 @@ public static class Extensions
|
||||
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)
|
||||
{
|
||||
tie = true;
|
||||
@@ -385,18 +443,6 @@ public static class Extensions
|
||||
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>
|
||||
/// Lightens a color by a specified amount.
|
||||
/// </summary>
|
||||
@@ -408,16 +454,6 @@ public static class Extensions
|
||||
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>
|
||||
/// Gets the nearest dead body to a player.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,8 +1,27 @@
|
||||
namespace MiraAPI.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Suffixes used for options numbers.
|
||||
/// </summary>
|
||||
public enum MiraNumberSuffixes
|
||||
{
|
||||
/// <summary>
|
||||
/// No suffix.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier suffix (x).
|
||||
/// </summary>
|
||||
Multiplier,
|
||||
|
||||
/// <summary>
|
||||
/// Seconds suffix (s).
|
||||
/// </summary>
|
||||
Seconds,
|
||||
|
||||
/// <summary>
|
||||
/// Percentage suffix (%).
|
||||
/// </summary>
|
||||
Percent,
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public static class VotingUtils
|
||||
/// <param name="source">The player who is sending the RPC. Should be the host.</param>
|
||||
/// <param name="voterId">The player who voted.</param>
|
||||
/// <param name="votedFor">The player who the voter voted for.</param>
|
||||
[MethodRpc((uint)MiraRpc.RemoveVote)]
|
||||
[MethodRpc((uint)MiraRpc.RemoveVote, SendImmediately = true)]
|
||||
public static void RpcRemoveVote(PlayerControl source, byte voterId, byte votedFor)
|
||||
{
|
||||
if (!source.IsHost()) return;
|
||||
@@ -107,7 +107,7 @@ public static class VotingUtils
|
||||
/// <param name="source">The player who sent this RPC.</param>
|
||||
/// <param name="srcPlayerId">The id of the player who casted the vote.</param>
|
||||
/// <param name="suspectPlayerId">The voted player's id.</param>
|
||||
[MethodRpc((uint)MiraRpc.CastVote)]
|
||||
[MethodRpc((uint)MiraRpc.CastVote, SendImmediately = true)]
|
||||
public static void RpcCastVote(PlayerControl source, byte srcPlayerId, byte suspectPlayerId)
|
||||
{
|
||||
CustomCastVote(srcPlayerId, suspectPlayerId);
|
||||
|
||||
Reference in new issue
Block a user