diff --git a/MiraAPI/MiraApiPlugin.cs b/MiraAPI/MiraApiPlugin.cs index ba70406..54d23ce 100644 --- a/MiraAPI/MiraApiPlugin.cs +++ b/MiraAPI/MiraApiPlugin.cs @@ -1,9 +1,13 @@ 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; @@ -66,6 +70,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/RoleGuidePatches.cs b/MiraAPI/Patches/RoleGuidePatches.cs index db4b347..6b17ea3 100644 --- a/MiraAPI/Patches/RoleGuidePatches.cs +++ b/MiraAPI/Patches/RoleGuidePatches.cs @@ -190,7 +190,7 @@ public static class RoleGuidePatches GameManager.Instance.LogicOptions.GetTaskBarMode().ToString()); var hoverColor = new Color32(255, 255, 255, 150); - foreach (var roleBehaviour in RoleManager.Instance.AllRoles) + foreach (var roleBehaviour in RoleManager.Instance.AllRoles.ToArray().OrderBy(x => x.GetRoleName())) { if (roleBehaviour.Role is not RoleTypes.Crewmate and not RoleTypes.Impostor and not RoleTypes.CrewmateGhost and @@ -203,6 +203,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)(() => @@ -330,15 +331,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")) { @@ -349,13 +346,15 @@ 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)); } } diff --git a/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs b/MiraAPI/Patches/Roles/RoleOptionsCollectionPatch.cs index 45a1987..1fc0046 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 MiraAPI.Roles; @@ -7,15 +10,64 @@ 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) + public static bool AnyRolesEnabledPrefix(dynamic __instance) { foreach (var keyValuePair in __instance.roles) { @@ -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) diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 3d9878a..7af4287 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; @@ -319,16 +320,15 @@ public interface ICustomRole : IOptionable 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 35280ab..e3525b9 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -563,4 +563,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; + } }