Merge branch 'dev' into more-colors

This commit is contained in:
AS authored and GitHub committed 2026-10-04 02:51:44 -04:00
commit 7deaf5e963
48 files changed
+988 -121

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@
<ItemGroup>
<PackageReference Include="Reactor" Version="2.5.0-ci.371" />
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.735" Private="false" ExcludeAssets="runtime;native" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2026.8.18" PrivateAssets="all" />
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2026.9.29" PrivateAssets="all" />
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" />
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.1.0-rc.1" PrivateAssets="all" ExcludeAssets="runtime" />
@@ -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<Color>(Palette.LightBlue));
Player.cosmetics.SetOutline(true, new Il2CppSystem.Nullable<Color>(Palette.LightBlue));
}
}
+1 -1
View File
@@ -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<uint, StringNames> Values = new()
{
+21
View File
@@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace MiraAPI.GameOptions;
/// <summary>
/// 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.
/// </summary>
public interface ISummarizedOptions
{
/// <summary>
/// Option string keys (StringNames) to omit from the wiki options list (e.g., a large set of related options).
/// </summary>
IReadOnlySet<StringNames> WikiHiddenOptionKeys { get; }
/// <summary>
/// Summary lines to insert when the first hidden option would have appeared.
/// Lines should already be formatted as "Title: Value".
/// </summary>
IEnumerable<string> GetWikiOptionSummaryLines();
}
@@ -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<int>
data.Title = StringName;
data.Type = global::OptionTypes.Player;
Values = new List<NetworkedPlayerInfo>();
Values = new();
AllowNone = allowNone;
Value = DefaultValue;
}
@@ -60,17 +60,16 @@ public class ModdedPlayerOption : ModdedOption<int>
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<OptionBehaviour>)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<int>
/// Returns a player ran through the filter.
/// </summary>
/// <returns>A list of filtered players.</returns>
public System.Collections.Generic.List<NetworkedPlayerInfo> GetFilteredPlayers()
public List<NetworkedPlayerInfo> GetFilteredPlayers()
{
return [.. GameData.Instance.AllPlayers.ToArray().Where(x => PlayerFilter?.Invoke(x) ?? true)];
}
@@ -23,10 +23,18 @@ public static class VanillaButtonPatches
{
playerTarget = role.Cast<TrackerRole>().currentTarget;
}
else if (role.Role is RoleTypes.Detective)
{
playerTarget = role.Cast<DetectiveRole>().currentTarget;
}
else if (role.Role is RoleTypes.GuardianAngel)
{
playerTarget = role.Cast<GuardianAngelRole>().currentTarget;
}
else if (role.Role is RoleTypes.SpiritGuide)
{
playerTarget = role.Cast<SpiritGuideRole>().currentTarget;
}
else if (role.Role is RoleTypes.Engineer)
{
ventTarget = role.Cast<EngineerRole>().currentTarget;
@@ -53,6 +61,10 @@ public static class VanillaButtonPatches
{
playerTarget = role.Cast<DetectiveRole>().currentTarget;
}
else if (role.Role is RoleTypes.SpiritGuide)
{
playerTarget = role.Cast<SpiritGuideRole>().currentTarget;
}
var genericEvent = new VanillaButtonClickEvent(__instance, playerTarget);
MiraEventManager.InvokeEvent(genericEvent);
+3 -4
View File
@@ -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<bool>)(() => GameManager.Instance != null));
yield return new WaitUntil((Il2CppSystem.Func<bool>)(() => 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<TextTranslatorTMP>());
modelTextClone.GetComponent<TextMeshPro>().text = "Gamemode";
modelTextClone.GetComponent<TextMeshPro>().text = MiraLocaleManager.Get("MiraApi.Gamemode", "Gamemode");
var gmText = modeValue!.Find("GameModeText").gameObject;
var gmTextClone = Object.Instantiate(gmText, modeValue);
gmText.SetActive(false);
@@ -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<CategoryHeaderMasked> VanillaHeaders = new();
private static readonly List<OptionBehaviour> VanillaOptions = new();
private static readonly List<OptionBehaviour> MainOptions = new();
private static readonly System.Collections.Generic.Dictionary<AbstractGameMode, System.Collections.Generic.List<AbstractOptionGroup>> GameModeGroups = [];
private static readonly System.Collections.Generic.Dictionary<AbstractGameMode, System.Collections.Generic.List<OptionBehaviour>> GameModeOptions = [];
private static readonly System.Collections.Generic.Dictionary<AbstractGameMode, System.Collections.Generic.List<CategoryHeaderMasked>> GameModeHeaders = [];
private static readonly Dictionary<AbstractGameMode, List<AbstractOptionGroup>> GameModeGroups = [];
private static readonly Dictionary<AbstractGameMode, List<OptionBehaviour>> GameModeOptions = [];
private static readonly Dictionary<AbstractGameMode, List<CategoryHeaderMasked>> 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<OptionBehaviour>();
__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<OptionBehaviour>();
var categoryHeaders = new System.Collections.Generic.List<CategoryHeaderMasked>();
var optionBehaviours = new List<OptionBehaviour>();
var categoryHeaders = new List<CategoryHeaderMasked>();
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<OptionBehaviour> optionBehaviours, ref System.Collections.Generic.List<CategoryHeaderMasked> categoryHeaders)
private static void CreateGroup(GameOptionsMenu menu, AbstractOptionGroup group, Transform container, ref float num, ref List<OptionBehaviour> optionBehaviours, ref List<CategoryHeaderMasked> categoryHeaders)
{
var categoryHeaderMasked = Object.Instantiate(
menu.categoryHeaderOrigin,
+3 -4
View File
@@ -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<Object>()).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<Object>()).Translate();
__instance.ValueText.text = TranslationController.Instance.GetString(__instance.Values[__instance.Value], Array.Empty<Object>());
__instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title).Translate();
__instance.ValueText.text = TranslationController.Instance.GetString(__instance.Values[__instance.Value]);
return false;
}
@@ -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();
}
+517 -45
View File
@@ -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<SpriteRenderer>()
.maskInteraction =
__instance.matchInfoSettingsMaskArea.transform.parent.GetAllChildren()
.First(x => x.name.Contains("BG_Gradient")).GetComponent<SpriteRenderer>()
settingsenuGradient.GetComponent<SpriteRenderer>()
.maskInteraction =
__instance.MatchInfoRoleMaskArea.transform.parent.GetAllChildren()
.First(x => x.name.Contains("BG_Gradient")).GetComponent<SpriteRenderer>()
roleMenuGradient.GetComponent<SpriteRenderer>()
.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<Scroller>();
}
@@ -98,12 +119,56 @@ public static class RoleGuidePatches
titleText.text = TranslationController.Instance.GetString(StringNames.MatchInfoGuideTitle);
}
if (GameManager.Instance.TryCast<NormalGameManager>() != null)
var regGame = GameManager.Instance.TryCast<NormalGameManager>() != null;
if (regGame)
{
if (__instance.NormalModeSettings.Count == 0)
{
var sortingOrderButton = Object.Instantiate(
HudManager.Instance.SettingsButton,
__instance.TabButtons[2].transform.parent);
SearchSortingOrderButton = sortingOrderButton.GetComponent<PassiveButton>();
SearchSortingOrderButton.name = "SearchSortingOrderButton";
_searchSortingOrderIdle = SearchSortingOrderButton.inactiveSprites.GetComponent<SpriteRenderer>();
_searchSortingOrderIdle.sprite = MiraAssets.SortingAzIdleSprite;
_searchSortingOrderHover = SearchSortingOrderButton.activeSprites.GetComponent<SpriteRenderer>();
_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<PassiveButton>();
SearchSortingGroupButton.name = "SearchSortingGroupButton";
_searchSortingGroupIdle = SearchSortingGroupButton.inactiveSprites.GetComponent<SpriteRenderer>();
_searchSortingGroupIdle.sprite = MiraAssets.GroupNoneIdleSprite;
_searchSortingGroupHover = SearchSortingGroupButton.activeSprites.GetComponent<SpriteRenderer>();
_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<PassiveButton>();
SearchSortingFilterButton.name = "SearchSortingFilterButton";
_searchSortingFilterIdle = SearchSortingFilterButton.inactiveSprites.GetComponent<SpriteRenderer>();
_searchSortingFilterIdle.sprite = MiraAssets.VisibleOnIdleSprite;
_searchSortingFilterHover = SearchSortingFilterButton.activeSprites.GetComponent<SpriteRenderer>();
_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<SpriteRenderer>()
.maskInteraction =
__instance.matchInfoSettingsMaskArea.transform.parent.GetAllChildren()
.First(x => x.name.Contains("BG_Gradient")).GetComponent<SpriteRenderer>()
settingsenuGradient.GetComponent<SpriteRenderer>()
.maskInteraction =
__instance.MatchInfoRoleMaskArea.transform.parent.GetAllChildren()
.First(x => x.name.Contains("BG_Gradient")).GetComponent<SpriteRenderer>()
roleMenuGradient.GetComponent<SpriteRenderer>()
.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<Scroller>();
DisplayNormalRoleSettings(__instance, true);
var searchIconButton = Object.Instantiate(
sortingOrderButton,
sortingOrderButton.transform.parent);
SearchIconButton = searchIconButton.GetComponent<PassiveButton>();
SearchIconButton.name = "SearchIconButton";
_searchIconIdle = SearchIconButton.inactiveSprites.GetComponent<SpriteRenderer>();
_searchIconIdle.sprite = MiraAssets.SearchIconIdleSprite;
_searchIconHover = SearchIconButton.activeSprites.GetComponent<SpriteRenderer>();
_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<MatchInfoGuideTabButton>();
var tmpText = searchButton.transform.GetChild(0).GetComponent<TextMeshPro>();
tmpText.GetComponent<TextTranslatorTMP>().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<SpriteRenderer>().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<PassiveButton>();
button.OnUp = true;
searchBoxTmp = searchBox.AddComponent<TextBoxTMP>();
searchBoxTmp.outputText = tmpText;
button.OnClick.AddListener((UnityAction)(() => { searchBoxTmp.GiveFocus(); }));
button.OnMouseOver = new UnityEvent();
button.OnMouseOut = new UnityEvent();
_rolesScroller = __instance.settingsTabs[2].GetComponent<Scroller>();
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<BoxCollider2D>();
playerButton.transform.GetChild(0).gameObject.SetActive(false);
var playerBtnInactive = playerButton.inactiveSprites.GetComponent<SpriteRenderer>();
playerBtnInactive.sprite = MiraAssets.WikiPlayersButtonIdleSprite;
playerBtnInactive.size = Vector2.one;
playerBtnInactive.transform.GetChild(0).gameObject.SetActive(false);
var playerBtnSelected = playerButton.selectedSprites.GetComponent<SpriteRenderer>();
playerBtnSelected.sprite = MiraAssets.WikiPlayersButtonOpenSprite;
playerBtnSelected.size = Vector2.one;
playerBtnSelected.transform.GetChild(0).gameObject.SetActive(false);
var playerBtnHighlight = playerButton.activeSprites.GetComponent<SpriteRenderer>();
playerBtnHighlight.sprite = MiraAssets.WikiPlayersButtonHoverSprite;
playerBtnHighlight.size = Vector2.one;
playerBtnHighlight.transform.GetChild(0).gameObject.SetActive(false);
var settingButton = __instance.TabButtons[1];
var settingsCollider = settingButton.GetComponent<BoxCollider2D>();
settingButton.transform.GetChild(0).gameObject.SetActive(false);
var settingBtnInactive = settingButton.inactiveSprites.GetComponent<SpriteRenderer>();
settingBtnInactive.sprite = MiraAssets.WikiSettingsButtonIdleSprite;
settingBtnInactive.size = Vector2.one;
settingBtnInactive.transform.GetChild(0).gameObject.SetActive(false);
var settingBtnSelected = settingButton.selectedSprites.GetComponent<SpriteRenderer>();
settingBtnSelected.sprite = MiraAssets.WikiSettingsButtonOpenSprite;
settingBtnSelected.size = Vector2.one;
settingBtnSelected.transform.GetChild(0).gameObject.SetActive(false);
var settingBtnHighlight = settingButton.activeSprites.GetComponent<SpriteRenderer>();
settingBtnHighlight.sprite = MiraAssets.WikiSettingsButtonHoverSprite;
settingBtnHighlight.size = Vector2.one;
settingBtnHighlight.transform.GetChild(0).gameObject.SetActive(false);
var rolesButton = __instance.TabButtons[2];
var rolesCollider = rolesButton.GetComponent<BoxCollider2D>();
rolesButton.transform.GetChild(0).gameObject.SetActive(false);
var rolesBtnInactive = rolesButton.inactiveSprites.GetComponent<SpriteRenderer>();
rolesBtnInactive.sprite = MiraAssets.WikiRolesButtonIdleSprite;
rolesBtnInactive.size = Vector2.one;
rolesBtnInactive.transform.GetChild(0).gameObject.SetActive(false);
var rolesBtnSelected = rolesButton.selectedSprites.GetComponent<SpriteRenderer>();
rolesBtnSelected.sprite = MiraAssets.WikiRolesButtonOpenSprite;
rolesBtnSelected.size = Vector2.one;
rolesBtnSelected.transform.GetChild(0).gameObject.SetActive(false);
var rolesBtnHighlight = rolesButton.activeSprites.GetComponent<SpriteRenderer>();
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<RoleBehaviour, MatchInfoRolePanel> RolePanels = [];
private static readonly Dictionary<RoleBehaviour, DetailedPanel> 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<Scroller>().Inner;
if (reset)
{
RolePanels.Clear();
@@ -198,67 +561,134 @@ public static class RoleGuidePatches
{
var panel = Object.Instantiate(
instance.MatchInfoRolePanelPrefab,
instance.settingsTabs[2].GetComponent<Scroller>().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<BoxCollider2D>();
collider.size = new Vector2(0.13f, 0.13f);
collider.offset = new Vector2(0, 0);
var passiveButton = panel.roleIcon.gameObject.AddComponent<PassiveButton>();
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<KeyValuePair<RoleBehaviour, DetailedPanel>, 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 = $"<size=60%>{customRole.RoleFactionTitle}</size>\n" + customRole.RoleMedDescription;
__instance.roleDescription.text =
$"<size=60%>{role.GetCategoryTitle()}</size>\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 = $"<size=60%>{TranslationController.Instance.GetString(role.TeamType is RoleTeamTypes.Crewmate ? StringNames.Crewmate : StringNames.Impostor)}</size>\n" + role.BlurbMed;
__instance.roleDescription.text = $"<size=60%>{role.GetCategoryTitle()}</size>\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
}
+2 -3
View File
@@ -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>(Color.clear));
__instance.currentTarget.cosmetics.SetOutline(false, new Il2CppSystem.Nullable<Color>(Color.clear));
}
__instance.currentTarget = target;
if (__instance.currentTarget)
{
__instance.currentTarget.cosmetics.SetOutline(true, new Nullable<Color>(customRole.Configuration.KillButtonOutlineColor));
__instance.currentTarget.cosmetics.SetOutline(true, new Il2CppSystem.Nullable<Color>(customRole.Configuration.KillButtonOutlineColor));
__instance.SetEnabled();
return false;
}
@@ -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<NetworkedPlayerInfo> players,
Il2CppSystem.Collections.Generic.List<NetworkedPlayerInfo> 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<NetworkedPlayerInfo>();
var newImpostors = new Il2CppSystem.Collections.Generic.List<NetworkedPlayerInfo>();
// Specified Seeker
if (__instance.hnsManager.LogicOptionsHnS.HasImpostorPlayerID() &&
@@ -113,7 +113,7 @@ public static class LogicRoleSelectionHnsPatch
return false;
}
public static void AssignRolesFromList(List<NetworkedPlayerInfo> players, int teamMax, List<RoleTypes> roleList, ref int rolesAssigned)
public static void AssignRolesFromList(Il2CppSystem.Collections.Generic.List<NetworkedPlayerInfo> players, int teamMax, List<RoleTypes> 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<RoleBehaviour> guaranteedRoles,
IEnumerable<RoleBehaviour> guaranteedRoles,
IGameOptions opts,
List<RoleTypes> list)
{
@@ -146,7 +146,7 @@ public static class LogicRoleSelectionHnsPatch
}
}
private static void AddPotentialRoles(System.Collections.Generic.IEnumerable<RoleBehaviour> source, IGameOptions opts, List<RoleTypes> list)
private static void AddPotentialRoles(IEnumerable<RoleBehaviour> source, IGameOptions opts, List<RoleTypes> list)
{
var roleOptions = opts.RoleOptions;
var potentialRoles = source.Where(x => !x.IsDead).Select(role => new RoleManager.RoleAssignmentData(
+2 -3
View File
@@ -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
/// <param name="instance">The <see cref="RoleBehaviour"/> object.</param>
/// <param name="taskStringBuilder">The <see cref="StringBuilder"/> to append the task hint to.</param>
[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
}
+10
View File
@@ -18,6 +18,7 @@
<string name="MiraApi.CustomCategory2">Custom Category 2</string>
<string name="MiraApi.ModifierMenuDescription">Configure modifiers and their settings here!</string>
<string name="MiraApi.CustomOptionMenuDescription">Apply game settings for this mod!</string>
<string name="MiraApi.Gamemode">Gamemode</string>
<string name="MiraApi.Gamemode.Classic">Classic</string>
<string name="MiraApi.Gamemode.Classic.Description">The classic Among Us experience!
Find the Impostors or deceive the Crew.</string>
@@ -44,4 +45,13 @@ Outlive the timer or eliminate the crew.</string>
<string name="MiraApi.ModifierDisplay.Page">Page</string>
<string name="MiraApi.Page">Page</string>
<string name="MiraApi.Main">Main</string>
<string name="MiraApi.AbilityType.Basic">Ability</string>
<string name="MiraApi.AbilityType.Indirect">Indirect Ability</string>
<string name="MiraApi.AbilityType.Interaction">Interaction Ability</string>
<string name="MiraApi.AbilityType.Radius">Radius Ability</string>
<string name="MiraApi.AbilityType.Menu">Menu Ability</string>
<string name="MiraApi.AbilityType.Meeting">Meeting Ability</string>
<string name="MiraApi.AbilityType.SelectiveMeeting">Selective Meeting Ability</string>
<string name="MiraApi.AbilityType.MeetingMenu">Meeting Menu Ability</string>
<string name="Options">Options</string>
</resources>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

+53 -3
View File
@@ -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");
/// <summary>
/// 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.
/// </summary>
string RoleWikiDescription => MiraLocaleManager.Get(RoleWikiDescriptionLocale, RoleLongDescription);
string RoleWikiDescription => MiraLocaleManager.Get(RoleWikiDescriptionLocale, RoleLongDescription) +
Helpers.GetOptionsText(GetType());
/// <summary>
/// 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<GridLayoutGroup>();
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.
/// </summary>
Func<bool> VisibleInSettings => () => true;
/// <summary>
/// Gets the list of abilities or other similar information to display below the text of a role's wiki page.
/// </summary>
[HideFromIl2Cpp]
public List<AdvancedWikiAbilityDescription> WikiAbilities => [];
}
public record struct AdvancedWikiAbilityDescription(string Name, string AbilityType, string Description, LoadableAsset<Sprite> Icon);
+145
View File
@@ -160,5 +160,150 @@ public static class MiraAssets
/// </summary>
public static LoadableResourceAsset ChatOpenSprite { get; } = new("MiraAPI.Resources.NormalChatOpen.png");
/// <summary>
/// Gets the sprite used for players button while hovering.
/// </summary>
public static LoadableResourceAsset WikiPlayersButtonHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarPlayersButtonHover.png");
/// <summary>
/// Gets the sprite used for the players button while hovering.
/// </summary>
public static LoadableResourceAsset WikiPlayersButtonIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarPlayersButtonIdle.png");
/// <summary>
/// Gets the sprite used for the players button while hovering.
/// </summary>
public static LoadableResourceAsset WikiPlayersButtonOpenSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarPlayersButtonOpen.png");
/// <summary>
/// Gets the sprite used for the settings button while hovering.
/// </summary>
public static LoadableResourceAsset WikiSettingsButtonHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarSettingsButtonHover.png");
/// <summary>
/// Gets the sprite used for the settings button while hovering.
/// </summary>
public static LoadableResourceAsset WikiSettingsButtonIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarSettingsButtonIdle.png");
/// <summary>
/// Gets the sprite used for the settings button while hovering.
/// </summary>
public static LoadableResourceAsset WikiSettingsButtonOpenSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarSettingsButtonOpen.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset WikiRolesButtonHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarRolesButtonHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset WikiRolesButtonIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarRolesButtonIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset WikiRolesButtonOpenSprite { get; } = new("MiraAPI.Resources.Wiki.SidebarRolesButtonOpen.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset GroupCountHoverSprite { get; } = new("MiraAPI.Resources.Wiki.GroupCountHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset GroupCountIdleSprite { get; } = new("MiraAPI.Resources.Wiki.GroupCountIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset GroupFactionHoverSprite { get; } = new("MiraAPI.Resources.Wiki.GroupFactionHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset GroupFactionIdleSprite { get; } = new("MiraAPI.Resources.Wiki.GroupFactionIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset GroupNoneHoverSprite { get; } = new("MiraAPI.Resources.Wiki.GroupNoneHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset GroupNoneIdleSprite { get; } = new("MiraAPI.Resources.Wiki.GroupNoneIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset SearchClearHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SearchClearHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset SearchClearIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SearchClearIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset SearchIconHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SearchIconHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset SearchIconIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SearchIconIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset SortingAzHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SortingAzHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset SortingAzIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SortingAzIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset SortingZaHoverSprite { get; } = new("MiraAPI.Resources.Wiki.SortingZaHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset SortingZaIdleSprite { get; } = new("MiraAPI.Resources.Wiki.SortingZaIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset VisibleAllHoverSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleAllHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset VisibleAllIdleSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleAllIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset VisibleOffHoverSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleOffHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset VisibleOffIdleSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleOffIdle.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset VisibleOnHoverSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleOnHover.png");
/// <summary>
/// Gets the sprite used for the roles button while hovering.
/// </summary>
public static LoadableResourceAsset VisibleOnIdleSprite { get; } = new("MiraAPI.Resources.Wiki.VisibleOnIdle.png");
internal static LoadableResourceAsset BlankSprite { get; } = new("MiraAPI.Resources.BlankSprite.png");
}
+194 -28
View File
@@ -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;
/// </summary>
public static class Helpers
{
public static ReadOnlyCollection<IModdedOption>? GetModdedOptionsForType(Type classType)
{
var optionGroups =
AccessTools.Field(typeof(ModdedOptionsManager), "Groups").GetValue(null) as List<AbstractOptionGroup>;
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<StringNames>? hiddenKeys;
try
{
var optionGroups =
AccessTools.Field(typeof(ModdedOptionsManager), "Groups").GetValue(null) as List<AbstractOptionGroup>;
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<size=50%> \n</size><b><color=#29D7B5>{MiraLocaleManager.Get("Options")}</color></b>");
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<Enum> 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();
}
/// <summary>
/// Get all living players.
/// </summary>
@@ -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<Collider2D>();
Physics2D.OverlapCircle(source, radius, filter, results);
return [.. results.ToArray()
.Where(collider2D => collider2D.CompareTag("DeadBody"))
.Select(collider2D => collider2D.GetComponent<DeadBody>())];
return
[
.. results.ToArray()
.Where(collider2D => collider2D.CompareTag("DeadBody"))
.Select(collider2D => collider2D.GetComponent<DeadBody>())
];
}
/// <summary>
@@ -299,9 +460,12 @@ public static class Helpers
{
var results = new CppCollections.List<Collider2D>();
Physics2D.OverlapCircle(source, radius, filter, results);
return [.. results.ToArray()
.Where(collider2D => colliderTag == null || collider2D.CompareTag(colliderTag))
.Select(collider2D => collider2D.GetComponent<T>())];
return
[
.. results.ToArray()
.Where(collider2D => colliderTag == null || collider2D.CompareTag(colliderTag))
.Select(collider2D => collider2D.GetComponent<T>())
];
}
/// <summary>
@@ -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
];
}
/// <summary>
@@ -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;
}
+1 -4
View File
@@ -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<Il2CppSystem.Object>());
MeetingHud.Instance.TitleText.text = TranslationController.Instance.GetString(StringNames.MeetingVotingResults);
var delays = new Dictionary<byte, int>();
var num = 0;