Decompiled source of ShapedGlass v0.2.2

plugins/MuckShapedGlass.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[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("MuckShapedGlass")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds Risk of Rain 2's Shaped Glass (+100% damage and half max HP per stack) to Muck as an Orange curse powerup.")]
[assembly: AssemblyFileVersion("0.2.2.0")]
[assembly: AssemblyInformationalVersion("0.2.2+879fc7ac66fec639f2f84b4b7a65d5c61c9ed734")]
[assembly: AssemblyProduct("Shaped Glass")]
[assembly: AssemblyTitle("MuckShapedGlass")]
[assembly: InternalsVisibleTo("MuckShapedGlass.Tests")]
[assembly: AssemblyVersion("0.2.2.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckShapedGlass
{
	internal static class EstimateCounts
	{
		public static int[] Select(int[] passed, int[] hostOwn, int[] localCounts)
		{
			if (passed != null && passed == hostOwn)
			{
				return localCounts;
			}
			return passed ?? localCounts;
		}
	}
	internal readonly struct GlassTuning
	{
		public const float MaxDamageBonusPerStack = 4f;

		public readonly float DamageBonusPerStack;

		public readonly float MaxHpPerStack;

		public readonly int MaxEffectiveStacks;

		public readonly bool RequireHoldToPickUp;

		private float Bonus
		{
			get
			{
				if (!(DamageBonusPerStack > 0f))
				{
					return 0f;
				}
				return Math.Min(DamageBonusPerStack, 4f);
			}
		}

		private float BonusPercent => Bonus * 100f;

		public GlassTuning(float damageBonusPerStack, float maxHpPerStack, int maxEffectiveStacks, bool requireHoldToPickUp)
		{
			DamageBonusPerStack = damageBonusPerStack;
			MaxHpPerStack = maxHpPerStack;
			MaxEffectiveStacks = maxEffectiveStacks;
			RequireHoldToPickUp = requireHoldToPickUp;
		}

		public int Effective(int stacks)
		{
			if (stacks <= 0 || MaxEffectiveStacks <= 0)
			{
				return 0;
			}
			return Math.Min(stacks, MaxEffectiveStacks);
		}

		public float DamageFactor(int stacks)
		{
			return 1f + Bonus * (float)Effective(stacks);
		}

		public float ScaleStrength(float vanilla, int stacks)
		{
			return vanilla * DamageFactor(stacks);
		}

		public int ScaleMaxHp(int vanillaMaxHp, int stacks)
		{
			int num = Effective(stacks);
			if (num == 0)
			{
				return vanillaMaxHp;
			}
			return (int)Math.Max(1.0, Math.Floor((double)vanillaMaxHp * HpFactor(num)));
		}

		public int ScaleShield(int vanillaShield, int stacks)
		{
			int num = Effective(stacks);
			if (num == 0 || vanillaShield <= 0)
			{
				return vanillaShield;
			}
			return (int)Math.Floor((double)vanillaShield * HpFactor(num));
		}

		public int ScaleMaxHpAndShield(int vanillaTotal, int shield, int stacks)
		{
			return ScaleMaxHp(vanillaTotal - shield, stacks) + ScaleShield(shield, stacks);
		}

		private double HpFactor(int n)
		{
			return Math.Pow((MaxHpPerStack > 0f) ? ((double)Math.Min(MaxHpPerStack, 1f)) : 1.0, n);
		}

		public static float ClampHp(float hp, int maxHp)
		{
			if (!(hp > (float)maxHp))
			{
				return hp;
			}
			return maxHp;
		}

		public bool PickupAllowed(bool isShapedGlass, bool interactHeld)
		{
			return !isShapedGlass || !RequireHoldToPickUp || interactHeld;
		}

		public static bool InteractHeld(bool keyHeld, bool chatTyping)
		{
			if (keyHeld)
			{
				return !chatTyping;
			}
			return false;
		}

		public string Description()
		{
			string text = string.Format(CultureInfo.InvariantCulture, "Deal +{0:0.##}% damage per stack. Max HP and shield x{1:0.##} per stack.", BonusPercent, MaxHpPerStack);
			if (RequireHoldToPickUp)
			{
				text += " Hold interact to pick up.";
			}
			return text;
		}

		public string PickupHint(string interactKey)
		{
			return string.Format(CultureInfo.InvariantCulture, "Hold {0} to pick up Shaped Glass (curse: +{1:0.##}% damage, x{2:0.##} max HP).", interactKey, BonusPercent, MaxHpPerStack);
		}

		public string Summary()
		{
			return string.Format(CultureInfo.InvariantCulture, "damage +{0:0.##}%/stack, max HP x{1:0.##}/stack, max {2} stacks, hold-to-pick-up {3}", BonusPercent, MaxHpPerStack, MaxEffectiveStacks, RequireHoldToPickUp ? "on" : "off");
		}
	}
	internal static class LocalPlayerTouch
	{
		public static bool Matches(int layer, bool localTag, int playerLayer)
		{
			return playerLayer >= 0 && layer == playerLayer && localTag;
		}
	}
	internal static class OnceClaim
	{
		private static readonly object Token = new object();

		private static readonly ConditionalWeakTable<object, object> seen = new ConditionalWeakTable<object, object>();

		public static bool TryClaim(object key)
		{
			if (key == null)
			{
				return false;
			}
			if (seen.TryGetValue(key, out var _))
			{
				return false;
			}
			seen.Add(key, Token);
			return true;
		}
	}
	internal static class PickupArt
	{
		public const string ObjResource = "MuckShapedGlass.pickup.obj";

		public const string JsonResource = "MuckShapedGlass.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("MuckShapedGlass.pickup.obj"), Read("MuckShapedGlass.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)
			{
				ShapedGlassPlugin.Log.LogWarning((object)("Shaped Glass 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
				{
					ShapedGlassPlugin.Log.LogWarning((object)"No Orange powerup to borrow a pickup model from; dropped Shaped Glass 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;
		}
	}
	[HarmonyPatch(typeof(Item), "OnTriggerStay", new Type[] { typeof(Collider) })]
	internal static class PickupGatePatch
	{
		private static bool logged;

		private static bool Prefix(Item __instance, Collider __0)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Powerup powerup = __instance.powerup;
				if (!((Object)(object)powerup != (Object)null) || ShapedGlassPowerup.Id < 0 || (powerup != ShapedGlassPowerup.Definition && powerup.id != ShapedGlassPowerup.Id))
				{
					return true;
				}
				int layer = (((Object)(object)__0 != (Object)null) ? ((Component)__0).gameObject.layer : (-1));
				bool localTag = (Object)(object)__0 != (Object)null && ((Component)__0).CompareTag("Local");
				if (!LocalPlayerTouch.Matches(layer, localTag, LayerMask.NameToLayer("Player")))
				{
					return true;
				}
				GlassTuning glassTuning = ShapedGlassPlugin.Cfg.Tuning();
				bool chatTyping = (Object)(object)ChatBox.Instance != (Object)null && ChatBox.Instance.typing;
				if (glassTuning.PickupAllowed(isShapedGlass: true, GlassTuning.InteractHeld(Input.GetKey(InputManager.interact), chatTyping)))
				{
					return true;
				}
				if (ShapedGlassPlugin.Cfg.ShowPickupHint.Value && (Object)(object)ChatBox.Instance != (Object)null && OnceClaim.TryClaim(__instance))
				{
					ChatBox.Instance.AppendMessage(-1, glassTuning.PickupHint(((object)Unsafe.As<KeyCode, KeyCode>(ref InputManager.interact)/*cast due to .constrained prefix*/).ToString()), "");
				}
				return false;
			}
			catch (Exception arg)
			{
				if (!logged)
				{
					logged = true;
					ShapedGlassPlugin.Log.LogError((object)$"Shaped Glass pickup gate failed: {arg}");
				}
				return true;
			}
		}
	}
	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 readonly struct PowerupClaim
	{
		public readonly bool Claimed;

		public readonly int Id;

		private PowerupClaim(bool claimed, int id)
		{
			Claimed = claimed;
			Id = id;
		}

		public static PowerupClaim Resolve(bool nameExists, bool existingIsOurs, int existingId, IEnumerable<int> existingIds)
		{
			if (nameExists && !existingIsOurs)
			{
				return new PowerupClaim(claimed: false, 0);
			}
			return new PowerupClaim(claimed: true, PowerupIds.Resolve(existingIds, nameExists, existingId));
		}

		public static bool FitsInventory(int id, int countAfterAdd)
		{
			if (id >= 0)
			{
				return id < countAfterAdd;
			}
			return false;
		}
	}
	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 readonly struct RegistrationPlan
	{
		public readonly bool Refused;

		public readonly bool Gap;

		public readonly bool AddToTables;

		public readonly bool AppendToPool;

		public readonly int Id;

		private RegistrationPlan(bool refused, bool gap, bool addToTables, bool appendToPool, int id)
		{
			Refused = refused;
			Gap = gap;
			AddToTables = addToTables;
			AppendToPool = appendToPool;
			Id = id;
		}

		public static RegistrationPlan Decide(bool nameExists, bool existingIsOurs, int existingId, IEnumerable<int> existingIds, bool alreadyInPool)
		{
			List<int> list = new List<int>();
			if (existingIds != null)
			{
				foreach (int existingId2 in existingIds)
				{
					list.Add(existingId2);
				}
			}
			PowerupClaim powerupClaim = PowerupClaim.Resolve(nameExists, existingIsOurs, existingId, list);
			if (!powerupClaim.Claimed)
			{
				return new RegistrationPlan(refused: true, gap: false, addToTables: false, appendToPool: false, 0);
			}
			if (!nameExists && !PowerupClaim.FitsInventory(powerupClaim.Id, list.Count + 1))
			{
				return new RegistrationPlan(refused: false, gap: true, addToTables: false, appendToPool: false, powerupClaim.Id);
			}
			return new RegistrationPlan(refused: false, gap: false, !nameExists, !alreadyInPool, powerupClaim.Id);
		}
	}
	internal static class RetiredDamageKey
	{
		public const string Section = "Shaped Glass";

		public const string Key = "DamageMultiplierPerStack";

		private const float OldDefault = 2f;

		public static string CustomisedValue(IEnumerable<string> cfgLines)
		{
			string text = null;
			foreach (string cfgLine in cfgLines)
			{
				string text2 = cfgLine.Trim();
				if (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal))
				{
					text = text2.Substring(1, text2.Length - 2).Trim();
					continue;
				}
				int num = text2.IndexOf('=');
				if (!(text != "Shaped Glass") && num >= 0 && !(text2.Substring(0, num).Trim() != "DamageMultiplierPerStack"))
				{
					string text3 = text2.Substring(num + 1).Trim();
					float result;
					return (float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out result) && result == 2f) ? null : text3;
				}
			}
			return null;
		}
	}
	internal sealed class ShapedGlassConfig
	{
		public static readonly GlassTuning DefaultTuning = new GlassTuning(1f, 0.5f, 10, requireHoldToPickUp: true);

		public readonly ConfigEntry<float> DamageBonusPerStack;

		public readonly ConfigEntry<float> MaxHpMultiplierPerStack;

		public readonly ConfigEntry<int> MaxEffectiveStacks;

		public readonly ConfigEntry<bool> RequireHoldToPickUp;

		public readonly ConfigEntry<bool> ShowPickupHint;

		public readonly ConfigEntry<KeyboardShortcut> GiveShapedGlassKey;

		public readonly ConfigEntry<bool> VerboseLogging;

		public readonly string RetiredDamageMultiplier;

		public ShapedGlassConfig(ConfigFile cfg)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			RetiredDamageMultiplier = ReadRetiredDamageMultiplier(cfg.ConfigFilePath);
			DamageBonusPerStack = cfg.Bind<float>("Shaped Glass", "DamageBonusPerStack", DefaultTuning.DamageBonusPerStack, new ConfigDescription("Damage added per stack, as a fraction of your damage without glass. Stacks add, they do not compound: damage is x(1 + this x stacks). Default 1, the Risk of Rain 2 card (+100% per stack): x2, x3, x4, up to x11 at 10 stacks. 0 removes the damage bonus. Range 0 to 4. Replaces DamageMultiplierPerStack, which compounded and is no longer read.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 4f), Array.Empty<object>()));
			MaxHpMultiplierPerStack = cfg.Bind<float>("Shaped Glass", "MaxHpMultiplierPerStack", DefaultTuning.MaxHpPerStack, new ConfigDescription("Max HP and Blue Pill shield multiplier applied once per stack. Stacks compound, and the result is floored with a minimum of 1 HP (shield may reach 0). Default 0.5, the Risk of Rain 2 card: x0.5, x0.25, x0.125. Range 0.05 to 1.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			MaxEffectiveStacks = cfg.Bind<int>("Shaped Glass", "MaxEffectiveStacks", DefaultTuning.MaxEffectiveStacks, new ConfigDescription("Stacks above this number change neither damage nor max HP. 0 turns the stat change off, and the item still drops. Default 10: x11 damage with the default bonus, and 100 max HP reaches the 1 HP floor at 6 stacks. Range 0 to 20.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>()));
			RequireHoldToPickUp = cfg.Bind<bool>("Shaped Glass", "RequireHoldToPickUp", DefaultTuning.RequireHoldToPickUp, "When true, Shaped Glass is picked up only while the interact key is held. Other powerups still pick up on touch.");
			ShowPickupHint = cfg.Bind<bool>("Shaped Glass", "ShowPickupHint", true, "When true, standing on Shaped Glass without holding interact shows one local chat line.");
			GiveShapedGlassKey = cfg.Bind<KeyboardShortcut>("Debug", "GiveShapedGlassKey", KeyboardShortcut.Empty, "Press in-game to give yourself one Shaped Glass stack. Empty disables the hotkey.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log the powerup id table when Shaped Glass registers.");
		}

		public GlassTuning Tuning()
		{
			return new GlassTuning(DamageBonusPerStack.Value, MaxHpMultiplierPerStack.Value, MaxEffectiveStacks.Value, RequireHoldToPickUp.Value);
		}

		private static string ReadRetiredDamageMultiplier(string path)
		{
			try
			{
				return File.Exists(path) ? RetiredDamageKey.CustomisedValue(File.ReadAllLines(path)) : null;
			}
			catch (Exception)
			{
				return null;
			}
		}

		public void HookSettingChanged()
		{
			DamageBonusPerStack.SettingChanged += OnGameplayChanged;
			MaxHpMultiplierPerStack.SettingChanged += OnGameplayChanged;
			MaxEffectiveStacks.SettingChanged += OnGameplayChanged;
			RequireHoldToPickUp.SettingChanged += OnGameplayChanged;
		}

		private void OnGameplayChanged(object sender, EventArgs e)
		{
			if ((Object)(object)ShapedGlassPowerup.Definition != (Object)null)
			{
				ShapedGlassPowerup.Definition.description = Tuning().Description();
			}
			try
			{
				if ((Object)(object)PlayerStatus.Instance != (Object)null && (Object)(object)PowerupInventory.Instance != (Object)null)
				{
					PlayerStatus.Instance.UpdateStats();
				}
			}
			catch (Exception arg)
			{
				ShapedGlassPlugin.Log.LogError((object)$"Shaped Glass could not reapply max HP after a config change: {arg}");
			}
			ShapedGlassPlugin.Log.LogInfo((object)Tuning().Summary());
		}
	}
	[BepInPlugin("evan.muck.shapedglass", "Shaped Glass", "0.2.2")]
	public class ShapedGlassPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.shapedglass";

		public const string Name = "Shaped Glass";

		public const string Version = "0.2.2";

		internal static ManualLogSource Log;

		internal static ShapedGlassConfig Cfg;

		private void Awake()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new ShapedGlassConfig(((BaseUnityPlugin)this).Config);
			if (Cfg.RetiredDamageMultiplier != null)
			{
				Log.LogWarning((object)("Your config sets DamageMultiplierPerStack = " + Cfg.RetiredDamageMultiplier + ". That key compounded and is no longer read. " + $"Damage now adds DamageBonusPerStack per stack (currently {Cfg.DamageBonusPerStack.Value:0.##}, so 1 means x2, x3, x4). Set that key instead."));
			}
			if (!ShapedGlassPowerup.TryBindCounts(out var why))
			{
				Log.LogError((object)("Shaped Glass disabled: " + why));
				return;
			}
			Harmony val = new Harmony("evan.muck.shapedglass");
			if (!TryPatch(val, typeof(UpdateStatsPatch)) || !TryPatch(val, typeof(StrengthPatch)))
			{
				val.UnpatchSelf();
				Log.LogError((object)"Shaped Glass disabled: a stat patch failed. It will not drop.");
				return;
			}
			if (!TryPatch(val, typeof(PickupGatePatch)))
			{
				Log.LogWarning((object)"Pickup gate failed; Shaped Glass will be picked up on touch.");
			}
			if (!TryPatch(val, typeof(HostHpEstimatePatch)))
			{
				Log.LogWarning((object)"Host HP-bar estimate patch failed; teammates' bars may lag.");
			}
			if (!TryPatch(val, typeof(DraculaPatch)))
			{
				Log.LogWarning((object)"Dracula clamp failed; a Dracula kill can leave HP above the halved max until the next stat update.");
			}
			ShapedGlassPowerup.Create();
			if (!TryPatch(val, typeof(RegisterShapedGlassPatch)))
			{
				val.UnpatchSelf();
				Log.LogError((object)"Shaped Glass disabled: the registration patch failed. It will not drop.");
			}
			else
			{
				ShapedGlassRunner.Ensure();
				Cfg.HookSettingChanged();
				Log.LogInfo((object)("Shaped Glass 0.2.2 loaded. Gameplay: " + Cfg.Tuning().Summary() + ". Every player needs this mod and the same set of powerup mods."));
			}
		}

		private static bool TryPatch(Harmony harmony, Type patchClass)
		{
			try
			{
				harmony.CreateClassProcessor(patchClass).Patch();
				return true;
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Shaped Glass failed to patch {patchClass.Name}: {arg}");
				return false;
			}
		}
	}
	internal static class ShapedGlassPowerup
	{
		public const string PowerupName = "Shaped Glass";

		private static FieldRef<PowerupInventory, int[]> counts;

		public static Powerup Definition { get; private set; }

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

		public static bool TryBindCounts(out string why)
		{
			why = null;
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(PowerupInventory), "powerups");
				if (fieldInfo == null)
				{
					why = "PowerupInventory.powerups was not found.";
					return false;
				}
				counts = AccessTools.FieldRefAccess<PowerupInventory, int[]>(fieldInfo);
				if (counts == null)
				{
					why = "PowerupInventory.powerups could not be bound.";
					return false;
				}
				return true;
			}
			catch (Exception ex)
			{
				why = ex.Message;
				return false;
			}
		}

		public static int[] CountsOf(PowerupInventory inv)
		{
			if ((Object)(object)inv == (Object)null || counts == null)
			{
				return null;
			}
			return counts.Invoke(inv);
		}

		public static int StacksIn(int[] stackCounts)
		{
			if (stackCounts == null || Id < 0 || Id >= stackCounts.Length)
			{
				return 0;
			}
			return stackCounts[Id];
		}

		public static int LocalStacks()
		{
			return StacksIn(CountsOf(PowerupInventory.Instance));
		}

		public static void Create()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Powerup obj = ScriptableObject.CreateInstance<Powerup>();
			obj.name = "Shaped Glass";
			((Object)obj).name = "Shaped Glass";
			obj.description = ShapedGlassPlugin.Cfg.Tuning().Description();
			obj.tier = (PowerTier)2;
			obj.sprite = LoadIcon();
			((Object)obj).hideFlags = (HideFlags)32;
			Definition = obj;
		}

		public static void GiveOneLocal()
		{
			int[] array = CountsOf(PowerupInventory.Instance);
			if (array == null || Id < 0 || Id >= array.Length)
			{
				ShapedGlassPlugin.Log.LogWarning((object)"Can't give Shaped Glass: not in a game yet, or the powerup isn't registered.");
				return;
			}
			if ((Object)(object)PowerupUI.Instance == (Object)null || (Object)(object)PlayerStatus.Instance == (Object)null)
			{
				ShapedGlassPlugin.Log.LogWarning((object)"Can't give Shaped Glass: the powerup bar or the player status is not ready.");
				return;
			}
			array[Id]++;
			PowerupUI.Instance.AddPowerup(Id);
			PlayerStatus.Instance.UpdateStats();
			ShapedGlassPlugin.Log.LogInfo((object)$"Debug: Shaped Glass stacks now {array[Id]} (local only; the host's copy of your powerups is not updated).");
		}

		public 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(ShapedGlassPowerup).Assembly.GetManifestResourceStream("MuckShapedGlass.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 = "ShapedGlassIcon";
				((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 = "ShapedGlassIcon";
				((Object)obj).hideFlags = (HideFlags)32;
				return obj;
			}
			catch (Exception arg)
			{
				ShapedGlassPlugin.Log.LogError((object)$"Failed to load Shaped Glass icon: {arg}");
				return null;
			}
		}
	}
	[HarmonyPatch(typeof(ItemManager), "InitAllPowerups")]
	internal static class RegisterShapedGlassPatch
	{
		[HarmonyPriority(600)]
		[HarmonyAfter(new string[] { "evan.muck.ukulele", "evan.muck.atgmissile", "evan.muck.crowbar", "evan.muck.stickybomb", "evan.muck.tritipdagger", "evan.muck.willothewisp", "evan.muck.devildeal" })]
		private static void Postfix(ItemManager __instance)
		{
			try
			{
				Powerup definition = ShapedGlassPowerup.Definition;
				if ((Object)(object)definition == (Object)null)
				{
					return;
				}
				Dictionary<int, Powerup> allPowerups = __instance.allPowerups;
				Dictionary<string, int> stringToPowerupId = __instance.stringToPowerupId;
				int value;
				bool num = stringToPowerupId.TryGetValue("Shaped Glass", out value);
				Powerup value2;
				RegistrationPlan registrationPlan = RegistrationPlan.Decide(existingIsOurs: num && allPowerups.TryGetValue(value, out value2) && (Object)(object)value2 == (Object)(object)definition, alreadyInPool: __instance.powerupsOrange != null && __instance.powerupsOrange.Contains(definition), nameExists: num, existingId: value, existingIds: allPowerups.Keys);
				if (registrationPlan.Refused)
				{
					ShapedGlassPlugin.Log.LogError((object)string.Format("The name {0} belongs to another powerup (id {1}). Shaped Glass will not register.", "Shaped Glass", value));
					return;
				}
				PickupArt.Apply(definition, __instance.powerupsOrange);
				if (registrationPlan.Gap)
				{
					ShapedGlassPowerup.Id = -1;
					ShapedGlassPlugin.Log.LogError((object)$"Shaped Glass id {registrationPlan.Id} does not fit an inventory of {allPowerups.Count + 1}. Another mod left a gap in powerup ids.");
					return;
				}
				if (registrationPlan.AddToTables)
				{
					allPowerups.Add(registrationPlan.Id, definition);
					stringToPowerupId.Add("Shaped Glass", registrationPlan.Id);
				}
				definition.id = registrationPlan.Id;
				if (registrationPlan.AppendToPool)
				{
					if (__instance.powerupsOrange == null)
					{
						__instance.powerupsOrange = (Powerup[])(object)new Powerup[1] { definition };
					}
					else
					{
						__instance.powerupsOrange = CollectionExtensions.AddToArray<Powerup>(__instance.powerupsOrange, definition);
					}
				}
				ShapedGlassPowerup.Id = registrationPlan.Id;
				int num2 = ((__instance.powerupsOrange != null) ? __instance.powerupsOrange.Length : 0);
				ShapedGlassPlugin.Log.LogInfo((object)$"Registered Shaped Glass as Orange powerup id {registrationPlan.Id} ({allPowerups.Count} powerups total, {num2} in the Orange pool).");
				if (ShapedGlassPlugin.Cfg.VerboseLogging.Value)
				{
					ShapedGlassPlugin.Log.LogInfo((object)("Powerup ids: " + string.Join(", ", from kv in allPowerups
						orderby kv.Key
						select $"{kv.Key}={kv.Value.name}")));
				}
			}
			catch (Exception arg)
			{
				ShapedGlassPlugin.Log.LogError((object)$"Shaped Glass registration failed: {arg}");
			}
		}
	}
	internal sealed class ShapedGlassRunner : MonoBehaviour
	{
		private static ShapedGlassRunner 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("MuckShapedGlass")
				{
					hideFlags = (HideFlags)61
				};
				Object.DontDestroyOnLoad((Object)val);
				instance = val.AddComponent<ShapedGlassRunner>();
			}
		}

		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 = ShapedGlassPlugin.Cfg.GiveShapedGlassKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				ShapedGlassPowerup.GiveOneLocal();
			}
		}
	}
	[HarmonyPatch(typeof(PlayerStatus), "UpdateStats", new Type[] { })]
	internal static class UpdateStatsPatch
	{
		private static bool logged;

		[HarmonyPriority(200)]
		private static void Postfix(PlayerStatus __instance)
		{
			try
			{
				int num = ShapedGlassPowerup.LocalStacks();
				if (num > 0)
				{
					GlassTuning glassTuning = ShapedGlassPlugin.Cfg.Tuning();
					__instance.maxHp = glassTuning.ScaleMaxHp(__instance.maxHp, num);
					__instance.hp = GlassTuning.ClampHp(__instance.hp, __instance.maxHp);
					__instance.maxShield = glassTuning.ScaleShield(__instance.maxShield, num);
					__instance.shield = GlassTuning.ClampHp(__instance.shield, __instance.maxShield);
				}
			}
			catch (Exception arg)
			{
				if (!logged)
				{
					logged = true;
					ShapedGlassPlugin.Log.LogError((object)$"Shaped Glass UpdateStats failed: {arg}");
				}
			}
		}
	}
	[HarmonyPatch(typeof(PowerupInventory), "GetStrengthMultiplier", new Type[] { typeof(int[]) })]
	internal static class StrengthPatch
	{
		private static bool logged;

		[HarmonyPriority(200)]
		private static void Postfix(PowerupInventory __instance, int[] __0, ref float __result)
		{
			try
			{
				int num = ShapedGlassPowerup.StacksIn(__0 ?? ShapedGlassPowerup.CountsOf(__instance));
				if (num > 0)
				{
					__result = ShapedGlassPlugin.Cfg.Tuning().ScaleStrength(__result, num);
				}
			}
			catch (Exception arg)
			{
				if (!logged)
				{
					logged = true;
					ShapedGlassPlugin.Log.LogError((object)$"Shaped Glass GetStrengthMultiplier failed: {arg}");
				}
			}
		}
	}
	[HarmonyPatch(typeof(PowerupInventory), "GetMaxHpAndShield", new Type[] { typeof(int[]) })]
	internal static class HostHpEstimatePatch
	{
		private static bool logged;

		[HarmonyPriority(200)]
		private static void Postfix(PowerupInventory __instance, int[] __0, ref int __result)
		{
			try
			{
				int num = ShapedGlassPowerup.StacksIn(EstimateCounts.Select(__0, HostOwnPowerups(), ShapedGlassPowerup.CountsOf(__instance)));
				if (num > 0)
				{
					int shield = __instance.GetShield(__0);
					__result = ShapedGlassPlugin.Cfg.Tuning().ScaleMaxHpAndShield(__result, shield, num);
				}
			}
			catch (Exception arg)
			{
				if (!logged)
				{
					logged = true;
					ShapedGlassPlugin.Log.LogError((object)$"Shaped Glass GetMaxHpAndShield failed: {arg}");
				}
			}
		}

		private static int[] HostOwnPowerups()
		{
			LocalClient instance = LocalClient.instance;
			if ((Object)(object)instance == (Object)null || Server.clients == null)
			{
				return null;
			}
			if (!Server.clients.TryGetValue(instance.myId, out var value) || value == null || value.player == null)
			{
				return null;
			}
			return value.player.powerups;
		}
	}
	[HarmonyPatch(typeof(PlayerStatus), "Dracula", new Type[] { })]
	internal static class DraculaPatch
	{
		private static bool logged;

		[HarmonyPriority(200)]
		private static void Postfix(PlayerStatus __instance)
		{
			try
			{
				if (ShapedGlassPowerup.LocalStacks() > 0)
				{
					__instance.hp = GlassTuning.ClampHp(__instance.hp, __instance.maxHp);
				}
			}
			catch (Exception arg)
			{
				if (!logged)
				{
					logged = true;
					ShapedGlassPlugin.Log.LogError((object)$"Shaped Glass Dracula clamp failed: {arg}");
				}
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MuckShapedGlass";

		public const string PLUGIN_NAME = "Shaped Glass";

		public const string PLUGIN_VERSION = "0.2.2";
	}
}