Decompiled source of TooManyEmotes v2.2.14
plugins/TooManyEmotes.dll
Decompiled a week 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 Dissonance.Integrations.Unity_NFGO; using GameNetcodeStuff; using HarmonyLib; using LCVR.Player; 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.Rendering.HighDefinition; 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("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; } } [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; 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 val = playerController.ItemSlots[playerController.currentItemSlot]; if ((Object)(object)sourceGrabbableEmoteProp != (Object)null && (Object)(object)sourceGrabbableEmoteProp != (Object)(object)val) { 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; } 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)) { 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("[DEBUG] 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.ItemSlots[HelperTools.localPlayerController.currentItemSlot])) ? 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; } 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; 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 val = __instance.ItemSlots[slot]; if ((Object)(object)value.sourceGrabbableEmoteProp != (Object)null && (Object)(object)value.sourceGrabbableEmoteProp != (Object)(object)val) { value.StopPerformingEmote(); } else if (Object.op_Implicit((Object)(object)val) && value.emotingProps.Count > 0) { val.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.ItemSlots[playerController.currentItemSlot]) { 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; if (!isLocalPlayer) { originalAnimator.SetInteger("emoteNumber", 0); } GrabbableObject val = playerController.ItemSlots[playerController.currentItemSlot]; if (Object.op_Implicit((Object)(object)val) && emotingProps.Count > 0) { val.EnableItemMeshes(false); } if (isLocalPlayer) { ThirdPersonEmoteController.OnStartCustomEmoteLocal(); } } 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; if (!isLocalPlayer) { originalAnimator.SetInteger("emoteNumber", 0); } else { 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_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: 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 val = playerController.ItemSlots[playerController.currentItemSlot]; if (Object.op_Implicit((Object)(object)val)) { val.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 val = playerController.ItemSlots[playerController.currentItemSlot]; if (Object.op_Implicit((Object)(object)val)) { val.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; 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 (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] private 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] private 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] private static void OnServerStart(StartOfRound __instance) { if (HelperTools.isServer) { } SyncManager.RotateEmoteSelectionServer(TerminalPatcher.emoteStoreSeed); } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] private static void ResetOverrideSeedFlag(StartOfRound __instance) { __instance.overrideRandomSeed = false; } [HarmonyPatch(typeof(StartOfRound), "ResetShip")] [HarmonyPostfix] private 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] private 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 (!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 != 0) { 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); } } } } } 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] private static void CheckIfShouldResetLocalSettings() { if (ConfigSettings.resetGloballyUnlockedEmotes) { ResetGloballyUnlockedEmotes(); } if (ConfigSettings.resetFavoriteEmotes) { ResetFavoritedEmotes(); } ConfigSettings.resetGloballyUnlockedEmotes = false; ConfigSettings.resetFavoriteEmotes = false; } [HarmonyPatch(typeof(GameNetworkManager), "SaveGameValues")] [HarmonyPostfix] private 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) { CustomLogging.LogError("Error while trying to save TooManyEmotes values when disconnecting as host."); CustomLogging.LogError(ex2.ToString()); } } [HarmonyPatch(typeof(StartOfRound), "LoadUnlockables")] [HarmonyPostfix] private static void LoadUnlockedEmotes(StartOfRound __instance) { if (!GameNetworkManager.Instance.isHostingGame || ConfigSync.instance.syncUnlockEverything) { return; } CustomLogging.Log("Loading game values."); try { if (!ConfigSync.instance.syncPersistentUnlocksGlobal) { SessionManager.ResetEmotesLocal(); string[] array = ES3.Load<string[]>("TooManyEmotes.UnlockedEmotes", HelperTools.currentSaveFileName, new string[0]); string[] array2 = array; foreach (string key in array2) { if (EmotesManager.allUnlockableEmotesDict.TryGetValue(key, out var value)) { SessionManager.UnlockEmoteLocal(value); } } } string text = "TooManyEmotes.CurrentEmoteCredits"