Decompiled source of AfterlifeArcade v0.3.0

plugins/AfterlifeArcade/AfterlifeArcade.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using AfterlifeArcade.Core;
using AfterlifeArcade.Core.Attribution;
using AfterlifeArcade.Core.Bets;
using AfterlifeArcade.Core.Bingo;
using AfterlifeArcade.Core.Client;
using AfterlifeArcade.Core.Economy;
using AfterlifeArcade.Core.Events;
using AfterlifeArcade.Core.Host;
using AfterlifeArcade.Core.Lobby;
using AfterlifeArcade.Core.Net;
using AfterlifeArcade.Core.Qte;
using AfterlifeArcade.Core.Side;
using AfterlifeArcade.Core.TileFall;
using AfterlifeArcade.Events;
using AfterlifeArcade.Input;
using AfterlifeArcade.Net;
using AfterlifeArcade.Patches;
using AfterlifeArcade.Session;
using AfterlifeArcade.UI;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Lc.Common;
using LethalCompanyInputUtils.Api;
using LethalNetworkAPI;
using LethalNetworkAPI.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("LethalCompanyInputUtils")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("thatumi")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0+982c64412eca66d42f5f16530caf0c5b3cd438ec")]
[assembly: AssemblyProduct("AfterlifeArcade")]
[assembly: AssemblyTitle("AfterlifeArcade")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.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 Lc.Common
{
	internal static class FontProvider
	{
		private static TMP_FontAsset? _hudFont;

		internal static TMP_FontAsset? Get()
		{
			if ((Object)(object)_hudFont != (Object)null)
			{
				return _hudFont;
			}
			TMP_FontAsset val = ReflectionGuard.Run("FontProvider.HUD", delegate
			{
				HUDManager instance = HUDManager.Instance;
				return (!((Object)(object)instance != (Object)null) || !((Object)(object)instance.chatText != (Object)null)) ? null : ((TMP_Text)instance.chatText).font;
			}, null);
			if ((Object)(object)val != (Object)null)
			{
				_hudFont = val;
				return val;
			}
			TMP_FontAsset val2 = ReflectionGuard.Run("FontProvider.Default", () => TMP_Settings.defaultFontAsset, null);
			if ((Object)(object)val2 != (Object)null)
			{
				return val2;
			}
			TMP_FontAsset? obj = ReflectionGuard.Run("FontProvider.Any", () => Resources.FindObjectsOfTypeAll<TMP_FontAsset>().FirstOrDefault(), null);
			if ((Object)(object)obj == (Object)null)
			{
				Log.WarnOnce("FontProvider.none", "FontProvider: no TMP font available yet; text will use TMP defaults");
			}
			return obj;
		}

		internal static void Invalidate()
		{
			_hudFont = null;
		}
	}
	internal enum LogKind
	{
		Debug,
		Info,
		Warning,
		Error
	}
	internal static class Log
	{
		private static readonly object Gate = new object();

		private static readonly HashSet<string> OnceKeys = new HashSet<string>(StringComparer.Ordinal);

		private static Action<LogKind, string>? _sink;

		internal static bool DebugEnabled { get; set; }

		internal static void SetSink(Action<LogKind, string>? sink)
		{
			_sink = sink;
		}

		internal static void Debug(string message)
		{
			if (DebugEnabled)
			{
				Write(LogKind.Debug, message);
			}
		}

		internal static void Info(string message)
		{
			Write(LogKind.Info, message);
		}

		internal static void Warn(string message)
		{
			Write(LogKind.Warning, message);
		}

		internal static void Error(string message)
		{
			Write(LogKind.Error, message);
		}

		internal static bool WarnOnce(string key, string message)
		{
			if (!MarkOnce(key))
			{
				return false;
			}
			Write(LogKind.Warning, message);
			return true;
		}

		internal static bool ErrorOnce(string key, string message)
		{
			if (!MarkOnce(key))
			{
				return false;
			}
			Write(LogKind.Error, message);
			return true;
		}

		internal static bool MarkOnce(string key)
		{
			lock (Gate)
			{
				return OnceKeys.Add(key);
			}
		}

		internal static void ResetOnce()
		{
			lock (Gate)
			{
				OnceKeys.Clear();
			}
		}

		private static void Write(LogKind kind, string message)
		{
			Action<LogKind, string> sink = _sink;
			try
			{
				if (sink != null)
				{
					sink(kind, message);
				}
				else
				{
					Console.WriteLine("[" + kind.ToString() + "] " + message);
				}
			}
			catch
			{
			}
		}
	}
	internal static class LogBepInEx
	{
		internal static void Init(ManualLogSource source)
		{
			Log.SetSink(delegate(LogKind kind, string message)
			{
				switch (kind)
				{
				case LogKind.Debug:
					source.LogDebug((object)message);
					break;
				case LogKind.Info:
					source.LogInfo((object)message);
					break;
				case LogKind.Warning:
					source.LogWarning((object)message);
					break;
				default:
					source.LogError((object)message);
					break;
				}
			});
		}
	}
	internal static class ReflectionGuard
	{
		internal const BindingFlags AllMembers = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private const BindingFlags Declared = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly object Gate = new object();

		private static readonly Dictionary<string, string> DisabledReasons = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, int> FailureCounts = new Dictionary<string, int>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Type?> TypeCache = new Dictionary<string, Type>(StringComparer.Ordinal);

		internal static int MaxFailuresBeforeDisable { get; set; } = 25;

		internal static IReadOnlyDictionary<string, string> DisabledFeatures
		{
			get
			{
				lock (Gate)
				{
					return new Dictionary<string, string>(DisabledReasons, StringComparer.Ordinal);
				}
			}
		}

		internal static event Action<string, string>? FeatureDisabled;

		internal static bool IsEnabled(string feature)
		{
			lock (Gate)
			{
				return !DisabledReasons.ContainsKey(feature);
			}
		}

		internal static void Disable(string feature, string reason)
		{
			lock (Gate)
			{
				if (DisabledReasons.ContainsKey(feature))
				{
					return;
				}
				DisabledReasons[feature] = reason;
			}
			Log.Warn("[" + feature + "] disabled: " + reason);
			try
			{
				ReflectionGuard.FeatureDisabled?.Invoke(feature, reason);
			}
			catch
			{
			}
		}

		internal static void Reset()
		{
			lock (Gate)
			{
				DisabledReasons.Clear();
				FailureCounts.Clear();
				TypeCache.Clear();
			}
		}

		internal static Type? FindType(string fullName, string feature, string? assemblyName = null)
		{
			string key = (assemblyName ?? "*") + "|" + fullName;
			lock (Gate)
			{
				if (TypeCache.TryGetValue(key, out Type value) && value != null)
				{
					return value;
				}
			}
			Type type = null;
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					if (assemblyName == null || string.Equals(assembly.GetName().Name, assemblyName, StringComparison.Ordinal))
					{
						type = assembly.GetType(fullName, throwOnError: false);
						if (type != null)
						{
							break;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Disable(feature, "type lookup " + fullName + " threw " + ex.GetType().Name + ": " + ex.Message);
				return null;
			}
			if (type != null)
			{
				lock (Gate)
				{
					TypeCache[key] = type;
				}
			}
			else
			{
				Disable(feature, "type " + fullName + ((assemblyName != null) ? (" (in " + assemblyName + ")") : "") + " not found");
			}
			return type;
		}

		internal static FieldInfo? Field(Type? type, string name, string feature)
		{
			if (type == null)
			{
				Disable(feature, "field " + name + ": owner type is null");
				return null;
			}
			Type type2 = type;
			while (type2 != null)
			{
				FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field;
				}
				type2 = type2.BaseType;
			}
			Disable(feature, "field " + type.FullName + "." + name + " not found");
			return null;
		}

		internal static MethodInfo? Method(Type? type, string name, Type[]? parameterTypes, string feature)
		{
			if (type == null)
			{
				Disable(feature, "method " + name + ": owner type is null");
				return null;
			}
			Type type2 = type;
			while (type2 != null)
			{
				List<MethodInfo> list = (from m in type2.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
					where m.Name == name
					select m).ToList();
				if (list.Count != 0)
				{
					if (parameterTypes == null)
					{
						if (list.Count == 1)
						{
							return list[0];
						}
						Disable(feature, "method " + type2.FullName + "." + name + " is ambiguous (" + list.Count + " overloads); pass parameter types");
						return null;
					}
					foreach (MethodInfo item in list)
					{
						if (ParametersMatch(item.GetParameters(), parameterTypes))
						{
							return item;
						}
					}
				}
				type2 = type2.BaseType;
			}
			Disable(feature, "method " + type.FullName + "." + name + Signature(parameterTypes) + " not found");
			return null;
		}

		internal static PropertyInfo? Property(Type? type, string name, string feature)
		{
			if (type == null)
			{
				Disable(feature, "property " + name + ": owner type is null");
				return null;
			}
			Type type2 = type;
			while (type2 != null)
			{
				PropertyInfo property = type2.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					return property;
				}
				type2 = type2.BaseType;
			}
			Disable(feature, "property " + type.FullName + "." + name + " not found");
			return null;
		}

		internal static bool TryGet<T>(FieldInfo? field, object? instance, out T value, string feature)
		{
			value = default(T);
			if (field == null || !IsEnabled(feature))
			{
				return false;
			}
			try
			{
				object value2 = field.GetValue(field.IsStatic ? null : instance);
				if (value2 is T val)
				{
					value = val;
					return true;
				}
				if (value2 == null && default(T) == null)
				{
					return true;
				}
				Log.WarnOnce(feature + "|type|" + field.Name, "[" + feature + "] field " + field.Name + " is " + (value2?.GetType().FullName ?? "null") + ", expected " + typeof(T).FullName);
				return false;
			}
			catch (Exception e)
			{
				CountFailure(feature, "get " + field.Name, e);
				return false;
			}
		}

		internal static bool TryGet<T>(PropertyInfo? property, object? instance, out T value, string feature)
		{
			value = default(T);
			MethodInfo methodInfo = property?.GetGetMethod(nonPublic: true);
			if (property == null || methodInfo == null || !IsEnabled(feature))
			{
				return false;
			}
			try
			{
				object value2 = property.GetValue(methodInfo.IsStatic ? null : instance, null);
				if (value2 is T val)
				{
					value = val;
					return true;
				}
				if (value2 == null && default(T) == null)
				{
					return true;
				}
				Log.WarnOnce(feature + "|type|" + property.Name, "[" + feature + "] property " + property.Name + " is " + (value2?.GetType().FullName ?? "null") + ", expected " + typeof(T).FullName);
				return false;
			}
			catch (Exception e)
			{
				CountFailure(feature, "get " + property.Name, e);
				return false;
			}
		}

		internal static bool TrySet(FieldInfo? field, object? instance, object? value, string feature)
		{
			if (field == null || !IsEnabled(feature))
			{
				return false;
			}
			try
			{
				field.SetValue(field.IsStatic ? null : instance, value);
				return true;
			}
			catch (Exception e)
			{
				CountFailure(feature, "set " + field.Name, e);
				return false;
			}
		}

		internal static bool TryInvoke(MethodInfo? method, object? instance, object?[]? args, out object? result, string feature)
		{
			result = null;
			if (method == null || !IsEnabled(feature))
			{
				return false;
			}
			try
			{
				result = method.Invoke(method.IsStatic ? null : instance, args);
				return true;
			}
			catch (TargetInvocationException ex) when (ex.InnerException != null)
			{
				CountFailure(feature, "invoke " + method.Name, ex.InnerException);
				return false;
			}
			catch (Exception e)
			{
				CountFailure(feature, "invoke " + method.Name, e);
				return false;
			}
		}

		internal static void Run(string key, Action body)
		{
			if (!IsEnabled(key))
			{
				return;
			}
			try
			{
				body();
			}
			catch (Exception e)
			{
				CountFailure(key, "threw", e);
			}
		}

		internal static T Run<T>(string key, Func<T> body, T fallback)
		{
			if (!IsEnabled(key))
			{
				return fallback;
			}
			try
			{
				return body();
			}
			catch (Exception e)
			{
				CountFailure(key, "threw", e);
				return fallback;
			}
		}

		internal static bool Prefix(string key, Func<bool> body)
		{
			return Run(key, body, fallback: true);
		}

		internal static int FailureCount(string key)
		{
			lock (Gate)
			{
				int value;
				return FailureCounts.TryGetValue(key, out value) ? value : 0;
			}
		}

		private static void CountFailure(string key, string what, Exception e)
		{
			int value;
			lock (Gate)
			{
				FailureCounts.TryGetValue(key, out value);
				value++;
				FailureCounts[key] = value;
			}
			if (value == 1)
			{
				Log.Error("[" + key + "] " + what + ": " + e);
			}
			if (value >= MaxFailuresBeforeDisable)
			{
				Disable(key, value + " exceptions (first logged above)");
			}
		}

		private static bool ParametersMatch(ParameterInfo[] actual, Type[] expected)
		{
			if (actual.Length != expected.Length)
			{
				return false;
			}
			for (int i = 0; i < actual.Length; i++)
			{
				if (actual[i].ParameterType != expected[i])
				{
					return false;
				}
			}
			return true;
		}

		private static string Signature(Type[]? parameterTypes)
		{
			if (parameterTypes != null)
			{
				return "(" + string.Join(", ", parameterTypes.Select((Type t) => t.Name)) + ")";
			}
			return "";
		}
	}
	internal static class TexGen
	{
		private delegate float CoverageFn(float x, float y);

		private static readonly Dictionary<string, Sprite> Cache = new Dictionary<string, Sprite>();

		internal static Sprite RoundedRect(int radius = 12, int size = 0)
		{
			if (size <= 0)
			{
				size = radius * 2 + 4;
			}
			string key = "rr|" + radius + "|" + size;
			return Get(key, delegate
			{
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0063: 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)
				Texture2D obj = NewTexture(size, size, key);
				Paint(obj, (float x, float y) => TexGenMath.FillCoverage(TexGenMath.RoundedRectDistance(x, y, size, size, radius)));
				return Sprite.Create(obj, new Rect(0f, 0f, (float)size, (float)size), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4((float)(radius + 1), (float)(radius + 1), (float)(radius + 1), (float)(radius + 1)));
			});
		}

		internal static Sprite Outline(int radius = 12, int thickness = 2, int size = 0)
		{
			if (size <= 0)
			{
				size = radius * 2 + 4;
			}
			string key = "ol|" + radius + "|" + thickness + "|" + size;
			return Get(key, delegate
			{
				//IL_0068: Unknown result type (might be due to invalid IL or missing references)
				//IL_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				Texture2D obj = NewTexture(size, size, key);
				Paint(obj, (float x, float y) => TexGenMath.StrokeCoverage(TexGenMath.RoundedRectDistance(x, y, size, size, radius), thickness));
				int num = Mathf.Max(radius, thickness) + 1;
				return Sprite.Create(obj, new Rect(0f, 0f, (float)size, (float)size), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4((float)num, (float)num, (float)num, (float)num));
			});
		}

		internal static Sprite Circle(int diameter = 64, int thickness = 0)
		{
			string key = "ci|" + diameter + "|" + thickness;
			return Get(key, delegate
			{
				//IL_0061: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				Texture2D obj = NewTexture(diameter, diameter, key);
				float r = (float)diameter * 0.5f;
				Paint(obj, delegate(float x, float y)
				{
					float distance = TexGenMath.CircleDistance(x, y, r, r, r - 0.5f);
					return (thickness <= 0) ? TexGenMath.FillCoverage(distance) : TexGenMath.StrokeCoverage(distance, thickness);
				});
				return Sprite.Create(obj, new Rect(0f, 0f, (float)diameter, (float)diameter), new Vector2(0.5f, 0.5f), 100f);
			});
		}

		internal static Sprite White()
		{
			return Get("white", delegate
			{
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				Texture2D obj = NewTexture(1, 1, "white");
				obj.SetPixel(0, 0, Color.white);
				obj.Apply(false, false);
				return Sprite.Create(obj, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 100f);
			});
		}

		private static Sprite Get(string key, Func<Sprite> create)
		{
			if (Cache.TryGetValue(key, out Sprite value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = create();
			((Object)value).name = "LcTexGen_" + key;
			((Object)value).hideFlags = (HideFlags)52;
			Cache[key] = value;
			return value;
		}

		private static Texture2D NewTexture(int w, int h, string key)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			return new Texture2D(w, h, (TextureFormat)4, false)
			{
				name = "LcTexGen_" + key,
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				hideFlags = (HideFlags)52
			};
		}

		private static void Paint(Texture2D tex, CoverageFn coverage)
		{
			//IL_005d: 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)
			int width = ((Texture)tex).width;
			int height = ((Texture)tex).height;
			Color32[] array = (Color32[])(object)new Color32[width * height];
			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					float num = coverage((float)j + 0.5f, (float)i + 0.5f);
					array[i * width + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)Mathf.RoundToInt(num * 255f));
				}
			}
			tex.SetPixels32(array);
			tex.Apply(false, false);
		}
	}
	internal static class TexGenMath
	{
		internal static float RoundedRectDistance(float px, float py, float w, float h, float r)
		{
			r = Math.Max(0f, Math.Min(r, Math.Min(w, h) * 0.5f));
			float num = w * 0.5f;
			float num2 = h * 0.5f;
			float val = Math.Abs(px - num) - (num - r);
			float num3 = Math.Abs(py - num2) - (num2 - r);
			float num4 = Math.Max(val, 0f);
			float num5 = Math.Max(num3, 0f);
			float num6 = (float)Math.Sqrt(num4 * num4 + num5 * num5);
			float num7 = Math.Min(Math.Max(val, num3), 0f);
			return num6 + num7 - r;
		}

		internal static float CircleDistance(float px, float py, float cx, float cy, float r)
		{
			float num = px - cx;
			float num2 = py - cy;
			return (float)Math.Sqrt(num * num + num2 * num2) - r;
		}

		internal static float FillCoverage(float distance)
		{
			return Clamp01(0.5f - distance);
		}

		internal static float StrokeCoverage(float distance, float thickness)
		{
			float num = FillCoverage(distance);
			float num2 = FillCoverage(distance + thickness);
			return Clamp01(num - num2);
		}

		internal static float Clamp01(float v)
		{
			if (!(v < 0f))
			{
				if (!(v > 1f))
				{
					return v;
				}
				return 1f;
			}
			return 0f;
		}
	}
	internal static class UiFactory
	{
		internal static class Theme
		{
			internal static Color PanelBg = new Color(0.05f, 0.05f, 0.06f, 0.92f);

			internal static Color PanelBorder = new Color(1f, 0.45f, 0.1f, 0.9f);

			internal static Color Text = new Color(0.98f, 0.62f, 0.25f, 1f);

			internal static Color TextDim = new Color(0.75f, 0.6f, 0.45f, 1f);

			internal static Color ButtonBg = new Color(0.16f, 0.09f, 0.05f, 0.95f);

			internal static Color ButtonHighlight = new Color(0.45f, 0.22f, 0.07f, 1f);

			internal static Color ButtonPressed = new Color(0.7f, 0.33f, 0.08f, 1f);

			internal static Color ButtonDisabled = new Color(0.12f, 0.12f, 0.12f, 0.7f);

			internal static Color Danger = new Color(0.9f, 0.18f, 0.15f, 1f);

			internal static Color BackdropColor = new Color(0f, 0f, 0f, 0.72f);

			internal static int CornerRadius = 10;

			internal static float FontSize = 26f;
		}

		internal static readonly Vector2 ReferenceResolution = new Vector2(1920f, 1080f);

		internal static Canvas Canvas(string name, int sortingOrder, bool dontDestroyOnLoad = false)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.layer = 5;
			if (dontDestroyOnLoad)
			{
				Object.DontDestroyOnLoad((Object)(object)val);
			}
			Canvas obj = val.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = sortingOrder;
			obj.pixelPerfect = false;
			CanvasScaler obj2 = val.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = ReferenceResolution;
			obj2.screenMatchMode = (ScreenMatchMode)0;
			obj2.matchWidthOrHeight = 0.5f;
			val.AddComponent<GraphicRaycaster>();
			return obj;
		}

		internal static RectTransform Rect(Transform parent, string name)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				layer = 5
			}.transform;
			((Transform)val).SetParent(parent, false);
			return val;
		}

		internal static RectTransform Fill(RectTransform rt, float padding = 0f)
		{
			//IL_0001: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = Vector2.zero;
			rt.anchorMax = Vector2.one;
			rt.pivot = new Vector2(0.5f, 0.5f);
			rt.offsetMin = new Vector2(padding, padding);
			rt.offsetMax = new Vector2(0f - padding, 0f - padding);
			return rt;
		}

		internal static RectTransform Place(RectTransform rt, Vector2 anchor, Vector2 offset, Vector2 size, Vector2? pivot = null)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_001d: 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)
			rt.anchorMin = anchor;
			rt.anchorMax = anchor;
			rt.pivot = pivot ?? anchor;
			rt.anchoredPosition = offset;
			rt.sizeDelta = size;
			return rt;
		}

		internal static RectTransform PlaceTL(RectTransform rt, float cx, float cy, float w, float h)
		{
			//IL_000b: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = new Vector2(0f, 1f);
			rt.anchorMax = new Vector2(0f, 1f);
			rt.pivot = new Vector2(0.5f, 0.5f);
			rt.anchoredPosition = new Vector2(cx, 0f - cy);
			rt.sizeDelta = new Vector2(w, h);
			return rt;
		}

		internal static LayoutElement Size(Component c, float width = -1f, float height = -1f, float flexibleWidth = -1f)
		{
			LayoutElement val = c.GetComponent<LayoutElement>();
			if ((Object)(object)val == (Object)null)
			{
				val = c.gameObject.AddComponent<LayoutElement>();
			}
			val.preferredWidth = width;
			val.preferredHeight = height;
			val.minHeight = height;
			val.flexibleWidth = flexibleWidth;
			return val;
		}

		internal static VerticalLayoutGroup Vertical(Component c, float spacing = 8f, int padding = 12)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			VerticalLayoutGroup obj = c.gameObject.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = spacing;
			((LayoutGroup)obj).padding = new RectOffset(padding, padding, padding, padding);
			((LayoutGroup)obj).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			return obj;
		}

		internal static HorizontalLayoutGroup Horizontal(Component c, float spacing = 8f, int padding = 0)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			HorizontalLayoutGroup obj = c.gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = spacing;
			((LayoutGroup)obj).padding = new RectOffset(padding, padding, padding, padding);
			((LayoutGroup)obj).childAlignment = (TextAnchor)3;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			return obj;
		}

		internal static Image Panel(Transform parent, string name, Color? color = null, bool border = true)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Fill(Rect(parent, name));
			Image obj = ((Component)val).gameObject.AddComponent<Image>();
			obj.sprite = TexGen.RoundedRect(Theme.CornerRadius);
			obj.type = (Type)1;
			((Graphic)obj).color = (Color)(((??)color) ?? Theme.PanelBg);
			((Graphic)obj).raycastTarget = true;
			if (border)
			{
				Image obj2 = ((Component)Fill(Rect((Transform)(object)val, "Border"))).gameObject.AddComponent<Image>();
				obj2.sprite = TexGen.Outline(Theme.CornerRadius);
				obj2.type = (Type)1;
				((Graphic)obj2).color = Theme.PanelBorder;
				((Graphic)obj2).raycastTarget = false;
			}
			return obj;
		}

		internal static TextMeshProUGUI Text(Transform parent, string text, float size = 0f, TextAlignmentOptions align = (TextAlignmentOptions)513, Color? color = null, string name = "Text")
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI val = ((Component)Rect(parent, name)).gameObject.AddComponent<TextMeshProUGUI>();
			TMP_FontAsset val2 = FontProvider.Get();
			if ((Object)(object)val2 != (Object)null)
			{
				((TMP_Text)val).font = val2;
			}
			((TMP_Text)val).text = text;
			((TMP_Text)val).fontSize = ((size > 0f) ? size : Theme.FontSize);
			((TMP_Text)val).alignment = align;
			((Graphic)val).color = (Color)(((??)color) ?? Theme.Text);
			((TMP_Text)val).enableWordWrapping = true;
			((TMP_Text)val).overflowMode = (TextOverflowModes)1;
			((Graphic)val).raycastTarget = false;
			return val;
		}

		internal static Button Button(Transform parent, string label, Action onClick, Vector2? size = null, string? name = null)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: 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)
			RectTransform obj = Rect(parent, name ?? ("Button_" + label));
			obj.sizeDelta = (Vector2)(((??)size) ?? new Vector2(260f, 52f));
			Image val = ((Component)obj).gameObject.AddComponent<Image>();
			val.sprite = TexGen.RoundedRect(Theme.CornerRadius);
			val.type = (Type)1;
			((Graphic)val).color = Color.white;
			Button val2 = ((Component)obj).gameObject.AddComponent<Button>();
			((Selectable)val2).targetGraphic = (Graphic)(object)val;
			((Selectable)val2).colors = Colors();
			((UnityEvent)val2.onClick).AddListener(Guarded("Button " + label, onClick));
			Fill(((TMP_Text)Text((Transform)(object)obj, label, 0f, (TextAlignmentOptions)514, null, "Label")).rectTransform, 6f);
			if (size.HasValue)
			{
				Size((Component)(object)val2, size.Value.x, size.Value.y);
			}
			return val2;
		}

		internal static Button CycleButton(Transform parent, Func<string> label, Action onClick, Vector2? size = null, string? name = null)
		{
			TextMeshProUGUI text = null;
			Button val = Button(parent, label(), delegate
			{
				onClick();
				if ((Object)(object)text != (Object)null)
				{
					((TMP_Text)text).text = label();
				}
			}, size, name);
			text = ((Component)val).GetComponentInChildren<TextMeshProUGUI>(true);
			return val;
		}

		internal static void SetLabel(Button button, string label)
		{
			TextMeshProUGUI componentInChildren = ((Component)button).GetComponentInChildren<TextMeshProUGUI>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				((TMP_Text)componentInChildren).text = label;
			}
		}

		internal static Image Picture(Transform parent, string name, Sprite? sprite, Color color, bool raycast = false)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)Rect(parent, name)).gameObject.AddComponent<Image>();
			obj.sprite = (Sprite)(((Object)(object)sprite != (Object)null) ? ((object)sprite) : ((object)TexGen.White()));
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = raycast;
			obj.preserveAspect = (Object)(object)sprite != (Object)null;
			return obj;
		}

		internal static Image Line(RectTransform parent, Vector2 from, Vector2 to, float thickness, Color color)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			Image obj = Picture((Transform)(object)parent, "Line", null, color);
			UiMath.Line(from.x, from.y, to.x, to.y, out var centreX, out var centreY, out var length, out var angleDeg);
			PlaceTL(((Graphic)obj).rectTransform, centreX, centreY, length, thickness);
			((Transform)((Graphic)obj).rectTransform).localRotation = Quaternion.Euler(0f, 0f, angleDeg);
			return obj;
		}

		internal static RectTransform Backdrop(Transform parent, string name = "Backdrop", Color? color = null)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Fill(Rect(parent, name));
			Image obj2 = ((Component)obj).gameObject.AddComponent<Image>();
			obj2.sprite = TexGen.White();
			((Graphic)obj2).color = (Color)(((??)color) ?? Theme.BackdropColor);
			((Graphic)obj2).raycastTarget = true;
			return obj;
		}

		internal static Toggle Toggle(Transform parent, string label, bool value, Action<bool> onChanged, float height = 44f)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect(parent, "Toggle_" + label);
			obj.sizeDelta = new Vector2(400f, height);
			Size((Component)(object)obj, -1f, height, 1f);
			Image val = ((Component)obj).gameObject.AddComponent<Image>();
			val.sprite = TexGen.RoundedRect(Theme.CornerRadius);
			val.type = (Type)1;
			((Graphic)val).color = Color.white;
			RectTransform obj2 = Place(Rect((Transform)(object)obj, "Box"), new Vector2(0f, 0.5f), new Vector2(8f, 0f), new Vector2(height - 12f, height - 12f));
			Image obj3 = ((Component)obj2).gameObject.AddComponent<Image>();
			obj3.sprite = TexGen.Outline(6);
			obj3.type = (Type)1;
			((Graphic)obj3).color = Theme.Text;
			((Graphic)obj3).raycastTarget = false;
			Image val2 = ((Component)Fill(Rect((Transform)(object)obj2, "Check"), 6f)).gameObject.AddComponent<Image>();
			val2.sprite = TexGen.RoundedRect(4);
			val2.type = (Type)1;
			((Graphic)val2).color = Theme.Text;
			((Graphic)val2).raycastTarget = false;
			TextMeshProUGUI obj4 = Text((Transform)(object)obj, label, 0f, (TextAlignmentOptions)513, null, "Label");
			((TMP_Text)obj4).rectTransform.anchorMin = new Vector2(0f, 0f);
			((TMP_Text)obj4).rectTransform.anchorMax = new Vector2(1f, 1f);
			((TMP_Text)obj4).rectTransform.offsetMin = new Vector2(height + 4f, 0f);
			((TMP_Text)obj4).rectTransform.offsetMax = new Vector2(-8f, 0f);
			Toggle obj5 = ((Component)obj).gameObject.AddComponent<Toggle>();
			((Selectable)obj5).targetGraphic = (Graphic)(object)val;
			obj5.graphic = (Graphic)(object)val2;
			((Selectable)obj5).colors = Colors(transparentNormal: true);
			obj5.isOn = value;
			UnityAction<bool> val3 = Guarded("Toggle " + label, onChanged);
			((UnityEvent<bool>)(object)obj5.onValueChanged).AddListener(val3);
			return obj5;
		}

		internal static ScrollRect ScrollList(Transform parent, string name, out RectTransform content, float spacing = 6f)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Fill(Rect(parent, name));
			ScrollRect val = ((Component)obj).gameObject.AddComponent<ScrollRect>();
			val.horizontal = false;
			val.vertical = true;
			val.movementType = (MovementType)2;
			val.scrollSensitivity = 30f;
			RectTransform val2 = Fill(Rect((Transform)(object)obj, "Viewport"));
			((Component)val2).gameObject.AddComponent<RectMask2D>();
			((Graphic)((Component)val2).gameObject.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0f);
			content = Rect((Transform)(object)val2, "Content");
			content.anchorMin = new Vector2(0f, 1f);
			content.anchorMax = new Vector2(1f, 1f);
			content.pivot = new Vector2(0.5f, 1f);
			content.anchoredPosition = Vector2.zero;
			content.sizeDelta = Vector2.zero;
			((LayoutGroup)Vertical((Component)(object)content, spacing, 4)).childAlignment = (TextAnchor)1;
			ContentSizeFitter obj2 = ((Component)content).gameObject.AddComponent<ContentSizeFitter>();
			obj2.verticalFit = (FitMode)2;
			obj2.horizontalFit = (FitMode)0;
			val.viewport = val2;
			val.content = content;
			return val;
		}

		internal static void ScrollIntoView(ScrollRect scroll, RectTransform item)
		{
			ReflectionGuard.Run("UiFactory.ScrollIntoView", delegate
			{
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: 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_005d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_0078: Unknown result type (might be due to invalid IL or missing references)
				//IL_007d: Unknown result type (might be due to invalid IL or missing references)
				//IL_008d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0092: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00be: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
				//IL_0109: Unknown result type (might be due to invalid IL or missing references)
				//IL_0115: Unknown result type (might be due to invalid IL or missing references)
				RectTransform content = scroll.content;
				RectTransform val = (RectTransform)(((Object)(object)scroll.viewport != (Object)null) ? ((object)scroll.viewport) : ((object)(RectTransform)((Component)scroll).transform));
				if (!((Object)(object)content == (Object)null))
				{
					Canvas.ForceUpdateCanvases();
					Rect rect = content.rect;
					float height = ((Rect)(ref rect)).height;
					rect = val.rect;
					float height2 = ((Rect)(ref rect)).height;
					if (!(height <= height2))
					{
						float y = ((Transform)content).InverseTransformPoint(((Transform)item).position).y;
						rect = item.rect;
						float num = 0f - (y + ((Rect)(ref rect)).height * (1f - item.pivot.y));
						rect = item.rect;
						float num2 = num + ((Rect)(ref rect)).height;
						float num3 = content.anchoredPosition.y;
						if (num < num3)
						{
							num3 = num;
						}
						else if (num2 > num3 + height2)
						{
							num3 = num2 - height2;
						}
						num3 = Mathf.Clamp(num3, 0f, height - height2);
						content.anchoredPosition = new Vector2(content.anchoredPosition.x, num3);
					}
				}
			});
		}

		internal static void LinkVertical(IList<Selectable> items, bool wrap = true, Selectable? left = null, Selectable? right = null)
		{
			Link(items, wrap, vertical: true, left, right);
		}

		internal static void LinkHorizontal(IList<Selectable> items, bool wrap = false, Selectable? up = null, Selectable? down = null)
		{
			Link(items, wrap, vertical: false, up, down);
		}

		internal static void Select(Selectable? target)
		{
			ReflectionGuard.Run("UiFactory.Select", delegate
			{
				EventSystem current = EventSystem.current;
				if (!((Object)(object)current == (Object)null) && !((Object)(object)target == (Object)null) && ((Component)target).gameObject.activeInHierarchy)
				{
					current.SetSelectedGameObject((GameObject)null);
					current.SetSelectedGameObject(((Component)target).gameObject);
				}
			});
		}

		internal static void Destroy(Component? root)
		{
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root.gameObject);
			}
		}

		private static void Link(IList<Selectable> items, bool wrap, bool vertical, Selectable? sideA, Selectable? sideB)
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			List<Selectable> list = new List<Selectable>();
			foreach (Selectable item in items)
			{
				if ((Object)(object)item != (Object)null && ((Component)item).gameObject.activeInHierarchy && item.IsInteractable())
				{
					list.Add(item);
				}
			}
			for (int i = 0; i < list.Count; i++)
			{
				Selectable val = ((i > 0) ? list[i - 1] : ((wrap && list.Count > 1) ? list[list.Count - 1] : null));
				Selectable val2 = ((i < list.Count - 1) ? list[i + 1] : ((wrap && list.Count > 1) ? list[0] : null));
				Navigation val3 = default(Navigation);
				((Navigation)(ref val3)).mode = (Mode)4;
				Navigation navigation = val3;
				if (vertical)
				{
					((Navigation)(ref navigation)).selectOnUp = val;
					((Navigation)(ref navigation)).selectOnDown = val2;
					((Navigation)(ref navigation)).selectOnLeft = sideA;
					((Navigation)(ref navigation)).selectOnRight = sideB;
				}
				else
				{
					((Navigation)(ref navigation)).selectOnLeft = val;
					((Navigation)(ref navigation)).selectOnRight = val2;
					((Navigation)(ref navigation)).selectOnUp = sideA;
					((Navigation)(ref navigation)).selectOnDown = sideB;
				}
				list[i].navigation = navigation;
			}
		}

		private static ColorBlock Colors(bool transparentNormal = false)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			ColorBlock defaultColorBlock = ColorBlock.defaultColorBlock;
			((ColorBlock)(ref defaultColorBlock)).normalColor = (Color)(transparentNormal ? new Color(0f, 0f, 0f, 0f) : Theme.ButtonBg);
			((ColorBlock)(ref defaultColorBlock)).highlightedColor = Theme.ButtonHighlight;
			((ColorBlock)(ref defaultColorBlock)).selectedColor = Theme.ButtonHighlight;
			((ColorBlock)(ref defaultColorBlock)).pressedColor = Theme.ButtonPressed;
			((ColorBlock)(ref defaultColorBlock)).disabledColor = Theme.ButtonDisabled;
			((ColorBlock)(ref defaultColorBlock)).colorMultiplier = 1f;
			((ColorBlock)(ref defaultColorBlock)).fadeDuration = 0.08f;
			return defaultColorBlock;
		}

		private static UnityAction Guarded(string key, Action action)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			return (UnityAction)delegate
			{
				ReflectionGuard.Run(key, action);
			};
		}

		private static UnityAction<T> Guarded<T>(string key, Action<T> action)
		{
			return delegate(T v)
			{
				ReflectionGuard.Run(key, delegate
				{
					action(v);
				});
			};
		}
	}
	internal static class UiMath
	{
		internal static void Line(float fromX, float fromY, float toX, float toY, out float centreX, out float centreY, out float length, out float angleDeg)
		{
			float num = toX - fromX;
			float num2 = 0f - (toY - fromY);
			centreX = (fromX + toX) * 0.5f;
			centreY = (fromY + toY) * 0.5f;
			length = (float)Math.Sqrt(num * num + num2 * num2);
			angleDeg = ((length > 0f) ? ((float)(Math.Atan2(num2, num) * (180.0 / Math.PI))) : 0f);
		}
	}
}
namespace AfterlifeArcade
{
	internal enum FallbackPadToggle
	{
		None,
		Select,
		LeftStickPress,
		RightStickPress,
		ButtonNorth
	}
	internal sealed class ArcadeConfig
	{
		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<bool> AutoOpen { get; }

		internal ConfigEntry<float> AutoOpenDelay { get; }

		internal ConfigEntry<int> MinPlayers { get; }

		internal ConfigEntry<bool> EnableTileFall { get; }

		internal ConfigEntry<bool> EnableQteDuel { get; }

		internal ConfigEntry<int> VoteSeconds { get; }

		internal ConfigEntry<int> TileFallMaxSeconds { get; }

		internal ConfigEntry<float> OverlayOpacity { get; }

		internal ConfigEntry<bool> DimSpectateView { get; }

		internal ConfigEntry<float> SoundVolume { get; }

		internal ConfigEntry<FallbackPadToggle> PadToggle { get; }

		internal ConfigEntry<bool> MiniHud { get; }

		internal ConfigEntry<bool> BetsEnabled { get; }

		internal ConfigEntry<string> Stakes { get; }

		internal ConfigEntry<float> LateWindowSeconds { get; }

		internal ConfigEntry<float> TogetherWindowSeconds { get; }

		internal ConfigEntry<int> PropMarkets { get; }

		internal ConfigEntry<bool> DisableIfEnhancedSpectator { get; }

		internal ConfigEntry<bool> LineForTheLiving { get; }

		internal ConfigEntry<int> EctoStart { get; }

		internal ConfigEntry<int> EctoFloor { get; }

		internal ConfigEntry<int> EctoTopUp { get; }

		internal ConfigEntry<bool> BingoEnabled { get; }

		internal ConfigEntry<int> CardSize { get; }

		internal ConfigEntry<CountMode> BingoCountMode { get; }

		internal ConfigEntry<bool> CompanyPool { get; }

		internal ConfigEntry<bool> UseCoroner { get; }

		internal ConfigEntry<bool> DeathRecap { get; }

		internal ConfigEntry<int> PracticeBestMs { get; }

		internal ConfigEntry<bool> DebugLog { get; }

		internal ArcadeConfig(ConfigFile file)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Expected O, but got Unknown
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Expected O, but got Unknown
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Expected O, but got Unknown
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Expected O, but got Unknown
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Expected O, but got Unknown
			//IL_033a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Expected O, but got Unknown
			//IL_036c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Expected O, but got Unknown
			//IL_039e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a8: Expected O, but got Unknown
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fd: Expected O, but got Unknown
			Enabled = file.Bind<bool>("General", "Enabled", true, "Master switch. When off, the mod does nothing (you are simply absent from the arcade).");
			AutoOpen = file.Bind<bool>("General", "AutoOpenOnDeath", true, "Open the arcade overlay automatically after you die.");
			AutoOpenDelay = file.Bind<float>("General", "AutoOpenDelaySeconds", 3f, new ConfigDescription("Seconds after death before the overlay opens (so you can watch your death first).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			MinPlayers = file.Bind<int>("Host", "MinPlayers", 2, new ConfigDescription("Host only: dead players with the overlay open needed to start a match.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 8), Array.Empty<object>()));
			EnableTileFall = file.Bind<bool>("Host", "EnableTileFall", true, "Host only: offer Tile Fall.");
			EnableQteDuel = file.Bind<bool>("Host", "EnableReactionDuel", true, "Host only: offer the Reaction Duel (QTE).");
			VoteSeconds = file.Bind<int>("Host", "VoteSeconds", 10, new ConfigDescription("Host only: length of the game vote.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 60), Array.Empty<object>()));
			TileFallMaxSeconds = file.Bind<int>("Host", "TileFallMaxSeconds", 90, new ConfigDescription("Host only: Tile Fall time limit (survivors share the win).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(30, 180), Array.Empty<object>()));
			OverlayOpacity = file.Bind<float>("UI", "OverlayOpacity", 0.92f, new ConfigDescription("Opacity of the arcade panel.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 1f), Array.Empty<object>()));
			DimSpectateView = file.Bind<bool>("UI", "DimSpectateView", false, "Darken the spectate camera behind the overlay.");
			SoundVolume = file.Bind<float>("UI", "SoundVolume", 0.6f, new ConfigDescription("Volume of the arcade's beeps.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			PadToggle = file.Bind<FallbackPadToggle>("Input", "PadToggleWithoutInputUtils", FallbackPadToggle.None, "Gamepad button that toggles the overlay when InputUtils is NOT installed. With InputUtils, bind 'Toggle Afterlife Arcade' in its keybind menu instead.");
			MiniHud = file.Bind<bool>("UI", "MiniHud", true, "While dead: show the small bingo card and bet ticker at the right edge, even with the overlay closed.");
			BetsEnabled = file.Bind<bool>("Bets", "Enabled", true, "Host: run Spectator Betting (dead players bet cosmetic Ecto on the round). Takes effect at the next session; a change of this or Bingo.Enabled from both off to on needs a game restart.");
			Stakes = file.Bind<string>("Bets", "Stakes", "10,25,50", "Host: the allowed stakes (up to 4, comma separated, 1-1000).");
			LateWindowSeconds = file.Bind<float>("Bets", "LateWindowSeconds", 5f, new ConfigDescription("Host: a bet placed less than this before its outcome became visible is refunded.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			TogetherWindowSeconds = file.Bind<float>("Bets", "TogetherWindowSeconds", 1.5f, new ConfigDescription("Host: 'Next to die' counts deaths this close to the first one as one answer (any of them wins).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			PropMarkets = file.Bind<int>("Bets", "PropMarkets", 1, new ConfigDescription("Host: yes/no markets on round events per round ('Will a mine go off?').", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>()));
			DisableIfEnhancedSpectator = file.Bind<bool>("Bets", "DisableIfEnhancedSpectator", true, "Turn Spectator Betting off when EnhancedSpectator is loaded (it has its own betting UI). On the host this switches the markets off; on a client it hides the bets tab.");
			LineForTheLiving = file.Bind<bool>("Bets", "LineForTheLiving", true, "At the end of a round you survived, show one line about what the dead bet on you.");
			EctoStart = file.Bind<int>("Ecto", "StartBalance", 100, new ConfigDescription("Host: Ecto a player starts with.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10000), Array.Empty<object>()));
			EctoFloor = file.Bind<int>("Ecto", "Floor", 10, new ConfigDescription("Host: at round start, anyone below this is topped up to TopUp.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10000), Array.Empty<object>()));
			EctoTopUp = file.Bind<int>("Ecto", "TopUp", 20, new ConfigDescription("Host: the top-up balance.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10000), Array.Empty<object>()));
			BingoEnabled = file.Bind<bool>("Bingo", "Enabled", true, "Host: deal Ghost Bingo cards to dead players (marked by what happens in the round).");
			CardSize = file.Bind<int>("Bingo", "CardSize", 4, new ConfigDescription("Host: 4 = 4x4 (10 lines), 5 = 5x5 with a free centre (12 lines). Company days use 3x3.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[2] { 4, 5 }), Array.Empty<object>()));
			BingoCountMode = file.Bind<CountMode>("Bingo", "CountMode", CountMode.WholeRound, "Host: WholeRound = every event of the round counts (a new card can arrive already marked); FromDeath = only events after you died.");
			CompanyPool = file.Bind<bool>("Bingo", "CompanyPool", true, "Host: deal (3x3) cards on Company days, with Company squares (sales, quota).");
			UseCoroner = file.Bind<bool>("Bingo", "UseCoroner", true, "Host: when the Coroner mod is installed, let its cause of death override our guess (no effect without Coroner).");
			DeathRecap = file.Bind<bool>("Bingo", "DeathRecap", true, "Show 'Killed by: ...' above your card after you die.");
			PracticeBestMs = file.Bind<int>("Stats", "PracticeBestReactionMs", 0, "Your best solo practice reaction time (0 = none yet). Written by the mod.");
			DebugLog = file.Bind<bool>("Debug", "VerboseLogging", false, "Log protocol and session events.");
		}

		internal HostConfig BuildHostConfig(string version)
		{
			HostConfig hostConfig = new HostConfig
			{
				HostVersion = version
			};
			hostConfig.Lobby.MinPlayers = MinPlayers.Value;
			hostConfig.Lobby.VoteMs = VoteSeconds.Value * 1000;
			List<GameId> list = new List<GameId>();
			if (EnableTileFall.Value)
			{
				list.Add(GameId.TileFall);
			}
			if (EnableQteDuel.Value)
			{
				list.Add(GameId.QteDuel);
			}
			hostConfig.Lobby.EnabledGames = list;
			hostConfig.TileFall.MaxDurationTicks = TileFallMaxSeconds.Value * hostConfig.TileFall.TickRate;
			hostConfig.TileFall.SuddenDeathAfterTicks = hostConfig.TileFall.MaxDurationTicks / 2;
			return hostConfig;
		}

		internal SideConfig BuildSideConfig(bool betsAllowed)
		{
			SideConfig sideConfig = new SideConfig();
			sideConfig.Bets.Enabled = BetsEnabled.Value && betsAllowed;
			sideConfig.Bets.Stakes = BetsConfig.ParseStakes(Stakes.Value);
			sideConfig.Bets.LateWindowMs = (int)(LateWindowSeconds.Value * 1000f);
			sideConfig.Bets.TogetherWindowMs = (int)(TogetherWindowSeconds.Value * 1000f);
			sideConfig.Bets.PropMarkets = PropMarkets.Value;
			sideConfig.Ecto.StartBalance = EctoStart.Value;
			sideConfig.Ecto.Floor = EctoFloor.Value;
			sideConfig.Ecto.TopUp = EctoTopUp.Value;
			sideConfig.Bingo.Enabled = BingoEnabled.Value;
			sideConfig.Bingo.CardSize = CardSize.Value;
			sideConfig.Bingo.CountMode = BingoCountMode.Value;
			sideConfig.Bingo.CompanyPool = CompanyPool.Value;
			sideConfig.Validate();
			return sideConfig;
		}
	}
	[BepInPlugin("dev.thatumi.AfterlifeArcade", "AfterlifeArcade", "0.3.0")]
	[BepInDependency("LethalNetworkAPI", "3.4.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private const string LnapiGuid = "LethalNetworkAPI";

		private const string TestedLnapiVersion = "3.4.2";

		private Harmony? _harmony;

		private void Awake()
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			LogBepInEx.Init(((BaseUnityPlugin)this).Logger);
			ArcadeConfig arcadeConfig;
			try
			{
				arcadeConfig = new ArcadeConfig(((BaseUnityPlugin)this).Config);
				Log.DebugEnabled = arcadeConfig.DebugLog.Value;
			}
			catch (Exception ex)
			{
				Log.Error("AfterlifeArcade config failed, plugin disabled: " + ex);
				return;
			}
			IArcadeInput input = null;
			try
			{
				input = ArcadeInputFactory.Create(arcadeConfig);
			}
			catch (Exception ex2)
			{
				Log.Error("AfterlifeArcade input setup failed (overlay keys disabled): " + ex2);
			}
			LogLnapiVersion();
			ArcadeNet.Init();
			ArcadeSession.Init(arcadeConfig, input, "0.3.0");
			_harmony = new Harmony("dev.thatumi.AfterlifeArcade");
			Patch(typeof(SpectatorInputPatches));
			Patch(typeof(RoundEndPatches));
			PatchSideGames(arcadeConfig);
			try
			{
				ArcadeRunner.Ensure();
			}
			catch (Exception ex3)
			{
				Log.Error("AfterlifeArcade runner failed, plugin inactive: " + ex3);
			}
			Log.Info("AfterlifeArcade 0.3.0 loaded (" + CoreInfo.Describe() + ")");
		}

		private static void LogLnapiVersion()
		{
			try
			{
				if (!Chainloader.PluginInfos.TryGetValue("LethalNetworkAPI", out var value) || ((value != null) ? value.Metadata : null) == null)
				{
					Log.Warn("AfterlifeArcade: LethalNetworkAPI plugin info not found; networking may not work.");
					return;
				}
				Version version = value.Metadata.Version;
				if (version.ToString() == "3.4.2")
				{
					Log.Info("Using LethalNetworkAPI " + version);
				}
				else
				{
					Log.Warn("AfterlifeArcade was tested with LethalNetworkAPI 3.4.2, this game runs " + version?.ToString() + ". If the arcade misbehaves, include this line in the report.");
				}
			}
			catch (Exception ex)
			{
				Log.Warn("AfterlifeArcade: could not read the LethalNetworkAPI version: " + ex.Message);
			}
		}

		private void PatchSideGames(ArcadeConfig cfg)
		{
			if (!cfg.BetsEnabled.Value && !cfg.BingoEnabled.Value)
			{
				Log.Info("AfterlifeArcade: bets and bingo are off in the config; their detectors are not patched");
			}
			else if (RpcStage.Available)
			{
				Type[] array = new Type[11]
				{
					typeof(DeathPatches),
					typeof(SettlementPatches),
					typeof(LandminePatches),
					typeof(TurretPatches),
					typeof(RoundManagerPatches),
					typeof(StormyWeatherPatches),
					typeof(ShipTeleporterPatches),
					typeof(EntranceTeleportPatches),
					typeof(TimeOfDayPatches),
					typeof(EnemyPatches),
					typeof(CompanyPatches)
				};
				foreach (Type patchClass in array)
				{
					Patch(patchClass);
				}
			}
		}

		private void Patch(Type patchClass)
		{
			try
			{
				_harmony.PatchAll(patchClass);
			}
			catch (Exception ex)
			{
				Log.Error("AfterlifeArcade: patching " + patchClass.Name + " failed, that feature is disabled: " + ex);
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "dev.thatumi.AfterlifeArcade";

		public const string PLUGIN_NAME = "AfterlifeArcade";

		public const string PLUGIN_VERSION = "0.3.0";
	}
}
namespace AfterlifeArcade.Core
{
	public enum Dir : byte
	{
		None,
		Up,
		Down,
		Left,
		Right
	}
	public static class DirExt
	{
		public static int Dx(this Dir d)
		{
			if (d != Dir.Left)
			{
				return (d == Dir.Right) ? 1 : 0;
			}
			return -1;
		}

		public static int Dy(this Dir d)
		{
			if (d != Dir.Up)
			{
				return (d == Dir.Down) ? 1 : 0;
			}
			return -1;
		}

		public static bool IsMove(this Dir d)
		{
			if ((int)d >= 1)
			{
				return (int)d <= 4;
			}
			return false;
		}
	}
	public enum GameId : byte
	{
		None,
		TileFall,
		QteDuel
	}
	public static class Games
	{
		public static readonly IReadOnlyList<GameId> All = new GameId[2]
		{
			GameId.TileFall,
			GameId.QteDuel
		};

		public static bool IsKnown(GameId g)
		{
			if (g != GameId.TileFall)
			{
				return g == GameId.QteDuel;
			}
			return true;
		}

		public static string DisplayName(GameId g)
		{
			return g switch
			{
				GameId.TileFall => "Tile Fall", 
				GameId.QteDuel => "Reaction Duel", 
				_ => "?", 
			};
		}

		public static string Blurb(GameId g)
		{
			return g switch
			{
				GameId.TileFall => "Tiles crack under your feet. Keep moving. Last one standing wins.", 
				GameId.QteDuel => "Press the shown key as fast as you can. Too early counts as a miss.", 
				_ => "", 
			};
		}

		public static byte Mask(IEnumerable<GameId> games)
		{
			byte b = 0;
			foreach (GameId game in games)
			{
				if (IsKnown(game))
				{
					b |= (byte)(1 << (int)game);
				}
			}
			return b;
		}

		public static List<GameId> FromMask(byte mask)
		{
			List<GameId> list = new List<GameId>();
			foreach (GameId item in All)
			{
				if ((mask & (1 << (int)item)) != 0)
				{
					list.Add(item);
				}
			}
			return list;
		}
	}
	public readonly struct Placement : IEquatable<Placement>
	{
		public ulong Id { get; }

		public int Place { get; }

		public Placement(ulong id, int place)
		{
			Id = id;
			Place = place;
		}

		public bool Equals(Placement other)
		{
			if (Id == other.Id)
			{
				return Place == other.Place;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is Placement other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return HashCode.Combine(Id, Place);
		}

		public override string ToString()
		{
			return Id + "#" + Place;
		}
	}
	public enum EndReason : byte
	{
		None,
		ShipLeft,
		GameOver,
		Fired,
		NewRound,
		HostStopped,
		Revived,
		Disconnected
	}
	public static class Ranking
	{
		public static List<Placement> Compute<T>(IReadOnlyList<T> items, Func<T, ulong> id, Comparison<T> better, Func<T, bool> unranked)
		{
			List<T> list = new List<T>();
			List<T> list2 = new List<T>();
			foreach (T item in items)
			{
				if (unranked(item))
				{
					list2.Add(item);
				}
				else
				{
					list.Add(item);
				}
			}
			for (int i = 1; i < list.Count; i++)
			{
				T val = list[i];
				int num = i - 1;
				while (num >= 0 && better(val, list[num]) < 0)
				{
					list[num + 1] = list[num];
					num--;
				}
				list[num + 1] = val;
			}
			List<Placement> list3 = new List<Placement>(items.Count);
			for (int j = 0; j < list.Count; j++)
			{
				int place = j + 1;
				if (j > 0 && better(list[j], list[j - 1]) == 0)
				{
					place = list3[j - 1].Place;
				}
				list3.Add(new Placement(id(list[j]), place));
			}
			foreach (T item2 in list2)
			{
				list3.Add(new Placement(id(item2), 0));
			}
			return list3;
		}
	}
	public static class CoreInfo
	{
		public const int FormatVersion = 2;

		public static string Describe()
		{
			return "AfterlifeArcade.Core protocol v" + 2;
		}
	}
	public sealed class Rng
	{
		private ulong _state;

		public Rng(ulong seed)
		{
			ulong x = seed;
			_state = SplitMix(ref x);
			if (_state == 0L)
			{
				_state = 11400714819323198485uL;
			}
		}

		public static ulong SplitMix(ref ulong x)
		{
			x += 11400714819323198485uL;
			ulong num = x;
			long num2 = (long)(num ^ (num >> 30)) * -4658895280553007687L;
			long num3 = (num2 ^ (num2 >>> 27)) * -7723592293110705685L;
			return (ulong)(num3 ^ (num3 >>> 31));
		}

		public ulong NextULong()
		{
			_state ^= _state >> 12;
			_state ^= _state << 25;
			_state ^= _state >> 27;
			return _state * 2685821657736338717L;
		}

		public uint NextUInt()
		{
			return (uint)(NextULong() >> 32);
		}

		public int Next(int maxExclusive)
		{
			if (maxExclusive <= 0)
			{
				throw new ArgumentOutOfRangeException("maxExclusive");
			}
			uint num = (uint)(-maxExclusive) % (uint)maxExclusive;
			uint num2;
			do
			{
				num2 = NextUInt();
			}
			while (num2 < num);
			return (int)(num2 % (uint)maxExclusive);
		}

		public int Range(int minInclusive, int maxInclusive)
		{
			if (maxInclusive < minInclusive)
			{
				throw new ArgumentOutOfRangeException("maxInclusive");
			}
			return minInclusive + Next(maxInclusive - minInclusive + 1);
		}

		public void Shuffle<T>(IList<T> list)
		{
			for (int num = list.Count - 1; num > 0; num--)
			{
				int index = Next(num + 1);
				T value = list[num];
				list[num] = list[index];
				list[index] = value;
			}
		}
	}
	public enum Waveform
	{
		Sine,
		Square,
		Triangle
	}
	public static class ToneSynth
	{
		public static float[] Tone(int sampleRate, float startHz, float endHz, float seconds, float volume, Waveform wave = Waveform.Sine, float attackSec = 0.005f, float releaseSec = 0.03f)
		{
			if (sampleRate < 1000 || sampleRate > 192000)
			{
				throw new ArgumentOutOfRangeException("sampleRate");
			}
			if (seconds <= 0f || seconds > 5f)
			{
				throw new ArgumentOutOfRangeException("seconds");
			}
			int num = Math.Max(1, (int)((float)sampleRate * seconds));
			float[] array = new float[num];
			double num2 = 0.0;
			volume = Math.Max(0f, Math.Min(1f, volume));
			int num3 = Math.Max(1, (int)(attackSec * (float)sampleRate));
			int num4 = Math.Max(1, (int)(releaseSec * (float)sampleRate));
			for (int i = 0; i < num; i++)
			{
				double num5 = (double)i / (double)num;
				double num6 = (double)startHz + (double)(endHz - startHz) * num5;
				num2 += Math.PI * 2.0 * num6 / (double)sampleRate;
				double num7 = wave switch
				{
					Waveform.Square => (Math.Sin(num2) >= 0.0) ? 1 : (-1), 
					Waveform.Triangle => 2.0 / Math.PI * Math.Asin(Math.Sin(num2)), 
					_ => Math.Sin(num2), 
				};
				double num8 = Math.Min(1.0, Math.Min((double)i / (double)num3, (double)(num - 1 - i) / (double)num4));
				array[i] = (float)(num7 * num8 * (double)volume);
			}
			return array;
		}

		public static float[] Concat(params float[][] parts)
		{
			int num = 0;
			float[][] array = parts;
			foreach (float[] array2 in array)
			{
				num += array2.Length;
			}
			float[] array3 = new float[num];
			int num2 = 0;
			array = parts;
			foreach (float[] array4 in array)
			{
				Array.Copy(array4, 0, array3, num2, array4.Length);
				num2 += array4.Length;
			}
			return array3;
		}
	}
}
namespace AfterlifeArcade.Core.TileFall
{
	public sealed class PlayoutClock
	{
		public double TickMs { get; }

		public double TargetDelayTicks { get; }

		public double ResyncTicks { get; }

		public bool Started { get; private set; }

		public int NewestTick { get; private set; }

		public double RenderTick { get; private set; }

		public int Resyncs { get; private set; }

		public PlayoutClock(double tickMs, double targetDelayTicks = 3.0, double resyncTicks = 40.0)
		{
			if (tickMs <= 0.0)
			{
				throw new ArgumentOutOfRangeException("tickMs");
			}
			TickMs = tickMs;
			TargetDelayTicks = targetDelayTicks;
			ResyncTicks = resyncTicks;
		}

		public void OnSnapshot(int tick)
		{
			if (!Started)
			{
				Started = true;
				NewestTick = tick;
				RenderTick = Math.Max(0.0, (double)tick - TargetDelayTicks);
			}
			else if (tick > NewestTick)
			{
				NewestTick = tick;
			}
		}

		public void Advance(double dtMs)
		{
			if (Started && !(dtMs <= 0.0))
			{
				double num = (double)NewestTick - RenderTick;
				if (num > TargetDelayTicks + ResyncTicks)
				{
					RenderTick = (double)NewestTick - TargetDelayTicks;
					Resyncs++;
				}
				else
				{
					double num2 = ((num > TargetDelayTicks + 3.0) ? 1.6 : ((num > TargetDelayTicks + 1.0) ? 1.25 : ((!(num < TargetDelayTicks - 1.0)) ? 1.0 : 0.85)));
					RenderTick = Math.Min(NewestTick, RenderTick + dtMs / TickMs * num2);
				}
			}
		}
	}
	public sealed class SnapshotBuffer
	{
		private readonly List<TileFallSnapshot> _items = new List<TileFallSnapshot>();

		private readonly int _capacity;

		public int Count => _items.Count;

		public TileFallSnapshot? Newest
		{
			get
			{
				if (_items.Count != 0)
				{
					return _items[_items.Count - 1];
				}
				return null;
			}
		}

		public SnapshotBuffer(int capacity = 64)
		{
			_capacity = Math.Max(2, capacity);
		}

		public bool Add(TileFallSnapshot s)
		{
			if (_items.Count > 0 && s.Tick <= _items[_items.Count - 1].Tick)
			{
				return false;
			}
			_items.Add(s);
			if (_items.Count > _capacity)
			{
				_items.RemoveAt(0);
			}
			return true;
		}

		public void Trim(double renderTick)
		{
			while (_items.Count > 2 && (double)_items[1].Tick <= renderTick)
			{
				_items.RemoveAt(0);
			}
		}

		public bool Sample(double renderTick, int slot, out float x, out float y, out bool alive)
		{
			x = (y = 0f);
			alive = false;
			if (_items.Count == 0)
			{
				return false;
			}
			TileFallSnapshot tileFallSnapshot = null;
			TileFallSnapshot tileFallSnapshot2 = null;
			foreach (TileFallSnapshot item in _items)
			{
				if ((double)item.Tick <= renderTick)
				{
					tileFallSnapshot = item;
					continue;
				}
				tileFallSnapshot2 = item;
				break;
			}
			if (tileFallSnapshot == null)
			{
				tileFallSnapshot = tileFallSnapshot2;
			}
			if (tileFallSnapshot2 == null)
			{
				tileFallSnapshot2 = tileFallSnapshot;
			}
			if (slot < 0 || slot >= tileFallSnapshot.Players.Length || slot >= tileFallSnapshot2.Players.Length)
			{
				return false;
			}
			TileFallPlayerState tileFallPlayerState = tileFallSnapshot.Players[slot];
			TileFallPlayerState tileFallPlayerState2 = tileFallSnapshot2.Players[slot];
			double num = ((tileFallSnapshot2.Tick == tileFallSnapshot.Tick) ? 1.0 : Clamp01((renderTick - (double)tileFallSnapshot.Tick) / (double)(tileFallSnapshot2.Tick - tileFallSnapshot.Tick)));
			if (Math.Max(Math.Abs(tileFallPlayerState.X - tileFallPlayerState2.X), Math.Abs(tileFallPlayerState.Y - tileFallPlayerState2.Y)) > 1)
			{
				TileFallPlayerState tileFallPlayerState3 = ((num >= 0.5) ? tileFallPlayerState2 : tileFallPlayerState);
				x = (int)tileFallPlayerState3.X;
				y = (int)tileFallPlayerState3.Y;
			}
			else
			{
				x = (float)((double)(int)tileFallPlayerState.X + (double)(tileFallPlayerState2.X - tileFallPlayerState.X) * num);
				y = (float)((double)(int)tileFallPlayerState.Y + (double)(tileFallPlayerState2.Y - tileFallPlayerState.Y) * num);
			}
			alive = ((num >= 1.0) ? tileFallPlayerState2.Alive : tileFallPlayerState.Alive);
			return true;
		}

		private static double Clamp01(double v)
		{
			if (!(v < 0.0))
			{
				if (!(v > 1.0))
				{
					return v;
				}
				return 1.0;
			}
			return 0.0;
		}
	}
	public sealed class LocalPredictor
	{
		private readonly struct Pending
		{
			public ushort Seq { get; }

			public Dir Dir { get; }

			public long SentMs { get; }

			public Pending(ushort seq, Dir dir, long sentMs)
			{
				Seq = seq;
				Dir = dir;
				SentMs = sentMs;
			}
		}

		private readonly List<Pending> _pending = new List<Pending>();

		private TileFallSnapshot? _snap;

		private long _snapRecvMs;

		private int _slot = -1;

		public const int MaxPending = 6;

		public const double DefaultRttMs = 250.0;

		public const int SafetyTicks = 2;

		public double TickMs { get; }

		public int X { get; private set; }

		public int Y { get; private set; }

		public bool Alive { get; private set; }

		public bool HasState => _snap != null;

		public int PendingCount => _pending.Count;

		public int LastCorrection { get; private set; }

		public int MaxCorrection { get; private set; }

		public int DeathSnap { get; private set; }

		public double RttMs { get; private set; }

		public int RttSamples { get; private set; }

		public LocalPredictor(double tickMs = 50.0)
		{
			if (tickMs <= 0.0)
			{
				throw new ArgumentOutOfRangeException("tickMs");
			}
			TickMs = tickMs;
			RttMs = 250.0;
		}

		public void Reset()
		{
			_pending.Clear();
			_snap = null;
			_slot = -1;
			int num = (DeathSnap = 0);
			int lastCorrection = (MaxCorrection = num);
			LastCorrection = lastCorrection;
			RttMs = 250.0;
			RttSamples = 0;
		}

		public void OnSnapshot(TileFallSnapshot snap, int slot, long localNowMs = 0L)
		{
			if (slot < 0 || slot >= snap.Players.Length)
			{
				return;
			}
			bool flag = _snap != null;
			bool alive = Alive;
			int x = X;
			int y = Y;
			_snap = snap;
			_snapRecvMs = localNowMs;
			_slot = slot;
			TileFallPlayerState tileFallPlayerState = snap.Players[slot];
			if (tileFallPlayerState.HasSeq)
			{
				for (int num = _pending.Count - 1; num >= 0; num--)
				{
					if (!TileFallSim.SeqNewer(_pending[num].Seq, tileFallPlayerState.LastSeq))
					{
						if (_pending[num].Seq == tileFallPlayerState.LastSeq)
						{
							SampleRtt(localNowMs - _pending[num].SentMs);
						}
						_pending.RemoveAt(num);
					}
				}
			}
			if (!tileFallPlayerState.Alive)
			{
				_pending.Clear();
			}
			Replay();
			int num2 = (flag ? Math.Max(Math.Abs(X - x), Math.Abs(Y - y)) : 0);
			if (flag && alive && !Alive)
			{
				DeathSnap = num2;
				LastCorrection = 0;
				return;
			}
			LastCorrection = num2;
			if (LastCorrection > MaxCorrection)
			{
				MaxCorrection = LastCorrection;
			}
		}

		public double LeadTicks(long localNowMs)
		{
			return (RttMs + (double)Math.Max(0L, localNowMs - _snapRecvMs)) / TickMs + 2.0;
		}

		public bool CanMove(Dir dir, long localNowMs = 0L)
		{
			if (_snap == null || !Alive || !dir.IsMove() || _pending.Count >= 6)
			{
				return false;
			}
			int num = X + dir.Dx();
			int num2 = Y + dir.Dy();
			switch (_snap.TileAt(num, num2))
			{
			case TileState.Fallen:
				return false;
			case TileState.Cracking:
				if ((double)(int)_snap.Timers[num2 * _snap.Width + num] <= LeadTicks(localNowMs))
				{
					return false;
				}
				break;
			}
			return true;
		}

		public bool IsDoomed(int x, int y, long localNowMs)
		{
			if (_snap == null || x < 0 || y < 0 || x >= _snap.Width || y >= _snap.Height)
			{
				return false;
			}
			int num = y * _snap.Width + x;
			if (_snap.Tiles[num] == TileState.Cracking)
			{
				return (double)(int)_snap.Timers[num] <= LeadTicks(localNowMs);
			}
			return false;
		}

		public void OnInputSent(ushort seq, Dir dir, long localNowMs = 0L)
		{
			if (_snap != null)
			{
				_pending.Add(new Pending(seq, dir, localNowMs));
				Apply(dir);
			}
		}

		private void SampleRtt(long sample)
		{
			if (sample >= 0 && sample <= 5000)
			{
				RttMs = ((RttSamples == 0) ? ((double)sample) : (RttMs * 0.8 + (double)sample * 0.2));
				RttSamples++;
			}
		}

		private void Replay()
		{
			if (_snap == null || _slot < 0)
			{
				return;
			}
			TileFallPlayerState tileFallPlayerState = _snap.Players[_slot];
			X = tileFallPlayerState.X;
			Y = tileFallPlayerState.Y;
			Alive = tileFallPlayerState.Alive;
			foreach (Pending item in _pending)
			{
				Apply(item.Dir);
			}
		}

		private void Apply(Dir dir)
		{
			if (_snap != null && Alive)
			{
				int x = X + dir.Dx();
				int y = Y + dir.Dy();
				if (_snap.TileAt(x, y) != TileState.Fallen)
				{
					X = x;
					Y = y;
				}
			}
		}
	}
	public sealed class MoveRepeater
	{
		private Dir _held;

		private Dir _buffered;

		private long _lastMoveMs;

		private bool _hasMoved;

		public int IntervalMs { get; }

		public MoveRepeater(int intervalMs = 170)
		{
			if (intervalMs < 1)
			{
				throw new ArgumentOutOfRangeException("intervalMs");
			}
			IntervalMs = intervalMs;
		}

		public void Reset()
		{
			_held = (_buffered = Dir.None);
			_hasMoved = false;
		}

		public Dir Update(Dir held, Dir pressed, long nowMs)
		{
			if (pressed.IsMove())
			{
				_buffered = pressed;
			}
			else if (held.IsMove() && held != _held)
			{
				_buffered = held;
			}
			_held = held;
			if (_hasMoved && nowMs - _lastMoveMs < IntervalMs)
			{
				return Dir.None;
			}
			Dir result;
			if (_buffered.IsMove())
			{
				result = _buffered;
				_buffered = Dir.None;
			}
			else
			{
				if (!_held.IsMove())
				{
					return Dir.None;
				}
				result = _held;
			}
			_lastMoveMs = nowMs;
			_hasMoved = true;
			return result;
		}

		public void Refund()
		{
			_hasMoved = false;
		}
	}
	public sealed class TileFallClientView
	{
		private ushort _nextSeq = 1;

		public IReadOnlyList<ulong> Participants { get; }

		public int LocalSlot { get; }

		public bool IsParticipant => LocalSlot >= 0;

		public PlayoutClock Clock { get; }

		public SnapshotBuffer Buffer { get; } = new SnapshotBuffer();

		public LocalPredictor Predictor { get; }

		public MoveRepeater Repeater { get; } = new MoveRepeater();

		public TileFallSnapshot? Latest => Buffer.Newest;

		public int SnapshotsReceived { get; private set; }

		public TileFallClientView(IReadOnlyList<ulong> participants, ulong localId, int tickMs)
		{
			Participants = participants;
			LocalSlot = -1;
			for (int i = 0; i < participants.Count; i++)
			{
				if (participants[i] == localId)
				{
					LocalSlot = i;
				}
			}
			Clock = new PlayoutClock(tickMs);
			Predictor = new LocalPredictor(tickMs);
		}

		public void OnSnapshot(TileFallSnapshot s, long localNowMs = 0L)
		{
			if (s.Players.Length == Participants.Count && Buffer.Add(s))
			{
				SnapshotsReceived++;
				Clock.OnSnapshot(s.Tick);
				if (IsParticipant)
				{
					Predictor.OnSnapshot(s, LocalSlot, localNowMs);
				}
			}
		}

		public bool Update(double dtMs, long localNowMs, Dir held, Dir pressed, bool inputAllowed, out ushort seq, out Dir move)
		{
			seq = 0;
			move = Dir.None;
			Clock.Advance(dtMs);
			Buffer.Trim(Clock.RenderTick - 2.0);
			if (!IsParticipant || !inputAllowed || !Predictor.HasState || !Predictor.Alive)
			{
				Repeater.Reset();
				return false;
			}
			Dir dir = Repeater.Update(held, pressed, localNowMs);
			if (!dir.IsMove())
			{
				return false;
			}
			if (!Predictor.CanMove(dir, localNowMs))
			{
				Repeater.Refund();
				return false;
			}
			seq = _nextSeq++;
			if (_nextSeq == 0)
			{
				_nextSeq = 1;
			}
			move = dir;
			Predictor.OnInputSent(seq, dir, localNowMs);
			return true;
		}

		public bool GetDisplay(int slot, out float x, out float y, out bool alive)
		{
			if (slot == LocalSlot && Predictor.HasState)
			{
				x = Predictor.X;
				y = Predictor.Y;
				alive = Predictor.Alive;
				return true;
			}
			return Buffer.Sample(Clock.RenderTick, slot, out x, out y, out alive);
		}
	}
	public static class SnapshotCodec
	{
		public const int MaxPlayers = 8;

		public static void Write(PacketWriter w, TileFallSnapshot s)
		{
			if (s.Tick < 0 || s.Tick > 65535)
			{
				throw new ArgumentOutOfRangeException("s", "tick out of range");
			}
			if (s.Players.Length > 8)
			{
				throw new ArgumentOutOfRangeException("s", "too many players");
			}
			int num = s.Width * s.Height;
			if (s.Tiles.Length != num || s.Timers.Length != num)
			{
				throw new ArgumentException("tile arrays do not match width*height");
			}
			w.UShort((ushort)s.Tick);
			w.Byte(s.Finished ? ((byte)1) : ((byte)0));
			w.Byte((byte)s.Width);
			w.Byte((byte)s.Height);
			WriteMask(w, s.Tiles, TileState.Fallen);
			WriteMask(w, s.Tiles, TileState.Cracking);
			for (int i = 0; i < num; i++)
			{
				if (s.Tiles[i] == TileState.Cracking)
				{
					w.Byte(s.Timers[i]);
				}
			}
			w.Byte((byte)s.Players.Length);
			TileFallPlayerState[] players = s.Players;
			for (int j = 0; j < players.Length; j++)
			{
				TileFallPlayerState tileFallPlayerState = players[j];
				w.Byte(tileFallPlayerState.X);
				w.Byte(tileFallPlayerState.Y);
				w.Byte((byte)((tileFallPlayerState.Alive ? 1u : 0u) | (uint)(tileFallPlayerState.HasSeq ? 2 : 0)));
				w.UShort(tileFallPlayerState.LastSeq);
			}
		}

		public static TileFallSnapshot Read(PacketReader r)
		{
			TileFallSnapshot tileFallSnapshot = new TileFallSnapshot
			{
				Tick = r.UShort()
			};
			byte b = r.Byte();
			if ((b & -2) != 0)
			{
				throw new PacketFormatException("unknown snapshot flags " + b);
			}
			tileFallSnapshot.Finished = (b & 1) != 0;
			tileFallSnapshot.Width = r.Byte();
			tileFallSnapshot.Height = r.Byte();
			if (tileFallSnapshot.Width < 3 || tileFallSnapshot.Width > 32 || tileFallSnapshot.Height < 3 || tileFallSnapshot.Height > 32)
			{
				throw new PacketFormatException("bad grid size");
			}
			int num = tileFallSnapshot.Width * tileFallSnapshot.Height;
			bool[] array = ReadMask(r, num);
			bool[] array2 = ReadMask(r, num);
			tileFallSnapshot.Tiles = new TileState[num];
			tileFallSnapshot.Timers = new byte[num];
			for (int i = 0; i < num; i++)
			{
				if (array[i] && array2[i])
				{
					throw new PacketFormatException("tile " + i + " both fallen and cracking");
				}
				if (array[i])
				{
					tileFallSnapshot.Tiles[i] = TileState.Fallen;
				}
				else if (array2[i])
				{
					tileFallSnapshot.Tiles[i] = TileState.Cracking;
				}
			}
			for (int j = 0; j < num; j++)
			{
				if (tileFallSnapshot.Tiles[j] == TileState.Cracking)
				{
					tileFallSnapshot.Timers[j] = r.Byte();
					if (tileFallSnapshot.Timers[j] == 0)
					{
						throw new PacketFormatException("cracking tile with zero timer");
					}
				}
			}
			int num2 = r.Count(8);
			tileFallSnapshot.Players = new TileFallPlayerState[num2];
			for (int k = 0; k < num2; k++)
			{
				TileFallPlayerState tileFallPlayerState = new TileFallPlayerState
				{
					X = r.Byte(),
					Y = r.Byte()
				};
				byte b2 = r.Byte();
				if ((b2 & -4) != 0)
				{
					throw new PacketFormatException("unknown player flags " + b2);
				}
				tileFallPlayerState.Alive = (b2 & 1) != 0;
				tileFallPlayerState.HasSeq = (b2 & 2) != 0;
				tileFallPlayerState.LastSeq = r.UShort();
				if (tileFallPlayerState.X >= tileFallSnapshot.Width || tileFallPlayerState.Y >= tileFallSnapshot.Height)
				{
					throw new PacketFormatException("player outside grid");
				}
				tileFallSnapshot.Players[k] = tileFallPlayerState;
			}
			return tileFallSnapshot;
		}

		public static byte[] Encode(TileFallSnapshot s)
		{
			PacketWriter packetWriter = new PacketWriter(96);
			Write(packetWriter, s);
			return packetWriter.ToArray();
		}

		public static TileFallSnapshot Decode(byte[] data)
		{
			PacketReader packetReader = new PacketReader(data);
			TileFallSnapshot result = Read(packetReader);
			packetReader.ExpectEnd();
			return result;
		}

		public static void WriteMask(PacketWriter w, TileState[] tiles, TileState state)
		{
			int num = (tiles.Length + 7) / 8;
			for (int i = 0; i < num; i++)
			{
				byte b = 0;
				for (int j = 0; j < 8; j++)
				{
					int num2 = i * 8 + j;
					if (num2 < tiles.Length && tiles[num2] == state)
					{
						b |= (byte)(1 << j);
					}
				}
				w.Byte(b);
			}
		}

		public static bool[] ReadMask(PacketReader r, int n)
		{
			bool[] array = new bool[n];
			int num = (n + 7) / 8;
			for (int i = 0; i < num; i++)
			{
				byte b = r.Byte();
				for (int j = 0; j < 8; j++)
				{
					int num2 = i * 8 + j;
					bool flag = (b & (1 << j)) != 0;
					if (num2 < n)
					{
						array[num2] = flag;
					}
					else if (flag)
					{
						throw new PacketFormatException("padding bit set in mask");
					}
				}
			}
			return array;
		}
	}
	public enum TileState : byte
	{
		Intact,
		Cracking,
		Fallen
	}
	public sealed class TileFallConfig
	{
		public int Width { get; set; } = 10;

		public int Height { get; set; } = 10;

		public int TickRate { get; set; } = 20;

		public int CrackTicks { get; set; } = 12;

		public int MaxMovesPerSecond { get; set; } = 6;

		public int CoyoteTicks { get; set; } = 4;

		public int GraceTicks { get; set; } = 30;

		public int MaxDurationTicks { get; set; } = 1800;

		public int SuddenDeathAfterTicks { get; set; } = 900;

		public int SuddenDeathEveryTicks { get; set; } = 6;

		public int MaxQueuedInputs { get; set; } = 3;

		public int MaxPlayers { get; set; } = 8;

		public int TickMs => 1000 / TickRate;

		public int BucketCapacity => TickRate + MaxMovesPerSecond - 1;

		public TileFallConfig Clone()
		{
			return (TileFallConfig)MemberwiseClone();
		}

		public void Validate()
		{
			if (Width < 3 || Width > 32)
			{
				throw new ArgumentException("Width must be 3..32");
			}
			if (Height < 3 || Height > 32)
			{
				throw new ArgumentException("Height must be 3..32");
			}
			if (TickRate < 5 || TickRate > 60 || 1000 % TickRate != 0)
			{
				throw new ArgumentException("TickRate must divide 1000 (5..60)");
			}
			if (CrackTicks < 1 || CrackTicks > 255)
			{
				throw new ArgumentException("CrackTicks must be 1..255");
			}
			if (MaxMovesPerSecond < 1 || MaxMovesPerSecond > TickRate)
			{
				throw new ArgumentException("MaxMovesPerSecond must be 1..TickRate");
			}
			if (GraceTicks < 0 || MaxDurationTicks < 1 || MaxDurationTicks > 65535)
			{
				throw new ArgumentException("bad durations");
			}
			if (SuddenDeathEveryTicks < 1)
			{
				throw new ArgumentException("SuddenDeathEveryTicks must be ≥ 1");
			}
			if (CoyoteTicks < 0 || CoyoteTicks > 20)
			{
				throw new ArgumentException("CoyoteTicks must be 0..20");
			}
			if (MaxQueuedInputs < 1 || MaxQueuedInputs > 16)
			{
				throw new ArgumentException("MaxQueuedInputs must be 1..16");
			}
			if (MaxPlayers < 1 || MaxPlayers > 8)
			{
				throw new ArgumentException("MaxPlayers must be 1..8");
			}
		}
	}
	public struct TileFallPlayerState : IEquatable<TileFallPlayerState>
	{
		public byte X;

		public byte Y;

		public bool Alive;

		public ushort LastSeq;

		public bool HasSeq;

		public bool Equals(TileFallPlayerState o)
		{
			if (X == o.X && Y == o.Y && Alive == o.Alive && LastSeq == o.LastSeq)
			{
				return HasSeq == o.HasSeq;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is TileFallPlayerState o)
			{
				return Equals(o);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return HashCode.Combine(X, Y, Alive, LastSeq, HasSeq);
		}
	}
	public sealed class TileFallSnapshot
	{
		public int Tick;

		public int Width;

		public int Height;

		public bool Finished;

		public TileState[] Tiles = Array.Empty<TileState>();

		public byte[] Timers = Array.Empty<byte>();

		public TileFallPlayerState[] Players = Array.Empty<TileFallPlayerState>();

		public TileState TileAt(int x, int y)
		{
			if (x >= 0 && y >= 0 && x < Width && y < Height)
			{
				return Tiles[y * Width + x];
			}
			return TileState.Fallen;
		}

		public int AliveCount()
		{
			int num = 0;
			TileFallPlayerState[] players = Players;
			for (int i = 0; i < players.Length; i++)
			{
				if (players[i].Alive)
				{
					num++;
				}
			}
			return num;
		}
	}
	public sealed class TileFallSim
	{
		private readonly struct QueuedMove
		{
			public ushort Seq { get; }

			public Dir Dir { get; }

			public QueuedMove(ushort seq, Dir dir)
			{
				Seq = seq;
				Dir = dir;
			}
		}

		private sealed class Player
		{
			public readonly ulong Id;

			public readonly Queue<QueuedMove> Queue = new Queue<QueuedMove>();

			public int X;

			public int Y;

			public bool Alive = true;

			public bool Left;

			public int EliminatedAtTick = -1;

			public int FallingSinceTick = -1;

			public int Tokens;

			public ushort LastSeq;

			public bool HasSeq;

			public ushort LastQueuedSeq;

			public bool HasQueuedSeq;

			public Player(ulong id, int x, int y, int tokens)
			{
				Id = id;
				X = x;
				Y = y;
				Tokens = tokens;
			}
		}

		private readonly TileFallConfig _cfg;

		private readonly TileState[] _tiles;

		private readonly byte[] _timers;

		private readonly Player[] _players;

		private readonly Rng _rng;

		private readonly List<int> _eliminatedThisTick = new List<int>();

		public TileFallConfig Config => _cfg;

		public int Tick { get; private set; }

		public bool IsFinished { get; private set; }

		public int PlayerCount => _players.Length;

		public IReadOnlyList<int> EliminatedThisTick => _eliminatedThisTick;

		public TileFallSim(TileFallConfig config, ulong seed, IReadOnlyList<ulong> playerIds)
		{
			_cfg = (config ?? throw new ArgumentNullException("config")).Clone();
			_cfg.Validate();
			if (playerIds == null || playerIds.Count < 1 || playerIds.Count > _cfg.MaxPlayers)
			{
				throw new ArgumentException("1.." + _cfg.MaxPlayers + " players required");
			}
			for (int i = 0; i < playerIds.Count; i++)
			{
				for (int j = 0; j < i; j++)
				{
					if (playerIds[i] == playerIds[j])
					{
						throw new ArgumentException("duplicate player id " + playerIds[i]);
					}
				}
			}
			_rng = new Rng(seed);
			_tiles = new TileState[_cfg.Width * _cfg.Height];
			_timers = new byte[_tiles.Length];
			_players = new Player[playerIds.Count];
			int[] array = SpawnPoints(_cfg.Width, _cfg.Height, playerIds.Count, _rng);
			for (int k = 0; k < _players.Length; k++)
			{
				_players[k] = new Player(playerIds[k], array[k] % _cfg.Width, array[k] / _cfg.Width, _cfg.BucketCapacity);
			}
		}

		public int SlotOf(ulong id)
		{
			for (int i = 0; i < _players.Length; i++)
			{
				if (_players[i].Id == id)
				{
					return i;
				}
			}
			return -1;
		}

		public ulong IdOf(int slot)
		{
			return _players[slot].Id;
		}

		public TileState GetTile(int x, int y)
		{
			return _tiles[y * _cfg.Width + x];
		}

		public int GetTimer(int x, int y)
		{
			return _timers[y * _cfg.Width + x];
		}

		public TileFallPlayerState GetPlayer(int slot)
		{
			Player player = _players[slot];
			return new TileFallPlayerState
			{
				X = (byte)player.X,
				Y = (byte)player.Y,
				Alive = player.Alive,
				LastSeq = player.LastSeq,
				HasSeq = player.HasSeq
			};
		}

		public int AliveCount()
		{
			int num = 0;
			Player[] players = _players;
			for (int i = 0; i < players.Length; i++)
			{
				if (players[i].Alive)
				{
					num++;
				}
			}
			return num;
		}

		public bool QueueInput(ulong playerId, ushort seq, Dir dir)
		{
			if (IsFinished || !dir.IsMove())
			{
				return false;
			}
			int num = SlotOf(playerId);
			if (num < 0)
			{
				return false;
			}
			Player player = _players[num];
			if (!player.Alive)
			{
				return false;
			}
			if (player.HasQueuedSeq && !SeqNewer(seq, player.LastQueuedSeq))
			{
				return false;
			}
			player.HasQueuedSeq = true;
			player.LastQueuedSeq = seq;
			if (player.Queue.Count >= _cfg.MaxQueuedInputs)
			{
				player.Queue.Dequeue();
			}
			player.Queue.Enqueue(new QueuedMove(seq, dir));
			return true;
		}

		public void RemovePlayer(ulong playerId)
		{
			int num = SlotOf(playerId);
			if (num >= 0)
			{
				Player player = _players[num];
				player.Left = true;
				if (player.Alive)
				{
					player.Alive = false;
					player.EliminatedAtTick = Tick;
					player.Queue.Clear();
				}
				CheckFinished();
			}
		}

		public void Step()
		{
			_eliminatedThisTick.Clear();
			if (IsFinished)
			{
				return;
			}
			Tick++;
			int width = _cfg.Width;
			Player[] players = _players;
			foreach (Player player in players)
			{
				if (!player.Alive)
				{
					continue;
				}
				player.Tokens = Math.Min(player.Tokens + _cfg.MaxMovesPerSecond, _cfg.BucketCapacity);
				if (player.Queue.Count != 0 && player.Tokens >= _cfg.TickRate)
				{
					QueuedMove queuedMove = player.Queue.Dequeue();
					player.Tokens -= _cfg.TickRate;
					player.LastSeq = queuedMove.Seq;
					player.HasSeq = true;
					int num = player.X + queuedMove.Dir.Dx();
					int num2 = player.Y + queuedMove.Dir.Dy();
					if (num >= 0 && num2 >= 0 && num < width && num2 < _cfg.Height && _tiles[num2 * width + num] != TileState.Fallen)
					{
						player.X = num;
						player.Y = num2;
					}
				}
			}
			for (int j = 0; j < _tiles.Length; j++)
			{
				if (_tiles[j] == TileState.Cracking)
				{
					_timers[j]--;
					if (_timers[j] == 0)
					{
						_tiles[j] = TileState.Fallen;
					}
				}
			}
			if (Tick > _cfg.GraceTicks)
			{
				players = _players;
				foreach (Player player2 in players)
				{
					if (player2.Alive)
					{
						Crack(player2.Y * width + player2.X);
					}
				}
			}
			if (Tick >= _cfg.SuddenDeathAfterTicks && (Tick - _cfg.SuddenDeathAfterTicks) % _cfg.SuddenDeathEveryTicks == 0)
			{
				int num3 = 0;
				for (int k = 0; k < _tiles.Length; k++)
				{
					if (_tiles[k] == TileState.Intact)
					{
						num3++;
					}
				}
				if (num3 > 0)
				{
					int num4 = _rng.Next(num3);
					for (int l = 0; l < _tiles.Length; l++)
					{
						if (_tiles[l] == TileState.Intact && num4-- == 0)
						{
							Crack(l);
							break;
						}
					}
				}
			}
			for (int m = 0; m < _players.Length; m++)
			{
				Player player3 = _players[m];
				if (!player3.Alive)
				{
					continue;
				}
				if (_tiles[player3.Y * width + player3.X] != TileState.Fallen)
				{
					player3.FallingSinceTick = -1;
					continue;
				}
				if (player3.FallingSinceTick < 0)
				{
					player3.FallingSinceTick = Tick;
				}
				if (Tick - player3.FallingSinceTick >= _cfg.CoyoteTicks)
				{
					player3.Alive = false;
					player3.EliminatedAtTick = Tick;
					player3.Queue.Clear();
					_eliminatedThisTick.Add(m);
				}
			}
			CheckFinished();
			if (!IsFinished && Tick >= _cfg.MaxDurationTicks)
			{
				IsFinished = true;
			}
		}

		public TileFallSnapshot Snapshot()
		{
			TileFallSnapshot tileFallSnapshot = new TileFallSnapshot
			{
				Tick = Tick,
				Width = _cfg.Width,
				Height = _cfg.Height,
				Finished = IsFinished,
				Tiles = (TileState[])_tiles.Clone(),
				Timers = (byte[])_timers.Clone(),
				Players = new TileFallPlayerState[_players.Length]
			};
			for (int i = 0; i < _players.Length; i++)
			{
				tileFallSnapshot.Players[i] = GetPlayer(i);
			}
			return tileFallSnapshot;
		}

		public List<Placement> Ranking()
		{
			int tieBreakTick = int.MaxValue;
			return AfterlifeArcade.Core.Ranking.Compute(_players, (Player p) => p.Id, (Player a, Player b) => (b.Alive ? tieBreakTick : b.EliminatedAtTick).CompareTo(a.Alive ? tieBreakTick : a.EliminatedAtTick), (Player p) => p.Left);
		}

		public static bool SeqNewer(ushort a, ushort b)
		{
			return (short)(a - b) > 0;
		}

		public static int[] SpawnPoints(int width, int height, int count, Rng rng)
		{
			List<int> list = new List<int>();
			int num = ((width >= 6 && height >= 6) ? 1 : 0);
			for (int i = num; i < height - num; i++)
			{
				for (int j = num; j < width - num; j++)
				{
					list.Add(i * width + j);
				}
			}
			rng.Shuffle(list);
			List<int> list2 = new List<int> { list[0] };
			while (list2.Count < count)
			{
				int item = -1;
				int num2 = -1;
				foreach (int item2 in list)
				{
					if (list2.Contains(item2))
					{
						continue;
					}
					int num3 = int.MaxValue;
					foreach (int item3 in list2)
					{
						int num4 = Math.Abs(item2 % width - item3 % width) + Math.Abs(item2 / width - item3 / width);
						if (num4 < num3)
						{
							num3 = num4;
						}
					}
					if (num3 > num2)
					{
						num2 = num3;
						item = item2;
					}
				}
				list2.Add(item);
			}
			return list2.ToArray();
		}

		private void Crack(int index)
		{
			if (_tiles[index] == TileState.Intact)
			{
				_tiles[index] = TileState.Cracking;
				_timers[index] = (byte)_cfg.CrackTicks;
			}
		}

		private void CheckFinished()
		{
			if (!IsFinished)
			{
				int num = AliveCount();
				if ((_players.Length >= 2) ? (num <= 1) : (num == 0))
				{
					IsFinished = true;
				}
			}
		}
	}
}
namespace AfterlifeArcade.Core.Side
{
	public enum SideToastKind
	{
		Bingo,
		Bet,
		RoundResults
	}
	public sealed class SideToast
	{
		public SideToastKind Kind { get; }

		public string Title { get; }

		public string Body { get; }

		public bool Good { get; }

		public SideToast(SideToastKind kind, string title, string body, bool good)
		{
			Kind = kind;
			Title = title;
			Body = body;
			Good = good;
		}
	}
	public readonly struct PendingBet
	{
		public byte MarketId { get; }

		public byte Option { get; }

		public byte StakeIndex { get; }

		public long SentAtMs { get; }

		public PendingBet(byte marketId, byte option, byte stakeIndex, long sentAtMs)
		{
			MarketId = marketId;
			Option = option;
			StakeIndex = stakeIndex;
			SentAtMs = sentAtMs;
		}
	}
	public sealed class SideClient
	{
		public const int FeedSize = 8;

		public const int RequestCooldownMs = 1000;

		public const int PendingTimeoutMs = 3000;

		private readonly Action<Message> _send;

		private readonly List<GameEventRow> _feed = new List<GameEventRow>();

		private readonly List<BingoWinMsg> _wins = new List<BingoWinMsg>();

		private readonly List<BetSettledMsg> _settled = new List<BetSettledMsg>();

		private readonly Queue<SideToast> _toasts = new Queue<SideToast>();

		private readonly List<PendingBet> _pending = new List<PendingBet>();

		private long _lastRequestMs = long.MinValue;

		private bool _resultsShown;

		public int LocalSlot { get; set; } = -1;

		public bool LocalDead { get; set; }

		public bool LineForTheLiving { get; set; } = true;

		public Func<int, string> NameOf { get; set; } = (int slot) => "Player " + slot;

		public byte RoundId { get; private set; }

		public BetBookMsg? Book { get; private set; }

		public BingoCard? Card { get; private set; }

		public ushort LastSeq { get; private set; }

		public IReadOnlyList<GameEventRow> Feed => _feed;

		public IReadOnlyList<BingoWinMsg> Wins => _wins;

		public IReadOnlyList<BetSettledMsg> SettledThisRound => _settled;

		public IReadOnlyList<PendingBet> Pending => _pending;

		public bool Available => Book != null;

		public bool BetsEnabled
		{
			get
			{
				if (Book != null)
				{
					return (Book.Flags & BookFlags.BetsEnabled) != 0;
				}
				return false;
			}
		}

		public bool BingoEnabled
		{
			get
			{
				if (Book != null)
				{
					return (Book.Flags & BookFlags.BingoEnabled) != 0;
				}
				return false;
			}
		}

		public bool BetsClosed
		{
			get
			{
				if (Book != null)
				{
					return (Book.Flags & (BookFlags.BetsClosed | BookFlags.RoundOver)) != 0;
				}
				return true;
			}
		}

		public bool RoundOver
		{
			get
			{
				if (Book != null)
				{
					return (Book.Flags & BookFlags.RoundOver) != 0;
				}
				return false;
			}
		}

		public GameEventRow? MyDeath { get; private set; }

		public int Requests { get; private set; }

		public int Gaps { get; private set; }

		public int Dropped { get; private set; }

		public int CardVersion { get; private set; }

		public int FeedVersion { get; private set; }

		public int MyBalance => (Book?.RowOf(LocalSlot)?.Balance).GetValueOrDefault();

		public SideClient(Action<Message> send)
		{
			_send = send ?? throw new ArgumentNullException("send");
		}

		public SideToast? TakeToast()
		{
			if (_toasts.Count <= 0)
			{
				return null;
			}
			return _toasts.Dequeue();
		}

		public void Reset()
		{
			Book = null;
			ResetRound(0);
			_lastRequestMs = long.MinValue;
			_toasts.Clear();
		}

		public bool RequestSnapshot(SideWhich which, long localMs)
		{
			if (which == SideWhich.None)
			{
				return false;
			}
			if (_lastRequestMs != long.MinValue && localMs - _lastRequestMs < 1000)
			{
				return false;
			}
			_las