From 4155b10ac63e7a78ac6eb03f74561d90955886a5 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Wed, 15 May 2024 20:34:53 -0400 Subject: [PATCH 1/4] WIP --- MiraAPI.Example/ExOptions.cs | 9 + MiraAPI.Example/ExampleOptions.cs | 16 -- MiraAPI/GameOptions/AbstractGameOption.cs | 39 ---- .../RegisterCustomOptionAttribute.cs | 18 -- .../RegisterCustomOptionsAttribute.cs | 43 ----- .../RegisterModdedOptionAttribute.cs | 41 ++++ MiraAPI/GameOptions/CustomNumberOption.cs | 100 ---------- MiraAPI/GameOptions/CustomOptionGroup.cs | 46 ----- MiraAPI/GameOptions/CustomOptionsManager.cs | 159 ---------------- MiraAPI/GameOptions/CustomOptionsTab.cs | 176 ------------------ MiraAPI/GameOptions/CustomStringOption.cs | 92 --------- MiraAPI/GameOptions/CustomToggleOption.cs | 78 -------- MiraAPI/GameOptions/IModOptions.cs | 10 - MiraAPI/GameOptions/IModdedOption.cs | 7 + MiraAPI/GameOptions/ModdedOptionsManager.cs | 73 ++++++++ .../GameOptions/OptionTypes/ModdedOption.cs | 47 +++++ .../OptionTypes/ModdedToggleOption.cs | 14 ++ MiraAPI/MiraAPI.csproj | 6 +- MiraAPI/MiraAPIPlugin.cs | 2 +- MiraAPI/Networking/SyncOptionsRpc.cs | 3 +- MiraAPI/Patches/HudManagerPatches.cs | 59 ------ .../Patches/Options/GameOptionsMenuPatch.cs | 109 ----------- .../Options/GameSettingsMenuPatches.cs | 175 ----------------- MiraAPI/Patches/Options/ToHudStringPatch.cs | 147 --------------- .../Patches/Roles/RolesSettingsMenuPatches.cs | 52 +++--- 25 files changed, 223 insertions(+), 1298 deletions(-) create mode 100644 MiraAPI.Example/ExOptions.cs delete mode 100644 MiraAPI.Example/ExampleOptions.cs delete mode 100644 MiraAPI/GameOptions/AbstractGameOption.cs delete mode 100644 MiraAPI/GameOptions/Attributes/RegisterCustomOptionAttribute.cs delete mode 100644 MiraAPI/GameOptions/Attributes/RegisterCustomOptionsAttribute.cs create mode 100644 MiraAPI/GameOptions/Attributes/RegisterModdedOptionAttribute.cs delete mode 100644 MiraAPI/GameOptions/CustomNumberOption.cs delete mode 100644 MiraAPI/GameOptions/CustomOptionGroup.cs delete mode 100644 MiraAPI/GameOptions/CustomOptionsManager.cs delete mode 100644 MiraAPI/GameOptions/CustomOptionsTab.cs delete mode 100644 MiraAPI/GameOptions/CustomStringOption.cs delete mode 100644 MiraAPI/GameOptions/CustomToggleOption.cs delete mode 100644 MiraAPI/GameOptions/IModOptions.cs create mode 100644 MiraAPI/GameOptions/IModdedOption.cs create mode 100644 MiraAPI/GameOptions/ModdedOptionsManager.cs create mode 100644 MiraAPI/GameOptions/OptionTypes/ModdedOption.cs create mode 100644 MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs delete mode 100644 MiraAPI/Patches/Options/GameOptionsMenuPatch.cs delete mode 100644 MiraAPI/Patches/Options/GameSettingsMenuPatches.cs delete mode 100644 MiraAPI/Patches/Options/ToHudStringPatch.cs diff --git a/MiraAPI.Example/ExOptions.cs b/MiraAPI.Example/ExOptions.cs new file mode 100644 index 0000000..487ac02 --- /dev/null +++ b/MiraAPI.Example/ExOptions.cs @@ -0,0 +1,9 @@ +using MiraAPI.GameOptions.Attributes; + +namespace MiraAPI.Example +{ + public class ExOptions + { + [RegisterModdedOption("Use Thing")] public bool useThing = false; + } +} diff --git a/MiraAPI.Example/ExampleOptions.cs b/MiraAPI.Example/ExampleOptions.cs deleted file mode 100644 index 1f8af5d..0000000 --- a/MiraAPI.Example/ExampleOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using MiraAPI.API.GameOptions; -using MiraAPI.GameOptions; -using MiraAPI.GameOptions.Attributes; -using MiraAPI.Utilities; -using MiraAPI.Utilities.Assets; -using UnityEngine; - -namespace MiraAPI.Example -{ - [RegisterCustomOptions(ExamplePlugin.Id)] - public class ExampleOptions : IModOptions - { - public LoadableAsset OptionsTabIcon => MiraAssets.Empty; - public static CustomToggleOption YeOrNa = new CustomToggleOption("Ye or Na", true); - } -} diff --git a/MiraAPI/GameOptions/AbstractGameOption.cs b/MiraAPI/GameOptions/AbstractGameOption.cs deleted file mode 100644 index 94ac265..0000000 --- a/MiraAPI/GameOptions/AbstractGameOption.cs +++ /dev/null @@ -1,39 +0,0 @@ -using BepInEx; -using MiraAPI.Roles; -using Reactor.Localization.Utilities; -using System; - -namespace MiraAPI.API.GameOptions; - -public abstract class AbstractGameOption -{ - public string Title { get; } - public StringNames StringName { get; } - public Type AdvancedRole { get; } - public bool Save { get; } - public bool ShowInHideNSeek { get; init; } - public CustomOptionGroup Group { get; set; } - public Func Hidden { get; set; } - public OptionBehaviour OptionBehaviour { get; protected set; } - public PluginInfo ParentMod { get; set; } - public void ValueChanged(OptionBehaviour optionBehaviour) - { - OnValueChanged(optionBehaviour); - CustomOptionsManager.SyncOptions(); - } - - protected abstract void OnValueChanged(OptionBehaviour optionBehaviour); - - protected AbstractGameOption(string title, Type roleType, bool save) - { - Title = title; - StringName = CustomStringName.CreateAndRegister(Title); - if (roleType is not null && roleType.IsAssignableTo(typeof(ICustomRole))) - { - AdvancedRole = roleType; - } - - Save = save; - Hidden = () => false; - } -} \ No newline at end of file diff --git a/MiraAPI/GameOptions/Attributes/RegisterCustomOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/RegisterCustomOptionAttribute.cs deleted file mode 100644 index 8044d55..0000000 --- a/MiraAPI/GameOptions/Attributes/RegisterCustomOptionAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace MiraAPI.GameOptions.Attributes -{ - [AttributeUsage(AttributeTargets.Field)] - public class RegisterCustomOptionAttribute : Attribute - { - private static readonly HashSet RegisteredAssemblies = []; - public string ModId { get; } - - public RegisterCustomOptionAttribute(string modId) - { - ModId = modId; - } - } -} diff --git a/MiraAPI/GameOptions/Attributes/RegisterCustomOptionsAttribute.cs b/MiraAPI/GameOptions/Attributes/RegisterCustomOptionsAttribute.cs deleted file mode 100644 index ea07a08..0000000 --- a/MiraAPI/GameOptions/Attributes/RegisterCustomOptionsAttribute.cs +++ /dev/null @@ -1,43 +0,0 @@ -using BepInEx.Unity.IL2CPP; -using MiraAPI.API.GameOptions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace MiraAPI.GameOptions.Attributes -{ - [AttributeUsage(AttributeTargets.Class)] - public class RegisterCustomOptionsAttribute : Attribute - { - private static readonly HashSet RegisteredAssemblies = []; - public string ModId { get; } - public List Options; - - public RegisterCustomOptionsAttribute(string modId) - { - ModId = modId; - } - - public static void Initialize() - { - IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) => Register(assembly); - } - - public static void Register(Assembly assembly) - { - if (!RegisteredAssemblies.Add(assembly)) return; - - foreach (var type in assembly.GetTypes()) - { - var attribute = type.GetCustomAttribute(); - if (attribute != null) - { - attribute.Options = type.GetFields(BindingFlags.Static | BindingFlags.Public) - .Where(fi => fi.FieldType.BaseType == typeof(AbstractGameOption) || fi.FieldType.BaseType == typeof(CustomOptionGroup)).ToList(); - CustomOptionsManager.RegisterOptionsForMod(type, attribute.Options, attribute.ModId); - } - } - } - } -} diff --git a/MiraAPI/GameOptions/Attributes/RegisterModdedOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/RegisterModdedOptionAttribute.cs new file mode 100644 index 0000000..b2e7b79 --- /dev/null +++ b/MiraAPI/GameOptions/Attributes/RegisterModdedOptionAttribute.cs @@ -0,0 +1,41 @@ +using BepInEx.Unity.IL2CPP; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace MiraAPI.GameOptions.Attributes +{ + [AttributeUsage(AttributeTargets.Property)] + public class RegisterModdedOptionAttribute : Attribute + { + private static readonly HashSet RegisteredAssemblies = []; + + public string Title { get; private set; } + public RegisterModdedOptionAttribute(string title) + { + Title = title; + } + + public static void Initialize() + { + IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) => Register(assembly); + } + + public static void Register(Assembly assembly) + { + if (!RegisteredAssemblies.Add(assembly)) return; + + foreach (var type in assembly.GetTypes()) + { + foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public)) + { + var attribute = property.GetCustomAttribute(); + if (attribute != null) + { + ModdedOptionsManager.Instance.CreateOption(type, attribute, property); + } + } + } + } + } +} diff --git a/MiraAPI/GameOptions/CustomNumberOption.cs b/MiraAPI/GameOptions/CustomNumberOption.cs deleted file mode 100644 index 131d569..0000000 --- a/MiraAPI/GameOptions/CustomNumberOption.cs +++ /dev/null @@ -1,100 +0,0 @@ -using BepInEx.Configuration; -using Reactor.Utilities; -using System; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace MiraAPI.API.GameOptions; - -public class CustomNumberOption : AbstractGameOption -{ - public float Value { get; private set; } - public float Min { get; } - public float Max { get; } - public float Increment { get; } - public NumberSuffixes SuffixType { get; } - public string NumberFormat { get; } - public float Default { get; } - public bool ZeroInfinity { get; } - public ConfigEntry Config { get; } - public Action ChangedEvent { get; set; } - - public CustomNumberOption(string title, float defaultValue, float min, float max, float increment, NumberSuffixes suffixType, bool zeroInfinity = false, string numberFormat = "0", Type role = null, bool save = true) : base(title, role, save) - { - Value = defaultValue; - Default = defaultValue; - Min = min; - Max = max; - Increment = increment; - SuffixType = suffixType; - NumberFormat = numberFormat; - ZeroInfinity = zeroInfinity; - - if (Save) - { - try - { - Config = MiraAPIPlugin.Instance.Config.Bind("Number Options", title, defaultValue); - SetValue(Save ? Config.Value : defaultValue); - } - catch (Exception e) - { - Logger.Warning(e.ToString()); - } - } - } - - public void SetValue(float newValue) - { - newValue = Mathf.Clamp(newValue, Min, Max); - - if (Save && (!AmongUsClient.Instance || AmongUsClient.Instance.AmHost)) - { - try - { - Config.Value = newValue; - - } - catch (Exception e) - { - Logger.Warning(e.ToString()); - } - } - - Value = newValue; - - var behaviour = (NumberOption)OptionBehaviour; - if (behaviour) - { - behaviour.Value = Value; - } - - ChangedEvent?.Invoke(Value); - } - - public NumberOption CreateNumberOption(NumberOption original, Transform container) - { - var numberOption = Object.Instantiate(original, container); - - numberOption.name = Title; - numberOption.Title = StringName; - numberOption.Value = Value; - numberOption.Increment = Increment; - numberOption.SuffixType = SuffixType; - numberOption.FormatString = NumberFormat; - numberOption.ValidRange = new FloatRange(Min, Max); - numberOption.ZeroIsInfinity = ZeroInfinity; - numberOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged; - numberOption.OnEnable(); - - OptionBehaviour = numberOption; - - return numberOption; - } - - protected override void OnValueChanged(OptionBehaviour optionBehaviour) - { - SetValue(optionBehaviour.GetFloat()); - } - -} \ No newline at end of file diff --git a/MiraAPI/GameOptions/CustomOptionGroup.cs b/MiraAPI/GameOptions/CustomOptionGroup.cs deleted file mode 100644 index 70fba6a..0000000 --- a/MiraAPI/GameOptions/CustomOptionGroup.cs +++ /dev/null @@ -1,46 +0,0 @@ -using BepInEx; -using MiraAPI.Roles; -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace MiraAPI.API.GameOptions; -public class CustomOptionGroup -{ - public string Title { get; } - public Func Hidden { get; set; } - - public GameObject Header; - public Type AdvancedRole { get; set; } - public PluginInfo ParentMod { get; set; } - - public readonly List Options = []; - public readonly List CustomNumberOptions; - public readonly List CustomToggleOptions; - public readonly List CustomStringOptions; - public CustomOptionGroup(string title, List numberOpt, - List toggleOpt, List stringOpt, Type role = null) - { - Title = title; - - if (role is not null && role.IsAssignableTo(typeof(ICustomRole))) - { - AdvancedRole = role; - } - - - Hidden = () => false; - CustomNumberOptions = numberOpt; - CustomToggleOptions = toggleOpt; - CustomStringOptions = stringOpt; - - Options.AddRange(CustomNumberOptions); - Options.AddRange(CustomToggleOptions); - Options.AddRange(CustomStringOptions); - - foreach (var option in Options) - { - option.Group = this; - } - } -} \ No newline at end of file diff --git a/MiraAPI/GameOptions/CustomOptionsManager.cs b/MiraAPI/GameOptions/CustomOptionsManager.cs deleted file mode 100644 index 072138a..0000000 --- a/MiraAPI/GameOptions/CustomOptionsManager.cs +++ /dev/null @@ -1,159 +0,0 @@ -using BepInEx; -using BepInEx.Unity.IL2CPP; -using MiraAPI.GameOptions; -using MiraAPI.Networking.Options; -using Reactor.Networking.Rpc; -using Reactor.Utilities; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using UnityEngine; - -namespace MiraAPI.API.GameOptions; - -public static class CustomOptionsManager -{ - public static readonly List CustomOptions = []; - public static readonly List CustomNumberOptions = []; - public static readonly List CustomToggleOptions = []; - public static readonly List CustomStringOptions = []; - public static readonly List CustomGroups = []; - public static readonly Dictionary RegisteredMods = []; - - public static void RegisterOptionsForMod(IModOptions type, List fields, string modId) - { - PluginInfo info = IL2CPPChainloader.Instance.Plugins[modId]; - - if (info == null) - { - return; - } - - Debug.Log($"Registering Options for {modId}"); - - RegisteredMods.Add(info, type); - - foreach (FieldInfo field in fields) - { - AbstractGameOption option = (AbstractGameOption)field.GetValue(null); - if (option == null) - { - CustomOptionGroup group = (CustomOptionGroup)field.GetValue(null); - group.ParentMod = info; - CustomGroups.Add(group); - } - - option.ParentMod = info; - - CustomOptions.Add(option); - switch (field.FieldType.Name) - { - case nameof(CustomNumberOption): - CustomNumberOptions.Add((CustomNumberOption)option); - break; - - case nameof(CustomToggleOption): - CustomToggleOptions.Add((CustomToggleOption)option); - break; - - case nameof(CustomStringOption): - CustomStringOptions.Add((CustomStringOption)option); - break; - - } - } - } - - public static void SyncOptions() - { - if (!AmongUsClient.Instance.AmHost) - { - return; - } - - var toggles = CustomToggleOptions.Select(x => x.Value).ToArray(); - var numbers = CustomNumberOptions.Select(x => x.Value).ToArray(); - var strings = CustomStringOptions.Select(x => x.IndexValue).ToArray(); - - Rpc.Instance.Send(new SyncOptionsRpc.Data(toggles, numbers, strings)); - } - - public static void SyncOptions(int targetId) - { - if (!AmongUsClient.Instance.AmHost) - { - return; - } - - var toggles = CustomToggleOptions.Select(x => x.Value).ToArray(); - var numbers = CustomNumberOptions.Select(x => x.Value).ToArray(); - var strings = CustomStringOptions.Select(x => x.IndexValue).ToArray(); - - Rpc.Instance.SendTo(targetId, new SyncOptionsRpc.Data(toggles, numbers, strings)); - } - - public static void UpdateToConfig() - { - foreach (var numberOpt in CustomNumberOptions) - { - numberOpt.SetValue(numberOpt.Save ? numberOpt.Config.Value : numberOpt.Default); - } - - foreach (var toggleOpt in CustomToggleOptions) - { - toggleOpt.SetValue(toggleOpt.Save ? toggleOpt.Config.Value : toggleOpt.Default); - } - - foreach (var stringOpt in CustomStringOptions) - { - stringOpt.SetValue(stringOpt.Save ? stringOpt.Config.Value : stringOpt.Default); - } - } - - - public static void ResetToDefault() - { - foreach (var numberOpt in CustomNumberOptions) - { - numberOpt.SetValue(numberOpt.Default); - } - - foreach (var stringOpt in CustomStringOptions) - { - stringOpt.SetValue(stringOpt.Default); - } - - foreach (var toggleOpt in CustomToggleOptions) - { - toggleOpt.SetValue(toggleOpt.Default); - } - - foreach (var option in CustomOptions) - { - if (!option.OptionBehaviour) - { - continue; - } - - option.ValueChanged(option.OptionBehaviour); - } - } - - public static void HandleOptionsSync(bool[] toggles, float[] numbers, int[] strings) - { - Logger.Warning("syncing options!"); - for (var i = 0; i < toggles.Length; i++) - { - CustomToggleOptions[i].SetValue(toggles[i]); - } - for (var i = 0; i < numbers.Length; i++) - { - CustomNumberOptions[i].SetValue(numbers[i]); - } - for (var i = 0; i < strings.Length; i++) - { - CustomStringOptions[i].SetValue(strings[i]); - } - } - -} \ No newline at end of file diff --git a/MiraAPI/GameOptions/CustomOptionsTab.cs b/MiraAPI/GameOptions/CustomOptionsTab.cs deleted file mode 100644 index c1c8e31..0000000 --- a/MiraAPI/GameOptions/CustomOptionsTab.cs +++ /dev/null @@ -1,176 +0,0 @@ -using MiraAPI.GameOptions; -using Reactor.Utilities.Extensions; -using System; -using System.Collections.Generic; -using TMPro; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace MiraAPI.API.GameOptions; -public static class CustomOptionsTab -{ - public static List CustomTabs; - public static List CustomScreens; - - public static GameObject InitializeForMod(IModOptions modOptions, GameSettingMenu __instance) - { - var gameBtn = __instance.transform.FindChild("Header/Tabs/GameTab").gameObject; - var roleBtn = __instance.transform.FindChild("Header/Tabs/RoleTab").gameObject; - var screen = CreateNewMenu(__instance); - var tab = CreateCustomTab(modOptions, __instance, screen, gameBtn, roleBtn); - - CustomScreens.Add(screen); - CustomTabs.Add(tab); - - SpriteRenderer rend = tab.transform.FindChild("Btn/Tab Background").GetComponent(); - rend.enabled = false; - - UpdateListeners(__instance, gameBtn.GetComponentInChildren(), roleBtn.GetComponentInChildren(), rend); - - var container = screen.transform.FindChild("GameGroup/SliderInner"); - container.DestroyChildren(); - //CreateNewResetButton(__instance, container); - - return container.gameObject; - } - - - /*private static void CreateNewResetButton(GameSettingMenu __instance, Transform container) - { - var resetBtn = __instance.RegularGameSettings.transform.FindChild("GameGroup/SliderInner/ResetToDefault"); - var newResetBtn = Object.Instantiate(resetBtn.gameObject, container); - newResetBtn.gameObject.name = "LaunchpadReset"; - - var tmp = newResetBtn.GetComponentInChildren(); - tmp.text = "Reset Options"; - tmp.alignment = TextAlignmentOptions.Center; - tmp.gameObject.transform.localPosition = new Vector3(0, 0, 0); - - newResetBtn.GetComponent().Destroy(); - newResetBtn.transform.FindChild("CheckBox").gameObject.Destroy(); - - var toggle = newResetBtn.GetComponent(); - - toggle.OnClick.RemoveAllListeners(); - toggle.OnMouseOver.RemoveAllListeners(); - toggle.OnMouseOut.RemoveAllListeners(); - toggle.OnClick.AddListener((UnityAction)(() => { CustomOptionsManager.ResetToDefault(); })); - toggle.OnMouseOver.AddListener((UnityAction)(() => { tmp.text = "Reset Options"; })); - toggle.OnMouseOut.AddListener((UnityAction)(() => { tmp.text = "Reset Options"; })); - }*/ - - private static void UpdateListeners(GameSettingMenu __instance, PassiveButton gameB, PassiveButton roleB, SpriteRenderer rend) - { - gameB.OnClick.RemoveAllListeners(); - /* gameB.OnClick.AddListener((UnityAction)(() => - { - __instance.RegularGameSettings.SetActive(true); - __instance.RolesSettings.gameObject.SetActive(false); - - CustomScreen.gameObject.SetActive(false); - rend.enabled = false; - - __instance.GameSettingsHightlight.enabled = true; - __instance.RolesSettingsHightlight.enabled = false; - })); - - roleB.OnClick.RemoveAllListeners(); - roleB.OnClick.AddListener((UnityAction)(() => - { - __instance.RegularGameSettings.SetActive(false); - __instance.RolesSettings.gameObject.SetActive(true); - - CustomScreen.gameObject.SetActive(false); - rend.enabled = false; - - __instance.GameSettingsHightlight.enabled = false; - __instance.RolesSettingsHightlight.enabled = true; - }));*/ - } - - public static GameObject CreateHeader(ToggleOption toggleOpt, Transform container, string title) - { - var header = Object.Instantiate(toggleOpt, container); - - header.Title = StringNames.None; - header.TitleText.text = title; - header.name = "Header"; - - var checkBox = header.transform.FindChild("CheckBox")?.gameObject; - if (checkBox) - { - checkBox.Destroy(); - } - - var background = header.transform.FindChild("Background")?.gameObject; - if (background) - { - background.Destroy(); - } - - header.GetComponent().Destroy(); - return header.gameObject; - } - - public static GameObject CreateCustomTab(IModOptions options, GameSettingMenu __instance, GameObject newSettings, - GameObject gameTab, GameObject roleTab) - { - var newTab = Object.Instantiate(gameTab, gameTab.transform.parent); - newTab.name = "Tab"; - gameTab.transform.position += new Vector3(-1, 0, 0); - - var inside = newTab.transform.FindChild("ColorButton"); - inside.name = "Btn"; - - var btn = inside.GetComponentInChildren(); - - btn.OnClick.RemoveAllListeners(); - - btn.OnClick.AddListener((Action)TabAction); - - var spriteRend = inside.GetComponentInChildren(); - spriteRend.sprite = options.OptionsTabIcon.LoadAsset(); - spriteRend.gameObject.transform.localScale = new Vector3(0.4f, 0.4f, 0.4f); - - return newTab; - - void TabAction() - { - __instance.RegularGameSettings.SetActive(false); - __instance.RolesSettings.gameObject.SetActive(false); - - foreach (var screen in CustomScreens) - { - screen.gameObject.SetActive(false); - } - - newSettings.gameObject.SetActive(true); - - var rend = inside.transform.FindChild("Tab Background").GetComponent(); - rend.enabled = true; - - gameTab.transform.FindChild("ColorButton/Tab Background").gameObject.GetComponent().enabled = false; - roleTab.transform.FindChild("Hat Button/Tab Background").gameObject.GetComponent().enabled = false; - - foreach (var tab in CustomTabs) - { - tab.transform.FindChild("Btn/Tab Background").gameObject.GetComponent().enabled = false; - } - } - } - - public static GameObject CreateNewMenu(GameSettingMenu __instance) - { - var gameSettings = __instance.RegularGameSettings; - var newSettings = Object.Instantiate(gameSettings, gameSettings.transform.parent); - newSettings.name = "Settings"; - newSettings.SetActive(false); - - var launchpadGroup = newSettings.transform.FindChild("GameGroup").gameObject; - var text = launchpadGroup.transform.FindChild("Text").gameObject.GetComponent(); - text.gameObject.GetComponent().Destroy(); - text.text = "Settings"; - - return newSettings; - } -} \ No newline at end of file diff --git a/MiraAPI/GameOptions/CustomStringOption.cs b/MiraAPI/GameOptions/CustomStringOption.cs deleted file mode 100644 index 48d6d53..0000000 --- a/MiraAPI/GameOptions/CustomStringOption.cs +++ /dev/null @@ -1,92 +0,0 @@ -using BepInEx.Configuration; -using Reactor.Localization.Utilities; -using Reactor.Utilities; -using System; -using System.Linq; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace MiraAPI.API.GameOptions; - -public class CustomStringOption : AbstractGameOption -{ - public int IndexValue { get; private set; } - public string Value => Options[IndexValue]; - public int Default { get; } - public string[] Options { get; private set; } - public ConfigEntry Config { get; } - public Action ChangedEvent { get; init; } - public CustomStringOption(string title, int defaultValue, string[] options, Type role = null, bool save = true) : base(title, role, save) - { - IndexValue = defaultValue; - Options = options; - Default = defaultValue; - - if (Save) - { - try - { - Config = MiraAPIPlugin.Instance.Config.Bind("String Options", title, defaultValue); - SetValue(Save ? Config.Value : defaultValue); - } - catch (Exception e) - { - Logger.Warning(e.ToString()); - } - } - - } - - public void SetValue(int newValue) - { - if (Save && (!AmongUsClient.Instance || AmongUsClient.Instance.AmHost)) - { - try - { - Config.Value = newValue; - - } - catch (Exception e) - { - Logger.Warning(e.ToString()); - } - } - - var oldValue = IndexValue; - IndexValue = newValue; - - var behaviour = (StringOption)OptionBehaviour; - if (behaviour) - { - behaviour.Value = newValue; - } - - if (oldValue != newValue) - { - ChangedEvent?.Invoke(newValue, Value); - } - } - - public void SetValue(string newValue) => SetValue(Options.ToList().IndexOf(newValue)); - - protected override void OnValueChanged(OptionBehaviour optionBehaviour) - { - SetValue(optionBehaviour.GetInt()); - } - - public StringOption CreateStringOption(StringOption original, Transform container) - { - var stringOption = Object.Instantiate(original, container); - - stringOption.name = Title; - stringOption.Title = StringName; - stringOption.Value = Options.ToList().IndexOf(Value); - stringOption.Values = Options.Select(CustomStringName.CreateAndRegister).ToArray(); - stringOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged; - stringOption.OnEnable(); - - OptionBehaviour = stringOption; - - return stringOption; - } -} \ No newline at end of file diff --git a/MiraAPI/GameOptions/CustomToggleOption.cs b/MiraAPI/GameOptions/CustomToggleOption.cs deleted file mode 100644 index 68e8031..0000000 --- a/MiraAPI/GameOptions/CustomToggleOption.cs +++ /dev/null @@ -1,78 +0,0 @@ -using BepInEx.Configuration; -using Reactor.Utilities; -using System; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace MiraAPI.API.GameOptions; - -public class CustomToggleOption : AbstractGameOption -{ - public bool Value { get; private set; } - public bool Default { get; } - public ConfigEntry Config { get; } - public Action ChangedEvent { get; init; } - public CustomToggleOption(string title, bool defaultValue, Type role = null, bool save = true) : base(title, role, save) - { - Default = defaultValue; - if (Save) - { - try - { - Config = MiraAPIPlugin.Instance.Config.Bind("Toggle Options", title, defaultValue); - SetValue(Save ? Config.Value : defaultValue); - } - catch (Exception e) - { - Logger.Warning(e.ToString()); - } - } - } - - public void SetValue(bool newValue) - { - if (Save && (!AmongUsClient.Instance || AmongUsClient.Instance.AmHost)) - { - try - { - Config.Value = newValue; - } - catch (Exception e) - { - Logger.Warning(e.ToString()); - } - } - - var oldValue = Value; - Value = newValue; - - var behaviour = (ToggleOption)OptionBehaviour; - if (behaviour) - { - behaviour.CheckMark.enabled = newValue; - } - - if (newValue != oldValue) - { - ChangedEvent?.Invoke(newValue); - } - } - - protected override void OnValueChanged(OptionBehaviour optionBehaviour) - { - SetValue(optionBehaviour.GetBool()); - } - - public ToggleOption CreateToggleOption(ToggleOption original, Transform container) - { - var toggleOption = Object.Instantiate(original, container); - - toggleOption.name = Title; - toggleOption.Title = StringName; - toggleOption.CheckMark.enabled = Value; - toggleOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged; - toggleOption.OnEnable(); - OptionBehaviour = toggleOption; - return toggleOption; - } -} \ No newline at end of file diff --git a/MiraAPI/GameOptions/IModOptions.cs b/MiraAPI/GameOptions/IModOptions.cs deleted file mode 100644 index 807e5af..0000000 --- a/MiraAPI/GameOptions/IModOptions.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MiraAPI.Utilities; -using UnityEngine; - -namespace MiraAPI.GameOptions -{ - public interface IModOptions - { - LoadableAsset OptionsTabIcon { get; } - } -} diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs new file mode 100644 index 0000000..205a142 --- /dev/null +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -0,0 +1,7 @@ +namespace MiraAPI.GameOptions +{ + public interface IModdedOption + { + + } +} diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs new file mode 100644 index 0000000..16637cd --- /dev/null +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -0,0 +1,73 @@ +using HarmonyLib; +using MiraAPI.GameOptions.Attributes; +using MiraAPI.GameOptions.OptionTypes; +using Reactor.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; + +namespace MiraAPI.GameOptions +{ + public class ModdedOptionsManager + { + public static ModdedOptionsManager Instance; + public List Options = new List(); + + private Dictionary PatchedOptions = new Dictionary(); + + public void CreateOption(Type type, RegisterModdedOptionAttribute attribute, PropertyInfo property) + { + Debug.LogError(property.GetConstantValue().GetType().ToString()); + + switch (property.GetConstantValue()) + { + case bool: + CreateToggleOption(type, attribute, property); + break; + default: + Debug.LogError($"Mira API does not support type {property.GetConstantValue().GetType()}"); + break; + } + } + + public static void PropertySetterPatch(MethodBase __originalMethod, object[] args) + { + IModdedOption opt = Instance.PatchedOptions.First(pair => pair.Key.GetSetMethod().Equals(__originalMethod)).Value; + + if (opt != null) + { + object value = args[0]; + switch (value) + { + case bool: + ModdedToggleOption toggleOpt = opt as ModdedToggleOption; + toggleOpt.SetValue((bool)value); + break; + } + } + } + + public ModdedToggleOption CreateToggleOption(Type type, RegisterModdedOptionAttribute attribute, PropertyInfo property) + { + var toggleOpt = new ModdedToggleOption(attribute.Title, (bool)property.GetValue(type)); + Options.Add(toggleOpt); + + var setterOriginal = property.GetSetMethod(); + var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch)); + PluginSingleton.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch)); + /* + var getterOriginal = property.GetGetMethod(); + var getterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertyGetterPatch)); + PluginSingleton.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch));*/ + + toggleOpt.PropertyInfo = property; + + if (!PatchedOptions.ContainsKey(property)) + PatchedOptions.Add(property, toggleOpt); + + return toggleOpt; + } + } +} diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs new file mode 100644 index 0000000..726290d --- /dev/null +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -0,0 +1,47 @@ +using Reactor.Localization.Utilities; +using System; +using System.Reflection; + +namespace MiraAPI.GameOptions.OptionTypes +{ + public abstract class ModdedOption : IModdedOption + { + public T Value { get; set; } + public T DefaultValue { get; set; } + public Action ChangedEvent { get; private set; } + + public PropertyInfo PropertyInfo; + public string Title { get; } + public StringNames StringName { get; } + public Func Hidden { get; set; } + public OptionBehaviour OptionBehaviour { get; protected set; } + + public ModdedOption(string title, T defaultValue) + { + Title = title; + DefaultValue = defaultValue; + Value = defaultValue; + StringName = CustomStringName.CreateAndRegister(Title); + Hidden = () => false; + } + + public void ValueChanged(OptionBehaviour optionBehaviour) + { + SetValue(GetValueFromOptionBehaviour(optionBehaviour)); + } + + public void SetValue(T newValue) + { + T oldVal = Value; + Value = newValue; + if (!Value.Equals(oldVal) && ChangedEvent != null) + { + ChangedEvent.Invoke(Value); + } + + PropertyInfo.SetValue(null, newValue); + } + + public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour); + } +} diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs new file mode 100644 index 0000000..04cad16 --- /dev/null +++ b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs @@ -0,0 +1,14 @@ +namespace MiraAPI.GameOptions.OptionTypes +{ + public class ModdedToggleOption : ModdedOption + { + public ModdedToggleOption(string title, bool defaultValue = false) : base(title, defaultValue) + { + } + + public override bool GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour) + { + return optionBehaviour.GetBool(); + } + } +} diff --git a/MiraAPI/MiraAPI.csproj b/MiraAPI/MiraAPI.csproj index bea5ef1..70c9115 100644 --- a/MiraAPI/MiraAPI.csproj +++ b/MiraAPI/MiraAPI.csproj @@ -1,4 +1,4 @@ - + net6.0 latest @@ -26,6 +26,10 @@ compile; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/MiraAPI/MiraAPIPlugin.cs b/MiraAPI/MiraAPIPlugin.cs index b352a9e..4cc3e10 100644 --- a/MiraAPI/MiraAPIPlugin.cs +++ b/MiraAPI/MiraAPIPlugin.cs @@ -27,7 +27,7 @@ public partial class MiraAPIPlugin : BasePlugin Harmony.PatchAll(); RegisterCustomRoleAttribute.Initialize(); - RegisterCustomOptionsAttribute.Initialize(); + RegisterModdedOptionAttribute.Initialize(); RegisterGameModeAttribute.Register(Assembly.GetCallingAssembly()); RegisterButtonAttribute.Register(Assembly.GetExecutingAssembly()); diff --git a/MiraAPI/Networking/SyncOptionsRpc.cs b/MiraAPI/Networking/SyncOptionsRpc.cs index d8a10c1..1af1837 100644 --- a/MiraAPI/Networking/SyncOptionsRpc.cs +++ b/MiraAPI/Networking/SyncOptionsRpc.cs @@ -1,5 +1,4 @@ using Hazel; -using MiraAPI.API.GameOptions; using Reactor.Networking.Attributes; using Reactor.Networking.Rpc; @@ -77,6 +76,6 @@ public class SyncOptionsRpc(MiraAPIPlugin plugin, uint id) : PlayerCustomRpc - /// Add options scrolling (on the hud text) - /// - public static void OptionsScrollingLogic(HudManager __instance) - { - if (Input.GetKeyDown(KeyCode.Tab)) - { - if (ToHudStringPatch.CurrentPage >= CustomOptionsManager.RegisteredMods.Count) - { - ToHudStringPatch.CurrentPage = 0; - } - else - { - ToHudStringPatch.CurrentPage += 1; - } - } - - var numPlayers = GameData.Instance ? GameData.Instance.PlayerCount : 10; - HudManager.Instance.GameSettings.text = GameOptionsManager.Instance.CurrentGameOptions.ToHudString(numPlayers); - var pos = __instance.GameSettings.transform.localPosition; - - if (!PlayerControl.LocalPlayer.CanMove) - { - return; - } - - if (ToHudStringPatch.CurrentPage == 0) - { - __instance.GameSettings.transform.localPosition = new Vector3(pos.x, _bounds.min, pos.z); - return; - } - - _bounds.max = __instance.GameSettings.rectTransform.sizeDelta.y + _bounds.min; - - var delta = Input.mouseScrollDelta.y switch - { - > 0f => -Increment, - < 0f => Increment, - _ => 0f - }; - - pos = new Vector3(pos.x, Mathf.Clamp(pos.y + delta, _bounds.min, _bounds.max), pos.z); - - __instance.GameSettings.transform.localPosition = pos; - } - [HarmonyPostfix, HarmonyPatch("Update")] public static void UpdatePostfix(HudManager __instance) { var local = PlayerControl.LocalPlayer; - if (LobbyBehaviour.Instance) - { - OptionsScrollingLogic(__instance); - } - if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance) { return; diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs deleted file mode 100644 index 157b231..0000000 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ /dev/null @@ -1,109 +0,0 @@ -using HarmonyLib; -using Il2CppSystem; -using MiraAPI.API.GameOptions; -using System.Linq; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace MiraAPI.Patches.Options; - -[HarmonyPatch(typeof(GameOptionsMenu))] -public static class GameOptionsMenuPatch -{ - /// - /// Change the OnValueChanged action for all custom game options - /// - [HarmonyPostfix, HarmonyPatch("Start")] - public static void StartPostfix(GameOptionsMenu __instance) - { - foreach (var customOption in CustomOptionsManager.CustomOptions) - { - if (customOption.AdvancedRole is not null || !customOption.OptionBehaviour) - { - continue; - } - - customOption.OptionBehaviour.OnValueChanged = (Action)customOption.ValueChanged; - } - } - - private static GameSettingMenu menu; - - - /// - /// Set the position and offset of all custom game options - /// - [HarmonyPostfix, HarmonyPatch("Update")] - public static void UpdatePostfix(GameOptionsMenu __instance) - { - if (!menu) - { - menu = Object.FindObjectsOfType().First(); - } - - if (menu.RegularGameSettings.active || menu.RolesSettings.gameObject.active || menu.HideNSeekSettings.active) - { - return; - } - - var startOffset = 2.15f; - __instance.GetComponentInParent().ContentYBounds.max = startOffset + __instance.Children.Count * 0.5f; - - foreach (var option in CustomOptionsManager.CustomOptions.Where(option => option.Group == null)) - { - if (!option.OptionBehaviour) continue; - - option.OptionBehaviour.enabled = !option.Hidden(); - option.OptionBehaviour.gameObject.SetActive(!option.Hidden()); - - if (!option.Hidden()) - { - startOffset -= 0.55f; - } - - var transform = option.OptionBehaviour.transform; - var optionPosition = transform.localPosition; - transform.localPosition = new Vector3(optionPosition.x, startOffset, optionPosition.z); - } - - foreach (var group in CustomOptionsManager.CustomGroups.Where(group => group.AdvancedRole == null)) - { - if (group.Header == null) - { - continue; - } - - group.Header.SetActive(!group.Hidden()); - - if (!group.Hidden()) - { - startOffset -= 0.5f; - } - - var position = group.Header.transform.localPosition; - group.Header.transform.localPosition = new Vector3(position.x, startOffset, position.z); - - foreach (var option in group.Options) - { - if (!option.OptionBehaviour) - { - continue; - } - - var enabled = !group.Hidden() && !option.Hidden(); - - option.OptionBehaviour.enabled = enabled; - option.OptionBehaviour.gameObject.SetActive(enabled); - - if (!group.Hidden() && !option.Hidden()) - { - startOffset -= 0.5f; - } - - var transform = option.OptionBehaviour.transform; - var optionPosition = transform.localPosition; - transform.localPosition = new Vector3(optionPosition.x, startOffset, optionPosition.z); - } - } - } -} \ No newline at end of file diff --git a/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs deleted file mode 100644 index d352239..0000000 --- a/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs +++ /dev/null @@ -1,175 +0,0 @@ -using HarmonyLib; -using MiraAPI.API.GameOptions; -using MiraAPI.GameModes; -using Reactor.Utilities; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace MiraAPI.Patches.Options; - -[HarmonyPatch(typeof(GameSettingMenu))] -public static class GameSettingsMenuPatches -{ - private static GameObject _gameBtn; - private static GameObject _roleBtn; - - [HarmonyPrefix, HarmonyPatch("Start")] - public static void StartPrefix(GameSettingMenu __instance) - { - if (CustomOptionsTab.CustomTabs != null) - { - return; - } - - CustomOptionsTab.CustomTabs = new(); - - _gameBtn = __instance.transform.FindChild("Header/Tabs/GameTab").gameObject; - _roleBtn = __instance.transform.FindChild("Header/Tabs/RoleTab").gameObject; - - var numberOpt = __instance.RegularGameSettings.GetComponentInChildren(); - var toggleOpt = Object.FindObjectOfType(); - var stringOpt = __instance.RegularGameSettings.GetComponentInChildren(); - - foreach (var pair in CustomOptionsManager.RegisteredMods) - { - var container = CustomOptionsTab.InitializeForMod(pair.Value, __instance).transform; - - foreach (var group in CustomOptionsManager.CustomGroups.Where(group => group.AdvancedRole == null && group.ParentMod == pair.Key)) - { - if (GameManager.Instance.IsNormal()) - { - group.Header = CustomOptionsTab.CreateHeader(toggleOpt, container, group.Title); - CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, - group.CustomToggleOptions, group.CustomNumberOptions, group.CustomStringOptions); - continue; - } - - if (!group.Options.Any(x => x.ShowInHideNSeek)) - { - continue; - } - - group.Header = CustomOptionsTab.CreateHeader(toggleOpt, container, group.Title); - __instance.AllHideAndSeekItems = __instance.AllHideAndSeekItems.Append(group.Header.transform).ToArray(); - - CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, - group.CustomToggleOptions, group.CustomNumberOptions, group.CustomStringOptions); - } - - CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, - CustomOptionsManager.CustomToggleOptions.Where(option => option.Group == null && option.ParentMod == pair.Key), - CustomOptionsManager.CustomNumberOptions.Where(option => option.Group == null && option.ParentMod == pair.Key), - CustomOptionsManager.CustomStringOptions.Where(option => option.Group == null && option.ParentMod == pair.Key)); - } - - if (!numberOpt || !toggleOpt || !stringOpt) - { - Logger.Error("OPTION PREFABS MISSING"); - } - } - - [HarmonyPostfix, HarmonyPatch("Update")] - public static void UpdatePatch(GameSettingMenu __instance) - { - if (CustomGameModeManager.ActiveMode == null) - { - return; - } - - _gameBtn.SetActive(CustomGameModeManager.ActiveMode.CanAccessSettingsTab()); - _roleBtn.SetActive(CustomGameModeManager.ActiveMode.CanAccessRolesTab()); - - if (!CustomGameModeManager.ActiveMode.CanAccessSettingsTab()) - { - __instance.RegularGameSettings.SetActive(false); - __instance.RolesSettings.gameObject.SetActive(false); - - //CustomOptionsTab.CustomScreen.gameObject.SetActive(true); - - __instance.GameSettingsHightlight.enabled = false; - __instance.RolesSettingsHightlight.enabled = false; - } - } - - public static void CreateOptionsFor(GameSettingMenu __instance, ToggleOption togglePrefab, NumberOption numberPrefab, StringOption stringPrefab, Transform container, - IEnumerable toggles, IEnumerable numbers, IEnumerable strings) - { - foreach (var customToggleOption in toggles) - { - if (customToggleOption.AdvancedRole is not null || customToggleOption.OptionBehaviour) - { - continue; - } - - ToggleOption toggleOption; - - if (GameManager.Instance.IsNormal()) - { - toggleOption = customToggleOption.CreateToggleOption(togglePrefab, container); - __instance.AllItems = __instance.AllItems.AddItem(toggleOption.transform).ToArray(); - continue; - } - - if (!customToggleOption.ShowInHideNSeek) - { - continue; - } - - toggleOption = customToggleOption.CreateToggleOption(togglePrefab, container); - __instance.AllHideAndSeekItems = __instance.AllHideAndSeekItems.AddItem(toggleOption.transform).ToArray(); - - } - - foreach (var customNumberOption in numbers) - { - if (customNumberOption.AdvancedRole is not null || customNumberOption.OptionBehaviour) - { - continue; - } - - NumberOption numberOption; - - if (GameManager.Instance.IsNormal()) - { - numberOption = customNumberOption.CreateNumberOption(numberPrefab, container); - __instance.AllItems = __instance.AllItems.AddItem(numberOption.transform).ToArray(); - continue; - } - - if (!customNumberOption.ShowInHideNSeek) - { - continue; - } - - numberOption = customNumberOption.CreateNumberOption(numberPrefab, container); - __instance.AllHideAndSeekItems = __instance.AllHideAndSeekItems.AddItem(numberOption.transform).ToArray(); - } - - foreach (var customStringOption in strings) - { - if (customStringOption.AdvancedRole is not null || customStringOption.OptionBehaviour) - { - continue; - } - - StringOption stringOption; - - if (GameManager.Instance.IsNormal()) - { - stringOption = customStringOption.CreateStringOption(stringPrefab, container); - __instance.AllItems = __instance.AllItems.AddItem(stringOption.transform).ToArray(); - continue; - } - - if (!customStringOption.ShowInHideNSeek) - { - continue; - } - - stringOption = customStringOption.CreateStringOption(stringPrefab, container); - __instance.AllHideAndSeekItems = __instance.AllHideAndSeekItems.AddItem(stringOption.transform).ToArray(); - } - } -} \ No newline at end of file diff --git a/MiraAPI/Patches/Options/ToHudStringPatch.cs b/MiraAPI/Patches/Options/ToHudStringPatch.cs deleted file mode 100644 index fae516d..0000000 --- a/MiraAPI/Patches/Options/ToHudStringPatch.cs +++ /dev/null @@ -1,147 +0,0 @@ -using AmongUs.GameOptions; -using BepInEx; -using HarmonyLib; -using MiraAPI.API.GameOptions; -using MiraAPI.Roles; -using MiraAPI.Utilities; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace MiraAPI.Patches.Options; - -[HarmonyPatch(typeof(IGameOptionsExtensions), "ToHudString")] -public static class ToHudStringPatch -{ - public static int CurrentPage = 0; - /// - /// Generic method to add options - /// - public static void AddOptions(StringBuilder sb, - IEnumerable numberOptions, IEnumerable stringOptions, IEnumerable toggleOptions) - { - foreach (var numberOption in numberOptions.Where(x => !x.Hidden())) - { - if (GameManager.Instance.IsHideAndSeek() && !numberOption.ShowInHideNSeek) - { - continue; - } - - sb.AppendLine(numberOption.Title + ": " + numberOption.Value + Helpers.GetSuffix(numberOption.SuffixType)); - } - - foreach (var toggleOption in toggleOptions.Where(x => !x.Hidden())) - { - if (GameManager.Instance.IsHideAndSeek() && !toggleOption.ShowInHideNSeek) - { - continue; - } - - sb.AppendLine(toggleOption.Title + ": " + (toggleOption.Value ? "On" : "Off")); - } - - foreach (var stringOption in stringOptions.Where(x => !x.Hidden())) - { - if (GameManager.Instance.IsHideAndSeek() && !stringOption.ShowInHideNSeek) - { - continue; - } - - sb.AppendLine(stringOption.Title + ": " + stringOption.Value); - } - } - - public static void Prefix() - { - if (GameManager.Instance is null || GameManager.Instance.IsHideAndSeek()) - { - return; - } - - foreach (var role in CustomRoleManager.CustomRoles.Values) - { - var customRole = role as ICustomRole; - if (customRole.IsGhostRole) - { - role.Role = RoleTypes.CrewmateGhost; - } - } - } - - /// - /// Update the HudOptions on the left of the screen if player is using Launchpad options - /// - public static void Postfix(IGameOptions __instance, ref string __result) - { - if (GameManager.Instance is null) - { - return; - } - - foreach (var pair in CustomRoleManager.CustomRoles) - { - var customRole = pair.Value as ICustomRole; - if (customRole.IsGhostRole) - { - pair.Value.Role = (RoleTypes)pair.Key; - } - } - - if (CurrentPage != 0) - { - PluginInfo currentPlugin = CustomOptionsManager.RegisteredMods.Keys.ToArray()[CurrentPage - 1]; - if (currentPlugin == null) - { - CurrentPage = 0; - return; - } - - var sb = new StringBuilder($"{currentPlugin.Metadata.Name} Options:\n"); - var groupsWithRoles = CustomOptionsManager.CustomGroups.Where(group => group.AdvancedRole != null && group.ParentMod == currentPlugin); - var groupsWithoutRoles = CustomOptionsManager.CustomGroups.Where(group => group.AdvancedRole == null && group.ParentMod == currentPlugin); - - AddOptions(sb, - CustomOptionsManager.CustomNumberOptions.Where(option => option.Group == null && option.ParentMod == currentPlugin && !option.Hidden()), - CustomOptionsManager.CustomStringOptions.Where(option => option.Group == null && option.ParentMod == currentPlugin && !option.Hidden()), - CustomOptionsManager.CustomToggleOptions.Where(option => option.Group == null && option.ParentMod == currentPlugin && !option.Hidden()) - ); - - foreach (var group in groupsWithoutRoles) - { - if (group.Hidden() || (GameManager.Instance.IsHideAndSeek() && !group.Options.Any(x => x.ShowInHideNSeek))) - { - continue; - } - - sb.AppendLine($"\n{group.Title}"); - AddOptions(sb, group.CustomNumberOptions, group.CustomStringOptions, group.CustomToggleOptions); - } - - var customOptionGroups = groupsWithRoles as CustomOptionGroup[] ?? groupsWithRoles.ToArray(); - if (customOptionGroups.Any()) - { - sb.AppendLine("\nRoles"); - - foreach (var group in customOptionGroups) - { - if (group.Hidden()) - { - continue; - } - - sb.AppendLine($"{group.Title}"); - AddOptions(sb, group.CustomNumberOptions, group.CustomStringOptions, group.CustomToggleOptions); - sb.Append("\n"); - } - } - - __result = sb.ToString(); - } - - if (CurrentPage == 0) - { - __result = "Normal Options:\n" + __result; - } - __result += $"\nUse TAB to switch pages (Page {CurrentPage}/{CustomOptionsManager.RegisteredMods.Count})"; - } -} \ No newline at end of file diff --git a/MiraAPI/Patches/Roles/RolesSettingsMenuPatches.cs b/MiraAPI/Patches/Roles/RolesSettingsMenuPatches.cs index 9cd5562..2c2fe52 100644 --- a/MiraAPI/Patches/Roles/RolesSettingsMenuPatches.cs +++ b/MiraAPI/Patches/Roles/RolesSettingsMenuPatches.cs @@ -1,8 +1,6 @@ using AmongUs.GameOptions; using HarmonyLib; using Il2CppInterop.Runtime; -using Il2CppSystem; -using MiraAPI.API.GameOptions; using MiraAPI.Networking.Options; using MiraAPI.Roles; using Reactor.Networking.Rpc; @@ -54,26 +52,26 @@ public static class RolesSettingsMenuPatches var startOffset = 0.5f; - foreach (var customOption in CustomOptionsManager.CustomOptions) - { - if (customOption.AdvancedRole is not null && customOption.AdvancedRole == role.GetType()) - { - startOffset -= 0.5f; + /* foreach (var customOption in CustomOptionsManager.CustomOptions) + { + if (customOption.AdvancedRole is not null && customOption.AdvancedRole == role.GetType()) + { + startOffset -= 0.5f; - switch (customOption) - { - case CustomNumberOption numberOption: - var numOpt = numberOption.CreateNumberOption(numberSet, newTab.transform); - numOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0); - break; + switch (customOption) + { + case CustomNumberOption numberOption: + var numOpt = numberOption.CreateNumberOption(numberSet, newTab.transform); + numOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0); + break; - case CustomToggleOption toggleOption: - var togOpt = toggleOption.CreateToggleOption(toggleSet, newTab.transform); - togOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0); - break; - } - } - } + case CustomToggleOption toggleOption: + var togOpt = toggleOption.CreateToggleOption(toggleSet, newTab.transform); + togOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0); + break; + } + } + }*/ var tmp = newTab.GetComponentInChildren(); tmp.defaultStr = role.NiceName; @@ -98,13 +96,13 @@ public static class RolesSettingsMenuPatches [HarmonyPostfix, HarmonyPatch("Start")] public static void StartPostfix(RolesSettingsMenu __instance) { - foreach (var customOption in CustomOptionsManager.CustomOptions) - { - if (customOption.AdvancedRole is not null) - { - customOption.OptionBehaviour.OnValueChanged = (Action)customOption.ValueChanged; - } - } + /* foreach (var customOption in CustomOptionsManager.CustomOptions) + { + if (customOption.AdvancedRole is not null) + { + customOption.OptionBehaviour.OnValueChanged = (Action)customOption.ValueChanged; + } + }*/ var scroll = __instance.GetComponentInChildren(); scroll.active = true; From 947a202c5596c4b15c9cad9dd557d09bac8aa30a Mon Sep 17 00:00:00 2001 From: ang-xd Date: Sat, 18 May 2024 00:10:07 -0400 Subject: [PATCH 2/4] Attribute based options API (wip) --- MiraAPI.Example/ExOptions.cs | 9 -- MiraAPI.Example/ExampleOptions.cs | 14 ++ MiraAPI.Example/ExamplePlugin.cs | 12 +- MiraAPI.Example/SomeRandomNonGroupClass.cs | 9 ++ .../Attributes/ModdedNumberOptionAttribute.cs | 44 +++++ .../Attributes/ModdedOptionAttribute.cs | 39 +++++ .../Attributes/ModdedStringOptionAttribute.cs | 33 ++++ .../Attributes/ModdedToggleOptionAttribute.cs | 33 ++++ .../RegisterModdedOptionAttribute.cs | 41 ----- MiraAPI/GameOptions/IModdedOption.cs | 16 +- MiraAPI/GameOptions/IModdedOptionGroup.cs | 13 ++ MiraAPI/GameOptions/ModdedGroupSingleton.cs | 20 +++ MiraAPI/GameOptions/ModdedOptionGroup.cs | 15 ++ MiraAPI/GameOptions/ModdedOptionsManager.cs | 121 +++++++++----- .../OptionTypes/ModdedNumberOption.cs | 64 ++++++++ .../GameOptions/OptionTypes/ModdedOption.cs | 31 ++-- .../OptionTypes/ModdedStringOption.cs | 49 ++++++ .../OptionTypes/ModdedToggleOption.cs | 32 +++- MiraAPI/GameOptions/UI/ModdedOptionsTab.cs | 153 ++++++++++++++++++ MiraAPI/IMiraConfig.cs | 16 ++ MiraAPI/MiraAPI.csproj | 4 - MiraAPI/MiraAPIPlugin.cs | 4 +- MiraAPI/Patches/HudManagerPatches.cs | 61 ++++++- .../Patches/Options/GameOptionsMenuPatch.cs | 109 +++++++++++++ .../Options/GameSettingsMenuPatches.cs | 68 ++++++++ MiraAPI/Patches/Options/HudStringPatch.cs | 118 ++++++++++++++ .../Utilities/Assets/LoadableResourceAsset.cs | 14 +- 27 files changed, 1024 insertions(+), 118 deletions(-) delete mode 100644 MiraAPI.Example/ExOptions.cs create mode 100644 MiraAPI.Example/ExampleOptions.cs create mode 100644 MiraAPI.Example/SomeRandomNonGroupClass.cs create mode 100644 MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs create mode 100644 MiraAPI/GameOptions/Attributes/ModdedOptionAttribute.cs create mode 100644 MiraAPI/GameOptions/Attributes/ModdedStringOptionAttribute.cs create mode 100644 MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs delete mode 100644 MiraAPI/GameOptions/Attributes/RegisterModdedOptionAttribute.cs create mode 100644 MiraAPI/GameOptions/IModdedOptionGroup.cs create mode 100644 MiraAPI/GameOptions/ModdedGroupSingleton.cs create mode 100644 MiraAPI/GameOptions/ModdedOptionGroup.cs create mode 100644 MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs create mode 100644 MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs create mode 100644 MiraAPI/GameOptions/UI/ModdedOptionsTab.cs create mode 100644 MiraAPI/IMiraConfig.cs create mode 100644 MiraAPI/Patches/Options/GameOptionsMenuPatch.cs create mode 100644 MiraAPI/Patches/Options/GameSettingsMenuPatches.cs create mode 100644 MiraAPI/Patches/Options/HudStringPatch.cs diff --git a/MiraAPI.Example/ExOptions.cs b/MiraAPI.Example/ExOptions.cs deleted file mode 100644 index 487ac02..0000000 --- a/MiraAPI.Example/ExOptions.cs +++ /dev/null @@ -1,9 +0,0 @@ -using MiraAPI.GameOptions.Attributes; - -namespace MiraAPI.Example -{ - public class ExOptions - { - [RegisterModdedOption("Use Thing")] public bool useThing = false; - } -} diff --git a/MiraAPI.Example/ExampleOptions.cs b/MiraAPI.Example/ExampleOptions.cs new file mode 100644 index 0000000..0ee7d49 --- /dev/null +++ b/MiraAPI.Example/ExampleOptions.cs @@ -0,0 +1,14 @@ +using MiraAPI.GameOptions; +using MiraAPI.GameOptions.Attributes; + +namespace MiraAPI.Example +{ + public class ExampleOptions : IModdedOptionGroup + { + public string GroupName => "General Group"; + [ModdedToggleOption("Use Thing")] public bool useThing { get; set; } = true; + [ModdedToggleOption("Use another thing")] public bool useAnotherThing { get; set; } = false; + [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; } = 0; + } +} diff --git a/MiraAPI.Example/ExamplePlugin.cs b/MiraAPI.Example/ExamplePlugin.cs index ed9a112..3371c5d 100644 --- a/MiraAPI.Example/ExamplePlugin.cs +++ b/MiraAPI.Example/ExamplePlugin.cs @@ -1,6 +1,7 @@ using BepInEx; using BepInEx.Unity.IL2CPP; using HarmonyLib; +using MiraAPI.Utilities.Assets; using Reactor; using Reactor.Networking; using Reactor.Networking.Attributes; @@ -11,14 +12,19 @@ namespace MiraAPI.Example; [BepInProcess("Among Us.exe")] [BepInDependency(ReactorPlugin.Id)] [ReactorModFlags(ModFlags.RequireOnAllClients)] -public partial class ExamplePlugin : BasePlugin +public partial class ExamplePlugin : BasePlugin, IMiraConfig { public Harmony Harmony { get; } = new(Id); public static ExamplePlugin Instance { get; private set; } + public ModdedOptionTabSettings TabSettings => new ModdedOptionTabSettings() + { + Title = "Example Mod", + TabIcon = MiraAssets.Empty + }; + public override void Load() { Instance = this; Harmony.PatchAll(); - } -} +} \ No newline at end of file diff --git a/MiraAPI.Example/SomeRandomNonGroupClass.cs b/MiraAPI.Example/SomeRandomNonGroupClass.cs new file mode 100644 index 0000000..76504f9 --- /dev/null +++ b/MiraAPI.Example/SomeRandomNonGroupClass.cs @@ -0,0 +1,9 @@ +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; } = 2; + } +} diff --git a/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs new file mode 100644 index 0000000..fe0943d --- /dev/null +++ b/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs @@ -0,0 +1,44 @@ +using MiraAPI.GameOptions.OptionTypes; +using System; +using System.Reflection; +using UnityEngine; + +namespace MiraAPI.GameOptions.Attributes +{ + public class ModdedNumberOptionAttribute : ModdedOptionAttribute + { + public float Min { get; private set; } + public float Max { get; private set; } + public float Increment { get; private set; } + public NumberSuffixes SuffixType { get; private set; } + public bool ZeroInfinity { get; private set; } + + public ModdedNumberOptionAttribute(string title, float min, float max, float increment = 1, NumberSuffixes suffixType = NumberSuffixes.None, bool zeroInfinity = false, Type roleType = null) : base(title, roleType) + { + Min = min; + Max = max; + Increment = increment; + SuffixType = suffixType; + ZeroInfinity = zeroInfinity; + } + + public override IModdedOption CreateOption(object value, PropertyInfo property) + { + Debug.LogError(value.ToString()); + var toggleOpt = new ModdedNumberOption(Title, (float)value, Min, Max, Increment, SuffixType, ZeroInfinity, RoleType); + return toggleOpt; + } + + public override void SetValue(object value) + { + ModdedNumberOption toggleOpt = HolderOption as ModdedNumberOption; + toggleOpt.SetValue((float)value); + } + + public override object GetValue() + { + ModdedNumberOption toggleOpt = HolderOption as ModdedNumberOption; + return toggleOpt.Value; + } + } +} diff --git a/MiraAPI/GameOptions/Attributes/ModdedOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedOptionAttribute.cs new file mode 100644 index 0000000..e2379b5 --- /dev/null +++ b/MiraAPI/GameOptions/Attributes/ModdedOptionAttribute.cs @@ -0,0 +1,39 @@ +using System; +using System.Reflection; + +namespace MiraAPI.GameOptions.Attributes +{ + [AttributeUsage(AttributeTargets.Property)] + public abstract class ModdedOptionAttribute : Attribute + { + public IModdedOption HolderOption { get; set; } + public string Title { get; private set; } + public Type RoleType { get; private set; } + public ModdedOptionAttribute(string title, Type roleType = null) + { + Title = title; + RoleType = roleType; + } + + public static void Register(Assembly assembly) + { + foreach (var type in assembly.GetTypes()) + { + foreach (PropertyInfo property in type.GetProperties()) + { + foreach (var attribute in property.GetCustomAttributes()) + { + if (attribute.GetType().BaseType == typeof(ModdedOptionAttribute)) + { + ModdedOptionsManager.RegisterOption(assembly, type, (ModdedOptionAttribute)attribute, property); + } + } + } + } + } + + public abstract void SetValue(object val); + public abstract object GetValue(); + public abstract IModdedOption CreateOption(object value, PropertyInfo property); + } +} diff --git a/MiraAPI/GameOptions/Attributes/ModdedStringOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedStringOptionAttribute.cs new file mode 100644 index 0000000..d9cfeb7 --- /dev/null +++ b/MiraAPI/GameOptions/Attributes/ModdedStringOptionAttribute.cs @@ -0,0 +1,33 @@ +using MiraAPI.GameOptions.OptionTypes; +using System; +using System.Reflection; + +namespace MiraAPI.GameOptions.Attributes +{ + public class ModdedStringOptionAttribute : ModdedOptionAttribute + { + public string[] Values; + public ModdedStringOptionAttribute(string title, string[] strings, Type roleType = null) : base(title, roleType) + { + Values = strings; + } + + public override IModdedOption CreateOption(object value, PropertyInfo property) + { + var opt = new ModdedStringOption(Title, (int)value, Values, RoleType); + return opt; + } + + public override void SetValue(object value) + { + ModdedStringOption opt = HolderOption as ModdedStringOption; + opt.SetValue((int)value); + } + + public override object GetValue() + { + ModdedStringOption opt = HolderOption as ModdedStringOption; + return opt.Value; + } + } +} diff --git a/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs new file mode 100644 index 0000000..5e0ea54 --- /dev/null +++ b/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs @@ -0,0 +1,33 @@ +using MiraAPI.GameOptions.OptionTypes; +using System; +using System.Reflection; +using UnityEngine; + +namespace MiraAPI.GameOptions.Attributes +{ + public class ModdedToggleOptionAttribute : ModdedOptionAttribute + { + public ModdedToggleOptionAttribute(string title, Type roleType = null) : base(title, roleType) + { + } + + public override IModdedOption CreateOption(object value, PropertyInfo property) + { + Debug.LogError(value.ToString()); + var toggleOpt = new ModdedToggleOption(Title, (bool)value, RoleType); + return toggleOpt; + } + + public override void SetValue(object value) + { + ModdedToggleOption toggleOpt = HolderOption as ModdedToggleOption; + toggleOpt.SetValue((bool)value); + } + + public override object GetValue() + { + ModdedToggleOption toggleOpt = HolderOption as ModdedToggleOption; + return toggleOpt.Value; + } + } +} diff --git a/MiraAPI/GameOptions/Attributes/RegisterModdedOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/RegisterModdedOptionAttribute.cs deleted file mode 100644 index b2e7b79..0000000 --- a/MiraAPI/GameOptions/Attributes/RegisterModdedOptionAttribute.cs +++ /dev/null @@ -1,41 +0,0 @@ -using BepInEx.Unity.IL2CPP; -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace MiraAPI.GameOptions.Attributes -{ - [AttributeUsage(AttributeTargets.Property)] - public class RegisterModdedOptionAttribute : Attribute - { - private static readonly HashSet RegisteredAssemblies = []; - - public string Title { get; private set; } - public RegisterModdedOptionAttribute(string title) - { - Title = title; - } - - public static void Initialize() - { - IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) => Register(assembly); - } - - public static void Register(Assembly assembly) - { - if (!RegisteredAssemblies.Add(assembly)) return; - - foreach (var type in assembly.GetTypes()) - { - foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public)) - { - var attribute = property.GetCustomAttribute(); - if (attribute != null) - { - ModdedOptionsManager.Instance.CreateOption(type, attribute, property); - } - } - } - } - } -} diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index 205a142..d5628a0 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -1,7 +1,19 @@ -namespace MiraAPI.GameOptions +using System; +using UnityEngine; + +namespace MiraAPI.GameOptions { public interface IModdedOption { - + public Type AdvancedRole { get; } + public OptionBehaviour OptionBehaviour { get; protected set; } + public string Title { get; } + public StringNames StringName { get; } + public Func Visible { get; set; } + public ModdedOptionGroup Group { get; set; } + public IMiraConfig ParentMod { get; set; } + public void ValueChanged(OptionBehaviour optionBehaviour); + public string GetHudStringText(); + public OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container); } } diff --git a/MiraAPI/GameOptions/IModdedOptionGroup.cs b/MiraAPI/GameOptions/IModdedOptionGroup.cs new file mode 100644 index 0000000..41c0d36 --- /dev/null +++ b/MiraAPI/GameOptions/IModdedOptionGroup.cs @@ -0,0 +1,13 @@ +using System; +using UnityEngine; + +namespace MiraAPI.GameOptions +{ + public interface IModdedOptionGroup + { + public string GroupName { get; } + public Func GroupVisible => () => true; + public Color GroupColor => Color.white; + public Type AdvancedRole => null; + } +} diff --git a/MiraAPI/GameOptions/ModdedGroupSingleton.cs b/MiraAPI/GameOptions/ModdedGroupSingleton.cs new file mode 100644 index 0000000..07dd84e --- /dev/null +++ b/MiraAPI/GameOptions/ModdedGroupSingleton.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; + +namespace MiraAPI.GameOptions +{ + public class ModdedGroupSingleton where T : ModdedOptionGroup + { + private static T _instance; + + public static T Instance + { + get => _instance ??= ModdedOptionsManager.Groups.OfType().Single(); + set + { + if (_instance != null) throw new InvalidOperationException($"Instance for {typeof(T)} is already set"); + _instance = value; + } + } + } +} diff --git a/MiraAPI/GameOptions/ModdedOptionGroup.cs b/MiraAPI/GameOptions/ModdedOptionGroup.cs new file mode 100644 index 0000000..6e7742b --- /dev/null +++ b/MiraAPI/GameOptions/ModdedOptionGroup.cs @@ -0,0 +1,15 @@ +using System; +using UnityEngine; + +namespace MiraAPI.GameOptions +{ + public class ModdedOptionGroup + { + public IMiraConfig ParentMod; + public GameObject Header; + public string GroupName; + public Func GroupVisible; + public Color GroupColor; + public Type AdvancedRole; + } +} diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index 16637cd..5ea9378 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -1,6 +1,6 @@ -using HarmonyLib; +using BepInEx.Unity.IL2CPP; +using HarmonyLib; using MiraAPI.GameOptions.Attributes; -using MiraAPI.GameOptions.OptionTypes; using Reactor.Utilities; using System; using System.Collections.Generic; @@ -12,62 +12,101 @@ namespace MiraAPI.GameOptions { public class ModdedOptionsManager { - public static ModdedOptionsManager Instance; - public List Options = new List(); + public static List Options = new(); + public static List Groups = new(); + private static Dictionary OptionAttributes = new(); + private static Dictionary OriginalTypes = new(); + public static Dictionary RegisteredMods = new(); - private Dictionary PatchedOptions = new Dictionary(); - - public void CreateOption(Type type, RegisterModdedOptionAttribute attribute, PropertyInfo property) + public static void Initialize() { - Debug.LogError(property.GetConstantValue().GetType().ToString()); - - switch (property.GetConstantValue()) + IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) => { - case bool: - CreateToggleOption(type, attribute, property); - break; - default: - Debug.LogError($"Mira API does not support type {property.GetConstantValue().GetType()}"); - break; - } + if (plugin.GetType().GetInterfaces().Contains(typeof(IMiraConfig))) + { + IMiraConfig config = (IMiraConfig)Activator.CreateInstance(plugin.GetType()); + RegisteredMods.Add(assembly, config); + } + + RegisterOptionGroups(assembly); + + ModdedOptionAttribute.Register(assembly); + }; } - public static void PropertySetterPatch(MethodBase __originalMethod, object[] args) + private static void RegisterOptionGroups(Assembly assembly) { - IModdedOption opt = Instance.PatchedOptions.First(pair => pair.Key.GetSetMethod().Equals(__originalMethod)).Value; - - if (opt != null) + foreach (var type in assembly.GetTypes()) { - object value = args[0]; - switch (value) + if (typeof(IModdedOptionGroup).IsAssignableFrom(type)) { - case bool: - ModdedToggleOption toggleOpt = opt as ModdedToggleOption; - toggleOpt.SetValue((bool)value); - break; + IModdedOptionGroup group = (IModdedOptionGroup)Activator.CreateInstance(type); + ModdedOptionGroup newGroup = new ModdedOptionGroup() + { + AdvancedRole = group.AdvancedRole, + GroupColor = group.GroupColor, + GroupName = group.GroupName, + GroupVisible = group.GroupVisible + }; + + Groups.Add(newGroup); + OriginalTypes.Add(type, newGroup); + + newGroup.ParentMod = RegisteredMods[assembly]; } } } - public ModdedToggleOption CreateToggleOption(Type type, RegisterModdedOptionAttribute attribute, PropertyInfo property) + public static void RegisterOption(Assembly assembly, Type type, ModdedOptionAttribute attribute, PropertyInfo property) { - var toggleOpt = new ModdedToggleOption(attribute.Title, (bool)property.GetValue(type)); - Options.Add(toggleOpt); + if (OptionAttributes.ContainsKey(property)) return; + object newObj = Activator.CreateInstance(type); + IModdedOption result = attribute.CreateOption(property.GetValue(newObj), property); - var setterOriginal = property.GetSetMethod(); - var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch)); - PluginSingleton.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch)); - /* - var getterOriginal = property.GetGetMethod(); - var getterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertyGetterPatch)); - PluginSingleton.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch));*/ + if (result != null) + { + var setterOriginal = property.GetSetMethod(); + var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch)); + PluginSingleton.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch)); - toggleOpt.PropertyInfo = property; + var getterOriginal = property.GetGetMethod(); + var getterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertyGetterPatch)); + PluginSingleton.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch)); - if (!PatchedOptions.ContainsKey(property)) - PatchedOptions.Add(property, toggleOpt); + attribute.HolderOption = result; + result.ParentMod = RegisteredMods[assembly]; - return toggleOpt; + Options.Add(result); + OptionAttributes.Add(property, attribute); + + if (OriginalTypes.ContainsKey(type)) + { + Debug.LogError($"grouping {attribute.Title} with {OriginalTypes[type].GroupName}"); + result.Group = OriginalTypes[type]; + } + } + } + + public static void PropertySetterPatch(MethodBase __originalMethod, object value) + { + ModdedOptionAttribute attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod().Equals(__originalMethod)).Value; + + if (attribute != null) + { + attribute.SetValue(value); + } + } + + public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result) + { + ModdedOptionAttribute attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod().Equals(__originalMethod)).Value; + + if (attribute != null) + { + __result = attribute.GetValue(); + return false; + } + return true; } } } diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs new file mode 100644 index 0000000..cf10468 --- /dev/null +++ b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs @@ -0,0 +1,64 @@ +using MiraAPI.Utilities; +using UnityEngine; + +namespace MiraAPI.GameOptions.OptionTypes +{ + public class ModdedNumberOption : ModdedOption + { + public float Min { get; private set; } + public float Max { get; private set; } + public float Increment { get; private set; } + public NumberSuffixes SuffixType { get; private set; } + public bool ZeroInfinity { get; private set; } + public ModdedNumberOption(string title, float defaultValue, float min, float max, float increment, NumberSuffixes suffixType, bool zeroInfinity, System.Type roleType) : base(title, defaultValue, roleType) + { + Min = min; + Max = max; + Increment = increment; + SuffixType = suffixType; + ZeroInfinity = zeroInfinity; + + Value = Mathf.Clamp(defaultValue, min, max); + } + + public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container) + { + var numberOption = Object.Instantiate(numberOpt, container); + + numberOption.name = Title; + numberOption.Title = StringName; + numberOption.Value = Value; + numberOption.Increment = Increment; + numberOption.SuffixType = SuffixType; + numberOption.FormatString = "0"; + numberOption.ValidRange = new FloatRange(Min, Max); + numberOption.ZeroIsInfinity = ZeroInfinity; + numberOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged; + numberOption.OnEnable(); + + OptionBehaviour = numberOption; + + return numberOption; + } + + public override string GetHudStringText() + { + return Title + ": " + Value + Helpers.GetSuffix(SuffixType); + } + + public override float GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour) + { + return Mathf.Clamp(optionBehaviour.GetFloat(), Min, Max); + } + + public override void OnValueChanged(float newValue) + { + Value = Mathf.Clamp(newValue, Min, Max); + + if (OptionBehaviour is null) return; + + NumberOption opt = OptionBehaviour as NumberOption; + opt.Value = newValue; + } + } +} diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index 726290d..a2b4518 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -1,6 +1,7 @@ -using Reactor.Localization.Utilities; +using MiraAPI.Roles; +using Reactor.Localization.Utilities; using System; -using System.Reflection; +using UnityEngine; namespace MiraAPI.GameOptions.OptionTypes { @@ -9,20 +10,26 @@ namespace MiraAPI.GameOptions.OptionTypes public T Value { get; set; } public T DefaultValue { get; set; } public Action ChangedEvent { get; private set; } - - public PropertyInfo PropertyInfo; public string Title { get; } public StringNames StringName { get; } - public Func Hidden { get; set; } - public OptionBehaviour OptionBehaviour { get; protected set; } + public Func Visible { get; set; } + public Type AdvancedRole { get; } + public OptionBehaviour OptionBehaviour { get; set; } + public ModdedOptionGroup Group { get; set; } = null; + public IMiraConfig ParentMod { get; set; } = null; - public ModdedOption(string title, T defaultValue) + public ModdedOption(string title, T defaultValue, Type roleType) { Title = title; DefaultValue = defaultValue; Value = defaultValue; StringName = CustomStringName.CreateAndRegister(Title); - Hidden = () => false; + Visible = () => true; + + if (roleType is not null && roleType.IsAssignableTo(typeof(ICustomRole))) + { + AdvancedRole = roleType; + } } public void ValueChanged(OptionBehaviour optionBehaviour) @@ -39,9 +46,15 @@ namespace MiraAPI.GameOptions.OptionTypes ChangedEvent.Invoke(Value); } - PropertyInfo.SetValue(null, newValue); + OnValueChanged(newValue); } + public abstract void OnValueChanged(T newValue); + public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour); + + public abstract OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container); + + public abstract string GetHudStringText(); } } diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs new file mode 100644 index 0000000..584fe19 --- /dev/null +++ b/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs @@ -0,0 +1,49 @@ +using Reactor.Localization.Utilities; +using System.Linq; +using UnityEngine; + +namespace MiraAPI.GameOptions.OptionTypes +{ + public class ModdedStringOption : ModdedOption + { + public string[] Values { get; private set; } + public ModdedStringOption(string title, int defaultValue, string[] values, System.Type roleType) : base(title, defaultValue, roleType) + { + Values = values; + } + + public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container) + { + var stringOption = Object.Instantiate(stringOpt, container); + + stringOption.name = Title; + stringOption.Title = StringName; + stringOption.Value = Value; + stringOption.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray(); + stringOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged; + stringOption.OnEnable(); + + OptionBehaviour = stringOption; + + return stringOption; + } + + public override string GetHudStringText() + { + return Title + ": " + Values[Value]; + } + + public override int GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour) + { + return optionBehaviour.GetInt(); + } + + public override void OnValueChanged(int newValue) + { + if (OptionBehaviour is null) return; + + StringOption opt = OptionBehaviour as StringOption; + opt.Value = newValue; + } + } +} diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs index 04cad16..1a6edc9 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs @@ -1,14 +1,42 @@ -namespace MiraAPI.GameOptions.OptionTypes +using UnityEngine; + +namespace MiraAPI.GameOptions.OptionTypes { public class ModdedToggleOption : ModdedOption { - public ModdedToggleOption(string title, bool defaultValue = false) : base(title, defaultValue) + public ModdedToggleOption(string title, bool defaultValue, System.Type roleType) : base(title, defaultValue, roleType) { } + public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container) + { + var toggleOption = UnityEngine.Object.Instantiate(toggleOpt, container); + + toggleOption.name = Title; + toggleOption.Title = StringName; + toggleOption.CheckMark.enabled = Value; + toggleOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged; + toggleOption.OnEnable(); + OptionBehaviour = toggleOption; + return toggleOption; + } + + public override string GetHudStringText() + { + return Title + ": " + (Value ? "On" : "Off"); + } + public override bool GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour) { return optionBehaviour.GetBool(); } + + public override void OnValueChanged(bool newValue) + { + if (OptionBehaviour is null) return; + + ToggleOption toggleOpt = OptionBehaviour as ToggleOption; + toggleOpt.CheckMark.enabled = newValue; + } } } diff --git a/MiraAPI/GameOptions/UI/ModdedOptionsTab.cs b/MiraAPI/GameOptions/UI/ModdedOptionsTab.cs new file mode 100644 index 0000000..f9a09ef --- /dev/null +++ b/MiraAPI/GameOptions/UI/ModdedOptionsTab.cs @@ -0,0 +1,153 @@ +using Reactor.Utilities.Extensions; +using System; +using System.Collections.Generic; +using TMPro; +using UnityEngine; +using UnityEngine.Events; +using Object = UnityEngine.Object; + +namespace MiraAPI.API.GameOptions; +public static class ModdedOptionsTab +{ + public static List CustomTabs = new List(); + public static List CustomScreens = new List(); + + public static GameObject InitializeForMod(IMiraConfig config, GameSettingMenu __instance) + { + var gameBtn = __instance.transform.FindChild("Header/Tabs/GameTab").gameObject; + var roleBtn = __instance.transform.FindChild("Header/Tabs/RoleTab").gameObject; + var screen = CreateNewMenu(config, __instance); + var tab = CreateCustomTab(config, __instance, screen, gameBtn, roleBtn); + + CustomScreens.Add(screen); + CustomTabs.Add(tab); + + SpriteRenderer rend = tab.transform.FindChild("Btn/Tab Background").GetComponent(); + rend.enabled = false; + + UpdateListeners(__instance, gameBtn.GetComponentInChildren(), roleBtn.GetComponentInChildren(), rend); + + var container = screen.transform.FindChild("GameGroup/SliderInner"); + container.DestroyChildren(); + //CreateNewResetButton(__instance, container); + + return container.gameObject; + } + + private static void UpdateListeners(GameSettingMenu __instance, PassiveButton gameB, PassiveButton roleB, SpriteRenderer rend) + { + gameB.OnClick.RemoveAllListeners(); + gameB.OnClick.AddListener((UnityAction)(() => + { + __instance.RegularGameSettings.SetActive(true); + __instance.RolesSettings.gameObject.SetActive(false); + + foreach (var screen in CustomScreens) screen.gameObject.SetActive(false); + + rend.enabled = false; + + __instance.GameSettingsHightlight.enabled = true; + __instance.RolesSettingsHightlight.enabled = false; + })); + + roleB.OnClick.RemoveAllListeners(); + roleB.OnClick.AddListener((UnityAction)(() => + { + __instance.RegularGameSettings.SetActive(false); + __instance.RolesSettings.gameObject.SetActive(true); + + foreach (var screen in CustomScreens) screen.gameObject.SetActive(false); + + rend.enabled = false; + + __instance.GameSettingsHightlight.enabled = false; + __instance.RolesSettingsHightlight.enabled = true; + })); + } + + public static GameObject CreateHeader(ToggleOption toggleOpt, Transform container, string title) + { + var header = Object.Instantiate(toggleOpt, container); + + header.Title = StringNames.None; + header.TitleText.text = title; + header.name = "Header"; + + var checkBox = header.transform.FindChild("CheckBox")?.gameObject; + if (checkBox) + { + checkBox.Destroy(); + } + + var background = header.transform.FindChild("Background")?.gameObject; + if (background) + { + background.Destroy(); + } + + header.GetComponent().Destroy(); + return header.gameObject; + } + + public static GameObject CreateCustomTab(IMiraConfig options, GameSettingMenu __instance, GameObject newSettings, + GameObject gameTab, GameObject roleTab) + { + var newTab = Object.Instantiate(gameTab, gameTab.transform.parent); + newTab.name = "Tab"; + newTab.transform.position = roleTab.transform.position; + newTab.transform.position += new Vector3(CustomTabs.Count + 1, 0, 0); + __instance.Tabs.transform.position -= new Vector3(0.5f, 0, 0); + + var inside = newTab.transform.FindChild("ColorButton"); + inside.name = "Btn"; + + var btn = inside.GetComponentInChildren(); + + btn.OnClick.RemoveAllListeners(); + + btn.OnClick.AddListener((Action)TabAction); + + var spriteRend = inside.GetComponentInChildren(); + // if (options.TabSettings.TabIcon != null) spriteRend.sprite = options.TabSettings.TabIcon.LoadAsset(); + + return newTab; + + void TabAction() + { + __instance.RegularGameSettings.SetActive(false); + __instance.RolesSettings.gameObject.SetActive(false); + + foreach (var screen in CustomScreens) + { + screen.gameObject.SetActive(false); + } + newSettings.gameObject.SetActive(true); + + SpriteRenderer rend = newTab.transform.FindChild("Btn/Tab Background").GetComponent(); + rend.enabled = true; + + gameTab.transform.FindChild("ColorButton/Tab Background").gameObject.GetComponent().enabled = false; + roleTab.transform.FindChild("Hat Button/Tab Background").gameObject.GetComponent().enabled = false; + + foreach (var tab in CustomTabs) + { + tab.transform.FindChild("Btn/Tab Background").gameObject.GetComponent().enabled = false; + } + } + } + + public static GameObject CreateNewMenu(IMiraConfig config, GameSettingMenu __instance) + { + var gameSettings = __instance.RegularGameSettings; + var newSettings = Object.Instantiate(gameSettings, gameSettings.transform.parent); + newSettings.name = "Settings"; + newSettings.SetActive(false); + + var launchpadGroup = newSettings.transform.FindChild("GameGroup").gameObject; + var text = launchpadGroup.transform.FindChild("Text").gameObject.GetComponent(); + text.gameObject.GetComponent().Destroy(); + text.text = $"{config.TabSettings.Title}"; + + return newSettings; + } +} \ No newline at end of file diff --git a/MiraAPI/IMiraConfig.cs b/MiraAPI/IMiraConfig.cs new file mode 100644 index 0000000..adf63a5 --- /dev/null +++ b/MiraAPI/IMiraConfig.cs @@ -0,0 +1,16 @@ +using MiraAPI.Utilities; +using UnityEngine; + +namespace MiraAPI +{ + public interface IMiraConfig + { + public ModdedOptionTabSettings TabSettings { get; } + } + + public struct ModdedOptionTabSettings(string title, LoadableAsset tabIcon) + { + public string Title { get; set; } = title; + public LoadableAsset TabIcon { get; set; } = tabIcon; + } +} diff --git a/MiraAPI/MiraAPI.csproj b/MiraAPI/MiraAPI.csproj index 70c9115..052c0f7 100644 --- a/MiraAPI/MiraAPI.csproj +++ b/MiraAPI/MiraAPI.csproj @@ -26,10 +26,6 @@ compile; build; native; contentfiles; analyzers; buildtransitive - - - - diff --git a/MiraAPI/MiraAPIPlugin.cs b/MiraAPI/MiraAPIPlugin.cs index 4cc3e10..077d721 100644 --- a/MiraAPI/MiraAPIPlugin.cs +++ b/MiraAPI/MiraAPIPlugin.cs @@ -2,7 +2,7 @@ using BepInEx.Unity.IL2CPP; using HarmonyLib; using MiraAPI.GameModes; -using MiraAPI.GameOptions.Attributes; +using MiraAPI.GameOptions; using MiraAPI.Hud; using MiraAPI.Roles; using Reactor; @@ -27,7 +27,7 @@ public partial class MiraAPIPlugin : BasePlugin Harmony.PatchAll(); RegisterCustomRoleAttribute.Initialize(); - RegisterModdedOptionAttribute.Initialize(); + ModdedOptionsManager.Initialize(); RegisterGameModeAttribute.Register(Assembly.GetCallingAssembly()); RegisterButtonAttribute.Register(Assembly.GetExecutingAssembly()); diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index 1f7afaa..f69fbf8 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -1,6 +1,8 @@ using HarmonyLib; using InnerNet; +using MiraAPI.GameOptions; using MiraAPI.Hud; +using MiraAPI.Patches.Options; using MiraAPI.Roles; using Reactor.Utilities.Extensions; using UnityEngine; @@ -16,10 +18,67 @@ namespace MiraAPI.Patches /// Custom role tab private static TaskPanelBehaviour _roleTab; + /// Scrolling increment + private const float Increment = 0.3f; + + /// Bounds for scrolling + private static FloatRange _bounds = new FloatRange(2.9f, 4.6f); + + /// + /// Add options scrolling (on the hud text) + /// + public static void OptionsScrollingLogic(HudManager __instance) + { + if (Input.GetKeyDown(KeyCode.Tab)) + { + if (ToHudStringPatch.CurrentPage >= ModdedOptionsManager.RegisteredMods.Count) + { + ToHudStringPatch.CurrentPage = 0; + } + else + { + ToHudStringPatch.CurrentPage += 1; + } + } + + var numPlayers = GameData.Instance ? GameData.Instance.PlayerCount : 10; + HudManager.Instance.GameSettings.text = GameOptionsManager.Instance.CurrentGameOptions.ToHudString(numPlayers); + var pos = __instance.GameSettings.transform.localPosition; + + if (!PlayerControl.LocalPlayer.CanMove) + { + return; + } + + if (ToHudStringPatch.CurrentPage == 0) + { + __instance.GameSettings.transform.localPosition = new Vector3(pos.x, _bounds.min, pos.z); + return; + } + + _bounds.max = __instance.GameSettings.rectTransform.sizeDelta.y + _bounds.min; + + var delta = Input.mouseScrollDelta.y switch + { + > 0f => -Increment, + < 0f => Increment, + _ => 0f + }; + + pos = new Vector3(pos.x, Mathf.Clamp(pos.y + delta, _bounds.min, _bounds.max), pos.z); + + __instance.GameSettings.transform.localPosition = pos; + } + [HarmonyPostfix, HarmonyPatch("Update")] public static void UpdatePostfix(HudManager __instance) { var local = PlayerControl.LocalPlayer; + if (LobbyBehaviour.Instance) + { + OptionsScrollingLogic(__instance); + } + if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance) { return; @@ -122,4 +181,4 @@ namespace MiraAPI.Patches } } } -} +} \ No newline at end of file diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs new file mode 100644 index 0000000..167fcf5 --- /dev/null +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -0,0 +1,109 @@ +using HarmonyLib; +using Il2CppSystem; +using MiraAPI.GameOptions; +using System.Linq; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace MiraAPI.Patches.Options; + +[HarmonyPatch(typeof(GameOptionsMenu))] +public static class GameOptionsMenuPatch +{ + /// + /// Change the OnValueChanged action for all custom game options + /// + [HarmonyPostfix, HarmonyPatch("Start")] + public static void StartPostfix(GameOptionsMenu __instance) + { + foreach (var customOption in ModdedOptionsManager.Options) + { + if (customOption.AdvancedRole is not null || !customOption.OptionBehaviour) + { + continue; + } + + customOption.OptionBehaviour.OnValueChanged = (Action)customOption.ValueChanged; + } + } + + private static GameSettingMenu menu; + + + /// + /// Set the position and offset of all custom game options + /// + [HarmonyPostfix, HarmonyPatch("Update")] + public static void UpdatePostfix(GameOptionsMenu __instance) + { + if (!menu) + { + menu = Object.FindObjectsOfType().First(); + } + + if (menu.RegularGameSettings.active || menu.RolesSettings.gameObject.active || menu.HideNSeekSettings.active) + { + return; + } + + var startOffset = 2.5f; + __instance.GetComponentInParent().ContentYBounds.max = startOffset + __instance.Children.Count * 0.5f; + + foreach (var option in ModdedOptionsManager.Options.Where(option => option.Group == null)) + { + if (!option.OptionBehaviour) continue; + + option.OptionBehaviour.enabled = option.Visible(); + option.OptionBehaviour.gameObject.SetActive(option.Visible()); + + if (option.Visible()) + { + startOffset -= 0.55f; + } + + var transform = option.OptionBehaviour.transform; + var optionPosition = transform.localPosition; + transform.localPosition = new Vector3(optionPosition.x, startOffset, optionPosition.z); + } + + foreach (var group in ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null)) + { + if (group.Header == null) + { + continue; + } + + group.Header.SetActive(group.GroupVisible()); + + if (group.GroupVisible()) + { + startOffset -= 0.5f; + } + + var position = group.Header.transform.localPosition; + group.Header.transform.localPosition = new Vector3(position.x, startOffset, position.z); + + foreach (var option in ModdedOptionsManager.Options.Where(x => x.Group == group)) + { + if (!option.OptionBehaviour) + { + continue; + } + + var enabled = group.GroupVisible() && option.Visible(); + + option.OptionBehaviour.enabled = enabled; + option.OptionBehaviour.gameObject.SetActive(enabled); + + if (group.GroupVisible() && option.Visible()) + { + startOffset -= 0.5f; + } + + var transform = option.OptionBehaviour.transform; + var optionPosition = transform.localPosition; + transform.localPosition = new Vector3(optionPosition.x, startOffset, optionPosition.z); + } + } + } +} \ No newline at end of file diff --git a/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs new file mode 100644 index 0000000..04ff2bb --- /dev/null +++ b/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs @@ -0,0 +1,68 @@ +using HarmonyLib; +using MiraAPI.API.GameOptions; +using MiraAPI.GameOptions; +using Reactor.Utilities; +using Reactor.Utilities.Extensions; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace MiraAPI.Patches.Options; + +[HarmonyPatch(typeof(GameSettingMenu))] +public static class GameSettingsMenuPatches +{ + private static GameObject _gameBtn; + private static GameObject _roleBtn; + + [HarmonyPrefix, HarmonyPatch("Start")] + public static void StartPrefix(GameSettingMenu __instance) + { + foreach (var tab in ModdedOptionsTab.CustomTabs) tab.gameObject.Destroy(); + foreach (var screen in ModdedOptionsTab.CustomScreens) screen.gameObject.Destroy(); + + ModdedOptionsTab.CustomScreens.Clear(); + ModdedOptionsTab.CustomTabs.Clear(); + + _gameBtn = __instance.transform.FindChild("Header/Tabs/GameTab").gameObject; + _roleBtn = __instance.transform.FindChild("Header/Tabs/RoleTab").gameObject; + + var numberOpt = __instance.RegularGameSettings.GetComponentInChildren(); + var toggleOpt = Object.FindObjectOfType(); + var stringOpt = __instance.RegularGameSettings.GetComponentInChildren(); + + foreach (var pair in ModdedOptionsManager.RegisteredMods) + { + var container = ModdedOptionsTab.InitializeForMod(pair.Value, __instance).transform; + + foreach (var group in ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == pair.Value)) + { + group.Header = ModdedOptionsTab.CreateHeader(toggleOpt, container, group.GroupName); + CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList()); + } + + CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, ModdedOptionsManager.Options.Where(x => x.Group is null).ToList()); + } + + if (!numberOpt || !toggleOpt || !stringOpt) + { + Logger.Error("OPTION PREFABS MISSING"); + } + } + + public static void CreateOptionsFor(GameSettingMenu __instance, ToggleOption togglePrefab, NumberOption numberPrefab, StringOption stringPrefab, Transform container, + List options) + { + foreach (IModdedOption option in options) + { + if (option.AdvancedRole is not null || option.OptionBehaviour) + { + continue; + } + + OptionBehaviour newOption = option.CreateOption(togglePrefab, numberPrefab, stringPrefab, container); + __instance.AllItems = __instance.AllItems.AddItem(newOption.transform).ToArray(); + } + } +} diff --git a/MiraAPI/Patches/Options/HudStringPatch.cs b/MiraAPI/Patches/Options/HudStringPatch.cs new file mode 100644 index 0000000..7daa59d --- /dev/null +++ b/MiraAPI/Patches/Options/HudStringPatch.cs @@ -0,0 +1,118 @@ +using AmongUs.GameOptions; +using HarmonyLib; +using MiraAPI.GameOptions; +using MiraAPI.Roles; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MiraAPI.Patches.Options; + +[HarmonyPatch(typeof(IGameOptionsExtensions), "ToHudString")] +public static class ToHudStringPatch +{ + public static int CurrentPage = 0; + /// + /// Generic method to add options + /// + public static void AddOptions(StringBuilder sb, + List options) + { + foreach (var opt in options) + { + sb.AppendLine(opt.GetHudStringText()); + } + } + + public static void Prefix() + { + if (GameManager.Instance is null || GameManager.Instance.IsHideAndSeek()) + { + return; + } + + foreach (var role in CustomRoleManager.CustomRoles.Values) + { + var customRole = role as ICustomRole; + if (customRole.IsGhostRole) + { + role.Role = RoleTypes.CrewmateGhost; + } + } + } + + /// + /// Update the HudOptions on the left of the screen if player is using Launchpad options + /// + public static void Postfix(IGameOptions __instance, ref string __result) + { + if (GameManager.Instance is null) + { + return; + } + + foreach (var pair in CustomRoleManager.CustomRoles) + { + var customRole = pair.Value as ICustomRole; + if (customRole.IsGhostRole) + { + pair.Value.Role = (RoleTypes)pair.Key; + } + } + + if (CurrentPage != 0) + { + IMiraConfig currentPlugin = ModdedOptionsManager.RegisteredMods.Values.ToArray()[CurrentPage - 1]; + if (currentPlugin == null) + { + CurrentPage = 0; + return; + } + + var sb = new StringBuilder($"{currentPlugin.TabSettings.Title}:\n"); + var groupsWithRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole != null && group.ParentMod == currentPlugin); + var groupsWithoutRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == currentPlugin); + + AddOptions(sb, + ModdedOptionsManager.Options.Where(option => option.Group == null && option.ParentMod == currentPlugin && option.Visible()).ToList() + ); + + foreach (var group in groupsWithoutRoles) + { + if (!group.GroupVisible()) + { + continue; + } + + sb.AppendLine($"\n{group.GroupName}"); + AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList()); + } + + var customOptionGroups = groupsWithRoles as ModdedOptionGroup[] ?? groupsWithRoles.ToArray(); + if (customOptionGroups.Any()) + { + sb.AppendLine("\nRoles"); + + foreach (var group in customOptionGroups) + { + if (!group.GroupVisible()) + { + continue; + } + + sb.AppendLine($"{group.GroupName}"); + AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList()); + sb.Append("\n"); + } + } + + __result = sb.ToString(); + } + + if (CurrentPage == 0) + { + __result = "Normal Options:\n" + __result; + } + __result += $"\nUse TAB to switch pages (Page {CurrentPage}/{ModdedOptionsManager.RegisteredMods.Count})"; + } +} \ No newline at end of file diff --git a/MiraAPI/Utilities/Assets/LoadableResourceAsset.cs b/MiraAPI/Utilities/Assets/LoadableResourceAsset.cs index 0288b6e..068ee13 100644 --- a/MiraAPI/Utilities/Assets/LoadableResourceAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableResourceAsset.cs @@ -1,5 +1,4 @@ -using System; -using UnityEngine; +using UnityEngine; namespace MiraAPI.Utilities.Assets { @@ -12,8 +11,15 @@ namespace MiraAPI.Utilities.Assets return _loadedAsset; } - return _loadedAsset = SpriteTools.LoadSpriteFromPath(resourcesFolder + name); - throw new Exception($"INVALID ASSET: {name}"); + try + { + return _loadedAsset = SpriteTools.LoadSpriteFromPath(resourcesFolder + name); + } + catch + { + Debug.LogError($"Not loading, invalid asset: {name}"); + return null; + } } } } From 0d746869de78c3a8abacbce425e5f6561f9ceb0e Mon Sep 17 00:00:00 2001 From: ang-xd Date: Sat, 18 May 2024 01:30:57 -0400 Subject: [PATCH 3/4] Enum options --- MiraAPI.Example/ExampleOptions.cs | 11 ++++ MiraAPI.Example/ExamplePlugin.cs | 2 +- .../Attributes/ModdedEnumOptionAttribute.cs | 33 ++++++++++++ .../OptionTypes/ModdedEnumOption.cs | 51 +++++++++++++++++++ MiraAPI/GameOptions/UI/ModdedOptionsTab.cs | 5 +- .../Options/GameSettingsMenuPatches.cs | 2 +- MiraAPI/Patches/Options/HudStringPatch.cs | 6 +-- 7 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs create mode 100644 MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs diff --git a/MiraAPI.Example/ExampleOptions.cs b/MiraAPI.Example/ExampleOptions.cs index 0ee7d49..0f04cf0 100644 --- a/MiraAPI.Example/ExampleOptions.cs +++ b/MiraAPI.Example/ExampleOptions.cs @@ -1,14 +1,25 @@ 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; } = true; [ModdedToggleOption("Use another thing")] public bool useAnotherThing { get; set; } = false; [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; } = 0; + [ModdedEnumOption("Enum test", typeof(TestEnum))] public TestEnum enumTest { get; set; } = TestEnum.Hello; + } + + public enum TestEnum + { + Hello, + Hi, + Bye } } diff --git a/MiraAPI.Example/ExamplePlugin.cs b/MiraAPI.Example/ExamplePlugin.cs index 3371c5d..007f0c4 100644 --- a/MiraAPI.Example/ExamplePlugin.cs +++ b/MiraAPI.Example/ExamplePlugin.cs @@ -18,7 +18,7 @@ public partial class ExamplePlugin : BasePlugin, IMiraConfig public static ExamplePlugin Instance { get; private set; } public ModdedOptionTabSettings TabSettings => new ModdedOptionTabSettings() { - Title = "Example Mod", + Title = "MiraAPI Example Mod", TabIcon = MiraAssets.Empty }; diff --git a/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs new file mode 100644 index 0000000..0b9d7e2 --- /dev/null +++ b/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs @@ -0,0 +1,33 @@ +using MiraAPI.GameOptions.OptionTypes; +using System; +using System.Reflection; + +namespace MiraAPI.GameOptions.Attributes +{ + public class ModdedEnumOptionAttribute : ModdedOptionAttribute + { + public Type EnumType; + public ModdedEnumOptionAttribute(string title, Type enumType, Type roleType = null) : base(title, roleType) + { + EnumType = enumType; + } + + public override IModdedOption CreateOption(object value, PropertyInfo property) + { + var opt = new ModdedEnumOption(Title, (int)value, EnumType, RoleType); + return opt; + } + + public override void SetValue(object value) + { + ModdedEnumOption opt = HolderOption as ModdedEnumOption; + opt.SetValue((int)value); + } + + public override object GetValue() + { + ModdedEnumOption opt = HolderOption as ModdedEnumOption; + return Enum.ToObject(EnumType.GetType(), opt.Value); + } + } +} diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs new file mode 100644 index 0000000..6a00e2d --- /dev/null +++ b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs @@ -0,0 +1,51 @@ +using Reactor.Localization.Utilities; +using System; +using System.Linq; +using UnityEngine; + +namespace MiraAPI.GameOptions.OptionTypes +{ + public class ModdedEnumOption : ModdedOption + { + public string[] Values; + + public ModdedEnumOption(string title, int defaultValue, Type enumType, System.Type roleType) : base(title, defaultValue, roleType) + { + Values = Enum.GetNames(enumType); + } + + public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container) + { + var stringOption = UnityEngine.Object.Instantiate(stringOpt, container); + + stringOption.name = Title; + stringOption.Title = StringName; + stringOption.Value = Value; + stringOption.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray(); + stringOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged; + stringOption.OnEnable(); + + OptionBehaviour = stringOption; + + return stringOption; + } + + public override string GetHudStringText() + { + return Title + ": " + Values[Value]; + } + + public override int GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour) + { + return optionBehaviour.GetInt(); + } + + public override void OnValueChanged(int newValue) + { + if (OptionBehaviour is null) return; + + StringOption opt = OptionBehaviour as StringOption; + opt.Value = newValue; + } + } +} diff --git a/MiraAPI/GameOptions/UI/ModdedOptionsTab.cs b/MiraAPI/GameOptions/UI/ModdedOptionsTab.cs index f9a09ef..cf5c6ae 100644 --- a/MiraAPI/GameOptions/UI/ModdedOptionsTab.cs +++ b/MiraAPI/GameOptions/UI/ModdedOptionsTab.cs @@ -65,12 +65,13 @@ public static class ModdedOptionsTab })); } - public static GameObject CreateHeader(ToggleOption toggleOpt, Transform container, string title) + public static GameObject CreateHeader(ToggleOption toggleOpt, Transform container, string title, Color color) { var header = Object.Instantiate(toggleOpt, container); header.Title = StringNames.None; header.TitleText.text = title; + header.TitleText.color = color; header.name = "Header"; var checkBox = header.transform.FindChild("CheckBox")?.gameObject; @@ -146,7 +147,7 @@ public static class ModdedOptionsTab var launchpadGroup = newSettings.transform.FindChild("GameGroup").gameObject; var text = launchpadGroup.transform.FindChild("Text").gameObject.GetComponent(); text.gameObject.GetComponent().Destroy(); - text.text = $"{config.TabSettings.Title}"; + text.text = $"{config.TabSettings.Title} Options"; return newSettings; } diff --git a/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs index 04ff2bb..333ddcd 100644 --- a/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingsMenuPatches.cs @@ -38,7 +38,7 @@ public static class GameSettingsMenuPatches foreach (var group in ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == pair.Value)) { - group.Header = ModdedOptionsTab.CreateHeader(toggleOpt, container, group.GroupName); + group.Header = ModdedOptionsTab.CreateHeader(toggleOpt, container, group.GroupName, group.GroupColor); CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList()); } diff --git a/MiraAPI/Patches/Options/HudStringPatch.cs b/MiraAPI/Patches/Options/HudStringPatch.cs index 7daa59d..f079c8f 100644 --- a/MiraAPI/Patches/Options/HudStringPatch.cs +++ b/MiraAPI/Patches/Options/HudStringPatch.cs @@ -69,7 +69,7 @@ public static class ToHudStringPatch return; } - var sb = new StringBuilder($"{currentPlugin.TabSettings.Title}:\n"); + var sb = new StringBuilder($"{currentPlugin.TabSettings.Title} Options:\n"); var groupsWithRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole != null && group.ParentMod == currentPlugin); var groupsWithoutRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == currentPlugin); @@ -84,7 +84,7 @@ public static class ToHudStringPatch continue; } - sb.AppendLine($"\n{group.GroupName}"); + sb.AppendLine($"\n{group.GroupColor.ToTextColor()}{group.GroupName}"); AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList()); } @@ -100,7 +100,7 @@ public static class ToHudStringPatch continue; } - sb.AppendLine($"{group.GroupName}"); + sb.AppendLine($"{group.GroupColor.ToTextColor()}{group.GroupName}"); AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList()); sb.Append("\n"); } From 32b63824876900c528b852695715ebe36ed3d408 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Sat, 18 May 2024 10:27:51 -0400 Subject: [PATCH 4/4] Neutral role support --- MiraAPI.Example/CustomRole.cs | 4 ++-- MiraAPI.Example/ModifierExample.cs | 9 +++++++++ .../Attributes/ModdedToggleOptionAttribute.cs | 2 -- MiraAPI/Patches/HudManagerPatches.cs | 1 + MiraAPI/Patches/Roles/TaskAdderPatch.cs | 2 +- MiraAPI/Roles/CustomRoleManager.cs | 2 +- MiraAPI/Roles/ICustomRole.cs | 18 +++++++++--------- MiraAPI/Roles/ModdedRoleTeams.cs | 9 +++++++++ 8 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 MiraAPI.Example/ModifierExample.cs create mode 100644 MiraAPI/Roles/ModdedRoleTeams.cs diff --git a/MiraAPI.Example/CustomRole.cs b/MiraAPI.Example/CustomRole.cs index 5c547c4..16568e9 100644 --- a/MiraAPI.Example/CustomRole.cs +++ b/MiraAPI.Example/CustomRole.cs @@ -6,10 +6,10 @@ namespace MiraAPI.Example [RegisterCustomRole(ExamplePlugin.Id)] public class CustomRole : CrewmateRole, ICustomRole { - public string RoleName => "Sussy Baka"; + public string RoleName => "Test Role"; public string RoleDescription => "Role decs?"; public string RoleLongDescription => "Extra role decs?"; public Color RoleColor => Palette.Orange; - public RoleTeamTypes Team => RoleTeamTypes.Crewmate; + public ModdedRoleTeams Team => ModdedRoleTeams.Neutral; } } diff --git a/MiraAPI.Example/ModifierExample.cs b/MiraAPI.Example/ModifierExample.cs new file mode 100644 index 0000000..537cf9f --- /dev/null +++ b/MiraAPI.Example/ModifierExample.cs @@ -0,0 +1,9 @@ +namespace MiraAPI.Example +{ + public class ModifierExample + { + //name, role specific, etc + + // mono behaviour stuff here (ModdedPlayerModifier inherits monobehaviour) + } +} diff --git a/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs index 5e0ea54..3459f9e 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs @@ -1,7 +1,6 @@ using MiraAPI.GameOptions.OptionTypes; using System; using System.Reflection; -using UnityEngine; namespace MiraAPI.GameOptions.Attributes { @@ -13,7 +12,6 @@ namespace MiraAPI.GameOptions.Attributes public override IModdedOption CreateOption(object value, PropertyInfo property) { - Debug.LogError(value.ToString()); var toggleOpt = new ModdedToggleOption(Title, (bool)value, RoleType); return toggleOpt; } diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index f69fbf8..7e65a24 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -85,6 +85,7 @@ namespace MiraAPI.Patches } //CustomGameModeManager.ActiveMode.HudUpdate(__instance); + if (local.Data.Role is null) return; if (local.Data.Role is ICustomRole customRole) { diff --git a/MiraAPI/Patches/Roles/TaskAdderPatch.cs b/MiraAPI/Patches/Roles/TaskAdderPatch.cs index 976d831..3984517 100644 --- a/MiraAPI/Patches/Roles/TaskAdderPatch.cs +++ b/MiraAPI/Patches/Roles/TaskAdderPatch.cs @@ -46,7 +46,7 @@ public static class TaskAdderPatch { if (__instance.role is ICustomRole lpRole) { - if (lpRole.IsOutcast) + if (lpRole.IsNeutral) { __instance.FileImage.color = Color.gray; } diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index e964814..e3c02a9 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -58,7 +58,7 @@ public static class CustomRoleManager } roleBehaviour.Role = (RoleTypes)roleId; - roleBehaviour.TeamType = customRole.Team; + roleBehaviour.TeamType = customRole.Team == ModdedRoleTeams.Neutral ? RoleTeamTypes.Crewmate : (RoleTeamTypes)customRole.Team; roleBehaviour.NameColor = customRole.RoleColor; roleBehaviour.StringName = CustomStringName.CreateAndRegister(customRole.RoleName); roleBehaviour.BlurbName = CustomStringName.CreateAndRegister(customRole.RoleDescription); diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 4a0a881..e76cec8 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -17,7 +17,7 @@ public interface ICustomRole Color RoleColor { get; } - RoleTeamTypes Team { get; } + ModdedRoleTeams Team { get; } LoadableAsset Icon => MiraAssets.Empty; @@ -25,17 +25,17 @@ public interface ICustomRole ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance{RoleName}"); - bool AffectedByLight => Team == RoleTeamTypes.Crewmate; + bool AffectedByLight => Team == ModdedRoleTeams.Crewmate; - bool CanGetKilled => Team == RoleTeamTypes.Crewmate; + bool CanGetKilled => Team == ModdedRoleTeams.Crewmate; - bool IsOutcast => false; + bool IsNeutral => Team == ModdedRoleTeams.Neutral; - bool CanKill => Team == RoleTeamTypes.Impostor; + bool CanKill => Team == ModdedRoleTeams.Impostor; - bool CanUseVent => Team == RoleTeamTypes.Impostor; + bool CanUseVent => Team == ModdedRoleTeams.Impostor; - bool TasksCount => Team == RoleTeamTypes.Crewmate; + bool TasksCount => Team == ModdedRoleTeams.Crewmate; bool IsGhostRole => false; @@ -45,7 +45,7 @@ public interface ICustomRole bool HideSettings => IsGhostRole; - RoleTypes GhostRole => Team == RoleTeamTypes.Crewmate ? RoleTypes.CrewmateGhost : RoleTypes.ImpostorGhost; + RoleTypes GhostRole => Team == ModdedRoleTeams.Crewmate ? RoleTypes.CrewmateGhost : RoleTypes.ImpostorGhost; void CreateOptions() { } @@ -55,7 +55,7 @@ public interface ICustomRole string GetCustomEjectionMessage(GameData.PlayerInfo player) { - return Team == RoleTeamTypes.Impostor ? $"{player.PlayerName} was The {RoleName}" : null; + return Team == ModdedRoleTeams.Impostor ? $"{player.PlayerName} was The {RoleName}" : null; } StringBuilder SetTabText() diff --git a/MiraAPI/Roles/ModdedRoleTeams.cs b/MiraAPI/Roles/ModdedRoleTeams.cs new file mode 100644 index 0000000..01913e5 --- /dev/null +++ b/MiraAPI/Roles/ModdedRoleTeams.cs @@ -0,0 +1,9 @@ +namespace MiraAPI.Roles +{ + public enum ModdedRoleTeams + { + Crewmate, + Impostor, + Neutral + } +}