fix double dummy roles being spawned on the panel

This commit is contained in:
neilmjain committed 2026-10-14 22:00:23 -04:00
1 parent 144c762ae3
commit 901503f762
1 file changed
+139 -20
+139 -20
View File
@@ -7,6 +7,7 @@ using System.Linq;
using AmongUs.GameOptions; using AmongUs.GameOptions;
using HarmonyLib; using HarmonyLib;
using Innersloth.Assets; using Innersloth.Assets;
using MiraAPI.GameModes;
using MiraAPI.LocalSettings; using MiraAPI.LocalSettings;
using MiraAPI.Modifiers; using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types; using MiraAPI.Modifiers.Types;
@@ -37,6 +38,7 @@ public static class RoleGuidePatches
private static readonly List<DetailedPanel> RoleEntries = []; private static readonly List<DetailedPanel> RoleEntries = [];
private static readonly List<DetailedPanel> ModifierEntries = []; private static readonly List<DetailedPanel> ModifierEntries = [];
private static readonly List<string> UntrackedRolePanelDiagnostics = [];
private static GameObject searchComponentContainer; private static GameObject searchComponentContainer;
public static PassiveButton SearchIconButton; public static PassiveButton SearchIconButton;
@@ -70,12 +72,18 @@ public static class RoleGuidePatches
private static int _activeGuideTabIndex = -1; private static int _activeGuideTabIndex = -1;
private static IEnumerator? _populate; private static IEnumerator? _populate;
private static bool _loading; private static bool _loading;
private static Type? _roleEntriesMode;
private static bool _roleEntriesInitialized;
private static bool _rolePanelsCleaned;
private static LoadingRing? _loadingSpinner; private static LoadingRing? _loadingSpinner;
private static SortingFilter _sortFilter = SortingFilter.EnabledOnly; private static SortingFilter _sortFilter = SortingFilter.EnabledOnly;
private static SortingMethod _sortMethod = SortingMethod.Alphabetical; private static SortingMethod _sortMethod = SortingMethod.Alphabetical;
private static SortingGroups _sortGrouping = SortingGroups.Ungrouped; private static SortingGroups _sortGrouping = SortingGroups.Ungrouped;
private static bool IsModifiersTabActive => _activeGuideTabIndex == ModifiersTabIndex; private static int ActiveGuideTabIndex =>
_guide && MatchInfoGuide.Instance == _guide ? MatchInfoGuide.Instance.activeTabIndex : _activeGuideTabIndex;
private static bool IsModifiersTabActive => ActiveGuideTabIndex == ModifiersTabIndex;
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPriority(Priority.First)] [HarmonyPriority(Priority.First)]
@@ -162,6 +170,10 @@ public static class RoleGuidePatches
RoleEntries.Clear(); RoleEntries.Clear();
ModifierEntries.Clear(); ModifierEntries.Clear();
UntrackedRolePanelDiagnostics.Clear();
_roleEntriesMode = null;
_roleEntriesInitialized = false;
_rolePanelsCleaned = false;
_guide = null; _guide = null;
_activeGuideTabIndex = -1; _activeGuideTabIndex = -1;
_uiInitialized = false; _uiInitialized = false;
@@ -223,11 +235,7 @@ public static class RoleGuidePatches
} }
_guide = instance; _guide = instance;
if (RoleEntries.Count == 0)
{
BuildEntries(); BuildEntries();
}
if (_populate != null) if (_populate != null)
{ {
yield break; yield break;
@@ -689,6 +697,8 @@ public static class RoleGuidePatches
} }
private static void BuildEntries() private static void BuildEntries()
{
if (ModifierEntries.Count == 0)
{ {
foreach (var modifier in ModifierManager.Modifiers.OfType<GameModifier>()) foreach (var modifier in ModifierManager.Modifiers.OfType<GameModifier>())
{ {
@@ -699,10 +709,31 @@ public static class RoleGuidePatches
null, null,
modifier)); modifier));
} }
}
var activeMode = CustomGameModeManager.ActiveMode;
var activeModeType = activeMode?.GetType();
if (_roleEntriesInitialized && _roleEntriesMode == activeModeType)
{
return;
}
foreach (var entry in RoleEntries)
{
if (entry.Panel)
{
entry.Panel!.gameObject.Destroy();
}
}
RoleEntries.Clear();
_rolePanelsCleaned = false;
foreach (var roleBehaviour in CustomRoleManager.AllStoredRoleBehaviours.Where(x => foreach (var roleBehaviour in CustomRoleManager.AllStoredRoleBehaviours.Where(x =>
x.Role is not RoleTypes.Crewmate and not RoleTypes.Impostor and x.Role is not RoleTypes.Crewmate and not RoleTypes.Impostor and
not RoleTypes.CrewmateGhost and not RoleTypes.ImpostorGhost)) not RoleTypes.CrewmateGhost and not RoleTypes.ImpostorGhost &&
CustomRoleUtils.CanSpawnOnCurrentMode(x))
.GroupBy(x => x.Role)
.Select(group => group.First()))
{ {
var modId = roleBehaviour is ICustomRole custom var modId = roleBehaviour is ICustomRole custom
? custom.ParentMod.MiraPlugin.GetAbbreviatedModName() ? custom.ParentMod.MiraPlugin.GetAbbreviatedModName()
@@ -714,6 +745,9 @@ public static class RoleGuidePatches
roleBehaviour, roleBehaviour,
null)); null));
} }
_roleEntriesMode = activeModeType;
_roleEntriesInitialized = true;
} }
private static void SetupTabButton(MatchInfoGuideTabButton button, Sprite inactive, Sprite selected, Sprite hover) private static void SetupTabButton(MatchInfoGuideTabButton button, Sprite inactive, Sprite selected, Sprite hover)
@@ -756,10 +790,54 @@ public static class RoleGuidePatches
_wikiScroller?.gameObject.SetActive(false); _wikiScroller?.gameObject.SetActive(false);
_activeGuideTabIndex = tabIndex; _activeGuideTabIndex = tabIndex;
MatchInfoGuide.Instance.SetActiveTab(tabIndex); MatchInfoGuide.Instance.SetActiveTab(tabIndex);
if (tabIndex == RolesTabIndex)
{
BuildEntries();
if (!_rolePanelsCleaned)
{
_rolePanelsCleaned = RemoveUntrackedRolePanels();
}
}
HidePanels(tabIndex == ModifiersTabIndex ? ModifierEntries : RoleEntries); HidePanels(tabIndex == ModifiersTabIndex ? ModifierEntries : RoleEntries);
StartPopulate(delay); StartPopulate(delay);
} }
private static bool RemoveUntrackedRolePanels()
{
if (_rolesScroller?.Inner == null)
{
return false;
}
var trackedPanels = RoleEntries
.Where(entry => entry.Panel)
.Select(entry => entry.Panel!)
.ToHashSet();
foreach (var panel in _rolesScroller.Inner.GetComponentsInChildren<MatchInfoRolePanel>(true))
{
if (!trackedPanels.Contains(panel))
{
var panelName = panel.roleName ? panel.roleName.text : panel.name;
UntrackedRolePanelDiagnostics.Add(panelName);
panel.gameObject.Destroy();
}
}
if (UntrackedRolePanelDiagnostics.Count > 0)
{
var panelNames = string.Join(", ", UntrackedRolePanelDiagnostics.Take(10));
var additionalCount = UntrackedRolePanelDiagnostics.Count - 10;
Logger<MiraApiPlugin>.Warning(
$"Removed or blocked {UntrackedRolePanelDiagnostics.Count} untracked Match Info role panel(s) in " +
$"{CustomGameModeManager.ActiveMode?.GetType().Name ?? "vanilla"} mode: {panelNames}" +
(additionalCount > 0 ? $", and {additionalCount} more" : string.Empty));
UntrackedRolePanelDiagnostics.Clear();
}
return true;
}
private static void HidePanels(List<DetailedPanel> entries) private static void HidePanels(List<DetailedPanel> entries)
{ {
foreach (var entry in entries) foreach (var entry in entries)
@@ -847,7 +925,7 @@ public static class RoleGuidePatches
if (_loadingSpinner != null) if (_loadingSpinner != null)
{ {
_loadingSpinner.gameObject.SetActive( _loadingSpinner.gameObject.SetActive(
_loading && _activeGuideTabIndex is RolesTabIndex or ModifiersTabIndex && _loading && ActiveGuideTabIndex is RolesTabIndex or ModifiersTabIndex &&
!(_wikiScroller && _wikiScroller.gameObject.activeSelf)); !(_wikiScroller && _wikiScroller.gameObject.activeSelf));
} }
} }
@@ -892,8 +970,16 @@ public static class RoleGuidePatches
}); });
_loading = false; _loading = false;
UpdateSpinner(); UpdateSpinner();
yield return CoForEachBudgeted(entries, EnsurePanel); if (!modifiers)
yield return CoForEachBudgeted(modifiers ? RoleEntries : ModifierEntries, PreloadPanel); {
RemoveUntrackedRolePanels();
yield return null;
if (_guide)
{
RemoveUntrackedRolePanels();
}
}
_populate = null; _populate = null;
} }
@@ -1125,7 +1211,8 @@ public static class RoleGuidePatches
return false; return false;
} }
var entries = _activeGuideTabIndex switch var activeTabIndex = ActiveGuideTabIndex;
var entries = activeTabIndex switch
{ {
RolesTabIndex => RoleEntries, RolesTabIndex => RoleEntries,
ModifiersTabIndex => ModifierEntries, ModifiersTabIndex => ModifierEntries,
@@ -1151,11 +1238,11 @@ public static class RoleGuidePatches
entry.Panel!.transform.SetAsLastSibling(); entry.Panel!.transform.SetAsLastSibling();
} }
var scroller = _activeGuideTabIndex == ModifiersTabIndex var scroller = activeTabIndex == ModifiersTabIndex
? _modifiersScroller ? _modifiersScroller
: guide.MatchInfoRoleScroller; : guide.MatchInfoRoleScroller;
scroller.SetYBoundsMax(GetGridScrollBounds(shown.Count)); scroller.SetYBoundsMax(GetGridScrollBounds(shown.Count));
var noResultsMessage = _activeGuideTabIndex == ModifiersTabIndex var noResultsMessage = activeTabIndex == ModifiersTabIndex
? _modifierNoResultsMessage ? _modifierNoResultsMessage
: guide.rolesEnabledMessage; : guide.rolesEnabledMessage;
SetNoResultsVisible(noResultsMessage, shown.Count == 0); SetNoResultsVisible(noResultsMessage, shown.Count == 0);
@@ -1634,6 +1721,17 @@ public static class RoleGuidePatches
[HarmonyPatch(typeof(MatchInfoRolePanel), nameof(MatchInfoRolePanel.SetPanel))] [HarmonyPatch(typeof(MatchInfoRolePanel), nameof(MatchInfoRolePanel.SetPanel))]
public static bool SetPanel(MatchInfoRolePanel __instance, RoleBehaviour role, int numPerGame, int chancePerGame) public static bool SetPanel(MatchInfoRolePanel __instance, RoleBehaviour role, int numPerGame, int chancePerGame)
{ {
var rolesInner = _rolesScroller ? _rolesScroller.Inner : null;
if (rolesInner &&
__instance.transform.IsChildOf(rolesInner) &&
!RoleEntries.Any(entry => entry.Panel == __instance))
{
UntrackedRolePanelDiagnostics.Add(
role is ICustomRole untrackedRole ? untrackedRole.RoleName : role.NiceName);
__instance.gameObject.Destroy();
return false;
}
__instance.roleCount.text = string.Format( __instance.roleCount.text = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
"{0} at {1}%", "{0} at {1}%",
@@ -1754,9 +1852,11 @@ public static class RoleGuidePatches
private static string _savedGroupLabel = string.Empty; private static string _savedGroupLabel = string.Empty;
private static Vector3 _savedFilterIconScale = Vector3.one; private static Vector3 _savedFilterIconScale = Vector3.one;
private static Vector3 _savedGroupIconScale = Vector3.one; private static Vector3 _savedGroupIconScale = Vector3.one;
private static Vector3 _savedFilterLabelPosition = Vector3.zero;
private static Vector3 _savedGroupLabelPosition = Vector3.zero;
private static bool IsSettingsTabActive => private static bool IsSettingsTabActive =>
_uiInitialized && MatchInfoGuide.Instance && _activeGuideTabIndex == SettingsTabIndex; _uiInitialized && MatchInfoGuide.Instance && ActiveGuideTabIndex == SettingsTabIndex;
private static void ResetSettingsState() private static void ResetSettingsState()
{ {
@@ -1918,18 +2018,25 @@ public static class RoleGuidePatches
private static void ShowSettingsEntries(IReadOnlyList<GuideOptionEntry> entries, bool showGroup, bool scrollToTop) private static void ShowSettingsEntries(IReadOnlyList<GuideOptionEntry> entries, bool showGroup, bool scrollToTop)
{ {
var rows = new List<KeyValuePair<string, string>>(entries.Count); var orderedEntries = showGroup
foreach (var entry in entries) ? entries.GroupBy(entry => entry.Group).SelectMany(group => group)
{ : entries;
var label = showGroup ? $"<size=60%>{entry.GroupName}</size>\n{entry.Title}" : entry.Title; var rows = orderedEntries
rows.Add(new KeyValuePair<string, string>(label, entry.Value)); .Select(entry => new AdvancedSettingsRow(entry.Title, entry.Value, showGroup ? entry.Group : null))
} .ToList();
ShowSettingsRows(rows, scrollToTop); ShowSettingsRows(rows, scrollToTop);
_settingsNoResults?.SetActive(entries.Count == 0); _settingsNoResults?.SetActive(entries.Count == 0);
} }
private static void ShowSettingsRows(IReadOnlyList<KeyValuePair<string, string>> rows, bool scrollToTop) private static void ShowSettingsRows(IReadOnlyList<KeyValuePair<string, string>> rows, bool scrollToTop)
{
var advancedRows = rows
.Select(row => new AdvancedSettingsRow(row.Key, row.Value, null))
.ToList();
ShowSettingsRows(advancedRows, scrollToTop);
}
private static void ShowSettingsRows(List<AdvancedSettingsRow> rows, bool scrollToTop)
{ {
var guide = MatchInfoGuide.Instance; var guide = MatchInfoGuide.Instance;
if (_settingsScroller == null || !guide || !guide.MatchInfoRolePanelPrefab) if (_settingsScroller == null || !guide || !guide.MatchInfoRolePanelPrefab)
@@ -2004,9 +2111,19 @@ public static class RoleGuidePatches
_savedGroupLabel = GroupSortingTmp.text; _savedGroupLabel = GroupSortingTmp.text;
_savedFilterIconScale = _searchSortingFilterIdle.transform.localScale; _savedFilterIconScale = _searchSortingFilterIdle.transform.localScale;
_savedGroupIconScale = _searchSortingGroupIdle.transform.localScale; _savedGroupIconScale = _searchSortingGroupIdle.transform.localScale;
_savedFilterLabelPosition = FilterSortingTmp.transform.localPosition;
_savedGroupLabelPosition = GroupSortingTmp.transform.localPosition;
SetPaginationSprites(_searchSortingFilterIdle, _searchSortingFilterHover, true); SetPaginationSprites(_searchSortingFilterIdle, _searchSortingFilterHover, true);
SetPaginationSprites(_searchSortingGroupIdle, _searchSortingGroupHover, false); SetPaginationSprites(_searchSortingGroupIdle, _searchSortingGroupHover, false);
FilterSortingTmp.transform.localPosition = new Vector3(
-0.15f,
_savedFilterLabelPosition.y,
_savedFilterLabelPosition.z);
GroupSortingTmp.transform.localPosition = new Vector3(
0.15f,
_savedGroupLabelPosition.y,
_savedGroupLabelPosition.z);
FilterSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.PrevPage", "Prev"); FilterSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.PrevPage", "Prev");
GroupSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.NextPage", "Next"); GroupSortingTmp.text = MiraLocaleManager.Get("MiraApi.Wiki.NextPage", "Next");
_sortButtonsRepurposed = true; _sortButtonsRepurposed = true;
@@ -2058,6 +2175,8 @@ public static class RoleGuidePatches
_searchSortingGroupHover.transform.localScale = _savedGroupIconScale; _searchSortingGroupHover.transform.localScale = _savedGroupIconScale;
FilterSortingTmp.text = _savedFilterLabel; FilterSortingTmp.text = _savedFilterLabel;
GroupSortingTmp.text = _savedGroupLabel; GroupSortingTmp.text = _savedGroupLabel;
FilterSortingTmp.transform.localPosition = _savedFilterLabelPosition;
GroupSortingTmp.transform.localPosition = _savedGroupLabelPosition;
SearchSortingOrderButton.gameObject.SetActive(true); SearchSortingOrderButton.gameObject.SetActive(true);
SearchSortingFilterButton.gameObject.SetActive(true); SearchSortingFilterButton.gameObject.SetActive(true);
SearchSortingGroupButton.gameObject.SetActive(true); SearchSortingGroupButton.gameObject.SetActive(true);