From 645d1dad1cf600d89385c55498936cb8970a2805 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Wed, 12 Mar 2025 18:01:58 -0400 Subject: [PATCH] Broken thing --- MiraAPI/MiraApiPlugin.cs | 9 ++- MiraAPI/Patches/MainMenuManagerPatch.cs | 26 +++++++ .../Patches/Options/LobbyViewPanePatches.cs | 8 +-- .../Options/NotificationPopperPatch.cs | 36 +++++++++- MiraAPI/Roles/CustomRoleConfiguration.cs | 5 +- MiraAPI/Roles/CustomRoleManager.cs | 16 +++++ MiraAPI/Utilities/Helpers.cs | 67 +++++++++++++++++++ 7 files changed, 157 insertions(+), 10 deletions(-) create mode 100644 MiraAPI/Patches/MainMenuManagerPatch.cs diff --git a/MiraAPI/MiraApiPlugin.cs b/MiraAPI/MiraApiPlugin.cs index 3380820..fa601ce 100644 --- a/MiraAPI/MiraApiPlugin.cs +++ b/MiraAPI/MiraApiPlugin.cs @@ -1,12 +1,19 @@ -using BepInEx; +using System; +using BepInEx; using BepInEx.Unity.IL2CPP; using HarmonyLib; +using Il2CppInterop.Runtime; using MiraAPI.PluginLoading; +using MiraAPI.Roles; +using MonoMod.Utils; using Reactor; using Reactor.Networking; using Reactor.Networking.Attributes; using Reactor.Utilities; +using TMPro; using UnityEngine; +using UnityEngine.ProBuilder; +using Delegate = Il2CppSystem.Delegate; namespace MiraAPI; diff --git a/MiraAPI/Patches/MainMenuManagerPatch.cs b/MiraAPI/Patches/MainMenuManagerPatch.cs new file mode 100644 index 0000000..a54efe0 --- /dev/null +++ b/MiraAPI/Patches/MainMenuManagerPatch.cs @@ -0,0 +1,26 @@ +using System; +using System.Linq; +using HarmonyLib; +using Il2CppInterop.Runtime; +using MiraAPI.Roles; +using MiraAPI.Utilities; +using TMPro; + +namespace MiraAPI.Patches; + +[HarmonyPatch(typeof(MainMenuManager))] + +public class MainMenuManagerPatch +{ + [HarmonyPostfix] + [HarmonyPatch(nameof(MainMenuManager.Start))] + public static void StartPatch() + { + TMP_Settings.defaultSpriteAsset.fallbackSpriteAssets.Add(CustomRoleManager.SpriteAsset); + /*TMP_Text.OnSpriteAssetRequest += DelegateSupport.ConvertDelegate>(new Func((index, str) => + { + var asset = Helpers.RegisteredSpriteAssets.First(x => x.name.Contains(str)); + return asset != null ? asset : null; + }));*/ + } +} diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index 89a6a24..c8e782a 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -330,8 +330,8 @@ public static class LobbyViewPanePatches numPerGame, chancePerGame, 61, - customRole.OptionsMenuColor, - customRole.Configuration.Icon.LoadAsset(), + customRole.RoleColor, + customRole.Configuration.Icon != null ? customRole.Configuration.Icon.LoadAsset() : MiraAssets.Empty.LoadAsset(), true); viewSettingsInfoPanelRoleVariant.iconSprite.transform.localScale = new Vector3(0.365f, 0.365f, 1f); viewSettingsInfoPanelRoleVariant.iconSprite.transform.localPosition = new Vector3(0.7144f, -0.028f, -2); @@ -340,7 +340,7 @@ public static class LobbyViewPanePatches viewSettingsInfoPanelRoleVariant.chanceTitle.color = viewSettingsInfoPanelRoleVariant.chanceBackground.color = viewSettingsInfoPanelRoleVariant.background.color = - customRole.OptionsMenuColor.FindAlternateColor(); + customRole.RoleColor.GetAlternateColor(); instance.settingsInfo.Add(viewSettingsInfoPanelRoleVariant.gameObject); num -= 0.664f; } @@ -418,7 +418,7 @@ public static class LobbyViewPanePatches role.StringName, maskLayer, role.TeamType == RoleTeamTypes.Crewmate, - customRole.Configuration.Icon.LoadAsset()); + customRole.Configuration.Icon != null ? customRole.Configuration.Icon.LoadAsset() : MiraAssets.Empty.LoadAsset()); viewPanel.header.icon.transform.localScale = new Vector3(0.465f, 0.465f, 1f); viewPanel.divider.material.SetInt(PlayerMaterial.MaskLayer, maskLayer); diff --git a/MiraAPI/Patches/Options/NotificationPopperPatch.cs b/MiraAPI/Patches/Options/NotificationPopperPatch.cs index 02efe2b..522bc2b 100644 --- a/MiraAPI/Patches/Options/NotificationPopperPatch.cs +++ b/MiraAPI/Patches/Options/NotificationPopperPatch.cs @@ -2,6 +2,10 @@ using HarmonyLib; using Il2CppSystem; using MiraAPI.Roles; +using Reactor.Utilities; +using UnityEngine; +using Action = System.Action; +using Object = Il2CppSystem.Object; namespace MiraAPI.Patches.Options; @@ -18,11 +22,14 @@ public static class NotificationPopperPatch [HarmonyArgument(3)] RoleTeamTypes teamType, [HarmonyArgument(4)] bool playSound) { - if (CustomRoleManager.CustomRoles.Values.FirstOrDefault(x=>x.StringName==key) is not ICustomRole customRole) + var role = CustomRoleManager.CustomRoles.Values.FirstOrDefault(x => x.StringName == key); + if (!role || role is not ICustomRole) { return true; } + Logger.Error("UPDATING ROLE TEXT"); + var text = teamType == RoleTeamTypes.Crewmate ? Palette.CrewmateSettingChangeText.ToTextColor() : Palette.ImpostorRed.ToTextColor(); @@ -30,6 +37,7 @@ public static class NotificationPopperPatch var item = TranslationController.Instance.GetString( StringNames.LobbyChangeSettingNotificationRole, string.Concat( + $"", "", text, TranslationController.Instance.GetString(key, Array.Empty()), @@ -39,7 +47,31 @@ public static class NotificationPopperPatch "" + roleChance + "%" ); - __instance.SettingsChangeMessageLogic(key, item, playSound); + CustomMessageLogic(__instance, role, item, playSound); return false; } + private static void CustomMessageLogic(NotificationPopper __instance, RoleBehaviour role, string item, bool playSound) + { + if (__instance.lastMessageKey == (int)role.StringName && __instance.activeMessages.Count > 0) + { + __instance.activeMessages.ToArray()[__instance.activeMessages.Count - 1].UpdateMessage(item); + } + else + { + __instance.lastMessageKey = (int)role.StringName; + var newMessage = UnityEngine.Object.Instantiate(__instance.notificationMessageOrigin, Vector3.zero, Quaternion.identity, __instance.transform); + newMessage.transform.localPosition = new Vector3(0f, 0f, -2f); + newMessage.SetUp(item, __instance.settingsChangeSprite, __instance.settingsChangeColor, new Action(delegate + { + __instance.OnMessageDestroy(newMessage); + })); + __instance.ShiftMessages(); + __instance.AddMessageToQueue(newMessage); + } + if (playSound) + { + SoundManager.Instance.PlaySoundImmediate(__instance.settingsChangeSound, false, 1f, 1f, null); + } + } } + diff --git a/MiraAPI/Roles/CustomRoleConfiguration.cs b/MiraAPI/Roles/CustomRoleConfiguration.cs index efaf444..85094a9 100644 --- a/MiraAPI/Roles/CustomRoleConfiguration.cs +++ b/MiraAPI/Roles/CustomRoleConfiguration.cs @@ -32,7 +32,6 @@ public record struct CustomRoleConfiguration TasksCountForProgress = role.Team is ModdedRoleTeams.Crewmate; HideSettings = roleBehaviour?.IsDead == true; ShowInFreeplay = roleBehaviour?.IsDead == false; - OptionsScreenshot = null; } /// @@ -58,12 +57,12 @@ public record struct CustomRoleConfiguration /// /// Gets or sets the Sprite used for the Role Options menu screenshot. /// - public LoadableAsset? OptionsScreenshot { get; set; } + public LoadableAsset? OptionsScreenshot { get; set; } = null; /// /// Gets or sets the Sprite used for the Role Icon. /// - public LoadableAsset Icon { get; set; } = MiraAssets.Empty; + public LoadableAsset? Icon { get; set; } = null; /// /// Gets or sets a value indicating whether the role is affected by light affectors on Airship. diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index a778a09..1484e26 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -13,6 +13,7 @@ using Reactor.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; +using UnityEngine.TextCore; using Object = UnityEngine.Object; namespace MiraAPI.Roles; @@ -24,7 +25,9 @@ public static class CustomRoleManager { internal static readonly Dictionary CustomRoles = []; internal static readonly Dictionary RoleIds = []; + internal static TMP_SpriteAsset SpriteAsset; + private static Dictionary _roleIcons = []; private static ushort _roleId = 100; private static ushort GetNextRoleId() @@ -58,6 +61,8 @@ public static class CustomRoleManager pluginInfo.CustomRoles.Add((ushort)role.Role, role); } + + SpriteAsset = Helpers.CreateSpriteAsset(_roleIcons, "RoleIcons"); } private static RoleBehaviour? RegisterRole(Type roleType, MiraPluginInfo parentMod) @@ -93,6 +98,17 @@ public static class CustomRoleManager roleBehaviour.MaxCount = customRole.Configuration.MaxRoleCount; roleBehaviour.RoleScreenshot = customRole.Configuration.OptionsScreenshot?.LoadAsset(); + if (customRole.Configuration.Icon != null) + { + var asset = customRole.Configuration.Icon.LoadAsset(); + if (asset != null) + { + roleBehaviour.RoleIconSolid = asset; + roleBehaviour.RoleIconWhite = asset; + _roleIcons.Add(roleBehaviour.NiceName, asset); + } + } + if (roleBehaviour.IsDead) { RoleManager.GhostRoles.Add(roleBehaviour.Role); diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index 6b3b79f..7e5e812 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -1,11 +1,15 @@ using System.Collections; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Text; +using HarmonyLib; using MiraAPI.Roles; +using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; +using UnityEngine.TextCore; using Object = UnityEngine.Object; namespace MiraAPI.Utilities; @@ -24,6 +28,69 @@ public static class Helpers return PlayerControl.AllPlayerControls.ToArray().Where(x => !x.Data.IsDead).ToList(); } + internal static readonly List RegisteredSpriteAssets = []; + + /// + /// Allows you to create a custom sprite asset. Normally used for TextMeshPro. + /// Ensure you set the TMP object's sprite asset to this one and use the sprite name tag. + /// https://docs.unity3d.com/Packages/com.unity.textmeshpro@4.0/manual/RichTextSprite.html + /// + /// The dictionary of sprites you want to add to the asset. String is the name of the sprite you'd like to use for rich text. + /// The name of the sprite asset. + /// The created sprite asset. + public static TMP_SpriteAsset CreateSpriteAsset(Dictionary sprites, string spriteAssetName) + { + var textures = sprites.Values.Select(x => x.texture).ToArray(); + var asset = ScriptableObject.CreateInstance(); + var image = new Texture2D(2048, 2048) { name = spriteAssetName }; + var rects = image.PackTextures(textures, 2); + + for (var i = 0; i < rects.Length; i++) + { + var rect = rects[i]; + var pair = sprites.ElementAt(i); + + var glyph = new TMP_SpriteGlyph + { + glyphRect = new GlyphRect + { + x = (int)(rect.x * image.width), + y = (int)(rect.y * image.height), + width = (int)(rect.width * image.width), + height = (int)(rect.height * image.height), + }, + metrics = new GlyphMetrics + { + width = (int)(rect.width * image.width), + height = (int)(rect.height * image.height), + horizontalBearingY = (int)(rect.height * image.height) * 0.75f, + horizontalBearingX = 0, + horizontalAdvance = (int)(rect.width * image.width), + }, + index = (uint)i, + sprite = pair.Value, + }; + + var character = new TMP_SpriteCharacter(0, asset, glyph) + { + name = pair.Key, + glyphIndex = (uint)i, + }; + + asset.spriteGlyphTable.Add(glyph); + asset.spriteCharacterTable.Add(character); + } + + asset.name = spriteAssetName; + asset.hashCode = TMP_TextUtilities.GetSimpleHashCode(asset.name); + asset.material = new Material(Shader.Find("TextMeshPro/Sprite")); + AccessTools.Property(asset.GetType(), "version").SetValue(asset, "1.1.0"); + asset.material.mainTexture = asset.spriteSheet = image; + asset.UpdateLookupTables(); + RegisteredSpriteAssets.Add(asset); + return asset.DontDestroyOnLoad().DontUnload(); + } + /// /// Returns an empty coroutine. ///