diff --git a/MiraAPI.Example/Modifiers/CaptainModifier.cs b/MiraAPI.Example/Modifiers/CaptainModifier.cs index 5845e5a..ca8a570 100644 --- a/MiraAPI.Example/Modifiers/CaptainModifier.cs +++ b/MiraAPI.Example/Modifiers/CaptainModifier.cs @@ -1,8 +1,20 @@ -using MiraAPI.Modifiers.Types; +using MiraAPI.Example.Options.Modifiers; +using MiraAPI.GameOptions; +using MiraAPI.Modifiers.Types; namespace MiraAPI.Example.Modifiers; public class CaptainModifier : GameModifier { public override string ModifierName => "Captain"; + + public override int GetAssignmentChance() + { + return (int)OptionGroupSingleton.Instance.Chance; + } + + public override int GetAmountPerGame() + { + return (int)OptionGroupSingleton.Instance.Amount; + } } diff --git a/MiraAPI.Example/Modifiers/HighPriorityModifier.cs b/MiraAPI.Example/Modifiers/HighPriorityModifier.cs index 1d1ddd5..f4dc30b 100644 --- a/MiraAPI.Example/Modifiers/HighPriorityModifier.cs +++ b/MiraAPI.Example/Modifiers/HighPriorityModifier.cs @@ -1,4 +1,5 @@ -using MiraAPI.Modifiers.Types; +using MiraAPI.GameOptions; +using MiraAPI.Modifiers.Types; namespace MiraAPI.Example.Modifiers; @@ -6,6 +7,16 @@ public class HighPriorityModifier : GameModifier { public override string ModifierName => "High Priority"; + public override int GetAssignmentChance() + { + return 100; + } + + public override int GetAmountPerGame() + { + return 1; + } + public override int Priority() { return 100; diff --git a/MiraAPI.Example/Options/Modifiers/CaptainModifierSettings.cs b/MiraAPI.Example/Options/Modifiers/CaptainModifierSettings.cs index 2ae297b..0246a1d 100644 --- a/MiraAPI.Example/Options/Modifiers/CaptainModifierSettings.cs +++ b/MiraAPI.Example/Options/Modifiers/CaptainModifierSettings.cs @@ -8,6 +8,12 @@ public class CaptainModifierSettings : AbstractOptionGroup { public override string GroupName => "Captain Settings"; + [ModdedNumberOption("Amount", 0, 5)] + public float Amount { get; set; } = 1; + + [ModdedNumberOption("Chance", 0, 100, 10)] + public float Chance { get; set; } = 50; + [ModdedNumberOption("Uses", 1, 5)] public float NumUses { get; set; } = 3; } diff --git a/MiraAPI/GameOptions/AbstractOptionGroup.cs b/MiraAPI/GameOptions/AbstractOptionGroup.cs index 96dcdd7..c3e9cde 100644 --- a/MiraAPI/GameOptions/AbstractOptionGroup.cs +++ b/MiraAPI/GameOptions/AbstractOptionGroup.cs @@ -16,6 +16,11 @@ public abstract class AbstractOptionGroup /// public abstract string GroupName { get; } + /// + /// Gets the Optionable type of the group. + /// + public virtual Type? OptionableType => null; + /// /// Gets the function that determines whether the group should be visible or not. /// @@ -41,10 +46,8 @@ public abstract class AbstractOptionGroup /// Base class for option groups. An option group is a collection of options that are displayed together in the options menu. /// /// The type of the optionable that this group contains. -public abstract class AbstractOptionGroup : AbstractOptionGroup, IOptionableGroup where T : IOptionable +public abstract class AbstractOptionGroup : AbstractOptionGroup where T : IOptionable { - /// - /// Gets the Optionable type of the group. - /// - public Type OptionableType => typeof(T); + /// + public override Type OptionableType => typeof(T); } diff --git a/MiraAPI/GameOptions/IOptionableGroup.cs b/MiraAPI/GameOptions/IOptionableGroup.cs deleted file mode 100644 index e476548..0000000 --- a/MiraAPI/GameOptions/IOptionableGroup.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace MiraAPI.GameOptions; - -/// -/// Interface for groups that have some type of "Optionable" class. -/// -public interface IOptionableGroup -{ - /// - /// Gets the type of the Optionable class. - /// - Type OptionableType { get; } -} diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index cbb6459..7aaa154 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -108,7 +108,7 @@ public static class ModdedOptionsManager RegisterOption(option, group, property.Name, pluginInfo); } - private static void RegisterOption( + internal static void RegisterOption( IModdedOption option, AbstractOptionGroup group, string propertyName, diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index 1bd1082..0dd2149 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -42,8 +42,6 @@ public abstract class BaseModifier : IOptionable x => x.Modifiers.Exists(y => y.TypeId == TypeId) ) ?? throw new InvalidOperationException("Modifier is not registered."); - internal ModifierOptionGroup? ModifierOptionsGroup { get; set; } - /// /// Gets the modifier name. /// diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs index fee96ab..5f6bbff 100644 --- a/MiraAPI/Modifiers/ModifierManager.cs +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -63,32 +63,32 @@ public static class ModifierManager IdToTypeModifierMap.Add(GetNextTypeId(), modifierType); TypeToIdModifierMap.Add(modifierType, _nextTypeId); - var bm = (BaseModifier)FormatterServices.GetUninitializedObject(modifierType); // this probably isn't a great idea - info.Modifiers.Add(bm); - if (!typeof(GameModifier).IsAssignableFrom(modifierType)) + BaseModifier modifier; + if (modifierType.GetConstructor(Type.EmptyTypes) != null) + { + modifier = (BaseModifier)Activator.CreateInstance(modifierType)!; + } + else + { + // this is probably terrible but its good enough for now. + modifier = (BaseModifier)FormatterServices.GetUninitializedObject(modifierType); + } + + info.Modifiers.Add(modifier); + + if (modifier is not GameModifier gameModifier) { return true; } if (modifierType.GetConstructor(Type.EmptyTypes) == null) { - Logger.Error($"Modifier {modifierType.FullName} does not have a parameterless constructor!"); + Logger.Error($"Game Modifier {modifierType.FullName} does not have a parameterless constructor!"); return false; } - if (Activator.CreateInstance(modifierType) is not GameModifier mod) - { - Logger.Error($"Modifier {modifierType.FullName} could not be cast to GameModifier!"); - return false; - } - - info.GameModifiers.Add(mod); - - info.PluginConfig.Bind(mod.AmountDefinition, 0); - info.PluginConfig.Bind(mod.ChanceDefinition, 0); - - var priority = mod!.Priority(); + var priority = gameModifier.Priority(); if (!PrioritiesToIdsMap.TryGetValue(priority, out var list)) { diff --git a/MiraAPI/Modifiers/Types/GameModifier.cs b/MiraAPI/Modifiers/Types/GameModifier.cs index 7e4cccb..abcd1f7 100644 --- a/MiraAPI/Modifiers/Types/GameModifier.cs +++ b/MiraAPI/Modifiers/Types/GameModifier.cs @@ -1,10 +1,4 @@ -using System; -using BepInEx.Configuration; -using MiraAPI.GameOptions.OptionTypes; -using MiraAPI.Utilities; -using Reactor.Utilities; - -namespace MiraAPI.Modifiers.Types; +namespace MiraAPI.Modifiers.Types; /// /// The base class for a game modifier. Game modifiers are applied at the start of the game on top of the player's role. @@ -14,72 +8,17 @@ public abstract class GameModifier : BaseModifier /// public override bool ShowInFreeplay => true; - /// - /// Gets the assignment configuration for the modifier. - /// - public virtual AssignmentConfiguration AssignmentConfiguration => new(0, 0, true, true); - - internal ConfigDefinition AmountDefinition => new("Modifiers", $"Amount {GetType().FullName}"); - - internal ConfigDefinition ChanceDefinition => new("Modifiers", $"Chance {GetType().FullName}"); - - internal ModdedNumberOption AmountOption { get; } = new("Amount", 0, 0, 15, 1, MiraNumberSuffixes.None); - - internal ModdedNumberOption ChanceOption { get; } = new("Chance", 0, 0, 100, 1, MiraNumberSuffixes.Percent); - /// /// Gets the chance of the modifier being assigned to a player. /// /// An int value between 0 and 100 representing percent. - public virtual int GetAssignmentChance() - { - return AssignmentConfiguration.CreateChanceOption && - ParentMod.PluginConfig.TryGetEntry(ChanceDefinition, out ConfigEntry entry) - ? entry.Value - : AssignmentConfiguration.DefaultChance; - } + public abstract int GetAssignmentChance(); /// /// Gets the amount of players that can have this modifier in a game. /// /// An int value greater than or equal to zero. - public virtual int GetAmountPerGame() - { - return AssignmentConfiguration.CreateAmountOption && - ParentMod.PluginConfig.TryGetEntry(AmountDefinition, out ConfigEntry entry) - ? entry.Value - : AssignmentConfiguration.DefaultAmount; - } - - /// - /// Sets the chance of the modifier being assigned to a player. - /// - /// The chance between 0 and 100. - public virtual void SetAssignmentChance(int chance) - { - if (ParentMod.PluginConfig.TryGetEntry(ChanceDefinition, out ConfigEntry entry)) - { - entry.Value = Math.Clamp(chance, 0, 100); - return; - } - - Logger.Error($"Error getting chance configuration for modifier: {ModifierName}"); - } - - /// - /// Sets the amount of players that can have this modifier in a game. - /// - /// The amount of players between zero and the max player count. - public virtual void SetAmountPerGame(int amount) - { - if (ParentMod.PluginConfig.TryGetEntry(AmountDefinition, out ConfigEntry entry)) - { - entry.Value = Math.Max(amount, 0); - return; - } - - Logger.Error($"Error getting amount configuration for modifier: {ModifierName}"); - } + public abstract int GetAmountPerGame(); /// /// Gets the priority at which the modifier will spawn. The higher the value, the higher up on the assignment list. diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index 7867c20..744c754 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -2,6 +2,7 @@ using System.Linq; using HarmonyLib; using MiraAPI.GameOptions; +using MiraAPI.Modifiers; using MiraAPI.Modifiers.Types; using MiraAPI.Roles; using MiraAPI.Utilities; @@ -44,7 +45,7 @@ public static class GameOptionsMenuPatch { var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups - .Where(x => x is not IOptionableGroup) ?? []; + .Where(x => x.OptionableType == null) ?? []; foreach (var group in filteredGroups) { @@ -109,36 +110,22 @@ public static class GameOptionsMenuPatch private static void ModifiersUpdate(ref float num) { - var modifiers = GameSettingMenuPatches.SelectedMod?.Modifiers.Select(x=>x.ModifierOptionsGroup) ?? []; + var groups = GameSettingMenuPatches.SelectedMod?.OptionGroups + .Where(x => x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? []; - foreach (var modGroup in modifiers) + foreach (var modGroup in groups) { - if (modGroup != null) - { - UpdateGroup(modGroup, ref num); - } + UpdateGroup(modGroup, ref num); } } private static void ModifiersCreate(GameOptionsMenu menu) { - var modifiers = GameSettingMenuPatches.SelectedMod?.Modifiers ?? []; - - var optLookup = GameSettingMenuPatches.SelectedMod?.OptionGroups - .OfType() - .ToLookup(x => x.OptionableType); - - foreach (var mod in modifiers) + var groups = GameSettingMenuPatches.SelectedMod?.OptionGroups + .Where(x => x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? []; + foreach (var group in groups) { - IModdedOption[] opts = mod is GameModifier gm ? [gm.AmountOption, gm.ChanceOption] : []; - if (optLookup?[mod.GetType()].Any() == false && opts.Length == 0) - { - continue; - } - - var groups = optLookup?[mod.GetType()] ?? []; - mod.ModifierOptionsGroup = new ModifierOptionGroup(mod.ModifierName, opts, [.. groups.OfType()]); - CreateGroup(menu, mod.ModifierOptionsGroup); + CreateGroup(menu, group); } } @@ -161,7 +148,7 @@ public static class GameOptionsMenuPatch } var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups - .Where(x => x is not IOptionableGroup) ?? []; + .Where(x => x.OptionableType == null) ?? []; foreach (var group in filteredGroups) { diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index ab3ead7..bb52c4a 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -1,5 +1,8 @@ using System; +using System.Linq; using HarmonyLib; +using MiraAPI.Modifiers; +using MiraAPI.Modifiers.Types; using MiraAPI.PluginLoading; using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; @@ -73,36 +76,43 @@ internal static class GameSettingMenuPatches var nextButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject; nextButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f); nextButton.name = "RightArrowButton"; - nextButton.transform.FindChild("Inactive").gameObject.GetComponent().sprite = MiraAssets.NextButton.LoadAsset(); - nextButton.transform.FindChild("Active").gameObject.GetComponent().sprite = MiraAssets.NextButtonActive.LoadAsset(); + nextButton.transform.FindChild("Inactive").gameObject.GetComponent().sprite = + MiraAssets.NextButton.LoadAsset(); + nextButton.transform.FindChild("Active").gameObject.GetComponent().sprite = + MiraAssets.NextButtonActive.LoadAsset(); nextButton.gameObject.GetComponent().DestroyImmediate(); var passiveButton = nextButton.gameObject.GetComponent(); passiveButton.OnClick = new ButtonClickedEvent(); - passiveButton.OnClick.AddListener((UnityAction)(() => - { - SelectedModIdx += 1; - if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins().Length) + passiveButton.OnClick.AddListener( + (UnityAction)(() => { - SelectedModIdx = 0; - } - UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); - })); + SelectedModIdx += 1; + if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins().Length) + { + SelectedModIdx = 0; + } + + UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); + })); var backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject; backButton.transform.localPosition = new Vector3(-4.4209f, 1.5272f, -25f); backButton.name = "LeftArrowButton"; backButton.gameObject.GetComponent().Destroy(); - backButton.transform.FindChild("Active").gameObject.GetComponent().flipX = backButton.transform.FindChild("Inactive").gameObject.GetComponent().flipX = true; - backButton.gameObject.GetComponent().OnClick.AddListener((UnityAction)(() => - { - SelectedModIdx -= 1; - if (SelectedModIdx < 0) + backButton.transform.FindChild("Active").gameObject.GetComponent().flipX = + backButton.transform.FindChild("Inactive").gameObject.GetComponent().flipX = true; + backButton.gameObject.GetComponent().OnClick.AddListener( + (UnityAction)(() => { - SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins().Length; - } - UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); - })); + SelectedModIdx -= 1; + if (SelectedModIdx < 0) + { + SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins().Length; + } + + UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); + })); // clone game settings tab for modifiers _modifiersTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent); @@ -110,23 +120,33 @@ internal static class GameSettingMenuPatches // create button for modifiers __instance.RoleSettingsButton.gameObject.SetActive(false); - _smallRolesButton = Object.Instantiate(__instance.RoleSettingsButton, __instance.RoleSettingsButton.transform.parent); - var pos = new Vector3(-3.65f, _smallRolesButton.transform.localPosition.y, _smallRolesButton.transform.localPosition.z); + _smallRolesButton = Object.Instantiate( + __instance.RoleSettingsButton, + __instance.RoleSettingsButton.transform.parent); + var pos = new Vector3( + -3.65f, + _smallRolesButton.transform.localPosition.y, + _smallRolesButton.transform.localPosition.z); _smallRolesButton.transform.localPosition = pos; - _smallRolesButton.OnClick.AddListener((UnityAction)(() => - { - __instance.ChangeTab(2, false); - })); - _smallRolesButton.OnMouseOver.AddListener((UnityAction)(() => - { - __instance.ChangeTab(2, true); - })); + _smallRolesButton.OnClick.AddListener( + (UnityAction)(() => + { + __instance.ChangeTab(2, false); + })); + _smallRolesButton.OnMouseOver.AddListener( + (UnityAction)(() => + { + __instance.ChangeTab(2, true); + })); var roleText = _smallRolesButton.buttonText; roleText.text = "Roles"; roleText.GetComponent().Destroy(); roleText.alignment = TextAlignmentOptions.Center; - roleText.transform.parent.localPosition = new Vector3(-.525f, roleText.transform.parent.localPosition.y, roleText.transform.parent.localPosition.z); + roleText.transform.parent.localPosition = new Vector3( + -.525f, + roleText.transform.parent.localPosition.y, + roleText.transform.parent.localPosition.z); foreach (var collider in _smallRolesButton.Colliders) { @@ -135,6 +155,7 @@ internal static class GameSettingMenuPatches col.size = new Vector2(col.size.x / 2, col.size.y); } } + foreach (var rend in _smallRolesButton.GetComponentsInChildren(true)) { rend.size = new Vector2(rend.size.x / 2, rend.size.y); @@ -142,15 +163,17 @@ internal static class GameSettingMenuPatches _modifiersButton = Object.Instantiate(_smallRolesButton, _smallRolesButton.transform.parent); _modifiersButton.OnClick = new ButtonClickedEvent(); - _modifiersButton.OnClick.AddListener((UnityAction)(() => - { - __instance.ChangeTab(3, false); - })); + _modifiersButton.OnClick.AddListener( + (UnityAction)(() => + { + __instance.ChangeTab(3, false); + })); _modifiersButton.OnMouseOver = new UnityEvent(); - _modifiersButton.OnMouseOver.AddListener((UnityAction)(() => - { - __instance.ChangeTab(3, true); - })); + _modifiersButton.OnMouseOver.AddListener( + (UnityAction)(() => + { + __instance.ChangeTab(3, true); + })); _modifiersButton.buttonText.text = "Modifiers"; pos.x = -2.27f; @@ -179,7 +202,8 @@ internal static class GameSettingMenuPatches _modifiersButton.gameObject.SetActive(false); _smallRolesButton.gameObject.SetActive(false); menu.RoleSettingsButton.gameObject.SetActive(false); - if (SelectedModIdx != 0 && SelectedMod?.GameModifiers.Count != 0) + if (SelectedModIdx != 0 && + SelectedMod?.OptionGroups.Exists(x => x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) == true) { _modifiersButton.gameObject.SetActive(true); _smallRolesButton.gameObject.SetActive(true); @@ -225,11 +249,13 @@ internal static class GameSettingMenuPatches { child.gameObject.DestroyImmediate(); } + roles.advancedSettingChildren.Clear(); roles.advancedSettingChildren = null; } - foreach (var header in roles.RoleChancesSettings.transform.GetComponentsInChildren()) + foreach (var header in roles.RoleChancesSettings.transform + .GetComponentsInChildren()) { header.gameObject.DestroyImmediate(); } diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index bda1c2b..fedded2 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -127,7 +127,7 @@ public static class LobbyViewPanePatches var num = 1.44f; var filteredGroups = SelectedMod.OptionGroups - .Where(x => x.GroupVisible.Invoke() && x is not IOptionableGroup); + .Where(x => x.OptionableType == null && x.GroupVisible.Invoke()); foreach (var group in filteredGroups) { @@ -280,7 +280,7 @@ public static class LobbyViewPanePatches viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f); var advancedRoleOptions = SelectedMod.OptionGroups - .Where(x => x is IOptionableGroup optionGroup && optionGroup.OptionableType == customRole.GetType()) + .Where(x => x.OptionableType == customRole.GetType()) .SelectMany(x => x.Options) .ToList(); @@ -390,7 +390,7 @@ public static class LobbyViewPanePatches var num2 = 1.08f; var filteredOptions = SelectedMod.OptionGroups - .Where(x => x is IOptionableGroup optionGroup && optionGroup.OptionableType == roleType) + .Where(x => x.OptionableType == roleType) .SelectMany(x=>x.Options) .ToList(); diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 13c190f..af17682 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -307,7 +307,7 @@ public static class RoleSettingMenuPatches // TODO: create sub groups under the role settings. var filteredOptions = GameSettingMenuPatches.SelectedMod?.OptionGroups - .Where(x=> x is IOptionableGroup optionableGroup && optionableGroup.OptionableType == role.GetType()) + .Where(x=> x.OptionableType == role.GetType()) .SelectMany(x=>x.Options) .ToList() ?? []; @@ -471,7 +471,7 @@ public static class RoleSettingMenuPatches if (GameSettingMenuPatches.SelectedMod is null || GameSettingMenuPatches.SelectedMod.OptionGroups - .Exists(x => x is IOptionableGroup optionGroup && optionGroup.OptionableType == role.GetType())) + .Exists(x => x.OptionableType == role.GetType())) { var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform); newButton.name = "ConfigButton"; diff --git a/MiraAPI/PluginLoading/MiraPluginInfo.cs b/MiraAPI/PluginLoading/MiraPluginInfo.cs index 2dfe479..efbf20c 100644 --- a/MiraAPI/PluginLoading/MiraPluginInfo.cs +++ b/MiraAPI/PluginLoading/MiraPluginInfo.cs @@ -74,8 +74,6 @@ public class MiraPluginInfo internal List Modifiers { get; } = []; - internal List GameModifiers { get; } = []; - internal Dictionary CustomRoles { get; } = []; internal Dictionary GameModes { get; } = []; diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index eced634..15fe686 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using BepInEx.Unity.IL2CPP; @@ -78,7 +79,7 @@ public sealed class MiraPluginManager if (RegisterRoleAttribute(type, info, out var role)) { - roles.Add(role!); + roles.Add(role); continue; } @@ -172,7 +173,7 @@ public sealed class MiraPluginManager return false; } - private static bool RegisterRoleAttribute(Type type, MiraPluginInfo pluginInfo, out Type? role) + private static bool RegisterRoleAttribute(Type type, MiraPluginInfo pluginInfo, [NotNullWhen(true)] out Type? role) { role = null; try