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);
+ }
+}