From a077cabea1cb22c509b7645b7c572698f482a625 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Tue, 20 Aug 2024 21:42:35 -0400 Subject: [PATCH 01/11] Modifiers base --- MiraAPI.Example/TorchModifier.cs | 26 +++++++ MiraAPI/MiraApiPlugin.cs | 4 +- MiraAPI/Modifiers/BaseModifier.cs | 10 +++ MiraAPI/Modifiers/ModifierManager.cs | 74 +++++++++++++++++++ .../Modifiers/RegisterModifierAttribute.cs | 4 + MiraAPI/Modifiers/Types/GameModifier.cs | 6 ++ MiraAPI/Modifiers/Types/TimedModifier.cs | 7 ++ MiraAPI/Networking/MiraRpc.cs | 4 +- MiraAPI/Patches/PlayerControlPatches.cs | 16 +++- MiraAPI/PluginLoading/MiraPluginManager.cs | 29 ++++++-- MiraAPI/Utilities/Extensions.cs | 15 ++++ 11 files changed, 181 insertions(+), 14 deletions(-) create mode 100644 MiraAPI.Example/TorchModifier.cs create mode 100644 MiraAPI/Modifiers/BaseModifier.cs create mode 100644 MiraAPI/Modifiers/ModifierManager.cs create mode 100644 MiraAPI/Modifiers/RegisterModifierAttribute.cs create mode 100644 MiraAPI/Modifiers/Types/GameModifier.cs create mode 100644 MiraAPI/Modifiers/Types/TimedModifier.cs diff --git a/MiraAPI.Example/TorchModifier.cs b/MiraAPI.Example/TorchModifier.cs new file mode 100644 index 0000000..6da062f --- /dev/null +++ b/MiraAPI.Example/TorchModifier.cs @@ -0,0 +1,26 @@ +using MiraAPI.GameOptions.Attributes; +using MiraAPI.Modifiers.Types; + +namespace MiraAPI.Example +{ + [RegisterModifier] + public class TorchModifier : GameModifier + { + public override string ModifierName => "Torch"; + + [ModdedNumberOption("Torch Chance", 0, 100, 10)] + public float chance { get; } = 50; + [ModdedNumberOption("Torch Amount", 0, 10, 1)] + public float amount { get; } = 3; + + public override int GetAmountPerGame() + { + return (int)amount; + } + + public override int GetAssignmentChance() + { + return (int)chance; + } + } +} diff --git a/MiraAPI/MiraApiPlugin.cs b/MiraAPI/MiraApiPlugin.cs index 9faf789..9c5b685 100644 --- a/MiraAPI/MiraApiPlugin.cs +++ b/MiraAPI/MiraApiPlugin.cs @@ -3,11 +3,11 @@ using BepInEx.Unity.IL2CPP; using HarmonyLib; using MiraAPI.PluginLoading; using Reactor; +using Reactor.Networking; +using Reactor.Networking.Attributes; using Reactor.Patches; using System; using System.Text; -using Reactor.Networking; -using Reactor.Networking.Attributes; using UnityEngine; namespace MiraAPI; diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs new file mode 100644 index 0000000..284f989 --- /dev/null +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -0,0 +1,10 @@ +namespace MiraAPI.Modifiers; +public abstract class BaseModifier +{ + public abstract string ModifierName { get; } + public uint ModifierId { get; set; } + public virtual void OnActivate() { } + public virtual void OnDeactivate() { } + public virtual void Update() { } + public virtual void OnDeath() { } +} \ No newline at end of file diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs new file mode 100644 index 0000000..fc07183 --- /dev/null +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -0,0 +1,74 @@ +using MiraAPI.Networking; +using MiraAPI.Utilities; +using Reactor.Networking.Attributes; +using Reactor.Utilities; +using Reactor.Utilities.Attributes; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace MiraAPI.Modifiers; + +[RegisterInIl2Cpp] +public class ModifierManager(IntPtr ptr) : MonoBehaviour(ptr) +{ + public List activeModifiersIds = new(); + public static List registeredModifiers = new(); + + public static void RegisterModifier(Type modifierType) + { + if (!typeof(BaseModifier).IsAssignableFrom(modifierType)) + { + return; + } + + BaseModifier newMod = (BaseModifier)Activator.CreateInstance(modifierType); + newMod.ModifierId = (uint)registeredModifiers.Count; + registeredModifiers.Add(newMod); + } + + + public bool HasModifier(PlayerControl target) where T : BaseModifier + { + uint id = registeredModifiers.Find(mod => mod.GetType() == typeof(T)).ModifierId; + return target.GetModifierManager().activeModifiersIds.Contains(id); + } + + public void AddModifier(PlayerControl target) where T : BaseModifier + { + uint id = registeredModifiers.Find(mod => mod.GetType() == typeof(T)).ModifierId; + RpcAddModifier(target, id); + } + + public void RemoveModifier(PlayerControl target) where T : BaseModifier + { + uint id = registeredModifiers.Find(mod => mod.GetType() == typeof(T)).ModifierId; + if (!activeModifiersIds.Contains(id)) + { + Logger.Error($"Cannot remove modifier {typeof(T).Name} because it is not active."); + return; + } + + RpcRemoveModifier(target, id); + } + + [MethodRpc((uint)MiraRpc.AddModifier)] + private static void RpcRemoveModifier(PlayerControl target, uint modifierId) + { + BaseModifier modifier = registeredModifiers.Find(mod => mod.ModifierId == modifierId); + if (modifier is null) return; + + modifier.OnDeactivate(); + target.GetModifierManager().activeModifiersIds.Remove(modifierId); + } + + [MethodRpc((uint)MiraRpc.AddModifier)] + private static void RpcAddModifier(PlayerControl target, uint modifierId) + { + BaseModifier modifier = registeredModifiers.Find(mod => mod.ModifierId == modifierId); + if (modifier is null) return; + + target.GetModifierManager().activeModifiersIds.Add(modifierId); + modifier.OnActivate(); + } +} \ No newline at end of file diff --git a/MiraAPI/Modifiers/RegisterModifierAttribute.cs b/MiraAPI/Modifiers/RegisterModifierAttribute.cs new file mode 100644 index 0000000..3b25497 --- /dev/null +++ b/MiraAPI/Modifiers/RegisterModifierAttribute.cs @@ -0,0 +1,4 @@ +using System; + +[AttributeUsage(AttributeTargets.Class)] +public class RegisterModifierAttribute : Attribute; \ No newline at end of file diff --git a/MiraAPI/Modifiers/Types/GameModifier.cs b/MiraAPI/Modifiers/Types/GameModifier.cs new file mode 100644 index 0000000..d1339ea --- /dev/null +++ b/MiraAPI/Modifiers/Types/GameModifier.cs @@ -0,0 +1,6 @@ +namespace MiraAPI.Modifiers.Types; +public abstract class GameModifier : BaseModifier +{ + public abstract int GetAssignmentChance(); + public abstract int GetAmountPerGame(); +} \ No newline at end of file diff --git a/MiraAPI/Modifiers/Types/TimedModifier.cs b/MiraAPI/Modifiers/Types/TimedModifier.cs new file mode 100644 index 0000000..2ed10a2 --- /dev/null +++ b/MiraAPI/Modifiers/Types/TimedModifier.cs @@ -0,0 +1,7 @@ +namespace MiraAPI.Modifiers.Types; +public abstract class TimedModifier : BaseModifier +{ + public abstract float Duration { get; } + public abstract bool StartOnAssignment { get; } + public abstract bool RemoveOnCompletion { get; } +} \ No newline at end of file diff --git a/MiraAPI/Networking/MiraRpc.cs b/MiraAPI/Networking/MiraRpc.cs index 4b73cf8..268f680 100644 --- a/MiraAPI/Networking/MiraRpc.cs +++ b/MiraAPI/Networking/MiraRpc.cs @@ -3,5 +3,7 @@ public enum MiraRpc : uint { SyncGameOptions, - SyncRoleOptions + SyncRoleOptions, + AddModifier, + RemoveModifier } \ No newline at end of file diff --git a/MiraAPI/Patches/PlayerControlPatches.cs b/MiraAPI/Patches/PlayerControlPatches.cs index 68bd25e..99b6efb 100644 --- a/MiraAPI/Patches/PlayerControlPatches.cs +++ b/MiraAPI/Patches/PlayerControlPatches.cs @@ -1,27 +1,35 @@ using HarmonyLib; using MiraAPI.Hud; +using MiraAPI.Modifiers; namespace MiraAPI.Patches; [HarmonyPatch(typeof(PlayerControl))] public static class PlayerControlPatches { - + + [HarmonyPostfix] + [HarmonyPatch(nameof(PlayerControl.Start))] + public static void PlayerControlStartPostfix(PlayerControl __instance) + { + __instance.gameObject.AddComponent(); + } + [HarmonyPostfix] [HarmonyPatch(nameof(PlayerControl.FixedUpdate))] public static void PlayerControlFixedUpdatePostfix(PlayerControl __instance) { if (!__instance.AmOwner) return; - + foreach (var button in CustomButtonManager.CustomButtons) { if (!button.Enabled(__instance.Data.Role)) { continue; } - + button.FixedUpdateHandler(__instance); } } - + } \ No newline at end of file diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index a96fbf8..66a656e 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -3,25 +3,27 @@ using BepInEx.Unity.IL2CPP; using Il2CppInterop.Runtime.Injection; using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; +using MiraAPI.Hud; +using MiraAPI.Modifiers; using MiraAPI.Roles; using Reactor.Utilities; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using MiraAPI.Hud; namespace MiraAPI.PluginLoading; public class MiraPluginManager { public readonly Dictionary RegisteredPlugins = []; - + private static MiraPluginManager _instance; - - public static MiraPluginManager Instance { + + public static MiraPluginManager Instance + { get => _instance ??= new MiraPluginManager(); - private set => _instance = value; + private set => _instance = value; } public void Initialize() @@ -34,6 +36,7 @@ public class MiraPluginManager var id = MetadataHelper.GetMetadata(plugin.GetType()).GUID; var info = new MiraPluginInfo(id, plugin as IMiraPlugin, IL2CPPChainloader.Instance.Plugins[id]); + RegisterModifierAttribute(assembly); RegisterOptionsGroups(assembly, info); RegisterOptionsAttributes(assembly, info); RegisterRoleAttribute(assembly, info); @@ -115,12 +118,24 @@ public class MiraPluginManager { Logger.Error($"{k}: {v}"); } - + Logger.Error(e); } } } - + + private static void RegisterModifierAttribute(Assembly assembly) + { + foreach (var type in assembly.GetTypes()) + { + var attribute = type.GetCustomAttribute(); + if (attribute != null) + { + ModifierManager.RegisterModifier(type); + } + } + } + private static void RegisterButtonAttribute(Assembly assembly) { foreach (var type in assembly.GetTypes()) diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index d810b8f..2cc615c 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -1,4 +1,5 @@ using MiraAPI.GameOptions; +using MiraAPI.Modifiers; using MiraAPI.Roles; using Reactor.Utilities.Extensions; using System.Linq; @@ -13,6 +14,20 @@ public static class Extensions return ModdedOptionsManager.Options.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour.Equals(optionBehaviour)); } + public static ModifierManager GetModifierManager(this PlayerControl player) + { + return player.gameObject.GetComponent(); + } + + public static void AddModifier(this PlayerControl player) where T : BaseModifier + { + player.GetModifierManager().AddModifier(); + } + + public static void RemoveModifier(this PlayerControl player) where T : BaseModifier + { + } + public static Color DarkenColor(this Color color) { return new Color(color.r - 0.3f, color.g - 0.3f, color.b - 0.3f); From 5c244880491741ec5b6376966dcba1ed52cf4468 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Fri, 23 Aug 2024 15:18:21 -0400 Subject: [PATCH 02/11] Idk --- MiraAPI.Example/ModifierExample.cs | 15 ++++++++---- MiraAPI/Modifiers/BaseModifier.cs | 3 ++- MiraAPI/Modifiers/ModifierManager.cs | 29 +++++++++++++++++++----- MiraAPI/Modifiers/Types/TimedModifier.cs | 21 ++++++++++++++--- MiraAPI/Utilities/Extensions.cs | 3 ++- 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/MiraAPI.Example/ModifierExample.cs b/MiraAPI.Example/ModifierExample.cs index 03df100..f30f079 100644 --- a/MiraAPI.Example/ModifierExample.cs +++ b/MiraAPI.Example/ModifierExample.cs @@ -1,8 +1,15 @@ -namespace MiraAPI.Example; +using MiraAPI.Modifiers.Types; -public class ModifierExample +namespace MiraAPI.Example; + +[RegisterModifier] +public class ModifierExample : TimedModifier { - //name, role specific, etc + public override string ModifierName => "Timed modifier example"; + public override float Duration => 10; - // mono behaviour stuff here (ModdedPlayerModifier inherits monobehaviour) + public override void OnTimerComplete() + { + + } } \ No newline at end of file diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index 284f989..e0855e6 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -2,7 +2,8 @@ public abstract class BaseModifier { public abstract string ModifierName { get; } - public uint ModifierId { get; set; } + public uint ModifierId { get; internal set; } + public PlayerControl Player { get; set; } public virtual void OnActivate() { } public virtual void OnDeactivate() { } public virtual void Update() { } diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs index fc07183..454a5bf 100644 --- a/MiraAPI/Modifiers/ModifierManager.cs +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -1,9 +1,11 @@ -using MiraAPI.Networking; +using MiraAPI.Modifiers.Types; +using MiraAPI.Networking; using MiraAPI.Utilities; using Reactor.Networking.Attributes; using Reactor.Utilities; using Reactor.Utilities.Attributes; using System; +using System.Collections; using System.Collections.Generic; using UnityEngine; @@ -12,7 +14,7 @@ namespace MiraAPI.Modifiers; [RegisterInIl2Cpp] public class ModifierManager(IntPtr ptr) : MonoBehaviour(ptr) { - public List activeModifiersIds = new(); + public Dictionary activeModifiers = new(); public static List registeredModifiers = new(); public static void RegisterModifier(Type modifierType) @@ -27,11 +29,25 @@ public class ModifierManager(IntPtr ptr) : MonoBehaviour(ptr) registeredModifiers.Add(newMod); } + public void Update() + { + foreach (var modifier in activeModifiers.Values) + { + modifier.Update(); + } + } + + public IEnumerator ModifierTimer(TimedModifier modifier) + { + yield return new WaitForSeconds(modifier.Duration); + modifier.timerOngoing = false; + modifier.OnTimerComplete(); + } public bool HasModifier(PlayerControl target) where T : BaseModifier { uint id = registeredModifiers.Find(mod => mod.GetType() == typeof(T)).ModifierId; - return target.GetModifierManager().activeModifiersIds.Contains(id); + return target.GetModifierManager().activeModifiers.ContainsKey(id); } public void AddModifier(PlayerControl target) where T : BaseModifier @@ -43,7 +59,7 @@ public class ModifierManager(IntPtr ptr) : MonoBehaviour(ptr) public void RemoveModifier(PlayerControl target) where T : BaseModifier { uint id = registeredModifiers.Find(mod => mod.GetType() == typeof(T)).ModifierId; - if (!activeModifiersIds.Contains(id)) + if (!activeModifiers.ContainsKey(id)) { Logger.Error($"Cannot remove modifier {typeof(T).Name} because it is not active."); return; @@ -59,7 +75,7 @@ public class ModifierManager(IntPtr ptr) : MonoBehaviour(ptr) if (modifier is null) return; modifier.OnDeactivate(); - target.GetModifierManager().activeModifiersIds.Remove(modifierId); + target.GetModifierManager().activeModifiers.Remove(modifierId); } [MethodRpc((uint)MiraRpc.AddModifier)] @@ -68,7 +84,8 @@ public class ModifierManager(IntPtr ptr) : MonoBehaviour(ptr) BaseModifier modifier = registeredModifiers.Find(mod => mod.ModifierId == modifierId); if (modifier is null) return; - target.GetModifierManager().activeModifiersIds.Add(modifierId); + target.GetModifierManager().activeModifiers.Add(modifierId, modifier); + modifier.Player = target; modifier.OnActivate(); } } \ No newline at end of file diff --git a/MiraAPI/Modifiers/Types/TimedModifier.cs b/MiraAPI/Modifiers/Types/TimedModifier.cs index 2ed10a2..7408ff2 100644 --- a/MiraAPI/Modifiers/Types/TimedModifier.cs +++ b/MiraAPI/Modifiers/Types/TimedModifier.cs @@ -1,7 +1,22 @@ -namespace MiraAPI.Modifiers.Types; +using Reactor.Utilities; + +namespace MiraAPI.Modifiers.Types; public abstract class TimedModifier : BaseModifier { public abstract float Duration { get; } - public abstract bool StartOnAssignment { get; } - public abstract bool RemoveOnCompletion { get; } + public abstract void OnTimerComplete(); + + internal bool timerOngoing = false; + + public void StartTimer(ModifierManager manager) + { + if (!timerOngoing) + { + Logger.Error("Can't start a timer that is already ongoing."); + return; + } + + timerOngoing = true; + Coroutines.Start(manager.ModifierTimer(this)); + } } \ No newline at end of file diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 2cc615c..25d6bcd 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -21,11 +21,12 @@ public static class Extensions public static void AddModifier(this PlayerControl player) where T : BaseModifier { - player.GetModifierManager().AddModifier(); + player.GetModifierManager().AddModifier(player); } public static void RemoveModifier(this PlayerControl player) where T : BaseModifier { + player.GetModifierManager().RemoveModifier(player); } public static Color DarkenColor(this Color color) From 81f9fa728b7705c8ff183ecd48b0aad52ba8fd40 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:45:18 -0400 Subject: [PATCH 03/11] rework how modifiers exist --- MiraAPI/GameOptions/ModdedOptionsManager.cs | 2 +- .../GameOptions/OptionTypes/ModdedOption.cs | 1 - MiraAPI/Modifiers/BaseModifier.cs | 4 +- MiraAPI/Modifiers/ModifierComponent.cs | 97 +++++++++++++++++++ MiraAPI/Modifiers/ModifierManager.cs | 76 +++------------ .../Patches/Options/RoleSettingMenuPatches.cs | 2 +- MiraAPI/Patches/PlayerControlPatches.cs | 2 +- MiraAPI/Patches/Roles/NameTagPatch.cs | 2 - MiraAPI/Utilities/Extensions.cs | 6 +- 9 files changed, 121 insertions(+), 71 deletions(-) create mode 100644 MiraAPI/Modifiers/ModifierComponent.cs diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index 0d8e15f..0ca20c3 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -10,7 +10,7 @@ using Reactor.Networking.Rpc; namespace MiraAPI.GameOptions; -public class ModdedOptionsManager +public static class ModdedOptionsManager { public static readonly Dictionary ModdedOptions = new(); public static readonly List Options = []; diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index 386cc7c..712955a 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -4,7 +4,6 @@ using Reactor.Localization.Utilities; using System; using MiraAPI.Networking; using Reactor.Networking.Rpc; -using Reactor.Utilities; using UnityEngine; namespace MiraAPI.GameOptions.OptionTypes; diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index 284f989..2e6812b 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -2,7 +2,9 @@ public abstract class BaseModifier { public abstract string ModifierName { get; } - public uint ModifierId { get; set; } + + public PlayerControl Player { get; internal set; } + public virtual void OnActivate() { } public virtual void OnDeactivate() { } public virtual void Update() { } diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs new file mode 100644 index 0000000..5dc1acc --- /dev/null +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -0,0 +1,97 @@ +using Reactor.Utilities.Attributes; +using System; +using System.Collections.Generic; +using MiraAPI.Networking; +using MiraAPI.Utilities; +using Reactor.Networking.Attributes; +using Reactor.Utilities; +using UnityEngine; + +namespace MiraAPI.Modifiers; + +[RegisterInIl2Cpp] +public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) +{ + public List activeModifiers = []; + + public PlayerControl player; + + public void Start() + { + player = GetComponent(); + } + + public bool HasModifier() where T : BaseModifier + { + return activeModifiers.Exists(x=>x is T); + } + + public void AddModifier() where T : BaseModifier + { + if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id)) + { + Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); + return; + } + + RpcAddModifier(player, id); + } + + public void RemoveModifier() where T : BaseModifier + { + if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id)) + { + Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); + return; + } + + RpcRemoveModifier(player, id); + } + + [MethodRpc((uint)MiraRpc.RemoveModifier)] + private static void RpcRemoveModifier(PlayerControl target, uint modifierId) + { + if (!ModifierManager.IdToTypeModifiers.TryGetValue(modifierId, out var type)) + { + Logger.Error($"Cannot remove modifier with id {modifierId} because it is not registered."); + return; + } + + var modifierComponent = target.GetModifierComponent(); + + var modifier = modifierComponent.activeModifiers.Find(x => x.GetType() == type); + + if (modifier is null) + { + Logger.Error($"Cannot remove modifier {type.Name} because it is not active."); + return; + } + + modifier.OnDeactivate(); + modifierComponent.activeModifiers.Remove(modifier); + } + + [MethodRpc((uint)MiraRpc.AddModifier)] + private static void RpcAddModifier(PlayerControl target, uint modifierId) + { + if (!ModifierManager.IdToTypeModifiers.TryGetValue(modifierId, out var type)) + { + Logger.Error($"Cannot add modifier with id {modifierId} because it is not registered."); + return; + } + + var modifier = (BaseModifier)Activator.CreateInstance(type); + + var modifierComponent = target.GetModifierComponent(); + + if (modifier is null) + { + Logger.Error($"Cannot add modifier {type.Name} because it is null."); + return; + } + + modifierComponent.activeModifiers.Add(modifier); + modifier.Player = modifierComponent.player; + modifier.OnActivate(); + } +} \ No newline at end of file diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs index fc07183..8093e09 100644 --- a/MiraAPI/Modifiers/ModifierManager.cs +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -1,74 +1,28 @@ -using MiraAPI.Networking; -using MiraAPI.Utilities; -using Reactor.Networking.Attributes; -using Reactor.Utilities; -using Reactor.Utilities.Attributes; -using System; +using System; using System.Collections.Generic; -using UnityEngine; namespace MiraAPI.Modifiers; -[RegisterInIl2Cpp] -public class ModifierManager(IntPtr ptr) : MonoBehaviour(ptr) +public static class ModifierManager { - public List activeModifiersIds = new(); - public static List registeredModifiers = new(); - + public static readonly Dictionary IdToTypeModifiers = []; + public static readonly Dictionary TypeToIdModifiers = []; + + private static uint _nextId; + + private static uint GetNextId() + { + return _nextId++; + } + public static void RegisterModifier(Type modifierType) { if (!typeof(BaseModifier).IsAssignableFrom(modifierType)) { return; } - - BaseModifier newMod = (BaseModifier)Activator.CreateInstance(modifierType); - newMod.ModifierId = (uint)registeredModifiers.Count; - registeredModifiers.Add(newMod); - } - - - public bool HasModifier(PlayerControl target) where T : BaseModifier - { - uint id = registeredModifiers.Find(mod => mod.GetType() == typeof(T)).ModifierId; - return target.GetModifierManager().activeModifiersIds.Contains(id); - } - - public void AddModifier(PlayerControl target) where T : BaseModifier - { - uint id = registeredModifiers.Find(mod => mod.GetType() == typeof(T)).ModifierId; - RpcAddModifier(target, id); - } - - public void RemoveModifier(PlayerControl target) where T : BaseModifier - { - uint id = registeredModifiers.Find(mod => mod.GetType() == typeof(T)).ModifierId; - if (!activeModifiersIds.Contains(id)) - { - Logger.Error($"Cannot remove modifier {typeof(T).Name} because it is not active."); - return; - } - - RpcRemoveModifier(target, id); - } - - [MethodRpc((uint)MiraRpc.AddModifier)] - private static void RpcRemoveModifier(PlayerControl target, uint modifierId) - { - BaseModifier modifier = registeredModifiers.Find(mod => mod.ModifierId == modifierId); - if (modifier is null) return; - - modifier.OnDeactivate(); - target.GetModifierManager().activeModifiersIds.Remove(modifierId); - } - - [MethodRpc((uint)MiraRpc.AddModifier)] - private static void RpcAddModifier(PlayerControl target, uint modifierId) - { - BaseModifier modifier = registeredModifiers.Find(mod => mod.ModifierId == modifierId); - if (modifier is null) return; - - target.GetModifierManager().activeModifiersIds.Add(modifierId); - modifier.OnActivate(); + + IdToTypeModifiers.Add(GetNextId(), modifierType); + TypeToIdModifiers.Add(modifierType, _nextId); } } \ No newline at end of file diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 2350801..bcdecbe 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -162,7 +162,7 @@ public static class RoleSettingMenuPatches { ICustomRole customRole = role as ICustomRole; __instance.roleDescriptionText.text = customRole.RoleLongDescription; - __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, Array.Empty()); + __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, []); __instance.roleScreenshot.sprite = Sprite.Create(role.RoleScreenshot.texture, new Rect(0, 0, 370, 230), Vector2.one / 2, 100); __instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced; __instance.roleHeaderSprite.color = customRole.RoleColor; diff --git a/MiraAPI/Patches/PlayerControlPatches.cs b/MiraAPI/Patches/PlayerControlPatches.cs index 99b6efb..55fd72e 100644 --- a/MiraAPI/Patches/PlayerControlPatches.cs +++ b/MiraAPI/Patches/PlayerControlPatches.cs @@ -12,7 +12,7 @@ public static class PlayerControlPatches [HarmonyPatch(nameof(PlayerControl.Start))] public static void PlayerControlStartPostfix(PlayerControl __instance) { - __instance.gameObject.AddComponent(); + __instance.gameObject.AddComponent(); } [HarmonyPostfix] diff --git a/MiraAPI/Patches/Roles/NameTagPatch.cs b/MiraAPI/Patches/Roles/NameTagPatch.cs index 3566416..929ccaf 100644 --- a/MiraAPI/Patches/Roles/NameTagPatch.cs +++ b/MiraAPI/Patches/Roles/NameTagPatch.cs @@ -1,6 +1,4 @@ using HarmonyLib; -using MiraAPI.GameModes; -using MiraAPI.GameOptions; using UnityEngine; namespace MiraAPI.Patches.Roles; diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 2cc615c..2291243 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -14,14 +14,14 @@ public static class Extensions return ModdedOptionsManager.Options.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour.Equals(optionBehaviour)); } - public static ModifierManager GetModifierManager(this PlayerControl player) + public static ModifierComponent GetModifierComponent(this PlayerControl player) { - return player.gameObject.GetComponent(); + return player.gameObject.GetComponent(); } public static void AddModifier(this PlayerControl player) where T : BaseModifier { - player.GetModifierManager().AddModifier(); + player.GetModifierComponent().AddModifier(); } public static void RemoveModifier(this PlayerControl player) where T : BaseModifier From 26284852c972fdca8512294bfd137a6c25b7ed9d Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:48:16 -0400 Subject: [PATCH 04/11] fix merge issues --- MiraAPI/Modifiers/ModifierComponent.cs | 17 +++++++++++++++++ MiraAPI/Modifiers/Types/TimedModifier.cs | 12 ++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index 5dc1acc..bfe7d0e 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -1,6 +1,8 @@ using Reactor.Utilities.Attributes; using System; +using System.Collections; using System.Collections.Generic; +using MiraAPI.Modifiers.Types; using MiraAPI.Networking; using MiraAPI.Utilities; using Reactor.Networking.Attributes; @@ -20,6 +22,14 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) { player = GetComponent(); } + + public void Update() + { + foreach (var modifier in activeModifiers) + { + modifier.Update(); + } + } public bool HasModifier() where T : BaseModifier { @@ -94,4 +104,11 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) modifier.Player = modifierComponent.player; modifier.OnActivate(); } + + public static IEnumerator ModifierTimer(TimedModifier modifier) + { + yield return new WaitForSeconds(modifier.Duration); + modifier.TimerActive = false; + modifier.OnTimerComplete(); + } } \ No newline at end of file diff --git a/MiraAPI/Modifiers/Types/TimedModifier.cs b/MiraAPI/Modifiers/Types/TimedModifier.cs index 7408ff2..a193a38 100644 --- a/MiraAPI/Modifiers/Types/TimedModifier.cs +++ b/MiraAPI/Modifiers/Types/TimedModifier.cs @@ -6,17 +6,17 @@ public abstract class TimedModifier : BaseModifier public abstract float Duration { get; } public abstract void OnTimerComplete(); - internal bool timerOngoing = false; + internal bool TimerActive; - public void StartTimer(ModifierManager manager) + public void StartTimer() { - if (!timerOngoing) + if (TimerActive) { - Logger.Error("Can't start a timer that is already ongoing."); + Logger.Error("Can't start a timer that is already started."); return; } - timerOngoing = true; - Coroutines.Start(manager.ModifierTimer(this)); + TimerActive = true; + Coroutines.Start(ModifierComponent.ModifierTimer(this)); } } \ No newline at end of file From 9b173a4298e195e514d201034a28f158085f4ad7 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Fri, 23 Aug 2024 20:30:34 -0400 Subject: [PATCH 05/11] Updates to Modifiers --- MiraAPI.Example/CustomRole.cs | 20 ----- MiraAPI.Example/CustomRole2.cs | 20 ----- MiraAPI.Example/CustomRole3.cs | 17 ----- MiraAPI.Example/CustomRoleSettings.cs | 19 ----- MiraAPI.Example/ExampleOptions.cs | 33 --------- MiraAPI.Example/ExampleOptions2.cs | 27 ------- MiraAPI.Example/ExamplePlugin.cs | 1 + MiraAPI.Example/MiraDebugWindow.cs | 33 +++++++++ MiraAPI.Example/ModifierExample.cs | 8 +- MiraAPI.Example/SomeRandomNonGroupClass.cs | 9 --- MiraAPI.Example/TorchModifier.cs | 26 ------- MiraAPI/Modifiers/BaseModifier.cs | 4 +- MiraAPI/Modifiers/ModifierComponent.cs | 74 ++++++------------- MiraAPI/Modifiers/ModifierManager.cs | 31 ++++++-- MiraAPI/Modifiers/Types/TimedModifier.cs | 25 ++++++- MiraAPI/Networking/MiraRpc.cs | 3 +- MiraAPI/Networking/SyncModifiersRpc.cs | 46 ++++++++++++ MiraAPI/Patches/AmongUsClientSyncPatch.cs | 3 +- .../Patches/Options/RoleSettingMenuPatches.cs | 12 +-- MiraAPI/Utilities/Extensions.cs | 21 +++++- 20 files changed, 189 insertions(+), 243 deletions(-) delete mode 100644 MiraAPI.Example/CustomRole.cs delete mode 100644 MiraAPI.Example/CustomRole2.cs delete mode 100644 MiraAPI.Example/CustomRole3.cs delete mode 100644 MiraAPI.Example/CustomRoleSettings.cs delete mode 100644 MiraAPI.Example/ExampleOptions.cs delete mode 100644 MiraAPI.Example/ExampleOptions2.cs create mode 100644 MiraAPI.Example/MiraDebugWindow.cs delete mode 100644 MiraAPI.Example/SomeRandomNonGroupClass.cs delete mode 100644 MiraAPI.Example/TorchModifier.cs create mode 100644 MiraAPI/Networking/SyncModifiersRpc.cs diff --git a/MiraAPI.Example/CustomRole.cs b/MiraAPI.Example/CustomRole.cs deleted file mode 100644 index 47b361b..0000000 --- a/MiraAPI.Example/CustomRole.cs +++ /dev/null @@ -1,20 +0,0 @@ -using MiraAPI.GameOptions.Attributes; -using MiraAPI.Roles; -using MiraAPI.Utilities.Assets; -using UnityEngine; - -namespace MiraAPI.Example; - -[RegisterCustomRole] -public class CustomRole : ImpostorRole, ICustomRole -{ - public string RoleName => "fortnite killer"; - public string RoleLongDescription => "ok so your objective is to eliminate everyone with your fortnite tactical shotgun, good luck"; - public string RoleDescription => RoleLongDescription; - public Color RoleColor => Palette.Orange; - public ModdedRoleTeams Team => ModdedRoleTeams.Impostor; - public LoadableAsset OptionsScreenshot => MiraAssets.Banner; - - [ModdedNumberOption("Testing level", min: 3, max: 9, roleType: typeof(CustomRole))] public float testingLevel { get; set; } = 4; - -} \ No newline at end of file diff --git a/MiraAPI.Example/CustomRole2.cs b/MiraAPI.Example/CustomRole2.cs deleted file mode 100644 index 3033a12..0000000 --- a/MiraAPI.Example/CustomRole2.cs +++ /dev/null @@ -1,20 +0,0 @@ -using MiraAPI.GameOptions.Attributes; -using MiraAPI.Roles; -using MiraAPI.Utilities.Assets; -using UnityEngine; - -namespace MiraAPI.Example; - -[RegisterCustomRole] -public class CustomRole2 : CrewmateRole, ICustomRole -{ - public string RoleName => "fortnite defender"; - public string RoleLongDescription => "ok so your objective is to save everyone from the fortnite killer"; - public string RoleDescription => RoleLongDescription; - public Color RoleColor => Palette.AcceptedGreen; - public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate; - public LoadableAsset OptionsScreenshot => MiraAssets.Banner; - - [ModdedToggleOption("Teleportation ability", roleType: typeof(CustomRole2))] public bool teleport { get; set; } = true; - -} \ No newline at end of file diff --git a/MiraAPI.Example/CustomRole3.cs b/MiraAPI.Example/CustomRole3.cs deleted file mode 100644 index bf309d8..0000000 --- a/MiraAPI.Example/CustomRole3.cs +++ /dev/null @@ -1,17 +0,0 @@ -using MiraAPI.Roles; -using MiraAPI.Utilities.Assets; -using UnityEngine; - -namespace MiraAPI.Example; - -[RegisterCustomRole] -public class CustomRole3 : CrewmateRole, ICustomRole -{ - public string RoleName => "fortnite helper"; - public string RoleLongDescription => "ok so your objective is to help"; - public string RoleDescription => RoleLongDescription; - public Color RoleColor => Palette.Brown; - public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate; - public LoadableAsset OptionsScreenshot => MiraAssets.Banner; - -} \ No newline at end of file diff --git a/MiraAPI.Example/CustomRoleSettings.cs b/MiraAPI.Example/CustomRoleSettings.cs deleted file mode 100644 index 34cb47a..0000000 --- a/MiraAPI.Example/CustomRoleSettings.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using MiraAPI.GameOptions; -using MiraAPI.GameOptions.Attributes; -using MiraAPI.GameOptions.OptionTypes; - -namespace MiraAPI.Example; - -public class CustomRoleSettings : IModdedOptionGroup -{ - public string GroupName => "Custom Role"; - - public Type AdvancedRole => typeof(CustomRole); - - [ModdedNumberOption("amogus", min: 3, max: 9)] public float amogus { get; set; } = 1; - - [ModdedEnumOption("dwayne the rock johnson", typeof(TestEnum))] public TestEnum dwayneTheRockJohnson { get; set; } = TestEnum.Hello; - - public ModdedToggleOption testToggle { get; } = new ("test toggle", false); -} \ No newline at end of file diff --git a/MiraAPI.Example/ExampleOptions.cs b/MiraAPI.Example/ExampleOptions.cs deleted file mode 100644 index 4160189..0000000 --- a/MiraAPI.Example/ExampleOptions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using MiraAPI.GameOptions; -using MiraAPI.GameOptions.Attributes; -using UnityEngine; - -namespace MiraAPI.Example; - -public class ExampleOptions : IModdedOptionGroup -{ - public string GroupName => "General Group"; - public Color GroupColor => Color.red; - - [ModdedToggleOption("Use Thing")] - public bool useThing { get; set; } = false; - - [ModdedToggleOption("Use another thing")] - public bool useAnotherThing { get; set; } = true; - - [ModdedNumberOption("Sussy level", min: 0, max: 10)] - public float sussyLevel { get; set; } = 4f; - - [ModdedStringOption("Sus choices", ["hello", "hello 2", "hello 3"])] - public int susChoices { get; set; } = 2; - - [ModdedEnumOption("Enum test", typeof(TestEnum))] - public TestEnum enumTest { get; set; } = TestEnum.Bye; -} - -public enum TestEnum -{ - Hello, - Hi, - Bye -} \ No newline at end of file diff --git a/MiraAPI.Example/ExampleOptions2.cs b/MiraAPI.Example/ExampleOptions2.cs deleted file mode 100644 index e6b8fb6..0000000 --- a/MiraAPI.Example/ExampleOptions2.cs +++ /dev/null @@ -1,27 +0,0 @@ -using MiraAPI.GameOptions; -using MiraAPI.GameOptions.OptionTypes; - -namespace MiraAPI.Example; - -public class ExampleOptions2 : IModdedOptionGroup -{ - public string GroupName => "Example 2"; - - public ModdedToggleOption UseThing { get; } = new("Use Thingy 5", false); - - public ModdedToggleOption UseThing2 { get; } = new("Use Thingy 6", false); - - public ModdedStringOption Sussy { get; } = new("Sussy", 0, ["Sussy 1", "Sussy 2", "Sussy 3"]); - - public ModdedEnumOption SussyEnum { get; } = new("Sussy Enum", 0, typeof(SussyEnum)); - - public ModdedNumberOption YeezyLevel { get; } = new("Yeezy Level", 50, 0, 100, 25, NumberSuffixes.Multiplier); -} - -public enum SussyEnum -{ - Sussy, - Baka, - Imposter, - Kanye -} \ No newline at end of file diff --git a/MiraAPI.Example/ExamplePlugin.cs b/MiraAPI.Example/ExamplePlugin.cs index 1f3ac7b..593d252 100644 --- a/MiraAPI.Example/ExamplePlugin.cs +++ b/MiraAPI.Example/ExamplePlugin.cs @@ -21,5 +21,6 @@ public partial class ExamplePlugin : BasePlugin, IMiraPlugin public override void Load() { Harmony.PatchAll(); + AddComponent(); } } \ No newline at end of file diff --git a/MiraAPI.Example/MiraDebugWindow.cs b/MiraAPI.Example/MiraDebugWindow.cs new file mode 100644 index 0000000..34bbb64 --- /dev/null +++ b/MiraAPI.Example/MiraDebugWindow.cs @@ -0,0 +1,33 @@ +using MiraAPI.Utilities; +using Reactor.Utilities.Attributes; +using Reactor.Utilities.ImGui; +using System; +using UnityEngine; + +namespace MiraAPI.Example; +[RegisterInIl2Cpp] +public class MiraDebugWindow : MonoBehaviour +{ + public DragWindow DebuggingWindow; + public MiraDebugWindow(IntPtr ptr) : base(ptr) + { + DebuggingWindow = new DragWindow(new Rect(10, 10, 0, 0), "MIRA API DEBUGGING", () => + { + if (GUILayout.Button("Test modifier")) + { + PlayerControl.LocalPlayer.AddModifier(); + } + if (GUILayout.Button("Remove modifier")) + { + PlayerControl.LocalPlayer.RemoveModifier(); + } + }); + + DebuggingWindow.Enabled = true; + } + + public void OnGUI() + { + DebuggingWindow.OnGUI(); + } +} \ No newline at end of file diff --git a/MiraAPI.Example/ModifierExample.cs b/MiraAPI.Example/ModifierExample.cs index f30f079..54a474e 100644 --- a/MiraAPI.Example/ModifierExample.cs +++ b/MiraAPI.Example/ModifierExample.cs @@ -6,10 +6,14 @@ namespace MiraAPI.Example; public class ModifierExample : TimedModifier { public override string ModifierName => "Timed modifier example"; - public override float Duration => 10; + public override float Duration => 1.5f; + public override bool AutoStart => true; + + private System.Random _rand = new(); public override void OnTimerComplete() { - + Player.SetColor((byte)_rand.Next(0, 18)); + StartTimer(); } } \ No newline at end of file diff --git a/MiraAPI.Example/SomeRandomNonGroupClass.cs b/MiraAPI.Example/SomeRandomNonGroupClass.cs deleted file mode 100644 index 7e57075..0000000 --- a/MiraAPI.Example/SomeRandomNonGroupClass.cs +++ /dev/null @@ -1,9 +0,0 @@ -using MiraAPI.GameOptions.Attributes; - -namespace MiraAPI.Example; - -public class SomeRandomNonGroupClass -{ - [ModdedStringOption("Yeah, idk", ["Idk 1", "idk 2", "idk 3", "idk 4"])] public static int yeaIdk { get; set; } = 3; - [ModdedNumberOption("Aw man", min: 45, max: 95)] public float sussyLevel { get; set; } = 63; -} \ No newline at end of file diff --git a/MiraAPI.Example/TorchModifier.cs b/MiraAPI.Example/TorchModifier.cs deleted file mode 100644 index 6da062f..0000000 --- a/MiraAPI.Example/TorchModifier.cs +++ /dev/null @@ -1,26 +0,0 @@ -using MiraAPI.GameOptions.Attributes; -using MiraAPI.Modifiers.Types; - -namespace MiraAPI.Example -{ - [RegisterModifier] - public class TorchModifier : GameModifier - { - public override string ModifierName => "Torch"; - - [ModdedNumberOption("Torch Chance", 0, 100, 10)] - public float chance { get; } = 50; - [ModdedNumberOption("Torch Amount", 0, 10, 1)] - public float amount { get; } = 3; - - public override int GetAmountPerGame() - { - return (int)amount; - } - - public override int GetAssignmentChance() - { - return (int)chance; - } - } -} diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index 2e6812b..07bac31 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -2,9 +2,9 @@ public abstract class BaseModifier { public abstract string ModifierName { get; } - + public PlayerControl Player { get; internal set; } - + public virtual void OnActivate() { } public virtual void OnDeactivate() { } public virtual void Update() { } diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index bfe7d0e..e9955af 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -1,12 +1,11 @@ -using Reactor.Utilities.Attributes; -using System; -using System.Collections; -using System.Collections.Generic; -using MiraAPI.Modifiers.Types; +using MiraAPI.Modifiers.Types; using MiraAPI.Networking; using MiraAPI.Utilities; using Reactor.Networking.Attributes; using Reactor.Utilities; +using Reactor.Utilities.Attributes; +using System; +using System.Collections.Generic; using UnityEngine; namespace MiraAPI.Modifiers; @@ -22,7 +21,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) { player = GetComponent(); } - + public void Update() { foreach (var modifier in activeModifiers) @@ -31,44 +30,16 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) } } - public bool HasModifier() where T : BaseModifier - { - return activeModifiers.Exists(x=>x is T); - } - - public void AddModifier() where T : BaseModifier - { - if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id)) - { - Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); - return; - } - - RpcAddModifier(player, id); - } - - public void RemoveModifier() where T : BaseModifier - { - if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id)) - { - Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); - return; - } - - RpcRemoveModifier(player, id); - } - - [MethodRpc((uint)MiraRpc.RemoveModifier)] - private static void RpcRemoveModifier(PlayerControl target, uint modifierId) + public static void RemoveModifier(PlayerControl target, uint modifierId) { if (!ModifierManager.IdToTypeModifiers.TryGetValue(modifierId, out var type)) { Logger.Error($"Cannot remove modifier with id {modifierId} because it is not registered."); return; } - + var modifierComponent = target.GetModifierComponent(); - + var modifier = modifierComponent.activeModifiers.Find(x => x.GetType() == type); if (modifier is null) @@ -76,39 +47,42 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) Logger.Error($"Cannot remove modifier {type.Name} because it is not active."); return; } - + modifier.OnDeactivate(); modifierComponent.activeModifiers.Remove(modifier); } - [MethodRpc((uint)MiraRpc.AddModifier)] - private static void RpcAddModifier(PlayerControl target, uint modifierId) + public static void AddModifier(PlayerControl target, uint modifierId) { if (!ModifierManager.IdToTypeModifiers.TryGetValue(modifierId, out var type)) { Logger.Error($"Cannot add modifier with id {modifierId} because it is not registered."); return; } - + var modifier = (BaseModifier)Activator.CreateInstance(type); var modifierComponent = target.GetModifierComponent(); - + if (modifier is null) { Logger.Error($"Cannot add modifier {type.Name} because it is null."); return; } - + modifierComponent.activeModifiers.Add(modifier); - modifier.Player = modifierComponent.player; + modifier.Player = modifierComponent.player; modifier.OnActivate(); + + if (modifier is TimedModifier timer && timer.AutoStart && target.AmOwner) + { + timer.StartTimer(); + } } - public static IEnumerator ModifierTimer(TimedModifier modifier) - { - yield return new WaitForSeconds(modifier.Duration); - modifier.TimerActive = false; - modifier.OnTimerComplete(); - } + [MethodRpc((uint)MiraRpc.RemoveModifier)] + public static void RpcRemoveModifier(PlayerControl target, uint modifierId) => RemoveModifier(target, modifierId); + + [MethodRpc((uint)MiraRpc.AddModifier)] + public static void RpcAddModifier(PlayerControl target, uint modifierId) => AddModifier(target, modifierId); } \ No newline at end of file diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs index 8093e09..32d95ef 100644 --- a/MiraAPI/Modifiers/ModifierManager.cs +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -1,4 +1,5 @@ -using System; +using MiraAPI.Networking; +using System; using System.Collections.Generic; namespace MiraAPI.Modifiers; @@ -7,22 +8,40 @@ public static class ModifierManager { public static readonly Dictionary IdToTypeModifiers = []; public static readonly Dictionary TypeToIdModifiers = []; - + private static uint _nextId; - + private static uint GetNextId() { - return _nextId++; + _nextId++; + return _nextId; } - + public static void RegisterModifier(Type modifierType) { if (!typeof(BaseModifier).IsAssignableFrom(modifierType)) { return; } - + IdToTypeModifiers.Add(GetNextId(), modifierType); TypeToIdModifiers.Add(modifierType, _nextId); } + + public static void SyncAllModifiers(int targetId = -1) + { + + } + + public static void HandleSyncModifiers(NetData[] data) + { + foreach (var netData in data) + { + PlayerControl plr = GameData.Instance.GetPlayerById((byte)netData.Id).Object; + foreach (var id in netData.Data) + { + ModifierComponent.AddModifier(plr, id); + } + } + } } \ No newline at end of file diff --git a/MiraAPI/Modifiers/Types/TimedModifier.cs b/MiraAPI/Modifiers/Types/TimedModifier.cs index a193a38..1725ed0 100644 --- a/MiraAPI/Modifiers/Types/TimedModifier.cs +++ b/MiraAPI/Modifiers/Types/TimedModifier.cs @@ -1,22 +1,41 @@ using Reactor.Utilities; +using UnityEngine; namespace MiraAPI.Modifiers.Types; public abstract class TimedModifier : BaseModifier { public abstract float Duration { get; } + public virtual bool AutoStart => false; public abstract void OnTimerComplete(); - internal bool TimerActive; + public bool TimerActive = false; + public float TimeRemaining; + + public override void Update() + { + if (!Player.AmOwner) return; + + if (TimeRemaining > 0 && TimerActive) + { + TimeRemaining -= Time.deltaTime; + } + else + { + TimerActive = false; + TimeRemaining = Duration; + OnTimerComplete(); + } + } public void StartTimer() { if (TimerActive) { - Logger.Error("Can't start a timer that is already started."); + Logger.Error("Can't start a timer that has already been started."); return; } TimerActive = true; - Coroutines.Start(ModifierComponent.ModifierTimer(this)); + TimeRemaining = Duration; } } \ No newline at end of file diff --git a/MiraAPI/Networking/MiraRpc.cs b/MiraAPI/Networking/MiraRpc.cs index 268f680..50625bc 100644 --- a/MiraAPI/Networking/MiraRpc.cs +++ b/MiraAPI/Networking/MiraRpc.cs @@ -5,5 +5,6 @@ public enum MiraRpc : uint SyncGameOptions, SyncRoleOptions, AddModifier, - RemoveModifier + RemoveModifier, + SyncModifiers } \ No newline at end of file diff --git a/MiraAPI/Networking/SyncModifiersRpc.cs b/MiraAPI/Networking/SyncModifiersRpc.cs new file mode 100644 index 0000000..e760522 --- /dev/null +++ b/MiraAPI/Networking/SyncModifiersRpc.cs @@ -0,0 +1,46 @@ +using Hazel; +using MiraAPI.Modifiers; +using Reactor.Networking.Attributes; +using Reactor.Networking.Rpc; + +namespace MiraAPI.Networking; + +[RegisterCustomRpc((uint)MiraRpc.SyncModifiers)] +public class SyncModifiersRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) +{ + public override RpcLocalHandling LocalHandling => RpcLocalHandling.None; + + public override void Write(MessageWriter writer, NetData[] data) + { + writer.WritePacked((uint)data.Length); + foreach (var netData in data) + { + writer.WritePacked(netData.Id); + writer.WriteBytesAndSize(netData.Data); + } + } + + public override NetData[] Read(MessageReader reader) + { + var length = reader.ReadPackedUInt32(); + var data = new NetData[length]; + for (var i = 0; i < length; i++) + { + var id = reader.ReadPackedUInt32(); + var bytes = reader.ReadBytesAndSize(); + data[i] = new NetData(id, bytes); + } + + return data; + } + + public override void Handle(PlayerControl playerControl, NetData[] data) + { + if (AmongUsClient.Instance.HostId != playerControl.OwnerId) + { + return; + } + + ModifierManager.HandleSyncModifiers(data); + } +} \ No newline at end of file diff --git a/MiraAPI/Patches/AmongUsClientSyncPatch.cs b/MiraAPI/Patches/AmongUsClientSyncPatch.cs index 121eca5..3e71ae5 100644 --- a/MiraAPI/Patches/AmongUsClientSyncPatch.cs +++ b/MiraAPI/Patches/AmongUsClientSyncPatch.cs @@ -14,7 +14,7 @@ public static class AmongUsClientSyncPatch { return; } - + if (clientData.Id == AmongUsClient.Instance.HostId) { return; @@ -22,5 +22,6 @@ public static class AmongUsClientSyncPatch ModdedOptionsManager.SyncAllOptions(clientData.Id); CustomRoleManager.SyncAllRoleSettings(clientData.Id); + // ModifierManager.SyncAllModifiers(clientData.Id); } } \ No newline at end of file diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index bcdecbe..48ff1dd 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -1,13 +1,13 @@ using HarmonyLib; +using MiraAPI.Networking; using MiraAPI.Roles; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; +using Reactor.Networking.Rpc; +using Reactor.Utilities; using Reactor.Utilities.Extensions; using System; using System.Linq; -using MiraAPI.Networking; -using Reactor.Networking.Rpc; -using Reactor.Utilities; using TMPro; using UnityEngine; using UnityEngine.Events; @@ -25,7 +25,7 @@ public static class RoleSettingMenuPatches { __instance.roleChances = new Il2CppSystem.Collections.Generic.List(); __instance.advancedSettingChildren = new Il2CppSystem.Collections.Generic.List(); - + var maskBg = __instance.scrollBar.transform.FindChild("MaskBg"); if (GameSettingMenuPatches.CurrentSelectedMod == 0) @@ -124,7 +124,7 @@ public static class RoleSettingMenuPatches private static void CreateAdvancedSettings(RolesSettingsMenu __instance, RoleBehaviour role) { - foreach (var option in __instance.advancedSettingChildren) + foreach (var option in __instance.advancedSettingChildren) { Object.Destroy(option.gameObject); } @@ -162,7 +162,7 @@ public static class RoleSettingMenuPatches { ICustomRole customRole = role as ICustomRole; __instance.roleDescriptionText.text = customRole.RoleLongDescription; - __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, []); + __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, Array.Empty()); __instance.roleScreenshot.sprite = Sprite.Create(role.RoleScreenshot.texture, new Rect(0, 0, 370, 230), Vector2.one / 2, 100); __instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced; __instance.roleHeaderSprite.color = customRole.RoleColor; diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 2291243..21d7d5a 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -1,6 +1,7 @@ using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.Roles; +using Reactor.Utilities; using Reactor.Utilities.Extensions; using System.Linq; using UnityEngine; @@ -19,13 +20,31 @@ public static class Extensions return player.gameObject.GetComponent(); } + public static bool HasModifier(this PlayerControl player) where T : BaseModifier + { + return player.GetModifierComponent().activeModifiers.Exists(x => x is T); + } + public static void AddModifier(this PlayerControl player) where T : BaseModifier { - player.GetModifierComponent().AddModifier(); + if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id)) + { + Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); + return; + } + + ModifierComponent.RpcAddModifier(player, id); } public static void RemoveModifier(this PlayerControl player) where T : BaseModifier { + if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id)) + { + Logger.Error($"Cannot add modifier {typeof(T).Name} because it is not registered."); + return; + } + + ModifierComponent.RpcRemoveModifier(player, id); } public static Color DarkenColor(this Color color) From a9ff1c7f6351f2ab14e38a4098e36f7b86d3a08f Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sat, 24 Aug 2024 13:36:49 -0400 Subject: [PATCH 06/11] networking modifiers --- MiraAPI/Modifiers/ModifierComponent.cs | 12 +++--- MiraAPI/Modifiers/ModifierManager.cs | 60 ++++++++++++++++++++++++-- MiraAPI/Utilities/Extensions.cs | 6 ++- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index e9955af..ac7c20a 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -13,7 +13,7 @@ namespace MiraAPI.Modifiers; [RegisterInIl2Cpp] public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) { - public List activeModifiers = []; + public readonly List ActiveModifiers = []; public PlayerControl player; @@ -24,7 +24,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) public void Update() { - foreach (var modifier in activeModifiers) + foreach (var modifier in ActiveModifiers) { modifier.Update(); } @@ -40,7 +40,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) var modifierComponent = target.GetModifierComponent(); - var modifier = modifierComponent.activeModifiers.Find(x => x.GetType() == type); + var modifier = modifierComponent.ActiveModifiers.Find(x => x.GetType() == type); if (modifier is null) { @@ -49,7 +49,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) } modifier.OnDeactivate(); - modifierComponent.activeModifiers.Remove(modifier); + modifierComponent.ActiveModifiers.Remove(modifier); } public static void AddModifier(PlayerControl target, uint modifierId) @@ -70,11 +70,11 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) return; } - modifierComponent.activeModifiers.Add(modifier); + modifierComponent.ActiveModifiers.Add(modifier); modifier.Player = modifierComponent.player; modifier.OnActivate(); - if (modifier is TimedModifier timer && timer.AutoStart && target.AmOwner) + if (modifier is TimedModifier { AutoStart: true } timer && target.AmOwner) { timer.StartTimer(); } diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs index 32d95ef..f647e8d 100644 --- a/MiraAPI/Modifiers/ModifierManager.cs +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -1,6 +1,8 @@ using MiraAPI.Networking; using System; using System.Collections.Generic; +using MiraAPI.Utilities; +using Reactor.Networking.Rpc; namespace MiraAPI.Modifiers; @@ -30,18 +32,68 @@ public static class ModifierManager public static void SyncAllModifiers(int targetId = -1) { - + var data = new List(); + + if (targetId == -1) + { + foreach (var player in GameData.Instance.AllPlayers) + { + data.Add(GetPlayerModifiers(player.Object)); + } + } + else + { + var player = GameData.Instance.GetPlayerById((byte)targetId).Object; + + data.Add(GetPlayerModifiers(player)); + } + + + Rpc.Instance.Send(PlayerControl.LocalPlayer, data.ToArray(), true); } - + public static void HandleSyncModifiers(NetData[] data) { foreach (var netData in data) { - PlayerControl plr = GameData.Instance.GetPlayerById((byte)netData.Id).Object; - foreach (var id in netData.Data) + var ids = new uint[netData.Data.Length / 4]; + Buffer.BlockCopy(netData.Data, 0, ids, 0, netData.Data.Length); + + var plr = GameData.Instance.GetPlayerById((byte)netData.Id).Object; + var modifierComponent = plr.GetComponent(); + + if (!modifierComponent) + { + continue; + } + + foreach (var modifier in modifierComponent.ActiveModifiers) + { + modifier.OnDeactivate(); + } + modifierComponent.ActiveModifiers.Clear(); + + foreach (var id in ids) { ModifierComponent.AddModifier(plr, id); } } } + + private static NetData GetPlayerModifiers(PlayerControl player) + { + var bytes = new List(); + var modifierComponent = player.GetComponent(); + if (!modifierComponent) + { + return new NetData(player.PlayerId, []); + } + + foreach (var modifier in modifierComponent.ActiveModifiers) + { + bytes.AddRange(BitConverter.GetBytes(TypeToIdModifiers[modifier.GetType()])); + } + + return new NetData(player.PlayerId, bytes.ToArray()); + } } \ No newline at end of file diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 21d7d5a..7187b5b 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -1,10 +1,12 @@ -using MiraAPI.GameOptions; +using System; +using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.Roles; using Reactor.Utilities; using Reactor.Utilities.Extensions; using System.Linq; using UnityEngine; +using Object = UnityEngine.Object; namespace MiraAPI.Utilities; @@ -22,7 +24,7 @@ public static class Extensions public static bool HasModifier(this PlayerControl player) where T : BaseModifier { - return player.GetModifierComponent().activeModifiers.Exists(x => x is T); + return player.GetModifierComponent().ActiveModifiers.Exists(x => x is T); } public static void AddModifier(this PlayerControl player) where T : BaseModifier From a88646465b3e676549c871f56efe93cad2b16a10 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Mon, 26 Aug 2024 20:02:05 -0400 Subject: [PATCH 07/11] GameModifiers, and fixes --- MiraAPI.Example/ExampleButton.cs | 5 +- MiraAPI.Example/ModifierExample.cs | 23 +++--- MiraAPI/Hud/CustomActionButton.cs | 8 +-- MiraAPI/Modifiers/BaseModifier.cs | 4 +- MiraAPI/Modifiers/ModifierComponent.cs | 18 ++++- MiraAPI/Modifiers/ModifierManager.cs | 78 +++++++++++++++++---- MiraAPI/Modifiers/Types/GameModifier.cs | 1 + MiraAPI/Patches/Modifiers/VentPatches.cs | 46 ++++++++++++ MiraAPI/Patches/PlayerControlPatches.cs | 8 ++- MiraAPI/Patches/Roles/RoleManagerPatches.cs | 11 +++ MiraAPI/Roles/ICustomRole.cs | 22 +++--- MiraAPI/Utilities/Extensions.cs | 25 +++++-- MiraAPI/Utilities/Helpers.cs | 3 +- 13 files changed, 202 insertions(+), 50 deletions(-) create mode 100644 MiraAPI/Patches/Modifiers/VentPatches.cs diff --git a/MiraAPI.Example/ExampleButton.cs b/MiraAPI.Example/ExampleButton.cs index d7058bc..df45015 100644 --- a/MiraAPI.Example/ExampleButton.cs +++ b/MiraAPI.Example/ExampleButton.cs @@ -1,4 +1,5 @@ using MiraAPI.Hud; +using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Utilities; using UnityEngine; @@ -13,7 +14,7 @@ public class ExampleButton : CustomActionButton public override float EffectDuration => 0f; public override int MaxUses => 5; public override LoadableAsset Sprite { get; } = new LoadableResourceAsset("MiraAPI.Resources.ExampleButton.png"); - + protected override void OnClick() { Logger.Info("Example button clicked!"); @@ -21,6 +22,6 @@ public class ExampleButton : CustomActionButton public override bool Enabled(RoleBehaviour role) { - return true; + return PlayerControl.LocalPlayer.HasModifier(); } } \ No newline at end of file diff --git a/MiraAPI.Example/ModifierExample.cs b/MiraAPI.Example/ModifierExample.cs index 54a474e..bb2ecb7 100644 --- a/MiraAPI.Example/ModifierExample.cs +++ b/MiraAPI.Example/ModifierExample.cs @@ -3,17 +3,22 @@ namespace MiraAPI.Example; [RegisterModifier] -public class ModifierExample : TimedModifier +public class ModifierExample : GameModifier { - public override string ModifierName => "Timed modifier example"; - public override float Duration => 1.5f; - public override bool AutoStart => true; + public override string ModifierName => "Example"; - private System.Random _rand = new(); - - public override void OnTimerComplete() + public override bool CanVent() { - Player.SetColor((byte)_rand.Next(0, 18)); - StartTimer(); + return true; + } + + public override int GetAmountPerGame() + { + return 1; + } + + public override int GetAssignmentChance() + { + return 100; } } \ No newline at end of file diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs index 4acef2b..05c933f 100644 --- a/MiraAPI/Hud/CustomActionButton.cs +++ b/MiraAPI/Hud/CustomActionButton.cs @@ -60,7 +60,7 @@ public abstract class CustomActionButton /// The button object in game. This is created by Mira API automatically. /// protected ActionButton Button { get; private set; } - + internal void CreateButton(Transform parent) { if (Button) @@ -99,10 +99,10 @@ public abstract class CustomActionButton { OnEffectEnd(); } - + EffectActive = false; } - + /// /// A utility function to override the sprite of the button. /// @@ -225,7 +225,7 @@ public abstract class CustomActionButton { Button.SetDisabled(); } - + Button.SetCoolDown(Timer, EffectActive ? EffectDuration : Cooldown); FixedUpdate(playerControl); diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index 07bac31..61f27bb 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -2,11 +2,11 @@ public abstract class BaseModifier { public abstract string ModifierName { get; } - public PlayerControl Player { get; internal set; } - + public uint ModifierId { get; internal set; } public virtual void OnActivate() { } public virtual void OnDeactivate() { } public virtual void Update() { } public virtual void OnDeath() { } + public virtual bool CanVent() => Player.Data.Role.CanVent; } \ No newline at end of file diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index ac7c20a..32e20ce 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -13,13 +13,14 @@ namespace MiraAPI.Modifiers; [RegisterInIl2Cpp] public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) { - public readonly List ActiveModifiers = []; + public List ActiveModifiers { get; private set; } public PlayerControl player; public void Start() { player = GetComponent(); + ActiveModifiers = new List(); } public void Update() @@ -50,6 +51,11 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) modifier.OnDeactivate(); modifierComponent.ActiveModifiers.Remove(modifier); + + if (target.AmOwner) + { + HudManager.Instance.SetHudActive(true); + } } public static void AddModifier(PlayerControl target, uint modifierId) @@ -72,11 +78,17 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) modifierComponent.ActiveModifiers.Add(modifier); modifier.Player = modifierComponent.player; + modifier.ModifierId = modifierId; modifier.OnActivate(); - if (modifier is TimedModifier { AutoStart: true } timer && target.AmOwner) + if (target.AmOwner) { - timer.StartTimer(); + if (modifier is TimedModifier { AutoStart: true } timer) + { + timer.StartTimer(); + } + + HudManager.Instance.SetHudActive(true); } } diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs index f647e8d..afe81b1 100644 --- a/MiraAPI/Modifiers/ModifierManager.cs +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -1,8 +1,12 @@ -using MiraAPI.Networking; -using System; -using System.Collections.Generic; +using MiraAPI.Modifiers.Types; +using MiraAPI.Networking; +using MiraAPI.Roles; using MiraAPI.Utilities; using Reactor.Networking.Rpc; +using Reactor.Utilities.Extensions; +using System; +using System.Collections.Generic; +using Random = System.Random; namespace MiraAPI.Modifiers; @@ -30,10 +34,58 @@ public static class ModifierManager TypeToIdModifiers.Add(modifierType, _nextId); } + public static void AssignModifiers(List plrs) + { + Random rand = new Random(); + + List filteredModifiers = []; + + foreach (var modifier in IdToTypeModifiers) + { + var mod = (GameModifier)Activator.CreateInstance(modifier.Value); + var num = mod.GetAmountPerGame(); + var chance = mod.GetAssignmentChance(); + + for (int i = 0; i < num; i++) + { + int randomNum = rand.Next(100); + + if (randomNum < Math.Clamp(chance, 0, 100)) + { + filteredModifiers.Add(TypeToIdModifiers[modifier.Value]); + } + } + } + + var shuffledModifiers = filteredModifiers.Randomize(); + if (shuffledModifiers.Count > plrs.Count) + { + shuffledModifiers = shuffledModifiers.GetRange(0, plrs.Count); + } + + while (shuffledModifiers.Count > 0) + { + var id = shuffledModifiers[0]; + + var mod = (GameModifier)Activator.CreateInstance(IdToTypeModifiers[id]); + var plr = plrs.Random(); + + if (plr.Data.Role is ICustomRole modRole) + { + if (!modRole.IsModifierApplicable(mod)) continue; + } + + if (plr.HasModifier(id) || mod.IsModifierValidOn(plr.Data.Role)) continue; + + shuffledModifiers.RemoveAt(0); + ModifierComponent.RpcAddModifier(plr, id); + } + } + public static void SyncAllModifiers(int targetId = -1) { var data = new List(); - + if (targetId == -1) { foreach (var player in GameData.Instance.AllPlayers) @@ -44,24 +96,24 @@ public static class ModifierManager else { var player = GameData.Instance.GetPlayerById((byte)targetId).Object; - + data.Add(GetPlayerModifiers(player)); } - - + + Rpc.Instance.Send(PlayerControl.LocalPlayer, data.ToArray(), true); } - + public static void HandleSyncModifiers(NetData[] data) { foreach (var netData in data) { var ids = new uint[netData.Data.Length / 4]; Buffer.BlockCopy(netData.Data, 0, ids, 0, netData.Data.Length); - + var plr = GameData.Instance.GetPlayerById((byte)netData.Id).Object; var modifierComponent = plr.GetComponent(); - + if (!modifierComponent) { continue; @@ -72,7 +124,7 @@ public static class ModifierManager modifier.OnDeactivate(); } modifierComponent.ActiveModifiers.Clear(); - + foreach (var id in ids) { ModifierComponent.AddModifier(plr, id); @@ -88,12 +140,12 @@ public static class ModifierManager { return new NetData(player.PlayerId, []); } - + foreach (var modifier in modifierComponent.ActiveModifiers) { bytes.AddRange(BitConverter.GetBytes(TypeToIdModifiers[modifier.GetType()])); } - + return new NetData(player.PlayerId, bytes.ToArray()); } } \ No newline at end of file diff --git a/MiraAPI/Modifiers/Types/GameModifier.cs b/MiraAPI/Modifiers/Types/GameModifier.cs index d1339ea..cda0844 100644 --- a/MiraAPI/Modifiers/Types/GameModifier.cs +++ b/MiraAPI/Modifiers/Types/GameModifier.cs @@ -3,4 +3,5 @@ public abstract class GameModifier : BaseModifier { public abstract int GetAssignmentChance(); public abstract int GetAmountPerGame(); + public virtual bool IsModifierValidOn(RoleBehaviour role) => true; } \ No newline at end of file diff --git a/MiraAPI/Patches/Modifiers/VentPatches.cs b/MiraAPI/Patches/Modifiers/VentPatches.cs new file mode 100644 index 0000000..e2c6e61 --- /dev/null +++ b/MiraAPI/Patches/Modifiers/VentPatches.cs @@ -0,0 +1,46 @@ +using HarmonyLib; +using MiraAPI.Modifiers; +using MiraAPI.Utilities; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace MiraAPI.Patches.Modifiers; + +[HarmonyPatch(typeof(Vent))] +public static class VentPatches +{ + [HarmonyPostfix] + [HarmonyPatch(nameof(Vent.CanUse))] + public static void CanUseVentPatch(Vent __instance, ref float __result, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse) + { + var @object = pc.Object; + var role = @object.Data.Role; + + List modifiers = @object.GetModifierComponent().ActiveModifiers; + if (modifiers.Count > 0) + { + if (role.CanVent && modifiers.Any(x => !x.CanVent())) + { + couldUse = canUse = false; + return; + } + else if (!role.CanVent && modifiers.Any(x => x.CanVent())) + { + couldUse = true; + } + + var num = float.MaxValue; + + canUse = couldUse; + if (canUse) + { + var center = @object.Collider.bounds.center; + var position = __instance.transform.position; + num = Vector2.Distance(center, position); + canUse &= num <= __instance.UsableDistance && !PhysicsHelpers.AnythingBetween(@object.Collider, center, position, Constants.ShipOnlyMask, false); + } + __result = num; + } + } +} \ No newline at end of file diff --git a/MiraAPI/Patches/PlayerControlPatches.cs b/MiraAPI/Patches/PlayerControlPatches.cs index 55fd72e..2be9a6d 100644 --- a/MiraAPI/Patches/PlayerControlPatches.cs +++ b/MiraAPI/Patches/PlayerControlPatches.cs @@ -1,17 +1,19 @@ using HarmonyLib; using MiraAPI.Hud; using MiraAPI.Modifiers; +using Reactor.Utilities.Extensions; namespace MiraAPI.Patches; [HarmonyPatch(typeof(PlayerControl))] public static class PlayerControlPatches { - - [HarmonyPostfix] + [HarmonyPrefix] [HarmonyPatch(nameof(PlayerControl.Start))] public static void PlayerControlStartPostfix(PlayerControl __instance) { + if (__instance.gameObject.TryGetComponent(out var comp)) comp.DestroyImmediate(); + __instance.gameObject.AddComponent(); } @@ -23,7 +25,7 @@ public static class PlayerControlPatches foreach (var button in CustomButtonManager.CustomButtons) { - if (!button.Enabled(__instance.Data.Role)) + if (!button.Enabled(__instance.Data?.Role)) { continue; } diff --git a/MiraAPI/Patches/Roles/RoleManagerPatches.cs b/MiraAPI/Patches/Roles/RoleManagerPatches.cs index a7cd867..9e84bf9 100644 --- a/MiraAPI/Patches/Roles/RoleManagerPatches.cs +++ b/MiraAPI/Patches/Roles/RoleManagerPatches.cs @@ -1,6 +1,8 @@ using AmongUs.GameOptions; using HarmonyLib; +using MiraAPI.Modifiers; using MiraAPI.Roles; +using System.Linq; using UnityEngine; namespace MiraAPI.Patches.Roles; @@ -8,6 +10,15 @@ namespace MiraAPI.Patches.Roles; [HarmonyPatch(typeof(RoleManager))] public static class RoleManagerPatches { + [HarmonyPostfix] + [HarmonyPatch(nameof(RoleManager.SelectRoles))] + public static void ModifierSelectionPatches(RoleManager __instance) + { + if (!AmongUsClient.Instance.AmHost) return; + + ModifierManager.AssignModifiers(PlayerControl.AllPlayerControls.ToArray().Where(plr => !plr.Data.IsDead && !plr.Data.Disconnected).ToList()); + } + [HarmonyPrefix] [HarmonyPatch(nameof(RoleManager.AssignRoleOnDeath))] public static bool AssignRoleOnDeath(RoleManager __instance, [HarmonyArgument(0)] PlayerControl plr) diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 67e6506..e1915a5 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -1,12 +1,13 @@ -using System; -using AmongUs.GameOptions; +using AmongUs.GameOptions; using BepInEx.Configuration; +using HarmonyLib; +using MiraAPI.Modifiers; +using MiraAPI.Networking; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; -using System.Text; -using HarmonyLib; -using MiraAPI.Networking; using Reactor.Utilities; +using System; +using System.Text; using UnityEngine; namespace MiraAPI.Roles; @@ -22,7 +23,7 @@ public interface ICustomRole Color RoleColor { get; } ModdedRoleTeams Team { get; } - + LoadableAsset OptionsScreenshot { get; } LoadableAsset Icon => MiraAssets.Empty; @@ -69,12 +70,17 @@ public interface ICustomRole var taskStringBuilder = Helpers.CreateForRole(this); return taskStringBuilder; } - + + bool IsModifierApplicable(BaseModifier modifier) + { + return true; + } + NetData GetNetData(RoleBehaviour roleBehaviour) { PluginSingleton.Instance.Config.TryGetEntry(NumConfigDefinition, out var numEntry); PluginSingleton.Instance.Config.TryGetEntry(ChanceConfigDefinition, out var chanceEntry); - + return new NetData((uint)roleBehaviour.Role, BitConverter.GetBytes(numEntry.Value).AddRangeToArray(BitConverter.GetBytes(chanceEntry.Value))); } } \ No newline at end of file diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 7187b5b..667cc5d 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -1,9 +1,9 @@ -using System; -using MiraAPI.GameOptions; +using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.Roles; using Reactor.Utilities; using Reactor.Utilities.Extensions; +using System.Collections.Generic; using System.Linq; using UnityEngine; using Object = UnityEngine.Object; @@ -19,12 +19,29 @@ public static class Extensions public static ModifierComponent GetModifierComponent(this PlayerControl player) { - return player.gameObject.GetComponent(); + return player.GetComponent(); + } + + public static List Randomize(this List list) + { + List randomizedList = new List(); + System.Random rnd = new(); + while (list.Count > 0) + { + int index = rnd.Next(0, list.Count); + randomizedList.Add(list[index]); + list.RemoveAt(index); + } + return randomizedList; } public static bool HasModifier(this PlayerControl player) where T : BaseModifier { - return player.GetModifierComponent().ActiveModifiers.Exists(x => x is T); + return player is not null && player.GetModifierComponent() is not null && player.GetModifierComponent().ActiveModifiers.Exists(x => x is T); + } + public static bool HasModifier(this PlayerControl player, uint id) + { + return player is not null && player.GetModifierComponent() is not null && player.GetModifierComponent().ActiveModifiers.Exists(x => x.ModifierId == id); } public static void AddModifier(this PlayerControl player) where T : BaseModifier diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index 765aaec..5e922cc 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -8,7 +8,6 @@ namespace MiraAPI.Utilities; public static class Helpers { public static readonly ContactFilter2D Filter = ContactFilter2D.CreateLegacyFilter(Constants.NotShipMask, float.MinValue, float.MaxValue); - public static PlainShipRoom GetRoom(Vector3 pos) { return ShipStatus.Instance.AllRooms.ToList().Find(room => room.roomArea.OverlapPoint(pos)); @@ -18,7 +17,7 @@ public static class Helpers { return Object.FindObjectsOfType().FirstOrDefault(body => body.ParentId == id); } - + public static string GetSuffix(NumberSuffixes suffix) { return suffix switch From dfa55911885696eeb75e093b936be9349422d252 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Mon, 26 Aug 2024 21:28:34 -0400 Subject: [PATCH 08/11] modifiers UI --- MiraAPI.Example/ExampleRole.cs | 16 ++++++++ MiraAPI.Example/MiraDebugWindow.cs | 4 +- MiraAPI.Example/ModifierExample.cs | 2 +- MiraAPI.Example/ModifierTimerExample.cs | 20 ++++++++++ MiraAPI/Modifiers/BaseModifier.cs | 2 + MiraAPI/Modifiers/ModifierComponent.cs | 50 ++++++++++++++++++++++-- MiraAPI/Modifiers/Types/TimedModifier.cs | 4 +- MiraAPI/Utilities/Helpers.cs | 28 +++++++++++++ 8 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 MiraAPI.Example/ExampleRole.cs create mode 100644 MiraAPI.Example/ModifierTimerExample.cs diff --git a/MiraAPI.Example/ExampleRole.cs b/MiraAPI.Example/ExampleRole.cs new file mode 100644 index 0000000..6de70f2 --- /dev/null +++ b/MiraAPI.Example/ExampleRole.cs @@ -0,0 +1,16 @@ +using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; +using UnityEngine; + +namespace MiraAPI.Example; + +[RegisterCustomRole] +public class ExampleRole : ImpostorRole, ICustomRole +{ + public string RoleName => "fortnite killer"; + public string RoleLongDescription => "ok so your objective is to eliminate everyone \nwith your fortnite tactical shotgun"; + public string RoleDescription => RoleLongDescription; + public Color RoleColor => Palette.Orange; + public ModdedRoleTeams Team => ModdedRoleTeams.Impostor; + public LoadableAsset OptionsScreenshot => MiraAssets.Empty; +} \ No newline at end of file diff --git a/MiraAPI.Example/MiraDebugWindow.cs b/MiraAPI.Example/MiraDebugWindow.cs index 34bbb64..5fa9a08 100644 --- a/MiraAPI.Example/MiraDebugWindow.cs +++ b/MiraAPI.Example/MiraDebugWindow.cs @@ -15,11 +15,11 @@ public class MiraDebugWindow : MonoBehaviour { if (GUILayout.Button("Test modifier")) { - PlayerControl.LocalPlayer.AddModifier(); + PlayerControl.LocalPlayer.AddModifier(); } if (GUILayout.Button("Remove modifier")) { - PlayerControl.LocalPlayer.RemoveModifier(); + PlayerControl.LocalPlayer.RemoveModifier(); } }); diff --git a/MiraAPI.Example/ModifierExample.cs b/MiraAPI.Example/ModifierExample.cs index bb2ecb7..aea6b77 100644 --- a/MiraAPI.Example/ModifierExample.cs +++ b/MiraAPI.Example/ModifierExample.cs @@ -5,7 +5,7 @@ namespace MiraAPI.Example; [RegisterModifier] public class ModifierExample : GameModifier { - public override string ModifierName => "Example"; + public override string ModifierName => "Example Modifier"; public override bool CanVent() { diff --git a/MiraAPI.Example/ModifierTimerExample.cs b/MiraAPI.Example/ModifierTimerExample.cs new file mode 100644 index 0000000..04d810b --- /dev/null +++ b/MiraAPI.Example/ModifierTimerExample.cs @@ -0,0 +1,20 @@ +using MiraAPI.Modifiers.Types; + +namespace MiraAPI.Example; + +[RegisterModifier] +public class ModifierTimerExample : TimedModifier +{ + public override string ModifierName => "Color Changer"; + public override bool HideOnUi => true; + public override float Duration => 10f; + public override bool AutoStart => true; + + private System.Random _rand = new(); + + public override void OnTimerComplete() + { + Player.SetColor((byte)_rand.Next(0, Palette.PlayerColors.Count)); + StartTimer(); + } +} diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index 61f27bb..2e2f107 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -2,11 +2,13 @@ public abstract class BaseModifier { public abstract string ModifierName { get; } + public virtual bool HideOnUi => false; public PlayerControl Player { get; internal set; } public uint ModifierId { get; internal set; } public virtual void OnActivate() { } public virtual void OnDeactivate() { } public virtual void Update() { } + public virtual void FixedUpdate() { } public virtual void OnDeath() { } public virtual bool CanVent() => Player.Data.Role.CanVent; } \ No newline at end of file diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index 32e20ce..f226057 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -6,6 +6,9 @@ using Reactor.Utilities; using Reactor.Utilities.Attributes; using System; using System.Collections.Generic; +using System.Linq; +using System.Text; +using TMPro; using UnityEngine; namespace MiraAPI.Modifiers; @@ -15,12 +18,27 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) { public List ActiveModifiers { get; private set; } - public PlayerControl player; + public PlayerControl Player; + + private TextMeshPro _modifierText; public void Start() { - player = GetComponent(); + Player = GetComponent(); ActiveModifiers = new List(); + + if (!Player.AmOwner) return; + + _modifierText = Helpers.CreateTextLabel("ModifierText", HudManager.Instance.transform, AspectPosition.EdgeAlignments.RightTop, new Vector3(10.1f, 3.5f, 0), textAlignment: TextAlignmentOptions.Right); + _modifierText.verticalAlignment = VerticalAlignmentOptions.Top; + } + + public void FixedUpdate() + { + foreach (var modifier in ActiveModifiers) + { + modifier.FixedUpdate(); + } } public void Update() @@ -29,6 +47,26 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) { modifier.Update(); } + + var filteredModifiers = ActiveModifiers.Where(mod => !mod.HideOnUi); + + if (Player.AmOwner && filteredModifiers.Count() > 0) + { + var stringBuild = new StringBuilder(); + foreach (var mod in filteredModifiers) + { + stringBuild.Append($"\n{mod.ModifierName}"); + if (mod is TimedModifier timer) + { + stringBuild.Append($" ({Math.Round(timer.Duration - timer.TimeRemaining, 0)}s/{timer.Duration}s)"); + } + } + _modifierText.text = $"Modifiers:{stringBuild}"; + } + else if (Player.AmOwner && filteredModifiers.Count() == 0 && _modifierText.text != string.Empty) + { + _modifierText.text = string.Empty; + } } public static void RemoveModifier(PlayerControl target, uint modifierId) @@ -66,6 +104,12 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) return; } + if (target.HasModifier(modifierId)) + { + Logger.Error($"Player already has modifier with id {modifierId}!"); + return; + } + var modifier = (BaseModifier)Activator.CreateInstance(type); var modifierComponent = target.GetModifierComponent(); @@ -77,7 +121,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) } modifierComponent.ActiveModifiers.Add(modifier); - modifier.Player = modifierComponent.player; + modifier.Player = modifierComponent.Player; modifier.ModifierId = modifierId; modifier.OnActivate(); diff --git a/MiraAPI/Modifiers/Types/TimedModifier.cs b/MiraAPI/Modifiers/Types/TimedModifier.cs index 1725ed0..8e8fda3 100644 --- a/MiraAPI/Modifiers/Types/TimedModifier.cs +++ b/MiraAPI/Modifiers/Types/TimedModifier.cs @@ -11,13 +11,13 @@ public abstract class TimedModifier : BaseModifier public bool TimerActive = false; public float TimeRemaining; - public override void Update() + public override void FixedUpdate() { if (!Player.AmOwner) return; if (TimeRemaining > 0 && TimerActive) { - TimeRemaining -= Time.deltaTime; + TimeRemaining -= Time.fixedDeltaTime; } else { diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index 5e922cc..4444b12 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -1,6 +1,7 @@ using MiraAPI.Roles; using System.Linq; using System.Text; +using TMPro; using UnityEngine; namespace MiraAPI.Utilities; @@ -13,6 +14,33 @@ public static class Helpers return ShipStatus.Instance.AllRooms.ToList().Find(room => room.roomArea.OverlapPoint(pos)); } + public static TextMeshPro CreateTextLabel(string name, Transform parent, +AspectPosition.EdgeAlignments alignment, Vector3 distance, float fontSize = 2f, +TextAlignmentOptions textAlignment = TextAlignmentOptions.Center) + { + var textObj = new GameObject(name) + { + transform = + { + parent = parent + }, + layer = LayerMask.NameToLayer("UI") + }; + + var textMeshPro = textObj.AddComponent(); + textMeshPro.fontSize = fontSize; + textMeshPro.alignment = textAlignment; + textMeshPro.font = HudManager.Instance.TaskPanel.taskText.font; + textMeshPro.fontMaterial = HudManager.Instance.TaskPanel.taskText.fontMaterial; + + var aspectPosition = textObj.AddComponent(); + aspectPosition.Alignment = alignment; + aspectPosition.DistanceFromEdge = distance; + aspectPosition.AdjustPosition(); + + return textMeshPro; + } + public static DeadBody GetBodyById(byte id) { return Object.FindObjectsOfType().FirstOrDefault(body => body.ParentId == id); From 39851c6269e9ab297ab98861cc14d780e606833d Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:33:33 -0400 Subject: [PATCH 09/11] hmm --- MiraAPI/Modifiers/ModifierComponent.cs | 18 ++++++++++-------- .../Patches/Options/RoleSettingMenuPatches.cs | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index f226057..34a5cab 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -18,16 +18,16 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) { public List ActiveModifiers { get; private set; } - public PlayerControl Player; + public PlayerControl player; private TextMeshPro _modifierText; public void Start() { - Player = GetComponent(); - ActiveModifiers = new List(); + player = GetComponent(); + ActiveModifiers = []; - if (!Player.AmOwner) return; + if (!player.AmOwner) return; _modifierText = Helpers.CreateTextLabel("ModifierText", HudManager.Instance.transform, AspectPosition.EdgeAlignments.RightTop, new Vector3(10.1f, 3.5f, 0), textAlignment: TextAlignmentOptions.Right); _modifierText.verticalAlignment = VerticalAlignmentOptions.Top; @@ -50,10 +50,12 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) var filteredModifiers = ActiveModifiers.Where(mod => !mod.HideOnUi); - if (Player.AmOwner && filteredModifiers.Count() > 0) + var baseModifiers = filteredModifiers as BaseModifier[] ?? filteredModifiers.ToArray(); + + if (player.AmOwner && baseModifiers.Any()) { var stringBuild = new StringBuilder(); - foreach (var mod in filteredModifiers) + foreach (var mod in baseModifiers) { stringBuild.Append($"\n{mod.ModifierName}"); if (mod is TimedModifier timer) @@ -63,7 +65,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) } _modifierText.text = $"Modifiers:{stringBuild}"; } - else if (Player.AmOwner && filteredModifiers.Count() == 0 && _modifierText.text != string.Empty) + else if (player.AmOwner && !baseModifiers.Any() && _modifierText.text != string.Empty) { _modifierText.text = string.Empty; } @@ -121,7 +123,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) } modifierComponent.ActiveModifiers.Add(modifier); - modifier.Player = modifierComponent.Player; + modifier.Player = modifierComponent.player; modifier.ModifierId = modifierId; modifier.OnActivate(); diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 48ff1dd..9d88cf2 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -162,7 +162,7 @@ public static class RoleSettingMenuPatches { ICustomRole customRole = role as ICustomRole; __instance.roleDescriptionText.text = customRole.RoleLongDescription; - __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, Array.Empty()); + __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, []); __instance.roleScreenshot.sprite = Sprite.Create(role.RoleScreenshot.texture, new Rect(0, 0, 370, 230), Vector2.one / 2, 100); __instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced; __instance.roleHeaderSprite.color = customRole.RoleColor; From 892c5c7c70fc8a5c5604542975f7b85e5f6b5ade Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:46:53 -0400 Subject: [PATCH 10/11] final touches --- MiraAPI/PluginLoading/MiraPluginManager.cs | 2 -- MiraAPI/Roles/ICustomRole.cs | 1 - MiraAPI/Roles/RoleId.cs | 1 - MiraAPI/Utilities/Extensions.cs | 5 ----- 4 files changed, 9 deletions(-) diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index d07558d..6071aeb 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -1,9 +1,7 @@ using BepInEx.Unity.IL2CPP; -using Il2CppInterop.Runtime.Injection; using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; using MiraAPI.Hud; -using MiraAPI.Hud; using MiraAPI.Modifiers; using MiraAPI.Roles; using MiraAPI.Utilities.Colors; diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 6ccf2f1..f6c74e3 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -5,7 +5,6 @@ using MiraAPI.Modifiers; using MiraAPI.Networking; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; -using Reactor.Utilities; using System; using System.Text; using MiraAPI.PluginLoading; diff --git a/MiraAPI/Roles/RoleId.cs b/MiraAPI/Roles/RoleId.cs index d1174f6..a3926db 100644 --- a/MiraAPI/Roles/RoleId.cs +++ b/MiraAPI/Roles/RoleId.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; namespace MiraAPI.Roles; diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 2da44e6..60e4b9d 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -3,15 +3,10 @@ using System; using System.Collections.Generic; using MiraAPI.GameOptions; using MiraAPI.Modifiers; -using MiraAPI.Roles; using Reactor.Utilities; -using Reactor.Utilities.Extensions; -using System.Collections.Generic; using System.Linq; using MiraAPI.Networking; -using Reactor.Utilities; using UnityEngine; -using Object = UnityEngine.Object; namespace MiraAPI.Utilities; From 87a1be741020a4a53a1a5afcd130f27bd67c26ec Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:59:16 -0400 Subject: [PATCH 11/11] modifiers README --- MiraAPI.Example/Buttons/ExampleButton.cs | 5 +++-- MiraAPI.Example/MiraDebugWindow.cs | 1 + .../GameModifierExample.cs} | 9 +++++---- .../{ => Modifiers}/ModifierTimerExample.cs | 5 +++-- MiraAPI/Modifiers/RegisterModifierAttribute.cs | 2 ++ .../Patches/Options/RoleSettingMenuPatches.cs | 2 +- MiraAPI/Utilities/Extensions.cs | 12 ++++++------ README.md | 17 ++++++++++++++++- 8 files changed, 37 insertions(+), 16 deletions(-) rename MiraAPI.Example/{ModifierExample.cs => Modifiers/GameModifierExample.cs} (53%) rename MiraAPI.Example/{ => Modifiers}/ModifierTimerExample.cs (82%) diff --git a/MiraAPI.Example/Buttons/ExampleButton.cs b/MiraAPI.Example/Buttons/ExampleButton.cs index e6dc949..4c9c7d6 100644 --- a/MiraAPI.Example/Buttons/ExampleButton.cs +++ b/MiraAPI.Example/Buttons/ExampleButton.cs @@ -1,4 +1,5 @@ -using MiraAPI.Hud; +using MiraAPI.Example.Modifiers; +using MiraAPI.Hud; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Utilities; @@ -23,6 +24,6 @@ public class ExampleButton : CustomActionButton public override bool Enabled(RoleBehaviour role) { - return PlayerControl.LocalPlayer.HasModifier(); + return PlayerControl.LocalPlayer.HasModifier(); } } \ No newline at end of file diff --git a/MiraAPI.Example/MiraDebugWindow.cs b/MiraAPI.Example/MiraDebugWindow.cs index 5fa9a08..56718e8 100644 --- a/MiraAPI.Example/MiraDebugWindow.cs +++ b/MiraAPI.Example/MiraDebugWindow.cs @@ -2,6 +2,7 @@ using Reactor.Utilities.Attributes; using Reactor.Utilities.ImGui; using System; +using MiraAPI.Example.Modifiers; using UnityEngine; namespace MiraAPI.Example; diff --git a/MiraAPI.Example/ModifierExample.cs b/MiraAPI.Example/Modifiers/GameModifierExample.cs similarity index 53% rename from MiraAPI.Example/ModifierExample.cs rename to MiraAPI.Example/Modifiers/GameModifierExample.cs index aea6b77..2b491ae 100644 --- a/MiraAPI.Example/ModifierExample.cs +++ b/MiraAPI.Example/Modifiers/GameModifierExample.cs @@ -1,11 +1,12 @@ -using MiraAPI.Modifiers.Types; +using MiraAPI.Modifiers; +using MiraAPI.Modifiers.Types; -namespace MiraAPI.Example; +namespace MiraAPI.Example.Modifiers; [RegisterModifier] -public class ModifierExample : GameModifier +public class GameModifierExample : GameModifier { - public override string ModifierName => "Example Modifier"; + public override string ModifierName => "Example Game Modifier"; public override bool CanVent() { diff --git a/MiraAPI.Example/ModifierTimerExample.cs b/MiraAPI.Example/Modifiers/ModifierTimerExample.cs similarity index 82% rename from MiraAPI.Example/ModifierTimerExample.cs rename to MiraAPI.Example/Modifiers/ModifierTimerExample.cs index 04d810b..e4b7832 100644 --- a/MiraAPI.Example/ModifierTimerExample.cs +++ b/MiraAPI.Example/Modifiers/ModifierTimerExample.cs @@ -1,6 +1,7 @@ -using MiraAPI.Modifiers.Types; +using MiraAPI.Modifiers; +using MiraAPI.Modifiers.Types; -namespace MiraAPI.Example; +namespace MiraAPI.Example.Modifiers; [RegisterModifier] public class ModifierTimerExample : TimedModifier diff --git a/MiraAPI/Modifiers/RegisterModifierAttribute.cs b/MiraAPI/Modifiers/RegisterModifierAttribute.cs index 3b25497..4c2fb56 100644 --- a/MiraAPI/Modifiers/RegisterModifierAttribute.cs +++ b/MiraAPI/Modifiers/RegisterModifierAttribute.cs @@ -1,4 +1,6 @@ using System; +namespace MiraAPI.Modifiers; + [AttributeUsage(AttributeTargets.Class)] public class RegisterModifierAttribute : Attribute; \ No newline at end of file diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index b7b46cd..fbcdb37 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -223,7 +223,7 @@ public static class RoleSettingMenuPatches { ICustomRole customRole = role as ICustomRole; __instance.roleDescriptionText.text = customRole.RoleLongDescription; - __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, Array.Empty()); + __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, []); __instance.roleScreenshot.sprite = Sprite.Create(customRole.OptionsScreenshot.LoadAsset().texture, new Rect(0, 0, 370, 230), Vector2.one / 2, 100); __instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced; __instance.roleHeaderSprite.color = customRole.RoleColor; diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 60e4b9d..0dd4231 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -71,14 +71,14 @@ public static class Extensions return ModdedOptionsManager.ModdedOptions.Values.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour.Equals(optionBehaviour)); } - public static ModifierComponent GetModifierComponent(this PlayerControl player) + public static ModifierComponent? GetModifierComponent(this PlayerControl player) { return player.GetComponent(); } public static List Randomize(this List list) { - List randomizedList = new List(); + List randomizedList = []; System.Random rnd = new(); while (list.Count > 0) { @@ -89,13 +89,13 @@ public static class Extensions return randomizedList; } - public static bool HasModifier(this PlayerControl player) where T : BaseModifier + public static bool HasModifier(this PlayerControl? player) where T : BaseModifier { - return player is not null && player.GetModifierComponent() is not null && player.GetModifierComponent().ActiveModifiers.Exists(x => x is T); + return player?.GetModifierComponent() != null && player.GetModifierComponent().ActiveModifiers.Exists(x => x is T); } - public static bool HasModifier(this PlayerControl player, uint id) + public static bool HasModifier(this PlayerControl? player, uint id) { - return player is not null && player.GetModifierComponent() is not null && player.GetModifierComponent().ActiveModifiers.Exists(x => x.ModifierId == id); + return player?.GetModifierComponent() != null && player.GetModifierComponent().ActiveModifiers.Exists(x => x.ModifierId == id); } public static void AddModifier(this PlayerControl player) where T : BaseModifier diff --git a/README.md b/README.md index d57449c..c6eaba9 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ A thorough, but simple, Among Us modding API and utility library that covers: - Roles - Options +- Modifiers - Buttons - Assets - Compatibility -- ~~Modifiers~~ (coming soon) - ~~Game Modes~~ (coming soon) Mira API strives to be simple and easy to use, while also using as many base game elements as possible. The result is a less intrusive, better modding API that covers general use cases. @@ -62,6 +62,21 @@ Mira API handles everything else, from adding the proper options to the settings See [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Roles/CustomRole.cs) for a code example. +## Modifiers +Mira API uses a different definition of 'modifiers' than other Among Us mods. For example, in Town Of Us, a modifier is an extra "ability" that is applied on top of the base role. However, in Mira API, modifiers are very flexible. A modifier is anything that "modifiers" a player's abilities or interactions. + +Mira provides 3 classes for working with modifiers: +- `BaseModifier`: The base for every modifier. You MUST add and remove this modifier from a player manually! +- `TimedModifier`: A modifier that has a time limit. This modifier has to be added manually, but will automatically remove itself after the time limit +- `GameModifier`: This works like the typical TOU modifier, where it is automatically applied at the beginning of the game, then removed at the end. + +Modifiers provide various overridable functions and properties for custom behaviour. They can also be used for "tagging" a player. You can check if a player has a modifier through the extension method `HasModifier` on a `PlayerControl` object. + +To start using a modifier, pick one of the base classes above and create a class that inherits from it. Implement the properties and methods you would like, then add the `[RegisterModifier]` attribute to the class. + +An example Game modifier can be found [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Modifiers/ExampleGameModifier.cs). +An example Timer modifier can be found [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Modifiers/ModifierTimerExample.cs). + ## Options Options are also very simple in Mira API. Mira API handles all the hard work behind the scenes, so developers only have to follow a few steps to create their custom options. The Options API is split up into Groups and Options. Every Option needs to be in a Group.