From b6881e4d773d8c72575368b9cab5bd17854312d2 Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:34:06 -0500 Subject: [PATCH] Add WIP advanced role guide tab --- MiraAPI/Patches/RoleGuidePatches.cs | 110 +++++++++++++++++++++++++++- MiraAPI/Roles/ICustomRole.cs | 22 +++++- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/MiraAPI/Patches/RoleGuidePatches.cs b/MiraAPI/Patches/RoleGuidePatches.cs index a4878e4..0b5c88b 100644 --- a/MiraAPI/Patches/RoleGuidePatches.cs +++ b/MiraAPI/Patches/RoleGuidePatches.cs @@ -7,7 +7,11 @@ using HarmonyLib; using Innersloth.Assets; using MiraAPI.Roles; using MiraAPI.Utilities; +using Reactor.Utilities.Extensions; +using TMPro; using UnityEngine; +using UnityEngine.Events; +using UnityEngine.UI; using Object = UnityEngine.Object; namespace MiraAPI.Patches; @@ -39,6 +43,8 @@ public static class RoleGuidePatches __instance.MatchInfoRoleMaskArea.transform.parent.GetAllChildren() .First(x => x.name.Contains("BG_Gradient")).GetComponent() .maskInteraction = SpriteMaskInteraction.VisibleInsideMask; + var wikiTab = Object.Instantiate(__instance.settingsTabs[2], __instance.settingsTabs[2].transform.parent); + AdvancedWikiTab = wikiTab.GetComponent(); } [HarmonyPrefix] @@ -75,13 +81,24 @@ public static class RoleGuidePatches bool enabled = ActiveInputManager.currentControlType == ActiveInputManager.InputType.Joystick; __instance.glyphL.enabled = enabled; __instance.glyphR.enabled = enabled; + if (!TitleText) + { + TitleText = __instance.transitionOpen.transform.FindChild("Text_Title")?.GetComponent()!; + if (TitleText) + { + TitleText.transform.GetComponent().Destroy(); + } + } + if (TitleText) + { + TitleText.text = TranslationController.Instance.GetString(StringNames.MatchInfoGuideTitle); + } if (GameManager.Instance.TryCast() != null) { if (__instance.NormalModeSettings.Count == 0) { __instance.numOfTabs = 3; __instance.TabButtons[0].SelectButton(true); - DisplayNormalRoleSettings(__instance, true); __instance.MatchInfoRoleMaskArea.transform.localPosition = new Vector3(-0.0184f, 0.15f, -0.1f); __instance.matchInfoPlayersMaskArea.transform.localPosition = @@ -100,6 +117,11 @@ public static class RoleGuidePatches __instance.MatchInfoRoleMaskArea.transform.parent.GetAllChildren() .First(x => x.name.Contains("BG_Gradient")).GetComponent() .maskInteraction = SpriteMaskInteraction.VisibleInsideMask; + __instance.MatchInfoRoleMaskArea.material.SetInt(PlayerMaterial.MaskLayer, 50); + __instance.matchInfoSettingsMaskArea.material.SetInt(PlayerMaterial.MaskLayer, 50); + var wikiTab = Object.Instantiate(__instance.settingsTabs[2], __instance.settingsTabs[2].transform.parent); + AdvancedWikiTab = wikiTab.GetComponent(); + DisplayNormalRoleSettings(__instance, true); } else { @@ -126,6 +148,9 @@ public static class RoleGuidePatches } private static Dictionary _rolePanels = []; + public static Scroller AdvancedWikiTab; + public static GameObject CurrentAdvancedTabObject; + public static TextMeshPro TitleText; public static void DisplayNormalRoleSettings(MatchInfoGuide instance, bool reset) { @@ -156,6 +181,7 @@ public static class RoleGuidePatches instance.CreateSettingsEntry( StringNames.GameTaskBarMode, GameManager.Instance.LogicOptions.GetTaskBarMode().ToString()); + var hoverColor = new Color32(255, 255, 255, 150); foreach (RoleBehaviour roleBehaviour in RoleManager.Instance.AllRoles) { if (roleBehaviour.Role != RoleTypes.Crewmate && roleBehaviour.Role != RoleTypes.Impostor && @@ -165,10 +191,31 @@ public static class RoleGuidePatches var panel = Object.Instantiate( instance.MatchInfoRolePanelPrefab, instance.settingsTabs[2].GetComponent().Inner); + var collider = panel.roleIcon.gameObject.AddComponent(); + collider.size = new Vector2(0.13f, 0.13f); + collider.offset = new Vector2(0, 0); + var passiveButton = panel.roleIcon.gameObject.AddComponent(); + passiveButton.OnMouseOver = new UnityEvent(); + passiveButton.OnMouseOver.AddListener( + (UnityAction)(() => + { + panel.roleIcon.color = hoverColor; + })); + passiveButton.OnMouseOut = new UnityEvent(); + passiveButton.OnMouseOut.AddListener( + (UnityAction)(() => + { + panel.roleIcon.color = Color.white; + })); + + passiveButton.OnClick = new Button.ButtonClickedEvent(); + passiveButton.OnClick.AddListener( + (UnityAction)(() => { DisplayAdvancedWiki(instance, roleBehaviour); })); _rolePanels.Add(roleBehaviour, panel); } } } + AdvancedWikiTab?.gameObject.SetActive(false); int num = 0; foreach (var pair in _rolePanels) @@ -200,8 +247,6 @@ public static class RoleGuidePatches } instance.MatchInfoRoleScroller.SetYBoundsMax(Mathf.Clamp(Mathf.Ceil((float)num / 2f) * 1.3f - 1.5f, 0f, 999f)); - instance.MatchInfoRoleMaskArea.material.SetInt(PlayerMaterial.MaskLayer, 50); - instance.matchInfoSettingsMaskArea.material.SetInt(PlayerMaterial.MaskLayer, 50); if (reset) { instance.CreatePlayerEntries(); @@ -242,6 +287,65 @@ public static class RoleGuidePatches return false; } + + [HarmonyPostfix] + [HarmonyPriority(Priority.First)] + [HarmonyPatch(typeof(MatchInfoGuide), nameof(MatchInfoGuide.SetActiveTab))] + private static void SetActiveTab(MatchInfoGuide __instance) + { + if (TitleText) + { + TitleText.text = TranslationController.Instance.GetString(StringNames.MatchInfoGuideTitle); + } + AdvancedWikiTab?.gameObject.SetActive(false); + } + + public static void DisplayAdvancedWiki(MatchInfoGuide instance, RoleBehaviour role) + { + Warning($"Opening advanced tab for {role.GetRoleName()}."); + instance.settingsTabs[2].SetActive(false); + AdvancedWikiTab?.gameObject.SetActive(true); + if (CurrentAdvancedTabObject) + { + CurrentAdvancedTabObject.SetActive(false); + CurrentAdvancedTabObject.Destroy(); + } + + if (AdvancedWikiTab == null) + { + Warning($"Wiki tab is null."); + return; + } + 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 description = TranslationController.Instance.GetString(role.BlurbNameLong); + if (description.Contains("STRMISS")) + { + var baseName = ($"{role.StringName}").Replace("Role", ""); + if (Enum.TryParse($"RolesHelp_{baseName}_01", out var helpName)) + { + description = TranslationController.Instance.GetString(helpName); + } + } + + desc.text = description; + desc.transform.localPosition = new Vector3(0, 0.925f, 0); + desc.rectTransform.sizeDelta = new Vector2(7.5f, 0.3f); + desc.alignment = TextAlignmentOptions.TopLeft; + CurrentAdvancedTabObject.transform.SetParent(AdvancedWikiTab.Inner.transform); + CurrentAdvancedTabObject.transform.localPosition = new Vector3(0f, 0f, 0f); + } + } [HarmonyPrefix] [HarmonyPriority(Priority.First)] [HarmonyPatch(typeof(MatchInfoRolePanel), nameof(MatchInfoRolePanel.SetPanel))] diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index adda079..233a836 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -6,7 +6,9 @@ using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.PluginLoading; using MiraAPI.Translation; +using TMPro; using UnityEngine; +using Object = UnityEngine.Object; namespace MiraAPI.Roles; @@ -68,7 +70,7 @@ public interface ICustomRole : IOptionable /// /// Gets the wiki description of the role. Used in the wiki, but not currently required. /// - string RoleWikiDescription => MiraLocaleManager.Get(RoleLongDescriptionLocale); + string RoleWikiDescription => MiraLocaleManager.Get(RoleWikiDescriptionLocale, RoleLongDescription); /// /// Gets the role's wiki description id for localization. @@ -310,6 +312,24 @@ public interface ICustomRole : IOptionable /// if the role is always displayed, otherwise if it is never displayable, or if it is dictated by amount and chance. public virtual bool? ForceShowRoleOnWiki => null; + /// + /// Gets the information to display in the wiki. + /// + 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.transform.localPosition = new Vector3(0, 0.925f, 0); + desc.rectTransform.sizeDelta = new Vector2(7.5f, 0.3f); + desc.alignment = TextAlignmentOptions.TopLeft; + obj.transform.SetParent(parent.Inner.transform); + return obj; + } + /// /// Gets the function that determines whether the role should be toggled on or off in the game settings. ///