diff --git a/.editorconfig b/.editorconfig index 2577e5d..b19810c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -38,3 +38,48 @@ dotnet_diagnostic.S2245.severity = none # IDE0130: Namespace does not match folder structure dotnet_diagnostic.IDE0130.severity = none + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = none + +# SA1516: Elements should be separated by blank line +dotnet_diagnostic.SA1516.severity = none + +# SA1503: Braces should not be omitted +dotnet_diagnostic.SA1503.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = none + +# SA1204: Static elements should appear before instance elements +dotnet_diagnostic.SA1204.severity = none + +# SA1202: Elements should be ordered by access +dotnet_diagnostic.SA1202.severity = none + +# SA1649: File name should match first type name +dotnet_diagnostic.SA1649.severity = none + +# SA1313: Parameter names should begin with lower-case letter +dotnet_diagnostic.SA1313.severity = none + +# SA1636: The file header copyright text should match the copyright text from the settings +dotnet_diagnostic.SA1636.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1407: Arithmetic expressions should declare precedence +dotnet_diagnostic.SA1407.severity = none + +# SA1133: Each attribute should be placed in its own set of square brackets +dotnet_diagnostic.SA1133.severity = none diff --git a/Directory.Build.props b/Directory.Build.props index de8767b..4bdcab7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,18 +28,15 @@ latest recommended true + $(NoWarn);SA0001 + + + + - - - - all - runtime; build; native; contentfiles; analyzers - + - - $(MSBuildThisFileDirectory)\stylecop.ruleset - diff --git a/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs b/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs index 8dead81..cf963fe 100644 --- a/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs +++ b/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs @@ -61,11 +61,14 @@ public class TeleportButton : CustomActionButton { Camera.main.orthographicSize = MeetingHud.Instance ? 3f : ft; ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height, Screen.width, Screen.height, Screen.fullScreen); - foreach (var cam in Camera.allCameras) cam.orthographicSize = Camera.main.orthographicSize; + foreach (var cam in Camera.allCameras) + cam.orthographicSize = Camera.main.orthographicSize; + yield return null; } - foreach (var cam in Camera.allCameras) cam.orthographicSize = zoomDistance; + foreach (var cam in Camera.allCameras) + cam.orthographicSize = zoomDistance; ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height, Screen.width, Screen.height, Screen.fullScreen); } @@ -75,12 +78,14 @@ public class TeleportButton : CustomActionButton { Camera.main.orthographicSize = MeetingHud.Instance ? 3f : ft; ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height, Screen.width, Screen.height, Screen.fullScreen); - foreach (var cam in Camera.allCameras) cam.orthographicSize = Camera.main.orthographicSize; + foreach (var cam in Camera.allCameras) + cam.orthographicSize = Camera.main.orthographicSize; yield return null; } - foreach (var cam in Camera.allCameras) cam.orthographicSize = 3f; + foreach (var cam in Camera.allCameras) + cam.orthographicSize = 3f; HudManager.Instance.ShadowQuad.gameObject.SetActive(true); IsZoom = false; diff --git a/MiraAPI.Example/ExampleEventHandlers.cs b/MiraAPI.Example/ExampleEventHandlers.cs index 20c0def..3647899 100644 --- a/MiraAPI.Example/ExampleEventHandlers.cs +++ b/MiraAPI.Example/ExampleEventHandlers.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; using MiraAPI.Events; using MiraAPI.Events.Mira; diff --git a/MiraAPI.Example/Options/ExampleOptions.cs b/MiraAPI.Example/Options/ExampleOptions.cs index 696653e..cba80e6 100644 --- a/MiraAPI.Example/Options/ExampleOptions.cs +++ b/MiraAPI.Example/Options/ExampleOptions.cs @@ -1,7 +1,7 @@ -using MiraAPI.GameOptions; +using System.Collections.Generic; +using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; using MiraAPI.Utilities; -using System.Collections.Generic; using UnityEngine; namespace MiraAPI.Example.Options; diff --git a/MiraAPI.Example/Roles/ChameleonRole.cs b/MiraAPI.Example/Roles/ChameleonRole.cs index c805763..e6e3adf 100644 --- a/MiraAPI.Example/Roles/ChameleonRole.cs +++ b/MiraAPI.Example/Roles/ChameleonRole.cs @@ -9,13 +9,13 @@ namespace MiraAPI.Example.Roles; public class ChameleonRole : CrewmateRole, ICustomRole { + private bool shouldHide; + public string IdPart => "Chameleon"; public string IdPrefix => "ApiExample.Role.Crewmate"; public Color RoleColor => Palette.AcceptedGreen; public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate; - private bool _shouldHide; - public CustomRoleConfiguration Configuration => new(this) { OptionsScreenshot = ExampleAssets.Banner, @@ -30,12 +30,12 @@ public class ChameleonRole : CrewmateRole, ICustomRole { Logger.Info("Initializing ChameleonRole for player: " + player.PlayerId); RoleBehaviourStubs.Initialize(this, player); - _shouldHide = true; + shouldHide = true; } public void FixedUpdate() { - if (!Player || !_shouldHide) + if (!Player || !shouldHide) { return; } @@ -76,7 +76,7 @@ public class ChameleonRole : CrewmateRole, ICustomRole { Logger.Info("Deinitializing ChameleonRole for player: " + targetPlayer.PlayerId); RoleBehaviourStubs.Deinitialize(this, targetPlayer); - _shouldHide = false; + shouldHide = false; foreach (var cosmetic in Player.cosmetics.transform.GetComponentsInChildren(true)) { cosmetic.color = Color.white; diff --git a/MiraAPI/Colors/CustomColor.cs b/MiraAPI/Colors/CustomColor.cs index f1b5c49..a778e31 100644 --- a/MiraAPI/Colors/CustomColor.cs +++ b/MiraAPI/Colors/CustomColor.cs @@ -32,18 +32,21 @@ public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 sha /// public CustomColorBrightness ColorBrightness { get; set; } = CustomColorBrightness.Darker; - /// - public CustomColor(StringNames name, Color32 mainColor) : this(name, mainColor, mainColor.GetShadowColor(60)) + /// + public CustomColor(StringNames name, Color32 mainColor) + : this(name, mainColor, mainColor.GetShadowColor(60)) { } - /// - public CustomColor(string name, Color32 mainColor) : this(name, mainColor, mainColor.GetShadowColor(60)) + /// + public CustomColor(string name, Color32 mainColor) + : this(name, mainColor, mainColor.GetShadowColor(60)) { } - /// - public CustomColor(string name, Color32 mainColor, Color32 shadowColor) : this(MiraLocaleManager.GetOrCreateLocaleString(name), mainColor, shadowColor) + /// + public CustomColor(string name, Color32 mainColor, Color32 shadowColor) + : this(MiraLocaleManager.GetOrCreateLocaleString(name), mainColor, shadowColor) { } } diff --git a/MiraAPI/Events/Mira/MiraButtonCancelledEvent.cs b/MiraAPI/Events/Mira/MiraButtonCancelledEvent.cs index 69236bf..6973c4f 100644 --- a/MiraAPI/Events/Mira/MiraButtonCancelledEvent.cs +++ b/MiraAPI/Events/Mira/MiraButtonCancelledEvent.cs @@ -7,7 +7,8 @@ namespace MiraAPI.Events.Mira; /// /// The type. /// The whose click was cancelled. -public sealed class MiraButtonCancelledEvent(T button) : MiraEvent where T : CustomActionButton +public sealed class MiraButtonCancelledEvent(T button) : MiraEvent + where T : CustomActionButton { /// /// Gets the whose click was cancelled. diff --git a/MiraAPI/Events/Mira/MiraButtonClickEvent.cs b/MiraAPI/Events/Mira/MiraButtonClickEvent.cs index 7c764e4..72237d2 100644 --- a/MiraAPI/Events/Mira/MiraButtonClickEvent.cs +++ b/MiraAPI/Events/Mira/MiraButtonClickEvent.cs @@ -8,7 +8,8 @@ namespace MiraAPI.Events.Mira; /// The type. /// The that was clicked. /// The generic invoked before button-specific events. -public class MiraButtonClickEvent(T button, MiraButtonClickEvent genericClickEvent) : MiraCancelableEvent where T : CustomActionButton +public class MiraButtonClickEvent(T button, MiraButtonClickEvent genericClickEvent) : MiraCancelableEvent + where T : CustomActionButton { /// /// Gets the that was clicked. diff --git a/MiraAPI/Events/MiraEventManager.cs b/MiraAPI/Events/MiraEventManager.cs index 745bb10..066ef74 100644 --- a/MiraAPI/Events/MiraEventManager.cs +++ b/MiraAPI/Events/MiraEventManager.cs @@ -17,7 +17,8 @@ public static class MiraEventManager /// The instance. /// Type of Event. /// If there was an event handler invoked for this event, return . Otherwise, return . - public static bool InvokeEvent(this T eventInstance) where T : MiraEvent + public static bool InvokeEvent(this T eventInstance) + where T : MiraEvent { EventWrappers.TryGetValue(typeof(T), out var handlers); @@ -109,7 +110,8 @@ public static class MiraEventManager /// The priority of the event handler. Lower values are called first. /// Type of event. /// A to use when unregistering the event. - public static MiraEventHandle RegisterEventHandler(Action handler, int priority = 0) where T : MiraEvent + public static MiraEventHandle RegisterEventHandler(Action handler, int priority = 0) + where T : MiraEvent { EventWrappers.TryAdd(typeof(T), []); diff --git a/MiraAPI/Events/Vanilla/Gameplay/VanillaButtonCancelledEvent.cs b/MiraAPI/Events/Vanilla/Gameplay/VanillaButtonCancelledEvent.cs index cf1bbbd..7320080 100644 --- a/MiraAPI/Events/Vanilla/Gameplay/VanillaButtonCancelledEvent.cs +++ b/MiraAPI/Events/Vanilla/Gameplay/VanillaButtonCancelledEvent.cs @@ -7,7 +7,8 @@ namespace MiraAPI.Events.Vanilla.Gameplay; /// /// The vanilla type. /// The vanilla whose click was cancelled. -public sealed class VanillaButtonCancelledEvent(T button) : MiraEvent where T : AbilityButton +public sealed class VanillaButtonCancelledEvent(T button) : MiraEvent + where T : AbilityButton { /// /// Gets the Vanilla whose click was cancelled. diff --git a/MiraAPI/Events/Vanilla/Gameplay/VanillaButtonClickEvent.cs b/MiraAPI/Events/Vanilla/Gameplay/VanillaButtonClickEvent.cs index 8c03d30..cd7028a 100644 --- a/MiraAPI/Events/Vanilla/Gameplay/VanillaButtonClickEvent.cs +++ b/MiraAPI/Events/Vanilla/Gameplay/VanillaButtonClickEvent.cs @@ -8,7 +8,8 @@ namespace MiraAPI.Events.Vanilla.Gameplay; /// The vanilla type. /// The vanilla that was clicked. /// The generic invoked before button-specific events. -public sealed class VanillaButtonClickEvent(T button, VanillaButtonClickEvent genericClickEvent) : MiraCancelableEvent where T : AbilityButton +public sealed class VanillaButtonClickEvent(T button, VanillaButtonClickEvent genericClickEvent) : MiraCancelableEvent + where T : AbilityButton { /// /// Gets the vanilla that was clicked. diff --git a/MiraAPI/Events/Vanilla/Meeting/Voting/ProcessVotesEvent.cs b/MiraAPI/Events/Vanilla/Meeting/Voting/ProcessVotesEvent.cs index 9357298..043ec56 100644 --- a/MiraAPI/Events/Vanilla/Meeting/Voting/ProcessVotesEvent.cs +++ b/MiraAPI/Events/Vanilla/Meeting/Voting/ProcessVotesEvent.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using MiraAPI.Voting; @@ -11,7 +12,7 @@ namespace MiraAPI.Events.Vanilla.Meeting.Voting; /// The player to be exiled. Will be if no player is to be exiled. public class ProcessVotesEvent(List votes, NetworkedPlayerInfo? exiledPlayer = null) : MiraEvent { - private readonly List _originalVotes = [.. votes]; + private readonly List originalVotes = [.. votes]; /// /// Gets a value indicating whether the exiled player has been modified by the event. @@ -21,7 +22,7 @@ public class ProcessVotesEvent(List votes, NetworkedPlayerInfo? exil /// /// Gets a value indicating whether the votes have been modified by the event. /// - public bool VotesModified => !Votes.SequenceEqual(_originalVotes); + public bool VotesModified => !Votes.SequenceEqual(originalVotes); /// /// Gets the list of s that are being processed. @@ -41,6 +42,7 @@ public class ProcessVotesEvent(List votes, NetworkedPlayerInfo? exil /// /// Gets or sets the player to be exiled. Will be if no player is to be exiled. /// + [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1513:Closing brace should be followed by blank line", Justification = "Looks weird.")] public NetworkedPlayerInfo? ExiledPlayer { get; diff --git a/MiraAPI/GameEnd/CustomGameOver.cs b/MiraAPI/GameEnd/CustomGameOver.cs index bb9e23b..88bb903 100644 --- a/MiraAPI/GameEnd/CustomGameOver.cs +++ b/MiraAPI/GameEnd/CustomGameOver.cs @@ -48,7 +48,8 @@ public abstract class CustomGameOver /// /// Type of the custom game over. /// The associated with the custom game over. - public static GameOverReason GameOverReason() where T : CustomGameOver + public static GameOverReason GameOverReason() + where T : CustomGameOver { return (GameOverReason)GameOverManager.GetGameOverId(); } @@ -58,7 +59,8 @@ public abstract class CustomGameOver /// /// A collection of winners. /// Type of the custom game over. - public static void Trigger(IEnumerable winners) where T : CustomGameOver + public static void Trigger(IEnumerable winners) + where T : CustomGameOver { var reason = GameOverManager.GetGameOverId(); var data = new GameOverData(reason, [.. winners]); diff --git a/MiraAPI/GameEnd/GameOverManager.cs b/MiraAPI/GameEnd/GameOverManager.cs index 0644cc9..bde0aa8 100644 --- a/MiraAPI/GameEnd/GameOverManager.cs +++ b/MiraAPI/GameEnd/GameOverManager.cs @@ -12,7 +12,7 @@ public static class GameOverManager private static readonly Dictionary GameOverIds = []; private static readonly Dictionary GameOverTypes = []; - private static int _nextId = Enum.GetNames().Length; + private static int nextId = Enum.GetNames().Length; /// /// Register a . @@ -39,9 +39,9 @@ public static class GameOverManager return false; } - GameOverIds.Add(gameOverType, _nextId); - GameOverTypes.Add(_nextId, gameOverType); - _nextId++; + GameOverIds.Add(gameOverType, nextId); + GameOverTypes.Add(nextId, gameOverType); + nextId++; return true; } @@ -68,7 +68,8 @@ public static class GameOverManager /// /// Type of the . /// The ID of the . - public static int GetGameOverId() where T : CustomGameOver + public static int GetGameOverId() + where T : CustomGameOver { return GetGameOverId(typeof(T)); } diff --git a/MiraAPI/GameModes/AbstractGameMode.cs b/MiraAPI/GameModes/AbstractGameMode.cs index 0ea8534..8344839 100644 --- a/MiraAPI/GameModes/AbstractGameMode.cs +++ b/MiraAPI/GameModes/AbstractGameMode.cs @@ -298,8 +298,8 @@ public abstract class AbstractGameMode : IOptionable instance.closedPosition = new Vector3(xPos, yPos, instance.closedPosition.z); instance.openPosition = new Vector3(instance.openPosition.x, yPos, instance.openPosition.z); instance.timer = instance.open - ? Mathf.Min(1f, instance.timer + Time.deltaTime / instance.animationTimeSeconds) - : Mathf.Max(0f, instance.timer - Time.deltaTime / instance.animationTimeSeconds); + ? Mathf.Min(1f, instance.timer + (Time.deltaTime / instance.animationTimeSeconds)) + : Mathf.Max(0f, instance.timer - (Time.deltaTime / instance.animationTimeSeconds)); Vector3 relativePos = new( Mathf.SmoothStep(instance.closedPosition.x, instance.openPosition.x, instance.timer), diff --git a/MiraAPI/GameModes/CustomGameModeManager.cs b/MiraAPI/GameModes/CustomGameModeManager.cs index b5e8177..f43fc0d 100644 --- a/MiraAPI/GameModes/CustomGameModeManager.cs +++ b/MiraAPI/GameModes/CustomGameModeManager.cs @@ -74,7 +74,8 @@ public static class CustomGameModeManager /// /// The AbstractGameMode subclass being checked. /// Whether the provided mode is the current active one. - public static bool IsActiveGameMode() where T : AbstractGameMode + public static bool IsActiveGameMode() + where T : AbstractGameMode { return ActiveMode is T; } @@ -108,6 +109,7 @@ public static class CustomGameModeManager { var defaultMode = new ClassicMode(); IdToModeMap.Add(0, defaultMode); + // no need to add to game mode option as it already contains it // because we cannot have the option be created with no values defaultMode.ID = 0; diff --git a/MiraAPI/GameModes/GameModeOption.cs b/MiraAPI/GameModes/GameModeOption.cs index 494f0a1..34ec457 100644 --- a/MiraAPI/GameModes/GameModeOption.cs +++ b/MiraAPI/GameModes/GameModeOption.cs @@ -19,7 +19,7 @@ namespace MiraAPI.GameModes; public static class GameModeOption { /// - /// Gets the current index of the Game Mode Option + /// Gets the current index of the Game Mode Option.
/// For the value as an , see . ///
public static int Value @@ -38,11 +38,6 @@ public static class GameModeOption LastValue = value; } } - internal static StringOption OptionBehaviour { get; set; } = null!; - - [SuppressMessage("Critical Code Smell", "S2223:Non-constant static fields should not be visible", Justification = "Internal behaviour that does not need property-level validation.")] - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Read above.")] // why so many warnings??? - internal static int LastValue; internal static readonly StringNames GamemodeName = MiraLocaleManager.GetOrCreateLocaleString("Gamemode"); internal static readonly StringNames CustomName = MiraLocaleManager.GetOrCreateLocaleString("Custom"); @@ -51,6 +46,12 @@ public static class GameModeOption [0] = MiraLocaleManager.GetOrCreateLocaleString("MiraApi.Gamemode.Classic"), }; + internal static StringOption OptionBehaviour { get; set; } = null!; + + [SuppressMessage("Critical Code Smell", "S2223:Non-constant static fields should not be visible", Justification = "Internal behaviour that does not need property-level validation.")] + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Read above.")] // why so many warnings??? + internal static int LastValue; + internal static void AddOption(AbstractGameMode mode) { if (!Values.ContainsKey(mode.ID)) @@ -110,6 +111,7 @@ public static class GameModeOption CustomGameModeManager.GetAndSetGameMode(); HudPatches.SetGameModeText(CustomGameModeManager.GetMode(Values.ElementAt(LastValue).Key).ColoredName); var gm = CustomGameModeManager.ActiveMode!; + if (gm != previousMode) { ModdedOptionsManager.AddSettingsChangeMessage( @@ -127,6 +129,7 @@ public static class GameModeOption } } } + // TODO: could make Values a dict of AbstractGameMode too } @@ -145,6 +148,7 @@ public static class GameModeOption GameOptionsMenuPatch.ToggleGamemodeOptions(CustomGameModeManager.ActiveMode, __instance); GameSettingMenu.Instance.RoleSettingsButton.gameObject.SetActive(CustomGameModeManager.ActiveMode.ShowNormalRoleSettings); } + return false; } diff --git a/MiraAPI/GameModes/HideAndSeekMode.cs b/MiraAPI/GameModes/HideAndSeekMode.cs index 492c3e7..316c3cf 100644 --- a/MiraAPI/GameModes/HideAndSeekMode.cs +++ b/MiraAPI/GameModes/HideAndSeekMode.cs @@ -104,6 +104,7 @@ public class HideAndSeekMode : AbstractGameMode list2.Add(networkedPlayerInfo); } } + var currentGameOptions = GameOptionsManager.Instance.CurrentGameOptions; var adjustedNumImpostors = GameOptionsManager.Instance.CurrentGameOptions.GetAdjustedNumImpostors(list2.Count); AssignRolesForTeam(list2, currentGameOptions, RoleTeamTypes.Impostor, Math.Max(adjustedNumImpostors, 1), RoleTypes.Impostor); @@ -204,19 +205,19 @@ public class HideAndSeekMode : AbstractGameMode Info($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Before Guaranteed Assignment"); var newImpostors = new List(); - // Specified Seeker if (HasImpostorPlayerID() && ValidateImpostorPlayerID(players) && !AmongUsClient.Instance.IsGamePublic) { + // Specified Seeker var networkedPlayerInfo = players.First(p => p.PlayerId == ImpostorPlayerID()); players.Remove(networkedPlayerInfo); newImpostors.Add(networkedPlayerInfo); Info($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}"); } - // Random Seeker else { + // Random Seeker var num2 = 0; while (num2 < teamMax && players.Count > 0) { @@ -226,6 +227,7 @@ public class HideAndSeekMode : AbstractGameMode { pseudoRandomList.PickRandom(); } + var networkedPlayerInfo = pseudoRandomList.PickRandom(); players.Remove(networkedPlayerInfo); newImpostors.Add(networkedPlayerInfo); @@ -233,6 +235,7 @@ public class HideAndSeekMode : AbstractGameMode Info($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}"); } } + Info($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Guaranteed Assignment"); AddGuaranteedRoles(guaranteedRoles, opts, list); @@ -301,6 +304,7 @@ public class HideAndSeekMode : AbstractGameMode { introCutscene.ImpostorTitle.text = impostor.Data.Role.GetRoleName(); } + PoolablePlayer? playerSlot = null; if (impostor != null) { @@ -397,6 +401,7 @@ public class HideAndSeekMode : AbstractGameMode impostor.cosmetics.SetBodyCosmeticsVisible(false); } } + ShipStatus.Instance.StartSFX(); HnsMusicHandler.Instance.OnGameStart(); HnsDangerMeter.Instance.OnGameStart(); @@ -429,6 +434,7 @@ public class HideAndSeekMode : AbstractGameMode mapOptions.IncludeDeadBodies = false; mapOptions.ShowLivePlayerPosition = false; } + return mapOptions; } @@ -441,14 +447,17 @@ public class HideAndSeekMode : AbstractGameMode { instance.Manager.RpcEndGame(GameOverReason.ImpostorDisconnect, !DataManager.Player.Ads.HasPurchasedAdRemoval); } + if (players.Any(x => !x.Data.Role.IsImpostor)) { if (HideAndSeekHudHelper.Instance.AllTimersExpired()) { instance.Manager.RpcEndGame(GameOverReason.HideAndSeek_CrewmatesByTimer, !DataManager.Player.Ads.HasPurchasedAdRemoval); } + return; } + instance.Manager.RpcEndGame(GameOverReason.HideAndSeek_ImpostorsByKills, !DataManager.Player.Ads.HasPurchasedAdRemoval); } @@ -462,10 +471,12 @@ public class HideAndSeekMode : AbstractGameMode { return PlayerBodyTypes.Horse; } + if (AprilFoolsMode.ShouldLongAround()) { return PlayerBodyTypes.Long; } + return PlayerBodyTypes.Normal; } @@ -507,8 +518,8 @@ public class HideAndSeekMode : AbstractGameMode instance.closedPosition = new Vector3(xPos, yPos, instance.closedPosition.z); instance.openPosition = new Vector3(instance.openPosition.x, yPos, instance.openPosition.z); instance.timer = instance.open - ? Mathf.Min(1f, instance.timer + Time.deltaTime / instance.animationTimeSeconds) - : Mathf.Max(0f, instance.timer - Time.deltaTime / instance.animationTimeSeconds); + ? Mathf.Min(1f, instance.timer + (Time.deltaTime / instance.animationTimeSeconds)) + : Mathf.Max(0f, instance.timer - (Time.deltaTime / instance.animationTimeSeconds)); Vector3 relativePos = new( Mathf.SmoothStep(instance.closedPosition.x, instance.openPosition.x, instance.timer), diff --git a/MiraAPI/GameOptions/AbstractOptionGroup.cs b/MiraAPI/GameOptions/AbstractOptionGroup.cs index 0045c52..c2977c2 100644 --- a/MiraAPI/GameOptions/AbstractOptionGroup.cs +++ b/MiraAPI/GameOptions/AbstractOptionGroup.cs @@ -35,7 +35,7 @@ public abstract class AbstractOptionGroup /// Gets a value indicating whether the group should be shown in the modifiers menu. ///
// Completed: make this not a boolean - [Obsolete("Use ParentMenu instead.")] + [Obsolete("Use ParentMenu instead.", true)] [SuppressMessage("Info Code Smell", "S1133:Deprecated code should be removed", Justification = "Retained.")] public virtual bool ShowInModifiersMenu => false; @@ -76,7 +76,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 where T : IOptionable +public abstract class AbstractOptionGroup : AbstractOptionGroup + where T : IOptionable { /// public override Type OptionableType => typeof(T); @@ -95,6 +96,7 @@ public abstract class AbstractOptionGroup : AbstractOptionGroup where T : IOp { return MenuCategory.Roles; } + return MenuCategory.Game; } } @@ -104,7 +106,8 @@ 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 abstract class AbstractRoleOptionGroup : AbstractOptionGroup + where T : ICustomRole { /// public override Type OptionableType => typeof(T); diff --git a/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs index 8b31da6..cf517ba 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs @@ -43,7 +43,8 @@ public class ModdedEnumOptionAttribute(string title, Type enumType, string[]? va /// The enum type. [AttributeUsage(AttributeTargets.Property)] public class ModdedEnumOptionAttribute(string title, string[]? values = null) - : ModdedOptionAttribute(title) where T : Enum + : ModdedOptionAttribute(title) + where T : Enum { internal override IModdedOption CreateOption(object? value, PropertyInfo property) { diff --git a/MiraAPI/GameOptions/Attributes/ModdedOptionVisibilityAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedOptionVisibilityAttribute.cs index a1efcaa..1d50f4b 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedOptionVisibilityAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedOptionVisibilityAttribute.cs @@ -1,8 +1,8 @@ -using MiraAPI.GameOptions.OptionTypes; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using MiraAPI.GameOptions.OptionTypes; namespace MiraAPI.GameOptions.Attributes; @@ -52,6 +52,7 @@ public sealed class ModdedOptionVisibilityAttribute(Type? holderType = null, str return () => (bool)vProperty.GetValue(instanceGet?.Invoke())!; } + if (member is MethodInfo vMethod) { if (vMethod.ReturnType != typeof(bool)) @@ -114,6 +115,7 @@ public sealed class ModdedOptionVisibilityAttribute(Type? holderType = null, str { return _ => (bool)vProperty.GetValue(instanceGet?.Invoke())!; } + if (indexParams[0].ParameterType != typeof(int)) { Error($"Indexer {memberName}'s parameter is not an int."); @@ -122,6 +124,7 @@ public sealed class ModdedOptionVisibilityAttribute(Type? holderType = null, str return i => (bool)vProperty.GetValue(instanceGet?.Invoke(), [i])!; } + if (member is MethodInfo vMethod) { if (vMethod.ReturnType != typeof(bool)) @@ -141,6 +144,7 @@ public sealed class ModdedOptionVisibilityAttribute(Type? holderType = null, str { return _ => (bool)vMethod.Invoke(instanceGet?.Invoke(), null)!; } + if (paramList[0].ParameterType != typeof(int)) { Error($"Method {memberName}'s parameter is not an int."); @@ -179,6 +183,7 @@ public sealed class ModdedOptionVisibilityAttribute(Type? holderType = null, str instanceGet = () => instanceProp.GetValue(null)!; } } + MemberInfo? member = type.GetMember(memberName, flags).FirstOrDefault(); if (instanceGet != null) { @@ -189,6 +194,7 @@ public sealed class ModdedOptionVisibilityAttribute(Type? holderType = null, str instanceGet = null; } } + return member; } #pragma warning restore S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields diff --git a/MiraAPI/GameOptions/Attributes/OptionList/ModdedEnumOptionListAttribute.cs b/MiraAPI/GameOptions/Attributes/OptionList/ModdedEnumOptionListAttribute.cs index dde2cfb..2104860 100644 --- a/MiraAPI/GameOptions/Attributes/OptionList/ModdedEnumOptionListAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/OptionList/ModdedEnumOptionListAttribute.cs @@ -1,7 +1,7 @@ -using MiraAPI.GameOptions.OptionTypes; -using System; +using System; using System.Collections; using System.Reflection; +using MiraAPI.GameOptions.OptionTypes; namespace MiraAPI.GameOptions.Attributes; @@ -41,7 +41,8 @@ public class ModdedEnumOptionListAttribute(string title, Type enumType, string[] /// The enum type. [AttributeUsage(AttributeTargets.Property)] public class ModdedEnumOptionListAttribute(string title, string[]? values = null) - : ModdedOptionListAttribute(title) where T : Enum + : ModdedOptionListAttribute(title) + where T : Enum { internal override IModdedOptionList CreateOptionList(IList value, PropertyInfo property) { diff --git a/MiraAPI/GameOptions/Attributes/OptionList/ModdedNumberOptionListAttribute.cs b/MiraAPI/GameOptions/Attributes/OptionList/ModdedNumberOptionListAttribute.cs index a793a37..80fdeda 100644 --- a/MiraAPI/GameOptions/Attributes/OptionList/ModdedNumberOptionListAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/OptionList/ModdedNumberOptionListAttribute.cs @@ -1,8 +1,8 @@ -using MiraAPI.GameOptions.OptionTypes; -using MiraAPI.Utilities; -using System; +using System; using System.Collections; using System.Reflection; +using MiraAPI.GameOptions.OptionTypes; +using MiraAPI.Utilities; namespace MiraAPI.GameOptions.Attributes; diff --git a/MiraAPI/GameOptions/Attributes/OptionList/ModdedOptionListAttribute.cs b/MiraAPI/GameOptions/Attributes/OptionList/ModdedOptionListAttribute.cs index 14cd78b..d0ee68e 100644 --- a/MiraAPI/GameOptions/Attributes/OptionList/ModdedOptionListAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/OptionList/ModdedOptionListAttribute.cs @@ -85,8 +85,10 @@ public abstract class ModdedOptionListAttribute(string title) : PropertyOptionAt { return string.Format(Title, index); } + Error("ModdedOptionList Title cannot contain more than the first parameter for formatting."); } + return Title + index; } } diff --git a/MiraAPI/GameOptions/Attributes/OptionList/ModdedToggleOptionListAttribute.cs b/MiraAPI/GameOptions/Attributes/OptionList/ModdedToggleOptionListAttribute.cs index 88faef6..0bcb0a6 100644 --- a/MiraAPI/GameOptions/Attributes/OptionList/ModdedToggleOptionListAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/OptionList/ModdedToggleOptionListAttribute.cs @@ -1,7 +1,7 @@ -using MiraAPI.GameOptions.OptionTypes; -using System; +using System; using System.Collections; using System.Reflection; +using MiraAPI.GameOptions.OptionTypes; namespace MiraAPI.GameOptions.Attributes; diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index 58e892c..f74c84f 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -48,24 +48,14 @@ public static class ModdedOptionsManager public static void AddSettingsChangeMessage(NotificationPopper notif, StringNames key, string value, Color textColor, TMP_SpriteAsset? sprite, bool playSound = true) { var text = textColor.ToTextColor(); - var item = sprite != null - ? TranslationController.Instance.GetString( - StringNames.LobbyChangeSettingNotification, - string.Concat( - "", - text, - TranslationController.Instance.GetString(key), - ""), - "" + value + "" - ) - : TranslationController.Instance.GetString( - StringNames.LobbyChangeSettingNotification, - "" + - text + - TranslationController.Instance.GetString(key) + "", - "" + value + ""); + var translatedKey = TranslationController.Instance.GetString(key); + var fontTag = ""; + var spritePrefix = sprite != null ? $"" : string.Empty; + + var item = TranslationController.Instance.GetString( + StringNames.LobbyChangeSettingNotification, + $"{spritePrefix}{fontTag}{text}{translatedKey}", + $"{fontTag}{value}"); notif.SettingsChangeMessageLogic(key, item, playSound); } @@ -110,6 +100,7 @@ public static class ModdedOptionsManager GameModeOptionGroups.Add(group.OptionableType, [group]); } } + pluginInfo.InternalOptionGroups.Add(group); RegisterGroupInternal(group, type); @@ -119,8 +110,7 @@ public static class ModdedOptionsManager [SuppressMessage( "Major Code Smell", "S3011:Reflection should not be used to increase accessibility of classes, methods, or fields", - Justification = "Dynamic singleton initialization requires reflection to bypass the private field access modifier because the type is only known at runtime." - )] + Justification = "Dynamic singleton initialization requires reflection to bypass the private field access modifier because the type is only known at runtime.")] private static void RegisterGroupInternal(AbstractOptionGroup group, Type type) { Groups.Add(group); @@ -248,6 +238,7 @@ public static class ModdedOptionsManager Error($"Failed to get option for {property.Name}"); return; } + if (propertyList.Count != optionList.Count) { Error("Mismatch in count between values and created options."); @@ -285,6 +276,7 @@ public static class ModdedOptionsManager { option.Visible = () => visibilityFunc(i); } + RegisterOption(option, group, property.Name + i, pluginInfo); } } diff --git a/MiraAPI/GameOptions/OptionGroupSingleton.cs b/MiraAPI/GameOptions/OptionGroupSingleton.cs index 919a286..1957b0b 100644 --- a/MiraAPI/GameOptions/OptionGroupSingleton.cs +++ b/MiraAPI/GameOptions/OptionGroupSingleton.cs @@ -7,7 +7,8 @@ namespace MiraAPI.GameOptions; /// Singleton for s. /// /// The option group type. -public static class OptionGroupSingleton where T : AbstractOptionGroup +public static class OptionGroupSingleton + where T : AbstractOptionGroup { private static T? _instance; diff --git a/MiraAPI/GameOptions/OptionNotifConfiguration.cs b/MiraAPI/GameOptions/OptionNotifConfiguration.cs index 0ee9b0c..aa02128 100644 --- a/MiraAPI/GameOptions/OptionNotifConfiguration.cs +++ b/MiraAPI/GameOptions/OptionNotifConfiguration.cs @@ -11,7 +11,7 @@ namespace MiraAPI.GameOptions; /// public record struct OptionNotifConfiguration { - [Obsolete("Default constructor is not supported. Please use the constructor that takes an AbstractOptionGroup parameter.")] + [Obsolete("Default constructor is not supported. Please use the constructor that takes an AbstractOptionGroup parameter.", true)] [SuppressMessage("Info Code Smell", "S1133:Deprecated code should be removed", Justification = "Will not be removed, as this throws instead.")] #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member (Justification: Read above.) public OptionNotifConfiguration() diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs index c6a1ada..69815b9 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs @@ -28,7 +28,8 @@ public class ModdedEnumOption : ModdedOption /// The type. /// An option list of string values to use in place of the name. /// Whether to include this option in the preset or not. - public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null, bool includeInPreset = true) : base(title, defaultValue, includeInPreset) + public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null, bool includeInPreset = true) + : base(title, defaultValue, includeInPreset) { Values = values ?? Enum.GetNames(enumType); @@ -111,7 +112,8 @@ public class ModdedEnumOption : ModdedOption /// An option for selecting an value. /// /// The type. -public class ModdedEnumOption : ModdedOption where T : Enum +public class ModdedEnumOption : ModdedOption + where T : Enum { /// /// Gets the string values of the . @@ -125,7 +127,8 @@ public class ModdedEnumOption : ModdedOption where T : Enum /// The default value as an . /// An option list of string values to use in place of the name. /// Whether to include this option in the preset or not. - public ModdedEnumOption(string title, T defaultValue, string[]? values = null, bool includeInPreset = true) : base(title, defaultValue, includeInPreset) + public ModdedEnumOption(string title, T defaultValue, string[]? values = null, bool includeInPreset = true) + : base(title, defaultValue, includeInPreset) { Values = values ?? Enum.GetNames(typeof(T)); diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs index d22b158..e9097ee 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs @@ -77,7 +77,8 @@ public class ModdedNumberOption : ModdedOption MiraNumberSuffixes suffixType, string? formatString = null, bool halfIncrements = false, - bool includeInPreset = true) : base(title, defaultValue, includeInPreset) + bool includeInPreset = true) + : base(title, defaultValue, includeInPreset) { Min = min; Max = max; @@ -130,7 +131,8 @@ public class ModdedNumberOption : ModdedOption MiraNumberSuffixes suffixType, string? formatString = null, bool zeroInfinity = false, - bool includeInPreset = true) : base(title, defaultValue, includeInPreset) + bool includeInPreset = true) + : base(title, defaultValue, includeInPreset) { Min = min; Max = max; diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index 1ff9e26..24747ad 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -125,6 +125,7 @@ public abstract class ModdedOption : IModdedOption Error($"Attempted to save {Title} to preset, but ConfigDefinition is null."); return; } + Bind(presetConfig); presetConfig[ConfigDefinition].BoxedValue = saveDefault ? DefaultValue : Value; } diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOptionList.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOptionList.cs index b4ae9ff..6f9e8d5 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOptionList.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOptionList.cs @@ -9,7 +9,8 @@ namespace MiraAPI.GameOptions.OptionTypes; /// Represents a modded option list. /// /// The type of options. -public class ModdedOptionList : IModdedOptionList where T : IModdedOption +public class ModdedOptionList : IModdedOptionList + where T : IModdedOption { /// public int Count { get; } diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs index d7094b7..32c736e 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs @@ -34,7 +34,8 @@ public class ModdedPlayerOption : ModdedOption /// /// The title of the option. /// Whether the option includes a none option. If None is selected, value will return -1. - public ModdedPlayerOption(string title, bool allowNone = true) : base(title, allowNone ? -1 : 0, false) + public ModdedPlayerOption(string title, bool allowNone = true) + : base(title, allowNone ? -1 : 0, false) { Data = ScriptableObject.CreateInstance(); var data = (PlayerSelectionGameSetting)Data; diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs index 3a96ca0..8381177 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedStringOption.cs @@ -36,6 +36,7 @@ public class ModdedStringOption : ModdedOption { return Array.IndexOf(Values, value); } + Info($"Cannot find a valid index for {value}. Returning -1 instead. Values: {string.Join(", ", Values)}"); return -1; } @@ -79,12 +80,14 @@ public class ModdedStringOption : ModdedOption { Error("StringOption is null."); } + Warning("OptionBehaviour is valid"); } else { Error("OptionBehaviour is null."); } + Warning("Data is valid"); } else @@ -100,7 +103,8 @@ public class ModdedStringOption : ModdedOption /// The default value as a string. /// An option list of string values to use. /// Whether to include this option in the preset or not. - public ModdedStringOption(string title, string defaultValue, string[] values, bool includeInPreset = true) : base(title, defaultValue, includeInPreset) + public ModdedStringOption(string title, string defaultValue, string[] values, bool includeInPreset = true) + : base(title, defaultValue, includeInPreset) { Values = values; Data = ScriptableObject.CreateInstance(); diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs index 8b15a31..6fa8be3 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs @@ -17,7 +17,8 @@ public class ModdedToggleOption : ModdedOption /// The option title. /// The default value. /// Whether to include this option in the preset or not. - public ModdedToggleOption(string title, bool defaultValue, bool includeInPreset = true) : base(title, defaultValue, includeInPreset) + public ModdedToggleOption(string title, bool defaultValue, bool includeInPreset = true) + : base(title, defaultValue, includeInPreset) { Data = ScriptableObject.CreateInstance(); diff --git a/MiraAPI/GlobalUsings.cs b/MiraAPI/GlobalUsings.cs index da49803..6a7d395 100644 --- a/MiraAPI/GlobalUsings.cs +++ b/MiraAPI/GlobalUsings.cs @@ -1,2 +1,3 @@ +global using static Reactor.Utilities.Logger; global using CppCollections = Il2CppSystem.Collections.Generic; global using CppIEnumerator = Il2CppSystem.Collections.IEnumerator; diff --git a/MiraAPI/HnsReimplemented/HideAndSeekHudHelper.cs b/MiraAPI/HnsReimplemented/HideAndSeekHudHelper.cs index 814e6ba..2f7d67a 100644 --- a/MiraAPI/HnsReimplemented/HideAndSeekHudHelper.cs +++ b/MiraAPI/HnsReimplemented/HideAndSeekHudHelper.cs @@ -26,8 +26,9 @@ public sealed class HideAndSeekHudHelper(nint cppPtr) : MonoBehaviour(cppPtr) private AudioClip finalHideAlertSfx; private AudioClip finalHideCountdownSfx; - [SuppressMessage("Style", "IDE0052:Remove unread private members", Justification = "Ignore for now; tentative code.")] + // ReSharper disable once NotAccessedField.Local (Justification: Read above.) + [SuppressMessage("Style", "IDE0052:Remove unread private members", Justification = "Ignore for now; tentative code.")] private AudioClip taskFinishedSound; // private const int SECONDS_TO_BEEP = 10; @@ -166,11 +167,13 @@ public sealed class HideAndSeekHudHelper(nint cppPtr) : MonoBehaviour(cppPtr) HudManager.Instance.TaskPanel.SetTaskText(string.Empty); return; } + var data = PlayerControl.LocalPlayer.Data; if (data == null) { return; } + var flag = data.Role != null && data.Role.IsImpostor; HudManager.Instance.tasksString.Clear(); if (PlayerControl.LocalPlayer.myTasks == null || PlayerControl.LocalPlayer.myTasks.Count == 0) @@ -190,20 +193,25 @@ public sealed class HideAndSeekHudHelper(nint cppPtr) : MonoBehaviour(cppPtr) playerTask.AppendTaskText(HudManager.Instance.tasksString); break; } + playerTask.AppendTaskText(HudManager.Instance.tasksString); } } + if (data.Role != null) { data.Role.AppendTaskHint(HudManager.Instance.tasksString); } + if (HideCountdown > 0f) { HideCountdown -= num; HudManager.Instance.tasksString.Append("\n\n" + ((int)HideCountdown)); } + HudManager.Instance.tasksString.TrimEnd(); } + HudManager.Instance.TaskPanel.SetTaskText(HudManager.Instance.tasksString.ToString()); } diff --git a/MiraAPI/HnsReimplemented/HnsMusicHandler.cs b/MiraAPI/HnsReimplemented/HnsMusicHandler.cs index 7d94884..76c4a25 100644 --- a/MiraAPI/HnsReimplemented/HnsMusicHandler.cs +++ b/MiraAPI/HnsReimplemented/HnsMusicHandler.cs @@ -21,6 +21,26 @@ public sealed class HnsMusicHandler(nint cppPtr) : MonoBehaviour(cppPtr) /// public static HnsMusicHandler Instance { get; private set; } + private static readonly Dictionary MusicNames = new() + { + { + LogicHnSMusic.HideAndSeekMusicTrack.Normal, + "HnS_Music_Normal" + }, + { + LogicHnSMusic.HideAndSeekMusicTrack.Task, + "HnS_Music_Task" + }, + { + LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel1, + "HnS_Music_DangerLevel1" + }, + { + LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel2, + "HnS_Music_DangerLevel2" + }, + }; + private HideAndSeekMusicCollection musicCollection; private float lastMusicSyncTime; @@ -45,26 +65,6 @@ public sealed class HnsMusicHandler(nint cppPtr) : MonoBehaviour(cppPtr) private float musicLerpSpeed = 5f; - private readonly Dictionary musicNames = new() - { - { - LogicHnSMusic.HideAndSeekMusicTrack.Normal, - "HnS_Music_Normal" - }, - { - LogicHnSMusic.HideAndSeekMusicTrack.Task, - "HnS_Music_Task" - }, - { - LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel1, - "HnS_Music_DangerLevel1" - }, - { - LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel2, - "HnS_Music_DangerLevel2" - }, - }; - private void Awake() { Instance = this; @@ -94,7 +94,7 @@ public sealed class HnsMusicHandler(nint cppPtr) : MonoBehaviour(cppPtr) if (normalSource == null) { normalSource = - SoundManager.Instance.GetNamedSfxSource(musicNames[LogicHnSMusic.HideAndSeekMusicTrack.Normal]); + SoundManager.Instance.GetNamedSfxSource(MusicNames[LogicHnSMusic.HideAndSeekMusicTrack.Normal]); } normalSource.outputAudioMixerGroup = SoundManager.Instance.MusicChannel; @@ -103,7 +103,7 @@ public sealed class HnsMusicHandler(nint cppPtr) : MonoBehaviour(cppPtr) if (taskSource == null) { taskSource = - SoundManager.Instance.GetNamedSfxSource(musicNames[LogicHnSMusic.HideAndSeekMusicTrack.Task]); + SoundManager.Instance.GetNamedSfxSource(MusicNames[LogicHnSMusic.HideAndSeekMusicTrack.Task]); } taskSource.outputAudioMixerGroup = SoundManager.Instance.MusicChannel; @@ -114,7 +114,7 @@ public sealed class HnsMusicHandler(nint cppPtr) : MonoBehaviour(cppPtr) { dangerLevel1Source = SoundManager.Instance.GetNamedSfxSource( - musicNames[LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel1]); + MusicNames[LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel1]); } dangerLevel1Source.outputAudioMixerGroup = SoundManager.Instance.MusicChannel; @@ -125,7 +125,7 @@ public sealed class HnsMusicHandler(nint cppPtr) : MonoBehaviour(cppPtr) { dangerLevel2Source = SoundManager.Instance.GetNamedSfxSource( - musicNames[LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel2]); + MusicNames[LogicHnSMusic.HideAndSeekMusicTrack.DangerLevel2]); } dangerLevel2Source.outputAudioMixerGroup = SoundManager.Instance.MusicChannel; diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs index 24e2397..abc762e 100644 --- a/MiraAPI/Hud/CustomActionButton.cs +++ b/MiraAPI/Hud/CustomActionButton.cs @@ -209,6 +209,7 @@ public abstract class CustomActionButton Keybind.OnActivate(() => { if (!Enabled(PlayerControl.LocalPlayer.Data.Role)) return; + // Invoke the generic button click event. var genericEvent = new MiraButtonClickEvent(this); MiraEventManager.InvokeEvent(genericEvent); @@ -248,8 +249,7 @@ public abstract class CustomActionButton Helpers.CreateKeybindIcon( Button.gameObject, Keybind.CurrentKey, - new Vector3(0.4f, 0.45f, -9f) - ); + new Vector3(0.4f, 0.45f, -9f)); KeybindText = KeybindIcon.transform.GetChild(0).GetComponent(); HudManagerPatches.ModdedKeybindIcons.Add(KeybindText); Button.usesRemainingSprite.transform.localPosition = new(-0.341f, 0.45f, -0.1f); @@ -584,7 +584,8 @@ public abstract class CustomActionButton /// Custom action button that has a target . /// /// The type of the target object. -public abstract class CustomActionButton : CustomActionButton where T : MonoBehaviour +public abstract class CustomActionButton : CustomActionButton + where T : MonoBehaviour { /// /// Gets or sets the target of the button. diff --git a/MiraAPI/Hud/CustomButtonManager.cs b/MiraAPI/Hud/CustomButtonManager.cs index 8f8f2e9..a532f8e 100644 --- a/MiraAPI/Hud/CustomButtonManager.cs +++ b/MiraAPI/Hud/CustomButtonManager.cs @@ -35,8 +35,7 @@ public static class CustomButtonManager [SuppressMessage( "Major Code Smell", "S3011:Reflection should not be used to increase accessibility of classes, methods, or fields", - Justification = "Dynamic singleton initialization requires reflection to bypass the private field access modifier because the type is only known at runtime." - )] + Justification = "Dynamic singleton initialization requires reflection to bypass the private field access modifier because the type is only known at runtime.")] internal static bool RegisterButton(Type buttonType, MiraPluginInfo pluginInfo) { if (!buttonType.IsAssignableTo(typeof(CustomActionButton)) || Activator.CreateInstance(buttonType) is not CustomActionButton button) diff --git a/MiraAPI/Hud/CustomButtonSingleton.cs b/MiraAPI/Hud/CustomButtonSingleton.cs index 957445b..d3861da 100644 --- a/MiraAPI/Hud/CustomButtonSingleton.cs +++ b/MiraAPI/Hud/CustomButtonSingleton.cs @@ -7,7 +7,8 @@ namespace MiraAPI.Hud; /// A utility class to get the instance of a . /// /// The type of the button you are trying to access. -public static class CustomButtonSingleton where T : CustomActionButton +public static class CustomButtonSingleton + where T : CustomActionButton { private static T? _instance; diff --git a/MiraAPI/Hud/CustomPlayerMenu.cs b/MiraAPI/Hud/CustomPlayerMenu.cs index 8841144..8fc248d 100644 --- a/MiraAPI/Hud/CustomPlayerMenu.cs +++ b/MiraAPI/Hud/CustomPlayerMenu.cs @@ -112,6 +112,7 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr) potentialVictims.Add(shapeshifterPanel); list2.Add(shapeshifterPanel.Button); } + ControllerManager.Instance.OpenOverlayMenu(name, backButton, defaultButtonSelected, list2); } } diff --git a/MiraAPI/Hud/MiraHudHelper.cs b/MiraAPI/Hud/MiraHudHelper.cs index bf71ce6..26f21a4 100644 --- a/MiraAPI/Hud/MiraHudHelper.cs +++ b/MiraAPI/Hud/MiraHudHelper.cs @@ -68,6 +68,7 @@ internal sealed class MiraHudHelper(nint cppPtr) : MonoBehaviour(cppPtr) { return; } + UpdateSubmergedButtons(instance); } @@ -122,6 +123,7 @@ internal sealed class MiraHudHelper(nint cppPtr) : MonoBehaviour(cppPtr) listButton.GetComponent().Destroy(); listButton.localPosition = new Vector3(0, 0, 0); } + settingsButton.transform.SetAsLastSibling(); chatButton.transform.SetParent(UiTopRight.transform, false); instance.Chat.chatButton = chatButton.GetComponent(); @@ -182,6 +184,7 @@ internal sealed class MiraHudHelper(nint cppPtr) : MonoBehaviour(cppPtr) oldPos = ModifierDisplayObject.transform.GetChild(1).localPosition; ModifierDisplayObject.transform.GetChild(1).localPosition = new Vector3(-0.45f, 0.3f, oldPos.z); } + MiraApiSettings.SetUpButtonPositions(); } diff --git a/MiraAPI/Hud/MultiTargetButton.cs b/MiraAPI/Hud/MultiTargetButton.cs index 6e4c4b8..9d44b4d 100644 --- a/MiraAPI/Hud/MultiTargetButton.cs +++ b/MiraAPI/Hud/MultiTargetButton.cs @@ -8,7 +8,8 @@ namespace MiraAPI.Hud; /// that has multiple target objects. /// /// The type of the target objects. -public abstract class MultiTargetButton : CustomActionButton where T : MonoBehaviour +public abstract class MultiTargetButton : CustomActionButton + where T : MonoBehaviour { /// /// Gets or sets the list of targets of the button. diff --git a/MiraAPI/Keybinds/KeybindUtils.cs b/MiraAPI/Keybinds/KeybindUtils.cs index e14bf77..3b8502b 100644 --- a/MiraAPI/Keybinds/KeybindUtils.cs +++ b/MiraAPI/Keybinds/KeybindUtils.cs @@ -70,6 +70,7 @@ public static class KeybindUtils var used = KeybindManager.Keybinds.Exists(e => e.DefaultKey == key); if (!used) return key; } + return KeyboardKeyCode.None; } } diff --git a/MiraAPI/Keybinds/VanillaKeybindSingleton.cs b/MiraAPI/Keybinds/VanillaKeybindSingleton.cs index 5605d4d..0754756 100644 --- a/MiraAPI/Keybinds/VanillaKeybindSingleton.cs +++ b/MiraAPI/Keybinds/VanillaKeybindSingleton.cs @@ -6,7 +6,8 @@ namespace MiraAPI.Keybinds; /// Singleton for getting a by . /// /// type. -public static class VanillaKeybinding where T : ActionButton +public static class VanillaKeybinding + where T : ActionButton { [SuppressMessage("Major Code Smell", "S2743:Static fields should not be used in generic types", Justification = "The instance holds a reference to the button that defines the class's type parameter.")] private static VanillaKeybind? _instance; diff --git a/MiraAPI/LocalSettings/Attributes/LocalEnumSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalEnumSettingAttribute.cs index 07114e5..6932f4f 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalEnumSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalEnumSettingAttribute.cs @@ -14,8 +14,8 @@ namespace MiraAPI.LocalSettings.Attributes; public class LocalEnumSettingAttribute( string? name = null, string? description = null, - string[]? names = null - ) : LocalSettingAttribute(name, description) + string[]? names = null) + : LocalSettingAttribute(name, description) { private readonly string? _name = name; private readonly string? _description = description; diff --git a/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs index e1b5047..56c0f8c 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs @@ -24,8 +24,8 @@ public class LocalNumberSettingAttribute( float max = 5, float increment = 1, MiraNumberSuffixes suffixType = MiraNumberSuffixes.None, - string? formatString = null - ) : LocalSettingAttribute(name, description) + string? formatString = null) + : LocalSettingAttribute(name, description) { private readonly string? _name = name; private readonly string? _description = description; diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs index 2d6b943..62e49cc 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs @@ -12,9 +12,9 @@ namespace MiraAPI.LocalSettings.Attributes; public abstract class LocalSettingAttribute( #pragma warning disable CS9113 // Parameter is unread (Justification: No idea why they exist, but they probably do for a reason unknown to me.) string? name = null, - string? description = null + string? description = null) #pragma warning restore CS9113 // Parameter is unread - ) : Attribute + : Attribute { /// /// Returns the created object. diff --git a/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs index 826df07..d63334d 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs @@ -26,8 +26,8 @@ public class LocalSliderSettingAttribute( bool displayValue = false, string? formatString = null, bool roundValue = false, - MiraNumberSuffixes suffixType = MiraNumberSuffixes.None - ) : LocalSettingAttribute(name, description) + MiraNumberSuffixes suffixType = MiraNumberSuffixes.None) + : LocalSettingAttribute(name, description) { private readonly string? _name = name; private readonly string? _description = description; diff --git a/MiraAPI/LocalSettings/Attributes/LocalToggleSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalToggleSettingAttribute.cs index 31d35a3..8b7abe8 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalToggleSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalToggleSettingAttribute.cs @@ -12,8 +12,8 @@ namespace MiraAPI.LocalSettings.Attributes; [AttributeUsage(AttributeTargets.Property)] public class LocalToggleSettingAttribute( string? name = null, - string? description = null - ) : LocalSettingAttribute(name, description) + string? description = null) + : LocalSettingAttribute(name, description) { private readonly string? _name = name; private readonly string? _description = description; diff --git a/MiraAPI/LocalSettings/LocalSettingsButton.cs b/MiraAPI/LocalSettings/LocalSettingsButton.cs index 44df186..e35617f 100644 --- a/MiraAPI/LocalSettings/LocalSettingsButton.cs +++ b/MiraAPI/LocalSettings/LocalSettingsButton.cs @@ -73,6 +73,7 @@ public class LocalSettingsButton(string text, Action onClick) offset += 0.5f; order = 1; } + if (last) offset += 0.6f; diff --git a/MiraAPI/LocalSettings/LocalSettingsManager.cs b/MiraAPI/LocalSettings/LocalSettingsManager.cs index c1190bb..4907d8c 100644 --- a/MiraAPI/LocalSettings/LocalSettingsManager.cs +++ b/MiraAPI/LocalSettings/LocalSettingsManager.cs @@ -18,8 +18,7 @@ public static class LocalSettingsManager [SuppressMessage( "Major Code Smell", "S3011:Reflection should not be used to increase accessibility of classes, methods, or fields", - Justification = "Dynamic singleton initialization requires reflection to bypass the private field access modifier because the type is only known at runtime." - )] + Justification = "Dynamic singleton initialization requires reflection to bypass the private field access modifier because the type is only known at runtime.")] internal static bool RegisterTab(Type type, BasePlugin pluginInfo) { if (Activator.CreateInstance(type, pluginInfo.Config) is not LocalSettingsTab tab) @@ -38,6 +37,7 @@ public static class LocalSettingsManager { AvailableTabs.Add(tab); } + TypeToTab.Add(type, tab); typeof(LocalSettingsTabSingleton<>).MakeGenericType(type) diff --git a/MiraAPI/LocalSettings/LocalSettingsTab.cs b/MiraAPI/LocalSettings/LocalSettingsTab.cs index b4dab91..ebc5c03 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTab.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTab.cs @@ -93,6 +93,7 @@ public abstract class LocalSettingsTab(ConfigFile config) { setting.RefreshOption(); } + foreach (var button in Buttons) { button.RefreshButton(); diff --git a/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs b/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs index f9d964d..0b840f2 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs @@ -7,7 +7,8 @@ namespace MiraAPI.LocalSettings; /// Singleton for s. /// /// The settings tab type. -public static class LocalSettingsTabSingleton where T : LocalSettingsTab +public static class LocalSettingsTabSingleton + where T : LocalSettingsTab { private static T? _instance; diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs b/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs index ee60941..7eeeecc 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs @@ -63,6 +63,7 @@ public class LocalEnumSetting( highlight.color = Tab!.TabAppearance.EnumHoverColor; highlight.gameObject.SetActive(false); } + toggleComp.Destroy(); if (last && order == 1) @@ -100,6 +101,7 @@ public class LocalEnumSetting( { tmp.text = Description; } + highlight?.gameObject.SetActive(true); })); button.OnMouseOut.AddListener((UnityAction)(() => @@ -116,6 +118,7 @@ public class LocalEnumSetting( offset += 0.5f; order = 1; } + if (last) offset += 0.6f; diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs b/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs index 326e519..83e96dc 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs @@ -73,6 +73,7 @@ public class LocalNumberSetting( highlight.color = Tab!.TabAppearance.NumberHoverColor; highlight.gameObject.SetActive(false); } + toggleComp.Destroy(); if (last && order == 1) @@ -110,6 +111,7 @@ public class LocalNumberSetting( { tmp.text = Description; } + highlight?.gameObject.SetActive(true); })); button.OnMouseOut.AddListener((UnityAction)(() => @@ -126,6 +128,7 @@ public class LocalNumberSetting( offset += 0.5f; order = 1; } + if (last) offset += 0.6f; diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalToggleSetting.cs b/MiraAPI/LocalSettings/SettingTypes/LocalToggleSetting.cs index daed9be..af76e24 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalToggleSetting.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalToggleSetting.cs @@ -20,8 +20,8 @@ public class LocalToggleSetting( Type tab, ConfigEntryBase configEntry, string? name = null, - string? description = null -) : LocalSettingBase(tab, configEntry, name, description) + string? description = null) + : LocalSettingBase(tab, configEntry, name, description) { private ToggleButtonBehaviour _toggle; @@ -79,6 +79,7 @@ public class LocalToggleSetting( offset += 0.5f; order = 1; } + if (last) offset += 0.6f; diff --git a/MiraAPI/MeetingAbilities/MeetingAbilityBehaviour.cs b/MiraAPI/MeetingAbilities/MeetingAbilityBehaviour.cs index 7959701..1f0f7f1 100644 --- a/MiraAPI/MeetingAbilities/MeetingAbilityBehaviour.cs +++ b/MiraAPI/MeetingAbilities/MeetingAbilityBehaviour.cs @@ -69,6 +69,7 @@ public class MeetingAbilityBehaviour : MonoBehaviour Error($"Could not initialize MeetingButtonBehaviour for {Button.GetType().Name}, Destroying..."); Destroy(this); } + _init = true; Button = but; CooldownText = Instantiate(HudManager.Instance.KillButton.cooldownTimerText, transform); @@ -113,6 +114,7 @@ public class MeetingAbilityBehaviour : MonoBehaviour CooldownText.text = _button.Timer.ToString(_button.CooldownTimerFormatString, NumberFormatInfo.InvariantInfo); UsesText.text = string.Empty; } + SetFillUp(_button.Timer, _button.InitialCooldown); } diff --git a/MiraAPI/MeetingAbilities/MeetingActionButton.cs b/MiraAPI/MeetingAbilities/MeetingActionButton.cs index aa48f6d..290419b 100644 --- a/MiraAPI/MeetingAbilities/MeetingActionButton.cs +++ b/MiraAPI/MeetingAbilities/MeetingActionButton.cs @@ -336,6 +336,7 @@ public abstract class MeetingActionButton { Button!.SetDisabled(); } + Button.SetCooldownFormat(Timer, Cooldown, CooldownTimerFormatString); Button.SetCoolDown(Timer, Cooldown); } diff --git a/MiraAPI/MeetingAbilities/MeetingButtonManager.cs b/MiraAPI/MeetingAbilities/MeetingButtonManager.cs index 02af7f5..5ca75d8 100644 --- a/MiraAPI/MeetingAbilities/MeetingButtonManager.cs +++ b/MiraAPI/MeetingAbilities/MeetingButtonManager.cs @@ -57,6 +57,7 @@ public static class MeetingButtonManager { ability.UsesLeft = ability.MaxUses; } + foreach (var ability in UntargetedButtons.Where(x => x.UsesMode == MeetingButtonUsesMode.PerGame)) { ability.UsesLeft = ability.MaxUses; @@ -101,6 +102,7 @@ public static class MeetingButtonManager var btn = ability.CreateButton(playerVoteArea); btn.transform.SetParent(playerVoteArea.Buttons.transform); } + var btn2 = ability.CreateButton(meetingHud.SkipVoteButton); btn2.transform.SetParent(meetingHud.SkipVoteButton.Buttons.transform); @@ -112,10 +114,12 @@ public static class MeetingButtonManager Error($"Failed to create targeted meeting button {ability.Name}: {e.Message}"); } } + foreach (var playerVoteArea in meetingHud.playerStates) { playerVoteArea.CancelButton.transform.SetAsFirstSibling(); } + meetingHud.SkipVoteButton.CancelButton.transform.SetAsFirstSibling(); } } diff --git a/MiraAPI/MeetingAbilities/MultiTargetMeetingButton.cs b/MiraAPI/MeetingAbilities/MultiTargetMeetingButton.cs index 586e3f6..6fb4bca 100644 --- a/MiraAPI/MeetingAbilities/MultiTargetMeetingButton.cs +++ b/MiraAPI/MeetingAbilities/MultiTargetMeetingButton.cs @@ -40,6 +40,7 @@ public abstract class MultiTargetMeetingButton : TargetedMeetingButton HandleFinish(); return; } + OnSelect(playerVoteArea, toggle); } @@ -53,6 +54,7 @@ public abstract class MultiTargetMeetingButton : TargetedMeetingButton { button.Renderer.sprite = Sprite.LoadAsset(); } + Timer = Cooldown; UsesLeft -= 1; OnFinish(); diff --git a/MiraAPI/MeetingAbilities/TargetedMeetingButtonSingleton.cs b/MiraAPI/MeetingAbilities/TargetedMeetingButtonSingleton.cs index 47dc791..3f20b61 100644 --- a/MiraAPI/MeetingAbilities/TargetedMeetingButtonSingleton.cs +++ b/MiraAPI/MeetingAbilities/TargetedMeetingButtonSingleton.cs @@ -7,7 +7,8 @@ namespace MiraAPI.MeetingAbilities; /// A utility class to get the instance of a . /// /// The type of the button you are trying to access. -public static class TargetedMeetingButtonSingleton where T : TargetedMeetingButton +public static class TargetedMeetingButtonSingleton + where T : TargetedMeetingButton { private static T? _instance; diff --git a/MiraAPI/MiraApiPlugin.cs b/MiraAPI/MiraApiPlugin.cs index 993744e..f8aec24 100644 --- a/MiraAPI/MiraApiPlugin.cs +++ b/MiraAPI/MiraApiPlugin.cs @@ -1,6 +1,4 @@ -global using static Reactor.Utilities.Logger; - -using System; +using System; using System.Globalization; using BepInEx; using BepInEx.Configuration; @@ -25,8 +23,10 @@ namespace MiraAPI; [BepInProcess("Among Us.exe")] [BepInDependency(ReactorPlugin.Id)] [ReactorModFlags(ModFlags.RequireOnAllClients)] +#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppression,) // ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated by BepInEx.) public partial class MiraApiPlugin : BasePlugin, IMiraPlugin +#pragma warning restore SA1515 // Single-line comment should be preceded by blank line { /// /// Gets the specified Culture for string manipulations. diff --git a/MiraAPI/MiraApiSettings.cs b/MiraAPI/MiraApiSettings.cs index 408fc11..1560955 100644 --- a/MiraAPI/MiraApiSettings.cs +++ b/MiraAPI/MiraApiSettings.cs @@ -57,6 +57,7 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) HudManagerPatches.ResizeUI(1f / OldButtonScaleFactor); HudManagerPatches.ResizeUI(ButtonUIFactorSlider.Value); } + OldButtonScaleFactor = ButtonUIFactorSlider.Value; } else if (configEntry == TopRightButtonsFactorSlider) @@ -65,6 +66,7 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) { ResizeUI(TopRightButtonsFactorSlider.Value); } + OldUiButtonScaleFactor = TopRightButtonsFactorSlider.Value; } else if (configEntry == WikiOnBottomRow) @@ -134,6 +136,7 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) { continue; } + if (button.transform.name.Contains("Friends List Button")) { button.gameObject.transform.localScale = new Vector3(0.2675f * actualScaleVal, 0.2675f * actualScaleVal, 1); @@ -148,6 +151,7 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) { baseGrid.ArrangeChilds(); } + baseAspect.AdjustPosition(); } @@ -163,6 +167,7 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) { continue; } + if (button.transform.name.Contains("Modifiers")) { button.gameObject.transform.localScale = new Vector3(0.65f * scaleFactor, 0.65f * scaleFactor, 1); @@ -177,6 +182,7 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) { extraGrid.ArrangeChilds(); } + extraAspect.AdjustPosition(); } diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index cc60627..0017fd9 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -41,8 +41,8 @@ public abstract class BaseModifier : IOptionable /// public MiraPluginInfo ParentMod => Array.Find( MiraPluginManager.Instance.RegisteredPlugins, - x => x.InternalModifiers.Exists(y => y.TypeId == TypeId) - ) ?? throw new InvalidOperationException("Modifier is not registered."); + x => x.InternalModifiers.Exists(y => y.TypeId == TypeId)) + ?? throw new InvalidOperationException("Modifier is not registered."); /// /// Gets the modifier name. diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index b5493b4..aeebfa2 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -26,6 +26,10 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) [HideFromIl2Cpp] public ImmutableList ActiveModifiers { get; private set; } = ImmutableList.Empty; + private readonly List _toRemove = []; + + private readonly List _toAdd = []; + private ModifierDisplayComponent? ModifierDisplay { get; set; } [HideFromIl2Cpp] @@ -33,10 +37,6 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) private PlayerControl _player = null!; - private readonly List _toRemove = []; - - private readonly List _toAdd = []; - internal void ClearModifiers() { _toRemove.AddRange(Modifiers); @@ -171,7 +171,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the e. /// An of s. [HideFromIl2Cpp] - public IEnumerable GetModifiers(Func? predicate = null) where T : BaseModifier + public IEnumerable GetModifiers(Func? predicate = null) + where T : BaseModifier { return ActiveModifiers.OfType().Where(x => predicate == null || predicate(x)); } @@ -210,7 +211,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the interface of the . /// An of s of type . [HideFromIl2Cpp] - public IEnumerable GetModifiersOfType(Func? predicate = null) where T : class + public IEnumerable GetModifiersOfType(Func? predicate = null) + where T : class { return ActiveModifiers.OfType().Where(x => predicate == null || predicate(x)); } @@ -223,7 +225,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the . /// if the was found, otherwise. [HideFromIl2Cpp] - public bool TryGetModifier([NotNullWhen(true)] out T? modifier, Func? predicate = null) where T : BaseModifier + public bool TryGetModifier([NotNullWhen(true)] out T? modifier, Func? predicate = null) + where T : BaseModifier { modifier = GetModifier(predicate); return modifier != null; @@ -278,7 +281,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the interface of the . /// if the of type was found, otherwise. [HideFromIl2Cpp] - public bool TryGetModifierOfType([NotNullWhen(true)] out T? modifier, Func? predicate = null) where T : class + public bool TryGetModifierOfType([NotNullWhen(true)] out T? modifier, Func? predicate = null) + where T : class { modifier = GetModifierOfType(predicate); return modifier != null; @@ -291,7 +295,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the . /// The if it is found, otherwise. [HideFromIl2Cpp] - public T? GetModifier(Func? predicate = null) where T : BaseModifier + public T? GetModifier(Func? predicate = null) + where T : BaseModifier { return GetModifiers(predicate).FirstOrDefault(); } @@ -341,7 +346,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the interface of the . /// The of type if it is found, otherwise. [HideFromIl2Cpp] - public T? GetModifierOfType(Func? predicate = null) where T : class + public T? GetModifierOfType(Func? predicate = null) + where T : class { return GetModifiersOfType(predicate).FirstOrDefault(); } @@ -352,7 +358,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The type. /// The predicate to check the by. [HideFromIl2Cpp] - public void RemoveModifier(Func? predicate = null) where T : BaseModifier + public void RemoveModifier(Func? predicate = null) + where T : BaseModifier { RemoveModifier(typeof(T), x => predicate == null || predicate((T)x)); } @@ -435,7 +442,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// if the is not active on this player, or there are multiple instances; /// else . [HideFromIl2Cpp] - public bool TryRemoveModifier(Func? predicate = null) where T : BaseModifier + public bool TryRemoveModifier(Func? predicate = null) + where T : BaseModifier { return TryGetModifier(out var modifier, predicate) && TryRemoveModifier(modifier); @@ -505,7 +513,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the . /// The new . [HideFromIl2Cpp] - public T? AddModifier(params object[] args) where T : BaseModifier + public T? AddModifier(params object[] args) + where T : BaseModifier { return AddModifier(typeof(T), args) as T; } @@ -544,6 +553,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) { modifier.UniqueId = Guid.NewGuid(); } + return modifier; } @@ -585,7 +595,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the . /// if the is present, otherwise. [HideFromIl2Cpp] - public bool HasModifier(Func? predicate = null) where T : BaseModifier + public bool HasModifier(Func? predicate = null) + where T : BaseModifier { return ActiveModifiers.Exists(x => x is T modifier && (predicate == null || predicate(modifier))); } @@ -636,7 +647,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the . /// if the is present, otherwise. [HideFromIl2Cpp] - public bool HasModifier(bool checkInactive, Func? predicate = null) where T : BaseModifier + public bool HasModifier(bool checkInactive, Func? predicate = null) + where T : BaseModifier { return ActiveModifiers.Exists(MatchExpr) || (checkInactive && _toAdd.Exists(MatchExpr)); bool MatchExpr(BaseModifier bm) @@ -701,7 +713,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the interface of the Modifier. /// True if the Modifier is present, false otherwise. [HideFromIl2Cpp] - public bool HasModifierOfType(Func? predicate = null) where T : class + public bool HasModifierOfType(Func? predicate = null) + where T : class { return ActiveModifiers.Exists(x => x is T modifier && (predicate == null || predicate(modifier))); } @@ -714,7 +727,8 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) /// The Type of the interface of the Modifier. /// True if the Modifier is present, false otherwise. [HideFromIl2Cpp] - public bool HasModifierOfType(bool checkInactive, Func? predicate = null) where T : class + public bool HasModifierOfType(bool checkInactive, Func? predicate = null) + where T : class { return ActiveModifiers.Exists(MatchExpr) || (checkInactive && _toAdd.Exists(MatchExpr)); bool MatchExpr(BaseModifier bm) diff --git a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs index 6f6f33a..555ab55 100644 --- a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs +++ b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs @@ -23,11 +23,15 @@ namespace MiraAPI.Modifiers.ModifierDisplay; [SuppressMessage("Style", "IDE0051:Remove unused private members", Justification = "Unity Convention.")] public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr) { + private const int ItemsPerPage = 3; + /// /// Gets the instance of the . /// public static ModifierDisplayComponent? Instance { get; private set; } + private readonly Dictionary _modifiers = []; + private RectTransform _children = null!; private GameObject _modTemplate = null!; private PassiveButton _toggleButton = null!; @@ -39,9 +43,6 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr) private PassiveButton _backButton = null!; private int _currentPage; - private const int ItemsPerPage = 3; - - private readonly Dictionary _modifiers = []; /// /// Gets a of the created s. diff --git a/MiraAPI/Modifiers/ModifierDisplay/ModifierUiComponent.cs b/MiraAPI/Modifiers/ModifierDisplay/ModifierUiComponent.cs index 8061250..cfe6319 100644 --- a/MiraAPI/Modifiers/ModifierDisplay/ModifierUiComponent.cs +++ b/MiraAPI/Modifiers/ModifierDisplay/ModifierUiComponent.cs @@ -25,6 +25,7 @@ public class ModifierUiComponent(nint cppPtr) : MonoBehaviour(cppPtr) private SpriteRenderer icon = null!; private TextMeshPro nameText = null!; private RectTransform descRect = null!; + // ReSharper restore InconsistentNaming private void Awake() { diff --git a/MiraAPI/Modifiers/ModifierExtensions.cs b/MiraAPI/Modifiers/ModifierExtensions.cs index 4d69428..47875a2 100644 --- a/MiraAPI/Modifiers/ModifierExtensions.cs +++ b/MiraAPI/Modifiers/ModifierExtensions.cs @@ -52,7 +52,8 @@ public static class ModifierExtensions /// The to add the to. /// The arguments to initialize the with. /// The Type. - public static void RpcAddModifier(this PlayerControl player, params object[] args) where T : BaseModifier + public static void RpcAddModifier(this PlayerControl player, params object[] args) + where T : BaseModifier { player.RpcAddModifier(typeof(T), args); } @@ -147,7 +148,8 @@ public static class ModifierExtensions /// The instance. /// Optional predicate to filter the s. /// if the has the , otherwise. - public static bool HasModifier(this PlayerControl player, Func? predicate = null) where T : BaseModifier + public static bool HasModifier(this PlayerControl player, Func? predicate = null) + where T : BaseModifier { return player.GetModifierComponent().HasModifier(predicate); } @@ -197,7 +199,8 @@ public static class ModifierExtensions /// The instance. /// Optional predicate to filter the s. /// if the has the of type , otherwise. - public static bool HasModifierOfType(this PlayerControl player, Func? predicate = null) where T : class + public static bool HasModifierOfType(this PlayerControl player, Func? predicate = null) + where T : class { return player.GetModifierComponent().HasModifierOfType(predicate); } @@ -234,7 +237,8 @@ public static class ModifierExtensions /// The predicate to check the by. /// The Type of the . /// if the was found, otherwise. - public static bool TryGetModifier(this PlayerControl player, [NotNullWhen(true)] out T? modifier, Func? predicate = null) where T : BaseModifier + public static bool TryGetModifier(this PlayerControl player, [NotNullWhen(true)] out T? modifier, Func? predicate = null) + where T : BaseModifier { return player.GetModifierComponent().TryGetModifier(out modifier, predicate); } @@ -285,7 +289,8 @@ public static class ModifierExtensions /// The predicate to check the of type by. /// The Type of the interface of the . /// if the of type was found, otherwise. - public static bool TryGetModifierOfType(this PlayerControl player, [NotNullWhen(true)] out T? modifier, Func? predicate = null) where T : class + public static bool TryGetModifierOfType(this PlayerControl player, [NotNullWhen(true)] out T? modifier, Func? predicate = null) + where T : class { return player.GetModifierComponent().TryGetModifierOfType(out modifier, predicate); } @@ -297,7 +302,8 @@ public static class ModifierExtensions /// The instance. /// Optional predicate to filter the s. /// The if found, otherwise. - public static T? GetModifier(this PlayerControl player, Func? predicate = null) where T : BaseModifier + public static T? GetModifier(this PlayerControl player, Func? predicate = null) + where T : BaseModifier { return player.GetModifierComponent().GetModifier(predicate); } @@ -352,7 +358,8 @@ public static class ModifierExtensions /// The instance. /// Optional predicate to filter the s. /// The of type if found, otherwise. - public static T? GetModifierOfType(this PlayerControl player, Func? predicate = null) where T : class + public static T? GetModifierOfType(this PlayerControl player, Func? predicate = null) + where T : class { return player.GetModifierComponent().GetModifierOfType(predicate); } @@ -546,7 +553,8 @@ public static class ModifierExtensions /// The instance. /// The arguments to initialize the with. /// The added . - public static T? AddModifier(this PlayerControl player, params object[] args) where T : BaseModifier + public static T? AddModifier(this PlayerControl player, params object[] args) + where T : BaseModifier { return player.GetModifierComponent().AddModifier(args); } diff --git a/MiraAPI/Modifiers/ModifierFactory.cs b/MiraAPI/Modifiers/ModifierFactory.cs index cd3c9be..780eb0c 100644 --- a/MiraAPI/Modifiers/ModifierFactory.cs +++ b/MiraAPI/Modifiers/ModifierFactory.cs @@ -63,7 +63,8 @@ public static class ModifierFactory /// Factory for creating instances of . More efficient than using reflection. /// /// The type. -public static class ModifierFactory where T : BaseModifier +public static class ModifierFactory + where T : BaseModifier { private static readonly Func Constructor = CreateConstructor(); diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs index ea60a34..935c82e 100644 --- a/MiraAPI/Modifiers/ModifierManager.cs +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -136,6 +136,7 @@ public static class ModifierManager { Warning($"No valid players for modifier {modifier.ModifierName.Translate()}"); } + continue; } @@ -188,6 +189,7 @@ public static class ModifierManager !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)) && diff --git a/MiraAPI/Modifiers/ModifierUtils.cs b/MiraAPI/Modifiers/ModifierUtils.cs index caee948..4d499c9 100644 --- a/MiraAPI/Modifiers/ModifierUtils.cs +++ b/MiraAPI/Modifiers/ModifierUtils.cs @@ -15,7 +15,8 @@ public static class ModifierUtils /// Select if is valid to be added to list. /// The type. /// An of s. - public static IEnumerable GetActiveModifiers(Func? predicate = null) where T : BaseModifier + public static IEnumerable GetActiveModifiers(Func? predicate = null) + where T : BaseModifier { return PlayerControl.AllPlayerControls.ToArray().SelectMany(x => x.GetModifiers(predicate)); } @@ -26,7 +27,8 @@ public static class ModifierUtils /// Select if is valid. /// The type. /// An of s with that . - public static IEnumerable GetPlayersWithModifier(Func? predicate = null) where T : BaseModifier + public static IEnumerable GetPlayersWithModifier(Func? predicate = null) + where T : BaseModifier { return PlayerControl.AllPlayerControls.ToArray().Where(x => x.HasModifier(predicate)); } diff --git a/MiraAPI/Networking/CustomGameOverRpc.cs b/MiraAPI/Networking/CustomGameOverRpc.cs index 42f4889..f6b77e8 100644 --- a/MiraAPI/Networking/CustomGameOverRpc.cs +++ b/MiraAPI/Networking/CustomGameOverRpc.cs @@ -12,8 +12,10 @@ namespace MiraAPI.Networking; /// /// [RegisterCustomRpc((uint)MiraRpc.CustomGameOver)] -// ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated via Activator.CreateInstance.) +#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppression.) +// ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated by Reactor.) public class CustomGameOverRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) +#pragma warning restore SA1515 // Single-line comment should be preceded by blank line { /// public override RpcLocalHandling LocalHandling => RpcLocalHandling.After; diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs index e840d00..4be8723 100644 --- a/MiraAPI/Networking/CustomMurderRpc.cs +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -81,6 +81,7 @@ public static class CustomMurderRpc { return; } + var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError; var beforeMurderEvent = new BeforeMurderEvent(source, target, inMeeting); @@ -222,6 +223,7 @@ public static class CustomMurderRpc { return; } + var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError; var beforeMurderEvent = new BeforeMurderEvent(source, target, isIndirect, ignoreDefense, inMeeting); @@ -351,6 +353,7 @@ public static class CustomMurderRpc { return; } + var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError; var beforeMurderEvent = new BeforeMurderEvent(source, target, framed, isIndirect, ignoreDefense, inMeeting); @@ -745,6 +748,7 @@ public static class CustomMurderRpc { yield break; } + var cam = Camera.main?.GetComponent(); var playerToLunge = framed ?? source; var isParticipant = playerToLunge.AmOwner || target.AmOwner; diff --git a/MiraAPI/Networking/Modifiers/AddModifierRpc.cs b/MiraAPI/Networking/Modifiers/AddModifierRpc.cs index 399d436..5b29282 100644 --- a/MiraAPI/Networking/Modifiers/AddModifierRpc.cs +++ b/MiraAPI/Networking/Modifiers/AddModifierRpc.cs @@ -13,8 +13,10 @@ namespace MiraAPI.Networking.Modifiers; /// Mira plugin. /// RPC ID. [RegisterCustomRpc((uint)MiraRpc.AddModifier)] +#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppression.) // ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated by Reactor.) public class AddModifierRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) +#pragma warning restore SA1515 // Single-line comment should be preceded by blank line { /// public override RpcLocalHandling LocalHandling => RpcLocalHandling.Before; diff --git a/MiraAPI/Networking/SyncOptionsRpc.cs b/MiraAPI/Networking/SyncOptionsRpc.cs index e9f091d..6de1933 100644 --- a/MiraAPI/Networking/SyncOptionsRpc.cs +++ b/MiraAPI/Networking/SyncOptionsRpc.cs @@ -7,8 +7,10 @@ namespace MiraAPI.Networking; // METHOD RPC DOESN'T WORK WITH THE ARRAYS AND STUFF SO THIS IS HOW WE WILL DO IT FOR NOW [RegisterCustomRpc((uint)MiraRpc.SyncGameOptions)] -// ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated via Activator.CreateInstance.) +#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppression.) +// ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated by Reactor.) internal sealed class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) +#pragma warning restore SA1515 // Single-line comment should be preceded by blank line { public override RpcLocalHandling LocalHandling => RpcLocalHandling.None; diff --git a/MiraAPI/Networking/SyncRoleOptionsRpc.cs b/MiraAPI/Networking/SyncRoleOptionsRpc.cs index 1f965ab..fb0f2af 100644 --- a/MiraAPI/Networking/SyncRoleOptionsRpc.cs +++ b/MiraAPI/Networking/SyncRoleOptionsRpc.cs @@ -6,8 +6,10 @@ using Reactor.Networking.Rpc; namespace MiraAPI.Networking; [RegisterCustomRpc((uint)MiraRpc.SyncRoleOptions)] -// ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated via Activator.CreateInstance.) +#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppression.) +// ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated by Reactor.) internal sealed class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) +#pragma warning restore SA1515 // Single-line comment should be preceded by blank line { public override RpcLocalHandling LocalHandling => RpcLocalHandling.None; diff --git a/MiraAPI/Patches/Accessibility/HudManagerFlashPatches.cs b/MiraAPI/Patches/Accessibility/HudManagerFlashPatches.cs index 1389f33..76dc0f4 100644 --- a/MiraAPI/Patches/Accessibility/HudManagerFlashPatches.cs +++ b/MiraAPI/Patches/Accessibility/HudManagerFlashPatches.cs @@ -9,8 +9,10 @@ namespace MiraAPI.Patches.Accessibility; [HarmonyPatch] [SuppressMessage("Style", "IDE0074:Use compound assignment", Justification = "Using compound assignment bypasses Unity lifetime checks.")] +#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppression.) // ReSharper disable ConvertIfStatementToNullCoalescingAssignment (Justification: Read above.) public static class HudManagerFlashPatches +#pragma warning restore SA1515 // Single-line comment should be preceded by blank line { [HarmonyPatch(typeof(HudManager), nameof(HudManager.StartReactorFlash))] [HarmonyPrefix] @@ -42,6 +44,7 @@ public static class HudManagerFlashPatches { yield break; } + var hudManager = HudManager.Instance; var wait = new WaitForSeconds(1f); var light = false; @@ -55,6 +58,7 @@ public static class HudManagerFlashPatches { SoundManager.Instance.PlaySound(ShipStatus.Instance.SabotageSound, false); } + light = !light; /*if (!MiraApiPlugin.IsMobile) diff --git a/MiraAPI/Patches/Events/CanUsePatches.cs b/MiraAPI/Patches/Events/CanUsePatches.cs index 5ae7519..7af9acd 100644 --- a/MiraAPI/Patches/Events/CanUsePatches.cs +++ b/MiraAPI/Patches/Events/CanUsePatches.cs @@ -21,6 +21,7 @@ public static class CanUsePatches yield return AccessTools.Method(typeof(PlatformConsole), nameof(PlatformConsole.CanUse)); yield return AccessTools.Method(typeof(SystemConsole), nameof(SystemConsole.CanUse)); yield return AccessTools.Method(typeof(ZiplineConsole), nameof(ZiplineConsole.CanUse)); + // vent is not necessary as it has its own patch } diff --git a/MiraAPI/Patches/Events/VanillaButtonPatches.cs b/MiraAPI/Patches/Events/VanillaButtonPatches.cs index c95ffe2..6b12a22 100644 --- a/MiraAPI/Patches/Events/VanillaButtonPatches.cs +++ b/MiraAPI/Patches/Events/VanillaButtonPatches.cs @@ -31,6 +31,7 @@ public static class VanillaButtonPatches { ventTarget = role.Cast().currentTarget; } + var genericEvent = new VanillaButtonClickEvent(__instance, playerTarget, ventTarget); MiraEventManager.InvokeEvent(genericEvent); if (genericEvent.IsCancelled) @@ -52,6 +53,7 @@ public static class VanillaButtonPatches { playerTarget = role.Cast().currentTarget; } + var genericEvent = new VanillaButtonClickEvent(__instance, playerTarget); MiraEventManager.InvokeEvent(genericEvent); if (genericEvent.IsCancelled) diff --git a/MiraAPI/Patches/Events/VentEventPatches.cs b/MiraAPI/Patches/Events/VentEventPatches.cs index 8eee46f..737d221 100644 --- a/MiraAPI/Patches/Events/VentEventPatches.cs +++ b/MiraAPI/Patches/Events/VentEventPatches.cs @@ -35,6 +35,7 @@ public static class VentEventPatches { _showButtons = !@event.IsCancelled; } + return !@event.IsCancelled; } @@ -52,6 +53,7 @@ public static class VentEventPatches { _showButtons = @event.IsCancelled; } + return !@event.IsCancelled; } } diff --git a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs index 0e3ab07..7db4211 100644 --- a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs +++ b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Text; @@ -23,8 +24,8 @@ namespace MiraAPI.Patches.Freeplay; [HarmonyPatch(typeof(TaskAdderGame))] public static class TaskAdderPatches { - private static Scroller _scroller = null!; private static readonly Dictionary Folders = []; + private static Scroller _scroller = null!; public static string CrewmateName => TranslationController.Instance.GetString(StringNames.Crewmate); public static string ImpostorName => TranslationController.Instance.GetString(StringNames.Impostor); @@ -33,6 +34,7 @@ public static class TaskAdderPatches [HarmonyPostfix] [HarmonyPatch(nameof(TaskAdderGame.Begin))] + [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "Don't know, but seems important enough to keep.")] public static void AddRolesFolder(TaskAdderGame __instance) { GameObject inner = new("Inner"); @@ -73,13 +75,13 @@ public static class TaskAdderPatches __instance.TaskParent = inner.transform; var crewmateFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == CrewmateName)!; var impostorFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == ImpostorName)!; - // var neutralFolder = __instance.CreateFolder("Neutral", __instance.Root, 2, Color.gray); + // var neutralFolder = __instance.CreateFolder(NeutralName, __instance.Root, 2, Color.gray); var modifiersFolder = __instance.CreateFolder(ModifiersName, __instance.Root, 0, Color.blue); Folders.Clear(); Folders.Add(crewmateFolder.FolderName, crewmateFolder); Folders.Add(impostorFolder.FolderName, impostorFolder); - // folders.Add("Neutrals", neutralFolder); + // Folders.Add(NeutralName, neutralFolder); Folders.Add(ModifiersName, modifiersFolder); var folderIdx = 2; @@ -266,6 +268,7 @@ public static class TaskAdderPatches stringBuilder.Append(t.FolderName); stringBuilder.Append('\\'); } + __instance.PathText.text = stringBuilder.ToString(); __instance.PathText.fontSizeMin = 3; __instance.PathText.fontSizeMax = 3; @@ -368,6 +371,7 @@ public static class TaskAdderPatches { // I hate you } + if (split is ["Roles", _, _] && Folders.Any(x => taskFolder.IsChildOf(x.Value))) { var plugin = MiraPluginManager.GetPluginByGuid(split[2]); @@ -381,6 +385,7 @@ public static class TaskAdderPatches { continue; } + var crewLocale = TranslationController.Instance.GetString(StringNames.Crewmate); var impLocale = TranslationController.Instance.GetString(StringNames.Impostor); @@ -389,6 +394,7 @@ public static class TaskAdderPatches { teamFolder = cs.Configuration.FreeplayFolder; } + if (split[1] != teamFolder) { continue; @@ -416,6 +422,7 @@ public static class TaskAdderPatches { customColor = Palette.ImpostorRed; } + roleAddButton.FileImage.color = customColor; roleAddButton.RolloverHandler.OutColor = customColor; } diff --git a/MiraAPI/Patches/GameModes/ConsolePatches.cs b/MiraAPI/Patches/GameModes/ConsolePatches.cs index b80e2b2..ecfdf70 100644 --- a/MiraAPI/Patches/GameModes/ConsolePatches.cs +++ b/MiraAPI/Patches/GameModes/ConsolePatches.cs @@ -10,67 +10,67 @@ internal static class ConsolePatches [HarmonyPrefix, HarmonyPatch(typeof(Console), nameof(Console.CanUse))] public static bool CanUsePatch(Console __instance, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] out bool canUse, [HarmonyArgument(2)] out bool couldUse) { - if (AmongUsClient.Instance.GameState == InnerNetClient.GameStates.Started && ShipStatus.Instance) + if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started || !ShipStatus.Instance) { - if (CustomGameModeManager.ActiveMode == null || CustomGameModeManager.ActiveMode.CanUseTasks(__instance)) - { - var task = __instance.FindTask(pc.Object); - - if (task && task.GetComponent()) - { - canUse = couldUse = true; - return true; - } - - canUse = false; - couldUse = false; - return true; - } + canUse = couldUse = true; + return true; + } + if (CustomGameModeManager.ActiveMode != null && !CustomGameModeManager.ActiveMode.CanUseTasks(__instance)) + { canUse = couldUse = false; return false; } - canUse = couldUse = true; + var task = __instance.FindTask(pc.Object); + + if (task && task.GetComponent()) + { + canUse = couldUse = true; + return true; + } + + canUse = false; + couldUse = false; return true; } [HarmonyPrefix, HarmonyPatch(typeof(SystemConsole), nameof(SystemConsole.CanUse))] public static bool SystemCanUsePatch(SystemConsole __instance, [HarmonyArgument(1)] out bool canUse, [HarmonyArgument(2)] out bool couldUse) { - if (AmongUsClient.Instance.GameState == InnerNetClient.GameStates.Started && ShipStatus.Instance) + if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started || !ShipStatus.Instance) { - if (CustomGameModeManager.ActiveMode == null || CustomGameModeManager.ActiveMode.CanUseSystemConsole(__instance)) - { - canUse = couldUse = true; - return true; - } - - canUse = couldUse = false; - return false; + canUse = couldUse = true; + return true; } - canUse = couldUse = true; - return true; + if (CustomGameModeManager.ActiveMode == null || CustomGameModeManager.ActiveMode.CanUseSystemConsole(__instance)) + { + canUse = couldUse = true; + return true; + } + + canUse = couldUse = false; + return false; } [HarmonyPrefix, HarmonyPatch(typeof(MapConsole), nameof(MapConsole.CanUse))] public static bool MapCanUsePatch(MapConsole __instance, [HarmonyArgument(1)] out bool canUse, [HarmonyArgument(2)] out bool couldUse) { - if (AmongUsClient.Instance.GameState == InnerNetClient.GameStates.Started && ShipStatus.Instance) + if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started || !ShipStatus.Instance) { - if (CustomGameModeManager.ActiveMode == null || CustomGameModeManager.ActiveMode.CanUseMapConsole(__instance)) - { - canUse = couldUse = true; - return true; - } - - canUse = couldUse = false; - return false; + canUse = couldUse = true; + return true; } - canUse = couldUse = true; - return true; + if (CustomGameModeManager.ActiveMode == null || CustomGameModeManager.ActiveMode.CanUseMapConsole(__instance)) + { + canUse = couldUse = true; + return true; + } + + canUse = couldUse = false; + return false; } [HarmonyPrefix, HarmonyPatch(typeof(MapConsole), nameof(MapConsole.Use))] diff --git a/MiraAPI/Patches/GameModes/GameLogicPatches.cs b/MiraAPI/Patches/GameModes/GameLogicPatches.cs index 0a482c1..719fe05 100644 --- a/MiraAPI/Patches/GameModes/GameLogicPatches.cs +++ b/MiraAPI/Patches/GameModes/GameLogicPatches.cs @@ -30,6 +30,7 @@ internal static class GameLogicPatches { return true; } + __result = CustomGameModeManager.ActiveMode.GetBodyType(player); return false; } diff --git a/MiraAPI/Patches/GameModes/GameManagerPatches.cs b/MiraAPI/Patches/GameModes/GameManagerPatches.cs index e8f4500..72b53d6 100644 --- a/MiraAPI/Patches/GameModes/GameManagerPatches.cs +++ b/MiraAPI/Patches/GameModes/GameManagerPatches.cs @@ -17,6 +17,7 @@ internal static class GameManagerPatches return true; } + [HarmonyPrefix, HarmonyPatch(nameof(GameManager.ShowCrewmatesKilled))] public static bool ShowCrewmatesKilledPrefix(ref bool __result) { @@ -28,6 +29,7 @@ internal static class GameManagerPatches return true; } + [HarmonyPatch(typeof(NormalGameManager), nameof(NormalGameManager.GetMapOptions))] [HarmonyPrefix] public static bool GetMapOptions(ref MapOptions __result) diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index 44b77b9..5c3038b 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -50,6 +50,7 @@ public static class HudManagerPatches { _storedButtonsParent = HudManager.Instance.transform.FindChild("Buttons"); } + if (_storedButtonsParent) { foreach (var aspect in _storedButtonsParent.GetComponentsInChildren(true)) @@ -146,6 +147,7 @@ public static class HudManagerPatches { return; } + __instance.chatButton.transform.localPosition = MiraHudHelper.ClonedChatButton.transform.localPosition + new Vector3(-0.3f, 0); } @@ -219,6 +221,7 @@ public static class HudManagerPatches fakeButton.ToggleVisible(true); fakeButton.Destroy(); } + ModdedKeybindIcons = []; foreach (var button in CustomButtonManager.CustomButtons) @@ -244,6 +247,7 @@ public static class HudManagerPatches Error($"Failed to create custom button {button.GetType().Name}: {e}"); } } + __instance.ImpostorVentButton.transform.SetParent(null); __instance.ImpostorVentButton.transform.SetParent(BottomRight.transform); @@ -273,6 +277,7 @@ public static class HudManagerPatches { continue; } + var icon = Helpers.CreateKeybindIcon(buttonObj, key, keybindIconPos); vanillaKeybindIcons.Add(icon.transform.GetChild(0).GetComponent(), actionId); if (!MiraApiPlugin.IsMobile) @@ -281,6 +286,7 @@ public static class HudManagerPatches KeybindManager.VanillaKeybinds[comp.GetType()].Button = comp; } } + MiraApiSettings.OldButtonScaleFactor = LocalSettingsTabSingleton.Instance.ButtonUIFactorSlider.Value; MiraApiSettings.OldUiButtonScaleFactor = diff --git a/MiraAPI/Patches/IntroCutscenePatches.cs b/MiraAPI/Patches/IntroCutscenePatches.cs index d236528..8abbefb 100644 --- a/MiraAPI/Patches/IntroCutscenePatches.cs +++ b/MiraAPI/Patches/IntroCutscenePatches.cs @@ -50,6 +50,7 @@ public static class IntroCutscenePatches public static void Postfix(Il2CppObjectBase __instance) { var wrapper = new StateMachineWrapper(__instance); + // run before the first yield if (wrapper.GetState() != 1) { @@ -117,17 +118,20 @@ public static class IntroCutscenePatches if (_usedFallback) { var wrapper = new StateMachineWrapper(__instance); + // run after the final yield if (wrapper.GetState() != -1) { return; } + introCutscene = wrapper.Instance; } else { introCutscene = __instance.Cast(); } + Info("IntroCutscene ended"); MeetingButtonManager.OnGameStart(); diff --git a/MiraAPI/Patches/Keybinds/OptionsMenuBehaviourPatch.cs b/MiraAPI/Patches/Keybinds/OptionsMenuBehaviourPatch.cs index 20fab5e..584d6cc 100644 --- a/MiraAPI/Patches/Keybinds/OptionsMenuBehaviourPatch.cs +++ b/MiraAPI/Patches/Keybinds/OptionsMenuBehaviourPatch.cs @@ -36,6 +36,7 @@ public static class OptionsMenuBehaviourPatch { return; } + if (_remapBackground == null) { return; diff --git a/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs index d986949..af70764 100644 --- a/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs +++ b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs @@ -14,14 +14,16 @@ namespace MiraAPI.Patches.LocalSettings; [HarmonyPatch(typeof(OptionsMenuBehaviour))] public static class OptionsMenuPatches { + private static readonly Dictionary> TabButtons = []; + private static int currentPage = 1; + internal static OptionsMenuBehaviour? Instance { get; private set; } + [SuppressMessage("Critical Code Smell", "S2223:Non-constant static fields should not be visible", Justification = "Internal behaviour that does not need property-level validation.")] [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Read above.")] internal static BoxCollider2D MaskCollider = null!; - private static SpriteRenderer? background; - private static int currentPage = 1; - private static readonly Dictionary> TabButtons = []; + private static SpriteRenderer? background; /// /// Creates the s and their content. @@ -31,6 +33,7 @@ public static class OptionsMenuPatches public static void StartPostfix(OptionsMenuBehaviour __instance) { Instance = __instance; + // Fix for tabs not being clickable in the main menu if (!AmongUsClient.Instance.IsInGame) { @@ -87,11 +90,13 @@ public static class OptionsMenuPatches TabButtons.Add(page, []); list = TabButtons[page]; } + list.Add(button); yOffset += 0.6f; tabIdx++; i++; } + if (i > 7) { i = 0; @@ -107,6 +112,7 @@ public static class OptionsMenuPatches { currentPage = 1; } + if (currentPage < 1) { currentPage = TabButtons.Count; @@ -220,6 +226,7 @@ public static class OptionsMenuPatches tab.Open(); } + private static void CustomClose(LocalSettingsTab tab) { if (tab.TabButton?.Button) diff --git a/MiraAPI/Patches/Menu/HatsTabPatches.cs b/MiraAPI/Patches/Menu/HatsTabPatches.cs index cffca31..dc2e805 100644 --- a/MiraAPI/Patches/Menu/HatsTabPatches.cs +++ b/MiraAPI/Patches/Menu/HatsTabPatches.cs @@ -18,8 +18,8 @@ namespace MiraAPI.Patches.Menu; [HarmonyPatch(typeof(HatsTab))] public static class HatsTabPatches { - private static SortedList> sortedHats = []; private static readonly Dictionary StoreNames = []; + private static SortedList> sortedHats = []; private static int currentPage; private static void PreviousPage(HatsTab hatsTab) @@ -44,6 +44,7 @@ public static class HatsTabPatches { return true; } + __instance.currentHat = HatManager.Instance.GetHatById(DataManager.Player.Customization.Hat); var allHats = HatManager.Instance.GetUnlockedHats().ToImmutableList(); @@ -80,6 +81,7 @@ public static class HatsTabPatches { return; } + if (sortedHats.Count == 0) return; if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow)) @@ -120,6 +122,7 @@ public static class HatsTabPatches text.fontSizeMax = 5f; text.fontSizeMin = 0f; var xLerp = __instance.XRange.Lerp(0.5f); + // ReSharper disable once PossibleLossOfFraction (Justification: Intended.) var yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset; text.transform.localPosition = new Vector3(xLerp, yLerp, -1f); @@ -127,6 +130,7 @@ public static class HatsTabPatches hatIndex += 5; loadRoutine = Coroutines.Start(CoGenerateChips(__instance, hats)); } + // ReSharper disable once PossibleLossOfFraction (Justification: Intended.) __instance.scroller.ContentYBounds.max = -(__instance.YStart - (hatIndex + 1) / __instance.NumPerRow * __instance.YOffset) - 3f; __instance.currentHatIsEquipped = true; @@ -143,6 +147,7 @@ public static class HatsTabPatches foreach (var hat in batch.OrderBy(HatManager.Instance.allHats.IndexOf)) { var hatXPosition = __instance.XRange.Lerp(hatIndex % __instance.NumPerRow / (__instance.NumPerRow - 1f)); + // ReSharper disable once PossibleLossOfFraction (Justification: Intended.) var hatYPosition = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset; GenerateColorChip(__instance, new Vector2(hatXPosition, hatYPosition), hat); @@ -153,6 +158,7 @@ public static class HatsTabPatches __instance.SetScrollerBounds(); yield return new WaitForSeconds(0.01f); } + __instance.currentHatIsEquipped = true; loadRoutine = null; } diff --git a/MiraAPI/Patches/Menu/InventoryUtility.cs b/MiraAPI/Patches/Menu/InventoryUtility.cs index 95004d0..8284005 100644 --- a/MiraAPI/Patches/Menu/InventoryUtility.cs +++ b/MiraAPI/Patches/Menu/InventoryUtility.cs @@ -10,7 +10,8 @@ namespace MiraAPI.Patches.Menu; public static class InventoryUtility { - public static void CreateNextBackButtons(T tab, Action previousPage, Action nextPage) where T : InventoryTab + public static void CreateNextBackButtons(T tab, Action previousPage, Action nextPage) + where T : InventoryTab { var title = tab.transform.FindChild("Text"); title.localPosition = new Vector3(.9123f, -.23f, -55); diff --git a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs index 333309a..cc2778c 100644 --- a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs +++ b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs @@ -1,12 +1,12 @@ -using AmongUs.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using AmongUs.Data; using HarmonyLib; using Innersloth.Assets; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; using TMPro; using UnityEngine; using Object = UnityEngine.Object; @@ -50,6 +50,7 @@ public static class NameplatesTabPatches { return true; } + __instance.plateId = HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.namePlate).ProdId; if (!SortedNameplates.ContainsKey("Vanilla")) AddRange(HatManager.Instance.GetUnlockedNamePlates().Select(x => ("Vanilla", x))); @@ -70,6 +71,7 @@ public static class NameplatesTabPatches { return; } + if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow)) { PreviousPage(__instance); @@ -109,6 +111,7 @@ public static class NameplatesTabPatches foreach (var visor in nameplates.OrderBy(HatManager.Instance.allNamePlates.IndexOf)) { var hatXPosition = __instance.XRange.Lerp(hatIndex % __instance.NumPerRow / (__instance.NumPerRow - 1f)); + // ReSharper disable once PossibleLossOfFraction (Justification: Intended.) var hatYPosition = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset; GenerateColorChip(__instance, new Vector2(hatXPosition, hatYPosition), visor); @@ -149,6 +152,7 @@ public static class NameplatesTabPatches void LoadNameplate(NamePlateViewData viewData) { colorChip.transform.GetChild(1).GetComponent().sprite = viewData.Image; + // (colorChip as NameplateChip).image.sprite = viewData?.Image; } } diff --git a/MiraAPI/Patches/Menu/SkinsTabPatches.cs b/MiraAPI/Patches/Menu/SkinsTabPatches.cs index 1825c1c..3ca3578 100644 --- a/MiraAPI/Patches/Menu/SkinsTabPatches.cs +++ b/MiraAPI/Patches/Menu/SkinsTabPatches.cs @@ -1,12 +1,12 @@ -using AmongUs.Data; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using AmongUs.Data; using HarmonyLib; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using TMPro; using UnityEngine; using Object = UnityEngine.Object; @@ -41,6 +41,7 @@ public static class SkinsTabPatches { return true; } + __instance.skinId = HatManager.Instance.GetSkinById(DataManager.Player.Customization.Skin).ProdId; var allSkins = HatManager.Instance.GetUnlockedSkins().ToImmutableList(); @@ -71,6 +72,7 @@ public static class SkinsTabPatches { return; } + if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow)) { PreviousPage(__instance); @@ -110,6 +112,7 @@ public static class SkinsTabPatches foreach (var skin in skins.OrderBy(HatManager.Instance.allSkins.IndexOf)) { var hatXPosition = __instance.XRange.Lerp(hatIndex % __instance.NumPerRow / (__instance.NumPerRow - 1f)); + // ReSharper disable once PossibleLossOfFraction (Justification: Intended.) var hatYPosition = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset; GenerateColorChip(__instance, new Vector2(hatXPosition, hatYPosition), skin); @@ -136,6 +139,7 @@ public static class SkinsTabPatches { colorChip.Button.OnClick.AddListener((Action)(() => __instance.SelectSkin(skin))); } + colorChip.Button.ClickMask = __instance.scroller.Hitbox; colorChip.ProductId = skin.ProductId; colorChip.SelectionHighlight.gameObject.SetActive(false); diff --git a/MiraAPI/Patches/Menu/VisorsTabPatches.cs b/MiraAPI/Patches/Menu/VisorsTabPatches.cs index fa208de..04c41d0 100644 --- a/MiraAPI/Patches/Menu/VisorsTabPatches.cs +++ b/MiraAPI/Patches/Menu/VisorsTabPatches.cs @@ -1,11 +1,11 @@ -using AmongUs.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using AmongUs.Data; using HarmonyLib; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; using TMPro; using UnityEngine; using Object = UnityEngine.Object; @@ -49,6 +49,7 @@ public static class VisorsTabPatches { return true; } + __instance.visorId = HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor).ProdId; if (!SortedVisors.ContainsKey("Vanilla")) AddRange(HatManager.Instance.GetUnlockedVisors().Select(x => ("Vanilla", x))); @@ -69,6 +70,7 @@ public static class VisorsTabPatches { return; } + if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow)) { PreviousPage(__instance); @@ -108,6 +110,7 @@ public static class VisorsTabPatches foreach (var visor in visors.OrderBy(HatManager.Instance.allVisors.IndexOf)) { var hatXPosition = __instance.XRange.Lerp(hatIndex % __instance.NumPerRow / (__instance.NumPerRow - 1f)); + // ReSharper disable once PossibleLossOfFraction (Justification: Intended.) var hatYPosition = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset; GenerateColorChip(__instance, new Vector2(hatXPosition, hatYPosition), visor); diff --git a/MiraAPI/Patches/Modifiers/EndGameDidWinPatch.cs b/MiraAPI/Patches/Modifiers/EndGameDidWinPatch.cs index a85aefa..d14e17f 100644 --- a/MiraAPI/Patches/Modifiers/EndGameDidWinPatch.cs +++ b/MiraAPI/Patches/Modifiers/EndGameDidWinPatch.cs @@ -33,6 +33,7 @@ internal static class EndGameDidWinPatch { CachedWinners.Add(new CachedPlayerData(networkedPlayerInfo)); } + continue; } diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index aee94ab..38ba561 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -36,7 +36,6 @@ internal static class GameOptionsMenuPatch [HarmonyPrefix] [HarmonyPatch(nameof(GameOptionsMenu.Initialize))] - // ReSharper disable once InconsistentNaming public static bool InitPatch(GameOptionsMenu __instance) { __instance.Children ??= new List(); @@ -101,8 +100,7 @@ internal static class GameOptionsMenuPatch texture, new Rect(0, 0, texture.width, texture.height), Vector2.one / 2, - 100f / scale - ); + 100f / scale); _modIcon.sprite = newSprite; _modIcon.drawMode = SpriteDrawMode.Simple; @@ -122,6 +120,7 @@ internal static class GameOptionsMenuPatch num -= 0.63f; opt.gameObject.SetActive(true); } + foreach (var opt in VanillaOptions) { opt.gameObject.SetActive(true); @@ -135,6 +134,7 @@ internal static class GameOptionsMenuPatch { opt.gameObject.SetActive(false); } + foreach (var opt in VanillaOptions) { opt.gameObject.SetActive(false); @@ -183,6 +183,7 @@ internal static class GameOptionsMenuPatch instance.ControllerSelectable.Add(obj); } } + instance.scrollBar.SetYBoundsMax(-num - 1.65f); } @@ -217,6 +218,7 @@ internal static class GameOptionsMenuPatch { MainOptions.Add(optionBehaviour); } + for (var i = 1; i < GameModeOption.Values.Count; i++) { GameModeOption.OptionBehaviour.Values = @@ -296,12 +298,14 @@ internal static class GameOptionsMenuPatch additionalVanillaScrollNum -= 0.45f; } } + foreach (var optionBehaviour in VanillaOptions) { if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost) { optionBehaviour.SetAsPlayer(); } + instance.Children.Add(optionBehaviour); } @@ -328,6 +332,7 @@ internal static class GameOptionsMenuPatch CreateGroup(instance, group, container, ref newNum, ref optionBehaviours, ref categoryHeaders); group.Ready = true; } + GameModeHeaders.Add(mode, categoryHeaders); GameModeOptions.Add(mode, optionBehaviours); } @@ -337,6 +342,7 @@ internal static class GameOptionsMenuPatch { instance.ControllerSelectable.Add(obj); } + instance.scrollBar.SetYBoundsMax(-num - 1.65f); } @@ -356,13 +362,13 @@ internal static class GameOptionsMenuPatch [HarmonyPostfix] [HarmonyPatch(nameof(GameOptionsMenu.Update))] - // ReSharper disable once InconsistentNaming public static void UpdatePatch(GameOptionsMenu __instance) { if (MenuState.Instance.CurrentModIdx == 0 && GameModeGroups.Count <= 0) { return; } + var num = 2.1f; switch (MenuState.Instance.CurrentMenu) @@ -383,6 +389,7 @@ internal static class GameOptionsMenuPatch GamemodeOptionsUpdate(ref num); break; } + var filteredGroups = MenuState.Instance.CurrentMod.InternalOptionGroups .Where(x => (x.OptionableType == null || CustomGameModeManager.ActiveMode!.GetType().IsAssignableTo(x.OptionableType)) && @@ -398,6 +405,7 @@ internal static class GameOptionsMenuPatch __instance.scrollBar.SetYBoundsMax(-num - 1.65f); } + private static void CreateGroup(GameOptionsMenu menu, AbstractOptionGroup group, Transform container, ref float num, ref System.Collections.Generic.List optionBehaviours, ref System.Collections.Generic.List categoryHeaders) { var categoryHeaderMasked = Object.Instantiate( @@ -569,6 +577,7 @@ internal static class GameOptionsMenuPatch UpdateGroup(group, ref num); } } + private static void CustomMenuOneUpdate(ref float num) { var groups = MenuState.Instance.CurrentMod.InternalOptionGroups @@ -696,14 +705,13 @@ internal static class GameOptionsMenuPatch group.Header = categoryHeaderMasked; } - var options = group.Options.Where(opt => opt.OptionBehaviour == null || !opt.OptionBehaviour - ).Select(opt => opt.CreateOption( - menu.checkboxOrigin, - menu.numberOptionOrigin, - menu.stringOptionOrigin, - menu.playerOptionOrigin, - container) - ); + var options = group.Options.Where(opt => opt.OptionBehaviour == null || !opt.OptionBehaviour) + .Select(opt => opt.CreateOption( + menu.checkboxOrigin, + menu.numberOptionOrigin, + menu.stringOptionOrigin, + menu.playerOptionOrigin, + container)); OptionPreset? defaultPreset = null; if (PresetManager.DefaultPresets.TryGetValue(MenuState.Instance.CurrentMod, out var preset)) diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 54af816..4bf0cf8 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -20,13 +20,13 @@ namespace MiraAPI.Patches.Options; internal static class GamePresetsTabPatches { + private static readonly object Lock = new(); + private static GameObject _saveButton = null!; private static GameObject _refreshButton = null!; private static GameObject _folderButton = null!; private static GameObject _newDivider = null!; - private static readonly object Lock = new(); - [HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.OnEnable))] public static class GamePresetsOnEnablePatch { @@ -57,14 +57,17 @@ internal static class GamePresetsTabPatches { _saveButton.SetActive(MenuState.Instance.CurrentModIdx != 0); } + if (_refreshButton) { _refreshButton.SetActive(MenuState.Instance.CurrentModIdx != 0); } + if (_folderButton) { _folderButton.SetActive(MenuState.Instance.CurrentModIdx != 0); } + if (_newDivider) { _newDivider.SetActive(MenuState.Instance.CurrentModIdx != 0); @@ -123,10 +126,12 @@ internal static class GamePresetsTabPatches { _saveButton.SetActive(false); } + if (_refreshButton) { _refreshButton.SetActive(false); } + if (_folderButton) { _folderButton.SetActive(false); @@ -267,6 +272,7 @@ internal static class GamePresetsTabPatches refreshButton.OnClick = new Button.ButtonClickedEvent(); refreshButton.OnClick.AddListener((UnityAction)Refresh); } + refreshButton = _refreshButton.GetComponent(); if (!_folderButton) @@ -317,6 +323,7 @@ internal static class GamePresetsTabPatches { PresetManager.LoadPresets(MenuState.Instance.CurrentMod); } + GameSettingMenu.Instance.ChangeTab(0, false); // refresh the tab } } diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index d4df499..e6ae2d5 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -37,6 +37,7 @@ internal static class GameSettingMenuPatches { Error("MenuState instance is null. Cannot change tab."); } + return false; } } diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index 09c4a4b..5f5ca9b 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -163,10 +163,10 @@ public static class LobbyViewPanePatches menu.scrollBar.ScrollToTop(); } + // CHANGED BECAUSE OF INLINING [HarmonyPostfix] [HarmonyPatch(nameof(LobbyViewSettingsPane.ChangeTab))] [HarmonyPatch(nameof(LobbyViewSettingsPane.RefreshTab))] - // CHANGED BECAUSE OF INLINING public static void SetTabPatch(LobbyViewSettingsPane __instance) { if (__instance.currentTab != ModifiersTabName || SelectedMod == null) @@ -408,8 +408,7 @@ public static class LobbyViewPanePatches if (viewSettingsInfoPanel.titleText.text.Contains("Game Mode")) { viewSettingsInfoPanel.titleText.text = TranslationController.Instance.GetString( - data.Title, CustomGameModeManager.ActiveMode != null ? $"{CustomGameModeManager.ActiveMode.Name}" : "Classic" - ); + data.Title, CustomGameModeManager.ActiveMode != null ? $"{CustomGameModeManager.ActiveMode.Name}" : "Classic"); } } @@ -598,6 +597,7 @@ public static class LobbyViewPanePatches header.Divider.color = Color.grey; header.Title.color = new Color32(50, 50, 50, 255); } + if (roleIcon != null && header.icon != null) { header.icon.material.SetInt(PlayerMaterial.MaskLayer, maskLayer); diff --git a/MiraAPI/Patches/Options/MenuState.cs b/MiraAPI/Patches/Options/MenuState.cs index e50efa7..611724d 100644 --- a/MiraAPI/Patches/Options/MenuState.cs +++ b/MiraAPI/Patches/Options/MenuState.cs @@ -672,6 +672,7 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr) public void NextMod() { CurrentModIdx++; + // The existence of vanilla settings offsets the off-by-one errors. if (CurrentModIdx > ModCount) CurrentModIdx = 0; CurrentMenu = GetNewPageIfNeeded(); @@ -682,6 +683,7 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr) public void PreviousMod() { CurrentModIdx--; + // The existence of vanilla settings offsets the off-by-one errors. if (CurrentModIdx < 0) CurrentModIdx = ModCount; CurrentMenu = GetNewPageIfNeeded(); diff --git a/MiraAPI/Patches/Options/NotificationPopperPatch.cs b/MiraAPI/Patches/Options/NotificationPopperPatch.cs index 83e22e5..18c7796 100644 --- a/MiraAPI/Patches/Options/NotificationPopperPatch.cs +++ b/MiraAPI/Patches/Options/NotificationPopperPatch.cs @@ -31,11 +31,9 @@ public static class NotificationPopperPatch "", textColor, TranslationController.Instance.GetString(key), - "" - ), + ""), "" + roleCount + "", - "" + roleChance + "%" - ); + "" + roleChance + "%"); __instance.SettingsChangeMessageLogic(key, item, playSound); return false; diff --git a/MiraAPI/Patches/Options/OptionsPatches.cs b/MiraAPI/Patches/Options/OptionsPatches.cs index b6725f9..64d9119 100644 --- a/MiraAPI/Patches/Options/OptionsPatches.cs +++ b/MiraAPI/Patches/Options/OptionsPatches.cs @@ -127,11 +127,13 @@ public static class OptionsPatches numberOption.MinusBtn.SetInteractable(true); numberOption.PlusBtn.SetInteractable(true); } + if (__instance.TryCast() is { } stringOption) { stringOption.MinusBtn.SetInteractable(true); stringOption.PlusBtn.SetInteractable(true); } + if (__instance.TryCast() is { } playerOption) { playerOption.MinusBtn.SetInteractable(true); @@ -168,9 +170,9 @@ public static class OptionsPatches return false; } + // UpdateValue was inlined. [HarmonyPrefix] [HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.Toggle))] - // UpdateValue was inlined. public static bool ToggleUpdate(ToggleOption __instance) { if (!__instance.IsCustom()) @@ -200,6 +202,7 @@ public static class OptionsPatches { return true; } + __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title).Translate(); return false; } @@ -227,6 +230,7 @@ public static class OptionsPatches { __instance.Value = __instance.ValidRange.min; } + __instance.OnValueChanged.Invoke(__instance); __instance.AdjustButtonsActiveState(); return false; @@ -255,6 +259,7 @@ public static class OptionsPatches { __instance.Value = __instance.ValidRange.max; } + __instance.OnValueChanged.Invoke(__instance); __instance.AdjustButtonsActiveState(); return false; @@ -274,6 +279,7 @@ public static class OptionsPatches { __instance.Value = 0; } + __instance.OnValueChanged.Invoke(__instance); __instance.AdjustButtonsActiveState(); return false; @@ -293,6 +299,7 @@ public static class OptionsPatches { __instance.Value = __instance.Values.Length - 1; } + __instance.OnValueChanged.Invoke(__instance); __instance.AdjustButtonsActiveState(); return false; diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 645bf57..c05018c 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -85,13 +85,16 @@ public static class RoleSettingMenuPatches { Object.Destroy(child.gameObject); } + foreach (var child in headers) { Object.Destroy(child.gameObject); } + yield return new WaitForEndOfFrame(); MenuState.Instance.QueuedRoleMenuRefresh[MenuState.Instance.CurrentModIdx] = false; } + if (!queuedRefresh && MenuState.Instance.FinishedRoleMenus.TryGetValue(MenuState.Instance.CurrentModIdx, out var finished) && finished) { var roleOptionSettings = roleMenu.scrollBar.Inner.GetComponentsInChildren(true); @@ -177,10 +180,12 @@ public static class RoleSettingMenuPatches { Object.Destroy(child.gameObject); } + foreach (var child in headers) { Object.Destroy(child.gameObject); } + yield return new WaitForEndOfFrame(); var roleGroups = MenuState.Instance.CurrentMod.InternalRoles.Values.OfType() @@ -351,6 +356,7 @@ public static class RoleSettingMenuPatches } } } + MenuState.Instance.QueuedRoleMenuRefresh[MenuState.Instance.CurrentModIdx] = false; MenuState.Instance.FinishedRoleMenus[MenuState.Instance.CurrentModIdx] = true; } @@ -399,11 +405,13 @@ public static class RoleSettingMenuPatches ControllerManager.Instance.SetDefaultSelection(__instance.ControllerSelectable[0]); } } + var passiveButton = __instance.currentTabButton; if (passiveButton) { passiveButton.SelectButton(false); } + __instance.AllButton.SelectButton(true); __instance.currentTabButton = __instance.AllButton; } @@ -423,8 +431,8 @@ public static class RoleSettingMenuPatches scroller.SetYBoundsMax(-ScrollerNum - 2); } - [HarmonyPrefix] // TODO: turn this into a fixed update + [HarmonyPrefix] [HarmonyPatch(nameof(RolesSettingsMenu.Update))] public static bool UpdatePatch(RolesSettingsMenu __instance) { @@ -549,6 +557,7 @@ public static class RoleSettingMenuPatches var categoryHeaderMasked = __instance.AdvancedRolesSettings.transform.Find("CategoryHeaderMasked").GetComponent(); categoryHeaderMasked.Title.text = TranslationController.Instance.GetString(StringNames.RoleSettingsLabel); var labelBg = __instance.AdvancedRolesSettings.transform.FindChild("InfoLabelBackground"); + // ReSharper disable once StringLiteralTypo (Justification: Actual name in-game.) var imgBg = __instance.AdvancedRolesSettings.transform.FindChild("Imagebackground"); imgBg.gameObject.SetActive(true); diff --git a/MiraAPI/Patches/PlayerControlPatches.cs b/MiraAPI/Patches/PlayerControlPatches.cs index aa47415..791334d 100644 --- a/MiraAPI/Patches/PlayerControlPatches.cs +++ b/MiraAPI/Patches/PlayerControlPatches.cs @@ -20,7 +20,6 @@ internal static class PlayerControlPatches { [HarmonyPostfix] [HarmonyPatch(nameof(PlayerControl.Start))] - // ReSharper disable once InconsistentNaming public static void PlayerControlStartPostfix(PlayerControl __instance) { if (__instance.gameObject.TryGetComponent(out var modifierComp)) @@ -39,7 +38,6 @@ internal static class PlayerControlPatches [HarmonyPostfix] [HarmonyPatch(nameof(PlayerControl.Die))] - // ReSharper disable once InconsistentNaming public static void PlayerControlDiePostfix(PlayerControl __instance, DeathReason reason) { var deathEvent = new PlayerDeathEvent(__instance, reason, Helpers.GetBodyById(__instance.PlayerId)); @@ -55,7 +53,6 @@ internal static class PlayerControlPatches [HarmonyPostfix] [HarmonyPatch(nameof(PlayerControl.CompleteTask))] - // ReSharper disable once InconsistentNaming public static void PlayerCompleteTaskPostfix(PlayerControl __instance, uint idx) { var playerTask = __instance.myTasks.ToArray().First(playerTask => playerTask.Id == idx); @@ -126,7 +123,6 @@ internal static class PlayerControlPatches [HarmonyPostfix] [HarmonyPatch(nameof(PlayerControl.FixedUpdate))] - // ReSharper disable once InconsistentNaming public static void PlayerControlFixedUpdatePostfix(PlayerControl __instance) { if (!__instance.AmOwner) diff --git a/MiraAPI/Patches/RoleGuidePatches.cs b/MiraAPI/Patches/RoleGuidePatches.cs index 053ff09..8fd7ea5 100644 --- a/MiraAPI/Patches/RoleGuidePatches.cs +++ b/MiraAPI/Patches/RoleGuidePatches.cs @@ -63,14 +63,17 @@ public static class RoleGuidePatches { Minigame.Instance.Close(); } + if (MapBehaviour.Instance) { MapBehaviour.Instance.Close(); } + if (HudManager.InstanceExists) { ConsoleJoystick.SetMode_MenuAdditive(); } + ControllerManager.Instance.OpenOverlayMenu("MatchInfoGuide", __instance.closeButton); var enabled = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Joystick; __instance.glyphL.enabled = enabled; @@ -112,6 +115,7 @@ public static class RoleGuidePatches __instance.TabButtons[0].SelectButton(true); __instance.CreateHnSModeSettings(); } + PlayerControl.LocalPlayer.NetTransform.Halt(); __instance.MatchInfoParent.SetActive(true); var instance = ControllerManager.Instance; @@ -244,6 +248,7 @@ public static class RoleGuidePatches return false; } + [HarmonyPrefix] [HarmonyPriority(Priority.First)] [HarmonyPatch(typeof(MatchInfoRolePanel), nameof(MatchInfoRolePanel.SetPanel))] diff --git a/MiraAPI/Patches/Roles/HauntMenuPatch.cs b/MiraAPI/Patches/Roles/HauntMenuPatch.cs index 851ddaa..2c873bf 100644 --- a/MiraAPI/Patches/Roles/HauntMenuPatch.cs +++ b/MiraAPI/Patches/Roles/HauntMenuPatch.cs @@ -8,8 +8,8 @@ namespace MiraAPI.Patches.Roles; /// /// Patches the to show the actual role name rather than team. /// -[HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.SetHauntTarget))] // SetFilterText was INLINED +[HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.SetHauntTarget))] public static class HauntMenuMinigamePatch { public static void Postfix(HauntMenuMinigame __instance) diff --git a/MiraAPI/Patches/Roles/HowToPlayPatch.cs b/MiraAPI/Patches/Roles/HowToPlayPatch.cs index 7cbc023..b2fd4e2 100644 --- a/MiraAPI/Patches/Roles/HowToPlayPatch.cs +++ b/MiraAPI/Patches/Roles/HowToPlayPatch.cs @@ -16,6 +16,7 @@ internal static class HowToPlayPatch { return; } + __instance.sceneIndex = 0; __instance.category = HowToPlayScene.HowToPlayCategory.RolesSelection; __instance.startPage.SetActive(false); @@ -35,16 +36,20 @@ internal static class HowToPlayPatch __instance.controllerSelectables.Add(component.GetComponent()); } } + foreach (var uiElement in __instance.controllerSelectables) { uiElement.ReceiveMouseOut(); } + ControllerManager.Instance.NewScene(__instance.name, __instance.closeButton, __instance.defaultButtonSelected, __instance.controllerSelectables); } + __instance.DisableAllScenes(); __instance.roleSelectionScene.SetActive(true); ControllerManager.Instance.SetDefaultSelection(__instance.defaultButtonSelected); } + public static void OpenRolePage(HowToPlayScene instance, RoleTypes roleType) { instance.category = HowToPlayScene.HowToPlayCategory.Roles; @@ -61,6 +66,7 @@ internal static class HowToPlayPatch } } } + instance.SetupDots(instance.rolesScenes[instance.sceneIndex].rolePages.Count); instance.ChangeScene(0); } diff --git a/MiraAPI/Patches/Roles/KillButtonPatches.cs b/MiraAPI/Patches/Roles/KillButtonPatches.cs index 2d1bbc0..fc32a7a 100644 --- a/MiraAPI/Patches/Roles/KillButtonPatches.cs +++ b/MiraAPI/Patches/Roles/KillButtonPatches.cs @@ -33,6 +33,7 @@ public static class KillButtonPatches { __instance.currentTarget.cosmetics.SetOutline(false, new Nullable(Color.clear)); } + __instance.currentTarget = target; if (__instance.currentTarget) { @@ -40,6 +41,7 @@ public static class KillButtonPatches __instance.SetEnabled(); return false; } + __instance.SetDisabled(); return false; diff --git a/MiraAPI/Patches/Roles/LogicRoleSelectionHnsPatch.cs b/MiraAPI/Patches/Roles/LogicRoleSelectionHnsPatch.cs index 7b0b44e..1a845fc 100644 --- a/MiraAPI/Patches/Roles/LogicRoleSelectionHnsPatch.cs +++ b/MiraAPI/Patches/Roles/LogicRoleSelectionHnsPatch.cs @@ -1,8 +1,8 @@ using System; +using System.Linq; using AmongUs.GameOptions; using HarmonyLib; using Il2CppSystem.Collections.Generic; -using System.Linq; using MiraAPI.Roles; namespace MiraAPI.Patches.Roles; @@ -71,6 +71,7 @@ public static class LogicRoleSelectionHnsPatch newImpostors.Add(networkedPlayerInfo); Info($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}"); } + // Random Seeker else { @@ -83,6 +84,7 @@ public static class LogicRoleSelectionHnsPatch { pseudoRandomList.PickRandom(); } + var networkedPlayerInfo = pseudoRandomList.PickRandom(); players.Remove(networkedPlayerInfo); newImpostors.Add(networkedPlayerInfo); @@ -90,6 +92,7 @@ public static class LogicRoleSelectionHnsPatch Info($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}"); } } + Info($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Guaranteed Assignment"); AddGuaranteedRoles(guaranteedRoles, opts, list); @@ -106,6 +109,7 @@ public static class LogicRoleSelectionHnsPatch default: throw new ArgumentOutOfRangeException(nameof(team), team, null); } + return false; } diff --git a/MiraAPI/Patches/Roles/RoleManagerPatches.cs b/MiraAPI/Patches/Roles/RoleManagerPatches.cs index a77d3cf..82e937f 100644 --- a/MiraAPI/Patches/Roles/RoleManagerPatches.cs +++ b/MiraAPI/Patches/Roles/RoleManagerPatches.cs @@ -13,27 +13,30 @@ namespace MiraAPI.Patches.Roles; [HarmonyPatch(typeof(RoleManager))] public static class RoleManagerPatches { - [HarmonyPrefix] - [HarmonyPatch(nameof(RoleManager.SetRole))] // NOTE: As of 2025.5.20 update, this is INLINED in ONE place: GameManager::ReviveEveryoneFreeplay(void). // Should not affect normal gameplay, but may cause issues in Freeplay. + [HarmonyPrefix] + [HarmonyPatch(nameof(RoleManager.SetRole))] public static bool SetRolePatch(RoleManager __instance, PlayerControl targetPlayer, RoleTypes roleType) { if (!targetPlayer) { return false; } + var data = targetPlayer.Data; if (data == null) { Error("It shouldn't be possible, but " + targetPlayer.name + " still doesn't have PlayerData during role selection."); return false; } + if (data.Role) { data.Role.Deinitialize(targetPlayer); Object.Destroy(data.Role.gameObject); } + var roleBehaviour = Object.Instantiate(__instance.AllRoles.ToArray().First(r => r.Role == roleType), data.gameObject.transform); roleBehaviour.Initialize(targetPlayer); targetPlayer.Data.Role = roleBehaviour; @@ -45,6 +48,7 @@ public static class RoleManagerPatches { targetPlayer.Data.RoleWhenAlive = new Il2CppSystem.Nullable(roleType); } + roleBehaviour.AdjustTasks(targetPlayer); switch (roleBehaviour.IsDead) { diff --git a/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs b/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs index 303ea6b..45a1987 100644 --- a/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs +++ b/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs @@ -24,6 +24,7 @@ public static class RoleOptionsCollectionPatch return true; } } + return false; } diff --git a/MiraAPI/Patches/Roles/SabotageButtonPatch.cs b/MiraAPI/Patches/Roles/SabotageButtonPatch.cs index 2cc026c..8114bfa 100644 --- a/MiraAPI/Patches/Roles/SabotageButtonPatch.cs +++ b/MiraAPI/Patches/Roles/SabotageButtonPatch.cs @@ -56,6 +56,7 @@ public static class SabotageButtonPatch __instance.SetDisabled(); return false; } + __instance.SetEnabled(); return false; } diff --git a/MiraAPI/Patches/Roles/SelectRolesPatch.cs b/MiraAPI/Patches/Roles/SelectRolesPatch.cs index f867901..993454a 100644 --- a/MiraAPI/Patches/Roles/SelectRolesPatch.cs +++ b/MiraAPI/Patches/Roles/SelectRolesPatch.cs @@ -37,10 +37,12 @@ public static class SelectRolesPatch return false; } } + if (!ApiHandlesRoleSelect) { return true; } + CppCollections.List list = new(); AmongUsClient.Instance.GetAllClients(list); List list2 = [.. list.ToArray() @@ -54,6 +56,7 @@ public static class SelectRolesPatch list2.Add(networkedPlayerInfo); } } + var currentGameOptions = GameOptionsManager.Instance.CurrentGameOptions; var adjustedNumImpostors = GameOptionsManager.Instance.CurrentGameOptions.GetAdjustedNumImpostors(list2.Count); AssignRolesForTeam(list2, currentGameOptions, RoleTeamTypes.Impostor, adjustedNumImpostors, RoleTypes.Impostor); @@ -116,6 +119,7 @@ public static class SelectRolesPatch AssignRolesFromList(players, teamMax, list, ref num); } } + private static void AssignRolesFromList(List players, int teamMax, List roleList, ref int rolesAssigned) { while (roleList.Count > 0 && players.Count > 0 && rolesAssigned < teamMax) diff --git a/MiraAPI/Patches/ServerDropdownPatch.cs b/MiraAPI/Patches/ServerDropdownPatch.cs index 2c91560..46a91e5 100644 --- a/MiraAPI/Patches/ServerDropdownPatch.cs +++ b/MiraAPI/Patches/ServerDropdownPatch.cs @@ -44,6 +44,7 @@ public static class ServerDropdownPatch { x += 2; } + var y = -0.55f * (num / 2f); serverListButton.transform.localPosition = new Vector3(x, __instance.y_posButton + y, -1f); serverListButton.transform.localScale = Vector3.one; diff --git a/MiraAPI/Patches/TmpAwakePatch.cs b/MiraAPI/Patches/TmpAwakePatch.cs index 853c557..cb3f223 100644 --- a/MiraAPI/Patches/TmpAwakePatch.cs +++ b/MiraAPI/Patches/TmpAwakePatch.cs @@ -22,6 +22,7 @@ public static class TmpAwakePatch __instance.UpdateMeshPadding(); return; } + __instance.m_spriteAsset.fallbackSpriteAssets.Add(TmpSpriteUtils.AssetHolder); __instance.UpdateMeshPadding(); } diff --git a/MiraAPI/Patches/Voting/MeetingHudPatches.cs b/MiraAPI/Patches/Voting/MeetingHudPatches.cs index a84857c..1239ac7 100644 --- a/MiraAPI/Patches/Voting/MeetingHudPatches.cs +++ b/MiraAPI/Patches/Voting/MeetingHudPatches.cs @@ -66,8 +66,9 @@ internal static class MeetingHudPatches var button = selectableElements[i]; var endPos = 1.3f - 0.65f * i; var duration = 0.25f + 0.1f * i; - __instance.StartCoroutine(Effects.All(Effects.Lerp(duration, (Action)(t => - button.transform.localPosition = Vector2.Lerp(Vector2.right * startPos, Vector2.right * endPos, Effects.ExpOut(t)))))); + __instance.StartCoroutine(Effects.All(Effects.Lerp( + duration, + (Action)(t => button.transform.localPosition = Vector2.Lerp(Vector2.right * startPos, Vector2.right * endPos, Effects.ExpOut(t)))))); } ControllerManager.Instance.OpenOverlayMenu(__instance.name, __instance.CancelButton, selectableElements[1], selectableElements); @@ -121,6 +122,7 @@ internal static class MeetingHudPatches meetingAbility.Button!.SetDisabled(); } } + MiraEventManager.InvokeEvent(new VotingCompleteEvent(__instance)); } @@ -254,7 +256,7 @@ internal static class MeetingHudPatches var voterStates = new Il2CppStructArray([ .. votes.Select( - v=> new MeetingHud.VoterState + v => new MeetingHud.VoterState { VoterId = v.Voter, VotedForId = v.Suspect, @@ -276,9 +278,9 @@ internal static class MeetingHudPatches } // TODO: figure out a way to do host-authorization since right now any player can send RpcCastVote + // Although this method is inlined in MeetingHud.Confirm, the next patch fixes that. [HarmonyPrefix] [HarmonyPatch(nameof(MeetingHud.CmdCastVote))] - // Although this method is inlined in MeetingHud.Confirm, the next patch fixes that. public static bool CmdCastVoteOverridePatch(byte playerId, byte suspectIdx) { VotingUtils.RpcCastVote(PlayerControl.LocalPlayer, playerId, suspectIdx); @@ -286,9 +288,9 @@ internal static class MeetingHudPatches } // TODO: figure out a way to do host-authorization since right now any player can send RpcQueueOverruleVotes + // Although this method is inlined in MeetingHud.Confirm, the next patch fixes that. [HarmonyPrefix] [HarmonyPatch(nameof(MeetingHud.CmdQueueOverruleVotes))] - // Although this method is inlined in MeetingHud.Confirm, the next patch fixes that. public static bool CmdQueueOverruleVotesPatch(PlayerId judgePlayerId, PlayerId targetPlayerId, ushort overruleNonce) { VotingUtils.RpcQueueOverruleVotes(PlayerControl.LocalPlayer, judgePlayerId.Value, targetPlayerId.Value, overruleNonce); diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 46758e5..a3b5158 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -356,6 +356,7 @@ public sealed class MiraPluginManager { Error($"Failed to register options for {type.Name}: {e.ToString()}"); } + return false; } @@ -382,6 +383,7 @@ public sealed class MiraPluginManager { Error($"Failed to register role for {type.Name}: {e}"); } + return false; } @@ -465,6 +467,7 @@ public sealed class MiraPluginManager { Error($"Failed to register gamemode {type.Name}: {e}"); } + return false; } @@ -601,6 +604,7 @@ public sealed class MiraPluginManager return false; } } + private static bool RegisterTargetedMeetingAbility(Type type, MiraPluginInfo info) { try diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs index 9ac9a1b..42f0a07 100644 --- a/MiraAPI/Presets/PresetManager.cs +++ b/MiraAPI/Presets/PresetManager.cs @@ -103,6 +103,7 @@ public static class PresetManager plugin.InternalPresets.Add(new OptionPreset(presetName, plugin, presetConfig)); } + plugin.Presets = [.. plugin.InternalPresets]; } @@ -159,6 +160,7 @@ public static class PresetManager // InternalMasterPresets.Add(new OptionPreset(presetName, plugin, presetConfig)); } + MasterPresets = [.. InternalMasterPresets]; } } diff --git a/MiraAPI/Presets/SavePresetPopup.cs b/MiraAPI/Presets/SavePresetPopup.cs index 5b93ab6..9fce9f9 100644 --- a/MiraAPI/Presets/SavePresetPopup.cs +++ b/MiraAPI/Presets/SavePresetPopup.cs @@ -9,10 +9,10 @@ using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; -[RegisterInIl2Cpp] +namespace MiraAPI.Presets; + // TODO: Give proper reasoning -[SuppressMessage("Design", "CA1050:Declare types in namespaces", Justification = "Reason pending.")] -[SuppressMessage("Major Bug", "S3903:Types should be defined in named namespaces", Justification = "Reason pending.")] +[RegisterInIl2Cpp] [SuppressMessage("Style", "IDE0051:Remove unused private members", Justification = "Unity Convention.")] #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member (Justification: Unity fields; ignore.) public class SavePresetPopup(nint cppPtr) : Minigame(cppPtr) diff --git a/MiraAPI/Roles/CustomRoleConfiguration.cs b/MiraAPI/Roles/CustomRoleConfiguration.cs index b0f2d4a..46fc3bd 100644 --- a/MiraAPI/Roles/CustomRoleConfiguration.cs +++ b/MiraAPI/Roles/CustomRoleConfiguration.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using AmongUs.GameOptions; using Il2CppInterop.Runtime.Attributes; using MiraAPI.GameModes; @@ -6,7 +7,6 @@ using MiraAPI.Patches.Freeplay; using MiraAPI.Utilities.Assets; using TMPro; using UnityEngine; -using System.Diagnostics.CodeAnalysis; namespace MiraAPI.Roles; @@ -15,7 +15,7 @@ namespace MiraAPI.Roles; /// public record struct CustomRoleConfiguration { - [Obsolete("Default constructor is not supported. Please use the constructor that takes an ICustomRole parameter.")] + [Obsolete("Default constructor is not supported. Please use the constructor that takes an ICustomRole parameter.", true)] [SuppressMessage("Info Code Smell", "S1133:Deprecated code should be removed", Justification = "Will not be removed, as this throws instead.")] #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member (Justification: Read above.) public CustomRoleConfiguration() diff --git a/MiraAPI/Roles/CustomRoleSingleton.cs b/MiraAPI/Roles/CustomRoleSingleton.cs index fe69eb0..0addc6f 100644 --- a/MiraAPI/Roles/CustomRoleSingleton.cs +++ b/MiraAPI/Roles/CustomRoleSingleton.cs @@ -7,7 +7,8 @@ namespace MiraAPI.Roles; /// A utility class to get the instance of a custom role. /// /// The you are trying to access. -public static class CustomRoleSingleton where T : ICustomRole +public static class CustomRoleSingleton + where T : ICustomRole { private static T? _instance; diff --git a/MiraAPI/Roles/CustomRoleUtils.cs b/MiraAPI/Roles/CustomRoleUtils.cs index 1487d20..c5b1feb 100644 --- a/MiraAPI/Roles/CustomRoleUtils.cs +++ b/MiraAPI/Roles/CustomRoleUtils.cs @@ -108,7 +108,8 @@ public static class CustomRoleUtils /// /// The you would like to check for. /// An of s. - public static IEnumerable GetActiveRolesOfType() where T : RoleBehaviour + public static IEnumerable GetActiveRolesOfType() + where T : RoleBehaviour { return GetActiveRoles().OfType(); } diff --git a/MiraAPI/Roles/RoleId.cs b/MiraAPI/Roles/RoleId.cs index 60c7734..3dd06b5 100644 --- a/MiraAPI/Roles/RoleId.cs +++ b/MiraAPI/Roles/RoleId.cs @@ -1,5 +1,5 @@ -using AmongUs.GameOptions; -using System; +using System; +using AmongUs.GameOptions; namespace MiraAPI.Roles; @@ -14,7 +14,8 @@ public static class RoleId /// The type of the role. /// The role ID as a ushort. /// Thrown when the role is not registered. - public static ushort Get() where T : ICustomRole + public static ushort Get() + where T : ICustomRole { return !CustomRoleManager.RoleIds.TryGetValue(typeof(T), out var roleId) ? throw new InvalidOperationException($"Role {typeof(T)} is not registered") @@ -51,7 +52,8 @@ public static class RoleId /// The type of the role. /// The role ID as a . /// Thrown when the role is not registered. - public static RoleTypes GetType() where T : ICustomRole + public static RoleTypes GetType() + where T : ICustomRole { return (RoleTypes)Get(); } @@ -73,7 +75,8 @@ public static class RoleId /// The type of the role. /// The object. /// Thrown when the role is not registered. - public static RoleBehaviour GetRole() where T : ICustomRole + public static RoleBehaviour GetRole() + where T : ICustomRole { return RoleManager.Instance.GetRole(GetType()); } diff --git a/MiraAPI/Translation/MiraLocaleManager.cs b/MiraAPI/Translation/MiraLocaleManager.cs index 73de3af..d26329b 100644 --- a/MiraAPI/Translation/MiraLocaleManager.cs +++ b/MiraAPI/Translation/MiraLocaleManager.cs @@ -402,9 +402,11 @@ public static class MiraLocaleManager { Error($"String for \"{name}\" in {language} was overwritten by duplicate!"); } + dict[name] = value; total++; } + Info($"Loaded {language.ToDisplayString()} translation with ({total} keys)"); } catch (XmlException ex) diff --git a/MiraAPI/Translation/MiraLocalizationProvider.cs b/MiraAPI/Translation/MiraLocalizationProvider.cs index 4c99933..4e757a0 100644 --- a/MiraAPI/Translation/MiraLocalizationProvider.cs +++ b/MiraAPI/Translation/MiraLocalizationProvider.cs @@ -25,6 +25,7 @@ public class MiraLocalizationProvider : LocalizationProvider result = MiraLocaleManager.Get(key); return true; } + result = null; return false; } @@ -47,6 +48,7 @@ public class MiraLocalizationProvider : LocalizationProvider { MiraApiPlugin.Culture = new(culture); } + if (!_loadedStrings) { MiraLocaleManager.LoadExternalLocale(); @@ -62,6 +64,7 @@ public class MiraLocalizationProvider : LocalizationProvider tab.Key.text = $"{LocalSettingsTab.GetShortName(tab.Value.Translate())}"; } + /*Warning($""); Warning($""); foreach (var stringName in TranslationController.Instance.currentLanguage.AllStrings) diff --git a/MiraAPI/Utilities/Assets/AddressablesLoader.cs b/MiraAPI/Utilities/Assets/AddressablesLoader.cs index aefc5cb..39626e9 100644 --- a/MiraAPI/Utilities/Assets/AddressablesLoader.cs +++ b/MiraAPI/Utilities/Assets/AddressablesLoader.cs @@ -1,9 +1,9 @@ -using Il2CppInterop.Runtime.Attributes; -using MiraAPI.Patches.Menu; -using Reactor.Utilities; -using System.Collections; +using System.Collections; using System.Collections.Generic; using System.Linq; +using Il2CppInterop.Runtime.Attributes; +using MiraAPI.Patches.Menu; +using Reactor.Utilities; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; @@ -14,8 +14,6 @@ namespace MiraAPI.Utilities.Assets; /// public static class AddressablesLoader { - private static bool _isInitialized; - private static readonly List<(string Location, string ProviderSuffix)> CatalogLocations = []; private static readonly List LoadedLocations = []; @@ -24,6 +22,8 @@ public static class AddressablesLoader private static readonly List<(string, string)> RegisteredNameplateKeys = []; private static readonly List RegisteredSkinKeys = []; + private static bool _isInitialized; + /// /// Gets a value indicating whether hats have been loaded by the addressables system. /// @@ -146,6 +146,7 @@ public static class AddressablesLoader internal static IEnumerator CoLoadAddressables(string location, string suffix = "") { while (!AmongUsClient.Instance) yield return null; + // Load the local/remote content catalog var catalogOperation = Addressables.LoadContentCatalog(location, suffix); yield return catalogOperation; @@ -195,7 +196,8 @@ public static class AddressablesLoader HatManager.Instance.allNamePlates = PrepareArray(namePlateData, [.. namePlateBehaviours.Select(x => x.Data)]); } - private static T[] PrepareArray(List data, List behaviours) where T : CosmeticData + private static T[] PrepareArray(List data, List behaviours) + where T : CosmeticData { var count = data.Count; for (var i = 0; i < behaviours.Count; i++) @@ -203,6 +205,7 @@ public static class AddressablesLoader behaviours[i].displayOrder = count + i; data.Add(behaviours[i]); } + return [.. data]; } @@ -224,6 +227,7 @@ public static class AddressablesLoader Error($"Failed to find tag {tag}"); } } + return behaviours; } @@ -245,6 +249,7 @@ public static class AddressablesLoader Error($"Failed to find tag {tag}"); } } + return behaviours; } } diff --git a/MiraAPI/Utilities/Assets/LoadableAddressableAsset.cs b/MiraAPI/Utilities/Assets/LoadableAddressableAsset.cs index 144f608..d72e77b 100644 --- a/MiraAPI/Utilities/Assets/LoadableAddressableAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableAddressableAsset.cs @@ -8,7 +8,8 @@ namespace MiraAPI.Utilities.Assets; /// /// The guid of the asset. /// The type of the asset to be loaded. -public class LoadableAddressableAsset(string uid) : LoadableAsset where T : UnityEngine.Object +public class LoadableAddressableAsset(string uid) : LoadableAsset + where T : UnityEngine.Object { private readonly Action>? _gcAction; @@ -18,7 +19,8 @@ public class LoadableAddressableAsset(string uid) : LoadableAsset where T /// /// The guid of the asset. /// A lambda that allows GCHandle.Alloc calls without garbage collection interferences. - public LoadableAddressableAsset(string uid, Action> garbageCollection) : this(uid) + public LoadableAddressableAsset(string uid, Action> garbageCollection) + : this(uid) { _gcAction = garbageCollection; } diff --git a/MiraAPI/Utilities/Assets/LoadableAddressableAssets.cs b/MiraAPI/Utilities/Assets/LoadableAddressableAssets.cs index cd4dccb..9e131bf 100644 --- a/MiraAPI/Utilities/Assets/LoadableAddressableAssets.cs +++ b/MiraAPI/Utilities/Assets/LoadableAddressableAssets.cs @@ -10,7 +10,8 @@ namespace MiraAPI.Utilities.Assets; /// A utility class for loading groups of assets from ALL addressable locations. /// /// The type of the asset to be loaded. -public class LoadableAddressableAssets(string key) where T : UnityEngine.Object +public class LoadableAddressableAssets(string key) + where T : UnityEngine.Object { private readonly Action>? _gcAction; @@ -25,7 +26,8 @@ public class LoadableAddressableAssets(string key) where T : UnityEngine.Obje /// /// The key of the assets. /// A lambda that allows GCHandle.Alloc calls without garbage collection interferences. - public LoadableAddressableAssets(string key, Action> garbageCollection) : this(key) + public LoadableAddressableAssets(string key, Action> garbageCollection) + : this(key) { _gcAction = garbageCollection; } diff --git a/MiraAPI/Utilities/Assets/LoadableAsset.cs b/MiraAPI/Utilities/Assets/LoadableAsset.cs index 2c7f7a3..28c7709 100644 --- a/MiraAPI/Utilities/Assets/LoadableAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableAsset.cs @@ -8,7 +8,8 @@ namespace MiraAPI.Utilities.Assets; /// You can create your own implementation of this class to load assets in different ways. /// /// The type of the asset to be loaded. -public abstract class LoadableAsset where T : UnityEngine.Object +public abstract class LoadableAsset + where T : UnityEngine.Object { /// /// Gets or sets reference to the loaded asset. Intended to be used for caching purposes. diff --git a/MiraAPI/Utilities/Assets/LoadableAssetWrapper.cs b/MiraAPI/Utilities/Assets/LoadableAssetWrapper.cs index a368492..af73288 100644 --- a/MiraAPI/Utilities/Assets/LoadableAssetWrapper.cs +++ b/MiraAPI/Utilities/Assets/LoadableAssetWrapper.cs @@ -6,7 +6,8 @@ namespace MiraAPI.Utilities.Assets; /// Wrapper class to turn any into a . /// /// The type of the asset to be loaded. Must be a subclass of . -public class LoadableAssetWrapper : LoadableAsset where T : UnityEngine.Object +public class LoadableAssetWrapper : LoadableAsset + where T : UnityEngine.Object { /// /// Initializes a new instance of the class. diff --git a/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs index a9b8eb1..1bb75ae 100644 --- a/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs @@ -63,6 +63,7 @@ public class LoadableAudioResourceAsset(string path) : LoadableAsset if (audioFormat != 1) // Only PCM supported throw new NotSupportedException("Only PCM WAV files are supported."); } + // Data chunk else if (chunkId == "data") { diff --git a/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs b/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs index 99a9c4c..49aaf0d 100644 --- a/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs @@ -11,7 +11,8 @@ namespace MiraAPI.Utilities.Assets; /// The name of the asset. /// The that contains the asset. /// The type of the asset to be loaded. -public class LoadableBundleAsset(string name, AssetBundle bundle) : LoadableAsset where T : UnityEngine.Object +public class LoadableBundleAsset(string name, AssetBundle bundle) : LoadableAsset + where T : UnityEngine.Object { /// /// Loads the asset from the . diff --git a/MiraAPI/Utilities/Assets/LoadableBundleSubAssetHolder.cs b/MiraAPI/Utilities/Assets/LoadableBundleSubAssetHolder.cs index 19f968c..9442875 100644 --- a/MiraAPI/Utilities/Assets/LoadableBundleSubAssetHolder.cs +++ b/MiraAPI/Utilities/Assets/LoadableBundleSubAssetHolder.cs @@ -93,6 +93,7 @@ public class LoadableBundleSubAssetHolder { sprite.DestroyImmediate(); } + SubSprites = []; return true; } diff --git a/MiraAPI/Utilities/Assets/PreloadedAsset.cs b/MiraAPI/Utilities/Assets/PreloadedAsset.cs index cf4ef7c..fe380f8 100644 --- a/MiraAPI/Utilities/Assets/PreloadedAsset.cs +++ b/MiraAPI/Utilities/Assets/PreloadedAsset.cs @@ -7,7 +7,8 @@ namespace MiraAPI.Utilities.Assets; /// /// /// -public class PreloadedAsset(T asset) : LoadableAsset where T : UnityEngine.Object +public class PreloadedAsset(T asset) : LoadableAsset + where T : UnityEngine.Object { private readonly T _loadedAsset = asset ?? throw new ArgumentNullException(nameof(asset)); diff --git a/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs b/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs index 3cc8ffc..435e250 100644 --- a/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs +++ b/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs @@ -11,8 +11,8 @@ namespace MiraAPI.Utilities.Assets; /// public static class TmpSpriteUtils { - private static Shader _spriteShader; private static readonly Dictionary LoadedSprites = []; + private static Shader _spriteShader; [SuppressMessage("Critical Code Smell", "S2223:Non-constant static fields should not be visible", Justification = "Internal behaviour that does not need property-level validation.")] [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Read above.")] @@ -67,6 +67,7 @@ public static class TmpSpriteUtils name = assetName + " Material", }; material.SetTexture(ShaderUtilities.ID_MainTex, sourceTexture); + // TODO: Using these values, the icons will NOT clip through chat boxes. However, this breaks quite literally every other tmp text that isn't masked. Please fix this if a good solution is found. /*material.SetFloat(ShaderUtilities.ID_StencilComp, 4); material.SetFloat(ShaderUtilities.ID_StencilID, 1);*/ diff --git a/MiraAPI/Utilities/ColorExtensions.cs b/MiraAPI/Utilities/ColorExtensions.cs index 4d83852..b077770 100644 --- a/MiraAPI/Utilities/ColorExtensions.cs +++ b/MiraAPI/Utilities/ColorExtensions.cs @@ -93,6 +93,7 @@ public static class ColorExtensions low = t; } } + return bestColor; } } diff --git a/MiraAPI/Utilities/ControllableComparer.cs b/MiraAPI/Utilities/ControllableComparer.cs index 4881ab0..49c4b0e 100644 --- a/MiraAPI/Utilities/ControllableComparer.cs +++ b/MiraAPI/Utilities/ControllableComparer.cs @@ -5,7 +5,8 @@ using System.Linq; namespace MiraAPI.Utilities; -internal sealed class ControllableComparer(T[] forcedToBottom, T[] forcedToTop, IComparer fallbackComparer) : IComparer where T : IComparable +internal sealed class ControllableComparer(T[] forcedToBottom, T[] forcedToTop, IComparer fallbackComparer) : IComparer + where T : IComparable { /// [SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "Warning cascades into forcing the entire tree to be ternary operators.")] diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 242c0e8..57cd64b 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -59,18 +59,22 @@ public static class Extensions self.gameObject.Destroy(); return; } + if (self.CloseSound && Constants.ShouldPlaySfx()) { SoundManager.Instance.PlaySound(self.CloseSound, false); } + if (PlayerControl.LocalPlayer.Data.Role.TeamType == RoleTeamTypes.Crewmate) { GameManager.Instance.LogicMinigame.OnMinigameClose(); } + if (PlayerControl.LocalPlayer) { PlayerControl.HideCursorTemporarily(); } + self.amClosing = Minigame.CloseState.Closing; self.logger.Info(string.Concat("Closing minigame ", self.GetType().Name)); var analytics = DebugAnalytics.Instance.Analytics; @@ -101,6 +105,7 @@ public static class Extensions button.cooldownTimerText.gameObject.SetActive(true); return; } + button.cooldownTimerText.gameObject.SetActive(false); } @@ -125,6 +130,7 @@ public static class Extensions { button.graphic.transform.localPosition = button.position; } + button.SetCooldownFill(num); } @@ -222,6 +228,7 @@ public static class Extensions public static void DeepDestroy(this GameObject obj, bool clearGc = true) { obj.Destroy(); + // Nuke(obj, clearGc); } @@ -266,9 +273,12 @@ public static class Extensions // Left as is intentionally, did you guys mean to use it in DeepDestroy? [SuppressMessage("Style", "IDE0051:Remove unused private members", Justification = "Not known until intent it clear.")] [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unclear, but please shut up.")] + +#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppressions.) // ReSharper disable once UnusedParameter.Local (Justification: Read above.) // ReSharper disable once UnusedMember.Local (Justification: Read above.) private static IEnumerator Nuke(GameObject? go, bool clearGc) +#pragma warning restore SA1515 // Single-line comment should be preceded by blank line { if (go == null) yield break; @@ -477,6 +487,7 @@ public static class Extensions tie = true; } } + return result; } @@ -502,6 +513,7 @@ public static class Extensions tie = true; } } + return result; } @@ -571,6 +583,7 @@ public static class Extensions type = type.BaseType; distance++; } + return type == to ? distance : int.MaxValue; } @@ -747,7 +760,8 @@ public static class Extensions float radius, ContactFilter2D filter, string? colliderTag = null, - Predicate? predicate = null) where T : Component + Predicate? predicate = null) + where T : Component { return Helpers.GetNearestObjectsOfType(playerControl.GetTruePosition(), radius, filter, colliderTag) .Find(predicate ?? (component => component)); diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index d8817e4..0d37e5d 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -179,7 +179,8 @@ public static class Helpers /// The position of where you want to check from. For example: PlayerControl.LocalPlayer.transform.position. /// The object type. /// The closest object. - public static T? FindClosestObjectOfType(List objectList, Vector3 position) where T : MonoBehaviour + public static T? FindClosestObjectOfType(List objectList, Vector3 position) + where T : MonoBehaviour { T? closest = null; var closestDistanceSqr = Mathf.Infinity; @@ -388,6 +389,7 @@ public static class Helpers Constants.ShipAndObjectsMask)) select playerControl); +#pragma warning disable SA1008 // Opening parenthesis should be spaced correctly (Justification: Formatting.) outputList.Sort( delegate (PlayerControl a, PlayerControl b) { @@ -399,6 +401,7 @@ public static class Helpers ? -1 : 0; }); +#pragma warning restore SA1008 // Opening parenthesis should be spaced correctly return outputList; } diff --git a/MiraAPI/Utilities/StateMachineWrapper.cs b/MiraAPI/Utilities/StateMachineWrapper.cs index 1321ec1..6b9d683 100644 --- a/MiraAPI/Utilities/StateMachineWrapper.cs +++ b/MiraAPI/Utilities/StateMachineWrapper.cs @@ -11,7 +11,8 @@ namespace MiraAPI.Utilities; /// A wrapper for state machine objects to access their parent instance and state. /// /// The type of the parent class that owns the state machine. -public class StateMachineWrapper where T : Il2CppObjectBase +public class StateMachineWrapper + where T : Il2CppObjectBase { private readonly Il2CppObjectBase _stateMachine; diff --git a/MiraAPI/VanillaEvents/UiResetEvents.cs b/MiraAPI/VanillaEvents/UiResetEvents.cs index 8c18edb..84d0022 100644 --- a/MiraAPI/VanillaEvents/UiResetEvents.cs +++ b/MiraAPI/VanillaEvents/UiResetEvents.cs @@ -24,10 +24,12 @@ internal static class UiResetEvents { wikiButton.transform.SetParent(null); } + if (subButton) { subButton.transform.SetParent(null); } + if (modDisplay) { modDisplay.transform.SetParent(null); @@ -41,6 +43,7 @@ internal static class UiResetEvents { return; } + var firstRow = @event.MainTopUiRow; var secondRow = @event.SecondTopUiRow; var opts = LocalSettingsTabSingleton.Instance; @@ -54,6 +57,7 @@ internal static class UiResetEvents { return; } + var secondRow = @event.SecondTopUiRow; subButton.transform.SetParent(secondRow.transform); } @@ -65,6 +69,7 @@ internal static class UiResetEvents { return; } + var secondRow = @event.SecondTopUiRow; modDisplay.transform.SetParent(secondRow.transform); } diff --git a/MiraAPI/Voting/VotingUtils.cs b/MiraAPI/Voting/VotingUtils.cs index 4edda79..3d3e206 100644 --- a/MiraAPI/Voting/VotingUtils.cs +++ b/MiraAPI/Voting/VotingUtils.cs @@ -71,6 +71,7 @@ public static class VotingUtils { voteData.DecreaseRemainingVotes(1); } + voteData.VoteForPlayer(suspectIdx); } @@ -121,8 +122,7 @@ public static class VotingUtils [SuppressMessage( "Style", "IDE0060:Remove unused parameter", - Justification = "Required parameter: The MethodRpc system mandates an InnerNetObject (or its derived class) as the first parameter for sender context, even if unused in the method body." - )] + Justification = "Required parameter: The MethodRpc system mandates an InnerNetObject (or its derived class) as the first parameter for sender context, even if unused in the method body.")] public static void RpcQueueOverruleVotes(PlayerControl source, byte srcPlayerId, byte suspectPlayerId, ushort overruleNonce) { CustomCastJudgeVote(srcPlayerId, suspectPlayerId, overruleNonce); @@ -192,8 +192,7 @@ public static class VotingUtils [SuppressMessage( "Style", "IDE0060:Remove unused parameter", - Justification = "Required parameter: The MethodRpc system mandates an InnerNetObject (or its derived class) as the first parameter for sender context, even if unused in the method body." - )] + Justification = "Required parameter: The MethodRpc system mandates an InnerNetObject (or its derived class) as the first parameter for sender context, even if unused in the method body.")] public static void RpcCastVote(PlayerControl source, byte srcPlayerId, byte suspectPlayerId) { CustomCastVote(srcPlayerId, suspectPlayerId); diff --git a/packages/AmongUs.GameLibs.Steam.2025.10.14.nupkg b/packages/AmongUs.GameLibs.Steam.2025.10.14.nupkg deleted file mode 100644 index f693dda..0000000 Binary files a/packages/AmongUs.GameLibs.Steam.2025.10.14.nupkg and /dev/null differ diff --git a/stylecop.ruleset b/stylecop.ruleset deleted file mode 100644 index 73b4f35..0000000 --- a/stylecop.ruleset +++ /dev/null @@ -1,219 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file