diff --git a/AmongUs.props b/AmongUs.props index 0819a88..b00f7ff 100644 --- a/AmongUs.props +++ b/AmongUs.props @@ -2,7 +2,7 @@ - + diff --git a/MiraAPI.Example/Modifiers/Freezer/FreezeModifier.cs b/MiraAPI.Example/Modifiers/Freezer/FreezeModifier.cs index 8cd0139..6a7f3a0 100644 --- a/MiraAPI.Example/Modifiers/Freezer/FreezeModifier.cs +++ b/MiraAPI.Example/Modifiers/Freezer/FreezeModifier.cs @@ -1,5 +1,4 @@ -using Il2CppSystem; -using MiraAPI.Example.Roles; +using MiraAPI.Example.Roles; using MiraAPI.Modifiers.Types; using MiraAPI.Translation; using UnityEngine; @@ -28,7 +27,7 @@ public class FreezeModifier : TimedModifier if (Player.AmOwner || PlayerControl.LocalPlayer.Data.Role is FreezerRole) { - Player.cosmetics.SetOutline(true, new Nullable(Palette.LightBlue)); + Player.cosmetics.SetOutline(true, new Il2CppSystem.Nullable(Palette.LightBlue)); } } diff --git a/MiraAPI/GameModes/GameModeOption.cs b/MiraAPI/GameModes/GameModeOption.cs index 34ec457..c58030a 100644 --- a/MiraAPI/GameModes/GameModeOption.cs +++ b/MiraAPI/GameModes/GameModeOption.cs @@ -39,7 +39,7 @@ public static class GameModeOption } } - internal static readonly StringNames GamemodeName = MiraLocaleManager.GetOrCreateLocaleString("Gamemode"); + internal static readonly StringNames GamemodeName = MiraLocaleManager.GetOrCreateLocaleString("MiraApi.Gamemode"); internal static readonly StringNames CustomName = MiraLocaleManager.GetOrCreateLocaleString("Custom"); internal static readonly Dictionary Values = new() { diff --git a/MiraAPI/GameOptions/ISummarizedOptions.cs b/MiraAPI/GameOptions/ISummarizedOptions.cs new file mode 100644 index 0000000..7873902 --- /dev/null +++ b/MiraAPI/GameOptions/ISummarizedOptions.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace MiraAPI.GameOptions; + +/// +/// Allows an option group to compress noisy option blocks in the wiki "Options" section. +/// Implementers can hide specific option keys and replace them with one or more summary lines. +/// +public interface ISummarizedOptions +{ + /// + /// Gets the option string keys (StringNames) to omit from the wiki options list (e.g., a large set of related options). + /// + IReadOnlySet WikiHiddenOptionKeys { get; } + + /// + /// Gets summary lines to insert when the first hidden option would have appeared. + /// Lines should already be formatted as "Title: Value". + /// + IEnumerable GetWikiOptionSummaryLines(); +} diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs index 32c736e..646de56 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedPlayerOption.cs @@ -1,6 +1,6 @@ using System; +using System.Collections.Generic; using System.Linq; -using Il2CppSystem.Collections.Generic; using MiraAPI.Networking; using MiraAPI.Translation; using MiraAPI.Utilities; @@ -43,7 +43,7 @@ public class ModdedPlayerOption : ModdedOption data.Title = StringName; data.Type = global::OptionTypes.Player; - Values = new List(); + Values = new(); AllowNone = allowNone; Value = DefaultValue; } @@ -60,17 +60,16 @@ public class ModdedPlayerOption : ModdedOption playerOption.name = $"{ParentMod!.OptionsTitleText}.PlayerOption.{TranslationController.Instance.GetString(StringName)}"; - var filteredList = GetFilteredPlayers(); - Values = filteredList.ToIl2CppList(); + Values = GetFilteredPlayers(); playerOption.SetUpFromData(Data, 20); playerOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged; playerOption.Title = StringName; playerOption.TitleText.text = Title.Translate(); - playerOption.Values = Values; + playerOption.Values = Values.ToIl2CppList(); playerOption.Value = Value; - playerOption.playerIndex = filteredList.FindIndex(p => p.PlayerId == Value); + playerOption.playerIndex = Values.FindIndex(p => p.PlayerId == Value); OptionBehaviour = playerOption; ModdedOptionsManager.CreatedPlayerOptions.TryAdd(playerOption, this); @@ -85,7 +84,7 @@ public class ModdedPlayerOption : ModdedOption /// Returns a player ran through the filter. /// /// A list of filtered players. - public System.Collections.Generic.List GetFilteredPlayers() + public List GetFilteredPlayers() { return [.. GameData.Instance.AllPlayers.ToArray().Where(x => PlayerFilter?.Invoke(x) ?? true)]; } diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index 0017fd9..0ca73e0 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -1,8 +1,16 @@ using System; +using System.Collections.Generic; +using Il2CppInterop.Runtime.Attributes; using MiraAPI.GameOptions; using MiraAPI.PluginLoading; +using MiraAPI.Roles; +using MiraAPI.Translation; +using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; +using TMPro; using UnityEngine; +using UnityEngine.UI; +using Object = UnityEngine.Object; namespace MiraAPI.Modifiers; @@ -44,16 +52,134 @@ public abstract class BaseModifier : IOptionable x => x.InternalModifiers.Exists(y => y.TypeId == TypeId)) ?? throw new InvalidOperationException("Modifier is not registered."); + /// + /// Gets the id part used to build the modifier's translation keys. It is recommended to simply call it what the modifier is, as other mods may utilize it. + /// + public virtual string IdPart => GetType().Name; + + /// + /// Gets the id part used to build the modifier's translation keys. It is recommended to call it ModGuid.Modifier.Type. + /// + public virtual string IdPrefix => GetType().Namespace!; + /// /// Gets the modifier name. /// - public abstract string ModifierName { get; } + public virtual string ModifierName => MiraLocaleManager.Get(ModifierNameLocale); + + /// + /// Gets the modifier's name id for localization. + /// + public virtual string ModifierNameLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart); + + /// + /// Gets the medium description of the modifier. Used in the modifier guide and options menu. + /// + public virtual string ModifierMedDescription => MiraLocaleManager.Get(ModifierMedDescriptionLocale, GetDescription()); + + /// + /// Gets the modifier's medium description id for localization. + /// + public virtual string ModifierMedDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "MedDescription"); + + /// + /// Gets the wiki description of the modifier. Used in the wiki and normally appends the options text as well. + /// + public virtual string ModifierWikiDescription => MiraLocaleManager.Get(ModifierWikiDescriptionLocale, GetDescription()) + + Helpers.GetOptionsText(GetType()); + + /// + /// Gets the modifier's wiki description id for localization. + /// + public virtual string ModifierWikiDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "WikiDescription"); + + /// + /// Gets the category of the modifier. Used in the wiki. + /// + public virtual string ModifierCategoryTitle => MiraLocaleManager.Get("Modifier"); + + /// + /// Gets whether the modifier is forcibly shown or disabled in the wiki screen. + /// + /// if the modifier is always displayed, otherwise if it is never displayable, or if it is dictated by amount and chance. + public virtual bool? ForceShowModifierOnWiki => false; + + /// + /// Gets the information to display in the wiki. + /// + /// The guide object. + /// The title text object. + /// The scroller parent. + /// The of the wiki. + public virtual GameObject GetAdvancedWiki(MatchInfoGuide guide, TextMeshPro titleText, Scroller parent) + { + parent.ScrollToTop(); + var obj = Helpers.CreateAdvancedWikiTab( + guide, + ModifierNameLocale, + ModifierName + $" ({ModifierCategoryTitle})", + ModifierWikiDescription, + titleText, + out var desc); + var num = 0; + if (WikiAbilities.Count != 0) + { + var grid = Object.Instantiate(parent.Inner, obj.transform); + var layoutGroup = grid.GetComponent(); + layoutGroup.startAxis = GridLayoutGroup.Axis.Vertical; + layoutGroup.constraint = GridLayoutGroup.Constraint.FixedColumnCount; + layoutGroup.spacing = new Vector2(0.75f, 0.4f); + grid.DestroyChildren(); + foreach (var ability in WikiAbilities) + { + num++; + var panel = Object.Instantiate( + guide.MatchInfoRolePanelPrefab, + grid); + panel.roleCount.text = ability.AbilityType; + panel.roleCount.transform.localPosition += new Vector3(-0.04f, 0); + panel.roleName.text = ability.Name; + panel.roleName.transform.localPosition += new Vector3(-0.04f, 0); + panel.roleDescription.text = ability.Description; + panel.roleDescription.rectTransform.sizeDelta = new Vector2(2.601f, 0.8f); + panel.roleDescription.transform.localPosition += new Vector3(0, -0.1f); + panel.roleIcon.sprite = ability.Icon.LoadAsset(); + panel.roleIcon.SetSizeLimit(0.13f); + + panel.roleIcon.material.SetInt(PlayerMaterial.MaskLayer, 50); + panel.roleName.fontMaterial.SetFloat(panel.STENCIL_NAME, 50f); + panel.roleDescription.fontMaterial.SetFloat(panel.STENCIL_NAME, 50f); + panel.roleCount.fontMaterial.SetFloat(panel.STENCIL_NAME, 50f); + panel.roleIcon.transform.localScale = new Vector3(3.5f, 3.5f, 1f); + } + + grid.localPosition = new Vector3(-3.9f, 1.1f - desc.textBounds.size.y, 0f); + grid.localScale = new Vector3(1.3f, 1.3f, 1); + } + + obj.transform.SetParent(parent.Inner.transform); + obj.transform.localPosition = new Vector3(0f, 0f, 0f); + parent.SetYBoundsMax(Mathf.Clamp(desc.textBounds.size.y - 2 + Mathf.Ceil(num / 2f) * 1.45f, 0f, 999f)); + return obj; + } + + /// + /// Gets the list of abilities or other similar information to display below the text of a modifier's wiki page. + /// + [HideFromIl2Cpp] + public virtual List WikiAbilities => []; /// /// Gets the modifier icon. Useless if is . /// public virtual LoadableAsset? ModifierIcon => null; + /// + /// Gets the for the Modifier Icon. + /// + [HideFromIl2Cpp] + public virtual TMP_SpriteAsset IconTmp => null!; + /// /// Gets a value indicating whether the modifier is hidden on the UI. Will be hidden either way if no description is provided. /// @@ -69,6 +195,11 @@ public abstract class BaseModifier : IOptionable /// public virtual Color FreeplayFileColor => Color.gray; + /// + /// Gets the of the modifier, used in the wiki and other UI outside freeplay. + /// + public virtual Color GeneralColor => FreeplayFileColor; + /// /// Gets a value indicating whether the modifier is unique. If , the player can only have one instance of this modifier. /// diff --git a/MiraAPI/Modifiers/Types/GameModifier.cs b/MiraAPI/Modifiers/Types/GameModifier.cs index f574fd3..b432920 100644 --- a/MiraAPI/Modifiers/Types/GameModifier.cs +++ b/MiraAPI/Modifiers/Types/GameModifier.cs @@ -9,6 +9,9 @@ namespace MiraAPI.Modifiers.Types; [MiraIgnore] public abstract class GameModifier : BaseModifier { + /// + public override bool? ForceShowModifierOnWiki => null; + /// public override bool ShowInFreeplay => true; diff --git a/MiraAPI/Patches/Events/VanillaButtonPatches.cs b/MiraAPI/Patches/Events/VanillaButtonPatches.cs index 6b12a22..70a218b 100644 --- a/MiraAPI/Patches/Events/VanillaButtonPatches.cs +++ b/MiraAPI/Patches/Events/VanillaButtonPatches.cs @@ -23,10 +23,18 @@ public static class VanillaButtonPatches { playerTarget = role.Cast().currentTarget; } + else if (role.Role is RoleTypes.Detective) + { + playerTarget = role.Cast().currentTarget; + } else if (role.Role is RoleTypes.GuardianAngel) { playerTarget = role.Cast().currentTarget; } + else if (role.Role is RoleTypes.SpiritGuide) + { + playerTarget = role.Cast().currentTarget; + } else if (role.Role is RoleTypes.Engineer) { ventTarget = role.Cast().currentTarget; @@ -53,6 +61,10 @@ public static class VanillaButtonPatches { playerTarget = role.Cast().currentTarget; } + else if (role.Role is RoleTypes.SpiritGuide) + { + playerTarget = role.Cast().currentTarget; + } var genericEvent = new VanillaButtonClickEvent(__instance, playerTarget); MiraEventManager.InvokeEvent(genericEvent); diff --git a/MiraAPI/Patches/GameModes/HudPatches.cs b/MiraAPI/Patches/GameModes/HudPatches.cs index d237967..3c2d815 100644 --- a/MiraAPI/Patches/GameModes/HudPatches.cs +++ b/MiraAPI/Patches/GameModes/HudPatches.cs @@ -1,6 +1,5 @@ using System.Collections; using HarmonyLib; -using Il2CppSystem; using MiraAPI.GameModes; using MiraAPI.Translation; using Reactor.Utilities; @@ -16,7 +15,7 @@ internal static class HudPatches [HarmonyPostfix, HarmonyPatch(nameof(HudManager.Update))] public static void HudUpdatePatch(HudManager __instance) { - if (GameManager.Instance != null && GameManager.Instance.GameHasStarted && + if (GameManager.Instance && GameManager.Instance.GameHasStarted && CustomGameModeManager.ActiveMode != null) { CustomGameModeManager.ActiveMode.HudUpdate(__instance); @@ -32,7 +31,7 @@ internal static class HudPatches private static IEnumerator CoPostHudStart(HudManager instance) { - yield return new WaitUntil((Func)(() => GameManager.Instance != null)); + yield return new WaitUntil((Il2CppSystem.Func)(() => GameManager.Instance != null)); if (GameManager.Instance.IsHideAndSeek()) yield return null; var infoPane = instance.gameObject.transform.FindChild("LobbyInfoPane"); @@ -42,7 +41,7 @@ internal static class HudPatches var modelText = modeLabel!.Find("Text_TMP").gameObject; var modelTextClone = Object.Instantiate(modelText, modeLabel); Object.Destroy(modelTextClone.GetComponent()); - modelTextClone.GetComponent().text = "Gamemode"; + modelTextClone.GetComponent().text = MiraLocaleManager.Get("MiraApi.Gamemode", "Gamemode"); var gmText = modeValue!.Find("GameModeText").gameObject; var gmTextClone = Object.Instantiate(gmText, modeValue); gmText.SetActive(false); diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index 38ba561..7f0920d 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -1,10 +1,10 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Linq; using BepInEx.Unity.IL2CPP.Utils.Collections; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; -using Il2CppSystem.Collections.Generic; using MiraAPI.GameModes; using MiraAPI.GameOptions; using MiraAPI.Modifiers; @@ -28,9 +28,9 @@ internal static class GameOptionsMenuPatch private static readonly List VanillaHeaders = new(); private static readonly List VanillaOptions = new(); private static readonly List MainOptions = new(); - private static readonly System.Collections.Generic.Dictionary> GameModeGroups = []; - private static readonly System.Collections.Generic.Dictionary> GameModeOptions = []; - private static readonly System.Collections.Generic.Dictionary> GameModeHeaders = []; + private static readonly Dictionary> GameModeGroups = []; + private static readonly Dictionary> GameModeOptions = []; + private static readonly Dictionary> GameModeHeaders = []; private static TextMeshPro _gamemodeDescription = null!; private static SpriteRenderer _modIcon = null!; @@ -38,7 +38,7 @@ internal static class GameOptionsMenuPatch [HarmonyPatch(nameof(GameOptionsMenu.Initialize))] public static bool InitPatch(GameOptionsMenu __instance) { - __instance.Children ??= new List(); + __instance.Children ??= new(); __instance.Children.Clear(); if (MenuState.Instance.CurrentModIdx == 0) @@ -324,8 +324,8 @@ internal static class GameOptionsMenuPatch .ToList(); GameModeGroups.Add(mode, [.. filteredGroups]); - var optionBehaviours = new System.Collections.Generic.List(); - var categoryHeaders = new System.Collections.Generic.List(); + var optionBehaviours = new List(); + var categoryHeaders = new List(); var newNum = mode.ShowNormalGameSettings ? num : num - additionalVanillaScrollNum; foreach (var group in filteredGroups) { @@ -406,7 +406,7 @@ internal static class GameOptionsMenuPatch __instance.scrollBar.SetYBoundsMax(-num - 1.65f); } - private static void CreateGroup(GameOptionsMenu menu, AbstractOptionGroup group, Transform container, ref float num, ref System.Collections.Generic.List optionBehaviours, ref System.Collections.Generic.List categoryHeaders) + private static void CreateGroup(GameOptionsMenu menu, AbstractOptionGroup group, Transform container, ref float num, ref List optionBehaviours, ref List categoryHeaders) { var categoryHeaderMasked = Object.Instantiate( menu.categoryHeaderOrigin, diff --git a/MiraAPI/Patches/Options/OptionsPatches.cs b/MiraAPI/Patches/Options/OptionsPatches.cs index 64d9119..8ed0f7d 100644 --- a/MiraAPI/Patches/Options/OptionsPatches.cs +++ b/MiraAPI/Patches/Options/OptionsPatches.cs @@ -1,5 +1,4 @@ using HarmonyLib; -using Il2CppSystem; using MiraAPI.GameOptions; using MiraAPI.Roles; using MiraAPI.Translation; @@ -165,7 +164,7 @@ public static class OptionsPatches return true; } - __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty()).Translate(); + __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title).Translate(); return false; } @@ -314,8 +313,8 @@ public static class OptionsPatches return true; } - __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty()).Translate(); - __instance.ValueText.text = TranslationController.Instance.GetString(__instance.Values[__instance.Value], Array.Empty()); + __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title).Translate(); + __instance.ValueText.text = TranslationController.Instance.GetString(__instance.Values[__instance.Value]); return false; } diff --git a/MiraAPI/Patches/Options/PlayerOptionPatches.cs b/MiraAPI/Patches/Options/PlayerOptionPatches.cs index 9cf0134..ab19fce 100644 --- a/MiraAPI/Patches/Options/PlayerOptionPatches.cs +++ b/MiraAPI/Patches/Options/PlayerOptionPatches.cs @@ -26,8 +26,8 @@ public static class PlayerOptionPatches { filteredPlayers.Sort((a, b) => a.PlayerId.CompareTo(b.PlayerId)); + moddedPlrOpt.Values = filteredPlayers; __instance.Values = filteredPlayers.ToIl2CppList(); - moddedPlrOpt.Values = __instance.Values; __instance.playerIndex = filteredPlayers.FindIndex(p => p.PlayerId == __instance.Value); __instance.SetValueText(); } diff --git a/MiraAPI/Patches/RoleGuidePatches.cs b/MiraAPI/Patches/RoleGuidePatches.cs index 6b17ea3..1a18950 100644 --- a/MiraAPI/Patches/RoleGuidePatches.cs +++ b/MiraAPI/Patches/RoleGuidePatches.cs @@ -5,8 +5,12 @@ using System.Linq; using AmongUs.GameOptions; using HarmonyLib; using Innersloth.Assets; +using MiraAPI.Modifiers; +using MiraAPI.Modifiers.Types; using MiraAPI.Roles; +using MiraAPI.Translation; using MiraAPI.Utilities; +using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; @@ -19,6 +23,24 @@ namespace MiraAPI.Patches; [HarmonyPatch] public static class RoleGuidePatches { + public static PassiveButton SearchIconButton; + private static SpriteRenderer _searchIconIdle; + private static SpriteRenderer _searchIconHover; + public static PassiveButton SearchSortingOrderButton; + private static SpriteRenderer _searchSortingOrderIdle; + private static SpriteRenderer _searchSortingOrderHover; + public static PassiveButton SearchSortingGroupButton; + private static SpriteRenderer _searchSortingGroupIdle; + private static SpriteRenderer _searchSortingGroupHover; + public static PassiveButton SearchSortingFilterButton; + private static SpriteRenderer _searchSortingFilterIdle; + private static SpriteRenderer _searchSortingFilterHover; + private static Scroller _rolesScroller; + private static bool _needsRoleRefresh; + private static bool _needsModifierRefresh; + private static bool _needsToggledRoleRefresh; + private static bool _needsToggledModifierRefresh; + [HarmonyPostfix] [HarmonyPriority(Priority.First)] [HarmonyPatch(typeof(MatchInfoGuide), nameof(MatchInfoGuide.CreateNormalModeSettings))] @@ -34,17 +56,23 @@ public static class RoleGuidePatches __instance.matchInfoSettingsMaskArea.size = __instance.MatchInfoRoleMaskArea.size = new Vector2(-6, 1.8f); - __instance.matchInfoPlayersMaskArea.transform.parent.GetAllChildren().First(x => x.name.Contains("BG_Gradient")) + var playerMenuGradient = __instance.matchInfoPlayersMaskArea.transform.parent.GetAllChildren() + .First(x => x.name.Contains("BG_Gradient")); + var settingsenuGradient = __instance.matchInfoSettingsMaskArea.transform.parent.GetAllChildren() + .First(x => x.name.Contains("BG_Gradient")); + var roleMenuGradient = __instance.MatchInfoRoleMaskArea.transform.parent.GetAllChildren() + .First(x => x.name.Contains("BG_Gradient")); + playerMenuGradient .GetComponent() .maskInteraction = - __instance.matchInfoSettingsMaskArea.transform.parent.GetAllChildren() - .First(x => x.name.Contains("BG_Gradient")).GetComponent() + settingsenuGradient.GetComponent() .maskInteraction = - __instance.MatchInfoRoleMaskArea.transform.parent.GetAllChildren() - .First(x => x.name.Contains("BG_Gradient")).GetComponent() + roleMenuGradient.GetComponent() .maskInteraction = SpriteMaskInteraction.VisibleInsideMask; + roleMenuGradient.transform.localScale = new Vector3(0.5297f, 0.1565f, 1); + roleMenuGradient.transform.localPosition = new Vector3(0, -0.62f, -5); var wikiTab = Object.Instantiate(__instance.settingsTabs[2], __instance.settingsTabs[2].transform.parent); - advancedWikiTab = wikiTab.GetComponent(); + _advancedInfoTabScroller = wikiTab.GetComponent(); } [HarmonyPrefix] @@ -98,12 +126,55 @@ public static class RoleGuidePatches titleText.text = TranslationController.Instance.GetString(StringNames.MatchInfoGuideTitle); } - if (GameManager.Instance.TryCast() != null) + var regGame = GameManager.Instance.TryCast() != null; + if (regGame) { if (__instance.NormalModeSettings.Count == 0) { - __instance.numOfTabs = 3; - __instance.TabButtons[0].SelectButton(true); + var sortingOrderButton = Object.Instantiate( + HudManager.Instance.SettingsButton, + __instance.TabButtons[2].transform.parent); + SearchSortingOrderButton = sortingOrderButton.GetComponent(); + FixSearchButton( + SearchSortingOrderButton, + "SearchSortingOrderButton", + SwitchMethod, + MiraAssets.SortingAzIdleSprite, + MiraAssets.SortingAzHoverSprite, + new Vector3(2.4f, 0.765f, -0.1f), + out _searchSortingOrderIdle, + out _searchSortingOrderHover); + + var sortingGroupButton = Object.Instantiate( + sortingOrderButton, + sortingOrderButton.transform.parent); + SearchSortingGroupButton = sortingGroupButton.GetComponent(); + FixSearchButton( + SearchSortingGroupButton, + "SearchSortingGroupButton", + SwitchGrouping, + MiraAssets.GroupNoneIdleSprite, + MiraAssets.GroupNoneHoverSprite, + new Vector3(1.8f, 0.765f, -0.1f), + out _searchSortingGroupIdle, + out _searchSortingGroupHover); + + var sortingFilterButton = Object.Instantiate( + sortingOrderButton, + sortingOrderButton.transform.parent); + SearchSortingFilterButton = sortingFilterButton.GetComponent(); + FixSearchButton( + SearchSortingFilterButton, + "SearchSortingFilterButton", + SwitchFilter, + MiraAssets.VisibleOnIdleSprite, + MiraAssets.VisibleOnHoverSprite, + new Vector3(1f, 0.765f, -0.1f), + out _searchSortingFilterIdle, + out _searchSortingFilterHover); + + __instance.numOfTabs = 4; + __instance.TabButtons[2].SelectButton(true); __instance.MatchInfoRoleMaskArea.transform.localPosition = new Vector3(-0.0184f, 0.15f, -0.1f); __instance.matchInfoPlayersMaskArea.transform.localPosition = @@ -113,25 +184,166 @@ public static class RoleGuidePatches __instance.matchInfoSettingsMaskArea.size = __instance.MatchInfoRoleMaskArea.size = new Vector2(-6, 1.8f); - __instance.matchInfoPlayersMaskArea.transform.parent.GetAllChildren().First(x => x.name.Contains("BG_Gradient")) + var playerMenuGradient = __instance.matchInfoPlayersMaskArea.transform.parent.GetAllChildren() + .First(x => x.name.Contains("BG_Gradient")); + var settingsenuGradient = __instance.matchInfoSettingsMaskArea.transform.parent.GetAllChildren() + .First(x => x.name.Contains("BG_Gradient")); + var roleMenuGradient = __instance.MatchInfoRoleMaskArea.transform.parent.GetAllChildren() + .First(x => x.name.Contains("BG_Gradient")); + playerMenuGradient .GetComponent() .maskInteraction = - __instance.matchInfoSettingsMaskArea.transform.parent.GetAllChildren() - .First(x => x.name.Contains("BG_Gradient")).GetComponent() + settingsenuGradient.GetComponent() .maskInteraction = - __instance.MatchInfoRoleMaskArea.transform.parent.GetAllChildren() - .First(x => x.name.Contains("BG_Gradient")).GetComponent() + roleMenuGradient.GetComponent() .maskInteraction = SpriteMaskInteraction.VisibleInsideMask; + roleMenuGradient.transform.localScale = new Vector3(0.5297f, 0.1565f, 1); + roleMenuGradient.transform.localPosition = new Vector3(0, -0.62f, -5); __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); + + var modifiersTab = Object.Instantiate( + __instance.settingsTabs[2], + __instance.settingsTabs[2].transform.parent); + modifiersTab.name = "ModifiersPanel"; + modifiersTab.transform.FindChild("MaskArea")?.transform.localPosition = + new Vector3(-0.0184f, 0.15f, -0.1f); + modifiersTab.transform.GetAllChildren() + .First(x => x.name.Contains("BG_Gradient")).transform.localPosition = new Vector3(0, -0.62f, -5); + _modifiersScroller = modifiersTab.GetComponent(); + __instance.settingsTabs.Add(modifiersTab); + + var wikiTab = Object.Instantiate( + __instance.settingsTabs[2], + __instance.settingsTabs[2].transform.parent); + wikiTab.name = "AdvancedWikiPanels"; wikiTab.transform.FindChild("MaskArea")?.transform.localPosition = new Vector3(-0.0184f, 0.15f, -0.1f); - advancedWikiTab = wikiTab.GetComponent(); + wikiTab.transform.GetAllChildren() + .First(x => x.name.Contains("BG_Gradient")).transform.localPosition = new Vector3(0, -0.62f, -5); + _advancedInfoTabScroller = wikiTab.GetComponent(); + DisplayNormalRoleSettings(__instance, true); + + var searchIconButton = Object.Instantiate( + sortingOrderButton, + sortingOrderButton.transform.parent); + SearchIconButton = searchIconButton.GetComponent(); + FixSearchButton( + SearchIconButton, + "SearchIconButton", + ClearSearchText, + MiraAssets.SearchIconIdleSprite, + MiraAssets.SearchIconHoverSprite, + new Vector3(-2.79f, 0.765f, -0.1f), + out _searchIconIdle, + out _searchIconHover); + + var searchBox = Object.Instantiate( + __instance.TabButtons[2].gameObject, + __instance.TabButtons[2].transform.parent); + var searchButton = searchBox.GetComponent(); + var tmpText = searchButton.transform.GetChild(0).GetComponent(); + tmpText.GetComponent().Destroy(); + tmpText.color = new Color(0.75f, 0.75f, 0.75f); + tmpText.text = string.Empty; + tmpText.fontSizeMax = 4; + tmpText.overflowMode = TextOverflowModes.Ellipsis; + tmpText.alignment = TextAlignmentOptions.Left; + tmpText.horizontalAlignment = HorizontalAlignmentOptions.Left; + tmpText.rectTransform.offsetMax = new Vector2(-0.1531f, 0.2972f); + tmpText.rectTransform.sizeDelta = new Vector2(3, 1); + tmpText.transform.localPosition = new Vector3(0, 0.0343f, -0.2f); + var inactive = searchButton.inactiveSprites; + var selected = searchButton.selectedSprites; + var highlight = searchButton.activeSprites; + var disabledSprite = searchButton.disabledSprites; + disabledSprite.GetComponent().size = new Vector2(3, 0.6f); + searchButton.Destroy(); + inactive.gameObject.SetActive(false); + selected.gameObject.SetActive(false); + highlight.gameObject.SetActive(false); + disabledSprite.gameObject.SetActive(true); + var button = searchBox.AddComponent(); + button.OnUp = true; + searchBoxTmp = searchBox.AddComponent(); + searchBoxTmp.outputText = tmpText; + button.OnClick.AddListener((UnityAction)(() => { searchBoxTmp.GiveFocus(); })); + button.OnMouseOver = new UnityEvent(); + button.OnMouseOut = new UnityEvent(); + _rolesScroller = __instance.settingsTabs[2].GetComponent(); + searchBoxTmp.OnChange = new Button.ButtonClickedEvent(); + searchBoxTmp.OnChange.AddListener( + (UnityAction)(() => + { + var text = searchBoxTmp.outputText.text; + if (text != string.Empty) + { + _searchIconIdle.sprite = MiraAssets.SearchClearIdleSprite; + _searchIconHover.sprite = MiraAssets.SearchClearHoverSprite; + } + else + { + _searchIconIdle.sprite = MiraAssets.SearchIconIdleSprite; + _searchIconHover.sprite = MiraAssets.SearchIconHoverSprite; + } + + if (MatchInfoGuide.Instance.activeTabIndex != 3) + { + SortAllRoles(text); + _rolesScroller.ScrollToTop(); + } + else + { + SortAllModifiers(text); + _modifiersScroller.ScrollToTop(); + } + })); + searchBoxTmp.transform.localPosition = new Vector3(-1.438f, 0.756f, -0.2f); + + var playerButton = __instance.TabButtons[0]; + FixTabButton( + playerButton, + MiraAssets.WikiPlayersButtonIdleSprite, + MiraAssets.WikiPlayersButtonOpenSprite, + MiraAssets.WikiPlayersButtonHoverSprite); + + var settingButton = __instance.TabButtons[1]; + FixTabButton( + settingButton, + MiraAssets.WikiSettingsButtonIdleSprite, + MiraAssets.WikiSettingsButtonOpenSprite, + MiraAssets.WikiSettingsButtonHoverSprite); + + var rolesButton = __instance.TabButtons[2]; + FixTabButton( + rolesButton, + MiraAssets.WikiRolesButtonIdleSprite, + MiraAssets.WikiRolesButtonOpenSprite, + MiraAssets.WikiRolesButtonHoverSprite); + rolesButton.OnClick = new Button.ButtonClickedEvent(); + rolesButton.OnClick.AddListener((Action)(() => OpenRolesTab())); + + var modifiersButton = Object.Instantiate( + __instance.TabButtons[2], + __instance.TabButtons[2].transform.parent); + FixTabButton( + modifiersButton, + MiraAssets.WikiRolesButtonIdleSprite, + MiraAssets.WikiRolesButtonOpenSprite, + MiraAssets.WikiRolesButtonHoverSprite); + modifiersButton.OnClick = new Button.ButtonClickedEvent(); + modifiersButton.OnClick.AddListener((Action)(() => OpenModifiersTab())); + __instance.TabButtons.Add(modifiersButton); + + playerButton.transform.localPosition = new Vector3(-3.6f, 0.656f, -0.2f); + settingButton.transform.localPosition = new Vector3(-3.6f, 0.056f, -0.2f); + rolesButton.transform.localPosition = new Vector3(-3.6f, -0.544f, -0.2f); + modifiersButton.transform.localPosition = new Vector3(-3.6f, -1.144f, -0.2f); } else { DisplayNormalRoleSettings(__instance, false); + ClearSearchText(); } } else if (__instance.HnSModeSettings.Count == 0) @@ -150,20 +362,291 @@ public static class RoleGuidePatches var instance2 = ControllerManager.Instance; var controllerSelectable2 = __instance.ControllerSelectable; instance2.SetCurrentSelected(controllerSelectable2[^1]); - __instance.SetActiveTab(0); + __instance.SetActiveTab(regGame ? 2 : 0); return false; } - private static readonly Dictionary RolePanels = []; - private static Scroller advancedWikiTab; + public static void FixTabButton(MatchInfoGuideTabButton button, Sprite inactive, Sprite selected, Sprite hover) + { + var buttonCollider = button.GetComponent(); + button.transform.GetChild(0).gameObject.SetActive(false); + var settingBtnInactive = button.inactiveSprites.GetComponent(); + settingBtnInactive.sprite = inactive; + settingBtnInactive.transform.GetChild(0).gameObject.SetActive(false); + var settingBtnSelected = button.selectedSprites.GetComponent(); + settingBtnSelected.sprite = selected; + settingBtnSelected.transform.GetChild(0).gameObject.SetActive(false); + var settingBtnHighlight = button.activeSprites.GetComponent(); + settingBtnHighlight.sprite = hover; + settingBtnInactive.size = settingBtnSelected.size = settingBtnHighlight.size = Vector2.one; + settingBtnHighlight.transform.GetChild(0).gameObject.SetActive(false); + buttonCollider.size = new Vector2(0.8f, 0.74f); + buttonCollider.offset = Vector2.zero; + button.transform.localScale = new Vector3(0.7f, 0.7f, 1); + } + + public static void FixSearchButton(PassiveButton button, string objName, Action onClick, Sprite inactive, Sprite hover, Vector3 pos, out SpriteRenderer inactiveSprite, out SpriteRenderer hoverSprite) + { + button.name = objName; + inactiveSprite = button.inactiveSprites.GetComponent(); + inactiveSprite.sprite = inactive; + hoverSprite = button.activeSprites.GetComponent(); + hoverSprite.sprite = hover; + button.OnClick = new Button.ButtonClickedEvent(); + button.OnClick.AddListener(onClick); + + button.transform.localPosition = pos; + button.transform.localScale = Vector3.one; + button.transform.GetChild(2).gameObject.SetActive(false); + } + + public static void OpenModifiersTab() + { + _advancedInfoTabScroller?.gameObject.SetActive(false); + MatchInfoGuide.Instance.SetActiveTab(3); + if (_needsToggledModifierRefresh) + { + ToggleModifierVisibility(); + } + + if (_needsModifierRefresh) + { + var text = string.Empty; + try + { + text = searchBoxTmp.outputText.text; + } + catch + { + // ignored + } + + SortAllModifiers(text); + } + + _needsModifierRefresh = false; + _needsToggledModifierRefresh = false; + } + + public static void OpenRolesTab() + { + _advancedInfoTabScroller?.gameObject.SetActive(false); + MatchInfoGuide.Instance.SetActiveTab(2); + if (_needsToggledRoleRefresh) + { + ToggleRoleVisibility(); + } + + if (_needsRoleRefresh) + { + var text = string.Empty; + try + { + text = searchBoxTmp.outputText.text; + } + catch + { + // ignored + } + + SortAllRoles(text); + } + + _needsRoleRefresh = false; + _needsToggledRoleRefresh = false; + } + + private static readonly Dictionary RolePanels = []; + private static readonly Dictionary ModifierPanels = []; + private static Scroller _modifiersScroller; + private static Scroller _advancedInfoTabScroller; + private static TextBoxTMP searchBoxTmp; private static GameObject currentAdvancedTabObject; private static TextMeshPro titleText; + public sealed class DetailedPanel(MatchInfoRolePanel panel, string title, string category, string modId) + { + public MatchInfoRolePanel Panel = panel; + public string Title = title; + public string Category = category; + public string ModId = modId; + public int Amount { get; set; } + public int Chance { get; set; } + public float LikelyhoodOfRole { get; set; } + } + + private static SortingFilter sortFilter = SortingFilter.EnabledOnly; + private static SortingMethod sortMethod = SortingMethod.Alphabetical; + private static SortingGroups sortOrder = SortingGroups.Ungrouped; + + public static void ClearSearchText() + { + _searchIconIdle.sprite = MiraAssets.SearchIconIdleSprite; + _searchIconHover.sprite = MiraAssets.SearchIconHoverSprite; + try + { + searchBoxTmp.outputText.SetText(string.Empty); + searchBoxTmp.SetText(string.Empty); + } + catch + { + // ignored + } + + var text = string.Empty; + + if (MatchInfoGuide.Instance.activeTabIndex != 3) + { + SortAllRoles(text); + _rolesScroller.ScrollToTop(); + _needsModifierRefresh = true; + } + else + { + SortAllModifiers(text); + _modifiersScroller.ScrollToTop(); + _needsRoleRefresh = true; + } + } + + public static void SwitchFilter() + { + var stepUp = (SortingFilter)((int)sortFilter + 1); + if (Enum.IsDefined(stepUp)) + { + sortFilter = stepUp; + } + else + { + sortFilter = SortingFilter.EnabledOnly; + } + + switch (sortFilter) + { + case SortingFilter.AllRoles: + _searchSortingFilterIdle.sprite = MiraAssets.VisibleAllIdleSprite; + _searchSortingFilterHover.sprite = MiraAssets.VisibleAllHoverSprite; + break; + case SortingFilter.DisabledOnly: + _searchSortingFilterIdle.sprite = MiraAssets.VisibleOffIdleSprite; + _searchSortingFilterHover.sprite = MiraAssets.VisibleOffHoverSprite; + break; + default: + _searchSortingFilterIdle.sprite = MiraAssets.VisibleOnIdleSprite; + _searchSortingFilterHover.sprite = MiraAssets.VisibleOnHoverSprite; + break; + } + + if (MatchInfoGuide.Instance.activeTabIndex != 3) + { + ToggleRoleVisibility(); + _needsToggledModifierRefresh = _needsModifierRefresh = true; + } + else + { + ToggleModifierVisibility(); + _needsToggledRoleRefresh = _needsRoleRefresh = true; + } + } + + public static void SwitchMethod() + { + var stepUp = (SortingMethod)((int)sortMethod + 1); + if (Enum.IsDefined(stepUp)) + { + sortMethod = stepUp; + } + else + { + sortMethod = SortingMethod.Alphabetical; + } + + _searchSortingOrderIdle.sprite = sortMethod is SortingMethod.Alphabetical + ? MiraAssets.SortingAzIdleSprite + : MiraAssets.SortingZaIdleSprite; + _searchSortingOrderHover.sprite = sortMethod is SortingMethod.Alphabetical + ? MiraAssets.SortingAzHoverSprite + : MiraAssets.SortingZaHoverSprite; + + var text = string.Empty; + try + { + text = searchBoxTmp.outputText.text; + } + catch + { + // ignored + } + + if (MatchInfoGuide.Instance.activeTabIndex != 3) + { + SortAllRoles(text); + _needsModifierRefresh = true; + } + else + { + SortAllModifiers(text); + _needsRoleRefresh = true; + } + } + + public static void SwitchGrouping() + { + var stepUp = (SortingGroups)((int)sortOrder + 1); + if (Enum.IsDefined(stepUp)) + { + sortOrder = stepUp; + } + else + { + sortOrder = SortingGroups.Ungrouped; + } + + switch (sortOrder) + { + case SortingGroups.Faction: + _searchSortingGroupIdle.sprite = MiraAssets.GroupFactionIdleSprite; + _searchSortingGroupHover.sprite = MiraAssets.GroupFactionHoverSprite; + break; + case SortingGroups.AmountChance: + _searchSortingGroupIdle.sprite = MiraAssets.GroupCountIdleSprite; + _searchSortingGroupHover.sprite = MiraAssets.GroupCountHoverSprite; + break; + default: + _searchSortingGroupIdle.sprite = MiraAssets.GroupNoneIdleSprite; + _searchSortingGroupHover.sprite = MiraAssets.GroupNoneHoverSprite; + break; + } + + var text = string.Empty; + try + { + text = searchBoxTmp.outputText.text; + } + catch + { + // ignored + } + + if (MatchInfoGuide.Instance.activeTabIndex != 3) + { + SortAllRoles(text); + _needsModifierRefresh = true; + } + else + { + SortAllModifiers(text); + _needsRoleRefresh = true; + } + } + public static void DisplayNormalRoleSettings(MatchInfoGuide instance, bool reset) { + var inner = instance.settingsTabs[2].GetComponent().Inner; if (reset) { RolePanels.Clear(); + ModifierPanels.Clear(); instance.CreateSettingsEntry( StringNames.GameNumImpostors, GameManager.Instance.AllGameSettingData[StringNames.GameNumImpostors] @@ -190,7 +673,41 @@ public static class RoleGuidePatches GameManager.Instance.LogicOptions.GetTaskBarMode().ToString()); var hoverColor = new Color32(255, 255, 255, 150); - foreach (var roleBehaviour in RoleManager.Instance.AllRoles.ToArray().OrderBy(x => x.GetRoleName())) + foreach (var modifier in ModifierManager.Modifiers.OfType()) + { + var panel = Object.Instantiate( + instance.MatchInfoRolePanelPrefab, + _modifiersScroller.Inner); + var amount = + modifier.GetAmountPerGame(); + var chance = + modifier.GetAssignmentChance(); + panel.SetModifierPanel( + modifier, + amount, + chance); + 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.ClickSound = HudManager.Instance.MapButton.ClickSound; + 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((Action)(() => { DisplayAdvancedWiki(instance, modifier); })); + ModifierPanels.Add( + modifier, + new DetailedPanel( + panel, + modifier.ModifierName, + modifier.ModifierCategoryTitle, + modifier.ParentMod.MiraPlugin.GetAbbreviatedModName())); + } + + foreach (var roleBehaviour in CustomRoleManager.AllStoredRoleBehaviours) { if (roleBehaviour.Role is not RoleTypes.Crewmate and not RoleTypes.Impostor and not RoleTypes.CrewmateGhost and @@ -198,67 +715,289 @@ public static class RoleGuidePatches { var panel = Object.Instantiate( instance.MatchInfoRolePanelPrefab, - instance.settingsTabs[2].GetComponent().Inner); + inner); + string abbreviation; + int amount; + int chance; + if (roleBehaviour is ICustomRole custom) + { + amount = custom.GetCount().GetValueOrDefault(0); + chance = custom.GetChance().GetValueOrDefault(0); + abbreviation = custom.ParentMod.MiraPlugin.GetAbbreviatedModName(); + } + else + { + abbreviation = "AU"; + amount = + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(roleBehaviour.Role); + chance = + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(roleBehaviour.Role); + } + + panel.SetRolePanel( + roleBehaviour, + amount, + chance); 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.ClickSound = HudManager.Instance.MapButton.ClickSound; passiveButton.OnMouseOver = new UnityEvent(); - passiveButton.OnMouseOver.AddListener( - (UnityAction)(() => - { - panel.roleIcon.color = hoverColor; - })); + passiveButton.OnMouseOver.AddListener((UnityAction)(() => { panel.roleIcon.color = hoverColor; })); passiveButton.OnMouseOut = new UnityEvent(); - passiveButton.OnMouseOut.AddListener( - (UnityAction)(() => - { - panel.roleIcon.color = Color.white; - })); + passiveButton.OnMouseOut.AddListener((UnityAction)(() => { panel.roleIcon.color = Color.white; })); passiveButton.OnClick = new Button.ButtonClickedEvent(); passiveButton.OnClick.AddListener( (Action)(() => { DisplayAdvancedWiki(instance, roleBehaviour); })); - RolePanels.Add(roleBehaviour, panel); + RolePanels.Add( + roleBehaviour, + new DetailedPanel( + panel, + roleBehaviour.GetRoleName(), + roleBehaviour.GetCategoryTitle(), + abbreviation)); } } } - advancedWikiTab?.gameObject.SetActive(false); + _advancedInfoTabScroller?.gameObject.SetActive(false); - var num = 0; - foreach (var (roleData, panel) in RolePanels) + if (MatchInfoGuide.Instance.activeTabIndex != 3) { - var amount = GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(roleData.Role); - var chance = GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(roleData.Role); - var forciblyShow = roleData is ICustomRole custom ? custom.ForceShowRoleOnWiki : null; - if (amount == 0 || chance == 0 || (Enum.IsDefined(roleData.Role) && roleData.IsRoleBlacklisted()) || - (roleData is ICustomRole custom2 && ((!custom2.CanSpawnOnCurrentMode() && forciblyShow == null) || - (forciblyShow.HasValue && !forciblyShow.Value)))) + ToggleRoleVisibility(); + } + else + { + ToggleModifierVisibility(); + } + + if (reset) + { + instance.CreatePlayerEntries(); + } + } + + + public static void ToggleModifierVisibility() + { + var num = 0; + foreach (var (modifier, holder) in ModifierPanels) + { + var panel = holder.Panel; + var amount = + modifier.GetAmountPerGame(); + var chance = + modifier.GetAssignmentChance(); + var forciblyShow = modifier.ForceShowModifierOnWiki switch + { + true => 2, + false => 1, + _ => 0, + }; + panel.SetModifierPanel( + modifier, + amount, + chance); + holder.Amount = amount; + holder.Chance = chance; + holder.LikelyhoodOfRole = amount * chance; + var isRoleDisabled = amount == 0 || chance == 0; + var isRoleNotVisible = !modifier.CanSpawnOnCurrentMode() || modifier.GetDescription() == string.Empty; + if ((forciblyShow == 0 && ((isRoleDisabled && sortFilter is SortingFilter.EnabledOnly) || + (!isRoleDisabled && sortFilter is SortingFilter.DisabledOnly) || + isRoleNotVisible)) + || forciblyShow == 1) { panel.gameObject.SetActive(false); continue; } panel.gameObject.SetActive(true); - panel.SetPanel( - roleData, - amount, - chance); num++; } - if (num == 0) + var text = string.Empty; + try { - instance.rolesEnabledMessage.SetActive(true); + text = searchBoxTmp.outputText.text; + } + catch + { + // ignored } - instance.MatchInfoRoleScroller.SetYBoundsMax(Mathf.Clamp(Mathf.Ceil(num / 2f) * 1.3f - 1.5f, 0f, 999f)); - instance.MatchInfoRoleScroller.SetYBoundsMax(Mathf.Clamp(Mathf.Ceil(num / 2f) * 1.3f - 1.5f, 0f, 999f)); - if (reset) + SortAllModifiers(text); + _needsRoleRefresh = true; + + /*var instance = MatchInfoGuide.Instance; + instance.rolesEnabledMessage.SetActive(num == 0);*/ + + _modifiersScroller.SetYBoundsMax(Mathf.Clamp(Mathf.Ceil(num / 2f) * 1.3f - 1.5f, 0f, 999f)); + } + + public static void ToggleRoleVisibility() + { + var num = 0; + foreach (var (roleData, holder) in RolePanels) { - instance.CreatePlayerEntries(); + var panel = holder.Panel; + int amount; + int chance; + int forciblyShow = 0; + if (roleData is ICustomRole custom) + { + amount = custom.GetCount().GetValueOrDefault(0); + chance = custom.GetChance().GetValueOrDefault(0); + if (custom.ForceShowRoleOnWiki.HasValue) + { + forciblyShow = custom.ForceShowRoleOnWiki.Value ? 2 : 1; + } + } + else + { + amount = + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(roleData.Role); + chance = + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(roleData.Role); + } + + panel.SetRolePanel( + roleData, + amount, + chance); + holder.Amount = amount; + holder.Chance = chance; + holder.LikelyhoodOfRole = amount * chance; + var isRoleDisabled = amount == 0 || chance == 0; + var isRoleNotVisible = (roleData is ICustomRole custom2 && + !custom2.CanSpawnOnCurrentMode()) || + (Enum.IsDefined(roleData.Role) && roleData.IsRoleBlacklisted()); + if ((forciblyShow == 0 && ((isRoleDisabled && sortFilter is SortingFilter.EnabledOnly) || + (!isRoleDisabled && sortFilter is SortingFilter.DisabledOnly) || + isRoleNotVisible)) + || forciblyShow == 1) + { + panel.gameObject.SetActive(false); + continue; + } + + panel.gameObject.SetActive(true); + num++; + } + + var text = string.Empty; + try + { + text = searchBoxTmp.outputText.text; + } + catch + { + // ignored + } + + SortAllRoles(text); + + var instance = MatchInfoGuide.Instance; + instance.rolesEnabledMessage.SetActive(num == 0); + + instance.MatchInfoRoleScroller.SetYBoundsMax(Mathf.Clamp(Mathf.Ceil(num / 2f) * 1.3f - 1.5f, 0f, 999f)); + } + + public static void SortAllRoles(string searchText) + { + var newSorted = sortMethod is SortingMethod.Alphabetical + ? RolePanels + .OrderByDescending(child => + child.Value.GetTitle().Equals(searchText, StringComparison.OrdinalIgnoreCase)) + .ThenByDescending(child => child.Value.GetTitle().Contains( + searchText, + StringComparison.InvariantCultureIgnoreCase)) + .ThenBy(GetSortingOrder()) + : RolePanels + .OrderByDescending(child => + child.Value.GetTitle().Equals(searchText, StringComparison.OrdinalIgnoreCase)) + .ThenByDescending(child => child.Value.GetTitle().Contains( + searchText, + StringComparison.InvariantCultureIgnoreCase)) + .ThenByDescending(GetSortingOrder()); + + foreach (var pair in newSorted) + { + pair.Value.Panel.transform.SetAsLastSibling(); + } + } + + public static void SortAllModifiers(string searchText) + { + var newSorted = sortMethod is SortingMethod.Alphabetical + ? ModifierPanels + .OrderByDescending(child => + child.Value.GetTitle().Equals(searchText, StringComparison.OrdinalIgnoreCase)) + .ThenByDescending(child => child.Value.GetTitle().Contains( + searchText, + StringComparison.InvariantCultureIgnoreCase)) + .ThenBy(GetModifierSortingOrder()) + : ModifierPanels + .OrderByDescending(child => + child.Value.GetTitle().Equals(searchText, StringComparison.OrdinalIgnoreCase)) + .ThenByDescending(child => child.Value.GetTitle().Contains( + searchText, + StringComparison.InvariantCultureIgnoreCase)) + .ThenByDescending(GetModifierSortingOrder()); + + foreach (var pair in newSorted) + { + pair.Value.Panel.transform.SetAsLastSibling(); + } + } + + public static Func, string> GetSortingOrder() + { + switch (sortOrder) + { + case SortingGroups.Faction: + return x => + $"{x.Value.Category} ({x.Value.Title}) {x.Value.LikelyhoodOfRole.ToString("0000.0", CultureInfo.InvariantCulture)}"; + case SortingGroups.AmountChance: + return x => + $"{x.Value.LikelyhoodOfRole.ToString("0000.0", CultureInfo.InvariantCulture)} {x.Value.Title} ({x.Value.Category})"; + default: + return x => + $"{x.Value.Title} ({x.Value.Category}) {x.Value.LikelyhoodOfRole.ToString("0000.0", CultureInfo.InvariantCulture)}"; + } + } + + public static Func, string> GetModifierSortingOrder() + { + switch (sortOrder) + { + case SortingGroups.Faction: + return x => + $"{x.Value.Category} ({x.Value.Title}) {x.Value.LikelyhoodOfRole.ToString("0000.0", CultureInfo.InvariantCulture)}"; + case SortingGroups.AmountChance: + return x => + $"{x.Value.LikelyhoodOfRole.ToString("0000.0", CultureInfo.InvariantCulture)} {x.Value.Title} ({x.Value.Category})"; + default: + return x => + $"{x.Value.Title} ({x.Value.Category}) {x.Value.LikelyhoodOfRole.ToString("0000.0", CultureInfo.InvariantCulture)}"; + } + } + + public static string GetTitle(this DetailedPanel panel) + { + switch (sortOrder) + { + case SortingGroups.Faction: + return + $"{panel.Category} ({panel.Title}) {panel.LikelyhoodOfRole.ToString("0000.0", CultureInfo.InvariantCulture)}"; + case SortingGroups.AmountChance: + return + $"{panel.LikelyhoodOfRole.ToString("0000.0", CultureInfo.InvariantCulture)} {panel.Title} ({panel.Category})"; + default: + return + $"{panel.Title} ({panel.Category}) {panel.LikelyhoodOfRole.ToString("0000.0", CultureInfo.InvariantCulture)}"; } } @@ -301,28 +1040,68 @@ public static class RoleGuidePatches [HarmonyPostfix] [HarmonyPriority(Priority.First)] [HarmonyPatch(typeof(MatchInfoGuide), nameof(MatchInfoGuide.SetActiveTab))] - private static void SetActiveTab() + private static void SetActiveTab(int tabIndex) { if (titleText) { - titleText.text = TranslationController.Instance.GetString(StringNames.MatchInfoGuideTitle); + switch (tabIndex) + { + case 0: + titleText.text = MiraLocaleManager.Get("MiraApi.Wiki.PlayersTab"); + break; + case 1: + titleText.text = MiraLocaleManager.Get("MiraApi.Wiki.SettingsTab"); + break; + case 2: + titleText.text = MiraLocaleManager.Get("Roles"); + break; + case 3: + titleText.text = MiraLocaleManager.Get("Modifiers"); + break; + default: + titleText.text = TranslationController.Instance.GetString(StringNames.MatchInfoGuideTitle); + break; + } } - advancedWikiTab?.gameObject.SetActive(false); + _advancedInfoTabScroller?.gameObject.SetActive(false); } - public static void DisplayAdvancedWiki(MatchInfoGuide instance, RoleBehaviour role) + public static void DisplayAdvancedWiki(MatchInfoGuide instance, BaseModifier modifier) { - Warning($"Opening advanced tab for {role.GetRoleName()}."); + var name = modifier.ModifierName; + Warning($"Opening advanced tab for {name}."); instance.settingsTabs[2].SetActive(false); - advancedWikiTab?.gameObject.SetActive(true); + instance.settingsTabs[3].SetActive(false); + _advancedInfoTabScroller?.gameObject.SetActive(true); if (currentAdvancedTabObject) { currentAdvancedTabObject.SetActive(false); currentAdvancedTabObject.Destroy(); } - if (advancedWikiTab == null) + if (_advancedInfoTabScroller == null) + { + Warning($"Wiki tab is null."); + return; + } + + currentAdvancedTabObject = modifier.GetAdvancedWiki(instance, titleText, _advancedInfoTabScroller); + } + + public static void DisplayAdvancedWiki(MatchInfoGuide instance, RoleBehaviour role) + { + Warning($"Opening advanced tab for {role.GetRoleName()}."); + instance.settingsTabs[2].SetActive(false); + instance.settingsTabs[3].SetActive(false); + _advancedInfoTabScroller?.gameObject.SetActive(true); + if (currentAdvancedTabObject) + { + currentAdvancedTabObject.SetActive(false); + currentAdvancedTabObject.Destroy(); + } + + if (_advancedInfoTabScroller == null) { Warning($"Wiki tab is null."); return; @@ -330,12 +1109,13 @@ public static class RoleGuidePatches if (role is ICustomRole custom) { - currentAdvancedTabObject = custom.GetAdvancedWiki(instance, titleText, advancedWikiTab); + currentAdvancedTabObject = custom.GetAdvancedWiki(instance, titleText, _advancedInfoTabScroller); } else { - advancedWikiTab.ScrollToTop(); - var titleTxt = role.GetRoleName() + $" ({TranslationController.Instance.GetString(role.TeamType is RoleTeamTypes.Crewmate ? StringNames.Crewmate : StringNames.Impostor)})"; + _advancedInfoTabScroller.ScrollToTop(); + 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")) { @@ -353,9 +1133,9 @@ public static class RoleGuidePatches description, titleText, out var desc); - currentAdvancedTabObject.transform.SetParent(advancedWikiTab.Inner.transform); + currentAdvancedTabObject.transform.SetParent(_advancedInfoTabScroller.Inner.transform); currentAdvancedTabObject.transform.localPosition = new Vector3(0f, 0f, 0f); - advancedWikiTab.SetYBoundsMax(Mathf.Clamp(desc.textBounds.size.y - 2, 0f, 999f)); + _advancedInfoTabScroller.SetYBoundsMax(Mathf.Clamp(desc.textBounds.size.y - 2, 0f, 999f)); } } @@ -364,18 +1144,23 @@ public static class RoleGuidePatches [HarmonyPatch(typeof(MatchInfoRolePanel), nameof(MatchInfoRolePanel.SetPanel))] public static bool SetPanel(MatchInfoRolePanel __instance, RoleBehaviour role, int numPerGame, int chancePerGame) { - __instance.roleCount.text = string.Format(CultureInfo.InvariantCulture, "{0} at {1}%", numPerGame.ToString(CultureInfo.InvariantCulture), chancePerGame); + __instance.roleCount.text = string.Format( + CultureInfo.InvariantCulture, + "{0} at {1}%", + numPerGame.ToString(CultureInfo.InvariantCulture), + chancePerGame); if (role is ICustomRole customRole) { __instance.roleName.text = customRole.RoleName; - __instance.roleDescription.text = $"{customRole.RoleFactionTitle}\n" + customRole.RoleMedDescription; + __instance.roleDescription.text = + $"{role.GetCategoryTitle()}\n" + customRole.RoleMedDescription; __instance.roleIcon.sprite = customRole.Configuration.Icon?.LoadAsset(); __instance.roleCount.text += $" ({customRole.ParentMod.MiraPlugin.GetAbbreviatedModName()})"; } else { __instance.roleName.text = role.NiceName; - __instance.roleDescription.text = $"{TranslationController.Instance.GetString(role.TeamType is RoleTeamTypes.Crewmate ? StringNames.Crewmate : StringNames.Impostor)}\n" + role.BlurbMed; + __instance.roleDescription.text = $"{role.GetCategoryTitle()}\n" + role.BlurbMed; __instance.roleIcon.sprite = role.RoleIconColor; __instance.roleCount.text += " (AU)"; } @@ -388,4 +1173,100 @@ public static class RoleGuidePatches __instance.roleIcon.transform.localScale = new Vector3(4f, 4f, 1f); return false; } + + public static void SetRolePanel( + this MatchInfoRolePanel instance, + RoleBehaviour role, + int numPerGame, + int chancePerGame) + { + instance.roleCount.text = string.Format( + CultureInfo.InvariantCulture, + "{0} at {1}%", + numPerGame.ToString(CultureInfo.InvariantCulture), + chancePerGame); + if (role is ICustomRole customRole) + { + instance.roleName.text = customRole.RoleName; + instance.roleDescription.text = + $"{role.GetCategoryTitle()}\n" + customRole.RoleMedDescription; + instance.roleIcon.sprite = customRole.Configuration.Icon?.LoadAsset(); + instance.roleCount.text += $" ({customRole.ParentMod.MiraPlugin.GetAbbreviatedModName()})"; + } + else + { + instance.roleName.text = role.NiceName; + instance.roleDescription.text = $"{role.GetCategoryTitle()}\n" + role.BlurbMed; + instance.roleIcon.sprite = role.RoleIconColor; + instance.roleCount.text += " (AU)"; + } + + instance.roleIcon.SetSizeLimit(0.13f); + instance.roleIcon.material.SetInt(PlayerMaterial.MaskLayer, 50); + instance.roleName.fontMaterial.SetFloat(instance.STENCIL_NAME, 50f); + instance.roleDescription.fontMaterial.SetFloat(instance.STENCIL_NAME, 50f); + instance.roleCount.fontMaterial.SetFloat(instance.STENCIL_NAME, 50f); + instance.roleIcon.transform.localScale = new Vector3(4f, 4f, 1f); + } + + public static void SetModifierPanel( + this MatchInfoRolePanel instance, + GameModifier modifier, + int numPerGame, + int chancePerGame) + { + instance.roleCount.text = string.Format( + CultureInfo.InvariantCulture, + "{0} at {1}%", + numPerGame.ToString(CultureInfo.InvariantCulture), + chancePerGame); + instance.roleName.text = modifier.ModifierName; + instance.roleDescription.text = + $"{modifier.ModifierCategoryTitle}\n" + modifier.ModifierMedDescription; + instance.roleIcon.sprite = modifier.ModifierIcon?.LoadAsset(); + instance.roleCount.text += $" ({modifier.ParentMod.MiraPlugin.GetAbbreviatedModName()})"; + + instance.roleIcon.SetSizeLimit(0.13f); + instance.roleIcon.material.SetInt(PlayerMaterial.MaskLayer, 50); + instance.roleName.fontMaterial.SetFloat(instance.STENCIL_NAME, 50f); + instance.roleDescription.fontMaterial.SetFloat(instance.STENCIL_NAME, 50f); + instance.roleCount.fontMaterial.SetFloat(instance.STENCIL_NAME, 50f); + instance.roleIcon.transform.localScale = new Vector3(4f, 4f, 1f); + } + + public static string GetCategoryTitle(this RoleBehaviour role) + { + if (role is ICustomRole customRole) + { + return customRole.GetCategoryTitle(); + } + + return TranslationController.Instance.GetString( + role.TeamType is RoleTeamTypes.Crewmate ? StringNames.Crewmate : StringNames.Impostor); + } + + public static string GetCategoryTitle(this ICustomRole customRole) + { + return customRole.RoleFactionTitle; + } +} + +public enum SortingGroups +{ + Ungrouped, + Faction, + AmountChance, +} + +public enum SortingMethod +{ + Alphabetical, + AlphabeticalDescending, +} + +public enum SortingFilter +{ + EnabledOnly, + DisabledOnly, + AllRoles, } diff --git a/MiraAPI/Patches/Roles/KillButtonPatches.cs b/MiraAPI/Patches/Roles/KillButtonPatches.cs index fc32a7a..9693346 100644 --- a/MiraAPI/Patches/Roles/KillButtonPatches.cs +++ b/MiraAPI/Patches/Roles/KillButtonPatches.cs @@ -1,5 +1,4 @@ using HarmonyLib; -using Il2CppSystem; using MiraAPI.Networking; using MiraAPI.Roles; using UnityEngine; @@ -31,13 +30,13 @@ public static class KillButtonPatches if (__instance.currentTarget && __instance.currentTarget != target) { - __instance.currentTarget.cosmetics.SetOutline(false, new Nullable(Color.clear)); + __instance.currentTarget.cosmetics.SetOutline(false, new Il2CppSystem.Nullable(Color.clear)); } __instance.currentTarget = target; if (__instance.currentTarget) { - __instance.currentTarget.cosmetics.SetOutline(true, new Nullable(customRole.Configuration.KillButtonOutlineColor)); + __instance.currentTarget.cosmetics.SetOutline(true, new Il2CppSystem.Nullable(customRole.Configuration.KillButtonOutlineColor)); __instance.SetEnabled(); return false; } diff --git a/MiraAPI/Patches/Roles/LogicRoleSelectionHnsPatch.cs b/MiraAPI/Patches/Roles/LogicRoleSelectionHnsPatch.cs index 1a845fc..c1e9da7 100644 --- a/MiraAPI/Patches/Roles/LogicRoleSelectionHnsPatch.cs +++ b/MiraAPI/Patches/Roles/LogicRoleSelectionHnsPatch.cs @@ -1,8 +1,8 @@ using System; +using System.Collections.Generic; using System.Linq; using AmongUs.GameOptions; using HarmonyLib; -using Il2CppSystem.Collections.Generic; using MiraAPI.Roles; namespace MiraAPI.Patches.Roles; @@ -14,7 +14,7 @@ public static class LogicRoleSelectionHnsPatch [HarmonyPatch(nameof(LogicRoleSelectionHnS.AssignRolesForTeam))] public static bool AssignRolesForTeam( LogicRoleSelectionHnS __instance, - List players, + Il2CppSystem.Collections.Generic.List players, IGameOptions opts, RoleTeamTypes team, int teamMax, @@ -58,7 +58,7 @@ public static class LogicRoleSelectionHnsPatch case RoleTeamTypes.Impostor: Info($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Before Guaranteed Assignment"); - var newImpostors = new List(); + var newImpostors = new Il2CppSystem.Collections.Generic.List(); // Specified Seeker if (__instance.hnsManager.LogicOptionsHnS.HasImpostorPlayerID() && @@ -113,7 +113,7 @@ public static class LogicRoleSelectionHnsPatch return false; } - public static void AssignRolesFromList(List players, int teamMax, List roleList, ref int rolesAssigned) + public static void AssignRolesFromList(Il2CppSystem.Collections.Generic.List players, int teamMax, List roleList, ref int rolesAssigned) { while (roleList.Count > 0 && players.Count > 0 && rolesAssigned < teamMax) { @@ -128,7 +128,7 @@ public static class LogicRoleSelectionHnsPatch } private static void AddGuaranteedRoles( - System.Collections.Generic.IEnumerable guaranteedRoles, + IEnumerable guaranteedRoles, IGameOptions opts, List list) { @@ -146,7 +146,7 @@ public static class LogicRoleSelectionHnsPatch } } - private static void AddPotentialRoles(System.Collections.Generic.IEnumerable source, IGameOptions opts, List list) + private static void AddPotentialRoles(IEnumerable source, IGameOptions opts, List list) { var roleOptions = opts.RoleOptions; var potentialRoles = source.Where(x => !x.IsDead).Select(role => new RoleManager.RoleAssignmentData( diff --git a/MiraAPI/Patches/Stubs/RoleBehaviourStubs.cs b/MiraAPI/Patches/Stubs/RoleBehaviourStubs.cs index 69b4089..f047fd3 100644 --- a/MiraAPI/Patches/Stubs/RoleBehaviourStubs.cs +++ b/MiraAPI/Patches/Stubs/RoleBehaviourStubs.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; using HarmonyLib; using Hazel; -using Il2CppSystem.Text; namespace MiraAPI.Patches.Stubs; @@ -45,9 +44,9 @@ public static class RoleBehaviourStubs /// The object. /// The to append the task hint to. [HarmonyReversePatch] - [HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.AppendTaskHint), typeof(StringBuilder))] + [HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.AppendTaskHint), typeof(Il2CppSystem.Text.StringBuilder))] [MethodImpl(MethodImplOptions.NoInlining)] - public static void AppendTaskHint(RoleBehaviour instance, StringBuilder taskStringBuilder) + public static void AppendTaskHint(RoleBehaviour instance, Il2CppSystem.Text.StringBuilder taskStringBuilder) { // nothing needed } diff --git a/MiraAPI/Presets/SavePresetPopup.cs b/MiraAPI/Presets/SavePresetPopup.cs index 8df354c..b9932ff 100644 --- a/MiraAPI/Presets/SavePresetPopup.cs +++ b/MiraAPI/Presets/SavePresetPopup.cs @@ -9,7 +9,10 @@ using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; +// TODO: for the love of fucking god please add a namespace on the asset bundle AND here - Atony. +/* namespace MiraAPI.Presets; +*/ [RegisterInIl2Cpp] [SuppressMessage("Style", "IDE0051:Remove unused private members", Justification = "Unity convention.")] diff --git a/MiraAPI/Resources/Locale/en_US.xml b/MiraAPI/Resources/Locale/en_US.xml index 7cd2c1c..245b7ae 100644 --- a/MiraAPI/Resources/Locale/en_US.xml +++ b/MiraAPI/Resources/Locale/en_US.xml @@ -17,6 +17,7 @@ Custom Category 2 Configure modifiers and their settings here! Apply game settings for this mod! + Gamemode Classic The classic Among Us experience! Find the Impostors or deceive the Crew. @@ -43,4 +44,15 @@ Outlive the timer or eliminate the crew. Page Page Main + Ability + Indirect Ability + Interaction Ability + Radius Ability + Menu Ability + Meeting Ability + Selective Meeting Ability + Meeting Menu Ability + Options + Player List + Game Settings diff --git a/MiraAPI/Resources/Wiki/GroupCountHover.png b/MiraAPI/Resources/Wiki/GroupCountHover.png new file mode 100644 index 0000000..0e8ec76 Binary files /dev/null and b/MiraAPI/Resources/Wiki/GroupCountHover.png differ diff --git a/MiraAPI/Resources/Wiki/GroupCountIdle.png b/MiraAPI/Resources/Wiki/GroupCountIdle.png new file mode 100644 index 0000000..5bf5f98 Binary files /dev/null and b/MiraAPI/Resources/Wiki/GroupCountIdle.png differ diff --git a/MiraAPI/Resources/Wiki/GroupFactionHover.png b/MiraAPI/Resources/Wiki/GroupFactionHover.png new file mode 100644 index 0000000..864e94d Binary files /dev/null and b/MiraAPI/Resources/Wiki/GroupFactionHover.png differ diff --git a/MiraAPI/Resources/Wiki/GroupFactionIdle.png b/MiraAPI/Resources/Wiki/GroupFactionIdle.png new file mode 100644 index 0000000..1d50880 Binary files /dev/null and b/MiraAPI/Resources/Wiki/GroupFactionIdle.png differ diff --git a/MiraAPI/Resources/Wiki/GroupNoneHover.png b/MiraAPI/Resources/Wiki/GroupNoneHover.png new file mode 100644 index 0000000..dd303b2 Binary files /dev/null and b/MiraAPI/Resources/Wiki/GroupNoneHover.png differ diff --git a/MiraAPI/Resources/Wiki/GroupNoneIdle.png b/MiraAPI/Resources/Wiki/GroupNoneIdle.png new file mode 100644 index 0000000..c215f2f Binary files /dev/null and b/MiraAPI/Resources/Wiki/GroupNoneIdle.png differ diff --git a/MiraAPI/Resources/Wiki/SearchClearHover.png b/MiraAPI/Resources/Wiki/SearchClearHover.png new file mode 100644 index 0000000..a23a1da Binary files /dev/null and b/MiraAPI/Resources/Wiki/SearchClearHover.png differ diff --git a/MiraAPI/Resources/Wiki/SearchClearIdle.png b/MiraAPI/Resources/Wiki/SearchClearIdle.png new file mode 100644 index 0000000..7f50a9b Binary files /dev/null and b/MiraAPI/Resources/Wiki/SearchClearIdle.png differ diff --git a/MiraAPI/Resources/Wiki/SearchIconHover.png b/MiraAPI/Resources/Wiki/SearchIconHover.png new file mode 100644 index 0000000..d64f9af Binary files /dev/null and b/MiraAPI/Resources/Wiki/SearchIconHover.png differ diff --git a/MiraAPI/Resources/Wiki/SearchIconIdle.png b/MiraAPI/Resources/Wiki/SearchIconIdle.png new file mode 100644 index 0000000..d16f7f9 Binary files /dev/null and b/MiraAPI/Resources/Wiki/SearchIconIdle.png differ diff --git a/MiraAPI/Resources/Wiki/SidebarPlayersButtonHover.png b/MiraAPI/Resources/Wiki/SidebarPlayersButtonHover.png new file mode 100644 index 0000000..c4bd871 Binary files /dev/null and b/MiraAPI/Resources/Wiki/SidebarPlayersButtonHover.png differ diff --git a/MiraAPI/Resources/Wiki/SidebarPlayersButtonIdle.png b/MiraAPI/Resources/Wiki/SidebarPlayersButtonIdle.png new file mode 100644 index 0000000..6c991be Binary files /dev/null and b/MiraAPI/Resources/Wiki/SidebarPlayersButtonIdle.png differ diff --git a/MiraAPI/Resources/Wiki/SidebarPlayersButtonOpen.png b/MiraAPI/Resources/Wiki/SidebarPlayersButtonOpen.png new file mode 100644 index 0000000..6e6ccbf Binary files /dev/null and b/MiraAPI/Resources/Wiki/SidebarPlayersButtonOpen.png differ diff --git a/MiraAPI/Resources/Wiki/SidebarRolesButtonHover.png b/MiraAPI/Resources/Wiki/SidebarRolesButtonHover.png new file mode 100644 index 0000000..8bfb8d1 Binary files /dev/null and b/MiraAPI/Resources/Wiki/SidebarRolesButtonHover.png differ diff --git a/MiraAPI/Resources/Wiki/SidebarRolesButtonIdle.png b/MiraAPI/Resources/Wiki/SidebarRolesButtonIdle.png new file mode 100644 index 0000000..c75d077 Binary files /dev/null and b/MiraAPI/Resources/Wiki/SidebarRolesButtonIdle.png differ diff --git a/MiraAPI/Resources/Wiki/SidebarRolesButtonOpen.png b/MiraAPI/Resources/Wiki/SidebarRolesButtonOpen.png new file mode 100644 index 0000000..162bf07 Binary files /dev/null and b/MiraAPI/Resources/Wiki/SidebarRolesButtonOpen.png differ diff --git a/MiraAPI/Resources/Wiki/SidebarSettingsButtonHover.png b/MiraAPI/Resources/Wiki/SidebarSettingsButtonHover.png new file mode 100644 index 0000000..7813904 Binary files /dev/null and b/MiraAPI/Resources/Wiki/SidebarSettingsButtonHover.png differ diff --git a/MiraAPI/Resources/Wiki/SidebarSettingsButtonIdle.png b/MiraAPI/Resources/Wiki/SidebarSettingsButtonIdle.png new file mode 100644 index 0000000..fdf65df Binary files /dev/null and b/MiraAPI/Resources/Wiki/SidebarSettingsButtonIdle.png differ diff --git a/MiraAPI/Resources/Wiki/SidebarSettingsButtonOpen.png b/MiraAPI/Resources/Wiki/SidebarSettingsButtonOpen.png new file mode 100644 index 0000000..ec5bb14 Binary files /dev/null and b/MiraAPI/Resources/Wiki/SidebarSettingsButtonOpen.png differ diff --git a/MiraAPI/Resources/Wiki/SortingAzHover.png b/MiraAPI/Resources/Wiki/SortingAzHover.png new file mode 100644 index 0000000..d9779c6 Binary files /dev/null and b/MiraAPI/Resources/Wiki/SortingAzHover.png differ diff --git a/MiraAPI/Resources/Wiki/SortingAzIdle.png b/MiraAPI/Resources/Wiki/SortingAzIdle.png new file mode 100644 index 0000000..5efb21b Binary files /dev/null and b/MiraAPI/Resources/Wiki/SortingAzIdle.png differ diff --git a/MiraAPI/Resources/Wiki/SortingZaHover.png b/MiraAPI/Resources/Wiki/SortingZaHover.png new file mode 100644 index 0000000..c9d817a Binary files /dev/null and b/MiraAPI/Resources/Wiki/SortingZaHover.png differ diff --git a/MiraAPI/Resources/Wiki/SortingZaIdle.png b/MiraAPI/Resources/Wiki/SortingZaIdle.png new file mode 100644 index 0000000..199071a Binary files /dev/null and b/MiraAPI/Resources/Wiki/SortingZaIdle.png differ diff --git a/MiraAPI/Resources/Wiki/VisibleAllHover.png b/MiraAPI/Resources/Wiki/VisibleAllHover.png new file mode 100644 index 0000000..9380f83 Binary files /dev/null and b/MiraAPI/Resources/Wiki/VisibleAllHover.png differ diff --git a/MiraAPI/Resources/Wiki/VisibleAllIdle.png b/MiraAPI/Resources/Wiki/VisibleAllIdle.png new file mode 100644 index 0000000..9ddcaee Binary files /dev/null and b/MiraAPI/Resources/Wiki/VisibleAllIdle.png differ diff --git a/MiraAPI/Resources/Wiki/VisibleOffHover.png b/MiraAPI/Resources/Wiki/VisibleOffHover.png new file mode 100644 index 0000000..ecfaefe Binary files /dev/null and b/MiraAPI/Resources/Wiki/VisibleOffHover.png differ diff --git a/MiraAPI/Resources/Wiki/VisibleOffIdle.png b/MiraAPI/Resources/Wiki/VisibleOffIdle.png new file mode 100644 index 0000000..a0c8ea9 Binary files /dev/null and b/MiraAPI/Resources/Wiki/VisibleOffIdle.png differ diff --git a/MiraAPI/Resources/Wiki/VisibleOnHover.png b/MiraAPI/Resources/Wiki/VisibleOnHover.png new file mode 100644 index 0000000..a4e5be8 Binary files /dev/null and b/MiraAPI/Resources/Wiki/VisibleOnHover.png differ diff --git a/MiraAPI/Resources/Wiki/VisibleOnIdle.png b/MiraAPI/Resources/Wiki/VisibleOnIdle.png new file mode 100644 index 0000000..30f1067 Binary files /dev/null and b/MiraAPI/Resources/Wiki/VisibleOnIdle.png differ diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index 060b1a5..25ab64e 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -45,6 +45,11 @@ public static class CustomRoleManager /// public static IReadOnlyList CustomRoleBehaviours { get; private set; } = []; + /// + /// Gets the of all roles as objects. + /// + public static IReadOnlyList AllStoredRoleBehaviours { get; private set; } = []; + /// /// Gets the of custom roles as objects. /// @@ -77,6 +82,7 @@ public static class CustomRoleManager CustomRoleBehaviours = [.. CustomRoles.Values]; CustomMiraRoles = [.. CustomRoles.Values.OfType()]; + AllStoredRoleBehaviours = [.. RoleManager.Instance.AllRoles]; } internal static void RegisterRoleTypes(List roles, MiraPluginInfo pluginInfo) diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 0030024..dc38423 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -1,14 +1,19 @@ using System; +using System.Collections.Generic; using System.Text; using BepInEx.Configuration; +using Il2CppInterop.Runtime.Attributes; using MiraAPI.GameModes; using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.PluginLoading; using MiraAPI.Translation; using MiraAPI.Utilities; +using MiraAPI.Utilities.Assets; using TMPro; using UnityEngine; +using UnityEngine.UI; +using Object = UnityEngine.Object; namespace MiraAPI.Roles; @@ -68,9 +73,10 @@ public interface ICustomRole : IOptionable string RoleLongDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "TabDescription"); /// - /// Gets the wiki description of the role. Used in the wiki, but not currently required. + /// Gets the wiki description of the role. Used in the wiki and normally appends the options text as well. /// - string RoleWikiDescription => MiraLocaleManager.Get(RoleWikiDescriptionLocale, RoleLongDescription); + string RoleWikiDescription => MiraLocaleManager.Get(RoleWikiDescriptionLocale, RoleLongDescription) + + Helpers.GetOptionsText(GetType()); /// /// Gets the role's wiki description id for localization. @@ -326,9 +332,45 @@ public interface ICustomRole : IOptionable RoleWikiDescription, titleText, out var desc); + var num = 0; + if (WikiAbilities.Count != 0) + { + var grid = Object.Instantiate(parent.Inner, obj.transform); + var layoutGroup = grid.GetComponent(); + layoutGroup.startAxis = GridLayoutGroup.Axis.Vertical; + layoutGroup.constraint = GridLayoutGroup.Constraint.FixedColumnCount; + layoutGroup.spacing = new Vector2(0.75f, 0.4f); + grid.DestroyChildren(); + foreach (var ability in WikiAbilities) + { + num++; + var panel = Object.Instantiate( + guide.MatchInfoRolePanelPrefab, + grid); + panel.roleCount.text = ability.AbilityType; + panel.roleCount.transform.localPosition += new Vector3(-0.04f, 0); + panel.roleName.text = ability.Name; + panel.roleName.transform.localPosition += new Vector3(-0.04f, 0); + panel.roleDescription.text = ability.Description; + panel.roleDescription.rectTransform.sizeDelta = new Vector2(2.601f, 0.8f); + panel.roleDescription.transform.localPosition += new Vector3(0, -0.1f); + panel.roleIcon.sprite = ability.Icon.LoadAsset(); + panel.roleIcon.SetSizeLimit(0.13f); + + panel.roleIcon.material.SetInt(PlayerMaterial.MaskLayer, 50); + panel.roleName.fontMaterial.SetFloat(panel.STENCIL_NAME, 50f); + panel.roleDescription.fontMaterial.SetFloat(panel.STENCIL_NAME, 50f); + panel.roleCount.fontMaterial.SetFloat(panel.STENCIL_NAME, 50f); + panel.roleIcon.transform.localScale = new Vector3(3.5f, 3.5f, 1f); + } + + grid.localPosition = new Vector3(-3.9f, 1.1f - desc.textBounds.size.y, 0f); + grid.localScale = new Vector3(1.3f, 1.3f, 1); + } + obj.transform.SetParent(parent.Inner.transform); obj.transform.localPosition = new Vector3(0f, 0f, 0f); - parent.SetYBoundsMax(Mathf.Clamp(desc.textBounds.size.y - 2, 0f, 999f)); + parent.SetYBoundsMax(Mathf.Clamp((desc.textBounds.size.y - 2) + (Mathf.Ceil(num / 2f) * 1.45f), 0f, 999f)); return obj; } @@ -336,4 +378,12 @@ public interface ICustomRole : IOptionable /// Gets the function that determines whether the role should be toggled on or off in the game settings. /// Func VisibleInSettings => () => true; + + /// + /// Gets the list of abilities or other similar information to display below the text of a role's wiki page. + /// + [HideFromIl2Cpp] + public List WikiAbilities => []; } + +public record struct AdvancedWikiAbilityDescription(string Name, string AbilityType, string Description, LoadableAsset Icon); diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs index bec994a..249e942 100644 --- a/MiraAPI/Utilities/Assets/MiraAssets.cs +++ b/MiraAPI/Utilities/Assets/MiraAssets.cs @@ -160,5 +160,150 @@ public static class MiraAssets /// public static LoadableResourceAsset ChatOpenSprite { get; } = new("MiraAPI.Resources.NormalChatOpen.png"); + /// + /// Gets the sprite used for players button while hovering. + /// + public static LoadableResourceAsset WikiPlayersButtonHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarPlayersButtonHover.png"); + + /// + /// Gets the sprite used for the players button while hovering. + /// + public static LoadableResourceAsset WikiPlayersButtonIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarPlayersButtonIdle.png"); + + /// + /// Gets the sprite used for the players button while hovering. + /// + public static LoadableResourceAsset WikiPlayersButtonOpenSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarPlayersButtonOpen.png"); + + /// + /// Gets the sprite used for the settings button while hovering. + /// + public static LoadableResourceAsset WikiSettingsButtonHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarSettingsButtonHover.png"); + + /// + /// Gets the sprite used for the settings button while hovering. + /// + public static LoadableResourceAsset WikiSettingsButtonIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarSettingsButtonIdle.png"); + + /// + /// Gets the sprite used for the settings button while hovering. + /// + public static LoadableResourceAsset WikiSettingsButtonOpenSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarSettingsButtonOpen.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset WikiRolesButtonHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarRolesButtonHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset WikiRolesButtonIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarRolesButtonIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset WikiRolesButtonOpenSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarRolesButtonOpen.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset GroupCountHoverSprite { get; } = new("MiraAPI.Resources.Wiki.GroupCountHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset GroupCountIdleSprite { get; } = new("MiraAPI.Resources.Wiki.GroupCountIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset GroupFactionHoverSprite { get; } = new("MiraAPI.Resources.Wiki.GroupFactionHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset GroupFactionIdleSprite { get; } = new("MiraAPI.Resources.Wiki.GroupFactionIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset GroupNoneHoverSprite { get; } = new("MiraAPI.Resources.Wiki.GroupNoneHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset GroupNoneIdleSprite { get; } = new("MiraAPI.Resources.Wiki.GroupNoneIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset SearchClearHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SearchClearHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset SearchClearIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SearchClearIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset SearchIconHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SearchIconHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset SearchIconIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SearchIconIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset SortingAzHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SortingAzHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset SortingAzIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SortingAzIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset SortingZaHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SortingZaHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset SortingZaIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SortingZaIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset VisibleAllHoverSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleAllHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset VisibleAllIdleSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleAllIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset VisibleOffHoverSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleOffHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset VisibleOffIdleSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleOffIdle.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset VisibleOnHoverSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleOnHover.png"); + + /// + /// Gets the sprite used for the roles button while hovering. + /// + public static LoadableResourceAsset VisibleOnIdleSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleOnIdle.png"); + internal static LoadableResourceAsset BlankSprite { get; } = new("MiraAPI.Resources.BlankSprite.png"); } diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index eded3d3..3584588 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -1,17 +1,24 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; +using System.Text; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; +using MiraAPI.GameOptions; +using MiraAPI.GameOptions.OptionTypes; using MiraAPI.Roles; +using MiraAPI.Translation; using MiraAPI.Utilities.Assets; using Rewired; using TMPro; using UnityEngine; using MethodBase = System.Reflection.MethodBase; using Object = UnityEngine.Object; +using Random = UnityEngine.Random; namespace MiraAPI.Utilities; @@ -20,6 +27,157 @@ namespace MiraAPI.Utilities; /// public static class Helpers { + public static ReadOnlyCollection? GetModdedOptionsForType(Type classType) + { + var optionGroups = + AccessTools.Field(typeof(ModdedOptionsManager), "Groups").GetValue(null) as List; + + return optionGroups?.FirstOrDefault(x => x.OptionableType == classType)?.Children; + } + + public static string GetOptionsText(Type classType) + { + var options = GetModdedOptionsForType(classType); + if (options == null) + { + return string.Empty; + } + + ISummarizedOptions? summaryProvider; + IReadOnlySet? hiddenKeys; + try + { + var optionGroups = + AccessTools.Field(typeof(ModdedOptionsManager), "Groups").GetValue(null) as List; + summaryProvider = + optionGroups?.FirstOrDefault(x => x.OptionableType == classType) as ISummarizedOptions; + hiddenKeys = summaryProvider?.WikiHiddenOptionKeys; + } + catch + { + summaryProvider = null; + hiddenKeys = null; + } + + var builder = new StringBuilder(); + builder.AppendLine( + MiraApiPlugin.Culture, + $"\n \n{MiraLocaleManager.Get("Options")}"); + + var insertedSummary = false; + foreach (var option in options) + { + if (!insertedSummary && summaryProvider != null && hiddenKeys != null) + { + StringNames? key = option switch + { + ModdedToggleOption t => t.StringName, + ModdedEnumOption e => e.StringName, + ModdedNumberOption n => n.StringName, + _ => null, + }; + if (key.HasValue && hiddenKeys.Contains(key.Value)) + { + foreach (var line in summaryProvider.GetWikiOptionSummaryLines()) + { + if (!string.IsNullOrWhiteSpace(line)) + { + builder.AppendLine(line); + } + } + + insertedSummary = true; + } + } + + switch (option) + { + case ModdedToggleOption toggleOption: + if (!toggleOption.Visible()) + { + continue; + } + + if (hiddenKeys != null && hiddenKeys.Contains(toggleOption.StringName)) + { + continue; + } + + builder.AppendLine( + TranslationController.Instance.GetString(toggleOption.StringName) + ": " + + toggleOption.Value); + break; + /*case ModdedMultiSelectOption enumOption: + if (!enumOption.Visible()) + { + continue; + } + + builder.AppendLine(enumOption.Title + ": " + enumOption.Values[enumOption.Value]); + break;*/ + case ModdedEnumOption enumOption: + if (!enumOption.Visible()) + { + continue; + } + + if (hiddenKeys != null && hiddenKeys.Contains(enumOption.StringName)) + { + continue; + } + + builder.AppendLine( + TranslationController.Instance.GetString(enumOption.StringName) + ": " + + MiraLocaleManager.Get( + enumOption.Values[enumOption.Value], + enumOption.Values[enumOption.Value])); + break; + case ModdedNumberOption numberOption: + if (!numberOption.Visible()) + { + continue; + } + + if (hiddenKeys != null && hiddenKeys.Contains(numberOption.StringName)) + { + continue; + } + + var optionStr = numberOption.Data.GetValueString(numberOption.Value); + if (optionStr.Contains(".000")) + { + optionStr = optionStr.Replace(".000", string.Empty); + } + else if (optionStr.Contains(".00")) + { + optionStr = optionStr.Replace(".00", string.Empty); + } + else if (optionStr.Contains(".0")) + { + optionStr = optionStr.Replace(".0", string.Empty); + } + + var title = TranslationController.Instance.GetString(numberOption.StringName); + if (numberOption is { NegativeWordValue: not "#", Value: -1 }) + { + builder.AppendLine(title + $": {numberOption.NegativeWordValue}"); + } + else if (numberOption is { ZeroWordValue: not "#", Value: 0 }) + { + builder.AppendLine(title + $": {numberOption.ZeroWordValue}"); + } + else + { + builder.AppendLine(title + ": " + optionStr); + } + + break; + } + } + + return builder.ToString(); + } + /// /// Get all living players. /// @@ -228,7 +386,7 @@ public static class Helpers var popper = HudManager.Instance.Notifier; var newMessage = Object.Instantiate(popper.notificationMessageOrigin, Vector3.zero, Quaternion.identity, popper.transform); newMessage.transform.localPosition = localPos; - newMessage.SetUp(text, spr ?? null, color, new System.Action(() => popper.OnMessageDestroy(newMessage))); + newMessage.SetUp(text, spr ?? null, color, new Action(() => popper.OnMessageDestroy(newMessage))); popper.lastMessageKey = -1; popper.ShiftMessages(); popper.AddMessageToQueue(newMessage); @@ -280,9 +438,12 @@ public static class Helpers { var results = new CppCollections.List(); Physics2D.OverlapCircle(source, radius, filter, results); - return [.. results.ToArray() - .Where(collider2D => collider2D.CompareTag("DeadBody")) - .Select(collider2D => collider2D.GetComponent())]; + return + [ + .. results.ToArray() + .Where(collider2D => collider2D.CompareTag("DeadBody")) + .Select(collider2D => collider2D.GetComponent()) + ]; } /// @@ -299,9 +460,12 @@ public static class Helpers { var results = new CppCollections.List(); Physics2D.OverlapCircle(source, radius, filter, results); - return [.. results.ToArray() - .Where(collider2D => colliderTag == null || collider2D.CompareTag(colliderTag)) - .Select(collider2D => collider2D.GetComponent())]; + return + [ + .. results.ToArray() + .Where(collider2D => colliderTag == null || collider2D.CompareTag(colliderTag)) + .Select(collider2D => collider2D.GetComponent()) + ]; } /// @@ -320,15 +484,18 @@ public static class Helpers return !ignoreColliders ? newList - : [.. from player in newList - let vector = player.GetTruePosition() - source - let magnitude = vector.magnitude - where !PhysicsHelpers.AnyNonTriggersBetween( - source, - vector.normalized, - magnitude, - Constants.ShipAndObjectsMask) - select player]; + : + [ + .. from player in newList + let vector = player.GetTruePosition() - source + let magnitude = vector.magnitude + where !PhysicsHelpers.AnyNonTriggersBetween( + source, + vector.normalized, + magnitude, + Constants.ShipAndObjectsMask) + select player + ]; } /// @@ -389,17 +556,16 @@ public static class Helpers Constants.ShipAndObjectsMask)) select playerControl); - outputList.Sort( - (a, b) => - { - var magnitude2 = (a.GetTruePosition() - source).magnitude; - var magnitude3 = (b.GetTruePosition() - source).magnitude; - return magnitude2 > magnitude3 - ? 1 - : magnitude2 < magnitude3 - ? -1 - : 0; - }); + outputList.Sort((a, b) => + { + var magnitude2 = (a.GetTruePosition() - source).magnitude; + var magnitude3 = (b.GetTruePosition() - source).magnitude; + return magnitude2 > magnitude3 + ? 1 + : magnitude2 < magnitude3 + ? -1 + : 0; + }); return outputList; } diff --git a/MiraAPI/Voting/VotingUtils.cs b/MiraAPI/Voting/VotingUtils.cs index 3d3e206..ad9e32c 100644 --- a/MiraAPI/Voting/VotingUtils.cs +++ b/MiraAPI/Voting/VotingUtils.cs @@ -300,10 +300,7 @@ public static class VotingUtils // If modified, these will visually change. votes = @event.Votes; - MeetingHud.Instance.TitleText.text = - TranslationController.Instance.GetString( - StringNames.MeetingVotingResults, - Il2CppSystem.Array.Empty()); + MeetingHud.Instance.TitleText.text = TranslationController.Instance.GetString(StringNames.MeetingVotingResults); var delays = new Dictionary(); var num = 0;