Compare commits

..
Author SHA1 Message Date
ang-xd 0af392d60e Make buttons act more like vanilla 2025-02-16 01:04:15 -05:00
ang-xd e910805e50 Delete log 2025-02-15 23:54:47 -05:00
ang-xd 2b1db0ee24 Whoops 2025-02-15 17:59:40 -05:00
ang-xd 2bb1d41e65 Fix issue #24 2025-02-15 13:49:38 -05:00
Pietrodjaowjao 7bf35fc8ae Fix player skin resetting on no-teleport murder 2025-02-15 15:49:09 -03:00
XtraCube 719ee0400d for MDB fr this time 🙏 2025-02-13 18:21:36 -05:00
ang-xd 4db7605d27 Some changes to role visibility name text 2025-02-13 18:10:42 -05:00
ang-xd c7d16f7fc9 Try fix console spam bug 2025-02-13 17:43:24 -05:00
ang-xd 8ed38c02ce This ones for Xtra 2025-02-13 17:37:44 -05:00
ang-xd 328ee52549 SetRole event 2025-02-13 17:08:21 -05:00
ang-xd e412e5ead5 Some fixes 2025-02-13 16:51:00 -05:00
XtraCube 59ab2736ab further optimization? 2025-02-13 16:19:12 -05:00
XtraCube bb27f83e8b miradisable -> miraignore 2025-02-13 16:18:53 -05:00
XtraCube 7fe2341be0 fix miradisable bug 2025-02-10 16:43:45 -05:00
XtraCube 5bf39593c5 get rid of annoying log 2025-02-10 16:41:00 -05:00
XtraCube 1e56c8860a Merge pull request #18 from All-Of-Us-Mods/optimization
optimize plugin loading (and other optimizations if I find them)
2025-02-09 22:11:31 -05:00
XtraCube e569c27010 replace most attributes and add MiraDisableAttribute.cs 2025-02-03 21:58:57 -05:00
XtraCube 145ae304eb remove extra thingy 2025-02-02 20:59:44 -05:00
XtraCube dcf557ca46 optimize plugin loading 2025-02-02 20:57:10 -05:00
36 changed files with 321 additions and 218 deletions

No files matched your search

@@ -9,7 +9,6 @@ using UnityEngine;
namespace MiraAPI.Example.Buttons.Freezer;
[RegisterButton]
public class FreezeButton : CustomActionButton<PlayerControl>
{
public override string Name => "Freeze";
-1
View File
@@ -6,7 +6,6 @@ using UnityEngine;
namespace MiraAPI.Example.Buttons;
[RegisterButton]
public class MeetingButton : CustomActionButton
{
public override string Name => "Call Meeting";
@@ -9,7 +9,6 @@ using UnityEngine;
namespace MiraAPI.Example.Buttons.Teleporter;
[RegisterButton]
public class TeleportButton : CustomActionButton
{
public override string Name => "Teleport";
@@ -17,6 +16,7 @@ public class TeleportButton : CustomActionButton
public override float Cooldown => OptionGroupSingleton<TeleporterOptions>.Instance.TeleportCooldown.Value;
public override float EffectDuration => OptionGroupSingleton<TeleporterOptions>.Instance.TeleportDuration;
public override int MaxUses => 2;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.TeleportButton;
@@ -3,7 +3,6 @@ using MiraAPI.Modifiers.Types;
namespace MiraAPI.Example.Modifiers;
[RegisterModifier]
public class CaptainModifier : GameModifier
{
public override string ModifierName => "Captain";
@@ -6,7 +6,6 @@ using UnityEngine;
namespace MiraAPI.Example.Modifiers.Freezer;
[RegisterModifier]
public class FreezeModifier : TimedModifier
{
public override string ModifierName => "Freezed";
@@ -3,7 +3,6 @@ using MiraAPI.Modifiers.Types;
namespace MiraAPI.Example.Modifiers;
[RegisterModifier]
public class HighPriorityModifier : GameModifier
{
public override string ModifierName => "High Priority";
-1
View File
@@ -4,7 +4,6 @@ using UnityEngine;
namespace MiraAPI.Example.Roles;
[RegisterCustomRole]
public class ChameloenRole : CrewmateRole, ICustomRole
{
public string RoleName => "Chamelon";
-1
View File
@@ -3,7 +3,6 @@ using UnityEngine;
namespace MiraAPI.Example.Roles;
[RegisterCustomRole]
public class FreezerRole : ImpostorRole, ICustomRole
{
public string RoleName => "Freezer";
@@ -3,7 +3,6 @@ using UnityEngine;
namespace MiraAPI.Example.Roles;
[RegisterCustomRole]
public class NeutralKillerRole : ImpostorRole, ICustomRole
{
public string RoleName => "Neutral Killer";
+5 -1
View File
@@ -3,7 +3,6 @@ using UnityEngine;
namespace MiraAPI.Example.Roles;
[RegisterCustomRole]
public class TeleporterRole : CrewmateRole, ICustomRole
{
public string RoleName => "Teleporter";
@@ -19,4 +18,9 @@ public class TeleporterRole : CrewmateRole, ICustomRole
DefaultChance = 73,
DefaultRoleCount = 4,
};
public bool CanLocalPlayerSeeRole(PlayerControl player)
{
return true;
}
}
-1
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Reactor.Utilities;
namespace MiraAPI.Events;
@@ -0,0 +1,30 @@
using AmongUs.GameOptions;
namespace MiraAPI.Events.Vanilla.Gameplay;
/// <summary>
/// Event that is invoked after a player's role is set. This event is not cancelable.
/// </summary>
public class SetRoleEvent : MiraEvent
{
/// <summary>
/// Gets the player whos role was changed.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Gets the role that the player was set to.
/// </summary>
public RoleTypes Role { get; }
/// <summary>
/// Initializes a new instance of the <see cref="SetRoleEvent"/> class.
/// </summary>
/// <param name="player">The player.</param>
/// <param name="role">The new role.</param>
public SetRoleEvent(PlayerControl player, RoleTypes role)
{
Player = player;
Role = role;
}
}
@@ -1,15 +1,10 @@
namespace MiraAPI.Events.Vanilla.Meeting;
/// <summary>
/// The event that is invoked when a player is ejected.
/// The event that is invoked when a player is ejected. Non-cancelable.
/// </summary>
public class EjectionEvent : MiraCancelableEvent
public class EjectionEvent : MiraEvent
{
/// <summary>
/// Gets the player that is being ejected.
/// </summary>
public PlayerControl? Player { get; }
/// <summary>
/// Gets the instance of the ExileController.
/// </summary>
@@ -18,11 +13,9 @@ public class EjectionEvent : MiraCancelableEvent
/// <summary>
/// Initializes a new instance of the <see cref="EjectionEvent"/> class.
/// </summary>
/// <param name="player">The player who is being ejected.</param>
/// <param name="controller">The exile controller.</param>
public EjectionEvent(PlayerControl player, ExileController controller)
public EjectionEvent(ExileController controller)
{
Player = player;
ExileController = controller;
}
}
-1
View File
@@ -1,6 +1,5 @@
namespace MiraAPI.GameModes;
[RegisterGameMode]
public class DefaultMode : CustomGameMode
{
public override string Name => "Default";
@@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace MiraAPI.GameModes;
[AttributeUsage(AttributeTargets.Class)]
public class RegisterGameModeAttribute : Attribute
{
private static readonly HashSet<Assembly> RegisteredAssemblies = [];
internal static void Register(Assembly assembly)
{
if (!RegisteredAssemblies.Add(assembly))
{
return;
}
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<RegisterGameModeAttribute>();
if (attribute != null)
{
CustomGameModeManager.RegisterGameMode(type);
}
}
}
}
@@ -31,7 +31,6 @@ public static class ModdedOptionsManager
{
if (Activator.CreateInstance(type) is not AbstractOptionGroup group)
{
Logger<MiraApiPlugin>.Error($"Skipping option group {type.Name}. Does not inherit AbstractOptionGroup.");
return false;
}
+54 -8
View File
@@ -65,6 +65,11 @@ public abstract class CustomActionButton
/// </summary>
public bool EffectActive { get; protected set; }
/// <summary>
/// Gets or sets a value indicating whether the timer is currently active.
/// </summary>
public bool TimerPaused { get; protected set; }
/// <summary>
/// Gets or sets the amount of uses left.
/// </summary>
@@ -94,6 +99,7 @@ public abstract class CustomActionButton
UsesLeft = MaxUses;
Timer = AmongUsClient.Instance?.NetworkMode == NetworkModes.FreePlay ? 0 : InitialCooldown;
EffectActive = false;
TimerPaused = false;
Button = Object.Instantiate(HudManager.Instance.AbilityButton, parent);
Button.name = Name + "Button";
@@ -154,7 +160,7 @@ public abstract class CustomActionButton
/// <param name="moveButton">Whether the button's position should change ingame.</param>
public virtual void SetButtonLocation(ButtonLocation location, bool moveButton = true)
{
if (HudManager.Instance == null || Button == null)
if (!HudManager.InstanceExists || Button == null)
{
return;
}
@@ -248,6 +254,15 @@ public abstract class CustomActionButton
SetTimer(Timer - amount);
}
/// <summary>
/// Sets whether the timer is paused or not.
/// </summary>
/// <param name="val">Whether you want to pause/resume the timer.</param>
public virtual void SetTimerPaused(bool val)
{
TimerPaused = val;
}
/// <summary>
/// Set the amount of uses this button has left.
/// </summary>
@@ -256,6 +271,11 @@ public abstract class CustomActionButton
{
UsesLeft = Mathf.Clamp(amount, 0, int.MaxValue);
Button?.SetUsesRemaining(UsesLeft);
if (Button != null)
{
Button.usesRemainingSprite.color = UsesLeft == 0 ? Color.red : Color.white;
}
}
/// <summary>
@@ -306,14 +326,24 @@ public abstract class CustomActionButton
}
/// <summary>
/// When the button is enabled, this method is called to determine if the button can be used.
/// When the button is usable, this method is called to determine if the button can be clicked.
/// By default, it takes into account the timer, effect, and uses.
/// You can override it to change the behavior.
/// </summary>
/// <returns>A value that represents whether the button can be used or not.</returns>
/// <returns>A value that represents whether the button can be clicked or not.</returns>
public virtual bool CanClick()
{
return Timer <= 0 && !EffectActive && CanUse();
}
/// <summary>
/// Whether the button should light up or not. This is also the base for CanClick.
/// You can override it to change the behaviour. Do not include timer in here, that is for CanClick.
/// </summary>
/// <returns>A value that represents whether the button should light up or not.</returns>
public virtual bool CanUse()
{
return Timer <= 0 && !EffectActive && (!LimitedUses || UsesLeft > 0);
return !LimitedUses || UsesLeft > 0;
}
/// <summary>
@@ -335,7 +365,7 @@ public abstract class CustomActionButton
/// </summary>
public virtual void ClickHandler()
{
if (!CanUse())
if (!CanClick())
{
return;
}
@@ -347,7 +377,7 @@ public abstract class CustomActionButton
}
OnClick();
Button?.SetDisabled();
if (HasEffect)
{
EffectActive = true;
@@ -367,7 +397,7 @@ public abstract class CustomActionButton
/// <param name="playerControl">The local PlayerControl.</param>
public virtual void FixedUpdateHandler(PlayerControl playerControl)
{
if (Timer >= 0)
if (Timer >= 0 && !TimerPaused)
{
Timer -= Time.deltaTime;
}
@@ -387,7 +417,17 @@ public abstract class CustomActionButton
Button?.SetDisabled();
}
Button?.SetCoolDown(Timer, EffectActive ? EffectDuration : Cooldown);
if (EffectActive)
{
Button?.SetFillUp(Timer, EffectDuration);
Button!.cooldownTimerText.text = Mathf.CeilToInt(Timer).ToString();
Button!.cooldownTimerText.gameObject.SetActive(true);
}
else
{
Button?.SetCoolDown(Timer, Cooldown);
}
FixedUpdate(playerControl);
}
@@ -446,6 +486,12 @@ public abstract class CustomActionButton<T> : CustomActionButton where T : MonoB
return base.CanUse() && Target != null;
}
/// <inheritdoc />
public override bool CanClick()
{
return base.CanClick() && Target != null;
}
/// <summary>
/// Use this to reset the button's target after used.
/// </summary>
+11 -11
View File
@@ -1,8 +1,8 @@
using MiraAPI.Events.Mira;
using MiraAPI.PluginLoading;
using Reactor.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
namespace MiraAPI.Hud;
@@ -12,22 +12,20 @@ namespace MiraAPI.Hud;
/// </summary>
public static class CustomButtonManager
{
/// <summary>
/// Gets a list of all registered custom buttons.
/// </summary>
public static ReadOnlyCollection<CustomActionButton> Buttons { get; internal set; } = new([]);
internal static readonly List<CustomActionButton> CustomButtons = [];
internal static readonly Dictionary<Type, Type> ButtonEventTypes = [];
internal static readonly Dictionary<Type, Type> ButtonCancelledEventTypes = [];
internal static void RegisterButton(Type buttonType, MiraPluginInfo pluginInfo)
internal static bool RegisterButton(Type buttonType, MiraPluginInfo pluginInfo)
{
if (!typeof(CustomActionButton).IsAssignableFrom(buttonType))
if (!buttonType.IsAssignableTo(typeof(CustomActionButton)) || Activator.CreateInstance(buttonType) is not CustomActionButton button)
{
Logger<MiraApiPlugin>.Error($"Skipping button {buttonType.Name}. Does not inherit CustomActionButton!");
return;
}
if (Activator.CreateInstance(buttonType) is not CustomActionButton button)
{
Logger<MiraApiPlugin>.Error($"Failed to create button from {buttonType.Name}");
return;
return false;
}
CustomButtons.Add(button);
@@ -38,5 +36,7 @@ public static class CustomButtonManager
ButtonEventTypes.Add(buttonType, typeof(MiraButtonClickEvent<>).MakeGenericType(buttonType));
ButtonCancelledEventTypes.Add(buttonType, typeof(MiraButtonCancelledEvent<>).MakeGenericType(buttonType));
return true;
}
}
+11 -5
View File
@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using AmongUs.GameOptions;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Patches.Stubs;
using Reactor.Utilities.Attributes;
using Reactor.Utilities.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
@@ -86,10 +86,16 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr)
/// <param name="playerMatch">Function to determine if player should show in the custom menu.</param>
/// <param name="onClick">Onclick action for player.</param>
[HideFromIl2Cpp]
public void Begin(Func<PlayerControl, bool> playerMatch, Action<PlayerControl> onClick)
public void Begin(Func<PlayerControl, bool> playerMatch, Action<PlayerControl?> onClick)
{
MinigameStubs.Begin(this, null);
var back = BackButton.GetComponent<PassiveButton>();
back.OnClick.AddListener((UnityAction)(() =>
{
onClick(null);
}));
DestroyableSingleton<DebugAnalytics>.Instance.Analytics.MinigameOpened(PlayerControl.LocalPlayer.Data, TaskType);
var list = PlayerControl.AllPlayerControls.ToArray().Where(playerMatch).ToList();
potentialVictims = [];
-9
View File
@@ -1,9 +0,0 @@
using System;
namespace MiraAPI.Hud;
/// <summary>
/// Attribute to register a button in the HUD. Necessary for the button to be recognized by Mira.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class RegisterButtonAttribute : Attribute;
+7 -7
View File
@@ -1,14 +1,14 @@
using System;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Modifiers.Types;
using MiraAPI.Utilities;
using Reactor.Utilities;
using Reactor.Utilities.Attributes;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Text;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Modifiers.Types;
using MiraAPI.Utilities;
using Reactor.Utilities;
using Reactor.Utilities.Attributes;
using TMPro;
using UnityEngine;
@@ -107,7 +107,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
var baseModifiers = filteredModifiers as BaseModifier[] ?? filteredModifiers.ToArray();
if (baseModifiers.Length != 0)
if (baseModifiers.Length != 0 && !MeetingHud.Instance)
{
var stringBuild = new StringBuilder();
foreach (var mod in baseModifiers)
+4 -3
View File
@@ -49,11 +49,11 @@ public static class ModifierManager
return TypeToIdModifierMap.GetValueOrDefault(type);
}
internal static void RegisterModifier(Type modifierType)
internal static bool RegisterModifier(Type modifierType)
{
if (!typeof(BaseModifier).IsAssignableFrom(modifierType))
{
return;
return false;
}
IdToTypeModifierMap.Add(GetNextId(), modifierType);
@@ -61,7 +61,7 @@ public static class ModifierManager
if (!typeof(GameModifier).IsAssignableFrom(modifierType))
{
return;
return true;
}
var mod = Activator.CreateInstance(modifierType) as GameModifier;
@@ -73,6 +73,7 @@ public static class ModifierManager
}
list.Add(_nextId);
return true;
}
internal static void AssignModifiers(List<PlayerControl> plrs)
@@ -1,9 +0,0 @@
using System;
namespace MiraAPI.Modifiers;
/// <summary>
/// Marks a class as a modifier that can be registered with the <see cref="ModifierManager"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class RegisterModifierAttribute : Attribute;
+2 -5
View File
@@ -5,7 +5,6 @@ using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
using Reactor.Networking.Attributes;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using System.Collections;
using System.Linq;
@@ -116,7 +115,6 @@ public static class CustomMurderRpc
target.RemoveProtection();
}
Logger<MiraApiPlugin>.Error($"{source.PlayerId} failed to murder {target.PlayerId} due to guardian angel protection");
return;
}
@@ -184,7 +182,6 @@ public static class CustomMurderRpc
source.shapeshiftTargetPlayerId,
target.PlayerId);
source.MyPhysics.StartCoroutine(source.KillAnimations.Random()?.CoPerformCustomKill(source, target, createDeadBody, teleportMurderer));
Logger<MiraApiPlugin>.Error($"{source.PlayerId} succeeded in murdering {target.PlayerId}");
}
/// <summary>
@@ -250,11 +247,11 @@ public static class CustomMurderRpc
}
target.Die(DeathReason.Kill, true);
yield return source.MyPhysics.Animations.CoPlayCustomAnimation(anim.BlurAnim);
sourcePhys.Animations.PlayIdleAnimation();
if (teleportMurderer)
{
yield return source.MyPhysics.Animations.CoPlayCustomAnimation(anim.BlurAnim);
sourcePhys.Animations.PlayIdleAnimation();
source.NetTransform.SnapTo(target.transform.position);
KillAnimation.SetMovement(source, true);
}
+10 -4
View File
@@ -26,11 +26,17 @@ public static class CanUsePatches
[HarmonyPrefix]
public static bool CanUsePatch(Il2CppSystem.Object __instance, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] out bool canUse, [HarmonyArgument(2)] out bool couldUse)
{
var @event = new PlayerCanUseEvent(__instance.Cast<IUsable>());
MiraEventManager.InvokeEvent(@event);
canUse = couldUse = false;
return !@event.IsCancelled;
IUsable usable = __instance.Cast<IUsable>();
if (usable != null)
{
var @event = new PlayerCanUseEvent(__instance.Cast<IUsable>());
MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled;
}
return true;
}
}
+1 -5
View File
@@ -15,13 +15,9 @@ public static class EjectionPatches
[HarmonyPatch(nameof(ExileController.Begin))]
public static void BeginPostfix(ExileController __instance)
{
var @event = new EjectionEvent(__instance.initData.networkedPlayer.Object, __instance);
var @event = new EjectionEvent(__instance);
MiraEventManager.InvokeEvent(@event);
if (@event.IsCancelled)
{
return;
}
if (!__instance.initData.networkedPlayer || !__instance.initData.networkedPlayer.Role ||
__instance.initData.networkedPlayer.Role is not ICustomRole role)
{
+27
View File
@@ -0,0 +1,27 @@
using HarmonyLib;
using MiraAPI.Roles;
using UnityEngine;
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))]
public static class HauntMenuMinigamePatch
{
public static void Postfix(HauntMenuMinigame __instance)
{
if (__instance.HauntTarget.Data.IsDead)
{
__instance.FilterText.color = Color.white;
return;
}
var role = __instance.HauntTarget.Data.Role;
var color = role is ICustomRole custom ? custom.RoleColor : role.TeamColor;
__instance.FilterText.text = role.NiceName;
__instance.FilterText.color = color;
}
}
+8 -1
View File
@@ -1,10 +1,11 @@
using HarmonyLib;
using MiraAPI.Roles;
using UnityEngine;
namespace MiraAPI.Patches.Roles;
/// <summary>
/// Set nametag color depending on option
/// Set nametag color depending on visibility.
/// </summary>
[HarmonyPatch(typeof(PlayerNameColor))]
public static class NameTagPatch
@@ -13,6 +14,12 @@ public static class NameTagPatch
[HarmonyPatch(nameof(PlayerNameColor.Get), typeof(RoleBehaviour))]
public static bool GetPatch([HarmonyArgument(0)] RoleBehaviour otherPlayerRole, ref Color __result)
{
if (otherPlayerRole is ICustomRole customRole && customRole.CanLocalPlayerSeeRole(otherPlayerRole.Player))
{
__result = customRole.RoleColor;
return false;
}
if (PlayerControl.LocalPlayer.Data.Role.IsImpostor && otherPlayerRole.IsImpostor)
{
return true;
@@ -5,10 +5,10 @@ using UnityEngine;
namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(RoleBehaviour))]
public static class RoleBehaviourPatches
public static class RoleBehaviourPatch
{
/// <summary>
/// Update TeamColor text for Launchpad roles
/// Update TeamColor text for Mira roles.
/// </summary>
[HarmonyPrefix]
[HarmonyPatch(nameof(RoleBehaviour.TeamColor), MethodType.Getter)]
@@ -22,4 +22,4 @@ public static class RoleBehaviourPatches
__result = behaviour.RoleColor;
return false;
}
}
}
@@ -1,5 +1,7 @@
using AmongUs.GameOptions;
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay;
using MiraAPI.Modifiers;
using MiraAPI.Roles;
using System.Linq;
@@ -9,6 +11,14 @@ namespace MiraAPI.Patches.Roles;
[HarmonyPatch(typeof(RoleManager))]
public static class RoleManagerPatches
{
[HarmonyPostfix]
[HarmonyPatch(nameof(RoleManager.SetRole))]
public static void SetRolePatch(RoleManager __instance, PlayerControl targetPlayer, RoleTypes roleType)
{
var @event = new SetRoleEvent(targetPlayer, roleType);
MiraEventManager.InvokeEvent(@event);
}
[HarmonyPostfix]
[HarmonyPatch(nameof(RoleManager.SelectRoles))]
public static void ModifierSelectionPatches(RoleManager __instance)
@@ -0,0 +1,9 @@
using System;
namespace MiraAPI.PluginLoading;
/// <summary>
/// Skip an element during plugin loading.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, Inherited = false)]
public class MiraIgnoreAttribute : Attribute;
+95 -52
View File
@@ -11,6 +11,7 @@ using Reactor.Networking;
using Reactor.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
@@ -38,42 +39,75 @@ public sealed class MiraPluginManager
}
var info = new MiraPluginInfo(miraPlugin, pluginInfo);
var roles = new List<Type>();
RegisterModifierAttribute(assembly);
RegisterAllOptions(assembly, info);
foreach (var type in assembly.GetTypes())
{
if (type.GetCustomAttribute<MiraIgnoreAttribute>() != null)
{
continue;
}
RegisterRoleAttribute(assembly, info);
RegisterButtonAttribute(assembly, info);
if (RegisterModifier(type))
{
continue;
}
RegisterColorClasses(assembly);
if (RegisterOptions(type, info))
{
continue;
}
if (RegisterRoleAttribute(type, info, out var role))
{
roles.Add(role!);
continue;
}
if (RegisterButtonAttribute(type, info))
{
continue;
}
RegisterColorClasses(type);
}
info.OptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority));
CustomRoleManager.RegisterRoleTypes(roles, info);
_registeredPlugins.Add(assembly, info);
Logger<MiraApiPlugin>.Info($"Registering mod {pluginInfo.Metadata.GUID} with Mira API.");
};
IL2CPPChainloader.Instance.Finished += PaletteManager.RegisterAllColors;
IL2CPPChainloader.Instance.Finished += MiraEventManager.SortAllHandlers;
IL2CPPChainloader.Instance.Finished += () =>
{
CustomButtonManager.Buttons = new ReadOnlyCollection<CustomActionButton>(CustomButtonManager.CustomButtons);
};
}
/// <summary>
/// Get a mira plugin by its GUID.
/// </summary>
/// <param name="guid">The plugin GUID.</param>
/// <param name="pluginId">The plugin GUID.</param>
/// <returns>A MiraPluginInfo.</returns>
public static MiraPluginInfo GetPluginByGuid(string guid)
public static MiraPluginInfo GetPluginByGuid(string pluginId)
{
return Instance._registeredPlugins.Values.First(plugin => plugin.PluginId == guid);
return Instance._registeredPlugins.Values.First(plugin => plugin.PluginId == pluginId);
}
private static void RegisterAllOptions(Assembly assembly, MiraPluginInfo pluginInfo)
private static bool RegisterOptions(Type type, MiraPluginInfo pluginInfo)
{
var filteredTypes = assembly.GetTypes().Where(type => type.IsAssignableTo(typeof(AbstractOptionGroup)));
foreach (var type in filteredTypes)
try
{
if (!type.IsAssignableTo(typeof(AbstractOptionGroup)))
{
return false;
}
if (!ModdedOptionsManager.RegisterGroup(type, pluginInfo))
{
continue;
return false;
}
foreach (var property in type.GetProperties())
@@ -92,53 +126,55 @@ public sealed class MiraPluginManager
ModdedOptionsManager.RegisterAttributeOption(type, attribute, property, pluginInfo);
}
}
pluginInfo.OptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority));
return true;
}
catch (Exception e)
{
Logger<MiraApiPlugin>.Error($"Failed to register options for {type.Name}: {e}");
}
return false;
}
private static void RegisterRoleAttribute(Assembly assembly, MiraPluginInfo pluginInfo)
private static bool RegisterRoleAttribute(Type type, MiraPluginInfo pluginInfo, out Type? role)
{
List<Type> roles = [];
foreach (var type in assembly.GetTypes())
role = null;
try
{
var attribute = type.GetCustomAttribute<RegisterCustomRoleAttribute>();
if (attribute == null)
{
continue;
}
if (!(typeof(RoleBehaviour).IsAssignableFrom(type) && typeof(ICustomRole).IsAssignableFrom(type)))
{
Logger<MiraApiPlugin>.Error($"{type.Name} does not inherit from RoleBehaviour or ICustomRole.");
continue;
return false;
}
if (!ModList.GetById(pluginInfo.PluginId).IsRequiredOnAllClients)
{
Logger<MiraApiPlugin>.Error("Custom roles are only supported on all clients.");
return;
return false;
}
roles.Add(type);
role = type;
return true;
}
CustomRoleManager.RegisterRoleTypes(roles, pluginInfo);
catch (Exception e)
{
Logger<MiraApiPlugin>.Error($"Failed to register role for {type.Name}: {e}");
}
return false;
}
private static void RegisterColorClasses(Assembly assembly)
private static void RegisterColorClasses(Type type)
{
foreach (var type in assembly.GetTypes())
try
{
if (type.GetCustomAttribute<RegisterCustomColorsAttribute>() == null)
{
continue;
return;
}
if (!type.IsStatic())
{
Logger<MiraApiPlugin>.Error($"Color class {type.Name} must be static.");
continue;
return;
}
foreach (var property in type.GetProperties())
@@ -157,29 +193,36 @@ public sealed class MiraPluginManager
PaletteManager.CustomColors.Add(color);
}
}
}
private static void RegisterModifierAttribute(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
catch (Exception e)
{
var attribute = type.GetCustomAttribute<RegisterModifierAttribute>();
if (attribute != null)
{
ModifierManager.RegisterModifier(type);
}
Logger<MiraApiPlugin>.Error($"Failed to register color class {type.Name}: {e}");
}
}
private static void RegisterButtonAttribute(Assembly assembly, MiraPluginInfo pluginInfo)
private static bool RegisterModifier(Type type)
{
foreach (var type in assembly.GetTypes())
try
{
var attribute = type.GetCustomAttribute<RegisterButtonAttribute>();
if (attribute != null)
{
CustomButtonManager.RegisterButton(type, pluginInfo);
}
return ModifierManager.RegisterModifier(type);
}
catch (Exception e)
{
Logger<MiraApiPlugin>.Error($"Failed to register modifier {type.Name}: {e}");
return false;
}
}
private static bool RegisterButtonAttribute(Type type, MiraPluginInfo pluginInfo)
{
try
{
return CustomButtonManager.RegisterButton(type, pluginInfo);
}
catch (Exception e)
{
Logger<MiraApiPlugin>.Error($"Failed to register button {type.Name}: {e}");
}
return false;
}
}
+10
View File
@@ -128,6 +128,16 @@ public interface ICustomRole
Logger<MiraApiPlugin>.Error($"Error getting count configuration for role: {RoleName}");
}
/// <summary>
/// Whether the local player can see this role.
/// </summary>
/// <param name="player">The player with the role.</param>
/// <returns>Whether they can see the role (name color) or not.</returns>
public virtual bool CanLocalPlayerSeeRole(PlayerControl player)
{
return (PlayerControl.LocalPlayer.Data.Role.IsImpostor && player.Data.Role.IsImpostor) || PlayerControl.LocalPlayer.Data.IsDead;
}
/// <summary>
/// This method runs on the HudManager.Update method ONLY when the LOCAL player has this role.
/// </summary>
@@ -1,6 +0,0 @@
using System;
namespace MiraAPI.Roles;
[AttributeUsage(AttributeTargets.Class)]
public class RegisterCustomRoleAttribute : Attribute;
-28
View File
@@ -8,7 +8,6 @@ using Reactor.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace MiraAPI.Utilities;
@@ -18,22 +17,6 @@ namespace MiraAPI.Utilities;
/// </summary>
public static class Extensions
{
public static IEnumerable<Type> GetTypesSafe(this Assembly assembly)
{
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
return ex.Types.Where(t => t != null);
}
catch
{
return Enumerable.Empty<Type>();
}
}
internal static NetData GetNetData(this ICustomRole role)
{
var count = role.GetCount();
@@ -345,17 +328,6 @@ public static class Extensions
.GetNearestDeadBodies(playerControl.GetTruePosition(), radius, Helpers.CreateFilter(Constants.NotShipMask))
.Find(component => component && !component.Reported);
}
/// <summary>
/// Get all living players.
/// </summary>
/// <returns>A list of alive players.</returns>
public static List<PlayerControl> GetAlivePlayers()
{
return PlayerControl.AllPlayerControls.ToArray().Where(x => !x.Data.IsDead).ToList();
}
/// <summary>
/// Finds the nearest object of a specified type to a player. It will only work if the object has a collider.
/// </summary>
+20 -11
View File
@@ -1,5 +1,5 @@
using System.Collections;
using MiraAPI.Roles;
using MiraAPI.Roles;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -15,6 +15,15 @@ namespace MiraAPI.Utilities;
/// </summary>
public static class Helpers
{
/// <summary>
/// Get all living players.
/// </summary>
/// <returns>A list of alive players.</returns>
public static List<PlayerControl> GetAlivePlayers()
{
return PlayerControl.AllPlayerControls.ToArray().Where(x => !x.Data.IsDead).ToList();
}
/// <summary>
/// Returns an empty coroutine.
/// </summary>
@@ -99,14 +108,14 @@ public static class Helpers
}
return (from player in newList
let vector = player.GetTruePosition() - source
let magnitude = vector.magnitude
where !PhysicsHelpers.AnyNonTriggersBetween(
source,
vector.normalized,
magnitude,
Constants.ShipAndObjectsMask)
select player).ToList();
let vector = player.GetTruePosition() - source
let magnitude = vector.magnitude
where !PhysicsHelpers.AnyNonTriggersBetween(
source,
vector.normalized,
magnitude,
Constants.ShipAndObjectsMask)
select player).ToList();
}
/// <summary>
@@ -168,7 +177,7 @@ public static class Helpers
select playerControl);
outputList.Sort(
delegate(PlayerControl a, PlayerControl b)
delegate (PlayerControl a, PlayerControl b)
{
var magnitude2 = (a.GetTruePosition() - source).magnitude;
var magnitude3 = (b.GetTruePosition() - source).magnitude;