diff --git a/MiraAPI.Example/ExamplePlugin.cs b/MiraAPI.Example/ExamplePlugin.cs index 0aed9c4..b20ec41 100644 --- a/MiraAPI.Example/ExamplePlugin.cs +++ b/MiraAPI.Example/ExamplePlugin.cs @@ -1,5 +1,4 @@ -using System.Linq; -using BepInEx; +using BepInEx; using BepInEx.Configuration; using BepInEx.Unity.IL2CPP; using HarmonyLib; diff --git a/MiraAPI.Example/Options/Modifiers/GeneralModifierOptions.cs b/MiraAPI.Example/Options/Modifiers/GeneralModifierOptions.cs index 1414799..0bcbcdc 100644 --- a/MiraAPI.Example/Options/Modifiers/GeneralModifierOptions.cs +++ b/MiraAPI.Example/Options/Modifiers/GeneralModifierOptions.cs @@ -9,7 +9,7 @@ public class GeneralModifierOptions : AbstractOptionGroup public override uint GroupPriority => 0; - public override bool ShowInModifiersMenu => true; + public override MenuCategory ParentMenu => MenuCategory.Modifiers; [ModdedToggleOption("Some Boolean Option")] public bool SomeBooleanOption { get; set; } = true; diff --git a/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs b/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs index 1473068..3c1fdeb 100644 --- a/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs +++ b/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs @@ -2,11 +2,17 @@ using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; using MiraAPI.Utilities; +using MiraAPI.Utilities.Assets; namespace MiraAPI.Example.Options.Roles; public class FreezerRoleSettings : AbstractOptionGroup { + public override OptionNotifConfiguration Configuration => new( + Palette.Blue, + TmpSpriteUtils.CreateSpriteAsset( + MiraAssets.ImpostorFile.LoadAsset(), + "ApiExample.Role.Impostor.FreezerRole")); public override string GroupName => "Custom Role"; [ModdedNumberOption("Freeze Duration", 1, 15, 1, MiraNumberSuffixes.Seconds)] diff --git a/MiraAPI.Example/Options/Roles/TeleporterOptions.cs b/MiraAPI.Example/Options/Roles/TeleporterOptions.cs index b6d02a4..d05569b 100644 --- a/MiraAPI.Example/Options/Roles/TeleporterOptions.cs +++ b/MiraAPI.Example/Options/Roles/TeleporterOptions.cs @@ -6,7 +6,7 @@ using MiraAPI.Utilities; namespace MiraAPI.Example.Options.Roles; -public class TeleporterOptions : AbstractOptionGroup +public class TeleporterOptions : AbstractRoleOptionGroup { public override string GroupName => "Teleporter"; diff --git a/MiraAPI.Example/Roles/ChameleonRole.cs b/MiraAPI.Example/Roles/ChameleonRole.cs index c1d7779..f601551 100644 --- a/MiraAPI.Example/Roles/ChameleonRole.cs +++ b/MiraAPI.Example/Roles/ChameleonRole.cs @@ -1,6 +1,7 @@ using AmongUs.GameOptions; using MiraAPI.Patches.Stubs; using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; using Reactor.Utilities; using TMPro; using UnityEngine; @@ -20,6 +21,8 @@ public class ChameloenRole : CrewmateRole, ICustomRole public CustomRoleConfiguration Configuration => new(this) { OptionsScreenshot = ExampleAssets.Banner, + Icon = MiraAssets.CrewmateFile, + IconTmp = TmpSpriteUtils.CreateSpriteAsset(MiraAssets.CrewmateFile.LoadAsset(), "ApiExample.Role.Impostor.ChameleonRole"), IntroSound = CustomRoleUtils.GetIntroSound(RoleTypes.Shapeshifter), }; diff --git a/MiraAPI.Example/Roles/FreezerRole.cs b/MiraAPI.Example/Roles/FreezerRole.cs index bef24d2..5a7f4bd 100644 --- a/MiraAPI.Example/Roles/FreezerRole.cs +++ b/MiraAPI.Example/Roles/FreezerRole.cs @@ -1,4 +1,5 @@ using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; using UnityEngine; namespace MiraAPI.Example.Roles; @@ -13,6 +14,8 @@ public class FreezerRole : ImpostorRole, ICustomRole public CustomRoleConfiguration Configuration => new CustomRoleConfiguration(this) { + Icon = MiraAssets.ImpostorFile, + IconTmp = TmpSpriteUtils.CreateSpriteAsset(MiraAssets.ImpostorFile.LoadAsset(), "ApiExample.Role.Impostor.FreezerRole"), OptionsScreenshot = ExampleAssets.Banner, MaxRoleCount = 2, }; diff --git a/MiraAPI/Events/MiraEventManager.cs b/MiraAPI/Events/MiraEventManager.cs index 005415b..30ffb74 100644 --- a/MiraAPI/Events/MiraEventManager.cs +++ b/MiraAPI/Events/MiraEventManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Reflection; -using Reactor.Utilities; namespace MiraAPI.Events; diff --git a/MiraAPI/Events/Vanilla/Meeting/Voting/AfterVoteEvent.cs b/MiraAPI/Events/Vanilla/Meeting/Voting/AfterVoteEvent.cs index 95583f3..60a3d36 100644 --- a/MiraAPI/Events/Vanilla/Meeting/Voting/AfterVoteEvent.cs +++ b/MiraAPI/Events/Vanilla/Meeting/Voting/AfterVoteEvent.cs @@ -1,6 +1,4 @@ -using MiraAPI.Voting; - -namespace MiraAPI.Events.Vanilla.Meeting.Voting; +namespace MiraAPI.Events.Vanilla.Meeting.Voting; /// /// Event that is invoked after the local player successfully votes a player or skips. This event is not cancelable. diff --git a/MiraAPI/Events/Vanilla/Meeting/Voting/BeforeVoteEvent.cs b/MiraAPI/Events/Vanilla/Meeting/Voting/BeforeVoteEvent.cs index 74fde38..e6b28f9 100644 --- a/MiraAPI/Events/Vanilla/Meeting/Voting/BeforeVoteEvent.cs +++ b/MiraAPI/Events/Vanilla/Meeting/Voting/BeforeVoteEvent.cs @@ -1,6 +1,4 @@ -using MiraAPI.Voting; - -namespace MiraAPI.Events.Vanilla.Meeting.Voting; +namespace MiraAPI.Events.Vanilla.Meeting.Voting; /// /// Event that is invoked before the local player confirms a vote on a player or when skipping. This event is cancelable. diff --git a/MiraAPI/GameEnd/GameOverManager.cs b/MiraAPI/GameEnd/GameOverManager.cs index eb39835..28d2f48 100644 --- a/MiraAPI/GameEnd/GameOverManager.cs +++ b/MiraAPI/GameEnd/GameOverManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Reactor.Utilities; namespace MiraAPI.GameEnd; diff --git a/MiraAPI/GameModes/CustomGameModeManager.cs b/MiraAPI/GameModes/CustomGameModeManager.cs index 7e274c4..3edd028 100644 --- a/MiraAPI/GameModes/CustomGameModeManager.cs +++ b/MiraAPI/GameModes/CustomGameModeManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Reactor.Utilities; namespace MiraAPI.GameModes; diff --git a/MiraAPI/GameOptions/AbstractOptionGroup.cs b/MiraAPI/GameOptions/AbstractOptionGroup.cs index f21a3cb..f8f0877 100644 --- a/MiraAPI/GameOptions/AbstractOptionGroup.cs +++ b/MiraAPI/GameOptions/AbstractOptionGroup.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using MiraAPI.Modifiers; +using MiraAPI.Roles; using UnityEngine; namespace MiraAPI.GameOptions; @@ -40,7 +42,7 @@ public abstract class AbstractOptionGroup /// /// Gets a value indicating which menu the group is in. /// - public virtual MenuCategory ParentMenu => MenuCategory.Roles; + public virtual MenuCategory ParentMenu => MenuCategory.Game; /// /// Gets the function that determines whether the group should be visible or not. @@ -58,9 +60,16 @@ public abstract class AbstractOptionGroup /// public virtual uint GroupPriority => uint.MaxValue; + /// + /// Gets the default notification settings for the group. + /// + public virtual OptionNotifConfiguration Configuration => new(new Color(0.7333f, 0.7333f, 0.7333f, 1)); + internal bool AllOptionsHidden { get; set; } internal CategoryHeaderMasked? Header { get; set; } + + internal Coroutine? CreationCoroutine { get; set; } } /// @@ -72,9 +81,7 @@ public abstract class AbstractOptionGroup : AbstractOptionGroup where T : IOp /// public override Type OptionableType => typeof(T); - /// - /// Gets a value indicating which menu the group is in. - /// + /// public override MenuCategory ParentMenu { get @@ -93,6 +100,33 @@ public abstract class AbstractOptionGroup : AbstractOptionGroup where T : IOp } } +/// +/// Base class for option groups. An option group is a collection of options that are displayed together in the options menu. +/// +/// The custom role that the group is for. +public abstract class AbstractRoleOptionGroup() : AbstractOptionGroup where T : ICustomRole +{ + /// + public override Type OptionableType => typeof(T); + + /// + public override MenuCategory ParentMenu => MenuCategory.Roles; + + /// + public override OptionNotifConfiguration Configuration + { + get + { + var role = CustomRoleManager.CustomMiraRoles.FirstOrDefault(x => x.GetType() == OptionableType); + if (role == null) + { + return new(new Color(0.7333f, 0.7333f, 0.7333f, 1)); + } + return new(role.RoleColor, role.Configuration.IconTmp); + } + } +} + /// /// Menu categories for option groups. /// diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index 907ab7d..646c22b 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -21,6 +21,16 @@ public interface IModdedOption /// string Title { get; set; } + /// + /// Gets or sets the parent of the option. + /// + AbstractOptionGroup ParentGroup { get; set; } + + /// + /// Gets the notification settings for the option. + /// + OptionNotifConfiguration Configuration { get; } + /// /// Gets the for the option, used for localization. /// diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index 3e6e04e..7dd26ee 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -12,6 +12,8 @@ using MiraAPI.PluginLoading; using MiraAPI.Utilities; using Reactor.Networking.Rpc; using Reactor.Utilities; +using TMPro; +using UnityEngine; namespace MiraAPI.GameOptions; @@ -30,6 +32,36 @@ public static class ModdedOptionsManager internal static uint NextId => _nextId++; private static uint _nextId = 1; + public static void AddSettingsChangeMessage(NotificationPopper notif, StringNames key, string value, Color textColor, TMP_SpriteAsset? sprite, bool playSound = true) + { + var item = string.Empty; + var text = textColor.ToTextColor(); + if (sprite != null) + { + item = TranslationController.Instance.GetString( + StringNames.LobbyChangeSettingNotification, + string.Concat( + "", + text, + TranslationController.Instance.GetString(key), + ""), + "" + value + "" + ); + } + else + { + item = TranslationController.Instance.GetString( + StringNames.LobbyChangeSettingNotification, + "" + + text + + TranslationController.Instance.GetString(key) + "", + "" + value + ""); + } + notif.SettingsChangeMessageLogic(key, item, playSound); + } + internal static bool RegisterGroup(Type type, MiraPluginInfo pluginInfo) { if (Activator.CreateInstance(type) is not AbstractOptionGroup group) @@ -123,6 +155,7 @@ public static class ModdedOptionsManager option.ConfigDefinition = new ConfigDefinition(groupName, propertyName); + option.ParentGroup = group; option.ParentMod = pluginInfo.MiraPlugin; pluginInfo.InternalOptions.Add(option); ModdedOptions.Add(option.Id, option); diff --git a/MiraAPI/GameOptions/OptionNotifConfiguration.cs b/MiraAPI/GameOptions/OptionNotifConfiguration.cs new file mode 100644 index 0000000..e16106f --- /dev/null +++ b/MiraAPI/GameOptions/OptionNotifConfiguration.cs @@ -0,0 +1,53 @@ +using System; +using Il2CppInterop.Runtime.Attributes; +using TMPro; +using UnityEngine; +namespace MiraAPI.GameOptions; + +/// +/// Used to configure the specific visuals for option notifications. +/// +public record struct OptionNotifConfiguration +{ +#pragma warning disable S1133 + [Obsolete("Default constructor is not supported")] +#pragma warning restore S1133 +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + public OptionNotifConfiguration() +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + { + throw new NotImplementedException("Default constructor is not supported."); + } + + /// + /// Initializes a new instance of the struct based on a group. + /// + /// The in which you are configuring. + public OptionNotifConfiguration(AbstractOptionGroup group) + { + PopUpIconTmp = group.Configuration.PopUpIconTmp; + PopUpTextColor = group.Configuration.PopUpTextColor; + } + + /// + /// Initializes a new instance of the struct from scratch. + /// + /// The text for the configuration. + /// The icon for the configuration. + public OptionNotifConfiguration(Color color, TMP_SpriteAsset asset = null!) + { + PopUpIconTmp = asset; + PopUpTextColor = color; + } + + /// + /// Gets or sets the for the icon on the options pop-up. + /// + public TMP_SpriteAsset PopUpIconTmp { get; set; } + + /// + /// Gets or sets the for the text on the options pop-up. + /// + [HideFromIl2Cpp] + public Color PopUpTextColor { get; set; } +} diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs index 1f92418..30809bd 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs @@ -90,7 +90,13 @@ public class ModdedEnumOption : ModdedOption /// protected override void OnValueChanged(int newValue) { - HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false); + ModdedOptionsManager.AddSettingsChangeMessage( + HudManager.Instance.Notifier, + StringName, + Data.GetValueString(newValue), + Configuration.PopUpTextColor, + Configuration.PopUpIconTmp, + false); if (!OptionBehaviour) { return; @@ -183,7 +189,13 @@ public class ModdedEnumOption : ModdedOption where T : Enum /// protected override void OnValueChanged(T newValue) { - HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(Convert.ToInt32(newValue, NumberFormatInfo.InvariantInfo)), false); + ModdedOptionsManager.AddSettingsChangeMessage( + HudManager.Instance.Notifier, + StringName, + Data.GetValueString(Convert.ToInt32(newValue, NumberFormatInfo.InvariantInfo)), + Configuration.PopUpTextColor, + Configuration.PopUpIconTmp, + false); if (!OptionBehaviour) { return; diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs index 1a5ef33..87f4ca9 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs @@ -225,9 +225,12 @@ public class ModdedNumberOption : ModdedOption protected override void OnValueChanged(float newValue) { Value = Mathf.Clamp(newValue, Min, Max); - HudManager.Instance.Notifier.AddSettingsChangeMessage( + ModdedOptionsManager.AddSettingsChangeMessage( + HudManager.Instance.Notifier, StringName, - Data.GetValueString(Value), + Data.GetValueString(newValue), + Configuration.PopUpTextColor, + Configuration.PopUpIconTmp, false); if (OptionBehaviour is NumberOption opt) diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index 206894d..4300c4f 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -4,7 +4,6 @@ using MiraAPI.Networking; using MiraAPI.PluginLoading; using Reactor.Localization.Utilities; using Reactor.Networking.Rpc; -using Reactor.Utilities; using UnityEngine; namespace MiraAPI.GameOptions.OptionTypes; @@ -190,4 +189,10 @@ public abstract class ModdedOption : IModdedOption { return option.Value; } + + /// + public AbstractOptionGroup ParentGroup { get; set; } + + /// + public virtual OptionNotifConfiguration Configuration => new(ParentGroup); } diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs index f6516cd..db397bd 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs @@ -126,7 +126,13 @@ public class ModdedPlayerOption : ModdedOption /// protected override void OnValueChanged(int newValue) { - HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false); + ModdedOptionsManager.AddSettingsChangeMessage( + HudManager.Instance.Notifier, + StringName, + Data.GetValueString(newValue), + Configuration.PopUpTextColor, + Configuration.PopUpIconTmp, + false); if (!OptionBehaviour) { return; diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs index 0b13f97..55b69b9 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs @@ -71,7 +71,13 @@ public class ModdedToggleOption : ModdedOption /// protected override void OnValueChanged(bool newValue) { - HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, newValue ? "On" : "Off", false); + ModdedOptionsManager.AddSettingsChangeMessage( + HudManager.Instance.Notifier, + StringName, + newValue ? "On" : "Off", + Configuration.PopUpTextColor, + Configuration.PopUpIconTmp, + false); if (!OptionBehaviour) { return; diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs index f5cf521..4c5df0c 100644 --- a/MiraAPI/Hud/CustomActionButton.cs +++ b/MiraAPI/Hud/CustomActionButton.cs @@ -7,7 +7,6 @@ using MiraAPI.LocalSettings; using MiraAPI.Patches; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; -using Reactor.Utilities; using Rewired; using TMPro; using UnityEngine; diff --git a/MiraAPI/Hud/CustomButtonManager.cs b/MiraAPI/Hud/CustomButtonManager.cs index 5a1ba3b..70ca14b 100644 --- a/MiraAPI/Hud/CustomButtonManager.cs +++ b/MiraAPI/Hud/CustomButtonManager.cs @@ -4,7 +4,6 @@ using System.Collections.ObjectModel; using System.Reflection; using MiraAPI.Events.Mira; using MiraAPI.PluginLoading; -using UnityEngine; namespace MiraAPI.Hud; diff --git a/MiraAPI/Hud/MultiTargetButton.cs b/MiraAPI/Hud/MultiTargetButton.cs index 12458d4..d52d845 100644 --- a/MiraAPI/Hud/MultiTargetButton.cs +++ b/MiraAPI/Hud/MultiTargetButton.cs @@ -1,6 +1,5 @@ using System.Linq; using HarmonyLib; -using Reactor.Utilities.Extensions; using UnityEngine; namespace MiraAPI.Hud; diff --git a/MiraAPI/Keybinds/KeybindManager.cs b/MiraAPI/Keybinds/KeybindManager.cs index 207fab5..042322a 100644 --- a/MiraAPI/Keybinds/KeybindManager.cs +++ b/MiraAPI/Keybinds/KeybindManager.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using Il2CppInterop.Runtime.Attributes; using MiraAPI.Utilities; -using Reactor.Utilities; using Rewired; namespace MiraAPI.Keybinds; diff --git a/MiraAPI/Keybinds/VanillaKeybind.cs b/MiraAPI/Keybinds/VanillaKeybind.cs index 594e583..5f34288 100644 --- a/MiraAPI/Keybinds/VanillaKeybind.cs +++ b/MiraAPI/Keybinds/VanillaKeybind.cs @@ -1,5 +1,4 @@ using System.Globalization; -using Rewired; namespace MiraAPI.Keybinds; diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingsButtonAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingsButtonAttribute.cs index 043a81d..83a3955 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingsButtonAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingsButtonAttribute.cs @@ -1,5 +1,4 @@ using System; -using BepInEx.Configuration; namespace MiraAPI.LocalSettings.Attributes; diff --git a/MiraAPI/LocalSettings/LocalSettingsButton.cs b/MiraAPI/LocalSettings/LocalSettingsButton.cs index 6f1fa57..5a1f371 100644 --- a/MiraAPI/LocalSettings/LocalSettingsButton.cs +++ b/MiraAPI/LocalSettings/LocalSettingsButton.cs @@ -1,6 +1,5 @@ using System; using MiraAPI.Utilities; -using Reactor.Localization.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; diff --git a/MiraAPI/LocalSettings/LocalSettingsManager.cs b/MiraAPI/LocalSettings/LocalSettingsManager.cs index 2f6fc16..874e258 100644 --- a/MiraAPI/LocalSettings/LocalSettingsManager.cs +++ b/MiraAPI/LocalSettings/LocalSettingsManager.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Reflection; using BepInEx.Unity.IL2CPP; -using MiraAPI.GameOptions; -using Reactor.Utilities; namespace MiraAPI.LocalSettings; @@ -14,6 +12,7 @@ public static class LocalSettingsManager { internal static readonly Dictionary TypeToTab = []; internal static readonly List Tabs = []; + internal static readonly List AvailableTabs = []; internal static bool RegisterTab(Type type, BasePlugin pluginInfo) { @@ -29,6 +28,10 @@ public static class LocalSettingsManager } Tabs.Add(tab); + if (tab.ShouldCreateButton) + { + AvailableTabs.Add(tab); + } TypeToTab.Add(type, tab); typeof(LocalSettingsTabSingleton<>).MakeGenericType(type) diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index 09e411a..6432b18 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -8,7 +8,6 @@ using MiraAPI.LocalSettings; using MiraAPI.Modifiers.ModifierDisplay; using MiraAPI.Modifiers.Types; using MiraAPI.Patches.Roles; -using Reactor.Utilities; using Reactor.Utilities.Attributes; using UnityEngine; diff --git a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs index 4a6411f..e93f477 100644 --- a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs +++ b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs @@ -4,7 +4,6 @@ using System.Linq; using HarmonyLib; using Il2CppInterop.Runtime.Attributes; using MiraAPI.LocalSettings; -using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Utilities.Attributes; using Reactor.Utilities.Extensions; @@ -138,7 +137,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr) internal void DestroyComponent(ModifierUiComponent component) { _modifiers.Remove(component.Modifier!); - component.gameObject.DeepDestroy(); + component.gameObject.Destroy(); RefreshModifiers(); } @@ -208,7 +207,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr) continue; } - mod.Value.gameObject.DeepDestroy(); + mod.Value.gameObject.Destroy(); _modifiers.Remove(mod.Key); } diff --git a/MiraAPI/Modifiers/ModifierExtensions.cs b/MiraAPI/Modifiers/ModifierExtensions.cs index 20f08be..e70a26a 100644 --- a/MiraAPI/Modifiers/ModifierExtensions.cs +++ b/MiraAPI/Modifiers/ModifierExtensions.cs @@ -5,7 +5,6 @@ using MiraAPI.Networking; using MiraAPI.Networking.Modifiers; using Reactor.Networking.Attributes; using Reactor.Networking.Rpc; -using Reactor.Utilities; namespace MiraAPI.Modifiers; diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs index 877e43b..c2688a0 100644 --- a/MiraAPI/Modifiers/ModifierManager.cs +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -6,7 +6,6 @@ using MiraAPI.Modifiers.Types; using MiraAPI.PluginLoading; using MiraAPI.Roles; using MiraAPI.Utilities; -using Reactor.Utilities; using Reactor.Utilities.Extensions; using Random = System.Random; @@ -153,7 +152,7 @@ public static class ModifierManager } var candidates = availablePlayers - .Where(x => IsGameModifierValid(x, modifier, modifier.TypeId)) + .Where(x => IsGameModifierPostCheck(x, modifier, modifier.TypeId)) .ToList(); if (candidates.Count == 0) @@ -179,7 +178,12 @@ public static class ModifierManager private static bool IsGameModifierValid(PlayerControl player, GameModifier modifier, uint modifierId) { return (player.Data.Role is not ICustomRole role || role.IsModifierApplicable(modifier)) && - modifier.IsModifierValidOn(player.Data.Role) && !player.HasModifier(modifierId) && + !player.HasModifier(modifierId) && modifier.IsModifierValidOn(player.Data.Role) && modifier.CanSpawnOnCurrentMode(); } + private static bool IsGameModifierPostCheck(PlayerControl player, GameModifier modifier, uint modifierId) + { + return (player.Data.Role is not ICustomRole role || role.IsModifierApplicable(modifier)) && + !player.HasModifier(modifierId) && modifier.IsModifierValidOnPostCheck(player.Data.Role); + } } diff --git a/MiraAPI/Modifiers/Types/GameModifier.cs b/MiraAPI/Modifiers/Types/GameModifier.cs index b58f8e0..f5ae883 100644 --- a/MiraAPI/Modifiers/Types/GameModifier.cs +++ b/MiraAPI/Modifiers/Types/GameModifier.cs @@ -33,6 +33,13 @@ public abstract class GameModifier : BaseModifier /// if the modifier is valid on the role, otherwise . public virtual bool IsModifierValidOn(RoleBehaviour role) => true; + /// + /// Determines whether the modifier is valid on a role, which runs after players are determined but before the modifiers are given. + /// + /// The role to be checked. + /// if the modifier is valid on the role, otherwise . + public virtual bool IsModifierValidOnPostCheck(RoleBehaviour role) => IsModifierValidOn(role); + /// /// Determines whether the player won the game with this modifier. The modifier with the highest priority will override other modifiers. /// diff --git a/MiraAPI/Modifiers/Types/TimedModifier.cs b/MiraAPI/Modifiers/Types/TimedModifier.cs index 6fe1c1d..472b3cf 100644 --- a/MiraAPI/Modifiers/Types/TimedModifier.cs +++ b/MiraAPI/Modifiers/Types/TimedModifier.cs @@ -1,5 +1,4 @@ using System; -using Reactor.Utilities; using UnityEngine; namespace MiraAPI.Modifiers.Types; diff --git a/MiraAPI/Networking/CustomGameOverRpc.cs b/MiraAPI/Networking/CustomGameOverRpc.cs index f7df4a7..6475d6a 100644 --- a/MiraAPI/Networking/CustomGameOverRpc.cs +++ b/MiraAPI/Networking/CustomGameOverRpc.cs @@ -4,7 +4,6 @@ using InnerNet; using MiraAPI.GameEnd; using Reactor.Networking.Attributes; using Reactor.Networking.Rpc; -using Reactor.Utilities; namespace MiraAPI.Networking; diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs index 9a6ff0b..7009819 100644 --- a/MiraAPI/Networking/CustomMurderRpc.cs +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -10,9 +10,6 @@ using MiraAPI.Events.Vanilla.Gameplay; using MiraAPI.Utilities; using Reactor.Networking.Attributes; using Reactor.Networking.Rpc; -using Reactor.Utilities; -using Reactor.Utilities.Extensions; -using Rewired; using UnityEngine; using Object = UnityEngine.Object; diff --git a/MiraAPI/Patches/Events/ReportBodyEventPatch.cs b/MiraAPI/Patches/Events/ReportBodyEventPatch.cs index 305311e..3eb1c29 100644 --- a/MiraAPI/Patches/Events/ReportBodyEventPatch.cs +++ b/MiraAPI/Patches/Events/ReportBodyEventPatch.cs @@ -4,7 +4,6 @@ using InnerNet; using MiraAPI.Events; using MiraAPI.Events.Vanilla.Meeting; using MiraAPI.Utilities; -using Reactor.Utilities; namespace MiraAPI.Patches.Events; diff --git a/MiraAPI/Patches/Events/VanillaButtonPatches.cs b/MiraAPI/Patches/Events/VanillaButtonPatches.cs index 490e401..71df73f 100644 --- a/MiraAPI/Patches/Events/VanillaButtonPatches.cs +++ b/MiraAPI/Patches/Events/VanillaButtonPatches.cs @@ -1,9 +1,7 @@ -using System.Linq; -using AmongUs.GameOptions; +using AmongUs.GameOptions; using HarmonyLib; using MiraAPI.Events; using MiraAPI.Events.Vanilla.Gameplay; -using MiraAPI.Events.Vanilla.Usables; namespace MiraAPI.Patches.Events; diff --git a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs index 3e4adc9..fa91e7b 100644 --- a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs +++ b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs @@ -254,6 +254,12 @@ public static class TaskAdderPatches .ToArray() .Any(x => x.FolderName == child.FolderName); + [HarmonyPostfix] + [HarmonyPatch(nameof(TaskAdderGame.OnDisable))] + public static void ClosePatch() + { + Utilities.Extensions.ClearGarbageCollector(); + } // yes it might be crazy patching the entire method, but i tried so many other methods and only this works :cry: // true -chip [HarmonyPrefix] @@ -278,7 +284,6 @@ public static class TaskAdderPatches __instance.ActiveItems.ToArray().Do(x => x.gameObject.DeepDestroy(false)); __instance.ActiveItems.Clear(); - Utilities.Extensions.ClearGarbageCollector(); float num = 0f; float num2 = 0f; diff --git a/MiraAPI/Patches/Hud/ButtonResetPatches.cs b/MiraAPI/Patches/Hud/ButtonResetPatches.cs index 5e46048..ab0dc32 100644 --- a/MiraAPI/Patches/Hud/ButtonResetPatches.cs +++ b/MiraAPI/Patches/Hud/ButtonResetPatches.cs @@ -1,6 +1,5 @@ using HarmonyLib; using MiraAPI.Hud; -using Reactor.Utilities; namespace MiraAPI.Patches.Hud; diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index a6cd68b..a508ccf 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -148,7 +148,7 @@ public static class HudManagerPatches foreach (var t in BottomLeft.GetComponentsInChildren(true)) { - t.gameObject.DeepDestroy(false); + t.gameObject.Destroy(); } Utilities.Extensions.ClearGarbageCollector(); diff --git a/MiraAPI/Patches/IntroCutscenePatches.cs b/MiraAPI/Patches/IntroCutscenePatches.cs index 7d52287..4ee990c 100644 --- a/MiraAPI/Patches/IntroCutscenePatches.cs +++ b/MiraAPI/Patches/IntroCutscenePatches.cs @@ -1,12 +1,10 @@ -using System.Linq; -using System.Reflection; +using System.Reflection; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using MiraAPI.Events; using MiraAPI.Events.Vanilla.Gameplay; using MiraAPI.Roles; using MiraAPI.Utilities; -using Reactor.Utilities; namespace MiraAPI.Patches; diff --git a/MiraAPI/Patches/Keybinds/ControlMapperPatches.cs b/MiraAPI/Patches/Keybinds/ControlMapperPatches.cs index f0c51c0..6666a2c 100644 --- a/MiraAPI/Patches/Keybinds/ControlMapperPatches.cs +++ b/MiraAPI/Patches/Keybinds/ControlMapperPatches.cs @@ -1,13 +1,8 @@ using System.Linq; using HarmonyLib; using MiraAPI.Keybinds; -using MiraAPI.LocalSettings; -using Reactor.Utilities; -using Reactor.Utilities.Extensions; using Rewired.UI.ControlMapper; -using TMPro; using UnityEngine; -using UnityEngine.Events; using UnityEngine.UI; namespace MiraAPI.Patches.Keybinds; diff --git a/MiraAPI/Patches/Keybinds/KeybindsPatch.cs b/MiraAPI/Patches/Keybinds/KeybindsPatch.cs index c08a47f..9c1034f 100644 --- a/MiraAPI/Patches/Keybinds/KeybindsPatch.cs +++ b/MiraAPI/Patches/Keybinds/KeybindsPatch.cs @@ -1,6 +1,5 @@ using HarmonyLib; using MiraAPI.Keybinds; -using MonoMod.Utils; using Rewired; namespace MiraAPI.Patches.Keybinds; diff --git a/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs index ab8ed2c..a8cab15 100644 --- a/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs +++ b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs @@ -3,7 +3,6 @@ using System.Linq; using HarmonyLib; using MiraAPI.LocalSettings; using MiraAPI.Utilities.Assets; -using Reactor.Utilities; using Reactor.Utilities.Extensions; using UnityEngine; using UnityEngine.Events; @@ -70,7 +69,7 @@ public static class OptionsMenuPatches int i = 0; int tabIdx = 0; int page = 1; - foreach (var settings in LocalSettingsManager.Tabs) + foreach (var settings in LocalSettingsManager.AvailableTabs) { var tab = settings.CreateTab(__instance); if (settings.ShouldCreateButton) @@ -165,12 +164,12 @@ public static class OptionsMenuPatches public static bool OpenTabGroupPrefix(OptionsMenuBehaviour __instance, ref int index) { __instance.Tabs.ToList().ForEach(x => x.Close()); // Close all vanilla tabs - LocalSettingsManager.Tabs.ForEach(CustomClose); // Close all mod tabs + LocalSettingsManager.AvailableTabs.ForEach(CustomClose); // Close all mod tabs // Tabs with index 10 and above are the mod settings tabs if (index >= 10) { - CustomOpen(LocalSettingsManager.Tabs[index - 10]); + CustomOpen(LocalSettingsManager.AvailableTabs[index - 10]); return false; } @@ -184,7 +183,7 @@ public static class OptionsMenuPatches [HarmonyPatch(nameof(OptionsMenuBehaviour.Open))] public static void OpenPostfix(OptionsMenuBehaviour __instance) { - LocalSettingsManager.Tabs.ForEach(CustomClose); + LocalSettingsManager.AvailableTabs.ForEach(CustomClose); currentPage = 1; UpdatePages(); } diff --git a/MiraAPI/Patches/Menu/HatsTabPatches.cs b/MiraAPI/Patches/Menu/HatsTabPatches.cs index 3ad0fa3..43d96c3 100644 --- a/MiraAPI/Patches/Menu/HatsTabPatches.cs +++ b/MiraAPI/Patches/Menu/HatsTabPatches.cs @@ -100,10 +100,9 @@ public static class HatsTabPatches if (loadRoutine != null) Coroutines.Stop(loadRoutine); hatIndex = 0; - foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy(); __instance.ColorChips.Clear(); __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy(false)); - Utilities.Extensions.ClearGarbageCollector(); var groupNameText = __instance.GetComponentInChildren(false); var group = sortedHats.Where(x => x.Key == storeNames[page]); diff --git a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs index bd9a6ce..6e96d5f 100644 --- a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs +++ b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs @@ -81,10 +81,9 @@ public static class NameplatesTabPatches private static void GenerateHats(NameplatesTab __instance, int page) { - foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy(); __instance.ColorChips.Clear(); __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy(false)); - Utilities.Extensions.ClearGarbageCollector(); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/SkinsTabPatches.cs b/MiraAPI/Patches/Menu/SkinsTabPatches.cs index 5395ff6..e1c97a5 100644 --- a/MiraAPI/Patches/Menu/SkinsTabPatches.cs +++ b/MiraAPI/Patches/Menu/SkinsTabPatches.cs @@ -83,10 +83,9 @@ public static class SkinsTabPatches private static void GenerateHats(SkinsTab __instance, int page) { - foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy(); __instance.ColorChips.Clear(); __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy(false)); - Utilities.Extensions.ClearGarbageCollector(); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/VisorsTabPatches.cs b/MiraAPI/Patches/Menu/VisorsTabPatches.cs index ed5f103..a1627cf 100644 --- a/MiraAPI/Patches/Menu/VisorsTabPatches.cs +++ b/MiraAPI/Patches/Menu/VisorsTabPatches.cs @@ -80,10 +80,9 @@ public static class VisorsTabPatches private static void GenerateHats(VisorsTab __instance, int page) { - foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy(); __instance.ColorChips.Clear(); __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy(false)); - Utilities.Extensions.ClearGarbageCollector(); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index 7b5e7a8..b50b430 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -1,8 +1,8 @@ using System; +using System.Collections; using System.Linq; -using AmongUs.GameOptions; +using BepInEx.Unity.IL2CPP.Utils.Collections; using HarmonyLib; -using Il2CppInterop.Runtime.Attributes; using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.Presets; @@ -35,6 +35,13 @@ internal static class GameOptionsMenuPatch HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = true; } + [HarmonyPostfix] + [HarmonyPatch(nameof(GameOptionsMenu.CloseMenu))] + public static void FullMenuClosePatch(GameOptionsMenu __instance) + { + Utilities.Extensions.ClearGarbageCollector(); + } + [HarmonyPostfix] [HarmonyPatch(nameof(GameOptionsMenu.Update))] // ReSharper disable once InconsistentNaming @@ -63,7 +70,7 @@ internal static class GameOptionsMenuPatch var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups .Where(x => x.OptionableType == null && - x.ParentMenu == MenuCategory.Roles) ?? []; + x.ParentMenu == MenuCategory.Game) ?? []; foreach (var group in filteredGroups) { @@ -74,9 +81,9 @@ internal static class GameOptionsMenuPatch __instance.scrollBar.SetYBoundsMax(-num - 1.65f); } - private static void UpdateGroup(AbstractOptionGroup group, ref float num) + private static void UpdateGroup(AbstractOptionGroup? group, ref float num) { - if (group.Options.Count == 0 || group.Header is null) + if (group is null || group.Options.Count == 0 || group.Header is null || group.CreationCoroutine is not null) { return; } @@ -102,7 +109,7 @@ internal static class GameOptionsMenuPatch { var newOpt = opt.OptionBehaviour; - if (newOpt is null) + if (!newOpt || newOpt == null) { continue; } @@ -171,7 +178,8 @@ internal static class GameOptionsMenuPatch private static void ModifiersUpdate(ref float num) { var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.ParentMenu is MenuCategory.Modifiers || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? []; + .Where(x => x.ParentMenu is MenuCategory.Modifiers || + x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? []; foreach (var modGroup in groups) { @@ -182,7 +190,8 @@ internal static class GameOptionsMenuPatch private static void ModifiersCreate(GameOptionsMenu menu) { var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.ParentMenu is MenuCategory.Modifiers || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? []; + .Where(x => x.ParentMenu is MenuCategory.Modifiers || + x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? []; foreach (var group in groups) { CreateGroup(menu, group); @@ -201,24 +210,21 @@ internal static class GameOptionsMenuPatch __instance.MapPicker.gameObject.SetActive(false); - if (__instance.name == "MODIFIERS TAB") + switch (__instance.name) { - ModifiersCreate(__instance); - return false; - } - else if (__instance.name == "CUSTOM TAB 1") - { - CustomMenuOneCreate(__instance); - return false; - } - else if (__instance.name == "CUSTOM TAB 2") - { - CustomMenuTwoCreate(__instance); - return false; + case "MODIFIERS TAB": + ModifiersCreate(__instance); + return false; + case "CUSTOM TAB 1": + CustomMenuOneCreate(__instance); + return false; + case "CUSTOM TAB 2": + CustomMenuTwoCreate(__instance); + return false; } var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.OptionableType == null && x.ParentMenu == MenuCategory.Roles) ?? []; + .Where(x => x.OptionableType == null && x.ParentMenu == MenuCategory.Game) ?? []; foreach (var group in filteredGroups) { @@ -264,7 +270,7 @@ internal static class GameOptionsMenuPatch return false; } - private static void CreateGroup(GameOptionsMenu menu, AbstractOptionGroup group) + private static IEnumerator CoCreateGroup(GameOptionsMenu menu, AbstractOptionGroup group) { var categoryHeaderMasked = Object.Instantiate( menu.categoryHeaderOrigin, @@ -311,7 +317,7 @@ internal static class GameOptionsMenuPatch defaultPreset = preset; } - foreach (var newOpt in options) + yield return options.CoLoopWithBudget(newOpt => { newOpt.SetClickMask(menu.ButtonClickMask); @@ -414,6 +420,7 @@ internal static class GameOptionsMenuPatch defaultPreset!.ResetOption(strOpt); })); } + if (!defaultPreset!.IsOptionInPreset(newOpt)) { resetBtn.Destroy(); @@ -421,7 +428,7 @@ internal static class GameOptionsMenuPatch newOpt.Initialize(); newOpt.gameObject.SetActive(false); - } + }).WrapToIl2Cpp(); var boxCol = categoryHeaderMasked.gameObject.AddComponent(); boxCol.size = new Vector2(7, 0.7f); @@ -440,5 +447,17 @@ internal static class GameOptionsMenuPatch : "(Click to close)"; })); headerBtn.SetButtonEnableState(true); + + group.CreationCoroutine = null; + } + + private static void CreateGroup(GameOptionsMenu menu, AbstractOptionGroup group) + { + if (group.CreationCoroutine != null) + { + GameSettingMenu.Instance.StopCoroutine(group.CreationCoroutine); + } + + group.CreationCoroutine = GameSettingMenu.Instance.StartCoroutine(CoCreateGroup(menu, group).WrapToIl2Cpp()); } } diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index a4b6cca..ff75606 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -8,7 +8,6 @@ using MiraAPI.Presets; using MiraAPI.Roles; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; -using Reactor.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; @@ -262,7 +261,7 @@ internal static class GamePresetsTabPatches iconSpriteRend.sprite = MiraAssets.RefreshIcon.LoadAsset(); Helpers.DivideSize(refreshButton.gameObject, 2f); - refreshButton.buttonText.gameObject.DeepDestroy(); + refreshButton.buttonText.gameObject.Destroy(); refreshButton.OnClick = new Button.ButtonClickedEvent(); refreshButton.OnClick.AddListener((UnityAction)Refresh); diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index 30ed1dd..d9cc5fb 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -199,7 +199,7 @@ internal static class GameSettingMenuPatches { SaveScrollPositions(__instance); SelectedModIdx += 1; - if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins.Length) + if (SelectedModIdx > MiraPluginManager.Instance.RegisteredOptionablePlugins.Length) { SelectedModIdx = 0; } @@ -220,7 +220,7 @@ internal static class GameSettingMenuPatches SelectedModIdx -= 1; if (SelectedModIdx < 0) { - SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins.Length; + SelectedModIdx = MiraPluginManager.Instance.RegisteredOptionablePlugins.Length; } UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); @@ -399,17 +399,17 @@ internal static class GameSettingMenuPatches { if (_text is not null && SelectedModIdx == 0) { - _text.text = $"(Page 0/{MiraPluginManager.Instance.RegisteredPlugins.Length})\nDefault"; + _text.text = $"(Page 0/{MiraPluginManager.Instance.RegisteredOptionablePlugins.Length})\nMain"; _text.fontSizeMax = 3.2f; SelectedMod = null; } else if (_text is not null) { _text.fontSizeMax = 2.3f; - SelectedMod = MiraPluginManager.Instance.RegisteredPlugins[SelectedModIdx - 1]; + SelectedMod = MiraPluginManager.Instance.RegisteredOptionablePlugins[SelectedModIdx - 1]; var name = SelectedMod.MiraPlugin.OptionsTitleText; - _text.text = $"(Page {SelectedModIdx}/{MiraPluginManager.Instance.RegisteredPlugins.Length})\n" + name[..Math.Min(name.Length, 25)]; + _text.text = $"(Page {SelectedModIdx}/{MiraPluginManager.Instance.RegisteredOptionablePlugins.Length})\n" + name[..Math.Min(name.Length, 25)]; } bool replaceWithModifiers = true; @@ -419,6 +419,8 @@ internal static class GameSettingMenuPatches _customTwoButton!.transform.localPosition = _customTwoBtnOgPos; menu.RoleSettingsButton.transform.localPosition = _roleBtnOgPos; + CleanupTab(settings, roles); + if (SelectedModIdx != 0) { _customOneButton.buttonText.text = SelectedMod!.MiraPlugin.CustomOptionMenuNameOne; @@ -432,7 +434,7 @@ internal static class GameSettingMenuPatches x => x.ParentMenu == MenuCategory.Modifiers || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true); var modHasOptions = SelectedMod.InternalOptionGroups.Exists(x => x.OptionableType == null && - x.ParentMenu != MenuCategory.Modifiers && x.ParentMenu != MenuCategory.Roles); + x.ParentMenu == MenuCategory.Game); _modifiersButton.gameObject.SetActive(true); _smallRolesButton.gameObject.SetActive(true); @@ -560,8 +562,6 @@ internal static class GameSettingMenuPatches { menu.ChangeTab(0, false); } - - CleanupTab(settings, roles); } private static void ClearOptions(Il2CppSystem.Collections.Generic.List options) @@ -606,6 +606,8 @@ internal static class GameSettingMenuPatches CleanupSettings(_customTwoTab, MenuCategory.CustomTwo); } + Utilities.Extensions.ClearGarbageCollector(); + void CleanupRoleSettings(RolesSettingsMenu rolesMenu) { if (rolesMenu.advancedSettingChildren != null) diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index c543447..dd80dc4 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -25,7 +25,7 @@ public static class LobbyViewPanePatches private static MiraPluginInfo? SelectedMod => SelectedModIdx == 0 ? null - : MiraPluginManager.Instance.RegisteredPlugins[SelectedModIdx - 1]; + : MiraPluginManager.Instance.RegisteredOptionablePlugins[SelectedModIdx - 1]; private static PassiveButton? ModifiersTabButton { get; set; } @@ -75,7 +75,7 @@ public static class LobbyViewPanePatches (UnityAction)(() => { SelectedModIdx += 1; - if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins.Length) + if (SelectedModIdx > MiraPluginManager.Instance.RegisteredOptionablePlugins.Length) { SelectedModIdx = 0; } @@ -99,7 +99,7 @@ public static class LobbyViewPanePatches SelectedModIdx -= 1; if (SelectedModIdx < 0) { - SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins.Length; + SelectedModIdx = MiraPluginManager.Instance.RegisteredOptionablePlugins.Length; } Refresh(__instance); @@ -109,7 +109,7 @@ public static class LobbyViewPanePatches private static void Refresh(LobbyViewSettingsPane menu) { ModifiersTabButton?.gameObject.SetActive(SelectedModIdx != 0); - menu.gameModeText.text = SelectedMod?.PluginInfo.Metadata.Name ?? "Default"; + menu.gameModeText.text = SelectedMod?.PluginInfo.Metadata.Name ?? "Main"; menu.RefreshTab(); menu.scrollBar.ScrollToTop(); } diff --git a/MiraAPI/Patches/Options/NotificationPopperPatch.cs b/MiraAPI/Patches/Options/NotificationPopperPatch.cs index b2c51e9..8024ea4 100644 --- a/MiraAPI/Patches/Options/NotificationPopperPatch.cs +++ b/MiraAPI/Patches/Options/NotificationPopperPatch.cs @@ -1,8 +1,6 @@ using System.Linq; using HarmonyLib; -using Il2CppSystem; using MiraAPI.Roles; -using Object = Il2CppSystem.Object; namespace MiraAPI.Patches.Options; @@ -26,12 +24,14 @@ public static class NotificationPopperPatch var textColor = customRole.OptionsMenuColor.ToTextColor(); + var icon = customRole.Configuration.IconTmp ? $"" : string.Empty; var item = TranslationController.Instance.GetString( StringNames.LobbyChangeSettingNotificationRole, string.Concat( + icon, "", textColor, - TranslationController.Instance.GetString(key, Array.Empty()), + TranslationController.Instance.GetString(key), "" ), "" + roleCount + "", diff --git a/MiraAPI/Patches/Options/OptionsPatches.cs b/MiraAPI/Patches/Options/OptionsPatches.cs index 0f99691..36651f9 100644 --- a/MiraAPI/Patches/Options/OptionsPatches.cs +++ b/MiraAPI/Patches/Options/OptionsPatches.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; -using System.Linq; -using HarmonyLib; +using HarmonyLib; using Il2CppSystem; using MiraAPI.GameOptions; -using MiraAPI.GameOptions.OptionTypes; using MiraAPI.Roles; using MiraAPI.Utilities; using UnityEngine; diff --git a/MiraAPI/Patches/Options/PlayerOptionPatches.cs b/MiraAPI/Patches/Options/PlayerOptionPatches.cs index 2b77bc1..9cf0134 100644 --- a/MiraAPI/Patches/Options/PlayerOptionPatches.cs +++ b/MiraAPI/Patches/Options/PlayerOptionPatches.cs @@ -1,12 +1,8 @@ -using System; -using System.Linq; +using System.Linq; using HarmonyLib; using MiraAPI.GameOptions; -using MiraAPI.GameOptions.OptionTypes; using MiraAPI.Utilities; -using Reactor.Utilities; using UnityEngine; -using Object = Il2CppSystem.Object; namespace MiraAPI.Patches.Options; diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index d123161..cb1283b 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -1,9 +1,10 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using AmongUs.GameOptions; +using BepInEx.Unity.IL2CPP.Utils.Collections; using HarmonyLib; -using Il2CppInterop.Runtime.InteropTypes.Arrays; using MiraAPI.GameOptions; using MiraAPI.Networking; using MiraAPI.Roles; @@ -11,7 +12,6 @@ using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Localization.Utilities; using Reactor.Networking.Rpc; -using Reactor.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; @@ -49,9 +49,7 @@ public static class RoleSettingMenuPatches __instance.scrollBar.SetScrollBounds(__instance); } - [HarmonyPrefix] - [HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))] - public static bool SetQuotaTabPatch(RolesSettingsMenu __instance) + private static IEnumerator CoQuotaTabPatch(RolesSettingsMenu roleMenu) { foreach (var obj in Headers) { @@ -59,8 +57,10 @@ public static class RoleSettingMenuPatches { continue; } - obj.DeepDestroy(false); + + obj.Destroy(); } + Headers.Clear(); foreach (var obj in RoleOptionSettings) { @@ -68,25 +68,26 @@ public static class RoleSettingMenuPatches { continue; } - obj.gameObject.DeepDestroy(false); + + obj.gameObject.Destroy(); } + RoleOptionSettings.Clear(); CurrentRole = null; CurrentRoleOptions = null; - __instance.roleChances = new Il2CppSystem.Collections.Generic.List(); - __instance.advancedSettingChildren = new Il2CppSystem.Collections.Generic.List(); - Utilities.Extensions.ClearGarbageCollector(); + roleMenu.roleChances = new Il2CppSystem.Collections.Generic.List(); + roleMenu.advancedSettingChildren = new Il2CppSystem.Collections.Generic.List(); - var maskBg = __instance.scrollBar.transform.FindChild("MaskBg"); - var hitbox = __instance.scrollBar.transform.FindChild("Hitbox"); - var dividerImage = __instance.transform.FindChild("HeaderButtons/DividerImage"); + var maskBg = roleMenu.scrollBar.transform.FindChild("MaskBg"); + var hitbox = roleMenu.scrollBar.transform.FindChild("Hitbox"); + var dividerImage = roleMenu.transform.FindChild("HeaderButtons/DividerImage"); if (GameSettingMenuPatches.SelectedModIdx == 0) { - __instance.AllButton.transform.parent.gameObject.SetActive(true); - __instance.AllButton.gameObject.SetActive(true); - __instance.scrollBar.transform.localPosition = new Vector3(-1.4957f, 0.657f, -4); + roleMenu.AllButton.transform.parent.gameObject.SetActive(true); + roleMenu.AllButton.gameObject.SetActive(true); + roleMenu.scrollBar.transform.localPosition = new Vector3(-1.4957f, 0.657f, -4); maskBg.localPosition = new Vector3(1.5353f, -.5734f, -.1f); maskBg.localScale = new Vector3(6.6811f, 3.3563f, 0.5598f); hitbox.localPosition = new Vector3(0.3297f, -.2333f, 4f); @@ -95,67 +96,70 @@ public static class RoleSettingMenuPatches var num = 0.662f; - var list = CustomRoleManager.AllRoles.Where( - r => !r.IsCustomRole() && !r.IsRoleBlacklisted() && r.TeamType == RoleTeamTypes.Crewmate && r.Role != RoleTypes.Crewmate && - r.Role != RoleTypes.CrewmateGhost).ToList(); - var list2 = CustomRoleManager.AllRoles.Where( - r => !r.IsCustomRole() && !r.IsRoleBlacklisted() && r.TeamType == RoleTeamTypes.Impostor && r.Role != RoleTypes.Impostor && - r.Role != RoleTypes.ImpostorGhost).ToList(); + var list = CustomRoleManager.AllRoles.Where(r => + !r.IsCustomRole() && !r.IsRoleBlacklisted() && r.TeamType == RoleTeamTypes.Crewmate && + r.Role != RoleTypes.Crewmate && + r.Role != RoleTypes.CrewmateGhost).ToList(); + var list2 = CustomRoleManager.AllRoles.Where(r => + !r.IsCustomRole() && !r.IsRoleBlacklisted() && r.TeamType == RoleTeamTypes.Impostor && + r.Role != RoleTypes.Impostor && + r.Role != RoleTypes.ImpostorGhost).ToList(); - if (__instance.roleTabs == null || __instance.roleTabs.Count == 0) + if (roleMenu.roleTabs == null || roleMenu.roleTabs.Count == 0) { var num2 = -1.928f; - __instance.roleTabs = new(); - __instance.roleTabs.Add(__instance.AllButton); + roleMenu.roleTabs = new(); + roleMenu.roleTabs.Add(roleMenu.AllButton); foreach (var t in list) { - __instance.AddRoleTab(t, ref num2); + roleMenu.AddRoleTab(t, ref num2); } foreach (var t in list2) { - __instance.AddRoleTab(t, ref num2); + roleMenu.AddRoleTab(t, ref num2); } } var categoryHeaderEditRole = Object.Instantiate( - __instance.categoryHeaderEditRoleOrigin, + roleMenu.categoryHeaderEditRoleOrigin, Vector3.zero, Quaternion.identity, - __instance.RoleChancesSettings.transform); + roleMenu.RoleChancesSettings.transform); categoryHeaderEditRole.SetHeader(StringNames.CrewmateRolesHeader, 20); categoryHeaderEditRole.transform.localPosition = new Vector3(4.986f, num, -2f); num -= 0.522f; var num3 = 0; foreach (var t in list) { - __instance.CreateQuotaOption(t, ref num, num3); + roleMenu.CreateQuotaOption(t, ref num, num3); num3++; } num -= 0.22f; var categoryHeaderEditRole2 = Object.Instantiate( - __instance.categoryHeaderEditRoleOrigin, + roleMenu.categoryHeaderEditRoleOrigin, Vector3.zero, Quaternion.identity, - __instance.RoleChancesSettings.transform); + roleMenu.RoleChancesSettings.transform); categoryHeaderEditRole2.SetHeader(StringNames.ImpostorRolesHeader, 20); categoryHeaderEditRole2.transform.localPosition = new Vector3(4.986f, num, -2f); num -= 0.522f; foreach (var t in list2) { - __instance.CreateQuotaOption(t, ref num, num3); + roleMenu.CreateQuotaOption(t, ref num, num3); num3++; } - return false; + Info($"Created {num3} role options for the default game."); + yield break; } ScrollerNum = 0.522f; - __instance.AllButton.transform.parent.gameObject.SetActive(false); - __instance.AllButton.gameObject.SetActive(false); - __instance.scrollBar.transform.localPosition = new Vector3(-1.4957f, 1.5261f, -4); + roleMenu.AllButton.transform.parent.gameObject.SetActive(false); + roleMenu.AllButton.gameObject.SetActive(false); + roleMenu.scrollBar.transform.localPosition = new Vector3(-1.4957f, 1.5261f, -4); maskBg.localPosition = new Vector3(1.5353f, -1.0607f, -.1f); maskBg.localScale = new Vector3(6.6811f, 4.1563f, 0.5598f); hitbox.localPosition = new Vector3(0.3297f, -.6333f, 4f); @@ -169,7 +173,8 @@ public static class RoleSettingMenuPatches if (roleGroups is null) { - return false; + Error("No role groups found for the selected mod."); + yield break; } // sort the groups by priority @@ -177,13 +182,14 @@ public static class RoleSettingMenuPatches .OrderBy(x => x.Key.Priority) .ThenBy(x => x.Key.Name); - var quotaThing = __instance.categoryHeaderEditRoleOrigin.transform.FindChild("QuotaHeader"); + var quotaThing = roleMenu.categoryHeaderEditRoleOrigin.transform.FindChild("QuotaHeader"); var usingNewQuota = false; - var template = __instance.transform.parent.parent.GetComponent().GameSettingsTab.categoryHeaderOrigin; + var template = roleMenu.transform.parent.parent.GetComponent().GameSettingsTab + .categoryHeaderOrigin; foreach (var grouping in sortedRoleGroups) { - if (!grouping.Any() || grouping.All(x=> x.Configuration.HideSettings || !x.VisibleInSettings())) + if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings || !x.VisibleInSettings())) { continue; } @@ -203,7 +209,7 @@ public static class RoleSettingMenuPatches template, Vector3.zero, Quaternion.identity, - __instance.RoleChancesSettings.transform); + roleMenu.RoleChancesSettings.transform); categoryHeaderMasked.SetHeader(name, 20); @@ -222,9 +228,9 @@ public static class RoleSettingMenuPatches var blankLabel = quotaInst.transform.FindChild("BlankLabel").gameObject; var chanceLabel = quotaInst.transform.FindChild("Chance Label").gameObject; var countLabel = quotaInst.transform.FindChild("# Label").gameObject; - blankLabel.DeepDestroy(false); - chanceLabel.DeepDestroy(false); - countLabel.DeepDestroy(false); + blankLabel.Destroy(); + chanceLabel.Destroy(); + countLabel.Destroy(); usingNewQuota = true; quotaThing = quotaInst; } @@ -246,19 +252,23 @@ public static class RoleSettingMenuPatches categoryHeaderMasked.Background.color = Palette.ImpostorRoleHeaderRed; break; default: - categoryHeaderMasked.Title.color = group.Color.Equals(MiraApiPlugin.DefaultHeaderColor) ? Color.white : group.Color.FindAlternateColor(); + categoryHeaderMasked.Title.color = group.Color.Equals(MiraApiPlugin.DefaultHeaderColor) + ? Color.white + : group.Color.FindAlternateColor(); categoryHeaderMasked.Divider.color = group.Color; categoryHeaderMasked.Background.color = group.Color; break; } - categoryHeaderMasked.Title.fontStyle = __instance.categoryHeaderEditRoleOrigin.Title.fontStyle; - categoryHeaderMasked.Title.font = __instance.categoryHeaderEditRoleOrigin.Title.font; - categoryHeaderMasked.Title.fontMaterial = __instance.categoryHeaderEditRoleOrigin.Title.fontMaterial; + categoryHeaderMasked.Title.fontStyle = roleMenu.categoryHeaderEditRoleOrigin.Title.fontStyle; + categoryHeaderMasked.Title.font = roleMenu.categoryHeaderEditRoleOrigin.Title.font; + categoryHeaderMasked.Title.fontMaterial = roleMenu.categoryHeaderEditRoleOrigin.Title.fontMaterial; categoryHeaderMasked.Divider.color = categoryHeaderMasked.Background.color; categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.55f, -0.1833f, 0); - categoryHeaderMasked.Background.size = new Vector2(categoryHeaderMasked.Background.size.x + 1.5f, categoryHeaderMasked.Background.size.y); + categoryHeaderMasked.Background.size = new Vector2( + categoryHeaderMasked.Background.size.x + 1.5f, + categoryHeaderMasked.Background.size.y); categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f; categoryHeaderMasked.transform.localPosition = new Vector3(-0.44f, ScrollerNum, -2f); @@ -278,20 +288,21 @@ public static class RoleSettingMenuPatches if (!RoleGroupHidden[group]) { - foreach (var role in grouping) - { - if (role is not RoleBehaviour roleBehaviour) + yield return + grouping.CoLoopWithBudget(role => { - continue; - } + if (role is not RoleBehaviour roleBehaviour) + { + return; + } - var option = CreateQuotaOption(__instance, roleBehaviour, num4); - if (option is not null) - { - RoleOptionSettings.Add(option); - num4++; - } - } + var option = CreateQuotaOption(roleMenu, roleBehaviour, num4); + if (option is not null) + { + RoleOptionSettings.Add(option); + num4++; + } + }); } var boxCol = categoryHeaderMasked.gameObject.AddComponent(); @@ -299,25 +310,28 @@ public static class RoleSettingMenuPatches boxCol.offset = new Vector2(1.5f, -0.3f); var headerBtn = categoryHeaderMasked.gameObject.AddComponent(); - headerBtn.ClickSound = __instance.BackButton.GetComponent().ClickSound; + headerBtn.ClickSound = roleMenu.BackButton.GetComponent().ClickSound; headerBtn.OnMouseOver = new UnityEvent(); headerBtn.OnMouseOut = new UnityEvent(); headerBtn.OnClick.AddListener( (UnityAction)(() => { - RolePositions[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition; + RolePositions[GameSettingMenuPatches.SelectedModIdx] = roleMenu.scrollBar.Inner.localPosition; if (RoleGroupHidden.TryGetValue(group, out var groupHidden)) { RoleGroupHidden[group] = !groupHidden; } + foreach (var obj in Headers) { if (!obj) { continue; } - obj.DeepDestroy(false); + + obj.Destroy(); } + Headers.Clear(); foreach (var obj in RoleOptionSettings) { @@ -325,11 +339,12 @@ public static class RoleSettingMenuPatches { continue; } - obj.gameObject.DeepDestroy(false); + + obj.gameObject.Destroy(); } + RoleOptionSettings.Clear(); - Utilities.Extensions.ClearGarbageCollector(); - __instance.SetQuotaTab(); + roleMenu.SetQuotaTab(); })); headerBtn.SetButtonEnableState(true); @@ -339,7 +354,22 @@ public static class RoleSettingMenuPatches } } - __instance.scrollBar.SetScrollBounds(__instance); + roleMenu.scrollBar.SetScrollBounds(roleMenu); + _quotaTabCoroutine = null; + } + + private static Coroutine? _quotaTabCoroutine; + + [HarmonyPrefix] + [HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))] + public static bool SetQuotaTabPatch(RolesSettingsMenu __instance) + { + if (_quotaTabCoroutine != null) + { + GameSettingMenu.Instance.StopCoroutine(_quotaTabCoroutine); + } + + _quotaTabCoroutine = GameSettingMenu.Instance.StartCoroutine(CoQuotaTabPatch(__instance).WrapToIl2Cpp()); return false; } @@ -364,6 +394,7 @@ public static class RoleSettingMenuPatches } [HarmonyPrefix] + // TODO: turn this into a fixed update [HarmonyPatch(nameof(RolesSettingsMenu.Update))] public static bool UpdatePatch(RolesSettingsMenu __instance) { @@ -437,8 +468,7 @@ public static class RoleSettingMenuPatches roleSetting.Role.StringName, roleSetting.RoleMaxCount, roleSetting.RoleChance, - roleSetting.Role.TeamType, - false); + roleSetting.Role.TeamType); if (AmongUsClient.Instance.AmHost) { @@ -452,12 +482,11 @@ public static class RoleSettingMenuPatches { foreach (var optBehaviour in __instance.AdvancedRolesSettings.GetComponentsInChildren()) { - optBehaviour.gameObject.DeepDestroy(false); + optBehaviour.gameObject.Destroy(); } CurrentRole = role; __instance.advancedSettingChildren.Clear(); - Utilities.Extensions.ClearGarbageCollector(); // TODO: create sub groups under the role settings. var filteredOptions = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups @@ -465,8 +494,6 @@ public static class RoleSettingMenuPatches .SelectMany(x => x.Options) .ToList() ?? []; - CurrentRoleOptions = filteredOptions; - foreach (var option in filteredOptions) { var newOpt = option.CreateOption( @@ -497,6 +524,7 @@ public static class RoleSettingMenuPatches newOpt.Initialize(); } + CurrentRoleOptions = filteredOptions; __instance.scrollBar.ScrollToTop(); } @@ -649,7 +677,7 @@ public static class RoleSettingMenuPatches var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform); newButton.name = "ConfigButton"; newButton.transform.localPosition = new Vector3(0.4473f, -0.3f, -2f); - newButton.transform.FindChild("Text_TMP").gameObject.DeepDestroy(); + newButton.transform.FindChild("Text_TMP").gameObject.Destroy(); newButton.activeSprites.Destroy(); var btnRend = newButton.transform.FindChild("ButtonSprite").GetComponent(); diff --git a/MiraAPI/Patches/PlayerControlPatches.cs b/MiraAPI/Patches/PlayerControlPatches.cs index 3c851b0..77b83c2 100644 --- a/MiraAPI/Patches/PlayerControlPatches.cs +++ b/MiraAPI/Patches/PlayerControlPatches.cs @@ -8,7 +8,6 @@ using MiraAPI.Hud; using MiraAPI.Modifiers; using MiraAPI.Utilities; using MiraAPI.Voting; -using Reactor.Utilities; using Reactor.Utilities.Extensions; namespace MiraAPI.Patches; diff --git a/MiraAPI/Patches/Roles/HowToPlayPatch.cs b/MiraAPI/Patches/Roles/HowToPlayPatch.cs index ee6e5ff..c7cc2e5 100644 --- a/MiraAPI/Patches/Roles/HowToPlayPatch.cs +++ b/MiraAPI/Patches/Roles/HowToPlayPatch.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Linq; +using System.Linq; using AmongUs.GameOptions; using HarmonyLib; using MiraAPI.Roles; diff --git a/MiraAPI/Patches/Roles/HudManagerPatches.cs b/MiraAPI/Patches/Roles/HudManagerPatches.cs index 79c2306..3de9b71 100644 --- a/MiraAPI/Patches/Roles/HudManagerPatches.cs +++ b/MiraAPI/Patches/Roles/HudManagerPatches.cs @@ -1,9 +1,7 @@ using HarmonyLib; using InnerNet; using MiraAPI.Roles; -using MiraAPI.Utilities; using Reactor.Utilities.Extensions; -using UnityEngine; namespace MiraAPI.Patches.Roles; @@ -73,7 +71,7 @@ public static class HudManagerPatches } else if (RoleTab != null) { - RoleTab.gameObject.DeepDestroy(); + RoleTab.gameObject.Destroy(); RoleTab = null; } } diff --git a/MiraAPI/Patches/Roles/RoleManagerPatches.cs b/MiraAPI/Patches/Roles/RoleManagerPatches.cs index 713ffdf..8d13595 100644 --- a/MiraAPI/Patches/Roles/RoleManagerPatches.cs +++ b/MiraAPI/Patches/Roles/RoleManagerPatches.cs @@ -1,7 +1,6 @@ using System.Linq; using AmongUs.GameOptions; using HarmonyLib; -using InnerNet; using MiraAPI.Events; using MiraAPI.Events.Vanilla.Gameplay; using MiraAPI.Modifiers; diff --git a/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs b/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs index ff57b37..d4d80d3 100644 --- a/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs +++ b/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs @@ -2,7 +2,6 @@ using HarmonyLib; using Il2CppSystem.Collections.Generic; using MiraAPI.Roles; -using Reactor.Utilities; namespace MiraAPI.Patches.Roles; diff --git a/MiraAPI/Patches/ServerDropdownPatch.cs b/MiraAPI/Patches/ServerDropdownPatch.cs index 6ff7e68..7749c9b 100644 --- a/MiraAPI/Patches/ServerDropdownPatch.cs +++ b/MiraAPI/Patches/ServerDropdownPatch.cs @@ -1,6 +1,5 @@ using System.Linq; using HarmonyLib; -using Reactor.Utilities; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; diff --git a/MiraAPI/Patches/TmpAwakePatch.cs b/MiraAPI/Patches/TmpAwakePatch.cs new file mode 100644 index 0000000..9405e01 --- /dev/null +++ b/MiraAPI/Patches/TmpAwakePatch.cs @@ -0,0 +1,27 @@ +using HarmonyLib; +using MiraAPI.Utilities.Assets; +using TMPro; + +namespace MiraAPI.Patches; +[HarmonyPatch] +public static class TmpAwakePatch +{ + [HarmonyPatch(typeof(TextMeshPro), nameof(TextMeshPro.Awake))] + [HarmonyPostfix] + public static void TmpAwakePostfix(TextMeshPro __instance) + { + if (!TmpSpriteUtils.AssetHolder || __instance.m_spriteAsset == TmpSpriteUtils.AssetHolder) + { + return; + } + + if (!__instance.m_spriteAsset) + { + __instance.m_spriteAsset = TmpSpriteUtils.AssetHolder; + __instance.UpdateMeshPadding(); + return; + } + __instance.m_spriteAsset.fallbackSpriteAssets.Add(TmpSpriteUtils.AssetHolder); + __instance.UpdateMeshPadding(); + } +} diff --git a/MiraAPI/Patches/Voting/DummyBehaviourPatch.cs b/MiraAPI/Patches/Voting/DummyBehaviourPatch.cs index 9e5c8a4..1d8c1e9 100644 --- a/MiraAPI/Patches/Voting/DummyBehaviourPatch.cs +++ b/MiraAPI/Patches/Voting/DummyBehaviourPatch.cs @@ -7,7 +7,6 @@ using MiraAPI.Events; using MiraAPI.Events.Vanilla.Meeting.Voting; using MiraAPI.Utilities; using MiraAPI.Voting; -using Reactor.Utilities; using Reactor.Utilities.Extensions; using UnityEngine; diff --git a/MiraAPI/Patches/Voting/MeetingHudPatches.cs b/MiraAPI/Patches/Voting/MeetingHudPatches.cs index 2aa9a82..7a4338a 100644 --- a/MiraAPI/Patches/Voting/MeetingHudPatches.cs +++ b/MiraAPI/Patches/Voting/MeetingHudPatches.cs @@ -208,13 +208,6 @@ internal static class MeetingHudPatches public static bool PopulateResultsPatch(MeetingHud __instance, ref Il2CppStructArray states) { var votes = states.Select(x=> new CustomVote(x.VoterId, x.VotedForId)).ToList(); - var @event = new PopulateResultsEvent(votes); - MiraEventManager.InvokeEvent(@event); - - if (@event.IsCancelled) - { - return false; - } VotingUtils.HandlePopulateResults(votes); return false; diff --git a/MiraAPI/PluginLoading/IMiraPlugin.cs b/MiraAPI/PluginLoading/IMiraPlugin.cs index f1ef793..a29dd4c 100644 --- a/MiraAPI/PluginLoading/IMiraPlugin.cs +++ b/MiraAPI/PluginLoading/IMiraPlugin.cs @@ -1,4 +1,5 @@ -using BepInEx.Configuration; +using System.Linq; +using BepInEx.Configuration; namespace MiraAPI.PluginLoading; @@ -7,11 +8,25 @@ namespace MiraAPI.PluginLoading; /// public interface IMiraPlugin { + /// + /// Gets or sets a value indicating whether to display the plugin in the options menu. + /// + public virtual bool DisplayOnOptionsMenu => true; + /// /// Gets the name to display on the options menu. /// string OptionsTitleText { get; } + /// + /// Gets the abbreviated name to display for other mods to pick up. + /// + /// The text for the plugin. + public virtual string GetAbbreviatedModName() + { + return new(OptionsTitleText.Where(c => !char.IsLower(c) && !char.IsWhiteSpace(c)).ToArray()); + } + /// /// Gets the name for the first custom category in the game options menu, if any. /// diff --git a/MiraAPI/PluginLoading/MiraPluginInfo.cs b/MiraAPI/PluginLoading/MiraPluginInfo.cs index 0df7cb5..db030b0 100644 --- a/MiraAPI/PluginLoading/MiraPluginInfo.cs +++ b/MiraAPI/PluginLoading/MiraPluginInfo.cs @@ -5,7 +5,6 @@ using BepInEx.Configuration; using MiraAPI.GameModes; using MiraAPI.GameOptions; using MiraAPI.Hud; -using MiraAPI.LocalSettings; using MiraAPI.Modifiers; using MiraAPI.Presets; diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 4639b4f..171ab90 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -34,6 +34,10 @@ public sealed class MiraPluginManager internal MiraPluginInfo[] RegisteredPlugins { get; private set; } = null!; + private readonly Dictionary _registeredOptionablePlugins = []; + + internal MiraPluginInfo[] RegisteredOptionablePlugins { get; private set; } = null!; + internal Dictionary> QueuedRoleRegistrations { get; } = []; internal static MiraPluginManager Instance { get; private set; } = new(); @@ -127,6 +131,10 @@ public sealed class MiraPluginManager QueuedRoleRegistrations.Add(info, roles); _registeredPlugins.Add(assembly, info); + if (miraPlugin.DisplayOnOptionsMenu) + { + _registeredOptionablePlugins.Add(assembly, info); + } info.SavePublicCollections(); PresetManager.CreateDefaultPreset(info); @@ -141,6 +149,7 @@ public sealed class MiraPluginManager // Cache all the registered plugins into an array for easy access RegisteredPlugins = [.. _registeredPlugins.Values]; + RegisteredOptionablePlugins = [.. _registeredOptionablePlugins.Values]; ModifierManager.Modifiers = new ReadOnlyCollection(ModifierManager.InternalModifiers); }; diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs index bec1216..f753db5 100644 --- a/MiraAPI/Presets/PresetManager.cs +++ b/MiraAPI/Presets/PresetManager.cs @@ -4,8 +4,7 @@ using System.Linq; using BepInEx.Configuration; using MiraAPI.PluginLoading; using MiraAPI.Roles; -using MiraAPI.Utilities; -using Reactor.Utilities; +using Reactor.Utilities.Extensions; using UnityEngine; namespace MiraAPI.Presets; @@ -62,7 +61,7 @@ public static class PresetManager { if (btn != null) { - btn.DeepDestroy(); + btn.Destroy(); } } diff --git a/MiraAPI/Presets/SavePresetPopup.cs b/MiraAPI/Presets/SavePresetPopup.cs index 23ce48b..87959e7 100644 --- a/MiraAPI/Presets/SavePresetPopup.cs +++ b/MiraAPI/Presets/SavePresetPopup.cs @@ -26,7 +26,7 @@ public class SavePresetPopup(nint cppPtr) : Minigame(cppPtr) // Cleanup holder object public void OnDestroy() { - transform.parent.gameObject.DeepDestroy(); + transform.parent.gameObject.Destroy(); } public override void Close() diff --git a/MiraAPI/Resources/BlankSprite.png b/MiraAPI/Resources/BlankSprite.png new file mode 100644 index 0000000..9523fc8 Binary files /dev/null and b/MiraAPI/Resources/BlankSprite.png differ diff --git a/MiraAPI/Roles/CustomRoleConfiguration.cs b/MiraAPI/Roles/CustomRoleConfiguration.cs index 819eb1c..f756fed 100644 --- a/MiraAPI/Roles/CustomRoleConfiguration.cs +++ b/MiraAPI/Roles/CustomRoleConfiguration.cs @@ -3,6 +3,7 @@ using System; using Il2CppInterop.Runtime.Attributes; using MiraAPI.Patches.Freeplay; using MiraAPI.Utilities.Assets; +using TMPro; using UnityEngine; namespace MiraAPI.Roles; @@ -87,6 +88,12 @@ public record struct CustomRoleConfiguration [HideFromIl2Cpp] public LoadableAsset? Icon { get; set; } = null; + /// + /// Gets or sets the for the Role Icon. + /// + [HideFromIl2Cpp] + public TMP_SpriteAsset IconTmp { get; set; } + /// /// Gets or sets the Intro sound for the Role. /// diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index 9e6540f..d85ed19 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -11,7 +11,6 @@ using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Localization.Utilities; using Reactor.Networking.Rpc; -using Reactor.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 5b72650..c0c97be 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -4,7 +4,6 @@ using BepInEx.Configuration; using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.PluginLoading; -using Reactor.Utilities; using UnityEngine; namespace MiraAPI.Roles; diff --git a/MiraAPI/Utilities/Assets/LoadableAddressableAsset.cs b/MiraAPI/Utilities/Assets/LoadableAddressableAsset.cs index b9e8c55..144f608 100644 --- a/MiraAPI/Utilities/Assets/LoadableAddressableAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableAddressableAsset.cs @@ -1,5 +1,4 @@ using System; -using Reactor.Utilities; using UnityEngine.AddressableAssets; namespace MiraAPI.Utilities.Assets; diff --git a/MiraAPI/Utilities/Assets/LoadableAddressableAssets.cs b/MiraAPI/Utilities/Assets/LoadableAddressableAssets.cs index 9cabf83..c70100a 100644 --- a/MiraAPI/Utilities/Assets/LoadableAddressableAssets.cs +++ b/MiraAPI/Utilities/Assets/LoadableAddressableAssets.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using Reactor.Utilities; using UnityEngine.AddressableAssets; namespace MiraAPI.Utilities.Assets; diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs index 8ae8986..10bceda 100644 --- a/MiraAPI/Utilities/Assets/MiraAssets.cs +++ b/MiraAPI/Utilities/Assets/MiraAssets.cs @@ -134,4 +134,6 @@ public static class MiraAssets /// Gets the sprite used for timed modifier file in TaskAdderGame. /// public static LoadableResourceAsset TimedModifierFile { get; } = new("MiraAPI.Resources.TimedModifierFile.png"); + + internal static LoadableResourceAsset BlankSprite { get; } = new("MiraAPI.Resources.BlankSprite.png"); } diff --git a/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs b/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs new file mode 100644 index 0000000..cdb4315 --- /dev/null +++ b/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using Reactor.Utilities.Extensions; +using TMPro; +using UnityEngine; + +namespace MiraAPI.Utilities.Assets; +public static class TmpSpriteUtils +{ + private static Shader _spriteShader; + private static Dictionary LoadedSprites = []; + public static TMP_SpriteAsset AssetHolder; + public static TMP_SpriteAsset CreateSpriteAsset(Sprite sprite, string assetName, float scale = 1) + { + return CreateSpriteAsset(sprite.texture, sprite.rect, assetName, scale); + } + public static TMP_SpriteAsset CreateSpriteAsset(Texture2D sourceTexture, Rect rect, string assetName, float scale = 1) + { + if (LoadedSprites.TryGetValue(assetName, out var existingAsset)) + { + return existingAsset; + } + + if (!AssetHolder) + { + AssetHolder = ScriptableObject.CreateInstance(); + AssetHolder.DontUnload().DontDestroy(); + AssetHolder.fallbackSpriteAssets = new(); + } + TMP_SpriteAsset spriteAsset = ScriptableObject.CreateInstance(); + spriteAsset.name = assetName; + spriteAsset.spriteSheet = sourceTexture; + + if (!_spriteShader) + { + _spriteShader = Shader.Find("TextMeshPro/Sprite"); + } + + var material = new Material(_spriteShader) + { + name = assetName + " Material", + }; + material.SetTexture(ShaderUtilities.ID_MainTex, sourceTexture); + material.SetFloat(ShaderUtilities.ID_StencilComp, 0); + material.SetFloat(ShaderUtilities.ID_StencilID, 0); + material.SetFloat(ShaderUtilities.ID_StencilOp, 0); + material.SetFloat(ShaderUtilities.ID_StencilWriteMask, 255); + material.SetFloat(ShaderUtilities.ID_StencilReadMask, 255); + material.SetFloat(ShaderUtilities.ShaderTag_CullMode, 0); + spriteAsset.material = material; + + spriteAsset.spriteInfoList = new Il2CppSystem.Collections.Generic.List(); + + AddSpriteToAsset(spriteAsset, rect, assetName, scale); + + spriteAsset.DontUnload().DontDestroy(); + spriteAsset.UpdateLookupTables(); + spriteAsset.fallbackSpriteAssets = new(); + AssetHolder.fallbackSpriteAssets.Add(spriteAsset); + AssetHolder.UpdateLookupTables(); + + LoadedSprites.Add(assetName, spriteAsset); + return spriteAsset; + } + + private static void AddSpriteToAsset(TMP_SpriteAsset spriteAsset, Rect rect, string spriteName, float scale) + { + TMP_Sprite newSprite = new TMP_Sprite + { + name = spriteName, + hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteName), + x = 0, + y = 0, + width = rect.width, + height = rect.height, + xOffset = -(rect.width / 3) + (scale - 1) * (rect.width / 2), + yOffset = rect.height / 1.25f, + xAdvance = rect.width, + scale = scale, + }; + + spriteAsset.spriteInfoList.Add(newSprite); + } +} diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 6c8b74b..9dd91e9 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; @@ -8,10 +9,8 @@ using System.Text.RegularExpressions; using HarmonyLib; using MiraAPI.GameOptions; using MiraAPI.Networking; -using MiraAPI.Patches.Menu; using MiraAPI.Roles; using MiraAPI.Voting; -using Reactor.Utilities; using Reactor.Utilities.Extensions; using Rewired; using Rewired.Data; @@ -56,7 +55,7 @@ public static class Extensions bool isComplete; if (self.amClosing == Minigame.CloseState.Closing) { - self.gameObject.DeepDestroy(); + self.gameObject.Destroy(); return; } if (self.CloseSound && Constants.ShouldPlaySfx()) @@ -229,13 +228,44 @@ public static class Extensions /// Whether to run the garbage collector immediately. public static void DeepDestroy(this GameObject obj, bool clearGc = true) { - if (MainMenuManagerPatches.NeedsDeepDestroy) + obj.Destroy(); + } + + /// + /// Runs a given action in a loop with a frame budget based on the current target frame rate. Once the budget is hit, the loop continues on the next frame. + /// + /// The collection to iterate over. + /// The action to perform on each item. + /// The type of the collection. + /// Coroutine. + public static IEnumerator CoLoopWithBudget(this IEnumerable collection, Action action) + { + var fps = Application.targetFrameRate > 0 ? Application.targetFrameRate : 60; + long budget = 1 / (fps * 2); // default is half of current frame time. + yield return collection.CoLoopWithBudget(budget, action); + } + + /// + /// Runs a given action in a loop with a given frame budget. Once the budget is hit, the loop continues on the next frame. + /// + /// The collection to iterate over. + /// The # of milliseconds to spend in the loop. + /// The action to perform on each item. + /// The type of the collection. + /// Coroutine. + public static IEnumerator CoLoopWithBudget(this IEnumerable collection, long frameBudget, Action action) + { + var timer = new Stopwatch(); + timer.Start(); + foreach (var item in collection) { - Coroutines.Start(Nuke(obj, clearGc)); - } - else - { - obj?.Destroy(); + action(item); + + if (timer.ElapsedMilliseconds > frameBudget) + { + timer.Restart(); + yield return null; + } } } @@ -326,7 +356,7 @@ public static class Extensions yield return null; if (clearGc) { - yield return CoFreeResources(); + ClearGarbageCollector(); } } @@ -335,16 +365,9 @@ public static class Extensions /// public static void ClearGarbageCollector() { - Coroutines.Start(CoFreeResources()); - } - - private static IEnumerator CoFreeResources() - { - yield return Resources.UnloadUnusedAssets(); - - GC.Collect(0, GCCollectionMode.Forced, blocking: true); - GC.WaitForPendingFinalizers(); - GC.Collect(0, GCCollectionMode.Forced, blocking: true); + Resources.UnloadUnusedAssets(); + Il2CppSystem.GC.Collect(0, Il2CppSystem.GCCollectionMode.Forced); + GC.Collect(0, GCCollectionMode.Forced); } /// diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index c089e40..9f66817 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -2,12 +2,10 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using AmongUs.GameOptions; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using MiraAPI.Roles; using MiraAPI.Utilities.Assets; -using Reactor.Utilities; using Rewired; using TMPro; using UnityEngine; diff --git a/MiraAPI/Voting/VotingUtils.cs b/MiraAPI/Voting/VotingUtils.cs index abafdd0..fb117e4 100644 --- a/MiraAPI/Voting/VotingUtils.cs +++ b/MiraAPI/Voting/VotingUtils.cs @@ -5,7 +5,6 @@ using MiraAPI.Events.Vanilla.Meeting.Voting; using MiraAPI.Networking; using MiraAPI.Utilities; using Reactor.Networking.Attributes; -using Reactor.Utilities; namespace MiraAPI.Voting; @@ -207,6 +206,9 @@ public static class VotingUtils return; } + // If modified, these will visually change. + votes = @event.Votes; + MeetingHud.Instance.TitleText.text = TranslationController.Instance.GetString( StringNames.MeetingVotingResults,