Decompiled source of MuchBetterEmotes v1.0.0

BepInEx/plugins/MuchBetterEmotes/MuchBetterEmotes.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalCompanyInputUtils.Api;
using LethalConfig;
using LethalConfig.ConfigItems;
using LethalNetworkAPI;
using LethalNetworkAPI.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Animations.Rigging;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Experimental.Animations;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.Playables;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("ClientNetworkTransform")]
[assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")]
[assembly: IgnoresAccessChecksTo("DissonanceVoip")]
[assembly: IgnoresAccessChecksTo("DunGen")]
[assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")]
[assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")]
[assembly: IgnoresAccessChecksTo("EasyTextEffects")]
[assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")]
[assembly: IgnoresAccessChecksTo("Unity.Burst")]
[assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")]
[assembly: IgnoresAccessChecksTo("Unity.Collections")]
[assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.Jobs")]
[assembly: IgnoresAccessChecksTo("Unity.Mathematics")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")]
[assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")]
[assembly: IgnoresAccessChecksTo("Unity.Services.QoS")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Relay")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")]
[assembly: IgnoresAccessChecksTo("Unity.XR.Management")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MuchBetterEmotes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("More emotes and a sign you can draw on, rebuilt for v81.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MuchBetterEmotes")]
[assembly: AssemblyTitle("MuchBetterEmotes")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 MuchBetterEmotes
{
	internal static class AssetsLoader
	{
		public static bool CanDraw
		{
			get
			{
				if (BasePixels != null)
				{
					return (Object)(object)PanelPrefab != (Object)null;
				}
				return false;
			}
		}

		public static AnimationClip[] Clips { get; private set; } = Array.Empty<AnimationClip>();

		public static AudioClip[] Claps { get; private set; } = Array.Empty<AudioClip>();

		public static GameObject? SignPrefab { get; private set; }

		public static GameObject? LegsPrefab { get; private set; }

		public static uint[]? BasePixels { get; private set; }

		public static GlyphFont? Glyphs { get; private set; }

		public static bool CanType => Glyphs != null;

		public static GameObject? WheelPrefab { get; private set; }

		public static GameObject? PanelPrefab { get; private set; }

		public static bool TryLoad()
		{
			string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;
			string text = Path.Combine(path, "muchbetteremotes");
			if (!File.Exists(text))
			{
				Plugin.Log.LogError((object)("Could not find the asset bundle at '" + text + "'."));
				return false;
			}
			AssetBundle val = AssetBundle.LoadFromFile(text);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)("Could not load the asset bundle at '" + text + "'."));
				return false;
			}
			bool flag = false;
			AnimationClip[] array = (AnimationClip[])(object)new AnimationClip[15];
			AnimationClip val2 = Load<AnimationClip>(val, "EmoteRest");
			if ((Object)(object)val2 != (Object)null)
			{
				array[0] = val2;
			}
			else
			{
				flag = true;
			}
			for (int i = 0; i < EmoteDefs.All.Count; i++)
			{
				EmoteDef emoteDef = EmoteDefs.All[i];
				AnimationClip val3 = Load<AnimationClip>(val, emoteDef.Clip);
				if ((Object)(object)val3 != (Object)null)
				{
					array[emoteDef.Input] = val3;
				}
				else
				{
					flag = true;
				}
			}
			List<AudioClip> list = new List<AudioClip>();
			for (int j = 0; j < Constants.Assets.Claps.Length; j++)
			{
				AudioClip val4 = Load<AudioClip>(val, Constants.Assets.Claps[j]);
				if ((Object)(object)val4 != (Object)null)
				{
					list.Add(val4);
				}
			}
			Claps = list.ToArray();
			SignPrefab = Load<GameObject>(val, "Sign");
			LegsPrefab = Load<GameObject>(val, "plegs");
			WheelPrefab = Load<GameObject>(val, "MoreEmotesMenu");
			PanelPrefab = Load<GameObject>(val, "SignPanel");
			Texture2D val5 = Load<Texture2D>(val, "SignBase");
			if ((Object)(object)val5 != (Object)null)
			{
				BasePixels = ReadPixels(val5);
			}
			Texture2D val6 = Load<Texture2D>(val, "SignGlyphs");
			if ((Object)(object)val6 != (Object)null)
			{
				Glyphs = ReadGlyphs(val6);
			}
			if (flag)
			{
				return false;
			}
			Clips = array;
			return true;
		}

		private static T? Load<T>(AssetBundle bundle, string name) where T : Object
		{
			T val = bundle.LoadAsset<T>(name);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)("Could not find " + typeof(T).Name + " '" + name + "' in the asset bundle."));
			}
			return val;
		}

		private static GlyphFont? ReadGlyphs(Texture2D texture)
		{
			int width = ((Texture)texture).width;
			int height = ((Texture)texture).height;
			try
			{
				Color32[] pixels = texture.GetPixels32();
				byte[] array = new byte[pixels.Length];
				for (int i = 0; i < height; i++)
				{
					for (int j = 0; j < width; j++)
					{
						array[i * width + j] = pixels[(height - 1 - i) * width + j].a;
					}
				}
				if (GlyphFont.TryCreate(array, width, height, out GlyphFont font))
				{
					return font;
				}
				Plugin.Log.LogError((object)(string.Format("Could not use Texture2D '{0}' because it is {1} x {2} ", "SignGlyphs", width, height) + $"and not {128} x {96}."));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not read Texture2D 'SignGlyphs': " + ex.Message));
			}
			return null;
		}

		private static uint[]? ReadPixels(Texture2D texture)
		{
			//IL_0070: 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_0025: Invalid comparison between Unknown and I4
			//IL_0029: 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)
			int num = 512;
			int num2 = 340;
			try
			{
				if (((Texture)texture).width == num && ((Texture)texture).height == num2 && (int)texture.format == 4)
				{
					return texture.GetPixelData<uint>(0).ToArray();
				}
				Plugin.Log.LogError((object)("Could not use Texture2D 'SignBase' because it is " + $"{((Texture)texture).width} x {((Texture)texture).height} {texture.format} and not {num} x {num2} RGBA32."));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not read Texture2D 'SignBase': " + ex.Message));
			}
			return null;
		}
	}
	internal static class ArmsTarget
	{
		private static Transform? _original;

		private static Transform? _free;

		public static void Free(PlayerControllerB player)
		{
			if (!EmoteControl.IsLocal(player))
			{
				return;
			}
			if ((Object)(object)_original == (Object)null)
			{
				_original = player.localArmsRotationTarget;
			}
			if ((Object)(object)_free == (Object)null)
			{
				Transform val = (((Object)(object)_original.parent != (Object)null) ? _original.parent.parent : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				_free = Object.Instantiate<Transform>(_original, val);
			}
			player.localArmsRotationTarget = _free;
		}

		public static void Restore(PlayerControllerB player)
		{
			if (EmoteControl.IsLocal(player) && (Object)(object)_original != (Object)null)
			{
				player.localArmsRotationTarget = _original;
			}
		}
	}
	internal static class LegsView
	{
		public static void Show(PlayerControllerB player)
		{
			if (EmoteControl.IsLocal(player))
			{
				Renderer legs = GetLegs(player);
				if (!((Object)(object)legs == (Object)null))
				{
					legs.sharedMaterial = ((Renderer)player.thisPlayerModel).sharedMaterial;
					legs.forceRenderingOff = false;
				}
			}
		}

		public static void Hide(PlayerControllerB player)
		{
			Renderer legs = GetLegs(player);
			if ((Object)(object)legs != (Object)null)
			{
				legs.forceRenderingOff = true;
			}
		}

		private static Renderer? GetLegs(PlayerControllerB player)
		{
			EmoteRig emoteRig = EmoteRig.Of(player);
			if (!((Object)(object)emoteRig != (Object)null))
			{
				return null;
			}
			return emoteRig.Legs;
		}
	}
	internal static class Props
	{
		public const string Back = "Back";

		private const int SignIndex = 6;

		private const float LegsHeight = 0.197f;

		private const float LegsScale = 13.99568f;

		public static GameObject? AddSign(PlayerControllerB player, out MeshRenderer? board, out MeshRenderer? back)
		{
			//IL_0055: 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)
			board = null;
			back = null;
			GameObject signPrefab = AssetsLoader.SignPrefab;
			if ((Object)(object)signPrefab == (Object)null)
			{
				return null;
			}
			GameObject val = Object.Instantiate<GameObject>(signPrefab, ((Component)player.playerBodyAnimator).transform);
			((Object)val).name = "Sign";
			val.transform.SetSiblingIndex(6);
			val.transform.SetLocalPositionAndRotation(new Vector3(0.029f, -0.45f, 1.3217f), Quaternion.Euler(65.556f, 180f, 180f));
			board = val.GetComponent<MeshRenderer>();
			((Renderer)board).enabled = false;
			back = AddBack(val, board);
			return val;
		}

		public static Renderer? AddLegs(PlayerControllerB player)
		{
			//IL_0092: 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)
			GameObject legsPrefab = AssetsLoader.LegsPrefab;
			if ((Object)(object)legsPrefab == (Object)null)
			{
				return null;
			}
			Transform transform = ((Component)player.playerBodyAnimator).transform;
			Transform parent = transform.parent;
			GameObject val = Object.Instantiate<GameObject>(legsPrefab, parent);
			Transform val2 = val.transform.Find("Mesh");
			Transform val3 = val.transform.Find("Armature");
			SkinnedMeshRenderer component = ((Component)val2).GetComponent<SkinnedMeshRenderer>();
			((Object)val2).name = "LEGS";
			val2.SetParent(parent);
			((Object)val3).name = "FistPersonLegs";
			val3.SetParent(transform);
			val3.localPosition = new Vector3(0f, 0.197f, 0f);
			val3.localScale = new Vector3(13.99568f, 13.99568f, 13.99568f);
			((Renderer)component).forceRenderingOff = true;
			Object.Destroy((Object)(object)val);
			return (Renderer?)(object)component;
		}

		private static MeshRenderer AddBack(GameObject sign, MeshRenderer board)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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)
			GameObject val = new GameObject("Back");
			val.layer = sign.layer;
			val.transform.SetParent(sign.transform, false);
			val.transform.SetLocalPositionAndRotation(new Vector3(0f, 0f, 0.005f), Quaternion.Euler(0f, 180f, 0f));
			val.transform.localScale = Vector3.one;
			val.AddComponent<MeshFilter>().sharedMesh = sign.GetComponent<MeshFilter>().sharedMesh;
			MeshRenderer val2 = val.AddComponent<MeshRenderer>();
			((Renderer)val2).sharedMaterial = ((Renderer)board).sharedMaterial;
			((Renderer)val2).renderingLayerMask = ((Renderer)board).renderingLayerMask;
			((Renderer)val2).enabled = false;
			return val2;
		}
	}
	internal static class LCVRCompat
	{
		private const string PluginType = "LCVR.Plugin";

		private const string FlagsType = "LCVR.Flags";

		private const string FlagsMember = "Flags";

		private const string VRMember = "VR";

		private static bool _ready;

		private static bool _inVR;

		public static bool InVR
		{
			get
			{
				if (!_ready)
				{
					Init();
				}
				return _inVR;
			}
		}

		private static void Init()
		{
			_ready = true;
			if (!Chainloader.PluginInfos.TryGetValue("io.daxcess.lcvr", out var value))
			{
				return;
			}
			try
			{
				object instance = value.Instance;
				if (instance != null && TryRead(instance.GetType().Assembly, out var vr))
				{
					_inVR = vr;
					return;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to read LCVR VR flag: " + (ex.InnerException ?? ex).Message));
				return;
			}
			Plugin.Log.LogWarning((object)"Could not read LCVR VR flag.");
		}

		private static bool TryRead(Assembly assembly, out bool vr)
		{
			vr = false;
			Type type = assembly.GetType("LCVR.Plugin", throwOnError: false);
			Type type2 = assembly.GetType("LCVR.Flags", throwOnError: false);
			PropertyInfo propertyInfo = type?.GetProperty("Flags", BindingFlags.Static | BindingFlags.Public);
			if (propertyInfo == null || type2 == null || !type2.IsEnum || propertyInfo.PropertyType != type2 || !Enum.IsDefined(type2, "VR"))
			{
				return false;
			}
			object value = propertyInfo.GetValue(null);
			if (value == null)
			{
				return false;
			}
			vr = ((Enum)value).HasFlag((Enum)Enum.Parse(type2, "VR"));
			return true;
		}
	}
	internal static class LethalConfigCompat
	{
		private static bool? _present;

		private static bool Present
		{
			get
			{
				bool valueOrDefault = _present == true;
				if (!_present.HasValue)
				{
					valueOrDefault = Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig");
					_present = valueOrDefault;
					return valueOrDefault;
				}
				return valueOrDefault;
			}
		}

		public static void Register(ConfigFile config)
		{
			if (!Present)
			{
				return;
			}
			try
			{
				RegisterEntries(config);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to register settings with LethalConfig: " + ex.Message));
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		private static void RegisterEntries(ConfigFile config)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			AssemblyDescriptionAttribute customAttribute = typeof(Plugin).Assembly.GetCustomAttribute<AssemblyDescriptionAttribute>();
			if (customAttribute != null)
			{
				LethalConfigManager.SetModDescription(customAttribute.Description);
			}
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				ConfigEntryBase value = item.Value;
				if (!(value is ConfigEntry<bool> val))
				{
					if (value is ConfigEntry<float> val2)
					{
						LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(val2, false));
					}
				}
				else
				{
					LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(val, false));
				}
			}
		}
	}
	internal static class TooManyEmotesCompat
	{
		private const string TypeName = "TooManyEmotes.Compatibility.TooManyEmotesCompat";

		private const string MethodName = "IsPlayerEmoting";

		private static bool _ready;

		private static Func<PlayerControllerB, bool>? _isEmoting;

		public static bool IsEmoting(PlayerControllerB player)
		{
			if (!_ready)
			{
				Init();
			}
			Func<PlayerControllerB, bool> isEmoting = _isEmoting;
			if (isEmoting == null)
			{
				return false;
			}
			try
			{
				return isEmoting(player);
			}
			catch (Exception ex)
			{
				_isEmoting = null;
				Plugin.Log.LogWarning((object)("Failed to call TooManyEmotes' IsPlayerEmoting: " + (ex.InnerException ?? ex).Message));
				return false;
			}
		}

		private static void Init()
		{
			_ready = true;
			if (!Chainloader.PluginInfos.TryGetValue("FlipMods.TooManyEmotes", out var value))
			{
				return;
			}
			try
			{
				MethodInfo methodInfo = (((object)value.Instance)?.GetType().Assembly.GetType("TooManyEmotes.Compatibility.TooManyEmotesCompat", throwOnError: false))?.GetMethod("IsPlayerEmoting", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(PlayerControllerB) }, null);
				if (methodInfo != null && methodInfo.ReturnType == typeof(bool))
				{
					_isEmoting = (Func<PlayerControllerB, bool>)Delegate.CreateDelegate(typeof(Func<PlayerControllerB, bool>), methodInfo);
					return;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to look up TooManyEmotes' IsPlayerEmoting: " + (ex.InnerException ?? ex).Message));
				return;
			}
			Plugin.Log.LogWarning((object)"Could not find TooManyEmotes IsPlayerEmoting.");
		}
	}
	internal static class ConfigManager
	{
		private static class Sections
		{
			public const string Emotes = "1. Emotes";

			public const string Movement = "2. Movement";

			public const string Wheel = "3. Wheel";

			public const string Sign = "4. Sign";
		}

		private const float MinSpeed = 0.05f;

		public static ConfigEntry<bool> MiddleFinger { get; private set; }

		public static ConfigEntry<bool> Clap { get; private set; }

		public static ConfigEntry<bool> Shy { get; private set; }

		public static ConfigEntry<bool> Griddy { get; private set; }

		public static ConfigEntry<bool> Twerk { get; private set; }

		public static ConfigEntry<bool> Salute { get; private set; }

		public static ConfigEntry<bool> Prisyadka { get; private set; }

		public static ConfigEntry<bool> Sign { get; private set; }

		public static ConfigEntry<bool> ThumbsUp { get; private set; }

		public static ConfigEntry<bool> Wave { get; private set; }

		public static ConfigEntry<bool> SixSeven { get; private set; }

		public static ConfigEntry<bool> MoveWhileEmoting { get; private set; }

		public static ConfigEntry<bool> HideHeldItem { get; private set; }

		public static ConfigEntry<bool> ShowOthers { get; private set; }

		public static ConfigEntry<bool> ShowWhileDrawing { get; private set; }

		public static ConfigEntry<bool> RememberDrawing { get; private set; }

		public static ConfigEntry<float> GriddySpeed { get; private set; }

		public static ConfigEntry<float> PrisyadkaSpeed { get; private set; }

		public static ConfigEntry<bool> StopOutside { get; private set; }

		public static ConfigEntry<float> Deadzone { get; private set; }

		public static void Initialize(ConfigFile config)
		{
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Expected O, but got Unknown
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Expected O, but got Unknown
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Expected O, but got Unknown
			config.SaveOnConfigSet = false;
			MiddleFinger = config.Bind<bool>("1. Emotes", "Middle Finger", true, "Emote toggle.");
			Clap = config.Bind<bool>("1. Emotes", "Clap", true, "Emote toggle.");
			Shy = config.Bind<bool>("1. Emotes", "Shy", true, "Emote toggle.");
			Griddy = config.Bind<bool>("1. Emotes", "The Griddy", true, "Emote toggle.");
			Twerk = config.Bind<bool>("1. Emotes", "Twerk", true, "Emote toggle.");
			Salute = config.Bind<bool>("1. Emotes", "Salute", true, "Emote toggle.");
			Prisyadka = config.Bind<bool>("1. Emotes", "Prisyadka", true, "Emote toggle.");
			Sign = config.Bind<bool>("1. Emotes", "Sign", true, "Emote toggle.");
			ThumbsUp = config.Bind<bool>("1. Emotes", "Thumbs Up", true, "Emote toggle.");
			Wave = config.Bind<bool>("1. Emotes", "Wave", true, "Emote toggle.");
			SixSeven = config.Bind<bool>("1. Emotes", "6 7", true, "Emote toggle.");
			MoveWhileEmoting = config.Bind<bool>("1. Emotes", "Move While Emoting", false, "Lets you walk during every emote.");
			HideHeldItem = config.Bind<bool>("1. Emotes", "Hide Held Item", true, "Hides item in players hand while emoting.");
			GriddySpeed = config.Bind<float>("2. Movement", "Griddy Speed", 0.5f, new ConfigDescription("Walk speed during the Griddy. REQUIRES 'The Griddy' SET TO TRUE.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			PrisyadkaSpeed = config.Bind<float>("2. Movement", "Prisyadka Speed", 0.34f, new ConfigDescription("Walk speed during Prisyadka. REQUIRES 'Prisyadka' SET TO TRUE.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			StopOutside = config.Bind<bool>("3. Wheel", "Stop Outside Slots", true, "Releasing the wheel outside the slots stops your emote.");
			Deadzone = config.Bind<float>("3. Wheel", "Controller Deadzone", 0.25f, new ConfigDescription("Fraction of full tilt the right stick needs to pick slot. Letting go inside it cancels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.9f), Array.Empty<object>()));
			ShowOthers = config.Bind<bool>("4. Sign", "Show Other Players Drawings", true, "If false, keeps other players signs empty for you. If true, signs shows their drawings when they show the sign.");
			ShowWhileDrawing = config.Bind<bool>("4. Sign", "Show While Drawing", false, "If true, other players see each line as you draw. If false, they see your drawing only when you show the sign.");
			RememberDrawing = config.Bind<bool>("4. Sign", "Remember Drawing", true, "Saves your drawing when panel closes.");
			ClearOrphanedEntries(config);
			config.Save();
			config.SaveOnConfigSet = true;
			SignCanvases.ShowOthers = ShowOthers.Value;
			ShowOthers.SettingChanged += OnShowChanged;
			LethalConfigCompat.Register(config);
		}

		public static bool Enabled(int emote)
		{
			switch (emote)
			{
			case 3:
			case 1003:
				return MiddleFinger.Value;
			case 4:
			case 1004:
				return Clap.Value;
			case 5:
				return Shy.Value;
			case 6:
				return Griddy.Value;
			case 7:
				return Twerk.Value;
			case 8:
				return Salute.Value;
			case 9:
				return Prisyadka.Value;
			case 10:
			case 1010:
				return Sign.Value;
			case 11:
				return ThumbsUp.Value;
			case 12:
				return Wave.Value;
			case 13:
				return SixSeven.Value;
			default:
				return true;
			}
		}

		private static void OnShowChanged(object sender, EventArgs args)
		{
			SignCanvases.ShowOthers = ShowOthers.Value;
		}

		private static void ClearOrphanedEntries(ConfigFile config)
		{
			try
			{
				PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries");
				if (propertyInfo != null && propertyInfo.GetValue(config) is Dictionary<ConfigDefinition, string> dictionary)
				{
					dictionary.Clear();
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not clear orphaned config entries: " + ex.Message));
			}
		}
	}
	internal static class Constants
	{
		internal static class Rig
		{
			public const ushort SortingOrder = 600;

			public const float Floor = 0.001f;

			public const float FadeShare = 0.3f;

			public const float StopFade = 0.1f;

			public const float DecayRate = 10f;
		}

		internal static class Emotes
		{
			public const float Cooldown = 0.5f;

			public const float GriddySpeed = 0.5f;

			public const float PrisyadkaSpeed = 0.34f;

			public const float StrafeLimit = 0.3f;

			public const int BucketSize = 16;

			public const float BucketRate = 4f;
		}

		internal static class Assets
		{
			public const string Bundle = "muchbetteremotes";

			public const string Sign = "Sign";

			public const string Legs = "plegs";

			public const string SignBase = "SignBase";

			public const string Glyphs = "SignGlyphs";

			public const string Wheel = "MoreEmotesMenu";

			public const string Panel = "SignPanel";

			public static readonly string[] Claps = new string[2] { "SingleClapEmote1", "SingleClapEmote2" };
		}

		internal static class Wheel
		{
			public const float Inner = 74.39f;

			public const float Outer = 167.32f;

			public const int Blocks = 8;
		}

		internal static class Draw
		{
			public const int Width = 512;

			public const int Height = 340;

			public const int MaxPoints = 8192;

			public const float SampleInterval = 1f / 30f;

			public const int MinSize = 2;

			public const int MaxSize = 32;

			public const int MaxText = 32;

			public const int MaxScale = 4;

			public const int Slots = 8;
		}

		internal static class Sync
		{
			public const int ChunkSize = 255;

			public const float FlushInterval = 0.15f;

			public const int BucketSize = 128;

			public const float BucketRate = 20f;

			public const int EditBucketSize = 12;

			public const float EditBucketRate = 12f;
		}

		internal static class Replay
		{
			public const int Budget = 280000;

			public const int LocalBudget = 1120000;

			public const float StepTime = 0.0012f;

			public const float LocalTime = 0.0048f;

			public const int ClockEvery = 32;
		}

		internal static class Brush
		{
			public const int Size = 8;

			public const int EraseSize = 16;

			public const int FillSize = 1;

			public const int TextSize = 128;

			public const int WallLuma = 128;

			public const float MinMove = 3f;

			public const float ReleaseMove = 1f;
		}

		internal static class Glyph
		{
			public const int Width = 8;

			public const int Height = 16;

			public const int Columns = 16;

			public const int Rows = 6;

			public const int First = 32;

			public const int Last = 126;
		}

		internal static class Panel
		{
			public const float Confirm = 2f;

			public const int ThumbWidth = 128;

			public const int ThumbHeight = 85;

			public static readonly int[] Presets = new int[3] { 4, 8, 16 };

			public static readonly int[] TextPresets = new int[3] { 1, 2, 4 };

			public static readonly uint[] Palette = new uint[8] { 4278190080u, 4294967295u, 4280821980u, 4280192240u, 4280866805u, 4282821180u, 4292633640u, 4290657420u };
		}

		internal static class Pad
		{
			public const float Speed = 1400f;

			public const float SlowSpeed = 140f;

			public const float Zone = 0.4f;
		}
	}
	internal enum FadeMode
	{
		Off,
		Active,
		Falling,
		Decay
	}
	internal sealed class Blend
	{
		private readonly float[] _start;

		private readonly float[] _weights;

		private float _time;

		private float _duration;

		public int Target { get; private set; }

		public bool Fading { get; private set; }

		public IReadOnlyList<float> Weights => _weights;

		public Blend(int count)
		{
			_start = new float[count];
			_weights = new float[count];
			_weights[0] = 1f;
		}

		public float Share(int[] inputs)
		{
			float num = 0f;
			for (int i = 0; i < inputs.Length; i++)
			{
				num += _weights[inputs[i]];
			}
			return num;
		}

		public void FadeTo(int input, float duration)
		{
			Array.Copy(_weights, _start, _weights.Length);
			Target = input;
			_time = 0f;
			_duration = duration;
			Fading = true;
		}

		public void Snap(int input)
		{
			Array.Clear(_weights, 0, _weights.Length);
			_weights[0] = 0.001f;
			_weights[input] = 0.999f;
			Target = input;
			Fading = false;
		}

		public void Update(float dt)
		{
			if (Fading)
			{
				_time += dt;
				float num = Math.Min(1f, _time / _duration);
				for (int i = 0; i < _weights.Length; i++)
				{
					_weights[i] = _start[i] * (1f - num) + ((i == Target) ? num : 0f);
				}
				if (Target != 0)
				{
					KeepFloor();
				}
				if (num >= 1f)
				{
					Fading = false;
				}
			}
		}

		private void KeepFloor()
		{
			float num = _weights[0];
			if (!(num >= 0.001f))
			{
				float num2 = 0.999f / (1f - num);
				for (int i = 1; i < _weights.Length; i++)
				{
					_weights[i] *= num2;
				}
				_weights[0] = 0.001f;
			}
		}
	}
	internal static class EmoteControl
	{
		public static Func<PlayerControllerB, bool> Blocked = (PlayerControllerB _) => false;

		public static event Action<PlayerControllerB, int>? Started;

		public static event Action<PlayerControllerB>? Ended;

		public static bool TryStart(PlayerControllerB? player, int pressed)
		{
			EmoteRig emoteRig = EmoteRig.Of(player);
			if ((Object)(object)player == (Object)null || (Object)(object)emoteRig == (Object)null || !emoteRig.Built)
			{
				return false;
			}
			bool flag = pressed == 1010;
			bool typingOk = flag && emoteRig.Current == 10;
			if (!CanStart(player, typingOk, flag ? 0f : 0.5f, ownEmote: true))
			{
				return false;
			}
			if (pressed == 10 && IsUsingItem())
			{
				return false;
			}
			int num = EmoteDefs.Next(emoteRig.Current, pressed);
			if (num == -1 || !EmoteDefs.TryGet(num, out var _) || !ConfigManager.Enabled(num))
			{
				return false;
			}
			if (!CanPerform(player, emoteRig, num))
			{
				return false;
			}
			player.timeSinceStartingEmote = 0f;
			player.performingEmote = true;
			player.playerBodyAnimator.SetInteger("emoteNumber", 1);
			player.StartPerformingEmoteServerRpc();
			emoteRig.Play(num);
			SendStart(GetIndex(player), num);
			EmoteControl.Started?.Invoke(player, num);
			return true;
		}

		public static bool TryVanilla(PlayerControllerB player, int id)
		{
			EmoteRig emoteRig = EmoteRig.Of(player);
			if ((Object)(object)emoteRig == (Object)null || !CanStart(player, typingOk: false, 0.5f, ownEmote: false))
			{
				return false;
			}
			if (!CanPerform(player, emoteRig, id))
			{
				return false;
			}
			player.timeSinceStartingEmote = 0f;
			player.performingEmote = true;
			player.playerBodyAnimator.SetInteger("emoteNumber", id);
			player.StartPerformingEmoteServerRpc();
			OnVanillaStart(player);
			return true;
		}

		public static void StopLocal(PlayerControllerB player)
		{
			if (!((Object)(object)EmoteRig.Of(player) == (Object)null))
			{
				player.performingEmote = false;
				player.timeSinceStartingEmote = 0f;
				player.StopPerformingEmoteServerRpc();
			}
		}

		public static void OnRemoteStart(PlayerControllerB player, int emote)
		{
			EmoteRig emoteRig = EmoteRig.Of(player);
			if (!((Object)(object)emoteRig == (Object)null) && emoteRig.Built)
			{
				if (emote == 0)
				{
					End(player, FadeMode.Decay);
					return;
				}
				player.performingEmote = true;
				emoteRig.Play(emote);
				EmoteControl.Started?.Invoke(player, emote);
			}
		}

		public static void OnVanillaStart(PlayerControllerB player)
		{
			EmoteRig emoteRig = EmoteRig.Of(player);
			if (!((Object)(object)emoteRig == (Object)null) && emoteRig.Current != 0)
			{
				End(player, FadeMode.Decay);
				SendStart(GetIndex(player), 0);
			}
		}

		public static void End(PlayerControllerB player, FadeMode mode)
		{
			EmoteRig emoteRig = EmoteRig.Of(player);
			if (!((Object)(object)emoteRig == (Object)null) && emoteRig.Current != 0)
			{
				emoteRig.Stop();
				emoteRig.Mode = mode;
				EmoteControl.Ended?.Invoke(player);
			}
		}

		public static bool IsLocal(PlayerControllerB? player)
		{
			if ((Object)(object)player != (Object)null)
			{
				return (Object)(object)player == (Object)(object)GameNetworkManager.Instance.localPlayerController;
			}
			return false;
		}

		public static int GetIndex(PlayerControllerB player)
		{
			return Array.IndexOf(StartOfRound.Instance.allPlayerScripts, player);
		}

		public static int GetLocalIndex()
		{
			PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController;
			if (!((Object)(object)localPlayerController != (Object)null))
			{
				return -1;
			}
			return GetIndex(localPlayerController);
		}

		public static PlayerControllerB? GetPlayer(int index)
		{
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			if (index < 0 || index >= allPlayerScripts.Length)
			{
				return null;
			}
			return allPlayerScripts[index];
		}

		private static void SendStart(int index, int emote)
		{
			try
			{
				EmoteSync.SendStart(index, emote);
			}
			catch (Exception ex)
			{
				DrawSync.Report(ex);
			}
		}

		private static bool IsUsingItem()
		{
			InputAction val = InputSystem.actions.FindAction("ActivateItem", false);
			if (val != null)
			{
				return val.IsPressed();
			}
			return false;
		}

		private static bool CanStart(PlayerControllerB player, bool typingOk, float cooldown, bool ownEmote)
		{
			if (!((NetworkBehaviour)player).IsOwner || !player.isPlayerControlled || (((NetworkBehaviour)player).IsServer && !player.isHostPlayerObject))
			{
				return false;
			}
			if (player.isPlayerDead || player.inTerminalMenu || (player.isTypingChat && !typingOk))
			{
				return false;
			}
			if (player.quickMenuManager.isMenuOpen || player.inSpecialMenu)
			{
				return false;
			}
			if (!ownEmote || (!player.disableInteract && !Blocked(player)))
			{
				return player.timeSinceStartingEmote >= cooldown;
			}
			return false;
		}

		private static bool CanPerform(PlayerControllerB player, EmoteRig rig, int id)
		{
			rig.Requested = id;
			try
			{
				return player.CheckConditionsForEmote();
			}
			finally
			{
				rig.Requested = 0;
			}
		}
	}
	internal readonly struct EmoteDef
	{
		public readonly int Id;

		public readonly string Name;

		public readonly string Clip;

		public readonly int Input;

		public readonly float Speed;

		public readonly float Fade;

		public readonly float LoopStart;

		public EmoteDef(int id, string name, string clip, int input, float speed, float fade, float loopStart)
		{
			Id = id;
			Name = name;
			Clip = clip;
			Input = input;
			Speed = speed;
			Fade = fade;
			LoopStart = loopStart;
		}
	}
	internal static class EmoteDefs
	{
		public const int None = 0;

		public const int Dance = 1;

		public const int Point = 2;

		public const int MiddleFinger = 3;

		public const int Clap = 4;

		public const int Shy = 5;

		public const int Griddy = 6;

		public const int Twerk = 7;

		public const int Salute = 8;

		public const int Prisyadka = 9;

		public const int Sign = 10;

		public const int ThumbsUp = 11;

		public const int Wave = 12;

		public const int SixSeven = 13;

		public const int DoubleMiddleFinger = 1003;

		public const int DoubleClap = 1004;

		public const int SignShown = 1010;

		public const int InputCount = 15;

		public const string RestClip = "EmoteRest";

		private static readonly EmoteDef[] _all = new EmoteDef[14]
		{
			new EmoteDef(3, "Middle Finger", "Middle_Finger", 1, 1f, 0.1f, 0f),
			new EmoteDef(1003, "Double Middle Finger", "D_Middle_Finger", 2, 1f, 0.3f, 0f),
			new EmoteDef(4, "Clap", "Clap", 3, 1.11f, 0.25f, 0f),
			new EmoteDef(1004, "Double Clap", "D_Clap", 4, 1.4f, 0.1f, 0f),
			new EmoteDef(5, "Shy", "Shy", 5, 1f, 0.1f, 0f),
			new EmoteDef(6, "The Griddy", "The_Griddy", 6, 1f, 0.25f, 0f),
			new EmoteDef(7, "Twerk", "Twerk", 7, 1f, 0.2f, 0f),
			new EmoteDef(8, "Salute", "Salute", 8, 1f, 0.1f, 0f),
			new EmoteDef(9, "Prisyadka", "Prisyadka", 9, 1.05f, 0.2f, 0f),
			new EmoteDef(10, "Sign", "Sign", 10, 1f, 0.1f, 0f),
			new EmoteDef(1010, "Sign Shown", "D_Sign", 11, 1f, 0.1f, 0f),
			new EmoteDef(11, "Thumbs Up", "ThumbsUp", 12, 1f, 0.1f, 0f),
			new EmoteDef(12, "Wave", "Wave", 13, 1f, 0.1f, 0.4f),
			new EmoteDef(13, "6 7", "SixSeven", 14, 1f, 0.1f, 0.3f)
		};

		public static IReadOnlyList<EmoteDef> All => _all;

		public static bool IsWireId(int id)
		{
			EmoteDef def;
			if (id != 0)
			{
				return TryGet(id, out def);
			}
			return true;
		}

		public static bool HidesItem(int id)
		{
			if (id != 11 && id != 12)
			{
				return id == 13;
			}
			return true;
		}

		public static bool HasSteps(int id)
		{
			if (id != 6 && id != 8)
			{
				return id == 9;
			}
			return true;
		}

		public static bool LoopsSteps(int id)
		{
			if (id != 6)
			{
				return id == 9;
			}
			return true;
		}

		public static bool TryGet(int id, out EmoteDef def)
		{
			for (int i = 0; i < _all.Length; i++)
			{
				if (_all[i].Id == id)
				{
					def = _all[i];
					return true;
				}
			}
			def = default(EmoteDef);
			return false;
		}

		public static string Label(int id)
		{
			switch (id)
			{
			case 1:
				return "Dance";
			case 2:
				return "Point";
			default:
			{
				if (!TryGet(id, out var def))
				{
					return "Empty";
				}
				return def.Name;
			}
			}
		}

		public static int Next(int current, int pressed)
		{
			if (pressed == 3 || pressed == 4)
			{
				if (current != pressed)
				{
					return pressed;
				}
				return pressed + 1000;
			}
			if (pressed == current)
			{
				return -1;
			}
			return pressed;
		}
	}
	internal sealed class EmoteRig : MonoBehaviour
	{
		private const string GraphName = "MuchBetterEmotes";

		private const string IdleState = "Idle1";

		private const float MinPitch = 0.59f;

		private const float MaxPitch = 0.79f;

		private const float NoiseRange = 22f;

		private const float NoiseLoudness = 0.6f;

		private const int NoiseId = 6;

		private const int RemoteNoiseId = 7;

		private readonly AnimationClipPlayable[] _inputs = (AnimationClipPlayable[])(object)new AnimationClipPlayable[15];

		private readonly float[] _lengths = new float[15];

		private PlayableGraph _graph;

		private AnimationMixerPlayable _mixer;

		private AnimationPlayableOutput _output;

		private Animator _animator;

		private PlayerControllerB _player;

		private bool _built;

		public bool Built => _built;

		public Blend Blend { get; private set; }

		public RigWeights Weights { get; private set; }

		public float Output { get; set; }

		public FadeMode Mode { get; set; }

		public int Current { get; set; }

		public int Requested { get; set; }

		public float SpeedApplied { get; set; }

		public float SpeedOriginal { get; set; }

		public float SpeedMultiplier { get; set; }

		public GameObject? Sign { get; set; }

		public MeshRenderer? Board { get; set; }

		public MeshRenderer? Back { get; set; }

		public Renderer? Legs { get; set; }

		public GrabbableObject? Held { get; set; }

		public bool HidingItem { get; set; }

		public List<BeltBagItem> Bags { get; } = new List<BeltBagItem>();

		public static EmoteRig? Of(PlayerControllerB? player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)player.playerBodyAnimator == (Object)null)
			{
				return null;
			}
			return ((Component)player.playerBodyAnimator).GetComponent<EmoteRig>();
		}

		public void Play(int emote)
		{
			if (_built && EmoteDefs.TryGet(emote, out var def))
			{
				bool flag = Current == 0;
				Current = emote;
				if (flag && !SyncRest())
				{
					Blend.Snap(def.Input);
				}
				else
				{
					Blend.FadeTo(def.Input, def.Fade);
				}
				StartClip(def);
			}
		}

		public void Stop()
		{
			if (_built)
			{
				Current = 0;
				SyncRest();
				Blend.FadeTo(0, 0.1f);
			}
		}

		public void Apply(float dt)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (!_built)
			{
				return;
			}
			WrapLoop(dt);
			Blend.Update(dt);
			IReadOnlyList<float> weights = Blend.Weights;
			int target = Blend.Target;
			for (int i = 0; i < weights.Count; i++)
			{
				float num = weights[i];
				PlayableExtensions.SetInputWeight<AnimationMixerPlayable>(_mixer, i, num);
				if (num <= 0f && i != target)
				{
					PlayableExtensions.SetSpeed<AnimationClipPlayable>(_inputs[i], 0.0);
				}
			}
			PlayableOutputExtensions.SetWeight<AnimationPlayableOutput>(_output, Output);
		}

		public void PlayClapSound()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			if (Current == 4 || Current == 1004)
			{
				AudioClip[] claps = AssetsLoader.Claps;
				if (_player.performingEmote && claps.Length != 0)
				{
					_player.movementAudio.pitch = Random.Range(0.59f, 0.79f);
					_player.movementAudio.PlayOneShot(claps[Random.Range(0, claps.Length)]);
					bool flag = _player.isInHangarShipRoom && StartOfRound.Instance.hangarDoorsClosed;
					int num = (((NetworkBehaviour)_player).IsOwner ? 6 : 7);
					RoundManager.Instance.PlayAudibleNoise(((Component)_player).transform.position, 22f, 0.6f, 0, flag, num);
				}
			}
		}

		public void PlayFootstepSound()
		{
			if (EmoteDefs.HasSteps(Current) && _player.performingEmote)
			{
				if (((NetworkBehaviour)_player).IsOwner)
				{
					_player.PlayFootstepLocal();
				}
				else
				{
					_player.PlayFootstepServer();
				}
			}
		}

		private void Awake()
		{
			_player = ((Component)this).GetComponentInParent<PlayerControllerB>();
			if ((Object)(object)_player == (Object)null)
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			Blend = new Blend(15);
			Weights = new RigWeights(_player);
			Weights.Capture();
			try
			{
				Build();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to set up emotes for player {_player.playerClientId}: {arg}");
				if (((PlayableGraph)(ref _graph)).IsValid())
				{
					((PlayableGraph)(ref _graph)).Destroy();
				}
			}
		}

		private void OnDestroy()
		{
			_built = false;
			if (((PlayableGraph)(ref _graph)).IsValid())
			{
				((PlayableGraph)(ref _graph)).Destroy();
			}
		}

		private void Build()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_007f: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: 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)
			_animator = ((Component)this).GetComponent<Animator>();
			AnimationClip[] clips = AssetsLoader.Clips;
			_graph = PlayableGraph.Create("MuchBetterEmotes");
			((PlayableGraph)(ref _graph)).SetTimeUpdateMode((DirectorUpdateMode)1);
			_mixer = AnimationMixerPlayable.Create(_graph, 15);
			for (int i = 0; i < 15; i++)
			{
				_inputs[i] = AnimationClipPlayable.Create(_graph, clips[i]);
				_lengths[i] = clips[i].length;
				((PlayableGraph)(ref _graph)).Connect<AnimationClipPlayable, AnimationMixerPlayable>(_inputs[i], 0, _mixer, i);
				PlayableExtensions.SetInputWeight<AnimationMixerPlayable>(_mixer, i, (i == 0) ? 1f : 0f);
				PlayableExtensions.SetSpeed<AnimationClipPlayable>(_inputs[i], 0.0);
			}
			_output = AnimationPlayableOutput.Create(_graph, "MuchBetterEmotes", _animator);
			AnimationPlayableOutputExtensions.SetAnimationStreamSource(_output, (AnimationStreamSource)1);
			AnimationPlayableOutputExtensions.SetSortingOrder(_output, (ushort)600);
			PlayableOutputExtensions.SetSourcePlayable<AnimationPlayableOutput, AnimationMixerPlayable>(_output, _mixer);
			PlayableOutputExtensions.SetWeight<AnimationPlayableOutput>(_output, 0f);
			((PlayableGraph)(ref _graph)).Play();
			_built = true;
		}

		private bool SyncRest()
		{
			//IL_0007: 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_0024: 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)
			AnimatorStateInfo currentAnimatorStateInfo = _animator.GetCurrentAnimatorStateInfo(0);
			if (!((AnimatorStateInfo)(ref currentAnimatorStateInfo)).IsName("Idle1"))
			{
				return false;
			}
			SetTimeQuiet(_inputs[0], ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).normalizedTime % 1f * _lengths[0]);
			PlayableExtensions.SetSpeed<AnimationClipPlayable>(_inputs[0], 1.0);
			return true;
		}

		private void WrapLoop(float dt)
		{
			//IL_0031: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (Current != 0 && EmoteDefs.TryGet(Current, out var def) && !(def.LoopStart <= 0f))
			{
				AnimationClipPlayable val = _inputs[def.Input];
				float time = (float)PlayableExtensions.GetTime<AnimationClipPlayable>(val);
				float speed = (float)PlayableExtensions.GetSpeed<AnimationClipPlayable>(val);
				if (LoopMath.TryWrap(time, dt, speed, _lengths[def.Input], def.LoopStart, out var wrapped))
				{
					SetTimeQuiet(val, wrapped);
				}
			}
		}

		private void StartClip(EmoteDef def)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			AnimationClipPlayable val = _inputs[def.Input];
			SetTimeQuiet(val, 0.0);
			PlayableExtensions.SetSpeed<AnimationClipPlayable>(val, (double)def.Speed);
		}

		private static void SetTimeQuiet(AnimationClipPlayable input, double time)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			PlayableExtensions.SetTime<AnimationClipPlayable>(input, time);
			PlayableExtensions.SetTime<AnimationClipPlayable>(input, time);
		}
	}
	internal static class HeldItem
	{
		public static void OnStarted(PlayerControllerB player, int emote)
		{
			EmoteRig emoteRig = EmoteRig.Of(player);
			Show(emoteRig);
			DisableBags(player, emoteRig);
			emoteRig.HidingItem = ConfigManager.HideHeldItem.Value || EmoteDefs.HidesItem(emote);
			if (emoteRig.HidingItem)
			{
				Hide(player, emoteRig);
			}
		}

		public static void OnEnded(PlayerControllerB player)
		{
			EmoteRig emoteRig = EmoteRig.Of(player);
			Show(emoteRig);
			emoteRig.HidingItem = false;
			EnableBags(player, emoteRig);
		}

		public static void Check(PlayerControllerB player, EmoteRig rig)
		{
			if (rig.HidingItem && rig.Current != 0)
			{
				GrabbableObject val = (player.isHoldingObject ? player.currentlyHeldObjectServer : null);
				if ((Object)(object)val != (Object)(object)rig.Held)
				{
					Show(rig);
					Hide(player, rig);
				}
			}
		}

		public static void OnPocket(BeltBagItem bag)
		{
			PlayerControllerB playerHeldBy = ((GrabbableObject)bag).playerHeldBy;
			if (!((Object)(object)playerHeldBy == (Object)null) && playerHeldBy.performingEmote)
			{
				EmoteRig emoteRig = EmoteRig.Of(playerHeldBy);
				if ((Object)(object)emoteRig != (Object)null && emoteRig.Current != 0)
				{
					DisableBag((GrabbableObject?)(object)bag, emoteRig);
				}
			}
		}

		private static void DisableBags(PlayerControllerB player, EmoteRig rig)
		{
			GrabbableObject[] itemSlots = player.ItemSlots;
			foreach (GrabbableObject item in itemSlots)
			{
				DisableBag(item, rig);
			}
			DisableBag(player.ItemOnlySlot, rig);
		}

		private static void DisableBag(GrabbableObject? item, EmoteRig rig)
		{
			BeltBagItem val = (BeltBagItem)(object)((item is BeltBagItem) ? item : null);
			if (val == null || !((GrabbableObject)val).isPocketed)
			{
				return;
			}
			Collider component = ((Component)val.useBagTrigger).GetComponent<Collider>();
			if (component.enabled)
			{
				component.enabled = false;
				if (!rig.Bags.Contains(val))
				{
					rig.Bags.Add(val);
				}
			}
		}

		private static void EnableBags(PlayerControllerB player, EmoteRig rig)
		{
			foreach (BeltBagItem bag in rig.Bags)
			{
				if (!((Object)(object)bag == (Object)null) && ((GrabbableObject)bag).isPocketed && !((Object)(object)((GrabbableObject)bag).playerHeldBy != (Object)(object)player))
				{
					((Component)bag.useBagTrigger).GetComponent<Collider>().enabled = true;
				}
			}
			rig.Bags.Clear();
		}

		private static void Hide(PlayerControllerB player, EmoteRig rig)
		{
			GrabbableObject val = (player.isHoldingObject ? player.currentlyHeldObjectServer : null);
			if (!((Object)(object)val == (Object)null) && !val.isPocketed)
			{
				val.EnableItemMeshes(false);
				rig.Held = val;
			}
		}

		private static void Show(EmoteRig rig)
		{
			GrabbableObject held = rig.Held;
			if (!((Object)(object)held == (Object)null))
			{
				rig.Held = null;
				if (!held.isPocketed)
				{
					held.EnableItemMeshes(true);
				}
			}
		}
	}
	internal static class LoopMath
	{
		public static float Wrap(float time, float length, float loopStart)
		{
			if (loopStart <= 0f || loopStart >= length || time < length)
			{
				return time;
			}
			return loopStart + (time - length) % (length - loopStart);
		}

		public static bool TryWrap(float time, float dt, float speed, float length, float loopStart, out float wrapped)
		{
			float num = time + dt * speed;
			wrapped = Wrap(num, length, loopStart);
			return wrapped != num;
		}
	}
	internal enum RigPart
	{
		RightArm,
		LeftArm,
		FpRightArm,
		FpLeftArm,
		LookHead,
		LookHead2
	}
	internal sealed class RigWeights
	{
		private const int PartCount = 6;

		private static readonly int[][] _inputs = new int[6][]
		{
			new int[3] { 8, 10, 11 },
			new int[2] { 10, 11 },
			new int[4] { 6, 9, 10, 11 },
			new int[4] { 6, 9, 10, 11 },
			new int[3] { 7, 10, 11 },
			new int[1] { 10 }
		};

		private readonly IRigConstraint?[] _constraints = (IRigConstraint?[])(object)new IRigConstraint[6];

		private readonly float[] _rest = new float[6];

		private bool _rested;

		public RigWeights(PlayerControllerB player)
		{
			_constraints[0] = (IRigConstraint?)(object)player.rightArmRig;
			_constraints[1] = (IRigConstraint?)(object)player.leftArmRig;
			_constraints[2] = (IRigConstraint?)(object)player.rightArmRigSecondaryLocal;
			_constraints[3] = (IRigConstraint?)(object)player.leftArmRigSecondaryLocal;
			_constraints[4] = (IRigConstraint?)(object)player.cameraLookRig1;
			_constraints[5] = (IRigConstraint?)(object)player.cameraLookRig2;
		}

		public void Capture()
		{
			for (int i = 0; i < 6; i++)
			{
				IRigConstraint val = _constraints[i];
				if (val != null && IsFp((RigPart)i))
				{
					_rest[i] = val.weight;
				}
			}
			_rested = true;
		}

		public void Apply(EmoteRig rig)
		{
			Blend blend = rig.Blend;
			if (rig.Output == 0f && blend.Target == 0 && !blend.Fading)
			{
				if (!_rested)
				{
					WriteRest();
					_rested = true;
				}
				return;
			}
			_rested = false;
			for (int i = 0; i < 6; i++)
			{
				IRigConstraint val = _constraints[i];
				if (val != null)
				{
					float num = (IsFp((RigPart)i) ? _rest[i] : val.weight);
					val.weight = num * (1f - Mathf.Clamp01(rig.Output * blend.Share(_inputs[i])));
				}
			}
		}

		private void WriteRest()
		{
			for (int i = 0; i < 6; i++)
			{
				IRigConstraint val = _constraints[i];
				if (val != null && IsFp((RigPart)i))
				{
					val.weight = _rest[i];
				}
			}
		}

		private static bool IsFp(RigPart part)
		{
			if (part != RigPart.FpRightArm)
			{
				return part == RigPart.FpLeftArm;
			}
			return true;
		}
	}
	internal sealed class EmoteInputs : LcInputActions
	{
		[InputAction("", Name = "Middle Finger")]
		public InputAction MiddleFinger { get; set; }

		[InputAction("", Name = "Clap")]
		public InputAction Clap { get; set; }

		[InputAction("", Name = "Shy")]
		public InputAction Shy { get; set; }

		[InputAction("", Name = "The Griddy")]
		public InputAction Griddy { get; set; }

		[InputAction("", Name = "Twerk")]
		public InputAction Twerk { get; set; }

		[InputAction("", Name = "Salute")]
		public InputAction Salute { get; set; }

		[InputAction("", Name = "Prisyadka")]
		public InputAction Prisyadka { get; set; }

		[InputAction("", Name = "Sign")]
		public InputAction Sign { get; set; }

		[InputAction("", Name = "Thumbs Up")]
		public InputAction ThumbsUp { get; set; }

		[InputAction("", Name = "Wave")]
		public InputAction Wave { get; set; }

		[InputAction("", Name = "6 7")]
		public InputAction SixSeven { get; set; }

		[InputAction("<Keyboard>/v", GamepadPath = "<Gamepad>/select", Name = "Emote Wheel")]
		public InputAction Wheel { get; set; }

		[InputAction("<Mouse>/scroll/up", GamepadPath = "<Gamepad>/dpad/right", Name = "Next Wheel Page")]
		public InputAction WheelNext { get; set; }

		[InputAction("<Mouse>/scroll/down", GamepadPath = "<Gamepad>/dpad/left", Name = "Previous Wheel Page")]
		public InputAction WheelPrevious { get; set; }

		[InputAction(/*Could not decode attribute arguments.*/)]
		public InputAction WheelStick { get; set; }

		[InputAction("<Keyboard>/enter", GamepadPath = "<Gamepad>/buttonWest", Name = "Show Sign")]
		public InputAction SignShow { get; set; }

		[InputAction("<Keyboard>/escape", GamepadPath = "<Gamepad>/buttonEast", Name = "Put Away Sign")]
		public InputAction SignAway { get; set; }
	}
	internal static class InputHandlers
	{
		public static EmoteInputs Inputs { get; private set; }

		public static void Init()
		{
			Inputs = new EmoteInputs();
			for (int i = 3; i <= 13; i++)
			{
				int emote = i;
				GetAction(emote).performed += delegate
				{
					StartEmote(emote);
				};
			}
			Inputs.Wheel.started += delegate
			{
				UseWheel(delegate(EmoteWheel wheel)
				{
					wheel.Open();
				});
			};
			Inputs.Wheel.canceled += delegate
			{
				UseWheel(delegate(EmoteWheel wheel)
				{
					wheel.Release();
				});
			};
			Inputs.WheelNext.performed += delegate
			{
				UseWheel(delegate(EmoteWheel wheel)
				{
					wheel.ChangePage(1);
				});
			};
			Inputs.WheelPrevious.performed += delegate
			{
				UseWheel(delegate(EmoteWheel wheel)
				{
					wheel.ChangePage(-1);
				});
			};
			Inputs.SignShow.performed += delegate
			{
				DrawPanel.OnShowKey();
			};
			Inputs.SignAway.performed += delegate
			{
				DrawPanel.OnAwayKey();
			};
		}

		public static string GetKey(InputAction action, int index)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			if (index >= action.bindings.Count)
			{
				return "";
			}
			return InputActionRebindingExtensions.GetBindingDisplayString(action, index, (DisplayStringOptions)0) ?? "";
		}

		public static InputAction? GetAction(int id)
		{
			return (InputAction?)(id switch
			{
				1 => InputSystem.actions.FindAction("Emote1", false), 
				2 => InputSystem.actions.FindAction("Emote2", false), 
				3 => Inputs.MiddleFinger, 
				4 => Inputs.Clap, 
				5 => Inputs.Shy, 
				6 => Inputs.Griddy, 
				7 => Inputs.Twerk, 
				8 => Inputs.Salute, 
				9 => Inputs.Prisyadka, 
				10 => Inputs.Sign, 
				11 => Inputs.ThumbsUp, 
				12 => Inputs.Wave, 
				13 => Inputs.SixSeven, 
				_ => null, 
			});
		}

		private static void StartEmote(int id)
		{
			EmoteControl.TryStart(GameNetworkManager.Instance.localPlayerController, id);
		}

		private static void UseWheel(Action<EmoteWheel> action)
		{
			EmoteWheel current = EmoteWheel.Current;
			if ((Object)(object)current != (Object)null)
			{
				action(current);
			}
		}
	}
	internal static class ModGUIDs
	{
		public const string InputUtils = "com.rune580.LethalCompanyInputUtils";

		public const string LethalConfig = "ainavt.lc.lethalconfig";

		public const string LethalNetworkAPI = "LethalNetworkAPI";

		public const string LethalPhonesHarmony = "LethalPhones";

		public const string TooManyEmotes = "FlipMods.TooManyEmotes";

		public const string LCVR = "io.daxcess.lcvr";

		public const string FirstPersonView = "com.seeya.firstpersonview";
	}
	internal static class DrawSync
	{
		private static LNetworkMessage<StrokeData> _strokeMessage = null;

		private static LNetworkMessage<int> _undoMessage = null;

		private static LNetworkMessage<int> _clearMessage = null;

		private static readonly Dictionary<int, RateBucket> _buckets = new Dictionary<int, RateBucket>();

		private static readonly Dictionary<int, RateBucket> _editBuckets = new Dictionary<int, RateBucket>();

		private static bool _resend = true;

		private static bool _reported;

		public static DrawWriter Pending { get; } = new DrawWriter();

		public static bool Hold { get; set; }

		public static void Initialize()
		{
			_strokeMessage = LNetworkMessage<StrokeData>.Connect("MuchBetterEmotes_Stroke", (Action<StrokeData, ulong>)OnServerStroke, (Action<StrokeData>)OnClientStroke, (Action<StrokeData, ulong>)null);
			_undoMessage = LNetworkMessage<int>.Connect("MuchBetterEmotes_Undo", (Action<int, ulong>)OnServerUndo, (Action<int>)OnClientUndo, (Action<int, ulong>)null);
			_clearMessage = LNetworkMessage<int>.Connect("MuchBetterEmotes_Clear", (Action<int, ulong>)OnServerClear, (Action<int>)OnClientClear, (Action<int, ulong>)null);
		}

		public static void Reset()
		{
			_resend = true;
			_reported = false;
			Hold = false;
			Pending.Start(-1);
		}

		public static void OnJoin()
		{
			_resend = true;
		}

		public static void Report(Exception ex)
		{
			if (!_reported)
			{
				_reported = true;
				Plugin.Log.LogError((object)$"Failed to send to other players: {ex}");
			}
		}

		public static void Flush()
		{
			try
			{
				if (!Hold)
				{
					SendPending();
				}
			}
			catch (Exception ex)
			{
				_resend = true;
				Report(ex);
			}
			finally
			{
				Pending.Start(EmoteControl.GetLocalIndex());
			}
		}

		public static void TrySend(PlayerControllerB local, int emote)
		{
			if (emote switch
			{
				10 => !Hold && _resend, 
				1010 => Hold || _resend, 
				_ => false, 
			})
			{
				SendLog(local);
			}
		}

		public static void SendLog(PlayerControllerB local)
		{
			int index = EmoteControl.GetIndex(local);
			if (index >= 0)
			{
				Pending.Start(index);
				Pending.AddLog(SignCanvases.Book.Get(index));
				_resend = false;
				Hold = false;
				Flush();
			}
		}

		private static void SendPending()
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (!singleton.IsConnectedClient)
			{
				return;
			}
			ulong localClientId = singleton.LocalClientId;
			for (int i = 0; i < Pending.Count; i++)
			{
				switch (Pending.GetKind(i))
				{
				case DrawKind.Stroke:
					Send(_strokeMessage, singleton, Pending.GetMessage(i), localClientId);
					break;
				case DrawKind.Undo:
					Send(_undoMessage, singleton, Pending.Player, localClientId);
					break;
				default:
					Send(_clearMessage, singleton, Pending.Player, localClientId);
					break;
				}
			}
		}

		private static void Send<T>(LNetworkMessage<T> message, NetworkManager network, T data, ulong sender)
		{
			if (network.IsServer)
			{
				message.SendClients(data, LNetworkUtils.AllConnectedClientsExcept(new ulong[2] { sender, network.LocalClientId }));
			}
			else
			{
				message.SendServer(data);
			}
		}

		private static void OnServerStroke(StrokeData data, ulong sender)
		{
			if (AcceptFrom(data.Player, sender) && SignCanvases.Book.TryApply(data, GetCount(), out var firstNew))
			{
				Send(_strokeMessage, NetworkManager.Singleton, data, sender);
				SignCanvases.OnApplied(data.Player, structural: false, firstNew);
			}
		}

		private static void OnClientStroke(StrokeData data)
		{
			if (!NetworkManager.Singleton.IsServer && IsRemote(data.Player) && SignCanvases.Book.TryApply(data, GetCount(), out var firstNew))
			{
				SignCanvases.OnApplied(data.Player, structural: false, firstNew);
			}
		}

		private static void OnServerUndo(int player, ulong sender)
		{
			OnServerEdit(_undoMessage, player, sender, undo: true);
		}

		private static void OnServerClear(int player, ulong sender)
		{
			OnServerEdit(_clearMessage, player, sender, undo: false);
		}

		private static void OnClientUndo(int player)
		{
			if (!NetworkManager.Singleton.IsServer && IsRemote(player) && Edit(player, undo: true))
			{
				SignCanvases.OnApplied(player, structural: true, 0);
			}
		}

		private static void OnClientClear(int player)
		{
			if (!NetworkManager.Singleton.IsServer && IsRemote(player) && Edit(player, undo: false))
			{
				SignCanvases.OnApplied(player, structural: true, 0);
			}
		}

		private static void OnServerEdit(LNetworkMessage<int> message, int player, ulong sender, bool undo)
		{
			if (AcceptEdit(player, sender) && Edit(player, undo))
			{
				Send(message, NetworkManager.Singleton, player, sender);
				SignCanvases.OnApplied(player, structural: true, 0);
			}
		}

		private static bool Edit(int player, bool undo)
		{
			DrawBook book = SignCanvases.Book;
			if (!undo)
			{
				return book.TryClear(player, GetCount());
			}
			return book.TryUndo(player, GetCount());
		}

		private static bool AcceptFrom(int index, ulong sender)
		{
			PlayerControllerB player = EmoteControl.GetPlayer(index);
			if ((Object)(object)player != (Object)null && ((NetworkBehaviour)player).OwnerClientId == sender && player.isPlayerControlled)
			{
				return RateBucket.TakeToken(_buckets, index, Time.realtimeSinceStartup, 128, 20f);
			}
			return false;
		}

		private static bool AcceptEdit(int index, ulong sender)
		{
			if (AcceptFrom(index, sender))
			{
				return RateBucket.TakeToken(_editBuckets, index, Time.realtimeSinceStartup, 12, 12f);
			}
			return false;
		}

		private static bool IsRemote(int index)
		{
			if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null)
			{
				return false;
			}
			PlayerControllerB player = EmoteControl.GetPlayer(index);
			if ((Object)(object)player != (Object)null)
			{
				return !((NetworkBehaviour)player).IsOwner;
			}
			return false;
		}

		private static int GetCount()
		{
			return StartOfRound.Instance.allPlayerScripts.Length;
		}
	}
	internal static class EmoteSync
	{
		private static LNetworkMessage<StartData> _startMessage = null;

		private static readonly Dictionary<int, RateBucket> _buckets = new Dictionary<int, RateBucket>();

		public static void Initialize()
		{
			_startMessage = LNetworkMessage<StartData>.Connect("MuchBetterEmotes_Start", (Action<StartData, ulong>)OnServerReceived, (Action<StartData>)OnClientReceived, (Action<StartData, ulong>)null);
		}

		public static void SendStart(int player, int emote)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (singleton.IsConnectedClient)
			{
				_startMessage.SendServer(new StartData(player, emote));
			}
		}

		private static void OnServerReceived(StartData message, ulong sender)
		{
			if (!message.IsValid)
			{
				return;
			}
			PlayerControllerB player = EmoteControl.GetPlayer(message.Player);
			if (!((Object)(object)player == (Object)null) && ((NetworkBehaviour)player).OwnerClientId == sender && RateBucket.TakeToken(_buckets, message.Player, Time.realtimeSinceStartup, 16, 4f) && (message.Emote == 0 || (player.isPlayerControlled && !player.isPlayerDead)))
			{
				NetworkManager singleton = NetworkManager.Singleton;
				_startMessage.SendClients(message, LNetworkUtils.AllConnectedClientsExcept(new ulong[2] { sender, singleton.LocalClientId }));
				if (singleton.IsHost && !((NetworkBehaviour)player).IsOwner)
				{
					EmoteControl.OnRemoteStart(player, message.Emote);
				}
			}
		}

		private static void OnClientReceived(StartData message)
		{
			if (!NetworkManager.Singleton.IsServer)
			{
				ReceiveOnClient(message);
			}
		}

		private static void ReceiveOnClient(StartData message)
		{
			if (!((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) && message.IsValid)
			{
				PlayerControllerB player = EmoteControl.GetPlayer(message.Player);
				if (!((Object)(object)player == (Object)null) && !((NetworkBehaviour)player).IsOwner)
				{
					EmoteControl.OnRemoteStart(player, message.Emote);
				}
			}
		}
	}
	internal sealed class RateBucket
	{
		private readonly int _size;

		private readonly float _rate;

		private float _tokens;

		private float _time;

		public RateBucket(int size, float rate)
		{
			_size = size;
			_rate = rate;
			_tokens = size;
		}

		public static bool TakeToken(Dictionary<int, RateBucket> buckets, int index, float now, int size, float rate)
		{
			if (!buckets.TryGetValue(index, out RateBucket value))
			{
				value = (buckets[index] = new RateBucket(size, rate));
			}
			return value.Take(now);
		}

		public bool Take(float now)
		{
			_tokens = Math.Min(_size, _tokens + Math.Max(0f, now - _time) * _rate);
			_time = now;
			if (_tokens < 1f)
			{
				return false;
			}
			_tokens -= 1f;
			return true;
		}
	}
	[Serializable]
	public struct StartData
	{
		public int Player;

		public int Emote;

		public bool IsValid
		{
			get
			{
				if (Player >= 0)
				{
					return EmoteDefs.IsWireId(Emote);
				}
				return false;
			}
		}

		public StartData(int player, int emote)
		{
			Player = player;
			Emote = emote;
		}
	}
	[Serializable]
	public struct StrokeData
	{
		public int Player;

		public bool Continue;

		public int[] Starts;

		public int[] Colors;

		public byte[] Sizes;

		public ushort[] X;

		public ushort[] Y;

		public StrokeData(int player, bool extend, int[] starts, int[] colors, byte[] sizes, ushort[] x, ushort[] y)
		{
			Player = player;
			Continue = extend;
			Starts = starts;
			Colors = colors;
			Sizes = sizes;
			X = x;
			Y = y;
		}

		internal bool IsValid(DrawLog log)
		{
			if (Starts == null || Colors == null || Sizes == null || X == null || Y == null)
			{
				return false;
			}
			int num = X.Length;
			int num2 = Starts.Length;
			if (num < 1 || num > 255 || Y.Length != num)
			{
				return false;
			}
			if (Colors.Length != num2 || Sizes.Length != num2)
			{
				return false;
			}
			bool num3;
			if (!Continue)
			{
				if (num2 == 0)
				{
					goto IL_0093;
				}
				num3 = Starts[0] != 0;
			}
			else
			{
				num3 = !log.Open;
			}
			if (!num3)
			{
				int num4 = -1;
				for (int i = 0; i < num2; i++)
				{
					if (Starts[i] <= num4 || Starts[i] >= num || !DrawLog.IsValidStroke(Colors[i], Sizes[i]))
					{
						return false;
					}
					num4 = Starts[i];
					int num5 = ((i + 1 < num2) ? Starts[i + 1] : num);
					if ((Sizes[i] == 1 || DrawLog.IsText(Sizes[i])) && num5 - Starts[i] != DrawLog.StrokePoints(Sizes[i]))
					{
						return false;
					}
				}
				return log.PointCount + num <= log.Capacity;
			}
			goto IL_0093;
			IL_0093:
			return false;
		}
	}
	[HarmonyPatch(typeof(BeltBagItem))]
	internal static class BeltBagItemPatch
	{
		[HarmonyPatch("PocketItem")]
		[HarmonyPostfix]
		private static void KeepOff(BeltBagItem __instance)
		{
			HeldItem.OnPocket(__instance);
		}
	}
	[HarmonyPatch(typeof(GameNetworkManager), "Disconnect")]
	internal static class GameNetworkManagerPatch
	{
		[HarmonyPrefix]
		private static void OnDisconnect()
		{
			try
			{
				DrawPanel current = DrawPanel.Current;
				if ((Object)(object)current != (Object)null)
				{
					current.Close();
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to close the drawing panel on disconnect: {arg}");
			}
			try
			{
				DrawSync.Reset();
				SignCanvases.DestroyAll();
				SignCanvases.ClearAll();
			}
			catch (Exception arg2)
			{
				Plugin.Log.LogError((object)$"Failed to reset the signs on disconnect: {arg2}");
			}
		}
	}
	[HarmonyPatch(typeof(HUDManager))]
	internal static class HUDManagerPatch
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void CreateUi(HUDManager __instance)
		{
			try
			{
				Transform parent = __instance.HUDContainer.transform.parent;
				DrawPanel.Create(parent, ((TMP_Text)__instance.chatText).font);
				EmoteWheel.Create(parent);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to create the HUD: {arg}");
			}
		}
	}
	[HarmonyPatch]
	internal static class InputBlockPatch
	{
		[HarmonyPatch(typeof(PlayerControllerB), "Interact_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "ItemSecondaryUse_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "ItemTertiaryUse_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "QEItemInteract_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "ActivateItem_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "ActivateItem_canceled")]
		[HarmonyPatch(typeof(PlayerControllerB), "Discard_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "ScrollMouse_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "UseUtilitySlot_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "InspectItem_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "OpenMenu_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "Jump_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "Crouch_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "Emote1_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "Emote2_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "SetFreeCamera_performed")]
		[HarmonyPatch(typeof(PlayerControllerB), "SpeedCheat_performed")]
		[HarmonyPatch(typeof(HUDManager), "SubmitChat_performed")]
		[HarmonyPatch(typeof(HUDManager), "EnableChat_performed")]
		[HarmonyPatch(typeof(HUDManager), "OpenMenu_performed")]
		[HarmonyPatch(typeof(HUDManager), "PingScan_performed")]
		[HarmonyPatch(typeof(QuickMenuManager), "OpenQuickMenu")]
		[HarmonyPatch(typeof(ShipBuildModeManager), "EnterBuildMode")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool BlockInput()
		{
			if (!DrawPanel.IsOpen)
			{
				return DrawPanel.ClosedFrame != Time.frameCount;
			}
			return false;
		}

		[HarmonyPatch(typeof(PlayerControllerB), "PlayerLookInput")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool BlockLook()
		{
			return !DrawPanel.IsOpen;
		}
	}
	[HarmonyPatch(typeof(PlayerAnimationEvents))]
	internal static class PlayerAnimationEventsPatch
	{
		[HarmonyPatch("PlayFootstepLocal")]
		[HarmonyPrefix]
		private static bool SkipLocalStep(PlayerAnimationEvents __instance)
		{
			return !HasEmoteSteps(__instance.thisPlayerController);
		}

		[HarmonyPatch("PlayFootstepServer")]
		[HarmonyPrefix]
		private static bool SkipServerStep(PlayerAnimationEvents __instance)
		{
			return !HasEmoteSteps(__instance.thisPlayerController);
		}

		private static bool HasEmoteSteps(PlayerControllerB player)
		{
			EmoteRig emoteRig = EmoteRig.Of(player);
			if ((Object)(object)emoteRig != (Object)null && EmoteDefs.LoopsSteps(emoteRig.Current))
			{
				return player.performingEmote;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal static class PlayerControllerBPatch
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		[HarmonyPriority(200)]
		[HarmonyAfter(new string[] { "LethalPhones" })]
		private static void CreateRig(PlayerControllerB __instance)
		{
			try
			{
				if (!((Object)(object)EmoteRig.Of(__instance) != (Object)null))
				{
					MeshRenderer board;
					MeshRenderer back;
					GameObject sign = Props.AddSign(__instance, out board, out back);
					Renderer legs = null;
					if (!Chainloader.PluginInfos.ContainsKey("com.seeya.firstpersonview"))
					{
						legs = Props.AddLegs(__instance);
					}
					EmoteRig emoteRig = ((Component)__instance.playerBodyAnimator).gameObject.AddComponent<EmoteRig>();
					emoteRig.Sign = sign;
					emoteRig.Board = board;
					emoteRig.Back = back;
					emoteRig.Legs = legs;
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to set up emotes for player {__instance.playerClientId}: {arg}");
			}
		}

		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void UpdateRig(PlayerControllerB __instance)
		{
			EmoteRig emoteRig = EmoteRig.Of(__instance);
			if (!((Object)(object)emoteRig == (Object)null))
			{
				CheckStops(__instance, emoteRig);
				HeldItem.Check(__instance, emoteRig);
				float deltaTime = Time.deltaTime;
				emoteRig.Output = GetOutput(emoteRig, __instance.emoteLayerWeight, deltaTime);
				emoteRig.Apply(deltaTime);
				emoteRig.Weights.Apply(emoteRig);
			}
		}

		[HarmonyPatch("Update")]
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static void ApplySpeed(PlayerControllerB __instance)
		{
			if (!((NetworkBehaviour)__instance).IsOwner || !__instance.performingEmote)
			{
				return;
			}
			EmoteRig emoteRig = EmoteRig.Of(__instance);
			if (!((Object)(object)emoteRig == (Object)null))
			{
				int current = emoteRig.Current;
				if (current == 6 || current == 9)
				{
					float num = ((current == 6) ? ConfigManager.GriddySpeed.Value : ConfigManager.PrisyadkaSpeed.Value);
					emoteRig.SpeedOriginal = __instance.movementSpeed;
					__instance.movementSpeed *= num;
					emoteRig.SpeedApplied = __instance.movementSpeed;
					emoteRig.SpeedMultiplier = num;
				}
			}
		}

		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		private static void UndoSpeed(PlayerControllerB __instance)
		{
			if (((NetworkBehaviour)__instance).IsOwner)
			{
				EmoteRig emoteRig = EmoteRig.Of(__instance);
				if (!((Object)(object)emoteRig == (Object)null) && emoteRig.SpeedMultiplier != 0f)
				{
					float movementSpeed = __instance.movementSpeed;
					__instance.movementSpeed = ((movementSpeed == emoteRig.SpeedApplied) ? emoteRig.SpeedOriginal : (movementSpeed / emoteRig.SpeedMultiplier));
					emoteRig.SpeedMultiplier = 0f;
				}
			}
		}

		[HarmonyPatch("Update")]
		[HarmonyFinalizer]
		[HarmonyPriority(800)]
		private static void ResetSpeed(PlayerControllerB __instance)
		{
			UndoSpeed(__instance);
		}

		[HarmonyPatch("CheckConditionsForEmote")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void CheckEmote(PlayerControllerB __instance, ref bool __result)
		{
			EmoteRig emoteRig = EmoteRig.Of(__instance);
			if ((Object)(object)emoteRig == (Object)null)
			{
				return;
			}
			int num = ((emoteRig.Requested != 0) ? emoteRig.Requested : emoteRig.Current);
			bool flag = !__instance.inSpecialInteractAnimation && !__instance.isPlayerDead && !__instance.isJumping && !__instance.isCrouching && !__instance.isClimbingLadder && !__instance.isGrabbingObjectAnimation && !__instance.inTerminalMenu;
			if (num == 6 || num == 9)
			{
				__result = flag && Mathf.Abs(__instance.moveInputVector.x) < 0.3f && !__instance.isSprinting && !__instance.isTypingChat;
			}
			else if (num == 10 || (num == 1010 && !ConfigManager.MoveWhileEmoting.Value))
			{
				__result = flag && !__instance.isWalking;
			}
			else if (ConfigManager.MoveWhileEmoting.Value)
			{
				int num2;
				switch (num)
				{
				case 0:
					num2 = (__instance.performingEmote ? 1 : 0);
					break;
				default:
					num2 = 0;
					break;
				case 1:
				case 2:
					num2 = 1;
					break;
				}
				bool flag2 = (byte)num2 != 0;
				if ((num != 0 || flag2) && (!flag2 || !TooManyEmotesCompat.IsEmoting(__instance)))
				{
					__result = flag && !__instance.isSprinting && (!__instance.isTypingChat || emoteRig.Requested == 1010);
				}
			}
		}

		[HarmonyPatch("PerformEmote")]
		[HarmonyPrefix]
		private static void SetRequest(PlayerControllerB __instance, int emoteID, out float __state)
		{
			__state = __instance.timeSinceStartingEmote;
			if (((NetworkBehaviour)__instance).IsOwner)
			{
				EmoteRig emoteRig = EmoteRig.Of(__instance);
				if ((Object)(object)emoteRig != (Object)null)
				{
					emoteRig.Requested = emoteID;
				}
			}
		}

		[HarmonyPatch("PerformEmote")]
		[HarmonyPostfix]
		private static void CheckVanilla(PlayerControllerB __instance, int emoteID, float __state)
		{
			if ((emoteID == 1 || emoteID == 2) && __instance.timeSinceStartingEmote < __state)
			{
				EmoteControl.OnVanillaStart(__instance);
			}
		}

		[HarmonyPatch("PerformEmote")]
		[HarmonyFinalizer]
		private static void ClearRequest(PlayerControllerB __instance)
		{
			EmoteRig emoteRig = EmoteRig.Of(__instance);
			if ((Object)(object)emoteRig != (Object)null)
			{
				emoteRig.Requested = 0;
			}
		}

		[HarmonyPatch("KillPlayer")]
		[HarmonyPostfix]
		private static void StopOnDeath(PlayerControllerB __instance)
		{
			EmoteRig emoteRig = EmoteRig.Of(__instance);
			if (!((Object)(object)emoteRig == (Object)null) && emoteRig.Current != 0 && ((NetworkBehaviour)__instance).IsOwner && __instance.isPlayerDead)
			{
				EmoteControl.End(__instance, FadeMode.Falling);
				EmoteControl.StopLocal(__instance);
			}
		}

		private static float GetOutput(EmoteRig rig, float layerWeight, float dt)
		{
			float num = Mathf.Clamp01(layerWeight / 0.3f);
			float output = rig.Output;
			switch (rig.Mode)
			{
			case FadeMode.Active:
				return num;
			case FadeMode.Falling:
				output = Mathf.Min(output, num);
				break;
			case FadeMode.Decay:
				output += (0f - output) * Mathf.Min(1f, 10f * dt);
				break;
			default:
				return 0f;
			}
			if (!(output < 0.001f))
			{
				return output;
			}
			return 0f;
		}

		private static void CheckStops(PlayerControllerB player, EmoteRig rig)
		{
			if (rig.Current == 0)
			{
				return;
			}
			if (!player.performingEmote)
			{
				EmoteControl.End(player, FadeMode.Falling);
			}
			else if (player.isPlayerDead || !player.isPlayerControlled)
			{
				EmoteControl.End(player, ((NetworkBehaviour)player).IsOwner ? FadeMode.Falling : FadeMode.Decay);
				if (((NetworkBehaviour)player).IsOwner)
				{
					player.performingEmote = false;
				}
			}
			else if (TooManyEmotesCompat.IsEmoting(player))
			{
				EmoteControl.End(player, FadeMode.Decay);
			}
			else
			{
				rig.Mode = FadeMode.Active;
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class StartOfRoundPatch
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void OnStart(StartOfRound __instance)
		{
			SignCanvases.ClearAll();
			if (AssetsLoader.CanDraw)
			{
				SignPainter.Create(__instance);
			}
		}

		[HarmonyPatch("OnPlayerConnectedClientRpc")]
		[HarmonyPostfix]
		private static void OnConnect()
		{
			DrawSync.OnJoin();
		}

		[HarmonyPatch("OnPlayerDC")]
		[HarmonyPostfix]
		private static void ClearPlayer(int playerObjectNumber)
		{
			EndEmote(playerObjectNumber);
			SignCanvases.ClearSlot(playerObjectNumber);
		}

		private static void EndEmote(int index)
		{
			PlayerControllerB player = EmoteControl.GetPlayer(index);
			if (!((Object)(object)player == (Object)null))
			{
				EmoteControl.End(player, FadeMode.Decay);
				player.performingEmote = false;
			}
		}
	}
	[BepInPlugin("com.seeya.muchbetteremotes", "Much Better Emotes", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("com.seeya.muchbetteremotes");

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			EmoteSync.Initialize();
			DrawSync.Initialize();
			if (AssetsLoader.TryLoad())
			{
				InputHandlers.Init();
				ConfigManager.Initialize(((BaseUnityPlugin)this).Config);
				_harmony.PatchAll();
				DrawingStore.Initialize();
				EmoteControl.Started += DrawingStore.OnStarted;
				EmoteControl.Started += SignProp.OnStarted;
				EmoteControl.Started += DrawPanel.OnStarted;
				EmoteControl.Started += HeldItem.OnStarted;
				EmoteControl.Ended += SignProp.OnEnded;
				EmoteControl.Ended += HeldItem.OnEnded;
				EmoteControl.Ended += DrawPanel.OnEnded;
				EmoteControl.Blocked = (PlayerControllerB player) => TooManyEmotesCompat.IsEmoting(player) || LCVRCompat.InVR;
				Log.LogInfo((object)"Much Better Emotes is loaded!");
			}
		}
	}
	internal sealed class Choice
	{
		private const string SureText = "Sure?";

		private readonly TMP_Text _label;

		private readonly GameObject _light;

		private readonly string _text;

		private bool _armed;

		private bool _shown;

		private float _armTime;

		public Button Button { get; }

		public Choice(Transform content, string path, string text)
		{
			Button = ((Component)content.Find(path)).GetComponent<Button>();
			_label = ((Component)content.Find(path + "/Label")).GetComponent<TMP_Text>();
			_light = ((Component)content.Find(path + "/Highlight")).gameObject;
			_text = text;
		}

		public bool Press(float now, bool confirm)
		{
			bool flag = !confirm || IsArmed(now);
			_armed = !flag;
			if (_armed)
			{
				_armTime = now;
			}
			Refresh(now);
			return flag;
		}

		public void Refresh(float now)
		{
			bool flag = IsArmed(now);
			if (flag != _shown)
			{
				_shown = flag;
				_label.text = (flag ? "Sure?" : _text);
				_light.SetActive(flag);
			}
		}

		public void Reset()
		{
			_armed = false;
			_shown = false;
			_label.text = _text;
			_light.SetActive(false);
		}

		private bool IsArmed(float now)
		{
			if (_armed)
			{
				return now - _armTime <= 2f;
			}
			return false;
		}
	}
	internal static class ColorMath
	{
		private const float WheelPressLimit = 1.08f;

		private const float WheelMinValue = 0.05f;

		public static uint ToPixel(int rgb)
		{
			uint num = (uint)((rgb >> 16) & 0xFF);
			uint num2 = (uint)((rgb >> 8) & 0xFF);
			uint num3 = (uint)(rgb & 0xFF);
			return 0xFF000000u | (num3 << 16) | (num2 << 8) | num;
		}

		public static int ToRgb(uint pixel)
		{
			uint num = pixel & 0xFF;
			uint num2 = (pixel >> 8) & 0xFF;
			uint num3 = (pixel >> 16) & 0xFF;
			return (int)((num << 16) | (num2 << 8) | num3);
		}

		public static string ToHex(int rgb)
		{
			return "#" + (rgb & 0xFFFFFF).ToString("X6");
		}

		public static bool TryParseHex(string? text, out int rgb)
		{
			rgb = 0;
			if (text == null)
			{
				return false;
			}
			int num = ((text.Length > 0 && text[0] == '#') ? 1 : 0);
			int num2 = text.Length - num;
			if (num2 != 3 && num2 != 6)
			{
				return false;
			}
			int num3 = 0;
			for (int i = num; i < text.Length; i++)
			{
				int num4 = HexValue(text[i]);
				if (num4 < 0)
				{
					return false;
				}
				num3 = (num3 << 4) | num4;
			}
			if (num2 == 3)
			{
				num3 = ((num3 >> 8) & 0xF) * 1114112 + ((num3 >> 4) & 0xF) * 4352 + (num3 & 0xF) * 17;
			}
			rgb = num3;
			return true;
		}

		public static int FromHsv(float hue, float sat, float val)
		{
			hue -= (float)Math.Floor(hue);
			sat = Math.Clamp(sat, 0f, 1f);
			val = Math.Clamp(val, 0f, 1f);
			float num = hue * 6f;
			int num2 = (int)num % 6;
			float num3 = num - (float)(int)num;
			float num4 = val * (1f - sat);
			float num5 = val * (1f - sat * num3);
			float num6 = val * (1f - sat * (1f - num3));
			float value;
			float value2;
			float value3;
			switch (num2)
			{
			case 0:
				value = val;
				value2 = num6;
				value3 = num4;
				break;
			case 1:
				value = num5;
				value2 = val;
				value3 = num4;
				break;
			case 2:
				value = num4;
				value2 = val;
				value3 = num6;
				break;
			case 3:
				value = num4;
				value2 = num5;
				value3 = val;
				break;
			case 4:
				value = num6;
				value2 = num4;
				value3 = val;
				break;
			default:
				value = val;
				value2 = num4;
				value3 = num5;
				break;
			}
			return (Channel(value) << 16) | (Channel(value2) << 8) | Channel(value3);
		}

		public static void ToHsv(int rgb, ref float hue, ref float sat, out float val)
		{
			float num = (float)((rgb >> 16) & 0xFF) / 255f;
			float num2 = (float)((rgb >> 8) & 0xFF) / 255f;
			float num3 = (float)(rgb & 0xFF) / 255f;
			float num4 = Math.Max(num, Math.Max(num2, num3));
			float num5 = Math.Min(num, Math.Min(num2, num3));
			float num6 = num4 - num5;
			val = num4;
			if (num4 <= 0f || num6 <= 0f)
			{
				if (!(num4 <= 0f))
				{
					sat = 0f;
				}
			}
			else
			{
				sat = num6 / num4;
				float num7 = ((num4 == num) ? ((num2 - num3) / num6) : ((num4 != num2) ? (4f + (num - num2) / num6) : (2f + (num3 - num) / num6)));
				hue = num7 / 6f;
				hue -= (float)Math.Floor(hue);
			}
		}

		public static bool PickWheel(float unitX, float unitY, bool press, ref float hue, ref float sat, ref float val)
		{
			float num = (unitX - 0.5f) * 2f;
			float num2 = (unitY - 0.5f) * 2f;
			float num3 = (float)Math.Sqrt(num * num + num2 * num2);
			if (press && num3 > 1.08f)
			{
				return false;
			}
			if (num3 > 1f)
			{
				num /= num3;
				num2 /= num3;
				num3 = 1f;
			}
			hue = (float)(Math.Atan2(num2, num) / (Math.PI * 2.0));
			hue -= (float)Math.Floor(hue);
			sat = num3;
			if (press && val < 0.05f)
			{
				val = 1f;
			}
			return true;
		}

		public static void WheelPoint(float hue, float sat, out float x, out float y)
		{
			double num = (double)hue * 2.0 * Math.PI;
			x = (float)Math.Cos(num) * sat;
			y = (float)Math.Sin(num) * sat;
		}

		public static char ValidateHex(string text, int index, char letter)
		{
			if (letter == '#')
			{
				if (index != 0 || text.IndexOf('#') >= 0)
				{
					return '\0';
				}
				return letter;
			}
			if (HexValue(letter) < 0)
			{
				return '\0';
			}
			int num = 0;
			for (int i = 0; i < text.Length; i++)
			{
				if (HexValue(text[i]) >= 0)
				{
					num++;
				}
			}
			if (num < 6)
			{
				return char.ToUpperInvariant(letter);
			}
			return '\0';
		}

		private static int Channel(float value)
		{
			return (int)(value * 255f + 0.5f);
		}

		private static int HexValue(char letter)
		{
			if (letter >= '0' && letter <= '9')
			{
				return letter - 48;
			}
			if (letter >= 'a' && letter <= 'f')
			{
				return letter - 97 + 10;
			}
			if (letter >= 'A' && letter <= 'F')
			{
				return letter - 65 + 10;
			}
			return -1;
		}
	}
	internal sealed class DragArea : MonoBehaviour, IPointerDownHandler, IEventSystemHandler, IDragHandler, IInitializePotentialDragHandler
	{
		private RectTransform? _rect;

		public Action<Vector2, bool>? Changed;

		public void OnInitializePotentialDrag(PointerEventData eventData)
		{
			eventData.useDragThreshold = false;
		}

		public void OnPointerDown(PointerEventData eventData)
		{
			Report(eventData, press: true);
		}

		public void OnDrag(PointerEventData eventData)
		{
			Report(eventData, press: false);
		}

		private void Report(PointerEventData eventData, bool press)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (Changed != null && (int)eventData.button == 0)
			{
				if (_rect == null)
				{
					_rect = (RectTransform)((Component)this).transform;
				}
				Vector2 val = default(Vector2);
				if (RectTransformUtility.ScreenPointToLocalPointInRectangle(_rect, eventData.position, eventData.pressEventCamera, ref val))
				{
					Changed(Rect.PointToNormalized(_rect.rect, val), press);
				}
			}
		}
	}
	internal sealed class DrawBook
	{
		private readonly int _capacity;

		private DrawLog?[] _logs = new DrawLog[8];

		private DrawLog _scratch;

		public DrawBook(int capacity)
		{
			_capacity = capacity;
			_scratch = new DrawLog(capacity);
		}

		public DrawLog Get(int player)
		{
			if (player >= _logs.Length)
			{
				Array.Resize(ref _logs, Math.Max(player + 1, _logs.Length * 2));
			}
			ref DrawLog reference = ref _logs[player];
			return reference ?? (reference = new DrawLog(_capacity));
		}

		public bool TryApply(StrokeData data, int playerCount, out int firstNew)
		{
			firstNew = 0;
			if (data.Player < 0 || data.Player >= playerCount)
			{
				return false;
			}
			DrawLog drawLog = Get(data.Player);
			firstNew = drawLog.PointCount;
			if (!data.IsValid(drawLog))
			{
				return false;
			}
			_scratch.CopyFrom(drawLog);
			if (!Append(data, _scratch))
			{
				return false;
			}
			_logs[data.Player] = _scratch;
			_scratch = drawLog;
			return true;
		}

		public bool TryUndo(int player, int playerCount)
		{
			if (player < 0 || player >= playerCount)
			{
				return false;
			}
			DrawLog drawLog = Get(player);
			bool result = drawLog.StrokeCount > 0;
			drawLog.Undo();
			return result;
		}

		public bool TryClear(int player, int playerCount)
		{
			if (player < 0 || player >= playerCount)
			{
				return false;
			}
			DrawLog drawLog = Get(player);
			bool result = drawLog.PointCount > 0;
			drawLog.Clear();
			return result;
		}

		public void ClearSlot(int player)
		{
			if (player >= 0 && player < _logs.Length)
			{
				_logs[player]?.Clear();
			}
		}

		public void ClearAll()
		{
			for (int i = 0; i < _logs.Length; i++)
			{
				_logs[i]?.Clear();
			}
		}

		private static bool Append(StrokeData data, DrawLog log)
		{
			int num = 0;
			for (int i = 0; i < data.X.Length; i++)
			{
				bool flag = num < data.Starts.Length && data.Starts[num] == i;
				if (!(flag ? log.Begin(data.Colors[num], data.Sizes[num], data.X[i], data.Y[i]) : log.Add(data.X[i], data.Y[i])))
				{
					return false;
				}
				if (flag)
				{
					num++;
				}
			}
			return true;
		}
	}
	[Serializable]
	internal sealed class DrawingData
	{
		public enum Result
		{
			Ok,
			Newer,
			Bad
		}

		public const int Current = 1;

		public const int MaxDepth = 4;

		public int Version = 1;

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

		public float[] Radii = Array.Empty<float>();

		public bool[] Erase = Array.Empty<bool>();

		public int[] Counts = Array.Empty<int>();

		public int[] X = Array.Empty<int>();

		public int[] Y = Array.Empty<int>();

		public string PenColor = "";

		public float PenRadius;

		public float EraserRadius;

		public static DrawingData From(DrawLog log, Pen pen)
		{
			int strokeCount = log.StrokeCount;
			DrawingData drawingData = new DrawingData();
			drawingData.Colors = new string[strokeCount];
			drawingData.Radii = new float[strokeCount];
			drawingData.Erase = new bool[strokeCount];
			drawingData.Counts = new int[strokeCount];
			drawingData.X = new int[log.PointCount];
			drawingData.Y = new int[log.PointCount];
			drawingData.PenColor = ColorMath.ToHex(pen.Color);
			drawingData.PenRadius = (float)pen.Size / 2f;
			drawingData.EraserRadius = (float)pen.EraseSize / 2f;
			DrawingData drawingData2 = drawingData;
			int num = 0;
			for (int i = 0; i < strokeCount; i++)
			{
				Stroke stroke = log.GetStroke(i);
				drawingData2.Colors[i] = ColorMath.ToHex((stroke.Color >= 0) ? stroke.Color : 0);
				drawingData2.Radii[i] = (float)stroke.Size / 2f;
				drawingData2.Erase[i] = stroke.Color < 0;
				drawingData2.Counts[i] = stroke.X.Length;
				for (int j = 0; j < stroke.X.Length; j++)
				{
					drawingData2.X[num] = stroke.X[j];
					drawingData2.Y[num] = stroke.Y[j];
					num++;
				}
			}
			return drawingData2;
		}

		public static int Hash(DrawLog log, Pen pen)
		{
			int num = -2128831035;
			num = (num ^ pen.Color) * 16777619;
			num = (num ^ pen.Size) * 16777619;
			num = (num ^ pen.EraseSize) * 16777619;
			num = (num ^ log.StrokeCount) * 16777619;
			for (int i = 0; i < log.StrokeCount; i++)
			{
				num = (num ^ log.StrokeColor(i)) * 16777619;
				num = (num ^ log.StrokeSize(i)) * 16777619;
				num = (num ^ log.StrokeStart(i)) * 16777619;
			}
			for (int j = 0; j < log.PointCount; j++)
			{
				num = (num ^ log.X(j)) * 16777619;
				num = (num ^ log.Y(j)) * 16777619;
			}
			return num;
		}

		public Result CopyTo(DrawLog target)
		{
			if (Version > 1)
			{
				return Result.Newer;
			}
			if (Version < 1 || Colors == null || Radii == null || Erase == null || Counts == null || X == null || Y == null)
			{
				return Result.Bad;
			}
			int num = Counts.Length;
			if (Colors.Length != num || Radii.Length != num || Erase.Length != num)
			{
				return Result.Bad;
			}
			long num2 = 0L;
			int[] counts = Counts;
			foreach (int num3 in counts)
			{
				if (num3 < 1)
				{
					return Result.Bad;
				}
				num2 += num3;
			}
			if (X.Length != num2 || Y.Length != num2)
			{
				return Result.Bad;
			}
			DrawLog drawLog = new DrawLog(target.Capacity);
			int num4 = 0;
			for (int j = 0; j < num; j++)
			{
				if (!Replay(j, num4, drawLog))
				{
					return Result.Bad;
				}
				num4 += Counts[j];
			}
			target.CopyFrom(drawLog);
			return Result.Ok;
		}

		public Result Load(DrawLog target, Pen? pen)
		{
			Result result = CopyTo(target);
			if (result == Result.Ok && pen != null)
			{
				ApplyPen(pen);
			}
			return result;
		}

		public void ApplyPen(Pen pen)
		{
			if (ColorMath.TryParseHex(PenColor, out var rgb) && DrawLog.IsValidStroke(rgb, 2))
			{
				pen.Color = rgb;
			}
			if (TryGetSize(PenRadius, out var size) && size >= 2 && size <= 32)
			{
				pen.Size = size;
			}
			if (TryGetSize(EraserRadius, out var size2) && size2 >= 2 && size2 <= 32)
			{
				pen.EraseSize = size2;
			}
		}

		public static bool IsTooDeep(string json)
		{
			int num = 0;
			bool flag = false;
			bool flag2 = false;
			foreach (char c in json)
			{
				if (flag)
				{
					if (flag2)
					{
						flag2 = false;
						continue;
					}
					switch (c)
					{
					case '\\':
						flag2 = true;
						break;
					case '"':
						flag = false;
						break;
					}
					continue;
				}
				switch (c)
				{
				case '"':
					flag = true;
					break;
				case '[':
				case '{':
					num++;
					if (num > 4)
					{
						return true;
					}
					break;
				case ']':
				case '}':
					if (num > 0)
					{
						num--;
					}
					break;
				}
			}
			return false;
		}

		private static bool TryGetSize(float radius, out int size)
		{
			size = 0;
			if (float.IsNaN(radius) || float.IsInfinity(radius) || radius < 0f || radius > 127.5f)
			{
				return false;
			}
			size = (int)Math.Round(radius * 2f);
			return DrawLog.IsValidStroke(0, size);
		}

		private bool Replay(int stroke, int first, DrawLog log)
		{
			if (!TryGetSize(Radii[stroke], out var size))
			{
				return false;
			}
			int rgb = -1;
			if (!Erase[stroke] && !ColorMath.TryParseHex(Colors[stroke], out rgb))
			{
				return false;
			}
			Stroke stroke2 = new Stroke(rgb, size, Counts[stroke]);
			for (int i = 0; i < stroke2.X.Length; i++)
			{
				int num = X[first + i];
				int num2 = Y[first + i];
				if (num < 0 || num > 65535 || num2 < 0 || num2 > 65535)
				{
					return false;
				}
				stroke2.X[i] = (ushort)num;
				stroke2.Y[i] = (ushort)num2;
			}
			if (log.AddStroke(stroke2))
			{
				if (DrawLog.IsText(size))
				{
					return !log.Open;
				}
				return true;
			}
			return false;
		}
	}
	internal static class DrawingFiles
	{
		public enum Status
		{
			Empty,
			Ok,
			Newer,
			Damaged
		}

		private const long SaveLimit = 104857