Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TooManyEmotes v2.3.15
plugins/TooManyEmotes.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Versioning; using AdvancedCompany.Cosmetics; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LCVR.Managers; using LethalCompanyInputUtils.Api; using MoreCompany.Cosmetics; using TMPro; using TooManyEmotes.Audio; using TooManyEmotes.Compatibility; using TooManyEmotes.Config; using TooManyEmotes.Input; using TooManyEmotes.Networking; using TooManyEmotes.Patches; using TooManyEmotes.Props; using TooManyEmotes.UI; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.Animations.Rigging; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Experimental.Rendering; using UnityEngine.InputSystem; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("TooManyEmotes")] [assembly: AssemblyDescription("Mod made by flipf17")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("TooManyEmotes")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("d6950625-e3a1-4896-a183-87110491bf18")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: InternalsVisibleTo("TooManyEmotesScrap")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace TooManyEmotes { [HarmonyPatch] public static class AdditionalEmoteData { public static void SetAdditionalEmoteData() { } public static void SetAdditionalPropData() { if (EmotesManager.allUnlockableEmotesDict != null) { AssignPropToEmote("jug_prop", "jug_band.jug"); AssignPropToEmote("guitar_prop", "jug_band.guitar"); AssignPropToEmote("banjo_prop", "jug_band.banjo"); AssignPropToEmote("fiddle_prop", "jug_band.fiddle"); AssignPropToEmote("sexy_saxophone.sexy_sax.prop", "sexy_saxophone.sexy_sax"); AssignPropToEmote("sexy_saxophone.epic_sax.prop", "sexy_saxophone.epic_sax"); AssignPropToEmote("trombone.prop", "sad_trombone"); AssignPropToEmote("old_chair.prop", "ma-ya-hi"); AssignPropToEmote("baseball_bat.prop", "miracle_trickshot"); AssignPropToEmote("dumbbell.prop", "pumping_iron"); AssignPropToEmote("paddle.prop", "paddle_royale"); AssignPropToEmote("gamepad.prop", "controller_crew"); AssignPropToEmote("jar_of_dirt.prop", "jar_of_dirt"); AssignPropToEmote("travelers.banjo.prop", "travelers.banjo"); AssignPropToEmote("travelers.harmonica.prop", "travelers.harmonica"); AssignPropToEmote("travelers.drums.prop", "travelers.drums"); AssignPropToEmote("travelers.flute.prop", "travelers.flute"); AssignPropToEmote("travelers.whistle.prop", "travelers.whistle"); AssignPropToEmote("travelers.piano.prop", "travelers.piano"); AssignPropToEmote("travelers.bow.prop", "travelers.bow"); } } public static void SetAdditionalMusicData() { if (EmotesManager.allUnlockableEmotesDict == null) { return; } SetEmoteDoesNotUseBoombox("jug_band.jug"); SetEmoteDoesNotUseBoombox("jug_band.guitar"); SetEmoteDoesNotUseBoombox("jug_band.banjo"); SetEmoteDoesNotUseBoombox("jug_band.fiddle"); SetEmoteDoesNotUseBoombox("hand_signals"); SetEmoteDoesNotUseBoombox("red_card"); SetEmoteDoesNotUseBoombox("sexy_saxophone.sexy_sax"); SetEmoteDoesNotUseBoombox("sexy_saxophone.epic_sax"); SetEmoteDoesNotUseBoombox("sad_trombone"); SetEmoteDoesNotUseBoombox("snake_summoner"); SetEmoteDoesNotUseBoombox("miracle_trickshot"); SetEmoteDoesNotUseBoombox("junk_food"); SetEmoteDoesNotUseBoombox("pumping_iron"); SetEmoteDoesNotUseBoombox("paddle_royale"); SetEmoteDoesNotUseBoombox("wolf_howl"); SetEmoteDoesNotUseBoombox("jar_of_dirt"); SetEmoteDoesNotUseBoombox("squeaky_step"); SetEmoteDoesNotUseBoombox("travelers.banjo"); SetEmoteDoesNotUseBoombox("travelers.harmonica"); SetEmoteDoesNotUseBoombox("travelers.drums"); SetEmoteDoesNotUseBoombox("travelers.flute"); SetEmoteDoesNotUseBoombox("travelers.whistle"); SetEmoteDoesNotUseBoombox("travelers.piano"); SetEmoteDoesNotUseBoombox("travelers.bow"); AssignMusicToEmote("jug_band.jug", "jug_band.jug"); AssignMusicToEmote("jug_band.guitar", "jug_band.guitar"); AssignMusicToEmote("jug_band.banjo", "jug_band.banjo"); AssignMusicToEmote("jug_band.fiddle", "jug_band.fiddle"); AssignMusicToEmote("travelers.banjo", "travelers.banjo"); AssignMusicToEmote("travelers.harmonica", "travelers.harmonica"); AssignMusicToEmote("travelers.drums", "travelers.drums"); AssignMusicToEmote("travelers.flute", "travelers.flute"); AssignMusicToEmote("travelers.whistle", "travelers.whistle"); AssignMusicToEmote("travelers.piano", "travelers.piano"); AssignMusicToEmote("travelers.bow", "travelers.bow"); if (EmotesManager.allUnlockableEmotesDict.TryGetValue("jug_band.banjo", out var value) && value.inEmoteSyncGroup) { foreach (UnlockableEmote item in value.emoteSyncGroup) { item.recordSongLoopValue = 1f; } } if (!EmotesManager.allUnlockableEmotesDict.TryGetValue("travelers.banjo", out value) || !value.inEmoteSyncGroup) { return; } foreach (UnlockableEmote item2 in value.emoteSyncGroup) { item2.recordSongLoopValue = 0.5f; } } public static void AssignPropToEmote(string propName, string emoteName) { if (!EmotePropManager.emotePropsDataDict.TryGetValue(propName, out var _)) { CustomLogging.LogWarning("Failed to assign prop: " + propName + " to emote. Prop does not exist!"); return; } if (!EmotesManager.allUnlockableEmotesDict.TryGetValue(emoteName, out var value2)) { CustomLogging.LogWarning("Failed to assign prop: " + propName + " to emote: " + emoteName + ". Emote does not exist!"); return; } if (!EmotePropManager.emotePropsDataDict.TryGetValue(propName, out var value3)) { CustomLogging.LogWarning("Failed to assign prop: " + propName + " to emote: " + emoteName + ". Prop data does not exist for: " + propName); return; } if (value2.propNamesInEmote == null) { value2.propNamesInEmote = new List<string>(); } value2.propNamesInEmote.Add(propName); if (value3.parentEmotes == null) { value3.parentEmotes = new List<UnlockableEmote>(); } if (!value3.parentEmotes.Contains(value2)) { value3.parentEmotes.Add(value2); } } public static void SetEmoteDoesNotUseBoombox(string emoteName) { if (EmotesManager.allUnlockableEmotesDict.TryGetValue(emoteName, out var value)) { value.isBoomboxAudio = false; } } public static void SetCanMoveWhileEmoting(string emoteName) { if (EmotesManager.allUnlockableEmotesDict.TryGetValue(emoteName, out var value)) { value.canMoveWhileEmoting = true; } } public static void AssignMusicToEmote(string emoteName, string audioName, string audioLoopName = "") { if (EmotesManager.allUnlockableEmotesDict.TryGetValue(emoteName, out var value) && AudioManager.AudioExists(audioName)) { value.overrideAudioClipName = audioName; value.overrideAudioLoopClipName = audioLoopName; } } } public static class QuickEmotes { public static UnlockableEmote GetQuickEmote(int index) { if (index < 0 || index >= 8) { CustomLogging.LogError("Failed to get quick emote name at index: " + index + ". Index must be within range: 0 and 8"); return null; } string key = ES3.Load<string>("QuickEmote" + index, SaveManager.TooManyEmotesSaveFileName, string.Empty); UnlockableEmote value = null; EmotesManager.allUnlockableEmotesDict.TryGetValue(key, out value); return value; } } public static class BoneMapper { public static Dictionary<Transform, Transform> CreateBoneMap(Transform sourceSkeleton, Transform targetSkeleton, List<string> sourceBoneNames, List<string> targetBoneNames = null) { if ((Object)(object)sourceSkeleton == (Object)null || (Object)(object)targetSkeleton == (Object)null || sourceBoneNames == null) { return null; } if (targetBoneNames == null) { targetBoneNames = sourceBoneNames; } if (sourceBoneNames.Count != targetBoneNames.Count) { CustomLogging.LogError("Attempted to map humanoid skeleton, but passed two sets of bone names with differing sizes."); return null; } int count = sourceBoneNames.Count; Transform[] array = (Transform[])(object)new Transform[count]; Transform[] array2 = (Transform[])(object)new Transform[count]; FindBones(sourceSkeleton, sourceBoneNames, array); FindBones(targetSkeleton, targetBoneNames, array2); Dictionary<Transform, Transform> dictionary = new Dictionary<Transform, Transform>(); for (int i = 0; i < count; i++) { if ((Object)(object)array[i] != (Object)null && !dictionary.ContainsKey(array[i])) { dictionary.Add(array[i], array2[i]); } } return dictionary; } private static void FindBones(Transform bone, List<string> boneNames, Transform[] boneArray) { if ((Object)(object)((Component)bone).GetComponent<Rig>() != (Object)null || ((Object)bone).name == "ScavengerModelArmsOnly") { return; } if (boneNames.Contains(((Object)bone).name)) { int num = boneNames.IndexOf(((Object)bone).name); if (!((Object)(object)boneArray[num] != (Object)null)) { boneArray[num] = bone; } } for (int i = 0; i < bone.childCount; i++) { FindBones(bone.GetChild(i), boneNames, boneArray); } } } internal static class CustomLogging { private static ManualLogSource logger; public static void InitLogger() { try { logger = Logger.CreateLogSource($"{((BaseUnityPlugin)Plugin.instance).Info.Metadata.Name}-{((BaseUnityPlugin)Plugin.instance).Info.Metadata.Version}"); } catch { logger = Plugin.defaultLogger; } } public static void Log(string message) { logger.LogInfo((object)message); } public static void LogError(string message) { logger.LogError((object)message); } public static void LogWarning(string message) { logger.LogWarning((object)message); } public static void LogVerbose(string message) { if (ConfigSettings.verboseLogs.Value) { logger.LogInfo((object)("[VERBOSE] " + message)); } } public static void LogErrorVerbose(string message) { if (ConfigSettings.verboseLogs.Value) { logger.LogError((object)("[VERBOSE] " + message)); } } public static void LogWarningVerbose(string message) { if (ConfigSettings.verboseLogs.Value) { logger.LogWarning((object)("[VERBOSE] " + message)); } } public static bool Assert(bool condition, string failMessage) { if (!condition) { LogWarning(failMessage); } return condition; } } public class OnAnimatorIKHandler : MonoBehaviour { private EmoteController emoteController; private Animator animator; public float handIKWeight = 0.8f; private void Awake() { animator = ((Component)this).GetComponent<Animator>(); if ((Object)(object)animator == (Object)null) { CustomLogging.LogWarning("OnIKHandler must be attached to a gameobject with an animator component."); } } public void SetParentEmoteController(EmoteController emoteController) { this.emoteController = emoteController; } protected void OnAnimatorIK(int layerIndex) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)emoteController) && emoteController.initialized && emoteController.IsPerformingCustomEmote()) { if (Object.op_Implicit((Object)(object)emoteController.ikLeftHand) && emoteController.ikLeftHand.localPosition != Vector3.zero) { animator.SetIKPositionWeight((AvatarIKGoal)2, handIKWeight); animator.SetIKRotationWeight((AvatarIKGoal)2, handIKWeight); animator.SetIKPosition((AvatarIKGoal)2, emoteController.ikLeftHand.position); animator.SetIKRotation((AvatarIKGoal)2, emoteController.ikLeftHand.rotation); } if (Object.op_Implicit((Object)(object)emoteController.ikRightHand) && emoteController.ikRightHand.localPosition != Vector3.zero) { animator.SetIKPositionWeight((AvatarIKGoal)3, handIKWeight); animator.SetIKRotationWeight((AvatarIKGoal)3, handIKWeight); animator.SetIKPosition((AvatarIKGoal)3, emoteController.ikRightHand.position); animator.SetIKRotation((AvatarIKGoal)3, emoteController.ikRightHand.rotation); } if (Object.op_Implicit((Object)(object)emoteController.ikHead) && emoteController.ikHead.localPosition != Vector3.zero) { animator.SetLookAtWeight(1f, 0.25f, 0.5f); animator.SetLookAtPosition(emoteController.ikHead.position); } } } } [HarmonyPatch] public class EmoteSyncGroup { public static int currentEmoteSyncId = 0; public static Dictionary<int, EmoteSyncGroup> allEmoteSyncGroups = new Dictionary<int, EmoteSyncGroup>(); public int syncId = -1; public List<EmoteController> syncGroup; public Dictionary<UnlockableEmote, EmoteController> leadEmoteControllerByEmote = new Dictionary<UnlockableEmote, EmoteController>(); public Dictionary<UnlockableEmote, EmoteAudioSource> currentEmoteAudioSources; public float timeStartedEmote; public UnlockableEmote performingEmote; public bool useAudio = true; public EmoteAudioPlayer currentAudioPlayer; public EmoteController leadEmoteController => (syncGroup != null && syncGroup.Count > 0) ? syncGroup[0] : null; public EmoteAudioSource leadEmoteAudioSource { get { if (currentEmoteAudioSources != null && currentEmoteAudioSources.Count > 0) { foreach (EmoteController item in syncGroup) { if ((Object)(object)item?.personalEmoteAudioSource != (Object)null && currentEmoteAudioSources.ContainsValue(item.personalEmoteAudioSource)) { return item.personalEmoteAudioSource; } } } return null; } } [HarmonyPatch(typeof(StartOfRound), "Awake")] [HarmonyPrefix] public static void Init() { currentEmoteSyncId = 0; allEmoteSyncGroups?.Clear(); } public static EmoteSyncGroup CreateEmoteSyncGroup(EmoteController emoteController, bool useAudio = true) { return new EmoteSyncGroup(emoteController, useAudio); } public EmoteSyncGroup(EmoteController emoteController, bool useAudio = true) { syncGroup = new List<EmoteController> { emoteController }; syncId = currentEmoteSyncId++; performingEmote = emoteController.performingEmote; leadEmoteControllerByEmote.Add(performingEmote, emoteController); this.useAudio = useAudio; if (useAudio && performingEmote.hasAudio) { if (!performingEmote.isBoomboxAudio) { currentEmoteAudioSources = new Dictionary<UnlockableEmote, EmoteAudioSource>(); if ((Object)(object)emoteController.personalEmoteAudioSource == (Object)null) { CustomLogging.LogError("Attempted to perform emote with personal audio source, which is null."); this.useAudio = false; } else { currentEmoteAudioSources[performingEmote] = emoteController.personalEmoteAudioSource; emoteController.personalEmoteAudioSource.SyncWithEmoteControllerAudio(emoteController); emoteController.personalEmoteAudioSource.AddToEmoteSyncGroup(this); } } else { EmoteAudioPlayer nearestEmoteAudioPlayer = EmoteAudioPlayerManager.GetNearestEmoteAudioPlayer(((Component)emoteController).transform); if ((Object)(object)nearestEmoteAudioPlayer != (Object)null && nearestEmoteAudioPlayer.CanPlayMusic()) { AssignExternalAudioPlayer(nearestEmoteAudioPlayer); } else { CustomLogging.LogWarning("Performing emote with no music. No available boomboxes found nearby. Don't worry. Everything will be okay."); } } UpdateAudioVolume(); } allEmoteSyncGroups[syncId] = this; timeStartedEmote = Time.time; } public void AddToEmoteSyncGroup(EmoteController emoteController) { if (syncGroup == null || syncGroup.Contains(emoteController)) { return; } syncGroup.Add(emoteController); if (!leadEmoteControllerByEmote.ContainsKey(emoteController.performingEmote) || (Object)(object)leadEmoteControllerByEmote[emoteController.performingEmote] == (Object)null) { leadEmoteControllerByEmote[emoteController.performingEmote] = emoteController; } if (!useAudio || !performingEmote.hasAudio) { return; } if (!performingEmote.isBoomboxAudio) { if (currentEmoteAudioSources != null && (!currentEmoteAudioSources.ContainsKey(emoteController.performingEmote) || (Object)(object)currentEmoteAudioSources[emoteController.performingEmote] == (Object)null)) { currentEmoteAudioSources[emoteController.performingEmote] = emoteController.personalEmoteAudioSource; emoteController.personalEmoteAudioSource.SyncWithEmoteSyncGroup(this, emoteController); emoteController.personalEmoteAudioSource.AddToEmoteSyncGroup(this); } } else if ((Object)(object)currentAudioPlayer == (Object)null) { EmoteAudioPlayer nearestEmoteAudioPlayer = EmoteAudioPlayerManager.GetNearestEmoteAudioPlayer(((Component)emoteController).transform); if ((Object)(object)nearestEmoteAudioPlayer != (Object)null && nearestEmoteAudioPlayer.CanPlayMusic()) { AssignExternalAudioPlayer(nearestEmoteAudioPlayer); } } UpdateAudioVolume(); } public void RemoveFromEmoteSyncGroup(EmoteController emoteController) { if (syncGroup == null) { return; } syncGroup.Remove(emoteController); if (syncGroup.Count <= 0) { DestroyEmoteSyncGroup(); return; } if (leadEmoteControllerByEmote.ContainsValue(emoteController)) { UnlockableEmote key = leadEmoteControllerByEmote.FirstOrDefault((KeyValuePair<UnlockableEmote, EmoteController> x) => (Object)(object)x.Value == (Object)(object)emoteController).Key; leadEmoteControllerByEmote.Remove(key); foreach (EmoteController item in syncGroup) { if ((Object)(object)item == (Object)null || item.performingEmote == null || item.performingEmote != key) { continue; } leadEmoteControllerByEmote[key] = item; break; } } if (currentEmoteAudioSources == null) { return; } if (currentEmoteAudioSources.ContainsValue(emoteController.personalEmoteAudioSource)) { emoteController.personalEmoteAudioSource.StopAudio(); emoteController.personalEmoteAudioSource.RemoveFromEmoteSyncGroup(); UnlockableEmote key2 = currentEmoteAudioSources.FirstOrDefault((KeyValuePair<UnlockableEmote, EmoteAudioSource> x) => (Object)(object)x.Value == (Object)(object)emoteController.personalEmoteAudioSource).Key; currentEmoteAudioSources.Remove(key2); if (useAudio && key2.hasAudio && !key2.isBoomboxAudio) { foreach (EmoteController item2 in syncGroup) { if (!((Object)(object)item2 == (Object)null) && item2.performingEmote != null && !((Object)(object)item2 == (Object)(object)emoteController)) { EmoteAudioSource personalEmoteAudioSource = item2.personalEmoteAudioSource; if (item2.performingEmote == key2 && (Object)(object)personalEmoteAudioSource != (Object)null && personalEmoteAudioSource.CanPlayMusic()) { currentEmoteAudioSources[key2] = personalEmoteAudioSource; personalEmoteAudioSource.SyncWithEmoteControllerAudio(item2); break; } } } } } UpdateAudioVolume(); } public void DestroyEmoteSyncGroup() { CustomLogging.LogVerbose("Cleaning up emote sync group with id: " + syncId); if (currentEmoteAudioSources != null) { foreach (EmoteAudioSource value in currentEmoteAudioSources.Values) { if ((Object)(object)value != (Object)null) { value.StopAudio(); value.RemoveFromEmoteSyncGroup(); } } currentEmoteAudioSources = null; } if ((Object)(object)currentAudioPlayer != (Object)null) { currentAudioPlayer.StopAudio(); currentAudioPlayer.RemoveFromEmoteSyncGroup(); } allEmoteSyncGroups.Remove(syncId); syncGroup = null; syncId = -1; } public void UpdateAudioVolume() { if (currentEmoteAudioSources != null) { foreach (EmoteAudioSource value in currentEmoteAudioSources.Values) { if ((Object)(object)value != (Object)null) { value.UpdateVolume(); } } } if ((Object)(object)currentAudioPlayer != (Object)null) { currentAudioPlayer.UpdateVolume(); } } public void AssignExternalAudioPlayer(EmoteAudioPlayer audioPlayer) { if ((Object)(object)currentAudioPlayer != (Object)null) { currentAudioPlayer.StopAudio(); currentAudioPlayer.RemoveFromEmoteSyncGroup(); currentAudioPlayer = null; } if (useAudio && performingEmote.hasAudio && performingEmote.isBoomboxAudio) { currentAudioPlayer = audioPlayer; if ((Object)(object)currentAudioPlayer != (Object)null) { currentAudioPlayer.SyncWithEmoteControllerAudio(leadEmoteController); currentAudioPlayer.AddToEmoteSyncGroup(this); currentAudioPlayer.UpdateVolume(); } } } } internal static class HelperTools { public static NetworkManager networkManager => NetworkManager.Singleton; public static bool isClient => networkManager.IsClient; public static bool isServer => networkManager.IsServer; public static bool isHost => networkManager.IsHost; public static PlayerControllerB localPlayerController => StartOfRound.Instance?.localPlayerController; public static QuickMenuManager quickMenuManager => localPlayerController?.quickMenuManager; public static TextMeshProUGUI[] controlTipLines => HUDManager.Instance?.controlTipLines; public static List<Item> allItems => StartOfRound.Instance?.allItemsList?.itemsList; public static SelectableLevel[] selectableLevels => StartOfRound.Instance?.levels; public static string currentSaveFileName => GameNetworkManager.Instance?.currentSaveFileName; public static int groupCredits => ((Object)(object)TerminalPatcher.terminalInstance != (Object)null) ? groupCredits : (-1); public static int currentEmoteCredits => TerminalPatcher.currentEmoteCredits; public static EmoteControllerPlayer emoteControllerLocal => EmoteControllerPlayer.emoteControllerLocal; public static bool TryGetPlayerByClientId(ulong clientId, out PlayerControllerB playerController) { playerController = null; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (val.actualClientId == clientId) { playerController = val; break; } } return (Object)(object)playerController != (Object)null; } public static bool TryGetPlayerByUsername(string username, out PlayerControllerB playerController) { playerController = null; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (val.playerUsername == username) { playerController = val; break; } } return (Object)(object)playerController != (Object)null; } public static EmoteController GetEmoteControllerById(ulong id) { foreach (EmoteController value in EmoteController.allEmoteControllers.Values) { if (value.emoteControllerId == id) { return value; } } return null; } public static GrabbableObject GetHeldGrabbable(this PlayerControllerB playerController) { int currentItemSlot = playerController.currentItemSlot; return (currentItemSlot == 50) ? playerController.ItemOnlySlot : playerController.ItemSlots[currentItemSlot]; } } [HarmonyPatch] public class EmoteControllerMaskedEnemy : EmoteController { public static Dictionary<MaskedPlayerEnemy, EmoteControllerMaskedEnemy> allMaskedEnemyEmoteControllers = new Dictionary<MaskedPlayerEnemy, EmoteControllerMaskedEnemy>(); public MaskedPlayerEnemy maskedEnemy; public int emoteCount = 0; public bool stoppedAndStaring = false; public bool behaviour1 = false; public Vector3 emotedAtPosition; public int id => (int)((NetworkBehaviour)maskedEnemy).NetworkObjectId; public float stopAndStareTimer { get { return (float)Traverse.Create((object)maskedEnemy).Field("stopAndStareTimer").GetValue(); } set { Traverse.Create((object)maskedEnemy).Field("stopAndStareTimer").SetValue((object)value); } } public NavMeshAgent agent => ((EnemyAI)maskedEnemy).agent; public PlayerControllerB lookingAtPlayer { get { Transform stareAtTransform = maskedEnemy.stareAtTransform; return (stareAtTransform != null) ? ((Component)stareAtTransform).GetComponentInParent<PlayerControllerB>() : null; } } public bool inKillAnimation => (bool)Traverse.Create((object)maskedEnemy).Field("inKillAnimation").GetValue(); public bool handsOut => (bool)Traverse.Create((object)maskedEnemy).Field("handsOut").GetValue(); public float localSpeed { get { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Vector3 val = (Vector3)Traverse.Create((object)maskedEnemy).Field("agentLocalVelocity").GetValue(); return ((Vector3)(ref val)).magnitude; } } public bool isMoving => animator.GetBool("IsMoving"); public override void Initialize(string sourceRootBoneName = "metarig") { base.Initialize(); if (initialized) { maskedEnemy = ((Component)this).GetComponentInParent<MaskedPlayerEnemy>(); if ((Object)(object)maskedEnemy == (Object)null) { CustomLogging.LogError("Failed to find MaskedPlayerEnemy component in parent of EmoteControllerMaskedEnemy."); } else { allMaskedEnemyEmoteControllers.Add(maskedEnemy, this); } } } protected override void OnDestroy() { base.OnDestroy(); allMaskedEnemyEmoteControllers?.Remove(maskedEnemy); } protected override bool CheckIfShouldStopEmoting() { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (!isPerformingEmote) { return false; } if (base.CheckIfShouldStopEmoting()) { return true; } return ((EnemyAI)maskedEnemy).isEnemyDead || (NetworkManager.Singleton.IsServer && (agent.speed > 0f || stopAndStareTimer <= 0f)) || (!NetworkManager.Singleton.IsServer && Vector3.Distance(emotedAtPosition, ((Component)maskedEnemy).transform.position) > 0.01f) || inKillAnimation; } public override bool IsPerformingCustomEmote() { return base.IsPerformingCustomEmote(); } public override bool CanPerformEmote() { return base.CanPerformEmote() && (Object)(object)lookingAtPlayer != (Object)null && (!NetworkManager.Singleton.IsServer || stopAndStareTimer >= 2f) && !inKillAnimation && ((NetworkManager.Singleton.IsServer && agent.speed == 0f) || (!NetworkManager.Singleton.IsServer && !isMoving)) && !((EnemyAI)maskedEnemy).isEnemyDead; } public override bool PerformEmote(UnlockableEmote emote, int overrideEmoteId = -1, bool doNotTriggerAudio = false) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) bool result = base.PerformEmote(emote, overrideEmoteId, doNotTriggerAudio); if (isPerformingEmote) { emoteCount++; emotedAtPosition = ((Component)maskedEnemy).transform.position; } return result; } public override void StopPerformingEmote() { base.StopPerformingEmote(); stoppedAndStaring = false; } protected override void CreateBoneMap() { boneMap = BoneMapper.CreateBoneMap(humanoidSkeleton, metarig, EmoteControllerPlayer.sourceBoneNames); } protected override ulong GetEmoteControllerId() { return ((Object)(object)maskedEnemy != (Object)null) ? ((NetworkBehaviour)maskedEnemy).NetworkObjectId : 0; } protected override string GetEmoteControllerName() { return ((Object)(object)maskedEnemy != (Object)null) ? ((Object)maskedEnemy).name : base.GetEmoteControllerName(); } } [DefaultExecutionOrder(-2)] public class EmoteControllerPlayer : EmoteController { public static Dictionary<PlayerControllerB, EmoteControllerPlayer> allPlayerEmoteControllers = new Dictionary<PlayerControllerB, EmoteControllerPlayer>(); public PlayerControllerB playerController; public Animator originalAnimator; private Dictionary<Transform, Transform> boneMapLocalPlayerArms; internal Transform humanoidHead; private Transform cameraContainerTarget; private Transform cameraContainerLerp; public static List<string> sourceBoneNames = new List<string> { "spine", "spine.001", "spine.002", "spine.003", "spine.004", "shoulder.L", "arm.L_upper", "arm.L_lower", "hand.L", "finger1.L", "finger1.L.001", "finger2.L", "finger2.L.001", "finger3.L", "finger3.L.001", "finger4.L", "finger4.L.001", "finger5.L", "finger5.L.001", "shoulder.R", "arm.R_upper", "arm.R_lower", "hand.R", "finger1.R", "finger1.R.001", "finger2.R", "finger2.R.001", "finger3.R", "finger3.R.001", "finger4.R", "finger4.R.001", "finger5.R", "finger5.R.001", "thigh.L", "shin.L", "foot.L", "heel.02.L", "toe.L", "thigh.R", "shin.R", "foot.R", "heel.02.R", "toe.R" }; public GrabbablePropObject sourceGrabbableEmoteProp; internal static float timeLastPeformedEmoteLocalPlayer = 0f; public static EmoteControllerPlayer emoteControllerLocal => ((Object)(object)HelperTools.localPlayerController != (Object)null && allPlayerEmoteControllers.ContainsKey(HelperTools.localPlayerController)) ? allPlayerEmoteControllers[HelperTools.localPlayerController] : null; public bool isLocalPlayer => (Object)(object)playerController == (Object)(object)StartOfRound.Instance?.localPlayerController; public ulong clientId => playerController.actualClientId; public ulong playerId => playerController.playerClientId; public ulong steamId => playerController.playerSteamId; public string username => playerController.playerUsername; public float timeSinceStartingEmote { get { return (float)Traverse.Create((object)playerController).Field("timeSinceStartingEmote").GetValue(); } set { Traverse.Create((object)playerController).Field("timeSinceStartingEmote").SetValue((object)value); } } public override void Initialize(string sourceRootBoneName = "metarig") { base.Initialize(); if (initialized) { originalAnimator = ((Component)metarig).GetComponentInChildren<Animator>(); playerController = ((Component)this).GetComponentInParent<PlayerControllerB>(); if ((Object)(object)playerController == (Object)null) { CustomLogging.LogError("Failed to find PlayerControllerB component in parent of EmoteControllerPlayer."); } else { allPlayerEmoteControllers.Add(playerController, this); } } } protected override void Start() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) base.Start(); if (initialized) { Transform val = FindChildRecursive("spine.004", metarig); if ((Object)(object)val != (Object)null) { cameraContainerTarget = new GameObject("CameraContainer_Target").transform; cameraContainerTarget.SetParent(val); cameraContainerTarget.localPosition = new Vector3(0f, 0.22f, 0f); cameraContainerTarget.localEulerAngles = new Vector3(-3f, 0f, 0f); cameraContainerLerp = new GameObject("CameraContainer_Lerp").transform; cameraContainerLerp.SetParent(humanoidSkeleton); cameraContainerLerp.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); } humanoidHead = FindChildRecursive("head", humanoidSkeleton); if (!Object.op_Implicit((Object)(object)humanoidHead)) { CustomLogging.LogError("Failed to find Head on: " + base.emoteControllerName); } } } protected override void OnDestroy() { base.OnDestroy(); allPlayerEmoteControllers?.Remove(playerController); } protected override void Update() { if (initialized && !((Object)(object)playerController == (Object)null) && (!((Object)(object)playerController == (Object)(object)HelperTools.localPlayerController) || (!ConfigSettings.disableEmotesForSelf.Value && !LCVR_Compat.LoadedAndEnabled))) { base.Update(); } } protected override void LateUpdate() { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (!initialized || (Object)(object)playerController == (Object)null || ((Object)(object)playerController == (Object)(object)HelperTools.localPlayerController && (ConfigSettings.disableEmotesForSelf.Value || LCVR_Compat.LoadedAndEnabled))) { return; } bool flag = isPerformingEmote; base.LateUpdate(); if (flag && !isPerformingEmote && playerController.performingEmote) { playerController.performingEmote = false; originalAnimator.SetInteger("emoteNumber", 0); AnimatorStateInfo currentAnimatorStateInfo = originalAnimator.GetCurrentAnimatorStateInfo(0); animator.Play(((AnimatorStateInfo)(ref currentAnimatorStateInfo)).fullPathHash, 0, 0f); if (isLocalPlayer) { timeSinceStartingEmote = 0f; playerController.StopPerformingEmoteServerRpc(); } } } protected override void TranslateAnimation() { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) if (!initialized || !isPerformingEmote || (Object)(object)playerController == (Object)null) { return; } base.TranslateAnimation(); if (Object.op_Implicit((Object)(object)humanoidHead) && Object.op_Implicit((Object)(object)cameraContainerLerp) && Object.op_Implicit((Object)(object)cameraContainerTarget)) { cameraContainerLerp.position = Vector3.Lerp(cameraContainerLerp.position, cameraContainerTarget.position, 25f * Time.deltaTime); cameraContainerLerp.rotation = Quaternion.Lerp(cameraContainerLerp.rotation, cameraContainerTarget.rotation, 25f * Time.deltaTime); if (!isLocalPlayer || !ThirdPersonEmoteController.firstPersonEmotesEnabled || !ThirdPersonEmoteController.isMovingWhileEmoting) { playerController.cameraContainerTransform.position = cameraContainerLerp.position; playerController.cameraContainerTransform.rotation = cameraContainerLerp.rotation; } if (isLocalPlayer) { playerController.localVisor.position = playerController.localVisorTargetPoint.position; playerController.localVisor.rotation = playerController.localVisorTargetPoint.rotation; } } if (!isLocalPlayer) { return; } playerController.playerModelArmsMetarig.rotation = playerController.localArmsRotationTarget.rotation; if (boneMapLocalPlayerArms == null) { return; } foreach (KeyValuePair<Transform, Transform> boneMapLocalPlayerArm in boneMapLocalPlayerArms) { Transform key = boneMapLocalPlayerArm.Key; Transform value = boneMapLocalPlayerArm.Value; if (!((Object)(object)key == (Object)null) && !((Object)(object)value == (Object)null)) { ((Component)value).transform.position = ((Component)key).transform.position; ((Component)value).transform.rotation = ((Component)key).transform.rotation; } } } protected override bool CheckIfShouldStopEmoting() { if ((Object)(object)playerController == (Object)null || !isPerformingEmote) { return false; } if (base.CheckIfShouldStopEmoting() || !playerController.performingEmote || performingEmote == null) { return true; } GrabbableObject heldGrabbable = playerController.GetHeldGrabbable(); if ((Object)(object)sourceGrabbableEmoteProp != (Object)null && (Object)(object)sourceGrabbableEmoteProp != (Object)(object)heldGrabbable) { return true; } return false; } public override bool IsPerformingCustomEmote() { return base.IsPerformingCustomEmote(); } public bool TryPerformingEmoteLocal(UnlockableEmote emote, int overrideEmoteId = -1, GrabbablePropObject sourcePropObject = null) { if (!initialized || ConfigSettings.disableEmotesForSelf.Value || LCVR_Compat.LoadedAndEnabled) { return false; } if (!isLocalPlayer) { CustomLogging.LogWarning("Cannot run TryPerformEmoteLocal on a character who does not belong to the local player. This is not allowed."); return false; } if (Time.time - timeLastPeformedEmoteLocalPlayer < 0.25f) { return false; } CustomLogging.Log("Attempting to perform emote on local player."); if (!CanPerformEmote()) { return false; } if (overrideEmoteId >= 0 && (emote.emoteSyncGroup == null || emote.emoteSyncGroup.Count <= 1 || overrideEmoteId < 0 || overrideEmoteId >= emote.emoteSyncGroup.Count)) { overrideEmoteId = -1; } if (emote.emoteSyncGroup != null && emote.emoteSyncGroup.Count > 1) { if (emote.randomEmote) { if (overrideEmoteId < 0) { overrideEmoteId = Random.Range(0, emote.emoteSyncGroup.Count); } } else { emote = emote.emoteSyncGroup[0]; } } if (overrideEmoteId >= 0 && emote.emoteSyncGroup != null && overrideEmoteId < emote.emoteSyncGroup.Count) { emote = emote.emoteSyncGroup[overrideEmoteId]; } else { overrideEmoteId = -1; } EmoteController emoteController = null; if (isPerformingEmote && performingEmote.IsEmoteInEmoteGroup(emote) && (!performingEmote.randomEmote || performingEmote.loopable)) { if (performingEmote.emoteSyncGroup != null && performingEmote.emoteSyncGroup.Count > 1) { overrideEmoteId = (performingEmote.emoteSyncGroup.IndexOf(performingEmote) + 1) % performingEmote.emoteSyncGroup.Count; if (performingEmote.emoteSyncGroup[overrideEmoteId] != null) { emote = performingEmote.emoteSyncGroup[overrideEmoteId]; } } if (emoteSyncGroup?.syncGroup != null && emoteSyncGroup.syncGroup.Count > 1) { if (emoteSyncGroup.syncGroup.Count > 1 && (performingEmote?.emoteSyncGroup == null || performingEmote.emoteSyncGroup.Count <= 1)) { timeLastPeformedEmoteLocalPlayer = Time.time; return true; } foreach (EmoteController item in emoteSyncGroup.syncGroup) { if ((Object)(object)item != (Object)(object)this) { emoteController = item; break; } } } } if ((Object)(object)emoteController != (Object)null) { return TrySyncingEmoteWithEmoteController(emoteController, overrideEmoteId); } CustomLogging.LogWarningVerbose("Trying to perform emote on local player. Emote: " + emote.emoteName + " | Emote id: " + emote.emoteId); bool result = ((!((Object)(object)sourcePropObject != (Object)null) || !((Object)(object)sourcePropObject == (Object)(object)HelperTools.localPlayerController.GetHeldGrabbable())) ? PerformEmote(emote, overrideEmoteId, AudioManager.emoteOnlyMode) : PerformEmote(emote, sourcePropObject, overrideEmoteId, AudioManager.emoteOnlyMode)); playerController.StartPerformingEmoteServerRpc(); SyncPerformingEmoteManager.SendPerformingEmoteUpdateToServer(emote, AudioManager.emoteOnlyMode); timeSinceStartingEmote = 0f; playerController.performingEmote = true; return result; } public bool TrySyncingEmoteWithEmoteController(EmoteController emoteController, int overrideEmoteId = -1) { if (!initialized || (Object)(object)emoteController == (Object)null || ConfigSettings.disableEmotesForSelf.Value || LCVR_Compat.LoadedAndEnabled) { return false; } if (!isLocalPlayer) { CustomLogging.LogWarning("Cannot run TrySyncingEmoteWithEmoteController on a character who does not belong to the local player. This is not allowed."); return false; } if (Time.time - timeLastPeformedEmoteLocalPlayer < 0.25f) { return false; } CustomLogging.Log("Attempting to sync emote for player: " + ((Object)playerController).name + " with emote controller with id: " + emoteController.emoteControllerId); if (!CanPerformEmote() || !emoteController.IsPerformingCustomEmote()) { return false; } if (overrideEmoteId >= 0 && (emoteController.performingEmote?.emoteSyncGroup == null || overrideEmoteId >= emoteController.performingEmote.emoteSyncGroup.Count || emoteController.performingEmote.emoteSyncGroup[overrideEmoteId] == null)) { overrideEmoteId = -1; } SyncWithEmoteController(emoteController, overrideEmoteId); if (performingEmote != null) { if (performingEmote.inEmoteSyncGroup) { overrideEmoteId = performingEmote.emoteSyncGroup.IndexOf(performingEmote); } playerController.StartPerformingEmoteServerRpc(); SyncPerformingEmoteManager.SendSyncEmoteUpdateToServer(emoteController, overrideEmoteId); timeSinceStartingEmote = 0f; playerController.performingEmote = true; timeLastPeformedEmoteLocalPlayer = Time.time; return true; } return false; } public override bool CanPerformEmote() { if (!isLocalPlayer) { return true; } if (!initialized || ConfigSettings.disableEmotesForSelf.Value || LCVR_Compat.LoadedAndEnabled) { return false; } bool flag = base.CanPerformEmote(); MethodInfo method = ((object)playerController).GetType().GetMethod("CheckConditionsForEmote", BindingFlags.Instance | BindingFlags.NonPublic); flag &= (bool)method.Invoke(playerController, new object[0]); bool flag2 = (Object)(object)playerController.inAnimationWithEnemy == (Object)null && (!isLocalPlayer || !CentipedePatcher.IsCentipedeLatchedOntoLocalPlayer()); return flag && flag2; } [HarmonyPatch(typeof(PlayerControllerB), "SwitchToItemSlot")] [HarmonyPostfix] private static void OnSwapItem(int slot, PlayerControllerB __instance) { if (allPlayerEmoteControllers.TryGetValue(__instance, out var value) && value.IsPerformingCustomEmote()) { GrabbableObject heldGrabbable = __instance.GetHeldGrabbable(); if ((Object)(object)value.sourceGrabbableEmoteProp != (Object)null && (Object)(object)value.sourceGrabbableEmoteProp != (Object)(object)heldGrabbable) { value.StopPerformingEmote(); } else if (Object.op_Implicit((Object)(object)heldGrabbable) && value.emotingProps.Count > 0) { heldGrabbable.EnableItemMeshes(false); } } } public bool PerformEmote(UnlockableEmote emote, GrabbablePropObject sourcePropObject, int overrideEmoteId = -1, bool doNotTriggerAudio = false) { if ((Object)(object)sourcePropObject != (Object)null && (Object)(object)sourcePropObject == (Object)(object)playerController.GetHeldGrabbable()) { sourceGrabbableEmoteProp = sourcePropObject; } bool result = PerformEmote(emote, overrideEmoteId, doNotTriggerAudio); if (isPerformingEmote) { if (!isLocalPlayer && SyncManager.isSynced && ConfigSync.instance.syncPersistentUnlocksGlobal && !SessionManager.unlockedEmotesByPlayer.TryGetValue(playerController.playerUsername, out var value) && !value.Contains(performingEmote)) { SessionManager.UnlockEmoteLocal(emote.emoteId, purchased: false, playerController.playerUsername); } } else { StopPerformingEmote(); } return result; } public override bool PerformEmote(UnlockableEmote emote, int overrideEmoteId = -1, bool doNotTriggerAudio = false) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)playerController == (Object)null || (isLocalPlayer && (ConfigSettings.disableEmotesForSelf.Value || LCVR_Compat.LoadedAndEnabled))) { return false; } bool result = base.PerformEmote(emote, overrideEmoteId, doNotTriggerAudio); if (isPerformingEmote) { cameraContainerLerp.SetPositionAndRotation(cameraContainerTarget.position, cameraContainerTarget.rotation); playerController.performingEmote = true; originalAnimator.SetInteger("emoteNumber", 1); GrabbableObject heldGrabbable = playerController.GetHeldGrabbable(); if (Object.op_Implicit((Object)(object)heldGrabbable) && emotingProps.Count > 0) { heldGrabbable.EnableItemMeshes(false); } if (isLocalPlayer) { ThirdPersonEmoteController.OnStartCustomEmoteLocal(); playerController.StartPerformingEmoteServerRpc(); } } return result; } public override bool SyncWithEmoteController(EmoteController emoteController, int overrideEmoteId = -1) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)playerController == (Object)null || (isLocalPlayer && (ConfigSettings.disableEmotesForSelf.Value || LCVR_Compat.LoadedAndEnabled))) { return false; } bool result = base.SyncWithEmoteController(emoteController, overrideEmoteId); if (isPerformingEmote) { cameraContainerLerp.SetPositionAndRotation(cameraContainerTarget.position, cameraContainerTarget.rotation); playerController.performingEmote = true; originalAnimator.SetInteger("emoteNumber", 1); if (isLocalPlayer) { ThirdPersonEmoteController.OnStartCustomEmoteLocal(); playerController.StartPerformingEmoteServerRpc(); } } return result; } public override void StopPerformingEmote() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)playerController == (Object)null || (isLocalPlayer && ConfigSettings.disableEmotesForSelf.Value)) { return; } base.StopPerformingEmote(); cameraContainerLerp.SetPositionAndRotation(cameraContainerTarget.position, cameraContainerTarget.rotation); GrabbableObject heldGrabbable = playerController.GetHeldGrabbable(); if (Object.op_Implicit((Object)(object)heldGrabbable)) { heldGrabbable.EnableItemMeshes(true); } if ((Object)(object)sourceGrabbableEmoteProp != (Object)null) { if (sourceGrabbableEmoteProp.isPerformingEmote) { sourceGrabbableEmoteProp.StopEmote(); } sourceGrabbableEmoteProp = null; } playerController.playerBodyAnimator.SetInteger("emote_number", 0); playerController.performingEmote = false; playerController.playerBodyAnimator.Update(0f); if (isLocalPlayer) { ThirdPersonEmoteController.OnStopCustomEmoteLocal(); ((Component)playerController.gameplayCamera).transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); playerController.StopPerformingEmoteServerRpc(); } } public override void ResetPerformingEmote() { if ((Object)(object)playerController == (Object)null || (isLocalPlayer && ConfigSettings.disableEmotesForSelf.Value)) { return; } base.ResetPerformingEmote(); if ((Object)(object)sourceGrabbableEmoteProp != (Object)null) { if (sourceGrabbableEmoteProp.isPerformingEmote) { sourceGrabbableEmoteProp.StopEmote(); } sourceGrabbableEmoteProp = null; } GrabbableObject heldGrabbable = playerController.GetHeldGrabbable(); if (Object.op_Implicit((Object)(object)heldGrabbable)) { heldGrabbable.EnableItemMeshes(true); } } protected override void CreateBoneMap() { boneMap = BoneMapper.CreateBoneMap(humanoidSkeleton, metarig, sourceBoneNames); List<string> list = new List<string> { "arm.L_upper", "arm.L_lower", "hand.L", "finger1.L", "finger1.L.001", "finger2.L", "finger2.L.001", "finger3.L", "finger3.L.001", "finger4.L", "finger4.L.001", "finger5.L", "finger5.L.001", "arm.R_upper", "arm.R_lower", "hand.R", "finger1.R", "finger1.R.001", "finger2.R", "finger2.R.001", "finger3.R", "finger3.R.001", "finger4.R", "finger4.R.001", "finger5.R", "finger5.R.001" }; boneMapLocalPlayerArms = BoneMapper.CreateBoneMap(humanoidSkeleton, playerController.localArmsTransform, list); } protected override ulong GetEmoteControllerId() { return ((Object)(object)playerController != (Object)null) ? ((NetworkBehaviour)playerController).NetworkObjectId : 0; } protected override string GetEmoteControllerName() { return ((Object)(object)playerController != (Object)null) ? playerController.playerUsername : base.GetEmoteControllerName(); } } [HarmonyPatch] [DefaultExecutionOrder(-2)] public class EmoteController : MonoBehaviour { public static Dictionary<GameObject, EmoteController> allEmoteControllers = new Dictionary<GameObject, EmoteController>(); public bool initialized = false; public Transform metarig; public Transform humanoidSkeleton; public Animator animator; public AnimatorOverrideController animatorController; public bool isPerformingEmote = false; public UnlockableEmote performingEmote; protected Dictionary<Transform, Transform> boneMap; public List<Transform> groundContactPoints = new List<Transform>(); public Transform propsParent; public List<PropObject> emotingProps = new List<PropObject>(); public Transform ikLeftHand; public Transform ikRightHand; public Transform ikLeftFoot; public Transform ikRightFoot; public Transform ikHead; public EmoteSyncGroup emoteSyncGroup; public EmoteAudioSource personalEmoteAudioSource; private float timePerformedEmote = 0f; public bool smoothTransitionToEmote = false; public ulong emoteControllerId => GetEmoteControllerId(); public string emoteControllerName => GetEmoteControllerName(); public float currentAnimationTimeNormalized { get { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); return ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime; } } public float currentAnimationTime { get { AnimationClip currentAnimationClip = GetCurrentAnimationClip(); return ((Object)(object)currentAnimationClip != (Object)null) ? (currentAnimationClip.length * (currentAnimationTimeNormalized % 1f)) : 0f; } } public int currentStateHash { get { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); return ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).shortNameHash; } } public bool isLooping { get { return animator.GetBool("loop"); } set { animator.SetBool("loop", value); } } public bool isAnimatorInLoopingState { get { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); return ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).IsName("emote_loop"); } } public bool isSimpleEmoteController => ((object)this).GetType() == typeof(EmoteController); public int emoteSyncId => (emoteSyncGroup != null) ? emoteSyncGroup.syncId : (-1); protected virtual void Awake() { if (!initialized) { Initialize(); } } public virtual void Initialize(string sourceRootBoneName = "metarig") { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Expected O, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) if (initialized || (Object)(object)Plugin.humanoidSkeletonPrefab == (Object)null || (Object)(object)Plugin.humanoidAnimatorController == (Object)null || (Object)(object)Plugin.humanoidAvatar == (Object)null) { return; } try { metarig = FindChildRecursive(sourceRootBoneName, ((Component)this).transform); humanoidSkeleton = Object.Instantiate<GameObject>(Plugin.humanoidSkeletonPrefab, metarig.parent).transform; ((Object)humanoidSkeleton).name = "HumanoidSkeleton"; humanoidSkeleton.SetSiblingIndex(metarig.GetSiblingIndex() + 1); animator = ((Component)humanoidSkeleton).GetComponent<Animator>(); OnAnimatorIKHandler onAnimatorIKHandler = ((Component)animator).gameObject.AddComponent<OnAnimatorIKHandler>(); onAnimatorIKHandler.SetParentEmoteController(this); animatorController = new AnimatorOverrideController(Plugin.humanoidAnimatorController); animator.runtimeAnimatorController = (RuntimeAnimatorController)(object)animatorController; humanoidSkeleton.SetLocalPositionAndRotation(metarig.localPosition + Vector3.down * 0.025f, Quaternion.identity); humanoidSkeleton.localScale = metarig.localScale; if (!isSimpleEmoteController) { allEmoteControllers.Add(((Component)this).gameObject, this); GameObject val = new GameObject("PersonalEmoteAudioSource"); val.transform.SetParent(humanoidSkeleton); val.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); val.transform.localScale = Vector3.one; personalEmoteAudioSource = val.AddComponent<EmoteAudioSource>(); } if ((Object)(object)propsParent == (Object)null) { propsParent = ((Component)this).transform.Find("EmoteProps"); if ((Object)(object)propsParent == (Object)null) { propsParent = new GameObject("EmoteProps").transform; propsParent.SetParent(humanoidSkeleton); propsParent.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); propsParent.localScale = Vector3.one; } } initialized = true; } catch (Exception ex) { Debug.LogError((object)("Failed to initialize EmoteController. Error: " + ex)); } } protected virtual void Start() { if (!initialized) { return; } if (boneMap == null) { if (!isSimpleEmoteController) { CreateBoneMap(); } else { Debug.LogWarning((object)"Using the base emote controller. Remember that when doing this, the bonemap will need to be built manually."); } } FindIkBones(); } protected virtual void OnEnable() { } protected virtual void OnDisable() { if (isPerformingEmote) { StopPerformingEmote(); } } protected virtual void OnDestroy() { if (isPerformingEmote) { StopPerformingEmote(); } allEmoteControllers?.Remove(((Component)this).gameObject); if (SyncPerformingEmoteManager.doNotTriggerAudioDict.ContainsKey(this)) { SyncPerformingEmoteManager.doNotTriggerAudioDict.Remove(this); } } protected virtual void Update() { if (initialized) { } } protected virtual void LateUpdate() { if (initialized) { if (isPerformingEmote && CheckIfShouldStopEmoting()) { StopPerformingEmote(); } if (!((Object)(object)animator == (Object)null) && !((Object)(object)animatorController == (Object)null) && boneMap != null && isPerformingEmote) { TranslateAnimation(); } } } protected virtual void TranslateAnimation() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) if (performingEmote == null || boneMap == null || boneMap.Count <= 0) { return; } foreach (KeyValuePair<Transform, Transform> item in boneMap) { Transform key = item.Key; Transform value = item.Value; if (!((Object)(object)key == (Object)null) && !((Object)(object)value == (Object)null)) { float num = Time.time - timePerformedEmote; float num2 = (smoothTransitionToEmote ? Mathf.Clamp01(num / 0.2f) : 1f); ((Component)value).transform.position = Vector3.Lerp(((Component)value).transform.position, ((Component)key).transform.position, num2); ((Component)value).transform.rotation = Quaternion.Slerp(((Component)value).transform.rotation, ((Component)key).transform.rotation, num2); } } } protected virtual bool CheckIfShouldStopEmoting() { if (isPerformingEmote) { return performingEmote == null || (!performingEmote.loopable && !performingEmote.isPose && currentAnimationTimeNormalized >= 1f); } return false; } protected virtual void CorrectVerticalPosition() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if (groundContactPoints == null) { return; } float num = 0f; foreach (Transform groundContactPoint in groundContactPoints) { num = Mathf.Min(num, ((Component)groundContactPoint).transform.position.y - metarig.position.y); } if (num < 0f) { metarig.position = new Vector3(metarig.position.x, metarig.position.y - num, metarig.position.z); } } public virtual bool IsPerformingCustomEmote() { return isPerformingEmote && performingEmote != null; } public virtual bool CanPerformEmote() { return initialized && (Object)(object)animator != (Object)null && ((Behaviour)animator).enabled; } public virtual bool PerformEmote(UnlockableEmote emote, int overrideEmoteId = -1, bool doNotTriggerAudio = false) { if (!initialized || !CanPerformEmote()) { return false; } if (!isSimpleEmoteController) { CustomLogging.Log("[" + emoteControllerName + "] Performing emote: " + emote.emoteName); } if (isPerformingEmote) { ResetPerformingEmote(); } if (emote.emoteSyncGroup != null) { if (overrideEmoteId >= 0 && overrideEmoteId < emote.emoteSyncGroup.Count && emote.emoteSyncGroup[overrideEmoteId] != null) { emote = emote.emoteSyncGroup[overrideEmoteId]; } else if (emote.randomEmote) { int index = Random.Range(0, emote.emoteSyncGroup.Count); UnlockableEmote unlockableEmote = emote.emoteSyncGroup[index]; if (unlockableEmote != null) { emote = unlockableEmote; } } } animatorController["emote"] = emote.animationClip; if ((Object)(object)emote.transitionsToClip != (Object)null) { animatorController["emote_loop"] = emote.transitionsToClip; } animator.SetBool("loop", (Object)(object)emote.transitionsToClip != (Object)null); animator.Play("emote", 0, 0f); animator.Update(0f); performingEmote = emote; isPerformingEmote = true; timePerformedEmote = Time.time; RecordStartingBonePositions(); PerformEmoteProps(); if (!isSimpleEmoteController) { CreateEmoteSyncGroup(doNotTriggerAudio); } DiscoBallPatcher.OnPerformEmote(this); return true; } public virtual bool SyncWithEmoteController(EmoteController emoteController, int overrideEmoteId = -1) { if (!initialized || !CanPerformEmote() || (Object)(object)emoteController == (Object)null || !emoteController.IsPerformingCustomEmote()) { return false; } if (!isSimpleEmoteController) { CustomLogging.Log("[" + emoteControllerName + "] Attempting to sync with emote controller: " + ((Object)emoteController).name + " Emote: " + emoteController.performingEmote.emoteName + " PlayEmoteAtTimeNormalized: " + emoteController.currentAnimationTimeNormalized % 1f); } if (isPerformingEmote) { ResetPerformingEmote(); } EmoteSyncGroup emoteSyncGroup = emoteController.emoteSyncGroup; if (emoteSyncGroup == null) { CustomLogging.LogWarning("[" + emoteControllerName + "] Attempted to sync with emote controller who is not a part of an emote sync group. Continuing anyways."); } UnlockableEmote unlockableEmote = emoteController.performingEmote; if (unlockableEmote.emoteSyncGroup != null) { if (overrideEmoteId >= 0 && overrideEmoteId < unlockableEmote.emoteSyncGroup.Count && unlockableEmote.emoteSyncGroup[overrideEmoteId] != null) { unlockableEmote = unlockableEmote.emoteSyncGroup[overrideEmoteId]; } else if (unlockableEmote.randomEmote) { if (unlockableEmote.hasAudio && !unlockableEmote.isBoomboxAudio) { unlockableEmote = emoteController.performingEmote; } else { int index = Random.Range(0, unlockableEmote.emoteSyncGroup.Count); UnlockableEmote unlockableEmote2 = unlockableEmote.emoteSyncGroup[index]; if (unlockableEmote2 != null) { unlockableEmote = unlockableEmote2; } } } else { bool flag = false; foreach (UnlockableEmote item in unlockableEmote.emoteSyncGroup) { if (!emoteSyncGroup.leadEmoteControllerByEmote.ContainsKey(item) || (Object)(object)emoteSyncGroup.leadEmoteControllerByEmote[unlockableEmote] == (Object)null) { unlockableEmote = item; flag = true; break; } } if (!flag) { int num = unlockableEmote.emoteSyncGroup.IndexOf(unlockableEmote); if (num >= 0) { num = (num + 1) % unlockableEmote.emoteSyncGroup.Count; unlockableEmote = unlockableEmote.emoteSyncGroup[num]; } } } } AnimationClip currentAnimationClip = emoteController.GetCurrentAnimationClip(); if (!unlockableEmote.ClipIsInEmote(currentAnimationClip)) { CustomLogging.LogError("[" + emoteControllerName + "] Attempted to sync with emote controller whose animation clip is not a part of their performing emote? Emote: " + emoteController.performingEmote?.ToString() + " AnimationClip: " + ((Object)currentAnimationClip).name); return false; } animatorController["emote"] = unlockableEmote.animationClip; if ((Object)(object)unlockableEmote.transitionsToClip != (Object)null) { animatorController["emote_loop"] = unlockableEmote.transitionsToClip; } float num2 = emoteController.currentAnimationTimeNormalized % 1f; animator.SetBool("loop", (Object)(object)unlockableEmote.transitionsToClip != (Object)null); animator.Play(((Object)(object)currentAnimationClip == (Object)(object)unlockableEmote.transitionsToClip) ? "emote_loop" : "emote", 0, num2); animator.Update(0f); performingEmote = unlockableEmote; isPerformingEmote = true; PerformEmoteProps(); if (!isSimpleEmoteController && emoteController.emoteSyncGroup != null) { AddToEmoteSyncGroup(emoteController.emoteSyncGroup); } DiscoBallPatcher.OnPerformEmote(this); return true; } protected void PerformEmoteProps() { if ((Object)(object)propsParent != (Object)null && performingEmote.propNamesInEmote != null) { LoadEmoteProps(); } if (emotingProps == null) { return; } foreach (PropObject emotingProp in emotingProps) { emotingProp.SyncWithEmoteController(this); } } protected void LoadEmoteProps() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) UnloadEmoteProps(); if (performingEmote.propNamesInEmote == null) { return; } foreach (string item in performingEmote.propNamesInEmote) { PropObject propObject = EmotePropManager.LoadEmoteProp(item); propObject.SetPropLayer(6); emotingProps.Add(propObject); ((Component)propObject).transform.SetParent(propsParent); ((Component)propObject).transform.localPosition = Vector3.zero; ((Component)propObject).transform.localRotation = Quaternion.identity; } } protected void UnloadEmoteProps() { if (emotingProps == null) { return; } foreach (PropObject emotingProp in emotingProps) { ((Component)emotingProp).transform.SetParent(EmotePropManager.propPoolParent); emotingProp.active = false; } emotingProps.Clear(); } public virtual void StopPerformingEmote() { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) if (initialized) { isPerformingEmote = false; if (!isSimpleEmoteController) { CustomLogging.Log(string.Format("[" + emoteControllerName + "] Stopping emote.")); } animatorController["emote"] = null; animatorController["emote_loop"] = null; RemoveFromEmoteSyncGroup(); UnloadEmoteProps(); if ((Object)(object)ikLeftHand != (Object)null) { ikLeftHand.localPosition = Vector3.zero; } if ((Object)(object)ikRightHand != (Object)null) { ikRightHand.localPosition = Vector3.zero; } if ((Object)(object)ikLeftFoot != (Object)null) { ikLeftFoot.localPosition = Vector3.zero; } if ((Object)(object)ikRightFoot != (Object)null) { ikRightFoot.localPosition = Vector3.zero; } if ((Object)(object)ikHead != (Object)null) { ikHead.localPosition = Vector3.zero; } DiscoBallPatcher.OnStopPerformingEmote(this); } } public virtual void ResetPerformingEmote() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) if (initialized) { isPerformingEmote = false; animatorController["emote"] = null; animatorController["emote_loop"] = null; RemoveFromEmoteSyncGroup(); UnloadEmoteProps(); if ((Object)(object)ikLeftHand != (Object)null) { ikLeftHand.localPosition = Vector3.zero; } if ((Object)(object)ikRightHand != (Object)null) { ikRightHand.localPosition = Vector3.zero; } if ((Object)(object)ikLeftFoot != (Object)null) { ikLeftFoot.localPosition = Vector3.zero; } if ((Object)(object)ikRightFoot != (Object)null) { ikRightFoot.localPosition = Vector3.zero; } if ((Object)(object)ikHead != (Object)null) { ikHead.localPosition = Vector3.zero; } } } public AnimationClip GetCurrentAnimationClip() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) if (!IsPerformingCustomEmote()) { return null; } if (!animator.GetBool("loop")) { return animatorController["emote"]; } AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); return animatorController[((AnimatorStateInfo)(ref currentAnimatorStateInfo)).IsName("emote_loop") ? "emote_loop" : "emote"]; } protected virtual void CreateBoneMap() { } public void CreateBoneMap(List<string> sourceBoneNames, List<string> targetBoneNames = null) { boneMap = BoneMapper.CreateBoneMap(humanoidSkeleton, metarig, sourceBoneNames, targetBoneNames); } protected virtual void FindIkBones() { Transform val = FindChildRecursive("root_ik"); if ((Object)(object)val == (Object)null) { CustomLogging.LogError("Failed to find root ik bone called \"root_ik\" in humanoid skeleton: " + emoteControllerName); return; } ikLeftHand = val.Find("hand_ik_l"); ikRightHand = val.Find("hand_ik_r"); ikLeftFoot = val.Find("foot_ik_l"); ikRightFoot = val.Find("foot_ik_r"); ikHead = val.Find("head_ik"); } protected virtual Transform FindChildRecursive(string objectName, Transform root = null) { if ((Object)(object)root == (Object)null) { root = humanoidSkeleton; } if (((Object)root).name == objectName) { return root; } for (int i = 0; i < root.childCount; i++) { Transform child = root.GetChild(i); Transform val = FindChildRecursive(objectName, child); if ((Object)(object)val != (Object)null) { return val; } } return null; } protected virtual ulong GetEmoteControllerId() { return 0uL; } protected virtual string GetEmoteControllerName() { return ((Object)this).name; } protected void CreateEmoteSyncGroup(bool doNotTriggerAudio = false) { emoteSyncGroup = EmoteSyncGroup.CreateEmoteSyncGroup(this, !doNotTriggerAudio); } protected void AddToEmoteSyncGroup(EmoteSyncGroup emoteSyncGroup) { CustomLogging.Log("Adding to emote sync group with id: " + emoteSyncGroup.syncId); emoteSyncGroup.AddToEmoteSyncGroup(this); this.emoteSyncGroup = emoteSyncGroup; } protected void RemoveFromEmoteSyncGroup() { if (emoteSyncGroup != null) { emoteSyncGroup.RemoveFromEmoteSyncGroup(this); } emoteSyncGroup = null; } protected void RecordStartingBonePositions() { } } [HarmonyPatch] public static class EmotesManager { public static List<UnlockableEmote> allUnlockableEmotes; public static Dictionary<string, UnlockableEmote> allUnlockableEmotesDict; public static List<UnlockableEmote> complementaryEmotes; internal static List<UnlockableEmote> complementaryEmotesDefault; public static List<string> allFavoriteEmotes; public static List<string> allQuickEmotes; public static List<UnlockableEmote> allEmotesTier0; public static List<UnlockableEmote> allEmotesTier1; public static List<UnlockableEmote> allEmotesTier2; public static List<UnlockableEmote> allEmotesTier3; public static HashSet<int> blacklistedEmoteIds = new HashSet<int>(); internal static CultureInfo defaultSortCulture = CultureInfo.CreateSpecificCulture("en-US"); public static void BuildEmotesList() { allUnlockableEmotes = new List<UnlockableEmote>(); allUnlockableEmotesDict = new Dictionary<string, UnlockableEmote>(); complementaryEmotesDefault = new List<UnlockableEmote>(); allFavoriteEmotes = new List<string>(); allQuickEmotes = new List<string>(8); while (allQuickEmotes.Count < allQuickEmotes.Capacity) { allQuickEmotes.Add(""); } allEmotesTier0 = new List<UnlockableEmote>(); allEmotesTier1 = new List<UnlockableEmote>(); allEmotesTier2 = new List<UnlockableEmote>(); allEmotesTier3 = new List<UnlockableEmote>(); Dictionary<string, List<UnlockableEmote>> dictionary = new Dictionary<string, List<UnlockableEmote>>(); for (int i = 0; i < Plugin.customAnimationClips.Count; i++) { AnimationClip val = Plugin.customAnimationClips[i]; UnlockableEmote unlockableEmote = new UnlockableEmote { emoteId = i, emoteName = ((Object)val).name, displayName = "", animationClip = val, rarity = 0 }; unlockableEmote.rarity = (Plugin.animationClipsTier1.Contains(val) ? 1 : (Plugin.animationClipsTier2.Contains(val) ? 2 : (Plugin.animationClipsTier3.Contains(val) ? 3 : 0))); if (Plugin.complementaryAnimationClips.Contains(val)) { unlockableEmote.complementary = true; } if (unlockableEmote.emoteName.Contains("_start") && !unlockableEmote.emoteName.Contains("_start_")) { string key = unlockableEmote.emoteName.Replace("_start", "_loop"); AnimationClip val2 = Plugin.customAnimationClipsLoopDict[key]; if ((Object)(object)val2 != (Object)null) { unlockableEmote.transitionsToClip = val2; unlockableEmote.emoteName = unlockableEmote.emoteName.Replace("_start", ""); ((Object)unlockableEmote.animationClip).name = unlockableEmote.emoteName + "_start"; ((Object)unlockableEmote.transitionsToClip).name = unlockableEmote.emoteName + "_loop"; } } else if (unlockableEmote.emoteName.Contains("_pose")) { unlockableEmote.isPose = true; unlockableEmote.emoteName = unlockableEmote.emoteName.Replace("_pose", ""); ((Object)unlockableEmote.animationClip).name = unlockableEmote.emoteName; } if (unlockableEmote.emoteName.Contains(".")) { string[] array = unlockableEmote.emoteName.Split(new char[1] { '.' }); if (array.Length != 0 && array[0].Length > 0) { if (array.Length > 3) { CustomLogging.LogError("Error parsing emote name: " + unlockableEmote.emoteName + ". Correct format: \"emote_group.optional_arg.emote_name\""); continue; } unlockableEmote.emoteSyncGroupName = array[0]; unlockableEmote.emoteName = unlockableEmote.emoteSyncGroupName + "." + array[^1]; unlockableEmote.displayName = unlockableEmote.emoteSyncGroupName; if ((Object)(object)unlockableEmote.transitionsToClip == (Object)null) { ((Object)unlockableEmote.animationClip).name = unlockableEmote.emoteName; } else { ((Object)unlockableEmote.animationClip).name = unlockableEmote.emoteName + "_start"; ((Object)unlockableEmote.transitionsToClip).name = unlockableEmote.emoteName + "_loop"; } if (!dictionary.TryGetValue(unlockableEmote.emoteSyncGroupName, out unlockableEmote.emoteSyncGroup)) { unlockableEmote.emoteSyncGroup = new List<UnlockableEmote>(); dictionary.Add(unlockableEmote.emoteSyncGroupName, unlockableEmote.emoteSyncGroup); } if (array.Length == 3 && array[1].ToLower().Contains("layer_")) { ((Object)val).name = ((Object)val).name.Replace("." + array[1], ""); if ((Object)(object)unlockableEmote.transitionsToClip != (Object)null) { ((Object)unlockableEmote.transitionsToClip).name = ((Object)unlockableEmote.transitionsToClip).name.Replace("." + array[1], ""); } if (!int.TryParse(array[1].Substring(6), out var result)) { CustomLogging.LogError("Failed to parse emote layer number in arg: " + array[1] + ". Emote will not be added."); continue; } unlockableEmote.purchasable = result == 0; while (unlockableEmote.emoteSyncGroup.Count <= result) { unlockableEmote.emoteSyncGroup.Add(null); } unlockableEmote.emoteSyncGroup[result] = unlockableEmote; } else { unlockableEmote.emoteSyncGroup.Add(unlockableEmote); unlockableEmote.purchasable = unlockableEmote.emoteSyncGroup.Count == 1; if (array.Length == 3 && array[1].ToLower() == "random") { unlockableEmote.randomEmote = true; ((Object)val).name = ((Object)val).name.Replace("." + array[1], ""); if ((Object)(object)unlockableEmote.transitionsToClip != (Object)null) { ((Object)unlockableEmote.transitionsToClip).name = ((Object)unlockableEmote.transitionsToClip).name.Replace("." + array[1], ""); } } } } } if (unlockableEmote.emoteName.Contains("_start") && !unlockableEmote.emoteName.Contains("_start_")) { string key2 = unlockableEmote.emoteName.Replace("_start", "_loop"); AnimationClip val3 = Plugin.customAnimationClipsLoopDict[key2]; if ((Object)(object)val3 != (Object)null) { unlockableEmote.transitionsToClip = val3; unlockableEmote.emoteName = unlockableEmote.emoteName.Replace("_start", ""); ((Object)unlockableEmote.animationClip).name = unlockableEmote.emoteName + "_start"; ((Object)unlockableEmote.transitionsToClip).name = unlockableEmote.emoteName + "_loop"; } } else if (unlockableEmote.emoteName.Contains("_pose")) { unlockableEmote.isPose = true; unlockableEmote.emoteName = unlockableEmote.emoteName.Replace("_pose", ""); ((Object)unlockableEmote.animationClip).name = unlockableEmote.emoteName; } if ((!((Object)(object)unlockableEmote.transitionsToClip != (Object)null) && !((Motion)unlockableEmote.animationClip).isLooping && !unlockableEmote.isPose && unlockableEmote.emoteSyncGroup == null) || true) { unlockableEmote.canSyncEmote = true; } if (unlockableEmote.displayName == "") { unlockableEmote.displayName = unlockableEmote.emoteName; } unlockableEmote.displayName = unlockableEmote.displayName.Replace('_', ' ').Trim(new char[1] { ' ' }); unlockableEmote.displayName = char.ToUpper(unlockableEmote.displayName[0]) + unlockableEmote.displayName.Substring(1).ToLower(); if (!allUnlockableEmotes.Contains(unlockableEmote)) { allUnlockableEmotes.Add(unlockableEmote); allUnlockableEmotesDict.Add(unlockableEmote.emoteName, unlockableEmote); if (ConfigSettings.blacklistedEmoteNames.Contains(unlockableEmote.displayName)) { blacklistedEmoteIds.Add(unlockableEmote.emoteId); } } if (Plugin.complementaryAnimationClips.Contains(val) && unlockableEmote.purchasable) { unlockableEmote.complementary = true; complementaryEmotesDefault.Add(unlockableEmote); } } try { allUnlockableEmotes.Sort((UnlockableEmote item1, UnlockableEmote item2) => string.Compare(item1.displayName, item2.displayName, ignoreCase: true, defaultSortCulture)); } catch (Exception) { if (ConfigSettings.verboseLogs.Value) { CustomLogging.LogWarningVerbose("Failed to apply default emote sort. Reverting to original sort method."); } allUnlockableEmotes = allUnlockableEmotes.OrderBy((UnlockableEmote item) => item.emoteName).ToList(); } int num = 0; foreach (UnlockableEmote allUnlockableEmote in allUnlockableEmotes) { allUnlockableEmote.emoteId = num++; if (!allUnlockableEmote.complementary) { if (allUnlockableEmote.rarity == 0) { allEmotesTier0.Add(allUnlockableEmote); } else if (allUnlockableEmote.rarity == 1) { allEmotesTier1.Add(allUnlockableEmote); } else if (allUnlockableEmote.rarity == 2) { allEmotesTier2.Add(allUnlockableEmote); } else if (allUnlockableEmote.rarity == 3) { allEmotesTier3.Add(allUnlockableEmote); } } } complementaryEmotes = new List<UnlockableEmote>(complementaryEmotesDefault); SaveManager.LoadFavoritedEmotes(); SaveManager.LoadQuickEmotes(); } } [HarmonyPatch] public static class SessionManager { public static List<UnlockableEmote> unlockedEmotes = new List<UnlockableEmote>(); public static List<UnlockableEmote> unlockedEmotesTier0 = new List<UnlockableEmote>(); public static List<UnlockableEmote> unlockedEmotesTier1 = new List<UnlockableEmote>(); public static List<UnlockableEmote> unlockedEmotesTier2 = new List<UnlockableEmote>(); public static List<UnlockableEmote> unlockedEmotesTier3 = new List<UnlockableEmote>(); internal static List<UnlockableEmote> emotesUnlockedThisSession = new List<UnlockableEmote>(); public static Dictionary<string, List<UnlockableEmote>> unlockedEmotesByPlayer = new Dictionary<string, List<UnlockableEmote>>(); public static List<UnlockableEmote> unlockedFavoriteEmotes = new List<UnlockableEmote>(); public static string localPlayerUsername => GameNetworkManager.Instance?.username; [HarmonyPatch(typeof(StartOfRound), "Awake")] [HarmonyPrefix] public static void ResetGameValues() { EmoteController.allEmoteControllers?.Clear(); EmoteControllerPlayer.allPlayerEmoteControllers?.Clear(); EmoteControllerMaskedEnemy.allMaskedEnemyEmoteControllers?.Clear(); EmoteAudioSource.allEmoteAudioSources?.Clear(); EmotesManager.complementaryEmotes = new List<UnlockableEmote>(EmotesManager.complementaryEmotesDefault); } [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] [HarmonyPostfix] public static void OnHostConnected(PlayerControllerB __instance) { if (!HelperTools.isServer) { return; } if (!unlockedEmotesByPlayer.ContainsKey(HelperTools.localPlayerController.playerUsername)) { unlockedEmotesByPlayer.Add(HelperTools.localPlayerController.playerUsername, unlockedEmotes); TerminalPatcher.currentEmoteCreditsByPlayer.Add(HelperTools.localPlayerController.playerUsername, TerminalPatcher.currentEmoteCredits); return; } foreach (UnlockableEmote item in unlockedEmotesByPlayer[HelperTools.localPlayerController.playerUsername]) { if (!IsEmoteUnlocked(item)) { unlockedEmotes.Add(item); } } unlockedEmotesByPlayer[HelperTools.localPlayerController.playerUsername] = unlockedEmotes; TerminalPatcher.currentEmoteCreditsByPlayer[HelperTools.localPlayerController.playerUsername] = TerminalPatcher.currentEmoteCredits; } [HarmonyPatch(typeof(StartOfRound), "Start")] [HarmonyPostfix] public static void OnServerStart(StartOfRound __instance) { if (HelperTools.isServer) { } SyncManager.RotateEmoteSelectionServer(TerminalPatcher.emoteStoreSeed); } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] public static void ResetOverrideSeedFlag(StartOfRound __instance) { __instance.overrideRandomSeed = false; } [HarmonyPatch(typeof(StartOfRound), "ResetShip")] [HarmonyPostfix] public static void ResetEmotesOnShipReset(StartOfRound __instance) { if (!ConfigSync.instance.syncUnlockEverything) { ResetProgressLocal(); } if (HelperTools.isServer) { SyncManager.RotateEmoteSelectionServer(); } } public static void ResetProgressLocal(bool forceResetAll = false) { CustomLogging.Log("Resetting progress."); if (!ConfigSync.instance.syncPersistentUnlocks || forceResetAll) { ResetEmotesLocal(); } if (!ConfigSync.instance.syncPersistentEmoteCredits || forceResetAll) { TerminalPatcher.currentEmoteCredits = ConfigSync.instance.syncStartingEmoteCredits; List<string> list = new List<string>(TerminalPatcher.currentEmoteCreditsByPlayer.Keys); foreach (string item in list) { TerminalPatcher.currentEmoteCreditsByPlayer[item] = ConfigSync.instance.syncStartingEmoteCredits; } } TerminalPatcher.emoteStoreSeed = 0; } [HarmonyPatch(typeof(StartOfRound), "SyncShipUnlockablesServerRpc")] [HarmonyPostfix] public static void SyncUnlockedEmotesWithClients(StartOfRound __instance) { if (!HelperTools.isServer || ConfigSync.instance.syncUnlockEverything || ConfigSync.instance.syncPersistentUnlocksGlobal) { return; } if (ConfigSync.instance.syncShareEverything) { CustomLogging.Log("Syncing unlocked emotes with clients."); SyncManager.SendOnUnlockEmoteUpdateMulti(TerminalPatcher.currentEmoteCredits); return; } HashSet<ulong> hashSet = new HashSet<ulong>(); PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (val.actualClientId != 0L && val.playerSteamId != 0) { hashSet.Add(val.actualClientId); } } foreach (ulong item in hashSet) { SyncManager.ServerSendSyncToClient(item); } } public static bool IsEmoteUnlocked(string emoteName, string playerUsername = "") { if (EmotesManager.allUnlockableEmotesDict.TryGetValue(emoteName, out var value)) { return IsEmoteUnlocked(value, playerUsername); } return false; } public static bool IsEmoteUnlocked(UnlockableEmote emote, string playerUsername = "") { if (emote == null) { return false; } List<UnlockableEmote> value = unlockedEmotes; if (playerUsername != "" && !unlockedEmotesByPlayer.TryGetValue(playerUsername, out value)) { return false; } if (emote.emoteSyncGroup != null && emote.emoteSyncGroup.Count > 0) { foreach (UnlockableEmote item in emote.emoteSyncGroup) { if (value.Contains(item)) { return true; } } } return value.Contains(emote); } public static List<UnlockableEmote> GetUnlockedEmotes(PlayerControllerB playerController) { return GetUnlockedEmotes(((Object)(object)playerController != (Object)null) ? playerController.playerUsername : ""); } public static List<UnlockableEmote> GetUnlockedEmotes(string playerUsername) { if (unlockedEmotesByPlayer.TryGetValue(playerUsername, out var value)) { return value; } return null; } public static void UnlockEmotesLocal(IEnumerable<UnlockableEmote> emotes, bool purchased = false, string playerUsername = "") { foreach (UnlockableEmote emote in emotes) { UnlockEmoteLocal(emote, purchased, playerUsername); } } public static void UnlockEmoteLocal(int emoteId, bool purchased = false, string playerUsername = "") { UnlockEmoteLocal((emoteId >= 0 && emoteId < EmotesManager.allUnlockableEmotes.Count) ? EmotesManager.allUnlockableEmotes[emoteId] : null, purchased, playerUsername); } public static void UnlockEmoteLocal(UnlockableEmote emote, bool purchased = false, string playerUsername = "") { if (emote == null || (emote.requiresHeldProp && ConfigSync.instance.syncRemoveGrabbableEmotesPartyPooperMode)) { return; } List<UnlockableEmote> list = unlockedEmotes; if (playerUsername != "" && playerUsername != localPlayerUsername) { if (!unlockedEmotesByPlayer.TryGetValue(playerUsername, out var value) && !ConfigSync.instance.syncShareEverything) { return; } if (value != null) { list = value; } } if (IsEmoteUnlocked(emote, playerUsername)) { return; } if (emote.emoteSyncGroup != null) { foreach (UnlockableEmote item in emote.emoteSyncGroup) { if (list.Contains(item)) { return; } } if (emote.emoteSyncGroup.Count > 0) { emote = emote.emoteSyncGroup[0]; } } if (!list.Contains(emote)) { list.Add(emote); } if (list != unlockedEmotes) { return; } if (!emote.complementary) { if (emote.rarity == 3 && !unlockedEmotesTier3.Contains(emote)) { unlockedEmotesTier3.Add(emote); } else if (emote.rarity == 2 && !unlockedEmotesTier2.Contains(emote)) { unlockedEmotesTier2.Add(emote); } else if (emote.rarity == 1 && !unlockedEmotesTier1.Contains(emote)) { unlockedEmotesTier1.Add(emote); } else if (emote.rarity == 0 && !unlockedEmotesTier0.Contains(emote)) { unlockedEmotesTier0.Add(emote); } } if (EmotesManager.allFavoriteEmotes.Contains(emote.emoteName) && !unlockedFavoriteEmotes.Contains(emote)) { unlockedFavoriteEmotes.Add(emote); } if (ConfigSync.instance.syncPersistentUnlocksGlobal && purchased && !emotesUnlockedThisSession.Contains(emote)) { emotesUnlockedThisSession.Add(emote); } } public static void RemoveEmoteLocal(UnlockableEmote emote) { unlockedEmotes.Remove(emote); unlockedEmotesTier0.Remove(emote); unlockedEmotesTier1.Remove(emote); unlockedEmotesTier2.Remove(emote); unlockedEmotesTier3.Remove(emote); unlockedFavoriteEmotes.Remove(emote); emotesUnlockedThisSession.Remove(emote); foreach (List<UnlockableEmote> value in unlockedEmotesByPlayer.Values) { value.Remove(emote); } } public static void ResetEmotesLocal() { CustomLogging.Log("Resetting unlocked emotes."); unlockedEmotes.Clear(); unlockedEmotesTier0.Clear(); unlockedEmotesTier1.Clear(); unlockedEmotesTier2.Clear(); unlockedEmotesTier3.Clear(); emotesUnlockedThisSession.Clear(); unlockedEmotesByPlayer.Clear(); PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (val.playerSteamId != 0L && !unlockedEmotesByPlayer.ContainsKey(val.playerUsername)) { unlockedEmotesByPlayer.Add(val.playerUsername, ((Object)(object)val == (Object)(object)HelperTools.localPlayerController || ConfigSync.instance.syncShareEverything) ? unlockedEmotes : new List<UnlockableEmote>()); } } UnlockEmotesLocal(ConfigSync.instance.syncUnlockEverything ? EmotesManager.allUnlockableEmotes : EmotesManager.complementaryEmotes); UpdateUnlockedFavoriteEmotes(); } public static void UpdateUnlockedFavoriteEmotes() { unlockedFavoriteEmotes?.Clear(); if (EmotesManager.allFavoriteEmotes == null || EmotesManager.allUnlockableEmotesDict == null || unlockedFavoriteEmotes == null) { return; } foreach (string allFavoriteEmote in EmotesManager.allFavoriteEmotes) { if (EmotesManager.allUnlockableEmotesDict.TryGetValue(allFavoriteEmote, out var value)) { if (value.emoteSyncGroup != null && value.emoteSyncGroup.Count > 0) { value = value.emoteSyncGroup[0]; } if (IsEmoteUnlocked(value)) { unlockedFavoriteEmotes.Add(value); } } } } internal static void UpdateBlacklistedEmotes() { } } public class UnlockableEmote { public int emoteId; public string emoteName; public string displayName = ""; public AnimationClip animationClip; public AnimationClip transitionsToClip = null; public bool purchasable = true; public bool requiresHeldProp = false; public GameObject requiredHeldPropPrefab = null; public bool complementary = false; public bool isPose = false; public bool canMoveWhileEmoting = false; private bool _isBoomboxAudio = true; public string overrideAudioClipName = ""; public string overrideAudioLoopClipName = ""; public string emoteSyncGroupName = ""; public List<UnlockableEmote> emoteSyncGroup; public float recordSongLoopValue = 0f; public bool randomEmote = false; public List<string> propNamesInEmote; public bool canSyncEmote = false; public int rarity = 0; public static string[] rarityColorCodes = new string[4] { ConfigSettings.emoteNameColorTier0.Value, ConfigSettings.emoteNameColorTier1.Value, ConfigSettings.emoteNameColorTier2.Value, ConfigSettings.emoteNameColorTier3.Value }; public string displayNameColorCoded => $"<color={nameColor}>{displayName}</color>"; public bool humanoidAnimation => ((Motion)animationClip).isHumanMotion; public bool loopable => ((Motion)animationClip).isLooping || ((Object)(object)transitionsToClip != (Object)null && ((Motion)transitionsToClip).isLooping); public bool hasAudio => audioClipName != "" || audioLoopClipName != ""; public bool isBoomboxAudio { get { return _isBoomboxAudio && !ConfigSettings.disableBoomboxRequirement.Value; } set { _isBoomboxAudio = value; } } public string audioClipName => ((Object)(object)animationClip != (Object)null && AudioManager.AudioExists(((Object)animationClip).name)) ? ((Object)animationClip).name : ((overrideAudioClipName != "" && AudioManager.AudioExists(overrideAudioClipName)) ? overrideAudioClipName : ""); public string audioLoopClipName => ((Object)(object)transitionsToClip != (Object)null && AudioManager.AudioExists(((Object)transitionsToClip).name)) ? ((Object)transitionsToClip).name : ((overrideAudioLoopClipName != "" && AudioManager.AudioExists(overrideAudioLoopClipName)) ? overrideAudioLoopClipName : ""); public bool inEmoteSyncGroup => emoteSyncGroup != null && !randomEmote; public bool favorite => EmotesManager.allFavoriteEmotes.Contains(emoteName); public string rarityText { get { if (rarity == 0) { return "Common"; } if (rarity == 1) { return "Rare"; } if (rarity == 2) { return "Epic"; } if (rarity == 3) { return "Legendary"; } return "Invalid"; } } public int price { get { int num = -1; if (complementary) { num = 0; } else if (rarity == 0) { num = ConfigSync.instance.syncBasePriceEmoteTier0; } else if (rarity == 1) { num = ConfigSync.instance.syncBasePriceEmoteTier1; } else if (rarity == 2) { num = ConfigSync.instance.syncBasePriceEmoteTier2; } else if (rarity == 3) { num = ConfigSync.instance.syncBasePriceEmoteTier3; } return (int)Mathf.Max((float)num * ConfigSync.instance.syncPriceMultiplierEmotesStore, 0f); } } public string nameColor => rarityColorCodes[rarity]; public bool IsEmoteInEmoteGroup(UnlockableEmote emote) { return this == emote || (emoteSyncGroup != null && emoteSyncGroup.Contains(emote)); } public bool ClipIsInEmote(AnimationClip clip) { if ((Object)(object)clip == (Object)null) { return false; } if ((Object)(object)clip == (Object)(object)animationClip || (Object)(object)clip == (Object)(object)transitionsToClip) { return true; } if (emoteSyncGroup != null) { foreach (UnlockableEmote item in emoteSyncGroup) { if ((Object)(object)clip == (Object)(object)item.animationClip || (Object)(object)clip == (Object)(object)item.transitionsToClip) { return true; } } } return false; } public AudioClip LoadAudioClip() { if (hasAudio && audioClipName.Length > 0) { return AudioManager.LoadAudioClip(audioClipName); } return null; } public AudioClip LoadAudioLoopClip() { if (hasAudio && (Object)(object)transitionsToClip != (Object)null && audioLoopClipName.Length > 0) { return AudioManager.LoadAudioClip(audioLoopClipName); } return null; } } [HarmonyPatch] public static class SaveManager { public static string TooManyEmotesSaveFileName = "TooManyEmotes_LocalSaveData"; private static List<string> globallyUnlockedEmoteNames = new List<string>(); [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] [HarmonyPrefix] public static void CheckIfShouldResetLocalSettings() { if (ConfigSettings.resetGloballyUnlockedEmotes) { ResetGloballyUnlockedEmotes(); } if (ConfigSettings.resetFavoriteEmotes) { ResetFavoritedEmotes(); } ConfigSettings.resetGloballyUnlockedEmotes = false; ConfigSettings.resetFavoriteEmotes = false; } [HarmonyPatch(typeof(GameNetworkManager), "SaveGameValues")] [HarmonyPostfix] public static void SaveUnlockedEmotes(GameNetworkManager __instance) { if (!__instance.isHostingGame || !StartOfRound.Instance.inShipPhase || ConfigSync.instance.syncUnlockEverything) { return; } CustomLogging.Log("Saving game values."); try { HashSet<string> hashSet; try { hashSet = new HashSet<string>(ES3.Load<string[]>("TooManyEmotes.UnlockedEmotes.PlayersList", HelperTools.currentSaveFileName, new string[0])); } catch (Exception ex) { CustomLogging.LogErrorVerbose("Error loading previous users list. Deleting key: TooManyEmotes.UnlockedEmotes.PlayersList from file: " + HelperTools.currentSaveFileName); CustomLogging.LogErrorVerbose(ex.ToString()); ES3.DeleteKey("TooManyEmotes.UnlockedEmotes.PlayersList", HelperTools.currentSaveFileName); hashSet = new HashSet<string>(); } foreach (string key in SessionManager.unlockedEmotesByPlayer.Keys) { hashSet.Add(key); } ES3.Save<string[]>("TooManyEmotes.UnlockedEmotes.PlayersList", hashSet.ToArray(), HelperTools.currentSaveFileName); foreach (string item in hashSet) { if (!ConfigSync.instance.syncPersistentUnlocksGlobal) { if (!SessionManager.unlockedEmotesByPlayer.ContainsKey(item)) { continue; } if (SessionManager.unlockedEmotesByPlayer.TryGetValue(item, out var value)) { CustomLogging.Log("Saving " + value.Count + " unlocked emotes for player: " + item); string[] array = new string[value.Count]; for (int i = 0; i < value.Count; i++) { array[i] = value[i].emoteName; } if (value == SessionManager.unlockedEmotes) { ES3.Save<string[]>("TooManyEmotes.UnlockedEmotes", array, HelperTools.currentSaveFileName); } else { ES3.Save<string[]>("TooManyEmotes.UnlockedEmotes.Player_" + item, array, HelperTools.currentSaveFileName); } } } if (TerminalPatcher.currentEmoteCreditsByPlayer.ContainsKey(item)) { CustomLogging.Log("Saving " + TerminalPatcher.currentEmoteCreditsByPlayer[item] + " emote credits for player: " + item); string text = "TooManyEmotes.CurrentEmoteCredits" + (ConfigSync.instance.syncPersistentUnlocks ? ".Persistent" : ""); if ((Object)(object)HelperTools.localPlayerController != (Object)null && HelperTools.localPlayerController.playerSteamId != 0L && item == HelperTools.localPlayerController.playerUsername) { ES3.Save<int>(text, TerminalPatcher.currentEmoteCredits, __instance.currentSaveFileName); } else { ES3.Save<int>(text + ".Player_" + item, TerminalPatcher.currentEmoteCreditsByPlayer[item], __instance.currentSaveFileName); } } } ES3.Save<int>("TooManyEmotes.EmoteStoreSeed", TerminalPatcher.emoteStoreSeed, __instance.currentSaveFileName); CustomLogging.Log("Saved Seed: " + TerminalPatcher.emoteStoreSeed); } catch (Exception ex2) { CustomLoggin