From e22032e1fbe2d0cf56b65ace0b83a045fc7b327f Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:53:09 -0400 Subject: [PATCH 01/36] fix neutral naming from outcast --- MiraAPI/Patches/IntroCutscenePatches.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MiraAPI/Patches/IntroCutscenePatches.cs b/MiraAPI/Patches/IntroCutscenePatches.cs index 01066c3..3dab1ed 100644 --- a/MiraAPI/Patches/IntroCutscenePatches.cs +++ b/MiraAPI/Patches/IntroCutscenePatches.cs @@ -19,7 +19,7 @@ public static void BeginImpostorPatch(IntroCutscene __instance) __instance.TeamTitle.text = $"{mode.Name}\n{mode.Description}"; } }*/ - + [HarmonyPrefix] [HarmonyPatch(nameof(IntroCutscene.BeginCrewmate))] public static bool BeginCrewmatePatch(IntroCutscene __instance) @@ -37,9 +37,9 @@ public static bool BeginCrewmatePatch(IntroCutscene __instance) barTransform.position = position; __instance.BackgroundBar.material.SetColor(ShaderID.Color, Color.gray); - __instance.TeamTitle.text = "OUTCAST"; + __instance.TeamTitle.text = "NEUTRAL"; __instance.impostorScale = 1f; - __instance.ImpostorText.text = "You are an Outcast. You do not have a team."; + __instance.ImpostorText.text = "You are Neutral. You do not have a team."; __instance.TeamTitle.color = Color.gray; __instance.ourCrewmate = __instance.CreatePlayer(0, Mathf.CeilToInt(7.5f), PlayerControl.LocalPlayer.Data, false); From 2b71438cd02c71ed1e7f8c42deaef6e477a08617 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:53:33 -0400 Subject: [PATCH 02/36] fix button reset after meeting --- .../ButtonResetPatches.cs} | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) rename MiraAPI/Patches/{MeetingHudPatches.cs => Hud/ButtonResetPatches.cs} (56%) diff --git a/MiraAPI/Patches/MeetingHudPatches.cs b/MiraAPI/Patches/Hud/ButtonResetPatches.cs similarity index 56% rename from MiraAPI/Patches/MeetingHudPatches.cs rename to MiraAPI/Patches/Hud/ButtonResetPatches.cs index 6abe4c0..b9756ce 100644 --- a/MiraAPI/Patches/MeetingHudPatches.cs +++ b/MiraAPI/Patches/Hud/ButtonResetPatches.cs @@ -1,20 +1,20 @@ using HarmonyLib; using MiraAPI.Hud; -namespace MiraAPI.Patches; +namespace MiraAPI.Patches.Hud; /// -/// Harmony patches for the MeetingHud class. +/// Reset button patches /// -[HarmonyPatch(typeof(MeetingHud))] -public static class MeetingHudPatches +[HarmonyPatch] +public static class ButtonResetPatches { /// /// Resets the cooldown and effect of all custom buttons when the meeting starts. /// [HarmonyPostfix] - [HarmonyPatch(nameof(MeetingHud.Start))] - public static void StartPostfix() + [HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.Start))] + public static void MeetingHudStartPostfix() { foreach (var customActionButton in CustomButtonManager.CustomButtons) { @@ -23,11 +23,11 @@ public static void StartPostfix() } /// - /// Resets the cooldown and effect of all custom buttons when the voting is complete. + /// Resets the cooldown and effect of all custom buttons after the exile screen is closed. /// [HarmonyPostfix] - [HarmonyPatch(nameof(MeetingHud.VotingComplete))] - public static void VotingCompletePostfix() + [HarmonyPatch(typeof(ExileController), nameof(ExileController.ReEnableGameplay))] + public static void ExileControllerReEnableGameplayPostfix() { foreach (var customActionButton in CustomButtonManager.CustomButtons) { From aa72770bfc864d43f77d452d24773e512d209d3a Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:06:35 -0400 Subject: [PATCH 03/36] change vent color for custom roles --- MiraAPI/Patches/Roles/VentOutlinePatch.cs | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 MiraAPI/Patches/Roles/VentOutlinePatch.cs diff --git a/MiraAPI/Patches/Roles/VentOutlinePatch.cs b/MiraAPI/Patches/Roles/VentOutlinePatch.cs new file mode 100644 index 0000000..401d450 --- /dev/null +++ b/MiraAPI/Patches/Roles/VentOutlinePatch.cs @@ -0,0 +1,24 @@ +using HarmonyLib; +using MiraAPI.Roles; +using MiraAPI.Utilities; +using UnityEngine; + +namespace MiraAPI.Patches.Roles; + +[HarmonyPatch(typeof(Vent), nameof(Vent.SetOutline))] +public static class VentOutlinePatch +{ + public static void Postfix(Vent __instance, bool on, bool mainTarget) + { + if (PlayerControl.LocalPlayer.Data.Role is not ICustomRole customRole) + { + return; + } + + var color = customRole.RoleColor; + + __instance.myRend.material.SetFloat(ShaderID.Outline, on ? 1 : 0); + __instance.myRend.material.SetColor(ShaderID.OutlineColor, color); + __instance.myRend.material.SetColor(ShaderID.AddColor, mainTarget ? color : Color.clear); + } +} From 74ab8c5581fddc9d07ab5ea63d4a6968546c9a8e Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:06:41 -0400 Subject: [PATCH 04/36] oops --- MiraAPI/Roles/RoleHintType.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MiraAPI/Roles/RoleHintType.cs b/MiraAPI/Roles/RoleHintType.cs index 8994016..f3d5cc6 100644 --- a/MiraAPI/Roles/RoleHintType.cs +++ b/MiraAPI/Roles/RoleHintType.cs @@ -1,6 +1,4 @@ -// Copyright (c) PlaceholderCompany. All rights reserved. - -namespace MiraAPI.Roles; +namespace MiraAPI.Roles; /// /// The type of hint style for a role to use. From a07c332dce2e22e6ef04e9e4d9b82c91af762c41 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:07:22 -0400 Subject: [PATCH 05/36] add DeathReason to modifier OnDeath method --- MiraAPI/Modifiers/BaseModifier.cs | 3 ++- MiraAPI/Patches/PlayerControlPatches.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index af9b491..b91c078 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -62,7 +62,8 @@ public virtual void FixedUpdate() /// /// Called when the player dies. /// - public virtual void OnDeath() + /// The Death Reason. + public virtual void OnDeath(DeathReason reason) { } diff --git a/MiraAPI/Patches/PlayerControlPatches.cs b/MiraAPI/Patches/PlayerControlPatches.cs index 7d4d97a..b9bc0f2 100644 --- a/MiraAPI/Patches/PlayerControlPatches.cs +++ b/MiraAPI/Patches/PlayerControlPatches.cs @@ -32,13 +32,13 @@ public static void PlayerControlStartPostfix(PlayerControl __instance) /// [HarmonyPostfix] [HarmonyPatch(nameof(PlayerControl.Die))] - public static void PlayerControlDiePostfix(PlayerControl __instance) + public static void PlayerControlDiePostfix(PlayerControl __instance, DeathReason reason) { var modifiersComponent = __instance.GetComponent(); if (modifiersComponent) { - modifiersComponent.ActiveModifiers.ForEach(x=>x.OnDeath()); + modifiersComponent.ActiveModifiers.ForEach(x=>x.OnDeath(reason)); } } From 0f26cadd9af89e7fe89932efbabe262f8618c7c6 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:07:36 -0400 Subject: [PATCH 06/36] clean up OptionGroupSingleton.cs --- MiraAPI/GameOptions/OptionGroupSingleton.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MiraAPI/GameOptions/OptionGroupSingleton.cs b/MiraAPI/GameOptions/OptionGroupSingleton.cs index 03a11fa..8c7e2af 100644 --- a/MiraAPI/GameOptions/OptionGroupSingleton.cs +++ b/MiraAPI/GameOptions/OptionGroupSingleton.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; namespace MiraAPI.GameOptions; @@ -7,13 +6,12 @@ namespace MiraAPI.GameOptions; /// Singleton for option groups. /// /// The option group type. -public class OptionGroupSingleton where T : AbstractOptionGroup +public static class OptionGroupSingleton where T : AbstractOptionGroup { private static T? _instance; /// /// Gets the instance of the option group. /// - /// Can't public static T Instance => _instance ??= ModdedOptionsManager.Groups.OfType().Single(); } From b0536842355d1ecd3efeec18e15e8118410d8be0 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:08:01 -0400 Subject: [PATCH 07/36] formatting --- MiraAPI/GameOptions/ModdedOptionsManager.cs | 6 +++--- MiraAPI/Patches/Roles/EjectionPatches.cs | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index b57528e..1300d61 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -18,10 +18,10 @@ namespace MiraAPI.GameOptions; /// public static class ModdedOptionsManager { - private static readonly Dictionary OptionAttributes = new(); - private static readonly Dictionary TypeToGroup = new(); + private static readonly Dictionary OptionAttributes = []; + private static readonly Dictionary TypeToGroup = []; - internal static readonly Dictionary ModdedOptions = new(); + internal static readonly Dictionary ModdedOptions = []; internal static readonly List Groups = []; internal static uint NextId => _nextId++; diff --git a/MiraAPI/Patches/Roles/EjectionPatches.cs b/MiraAPI/Patches/Roles/EjectionPatches.cs index bbea1e8..9f85872 100644 --- a/MiraAPI/Patches/Roles/EjectionPatches.cs +++ b/MiraAPI/Patches/Roles/EjectionPatches.cs @@ -3,15 +3,18 @@ namespace MiraAPI.Patches.Roles; +/// +/// Patches for custom ejection messages. +/// [HarmonyPatch(typeof(ExileController))] public static class EjectionPatches { - [HarmonyPostfix] [HarmonyPatch(nameof(ExileController.Begin))] - public static void Begin(ExileController __instance) + public static void BeginPostfix(ExileController __instance) { - if (!__instance.initData.networkedPlayer || !__instance.initData.networkedPlayer.Role || __instance.initData.networkedPlayer.Role is not ICustomRole role) + if (!__instance.initData.networkedPlayer || !__instance.initData.networkedPlayer.Role || + __instance.initData.networkedPlayer.Role is not ICustomRole role) { return; } @@ -23,4 +26,4 @@ public static void Begin(ExileController __instance) __instance.completeString = role.GetCustomEjectionMessage(__instance.initData.networkedPlayer); } -} \ No newline at end of file +} From d1c469746e7ed091014f62e2507f311c4394612c Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:08:18 -0400 Subject: [PATCH 08/36] fix poor documentation in ICustomRole --- MiraAPI/Roles/ICustomRole.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index ffe2327..08875f9 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -41,7 +41,7 @@ public interface ICustomRole ModdedRoleTeams Team { get; } /// - /// Gets the **maximum amount** of players that can have this role at a time. This is not the same as the **number** of players that can have this role in a game. + /// Gets the hard limit of players that can have this role. This property is used to set a limit in the Role Options menu. /// int MaxPlayers => 15; From f17089f4355de4140736836ebc76a1d77f1efee8 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:14:30 -0400 Subject: [PATCH 09/36] split HudManager patches --- MiraAPI/Patches/HudManagerPatches.cs | 68 +------------------- MiraAPI/Patches/Roles/HudManagerPatches.cs | 74 ++++++++++++++++++++++ 2 files changed, 75 insertions(+), 67 deletions(-) create mode 100644 MiraAPI/Patches/Roles/HudManagerPatches.cs diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index c3ee9e8..45bd114 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -17,62 +17,6 @@ public static class HudManagerPatches // Custom buttons parent. private static GameObject? _bottomLeft; - // Custom role tab. - private static TaskPanelBehaviour? _roleTab; - - /// - /// Update custom role tab and custom role hud elements. - /// - /// The HudManager instance. - [HarmonyPostfix] - [HarmonyPatch(nameof(HudManager.Update))] - public static void UpdatePostfix(HudManager __instance) - { - var local = PlayerControl.LocalPlayer; - - if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance) - { - return; - } - - // CustomGameModeManager.ActiveMode?.HudUpdate(__instance); - - switch (local?.Data?.Role) - { - case null: - return; - - case ICustomRole customRole: - { - customRole.HudUpdate(__instance); - - if (customRole.RoleHintType != RoleHintType.RoleTab) - { - _roleTab?.gameObject.Destroy(); - return; - } - - if (_roleTab == null) - { - _roleTab = CustomRoleManager.CreateRoleTab(customRole); - } - else - { - CustomRoleManager.UpdateRoleTab(_roleTab, customRole); - } - - break; - } - - default: - if (_roleTab != null) - { - _roleTab.gameObject.Destroy(); - } - break; - } - } - /* /// /// Trigger hudstart on current custom gamemode @@ -136,7 +80,7 @@ public static void StartPostfix(HudManager __instance) } /// - /// Set the custom role tab and custom buttons active when the hud is active. + /// Set the custom buttons active when the hud is active. /// /// HudManager instance. /// The local PlayerControl. @@ -151,19 +95,9 @@ public static void SetHudActivePostfix(HudManager __instance, PlayerControl loca return; } - if (_roleTab) - { - _roleTab?.gameObject.SetActive(isActive); - } - foreach (var button in CustomButtonManager.CustomButtons) { button.SetActive(isActive, role); } - - if (role is ICustomRole customRole) - { - __instance.ImpostorVentButton.gameObject.SetActive(isActive && customRole.CanUseVent); - } } } diff --git a/MiraAPI/Patches/Roles/HudManagerPatches.cs b/MiraAPI/Patches/Roles/HudManagerPatches.cs new file mode 100644 index 0000000..34f0bb9 --- /dev/null +++ b/MiraAPI/Patches/Roles/HudManagerPatches.cs @@ -0,0 +1,74 @@ +using HarmonyLib; +using InnerNet; +using MiraAPI.Roles; +using Reactor.Utilities.Extensions; + +namespace MiraAPI.Patches.Roles; + +/// +/// HudManager patches for roles. +/// +[HarmonyPatch(typeof(HudManager))] +public static class HudManagerPatches +{ + // Custom role tab. + private static TaskPanelBehaviour? _roleTab; + + /// + /// Fixes Kill Button not showing for Neutral killing role. + /// + [HarmonyPostfix] + [HarmonyPatch(nameof(HudManager.SetHudActive), typeof(PlayerControl), typeof(RoleBehaviour), typeof(bool))] + public static void SetHudActivePostfix( + HudManager __instance, + PlayerControl localPlayer, + RoleBehaviour role, + bool isActive) + { + var flag = localPlayer.Data != null && localPlayer.Data.IsDead; + + if (role is ICustomRole) + { + __instance.KillButton.ToggleVisible(isActive && role.CanUseKillButton && !flag); + __instance.ImpostorVentButton.ToggleVisible(isActive && role.CanVent && !flag); + } + + if (_roleTab) + { + _roleTab?.gameObject.SetActive(isActive); + } + } + + /// + /// Update custom role tab and custom role hud elements. + /// + [HarmonyPostfix] + [HarmonyPatch(nameof(HudManager.Update))] + public static void UpdatePostfix(HudManager __instance) + { + var local = PlayerControl.LocalPlayer; + + if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance) + { + return; + } + + var role = local?.Data?.Role; + + if (role is ICustomRole { RoleHintType: RoleHintType.RoleTab } customRole) + { + customRole.HudUpdate(__instance); + + if (_roleTab == null) + { + _roleTab = CustomRoleManager.CreateRoleTab(customRole); + } + + CustomRoleManager.UpdateRoleTab(_roleTab, customRole); + } + else if (_roleTab) + { + _roleTab?.gameObject.Destroy(); + } + } +} From a3dd4acf43c83439735a1b278f385b3b9acaf8f3 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:29:42 -0400 Subject: [PATCH 10/36] code document custom murder RPCs --- MiraAPI/Networking/CustomMurderRpc.cs | 271 +++++++++++++++++++++++++ MiraAPI/Networking/CustomMurderRpcs.cs | 186 ----------------- 2 files changed, 271 insertions(+), 186 deletions(-) create mode 100644 MiraAPI/Networking/CustomMurderRpc.cs delete mode 100644 MiraAPI/Networking/CustomMurderRpcs.cs diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs new file mode 100644 index 0000000..b36810a --- /dev/null +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -0,0 +1,271 @@ +using AmongUs.GameOptions; +using Assets.CoreScripts; +using Reactor.Networking.Attributes; +using Reactor.Utilities; +using Reactor.Utilities.Extensions; +using System.Collections; +using System.Linq; +using UnityEngine; + +namespace MiraAPI.Networking; + +/// +/// Custom murder RPCs to fix issues with default ones. +/// +public static class CustomMurderRpc +{ + /// + /// Networked Custom Murder method. + /// + /// The killer. + /// The player to murder. + /// Whether the murder was successful or not. + /// Should the kill timer be reset. + /// Should a dead body be created. + /// Should the killer be snapped to the dead player. + /// Should the kill animation be shown. + /// Should the kill sound be played. + [MethodRpc((uint)MiraRpc.CustomMurder)] + public static void RpcCustomMurder( + this PlayerControl source, + PlayerControl target, + bool didSucceed = true, + bool resetKillTimer = true, + bool createDeadBody = true, + bool teleportMurderer = true, + bool showKillAnim = true, + bool playKillSound = true) + { + var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError; + var murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags; + + source.CustomMurder( + target, + murderResultFlags2, + resetKillTimer, + createDeadBody, + teleportMurderer, + showKillAnim, + playKillSound); + } + + /// + /// Custom Murder method without networking. If you need a networked version, use . + /// + /// The killer. + /// The player to murder. + /// Murder result flags. + /// Should the kill timer be reset. + /// Should a dead body be created. + /// Should the killer be snapped to the dead player. + /// Should the kill animation be shown. + /// Should the kill sound be played. + public static void CustomMurder( + this PlayerControl source, + PlayerControl target, + MurderResultFlags resultFlags, + bool resetKillTimer = true, + bool createDeadBody = true, + bool teleportMurderer = true, + bool showKillAnim = true, + bool playKillSound = true) + { + source.isKilling = false; + Logger.Debug($"{source.PlayerId} trying to murder {target.PlayerId}"); + var data = target.Data; + if (resultFlags.HasFlag(MurderResultFlags.FailedError)) + { + return; + } + + if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) || + (resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1)) + { + target.protectedByGuardianThisRound = true; + var flag = PlayerControl.LocalPlayer.Data.Role.Role == RoleTypes.GuardianAngel; + if (flag && PlayerControl.LocalPlayer.Data.PlayerId == target.protectedByGuardianId) + { + StatsManager.Instance.IncrementStat(StringNames.StatsGuardianAngelCrewmatesProtected); + DestroyableSingleton.Instance.OnProtectACrewmate(); + } + + if (source.AmOwner || flag) + { + target.ShowFailedMurder(); + + if (resetKillTimer) + { + source.SetKillTimer( + GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown) / 2f); + } + } + else + { + target.RemoveProtection(); + } + + Logger.Debug($"{source.PlayerId} failed to murder {target.PlayerId} due to guardian angel protection"); + return; + } + + if (!resultFlags.HasFlag(MurderResultFlags.Succeeded) && + !resultFlags.HasFlag(MurderResultFlags.DecisionByHost)) + { + return; + } + + DestroyableSingleton.Instance.Analytics.Kill(target.Data, source.Data); + if (source.AmOwner) + { + StatsManager.Instance.IncrementStat( + GameManager.Instance.IsHideAndSeek() + ? StringNames.StatsImpostorKills_HideAndSeek + : StringNames.StatsImpostorKills); + + if (source.CurrentOutfitType == PlayerOutfitType.Shapeshifted) + { + StatsManager.Instance.IncrementStat(StringNames.StatsShapeshifterShiftedKills); + } + + if (Constants.ShouldPlaySfx() && playKillSound) + { + SoundManager.Instance.PlaySound(source.KillSfx, false, 0.8f); + } + + if (resetKillTimer) + { + source.SetKillTimer( + GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown)); + } + } + + DestroyableSingleton.Instance.WriteMurder(); + target.gameObject.layer = LayerMask.NameToLayer("Ghost"); + if (target.AmOwner) + { + StatsManager.Instance.IncrementStat(StringNames.StatsTimesMurdered); + if (Minigame.Instance) + { + try + { + Minigame.Instance.Close(); + Minigame.Instance.Close(); + } + catch + { + // ignored + } + } + + if (showKillAnim) + { + DestroyableSingleton.Instance.KillOverlay.ShowKillAnimation(source.Data, data); + } + + target.cosmetics.SetNameMask(false); + target.RpcSetScanner(false); + } + + DestroyableSingleton.Instance.OnMurder( + source.AmOwner, + target.AmOwner, + source.CurrentOutfitType == PlayerOutfitType.Shapeshifted, + source.shapeshiftTargetPlayerId, + target.PlayerId); + Coroutines.Start(source.KillAnimations.Random()?.CoPerformCustomKill(source, target, createDeadBody, teleportMurderer)); + Logger.Debug($"{source.PlayerId} succeeded in murdering {target.PlayerId}"); + } + + /// + /// Perform a custom kill animation. + /// + /// The kill animation. + /// The murderer. + /// The murdered player. + /// Should a dead body be created. + /// Should the murder be teleported. + /// Coroutine. + public static IEnumerator CoPerformCustomKill( + this KillAnimation anim, + PlayerControl source, + PlayerControl target, + bool createDeadBody = true, + bool teleportMurderer = true) + { + var cam = Camera.main?.GetComponent(); + var isParticipant = PlayerControl.LocalPlayer == source || PlayerControl.LocalPlayer == target; + var sourcePhys = source.MyPhysics; + + if (teleportMurderer) + { + KillAnimation.SetMovement(source, false); + } + + KillAnimation.SetMovement(target, false); + + if (isParticipant) + { + PlayerControl.LocalPlayer.isKilling = true; + source.isKilling = true; + } + + DeadBody? deadBody = null; + + if (createDeadBody) + { + deadBody = Object.Instantiate(GameManager.Instance.DeadBodyPrefab); + deadBody.enabled = false; + deadBody.ParentId = target.PlayerId; + deadBody.bodyRenderers.ToList().ForEach(target.SetPlayerMaterialColors); + + target.SetPlayerMaterialColors(deadBody.bloodSplatter); + var vector = target.transform.position + anim.BodyOffset; + vector.z = vector.y / 1000f; + deadBody.transform.position = vector; + } + + if (isParticipant) + { + if (cam != null) + { + cam.Locked = true; + } + + ConsoleJoystick.SetMode_Task(); + if (PlayerControl.LocalPlayer.AmOwner) + { + PlayerControl.LocalPlayer.MyPhysics.inputHandler.enabled = true; + } + } + + target.Die(DeathReason.Kill, true); + yield return source.MyPhysics.Animations.CoPlayCustomAnimation(anim.BlurAnim); + sourcePhys.Animations.PlayIdleAnimation(); + + if (teleportMurderer) + { + source.NetTransform.SnapTo(target.transform.position); + KillAnimation.SetMovement(source, true); + } + + KillAnimation.SetMovement(target, true); + + if (deadBody != null) + { + deadBody.enabled = true; + } + + if (!isParticipant) + { + yield break; + } + + if (cam != null) + { + cam.Locked = true; + } + + PlayerControl.LocalPlayer.isKilling = false; + source.isKilling = false; + } +} diff --git a/MiraAPI/Networking/CustomMurderRpcs.cs b/MiraAPI/Networking/CustomMurderRpcs.cs deleted file mode 100644 index 17fb4ae..0000000 --- a/MiraAPI/Networking/CustomMurderRpcs.cs +++ /dev/null @@ -1,186 +0,0 @@ -using AmongUs.GameOptions; -using Assets.CoreScripts; -using Reactor.Networking.Attributes; -using Reactor.Utilities; -using Reactor.Utilities.Extensions; -using System.Collections; -using System.Linq; -using UnityEngine; - -namespace MiraAPI.Networking; -public static class CustomMurderRpcs -{ - [MethodRpc((uint)MiraRpc.CustomMurder)] - public static void RpcCustomMurder(this PlayerControl source, PlayerControl target, bool didSucceed = true, - bool resetKillTimer = true, bool createDeadBody = true, bool teleportMurderer = true, bool showKillAnim = true, bool playKillSound = true) - { - var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError; - var murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags; - - source.CustomMurder(target, murderResultFlags2, resetKillTimer, createDeadBody, teleportMurderer, showKillAnim, playKillSound); - } - - public static void CustomMurder(this PlayerControl source, PlayerControl target, MurderResultFlags resultFlags, - bool resetKillTimer = true, bool createDeadBody = true, bool teleportMurderer = true, bool showKillAnim = true, bool playKillSound = true) - { - source.isKilling = false; - source.logger.Debug(string.Format("{0} trying to murder {1}", source.PlayerId, target.PlayerId), null); - var data = target.Data; - if (resultFlags.HasFlag(MurderResultFlags.FailedError)) - { - return; - } - if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) || (resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1)) - { - target.protectedByGuardianThisRound = true; - var flag = PlayerControl.LocalPlayer.Data.Role.Role == RoleTypes.GuardianAngel; - if (flag && PlayerControl.LocalPlayer.Data.PlayerId == target.protectedByGuardianId) - { - StatsManager.Instance.IncrementStat(StringNames.StatsGuardianAngelCrewmatesProtected); - DestroyableSingleton.Instance.OnProtectACrewmate(); - } - if (source.AmOwner || flag) - { - target.ShowFailedMurder(); - - if (resetKillTimer) - { - source.SetKillTimer(GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown) / 2f); - } - } - else - { - target.RemoveProtection(); - } - source.logger.Debug(string.Format("{0} failed to murder {1} due to guardian angel protection", source.PlayerId, target.PlayerId), null); - return; - } - if (resultFlags.HasFlag(MurderResultFlags.Succeeded) || resultFlags.HasFlag(MurderResultFlags.DecisionByHost)) - { - DestroyableSingleton.Instance.Analytics.Kill(target.Data, source.Data); - if (source.AmOwner) - { - if (GameManager.Instance.IsHideAndSeek()) - { - StatsManager.Instance.IncrementStat(StringNames.StatsImpostorKills_HideAndSeek); - } - else - { - StatsManager.Instance.IncrementStat(StringNames.StatsImpostorKills); - } - if (source.CurrentOutfitType == PlayerOutfitType.Shapeshifted) - { - StatsManager.Instance.IncrementStat(StringNames.StatsShapeshifterShiftedKills); - } - if (Constants.ShouldPlaySfx() && playKillSound) - { - SoundManager.Instance.PlaySound(source.KillSfx, false, 0.8f, null); - } - - if (resetKillTimer) - { - source.SetKillTimer(GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown)); - } - } - DestroyableSingleton.Instance.WriteMurder(); - target.gameObject.layer = LayerMask.NameToLayer("Ghost"); - if (target.AmOwner) - { - StatsManager.Instance.IncrementStat(StringNames.StatsTimesMurdered); - if (Minigame.Instance) - { - try - { - Minigame.Instance.Close(); - Minigame.Instance.Close(); - } - catch - { - } - } - - if (showKillAnim) - { - DestroyableSingleton.Instance.KillOverlay.ShowKillAnimation(source.Data, data); - } - - target.cosmetics.SetNameMask(false); - target.RpcSetScanner(false); - } - DestroyableSingleton.Instance.OnMurder(source.AmOwner, target.AmOwner, source.CurrentOutfitType == PlayerOutfitType.Shapeshifted, source.shapeshiftTargetPlayerId, (int)target.PlayerId); - Coroutines.Start(source.KillAnimations.Random().CoPerformCustomKill(source, target, createDeadBody, teleportMurderer)); - source.logger.Debug(string.Format("{0} succeeded in murdering {1}", source.PlayerId, target.PlayerId), null); - } - } - - public static IEnumerator CoPerformCustomKill(this KillAnimation anim, PlayerControl source, PlayerControl target, - bool createDeadBody = true, bool teleportMurderer = true) - { - var cam = Camera.main.GetComponent(); - var isParticipant = PlayerControl.LocalPlayer == source || PlayerControl.LocalPlayer == target; - var sourcePhys = source.MyPhysics; - - if (teleportMurderer) - { - KillAnimation.SetMovement(source, false); - } - - KillAnimation.SetMovement(target, false); - - if (isParticipant) - { - PlayerControl.LocalPlayer.isKilling = true; - source.isKilling = true; - } - - DeadBody deadBody = null; - - if (createDeadBody) - { - deadBody = Object.Instantiate(GameManager.Instance.DeadBodyPrefab); - deadBody.enabled = false; - deadBody.ParentId = target.PlayerId; - deadBody.bodyRenderers.ToArray().ToList().ForEach(target.SetPlayerMaterialColors); - - target.SetPlayerMaterialColors(deadBody.bloodSplatter); - var vector = target.transform.position + anim.BodyOffset; - vector.z = vector.y / 1000f; - deadBody.transform.position = vector; - } - - if (isParticipant) - { - cam.Locked = true; - ConsoleJoystick.SetMode_Task(); - if (PlayerControl.LocalPlayer.AmOwner) - { - PlayerControl.LocalPlayer.MyPhysics.inputHandler.enabled = true; - } - } - - target.Die(DeathReason.Kill, true); - yield return source.MyPhysics.Animations.CoPlayCustomAnimation(anim.BlurAnim); - sourcePhys.Animations.PlayIdleAnimation(); - - if (teleportMurderer) - { - source.NetTransform.SnapTo(target.transform.position); - KillAnimation.SetMovement(source, true); - } - KillAnimation.SetMovement(target, true); - - if (deadBody) - { - deadBody.enabled = true; - } - - if (isParticipant) - { - cam.Locked = false; - PlayerControl.LocalPlayer.isKilling = false; - source.isKilling = false; - } - - yield break; - } -} \ No newline at end of file From f8bb63483fbacf6ed836cc013e849d3d0d2e7854 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:54:35 -0400 Subject: [PATCH 11/36] fixing possible issues with ghost roles --- MiraAPI/Roles/CustomRoleManager.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index ca34213..a3d36ec 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -35,6 +35,11 @@ private static ushort GetNextRoleId() internal static void RegisterInRoleManager() { RoleManager.Instance.AllRoles = RoleManager.Instance.AllRoles.Concat(CustomRoles.Values).ToArray(); + + foreach (var role in CustomRoles.Values.Where(x=>x.IsDead)) + { + RoleManager.GhostRoles.Add(role.Role); + } } internal static void RegisterRoleTypes(List roles, MiraPluginInfo pluginInfo) @@ -81,8 +86,8 @@ internal static void RegisterRoleTypes(List roles, MiraPluginInfo pluginIn roleBehaviour.BlurbNameLong = CustomStringName.CreateAndRegister(customRole.RoleLongDescription); roleBehaviour.AffectedByLightAffectors = customRole.AffectedByLight; roleBehaviour.CanBeKilled = customRole.CanGetKilled; - roleBehaviour.CanUseKillButton = customRole.CanKill; - roleBehaviour.TasksCountTowardProgress = customRole.TasksCount; + roleBehaviour.CanUseKillButton = customRole.CanUseKill; + roleBehaviour.TasksCountTowardProgress = customRole.TasksCountForProgress; roleBehaviour.CanVent = customRole.CanUseVent; roleBehaviour.DefaultGhostRole = customRole.GhostRole; roleBehaviour.MaxCount = customRole.MaxPlayers; From f207849e5efbcc654c7cf189f92401a08f647645 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:12:50 -0400 Subject: [PATCH 12/36] fix neutral killing issues and rename some properties in ICustomRole to clear confusion --- MiraAPI.Example/Roles/NeutralKillerRole.cs | 27 +++++++++ MiraAPI/Networking/CustomMurderRpc.cs | 20 +++---- MiraAPI/Patches/HudManagerPatches.cs | 2 - MiraAPI/Patches/Roles/KillButtonPatches.cs | 65 ++++++++++++++++++++++ MiraAPI/Roles/CustomRoleManager.cs | 2 +- MiraAPI/Roles/ICustomRole.cs | 8 +-- 6 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 MiraAPI.Example/Roles/NeutralKillerRole.cs create mode 100644 MiraAPI/Patches/Roles/KillButtonPatches.cs diff --git a/MiraAPI.Example/Roles/NeutralKillerRole.cs b/MiraAPI.Example/Roles/NeutralKillerRole.cs new file mode 100644 index 0000000..0cbc6fd --- /dev/null +++ b/MiraAPI.Example/Roles/NeutralKillerRole.cs @@ -0,0 +1,27 @@ +using MiraAPI.Roles; +using UnityEngine; + +namespace MiraAPI.Example.Roles; + +[RegisterCustomRole] +public class NeutralKillerRole : ImpostorRole, ICustomRole +{ + public string RoleName => "Neutral Killer"; + public string RoleDescription => "Neutral who can kill."; + public string RoleLongDescription => RoleDescription; + public Color RoleColor => Color.magenta; + public ModdedRoleTeams Team => ModdedRoleTeams.Neutral; + public bool CanUseKill => true; + public bool CanGetKilled => true; + public bool CanUseVent => true; + + public override void SpawnTaskHeader(PlayerControl playerControl) + { + // remove existing task header. + } + + public override bool DidWin(GameOverReason gameOverReason) + { + return GameManager.Instance.DidHumansWin(gameOverReason); + } +} diff --git a/MiraAPI/Networking/CustomMurderRpc.cs b/MiraAPI/Networking/CustomMurderRpc.cs index b36810a..619a0ab 100644 --- a/MiraAPI/Networking/CustomMurderRpc.cs +++ b/MiraAPI/Networking/CustomMurderRpc.cs @@ -1,10 +1,11 @@ -using AmongUs.GameOptions; +using System.Collections; +using System.Linq; +using AmongUs.GameOptions; using Assets.CoreScripts; +using BepInEx.Unity.IL2CPP.Utils; using Reactor.Networking.Attributes; using Reactor.Utilities; using Reactor.Utilities.Extensions; -using System.Collections; -using System.Linq; using UnityEngine; namespace MiraAPI.Networking; @@ -71,7 +72,7 @@ public static void CustomMurder( bool playKillSound = true) { source.isKilling = false; - Logger.Debug($"{source.PlayerId} trying to murder {target.PlayerId}"); + Logger.Error($"{source.PlayerId} trying to murder {target.PlayerId}"); var data = target.Data; if (resultFlags.HasFlag(MurderResultFlags.FailedError)) { @@ -104,7 +105,7 @@ public static void CustomMurder( target.RemoveProtection(); } - Logger.Debug($"{source.PlayerId} failed to murder {target.PlayerId} due to guardian angel protection"); + Logger.Error($"{source.PlayerId} failed to murder {target.PlayerId} due to guardian angel protection"); return; } @@ -134,8 +135,7 @@ public static void CustomMurder( if (resetKillTimer) { - source.SetKillTimer( - GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown)); + source.SetKillTimer(GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown)); } } @@ -172,8 +172,8 @@ public static void CustomMurder( source.CurrentOutfitType == PlayerOutfitType.Shapeshifted, source.shapeshiftTargetPlayerId, target.PlayerId); - Coroutines.Start(source.KillAnimations.Random()?.CoPerformCustomKill(source, target, createDeadBody, teleportMurderer)); - Logger.Debug($"{source.PlayerId} succeeded in murdering {target.PlayerId}"); + source.MyPhysics.StartCoroutine(source.KillAnimations.Random()?.CoPerformCustomKill(source, target, createDeadBody, teleportMurderer)); + Logger.Error($"{source.PlayerId} succeeded in murdering {target.PlayerId}"); } /// @@ -262,7 +262,7 @@ public static IEnumerator CoPerformCustomKill( if (cam != null) { - cam.Locked = true; + cam.Locked = false; } PlayerControl.LocalPlayer.isKilling = false; diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index 45bd114..cdef7cc 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -1,7 +1,5 @@ using HarmonyLib; -using InnerNet; using MiraAPI.Hud; -using MiraAPI.Roles; using Reactor.Utilities.Extensions; using UnityEngine; using Object = UnityEngine.Object; diff --git a/MiraAPI/Patches/Roles/KillButtonPatches.cs b/MiraAPI/Patches/Roles/KillButtonPatches.cs new file mode 100644 index 0000000..52b5cba --- /dev/null +++ b/MiraAPI/Patches/Roles/KillButtonPatches.cs @@ -0,0 +1,65 @@ +using HarmonyLib; +using Il2CppSystem; +using MiraAPI.Networking; +using MiraAPI.Roles; +using UnityEngine; + +namespace MiraAPI.Patches.Roles; + +/// +/// Fix kill button issues for neutral killers. +/// +[HarmonyPatch(typeof(KillButton))] +public static class KillButtonPatches +{ + [HarmonyPrefix] + [HarmonyPatch(nameof(KillButton.SetTarget))] + public static bool SetTargetPrefix(KillButton __instance, PlayerControl target) + { + if (!PlayerControl.LocalPlayer || PlayerControl.LocalPlayer.Data == null || !PlayerControl.LocalPlayer.Data.Role) + { + return false; + } + + if (PlayerControl.LocalPlayer.Data.Role is not ICustomRole customRole) + { + return true; + } + + if (__instance.currentTarget && __instance.currentTarget != target) + { + __instance.currentTarget.cosmetics.SetOutline(false, new Nullable(Color.clear)); + } + __instance.currentTarget = target; + if (__instance.currentTarget) + { + __instance.currentTarget.cosmetics.SetOutline(true, new Nullable(customRole.RoleColor)); + __instance.SetEnabled(); + return false; + } + __instance.SetDisabled(); + + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(KillButton.DoClick))] + public static bool DoClickPrefix(KillButton __instance) + { + if (PlayerControl.LocalPlayer.Data.Role is not ICustomRole) + { + return true; + } + + if (!__instance.isActiveAndEnabled || !__instance.currentTarget || __instance.isCoolingDown || + PlayerControl.LocalPlayer.Data.IsDead || !PlayerControl.LocalPlayer.CanMove) + { + return false; + } + + PlayerControl.LocalPlayer.RpcCustomMurder(__instance.currentTarget); + __instance.SetTarget(null); + + return false; + } +} diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index a3d36ec..7fad146 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -84,7 +84,7 @@ internal static void RegisterRoleTypes(List roles, MiraPluginInfo pluginIn roleBehaviour.StringName = CustomStringName.CreateAndRegister(customRole.RoleName); roleBehaviour.BlurbName = CustomStringName.CreateAndRegister(customRole.RoleDescription); roleBehaviour.BlurbNameLong = CustomStringName.CreateAndRegister(customRole.RoleLongDescription); - roleBehaviour.AffectedByLightAffectors = customRole.AffectedByLight; + roleBehaviour.AffectedByLightAffectors = customRole.AffectedByLightOnAirship; roleBehaviour.CanBeKilled = customRole.CanGetKilled; roleBehaviour.CanUseKillButton = customRole.CanUseKill; roleBehaviour.TasksCountTowardProgress = customRole.TasksCountForProgress; diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 08875f9..0ac41bb 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -68,9 +68,9 @@ public interface ICustomRole ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance {GetType().FullName}"); /// - /// Gets a value indicating whether the role is affected by light sabotages. + /// Gets a value indicating whether the role is affected by light affectors on Airship. /// - bool AffectedByLight => Team == ModdedRoleTeams.Crewmate; + bool AffectedByLightOnAirship => Team == ModdedRoleTeams.Crewmate; /// /// Gets a value indicating whether the role can be killed by others. @@ -80,7 +80,7 @@ public interface ICustomRole /// /// Gets a value indicating whether the role can kill others. /// - bool CanKill => Team == ModdedRoleTeams.Impostor; + bool CanUseKill => Team == ModdedRoleTeams.Impostor; /// /// Gets a value indicating whether the role can use vents. @@ -90,7 +90,7 @@ public interface ICustomRole /// /// Gets a value indicating whether the role's tasks count towards task progress. /// - bool TasksCount => Team == ModdedRoleTeams.Crewmate; + bool TasksCountForProgress => Team == ModdedRoleTeams.Crewmate; /// /// Gets a value indicating whether the role is a Ghost. From 97ded2fc3270d58627e780442bc81ab03162c3f2 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:53:08 -0400 Subject: [PATCH 13/36] some updates to example mod --- .../Buttons/Freezer/FreezeButton.cs | 8 +++++--- .../Buttons/Teleporter/TeleportButton.cs | 4 ++-- .../Options/Roles/CustomRole2Settings.cs | 16 --------------- .../Options/Roles/FreezerRoleSettings.cs | 20 +++++++++++++++++++ .../Options/Roles/TeleporterOptions.cs | 8 ++++---- 5 files changed, 31 insertions(+), 25 deletions(-) delete mode 100644 MiraAPI.Example/Options/Roles/CustomRole2Settings.cs create mode 100644 MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs diff --git a/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs b/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs index f8de0ab..d357932 100644 --- a/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs +++ b/MiraAPI.Example/Buttons/Freezer/FreezeButton.cs @@ -1,5 +1,7 @@ using MiraAPI.Example.Modifiers.Freezer; +using MiraAPI.Example.Options.Roles; using MiraAPI.Example.Roles; +using MiraAPI.GameOptions; using MiraAPI.Hud; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; @@ -11,9 +13,9 @@ namespace MiraAPI.Example.Buttons.Freezer; public class FreezeButton : CustomActionButton { public override string Name => "Freeze"; - public override float Cooldown => 5f; + public override float Cooldown => OptionGroupSingleton.Instance.FreezeDuration; public override float EffectDuration => 0f; - public override int MaxUses => 0; + public override int MaxUses => (int)OptionGroupSingleton.Instance.FreezeUses; public override LoadableAsset Sprite => ExampleAssets.ExampleButton; protected override void OnClick() @@ -23,7 +25,7 @@ protected override void OnClick() public override PlayerControl? GetTarget() { - return PlayerControl.LocalPlayer.GetClosestPlayer(true, Distance, false); + return PlayerControl.LocalPlayer.GetClosestPlayer(true, Distance); } public override void SetOutline(bool active) diff --git a/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs b/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs index 99199b8..3a8d810 100644 --- a/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs +++ b/MiraAPI.Example/Buttons/Teleporter/TeleportButton.cs @@ -13,7 +13,7 @@ public class TeleportButton : CustomActionButton { public override string Name => "Teleport"; - public override float Cooldown => OptionGroupSingleton.Instance.TeleportCooldown; + public override float Cooldown => OptionGroupSingleton.Instance.TeleportCooldown.Value; public override float EffectDuration => OptionGroupSingleton.Instance.TeleportDuration; @@ -22,7 +22,7 @@ public class TeleportButton : CustomActionButton public override LoadableAsset Sprite => ExampleAssets.TeleportButton; public static bool IsZoom { get; private set; } - public override bool Enabled(RoleBehaviour role) + public override bool Enabled(RoleBehaviour? role) { return role is TeleporterRole; } diff --git a/MiraAPI.Example/Options/Roles/CustomRole2Settings.cs b/MiraAPI.Example/Options/Roles/CustomRole2Settings.cs deleted file mode 100644 index e10931b..0000000 --- a/MiraAPI.Example/Options/Roles/CustomRole2Settings.cs +++ /dev/null @@ -1,16 +0,0 @@ -using MiraAPI.Example.Roles; -using MiraAPI.GameOptions; -using MiraAPI.GameOptions.Attributes; -using System; - -namespace MiraAPI.Example.Options.Roles; - -public class CustomRole2Settings : AbstractOptionGroup -{ - public override string GroupName => "Custom Role"; - - public override Type AdvancedRole => typeof(FreezerRole); - - [ModdedToggleOption("Teleportation ability")] public bool Teleport { get; set; } = true; - -} \ No newline at end of file diff --git a/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs b/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs new file mode 100644 index 0000000..0e26665 --- /dev/null +++ b/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs @@ -0,0 +1,20 @@ +using MiraAPI.Example.Roles; +using MiraAPI.GameOptions; +using MiraAPI.GameOptions.Attributes; +using System; +using MiraAPI.Utilities; + +namespace MiraAPI.Example.Options.Roles; + +public class FreezerRoleSettings : AbstractOptionGroup +{ + public override string GroupName => "Custom Role"; + + public override Type AdvancedRole => typeof(FreezerRole); + + [ModdedNumberOption("Freeze Duration", 1, 15, 1, MiraNumberSuffixes.Seconds)] + public float FreezeDuration { get; set; } = 5; + + [ModdedNumberOption("Freeze Uses", 1, 5)] + public float FreezeUses { get; set; } = 1; +} diff --git a/MiraAPI.Example/Options/Roles/TeleporterOptions.cs b/MiraAPI.Example/Options/Roles/TeleporterOptions.cs index b8bb823..34ccb94 100644 --- a/MiraAPI.Example/Options/Roles/TeleporterOptions.cs +++ b/MiraAPI.Example/Options/Roles/TeleporterOptions.cs @@ -1,8 +1,9 @@ -using MiraAPI.Example.Roles; +using System; +using MiraAPI.Example.Roles; using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; +using MiraAPI.GameOptions.OptionTypes; using MiraAPI.Utilities; -using System; namespace MiraAPI.Example.Options.Roles; @@ -12,8 +13,7 @@ public class TeleporterOptions : AbstractOptionGroup public override Type AdvancedRole => typeof(TeleporterRole); - [ModdedNumberOption("Teleport Cooldown", 5, 60, 2.5f, MiraNumberSuffixes.Seconds)] - public float TeleportCooldown { get; set; } = 5; + public ModdedNumberOption TeleportCooldown { get; set; } = new("Teleport Cooldown", 10, 5, 60, 2.5f, MiraNumberSuffixes.Seconds); [ModdedNumberOption("Teleport Duration", 5, 25, 1, MiraNumberSuffixes.Seconds)] public float TeleportDuration { get; set; } = 10; From 3cada4cb3d0b8df6d25beb5b920ba3b67213d647 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:57:25 -0400 Subject: [PATCH 14/36] fix problem with TimedModifier using RPC on every client, despite being replicated --- MiraAPI/Modifiers/BaseModifier.cs | 5 +++++ MiraAPI/Modifiers/ModifierComponent.cs | 1 + MiraAPI/Modifiers/Types/TimedModifier.cs | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index b91c078..9d6e82c 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -10,6 +10,11 @@ public abstract class BaseModifier /// public PlayerControl? Player { get; internal set; } + /// + /// Gets the modifier component that the modifier is attached to. + /// + public ModifierComponent? ModifierComponent { get; internal set; } + /// /// Gets the modifier id. /// diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index eecda1a..9517711 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -213,6 +213,7 @@ public void RemoveModifier(BaseModifier modifier) _toAdd.Add(modifier); modifier.Player = _player; + modifier.ModifierComponent = this; modifier.ModifierId = modifierId.Value; return modifier; } diff --git a/MiraAPI/Modifiers/Types/TimedModifier.cs b/MiraAPI/Modifiers/Types/TimedModifier.cs index f18b136..99a6361 100644 --- a/MiraAPI/Modifiers/Types/TimedModifier.cs +++ b/MiraAPI/Modifiers/Types/TimedModifier.cs @@ -71,7 +71,7 @@ public override void FixedUpdate() if (RemoveOnComplete) { - Player?.RpcRemoveModifier(ModifierId); + ModifierComponent?.RemoveModifier(ModifierId); } } } From b032e91e089bddf33a462ad6ddb5a9a2e1af0fa9 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:57:32 -0400 Subject: [PATCH 15/36] using statements --- MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs index c3f0f5e..7a01f19 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs @@ -1,8 +1,8 @@ -using MiraAPI.Networking; -using Reactor.Localization.Utilities; -using System; +using System; using System.Linq; using Il2CppInterop.Runtime.InteropTypes.Arrays; +using MiraAPI.Networking; +using Reactor.Localization.Utilities; using UnityEngine; using Object = UnityEngine.Object; From 3d9bbc0833f450aea8cdf58df327974ddd2eb80a Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:01:34 -0400 Subject: [PATCH 16/36] comments --- .../Modifiers/RegisterModifierAttribute.cs | 5 +++- MiraAPI/Networking/MiraRpc.cs | 28 ++++++++++++++++++- MiraAPI/Networking/NetData.cs | 19 ++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/MiraAPI/Modifiers/RegisterModifierAttribute.cs b/MiraAPI/Modifiers/RegisterModifierAttribute.cs index 4c2fb56..4858b96 100644 --- a/MiraAPI/Modifiers/RegisterModifierAttribute.cs +++ b/MiraAPI/Modifiers/RegisterModifierAttribute.cs @@ -2,5 +2,8 @@ namespace MiraAPI.Modifiers; +/// +/// Marks a class as a modifier that can be registered with the . +/// [AttributeUsage(AttributeTargets.Class)] -public class RegisterModifierAttribute : Attribute; \ No newline at end of file +public class RegisterModifierAttribute : Attribute; diff --git a/MiraAPI/Networking/MiraRpc.cs b/MiraAPI/Networking/MiraRpc.cs index 40861ce..5126dea 100644 --- a/MiraAPI/Networking/MiraRpc.cs +++ b/MiraAPI/Networking/MiraRpc.cs @@ -1,11 +1,37 @@ namespace MiraAPI.Networking; +/// +/// Mira RPCs. +/// public enum MiraRpc : uint { + /// + /// Syncs the game options. + /// SyncGameOptions, + + /// + /// Syncs the role options. + /// SyncRoleOptions, + + /// + /// Adds a modifier to a player. + /// AddModifier, + + /// + /// Removes a modifier from a player. + /// RemoveModifier, + + /// + /// Syncs all modifiers at once. + /// SyncModifiers, + + /// + /// Custom Murder RPC. + /// CustomMurder, -} \ No newline at end of file +} diff --git a/MiraAPI/Networking/NetData.cs b/MiraAPI/Networking/NetData.cs index 083a9d2..ce5de46 100644 --- a/MiraAPI/Networking/NetData.cs +++ b/MiraAPI/Networking/NetData.cs @@ -1,8 +1,25 @@ namespace MiraAPI.Networking; +/// +/// Used to network data and mark it with an ID. +/// +/// The ID of the data. +/// The byte[] data. public readonly struct NetData(uint id, byte[] data) { + /// + /// Gets the ID of the data. + /// public uint Id { get; } = id; + + /// + /// Gets the byte[] data. + /// public byte[] Data { get; } = data; + + /// + /// Gets the length of the data in bytes. + /// + /// An int representing the number of bytes this NetData takes up. public int GetLength() => 4 + Data.Length; -} \ No newline at end of file +} From dc483adc1c7a564ed8ccca3b6471814a841b4992 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:04:20 -0400 Subject: [PATCH 17/36] more comments + nullable stuff --- MiraAPI/Networking/SyncModifiersRpc.cs | 18 ++++++++++++------ MiraAPI/Networking/SyncOptionsRpc.cs | 20 +++++++++++++------- MiraAPI/Networking/SyncRoleOptionsRpc.cs | 19 ++++++++++++------- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/MiraAPI/Networking/SyncModifiersRpc.cs b/MiraAPI/Networking/SyncModifiersRpc.cs index 69154f9..9598f0f 100644 --- a/MiraAPI/Networking/SyncModifiersRpc.cs +++ b/MiraAPI/Networking/SyncModifiersRpc.cs @@ -10,8 +10,14 @@ internal class SyncModifiersRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc { public override RpcLocalHandling LocalHandling => RpcLocalHandling.None; - public override void Write(MessageWriter writer, NetData[] data) + public override void Write(MessageWriter writer, NetData[]? data) { + if (data == null) + { + writer.WritePacked(0U); + return; + } + writer.WritePacked((uint)data.Length); foreach (var netData in data) { @@ -26,21 +32,21 @@ public override NetData[] Read(MessageReader reader) var data = new NetData[length]; for (var i = 0; i < length; i++) { - var id = reader.ReadPackedUInt32(); + var dataId = reader.ReadPackedUInt32(); var bytes = reader.ReadBytesAndSize(); - data[i] = new NetData(id, bytes); + data[i] = new NetData(dataId, bytes); } return data; } - public override void Handle(PlayerControl playerControl, NetData[] data) + public override void Handle(PlayerControl playerControl, NetData[]? data) { if (AmongUsClient.Instance.HostId != playerControl.OwnerId) { return; } - ModifierManager.HandleSyncModifiers(data); + ModifierManager.HandleSyncModifiers(data ?? []); } -} \ No newline at end of file +} diff --git a/MiraAPI/Networking/SyncOptionsRpc.cs b/MiraAPI/Networking/SyncOptionsRpc.cs index 98efc70..288c690 100644 --- a/MiraAPI/Networking/SyncOptionsRpc.cs +++ b/MiraAPI/Networking/SyncOptionsRpc.cs @@ -10,9 +10,15 @@ namespace MiraAPI.Networking; internal class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) { public override RpcLocalHandling LocalHandling => RpcLocalHandling.None; - - public override void Write(MessageWriter writer, NetData[] data) + + public override void Write(MessageWriter writer, NetData[]? data) { + if (data == null) + { + writer.WritePacked(0U); + return; + } + writer.WritePacked((uint)data.Length); foreach (var netData in data) { @@ -27,21 +33,21 @@ public override NetData[] Read(MessageReader reader) var data = new NetData[length]; for (var i = 0; i < length; i++) { - var id = reader.ReadPackedUInt32(); + var dataId = reader.ReadPackedUInt32(); var bytes = reader.ReadBytesAndSize(); - data[i] = new NetData(id, bytes); + data[i] = new NetData(dataId, bytes); } return data; } - public override void Handle(PlayerControl playerControl, NetData[] data) + public override void Handle(PlayerControl playerControl, NetData[]? data) { if (AmongUsClient.Instance.HostId != playerControl.OwnerId) { return; } - ModdedOptionsManager.HandleSyncOptions(data); + ModdedOptionsManager.HandleSyncOptions(data ?? []); } -} \ No newline at end of file +} diff --git a/MiraAPI/Networking/SyncRoleOptionsRpc.cs b/MiraAPI/Networking/SyncRoleOptionsRpc.cs index 7160912..290fc0f 100644 --- a/MiraAPI/Networking/SyncRoleOptionsRpc.cs +++ b/MiraAPI/Networking/SyncRoleOptionsRpc.cs @@ -8,11 +8,16 @@ namespace MiraAPI.Networking; [RegisterCustomRpc((uint)MiraRpc.SyncRoleOptions)] internal class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) { - public override RpcLocalHandling LocalHandling => RpcLocalHandling.None; - public override void Write(MessageWriter writer, NetData[] data) + public override void Write(MessageWriter writer, NetData[]? data) { + if (data == null) + { + writer.WritePacked(0U); + return; + } + writer.WritePacked((uint)data.Length); foreach (var netData in data) { @@ -27,21 +32,21 @@ public override NetData[] Read(MessageReader reader) var data = new NetData[length]; for (var i = 0; i < length; i++) { - var id = reader.ReadPackedUInt32(); + var dataId = reader.ReadPackedUInt32(); var bytes = reader.ReadBytesAndSize(); - data[i] = new NetData(id, bytes); + data[i] = new NetData(dataId, bytes); } return data; } - public override void Handle(PlayerControl playerControl, NetData[] data) + public override void Handle(PlayerControl playerControl, NetData[]? data) { if (AmongUsClient.Instance.HostId != playerControl.OwnerId) { return; } - CustomRoleManager.HandleSyncRoleOptions(data); + CustomRoleManager.HandleSyncRoleOptions(data ?? []); } -} \ No newline at end of file +} From cb943c2173fa2ea8bfb93e391e881ab0e086d1bc Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:37:45 -0400 Subject: [PATCH 18/36] CanKill -> UseVanillaKillButton ICustomRole changes to clear up confusion Add KillButtonOutlineColor to ICustomRole --- MiraAPI.Example/Roles/NeutralKillerRole.cs | 2 +- MiraAPI/Patches/Roles/KillButtonPatches.cs | 8 ++++- MiraAPI/Roles/CustomRoleManager.cs | 2 +- MiraAPI/Roles/ICustomRole.cs | 36 ++++++++++++++-------- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/MiraAPI.Example/Roles/NeutralKillerRole.cs b/MiraAPI.Example/Roles/NeutralKillerRole.cs index 0cbc6fd..820c334 100644 --- a/MiraAPI.Example/Roles/NeutralKillerRole.cs +++ b/MiraAPI.Example/Roles/NeutralKillerRole.cs @@ -11,7 +11,7 @@ public class NeutralKillerRole : ImpostorRole, ICustomRole public string RoleLongDescription => RoleDescription; public Color RoleColor => Color.magenta; public ModdedRoleTeams Team => ModdedRoleTeams.Neutral; - public bool CanUseKill => true; + public bool UseVanillaKillButton => true; public bool CanGetKilled => true; public bool CanUseVent => true; diff --git a/MiraAPI/Patches/Roles/KillButtonPatches.cs b/MiraAPI/Patches/Roles/KillButtonPatches.cs index 52b5cba..7b7010a 100644 --- a/MiraAPI/Patches/Roles/KillButtonPatches.cs +++ b/MiraAPI/Patches/Roles/KillButtonPatches.cs @@ -12,6 +12,9 @@ namespace MiraAPI.Patches.Roles; [HarmonyPatch(typeof(KillButton))] public static class KillButtonPatches { + /// + /// SetTarget for custom roles. + /// [HarmonyPrefix] [HarmonyPatch(nameof(KillButton.SetTarget))] public static bool SetTargetPrefix(KillButton __instance, PlayerControl target) @@ -33,7 +36,7 @@ public static bool SetTargetPrefix(KillButton __instance, PlayerControl target) __instance.currentTarget = target; if (__instance.currentTarget) { - __instance.currentTarget.cosmetics.SetOutline(true, new Nullable(customRole.RoleColor)); + __instance.currentTarget.cosmetics.SetOutline(true, new Nullable(customRole.KillButtonOutlineColor)); __instance.SetEnabled(); return false; } @@ -42,6 +45,9 @@ public static bool SetTargetPrefix(KillButton __instance, PlayerControl target) return false; } + /// + /// Use Custom Murder if player is custom role. + /// [HarmonyPrefix] [HarmonyPatch(nameof(KillButton.DoClick))] public static bool DoClickPrefix(KillButton __instance) diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index 7fad146..f63a5c8 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -86,7 +86,7 @@ internal static void RegisterRoleTypes(List roles, MiraPluginInfo pluginIn roleBehaviour.BlurbNameLong = CustomStringName.CreateAndRegister(customRole.RoleLongDescription); roleBehaviour.AffectedByLightAffectors = customRole.AffectedByLightOnAirship; roleBehaviour.CanBeKilled = customRole.CanGetKilled; - roleBehaviour.CanUseKillButton = customRole.CanUseKill; + roleBehaviour.CanUseKillButton = customRole.UseVanillaKillButton; roleBehaviour.TasksCountTowardProgress = customRole.TasksCountForProgress; roleBehaviour.CanVent = customRole.CanUseVent; roleBehaviour.DefaultGhostRole = customRole.GhostRole; diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 0ac41bb..3087dbe 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -57,30 +57,20 @@ public interface ICustomRole [HideFromIl2Cpp] LoadableAsset Icon => MiraAssets.Empty; - /// - /// Gets the BepInEx ConfigDefinition for the amount of players that can have this role. - /// - ConfigDefinition NumConfigDefinition => new("Roles", $"Num {GetType().FullName}"); - - /// - /// Gets the BepInEx ConfigDefinition for the chance of this role being selected. - /// - ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance {GetType().FullName}"); - /// /// Gets a value indicating whether the role is affected by light affectors on Airship. /// bool AffectedByLightOnAirship => Team == ModdedRoleTeams.Crewmate; /// - /// Gets a value indicating whether the role can be killed by others. + /// Gets a value indicating whether the role can be killed by vanilla murder system. /// bool CanGetKilled => Team == ModdedRoleTeams.Crewmate; /// - /// Gets a value indicating whether the role can kill others. + /// Gets a value indicating whether the role should use the vanilla kill button. /// - bool CanUseKill => Team == ModdedRoleTeams.Impostor; + bool UseVanillaKillButton => Team == ModdedRoleTeams.Impostor; /// /// Gets a value indicating whether the role can use vents. @@ -102,6 +92,16 @@ public interface ICustomRole /// bool HideSettings => IsGhostRole; + /// + /// Gets the outline color for the KillButton if is true. + /// + Color KillButtonOutlineColor => Team switch + { + ModdedRoleTeams.Impostor => Palette.ImpostorRed, + ModdedRoleTeams.Crewmate => Palette.CrewmateBlue, + _ => RoleColor, + }; + /// /// Gets the role hint style. See enum for all options. /// @@ -112,6 +112,16 @@ public interface ICustomRole /// RoleTypes GhostRole => Team == ModdedRoleTeams.Impostor ? RoleTypes.ImpostorGhost : RoleTypes.CrewmateGhost; + /// + /// Gets the BepInEx ConfigDefinition for the amount of players that can have this role. + /// + ConfigDefinition NumConfigDefinition => new("Roles", $"Num {GetType().FullName}"); + + /// + /// Gets the BepInEx ConfigDefinition for the chance of this role being selected. + /// + ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance {GetType().FullName}"); + /// /// Gets the parent mod of this role. /// From 9665188a18461b50e1a46ec9f5e4978e06868f72 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:46:10 -0400 Subject: [PATCH 19/36] various adjustments to code style --- MiraAPI/GameOptions/IModdedOption.cs | 2 +- MiraAPI/Patches/Modifiers/VentPatches.cs | 4 +- .../Options/NotificationPopperPatch.cs | 34 ++--- .../Patches/Options/RoleSettingMenuPatches.cs | 118 +++++++++++++----- 4 files changed, 107 insertions(+), 51 deletions(-) diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index 4f7e950..544d64b 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -17,7 +17,7 @@ internal interface IModdedOption OptionBehaviour? OptionBehaviour { get; } Func Visible { get; set; } ConfigDefinition? ConfigDefinition { get; set; } - OptionBehaviour? CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container); + OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container); float GetFloatData(); NetData GetNetData(); void HandleNetData(byte[] data); diff --git a/MiraAPI/Patches/Modifiers/VentPatches.cs b/MiraAPI/Patches/Modifiers/VentPatches.cs index 6894848..4aeea09 100644 --- a/MiraAPI/Patches/Modifiers/VentPatches.cs +++ b/MiraAPI/Patches/Modifiers/VentPatches.cs @@ -23,10 +23,10 @@ public static void CanUseVentPatch(Vent __instance, ref float __result, [Harmony switch (role.CanVent) { - case true when modifiers.Any(x => !x.CanVent()): + case true when modifiers.Exists(x => !x.CanVent()): couldUse = canUse = false; return; - case false when modifiers.Any(x => x.CanVent()): + case false when modifiers.Exists(x => x.CanVent()): couldUse = true; break; } diff --git a/MiraAPI/Patches/Options/NotificationPopperPatch.cs b/MiraAPI/Patches/Options/NotificationPopperPatch.cs index 0a8fc57..faa24eb 100644 --- a/MiraAPI/Patches/Options/NotificationPopperPatch.cs +++ b/MiraAPI/Patches/Options/NotificationPopperPatch.cs @@ -6,34 +6,38 @@ namespace MiraAPI.Patches.Options; [HarmonyPatch(typeof(NotificationPopper))] -public class NotificationPopperPatch +public static class NotificationPopperPatch { [HarmonyPrefix] [HarmonyPatch(nameof(NotificationPopper.AddRoleSettingsChangeMessage))] - public static bool RoleChangeMsgPatch(NotificationPopper __instance, - [HarmonyArgument(0)] StringNames key, [HarmonyArgument(1)] int roleCount, [HarmonyArgument(2)] int roleChance, [HarmonyArgument(3)] RoleTeamTypes teamType, + public static bool RoleChangeMsgPatch( + NotificationPopper __instance, + [HarmonyArgument(0)] StringNames key, + [HarmonyArgument(1)] int roleCount, + [HarmonyArgument(2)] int roleChance, + [HarmonyArgument(3)] RoleTeamTypes teamType, [HarmonyArgument(4)] bool playSound) { if (CustomRoleManager.CustomRoles.Values.Any(role => role.StringName == key)) { - var item = string.Empty; - var text = (teamType == RoleTeamTypes.Crewmate) ? Palette.CrewmateSettingChangeText.ToTextColor() : Palette.ImpostorRed.ToTextColor(); - item = DestroyableSingleton.Instance.GetString(StringNames.LobbyChangeSettingNotificationRole, new Object[] - { - string.Concat( - [ + var text = teamType == RoleTeamTypes.Crewmate + ? Palette.CrewmateSettingChangeText.ToTextColor() + : Palette.ImpostorRed.ToTextColor(); + + var item = DestroyableSingleton.Instance.GetString( + StringNames.LobbyChangeSettingNotificationRole, "", text, DestroyableSingleton.Instance.GetString(key, Array.Empty()), - "" - ]), - "" + roleCount.ToString() + "", - "" + roleChance.ToString() + "%" - }); + "", + "" + roleCount + "", + "" + roleChance + "%" + ); + __instance.SettingsChangeMessageLogic(key, item, playSound); return false; } return true; } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 420fff8..8ba2c93 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -54,58 +54,72 @@ public static bool PatchStart(RolesSettingsMenu __instance) var num3 = 0; - var crewRoles = GameSettingMenuPatches.SelectedMod.CustomRoles.Values + var crewRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values .OfType() - .Where(role => role.Team == ModdedRoleTeams.Crewmate && !role.HideSettings) + .Where(role => role is { Team: ModdedRoleTeams.Crewmate, HideSettings: false }) .ToList(); - if (crewRoles.Count > 0) + if (crewRoles is { Count: > 0 }) { - - var categoryHeaderEditRole = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform); + var categoryHeaderEditRole = Object.Instantiate( + __instance.categoryHeaderEditRoleOrigin, + Vector3.zero, + Quaternion.identity, + __instance.RoleChancesSettings.transform); categoryHeaderEditRole.SetHeader(StringNames.CrewmateRolesHeader, 20); categoryHeaderEditRole.transform.localPosition = new Vector3(4.986f, num, -2f); num -= 0.522f; foreach (var role in crewRoles) { - CreateQuotaOption(__instance, role as RoleBehaviour, ref num, num3); + if (role is RoleBehaviour roleBehaviour) + { + CreateQuotaOption(__instance, roleBehaviour, ref num, num3); + } num3++; } } - var impRoles = GameSettingMenuPatches.SelectedMod.CustomRoles.Values + var impRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values .OfType() - .Where(role => role.Team == ModdedRoleTeams.Impostor && !role.HideSettings) + .Where(role => role is { Team: ModdedRoleTeams.Impostor, HideSettings: false }) .ToList(); - if (impRoles.Count > 0) + if (impRoles is { Count: > 0 }) { num -= 0.4f; - var categoryHeaderEditRole2 = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin, - Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform); + var categoryHeaderEditRole2 = Object.Instantiate( + __instance.categoryHeaderEditRoleOrigin, + Vector3.zero, + Quaternion.identity, + __instance.RoleChancesSettings.transform); categoryHeaderEditRole2.SetHeader(StringNames.ImpostorRolesHeader, 20); categoryHeaderEditRole2.transform.localPosition = new Vector3(4.986f, num, -2f); num -= 0.522f; - foreach (var role in impRoles) { - CreateQuotaOption(__instance, role as RoleBehaviour, ref num, num3); + if (role is RoleBehaviour roleBehaviour) + { + CreateQuotaOption(__instance, roleBehaviour, ref num, num3); + } num3++; } } - var neutRoles = GameSettingMenuPatches.SelectedMod.CustomRoles.Values + var neutRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values .OfType() - .Where(role => role.Team == ModdedRoleTeams.Neutral && !role.HideSettings) + .Where(role => role is { Team: ModdedRoleTeams.Neutral, HideSettings: false }) .ToList(); - if (neutRoles.Count > 0) + if (neutRoles is { Count: > 0 }) { num -= 0.4f; - var categoryHeaderEditRole3 = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin, - Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform); + var categoryHeaderEditRole3 = Object.Instantiate( + __instance.categoryHeaderEditRoleOrigin, + Vector3.zero, + Quaternion.identity, + __instance.RoleChancesSettings.transform); categoryHeaderEditRole3.SetHeader(StringNames.None, 20); categoryHeaderEditRole3.Title.text = "Neutral Roles"; categoryHeaderEditRole3.Background.color = Color.gray; @@ -115,7 +129,10 @@ public static bool PatchStart(RolesSettingsMenu __instance) foreach (var role in neutRoles) { - CreateQuotaOption(__instance, role as RoleBehaviour, ref num, num3); + if (role is RoleBehaviour roleBehaviour) + { + CreateQuotaOption(__instance, roleBehaviour, ref num, num3); + } num3++; } } @@ -161,12 +178,18 @@ private static void ValueChanged(OptionBehaviour obj) } roleSetting.UpdateValuesAndText(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions); - DestroyableSingleton.Instance.Notifier.AddRoleSettingsChangeMessage(roleSetting.Role.StringName, roleSetting.RoleMaxCount, roleSetting.RoleChance, roleSetting.Role.TeamType, false); + DestroyableSingleton.Instance.Notifier.AddRoleSettingsChangeMessage( + roleSetting.Role.StringName, + roleSetting.RoleMaxCount, + roleSetting.RoleChance, + roleSetting.Role.TeamType, + false); if (AmongUsClient.Instance.AmHost) { Rpc.Instance.Send(PlayerControl.LocalPlayer, [role.GetNetData()], true); } + GameOptionsManager.Instance.GameHostOptions = GameOptionsManager.Instance.CurrentGameOptions; } @@ -181,7 +204,7 @@ private static void CreateAdvancedSettings(RolesSettingsMenu __instance, RoleBeh var num = -0.872f; - var filteredOptions = GameSettingMenuPatches.SelectedMod.Options.Where(x => x.AdvancedRole == role.GetType()); + var filteredOptions = GameSettingMenuPatches.SelectedMod?.Options.Where(x => x.AdvancedRole == role.GetType()) ?? []; foreach (var option in filteredOptions) { @@ -190,7 +213,12 @@ private static void CreateAdvancedSettings(RolesSettingsMenu __instance, RoleBeh continue; } - var newOpt = option.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.AdvancedRolesSettings.transform); + var newOpt = option.CreateOption( + __instance.checkboxOrigin, + __instance.numberOptionOrigin, + __instance.stringOptionOrigin, + __instance.AdvancedRolesSettings.transform); + newOpt.transform.localPosition = new Vector3(2.17f, num, -2f); newOpt.SetClickMask(__instance.ButtonClickMask); @@ -199,10 +227,11 @@ private static void CreateAdvancedSettings(RolesSettingsMenu __instance, RoleBeh { renderer.material.SetInt(PlayerMaterial.MaskLayer, 20); } - foreach (var textMeshPro in newOpt.GetComponentsInChildren(true)) + + foreach (var fontMat in newOpt.GetComponentsInChildren(true).Select(x=>x.fontMaterial)) { - textMeshPro.fontMaterial.SetFloat(ShaderID.StencilComp, 3f); - textMeshPro.fontMaterial.SetFloat(ShaderID.Stencil, 20); + fontMat.SetFloat(ShaderID.StencilComp, 3f); + fontMat.SetFloat(ShaderID.Stencil, 20); } newOpt.LabelBackground.enabled = false; @@ -215,12 +244,24 @@ private static void CreateAdvancedSettings(RolesSettingsMenu __instance, RoleBeh __instance.scrollBar.CalculateAndSetYBounds(__instance.advancedSettingChildren.Count + 3, 1f, 6f, 0.45f); __instance.scrollBar.ScrollToTop(); } + private static void ChangeTab(RoleBehaviour role, RolesSettingsMenu __instance) { - var customRole = role as ICustomRole; + if (role is not ICustomRole customRole) + { + Logger.Error($"Role {role.NiceName} is not a custom role."); + return; + } + __instance.roleDescriptionText.text = customRole.RoleLongDescription; - __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, new Il2CppReferenceArray(0)); - __instance.roleScreenshot.sprite = Sprite.Create(customRole.OptionsScreenshot.LoadAsset().texture, new Rect(0, 0, 370, 230), Vector2.one / 2, 100); + __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString( + role.StringName, + new Il2CppReferenceArray(0)); + __instance.roleScreenshot.sprite = Sprite.Create( + customRole.OptionsScreenshot.LoadAsset().texture, + new Rect(0, 0, 370, 230), + Vector2.one / 2, + 100); __instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced; __instance.roleHeaderSprite.color = customRole.RoleColor; __instance.roleHeaderText.color = customRole.RoleColor.GetAlternateColor(); @@ -244,15 +285,25 @@ private static void ChangeTab(RoleBehaviour role, RolesSettingsMenu __instance) optionBehaviour.SetAsPlayer(); } } + __instance.RoleChancesSettings.SetActive(false); __instance.AdvancedRolesSettings.SetActive(true); __instance.RefreshChildren(); } - public static void CreateQuotaOption(RolesSettingsMenu __instance, RoleBehaviour role, ref float yPos, int index) + private static void CreateQuotaOption(RolesSettingsMenu __instance, RoleBehaviour role, ref float yPos, int index) { - var customRole = role as ICustomRole; - var roleOptionSetting = Object.Instantiate(__instance.roleOptionSettingOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform); + if (role is not ICustomRole customRole) + { + Logger.Error($"Role {role.NiceName} is not a custom role."); + return; + } + + var roleOptionSetting = Object.Instantiate( + __instance.roleOptionSettingOrigin, + Vector3.zero, + Quaternion.identity, + __instance.RoleChancesSettings.transform); roleOptionSetting.transform.localPosition = new Vector3(-0.15f, yPos, -2f); roleOptionSetting.SetRole(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions, role, 20); roleOptionSetting.labelSprite.color = customRole.RoleColor; @@ -265,7 +316,8 @@ public static void CreateQuotaOption(RolesSettingsMenu __instance, RoleBehaviour roleOptionSetting.titleText.horizontalAlignment = HorizontalAlignmentOptions.Left; if (GameSettingMenuPatches.SelectedMod is null || - GameSettingMenuPatches.SelectedMod.Options.Any(x => x.AdvancedRole != null && x.AdvancedRole.IsInstanceOfType(role))) + GameSettingMenuPatches.SelectedMod.Options.Exists( + x => x.AdvancedRole != null && x.AdvancedRole.IsInstanceOfType(role))) { var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform); newButton.name = "ConfigButton"; @@ -289,4 +341,4 @@ public static void CreateQuotaOption(RolesSettingsMenu __instance, RoleBehaviour yPos += -0.43f; } } -} \ No newline at end of file +} From 812cef5f2b18ce3e532f3f71954b1f1b7e6ef6e5 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:10:44 -0400 Subject: [PATCH 20/36] fix loadable asset code docs and rework logic slightly --- MiraAPI/Utilities/Assets/LoadableAsset.cs | 14 ++++++------- .../Utilities/Assets/LoadableBundleAsset.cs | 20 +++++++++---------- .../Utilities/Assets/LoadableResourceAsset.cs | 20 ++++++++----------- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/MiraAPI/Utilities/Assets/LoadableAsset.cs b/MiraAPI/Utilities/Assets/LoadableAsset.cs index 464da65..798dfc9 100644 --- a/MiraAPI/Utilities/Assets/LoadableAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableAsset.cs @@ -5,17 +5,17 @@ /// Mira uses the pattern in various locations. /// You can create your own implementation of this class to load assets in different ways. /// -/// The type of the asset to be loaded +/// The type of the asset to be loaded. public abstract class LoadableAsset where T : UnityEngine.Object { /// - /// A reference to the loaded asset. Intended to be used for caching purposes. + /// Gets or sets reference to the loaded asset. Intended to be used for caching purposes. /// - protected T LoadedAsset; - + protected T? LoadedAsset { get; set; } + /// - /// The method that loads the asset. + /// Loads the asset from the source. /// - /// The loaded asset + /// The loaded asset. public abstract T LoadAsset(); -} \ No newline at end of file +} diff --git a/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs b/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs index 8ce1b07..6f46eef 100644 --- a/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableBundleAsset.cs @@ -7,30 +7,30 @@ namespace MiraAPI.Utilities.Assets; /// /// A utility class for loading assets from an asset bundle. /// -/// The name of the asset -/// The AssetBundle that contains the asset -/// The type of the asset to be loaded +/// The name of the asset. +/// The AssetBundle that contains the asset. +/// The type of the asset to be loaded. public class LoadableBundleAsset(string name, AssetBundle bundle) : LoadableAsset where T : UnityEngine.Object { /// /// Loads the asset from the asset bundle. /// - /// The asset + /// The asset. /// The asset did not load properly. public override T LoadAsset() { - if (LoadedAsset) + if (LoadedAsset != null) { return LoadedAsset; } LoadedAsset = bundle.LoadAsset(name); - - if (!LoadedAsset) + + if (LoadedAsset == null) { - throw new Exception($"INVALID ASSET: {name}"); + throw new InvalidOperationException($"INVALID ASSET: {name}"); } - + return LoadedAsset; } -} \ No newline at end of file +} diff --git a/MiraAPI/Utilities/Assets/LoadableResourceAsset.cs b/MiraAPI/Utilities/Assets/LoadableResourceAsset.cs index 29ce7ad..1dfda4a 100644 --- a/MiraAPI/Utilities/Assets/LoadableResourceAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableResourceAsset.cs @@ -1,28 +1,24 @@ using System.Reflection; -using Reactor.Utilities; using UnityEngine; namespace MiraAPI.Utilities.Assets; +/// +/// A utility class for loading assets from embedded resources. +/// +/// The path to the embedded resource. public class LoadableResourceAsset(string path) : LoadableAsset { private readonly Assembly _assembly = Assembly.GetCallingAssembly(); + /// public override Sprite LoadAsset() { - if (LoadedAsset) + if (LoadedAsset != null) { return LoadedAsset; } - try - { - return LoadedAsset = SpriteTools.LoadSpriteFromPath(path, _assembly); - } - catch - { - Logger.Error($"Not loading, invalid asset: {path}"); - return null; - } + return LoadedAsset = SpriteTools.LoadSpriteFromPath(path, _assembly); } -} \ No newline at end of file +} From 74055b8a175e90f05e6926a7120157e49f05811f Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:11:10 -0400 Subject: [PATCH 21/36] various code alterations to patches to fix warnings --- MiraAPI/Modifiers/Types/TimedModifier.cs | 1 - MiraAPI/Patches/AmongUsClientSyncPatch.cs | 5 +- MiraAPI/Patches/IntroCutscenePatches.cs | 47 +-- MiraAPI/Patches/Modifiers/VentPatches.cs | 1 - .../Patches/Options/GameOptionsMenuPatch.cs | 56 +++- .../Patches/Options/LobbyViewPanePatches.cs | 300 ++++++++++-------- .../Options/NotificationPopperPatch.cs | 34 +- MiraAPI/Patches/Options/OptionsPatches.cs | 15 +- .../Patches/Options/RoleSettingMenuPatches.cs | 6 +- MiraAPI/Patches/Options/SettingPatches.cs | 31 +- MiraAPI/Patches/Roles/NameTagPatch.cs | 2 +- MiraAPI/Patches/Roles/RoleBehaviourPatches.cs | 2 +- MiraAPI/Patches/Roles/RoleManagerPatches.cs | 9 +- .../Roles/RoleOptionsCollectionPatch.cs | 8 +- MiraAPI/Patches/Roles/TaskAdderPatch.cs | 194 ++++++----- MiraAPI/Patches/Roles/TaskPanelPatch.cs | 39 ++- MiraAPI/Patches/Roles/VentOutlinePatch.cs | 3 + 17 files changed, 438 insertions(+), 315 deletions(-) diff --git a/MiraAPI/Modifiers/Types/TimedModifier.cs b/MiraAPI/Modifiers/Types/TimedModifier.cs index 99a6361..b189b47 100644 --- a/MiraAPI/Modifiers/Types/TimedModifier.cs +++ b/MiraAPI/Modifiers/Types/TimedModifier.cs @@ -1,5 +1,4 @@ using System; -using MiraAPI.Utilities; using Reactor.Utilities; using UnityEngine; diff --git a/MiraAPI/Patches/AmongUsClientSyncPatch.cs b/MiraAPI/Patches/AmongUsClientSyncPatch.cs index a25dc3f..3034b42 100644 --- a/MiraAPI/Patches/AmongUsClientSyncPatch.cs +++ b/MiraAPI/Patches/AmongUsClientSyncPatch.cs @@ -6,6 +6,9 @@ namespace MiraAPI.Patches; +/// +/// Sync all options, role settings, and modifiers to the player when they join the game. +/// [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.CreatePlayer))] public static class AmongUsClientSyncPatch { @@ -25,4 +28,4 @@ public static void Postfix(ClientData clientData) CustomRoleManager.SyncAllRoleSettings(clientData.Id); ModifierManager.SyncAllModifiers(clientData.Id); } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/IntroCutscenePatches.cs b/MiraAPI/Patches/IntroCutscenePatches.cs index 3dab1ed..2250d3d 100644 --- a/MiraAPI/Patches/IntroCutscenePatches.cs +++ b/MiraAPI/Patches/IntroCutscenePatches.cs @@ -24,28 +24,33 @@ public static void BeginImpostorPatch(IntroCutscene __instance) [HarmonyPatch(nameof(IntroCutscene.BeginCrewmate))] public static bool BeginCrewmatePatch(IntroCutscene __instance) { - if (PlayerControl.LocalPlayer.Data.Role is ICustomRole customRole) + if (PlayerControl.LocalPlayer.Data.Role is not ICustomRole customRole) { - if (customRole.Team is not ModdedRoleTeams.Neutral) - { - return true; - } - - var barTransform = __instance.BackgroundBar.transform; - var position = barTransform.position; - position.y -= 0.25f; - barTransform.position = position; - - __instance.BackgroundBar.material.SetColor(ShaderID.Color, Color.gray); - __instance.TeamTitle.text = "NEUTRAL"; - __instance.impostorScale = 1f; - __instance.ImpostorText.text = "You are Neutral. You do not have a team."; - __instance.TeamTitle.color = Color.gray; - - __instance.ourCrewmate = __instance.CreatePlayer(0, Mathf.CeilToInt(7.5f), PlayerControl.LocalPlayer.Data, false); - return false; + return true; } - return true; + + if (customRole.Team is not ModdedRoleTeams.Neutral) + { + return true; + } + + var barTransform = __instance.BackgroundBar.transform; + var position = barTransform.position; + position.y -= 0.25f; + barTransform.position = position; + + __instance.BackgroundBar.material.SetColor(ShaderID.Color, Color.gray); + __instance.TeamTitle.text = "NEUTRAL"; + __instance.impostorScale = 1f; + __instance.ImpostorText.text = "You are Neutral. You do not have a team."; + __instance.TeamTitle.color = Color.gray; + + __instance.ourCrewmate = __instance.CreatePlayer( + 0, + Mathf.CeilToInt(7.5f), + PlayerControl.LocalPlayer.Data, + false); + return false; } /* @@ -55,4 +60,4 @@ public static void GameBeginPatch() { CustomGameModeManager.ActiveMode?.Initialize(); }*/ -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Modifiers/VentPatches.cs b/MiraAPI/Patches/Modifiers/VentPatches.cs index 4aeea09..6646336 100644 --- a/MiraAPI/Patches/Modifiers/VentPatches.cs +++ b/MiraAPI/Patches/Modifiers/VentPatches.cs @@ -1,6 +1,5 @@ using HarmonyLib; using MiraAPI.Utilities; -using System.Linq; using UnityEngine; namespace MiraAPI.Patches.Modifiers; diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index 4c47865..a51b2f9 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -30,12 +30,14 @@ public static void UpdatePatch(GameOptionsMenu __instance) } var num = 2.1f; - var filteredGroups = GameSettingMenuPatches.SelectedMod.OptionGroups.Where(x => x.GroupVisible.Invoke() && x.AdvancedRole is null); + var filteredGroups = + GameSettingMenuPatches.SelectedMod?.OptionGroups.Where( + x => x.GroupVisible.Invoke() && x.AdvancedRole is null) ?? []; foreach (var group in filteredGroups) { var filteredOpts = group.Options.Where(x => x.Visible.Invoke()).ToList(); - if (filteredOpts.Count == 0) + if (filteredOpts.Count == 0 || group.Header is null) { continue; } @@ -50,7 +52,12 @@ public static void UpdatePatch(GameOptionsMenu __instance) { var newOpt = opt.OptionBehaviour; - if (opt.Visible.Invoke() == false) + if (newOpt is null) + { + continue; + } + + if (!opt.Visible.Invoke()) { newOpt.gameObject.SetActive(false); continue; @@ -70,7 +77,6 @@ public static void UpdatePatch(GameOptionsMenu __instance) } __instance.scrollBar.SetYBoundsMax(-num - 1.65f); - } [HarmonyPrefix] @@ -84,11 +90,15 @@ public static bool SettingsPatch(GameOptionsMenu __instance) __instance.MapPicker.gameObject.SetActive(false); - var filteredGroups = GameSettingMenuPatches.SelectedMod.OptionGroups.Where(x => x.AdvancedRole is null); + var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups.Where(x => x.AdvancedRole is null) ?? []; foreach (var group in filteredGroups) { - var categoryHeaderMasked = Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer); + var categoryHeaderMasked = Object.Instantiate( + __instance.categoryHeaderOrigin, + Vector3.zero, + Quaternion.identity, + __instance.settingsContainer); categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20); if (group.GroupColor != Color.clear) { @@ -96,7 +106,10 @@ public static bool SettingsPatch(GameOptionsMenu __instance) categoryHeaderMasked.Divider.color = group.GroupColor; categoryHeaderMasked.Title.color = group.GroupColor.GetAlternateColor(); } - categoryHeaderMasked.Background.size = new Vector2(categoryHeaderMasked.Background.size.x + 1.5f, categoryHeaderMasked.Background.size.y); + + categoryHeaderMasked.Background.size = new Vector2( + categoryHeaderMasked.Background.size.x + 1.5f, + categoryHeaderMasked.Background.size.y); categoryHeaderMasked.gameObject.SetActive(false); group.Header = categoryHeaderMasked; @@ -105,8 +118,12 @@ public static bool SettingsPatch(GameOptionsMenu __instance) newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f); newText.gameObject.GetComponent().Destroy(); - var options = group.Options.Select(opt => opt.CreateOption(__instance.checkboxOrigin, - __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer)); + var options = group.Options.Select( + opt => opt.CreateOption( + __instance.checkboxOrigin, + __instance.numberOptionOrigin, + __instance.stringOptionOrigin, + __instance.settingsContainer)); foreach (var newOpt in options) { @@ -142,8 +159,10 @@ public static bool SettingsPatch(GameOptionsMenu __instance) if (newOpt is ToggleOption toggle) { toggle.CheckMark.sprite = MiraAssets.Checkmark.LoadAsset(); - toggle.CheckMark.color = group.GroupColor != Color.clear ? group.GroupColor : MiraAssets.AcceptedTeal; - var rend = toggle.CheckMark.transform.parent.FindChild("ActiveSprite").GetComponent(); + toggle.CheckMark.color = + group.GroupColor != Color.clear ? group.GroupColor : MiraAssets.AcceptedTeal; + var rend = toggle.CheckMark.transform.parent.FindChild("ActiveSprite") + .GetComponent(); rend.sprite = MiraAssets.CheckmarkBox.LoadAsset(); rend.color = group.GroupColor != Color.clear ? group.GroupColor : MiraAssets.AcceptedTeal; } @@ -162,11 +181,14 @@ public static bool SettingsPatch(GameOptionsMenu __instance) headerBtn.ClickSound = __instance.BackButton.GetComponent().ClickSound; headerBtn.OnMouseOver = new UnityEvent(); headerBtn.OnMouseOut = new UnityEvent(); - headerBtn.OnClick.AddListener((UnityAction)(() => - { - group.AllOptionsHidden = !group.AllOptionsHidden; - newText.text = group.AllOptionsHidden ? "(Click to open)" : "(Click to close)"; - })); + headerBtn.OnClick.AddListener( + (UnityAction)(() => + { + group.AllOptionsHidden = !group.AllOptionsHidden; + newText.text = group.AllOptionsHidden + ? "(Click to open)" + : "(Click to close)"; + })); headerBtn.SetButtonEnableState(true); } @@ -207,4 +229,4 @@ public static bool InitPatch(GameOptionsMenu __instance) return false; } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index a74588e..01c844b 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -16,10 +16,11 @@ namespace MiraAPI.Patches.Options; [HarmonyPatch(typeof(LobbyViewSettingsPane))] public static class LobbyViewPanePatches { - public static int CurrentSelectedMod { get; private set; } - - public static MiraPluginInfo? SelectedMod => CurrentSelectedMod == 0 ? null : MiraPluginManager.Instance.RegisteredPlugins()[CurrentSelectedMod-1]; + private static int CurrentSelectedMod { get; set; } + private static MiraPluginInfo? SelectedMod => CurrentSelectedMod == 0 + ? null + : MiraPluginManager.Instance.RegisteredPlugins()[CurrentSelectedMod - 1]; [HarmonyPostfix] [HarmonyPatch(nameof(LobbyViewSettingsPane.Awake))] @@ -33,92 +34,96 @@ public static void AwakePatch(LobbyViewSettingsPane __instance) nextButton.transform.localPosition = new Vector3(-5.4f, 2.4f, -2f); nextButton.transform.localScale = new Vector3(3, 3, 2); nextButton.name = "RightArrowButton"; - + var normal = nextButton.transform.FindChild("Normal").GetComponentInChildren(); normal.transform.localPosition = new Vector3(0, 0f, 0.3f); normal.sprite = MiraAssets.NextButton.LoadAsset(); - + var hover = nextButton.transform.FindChild("Hover").GetComponentInChildren(); hover.transform.localPosition = new Vector3(0, 0f, 0.3f); hover.sprite = MiraAssets.NextButtonActive.LoadAsset(); - + var passiveButton = nextButton.gameObject.GetComponent(); passiveButton.OnClick = new Button.ButtonClickedEvent(); - passiveButton.OnClick.AddListener((UnityAction)(() => - { - CurrentSelectedMod += 1; - if (CurrentSelectedMod > MiraPluginManager.Instance.RegisteredPlugins().Length) + passiveButton.OnClick.AddListener( + (UnityAction)(() => { - CurrentSelectedMod = 0; - } - __instance.RefreshTab(); - __instance.scrollBar.ScrollToTop(); - })); + CurrentSelectedMod += 1; + if (CurrentSelectedMod > MiraPluginManager.Instance.RegisteredPlugins().Length) + { + CurrentSelectedMod = 0; + } + + __instance.RefreshTab(); + __instance.scrollBar.ScrollToTop(); + })); // Create the back button var backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject; backButton.transform.localPosition = new Vector3(-6.3f, 2.4f, -2f); backButton.name = "LeftArrowButton"; backButton.transform.FindChild("Normal").gameObject.GetComponentInChildren().flipX - = backButton.transform.FindChild("Hover").gameObject.GetComponentInChildren().flipX + = backButton.transform.FindChild("Hover").gameObject.GetComponentInChildren().flipX = true; - + var passiveButton2 = backButton.gameObject.GetComponent(); passiveButton2.OnClick = new Button.ButtonClickedEvent(); - passiveButton2.OnClick.AddListener((UnityAction)(() => - { - CurrentSelectedMod -= 1; - if (CurrentSelectedMod < 0) + passiveButton2.OnClick.AddListener( + (UnityAction)(() => { - CurrentSelectedMod = MiraPluginManager.Instance.RegisteredPlugins().Length; - } - __instance.RefreshTab(); - __instance.scrollBar.ScrollToTop(); - })); + CurrentSelectedMod -= 1; + if (CurrentSelectedMod < 0) + { + CurrentSelectedMod = MiraPluginManager.Instance.RegisteredPlugins().Length; + } + + __instance.RefreshTab(); + __instance.scrollBar.ScrollToTop(); + })); } - + [HarmonyPostfix] [HarmonyPatch(nameof(LobbyViewSettingsPane.Update))] public static void UpdatePatch(LobbyViewSettingsPane __instance) { __instance.gameModeText.text = SelectedMod?.PluginInfo.Metadata.Name ?? "Default"; } - + [HarmonyPrefix] [HarmonyPatch(nameof(LobbyViewSettingsPane.DrawNormalTab))] public static bool DrawNormalTabPatch(LobbyViewSettingsPane __instance) { - if (CurrentSelectedMod == 0) - { - return true; - } - - DrawOptionsTab(__instance); - return false; + if (CurrentSelectedMod == 0) + { + return true; + } + + DrawOptionsTab(__instance); + return false; } - + [HarmonyPrefix] [HarmonyPatch(nameof(LobbyViewSettingsPane.DrawRolesTab))] public static bool DrawRolesTabPatch(LobbyViewSettingsPane __instance) { - if (CurrentSelectedMod == 0) - { - return true; - } - - DrawRolesTab(__instance); - return false; + if (CurrentSelectedMod == 0) + { + return true; + } + + DrawRolesTab(__instance); + return false; } - + private static void DrawOptionsTab(LobbyViewSettingsPane instance) { if (SelectedMod == null) { return; } - + var num = 1.44f; - + var filteredGroups = SelectedMod.OptionGroups.Where(x => x.GroupVisible.Invoke() && x.AdvancedRole is null); foreach (var group in filteredGroups) @@ -127,8 +132,11 @@ private static void DrawOptionsTab(LobbyViewSettingsPane instance) { continue; } - - var categoryHeaderMasked = Object.Instantiate(instance.categoryHeaderOrigin, instance.settingsContainer, true); + + var categoryHeaderMasked = Object.Instantiate( + instance.categoryHeaderOrigin, + instance.settingsContainer, + true); categoryHeaderMasked.SetHeader(StringNames.Name, 61); categoryHeaderMasked.Title.text = group.GroupName; categoryHeaderMasked.transform.localScale = Vector3.one; @@ -144,8 +152,11 @@ private static void DrawOptionsTab(LobbyViewSettingsPane instance) { continue; } - - var viewSettingsInfoPanel = Object.Instantiate(instance.infoPanelOrigin, instance.settingsContainer, true); + + var viewSettingsInfoPanel = Object.Instantiate( + instance.infoPanelOrigin, + instance.settingsContainer, + true); viewSettingsInfoPanel.transform.localScale = Vector3.one; float num2; if (i % 2 == 0) @@ -160,25 +171,35 @@ private static void DrawOptionsTab(LobbyViewSettingsPane instance) { num2 = -3f; } + viewSettingsInfoPanel.transform.localPosition = new Vector3(num2, num, -2f); var data = option.Data; - + + if (data == null) + { + continue; + } + if (data.Type == OptionTypes.Checkbox) { - viewSettingsInfoPanel.SetInfoCheckbox(data.Title, 61, Mathf.Approximately(option.GetFloatData(), 1)); + viewSettingsInfoPanel.SetInfoCheckbox( + data.Title, + 61, + Mathf.Approximately(option.GetFloatData(), 1)); } else { viewSettingsInfoPanel.SetInfo(data.Title, data.GetValueString(option.GetFloatData()), 61); } - + instance.settingsInfo.Add(viewSettingsInfoPanel.gameObject); i++; } - + num -= 0.59f; } + instance.scrollBar.CalculateAndSetYBounds(instance.settingsInfo.Count + 10, 2f, 6f, 0.59f); } @@ -188,7 +209,7 @@ private static void DrawRolesTab(LobbyViewSettingsPane instance) { return; } - + var num = 0.95f; var num2 = -6.53f; var categoryHeaderMasked = @@ -197,7 +218,7 @@ private static void DrawRolesTab(LobbyViewSettingsPane instance) categoryHeaderMasked.transform.localScale = Vector3.one; categoryHeaderMasked.transform.localPosition = new Vector3(-9.77f, 1.26f, -2f); instance.settingsInfo.Add(categoryHeaderMasked.gameObject); - + var list = new List(); foreach (var team in Enum.GetValues()) @@ -205,12 +226,12 @@ private static void DrawRolesTab(LobbyViewSettingsPane instance) var filteredRoles = SelectedMod.CustomRoles.Values .OfType() .Where(x => !x.HideSettings && x.Team == team).ToList(); - + if (filteredRoles.Count == 0) { continue; } - + var categoryHeaderRoleVariant = Object.Instantiate(instance.categoryHeaderRoleOrigin, instance.settingsContainer, true); @@ -229,7 +250,7 @@ private static void DrawRolesTab(LobbyViewSettingsPane instance) categoryHeaderRoleVariant.Title.color = Color.white; break; } - + categoryHeaderRoleVariant.transform.localScale = Vector3.one; categoryHeaderRoleVariant.transform.localPosition = new Vector3(0.09f, num, -2f); instance.settingsInfo.Add(categoryHeaderRoleVariant.gameObject); @@ -242,42 +263,48 @@ private static void DrawRolesTab(LobbyViewSettingsPane instance) { continue; } - + var chancePerGame = GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(roleBehaviour.Role); var numPerGame = GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(roleBehaviour.Role); - + var flag = numPerGame == 0; - + var viewSettingsInfoPanelRoleVariant = Object.Instantiate( - instance.infoPanelRoleOrigin, instance.settingsContainer, true); + instance.infoPanelRoleOrigin, + instance.settingsContainer, + true); viewSettingsInfoPanelRoleVariant.transform.localScale = Vector3.one; viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f); - + var advancedRoleOptions = SelectedMod.Options .Where(x => x.AdvancedRole == customRole.GetType()) .ToList(); - + if (!flag && advancedRoleOptions.Count > 0) { list.Add(customRole.GetType()); } - viewSettingsInfoPanelRoleVariant.SetInfo(roleBehaviour.NiceName, numPerGame, chancePerGame, - 61, customRole.RoleColor, customRole.Icon.LoadAsset(), true); - - viewSettingsInfoPanelRoleVariant.titleText.color = - viewSettingsInfoPanelRoleVariant.chanceTitle.color = - viewSettingsInfoPanelRoleVariant.chanceBackground.color = - viewSettingsInfoPanelRoleVariant.background.color = + viewSettingsInfoPanelRoleVariant.SetInfo( + roleBehaviour.NiceName, + numPerGame, + chancePerGame, + 61, + customRole.RoleColor, + customRole.Icon.LoadAsset(), + true); + + viewSettingsInfoPanelRoleVariant.titleText.color = + viewSettingsInfoPanelRoleVariant.chanceTitle.color = + viewSettingsInfoPanelRoleVariant.chanceBackground.color = + viewSettingsInfoPanelRoleVariant.background.color = customRole.RoleColor.GetAlternateColor(); instance.settingsInfo.Add(viewSettingsInfoPanelRoleVariant.gameObject); num -= 0.664f; } - - } if (list.Count > 0) @@ -312,7 +339,7 @@ private static void DrawRolesTab(LobbyViewSettingsPane instance) advancedRoleViewPanel.transform.localScale = Vector3.one; advancedRoleViewPanel.transform.localPosition = new Vector3(num4, num, -2f); var num5 = SetUpAdvancedRoleViewPanel(advancedRoleViewPanel, list[k], 0.59f, 61); - + if (num5 > num3) { num3 = num5; @@ -325,59 +352,74 @@ private static void DrawRolesTab(LobbyViewSettingsPane instance) instance.scrollBar.SetYBoundsMax(-num); } - private static float SetUpAdvancedRoleViewPanel(AdvancedRoleViewPanel viewPanel, Type roleType, float spacingY, int maskLayer) + private static float SetUpAdvancedRoleViewPanel( + AdvancedRoleViewPanel viewPanel, + Type roleType, + float spacingY, + int maskLayer) { - if (SelectedMod == null) - { - return 0; - } - - var role = SelectedMod.CustomRoles.Values.FirstOrDefault(x => x.GetType() == roleType); - - if (role == null) - { - return 0; - } - - if (role is not ICustomRole customRole) - { - return 0; - } - - viewPanel.header.SetHeader(role.StringName, maskLayer, role.TeamType == RoleTeamTypes.Crewmate, customRole.Icon.LoadAsset()); - viewPanel.divider.material.SetInt(PlayerMaterial.MaskLayer, maskLayer); - - var num = viewPanel.yPosStart; - var num2 = 1.08f; - - var filteredOptions = SelectedMod.Options - .Where(x => x.AdvancedRole == roleType) - .ToList(); - - for (var i = 0; i < filteredOptions.Count; i++) - { - var option = filteredOptions[i]; - var baseGameSetting = option.Data; - var viewSettingsInfoPanel = Object.Instantiate(viewPanel.infoPanelOrigin, viewPanel.transform, true); - viewSettingsInfoPanel.transform.localScale = Vector3.one; - viewSettingsInfoPanel.transform.localPosition = new Vector3(viewPanel.xPosStart, num, -2f); - - var value = option.GetFloatData(); - - if (baseGameSetting.Type == OptionTypes.Checkbox) - { - viewSettingsInfoPanel.SetInfoCheckbox(baseGameSetting.Title, maskLayer, value > 0f); - } - else - { - viewSettingsInfoPanel.SetInfo(baseGameSetting.Title, baseGameSetting.GetValueString(value), maskLayer); - } - num -= spacingY; - if (i > 0) - { - num2 += 0.8f; - } - } - return num2; + if (SelectedMod == null) + { + return 0; + } + + var role = SelectedMod.CustomRoles.Values.FirstOrDefault(x => x.GetType() == roleType); + + if (role == null) + { + return 0; + } + + if (role is not ICustomRole customRole) + { + return 0; + } + + viewPanel.header.SetHeader( + role.StringName, + maskLayer, + role.TeamType == RoleTeamTypes.Crewmate, + customRole.Icon.LoadAsset()); + viewPanel.divider.material.SetInt(PlayerMaterial.MaskLayer, maskLayer); + + var num = viewPanel.yPosStart; + var num2 = 1.08f; + + var filteredOptions = SelectedMod.Options + .Where(x => x.AdvancedRole == roleType) + .ToList(); + + for (var i = 0; i < filteredOptions.Count; i++) + { + var option = filteredOptions[i]; + var baseGameSetting = option.Data; + var viewSettingsInfoPanel = Object.Instantiate(viewPanel.infoPanelOrigin, viewPanel.transform, true); + viewSettingsInfoPanel.transform.localScale = Vector3.one; + viewSettingsInfoPanel.transform.localPosition = new Vector3(viewPanel.xPosStart, num, -2f); + + var value = option.GetFloatData(); + + if (baseGameSetting == null) + { + continue; + } + + if (baseGameSetting.Type == OptionTypes.Checkbox) + { + viewSettingsInfoPanel.SetInfoCheckbox(baseGameSetting.Title, maskLayer, value > 0f); + } + else + { + viewSettingsInfoPanel.SetInfo(baseGameSetting.Title, baseGameSetting.GetValueString(value), maskLayer); + } + + num -= spacingY; + if (i > 0) + { + num2 += 0.8f; + } + } + + return num2; } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Options/NotificationPopperPatch.cs b/MiraAPI/Patches/Options/NotificationPopperPatch.cs index faa24eb..d13b854 100644 --- a/MiraAPI/Patches/Options/NotificationPopperPatch.cs +++ b/MiraAPI/Patches/Options/NotificationPopperPatch.cs @@ -18,26 +18,26 @@ public static bool RoleChangeMsgPatch( [HarmonyArgument(3)] RoleTeamTypes teamType, [HarmonyArgument(4)] bool playSound) { - if (CustomRoleManager.CustomRoles.Values.Any(role => role.StringName == key)) + if (CustomRoleManager.CustomRoles.Values.All(role => role.StringName != key)) { - var text = teamType == RoleTeamTypes.Crewmate - ? Palette.CrewmateSettingChangeText.ToTextColor() - : Palette.ImpostorRed.ToTextColor(); + return true; + } - var item = DestroyableSingleton.Instance.GetString( - StringNames.LobbyChangeSettingNotificationRole, - "", - text, - DestroyableSingleton.Instance.GetString(key, Array.Empty()), - "", - "" + roleCount + "", - "" + roleChance + "%" - ); + var text = teamType == RoleTeamTypes.Crewmate + ? Palette.CrewmateSettingChangeText.ToTextColor() + : Palette.ImpostorRed.ToTextColor(); - __instance.SettingsChangeMessageLogic(key, item, playSound); - return false; - } + var item = DestroyableSingleton.Instance.GetString( + StringNames.LobbyChangeSettingNotificationRole, + "", + text, + DestroyableSingleton.Instance.GetString(key, Array.Empty()), + "", + "" + roleCount + "", + "" + roleChance + "%" + ); - return true; + __instance.SettingsChangeMessageLogic(key, item, playSound); + return false; } } diff --git a/MiraAPI/Patches/Options/OptionsPatches.cs b/MiraAPI/Patches/Options/OptionsPatches.cs index de7da75..a3be128 100644 --- a/MiraAPI/Patches/Options/OptionsPatches.cs +++ b/MiraAPI/Patches/Options/OptionsPatches.cs @@ -16,7 +16,8 @@ public static bool ToggleInit(ToggleOption __instance) return true; } - __instance.TitleText.text = DestroyableSingleton.Instance.GetString(__instance.Title, Array.Empty()); + __instance.TitleText.text = + DestroyableSingleton.Instance.GetString(__instance.Title, Array.Empty()); return false; } @@ -37,7 +38,8 @@ public static bool NumberInit(NumberOption __instance) return true; } - __instance.TitleText.text = DestroyableSingleton.Instance.GetString(__instance.Title, Array.Empty()); + __instance.TitleText.text = + DestroyableSingleton.Instance.GetString(__instance.Title, Array.Empty()); return false; } @@ -58,8 +60,11 @@ public static bool StringInit(StringOption __instance) return true; } - __instance.TitleText.text = DestroyableSingleton.Instance.GetString(__instance.Title, Array.Empty()); - __instance.ValueText.text = DestroyableSingleton.Instance.GetString(__instance.Values[__instance.Value], Array.Empty()); + __instance.TitleText.text = + DestroyableSingleton.Instance.GetString(__instance.Title, Array.Empty()); + __instance.ValueText.text = DestroyableSingleton.Instance.GetString( + __instance.Values[__instance.Value], + Array.Empty()); return false; } @@ -70,4 +75,4 @@ public static bool StringUpdate(StringOption __instance) { return !__instance.IsCustom(); } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 8ba2c93..d3eb9f7 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -57,7 +57,7 @@ public static bool PatchStart(RolesSettingsMenu __instance) var crewRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values .OfType() .Where(role => role is { Team: ModdedRoleTeams.Crewmate, HideSettings: false }) - .ToList(); + .ToList() ?? []; if (crewRoles is { Count: > 0 }) { @@ -83,7 +83,7 @@ public static bool PatchStart(RolesSettingsMenu __instance) var impRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values .OfType() .Where(role => role is { Team: ModdedRoleTeams.Impostor, HideSettings: false }) - .ToList(); + .ToList() ?? []; if (impRoles is { Count: > 0 }) { @@ -110,7 +110,7 @@ public static bool PatchStart(RolesSettingsMenu __instance) var neutRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values .OfType() .Where(role => role is { Team: ModdedRoleTeams.Neutral, HideSettings: false }) - .ToList(); + .ToList() ?? []; if (neutRoles is { Count: > 0 }) { diff --git a/MiraAPI/Patches/Options/SettingPatches.cs b/MiraAPI/Patches/Options/SettingPatches.cs index 1b228a3..3c20bfe 100644 --- a/MiraAPI/Patches/Options/SettingPatches.cs +++ b/MiraAPI/Patches/Options/SettingPatches.cs @@ -9,7 +9,7 @@ namespace MiraAPI.Patches.Options; /// Patches for the various game settings. /// [HarmonyPatch] -public class SettingPatches +public static class SettingPatches { /// /// Prefix for the method. Adds support for custom number suffixes. @@ -25,33 +25,24 @@ public static bool ValueStringPatch( ref string __result, [HarmonyArgument(0)] float value) { - var result = string.Empty; + string result; var suffix = (MiraNumberSuffixes)__instance.SuffixType; if (__instance.ZeroIsInfinity && Mathf.Abs(value) < 0.0001f) { result = ""; } - else if (suffix == MiraNumberSuffixes.None) - { - result = value.ToString(__instance.FormatString); - } - else if (suffix == MiraNumberSuffixes.Multiplier) - { - result = value.ToString(__instance.FormatString) + "x"; - } - else if (suffix == MiraNumberSuffixes.Percent) - { - result = value.ToString(__instance.FormatString) + "%"; - } else { - result = DestroyableSingleton.Instance.GetString( - StringNames.GameSecondsAbbrev, - (Il2CppSystem.Object[]) - [ - value.ToString(__instance.FormatString, CultureInfo.InvariantCulture) - ]); + result = suffix switch + { + MiraNumberSuffixes.None => value.ToString(__instance.FormatString, NumberFormatInfo.InvariantInfo), + MiraNumberSuffixes.Multiplier => value.ToString(__instance.FormatString, NumberFormatInfo.InvariantInfo) + "x", + MiraNumberSuffixes.Percent => value.ToString(__instance.FormatString, NumberFormatInfo.InvariantInfo) + "%", + _ => DestroyableSingleton.Instance.GetString( + StringNames.GameSecondsAbbrev, + (Il2CppSystem.Object[]) [value.ToString(__instance.FormatString, CultureInfo.InvariantCulture)]), + }; } __result = result; diff --git a/MiraAPI/Patches/Roles/NameTagPatch.cs b/MiraAPI/Patches/Roles/NameTagPatch.cs index b427112..821db1c 100644 --- a/MiraAPI/Patches/Roles/NameTagPatch.cs +++ b/MiraAPI/Patches/Roles/NameTagPatch.cs @@ -32,4 +32,4 @@ public static bool GetPatch([HarmonyArgument(0)] RoleBehaviour otherPlayerRole, return false; } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Roles/RoleBehaviourPatches.cs b/MiraAPI/Patches/Roles/RoleBehaviourPatches.cs index 5879df8..98cb5e6 100644 --- a/MiraAPI/Patches/Roles/RoleBehaviourPatches.cs +++ b/MiraAPI/Patches/Roles/RoleBehaviourPatches.cs @@ -22,4 +22,4 @@ public static bool PrefixTeamColorGetter(RoleBehaviour __instance, ref Color __r __result = behaviour.RoleColor; return false; } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Roles/RoleManagerPatches.cs b/MiraAPI/Patches/Roles/RoleManagerPatches.cs index 350767f..4f75a16 100644 --- a/MiraAPI/Patches/Roles/RoleManagerPatches.cs +++ b/MiraAPI/Patches/Roles/RoleManagerPatches.cs @@ -18,7 +18,9 @@ public static void ModifierSelectionPatches(RoleManager __instance) return; } - ModifierManager.AssignModifiers(PlayerControl.AllPlayerControls.ToArray().Where(plr => !plr.Data.IsDead && !plr.Data.Disconnected).ToList()); + ModifierManager.AssignModifiers( + PlayerControl.AllPlayerControls.ToArray().Where(plr => !plr.Data.IsDead && !plr.Data.Disconnected) + .ToList()); } [HarmonyPrefix] @@ -39,9 +41,8 @@ public static bool AssignRoleOnDeath(RoleManager __instance, [HarmonyArgument(0) { return true; } - + plr.RpcSetRole(role.GhostRole); return false; - } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs b/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs index c146b8a..ed4f7b7 100644 --- a/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs +++ b/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs @@ -14,13 +14,14 @@ public static class RoleOptionsCollectionPatch [HarmonyPatch(nameof(RoleOptionsCollectionV08.GetChancePerGame))] public static bool GetChancePrefix([HarmonyArgument(0)] RoleTypes roleType, ref int __result) { - if (!CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole)) + if (!CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole) || customRole == null) { return true; } if (customRole.HideSettings) { + __result = 0; return false; } @@ -36,18 +37,19 @@ public static bool GetChancePrefix([HarmonyArgument(0)] RoleTypes roleType, ref [HarmonyPatch(nameof(RoleOptionsCollectionV08.GetNumPerGame))] public static bool GetNumPrefix([HarmonyArgument(0)] RoleTypes roleType, ref int __result) { - if (!CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole)) + if (!CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole) || customRole == null) { return true; } + if (customRole.HideSettings) { + __result = 0; return false; } customRole.ParentMod.PluginConfig.TryGetEntry(customRole.NumConfigDefinition, out var entry); __result = entry.Value; return false; - } } diff --git a/MiraAPI/Patches/Roles/TaskAdderPatch.cs b/MiraAPI/Patches/Roles/TaskAdderPatch.cs index 9db0f92..8a3ae90 100644 --- a/MiraAPI/Patches/Roles/TaskAdderPatch.cs +++ b/MiraAPI/Patches/Roles/TaskAdderPatch.cs @@ -15,9 +15,8 @@ namespace MiraAPI.Patches.Roles; [HarmonyPatch] public static class TaskAdderPatch { - private static TaskFolder _rolesFolder; - private static readonly System.Collections.Generic.Dictionary ModsFolders = new(); - private static Scroller _scroller; + private static readonly System.Collections.Generic.Dictionary ModsFolders = []; + private static Scroller? _scroller; [HarmonyPostfix] [HarmonyPatch(typeof(TaskAdderGame), nameof(TaskAdderGame.Begin))] @@ -56,18 +55,17 @@ public static void AddRolesFolder(TaskAdderGame __instance) __instance.TaskParent = inner.transform; - _rolesFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); - _rolesFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); - _rolesFolder.gameObject.SetActive(false); - _rolesFolder.FolderName = "Roles"; - _rolesFolder.name = "RolesFolder"; + var rolesFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); + rolesFolder.gameObject.SetActive(false); + rolesFolder.FolderName = "Roles"; + rolesFolder.name = "RolesFolder"; foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins()) { var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); newFolder.FolderName = newFolder.name = plugin.PluginInfo.Metadata.Name; newFolder.gameObject.SetActive(false); - _rolesFolder.SubFolders.Add(newFolder); + rolesFolder.SubFolders.Add(newFolder); if (!ModsFolders.ContainsKey(plugin.PluginInfo.Metadata.Name)) { @@ -75,7 +73,7 @@ public static void AddRolesFolder(TaskAdderGame __instance) } } - __instance.Root.SubFolders.Add(_rolesFolder); + __instance.Root.SubFolders.Add(rolesFolder); __instance.GoToRoot(); } @@ -92,7 +90,12 @@ public static void RoleGetterPatch(TaskAddButton __instance) __instance.RolloverHandler.OutColor = __instance.FileImage.color; } - private static void AddFileAsChildCustom(this TaskAdderGame instance, TaskFolder taskFolder, TaskAddButton item, ref float xCursor, ref float yCursor, ref float maxHeight) + private static void AddFileAsChildCustom( + this TaskAdderGame instance, + TaskAddButton item, + ref float xCursor, + ref float yCursor, + ref float maxHeight) { item.transform.SetParent(instance.TaskParent); item.transform.localPosition = new Vector3(xCursor, yCursor, 0f); @@ -105,9 +108,9 @@ private static void AddFileAsChildCustom(this TaskAdderGame instance, TaskFolder yCursor -= maxHeight; maxHeight = 0f; } + instance.ActiveItems.Add(item.transform); } - // yes it might be crazy patching the entire method, but i tried so many other methods and only this works :cry: [HarmonyPrefix] @@ -121,11 +124,13 @@ public static bool ShowPatch(TaskAdderGame __instance, [HarmonyArgument(0)] Task stringBuilder.Append(__instance.Hierarchy.ToArray()[i].FolderName); stringBuilder.Append("\\"); } + __instance.PathText.text = stringBuilder.ToString(); for (var j = 0; j < __instance.ActiveItems.Count; j++) { Object.Destroy(__instance.ActiveItems.ToArray()[j].gameObject); } + __instance.ActiveItems.Clear(); var num = 0f; var num2 = 0f; @@ -148,6 +153,7 @@ public static bool ShowPatch(TaskAdderGame __instance, [HarmonyArgument(0)] Task num2 -= num3; num3 = 0f; } + __instance.ActiveItems.Add(folderTransform); if (!taskFolder2 || !taskFolder2.Button) { @@ -155,7 +161,8 @@ public static bool ShowPatch(TaskAdderGame __instance, [HarmonyArgument(0)] Task } ControllerManager.Instance.AddSelectableUiElement(taskFolder2.Button); - if (!string.IsNullOrEmpty(__instance.restorePreviousSelectionByFolderName) && taskFolder2.FolderName.Equals(__instance.restorePreviousSelectionByFolderName)) + if (!string.IsNullOrEmpty(__instance.restorePreviousSelectionByFolderName) && + taskFolder2.FolderName.Equals(__instance.restorePreviousSelectionByFolderName, System.StringComparison.Ordinal)) { __instance.restorePreviousSelectionFound = taskFolder2.Button; } @@ -173,63 +180,90 @@ public static bool ShowPatch(TaskAdderGame __instance, [HarmonyArgument(0)] Task { var taskAddButton = Object.Instantiate(__instance.TaskPrefab); taskAddButton.MyTask = list.ToArray()[l]; - if (taskAddButton.MyTask.TaskType == TaskTypes.DivertPower) + switch (taskAddButton.MyTask.TaskType) { - var targetSystem = taskAddButton.MyTask.Cast().TargetSystem; - taskAddButton.Text.text = DestroyableSingleton.Instance.GetString(StringNames.DivertPowerTo, DestroyableSingleton.Instance.GetString(targetSystem)); + case TaskTypes.DivertPower: + { + var targetSystem = taskAddButton.MyTask.Cast().TargetSystem; + taskAddButton.Text.text = DestroyableSingleton.Instance.GetString( + StringNames.DivertPowerTo, + DestroyableSingleton.Instance.GetString(targetSystem)); + break; + } + case TaskTypes.FixWeatherNode: + { + var nodeId = ((WeatherNodeTask)taskAddButton.MyTask).NodeId; + taskAddButton.Text.text = + DestroyableSingleton.Instance.GetString( + StringNames.FixWeatherNode, + Array.Empty()) + " " + + DestroyableSingleton.Instance.GetString( + WeatherSwitchGame.ControlNames[nodeId], + Array.Empty()); + break; + } + default: + taskAddButton.Text.text = + DestroyableSingleton.Instance.GetString(taskAddButton.MyTask.TaskType); + break; } - else if (taskAddButton.MyTask.TaskType == TaskTypes.FixWeatherNode) + + __instance.AddFileAsChildCustom(taskAddButton, ref num, ref num2, ref num3); + if (taskAddButton.Button == null) { - var nodeId = ((WeatherNodeTask)taskAddButton.MyTask).NodeId; - taskAddButton.Text.text = DestroyableSingleton.Instance.GetString(StringNames.FixWeatherNode, Array.Empty()) + " " + DestroyableSingleton.Instance.GetString(WeatherSwitchGame.ControlNames[nodeId], - Array.Empty()); + continue; } - else + + ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button); + if (__instance.Hierarchy.Count == 1 || flag) { - taskAddButton.Text.text = DestroyableSingleton.Instance.GetString(taskAddButton.MyTask.TaskType); + continue; } - __instance.AddFileAsChildCustom(taskFolder, taskAddButton, ref num, ref num2, ref num3); - if (taskAddButton != null && taskAddButton.Button != null) + + var component = + ControllerManager.Instance.CurrentUiState.CurrentSelection.GetComponent(); + if (component != null) { - ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button); - if (__instance.Hierarchy.Count != 1 && !flag) - { - var component = ControllerManager.Instance.CurrentUiState.CurrentSelection.GetComponent(); - if (component != null) - { - __instance.restorePreviousSelectionByFolderName = component.FolderName; - } - ControllerManager.Instance.SetDefaultSelection(taskAddButton.Button); - flag = true; - } + __instance.restorePreviousSelectionByFolderName = component.FolderName; } + + ControllerManager.Instance.SetDefaultSelection(taskAddButton.Button); + flag = true; } + if (taskFolder.FolderName == "Roles") { for (var m = 0; m < DestroyableSingleton.Instance.AllRoles.Length; m++) { var roleBehaviour = DestroyableSingleton.Instance.AllRoles[m]; - if (roleBehaviour.Role != RoleTypes.ImpostorGhost && roleBehaviour.Role != RoleTypes.CrewmateGhost && !CustomRoleManager.CustomRoles.ContainsKey((ushort)roleBehaviour.Role)) + if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost || + CustomRoleManager.CustomRoles.ContainsKey((ushort)roleBehaviour.Role)) + { + continue; + } + + var taskAddButton2 = Object.Instantiate(__instance.RoleButton); + taskAddButton2.SafePositionWorld = __instance.SafePositionWorld; + taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe"; + __instance.AddFileAsChildCustom(taskAddButton2, ref num, ref num2, ref num3); + taskAddButton2.Role = roleBehaviour; + + if (taskAddButton2.Button == null) + { + continue; + } + + ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button); + switch (m) { - var taskAddButton2 = Object.Instantiate(__instance.RoleButton); - taskAddButton2.SafePositionWorld = __instance.SafePositionWorld; - taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe"; - __instance.AddFileAsChildCustom(_rolesFolder, taskAddButton2, ref num, ref num2, ref num3); - taskAddButton2.Role = roleBehaviour; - if (taskAddButton2 != null && taskAddButton2.Button != null) - { - ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button); - if (m == 0 && __instance.restorePreviousSelectionFound != null) - { - ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound); - __instance.restorePreviousSelectionByFolderName = string.Empty; - __instance.restorePreviousSelectionFound = null; - } - else if (m == 0) - { - ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button); - } - } + case 0 when __instance.restorePreviousSelectionFound != null: + ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound); + __instance.restorePreviousSelectionByFolderName = string.Empty; + __instance.restorePreviousSelectionFound = null; + break; + case 0: + ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button); + break; } } } @@ -240,27 +274,33 @@ public static bool ShowPatch(TaskAdderGame __instance, [HarmonyArgument(0)] Task for (var m = 0; m < plugin.CustomRoles.Count; m++) { var roleBehaviour = plugin.CustomRoles.ElementAt(m).Value; - if (roleBehaviour.Role != RoleTypes.ImpostorGhost && roleBehaviour.Role != RoleTypes.CrewmateGhost && !roleBehaviour.IsDead) + if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost || + roleBehaviour.IsDead) { - var taskAddButton2 = Object.Instantiate(__instance.RoleButton); - taskAddButton2.SafePositionWorld = __instance.SafePositionWorld; - taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe"; - __instance.AddFileAsChildCustom(_rolesFolder, taskAddButton2, ref num, ref num2, ref num3); - taskAddButton2.Role = roleBehaviour; - if (taskAddButton2 != null && taskAddButton2.Button != null) - { - ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button); - if (m == 0 && __instance.restorePreviousSelectionFound != null) - { - ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound); - __instance.restorePreviousSelectionByFolderName = string.Empty; - __instance.restorePreviousSelectionFound = null; - } - else if (m == 0) - { - ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button); - } - } + continue; + } + + var taskAddButton2 = Object.Instantiate(__instance.RoleButton); + taskAddButton2.SafePositionWorld = __instance.SafePositionWorld; + taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe"; + __instance.AddFileAsChildCustom(taskAddButton2, ref num, ref num2, ref num3); + taskAddButton2.Role = roleBehaviour; + if (taskAddButton2.Button == null) + { + continue; + } + + ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button); + switch (m) + { + case 0 when __instance.restorePreviousSelectionFound != null: + ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound); + __instance.restorePreviousSelectionByFolderName = string.Empty; + __instance.restorePreviousSelectionFound = null; + break; + case 0: + ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button); + break; } } } @@ -271,17 +311,19 @@ public static bool ShowPatch(TaskAdderGame __instance, [HarmonyArgument(0)] Task chip.GetComponent().maskInteraction = SpriteMaskInteraction.VisibleInsideMask; } - if (_scroller) + if (_scroller && _scroller != null) { _scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 3, 1f); _scroller.SetYBoundsMin(0.0f); _scroller.ScrollToTop(); } + if (__instance.Hierarchy.Count == 1) { ControllerManager.Instance.SetBackButton(__instance.BackButton); return false; } + ControllerManager.Instance.SetBackButton(__instance.FolderBackButton); return false; } diff --git a/MiraAPI/Patches/Roles/TaskPanelPatch.cs b/MiraAPI/Patches/Roles/TaskPanelPatch.cs index e93ab10..3d4ae14 100644 --- a/MiraAPI/Patches/Roles/TaskPanelPatch.cs +++ b/MiraAPI/Patches/Roles/TaskPanelPatch.cs @@ -24,7 +24,12 @@ public static bool UpdatePrefix(TaskPanelBehaviour __instance) var vector = __instance.background.sprite.bounds.extents; var vector2 = __instance.tab.sprite.bounds.extents; - transform.localScale = __instance.taskText.textBounds.size.x > 0f ? new Vector3(__instance.taskText.textBounds.size.x + 0.4f, __instance.taskText.textBounds.size.y + 0.3f, 1f) : Vector3.zero; + transform.localScale = __instance.taskText.textBounds.size.x > 0f + ? new Vector3( + __instance.taskText.textBounds.size.x + 0.4f, + __instance.taskText.textBounds.size.y + 0.3f, + 1f) + : Vector3.zero; vector.y = -vector.y; vector = vector.Mul(transform.localScale); @@ -39,20 +44,24 @@ public static bool UpdatePrefix(TaskPanelBehaviour __instance) { return false; } - var closePosition = new Vector3(-__instance.background.sprite.bounds.size.x * __instance.background.transform.localScale.x, __instance.closedPosition.y, __instance.closedPosition.z); + + var closePosition = new Vector3( + -__instance.background.sprite.bounds.size.x * __instance.background.transform.localScale.x, + __instance.closedPosition.y, + __instance.closedPosition.z); __instance.closedPosition = closePosition; - if (__instance.open) - { - __instance.timer = Mathf.Min(1f, __instance.timer + Time.deltaTime / __instance.animationTimeSeconds); - } - else - { - __instance.timer = Mathf.Max(0f, __instance.timer - Time.deltaTime / __instance.animationTimeSeconds); - } - Vector3 relativePos; - relativePos = new Vector3(Mathf.SmoothStep(__instance.closedPosition.x, __instance.openPosition.x, __instance.timer), Mathf.SmoothStep(__instance.closedPosition.y, __instance.openPosition.y, __instance.timer), __instance.openPosition.z); - __instance.transform.localPosition = AspectPosition.ComputePosition(AspectPosition.EdgeAlignments.LeftTop, relativePos); + + __instance.timer = __instance.open + ? Mathf.Min(1f, __instance.timer + Time.deltaTime / __instance.animationTimeSeconds) + : Mathf.Max(0f, __instance.timer - Time.deltaTime / __instance.animationTimeSeconds); + + var relativePos = new Vector3( + Mathf.SmoothStep(__instance.closedPosition.x, __instance.openPosition.x, __instance.timer), + Mathf.SmoothStep(__instance.closedPosition.y, __instance.openPosition.y, __instance.timer), + __instance.openPosition.z); + __instance.transform.localPosition = AspectPosition.ComputePosition( + AspectPosition.EdgeAlignments.LeftTop, + relativePos); return false; } - -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Roles/VentOutlinePatch.cs b/MiraAPI/Patches/Roles/VentOutlinePatch.cs index 401d450..5e50e96 100644 --- a/MiraAPI/Patches/Roles/VentOutlinePatch.cs +++ b/MiraAPI/Patches/Roles/VentOutlinePatch.cs @@ -5,6 +5,9 @@ namespace MiraAPI.Patches.Roles; +/// +/// Set vent outline color based on the player's role. +/// [HarmonyPatch(typeof(Vent), nameof(Vent.SetOutline))] public static class VentOutlinePatch { From 0e56a30c5c7057cf0ae0bb1b54602cc263d5d7de Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:19:32 -0400 Subject: [PATCH 22/36] fix most non-documentation warnings --- .editorconfig | 2 + MiraAPI.Example/ExampleAssets.cs | 4 +- MiraAPI.Example/ExampleColors.cs | 6 +-- MiraAPI.Example/MiraDebugWindow.cs | 18 +++----- MiraAPI/GameModes/CustomGameModeManager.cs | 18 ++++---- MiraAPI/GameModes/DefaultMode.cs | 2 +- .../GameModes/RegisterGameModeAttribute.cs | 2 +- MiraAPI/GameOptions/OptionGroupSingleton.cs | 2 + MiraAPI/Hud/CustomButtonSingleton.cs | 2 + MiraAPI/Hud/RegisterButtonAttribute.cs | 2 +- MiraAPI/MiraApiPlugin.cs | 6 +-- MiraAPI/Modifiers/ModifierComponent.cs | 2 +- MiraAPI/Networking/SyncModifiersRpc.cs | 2 +- MiraAPI/Networking/SyncOptionsRpc.cs | 2 +- MiraAPI/Networking/SyncRoleOptionsRpc.cs | 2 +- .../Patches/Colors/ScrollingColorsPatch.cs | 10 ++--- MiraAPI/Patches/GameModes/ConsolePatches.cs | 5 +-- MiraAPI/Patches/GameModes/DeadBodyPatch.cs | 6 +-- MiraAPI/Patches/GameModes/GameLogicPatches.cs | 6 +-- .../Patches/GameModes/ImpostorTargetPatch.cs | 8 ++-- .../Patches/GameModes/MapBehaviourPatch.cs | 6 +-- MiraAPI/Patches/GameModes/OnDeathPatch.cs | 6 +-- MiraAPI/Patches/GameModes/VentPatches.cs | 8 +--- MiraAPI/Patches/Modifiers/VentPatches.cs | 2 +- .../Patches/Options/GameSettingMenuPatches.cs | 1 - MiraAPI/Patches/Roles/TaskPanelPatch.cs | 4 +- MiraAPI/PluginLoading/IMiraPlugin.cs | 2 +- MiraAPI/PluginLoading/MiraPluginManager.cs | 2 +- MiraAPI/Roles/ModdedRoleTeams.cs | 4 +- MiraAPI/Roles/RegisterCustomRoleAttribute.cs | 2 +- MiraAPI/Roles/RoleId.cs | 4 +- MiraAPI/Utilities/Assets/MiraAssets.cs | 6 +-- MiraAPI/Utilities/Assets/SpriteTools.cs | 24 ++++------ MiraAPI/Utilities/MiraNumberSuffixes.cs | 4 +- MiraAPI/Utilities/ShaderID.cs | 45 +++++++++---------- 35 files changed, 102 insertions(+), 125 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..017cd3a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,2 @@ +[*.{cs,vb}] +dotnet_diagnostic.CA1707.severity = none \ No newline at end of file diff --git a/MiraAPI.Example/ExampleAssets.cs b/MiraAPI.Example/ExampleAssets.cs index a96dfc8..732d9f7 100644 --- a/MiraAPI.Example/ExampleAssets.cs +++ b/MiraAPI.Example/ExampleAssets.cs @@ -6,7 +6,7 @@ public static class ExampleAssets { public static LoadableResourceAsset ExampleButton { get; } = new("MiraAPI.Example.Resources.ExampleButton.png"); - // Credit to EpicHorrors for the teleport button asset. + // Credit to EpicHorrors for the teleport button asset. public static LoadableResourceAsset TeleportButton { get; } = new("MiraAPI.Example.Resources.TeleportButton.png"); public static LoadableResourceAsset Banner { get; } = new("MiraAPI.Example.Resources.FortniteBanner.jpeg"); -} \ No newline at end of file +} diff --git a/MiraAPI.Example/ExampleColors.cs b/MiraAPI.Example/ExampleColors.cs index 3212093..6833d3c 100644 --- a/MiraAPI.Example/ExampleColors.cs +++ b/MiraAPI.Example/ExampleColors.cs @@ -6,9 +6,9 @@ namespace MiraAPI.Example; [RegisterCustomColors] public static class ExampleColors { - public static CustomColor Cerulean { get; } = new("Cerulean", new Color(0.0f, 0.48f, 0.65f)); + public static CustomColor Cerulean { get; } = new("Cerulean", new Color(0.0f, 0.48f, 0.65f)); public static CustomColor Rose { get; } = new("Rose", new Color(0.98f, 0.26f, 0.62f)); - + public static CustomColor Gold { get; } = new("Gold", new Color(1.0f, 0.84f, 0.0f)); -} \ No newline at end of file +} diff --git a/MiraAPI.Example/MiraDebugWindow.cs b/MiraAPI.Example/MiraDebugWindow.cs index 9b0fa7e..13a41d4 100644 --- a/MiraAPI.Example/MiraDebugWindow.cs +++ b/MiraAPI.Example/MiraDebugWindow.cs @@ -4,20 +4,16 @@ using UnityEngine; namespace MiraAPI.Example; + [RegisterInIl2Cpp] -public class MiraDebugWindow(IntPtr ptr) : MonoBehaviour(ptr) +public class MiraDebugWindow(IntPtr cppPtr) : MonoBehaviour(cppPtr) { - public readonly DragWindow DebuggingWindow = new(new Rect(10, 10, 0, 0), "MIRA API DEBUGGING", () => - { - if (GUILayout.Button("Test modifier")) - { - //PlayerControl.LocalPlayer.AddModifier(); - } - if (GUILayout.Button("Remove modifier")) + public DragWindow DebuggingWindow { get; } = new( + new Rect(10, 10, 0, 0), + "MIRA API DEBUGGING", + () => { - //PlayerControl.LocalPlayer.RemoveModifier(); - } - }) + }) { Enabled = true, }; diff --git a/MiraAPI/GameModes/CustomGameModeManager.cs b/MiraAPI/GameModes/CustomGameModeManager.cs index 028eb94..388eb5d 100644 --- a/MiraAPI/GameModes/CustomGameModeManager.cs +++ b/MiraAPI/GameModes/CustomGameModeManager.cs @@ -6,12 +6,12 @@ namespace MiraAPI.GameModes; /// -/// Manages custom gamemodes +/// Manages custom gamemodes. /// public static class CustomGameModeManager { /// - /// List of registered gamemodes + /// List of registered gamemodes. /// internal static readonly Dictionary GameModes = []; @@ -21,14 +21,14 @@ public static bool IsDefault() } /// - /// Current gamemode + /// Current gamemode. /// public static CustomGameMode? ActiveMode { get; internal set; } = new DefaultMode(); /// - /// Set current gamemode + /// Set current gamemode. /// - /// gamemode ID + /// gamemode ID. public static void SetGameMode(int id) { if (GameModes.TryGetValue(id, out var gameMode)) @@ -41,9 +41,9 @@ public static void SetGameMode(int id) } /// - /// Register gamemode from type + /// Register gamemode from type. /// - /// Type of gamemode class, should inherit from + /// Type of gamemode class, should inherit from . internal static void RegisterGameMode(Type gameModeType) { if (!typeof(CustomGameMode).IsAssignableFrom(gameModeType)) @@ -59,7 +59,7 @@ internal static void RegisterGameMode(Type gameModeType) Logger.Error($"Failed to create instance of {gameModeType.Name}"); return; } - + if (GameModes.Any(x => x.Key == gameMode.Id)) { Logger.Error($"ID for gamemode {gameMode.Name} already exists!"); @@ -68,4 +68,4 @@ internal static void RegisterGameMode(Type gameModeType) GameModes.Add(gameMode.Id, gameMode); } -} \ No newline at end of file +} diff --git a/MiraAPI/GameModes/DefaultMode.cs b/MiraAPI/GameModes/DefaultMode.cs index 2e7fcb0..73c4ca8 100644 --- a/MiraAPI/GameModes/DefaultMode.cs +++ b/MiraAPI/GameModes/DefaultMode.cs @@ -6,4 +6,4 @@ public class DefaultMode : CustomGameMode public override string Name => "Default"; public override string Description => "Default Among Us GameMode"; public override int Id => 0; -} \ No newline at end of file +} diff --git a/MiraAPI/GameModes/RegisterGameModeAttribute.cs b/MiraAPI/GameModes/RegisterGameModeAttribute.cs index b329d53..4c66c35 100644 --- a/MiraAPI/GameModes/RegisterGameModeAttribute.cs +++ b/MiraAPI/GameModes/RegisterGameModeAttribute.cs @@ -25,4 +25,4 @@ internal static void Register(Assembly assembly) } } } -} \ No newline at end of file +} diff --git a/MiraAPI/GameOptions/OptionGroupSingleton.cs b/MiraAPI/GameOptions/OptionGroupSingleton.cs index 8c7e2af..53b2029 100644 --- a/MiraAPI/GameOptions/OptionGroupSingleton.cs +++ b/MiraAPI/GameOptions/OptionGroupSingleton.cs @@ -13,5 +13,7 @@ public static class OptionGroupSingleton where T : AbstractOptionGroup /// /// Gets the instance of the option group. /// +#pragma warning disable CA1000 public static T Instance => _instance ??= ModdedOptionsManager.Groups.OfType().Single(); +#pragma warning restore CA1000 } diff --git a/MiraAPI/Hud/CustomButtonSingleton.cs b/MiraAPI/Hud/CustomButtonSingleton.cs index 3e732be..b338e61 100644 --- a/MiraAPI/Hud/CustomButtonSingleton.cs +++ b/MiraAPI/Hud/CustomButtonSingleton.cs @@ -13,5 +13,7 @@ public static class CustomButtonSingleton where T : CustomActionButton /// /// Gets the instance of the button. /// +#pragma warning disable CA1000 public static T Instance => _instance ??= CustomButtonManager.CustomButtons.OfType().Single(); +#pragma warning restore CA1000 } diff --git a/MiraAPI/Hud/RegisterButtonAttribute.cs b/MiraAPI/Hud/RegisterButtonAttribute.cs index feda3a8..82e0ca5 100644 --- a/MiraAPI/Hud/RegisterButtonAttribute.cs +++ b/MiraAPI/Hud/RegisterButtonAttribute.cs @@ -6,4 +6,4 @@ namespace MiraAPI.Hud; /// Attribute to register a button in the HUD. Necessary for the button to be recognized by Mira. /// [AttributeUsage(AttributeTargets.Class)] -public class RegisterButtonAttribute : Attribute; \ No newline at end of file +public class RegisterButtonAttribute : Attribute; diff --git a/MiraAPI/MiraApiPlugin.cs b/MiraAPI/MiraApiPlugin.cs index b928565..cfe314d 100644 --- a/MiraAPI/MiraApiPlugin.cs +++ b/MiraAPI/MiraApiPlugin.cs @@ -16,10 +16,10 @@ namespace MiraAPI; [ReactorModFlags(ModFlags.RequireOnAllClients)] public partial class MiraApiPlugin : BasePlugin { - public Harmony Harmony { get; } = new(Id); - private static MiraPluginManager PluginManager { get; set; } + private static MiraPluginManager? PluginManager { get; set; } - public static Color MiraColor = new Color32(238, 154, 112, 255); + public Harmony Harmony { get; } = new(Id); + public static Color MiraColor { get; } = new Color32(238, 154, 112, 255); public override void Load() { diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index 9517711..8b32dc2 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -18,7 +18,7 @@ namespace MiraAPI.Modifiers; /// The component for handling modifiers. /// [RegisterInIl2Cpp] -public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr) +public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) { /// /// Gets the active modifiers on the player. diff --git a/MiraAPI/Networking/SyncModifiersRpc.cs b/MiraAPI/Networking/SyncModifiersRpc.cs index 9598f0f..288e2c1 100644 --- a/MiraAPI/Networking/SyncModifiersRpc.cs +++ b/MiraAPI/Networking/SyncModifiersRpc.cs @@ -6,7 +6,7 @@ namespace MiraAPI.Networking; [RegisterCustomRpc((uint)MiraRpc.SyncModifiers)] -internal class SyncModifiersRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) +internal sealed class SyncModifiersRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) { public override RpcLocalHandling LocalHandling => RpcLocalHandling.None; diff --git a/MiraAPI/Networking/SyncOptionsRpc.cs b/MiraAPI/Networking/SyncOptionsRpc.cs index 288c690..a80ace3 100644 --- a/MiraAPI/Networking/SyncOptionsRpc.cs +++ b/MiraAPI/Networking/SyncOptionsRpc.cs @@ -7,7 +7,7 @@ namespace MiraAPI.Networking; // METHOD RPC DOESNT WORK WITH THE ARRAYS AND STUFF SO THIS IS HOW WE WILL DO IT FOR NOW [RegisterCustomRpc((uint)MiraRpc.SyncGameOptions)] -internal class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) +internal sealed class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) { public override RpcLocalHandling LocalHandling => RpcLocalHandling.None; diff --git a/MiraAPI/Networking/SyncRoleOptionsRpc.cs b/MiraAPI/Networking/SyncRoleOptionsRpc.cs index 290fc0f..adfe537 100644 --- a/MiraAPI/Networking/SyncRoleOptionsRpc.cs +++ b/MiraAPI/Networking/SyncRoleOptionsRpc.cs @@ -6,7 +6,7 @@ namespace MiraAPI.Networking; [RegisterCustomRpc((uint)MiraRpc.SyncRoleOptions)] -internal class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) +internal sealed class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc(plugin, id) { public override RpcLocalHandling LocalHandling => RpcLocalHandling.None; diff --git a/MiraAPI/Patches/Colors/ScrollingColorsPatch.cs b/MiraAPI/Patches/Colors/ScrollingColorsPatch.cs index 9fdc6d4..e89de23 100644 --- a/MiraAPI/Patches/Colors/ScrollingColorsPatch.cs +++ b/MiraAPI/Patches/Colors/ScrollingColorsPatch.cs @@ -7,8 +7,8 @@ namespace MiraAPI.Patches.Colors; [HarmonyPatch(typeof(PlayerTab))] public static class ScrollingColorsPatch { - /// Collider - private static BoxCollider2D _collider; + // Collider + private static BoxCollider2D? _collider; /// /// Add scrolling to the colors tab @@ -20,7 +20,7 @@ public static void AddScrollingToColorsTabPatch(PlayerTab __instance) { return; } - + var tab = PlayerCustomizationMenu.Instance.Tabs[1].Tab; if (__instance.scroller == null) @@ -31,7 +31,7 @@ public static void AddScrollingToColorsTabPatch(PlayerTab __instance) var maskObj = new GameObject { layer = 5, - name = "SpriteMask" + name = "SpriteMask", }; maskObj.transform.SetParent(__instance.transform); maskObj.transform.localPosition = new Vector3(0, 0, 0); @@ -55,4 +55,4 @@ public static void AddScrollingToColorsTabPatch(PlayerTab __instance) __instance.SetScrollerBounds(); } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/GameModes/ConsolePatches.cs b/MiraAPI/Patches/GameModes/ConsolePatches.cs index 50248e7..e2f24b2 100644 --- a/MiraAPI/Patches/GameModes/ConsolePatches.cs +++ b/MiraAPI/Patches/GameModes/ConsolePatches.cs @@ -1,5 +1,4 @@ - -/* +/* [HarmonyPatch] public static class ConsolePatches { @@ -69,4 +68,4 @@ public static bool MapCanUsePatch(MapConsole __instance, [HarmonyArgument(0)] Ne canUse = couldUse = true; return true; } -}*/ \ No newline at end of file +}*/ diff --git a/MiraAPI/Patches/GameModes/DeadBodyPatch.cs b/MiraAPI/Patches/GameModes/DeadBodyPatch.cs index edd4e58..07a2899 100644 --- a/MiraAPI/Patches/GameModes/DeadBodyPatch.cs +++ b/MiraAPI/Patches/GameModes/DeadBodyPatch.cs @@ -1,6 +1,4 @@ - - -/* +/* [HarmonyPatch(typeof(DeadBody))] public static class DeadBodyPatch { @@ -10,4 +8,4 @@ public static bool OnClickPatch(DeadBody __instance) { return CustomGameModeManager.ActiveMode == null || CustomGameModeManager.ActiveMode.CanReport(__instance); } -}*/ \ No newline at end of file +}*/ diff --git a/MiraAPI/Patches/GameModes/GameLogicPatches.cs b/MiraAPI/Patches/GameModes/GameLogicPatches.cs index 77eca63..a62be83 100644 --- a/MiraAPI/Patches/GameModes/GameLogicPatches.cs +++ b/MiraAPI/Patches/GameModes/GameLogicPatches.cs @@ -1,6 +1,4 @@ - - -/* +/* [HarmonyPatch] public static class GameLogicPatches { @@ -19,4 +17,4 @@ public static bool AssignRolesPatch(LogicRoleSelectionNormal __instance) CustomGameModeManager.ActiveMode?.AssignRoles(out runOriginal, __instance); return runOriginal; } -}*/ \ No newline at end of file +}*/ diff --git a/MiraAPI/Patches/GameModes/ImpostorTargetPatch.cs b/MiraAPI/Patches/GameModes/ImpostorTargetPatch.cs index 58f1187..ad0d9e0 100644 --- a/MiraAPI/Patches/GameModes/ImpostorTargetPatch.cs +++ b/MiraAPI/Patches/GameModes/ImpostorTargetPatch.cs @@ -1,6 +1,4 @@ - - -/* +/* /// /// Allow Impostors to kill each other if can kill is enabled in gamemode or friendly fire is toggled on /// @@ -16,11 +14,11 @@ public static bool Prefix(ImpostorRole __instance, [HarmonyArgument(0)] Networke { return true; } - + __result = target is { Disconnected: false, IsDead: false } && target.PlayerId != __instance.Player.PlayerId && !(target.Role == null) && !(target.Object == null) && !target.Object.inVent && !target.Object.inMovingPlat; return false; } -}*/ \ No newline at end of file +}*/ diff --git a/MiraAPI/Patches/GameModes/MapBehaviourPatch.cs b/MiraAPI/Patches/GameModes/MapBehaviourPatch.cs index 8f17b99..9a4bd8b 100644 --- a/MiraAPI/Patches/GameModes/MapBehaviourPatch.cs +++ b/MiraAPI/Patches/GameModes/MapBehaviourPatch.cs @@ -1,6 +1,4 @@ - - -/* +/* [HarmonyPatch(typeof(MapBehaviour))] public static class MapBehaviourPatch { @@ -17,4 +15,4 @@ public static bool ShowSabotagePatch(MapBehaviour __instance) return true; } -}*/ \ No newline at end of file +}*/ diff --git a/MiraAPI/Patches/GameModes/OnDeathPatch.cs b/MiraAPI/Patches/GameModes/OnDeathPatch.cs index 275321d..99f9211 100644 --- a/MiraAPI/Patches/GameModes/OnDeathPatch.cs +++ b/MiraAPI/Patches/GameModes/OnDeathPatch.cs @@ -1,6 +1,4 @@ - - -/* +/* [HarmonyPatch(typeof(KillAnimation))] public static class OnDeathPatch { @@ -10,4 +8,4 @@ public static void OnDeathPostfix([HarmonyArgument(0)] PlayerControl source, [Ha { CustomGameModeManager.ActiveMode?.OnDeath(target); } -}*/ \ No newline at end of file +}*/ diff --git a/MiraAPI/Patches/GameModes/VentPatches.cs b/MiraAPI/Patches/GameModes/VentPatches.cs index a86aa46..597c40a 100644 --- a/MiraAPI/Patches/GameModes/VentPatches.cs +++ b/MiraAPI/Patches/GameModes/VentPatches.cs @@ -1,10 +1,7 @@ - - -/* +/* [HarmonyPatch(typeof(Vent))] public static class VentPatches { - [HarmonyPrefix] [HarmonyPatch(nameof(Vent.CanUse))] public static bool CanUseVentPatch(Vent __instance, ref float __result, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse) @@ -47,5 +44,4 @@ public static bool SetOutlinePatch(Vent __instance, [HarmonyArgument(0)] bool on return false; } - -}*/ \ No newline at end of file +}*/ diff --git a/MiraAPI/Patches/Modifiers/VentPatches.cs b/MiraAPI/Patches/Modifiers/VentPatches.cs index 6646336..8c6c26a 100644 --- a/MiraAPI/Patches/Modifiers/VentPatches.cs +++ b/MiraAPI/Patches/Modifiers/VentPatches.cs @@ -42,4 +42,4 @@ public static void CanUseVentPatch(Vent __instance, ref float __result, [Harmony } __result = num; } -} \ No newline at end of file +} diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index 13c9036..a44b35c 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -154,7 +154,6 @@ private static void UpdateText(GameSettingMenu menu, GameOptionsMenu settings, R } roles.roleChances.Clear(); - roles.roleChances = null; roles.AdvancedRolesSettings.gameObject.SetActive(false); roles.RoleChancesSettings.gameObject.SetActive(true); roles.SetQuotaTab(); diff --git a/MiraAPI/Patches/Roles/TaskPanelPatch.cs b/MiraAPI/Patches/Roles/TaskPanelPatch.cs index 3d4ae14..431d47b 100644 --- a/MiraAPI/Patches/Roles/TaskPanelPatch.cs +++ b/MiraAPI/Patches/Roles/TaskPanelPatch.cs @@ -7,9 +7,9 @@ namespace MiraAPI.Patches.Roles; public static class TaskPanelPatch { /// - /// This patch is to override the automatic updating of the y position on the tab (which is in base game), + /// This patch is to override the automatic updating of the y position on the tab (which is in base game) /// because I can't change the custom tab y pos if it's being overriden every frame. - /// Im sure there is an easier/better way, but this is the fix that worked for me + /// Im sure there is an easier/better way, but this is the fix that worked for me. /// [HarmonyPrefix] [HarmonyPatch(nameof(TaskPanelBehaviour.Update))] diff --git a/MiraAPI/PluginLoading/IMiraPlugin.cs b/MiraAPI/PluginLoading/IMiraPlugin.cs index 9a71645..07928da 100644 --- a/MiraAPI/PluginLoading/IMiraPlugin.cs +++ b/MiraAPI/PluginLoading/IMiraPlugin.cs @@ -6,4 +6,4 @@ public interface IMiraPlugin { string OptionsTitleText { get; } public ConfigFile GetConfigFile(); -} \ No newline at end of file +} diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 794d74e..423cb09 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -15,7 +15,7 @@ namespace MiraAPI.PluginLoading; -internal class MiraPluginManager +internal sealed class MiraPluginManager { private readonly Dictionary _registeredPlugins = []; diff --git a/MiraAPI/Roles/ModdedRoleTeams.cs b/MiraAPI/Roles/ModdedRoleTeams.cs index ca7adad..e599cd7 100644 --- a/MiraAPI/Roles/ModdedRoleTeams.cs +++ b/MiraAPI/Roles/ModdedRoleTeams.cs @@ -4,5 +4,5 @@ public enum ModdedRoleTeams { Crewmate, Impostor, - Neutral -} \ No newline at end of file + Neutral, +} diff --git a/MiraAPI/Roles/RegisterCustomRoleAttribute.cs b/MiraAPI/Roles/RegisterCustomRoleAttribute.cs index db9a0e7..a6352f1 100644 --- a/MiraAPI/Roles/RegisterCustomRoleAttribute.cs +++ b/MiraAPI/Roles/RegisterCustomRoleAttribute.cs @@ -3,4 +3,4 @@ namespace MiraAPI.Roles; [AttributeUsage(AttributeTargets.Class)] -public class RegisterCustomRoleAttribute : Attribute; \ No newline at end of file +public class RegisterCustomRoleAttribute : Attribute; diff --git a/MiraAPI/Roles/RoleId.cs b/MiraAPI/Roles/RoleId.cs index 9970d62..05f9cd0 100644 --- a/MiraAPI/Roles/RoleId.cs +++ b/MiraAPI/Roles/RoleId.cs @@ -13,7 +13,7 @@ public static ushort Get() where T : RoleBehaviour return roleId; } - + public static ushort Get(Type type) { if (!CustomRoleManager.RoleIds.TryGetValue(type, out var roleId)) @@ -23,4 +23,4 @@ public static ushort Get(Type type) return roleId; } -} \ No newline at end of file +} diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs index 527fadb..514bd25 100644 --- a/MiraAPI/Utilities/Assets/MiraAssets.cs +++ b/MiraAPI/Utilities/Assets/MiraAssets.cs @@ -11,7 +11,5 @@ public static class MiraAssets public static readonly LoadableResourceAsset Cog = new("MiraAPI.Resources.Cog.png"); public static readonly LoadableResourceAsset Checkmark = new("MiraAPI.Resources.Checkmark.png"); public static readonly LoadableResourceAsset CheckmarkBox = new("MiraAPI.Resources.CheckMarkBox.png"); - - - public static readonly Color32 AcceptedTeal = new Color32(43, 233, 198, 255); -} \ No newline at end of file + public static readonly Color32 AcceptedTeal = new(43, 233, 198, 255); +} diff --git a/MiraAPI/Utilities/Assets/SpriteTools.cs b/MiraAPI/Utilities/Assets/SpriteTools.cs index d086a9e..bf79182 100644 --- a/MiraAPI/Utilities/Assets/SpriteTools.cs +++ b/MiraAPI/Utilities/Assets/SpriteTools.cs @@ -1,9 +1,9 @@ -using Il2CppInterop.Runtime; -using Il2CppInterop.Runtime.InteropTypes.Arrays; -using Reactor.Utilities.Extensions; -using System; +using System; using System.Reflection; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.InteropTypes.Arrays; using Reactor.Utilities; +using Reactor.Utilities.Extensions; using UnityEngine; namespace MiraAPI.Utilities.Assets; @@ -13,15 +13,9 @@ namespace MiraAPI.Utilities.Assets; /// public static class SpriteTools { - /// - /// Delegate for LoadImage. - /// - public delegate bool DLoadImage(IntPtr tex, IntPtr data, bool markNonReadable); + private delegate bool DLoadImage(IntPtr tex, IntPtr data, bool markNonReadable); - /// - /// The ICall for LoadImage. - /// - public static DLoadImage? ICallLoadImage; + private static DLoadImage? _iCallLoadImage; /// /// Load an image into a texture. @@ -32,18 +26,18 @@ public static class SpriteTools /// True if succeeded. public static bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable) { - ICallLoadImage ??= IL2CPP.ResolveICall("UnityEngine.ImageConversion::LoadImage"); + _iCallLoadImage ??= IL2CPP.ResolveICall("UnityEngine.ImageConversion::LoadImage"); var il2CPPArray = (Il2CppStructArray)data; - return ICallLoadImage.Invoke(tex.Pointer, il2CPPArray.Pointer, markNonReadable); + return _iCallLoadImage.Invoke(tex.Pointer, il2CPPArray.Pointer, markNonReadable); } /// /// Load a sprite from a resource path. /// /// The path to the resource. - /// A sprite made from the resource + /// A sprite made from the resource. /// The resource cannot be found. public static Sprite LoadSpriteFromPath(string resourcePath, Assembly assembly) { diff --git a/MiraAPI/Utilities/MiraNumberSuffixes.cs b/MiraAPI/Utilities/MiraNumberSuffixes.cs index 3682e2b..20ed21c 100644 --- a/MiraAPI/Utilities/MiraNumberSuffixes.cs +++ b/MiraAPI/Utilities/MiraNumberSuffixes.cs @@ -4,5 +4,5 @@ public enum MiraNumberSuffixes None, Multiplier, Seconds, - Percent -} \ No newline at end of file + Percent, +} diff --git a/MiraAPI/Utilities/ShaderID.cs b/MiraAPI/Utilities/ShaderID.cs index 08775bf..d250336 100644 --- a/MiraAPI/Utilities/ShaderID.cs +++ b/MiraAPI/Utilities/ShaderID.cs @@ -5,8 +5,8 @@ namespace MiraAPI.Utilities; public static class ShaderID { - private static readonly Dictionary Cache = new(); - + private static readonly Dictionary Cache = []; + public static int Get(string name) { if (Cache.TryGetValue(name, out var id)) @@ -18,74 +18,74 @@ public static int Get(string name) Cache[name] = id; return id; } - + // For player shader public static readonly int BodyColor = Shader.PropertyToID("_BodyColor"); public static readonly int BackColor = Shader.PropertyToID("_BackColor"); public static readonly int VisorColor = Shader.PropertyToID("_VisorColor"); - + // Main texture, very obviously used in any shader with a texture public static readonly int MainTex = Shader.PropertyToID("_MainTex"); - + // Any masking stuff, like MeetingHud bubbles public static readonly int Mask = Shader.PropertyToID("_Mask"); public static readonly int MaskComp = Shader.PropertyToID("_MaskComp"); public static readonly int MaskLayer = Shader.PropertyToID("_MaskLayer"); public static readonly int Stencil = Shader.PropertyToID("_Stencil"); public static readonly int StencilComp = Shader.PropertyToID("_StencilComp"); - + // Used in many tasks public static readonly int Color = Shader.PropertyToID("_Color"); - + // Has 2 uses in the game, provided for convenience public static readonly int Opacity = Shader.PropertyToID("_Opacity"); - + // Used once in CooldownHelpers, once in PowerBarMining public static readonly int NormalizedUvs = Shader.PropertyToID("_NormalizedUvs"); - + // Used in many consoles public static readonly int Outline = Shader.PropertyToID("_Outline"); public static readonly int OutlineColor = Shader.PropertyToID("_OutlineColor"); - + // Used in some consoles public static readonly int AddColor = Shader.PropertyToID("_AddColor"); - + // Has some uses in various locations public static readonly int Percent = Shader.PropertyToID("_Percent"); public static readonly int PercentY = Shader.PropertyToID("_PercentY"); public static readonly int Desat = Shader.PropertyToID("_Desat"); - + // Used in LightSource public static readonly int PlayerRadius = Shader.PropertyToID("_PlayerRadius"); public static readonly int LightRadius = Shader.PropertyToID("_LightRadius"); public static readonly int LightOffset = Shader.PropertyToID("_LightOffset"); public static readonly int FlashlightSize = Shader.PropertyToID("_FlashlightSize"); public static readonly int FlashlightAngle = Shader.PropertyToID("_FlashlightAngle"); - + // Used once for LightSourceGpuRenderer public static readonly int DepthCompressionValue = Shader.PropertyToID("_DepthCompressionValue"); - + // Used in ProgressTracker public static readonly int Buckets = Shader.PropertyToID("_Buckets"); public static readonly int FullBuckets = Shader.PropertyToID("_FullBuckets"); - + // Used once in IntroCutscene and EndGameManager public static readonly int Rad = Shader.PropertyToID("_Rad"); - + // Used only once for Quick Chat public static readonly int FaceColor = Shader.PropertyToID("_FaceColor"); - + // Used for NavigationMinigame public static readonly int CrossHair = Shader.PropertyToID("_CrossHair"); public static readonly int CrossColor = Shader.PropertyToID("_CrossColor"); - + // Used for both SurveillanceMinigame (Planet and Normal) public static readonly int Center = Shader.PropertyToID("_Center"); public static readonly int Color2 = Shader.PropertyToID("_Color2"); - + // Used in ReactorShipRoom public static readonly int Speed = Shader.PropertyToID("_Speed"); - + // Used only once in CourseMinigame public static readonly int AltTex = Shader.PropertyToID("_AltTex"); public static readonly int Perc = Shader.PropertyToID("_Perc"); @@ -93,8 +93,7 @@ public static int Get(string name) // Used once in TextMarquee public static readonly int VertexOffsetX = Shader.PropertyToID("_VertexOffsetX"); public static readonly int VertexOffsetY = Shader.PropertyToID("_VertexOffsetY"); - + // Used in VertLineBehaviour public static readonly int Fade = Shader.PropertyToID("_Fade"); - -} \ No newline at end of file +} From 51d08bb168569780524adff742717a0edb40dadb Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:22:50 -0400 Subject: [PATCH 23/36] hide mod in TaskAdder if it has no roles --- MiraAPI/Patches/Roles/TaskAdderPatch.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MiraAPI/Patches/Roles/TaskAdderPatch.cs b/MiraAPI/Patches/Roles/TaskAdderPatch.cs index 8a3ae90..0f3037f 100644 --- a/MiraAPI/Patches/Roles/TaskAdderPatch.cs +++ b/MiraAPI/Patches/Roles/TaskAdderPatch.cs @@ -62,6 +62,11 @@ public static void AddRolesFolder(TaskAdderGame __instance) foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins()) { + if (plugin.CustomRoles.Count == 0) + { + continue; + } + var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); newFolder.FolderName = newFolder.name = plugin.PluginInfo.Metadata.Name; newFolder.gameObject.SetActive(false); From ae10917f2a45fd9abd4318739277b796e90f1090 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:22:56 -0400 Subject: [PATCH 24/36] hidefromil2cpp --- MiraAPI.Example/MiraDebugWindow.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MiraAPI.Example/MiraDebugWindow.cs b/MiraAPI.Example/MiraDebugWindow.cs index 13a41d4..d0726e7 100644 --- a/MiraAPI.Example/MiraDebugWindow.cs +++ b/MiraAPI.Example/MiraDebugWindow.cs @@ -1,6 +1,7 @@ -using Reactor.Utilities.Attributes; +using System; +using Il2CppInterop.Runtime.Attributes; +using Reactor.Utilities.Attributes; using Reactor.Utilities.ImGui; -using System; using UnityEngine; namespace MiraAPI.Example; @@ -8,6 +9,7 @@ namespace MiraAPI.Example; [RegisterInIl2Cpp] public class MiraDebugWindow(IntPtr cppPtr) : MonoBehaviour(cppPtr) { + [HideFromIl2Cpp] public DragWindow DebuggingWindow { get; } = new( new Rect(10, 10, 0, 0), "MIRA API DEBUGGING", From f92d08b825b2c961b9986f3ea550ba15c466e0b8 Mon Sep 17 00:00:00 2001 From: Rubyboat <52091447+Rubyboat1207@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:37:53 -0500 Subject: [PATCH 25/36] Added Loadable Audio Resource Asset --- .../Assets/LoadableAudioResourceAsset.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs diff --git a/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs new file mode 100644 index 0000000..7e4decf --- /dev/null +++ b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs @@ -0,0 +1,66 @@ +using System; +using System.IO; +using System.Reflection; +using System.Text; +using Cpp2IL.Core.Extensions; +using MiraAPI.Utilities.Assets; +using Reactor.Utilities.Extensions; +using UnityEngine; + +namespace MiraAPI.Utilities.Assets; + +/// +/// A utility class for loading .WAV audio assets from the DLL's embedded resources. +/// +/// The path of the wave file +public class LoadableAudioResourceAsset(string path) : LoadableAsset +{ + private readonly Assembly _assembly = Assembly.GetCallingAssembly(); + + /// + /// Loads the asset from embedded resources. + /// + /// The asset to load. + /// Attempted to load an Audio file in non WAV format. + /// Stream failed to load. Check if the name of your asset was correct. + public override AudioClip LoadAsset() + { + var assetStream = _assembly.GetManifestResourceStream(path); + + if (assetStream != null) + { + var audioBytes = assetStream.ReadFully(); + + string riffHeader = Encoding.ASCII.GetString(audioBytes.SubArray(0, 4)); + string waveHeader = Encoding.ASCII.GetString(audioBytes.SubArray(8, 4)); + + if (riffHeader != "RIFF" || waveHeader != "WAVE") + { + throw new NotSupportedException($"Attempted to load an Audio file in non WAV format '{path}'."); + } + + int channels = BitConverter.ToInt16(audioBytes, 22); + int sampleRate = BitConverter.ToInt32(audioBytes, 24); + int dataSize = BitConverter.ToInt32(audioBytes, 40); + + float[] audioData = new float[dataSize / 2]; + for (int i = 0; i < audioData.Length; i++) + { + audioData[i] = BitConverter.ToInt16(audioBytes, 44 + i * 2) / 32768.0f; + } + + AudioClip audioClip = AudioClip.Create( + "WavClip", + audioData.Length, + channels, + sampleRate, + false + ); + audioClip.SetData(audioData, 0); + + return audioClip; + } + + throw new FileNotFoundException("Stream failed to load. Check if the name of your asset was correct."); + } +} From 9c14db0321942caa91dec77a4ff65c5447355ec4 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:39:03 -0400 Subject: [PATCH 26/36] slight fix to options Click To Close text --- MiraAPI/Patches/Options/GameOptionsMenuPatch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index a51b2f9..517f7d0 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -114,7 +114,7 @@ public static bool SettingsPatch(GameOptionsMenu __instance) group.Header = categoryHeaderMasked; var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform); - newText.text = $"(Click to open)"; + newText.text = "(Click to close)"; newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f); newText.gameObject.GetComponent().Destroy(); From f83dce99ef5ec69527ee5e2263a70c86517afca6 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Tue, 3 Sep 2024 20:59:26 -0400 Subject: [PATCH 27/36] add group priorities for ordering groups --- MiraAPI.Example/Options/ExampleOptions2.cs | 2 ++ MiraAPI/GameOptions/AbstractOptionGroup.cs | 6 ++++++ MiraAPI/PluginLoading/MiraPluginManager.cs | 2 ++ 3 files changed, 10 insertions(+) diff --git a/MiraAPI.Example/Options/ExampleOptions2.cs b/MiraAPI.Example/Options/ExampleOptions2.cs index c521ea8..56dd209 100644 --- a/MiraAPI.Example/Options/ExampleOptions2.cs +++ b/MiraAPI.Example/Options/ExampleOptions2.cs @@ -8,6 +8,8 @@ public class ExampleOptions2 : AbstractOptionGroup { public override string GroupName => "Example Options 2"; + public override uint GroupPriority => 0; // This group will be displayed first. The default value is uint.MaxValue. + public ModdedToggleOption ToggleOpt1 { get; } = new("Toggle Option 1", false); public ModdedToggleOption ToggleOpt2 { get; } = new("Toggle Option 2", false) diff --git a/MiraAPI/GameOptions/AbstractOptionGroup.cs b/MiraAPI/GameOptions/AbstractOptionGroup.cs index aeae257..418f25b 100644 --- a/MiraAPI/GameOptions/AbstractOptionGroup.cs +++ b/MiraAPI/GameOptions/AbstractOptionGroup.cs @@ -26,6 +26,12 @@ public abstract class AbstractOptionGroup /// public virtual Color GroupColor => Color.clear; + /// + /// Gets the group priority. This is used to determine the order in which groups are displayed in the options menu. + /// Zero is the highest priority, and the default value is the max uint value. + /// + public virtual uint GroupPriority => uint.MaxValue; + /// /// Gets the role the group is associated with. This is used for the advanced role options menu. /// diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 423cb09..2a735b8 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -83,6 +83,8 @@ private static void RegisterAllOptions(Assembly assembly, MiraPluginInfo pluginI ModdedOptionsManager.RegisterAttributeOption(type, attribute, property, pluginInfo); } } + + pluginInfo.OptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority)); } private static void RegisterRoleAttribute(Assembly assembly, MiraPluginInfo pluginInfo) From 4e474dc5b2b8a5bad345dd2572e6893009241420 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:05:32 -0400 Subject: [PATCH 28/36] formatting --- .../Assets/LoadableAudioResourceAsset.cs | 59 +++++++++---------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs index 7e4decf..25640e4 100644 --- a/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs @@ -3,7 +3,6 @@ using System.Reflection; using System.Text; using Cpp2IL.Core.Extensions; -using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; using UnityEngine; @@ -25,42 +24,38 @@ public class LoadableAudioResourceAsset(string path) : LoadableAsset /// Stream failed to load. Check if the name of your asset was correct. public override AudioClip LoadAsset() { - var assetStream = _assembly.GetManifestResourceStream(path); + var assetStream = _assembly.GetManifestResourceStream(path) ?? + throw new FileNotFoundException( + "Stream failed to load. Check if the name of your asset was correct."); + var audioBytes = assetStream.ReadFully(); - if (assetStream != null) - { - var audioBytes = assetStream.ReadFully(); - - string riffHeader = Encoding.ASCII.GetString(audioBytes.SubArray(0, 4)); - string waveHeader = Encoding.ASCII.GetString(audioBytes.SubArray(8, 4)); - - if (riffHeader != "RIFF" || waveHeader != "WAVE") - { - throw new NotSupportedException($"Attempted to load an Audio file in non WAV format '{path}'."); - } - - int channels = BitConverter.ToInt16(audioBytes, 22); - int sampleRate = BitConverter.ToInt32(audioBytes, 24); - int dataSize = BitConverter.ToInt32(audioBytes, 40); + var riffHeader = Encoding.ASCII.GetString(audioBytes.SubArray(0, 4)); + var waveHeader = Encoding.ASCII.GetString(audioBytes.SubArray(8, 4)); - float[] audioData = new float[dataSize / 2]; - for (int i = 0; i < audioData.Length; i++) - { - audioData[i] = BitConverter.ToInt16(audioBytes, 44 + i * 2) / 32768.0f; - } + if (riffHeader != "RIFF" || waveHeader != "WAVE") + { + throw new NotSupportedException($"Attempted to load an Audio file in non WAV format '{path}'."); + } - AudioClip audioClip = AudioClip.Create( - "WavClip", - audioData.Length, - channels, - sampleRate, - false - ); - audioClip.SetData(audioData, 0); + int channels = BitConverter.ToInt16(audioBytes, 22); + var sampleRate = BitConverter.ToInt32(audioBytes, 24); + var dataSize = BitConverter.ToInt32(audioBytes, 40); - return audioClip; + var audioData = new float[dataSize / 2]; + for (var i = 0; i < audioData.Length; i++) + { + audioData[i] = BitConverter.ToInt16(audioBytes, 44 + i * 2) / 32768.0f; } - throw new FileNotFoundException("Stream failed to load. Check if the name of your asset was correct."); + var audioClip = AudioClip.Create( + "WavClip", + audioData.Length, + channels, + sampleRate, + false + ); + audioClip.SetData(audioData, 0); + + return audioClip; } } From 08a4dc75f28ffca567a17553ab0e98c2c9d3bcee Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:05:55 -0400 Subject: [PATCH 29/36] fix period oops --- MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs index 25640e4..be32627 100644 --- a/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs @@ -11,7 +11,7 @@ namespace MiraAPI.Utilities.Assets; /// /// A utility class for loading .WAV audio assets from the DLL's embedded resources. /// -/// The path of the wave file +/// The path of the wave file. public class LoadableAudioResourceAsset(string path) : LoadableAsset { private readonly Assembly _assembly = Assembly.GetCallingAssembly(); From 24dca63df11eb9c462b601f3f81611d738b047b7 Mon Sep 17 00:00:00 2001 From: Rubyboat <52091447+Rubyboat1207@users.noreply.github.com> Date: Tue, 3 Sep 2024 20:17:50 -0500 Subject: [PATCH 30/36] File name more descriptive --- MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs index be32627..381e9bc 100644 --- a/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableAudioResourceAsset.cs @@ -48,7 +48,7 @@ public override AudioClip LoadAsset() } var audioClip = AudioClip.Create( - "WavClip", + path, audioData.Length, channels, sampleRate, From 75dd5e8a35576f2a8e6e0c9efbfc0628cee574a3 Mon Sep 17 00:00:00 2001 From: CallOfCreator Date: Wed, 4 Sep 2024 16:27:57 +0000 Subject: [PATCH 31/36] Add CanUseSabotage support in ICustomRole --- MiraAPI/Patches/HudManagerPatches.cs | 35 ++++++++++++++++++++++++++++ MiraAPI/Roles/ICustomRole.cs | 5 ++++ 2 files changed, 40 insertions(+) diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index cdef7cc..ddd31f8 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -1,5 +1,6 @@ using HarmonyLib; using MiraAPI.Hud; +using MiraAPI.Roles; using Reactor.Utilities.Extensions; using UnityEngine; using Object = UnityEngine.Object; @@ -92,10 +93,44 @@ public static void SetHudActivePostfix(HudManager __instance, PlayerControl loca { return; } + if (CustomRoleManager.GetCustomRoleBehaviour(role.Role, out var customRole)) + { + if (customRole.CanUseSabotage) + { + __instance.SabotageButton.gameObject.SetActive(true); + } + else + { + __instance.SabotageButton.gameObject.SetActive(role.IsImpostor && isActive); + } + } foreach (var button in CustomButtonManager.CustomButtons) { button.SetActive(isActive, role); } } + + /// + /// Patches the Sabotage button to check if the player's custom role can use sabotage. + /// + [HarmonyPatch(typeof(SabotageButton), nameof(SabotageButton.DoClick))] + [HarmonyPrefix] + public static bool StartPrefix(SabotageButton __instance) + { + + var player = PlayerControl.LocalPlayer; + + if (CustomRoleManager.GetCustomRoleBehaviour(player.Data.Role.Role, out var customRole)) + { + if (!customRole.CanUseSabotage) return false; + + DestroyableSingleton.Instance.ToggleMapVisible(new MapOptions() + { + Mode = MapOptions.Modes.Sabotage + }); + return false; + } + return true; + } } diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 3087dbe..9fc2b61 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -77,6 +77,11 @@ public interface ICustomRole /// bool CanUseVent => Team == ModdedRoleTeams.Impostor; + /// + /// Gets a value indicating whether the role can use the sabotage button. + /// + bool CanUseSabotage => Team == ModdedRoleTeams.Impostor; + /// /// Gets a value indicating whether the role's tasks count towards task progress. /// From fa8fdfc4f66fba2cec46b8135214f6183a2228e2 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Wed, 4 Sep 2024 17:30:08 -0400 Subject: [PATCH 32/36] Create ChameleonRole.cs --- MiraAPI.Example/Roles/ChameleonRole.cs | 46 ++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 MiraAPI.Example/Roles/ChameleonRole.cs diff --git a/MiraAPI.Example/Roles/ChameleonRole.cs b/MiraAPI.Example/Roles/ChameleonRole.cs new file mode 100644 index 0000000..4c10d55 --- /dev/null +++ b/MiraAPI.Example/Roles/ChameleonRole.cs @@ -0,0 +1,46 @@ +using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; +using TMPro; +using UnityEngine; + +namespace MiraAPI.Example.Roles; + +[RegisterCustomRole] +public class ChameloenRole : CrewmateRole, ICustomRole +{ + public string RoleName => "Chamelon"; + public string RoleLongDescription => "Stay invisible while not moving."; + public string RoleDescription => RoleLongDescription; + public Color RoleColor => Palette.AcceptedGreen; + public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate; + public LoadableAsset OptionsScreenshot => ExampleAssets.Banner; + public int MaxPlayers => 2; + + public void PlayerControlFixedUpdate(PlayerControl playerControl) + { + if (playerControl.MyPhysics.Velocity.magnitude > 0) + { + SpriteRenderer rend = playerControl.cosmetics.currentBodySprite.BodySprite; + TextMeshPro tmp = playerControl.cosmetics.nameText; + tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, 1), Time.deltaTime * 4f); + rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, 1), Time.deltaTime * 4f); + + foreach (var cosmetic in playerControl.cosmetics.transform.GetComponentsInChildren()) + { + cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, 1), Time.deltaTime * 4f); + } + } + else + { + SpriteRenderer rend = playerControl.cosmetics.currentBodySprite.BodySprite; + TextMeshPro tmp = playerControl.cosmetics.nameText; + tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f); + rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f); + + foreach (var cosmetic in playerControl.cosmetics.transform.GetComponentsInChildren()) + { + cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f); + } + } + } +} \ No newline at end of file From 17da5942070497ad37824db9ae81aa53e9063c48 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:34:36 -0400 Subject: [PATCH 33/36] fix notif popper issue --- MiraAPI/Patches/Options/NotificationPopperPatch.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/MiraAPI/Patches/Options/NotificationPopperPatch.cs b/MiraAPI/Patches/Options/NotificationPopperPatch.cs index d13b854..fa78e8c 100644 --- a/MiraAPI/Patches/Options/NotificationPopperPatch.cs +++ b/MiraAPI/Patches/Options/NotificationPopperPatch.cs @@ -18,7 +18,7 @@ public static bool RoleChangeMsgPatch( [HarmonyArgument(3)] RoleTeamTypes teamType, [HarmonyArgument(4)] bool playSound) { - if (CustomRoleManager.CustomRoles.Values.All(role => role.StringName != key)) + if (CustomRoleManager.CustomRoles.Values.FirstOrDefault(x=>x.StringName==key) is not ICustomRole customRole) { return true; } @@ -29,10 +29,12 @@ public static bool RoleChangeMsgPatch( var item = DestroyableSingleton.Instance.GetString( StringNames.LobbyChangeSettingNotificationRole, - "", - text, - DestroyableSingleton.Instance.GetString(key, Array.Empty()), - "", + string.Concat( + "", + text, + DestroyableSingleton.Instance.GetString(key, Array.Empty()), + "" + ), "" + roleCount + "", "" + roleChance + "%" ); From 7f65f2d5bb7340eda7a993e625e913bf0f3a6740 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:34:45 -0400 Subject: [PATCH 34/36] remove warning from example build --- MiraAPI.Example/Roles/ChameleonRole.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MiraAPI.Example/Roles/ChameleonRole.cs b/MiraAPI.Example/Roles/ChameleonRole.cs index 4c10d55..a7d5a5d 100644 --- a/MiraAPI.Example/Roles/ChameleonRole.cs +++ b/MiraAPI.Example/Roles/ChameleonRole.cs @@ -43,4 +43,4 @@ public void PlayerControlFixedUpdate(PlayerControl playerControl) } } } -} \ No newline at end of file +} From b2651adcabe6ca31c5bcc00b95694f91c3134703 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Thu, 5 Sep 2024 07:18:20 -0400 Subject: [PATCH 35/36] adjustments --- MiraAPI/Patches/HudManagerPatches.cs | 34 ----------- MiraAPI/Patches/Roles/HudManagerPatches.cs | 3 +- MiraAPI/Patches/Roles/SabotageButtonPatch.cs | 62 ++++++++++++++++++++ 3 files changed, 64 insertions(+), 35 deletions(-) create mode 100644 MiraAPI/Patches/Roles/SabotageButtonPatch.cs diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index ddd31f8..50e248b 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -93,44 +93,10 @@ public static void SetHudActivePostfix(HudManager __instance, PlayerControl loca { return; } - if (CustomRoleManager.GetCustomRoleBehaviour(role.Role, out var customRole)) - { - if (customRole.CanUseSabotage) - { - __instance.SabotageButton.gameObject.SetActive(true); - } - else - { - __instance.SabotageButton.gameObject.SetActive(role.IsImpostor && isActive); - } - } foreach (var button in CustomButtonManager.CustomButtons) { button.SetActive(isActive, role); } } - - /// - /// Patches the Sabotage button to check if the player's custom role can use sabotage. - /// - [HarmonyPatch(typeof(SabotageButton), nameof(SabotageButton.DoClick))] - [HarmonyPrefix] - public static bool StartPrefix(SabotageButton __instance) - { - - var player = PlayerControl.LocalPlayer; - - if (CustomRoleManager.GetCustomRoleBehaviour(player.Data.Role.Role, out var customRole)) - { - if (!customRole.CanUseSabotage) return false; - - DestroyableSingleton.Instance.ToggleMapVisible(new MapOptions() - { - Mode = MapOptions.Modes.Sabotage - }); - return false; - } - return true; - } } diff --git a/MiraAPI/Patches/Roles/HudManagerPatches.cs b/MiraAPI/Patches/Roles/HudManagerPatches.cs index 34f0bb9..f833651 100644 --- a/MiraAPI/Patches/Roles/HudManagerPatches.cs +++ b/MiraAPI/Patches/Roles/HudManagerPatches.cs @@ -27,10 +27,11 @@ public static void SetHudActivePostfix( { var flag = localPlayer.Data != null && localPlayer.Data.IsDead; - if (role is ICustomRole) + if (role is ICustomRole customRole) { __instance.KillButton.ToggleVisible(isActive && role.CanUseKillButton && !flag); __instance.ImpostorVentButton.ToggleVisible(isActive && role.CanVent && !flag); + __instance.SabotageButton.gameObject.SetActive(isActive && customRole.CanUseSabotage); } if (_roleTab) diff --git a/MiraAPI/Patches/Roles/SabotageButtonPatch.cs b/MiraAPI/Patches/Roles/SabotageButtonPatch.cs new file mode 100644 index 0000000..49c5190 --- /dev/null +++ b/MiraAPI/Patches/Roles/SabotageButtonPatch.cs @@ -0,0 +1,62 @@ +using HarmonyLib; +using MiraAPI.Roles; + +namespace MiraAPI.Patches.Roles; + +[HarmonyPatch(typeof(SabotageButton))] +public class SabotageButtonPatch +{ + /// + /// Patches the Sabotage button to check if the player's custom role can use sabotage. + /// + [HarmonyPatch(nameof(SabotageButton.DoClick))] + [HarmonyPrefix] + public static bool DoClickPrefix(SabotageButton __instance) + { + var player = PlayerControl.LocalPlayer; + + if (player.Data.Role is not ICustomRole customRole) + { + return true; + } + + if (!customRole.CanUseSabotage || PlayerControl.LocalPlayer.inVent || !GameManager.Instance.SabotagesEnabled()) + { + return false; + } + + DestroyableSingleton.Instance.ToggleMapVisible( + new MapOptions + { + Mode = MapOptions.Modes.Sabotage, + }); + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(SabotageButton.Refresh))] + public static bool RefreshPrefix(SabotageButton __instance) + { + var player = PlayerControl.LocalPlayer; + if (GameManager.Instance == null || player == null) + { + __instance.ToggleVisible(false); + __instance.SetDisabled(); + return false; + } + + if (player.Data.Role is not ICustomRole customRole) + { + return true; + } + + if (player.inVent || !GameManager.Instance.SabotagesEnabled() || player.petting) + { + __instance.ToggleVisible(customRole.CanUseSabotage && GameManager.Instance.SabotagesEnabled()); + __instance.SetDisabled(); + return false; + } + __instance.SetEnabled(); + return false; + } +} From 400ad535a4fce6ac3c44f28a009c8ab49511f5a4 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Thu, 5 Sep 2024 18:09:50 -0400 Subject: [PATCH 36/36] bump version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 2431ce4..974f974 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,7 +8,7 @@ true - 0.1.4 + 0.1.5 dev All Of Us, XtraCube, Angxl