diff --git a/MiraAPI.Example/Roles/ChameleonRole.cs b/MiraAPI.Example/Roles/ChameleonRole.cs index c1d7779..f601551 100644 --- a/MiraAPI.Example/Roles/ChameleonRole.cs +++ b/MiraAPI.Example/Roles/ChameleonRole.cs @@ -1,6 +1,7 @@ using AmongUs.GameOptions; using MiraAPI.Patches.Stubs; using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; using Reactor.Utilities; using TMPro; using UnityEngine; @@ -20,6 +21,8 @@ public class ChameloenRole : CrewmateRole, ICustomRole public CustomRoleConfiguration Configuration => new(this) { OptionsScreenshot = ExampleAssets.Banner, + Icon = MiraAssets.CrewmateFile, + IconTmp = TmpSpriteUtils.CreateSpriteAsset(MiraAssets.CrewmateFile.LoadAsset(), "ApiExample.Role.Impostor.ChameleonRole"), IntroSound = CustomRoleUtils.GetIntroSound(RoleTypes.Shapeshifter), }; diff --git a/MiraAPI.Example/Roles/FreezerRole.cs b/MiraAPI.Example/Roles/FreezerRole.cs index bef24d2..5a7f4bd 100644 --- a/MiraAPI.Example/Roles/FreezerRole.cs +++ b/MiraAPI.Example/Roles/FreezerRole.cs @@ -1,4 +1,5 @@ using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; using UnityEngine; namespace MiraAPI.Example.Roles; @@ -13,6 +14,8 @@ public class FreezerRole : ImpostorRole, ICustomRole public CustomRoleConfiguration Configuration => new CustomRoleConfiguration(this) { + Icon = MiraAssets.ImpostorFile, + IconTmp = TmpSpriteUtils.CreateSpriteAsset(MiraAssets.ImpostorFile.LoadAsset(), "ApiExample.Role.Impostor.FreezerRole"), OptionsScreenshot = ExampleAssets.Banner, MaxRoleCount = 2, }; diff --git a/MiraAPI/Patches/Options/NotificationPopperPatch.cs b/MiraAPI/Patches/Options/NotificationPopperPatch.cs index b2c51e9..0ba8605 100644 --- a/MiraAPI/Patches/Options/NotificationPopperPatch.cs +++ b/MiraAPI/Patches/Options/NotificationPopperPatch.cs @@ -26,9 +26,11 @@ public static class NotificationPopperPatch var textColor = customRole.OptionsMenuColor.ToTextColor(); + var icon = customRole.Configuration.IconTmp ? $"" : string.Empty; var item = TranslationController.Instance.GetString( StringNames.LobbyChangeSettingNotificationRole, string.Concat( + icon, "", textColor, TranslationController.Instance.GetString(key, Array.Empty()), diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index d123161..67a4894 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -437,8 +437,7 @@ public static class RoleSettingMenuPatches roleSetting.Role.StringName, roleSetting.RoleMaxCount, roleSetting.RoleChance, - roleSetting.Role.TeamType, - false); + roleSetting.Role.TeamType); if (AmongUsClient.Instance.AmHost) { diff --git a/MiraAPI/Patches/TmpAwakePatch.cs b/MiraAPI/Patches/TmpAwakePatch.cs new file mode 100644 index 0000000..3cb9ee6 --- /dev/null +++ b/MiraAPI/Patches/TmpAwakePatch.cs @@ -0,0 +1,32 @@ +using System.Linq; +using HarmonyLib; +using MiraAPI.Utilities.Assets; +using TMPro; + +namespace MiraAPI.Patches; +[HarmonyPatch] +public static class TmpAwakePatch +{ + [HarmonyPatch(typeof(TextMeshPro), nameof(TextMeshPro.Awake))] + [HarmonyPostfix] + public static void TmpAwakePostfix(TextMeshPro __instance) + { + if (!TmpSpriteUtils.AssetHolder || !__instance.m_spriteAsset) + { + return; + } + + /* + if (__instance.m_spriteAsset.name == "RoleIcons") + { + Error($"Stencil Comp: {__instance.m_spriteAsset.material.GetFloat(ShaderUtilities.ID_StencilComp)}"); + Error($"Stencil ID: {__instance.m_spriteAsset.material.GetFloat(ShaderUtilities.ID_StencilID)}"); + Error($"Stencil Op: {__instance.m_spriteAsset.material.GetFloat(ShaderUtilities.ID_StencilOp)}"); + Error($"Stencil Write: {__instance.m_spriteAsset.material.GetFloat(ShaderUtilities.ID_StencilWriteMask)}"); + Error($"Stencil Read: {__instance.m_spriteAsset.material.GetFloat(ShaderUtilities.ID_StencilReadMask)}"); + Error($"Cull Mode: {__instance.m_spriteAsset.material.GetFloat(ShaderUtilities.ShaderTag_CullMode)}"); + }*/ + __instance.m_spriteAsset.fallbackSpriteAssets.Add(TmpSpriteUtils.AssetHolder); + __instance.UpdateMeshPadding(); + } +} diff --git a/MiraAPI/Resources/BlankSprite.png b/MiraAPI/Resources/BlankSprite.png new file mode 100644 index 0000000..9523fc8 Binary files /dev/null and b/MiraAPI/Resources/BlankSprite.png differ diff --git a/MiraAPI/Roles/CustomRoleConfiguration.cs b/MiraAPI/Roles/CustomRoleConfiguration.cs index 819eb1c..f756fed 100644 --- a/MiraAPI/Roles/CustomRoleConfiguration.cs +++ b/MiraAPI/Roles/CustomRoleConfiguration.cs @@ -3,6 +3,7 @@ using System; using Il2CppInterop.Runtime.Attributes; using MiraAPI.Patches.Freeplay; using MiraAPI.Utilities.Assets; +using TMPro; using UnityEngine; namespace MiraAPI.Roles; @@ -87,6 +88,12 @@ public record struct CustomRoleConfiguration [HideFromIl2Cpp] public LoadableAsset? Icon { get; set; } = null; + /// + /// Gets or sets the for the Role Icon. + /// + [HideFromIl2Cpp] + public TMP_SpriteAsset IconTmp { get; set; } + /// /// Gets or sets the Intro sound for the Role. /// diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 5b72650..1915949 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -5,6 +5,7 @@ using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.PluginLoading; using Reactor.Utilities; +using TMPro; using UnityEngine; namespace MiraAPI.Roles; diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs index 8ae8986..10bceda 100644 --- a/MiraAPI/Utilities/Assets/MiraAssets.cs +++ b/MiraAPI/Utilities/Assets/MiraAssets.cs @@ -134,4 +134,6 @@ public static class MiraAssets /// Gets the sprite used for timed modifier file in TaskAdderGame. /// public static LoadableResourceAsset TimedModifierFile { get; } = new("MiraAPI.Resources.TimedModifierFile.png"); + + internal static LoadableResourceAsset BlankSprite { get; } = new("MiraAPI.Resources.BlankSprite.png"); } diff --git a/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs b/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs new file mode 100644 index 0000000..8256eb9 --- /dev/null +++ b/MiraAPI/Utilities/Assets/TmpSpriteUtils.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using Reactor.Utilities.Extensions; +using TMPro; +using UnityEngine; + +namespace MiraAPI.Utilities.Assets; +public static class TmpSpriteUtils +{ + private static Shader _spriteShader; + private static Dictionary LoadedSprites = []; + public static TMP_SpriteAsset AssetHolder; + public static TMP_SpriteAsset CreateSpriteAsset(Sprite sprite, string assetName) + { + return CreateSpriteAsset(sprite.texture, sprite.rect, assetName); + } + public static TMP_SpriteAsset CreateSpriteAsset(Texture2D sourceTexture, Rect rect, string assetName) + { + if (LoadedSprites.TryGetValue(assetName, out var existingAsset)) + { + return existingAsset; + } + + if (!AssetHolder) + { + AssetHolder = ScriptableObject.CreateInstance(); + AssetHolder.DontUnload().DontDestroy(); + AssetHolder.fallbackSpriteAssets = new(); + } + TMP_SpriteAsset spriteAsset = ScriptableObject.CreateInstance(); + spriteAsset.name = assetName; + spriteAsset.spriteSheet = sourceTexture; + + if (!_spriteShader) + { + _spriteShader = Shader.Find("TextMeshPro/Sprite"); + } + + var material = new Material(_spriteShader) + { + name = assetName + " Material", + }; + material.SetTexture(ShaderUtilities.ID_MainTex, sourceTexture); + material.SetFloat(ShaderUtilities.ID_StencilComp, 0); + material.SetFloat(ShaderUtilities.ID_StencilID, 0); + material.SetFloat(ShaderUtilities.ID_StencilOp, 0); + material.SetFloat(ShaderUtilities.ID_StencilWriteMask, 255); + material.SetFloat(ShaderUtilities.ID_StencilReadMask, 255); + material.SetFloat(ShaderUtilities.ShaderTag_CullMode, 0); + spriteAsset.material = material; + + spriteAsset.spriteInfoList = new Il2CppSystem.Collections.Generic.List(); + + AddSpriteToAsset(spriteAsset, rect, assetName); + + spriteAsset.DontUnload().DontDestroy(); + spriteAsset.UpdateLookupTables(); + spriteAsset.fallbackSpriteAssets = new(); + AssetHolder.fallbackSpriteAssets.Add(spriteAsset); + AssetHolder.UpdateLookupTables(); + + LoadedSprites.Add(assetName, spriteAsset); + return spriteAsset; + } + + private static void AddSpriteToAsset(TMP_SpriteAsset spriteAsset, Rect rect, string spriteName) + { + TMP_Sprite newSprite = new TMP_Sprite + { + name = spriteName, + hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteName), + x = 0, + y = 0, + width = rect.width, + height = rect.height, + xOffset = -(rect.width / 3), + yOffset = rect.height / 1.25f, + xAdvance = rect.width, + scale = 1, + }; + + spriteAsset.spriteInfoList.Add(newSprite); + } +}