Decompiled source of WKMapLoader v1.0.5

WKMapLoader.dll

Decompiled 5 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Rendering;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("WKMapLoader")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyInformationalVersion("1.0.4")]
[assembly: AssemblyProduct("WKMapLoader")]
[assembly: AssemblyTitle("WKMapLoader")]
[assembly: AssemblyVersion("1.0.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WKMapLoader
{
	internal static class BoomCourse
	{
		private const int Stages = 5;

		private const float Gap = 3f;

		private const float T = 0.5f;

		private const float L = 5f;

		private const float P = 7f;

		private const float W = 9f;

		private const float DefaultPerBar = 3f;

		public static bool TestMode;

		public const string NoteText = "Use boom-bars to push off the walls up to next lvl. Reach the top.";

		public static string Generate(string mapsDir, float perBar)
		{
			float ground = perBar;
			float air = perBar;
			string how = ((perBar > 0f) ? $"set by hand: {perBar:0.0} m per boom-bar" : null);
			bool flag = perBar <= 0f && BoomLog.PerBarHeights(out ground, out air, out how);
			if (how == null)
			{
				ground = (air = 3f);
				how = $"default {3f:0.0} m per boom-bar (no attempts logged)";
			}
			StringBuilder obj = new StringBuilder();
			StringBuilder mtl = new StringBuilder();
			CultureInfo ic = CultureInfo.InvariantCulture;
			int vcount = 0;
			obj.AppendLine("# Boom course - explosive rebar wall tower (WK Map Loader 'boomcourse')");
			obj.AppendLine("mtllib boom_course.mtl");
			Mat("post", 0.25f, 0.25f, 0.25f);
			Mat("button", 0.8f, 0.05f, 0.03f);
			Mat("lamp", 1f, 0.85f, 0.6f);
			Mat("button_yellow", 0.9f, 0.75f, 0.05f);
			Mat("save_green", 0.1f, 0.75f, 0.2f);
			Mat("win_green", 0.15f, 0.9f, 0.3f);
			Mat("wall", 0.42f, 0.4f, 0.38f);
			Mat("ground", 0.3f, 0.28f, 0.26f);
			Box("ground", "ground", -7f, -1f, -11f, 7f, 0f, 6f);
			Box("spawn", "post", -0.5f, 0.5f, -8.5f, 0.5f, 1.5f, -7.5f);
			Button("checkpoint_6_rebar_explosive", "button", -3f, 0f, -9f);
			Button("buddy_button", "button_yellow", -1.8f, 0f, -9f);
			Box("note", "post", 1f, 0.05f, -8f, 1.4f, 0.15f, -7.6f);
			float num = 0f;
			if (TestMode)
			{
				ground = (air = 0f);
				how = "test walls (boomcourse test)";
				Box("wall_test_a", "wall", -2.5f, 0f, -4f, -2f, 300f, 4f);
				Box("wall_test_b", "wall", 2f, 0f, -4f, 2.5f, 300f, 4f);
				Mat("mark", 0.9f, 0.9f, 0.9f);
				for (int i = 5; (float)i < 300f; i += 5)
				{
					float num2 = ((i % 10 == 0) ? 0.12f : 0.05f);
					Box($"deco_mark_a_{i}", "mark", -1.99f, (float)i - num2, -4f, -1.97f, (float)i + num2, 4f);
					Box($"deco_mark_b_{i}", "mark", 1.97f, (float)i - num2, -4f, 1.99f, (float)i + num2, 4f);
				}
				for (int j = 10; (float)j < 300f; j += 20)
				{
					Box($"light_25.{j:000}", "lamp", -0.15f, j, 5f, 0.15f, (float)j + 0.3f, 5.3f);
				}
				num = 300f;
			}
			float num3 = 0f;
			int num4 = 1;
			for (int k = 0; k < ((!TestMode) ? 5 : 0); k++)
			{
				int num5 = k + 2;
				float num6 = WallHeight(num5);
				float num7 = (float)k / 4f;
				string text = "platform_" + (k + 1);
				Mat(text, 0.75f - 0.4f * num7, 0.5f + 0.1f * num7, 0.3f + 0.45f * num7);
				Box($"wall_{k + 1}_a", "wall", num3 - (float)num4 * 3f / 2f, num, -2.5f, num3 - (float)num4 * 2f, num + num6, 2.5f);
				Box($"wall_{k + 1}_b", "wall", num3 + (float)num4 * 3f / 2f, num, -2.5f, num3 + (float)num4 * 2f, num + num6 - 0.5f, 2.5f);
				Box($"light_20.{k:000}", "lamp", num3 - 0.15f, num + num6 * 0.6f, 3.5f, num3 + 0.15f, num + num6 * 0.6f + 0.3f, 3.8f);
				float num8 = num + num6;
				Box(text, text, num3 + (float)num4 * 3f / 2f, num8 - 0.5f, -4.5f, num3 + (float)num4 * 9f, num8, 4.5f);
				float num9 = num3 + (float)num4 * 5.5f;
				Box($"light_16.{k:000}", "lamp", num9 - 0.15f, num8 + 5f, -0.15f, num9 + 0.15f, num8 + 5.3f, 0.15f);
				if (k < 4)
				{
					Button($"checkpoint_{num5 + 1}_rebar_explosive", "button", num9, num8, 3.6f);
					if (k == 1)
					{
						Box("save_pad_3", "save_green", num9 - 1f, num8, -4.3f, num9 + 1f, num8 + 0.08f, -2.8f);
					}
				}
				else
				{
					Box("exit", "post", num9 - 0.5f, num8 + 0.5f, -1f, num9 + 0.5f, num8 + 1.5f, 0f);
					Box("smooth_post_win", "post", num9 - 0.12f, num8, 2.4f, num9 + 0.12f, num8 + 1f, 2.64f);
					Oval("win_button", "win_green", num9, num8 + 1f, 2.52f, 0.35f, 0.22f, 0.12f);
				}
				num3 = num9;
				num = num8;
				num4 = -num4;
			}
			string path = Path.Combine(mapsDir, "boom_course.obj");
			File.WriteAllText(path, obj.ToString());
			File.WriteAllText(Path.ChangeExtension(path, ".mtl"), mtl.ToString());
			File.WriteAllText(Path.Combine(mapsDir, "boom_course_note.txt"), "Use boom-bars to push off the walls up to next lvl. Reach the top.");
			IEnumerable<string> values = from b in Enumerable.Range(2, 5)
				select WallHeight(b).ToString("0.0", ic);
			File.WriteAllText(Path.Combine(mapsDir, "boom_course_report.txt"), (flag ? "calibrated from logged attempts" : "NOT calibrated") + string.Format("\n{0}\nwall heights: {1} m\ntop: {2:0.0} m\n", how, string.Join(", ", values), num));
			Plugin.Say(string.Format("Boom course rebuilt - {0}. Walls {1} m, top at {2:0} m.", how, string.Join(", ", values), num));
			return "boom_course";
			void Box(string name, string mat, float x0, float y0, float z0, float x1, float y1, float z1)
			{
				if (x0 > x1)
				{
					float num10 = x0;
					x0 = x1;
					x1 = num10;
				}
				obj.AppendLine("o " + name);
				obj.AppendLine("usemtl " + mat);
				float[][] array = new float[8][]
				{
					new float[3] { x0, y0, z1 },
					new float[3] { x1, y0, z1 },
					new float[3] { x1, y1, z1 },
					new float[3] { x0, y1, z1 },
					new float[3] { x0, y0, z0 },
					new float[3] { x1, y0, z0 },
					new float[3] { x1, y1, z0 },
					new float[3] { x0, y1, z0 }
				};
				foreach (float[] array2 in array)
				{
					obj.AppendLine("v " + F(0f - array2[0]) + " " + F(array2[1]) + " " + F(array2[2]));
				}
				int[][] array3 = new int[6][]
				{
					new int[4] { 2, 1, 4, 3 },
					new int[4] { 5, 6, 7, 8 },
					new int[4] { 1, 5, 8, 4 },
					new int[4] { 6, 2, 3, 7 },
					new int[4] { 3, 4, 8, 7 },
					new int[4] { 1, 2, 6, 5 }
				};
				foreach (int[] source in array3)
				{
					obj.AppendLine("f " + string.Join(" ", source.Select((int num11) => (num11 + vcount).ToString(ic))));
				}
				vcount += 8;
			}
			void Button(string name, string mat, float x, float y, float z)
			{
				Box("smooth_post_" + name, "post", x - 0.12f, y, z - 0.12f, x + 0.12f, y + 1f, z + 0.12f);
				Box(name, mat, x - 0.3f, y + 1f, z - 0.3f, x + 0.3f, y + 1.25f, z + 0.3f);
			}
			string F(float v)
			{
				return v.ToString("0.###", ic);
			}
			void Mat(string name, float r, float g, float b)
			{
				mtl.AppendLine("newmtl " + name + "\nKd " + F(r) + " " + F(g) + " " + F(b) + "\n");
			}
			void Oval(string name, string mat, float cx, float y0, float cz, float rx, float rz, float h, int seg = 16)
			{
				obj.AppendLine("o " + name);
				obj.AppendLine("usemtl " + mat);
				for (int l = 0; l < 2; l++)
				{
					for (int m = 0; m < seg; m++)
					{
						float num10 = (float)m * MathF.PI * 2f / (float)seg;
						obj.AppendLine("v " + F(0f - (cx + Mathf.Cos(num10) * rx)) + " " + F(y0 + (float)l * h) + " " + F(cz + Mathf.Sin(num10) * rz));
					}
				}
				obj.AppendLine("v " + F(0f - cx) + " " + F(y0) + " " + F(cz));
				obj.AppendLine("v " + F(0f - cx) + " " + F(y0 + h) + " " + F(cz));
				int b0 = vcount;
				int num11 = b0 + 2 * seg + 1;
				int num12 = b0 + 2 * seg + 2;
				for (int n = 0; n < seg; n++)
				{
					obj.AppendLine($"f {B(n + 1)} {Tp(n + 1)} {Tp(n)} {B(n)}");
					obj.AppendLine($"f {num12} {Tp(n)} {Tp(n + 1)}");
					obj.AppendLine($"f {num11} {B(n + 1)} {B(n)}");
				}
				vcount += 2 * seg + 2;
				int B(int num13)
				{
					return b0 + 1 + num13 % seg;
				}
				int Tp(int num13)
				{
					return b0 + 1 + seg + num13 % seg;
				}
			}
			float WallHeight(int bars)
			{
				return ground + (float)(bars - 1) * air;
			}
		}
	}
	internal static class BoomLog
	{
		public class Attempt
		{
			public bool midAir;

			public float blastDist;

			public float angleFromUp;

			public float launchSpeed;

			public float rise;

			public float sideways;

			public float airtime;
		}

		public static readonly List<Attempt> Attempts = new List<Attempt>();

		private static Attempt cur;

		private static Vector3 start;

		private static Vector3 lastPos;

		private static float startTime;

		private static float apex;

		private static bool sawAir;

		private static string csvPath;

		public static void Init(string mapsDir)
		{
			csvPath = Path.Combine(mapsDir, "boom_log.csv");
		}

		public static void OnExplosion(UT_Explosion ex)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			ENT_Player playerObject = ENT_Player.playerObject;
			if (!((Object)(object)playerObject == (Object)null) && !((Object)(object)ex == (Object)null) && !playerObject.noclip)
			{
				Vector3 position = ((Component)ex).transform.position;
				Vector3 position2 = ((Component)playerObject).transform.position;
				float num = Vector3.Distance(position, position2);
				if (!(num > ex.maxDistance + 1f))
				{
					Finish(playerObject);
					Vector3 val = position2 - position;
					Vector3 normalized = ((Vector3)(ref val)).normalized;
					cur = new Attempt
					{
						midAir = !playerObject.IsGrounded(false),
						blastDist = num,
						angleFromUp = Vector3.Angle(normalized, Vector3.up)
					};
					start = (lastPos = position2);
					apex = position2.y;
					startTime = Time.time;
					sawAir = cur.midAir;
				}
			}
		}

		public static void Track(ENT_Player p)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0057: Unknown result type (might be due to invalid IL or missing references)
			if (cur != null && !((Object)(object)p == (Object)null))
			{
				Vector3 position = ((Component)p).transform.position;
				float deltaTime = Time.deltaTime;
				if (Time.time - startTime < 0.25f && deltaTime > 0f)
				{
					Attempt attempt = cur;
					float launchSpeed = cur.launchSpeed;
					Vector3 val = position - lastPos;
					attempt.launchSpeed = Mathf.Max(launchSpeed, ((Vector3)(ref val)).magnitude / deltaTime);
				}
				lastPos = position;
				apex = Mathf.Max(apex, position.y);
				bool flag = p.IsGrounded(false);
				if (!flag)
				{
					sawAir = true;
				}
				if ((sawAir && flag && Time.time - startTime > 0.15f) || p.IsHanging() || Time.time - startTime > 6f)
				{
					Finish(p);
				}
			}
		}

		private static void Finish(ENT_Player p)
		{
			//IL_001e: 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_0023: 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_0054: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			if (cur == null)
			{
				return;
			}
			Vector3 val = (((Object)(object)p != (Object)null) ? ((Component)p).transform.position : lastPos);
			cur.rise = apex - start.y;
			Attempt attempt = cur;
			Vector2 val2 = new Vector2(val.x - start.x, val.z - start.z);
			attempt.sideways = ((Vector2)(ref val2)).magnitude;
			cur.airtime = Time.time - startTime;
			Attempt attempt2 = cur;
			cur = null;
			if (attempt2.launchSpeed < 0.5f && attempt2.rise < 0.3f)
			{
				return;
			}
			Attempts.Add(attempt2);
			Plugin.Say(string.Format("Boom #{0} ({1}): blast {2:0.0} m away, push {3:0}° from straight up ", Attempts.Count, attempt2.midAir ? "mid-air" : "ground", attempt2.blastDist, attempt2.angleFromUp) + $"-> {attempt2.rise:0.0} m up, {attempt2.sideways:0.0} m sideways, {attempt2.launchSpeed:0.0} m/s");
			try
			{
				CultureInfo invariantCulture = CultureInfo.InvariantCulture;
				if (!File.Exists(csvPath))
				{
					File.WriteAllText(csvPath, "time,mode,blast_dist_m,push_angle_from_up_deg,launch_speed_mps,rise_m,sideways_m,airtime_s\n");
				}
				File.AppendAllText(csvPath, string.Join(",", DateTime.Now.ToString("s", invariantCulture), attempt2.midAir ? "air" : "ground", attempt2.blastDist.ToString("0.00", invariantCulture), attempt2.angleFromUp.ToString("0", invariantCulture), attempt2.launchSpeed.ToString("0.00", invariantCulture), attempt2.rise.ToString("0.00", invariantCulture), attempt2.sideways.ToString("0.00", invariantCulture), attempt2.airtime.ToString("0.00", invariantCulture)) + "\n");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("boom_log.csv: " + ex.Message));
			}
		}

		private static float Percentile(IEnumerable<float> values, float share)
		{
			List<float> list = values.OrderBy((float x) => x).ToList();
			if (list.Count == 0)
			{
				return 0f;
			}
			return list[Mathf.Clamp(Mathf.FloorToInt(share * (float)(list.Count - 1) + 0.5f), 0, list.Count - 1)];
		}

		public static bool PerBarHeights(out float ground, out float air, out string how)
		{
			List<Attempt> source = Attempts.Where((Attempt a) => a.rise > 1f).ToList();
			List<float> list = (from a in source
				where !a.midAir
				select a.rise).ToList();
			List<float> list2 = (from a in source
				where a.midAir
				select a.rise).ToList();
			ground = ((list.Count >= 2) ? Percentile(list, 0.25f) : 0f);
			air = ((list2.Count >= 2) ? Percentile(list2, 0.25f) : 0f);
			if (ground <= 0f && air > 0f)
			{
				ground = air;
			}
			if (air <= 0f && ground > 0f)
			{
				air = ground;
			}
			how = $"{list.Count} ground + {list2.Count} mid-air attempts; 75% of them reached {ground:0.0} m (ground) / {air:0.0} m (mid-air)";
			return ground > 0f;
		}

		public static void Report()
		{
			if (Attempts.Count == 0)
			{
				Plugin.Say("No boom attempts logged yet - blast yourself with explosive rebar.");
				return;
			}
			StringBuilder stringBuilder = new StringBuilder($"Boom report ({Attempts.Count} attempts, also in boom_log.csv):\n");
			bool[] array = new bool[2] { false, true };
			foreach (bool air in array)
			{
				List<Attempt> list = Attempts.Where((Attempt a) => a.midAir == air).ToList();
				if (list.Count == 0)
				{
					continue;
				}
				stringBuilder.AppendLine(air ? "  MID-AIR blasts" : "  GROUND blasts");
				int lo;
				for (lo = 0; lo < 180; lo += 15)
				{
					List<Attempt> list2 = list.Where((Attempt a) => a.angleFromUp >= (float)lo && a.angleFromUp < (float)(lo + 15)).ToList();
					if (list2.Count != 0)
					{
						stringBuilder.AppendLine($"    push {lo,3}-{lo + 15,3}° from up: {list2.Count,2}x  up avg {list2.Average((Attempt a) => a.rise):0.0} / best {list2.Max((Attempt a) => a.rise):0.0} m, " + $"sideways avg {list2.Average((Attempt a) => a.sideways):0.0} m, speed avg {list2.Average((Attempt a) => a.launchSpeed):0.0} m/s");
					}
				}
				List<Attempt> list3 = list.Where((Attempt a) => a.rise > 1f).ToList();
				if (list3.Count > 0)
				{
					stringBuilder.AppendLine($"    blast distance: launches that worked were {list3.Min((Attempt a) => a.blastDist):0.0}-{list3.Max((Attempt a) => a.blastDist):0.0} m from the blast");
				}
			}
			if (PerBarHeights(out var _, out var _, out var how))
			{
				stringBuilder.AppendLine("  Course sizing: " + how);
			}
			string[] array2 = stringBuilder.ToString().TrimEnd().Split('\n');
			for (int i = 0; i < array2.Length; i++)
			{
				Plugin.Say(array2[i]);
			}
		}

		public static void Clear()
		{
			Attempts.Clear();
			cur = null;
			Plugin.Say("Boom log cleared (boom_log.csv kept).");
		}
	}
	[HarmonyPatch(typeof(UT_Explosion), "Explode")]
	internal static class ExplosionLogPatch
	{
		private static void Prefix(UT_Explosion __instance)
		{
			try
			{
				BoomLog.OnExplosion(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Boom log: " + ex.Message));
			}
		}
	}
	internal static class CustomMenu
	{
		private const string TabName = "CUSTOM";

		private const string ModeDatabaseId = "wkcustommodes";

		internal static readonly Dictionary<M_Gamemode, string> ModeToMap = new Dictionary<M_Gamemode, string>();

		private static WKAssetDatabase modeDb;

		private static readonly HashSet<UI_TabGroup> injected = new HashSet<UI_TabGroup>();

		public static void TryInject(UI_TabGroup group)
		{
			try
			{
				Inject(group);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("CUSTOM tab injection failed: " + ex));
			}
		}

		private static void Inject(UI_TabGroup group)
		{
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0410: Unknown result type (might be due to invalid IL or missing references)
			//IL_0407: Unknown result type (might be due to invalid IL or missing references)
			//IL_0415: Unknown result type (might be due to invalid IL or missing references)
			//IL_042a: Unknown result type (might be due to invalid IL or missing references)
			//IL_042f: Unknown result type (might be due to invalid IL or missing references)
			//IL_04af: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0732: Unknown result type (might be due to invalid IL or missing references)
			//IL_0737: Unknown result type (might be due to invalid IL or missing references)
			//IL_0742: Unknown result type (might be due to invalid IL or missing references)
			//IL_074a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0752: Unknown result type (might be due to invalid IL or missing references)
			//IL_075a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0767: Expected O, but got Unknown
			//IL_07a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ac: Expected O, but got Unknown
			//IL_06b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_069b: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)group == (Object)null || group.tabs == null || injected.Contains(group))
			{
				return;
			}
			string text = string.Join("/", group.tabs.Select((Tab t) => t.name));
			Tab tmpl = ((IEnumerable<Tab>)group.tabs).FirstOrDefault((Func<Tab, bool>)((Tab x) => x.name != null && x.name.ToLowerInvariant().Contains("event"))) ?? ((IEnumerable<Tab>)group.tabs).FirstOrDefault((Func<Tab, bool>)((Tab x) => x.name != null && x.name.ToLowerInvariant().Contains("challenge")));
			if (tmpl == null || (Object)(object)tmpl.button == (Object)null || (Object)(object)tmpl.tabObject == (Object)null)
			{
				Plugin.Log.LogDebug((object)("Tab group '" + ((Object)group).name + "' [" + text + "] is not the play screen - skipped."));
				return;
			}
			injected.Add(group);
			Scene scene = ((Component)group).gameObject.scene;
			Plugin.Log.LogInfo((object)("Menu tabs: " + text));
			List<string> list = (from g in Plugin.Instance.AllMapFiles().GroupBy<string, string>(Path.GetFileNameWithoutExtension, StringComparer.OrdinalIgnoreCase)
				select g.First() into p
				orderby Path.GetFileNameWithoutExtension(p)
				select p).ToList();
			Tab val = ((IEnumerable<Tab>)group.tabs).FirstOrDefault((Func<Tab, bool>)((Tab t) => string.Equals(t.name?.Trim(), "custom", StringComparison.OrdinalIgnoreCase)));
			if (val != null && (Object)(object)val.button != (Object)null && (Object)(object)val.tabObject != (Object)null)
			{
				UseBuiltInTab(val, list, scene);
				return;
			}
			bool activeSelf = tmpl.tabObject.activeSelf;
			tmpl.tabObject.SetActive(false);
			GameObject val2 = Object.Instantiate<GameObject>(tmpl.tabObject, tmpl.tabObject.transform.parent);
			tmpl.tabObject.SetActive(activeSelf);
			((Object)val2).name = "CUSTOM Page";
			UI_Gamemode_Button[] componentsInChildren = val2.GetComponentsInChildren<UI_Gamemode_Button>(true);
			UI_Gamemode_Button val3 = componentsInChildren.FirstOrDefault();
			if ((Object)(object)val3 == (Object)null)
			{
				UI_Gamemode_Button val4 = ((IEnumerable<UI_Gamemode_Button>)Resources.FindObjectsOfTypeAll<UI_Gamemode_Button>()).FirstOrDefault((Func<UI_Gamemode_Button, bool>)((UI_Gamemode_Button c) => ((Component)c).gameObject.scene == scene));
				if ((Object)(object)val4 == (Object)null)
				{
					Plugin.Log.LogWarning((object)"No level card found to copy - CUSTOM tab not added.");
					Object.Destroy((Object)(object)val2);
					return;
				}
				val3 = Object.Instantiate<UI_Gamemode_Button>(val4, val2.transform);
			}
			UI_Gamemode_Button[] array = componentsInChildren;
			foreach (UI_Gamemode_Button val5 in array)
			{
				if ((Object)(object)val5 != (Object)(object)val3)
				{
					Object.Destroy((Object)(object)((Component)val5).gameObject);
				}
			}
			TMP_Text[] componentsInChildren2 = val2.GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val6 in componentsInChildren2)
			{
				if ((Object)(object)((Component)val6).GetComponentInParent<UI_Gamemode_Button>(true) == (Object)null && val6.text.Trim().Equals(tmpl.name.Trim(), StringComparison.OrdinalIgnoreCase))
				{
					val6.text = "CUSTOM";
				}
			}
			M_Gamemode gamemode = val3.gamemode;
			ModeToMap.Clear();
			RectTransform component = ((Component)val3).GetComponent<RectTransform>();
			bool flag = (Object)(object)((Component)((Component)val3).transform.parent).GetComponent<LayoutGroup>() != (Object)null;
			Vector2 val7 = (((Object)(object)component != (Object)null) ? component.anchoredPosition : Vector2.zero);
			float num2;
			Rect rect;
			if (!((Object)(object)component != (Object)null))
			{
				num2 = 300f;
			}
			else
			{
				rect = component.rect;
				num2 = ((Rect)(ref rect)).width;
			}
			float num3 = num2 + 24f;
			Selectable val8 = null;
			for (int num4 = 0; num4 < list.Count; num4++)
			{
				UI_Gamemode_Button val9 = ((num4 == 0) ? val3 : Object.Instantiate<UI_Gamemode_Button>(val3, ((Component)val3).transform.parent));
				((Object)val9).name = "Card " + Path.GetFileNameWithoutExtension(list[num4]);
				val9.gamemode = MakeMode(gamemode, list[num4]);
				if (!flag)
				{
					RectTransform component2 = ((Component)val9).GetComponent<RectTransform>();
					if (component2 != null)
					{
						component2.anchoredPosition = val7 + new Vector2(num3 * (float)num4, 0f);
					}
				}
				if ((Object)(object)val8 == (Object)null)
				{
					val8 = ((Component)val9).GetComponent<Selectable>();
				}
			}
			if (list.Count == 0)
			{
				((Component)val3).gameObject.SetActive(false);
			}
			RegisterModes();
			Button val10 = (from t in @group.tabs
				select t.button into b
				where (Object)(object)b != (Object)null && (Object)(object)((Component)b).transform.parent == (Object)(object)((Component)tmpl.button).transform.parent
				orderby ((Component)b).transform.GetSiblingIndex()
				select b).Last();
			GameObject val11 = Object.Instantiate<GameObject>(((Component)tmpl.button).gameObject, ((Component)tmpl.button).transform.parent);
			((Object)val11).name = "CUSTOM Tab";
			val11.transform.SetSiblingIndex(((Component)val10).transform.GetSiblingIndex() + 1);
			if ((Object)(object)((Component)val11.transform.parent).GetComponent<LayoutGroup>() == (Object)null)
			{
				List<RectTransform> list2 = (from t in @group.tabs
					select t.button into b
					where (Object)(object)b != (Object)null
					select ((Component)b).GetComponent<RectTransform>() into rt
					orderby rt.anchoredPosition.x
					select rt).ToList();
				RectTransform val12 = list2.Last();
				float num5;
				if (list2.Count <= 1)
				{
					rect = val12.rect;
					num5 = ((Rect)(ref rect)).width + 16f;
				}
				else
				{
					num5 = val12.anchoredPosition.x - list2[list2.Count - 2].anchoredPosition.x;
				}
				float num6 = num5;
				val11.GetComponent<RectTransform>().anchoredPosition = val12.anchoredPosition + new Vector2(num6, 0f);
			}
			TMP_Text componentInChildren = val11.GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = "CUSTOM";
			}
			Button component3 = val11.GetComponent<Button>();
			group.tabs.Add(new Tab
			{
				name = "CUSTOM",
				button = component3,
				tabObject = val2,
				buttonText = componentInChildren,
				firstSelect = val8
			});
			if (typeof(UI_TabGroup).GetField("group", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(group) != null)
			{
				((UnityEvent)component3.onClick).AddListener((UnityAction)delegate
				{
					group.SelectTab("CUSTOM");
				});
			}
			val2.SetActive(false);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(KeepLabel(componentInChildren));
			}
			Plugin.Log.LogInfo((object)string.Format("{0} tab added with {1} map card(s): {2}", "CUSTOM", list.Count, string.Join(", ", list.Select(Path.GetFileNameWithoutExtension))));
		}

		private static void UseBuiltInTab(Tab tab, List<string> maps, Scene scene)
		{
			//IL_0007: 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)
			tab.onlyDev = false;
			((Component)tab.button).gameObject.SetActive(true);
			TMP_Text val = (((Object)(object)tab.buttonText != (Object)null) ? tab.buttonText : ((Component)tab.button).GetComponentInChildren<TMP_Text>(true));
			if ((Object)(object)val != (Object)null)
			{
				val.text = "CUSTOM";
				((MonoBehaviour)Plugin.Instance).StartCoroutine(KeepLabel(val));
			}
			GameObject tabObject = tab.tabObject;
			UI_Gamemode_Button[] componentsInChildren = tabObject.GetComponentsInChildren<UI_Gamemode_Button>(true);
			UI_Gamemode_Button val2 = ((IEnumerable<UI_Gamemode_Button>)componentsInChildren).FirstOrDefault((Func<UI_Gamemode_Button, bool>)((UI_Gamemode_Button c) => (Object)(object)((Component)c).transform.parent != (Object)null && ((Object)((Component)c).transform.parent).name == "Content")) ?? componentsInChildren.FirstOrDefault();
			if ((Object)(object)val2 == (Object)null)
			{
				UI_Gamemode_Button val3 = ((IEnumerable<UI_Gamemode_Button>)Resources.FindObjectsOfTypeAll<UI_Gamemode_Button>()).FirstOrDefault((Func<UI_Gamemode_Button, bool>)((UI_Gamemode_Button c) => ((Component)c).gameObject.scene == scene));
				if ((Object)(object)val3 == (Object)null)
				{
					Plugin.Log.LogWarning((object)"No level card found to copy - CUSTOM tab left empty.");
					return;
				}
				val2 = Object.Instantiate<UI_Gamemode_Button>(val3, tabObject.transform);
			}
			foreach (Transform item in ((IEnumerable)((Component)val2).transform.parent).Cast<Transform>().ToList())
			{
				if ((Object)(object)item != (Object)(object)((Component)val2).transform)
				{
					Object.Destroy((Object)(object)((Component)item).gameObject);
				}
			}
			tab.firstSelect = FillCards(val2, maps);
			Plugin.Log.LogInfo((object)string.Format("Built-in Custom tab enabled with {0} map card(s): {1}", maps.Count, string.Join(", ", maps.Select(Path.GetFileNameWithoutExtension))));
		}

		private static Selectable FillCards(UI_Gamemode_Button cardTemplate, List<string> maps)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			M_Gamemode gamemode = cardTemplate.gamemode;
			ModeToMap.Clear();
			RectTransform component = ((Component)cardTemplate).GetComponent<RectTransform>();
			bool flag = (Object)(object)((Component)((Component)cardTemplate).transform.parent).GetComponent<LayoutGroup>() != (Object)null;
			Vector2 val = (((Object)(object)component != (Object)null) ? component.anchoredPosition : Vector2.zero);
			float num;
			if (!((Object)(object)component != (Object)null))
			{
				num = 300f;
			}
			else
			{
				Rect rect = component.rect;
				num = ((Rect)(ref rect)).width;
			}
			float num2 = num + 24f;
			Selectable val2 = null;
			for (int i = 0; i < maps.Count; i++)
			{
				UI_Gamemode_Button val3 = ((i == 0) ? cardTemplate : Object.Instantiate<UI_Gamemode_Button>(cardTemplate, ((Component)cardTemplate).transform.parent));
				((Object)val3).name = "Card " + Path.GetFileNameWithoutExtension(maps[i]);
				val3.gamemode = MakeMode(gamemode, maps[i]);
				((Component)val3).gameObject.SetActive(true);
				if (!flag)
				{
					RectTransform component2 = ((Component)val3).GetComponent<RectTransform>();
					if (component2 != null)
					{
						component2.anchoredPosition = val + new Vector2(num2 * (float)i, 0f);
					}
				}
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = ((Component)val3).GetComponent<Selectable>();
				}
			}
			if (maps.Count == 0)
			{
				((Component)cardTemplate).gameObject.SetActive(false);
			}
			RegisterModes();
			return val2;
		}

		private static IEnumerator KeepLabel(TMP_Text label)
		{
			float until = Time.realtimeSinceStartup + 5f;
			while ((Object)(object)label != (Object)null && Time.realtimeSinceStartup < until)
			{
				if (label.text != "CUSTOM")
				{
					label.text = "CUSTOM";
				}
				yield return null;
			}
		}

		private static M_Gamemode MakeMode(M_Gamemode baseMode, string mapPath)
		{
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(mapPath);
			string text = Pretty(fileNameWithoutExtension);
			string directoryName = Path.GetDirectoryName(mapPath);
			M_Gamemode val = ((IEnumerable<M_Gamemode>)CL_AssetManager.GetFullCombinedAssetDatabase().gamemodeAssets).FirstOrDefault((Func<M_Gamemode, bool>)((M_Gamemode g) => (Object)(object)g != (Object)null && (((Object)g).name == "GM_Campaign" || g.gamemodeName == "Campaign")));
			if ((Object)(object)val != (Object)null)
			{
				baseMode = val;
			}
			M_Gamemode val2 = Object.Instantiate<M_Gamemode>(baseMode);
			((Object)val2).name = "WKMap_" + fileNameWithoutExtension;
			((Object)val2).hideFlags = (HideFlags)32;
			val2.gamemodeName = "Custom Map - " + text;
			string path = Path.Combine(directoryName, fileNameWithoutExtension + ".txt");
			string text2 = (File.Exists(path) ? File.ReadAllText(path).Replace("\r\n", "\n") : null);
			string text3 = text;
			if (text2 != null && text2.StartsWith("title:", StringComparison.OrdinalIgnoreCase))
			{
				int num = text2.IndexOf('\n');
				text3 = ((num < 0) ? text2.Substring(6) : text2.Substring(6, num - 6)).Trim();
				text2 = ((num < 0) ? "" : text2.Substring(num + 1));
				if (text3.Length == 0)
				{
					text3 = text;
				}
			}
			val2.useCustomCapsuleName = true;
			val2.capsuleName = text3.ToUpperInvariant();
			val2.modeDescription = text2 ?? ("Custom map: " + text + ".\nBuilt in a 3D tool, loaded by WK Map Loader.\nFall off = back to the start. No mass.");
			val2.introText = text3;
			val2.newGameText = "Play";
			Plugin.Log.LogInfo((object)("[card] '" + text + "' based on '" + baseMode.gamemodeName + "' (scene '" + baseMode.gamemodeScene + "', objects: " + string.Join(", ", from o in baseMode.gamemodeObjects ?? new List<GameObject>()
				where (Object)(object)o != (Object)null
				select ((Object)o).name) + ")"));
			val2.gamemodeScene = "Game-Main";
			val2.modeType = (GameType)4;
			val2.playlistLevelAssets = new List<LevelAssetHolder>();
			val2.playlistLevels = new List<M_Level>();
			val2.regions = new List<M_Region>();
			val2.startItems = new List<SpawnItem>();
			val2.gamemodeSettings = new List<GamemodeSetting>();
			val2.useGamemodeSettings = false;
			val2.gamemodeModule = null;
			val2.modeTags = new List<string> { "custommap" };
			val2.isEndless = false;
			val2.hasRevives = false;
			val2.hasPerks = false;
			val2.allowAchievements = false;
			val2.allowHeightAchievements = false;
			val2.allowLeaderboardScoring = false;
			val2.allowCompetitive = false;
			val2.allowHardmode = false;
			val2.allowIronKnuckle = false;
			val2.steamLeaderboardName = "";
			val2.roachBankID = "custommap-bank";
			val2.showBankAmount = false;
			val2.saveRoaches = false;
			val2.gooSpeedMult = 0f;
			val2.gooSpeedIncreaseMult = 0f;
			val2.hiddenUntilUnlocked = false;
			val2.unlockAchievement = "";
			val2.unlockHint = "";
			val2.devOnly = false;
			Sprite val3 = LoadSprite(Path.Combine(directoryName, fileNameWithoutExtension + ".png"));
			if ((Object)(object)val3 != (Object)null)
			{
				val2.capsuleArt = val3;
				val2.screenArt = val3;
			}
			ModeToMap[val2] = mapPath;
			return val2;
		}

		private static void RegisterModes()
		{
			if ((Object)(object)modeDb == (Object)null)
			{
				modeDb = ScriptableObject.CreateInstance<WKAssetDatabase>();
				((Object)modeDb).hideFlags = (HideFlags)32;
				Plugin.FillNullLists(modeDb);
				modeDb.id = "wkcustommodes";
				CL_AssetManager.AddNewDatabase("wkcustommodes", modeDb);
			}
			modeDb.gamemodeAssets.Clear();
			modeDb.gamemodeAssets.AddRange(ModeToMap.Keys);
			CL_AssetManager.RefreshDatabaseList();
		}

		private static Sprite LoadSprite(string png)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (!File.Exists(png))
			{
				return null;
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)0
			};
			if (!ImageConversion.LoadImage(val, File.ReadAllBytes(png)))
			{
				return null;
			}
			((Object)val).hideFlags = (HideFlags)32;
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
			((Object)obj).hideFlags = (HideFlags)32;
			return obj;
		}

		private static string Pretty(string id)
		{
			return string.Join(" ", from w in id.Replace('_', ' ').Replace('-', ' ').Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
				select char.ToUpperInvariant(w[0]) + w.Substring(1));
		}
	}
	[HarmonyPatch(typeof(UI_TabGroup), "Start")]
	internal static class TabGroupStartPatch
	{
		private static void Prefix(UI_TabGroup __instance)
		{
			CustomMenu.TryInject(__instance);
		}
	}
	[HarmonyPatch(typeof(M_Gamemode), "GetGenerationList")]
	internal static class CustomModeGenerationPatch
	{
		private static void Prefix(M_Gamemode __instance)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Log.LogInfo((object)($"[card] GetGenerationList on '{__instance?.gamemodeName}' (ours: {CustomMenu.ModeToMap.ContainsKey(__instance)}, " + string.Format("type {0}, player {1})", __instance?.modeType, ((Object)(object)ENT_Player.playerObject != (Object)null) ? "yes" : "no")));
			if (!CustomMenu.ModeToMap.TryGetValue(__instance, out var value))
			{
				return;
			}
			try
			{
				LevelAssetHolder val = Plugin.Instance.BuildHolder(value);
				if (val != null)
				{
					__instance.playlistLevelAssets = new List<LevelAssetHolder> { val };
					Plugin.Log.LogInfo((object)("[card] built '" + val.id + "' from " + value));
				}
				else
				{
					Plugin.Log.LogError((object)("[card] could not build map '" + value + "'."));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[card] building map failed: " + ex));
			}
		}

		private static void Postfix(M_Gamemode __instance, List<LevelAssetHolder> __result)
		{
			if (CustomMenu.ModeToMap.ContainsKey(__instance))
			{
				Plugin.Log.LogInfo((object)("[card] generation list: " + ((__result == null) ? "null" : string.Join(", ", __result.Select((LevelAssetHolder h) => h?.id ?? "null")))));
			}
		}
	}
	[HarmonyPatch(typeof(FXManager), "FXRender")]
	internal static class FogPatch
	{
		public static float MapFogScale = 0.25f;

		public static bool Active;

		private static void Postfix()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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)
			if (Active && FXManager.fxData != null && FXManager.showFog)
			{
				float num = FXManager.fxData.fogMultiplier * MapFogScale;
				Color fog = FXManager.fxData.fog;
				Shader.SetGlobalVector("_FOG", new Vector4(fog.r, fog.g, fog.b, num));
				Shader.SetGlobalFloat("_FOGMULT", num);
			}
		}
	}
	internal class PropSpec
	{
		public string Mesh;

		public Vector3 Forward = Vector3.forward;

		public Vector3 Up = Vector3.up;

		public float Scale = 1f;

		public string Action;

		public bool Screen;

		public bool Tree;

		public float Yaw;

		public Quaternion Rotation => Quaternion.LookRotation(Forward, Up);

		public static PropSpec Parse(string name)
		{
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			name = Regex.Replace(name, "\\.\\d{3}$", "");
			string[] array = name.Split(':');
			PropSpec propSpec = new PropSpec
			{
				Mesh = ((array.Length > 1) ? array[1] : "")
			};
			foreach (string item in array.Skip(2))
			{
				float result;
				float result2;
				Vector3 v;
				if (item.StartsWith("btn="))
				{
					propSpec.Action = item.Substring(4);
				}
				else if (item == "screen")
				{
					propSpec.Screen = true;
				}
				else if (item == "tree")
				{
					propSpec.Tree = true;
				}
				else if (item.StartsWith("y") && float.TryParse(item.Substring(1), NumberStyles.Float, CultureInfo.InvariantCulture, out result))
				{
					propSpec.Yaw = result;
				}
				else if (item.StartsWith("s") && float.TryParse(item.Substring(1), NumberStyles.Float, CultureInfo.InvariantCulture, out result2))
				{
					propSpec.Scale = result2;
				}
				else if ((item.StartsWith("f") || item.StartsWith("u")) && TryVec(item.Substring(1), out v))
				{
					if (item[0] == 'f')
					{
						propSpec.Forward = v;
					}
					else
					{
						propSpec.Up = v;
					}
				}
			}
			return propSpec;
		}

		private static bool TryVec(string s, out Vector3 v)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			v = Vector3.zero;
			string[] array = s.Split(',');
			if (array.Length != 3)
			{
				return false;
			}
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			if (!float.TryParse(array[0], NumberStyles.Float, invariantCulture, out var result) || !float.TryParse(array[1], NumberStyles.Float, invariantCulture, out var result2) || !float.TryParse(array[2], NumberStyles.Float, invariantCulture, out var result3))
			{
				return false;
			}
			v = new Vector3(result, result2, result3);
			return ((Vector3)(ref v)).sqrMagnitude > 1E-06f;
		}
	}
	internal static class GameProps
	{
		private static readonly string[] SourceLevels = new string[2] { "Training_Level_01", "M3_Habitation_Pier_03" };

		private static readonly List<GameObject> sources = new List<GameObject>();

		private static readonly List<AsyncOperationHandle<GameObject>> handles = new List<AsyncOperationHandle<GameObject>>();

		private static bool sourcesTried;

		private static readonly Dictionary<string, MeshFilter> cache = new Dictionary<string, MeshFilter>();

		public static bool TryGet(string meshName, out Mesh mesh, out Material[] mats, out CL_Button button)
		{
			mesh = null;
			mats = null;
			button = null;
			if (!cache.TryGetValue(meshName, out var value) || (Object)(object)value == (Object)null)
			{
				LoadSources();
				value = sources.SelectMany((GameObject s) => s.GetComponentsInChildren<MeshFilter>(true)).FirstOrDefault((Func<MeshFilter, bool>)((MeshFilter m) => (Object)(object)m.sharedMesh != (Object)null && ((Object)m.sharedMesh).name == meshName)) ?? ((IEnumerable<MeshFilter>)Resources.FindObjectsOfTypeAll<MeshFilter>()).FirstOrDefault((Func<MeshFilter, bool>)((MeshFilter m) => (Object)(object)m.sharedMesh != (Object)null && ((Object)m.sharedMesh).name == meshName));
				if ((Object)(object)value == (Object)null)
				{
					return false;
				}
				cache[meshName] = value;
				Plugin.Log.LogInfo((object)("Prop '" + meshName + "' from '" + ((Object)((Component)value).transform.root).name + "/" + ((Object)value).name + "'"));
			}
			mesh = value.sharedMesh;
			MeshRenderer component = ((Component)value).GetComponent<MeshRenderer>();
			mats = (Material[])(((object)((component != null) ? ((Renderer)component).sharedMaterials : null)) ?? ((object)new Material[0]));
			button = ((Component)value).GetComponent<CL_Button>();
			return true;
		}

		public static MeshFilter FindTree(string meshName)
		{
			LoadSources();
			MeshFilter val = (from m in sources.SelectMany((GameObject s) => s.GetComponentsInChildren<MeshFilter>(true))
				where (Object)(object)m.sharedMesh != (Object)null && ((Object)m.sharedMesh).name == meshName
				orderby ((Component)m).GetComponentsInChildren<CL_Button>(true).Length != 0 descending
				select m).FirstOrDefault();
			if ((Object)(object)val != (Object)null)
			{
				Plugin.Log.LogInfo((object)("Prop tree '" + meshName + "' from '" + ((Object)((Component)val).transform.root).name + "/" + ((Object)val).name + "'"));
			}
			return val;
		}

		private static void LoadSources()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Invalid comparison between Unknown and I4
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (sourcesTried)
			{
				return;
			}
			sourcesTried = true;
			string[] sourceLevels = SourceLevels;
			foreach (string text in sourceLevels)
			{
				try
				{
					LevelAssetHolder levelAsset = CL_AssetManager.GetLevelAsset(text, "");
					if (levelAsset?.levelAssetReference == null)
					{
						Plugin.Log.LogWarning((object)("Prop source level '" + text + "' not in the game's database."));
						continue;
					}
					AsyncOperationHandle<GameObject> item = Addressables.LoadAssetAsync<GameObject>(((AssetReference)levelAsset.levelAssetReference).RuntimeKey);
					GameObject val = item.WaitForCompletion();
					if ((int)item.Status == 1 && (Object)(object)val != (Object)null)
					{
						handles.Add(item);
						sources.Add(val);
					}
					else
					{
						Plugin.Log.LogWarning((object)("Prop source level '" + text + "' failed to load: " + item.OperationException?.Message));
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Prop source level '" + text + "': " + ex.Message));
				}
			}
		}
	}
	public class MapScreen : MonoBehaviour
	{
		internal static readonly List<MapScreen> All = new List<MapScreen>();

		private TextMeshPro tmp;

		private const float PanelW = 0.88f;

		private const float PanelH = 0.8f;

		private void OnEnable()
		{
			All.Add(this);
		}

		private void OnDisable()
		{
			All.Remove(this);
		}

		private IEnumerator Start()
		{
			yield return (object)new WaitForSeconds(0.5f);
			TMP_FontAsset val = HudFont();
			Plugin.Log.LogInfo((object)("Screen: making text with font '" + (((Object)(object)val != (Object)null) ? ((Object)val).name : "none") + "'"));
			if (!((Object)(object)val == (Object)null))
			{
				MeshFilter component = ((Component)this).GetComponent<MeshFilter>();
				Bounds? obj;
				if (component == null)
				{
					obj = null;
				}
				else
				{
					Mesh sharedMesh = component.sharedMesh;
					obj = ((sharedMesh != null) ? new Bounds?(sharedMesh.bounds) : ((Bounds?)null));
				}
				Bounds mb = (Bounds)(((??)obj) ?? new Bounds(Vector3.zero, new Vector3(4f, 2f, 0.5f)));
				MakeBlackPanel(mb);
				GameObject val2 = new GameObject("screen_text");
				val2.transform.SetParent(((Component)this).transform, false);
				val2.transform.localPosition = new Vector3(((Bounds)(ref mb)).center.x, ((Bounds)(ref mb)).center.y, ((Bounds)(ref mb)).max.z + 0.04f);
				val2.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
				tmp = val2.AddComponent<TextMeshPro>();
				((TMP_Text)tmp).font = val;
				((TMP_Text)tmp).rectTransform.sizeDelta = new Vector2(((Bounds)(ref mb)).size.x * 0.88f * 0.97f, ((Bounds)(ref mb)).size.y * 0.8f * 0.97f);
				((TMP_Text)tmp).enableAutoSizing = true;
				((TMP_Text)tmp).fontSizeMin = 1f;
				((TMP_Text)tmp).fontSizeMax = 12f;
				((TMP_Text)tmp).alignment = (TextAlignmentOptions)513;
				((Graphic)tmp).color = new Color(0.55f, 1f, 0.55f);
				((TMP_Text)tmp).richText = true;
				Plugin.Log.LogInfo((object)"Screen: text ready");
				GlowGame.RefreshScreens(force: true);
				Plugin.Log.LogInfo((object)"Screen: first text shown");
			}
		}

		private void MakeBlackPanel(Bounds mb)
		{
			//IL_005a: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: 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_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			Material val = (from r in ((Component)((Component)this).transform.root).GetComponentsInChildren<Renderer>()
				select r.sharedMaterial).FirstOrDefault((Func<Material, bool>)((Material m) => (Object)(object)m != (Object)null && ((Object)m).name.StartsWith("WKMap_Tint_")));
			Material val2 = GlowGame.LitMaterial(val, Color.black, dull: true);
			if (!((Object)(object)val2 == (Object)null))
			{
				Plugin.Log.LogInfo((object)("Screen: black panel from '" + ((Object)val).name + "' (shader '" + ((Object)val.shader).name + "')"));
				float num = ((Bounds)(ref mb)).size.x * 0.88f / 2f;
				float num2 = ((Bounds)(ref mb)).size.y * 0.8f / 2f;
				float num3 = ((Bounds)(ref mb)).max.z + 0.02f;
				Vector3 center = ((Bounds)(ref mb)).center;
				Mesh val3 = new Mesh
				{
					name = "screen_panel"
				};
				Vector3[] array = (Vector3[])(object)new Vector3[4]
				{
					new Vector3(center.x - num, center.y - num2, num3),
					new Vector3(center.x + num, center.y - num2, num3),
					new Vector3(center.x + num, center.y + num2, num3),
					new Vector3(center.x - num, center.y + num2, num3)
				};
				val3.vertices = array.Concat(array).ToArray();
				val3.normals = Enumerable.Repeat<Vector3>(Vector3.forward, 4).Concat(Enumerable.Repeat<Vector3>(Vector3.back, 4)).ToArray();
				val3.uv = (Vector2[])(object)new Vector2[8]
				{
					Vector2.zero,
					Vector2.right,
					Vector2.one,
					Vector2.up,
					Vector2.zero,
					Vector2.right,
					Vector2.one,
					Vector2.up
				};
				val3.triangles = new int[12]
				{
					0, 2, 1, 0, 3, 2, 4, 5, 6, 4,
					6, 7
				};
				val3.RecalculateBounds();
				GameObject val4 = new GameObject("screen_panel");
				val4.transform.SetParent(((Component)this).transform, false);
				val4.AddComponent<MeshFilter>().sharedMesh = val3;
				((Renderer)val4.AddComponent<MeshRenderer>()).sharedMaterial = val2;
			}
		}

		private static TMP_FontAsset HudFont()
		{
			TMP_Text[] array = Object.FindObjectsByType<TMP_Text>((FindObjectsSortMode)0);
			foreach (TMP_Text val in array)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)val.font != (Object)null && ((Behaviour)val).isActiveAndEnabled)
				{
					return val.font;
				}
			}
			return null;
		}

		internal void SetText(string s)
		{
			if ((Object)(object)tmp != (Object)null && ((TMP_Text)tmp).text != s)
			{
				((TMP_Text)tmp).text = s;
			}
		}
	}
	public class MapGlowHold : MonoBehaviour
	{
		public bool glow;

		public bool pass;

		public int lane = 1;

		public int variant = -1;

		internal bool lit;

		internal Vector3 home;

		internal int owner;

		internal float litAt;

		internal float litUntil;

		private CL_Handhold hold;

		private Renderer rend;

		private Material gray;

		private GameObject lamp;

		internal static readonly List<MapGlowHold> All = new List<MapGlowHold>();

		internal bool Held
		{
			get
			{
				if ((Object)(object)hold != (Object)null)
				{
					return hold.GetHolding();
				}
				return false;
			}
		}

		internal bool HasLamp => (Object)(object)lamp != (Object)null;

		private void OnEnable()
		{
			if (glow)
			{
				All.Add(this);
			}
		}

		private void OnDisable()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			All.Remove(this);
			SetLit(on: false);
		}

		private void Awake()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			home = ((Component)this).transform.localPosition;
		}

		private void Start()
		{
			hold = ((Component)this).GetComponent<CL_Handhold>();
			rend = ((Component)this).GetComponent<Renderer>();
			if (pass)
			{
				Collider[] components = ((Component)this).GetComponents<Collider>();
				for (int i = 0; i < components.Length; i++)
				{
					MakeTrigger(components[i]);
				}
			}
		}

		internal static void MakeTrigger(Collider c)
		{
			MeshCollider val = (MeshCollider)(object)((c is MeshCollider) ? c : null);
			if (val != null)
			{
				val.convex = true;
			}
			c.isTrigger = true;
		}

		private void Update()
		{
			if (!lit && (Object)(object)rend != (Object)null)
			{
				gray = rend.sharedMaterial;
			}
			if (lit && Held)
			{
				GlowGame.OnGrab(this);
			}
		}

		internal void SetLit(bool on, Color color = default(Color))
		{
			//IL_005f: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rend == (Object)null)
			{
				rend = ((Component)this).GetComponent<Renderer>();
			}
			if ((Object)(object)rend == (Object)null || lit == on)
			{
				lit = on;
				return;
			}
			if ((Object)(object)gray == (Object)null)
			{
				gray = rend.sharedMaterial;
			}
			lit = on;
			if (color == default(Color))
			{
				color = GlowGame.PlayerColors[0];
			}
			rend.sharedMaterial = (on ? GlowGame.LitMaterial(gray, color) : gray);
			if (on && (Object)(object)lamp == (Object)null && GlowGame.LampCount < 6)
			{
				lamp = GlowGame.MakeLamp(((Component)this).transform, color);
			}
			if (!on && (Object)(object)lamp != (Object)null)
			{
				Object.Destroy((Object)(object)lamp);
				lamp = null;
			}
		}
	}
	public class MapLane : MonoBehaviour
	{
		public int lane = 1;

		public int variant = -1;

		public bool spin;

		private Vector3 basePos;

		private Quaternion baseRot;

		private float dx;

		private bool started;

		internal static readonly List<MapLane> All = new List<MapLane>();

		private static readonly Dictionary<int, Vector3> axes = new Dictionary<int, Vector3>();

		private const float SpinPeriod = 15f;

		private void Start()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			basePos = ((Component)this).transform.localPosition;
			baseRot = ((Component)this).transform.localRotation;
			if (((Object)this).name.ToLowerInvariant().Contains("cyl_body"))
			{
				axes[lane] = basePos;
			}
			started = true;
			All.Add(this);
			GlowGame.PlaceLane(this);
		}

		private void OnDestroy()
		{
			All.Remove(this);
		}

		internal void Place(float dx, bool show)
		{
			if (started)
			{
				this.dx = dx;
				show &= GlowGame.OnMap(variant);
				Pose();
				if (((Component)this).gameObject.activeSelf != show)
				{
					((Component)this).gameObject.SetActive(show);
				}
			}
		}

		private void Update()
		{
			if (spin)
			{
				Pose();
			}
		}

		private void Pose()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = basePos;
			Quaternion localRotation = baseRot;
			if (spin && axes.TryGetValue(lane, out var value))
			{
				Quaternion val2 = Quaternion.AngleAxis(Time.time * 360f / 15f, Vector3.right);
				val = value + val2 * (basePos - value);
				localRotation = val2 * baseRot;
			}
			((Component)this).transform.localPosition = val + Vector3.right * dx;
			((Component)this).transform.localRotation = localRotation;
		}
	}
	public class MapBarrier : MonoBehaviour
	{
		public int lane = 1;

		internal static readonly List<MapBarrier> All = new List<MapBarrier>();

		private void Start()
		{
			All.Add(this);
			Set(!GlowGame.IsOpen(lane));
		}

		private void OnDestroy()
		{
			All.Remove(this);
		}

		internal void Set(bool on)
		{
			Collider[] components = ((Component)this).GetComponents<Collider>();
			for (int i = 0; i < components.Length; i++)
			{
				components[i].enabled = on;
			}
		}
	}
	internal static class GlowGame
	{
		private enum State
		{
			Idle,
			Countdown,
			Running,
			Result
		}

		private class Lane
		{
			public int id;

			public State state;

			public float countEnd;

			public float endTime;

			public float nextLight;

			public int lastCount;

			public bool warned10;

			public Random rng = new Random();
		}

		private class Score
		{
			public int pressed;

			public int missed;

			public bool newBest;

			public readonly List<float> reactions = new List<float>();
		}

		private class Item
		{
			public string label;

			public string value;

			public Action<int> change;

			public Color? color;
		}

		private static readonly string[] MapNames = new string[2] { "Flat Wall", "Cylinder" };

		public static bool Multiplayer;

		public static bool Coop;

		public static bool SharedTrail;

		public static int Players = 2;

		public static float Lifespan = 3f;

		public static float Interval = 1f;

		public static float Duration = 60f;

		private const float LifeStep = 0.5f;

		private const float LifeMin = 0.5f;

		private const float LifeMax = 10f;

		private const float IntStep = 0.25f;

		private const float IntMin = 0.25f;

		private const float IntMax = 5f;

		private const float DurStep = 15f;

		private const float DurMin = 15f;

		private const float DurMax = 600f;

		public const float LaneOffset = 10.75f;

		internal const int MaxLamps = 6;

		internal static readonly Color[] PlayerColors = (Color[])(object)new Color[4]
		{
			new Color(0.2f, 1f, 0.3f),
			new Color(0.25f, 0.55f, 1f),
			new Color(1f, 0.55f, 0.1f),
			new Color(1f, 0.3f, 0.8f)
		};

		private static readonly string[] ColorNames = new string[4] { "green", "blue", "orange", "pink" };

		private static readonly Lane[] lanes = new Lane[3]
		{
			null,
			new Lane
			{
				id = 1
			},
			new Lane
			{
				id = 2
			}
		};

		private static readonly Score[] scores = new Score[5]
		{
			null,
			new Score(),
			new Score(),
			new Score(),
			new Score()
		};

		private static int tab;

		private static int row;

		private static int map;

		private static float nextScreen;

		private static readonly Dictionary<string, int> best = new Dictionary<string, int>();

		private static readonly Dictionary<(Material, Color, bool), Material> litCache = new Dictionary<(Material, Color, bool), Material>();

		private static readonly Dictionary<Color, Texture2D> colorTex = new Dictionary<Color, Texture2D>();

		private const string Dim = "#6FBF6F";

		private const string Bright = "#C8FFC8";

		private const string Hi = "#FFFFFF";

		private static bool TwoWalls
		{
			get
			{
				if (Multiplayer && !Coop)
				{
					return !SharedTrail;
				}
				return false;
			}
		}

		private static bool ColoredHolds
		{
			get
			{
				if (Multiplayer && !Coop)
				{
					return SharedTrail;
				}
				return false;
			}
		}

		private static int WallCount
		{
			get
			{
				if (!TwoWalls)
				{
					return 1;
				}
				return 2;
			}
		}

		private static IEnumerable<Lane> Active => lanes.Skip(1).Take(WallCount);

		private static int Scorers
		{
			get
			{
				if (Multiplayer && !Coop)
				{
					if (!TwoWalls)
					{
						return Players;
					}
					return 2;
				}
				return 1;
			}
		}

		private static bool Busy => Active.Any((Lane l) => l.state == State.Countdown || l.state == State.Running);

		public static bool Running => Active.Any((Lane l) => l.state == State.Running);

		internal static int LampCount => MapGlowHold.All.Count((MapGlowHold h) => h.HasLamp);

		private static string ModeText
		{
			get
			{
				if (Multiplayer)
				{
					if (!Coop)
					{
						if (!SharedTrail)
						{
							return "comp, wall each";
						}
						return $"comp x{Players}, shared wall";
					}
					return $"co-op x{Players}";
				}
				return "1 player";
			}
		}

		private static string Settings => $"{MapNames[map]}, {ModeText}, lifespan {Lifespan:0.#} s, every {Interval:0.##} s, {Duration:0} s";

		internal static int VariantOf(string name)
		{
			string text = name.ToLowerInvariant();
			if (!text.Contains("_cyl"))
			{
				if (!text.Contains("_flat"))
				{
					return -1;
				}
				return 0;
			}
			return 1;
		}

		internal static bool OnMap(int variant)
		{
			if (variant >= 0)
			{
				return variant == map;
			}
			return true;
		}

		internal static bool InPlay(MapGlowHold h, int lane)
		{
			if (h.lane == lane)
			{
				return OnMap(h.variant);
			}
			return false;
		}

		private static IEnumerable<int> PlayersOn(int lane)
		{
			if (!TwoWalls)
			{
				if (!ColoredHolds)
				{
					return new int[1] { 1 };
				}
				return Enumerable.Range(1, Players);
			}
			return new int[1] { lane };
		}

		private static string ScorerName(int p)
		{
			if (Multiplayer)
			{
				if (!Coop)
				{
					return $"P{p}";
				}
				return "Team";
			}
			return "You";
		}

		internal static bool IsOpen(int lane)
		{
			if (lane >= 1 && lane <= 2)
			{
				return lanes[lane].state == State.Running;
			}
			return false;
		}

		private static string Who(Lane l)
		{
			if (!TwoWalls)
			{
				return "";
			}
			return $"P{l.id} ";
		}

		internal static void PlaceLane(MapLane l)
		{
			float dx = ((!TwoWalls) ? 0f : ((l.lane == 1) ? (-10.75f) : 10.75f));
			l.Place(dx, l.lane == 1 || TwoWalls);
		}

		private static void ApplyLayout()
		{
			foreach (MapLane item in MapLane.All.ToList())
			{
				if ((Object)(object)item != (Object)null)
				{
					PlaceLane(item);
				}
			}
		}

		private static List<Item> Items()
		{
			//IL_033b: Unknown result type (might be due to invalid IL or missing references)
			List<Item> list = new List<Item> { null };
			if (tab == 0)
			{
				list.Add(new Item
				{
					label = "Map",
					value = MapNames[map],
					change = delegate(int d)
					{
						map = (map + d + MapNames.Length) % MapNames.Length;
					}
				});
				list.Add(new Item
				{
					label = "Multiplayer",
					value = (Multiplayer ? "ON" : "OFF"),
					change = delegate
					{
						Multiplayer = !Multiplayer;
					}
				});
				if (Multiplayer)
				{
					list.Add(new Item
					{
						label = "Mode",
						value = (Coop ? "Co-op" : "Comp"),
						change = delegate
						{
							Coop = !Coop;
						}
					});
					if (!Coop)
					{
						list.Add(new Item
						{
							label = "Trails",
							value = (SharedTrail ? "1 shared" : "1 per player"),
							change = delegate
							{
								SharedTrail = !SharedTrail;
							}
						});
					}
					if (Coop || SharedTrail)
					{
						list.Add(new Item
						{
							label = "Player count",
							value = Players.ToString(),
							change = delegate(int d)
							{
								Players = Mathf.Clamp(Players + d, 2, 4);
							}
						});
					}
				}
				list.Add(new Item
				{
					label = "Btn lifespan",
					value = $"{Lifespan:0.0} s",
					change = delegate(int d)
					{
						Lifespan = Mathf.Clamp(Lifespan + (float)d * 0.5f, 0.5f, 10f);
					}
				});
				list.Add(new Item
				{
					label = "Interval",
					value = $"{Interval:0.00} s",
					change = delegate(int d)
					{
						Interval = Mathf.Clamp(Interval + (float)d * 0.25f, 0.25f, 5f);
					}
				});
				list.Add(new Item
				{
					label = "Duration",
					value = $"{Duration:0} s",
					change = delegate(int d)
					{
						Duration = Mathf.Clamp(Duration + (float)d * 15f, 15f, 600f);
					}
				});
			}
			else
			{
				for (int num = 0; num < PowerUps.Names.Length; num++)
				{
					int k = num;
					list.Add(new Item
					{
						label = PowerUps.Shapes[k] + " - " + PowerUps.Names[k],
						value = (PowerUps.Enabled[k] ? "ON" : "OFF"),
						color = PowerUps.Colors[k],
						change = delegate
						{
							PowerUps.Enabled[k] = !PowerUps.Enabled[k];
						}
					});
				}
				list.Add(new Item
				{
					label = "Max at once",
					value = PowerUps.MaxAtOnce.ToString(),
					change = delegate(int d)
					{
						PowerUps.MaxAtOnce = Mathf.Clamp(PowerUps.MaxAtOnce + d, 1, 10);
					}
				});
				list.Add(new Item
				{
					label = "Spawn every",
					value = $"{PowerUps.SpawnEvery:0} s",
					change = delegate(int d)
					{
						PowerUps.SpawnEvery = Mathf.Clamp(PowerUps.SpawnEvery + (float)d * 5f, 5f, 60f);
					}
				});
			}
			return list;
		}

		public static void Menu(string key)
		{
			if (Busy)
			{
				return;
			}
			foreach (Lane item in Active)
			{
				if (item.state == State.Result)
				{
					item.state = State.Idle;
				}
			}
			List<Item> list = Items();
			row = Mathf.Clamp(row, 0, list.Count - 1);
			int num = ((key == "left") ? (-1) : ((key == "right") ? 1 : 0));
			if (key == "up")
			{
				row = (row + list.Count - 1) % list.Count;
			}
			else if (key == "down")
			{
				row = (row + 1) % list.Count;
			}
			else if (num != 0 && row == 0)
			{
				tab = 1 - tab;
			}
			else if (num != 0)
			{
				list[row].change(num);
				ApplyLayout();
			}
			RefreshScreens(force: true);
		}

		public static void StandPressed(int lane)
		{
			if (lane >= 1 && lane <= WallCount)
			{
				Lane lane2 = lanes[lane];
				if (lane2.state != State.Countdown && lane2.state != State.Running)
				{
					BeginCountdown(lane);
				}
			}
		}

		public static void BeginCountdown(int lane, float seconds = 0f)
		{
			if (seconds > 0f)
			{
				Duration = seconds;
			}
			if (!MapGlowHold.All.Any((MapGlowHold h) => InPlay(h, lane)))
			{
				MapSession.ShowTitle("No glow holds here");
				return;
			}
			Lane lane2 = lanes[lane];
			foreach (Lane item in Active)
			{
				if (item.state == State.Result)
				{
					item.state = State.Idle;
				}
			}
			lane2.state = State.Countdown;
			lane2.countEnd = Time.time + 3f;
			lane2.lastCount = 4;
			RefreshScreens(force: true);
		}

		private static void Go(Lane l)
		{
			l.state = State.Running;
			foreach (int item in PlayersOn(l.id))
			{
				Score obj = scores[item];
				obj.pressed = (obj.missed = 0);
				obj.reactions.Clear();
				obj.newBest = false;
			}
			l.warned10 = false;
			l.endTime = Time.time + Duration;
			l.nextLight = Time.time + 0.3f;
			l.rng = new Random(StableHash(Settings));
			foreach (MapBarrier item2 in MapBarrier.All)
			{
				if (item2.lane == l.id)
				{
					item2.Set(on: false);
				}
			}
			PowerUps.StartLane(l.id);
			MapSession.ShowTitle(Who(l) + "GO!");
			Plugin.Say("Glow wall " + Who(l) + ": " + Settings);
			RefreshScreens(force: true);
		}

		public static void Stop(bool showScore)
		{
			foreach (Lane item in Active.ToList())
			{
				if (item.state == State.Running && showScore)
				{
					Finish(item);
				}
				else if (item.state == State.Running || item.state == State.Countdown)
				{
					EndRound(item);
				}
			}
			RefreshScreens(force: true);
		}

		public static void Reset()
		{
			foreach (Lane item in lanes.Skip(1))
			{
				item.state = State.Idle;
			}
			PowerUps.Reset();
		}

		private static void EndRound(Lane l)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			l.state = State.Idle;
			foreach (MapGlowHold item in MapGlowHold.All.ToList())
			{
				if (item.lane == l.id)
				{
					item.SetLit(on: false);
				}
			}
			foreach (MapBarrier item2 in MapBarrier.All)
			{
				if (item2.lane == l.id)
				{
					item2.Set(on: true);
				}
			}
			PowerUps.ClearLane(l.id);
			MoveOutOfLane(l.id);
		}

		private static void MoveOutOfLane(int lane)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			ENT_Player playerObject = ENT_Player.playerObject;
			if ((Object)(object)playerObject == (Object)null)
			{
				return;
			}
			foreach (MapBarrier item in MapBarrier.All)
			{
				if (!((Object)(object)item == (Object)null) && item.lane == lane && ((Object)item).name.Contains("front") && ((Component)item).gameObject.activeInHierarchy && !((Object)(object)((Component)item).transform.parent == (Object)null))
				{
					Transform parent = ((Component)item).transform.parent;
					Vector3 val = parent.InverseTransformPoint(((Component)playerObject).transform.position);
					Vector3 localPosition = ((Component)item).transform.localPosition;
					if (!(val.z > localPosition.z) && !(Mathf.Abs(val.x - localPosition.x) > 11.75f))
					{
						((GameEntity)playerObject).Teleport(parent.TransformPoint(new Vector3(localPosition.x, 1.2f, localPosition.z + 4f)), ((Component)playerObject).transform.rotation, false);
						break;
					}
				}
			}
		}

		private static void Finish(Lane l)
		{
			EndRound(l);
			l.state = State.Result;
			string settings = Settings;
			foreach (int item in PlayersOn(l.id))
			{
				Score score = scores[item];
				score.newBest = !best.TryGetValue(settings, out var value) || score.pressed > value;
				if (score.newBest)
				{
					best[settings] = score.pressed;
				}
				Plugin.Say($"Glow wall {ScorerName(item)}: {score.pressed} pressed, {score.missed} missed, {Pct(score)}, {Rate(score)}, avg reaction {AvgRx(score)}, fastest {BestRx(score)}" + (score.newBest ? ", new best" : ""));
			}
			MapSession.ShowTitle("GAME OVER...");
		}

		public static void OnGrab(MapGlowHold h)
		{
			//IL_002f: 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)
			Lane l = lanes[Mathf.Clamp(h.lane, 1, 2)];
			int owner = h.owner;
			h.SetLit(on: false);
			if (l.state != State.Running)
			{
				return;
			}
			int num = ((Coop || !Multiplayer) ? 1 : Mathf.Clamp(owner, 1, 4));
			Score score = scores[num];
			score.pressed++;
			score.reactions.Add(Time.time - h.litAt);
			MapSession.ShowTitle((Scorers > 1) ? $"{ScorerName(num)}: {score.pressed}" : score.pressed.ToString());
			if (!MapGlowHold.All.Any((MapGlowHold x) => x.lane == l.id && x.lit && x.owner == owner))
			{
				LightNext(l, Time.time, owner);
				if (!ColoredHolds)
				{
					l.nextLight = Time.time + Interval;
				}
			}
			RefreshScreens(force: true);
		}

		public static void Tick()
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			bool flag = false;
			foreach (Lane item in Active.ToList())
			{
				if (item.state == State.Countdown)
				{
					flag = true;
					int num = Mathf.CeilToInt(item.countEnd - time);
					if (num != item.lastCount && num > 0)
					{
						item.lastCount = num;
						MapSession.ShowTitle(Who(item) + num);
						RefreshScreens(force: true);
					}
					if (time >= item.countEnd)
					{
						Go(item);
					}
				}
				else
				{
					if (item.state != State.Running)
					{
						continue;
					}
					flag = true;
					foreach (MapGlowHold item2 in MapGlowHold.All.ToList())
					{
						if (item2.lane == item.id && item2.lit && time >= item2.litUntil)
						{
							int num2 = ((Coop || !Multiplayer) ? 1 : Mathf.Clamp(item2.owner, 1, 4));
							item2.SetLit(on: false);
							scores[num2].missed++;
						}
					}
					if (time >= item.endTime)
					{
						Finish(item);
						RefreshScreens(force: true);
						continue;
					}
					if (!item.warned10 && Duration > 15f && item.endTime - time <= 10f)
					{
						item.warned10 = true;
						MapSession.ShowTitle(Who(item) + "10 s left");
					}
					if (time < item.nextLight)
					{
						continue;
					}
					item.nextLight += Interval;
					if (item.nextLight < time)
					{
						item.nextLight = time + Interval;
					}
					foreach (int item3 in PlayersOn(item.id))
					{
						LightNext(item, time, ColoredHolds ? item3 : item.id);
					}
				}
			}
			PowerUps.Tick((from x in Active
				where x.state == State.Running
				select x.id).ToList());
			if (flag)
			{
				RefreshScreens(force: false);
			}
		}

		private static void LightNext(Lane l, float now, int owner)
		{
			//IL_00d8: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			List<MapGlowHold> list = (from h in MapGlowHold.All
				where InPlay(h, l.id)
				orderby Mathf.Round(h.home.y * 100f), Mathf.Round(h.home.x * 100f), Mathf.Round(h.home.z * 100f)
				select h).ToList();
			if (list.Count == 0)
			{
				return;
			}
			int num = l.rng.Next(list.Count);
			Color color = (ColoredHolds ? PlayerColors[Mathf.Clamp(owner - 1, 0, 3)] : PlayerColors[0]);
			for (int num2 = 0; num2 < list.Count; num2++)
			{
				MapGlowHold mapGlowHold = list[(num + num2) % list.Count];
				if (!mapGlowHold.lit && !mapGlowHold.Held)
				{
					mapGlowHold.owner = owner;
					mapGlowHold.SetLit(on: true, color);
					mapGlowHold.litAt = now;
					mapGlowHold.litUntil = now + Lifespan;
					break;
				}
			}
		}

		private static int StableHash(string s)
		{
			int num = -2128831035;
			foreach (char c in s)
			{
				num = (num ^ c) * 16777619;
			}
			return num & 0x7FFFFFFF;
		}

		private static string Clock(float t)
		{
			return $"{(int)(t / 60f)}:{t % 60f:00.0}";
		}

		public static void RefreshScreens(bool force)
		{
			if (MapScreen.All.Count == 0 || (!force && Time.time < nextScreen))
			{
				return;
			}
			nextScreen = Time.time + 0.1f;
			string text = ScreenText();
			foreach (MapScreen item in MapScreen.All)
			{
				item.SetText(text);
			}
		}

		private static string Hex(Color c)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return "#" + ColorUtility.ToHtmlStringRGB(c);
		}

		private static string Title(string s)
		{
			return "<align=center><size=130%><b>" + s + "</b></size></align>\n";
		}

		private static string Hint(string s)
		{
			return "\n<align=center><size=65%><color=#6FBF6F>" + s + "</color></size></align>";
		}

		private static string Row(string label, params string[] values)
		{
			StringBuilder stringBuilder = new StringBuilder("<pos=4%><color=#6FBF6F>" + label + "</color><color=#C8FFC8>");
			float num = ((values.Length <= 1) ? 52f : 40f);
			float num2 = ((values.Length <= 1) ? 0f : (58f / (float)values.Length));
			for (int i = 0; i < values.Length; i++)
			{
				stringBuilder.Append($"<pos={num + (float)i * num2:0}%>{values[i]}");
			}
			return stringBuilder.Append("</color>\n").ToString();
		}

		private static string Pct(Score s)
		{
			int num = s.pressed + s.missed;
			if (num <= 0)
			{
				return "-";
			}
			return $"{100f * (float)s.pressed / (float)num:0}%";
		}

		private static string Rate(Score s)
		{
			return $"{(float)s.pressed / Duration * 60f:0.0}/min";
		}

		private static string AvgRx(Score s)
		{
			if (s.reactions.Count <= 0)
			{
				return "-";
			}
			return $"{s.reactions.Average():0.00} s";
		}

		private static string BestRx(Score s)
		{
			if (s.reactions.Count <= 0)
			{
				return "-";
			}
			return $"{s.reactions.Min():0.00} s";
		}

		private static string TabBar()
		{
			return "<align=center><size=90%>" + T(0, "GAME") + "      " + T(1, "POWER-UPS") + "</size></align>\n";
			static string T(int i, string name)
			{
				if (i != tab)
				{
					return "<color=#6FBF6F>" + name + "</color>";
				}
				if (row != 0)
				{
					return "<color=#FFFFFF><b>" + name + "</b></color>";
				}
				return "<color=#FFFFFF><u><b>" + name + "</b></u></color>";
			}
		}

		private static string ScreenText()
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			List<Lane> source = Active.ToList();
			if (source.All((Lane l) => l.state == State.Idle))
			{
				stringBuilder.Append(TabBar());
				stringBuilder.Append(Title((tab == 0) ? "GLOW WALL" : "POWER-UPS"));
				List<Item> list = Items();
				row = Mathf.Clamp(row, 0, list.Count - 1);
				for (int num = 1; num < list.Count; num++)
				{
					Item item = list[num];
					string text = (item.color.HasValue ? ("<color=" + Hex(item.color.Value) + ">" + item.label + "</color>") : item.label);
					stringBuilder.Append((num == row) ? ("<pos=4%><color=#FFFFFF><u>" + text + "</u><pos=62%><u>" + item.value + "</u></color>\n") : ("<pos=4%><color=#6FBF6F>" + text + "</color><pos=62%><color=#C8FFC8>" + item.value + "</color>\n"));
				}
				if (tab == 0 && ColoredHolds)
				{
					stringBuilder.Append(Row("Colors", string.Join("  ", from i in Enumerable.Range(0, Players)
						select $"<color={Hex(PlayerColors[i])}>P{i + 1} {ColorNames[i]}</color>")));
				}
				string text2 = ((tab == 1) ? "fly through a power-up to use it" : (TwoWalls ? "each player presses the red btn at their wall" : "press the red btn at the wall to start"));
				stringBuilder.Append(Hint(text2 + "<br>up/down = row   left/right = change   top row = tabs"));
				return stringBuilder.ToString();
			}
			bool flag = source.All((Lane l) => l.state == State.Result || l.state == State.Idle);
			stringBuilder.Append(Title(flag ? "GAME OVER..." : "GLOW WALL"));
			int scorers = Scorers;
			List<int> cols = Enumerable.Range(1, scorers).ToList();
			if (scorers > 1)
			{
				stringBuilder.Append(Row("", V(Name)));
			}
			if (!flag)
			{
				stringBuilder.Append(Row("Time left", V(delegate(int p)
				{
					Lane lane = LaneOf(p);
					return (lane.state != State.Countdown) ? ((lane.state != State.Running) ? ((lane.state != State.Result) ? "press red btn" : "done") : Clock(Mathf.Max(0f, lane.endTime - Time.time))) : $"<b>{Mathf.Max(1, Mathf.CeilToInt(lane.countEnd - Time.time))}</b>";
				})));
			}
			stringBuilder.Append(Row("Missed", V((int p) => (!Played(p)) ? "-" : scores[p].missed.ToString())));
			if (source.Any((Lane l) => l.state == State.Result))
			{
				stringBuilder.Append(Row("Accuracy", V((int p) => (!Done(p)) ? "-" : Pct(scores[p]))));
				stringBuilder.Append(Row("Press rate", V((int p) => (!Done(p)) ? "-" : Rate(scores[p]))));
				stringBuilder.Append(Row("Avg reaction", V((int p) => (!Done(p)) ? "-" : AvgRx(scores[p]))));
				stringBuilder.Append(Row("Fastest", V((int p) => (!Done(p)) ? "-" : BestRx(scores[p]))));
				stringBuilder.Append(Row("Best", V((int p) => Done(p) ? ((!scores[p].newBest) ? ((!best.TryGetValue(Settings, out var value)) ? "-" : value.ToString()) : "NEW!") : "-")));
			}
			string text3 = ((scorers != 1) ? string.Join("   ", cols.Select((int p) => Name(p) + " " + (Played(p) ? scores[p].pressed.ToString() : "-"))) : ((Played(1) ? scores[1].pressed.ToString() : "-") ?? ""));
			stringBuilder.Append("\n<align=center><size=140%><color=#FFFFFF>Pressed btns - " + text3 + "</color></size></align>\n");
			if (flag && scorers > 1 && cols.All(Done) && !Coop)
			{
				int top = cols.Max((int p) => scores[p].pressed);
				List<int> list2 = cols.Where((int p) => scores[p].pressed == top).ToList();
				stringBuilder.Append("<align=center><b>" + ((list2.Count > 1) ? "Draw!" : (ScorerName(list2[0]) + " wins!")) + "</b></align>\n");
			}
			stringBuilder.Append(Hint(Settings + ((!Busy) ? "<br>red btn = play again   arrows = settings" : "")));
			return stringBuilder.ToString();
			static bool Done(int p)
			{
				return LaneOf(p).state == State.Result;
			}
			static Lane LaneOf(int p)
			{
				if (!TwoWalls)
				{
					return lanes[1];
				}
				return lanes[p];
			}
			static string Name(int p)
			{
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				if (!ColoredHolds)
				{
					return "<b>" + ScorerName(p) + "</b>";
				}
				return $"<color={Hex(PlayerColors[p - 1])}><b>P{p}</b></color>";
			}
			static bool Played(int p)
			{
				return LaneOf(p).state != State.Idle;
			}
			string[] V(Func<int, string> f)
			{
				return cols.Select(f).ToArray();
			}
		}

		public static Material LitMaterial(Material gray, Color color, bool dull = false)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0054: 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)
			//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_009e: Expected O, but got Unknown
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)gray == (Object)null)
			{
				return null;
			}
			if (litCache.TryGetValue((gray, color, dull), out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			if (!colorTex.TryGetValue(color, out var value2) || (Object)(object)value2 == (Object)null)
			{
				value2 = new Texture2D(2, 2)
				{
					filterMode = (FilterMode)0
				};
				value2.SetPixels(Enumerable.Repeat<Color>(color, 4).ToArray());
				value2.Apply();
				colorTex[color] = value2;
			}
			value = new Material(gray)
			{
				name = (dull ? "WKMap_GlowDull_" : "WKMap_GlowLit_") + ColorUtility.ToHtmlStringRGB(color)
			};
			string[] texturePropertyNames = value.GetTexturePropertyNames();
			foreach (string text in texturePropertyNames)
			{
				if (!((Object)(object)value.GetTexture(text) == (Object)null) && !text.ToLowerInvariant().Contains("noise") && !text.ToLowerInvariant().Contains("normal") && !text.ToLowerInvariant().Contains("bump"))
				{
					value.SetTexture(text, (Texture)(object)((dull && text.ToLowerInvariant().Contains("emis")) ? Texture2D.blackTexture : value2));
				}
			}
			if (dull)
			{
				texturePropertyNames = value.GetPropertyNames((MaterialPropertyType)2);
				foreach (string text2 in texturePropertyNames)
				{
					if (text2.ToLowerInvariant().Contains("emis"))
					{
						value.SetColor(text2, Color.black);
					}
				}
				texturePropertyNames = value.GetPropertyNames((MaterialPropertyType)0);
				foreach (string text3 in texturePropertyNames)
				{
					string text4 = text3.ToLowerInvariant();
					if (text4.Contains("emis") || text4.Contains("gloss") || text4.Contains("smooth") || text4.Contains("metal") || text4.Contains("spec"))
					{
						value.SetFloat(text3, 0f);
					}
				}
				value.DisableKeyword("_EMISSION");
			}
			if (value.HasProperty("_EmissionMultiplier"))
			{
				value.SetFloat("_EmissionMultiplier", dull ? 0f : 3f);
			}
			if (value.HasProperty("_ShimmerColor"))
			{
				value.SetColor("_ShimmerColor", color);
			}
			if (value.HasProperty("_Shimmer"))
			{
				value.SetFloat("_Shimmer", dull ? 0f : 1f);
			}
			if (value.HasProperty("_ShimmerSpeed"))
			{
				value.SetFloat("_ShimmerSpeed", 2f);
			}
			if (value.HasProperty("_ShimmerFrequency"))
			{
				value.SetFloat("_ShimmerFrequency", 1f);
			}
			litCache[(gray, color, dull)] = value;
			return value;
		}

		public static GameObject MakeLamp(Transform hold, Color color)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_007c: 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_00be: Expected O, but got Unknown
			GameObject val = new GameObject("glow_lamp");
			val.transform.SetParent(hold, false);
			val.transform.position = hold.position + hold.forward * 0.4f + Vector3.up * 0.01f;
			CL_Lamp obj = val.AddComponent<CL_Lamp>();
			obj.lampActive = true;
			obj.range = (obj.curRange = 2.5f);
			obj.startColor = (obj.color = color);
			obj.intensity = 1f;
			obj.attentuation = 1;
			obj.emissiveMult = 1f;
			obj.bypassLightmap = 1f;
			obj.fadeStart = (obj.fadeTarget = 1f);
			return val;
		}
	}
	public class MapCheckpoint : MonoBehaviour
	{
		public string action = "checkpoint";

		public int count = 1;

		public string item = "rebar";

		private void Start()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			CL_Button component = ((Component)this).GetComponent<CL_Button>();
			if (!((Object)(object)component == (Object)null))
			{
				if (component.activeEvent == null)
				{
					component.activeEvent = new UnityEvent();
				}
				component.activeEvent.AddListener(new UnityAction(Apply));
			}
		}

		public void Apply()
		{
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			if (action.StartsWith("menu_"))
			{
				GlowGame.Menu(action.Substring(5));
				return;
			}
			if (action.StartsWith("glowstart_") && int.TryParse(action.Substring(10), out var result))
			{
				GlowGame.StandPressed(result);
				return;
			}
			switch (action)
			{
			case "buddy":
				MapSession.BuddyOn = !MapSession.BuddyOn;
				if (MapSession.BuddyOn)
				{
					ItemTools.EnsureBuddy();
				}
				else
				{
					ItemTools.RemoveBuddy();
				}
				MapSession.ShowTitle(MapSession.BuddyOn ? "Buddy ON" : "Buddy OFF");
				return;
			case "glowgame":
				GlowGame.StandPressed(1);
				return;
			case "win":
				MapSession.Win();
				return;
			case "tpspawn":
				MapSession.OnRespawn();
				if (MapSession.SavedRespawn.HasValue && (Object)(object)ENT_Player.playerObject != (Object)null)
				{
					((GameEntity)ENT_Player.playerObject).Teleport(MapSession.SavedRespawn.Value, ((Component)ENT_Player.playerObject).transform.rotation, false);
					MapSession.ShowTitle("Tp to checkpoint");
				}
				else
				{
					Plugin.Instance.BackToSpawn();
					MapSession.ShowTitle("Tp to spawn");
				}
				return;
			}
			if (!ItemTools.ClearInventory())
			{
				Plugin.Log.LogWarning((object)"Could not clear inventory.");
			}
			int num = ItemTools.Give(item, count);
			if (MapSession.BuddyOn)
			{
				ItemTools.EnsureBuddy();
			}
			Plugin.Say($"Checkpoint: inventory reset, {num}x {ItemTools.LastResolvedName ?? item}");
			if (num > 0)
			{
				MapSession.ShowTitle($"+{num} {ItemTools.ShortName(item, num)}");
			}
		}
	}
	internal static class MapSession
	{
		public static bool BuddyOn;

		public static bool GodmodeMap;

		private static bool godmodeSetByUs;

		public static Vector3? SavedRespawn;

		public static readonly List<Bounds> PadsLocal = new List<Bounds>();

		public static readonly List<(Vector3 pos, string text)> NotesLocal = new List<(Vector3, string)>();

		private static readonly List<Bounds> padsWorld = new List<Bounds>();

		private static Bounds? lastPad;

		private static Coroutine fireworks;

		private const float WinScreenDelay = 10f;

		public static float WinFlySpeed = 4.2f;

		private static bool winActive;

		private static Vector3 lastPos;

		private static float[] savedSpeeds;

		public static void ResetForNewMap()
		{
			BuddyOn = false;
			GodmodeMap = false;
			SavedRespawn = null;
			PadsLocal.Clear();
			NotesLocal.Clear();
			GlowGame.Reset();
			StopFireworks();
		}

		public static void OnEnterLevel(Transform level)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			BuddyOn = false;
			SavedRespawn = null;
			lastPad = null;
			StopFireworks();
			padsWorld.Clear();
			foreach (Bounds item in PadsLocal)
			{
				Bounds current = item;
				padsWorld.Add(new Bounds(level.TransformPoint(((Bounds)(ref current)).center), ((Bounds)(ref current)).size));
			}
			foreach (var item2 in NotesLocal)
			{
				ItemTools.DropNote(level.TransformPoint(item2.pos), item2.text);
			}
			ENT_Player playerObject = ENT_Player.playerObject;
			if (GodmodeMap && (Object)(object)playerObject != (Object)null && !playerObject.GetGodMode())
			{
				playerObject.SetGodMode(true);
				godmodeSetByUs = true;
				Plugin.Say("Godmode on for this map.");
			}
		}

		public static void Tick(ENT_Player p)
		{
			//IL_0028: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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)
			if ((Object)(object)p == (Object)null)
			{
				return;
			}
			BoostFlight(p);
			GlowGame.Tick();
			if (padsWorld.Count == 0)
			{
				return;
			}
			Vector3 position = ((Component)p).transform.position;
			Bounds val = default(Bounds);
			foreach (Bounds item in padsWorld)
			{
				Bounds current = item;
				((Bounds)(ref val))..ctor(((Bounds)(ref current)).center + Vector3.up * 1.2f, ((Bounds)(ref current)).size + new Vector3(0.2f, 2.6f, 0.2f));
				if (!((Bounds)(ref val)).Contains(position))
				{
					continue;
				}
				if (lastPad.HasValue)
				{
					Bounds value = lastPad.Value;
					if (((Bounds)(ref value)).center == ((Bounds)(ref current)).center)
					{
						break;
					}
				}
				lastPad =