From edca42247d96cce24e6d56680889edc3a353a1a1 Mon Sep 17 00:00:00 2001 From: AlchlcDvl <56699741+AlchlcDvl@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:52:18 -0500 Subject: [PATCH] finish full implementation of game modes i hope --- MiraAPI/GameModes/AbstractGameMode.cs | 30 +++++++++---------- MiraAPI/GameOptions/AbstractOptionGroup.cs | 5 ++++ .../Attributes/ModdedEnumOptionAttribute.cs | 11 +++++-- .../Attributes/ModdedNumberOptionAttribute.cs | 15 ++++++++-- .../Attributes/ModdedOptionAttribute.cs | 8 ++++- .../Attributes/ModdedToggleOptionAttribute.cs | 5 ++-- MiraAPI/GameOptions/IModdedOption.cs | 1 + MiraAPI/GameOptions/ModdedOptionsManager.cs | 1 + .../OptionTypes/ModdedEnumOption.cs | 3 +- .../OptionTypes/ModdedNumberOption.cs | 4 ++- .../GameOptions/OptionTypes/ModdedOption.cs | 14 ++++++++- .../OptionTypes/ModdedToggleOption.cs | 3 +- .../Patches/GameModes/IntroCutscenePatch.cs | 20 +++++++++++++ MiraAPI/Patches/GameModes/WinnersPatch.cs | 27 +++++++++++++++++ .../Patches/Options/GameOptionsMenuPatch.cs | 6 ++-- .../Patches/Options/LobbyViewPanePatches.cs | 2 +- .../Patches/Options/RoleSettingMenuPatches.cs | 15 ++++++---- MiraAPI/Roles/ICustomRole.cs | 9 +++++- 18 files changed, 141 insertions(+), 38 deletions(-) create mode 100644 MiraAPI/Patches/GameModes/IntroCutscenePatch.cs create mode 100644 MiraAPI/Patches/GameModes/WinnersPatch.cs diff --git a/MiraAPI/GameModes/AbstractGameMode.cs b/MiraAPI/GameModes/AbstractGameMode.cs index 0f820c8..b7acea3 100644 --- a/MiraAPI/GameModes/AbstractGameMode.cs +++ b/MiraAPI/GameModes/AbstractGameMode.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System.Collections; +using System.Collections.Generic; using UnityEngine; namespace MiraAPI.GameModes; @@ -28,6 +29,11 @@ public abstract class AbstractGameMode /// public uint ID { get; set; } + /// + /// Gets a value indicating whether a custom intro sequence is implemented by the game mode. + /// + public virtual bool ShowGameModeIntroCutscene => false; + /// /// Called when Intro Cutscene is destroyed. /// @@ -91,18 +97,6 @@ public abstract class AbstractGameMode runOriginal = true; } - /// - /// Should Roles Settings be available when this gamemode is selected. - /// - /// True if Role Settings are enabled in this game mode. - public virtual bool AreRoleSettingsEnabled() => true; - - /// - /// Should Game Settings be available when this gamemode is selected. - /// - /// True if Game Settings are enabled in this mode. - public virtual bool AreGameSettingsEnabled() => true; - /// /// Custom winner selection. /// @@ -110,10 +104,14 @@ public abstract class AbstractGameMode public virtual List? CalculateWinners() => null; /// - /// Show gamemode in Intro Cutscene. + /// The IEnumerator that plays the intro cutscene for this gamemode. /// - /// True if the game mode should be shown in the intro cutscene. - public virtual bool ShowGameModeIntroCutscene() => false; + /// An instance of IntroCutscene. + /// An IEnumerator to run the intro cutscene instead of the base game one. + public virtual IEnumerator IntroCutscene(IntroCutscene __instance) + { + yield return new WaitForEndOfFrame(); + } /// /// Can Admin be used in this gamemode. diff --git a/MiraAPI/GameOptions/AbstractOptionGroup.cs b/MiraAPI/GameOptions/AbstractOptionGroup.cs index 418f25b..9cbdc95 100644 --- a/MiraAPI/GameOptions/AbstractOptionGroup.cs +++ b/MiraAPI/GameOptions/AbstractOptionGroup.cs @@ -37,6 +37,11 @@ public abstract class AbstractOptionGroup /// public virtual Type? AdvancedRole => null; + /// + /// Gets the role the group is associated with. This is used for the advanced role options menu. + /// + public virtual Type? AdvancedMode => null; + internal bool AllOptionsHidden { get; set; } internal CategoryHeaderMasked? Header { get; set; } diff --git a/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs index 2944520..5bb8433 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs @@ -7,13 +7,18 @@ namespace MiraAPI.GameOptions.Attributes; /// /// Attribute for creating an enum option. /// +/// The title of the option. +/// The Enum type. +/// An option list of string values to use in place of the enum name. +/// An optional role type. +/// An optional game mode type. [AttributeUsage(AttributeTargets.Property)] -public class ModdedEnumOptionAttribute(string title, Type enumType, string[]? values = null, Type? roleType = null) - : ModdedOptionAttribute(title, roleType) +public class ModdedEnumOptionAttribute(string title, Type enumType, string[]? values = null, Type? roleType = null, Type? modeType = null) + : ModdedOptionAttribute(title, roleType, modeType) { internal override IModdedOption CreateOption(object? value, PropertyInfo property) { - var opt = new ModdedEnumOption(Title, (int)(value ?? 0), enumType, values, RoleType); + var opt = new ModdedEnumOption(Title, (int)(value ?? 0), enumType, values, RoleType, ModeType); return opt; } diff --git a/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs index 2bd4438..50db882 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs @@ -8,6 +8,14 @@ namespace MiraAPI.GameOptions.Attributes; /// /// A number option attribute for the modded options system. /// +/// The title of the option. +/// The minimum value. +/// The maximum value. +/// The increment. +/// The suffix type. +/// Whether zero is infinity or not. +/// An optional role type. +/// An optional game mode type. [AttributeUsage(AttributeTargets.Property)] public class ModdedNumberOptionAttribute( string title, @@ -16,12 +24,13 @@ public class ModdedNumberOptionAttribute( float increment = 1, MiraNumberSuffixes suffixType = MiraNumberSuffixes.None, bool zeroInfinity = false, - Type? roleType = null) - : ModdedOptionAttribute(title, roleType) + Type? roleType = null, + Type? modeType = null) + : ModdedOptionAttribute(title, roleType, modeType) { internal override IModdedOption CreateOption(object? value, PropertyInfo property) { - return new ModdedNumberOption(Title, (float)(value ?? min+increment), min, max, increment, suffixType, zeroInfinity, RoleType); + return new ModdedNumberOption(Title, (float)(value ?? min+increment), min, max, increment, suffixType, zeroInfinity, RoleType, ModeType); } /// diff --git a/MiraAPI/GameOptions/Attributes/ModdedOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedOptionAttribute.cs index 702d75e..bb9dab5 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedOptionAttribute.cs @@ -8,8 +8,9 @@ namespace MiraAPI.GameOptions.Attributes; /// /// The option title. /// Optional parameter to specify a role Type. +/// Optional parameter to specify a game mode Type. [AttributeUsage(AttributeTargets.Property)] -public abstract class ModdedOptionAttribute(string title, Type? roleType = null) : Attribute +public abstract class ModdedOptionAttribute(string title, Type? roleType = null, Type? modeType = null) : Attribute { internal IModdedOption? HolderOption { get; set; } @@ -23,6 +24,11 @@ public abstract class ModdedOptionAttribute(string title, Type? roleType = null) /// protected Type? RoleType { get; private set; } = roleType; + /// + /// Gets the game mode type of the option. + /// + protected Type? ModeType { get; private set; } = modeType; + /// /// Sets the value of the option. /// diff --git a/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs index c389292..5adfe77 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs @@ -9,12 +9,13 @@ namespace MiraAPI.GameOptions.Attributes; /// /// The option title. /// An optional role type. +/// An optional game mode type. [AttributeUsage(AttributeTargets.Property)] -public class ModdedToggleOptionAttribute(string title, Type? roleType = null) : ModdedOptionAttribute(title, roleType) +public class ModdedToggleOptionAttribute(string title, Type? roleType = null, Type? modeType = null) : ModdedOptionAttribute(title, roleType, modeType) { internal override IModdedOption CreateOption(object? value, PropertyInfo property) { - var toggleOpt = new ModdedToggleOption(Title, (bool)(value ?? false), RoleType); + var toggleOpt = new ModdedToggleOption(Title, (bool)(value ?? false), RoleType, ModeType); return toggleOpt; } diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index 1aee3c4..b2c542e 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -14,6 +14,7 @@ public interface IModdedOption IMiraPlugin? ParentMod { get; set; } BaseGameSetting Data { get; } Type? AdvancedRole { get; set; } + Type? AdvancedMode { get; set; } OptionBehaviour? OptionBehaviour { get; } Func Visible { get; set; } ConfigDefinition? ConfigDefinition { get; set; } diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index 3f30019..1913d07 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -121,6 +121,7 @@ public static class ModdedOptionsManager option.ParentMod = pluginInfo.MiraPlugin; option.AdvancedRole = group.AdvancedRole; + option.AdvancedMode = group.AdvancedMode; pluginInfo.Options.Add(option); diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs index 0d236a0..74c1860 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs @@ -26,7 +26,8 @@ public class ModdedEnumOption : ModdedOption /// The Enum type. /// An option list of string values to use in place of the enum name. /// An optional role type to specify for a specific role. - public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null, Type? roleType = null) : base(title, defaultValue, roleType) + /// An optional game mode type to specify for a specific mode. + public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null, Type? roleType = null, Type? modeType = null) : base(title, defaultValue, roleType, modeType) { Values = values ?? Enum.GetNames(enumType); Data = ScriptableObject.CreateInstance(); diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs index e86d88d..3256654 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs @@ -48,6 +48,7 @@ public class ModdedNumberOption : ModdedOption /// The suffix type. /// Whether zero is infinity or not. /// An optional role type. + /// An optional game mode type. public ModdedNumberOption( string title, float defaultValue, @@ -56,7 +57,8 @@ public class ModdedNumberOption : ModdedOption float increment, MiraNumberSuffixes suffixType, bool zeroInfinity = false, - Type? roleType = null) : base(title, defaultValue, roleType) + Type? roleType = null, + Type? modeType = null) : base(title, defaultValue, roleType, modeType) { Min = min; Max = max; diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index d811f1d..3719353 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -1,4 +1,5 @@ using BepInEx.Configuration; +using MiraAPI.GameModes; using MiraAPI.Networking; using MiraAPI.PluginLoading; using MiraAPI.Roles; @@ -77,6 +78,11 @@ public abstract class ModdedOption : IModdedOption /// public Type? AdvancedRole { get; set; } + /// + /// Gets or sets the advanced mode of the option. + /// + public Type? AdvancedMode { get; set; } + /// /// Gets or sets the option behaviour of the option. /// @@ -103,7 +109,8 @@ public abstract class ModdedOption : IModdedOption /// The option title. /// The default value. /// The Role Type or null if it doesn't belong to a role. - protected ModdedOption(string title, T defaultValue, Type? roleType) + /// The Game Mode Type or null if it doesn't belong to a mode. + protected ModdedOption(string title, T defaultValue, Type? roleType, Type? modeType) { Id = ModdedOptionsManager.NextId; Title = title; @@ -116,6 +123,11 @@ public abstract class ModdedOption : IModdedOption { AdvancedRole = roleType; } + + if (modeType is not null && modeType.IsAssignableTo(typeof(AbstractGameMode))) + { + AdvancedMode = modeType; + } } internal void ValueChanged(OptionBehaviour optionBehaviour) diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs index 12f8f5f..79e8281 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs @@ -16,7 +16,8 @@ public class ModdedToggleOption : ModdedOption /// The option title. /// The default value. /// An optional role type. - public ModdedToggleOption(string title, bool defaultValue, Type? roleType = null) : base(title, defaultValue, roleType) + /// An optional game mode type. + public ModdedToggleOption(string title, bool defaultValue, Type? roleType = null, Type? modeType = null) : base(title, defaultValue, roleType, modeType) { Data = ScriptableObject.CreateInstance(); diff --git a/MiraAPI/Patches/GameModes/IntroCutscenePatch.cs b/MiraAPI/Patches/GameModes/IntroCutscenePatch.cs new file mode 100644 index 0000000..5701014 --- /dev/null +++ b/MiraAPI/Patches/GameModes/IntroCutscenePatch.cs @@ -0,0 +1,20 @@ +using BepInEx.Unity.IL2CPP.Utils.Collections; +using HarmonyLib; +using MiraAPI.GameModes; + +namespace MiraAPI.Patches.GameModes; + +[HarmonyPatch(typeof(IntroCutscene), nameof(IntroCutscene.CoBegin))] +internal static class IntroCutscenePatch +{ + public static bool Prefix(IntroCutscene __instance, ref Il2CppSystem.Collections.IEnumerator __result) + { + if (CustomGameModeManager.ActiveMode?.ShowGameModeIntroCutscene == true) + { + __result = CustomGameModeManager.ActiveMode.IntroCutscene(__instance).WrapToIl2Cpp(); + return false; + } + + return true; + } +} diff --git a/MiraAPI/Patches/GameModes/WinnersPatch.cs b/MiraAPI/Patches/GameModes/WinnersPatch.cs new file mode 100644 index 0000000..01f9859 --- /dev/null +++ b/MiraAPI/Patches/GameModes/WinnersPatch.cs @@ -0,0 +1,27 @@ +using HarmonyLib; +using MiraAPI.GameModes; + +namespace MiraAPI.Patches.GameModes; + +[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnGameEnd))] +internal static class WinnersPatch +{ + [HarmonyPostfix] + public static void ShowWinningScreen() + { + if (CustomGameModeManager.ActiveMode is null or DefaultMode) + return; + + var winners = CustomGameModeManager.ActiveMode.CalculateWinners(); + + if (winners is null) + return; + + EndGameResult.CachedWinners.Clear(); + + foreach (var data in winners) + { + EndGameResult.CachedWinners.Add(new(data)); + } + } +} diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index 92cd925..7e75ede 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using MiraAPI.GameModes; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Localization.Utilities; @@ -32,7 +33,7 @@ public static class GameOptionsMenuPatch var num = 2.1f; var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups.Where( - x => x.AdvancedRole is null) ?? []; + x => x.AdvancedRole is null && (x.AdvancedMode is null || x.AdvancedMode == CustomGameModeManager.ActiveMode?.GetType())) ?? []; foreach (var group in filteredGroups) { @@ -99,7 +100,8 @@ public static class GameOptionsMenuPatch __instance.MapPicker.gameObject.SetActive(false); - var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups.Where(x => x.AdvancedRole is null) ?? []; + var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups.Where(x => x.AdvancedRole is null && (x.AdvancedMode is null || x.AdvancedMode == + CustomGameModeManager.ActiveMode?.GetType())) ?? []; foreach (var group in filteredGroups) { diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index 0649873..e00d1cd 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -280,7 +280,7 @@ public static class LobbyViewPanePatches viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f); var advancedRoleOptions = SelectedMod.Options - .Where(x => x.AdvancedRole == customRole.GetType()) + .Where(x => x.AdvancedRole == customRole.GetType() && (x.AdvancedMode is null || x.AdvancedMode == customRole.AssociatedGameMode)) .ToList(); if (!flag && advancedRoleOptions.Count > 0) diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 4461423..17bce70 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -1,5 +1,6 @@ using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; +using MiraAPI.GameModes; using MiraAPI.Networking; using MiraAPI.Roles; using MiraAPI.Utilities; @@ -18,7 +19,7 @@ using Object = UnityEngine.Object; namespace MiraAPI.Patches.Options; [HarmonyPatch(typeof(RolesSettingsMenu))] -public static class RoleSettingMenuPatches +internal static class RoleSettingMenuPatches { [HarmonyPrefix] [HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))] @@ -56,7 +57,8 @@ public static class RoleSettingMenuPatches var crewRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values .OfType() - .Where(role => role is { Team: ModdedRoleTeams.Crewmate, Configuration.HideSettings: false }) + .Where(role => role is { Team: ModdedRoleTeams.Crewmate, Configuration.HideSettings: false } && (role.AssociatedGameMode is null || role.AssociatedGameMode == + CustomGameModeManager.ActiveMode?.GetType())) .ToList() ?? []; if (crewRoles is { Count: > 0 }) @@ -82,7 +84,8 @@ public static class RoleSettingMenuPatches var impRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values .OfType() - .Where(role => role is { Team: ModdedRoleTeams.Impostor, Configuration.HideSettings: false }) + .Where(role => role is { Team: ModdedRoleTeams.Impostor, Configuration.HideSettings: false } && (role.AssociatedGameMode is null || role.AssociatedGameMode == + CustomGameModeManager.ActiveMode?.GetType())) .ToList() ?? []; if (impRoles is { Count: > 0 }) @@ -109,7 +112,8 @@ public static class RoleSettingMenuPatches var neutRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values .OfType() - .Where(role => role is { Team: ModdedRoleTeams.Neutral, Configuration.HideSettings: false }) + .Where(role => role is { Team: ModdedRoleTeams.Neutral, Configuration.HideSettings: false } && (role.AssociatedGameMode is null || role.AssociatedGameMode == + CustomGameModeManager.ActiveMode?.GetType())) .ToList() ?? []; if (neutRoles is { Count: > 0 }) @@ -208,7 +212,8 @@ public static class RoleSettingMenuPatches var num = -0.872f; - var filteredOptions = GameSettingMenuPatches.SelectedMod?.Options.Where(x => x.AdvancedRole == role.GetType()) ?? []; + var filteredOptions = GameSettingMenuPatches.SelectedMod?.Options?.Where(x => x.AdvancedRole == role.GetType() && (x.AdvancedMode is null || x.AdvancedMode == + CustomGameModeManager.ActiveMode?.GetType())) ?? []; foreach (var option in filteredOptions) { diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index ba318b2..eae4d30 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -1,9 +1,11 @@ using BepInEx.Configuration; +using MiraAPI.GameModes; using MiraAPI.Modifiers; using MiraAPI.PluginLoading; using MiraAPI.Utilities; -using System.Text; using Reactor.Utilities; +using System; +using System.Text; using UnityEngine; namespace MiraAPI.Roles; @@ -48,6 +50,11 @@ public interface ICustomRole /// MiraPluginInfo ParentMod => CustomRoleManager.FindParentMod(this); + /// + /// Gets the game mode this role is a part of. + /// + Type AssociatedGameMode => typeof(DefaultMode); + /// /// This method runs on the PlayerControl.FixedUpdate method for ALL players with this role. ///