Decompiled source of WillOTheWisp v0.1.2

plugins/MuckWisp.dll

Decompiled a day 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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MuckWisp")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds Risk of Rain 2's Will-o'-the-wisp (a pillar on kill) to Muck as a Blue powerup.")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2+879fc7ac66fec639f2f84b4b7a65d5c61c9ed734")]
[assembly: AssemblyProduct("Will-o'-the-wisp")]
[assembly: AssemblyTitle("MuckWisp")]
[assembly: InternalsVisibleTo("MuckWisp.Tests")]
[assembly: AssemblyVersion("0.1.2.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckWisp
{
	[HarmonyPatch(typeof(PlayerStatus), "AddKill")]
	internal static class AddKillPatch
	{
		private static void Postfix(Mob __1)
		{
			try
			{
				if (!((Object)(object)__1 == (Object)null) && !((Object)(object)PowerupInventory.Instance == (Object)null))
				{
					int num = StackCount.Read(WispPowerup.Counts.Invoke(PowerupInventory.Instance), WispPowerup.Id);
					if (num > 0 && WispBlast.Ledger.TryClaim(__1.id))
					{
						WispBlast.Schedule(__1, num);
					}
				}
			}
			catch (Exception arg)
			{
				WispPlugin.Log.LogError((object)$"Will-o'-the-wisp kill hook failed: {arg}");
			}
		}
	}
	internal sealed class KillLedger
	{
		private readonly HashSet<int> _ids = new HashSet<int>();

		public int Count => _ids.Count;

		public bool TryClaim(int mobId)
		{
			if (_ids.Contains(mobId))
			{
				return false;
			}
			_ids.Add(mobId);
			return true;
		}

		public void Reset()
		{
			_ids.Clear();
		}
	}
	internal static class PickupArt
	{
		public const string ObjResource = "MuckWisp.pickup.obj";

		public const string JsonResource = "MuckWisp.pickup.json";

		public static void Apply(Powerup powerup, Powerup[] pool)
		{
			if ((Object)(object)powerup == (Object)null || (Object)(object)powerup.mesh != (Object)null)
			{
				return;
			}
			Powerup val = Donor(pool, powerup);
			try
			{
				PickupMesh data = PickupMesh.From(PickupModel.Parse(Read("MuckWisp.pickup.obj"), Read("MuckWisp.pickup.json")));
				Material val2 = (((Object)(object)val != (Object)null) ? val.material : null);
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = FallbackShader();
				}
				bool vertexColors = (Object)(object)val2 == (Object)null || !val2.HasProperty("_MainTex");
				powerup.mesh = ToMesh(data, powerup.name, vertexColors);
				powerup.material = ToMaterial(data, val2, powerup.name);
			}
			catch (Exception ex)
			{
				WispPlugin.Log.LogWarning((object)("Will-o'-the-wisp pickup model was not applied (" + ex.Message + ")."));
				if ((Object)(object)val != (Object)null && (Object)(object)val.mesh != (Object)null)
				{
					powerup.mesh = val.mesh;
					powerup.material = val.material;
				}
				else
				{
					WispPlugin.Log.LogWarning((object)"No Blue powerup to borrow a pickup model from; dropped wisps will be invisible.");
				}
			}
		}

		private static Powerup Donor(Powerup[] pool, Powerup self)
		{
			if (pool == null)
			{
				return null;
			}
			Powerup val = null;
			foreach (Powerup val2 in pool)
			{
				if (!((Object)(object)val2 == (Object)null) && val2 != self)
				{
					if ((Object)(object)val2.material != (Object)null)
					{
						return val2;
					}
					if ((Object)(object)val == (Object)null && (Object)(object)val2.mesh != (Object)null)
					{
						val = val2;
					}
				}
			}
			return val;
		}

		private static string Read(string name)
		{
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
			if (stream == null)
			{
				throw new FileNotFoundException(name);
			}
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}

		private static Mesh ToMesh(PickupMesh data, string itemName, bool vertexColors)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			Mesh val = new Mesh
			{
				name = itemName + " pickup",
				hideFlags = (HideFlags)32
			};
			List<Vector3> list = new List<Vector3>(data.Positions.Length);
			List<Vector3> list2 = new List<Vector3>(data.Normals.Length);
			List<Vector2> list3 = new List<Vector2>(data.U.Length);
			List<Color> list4 = new List<Color>(data.Positions.Length);
			for (int i = 0; i < data.Positions.Length; i++)
			{
				list.Add(new Vector3(data.Positions[i].X, data.Positions[i].Y, data.Positions[i].Z));
				list2.Add(new Vector3(data.Normals[i].X, data.Normals[i].Y, data.Normals[i].Z));
				list3.Add(new Vector2(data.U[i], 0.5f));
				list4.Add((Color)(vertexColors ? new Color(data.R[i], data.G[i], data.B[i], 1f) : Color.white));
			}
			val.SetVertices(list);
			val.SetNormals(list2);
			val.SetUVs(0, list3);
			val.SetColors(list4);
			val.SetTriangles(data.Triangles, 0);
			val.RecalculateBounds();
			return val;
		}

		private static Material FallbackShader()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			Shader val = Shader.Find("Standard");
			if ((Object)(object)val == (Object)null)
			{
				val = Shader.Find("Diffuse");
			}
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return new Material(val);
		}

		private static Material ToMaterial(PickupMesh data, Material source, string itemName)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0067: Expected O, but got Unknown
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source == (Object)null)
			{
				return null;
			}
			if (!source.HasProperty("_MainTex"))
			{
				MaterialSpec materialSpec = data.Palette[0];
				return new Material(source)
				{
					name = itemName + " pickup",
					color = new Color(materialSpec.R, materialSpec.G, materialSpec.B, 1f),
					hideFlags = (HideFlags)32
				};
			}
			Texture2D val = new Texture2D(data.Palette.Count, 1, (TextureFormat)4, false)
			{
				name = itemName + " palette",
				filterMode = (FilterMode)0,
				wrapMode = (TextureWrapMode)1,
				hideFlags = (HideFlags)32
			};
			for (int i = 0; i < data.Palette.Count; i++)
			{
				MaterialSpec materialSpec2 = data.Palette[i];
				val.SetPixel(i, 0, new Color(materialSpec2.R, materialSpec2.G, materialSpec2.B, 1f));
			}
			val.Apply(false, true);
			Material val2 = new Material(source)
			{
				name = itemName + " pickup",
				hideFlags = (HideFlags)32
			};
			val2.mainTexture = (Texture)(object)val;
			val2.mainTextureScale = Vector2.one;
			val2.mainTextureOffset = Vector2.zero;
			if (val2.HasProperty("_Color"))
			{
				val2.color = Color.white;
			}
			return val2;
		}
	}
	internal readonly struct Vec3
	{
		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public Vec3(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public static Vec3 operator -(Vec3 a, Vec3 b)
		{
			return new Vec3(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
		}
	}
	internal readonly struct MaterialSpec
	{
		public readonly float R;

		public readonly float G;

		public readonly float B;

		public readonly float Emission;

		public MaterialSpec(float r, float g, float b, float emission)
		{
			R = r;
			G = g;
			B = b;
			Emission = emission;
		}
	}
	internal sealed class SubMesh
	{
		public readonly string Material;

		public readonly int[] Triangles;

		public SubMesh(string material, int[] triangles)
		{
			Material = material;
			Triangles = triangles;
		}
	}
	internal sealed class PickupModel
	{
		private sealed class ObjObject
		{
			public readonly List<Vec3> Positions = new List<Vec3>();

			public readonly List<Vec3> Normals = new List<Vec3>();

			public readonly List<string> MaterialOrder = new List<string>();

			public readonly Dictionary<string, List<int>> Triangles = new Dictionary<string, List<int>>();
		}

		public readonly string Name;

		public readonly Vec3[] Positions;

		public readonly Vec3[] Normals;

		public readonly IReadOnlyList<SubMesh> SubMeshes;

		public readonly IReadOnlyDictionary<string, MaterialSpec> Materials;

		public int TriangleCount => Positions.Length / 3;

		public PickupModel(string name, Vec3[] positions, Vec3[] normals, IReadOnlyList<SubMesh> subMeshes, IReadOnlyDictionary<string, MaterialSpec> materials)
		{
			Name = name;
			Positions = positions;
			Normals = normals;
			SubMeshes = subMeshes;
			Materials = materials;
		}

		public static PickupModel Parse(string objText, string json)
		{
			if (!(JsonReader.Parse(json) is Dictionary<string, object> obj))
			{
				throw new FormatException("pickup json: top level is not an object.");
			}
			string text = Str(obj, "name");
			Dictionary<string, MaterialSpec> dictionary = ReadMaterials(Obj(obj, "materials"));
			Dictionary<string, ObjObject> dictionary2 = ReadObj(objText, text);
			if (dictionary2.Count != 1)
			{
				throw new FormatException($"{text}: a pickup OBJ needs exactly one object, it has {dictionary2.Count}.");
			}
			ObjObject objObject = null;
			foreach (ObjObject value in dictionary2.Values)
			{
				objObject = value;
			}
			List<SubMesh> list = new List<SubMesh>();
			foreach (string item in objObject.MaterialOrder)
			{
				if (!dictionary.ContainsKey(item))
				{
					throw new FormatException(text + ": uses undefined material '" + item + "'.");
				}
				list.Add(new SubMesh(item, objObject.Triangles[item].ToArray()));
			}
			PickupModel pickupModel = new PickupModel(text, objObject.Positions.ToArray(), objObject.Normals.ToArray(), list, dictionary);
			int num = (int)Num(obj, "tris");
			if (pickupModel.TriangleCount != num)
			{
				throw new FormatException($"{text}: OBJ has {pickupModel.TriangleCount} triangles, the json says {num}.");
			}
			return pickupModel;
		}

		private static Dictionary<string, ObjObject> ReadObj(string text, string name)
		{
			if (text == null)
			{
				throw new FormatException(name + ": OBJ text is null.");
			}
			List<Vec3> list = new List<Vec3>();
			List<Vec3> list2 = new List<Vec3>();
			Dictionary<string, ObjObject> dictionary = new Dictionary<string, ObjObject>();
			ObjObject objObject = null;
			string text2 = null;
			string[] array = text.Split(new char[1] { '\n' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
				if (array2.Length == 0)
				{
					continue;
				}
				switch (array2[0])
				{
				case "v":
				{
					Vec3 vec2 = ReadVec(array2, name, i);
					list.Add(new Vec3(0f - vec2.X, vec2.Y, vec2.Z));
					break;
				}
				case "vn":
				{
					Vec3 vec = ReadVec(array2, name, i);
					list2.Add(new Vec3(0f - vec.X, vec.Y, vec.Z));
					break;
				}
				case "o":
				{
					string text3 = string.Join(" ", array2, 1, array2.Length - 1);
					if (dictionary.ContainsKey(text3))
					{
						throw new FormatException(name + ": OBJ object " + text3 + " appears twice.");
					}
					objObject = new ObjObject();
					dictionary.Add(text3, objObject);
					text2 = null;
					break;
				}
				case "usemtl":
					text2 = ((array2.Length > 1) ? array2[1] : null);
					break;
				case "f":
					if (objObject == null || text2 == null)
					{
						throw new FormatException($"{name}: OBJ line {i + 1} has a face outside an object or material.");
					}
					if (array2.Length != 4)
					{
						throw new FormatException($"{name}: OBJ line {i + 1} is not a triangle.");
					}
					AddTriangle(objObject, text2, list, list2, array2, name, i);
					break;
				}
			}
			return dictionary;
		}

		private static void AddTriangle(ObjObject obj, string material, List<Vec3> vertices, List<Vec3> normals, string[] tokens, string name, int line)
		{
			if (!obj.Triangles.TryGetValue(material, out var value))
			{
				value = new List<int>();
				obj.Triangles.Add(material, value);
				obj.MaterialOrder.Add(material);
			}
			int[] array = new int[3] { 1, 3, 2 };
			foreach (int num in array)
			{
				string[] array2 = tokens[num].Split(new char[1] { '/' });
				Vec3 item = vertices[Index(array2[0], vertices.Count, name, line)];
				if (array2.Length < 3 || array2[2].Length == 0)
				{
					throw new FormatException($"{name}: OBJ line {line + 1} has a corner without a normal.");
				}
				value.Add(obj.Positions.Count);
				obj.Positions.Add(item);
				obj.Normals.Add(normals[Index(array2[2], normals.Count, name, line)]);
			}
		}

		private static int Index(string raw, int count, string name, int line)
		{
			if (!int.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result == 0)
			{
				throw new FormatException($"{name}: OBJ line {line + 1} has a bad index '{raw}'.");
			}
			int num = ((result > 0) ? (result - 1) : (count + result));
			if (num < 0 || num >= count)
			{
				throw new FormatException($"{name}: OBJ line {line + 1} index {raw} is out of range.");
			}
			return num;
		}

		private static Vec3 ReadVec(string[] tokens, string name, int line)
		{
			if (tokens.Length < 4)
			{
				throw new FormatException($"{name}: OBJ line {line + 1} needs three numbers.");
			}
			return new Vec3(Float(tokens[1], name, line), Float(tokens[2], name, line), Float(tokens[3], name, line));
		}

		private static float Float(string raw, string name, int line)
		{
			if (!float.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				throw new FormatException($"{name}: OBJ line {line + 1} has a bad number '{raw}'.");
			}
			return result;
		}

		private static Dictionary<string, MaterialSpec> ReadMaterials(Dictionary<string, object> raw)
		{
			Dictionary<string, MaterialSpec> dictionary = new Dictionary<string, MaterialSpec>();
			foreach (KeyValuePair<string, object> item in raw)
			{
				if (!(item.Value is Dictionary<string, object> obj))
				{
					throw new FormatException("pickup json: material " + item.Key + " is not an object.");
				}
				Vec3 vec = ReadVec3(obj, "rgb");
				dictionary.Add(item.Key, new MaterialSpec(vec.X, vec.Y, vec.Z, (float)Num(obj, "emission")));
			}
			return dictionary;
		}

		private static Vec3 ReadVec3(Dictionary<string, object> obj, string key)
		{
			List<object> list = Arr(obj, key);
			if (list.Count != 3 || !(list[0] is double num) || !(list[1] is double num2) || !(list[2] is double num3))
			{
				throw new FormatException("pickup json: '" + key + "' is not three numbers.");
			}
			return new Vec3((float)num, (float)num2, (float)num3);
		}

		private static object Field(Dictionary<string, object> obj, string key)
		{
			if (!obj.TryGetValue(key, out var value))
			{
				throw new FormatException("pickup json: missing '" + key + "'.");
			}
			return value;
		}

		private static string Str(Dictionary<string, object> obj, string key)
		{
			return (Field(obj, key) as string) ?? throw new FormatException("pickup json: '" + key + "' is not a string.");
		}

		private static double Num(Dictionary<string, object> obj, string key)
		{
			object obj2 = Field(obj, key);
			if (obj2 is double)
			{
				return (double)obj2;
			}
			throw new FormatException("pickup json: '" + key + "' is not a number.");
		}

		private static List<object> Arr(Dictionary<string, object> obj, string key)
		{
			return (Field(obj, key) as List<object>) ?? throw new FormatException("pickup json: '" + key + "' is not an array.");
		}

		private static Dictionary<string, object> Obj(Dictionary<string, object> obj, string key)
		{
			return (Field(obj, key) as Dictionary<string, object>) ?? throw new FormatException("pickup json: '" + key + "' is not an object.");
		}
	}
	internal sealed class PickupMesh
	{
		public readonly Vec3[] Positions;

		public readonly Vec3[] Normals;

		public readonly float[] U;

		public readonly float[] R;

		public readonly float[] G;

		public readonly float[] B;

		public readonly int[] Triangles;

		public readonly IReadOnlyList<MaterialSpec> Palette;

		private PickupMesh(Vec3[] positions, Vec3[] normals, float[] u, float[] r, float[] g, float[] b, int[] triangles, IReadOnlyList<MaterialSpec> palette)
		{
			Positions = positions;
			Normals = normals;
			U = u;
			R = r;
			G = g;
			B = b;
			Triangles = triangles;
			Palette = palette;
		}

		public static PickupMesh From(PickupModel model)
		{
			Vec3 vec = new Vec3(float.MaxValue, float.MaxValue, float.MaxValue);
			Vec3 vec2 = new Vec3(float.MinValue, float.MinValue, float.MinValue);
			Vec3[] positions = model.Positions;
			for (int i = 0; i < positions.Length; i++)
			{
				Vec3 vec3 = positions[i];
				vec = new Vec3(Math.Min(vec.X, vec3.X), Math.Min(vec.Y, vec3.Y), Math.Min(vec.Z, vec3.Z));
				vec2 = new Vec3(Math.Max(vec2.X, vec3.X), Math.Max(vec2.Y, vec3.Y), Math.Max(vec2.Z, vec3.Z));
			}
			Vec3 vec4 = new Vec3((vec.X + vec2.X) / 2f, (vec.Y + vec2.Y) / 2f, (vec.Z + vec2.Z) / 2f);
			int count = model.SubMeshes.Count;
			List<Vec3> list = new List<Vec3>(model.Positions.Length);
			List<Vec3> list2 = new List<Vec3>(model.Positions.Length);
			List<float> list3 = new List<float>(model.Positions.Length);
			List<float> list4 = new List<float>(model.Positions.Length);
			List<float> list5 = new List<float>(model.Positions.Length);
			List<float> list6 = new List<float>(model.Positions.Length);
			List<MaterialSpec> list7 = new List<MaterialSpec>(count);
			for (int j = 0; j < count; j++)
			{
				SubMesh subMesh = model.SubMeshes[j];
				MaterialSpec item = model.Materials[subMesh.Material];
				list7.Add(item);
				float item2 = ((float)j + 0.5f) / (float)count;
				int[] triangles = subMesh.Triangles;
				foreach (int num in triangles)
				{
					list.Add(model.Positions[num] - vec4);
					list2.Add(model.Normals[num]);
					list3.Add(item2);
					list4.Add(item.R);
					list5.Add(item.G);
					list6.Add(item.B);
				}
			}
			int[] array = new int[list.Count];
			for (int k = 0; k < array.Length; k++)
			{
				array[k] = k;
			}
			return new PickupMesh(list.ToArray(), list2.ToArray(), list3.ToArray(), list4.ToArray(), list5.ToArray(), list6.ToArray(), array, list7);
		}
	}
	internal sealed class JsonReader
	{
		private readonly string text;

		private int pos;

		private JsonReader(string text)
		{
			this.text = text;
		}

		public static object Parse(string text)
		{
			if (text == null)
			{
				throw new FormatException("JSON text is null.");
			}
			JsonReader jsonReader = new JsonReader(text);
			object result = jsonReader.ReadValue();
			jsonReader.SkipWhitespace();
			if (jsonReader.pos != text.Length)
			{
				throw jsonReader.Error("trailing characters");
			}
			return result;
		}

		private object ReadValue()
		{
			SkipWhitespace();
			if (pos >= text.Length)
			{
				throw Error("unexpected end");
			}
			char c = text[pos];
			switch (c)
			{
			case '{':
				return ReadObject();
			case '[':
				return ReadArray();
			case '"':
				return ReadString();
			case 't':
				Expect("true");
				return true;
			case 'f':
				Expect("false");
				return false;
			case 'n':
				Expect("null");
				return null;
			default:
				switch (c)
				{
				case '-':
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					return ReadNumber();
				default:
					throw Error($"unexpected '{c}'");
				}
			}
		}

		private Dictionary<string, object> ReadObject()
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			pos++;
			SkipWhitespace();
			if (Peek() == '}')
			{
				pos++;
				return dictionary;
			}
			while (true)
			{
				SkipWhitespace();
				if (Peek() != '"')
				{
					throw Error("expected a key");
				}
				string key = ReadString();
				SkipWhitespace();
				if (Peek() != ':')
				{
					break;
				}
				pos++;
				dictionary[key] = ReadValue();
				SkipWhitespace();
				char c = Peek();
				pos++;
				switch (c)
				{
				case ',':
					break;
				case '}':
					return dictionary;
				default:
					throw Error("expected ',' or '}'");
				}
			}
			throw Error("expected ':'");
		}

		private List<object> ReadArray()
		{
			List<object> list = new List<object>();
			pos++;
			SkipWhitespace();
			if (Peek() == ']')
			{
				pos++;
				return list;
			}
			while (true)
			{
				list.Add(ReadValue());
				SkipWhitespace();
				char c = Peek();
				pos++;
				switch (c)
				{
				case ',':
					break;
				case ']':
					return list;
				default:
					throw Error("expected ',' or ']'");
				}
			}
		}

		private string ReadString()
		{
			pos++;
			StringBuilder stringBuilder = new StringBuilder();
			while (pos < text.Length)
			{
				char c = text[pos++];
				switch (c)
				{
				case '"':
					return stringBuilder.ToString();
				default:
					stringBuilder.Append(c);
					break;
				case '\\':
				{
					if (pos >= text.Length)
					{
						throw Error("unterminated escape");
					}
					char c2 = text[pos++];
					switch (c2)
					{
					case '"':
						stringBuilder.Append('"');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'u':
						if (pos + 4 > text.Length)
						{
							throw Error("short \\u escape");
						}
						stringBuilder.Append((char)int.Parse(text.Substring(pos, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture));
						pos += 4;
						break;
					default:
						throw Error($"bad escape '\\{c2}'");
					}
					break;
				}
				}
			}
			throw Error("unterminated string");
		}

		private double ReadNumber()
		{
			int num = pos;
			while (pos < this.text.Length && "+-0123456789.eE".IndexOf(this.text[pos]) >= 0)
			{
				pos++;
			}
			string text = this.text.Substring(num, pos - num);
			if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				throw Error("bad number '" + text + "'");
			}
			return result;
		}

		private void Expect(string word)
		{
			if (string.CompareOrdinal(text, pos, word, 0, word.Length) != 0)
			{
				throw Error("expected '" + word + "'");
			}
			pos += word.Length;
		}

		private char Peek()
		{
			if (pos >= text.Length)
			{
				return '\0';
			}
			return text[pos];
		}

		private void SkipWhitespace()
		{
			while (pos < text.Length && char.IsWhiteSpace(text[pos]))
			{
				pos++;
			}
		}

		private FormatException Error(string what)
		{
			return new FormatException($"JSON: {what} at offset {pos}.");
		}
	}
	internal static class PowerupIds
	{
		public static int Resolve(IEnumerable<int> existingIds, bool nameExists, int existingId)
		{
			if (nameExists)
			{
				return existingId;
			}
			int num = 0;
			bool flag = false;
			foreach (int existingId2 in existingIds)
			{
				if (!flag || existingId2 > num)
				{
					num = existingId2;
					flag = true;
				}
			}
			if (!flag)
			{
				return 0;
			}
			return num + 1;
		}
	}
	internal enum NameClaim
	{
		Fresh,
		Ours,
		Taken
	}
	internal static class PowerupNames
	{
		public static NameClaim Claim(bool nameExists, bool sameObject)
		{
			if (!nameExists)
			{
				return NameClaim.Fresh;
			}
			if (!sameObject)
			{
				return NameClaim.Taken;
			}
			return NameClaim.Ours;
		}

		public static T[] Without<T>(T[] items, T remove) where T : class
		{
			if (items == null || remove == null)
			{
				return items;
			}
			int num = 0;
			for (int i = 0; i < items.Length; i++)
			{
				if (items[i] != remove)
				{
					num++;
				}
			}
			if (num == items.Length)
			{
				return items;
			}
			T[] array = new T[num];
			int num2 = 0;
			for (int j = 0; j < items.Length; j++)
			{
				if (items[j] != remove)
				{
					array[num2++] = items[j];
				}
			}
			return array;
		}
	}
	internal static class StackCount
	{
		public static int Read(int[] counts, int id)
		{
			if (counts == null || id < 0 || id >= counts.Length)
			{
				return 0;
			}
			return Math.Max(0, counts[id]);
		}
	}
	public static class WispBlast
	{
		public static bool IsFiring;

		internal static readonly KillLedger Ledger = new KillLedger();

		private static bool _loggedWeapon;

		private static bool _loggedStrength;

		private static readonly List<Collider> ColliderBuffer = new List<Collider>();

		internal static void Schedule(Mob mob, int stacks)
		{
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)mob == (Object)null))
			{
				WispConfig cfg = WispPlugin.Cfg;
				ReadWeapon(out var heldAttack, out var heldIsBow, out var arrowAttack);
				float strength = ReadStrength();
				int num = WispDamage.BaseDamage(heldAttack, heldIsBow, arrowAttack, strength, cfg.MinimumBaseDamage.Value);
				float fraction = WispScaling.DamageFraction(stacks, cfg.BaseDamageFraction.Value, cfg.DamageFractionPerStack.Value);
				int num2 = WispScaling.FullDamage(num, fraction);
				float num3 = WispScaling.Radius(stacks, cfg.BaseRadius.Value, cfg.RadiusPerStack.Value);
				float value = cfg.Delay.Value;
				float value2 = cfg.SweetSpotInnerFraction.Value;
				float value3 = cfg.SweetSpotOuterMultiplier.Value;
				bool value4 = cfg.HitNeutralMobs.Value;
				bool value5 = cfg.ShowPillar.Value;
				ReadBody((Component)(object)mob, out var center, out var _);
				if (cfg.VerboseLogging.Value)
				{
					WispPlugin.Log.LogInfo((object)$"Will-o'-the-wisp scheduled: {stacks} stacks, base {num}, full {num2}, radius {num3:0.#}m.");
				}
				((MonoBehaviour)WispRunner.Instance).StartCoroutine(Fire(center, num2, num3, value, value2, value3, value4, value5));
			}
		}

		private static IEnumerator Fire(Vector3 at, int full, float radius, float delay, float inner, float outer, bool hitNeutral, bool showPillar)
		{
			//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)
			WispConfig cfg = WispPlugin.Cfg;
			yield return (object)new WaitForSeconds(delay);
			try
			{
				if (showPillar)
				{
					WispPillar.Spawn(at, radius, inner);
				}
				List<BlastMob> list = new List<BlastMob>();
				List<HitableMob> list2 = new List<HitableMob>();
				Collect(list, list2);
				List<BlastHit> list3 = WispPlan.Targets(list, at.x, at.y, at.z, full, radius, inner, outer, hitNeutral);
				IsFiring = true;
				try
				{
					for (int i = 0; i < list3.Count; i++)
					{
						HitableMob val = list2[list3[i].Index];
						if (!((Object)(object)val == (Object)null))
						{
							((Hitable)val).Hit(list3[i].Damage, 0f, 2, ((Component)val).transform.position, -1);
						}
					}
				}
				finally
				{
					IsFiring = false;
				}
				if (cfg.VerboseLogging.Value)
				{
					WispPlugin.Log.LogInfo((object)$"Will-o'-the-wisp fired: {list3.Count} hits, full {full}, radius {radius:0.#}m.");
				}
			}
			catch (Exception arg)
			{
				IsFiring = false;
				WispPlugin.Log.LogError((object)$"Will-o'-the-wisp blast failed: {arg}");
			}
		}

		private static void Collect(List<BlastMob> snapshot, List<HitableMob> hitables)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Invalid comparison between Unknown and I4
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_009c: 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)
			Dictionary<int, Mob> dictionary = (((Object)(object)MobManager.Instance != (Object)null) ? MobManager.Instance.mobs : null);
			if (dictionary == null)
			{
				return;
			}
			foreach (Mob value in dictionary.Values)
			{
				if (!((Object)(object)value == (Object)null))
				{
					HitableMob component = ((Component)value).GetComponent<HitableMob>();
					if (!((Object)(object)component == (Object)null))
					{
						ReadBody((Component)(object)value, out var center, out var extents);
						bool neutral = (Object)(object)value.mobType != (Object)null && (int)value.mobType.behaviour == 0;
						snapshot.Add(new BlastMob(center.x, center.y, center.z, extents.x, extents.y, extents.z, ((Hitable)component).hp > 0, neutral));
						hitables.Add(component);
					}
				}
			}
		}

		private static void ReadBody(Component host, out Vector3 center, out Vector3 extents)
		{
			//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_0012: 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_0053: 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_005b: 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_0067: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			center = host.transform.position;
			extents = Vector3.zero;
			host.GetComponentsInChildren<Collider>(false, ColliderBuffer);
			for (int i = 0; i < ColliderBuffer.Count; i++)
			{
				Collider val = ColliderBuffer[i];
				if (!((Object)(object)val == (Object)null) && val.enabled && !val.isTrigger)
				{
					Bounds bounds = val.bounds;
					center = ((Bounds)(ref bounds)).center;
					bounds = val.bounds;
					extents = ((Bounds)(ref bounds)).extents;
					break;
				}
			}
		}

		private static void ReadWeapon(out int heldAttack, out bool heldIsBow, out int arrowAttack)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			heldAttack = 0;
			heldIsBow = false;
			arrowAttack = 0;
			Hotbar instance = Hotbar.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				WarnWeapon();
				return;
			}
			InventoryItem currentItem = instance.currentItem;
			if ((Object)(object)currentItem == (Object)null)
			{
				return;
			}
			heldAttack = currentItem.attackDamage;
			heldIsBow = (int)currentItem.type == 9;
			if (heldIsBow)
			{
				InventoryUI instance2 = InventoryUI.Instance;
				InventoryCell val = (((Object)(object)instance2 != (Object)null) ? instance2.arrows : null);
				if ((Object)(object)val == (Object)null)
				{
					WarnWeapon();
				}
				else if ((Object)(object)val.currentItem != (Object)null)
				{
					arrowAttack = val.currentItem.attackDamage;
				}
			}
		}

		private static float ReadStrength()
		{
			PowerupInventory instance = PowerupInventory.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				WarnStrength(null);
				return 1f;
			}
			try
			{
				return instance.GetStrengthMultiplier((int[])null);
			}
			catch (Exception e)
			{
				WarnStrength(e);
				return 1f;
			}
		}

		private static void WarnWeapon()
		{
			if (!_loggedWeapon)
			{
				_loggedWeapon = true;
				WispPlugin.Log.LogWarning((object)"Will-o'-the-wisp could not read the held weapon. Blasts use MinimumBaseDamage until the weapon can be read.");
			}
		}

		private static void WarnStrength(Exception e)
		{
			if (!_loggedStrength)
			{
				_loggedStrength = true;
				if (e == null)
				{
					WispPlugin.Log.LogWarning((object)"Will-o'-the-wisp could not read strength. Blasts use a strength of 1.");
				}
				else
				{
					WispPlugin.Log.LogWarning((object)("Will-o'-the-wisp could not read strength. Blasts use a strength of 1. " + e.Message));
				}
			}
		}
	}
	internal sealed class WispConfig
	{
		public readonly ConfigEntry<float> BaseDamageFraction;

		public readonly ConfigEntry<float> DamageFractionPerStack;

		public readonly ConfigEntry<float> BaseRadius;

		public readonly ConfigEntry<float> RadiusPerStack;

		public readonly ConfigEntry<float> Delay;

		public readonly ConfigEntry<float> SweetSpotInnerFraction;

		public readonly ConfigEntry<float> SweetSpotOuterMultiplier;

		public readonly ConfigEntry<int> MinimumBaseDamage;

		public readonly ConfigEntry<bool> HitNeutralMobs;

		public readonly ConfigEntry<bool> ShowPillar;

		public readonly ConfigEntry<float> PillarDuration;

		public readonly ConfigEntry<KeyboardShortcut> GiveWispKey;

		public readonly ConfigEntry<bool> VerboseLogging;

		public WispConfig(ConfigFile cfg)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Expected O, but got Unknown
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Expected O, but got Unknown
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Expected O, but got Unknown
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			BaseDamageFraction = cfg.Bind<float>("Blast", "BaseDamageFraction", 3.5f, new ConfigDescription("Full pillar damage with 1 stack, as a multiple of weapon damage. 3.5 is the RoR2 wiki's 350%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			DamageFractionPerStack = cfg.Bind<float>("Blast", "DamageFractionPerStack", 2.8f, new ConfigDescription("Added to that multiple for each stack after the first. 2.8 is the RoR2 wiki's +280% per stack.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			BaseRadius = cfg.Bind<float>("Blast", "BaseRadius", 12f, new ConfigDescription("Blast radius in meters with 1 stack. 12 is the RoR2 wiki value. Muck's scale may feel larger. Tune this in game.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>()));
			RadiusPerStack = cfg.Bind<float>("Blast", "RadiusPerStack", 2.4f, new ConfigDescription("Extra meters of radius per stack after the first. 2.4 is the RoR2 wiki value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			Delay = cfg.Bind<float>("Blast", "Delay", 1f, new ConfigDescription("Seconds between the kill and the blast. 1 is the RoR2 wiki value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			SweetSpotInnerFraction = cfg.Bind<float>("Blast", "SweetSpotInnerFraction", 0.5f, new ConfigDescription("Fraction of the radius that takes full damage. RoR2 sweet spot falloff is full damage out to half the radius.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			SweetSpotOuterMultiplier = cfg.Bind<float>("Blast", "SweetSpotOuterMultiplier", 0.25f, new ConfigDescription("Damage multiple past the inner band and still inside the radius. RoR2 sweet spot falloff uses 25% there.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			MinimumBaseDamage = cfg.Bind<int>("Blast", "MinimumBaseDamage", 15, new ConfigDescription("Weapon damage used when the held attack is lower than this. 15 is the Rock and Gold Sword attack, so an empty hand still blasts. Applied before strength.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			HitNeutralMobs = cfg.Bind<bool>("Blast", "HitNeutralMobs", false, "If true, the blast also hits neutral animals such as cows.");
			ShowPillar = cfg.Bind<bool>("Visual", "ShowPillar", true, "Draw a green pillar on the killer's screen.");
			PillarDuration = cfg.Bind<float>("Visual", "PillarDuration", 0.6f, new ConfigDescription("Seconds the pillar stays visible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 5f), Array.Empty<object>()));
			GiveWispKey = cfg.Bind<KeyboardShortcut>("Debug", "GiveWispKey", KeyboardShortcut.Empty, "Press in-game to give yourself one Will-o'-the-wisp stack. Local test helper. Empty means disabled.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log each scheduled blast and each fired blast.");
		}

		public string Description()
		{
			return Describe(Delay.Value, BaseRadius.Value, BaseDamageFraction.Value, DamageFractionPerStack.Value);
		}

		internal static string Describe(float delay, float radius, float fraction, float perStack)
		{
			return string.Format(CultureInfo.InvariantCulture, "On kill, after {0}s a pillar hits enemies within {1}m for {2}% (+{3}% per stack) weapon damage.", Num(delay), Num(radius), RoundPercent(fraction), RoundPercent(perStack));
		}

		private static int RoundPercent(float fraction)
		{
			return (int)Math.Round(fraction * 100f, MidpointRounding.ToEven);
		}

		private static string Num(float value)
		{
			return value.ToString("0.#", CultureInfo.InvariantCulture);
		}
	}
	internal static class WispDamage
	{
		public static int BaseDamage(int heldAttack, bool heldIsBow, int arrowAttack, float strength, int minBase)
		{
			int num = (heldIsBow ? arrowAttack : heldAttack);
			if (num < minBase)
			{
				num = minBase;
			}
			return Math.Max(1, RoundToInt((float)num * strength));
		}

		private static int RoundToInt(float value)
		{
			return (int)Math.Round(value, MidpointRounding.ToEven);
		}
	}
	internal static class WispHooks
	{
		internal static bool KillHookApplied { get; private set; }

		internal static void Apply(Harmony harmony)
		{
			try
			{
				harmony.CreateClassProcessor(typeof(AddKillPatch)).Patch();
				KillHookApplied = true;
			}
			catch (Exception arg)
			{
				KillHookApplied = false;
				WispPlugin.Log.LogError((object)$"Will-o'-the-wisp could not patch PlayerStatus.AddKill. The powerup will not be registered. {arg}");
				return;
			}
			try
			{
				harmony.CreateClassProcessor(typeof(UnnumberWispPatch)).Patch();
				harmony.CreateClassProcessor(typeof(RegisterWispPatch)).Patch();
			}
			catch (Exception arg2)
			{
				WispPlugin.Log.LogError((object)$"Will-o'-the-wisp patched the kill hook but could not patch powerup registration. {arg2}");
			}
		}
	}
	internal sealed class WispPillar : MonoBehaviour
	{
		private sealed class Band
		{
			public Material Material;

			public Color Color;
		}

		private static readonly string[] ShaderCandidates = new string[6] { "Legacy Shaders/Particles/Additive", "Particles/Additive", "Particles/Standard Unlit", "Unlit/Color", "Sprites/Default", "Hidden/Internal-Colored" };

		private static Shader _shader;

		private static Mesh _cylinder;

		private static bool _missingLogged;

		private readonly List<Band> _bands = new List<Band>();

		private float _life;

		private float _age;

		public static void Spawn(Vector3 at, float radius, float innerFraction)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0030: 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_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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			Shader val = FindShader();
			Mesh val2 = CylinderMesh();
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null))
			{
				GameObject val3 = new GameObject("WispPillar");
				val3.transform.position = at + Vector3.up * 6f;
				WispPillar wispPillar = val3.AddComponent<WispPillar>();
				wispPillar._life = WispPlugin.Cfg.PillarDuration.Value;
				float num = radius * 2f;
				wispPillar.AddBand(val3.transform, val2, val, num, new Color(0.55f, 1f, 0.45f, 0.22f));
				if (innerFraction > 0f)
				{
					wispPillar.AddBand(val3.transform, val2, val, num * innerFraction, new Color(0.55f, 1f, 0.45f, 0.6f));
				}
			}
		}

		private void AddBand(Transform parent, Mesh mesh, Shader shader, float diameter, 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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("WispPillarBand");
			val.transform.SetParent(parent, false);
			val.transform.localScale = new Vector3(diameter, 6f, diameter);
			val.AddComponent<MeshFilter>().sharedMesh = mesh;
			MeshRenderer obj = val.AddComponent<MeshRenderer>();
			Material val2 = new Material(shader)
			{
				name = "WispPillar",
				hideFlags = (HideFlags)61
			};
			if (val2.HasProperty("_MainTex"))
			{
				val2.mainTexture = (Texture)(object)Texture2D.whiteTexture;
			}
			((Renderer)obj).sharedMaterial = val2;
			((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)obj).receiveShadows = false;
			Band band = new Band
			{
				Material = val2,
				Color = color
			};
			_bands.Add(band);
			Apply(band, 1f);
		}

		private void Update()
		{
			_age += Time.deltaTime;
			if (_life <= 0f || _age >= _life)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			float fade = 1f - Mathf.Clamp01(_age / _life);
			for (int i = 0; i < _bands.Count; i++)
			{
				Apply(_bands[i], fade);
			}
		}

		private void OnDestroy()
		{
			for (int i = 0; i < _bands.Count; i++)
			{
				if ((Object)(object)_bands[i].Material != (Object)null)
				{
					Object.Destroy((Object)(object)_bands[i].Material);
				}
			}
		}

		private static void Apply(Band band, float fade)
		{
			//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_0030: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			Color color = band.Color;
			color.a *= fade;
			if (band.Material.HasProperty("_Color"))
			{
				band.Material.SetColor("_Color", color);
			}
			if (band.Material.HasProperty("_TintColor"))
			{
				band.Material.SetColor("_TintColor", color);
			}
			band.Material.color = color;
		}

		private static Mesh CylinderMesh()
		{
			if ((Object)(object)_cylinder != (Object)null)
			{
				return _cylinder;
			}
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)2);
			_cylinder = obj.GetComponent<MeshFilter>().sharedMesh;
			Object.DestroyImmediate((Object)(object)obj);
			return _cylinder;
		}

		private static Shader FindShader()
		{
			if ((Object)(object)_shader != (Object)null)
			{
				return _shader;
			}
			if (_missingLogged)
			{
				return null;
			}
			string[] shaderCandidates = ShaderCandidates;
			foreach (string text in shaderCandidates)
			{
				Shader val = Shader.Find(text);
				if (!((Object)(object)val == (Object)null))
				{
					_shader = val;
					WispPlugin.Log.LogInfo((object)("Will-o'-the-wisp pillar uses shader '" + text + "'."));
					return _shader;
				}
			}
			_missingLogged = true;
			WispPlugin.Log.LogWarning((object)"No usable built-in shader found for the wisp pillar. The pillar is hidden and damage still works.");
			return null;
		}
	}
	internal readonly struct BlastMob
	{
		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public readonly float HalfX;

		public readonly float HalfY;

		public readonly float HalfZ;

		public readonly bool Alive;

		public readonly bool Neutral;

		public BlastMob(float x, float y, float z, bool alive, bool neutral)
			: this(x, y, z, 0f, 0f, 0f, alive, neutral)
		{
		}

		public BlastMob(float x, float y, float z, float halfX, float halfY, float halfZ, bool alive, bool neutral)
		{
			X = x;
			Y = y;
			Z = z;
			HalfX = halfX;
			HalfY = halfY;
			HalfZ = halfZ;
			Alive = alive;
			Neutral = neutral;
		}
	}
	internal readonly struct BlastHit
	{
		public readonly int Index;

		public readonly int Damage;

		public BlastHit(int index, int damage)
		{
			Index = index;
			Damage = damage;
		}
	}
	internal static class WispPlan
	{
		public static List<BlastHit> Targets(IReadOnlyList<BlastMob> mobs, float ox, float oy, float oz, int full, float radius, float innerFraction, float outerMultiplier, bool hitNeutral)
		{
			List<BlastHit> list = new List<BlastHit>();
			for (int i = 0; i < mobs.Count; i++)
			{
				BlastMob mob = mobs[i];
				if (mob.Alive && (!mob.Neutral || hitNeutral))
				{
					int num = WispScaling.DamageAt(full, DistanceToBody(mob, ox, oy, oz), radius, innerFraction, outerMultiplier);
					if (num != 0)
					{
						list.Add(new BlastHit(i, num));
					}
				}
			}
			return list;
		}

		private static float DistanceToBody(BlastMob mob, float ox, float oy, float oz)
		{
			float num = Clamp(ox, mob.X, mob.HalfX);
			float num2 = Clamp(oy, mob.Y, mob.HalfY);
			float num3 = Clamp(oz, mob.Z, mob.HalfZ);
			float num4 = num - ox;
			float num5 = num2 - oy;
			float num6 = num3 - oz;
			return (float)Math.Sqrt(num4 * num4 + num5 * num5 + num6 * num6);
		}

		private static float Clamp(float value, float center, float half)
		{
			float num = Math.Abs(half);
			float num2 = center - num;
			float num3 = center + num;
			if (value < num2)
			{
				return num2;
			}
			if (value > num3)
			{
				return num3;
			}
			return value;
		}
	}
	[BepInPlugin("evan.muck.willothewisp", "Will-o'-the-wisp", "0.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class WispPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.willothewisp";

		public const string Name = "Will-o'-the-wisp";

		public const string Version = "0.1.2";

		internal static ManualLogSource Log;

		internal static WispConfig Cfg;

		private void Awake()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new WispConfig(((BaseUnityPlugin)this).Config);
			WispPowerup.Create();
			WispRunner.Ensure();
			WispHooks.Apply(new Harmony("evan.muck.willothewisp"));
			Log.LogInfo((object)"Will-o'-the-wisp 0.1.2 loaded. Every player in a lobby needs this mod (same version).");
		}
	}
	internal static class WispPowerup
	{
		public const string PowerupName = "Will-o'-the-wisp";

		internal static readonly FieldRef<PowerupInventory, int[]> Counts = AccessTools.FieldRefAccess<PowerupInventory, int[]>("powerups");

		public static Powerup Definition { get; private set; }

		public static int Id { get; internal set; } = -1;

		public static void Create()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			Powerup obj = ScriptableObject.CreateInstance<Powerup>();
			obj.name = "Will-o'-the-wisp";
			((Object)obj).name = "Will-o'-the-wisp";
			obj.description = WispPlugin.Cfg.Description();
			obj.tier = (PowerTier)1;
			obj.sprite = LoadIcon();
			((Object)obj).hideFlags = (HideFlags)32;
			Definition = obj;
		}

		public static void GiveOneLocal()
		{
			PowerupInventory instance = PowerupInventory.Instance;
			int[] array = (((Object)(object)instance != (Object)null) ? Counts.Invoke(instance) : null);
			if (array == null || Id < 0 || Id >= array.Length || (Object)(object)PowerupUI.Instance == (Object)null || (Object)(object)PlayerStatus.Instance == (Object)null)
			{
				WispPlugin.Log.LogWarning((object)"Can't give Will-o'-the-wisp: not in a game yet, or the powerup isn't registered.");
				return;
			}
			array[Id]++;
			PowerupUI.Instance.AddPowerup(Id);
			PlayerStatus.Instance.UpdateStats();
			WispPlugin.Log.LogInfo((object)$"Debug: Will-o'-the-wisp stacks now {array[Id]} (local only; the host's copy of your powerups is not updated).");
		}

		private static Sprite LoadIcon()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0084: 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)
			try
			{
				using Stream stream = typeof(WispPowerup).Assembly.GetManifestResourceStream("MuckWisp.icon.png");
				if (stream == null)
				{
					throw new FileNotFoundException("embedded icon.png missing");
				}
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(val, memoryStream.ToArray()))
				{
					throw new InvalidDataException("icon.png failed to decode");
				}
				((Object)val).name = "WispIcon";
				((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), 100f);
				((Object)obj).name = "WispIcon";
				((Object)obj).hideFlags = (HideFlags)32;
				return obj;
			}
			catch (Exception arg)
			{
				WispPlugin.Log.LogError((object)$"Failed to load Will-o'-the-wisp icon: {arg}");
				return null;
			}
		}
	}
	[HarmonyPatch(typeof(ItemManager), "InitAllPowerups")]
	internal static class UnnumberWispPatch
	{
		[HarmonyPriority(600)]
		private static void Prefix(ItemManager __instance)
		{
			Powerup definition = WispPowerup.Definition;
			if (!((Object)(object)definition == (Object)null))
			{
				__instance.powerupsBlue = PowerupNames.Without(__instance.powerupsBlue, definition);
			}
		}
	}
	[HarmonyPatch(typeof(ItemManager), "InitAllPowerups")]
	internal static class RegisterWispPatch
	{
		[HarmonyPriority(600)]
		[HarmonyAfter(new string[] { "evan.muck.ukulele", "evan.muck.atgmissile", "evan.muck.crowbar", "evan.muck.stickybomb", "evan.muck.tritipdagger" })]
		private static void Postfix(ItemManager __instance)
		{
			if (!WispHooks.KillHookApplied)
			{
				return;
			}
			Powerup definition = WispPowerup.Definition;
			if ((Object)(object)definition == (Object)null)
			{
				return;
			}
			Dictionary<int, Powerup> allPowerups = __instance.allPowerups;
			Dictionary<string, int> stringToPowerupId = __instance.stringToPowerupId;
			if (allPowerups == null || stringToPowerupId == null)
			{
				return;
			}
			int value;
			bool num = stringToPowerupId.TryGetValue("Will-o'-the-wisp", out value);
			Powerup value2;
			bool sameObject = num && allPowerups.TryGetValue(value, out value2) && (Object)(object)value2 == (Object)(object)definition;
			NameClaim nameClaim = PowerupNames.Claim(num, sameObject);
			if (nameClaim == NameClaim.Taken)
			{
				WispPlugin.Log.LogWarning((object)"The name Will-o'-the-wisp is already used by another powerup. This one will not be added to the drop pool.");
				return;
			}
			PickupArt.Apply(definition, __instance.powerupsBlue);
			int num2;
			if (nameClaim == NameClaim.Fresh)
			{
				num2 = PowerupIds.Resolve(allPowerups.Keys, nameExists: false, 0);
				allPowerups.Add(num2, definition);
				stringToPowerupId.Add("Will-o'-the-wisp", num2);
				definition.id = num2;
			}
			else
			{
				num2 = value;
			}
			if (__instance.powerupsBlue != null && !__instance.powerupsBlue.Contains(definition))
			{
				__instance.powerupsBlue = CollectionExtensions.AddToArray<Powerup>(__instance.powerupsBlue, definition);
			}
			WispPowerup.Id = num2;
			int num3 = ((__instance.powerupsBlue != null) ? __instance.powerupsBlue.Length : 0);
			WispPlugin.Log.LogInfo((object)$"Registered Will-o'-the-wisp as Blue powerup id {num2} ({allPowerups.Count} powerups total, {num3} in the Blue pool).");
			if (WispPlugin.Cfg.VerboseLogging.Value)
			{
				WispPlugin.Log.LogInfo((object)("Powerup ids: " + string.Join(", ", from kv in allPowerups
					orderby kv.Key
					select $"{kv.Key}={kv.Value.name}")));
			}
		}
	}
	internal sealed class WispRunner : MonoBehaviour
	{
		private static WispRunner _instance;

		public static WispRunner Instance
		{
			get
			{
				Ensure();
				return _instance;
			}
		}

		public static void Ensure()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("MuckWisp")
				{
					hideFlags = (HideFlags)61
				};
				Object.DontDestroyOnLoad((Object)val);
				_instance = val.AddComponent<WispRunner>();
			}
		}

		private void OnEnable()
		{
			SceneManager.activeSceneChanged += OnSceneChanged;
		}

		private void OnDisable()
		{
			SceneManager.activeSceneChanged -= OnSceneChanged;
		}

		private void Update()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = WispPlugin.Cfg.GiveWispKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				WispPowerup.GiveOneLocal();
			}
		}

		private static void OnSceneChanged(Scene previous, Scene next)
		{
			if ((Object)(object)_instance != (Object)null)
			{
				((MonoBehaviour)_instance).StopAllCoroutines();
			}
			WispBlast.Ledger.Reset();
		}
	}
	internal static class WispScaling
	{
		public static float DamageFraction(int stacks, float baseFraction, float perStack)
		{
			if (stacks <= 0)
			{
				return 0f;
			}
			return baseFraction + perStack * (float)(stacks - 1);
		}

		public static float Radius(int stacks, float baseRadius, float perStack)
		{
			if (stacks <= 0)
			{
				return 0f;
			}
			return baseRadius + perStack * (float)(stacks - 1);
		}

		public static int FullDamage(int baseDamage, float fraction)
		{
			if (fraction <= 0f)
			{
				return 0;
			}
			return Math.Max(1, RoundToInt((float)baseDamage * fraction));
		}

		public static int DamageAt(int full, float distance, float radius, float innerFraction, float outerMultiplier)
		{
			if (distance > radius)
			{
				return 0;
			}
			if (distance <= radius * innerFraction)
			{
				return full;
			}
			return Math.Max(1, RoundToInt((float)full * outerMultiplier));
		}

		private static int RoundToInt(float value)
		{
			return (int)Math.Round(value, MidpointRounding.ToEven);
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MuckWisp";

		public const string PLUGIN_NAME = "Will-o'-the-wisp";

		public const string PLUGIN_VERSION = "0.1.2";
	}
}