diff --git a/MiraAPI.Example/Roles/NeutralKillerRole.cs b/MiraAPI.Example/Roles/NeutralKillerRole.cs index 55fdc51..1afa4eb 100644 --- a/MiraAPI.Example/Roles/NeutralKillerRole.cs +++ b/MiraAPI.Example/Roles/NeutralKillerRole.cs @@ -1,4 +1,5 @@ -using MiraAPI.Example.GameOver; +using Il2CppInterop.Runtime.Attributes; +using MiraAPI.Example.GameOver; using MiraAPI.GameEnd; using MiraAPI.Roles; using UnityEngine; @@ -22,6 +23,7 @@ public class NeutralKillerRole : ImpostorRole, ICustomRole public RoleOptionsGroup RoleOptionsGroup { get; } = new("Outcast", Color.gray); + [HideFromIl2Cpp] public TeamIntroConfiguration? IntroConfiguration { get; } = new( Color.gray, "OUTCAST", diff --git a/MiraAPI/Patches/IntroCutscenePatches.cs b/MiraAPI/Patches/IntroCutscenePatches.cs index 41217e6..5495251 100644 --- a/MiraAPI/Patches/IntroCutscenePatches.cs +++ b/MiraAPI/Patches/IntroCutscenePatches.cs @@ -1,8 +1,12 @@ -using HarmonyLib; +using System.Linq; +using System.Reflection; +using HarmonyLib; +using Il2CppInterop.Runtime.InteropTypes; using MiraAPI.Events; using MiraAPI.Events.Vanilla.Gameplay; using MiraAPI.Roles; using MiraAPI.Utilities; +using Reactor.Utilities; namespace MiraAPI.Patches; @@ -29,14 +33,29 @@ public static class IntroCutscenePatches MiraEventManager.InvokeEvent(@event); } - [HarmonyPostfix] - [HarmonyPatch(typeof(IntroCutscene._ShowRole_d__41), nameof(IntroCutscene._ShowRole_d__41.MoveNext))] - [HarmonyPriority(Priority.Last)] - public static void ShowRolePatch(IntroCutscene._ShowRole_d__41 __instance) + [HarmonyPatch] + public static class IntroCutsceneShowRolePatch { - var realInstance = __instance.__4__this; - var @event = new IntroRoleRevealEvent(realInstance); - MiraEventManager.InvokeEvent(@event); + public static MethodBase TargetMethod() + { + return Helpers.GetStateMachineMoveNext("ShowRole")!; + } + + public static void Postfix(Il2CppObjectBase __instance) + { + var wrapper = new StateMachineWrapper(__instance); + // run before the first yield + if (wrapper.GetState() != 1) + { + return; + } + + var introCutscene = wrapper.Instance; + + Logger.Info("IntroCutscene ShowRole reached"); + var @event = new IntroRoleRevealEvent(introCutscene); + MiraEventManager.InvokeEvent(@event); + } } [HarmonyPrefix] @@ -66,16 +85,54 @@ public static class IntroCutscenePatches } } - [HarmonyPostfix] - [HarmonyPatch(nameof(IntroCutscene.OnDestroy))] - public static void GameBeginPatch(IntroCutscene __instance) + [HarmonyPatch] + public static class IntroCutsceneDestroyPatch { - MiraEventManager.InvokeEvent(new IntroEndEvent(__instance)); + private static bool _usedFallback; - var @event = new BeforeRoundStartEvent(true); - MiraEventManager.InvokeEvent(@event); + public static MethodBase TargetMethod() + { + var onDestroy = AccessTools.Method(typeof(IntroCutscene), "OnDestroy"); + if (onDestroy != null) + { + _usedFallback = false; + Logger.Info("Using OnDestroy for IntroCutsceneDestroyPatch"); + return onDestroy; + } - if (@event.IsCancelled) return; - MiraEventManager.InvokeEvent(new RoundStartEvent(true)); + _usedFallback = true; + return Helpers.GetStateMachineMoveNext("CoBegin")!; + } + + public static void Postfix(Il2CppObjectBase __instance) + { + IntroCutscene introCutscene; + + if (_usedFallback) + { + var wrapper = new StateMachineWrapper(__instance); + // run after the final yield + if (wrapper.GetState() != -1) + { + return; + } + introCutscene = wrapper.Instance; + } + else + { + introCutscene = __instance.Cast(); + } + + + Logger.Info("IntroCutscene ended"); + + MiraEventManager.InvokeEvent(new IntroEndEvent(introCutscene)); + + var @event = new BeforeRoundStartEvent(true); + MiraEventManager.InvokeEvent(@event); + + if (@event.IsCancelled) return; + MiraEventManager.InvokeEvent(new RoundStartEvent(true)); + } } } diff --git a/MiraAPI/Patches/Menu/HatsTabPatches.cs b/MiraAPI/Patches/Menu/HatsTabPatches.cs index 8d36e9c..8cd2bfd 100644 --- a/MiraAPI/Patches/Menu/HatsTabPatches.cs +++ b/MiraAPI/Patches/Menu/HatsTabPatches.cs @@ -6,10 +6,13 @@ using System.Linq; using AmongUs.Data; using HarmonyLib; using MiraAPI.Utilities; +using MiraAPI.Utilities.Assets; using Reactor.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; +using UnityEngine.Events; +using UnityEngine.UI; using Object = UnityEngine.Object; namespace MiraAPI.Patches.Menu; @@ -20,6 +23,20 @@ public static class HatsTabPatches private static SortedList> sortedHats = []; private static int currentPage; + private static void PreviousPage(HatsTab hatsTab) + { + currentPage--; + currentPage = currentPage < 0 ? sortedHats.Count - 1 : currentPage; + GenerateHats(hatsTab, currentPage); + } + + private static void NextPage(HatsTab hatsTab) + { + currentPage++; + currentPage = currentPage > sortedHats.Count - 1 ? 0 : currentPage; + GenerateHats(hatsTab, currentPage); + } + [HarmonyPatch(nameof(HatsTab.OnEnable))] [HarmonyPrefix] public static bool OnEnablePrefix(HatsTab __instance) @@ -38,6 +55,8 @@ public static class HatsTabPatches } } + InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage); + GenerateHats(__instance, currentPage); return false; @@ -45,22 +64,17 @@ public static class HatsTabPatches [HarmonyPatch(typeof(HatsTab), nameof(HatsTab.Update))] [HarmonyPrefix] - public static void UpdatePrefix(HatsTab __instance) { if (sortedHats.Count == 0) return; if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow)) { - currentPage--; - currentPage = currentPage < 0 ? sortedHats.Count - 1 : currentPage; - GenerateHats(__instance, currentPage); + PreviousPage(__instance); } else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow)) { - currentPage++; - currentPage = currentPage > sortedHats.Count - 1 ? 0 : currentPage; - GenerateHats(__instance, currentPage); + NextPage(__instance); } } @@ -84,10 +98,10 @@ public static class HatsTabPatches text.gameObject.transform.localScale = Vector3.one; text.GetComponent().Destroy(); text.EnableStencilMasking(); - text.text = $"{groupName} ({currentPage + 1}/{sortedHats.Count})\nPress Ctrl or Tab to cycle pages"; + text.text = $"{groupName} ({currentPage + 1}/{sortedHats.Count})"; text.alignment = TextAlignmentOptions.Center; - text.fontSize = 3f; - text.fontSizeMax = 3f; + text.fontSize = 5f; + text.fontSizeMax = 5f; text.fontSizeMin = 0f; var xLerp = __instance.XRange.Lerp(0.5f); var yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset; @@ -125,9 +139,17 @@ public static class HatsTabPatches { var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner); colorChip.gameObject.name = hat.ProductId; - colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip())); - colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectHat(hat))); - colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectHat(HatManager.Instance.GetHatById(DataManager.Player.Customization.Hat)))); + if (ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard) + { + colorChip.Button.OnClick.AddListener((Action)__instance.ClickEquip); + colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectHat(hat))); + colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectHat(HatManager.Instance.GetHatById(DataManager.Player.Customization.Hat)))); + } + else + { + colorChip.Button.OnClick.AddListener((Action)(() => __instance.SelectHat(hat))); + } + colorChip.Inner.SetHat(hat, __instance.HasLocalPlayer() ? PlayerControl.LocalPlayer.Data.DefaultOutfit.ColorId : DataManager.Player.Customization.Color); colorChip.Button.ClickMask = __instance.scroller.Hitbox; colorChip.SelectionHighlight.gameObject.SetActive(false); diff --git a/MiraAPI/Patches/Menu/InventoryUtility.cs b/MiraAPI/Patches/Menu/InventoryUtility.cs new file mode 100644 index 0000000..95004d0 --- /dev/null +++ b/MiraAPI/Patches/Menu/InventoryUtility.cs @@ -0,0 +1,54 @@ +using System; +using MiraAPI.Utilities.Assets; +using TMPro; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.UI; +using Object = UnityEngine.Object; + +namespace MiraAPI.Patches.Menu; + +public static class InventoryUtility +{ + public static void CreateNextBackButtons(T tab, Action previousPage, Action nextPage) where T : InventoryTab + { + var title = tab.transform.FindChild("Text"); + title.localPosition = new Vector3(.9123f, -.23f, -55); + title.GetComponent().alignment = TextAlignmentOptions.Center; + + tab.scroller.Hitbox.transform.localScale -= new Vector3(0, 0.1f, 0); + tab.scroller.Hitbox.transform.localPosition -= new Vector3(0, 0.1f, 0); + + var leftButtonElement = Object.Instantiate(PlayerCustomizationMenu.Instance.BackButton.gameObject, tab.transform); + var leftSpriteRenderer = leftButtonElement.GetComponent(); + var leftButton = leftButtonElement.GetComponent(); + + leftButtonElement.transform.localPosition = new Vector3(-1.1f, -0.2f, -55); + leftSpriteRenderer.sprite = MiraAssets.NextButton.LoadAsset(); + leftSpriteRenderer.flipX = true; + + leftButton.OnMouseOver = new UnityEvent(); + leftButton.OnMouseOver.AddListener((Action)(() => leftSpriteRenderer.sprite = MiraAssets.NextButtonActive.LoadAsset())); + leftButton.OnMouseOut = new UnityEvent(); + leftButton.OnMouseOut.AddListener((Action)(() => leftSpriteRenderer.sprite = MiraAssets.NextButton.LoadAsset())); + + leftButton.OnClick = new Button.ButtonClickedEvent(); + leftButton.OnClick.AddListener((Action)(() => previousPage(tab))); + + var rightButtonElement = Object.Instantiate(leftButtonElement, tab.transform); + var rightSpriteRenderer = rightButtonElement.GetComponent(); + var rightButton = rightButtonElement.GetComponent(); + + rightButtonElement.transform.localPosition = new Vector3(2.8f, -0.2f, -55); + rightSpriteRenderer.sprite = MiraAssets.NextButton.LoadAsset(); + rightSpriteRenderer.flipX = false; + + rightButton.OnMouseOver = new UnityEvent(); + rightButton.OnMouseOver.AddListener((Action)(() => rightSpriteRenderer.sprite = MiraAssets.NextButtonActive.LoadAsset())); + rightButton.OnMouseOut = new UnityEvent(); + rightButton.OnMouseOut.AddListener((Action)(() => rightSpriteRenderer.sprite = MiraAssets.NextButton.LoadAsset())); + + rightButton.OnClick = new Button.ButtonClickedEvent(); + rightButton.OnClick.AddListener((Action)(() => nextPage(tab))); + } +} diff --git a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs index 11657a0..0b7acfe 100644 --- a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs +++ b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs @@ -26,6 +26,20 @@ public static class NameplatesTabPatches } } + private static void PreviousPage(NameplatesTab tab) + { + currentPage--; + currentPage = currentPage < 0 ? SortedNameplates.Count - 1 : currentPage; + GenerateHats(tab, currentPage); + } + + private static void NextPage(NameplatesTab tab) + { + currentPage++; + currentPage = currentPage > SortedNameplates.Count - 1 ? 0 : currentPage; + GenerateHats(tab, currentPage); + } + [HarmonyPatch(nameof(NameplatesTab.OnEnable))] [HarmonyPrefix] public static bool OnEnablePrefix(NameplatesTab __instance) @@ -33,6 +47,9 @@ public static class NameplatesTabPatches __instance.plateId = HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.namePlate).ProdId; if (!SortedNameplates.ContainsKey("Vanilla")) AddRange(DestroyableSingleton.Instance.GetUnlockedNamePlates().Select(x => ("Vanilla", x))); + + InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage); + GenerateHats(__instance, currentPage); return false; @@ -45,15 +62,11 @@ public static class NameplatesTabPatches { if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow)) { - currentPage--; - currentPage = currentPage < 0 ? SortedNameplates.Count - 1 : currentPage; - GenerateHats(__instance, currentPage); + PreviousPage(__instance); } else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow)) { - currentPage++; - currentPage = currentPage > SortedNameplates.Count - 1 ? 0 : currentPage; - GenerateHats(__instance, currentPage); + NextPage(__instance); } } @@ -73,10 +86,10 @@ public static class NameplatesTabPatches text.gameObject.transform.localScale = Vector3.one; text.GetComponent().Destroy(); text.EnableStencilMasking(); - text.text = $"{groupName} ({currentPage + 1}/{SortedNameplates.Count})\nPress Ctrl or Tab to cycle pages"; + text.text = $"{groupName} ({currentPage + 1}/{SortedNameplates.Count})"; text.alignment = TextAlignmentOptions.Center; - text.fontSize = 3f; - text.fontSizeMax = 3f; + text.fontSize = 5f; + text.fontSizeMax = 5f; text.fontSizeMin = 0f; float xLerp = __instance.XRange.Lerp(0.5f); float yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset; @@ -99,9 +112,20 @@ public static class NameplatesTabPatches { var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner); colorChip.gameObject.name = namePlate.ProductId; - colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip())); - colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectNameplate(namePlate))); - colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectNameplate(HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.NamePlate)))); + + if (ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard) + { + colorChip.Button.OnClick.AddListener((Action)__instance.ClickEquip); + colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectNameplate(namePlate))); + colorChip.Button.OnMouseOut.AddListener( + (Action)(() => __instance.SelectNameplate( + HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.NamePlate)))); + } + else + { + colorChip.Button.OnClick.AddListener((Action)(() => __instance.SelectNameplate(namePlate))); + } + colorChip.Button.ClickMask = __instance.scroller.Hitbox; colorChip.ProductId = namePlate.ProdId; diff --git a/MiraAPI/Patches/Menu/SkinsTabPatches.cs b/MiraAPI/Patches/Menu/SkinsTabPatches.cs index 9378d8b..b5ea7d7 100644 --- a/MiraAPI/Patches/Menu/SkinsTabPatches.cs +++ b/MiraAPI/Patches/Menu/SkinsTabPatches.cs @@ -18,6 +18,20 @@ public static class SkinsTabPatches private static SortedList> sortedSkins = []; private static int currentPage; + private static void PreviousPage(SkinsTab tab) + { + currentPage--; + currentPage = currentPage < 0 ? sortedSkins.Count - 1 : currentPage; + GenerateHats(tab, currentPage); + } + + private static void NextPage(SkinsTab tab) + { + currentPage++; + currentPage = currentPage > sortedSkins.Count - 1 ? 0 : currentPage; + GenerateHats(tab, currentPage); + } + [HarmonyPatch(nameof(SkinsTab.OnEnable))] [HarmonyPrefix] public static bool OnEnablePrefix(SkinsTab __instance) @@ -36,6 +50,8 @@ public static class SkinsTabPatches } } + InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage); + GenerateHats(__instance, currentPage); return false; @@ -48,15 +64,11 @@ public static class SkinsTabPatches { if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow)) { - currentPage--; - currentPage = currentPage < 0 ? sortedSkins.Count - 1 : currentPage; - GenerateHats(__instance, currentPage); + PreviousPage(__instance); } else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow)) { - currentPage++; - currentPage = currentPage > sortedSkins.Count - 1 ? 0 : currentPage; - GenerateHats(__instance, currentPage); + NextPage(__instance); } } @@ -76,10 +88,10 @@ public static class SkinsTabPatches text.gameObject.transform.localScale = Vector3.one; text.GetComponent().Destroy(); text.EnableStencilMasking(); - text.text = $"{groupName} ({currentPage + 1}/{sortedSkins.Count})\nPress Ctrl or Tab to cycle pages"; + text.text = $"{groupName} ({currentPage + 1}/{sortedSkins.Count})"; text.alignment = TextAlignmentOptions.Center; - text.fontSize = 3f; - text.fontSizeMax = 3f; + text.fontSize = 5f; + text.fontSizeMax = 5f; text.fontSizeMin = 0f; float xLerp = __instance.XRange.Lerp(0.5f); float yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset; @@ -102,9 +114,18 @@ public static class SkinsTabPatches { var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner); colorChip.gameObject.name = skin.ProductId; - colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip())); - colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectSkin(skin))); - colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectSkin(HatManager.Instance.GetSkinById(DataManager.Player.Customization.Skin)))); + if (ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard) + { + colorChip.Button.OnClick.AddListener((Action)__instance.ClickEquip); + colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectSkin(skin))); + colorChip.Button.OnMouseOut.AddListener( + (Action)(() => + __instance.SelectSkin(HatManager.Instance.GetSkinById(DataManager.Player.Customization.Skin)))); + } + else + { + colorChip.Button.OnClick.AddListener((Action)(() => __instance.SelectSkin(skin))); + } colorChip.Button.ClickMask = __instance.scroller.Hitbox; colorChip.ProductId = skin.ProductId; colorChip.SelectionHighlight.gameObject.SetActive(false); diff --git a/MiraAPI/Patches/Menu/VisorsTabPatches.cs b/MiraAPI/Patches/Menu/VisorsTabPatches.cs index 92d5b8e..e801c1a 100644 --- a/MiraAPI/Patches/Menu/VisorsTabPatches.cs +++ b/MiraAPI/Patches/Menu/VisorsTabPatches.cs @@ -25,6 +25,21 @@ public static class VisorsTabPatches } } + private static void PreviousPage(VisorsTab tab) + { + currentPage--; + currentPage = currentPage < 0 ? SortedVisors.Count - 1 : currentPage; + GenerateHats(tab, currentPage); + } + + private static void NextPage(VisorsTab tab) + { + currentPage++; + currentPage = currentPage > SortedVisors.Count - 1 ? 0 : currentPage; + GenerateHats(tab, currentPage); + } + + [HarmonyPatch(nameof(VisorsTab.OnEnable))] [HarmonyPrefix] public static bool OnEnablePrefix(VisorsTab __instance) @@ -32,6 +47,9 @@ public static class VisorsTabPatches __instance.visorId = HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor).ProdId; if (!SortedVisors.ContainsKey("Vanilla")) AddRange(DestroyableSingleton.Instance.GetUnlockedVisors().Select(x => ("Vanilla", x))); + + InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage); + GenerateHats(__instance, currentPage); return false; @@ -44,15 +62,11 @@ public static class VisorsTabPatches { if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.RightControl) || Input.GetKeyDown(KeyCode.LeftArrow)) { - currentPage--; - currentPage = currentPage < 0 ? SortedVisors.Count - 1 : currentPage; - GenerateHats(__instance, currentPage); + PreviousPage(__instance); } else if (Input.GetKeyDown(KeyCode.Tab) || Input.GetKeyDown(KeyCode.RightArrow)) { - currentPage++; - currentPage = currentPage > SortedVisors.Count - 1 ? 0 : currentPage; - GenerateHats(__instance, currentPage); + NextPage(__instance); } } @@ -72,10 +86,10 @@ public static class VisorsTabPatches text.gameObject.transform.localScale = Vector3.one; text.GetComponent().Destroy(); text.EnableStencilMasking(); - text.text = $"{groupName} ({currentPage + 1}/{SortedVisors.Count})\nPress Ctrl or Tab to cycle pages"; + text.text = $"{groupName} ({currentPage + 1}/{SortedVisors.Count})"; text.alignment = TextAlignmentOptions.Center; - text.fontSize = 3f; - text.fontSizeMax = 3f; + text.fontSize = 5f; + text.fontSizeMax = 5f; text.fontSizeMin = 0f; float xLerp = __instance.XRange.Lerp(0.5f); float yLerp = __instance.YStart - hatIndex / __instance.NumPerRow * __instance.YOffset; @@ -98,9 +112,20 @@ public static class VisorsTabPatches { var colorChip = Object.Instantiate(__instance.ColorTabPrefab, __instance.scroller.Inner); colorChip.gameObject.name = visor.ProductId; - colorChip.Button.OnClick.AddListener((Action)(() => __instance.ClickEquip())); - colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectVisor(visor))); - colorChip.Button.OnMouseOut.AddListener((Action)(() => __instance.SelectVisor(HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor)))); + + if (ActiveInputManager.currentControlType == ActiveInputManager.InputType.Keyboard) + { + colorChip.Button.OnClick.AddListener((Action)__instance.ClickEquip); + colorChip.Button.OnMouseOver.AddListener((Action)(() => __instance.SelectVisor(visor))); + colorChip.Button.OnMouseOut.AddListener( + (Action)(() => + __instance.SelectVisor(HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor)))); + } + else + { + colorChip.Button.OnClick.AddListener((Action)(() => __instance.SelectVisor(visor))); + } + colorChip.Button.ClickMask = __instance.scroller.Hitbox; colorChip.ProductId = visor.ProductId; colorChip.SelectionHighlight.gameObject.SetActive(false); diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs index 4368508..bccb204 100644 --- a/MiraAPI/Utilities/Assets/MiraAssets.cs +++ b/MiraAPI/Utilities/Assets/MiraAssets.cs @@ -12,7 +12,7 @@ public static class MiraAssets { var boxTex = SpriteTools.LoadTextureFromResourcePath( "MiraAPI.Resources.RoundedBox.png", - System.Reflection.Assembly.GetCallingAssembly()); + System.Reflection.Assembly.GetExecutingAssembly()); var boxSprite = Sprite.Create( boxTex, new Rect(0, 0, boxTex.width, boxTex.height), diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index a845fb4..3df7113 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -1,15 +1,16 @@ -using System; -using System.Collections; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; +using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; -using MiraAPI.Keybinds; using MiraAPI.Roles; using MiraAPI.Utilities.Assets; +using Reactor.Utilities; using Rewired; using TMPro; using UnityEngine; +using MethodBase = System.Reflection.MethodBase; using Object = UnityEngine.Object; namespace MiraAPI.Utilities; @@ -38,6 +39,31 @@ public static class Helpers return keybindIcon; } + public static MethodBase? GetStateMachineMoveNext(string methodName) + { + var typeName = typeof(T).FullName; + var showRoleStateMachine = + typeof(T) + .GetNestedTypes() + .FirstOrDefault(x=>x.Name.Contains(methodName)); + + if (showRoleStateMachine == null) + { + Logger.Error($"Failed to find {methodName} state machine for {typeName}"); + return null; + } + + var moveNext = AccessTools.Method(showRoleStateMachine, "MoveNext"); + if (moveNext == null) + { + Logger.Error($"Failed to find MoveNext method for {typeName}.{methodName}"); + return null; + } + + Logger.Info($"Found {methodName}.MoveNext"); + return moveNext; + } + /// /// Creates a draggable scroller. Add items into the scroller.Inner transform. This does not automatically position children. /// diff --git a/MiraAPI/Utilities/StateMachineWrapper.cs b/MiraAPI/Utilities/StateMachineWrapper.cs new file mode 100644 index 0000000..f8d9d8b --- /dev/null +++ b/MiraAPI/Utilities/StateMachineWrapper.cs @@ -0,0 +1,42 @@ +using System.Reflection; +using HarmonyLib; +using Il2CppInterop.Runtime.InteropTypes; + +namespace MiraAPI.Utilities; + +/// +/// A wrapper for state machine objects to access their parent instance and state. +/// +/// The type of the parent class that owns the state machine. +public class StateMachineWrapper where T : Il2CppObjectBase +{ + private readonly Il2CppObjectBase _stateMachine; + private readonly PropertyInfo _thisProperty; + private readonly PropertyInfo _stateProperty; + + private T? _parentInstance; + + /// + /// Gets the instance of the parent class that owns the state machine. + /// + public T Instance => _parentInstance ??= (T)_thisProperty.GetValue(_stateMachine)!; + + /// + /// Initializes a new instance of the class. + /// + /// The state machine instance to wrap. + public StateMachineWrapper(Il2CppObjectBase stateMachine) + { + _stateMachine = stateMachine; + + var type = _stateMachine.GetType(); + _thisProperty = AccessTools.Property(type, "__4__this"); + _stateProperty = AccessTools.Property(type, "__1__state"); + } + + /// + /// Gets the current state of the state machine. + /// + /// The current state as an integer. + public int GetState() => (int)_stateProperty.GetValue(_stateMachine)!; +}