Decompiled source of EmotePlaylist v1.0.3

EmotePlaylist.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using TooManyEmotes;
using TooManyEmotes.Patches;
using TooManyEmotes.Props;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EmotePlaylist")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: AssemblyProduct("EmotePlaylist")]
[assembly: AssemblyTitle("EmotePlaylist")]
[assembly: AssemblyVersion("1.0.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 EmotePlaylist
{
	internal static class InputUtil
	{
		private struct EdgeCache
		{
			public int Frame;

			public bool Result;

			public bool WasDown;
		}

		private static readonly Dictionary<KeyCode, Key> KeyMap = new Dictionary<KeyCode, Key>
		{
			{
				(KeyCode)97,
				(Key)15
			},
			{
				(KeyCode)98,
				(Key)16
			},
			{
				(KeyCode)99,
				(Key)17
			},
			{
				(KeyCode)100,
				(Key)18
			},
			{
				(KeyCode)101,
				(Key)19
			},
			{
				(KeyCode)102,
				(Key)20
			},
			{
				(KeyCode)103,
				(Key)21
			},
			{
				(KeyCode)104,
				(Key)22
			},
			{
				(KeyCode)105,
				(Key)23
			},
			{
				(KeyCode)106,
				(Key)24
			},
			{
				(KeyCode)107,
				(Key)25
			},
			{
				(KeyCode)108,
				(Key)26
			},
			{
				(KeyCode)109,
				(Key)27
			},
			{
				(KeyCode)110,
				(Key)28
			},
			{
				(KeyCode)111,
				(Key)29
			},
			{
				(KeyCode)112,
				(Key)30
			},
			{
				(KeyCode)113,
				(Key)31
			},
			{
				(KeyCode)114,
				(Key)32
			},
			{
				(KeyCode)115,
				(Key)33
			},
			{
				(KeyCode)116,
				(Key)34
			},
			{
				(KeyCode)117,
				(Key)35
			},
			{
				(KeyCode)118,
				(Key)36
			},
			{
				(KeyCode)119,
				(Key)37
			},
			{
				(KeyCode)120,
				(Key)38
			},
			{
				(KeyCode)121,
				(Key)39
			},
			{
				(KeyCode)122,
				(Key)40
			},
			{
				(KeyCode)48,
				(Key)50
			},
			{
				(KeyCode)49,
				(Key)41
			},
			{
				(KeyCode)50,
				(Key)42
			},
			{
				(KeyCode)51,
				(Key)43
			},
			{
				(KeyCode)52,
				(Key)44
			},
			{
				(KeyCode)53,
				(Key)45
			},
			{
				(KeyCode)54,
				(Key)46
			},
			{
				(KeyCode)55,
				(Key)47
			},
			{
				(KeyCode)56,
				(Key)48
			},
			{
				(KeyCode)57,
				(Key)49
			},
			{
				(KeyCode)32,
				(Key)1
			},
			{
				(KeyCode)304,
				(Key)51
			},
			{
				(KeyCode)303,
				(Key)52
			},
			{
				(KeyCode)306,
				(Key)55
			},
			{
				(KeyCode)305,
				(Key)56
			},
			{
				(KeyCode)308,
				(Key)53
			},
			{
				(KeyCode)307,
				(Key)54
			},
			{
				(KeyCode)9,
				(Key)3
			},
			{
				(KeyCode)301,
				(Key)72
			},
			{
				(KeyCode)8,
				(Key)65
			},
			{
				(KeyCode)13,
				(Key)2
			},
			{
				(KeyCode)27,
				(Key)60
			},
			{
				(KeyCode)273,
				(Key)63
			},
			{
				(KeyCode)274,
				(Key)64
			},
			{
				(KeyCode)276,
				(Key)61
			},
			{
				(KeyCode)275,
				(Key)62
			},
			{
				(KeyCode)277,
				(Key)70
			},
			{
				(KeyCode)127,
				(Key)71
			},
			{
				(KeyCode)278,
				(Key)68
			},
			{
				(KeyCode)279,
				(Key)69
			},
			{
				(KeyCode)280,
				(Key)67
			},
			{
				(KeyCode)281,
				(Key)66
			},
			{
				(KeyCode)282,
				(Key)94
			},
			{
				(KeyCode)283,
				(Key)95
			},
			{
				(KeyCode)284,
				(Key)96
			},
			{
				(KeyCode)285,
				(Key)97
			},
			{
				(KeyCode)286,
				(Key)98
			},
			{
				(KeyCode)287,
				(Key)99
			},
			{
				(KeyCode)288,
				(Key)100
			},
			{
				(KeyCode)289,
				(Key)101
			},
			{
				(KeyCode)290,
				(Key)102
			},
			{
				(KeyCode)291,
				(Key)103
			},
			{
				(KeyCode)292,
				(Key)104
			},
			{
				(KeyCode)293,
				(Key)105
			}
		};

		private static readonly Dictionary<KeyCode, EdgeCache> Edges = new Dictionary<KeyCode, EdgeCache>();

		public static bool WasPressedThisFrame(KeyCode keyCode)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			int frameCount = Time.frameCount;
			if (Edges.TryGetValue(keyCode, out var value) && value.Frame == frameCount)
			{
				return value.Result;
			}
			bool wasDown = value.WasDown;
			bool flag = false;
			try
			{
				Keyboard current = Keyboard.current;
				if (current != null && KeyMap.TryGetValue(keyCode, out var value2))
				{
					KeyControl val = current[value2];
					if (val != null && ((ButtonControl)val).wasPressedThisFrame)
					{
						flag = true;
					}
				}
			}
			catch
			{
			}
			if (!flag)
			{
				try
				{
					if (Input.GetKeyDown(keyCode))
					{
						flag = true;
					}
				}
				catch
				{
				}
			}
			bool flag2 = IsDown(keyCode);
			if (!flag)
			{
				flag = flag2 && !wasDown;
			}
			Edges[keyCode] = new EdgeCache
			{
				Frame = frameCount,
				Result = flag,
				WasDown = flag2
			};
			return flag;
		}

		public static bool IsDown(KeyCode keyCode)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Keyboard current = Keyboard.current;
				if (current != null && KeyMap.TryGetValue(keyCode, out var value))
				{
					KeyControl val = current[value];
					if (val != null && ((ButtonControl)val).isPressed)
					{
						return true;
					}
				}
			}
			catch
			{
			}
			try
			{
				if (Input.GetKey(keyCode))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		public unsafe static string TipLabel(KeyCode keyCode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Invalid comparison between Unknown and I4
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected I4, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Invalid comparison between Unknown and I4
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Invalid comparison between Unknown and I4
			//IL_0066: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			KeyCode val = keyCode;
			if ((int)val != 13)
			{
				if ((int)val != 27)
				{
					switch (val - 303)
					{
					case 0:
					case 1:
						return "Shift";
					case 2:
					case 3:
						return "Ctrl";
					case 4:
					case 5:
						return "Alt";
					default:
						if ((int)keyCode >= 48 && (int)keyCode <= 57)
						{
							return ((char)(48 + (keyCode - 48))).ToString();
						}
						return ((object)(*(KeyCode*)(&keyCode))/*cast due to .constrained prefix*/).ToString();
					}
				}
				return "Esc";
			}
			return "Enter";
		}
	}
	internal sealed class PlaylistHud : MonoBehaviour
	{
		private static PlaylistHud? _instance;

		private Canvas? _canvas;

		private Image? _backdrop;

		private TextMeshProUGUI? _label;

		private bool _fontAssigned;

		private bool _visible;

		private float _nextRefresh;

		private string _cachedText = "";

		private static readonly Color PanelBg = new Color(0.05f, 0.05f, 0.08f, 0.78f);

		private static readonly Color TextColor = new Color(0.92f, 0.92f, 0.95f, 1f);

		private static readonly Color AccentOrange = new Color(0.855f, 0.4f, 0.185f, 1f);

		internal static PlaylistHud? Instance => _instance;

		internal static bool IsVisible
		{
			get
			{
				if ((Object)(object)_instance != (Object)null)
				{
					return _instance._visible;
				}
				return false;
			}
		}

		internal static void EnsureExists()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("EmotePlaylistHUD");
				Object.DontDestroyOnLoad((Object)val);
				_instance = val.AddComponent<PlaylistHud>();
			}
		}

		internal static void Toggle()
		{
			EnsureExists();
			if (!((Object)(object)_instance == (Object)null))
			{
				_instance.SetVisible(!_instance._visible);
			}
		}

		internal static void Show()
		{
			EnsureExists();
			_instance?.SetVisible(visible: true);
		}

		internal static void Hide()
		{
			_instance?.SetVisible(visible: false);
		}

		private void Awake()
		{
			_instance = this;
			BuildUi();
		}

		private void OnDestroy()
		{
			if ((Object)(object)_instance == (Object)(object)this)
			{
				_instance = null;
			}
		}

		private void BuildUi()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Expected O, but got Unknown
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_canvas != (Object)null))
			{
				_canvas = ((Component)this).gameObject.AddComponent<Canvas>();
				_canvas.renderMode = (RenderMode)0;
				_canvas.sortingOrder = 4900;
				CanvasScaler obj = ((Component)this).gameObject.AddComponent<CanvasScaler>();
				obj.uiScaleMode = (ScaleMode)1;
				obj.referenceResolution = new Vector2(1920f, 1080f);
				((Component)this).gameObject.AddComponent<GraphicRaycaster>();
				GameObject val = new GameObject("Panel", new Type[1] { typeof(RectTransform) });
				val.transform.SetParent(((Component)this).transform, false);
				RectTransform component = val.GetComponent<RectTransform>();
				component.anchorMin = new Vector2(0f, 0.5f);
				component.anchorMax = new Vector2(0f, 0.5f);
				component.pivot = new Vector2(0f, 0.5f);
				component.sizeDelta = new Vector2(360f, 280f);
				component.anchoredPosition = new Vector2(24f, 40f);
				_backdrop = val.AddComponent<Image>();
				((Graphic)_backdrop).color = PanelBg;
				((Graphic)_backdrop).raycastTarget = false;
				GameObject val2 = new GameObject("Label", new Type[1] { typeof(RectTransform) });
				val2.transform.SetParent(val.transform, false);
				RectTransform component2 = val2.GetComponent<RectTransform>();
				component2.anchorMin = Vector2.zero;
				component2.anchorMax = Vector2.one;
				component2.offsetMin = new Vector2(14f, 10f);
				component2.offsetMax = new Vector2(-14f, -10f);
				_label = val2.AddComponent<TextMeshProUGUI>();
				((TMP_Text)_label).alignment = (TextAlignmentOptions)257;
				((TMP_Text)_label).fontStyle = (FontStyles)0;
				((Graphic)_label).color = TextColor;
				((TMP_Text)_label).enableWordWrapping = true;
				((Graphic)_label).raycastTarget = false;
				((TMP_Text)_label).overflowMode = (TextOverflowModes)0;
				((TMP_Text)_label).fontSize = 20f;
				((TMP_Text)_label).lineSpacing = 4f;
				((TMP_Text)_label).text = "";
				TryAssignFont();
				((Behaviour)_canvas).enabled = false;
				if ((Object)(object)_backdrop != (Object)null)
				{
					((Component)_backdrop).gameObject.SetActive(false);
				}
				Plugin.Log.LogInfo((object)"EmotePlaylist HUD built.");
			}
		}

		private void TryAssignFont()
		{
			if ((Object)(object)_label == (Object)null || _fontAssigned)
			{
				return;
			}
			try
			{
				TMP_FontAsset val = null;
				HUDManager instance = HUDManager.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					if (instance.controlTipLines != null)
					{
						TextMeshProUGUI[] controlTipLines = instance.controlTipLines;
						foreach (TextMeshProUGUI val2 in controlTipLines)
						{
							if ((Object)(object)val2 != (Object)null && (Object)(object)((TMP_Text)val2).font != (Object)null)
							{
								val = ((TMP_Text)val2).font;
								break;
							}
						}
					}
					if ((Object)(object)val == (Object)null && (Object)(object)instance.clockNumber != (Object)null && (Object)(object)((TMP_Text)instance.clockNumber).font != (Object)null)
					{
						val = ((TMP_Text)instance.clockNumber).font;
					}
					if ((Object)(object)val == (Object)null && (Object)(object)instance.weightCounter != (Object)null)
					{
						val = ((TMP_Text)instance.weightCounter).font;
					}
				}
				if ((Object)(object)val == (Object)null && (Object)(object)TMP_Settings.defaultFontAsset != (Object)null)
				{
					val = TMP_Settings.defaultFontAsset;
				}
				if ((Object)(object)val != (Object)null)
				{
					((TMP_Text)_label).font = val;
					_fontAssigned = true;
					Plugin.Log.LogInfo((object)("EmotePlaylist HUD font: " + ((Object)val).name));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("EmotePlaylist HUD font assign failed: " + ex.Message));
			}
		}

		private void LateUpdate()
		{
			if ((Object)(object)_canvas == (Object)null || (Object)(object)_label == (Object)null)
			{
				BuildUi();
			}
			if (_visible)
			{
				TryAssignFont();
				if (!(Time.unscaledTime < _nextRefresh))
				{
					_nextRefresh = Time.unscaledTime + 0.5f;
					RefreshText();
				}
			}
		}

		private void RefreshText()
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_label == (Object)null)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder(256);
			stringBuilder.Append("<b><color=#DA662F>Emote Playlist</color></b>\n");
			int index;
			int remainSeconds;
			int total;
			bool flag = PlaylistRunner.TryGetStatus(out index, out remainSeconds, out total);
			string[] overrideNames = GetOverrideNames();
			for (int i = 0; i < 8; i++)
			{
				string slotName = GetSlotName(i, overrideNames);
				string text = $"{i + 1}. {slotName}";
				if (flag && i == index)
				{
					stringBuilder.Append($"<color=#DA662F><b>> {text}</b>  ({remainSeconds}s)</color>\n");
				}
				else
				{
					stringBuilder.Append(text).Append('\n');
				}
			}
			string text2 = InputUtil.TipLabel(Plugin.PlaylistKey.Value);
			string text3 = InputUtil.TipLabel(Plugin.ListKey.Value);
			if (flag)
			{
				stringBuilder.Append($"\n<color=#AAAAAA>{text2} stop · {text3} hide · {index + 1}/{Mathf.Max(total, 1)} ({remainSeconds}s)</color>");
			}
			else
			{
				stringBuilder.Append("\n<color=#AAAAAA>" + text2 + " start/stop · " + text3 + " hide</color>");
			}
			_cachedText = stringBuilder.ToString();
			((TMP_Text)_label).text = _cachedText;
			((Graphic)_label).color = TextColor;
		}

		private static string[]? GetOverrideNames()
		{
			string text = Plugin.PlaylistOverride?.Value;
			if (string.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			string[] array = text.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			string[] array2 = new string[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array2[i] = array[i].Trim();
			}
			return array2;
		}

		private static string GetSlotName(int index, string[]? overrideNames)
		{
			if (overrideNames != null)
			{
				if (index < overrideNames.Length && !string.IsNullOrEmpty(overrideNames[index]))
				{
					return overrideNames[index];
				}
				return "(empty)";
			}
			try
			{
				UnlockableEmote quickEmote = QuickEmotes.GetQuickEmote(index);
				if (quickEmote != null && !string.IsNullOrEmpty(quickEmote.emoteName))
				{
					return quickEmote.emoteName;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)$"HUD quick slot {index}: {ex.Message}");
			}
			return "(empty)";
		}

		private void SetVisible(bool visible)
		{
			_visible = visible;
			if ((Object)(object)_canvas != (Object)null)
			{
				((Behaviour)_canvas).enabled = visible;
			}
			if ((Object)(object)_backdrop != (Object)null && ((Component)_backdrop).gameObject.activeSelf != visible)
			{
				((Component)_backdrop).gameObject.SetActive(visible);
			}
			if (visible)
			{
				_nextRefresh = 0f;
				RefreshText();
				Plugin.Log.LogInfo((object)"EmotePlaylist HUD shown.");
			}
			else
			{
				Plugin.Log.LogInfo((object)"EmotePlaylist HUD hidden.");
			}
		}
	}
	internal static class PlaylistNet
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnMessage;
		}

		public const string MessageName = "MrGlim.EmotePlaylist";

		private static bool _registered;

		public static void EnsureRegistered()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			if (!((Object)(object)singleton == (Object)null) && !_registered)
			{
				CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
				object obj = <>O.<0>__OnMessage;
				if (obj == null)
				{
					HandleNamedMessageDelegate val = OnMessage;
					<>O.<0>__OnMessage = val;
					obj = (object)val;
				}
				customMessagingManager.RegisterNamedMessageHandler("MrGlim.EmotePlaylist", (HandleNamedMessageDelegate)obj);
				_registered = true;
			}
		}

		public unsafe static void BroadcastStart(PlaylistState state)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			EnsureRegistered();
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null)
			{
				return;
			}
			FastBufferWriter val = WriteState(1, state);
			try
			{
				if (singleton.IsServer)
				{
					singleton.CustomMessagingManager.SendNamedMessageToAll("MrGlim.EmotePlaylist", val, (NetworkDelivery)2);
				}
				else
				{
					singleton.CustomMessagingManager.SendNamedMessage("MrGlim.EmotePlaylist", 0uL, val, (NetworkDelivery)2);
				}
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public static void BroadcastStop(ulong ownerPlayerId)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			EnsureRegistered();
			NetworkManager singleton = NetworkManager.Singleton;
			if (!((Object)(object)singleton == (Object)null))
			{
				FastBufferWriter val = default(FastBufferWriter);
				((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1);
				byte b = 0;
				((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref ownerPlayerId, default(ForPrimitives));
				if (singleton.IsServer)
				{
					singleton.CustomMessagingManager.SendNamedMessageToAll("MrGlim.EmotePlaylist", val, (NetworkDelivery)2);
				}
				else
				{
					singleton.CustomMessagingManager.SendNamedMessage("MrGlim.EmotePlaylist", 0uL, val, (NetworkDelivery)2);
				}
				((FastBufferWriter)(ref val)).Dispose();
			}
		}

		private static FastBufferWriter WriteState(byte op, PlaylistState state)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			string text = string.Join("|", state.EmoteNames);
			int byteCount = Encoding.UTF8.GetByteCount(text);
			FastBufferWriter result = default(FastBufferWriter);
			((FastBufferWriter)(ref result))..ctor(64 + byteCount, (Allocator)2, -1);
			((FastBufferWriter)(ref result)).WriteValueSafe<byte>(ref op, default(ForPrimitives));
			((FastBufferWriter)(ref result)).WriteValueSafe<ulong>(ref state.OwnerPlayerId, default(ForPrimitives));
			((FastBufferWriter)(ref result)).WriteValueSafe<double>(ref state.StartServerTime, default(ForPrimitives));
			((FastBufferWriter)(ref result)).WriteValueSafe<int>(ref state.SecondsPerEmote, default(ForPrimitives));
			((FastBufferWriter)(ref result)).WriteValueSafe(text, false);
			return result;
		}

		private unsafe static void OnMessage(ulong sender, FastBufferReader reader)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			byte b = default(byte);
			((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
			ulong ownerPlayerId = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref ownerPlayerId, default(ForPrimitives));
			NetworkManager singleton = NetworkManager.Singleton;
			if (b == 0)
			{
				PlaylistRegistry.Clear(ownerPlayerId);
				if ((Object)(object)singleton != (Object)null && singleton.IsServer)
				{
					FastBufferWriter val = default(FastBufferWriter);
					((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1);
					byte b2 = 0;
					((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b2, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref ownerPlayerId, default(ForPrimitives));
					singleton.CustomMessagingManager.SendNamedMessageToAll("MrGlim.EmotePlaylist", val, (NetworkDelivery)2);
					((FastBufferWriter)(ref val)).Dispose();
				}
				return;
			}
			double startServerTime = default(double);
			((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref startServerTime, default(ForPrimitives));
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			string text = default(string);
			((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
			num = Mathf.Clamp(num, 60, 300);
			string[] emoteNames = (string.IsNullOrEmpty(text) ? Array.Empty<string>() : text.Split('|'));
			PlaylistState state = new PlaylistState
			{
				OwnerPlayerId = ownerPlayerId,
				StartServerTime = startServerTime,
				SecondsPerEmote = num,
				EmoteNames = emoteNames
			};
			PlaylistRegistry.Set(state);
			if (!((Object)(object)singleton != (Object)null) || !singleton.IsServer || sender == 0L)
			{
				return;
			}
			FastBufferWriter val2 = WriteState(1, state);
			try
			{
				singleton.CustomMessagingManager.SendNamedMessageToAll("MrGlim.EmotePlaylist", val2, (NetworkDelivery)2);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose();
			}
		}
	}
	internal sealed class PlaylistRunner : MonoBehaviour
	{
		private static PlaylistRunner? _instance;

		private ulong _followingOwner;

		private int _lastIndex = -1;

		private bool _running;

		private string _tip = "";

		private float _tipUntil;

		private bool _didShowListOnStart;

		private bool _hadLocalPlayer;

		internal static bool IsRunning
		{
			get
			{
				if ((Object)(object)_instance != (Object)null)
				{
					return _instance._running;
				}
				return false;
			}
		}

		internal static bool TryGetStatus(out int index, out int remainSeconds, out int total)
		{
			index = 0;
			remainSeconds = 0;
			total = 0;
			if ((Object)(object)_instance == (Object)null || !_instance._running)
			{
				return false;
			}
			if (!PlaylistRegistry.TryGet(_instance._followingOwner, out PlaylistState state) || state.EmoteNames.Length == 0)
			{
				return false;
			}
			double num = ServerNow();
			index = state.IndexAt(num);
			total = state.EmoteNames.Length;
			remainSeconds = state.SecondsPerEmote - (int)((num - state.StartServerTime) % (double)state.SecondsPerEmote);
			if (remainSeconds <= 0)
			{
				remainSeconds = state.SecondsPerEmote;
			}
			return true;
		}

		private void Awake()
		{
			_instance = this;
		}

		private void OnDestroy()
		{
			if ((Object)(object)_instance == (Object)(object)this)
			{
				_instance = null;
			}
		}

		private void Update()
		{
			//IL_0085: 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_0191: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.Enabled == null || !Plugin.Enabled.Value)
			{
				return;
			}
			PlaylistNet.EnsureRegistered();
			PlaylistHud.EnsureExists();
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if ((Object)(object)val == (Object)null || val.isPlayerDead)
			{
				StopLocal(_running);
				ClearStatusTip();
				_hadLocalPlayer = false;
				return;
			}
			if (!_hadLocalPlayer)
			{
				_hadLocalPlayer = true;
				MaybeShowListOnStart();
			}
			if (!val.isTypingChat && !val.inTerminalMenu)
			{
				if (InputUtil.WasPressedThisFrame(Plugin.PlaylistKey.Value))
				{
					ToggleOwnPlaylist(val);
				}
				if (InputUtil.WasPressedThisFrame(Plugin.ListKey.Value))
				{
					PlaylistHud.Toggle();
				}
			}
			if (!_running)
			{
				if (Time.unscaledTime < _tipUntil && !string.IsNullOrEmpty(_tip))
				{
					ShowControlTip(_tip);
				}
				return;
			}
			if (!PlaylistRegistry.TryGet(_followingOwner, out PlaylistState state) || state.EmoteNames.Length == 0)
			{
				StopLocal(broadcast: false);
				ClearStatusTip();
				return;
			}
			double num = ServerNow();
			int num2 = state.IndexAt(num);
			if (num2 != _lastIndex)
			{
				PlayIndex(state, num2);
				_lastIndex = num2;
			}
			int num3 = state.SecondsPerEmote - (int)((num - state.StartServerTime) % (double)state.SecondsPerEmote);
			if (num3 <= 0)
			{
				num3 = state.SecondsPerEmote;
			}
			_tip = $"Playlist {num2 + 1}/{state.EmoteNames.Length}: {state.EmoteNames[num2]} ({num3}s)  [{InputUtil.TipLabel(Plugin.PlaylistKey.Value)} stop]";
			ShowControlTip(_tip);
		}

		private void MaybeShowListOnStart()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (_didShowListOnStart || Plugin.ShowListOnStart == null || !Plugin.ShowListOnStart.Value)
			{
				return;
			}
			_didShowListOnStart = true;
			PlaylistHud.Show();
			string text = InputUtil.TipLabel(Plugin.ListKey.Value);
			string text2 = InputUtil.TipLabel(Plugin.PlaylistKey.Value);
			try
			{
				HUDManager instance = HUDManager.Instance;
				if (instance != null)
				{
					instance.DisplayTip("Emote Playlist", text + " list · " + text2 + " start after setting TME quick slots", false, false, "LC_Tip1");
				}
			}
			catch
			{
			}
			Plugin.Log.LogInfo((object)"ShowListOnStart: playlist HUD shown + tip.");
		}

		internal void Join(ulong ownerPlayerId)
		{
			if (PlaylistRegistry.TryGet(ownerPlayerId, out PlaylistState state) && state.EmoteNames.Length != 0)
			{
				_followingOwner = ownerPlayerId;
				_running = true;
				_lastIndex = -1;
				double serverTime = ServerNow();
				int num = state.IndexAt(serverTime);
				PlayIndex(state, num);
				_lastIndex = num;
				FlashTip($"Joined playlist ({state.EmoteNames.Length} emotes)", 3f);
				Plugin.Log.LogInfo((object)$"Joined playlist owner={ownerPlayerId} index={num} duration={state.SecondsPerEmote}s");
			}
		}

		private void ToggleOwnPlaylist(PlayerControllerB player)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Log.LogInfo((object)$"Playlist key pressed ({Plugin.PlaylistKey.Value}).");
			if (_running && _followingOwner == player.playerClientId)
			{
				StopLocal(broadcast: true);
				FlashTip("Playlist stopped", 2.5f);
				return;
			}
			List<string> list = BuildPlaylist();
			if (list.Count == 0)
			{
				Plugin.Log.LogWarning((object)"Playlist empty — assign TooManyEmotes quick slots (wheel remotes 1–8) first.");
				FlashTip("Playlist empty — set TME quick slots 1–8 first", 4f);
				try
				{
					string text = InputUtil.TipLabel(Plugin.PlaylistKey.Value);
					HUDManager instance = HUDManager.Instance;
					if (instance != null)
					{
						instance.DisplayTip("Emote Playlist", "Assign TooManyEmotes quick slots (wheel remotes), then press " + text + ".", false, false, "LC_Tip1");
					}
					return;
				}
				catch
				{
					return;
				}
			}
			PlaylistState playlistState = new PlaylistState
			{
				OwnerPlayerId = player.playerClientId,
				StartServerTime = ServerNow(),
				SecondsPerEmote = Plugin.ClampedSeconds(),
				EmoteNames = list.ToArray()
			};
			PlaylistRegistry.Set(playlistState);
			PlaylistNet.BroadcastStart(playlistState);
			_followingOwner = player.playerClientId;
			_running = true;
			_lastIndex = -1;
			FlashTip($"Playlist started ({list.Count} emotes)", 2.5f);
			Plugin.Log.LogInfo((object)$"Started playlist ({list.Count} emotes, {playlistState.SecondsPerEmote}s each).");
		}

		private void StopLocal(bool broadcast)
		{
			if (_running)
			{
				ulong followingOwner = _followingOwner;
				_running = false;
				_lastIndex = -1;
				if (broadcast)
				{
					PlaylistRegistry.Clear(followingOwner);
					PlaylistNet.BroadcastStop(followingOwner);
					Plugin.Log.LogInfo((object)"Stopped playlist.");
				}
			}
		}

		private void FlashTip(string text, float seconds)
		{
			_tip = text;
			_tipUntil = Time.unscaledTime + seconds;
			ShowControlTip(text);
		}

		private void ClearStatusTip()
		{
			_tip = "";
			_tipUntil = 0f;
		}

		private static void ShowControlTip(string text)
		{
			try
			{
				HUDManager instance = HUDManager.Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					instance.ChangeControlTip(3, text, false);
				}
			}
			catch
			{
			}
		}

		private static List<string> BuildPlaylist()
		{
			List<string> list = new List<string>();
			string value = Plugin.PlaylistOverride.Value;
			if (!string.IsNullOrWhiteSpace(value))
			{
				string[] array = value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length > 0)
					{
						list.Add(text);
					}
				}
				return list;
			}
			for (int j = 0; j < 8; j++)
			{
				try
				{
					UnlockableEmote quickEmote = QuickEmotes.GetQuickEmote(j);
					if (quickEmote != null && !string.IsNullOrEmpty(quickEmote.emoteName))
					{
						list.Add(quickEmote.emoteName);
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)$"Quick slot {j} failed: {ex.Message}");
				}
			}
			return list;
		}

		private static void PlayIndex(PlaylistState state, int index)
		{
			EmoteControllerPlayer emoteControllerLocal = EmoteControllerPlayer.emoteControllerLocal;
			if ((Object)(object)emoteControllerLocal == (Object)null || index < 0 || index >= state.EmoteNames.Length)
			{
				return;
			}
			string text = state.EmoteNames[index];
			if (!EmotesManager.allUnlockableEmotesDict.TryGetValue(text, out var value) || value == null)
			{
				Plugin.Log.LogWarning((object)("Playlist emote not found: " + text));
				return;
			}
			try
			{
				emoteControllerLocal.TryPerformingEmoteLocal(value, -1, (GrabbablePropObject)null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to play " + text + ": " + ex.Message));
			}
		}

		private static double ServerNow()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null)
			{
				return Time.realtimeSinceStartupAsDouble;
			}
			NetworkTime serverTime = singleton.ServerTime;
			return ((NetworkTime)(ref serverTime)).Time;
		}
	}
	[HarmonyPatch(typeof(SyncWithEmoteControllerManager), "SyncWithEmoteController_performed")]
	internal static class SyncPlaylistPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix(PlayerControllerB __instance)
		{
			if (Plugin.Enabled == null || !Plugin.Enabled.Value)
			{
				return true;
			}
			EmoteController lookingAtSyncableEmoteController = SyncWithEmoteControllerManager.lookingAtSyncableEmoteController;
			EmoteControllerPlayer val = (EmoteControllerPlayer)(object)((lookingAtSyncableEmoteController is EmoteControllerPlayer) ? lookingAtSyncableEmoteController : null);
			if (val == null || (Object)(object)val.playerController == (Object)null)
			{
				return true;
			}
			ulong playerClientId = val.playerController.playerClientId;
			if (!PlaylistRegistry.TryGet(playerClientId, out PlaylistState _))
			{
				return true;
			}
			(((Object)(object)Plugin.Instance != (Object)null) ? ((Component)Plugin.Instance).GetComponent<PlaylistRunner>() : null)?.Join(playerClientId);
			SyncWithEmoteControllerManager.ResetState();
			return false;
		}
	}
	internal sealed class PlaylistState
	{
		public ulong OwnerPlayerId;

		public double StartServerTime;

		public int SecondsPerEmote;

		public string[] EmoteNames = Array.Empty<string>();

		public int IndexAt(double serverTime)
		{
			if (EmoteNames.Length == 0 || SecondsPerEmote <= 0)
			{
				return 0;
			}
			double num = serverTime - StartServerTime;
			if (num < 0.0)
			{
				num = 0.0;
			}
			int num2 = (int)(num / (double)SecondsPerEmote) % EmoteNames.Length;
			if (num2 >= 0)
			{
				return num2;
			}
			return num2 + EmoteNames.Length;
		}
	}
	internal static class PlaylistRegistry
	{
		private static readonly Dictionary<ulong, PlaylistState> ByOwner = new Dictionary<ulong, PlaylistState>();

		public static void Set(PlaylistState state)
		{
			ByOwner[state.OwnerPlayerId] = state;
		}

		public static void Clear(ulong ownerPlayerId)
		{
			ByOwner.Remove(ownerPlayerId);
		}

		public static bool TryGet(ulong ownerPlayerId, out PlaylistState state)
		{
			return ByOwner.TryGetValue(ownerPlayerId, out state);
		}

		public static void ClearAll()
		{
			ByOwner.Clear();
		}
	}
	[BepInPlugin("com.benhough.lethal.EmotePlaylist", "EmotePlaylist", "1.0.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.benhough.lethal.EmotePlaylist";

		public const string ModName = "EmotePlaylist";

		public const string ModVersion = "1.0.3";

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<KeyCode> PlaylistKey { get; private set; }

		internal static ConfigEntry<KeyCode> ListKey { get; private set; }

		internal static ConfigEntry<bool> ShowListOnStart { get; private set; }

		internal static ConfigEntry<int> SecondsPerEmote { get; private set; }

		internal static ConfigEntry<string> PlaylistOverride { get; private set; }

		private void Awake()
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable emote playlists.");
			PlaylistKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "PlaylistKey", (KeyCode)290, "Start/stop a looping playlist of your 8 TooManyEmotes quick slots (the wheel remotes).");
			ListKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ListKey", (KeyCode)291, "Toggle the on-screen emote playlist panel.");
			ShowListOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowListOnStart", true, "Auto-show the playlist panel (and a tip) when your local player first becomes available this session.");
			SecondsPerEmote = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SecondsPerEmote", 120, "How long each playlist emote plays (clamped 60–300).");
			PlaylistOverride = ((BaseUnityPlugin)this).Config.Bind<string>("General", "PlaylistOverride", "", "Optional comma-separated emote names. Empty = use current quick slots 1–8.");
			try
			{
				new Harmony("com.benhough.lethal.EmotePlaylist").PatchAll(typeof(Plugin).Assembly);
				((Component)this).gameObject.AddComponent<PlaylistRunner>();
				PlaylistHud.EnsureExists();
				Log.LogInfo((object)"EmotePlaylist v1.0.3 loaded.");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to start: {arg}");
			}
		}

		internal static int ClampedSeconds()
		{
			return Mathf.Clamp(SecondsPerEmote?.Value ?? 120, 60, 300);
		}
	}
	internal static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.benhough.lethal.EmotePlaylist";

		public const string PLUGIN_NAME = "EmotePlaylist";

		public const string PLUGIN_VERSION = "1.0.3";
	}
}