Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BetterEmote v0.0.2
BetterEmote/plugins/BetterEmote.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.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 System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BetterEmote.Audio; using BetterEmote.EmoteControllers; using BetterEmote.Input; using BetterEmote.Networking; using BetterEmote.Props; using BetterEmote.UI; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Experimental.Rendering; using UnityEngine.InputSystem; 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("BetterEmote")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BetterEmote")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("0501B98A-15BF-4EA4-BA7F-A042B6ABDD77")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [CompilerGenerated] internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { int ICollection.Count => _items.Length; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection<T>.Count => _items.Length; T IReadOnlyList<T>.this[int index] => _items[index]; int ICollection<T>.Count => _items.Length; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } public <>z__ReadOnlyArray(T[] items) { _items = items; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } void ICollection.CopyTo(Array array, int index) { ((ICollection)_items).CopyTo(array, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return ((IList)_items).Contains(value); } int IList.IndexOf(object value) { return ((IList)_items).IndexOf(value); } void IList.Insert(int index, object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return ((IEnumerable<T>)_items).GetEnumerator(); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return ((ICollection<T>)_items).Contains(item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { ((ICollection<T>)_items).CopyTo(array, arrayIndex); } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { return ((IList<T>)_items).IndexOf(item); } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BetterEmote { [HarmonyPatch] public static class AdditionalEmoteData { public static void SetAdditionalEmoteData() { } public static void SetAdditionalPropData() { if (EmoteManager.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 (EmoteManager.AllUnlockableEmotesDict == null) { return; } 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 (EmoteManager.AllUnlockableEmotesDict.TryGetValue("jug_band.banjo", out var value) && value.inEmoteSyncGroup) { foreach (UnlockableEmote item in value.EmoteSyncGroup) { item.RecordSongLoopValue = 1f; } } if (!EmoteManager.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 _)) { Logger.LogWarning("Failed to assign prop: " + propName + " to emote. Prop does not exist!"); return; } if (!EmoteManager.AllUnlockableEmotesDict.TryGetValue(emoteName, out var value2)) { Logger.LogWarning("Failed to assign prop: " + propName + " to emote: " + emoteName + ". Emote does not exist!"); return; } if (!EmotePropManager.EmotePropsDataDict.TryGetValue(propName, out var value3)) { Logger.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 SetCanMoveWhileEmoting(string emoteName) { if (EmoteManager.AllUnlockableEmotesDict.TryGetValue(emoteName, out var value)) { value.CanMoveWhileEmoting = true; } } public static void AssignMusicToEmote(string emoteName, string audioName, string audioLoopName = "") { if (EmoteManager.AllUnlockableEmotesDict.TryGetValue(emoteName, out var value) && AudioManager.AudioExists(audioName)) { value.OverrideAudioClipName = audioName; value.OverrideAudioLoopClipName = audioLoopName; } } } [BepInPlugin("ishid4.mods.betteremote", "Better Emote", "0.0.2")] public class BetterEmote : BaseUnityPlugin { public static BetterEmote Instance; public static List<AnimationClip> CustomAnimationClips; public static HashSet<AnimationClip> CustomAnimationClipsHash; public static Dictionary<string, AnimationClip> CustomAnimationClipsLoopDict; public static List<AnimationClip> ComplementaryAnimationClips = new List<AnimationClip>(); public static List<AnimationClip> AnimationClipsTier0 = new List<AnimationClip>(); public static List<AnimationClip> AnimationClipsTier1 = new List<AnimationClip>(); public static List<AnimationClip> AnimationClipsTier2 = new List<AnimationClip>(); public static List<AnimationClip> AnimationClipsTier3 = new List<AnimationClip>(); public static List<AnimationClip> AnimationClipsSpecial = new List<AnimationClip>(); public static GameObject RadialMenuPrefab; public static RuntimeAnimatorController HumanoidAnimatorController; public static Avatar HumanoidAvatar; public static GameObject HumanoidSkeletonPrefab; public static ConfigEntry<int> NexusID; private Harmony _harmony; public static ManualLogSource defaultLogger => ((BaseUnityPlugin)Instance).Logger; private void Awake() { Instance = this; Logger.InitLogger(); ConfigSettings.BindConfigSettings(); Keybinds.InitKeybinds(); LoadEmoteAssets(); LoadMiscAnimationAssets(); LoadRadialMenuAsset(); AudioManager.LoadAudioAssets(); EmotePropManager.LoadPropAssets(); EmoteManager.BuildEmotesList(); AudioManager.BuildAudioClipList(); EmotePropManager.BuildEmotePropList(); AdditionalEmoteData.SetAdditionalEmoteData(); AdditionalEmoteData.SetAdditionalPropData(); AdditionalEmoteData.SetAdditionalMusicData(); NexusID = ((BaseUnityPlugin)this).Config.Bind<int>("Other", "NexusID", 3027, "Nexus mod ID for updates."); _harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); Logger.Log(typeof(BetterEmote).Namespace + " finished loading!"); } private static void LoadEmoteAssets() { CustomAnimationClips = new List<AnimationClip>(); CustomAnimationClipsHash = new HashSet<AnimationClip>(); CustomAnimationClipsLoopDict = new Dictionary<string, AnimationClip>(); ComplementaryAnimationClips = new List<AnimationClip>(LoadEmoteAssetBundle("Assets/emotes_complementary")); AnimationClipsTier0 = new List<AnimationClip>(LoadEmoteAssetBundle("Assets/emotes_0")); AnimationClipsTier1 = new List<AnimationClip>(LoadEmoteAssetBundle("Assets/emotes_1")); AnimationClipsTier2 = new List<AnimationClip>(LoadEmoteAssetBundle("Assets/emotes_2")); AnimationClipsTier3 = new List<AnimationClip>(LoadEmoteAssetBundle("Assets/emotes_3")); AnimationClipsSpecial = new List<AnimationClip>(LoadEmoteAssetBundle("Assets/emotes_special")); ComplementaryAnimationClips.AddRange(AnimationClipsSpecial); CustomAnimationClipsHash.UnionWith(ComplementaryAnimationClips); CustomAnimationClipsHash.UnionWith(AnimationClipsTier0); CustomAnimationClipsHash.UnionWith(AnimationClipsTier1); CustomAnimationClipsHash.UnionWith(AnimationClipsTier2); CustomAnimationClipsHash.UnionWith(AnimationClipsTier3); foreach (AnimationClip item in CustomAnimationClipsHash) { if (((Object)item).name.EndsWith("_loop")) { if (CustomAnimationClipsLoopDict.ContainsKey(((Object)item).name)) { Logger.LogWarning("Attempted to add duplicate emote in CustomAnimationClipsLoopDict. AnimationClip: " + ((Object)item).name); } else { CustomAnimationClipsLoopDict.Add(((Object)item).name, item); } } } CustomAnimationClips = new List<AnimationClip>(CustomAnimationClipsHash); CustomAnimationClipsHash = new HashSet<AnimationClip>(CustomAnimationClips); foreach (AnimationClip value in CustomAnimationClipsLoopDict.Values) { CustomAnimationClips.Remove(value); } } private static AnimationClip[] LoadEmoteAssetBundle(string assetBundleName) { try { string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location), assetBundleName); AssetBundle val = AssetBundle.LoadFromFile(text); AnimationClip[] array = val.LoadAllAssets<AnimationClip>(); Logger.Log($"Successfully loaded {array.Length} animation clips from asset bundle: {assetBundleName}"); return array; } catch { Logger.LogError("Failed to load emotes Asset Bundle."); return (AnimationClip[])(object)new AnimationClip[0]; } } private static void LoadMiscAnimationAssets() { try { string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location), "Assets/misc"); AssetBundle val = AssetBundle.LoadFromFile(text); HumanoidAnimatorController = val.LoadAsset<RuntimeAnimatorController>("humanoid_animator_controller"); HumanoidAvatar = val.LoadAsset<Avatar>("player_maleAvatar"); HumanoidSkeletonPrefab = val.LoadAsset<GameObject>("humanoid_skeleton"); Animator val2 = HumanoidSkeletonPrefab.GetComponentInChildren<Animator>(); if ((Object)(object)val2 == (Object)null) { val2 = HumanoidSkeletonPrefab.AddComponent<Animator>(); } if (Object.op_Implicit((Object)(object)HumanoidAvatar)) { val2.avatar = HumanoidAvatar; } if (!Object.op_Implicit((Object)(object)HumanoidAnimatorController)) { Logger.LogError("Failed to load humanoid animator controller from asset bundle: misc"); } if (!Object.op_Implicit((Object)(object)HumanoidAvatar)) { Logger.LogError("Failed to load humanoid avatar from asset bundle: misc"); } if (!Object.op_Implicit((Object)(object)HumanoidSkeletonPrefab)) { Logger.LogError("Failed to load humanoid skeleton prefab from asset bundle: misc"); } } catch { Logger.LogError("Failed to load misc Asset Bundle."); } } private static void LoadRadialMenuAsset() { try { string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location), "Assets/radial_menu"); AssetBundle val = AssetBundle.LoadFromFile(text); RadialMenuPrefab = val.LoadAsset<GameObject>("RadialMenu"); Logger.Log("Successfully loaded radial menu asset."); } catch { Logger.LogError("Failed to load radial menu asset."); } } } public static class BoneMapper { public static Dictionary<Transform, Transform> CreateBoneMap(Transform sourceSkeleton, Transform targetSkeleton, List<string> sourceBoneNames, List<string> targetBoneNames = null) { if (!Object.op_Implicit((Object)(object)sourceSkeleton) || !Object.op_Implicit((Object)(object)targetSkeleton) || sourceBoneNames == null) { return null; } if (targetBoneNames == null) { targetBoneNames = sourceBoneNames; } if (sourceBoneNames.Count != targetBoneNames.Count) { Logger.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 (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); } } } public class ConfigSettings { public static ConfigEntry<bool> UnlockEverything; public static ConfigEntry<bool> ShareEverything; public static ConfigEntry<bool> SyncUnsharedEmotes; public static ConfigEntry<string> OpenEmoteMenuKeybind; public static ConfigEntry<string> StopEmoteKeybind; public static ConfigEntry<bool> DisableExternalSourceRequirement; public static ConfigEntry<float> BaseEmoteAudioVolume; public static ConfigEntry<float> EmoteAudioMaxVolume; public static ConfigEntry<float> EmoteAudioIncreasePerPlayerSyncing; public static ConfigEntry<float> EmoteAudioMinDistance; public static ConfigEntry<float> EmoteAudioMaxDistance; public static ConfigEntry<string> EmoteNameColorTier0; public static ConfigEntry<string> EmoteNameColorTier1; public static ConfigEntry<string> EmoteNameColorTier2; public static ConfigEntry<string> EmoteNameColorTier3; public static ConfigEntry<bool> VerboseLogs; public static ConfigFile config => ((BaseUnityPlugin)BetterEmote.Instance).Config; public static void BindConfigSettings() { Logger.Log("Binding Configs"); UnlockEverything = AddConfigEntry("Emote Settings", "UnlockEverything", defaultValue: false, "[Host only] If true, every emote will be unlocked at the start of the game."); ShareEverything = AddConfigEntry("Emote Settings", "ShareEverything", defaultValue: false, "[Host only] If this setting is set to false, each player will have their own emotes. Killing a boss will only unlock emotes for the player that killed it."); SyncUnsharedEmotes = AddConfigEntry("Emote Settings", "CanSyncUnsharedEmotes", defaultValue: true, "[Host only] Only applies if ShareEverything is false. If set to true, players will be able to sync emotes with other players, even if they do not have the emote being performed unlocked."); OpenEmoteMenuKeybind = AddConfigEntry("Emote Radial Menu", "OpenEmoteMenuKeybind", "<Keyboard>/backquote", "Open radial menu."); StopEmoteKeybind = AddConfigEntry("Emote Radial Menu", "StopEmoteKeybind", "<Keyboard>/e", "Stop the emote you are currently performing."); DisableExternalSourceRequirement = AddConfigEntry("Emote Audio", "DisableExternalSourceRequirement", defaultValue: true, "If set to true, emote audio that normally requires a nearby boombox will be played from your character instead."); BaseEmoteAudioVolume = AddConfigEntry("Emote Audio", "BaseEmoteAudioVolume", 0.25f, "The base emote audio volume. The volume slider in the emote menu will be based off of this value."); EmoteAudioMaxVolume = AddConfigEntry("Emote Audio", "MaxEmoteAudioVolume", 0.8f, "The max volume that emote audio will reach. Emote audio volume may dynamically change by increasing the number of players syncing an emote, or adjusting the volume slider in the emote menu. This setting will not affect emote audio volume, aside from preventing the volume from going higher than this value."); EmoteAudioIncreasePerPlayerSyncing = AddConfigEntry("Emote Audio", "VolumeGainPerPlayerSyncingEmote", 0.05f, "By how much emote audio volume will increase by per player syncing with that emote."); EmoteAudioMinDistance = AddConfigEntry("Emote Audio", "MinAudioDistance", 10f, "The range from an emote audio source at which the volume will start to fade."); EmoteAudioMaxDistance = AddConfigEntry("Emote Audio", "MaxAudioDistance", 40f, "The range from an emote audio source at which the audio can no longer be heard."); EmoteNameColorTier0 = AddConfigEntry("Accessibility", "EmoteNameColorCommon", "#00FF00", "The color of the [common] emote name in the terminal."); EmoteNameColorTier1 = AddConfigEntry("Accessibility", "EmoteNameColorRare", "#2828FF", "The color of the [rare] emote name in the terminal."); EmoteNameColorTier2 = AddConfigEntry("Accessibility", "EmoteNameColorEpic", "#AA00EE", "The color of the [epic] emote name in the terminal."); EmoteNameColorTier3 = AddConfigEntry("Accessibility", "EmoteNameColorLegendary", "#FF2222", "The color of the [legendary] emote name in the terminal."); VerboseLogs = AddConfigEntry("Other", "VerboseLogs", defaultValue: false, "Set to true if you want to receive ALL logs. Most of the time, you will want to keep this disabled unless troubleshooting a specific issue."); config.Save(); } private static ConfigEntry<T> AddConfigEntry<T>(string section, string name, T defaultValue, string description) { return config.Bind<T>(section, name, defaultValue, description); } } public class OnAnimatorIKHandler : MonoBehaviour { public float handIKWeight = 0.8f; private Animator _animator; private EmoteController _emoteController; private void Awake() { _animator = ((Component)this).GetComponent<Animator>(); if ((Object)(object)_animator == (Object)null) { Logger.LogWarning("OnIKHandler must be attached to a gameobject with an animator component."); } } 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); } } } public void SetParentEmoteController(EmoteController emoteController) { _emoteController = emoteController; } } [HarmonyPatch] public static class EmoteManager { public static List<UnlockableEmote> AllUnlockableEmotes; public static Dictionary<string, UnlockableEmote> AllUnlockableEmotesDict; public static List<UnlockableEmote> ComplementaryEmotes; internal static List<UnlockableEmote> ComplementaryEmotesDefault; public static List<string> AllFavoriteEmotes; public static List<string> AllQuickEmotes; public static List<UnlockableEmote> AllEmotesTier0; public static List<UnlockableEmote> AllEmotesTier1; public static List<UnlockableEmote> AllEmotesTier2; public static List<UnlockableEmote> AllEmotesTier3; public static 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 < BetterEmote.CustomAnimationClips.Count; i++) { AnimationClip val = BetterEmote.CustomAnimationClips[i]; UnlockableEmote unlockableEmote = new UnlockableEmote { EmoteId = i, EmoteName = ((Object)val).name, DisplayName = "", AnimationClip = val, Rarity = 0 }; unlockableEmote.Rarity = (BetterEmote.AnimationClipsTier1.Contains(val) ? 1 : (BetterEmote.AnimationClipsTier2.Contains(val) ? 2 : (BetterEmote.AnimationClipsTier3.Contains(val) ? 3 : 0))); if (BetterEmote.ComplementaryAnimationClips.Contains(val)) { unlockableEmote.Complementary = true; } if (unlockableEmote.EmoteName.Contains("_start") && !unlockableEmote.EmoteName.Contains("_start_")) { string key = unlockableEmote.EmoteName.Replace("_start", "_loop"); AnimationClip val2 = BetterEmote.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) { Logger.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)) { Logger.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 = BetterEmote.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 (BetterEmote.ComplementaryAnimationClips.Contains(val) && unlockableEmote.Purchasable) { unlockableEmote.Complementary = true; ComplementaryEmotesDefault.Add(unlockableEmote); } } 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); } public static UnlockableEmote GetEmoteByName(string emoteName) { return AllUnlockableEmotes.FirstOrDefault((UnlockableEmote emote) => emote.EmoteName == emoteName); } } [HarmonyPatch] public class EmoteSyncGroup { public static int CurrentEmoteSyncId; public static Dictionary<int, EmoteSyncGroup> AllEmoteSyncGroups = new Dictionary<int, EmoteSyncGroup>(); public EmoteAudioPlayer CurrentAudioPlayer; public Dictionary<UnlockableEmote, EmoteAudioSource> CurrentEmoteAudioSources; public Dictionary<UnlockableEmote, EmoteController> LeadEmoteControllerByEmote = new Dictionary<UnlockableEmote, EmoteController>(); public UnlockableEmote PerformingEmote; public List<EmoteController> SyncGroup; public int SyncId = -1; public float TimeStartedEmote; public bool UseAudio; public EmoteController leadEmoteController { get { List<EmoteController> syncGroup = SyncGroup; return (syncGroup != null && syncGroup.Count > 0) ? SyncGroup[0] : null; } } public EmoteAudioSource leadEmoteAudioSource { get { Dictionary<UnlockableEmote, EmoteAudioSource> currentEmoteAudioSources = CurrentEmoteAudioSources; if (currentEmoteAudioSources == null || currentEmoteAudioSources.Count <= 0) { return null; } foreach (EmoteController item in SyncGroup) { if ((Object)(object)item?.personalEmoteAudioSource != (Object)null && CurrentEmoteAudioSources.ContainsValue(item.personalEmoteAudioSource)) { return item.personalEmoteAudioSource; } } return null; } } public EmoteSyncGroup(EmoteController emoteController, bool useAudio = true) { SyncGroup = new List<EmoteController> { emoteController }; SyncId = CurrentEmoteSyncId++; PerformingEmote = emoteController.PerformingEmote; LeadEmoteControllerByEmote.Add(PerformingEmote, emoteController); UseAudio = useAudio; if (useAudio && PerformingEmote.hasAudio) { if (!PerformingEmote.isExternalSourceAudio) { CurrentEmoteAudioSources = new Dictionary<UnlockableEmote, EmoteAudioSource>(); if ((Object)(object)emoteController.personalEmoteAudioSource == (Object)null) { Logger.LogError("Attempted to perform emote with personal audio source, which is null."); 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 { Logger.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 static EmoteSyncGroup CreateEmoteSyncGroup(EmoteController emoteController, bool useAudio = true) { return new EmoteSyncGroup(emoteController, useAudio); } 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.isExternalSourceAudio) { 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) { 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() { Logger.LogVerbose("Cleaning up emote sync group with id: " + SyncId); if (CurrentEmoteAudioSources != null) { foreach (EmoteAudioSource item in CurrentEmoteAudioSources.Values.Where((EmoteAudioSource emoteAudioSource) => emoteAudioSource != null)) { item.StopAudio(); item.RemoveFromEmoteSyncGroup(); } CurrentEmoteAudioSources = null; } if (CurrentAudioPlayer != null) { CurrentAudioPlayer.StopAudio(); CurrentAudioPlayer.RemoveFromEmoteSyncGroup(); } AllEmoteSyncGroups.Remove(SyncId); SyncGroup = null; SyncId = -1; } public void UpdateAudioVolume() { if (CurrentEmoteAudioSources != null) { foreach (EmoteAudioSource item in CurrentEmoteAudioSources.Values.Where((EmoteAudioSource emoteAudioSource) => emoteAudioSource != null)) { item.UpdateVolume(); } } CurrentAudioPlayer?.UpdateVolume(); } public void AssignExternalAudioPlayer(EmoteAudioPlayer audioPlayer) { if ((Object)(object)CurrentAudioPlayer != (Object)null) { CurrentAudioPlayer.StopAudio(); CurrentAudioPlayer.RemoveFromEmoteSyncGroup(); CurrentAudioPlayer = null; } if (UseAudio && PerformingEmote.hasAudio) { CurrentAudioPlayer = audioPlayer; if ((Object)(object)CurrentAudioPlayer != (Object)null) { CurrentAudioPlayer.SyncWithEmoteControllerAudio(leadEmoteController); CurrentAudioPlayer.AddToEmoteSyncGroup(this); CurrentAudioPlayer.UpdateVolume(); } } } } public static class EmoteUnlockManager { public static readonly Dictionary<string, int> BossTiers = new Dictionary<string, int> { { "Eikthyr", 1 }, { "gd_king", 2 }, { "Bonemass", 3 }, { "Dragon", 4 }, { "GoblinKing", 5 }, { "SeekerQueen", 6 }, { "Fader", 7 } }; private static readonly Dictionary<int, List<UnlockableEmote>> EmotesByTier = new Dictionary<int, List<UnlockableEmote>>(); public static void Initialize() { Logger.Log("Initializing EmoteUnlockManager..."); OrganizeEmotesByTier(); Logger.Log("EmoteUnlockManager initialized!"); } private static void OrganizeEmotesByTier() { for (int i = 1; i <= 7; i++) { EmotesByTier[i] = new List<UnlockableEmote>(); } foreach (UnlockableEmote allUnlockableEmote in EmoteManager.AllUnlockableEmotes) { if (allUnlockableEmote != null && !string.IsNullOrEmpty(allUnlockableEmote.EmoteName) && !SaveManager.IsEmoteUnlocked(allUnlockableEmote)) { int key = DetermineEmoteTier(allUnlockableEmote); if (EmotesByTier.ContainsKey(key)) { EmotesByTier[key].Add(allUnlockableEmote); } } } for (int j = 1; j <= 7; j++) { Logger.Log($"Tier {j} emotes: {EmotesByTier[j].Count}"); } } private static int DetermineEmoteTier(UnlockableEmote emote) { int rarity = emote.Rarity; if (rarity >= 0 && rarity <= 3) { switch (emote.Rarity) { case 0: return Random.Range(1, 3); case 1: return Random.Range(2, 5); case 2: return Random.Range(4, 6); case 3: return Random.Range(5, 8); } } if (emote.hasAudio && emote.loopable) { return Random.Range(5, 8); } if (emote.hasAudio) { return Random.Range(3, 6); } if (emote.AnimationClip.length > 5f) { return Random.Range(2, 5); } return Random.Range(1, 3); } public static void UnlockEmoteForBoss(string bossName, string playerName) { if (EmotesByTier.Count == 0) { Logger.LogWarning("EmotesByTier is empty. Initializing..."); OrganizeEmotesByTier(); } if (!BossTiers.TryGetValue(bossName, out var value)) { Logger.LogWarning("Unknown boss: " + bossName); return; } Logger.Log($"Player killed boss: {bossName} (Tier {value}) by {playerName}"); List<UnlockableEmote> list = new List<UnlockableEmote>(); for (int i = 1; i <= value; i++) { int num = ((i != value) ? 1 : 3); for (int j = 0; j < num; j++) { list.AddRange(EmotesByTier[i].Where((UnlockableEmote emote) => !SaveManager.IsEmoteUnlocked(emote))); } } if (list.Count == 0) { Logger.Log("No new emotes to unlock for this tier"); return; } UnlockableEmote unlockableEmote = list[Random.Range(0, list.Count)]; if (unlockableEmote != null) { Logger.Log("Unlocking new emote: " + unlockableEmote.EmoteName + "!"); SaveManager.UnlockEmoteLocal(unlockableEmote, playerName); SaveManager.SaveUnlockedEmote(unlockableEmote); ShowUnlockNotification(unlockableEmote, bossName); } } private static void ShowUnlockNotification(UnlockableEmote emote, string bossName) { if (emote != null) { string bossColor = GetBossColor(bossName); MessageHud.instance.ShowMessage((MessageType)2, "<size=18><color=yellow>✨ EMOTE UNLOCKED ✨</color></size>\n<size=16><color=" + emote.nameColor + ">" + emote.rarityText + " - " + emote.DisplayName + "</color></size>\n<color=white>Reward for defeating <color=" + bossColor + ">" + bossName + "</color></color>\n<size=12><i>Press [<color=white>" + KeybindDisplayNames.GetKeybindDisplayName(ConfigSettings.OpenEmoteMenuKeybind.Value) + "</color>] to try your new emote!</i></size>", 0, (Sprite)null, false, true); PlayUnlockFx(Player.m_localPlayer); } } private static void PlayUnlockFx(Player player) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_0091: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !((Object)(object)ZNetScene.instance == (Object)null)) { Vector3 val = ((Component)player).transform.position + Vector3.up * 1.3f; GameObject prefab = ZNetScene.instance.GetPrefab("vfx_perfectblock"); if ((Object)(object)prefab != (Object)null) { Object.Instantiate<GameObject>(prefab, val, Quaternion.identity); } GameObject prefab2 = ZNetScene.instance.GetPrefab("sfx_Potion_frostresist_Start"); if ((Object)(object)prefab2 != (Object)null) { Object.Instantiate<GameObject>(prefab2, ((Component)player).transform.position, Quaternion.identity); } } } private static string GetBossColor(string bossName) { if (1 == 0) { } string result = bossName switch { "Eikthyr" => "#8B4513", "gd_king" => "#228B22", "Bonemass" => "#2E8B57", "Dragon" => "#1E90FF", "GoblinKing" => "#FFD700", "SeekerQueen" => "#9932CC", "Fader" => "#DC143C", _ => "white", }; if (1 == 0) { } return result; } } [HarmonyPatch] public static class EmoteUnlockPatches { [HarmonyPatch(typeof(ZNet), "Awake")] [HarmonyPostfix] public static void Init() { ZRoutedRpc.instance.Register<ZPackage>("BetterEmote.BossKilledRPC", (Action<long, ZPackage>)OnBossKilledRPC); } [HarmonyPatch(typeof(Player), "SetLocalPlayer")] [HarmonyPostfix] public static void OnPlayerIDSet(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !SaveManager.IsInitialized) { SaveManager.ResetEmotesLocal(); EmoteUnlockManager.Initialize(); Helper.InvokeLater(1f, delegate { SaveManager.LoadUnlockedEmotes(); SaveManager.LoadFavoritedEmotes(); SaveManager.IsInitialized = true; }); } } [HarmonyPatch(typeof(Character), "OnDeath")] [HarmonyPostfix] public static void OnCharacterDeath(Character __instance) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown if (!((Object)(object)__instance == (Object)null) && __instance.IsBoss()) { string text = __instance.GetHoverName() ?? __instance.m_name; string bossName = GetBossName(text.ToLower()); ZPackage val = new ZPackage(); val.Write(bossName); ZRoutedRpc.instance.InvokeRoutedRPC(0L, "BetterEmote.BossKilledRPC", new object[1] { val }); } } private static string GetBossName(string bossName) { if (bossName.Contains("eikthyr")) { bossName = "Eikthyr"; } else if (bossName.Contains("elder") || bossName.Contains("gd_king")) { bossName = "gd_king"; } else if (bossName.Contains("bonemass")) { bossName = "Bonemass"; } else if (bossName.Contains("moder") || bossName.Contains("dragon")) { bossName = "Dragon"; } else if (bossName.Contains("yagluth") || bossName.Contains("goblinking")) { bossName = "GoblinKing"; } else if (bossName.Contains("queen") || bossName.Contains("seekerqueen")) { bossName = "SeekerQueen"; } else { if (!bossName.Contains("fader")) { Logger.LogWarning("Not a recognized boss: " + bossName); return ""; } bossName = "Fader"; } return bossName; } private static void OnBossKilledRPC(long sender, ZPackage pkg) { Logger.Log($"OnBossKilledRPC called from sender: {sender}"); if (sender == ZRoutedRpc.instance.GetServerPeerID() || sender == ZDOMan.instance.m_sessionID) { string text = pkg.ReadString(); Player localPlayer = Player.m_localPlayer; string text2 = ((localPlayer != null) ? localPlayer.GetPlayerName() : null) ?? "unknown"; Logger.Log("Processing boss kill reward for: " + text + ", player: " + text2); EmoteUnlockManager.UnlockEmoteForBoss(text, text2); } } } internal static class Helper { private sealed class HelperRunner : MonoBehaviour { } private static HelperRunner _runner; private static ZNet zNet => ZNet.instance; public static bool isClient => !zNet.IsServer(); public static bool isServer => zNet.IsDedicated() || zNet.IsServer(); public static bool isHost => zNet.IsServer() && !zNet.IsDedicated(); public static bool isDedicated => zNet.IsDedicated(); public static Player localPlayerController { get { Player localPlayer = Player.m_localPlayer; return (localPlayer != null) ? ((Component)localPlayer).GetComponent<Player>() : null; } } public static EmoteControllerPlayer emoteControllerLocal => EmoteControllerPlayer.emoteControllerLocal; public static bool TryGetPlayerByClientId(long clientId, out Player playerController) { //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) playerController = null; if ((Object)(object)ZNet.instance == (Object)null) { Logger.LogWarning($"ZNet.instance is null when looking for client {clientId}"); return false; } ZNetPeer peer = ZNet.instance.GetPeer(clientId); ZDOID zDOID; if (isDedicated) { if (peer != null) { ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.FindInstance(peer.m_characterID) : null); if ((Object)(object)val != (Object)null) { playerController = val.GetComponent<Player>(); if ((Object)(object)playerController != (Object)null) { Logger.Log($"Found player for client {clientId}: {playerController.GetPlayerName()} via ZNetPeer"); return true; } } } foreach (ZDO value in ZNet.instance.m_zdoMan.m_objectsByID.Values) { int stableHashCode = StringExtensionMethods.GetStableHashCode("Player"); if (value.GetPrefab() != stableHashCode && value.m_prefab != stableHashCode) { continue; } long owner = value.GetOwner(); if (owner != clientId && ((ZDOID)(ref value.m_uid)).UserID != clientId && ((ZDOID)(ref value.m_uid)).ID != clientId) { continue; } ZNetScene instance2 = ZNetScene.instance; ZNetView val2 = ((instance2 != null) ? instance2.FindInstance(value) : null); GameObject val3 = ((val2 != null) ? ((Component)val2).gameObject : null); if ((Object)(object)val3 == (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { val3 = ZNetScene.instance.CreateObject(value); } if ((Object)(object)val3 != (Object)null) { playerController = val3.GetComponent<Player>(); if ((Object)(object)playerController != (Object)null) { Logger.Log($"Found player for client {clientId}: {playerController.GetPlayerName()} via ZDO"); return true; } } foreach (Player allPlayer in Player.GetAllPlayers()) { zDOID = ((Character)allPlayer).GetZDOID(); if (((ZDOID)(ref zDOID)).UserID != clientId) { zDOID = ((Character)allPlayer).GetZDOID(); if (((ZDOID)(ref zDOID)).ID != clientId) { continue; } } playerController = allPlayer; Logger.Log($"Found player for client {clientId}: {playerController.GetPlayerName()} via GetAllPlayers"); return true; } Logger.Log($"Found ZDO for client {clientId}, but couldn't get Player component"); return false; } Logger.Log($"Could not find player ZDO for client {clientId} on dedicated server"); return false; } if (peer != null) { foreach (Player allPlayer2 in Player.GetAllPlayers()) { zDOID = ((Character)allPlayer2).GetZDOID(); if (((ZDOID)(ref zDOID)).UserID == ((ZDOID)(ref peer.m_characterID)).UserID) { playerController = allPlayer2; Logger.Log("Found player by peer match: " + allPlayer2.GetPlayerName()); return true; } } } if (ZNet.instance.m_players != null) { PlayerInfo playerInfo = ZNet.instance.m_players.Find((PlayerInfo p) => ((ZDOID)(ref p.m_characterID)).ID == clientId); if (playerInfo.m_name != null) { Logger.Log("Found player info: " + playerInfo.m_name); playerController = Player.GetAllPlayers().Find((Player p) => p.GetPlayerName() == playerInfo.m_name); if ((Object)(object)playerController != (Object)null) { return true; } } } Logger.LogWarning($"Failed to find player for client ID: {clientId}"); return false; } public static EmoteController GetEmoteControllerById(long emoteControllerId) { Logger.Log("EmoteController.AllEmoteControllers.Values: " + string.Join(", ", EmoteControllerPlayer.AllPlayerEmoteControllers.Values.Select((EmoteControllerPlayer c) => c.emoteControllerId))); return EmoteControllerPlayer.AllPlayerEmoteControllers.Values.FirstOrDefault((EmoteControllerPlayer emoteController) => emoteController.emoteControllerId == emoteControllerId); } private static void EnsureRunner() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_runner != (Object)null)) { GameObject val = new GameObject("BetterEmote_HelperRunner"); Object.DontDestroyOnLoad((Object)(object)val); _runner = val.AddComponent<HelperRunner>(); } } public static void InvokeLater(float delaySeconds, Action action) { if (action != null) { EnsureRunner(); ((MonoBehaviour)_runner).StartCoroutine(Run(delaySeconds, action)); } } private static IEnumerator Run(float delay, Action action) { if (delay > 0f) { yield return (object)new WaitForSeconds(delay); } try { action(); } catch (Exception arg) { Logger.LogError($"InvokeLater exception: {arg}"); } } } internal static class Logger { private static ManualLogSource logger; public static void InitLogger() { try { logger = Logger.CreateLogSource($"{((BaseUnityPlugin)BetterEmote.Instance).Info.Metadata.Name}-{((BaseUnityPlugin)BetterEmote.Instance).Info.Metadata.Version}"); } catch { logger = BetterEmote.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; } } [HarmonyPatch] public static class SaveManager { [Serializable] private class BetterEmoteSaveModel { public List<string> Unlocked = new List<string>(); public List<string> Favorites = new List<string>(); } private static readonly object _fileLock = new object(); 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 bool IsInitialized { get; set; } = false; [HarmonyPatch(typeof(Player), "SetLocalPlayer")] [HarmonyPostfix] private static void ResetGameValues(Player __instance) { if (!((Object)(object)__instance == (Object)null)) { EmoteController.AllEmoteControllers?.Clear(); EmoteControllerPlayer.AllPlayerEmoteControllers?.Clear(); EmoteAudioSource.AllEmoteAudioSources?.Clear(); EmoteManager.ComplementaryEmotes = new List<UnlockableEmote>(EmoteManager.ComplementaryEmotesDefault ?? new List<UnlockableEmote>()); } } public static void ResetProgressLocal(bool forceResetAll = false) { Logger.Log("Resetting progress."); if (forceResetAll) { ResetEmotesLocal(); } } public static bool IsEmoteUnlocked(string emoteName, string playerUsername = "") { if (EmoteManager.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(Player playerController) { return GetUnlockedEmotes(((Object)(object)playerController != (Object)null) ? playerController.GetPlayerName() : ""); } 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, string playerUsername = "") { foreach (UnlockableEmote emote in emotes) { UnlockEmoteLocal(emote, playerUsername); } } public static void UnlockEmoteLocal(int emoteId, string playerUsername = "") { UnlockEmoteLocal((emoteId >= 0 && emoteId < EmoteManager.AllUnlockableEmotes.Count) ? EmoteManager.AllUnlockableEmotes[emoteId] : null, playerUsername); } public static void UnlockEmoteLocal(UnlockableEmote emote, string playerUsername = "") { if (emote == null) { return; } List<UnlockableEmote> list = UnlockedEmotes; if (playerUsername != "" && playerUsername != Helper.localPlayerController.GetPlayerName()) { 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 (EmoteManager.AllFavoriteEmotes.Contains(emote.EmoteName) && !UnlockedFavoriteEmotes.Contains(emote)) { UnlockedFavoriteEmotes.Add(emote); } if (!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() { Logger.Log("Resetting unlocked emotes."); UnlockedEmotes.Clear(); UnlockedEmotesTier0.Clear(); UnlockedEmotesTier1.Clear(); UnlockedEmotesTier2.Clear(); UnlockedEmotesTier3.Clear(); EmotesUnlockedThisSession.Clear(); UnlockedEmotesByPlayer.Clear(); foreach (Player s_player in Player.s_players) { if (s_player.GetPlayerID() != 0) { UnlockedEmotesByPlayer.Add(s_player.GetPlayerName(), ((Object)(object)s_player == (Object)(object)Helper.localPlayerController || ConfigSync.Instance.syncShareEverything) ? UnlockedEmotes : new List<UnlockableEmote>()); } } UnlockEmotesLocal(ConfigSync.Instance.syncUnlockEverything ? EmoteManager.AllUnlockableEmotes : EmoteManager.ComplementaryEmotes); UpdateUnlockedFavoriteEmotes(); } public static void UpdateUnlockedFavoriteEmotes() { UnlockedFavoriteEmotes?.Clear(); if (EmoteManager.AllFavoriteEmotes == null || EmoteManager.AllUnlockableEmotesDict == null || UnlockedFavoriteEmotes == null) { return; } foreach (string allFavoriteEmote in EmoteManager.AllFavoriteEmotes) { if (EmoteManager.AllUnlockableEmotesDict.TryGetValue(allFavoriteEmote, out var value)) { List<UnlockableEmote> emoteSyncGroup = value.EmoteSyncGroup; if (emoteSyncGroup != null && emoteSyncGroup.Count > 0) { value = value.EmoteSyncGroup[0]; } if (IsEmoteUnlocked(value)) { UnlockedFavoriteEmotes.Add(value); } } } } public static void SaveUnlockedEmote(UnlockableEmote emote) { if (emote != null) { BetterEmoteSaveModel betterEmoteSaveModel = LoadFile(); if (!betterEmoteSaveModel.Unlocked.Contains(emote.EmoteName)) { betterEmoteSaveModel.Unlocked.Add(emote.EmoteName); } SaveFile(betterEmoteSaveModel); Logger.Log("Saved emote " + emote.EmoteName + " for character."); } } public static void LoadUnlockedEmotes() { Player localPlayer = Player.m_localPlayer; string text = ((localPlayer != null) ? localPlayer.GetPlayerName() : null); BetterEmoteSaveModel betterEmoteSaveModel = LoadFile(); if (betterEmoteSaveModel.Unlocked == null || betterEmoteSaveModel.Unlocked.Count == 0) { Logger.LogVerbose("LoadUnlockedEmotes: no saved unlocked emotes for player " + text + "."); return; } int num = 0; foreach (string item in betterEmoteSaveModel.Unlocked.Distinct()) { if (!string.IsNullOrEmpty(item)) { if (!EmoteManager.AllUnlockableEmotesDict.TryGetValue(item, out var value)) { Logger.LogVerbose("LoadUnlockedEmotes: saved emote not found in current dictionary: " + item); } else if (!IsEmoteUnlocked(value)) { Logger.LogVerbose("Re-unlocking previously unlocked emote: " + item); UnlockEmoteLocal(value); num++; } } } if (num > 0) { Logger.Log($"Loaded {num} previously unlocked emotes for character: {text}"); } else { Logger.LogVerbose("LoadUnlockedEmotes: no new emotes were applied (all already unlocked)."); } } public static void SaveFavoritedEmotes() { if (EmoteManager.AllFavoriteEmotes != null) { BetterEmoteSaveModel betterEmoteSaveModel = LoadFile(); betterEmoteSaveModel.Favorites = EmoteManager.AllFavoriteEmotes.Distinct().ToList(); SaveFile(betterEmoteSaveModel); Logger.Log("Saved favorited emotes"); } } public static void LoadFavoritedEmotes() { EmoteManager.AllFavoriteEmotes.Clear(); try { BetterEmoteSaveModel betterEmoteSaveModel = LoadFile(); foreach (string favorite in betterEmoteSaveModel.Favorites) { if (!string.IsNullOrEmpty(favorite)) { EmoteManager.AllFavoriteEmotes.Add(favorite); } } Logger.Log($"Loaded {EmoteManager.AllFavoriteEmotes.Count} favorited emotes"); UpdateUnlockedFavoriteEmotes(); } catch (Exception ex) { Logger.LogError("Error while trying to load favorited emotes. They will likely be reset.\n" + ex); } } public static bool DeleteCharacterSave() { Player localPlayer = Player.m_localPlayer; string text = ((localPlayer != null) ? localPlayer.GetPlayerName() : null); if (string.IsNullOrEmpty(text)) { Logger.LogErrorVerbose("Failed to find player name."); return false; } try { string characterFilePath = GetCharacterFilePath(text); if (File.Exists(characterFilePath)) { File.Delete(characterFilePath); Logger.Log("Deleted emote save file: " + characterFilePath); return true; } } catch (Exception ex) { Logger.LogErrorVerbose("Failed deleting emote save file: " + ex); } return false; } private static BetterEmoteSaveModel LoadFile() { Player localPlayer = Player.m_localPlayer; string text = ((localPlayer != null) ? localPlayer.GetPlayerName() : null); if (string.IsNullOrEmpty(text)) { Logger.LogErrorVerbose("Failed to find player name."); return new BetterEmoteSaveModel(); } try { string characterFilePath = GetCharacterFilePath(text); if (!File.Exists(characterFilePath)) { Logger.LogVerbose("LoadFile: no file at `" + characterFilePath + "`"); return new BetterEmoteSaveModel(); } string text2 = File.ReadAllText(characterFilePath, Encoding.UTF8); if (string.IsNullOrWhiteSpace(text2)) { return new BetterEmoteSaveModel(); } BetterEmoteSaveModel betterEmoteSaveModel = JsonUtility.FromJson<BetterEmoteSaveModel>(text2); Logger.LogVerbose($"LoadFile: parsed Unlocked: {betterEmoteSaveModel.Unlocked.Count} Favorites: {betterEmoteSaveModel.Favorites.Count}"); return betterEmoteSaveModel; } catch (Exception ex) { Logger.LogErrorVerbose("Failed reading emote save file: " + ex); return new BetterEmoteSaveModel(); } } private static void SaveFile(BetterEmoteSaveModel data) { Player localPlayer = Player.m_localPlayer; string text = ((localPlayer != null) ? localPlayer.GetPlayerName() : null); if (string.IsNullOrEmpty(text)) { Logger.LogErrorVerbose("Failed to find player name."); return; } try { string characterFilePath = GetCharacterFilePath(text); string contents = JsonUtility.ToJson((object)data, false); lock (_fileLock) { File.WriteAllText(characterFilePath, contents, Encoding.UTF8); } } catch (Exception ex) { Logger.LogErrorVerbose("Failed writing emote save file: " + ex); } } private static string GetCharacterFilePath(string playerName) { string text = Path.Combine(Application.persistentDataPath, "characters"); try { Directory.CreateDirectory(text); } catch { } char[] invalid = Path.GetInvalidFileNameChars(); string text2 = string.Concat(playerName.Select((char c) => invalid.Contains(c) ? '_' : c)); return Path.Combine(text, text2 + "_BetterEmote.json"); } } public class UnlockableEmote { public static string[] RarityColorCodes = new string[4] { ConfigSettings.EmoteNameColorTier0.Value, ConfigSettings.EmoteNameColorTier1.Value, ConfigSettings.EmoteNameColorTier2.Value, ConfigSettings.EmoteNameColorTier3.Value }; private bool _isExternalSourceAudio; public AnimationClip AnimationClip; public bool CanMoveWhileEmoting = false; public bool CanSyncEmote = false; public bool Complementary = false; public string DisplayName = ""; public int EmoteId; public string EmoteName; public List<UnlockableEmote> EmoteSyncGroup; public string EmoteSyncGroupName = ""; public bool IsPose = false; public string OverrideAudioClipName = ""; public string OverrideAudioLoopClipName = ""; public List<string> PropNamesInEmote; public bool Purchasable = true; public bool RandomEmote = false; public int Rarity = 0; public float RecordSongLoopValue = 0f; public GameObject RequiredHeldPropPrefab = null; public bool RequiresHeldProp = false; public AnimationClip TransitionsToClip = null; 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 => !string.IsNullOrEmpty(audioClipName) || !string.IsNullOrEmpty(audioLoopClipName); public bool isExternalSourceAudio { get { return _isExternalSourceAudio && !ConfigSettings.DisableExternalSourceRequirement.Value; } set { _isExternalSourceAudio = 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 => EmoteManager.AllFavoriteEmotes.Contains(EmoteName); public string rarityText { get { int rarity = Rarity; if (1 == 0) { } string result = rarity switch { 0 => "Common", 1 => "Rare", 2 => "Epic", 3 => "Legendary", _ => "Invalid", }; if (1 == 0) { } return result; } } 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; } return EmoteSyncGroup != null && EmoteSyncGroup.Any((UnlockableEmote emote) => (Object)(object)clip == (Object)(object)emote.AnimationClip || (Object)(object)clip == (Object)(object)emote.TransitionsToClip); } public AudioClip LoadAudioClip() { if (!hasAudio || audioClipName.Length <= 0) { return null; } return AudioManager.LoadAudioClip(audioClipName); } public AudioClip LoadAudioLoopClip() { if (!hasAudio || TransitionsToClip == null || audioLoopClipName.Length <= 0) { return null; } return AudioManager.LoadAudioClip(audioLoopClipName); } } } namespace BetterEmote.UI { [HarmonyPatch] public static class AnimationPreviewer { private const int RenderLayer = 23; public static bool Enabled; public static GameObject PreviewBoombox; private static Camera renderingCamera; private static GameObject previewPlayerObject; private static EmoteController simpleEmoteController; private static int renderLayerMask => 8388608; public static void InitializeAnimationRenderer() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_00c8: 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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) Logger.Log("Initializing animation renderer"); renderingCamera = new GameObject("AnimationRenderingCamera").AddComponent<Camera>(); Object.Destroy((Object)(object)((Component)renderingCamera).GetComponent<AudioListener>()); renderingCamera.cullingMask = renderLayerMask; renderingCamera.clearFlags = (CameraClearFlags)2; renderingCamera.cameraType = (CameraType)4; renderingCamera.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0f); renderingCamera.allowHDR = false; renderingCamera.allowMSAA = false; renderingCamera.farClipPlane = 5f; renderingCamera.targetTexture = EmoteMenu.RenderTexture; ((Component)renderingCamera).transform.position = Vector3.down * 1000f; EmoteMenu.RenderTextureImageUI.texture = (Texture)(object)EmoteMenu.RenderTexture; Light val = new GameObject("Spotlight").AddComponent<Light>(); val.type = (LightType)0; ((Component)val).transform.SetParent(((Component)renderingCamera).transform); ((Component)val).transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); val.intensity = 1f; val.range = 10f; val.innerSpotAngle = 60f; val.spotAngle = 80f; ((Component)val).gameObject.layer = 23; DisableRenderCameraNextFrame(); Enabled = true; } [HarmonyPatch(typeof(Player), "SetLocalPlayer")] [HarmonyPostfix] public static void InitializePlayerCloneRenderObject(Player __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)Player.m_localPlayer != (Object)(object)__instance) && Enabled && Object.op_Implicit((Object)(object)BetterEmote.RadialMenuPrefab) && (Object)(object)previewPlayerObject == (Object)null) { ((MonoBehaviour)__instance).StartCoroutine(InitPlayerCloneAfterSpawnAnimation()); } IEnumerator InitPlayerCloneAfterSpawnAnimation() { yield return (object)new WaitForSeconds(2f); if (Logger.Assert((Object)(object)renderingCamera != (Object)null, "Render camera is null!")) { previewPlayerObject = new GameObject("PreviewPlayerAnimationObject"); previewPlayerObject.transform.SetParent(((Component)renderingCamera).transform); previewPlayerObject.transform.localPosition = new Vector3(0f, -1.25f, 3f); previewPlayerObject.transform.localEulerAngles = new Vector3(0f, 180f, 0f); previewPlayerObject.SetActive(false); Transform playerVisual = ((Component)__instance).transform.Find("Visual"); if (Object.op_Implicit((Object)(object)playerVisual)) { GameObject previewVisual = Object.Instantiate<GameObject>(((Component)playerVisual).gameObject, previewPlayerObject.transform); Component[] componentsInChildren = previewVisual.GetComponentsInChildren<Component>(true); foreach (Component component in componentsInChildren) { if ((!(component is Transform) && !(component is SkinnedMeshRenderer) && !(component is MeshFilter) && !(component is Animator)) || 1 == 0) { Object.DestroyImmediate((Object)(object)component); } } ((Object)previewVisual).name = "Visual"; previewVisual.transform.localPosition = Vector3.zero; previewVisual.transform.localRotation = Quaternion.identity; previewVisual.transform.localScale = Vector3.one; previewVisual.SetActive(true); simpleEmoteController = previewPlayerObject.AddComponent<EmoteController>(); simpleEmoteController.Initialize(); simpleEmoteController.CreateBoneMap(EmoteControllerPlayer.SourceBoneNames); simpleEmoteController.smoothTransitionToEmote = false; GameObject boomboxPrefab = null; if ((Object)(object)ObjectDB.instance != (Object)null && (Object)(object)ObjectDB.instance.GetItemPrefab("Amber") != (Object)null) { boomboxPrefab = ObjectDB.instance.GetItemPrefab("Wisp"); } if (Object.op_Implicit((Object)(object)boomboxPrefab)) { Logger.Log("Creating boombox prefab"); PreviewBoombox = Object.Instantiate<GameObject>(boomboxPrefab, previewPlayerObject.transform); ((Object)PreviewBoombox).name = "BoomboxAudioIndicator"; PreviewBoombox.transform.localPosition = new Vector3(-1f, 0.2f, -0.5f); PreviewBoombox.transform.localEulerAngles = new Vector3(0f, 40f, 90f); Component[] componentsInChildren2 = PreviewBoombox.GetComponentsInChildren<Component>(true); foreach (Component component2 in componentsInChildren2) { if ((!(component2 is Transform) && !(component2 is SkinnedMeshRenderer) && !(component2 is MeshFilter) && !(component2 is Animator)) || 1 == 0) { Object.DestroyImmediate((Object)(object)component2); } } } yield return null; SetObjectLayerRecursive(previewPlayerObject, 23); } } } } public static void SetPreviewAnimation(UnlockableEmote emote) { if (!Enabled || !Object.op_Implicit((Object)(object)previewPlayerObject) || !Object.op_Implicit((Object)(object)simpleEmoteController)) { return; } if (Enabled && emote != null) { previewPlayerObject.SetActive(true); ((Behaviour)renderingCamera).enabled = true; if (Object.op_Implicit((Object)(object)PreviewBoombox)) { PreviewBoombox.SetActive(emote.hasAudio && emote.isExternalSourceAudio); } simpleEmoteController.PerformEmote(emote); if (simpleEmoteController.emotingProps == null) { return; } { foreach (PropObject emotingProp in simpleEmoteController.emotingProps) { emotingProp.SetPropLayer(23); } return; } } simpleEmoteController.StopPerformingEmote(); previewPlayerObject.SetActive(false); if (Object.op_Implicit((Object)(object)PreviewBoombox)) { PreviewBoombox.SetActive(false); } DisableRenderCameraNextFrame(); } private static void DisableRenderCameraNextFrame() { ((MonoBehaviour)Hud.instance).StartCoroutine(DisableRenderCameraNextFrameCoroutine()); static IEnumerator DisableRenderCameraNextFrameCoroutine() { yield return null; ((Behaviour)renderingCamera).enabled = false; } } private static void SetObjectLayerRecursive(GameObject obj, int layer) { if (Object.op_Implicit((Object)(object)obj)) { obj.layer = layer; for (int i = 0; i < obj.transform.childCount; i++) { Transform child = obj.transform.GetChild(i); SetObjectLayerRecursive((child != null) ? ((Component)child).gameObject : null, layer); } } } } [HarmonyPatch] public static class EmoteMenu { public static GameObject MenuGameObject; public static RectTransform MenuTransform; public static CanvasGroup CanvasGroup; public static RawImage RenderTextureImageUI; public static RenderTexture RenderTexture; public static Text SwapPageText; public static Text CurrentEmoteText; public static float HoveredAlpha = 1f; public static float UnhoveredAlpha = 0.5f; public static Color DefaultUIColor = new Color(4f / 85f, 4f / 85f, 4f / 85f); public static List<EmoteUIElement> EmoteUIElementsList; public static int HoveredEmoteUIIndex = -1; public static int CurrentPage; public static List<EmoteLoadoutUIElement> EmoteLoadoutUIElementsList = new List<EmoteLoadoutUIElement>(); public static List<List<UnlockableEmote>> EmoteLoadouts = new List<List<UnlockableEmote>>(); public static Color SelectedLoadoutUIColor = new Color(0.36078432f, 0.6f, 0.80784315f); public static int CurrentLoadoutIndex; public static int HoveredLoadoutUIIndex = -1; public static Vector2 CurrentThumbstickPosition = Vector2.zero; private static bool firstTimeOpeningMenu; public static bool InventoryVisible = false; private static Slider emoteVolumeSlider; private static Toggle muteEmoteToggle; private static Toggle emoteOnlyModeToggle; private static Toggle enableDmcaFreeToggle; private static GameObject allowMovingWhileEmotingGameObject; private static Toggle allowMovingWhileEmotingToggle; private static float currentVolumeSetting = 1f; private static bool currentMuteSetting; private static bool currentEmoteOnlyMode; private static bool currentDmcaFreeSetting; private static bool currentAllowMovingWhileEmoting; public static List<UnlockableEmote> AllUnlockedEmotesFiltered = new List<UnlockableEmote>(); public static int hoveredEmoteIndex => (HoveredEmoteUIIndex >= 0) ? (HoveredEmoteUIIndex + 8 * CurrentPage) : (-1); public static int numPages => (currentLoadoutEmotesList != null) ? (Mathf.Max((currentLoadoutEmotesList.Count - 1) / EmoteUIElementsList.Count, 0) + 1) : 0; public static UnlockableEmote previewingEmote => (currentLoadoutEmotesList == null || hoveredEmoteIndex < 0 || hoveredEmoteIndex >= currentLoadoutEmotesList.Count) ? null : (currentLoadoutEmotesList[hoveredEmoteIndex].inEmoteSyncGroup ? currentLoadoutEmotesList[hoveredEmoteIndex].EmoteSyncGroup[0] : currentLoadoutEmotesList[hoveredEmoteIndex]); public static List<UnlockableEmote> currentLoadoutEmotesList => (EmoteLoadouts != null && CurrentLoadoutIndex >= 0 && CurrentLoadoutIndex < EmoteLoadouts.Count) ? EmoteLoadouts[CurrentLoadoutIndex] : null; public static int numLoadouts => EmoteLoadouts.Count; public static bool usingController => false; public static bool isMenuOpen => (Object)(object)MenuGameObject != (Object)null && MenuGameObject.activeSelf; [HarmonyPatch(typeof(Hud), "Awake")] [HarmonyPostfix] public static void InitializeUI(Hud __instance) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown if (Logger.Assert((Object)(object)BetterEmote.RadialMenuPrefab != (Object)null, "Radial menu prefab is null??")) { Logger.Log("Initializing emote menu"); AnimationPreviewer.Enabled = false; firstTimeOpeningMenu = true; HoveredEmoteUIIndex = -1; CurrentPage = 0; CurrentLoadoutIndex = -1; HoveredLoadoutUIIndex = -1; CurrentThumbstickPosition = Vector2.zero; MenuGameObject = Object.Instantiate<GameObject>(BetterEmote.RadialMenuPrefab, ((Component)__instance.m_radialMenu).transform.parent.parent); MenuGameObject.transform.SetAsFirstSibling(); ((Object)MenuGameObject).name = "EmotesRadialMenu"; MenuTransform = MenuGameObject.GetComponent<RectTransform>(); RenderTextureImageUI = MenuGameObject.GetComponentInChildren<RawImage>(); RenderTexture = new RenderTexture(1024, 1024, 24); RenderTexture.format = (RenderTextureFormat)0; RenderTexture.graphicsFormat = (GraphicsFormat)8; RenderTexture.depthStencilFormat = (GraphicsFormat)92; Transform transform = ((Component)((Transform)MenuTransform).Find("MenuUI/RadialMenu/RadialElements")).transform; SwapPageText = ((Component)((Transform)MenuTransform).Find("MenuUI/RadialMenu/RadialBase/SwapPageText")).GetComponent<Text>(); CurrentEmoteText = ((Component)((Transform)MenuTransform).Find("MenuUI/RadialMenu/RadialBase/CurrentEmoteText")).GetComponent<Text>(); CurrentEmoteText.text = ""; EmoteUIElementsList = new List<EmoteUIElement>(); CurrentPage = 0; HoveredEmoteUIIndex = -1; for (int i = 0; i < transform.childCount; i++) { Transform child = transform.GetChild(i); EmoteUIElement emoteUIElement = new EmoteUIElement(); emoteUIElement.UIGameObject = ((Component)child).gameObject; emoteUIElement.UIRectTransform = (RectTransform)(object)((child is RectTransform) ? child : null); emoteUIElement.ID = i; emoteUIElement.BackgroundImage = ((Component)child).GetComponentInChildren<Image>(); emoteUIElement.TextContainer = ((Component)child).GetComponentInChildren<Text>(); EmoteUIElement item = emoteUIElement; EmoteUIElementsList.Add(item); } Transform transform2 = ((Component)((Transform)MenuTransform).Find("MenuUI/EmoteLoadouts")).transform; ((Component)transform2).gameObject.AddComponent<AdditionalPanelUI>(); EmoteLoadoutUIElement.UICount = 0; EmoteLoadoutUIElementsList.Clear(); EmoteLoadoutUIElementsList.Add(((Component)transform2.GetChild(0)).gameObject.AddComponent<EmoteLoadoutUIElement>()); EmoteLoadoutUIElementsList.AddRange(new <>z__ReadOnlyArray<EmoteLoadoutUIElement>(new EmoteLoadoutUIElement[6] { Object.Instantiate<EmoteLoadoutUIElement>(EmoteLoadoutUIElementsList[0], transform2), Object.Instantiate<EmoteLoadoutUIElement>(EmoteLoadoutUIElementsList[0], transform2), Object.Instantiate<EmoteLoadoutUIElement>(EmoteLoadoutUIElementsList[0], transform2), Object.Instantiate<EmoteLoadoutUIElement>(EmoteLoadoutUIElementsList[0], transform2), Object.Instantiate<EmoteLoadoutUIElement>(EmoteLoadoutUIElementsList[0], transform2), Object.Instantiate<EmoteLoadoutUIElement>(EmoteLoadoutUIElementsList[0], transform2) })); for (int j = 0; j < EmoteLoadoutUIElementsList.Count; j++) { ((Object)EmoteLoadoutUIElementsList[j]).name = "EmoteLoadout_" + j; } EmoteLoadoutUIElementsList[0].loadoutName = "Favorites"; EmoteLoadoutUIElementsList[1].loadoutName = $"<color={UnlockableEmote.RarityColorCodes[3]}>Legendary</color>"; EmoteLoadoutUIElementsList[2].loadoutName = $"<color={UnlockableEmote.RarityColorCodes[2]}>Epic</color>"; EmoteLoadoutUIElementsList[3].loadoutName = $"<color={UnlockableEmote.RarityColorCodes[1]}>Rare</color>"; EmoteLoadoutUIElementsList[4].loadoutName = $"<color={UnlockableEmote.RarityColorCodes[0]}>Common</color>"; EmoteLoadoutUIElementsList[5].loadoutName = "Complementary"; EmoteLoadoutUIElementsList[6].loadoutName = "All"; EmoteLoadouts.Clear(); EmoteLoadouts.AddRange(new List<UnlockableEmote>[7] { SaveManager.UnlockedFavoriteEmotes, SaveManager.UnlockedEmotesTier3, SaveManager.UnlockedEmotesTier2, SaveManager.UnlockedEmotesTier1, SaveManager.UnlockedEmotesTier0, EmoteManager.ComplementaryEmotes, AllUnlockedEmotesFiltered }); Logger.Log("emoteLoadouts.Count: " + EmoteLoadouts.Count); if (CurrentLoadoutIndex < 0 || CurrentLoadoutIndex >= EmoteLoadouts.Count) { CurrentLoadoutIndex = EmoteLoadouts.Count - 1; } Transform val = ((Transform)MenuTransform).Find("MenuUI/AdditionalUI"); ((Component)val).gameObject.AddComponent<AdditionalPanelUI>(); AudioManager.LoadPreferences(); Transform obj = val.Find("AudioVolumePanel"); emoteVolumeSlider = ((obj != null) ? ((Component)obj).GetComponentInChildren<Slider>() : null); emoteVolumeSlider.value = Mathf.Clamp(AudioManager.EmoteVolumeMultiplier, 0f, emoteVolumeSlider.maxValue); ((UnityEvent<float>)(object)emoteVolumeSlider.onValueChanged).AddListener((UnityAction<float>)delegate { OnUpdateEmoteVolume(emoteVolumeSlider); }); currentVolumeSetting = emoteVolumeSlider.value; Transform obj2 = val.Find("MasterMutePanel"); muteEmoteToggle = ((obj2 != null) ? ((Component)obj2).GetComponentInChildren<Toggle>() : null); muteEmoteToggle.isOn = AudioManager.MuteEmoteAudio; ((UnityEvent<bool>)(object)muteEmoteToggle.onValueChanged).AddListener((UnityAction<bool>)delegate { OnUpdateToggleMuteEmote(muteEmoteToggle); }); currentMuteSetting = muteEmoteToggle.isOn; Transform obj3 = val.Find("EmoteOnlyModePanel"); emoteOnlyModeToggle = ((obj3 != null) ? ((Component)obj3).GetComponentInChildren<Toggle>() : null); emoteOnlyModeToggle.isOn = AudioManager.EmoteOnlyMode; ((UnityEvent<bool>)(object)emoteOnlyModeToggle.onValueChanged).AddListener((UnityAction<bool>)delegate { OnUpdateToggleEmoteOnlyMode(emoteOnlyModeToggle); }); currentEmoteOnlyMode = emoteOnlyModeToggle.isOn; Transform obj4 = val.Find("DmcaFreePanel"); enableDmcaFreeToggle = ((obj4 != null) ? ((Component)obj4).GetComponentInChildren<Toggle>() : null); enableDmcaFreeToggle.isOn = AudioManager.DmcaFreeMode; ((UnityEvent<bool>)(object)enableDmcaFreeToggle.onValueChanged).AddListener((UnityAction<bool>)delegate { OnUpdateToggleDmcaFreeMode(enableDmcaFreeToggle); }); currentDmcaFreeSetting = enableDmcaFreeToggle.isOn; Transform obj5 = val.Find("MoveWhileEmotingPanel"); allowMovingWhileEmotingGameObject = ((obj5 != null) ? ((Component)obj5).gameObject : null); GameObject obj6 = allowMovingWhileEmotingGameObject; allowMovingWhileEmotingToggle = ((obj6 != null) ? obj6.GetComponentInChildren<Toggle>() : null); allowMovingWhileEmotingToggle.isOn = true; ((UnityEvent<bool>)(object)allowMovingWhileEmotingToggle.onValueChanged).AddListener((UnityAction<bool>)delegate { OnUpdateToggleAllowMovingWhileEmoting(allowMovingWhileEmotingToggle); }); currentAllowMovingWhileEmoting = allowMovingWhileEmotingToggle.isOn; AnimationPreviewer.InitializeAnimationRenderer(); MenuGameObject.SetActive(false); } } [HarmonyPatch(typeof(Hud), "Update")] [HarmonyPostfix] public static void GetInput() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if (!isMenuOpen) { return; } if (AdditionalPanelUI.Hovered || HoveredLoadoutUIIndex != -1) { if (HoveredEmoteUIIndex != -1) { OnHoveredNewElement(-1); } return; } RectTransform uIRectTransform = EmoteUIElementsList[0].UIRectTransform; float num = Vector2.Distance(Vector2.op_Implicit(MenuGameObject.transform.position), Vector2.op_Implicit(((Transform)uIRectTransform).position)) * 0.815f; Vector2 val = (usingController ? CurrentThumbstickPosition : (Vector2.op_Implicit(ZInput.pointerPosition) - Vector2.op_Implicit(MenuGameObject.transform.position))); int num2 = -1; if ((!usingController && ((Vector2)(ref val)).magnitude >= num) || (usingController && CurrentThumbstickPosition != Vector2.zero)) { float num3 = Mathf.Atan2(val.y, 0f - val.x) * 57.29578f - 67.5f; if (num3 < 0f) { num3 += 360f; } num2 = Mathf.FloorToInt(num3 / 45f); } if (num2 != HoveredEmoteUIIndex) { OnHoveredNewElement(num2); } } public static void OnUpdateThumbStickAngle(CallbackContext context) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Helper.localPlayerController == (Object)null) && ((CallbackContext)(ref context)).performed && isMenuOpen) { CurrentThumbstickPosition = ((CallbackContext)(ref context)).ReadValue<Vector2>(); CurrentThumbstickPosition = ((((Vector2)(ref CurrentThumbstickPosition)).magnitude > 0.75f) ? CurrentThumbstickPosition : Vector2.zero); if (CurrentThumbstickPosition == Vector2.zero && previewingEmote != null) { Helper.emoteControllerLocal.TryPerformingEmoteLocal(previewingEmote); CloseEmoteMenu(); } } } public static void OnHoveredNewLoadoutElement(int index) { if (HoveredLoadoutUIIndex == index) { return; } HoveredLoadoutUIIndex = index; foreach (EmoteLoadoutUIElement emoteLoadoutUIElements in EmoteLoadoutUIElementsList) { emoteLoadoutUIElements.OnHover(emoteLoadoutUIElements.id == index); } } public static void OnHoveredNewElement(int index) { if (HoveredEmoteUIIndex != -1 && HoveredEmoteUIIndex != index) { EmoteUIElementsList[HoveredEmoteUIIndex].OnHover(hovered: false); } if (index != -1) { EmoteUIElementsList[index].OnHover(); } HoveredEmoteUIIndex = index; AnimationPreviewer.SetPreviewAnimation(previewingEmote); if (previewingEmote != null) { CurrentEmoteText.text = previewingEmote.displayNameColorCoded + (EmoteManager.AllFavoriteEmotes.Contains(previewingEmote.EmoteName) ? " *" : ""); } else { CurrentEmoteText.text = ""; } } public static void SwapPrevPage() { HoveredEmoteUIIndex = -1; CurrentPage--; CurrentPage = ((CurrentPage < 0) ? (numPages - 1) : CurrentPage); UpdateEmoteWheel(); } public static void SwapNextPage() { HoveredEmoteUIIndex = -1; CurrentPage = (CurrentPage + 1) % numPages; UpdateEmoteWheel(); } public static void UpdateEmoteWheel() { //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) if (currentLoadoutEmotesList == AllUnlockedEmotesFiltered) { SortFilteredEmotes(); } CurrentPage = Mathf.Clamp(CurrentPage, 0, numPages - 1); SwapPageText.text = $"Page [{CurrentPage + 1} / {numPages}]"; for (int i = 0; i < EmoteLoadoutUIElementsList.Count; i++) { EmoteLoadoutUIElement emoteLoadoutUIElement = EmoteLoadoutUIElementsList[i]; emoteLoadoutUIElement.OnHover(HoveredLoadoutUIIndex == emoteLoadoutUIElement.id); } for (int j = 0; j < EmoteLoadouts.Count; j++) { List<UnlockableEmote> list = EmoteLoadouts[j]; EmoteLoadoutUIElement emoteLoadoutUIElement2 = EmoteLoadoutUIElementsList[j]; emoteLoadoutUIElement2.textContainer.text = emoteLoadoutUIElement2.loadoutName + " [" + list.Count + "]"; } for (int k = 0; k < EmoteUIElementsList.Count; k++) { EmoteUIElement emoteUIElement = EmoteUIElementsList[k]; int num = k + 8 * CurrentPage; emoteUIElement.TextContainer.text = ""; emoteUIElement.Emote = null; Color defaultUIColor = DefaultUIColor; Color white = Color.white; if (num < currentLoadoutEmotesList.Count) { UnlockableEmote unlockableEmote = currentLoadoutEmotesList[num]; if (unlockableEmote != null) { emoteUIElement.Emote = unlockableEmote; emoteUIElement.TextContainer.text = unlockableEmote.DisplayName + ((EmoteManager.AllFavoriteEmotes.Count > 0 && EmoteManager.AllFavoriteEmotes.Contains(unlockableEmote.EmoteName)) ? " *" : ""); int num2 = EmoteManager.AllQuickEmotes.IndexOf(unlockableEmote.EmoteName); if (num2 >= 0 && num2 < EmoteManager.AllQuickEmotes.Count) { Text textContainer = emoteUIElement.TextContainer; textContainer.text = textContainer.text + " [" + (num2 + 1) + "]"; } } } emoteUIElement.BaseColor = defaultUIColor; ((Graphic)emoteUIElement.TextContainer).color = white; emoteUIElement.OnHover(hovered: false); } if (HoveredEmoteUIIndex >= 0 && HoveredEmoteUIIndex < 8) { OnHoveredNewElement(HoveredEmoteUIIndex); } } private static void SortFilteredEmotes() { AllUnlockedEmotesFiltered.Clear(); AllUnlockedEmotesFiltered.AddRange(EmoteManager.ComplementaryEmotes); AllUnlockedEmotesFiltered.AddRange(SaveManager.UnlockedEmotesTier0); AllUnlockedEmotesFiltered.AddRange(SaveManager.UnlockedEmotesTier1); AllUnlockedEmotesFiltered.AddRange(SaveManager.UnlockedEmotesTier2); AllUnlockedEmotesFiltered.AddRange(SaveManager.UnlockedEmotesTier3); for (int num = SaveManager.UnlockedFavoriteEmotes.Count - 1; num >= 0; num--) { if (!AllUnlockedEmotesFiltered.Contains(SaveManager.UnlockedFavoriteEmotes[num])) { AllUnlockedEmotesFiltered.Add(SaveManager.UnlockedFavoriteEmotes[num]); } } AllUnlockedEmotesFiltered.Sort((UnlockableEmote item1, UnlockableEmote item2) => string.Compare(item1.DisplayName, item2.DisplayName, StringComparison.OrdinalIgnoreCase)); } public static void SetCurrentEmoteLoadout(int loadoutIndex) { if (CurrentLoadoutIndex != loadoutIndex) { CurrentPage = 0; CurrentLoadoutIndex = loadoutIndex; UpdateEmoteWheel(); } } public static void ToggleFavoriteHoveredEmote() { if (isMenuOpen && previewingEmote != null) { UnlockableEmote unlockableEmote = previewingEmote; if (previewingEmote.EmoteSyncGroup != null && previewingEmote.EmoteSyncGroup.Count > 0) { unlockableEmote = previewingEmote.EmoteSyncGroup[0]; } string emoteName = unlockableEmote.EmoteName; if (EmoteManager.AllFavoriteEmotes.Contains(emoteName)) { EmoteManager.AllFavoriteEmotes.Remove(emoteName); } else { EmoteManager.AllFavoriteEmotes.Add(emoteName); } SaveManager.UpdateUnlockedFavoriteEmotes(); SaveManager.SaveFavoritedEmotes(); UpdateEmoteWheel(); } } public static void OpenEmoteMenu() { //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) Logger.Log("Opening emote menu"); if (!Object.op_Implicit((Object)(object)Helper.localPlayerController)) { return; } if (firstTimeOpeningMenu) { Logger.Assert(EmoteLoadouts != null && EmoteLoadouts.Count > 0, "Error opening emote menu. Emote loadouts are null or empty!"); int currentEmoteLoadout = ((EmoteLoadouts[0].Count <= 0) ? CurrentLoadoutIndex : 0); SetCurrentEmoteLoadout(currentEmoteLoadout); if (CurrentLoadout