diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index 78a5cd0..9f6aa93 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -35,6 +35,11 @@ public abstract class BaseModifier /// public virtual bool ShowInFreeplay => false; + /// + /// Gets a value indicating whether the modifier is unique. If true, the player can only have one instance of this modifier. + /// + public virtual bool Unique => true; + /// /// Gets the HUD information for this modifier. Defaults to the modifier name. Does nothing if is true. /// diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index 7db81f5..2b00652 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -122,6 +122,38 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) } } + /// + /// Gets a collection of modifiers by their type, or null if the player doesn't have one. + /// + /// The Type of the Modifier. + /// The Modifier if it is found, null otherwise. + public IEnumerable GetModifiersByType() where T : BaseModifier + { + return ActiveModifiers.OfType(); + } + + /// + /// Gets a modifier by its type, or null if the player doesn't have it. + /// + /// The Type of the Modifier. + /// The Modifier if it is found, null otherwise. + public T? GetModifier() where T : BaseModifier + { + return GetModifiersByType().FirstOrDefault(); + } + + /// + /// Tries to get a modifier by its type. + /// + /// The modifier or null. + /// The Type of the Modifier. + /// True if the modifier was found, false otherwise. + public bool TryGetModifier(out T? modifier) where T : BaseModifier + { + modifier = GetModifier(); + return modifier != null; + } + /// /// Removes a modifier from the player. /// @@ -228,7 +260,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) return null; } - if (Modifiers.Find(x => x.ModifierId == modifierId) != null) + if (Modifiers.Find(x => x.ModifierId == modifierId && x.Unique) != null) { Logger.Error($"Player already has modifier with id {modifierId}!"); return null; @@ -260,19 +292,23 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// Checks if a player has an active or queued modifier by its ID. /// /// The Modifier ID. + /// The predicate to check the modifier. /// True if the Modifier is present, false otherwise. - public bool HasModifier(uint id) + public bool HasModifier(uint id, Func? predicate=null) { - return ActiveModifiers.Exists(x => x.ModifierId == id) || _toAdd.Exists(x => x.ModifierId == id); + return ActiveModifiers.Exists(x => x.ModifierId == id && (predicate == null || predicate(x))) || + _toAdd.Exists(x => x.ModifierId == id && (predicate == null || predicate(x))); } /// /// Checks if a player has an active or queued modifier by its type. /// + /// The predicate to check the modifier. /// The Type of the Modifier. /// True if the Modifier is present, false otherwise. - public bool HasModifier() + public bool HasModifier(Func? predicate=null) where T : BaseModifier { - return ActiveModifiers.Exists(x => x is T) || _toAdd.Exists(x => x is T); + return ModifierManager.GetModifierId(typeof(T)) is { } id && + HasModifier(id, (Func?)predicate); } } diff --git a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs index e36ac61..d1c6cb9 100644 --- a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs +++ b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs @@ -338,12 +338,12 @@ internal static class TaskAdderPatches { if (PlayerControl.LocalPlayer.HasModifier(modifier.ModifierId)) { - PlayerControl.LocalPlayer.GetModifierComponent()!.RemoveModifier(modifier.ModifierId); + PlayerControl.LocalPlayer.RemoveModifier(modifier.ModifierId); taskAddButton.Overlay.enabled = false; } else { - PlayerControl.LocalPlayer.GetModifierComponent()!.AddModifier(ModifierManager.GetModifierType(modifier.ModifierId)!); + PlayerControl.LocalPlayer.AddModifier(ModifierManager.GetModifierType(modifier.ModifierId)!); taskAddButton.Overlay.enabled = true; } })); diff --git a/MiraAPI/Patches/PlayerControlPatches.cs b/MiraAPI/Patches/PlayerControlPatches.cs index c7918fe..fbabe1a 100644 --- a/MiraAPI/Patches/PlayerControlPatches.cs +++ b/MiraAPI/Patches/PlayerControlPatches.cs @@ -126,6 +126,6 @@ public static class PlayerControlPatches [HarmonyPatch(nameof(PlayerControl.OnDestroy))] public static void PlayerControlOnDestroyPrefix(PlayerControl __instance) { - Utilities.Extensions.ModifierComponents.Remove(__instance); + Utilities.ModifierExtensions.ModifierComponents.Remove(__instance); } } diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 89fc17b..6912ac8 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -41,7 +41,7 @@ public static class Extensions } /// - /// Enables Among Us style masking on a TMP text object. + /// Enables stencil masking on a TMP text object. /// /// The TMP text. public static void EnableStencilMasking(this TMP_Text text) @@ -141,30 +141,6 @@ public static class Extensions opt => opt.OptionBehaviour && opt.OptionBehaviour == optionBehaviour); } - internal static Dictionary ModifierComponents { get; } = []; - - /// - /// Gets the ModifierComponent for a player. - /// - /// The PlayerControl object. - /// A ModifierComponent if there is one, null otherwise. - public static ModifierComponent? GetModifierComponent(this PlayerControl player) - { - if (ModifierComponents.TryGetValue(player, out var component)) - { - return component; - } - - component = player.GetComponent(); - if (component == null) - { - return null; - } - - ModifierComponents[player] = component; - return component; - } - /// /// Randomizes a list. /// @@ -185,105 +161,6 @@ public static class Extensions return randomizedList; } - /// - /// Gets a modifier by its type, or null if the player doesn't have it. - /// - /// The PlayerControl object. - /// The Type of the Modifier. - /// The Modifier if it is found, null otherwise. - public static T? GetModifier(this PlayerControl? player) where T : BaseModifier - { - return player?.GetModifierComponent()?.ActiveModifiers.Find(x => x is T) as T; - } - - /// - /// Checks if a player has a modifier. - /// - /// The PlayerControl object. - /// The Type of the Modifier. - /// True if the Modifier is present, false otherwise. - public static bool HasModifier(this PlayerControl? player) where T : BaseModifier - { - return player?.GetModifierComponent() != null && - player.GetModifierComponent()!.HasModifier(); - } - - /// - /// Checks if a player has a modifier by its ID. - /// - /// The PlayerControl object. - /// The Modifier ID. - /// True if the Modifier is present, false otherwise. - public static bool HasModifier(this PlayerControl? player, uint id) - { - return player?.GetModifierComponent() != null && - player.GetModifierComponent()!.HasModifier(id); - } - - /// - /// Remote Procedure Call to remove a modifier from a player. - /// - /// The player to remove the modifier from. - /// The ID of the modifier. - [MethodRpc((uint)MiraRpc.RemoveModifier)] - public static void RpcRemoveModifier(this PlayerControl target, uint modifierId) - { - target.GetModifierComponent()?.RemoveModifier(modifierId); - } - - /// - /// Remote Procedure Call to remove a modifier from a player. - /// - /// The player to remove the modifier from. - /// The Type of the Modifier. - public static void RpcRemoveModifier(this PlayerControl player) where T : BaseModifier - { - var id = ModifierManager.GetModifierId(typeof(T)); - - if (id == null) - { - Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); - return; - } - - player.RpcRemoveModifier(id.Value); - } - - /// - /// Remote Procedure Call to add a modifier to a player. - /// - /// The player to add the modifier to. - /// The modifier ID. - [MethodRpc((uint)MiraRpc.AddModifier)] - public static void RpcAddModifier(this PlayerControl target, uint modifierId) - { - var type = ModifierManager.GetModifierType(modifierId); - if (type == null) - { - Logger.Error($"Cannot add modifier with id {modifierId} because it is not registered."); - return; - } - - target.GetModifierComponent()?.AddModifier(type); - } - - /// - /// Remote Procedure Call to add a modifier to a player. - /// - /// The player to add the modifier to. - /// The modifier Type. - public static void RpcAddModifier(this PlayerControl player) where T : BaseModifier - { - var id = ModifierManager.GetModifierId(typeof(T)); - if (id == null) - { - Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); - return; - } - - player.RpcAddModifier(id.Value); - } - /// /// Darkens a color by a specified amount. /// @@ -339,6 +216,7 @@ public static class Extensions .GetNearestDeadBodies(playerControl.GetTruePosition(), radius, Helpers.CreateFilter(Constants.NotShipMask)) .Find(component => component && !component.Reported); } + /// /// Finds the nearest object of a specified type to a player. It will only work if the object has a collider. /// @@ -387,10 +265,6 @@ public static class Extensions return predicate != null ? filteredPlayers.Find(predicate) : filteredPlayers.FirstOrDefault(); } - private static readonly int _outline = Shader.PropertyToID("_Outline"); - private static readonly int _outlineColor = Shader.PropertyToID("_OutlineColor"); - private static readonly int _addColor = Shader.PropertyToID("_AddColor"); - /// /// Fixed version of Reactor's SetOutline. /// @@ -398,8 +272,8 @@ public static class Extensions /// The outline color. public static void UpdateOutline(this Renderer renderer, Color? color) { - renderer.material.SetFloat(_outline, color.HasValue ? 1 : 0); - renderer.material.SetColor(_outlineColor, color.HasValue ? color.Value : Color.clear); - renderer.material.SetColor(_addColor, color.HasValue ? color.Value : Color.clear); + renderer.material.SetFloat(ShaderID.Outline, color.HasValue ? 1 : 0); + renderer.material.SetColor(ShaderID.OutlineColor, color ?? Color.clear); + renderer.material.SetColor(ShaderID.AddColor, color ?? Color.clear); } } diff --git a/MiraAPI/Utilities/ModifierExtensions.cs b/MiraAPI/Utilities/ModifierExtensions.cs new file mode 100644 index 0000000..dadb62c --- /dev/null +++ b/MiraAPI/Utilities/ModifierExtensions.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using MiraAPI.Modifiers; +using MiraAPI.Networking; +using Reactor.Networking.Attributes; +using Reactor.Utilities; + +namespace MiraAPI.Utilities; + +/// +/// Extensions for modifiers. +/// +public static class ModifierExtensions +{ + /// + /// Gets the dictionary to cache ModifierComponents. + /// + public static Dictionary ModifierComponents { get; } = []; + + /// + /// Remote Procedure Call to add a modifier to a player. + /// + /// The player to add the modifier to. + /// The modifier ID. + [MethodRpc((uint)MiraRpc.AddModifier)] + public static void RpcAddModifier(this PlayerControl target, uint modifierId) + { + var type = ModifierManager.GetModifierType(modifierId); + if (type == null) + { + Logger.Error($"Cannot add modifier with id {modifierId} because it is not registered."); + return; + } + + target.AddModifier(type); + } + + /// + /// Remote Procedure Call to add a modifier to a player. + /// + /// The player to add the modifier to. + /// The modifier Type. + public static void RpcAddModifier(this PlayerControl player) where T : BaseModifier + { + var id = ModifierManager.GetModifierId(typeof(T)); + if (id == null) + { + Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); + return; + } + + player.RpcAddModifier(id.Value); + } + + /// + /// Remote Procedure Call to remove a modifier from a player. + /// + /// The player to remove the modifier from. + /// The ID of the modifier. + [MethodRpc((uint)MiraRpc.RemoveModifier)] + public static void RpcRemoveModifier(this PlayerControl target, uint modifierId) + { + target.RemoveModifier(modifierId); + } + + /// + /// Remote Procedure Call to remove a modifier from a player. + /// + /// The player to remove the modifier from. + /// The Type of the Modifier. + public static void RpcRemoveModifier(this PlayerControl player) where T : BaseModifier + { + var id = ModifierManager.GetModifierId(typeof(T)); + if (id == null) + { + Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); + return; + } + + player.RpcRemoveModifier(id.Value); + } + + /// + /// Gets the ModifierComponent for a player. + /// + /// The PlayerControl object. + /// A ModifierComponent if there is one, null otherwise. + public static ModifierComponent? GetModifierComponent(this PlayerControl player) + { + if (ModifierComponents.TryGetValue(player, out var component)) + { + return component; + } + + component = player.GetComponent(); + if (!component) + { + return null; + } + + ModifierComponents[player] = component; + return component; + } + + /// + /// Gets the ModifierComponent for a player. + /// + /// The PlayerControl object. + /// The ModifierComponent if it is found. + /// A ModifierComponent if there is one, null otherwise. + public static bool TryGetModifierComponent(this PlayerControl player, out ModifierComponent? component) + { + if (ModifierComponents.TryGetValue(player, out component)) + { + return component; + } + + component = player.GetComponent(); + return component; + } + + /// + /// Gets a collection of modifiers by their type, or null if the player doesn't have one. + /// + /// The PlayerControl object. + /// The Type of the Modifier. + /// The Modifier if it is found, null otherwise. + public static IEnumerable GetModifiersByType(this PlayerControl player) where T : BaseModifier + { + return player.GetModifierComponent()!.GetModifiersByType(); + } + + /// + /// Gets a modifier by its type, or null if the player doesn't have it. + /// + /// The PlayerControl object. + /// The Type of the Modifier. + /// The Modifier if it is found, null otherwise. + public static T? GetModifier(this PlayerControl player) where T : BaseModifier + { + return player.GetModifierComponent()!.GetModifier(); + } + + /// + /// Tries to get a modifier by its type. + /// + /// The PlayerControl object. + /// The modifier or null. + /// The Type of the Modifier. + /// True if the modifier was found, false otherwise. + public static bool TryGetModifier(this PlayerControl player, out T? modifier) where T : BaseModifier + { + return player.GetModifierComponent()!.TryGetModifier(out modifier); + } + + /// + /// Removes a modifier from the player. + /// + /// The PlayerControl object. + /// The modifier type. + public static void RemoveModifier(this PlayerControl player, Type type) + { + player.GetModifierComponent()!.RemoveModifier(type); + } + + /// + /// Removes a modifier from the player. + /// + /// The PlayerControl object. + /// The modifier type. + public static void RemoveModifier(this PlayerControl player) where T : BaseModifier + { + player.GetModifierComponent()!.RemoveModifier(); + } + + /// + /// Removes a modifier from the player. + /// + /// The PlayerControl object. + /// The modifier ID. + public static void RemoveModifier(this PlayerControl player, uint modifierId) + { + player.GetModifierComponent()!.RemoveModifier(modifierId); + } + + /// + /// Removes a modifier from the player. + /// + /// The PlayerControl object. + /// The modifier object. + public static void RemoveModifier(this PlayerControl player, BaseModifier modifier) + { + player.GetModifierComponent()!.RemoveModifier(modifier); + } + + /// + /// Adds a modifier to the player. + /// + /// The PlayerControl object. + /// The modifier to add. + /// The modifier that was added. + public static BaseModifier? AddModifier(this PlayerControl player, BaseModifier modifier) + { + return player.GetModifierComponent()!.AddModifier(modifier); + } + + /// + /// Adds a modifier to the player. + /// + /// The PlayerControl object. + /// The modifier type. + /// The modifier that was added. + public static BaseModifier? AddModifier(this PlayerControl player, Type type) + { + return player.GetModifierComponent()!.AddModifier(type); + } + + /// + /// Adds a modifier to the player. + /// + /// The PlayerControl object. + /// The Type of the modifier. + /// The new modifier. + public static T? AddModifier(this PlayerControl player) where T : BaseModifier + { + return player.GetModifierComponent()!.AddModifier(); + } + + /// + /// Checks if a player has an active or queued modifier by its ID. + /// + /// The PlayerControl object. + /// The Modifier ID. + /// The predicate to check the modifier. + /// True if the Modifier is present, false otherwise. + public static bool HasModifier(this PlayerControl player, uint id, Func? predicate=null) + { + return player.GetModifierComponent()!.HasModifier(id, predicate); + } + + /// + /// Checks if a player has an active or queued modifier by its type. + /// + /// The PlayerControl object. + /// The predicate to check the modifier. + /// The Type of the Modifier. + /// True if the Modifier is present, false otherwise. + public static bool HasModifier(this PlayerControl player, Func? predicate=null) where T : BaseModifier + { + return player.GetModifierComponent()!.HasModifier(predicate); + } +}