From 2cfdbea51f1d22522e40a4f7dd0d7fb7f30ca123 Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Wed, 23 Sep 2026 21:53:46 -0500 Subject: [PATCH 1/2] Reduce duped code --- MiraAPI/Patches/RoleGuidePatches.cs | 23 +++++++++++------------ MiraAPI/Roles/ICustomRole.cs | 18 +++++++++--------- MiraAPI/Utilities/Helpers.cs | 17 +++++++++++++++++ 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/MiraAPI/Patches/RoleGuidePatches.cs b/MiraAPI/Patches/RoleGuidePatches.cs index e1ae59c..c968932 100644 --- a/MiraAPI/Patches/RoleGuidePatches.cs +++ b/MiraAPI/Patches/RoleGuidePatches.cs @@ -183,7 +183,7 @@ public static class RoleGuidePatches StringNames.GameTaskBarMode, GameManager.Instance.LogicOptions.GetTaskBarMode().ToString()); var hoverColor = new Color32(255, 255, 255, 150); - foreach (RoleBehaviour roleBehaviour in RoleManager.Instance.AllRoles) + foreach (RoleBehaviour roleBehaviour in RoleManager.Instance.AllRoles.ToArray().OrderBy(x => x.GetRoleName())) { if (roleBehaviour.Role != RoleTypes.Crewmate && roleBehaviour.Role != RoleTypes.Impostor && roleBehaviour.Role is not RoleTypes.CrewmateGhost && @@ -196,6 +196,7 @@ public static class RoleGuidePatches collider.size = new Vector2(0.13f, 0.13f); collider.offset = new Vector2(0, 0); var passiveButton = panel.roleIcon.gameObject.AddComponent(); + passiveButton.ClickSound = HudManager.Instance.MapButton.ClickSound; passiveButton.OnMouseOver = new UnityEvent(); passiveButton.OnMouseOver.AddListener( (UnityAction)(() => @@ -320,15 +321,11 @@ public static class RoleGuidePatches if (role is ICustomRole custom) { CurrentAdvancedTabObject = custom.GetAdvancedWiki(instance, TitleText, AdvancedWikiTab); - CurrentAdvancedTabObject.transform.localPosition = new Vector3(0f, 0f, 0f); } else { AdvancedWikiTab.ScrollToTop(); - CurrentAdvancedTabObject = new GameObject($"{role.Role}"); - TitleText.text = role.GetRoleName() + $" ({TranslationController.Instance.GetString(role.TeamType is RoleTeamTypes.Crewmate ? StringNames.Crewmate : StringNames.Impostor)})"; - var desc = Object.Instantiate(instance.MatchInfoRolePanelPrefab.roleCount, CurrentAdvancedTabObject.transform); - desc.fontSizeMin = desc.fontSizeMax = desc.fontSize = 2f; + var titleTxt = role.GetRoleName() + $" ({TranslationController.Instance.GetString(role.TeamType is RoleTeamTypes.Crewmate ? StringNames.Crewmate : StringNames.Impostor)})"; var description = TranslationController.Instance.GetString(role.BlurbNameLong); if (description.Contains("STRMISS")) { @@ -339,14 +336,16 @@ public static class RoleGuidePatches } } - desc.text = description; - desc.rectTransform.sizeDelta = new Vector2(7.5f, 0.3f); - desc.alignment = TextAlignmentOptions.TopLeft; + CurrentAdvancedTabObject = Helpers.CreateAdvancedWikiTab( + instance, + role.Role.ToString(), + titleTxt, + description, + TitleText, + out var desc); CurrentAdvancedTabObject.transform.SetParent(AdvancedWikiTab.Inner.transform); - desc.transform.localPosition = new Vector3(0, 1.125f, 0); CurrentAdvancedTabObject.transform.localPosition = new Vector3(0f, 0f, 0f); - desc.ForceMeshUpdate(); - AdvancedWikiTab.SetYBoundsMax(Mathf.Clamp(desc.textBounds.size.y- 2, 0f, 999f)); + AdvancedWikiTab.SetYBoundsMax(Mathf.Clamp(desc.textBounds.size.y - 2, 0f, 999f)); } } [HarmonyPrefix] diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index d495298..6906ab5 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -6,6 +6,7 @@ using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.PluginLoading; using MiraAPI.Translation; +using MiraAPI.Utilities; using TMPro; using UnityEngine; using Object = UnityEngine.Object; @@ -318,16 +319,15 @@ public interface ICustomRole : IOptionable public virtual GameObject GetAdvancedWiki(MatchInfoGuide guide, TextMeshPro titleText, Scroller parent) { parent.ScrollToTop(); - var obj = new GameObject(RoleNameLocale); - titleText.text = RoleName + $" ({RoleFactionTitle})"; - var desc = Object.Instantiate(guide.MatchInfoRolePanelPrefab.roleCount, obj.transform); - desc.fontSizeMin = desc.fontSizeMax = desc.fontSize = 2f; - desc.text = RoleWikiDescription; - desc.rectTransform.sizeDelta = new Vector2(7.5f, 0.3f); - desc.alignment = TextAlignmentOptions.TopLeft; + var obj = Helpers.CreateAdvancedWikiTab( + guide, + RoleNameLocale, + RoleName + $" ({RoleFactionTitle})", + RoleWikiDescription, + titleText, + out var desc); obj.transform.SetParent(parent.Inner.transform); - desc.transform.localPosition = new Vector3(0, 1.125f, 0); - desc.ForceMeshUpdate(); + obj.transform.localPosition = new Vector3(0f, 0f, 0f); parent.SetYBoundsMax(Mathf.Clamp(desc.textBounds.size.y - 2, 0f, 999f)); return obj; } diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index 5a70c2c..a983d53 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -594,4 +594,21 @@ public static class Helpers // This should be patchable by mods when a vanilla role is meant to be replaced by a custom role. return false; } + public static GameObject CreateAdvancedWikiTab(MatchInfoGuide guide, string objName, string title, string description, TextMeshPro titleTmp, out TextMeshPro descriptionTmp) + { + var obj = new GameObject(objName); + titleTmp.text = title; + descriptionTmp = Object.Instantiate(guide.MatchInfoRolePanelPrefab.roleCount, obj.transform); + descriptionTmp.fontSizeMin = descriptionTmp.fontSizeMax = descriptionTmp.fontSize = 2f; + var sourceTmp = HudManager.Instance.TaskPanel.taskText; + descriptionTmp.font = sourceTmp.font; + descriptionTmp.fontMaterial = sourceTmp.fontMaterial; + descriptionTmp.EnableStencilMasking(); + descriptionTmp.text = description; + descriptionTmp.rectTransform.sizeDelta = new Vector2(7.5f, 0.3f); + descriptionTmp.alignment = TextAlignmentOptions.TopLeft; + descriptionTmp.transform.localPosition = new Vector3(0, 1.125f, 0); + descriptionTmp.ForceMeshUpdate(); + return obj; + } } From f89ffb0d867e41349b34665b63ef7b7d1a63e7ce Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Thu, 24 Sep 2026 17:08:56 -0500 Subject: [PATCH 2/2] Find the latest role options collection at start This should help avoid the api breaking from additional roles and additional settings. --- MiraAPI/MiraApiPlugin.cs | 5 ++ .../Roles/RoleOptionsCollectionPatch.cs | 68 ++++++++++++++++--- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/MiraAPI/MiraApiPlugin.cs b/MiraAPI/MiraApiPlugin.cs index 0ea0b20..9fe50b8 100644 --- a/MiraAPI/MiraApiPlugin.cs +++ b/MiraAPI/MiraApiPlugin.cs @@ -1,10 +1,14 @@ global using static Reactor.Utilities.Logger; using System; using System.Globalization; +using System.Linq; +using System.Reflection; +using AmongUs.GameOptions; using BepInEx; using BepInEx.Configuration; using BepInEx.Unity.IL2CPP; using HarmonyLib; +using MiraAPI.Patches.Roles; using MiraAPI.PluginLoading; using MiraAPI.Translation; using MiraAPI.VanillaEvents; @@ -70,6 +74,7 @@ public partial class MiraApiPlugin : BasePlugin, IMiraPlugin public override void Load() { Harmony.PatchAll(); + RoleOptionsCollectionPatch.PatchRoleMethods(Harmony); UiResetEvents.Initialize(); JudgeEvents.Initialize(); diff --git a/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs b/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs index 011fc28..8f71dde 100644 --- a/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs +++ b/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs @@ -1,4 +1,7 @@ -using AmongUs.GameOptions; +using System; +using System.Linq; +using System.Reflection; +using AmongUs.GameOptions; using HarmonyLib; using Il2CppSystem.Collections.Generic; using MiraAPI.Roles; @@ -8,17 +11,66 @@ namespace MiraAPI.Patches.Roles; /// /// Patches to return the correct role counts. /// -[HarmonyPatch(typeof(RoleOptionsCollectionV11))] public static class RoleOptionsCollectionPatch { + internal static Assembly BaseAssembly { get; private set; } + internal static Type[] BaseTypes { get; private set; } + internal static Type CollectionsType { get; private set; } + public static void PatchRoleMethods(Harmony harmony) + { + var assembly = Array.Find( + AppDomain.CurrentDomain.GetAssemblies(), + ass => ass.GetName().Name == "Assembly-CSharp" + ); + if (assembly != null) + { + var compatType = typeof(RoleOptionsCollectionPatch); + BaseAssembly = assembly; + BaseTypes = AccessTools.GetTypesFromAssembly(assembly); + var collections = BaseTypes.Where(x => x.Name.Contains("RoleOptionsCollectionV")).ToArray(); + var pairings = new System.Collections.Generic.Dictionary(); + var newestId = 0; + foreach (var collection in collections) + { + var remainer = collection.Name.Replace("RoleOptionsCollectionV", string.Empty); + if (int.TryParse(remainer, out var id)) + { + pairings.Add(id, collection); + if (newestId < id) + { + newestId = id; + } + } + } + + if (pairings.TryGetValue(newestId, out var typeToGet)) + { + CollectionsType = typeToGet; + } + + var anyRolesEnabledMethod = AccessTools.Method(CollectionsType, "AnyRolesEnabled"); + harmony.Patch( + anyRolesEnabledMethod, + new HarmonyMethod(AccessTools.Method(compatType, nameof(AnyRolesEnabledPrefix)))); + + var chancePerGameMethod = AccessTools.Method(CollectionsType, "GetChancePerGame"); + harmony.Patch( + chancePerGameMethod, + new HarmonyMethod(AccessTools.Method(compatType, nameof(GetChancePrefix)))); + + var numPerGameMethod = AccessTools.Method(CollectionsType, "GetNumPerGame"); + harmony.Patch( + numPerGameMethod, + new HarmonyMethod(AccessTools.Method(compatType, nameof(GetNumPrefix)))); + Info($"Patched methods for RoleOptionsCollectionV{newestId}"); + } + } /// /// This patch fixes being inlined (2025.9.9) in the original code. /// - [HarmonyPrefix] - [HarmonyPatch(nameof(RoleOptionsCollectionV11.AnyRolesEnabled))] - public static bool AnyRolesEnabledPrefix(RoleOptionsCollectionV11 __instance, ref bool __result) + public static bool AnyRolesEnabledPrefix(dynamic __instance, ref bool __result) { - foreach (KeyValuePair keyValuePair in __instance.roles) + foreach (KeyValuePair keyValuePair in __instance.roles) { if (__instance.GetNumPerGame(keyValuePair.Key) > 0) { @@ -32,8 +84,6 @@ public static class RoleOptionsCollectionPatch /// Set the role chance for custom Launchpad roles based on config. /// /// Return to skip original method, to not. - [HarmonyPrefix] - [HarmonyPatch(nameof(RoleOptionsCollectionV11.GetChancePerGame))] public static bool GetChancePrefix(RoleTypes role, ref int __result) { if (!CustomRoleManager.GetCustomRoleBehaviour(role, out var customRole) || customRole == null) @@ -62,8 +112,6 @@ public static class RoleOptionsCollectionPatch /// Set the amount for custom Launchpad roles based on config. /// /// Return to skip original method, to not. - [HarmonyPrefix] - [HarmonyPatch(nameof(RoleOptionsCollectionV11.GetNumPerGame))] public static bool GetNumPrefix(RoleTypes role, ref int __result) { if (!CustomRoleManager.GetCustomRoleBehaviour(role, out var customRole) || customRole == null)