From a26655f08f28752909109fc057524d6cfb98354c Mon Sep 17 00:00:00 2001 From: D1GQ Date: Mon, 20 Oct 2025 22:01:23 -0500 Subject: [PATCH] Add outfit presets --- BAUPlugin.cs | 4 +- BetterAmongUs.csproj | 2 +- Data/BetterDataManager.cs | 3 +- Data/{ => Json}/AbstractJsonFile.cs | 8 +- Data/{ => Json}/BetterDataFile.cs | 9 +- Data/{ => Json}/BetterGameSettingsFile.cs | 2 +- Data/Json/OutfitData.cs | 97 ++++++++++++++++ Data/Replay/Events/IReplayEvent.cs | 5 +- Data/Replay/Replay.cs | 2 +- Helpers/ExtendedPlayerInfo.cs | 2 +- Helpers/ObjectHelper.cs | 12 ++ Modules/UserDataLoader.cs | 2 +- {Items => Network/Configs}/BannedUserData.cs | 8 +- {Items => Network/Configs}/UserData.cs | 14 +-- Network/GitHubFile.cs | 3 - Patches/Client/ClientPatch.cs | 2 +- Patches/Client/MainMenuPatch.cs | 10 +- Patches/Gameplay/UI/IntroCutscenePatch.cs | 41 ++++--- Patches/Gameplay/UI/PlayerTabPatch.cs | 116 +++++++++++++++++++ 19 files changed, 297 insertions(+), 45 deletions(-) rename Data/{ => Json}/AbstractJsonFile.cs (96%) rename Data/{ => Json}/BetterDataFile.cs (80%) rename Data/{ => Json}/BetterGameSettingsFile.cs (99%) create mode 100644 Data/Json/OutfitData.cs rename {Items => Network/Configs}/BannedUserData.cs (81%) rename {Items => Network/Configs}/UserData.cs (80%) create mode 100644 Patches/Gameplay/UI/PlayerTabPatch.cs diff --git a/BAUPlugin.cs b/BAUPlugin.cs index 512c619..4eaa94f 100644 --- a/BAUPlugin.cs +++ b/BAUPlugin.cs @@ -3,11 +3,12 @@ using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BetterAmongUs.Data; +using BetterAmongUs.Data.Json; using BetterAmongUs.Helpers; -using BetterAmongUs.Items; using BetterAmongUs.Items.Attributes; using BetterAmongUs.Modules; using BetterAmongUs.Network; +using BetterAmongUs.Network.Configs; using BetterAmongUs.Patches.Gameplay.UI.Settings; using HarmonyLib; using Il2CppInterop.Runtime.Injection; @@ -100,6 +101,7 @@ internal class BAUPlugin : BasePlugin GameSettingMenuPatch.SetupSettings(true); FileChecker.Initialize(); InstanceAttribute.RegisterAll(); + OutfitData.Init(); if (File.Exists(Path.Combine(BetterDataManager.filePathFolder, "better-log.txt"))) File.WriteAllText(Path.Combine(BetterDataManager.filePathFolder, "better-previous-log.txt"), File.ReadAllText(Path.Combine(BetterDataManager.filePathFolder, "better-log.txt"))); diff --git a/BetterAmongUs.csproj b/BetterAmongUs.csproj index c95042d..dcfd561 100644 --- a/BetterAmongUs.csproj +++ b/BetterAmongUs.csproj @@ -12,7 +12,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Data/BetterDataManager.cs b/Data/BetterDataManager.cs index b2625a1..46879af 100644 --- a/Data/BetterDataManager.cs +++ b/Data/BetterDataManager.cs @@ -1,4 +1,5 @@ -using BetterAmongUs.Helpers; +using BetterAmongUs.Data.Json; +using BetterAmongUs.Helpers; namespace BetterAmongUs.Data; diff --git a/Data/AbstractJsonFile.cs b/Data/Json/AbstractJsonFile.cs similarity index 96% rename from Data/AbstractJsonFile.cs rename to Data/Json/AbstractJsonFile.cs index 51de3da..af560c4 100644 --- a/Data/AbstractJsonFile.cs +++ b/Data/Json/AbstractJsonFile.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace BetterAmongUs.Data; +namespace BetterAmongUs.Data.Json; internal abstract class AbstractJsonFile { @@ -22,10 +22,14 @@ internal abstract class AbstractJsonFile if (_hasInit) return; _hasInit = true; - if (!CheckFile() || !Load()) + if (!CheckFile()) { Save(); + return; } + + Load(); + Save(); } protected virtual bool Load() diff --git a/Data/BetterDataFile.cs b/Data/Json/BetterDataFile.cs similarity index 80% rename from Data/BetterDataFile.cs rename to Data/Json/BetterDataFile.cs index 81e2601..7acb728 100644 --- a/Data/BetterDataFile.cs +++ b/Data/Json/BetterDataFile.cs @@ -1,7 +1,7 @@ using BetterAmongUs.Items.Structs; using System.Text.Json.Serialization; -namespace BetterAmongUs.Data; +namespace BetterAmongUs.Data.Json; internal sealed class BetterDataFile : AbstractJsonFile { @@ -12,6 +12,7 @@ internal sealed class BetterDataFile : AbstractJsonFile var success = base.Load(); if (success) { + SelectedOutfitPreset = Math.Clamp(SelectedOutfitPreset, 0, 5); AllCheatData = [.. CheatData, .. SickoData, .. AUMData, .. KNData]; } return success; @@ -41,6 +42,12 @@ internal sealed class BetterDataFile : AbstractJsonFile internal HashSet AllCheatData { get; set; } = []; + [JsonPropertyName("selectedOutfitPreset")] + public int SelectedOutfitPreset { get; set; } = 0; + + [JsonPropertyName("outfitData")] + public HashSet OutfitData { get; set; } = [new(), new(), new(), new(), new(), new()]; + [JsonPropertyName("cheatData")] public HashSet CheatData { get; set; } = []; diff --git a/Data/BetterGameSettingsFile.cs b/Data/Json/BetterGameSettingsFile.cs similarity index 99% rename from Data/BetterGameSettingsFile.cs rename to Data/Json/BetterGameSettingsFile.cs index f2461a3..d50f72f 100644 --- a/Data/BetterGameSettingsFile.cs +++ b/Data/Json/BetterGameSettingsFile.cs @@ -3,7 +3,7 @@ using System.Text; using System.Text.Json; using System.Text.Json.Serialization; -namespace BetterAmongUs.Data; +namespace BetterAmongUs.Data.Json; internal sealed class BetterGameSettingsFile : AbstractJsonFile { diff --git a/Data/Json/OutfitData.cs b/Data/Json/OutfitData.cs new file mode 100644 index 0000000..cfed762 --- /dev/null +++ b/Data/Json/OutfitData.cs @@ -0,0 +1,97 @@ +using AmongUs.Data; + +namespace BetterAmongUs.Data.Json; + +[Serializable] +internal class OutfitData +{ + public string HatId = HatData.EmptyId; + public string PetId = PetData.EmptyId; + public string SkinId = SkinData.EmptyId; + public string VisorId = VisorData.EmptyId; + public string NamePlateId = NamePlateData.EmptyId; + + internal static OutfitData GetOutfitData() => BetterDataManager.BetterDataFile.OutfitData.ElementAt(BetterDataManager.BetterDataFile.SelectedOutfitPreset); + internal static OutfitData GetOutfitData(int index) => BetterDataManager.BetterDataFile.OutfitData.ElementAt(index); + + private static bool ignoreChange; + internal static void Init() + { + FindPreset(); + + var Save = () => + { + if (ignoreChange) return; + GetOutfitData().LoadToData(); + BetterDataManager.BetterDataFile.Save(); + }; + + DataManager.Player.Customization.OnHatChanged += Save; + DataManager.Player.Customization.OnPetChanged += Save; + DataManager.Player.Customization.OnSkinChanged += Save; + DataManager.Player.Customization.OnVisorChanged += Save; + DataManager.Player.Customization.OnNamePlateChanged += Save; + } + + internal static void FindPreset() + { + var collection = BetterDataManager.BetterDataFile.OutfitData; + int i = 0; + foreach (var data in collection) + { + if (data.HatId == DataManager.Player.Customization.Hat && + data.PetId == DataManager.Player.Customization.Pet && + data.SkinId == DataManager.Player.Customization.Skin && + data.VisorId == DataManager.Player.Customization.Visor && + data.NamePlateId == DataManager.Player.Customization.NamePlate) + { + BetterDataManager.BetterDataFile.SelectedOutfitPreset = i; + BetterDataManager.BetterDataFile.Save(); + return; + } + i++; + } + + BetterDataManager.BetterDataFile.SelectedOutfitPreset = 0; + BetterDataManager.BetterDataFile.Save(); + } + + private void Validate() + { + if (!HatManager.Instance.GetUnlockedHats().Any(item => item.ProductId == HatId)) + HatId = HatData.EmptyId; + if (!HatManager.Instance.GetUnlockedPets().Any(item => item.ProductId == PetId)) + PetId = PetData.EmptyId; + if (!HatManager.Instance.GetUnlockedSkins().Any(item => item.ProductId == SkinId)) + SkinId = SkinData.EmptyId; + if (!HatManager.Instance.GetUnlockedVisors().Any(item => item.ProductId == VisorId)) + VisorId = VisorData.EmptyId; + if (!HatManager.Instance.GetUnlockedNamePlates().Any(item => item.ProductId == NamePlateId)) + NamePlateId = NamePlateData.EmptyId; + } + + internal void Load(Action callback) + { + Validate(); + + ignoreChange = true; + DataManager.Player.Customization.Hat = HatId; + DataManager.Player.Customization.Pet = PetId; + DataManager.Player.Customization.Skin = SkinId; + DataManager.Player.Customization.Visor = VisorId; + DataManager.Player.Customization.NamePlate = NamePlateId; + ignoreChange = false; + + callback.Invoke(); + BetterDataManager.BetterDataFile.Save(); + } + + internal void LoadToData() + { + HatId = DataManager.Player.Customization.Hat; + PetId = DataManager.Player.Customization.Pet; + SkinId = DataManager.Player.Customization.Skin; + VisorId = DataManager.Player.Customization.Visor; + NamePlateId = DataManager.Player.Customization.NamePlate; + } +} diff --git a/Data/Replay/Events/IReplayEvent.cs b/Data/Replay/Events/IReplayEvent.cs index aa33273..accc0f2 100644 --- a/Data/Replay/Events/IReplayEvent.cs +++ b/Data/Replay/Events/IReplayEvent.cs @@ -1,7 +1,10 @@ -namespace BetterAmongUs.Data.Replay.Events; +using System.Text.Json.Serialization; + +namespace BetterAmongUs.Data.Replay.Events; public interface IReplayEvent { + [JsonIgnore] string Id { get; } void Play(); } diff --git a/Data/Replay/Replay.cs b/Data/Replay/Replay.cs index 8b4c0d4..da2c18f 100644 --- a/Data/Replay/Replay.cs +++ b/Data/Replay/Replay.cs @@ -7,7 +7,7 @@ internal class Replay { public int MapId; public List PlayerData = []; - public (float timeStamp, IReplayEvent) Events; + public Dictionary> Events = []; public void Load() { diff --git a/Helpers/ExtendedPlayerInfo.cs b/Helpers/ExtendedPlayerInfo.cs index dc5bbce..d5c1469 100644 --- a/Helpers/ExtendedPlayerInfo.cs +++ b/Helpers/ExtendedPlayerInfo.cs @@ -1,7 +1,7 @@ using AmongUs.GameOptions; -using BetterAmongUs.Items; using BetterAmongUs.Managers; using BetterAmongUs.Modules; +using BetterAmongUs.Network.Configs; using Il2CppInterop.Runtime.Attributes; using InnerNet; using UnityEngine; diff --git a/Helpers/ObjectHelper.cs b/Helpers/ObjectHelper.cs index 41bfa8a..cbb0937 100644 --- a/Helpers/ObjectHelper.cs +++ b/Helpers/ObjectHelper.cs @@ -102,4 +102,16 @@ internal static class ObjectHelper color ??= Color.green; sprite.color = (sprite.color * 0.6f) + ((Color)color * 0.5f); } + + internal static void SetLayers(this GameObject go, string layerName) + { + int layer = LayerMask.NameToLayer(layerName); + if (layer == -1) return; + + Transform[] allChildren = go.GetComponentsInChildren(true); + foreach (Transform child in allChildren) + { + child.gameObject.layer = layer; + } + } } diff --git a/Modules/UserDataLoader.cs b/Modules/UserDataLoader.cs index 68d6e10..1eff917 100644 --- a/Modules/UserDataLoader.cs +++ b/Modules/UserDataLoader.cs @@ -3,10 +3,10 @@ using Il2CppInterop.Runtime.Attributes; using System.Collections; using System.Text.Json; using BetterAmongUs.Helpers; -using BetterAmongUs.Items; using UnityEngine; using UnityEngine.Networking; using System.Reflection; +using BetterAmongUs.Network.Configs; namespace BetterAmongUs.Modules; diff --git a/Items/BannedUserData.cs b/Network/Configs/BannedUserData.cs similarity index 81% rename from Items/BannedUserData.cs rename to Network/Configs/BannedUserData.cs index ee0a5b2..d157d09 100644 --- a/Items/BannedUserData.cs +++ b/Network/Configs/BannedUserData.cs @@ -1,7 +1,7 @@ using BetterAmongUs.Helpers; using System.Text.Json.Serialization; -namespace BetterAmongUs.Items; +namespace BetterAmongUs.Network.Configs; [method: JsonConstructor] internal class BannedUserData(string name = "", string puid = "", string friendCode = "", string reason = "") @@ -30,7 +30,7 @@ internal class BannedUserData(string name = "", string puid = "", string friendC if (EOSManager.Instance) { - var data = AllBannedUsers?.FirstOrDefault(user => user.Puid == Utils.GetHashStr(EOSManager.Instance.ProductUserId) || user.FriendCode == Utils.GetHashStr(EOSManager.Instance.FriendCode)); + var data = AllBannedUsers?.FirstOrDefault(user => user.Puid == EOSManager.Instance.ProductUserId.GetHashStr() || user.FriendCode == EOSManager.Instance.FriendCode.GetHashStr()); if (data != null) { bannedData = data; @@ -43,6 +43,6 @@ internal class BannedUserData(string name = "", string puid = "", string friendC return false; } internal static bool CheckPlayerBan(NetworkedPlayerInfo data) => AllBannedUsers?.FirstOrDefault(user => user.Puid == data.GetHashPuid() || user.FriendCode == data.GetHashFriendcode()) != null; - internal static bool CheckPuidBan(string puid) => AllBannedUsers?.FirstOrDefault(user => user.Puid == Utils.GetHashStr(puid)) != null; - internal static bool CheckFriendCodeBan(string friendcode) => AllBannedUsers?.FirstOrDefault(user => user.FriendCode == Utils.GetHashStr(friendcode)) != null; + internal static bool CheckPuidBan(string puid) => AllBannedUsers?.FirstOrDefault(user => user.Puid == puid.GetHashStr()) != null; + internal static bool CheckFriendCodeBan(string friendcode) => AllBannedUsers?.FirstOrDefault(user => user.FriendCode == friendcode.GetHashStr()) != null; } \ No newline at end of file diff --git a/Items/UserData.cs b/Network/Configs/UserData.cs similarity index 80% rename from Items/UserData.cs rename to Network/Configs/UserData.cs index 068a675..cbf87f4 100644 --- a/Items/UserData.cs +++ b/Network/Configs/UserData.cs @@ -1,7 +1,7 @@ using BetterAmongUs.Helpers; using System.Text.Json.Serialization; -namespace BetterAmongUs.Items; +namespace BetterAmongUs.Network.Configs; [Flags] internal enum MultiPermissionFlags : ushort @@ -47,7 +47,7 @@ internal class UserData(string name = "", string puid = "", string friendCode = { if (EOSManager.Instance) { - var data = AllUsers?.FirstOrDefault(user => user.Puid == Utils.GetHashStr(EOSManager.Instance.ProductUserId) || user.FriendCode == Utils.GetHashStr(EOSManager.Instance.FriendCode)); + var data = AllUsers?.FirstOrDefault(user => user.Puid == EOSManager.Instance.ProductUserId.GetHashStr() || user.FriendCode == EOSManager.Instance.FriendCode.GetHashStr()); if (data != null) { Logger.Log($"Found local UserData({data.Name})"); @@ -62,8 +62,8 @@ internal class UserData(string name = "", string puid = "", string friendCode = } } internal static UserData? GetPlayerUserData(NetworkedPlayerInfo data) => AllUsers?.FirstOrDefault(user => user.Puid == data.GetHashPuid() || user.FriendCode == data.GetHashFriendcode()) ?? AllUsers.First(); - internal static UserData? GetPlayerUserDataFromPuid(string puid) => AllUsers?.FirstOrDefault(user => user.Puid == Utils.GetHashStr(puid)) ?? AllUsers.First(); - internal static UserData? GetPlayerUserDataFromFriendCode(string friendcode) => AllUsers?.FirstOrDefault(user => user.FriendCode == Utils.GetHashStr(friendcode)) ?? AllUsers.First(); + internal static UserData? GetPlayerUserDataFromPuid(string puid) => AllUsers?.FirstOrDefault(user => user.Puid == puid.GetHashStr()) ?? AllUsers.First(); + internal static UserData? GetPlayerUserDataFromFriendCode(string friendcode) => AllUsers?.FirstOrDefault(user => user.FriendCode == friendcode.GetHashStr()) ?? AllUsers.First(); private bool HasPermission(MultiPermissionFlags permissionFlags) { @@ -82,7 +82,7 @@ internal class UserData(string name = "", string puid = "", string friendCode = internal bool IsSponsorTier1() => IsSponsor(); internal bool IsSponsor() => HasPermission(MultiPermissionFlags.Contributor_1 | MultiPermissionFlags.Contributor_2 | MultiPermissionFlags.Contributor_3 | MultiPermissionFlags.Dev); internal bool HasAll() => (Permissions & (ushort)MultiPermissionFlags.All) == (ushort)MultiPermissionFlags.All; - internal bool IsVerified() => Puid == Utils.GetHashStr(EOSManager.Instance.ProductUserId) && FriendCode == Utils.GetHashStr(EOSManager.Instance.FriendCode); - internal bool IsVerified(NetworkedPlayerInfo data) => Puid == Utils.GetHashStr(data?.Puid ?? string.Empty) && FriendCode == Utils.GetHashStr(data?.FriendCode ?? string.Empty); - internal bool IsVerified(PlayerControl player) => Puid == Utils.GetHashStr(player?.Data?.Puid ?? string.Empty) && FriendCode == Utils.GetHashStr(player?.Data?.FriendCode ?? string.Empty); + internal bool IsVerified() => Puid == EOSManager.Instance.ProductUserId.GetHashStr() && FriendCode == EOSManager.Instance.FriendCode.GetHashStr(); + internal bool IsVerified(NetworkedPlayerInfo data) => Puid == (data?.Puid ?? string.Empty).GetHashStr() && FriendCode == (data?.FriendCode ?? string.Empty).GetHashStr(); + internal bool IsVerified(PlayerControl player) => Puid == (player?.Data?.Puid ?? string.Empty).GetHashStr() && FriendCode == (player?.Data?.FriendCode ?? string.Empty).GetHashStr(); } \ No newline at end of file diff --git a/Network/GitHubFile.cs b/Network/GitHubFile.cs index 4d91289..a3dbb86 100644 --- a/Network/GitHubFile.cs +++ b/Network/GitHubFile.cs @@ -4,9 +4,6 @@ using UnityEngine.Networking; namespace BetterAmongUs.Network; -/// -/// Class representing a file from GitHub (for custom hats). -/// internal class GitHubFile { /// diff --git a/Patches/Client/ClientPatch.cs b/Patches/Client/ClientPatch.cs index 3f9c1b0..912081a 100644 --- a/Patches/Client/ClientPatch.cs +++ b/Patches/Client/ClientPatch.cs @@ -1,9 +1,9 @@ using BepInEx.Unity.IL2CPP.Utils; using BetterAmongUs.Data; using BetterAmongUs.Helpers; -using BetterAmongUs.Items; using BetterAmongUs.Managers; using BetterAmongUs.Modules; +using BetterAmongUs.Network.Configs; using BetterAmongUs.Patches.Gameplay.UI.Chat; using BetterAmongUs.Patches.Gameplay.UI.Settings; using HarmonyLib; diff --git a/Patches/Client/MainMenuPatch.cs b/Patches/Client/MainMenuPatch.cs index 7f9d20f..0ac4644 100644 --- a/Patches/Client/MainMenuPatch.cs +++ b/Patches/Client/MainMenuPatch.cs @@ -1,6 +1,6 @@ using BetterAmongUs.Helpers; -using BetterAmongUs.Items; using BetterAmongUs.Modules; +using BetterAmongUs.Network.Configs; using HarmonyLib; using UnityEngine; using UnityEngine.SceneManagement; @@ -11,6 +11,7 @@ namespace BetterAmongUs.Patches.Client; internal static class MainMenuPatch { private static SpriteRenderer? sprite; + internal static PassiveButton? ButtonPrefab; [HarmonyPatch(nameof(MainMenuManager.LateUpdate))] [HarmonyPostfix] @@ -72,5 +73,12 @@ internal static class MainMenuPatch logo.GetComponent().sprite = Utils.LoadSprite("BetterAmongUs.Resources.Images.BetterAmongUs-Logo.png", 1f); __instance.transform.Find("MainUI/AspectScaler/BackgroundTexture")?.gameObject?.SetSpriteColors(sprite => ObjectHelper.AddColor(sprite)); + + if (ButtonPrefab == null) + { + ButtonPrefab = UnityEngine.Object.Instantiate(__instance.inventoryButton); + ButtonPrefab.gameObject.SetActive(false); + UnityEngine.Object.DontDestroyOnLoad(ButtonPrefab); + } } } diff --git a/Patches/Gameplay/UI/IntroCutscenePatch.cs b/Patches/Gameplay/UI/IntroCutscenePatch.cs index dcb03f6..e0cfc5e 100644 --- a/Patches/Gameplay/UI/IntroCutscenePatch.cs +++ b/Patches/Gameplay/UI/IntroCutscenePatch.cs @@ -1,5 +1,7 @@ -using BetterAmongUs.Helpers; +using BepInEx.Unity.IL2CPP.Utils; +using BetterAmongUs.Helpers; using HarmonyLib; +using System.Collections; using UnityEngine; namespace BetterAmongUs.Patches.Gameplay.UI; @@ -7,26 +9,29 @@ namespace BetterAmongUs.Patches.Gameplay.UI; [HarmonyPatch] internal static class IntroCutscenePatch { - [HarmonyPatch(typeof(IntroCutscene._ShowRole_d__41), nameof(IntroCutscene._ShowRole_d__41.MoveNext))] + [HarmonyPatch(typeof(IntroCutscene), nameof(IntroCutscene.CoBegin))] [HarmonyPostfix] - private static void ShowRole_MoveNext_Postfix(IntroCutscene._ShowRole_d__41 __instance) + private static void ShowRole_MoveNext_Postfix(IntroCutscene __instance) { - try + __instance.StartCoroutine(CoWaitForShowRole(__instance)); + } + + private static IEnumerator CoWaitForShowRole(IntroCutscene __instance) + { + while (!__instance.YouAreText.gameObject.active) { - var introCutscene = __instance.__4__this; - Color RoleColor = Utils.HexToColor32(PlayerControl.LocalPlayer.Data.RoleType.GetRoleHex()); - introCutscene.ImpostorText.gameObject.SetActive(false); - introCutscene.TeamTitle.gameObject.SetActive(false); - introCutscene.BackgroundBar.material.color = RoleColor; - introCutscene.BackgroundBar.transform.SetLocalZ(-15); - introCutscene.transform.Find("BackgroundLayer").transform.SetLocalZ(-16); - introCutscene.YouAreText.color = RoleColor; - introCutscene.RoleText.color = RoleColor; - introCutscene.RoleBlurbText.color = RoleColor; - } - catch (Exception ex) - { - Logger.Error(ex); + yield return null; } + + var introCutscene = __instance; + Color RoleColor = Utils.HexToColor32(PlayerControl.LocalPlayer.Data.RoleType.GetRoleHex()); + introCutscene.ImpostorText.gameObject.SetActive(false); + introCutscene.TeamTitle.gameObject.SetActive(false); + introCutscene.BackgroundBar.material.color = RoleColor; + introCutscene.BackgroundBar.transform.SetLocalZ(-15); + introCutscene.transform.Find("BackgroundLayer").transform.SetLocalZ(-16); + introCutscene.YouAreText.color = RoleColor; + introCutscene.RoleText.color = RoleColor; + introCutscene.RoleBlurbText.color = RoleColor; } } \ No newline at end of file diff --git a/Patches/Gameplay/UI/PlayerTabPatch.cs b/Patches/Gameplay/UI/PlayerTabPatch.cs new file mode 100644 index 0000000..de5af12 --- /dev/null +++ b/Patches/Gameplay/UI/PlayerTabPatch.cs @@ -0,0 +1,116 @@ +using BetterAmongUs.Data; +using BetterAmongUs.Data.Json; +using BetterAmongUs.Helpers; +using BetterAmongUs.Patches.Client; +using HarmonyLib; +using TMPro; +using UnityEngine; + +namespace BetterAmongUs.Patches.Gameplay.UI; + +[HarmonyPatch(typeof(PlayerTab))] +internal static class PlayerTabPatch +{ + private static List presetButtons = []; + private static float cooldown = 0f; + + [HarmonyPatch(nameof(PlayerTab.OnEnable))] + [HarmonyPrefix] + private static void OnEnable_Postfix(PlayerTab __instance) + { + foreach (var button in presetButtons.ToArray()) + { + if (button == null) continue; + UnityEngine.Object.Destroy(button.gameObject); + } + presetButtons.Clear(); + + for (int i = 0; i <= 5; i++) + { + int currentI = i; + var name = currentI == 0 ? "Among Us Preset" : $"Preset {i}"; + var button = __instance.CreateButton(name, new Vector3(2.5f, 1.55f - currentI * 0.45f, 0f), () => + { + if (cooldown > 0f || BetterDataManager.BetterDataFile.SelectedOutfitPreset == currentI) return; + cooldown = 0.5f; + + BetterDataManager.BetterDataFile.SelectedOutfitPreset = currentI; + + foreach (var button in presetButtons) + { + if (button == null) continue; + button.SetPassiveButtonHoverStateInactive(); + } + + var data = OutfitData.GetOutfitData(currentI); + data.Load(() => + { + if (LoadPlayerOutfit(data)) + { + __instance.PlayerPreview.UpdateFromLocalPlayer(PlayerMaterial.MaskType.None); + } + else + { + __instance.PlayerPreview.UpdateFromDataManager(PlayerMaterial.MaskType.None); + } + }); + }); + presetButtons.Add(button); + } + } + + private static bool LoadPlayerOutfit(OutfitData data) + { + var player = PlayerControl.LocalPlayer; + if (player != null) + { + player.RpcSetHat(data.HatId); + player.RpcSetPet(data.PetId); + player.RpcSetSkin(data.SkinId); + player.RpcSetVisor(data.VisorId); + player.RpcSetNamePlate(data.NamePlateId); + return true; + } + + return false; + } + + private static PassiveButton CreateButton(this PlayerTab __instance, string name, Vector3 pos, Action callback) + { + var button = UnityEngine.Object.Instantiate(MainMenuPatch.ButtonPrefab, __instance.transform); + button.gameObject.SetActive(true); + button.gameObject.SetLayers("UI"); + button.transform.localPosition = pos; + button.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f); + button.OnClick = new(); + button.OnClick.AddListener(callback); + button.DestroyTextTranslators(); + var text = button.GetComponentInChildren(); + text?.SetText(name); + return button; + } + + [HarmonyPatch(nameof(PlayerTab.Update))] + [HarmonyPrefix] + private static void Updatee_Postfix(PlayerTab __instance) + { + if (cooldown > 0f) + { + cooldown -= Time.deltaTime; + } + else + { + cooldown = 0f; + } + + for (int i = 0; i < presetButtons.Count; i++) + { + PassiveButton? button = presetButtons[i]; + if (button == null) continue; + if (i == BetterDataManager.BetterDataFile.SelectedOutfitPreset) + { + button.SetPassiveButtonHoverStateActive(); + } + } + } +}