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..60b5ea6 --- /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 +{ + /// + /// Option string keys (StringNames) to omit from the wiki options list (e.g., a large set of related options). + /// + IReadOnlySet WikiHiddenOptionKeys { get; } + + /// + /// Summary lines to insert when the first hidden option would have appeared. + /// Lines should already be formatted as "Title: Value". + /// + IEnumerable GetWikiOptionSummaryLines(); +} \ No newline at end of file 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/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..5822bf8 100644 --- a/MiraAPI/Patches/RoleGuidePatches.cs +++ b/MiraAPI/Patches/RoleGuidePatches.cs @@ -7,6 +7,7 @@ using HarmonyLib; using Innersloth.Assets; using MiraAPI.Roles; using MiraAPI.Utilities; +using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; @@ -19,6 +20,20 @@ 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; + [HarmonyPostfix] [HarmonyPriority(Priority.First)] [HarmonyPatch(typeof(MatchInfoGuide), nameof(MatchInfoGuide.CreateNormalModeSettings))] @@ -34,15 +49,21 @@ 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(); } @@ -98,12 +119,56 @@ 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) { + var sortingOrderButton = Object.Instantiate( + HudManager.Instance.SettingsButton, + __instance.TabButtons[2].transform.parent); + SearchSortingOrderButton = sortingOrderButton.GetComponent(); + SearchSortingOrderButton.name = "SearchSortingOrderButton"; + _searchSortingOrderIdle = SearchSortingOrderButton.inactiveSprites.GetComponent(); + _searchSortingOrderIdle.sprite = MiraAssets.SortingAzIdleSprite; + _searchSortingOrderHover = SearchSortingOrderButton.activeSprites.GetComponent(); + _searchSortingOrderHover.sprite = MiraAssets.SortingAzHoverSprite; + SearchSortingOrderButton.OnClick = new Button.ButtonClickedEvent(); + SearchSortingOrderButton.OnClick.AddListener(new Action(SwitchMethod)); + sortingOrderButton.transform.localPosition = new Vector3(2.4f, 0.765f, -0.1f); + sortingOrderButton.transform.localScale = Vector3.one; + sortingOrderButton.transform.GetChild(2).gameObject.SetActive(false); + + var sortingGroupButton = Object.Instantiate( + sortingOrderButton, + sortingOrderButton.transform.parent); + SearchSortingGroupButton = sortingGroupButton.GetComponent(); + SearchSortingGroupButton.name = "SearchSortingGroupButton"; + _searchSortingGroupIdle = SearchSortingGroupButton.inactiveSprites.GetComponent(); + _searchSortingGroupIdle.sprite = MiraAssets.GroupNoneIdleSprite; + _searchSortingGroupHover = SearchSortingGroupButton.activeSprites.GetComponent(); + _searchSortingGroupHover.sprite = MiraAssets.GroupNoneHoverSprite; + SearchSortingGroupButton.OnClick = new Button.ButtonClickedEvent(); + SearchSortingGroupButton.OnClick.AddListener(new Action(SwitchGrouping)); + sortingGroupButton.transform.localPosition = new Vector3(1.8f, 0.765f, -0.1f); + sortingGroupButton.transform.localScale = Vector3.one; + + var sortingFilterButton = Object.Instantiate( + sortingOrderButton, + sortingOrderButton.transform.parent); + SearchSortingFilterButton = sortingFilterButton.GetComponent(); + SearchSortingFilterButton.name = "SearchSortingFilterButton"; + _searchSortingFilterIdle = SearchSortingFilterButton.inactiveSprites.GetComponent(); + _searchSortingFilterIdle.sprite = MiraAssets.VisibleOnIdleSprite; + _searchSortingFilterHover = SearchSortingFilterButton.activeSprites.GetComponent(); + _searchSortingFilterHover.sprite = MiraAssets.VisibleOnHoverSprite; + SearchSortingFilterButton.OnClick = new Button.ButtonClickedEvent(); + SearchSortingFilterButton.OnClick.AddListener(new Action(SwitchFilter)); + sortingFilterButton.transform.localPosition = new Vector3(1f, 0.765f, -0.1f); + sortingFilterButton.transform.localScale = Vector3.one; + __instance.numOfTabs = 3; - __instance.TabButtons[0].SelectButton(true); + __instance.TabButtons[2].SelectButton(true); __instance.MatchInfoRoleMaskArea.transform.localPosition = new Vector3(-0.0184f, 0.15f, -0.1f); __instance.matchInfoPlayersMaskArea.transform.localPosition = @@ -113,25 +178,174 @@ 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 wikiTab = Object.Instantiate( + __instance.settingsTabs[2], + __instance.settingsTabs[2].transform.parent); wikiTab.transform.FindChild("MaskArea")?.transform.localPosition = new Vector3(-0.0184f, 0.15f, -0.1f); + wikiTab.transform.GetAllChildren() + .First(x => x.name.Contains("BG_Gradient")).transform.localPosition = new Vector3(0, -0.62f, -5); advancedWikiTab = wikiTab.GetComponent(); + DisplayNormalRoleSettings(__instance, true); + + var searchIconButton = Object.Instantiate( + sortingOrderButton, + sortingOrderButton.transform.parent); + SearchIconButton = searchIconButton.GetComponent(); + SearchIconButton.name = "SearchIconButton"; + _searchIconIdle = SearchIconButton.inactiveSprites.GetComponent(); + _searchIconIdle.sprite = MiraAssets.SearchIconIdleSprite; + _searchIconHover = SearchIconButton.activeSprites.GetComponent(); + _searchIconHover.sprite = MiraAssets.SearchIconHoverSprite; + SearchIconButton.OnClick = new Button.ButtonClickedEvent(); + SearchIconButton.OnClick.AddListener(new Action(ClearSearchText)); + searchIconButton.transform.localPosition = new Vector3(-2.79f, 0.765f, -0.1f); + searchIconButton.transform.localScale = Vector3.one; + + 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; + } + + var newSorted = RolePanels + .OrderByDescending(child => + child.Value.GetTitle().Equals(text, StringComparison.OrdinalIgnoreCase)) + .ThenByDescending(child => child.Value.GetTitle().Contains( + text, + StringComparison.InvariantCultureIgnoreCase)) + .ThenBy(GetSortingOrder()); + + foreach (var pair in newSorted) + { + pair.Value.Panel.transform.SetAsLastSibling(); + } + + _rolesScroller.ScrollToTop(); + })); + searchBoxTmp.transform.localPosition = new Vector3(-1.438f, 0.756f, -0.2f); + + var playerButton = __instance.TabButtons[0]; + var playerCollider = playerButton.GetComponent(); + playerButton.transform.GetChild(0).gameObject.SetActive(false); + var playerBtnInactive = playerButton.inactiveSprites.GetComponent(); + playerBtnInactive.sprite = MiraAssets.WikiPlayersButtonIdleSprite; + playerBtnInactive.size = Vector2.one; + playerBtnInactive.transform.GetChild(0).gameObject.SetActive(false); + var playerBtnSelected = playerButton.selectedSprites.GetComponent(); + playerBtnSelected.sprite = MiraAssets.WikiPlayersButtonOpenSprite; + playerBtnSelected.size = Vector2.one; + playerBtnSelected.transform.GetChild(0).gameObject.SetActive(false); + var playerBtnHighlight = playerButton.activeSprites.GetComponent(); + playerBtnHighlight.sprite = MiraAssets.WikiPlayersButtonHoverSprite; + playerBtnHighlight.size = Vector2.one; + playerBtnHighlight.transform.GetChild(0).gameObject.SetActive(false); + + var settingButton = __instance.TabButtons[1]; + var settingsCollider = settingButton.GetComponent(); + settingButton.transform.GetChild(0).gameObject.SetActive(false); + var settingBtnInactive = settingButton.inactiveSprites.GetComponent(); + settingBtnInactive.sprite = MiraAssets.WikiSettingsButtonIdleSprite; + settingBtnInactive.size = Vector2.one; + settingBtnInactive.transform.GetChild(0).gameObject.SetActive(false); + var settingBtnSelected = settingButton.selectedSprites.GetComponent(); + settingBtnSelected.sprite = MiraAssets.WikiSettingsButtonOpenSprite; + settingBtnSelected.size = Vector2.one; + settingBtnSelected.transform.GetChild(0).gameObject.SetActive(false); + var settingBtnHighlight = settingButton.activeSprites.GetComponent(); + settingBtnHighlight.sprite = MiraAssets.WikiSettingsButtonHoverSprite; + settingBtnHighlight.size = Vector2.one; + settingBtnHighlight.transform.GetChild(0).gameObject.SetActive(false); + + var rolesButton = __instance.TabButtons[2]; + var rolesCollider = rolesButton.GetComponent(); + rolesButton.transform.GetChild(0).gameObject.SetActive(false); + var rolesBtnInactive = rolesButton.inactiveSprites.GetComponent(); + rolesBtnInactive.sprite = MiraAssets.WikiRolesButtonIdleSprite; + rolesBtnInactive.size = Vector2.one; + rolesBtnInactive.transform.GetChild(0).gameObject.SetActive(false); + var rolesBtnSelected = rolesButton.selectedSprites.GetComponent(); + rolesBtnSelected.sprite = MiraAssets.WikiRolesButtonOpenSprite; + rolesBtnSelected.size = Vector2.one; + rolesBtnSelected.transform.GetChild(0).gameObject.SetActive(false); + var rolesBtnHighlight = rolesButton.activeSprites.GetComponent(); + rolesBtnHighlight.sprite = MiraAssets.WikiRolesButtonHoverSprite; + rolesBtnHighlight.size = Vector2.one; + rolesBtnHighlight.transform.GetChild(0).gameObject.SetActive(false); + + playerCollider.size = settingsCollider.size = rolesCollider.size = new Vector2(0.8f, 0.74f); + playerCollider.offset = settingsCollider.offset = rolesCollider.offset = Vector2.zero; + playerButton.transform.localScale = settingButton.transform.localScale = + rolesButton.transform.localScale = new Vector3(0.7f, 0.7f, 1); + 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); } else { DisplayNormalRoleSettings(__instance, false); + ClearSearchText(); } } else if (__instance.HnSModeSettings.Count == 0) @@ -150,17 +364,166 @@ 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 readonly Dictionary RolePanels = []; private static Scroller advancedWikiTab; + 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 newSorted = sortMethod is SortingMethod.Alphabetical + ? RolePanels.OrderBy(GetSortingOrder()) + : RolePanels.OrderByDescending(GetSortingOrder()); + + foreach (var pair in newSorted) + { + pair.Value.Panel.transform.SetAsLastSibling(); + } + + _rolesScroller.ScrollToTop(); + } + + 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; + } + + ToggleRoleVisibility(); + var newSorted = sortMethod is SortingMethod.Alphabetical + ? RolePanels.OrderBy(GetSortingOrder()) + : RolePanels.OrderByDescending(GetSortingOrder()); + + foreach (var pair in newSorted) + { + pair.Value.Panel.transform.SetAsLastSibling(); + } + } + + 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 newSorted = sortMethod is SortingMethod.Alphabetical + ? RolePanels.OrderBy(GetSortingOrder()) + : RolePanels.OrderByDescending(GetSortingOrder()); + + foreach (var pair in newSorted) + { + pair.Value.Panel.transform.SetAsLastSibling(); + } + } + + 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 newSorted = sortMethod is SortingMethod.Alphabetical + ? RolePanels.OrderBy(GetSortingOrder()) + : RolePanels.OrderByDescending(GetSortingOrder()); + + foreach (var pair in newSorted) + { + pair.Value.Panel.transform.SetAsLastSibling(); + } + } + public static void DisplayNormalRoleSettings(MatchInfoGuide instance, bool reset) { + var inner = instance.settingsTabs[2].GetComponent().Inner; if (reset) { RolePanels.Clear(); @@ -198,67 +561,134 @@ public static class RoleGuidePatches { var panel = Object.Instantiate( instance.MatchInfoRolePanelPrefab, - instance.settingsTabs[2].GetComponent().Inner); + inner); + var amount = + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(roleBehaviour.Role); + var chance = + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(roleBehaviour.Role); + panel.SetPanel( + 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(), + roleBehaviour is ICustomRole custom + ? custom.ParentMod.MiraPlugin.GetAbbreviatedModName() + : "AU")); } } } advancedWikiTab?.gameObject.SetActive(false); - var num = 0; - foreach (var (roleData, panel) in RolePanels) + ToggleRoleVisibility(); + if (reset) { + instance.CreatePlayerEntries(); + } + } + + public static void ToggleRoleVisibility() + { + var num = 0; + foreach (var (roleData, holder) in RolePanels) + { + var panel = holder.Panel; 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)))) + var showVal = roleData is ICustomRole custom ? custom.ForceShowRoleOnWiki : null; + var forciblyShow = showVal switch + { + true => 2, + false => 1, + _ => 0, + }; + panel.SetPanel( + 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); - panel.SetPanel( - roleData, - amount, - chance); num++; } - if (num == 0) + var newSorted = sortMethod is SortingMethod.Alphabetical + ? RolePanels.OrderBy(GetSortingOrder()) + : RolePanels.OrderByDescending(GetSortingOrder()); + + foreach (var pair in newSorted) { - instance.rolesEnabledMessage.SetActive(true); + pair.Value.Panel.transform.SetAsLastSibling(); } + var instance = MatchInfoGuide.Instance; + instance.rolesEnabledMessage.SetActive(num == 0); + 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) + } + + public static Func, string> GetSortingOrder() + { + switch (sortOrder) { - instance.CreatePlayerEntries(); + 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)}"; } } @@ -335,7 +765,8 @@ public static class RoleGuidePatches else { advancedWikiTab.ScrollToTop(); - var titleTxt = role.GetRoleName() + $" ({TranslationController.Instance.GetString(role.TeamType is RoleTeamTypes.Crewmate ? StringNames.Crewmate : StringNames.Impostor)})"; + 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")) { @@ -364,18 +795,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 +824,40 @@ public static class RoleGuidePatches __instance.roleIcon.transform.localScale = new Vector3(4f, 4f, 1f); return false; } + + 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/Resources/Locale/en_US.xml b/MiraAPI/Resources/Locale/en_US.xml index be53733..26af113 100644 --- a/MiraAPI/Resources/Locale/en_US.xml +++ b/MiraAPI/Resources/Locale/en_US.xml @@ -18,6 +18,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. @@ -44,4 +45,13 @@ 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 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/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;